From 562b9ba31b5da4be327ea95bfdf427c3e836f436 Mon Sep 17 00:00:00 2001 From: sean Date: Thu, 23 May 2019 09:24:30 -0700 Subject: [PATCH] added processString function etc --- processString.py | 9 + venv2/bin/activate | 78 + venv2/bin/activate.csh | 42 + venv2/bin/activate.fish | 101 + venv2/bin/activate.ps1 | 60 + venv2/bin/activate_this.py | 46 + venv2/bin/easy_install | 10 + venv2/bin/easy_install-2.7 | 10 + venv2/bin/pip | 10 + venv2/bin/pip2 | 10 + venv2/bin/pip2.7 | 10 + venv2/bin/python | 1 + venv2/bin/python-config | 78 + venv2/bin/python2 | 1 + venv2/bin/python2.7 | Bin 0 -> 3691008 bytes venv2/bin/wheel | 10 + venv2/lib/python2.7/LICENSE.txt | 1 + venv2/lib/python2.7/UserDict.py | 1 + venv2/lib/python2.7/_abcoll.py | 1 + venv2/lib/python2.7/_weakrefset.py | 1 + venv2/lib/python2.7/abc.py | 1 + venv2/lib/python2.7/codecs.py | 1 + venv2/lib/python2.7/copy_reg.py | 1 + venv2/lib/python2.7/distutils/__init__.py | 134 + venv2/lib/python2.7/distutils/distutils.cfg | 6 + venv2/lib/python2.7/encodings | 1 + venv2/lib/python2.7/fnmatch.py | 1 + venv2/lib/python2.7/genericpath.py | 1 + venv2/lib/python2.7/lib-dynload | 1 + venv2/lib/python2.7/linecache.py | 1 + venv2/lib/python2.7/locale.py | 1 + .../lib/python2.7/no-global-site-packages.txt | 0 venv2/lib/python2.7/ntpath.py | 1 + venv2/lib/python2.7/orig-prefix.txt | 1 + venv2/lib/python2.7/os.py | 1 + venv2/lib/python2.7/posixpath.py | 1 + venv2/lib/python2.7/re.py | 1 + .../python2.7/site-packages/easy_install.py | 5 + .../pip-19.1.1.dist-info/INSTALLER | 1 + .../pip-19.1.1.dist-info/LICENSE.txt | 20 + .../pip-19.1.1.dist-info/METADATA | 75 + .../site-packages/pip-19.1.1.dist-info/RECORD | 618 ++ .../site-packages/pip-19.1.1.dist-info/WHEEL | 6 + .../pip-19.1.1.dist-info/entry_points.txt | 5 + .../pip-19.1.1.dist-info/top_level.txt | 1 + .../python2.7/site-packages/pip/__init__.py | 1 + .../python2.7/site-packages/pip/__main__.py | 19 + .../site-packages/pip/_internal/__init__.py | 78 + .../site-packages/pip/_internal/build_env.py | 215 + .../site-packages/pip/_internal/cache.py | 224 + .../pip/_internal/cli/__init__.py | 4 + .../pip/_internal/cli/autocompletion.py | 152 + .../pip/_internal/cli/base_command.py | 340 + .../pip/_internal/cli/cmdoptions.py | 809 ++ .../pip/_internal/cli/main_parser.py | 104 + .../site-packages/pip/_internal/cli/parser.py | 261 + .../pip/_internal/cli/status_codes.py | 8 + .../pip/_internal/commands/__init__.py | 79 + .../pip/_internal/commands/check.py | 41 + .../pip/_internal/commands/completion.py | 94 + .../pip/_internal/commands/configuration.py | 253 + .../pip/_internal/commands/download.py | 176 + .../pip/_internal/commands/freeze.py | 96 + .../pip/_internal/commands/hash.py | 57 + .../pip/_internal/commands/help.py | 37 + .../pip/_internal/commands/install.py | 587 ++ .../pip/_internal/commands/list.py | 302 + .../pip/_internal/commands/search.py | 135 + .../pip/_internal/commands/show.py | 168 + .../pip/_internal/commands/uninstall.py | 78 + .../pip/_internal/commands/wheel.py | 186 + .../pip/_internal/configuration.py | 384 + .../site-packages/pip/_internal/download.py | 979 ++ .../site-packages/pip/_internal/exceptions.py | 274 + .../site-packages/pip/_internal/index.py | 1108 +++ .../site-packages/pip/_internal/locations.py | 211 + .../pip/_internal/models/__init__.py | 2 + .../pip/_internal/models/candidate.py | 31 + .../pip/_internal/models/format_control.py | 73 + .../pip/_internal/models/index.py | 31 + .../pip/_internal/models/link.py | 163 + .../pip/_internal/operations/__init__.py | 0 .../pip/_internal/operations/check.py | 155 + .../pip/_internal/operations/freeze.py | 247 + .../pip/_internal/operations/prepare.py | 426 + .../site-packages/pip/_internal/pep425tags.py | 381 + .../site-packages/pip/_internal/pyproject.py | 171 + .../pip/_internal/req/__init__.py | 78 + .../pip/_internal/req/constructors.py | 340 + .../pip/_internal/req/req_file.py | 383 + .../pip/_internal/req/req_install.py | 1028 +++ .../pip/_internal/req/req_set.py | 193 + .../pip/_internal/req/req_tracker.py | 96 + .../pip/_internal/req/req_uninstall.py | 633 ++ .../site-packages/pip/_internal/resolve.py | 393 + .../pip/_internal/utils/__init__.py | 0 .../pip/_internal/utils/appdirs.py | 268 + .../pip/_internal/utils/compat.py | 264 + .../pip/_internal/utils/deprecation.py | 93 + .../pip/_internal/utils/encoding.py | 39 + .../pip/_internal/utils/filesystem.py | 30 + .../pip/_internal/utils/glibc.py | 93 + .../pip/_internal/utils/hashes.py | 115 + .../pip/_internal/utils/logging.py | 371 + .../site-packages/pip/_internal/utils/misc.py | 1011 ++ .../pip/_internal/utils/models.py | 40 + .../pip/_internal/utils/outdated.py | 162 + .../pip/_internal/utils/packaging.py | 85 + .../pip/_internal/utils/setuptools_build.py | 8 + .../pip/_internal/utils/temp_dir.py | 155 + .../pip/_internal/utils/typing.py | 29 + .../site-packages/pip/_internal/utils/ui.py | 424 + .../pip/_internal/vcs/__init__.py | 604 ++ .../site-packages/pip/_internal/vcs/bazaar.py | 103 + .../site-packages/pip/_internal/vcs/git.py | 362 + .../pip/_internal/vcs/mercurial.py | 105 + .../pip/_internal/vcs/subversion.py | 234 + .../site-packages/pip/_internal/wheel.py | 1098 +++ .../site-packages/pip/_vendor/__init__.py | 109 + .../site-packages/pip/_vendor/appdirs.py | 604 ++ .../pip/_vendor/cachecontrol/__init__.py | 11 + .../pip/_vendor/cachecontrol/_cmd.py | 57 + .../pip/_vendor/cachecontrol/adapter.py | 133 + .../pip/_vendor/cachecontrol/cache.py | 39 + .../_vendor/cachecontrol/caches/__init__.py | 2 + .../_vendor/cachecontrol/caches/file_cache.py | 146 + .../cachecontrol/caches/redis_cache.py | 33 + .../pip/_vendor/cachecontrol/compat.py | 29 + .../pip/_vendor/cachecontrol/controller.py | 367 + .../pip/_vendor/cachecontrol/filewrapper.py | 80 + .../pip/_vendor/cachecontrol/heuristics.py | 135 + .../pip/_vendor/cachecontrol/serialize.py | 186 + .../pip/_vendor/cachecontrol/wrapper.py | 29 + .../pip/_vendor/certifi/__init__.py | 3 + .../pip/_vendor/certifi/__main__.py | 2 + .../pip/_vendor/certifi/cacert.pem | 4658 ++++++++++ .../site-packages/pip/_vendor/certifi/core.py | 15 + .../pip/_vendor/chardet/__init__.py | 39 + .../pip/_vendor/chardet/big5freq.py | 386 + .../pip/_vendor/chardet/big5prober.py | 47 + .../pip/_vendor/chardet/chardistribution.py | 233 + .../pip/_vendor/chardet/charsetgroupprober.py | 106 + .../pip/_vendor/chardet/charsetprober.py | 145 + .../pip/_vendor/chardet/cli/__init__.py | 1 + .../pip/_vendor/chardet/cli/chardetect.py | 85 + .../pip/_vendor/chardet/codingstatemachine.py | 88 + .../pip/_vendor/chardet/compat.py | 34 + .../pip/_vendor/chardet/cp949prober.py | 49 + .../pip/_vendor/chardet/enums.py | 76 + .../pip/_vendor/chardet/escprober.py | 101 + .../pip/_vendor/chardet/escsm.py | 246 + .../pip/_vendor/chardet/eucjpprober.py | 92 + .../pip/_vendor/chardet/euckrfreq.py | 195 + .../pip/_vendor/chardet/euckrprober.py | 47 + .../pip/_vendor/chardet/euctwfreq.py | 387 + .../pip/_vendor/chardet/euctwprober.py | 46 + .../pip/_vendor/chardet/gb2312freq.py | 283 + .../pip/_vendor/chardet/gb2312prober.py | 46 + .../pip/_vendor/chardet/hebrewprober.py | 292 + .../pip/_vendor/chardet/jisfreq.py | 325 + .../pip/_vendor/chardet/jpcntx.py | 233 + .../pip/_vendor/chardet/langbulgarianmodel.py | 228 + .../pip/_vendor/chardet/langcyrillicmodel.py | 333 + .../pip/_vendor/chardet/langgreekmodel.py | 225 + .../pip/_vendor/chardet/langhebrewmodel.py | 200 + .../pip/_vendor/chardet/langhungarianmodel.py | 225 + .../pip/_vendor/chardet/langthaimodel.py | 199 + .../pip/_vendor/chardet/langturkishmodel.py | 193 + .../pip/_vendor/chardet/latin1prober.py | 145 + .../pip/_vendor/chardet/mbcharsetprober.py | 91 + .../pip/_vendor/chardet/mbcsgroupprober.py | 54 + .../pip/_vendor/chardet/mbcssm.py | 572 ++ .../pip/_vendor/chardet/sbcharsetprober.py | 132 + .../pip/_vendor/chardet/sbcsgroupprober.py | 73 + .../pip/_vendor/chardet/sjisprober.py | 92 + .../pip/_vendor/chardet/universaldetector.py | 286 + .../pip/_vendor/chardet/utf8prober.py | 82 + .../pip/_vendor/chardet/version.py | 9 + .../pip/_vendor/colorama/__init__.py | 6 + .../pip/_vendor/colorama/ansi.py | 102 + .../pip/_vendor/colorama/ansitowin32.py | 257 + .../pip/_vendor/colorama/initialise.py | 80 + .../pip/_vendor/colorama/win32.py | 152 + .../pip/_vendor/colorama/winterm.py | 169 + .../pip/_vendor/distlib/__init__.py | 23 + .../pip/_vendor/distlib/_backport/__init__.py | 6 + .../pip/_vendor/distlib/_backport/misc.py | 41 + .../pip/_vendor/distlib/_backport/shutil.py | 761 ++ .../_vendor/distlib/_backport/sysconfig.cfg | 84 + .../_vendor/distlib/_backport/sysconfig.py | 788 ++ .../pip/_vendor/distlib/_backport/tarfile.py | 2607 ++++++ .../pip/_vendor/distlib/compat.py | 1120 +++ .../pip/_vendor/distlib/database.py | 1339 +++ .../pip/_vendor/distlib/index.py | 516 ++ .../pip/_vendor/distlib/locators.py | 1295 +++ .../pip/_vendor/distlib/manifest.py | 393 + .../pip/_vendor/distlib/markers.py | 131 + .../pip/_vendor/distlib/metadata.py | 1094 +++ .../pip/_vendor/distlib/resources.py | 355 + .../pip/_vendor/distlib/scripts.py | 417 + .../site-packages/pip/_vendor/distlib/t32.exe | Bin 0 -> 92672 bytes .../site-packages/pip/_vendor/distlib/t64.exe | Bin 0 -> 102400 bytes .../site-packages/pip/_vendor/distlib/util.py | 1756 ++++ .../pip/_vendor/distlib/version.py | 736 ++ .../site-packages/pip/_vendor/distlib/w32.exe | Bin 0 -> 89088 bytes .../site-packages/pip/_vendor/distlib/w64.exe | Bin 0 -> 99328 bytes .../pip/_vendor/distlib/wheel.py | 988 ++ .../site-packages/pip/_vendor/distro.py | 1216 +++ .../pip/_vendor/html5lib/__init__.py | 35 + .../pip/_vendor/html5lib/_ihatexml.py | 288 + .../pip/_vendor/html5lib/_inputstream.py | 923 ++ .../pip/_vendor/html5lib/_tokenizer.py | 1721 ++++ .../pip/_vendor/html5lib/_trie/__init__.py | 14 + .../pip/_vendor/html5lib/_trie/_base.py | 37 + .../pip/_vendor/html5lib/_trie/datrie.py | 44 + .../pip/_vendor/html5lib/_trie/py.py | 67 + .../pip/_vendor/html5lib/_utils.py | 124 + .../pip/_vendor/html5lib/constants.py | 2947 ++++++ .../pip/_vendor/html5lib/filters/__init__.py | 0 .../filters/alphabeticalattributes.py | 29 + .../pip/_vendor/html5lib/filters/base.py | 12 + .../html5lib/filters/inject_meta_charset.py | 73 + .../pip/_vendor/html5lib/filters/lint.py | 93 + .../_vendor/html5lib/filters/optionaltags.py | 207 + .../pip/_vendor/html5lib/filters/sanitizer.py | 896 ++ .../_vendor/html5lib/filters/whitespace.py | 38 + .../pip/_vendor/html5lib/html5parser.py | 2791 ++++++ .../pip/_vendor/html5lib/serializer.py | 409 + .../_vendor/html5lib/treeadapters/__init__.py | 30 + .../_vendor/html5lib/treeadapters/genshi.py | 54 + .../pip/_vendor/html5lib/treeadapters/sax.py | 50 + .../_vendor/html5lib/treebuilders/__init__.py | 88 + .../pip/_vendor/html5lib/treebuilders/base.py | 417 + .../pip/_vendor/html5lib/treebuilders/dom.py | 236 + .../_vendor/html5lib/treebuilders/etree.py | 340 + .../html5lib/treebuilders/etree_lxml.py | 366 + .../_vendor/html5lib/treewalkers/__init__.py | 154 + .../pip/_vendor/html5lib/treewalkers/base.py | 252 + .../pip/_vendor/html5lib/treewalkers/dom.py | 43 + .../pip/_vendor/html5lib/treewalkers/etree.py | 130 + .../html5lib/treewalkers/etree_lxml.py | 213 + .../_vendor/html5lib/treewalkers/genshi.py | 69 + .../pip/_vendor/idna/__init__.py | 2 + .../site-packages/pip/_vendor/idna/codec.py | 118 + .../site-packages/pip/_vendor/idna/compat.py | 12 + .../site-packages/pip/_vendor/idna/core.py | 396 + .../pip/_vendor/idna/idnadata.py | 1979 ++++ .../pip/_vendor/idna/intranges.py | 53 + .../pip/_vendor/idna/package_data.py | 2 + .../pip/_vendor/idna/uts46data.py | 8205 +++++++++++++++++ .../site-packages/pip/_vendor/ipaddress.py | 2419 +++++ .../pip/_vendor/lockfile/__init__.py | 347 + .../pip/_vendor/lockfile/linklockfile.py | 73 + .../pip/_vendor/lockfile/mkdirlockfile.py | 84 + .../pip/_vendor/lockfile/pidlockfile.py | 190 + .../pip/_vendor/lockfile/sqlitelockfile.py | 156 + .../pip/_vendor/lockfile/symlinklockfile.py | 70 + .../pip/_vendor/msgpack/__init__.py | 66 + .../pip/_vendor/msgpack/_version.py | 1 + .../pip/_vendor/msgpack/exceptions.py | 41 + .../pip/_vendor/msgpack/fallback.py | 977 ++ .../pip/_vendor/packaging/__about__.py | 27 + .../pip/_vendor/packaging/__init__.py | 26 + .../pip/_vendor/packaging/_compat.py | 31 + .../pip/_vendor/packaging/_structures.py | 68 + .../pip/_vendor/packaging/markers.py | 296 + .../pip/_vendor/packaging/requirements.py | 138 + .../pip/_vendor/packaging/specifiers.py | 749 ++ .../pip/_vendor/packaging/utils.py | 57 + .../pip/_vendor/packaging/version.py | 420 + .../pip/_vendor/pep517/__init__.py | 4 + .../pip/_vendor/pep517/_in_process.py | 207 + .../site-packages/pip/_vendor/pep517/build.py | 108 + .../site-packages/pip/_vendor/pep517/check.py | 202 + .../pip/_vendor/pep517/colorlog.py | 115 + .../pip/_vendor/pep517/compat.py | 23 + .../pip/_vendor/pep517/envbuild.py | 158 + .../pip/_vendor/pep517/wrappers.py | 163 + .../pip/_vendor/pkg_resources/__init__.py | 3286 +++++++ .../pip/_vendor/pkg_resources/py31compat.py | 23 + .../pip/_vendor/progress/__init__.py | 177 + .../site-packages/pip/_vendor/progress/bar.py | 91 + .../pip/_vendor/progress/counter.py | 41 + .../pip/_vendor/progress/spinner.py | 43 + .../site-packages/pip/_vendor/pyparsing.py | 6493 +++++++++++++ .../pip/_vendor/pytoml/__init__.py | 4 + .../site-packages/pip/_vendor/pytoml/core.py | 13 + .../pip/_vendor/pytoml/parser.py | 341 + .../site-packages/pip/_vendor/pytoml/test.py | 30 + .../site-packages/pip/_vendor/pytoml/utils.py | 67 + .../pip/_vendor/pytoml/writer.py | 106 + .../pip/_vendor/requests/__init__.py | 133 + .../pip/_vendor/requests/__version__.py | 14 + .../pip/_vendor/requests/_internal_utils.py | 42 + .../pip/_vendor/requests/adapters.py | 533 ++ .../site-packages/pip/_vendor/requests/api.py | 158 + .../pip/_vendor/requests/auth.py | 305 + .../pip/_vendor/requests/certs.py | 18 + .../pip/_vendor/requests/compat.py | 74 + .../pip/_vendor/requests/cookies.py | 549 ++ .../pip/_vendor/requests/exceptions.py | 126 + .../pip/_vendor/requests/help.py | 119 + .../pip/_vendor/requests/hooks.py | 34 + .../pip/_vendor/requests/models.py | 953 ++ .../pip/_vendor/requests/packages.py | 16 + .../pip/_vendor/requests/sessions.py | 770 ++ .../pip/_vendor/requests/status_codes.py | 120 + .../pip/_vendor/requests/structures.py | 103 + .../pip/_vendor/requests/utils.py | 977 ++ .../site-packages/pip/_vendor/retrying.py | 267 + .../site-packages/pip/_vendor/six.py | 952 ++ .../pip/_vendor/urllib3/__init__.py | 92 + .../pip/_vendor/urllib3/_collections.py | 329 + .../pip/_vendor/urllib3/connection.py | 391 + .../pip/_vendor/urllib3/connectionpool.py | 896 ++ .../pip/_vendor/urllib3/contrib/__init__.py | 0 .../urllib3/contrib/_appengine_environ.py | 30 + .../contrib/_securetransport/__init__.py | 0 .../contrib/_securetransport/bindings.py | 593 ++ .../contrib/_securetransport/low_level.py | 346 + .../pip/_vendor/urllib3/contrib/appengine.py | 289 + .../pip/_vendor/urllib3/contrib/ntlmpool.py | 111 + .../pip/_vendor/urllib3/contrib/pyopenssl.py | 466 + .../urllib3/contrib/securetransport.py | 804 ++ .../pip/_vendor/urllib3/contrib/socks.py | 192 + .../pip/_vendor/urllib3/exceptions.py | 246 + .../pip/_vendor/urllib3/fields.py | 178 + .../pip/_vendor/urllib3/filepost.py | 98 + .../pip/_vendor/urllib3/packages/__init__.py | 5 + .../urllib3/packages/backports/__init__.py | 0 .../urllib3/packages/backports/makefile.py | 53 + .../pip/_vendor/urllib3/packages/six.py | 868 ++ .../packages/ssl_match_hostname/__init__.py | 19 + .../ssl_match_hostname/_implementation.py | 156 + .../pip/_vendor/urllib3/poolmanager.py | 450 + .../pip/_vendor/urllib3/request.py | 150 + .../pip/_vendor/urllib3/response.py | 705 ++ .../pip/_vendor/urllib3/util/__init__.py | 54 + .../pip/_vendor/urllib3/util/connection.py | 134 + .../pip/_vendor/urllib3/util/queue.py | 21 + .../pip/_vendor/urllib3/util/request.py | 118 + .../pip/_vendor/urllib3/util/response.py | 87 + .../pip/_vendor/urllib3/util/retry.py | 411 + .../pip/_vendor/urllib3/util/ssl_.py | 381 + .../pip/_vendor/urllib3/util/timeout.py | 242 + .../pip/_vendor/urllib3/util/url.py | 230 + .../pip/_vendor/urllib3/util/wait.py | 150 + .../pip/_vendor/webencodings/__init__.py | 342 + .../pip/_vendor/webencodings/labels.py | 231 + .../pip/_vendor/webencodings/mklabels.py | 59 + .../pip/_vendor/webencodings/tests.py | 153 + .../_vendor/webencodings/x_user_defined.py | 325 + .../site-packages/pkg_resources/__init__.py | 3286 +++++++ .../pkg_resources/_vendor/__init__.py | 0 .../pkg_resources/_vendor/appdirs.py | 608 ++ .../_vendor/packaging/__about__.py | 21 + .../_vendor/packaging/__init__.py | 14 + .../_vendor/packaging/_compat.py | 30 + .../_vendor/packaging/_structures.py | 68 + .../_vendor/packaging/markers.py | 301 + .../_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 | 5742 ++++++++++++ .../pkg_resources/_vendor/six.py | 868 ++ .../pkg_resources/extern/__init__.py | 73 + .../site-packages/pkg_resources/py31compat.py | 23 + .../setuptools-41.0.1.dist-info/INSTALLER | 1 + .../setuptools-41.0.1.dist-info/LICENSE | 19 + .../setuptools-41.0.1.dist-info/METADATA | 77 + .../setuptools-41.0.1.dist-info/RECORD | 186 + .../setuptools-41.0.1.dist-info/WHEEL | 6 + .../dependency_links.txt | 2 + .../entry_points.txt | 65 + .../setuptools-41.0.1.dist-info/top_level.txt | 3 + .../setuptools-41.0.1.dist-info/zip-safe | 1 + .../site-packages/setuptools/__init__.py | 228 + .../setuptools/_deprecation_warning.py | 7 + .../setuptools/_vendor/__init__.py | 0 .../setuptools/_vendor/packaging/__about__.py | 21 + .../setuptools/_vendor/packaging/__init__.py | 14 + .../setuptools/_vendor/packaging/_compat.py | 30 + .../_vendor/packaging/_structures.py | 68 + .../setuptools/_vendor/packaging/markers.py | 301 + .../_vendor/packaging/requirements.py | 127 + .../_vendor/packaging/specifiers.py | 774 ++ .../setuptools/_vendor/packaging/utils.py | 14 + .../setuptools/_vendor/packaging/version.py | 393 + .../setuptools/_vendor/pyparsing.py | 5742 ++++++++++++ .../site-packages/setuptools/_vendor/six.py | 868 ++ .../site-packages/setuptools/archive_util.py | 173 + .../site-packages/setuptools/build_meta.py | 254 + .../site-packages/setuptools/cli-32.exe | Bin 0 -> 65536 bytes .../site-packages/setuptools/cli-64.exe | Bin 0 -> 74752 bytes .../site-packages/setuptools/cli.exe | Bin 0 -> 65536 bytes .../setuptools/command/__init__.py | 18 + .../site-packages/setuptools/command/alias.py | 80 + .../setuptools/command/bdist_egg.py | 502 + .../setuptools/command/bdist_rpm.py | 43 + .../setuptools/command/bdist_wininst.py | 21 + .../setuptools/command/build_clib.py | 98 + .../setuptools/command/build_ext.py | 321 + .../setuptools/command/build_py.py | 270 + .../setuptools/command/develop.py | 221 + .../setuptools/command/dist_info.py | 36 + .../setuptools/command/easy_install.py | 2342 +++++ .../setuptools/command/egg_info.py | 717 ++ .../setuptools/command/install.py | 125 + .../setuptools/command/install_egg_info.py | 62 + .../setuptools/command/install_lib.py | 121 + .../setuptools/command/install_scripts.py | 65 + .../setuptools/command/launcher manifest.xml | 15 + .../setuptools/command/py36compat.py | 136 + .../setuptools/command/register.py | 18 + .../setuptools/command/rotate.py | 66 + .../setuptools/command/saveopts.py | 22 + .../site-packages/setuptools/command/sdist.py | 221 + .../setuptools/command/setopt.py | 149 + .../site-packages/setuptools/command/test.py | 271 + .../setuptools/command/upload.py | 196 + .../setuptools/command/upload_docs.py | 206 + .../site-packages/setuptools/config.py | 656 ++ .../site-packages/setuptools/dep_util.py | 23 + .../site-packages/setuptools/depends.py | 186 + .../site-packages/setuptools/dist.py | 1278 +++ .../site-packages/setuptools/extension.py | 57 + .../setuptools/extern/__init__.py | 73 + .../site-packages/setuptools/glibc.py | 86 + .../site-packages/setuptools/glob.py | 174 + .../site-packages/setuptools/gui-32.exe | Bin 0 -> 65536 bytes .../site-packages/setuptools/gui-64.exe | Bin 0 -> 75264 bytes .../site-packages/setuptools/gui.exe | Bin 0 -> 65536 bytes .../site-packages/setuptools/launch.py | 35 + .../site-packages/setuptools/lib2to3_ex.py | 62 + .../site-packages/setuptools/monkey.py | 179 + .../site-packages/setuptools/msvc.py | 1301 +++ .../site-packages/setuptools/namespaces.py | 107 + .../site-packages/setuptools/package_index.py | 1136 +++ .../site-packages/setuptools/pep425tags.py | 319 + .../site-packages/setuptools/py27compat.py | 28 + .../site-packages/setuptools/py31compat.py | 32 + .../site-packages/setuptools/py33compat.py | 55 + .../site-packages/setuptools/sandbox.py | 491 + .../setuptools/script (dev).tmpl | 6 + .../site-packages/setuptools/script.tmpl | 3 + .../site-packages/setuptools/site-patch.py | 74 + .../site-packages/setuptools/ssl_support.py | 260 + .../site-packages/setuptools/unicode_utils.py | 44 + .../site-packages/setuptools/version.py | 6 + .../site-packages/setuptools/wheel.py | 211 + .../setuptools/windows_support.py | 29 + .../wheel-0.33.4.dist-info/INSTALLER | 1 + .../wheel-0.33.4.dist-info/LICENSE.txt | 22 + .../wheel-0.33.4.dist-info/METADATA | 60 + .../wheel-0.33.4.dist-info/RECORD | 32 + .../wheel-0.33.4.dist-info/WHEEL | 6 + .../wheel-0.33.4.dist-info/entry_points.txt | 6 + .../wheel-0.33.4.dist-info/top_level.txt | 1 + .../python2.7/site-packages/wheel/__init__.py | 2 + .../python2.7/site-packages/wheel/__main__.py | 19 + .../site-packages/wheel/bdist_wheel.py | 372 + .../site-packages/wheel/cli/__init__.py | 88 + .../site-packages/wheel/cli/convert.py | 269 + .../python2.7/site-packages/wheel/cli/pack.py | 58 + .../site-packages/wheel/cli/unpack.py | 25 + .../python2.7/site-packages/wheel/metadata.py | 141 + .../site-packages/wheel/pep425tags.py | 185 + .../python2.7/site-packages/wheel/pkginfo.py | 43 + .../lib/python2.7/site-packages/wheel/util.py | 46 + .../site-packages/wheel/wheelfile.py | 169 + venv2/lib/python2.7/site.py | 851 ++ venv2/lib/python2.7/sre.py | 1 + venv2/lib/python2.7/sre_compile.py | 1 + venv2/lib/python2.7/sre_constants.py | 1 + venv2/lib/python2.7/sre_parse.py | 1 + venv2/lib/python2.7/stat.py | 1 + venv2/lib/python2.7/types.py | 1 + venv2/lib/python2.7/warnings.py | 1 + venv2/local/bin | 1 + venv2/local/lib | 1 + 481 files changed, 145063 insertions(+) create mode 100644 processString.py create mode 100644 venv2/bin/activate create mode 100644 venv2/bin/activate.csh create mode 100644 venv2/bin/activate.fish create mode 100644 venv2/bin/activate.ps1 create mode 100644 venv2/bin/activate_this.py create mode 100755 venv2/bin/easy_install create mode 100755 venv2/bin/easy_install-2.7 create mode 100755 venv2/bin/pip create mode 100755 venv2/bin/pip2 create mode 100755 venv2/bin/pip2.7 create mode 120000 venv2/bin/python create mode 100755 venv2/bin/python-config create mode 120000 venv2/bin/python2 create mode 100755 venv2/bin/python2.7 create mode 100755 venv2/bin/wheel create mode 120000 venv2/lib/python2.7/LICENSE.txt create mode 120000 venv2/lib/python2.7/UserDict.py create mode 120000 venv2/lib/python2.7/_abcoll.py create mode 120000 venv2/lib/python2.7/_weakrefset.py create mode 120000 venv2/lib/python2.7/abc.py create mode 120000 venv2/lib/python2.7/codecs.py create mode 120000 venv2/lib/python2.7/copy_reg.py create mode 100644 venv2/lib/python2.7/distutils/__init__.py create mode 100644 venv2/lib/python2.7/distutils/distutils.cfg create mode 120000 venv2/lib/python2.7/encodings create mode 120000 venv2/lib/python2.7/fnmatch.py create mode 120000 venv2/lib/python2.7/genericpath.py create mode 120000 venv2/lib/python2.7/lib-dynload create mode 120000 venv2/lib/python2.7/linecache.py create mode 120000 venv2/lib/python2.7/locale.py create mode 100644 venv2/lib/python2.7/no-global-site-packages.txt create mode 120000 venv2/lib/python2.7/ntpath.py create mode 100644 venv2/lib/python2.7/orig-prefix.txt create mode 120000 venv2/lib/python2.7/os.py create mode 120000 venv2/lib/python2.7/posixpath.py create mode 120000 venv2/lib/python2.7/re.py create mode 100644 venv2/lib/python2.7/site-packages/easy_install.py create mode 100644 venv2/lib/python2.7/site-packages/pip-19.1.1.dist-info/INSTALLER create mode 100644 venv2/lib/python2.7/site-packages/pip-19.1.1.dist-info/LICENSE.txt create mode 100644 venv2/lib/python2.7/site-packages/pip-19.1.1.dist-info/METADATA create mode 100644 venv2/lib/python2.7/site-packages/pip-19.1.1.dist-info/RECORD create mode 100644 venv2/lib/python2.7/site-packages/pip-19.1.1.dist-info/WHEEL create mode 100644 venv2/lib/python2.7/site-packages/pip-19.1.1.dist-info/entry_points.txt create mode 100644 venv2/lib/python2.7/site-packages/pip-19.1.1.dist-info/top_level.txt create mode 100644 venv2/lib/python2.7/site-packages/pip/__init__.py create mode 100644 venv2/lib/python2.7/site-packages/pip/__main__.py create mode 100644 venv2/lib/python2.7/site-packages/pip/_internal/__init__.py create mode 100644 venv2/lib/python2.7/site-packages/pip/_internal/build_env.py create mode 100644 venv2/lib/python2.7/site-packages/pip/_internal/cache.py create mode 100644 venv2/lib/python2.7/site-packages/pip/_internal/cli/__init__.py create mode 100644 venv2/lib/python2.7/site-packages/pip/_internal/cli/autocompletion.py create mode 100644 venv2/lib/python2.7/site-packages/pip/_internal/cli/base_command.py create mode 100644 venv2/lib/python2.7/site-packages/pip/_internal/cli/cmdoptions.py create mode 100644 venv2/lib/python2.7/site-packages/pip/_internal/cli/main_parser.py create mode 100644 venv2/lib/python2.7/site-packages/pip/_internal/cli/parser.py create mode 100644 venv2/lib/python2.7/site-packages/pip/_internal/cli/status_codes.py create mode 100644 venv2/lib/python2.7/site-packages/pip/_internal/commands/__init__.py create mode 100644 venv2/lib/python2.7/site-packages/pip/_internal/commands/check.py create mode 100644 venv2/lib/python2.7/site-packages/pip/_internal/commands/completion.py create mode 100644 venv2/lib/python2.7/site-packages/pip/_internal/commands/configuration.py create mode 100644 venv2/lib/python2.7/site-packages/pip/_internal/commands/download.py create mode 100644 venv2/lib/python2.7/site-packages/pip/_internal/commands/freeze.py create mode 100644 venv2/lib/python2.7/site-packages/pip/_internal/commands/hash.py create mode 100644 venv2/lib/python2.7/site-packages/pip/_internal/commands/help.py create mode 100644 venv2/lib/python2.7/site-packages/pip/_internal/commands/install.py create mode 100644 venv2/lib/python2.7/site-packages/pip/_internal/commands/list.py create mode 100644 venv2/lib/python2.7/site-packages/pip/_internal/commands/search.py create mode 100644 venv2/lib/python2.7/site-packages/pip/_internal/commands/show.py create mode 100644 venv2/lib/python2.7/site-packages/pip/_internal/commands/uninstall.py create mode 100644 venv2/lib/python2.7/site-packages/pip/_internal/commands/wheel.py create mode 100644 venv2/lib/python2.7/site-packages/pip/_internal/configuration.py create mode 100644 venv2/lib/python2.7/site-packages/pip/_internal/download.py create mode 100644 venv2/lib/python2.7/site-packages/pip/_internal/exceptions.py create mode 100644 venv2/lib/python2.7/site-packages/pip/_internal/index.py create mode 100644 venv2/lib/python2.7/site-packages/pip/_internal/locations.py create mode 100644 venv2/lib/python2.7/site-packages/pip/_internal/models/__init__.py create mode 100644 venv2/lib/python2.7/site-packages/pip/_internal/models/candidate.py create mode 100644 venv2/lib/python2.7/site-packages/pip/_internal/models/format_control.py create mode 100644 venv2/lib/python2.7/site-packages/pip/_internal/models/index.py create mode 100644 venv2/lib/python2.7/site-packages/pip/_internal/models/link.py create mode 100644 venv2/lib/python2.7/site-packages/pip/_internal/operations/__init__.py create mode 100644 venv2/lib/python2.7/site-packages/pip/_internal/operations/check.py create mode 100644 venv2/lib/python2.7/site-packages/pip/_internal/operations/freeze.py create mode 100644 venv2/lib/python2.7/site-packages/pip/_internal/operations/prepare.py create mode 100644 venv2/lib/python2.7/site-packages/pip/_internal/pep425tags.py create mode 100644 venv2/lib/python2.7/site-packages/pip/_internal/pyproject.py create mode 100644 venv2/lib/python2.7/site-packages/pip/_internal/req/__init__.py create mode 100644 venv2/lib/python2.7/site-packages/pip/_internal/req/constructors.py create mode 100644 venv2/lib/python2.7/site-packages/pip/_internal/req/req_file.py create mode 100644 venv2/lib/python2.7/site-packages/pip/_internal/req/req_install.py create mode 100644 venv2/lib/python2.7/site-packages/pip/_internal/req/req_set.py create mode 100644 venv2/lib/python2.7/site-packages/pip/_internal/req/req_tracker.py create mode 100644 venv2/lib/python2.7/site-packages/pip/_internal/req/req_uninstall.py create mode 100644 venv2/lib/python2.7/site-packages/pip/_internal/resolve.py create mode 100644 venv2/lib/python2.7/site-packages/pip/_internal/utils/__init__.py create mode 100644 venv2/lib/python2.7/site-packages/pip/_internal/utils/appdirs.py create mode 100644 venv2/lib/python2.7/site-packages/pip/_internal/utils/compat.py create mode 100644 venv2/lib/python2.7/site-packages/pip/_internal/utils/deprecation.py create mode 100644 venv2/lib/python2.7/site-packages/pip/_internal/utils/encoding.py create mode 100644 venv2/lib/python2.7/site-packages/pip/_internal/utils/filesystem.py create mode 100644 venv2/lib/python2.7/site-packages/pip/_internal/utils/glibc.py create mode 100644 venv2/lib/python2.7/site-packages/pip/_internal/utils/hashes.py create mode 100644 venv2/lib/python2.7/site-packages/pip/_internal/utils/logging.py create mode 100644 venv2/lib/python2.7/site-packages/pip/_internal/utils/misc.py create mode 100644 venv2/lib/python2.7/site-packages/pip/_internal/utils/models.py create mode 100644 venv2/lib/python2.7/site-packages/pip/_internal/utils/outdated.py create mode 100644 venv2/lib/python2.7/site-packages/pip/_internal/utils/packaging.py create mode 100644 venv2/lib/python2.7/site-packages/pip/_internal/utils/setuptools_build.py create mode 100644 venv2/lib/python2.7/site-packages/pip/_internal/utils/temp_dir.py create mode 100644 venv2/lib/python2.7/site-packages/pip/_internal/utils/typing.py create mode 100644 venv2/lib/python2.7/site-packages/pip/_internal/utils/ui.py create mode 100644 venv2/lib/python2.7/site-packages/pip/_internal/vcs/__init__.py create mode 100644 venv2/lib/python2.7/site-packages/pip/_internal/vcs/bazaar.py create mode 100644 venv2/lib/python2.7/site-packages/pip/_internal/vcs/git.py create mode 100644 venv2/lib/python2.7/site-packages/pip/_internal/vcs/mercurial.py create mode 100644 venv2/lib/python2.7/site-packages/pip/_internal/vcs/subversion.py create mode 100644 venv2/lib/python2.7/site-packages/pip/_internal/wheel.py create mode 100644 venv2/lib/python2.7/site-packages/pip/_vendor/__init__.py create mode 100644 venv2/lib/python2.7/site-packages/pip/_vendor/appdirs.py create mode 100644 venv2/lib/python2.7/site-packages/pip/_vendor/cachecontrol/__init__.py create mode 100644 venv2/lib/python2.7/site-packages/pip/_vendor/cachecontrol/_cmd.py create mode 100644 venv2/lib/python2.7/site-packages/pip/_vendor/cachecontrol/adapter.py create mode 100644 venv2/lib/python2.7/site-packages/pip/_vendor/cachecontrol/cache.py create mode 100644 venv2/lib/python2.7/site-packages/pip/_vendor/cachecontrol/caches/__init__.py create mode 100644 venv2/lib/python2.7/site-packages/pip/_vendor/cachecontrol/caches/file_cache.py create mode 100644 venv2/lib/python2.7/site-packages/pip/_vendor/cachecontrol/caches/redis_cache.py create mode 100644 venv2/lib/python2.7/site-packages/pip/_vendor/cachecontrol/compat.py create mode 100644 venv2/lib/python2.7/site-packages/pip/_vendor/cachecontrol/controller.py create mode 100644 venv2/lib/python2.7/site-packages/pip/_vendor/cachecontrol/filewrapper.py create mode 100644 venv2/lib/python2.7/site-packages/pip/_vendor/cachecontrol/heuristics.py create mode 100644 venv2/lib/python2.7/site-packages/pip/_vendor/cachecontrol/serialize.py create mode 100644 venv2/lib/python2.7/site-packages/pip/_vendor/cachecontrol/wrapper.py create mode 100644 venv2/lib/python2.7/site-packages/pip/_vendor/certifi/__init__.py create mode 100644 venv2/lib/python2.7/site-packages/pip/_vendor/certifi/__main__.py create mode 100644 venv2/lib/python2.7/site-packages/pip/_vendor/certifi/cacert.pem create mode 100644 venv2/lib/python2.7/site-packages/pip/_vendor/certifi/core.py create mode 100644 venv2/lib/python2.7/site-packages/pip/_vendor/chardet/__init__.py create mode 100644 venv2/lib/python2.7/site-packages/pip/_vendor/chardet/big5freq.py create mode 100644 venv2/lib/python2.7/site-packages/pip/_vendor/chardet/big5prober.py create mode 100644 venv2/lib/python2.7/site-packages/pip/_vendor/chardet/chardistribution.py create mode 100644 venv2/lib/python2.7/site-packages/pip/_vendor/chardet/charsetgroupprober.py create mode 100644 venv2/lib/python2.7/site-packages/pip/_vendor/chardet/charsetprober.py create mode 100644 venv2/lib/python2.7/site-packages/pip/_vendor/chardet/cli/__init__.py create mode 100644 venv2/lib/python2.7/site-packages/pip/_vendor/chardet/cli/chardetect.py create mode 100644 venv2/lib/python2.7/site-packages/pip/_vendor/chardet/codingstatemachine.py create mode 100644 venv2/lib/python2.7/site-packages/pip/_vendor/chardet/compat.py create mode 100644 venv2/lib/python2.7/site-packages/pip/_vendor/chardet/cp949prober.py create mode 100644 venv2/lib/python2.7/site-packages/pip/_vendor/chardet/enums.py create mode 100644 venv2/lib/python2.7/site-packages/pip/_vendor/chardet/escprober.py create mode 100644 venv2/lib/python2.7/site-packages/pip/_vendor/chardet/escsm.py create mode 100644 venv2/lib/python2.7/site-packages/pip/_vendor/chardet/eucjpprober.py create mode 100644 venv2/lib/python2.7/site-packages/pip/_vendor/chardet/euckrfreq.py create mode 100644 venv2/lib/python2.7/site-packages/pip/_vendor/chardet/euckrprober.py create mode 100644 venv2/lib/python2.7/site-packages/pip/_vendor/chardet/euctwfreq.py create mode 100644 venv2/lib/python2.7/site-packages/pip/_vendor/chardet/euctwprober.py create mode 100644 venv2/lib/python2.7/site-packages/pip/_vendor/chardet/gb2312freq.py create mode 100644 venv2/lib/python2.7/site-packages/pip/_vendor/chardet/gb2312prober.py create mode 100644 venv2/lib/python2.7/site-packages/pip/_vendor/chardet/hebrewprober.py create mode 100644 venv2/lib/python2.7/site-packages/pip/_vendor/chardet/jisfreq.py create mode 100644 venv2/lib/python2.7/site-packages/pip/_vendor/chardet/jpcntx.py create mode 100644 venv2/lib/python2.7/site-packages/pip/_vendor/chardet/langbulgarianmodel.py create mode 100644 venv2/lib/python2.7/site-packages/pip/_vendor/chardet/langcyrillicmodel.py create mode 100644 venv2/lib/python2.7/site-packages/pip/_vendor/chardet/langgreekmodel.py create mode 100644 venv2/lib/python2.7/site-packages/pip/_vendor/chardet/langhebrewmodel.py create mode 100644 venv2/lib/python2.7/site-packages/pip/_vendor/chardet/langhungarianmodel.py create mode 100644 venv2/lib/python2.7/site-packages/pip/_vendor/chardet/langthaimodel.py create mode 100644 venv2/lib/python2.7/site-packages/pip/_vendor/chardet/langturkishmodel.py create mode 100644 venv2/lib/python2.7/site-packages/pip/_vendor/chardet/latin1prober.py create mode 100644 venv2/lib/python2.7/site-packages/pip/_vendor/chardet/mbcharsetprober.py create mode 100644 venv2/lib/python2.7/site-packages/pip/_vendor/chardet/mbcsgroupprober.py create mode 100644 venv2/lib/python2.7/site-packages/pip/_vendor/chardet/mbcssm.py create mode 100644 venv2/lib/python2.7/site-packages/pip/_vendor/chardet/sbcharsetprober.py create mode 100644 venv2/lib/python2.7/site-packages/pip/_vendor/chardet/sbcsgroupprober.py create mode 100644 venv2/lib/python2.7/site-packages/pip/_vendor/chardet/sjisprober.py create mode 100644 venv2/lib/python2.7/site-packages/pip/_vendor/chardet/universaldetector.py create mode 100644 venv2/lib/python2.7/site-packages/pip/_vendor/chardet/utf8prober.py create mode 100644 venv2/lib/python2.7/site-packages/pip/_vendor/chardet/version.py create mode 100644 venv2/lib/python2.7/site-packages/pip/_vendor/colorama/__init__.py create mode 100644 venv2/lib/python2.7/site-packages/pip/_vendor/colorama/ansi.py create mode 100644 venv2/lib/python2.7/site-packages/pip/_vendor/colorama/ansitowin32.py create mode 100644 venv2/lib/python2.7/site-packages/pip/_vendor/colorama/initialise.py create mode 100644 venv2/lib/python2.7/site-packages/pip/_vendor/colorama/win32.py create mode 100644 venv2/lib/python2.7/site-packages/pip/_vendor/colorama/winterm.py create mode 100644 venv2/lib/python2.7/site-packages/pip/_vendor/distlib/__init__.py create mode 100644 venv2/lib/python2.7/site-packages/pip/_vendor/distlib/_backport/__init__.py create mode 100644 venv2/lib/python2.7/site-packages/pip/_vendor/distlib/_backport/misc.py create mode 100644 venv2/lib/python2.7/site-packages/pip/_vendor/distlib/_backport/shutil.py create mode 100644 venv2/lib/python2.7/site-packages/pip/_vendor/distlib/_backport/sysconfig.cfg create mode 100644 venv2/lib/python2.7/site-packages/pip/_vendor/distlib/_backport/sysconfig.py create mode 100644 venv2/lib/python2.7/site-packages/pip/_vendor/distlib/_backport/tarfile.py create mode 100644 venv2/lib/python2.7/site-packages/pip/_vendor/distlib/compat.py create mode 100644 venv2/lib/python2.7/site-packages/pip/_vendor/distlib/database.py create mode 100644 venv2/lib/python2.7/site-packages/pip/_vendor/distlib/index.py create mode 100644 venv2/lib/python2.7/site-packages/pip/_vendor/distlib/locators.py create mode 100644 venv2/lib/python2.7/site-packages/pip/_vendor/distlib/manifest.py create mode 100644 venv2/lib/python2.7/site-packages/pip/_vendor/distlib/markers.py create mode 100644 venv2/lib/python2.7/site-packages/pip/_vendor/distlib/metadata.py create mode 100644 venv2/lib/python2.7/site-packages/pip/_vendor/distlib/resources.py create mode 100644 venv2/lib/python2.7/site-packages/pip/_vendor/distlib/scripts.py create mode 100644 venv2/lib/python2.7/site-packages/pip/_vendor/distlib/t32.exe create mode 100644 venv2/lib/python2.7/site-packages/pip/_vendor/distlib/t64.exe create mode 100644 venv2/lib/python2.7/site-packages/pip/_vendor/distlib/util.py create mode 100644 venv2/lib/python2.7/site-packages/pip/_vendor/distlib/version.py create mode 100644 venv2/lib/python2.7/site-packages/pip/_vendor/distlib/w32.exe create mode 100644 venv2/lib/python2.7/site-packages/pip/_vendor/distlib/w64.exe create mode 100644 venv2/lib/python2.7/site-packages/pip/_vendor/distlib/wheel.py create mode 100644 venv2/lib/python2.7/site-packages/pip/_vendor/distro.py create mode 100644 venv2/lib/python2.7/site-packages/pip/_vendor/html5lib/__init__.py create mode 100644 venv2/lib/python2.7/site-packages/pip/_vendor/html5lib/_ihatexml.py create mode 100644 venv2/lib/python2.7/site-packages/pip/_vendor/html5lib/_inputstream.py create mode 100644 venv2/lib/python2.7/site-packages/pip/_vendor/html5lib/_tokenizer.py create mode 100644 venv2/lib/python2.7/site-packages/pip/_vendor/html5lib/_trie/__init__.py create mode 100644 venv2/lib/python2.7/site-packages/pip/_vendor/html5lib/_trie/_base.py create mode 100644 venv2/lib/python2.7/site-packages/pip/_vendor/html5lib/_trie/datrie.py create mode 100644 venv2/lib/python2.7/site-packages/pip/_vendor/html5lib/_trie/py.py create mode 100644 venv2/lib/python2.7/site-packages/pip/_vendor/html5lib/_utils.py create mode 100644 venv2/lib/python2.7/site-packages/pip/_vendor/html5lib/constants.py create mode 100644 venv2/lib/python2.7/site-packages/pip/_vendor/html5lib/filters/__init__.py create mode 100644 venv2/lib/python2.7/site-packages/pip/_vendor/html5lib/filters/alphabeticalattributes.py create mode 100644 venv2/lib/python2.7/site-packages/pip/_vendor/html5lib/filters/base.py create mode 100644 venv2/lib/python2.7/site-packages/pip/_vendor/html5lib/filters/inject_meta_charset.py create mode 100644 venv2/lib/python2.7/site-packages/pip/_vendor/html5lib/filters/lint.py create mode 100644 venv2/lib/python2.7/site-packages/pip/_vendor/html5lib/filters/optionaltags.py create mode 100644 venv2/lib/python2.7/site-packages/pip/_vendor/html5lib/filters/sanitizer.py create mode 100644 venv2/lib/python2.7/site-packages/pip/_vendor/html5lib/filters/whitespace.py create mode 100644 venv2/lib/python2.7/site-packages/pip/_vendor/html5lib/html5parser.py create mode 100644 venv2/lib/python2.7/site-packages/pip/_vendor/html5lib/serializer.py create mode 100644 venv2/lib/python2.7/site-packages/pip/_vendor/html5lib/treeadapters/__init__.py create mode 100644 venv2/lib/python2.7/site-packages/pip/_vendor/html5lib/treeadapters/genshi.py create mode 100644 venv2/lib/python2.7/site-packages/pip/_vendor/html5lib/treeadapters/sax.py create mode 100644 venv2/lib/python2.7/site-packages/pip/_vendor/html5lib/treebuilders/__init__.py create mode 100644 venv2/lib/python2.7/site-packages/pip/_vendor/html5lib/treebuilders/base.py create mode 100644 venv2/lib/python2.7/site-packages/pip/_vendor/html5lib/treebuilders/dom.py create mode 100644 venv2/lib/python2.7/site-packages/pip/_vendor/html5lib/treebuilders/etree.py create mode 100644 venv2/lib/python2.7/site-packages/pip/_vendor/html5lib/treebuilders/etree_lxml.py create mode 100644 venv2/lib/python2.7/site-packages/pip/_vendor/html5lib/treewalkers/__init__.py create mode 100644 venv2/lib/python2.7/site-packages/pip/_vendor/html5lib/treewalkers/base.py create mode 100644 venv2/lib/python2.7/site-packages/pip/_vendor/html5lib/treewalkers/dom.py create mode 100644 venv2/lib/python2.7/site-packages/pip/_vendor/html5lib/treewalkers/etree.py create mode 100644 venv2/lib/python2.7/site-packages/pip/_vendor/html5lib/treewalkers/etree_lxml.py create mode 100644 venv2/lib/python2.7/site-packages/pip/_vendor/html5lib/treewalkers/genshi.py create mode 100644 venv2/lib/python2.7/site-packages/pip/_vendor/idna/__init__.py create mode 100644 venv2/lib/python2.7/site-packages/pip/_vendor/idna/codec.py create mode 100644 venv2/lib/python2.7/site-packages/pip/_vendor/idna/compat.py create mode 100644 venv2/lib/python2.7/site-packages/pip/_vendor/idna/core.py create mode 100644 venv2/lib/python2.7/site-packages/pip/_vendor/idna/idnadata.py create mode 100644 venv2/lib/python2.7/site-packages/pip/_vendor/idna/intranges.py create mode 100644 venv2/lib/python2.7/site-packages/pip/_vendor/idna/package_data.py create mode 100644 venv2/lib/python2.7/site-packages/pip/_vendor/idna/uts46data.py create mode 100644 venv2/lib/python2.7/site-packages/pip/_vendor/ipaddress.py create mode 100644 venv2/lib/python2.7/site-packages/pip/_vendor/lockfile/__init__.py create mode 100644 venv2/lib/python2.7/site-packages/pip/_vendor/lockfile/linklockfile.py create mode 100644 venv2/lib/python2.7/site-packages/pip/_vendor/lockfile/mkdirlockfile.py create mode 100644 venv2/lib/python2.7/site-packages/pip/_vendor/lockfile/pidlockfile.py create mode 100644 venv2/lib/python2.7/site-packages/pip/_vendor/lockfile/sqlitelockfile.py create mode 100644 venv2/lib/python2.7/site-packages/pip/_vendor/lockfile/symlinklockfile.py create mode 100644 venv2/lib/python2.7/site-packages/pip/_vendor/msgpack/__init__.py create mode 100644 venv2/lib/python2.7/site-packages/pip/_vendor/msgpack/_version.py create mode 100644 venv2/lib/python2.7/site-packages/pip/_vendor/msgpack/exceptions.py create mode 100644 venv2/lib/python2.7/site-packages/pip/_vendor/msgpack/fallback.py create mode 100644 venv2/lib/python2.7/site-packages/pip/_vendor/packaging/__about__.py create mode 100644 venv2/lib/python2.7/site-packages/pip/_vendor/packaging/__init__.py create mode 100644 venv2/lib/python2.7/site-packages/pip/_vendor/packaging/_compat.py create mode 100644 venv2/lib/python2.7/site-packages/pip/_vendor/packaging/_structures.py create mode 100644 venv2/lib/python2.7/site-packages/pip/_vendor/packaging/markers.py create mode 100644 venv2/lib/python2.7/site-packages/pip/_vendor/packaging/requirements.py create mode 100644 venv2/lib/python2.7/site-packages/pip/_vendor/packaging/specifiers.py create mode 100644 venv2/lib/python2.7/site-packages/pip/_vendor/packaging/utils.py create mode 100644 venv2/lib/python2.7/site-packages/pip/_vendor/packaging/version.py create mode 100644 venv2/lib/python2.7/site-packages/pip/_vendor/pep517/__init__.py create mode 100644 venv2/lib/python2.7/site-packages/pip/_vendor/pep517/_in_process.py create mode 100644 venv2/lib/python2.7/site-packages/pip/_vendor/pep517/build.py create mode 100644 venv2/lib/python2.7/site-packages/pip/_vendor/pep517/check.py create mode 100644 venv2/lib/python2.7/site-packages/pip/_vendor/pep517/colorlog.py create mode 100644 venv2/lib/python2.7/site-packages/pip/_vendor/pep517/compat.py create mode 100644 venv2/lib/python2.7/site-packages/pip/_vendor/pep517/envbuild.py create mode 100644 venv2/lib/python2.7/site-packages/pip/_vendor/pep517/wrappers.py create mode 100644 venv2/lib/python2.7/site-packages/pip/_vendor/pkg_resources/__init__.py create mode 100644 venv2/lib/python2.7/site-packages/pip/_vendor/pkg_resources/py31compat.py create mode 100644 venv2/lib/python2.7/site-packages/pip/_vendor/progress/__init__.py create mode 100644 venv2/lib/python2.7/site-packages/pip/_vendor/progress/bar.py create mode 100644 venv2/lib/python2.7/site-packages/pip/_vendor/progress/counter.py create mode 100644 venv2/lib/python2.7/site-packages/pip/_vendor/progress/spinner.py create mode 100644 venv2/lib/python2.7/site-packages/pip/_vendor/pyparsing.py create mode 100644 venv2/lib/python2.7/site-packages/pip/_vendor/pytoml/__init__.py create mode 100644 venv2/lib/python2.7/site-packages/pip/_vendor/pytoml/core.py create mode 100644 venv2/lib/python2.7/site-packages/pip/_vendor/pytoml/parser.py create mode 100644 venv2/lib/python2.7/site-packages/pip/_vendor/pytoml/test.py create mode 100644 venv2/lib/python2.7/site-packages/pip/_vendor/pytoml/utils.py create mode 100644 venv2/lib/python2.7/site-packages/pip/_vendor/pytoml/writer.py create mode 100644 venv2/lib/python2.7/site-packages/pip/_vendor/requests/__init__.py create mode 100644 venv2/lib/python2.7/site-packages/pip/_vendor/requests/__version__.py create mode 100644 venv2/lib/python2.7/site-packages/pip/_vendor/requests/_internal_utils.py create mode 100644 venv2/lib/python2.7/site-packages/pip/_vendor/requests/adapters.py create mode 100644 venv2/lib/python2.7/site-packages/pip/_vendor/requests/api.py create mode 100644 venv2/lib/python2.7/site-packages/pip/_vendor/requests/auth.py create mode 100644 venv2/lib/python2.7/site-packages/pip/_vendor/requests/certs.py create mode 100644 venv2/lib/python2.7/site-packages/pip/_vendor/requests/compat.py create mode 100644 venv2/lib/python2.7/site-packages/pip/_vendor/requests/cookies.py create mode 100644 venv2/lib/python2.7/site-packages/pip/_vendor/requests/exceptions.py create mode 100644 venv2/lib/python2.7/site-packages/pip/_vendor/requests/help.py create mode 100644 venv2/lib/python2.7/site-packages/pip/_vendor/requests/hooks.py create mode 100644 venv2/lib/python2.7/site-packages/pip/_vendor/requests/models.py create mode 100644 venv2/lib/python2.7/site-packages/pip/_vendor/requests/packages.py create mode 100644 venv2/lib/python2.7/site-packages/pip/_vendor/requests/sessions.py create mode 100644 venv2/lib/python2.7/site-packages/pip/_vendor/requests/status_codes.py create mode 100644 venv2/lib/python2.7/site-packages/pip/_vendor/requests/structures.py create mode 100644 venv2/lib/python2.7/site-packages/pip/_vendor/requests/utils.py create mode 100644 venv2/lib/python2.7/site-packages/pip/_vendor/retrying.py create mode 100644 venv2/lib/python2.7/site-packages/pip/_vendor/six.py create mode 100644 venv2/lib/python2.7/site-packages/pip/_vendor/urllib3/__init__.py create mode 100644 venv2/lib/python2.7/site-packages/pip/_vendor/urllib3/_collections.py create mode 100644 venv2/lib/python2.7/site-packages/pip/_vendor/urllib3/connection.py create mode 100644 venv2/lib/python2.7/site-packages/pip/_vendor/urllib3/connectionpool.py create mode 100644 venv2/lib/python2.7/site-packages/pip/_vendor/urllib3/contrib/__init__.py create mode 100644 venv2/lib/python2.7/site-packages/pip/_vendor/urllib3/contrib/_appengine_environ.py create mode 100644 venv2/lib/python2.7/site-packages/pip/_vendor/urllib3/contrib/_securetransport/__init__.py create mode 100644 venv2/lib/python2.7/site-packages/pip/_vendor/urllib3/contrib/_securetransport/bindings.py create mode 100644 venv2/lib/python2.7/site-packages/pip/_vendor/urllib3/contrib/_securetransport/low_level.py create mode 100644 venv2/lib/python2.7/site-packages/pip/_vendor/urllib3/contrib/appengine.py create mode 100644 venv2/lib/python2.7/site-packages/pip/_vendor/urllib3/contrib/ntlmpool.py create mode 100644 venv2/lib/python2.7/site-packages/pip/_vendor/urllib3/contrib/pyopenssl.py create mode 100644 venv2/lib/python2.7/site-packages/pip/_vendor/urllib3/contrib/securetransport.py create mode 100644 venv2/lib/python2.7/site-packages/pip/_vendor/urllib3/contrib/socks.py create mode 100644 venv2/lib/python2.7/site-packages/pip/_vendor/urllib3/exceptions.py create mode 100644 venv2/lib/python2.7/site-packages/pip/_vendor/urllib3/fields.py create mode 100644 venv2/lib/python2.7/site-packages/pip/_vendor/urllib3/filepost.py create mode 100644 venv2/lib/python2.7/site-packages/pip/_vendor/urllib3/packages/__init__.py create mode 100644 venv2/lib/python2.7/site-packages/pip/_vendor/urllib3/packages/backports/__init__.py create mode 100644 venv2/lib/python2.7/site-packages/pip/_vendor/urllib3/packages/backports/makefile.py create mode 100644 venv2/lib/python2.7/site-packages/pip/_vendor/urllib3/packages/six.py create mode 100644 venv2/lib/python2.7/site-packages/pip/_vendor/urllib3/packages/ssl_match_hostname/__init__.py create mode 100644 venv2/lib/python2.7/site-packages/pip/_vendor/urllib3/packages/ssl_match_hostname/_implementation.py create mode 100644 venv2/lib/python2.7/site-packages/pip/_vendor/urllib3/poolmanager.py create mode 100644 venv2/lib/python2.7/site-packages/pip/_vendor/urllib3/request.py create mode 100644 venv2/lib/python2.7/site-packages/pip/_vendor/urllib3/response.py create mode 100644 venv2/lib/python2.7/site-packages/pip/_vendor/urllib3/util/__init__.py create mode 100644 venv2/lib/python2.7/site-packages/pip/_vendor/urllib3/util/connection.py create mode 100644 venv2/lib/python2.7/site-packages/pip/_vendor/urllib3/util/queue.py create mode 100644 venv2/lib/python2.7/site-packages/pip/_vendor/urllib3/util/request.py create mode 100644 venv2/lib/python2.7/site-packages/pip/_vendor/urllib3/util/response.py create mode 100644 venv2/lib/python2.7/site-packages/pip/_vendor/urllib3/util/retry.py create mode 100644 venv2/lib/python2.7/site-packages/pip/_vendor/urllib3/util/ssl_.py create mode 100644 venv2/lib/python2.7/site-packages/pip/_vendor/urllib3/util/timeout.py create mode 100644 venv2/lib/python2.7/site-packages/pip/_vendor/urllib3/util/url.py create mode 100644 venv2/lib/python2.7/site-packages/pip/_vendor/urllib3/util/wait.py create mode 100644 venv2/lib/python2.7/site-packages/pip/_vendor/webencodings/__init__.py create mode 100644 venv2/lib/python2.7/site-packages/pip/_vendor/webencodings/labels.py create mode 100644 venv2/lib/python2.7/site-packages/pip/_vendor/webencodings/mklabels.py create mode 100644 venv2/lib/python2.7/site-packages/pip/_vendor/webencodings/tests.py create mode 100644 venv2/lib/python2.7/site-packages/pip/_vendor/webencodings/x_user_defined.py create mode 100644 venv2/lib/python2.7/site-packages/pkg_resources/__init__.py create mode 100644 venv2/lib/python2.7/site-packages/pkg_resources/_vendor/__init__.py create mode 100644 venv2/lib/python2.7/site-packages/pkg_resources/_vendor/appdirs.py create mode 100644 venv2/lib/python2.7/site-packages/pkg_resources/_vendor/packaging/__about__.py create mode 100644 venv2/lib/python2.7/site-packages/pkg_resources/_vendor/packaging/__init__.py create mode 100644 venv2/lib/python2.7/site-packages/pkg_resources/_vendor/packaging/_compat.py create mode 100644 venv2/lib/python2.7/site-packages/pkg_resources/_vendor/packaging/_structures.py create mode 100644 venv2/lib/python2.7/site-packages/pkg_resources/_vendor/packaging/markers.py create mode 100644 venv2/lib/python2.7/site-packages/pkg_resources/_vendor/packaging/requirements.py create mode 100644 venv2/lib/python2.7/site-packages/pkg_resources/_vendor/packaging/specifiers.py create mode 100644 venv2/lib/python2.7/site-packages/pkg_resources/_vendor/packaging/utils.py create mode 100644 venv2/lib/python2.7/site-packages/pkg_resources/_vendor/packaging/version.py create mode 100644 venv2/lib/python2.7/site-packages/pkg_resources/_vendor/pyparsing.py create mode 100644 venv2/lib/python2.7/site-packages/pkg_resources/_vendor/six.py create mode 100644 venv2/lib/python2.7/site-packages/pkg_resources/extern/__init__.py create mode 100644 venv2/lib/python2.7/site-packages/pkg_resources/py31compat.py create mode 100644 venv2/lib/python2.7/site-packages/setuptools-41.0.1.dist-info/INSTALLER create mode 100644 venv2/lib/python2.7/site-packages/setuptools-41.0.1.dist-info/LICENSE create mode 100644 venv2/lib/python2.7/site-packages/setuptools-41.0.1.dist-info/METADATA create mode 100644 venv2/lib/python2.7/site-packages/setuptools-41.0.1.dist-info/RECORD create mode 100644 venv2/lib/python2.7/site-packages/setuptools-41.0.1.dist-info/WHEEL create mode 100644 venv2/lib/python2.7/site-packages/setuptools-41.0.1.dist-info/dependency_links.txt create mode 100644 venv2/lib/python2.7/site-packages/setuptools-41.0.1.dist-info/entry_points.txt create mode 100644 venv2/lib/python2.7/site-packages/setuptools-41.0.1.dist-info/top_level.txt create mode 100644 venv2/lib/python2.7/site-packages/setuptools-41.0.1.dist-info/zip-safe create mode 100644 venv2/lib/python2.7/site-packages/setuptools/__init__.py create mode 100644 venv2/lib/python2.7/site-packages/setuptools/_deprecation_warning.py create mode 100644 venv2/lib/python2.7/site-packages/setuptools/_vendor/__init__.py create mode 100644 venv2/lib/python2.7/site-packages/setuptools/_vendor/packaging/__about__.py create mode 100644 venv2/lib/python2.7/site-packages/setuptools/_vendor/packaging/__init__.py create mode 100644 venv2/lib/python2.7/site-packages/setuptools/_vendor/packaging/_compat.py create mode 100644 venv2/lib/python2.7/site-packages/setuptools/_vendor/packaging/_structures.py create mode 100644 venv2/lib/python2.7/site-packages/setuptools/_vendor/packaging/markers.py create mode 100644 venv2/lib/python2.7/site-packages/setuptools/_vendor/packaging/requirements.py create mode 100644 venv2/lib/python2.7/site-packages/setuptools/_vendor/packaging/specifiers.py create mode 100644 venv2/lib/python2.7/site-packages/setuptools/_vendor/packaging/utils.py create mode 100644 venv2/lib/python2.7/site-packages/setuptools/_vendor/packaging/version.py create mode 100644 venv2/lib/python2.7/site-packages/setuptools/_vendor/pyparsing.py create mode 100644 venv2/lib/python2.7/site-packages/setuptools/_vendor/six.py create mode 100644 venv2/lib/python2.7/site-packages/setuptools/archive_util.py create mode 100644 venv2/lib/python2.7/site-packages/setuptools/build_meta.py create mode 100644 venv2/lib/python2.7/site-packages/setuptools/cli-32.exe create mode 100644 venv2/lib/python2.7/site-packages/setuptools/cli-64.exe create mode 100644 venv2/lib/python2.7/site-packages/setuptools/cli.exe create mode 100644 venv2/lib/python2.7/site-packages/setuptools/command/__init__.py create mode 100644 venv2/lib/python2.7/site-packages/setuptools/command/alias.py create mode 100644 venv2/lib/python2.7/site-packages/setuptools/command/bdist_egg.py create mode 100644 venv2/lib/python2.7/site-packages/setuptools/command/bdist_rpm.py create mode 100644 venv2/lib/python2.7/site-packages/setuptools/command/bdist_wininst.py create mode 100644 venv2/lib/python2.7/site-packages/setuptools/command/build_clib.py create mode 100644 venv2/lib/python2.7/site-packages/setuptools/command/build_ext.py create mode 100644 venv2/lib/python2.7/site-packages/setuptools/command/build_py.py create mode 100644 venv2/lib/python2.7/site-packages/setuptools/command/develop.py create mode 100644 venv2/lib/python2.7/site-packages/setuptools/command/dist_info.py create mode 100644 venv2/lib/python2.7/site-packages/setuptools/command/easy_install.py create mode 100644 venv2/lib/python2.7/site-packages/setuptools/command/egg_info.py create mode 100644 venv2/lib/python2.7/site-packages/setuptools/command/install.py create mode 100644 venv2/lib/python2.7/site-packages/setuptools/command/install_egg_info.py create mode 100644 venv2/lib/python2.7/site-packages/setuptools/command/install_lib.py create mode 100644 venv2/lib/python2.7/site-packages/setuptools/command/install_scripts.py create mode 100644 venv2/lib/python2.7/site-packages/setuptools/command/launcher manifest.xml create mode 100644 venv2/lib/python2.7/site-packages/setuptools/command/py36compat.py create mode 100644 venv2/lib/python2.7/site-packages/setuptools/command/register.py create mode 100644 venv2/lib/python2.7/site-packages/setuptools/command/rotate.py create mode 100644 venv2/lib/python2.7/site-packages/setuptools/command/saveopts.py create mode 100644 venv2/lib/python2.7/site-packages/setuptools/command/sdist.py create mode 100644 venv2/lib/python2.7/site-packages/setuptools/command/setopt.py create mode 100644 venv2/lib/python2.7/site-packages/setuptools/command/test.py create mode 100644 venv2/lib/python2.7/site-packages/setuptools/command/upload.py create mode 100644 venv2/lib/python2.7/site-packages/setuptools/command/upload_docs.py create mode 100644 venv2/lib/python2.7/site-packages/setuptools/config.py create mode 100644 venv2/lib/python2.7/site-packages/setuptools/dep_util.py create mode 100644 venv2/lib/python2.7/site-packages/setuptools/depends.py create mode 100644 venv2/lib/python2.7/site-packages/setuptools/dist.py create mode 100644 venv2/lib/python2.7/site-packages/setuptools/extension.py create mode 100644 venv2/lib/python2.7/site-packages/setuptools/extern/__init__.py create mode 100644 venv2/lib/python2.7/site-packages/setuptools/glibc.py create mode 100644 venv2/lib/python2.7/site-packages/setuptools/glob.py create mode 100644 venv2/lib/python2.7/site-packages/setuptools/gui-32.exe create mode 100644 venv2/lib/python2.7/site-packages/setuptools/gui-64.exe create mode 100644 venv2/lib/python2.7/site-packages/setuptools/gui.exe create mode 100644 venv2/lib/python2.7/site-packages/setuptools/launch.py create mode 100644 venv2/lib/python2.7/site-packages/setuptools/lib2to3_ex.py create mode 100644 venv2/lib/python2.7/site-packages/setuptools/monkey.py create mode 100644 venv2/lib/python2.7/site-packages/setuptools/msvc.py create mode 100644 venv2/lib/python2.7/site-packages/setuptools/namespaces.py create mode 100644 venv2/lib/python2.7/site-packages/setuptools/package_index.py create mode 100644 venv2/lib/python2.7/site-packages/setuptools/pep425tags.py create mode 100644 venv2/lib/python2.7/site-packages/setuptools/py27compat.py create mode 100644 venv2/lib/python2.7/site-packages/setuptools/py31compat.py create mode 100644 venv2/lib/python2.7/site-packages/setuptools/py33compat.py create mode 100644 venv2/lib/python2.7/site-packages/setuptools/sandbox.py create mode 100644 venv2/lib/python2.7/site-packages/setuptools/script (dev).tmpl create mode 100644 venv2/lib/python2.7/site-packages/setuptools/script.tmpl create mode 100644 venv2/lib/python2.7/site-packages/setuptools/site-patch.py create mode 100644 venv2/lib/python2.7/site-packages/setuptools/ssl_support.py create mode 100644 venv2/lib/python2.7/site-packages/setuptools/unicode_utils.py create mode 100644 venv2/lib/python2.7/site-packages/setuptools/version.py create mode 100644 venv2/lib/python2.7/site-packages/setuptools/wheel.py create mode 100644 venv2/lib/python2.7/site-packages/setuptools/windows_support.py create mode 100644 venv2/lib/python2.7/site-packages/wheel-0.33.4.dist-info/INSTALLER create mode 100644 venv2/lib/python2.7/site-packages/wheel-0.33.4.dist-info/LICENSE.txt create mode 100644 venv2/lib/python2.7/site-packages/wheel-0.33.4.dist-info/METADATA create mode 100644 venv2/lib/python2.7/site-packages/wheel-0.33.4.dist-info/RECORD create mode 100644 venv2/lib/python2.7/site-packages/wheel-0.33.4.dist-info/WHEEL create mode 100644 venv2/lib/python2.7/site-packages/wheel-0.33.4.dist-info/entry_points.txt create mode 100644 venv2/lib/python2.7/site-packages/wheel-0.33.4.dist-info/top_level.txt create mode 100644 venv2/lib/python2.7/site-packages/wheel/__init__.py create mode 100644 venv2/lib/python2.7/site-packages/wheel/__main__.py create mode 100644 venv2/lib/python2.7/site-packages/wheel/bdist_wheel.py create mode 100644 venv2/lib/python2.7/site-packages/wheel/cli/__init__.py create mode 100644 venv2/lib/python2.7/site-packages/wheel/cli/convert.py create mode 100644 venv2/lib/python2.7/site-packages/wheel/cli/pack.py create mode 100644 venv2/lib/python2.7/site-packages/wheel/cli/unpack.py create mode 100644 venv2/lib/python2.7/site-packages/wheel/metadata.py create mode 100644 venv2/lib/python2.7/site-packages/wheel/pep425tags.py create mode 100644 venv2/lib/python2.7/site-packages/wheel/pkginfo.py create mode 100644 venv2/lib/python2.7/site-packages/wheel/util.py create mode 100644 venv2/lib/python2.7/site-packages/wheel/wheelfile.py create mode 100644 venv2/lib/python2.7/site.py create mode 120000 venv2/lib/python2.7/sre.py create mode 120000 venv2/lib/python2.7/sre_compile.py create mode 120000 venv2/lib/python2.7/sre_constants.py create mode 120000 venv2/lib/python2.7/sre_parse.py create mode 120000 venv2/lib/python2.7/stat.py create mode 120000 venv2/lib/python2.7/types.py create mode 120000 venv2/lib/python2.7/warnings.py create mode 120000 venv2/local/bin create mode 120000 venv2/local/lib diff --git a/processString.py b/processString.py new file mode 100644 index 0000000..41dc4c4 --- /dev/null +++ b/processString.py @@ -0,0 +1,9 @@ +def processString(s): + voltages = [] + for i in range(0, 96): + x = s[i*4:i*4+4] + x = int(x, 16)/100 + voltages.append(x) + for z in range(0, 96): + print("Cell " + str(z + 1) + "= %.2f" % (voltages[z])) + return diff --git a/venv2/bin/activate b/venv2/bin/activate new file mode 100644 index 0000000..960f35c --- /dev/null +++ b/venv2/bin/activate @@ -0,0 +1,78 @@ +# This file must be used with "source bin/activate" *from bash* +# you cannot run it directly + +deactivate () { + unset -f pydoc >/dev/null 2>&1 + + # reset old environment variables + # ! [ -z ${VAR+_} ] returns true if VAR is declared at all + if ! [ -z "${_OLD_VIRTUAL_PATH+_}" ] ; then + PATH="$_OLD_VIRTUAL_PATH" + export PATH + unset _OLD_VIRTUAL_PATH + fi + if ! [ -z "${_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-}" ] || [ -n "${ZSH_VERSION-}" ] ; then + hash -r 2>/dev/null + fi + + if ! [ -z "${_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 irrelevant variables +deactivate nondestructive + +VIRTUAL_ENV="/home/sean/Downloads/BMS-View/venv2" +export VIRTUAL_ENV + +_OLD_VIRTUAL_PATH="$PATH" +PATH="$VIRTUAL_ENV/bin:$PATH" +export PATH + +# unset PYTHONHOME if set +if ! [ -z "${PYTHONHOME+_}" ] ; then + _OLD_VIRTUAL_PYTHONHOME="$PYTHONHOME" + unset PYTHONHOME +fi + +if [ -z "${VIRTUAL_ENV_DISABLE_PROMPT-}" ] ; then + _OLD_VIRTUAL_PS1="${PS1-}" + if [ "x" != x ] ; then + PS1="${PS1-}" + else + PS1="(`basename \"$VIRTUAL_ENV\"`) ${PS1-}" + fi + export PS1 +fi + +# Make sure to unalias pydoc if it's already there +alias pydoc 2>/dev/null >/dev/null && unalias pydoc || true + +pydoc () { + python -m pydoc "$@" +} + +# 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-}" ] || [ -n "${ZSH_VERSION-}" ] ; then + hash -r 2>/dev/null +fi diff --git a/venv2/bin/activate.csh b/venv2/bin/activate.csh new file mode 100644 index 0000000..36276a9 --- /dev/null +++ b/venv2/bin/activate.csh @@ -0,0 +1,42 @@ +# This file must be used with "source bin/activate.csh" *from csh*. +# You cannot run it directly. +# Created by Davide Di Blasi . + +set newline='\ +' + +alias deactivate 'test $?_OLD_VIRTUAL_PATH != 0 && setenv PATH "$_OLD_VIRTUAL_PATH:q" && unset _OLD_VIRTUAL_PATH; rehash; test $?_OLD_VIRTUAL_PROMPT != 0 && set prompt="$_OLD_VIRTUAL_PROMPT:q" && unset _OLD_VIRTUAL_PROMPT; unsetenv VIRTUAL_ENV; test "\!:*" != "nondestructive" && unalias deactivate && unalias pydoc' + +# Unset irrelevant variables. +deactivate nondestructive + +setenv VIRTUAL_ENV "/home/sean/Downloads/BMS-View/venv2" + +set _OLD_VIRTUAL_PATH="$PATH:q" +setenv PATH "$VIRTUAL_ENV:q/bin:$PATH:q" + + + +if ("" != "") then + set env_name = "" +else + set env_name = "$VIRTUAL_ENV:t:q" +endif + +# Could be in a non-interactive environment, +# in which case, $prompt is undefined and we wouldn't +# care about the prompt anyway. +if ( $?prompt ) then + set _OLD_VIRTUAL_PROMPT="$prompt:q" +if ( "$prompt:q" =~ *"$newline:q"* ) then + : +else + set prompt = "[$env_name:q] $prompt:q" +endif +endif + +unset env_name + +alias pydoc python -m pydoc + +rehash diff --git a/venv2/bin/activate.fish b/venv2/bin/activate.fish new file mode 100644 index 0000000..4b2bc39 --- /dev/null +++ b/venv2/bin/activate.fish @@ -0,0 +1,101 @@ +# This file must be used using `source bin/activate.fish` *within a running fish ( http://fishshell.com ) session*. +# Do not run it directly. + +function _bashify_path -d "Converts a fish path to something bash can recognize" + set fishy_path $argv + set bashy_path $fishy_path[1] + for path_part in $fishy_path[2..-1] + set bashy_path "$bashy_path:$path_part" + end + echo $bashy_path +end + +function _fishify_path -d "Converts a bash path to something fish can recognize" + echo $argv | tr ':' '\n' +end + +function deactivate -d 'Exit virtualenv mode and return to the normal environment.' + # reset old environment variables + if test -n "$_OLD_VIRTUAL_PATH" + # https://github.com/fish-shell/fish-shell/issues/436 altered PATH handling + if test (echo $FISH_VERSION | tr "." "\n")[1] -lt 3 + set -gx PATH (_fishify_path $_OLD_VIRTUAL_PATH) + else + set -gx PATH $_OLD_VIRTUAL_PATH + end + 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" + # Set an empty local `$fish_function_path` to allow the removal of `fish_prompt` using `functions -e`. + set -l fish_function_path + + # Erase virtualenv's `fish_prompt` and restore the original. + functions -e fish_prompt + functions -c _old_fish_prompt fish_prompt + functions -e _old_fish_prompt + set -e _OLD_FISH_PROMPT_OVERRIDE + end + + set -e VIRTUAL_ENV + + if test "$argv[1]" != 'nondestructive' + # Self-destruct! + functions -e pydoc + functions -e deactivate + functions -e _bashify_path + functions -e _fishify_path + end +end + +# Unset irrelevant variables. +deactivate nondestructive + +set -gx VIRTUAL_ENV "/home/sean/Downloads/BMS-View/venv2" + +# https://github.com/fish-shell/fish-shell/issues/436 altered PATH handling +if test (echo $FISH_VERSION | tr "." "\n")[1] -lt 3 + set -gx _OLD_VIRTUAL_PATH (_bashify_path $PATH) +else + set -gx _OLD_VIRTUAL_PATH $PATH +end +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 + +function pydoc + python -m pydoc $argv +end + +if test -z "$VIRTUAL_ENV_DISABLE_PROMPT" + # Copy the current `fish_prompt` function as `_old_fish_prompt`. + functions -c fish_prompt _old_fish_prompt + + function fish_prompt + # Save the current $status, for fish_prompts that display it. + set -l old_status $status + + # Prompt override provided? + # If not, just prepend the environment name. + if test -n "" + printf '%s%s' "" (set_color normal) + else + printf '%s(%s) ' (set_color normal) (basename "$VIRTUAL_ENV") + end + + # Restore the original $status + echo "exit $old_status" | source + _old_fish_prompt + end + + set -gx _OLD_FISH_PROMPT_OVERRIDE "$VIRTUAL_ENV" +end diff --git a/venv2/bin/activate.ps1 b/venv2/bin/activate.ps1 new file mode 100644 index 0000000..6d8ae2a --- /dev/null +++ b/venv2/bin/activate.ps1 @@ -0,0 +1,60 @@ +# This file must be dot sourced from PoSh; you cannot run it directly. Do this: . ./activate.ps1 + +$script:THIS_PATH = $myinvocation.mycommand.path +$script:BASE_DIR = split-path (resolve-path "$THIS_PATH/..") -Parent + +function global:deactivate([switch] $NonDestructive) +{ + if (test-path variable:_OLD_VIRTUAL_PATH) + { + $env:PATH = $variable:_OLD_VIRTUAL_PATH + remove-variable "_OLD_VIRTUAL_PATH" -scope global + } + + if (test-path function:_old_virtual_prompt) + { + $function:prompt = $function:_old_virtual_prompt + remove-item function:\_old_virtual_prompt + } + + if ($env:VIRTUAL_ENV) + { + $old_env = split-path $env:VIRTUAL_ENV -leaf + remove-item env:VIRTUAL_ENV -erroraction silentlycontinue + } + + if (!$NonDestructive) + { + # Self destruct! + remove-item function:deactivate + remove-item function:pydoc + } +} + +function global:pydoc +{ + python -m pydoc $args +} + +# unset irrelevant variables +deactivate -nondestructive + +$VIRTUAL_ENV = $BASE_DIR +$env:VIRTUAL_ENV = $VIRTUAL_ENV + +$global:_OLD_VIRTUAL_PATH = $env:PATH +$env:PATH = "$env:VIRTUAL_ENV/bin:" + $env:PATH +if (!$env:VIRTUAL_ENV_DISABLE_PROMPT) +{ + function global:_old_virtual_prompt + { + "" + } + $function:_old_virtual_prompt = $function:prompt + function global:prompt + { + # Add a prefix to the current prompt, but don't discard it. + write-host "($( split-path $env:VIRTUAL_ENV -leaf )) " -nonewline + & $function:_old_virtual_prompt + } +} diff --git a/venv2/bin/activate_this.py b/venv2/bin/activate_this.py new file mode 100644 index 0000000..59b5d72 --- /dev/null +++ b/venv2/bin/activate_this.py @@ -0,0 +1,46 @@ +"""Activate virtualenv for current interpreter: + +Use exec(open(this_file).read(), {'__file__': this_file}). + +This can be used when you must use an existing Python interpreter, not the virtualenv bin/python. +""" +import os +import site +import sys + +try: + __file__ +except NameError: + raise AssertionError("You must use exec(open(this_file).read(), {'__file__': this_file}))") + +# prepend bin to PATH (this file is inside the bin directory) +bin_dir = os.path.dirname(os.path.abspath(__file__)) +os.environ["PATH"] = os.pathsep.join([bin_dir] + os.environ.get("PATH", "").split(os.pathsep)) + +base = os.path.dirname(bin_dir) + +# virtual env is right above bin directory +os.environ["VIRTUAL_ENV"] = base + +# add the virtual environments site-package to the host python import mechanism +IS_PYPY = hasattr(sys, "pypy_version_info") +IS_JYTHON = sys.platform.startswith("java") +if IS_JYTHON: + site_packages = os.path.join(base, "Lib", "site-packages") +elif IS_PYPY: + site_packages = os.path.join(base, "site-packages") +else: + IS_WIN = sys.platform == "win32" + if IS_WIN: + site_packages = os.path.join(base, "Lib", "site-packages") + else: + site_packages = os.path.join(base, "lib", "python{}".format(sys.version[:3]), "site-packages") + +prev = set(sys.path) +site.addsitedir(site_packages) +sys.real_prefix = sys.prefix +sys.prefix = base + +# Move the added items to the front of the path, in place +new = list(sys.path) +sys.path[:] = [i for i in new if i not in prev] + [i for i in new if i in prev] diff --git a/venv2/bin/easy_install b/venv2/bin/easy_install new file mode 100755 index 0000000..139a8c9 --- /dev/null +++ b/venv2/bin/easy_install @@ -0,0 +1,10 @@ +#!/home/sean/Downloads/BMS-View/venv2/bin/python2.7 +# -*- 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/venv2/bin/easy_install-2.7 b/venv2/bin/easy_install-2.7 new file mode 100755 index 0000000..139a8c9 --- /dev/null +++ b/venv2/bin/easy_install-2.7 @@ -0,0 +1,10 @@ +#!/home/sean/Downloads/BMS-View/venv2/bin/python2.7 +# -*- 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/venv2/bin/pip b/venv2/bin/pip new file mode 100755 index 0000000..979b5e0 --- /dev/null +++ b/venv2/bin/pip @@ -0,0 +1,10 @@ +#!/home/sean/Downloads/BMS-View/venv2/bin/python2.7 +# -*- coding: utf-8 -*- +import re +import sys + +from pip._internal import main + +if __name__ == '__main__': + sys.argv[0] = re.sub(r'(-script\.pyw?|\.exe)?$', '', sys.argv[0]) + sys.exit(main()) diff --git a/venv2/bin/pip2 b/venv2/bin/pip2 new file mode 100755 index 0000000..979b5e0 --- /dev/null +++ b/venv2/bin/pip2 @@ -0,0 +1,10 @@ +#!/home/sean/Downloads/BMS-View/venv2/bin/python2.7 +# -*- coding: utf-8 -*- +import re +import sys + +from pip._internal import main + +if __name__ == '__main__': + sys.argv[0] = re.sub(r'(-script\.pyw?|\.exe)?$', '', sys.argv[0]) + sys.exit(main()) diff --git a/venv2/bin/pip2.7 b/venv2/bin/pip2.7 new file mode 100755 index 0000000..979b5e0 --- /dev/null +++ b/venv2/bin/pip2.7 @@ -0,0 +1,10 @@ +#!/home/sean/Downloads/BMS-View/venv2/bin/python2.7 +# -*- coding: utf-8 -*- +import re +import sys + +from pip._internal import main + +if __name__ == '__main__': + sys.argv[0] = re.sub(r'(-script\.pyw?|\.exe)?$', '', sys.argv[0]) + sys.exit(main()) diff --git a/venv2/bin/python b/venv2/bin/python new file mode 120000 index 0000000..8f7a3c1 --- /dev/null +++ b/venv2/bin/python @@ -0,0 +1 @@ +python2.7 \ No newline at end of file diff --git a/venv2/bin/python-config b/venv2/bin/python-config new file mode 100755 index 0000000..3e01499 --- /dev/null +++ b/venv2/bin/python-config @@ -0,0 +1,78 @@ +#!/home/sean/Downloads/BMS-View/venv2/bin/python + +import sys +import getopt +import sysconfig + +valid_opts = ['prefix', 'exec-prefix', 'includes', 'libs', 'cflags', + 'ldflags', 'help'] + +if sys.version_info >= (3, 2): + valid_opts.insert(-1, 'extension-suffix') + valid_opts.append('abiflags') +if sys.version_info >= (3, 3): + valid_opts.append('configdir') + + +def exit_with_usage(code=1): + sys.stderr.write("Usage: {0} [{1}]\n".format( + sys.argv[0], '|'.join('--'+opt for opt in valid_opts))) + sys.exit(code) + +try: + opts, args = getopt.getopt(sys.argv[1:], '', valid_opts) +except getopt.error: + exit_with_usage() + +if not opts: + exit_with_usage() + +pyver = sysconfig.get_config_var('VERSION') +getvar = sysconfig.get_config_var + +opt_flags = [flag for (flag, val) in opts] + +if '--help' in opt_flags: + exit_with_usage(code=0) + +for opt in opt_flags: + if opt == '--prefix': + print(sysconfig.get_config_var('prefix')) + + elif opt == '--exec-prefix': + print(sysconfig.get_config_var('exec_prefix')) + + elif opt in ('--includes', '--cflags'): + flags = ['-I' + sysconfig.get_path('include'), + '-I' + sysconfig.get_path('platinclude')] + if opt == '--cflags': + flags.extend(getvar('CFLAGS').split()) + print(' '.join(flags)) + + elif opt in ('--libs', '--ldflags'): + abiflags = getattr(sys, 'abiflags', '') + libs = ['-lpython' + pyver + abiflags] + libs += getvar('LIBS').split() + libs += getvar('SYSLIBS').split() + # add the prefix/lib/pythonX.Y/config dir, but only if there is no + # shared library in prefix/lib/. + if opt == '--ldflags': + if not getvar('Py_ENABLE_SHARED'): + libs.insert(0, '-L' + getvar('LIBPL')) + if not getvar('PYTHONFRAMEWORK'): + libs.extend(getvar('LINKFORSHARED').split()) + print(' '.join(libs)) + + elif opt == '--extension-suffix': + ext_suffix = sysconfig.get_config_var('EXT_SUFFIX') + if ext_suffix is None: + ext_suffix = sysconfig.get_config_var('SO') + print(ext_suffix) + + elif opt == '--abiflags': + if not getattr(sys, 'abiflags', None): + exit_with_usage() + print(sys.abiflags) + + elif opt == '--configdir': + print(sysconfig.get_config_var('LIBPL')) diff --git a/venv2/bin/python2 b/venv2/bin/python2 new file mode 120000 index 0000000..8f7a3c1 --- /dev/null +++ b/venv2/bin/python2 @@ -0,0 +1 @@ +python2.7 \ No newline at end of file diff --git a/venv2/bin/python2.7 b/venv2/bin/python2.7 new file mode 100755 index 0000000000000000000000000000000000000000..f0691d4ee883da52afa53a3c0a130a87452cb640 GIT binary patch literal 3691008 zcma&P2UHW^^FF*FAQm7L6|AACAR;J;Sda)PAd0b26w#nyLlnV+1&M+RSis&P_Kv97 zTU6|*v3HGPZ{Ww?|8sX{-}5EE>^bk8^Kds$CUfV`%)PtW>@Mk@yLB-%FyI8K!gb`> zE5*SYJS7v}2=x)RI2q@|)qubCxq6%lutoqGy{p>}GsHK9)|B+BK+8V2ncmlJ>yw_) z3LdhSeNIe*uj9Ky%Slj^ea@I|YU$HDZT8=~yrR(l@8|eWXdChGb2@G6YwSR1|Mzn& zHX4lw|2d7ea{E2CD$mP`tD#-;lZAtQF7FBI=Y6c)$|zn!E5wue$WTJ>>$ZXL9uuKu zx=iHH$HuYkS8bZ}uXWo88lL8aR`^^`_#E@||F6*SxL(wsSX?|P%t9;V-F@WnLEc{O zBjemg4j(lx)h*S>+s)gnMat+Ft@v@F77qF@P{{*z?lJp4)|Z~8y9k(v*OfT^A1|CWu~6CQWt}Pr5C<) z&6HVnCB`PE%2u%|Q_hsjv$C9STW?0&JM*e{nkmcse(J>}BmeB0RRYZ<23&TwO_OHo zp0U<#R+-i48Dzk1m1&)A9i$}&wHHWiVp|@N`8bsvnCo$9fl@NNu91D9^E21?EeOt(jLDQ$@5!m{ zwg&bIE_@^Nl9d{);w-rrwVk77QcIPQslS<{sa(RH$h6^1!6zjr&GRwNQEO&Y@xN*< z`ylnN)>5O4kee>)*I}bXI>V?;vh1AI#;V4(4TJa1oUK|GrQ|vpn^ZAguI8$8Ml}rQ zhuS`sOVpfFUPUSM$nu_6&0l836<0s+;qPp(;c|k9b&_OWGc;Jzmz_cnSony@fnwoMpvZU$5IR%$}u7-`1t-V!G zi7LkumUXQ}+FOjB|PHp|X(>q{jk z@{%ORMxCTHf(<1y7tX+6WmGNunN#*&CnqD*>`Vu#EY@HVr+`IdT9d9VIZuOWO5>W> zTL*^OI&FlGU?VJ%VADNTTN}INwz67{gqDA zua<$Zu9_NHWm!oURHsqpsgp!0GqRLzY-!k8*2(lX=TxleuCy^S?PM`2+fWj4NzP3-kU@dj%jU?q zPNqsJr>T`?93e3%v~M*gc#YD&zp}PW>ZnwjI%G8tp6`&Gw~(7YZEc{v0Vlbe2h~6A zj?)}j^_I;!W6MJkHU?Zf2h;P$2He%GO`KBMR%)A<*EPz?)aG2>a~ad+j!s;bfyp3A zgWcy7*;)_?5Tpxi!`$UU0Nns#fF1xjpeKMyZ)*EM8v%#}^dl}B+WvqTdX1mpxB>Kf zAhil;V*!HzgNciSHXbk(FbpsnkOUY5NCu<;#sS6yQUOdRLOY4Jr$CzyPy(hCmkI4m zz$`!(U^ZYbAP+Dfz+@q`O90CND~MYKZ9Z)mK)VL8mR_%ib~9~nfp$A!2Vf_l2q4HV z*xn1+2RH~g3^)Qf22cY|0!jd<0cQc{08B1Gdy%#;L3;&om0n+m_6Fc4;0|&3puG=x z2zUf|3Mc~z@{G2hL;H%hUqkzrw%`vYed_|)K-PII-mx?3}6AU1XuxV0CfPi06RckKz%?1KqG(ypfR8sfQc)# zZh#g54}d4Y3&5l`v_7=mp4tx3`T}4H;QRqy03iS-U7_s;=nm*XTu*3w(KgfR4cmPH z5yVA88%5iFsbzZ6upL9M2S7WJwqv1}r$L(m$OOy;WCNJYhIS5M9=&GVeAr${ua`i(46uS;uYxuoPykQ? z)&K-q2ixoE^+ssfvFsLly%pMRfbD=C#O;E14`44~A94GkJpeciI6~YpYSqx5r0o)D z&jQW^E&wh9N&!qRL3-+(`We*i%Y*ufWwA+%KhMu4gSQ$RI9O@JAI zi8Zvf0CnhfJ!Ch?x8GuZ{41gdrX*(O**?_t9Iv3hJzyiQRz#_mB zz;eI}KmlM4U>!h^jj+85uo*O;p*;h*04OEy61A79y#lQUaEo5wp_YCA9&A4VJOpToe@ZQTpGg_LegW-E zz-z!8z+1pOzmkq%2aEtD01^RAlAs*}NCu=3Hx}9nfXRR<#HB%-4wweWATATy8Gu=U zEI>A3HefDb9v~O62*6|sv`YcY04o5i0ILD{fC9i8ztf1?Onh< zdi{{v$J9QB_Br4+y?z7jTiSjP?FT?Ppn|ynp#22+4ERDE>+je9Y_r#Y;PpR&<4>Bv z8V#*6zyx3ls19IK1KOGZGk`g95^61>wE@(o*L9$^qwRXs)`zwMpb@|x&=}AZz{D9^ z7l13k4d4!7(h6EHKx=?Ezz5JCAV>$;_NCVyq3s0lr`MgK?E(k}gaAST-2mMIazHOY zA3!)D0?-%G56~Yl01yip1Yi;m?GV5)Kms5Uz+@D(Nq}T}odWGxzyx|diP|a9rU9k` zrU9k{G61sxSpX*4)Xs)>9&O`0Q0wq|K42kW5nw4`8Gy-3YF9yp$G zf1&+H+Z>&^GN9IoT4QKU0W|<-0CRu^z!G2uumRKp)B!N5M{PrB?Ey{bbyH}aY1@Tb zrr!*;GjZh#g5CLYwbg4Pq@1@H#60kj3Q2Xp}V0y+T#0D*wcfFM8!fJs+qLutDk zv|#`_peLXgfJtv^*>)e=4u>`p5C!N5=nsei3;@Ie1_9y$Ljg>NK|36f02m2K1dIVB z15yBE0pkGU0jU5%roeU@U@AcQKm0VR+7am9ndI z1DD_W^TlPypv;y>kB`fbn}4CLol!*lkh3k`*0CJvo6*mC<+NuXj;+4Ae_rgxdDZ&t znCVjDDgC+P$^JmDEws~ijUSc!>Dy{M<7Nwo{u=k{N9d4FJqmmruSIaDCN8?t@cNcc zNmH{H*G_%8-=yP@VsGWFaGd=tO*_iLOyv~tEo$EHy;_Zo#HMqO9`iaaLov-sFqlh%E$N0$8CY!dHp zTQzQO$LlXvEu*bGOW&Ex+^j-}qPgg+0EwOpa*LIJmd% zev{;Lq3@h(g+1;5XS3&?)*nk7jych??WfD7vQjteTjd?&4lXM@v*hcWZ86ur-j!=2j$Kp!XWfrixqqBfj!o_E{^IW7dwstK*{|>4`~JuY%MQfaJ=>RwXItZJl>?wwJNPX8g}$Z zz4G{F-C7S%-070+o<5s2vmS-+%F#@q0D~pX&Yk_La%!mR=5T()Hhm zXTHN4#;o-$XcSYYeErp7X{$R8w_m=yiZ(dYuxQ?{;7$2u37hhLm&pdFZLkfqzLjn| zETx~MX@vRak!@-n9iK7r&H3$(df&N9Ba^KI=g@8LPm-*I;&>2;zkU%xP}U-o5*U%imCri%w4_U4VA$!Bb$FazuJwJ{9h|GIs4fS@xq>K}GrH=}m2ByZ%aZEq-`CN`Crw)bq|?SKcqWx?bbd#lY@+ zHItP?H$^P5%P&$oM_-XF44>B}^losQyN<)(E-2X*>vq%VmMrLBgiTmtWVO6^lG66e z`)950`Y54zPy657;W`7LgtFb&zjsT^-etdaN+8+_v+kesCx8r`karWdrdC+&?<3&YW&}`zTd|yj{fR;ep&~k zX-*dDwMX{~X|F!^>e=~qL*JM!vH8^@eOt+dt&w{o=jScSxqtsv{F~i7i)%V54Rb4& zSIG)mG(fSf^`B=C;tH$opZh~O;p>_KUzWE#-_LB@E>HK+OE==z=eIwpa0{9p_i@F} zo@=`O_B+_vxB2+aYZYPX-y|nz2NN1iUrbTO)q>e?BsZ7 zOjP|#xkdBd)v3Lse5Bu;@4aRo?;CwD=*M%DL95GdJfC5{&E`YE^n@lm0_xP8eEnO& zfZz!oo`7Wi_ehc4tF=nCKjliTTSm_6#BhKw+DmDkD1(7WG;^VJ#B2* zZU4|eT z4M$LXtg|2DL57xwd2y_wmy>KKm9 zZ?>$+JIT8F+4Ii%aR*$Q-s`jI-hnkuQn#Eqy>Fs%izyGDzfVq&JbEO$(7kWrV88i2 zYbqTZ{jKTV^T3Fk14j3J_Ofr|cL|r>hCevE&+|*O@}!Hi?%g@?Wp`h{l_&EC?0j-+ zvDNK&^_n})x=|-%#L@{YaLm3EQ`Eq+`n4{_ONdn>5q`>#lcg@>-z>wGmR?ZqhT z@#9veM7w={+O>z>^CfvhPuv}K`nlAhXTa5zl*rcgjo0>0E!W1(y~K-o4$a@WP|N?RPfYX>%>()AN*5FE-3R=ixS^^*tN2$-BDMpA{PRYQv;G z3v3TIpS-v4?)ht0S*OlEyz}}hrw&EmYi%1BYp}(7p^<9z2#3%~3o8C!6s z;)O$FcYlx0A2I!1zk$PwkE^SNRIiBgf3x#Qe(<;BJKy)%*lNwKky4-ZQF$r*BHtT) zk1jr(8r|4?!sRE2zRoQ@SGvr-LBwt|mkIlZ9=x`wyvyPH69=|mHphE{<%m_aTDy7& z)o(fR=(6^j-}cI^2OP6_65bt50@>NGCys|?CAK#cUH$=(GHotr~181W%(Pz zx<|DecIo*_=W}Vt`VMi}x%Ihy&0BL0*n6!wa=YAWmtjs`%I&)Tt0%ie#H7^Nuy{qo z;S=mLw@~!d?2j)ufH7=*7R{PzIW`G-=M&tbC#YbX2s;!k;IkQKe#pI#kri^M0IFZ z^3(qwdVPwS_3{11sGUO+_bpSs2z`A{_Wp0-;y3nJs{L`y&)k>cRDIFZ>3wRQ`S7#p z%k@LX#jf7sH?pYsY`+D&#-G1=IVZ^N<;dGHZdVe&MAfT$*xS?Ub(<~!dMl@$w&^`; zjbE>g5}(wE7tVx8ruVA*di4UMYE!h4kG=(V=;Yh?P2899*IRv=7rAA&MYVw&)9hU9 zpZ_*I>6^smLe(D~l4XUNqhEKP9rahs)gltdFc)zFw|%+cwep@uWWybE}4?t!|ezvCFUM zfQ#)Miw6u3-MOvKx>t##Mr+uEZvwO<1rBh$37aMFpbIf6$#qF?NyA#q^Oc`|Q zdwKY%*RQ1C-QOD@JTt$+oBY|Fxo^layRq)C?Kh3EXuIuJhm3u{I`+^m4huE=SLN-} zl;ukwgeZ=g+b3JA)cd-+$?yoV)A-7rTvv) zzvG)c&;I;3HTcO!({u0EZk#jt?fS!9g{n)?)P9D??!Wo-HYjeC#VqeH>TZh<48LUm zq*T*-y7lYbXI!Ub|H_^dS4&bmVy{!-%MOyGyA6)qOIdLB+kmW~0z*~ogVEnF?;iZ8 z|AZCNnxR9?d)9fmuc7&g+@-lGihb5Dp2kB{&J}-|y47||gADhyMs4Q=yZ20g^{q=$ za`B$LjuUR24vgNg^Fhl^eb4+C=xMDj{IKTo+#aJEXIZ%1b9;7Tu4%#SdGnP%X7SfY zrPuk{BI2l|Nz0`*G_{jnHJ!R!-fwf~<5rD_O+Do~qrkJr)utuhHGIFEXtOSKM2v61 zOTR^i#?nqDJBlr=a|?X(?P9d9tDjhxHMV&hcyQ>y8fMuw6)$$AkEot#D2qD4{k}VM zu3>1ReM;EgJqPxVw4S@Z*M4iC1~>Yp8g~3>{(NuUpqOCidU^c{9zN@Jtnd7P4Y$tw z^YVoH;*+Dv4L2MaYQ15ew3{+NdSUP5`>h6@dQq!}U$gXD!~b3xIleGz@ziE7$JH+} zTW#p`ulkQ8{cM(4?5{Z6ZoBW2K6_?YeKW@2%xB%#fD4-V%ZWMVE4Cd_rM78j-6|q| z=ep*LJU`lx3+Z_6$$<9W0r%!4A1QBt?Dp}T$e;IaCEtV??+7hZ&+TDs>!(;`enV4l zwWDKJVpyx`dCBK%dA8rTTK3)Tk~>2#EH2wbxoZz>l3<7yb;;UY^n5WBcSAuihO>zN$+2*V<}KNXmqd z@#dkI+iohj_oO`jW6PYFw_e4&OZH?|bmT_IF8A53_FZf0(rtUlZ=I#Th zlFaYP3yyx7*?ZmEi+PVs8pRwe(Q-HDW`BCR)trKfh~j$M2p^Hakh&(kA*1lOJX+RXjvoMWX$=ev$eiQFGM)6cX|(Ae^) z)=jHgmTWj!mZ~*fUuZTk(W7c``Vgy%w^3Z|g|HE=O%5G7`@7fb)kbOctCysHzGmh* z{k5g>frRiivgCZ<&GjutHEaKHYwViX3kgL_Ozzb4{b*8F5D=7{lvShN(f;Q$LX5w3 zzhJg+#h8Kd-CXaRp0`PhOsaLnXv>=g^ViOjy)Qpquq5|w^8qWXPV91R)42r2tXhRn za@@Ly`$TN((tgL)9gXZ?50Y!vOxW|VON%7Qv9Xr3K7Rc7yjEw|hvCwc(jfn+_w&xG z9IH+Qe&z(f(R-byPj~(~CQ`At-n_vRFAX&Motb&^8>G3|#O=S-T8anU) z*y%F&J^F{ul45OxOS=jdtb0*S8ryi%@LN+)Zn}~6{H;m&v|i;)bBk!R+K9Kf^gy zqModYJaXG$X|v9Q9v|AUYWR@e-$D*NUp;n`t30@815eEgo4KlO<9^p45HZ`lUGE0b z=C%gce=K)s-1x-C=-_u(+F!}sy>?x|REssEeq7%Dda~8=mj*B6{IY6Ji2P8PRqcHL zdUxz^R=v4)^YD6&Lk(J+t24SM>{QLWDS2_v;=7?i)u1ghtHzU_-qHq@rcJxlXQEM~ zQJI&FrY=5rxcaI+BW_OKR?6-3oq2p(MyprbnmG=spOg0YmZJ4*yH1C%&hywfyR7&A z5jU%r+YNeCJFfIn^i=iHKEMB*4BT?Mu)%`pt@1%7eheWx^7p+e?=m+wGXK$p&9bJ$PrkBMKiivHc*Ei4x{2`%^52@gc+>95 zM(3Qc*~(Jx)ajpzcfS6Ltd(ng;=&GN|6ZZ`Ffule#}@uUV}Lm$|B%sprA@~%-Ar{Z9<7Qz4Z-L>xZ$*>KV8?M>dum3um#Zwl~ zo*uvEqQ&X2!(2}9GIZ%#JSKhS%7zd97p1vwemo*7cWL0grm?OQe*bA2^ry-1OOD%> z+rIfVbdbdiyYRWV`r!??_V}LZ`!%vf)`hME^CY2FEc0d-e>aZ&5nf?Fc1Kp*Mg`0L zk9|E+{ASyr{Mi9hKd-Hwzo+BvSy7tE)V15Z!}gkFOdhPr8=U%G^Wo4vb92(CCP5oYE|2=ieGN zyiR{D$qW9oZ}qktEgjamsyh$=n_9NEyXyCjwkex@4qYgG*`VNJeA@BiJ+}`p9eix@ z>%1>XTQ}8{1gRYox~4^)yt%sUS7iCiWj1X!XEtA&GX0s~QD>6}Uv`?TjGcLVSDm(- zN)A;Ny56^5ICV~lOY0{`J0}#+Z0i0!>|=?n?WCePVaATfW$)V>UH2Xq@;c6D#X`FY zH|z&L_|SX6$5V?fH{}?fw|wR~=)pySeut_p7D)mG!l=ZMIA?A2>R*w~f{P zWizfAKc7%mZQg{$J$*0TcwbsRDl+wE;0TjP?N4_&lJE3S5jQ37;LAfVHY~|aZ*#HJ zI(3(pJMPS1Kd8Z|vr)C~jB_xX&;2%AwCu%>PTON@yA+w-ZrOCS-R~-~{kybH2=>}g zXH9HSy~(dfFYH;g@u;m;=-Jh>&|Qa92YP8t`t05IZ*`rF*>g|$t`*M9>TB8WkXCx$ zSXNy>&#u+cr_X+C(8sfLzWVsXa5=0#J{YbY(8r&G3qSSo?79$rJo_zOAAd+>T!%&C z@E$I_)n_M4WV|2x=+`fX80)jcejn7w=ZWy=MfzpGspzwFNu*z2k#QXo;TMbKu`yiU ztgl~ojha3_Qbd19J^lL6Adi-M>eE^zkL>z7ef^q?@P|a=(^{l{c8bIyUxa7BZR_(V zO{8DAc2TeX7?J$G6NxAL-CbY5$3^B#TM<4BjOeq|OT?{_sV=ZEb{pPE0 zTmwb&t`@1!OcB1h$b4)g;wL9k4<#aegoyq1aJ!1Waj|PC^zoZS#v36ru2v#-9xh^M zfJmGditzVE){T`S_Jc%t_WP~A@eUQ~_mPO5lOlFTh}0GPJy~DB>^EwCe6~m)CyLl_ zERsi05ndxQ-YO#f{u9Z2J+XC0ByNpF_%0&jx+cOm5UIDTBJ-%B$hg?`aQgO}P9k~P zD8hFXS)YfA#DA2C|5}mpE)?-UOoaCnu^%B~KTbrSU4x~sK7&Q#%&yPUr{7z|KD#DW zpT3pI`qo^e-@PJn$PyWEKaqTO5V6Cqo73l~M#N8corXSre-S=Sq+e4J`|SP$efFP; z%rAD`k6wKdJM20#efset@xLLmKCo*@_1SR~vGYr0yzH8HeRkOOkNW1XS|pzA+BSW5 z*fqcU_#_eDU&Mb;k$OuJ(P!5U>Ff8Ph(5dDL!bTzk@~DI63?$9<2oh6JBiq7FQU(` zDb?raM3ML_MeJ`2)^BHxNPNmf_%KN#qNjDH{J-5JkAlB*AGSdW!HY|v(N79(8sg;n)LB^MB=$!B>tyG=6g$# zer-hjd@eHH5RrcW6VZ1N@qdBHc-eJ<`r@!d#7?qEzuQIlgCgVY>#4ur9FciyCF197 zk@y@H885qTT;F)veINRGc8{4po?UCNk2e&Fo0UjC^zr9K{InL)|16TPhQRAP-e&ig=F43u3wrhQMdCSBq^{UKiu(E;B@(xXB7SZY8E^I={r2~X_+LZB|2HD~?0EtD z{Lc~@*JDVyKK*6A^z++9{87RQQ+;+^MCN0TNPNOX^1|*r)@R>Vguf<|uM;Bvj}hV7 zeY5)dtqO70w?0^k)B}4yf<8NQMf!ay61M>@_1j_hUF);26tUAu#Ghv(aoZcj#%s$- z7q#M-h%ER#lm3HPzvf)#G<W_4}f}$DLZpkpDji zBcB%nKPZD-gnTop+7Efv2aGBVhnI91uO(;4X%~S5Am}aJTONn{YIFGE62yz%SPMnM z;*-1`{V^l^#?-G|iu$hR{KvQ>9Z;V;(10g;K|Yv_?1udw(CPOYe2&>yCD!5fRQzAK zZ&`}GGy(zwQkVY=_cbOUU$GhEAcJ|xq&oGh7=?BeJPY^tL3o*ctQ$lJbIl(+IB_$5!DY&H(q=L(4V5_mHk;T5cN+kM1Sl_ze6nYnw40$ z7R2v|@?h~%{D=L*^yj4!jF<7VXP_NnUNo>q-lzyi3f=J%*B<$b>ac?Y8PETPdo7`W znVqDrn8%C6$Ae=m{_<9p{ht7K7_VMiIWN`y&`yyYSFJB(Cq{;RS0#FmvjFZV3`Aag z8T+O3`L2{uhnFenE|w^)O5}kD_$y*^c^EUBOBd z=5IZC$?V7nV%^%29Uq7T<28BcIaV*+=Ng9osP7%%&S8Lw}Ed>N#h@k?pF>VY&D!LQGduSkrWa6jm0A;0cew`~8xq?qbo zdkpJmKGNJ-N3<_bhk^kaN&M;V$ZK1oeIv5-6BbgI_dK0?umd}c=X_f87KC-XEv*~c zcQ~)vGjf>JZ$b87;CR{fA58ke0?Wob&K&Dmi04Wf^3oXe^AoQP_iNTCegN_hi62Yx zRMf;cvuDjPc?0#p?Duy>`_4#nS>ecgd@2A3etCnfbf3Kz`H`PuSX{tcMe1XXaqEpJ$Hs5K4S`AIAS* znL_hDO2@vhC-!@wHf(?h^*My{zS0Ea;7%qZs6N|QNB;lo3pQ9BJdBVJGQba5^haL$ z19|rQ3X>SBA88zp%N}X2Q9snzoW?u~&l%c6aaMK4Ix!~wKqyGo@5W!a2ghDuy@CQ@ zJePqU4ki5|lwY+8<|UMN{C>3lhu1(0LOt&UH<+EIDY#!~N_G}N0W)590pp)b{B~N; zi$YL;Ao1g)P~W2h*A2`*+_Rp5@s}R19RCNTubP4Ljy=PR$z1q2>(^r+?icP-oa^}@ zU(^QseTwRwCDohq434W8@rP*Lt7(Gsu`Ti2X#PrDU|!hs&6w<*w zFc`1tig94iA7FAe26^u^>^B8z&XMLttj>D)&jIz5{4l@ld2UQP_#z)s6YaD?n%hao zZ`w@_ct*&}HyA$ePZ-uuAlX?;^`LryE4FaF7dHa+i(X(ng>~CThP;A~H-ve;3lx}r zSq%0o^^^-(ymXMU!4#ts)!{ak?k5%W`~<7ZV!E7O;(K|e8@@a!e3!?jc= z!hT`tAk>%jKz&#)c)0-6iXB%dbdL811fafpCFW%->0gF=wuFb3s(hq8Go5&2+D~am z;qicm_{kLi6~>k8Havv=t<$57U9!QH;fLjUxU5t&>T47$4zysTTy0 z>F1iEKSCVpQQfMm<8g&0cEc$kU%a2~aNeyT`wb}$+NMx=AjQN-d7=NRxt0Cz)B^3u zNne=1?&RnA#psU^x86Nbzr_^fkCXkyFmUFNdIma}De#bP#(PY`IIw5ZF=>*3e3FL^ z|JsG}Rd+D*5(_98kPK74;c7u%*gXBNQ=iUMpVBh)r#h;0hhW{~^F`0)hTQ=#pCIqd zPW?1wjEHXw17v)Wj{RI=UWa1c`ci-%k)P@-IKRe_ooN(L*%Zug81ab`92ZC52O+$? zEbohcs&u|zp9wki}Eh8t0>u?Chd^A!=1 znH?2PHx7tX_B?PAKvfx2<^1Lb8(wd1T}IO6M_Pn()$% z@}(S(eqwe&w;9G?+NAQl2y2UWI6tgUC_i4DdmyiAjPYzwe3PNbC)wb<>qWeMYviTR z@Hix#{B#+Cyy_5-5AcOm;XJ0Y*N$GpEG{wU2a?Q@)8!f~<&)YyJd+gi$Nz94;HH>|@v1FSwHmvD}7JI*#ysn z8-YA0!MZ9WGn^Chl0C2>fZ!`H0bg=g_&MzJ~(W6G%5NpSmHh zrSmzRh~Eh1$Ly#!b@N6?$SM%0lJsc!`1+7aShEM zzRwEtx_(RaKUL@Z@>R;aq6|HEBl{z1-H=bj^=&lqPh+rO`2~y*&SH2Lv=sSzDokuu z(!WLeA#HJ{!?faMK`+!-9z#20DUYdg#QoV!M)^jSv=oolM8d z6y|TeC>k%Vhr<3&>xg!=yRkkYEWAX*y2|Xl(m8LxDTwSRSGI2#fOb4=v7XuWrc9bB z$j`SpuK7F*=eeP5nE#p-oayeQzmDQBS%Q_op1;myK9m8|-%01|&|7$hD@=37%U`1% z2qQ10l&`CC7-tC$_yVjaY+Tx{mHjueVs`$oBvHS4^DxfBdEh^^&LmAoKZQEK>xA}g zb?Pb%;?Mk4cECKsOJ0%`$Pb-^6H8e4cEC%fzmdK_3hVQk;bg}a`(@7rWMV>f;xPr+ zy-`SW=O7^bey6RJXQmK8$QJG7#pAeOnd0RHyyX4di1Wyv_!tjn2ZFJ%@_y<)tv|}G z7(H*&Pk{Pic5K_@xGoZ3jpF%a74j9tw{=B*JDuYJzew`41oegW^EBwQJSqpY;w=bu z9ti$0UbU$*Ump&@m_PC#7-3=Fy$MD>=_cBDp*q~?jJztj5pTa5%v&a-0+E;bRF02% zcd}3C1Mgwkar+@|tY7#C4gBZX^Rk(2r8pPmV*GJ-!1L=VFWS}UXBSlG?7(3*F2(xF z<66t-1?t(MGJluya#n@ono4$B)A}j##&NA7eh$23ekQep1c3FB0E?_EVB9+)v@?;MuXh zsNb2+S5R6wXP8i|U#SNDUqH8EdCtOPyK3{J`D#zcB>PlvS{y(5~tb7>SSJL;0FtUHM8}g(7Rp*((#4ix) zmd?L5B|f$T>OZ98z0Sn%q2mw@{k{Nh@G=MbW%GAc2b{lx|9?ZtA1$t6&B;$L0eN*x z^j~-`_h2vNx%L=mPqKfW##N-j^=%IE4PoJ8@swv)&f|oU$V;mrZ%^@G1_qga(Hkq? z0)(BHLlg&TC>|G`C4Mu-p=c=D5#lp45$zo6jeC5-&mt-E+V$wCaD3Gb3X=7k)D7zy zvjER!rEzUIgasw=k6}JC{fcS04m*-Rk0_oMKhgg<;q0MlB!R)B%Jd$vH z(vs${hcWs&j_jQDLH!x@dmPSUc;+w7)1PB-GEAKvq|kAJ5kHjjl4mCRG zC2GM$W^s^R#&vHteZ~*;8L$0n%`@zJP$o~Dk=L*v=0ITj^Rg)z`LHTA_}5Q~j|(Av zvL8=;U^C>C*ntxWgo~F5$|L8E_Jwsr3F%<|bm)loJCXi9TE~*Ep#KyGt{<&S9)+04 zG}13kKs(Aim={}`ci+j5^eX0Y1?f*DKh=xSA4nfBY?|}+L;cdS$7R_fFKvwV8EJ_6 z>5wL-FZacM+5c@}5(>)~<24_^APAHnFF)OozuFZ?WJ>zqXr1YI6)Sc#>31f76oI(E z5PqMn=Y@5m^_KFQ(@DP#B!cy;(%GLjfB~{~)mi5_>ef)?D|C)a0|sGTNp;re8nkYB z(0T9uqB!o57wCY-;`g;d-Uff@%tbw zIHQT6rYAVe#*^6opo$`XtlA?B+i^?S7; z@-hotH-tQX>V$mKK&*#Dq(6}MQ_=yLmu#9ZmN4&_{YZ8pD9BXO9|iTsjw>p3;&2So z!FVlwU!bz$20~bvon^Kdd11YZf`l?&dISAo*BLW;?1sGZBrIqk^$q!k`=~(s^>ya^ zUkD4c^Ya<5OPB@te+IN(HI!n%LVcP;nK6CEU|bI)(LCoZ_@hF9_7gwE0rm4XVSF$P z@cjDT$g6W8;2^kK!n5)zZrAC$IQTd(2NO_#3Vol&sKWEqJuts_JX`a6f}dFJ7K@$=7V+$sGeiVP80IS zgN~zIh|h-&7N5>*u>SiKf2a%cP1p_HAa$sI`qO&INigs3xU6$yp-%YY`4r5oFi-D+ z8nf>)2nq(|GXEFO`@wM$i@#Lo_m?utuOtoQ_MG%@c11gq5absVAKV>zZYR!DC_i4b zl$X43xDMD89~grAiu+jMFl~96LH$;2!}ahn@!Q;ST&6nfO!Hv0BOQ%)gnpMm8SwKW z4C`FT%Rz9D@kR7~(3k9wp?Q&X7xz1NtKb`NXg!}`jCpB91;^Bw9i>kF%!6`eeDG$R z5lLjf0uI0!pVtuUGl=+)Esi0C##(h+%&^B8Au%40wW8aQMMXC*s%B_Z?^ADD&U)X;o zVIGD3k&6fFYjd$a-;n*$k;r=t#sw0y4A*VaI#BT!GbPk{Of>0l#&P+Rofw)g&FTM4 z3-fCLHmT zd-TTmtRwpkAur61{wa8Ue87U!7MK`Xg@0%gn4l%z7!l_ z`YUL^Cd~H{@Nvdl&_B-->b4l>FXNq@D%+n8{j&IIGb+!ccd$ONyxZvH{T@s}W~XQa zuKX*=pYaeD#{VRL5{NH@X~lT82IJO-_!l1R{1xMbJp#UAN%gFy^8n}-{0}>r&dg5d zP@G2)US6`G-k6^f+7AkG4h}~BPg`)sh@<++q2nc0xds3E*<>dTUh;mj8)-rCFMPkC z{Y5V&#?6WB`10!&_`kez+|-muE*;}Dh3wnGc$t0GRNTM7^2LiG%sX}-PeRv46V2Im zM18pd?k{rajTA7;>_{weeN)hJAUl1{czGn+7vgi2>PJP#nL<2&!;XXLS3HCT69mee zm%Y7^SGB}`t%$D!{jxfdjjKG~R{^M>bRX;a9_gQ}i@fqxeV&0Z@*;I5eRjbHh_LgtHjL5#k1J|Dxl&@3XXybXJqvIg)kq7K))<*Lz1zswvZiHH{`W6-oeBlf(gjyL8cZCMIAJ1J!n4SY5@OhG?w*i$KBD{kIw`*n0?hD7zoH#TDR*AL;j>6_Ulge3!Tt^ z>8#4}$tOE%x*qQV>7S!{+K8^7hqB>iFwN6vw11gT{Lf(QS5l1gXfYMmt0?SOu5+F+ z3Ce@{fBLy8Z{a7|u>*%0|7kM%-=6q-5MIV}$8bFfB)*0TpM7pZxsK1^J2`%*#~bd-q0OKCp6~geH<5TCart-Kt*5Z=ve|q5ODBhmSLVV%CGh zAh3+^vf3VbWqXX9A?4A{o9sK{JYxTInn_ivlO!oDuprPKFAl>|U*mz}V)y4WQ9;O9 z-TLSp7mcBKO6Yvx095DdP(4d#Va83X;2U3IA~XMG8tfN)f&bkM^Mdgn^N<-sacdY# z{!d5#H}M>#mFZug@7-C{?`v2;Se*vGsSVeIC z39JLme#Il4L7CKVE2^ssLtF>IEH68Sy#K+v%BMI-c0oJxg&2nln!v^*(H})p<$0ur zkF$O?ipuN2X$Tkdlhe7+sI4RFCwVsH{eZOc(vSR4qPV#dZ$RtqN2+IBb>VlnK(r&H z<5;XB*nd*}=jqfZJI%}Ii>~8^Y0k?w*kHW&1g~PyK?~qnr8cC7Qi^KR-qc|5s99VwUE|ufimFh%F=T8ojo#uV0-=8>7adyCc z*b&I9S7N@dP@!ai0<(Wkr~apqokg^c$;rMqtXIr`S$jw*NCNRrwEwE;jr&buyx~xO zOkd3&dZL;&NAM%p*=zlixv#36`JuuE%s<%3^s4u7E+uo%Aw?FcV z-smS*5#0V6isO~q;=I^Q`sq+e%+IbVxQ9ANe7+QU?gQp!HSx}!kRP}i5&@D%d_h;_ zRsPs-Kbl|HpgR`lJo-LvOZxd#KipDWKVe$&Qbh4roku%6iLXKRDP4g2H;F$p1p9qO zzXxHI;D4Y(xw82sAC2P`;_prCQc?aG1qMOY;k(1mCB_k+1k_ z#ap1VvzqSbbg)-*lb`a|4eZqA^SRbZR9%Z4}uaxu;4rB3`aMDa-Xr2xw zf5y{%#8n#pN3jC!58s6o0$%dc(gpb>`X2g#cyo%UW)`jt?0Mx(c0j)@J}T&)iBRXT zO!B<^5XJ#}f$!NcZJGWT`W*+BUtZSsK)##K_txs2kgrO=+q#kd1=z7NJ05g>X$#^f z60f?1b;A5$(vIR*@fp_#oCQ#ab!v{e|k^b@!Hb-v4pYc} z9^KD^*5H3>^K}UO9lBmc7*{eZ7yR{;UQ)h4nC85^gT%3Z)8)7_2=PzqiM%Qezt_Z* ze!MU*nqz!~^~8T|Y84qb<_9Mt2A)XZ-kT0V1MOa;M+5sG3 z`Zsj0TZjM!#!LER{TP$|+Z0b3yU-UTkNoTo!(sao)hSP&7k=-k3IS(!q<67SgnC;E zb;$IUI{Wu1s!xx3xMzIDTZ8i_kY8rUOUKVJs7J;tj^N69hV%!)#AiHr7WuX0=W{vo zN)6_5CF%DO_V1}!X_JY!fOIfBN&9hLS0Ottj>vnMqJ1Hc7a$#MTwKFeyagc+azE6U z?!Z1QIb)#q;$%&d^#^r2{9Q-uDo|L4tQIV@1j|4;id z{z4v~QC(@Gunq;@kMgBDj`{URGu+?iXh+f<<0Gf~Zw78N`!c$p`Wo>^+96-0^E>-& znsG*8a%L-eY_)dcZ^3v%C+}spoxA zW-LA)R6pn~$0@>4Up5^A2qM%;&uG+FIANX0X<%!p9^Owue;|FlytGICJNC$rCcd6G z+UK0XF%V&$90PUE{8a1sX%Ew!`N<8$xE-N>Z*)OkDaZK&;o>D5>@a<~d0qZ>8tEHE zqJ1rW?-SzFeHiL1E?|AK|Hr^2G=S+#;J!jB|9UR}7xD>VVfH`I#Bqfa-`D|pbvLY^ zT2ycGuv{~J=?UyHnDm=Kn%H=gbjJIa)p$pd-Lw{pE%!2II%>sRKzsLs__-~a#w%7B-fvDmNj zjSc@=xS!pK{L#|yv+TKtOm35X&aJZjr{E_WubQqmhOfiC%!H2Eyp~S#;a>}N`!@{r zudKy-fb!-g0VWi)rRPhy z5dVnQfwmf43C9utx;5ID(D{?;#IGZLxewY0_jp<7g#N3PmE+d87uq?~+=SPwN%eCG z)L4EMOJRb82>Z= zGKlPyVI2zR1An?=d^j!67gFc`Ym53473vG;Mc+c%Gk;|FP#;IZasIH3@c!$3uMdkv zednoIZ@AjPc~P>%{enb;gjd5r9HhJ`==aFN?pk&hL8uu&gnEwDf!TAvD9C z>5Y7m3fBiP%S(4y$C&;yx(*Plk>i#mARnH9j)2>|Jck4^{Ufwq;a(MU&n@5uAfJg_oQhUhhWlg)*kItqc{l1jYc%yJUXt6hWW;17>!F!_l-VAnmZ03 z6l#DdzA2}8dbGuPnreveE+_jk4aUuc_+R1Zk7PFl7GxBvb6?|-&(V2~P`Evg*P|8$ z3Pe~pCb*%#>=e!m;eHcynitCHX#X6o8-}!=Y@_1@VV%4oM>}$Qo>DRSnN4|=(fv4u zDQdH)#v@JCO9(z;O<%+lpbZLkAJ^GLP&lyJ38w{CN2XWx(uIgk!&$O^&-u z`!C5Cw11!c><+s>ew~S}Jgx*t9OG4Vf3(nV=bmU^`WWLmj_d?OVEKOO_s3@B&-$Tg zN8wu8enVL9n0~3w_wgDK7RJx}gFV7@=H&z(XL1_cUzbq78!7&hTsvNGKIz|u>B;OQ z(e-u1h+iFwe9<|aM+)MnQXQ7k^(3yuUxhd@JMv~w03gkYH*JA@MF;FxnD5=_c&z9N zt`FD?9LIJ-eRVO$U)T>0rFp9Ef%8aMSKEUEi~lQ~^*@^OD5d97K-u%MsUP`C_gQ-p z|EW6iGn=A^m^JuaiTvNe9v}=N_-O$PH6J%WYn~s)|ApITA?%Eo==^Rdp?XdV!u=O4 zr@V}SbTfVK2*%+t)p;SsVHZ6Q4cy>mANk`v4?XlD-r64R6w!TT?EefgxeWv4{VBi& zbu7|c8cY+$d$h)UHK4@&2ti&x6(f(c6`teU0r^)|v0pem;DtS=mBnAHbNxe1EY=Nq5no{|wMhfAU9_iF|$Ht*M@q=y`^z)NeM}XZ|a-5LghQ{_|*FtG;4h zI+DH%%{$E*tiwwt_(m71TXWhU34S&RB0uA?-k@xFaizRh9KiTsHsSJsL8vdW$93`% z>0fYSaj@fzboQq+Vd69YW!EtNC8VE3`IWzbfqc~s*HDSRJn3jtyNbEDD!YP>bLPd^m- zq@kEc8S&CK$mh}bH5eu@2Z*=Ox!xc;9{ZK8s=OX*fM@+mCS!iB$R3bY1#9;@i=BsHN+gdK15c z)`1E-FFk?yTnG#EQ$g#SP*-LUO2$`AM}OG=jbM`23Hd_$-V}^9CwHLuT=wJ{AuliJ z_*}Xj*H2%Hk7qBmqrHjijNs=t@<-AH^994?7RI@jS3fpTK{5<32jBY&1EkiRq+7eZm(n@;xA>3VV@o&#z9OjV#i$$}kP z$G8PpKk@W|7(cWx?TYyl;!r{JMV*cM!n}I~<;vpX|C-~t9`2!j%c)-l ze8*;TiQ>S8p}v;x=juTE@5q05T4#)jA4&Sk2~fKryNJK+j{e9_SDs(xFmaeaYbid7 zq+dG$`J|ONt}ZlxzeD1fe$f)lmp|!`CV%oeqyM$Ze{0ISjPCyu_G`|xo;;%GA-o|w zy=Xnxm_wpLJcv(%b&}Q9E1kOP3+^$0l(f!3I(Tt!j=aY?%r8tsUIw|*xc-bA zg`a}`A;eYija2#O71eDCUB8E`5XU*ueBVRgYhakXJfS*Ne8lnYsfxVP1^az58~GNb zKUvs6)&zqfjjG@qGon#n-4}T%S6&XnZjt$~(xROT;=j|ps2G6tfO}hx+Y152yx{Re zZ(1LM6Od2LM?3Av4%^K!{RpZlvmAFMB&9??KOj=|lW~v@W^Oc>tk4 zKleiYYKAyd&>H;z1bgH;dR}@*veSGZ=2v~J@_e~Q`y+K47D6b+?OOoa(bBq&y+D4U z0NA+xX5)Ar$o^T_U-RojYUTP-2BN;2?xUYX`eradX2&B2>rhMayaWz2ehfV?NJ09m zpgXpoQhQ2yW+n0I5C=Ao)H?GhY5>~NKE+A{H+Tu9<3`10+_S7Aeix{*er@L9xc(n` zZyq0MRptLbY#KncKu3cM7Ag`Q#9|NPQlS?TZRn!Afrv|0byDdnI!P5(Np~8zQc(lq zSmG8HwZIXgFizZqqgXP|7#AcuE)g9qb-;*%QCy+|en01)bDw&iPgAeoKlA#%zBAwQ zC0+HdTjx3V+;h)8cX^)tz47lSl{a^)g)wb$ug4lszenx%dgE_?q1qu`jN9SV-72T@ zTJ0}>eEFbVH?Vbeq+9Ow%polw9Ha72GG6fc^Y=C0US<5-Pu21bi`)3XQ6UAGDaglEpv_Vsb2 zjgBe2xfy{7uS;xxaqCv)l_S+Y-p{{4R_nWJ{6)_;Id<+yy&?#nVfAhHApyYaM!W9i)=$s3^)a}y zd;JapLHgsR~gS*e9qBx2oG^~utJf4E;ap^=QVDB z#J;=3`}wtGhciXb1fF-BbR}PA`^!&ZaJ*1`_jTj1=`8!Wm{!u$8kLtVEldWI3ZJl$}?0hdej+3)f+i}p!zti+@ z3}~D=_8|S@qh`0sCu>E0oY`Ui(6;-MKJaKQe+jBD_6*3LyDa_$HvYmb-Rn#n$8%iX z#_N~c^T3~L{o0x>b4ZE834PR%yWi)E4`s$Ug7crR8t4_+xGoyw~E!zZ|RWh4AZM zKSO#Zc1WIqH+UUwb~xJfNgkjP5^B5G^~M*>&Nj>j-!eV_!}5Qa(Y-P!sQkoN&=K(Z zj4AXN^Pl4e4t3ny;Wor=(Pw#DEYx$a_rT!7+h?ibW#dnU{e`FPoaChOADUlP zJ{0foZ-zjLOAU(um-nfh%H`NjY6?KJ%dEDuQ?gnf0XTP;VHY1z{j5>tX=-F`Qc8&*G6wMNEex$5sXK|GqnCW z2YtlO^JIqsEB`9%7g`HEhv`Ksr*gO2`F$q;c9-WMt`<~7AIJL8ujRUHasUP|9W{dB z%}>?q26kQR?!lP|q*6YyN&RrU8KN+%Jo#AltII9^%d)wYU;ZcUk9U|pN1)+^x9-<| zgywLshi_M&v2(?B<9~qrNqlHc#`WI-{}f)geF0yuoAWBCV&~5ZQ72$zb~NS&}93)9%bdPy;kjfqm?)s@j>+IyG8Z%{`Pv9SMs;?nMqgjax4E* z8y}N4j=bA=0_GC?%-VchdtVUDAW#TT-++Mwua^5S?l*BuXLKi$f|=lNQ-5Fc{(bN*YDha@%PkhoW1%dYDwP~-#`F&dj3+$8?5{bAy9aQ z)&XSuKzP&gBDjrvEusSoZ_wWlx^7%~`*!t*Tdn+uo~ir@dp^vp@qc}Z@|OJ_H1st0 z%DqteDL1R19B2F$a6h^3+G~$?L~D)pt_zjt>>TggZJ@e)m-2+&UktU~>on6R_b~{> z%g2Fdr?q_Z%R1hnn0pNslrJQ-UA}7kIO})Ir>Q0whTQ7~&(ZQ}doK4@>(`fC`>uah z>jgJ(FIg6odL`|-wRz*RZ7qCZRwK+!#t*ahO4;Aj95$Y~M9V*&o~Qa&Yxl%uPjn^G z4c%)`zm~swqx#8ZX3z~6sDCaz_7qpn_s7*#5G3KZe4qYv%noZG)3~(F>ifzOmA`hK z_KRt~Y*0lvlzL?bH3KPGJiHF-IllP`uH=A~&p%sv<92QL*BbxI2IV=6KTkP8zqqEP zJYmlnxYo))=4|CZGe11X_#U%o<2vov*BP%^+-`s2(T<#G{8=!I=$VomM)2Ba*F6P4 z7oPr`S`2REUavyDa&cxZ=|0!pf6-reYWYXLK>b0zD+pE(Gx;aLPxG-8jqx0C_g@@aIbX|Hu2TE-Auh{n z5Me~*S9?_d&nXT*Vf`zaKGcyzjQ`Q}NnWJ#)v7^ohvfsQUufJw_1)_wMU~&WKrd)+ z5_}tK%XM?FR)@XD%5Q$5^5rL}{2Q#nern@veUHldy_Ii2U&|-#zFjo8d%d7v?b*H^ z4ULx{AKz_uTl=K?r~VhuJ9??fxl#T9F!P7GyyKmTyBv|(iq2yTu0!yj;6mq%TFvXkrmdA@yz$CZEW zVcK6FZTgIDR-UuxW)B$Oj{Yuk*6!ExA3RRK_~(}?Uq3@Rs_9-=ntxVK)wuC}YxtvK zf05HZNd3gy|05U|B%Ty0o;(Te>hyoJ+UI1m&xxmM`B^Iemb29k$!p_wxX%1Jvse40 z_5$3;VR|lqT-*KaW(XPPia4LaN*1Le8~5ipM1gOG>=ii z|9G(e;H0FMPn@KFa=X<_j(>}ssRi-i09TH7yeD_YsG_)+4z`wy6X8!`y!3N;_7ARY>zcL5T#r)^8xc@v79Y^|C`~7jg1%XTCv|pUH=#e!G>Y9-;pH8oTbv=2!JstDF}bf1%ZD?Q`mH&oI8@sVe84 zcCO$I;{$fxr_=LJe`n>7zfs4*Wh>u{4lepHKVPp~G~R!n_OBM5pPb)j`rCav+PksO zY}Z}*7u9pnE#gjcY&(hlAxvwv>cFe`w>vnt;`L{Opy59dkaK4s*CHD-h&=1AlO z2WWr!FRfOv3Ea)6UKPJ?79Cc2`mJ$(A{tKQEZnd4`nbsn#!EXZQ^jf_KYC)Vd}2yI zkB-aRljZI7RLOlfNj_XA*u9&Occ%**(RbPG$V@&vR+`L@mo6&=*{pn(txe^tQ`w1p zX%b`xpQcVfRlf6YYg3hx%D$j7JvCCySA$w%YFD)|ITef)tL5@kP@AgC*VB{uqAYOou&*{=-sL{v?MQiY43`=$PfrD??nFU<53zw6CYzs{s%A$E5K`V3R0|_}0{m#Q zFoGXU;$kDBMPZ^cwGWD_j9O5rl*h-jqH-`+$xjvCWl*><4Gl|^g{f?1s!W$CO``Nf zVPc}Zrw~*|#>=SY#PsAuz5=bsMhoLpPHCxT)ha4>85=21lwGw8MyrLIT&6GsU0r=D z)$&w%=f252s%MQeGF=s8XQ6v_Y9c=))fVesS}Rwf^GLD0cQP0QtjWd2LEOd1Mkc4m zT{DhVsxbYS(c);SD!NOH!3-s_q-)YWJEzBj3Rn!RR!eXY!DW!0foD zXpgp;Dog}ph4NT1kslu~kBHgF&08=uCFQ#(tOquHs#4BB3NG&@p88>0o`k78E5t%yBqxEh{!RetV-=kM1xKd#RSMau@_2b~p_+9zP*aF!of_=Qm&CW#x-oZ!_Rzsy z43#NW;cH`~`Kf$u-{eTJy9DK=e$I%aBhXXq;4BhM>>ewjlH!cJs?J_0S#_=jH$z{6 z+X3cG_5^vVgCPZT{Rn3|}dW@wVh@$7hh61@wT5O+Z>(fQEo z(-ZmHZd}}T!7;57swT}nhI;Ro>uPgM)zGP3Z?B2aK>9d3w2K#Fb=PqWOpZ)cW*ff4Mg368;L_PTzel0Evr@=dGUw|psB2#6k2aO?cyad;jxPmXorFUy5 z7ZZp;@FcD!atP@^H4PD*A>7h`a=d+P(>= zv@{XAR2hxvI#|^O5>y~R?kon)a1ps;4MrcED!>rgET{w%ryfMljKKv*#tV7z7<|1a z-iKYlaj|J~3J;oxtcpYg{Hwi6Islqbu2HD&*}1PG-9YLrz06(AJ2i@!?_{&nXuiFO z#M&P$Fkn2G*gc7;1*Kh0CQBpE%C5bN)98~3KT}g0vP6@;5)-o7Viu}TmZdA?o%!J8 z`(R8LHb(Y3Z7X}lEudD%QJlW6Z&_{Jd*@ir0icGnrAEs_q3XVeXNsXF5P{(k2tE0! zJ!3Vzb;Wl%j~#P8YD@+(XBp?4Zp0CvbC&nrc&F>YzMIwx<74g@)|rZEAa#4F7{(gu zYN&l47RE&p*j*o(s*br zHOmH-u-J);a#e}o-YqJkvZ;Z0yTnkCxTiK5lj-BubOoW`RXbal!RRJBxS+FZ!c_nf z-t}SBeH=Y%TtWxzo6VL`@iWiN)@mbpjL+!O5&@79AYO>AYoeEUI{tB?$#r`1!>P-F z+;uEkh~G@?maovoFj_cAMH$~IDm&3%r$#`WbtKaJW>F^*50~bjs!mVB=_E>sCg{Z3 zp&jQUF_7ShfKc6W?uPO5WMM~sCqjz*?jgUa6(+)L!aIXYF`$AQNNSQpNvR43Gxx@2 z2EObl++(EZxXUl3UO*~mLx)k_nV@q+uXS;k@f3hf7zhDI}sZ!B~y?#yWFNZAYq;Li01 zYFc(wua7UpFU0mk2sR-!?cfTC^_!7AhTm)$&(~_%q1yK8og?yAET%Tiy1GcS?A$k1 zs3AtV@Fvx%qDxh$_IcNX+l_21j5+5y7wL|xnl#u2a6xGf7*M{n+Fh_=`>D9%76jRy z=(w1q;2IYfs%0oOjcG<`%z;{{X7;?oUZ;JfiV!F+C-S#Xl`E)cRmaB6zHGHJn%%QE~sG%Yl4%&YWeZW>4_-9OfoOObym}Ex1*Y$thw0aW)qox zn-P%3^W6oJy-Zidg*NShr=h)3-1*wMd89FL*zFQl_pjsTa&;m#k`R1 zg-fRkljtpC)a^)|5O_2D&Mue7eN&6y4sF#>(m}-$khw{NoC9l3*&jmpi7>F+>A&xNR~mkxqe>JFY(h2U>daB=ZVyN+|D5={sb0 z1M*h+UZNtT>~M2_j6>t_p6u?zKCp8U0*X-RBFQAQ2ksCT6lX-Hu$oj9?+tJTbP5!BAcLo?QN%p)K2yMZuo<6~5oTL#n^s%~ZsC1xfB)phIN9 z_Yd9uUBJ~fhLPH|tUHwC@*2A{GA(sgJ-alyRV(e=wT6%GEAV!g4|uz39i^~z7^lpp@$pinR&wFQG_|gWVgp!VxTrK$lu+eL z4@~dEa3zC|wAqw*{BiLlib$iIW-u9%F$GGUH|<8Vp~=iRYPzjfER8vVQDui}J7iQD zL6>yWWegRsh92lO8w<6Os(0?_&+xU5nAr{Gi3+@4J;R&B!hus}R9aVKj8#=!=3JRa zxREl8^cG!7u8yu#!|DnG0Fr$u0e5#%O%`;vj<`jNQD6k4%LRKqBb zoWM0LdP}9cNy55_cdm=>By-Os#4KkSGF)Q30jo7{rcSh;{k1UDoM)O3KehBZgmK?$l` z=QdA~E1>_@ihj77#9FL&=InXp?HDyA2;pZ@&%zHhS=fNJYytw@*uZ zT6Et(7mY_Ym!eC1L|q9&tcz_d?U5Nv*oTk>>+jLOB`P_$+JUv3>_(R^OBk7Ovkm9d z(LOFCV6B2hjL-txu}C0s1>_;s5GXZsS%QwbJ6uP?4xLM}5VasR( zX+J-&e72iniw4NrT=Z4p3DwRI)B#D1~Q{f?b33zt^~qPkfK6EwF{&Z zo7=~$v(<&~{lY~gWJpyd@Ju3k3A=e*i!hX(bb1)dkSd1ZUdF_ZAvctv3+8ShVJz0+ zweyQewjbtA<}UbI%mVOEbvoBqBzMf7o8PyyaKR*U2RF;pVGk})o{-S&ay(7hAZ2Yqa(rd} z=>{xZOmbh>6HYa=EtX92b_2K z3d3TN&=t-lWW*Ksm9`o$%w&fq^2q3Q1OSf2KCNu%XVxy8uy-fjPs6i|6E41C`ilj- z35c+Mpw+j8-?=O%c+pj(PFrt7?o^s`*<{%M(LrEKVNby&TUH_Q4Y-@Eg25@FjGH{} z&_SdVro@L;8LVD~nT9UB?I>ae0BLLO9N9`j%i-#Iw;B>gU@Uh@fAn2PE{fq@);-)H z^AN@bvXKFmS2vN7&1SZp$(nPZ@Eh3SBCM@c3u9tPd+Wp0e(hm-eAnCnkWddejX8 z-P8jsg{Xk~ysRl|uyG1O6nggsIun9=^85V*9lnT$TB$=AXstiXNFcKb6zzDjb6!6n zcicK^1}Tyif0cX+aon|#e3H2>d{Gt*ZKR}iSyR*3)VDu&|7;ExohkVN#D{nbrn(NM zU0&x!GL?3#0=TNqVO=7J>R}sE3@?F1-pKH3tGq6tqp$jq7ml^&qi8EgbK`&ydx+BK zl=c*`jBJa|=jL%}Lp?D&h}3+00b368Gp$!a6Aa6-MUQRJsOpw8oJ$SVLbKyX;xgf& zsS5zIJBq^#h;utIo3DmTtI>v4ePa#fmsxyrZ|hvLt0UV`y0m&eI+9HptjanAa%&$Y z9L$h_ry&hJ&um};Ys*8fsq836v&*hgLza$YxVOoicrO~kyF?Z{M=tehm&6LA4Q1*l z^~SUTy-vnAi6w4XNh6e7+ZAhT8>4L4hTcfo&7-m!gBgppADZ5R7vc+Y25h{G2H5dW~z)tRP{ZP;jV#^D^rH{zNrv|{EjDn=+X&9RS8iNyd?ylR(yM*nbjlkFdrl-3SMS{FAqVz(hK7E2#HHp^VZt>0=plxtbc zRgs~htef5GN*+F0KXLI))@M7jK{?3+tHKeaS}M+xJA5{q4eYFy$7R(3*YTq)jPIS( zwu1Ic6l}_X-I8$1Al{)k`}t)q~}3`e1m@EPMOJqDHO0o5na`? z4vF!y4d-V0C~s!NUKUL1t~-*|oziHU5hkAEux=Tzvw@0n)AlCi=b8$*-I7lIw-hFK zx$U=ju(BvQhM7sWmd6Pc>lo#-z}k-TW_%A>=2YIJ&9qoAI9JD4Ts+!ZJIc72 zY+B)`7HZu;Dn{HS8e})%D2z<}P}qcMhjU$7Qb9i}MFDdW*c34*T)(lD!NQCWK?Ai7 z_7*?1?z0OXP?NJ@HKYl^E(lyu!NDu}>C(rnL*f!U%Dd%|oav4_i$lp&+sksxMP7>S5xe|H&ee^&Q#9z3wtI`1i3;Na zK04^G8G!gRl+{BJk-SvW2KDoS(LJ&|BzBa-;l2`dvmUQ&4fyOsy;xK#rYv1G6F zMDobcKQxIRi(^9KOS-`=)!tsjDVfNuP*P7lj^ftibTrSn{8NY3&R7(4)xGE<=BhRb zRCU)*Qz#oYBjML)dF9wLqE$3jNePr$IB03Cq`nJp(_i}@{5S%T zEEZ@xbS7dtic1pjSEgYYowDopl$Y&0p5Flo%foAIcnh6Q_Lz`qLz2uM;Fr#Z;#ARX z8#%fmWu(`*St_#Xp{;s7lr>ky$^(w{;V6kj2A@ewaB~i(VY%ZOwuf#GFVRK1p0u^j z$TehU<><4F%xZP+J;`J+FO@DQm3IAKh811Twy_M`LDFI52ckNfIXV*Hm)NTndd_%xXCB8Cd|$T# z-^E14aQR~Yu!PAC$2h%*w+)p@#(}@VetI^7SS?UN4z%i%IVy$XFBZHLiA4lm)fpfgUX=1$B+cXhXBFT-z5jN7pYe{WSq z5XaYIacg+z7vfSHAf^5B8_6W}7x)~HzME!T5)baPiS0Y2LABEYThm9DM6euEzyZe5 z*fkNTP?>^W)>Q~QLm0J?f+M85aOK_1HYD=3(Q&u~+OKeFcG4}N$UJ$XJbH3@=OrQ{ zvW^z6n@MflXnnzCHIJh#dMMJ5Htrkgj+pOQA2UnA2j%1xq(=LLoM7w>XMSVDj3g4g zdBlBfVh~E8{2($SMlaDZCSrq|c3JP0rjl^wLWp>Gd`!@>JHD>2t3Wk)pfz{67K-hg zaP>V@(3cY7x~!!Bqkj1iJ4)J}{6Wa93_~?q5pW5&8>ghdsB^gHcKrsHv${u(v!Uv4 z?1=3yqon&qG?lidG9z_jjwb1D?bEFcsXO7Qi5@<@D8IWfUD+%tr-uKtWmbsg;bMHf zTxvIh^M^&z*LpED^VzSZ6uZ!wru4*~bKg#tD9s8xR5)5k>muPB$EYmAm{InhM?GqaK55W!WZQP4ed`EWLG?&+s_I?mFO*_3Y39jVa{#!1yZ|N#N-j(`Cu$VjO$iX~y6wQorPHNw=(7PfJj#m*FN@=}ai_`pls_3< zllxt9myFJxys0G+LK!iv6p;0KjR7t<4C4w=T;@dX+FNe;-M`Ug=-_0@#zZC5cC%b zu3COg(-s3@UzMe}_DO32fmRj%g9r{CcPsnW;nB@FLJ6`z6Yh+w*fbWulXtPu;*%)UZ~(WER?*AchN)9ZM@VgFL?sl=YlpQ3oSQFr&#LE^fw+p5qi` z$AVpiaf=Mn5MieYV@`f_v?dc&d>J;UTPO#Jwo=&r!9@vlUs(w7!-!h}c0%Jn)8rJt z@twi-3C&P6Fu7Vni};q(X8gn&vvb}br#>#7cH8)3xzdQ`Xz8 z-jD7enpxC~nshsEUYiP!zF2MT65=J0J-(xicaQ<+mEi82NA4n!#tgIJ=pf~vKY%+( zWHiIQgqZvJc^|ZnL`7&DT}#90scRt8ypa_we@B>~FNcnz8#`(*s1vQ4HoL5T;ETu! z4q1AH63!_t=^K(Q-u*g-w5jN?<2Fjr%*w4uOLZ*9N>xJ@LSJ>>DWO4K8zn5=(L-Z9 zwrm$|C_|vLNA?R*#%3#;>EotTzeE~FW1CVyrp_rPgLL~oI0I_;-05PB-q@``@Cr3pyL+Osb15B40DCT}UK%W1|UU zn>!Ka6G?2&;Bx3AXXi(;+JLQ2=hvGvo3f+R6B90NkT$mEAirY)KX98Ba?PxBm9U@4 zQDcV@JjP0RrUG(U=@#Ls;!Trw?;O0#%Z(Tdiz{v;-pSCEM|VKvQX^R`P-_HslF>D>{1!1N^lQqm!A_)>EjdH7p_Zc3q zpt!YyZ8o@`CpHno%kqMbO?W8C1R`HCyuqsD8uA@nhaE zEkuONkFmBZf!lk5rnU>)Sho1>Z8fnIk z*6xKkqy=Te%2B8B2)fN%vn zew{tLgl9|W0V%9m*`8JS3%`42^^i3z=qJ59yS+R%wcTz=wXa=*D*+v2hXf<5nz}~l zIItT6sq_hLe(AR;d>-!pQ_#^L)8GwyeoM;RRRAS`NPKjgKiI3^fV&gjApk3)$9e`! zZsgfCiCeSHKAp&;_u0mKir!{;8J_+H*{-QDo5H9xhWcv9#L@|#A>*F26jl#1BlmU7 zzJSe_J63jKWvJ$cQHVwnIiu(*9lhZ-BhdvEIiOo}Kwxl-4to2EAAh3`Ben|j&SZe9J1 z1fp2yL6hnY@11$5wsO?5+>}MG&{^|vR&d!d93FS_;#-$*!PItTaS4BdS&iu=xU=P9 z3x>&npZV&KvljdOY8(!hyL`+&gh7g6NQE>t%r<*ujf*Lu)^hNvh}(&(dAU{DIgOmG zbZ_K5BIN1ny1og+o!G(E5X)K+YYiS5G*nl76}6SdH@_?oBj{+PK|fs8Q%~?a*ajVDS$)5&qkfX`_^=vPO{?yX5JR|5OxNeZS+6aJV?AG*Jf`$syEk}fOm@Bf8mt5rzvOFG0g;8l(1Z8isoJH}DswQ~g|VXv zqG4x)$5p2D*dLoG7xSl`bh4|W8p;_$zVRUj zBJq_qan!Lj**Zj)18rB|F;SvGo2jTQ{5Vu$0LebfN*gvcrY;~iCse;t=f=Qoz zqAKn}`pk!Dr}QTPu|%QUriq=h%iHS6oc85Zk5Hlu&p&x zgT!M)BwKXB-`y`F&4uKs>hl|Ts*E@IaTo;fKIUM~$?W1~9dC>KAX7@KzhSqC0YJH;%fPV_xComybu*xH?dhHTSCrx9W+Y;)k}urcn0!`i0nPQIU-<*nvgN)jvg&<3$_b|=m#=+nev z=hed_qTW+GZ78*tWtHm5ZrxIx0WRYsiB19B&?mkoX@(iaS|qYcg)g)lpwiAkAj?7} zuRlgMjOA1Vei@Ezku zt9wwz-ssq9hElg5!ULSExCE}Qfktx!e8l%Is+yi4I%^hRc8j1EOXRt`emhQ@w1lVM zW6tU4NbJ47G2_PzV#W~t{gIjI7dDXZzZ&lr{oEs|yIc**yH*!>(Fc-6I%a+s9#83> zYlmWPl8}|Ex%;qm=I1jTpECF*5GX39cODaVN7&$$-PRK&ZLv61bL*1v2N!m_h7G;3 z!8;s~aSitv0CA{kt-aiDpzXU8g0UW$wXDM$1uNwC+{JMF&#fWYF=kZMB@6N&*YDgk z#T>(C9h&=gZeK+1VWA2&l&qq*|A~q)aQl@KiD{8j-VBZf1hsh&;H4tXz7rvuu1pASxhqZ6uyJN`tf9a{De?Q8y#4 zXe8K)ikmb?4pRr2qIDF*gohTmr93hX_hcK%?0xP!RKp@QChE{UTS4|GZ!`tbWd-7+ zwu(erXeoV7rpdnTI_oDo81UE;l_WvYK8v~<%7)OuxFSX&_iyOEmI-AGuA8Y5q&v7? zBK^oEj@D0Xodn5eEb!vblel?=^Z>iCUu2ewzt{~C-fqw!`Zylw6wd4rve5n@;hjP7 zHtf_%E&P1Zt$6uV&m6vn38|eXirT^5wJ#4tGa?pQCzZ@O9-x=xbkd&?AC^u$}gDdSzpJp~NhT-*JlHPLyv=Bj$tJAmaThB6KDLj?kl53p#DNP=QZ7&2GeA@1f!F+73GjXF5` z7w;l&jC8>n;!Wpp8l#^@j`SpncDlGFg55F;-dLB(4ohL_LzYhu`Fr=WI*CA0i+)uM z6(>36Fk!=9Bz|L=k=tLCTd#c1YGb%t8k1&0-*Y2-w4V;uaK-o&ArN9?xs)uUvqcA{N4>a?-Juq0pFOx%TI6ZPt)WvBTjZb187bM-La~OKf{ZJ3w zhojTLXq)QSxV~$77+Z!?8ESySi~5p{4J&ed1$hRZ^PG^(jBLhtb%r{A+WHTDsnn*SjS;PlyU< z=rT%3Q`uTu^tw2X@Q~YQ6f?y~?1m~`R!~<9F5WV9cJ`zbPsSfzI|pw!WKYKX(>m|Z z4BwvAp(mXh($2e+`$Kq2`0kAG-6{B6ZvG0V$RD%}4h#;$|K)!N+rNi|#DAmTcm8{b zD=EM2_CGE0aLCkBUQ$o^3oYS)ucdrxPapc( zl9#WyFRyy}@`t=gpXEyaH-`iriyjoV zpw#R@{9SLAp>^~%Ql|Ct*N{)qI;vJmyj^_R=)e9qzAZbg@vuhV9PCH`Nw`2Wjw|DWTbqUnME3u4>(1kQ0%WtVn7{|TL<9Lt=KlNX z$CvOY!4D7K82SE*`+a|C?E3@u`~Kj__Xh^2*!L;?d&S${g}?azu;6OQmuu?v4vLgN zG{B#Bci-Wk;GM2~B6z=*e~0`0$Y9<+Kc-YVBEXE|llFPmegBBy3-CfrkMPDHv7;h7Q>{&_o z!&TxVcjFJdU3jVs?-)? z28kd3Of8=w{@Om}!^GeBIORFwe|m!QBJs-}qr5`=ea}%oL;Um4RbD54#Wv-$#9P-Z zZxFx6B19TcyAY;?85uH@D%Y|Uab1}6Tf+@@-*?`vy~4L|4v$Y zhIsxN%7=;fJz05 z^a;wF#8XdEK2JO`sCz=24k@$AAe~b8!o~Y%Qh8p5CN#`icMLDCKG5*9|EjB>rFKpBduMvp7FY{F9GYIXU7V?N?qTe%MCk72;Q? zl+O@<&5M-RiU0ZUl+O~s>e0#@#NTJ*^&Igld$oL%_N2&ZJ;{Rs(#xn6Y9j)b8h#!_z-X^|cc3UO>^`~k1HR5U0XPtQSNG%`S8TbFh znaX>JpLVkH1o1OX|6bxRwEQGVe8u8YAMxWXe@GF}B=oxd#3#*9(!}qy@`J>E{K<6T z!^A&l`Cg9rCZGQh@3H)$Li}-Nw;AGIpE~ht9-;M}CH}}GlsAaqYkB4zaWB8wh0k~4 z3&blno-Pu9qv_Kk{twSmJ(q}QPE)>2{6(iLUm>2i@vcq$6*j+EC0;s7<*X6+c3AJi zgS+DXf1l~oLp){qQiAxhwQn!+$DN`2B#C={`iKu&zfKWYn}e7*}`=)xDf@KzVT)P*m1;VWHuy9;0K z!q>X+^)5X4Mcn_r|MYa>i7vdi3r}|8eO-8}3-9m3(_Q#r7oO?Dhr94x7hdebD_!_Z z7hdneXS?u57e3d8H@oopE_|U2U+ltLUHDQLzTAbcbm8qTe6%!N&@L;uz|99bu zF1)u3Pj=ybU3jVs@9)CXUHD)Zp6SAeyYO5WUhKjvUHD8FUhl$ZyYNOAKG%geyYTrg ze4z_p?7~}J_)-_X+=Z`n;q5MbwF_VC!q>a-;Fn$ezY9-v;k{jWvJ3C)!c$#%e;1za z!UwzXOcy@fh3C5PVi#WN!e_ehdKW(1g*UqJxh}j(JpG{VQ_K_Ze?a*H@iVM^i})7f z%fxpWZxes1@ipS3#)G@#emHJCL43-1lK3l)r-;AMc$)Y&;~C-?8_yBnX}m)G661B^ zRpSlfmlUh+k>EPW)=)4dU-L-XwmV@de@^HQplr3FFJeKWDs6{8r;@ z#J^@d_;uX=|6x2q{71%<#P2koBK{lWY2tq}o*{n9-!u;7h(E)4h4>4M*NF$l8^j-O zyh;3##utb`)_9BfvBsB)A7{Kx{Mp9Wh@WLVxF>G^O~w<%Gscs|hmEI*XN_lwAMl^Y zx_#Ij@xJvZJ6AeddVYXuL`M%3o-` z=850&6XgrUe}CEuPW~eCTfd~&Z4tlA_!4n{-DTpxnNK?TE5!fJ#h?2?o}#h zjrfwuStow#N40!#Z``lGVdZ;>U;lHpPlCADznA!xc0M6V{A}ZW#BaCjrig#%XL{X! z;@`INY2x2AK1lpXyKaW~53Kw!ac|EY@gG_FBJuIJsXtVRzoV#phWH7lPo4NrO#UqK zpBZluf9|TlOOyh?*I22?;)Q2vH3sozghWS;s@TU?Vcq5#9ygC zeZ&v3@+sn>{}WHNRi8BR9+NXjJoJC!C)}xWhKV0;a&p8=7B?!yA8F-hhB=P2Zm8XcedX*0{ zpH!Y9zWlkrbNUPu4_>3?XNWh)lsAaC^2+CkCyI}7@|(odAJOs)#21c{)3!mdOg#6G zTE0y@{Y@>uO1!;C%dZg+-lKe-c(blNcre~B$u}wQA^t%6FsElP@%jg~e3E$MDUWyM z`-r#B)bjnrlTTNkCf;r+A7uW1~) zpO=W&kJIseg?Q^amER`b{D!qJ^Bc7M8u8Y{j&ODk{zmP1t(Na09{l8K+K$YtT0TiU z`3~iM#M4FPDdNeT@_yzAs6V8Mr(dV#2Z=A6Ju}RYSNr6Mr?x-V)vH20ccsdoA)Y)@ z`{^w4W=+dCi3is!pC_KaM)@M~+}|s25noQ}b(e@YpRDEE#B&wpJ;6z?e*PT0|6Kn& z|Gm1wm-;pDoh6>TP3_Ylex9u}%n{H0T>Yv^{5@|~`SZk2KU_6iApXHsU4K|4{=TDd zj32KS@urnuBEI&T$GP&$#6N81SBPiM(Kz2G{_3x3e_SP=xJT`?M*N>l{yOp8=e2zB z*tq>aW955@=f0x)Cy3u-<$H-IzN+$*#J_Ik`-m@pL(8X#f7{CU6VH83%cqI|$jT2A zPi#{CGsJ&p<%fwcEUWw+@ei8)i^MAzY55BAdrbZe@u4%--|EEwVC83tKlpt0w+8V8 zeyRRBN4)ulM>~IO5TL~ zUgA&vxt327f4Y_LBYwS=PZ2-S%J&n$-^!t$Z)><8L(oC;ksszK{5w@4#((c%_KH z%gXl?Uw*ERk7?rXweo|+Z{Dc-XNcd^R)5G5AAYLl2^HeE{Zz}>iT}WOgZKwcpE=^~ zJ2n0^iC<^s=ZPo&pzXCnJoRhUXN`F7c9q|IbUeNt_-$>M3~`@_SBU2_nzzjmPi|2E zuM_{w+cnP56K{V~^;jgHHonTdqWZ59_xa~K@!Y7z^T)^Q>*uqB#C?97A@1kJMdC@D zkIWFi>>jm$gLv+)qntl462G=zQy8j+K2e~y=EWc3rV%l8gXxj-Y3QDo7(vpcin#C!Rs{c zrHQwWQTc=k@O;Uj2>gpCZ0?ugV`L-X64dTH?W-dfg)N^nWRD5chUi zWNz}8i06j%x+}z&uh#ZmCEk2hzq9AO&D+=xi^N}Pc339v?XXVV+aXArz1`LDAG1Rb z@ut}!McmtAn7P@ZNZi|DhIsq!YKK|kEwjVmv2i`UKMWIpli8s{+}mM}xVOVR@$_AK zy=CIQzU$1bzP(S0>$zs_+ef@@^;#wF>$Oh&?N+bEQ{(a*Z`b}+A@22@Bi`7fc3vdz z`)P}K;#)e8SR&pu`^-Kqu8-G$j`&Tc{{nHZf8w~f9It;raj*X{aj$=lc>9fNpCWOu zf1j-ja{uZl{;#HghPc;%fw)-tSNou!e#ksdnFY%X~{1ox_E_sSa5HyH;`z#WF!>_d_E5yBh+Qb_sf0cOaa9v+* z;@OSz^7gsZ^qeQ&_-EB~k$CRAYKIo_!|&Dk*Anr@9V%yqc+%vwiJxwH(JJxFzpnZ7 zI`P(bRG;A4@p`?&>eWNMY4z$QUN<>O;#XK6+edus{c6E};_Wk&&c12lC%s+gO@qWg z`xlinOuY3UTCW`Oza6aWoJHc9Th-1p#M|FiId$S+yv5d&iRV{UpE=^mZ>gLn@lTq4 z=7}ek&3}jo%POZu{HrJFdcqR%vtF+KafNvLc9qj6{yJ;-RpQB;RG)R?t#7KFAQktY zLwZ!79^&=)X};Y{ymgz(NfN*JlWONa;>+Jxefo*FzoBx{#9w-e`4926@2Q+&;yII( zBmQddKg6GVkGA^^@xbKNnOi@dCBEeW^B>}A>laPp>)wBe*G$eL@n*mFuNLvUOwJPV z|1>!(#FHkcP5kpFXO;ND4{E*EiPz6ieFFTQMR{qdAoz~S=^_3~lhaGQeY(m?690zD z=_7uZ$>}HFK27DMiGSJT3=%)?Z>sSy@wCaw5r35Riz4yN57ZB5h}XZa{#+-1&-Zk^ zm?ggBujW6*TVGQ-P2#s$z2=EOBd2j=k$C%5wNH!qh6j}|5l`Kv^;#jGG=18{-)Z`+ z62IX$=0C)fU)6dAC&&HgroXB_J;eY0cPgiscymeRB#D2=*6aF+f9gJ!(@#8Ya?-?` zw!S(@y!9E2Kg4riQGIg6udw)BBtHLp)n|ryU~=lj&o@7uC0_ZK>N7_?_;>Rk;wL?z zcAh7mwD`P8y#8-0r$v0-{CSD^vib80@z$4BPMi2Uy#Em2{af=N;;nyGIRXAUjl8r} z5S;lb9WQ!_H-Dt|=_Q`KRplg!pJn#xBYy9n&3}luzNB)}#D9K*jbFr<+p5nn@n%cq zEs8e#{Flv&wq%2c~SQhdx^I{ zul7k2|98`;kN6!~jnDnWbEZ$4_*;DbOgwS7`4925=~HL^an0XmiT~T3+U^bF!4Fmb z9P#@tUdFb$Lf5rNW79)`&5Yg`RNSvQC&}H5MMY>{c4W*T955N5^rr*{g;Th|4#K>A)Yqg zb9&qk?T_kyP%rW2k0>t^_vdWt#ILpe&{^Ww|5fLqbHvk+w)jB&M!&C0{F<+-oJHd8 zqf}0d_!msh67i3joE73plhY>twkxz?H7Z@7e1)#W5dLACMQSy7thjpUy*oV=Z;Bg|@#f(wC+Ls+&(lm!5Ao!@wtFw})?q3qN&Mi3&98~4zpMK6 z6VI8PH1S)Gu>6F0?!CI7J50RQqx$5CzvUG3AL6-x&~aggc-rLDi9d18=10VD{!bmp z=ZH5SuKF~IpKf-ZC;m~Bvq(H`a$3Z%w|roU`1{NcSBM9Ps6K7t=Xn1io_W6d=Q{D+ z!73*>Gwwgzy#Ek?**COb^b&6$q;itPKWp{sBfe_&>L=blQ01hFzs&R*B;MX_{zE)% za&pASOiqz_`b@RY4Dr?hs!yHxyZ!u#`29;Zei3ikIn*WMetmm|_=7Liab%77^0W0k zPw#W$?UfnQ{+J=|^NL~SmRD4W*MFq-ogp3^Z0D4S*W0ScD)H7G8fVsshvz7WR}Oik z%ddj7;`Q?9D2l|r-DZg2eV4XlgLq?J$Eh{q-ad)v#;@DHQ|-`8yz#Rmot{bJ=`+p# z#9M1Bw?aI9^-)gFEb*YA@*Bjx-R6jUyETb>yY)OTUN3LAUgAG7yDboJt*9M>=f~yv z`wRMsZ$3uj@Emb}AIf4EzCt`G==D~Kd;Qmm-)a3i@q)OXKg2WM+kKI^-}h+| zPhP2VmWbzUzk4)A9p!^9i^tohXv zabK?$;^{wWd{`sCuB>%^CD&~Y$0CvKnR zr|A8*J;YlkKS}&U_B@Id@rAeAJdSwg8LEGp_#GyHnD`a8o>?TGs%ihK5I^QK>i;vu ze{b>|#Jztui8pP&Hc#B!XMuRao~N-y-23MW@zfQnf1CK)uhnj=#Fw9*ba5aUjJKEf z=LB&tKS|vCVLx&2=Yzy6x7q#I#Or^y^*7?{re}q??-zCAUj8icz9Y3A8^lw`s~zTv z`{$@E5>G!#?b#y!`pZ=Q67gqSJGP1Y=dr92_wv_?Uup7#p?G_}$mI7D_s@0dBktv= zh$mj9?b1&?Z}Kz5{qtdR#J&6?@t~#hE5u)I@@I+r=giCz_wt*>-(mKdC;mQ@-y-gx zSF=pq%U>b>0h8Y*{#ld1PW(3GJui&6SMK9Sxqg=*?&Cl&@$Z?O6!9}`Jv>dk@gDWh zLE`O?YJA8LA2$Cl5?{0H&M+U)cv~ml`o6aBEb)fPZxUZO`3uC~{weK8i^M>=0;>Ves#EatXSUy+Rd6LA_o3(xWh%bCm^O+R!MYGQ! z@jsuT@`s5hck8^VNZiNaI`M6Oo=CjWP(NuB_i`4ApJjGfBHp@Opw{RwI(M={N2VY#J&6( z;$Htc@uta{BmP%?;p(|8`77jG}Ge-H6>i&shFuQd57;$D6~ zaj$=xc(PCPv0>t~Ccj8L;qwaOFaD^;^BLlUCcirsUGsM08I&rW6Eb)Gm(!ADseA=jkwo;op{aUB(}xd@e1Qf;<>Ww-$y)M)3}`?K4iK0Q~HBkto*k@z(xr%wD+#v8=zM{EC`BktuliGRW5EE4~|@g?HPy{i8* zaW8*`_)ksF8u15=2ixQA@`M$&a}RMZKSBJE2ecpc5r2~Le&Wf?)IMqAUj88Q<4jJD z`16cch$k%m&k*V_v7O#@moz!aACZ?er!BJ+{^DJ?#IU@@n4&qe&UDz zP5pC_c=8I>KSSK>KTQ1ZOiqRPvyIn@d-=1(z5Wg2&ow#o#4j?wNPKTu_dQ$0efusE zFPNM*@z)z)BmS?Ks{D20UVd;h@%Hla6T}-|Q2D*Y6VK4~jTCWj|1@!L|3T(v{|s?&{~~d}ep@H* z?L14|uhTY(`*p}g;)#FN`nHH48fbl2i2L=%HR4{*I`Jo)oWzUc_V<33B%b<(wtFA( zm)@Y`b&9z6t3l$Y+jWPDuWiD)3ywDtRe_p6Xym7pqQ(Y$R&kL;+_veLD zFOBQz&kOYv_veKVgPxyn`I{(RIr@yo5f5-*EiH@8F2gH?!o{pW}`CbYhb#QnLo z7V-8ARQ?k2^gXJ_?91c&c>U*yzryriAYQ*i_jwc9xE!y4Kl9DnABTy1{d2@yD=NP) z7nkq#>?i&f(=$W7ann(*e^rQo=$o1^%@E&c=hGI5`+BV~e^C9QO}trG{d@Cq{d4wQ zizVW|UMs|}wtB4*&-_H|)w?q;fB3l?mny`Yf6{ZO#gTY#Hz7M(^jEA-*tW&y6Y2b+6UWy~ICe^&KXj`h<>; z4dT9D^J5P!f06hNc0cJd@nlu^+1H4#WwpPr6Hf)Y4i@a9>mH`(9(#zl4pp8Y-Z0)v zJTb3+n@Y+A&(Me&VfzG%jU`xBsaAIZQnBR4rd5-rA=2nIYbM zquvi#C%$a=>8ubR9M?E7SR{Lzp9~ZKtoccWxbGKp#9!?9eTX;iR=X_|_kOibJRHAD zaXo#1=_lR{wB0krmmj6}TqW+~z&i1-S$z|i#INh?+fV#WpVz#uLfqGDj<~PaGI3w8 z72<)#fi>c(5$*5m#M@^b<>LSB?zsNGzH`KXWc6Jj-ujTrUm^amA8UOR<8k@EzWu~= ze^q^kiHGAL@x&Xn9~FrwAJpqrh}R!bK0`cde4TjZMy*%hL|lJwpMK(ZnSCNV>x88DG{#&-{ed}rB$%C}~Ao1JGo*Ckqw`uzh6R&(p<9vm9 z+T!pG@ddk2XPtQPDwSWX#P#=fm?3`qkCLuGHi#!KRym8rQ_s=7u}!@GMJ>NZ+_(EW zbGtt)acNx7`sztepI+k2FHt*Zh&O+yzaJ)^{H^i|^Ltd!IpRlOqT^0aHLj1hTQBiH zoByPUH~*sYGsIhe(eZkic;#NrA7+RzU!iuc6VJWqC~Ys|l{=Kr5pS8D7l^n2pmts) zp1xb9gofnDs9FTPNT_)bXSo8cf;#oFuC#gMqr{a31o}fHQyxFJs`=^L|e;y_tyjt&nDH3lVqw*WX>pxXJ=7_i3$_J<8 zdV2c|6Muo72d)rLU#Nb#Ox)|g!rc61op@tW?~4rf#Pv!4MBA~Cc=C^0F2(#0%KM48 z?^C`?+}C%V_@f?Be@N_&>(hRT);C4`Y#Z|d(}QG%Z_i%hf3*7-Qp8jHj&l90pZM}-ZTAfEP47^D9wy$l=V|ANr|(exio}yY zRz5>KXZb^exNqMD;^{lIeHWRpYQ0*-*A7;`M10{b8YfqX*T16WSBbY)RR2_6|LxHz z75H}SC!RY+<)n%GcAO{f+i{Wj-=3FrS6U_>{7&QK3h{*(sK2cdZyu=ntTW%C<%8G8 z>y`YCe(oi{e23;UeZ-q4Cq>-1V}^M0r`nFQ#B=xRb?1oJ?@_)&JUB<={NU?I|7-R0 zF!2Mf*877h#52!*w6k-Ac;&0g=ZL4DrSpy^@%pdyx{Jiq_v!N`mWVIhW9I;gd%LX> zZ~s{B*7y2&eVcdd=YHa?Un-v=Ub$T3VV$`D`+cj-&7SMT?>|xPnYf(vN!xu}#D9H= z_MCp=e|oFNnL*;MM_7C&-Y`B)y#74BZjpHIw`%7Kac}23@tnzT5U>AL?ce)`czu&+ z=yhkAo89J!H|)7d3&fM>s=qA~|JM6;yj~$*FK8TV6Hl+Je(S_5quTC0Z;b1o`jg5@ z5cl>;G5@97VUYMjLFE^TxBsZun<1VvzDT@vklM5FO>sSaoa`r_FR349h%cX|_8caD z#Ei~gW{9V4o?9p0JXrm3j(BE7?KV%mQPF*t1>)YG%fthl2dxs%9IAO#&n(%&^hpqJ z`h14CAE$eYkIw9;(_f?trBnFqV2dw+}mxPxX;sqE93R@<9m^~_uCobC;Zm(Ug8U7 zn`aSE*uK>w@yf^5-&Tl!>b)9Q*NA)jC*B;_zx4x)Z^U!AY2G`?-14Ff@z(cM?i}$2 zJIB&4w{@= z6q`dWg`u!WmJT*jLt;@Zp^Y#|Ek%peQfhK)721+4mF6R|bXqDch3~J|;C_8kq3ip|HMnn=COp^mMGGFyH(42fVz^&l zwBdez(S`f< z|IEgpAw9SCP2t}D3wV69#-DyM&(V20AAo1KS-CEJ+PCs^xYu_HKk~kvR@$xn$>MtZ zN7y`h9Ud+1c+rAS_uFJ8P8;6Tdxkr3Z-*{j^T~bq4r_%g@h9;3adv((g9q}`r;7Co z{%G^_Ww`gN3OszS)hnd?_rB?V-V6Vs?w5_=@jcdWXYdX6{7?DJV*c^%R=x&L|6~29 z4sU+d&T~5O__5Y*eR!td?+oGTZMNPK+}mvo_w$4d?)zi))5Usu|E$Ab{ddjJz@uwz z-hM_u*^U=E{A68!l|O^^{@DJW(EWT5zNh-(0={p_j)RrY7USQwmwEfFqI-S2@E0n6 zAMWwT@I}|^JA?cFSUUUvf4vp>Q}v#!8oW8P<9-WX-p{s68}9Et@4#!iuI$05-?8|8 zxQ`DhJW!kwJpPyU{|P+Z)vn8C@X)_k1^4e%RX>Mz`>5qvgM0t4!@d7Ua6gU=>3+Te zzh`f|zMH|X%56Sj39spQ3c=@*|6^=_ufn~26P`V6?U}+;&7;lWqmSBgp>Yn@t9rHI zFH=A7z>^znyz0Y``kS5458!{%_ghnV_DjoS2#@6>cqE^|%X)9X6h6J)@|?l{JhwcT z@JRQ0gD(`@XJ459k_3oF5I`v0Pfo*h5L3H!+pDCaNjPyFBRL<+kXIm zhQ4bvf=Bmm5{|R0+{eQ) ze002(&){p$2Q*Ij{RMBgI=rm+JvQOfpX&Y;JktE$0Pe@fl>U7?KODhFue0-wF}$|7 z)oTh5*4f>*BfR+yyN{5=)1O%Wb9i%P@k?K^c-}tACN`dz;nBwC6?j-SZ@`gWBz z_oFu4+rJCnb{oyV!GrUy{Ri;TmiGIs5j_2@jSpjZU-O?6c=i;_dkRngYCeO9n#Wkc zeY=#uR&0k@&jVE83-!+s-pSQ};PG>JMhJCtb7+9-D%fH zJ$R6%w*!lS?0d43lje!%)+0?*5~U-aO?LzYJ$ z9{$^W08jp9K7r?l*?OmN-(P0%>iKW@E>WueF-nCeDKZU_S#flf`{@ld*P(i-v?~L-=Oj_{EhN9{8)JheuBIU|A;(=AM>bPR`$Cg{B)Hc z!Ov9rG5pIapTRGYPvDo!r||3LGkCOxjSo5e4=O*0|5;x7RQ#XcR6c}P6~79fsC*4xQ~5f)vX$i@ z!C$8G4fsCtCj4M|3*J)v7=EbAx8Yrt@4#QH@?ChQ@(KJXmG8mdCQsoT-ec!WLwId( z+wM6%u=9sGJk#^i{+%4Zo~@~T>D$GAa^25t9;^)ifWDhkfd`k`yh;c!zsT;B*5T_b zP7MFBw?F)|@(%o4@-94A`;Xz5sr(e~<4p5A#dfG{ZT+wX_i-kM`#6)*^*z=J+{c+2 z+{c;dyTyFw%D(}RuD5n>!EaRgHvC?B2j0^6n-X}WcId+&dzbaY0etj&o5vW!AJlqt z_>b?jab^yWHna2mB|LtqT~7rU7u#o3F*Jb8)53E`J(zpKKd=UALNU2!7# zi`TXByaA6luP|mvxe0xgnvTYy$TQYySzF)QJe_Alj_xgFSfIMTJTVDV)zmNwBtw{UJETw z7oHuc?M~O2pwR1^vKkaHf>B4FyxV*St zZ~rRX^RL4_|1tb*>j2*ZVD%Z^1qP81DI}aL<1N_xxvY&%b&_F`r0(x8T<* z&ko${wSbScU6ydKS8!!f?)7TJyXyzXJFC2k`$X{}J5tul=YP|MB{LdL8chM{v(SfqVN8;hz5(?)is5F6Og` z@@c^L_21FMpD!Q4Um_2FQj8y#?f71W&;9u~cuR4*@Z0`j=e;RBR{071E%F8YSb62A z#XSA~djvmqe;dbA_-$8My@v4M47)BH!_#-_e2lK=B_{CT+ASVA%?xJntYdL<`0aXs z*BoAc*On`Bmhj{q7AN>wu^pbJ=jBT9=5z|)q+iQzv{oHl%M zp2g|H1I0<;zfhbWJUrLp4B)|GmQM=5QE`UwT555|@c2-RlfiFMoC$n-w#Avj1I5YV zcPP#r-u%49S;CW7T0X%w#r|`T;*{XiFIt=mJXD+zzEqqlJUz?e)Zxi1ET0G-=)K$x zc;`%u(}L$Ow>UBU44t>M;Yr8Fu`WDPoCJQG-Xqk5*Dka1c>qsdX8EM>-nP1b2T%TG z`{@`SC{6}1seLB!_a18Z0dx3{DnExGaF~@3^gB+Ur`l8HOYnn!q4ODdc8JwCgddL`~0%FA2si>dinG3UOv+AAQ~ut-(RhK6aGE9KVQ!{3CiE3-&6GA2P@71 z{(gA|zgW-bPvF1V+WN^9ew2I$zi%t`L-@w>IXvCc$}iyC>w5=F`0x7ax4$a3e_vjL zA1E)wpQv`I!2kORiyy+Dt~{&oE4HxmHTc2uI{cO=TKNe6HhB~NJ>}Dar%$msG5lzi z@529GvGNK0Bk~@+BJab`e}cssz<>I9^AvuAe&;fTze43l@Y^@HIAi#`TxY~d( z^!w!)?$?!VcT0S}4uOsI0;N3dEg-41LTwmP2ejQPQmv6UzQh}#^%O`~U`F|BYI#=_1@IY}Q zxSzK-;LW@Ad<8uDgyj>%{kof@Z{qbCxQEUdk>!Ms_Scbeul+K;eOsegvWoj zdX3?c;$(22|D3>sr&~TVcy_wwlf!+yn#0qZZF?=@`Nu3ya6_^G_;qm!UVhc4E8|rK zo_*Bfgm9lHtil(cwm5Zo{t=54!F}Gc0S~{f-+jRIp2dmbYmN)>a6>yE?!sfmN#JYd z_u$c2tepq&_`{Y@3Qu)kUbJo%8t$>3||_u%=B*3L6{a+<}-;cMpi;7M2G z4?Iwu;Mc|eKEaK}{Hx8zuJPsu_6NTYQi9jMZ~IFHo}8fm0QdW7Re0xZ z>JRYjc#9LkeV)Go&o9;UV{~0##BiT?YST6E)P<*hodWmkqaOU8lkK`MgNJupf1AMn zbDGT~P2r2v>~}^pc>jym-*R~EJd5wYoAm35&T}?i8JCtQ-+9QcQ~dXrEML8;*dKx; zElv$SJx=Ep@cuKcd;|~AFmJ&}4f7P9EUdrzd-6Qb7wjLd)NB6RVxI5O^>2Ar^i$+9 z{Dbl~y!JcI`@w_H?zXbt1n$qb_u$v-y@CUHrt39-U$WQBzZW?{`RFfpemjF_=U6^D z{A|^04nN?t)<2i;mO@IY}Q_yvm7 zfR|rr<6#S)e9rQT;g={*8=ih$=cVvSaT55IiqnIqU$c4L0X$Hg6z=_J2v446^%}$D zv#nkk+{d>G{IMF}X7F^N{sX^8^_s({U(s_5@KAAr-xvGOZHiNZ=cnqt0v?}b`843c z3v9cL;OXZrpE10qJTrJl*B!~t#XL{Hcc+#8x(CmGy2*+U;Ncf6&lLXq2W)!{;Z5x? zV|cFpE`zTb2jFYQ0r;A60KR4%fX8ZwC49{|a7(d${_NWk9uDnzRDnmTR|xm^~Pi6t3(?C3twNZLbPE)%X^| zeVnYqeVnYreVmNoH|}81K{epvFKmBr!E=poG2F-7Hr&VCF5Ji41n%Q)5ANgb0Pf>$ z3SUe6#SmWps>Vrprtvm|`?x)U`?x)W`?#IM57}M61BFiyuL%!@LI%<^FzFukY5{ zjuVuhZei^;OAYsKIwu`_$p@I?L|I`FGDepT|Dd;`?`a+n+1$m9N3~^*rGnm2bhHqw+C)q4Ej*MJnHef9g$ko8zeW%I46Xp4o zPqp=qQNAY6;OTC*-X*-R??Xj@D%LA{p{^6*O&#}BxR;;7^A}m1(w~d*pPcEr6!;ge zFz>>zxzap=A0vQZ?AC2Luk3VofkDbG(Z`FJhJU-XXtAoE3^SoL8 zqy(QXHd#5ZsK9HA6TrI)1?e#mV7s zQTxo{i+!zqmhk+`>Ob>h|9QXnA9#M>#@2t}sp5q2(-o%*pYE=CFnFvu5q#~1jWZ4S z;x6mYEqJInF?>VCX~U=gv^ZUOmRkEH@XZyc2hSB}08bPrh5Py55FQL|esTY@9FF%C)_&kEgP22A>_<_FN z;o0MD9(V>XZENSBbNE4uvw&Z6fX)p5R@}Zd#VNxNQJf0g^QppHic^Olt~e3A`AK`; zpb76Qj(@Mr&u8AOI75{8e8%vJ;$-l5D$WF+Y2JGVPjwv3;U_819Parn;X!EIE4Z`R z{(e8D1pl$_r-X37A6JFPYg>Cpa6j%h;CJe{--1__r+=r;+dn$R#<3pC*DkX0zYib1 z%gPVnS;_KD;rYGR&xi2Nt+qdo;EQjXkKx((%rm<7qX~Us*I83|e{SawGkE&MZB~w( zIedDJ#hJsCb!}W)z~lR^{1P6WXV)XaUB!M<+u7Dzf(QEkU>Tl0+1j&0S3898czxS0 zRd}?q#i_wZdf#jxo-6(U-dW%BAHjWp$>4q-K7j}N9_S1neND%&yNm7SaZ2#8toDI> zoCxl58t}#O)^06$qI~?jl)fMNINwA0&Z*XK2XG(fhj1U~NAQa@&S!8R=cjNV=Vx%w zXAbvqehK$+KDejY{*lJ{GTg^`|DLC}kB{?hl=pnP@TQMfa3AM;@M+h!`v9J4d!=w6 z=ZA35XAJjoK7;!>KY_=(&y~Y{oS(z<2dtm?_gB5$@{?`6t=?N~|LhZ*M}nts(Rn+( ze~QJA;GJ*VIM#s2Z#HkjlZ$P=EqM4{%QJ?1d$!?=$5=icc$(@u5xzLx@=4%9&F1%d z@c3~SzYotoXYDh9H{W69Q+Rxil^?=8*O-s!+TX|Uuy5^_!J~6+y%Tu-+VxkCM^kv} z-!)ql`$O<~i(iMAH?sYr3HR;d-%IrVcD~MkGL&}@|6W}0Nad^W{ByP)Yw+3cZ9b;~ z|AFE(;U1?2_c$H+j}@m2j}NiqR02Oj`3&IKD^3dcI77I{$>4uboC& zhx@$r0`75^aF0{|M{#@2l}`npKHJtC!bkEt+~>C=xW{S0Jx&V$hqn*>fAR%p`zGhw>{xp@J!>6a%epkA`n9rUnUxtrPvho#p^k$nssloSG zoH~4;V{Lqh;EStmoNU1lRGb+8Onql3fxklKd+?18vHKBy_?b=fA^a%C8Nv5g`7!)w zFV*jl;O|hJ8T?%;pTqz45{nZ)P~0x>Rh$O=1M&|1Bk}?K)A9`dMS1wo;(GmhtqT94 z&(p%A^X$CWzmx3av!7?SQ9ji9SQqZ+nLW6lXZGRe>pU}s`*~&#_w%|r-1Aw&6Te=5 zu-HC+K30O)0y{od;A6!J;peIStMGg?8~^L{3#^?Z_&xf5L<64d_p>c{s5mkFYQ49x z4X>SG`E=p&H!Pn7eyQr!gYR{t9Zv`Fh1w^D`}yh+ezQO43ICDSJAo&GUC++o`Oxyu z;cwFONOSn;IBSO`JXik@mc@SJ=g%d0@=x^>y5jhE(S1M4F0gU9f%5q+=1q8ZhItFV z*vQ7)7~V`QpEkVzXY&p`zQW>n;nTm^aXEqe{?&souC(}lc%pfj2|T#Y%1`0G-_777 z&GY1N&!_pX;`U0l-WI%nmetq4gW=o7-=o_@`M~e{!2LbCL-6d8ShOc?A zFT8(%<tNL-^;r^V- z9RA8z+4b5IKG`mOqz7zN2Wu7A>*Y)EpMKBE$8ay-hW}CJGx#y;pL4j6lhNA6eBP__ZTM&R zxAyPD6O|vsJ%gF2hsBiQ%Vvec{hi zK3#aMI4RucVKTVSw@lzZ-!g~$yh`|}V!QdgN)`UMW7Tf(+3c=z0}Hg;i1->!jlbcyc)vez0F7Pv}5td@LJbAgO`sppTIjOm`~x!JI!bCZPlJR z{0Hx{cAmq-eJuY4eDNKNUtPDj9Xs1v`5HXXd|e&>ly}>DBlzi>mm9%5$J=_x@X`0o zbNHv#|4Zu?^GxM6_yzJ7d@N7kSILL)pUbE4U(1*9Tjb&T#XRTo2K;_`2OivK*ZTwb zM)C~4nS2i4K^~Qg`S`fkfTz3I`9lid%hwC<>NqulN1hKnRrv+{eBC!GZ&1wB%U9qH z#jnF>iXXxKcM37w%lF}VtmlQ`osM}558r8?!9C6l-sxHS9Nv6``5gYUjcor~z#n*w z`4Zmzry@{#5>cp|UEr}7BiIm+TR;OW23oA6BKTkumiwtB_z z%j9kNeew=Gd9Af)7ryK6c7B_{r*E`8d+@%z4{yo`@Qyr%*W^QZ{Cb;z7{O~*yAM8w z$7;_EUXxGYhpPWf;UAaJ;GNf5JLK@Dw)-5O$`|m3de{aXlK0NQ*dDH-2zSum4 zU$VY!uMF;SCh);}R(=Xk^}dc7{8Q?OIlQdjh0Wn_IK#%t1^j^xY`y)*6#GxyxBfhU zr}7kD*6&&3#}?y!QvE-He@Whh`|*7U4|lZd!4doiiZg{bRX%uJF`u8SdxczBnp;1KHZHdRp31Wgf1bP#KSVx;A1=?~Z7k&JvynmQS!*al8BbI!f^7DK=hJ;IZO_aDN|Z6+Zo{ zofp;N`Ty+r62bkwuMK$8)BPs8;>7TesGZyJ^nEtocHxoYB=Af2vHhzDAAQ@l*8raX zSI13w^kI8X^#tzw*A(8r#zF>%-5{abW<@biR?or+>9LIo!|p=J4rBRz6UB*x7mzoU8Sg;GMr&`4HaJ z`;@Bi>~1UHg`cSB@e=rV*0KynaR1KZ7@luo{cHxmM)@z`{yoah6N>$FrsG!^?%$(K z;Ql?z44&^{8wi>1wYY z+`p$efP4Odwu9%tw%)52!k4O76~2keH{d0C6aFN52fn2|g?s-W!pqlK|6Ia%Rh;sc z#dZ$XwQ;5mPbLJouLR7@mE`JcB=eKN}w=@aE1oF3sTC zbF5xD{P>Nx4<7k9hd1?H?h+m=POxopyX^5@+b$(|{98NUsKE0BET0hmj(^%bKoy>y zx5-MLb$F~e5&T%iX~3_!*nWr7g6I2NJ~8}6#c9K*nVn~L;fdlT@P6Iexd%^wX>kVd z?Aex23jaTSZ*d4u^*iw~JXV|x{)K(QmG+##qd(ew$P6ARP7Z&c+Gh^0{mSaKgopcC zy@IgVe~t~+f8e#2t}o!h-s(T_4|@NB$C{_D!;@!OoQSUPJ2&9L#dbd2qVHvKV)*Ia zf8gO!*3MmcvZuvK;AbjM58kg?`wZa0Gc8UEzg*8h58>JImd_X-?qP8<_)QDjUK4m< z=fg92@C=KS!`Hn34IV$o>a~Q2yIY)KyJG(tsC`QC#ba#TsKCSBEKUf2qMjSC!t;|i zw($p^)htc~PnAyt9^It=0}m7@h7Y~}z=OJtt6g~Xbjv4!`|o;s@TPvBJAmiATAUQ_ z-#s6~vv;chz(d8!;CEhbR;omKvAw2oC_2)4>ezN70!S{c(z7Gy> zCN^%&;OUbrP7Z&8+Gh?gUt#UDgy%b2oM4Ay|JlpGuL_U$v*Ta|9x6@<|FQQUc&_(^ z)Zy6_qq(= zf#Rg_)7my}4B@p8+5F)co`#lB20!F!8h_x6zFkMm;K8;QCx>6H?KOwj^!|h;Jloph z1UnY{&rvV6d`j^0PIetyfd^YzoDhD!_aFFi*V(vQhbLQFoCrQsoCdrf*?!uB=UZ5u z7(U#`>eYtFm)U;Ng$Ih0z;9JPJ$UUmmd^m5Jkjz=;eXg#;}3kaxs4lRc&IoTeA@@? zKHvmCI?3uagU1!iCx@T#Q_E)#ukB&=TEg=uSe)QV#r|{Wr52|IkB_zey+T)<5T5G1 zt_n}|zL7dSdc5V+g>UenUB~s|$$FMghzJbHnxw+zn}Cxq{) zI1zkzc>^9EXz`oy_}i9e3{T|={9k)n|L?&&$8WrH{2jpinqSJ`*`>DL3Ebx=XYlwe zE1%Q9Z$5_ymzgi%>E-52cz&gMP%Un+1o!0|t9TRc%ePkX z81BorSMd(qm+!9P3EY?Ot>S&SFF#ntQ@AfbT*XIlUw*ubXK-JBvWidPzWi(z&*8rO zd=+27efi}o9_&=?w`-PP#mjJCzOsska9_T#<;J$og6>q|Q`PM2P!+rVo zD&B$n^4(QDf&22kRlE=PR_4FJD>3L%1(rUBzo~U%tMIM{r-hv5Gh0zIgtQG5fygY)x zP2Pa}^B!%uKkw0jpQ<>0_=)lX{L}I={G;*={xx|H|Gaz-zgS+{t=JCVkVo*x{l~U@ z44_ux^>ybsS`Wj=r>hnuJH z@HOT`c=lTJ5j>8~$ME0{<{3Oa(tHAs-e^9B=WjA!(zPFzpJD&s`=>v@*QYmae;>g8 z`MnhG&+p~*@7a7>N&BC#*Pq|3!2P+Y0eq?aM{v)-_RM0vfA!*OX5U?)i`4EtMa`J^u{u`3HWy3hbZvpDNt*ufsk6G5j@Z|0&$_ zZ|+xY|Dp14!9D*N?)j&5wf_X}`On~OB_xzXevz1RdDz>xd--nM>egOCUQ@H1!!#)3!&R@K~o__`I`48Y%DxVSD z^RGRpSg)D-PaW?0M{v(SfqVW#xaU8Hd;WEumwA1Er~I36&wmOpz1+s18Qk;F;huk} z^FCj1bcG#%8*tCR1^4`C@P8`*1>E!RJiplf4dvg3d;SUB^UvU({~Yf5FX5hlSLeCj z4x1fd?b(NW{^bLU`R}Uo6}aag!ae^O?)|3+_xuNN&%dJcanHY__N>ACzq0%6J@}aq z+w&45c%*ub;iK*CzIFy*DE7UZ*Pa~lur}x^_s%>Qu!I&^UvX)f2iwwU$5uifP4NexaU8Eze4#e;K3dC zd{pOU#qBci;}m@M9eWQ<0uS|m_zdpz5_9W6zt7;NMo9G2H8w!M$D!dd>0+ z4lU;C$K@*A>syC=eaG;tl;;%g`%Ck%;(BjZ`4-&wml*E*OA7b=Cve|iW^m8H)hgz* zP(B^F=f8kw+FzD%&p*)oneW%0e+2h=wl-bAOYOov|0Vn}&$aQh{OV%8JpVp?dzBx+ zJ^vK$`R8zN|I*>b^?LhP;GX{gzEFFP;GTc&h+_QmE3CeCxaS|iJ^uv0X5JF+`H$hA zfBiMZeD+a$HsPNC6#jCRpTRx<9PastuPv_E+rI(#{9AC(e+GYt@>#$=|IX`*>pfNF zyKv7xfqVWL-1DEqJ^v-#^Y6yRe9lomeYpP~w*2~H`HNJ(0{7p;hH(EqYz+6`!}jR< zJ?sGP`B&ah%;#syrv~@-AHgH_|1sR#KZASw2S*my>+N5Kd;8bnp8ptrtMZ@1J^$uW z#r6JMy5>Hp71;yPdf0?UUojefOpmYOZW~ix9iP7 z_aA)xU+iOXYVc?`%Oiq&z1r}_?z+B!d%c$Mr>S1$Hy7*c+ocarRj&cuw@V85?UKVi z|5CfSUhh{GxaXh1{r9as_^a31?*F@52!cM`-vcv*hhN@(rTiG)R{Kohi+9=Y;A(Fv z=AW%+zsqjYcd>G9c>h?7(|Bt!j_1>YALsoL{@E8jdL{o19>3h4&!56Q&jmbqn&pw7 zSIozsbC|=E={76tUBa`YwfykGW@4|S^gnBQk)vxzXw!@|3GmX@ceC-M*`pa zHQ`GBJ@{K=%Vz*jj@t!*^KzIo#V}4&UxI7AN?6vHdr_%l6+A{MUzD z`3gMq_J{j(CJp$DcdI<&(n?(|$UK zcb;VB7x2Y%%%g7<+tbIfHvHjTw_h0t`tT#Q-W=}x#TLW@AdTHokOjB3it1n4dDlGXYG^0 zL-q3s{K3%5=kV(^4$R?SR{7wj8y zc&P7?#_-!zz74)&JpsT${lEtnxYBk6&~64Jsd8RIKls@dtjT%7<_tAFA*RRX&3I z{@8$@t@1J4_s2H;Ln@!Z*Ni{#ZM^;AzW)y4dntYn_x>=4M+eyWSsE4F(~qZRc>ncw zK3s$Q_cZJ98z*+Yk-*n_t(}+VaBqh>ymO%C8GO5#ryr+E@cVU~3gJEuSK;N{;z#f` z+a2D&PRAYin(Yqn|G~;9a6gXp;Ct%4F@>+$?(l7NUYEhWpHJY)dN$vl!+o5e!~56R zcWr|26x-j&`4T*MgY}aT?&EwFey{2q!F^n6z?-|N{oy{&wBeb~>k_z+Gd=i4I`2*4 zejYM}C(pEeGPsYE6Zn2F*Kr^2FG~hlCwBbGu zbl^S?^x-}Z4B$QvSKnVAKRfT)Mir~JVHsHRW#&92J+HfCd61a~u zJ-ClEDcr}IA>7BA4DS2I1n&Dqa7nQ}eZMHdeZQ!}eZQ!|eZOeJeZOeIeZNTIzF!2F z7W4FeUV?i+58=LDs_@YvHeN;WHTxZW&3*^>{iqH1{V0L^e$<2eew4y}KN`Y)Kg!^~ zA5GxCALVf0kLGaSkAiWr{rz}Ug8T6(g!}kkh5Ps)!TtEvfcx<)hWqiT4fo?w0$;P= z!Po3}@HP7#e9e9b_izL-@M$ZCB=7rtoXOWaqu%4~q3#^FCO3u>a;Oaq95+bGCd0|G!)8zCZ&$ zy2|dGwBX6v7AJ-;*0JlmHvEsL*!64|o)0We0zXpk!|cHqhgrP_@JMk|_z91->-QnN z|6Lo$#_;$o%O|5>Z0$3FrzcrHGkBmlIsECW*Bstgy_WF!vzAYInf=%Ai+TT!;2U18 z?Fdg_qwNTfK4EcV_}aT@JHqqdTL0|A1I0<YO?6TxrLJYECdI?LLp1rHP_hTo$&ZTPcLO@oaHls2a1!z zpK-Xw8Ny%iIh*et!xQzx9PZyin8UN1?fPf|kDsIacktwmmQVD1M_FduYAo ztBRg$y$yJ%`)V=V<5zxEjMGs35j;P^%FXGDAN;r&$K#LT?^66JeDQ9ZS6{-v`&^s% zX#S)aKRMa{Zo&6!+V~K|-+HEvKM8#FSX*xo?)SI)aKC>xpzD6y1Rm7wd}9XhtZUz& zYW=iWuaA1af`|Hk`U0MwYRgxDR*VyB-oFm_`yy?4aEs;Hg?pYk+^-Af@H_Rq!4jUI zWcdYG7xVP%g%Z4Tx{bpXc&0cZ+^-|5@J8ACPaPgAP6YSs-v&I|#oDI@Pv2|xiQ#@- z+=jiA$+7bV|b)E8Qib0C-78pX7Kz(%O{5q z)z9bfvVKn#Op5*Fdn#XoH#N^3!hfRjRd~9iwSNTvwaPc(i?!`MA%^?BK^s0*|L?+M zwL=2;`Gg)k^XF~gf#Rg_t@K^>A-u17;0*p`m7l<4pXZ1ByulnEZf@Zr|uPOGo_%!>w0`I)Q zt`|ahUtWhVLiIOzu722{Yus$X{r>I%?&EXi+G76qsJ=D$w)@+7ID)4iwdKd~Pd#ME z%?$nw`5f-|iI?zD_hqZUDCW8U!5W|8Uav8HU7cr6;c23N2*2=oI)C_OalPT`_IC?D z*j(oi@IUzZ1AMCcp*^_Yr|iT1zUBb#_g^RQY-0D(W^iwx)~|~7s;GTBaBrUh+}mdX z56`f;OSrdBa9we|-ab`$bg`AI!Tr8Y9q#v^+VK8bHV${;-VRInQ`HXTX)#Z4hdSKb zp$`u~Zp#ng-VQ0;+aZH{KbgRTOD+Bs?(_RI_?r6=*B9#*?x67#?(H*xzgz7yg4YhR z^U@hSzR$*i@(soH`g5~2`sb}&oqoQ}o3-Jg=D#{{pFbVK^T%7BBlxtoQSivWG2GiB zgO}S@zHw`@UbSD_-yL{WK7fbv3A`>}!1s_>|4>|SL*9f(@*eyM`3T;U&)`jYa9eS` z2g$4OljKc!SKfsmCr{xW`2^mUFW|Afniuoz$(!&*-h-bjAHh@k3_g$t|5sdZUtWd( zMBapF@-F;xc?uuPC-9Mc0Uye%e=O!Xl{euNc@KVx-!`t#U zd?xS1r}8m;BA>$-^76cx&s<)I=khi@_>C>!hc8ur48LDKhu0OS{MTYWHF+IgmB;Xk zyazAKNAQw-27i>i^tWO@EqM*zl(*mwc@N%|kKi5o41TmcxU-l~TV919C2zuGc^7_! zd+25AH6mcPOvIQ+XSHm%I$A-p4>!jG0O;ca>K@5MYnEpNgTc@KWBd;}lJ=kSZ= z<$n~{JCrxznY;rZ%ZKo(d!;a&L>-jRp@Ear2xya8{^ zJMg3ALwG8m!Uystye|(QEar2%yaDgYd+?!r1V2|khfn3@WpTX|c^#g~WB6F!gOB7B z_(Hyb&*hbW74!L%Jc8%)Hhd-@!Yeb2JB63!OL!m;|6R=IVa0F2m+~IGrZ^*bRX&4< z^5CK3dbg5S;Rnf^@P@n#kK`%5E}y{nke41V=99{6@N?uX_&}b(`|=_DbomtClP}>P zl!yN*=9$PF@Q%C#kL3gS5%LV)lF#8ydG)`=e10Ks!YA@B{3r4hp2;Wh%jFCBSYG*G zF`tnJ-$K9!f& zD&`aZ&f2R6-%8$sSL6wNGx-o+l274*dOyyN-6 z+wyRoVm?R78}L}(gP$WG!3Xjgye|(PRa|dRUWF&}Cj2CM7v7Z*;TOuM@S%JOPvzBh zi+PUaP54OOgHPlmcqX62b9s5a;(BNDI(#Z`!CwXc8Vm{^HYyX0m z^Ebqfx@(H{zU%-3v%A%CFlfd5I}f#>o8{1*8H zzRS(F|1RL6yzGZpk`Lj*E!Iz_@P}2t zv`I0aiptmEWqAu;lK0?M`3SzFdndx3X=qUjJFxbDmZB<5a!}-=}Nw2k^(M{1E<)OD+Bw9{t^X0^eQn zXYhNyUhplo-cnes*QIxF8$9x_3g1EH2k@Qc6Zp~Ju{`JS5B<}gYhA)W@eeCM*shq* zkM6L1W;+x;zt6m}W6^h0K5h73@-95mev-gXI>okEAO1YWN#VKn^CA39#Tmn2syGvP ztTP7Hss;&kBW zDNX`Ub$;K2dz=CM+ln)Ur@B5F!F~VA;Fl@R6dozg4E_eyYYzXZ;w<5rt|x=1>`~Z` zCn`=EK2@9$9w|;0-dCJD{CA4efakj2Zo4XfX7Kg2U(Df=;w<3nyx;btpt?t4J8q&lWq7XnoeF#l#i_!# zR-8IK)4Wv#-%W9v@E_>BD~894(}p(`rwgwtpB_9fS^fL)*C|d4-$QXm@N|8PGlrj_ zI1_lJI5T*lI63?b#aX~#syM+;#qAicr@!GIrviVi;#A@Jx)!Gf_c#&!7{zJA6UAx4 zJx&{bvf^~%q2eTPkJE>LRB=*x`Y5Z{5Pq)Oc?|!o;!NO)?te_-mnlvTPZeiD*ZrF% z{07A-?Ofcx7b;E#9w|-;|EuEE;6G5D2p;JER)hY5FnHu&3;rX;X~Q$!U+lo2pg0Nq zmx|Mehq`Y%fbXg}L-`842b={mXv&z2S^hJRFXI`H)sCxPb=TAUu->otIH zt~f(@qBtYC$I0N^D9#j~>iM1-{L8A>9R5VbS;A92w-oGB+>V}48NRdPgz!jls_?P$ zsl#_yoCZACb6rii$BE(lC{70+D^3^gaeDCQDb4^MC{7B$PW2kWU#d76Jl6Ab6Zjp9 zGlL(Y`Gh$VDSpE5kv^Mw_-$Em_&#i_$1#fjh^rwM7S=&snzN z9;XXGPH}qhL~;6XkCVbrQJfJxRGcx~<4oWmRGb++)pM&k+~X|ZA5)xQ*Wz{z{P|b- zf7E{}@J}jE6&~sN;~L!KMDWilP7@yMx#||&$Bi~TRh%w7(evU7+~f4&+bd2APxZX} z5bopQ82&BAnZOf0-#>+KcB-8xIm)L6ze#c0@La!F>A<}|C-B=8rw@-6X8`x@K7`K|XADpE`=Si)ai;JG z6(@(s`aRVg{wlT45*}!LF4cs5z8TX7ojP;r`Y-|jKIp*S6Qru$G`xL;TH z;D;*C03In$3io-E4E}n>nZS>hPvIxXXYlvR=kQqb=}UN~`v~Fg#qHQroGSbi@)|tc z!rqS;!K?4E@xKB8<`L#i_!ktv1+V>TtCjt|4Nnxm15a#-ZWezGk2=;48T=;2nZW-dpTbkU7c_@Y6+eGxar=JhOSawx{Cs)v zu44I-ybS-oJcM5*ufea8NAMfuP5AHSG5mIU2mUvC3cp`Igg-1F!8b~+ea7%j|b2%gQUhga`Xr{|JsN_LKkW`GgX@{8bzOEAV7* zTRw!}=HKyw-~aLrR`RLCv%1BJ;Ql?J2K=Rp(}D+kS)3UD3gy#=FJEf;B=G;f%G$FB zpZ;WvmHboqe<$0m_z+(H#^YB!gCBOV#h<|A*%MdF=kT|jsqG7oj0ei@2LD7o*iWIgA;=?GI1>Z}XM@6T{!B_-**F4_5z&zgp#c@a#pFPYVB>$`9dXwND0rmda1y$$=I> zhi`VJ`ae87Lhb+VV*7vMCF=k1=-syehVZ(NSMdHz)&B6GYyWD%n|oXN7=GZZ)&Jq~ z%<@U#Z~Jn%vK@Qy{C66E;Q!Ej$cOOcRE4EKxDO8%Cp@vZy-xeG#i_wREsx-- zya~TR9>aei@4zSW1b&me5C5|~h5ubXg8y5d!Poyv7(DWC3V)(Jhwms~!1tC1?_@NAP>(8T=vn6u#b9 zZM)>~E#wRMF7n{L#rEG{UWPa2A^cVH8vO0@2>yO~6MniphMy(xz`r6-;NO<_;Xjn8 z@ayCw_|5VRey4m2|Cc<6Kk8iDE(`eP^5CRm`|m6-!}pVi@Pp(v_~G&h{#JPt{%&~; zKTY0&e@dRfzaa0!zadZIWBCaF3wZ|5@2iybnK1p2E+SkKo^tXYe1&r|_T3bNFxN3-~?q z;N)WauYI0vmomI858+$OYw(@r5qvLs6aGAT41bBd13yBZz~3(K!%vo{@U!G2_*dl_ z{37`jex*Ez|3bci|6U%PQf&XbO<1-?mo`-qL(m1m9WZoACYQG5m0O2mUU3 z0{@7-5C4ihgGd@7&9ZYl_{JVVvR`Zo0+_^TAB2X7v_=}HR@;F02_@K|w%@JkN1 ze8%wnt(H#)f3Kd)n!r07+V5Ux@KkYf_=Q{BeC!-P+QjB#m+<5*mQU~@`>*$(3my(v z+Mxtb&$0HYz(d6e;WubMt-?FMwEeUW&)b$q1iyJ#`+auv-8xY&Po_va$u(`1{K?Nx!NZ?xq@_&c;;RN=LC zY`>_(BgKi}`|JBk4S0Tt<TTR*J8L&XWFMsZ5;0#6kugrB20Rd~3s#vgd3I1&6j z#cBLM_U=4>zIy)u|4G}VXk;6OPL!rahPF}BAu$R~+NO;YEt7m0w2UPVg(;~~^qCTl zwb8<8VfaKRTVrja7$r{mq-jx8S|(J#&-?wjKi@si?eg{i?+^VUbM3tD=ka`==UL~R zGw|Riw!XLM%8B749oMztKhyDH7oHtr{gS{hRy{rVOgRI1qMQ_dxpIc^j+WOL9w{e- zU!j}{{PgW@eV@St<>c@q^gGmZc&6W>UeXWN_&K#WekMBJD8Y}}M%y2FqMQ)^VeQ|m z@Y+_|{=fs}MDTCkXWQon{BZp~V+)=gWbKLJ7rXN>_{&bz@iRP8P6Gdua(eLFbRIW= zN6Jazd;ipqSBLOTf4$UE{e0&kxY}fq&ACANY@y z(}kzXN#LzpZ2Q@Ruc@2?JW@^yzh)~tP8h;3x>frxc)q{J5B%@SnZTbkx11R~QBDru zT>JMqeDr(U&M)Dia)Q%}!mrUdtisP!P92^qCxTz*#t(ca z<+R|ja$@*5-2MxG|NFK5fd|S-;Gb~YANVWW_6MHsYyFeLKYNF^Kkz`m&p3uh%E{pF z_mL;??1{FWpTPs=iVlQ96%nz(=ducAy1M-e~QK;Xl&v?Y7~a!>pbzJW@^q-*ZjdZuH>& zwVt_hzcYa6du#l_U#R~P1YHlG!u-=gu{fY)?AyakVx6T@#;P8(i6&90Ml;em1z_&v($!FPC{#t%H% z!`hX?kJ9y)A^fTXY`qx61Lb7!hgHu6{vqYe;K^&Po*aIaJAQ`W?v9_~p>l$Lar}Hx z&)t;Z{R_4Ifu{}o`w)Jba;orLIdyoboCrQpP6HlZWc9S**=wwx7@p|-v<-h!XydsH zkCcHG_R z{wuWofv2yudNTOa)z1_7iTZwU2G3t%IXQeIH-6yFXWM$Zga^tA&a^x?ey-5+D#0Jt zez^iqBm4UhexY)z@KcpjheyhZ;4e_S8t~gr*7$*EyIMUlyr!Hs{JqNQ!XxD*@HgD7 z^LKdpEIVEu!1I@@o-Y^sxqtFg?06f#-A%R~7{QZc?L4~nm10izCHq{5*HSy)h~TH6 zrGA44AF+C3c(jSNw+&CeYV+;FL-`P%ec#S6#_;%3bN6>{9M135@sIMHhpMLzUswI+ z>T=Uxv$1VAdYC>?&H$dMKT~*km(??byPPpRR89szL+1q(xXYQrvoBgd=Wuu4J%^Wd z|HcxYC@1(@ar|7R=io~4J6kqRD)3l2A>5sRRpH-IP92`7R!0QiNd4S^hhMbu+=8dd ziQ(=%w++vKV&km~kCc_zTvq6#iF@!y)`*HSI6p>E|scgI}R~ zCh)$-^9-JU&T?}2#`^wr4xhGly$+tAXF0+7_HVa-m33XB1P`BQ{ZoPGpS8aa;qLlI z72ec#tPYQq6TyF}^MVGvrtN17o}X*=#PEL}t>X=Nq;c4Vr{`Es0>9pkANZFJw)JZO z&jyy0!Z+9UZ3ut%ftHiOzjD9z{{-IC^f`QoyKVY8{Nt)K99ms&xksv}3J-3uoH{&K zyCQf~ISqKMoEAJ#P7H6aX8X-Hd~}U%H@fiXzqP#JmFw*LrXIX}wLQl=fM;i0P6oe! z`>j{*->2{^^qgk?Z#=%V*w5SEY3<73hv0Yt-=>Sw|eUE$OXa-NdWp(86Roe&nVh5c+!qbuE1mj}6_SE-)C3y1=8z&Wb{!RP)5dO#C+Bm7g z>PAv(^f!WVzEoH{(c#Qr{pzenqD8{WUs z?z2kZr@G}1f5!W?oq?b2wlna>lkEIu3IBqoFaJ-my-n?RCUAE=F@?M1kHHnioRRWJ z@L(Iezq4Y%bo3bqXhrejpk+eRQ*!HWRmJ80Q%xVj)9>1K z$QnFb>q#qfTJT)=+xOvadpdx-?SA8j#d=&nwBYOB6|S6b2R{A91}l9EcXck|eSObZ z`B8Dc?z&|NUtDeZRk+(9EaA^oo#h`F^W#t0@kR&k>g>aFZD$7Xgq;^cOwZ_L~!U{}S6T%;2Fu=kTK+v*j{}=gSRO&UXpVCRRuA^I|>6DyIa$ zVh@d5c&wZdzUH=ezFdWe+CSFefpQ}FSJXcZcyP7#PYa%2ZS9HSA6L8D@QZe|{dE@} zD<^?}Ksi15QOX&>1LdUfk1A&f-$*%Qc=}UoR|Y>zITQE~cCdEM;E{52_!-KX!(XAC zB|O*o4t`M_Kc81l34WLEf3Cn28G~- zw&1&LV$-+bYsowCi?*_HmB9Db^cnqB>z@g{f0J#OX7KQOYkv+uQu%Xu`3T#OFX6#; zmJ{4j?9aVaPYGVW#g2O`@aVVp_aXd!dJk$99vo@))ak#moCxl&(>CD2;g-{aXV+Rz z0$(+r;hX8cqdxq}`d(%LcgIU9{Jn1Z!Z&vBse>n5+J1IMzsBm%;g{+9&m5kzqG#(;UCxg7_0EkEjDiJ@cb8+lfuJaTR#lp z%{N&+6L_s{K85FBu2JqyCHvJgx z@-w>fbGXZ&!(D#qZ~yQ5%XH<};4Z%oclj;&<=Vc*@O?Jdb`L(f$=1^zd>u_cgm<)D zM)0P53J;FA{-41|zqS5f!k=`#U7rc=F7{hl(#SX4?P|goy5GA4pZ?0G@4{;up96TL{1o2#v#r+|JXih%Uc1ibJBK%wzksK2 zwQ*8j6x+K{eg!_&^mTZw{0LsY-s*|rfwn(w_*B#P;E~2*A0E1K3xBSTE5`7*oNVK8 z2H#xM=kTX$`rzJT|J)JVdRKy<{9`+wiQpg8eyRZ<)$O`>3m(mEelh$^<+S0^2lV|T zJWx&o-&@BUJ$Um58*c-6`g^Mi^~JN}u#Yk#o)>2iE(X4d>Pg^h{#D0a z@XzgR=bL@_K{_uUz)$(RGk&aJ-EB>Fn~wuhY38n$+qWHxa)@*d~~BtpTk{0M9X5k+;yQ8?vA&I zaCf{tfxF}FDcl`zFW~NYdkJ^P+wF&obuP}f@z8z&pzC(RI8%q`Tv4_^R_kcy^r52jL5M zJ_yflwQ(E#v)FGPO<#k%>xU8C{SHwBzT0=~_@oIxQonc8f*+^rw=w*=pSSIH8@`9~ zJMg{bUHD{S+w%lo*8K)OxNBD*eueT=_({83okREmntlX-i+l{P>UwnsKU&jI;I7}M z@c+{EIs6BIvwoPve=1+VKcxF8f`1jq$-9+bf}bES!`<{1xSKwNhh@87SB1OjYjD>O zb@oIrmw)=^da1h^BVkU-8WT-pQihlBDni~`3Br=f12>2`ZI>RangpnoDO{7 z6Er^I-&B4AcjKf7cR79d-5OUZ+)Y1(yZ#x$-S&12KR|V6a5qjS@I&XeJ)FW_|IFae z(taw3k593FTfiSZ#C!>N^9>#?j?adBP6zI$FT>q9slboZ{wRdIc2(iuQNPvTZu&a> zDor23-8gK*-F~(O|G9Ev_%-ql{Cas8ev3STyZZZZ*KY&3n?8lRc8%aJXAE~a8QkUM zaCbZ~hr8o}1>EH?;VwUTtT?U~>YozatzTvM>+ZJwW(aq6R^e{_t-;-T7r`G;Jq@^< zz6p2Jx8QF2819bW+Hm*#m>sz5&o2BCH?H6=zXx~Q+dkao58y68g}d$B5bm~bBe=^S z{~OQ#jZglKXPa&xtoX-Wmvi^iPT-xY&O6|_a)Zr^Ih*VEA4~8<{@cD6sKAroXdJ?~ zQBDMy|;xs8Vkd>{2s z2wyeM;i3AW4j-wW2=3Oq20Tzs3qDg$40r2G8~*ikZN2Nl->G&baQ8dxJ$SsD#yLE= z&iXTjyY*!VZ?0)MWB4akPpw*9US%!caEqc};KnWdoAL(yH}W>Ts`@+dD>Z!|-qQ3L z{Fj=30^j-^eXk1dD<_A$ew)KX_5Tv?`YqhD*gxqXZ9G@u(Vxw0^qb7<@TsP6!TY=0 z^}`tcnt`>q4S(MAY>(A}zgW}Pwkp=|<{QJ^_OlJ2{;$>FgS+i#AD(Oa5!`J*$MA)w zpTXVsGlvJaT0Ozm#df*vX9=D^V$;X)ue$g4z{|T>|99adjjIHHgT`kM{;V@?c@5xm z<)rYow&z3mVs7=1;o$0|J3c&Ni`zq9^{;2%^@1Ag--tezIUt(+Ksv)k^$Q!V!{ zT{#K-zSPc_d+-;Zrtt%xYrZMGq;?JAsT)7=sp`q#Ybs{~KS#%zGkEFu*3UWI9iPnM zwc9j);0@&j+Z4x-J02*(BdymJxH}#Q;qG{#3J;Z2hr8o}2=0yt8t_axEx0=#h~e&d zpbc-{X#LZL&(uE&{H0n?d+_itmNS5tX12UixZ6Jt;fseXXAF1y#~dEL%(kC%c)Xi= zSSv2ytM0OTs_?~+?YYqge0P1H(u7a--u@1JD~;zaJW@YraJLD)3A>A>3_es_>unY&%njyX{N_cjpTYcoQyxZCbc z;H#Z#{XBy=m6O9aRXua~;ufoC2~U(0yrejOUgX9Pys7ubRNzzPgz(LkQ-x0_*3Wf# zSR^Vzn={!CwLpL4i- zuh$%&ZfV!sOWPH5rn;_PhL_%I_c7MsZ#~qG6YB8b2%A2EyZ31I;F;~GDz7+1> zJ2Zr+TUoot@JKls+`R{B0?(_KGlQpVsh{Di-tPpTzQ}Tx@JKnq_Qn2p@8c@L6ZKC8 z9x5k3gkPbYF+5aG24D4lIrux3GlOSOuy*Be_r9q)yr%IS>|lTA zt^>RGO_kv3HrAgJ{H5AXrf_$DJ%qdS=n;I?`7+#{7iVyH{+q+y@zosej-wWEcf7QO zyZVD2i~Zy3FTq{?Ww@)q0(bRCa94i=?&@#CUHvh9!!>Mw)P}p~zdG=H^q$59exBYx z)`O=%u=@x4@PpiVfM>f|KMvvHE6qpn{N?6jcoLas@aSdc6ZmyYTYsnUc&PW%!^5we z=kVH_&F64;y-f1+x4PW=^=IZnHc4hE^Jcm!UzR%%qKe~jw z{e4yYe^-CFsci@9@P*!^IELS&`+J)^75kyDB`fUck zP1mt=xVtXDgx{>|Z>3ih`@g35eU;&^e`@e(HT(Mpe5C91ZMgg1q62r|uZ-dO*7h7$ zUE6!tZ)d9?n(*=!)^A<-31`@OWde8CWd`tt#?KI5)^)iN-1W~Co~&m5Gl!2NyUtV6 z{>`;3*7f2FUDx+RxZ6KA;kCo9A6oGIqvkXCQ2kkXRdGBVr0cyQ+?^lR;r*{!egl5j zDc1i@xa+qLJb8lE(SxTq*m?aB?ygUb;qE%xl76z)ncP)u?;mu&nZg^Jgex}lK@NBC zPoBfeJK6GG!aJ|E`2~L~&exqkmf)lH?76=RJZsqBhwxSJVTONdTN`0@c&eNT?#@FS z@ZdzNrv;Cd6T{tkXdAwGg5`AKk#Z8ad(UtW-aooWUlfqBd^?@NgS>29L#_;?# z*3TK-org}~$-36A89Y`_4tM9*b9n6myRNc?hsp`=E{-2}eqDlZu$?XU3Os+c#t+RwaJRl!;BI{n;ck7e!rl5_hi7g*g}e2=0e945%pTOPvK7+gUJ%_vXeGYf)`x5Tf_h3;R zKW=?5!QJ{^fxGoRguC^<3U}*!9q!im2=3PR20YYuwFP(UdjdasQ#-Fu;p^*rt0COo z_d9`aqUopb7t0s$9py{-{_@Jb#s1$@9>d=xZ^P4_Y&+bA2Zva00zX1IJ^11zea`|9 z4z`>W?v~3C9)CdhFTt~eEGL7%P4!IR`61S>89Y)>4u7|D=J3wTwV#5g2UZ!x?memo#PgJ`a@a!PVX~84q#PCy;(}qWN z%jv??1FW6|{;5yddenoDcC?%UJl)@NQut?7&k!DIJdfe&ewLHLH*(L3!|&ePp5vau z^L;HRhrd9-%Q=T9dt1Af^nENR_`Chvji2|eZSM&z!Jl)!U9YRa!#CRBhwy!sQ-yE! zWy`6<)4eSxg1=Qc4S4BmmeYa<%8B8xRsXc%$-Wvt@UUt1B=GIs?@hqdy>$O1JlxB2 zQusgDvVI=I!@YH!43FPnIT`#sjl&5%(0V$9r?0o196nUe9G)Dk@k4)|O1^!?U96?nF%{e1}UX?a!Qp|-1ax^g1;qqp06Wdq*d+v;h-lRd1S1pWiHs|Ww7 zybu4Ad;q^yp2DA?`^JaxC(1|g7s$u(ZR8nzH~9pCf2MBx0ME~`c2(%giQukX4ftYn?JwZ*Nc{u+me`xI*z)O$W_Bn^YU(fZ=;kDyzd$ok8ms>r-KZ^bQfE#b{vd(KN z@K`w^{08?s)bP)I$=X$ihsufIZh1A}Yk$RZTJZcbYflV+%-x>=&q5oAU3j9L1pX2C z{see)2c2iZL*=CKK+nSt;mzk+J!5#L@sq(X`lqd56L|6*%bC%Ylf&0lJ#%>f1dShf zq@3WN#qs0TixPaIZ^&*5{r{C48!ke2}yXx@l600MEKS%FFXu!YyWsM(rqMR7+ z-rv=RXR4evUa6#_de$#{I6fPc8%ewa&q|ecr9Oe zw2zJF(!Yw!H{H)Xgn#RNn{Nak>3g3BytbBI&u_w$C+a#sJpH_R44O3oh2OC*_4*%-|HXi2i(ROyeyo85uv*i&y zQd}-qY^CcP@TcCY>l^UsX#4vRzMrlGRN?VKTJPv>%ZcDSYJ1Xvmk+d@7Ccl=41ck% zC$!tFsmnpKSj%Z z2!H(-EoTf5m6O3gsd^^x->aS(Jc+HI9KNyYnZqwsJxh3?oZzwI_<64T{RnvT^|roO z;L)4y??d>xYF8Eh$3c0Ff^V+hiD zJG{THZGXD(>;%h6;O@Dw9=v&n)iZ!+$6HPc|Dqc|@Q!wtmOo&701s|?JB{W@3;FjD)8{#mJ`9hp_~Rh__OZIg{LQ5 zP7J@?Eq8e58q4XzW9209?<=PVul?L|2Jk>RDg2>(Y`qx5@A%_7E01Hx@btsht_*&) z>Y2cQr<@r)Qce!PRylL{GjFnbmhe2$a(_Z`xv#gdc9r0LUB|A#Bjtqf8&yvgzN6}? z!?O=*{J?KlP6M9oWZSV8JpG`?5By%`wBhAH*!tduhssIdk0_@HPli^{fPSLYlfp~) z=z2apKE~G5F+5aG27iWfCh)12*9@L%zZ0xpT<(Xv-_3zHx3Kf63Ov_(5y4-ndK&QP zrCMI_>?|!W_&&;M!(C1n?s5|Np~~sO7YFG64S1+}Qur~-8Nyx8818a1__4~Fz@vR^ zxzFJ7nbto!{5HK`eGYdyOSsDk)+jFbMD>*5AA5q`4^V;UpR#&F_!-Kn!ejj&WgQ;$ zEhmCs=hk<4s{18c@Z@xjANXcEA8fhO--x++T9qh=jwN|m7?<&6opPpy?g$4Xqnm%8jh1D;;4>ml&$ zY>hYgIt$yLx8eCEmeYkN%1PjBUZwkm;EBF38^A;5r11DY+m8?7wQpNJV|ex%YgZ26 zM(vuz`yaOb<`SNLR?BOh;&Ok#`#o{^*}H}-kC$rj#JxusUe^8mF?{{|)c^2x|7`0; z7hYP;`ZWCc%qyPzJqcm@O%Db?V7=-%E{r+zSP<~ zhi9s12`@dtrVpN2T#l!?@B82`rvi65A$%9Ls|r8!zjQqT-cUUe{PoIdz?=GBxCI|5 zCx-8*oHjhaQR^>!s+v67ZgKf_01I=c32#IHv@6ITg6e3E`*d{HqGD={iy!ewO+n zg75sW)zg5xoEF^W#PB_Je@h#F#8I}N?ZUsQdJ_11RZkD@at3ghlfut(=cn*Q{WFID z!i^vJcT~>=?s8^umy^TKRXua~2{Y@TC4A(@&w9o2^ML9p!Cg)T?s7u-xBq7Q$0|H| zm96h}_)_&m@XH_7_<_5e7To2;@C#K>8(vdAU3j>Lt&a(O9j#wIxXT&9T}}#rvl~C~ z^07L8fZyiE4}9BySpQ7mE@uXJIXQgwzuW$O4xg@X=ch~f+L}IiQgQs8qk2kkms5ee zoDlAQ2dWCc`#9~t;LlM#5&Ux1(}2627To2;@Z+9h)J@YgGU2;cBn>z^@vZ%v=UkCIQ|E@uXJIXV2D%9+DAP(4fd@tQt(a&i1T>z~%2 zCAiC}z+FxVU*jQdf8ftkJ#~0n>w5&>S@ksFE~f=|IWc@A)zgOWaJ((AF1+WqKk$#M zo*vxg4B##&g`c7RAHt7!$ItLjYx)fS4fzD_a%OOslf!quO27u@Bv;4UYIZ=-tJ@EcT57k-cG zN#J{_o*vxg4B##&g`fDKtzSdY2m0c$d|) zgilpZP%e(2H>sWy+~ri@E+>Rvu6nBQ4Lep(9lmtiANYqdt1cR2&N%Sqv%apMQR{W~>&;2qVI!4J^!*aYr!W^k92!+-C_5BwN6 ze&7Su6Fj9jeok`dUvQUGfxDa#ez6-r@R|Cj4xg)@2!53tKX8}Rg1ej;{v|hl;Ag9z zF1);!#t(cA9nbgRE@uFDIVt?dZv4P+)A}`r*Hupj-&*xd;4WtdcR4xyqi+1bzoqqS z36E7zut9PB?5lc8aFSm7e&A=j@dI}`1Gvjc;YYgh18?g3*cd)@;|IR({dV3ofxDa;+~wr(H@NG-@Ttx( zmhkf0wtfXqEsmdOsGbts%Yh9>B3XhlfYl7dU|k|Gl09C6n@fK*8fBJHNVyML-cKD#cugjY%$KTj);pL^Z@3+{3%aF-LpkJNjrtMJY>_Ps_O9;u!P{)p;nz+FxY z?s8)I0b1^Dc&zI!UHD5ieFE>f^LMz*8Ngjm3g2J(L-^<2{tMn#|77r6w11z#UCs>d za&ma$-YX72^J8|tvxN8E_<4G9{Ol0e_e~|Z%c;O!P6&UG8$a;6>Z!xWZv4P^Q#}p1 z%W1(~P7HsS+y20x?T(+}bJdf;o2sV=cR2&N%Squ?ecv>Mhx#653_n=oErVB=I{$*Z zoEhBZ8dA! zU!>nPZopkm3+{4a_(tyh9o{_0>gmFVswaV;p#JH>UCsdRa#HyAs%HofK5O-i;Xica z2mWa{e&8-=26s6*d=J$#hc{Kv5`Lo_KYD(}_PRkZa^nZ?aw>3_6T%-*eih#QuD-v3 zC;y}S7U1r=ur@r^ebfouy%)L%&o%uF{#4!1vw+w1-mp%kxPGO2pWXoO-jABXn|iO& z5boaVH-fwO1CHVD{bF4`hvb&mW~#prpXxcL@`lCwbJbsmyZR%ztG@wv?ZdY+{Zckgc;!BaioG>6Cc+PGT4-TS1f&o1`QG_iVW^xAqW z?*pmBV@)5y-TQUN@J)3;&lDcq@QjuD;d6@hEUq=L!CgPp>4%qB<}~5y$E+V(@P2Cd z75Ct=)`vbkdBAc8aQD868N8|bb9lLJ{n>bKvAym+#x3{`>bDL&{IxBw>c+@<(CS>m zpQ7``a#)L1b9 zdeX{qK7|M8*>a!3`&zzBxNC2)X|bO8Pd48cJiEy}hP!sn;IC4<7Vs#y^{%p6alYSv zjrCg-?#5vUo~XTD_s^dWP^s@0(b{v#V|T;049{T|ac;`>7xL@WrpK zKNoP<-ty+f{P1(Od@Jxo>th}6+8e=ZKezb~;LVA73U}?QREzZ-rFPZe!F`s~hr4!- z;6ZBb8pAhvt&N8n+_fu*FYdAV*0(6uncQt2!Ckw?@DHk8Q+PJFoZ6PfeAlifJXgC~ z@V9E5cj2yG2|T~m<~xH&e=*PDu3fFIigkWQ?drhGAF=!Yr*PM<1w8+f_Al_M?x(G6 zU7W9LR|wCxv+GM;c)YE70(b3N!oQ++m0!sDTD$7-(bD>@19$E1!;`OAdk1vgKR<@M z_Ga+fMpn-R?%sn_*``>3vtrYSaMuq5_+{#c5j@vthS< z+8e|3qphFE@R~e>yLQ!U#X5hXb~WMUw_E-Q?%FklM_;pc&EU^byOwa*uHePRdYZ@B zd|UAJZRRoDwQB~yN$py|Q;pB&ON#S#?drgT^Q~Q7czlyxpBcbiyHa@PA*+80&$NFD zwk_7<+SP?G)UH0fdA1!#E#R(Qr?R=X) zg}e4vwlCHh+-u+C)ZkOCcYU~P?}$FMLgI0fihhm-aiRKa9wQCGt zU+tR0BQ3|;j>UY}t|mOVz}nS<->l=bF5I;%f#;vG`e*Q(-s_daUAtQKVx60)T^)GT zvHi{z?%K7Wt6fX@RWG#TmdZF?Bj5WYA@`$4#C*Ao5`wX3{SalXmTw!U=W zu3de2a*-{s0sK&HC&zHtt_;4&ZN8PAi}ghE5boMFfWKPp8o_Hi{weQL%y;do!NZHK zU3K_ZH4a;F*RB}ezs1@$hUe<%4DQ-he_654gVe4jeA>4B5!|(F3eVN98T@FqYYBJl z3SM5Ur+<{qw*{XbX&%E}yJqk_u=B~y0x^{Ko$v13yb>SWDj|Onpt`wg9 z-RfV$J33DZqGCO+U0wLww7mN8vf8zPyLOddQOr-jY3-`OYwkP_?%EZ>Yj;??2K3v_ zQ@Cqa<(0*HKCE`t;7z>`tq*tY8pFS&_k+#guKt`}wL0f;_a4UaZpAtWs&fi=b=F>0 zoZi*hg1b7qa93vn&!25|_TaA0*6zjmp07GP@Xkh-(}ln03-&$f0KU@~&8KkJt_3{T z`v8~ln(7Z;U97XD_X<|w?tPRsc%t`Cw&7*Hm$Cz&>bh|PcjIsgzf%2Heoe8?@*aAx z1w4Dc^=BO(${TR^9=8@e)O*P~aM#a$c>EphAK_*F4p#~fUSstS;kD;l{bP8drZ2%al$YVpmsj9h$|HDP-hfB)CVUTh3-0QV;rnX(HvC|D z2mV%h7w+1dz~83nd+_(j`|uOx1Nh1E6n>U`2tQAr!M`)teIW4U0UK8{__Si-Kks_Y2f-8)~`3 zf32K3e0r+oEa91Qf)5v$`|p)gg2$&=P6giD$m)sUxpErt=nXc%CSBt)hVQh7ZQt7P z{w_9ty6~)N`3d~XdS65j-n_=zHGt=PSxySSPwg7Qr@LA`V|b{X9R9YgtzC2Y2KU=_ zk0m_Y*XjvAV*hscMck(Age7?MP`l1kfk(;-;k_$uIacA>n{^$GzK_)r!FO=)!Gnj} zSWb(soEUzWes{YK&kwSkE! z{BYHIa?#h-bF6)MQ#mR9Fw0qdw3zeOzuWeC3IC9O|ET=2;`Hy;eUBA*e@(mY8o{fo zvjJ~zZ1uF@@mp+uG5m$@xm|d!_w{w*p>h)Vw#wF;xXATc9wVWk9kF8z7$BWDNwaO{ML$#{{Pn8qG_fk$3 zKHAI9YwGYwIT3tc}F4nJBsb9l6&2oU#qqPFem}GX zZ=P(&jTLyRoDlvF)l-G%q197|2g-@y$10}*ze)Yvf@gjUV`>JKFK*1l~W- z*7q4aQcey(U-iu4*YB(2Sa{a5dV*7ns>SY{?_);K_-0opuI~j?(gi zKkw7FJ)Fa9AGVw&JWx(>T5);Zpx>h`!JBQ%slc-%?e9bQKiuzH!uxNtoH{&FP6QvT ze;V-M1D4Z*N6Lxe&s6`k;Zb5aU3ghJ3H(PJTR->Uoxj@lc>qt3u>MKmmmY07LwNit z8_#2Sez@gi@IPt1P2jGc8Qj&A!~dw9Io#E=gu8lzPZr0|g_l^nO7QR_8b9z%?F!*{ zYPnb8`3E(A;Hh#V_{DDgz~lF8{LqyX!`=0hHhik*h`aDmISKr)>gOIjc)!LEJb$ak z5BzV+8N#FEEN2W)b^e~i=X$T{9R6heF7^W6J~Ui;e|~U!ak+m?zZ+75-#N1VX$77f zZ+{=c&sR#~6T?sWn;loQ;V-^i#|iL2ISJhT z-dqpf*u&lnJ%C5=wt7r; z>&6;C@K`x9{FUq3_OK1_G;BTX!qfk$ecT)#tSRf!9<|jjsGUys7CU_)eO>0pE9b{k{PFSz0d^ zaCaX?`OISf#5!+kz}@%a1NybL9ZTWvIobG&#r%^u)%FlxzSWjj2mWIHj(h_DYu$2s z@Z{xoJ$wL9KWBBM@Y0&vpTb{#sx99!JpXUY$>9I1-+h?C&v}t8ml-@%P7WX3Z{Huy z;lX;=KTG;qR!{h)Vn5%jey+kFkk{a^yu%QU+*gjFD}+I*LCbF zygRY`0PFC0M_axL-2F~s5B_Q84B*Mvtd11E=8G(U2*2Q`R?iq7C?|t&q2uHUys3I- z@a(HrPY!=%bsL9s_+wXFJxh3~oZuV9etyZ$)~*t~{AS%J0MEW`e;>lnbHDouzg+dy z;jwZe_%=1GrvZ;*tEUBzHJ%3W&6P8PH@|A@dq(f;e!o$%f1afMR~4S`YUhJ>cs#Vf zkKn7?1uuWX=G%hj=UYw!fAy}mzv#g~q~AM8;oGVohVVq!6Gm`%-jTuG^b`02cUnDD z_`dQPd~I#_7V!J@{;uF##s0rf=bI(?HC>x;2){!2EmYwfKUd$6!(F=?@Vfe~33v6! z@IS9^`5m~czYBNukKr3?zEk)MwVkP5QtThMUv9#4ZGU=jw|yMILv1f+@a#>t-i6;O z&i4v!H>&UtKh^eMHTcWq4fuDI(}F*|f#r1If7J9{_%Ael0)Ml70RP-eZ99{~H~EL{ z$AfXPy+3#DgT26y-KK!rgH|m=)`PAh6|Ch5!0f+77_o z_XrKR``xtkx5fN34zZjed~~_K|AOa-+w>FowmQz7!c*ns@bVFs6JA%W=ly@T?MxN^ zsRQi_V;!FT%>F)t57e#(yrz0u@N{B1G5jmaX~WB(w*KkD1LY*}SnFL69*iw#0MD+r zdQ!MMj~K$s${E8GY2e^Jvsa%T3&N_{(WoL5+16a;QHcnf6f^;9!l^e zvz!V%{i*$Z2>-p#qS^yii{qyNa-mBW8@ zr`0ouf9(xA9)r8(9{kSo-1iRdJ(eYStaerCswaeB_k0_NRe19k)~-4{R89mxMe9Wa zp8e8tTJZdb8b9!h-S!6_Ydm-1iEpZv4Pq{{+7;j-MTLou>r9@-VBX0*_Ts1n(^E zIHUnjoLnP>2tawhcSZTcyE zdZGCYo?K_1!>6ie4iA55)0bw&%c&QEl-qm(q*@aKvX~!Wc{D%jd58?akxOW8KP{(^2{I*LhX9ABuY2Uxh=()8& zhu^K|vgdG@v!p90_}^lGu6L=`Q-UXFSUnYZ^k@6~5PpW9BdEe%P95%YBKY^!KMiSr#D$WG5jCOX~WagEvHM@_)Fnut6f9*z318Yk{R4R|2T!;s_QljxSM_nci$g{ zw-%R|+fG*DZaW#lPko|YmutY?b=oHU$2-}0Xu*GanRxeGP zczm1H9l@{CbHok!=orgs!4uUT!_U!icpJXmgW68QBju!U_uTgo-qG{IV|adtwJU?W zc1_^@w^+Mo@c4Gi$>FYDbNIqN&khfj6IP1L%U$2D!av=%YJS_U7 z_%1uHUc$4#+jceB5aU@nCHUwX%c;Qg`z$Af@1~q8Jb1U|)alBJ;Hlor(V(}qAA~3O zT0JrRBKLj|cygfSbm7^;auWC{YF7_lyVlw@fQQOS;U_9*2=7cSXAF<-v3fH2Mar4L z*PGe?eFo3&wwxS(wsPk1h1#`*2g(UHDvlrbeOU>fzQX#s0+0S?^@Q+8b$@sjK6<;x z4?MZcaw7PVZv4Q@O^qLTey8OmaQA+a9(?g^%Nf9ve^^cmckd?|!h`2q&KMpkCxg59 zlT6@=o@1TC1Lfp!_kNN&e7dRCvxH|4Tf2g17ni$xKS>E5t!6nDc%qyT?%pd>h3BfL z4v&-*!QFd28t{eQ-_nAI%8B9bJsxd%XKia&7oIOQe&FtX7d?2eq2&zVsd7@dd(XfS z{`;MEe=j^zP6l`H8JNI#+{JQc@cbcbR}Od2_0QqyGcKV3O(_*6Mvc=mwC4}8`0`0%oO z9v|LUP6}W3JU%>lqP1%bkCl_bS3QppU+DSw89Y=@4qx>=KD@8@11#bB{nkIhbBp6= z)${o9%sr0}PnFYxKjnvZ-q(S<>y}fvyG~o(xLA)nKC8jqb%bz};`HnNev1{C7lJDM z+%4>T+d4eG*``n6?mdb<_`5E$^?Cq*RPRen;qE<(L-;^BWB3NDKZ8F{K7sFhiMBKF ztu=iP-%UP;yPPHb4VpfD-v7IwBlxQRgsP7{fm(&*AR5hB@5bf4G3V`;oGj z6#Lu(eIYWrLM44!Rn^ULAK z={=cq_~xo}2~U(0Y*(!Rc-2{gPrqg3wgQio6TcW#PEH{C><<)~{#?Av~*EyMi5x{kEz<;TsK8vJBU--Ns6+k&6Ei`ChM zyXBa`T|GJ6E#Eo(diBE+9&M$5*s<6TZuyqrpH-a|c%YmR?v`&A9-d*#w+_#?v^pZV zTfPnWv05Kn@I*NY+%4Z8{7YJ1L%3VMBl!C?{S@w&?+m`x%We5C;coc`^z`vn-X7Ers z!Apz%=Ju!Mf8*i5@!G%f2;S5Fr2+rd)^?q(1&_6ziQ)h4_OtLL(D!lhKsjT0qMRvw zq~9Iu?Nsa^xBu?L-EqJG?v6iNI~Q}#(0n^^H{a?m#p&IAYj8K;I^4}Shks4;ox_KZ z*>U(19%?%fysTKy<;p3+&-j<+RN$HRJ1zL+I@|C!{;$>9g-6@i-;d!xQJquxqjy!MrfI&=@;;KX!<2Q*7U)i#s2SV`Vu^mhw%Anw!T#1 zTimZzv0i8(}3SIwE4zxcOP0C?(RcN;Q!jju9x=UpWfB-Q}~AK z+Hx7fQ;qKt+>P%HzSfH@X9B-zJ3Ee@!Qp5=Pu{)@QW{5x&2?lA9~R0FTcLn|0BKMuLF12=f`k&A9x1uD`x^v&)jKcJ-s&+ z=j-Ymz;9KZBluML<-LkIuFe+R)fvP42UtI};hE|jHjDWceV)Rfrq4@wS)aqbi#hAb z8}LZpf$t(8z-#ghUX{<`o64(iEY{PMH{lI=4<5@$@RocIKUQAer#Rn^ybgc6JchUB zJ^11B2|ST6;9Yrj-(o%MYCqE6ujn_s=l9{6aufKqdQW>FK37f(&(&W;`1Q&e!~dq7 z2|Q8G6n?L!&*2Zq7w}LyOSsD^?O*Jlhm})-M;d=2+~w5Zk18jEr&?YO_#f0iZFsD@ zyYNuUJB6>lgZ8KJA8lg$aFQ3Ay0?1O6^~6MmxR z+k#)H?L!QIuX5V(56e67PsqFQGvx{V9C;7^MR^~7fqVeJOrFB8k`Lim%SZ6v$j9(M z$}{+F@(KJt`4s*y`3!!DmP-!r%IEMkj~C&THVG)qMN# zugX(+-@VrwzCpu|pELM3lrw?f@rYd?&fz=gy?=A~;jgv);K1T|_>S^R@SkmI(}(Z_ z^d7t_{B%tp!C$B88}Pj}eF8u4emjos!QFkoefVY%*z^PVN$z|T{^%h#-iGj_^j@_Q z{DWcZtY#M&Uupu3(vh_9xOfgniT~1Kc3^o2PMx}tn==l zvghuZz!m?k;<G=kp zw>*EU=dtHc^Stf((>?Ea{tVB%p4%I??LQ`-SG?(ap4%G>?LYQCwHBD zbGt%l9(lg4m(%ckJI|Y*Z|`}_bKPmY^1sHO@90h6_S}xN>_2uqf2lWp*K@m~X#X+s zd}nX^p69!G-uL`vo)0{Kx#y|pyLvwKJo0?x`71mhd;Ut#GtYPPeB$}5JfC{LyXP~{ zU+sDB`D;9%d*1MT;rVMlUwXcW=fQ)IkN-VAFM0lY&&!^_!SjmedwCvu-t@fc`QDz_ zJb$C-b5?Rm-b_jq3R{8-N`o*(CV==t%US3N($^P1;f&+DGQ z*Yn8p_j%s%{QaIcJ^z5`EzdvbdF*-OdE4_3d*1QHpJpYpC z4bQ*qdDHW+c;52-tDeW6pYM6w^P%S*&%f?@*YgWJPdvZS^PcAydEWQ@V$TPjf5Y?C z^KW`S^nB#`$n$S`KKA?)&oj?2^?c&_Wu8wxzufbg=il}`_xwAa&pjV|zVQ6Jo-aNB zp69{CkB|TV@x0{u6`q$p|AFTf&#&}6^gQ#t>iJck*F68B=XKA2>iN*~ zYds%%{u|H7p8wYK%=7C!pLl+~=Tpyr=lRU@-+P{WKJ$F;`5!!Acz%QDOV4lgJov}s zUq=i+dOZ1 zKKDHK{C3aVp5Nhl$Md^9?|S|>&lAt@_Ppo$J)ZYHUwA(7{9eyf&+qem==tA0A9;Si z=VQ+w@I3SULC+_iKjit;^QGrA&mZT^Cx&-^?Y^DYo4#+dEN81JdZqI+w+F!CC{6luj6^k^Cx;9 zd%mvcZO@&N zyyAK0`LjHqc)p?MQ_nZ@eCGMHJR;^Nl@UdLDWn{Oj@YzlrB1&!6Xc z+4JXnUh#ZW&qL2Q^StW$3p}rRzPaah&#RtCo^RoK!}BdYZ+gC!=Pl2-_B{6dg`T%P z-^TNf=P&ZS>v_%d#Pb(>-t+t=p7%Z9*7Jeq+j*XPzP;x|&v)>A_k35+=blHNFFb#R=Z|lZ|MUO;(*ytMf&cWte|q3QJ@B6% z_)ib~rw9Ji1OMrP|MbBBKYO5Y(p{y-C)PaiIa>#fk7ma{;jwAsq#u_a{|4Z(?Js%i z*1=<&UG{(fyZ*+}|NXxepW7=Mb9?^S|NXPs1uH(UI1{>ky7KdvR(yWKiqF}~&*!fA zy!wjIqm`e}T=9906`#|UpHE%!dCe7{`zt?xWX0#TR(wuYetzGI&ug#v+*$eg-77wq zR(y_Eetz4E&+DxC++6wjEh|1hamD9o<>!M|d|r3O=i188Z(Q+ty%nFsm7n)m@%c$B zK9^U1e&veKPhRmkSowLU6`$8%@psS&Jyh zB8%{`2qHI>O~97D|NEZIwAIh=_j&B(O-@ddljP(iIk^zmCEhByHE})SLcvXm>k}^( zT#vW`@dClMh+~Q82(CihkT_THrFy`Ph$jd>OWc?^OYm{xCd8S74-+>fP7}PBxEXPR z;O)fCiQ@!sB5pzK5WI%CC9zF#0r7RjXRfmSUl7L;9}>KXxE1kM!S52cCN2~_pZI#> zrGn=YJBb$vewMfm@f^W3iEkjz75pUejl>fKKSq2LahBjQ#BGT)1&<(ZN1P^jFmZe0 z1i^iXZzhft+>1D#*de$ZaR*|X;1uGH#AmKZ{}c02OC1v2jyREctKinew-6T!Zc2PB z@lwI{h&vH45L}BmiFl6SD#W)D=L)`57x;GK34+fOcP7pfe4IF$I8*Rp;uPXE!F!2Q zi4z2GC+>=3+$_+Db0-~!^|#Ahx_{}X2s9}>KX_&(yTg5M<`L0l+!KJiH6 zrGn=Y-%q?i@Uz4Z5YG`jlXw(yuHYw$A0(b2_%UKGKq^b{7~+SBGX;+z9z&cacrfw9 z#0i4?5RWB}6Woh<9I-=iH{wT#ZGuyX#}l8qApK7~f%uT%cEpboZx!5{_%Y%_!A*%L z5-$~8k2srnf#6!iImB}WS0SE6oGbW}9r$tL34+fOPbSV1e4O|R;!MGZiJv4+6TFvr z3UPwq?Zi`w;{u}yFR@l(WSN~QmabBPZLUPL^Dc&p%diDwcQ3Z75w zCtfOeE^!|50>RG`=M&EnJd^lo;#|Q`63-%@AowxjXNa=|k0CaRGX;+zewH{*@L=Kq zaf0AJ#Lp4O3GPK4Bz6eyMm(F?COC!odEzrA(*MMBhz|*FNBjcuR>7@_=MonRZc6+j z@lwI{h+iUJAh;Is%fxd8S0R3dI9KqcI>4_IPY`^Tcph<<;N!%v5oZcMOgx`BP4Hgg z*NGDZZzq0(I8N{;;x~yMg4YngMQjsXK>RlGnZKm}i5Czb61<4`9pbHm-z9#RxKQwX z;`fM`3Z6^+KJfy<&k}z?JV)?M;)TSyf}bS*ka&XN$A~{7&JsL^_+#Qs!6S$l5vK_r zO#BIPg5W;HpAyFj?nV3=u|se-;>E-^!70Q`h|ipt{wH2ad`NIR;?Ie<3T{pO1#zL^ zro>+oFBM#m_$%TCf@={kBc3C;3h~#(xq>gn0DnU~LGW4P<-}Qnj}xyT&J=u@xPUlK z@Lu9?i4z2GC;pB&PVgq;mBbFgYlv47+XNR7e@}eoob*3&A@L!>i-=bfZx#G5@fzYn z!SjjN5-$}zmv|lV0>RG`uP2@(cqZ`%;#|Q`5*HCq5d0YNM&c~NV~Bqs&J;X?coT7& z;K9T{5+?}mL;MqQoZw!>n~5EQyAf|8wh2xl{+am9S?Pb`t;B}}w}`-l?+Zzui_ah%{y#QTXIg4YlqAhroEAU;TZ=1=K=;zPuT1TP}~jd-izcZm-Z z7Yd$Fe1v$Z;JL)V6E6__Eb&p|If7>r|3RE9_(|ep#1jNRMr;vh2_8eNh%*I`AU;l< zCU`J$F>!+6KEx-8;{^92K1u8l+>Q7Yu}yFa@oC~SXQcm$&k!FH+>ZE9;;n*P6Q3n6 z6x@{f9Pv`Y^@z_CFA!Xd_%Gr)f~ydh5a$ZMgf9h+E+w8I_$=`S;w-_(i7ygo3O-DH zi8xL0UgFEd34*s1Um=bYyovZKu|x10;%me3I)$6rVEN%DtIn2T~E{k!Os%&$)lPhcqTDjP*kqqCyD8Lq9zD_jF>JbDogMf zV!E2BOu-|F>0+YN1P>;rYl%t_+=rM?DOH@{Uc_`UQ4Yb~i0N9QY=TpW=~AN3oRa=0 zrYng$B)Akg|@FjeK zY&2a()C9q2iRmJuvIHL|rfZ1G6nvPNE+HyS@LpoNf~W+++llD{qT&Q^BBtwyatK~S zOqUO36I?({R}XdOr1U>AT|Cqw!HbCL+M%`zX8EJ$l(h-}vOUD%U$&RHTj=-rKYua) znNB?^bn=VwC;u5iSFKY`ZPq&2rXI5hXIA#S*_*4|ay#4XdHDz!!KD8bHn$mOk#z|B zSHoN{G&FdOpx4^9Ew3q86*rD4RDJ zIsIf)5UnCVRft~PuA8$X~DfzC!SWySxs3$?;4aUA{-fDCEt3;@kMJTsB8U$XcC?P!%XP9df3$5#(c7TB^ zD!GFVl}ffR(7DX$6RO8J~0BWh;Ugx^GqKwe5Xk>TV_Te+L>6Wi$zHk#A*z;d+t?{VB)_} zD|>LD%KG0~|0j(QYjwlnVgT4|f>yJj@4(rtMvN8A_u$_`lTnMB=)oCi2Z^<+jcE&=a9=SvnyTP> z4E6BX9fve)Bd017tV#(}G3u=)p@hgv34I+%cm)Y7Qdm>-w2Z=xdZAHhT}GEzBw^ew zvZUWG*AG+t3JMlSH?wd(jy3>S?(bD~iX@mc=nc&O>X`p)IVK`ESFezeo~fO!ZD7*3vbtI{`*U3P9I~Jwxv4@67OGLVhs);wAsw)(+ZCk|K zRt`r)IjZtXph1j4Oaj;zBq1_f`;HAE;XqC6DNKE}4Mg6c>vZ!_PB*n^-nWu9=`XaS zpeY3xbPb_k0(FFmmCmfMkSG}W6|(v}+j442$tqW}wkSYWQwg<*{B~p)0wa)WZQ1R-fi`#k zqc}fgCB=CHU7S`d%R2@Ip{uwbA+%@~<@rn35rF(|fwS6mUW>kSAat~SLRjH1XN zpSqQtY%2C=$Yj;W5jJ&n3v_`wJkD#n-a#se7KVw=Ajx`-M1=^gusP5BFJ`5j0*c)uI z<>n4p^ir)+ch`j4%8s@+e-wolsf1su+rDQPSkZkPp-?@mtW4DC0+tKaI0NSNDy;;H zJmv;#25bS3m?zmau;2Cg*GFI;1g77LAPotg0VI|rmRZkmT?7N#^O)+c6xb7pSZ|F- z!uNGp?8|?UWG(b@ww()&tmyXq@hF<4r>u10aoV~9Wg_hJ=Aq6IL3Q+r*F2|l=mpB^ zxtvzfdi5emboNM{eK5P%Jr2_C{lINyt`6#AJvv}sL!l7 zbohIQ=jiZ@4Cm?ac+yYS;aeFVtHVz*oQ1H+Gao(b4JM6xH3{N@Tne}ZXJGX#gr1jP z$jC2Cx8GIfPA*EfdoN^|cc{CO2aT`&ti(+1o&K2!A}< zPNbRG36OkRhxj~0KI8DmmtSJfpHGWwF-S0TPfQ6-?-w{c{OcN58co|ih`>Cb9_KZa zR)7rDtHRVHLG3Qk)4PXE?~JyGy%AeIW{)Tp*eR@EC@0>HAzP^a+67fBI1yMy!4nS~bZ?w_1^GRvrUi|Ps^*XW{!Ku#1hB+1+i|W4t1`smL_`xLg zXTT8MiL%_Q+K|4}J_tP+d)rMNkS*!^y3K5fCq01_%Xeo@oB9lNR_^=I!I;Af2>!sJ z3BXFOmVG<>zrjIiPrt)WZDzVN+Ug0#g8fUhx{MlHHGkI@bPa1sI9pDA%x?^fBUYC~ zI`Me%Z~6OjzEGbsM+21Q-&m%Sz;_kB`60NghajwbTER9)LUSZ|gKcKrDAnzZ>dLD4 zcCVU}d=*TPB@OY`6E&*a?in&nO?(eSF6dhSTT|O0wGgT@al5(~VzEAaPZw782>b4g zXSEgCD>yVV!U*2{2{fIz)ivYcU5&jF$BjU@C@RETz2=YVgJY0=uwMs~Eeewz09mmS zm;$mgd*1V4n34E{`UAg)xkG*aJAR7_c?qs&D}>pSd<2RjH%-1(40|gEW59q6;X{Ex z>LAHNS>7wI2*1j3(DgiR!FknLZ_8(1X4QKK`&EnR9dy3yYjwo)@MTn})Enp><;(ve z`&Pp|r=m`y-k`&#?9SlOxQM%g&iA~nju{W{2I<*w6H`c97o>)HS=BsQ-F9bi>dg^8 z^8&~}^tC$Vd3X=V&l!R4l!!fV1?mIK!Eu5)cBtL_4aV9y1p-wJGlj-Q6essp@(j9+ zgXqkIk?37_Rq5Sf?AE3N~2E7{?rVaeQ2}WRIW1JO9Mjz=GA|sf*z^OD}haX^g zwhl*dTFunq-58SABpqJL@E9GAA^p8N{2PZ!A01xCa1R|GLHZOO9>@3&I_ze+wGPKH z+)#%blD?J>?_|$ht0wjQ4MWa4tHUod{tq4gjPZMQ_-Tf>=k82(y^dyr<4 z4j;86{w*C|!SGxi4l?|-4qu_Hp48!;3_qg7pE5i`hw~U7pu>Y1?xn*m$mb3nzJc+# z=7!5hf5BCW`hp@ z%y5AYw;|199e$JX@9OX*hF{U)U%6#^HblelbRF(Qn#U0K1@6i6rM_4Qg=f$ErdAD{ zFg4}j%;3?!)V^uV|H$})kBG6=G5aIUUVaw^_3FJ9h zMx~-sQ53X`+HD8(z}E|ynmXVndwv3HZUjD&bJJQ^NaTgm2*wO;-66Dx4JKLWM1$uM z=NjM_>G_3Eye@>+18+s4cwL+%RVuRx>^#sJR6|e`I0+D4^jvfeo2RI!*yh|<=hUur zxZ|1ezEr*JAk;b4S{W3=b{Z%Lt(pnQsO{1{G&|KJwPBHB8;|ogdww3+`Y_qO+|Bn* zP}wXdFey}eY1sf>x(y6rO$A%gVD5*7ESU5Tn#aSluGZsuFvp*OUiB1Zgz5+O_WV(d z!T}X{^E_i9pmc~Q@I;o8`syA?)1DVqQ~HfScSvbesNXz+hd{RMkdT!j)-ab6XtGm` z2*&;xCu>kY3{V3LX7h9`n5UrQjXc|BmyTP|d|1e)waRv|P93NB(0gu;sv_V_zM0{5dPzCaI1pw!Ln zRzInk9yU8j6&S!YsAf;#4JRuAYt5ct2AQbuO282_A;z2fCeLT>dFhwA)V$#&Qnv$m z19x)gCZ|Q6I0{1*NC#_gAVX_#6ng-yLXwwe^F^FJkF7+r33PkjmiG8L2h?{6GfRx_ ztZ!ImHq3^l3>X1T^lGIH0dfnxwSdw|?c3O%_exbXqE#afFPZljkYdk!p$1|vG-hX> zQV-Q-ss~!(H9m5)=P#{SA<@wnP-&UrnQ`{KN2wf&kj(mcQ)k&opLZX*tpm5vSm8N7 zmP5wQ0Id(bjG52d1$$m!#%^TE!T6OeWz>}5_y>mxJV*O-2FCC*o-oh&?HZ<$$9zs^ zlo9xr@|bZf!WL&UYF-S-tAl^B`ygZg+Rhx{nF$8Y4($1_o~KQXKZgIe(_w0_7MRF&1L~H(`i#hJ8QcH5w4Yr#c(gt5Hb(WS&&K2Q z+6azy#`yx>8q4X(`)JuJ9uspTSK}3};_P`{9Bh8%*DbkPJ2%5c4GVE!KD{VnfPcfy6Fk?0kt8^}|}(^Az|;WLIkX$lRL56-B>B_=+u3M?@(vNDj zsQVeZO!D@_oN~w;7>?TfY_!_#HHX07t!f0IKGLF$hl?5;5oZkl<7xoODf5(@&VAwyOmx`mc6V@-`FzWk;PE!=6m-4$ z3uM*lawJ>-9^w(TwI*8K3Q-8Z!Eh|YMQIK&1jl36te-Hg#EQ=^%!&QmfmR!w?wHq; z4Ju?k!tF7ZGqkI3V!KB#m-SRXZE0L`3^L)_oYaiTaLX#r(PLtFM1<3 z8a(ytb%W5&A>ECj;~GD^t;FrE3JKIloaJ~0DlAK4BY$s#{tFD?I<2C(GTo+C*bZ;*ff|w{|7c&}0Sqx13H_YVbi4)V z;|+|19bPagc{gt_c-zw*by1%&3l_@k z!BR-}@Whkqz%7uNdD<`!Si_4NOD!6q7AglL(TX`$9g1r59#7y?XC}2F-I5 z!KB9cM^lYpi#3fPcz;z`dHdA{q@p&v85K-=CV~T^t92%&W@tD(f-)=H688$m=90Dr zDv*fs)Pr`KfRQsblXsxQct+9L`WS4~!>Llj>8)t*)1{b>GD2!qtY}zt4V`laa&pN{ zJZU|`tO=4e8>5PIG9$6j{55gA9C8H#Hoziv{;DO{KIO-a(Q7Q z8F}90Ul-BMKlQ^awwyoJ7wP2OX5hm(BM#gQ#XZd6X(uuY2c1a3QPcT~s!cSTum--?ZgxmMjQ8M+`tS;tB3 zWpEk6K~WwPKNV;mJcAv<4)!ZCgnkcCGO+sd#p2>GM62Q71c1I zg!N`e^s%yahshGW>sp3swU2fdOcTxi2aTCA2O5?4dHLnG)@tivU3&vVIBXO5$!_Kr z@CTc+|7LSN*vJ*f7B!njIVVG21>5~`Ap8}zmnEt-b{R83YQ*uvRB#Ps-f z4ysg~O5S;UO$ZrT{;Vv72biTl#~}~J%-x)%yWQ-Le^REBvOOq6Ny942jboAnN&0dY zW&$TrW&$k+)dVfFvT?Mo9`K+TtsQfFvBYvC^-)0U*z+)7bn<<`mA4&t1Fza1C8o;C zpD-Vq;A$qZP}K)VAC5z*thjU_%7&;g>0FYYiWbsW$RcD(?W1zp#rS=yzE5V^QzUJ8 z4vSwFtD$d94#{=dpI!(&6MwwsPBk(Uz!74qAI;w?9M;0o!ZYT5NX!JIhSH7|^= zZVN=V8gwh9H_oPN4S{SPLw_RA_f$6p?5^tvN~N+Xtm$iM2u0e#ym)@4m1^B^bX+r( zrV0>3b{C7>@~1T6H7pO-8id2VN0RrwNH)%s?#uPIY#M9a!R)>&;R24%giF`5Q9q8Rs(c zO0w%CVuOAD8n-=TM;?qadUt})+Lkwx&#xmK*jEmwM0vmt8GB8K$HY8h}xE}5tEHTQyi@%@5s(H{2L;SK(perMyQKUjiN<4&gMTFU|R0uN=wJg1lHRyYjC;6A07p+Sp#FSXN--$|! zSUX!gL7{eGP~%46A}(qY3*ld+j@~aJ0~@gF!g#GGkm-<+Vb)!1b$O0PYavF&`u?y* zVeRFge@Ny)!WdR@A|!Xv8|aZx>0Z!Xc8J?cPF30&7aryam=1^oje!a3TVQQO6@!Jf zH^5mKIV!|H@5XT$84>uSt5Xy?_Rf$MeFq}f7DWyUJ#H(T8Hkfi{$%Qu;`<#JEc0j+^GgaHT4lt7j?f0 zBFEgKYsyZoID{7=Q%vIo0MdL|^;^L}8CV9b&jDLYHb zPPS|)T9&+4bbsoe6SAGo`mg|F;2}~DlG-65v^PK$E6^A*YCFpms}2W`VjxqK$x^>G zwCD;4oFQ5)o;JX%azkMrBp<9$TM@si1BHim{ISgt7B?JQCbF^;nbs4UCv>A0Q%RSGa_Ax!KX-@9_K+EVx_@tpI(Y zoscj(F!}E#zX4pih(8YQU8>;1DUUyfP;r|9kSMz?HZ?0$5oL?IpRq zvcFFHM`foVqOzE}2tkFIF0)T7EU^9TpGxX^C6j}$YnNrkT{0x>ywZO?H4`?KoKdRL zG?w}xC%tUv)nVz|@rq^2C*!M@}7qwOpUARcHP3OR{VGFafH=&TyCQ;;6UhzYAX8z(}rBoJKf` zP%()5+vfO+aFe}<%(JINBqQ5kF*#w$!jiTYBfK?KjoPLYM=|kdCPFc_U9GRgtGNT! zoV;T{j)@Zush(ehY{gq?!egUTE0=sDZ)?`fwsu4M?Pa#*qv#u9%wXeU*1_n1D0x-5zVj{WCF9XXbI`UatlM zfc9Dqq22AQpuL{7osk@JoJtEhPF)noskA6nT~uS_&#^b!)}r1o9=X}J)*F1^nSf(6 zm_8$`BvIzLg^Pq^AKjXuQLWE5g_QIQh=u8}imnhVBACT2^R+$0Q#OZZ#sx-u?L9X@ z-+Ik8Jc*35A|OYaXy~j{?hVzta$sJKNP@rRv*<+gu$T?dPl|D0aW%j|c0kr%Z}8Cw zHJOWP&{ce>lg%2190(s|c(4xdVE8VCb6rIU;^eV6s%XquXT;g$TvfX@t;Hpqfy%kf zHSQUQZD$agamkizv*pbH$YamHOrhWiI`25v`Qlj+?UBdec!}*M{IzCrthMNNpuMdi z8S)5wJ0?Two)|h%^B|IOM2r0bOOv;5jGfn;X0j*01BW1zaFYDW}?irV2_?^%r$MbEJX1~ z9}TrWZzUqa{C6}K{#nd<`A6aZi;nU!>cTTv4>!stnytThnypVdiyx~V#Omd_whRQE z{Gsir-2l{%jV}!~{%deRbYC##sQVDmdJI)$&0d6s?By^*N=O0&kuDPWI9$U-U7hJH z^B!Hpv$aJ64>M|j=6|k{v||vXy0eTrC<78`&T`tboM7aU3G|`n>jM)JVgB26&X&x1 zk#i!LG*|Pl&8QNL0<|6U*em^Cda9GoX>A6rOeV)i;x*(yed2d!NjMTY+%yo zV1lT-bWvY2>Me5SsARy5|;1jG0tBmwe?3EGFPfO-Qh4acaT{iyaxTnTT; zYd~FM%l%GtO2?erLbZ@%o3%f{MNZU&DYzZLBbhzq+@mJ2~4C>?!#Li+4sT9MRLZ1odMcD5_crd9LUS7N*nAI9*nDF;gNX3;rSb3e3adJv+;@~clN2?Bw z&dI9w9NLdhMH#8CkA7*7n%<2w*IdjGBYAhul#*E=AsA=R8}KgMJ!>P7kFs+<4+&i# zWQ}6>T#GOF!TGcW6-Baj{%78BPaf~ZaGyhNIFI2iuqWBVMfy9)hfAY8Cz_<@5cy9< zs4B132sxcz3$R%+z5jwQaRe$DZUnAdf0kV^>#u6Qz^aQY4NeJFV;Co}KyJuJ0AI^G_Rd?+_Us3f|QuVG@vQ*vvOvm>i_q7r9w6s5$25pSv;*||hqm+3f zGMp}k4D26Jai3fejB6aBlE6B2SisHv1lmu5@1*r%gL^py=f#y8B?Fr~r@8+-=W_@{ z0oHsOB56XwQX~^!l{Oi;H%_qGpFfV0)s^SSu(N@yKkjs!qGyD*3ljfkm``QN?0QeY zgVkJjKlX~!@u4Abca)lsuJi=lqzDZZ2p7YeqoUG4=>sw;=NDZk^*QPRqsn@ylOBtG z-;hq*dqu|LaCq|ZaoFYSYQhIz-GM~NZ&-cE@6sy><1!S>Q8Z^vAG$n6s{k5;;hu*d zH+)qZzfX28QqNWN&{MG0`F!Bj%`&*WiQ92t>w`kKzquwjwqBXre=TxSR}@oH55;_n z{xh^kH#f*7faF^!(cw!(*!@xJ7>XntRZq%{@7cDQt@p_6Wj0^LDRCsZAKlVg8ETss-A*y(<@kV#4ZiN;~ZEZn=&WE&`Pt}~a= z!zNC7lX~EVW?~_CPhG9U!*Id;I4Cd$CuqWNPC&0&dvUV8xjLthYt%ACgfOnH#u94L7*sLF{Sk0M6UxxFn~p zhu3muHQcEj!>kBYbMl@$2QG^}bgcM;AsRk`_l1r*K3l|DDCd0KCsW6MES2JAJFe5@ z0Ep@)bRshi^p#IKm|-$9=o-|nbXbFnMJV0VWiK8=3-B~kmYc6XhQz)26V?JRe8dTK z+FHIW)ZW+FUUY#6FVWBy4nuyY;a=kbWa8a_7qss*%nWYvaB`p}k~7Z09RbatmZiL| zx99IbDZaq)IOSq{$~#q}+A%8VTJ{CH!uu<>;1y>`K~!kfINep>eu?y7aVf%9&c$Gn z%qJ1>_@~i_WiI%r4E(V^!WoO2i6Z~yxH9N^neRQ1?BS7LM$`~gAC`w!?ZAVO-)qgk0D^K8PPr;11mSW+(D~ z`SN!>(aetX_%}2z{zD&?xBL{GiXxp@C99o_sipkD99C@R@IB&NsemR+*vWKRk?K4Jv!-gR`Tp_sqg~~|DnTwvXH$xyo}*3 zIy{5nH3(zkOeG7s+{@!9DWDEVuM{5Hq{wlO9Ol5G1era-{>a=PnQ`}X0X2YisSTgLvkRiEv0f3>J=nZns!wABn`Tr!3#rc#V!gi z^8ON8MOwnG&Wmev7%QEvrA=_29;oldVWUHx_>ol@&P4=PK^Y7Hl*5mk(_wfYypPi{|`(MD7sICw6hgY(qEZG zMzO1O>W7k=V%eGcxugztHm=JEEgKy3l+K=p0YIB+Yhz@eD)s1& z)mUsxHCF1;rk^IgUP{7)$oEN`i8RP<=Mulu1{F=rWfFX>L0gD@|oD?R?QGJF1?$hN8@l@U%xn!;8UZOr#T-_yLe9t6Aa zhXH!?AcMAC0Rzw_B{TTK5+>uvh{)gx*8-;aT{fIu*z9@lqZ`z>x-ZmnxrKL4f*A%W zUlGr#u2xYaG;ZPscJQ4P1efpN_w0{&uckZ$N_md`21yN&V$3AywAFa~$>B1CN%2gg zDc;W`2bwK}>k#1zo3Rcd7YbNAVYel95nY6$YLcDFWv&t4+tTwIZmU-YKtt_eAMPMk z;9+p$@MLfGhaeFye%T8jrBF0pmJiU;%FdyQ$1+T5EExvUcjj3bI5IX=>}u@HBNu1r z?okP=q<5<>M(-Z|7QOojBe-J1%_n^B4-MD^<~J&NCqzO|4^(niL$o{%)cy;#?RkSy zuB!1Z{(OoMr)0Rw?&HYUy+F zp(wzoeci-~%PUy>a5l#D5AAH|OCdWOejDIXOfnx#3zAJXWl!ZOH^7F0<1x-LT>35t_3HAL83ha}&~WirzM4 zV=N(wwg%{bh9UPLDT1!o`D*8-y19TTs9u7!}242RA>*&~4Uxe?V9Cf?A~#d)Oytw0E+Yju%agwCN(^h#0?${yn*|nnb&M4*RkBSA{8>{fYnNS zBa)DNHGXu5YmH5#NE7s8ig~Dbq3kaz7=+iAw|*k;N5ZUOYlT^lLrxgv{b4`jhL!G- z-SA))3l_}yrG3TO<0LYd@;KK4&kWo%;2DLLF&8)T+PkE_;a^FIa;T2+TQDpq;SVbN z4RWE=#-1VLOmV_a`Ud-7D$;?P@k=1V1oH^V08R3eY>~TKoixd#EPIWV-81}t^t9?-$`T6%y|)aPC9vS08+?-4wnOx{yX*y?E2vl`V{XsW zT)+7w_yC1!hYDDwzazi7PR$}u+(u^C!wFzxZ}Jg5ggJq> zH^m=%|6sfn9}TZV;=+P)#(I7N0?2vW2?)o;h9sD@5KorqMpHmiOjpNQ*x9*&LyTIqi)=4{VGsL}a%E{1)@m zX4uou(I*^?VX}+4?od@vQOw;P>e5M8ds5@#|E!u!VjO1E;p*B^sNS7OTeV+rcs5an z7GfpWI;2S8)o|bo1fX}0fbXhhy!r~d;>c(jvLJrOX`EtTo@&oOaR%$|RA+OWefAB1 zQ6QhnyB1#kEnxXY`V|_2V4)AisQqv9n!g$5UfdyA%i+3+wIuJ@dDdK?9LZUeHmz_z z*BsY8R;zoXFg!{y_0p5Got^Eo6A;@gko^}X0i4J;L|CVna4(c|sQ4`-_>z+^epYDL z4J=thod#M^%obR??Px0^)Uze*o*L>tRNR2iWp=0km9YySf-Wh~csDcpx0K->7_NV)!Dn4i#U$7V4Xzz;hrn;)GX^)|D3myz2O;&W) z5La0VR#eX@sUNnxRfV4qAEK(FYWzv@d?kly@Hp-UD%B3fO9{CUFZGxad=h@Q({UeG z1%J=XY+f3SX~+(mwA2-4n4Y2SX|o0U@V8k ziN#+e3P~O-y#e<}yTX!8g9Ob@{?+j19pG@Ab+n)E|LBFcF-@!y<$0^gu!(;`#oI95 z6R_Rq1~2I~yz!(B>R%mU<#?&v%G=%p^;aFv@_>tF`a)%X0PC9C&v5J8Qni*5w3dcK zte0An zx(6L@1k!NC6`?+X0L1T2U5}w54fmK1ckWbY#P#<4J(#$mVc5`~_afTpzl5P!hyYrL zCiF%VFcbrJ@_J1!tNy*V79Zz+`AfLA{aFQnyNK-e|EtcQUPbd}mS=E==LnwSVmaGQ zvrx~IRjAUW>-iki8^r7|F@E)ce11IY;|<)wUb+V3+1%w{gP}DEL#tu&K6AGqbdlLm zrD6o*+_dj88M;H@FU2F%>{`^o{P%!Cn$d;yAvK8CwZ{zEa z?jYX+ayPnt(lA!g2=>rjj!VEnkRiA?WS?iL9npGRWqFbt*@JJ&<`3@MvEIO=Sd-w( zf=5l^T@l26zcb521@Bkr!4^^Qf#2emc{2$P?sbaa{$D~}d<8;{-x2PGpQ#O~fnG(x z7q~0R6MO;{gqwb#t{MvIx&4I>BjV=_v%2apog35X{0&^pqh4=jQ?BZ4@OPrB|Dh!H zAh6bK4;OX6D4ueMm!GPjy}`(&;u~x#Ig;;;xl}>R48jU5Y9qAIGH6T>Qr}jMLIw2# z&C^A76%3eU7$K(yYTo}jm`=gh$Vw`&%x)|6FPAUwDF<hI=9!6U;07KB)*M3HNRY7q7yXX|PSMt(v89s5~cY zp4U9fEBm192C}&xY}ApKOs20KPIYK!>1)db)#c_WTj16Vm{+(Xf!5=#9-aWoI~K2r zGl|qTGYhx?x5SqMm`3mSu3&~0H|VRPN}5Ypy`CIp1A|G!Xu&h0C@g39*W~~1h zX6%aFD6)T4rw{6CZ6oOmAocJxdPnDfp<7-Gx{pcs5LqVIp625*Q%n)?aN#{Xa$r3we!n5)r2%Pyi(y_Xm z7t>AL>Qt-6Ma=&6be)8iwvUWrJTky&a+htTvGRx{{-gPCvHIWs1pZUN|G$d&u=qEa zJe0*_CC9D>vIyP-{&*xMGXi(GqA*&yO>Bn}AT@Iv7H@-Ny?*gNw=C@5su=6_RrJ4& z;#z;961TCGl7ExI_vew^h{?MvB~#Rf))&^LACXwVMEPREK-zp2@16}n>BkzO1L*&1 zX17Q*a~YZBR4Sod8TDfYUSaY8DFZ9=(#Dh)z7iB1i2jDY2-Hwdqa1&c!yB>5OjR=g zsAjz8_3AkibHa5?MIaa%I3U`iIbPlJQ1{b_njsr66X{TMa1@QaUDMoDUDG@R8pD4z z0&_0fp8o;pR8E(2;#weE^@7nj50s)L_5fT!C0vM5gQj=G|Bt!4iX-caUpW`R5obsO}hN0yM?n55WC z9s@Z%`m%IcP0w=oalaGBceE2%UDii7YS6KmJo*{fL@2=UjU%o>Fs>GYL8UVQH33-r zlPEigeVlpeW^#9i8Ck50FLy|G(K`dty#?DiI zCL&;Xng_*S(L8W=F&-J?LrR;B((Tq0Z}b@=I~n%)yEtw3mkLYws5g3}G<;{1Dp8Itpaeq4FLx)ba@)DnbA7}Q z=p5gvuRF&e2P$KAJ&f;VJw8Z3tMC6$HaLY3pMI?Bt+|XKm-j%Cma`FN9!y@ZPd;$z-7B{n}?D-E4WsSn7Hm1e^Mg(JD z?g;q=ZZZ7R4ZP>0x^OW8EPo8*4Q?>TyH}=iG42m>ma8{4X&B-ptp`#p|B0I9k{sf7 zw)$Mvspp5V1Xq2X8jW)f+Mk_5sk@kps%75-caIySGOjT%VtzyvV16JW{46TxVCa%Y zzYV#Je1Cr`s}QqlW(^sAyZK4(u{-qW`;A;f-w%67!o#DmLqy|?fWlecXO?7SLHyWZ zn6Zbi+F9=}`30@)6{rlangTuXVu&YW2g8@yhd^_;e1^rcwa242|Gg|NCrT8aSd5|( z??JTH5#z)y&e?fGNe2IBaL2Qs4egNAgQ&`HA!;zo6^!3dmbN;}CR|w}%Ztp?5K&sQ zhF@ezVWUw0e{l$E&gh`4yJpab4E9qVGBWr@dtSexX*@z#r5so0DV~(LK0pquHR$PsLz@-<3V@sS#`goHg|KVm<~F9ItgiHR+h~d?%Jh@9|@XxdD#> z7sG$ri1^b;g~es}za%sBv^9^LWqv_ngVo|qtfs-FmJjj^>hQ;SjV7wN)z+8bG40xk zL%xyowe@=WLuAGjxK7jIzM}0TaPa%(4W0blE4cW(`92Ol3alk=8 z!miBr4g@-3PE*?|3dbti%8`|7_)kZuc$Niw)8Pw5&^m?RiiMBnMdV7fRAshonz9?2^8~N4dE^@4;3< zJ%}1WQB6fUlBR_|mv!<2lDteZFLJ@#4Wa+q?p8YI2V;}>@jRkMxA86X6HmIu`|u>! zZU3eo2A((Qt%H20Ms+j$#h}yATBl#vpCsObo^kWG)+{Z^#0Em#uQ5JM z#7{8EzcFZFE8>7-26rnsnm>-0h4t6(hkzfW+5(eV;kV2QM2fQCEmvmiRH<`ybx zECJ1sf}SjaEodq%KGJ`MSiPz18{dxHKd9!=4fu)04{XdILF&I+9bLL`?+p+N9z(|a z4hOk)}$kTz}ETH0k4T4(B3}0-E0h1v8@uB;eBxKU9W?Q?OQtOpKAj zv&dT9Mb>Y7ULPTn1^mfp^%v~Bk_I;Q9eClwkC`V*9$s)j@SjO()^u!lRbwXj*ToH4 zd(J+-#f^7&umxHq;_oQ?_{ja4KrzSoH})_7O=^!6d)wwt(8laQ45(sjAkK{031r58 zi@)}^G4J!IZsg~T!w9n1jordv(jG}}A+#+H0w@dOqCGWuBof?d+FZZy{pd*0jR^f~^(a&qu0 z&Nox;hE#_BHT(rzp(AYghXOV*xdWp0rvNhYw&N=RN7C}j_%Q%=-b`~7HH^N z;2O)et?!(kIxQ-@L~X;r9b3ug?UM(YN{309!{J3;xACPXDKmLW3B@kgl{epymf%AU z-NBQ;sr<#}Q;m8A0nMi31BPLralPPYfL-`D4E(|ii`s)`vz{+MT#;_TF^tOnI|?f3 z>bX_=^eR5O5c)jL1Lq*Hw~&efz8F6~?foMw!nqtU3br^LO^?&J>9nrg(n=bn^&z0p zgGhY~U4;=)e4W#AhM2(fnVYOqY(aQFb5lB2N1Xj2RZWB3tOH1p_D+H~hd1z&c8RBP z-I1R-vFB%jTgVq1ZKQ&|I0BTx7X23El-7KE4r}ws#EO>kk~Cy2@|v`Gxfh50;%G0s z`^UJ{{KNVDURiz)GN%>)KJUb%;T3CthTls!^jG0ZH`s8Om^~)+eM4>r@^;UF$gErn zd%v0^gJZvX2ESN?|JWh|z7C_7H&mETR@Jj0e-e2h$|9Y-7nhAlqU*=eKao0=sbJQf z%z}~ckSc)hg2BFl0VkAHsAeK)6F}=Vx8bSoO`wVM(N%E`@~Qo(5vQ*m1k_qX^S})I z3m4a(lBem5RxKMxSyQkw9lJ`q#h&*xBB6p4bhR#`Fggj|jXIG-Q*c;?FYu#It~~*$ z?;S?9)T@Fnb=>Z|jp7nSix?&g!xYOFV)%of`6h-;Y%YXi^?t!< z{}cxn)qxDACe3=kJrw0)0F{A1;Y##nCGJ8cFj%Lv4nbGx(}TgP)0#$D3=lzo=Gj4PHQM`QBFTn?opW z($sbD2@HE|t8oIuGhf96hW0r!nTnq$F88EXp9wDCeyc|WOLkGL3BZ-gK0EtwWiJz0 zUiRYx%gasytX%fx;NQz$DY&BS>4GcDP6Mt~b`^o;?Oy*JQ_IVqC9u5gUVxR$t}VEt z-J1hUuPA%A;EJ*{fh(0=Phff3JD>eq*{=#LFMBXx<+7Uzt|)uI`S-Hl5?oPs7I3Aq zTMH~N`zVG;rFMTPu)ORsfR)Q`FF5qoorANOHN1OQ4nS4=mlz&~#I*<=hCGVFX4UVB z!HP}Lu<%y})u$K->1G3`b)iQD{z0b({Jc|kQ{3P3n};osN7ch0`I?RDKsW^% z)LDAlB78Ky$SrTO4bvT^0&_t#PzG1zyyke77O3V#HC{uI|9Q-`I2^J}2UG_+qOoZX z?U?y*HB3Y$K>cutT1|Titq#VQ{vh-HS-E4y*28{M7L-V&>YDXONSokA`pf(7wtKu0 zg$BR8Kka1-C$ctpg0&;W+AxzYHbacrBTh9!Zgn98Mf1QR4piHB!i5(9g{y{#@UUa-u8(senJXm!E$KRzhq zLM$ad3SOa!2;e)+M1#6B@NJ`{9BFbr+>N2?y6o zaIkjqAKft{fBscU$>FzjVs&ypugaHwB)YD*j+()!cM%mDN_f-559Y|zfgns;>uIE| zlGQ8d+Asset=CBY!q-q0zw4P_SV6QfW(jS(K+KiEi4&fB^DA}`4}&$nY_onfRO}_P{%@83Ey&T9D{o^=cTJ}PmJi#E$;khG`UBi4 z^X6gJ&u^k%uYZd}DCzwo8A8*rb@XzJ!~NLC<77^mz5WIm4gVemehil3?{am|M|JN& zHBi(Gh(`;Kg1= z!fvzLJr@&#b7s<d2saXqI%a}WX5uF)|#`sNUjCog_dH@tw z=kENH0v<6Dv=4y@fWOU)jJ9XO^WupQ!T0ae?|e6iGU%$pM!vR?10no8QZ@M)Y-gb( z)q_jOGoDpT|B^OAnachIIzeJe5EDvl@D+mJO6<24v9l1=r2QDF)13%<%^elq)~Yo) zq9(iOac@UWtP^)Pl%i@PA(U$ma#`oysM3+o5IWA#rp3$`yJfxf_BRkaeWwpvNb2`F zr*6f+LKX`Qo9RVO`je2RT3>-wo7GOFSAM9_fcf!{5KKa0T+)5ZP?HVSJ1Rc{_|nn5-sB@6jI}iVC1ixvX zUzlcpX@m9b0m>e)4_DB90FBn^cyyqf}`E>s}k??p#aVkwa#%1J?>daaMTz8eT8LipGgALOgOFC-y9crc~ zYQDL+b5befE42v5@?Y*#Z6(q5B|ifj{XPW}eoKfccbM`Ey515~7XAm2H?(L+>u6V* zk!yAG+swQ5J?8b5AH2$E=cAPyB$V4c{92140`BiT1rk}@1WN45wbH`#a#)dUz5a(J zaYcX;H`bZyN$zi(+QAdVr`D`h&#|BI?KesBnbXjesc=C#u|cYN7(6Tk_3+Sbl&4^E zISu7<$iPV$I#F(i7>@(eY-c9E03a9GIZDu z54!zVaK-vECL@hM58V6=(+A~ti(yvx1s|-U#(W1Ej?1vm*{8dZ_JXfw!rD* zVb&!}MnmLyl|H<%O{?eLlH)ID1(*oVgwOAfjk zqYhS0mT+z5|Dv&z=IE!O*-IL1mY+fY{f&s5+0B|DSiY|$JYY1Z%sr^3PY zHO&5h3>`HYoA(TSTStZyM=yp`fgA6@@L%ypve?@U1JkTg{B7uaZpB~Vu+XKev=Dxl zJS-f=8odYhd{|D4`UUFRVB||T;R_8=SpDgl%&CcgwsjR}C(lP?PvP6&SPZzkZ*w~i z{@@3{3R1uaIVgd8*>?CX9+yY1{^85jR6)Vxf!Pc)LHqF1c}aOIl-h!`1G$xbm4Cmy;*5MjRbeA=_lwV z`*&Q2;D-lpSWnbvn>bu#CnDR-4#dG0w`smDg>S0W``_6{lkM6?V7qxUyC*^&!!K0v zv6WJ-Tr`mOqyyQN?;Gj`IDdrtS!+IDpq4jyA8PXeZbVwiDz}Y_o&Qd_$6r*%XW|Q2 zA8UC%L_ei3-jvnBkrC{MoBCbsXPna{ipHtVWuthB+rI|>6RXqla%bs8P>FO`{1%0E5={R`cle1^P=bYFsw!|LvY;@$XC3FZqP zNE~D-aEeG}KyF@p|MTYcx6r$9@s;p{PW9HrabYhCRRvvvdqW(*S|?w|*J6iLapD0# z-@RbJ*}Dq=C4m#!4csMpPM&P#X5Uzn&p=vsOKrImadkRm=eFF*z;dTBv=~B z#co(}DhI>V8_2?JTaR%Q`i6zv_pg!xER(?1Yzvl3luAcJ8!BFCB zA6`98@5PzR*BsV#aE5sETfmG{5%`0AZ!zC8oiDFCED?P?+#Vb%o)6K2I)yZq!c1U{ z@39q2_LURSkNxIH+OXjEL?JscG1ObQu7%pG%g;rG9$92pGlH40gW>5Mc*?=A)yCKg zFx>;a@!gLeJf!>+ZkIeaRM6QW$3yWxAHwbIaFoVMG(APJm05XHV(?z~JgX4-pEkG> z!N0eP$M9v=ov^VDdPS=*abn8580gcW>o$E?Gpsg@@`69XBiP~tWRrg`wXP;9({_6b zNXV#4IioSch+H;ml13mYo?G!fLJtV6x0CcI?zVtCNO#bzG>`LP7B3*aLgT0$qR}|@ z2}$G=1A9Ta8cDFg*VIDQ9zcrB8GHW3a|i~s@jBBUbAOXg1H7M;ytw#${+W$5?>#A7j(fB*KUGpw>M zt-emK%WPjUJ{WTqqnr1y;F{D1$yDF1uyD#r3%}aakzJ)v`{2NV=CqW)i~kB4k>}_| zz3yWvY`PB;%DGIo@r%Ea)W=15^2PltdoedAMyCg-m#HcsN)J37oe`W?rvAFbS%`mV zi@UmMH}D|7*w~L>=8w5k7c>YZ%uVN!LS zajZ_t;TY2y3y@kaweK&20spB8Ya;%A3QS$6E~vja7pGW3A?}XGKuU8#9G3ml2xA1r zX!ff@QtfIDMTWqgkl7PuY6R)~A`C$cKo~w1<09*0R;*WTDHrpEr}P)~I4Y`dyx>Px zeu#=zeZEXu)hpEIWXne0p&qLaei$GtHO(WT;bZOJgQmX=j_9xhSN=YM9{-EDCnk_w z1T8_bbv%)$p?Bi5IqNf607D8{Xc=8LG3tr0Bx3lz`DrRKsKWC z0ujDw&x9DMYbBLAs?&diC_wG6h*8@R<}ij!0(`5*pE`V%C?^^QR9ItmP;#jE@k7@H z@oa#Mgq-VNIjKXf4KWm=I8xxds`8MVL}qxoPt3lYit5)kQ64}^%agb@#eMH|CEiYkSwS@lCwxRWL#jA47} z3+e2>aBk*|OSU|_Du!)u(4+^3M5C92Q_55tAQrGC+U`9BS6_~g&Q?QYQM>gp9wRg{ z0ZsE=h$h)OyAw1oVjiJGp2H8c#Dkhi z>Yz25SWXDl-VQh!45Stx!IHZY$^}1P^JkyRhLddN9l-tKG+TbF(Tgw```349JO=nq zG=O$o3q)I;J8-gJm4ACAni1J3Ma3WlzIB<=JdXF#P$&GHHI~9}s$ylaK~*CC5BzW& zwxcy(k@LG4*N}nzYt~2Vp8!Qw;Vg=5<%jrqu%CZ2&Hi$cx%>k5TIw}86k9g7j8$rj zs8RMuvYm1Gd%x3(zf+x!@pr7VITw(qQT7q%@&DCP_OaLObthxy)WFP{9LIk%#4rcl z6#C~AMq>zk>g3)w9MM@G|9Z`=G;csZPW8}-!E25-_;E765eZXZmC`ALc)z?HDe3s2 z+fx3TgR=O&BCk1NG;VJuAPUw{+GPH!&b4bo7^Q}(@1*hQ7H90?>u4BU_ZepB!)SOv zj?Ldq)`?8yz+UhsK33yBHjd&S)OX`EqHAI@@@sZFrTklP8mf6n|3A9U1H8&&>Ej6y z3?-f*Mj{f88Z-zB`jrMe>6;e`t+WgdnPJc)@l?+W`GP}9oC-o!qGn71jVS4aYNqe~s zH4N-Di#vi6b!LpjkvObMi%c$&T0}yVV)+i9;3I$1*-mvE7C*dFZb^+WSpjE20&-ZcQHpSD%P- zwmV) z>f(R4Okex}g=1NLJ-C7g%TZ7XjcL`gP0!LE zpW7|jYS#jq$|^4Fv1z3}UZvVCw?aP}VnyH8DF5>e(p3jr(_YTE<)BaWuD|kVBsUz* zIGs;-H_4I&wrVGhR*na3hT0oHGSph5%BlG+wQ6E1q?KQe#K@WBww(n&l?!zgzydbw z$;W%M5?zgN1Vqqx$XF7)I)J}LVmEEG$KYU6#-1AUU92&JoVkqYNfB;(dZW=jfLlwO zFO4=6w8-%$M#G5wPLx#<{jef>WV(AA&p(Mv#62M;n+HNF8ZIDdH|(Ug?Emu6AB1!{ zuNBdu{*(8|Q*n+-Dwrjn+EFWT#l&dzwC~@W^A`Q1Xfuv?~XWECZ{%PhcZ02KBDP{z0|H4Dv$C3=gG;HC)_EIvlOSjD*| z+uP)|syrPSn?(bf~p0EX%qTaT9q3mOko#59nKl{uA%U-PP)F`CmVU>n13>W@UCHHkLEp zXXL}?k=1J4tb%+8{ptc+s<*{Qk>r3)?r&>7YEsq|HqZl%m#4Z@B zxhP#icKqm6cm4ikMyfhRm&j|g3cFm*cC0UBm5 zz-z-b!82&4t@wr-PQ>(_OF!rgPjX;AyIK9vKDhYY#zwz=y=jUjRW`BUO2auO$L6AD zWi0bb^9E=)CO^m_3Fg#?w<=`{8X)MoYK%c0=iXAMa8l%xD7HQZI_DwmP2uVkmKrnO&kx6IGgCfnY{a~yXeN1s2j(Hk+`GTlYUi*NM-+2CC(!g) z6Xb^QM#3X*CE+X*^1e4Ry+0|?hV0&~_ptV)9jmlW8Qu#FT000mQDW=zAK2EQ-@j5H|s+lr;+;hR5_RjkU=W2S9a+T;z5kq0h47 zRAsRU-5^M$0LWJCA6afAAyvsZN%j91)l!MIztDQz!-oUYk|`o){G_{uym{c8$Rn62 z*+-)`7u=%^u2H;OE|7-!hI54lc9~lH37ul;s7e`vgM@Xld!7WM(l9_O_oC7{(-V1X z9|DCk27acya)yR22OD(;vBn3Koym+zoFF_B$=-*Mk>H` zY3noMtZd;Lc4I8}Iq5pvG96@;jg)e0T%K1faD!x--&c4mN3rq_5*Of;r0#uoiptHQ z;os6594#2F3YOF|S#|^Do;$-=p|XIM#whjqlanny>Xc+7NWKJ=iuHs9T4^2j= zTE$LkuGsXmgt?B{(s;7GqFxgS%?rp<0`KgudA~h@1-4ZBF*1E~7WEz}r;x7^? zLv0#F4Aqt+NoZyY;e5A{gnntZ5fP&zru{9P7JxjsZDz##$fXu3-ycLCt+;o@4RkS5 zbVNEz^7;|^Ca6rlKw?`i?6K{M;^eJu{X32oLwWoPg>z72iGS9YM~}=7?w$i22`Ede z)t@~9WOBkXd?}U2=1a0fhO3Bf`RPeE%TjguMSOe)kjG_43OB$)09_ijbm1tsg1qlK z8K9!Kt9t@3=hutm5@C2Gc1ZBeYf>~kw+WEw-Z)Ol1!B?XKGdg-qpNtCt6NNJwSCTB z3l~#Qf^j{M=oA^UOVtPhHY%VOx`o8OxqZU1Y90GHc3(QT@YhgXd$UbVI6h5~+HUw9qRby&6X_b}i0a8CVwT)Y^`(mB&jn&|{52O|XpVaYVKoa-q1o&n6SqpPsDXB4lVbod&CjpV^M& zTXgYW=!)CGCc-qoh{9>w#&D?gr(jP6r35~rTzToLoW)gcbZ=`8o>cVM8OFmzIJ#J< z1b>_+Ot%Klsf%MhBdMFqQ-7(5p8zhP58C415FHt2BExnE{l#^mR>d~yJA8R&s~)o8AuU&uR~ za8J%KG4YD}3ix&*T6}OJp{!mOLx_rEb z$u$K2jx*?6*Bc%08rARfSUs^QR9>u{(+xM@gTcB;Ps1x5o zr}w=xYX|>ujr=qn5V8B5HJ!wJzx;4-V`4$)6=rIMJlGXEXC2AuD=iAG|3Qme{=|xZ z@ldFHlUzeWkpkC{%g$D6y3c$sTz3p3Hh&J53x1N<6iYI zbZJFwst<(|a?_g#fiq7-*=+nY+~c#LM;F}rMe7pe1{a#G{*~W841N%O(VLjd5ZZTC zXKy^&FiWU!4TdGYdt4%*p9g23ia3ictyJh)gegteM4A`LBW_;>qvEKe%$d?wJ!JF9 zTHEo$|G_e^tNd;^UrcSHm=6-)m3&9)4O7|&E}WLJmv*2byio1gl;?)?g9!;|7;59s zt312Z`?)qOfHtJJCr81XcUX~?37@dlHE(jNa9Hs5QmMp_=K_lKw-GXo2=WGj*YlQDI3pz3YkK%ur|H&0?aO!E+6cB@FN3L9Zsf$E*=j+TWxW5P`{Is?b0p z7{cpsw~FsiUP}T1?aVYRNme|hbV=lklgO)4irs7~Rvukb5&g{lx=hK&WnykT2nC0@ z>%K3^M_Z8cRVQ1X-~Ta~h=BZUmN)Rp`EL?z-P|d~)hQOBAS$LkiWt2NYUl(^hmY@w7d^>`ykI0%wS?s|@~VoW{&jt_x9sypCL{fEw7b%-sLvD&KYxjC-G?5|dGgL&kElLGrv`k~{kmmDwI{9{V(yj}SeL?lAC5#drd$hJc!Sxfc;?!wAnZu&ledQ4?De z78GuXlpIBoU~>iqc+;}r;X^v6lpI$w2^^X*`1B2?gw^iRyVY6RkNd|E7+Vj9%q!*D z>H`r5x59425!(*yflAi4dwwaz__@)_M%u$U0~jDe<& zwam~0Lfu7o`9b94TMnX3x7E0V8j~Ki>L)ph(;~51gMy54?Wuaf zOnVEBswlj+W&b-0s(2+meAs8=ke0OJDtC;Z46 zjymo+9vFPMa-I-bnPKf`8jWn77`2twCD`AH6NkM$q3cFM3{Q64)O`6#8%&qwPjL5X zb_L5}?&h>4L2B(XtvvcZ(895c1_jHHGDdpJy~YPSV0uWC%4b3T)=r7`TYLuvh~cpj zUM6Fc9qN^TZ?R1=;Kqx#p`i^9yMQw!Ub9$q3jszK!AI9X(p)WZ&y1P3?d2(nvY~$v=Li9&69t^!jp6tCz*_R@)=a>$JzS+AA_oc zsNug&nIA6lL|@#(%)`$)6_5NKoM#X|&aDuJ-JiVM`tll7pjK&HjPAM6&uAR5?3wzK za4UIPzoEK~w>7u^f1}}ktBvivKr(k7B~YhFco&?iuNv0C?=%5UNaCgCwjald#kt`o zva5D)JT3&YGCYD$T_pb-$<#6$u6)rWS!~H^L{Ad)$@Xx1z9=;z#eOnUzb{ z8*KNp0`?lJ9rP~f-LR{OQ$p}~FPlh?;MH9L5k+Dwk-5pFV9UoeOg!s@`LZrjv1{-X zs1f+h#cEre2ZjsD6sjAbNtM@pHDh(C?i<=mU6~n7s(`Y2tFGD63VS}nYj(7*JOSD3 z-oU2Mk3Z%i!30CzMr0oHLyDXk<~n6tp7v%`nn6QY-@+XKXEN*ArRr#zMir&Wa27W+79?- z(W+|Jauwn$tB%AbJNnrXu>o(olHXXHz&vB*qUF+WQGbg|8Qbz)N~nyapm5=dEY|OR zf~JV)EWa+nHm#58hPl{WY3P4=%I?11xFBt^qyjCc+zjQ49h2#X*o(};L>tKoB$>{4 zIF#sGSusUtPz=Ct$70GC1c&_>$5|2yy%2|Q_eyj>_!T`%`_s05Lx+)TW?fyIVEY37 z>CHs`8%!Q6S!&e4sq5P93>o=@!qc_hKwkpe-*C08ZcQS3S#z|%FU?0+!jj9QKQlTs z$j36s?C6DzR*Df-wP3LUcG^w8BTvcn1A`$$itv<%gdJKeE%QO-mDXp<*5;DD;<{xl zSPw3Vo{;R#3YM@t>5m^Hl1R=68|&BUzxrzwN39Vx{NWx$xeYROad%2&JTt0k*A-Y2 zxO|qAa=}*Y+rj@$=F9MlMp4Fe0tH17rwm^nZ$kB5xtdik_92UkPDpXgg(lG(|-K`T--sNbuJB}D; zwAI^~(at>wfjnOHRK(Load7W3QV5Gw^WG8qydm!-!eY1sUZ*b5D$C|y8J(HF);)E- zLA|&M(lJet9wJZh`q-umXJ$zgz`}IG$fgTt)G4`5f*TE>{Z5;rYxnvS!0_T+>X_n| z{J|G-v`}ISf1D7vUBOyBP^UZ2rcO<3p>@I(wsKY8T=Kt!@Jg+qjo`ZtwlJ!@TvuvB zpo)fRX`*PvyUMJ5yOIlq$2=m$JQE-_n-^D3^{cD<3`o^r*F!6K;cQKo9FM*whrXaT z`!Di(J4HwuLw5%QNm?R7U`Sf-eRzpqqLpuAiB`|2dzGIsl05=BM0M|c<2JYX$9x{1 zaYuR6K?cLc7PLuu@ayM@83MBn)wV$ggSB7pq&V-H7Ma3|?>*ooR@;v-oa7Xr^ zJ49S|jttpq3v8@ksO~!gD`IjfiZ*>*-{#@-J|c$Zsp0T5@fai5>ICD4sk}PB<@)}$ zP;GBBVTnR@<}RbTNrm+sQcmHNviz$T9r8ib)*f* z&#gh&W3nMR8&8B30-9%30?dQf znL+osFMbgUpSgo2?<8oj*m~G!E_TycVyIc(Ix~sv;fAcnb8>CAchm-3Zz2rye+U!B zt{WVjLD^9WT2OkD@n*VT#3Ch@$mr>oXFHG#@-IOyddFgndtd|@4849=AXzMo2Z<_F zyWkq@hufkWtz+`~Ez@$BzIX=0Q(r{67(*&s%VewGP~GL^@okk9KoaZW=|rfypU&`i zhrCp;gktI%1E{%@>|!NVVuZgX36Xi$s73Q#jg?v$)O^vr@Yo%o2rgo`nZcu+Ggeh~ zbNpTiyU%(VbBf8BgM$u_C!)34iK**W)aa-G8{s)w1N=y?cCTLDyfW}NZy9uwxKM8_GW1@b|&NuEcd!Hv7V=%e{+L)vem|E(_$#RXNprcw*jh_wkvSyFXp7 zil_Cw!kA;09lWRT#7xXmKk`^&so?e&McR_Ot461IP9*xFXWa8YhH*C#((;V+vxDVspSR@ocYxT4d#vh~-s!miLCcaSQxzwL3Mg%Fjkd29w~}zhmGv zRqo;+89wu;i&{Q&5iO4&)dytjn;g~>{n)hrfMf zunAUAB8eeK=QOP$FKD2KEnM6zRTjJJK;syX{73gqruFhv85#U+zBoqG8QVHyKUqLK zZ$UO1rcGGs=e!h7x{97B6i1f-(YmzocG*{5@`0;jmlEn3&H>XPdYWdYq;!(F6xcWL z^v{F{Q4H&IM<>O=@4uCoc_?11wq7lg9MhDfzB$Qj?gUHHEajtwvz>9l$aKe211RpN zD{WA{Uu%P^50-sm)R{VK`ujudcry5nge+62ZUxD;`Q#OQwgKg1OsDstVr`jvyMJ?` z(=Eh(#fG;H$OJvkGB{|mJRq}%$NVIr%J&+=V@PAI;L}J(`F~o(LoChnrtfy|{Gk5y zihcUePA^%5xlis9D8F74O|uehUeRQ_I{H=7%jl&Qd} z-6--jyZLF3;ezo*3nj*BOU2J8jLvc{g0_vX!Rz=Cv(?Ng+h;9Wq%4`s)0;pL8OE=f z0<_Rn0$1}^OZY8Xzu5JkC-8O(N=EC2ZUJebr~9sGnI$<<`(D`dNgtP+K)1P#i`}bO zYmj}4vQvH7?6%50zp5Kem_N%=X)~cF3^B-J*BK5V=Q-AACOoilXPJD*Wm!(`Y%`@5 z+k_<33T1s;d!dzlXwN2HmwUZuhHI9Oeyd z7Flu0l9+ueNBM=#>ev5X7N?Mn%WDJa1kH16`GinELNJ7^?&B~np`y?}4`yD z560eY3C+zHsDye%#G9BM^4+G_)cm%&pS-=Sxx(&v6$z&v6N&964FEj$d*t2Sbd$ox z)-f~zN^*Z-rdpS1NgC9j8+SW-MU^clFZEot+s_0SI)z((tik1|q&ynp0D#3vp9d#Q zZ0Z!dMw+jgnI`GplieVeM3~-_;{&PNJHwI^OMp=i8PRP)*9`4_$3v}nzebKk07;BZ zA}QbQVa3F|r+$HEVR~wNOI=48EPeAP!m_?rQ)z^m1ZC=dkz1zw{$4B8UDMbS&lU{1 z*zLVB5tUGnxUc63+8pp^BBVzQC$(fO?SuJf#c1{7M4dN;d!j{=0tG+0G;K-rtm?AR zg7*0DNJWA2^dPKaz$xErQP8TGA|>}*ZG3^A2ENJY4E$wePqNCO%kO^5$#e(NI2=NJ za*mN!7mO18pkrOR{$9i`)+uNFBvBqUfDOKi*y)%Ye9aNyZ;)3SYE?O@QDHh(L`|zy zS3*>IbOYKL!_LWosbw!V^XOt%bF<>ScgU~aRF7WdN6tskX9WFyBz7L#tm~S!2{!(M z8&-JGc272#Dik7b zRns*7*CraqJ&Oj|GP0=LaEW#Bziet75^TRasdAVB2ngT>#mB~Kr=u(7%38!`4*c1C zo-D0cr6raCG=~@PX=N9Ysu{~hnMPhje^M(K=ZQaq#trbfPUZ{;yOfipW#`lwTaSzJ z;;;8g_IE8+sCG{VQNw%vmNl$%yYec-pf`C2dk#Suj;GLc(a&T#@g7f%X(uh+O3UD8 zr}0ae;}5f}f-CP51;f?E9y6(>3_8=@BaCXcjMn-@?|vq|@8DqDU1qh{fk>~7AZbDy z;j{gyLgQ(-_-y-P?We8Bm&m5c49k>d?rlp_^y>gbzpE!n^fTrHnr-Oa0reXh}sckYIdN>yO;#7o}XmwIMA!8+q|kQ#SjeDmZ*TW zsqPDJQn?sWRf9NQ5dBmHXj@r(e+uy-aqUqE3EZqL#dyIXM7y5adx#etsP}#R`(AqQ ztoP`4oQ(ySQ^~qzcvz^f?1tuTQWN}P2H$+7EaARU!CyeDxPc!2yBmS?W;=slulJ?? z{VHzVaL@71v@mrE-F^45>TdI4((D2cQ^8LE&;PFcpYAP^3Du3mUclSee~#-?`OPai zz69TSZm=Rmx$bZP^KhWZ*JlZkO=I@U9K_Dfh4Lc)VGDxntVZQsTx8R>| z5XcN)EX~VSW`k`IB#pO#pv*wfA*e(9v`<+gg*;U}`%tPl{d}s(J#^K0mDsy4kUiOd z;_CDfFG}f8qMIjbQ`Xu+23hC^PA7Ju9W=SVU-In=y~(W0&1nj&{(J#Q?Sw6TYlN%3Sh&(ciywWv1J}`;yrn)7O+^DLLFehXAj!cc^wfF_-qPX*?iQ zdkB2y(ojv~zFc+L*uCm-*NK3d#)Cq&OGs4H*t6;ocfGw8Rra|wQ};mCF1PQYReLrJ zuW6hds%^CIOG33P?PXYH*+QL8Sy8jI=%sGF*jK1@xdr6>Env$~i>%q%Dl|`XF^h!X z*6W;An`c%m+=l>Q;9}NZhAkD;h5PdpDytW;Mcoyk&`{~C7H~kzfQ34LQM2=)%JWED zve?9R8Zv8sFRB_+vvX+Gq2~Q> zg}!^%>>O5gz`}#AEWY;w>#98#_DOzs;1aW{whQ~|+YrH1NwW*S&Gf4{G<4h%exj8P zMBC9_LpZXcdHHbjL>^5?DRha1dx{_${E9Q1wdQZ#;I-yoAMWiXDq>s+lL;KVb)xVD zwhNEtD?B9UAod(957}PxFgWFgYNrTR>~{5Gg^m%AgTg7`Nd=Q+-xwmFN z0>v%RIt8B^j{1mv=FePYh$L zt(P&b+X%rTUs8xYywnGv?DG;)L;=ySN@EpBR~Z8VAx9^lFPaN7ep`OpV4FmPQR6hB zTnbK8YlhQjlggl@4*YO!_h=sCz9J_h09r&0G9@hN_~ktbX($XS(hKx277RoLM-qr< zhFg^gd0^5Bu29JmaFUXWiMjH$=S`4M*C;X2lPBU!PF!I`T(i>llg9Rq6mAaHeG65% z>yDt9iZfuod1hRYu&(;B+&Yw^E(&8Dm^+%Jr|~m2v~@MNn~f=tjhYy)KYXHkB-CkC z*zHd4NXV5M7U-iqPoozNHM>SYuVkM zBnV!f4nn`kZUX6A^57uzy&zj=Em!Dyg51O-ty#j&)pTp@c4?-|NQ`DxuVdIBiL%MN zj>OziA_&qR3URlRu;7Rp)b0Ee02R-fdY(z41Le+SnOrdp2_^}tlg1FO=GBGf_-tC% zZ*^&GRA%E$3@ne{z2Ekdu|dt3B#Ew^c$`1Ka0)=Lp2)zNt${O2b=(|0)rO(7JFr+Q z{Bf=o^&fTt{Y~`4gKh6p)GxF$m~14vB(xN98Z_{Y^tEKyA;B6YS?`m)L=xLWN~C#) zLX_qarFoJxI%a|-#r?+aY3hfMs%&?`X^e`^jEhr_(~FzL!w>HE9>)&C@mycRF&@V& z5;zKYxqu%jSHXt{K|0Cxp(yfH=`Z(;+T6(R7ft6NT}D-;{Q9a6)UaeO1ZgIgfZKT~&x$ z4JdaB%Kb$D6zYV)75ve_=!vF1^#Z0c;&vxaRa7QX?xbk~lGg2FL*;qEYpoE!-j)~x zNeAgK`#E%6SxWHm_O=Wcf|Oo`%2C#ZnT*$tCnW;*Ur2s3dn=po{c+rPju6pNYt1*|IIc{C=&8yto!i~_?@Bw@ zThlzw0MVvP{M5WLLEA4_mdCTwL3+ttB3P5ar0B)7zU7E9G|nP0cqg?JEC6LYX91eb zC^c#ZH9I>Rl+dDR6c@|tr7FXUPMXi^BdjInVX9dt^?PhAth@JHXK8uy8!dMXj^o5o zaFMuloo_lv-w6w1tL0X1B$zbRnc{z;K}_nE8y*4FWq=AT<#b2EtY%P^2~c+dDmox9 zc*kLAi^Zy_P`&2}BfdV;IoN@EEasx2f>MeMX( zcfuTHx+phXQ$NNXc#%RclJB`c5NiA_(dVPL(t3#sZCUFplnoY2*MRZoX1Cc$GiNn9 zK)OFAss2NPWr@_^pRP{LdBn#LCSI}Yan7zre2ZAd`x5W~)~=Vq@=iFFiHh-*%7;1E zi>FnRIl)IF2H+SDOAiW8pa|DX$vF8CsvFN+OTn&GG29oalD+m}sAbjw-;b=Chvb-L zh5LRA_MlJW{esz{3l$FDW}gMLcKoY-%3*Vd?w?Z4A-dO6-Qs!b@;TYYZFQoQPR;59 z(4haDX_!wFpyoZ)$h+vw2u?HT-J zYg?JuFwZcrt}KUcN+I{`a3Pz1p?`6_DP`^uX#lGBr1w)8*2 zA+&%)MWN{L2KW!3bDpq5Ny22)V`tD+!Yzt06bpUq=usPVnmQS9UPm20Ldy(m{M+oW ziq~xtLs&C%{zS~Z%Kdj9Z){BJB9g-4?exo5LFW2wz zLXS#QFwL|8TWO@AH}_nrKj=l))pLqRh8DCL8phd$NoxI;jR-BcWN2$b%ZH+Y%~*jZ z=@`Do59M;qtvC*CLz(TF&2NN8CLY)DQlhR0QCQ18&8~be*-yO`FM41DVa;sHFm6 zzepRh5@;9#gWM>FNs?!}JFd`2^K$E0i=>TltKd}4#EX?u3ot~)CMjqDfsvZ( z?37S#>nn+mXPrJxv&K=Hvj4R6?$XAMeG^vj1obaIV3!Zjc?u?^)m^3GJb z9$hU`VhK`WNkU3ID(nshGx)^^r(5$(M3JLOHa}*R=cqK1i+p5__B@H~;3J2ESp<~R zRjQP^?+~EpYM!wEN#HsGc&KAs_z&GwD7{NO>ok=CU;fW3G*C(i*N}RiyA5iJzQ5lm!W$u-R=T8=ywVRLJ*D5hMnBpT(zuI#CGSzB0ERpkY zA9)0kOJ=Cq;zhfVFIY`yixzHHE~fi%{m5KZP=nncE$$9oTx2jaR(qJ=qSKQ9!k@TS zM0qD!-WER7RSK?Hc73V8K;e((8g=^2?BK62J>``XfOnoR79s`Jr+v#qe_YqGmRIx6 zGoKVo9`W=Yr@SCHfoLa1Jm4cL6|qYqVuuwvLJEomQCw=J_%+l_WqZMAD^j*~C;Eyn zS2hmd9z-=?;634GCgEUct@+@=!O-*9q8OsC*DE=z;P+n>Pv*uE>9aa9c{blpdk#rj z_z-A7xtx}n-&&&!aT_ zf_L1^Olz)NaHCq#A86mmJG=q@NS{S?9DJl52Ops{^<^hD=X#J3c@VJsHE;3JHa7ey zy2zpv3$Qc!@Q2C$ZcD>sI!1XD4C0O5Gk~J=#+s3HZ-!i7j`jc6dKPs=2WB39b9=TN_u#n(e-t=DKc%akL*c{X#B-|WTi!B<@m^NE$86iY#(_H zk-=EDHQbXP#sNy^8OTg-Rss$JK_^bZs1u(ijh+u%+M4{hJ za!yBtVQ}sa1PPTa(-0j`Uuk3DJHd%(?M2pxbu{n#z38dwhownAki9fhrWqO@`T*&0 zzP29Iw2ip#^QtDDM_!t)d7^R-E0O&-^-3u(oQZz!Uj*r%u2VzTTzxNs`}TP{m60B* zoy%8Q?3%gc!PzBEnRfQ#{uK5`$z@;SRV<}q#>d(CFDo4aknyAq7f$5u2))I^nQpXz z)vRfBx80+V3)7k9TpsV(GX*ysYKNbrI2l6KN21H_XFTjmmL(;!v5psY?S@*Hm|y_g zKyE2@YIy;WD-$4piI+{Ev8rsh?QRN+r=_24ZFmt=rAExLwbly{KO!t)q9A;Th|)!>R8N*)>#GEgT9b(`aYwEp~XOJJ(*y z)R)ryf6S*liXSU{C@;x*a)s?Jvl|8N>T169;9l<0kqo`>v#1)hpP@~NzV7npnlr8P znSu)vNw+@(_`9C81<8posAg$@oM?@AgXD})wB+bo!+G=QivTf8K{*ee#h1KUYgA~9 zf&nm%AUf?8pL>qw)^XsCmy`QQB9RL-kJl_A$vIRvc0v1;hUdu<^Ev(mGe1NhRgOK$ z0WxR%$QLbA$ix--Cwa;FZZ_n9#&F&4(K^*VMPNj`<+oaF)-NNNS85h2WtP+Xg}igZ z3H|M0bb+tFObplC{mIr!bh$fq5u_eJWv9FM6~bc|lS&%HJ4h9L;V? zXPdTBySiK9cF(Z^;Wdw2x|HM}0)$ZUM3hH%&r9Q18k$O;Ygi_yRu{le|H8))NJ&$@<3iJ(k9*LlycF*b{ zm%ocWRCh3VL|eNFimMrtQq`4}+c6S7V}}|#_)EW(0tyr#8MhmpPod*p|2fpD zgIh%s5SdK=ew!;|ogy_CH__0e#8q*-a#}I5(!G`9Kraf+b9|{Vhq+ zaV_A~L;d0k3!30$7~yw;KN#VBxmfn9G<)Y_d|Jy2pvUqBTP%r3K{o z%ykedn@LTepCvPGdIXudUqFKkF{G(gZm`&*myc?$Wkv$K>?RQBSfZ?50MSeVy{vpU zPPkz&RpRdf_LyblY|To1BZH&3&sfbOR7^yF|g+NKDQ&{Rx z&ZGgv6&j?phbLN9Q>{$~1;5R*s$TVmO{4YxvHVRsb8Usc-r@MS$Xvh=4f>ceWAGj? z_~K_T;}r(;7BgH1Dqi@Otu=iA(}^aC4N9XvvzcfS?T(SM=;^m2jAZu)5 z%}Ru_U&E8W`O1r;4^W6k?s$;&`?{~MQ#pU%`2y|*?^>yIRB9Hb0%F-QR?k}#AUgSa z-bTWt{?^UdKs8_AB?E}Fw(YDMOa;$4o?P`KwlPXj#^P0Y)+Qy#p;Zux_9%xpMbAJq znUtw*-@1+O(KJ8uLl3o5*|!dqF7(~-khImk8<(JzF38t!pocAqWN;iex8t>LUlFtThmQI*t>D` zgJYoeBW(LiHd5K{vQNOwTia^^(hitx?1jn5A#Ez{Z#KbfOS0W$5;twIo@%jvR2jDS ze+sr){IpPOxyp?K=u33Ft1$L^Cs;>s`_T{b{ELjB2X0bi+9DtMhee|Q@sV8>sl{~; z`7b=l@?T35akawW+iJMaB}&qClcDaCao>O8@l7ndw0p?#b1T`a!vP8nN4>B;-Rmp9 z<7v};$zK3gvfp(An*R+z3*TU_Yppv5qHG%!g?+lRDF^6!`5&2JSURD~j2E>135x2m zkkPg6Yk|zkQPRRU$n-XwXiV;zWD1AMxFxgTBcZ=c(#x+faJPgU8YiK;o4*r!W^^EF zmtNTj7C>8TBGpl-)XBcy&>RvhGVZT@Cz3C*yNrJjr1=MhyzK|ojUWz5`dkCleGY&C zF#?>t|GNvR>V+1Uc822Y=5$hQNVLU-rb2%IR>;}bvyfwrI$Wi&OLy-syPM)_FKXLe zKyZ33M5hBolW-@4VGfO60Epb*+PcF`j=rGwEf>NI01Cbj@CbHjCjrTIlj_G_`fV!E zSL;jz*dxhdw|GvN|@J~To z=JF^fs0sTWv%!48=IpA#(LUFSma8y0ms~_)1GXX<@Hz7+$$nb$U0a((-^_p%T)8Q| z4-15_)HOvwYpJU3tt!PcRv}q{&dr=N7CCqE=p6mM;rfV=yw4&z|50VQ77_`rH}Ytl ziLt`W>`1&Kn>h(ZP-e;ae(NWc#mX|zNC_5YVBJ~sTCO`oIX1h&f~r|MIuu8<$n)?_ zg!@R%vR2`md zz_|qt)?3rJ09r~~SaGA9!dHb%?mskFbP~(QZ4+x+)<(NK`H|(D+WdT2=$z8FxTGr{ zQtpHu_S*1sM3?_P{hPktZTN=UH6g}e<2^dAN|VUqFEf0L8~Zy6>R(0d zZFDt%@Gi^-|D~#|bkF=rKs@cmF-8vS5^A0FO6&1QcPcXLHXr$kMGg)cW?JM{i%gsC zBfWlZQ1G^oTu&q%>{=iBBRO^O_!%F0cq3rFo&v6crsBX9PRqxOzHSEr&+5yzoq2tW z7hR70!F|11M<7wPCe3lpS1w>+(n28=j&-Kf!|^jx-FI3n@n(Dx9^a$q_Bex*p2Bx8x*ktdoI&LD|dur2Wr(c>8y>bmNh3g2R(YXA)*e7k1{&PW&b>dVQ*s zO0H0Ip%OJs8=@#T3Zru zb}v%jTKc#MKHY12yQu?G*mB#2?Za7_xUx2=v&m_uHs#T7dBNH($VO7}3{q@;aXy*B z64^ypVMuMseHfYUc*Qo$M#t^ZL7Zt<9$^lYCRp+WePVxu+;Rl33gz`+g~I zmCh@p4}RBAxr2k+rz$SoT5tlpf)i3}KdC%0wBV2JrE^kqtM-eJ z*ieEiu6~UV{}rm+hARp8CO_2hwXVD$lLL}6&REd3_LHjBZlu03UQBdb($w+Ty*ni2 z;nBS53I|K!o5A{Zpu(4mbMkj-#Ct+i8_mljf&+gnmiO&|IM;jy*l@lh+LPAb+=e%C z!qTvcS@YffA>Yty`ij|d6(6je=a;J$#(3G`YP20%H_v&>6unDxg_6P5He3IC9}nA^?bIEp`=O{$=+tT_AE>I1Nq65LK>p~TO_HYi+$2pKtt-iwJgH=%S~WaP zU`r9KsE4pDIwy^;XxmGRkQ0(C-P}-}>}y$bu07JoWRU4w z&fhPFM&DH7iCby{x2AUja#fj3fpTRE)HfKhBxHPaE3x$Y!#ot5uh>CBcZ=oV$7LLU zO@WDoYQLr^*RGF}6&-qnbxggfZH5QOoMq$pRD+SVr;mKpA_oV9edI`A&iw#j@ld&2 z1cpPo^ZDj+m4DRv#~lB-&_B-gkLf(z{1P$1meT0w{b1>dX3l1&3r-thEd7uEU@Bs< zzU0vSG8YikVyWWimUopdUi57%vd-?o?6;fmvbSI}!JZ!0QS&y4K|1$BNXP82 zXTC%y3GsiYZa7zNVCzn&y-*B&5w=Ep&G?mFX4mIT#O{rUSb1pa}DIWLQY5vbWt!e+60LOI$l64sj4`$d&SWdJ5x@vwe`VRk(6ERC)nPG159R zen}3`j(O!cPTjUVY1vZwzK#?`YJTewx@P<^waQu9qob8;@SbJkd+{*cj4q^1$Z`6W@VQ-MnC zVMfX3?*m?uU=yDJFtHj*+{bx1Ig-_>`ly9%cf(sWqel0$%aW}Zs=a~yQU|@p6SGz- zBSTZS(dw8_zkCy_+m9+mu+mT*tESF;D$D+ds$<$AOYJc_K^=9tH{B@$wZn$sRMnD zg8dyYZgx#x+gw6ChtxCO1Zav$KhGqx&VJ4<$OO!+W?jqvC${T-=TdGKmeh zxa8Z9*k{X{(^o6~Or^i}NEpc{Zh1sy6?JLcliT0BGn^AT&e2ME1PS2-=d}PZP~rUr z;IJbAn>eDfuR^;i^z0U)9TeJDp?8B^GVL!K$lKUtaic*2GfAC0Pj|bF(T7}nvV(>$ zD&ZcQRo`_t$~LVrxUQXu5L+_!7pF!m=MZ_-kNYc9y^8*Z$lwf;X)!?x)gR}LSJPpu zS`U|5RC(bhHB$D>YL%54%xZa3uUA7cmn-vXu;8~O177dO9j-# z(W1r;?uAimww!}Yt^3E4O5(z$r|a0^sd6NwforCl`ALNp4Y`6`Zf`(<_@-*BRies5 z{27*oe?z=>6k`C!`1*+66#6@vj^=!$wxQ9hfAw!pMqD3`{#9Q1&CCPk3;v>gC5AL? zMG&}&93+1*&`M@l@ZD7SDEEHA6xg8r-Ks9rxz9VWw9y2N_blS@Ao9ChuZxO;L%zXz zHa0-4Y~@%1Q%_7+D=)EzHWjZ1Xa!e;MdIhPB*g^}-BvD+!@hm+LPymFIBMKX+BKKi zjrKeNjI55~)D`}Qm$qt2WGf-XkGVCDXZ%QulKkLA@^tq(6qMBE*-FRI(vE7no93J8 zqrIua6b4FyG^*-=no{5U-km#CGmrNo=d!+=*hzI=74m2E1-R zLL}1hEJ2Np@^ay!z?L?B>Vq`?{Xqd)-5mgj6YTFxue8$f-%8>1_yH$$uo9ADYhOd`*o&U;}QBmZIEpva+%WoS@_D0#D@ zbTv+xOPsDC4?F5hhGO}CiSo0*QLJW7G(p8zqWj@N@B3{*aV$w9sKsZ2g;giKeA94u z7GJ>^=-agbxN^G6%jgEy^2t>*NBgvg6X?$6$L5jW&mc*3o%>18c*aM=jkvDt;nf#E zonjQY>0D@he;lMjjqmXe6;}I%?n~h`C|KnbgsK&e7Xx{BRN9`0KRghw`gFZ33_QOI#K=m```#1pu#4)4TrM^?V1t z7i%s5n%h;8@tl{2S^M;dwO|-2e!cc&_~N!I=B5-Ff;I$ zs2pgKgMyJh@(UmNk&hglh&;$g?stjWp)4Kd?A0A(`sy$N6x*??a|IhZieM(x3Q~b$ z>wUlX< zq*Y1HZ+$Kp7p@tR&PgPG+zUz{Pt(lhp5omS9NC;encTHR)coEjG;a~`YFY>(4N1ezx(VS@G;r0`e;*3vBt6|J0Yb_ za#Z)M8=K)0hhBLlHD_g~+SL0pu`UNtOAdu_!C=F0V6d*hB(HlfH>585MNJA))Nt_- zL+U-GMh)JMpaWfc>dr?&OEflTEA&gNkS; zHvV{UYj^#KO&U2eWXl@pCC^)|jKRUfldX)~R0jJPE3#Svq2SS@rBvG}WtU?=kf z@@xMCrc?JqbRO~6Zo(*6yS25W=56K0ZAsID8Ytauow zS_^dY!25zv&<@}g(f>qhe!$*l*O+qr**SDg4sYxL&AzNZ&KSFWWk*ljDJ*s*_FhG4 zgdbMZ>!H-q(T-nX1R^z`yE$q&?T;K);f}d9!L{2?=;T6 zojkPnR?C_**^*CC^2O#>LR=-{Q@%wKZTr>U#*4d@pGf@qyw@pMOT+)nN7HYoCMv^f zrg9z6iCx+^=s764WZk-O6=#dAFl0?a$?Cu zx8y3r>qQ<31J}6PcGjS#t>N_wu$9qbAH{;ndtP#n5cMj7I1xBJNB6WISS z$*_OgBD3cB$SxK+I9Tf==lh%|`^X~_IiK~B|52n+yX^)cRLtx)=A^J}9L`Zd&7x~H zY^rwU?H;{xBNO3tQ;>=RM(gg8dD8Q_mw-nyQ8jo8M{DL~%xMco~VyOtulA`%R zJILDGxW{!jiLkrBHxMB(q<2`owR^Jyu@AvvTy)C!8|uAJZ{{Ob! z!MEy1WJ~aIi+qxE01D|ook+5bBsBE#w?7Z?$l<|sA9d^P}(3-*g9M@{ic~$NDd(^;b-(ArpYy`aOuRL0k8;)kI;nVd2h{nmG)_VQNNuFS}Gbp(9 zScHPh?x2&OeO97Jy{qJd_1G|L3g z@i4~lKr>as4f_4Q6Ne&I`&c|qD7@Fyk4a=9bIGxW50aAUB_`US@wc6_ zSlfiiSk6%@Ew~H4lVk7cTKsBgQOLXY0+^gL+n!QSZL+G$fYL33DOqN!$ z$Cw$LJ*p&inc%=y;!~SD;<1Yq!6{GE`X}-R3%=D$mS-fE6zx^#k&hb(f9*+?j@|!Z zoUG91wd(d1fx*52@OI~+x@V}49Yp=O`I@bUx5b@58=7Hc`zTpjT6edAcCw1V0xEgD zV}`zOSykI6Gn3-xzMQ9LpURm_`tYePK4LpWA_m(2X@newYW^Glb0QStW`sPRmz$fC zvM7}w(@}8Y*yiBrUv@Soz-$fnCj)DQaJ)J-IB>_#Mz{DW>*iAbDB^({(LKl$NsDH> z8$KQsaX3+Bk5zEI=wgM{? zT*kRiEfNP!@F$n<6w*xz4_NXxO-1T)4NS66_4Lcciwudw;QeT{ahdTV$$r@K(b$`bL|kMCh7#O~Bfn}5nz zn;C*JI78Rrc^g8|f+KcrIN0aOF4Tk`t}LM%j-o22{8`a%MZpaQ-~*C+k7Oap_auV&CO~S zsdAE1&|Q1o3ywN8!cp=IRNO>G0aLQ5CxI5ll#jSX0j*i`Hc5| z#*2risOB1N5t7=4aav!;W`=|OC%UA#i&m4^A0b3b!Rz_vlRgx^_Kl3%$V3>o|9#T&?R0gL|&==h{k16HL$Z40RwMFr4?^ zA?ycCx_~Ukbr|33=ltLU0@1gyMZlN)Zk;%g?J`+2{l&LbUfH_;;h4 z(|2F{_PuYtHS@0T@>uUy>#>acQ`Fd5-*{Q`CeEx%FFt3Kwy;>os4Gj3JWk(anbj zX>6^O%;WALgE`J%c0a>sL$Pav=pX^S#Nsuk()D?k!8ftXK(Q-%oCVpIt_7hh8%dB- z4_9W2nkMwz`yctQCS-FJt)h|_QiTju^GU$Q&k_lI;oJ1-c^J^8>G(_`$-1b-H3WFa zbd*xLY{;e`8$^(}P)#m7pO0YZDrJOSw z{{F)*p*n$g9u&C2jVaR%+memq79(NXv{!XX%N=y#b`McJeJoy9njDTex9e(mn#gSF<^{$j)vFcuN6@q$xrYz_;ay;3VM zH0U@Th@BVKdUfeT%rD8Bxl;@QI83tDWeT4dimr@Ef3JhCba1&CEC{BDVK?RoyKDSm@}HBn>}b7)#1`W~PzCEG`qa(_*#*i4LzDWI?}1`8P)v9rL_pQ)XiR6f$$)SF@V0IU}wcU=V*p>Qs<& z^GW5^hiS&S5{K<~*tqS(e%MF6HE9&zY6=eY<|k|BhU4^AkEd4tm`bnYa|xcrp~|Ak zgOO=}-rG;L{Vmr&c7$N@vvO^ZD6u0{wLc*`x*w|Rvk4qq@iR#)!Dt}oxSL!%g|^`b z?axHhvVWdb0O1fe)>`6L(V?HQlv&e8n#tL4(Tu$BS-=;rtvUcNeYu3?yIsFf9n>9( zNmv(xX%=q#CGZD)L##w08S7Mjk8p4~avV0}$}|gMAfZbAbh8EICcR;IYq0NRSGaYG zPn>fP)^1g#%2>RmLkb(>nwAbm|8w0>MCdD2*LvIEsLcJWJa}mRl=}SDTJ^D+N@h7s zR1?XVZ0nx}(bn^F*hpCKzTa-S`YKmFxd{4zpyZy-FtyI{B#p!d;;?j4E&v(;ESw#|3)Qb z^+b~l*+?aBW|zcuAyle|P%o)7ylWy|5s6%lXtOGK@iRY7*2zIOvTLKHLwsX>;APi^ zs7@j=mgjPf*S6|tyS6-@bJ-6XLR?*5_;J;lGqY{&>(Xk98kEP3F!86+E~& zD%~ARaAIm1kDke{n)7^B)BFWHi1nAAss-k~Z!Pc!XeWEAy~ObQd?}R}vmT!i28sEEaT6yf1a%xrh&JE= ztJ+o)=<`nhxrtA2^dWl>+oIfG#w+0=XG)?y2DF`nK9%JRpXCX%w75UMVV&Ye`MB#; zeErBlst2o@eA|}#L_p#yC|)$^V@P9d3UvR8`wfJXLu73ML*!Jpek3*j^oI(>{AH_I zcn7Hyky2$~B4T~^PgZds$>Ng*JSHu24VN99y@!q*r0vqB`Oxb854Efr6ioS1cJ~(@ zAqdcWQr$Do3}SHUBJ~eDvqwn^O1Dk2PCM0w;xIUz%Pit(`bSmP^6*O@wTS7{7KZ%m(<6ykm zK3CH!)q-9iO@*q;a1jeoz8T6F&)T}Tjnk8CoZ|odFx#&fohG17^QfD%i)!ZGTm0IN zTBuEHuM~~*rmJo)#ezsTJ5WePtgH0Xx~IA#+EsfAG7u+^kB$fc$gI3gDefW#U5Fjrufg*#i_>~ng@=(IALrSXeGKc3 zx|PHVMvx2ID347cNv~HP%cBg~xLkCkjHmUXE0)pB@-PyKAC-P&{LDRb-4Entvg$w^ z9~tkq6#dEGY{>jvzVBDzysl0fJ*DLwU7hCa;r%?4#Z%`!|2K z`Jcy38dPd$!CaHcwdgdS8GDp0mI+8%k9TpP5|ISp`tPP9vpzZ7irmCQf928ct9h+p zpDuT4Ujl+RK17qvIfCh(ta>jd5;lAx6MZ0Ilz}oX;n8_1IDKL6(o5vY{_+btt3qcI7~Hf5N{FT{9mvI_noPgUp*-Vxj@g~+h z2``DSb4dgWv#DKaRn5hK55E3aM=8SVj@XAxeDA*2_;OFMAn85(cC_g<%5#y-gWa|~ zYt9`U!4I;1>5*jVr>gXsldyz6=DehORfz5*aMJJ{oR;CteYgM(v+ z*}#3Wiz?8)laKt8ikgK*p^v?mShPhyceU!>L9Dbk=!@KJtKPglsa}?FovpWofcaM+ z5WaqQBcFRg(GFp)NXj`!Z-6=~i4AiWEf{m5w%ScMbBJzVXWIac?C)RM+{ai!{)*JU z#^6Epe5i-mp)Dvif#1TXR3sHKB%MoQoIq0y-p2HCl}9Hwz{kY$n#xj#C@UBF)*lzNIW|{@E;7O*3GJQ;gD#38DWte2zU|W{j$MX=Kln%$IVH3;A zM02|$&!@1OAMGF?()j1Ww)vX!Do@c2lpE|PczmMtETgS(y1hPB*Pq6m8XEuB)X>py zP7U>WC-kpeLK5|Rx=*i`^*ib)>Nf!>;#$6GPN8W=r~#qiQ)YU1s`Bm#+fLdwY5Vvi z0o2ALryW1`jnxKViMgv40Hhm8v;aMml=K5SqNTFMMDuqERE^RZ%Ul<6I&%k|OH>tQT2C`_+{fyp^f5*$By@qJxlcbuvDhpIvE~QaHM37l|LHTu3rKvb+ZxNmC zT%T!7>{LwE%?)o_{-h`&2%kX&TnZWeGC}eKPm=XssBQt_hE0>>q@xIN3l$vCI^@s2 zQr!0{Jf3x+-apfO{W9$08{NIVn%_q>zpv1#*6Vu{7c*a1NZH4K#Te|~pZo4s(n1A@ zPjmOB%7{$MsH?c!DN4`zH3i2DdXZk)3`VJ3s#vIPztND#Fg~D(5B=jE|9FE( z&=<#1#4O?6^L7?0#_5wh9^<~J`)Vxa>_BK|R{4vE{>r08-`*+{ zM-@!_Fp;R?Yi;>{9$fXhurXC;Z9#v^UHlpyrdqtH-i{aa=(raN>VwXCbdJC$EVU>u!sLXQ3>T~wH4O=#tk%_@@HLH?r1aJ#xHl;t5=+n1`cb#0 z<2CMRed-dQZYe=sXOUgn-r%N6)oG2>umJKsp0nuXh0F0AOKL>6USN`RM)b{F!eVd` z{??A9Mq@O;OEIVgc{;;h_+zN<0tIg12j2hd?nVK9!CtNjMeJ5qqu3 zAGXqZuksm2TLv@R`e7g$uCxrzO8SfWUaU(jH9h-7BsxICnC|Gh(}}DS+VMb5P~`z0 zkieiYxI^#;liwCvb^jt+!f0HmU^c$DVNx*EXT0-mp)P}uDax2iWWsMgw0Y71HBn9{ zpY(e!BvBs?R>D6EM43%8tp;s>QYTMmJo64?=FR#Uu>gX2-)$(aLVwWe!vM-LLCxt$c=+kAIw(4h4Y)WKS9`BBElnh(0d5vZUs0 zZ?{-?BMIDGrK%s1OHDA71O+id2mwO97ZODQr78+y0j!CLBE=-g^?I$SJSy0GS3pG# zornbxsRB|IIBQfabcDR$-|o3d@Ol1kK5)-DyR);iZFY8ccJ?PC=|-=71F@a{W}$F9 zi3!-cx-mJ@2fgp>h$-@Bj%R8L8_!yGo3{qRP2;E6Adq|x>Dm{*Kd2&PU)97G;c9A| zTco^$8CJeHY=PLPywc{F>3}){q+Rwk4rnbvc1VP4qy>r%pp_#d!a1Wksw7;^YAIq4 zbOV#dj|Ki*c+sh8$A;bm@cn*{XXc8$17;IX2JV70EO`sRb>hz;m-7&S*#jTlN3bi* z`+aR|Jnmp;0Bfb8^Biz%fSHujy{{3&#qgb0B@+_NN-%sOL64FCc0KIG#yHGN2dgkT zBBkDKns*lpyVaqxJWpAwc94OwvIUCIyIN5dx^iIkY3W{C30ar3l2{6${E8-Tg%wm$(mgq*L7cBJG&f!9BNvZ~30xrtFQ%CaIYAS6s1 zaHu#M&?apB(J?1D3A7Iga<~=RdUvJ&oPAqL9Hz{HqPYWA&EpfyURqE}sndHi=VXnG z3q)V)LM|isQ)Uc9_9uA1GhWn(?<0xM>Yz|)@}fik(%0%7SzeZ-FB|#_k6}7Hfl`>n zVX8ncDW~ud3Sl$!-_`l*>JuuY_e~AhDc(x9eIxnX61?$#wx^J74*8m5@VR3Y>$22% zm(fR7*&e=^7##w%9encul`M7^P!0Pk;3u|4>(Udl{^mxlT9Ut&s2a^!`IYPOqc60T zegmofQ;Uvm&(`=jp<^6cege9Cg|6E7F>P`%Qr*s>4X;v7FEqbXIXtGTrJZ)9Q=0s+ zNvXw*RF1nS7FYckE9Lo#I?FFJHFFc-VjkD>jIA4%HDlc{B*CdE{scW_apuSMQJgnm z_bF#M7~N~}98KoF91OCqQ~mT#2xa!-K**n?NtG49<|ovGMGrF^4@`NT-_$@+d*FhR z=~k52RpRIJZ9=0IXuVQvjBF>hZ4$=z87KJJIK#BLK?MJ1%7iDHJ+p?b+#RiOU1*0r zy>Z`3dw>J6Rv~Bc4IzO-67qq&nE}qe6}a+TClLjslXyd&xsfmOX5^> zDv2R%W&t&_bGnr#ALN~&Vjk3B5^6u{{LwF64Nkql)?nZ9ip*ZNjQMTYuQdd}n;;(B z++!mT?@@>F8{S1#2mV{Pq3Jy!FE~{)b(g@yjZu+TJ0NmWGM5K({|H6zP6>`Y;LT%& z4Fq*)yllf})*93bX{!$}uzRG6qy@#%9 z-%9VMNNW-_uV(+aq%qR=HcVME*hDge?o)OAX*vSpiqVS@?a-NPNN*t}0JI_0)=De{ zRNpP7cJ{IfW9kzdi!kwgERTD%XW-+X@i$a`bdT_@$IaXKs?_f8l0ea`fG~jI1O>c# zdDG~WznQBQig%M9F;vV!|Dy$8nd&z%(LnwWh{7rm|0DWlqW@i%c$KA@%F-*A*vIX< zO&c3(KIfsvV_5o<)TKgZ7sS!*GbYmjKc3`x@Avl$w0AnL-qY3dw4^~*j%Ty;7(#Ou z8%_K%fsyCTd9s!{#ZadA-56)i^Qgs8#o_xD0uThwB9R2_!L98wv-nA{gYu*>D7CZ4 z@OWvu&^LK)ILy7&zGoj>LA7S1(MX2_#QZ7DWwe{|MMfx(L-0j-H z^1pi$4&47bD41x5Jfx`mzvRpE?>>B+!Tk8{gEYlkZIM|T1(I``iyON(^{%QWMu1lA zNOikV&2J>XL#JopO^r98G-kWXs9V zeZ}=9?uo@bZ0e0P!es#ES`mmTfddwrI8wBrvWL*!L@|119T_jCQd4m`c1AWCPl6jU zDp36N)5=A@3v{bccvTN3v98)+vJ+7fz0RI#*X9o1iHQ@=pczTB7*2EWQDMVa7_U1$ zD}y%@>>Zt2lN0JyLKh3zSxiCZ2~n@vC~Fm^Zk+z2D&l~d`Z#&T)#6XkW-3rRlRrqe z8;^khdgJliAUlH*W%1s|sq`%|<~*)6x{3%>@_d~WDd-wQU~YR33SU(U%3`;PO~i*b zrvX*k(`NfgEoyFbY0nz9YE_i^C>4JK@xS~#U=slq;NSc^_A>%vSJX|IrTRmEcI62e zX97a+ebk9lwGo8e6$jI=|5ugXQY9z9fd|Z>WPz3VcKhsERhQfTKLLFvsp$EKULf;m zd;{g8i?BR>SM)|HTFrYjL=1%um`#B1vWGS_EBQndZ53@tExPQ{U%-}*{ZxYj#80nl zd+!^_Au_!@d=45Y|D+H3-}|zVCl1}*5+~O4R!TVkOeM8j12PG3QDUdTrG$EKfe{K2 zq>VK+lVCP~AS8C>oEI5{dEU|vm8-#=@}Hy!uttci{Mn@Q4m@6;qIJ|Z%tAC^%yQhTg;j%KmRM9b|sQH0_k|z;RLDYRI z6>%wBNM_}SH1aRs2=;V)qVNj>jN!-X{^fc-V~%32O)%c!kb}|)(3t7I!EPgr#UeF0 zr4>4{z6k|4aV%4A{YTHGQVtZQ`QGWTn^`R}(n*K(ydWM!#ina55EY>+tqC#V_ zZWF;zYwtKAZK`5Vjp>VQ+FBI~b>i7_b-3G9%{fL_dMwt71^=Q8V+6Wf!N;`Eef-3Z zs)wy9klB4Qb6V!|u3VPP7&vIY2ZVuhOPsBcnGzwaZ~c;HENbT4^p4v4mdZLJJ~kwn zl82h>xQ2K1MW@K_#I%$&kT|!;D*wFO6$mBtmL!uWBVL{EcNf>4=OY`0bV4yu! z#RT>^ndV%Hq(|Hm?#0R#`|pv~%5Fq$LNWZn+-`>@H2C7v@)9*UTEPYj zdGU4dHTCY7cqxP0Enm6X(zQ##KXi|M#bUF(h#boeS)k53@-4bkO!tw3TV2++y-f)TOyA0?-f_@EX@juk& z(d=v$>V@}Dw!0>p#@BJbyCl7_zUq0b#cdgc70iYyHCs{G0Mt1L=0yeWT?md@^{oe! z{FXs~?vJgfeLQuPu7cT`V-!N!#ciq zRx!?^BHO-OyAhM5_i%fxSpvnUuC5a&{ugjh_07jtLEUCEgAl(fLhE;HTrQeFBu@J$ z?b@s(*lH5?>x~G+@n0%lNm#eyBb50oIy*ki&EHc%Uw=GnK20$50Brke%&BrCHzy(F z=;8SFgqd+BF>iNH_L@q@KHL1GrZavp|(+ zn(^s{_JO$%Z?v|VOIB01XiU0FxqFlNNs~+eX+?C#LKc)dzal0(_Dnd+P@+k;-AS3Z z`O)fkd5Z2twAT!Ox;f$H9KgInTbQ>=qsrAUR{YK%37=-_g2m^C$Kkt9Mna2~XKXEe zcq2JDYaa6q@BZY9bZ3Xp4+@GlzuFNnlhNnsL)-s#Q?WcHkH$ZI)YAXDA1}4g+lm&( z*flT><~6p7z1H+#Gu0++a)nD+j;AJf&?n2gLkX`{zLkea7>Ow+-R1T8c=F2Z%{+F5 zI>saAAICYIz-!KxIalNi%o#xI4eXB(*~=+pIyO70d71wN9)22#yfOPy`bNfPFcj-t)SthkzpBHCUhZ9NE3PIvIOhCWOv!r zkQ}XZ6b5clb}qwbJJNv19LK39`BbXMPAx(Ac){?b3MUJkgvc3|GhAjKOJL6pxZotk zd+a1mibPJ)hCA0`YSQo~+n{zbVT^PR6)s1QH95t*ko^}I-$c4YD*Ofbv1A7nz3 zcd3oRx$IBDXkJ=wG$_YRC09}EwhMJkj`=2<+y{oH8qSM$Ps%o%qiOgwA zd-*%Af8_Tg4>cr{qa06K_%Uy^M3P|GlX z_yyHyM<_6>c?-hn@p29qPbx=c&^pxaLVWP(^wVo)4BPGeFj~;yTfw0WdufCg_hE+q zs`(nBkZh1OU_cw06xAB#+x5G|tJB6E7@c?!T%Gyxp3Z0XYWt5ZUzU}ez4m>WA09bM zc9|HUd z8 z<`+M;MNv7^X(E{x2_K@xU%JxiraTA@RWA=8d; z>!hMdcW9$4fAg}G!&%;_#<0VYHGz&Mr@N>Rh?1rxq^O{xFluM*lAag-)tq{ABFnWB zILPnb4VzP#glZ+m1xgO-dyu|cSA4@wOizOR*p7;AL2S$~^)LTHf|``~=g+T=8+JNt!TlT=s85(g`# z?>}skT3yPM9)oD#=YZ0_#W?{0;r+p^g4Fzra-+SN#(JhKKY6TK)sUF*?+YZ;dsBXZ z@~=;IA(trRW`(q<=R(FRWSBz!L#hfhhJmP7_G;-8P23xTVMVx1_8vLQ`ZFmpu5etP zYNb8xDV zQuBQ<>)zD-tla1=cG6#7bAUOs0s>1axH^WL&{BYUPB-G_MJEAyFZ7(;%k1+DJ3zUe ztK4=HvcP`cpxnL}K&(wJ43aiT+T|x-4vm@%n$ZExyB0_V&u{9i9a|~wQ$F&`h5W0H zDRE_Av6Bob!&CEnhNAsJH4rkYqiX{)CZPTEKAr9$J0i+Kn#P6kWYO2h6%wvV;4(8B zxQfy$&>O3eb1TUREI41mugryvB!byaB;V`kR-tGQSE%zd9o_oZ_=}KqrKe#s99jv`29ozzc*R1PYV{pnHuA~aSD0Y!Qfx+ z_$ky_W1sM$0>~A>Rv*dr3hAhjTz|~mp^$Wi{79`Xut{#51I=&xN$wFqO#%F6X>@~> z1A6LS6$5pGxu(8Kpq>yYzS4HRp459!x?X$R^@5a0Lm}=ib)F~BZ-8k-+clO2EwE(u z--x-MZ%jD-A1ae`CNj~pwffh)!Gqk0bt8@j_WirkA?@WK(piJEXkOoyUJ*cL%nFgbSm1)5x6&<)kMF*aK zIssl&7ICU|Z)%X9J86o27fJFz_T zx!WZjTA$f&8yb1Iw%2*|$vp4qhxqsX-5o_)eJ<;s;Z@_XIe@x}P*I3hMmQyEQ{{cNlT0+y5 zW`)Klf7cXd{dkRZbRsoOz069V-31cq&*}3cth%PyufFM$76bD#O`X0f!Cw4*B1UtQ zE1~r{vG#*ngd43e69}o$I2{#eY4>1d^cF%Y6sW6QwAJyu)EjKHe}eEU8(lbE zsrhpd4_OFb(LB{5VAT+dzFZ;xnzonT3nwQrt!LTbYn49$QmlD0M`|)rwpe zhcdU|I-NYsYy9M)`*`?nef@`@7-pHI$@teAcWINavalOM%Sq=~iP_aI)6I>`6iEpLWf9IK%+ayA*n%{=$3C>IooY(keJByYf;C7`$)`0igA?WPz8)<#=ydRI+%G$PE@2t)p zM0FqrEg5R^kYt1Mbdl6j*m4|Ar}1%Ms!vlQAEA-^awDzU$Z1&7u@;ckGaYbrOaWwb zcDo0Qpz?ExSWm3K46;?hLfO<7uxqWW6SNss9hB7EIuaGl}~|TAgi~@KTG3 zqiW^rT#f3i+o;O+EPm2Y&IXSypwnx+^4(1wp5R!GVR5CyQGPBaU0AtNC{<6LzBo_F z=|=!)ozby^CWq7+iddv&g}zHd;50xg6Pg;N@~T~@0U94Y-&X;yp_FF+dCKpoX~;QJ zE`(p*(dt9i@z7sxIKG5edm;S#Hb8LyjUQ!}^DX;hIC*n8Ip&DowWl!gJ>8mc%Un~= zSEsK$h?T&1@C#a2f0HX6bD4MAk|ewV_n8>Vxp~E&54vM9vgDMy>)?B|ILszd zD9nBEf0DgSAkR~F^mxQ1yFzOHZ6{0&W)aHZ*)fj^X?`ip2mhEK^Zu^h!>i$>=B$eV zDO{Qq%g^jUh5#;6aMOT`WY43jykl9IonTqdnVp2 zzAEi+Z{gFM^*dqEb_Ypgtm#9cXHcPpzpYLAZfVl*?yR8O7~pWo_%4aM=;0*h!XpYV zENLT_9w_>iFEf-M3_3?xAMHQz;e)Q?HB>m?2VEr441R?D44%{#nlPPR^bV)ST-Fqv zDs8SgNQPc$D_T8$8NOuK#ra|CoB@htL_?sv4itZD<6<>$O6=#A@V7n^Map;3f>}M( zg7Gh(=?}CEY#p&LevV!$j)IzKH+`OwX7^nWbk)aLj1FL8#r1%Mk+%H>z7?-j_yPQx zi6EMB4qe(^EuZNv?u`cGRT84?o#&#r%_Z8pZ(diE!5Aj8w07wbMR|kR!ZEiI zzNE^@DHDMf>!C;sJXm1U9;#!EeEdbC`KY@B5GZW`#?^P&0C%9mDTZu-Se@3D>}~vQ zaaJxG=h$zTLYkxk`|VINcFo0nbIyFmXD#FA*QL4b_C17$tipz7+xgb<@}5lO7v>Zm zuO=z7NBB=4DE_iPn^O((AL$Y3xsWb;7RiutThC^ZQa6NgKiA75JmtN3yc|pDYJrDl zQ^sJV^+ji)@r|~=j+(QK{eA^UhhqXece@oal52O;9Na%4KY+B7lAHN=!7stcI2;K} zN6ki*K@i131yXHz6KBONN#y*|EOP#q5-pzJGA_Jiz68HmnP$QX zPe$>PKbjNVDGfZ)_&t13j!A&pWGWYEhY4+!0!@4&R-k6CKwqcZ0)0NmFHoKTDp2FD z|3`sj#E@V1S0Jf-k`}jZ4=m8ulQ&^SY-O9jQshzYkeoqQE$y;r<-M@nYYjPNuOlOE zXNQnI0k#Kj<#9V(16{X+jQ#)k;k#SK^@Q0xbbvl%*73vE(#soLRh*;o;#56ZUff4A zvAU}hLgK`n&%^^vM$g1J7|*-~9Iskp&gF~Ve?yhDZ`-t)-qH`1cEVCNnGrXRQwch@ zdBE0Dr?k)PW+JTDcGc}R6qBFW62t4|7)PIAV%%T^8`|NTmvzjE-FEX;fAtVqOWC7> z6gmMnbL_5MrK4r3)@@dZcy4sj++Do6U^wc>pAkYBz@^1>OKx390eN8!hUhu{ECb_~ zr?D+bvf8_;_4xneemKjSYK zKwe5=>+*B7YY%=jZI=q9&hG#e*9`#m7|$J)$$Rz%|pce)gw@kuKkF^(IbYV_h1{9#2q$`^%|&m zQJOc#0i78Gnl?x=Nf)J=A-s9HE_!2gpN=*pG;za4Cf8n{Pi(FjwPg5G=zSl!Kyk?k z5&L=CboxsuIyvS@E-1?8VhbIK#WYsTYb2y8 zYNUDFUG$7t^bRVDrr=D(T;ALb#fQw-yxTsKtC{p|(&WO&Y~QG8h8Ca*6rUzY`pjLS z=;gG*5jpb<+`sxNRdUAq(-G6ssWqG@TdMXq^UF9y9qb~0Xo|rzX^N~@rORM_Wo}mk zNh&TtBpSqr_TH}qM{|o~iMnd>TEfe=tH67i0J5x#P~ly6`q(>R5OSCrh5YQ~emLF&W3(6OeiN3aQu=b8=C% z$>nWml5sZ98d7U%XU^gq@oo8c?ETUzbp2C~G>)BW$@H4Ot(8RLsU(#W{@C3tGSn=Q zYFu45aTE>u?4RdhW*{sT0>84YcM?<(Rica^IJHX~-2# z;M+{0%Bq|qGEGTe#WR4pi|^QmT_(F|-pcQ3h8j>#yG(eTc1fCFc~5@1Kp-co#Vkg?5KrtwVcY*PfLI;hlp-7|$}PcO^=AGED zRm%JX5jqRVD%vXrJ>U}mgX~h_F22-Hy(i1{!_mOVH7oA75v#qVns(u07EdDBF0!Wr zP<4@goAQZXmty))hP0B{fHh5dbHeAM-Cax)U*-?C%Dggun32Xe^||l24^X=o-mhBi zfq+BSDW+jqI9vA@q0x*j)BF*6$5ECavw)`t+Pk=g#HsLu6m_4CYVr-tkYwraC@Ra# zCd$=ID)P*}he&Sg_b+XSvqn=tW!P*c;$PSQeW{>m>$MwN@o2uQm97o4`iI4D(wlZ2 zAZf9t?U2Icj7L;~7cn!g`bK@!gw zwOWU<#f#kM)qR!V@LnLi{!Ag}8^F99`;*sVC2*d4N9k3=*1JG3uk-11x4iIvNoh|C zKoi$lYWNzDsm&Ow3|F@c=by|jdsKzCrnVZ^Woyt~ZC$VGOFrvusE=mUq^R0{&hnOJ zTAlfEa4G8!F+(Ys>GTgTVQC}!%bWQl;s^tCqchT;*W2Y9FF}jjyL(wf zO1reHX5+<{gdS<>5{-lA<;SgR?KyIV(3$S>sCzu%9)<2P(LD;><7W4`-aSUR$6)uk z+&%ibM-TVNc8`nQ;{x|ccaO8&<23hZ;2w3{Bi=oZLpB5+bdNvW!??#z_t@+nYu)2p z_gL;83wZR2=AZHp&fH>gX)W4(KWCTtyoQFIR^^5O4lxd(oJh|&E9PwhR1ETh2V-L1&x$jj5l%k!l+WMK z=!^$l*<9yB>;FOMGauDJ^83xa1Uhf)rQ(K8j$93pxdlezWW>YfLr8G%M@(5kd z;>?4h^?-TuZMCWFXJ~081*?GN)zo-V6eQmHs@R_d0ES<>>GY<+`Q*xj*xNw8Nk+(J zFj}Zaf{Ml;(F!iY83NsqR(#n@N+2ZFNDxyfYv`^oR206H`q?rSaY(cb`A)goVwr6IXd z&4=waNjkV@3ip@6eLlC|oQV5g&Z0>su94q@`6+C08%6|4cm=q7bYWJUER0T=bP-;x}ZTfxpG2nj`InmLi67 zy!$D!{>_ztt#REiQ2gKm!ByUAIgxK?ICU1wK4mS}nx-eg%Q-vTnxXjEbgoD^QlfOk zoJS02tZsl5L_t1+k+-ep!1YtsLQaton_ubv`L&%7$~oweyf^!ju0FI;Rc%*UQD-KT zv@DA+|BL1*BO|k$9}OP)+ZPi`$vMz{ctUsnE=@qS1!H$2DA-KLML?OCk5 z^JHO5ixjlUDJ(At6usC)zZHvFLwb322WJocPTJPw+a{+a7Cv?O*t|_6rRD68Ehkv!9 zYRhzz)mJomyhHV_cnU~n?gdF(kRWKca#8OPWwn+Q6_>0G@DJeP2HT41;9Wx$wf3np z#Q#k(70~HzUwx?sRK;l*S9up|6sstBZ(%|oNmucP1~mgOV;1|lo?DU>?*N@kP|YT| zQwcbV#saSmNkVu5O9u~{*Fe#y>aT^_?M*vv7g^H;W-o-#jOGL($a{$!Ar9I9oe(oM zCIMO1@-JE-Ww7^CX*>{i@-GU()PiA_0jNssjGT{Vq!Dj{di_&u0oiQDvut&x)Wz%a zN3GL#t{E|`O%3Xz@z11=@YO(N@4B-$pPg&>hh}*GEIow0%<23{Pc@rJN13GOwu+KD zZ_wJ}&Zqn!q&RLv`c1yCqNcx{EtewZ@`>@{pKpCdx8Ph3BQcs$7hWWzqbFNy*(E;~ zuY50VQI*k$2?5Jzj4JQlKK4*^@nphGi6SDmbHXP(7vTYQ7q0opC^N3Ef39hp_uCi@ zl|f9S>149S3&Xa`=#DnMHp_67St8|ud6po$$f33BTZsuGHJb4sa+Xwb)^E1|Y$mdD zPuFZ*C)n0=RdXh?ME=3WMO#EG%~bxq8d0+ob*5&~euYyA*m&!K*^j013msYC-p zD@-L$<|8-K!QS$HWHWWSlmKg>@cmX1joT^=`iqwH5zJ~bXto?JU;Bd!ntikt3*5}Z z?%SSvw3oBwW*!L?zen#471qEEG?LxDI_#1YBSjxex;d#SLSf*@TA~p^c($6 z&TUbao4GjR4)H>rM7$y2*)#U$1Ix`|`}wykmHm;?)9O*+bq* z7ONBOR}g0sz${Z9w}#J!X!SX>Q&{24@0BX*r zK^S}cg(jN);Bwmyn%IR&7w;9k^bxtyMkcp+k?A(Fqc`0}E>>@i#-HXQ+u6tt-WV78 z3Xw9@|4+9#=aW?Sf#~8!S~Uj^YA03H9(BwUJHR6$@kDQqD9i9Buw8|+UfmGhBtc&< z=xE~FWtQF@FR0sQdiy=6Aqx>JcRVmpCJ{=|LEw=q;lGRfndg4eN05(2{mejpL=y`g zqP4g}poxU*4RD#qcDgNq2Y9l*OOj>%Ov2B6&=)s+tEV%9c!B;({WSy)E2tVl4k1}q z`w|Exm7JIE52%8>{W0UtP0t1w?75E1z7ypTV{Way3zbbY`Ol@ch!22B-T?9ctjN)p zhElKH#kwPMsXKbU-Xcrf)>zV8y^s^m7YS0!D{C`yzCfz@$7>pkE!bIQ{bWn%Y&P6j zKS(Wky>)9LTu_=Xyag|VxCPHw_?GcDPSR%YUT*|ors*znW$DX&TJ^kRb%MR;B)73o z+fpBYb=1UX=3|iJ)~zZ{G7FyE4$$t`_#% zLfw<(C8mn#CFVz!;0r8|HM@l4d)0E%Nzrn`&C`1rS!|a}zxZqKTChXw>vl;2&f+EA zVN?OB2LtQmY0Ay|?z`r6hk+K% zp5%d4grEgv?wfvTu?4%n5D@OCEIHe9*1g_}iR_?p(|qGKg*%THEt(}xgQym|ne(H7N-vEvl`cbM4V|A@_1>^jA^BvzWs zrvh z!74t{p(%Njy~9WrjhhA{Gmglr;(7XS0BhlBrz9v!^*UX33N{o-yXM{hjy$s>^3nf} zJgFiw|Aa`-4A*@wo6YbOf^BXqiE_DhB~m~gc=~t$9r=}@$T-zwBdQ~(@K~+Ofdagp)t1b~svv&@|lfe**)bnmhwb##$)h3mB3v~Y~?4=diFCwx>nsMqQ7Rpr*R~IL$1coYEw5Foz;rY4gw(c#zYdF)@G5Y?8%F%D z2P(m{*qFangFl-Ve`qiT zVGHcHGSsBtkYBe1v+qxJ*~I_Mtu%$%%7rG9jrTunO<`)W&f}mD7?|`p&c*)Z4||19 zf{dgVq2s~{s6c`tz-%GfecH3g!^r|4esyQGjLSc}!hhe#09Ezh_Zr`32Zb=Lf%i7D z=oE`;wvT5txp9ddQj0A^?&#gL!wspI_6v$dHY{lR0f-6@M-Q%Ve+`;PkB zj6XS4co&+I4m4AJL_5OQbNWy%G;+J<{V&VQ-lZVtxVQz+xjCzMEbcKsx4VTCk%ehN zlhr7jMfUrEd*@}lCD>_}Zo7F~{>XH(O>K9DmuOR3%(|6!TSEt}3r1p70>z(z#eBif ziPOOg3jRhpP(%6Ws0%I>?r8RyBC7itCSe;99qVtFO9#_PpV90e@~NlWp0T=|aYrqe7;9s#mr{O*Zw3Y;)NoN(+^!w@pBIpXDh8=LF&CIxSy#71;v{uZ~$ZtXmx!? zl9@(e2nC}|KhezV%T{3VKP>EG2P%bt#u#E_bt>s@BJ)_rTI&vCZW6F9HsYjPmW##H zaDifu-~oHRgGFhnicJzJsx&@gJHP+nnJ0MgM{{F9ak1ru#gsLN}%{SC>RvwAL~q?EWGQFk4EN!_C#3U}Y` z6af9bV7R5bVeZBR)5K-5`&LSK?^DR;ILw;N`yF#_N$0HfOFBha$!=u1*WumRw3I)N z8CWMOqZ$1kZY<8&>0K5Owlu|}QLHPP_;Hv7k=xq@WkglKv1&)e;6%gstC1V7$#C8d z7xwbPmvQ&OJ?T<*Y$s0?n*+dPviV_5U%&$~ckVpPUaorjH2DCKr2G9Q1j-+UJDR-V zdzz!q8Da?k>n`a-r-SN%cTLl}+Mo_^0;R4sc!9*)4MS1B$D1=EV3(iLOTI}&*;W>_ z0hep%$NgM8c*B+J4KCMfl+iq*wIhg3XZeE>e(OWYrj9J;>YO8=~{4a<<)Za+v zReLQ$2QNWL8(5^Rzj3j%T&z_onTGnW){h171TqM5hIeJDlb+EW-G~PNiN;Oom=;qGZ`k7uaO&DTUUw=aNi3yNFmutzF8PnS5AR3$0(D zW1$}fn!Ret0p9)%WVDP1^ES6(xG-fsKDz-8w9LG(zy@UJCX8o~w6%k<_}4SVexx19 z)`oW&u-L)%?zhvwJHN}&o}Eze46t=-OgN^MsO8j zucA+$O9Y}_dxx#y5fvuWn+`UHT4SY>nOOO1$9M9_S&KI~wNFLAHkSDucaPc^s@AgGy3FT~ zP_1?GE+=!dF_Yd{>~PnM;a)cf5`@sT3-i-n52l z(IHE`Us~H@-+9mo}UoRycY*fpL8Mjtn5wGWDEH+^c;x!qkXqCC67G! zC1EUe0!6d=HYf9=dvTZwXj!2#(=1$t$*6e{jM^cW{ApXyzdoy}!lTJgy;dndZ(gMp zU`Oxosdfd}C@<15%`5;Poz}UyKc1~RNK2!8r<(bd5Ob2KMzTpUzlLm~hIOZR ziByx>jes?|VXO+vz4;k9kLgNWPGDYMwY=!P)rwZ+pVcEUZ&SAeJ%(0G4HRA1Tiu|v z8UTT!6$L+=xk2#1&kqWLuP;|=yHVN=GRSy%m_8?_rN$($+piFH(7cHl`?8vsTuvQs z803Ru;Uhwm^0=EmT$UGJE1Q$_3d4usrSzd~=u=P_nhS(KCR6FO?bDYyYYup)g>tDn2rZHVxqO%@{IG4l^JTU^()3>8%h@FJNp@_@*|ET^Pc79k zyDU^}GbV`CkK-!S<|^SyUh|GE>t>21rTEd+HSt%tMrhrwxkVQP=gk?5&aV}3F>9MT z5%}kgG_qxzWyEMQWf$IDK7z!t+c@@))<^t~a%N1m%$^%u%PdiNw8?NX|CDoJB+d(O zhni2UVk={=r3QP}BpAsGGiDUf^!gF8{h?adsaNhH!Bw}a<1F{uvsWyQH$$G~5Opb}iZy8Wln-n$DY=0|Jt#dDz0K-2}2fLXW zU>OXVv32cS|2_~p&dw?{A;)gq+aL4sSV*k${S@EgLeKFOTS|ZqyoK>aW26dyG~*St zUOMyD+3T{B@wlIM&#{gdo%hAIO3FQeZ(86-t%~ek5_t6lT`K4SI0`KM^;Rvn&tOXX z(t)n1n9e><6wzh9gW+8v&NqjThQj;ikAf0sABb7?LXrvI`}{UG1ETQ-pm29%@k7nG%go-Jp-~Jp+T?Un21-^-MH-A;f~{T_MZ5}u z!)pr_<{wG}W7}^FmE6ctI^N179lb8!)^x+M?=5v@-QyO++;Hn3c+CqBOtm&;-j-4D(PKsG z&GFy(Kyp7>(JpV@j^ucELMU2iK``8x;{kMyD$x%TUD2{feIO`~0*fsFP@|;?K^~7z5ZIvDprOiZARP>QsQ~{NA|9#sD zpFi_i(v=!OzEI{G?n&O}>lm=5 zmKrq}xBnanl`cGp%pRyWcP3yatdYW2zd2CIR2*Jt?jisfbVKE4tc|Q_z29zic@0!B zdFGuMy;9MYb+&*{m|G*t!iX%UHPyAd@T>y#G(fro#Z&{=iB)>_69_3Ju96VZa9NB7vcJjptg*q!6Usd|@`PU}7ne1_P#`! zX+B(M3s=!w7MwV?-?yJ*urIO@Zj#1fZ=8AZM8Ny)CpN)Zozm{;&9b3=ZDL{ECsjANk z6#tBKaifF*m|+%E9d7NxKF>D~bG})8o%Le6qeSyNnU1{9r)m2iD~M8WBzjmam&Pav zsV2e8xfWJ=8(=d3sj%68<{{GcI+Ll;=B|rctODc( z0aYxW&8wm#nmuGF`l&ysBFbwC#j2VTB>)){^$V{ieiHjLc6Uv7h!vN|Xltb%RkrC- z+G6y70C8qK#eG3tw%aAx=G%g(ve=2T6TDlLDFn^rGo(g-);S*b33AgqNDb5mk)V}0 zc&g@)6z}$Pli*)WPyC;^u&g-={x4XI$h%_cNm|R@Ig4Xgl%8P)8tW*ZmsZ*ex9hPE zX8)5m`~N?-1h7%rGZqErEh>ARZTQK7`!h(0u$R#Cbgmldl+blLMBy4>B0QSh zlCvj>j?u)kYT_aR(%Br1e*#=o#S*FlBQO%18qcH7J3jf&MDy)z*NvC)%sEgaNg001 zyBYDEBG#C#d@#@SBn6$e-wQTj;QkzJlC|?4lITIZCuuk=NnJUK+ z-m_k$2iCpngmGlnC~)-Y6|1SiZu2611KN-NsiDG?SWOL0tyoNj2O)3wM2ol(mYEZ@ zmvN(G%pKXiLvD-hA+(Hv;^dd9$;_9inGN1}W7m5#`7)356O>IX#;qdInm3|*;7SW_ z?3?f%jVWYN|I-Ny37y0{Z!jrgvmd$A%c%+|e5B0(^$Rt#2qqL&i1QMQcM(5#OMQb@=gSH-=B2!W;egc8B+LOJ7It zaqyyq*g#}S8|w)sdo6b|d5zc;lC)Dogr4(wQt$zPAy-Eb7$yg+^=!Pb?ZTYG?T2#; z7dGk3`y;+{N_(brI|{N~@kZ$& z_;WnnyK_{nIP;-Rg4NkxZ+-Jqk^Jnb*48jc?}`8%6|Cp@Sg-K04(GcHwzBs{LZ%Lju!9q97X{;1umevAalF9jt3Yr{XzB5&SQ$j_^Se7Hr+b(yPkHwTuHx45 zq>hnX>^?S?U&kyWj4|}V({>Eiw*+K*Yrb$hBrg$*p7x*~3r*(_?PC=W<(%=6T2@=h z=kfM$#HT7YnmEWIuID43;3GZ^4wyto7rW#E$0UaOv6~eeiLdd3b%v3h_7?l5(Ts*q zo*w6Q0HjBFyXg%2oaJ#Bn8v*H!=X^yHOQ1G1#Y7I?Uwy?E;CLaWT_z>KCtGu#b1gf zb^?|TLLhpN5AWPYZUnu^2Q&RMJgWM(`rRc!pYo`ZOcc~Y(`Jrn&zSwYMr~)d$%8T$ zDn3H6Gn$AdUi%+gonKQfX<++tz9!Sl`v(KnPc2lwtW!eH=3}J_&(+~13F%H0m}N5{ zZMO^*zpTuhK9!wLw)F2Jz&krxQqOP>&h5diRiG$JC-PeVR)>LHY7Y*KQ?laCleq9a zc`IU5?3(ezVRihD-VW}LWhrqT<+y~+A_=Eg9g(!DJ@NcY>;>#F1C&~?adV&y2B<_C zd*CEgo+D8A%5Vg6iA`yDW>r3f4p2j84nN)_-(oa_iu`7TOlNQK1Ho184a^Dx=ph%+ zq^{Uxv09;M#;H&!Hh$(&YAa&XSkoITOcn86AuZRQD}>icR(0EnO$eZUUQyXG4!#VI zdvJUWnUh^JWm{^IUoV^HTeed8sXD^sB=4{$U0J2% z;Q8wmtWt(!kC;jhRY5?Aw}1%^Fo z>-LrDZgk)Ksjb^D6p8UeI|*=@2dNPbi!4P8Knss74LZ33u*+zPpI< z((5zp_7REE?0rvI{BwQ$O~7xibVR-Zkt`}G{guS3{yKQy9->FJCmxR0#)93-QOUjL z4o7Vw1HRjk#x_JpJ)A$#{hz5H zb@oo8fi)Q(qrGyi>#ry$qjRI9WFW!P2cO-0yzEFV(d>VfH^k0^18Uq|P_&w4dQav3 zd)~dpS2{DBb&80@Zywu{M+fhet97vzHB*d$nS zrL129pfxsvqFdyW%0Cz*45?!H`D3IBNzaq;XQ)W6hXcjicoPWXr&|=8Z*54$XUqeT%S$(^t!S+;v$r5Y|NKU3r3$!dmkwXtMHNA&b798H-iIH+to%?RWi=@oCJ&M-?>bRP79lj*Q#JxM8OP;be$mPw8yqj1w$HZ9oeu6j z!NF)*)Z)d&8~n1}2Pl5l4ot0espz1$xVGk2D}@wIB?=qvq}*s%u3+J0xjouzo>)v3 z!gr89Pr~G!oj$XeAj+}p5nIeliX0ogc#iGBuLwTctC>yR}TWcdmx}}uGmXbu*G3B+(NjNZ@QF+ zcaU-z;qqypbD;1oDyv4I=s(Is)^SX=-m;J^whu8kac^p%_+Fbo5hXJB@S*y7tNH-N zn5=oKh@Y6&uSA2@Vfe_8D{JK9x{9PhIHu$bh-UM@-|7)w*(3Z_&Z5J$dWIMG3@=%9 zJU*vz$K;&CooD7OIvQWNJ0P4LauyvrrDwG@IfXxUQB2|XGdX^r!-+BN`HawYwxfDL zWduftI1v2j`&S*v>P%2k~{pnQO4jF;bENWUf+TQ{~$Iid5ESm90Vk`XPEz>=qun!7@ zUZj=7JJ6<*#N1bzb|yg;^Xo=ms&IC#CWYJArEsVQwGoFoQ+~vDwKj2B|6ys41MF-w z+Xg)iO7GbFl4M!h<%f^}j;oV}@$sFp`aB6**+0W~5fs9|dHLI@#7Q6q!Rta0{sl0z z!S?O@|Ikq0=AFrkpzQtFYRtDWeM3E($QN^&h_$-bj?Ika-qu&8J;V~#^O!fxeFBRn z*S4HKyQj^^Eek_EzB5L2_{>$D(-tGHQwn#+mE)|gG^n#;qO7Y zSa6ZWDZ!JI9a^0SiZ8OQQPJwn?PtZh_&$pjKnYu!brMk?vql zc&S&DZE&QJB=6>$YM37d@jcxmXX-y5X#&&w(c7G`JLOasxEq*?B|~Hq>~*whHetC| zsk;~=sCo!DrV$|J-=bCsc0`G!s^mCM;F~d%fc0)^@k37W{>Q z@j4GyA-6nW8|U&1G=#PMD0zp$7$H&?ib?sOd0R)hqln`Pv#UY2Q|)TdKTCME@NwuL zV*t!&tW}0y^lt?RdF#eum@v5wWj|Va|4gcaIVCQiHB=XEUdDS(L&wVlSF*RzJ(UPB z1?p7~)a#y9+C80Bd)&|^v9V`&DZbvNX0?9cEEh}oS(PG@_u|ajNFUN~aUn8L+6DQP z*q8ZGB;mw^|3w~lE*bQO0Y=9QlW-i!6;L0PX| z0`7|K6t0$p|C;*%$(1djmZ(>@F{!|WBA1b5m6(^3oLfDfs&it?R%o^J=`MSz z3pMJsVu_xk%dtkV`Za#NL$ zJ>Q=kRH7m;YCioouIWajWuHaOk#{AT}+ z@-&O`2zHtp;@$coW>^M%lCUD}+^Y7uA*edq>-`kbNEUzoX?I050*tOtBzkE@^irZ( zL7aCLD~M?Fak95FyVln%9{%ZiygbskA@>2XDno<76d(s6O{qaI>2-CbrK;#$UE>_h zSVd}Lm++u||9H)|vsrmirB2M*%t|6s&7PQJX-OdR!4||#i3Ov$*w}nc7(NnZXSLUR zEDqlEsfEh;u|AtAL5rsK^LpQZh(QN)4-_v|C`Tp#nxggVsjou$GofM*SDTTTfb57blBVh z6HgrLq^J2b4S0wB7xD1^Gts~F=CIov8V?oHKCXMsDSv+zs9Z10Wz&HpeDv+C zMs%!n2?GjN+AY%QEw9;*c=|6}mdm^pUTI{Snk~FiRA<`B>zROil&4AH^g5aX-_HXM zRX5`}zjsjMKAaaPEqn#Dbmx{hP39UzG6a{oRtrr%rkSE(ni(mQ4h52JQLo^m0!@AE zogsEqsb^7nL4AjfQ&227)Vvs1>)Mq@s;1MF7A4<$w-vCvjH$)?4-oBrI|VK2w)KZ2G-1^iX{&34YI;YGA<5O#d{#YJUQVq_(KCiDq=&=WlmQhm{G8lrNY*=s?^D z)T!q9=T(A=y>FWNVOt&2%}=mVSKHN+{PHNUoP5zCJ2gqTRO8>4`%rw z+dj8d6A92izYf4a&O|-nbNhowNwK-ghK<=uh?hvsn$3Ev&YRQhd`VIq33yXq=OF7w z;xxZAw`*tqK|USSOl5EtAgCWx2xW>T!pkMZ0sN43I&oeW0o9iH<`e56*ev76fea9i z(;SZZgj(eaQDRSjKW$NeL9ll{UBY~1tD$FVUi8*9b2`CV(HywXt#*sMGFj2^FXF-+ z_N%$Vdb;sG-|6NCwq22Bk{nd4ODvT49FeiHxsd^q%hgXs&q_#O61-0jU6$VEHYayJF5=@TsR<6)XhHJ-6zxUR^bVT2@iptvkRfhJs?gQd zr>l4~v};#@qBYbySOS5)?uS86`i;17*2MGm4w3E1kg`u@D*oM1t2FkcqeOcUVOJ~$ zBYoaSiL}K-uVMiMt4viGr$dS!0)uxKp$_ONfF!qv}2)>X`{^wMm4gGsI2S&eh64uzvZr z=tCb+9sIi-so=tWC<`poXOg}$>1x#hqYDvbVSav^4Z$gfo6k@SPcgSzcL?0UYV{VHdf|)gIPcJh%N2|o)%{{CA%|^{BJ;OidwD>V+%Hh;NkwjhIY6Xf? zRROk%qmky^A4)oXvMPO^*C|{;|(Da27jp58Ny37Olk&l8`XuVvR}GQnb@GbcA!|-j49Y00SOrTpF)p20}!)c zIueH5!>j`BXiJu$V7VvVNKDW2M>||jCo$F5&?H#JN_O8hl6ttQ&AKtHPpo-YH*yBROHhRbw^$^BJ zzt~1aNDL`+|D;eJI&sFr2rDM#wfIYVq(Ob77rhxe@3NFowF9B*w_(P_+QvKoWwtA1 zo}+=kjLx8PjmWJ&h%Jp_X@mMiJH107y_aQXUJC?CL{0ji5D)S`=qZ6zr@O16c{7t? zx>c4|)=k(rvDRplgZH$8vMmKo^TJ!~BpxWfg_tVdam>~Bo9-8^a*{4k^otFa2b8JD zkC)S#7xGeG&OM3K7D5<viqkh^yvpZ0k7_$klEw)g*xBf1C?{P~n`DV(eO*qb<+YM3%%^ON{_7SY%sl zGHdFU87I!1&ns(6(=a~ZABCH83Xz4!P6`xXQm*lY-aN-HGz0T4uNMApI!czp9owcH z?uK!|$!sD1%8yRcw@;Ac!^Jg9yZr-Vt`-DyFggL3$f$~C9yr1_sU0rfYu%6v>4Af^ zU>gD3&;)Qpey(~YrM`>NW>Ya{`$QW?VHb8WKFGVIn}(Pcc^bmnS{NY*hWQtE3lDpY zJw1|h;KNLYbUTwUo!&r~FQsp)682UP#G&suBPnhWEeGmhCTE962+Kb4ooZWk%b>cd zoZmv>tz}JE2JqzA{HVKZ{cb&5nn~7&6Q9xeV!@t&wlB$@MKa&I0Tngl$!wxdg=Jx- z$`{c*P;`(z*s>LPu7O0kYcuZySmlZk^E}^Np>}A!){*hoe8{{jDQQwbb zEQk#ykt)R`$~X>Kul=gmuGf0KilUf+5K+WlQ9w~Sn^07gB0}Exx6Vuw|L;9MnmN0- zwbx#4uib+_;wI7E+lHQC4a)S&&j-X36W_|EXDHfU#Z-R>WoiVV;5hlr%#A$Cj{-UP zegLCK$0Hip&i;x5WXBapHh4e&tA#{;V*?drScSrwQ(wce)VYh#ht#c5p2=rqq9j$BPKEedL>ubS9$=@sI7G4ipUR%bxpW z40{eg0N2@aU2?u?AJ`;xD;rKhU04(IP)(2WtfE9h~t9I8^- z-Ay^eEZ!BrK`y9PcNrjG67Q?)MI|irub&whocE%EL1{Mwfs9US)C6|oCS7|fYlMGY z4=PFw+O*K-@DssK0{Trv96&~TS!Z?Q57(AopBgqV6Vy4}Vszl!Ss&&}+NA@D3rA|* z@3bH1KX^4nEJ$zFQ~nONFS1cFPfSvW+1n5%Ky%NN!$gFmLy^z92eGiL8E({-wF(1u z+y<;GYh9ax9;fb85O{6vrHGRCSvFbe=7_vSB5IhT3dN1qN98FB+muF8T@*FqZ&7Xi zhrT%vB_?zgqtkp_v$(w1ET2~#&rq-zj`AMNqsPh0z|H3;?#tU}90$53%GKaRB;N`Z ztX?2sAM9+b*TY!+bT!AKeei7i;~{OhNyQ9N$uk&9<^gHZ5~ridQ!c z;Akvj9B1KSh8{#CCh)__^qTj!S;WmkQ>R)mA&L;cn@LPKb}hQ+6aUtad0n(K_qu8E z-^=+$eGaDP90|wDvb~A=i^K-s3$D_BT9$EJ7%7A?>&e z`Qz2@V)!Zl&@SLmW;qi!{!bCtn4(%jI1bmgQ+^;Ep9g12rirn2|C+FWzwzsCH1YYa zIw& zU#sH$nzAE{Vk2?F_Gk0J(od+7dS3JEd0X}LpdL->bdvOYA;0@pQE7VVR0QR*-0mVN zD%(+z&4D7VBd%N4*yUe5&I96@)7Z#BWXG~-UEMo2I-i*;j%MK4b$VC#g7PHjy+IiF zT;0{(ueez2IoAks<6daqO!d+9MW6W(S%2z}Jk{^MiMkSJO|&D1Jmyi?!9?r=1aaH6y*K z#fIu1(pzk3@n^95hqT`U-8a151ezk);*;Nkf$pn*3ye>F3#$3Iz_@CCzs~nio+)ha z?e2cKu3}>#@(VzENnmVhUBC88ZYu$@rrt-kU~EM)HP!OyvF}FdmOc!jX2=LZfiFth zO-zE7bw5}j{&d5;%IhzId&ItdU%GSo<_$UoV%~%uqI~cx6F>Q9_f=9lNp2XQa6=DV zZ3o;Nhkbe5&f86I{T~uR>3jI`y3ZCeS?$)GL`Pz|m-_|YNdeQK13S%ogCcI7)z;~3 zKXJDbt8_+Q`H2~Z+;%dJaWTn#z(+U|PIoSk53XH)bTbV2xa7 zq9eZqS4af0HduGUy}^C>+w8k!i zlH||A$vaAte>X?bw+yBG-98RQU&&cSeZ>)!Mjv_wc@TuW5dAr+V2og3&HN!Fz`?lJ-rd%*dadULb)1e!+9!|8UO+OG2Y zZ&RzjUMtTE%;pjnC=vWCsyoa5OR+Vw7%_khNA1yYX+9Z4OAnUVRBI{q^+~Ie)FjLK$Pn%vZT1 zAnt9F`}gKnNtfIjo{CHiET3Jzzxl!t*kQ3z9%{ZY zgNB!rWq7nBhHV{&MLKS;)r@+d_Q16&l~K=vI-d+!*6*Z)?ZT0XJCjPLva^Ea2f)nVDGLUSS6HoR zR?7b(7C8g4b>5gpZ3UKYvDkfHA+cVEzK81sLQ_9DCaET=6|?JhjL>dGoYzrvFXHmL znJmRrv`J{>Y%&fvUwAcL-tdGbBZEsu?Lv```--OqX_$$cg{J6b(uPHQR)-?phDBOc z+pA^u(^SMX-A0B+(-fL|h9+m>c*x-ENGguztS*k67>W#7?N&>aBAqK1{WpW;LMSY2 zZ?hOI=Vn{XfSlSlwZ*0ra*t9#{!!G~c*P5zgdX&Ym0C(FBAdQRfu;W$$6iRffZ?h7 zqMSUhyVb^*8%Nsdft5ukB^O19WJG6Wke(IH|7GE|!B`=Z!3kOYJ|U`O=$=sQ>a5~e zMy2LUwPX*EwQ3hm-aa&X+|cN$K}3DYMUnmce#ICenEzqmhJRiLMTup0pG(gr`nQD?bRB>9Pgv*MZ0Sv)2cFTM{WlyVvtu z_Z1Q?K%~<>$971%BpB$kc~-#9rOM?=&)ynzdxaVzzxy4!ZGuq7_LQ?Z=t}T{e?#PA z@#i06&`9U;ZqqT@&6vh8I9cP4jy*!cIru zPg{{yh(O`K!`1tnhRdr`!;AMzv^ne4;uzPrBE4QFZ`eFnNc2z~38lFqQ<=J0m#-dG zR~f+Y=zzb9Bc}|D4EoFcmvi-P$-lkvJHc#VyH)NX;)){G)h=W4_dI}^RDNK3U|F}T zAl6P1pj2-gAWWC-dJz@G^%*cZ0Kcw7Bku+mZ%In}Wnpmf{-mUbo6Uk!@G@S%oI*w2 zs)L{YR0JYV4^)PyQ*QD1e$Hx0uB34F_t~;#On1%CgGiD2-1dy%;vdvHB%GS3%>Qkk zD8OINGP1oMoJKyeMGPh$1fkO=Nt7mj4) zoE^zoSK9@UC}OcX8eA0RUy;))ux#dd*5!sc{H;1s4NW%CTYA6R079Q5vUuufmRIDa zS#Yw1cM~h2c*mmrQq^-W!0leAvdFv4OBb8k{fup*=5e{^He9`BfP^yHZW3`exSSks zxN67KlKUqkW~=xk&bL@b>$(Cx7ERt05_uzi}d`=oht}j8Ub|(MAAdmyR&Us%T8OoO$)W0^t#2nwOETkB&)z6JJbTZ zN^-lADp&4OKqf6q@;?uh|BJ!68ipE-91Jer>M!-dq@)*{Eqo!kn2#Av1Is#4N^b3# zAH*YpWk(Qp?XU2=kXIBz?sIMzP2r#%*YOh0{h~m6!SwbBF@Lg&*lk(*C9-_T&aq`H z0VNBc2bO(sK%;HF>EQ%w>ODL%pxUh@+C4ayh6Fy$+RmK#3|~M>5pv}$CLxRCnNvgx zB54(q7-?U=b=EYvd+1T3Mssq8UcRs8(7?(oIwnhqer9Zb?~mL*4JFp;d|E#%+Uor< z#{fX~zH3J#kVzrp&yHsNToO6@?8u;>U0aJT=x5O#kxLw_^VGE=ntLdE*uec!%^r>Q zI`U#85$9_+u?T@u*l^3yna@Xs(QL%%unv3)^bzZG4uEB#t^hv%C}1T{RyoTY{K?!6VaP zUq(;%4F_IUj##H*7ugU>?eeZXk>7U{Be?V$=&=vB=+rdTU1iT}jlv#IJP7bKztF7F z{)@ktQUs1H%bPsirsb4(5k1G>_K3gh5sF-%=FPVXWkQ)tM%RSOn4g2{NX-z_{b|^Z z-g3ikOyG``GRs|{rX!bQ3`Q-rw~ANXNT)y^PfcyO&2oCO&%eDhz`xD%r}|yxPKN;T z4sF(WQX@~txr-M|0rT}DP0ClKF+)kE$Q|a5NGiKxues^UE*l@J-hga=PY7jG>Gh%5 zOkT1+?7w8agmP}B3hUs9PWJlcnI-I!@l=Z;(QKbsg=u|8a#!5)aD$0As9$$^%22-I zUBPeZgFMy)q1$@Tg?dF6Xq??i;_-8k8RUDL))+eyrkRzfWs_V9U-He_t4wW(I*%2M z?Tbq6q6q5gHLf-BaF)&Z@v1jbc&BUPM;%n7vCLa&hDJufdP|e^T&4J@^N?G``MK*X z*U5gaw~ZM-)w?N?E0)^PPg!FrdES+l;xcDhvAPSa@|N++NBD`iSz_uBe&XfvlKuU} zQYH51Mp{2+=U^R%>@_0RSf@$oQQm=j{z_uCnxT1`t=b(Rk#&AW)2T?7MB6JcNXJ|* z^_nYa9eu>Tq4XqoriJ2#MVaNX9fB*ZmE=c_O_BwrK%H_8w(K)>myR$R+ixX!k@gg1`EDt0}JM$TDou0;Y`l@oT&C~1@oNRb3^FDFavE1*+ z`Ub{>y~(S6c9>lBZOeQlF_#sl#H~k1qr0C55jqI5jkE>GK=B+3mb^9dYG;m2ywVsVa}S|QmZ~UNGEFF7UP#K#;>dQ;-|*-!O^XA z)5$}Bo9m~}oybUurBAu-C8~z3Z^xX;%+p0JiyeogCz0;$6NjwR)HSsUutbpyhmVdY z$RB(=yUOx4#cMZ7Jaa60nW|dPo3#;{J%&!j(FVs3oYpV!J`=7l^YGey1iBnLQ<+i#k-Mi|1?>!ted1^UH{8C zByq=JZ$mZ0zZ%PqK(RR5q&UKho~U?=BTZzzcrC2rmq<#@CV`ut^>2jh_+0!>9+YTt zWPc+-Y~=adok`znh9>py4c#g<+_=2_xTW(ZPScVoZ0i8p+=b`vQ_9Xk&ZA&7~nM6b#mA{h(DzX+H`u}U;#e=kP z+y7S!SG2Eh;rmPd7Fr})_+?^43v7d|aDK<%0u|qBM3LX*YDmXS*-x&|A}~u+Mk|T} z%sksfz{e2lz7YJx3a(o!PTq{HGs88|=DzezCnqcs`fgwPo_!O!eK|JjuD<6rEPUP1 zaSJH1oDE)RYUhN?du3hK8}r_)i=&;6<_lm<<43~(+6iV1F}l&qoV5~Z{1Ovsn=f_4 zO-S6nCm8nSE^`4ORA2BP+4muNeJo z^3{GQ%y)JxX`OSwl!_>Nnw&2rVK8;0wqLJjCTRM(PZnNeig8qV_Fnii!m$5a0FkqV zjGQNnl`)pv;ZLbJ^S%3&bR|h{nx$us_tUrI1zZxuQ@z>BG0bnJh{W2*)t6OASYL+y ztuIx6u45B@d26Bi5~z5K_}alVp5j-&;(*_HCsqBh-|qM4S--BWZ#PMa<$jIpmFN%X z09f1m@p{rOCCyuO4rcIYu=1$TjvNb_)raqu@3Zw;%6;uXhojCl3h=zu!gA{)%vQbW zx%>ELfs9$Ao7P~|QY9yp`0lTmf~GmpITW*C!fxGV5p4YCRMjmCH|QD0TG*WPm@a5r z^zV%=BiyL5eipRs(j2X)YmM5zTESTB=y;o@ViO4Gd*9BtgFW+B9sjag*s#n{bqW!j zACK?@FS?XMuh*@+Z=jwuWbIF;v`iVUdehFkT5{PfmaA@4lNlUV?c9ZYQ*?;mUe9=m zEBy9eC6W0)4R^&*m`~mjr!Zerd1Xj)fF~d} zIIiEi3H?4X>&Z#3WtSF76I}3fwnMC-{czTLTFZKJzp~HmY5{d}*|Drs|?al`pNF5fD~P6=x`)8A>u{Fa`sz1!FS$&3DO zvq$AZXpz;PJM1~xKgHik`OxiRJl>aYr^bDlntn8TUY5%zzLB*My$ev@Z%WmgGA(AI&mj_rrGKA+E{~ycftQmhUEDl%w<~~x=Sn9o8J<`2CQB}u>nCp|SyyH5P}1NBHM_bQovW|tHT2>`X(CUwpmhJ3)!OBiLSjmy zoIxhx23(sG5nPeV)VmS)=+k_Z4&`%kUE%3uK4xRMe059LpBSBX;}SIZ;Csnk{$Ue$ z_ruKH3J#R=ycU7V;(r4|T<%Kp?tyAyJc}V$-%tbqyssw&g6=-ruRxweW!+B_QhBpbOY?e80D#0A?xcQUyE^CfDIIq9k<1m%~W0BKo3V*F4a zs83LVwhO@8(~4zA{K{8Rxn}I;SupJ)S3gqy`l!(dbZg^$dEU+BQ|kAm!eyixA3FC5 zW<%PelE}W2$S!VNh9cj=D7&AYRA(_!sNWbViZN+MT0AWj*M_ov*8mqXV}fwOE>dqcHgG>dkG6!QO7}3Ah2}p0jZLSJcIGTeG@%Pbxb~a0GvgiN8_kXcxa#5n^$G&eOBh zEdVPJWk+1U3fyyP%Xu<6f9~msnA*jwlarJzaAy?rHd5_t;fd!eO&dNO347yxqCIvq`&()Di_E@B0fHffKidCzAdBR6>M zL{3~2F^Bs^xE)EbIC_h<6I{_EX;D(RdTWbtY)%e#1xE)L{h1xOX#jmF2|W5~fCsL{ zU(_Vm%A<>PKd)*gSrVLou*I199s=|PjJo6fxlE-O-iqXS*4j^*_71!Xu8Dd)!}qdr zlV&%TyZ>lEdTBhmiS5+g-jS8ePlWft<6wyhuGd^6i}mWN?;NK6K8+Q~dFf?VllSpt zddrhJ+o5en)3sb1dE)7>hE8@cY-BQ5cO8I(m)_xU4>y6?Oatz-tSs`^hqG{>rwPNc zODy+KBLG`Jou(Vr^!>iQg51-*lW5+pRcTyzeqLrU0wBEJk#$~6(1np}={N;&jFzgO zuG@r6W2tQ(!x3{7>pU#S%lf-pG^MeEXU|S}!Fh=Tix#z6B@0;f5(G4a)N6E2to++BYOG}jJ4Zg;R^Pit1F?uE^ zd^2~~%X}OC3Y}>tR-&es3mLoK|5cg}-{(a#bk* zmr$VO7jI@&GnouLYPO0-!$-?NoEKA1%P?mP&~bgz=}StFgTkbyMr;bBeuZ9z@$d2X zy4QvBf2HnUy`!z}K;;nesL)xmd(^d0ovBTI!d(I8Fntp&Y#$`6{g7wYKL9N?L;HLTA7%KJq{C$(DB+3z)yJy?Kw^ zbzFA*$@>xM?iiK30XYKzIqw3>rBjajl8JX4`D4P!e;~|%;x&ys^N(RR?q7HKAbYBp z@q$q5YImd3{ad!YUkfEz<`;Nn1O*u2wrnAGomsa;BA2o8t3AHnRgwp zw_)A_PwGO`-W%-}SlQ*QO9y`@I^(xSTVN192uGXC4NExEysXG&Qwy`Y<1Cx7HCM`f z1Z?MnG~4_L3K)FpYDTvEj#Ijrv%+jsnZEU_Sb+?BX=jw)$N}^c}PJBDr*brHa zMIm-H^T*3&M3pcf(Qx8?{!l!kXe&2Z{fz8kx*FyV(ap{<-ey*U2)!9=CBoh74l@1( zNWRgWdH>%Kvt-^%PEf}VNEBJ;K;Ocoq(^FeXCyDA`2Jxoy;uzKTK9rKm-nbB zm?_OacDbf3I)}M@p?y0hkM4$a2-RU8?cM)Vdc)54j#L(*8e+jy)_`~+Tmmem3?U|H_<*xG7g4`AT^o-xe9MY-y z#$;}#_ZmSBt`{-h6GuoJt--gO)jjxnQFfXrmFk(ta`2s_)?>Nt=&e|>_i+uNPV}K1 zc(IsNaiyib{k~;c2NoW)Qmliwsmey(Ifs6jzn)3*R09DbV!jDeByH=u7D7Wj?WJQY zP+6-Q8U$$H5?tApox$v-Gam0J6ef(c52d|%t9>Nf1(&|2%0}`6XH&@KKY2h8Wy|ev zab!eSmqd1VP*o>ymr;a!mmQUJt(Vl$;y;OD-q`E4zhf8@l(nhEBG0#ZkFD;#dO&BVFUyV7UE8UMH0joEjj;~q9ujxiz3rH zxTmQFGvjo34z(l%>h9mwbR$-9)F~i`GrOet{y5y&&ov9FdH0D)kwXQ$Jz7~vwCls4 z#F5(dJc_+_8s|;ZuhhpO>G&JXroK%@w%UVnCLqeMt+wBLPnwMK*)P)L+4gN0E*2FH2Wdjn#HS@_`_)m%?uxv3j%ZQm!_zgKmYzUt}89`TTAw2v5{Sp z+(^C~?kl_7EePL0S#ApHl}gTuR_Bx^XBAMdQWzQe8Gzo$+NLtCD8l+w=Fnpj^(52xtg*3gXhB*mVzw|#5k$pjbO zjHJk_V5B-2SzQxM8BMFz+dBrU9b|mU-FQpGaI7}tjA+I(+@l4GcxWmZsX9HH@euZ8 z=|z!|t%8&;-$JhKts*1Q>yFGmBiiaFGT~)SMrUbfL_1Q?7RrXHr`1hGk?~o4cXYuY zG*A&^~C7Uu}lNt4aQ@oGs4oRzz$@4!* zffY&TX3+w!_v~EI!TanrnFC-5y9u`&-gTVH;Y`?^x+iwl`6{%)vc+0 z+j~Tbb?f1eQKd#U@uVmx*cH~urnkE(Fol55<=4oG@1dp)B7w11qYi$NJPNO7qM{4Z z-O8VAtFEp|oqKz~B#yA8db^O)ZCV!pDqK0=lmPSw|8hi<+i6LoZC0cXi0_=MJe5ei zg6O6xD2s)0FbTgq0_f%o4ZGzZ>03_?_Bv06!uwK*+HyECvMuNH2`C7(RO<&@ncR4p ze6RLG$~>yXgySlD;BL1{#5%sYLrL}hcuqmxBSmEC#@31wlkx;Ez#h!7U1Lz$70tH0 zsXoK6jg+K6dH-FZTV`wA@m9GhxODfChDTF^p?z>5D`}-{s=c4{cxN!WAlr=~1p;IR zbfpN8JzdhZZErx93HDrbnhooDU1sK3KZ-$-crl6mOy&o|<`F+npWl7L=C=zhPtYpJ ze8o?^%PKh4``k~=@oS%@MA5Ptgx$K=@Jfpss2B-Vr(fjMZnb+-8Ka{MLp?@wlkAfP zA>G3t!hHAi>+RK-eS?KtzBiAW-E6D3pzd__I=!*A(1QYo1mpP6eMD9`#EE2eLB74)U#H6l zRDr$rrc&}P86BS9NM z7TpNFEb{O96X2FvzFU4#H+Or_lie+h2Oc5twlos>E1{NHuMwa*3b%(FdPDJg!VlXL zf&mEJ^$+uEJcOZDl*pP-GK)Dw;9C_m!36+rx!fenJOjj&a+FZC$flx_b7(< zS0tY=%QPGLLk){gRd>Dv(Yj<7lnt@{ zkE|qbeN!TWe#9?hA%jzYcU+jM znNat=e2G`wCcubJPIKcAs{f;5O@`q41WR1-0xx`8^0jdh2N!7`Q$+cy944^uskV-f zwDhS}@cDzyk|y-4(h*Y}Jso4F(e*Wz&MW4caJ^>nN>u@N+?j7x9BWkwO@8|T-j`ND zbJBcO>2P~EG?ux9n*<4^WJ(AVGqhqaQ`GM#e*L*@EtnqYJwSOkTXP@FJcm+dU(NR! zeAh1Jqd*^3oZoZ(ru$P=;@mqXCOnN|fA*t$f!Rpl55jhB@_yoq2SSzvZfeV?yTN7* zwH25N)r-H(deA2wc~RNR+^si9E8vq*Hs*&?qr(9N#If1@3=$mbGzNwrm&RCcGkOY)#k zK?v;8veUeKgP5|Wzk;37b?&Y$B-E)Ajwtu0op2MxbZMs4(bIMnTD^WzO^<{&Y z0JhU@$8w)Rh4&w(FxQmo67rzevN|MpdwOuoNS59=#V>ioucw6!-3 zKi12$0rXffnM-~92WEavROwZrsb7Ou*M<8hMKV_t6RO^t5=yQOML^ZD+z%8J!KB)^ zg{n!-D(Yoam3#b<#5FtF+kN?xBcVI^1vTGC5rV-Q+CmSX`ty)@iOWf6ht2iVeT%P} z)S5){xt5$;Py}c5n7$+5uz2*r+j>0qB1cF}#&t5qyoX5E8$%4i#C)t&-02oauDv?5 z3;wIPW5)p?vD~F!N`bbGD=*&q*8rhIesdd!(A*5ZC#+tWE?c7eGSY#_oo)`lR53e* zj%~#@y|cW=Eg%k**D+H`scS^~Nq*50TirB^EBJ%zypAlI`$Q2&g~`v!JA~rwlt_JI zUM9wD#{|6ad$@RkD+3*;xw};~p)mBxQ9ofO3Bk2ZE(qhC#SO}`4)*zm!+*;$bQ;fb z-K|AlIzL*?H5e^@Ot`_O)@>v^W6Q|3J$RRy%@6i29*2}RhFg1<%~Z46CIG>?Ip@ok zb()vcqXkcHGnd(e3^hSh+y(_GG=IJ}+q6*hO)HHY)4M63@u2}|X}Y_LV`aOgBe<2l z_$1pHIfmAg7Ji>Aa>&8Kd!o9kDQVzr{lRB5UY^_z#a^*COgQSs8&)j1FCytgAY^K% z?_8aR&+FXZ9K~;}SI+@jsB>{95X&8mrVXuI>@mNiq8JM9MzEz=~YaxMP{NeP~sR%?DdqNcv(D=X7`RE zHILMyO0Eb6D!2}#?X%F&m}MD7A8k;+bn-<9YOz^45u|I8)2l$SChL z3QE_81}?)rO`rtd-_&bvTt49*{jBdGM#MIfsI@~IHl}N zsouPeI3*31KfeeFNVe=P$?lD2d7%6=?JD)C_TZEhicxw4-=dUD{mR}}QaOMAcHsU- zPZs5K;5t5ltA8Lo zK4wCiF!E7pNCT0cM55UTeWLC;aD*JKBp$+n`17f2DB!k?Q>5>yAPB0OpsMidv+N{& zsKk+~nG#$cHvAMzt?NT?MW6PLw@Ye0ecWDgvDBvih~DH1l{Q&NMkIWLWr1klkg$rX zjhmp*@DDaDbiJm%6AZ*9lELypIY+>_@SdL`t$c48AWw;Z(Gov*`wdP{bm}Gx7o4SV zEOXwQwthdI=yOAZy$f%*^}B#vfIyDr%JqJbCNa+|JI5xz$gg5naep2<>7;o8OCJ-L zTW}`Tw)SfK>sd&ycv&*ChCt&*=87IU_Qs;l>i0$Mp08G zvYqt(OQw}#6E!0pdVRt1W<3SYtXwyHPALED(l)yG7F;yH^9e0WQ$zWi7H${&6sY_} zmLpildjL|b75yRI|?%lfoX ze(_0ZWr1+ug>~et-DoBRa%1j!PYaq|2SKm^_wLmA+(X$)5oTF8u28 zzU*Fz)CFV)Qjby>nDNpR#1Cvc-e%^#p4P^r!~LzlXE-gaAgQrg<(q_yBHxSTC=~IH z+|YlemwLQk|H#CzBsoOgFtv-RFu@^={-5Cb9N4pg-et=m@vbGGH4Xrx5|;cCk@6Q% z?@Z1+qOee@+k)`HmY;Zc zU6So`yi=7=C|c6zJN2QF##+~Ox2al!4Z3MxqWOr9UXF>=B@?iT+_qjbbQuOauFMMm zzurUmh&2Cdn!5uQjj9LvRlj8>DfwQk#PHlZ$`{MMdST<~+Nz2-@yBGy>f+@*4yy-T z$MEYjQwO1O6U?I9WdyI?7Qm4i#tS!FK8_BSF!96`eR!(34ew*S^px+l@cZxvnJ2h5 z(yX%TrMg+WMdcfdeLu7cNzK$^T-p`Tx#l<@bj~9XSJIGXi*}|(ooSv}i%Vo)6wB<5 zU^)I~O`3mu4I+4KH#!tO8zdxC1l}W~XJ)K;+AYla)!j5&8f_zch6@ymW&VsH7U!tG zLY8=NhC72K^Z4XT7)Nm$o}q(Hl9kev{w5FMbTfI#cU^5NPNYF=Tq2QmQ5yY|zSGP? zHS?t$T8|oxEU11X=-T9Jw~)Q#GeZ)~yW7>Z%AMvs@;2+VMBlgvOkO=6u2#af5_PJNA~y!hw)O11`79eqTO9qd%<%)$+)bR=U(3Dq#x{cpf>kP4|?(aV)CsZpI1bG#3etd==XjG zQ7$H$Q+eq>Ym8Z9Bym{UnnqV?sCeclru3eKl#_GrD5YxTVez#% z^T8*kyNNy1lioIKg~D1&VGlKx6}nGdwL`i$>2XTTH~#g_X z)dmLR1SbV2y9uo|W34Yb(PsADe4RG=US+s>T#~0utF_rfSWp0W8wd4ZrF+@;ZhgPQ zx8LN6%Xrg*g_IS`ph5*zqG1enT!7>WSQzjU!S!!qeNi#~w86%G3+ z_$bR%@V=iZ#q9nEdpkn57ze1&PqM&L;98^AJTpBK*dSQo{aP5dE6^$C_2E3B1JIy2JG4~Dkws`xizF) zp7@dbzqjZLp2@LUT!!t5&XK|1x-;1l-_mu7W;Scpux3kZEIw!sQYCG8?bal0| zGTNTQ{W-aou-gW$1;A(0Ncp^uRFzlSj?J@en_BZGtEmnu7t4JaYj2{4DJsoc)mR$% zi62f7Jpe=mt=XA{vhZ&@GfzUqF*^^{m2}%toP2qqn@ribgXo9OKbjt`aREx;#+4Z? z>Gqe`jm*W#^9tR+l(UiK<}X!jQpd&~z2htMxv|L;>sJP%V0ogKnm1q9`M9c#bvco~ zGNk4ne6UaRQ(M>^Na#FPi5W^XDG)pl3lH70`*^Am33Rqvilu%?I5CUu zssE9mk-4>}Fngkk)#m4QEqW~XV$9>cBtPW|qKYGH&CJ2;`G8E{j6s+rW5l$Q{OzSz zAai9J?Gl9do|FQX6739a+&f%e%Y}o^Wf>9@neBzx0?E%*8VY!cHbc1E=k8_$M$64{ zTjkoaT^f=9#3T_@92r&UJ|ZhL+$Qd&B0v=J*1ss{(8qJ?V87imEJ^U?bdGm!NK-lyn0F=9jhnU?CqZVSHG4w;bThFd*D3L7f!nJv@Xr2qr*Des$3{9Z&m8?^c#HB!`B#p;MKXwg%#lF zt6AT|MRhI9K3Fuj34)<1glnrC&q|>3VuJ}^^XSGAAUV5X4EdHE7mm0dN{{u58D)t? z^RXfR#o({NuU>QXG(sJD?nkYs4w;HasmYJ~NO7@(&o;vZUAa}eiVKsP<5B+9Ut@I+ z_D0653>SQ-LR~+Sns7+h+G839z_9|7G&2b;C9d&!8!eOV_!XvQaL?o0aF z*ca*qR@{5(UP!t#3_6DNZCIBTc53)yDfc{qM)xAK$St5egIR!Fd7dAyJuIGPyDXP} z7&!->2SK@quR8aSS$=`+yV#MpNc{>5J&VdMnl=IaITTbzx3dsGQHl7+3PF zh0Rb;1&v+3TZLi;BfntvyuX?z)%Gr*m*YRL5~!GbvL(JO3M-a7FRqdJbTEXIyps-A za%BHV2&^e&DW9!V`&71cEVthlPL*fKSStgKWDEhk2MEa_3?u2M+zUH0!EH)&NpDJS zEbEb&&mxnXs(KTg*cbwF3pt6j;O&a6SGoT|sqYs$U4?opwn2`C4Y_42_@8iNLh_Up zAHtRez^pj@fBns^Veuierb-LwB9jIKAVV-1#R|5w5dFe?DsJ`H$*O{?z z)@wJdc0Gs{*k?()9j6~4*k$Ijq^Hu5%Z2hMAqsn!nq13M45a?xM?_W)w+#eU_n%7i zPIvl1qGJWmw@(lVgG=-2<9|gO} z{C20eQU!ljluss!EBu#&^k8Vb2W5oa96&QpD4Wl5tLtH_OD!T_!q|_odd2B2lH3Fm zn6ZjM+5w#o9AgVJ7cGp$NoUa}iIBHDr^f|$1;n~O{0T;fCb@@7{15Ydb3XxD1-OsU#N()Hdo8vD1#f=>&UtkUJoo3r zG=8nTS%btfN0@url^20aMYLaVD})br>_KFNb~Ufyv+nllXx6^z>G`KCCJiQ>EU}DWrT!PH+V)bI7i4%;HoWL0|Dnpj$?cckR2tVKj3B z@hdMBF`&0RhGff_EHbKVjen$hZ+?xKIeuQQls&JqRaON9ch%rVdS6jw<^b;=^6)&w zC4s9u%7Fu;9di`xf#rM6kckxW@8eK}=Q<}$lQBnma+9LSka5su6-wuyH;PKaE!J>& zp9w?_1$Mj#Z%0h?L45756f|iSiE-x>&=%SZ2r(Hm5)Y`jmf#sBtW|=nla^CA&O>v; zH7Pl2$NU!PCJpXr_Sc={Uuk+?kO<29@S8)p^C@dVmFezYRS_%5`3QJzwuxC#WQ9d$ zjscIdjV?uhptIt&JhqV!Z1X=$0Nb0zR{qBiDZlnR;C`q2-;C$m>^PP-_Vx60|J2^a;$_TU|3o z8kap6Band7k_AGnS3!nN>{aIR4KjApDLz8}iwgcf9^UwgXef*3eyQ^*B~58p3i+e@ ziw$r1pmM|r&Clf!f^xy{r*0zEsJamSl~|;nk$aAm640F88!d^Q$3?D=IYz%eV`X6T z6GcYJus_Y+PP|5VGrxx2uc5(k{COhe(UDq^a%i*?k=U&eCc ze|1CTHFl($Zibx-Rz z*1E|w$t>{+r3<_jPmbcc7Fb}%(q>7Q3=Qnw$Y_5X8tDEV4t2Y(#;^Oh)$XKotcAlPFT1Fz;4C4ZT}Z$We3kL|a&_W0nU*~v+zO@opAaCub|Fd0ubtP4i| zEK2^W_JdIVZ>8-h(`rVjZf!96J+z-Q>XJ&mp^=d(tApHYV8bk2?W;u+nj-hwI89c; z;rH0?cm3LwsWzTE7@gFyC^oguy?UNa=KM>v3eZ^BV2ii6v?mMMa*|RqPj6$Ozgk)f z0hbWV`b=fgRYb5)b=)K*zdlnHnHoI-LEZnM?%>+wDX#K-)(9dYX9~Yq zER7)?ik+PtT$^%A0c2jw;M$^7l3Nqigs4DE-VsP<3X;vF%k@)k5aiN(TZ1U;ynlWO zJ}bNc0|WEkv4P}%b+6lZDGiVn;_w18dtcuIayg2e<%g0@BON*h)~u$fdt$PI>_Lj^ zX`#hriA&OYOw}kL3$-I5C_51LH}MJjs<&Hq8J1uw@FYt~=pEdz7yA9(bdUPG+xvL~ z{hfS~%|$_R_!B-tj~r3V(hytEJ=l86sWhi_sYQ>t0hX zFx5YwiQGI*r&C;F@CgCm+~TUYk4SRU2y%F!m4Q3!c^MTo<&W-?^vCqxd*(~}Hiei5 zot1mSg1%h+)afL}a{FJ>gQ0Bm?mxr7u5$^|E04il5B2PmDf|a`E730Os{9S)_(7^_ zbWck;nvsKno^UxCg^j=l41|6ChGIBx(!AWGWsYP|^KJvdUUy{ABvvFP6qBC>dh^G_ z5;~#JyGFWviH)lg)~w>!ShcID>uIV@FNJxZ35|z4*D1dq&8)9>=a2?rKZ07RD;a`F zg#9)BU?xsc7h;*Yeo`}%eEY3)mDpb8eC?8iZmuQanxx}s1s}MrpI*i564h6x6X5ZO zbZ_#_wuWubjj!Q{mc-)^>2CQI>4x>H{LXU9yGgw4wEG1(?Dat6G-37G_sJ%bM#%NG;_$tk3C~fcJxP#qNML?c~qSMmLno*%n z915+)qmm}(T%{8}>R%`X2p3s?n0qb=D*tB5eM$dkJb#YTV+D774$?XP2pQj|ZY=R{ zvsWxunhX$b5tM4yqSu^nCfWNLP;vA|iNts`GEm_Wrq-bEQ&urURaVlQ=#5X2i*;PY zadjurq7Sv zogpAgU4I1+UQDr$GhrABKTIkKL(|=|tF6M69Ph`Q4D#-w4ugZsuC+YySx55atDidi zaYNIs4_R)8QuO3Q!P2`ibN#WOZ*=8m0(=`?hf>XV=7vn0lBwdp&2?ZwZ??r@^01v= z@)Nt0sO@yTlC~;|sV!T5s1ER5uto$f!at%l;~VniR;0!o<0nsvCs!z0c)&>L#Von^ z+29012vkT^u2O~9v-#r_&-qE*Zg0NTxLNnpuCY%H!?!Clw#q)HASqCB19eKeaL%IZ zMF^EE7|YxT9p`WNyZ$K?jp1*nfi=nw$19o@P1bPKXP4-s%xJH zWR9NVI}eJE8W za>3VXr(vVJncl+Ip!ZvG9}sW!WS-xniz_>FKa2JI0q-OTwF;VY|Mq2@NgRJ*uzyuZ z%C^>;Sg3Sgc1*9D_{8hG@@c}Qr7WhxdU+xBnh~9wP9@$QH*ld(Mw)uIybPX@T70^- z-at--sG_o5Cu??gR{8v8fAKPDHwDY{lJz)H=|8;X55ocb<$WW37)sR^XEP&Tx%~o0 zlgHeW-zMIR9~Gw&Byt;l0|gpvXA{!A@c_h(KhlGVx#Rzqnqm)5^EzspmHJ&oOk&s) zmtRgPf7q>5v>wH}P1mS>cC(pkjJL!x;SF%2JBwhEJpwx)OB1YDeqUlT0(UK?z$b;p zJxW6Ib+B|4IfHilAb~aIk^2uM+;O_@^!tFy#X4PSI4JY^J5?#5$@Qi>t@}Xl zj_DwxPszpZ9H{xlcs9+|Q70Q3eWpQ>C2=GWWebBSNOI4UKmNYUXVY|S=zW(} zb6VN?U3wkFf8@_F*IDFugjer0Xc;#(Ty*f|L`Hi`IO-b&pG86UYnfl#j2TO!=cx{f ze614v^ciqnNvu7XDDIoKwERSUAq8KSw{Df*=99@Jyzs!?}>Pc2FX zVU?v*S$yWlUz(Wt(Ck)PG!S=>8*I%s4~wta@k}Xe2I8K}myfuke6i+#&QiBBA1KF6 zCVx~~pzAth&ZVmN7D&ZnH})zXm?I=K76H-hoN#Pb zj`y4h)fysASz<)j~KJcgKS~lEY4=0aLecwe~!^H(? z_8#*UVT7$-5B#F;a{#6rG1=nYT7Z}>=4fKNvev}4nqb)4!#<>Li!pNX6S&K}hxDWi z2#iO{evDIPf!^;}*HhnKuCCwccRjV5keg!xt=X(ac6gi4ii7(EhQQ0mi&%py*NLCd z+e3@&UrIRtz(TG-=pmxd0etk!Uo^G7KO9Ykbj-U?)UAsyUyYLiJrBM4?iH5YXsBQn zqb<(gST@g_U1Ob1&7&iJSKUuHFkeN1HgGLIB%P}~JZmu^M}OH~@i7ez^xo$WYWr&% z)PGbEx?%?*_Yi@V**;DGz!s!J2hh#Xp)=`_JG9WxdDcZxAe$@Ir-GyCzw`LMB+#|m z9l*dTbon}*mJH3}Ww+x(<#ne7mJVU2Lb2iNg0COTphJ;&#U|r)XjQ1&S{Gu1=r9n_ zccDSRkD)jOY*}K>0RitZe*Arh{ksO>68!Mk=64U?Y(^j?0`DscgwXr$T_ln*y%cl$ zDG-oXiBB5Bx^)^=xXYsAE_vv- zM=|TFJ6~MFgC|!VI?@{33knyy?$k6bui|NC7s(2xBzo5Q z957|MdN?N`rlqRHsVcj2Q>p)9^CTxk*ynI>=?+k?yTbL3XKHs*!P&Qv{Q8tBmtA#P>69sJi&E12gknXh z@CRu%g=xKK%z-s03lAGxvMJLvdeWrZ4|3cG-D4+KiryM9}$;_)uq;mpddJ~Df<>E6 zDrEAWLp;J9&9C(mIeDW4_~TVH(veqkcKEDR$a(hwE<4@)2QTmBl0J#eYaJirg&h2kJ$LCiTbnLq5BQ!+C8XRQlC14yNPvHW9Br!#*sh9 z0Z@{-`=J;3i!9-S-18PKi6KuZuS7$9{_?^+&7xsTns`oIL ztR<{^SAhdShRb@AuL#ks+;jh-o}KOq|K}loI3}~?C4zxI+rK z6aCa_4XMp7)t6l!e96k5-;nw(F$rVHkNnhe4XH0#s_!b}alYLh-!YIz{xc$~9J#>N zpV66U>X=Ml*i~8e3Yk0nk3<|w{udP0_8W=dg%T&~@N1O=V$^dk#zKn`tQ<;iH}F+f z``w${koS5~t!2NtFi|>y7JzxJ@gog^4`Z>HXL`xB6_7FQ2C;g@Z^rw zV`9TUZc&@P2{&K~aLNj$Z}!e6fb-V4vlk*|Lff8ST+ZWy!JAyx3>&c_8Rc8HEg6#G zmb?XG91|Rk2cS^o&?o>QU0yl`x@nx9bqgW&T7Ltf1b(|?gE^@;{Z~g&+_^~O%NN3^ zktg+4KhJKMZCaE)dW!P&B@aAINbV^T51)u!yOFzVUX#o11^Nn!2|<9P?u1m%9}1%* zrx&5 z3;9#Vl3bYw;a4_-wL9cvgf_^qgb^lppqDqJ$}f)f`fZ<$bLI-s>Zk@!Cr#6LImz_- z(IZKw^$v1n0Uz?ToD1*vZdonss@Y$d+g4eBMp}Ukwc2)j`eAB=lNuv$LVlP3gGgZ6 zMSxZG`aBd9+)?KYIfKe2gFMMw|2sMBj(~xarN#v058R#>NxaP)*|Cif;*v{^(7Ap_ zC{{K*RQ?|3cWr6WYRB-8*5eYZem5hcP*o;MP7o^Lx>h-Q$Qw?=*MF$f6{Gx4&*0&z z8Ma-_h_WtF)}rBlWc00DwK6qBXUB8k;6cS|Div>Wb%8+TK%pMBIDbu)VX45%Ev z%X?m%hR!}Hxmk~C+{OQqUMwipgl_y;6FMj0U5I-Kk>k>3)@~^s3;9~s4*qufU3Njn z1fLI)hASz}&K=L0LDCYFM%Nu7P)Jy(G-@5tNi0N2E!W;;&j?FS5wB;M)iXN;uq?Sy zwAs%Bls`^n3;T1)veXMk1XibLMgx@}qfjq_kkzaLHj)U`_Ge&}QeP69VU1z~J`dQU zT8K7{&HZbehH-9Q{tg}%Ju$C#b%7l??hk{^Hn=U)_Io0v<-H9-8X8UO2@H> zE>%VZi`XhYwm|~UwPvwj*;-j+ndgi9j<)oVuZrJax1;g^KkVS&f44Sz8K;`(4fWgj zmP9kFiWM}aZt+!m<7qJaVa2=b;ay{mSpA=6GtwK*CzQOuIRD+iEpO3kN%Dc>$c9k2 z!|ny^L+->Qdf=~IUW;_LtNz*kk84?KAGZoY74xpWb-c`Z3A~C#>Gu* zfv>oiCFY!cBAG8YN==~EK>lc%LNe~)f+bJ3K_EPCbwKYLrwQhcAGjpltd6oLD%SuM zz}ug-uPd6ZN~RH8NK1 z&Kr-U=hs6L7UlTDGvlQsq#8NGV1n!90W3BeRxs0LdYw-PqjiUGt1~$ZRg|lO<3&i=N8lL83Y+r)dPOaG*jD z+PO?sO0xJmV2Ui_#%iAs$JJH4qT7aWY|v8geXvYiNMCs=gL88zEf@Hz7d_ojO{XvU zbr5^0ZyIE2S0x_~^?*5bmr}y- zXC)68TRA*0(^daeAF`79|HnR?|H_L9_}asag!WLZwn(9yNz(5P{5AlJd-3f=2~Z@W zRH4Yx{`!<2OBcIWC(?gwqzHHI6zzNaGB9*ahya6Z_?2CSW{Hgx%MBWau(7nQN#IQi4tHC6meGs3YvR@m< zrG&?nz`>QrdGkecl)bS|&3W^{&Y&u>*#IpauOxG_din2P@{sc zN&*Sy=RuzT_WpD*KLs2(Mnl&dAYIzH(1=99ol*Zah_CRzuSz0l{QJ@8pPTq9D!%KD>uC2ynjaa*{+X}6j3G-E?=zgt~p{AqqB!FSDzx0+)t7p6`mRmWNGo-sp&nQG2A0Fe|{kA8}upZ+_^xrNU(MCij zKKYlbe%+)zdqJV6_|>}Wh=2-9y~)pB_>LwZ&-<#4KLPROJZ?l{IRh7%;GBKo{W{&G zuzUi;@S+b4kQjE4THuvMO+c*m2&zrE2)a_z8xG4W-Zr6LWFT35jy}tMMJc$Idd5Uk z%5L~|rw!tn)&3v`dWH1Mwf~&DZMSmp57m|sY7RwL7%{|gatSX`44c?*XVH>jliUu! zCo9kEFO_~(DB92abGv9iVd~NW4W{J0X8k|QTQL1MLHap9j<{Gdc<7D0MkG-AY0UCJ zm9wV2p3IV})!g{h{GW=*mab1xvAExcojRP(CIWh5$)(tef;sV> z4!41g=>R$YfyPA3BJ0vTZ|@c|LLz4AF~jCxvu)75FvSJExC{h1dsRQPa~6F8t{r!o zkA(9T7#0s)LSW_Revv=nzEvcqA|)iSU|U7P=YJQtq3tHN#UK3fTQfV;_c>&B(zQ`E z{%>~JvP2o1WVz1<7}?(Mgog8y(P`9`jWz+K*ET~(%IsIMDiU|AO<%q)Ik<#?J4yNT z*930(Hy`fo%~Iw~Vp-RuxQ)cr3{7fDqrvhGO)Rd4IPbS|Y54pTc5)oGA*D?C6suRU zN>Z9t8wo|^*hAt_7;!e5aI~Ar5G<1MB)oo|O6Bp4X29r1(Vc|!z@595LF07wla;CT z$a}6^+r;Twktt$&>WGYJNk&n(kHuo%E%K-OhbG0b5(M(4=~=^K8EyJi6-6&<_E%B< zMNR$+RJIWJQtt7b-b)v#!W3q_6^78&5=da84nx4Z4>?phj0SaSjyOQId$;Wg}%$N9K3Qe74 zPp~h$+w!Fz%u&9KuZnqmB^>Gbl{4dAy42RXBz5Y;PMthd64_l6`6)bgVOn_|9Wve{ z)@cRhnIjnYgW*V^I5Oyy( zk>u8Mo@uV%;TI(1-SHH1LR6t6P~yxdyFg}y*ZN4m%m`KIE+Sr*?3a?)wND(s-Z%wy z@fCS3ReB+XuOJ+_;eXBBB)JR8sbYNub-(x!VrW+sIe_)~(Fe6z zetf*1W6$9ItuiTnceb zqsY=ajiDptotQH&Q2qq~twH-RQXE^zs{(+=2nxDkiPEk&C0QQu=M7KLy(hTs5m+{q z`ErYs+Aud0bJD|H)NwgPvD`dw=8adzyQv{O_3AVpI|FEK2aT~El-ZJDgrfuS(tk=x zWYBg-F_KE9MeK6s{ix9dXZ@-a&K+A}^Ojz)lw(OVxs5*)x1j&+x&uAx-Y{=0Atu~g zzP4|0$t-4K;wEOlPi^3)FPK|d`W`0kpyGB1Zdy+qZ$9Mj55(5;iA;U`W=w*|@BS^& zb5HFHzO$sc%I;9QxxH?(Q008P$R~jGRrK!3S40QjsC^oWAb^K2CUtE+k zj&;c0#UXJ?D6n9!{@`6_S9UN`vv^lhQejhE?PP~r>~r7k_f7NaWQ}}4?f*r+;|l(( zq9uU^2leOAaCI$Dr+gV)3^plj%3D*}B`r3)dzj=RPRU~T%nnxR_JjOc>rXrj^gUd? zOFfAHeSvat-@ z8i=vDwYkX$=CxB9=?fuivuzK1KS=2rmt;-@z@irD+uw2ox++VqGDB7)s-;Lj`>;7F&b})RW+II^qI-!50 zlJz#0UQ@q8A6-m^9J;L<_;O&smCIE-QaiT)f7m(`I2+6N|Bq#?qeF}&G-(tfOQsAa zGZ}RbGboBeDbYeDW-93{xwq@yulw5W>qb5KXo{&PZ_w4cZQk?MAcvk*dbuj{dxj>}wP`4A($M6SO+tKo zeTZW;(Iqd!!ayRWt4J={&OyC@r*-A_*IidGR{ScCkXb8*hKz`vBeLPjtT=sIOQMEZ zG@#_AKwQ8?1>%AvUUcUf*M@2xTz{Np^&cVz>w-D1vi}sy;;Fm0F;Q*&KcX8Qf$bCW z@KL4<90;$X(!cnqRBMF_MG%|X{c54>L81d@DlFo-!ds`bp%pcLek0kv-dSRCvU$Fg zm9SLjuaP~P@d2`YPf=8Rawhn*=~cy7!JlPZ|M{5#!UYb}Ok?VY1OvP)8`iY14pxi= zV5Q6&Rc@A1U3F;0tl~t`E4|6&5|1zQn-Qh^VYpr3t0$S}mBmyJV#24#^Bg*j<+jcD z-Oo<@DL8pwXxbJ&v-HYa({FBVoX`VTHuVekRK-`*c7K7({0To+k>!8G@*iRWyyo&Z zskFGHa>4JF3Vx~NKS-iA^R}v$CBCAW=@_itT}H{Dc@- zv>0*O9-Y*UPo*^+LW8h3*=xJy&+oNcx_Wi182&k% z9Bv)|;DDBVlQl4lr$N6B#trIp?*7z-E2VI08IHm?#4-wBJzb=C6AhnNo&E++GA`6^ z`>cVCW92j>zq z{-qBIVN6_YF+`$%ZuF4+$>SpUnKkk%eh|Tz_tQ(YWh0c|&fdOOTfIqDPDv=qW;{!u zMD6!uPc5^>`R`M}lHaC8NsCBnZi4!5QcsB(y~k+?PWHhuH9YU?YQ`cIEN__|JrtMh zXI&>HemgCe|Fs5Nq3v-3oUtrCra;1*oA~se0h~XSFB2l>VQIAA??$k~lMwjc)X_Xz zFpNg91+L@ME%5#ZoE({acwqoq{%(Y{R81q;|8lIP+IH-~LT}r!muSXn=;sphn=uxz zBVgmi3vCr^j|RTG*C2s&XfE^0wm5`?X>1F@5s~PYa6#7u6d*YGR${b%xFBN-1~v)# zN#2f_BOEmL=8d1SOO*AA!lw@N z>0J(F)=SA*Vo6FKw_JZbsa$z-EA4WX)+dX&o@-nc{i(wH3SANkEy^b(T$2+ulR;@+ zm>V|^k?3Z_b9Q#HVWYoi_496i8rBSyP*_JKJl@e+IaMLLSvfhyY4?BBDj^T)@r7#5 z&=iW^oRAVOUFJAF zM0E!vq3qwv7qfpV_1f!(PI09nbg#=~MgP#q;a)73xbO_I?=2;?u3Wm=oAILBx_uQD zYa~~AC;p>|?=iL3=yi7kjf4lu+bT@E=GcI(cXCw^m-V{6$C7_m17_|K5yRO8qk^ z%N<^*^PKdr?s+~^X-$VUCEvw42YKOWb$$n0jSll_)Dg}ams4q_xA9kPU(>^%6E2=W zuhM1=qWkGb^J;nn>gpOXX8HS|pe5>JY>BS61mvM^Zono5t;OOHTM3l=VutgaYzHS^il=J;M zy7Y&=jFmzN_5EOmV0hzeC|2b5zhbjhJp8Yy4$A0EH!1+Ni?=e{Wvx>g976fU zTkSxoz~8Yzfgj|c%Zh&EC7)u;r-8fLH<`D)b}FC$!abZ1vE;kHwQcsd&!cy2`ltRX zd$+>|k}A@|QC{I=IczKBMS+oDQVdacSIqkf@9A`2UPODcjH!aV|zj)o&tRGl3D3599&aeloUpE*&wDj``j z5X-m-ROO6j*%yspOA^rg1@x-|SqVZ$(eWdXF*uMnckeQdP2p<5luYFyNT-FQkNiSx zS7+~QRpMytZ$fluVhc^x(mQy2EqlnmA$h6tXMUKBH@B-bUA&F+ zd0691;@sWTO@$0wy%yS)6;?U)u3g1G)$J_yk1ADhy5&!ZNHWN5y7H;D@!?I$tmC1z+BHRHhFy)@8YB2 zoK4t%WWk1ZqJ^gWUL|IYjaeQhu^fC>Id@b(=M zEm-PbDT5MbDusZzs+X49r}HZ7Ms-!_3Q1BOu|woZ5>dz|CH|3E|1@`tI6d-kXBehk&X6aS5hRI%mp!+HXb^PopBn-An;I8u>+5?x-~Q$`RRAhK-W!1df@iFq)_MtHIS!e6o{CJWiwJJN z*M3g}yzMHcIHOG+$j$fWutTo3EE#FVjKsPRup-o*o}O*kQCueA$G?MG9OXZvt;&{( zr>-Jc+CRnL!!KjE!+yj&A+Iw1-4+$Y zoBm;lVqj)7^rkSrwsv?16Qn}4^|8cHqB zD6MTGuDa^Ve3x>7&LZY0a^+s9oRonty2OnnW*3ha)5m4=CM>nkj1l}LTp6v8`>MiI zDSVAwRpm^Y6pq0>YSd`OiANE8qy28b(kzY(>q=+F79(w3W$%;Esz@3#y)m({3Q`mb zB3`7_Buw8WLLv<}%tlaD4Ha(FK$p!+KVVbKvo;iR>64NLYlXs0y4LpIycOKCx>|Hh zE$asMqdgXGf?DqI5SM~aS zs!^VB2i2dVu{7?lWSF%TU|uH)9BJQGs)bY0Yvu|Jqkwd4Zc0|W&GvasYj30Vte;80 z*~)s?rOa|E_pBx5dQxbw|Cmd9&?Q~3q)T0)iKMWs$GQx+xD1(Ouu(XROg0KvxNN=M zPquQ$ntuJIHo@iE1anx&bf5LQsJ%7jEV8gRM~y#~4QM-ZWp@3_3I5$EVi@iOSJA_3 zXvu;@R^ni5O@v!(^j4yx2Ktq-GDRQ!YL)|G5Nog#YgCGHNP(QxCK`Q~#Xwr~0$`c< zoY;`mmRbF^5L;>?rg|NgRZF<`IxWPhR-29r0JO(w3z^io$9OeRXVIbyU^!Q2r*>yn zm!g*V{sFu5J-rPTYT?#pQPv1eYh}S3k;F`;SM11XYLOOaCMc-E$gCyHYcRVGKQ#i~ z3}^B5ubMf1TrC3QYVgESsOU@;nLetv%`P*oKKsA4a$`}p-DEJV4*nYv-XsIXp-bJC zevdNQD9hSwb5L#I@;N&?xGK8@$dX(7QF3vbG+GC8ihC)m!sJOrUZtH)#7I>hYx?IG znq564oOXZN?Cv<!{9|SlbycaSw?`MZE4(zq8ay*n{P1yZgVt=6_{`X?AK$z2la`|0X!X;>AW(D7};X zLujXT9Oz6)iH;+FTn&F4k%$tTyE-0Nr3Ua&%_Xjme5)gEHPPNnGi|C1t1>Mb?i4L5 zo?1fk5KDWS9C~djC-qE+-)vd6@oKR?uDMzs+X`Q1UCFx{Z(_~WfI}htK06!xiB4Xj ziRBNXNVf$UJuGotJBlI);A}tHlo8`f z*4Hz(uQF0xQ>U(^sXy#Ian7K0&NS+407a)~MrdTUS82}c!OE_7@pP#J0D1Ph&& z5Nq1~=7vaAcTkEpMPGQBIwie_E7?50IT?~K1d$JBuHJpRQ9@u~?CS(^kL1~`7)6J=OUD0PDB#2CE#hd7f9^!R zd4cHFagc{ZVI{RtSz^hrKvT+;Y92Gh!L!&qODdjx>aAdx@4v-(Ku4#>T|J0Js9^?T zTo^W@GnVKz>5$+z1u6}`cq(My<~JaK^+Gc2Pgdz>NTwBJ$t`Y~Rd8G2_i($-9rv3~ z7xZ%FUYO34{|WNv@B&2e^aFeAri*}IFrjYcP|*;57Kbrbs)Rc(k*iOnDFz{YEUl!6 zq*2^}Pd|+PwHj40>Cj0QK;2N`nT!Oo`W-pNy*+&E9g@H-H=2&$IHCr7xzTHC5WRz@ zVK~uIAh~_Zlw7G{yg?E8GhAtSYCIN9xl{f{-zv9upEF_cXoS0isYY}yzc z7a%6`M{9w`TKu-cdRhxA!w_aYpsDa6^&|g0(Kb9DU8eNBkW!NLv(3z)+Wb$~_2+kE z<8SjDDOH?6NUL2sOt=$!d`-7MK-YsTvRN6_;BxN{+G%W;k-Y#~PehifFZ{f&Hgs3_ zpq=TH5G#!65>)b*N?~=cwjkTP2QwgH6J-OVvyxz`4-b?jl z$?V!{{GKTof)NN6{l`YrMj_58bx_6g0j7W5bC7m43eD`sw;a2Jb}%_q4_k&SZDN6j zL%D~glWpW(d6CAJESmbN7YBVVN}^NeaCut@%LDL zHY9F9MiEh-@qOFz2KskaUPV0ni(82^Z0nfag4U_DSx~uk{e7wed1idgxb@3?%y^9y zf1D-9U5NMxnP>=?FPGYaxz@-Z#2@>2yA=~EYN?)-t#JQz^%H7ykQ6%Of5)(9N!bvd zfPjoRfr8`-2Sg>unvONl$%~!_(criG3EiHC#6rzJs0G{l6c&k0<}vnYT?tnov5XHy z*70txwT1W6Xo}>1FLtk(@tj37v}$=8;64!bck|t0e>Zt5DE3P}$SJ-8%!ii$iuNU6 zdetwkCa)g{@v^cY>r`3G-IU2Ll-vtQ18+ni-QR0|c}N^gej*yPl3_afqN_wYB%gU6 zUOr=K?aAh~{g6y36G1b&HPn+z4(c>P`LmkC!t(#wn*32-a!uQFJF?B=AE=1k7^6fS zZwAUNd4c=qUV3sPPq3hC+guqciPJ1wcY`?7+s@H7Av1Cq8BS}63*VjJvu7s<=BzR8;H|A6aW% z`g*x*Rdbcw?2=BbNQD;N<5lL?OqdOV_#P?!uxy!j?DBuR6v}ax_e?D=uKoW{ps+W| zFO#CUYl|E$qD{|8UQ;y~jh8x`c1hj}cukxnI4!9(3!PHRXcmlkh$g zy4?P-6FmP)GyOY-W@E`+KDTaco9+5IcqiTHOT7h?haeSNK+b~6!xHk}VU|i4m4loO zNX_IK0m#P$vYC(&$81@9?Jb5R|3iA7>&_RoH^3oDejW|}ALd+(i4KhaU&H#Ip0krq z?ErxoODv`h^5&5n^>Xkw8%t|$tD-X`wR; zi-ABhj}e7mvD0O8d#0je8D|AG@%B^HqoRrGanx6*t65FDB%#yJxhA4y*+rKhHE*#x z+ZVdF8U)wcKa8D9_m-!iZsh>u;&$u6K1ppYyL0j!5Y$Z&+cQAXMo*hgSuu^%DXZJx z)NY77tceUdTYj_bYJ)&`<7aJHjgr?W@8fhW%f!qo9klsFg#+lYUE=Xu*@);iQT)dF zDZPSGix1=Bh^=BZ)*jAXOSt0Qr%QWV%h`sy%J0v%>__}pgWu=)l@XVWVkIV%P9X*I z`qi)7vP6dQ{>wL^!maF9^AEMhJL3;`!DNM^Voeu(OSbeMAYlocDnbloKki&aQbM0* zoyA91^QmajdgkJmI(k@sdZ0F#l_OI{#~>-ZN=( znB<!frj@u-nwV8kC-3Eq1bIRo>yYDElp zc`}KgE-QFD3)V}Y(Akb9ZXJ5N+KTI$Fiz_%?0rf|gKt+7JNJpT>Fn2L<@js0kc^G7 zS@EttT$PqD)*8#_2=ROK$AlA>9QyR$CZSKSYRa5l+HA?5x=VJ}sJSJp<}p7)HuKc( zPvMKM`y&ACow(2i1`)WDiIalxC`y&Ish`6_ZgYMMc6xK$=;BA-U0mtGT~l#?x(7b0 z5iVVv&qeygTSFaaJAP#D4kzTJ&t`{sKNZT%z*N_*c$l2|G*=$_n4`@JtG zpts4gm6G^~D}13fUf6lZ4@~Yz621ELRGh`t1O$dvh{};gK|pe~1b>oNOsqw&gHbzx zftcnw0XfT+>qgE5|7pvb+}vdyi#pu-a=cSHk2;|3E$1l&I1!G`v0nAHoI3X|Q_e3f zvF$dOI4VelhHItN_gvA1E;YTZXp#~iB@uM^vP-NLv>@F(xQ)bol`PJf{bxt5^`CB+ zyk;eY08`k@-H$S7rg>(lwy?X?Y_*32O3IF1H9xE1c(c3u6r9Yys|y?EVpl^IYS3<( z34G4$Bsbn?xx}g@YP83?#Di2nb>#-;nC+e9e_}yRxdmUhmfu#%-(WDB)^RXCz2bDZ z%&(wAyK>(eWixW)_yz4qY%<&CfcL?3+;4j6ayDRWa(hL*tcV#dqNO5cDq=Dbb{S=q zA+#&wPvqh{O%buln76LM{0H-!SlXqcHEGpM6< zBiM+L}Xh`D|V`|7$Dw6)gy_4A5KT3f4*NN8fhvVUUcBwE8 zJ@?_-?5I%D4#Ii{q-4Deb}lwyN-WLCX>Hb#sWP5Oad({QF?-;E_wKW}6GLRRj$dCd z0|C7@tuRkae7`4mw;42*0`L|Gyvad<=TzVgE0El4iLJ(QYCT=ldi?A=x;S0QJOlmx zSv8$yT_`FX!@+$FsttWp3buoGZVuCZ(BkfTlG4jLouav}DT7q~BKN&c-ygc~hxPqB z-!v;l&HD8jn$w5~@9vJOG_l%0?cK@Q7wqDHA{3*mYDFUkXf4klWK-!}9$ev~WZYOr z9_+UQ(v4=j)L|7<+Y`6yG6@J1{AGMY%Z>cR#vm2%5n8&RZMAiM(lVNfs8e_Bx7>9) zyWBs{Q|@#x>T++e#N;zv;%rOo;*EBRA6a74lOJ0>cUxjtudhpdl0*>a&n~eyiIU`W zbcqu!F=M$)Y+#Aqy=0d-gv3>s$pc1$pJW-6pK}=xW23LB+_%NLa)AHxhKzIJSCa%3-6h@;ZuYuQpJ%V60`f`+fhQ8oW&fu zxOw1RR!;99REsep2Z@phaV;@$ftv=ECD^Mp{t_ZUP#<4Sdp{hGICURi3pHGru5*if zS#^}ioMXV};tS?nx>>Y3H0?C9=;UhR<%~X2N?hXeH|ZwP!EIwtC42I_{p?=RhS0S0 z$;myWWuXVF@f9l4I^+@(eJXSSDxOcb@ucoxB7Q6UPVXp&o+6lUzgCx(7?p%d7x*z zbLJdY`5V}~yqlfB)$c%PIVULKUBo2%BPq|}J8Hpsh~$gA_>VpfA0JEXO&C-;d8JI7I(rK! z&83ZHs}jHcs-sW@BTT737j{foRaLrZw#AaV;i-Qmpufo9=WnByRf)Gk=`$I)e^B>> z$63Wz@ic3>^E>|+r6L8GT1?h&2@LkTiPDQ@j9#OrXgJdTq==n#jRqHy-O~Vt3k8OW zbBji=NX}La+_FPXFA<_U5n91I{9U!_u2Z_gT_?52PRkt_OB~cww~+GB&7_=|C4(ft9ueMed`nRE5rIf_y&bfA%-9`9CFI307|Ih2B}BPDL%B-aLf3jtUYsvlUKer3i}Z0KXIEzydNOA4#nMoD|n<3 zCyc}8J#4xREen25@ekdKAZ(b~qGUYqk;tiarCv33PyQ%i9vl>8DzHXYUE6uO?kL=h#2(Aw!+;ti4~HuPUSl>raM2NYAAS9n`z-a%sAw zQg)j}SNG+y;N|x~QA^2zTc-a3cQkP2Qd&3!ae{_VJKvu-juMg+aUsO)%e1+j!xwZq zafdrbtvlfR{qbrBoc6FX8^=|2)4NH|8BbLMoZ#m%K7)6AXrB}BH@9S`gNf@Bg6ub1 z_MtQmx`gbw)BA%qN6cO7d~5ynBw`sT#mQS5rdY=8Rm^;+V%rdJX}n??Q%4Xrd>m(1 zA7%@pgx=y=E$?3M_Tl#QTHd{=_w#(>LWPSpNy`pJRv;w8wNt(a!ek$5?ThP9I zZp0q+S_btVDA~=0vQXh_^7zm3hxMLs0Fs|DA{NIGAZ&3I`M3brL8Z0g&AC|?3tB3d zx$2&JvmA}|dEX(&kM4G&+powXd~`=4@Ync5_;D7j2S_MGus;hBq9rK};yyB=rB4wO zhwu8-QPFp-GlV+^7=wNJwYG;qKO$IR_4}`?idb^TlMJ2A{wK=9n|(Wf20|}*+H)h(#r;UyLZnaMZsuv*d(=2ZIC`{fP|UGE7RWCkp(0SiZaXL5IL6q)SvVaxv z^{n`cycpNxv9wvYu*CO!P3V!oMZ?O9{&0iq$t#3m{}#K^euk11`0v{)AZFHEO&Pb# z(z;DDFq;+iu=1B}Uw@&XEcRFF=ZOEKeoDQ)3k@^hsfFBXWv~OJ(P*{On-XCUNXNiN zn0N|Bdy|WwhFayV`iD9CSb_bA`KJ-#ResAkN%NJe^Ci!{3q1A#!m*66wa;~V)>&>= zg?f}fO-c@50*qMN%;%&{mgpskzLM*7E1a=l zUZVs&ZbqW`_PNd#{PnHIipqsP^m81@ibv4i61%>e&W?>)Tt{l z+r2it`7VUAkSITgKY^)_GgY{ohz^UF84=1=<+wW7pFCY-J5&clBsLhH2WLilh~@!{ zA5qRhaIo9Cdm9O$;~HW!GW8ux>}WksoAiN3t+Ur3<7Ybh2Z{Xb=f|6Trk$ZReRqZy z9gJGqnf!RyyEKg4onUP1gS7uc{?Mtx{sW~5beEKLHzGaUYOB9ODY4`kuh_(&mZ>_s zc-vQ4ozE-L`Kor_^YR~}hSFx#&Sv+b*K4Pn^JAH-XxE0Lqr$j6j`rXd#JEhqC;3@c z?H%+#u5`<4D$xDbmee8<3`hN!w$r)9E{vtU{fu%UKyRH$wkpbonQ>F0_{ z&B&-AQ$Cm5#)+wg998zlig&aq1gv6e5c9v3!iQpv(nG3nqrUFvi<$eLZ)SJEe{7@| zzUO8)AA@TW>&=8CxL^8>SjO;Ap>d8OcqRTXRS|3Y9&$2?xK}Z9T9id(_gVv29dG2- zeNSTHy#Kh+rq5(Lfai{59Fv2|iN*?|`-%2CF;)6C>xpO}-V_c;2cd$&uu*CxgvH@T zexao^qTUPD}j)l^8|Y z@G6)bxWTbR=)eO$)Rjr=@_0|q-};CECTG5EV>0;-xB9MMW@9p6iRxd#y*kQl7O`Sg z94NVE{LI`keh_4J7cz~AyW6q`obIIr2K#S6r7IlRID`r(JgT9sv;3tpgW;=NIOLM< zy;qsZ8%Ksk-85*ijG1fsBz>x{)6Krl~1WjG{obPoND>q{A1718d+@WK=EEcDW#+DJO{y@e=zVse;()t z%pIvum%PRLs|GmIrjS;`bBD>(Ua)x?14Y0(hA`2@Y+B4>cC)8-fV*5(jf!HF4Bk zz2QJJtm;O_+>NpY?1&I$1QBs0kK6iimiFWZk_mU8P8&e9u?(O67B4a4Mm%&0BhfJH z*PL`OzbYX#Jlu@2vv%`~%EndZbyhmg-ylo?_-k0k?(dlZQ}OCI~&R{rCvsSGC{Wr)xGIh! zyJx8Tb@ILFuBC3}5~8Am*MlJ9E`1pgvE=1;9-zOtpYs40sqHK}F@e{{)y=y8N{s}j z>~;MGd|laLP1YQqTvpNaly(VdtuuZa(ufm*D+Ti8UlFIjAxsUA_Jo%jiP; zSg&5oHcU+{?eWK$DR~!2{OkwXA6<%q#QUd7wS;-P&_jl&&a@0(XOwzqIUZ>cr{XCT zORHkg+WvADwKXJ%6G6t({&_{UZ1*ZF4xzCiZ4crO+(BFo7x!Zj_r2nD#F1}P5Vuls zDK74jAnrrOWxBZ4LEMXqo8saY2XPN7PSR=M%?aY}vN%S9xc7s&8;GNw^CZ|NdKS@M zAH{KtFtr^?ZCmo=jk?1KsOfbDQ5{dNsnl3nf#8%T{;knod4{mkc1JE_H}@Z4RSw_> zL$FP82X1$D(h`B#pt$dYxL{aTC~jpC7Yxfh#eEpWQFIDr-&5R+<#CzBy{fnegSbpr z$McH2D~Oxo;vQGr4e_#Ws2);WpLm=bszSweAr9jyH&8O}znj>h{@Fz931KM@9ED-X z&R+O5#F?oTTwLdiwH?7YtY9HG9DE|1^)C7kNgy~h#s3nF37@pgiT>ixvt9eY?hK?0 zs%W3C;@UFU3eH;IRqpPf^6fvQ?P9|8pjmMV{Q1_bI7Q_DZJ0ycNp&{k?<591g#=n~ z6h9LqGfPPUgl!P1(i3Ye?=Jn zh<}7%CS>-$_H;QEac_=fyb76p7JueGpc;4|&TErU5+>j9rv$j4P#IPgh7`MdeU7`6 zDpZt5nSjuQwLtRO%_1sjGtMKT-1&>HY0IsXj@{z-|4U_|dB2`Et{$gAU1LYGqg8yF z$`XCXxG_k|!?C`p_&7q+Xs1AK`iXJfd&fp+P^|0XJtg5xdwrY7o{@QZK&|-nWeJo-bu$H3PVrgrpIKDCRnGN#w0vJo~Gf;=@D(_tXB$Rm2f};M= zAS{9m%agI^G1&m_2yW|AXefjm5W53WOysH~iOhf>QX}T~K<%#<6LI$IYIDZo4Mv{a zKp}74r?L~}YG%0;_?Bgi34>FCT1yG2HRE`Mnrj=!6DoXN!&y8j#k+4!ZQ8!wyX=Y+ zYVF6fSbw~-=@;>#_;$p5PlZ_g&k*Cq^fgnNH0*H;pQ6py$)=U*Bz+nu^am++HVp;l zFoB9??0(b|r-|mE!_{134-#3Qt6gGW619l^PpsrQD#?U_W1Qto#Eq$%0^kP?aN-bG zcBldF?7id4=99Q8v2a&=r{(@ed08dbUCsm>&#i%_gSRu|BC@txhV5RnenEzo%AlNQ z>I2~~5@1`-w8v{X-KdNY;1f9yzW;@#hZTPg?DFl+L^MyT0V}|A?IbTa#j}$VO48j^ zq?2tvC!zNY{7{Uxh`2!BZ*Rdm!*0>bejU8`-85WNJej3|Z|C#1sEw9QEaS}A>FACJjRy@A&!M&IIl=Di9UPA&>u$v%WyI*< zxM;J`v_e~NT+_i@RBFmxBiI=H-*Q=9Z&y!Dpi@c4QAEFoqF_ibV_HxQL|1rUl3cWZ zLOX{LD3nx-(|3neo7_*x57K@D)v_Yi63fprORRH&C8m3WUE(_yo6(jovPiFh1kyUZ z_aj?s4?B37F7b4RM_{ifQA^-AQen!PyVPyqWU*sCNOhYH0jGFH^zr>o9EOXb8_6X42H@hi8Li&v~1h z3?kT!*VB7cMoKOqt->8A08S((g4Zgzb=_@RN6;u7zg2B9Z)q!bx8|HU=(WZ2Q%mve zcYwYH=(YpO)LJimsB%)>&iqJeMy^PPImLoyb{cmQtavMP4&^Gm_Um6_>1D^a|MvTW zR%Y9E{d)?Q`Pn!|(D~Mbh~4iu_&5mW{}#NFOsq(#zk%xXx?2O^dvP`E&!hz204<6Z ze^r)w_P<`N@joG3@ig^z|o9?>2uOKkCVtdGu3OqH-E1FW2VVdOVQ- zT-A{rn|c@Lod9v>C-jbvK7s0aKKUir!LGVWj+99VxnCkOC%S?MUNHm6yCi3LE@1wu ze&2Z3-MfJ4Rh)#eX|7ykuk|-jqiQ}fSB3UK0($d$BkZfRz#x0?5TIWFzvM$W3Nd7t z40r+7`*0yqT3*^cGs2Fhmj5mY&)*pjfqefGh;`&^lJ`MZMO zANaLfCec-c^*F;&zx#EdhyDJ7C6GL3-<9?C&snjo((u#aSf`?Mh1QlFsDgu4|B4kf zX83L9i~hd0lIN}Ln9#d8$yZN%N89-R@5|Jbzx@*XJ?MSii*EiD1Xe1rkpNp`BcL2# zqMINlcVR!78yTo6qj$m4g=0aruanigwJ&E@EUgE0>8gZ=Y7_nhM<{ukl1H89%pNOV z$tYeaWp_OhU(r2$R80jNnRz^ai1p7UgB^dH^buV5TB&cbPNFMx8^Cc>Mk`j68s*x{y-(h(&qWC6TC#O-cr73r$I);&|qIki7zP8TjCOTlZb62 zj;Z*7_w^T4MJ_3J)7*!MQ@TDZL`@s42C7Llvc#QW94EUt!MRuJuf1Wc669te!BhmvQD?oUhdU8-6n1< znP0(H$FC-6={?$qW<0T(J&RsryZ7oAwOJ49OeIc-x$%OCAnvqq;B2h%x2mhoifQF_ zy=ir&dp{3yddYQyx!Av0IqhsaW&p}om+uh{&irpIsmzCgznn0yHTIYMl?Q?nGpL7c z@IcU=8E=W+DM2p%H(oa}EnC!?jS2Vm$xujI@MyhN2_bFO=panThP990+{uKXAi)P| z4nM`#{=_0tlMwsQxM8}`;QBvUV#f4WnfZD**`gnj#rI{PDE~iOX*GN3_z9#Lycbyv z1TfY7+l+X-i&Q#&hcd-78mm-2m6}IHnYGU>0x}pmt-F(&#I1b_UEwpnQ{lvPqjWoa z*8`cyX$)Y`2XGJY@cbkwoE850#IaWLhUZ3q;c<6GP4IUCAh6Wp@dSyPju8VVb7S{LXgS^YAihf$L*U4K z&uai{9cGO>KzK?9@OS_N@-GI&e}qDGJn&w7 zD8c#$WnZNyRKb^Jc>_s&T+XdA`yCC7Q+9+3XNoE^bhGkDvn$TNRMNy}1+L3KiaZa@gc&w&X z--{0F$!TI($*)0ColDafhgn8RN+e$InM^cme( zIhxnnJ2+p6AOEkSkzOW;)t>)A{2fMf=g~u;crUxqR`kd6%*w{U@eS#s)Yc-riE@!f zhnozOMP1g7*B;k-_HKc|j+6E;iQUtF7%> zXdpWCC0n!>lki}M{(ujXo}ozn2JP6vT?51Pu+{?Xd44q}3v?XZtt>po1$tYWIsvUj zIMh0m%!4=;D8k?TuMK5^tJhjzuEsN^;63(eIZL zgu8w-13YwmqR?5Lrc@Dd(MzAx?v(<%#%^)Z=CAN5MJhho>979@kTH2yNxo!&)U3aH zLzB&Ye&H69B0RSqt23}Y-gnF?Pb|3#vAuYF-2Hnl3dcl2*`Fs+bl27@ETn7CH#| zK+u%TDuXb}L8zt5H6FSkG{;(E!{O{`-cUrW>17T`*T-mv*)nzYh5*QaY!{TyvGH>5 zh!4jn3BeLIcA3vpW*90ZdFLzl2-5K+B}N|_U<)hBnBYdcRjkEqQ5~_g(dsHi%*5pA zE0VFOSS)eUU8KwYsM@dO{~y-==Mc|m?g3ofqEn3PcGkA{BDx7y+8ax44YkP-rFGCd z76J8hxc7?Zj|-fj$mEk^0)z+Ce*HJ)nuNRoVIBbUr|hwD8UGy^ReJCVoFIN*BE3$o zrZiX6M}1sPGh9viPkIfM9xVwk`J$>L+0z7Sm7JKA`mOX`#!tubrp<@eW)3e_xHlP2 z{sk0CH~gPfgtc}A%g2&08Nko?c1$R^I3e$Bn62^mmgATCCZ9?79_#Dm63+P>45JL7 zjIj+D*AAPF*sH84ObNIklTyN~cu>g2bKV#ABSz&7RR|4QfdXCJt}60b{zQiVAu|K0 zOv#`NR-||%{>IpaRUo?Shg_Ud;Vd-21Dhi0>-OwZ-c+3wPRMH!LC2lPv(88S8$7_H z+gZ>=uNB+V4eLsNjTE0>G6&GP(UV?tL|=YaW#gG=|6)5tOkg4hTz+#U#1e0t>{g51 zXc??Ws9FemWll)|&CEj3#N<;-Fx9C5tva%a+wUsNDSgQTZs6Au4dkvN@%K$xaRfo|`LXT@f3zkhQ) zlz%ulLYxq&pW>Z^9wHzWyITa0dIT@nd+Ky7`IikETKD*J{<&dVhN27;3nu{Z23*8U zLT@oRp`xS`*xwWCfD1p$r@votz_;+=fpocunD`lCExy$v${yhGU)ZNUxu^VNjUe+Lkhd)_Iw-IK+%&L%FW_)bQ8y+*oZcOd*E zO}qIa6%jdWXNEJfu|s3I4MNk`C))VTd_1VBtGE1lYP!;jWz3l;;T-dr(cvjd+Vm%f?Kzg+=w%L&S%f4Kej)gMLa7+tZjj`4kLhT`QRHTCy)QjE?+Zio z*N}|mM=~zzR1-WI%Q!Iy)Y#Evq+=Rgt(UO;X747cm+d&SJ1mZ#=D&E1no##g zq0&n?NV;kxGKXk?t0wO>xOBsccX zF&&05gYuVBZS`I+H@{fXg%6Mu(_ zZn2Z51aIDa$ap{wX^XpkS78iyP=P!uKr^M}|K$I~JfZYQhESZhyq|a7;tEqxw9hpr zRQO+V8LBa>*JlakI^@dEH@rSgU~C>a3Z0N39A{m{%4e4I@tJW!14#7Hr~rw6sq(Sp zLwSaUzPmBNLioRb8Z6Yxsy9~oHLFNN860kp5kV{b#NhV?t1=K~`{IjpXs9)D8SH8K z0qq|qD3eY*S3n;_pc^%1)vTXG|G()96+S_Zic|K>d2X3~|F4qm|6S6*PN|3xtRBv< z^DQj#HY11cwkIpRS5+d1^bM+<8h`3`MU7Jvyoor$^tabMHIST?d%gW!CdaaKXz8uK zsjjxX|0@OLC25a$>3~xb`~pfDGSOkgWgV~mF=1K=Chj^#mxqcoS<~kvAVM%T46&VyD&eO~tpv#pcCM~WMi`ju*$!AJhtCAYx8eLS|Dgb&jN zF>xDuXbLC3gZ&aRAp}v|@&<_!J@SBji&V{zh#`1xN^LT8bRy`%O%+oYYdRia9A%w> zBfHQm(0FzAZrBW$zP}{Td*%&0PZ;GqyKq;xq_Q?6I79vb+lIOAdSsevNXe(IiLC_; ztyyp{pguq~xb*>7a;kO}GAMDmOONP@3S8xtziL)~W3*s-OCGnu+zyuD%{#4i#~~ZU zCm(;@sAcaIT?;PbfmmujljOui#z-Rek3Y|~o)qt96Rpd7IaIhKq^#i@{?DXPv`t;1 zL{;yKmX)lpgjn)9t_kC5f;8RZT@&^JWs%S($Jh&fPG=eI_l}~w@FU4uVRc%%Ql~$u z6$ZnU?p;hl|2$H4+Afs4ft5W<&wl3UiGro=F~CWxHNF4I(2RE*u<8~g&+30-dXE$S zO9Zov8_eXz)n@G2n%~-^65;5f$kD@D(W=oXH~rpQ%7rDlwaJa{j8s}Xaea@1d#e4D zkY6=orblReqnoIWh4b9xHVnJ@tghKJTH8DCZp4$ZrYS|NP$s9PhUX1sHKHCZ&25g{ zl@sG3AS#aBP+=m7T>8RcG>t+@&d;V;2riS*nm@HqmmF-CUk$UTN+p~Ld%RcDkq~OW z7w4T%h3^$oA@6MG#Kxc(L;d+D`fHyw6A&|E)9 z$CKK_NaPd;{$u-)DGTRXk?4uJvU=qSCH$u`Ty!?=!M^rN;naOwvGa&Te>StOGIS94 zYv#2)QG0Nj2mzWy!R0l_*;pifd1(5J01171Pvz3=2eUJ9U7ua4wB#3Z zT~`9nR(Sk`CW24B@z>&3;s_AzvRc9^l$HNcvhqk1|nCn@_b!BbSwtrP|Ab(#Pmwc<= z*DgG2O|zWXb&dMO8nlWO9KYzk-g7Tvf^u5nvFopo>;~TkzD32 zu1RIpf66u`~aUFw&m(z1mBr}{C1^^4}o(7#*A&WD9P0A=s~a$S5}|j#1S9eo4Vh$X6ka)mXhEJrEstZhw@##&|>#LzJ!rKYPr7J>DJX z%lt6!G@?ETqJ~&hsA#OtiCE$bfQn<=jcgfF9&@|6Eu+_j)!>EN@KB5NsG?SBn|i3Z zk}RUkj>JwH!;#{BiqtiQ;(N`XqKH_=XU$pMeIn>QKtkNn!N9=;QS{p62ewP+YfU<5 z12-BRc5WGQtV9aPCF}Defc=u z8tspEG2;L>AkykYmyF#+KcYw623jJfu zCe`qsP(C=BZC)QT`VA2VKyifl0mamI)jwoBi@b}sRs#;VAyH?J&qoDDmYyD;*FM32 z)(W>>Il|grdXAVq|(rlYRf9;n#t|fwTcOd&PCQhlf@b%UY|lBo^GyXjna(`5ZbvyZL8T4 zkc<5R7pU*aA(!d>fQ@KR4iYbNiHC2rzC9Ks)^~~XNF>ik$|J0z2SDj!NusxN2mG0F z$KGb~%SKtJXB{a_mFBN9^Rt*GuN8_Naav(}hAfoL&6|vCA2c(HvCl%#jqK#$+I@~; z3Y1-*rq;eeYK2-ym(@D9yjEW8z-|)P4?sUFQ@3%w20)kX$RF_vkhSdhk6I1E9yaOz zZeq0N?oQLZa|Ch>qOsuk3|wDdPeKJl)s70r67#QNu!>vyH3-ts&&o2{Au7koj?{V4 zve$W1)K>g5&oftP<)?d7S4s`-Pa+E#wuiFB(smtF^Tn-o^@fqbpJt$SPl4{WS_;d1 z@7V>N&khyd#d=5T%|j%@GrRP;lf3kD06%_+Xf6@%yTQP&=%+d-lj?POfOXM9Pzx4U zg_dsV_6;@2-%rsNQg}`~(rQUA=2HP=qle0yG8)xiMZtLv*PQM4}~frQ9edeRHVrW;I%)K>f9g zwc4ovR256tbRX{-D$sKa+(joAHbVKpG3>6rtC2*J`yN}|RQIZKuO&B|^Cp0jb~8MBhsEe=wLJ@zzPU5QQR+8L zS5q^#ut&V&>+D$gyRV0dxjw zhwF;v#4wQ!^kCg7$5Rlh!&-?bE!jKec+I@uybkBundqgD(glvRUzZ5(?h8NPqA? z)5yOC777${#dS994eM)XcJ^Mq-;q@5-IbbL2q8&3{#(R|8)eSIpF$Q5d;D-Pq}<19}_{$g6#(L`DDW`HTeql7t4wrt-+zjojDYWOWaypdXUqdV7DYWi!1;7d0keSZIV)x~;7} zFpd5(DiGm5-zpCLfc!!r(3PIBc+0&A(t z^PDd6ewSWqkz|9#)x%L)J9?-=tMgV+-00P06=+2zR*<=hJlQ57$g2va6PP`0?CKR=f4amwh$pq87gYbm(IqI z2Wl1J%+BzM>?e3K&2aRQD_vflO$njGxmVG{UF6ORJ+&k&^yw1cvkcT!+QQeJI|9|=x8%m^yAga!X5rtoiSIJ3k#CK zi<*Xq;GCGp=(E?HnsAMD+GNb=)fhmPo7wTlEa&~o$?(|}3ZREBnWwFZYzS4*%;!5M zNEsa;_HaSQB%;`DR|2l&JqhSPmsB{6;C<|sYM5I!y#~|i@5w`Ddk5Ld_^7}kyV~~% zfoTE|j%KiwA1ALw6rrN#`f}^wx*Fot>Jb4c`(Fp)86b#^T{DT56}D1nGZ$KkGO@#@ zijO7!XL(0m%>P{~shTY4!F~@*sFMS%xIkr!H7VV@8QvW!%~l&m>WAcX0{QEPunrg> zw2mz+<+b;^fZ`?CIqfv0e#1#L83S%S%M^y*E#GJcSNJ_iRm%`fI1OjX63C?2p+1qN z#|CnNIcR+v~h<*?Enrsa+j=QprMpv&3t??fq z2Xk|uGRD%5-i(Oul^G6y+`_b?9IijmruDV^+3;`|-Mx-?GcA3rK*m+XdZCL{jK116{yoELCk$F<`!4@q*3Y_#EwrS6{=`>RXNxzkvO1r} zC!Wa$KA*^1s+jL`@-51C8mw&>Aj|FCk2X}y(T*IhwiheBIOsPbnvHgF7X4+y*~7~TqlIcVc_YKoncnFvt~)o_DtVoBMBebaMs=0f>PLEM7^{J^Jkmi6+6uIH z&=1}H3zfIb(paTaiP7(^{Ca2e_8a@}z2^c|>=kdYpW5(;rTy%cYRW({!P)nyA)R4Q zi}0kT(&_&dTC#`xl61O(k&^sWMaG(TP{CMQmq)}!clGX{P%R<4)vr#X9)si@&vY50 zQ<>7mszSv3c>(0@R(sPX<+cAH*M#Kj>QxW&%(pxl(;YFmne;Mps{yq+{J4Qk4D}l2 zb$EvHG1Bu!Yf2fiF>KmTamto=5u%KgH`UlO!=dFQdT(uDy`G~48}sifNVNS!WQZH> zVJn0voK1J8zYNvVyitx_y@g~j#n)u(Z_~mD|fXC{za%INdtwB%sE+IRKGb}Or zoBjsyX#s@LYC)prW1poa9&o9ht!QVjdXO5x|CvlWQyzcI{XWlc)&k*Ge>I;p_Xs8z z-sO$r2%D?HB!-LAUVNgXExBLYDyhd9Hw6E}*ptS9uJ4@9`mv{cy0)Fyx)sOL&jJuj z8-jl~;5YpBe9GhfF@184ye0r1VSo>MF$b6{eb53r(LR@M%6lNhox^Akqalip(MIpi z^(RY9IPqCIqPB(E;ZuJ&MVvqT3E@;8u=P5jmsHd43aXUaD5@qC6|%wJ~spIS3V?t#5z2g1g z<6}PDDTWs?BLR$NsTL}F=rg5U#g3ONKYy|XN6Tx)2pR%CDGhc`bc7C3(kk2?_j?j{ zV!ml_{~KYCCu|M4ntCI#k5i{G6%iD26W~L=2f2(-Gm9R7ms!?rm7L3obZVnY zelsTD3U{3we01cajCO)FRcc&>?!Wdai(bcy_}%@VQ-l-b2rzPryJi*K0l+UAA_KU0 zj{2Ih_0f)6OOG&|iuPLOF`nx`BrTRvZvwBObN?xIBQGf=rNXygRkS9*xx{!Fl5NU^ zQBy_AGULT6E(;arH|L0XxS#GloIsGt2JuHzYr(CJa6H09^dexrv>tYgH|PRI$LgHx zh|xhO8?i%`UmW5MAF(R_VThogU8SZt@itA@=w*kp_xp9}rAxAYBrG%9Y5Mh_2>bHK zE>5qdcXT5u7&pHM30{kJiGu5%1nWRdMKfBJw^ldeBN*5rR%>28saxd+@ZJT~F_b^b z7>`UKzaJ)&9a;QHM}FXdI&8AW74$8Cm2C0Hif1l=-<#+MfDTpt`$KIiBv5Y_S*<-cWyFHc}Pt4UM#4@%j@xWIu z@v8U;-oS?{wU+jzR++dlg#Fk~vh+7%FkF0^I5aE-_d>;<7AmZZHCfqux1wKtq|t#2 zmC4v1?^h08QOTEU(ZZ&r07{@sUj6@i)imhUjt?|fR&$}*NmeLUR#%0^H0l8xjl#`p zC3zv(RK*d$a9$ixe%rDHHByPaNo0rJHM1jQlJ-v?W3qM(yQ>Df+yM`kp;sIDWySsa zl(Wn7JW(`4PdbTc3>9ufsHnZYQl0({Vh5XgIzFLk1UnMlc9vas{Eb#!t@Yc?*J^Ny zs0%4yuE+B4CuqaD#SU{bu&7to2(|xW2GqZvKVH8zCtcnWR-erp+jzTHVyx*vXi1SI zciib5kG!BWX47i+>nxB_1@hW|f-vJTa)yn^LC#av?|pg)V=|PzOO7*nF5kH${26~AwzdQHQ%EvZojtI_97MBl4rj^uV z6yk90IL07vLgaSQLGQu z-a=I9HtKWoJqMdVeG`}Gu5g@W{f5YlUjbj9;Px(Pj58zBT- zA0w4sjzPdcq{WQZhAAIo%9o_ipE111!^FT^`6d7Cu*W1RT{V=lC&Lg_c4$`Nix|h= zKt{avMSgr%z#R{3_n-&QPC-R(J#-6zo_QC7Xenz^nh79eJwlZ&hES`5FKMhh9-Xf> zHqjhUt%8GxyH{W6!E@CK;BcA$Sv3_wNUAsLAF`h}<$B?_>`I#|1pRJ5WZ)e%u?aht zK?A(&@4)jgd9L;%HMI8%BaVR)a+c#1JO^(4DfxeN(;L?-nDbb~-;4{m?)nFU)*8Xg zFoK>$@TpGhLSkJMTRY+DSShR-(gLHC$drDhM1ZsPZ2hUG;+K3Wgeri~Bzu!vt3zNW zD=-(yj64tVG^85%yw-3K^)Xwoa-m*8Dk-$oyXT; z6x%Lu6x0v9c5v9PlQr;e$#YY3iWl;85?8AfR1&Rq6ZC;-dT4(j7kK>_-Y+FIWbw;wWYFz!o~&{`s=^GwFTN@g%x6&hkYboh_i>0~zql2@5thCx2jLuOYvt-7UNLHeu-?ASyCRds zBlE!@bnp24rOUG)#KUT?mO2P3^c5YLM+P8bVJ#g<9%E|9;c%aLUwaRYePH*JloPla zPw);(%CU1HOpT2n;BWnl`k2)&Gd_8nb+o4?x$QZL&eq?#a!y|<)%hPG0au`?0}`Yb zilFg*(AhH{;>#q!BvD%sE*}m))Yh+Xmk3untfFF(6K7_G796#d5%LD*OB&QAv^G5= z5Gvn0YX~~Lcy(4S1VY7hZ-K0s3#NoIcF?IOhc}AjB{@|y^{KsR0!gt?pY)x(!nt6#pewJ`>zzgH3mCbb%KbOz@O9Ssx=~CT`fGA2s&Ub z#17ej5mate8ln2*Y0)y}RmllYac{XLrNxJYjYw(q&Q;`_JD5NP3L-GTz z>v3TzDD$;G$$?)IZvgrgFwFy|dHt~N_oFo0?OTOs&;?aX{E2vjPc@L9>Z&jwy~$1u zVBbmP&TYByZ1g-vq?#vyi}ztY%RywgSY2c4b+J#t`{jjC0294!##6E7hB$$SJBho` z-IA@MX@C*0)G$1##-VeehzE5M9a+TD+tBGenx=bnd?Uv1V0?STV~PQ%agP-0rLJhX z@N_cw0H>4u=AOXp47FI=T~3*x?ZE(zFdG{sFgdy04`8Gsd>g}}NX1SN{{Er2=1mFL z;km+uuO@>MHz|25l+Y@AUJ3}R&o+RVfNQx<`WTZoVbTWxNBk=~zANKn8Gk$D?Kbl~ zT)nXlByEn1QUfG1wy?WQ2<|hqjO*h3I2L;?yUkD}6(!8MZ;><^NqM92g?tg57t}>q zQ6OXF6*%m`@Lmw)MA_Lywj8D-skl;C;g8#%oP@Uqpj2_zRDh#iu6w$D%k^kbe`rnz zq)uG{Y0K#YYSf#^E=jGT=kUo}ZeL-Ggrv)K(np!JF_X?@QhRoWGpL0w|1LzRS6Ehf zx2;NLq_}44%ypT0-xy@>E}8F7^4K#Hofq~=6IX$H39mI_oSGZp4VVb`Jdn8qa1r>LAap&d;lo+bpK!Hj$jK3M;CB-Y zfwK0tK5(9!rcVg&0tj8-Dcu+cX<3axFh{?bC%4bpcOuMtRVVhPfGb6pb2CTL!=ZA@ zSr{8G{s6hdcxn_|_O%EjzcWi1;$eO-@|UbcCN(-R&0H|Y;U zasvG`RIV%o9P(9#3h@@fRp>9E2xqxPXF(T2mUiJRyP2j;PT9zyn4GRG5BLy4w94VG zGCtov!3DdElM3>wJ%3^ypn=#oALxKY@KT&(KSjBbuZ!R;U4<98fVPLpJHDp@5$tSwN%cDtHy0dvC zdXMd)S>i{eh-EEjS$6BH>K?|VC+v=I8RhE*ym$-Pe+4@~y4!={-o0=a%!R!VoS5nD z5Z;94J5$BZ>vebrjU@p)USBhFwFlQDTqQH4^FN6}3ghM+mVO{&^(@)RW^W4a@;DRA zZgogx+T~4kRav54Z)+4o?m|DW4g_*AMoI%NTZ6_Cbb5paZ6^qi#(}E6Q8;Mt2rN#m z&#%bw%-=fS5S{M?9$9mi>wLGY1zxyC<>KYLhGi_%#kbUHcQLJP%6I{wh=W3=UWh1d zA)%c$Uy#{w#S-a(#-qkz#>uGoR-iM7ho#}=0-_m1Hx0sUsb|C10-?a2pdm>ifjd?p z@=&~n3=9eBLnOR{2jI^CN-qc1gNX11;DrG)y{`%!9LA}SB#MqiWL7RY%37pj4gN2$ zLD%TDxykenT*bbf0A9?yMe{ojAZB$jiCv@W4bs7Xn50j4)0~#i>a*o0fM*u#n@GGf z!flb`{R)PU+Ulcd84^H5-qcwTd;$ct1#F07nA|yV?y@xD8kQ4qJ)SS5+(lAKZ`P!| zB_zm_oalV~lV0m!tdyaq5kf&aj-<|ENo6+;7&Qq|^yTS%qC3i;HTQ zidwY2Tr+>flr}a%XKN`u2;s({@OsQvV)Rqk1!u8d+?p^d%NV?TwS(m3$3r%r!A~5Z z%bhZ@5t_J-G4%PB`obqTa10D+=Se#a+oA2uH1ibRL!20I71sKmH0ADxHMj#4Ox*Lv zM#F!#H!yQQZXY>0{$YF@azDBNe@ zd&tAE^39ig%~t}1iXv^~hcCI_k8dD)x1(_D!>9Cmvt=l(4ocN1lmY>$M0y%Mr?3#%?S<|M z>;OQEQ-!=eL8Z!Ea34b6bm<4NBQ@H)F)32M&I4K|8bPS2jOP3Tbx5_Fs0^(pIuW2p zDb=YM#U^71dTkPBi(l8nav-fQywn~*ks(b<> z3e+>W8A@j8Sq7UtmbF)6T)U@q09mOY=3?qfHyG9eBplZ>)QvzGxhH1s?ubK+E^iMK zu!Y9mq9pbD4~$EAL*gnRZg8TShB%F?3B~e@lC8~rNdgAqal<6_f#BE7LL%wk+azJa z&5^k7Mx5Ty!Axxk_Xivzxb@2KA}{b_IXr99oP3MUS<(L!e{UAg1J4IQSKVzme~h@!~L zFw4JnRYxsZRRDr#&^TuFID$4-I z_!cc|TuuNtO1nMi3A6`}#hg@~RTnSA`A1B0PV@!!*{`S$8>#OT#Z*}3N}>jZ7r=^W%bFrnFWWef+JQ>1RG>6 z7a(dx+8xE2t_AvUw%fMqT1Rx4}4 zsJ~E5X2E_-(4veOe8fa&ESPWuOi7LM*6v_Pz_(B!B6MrBWSN z&72PGxXs3+RPcdE=v&{#YLR4DZN_>SNyULUJ4P62J z;aGjfIh#}9E8;l-pwRoXt+dIgvBpK8YXAzr7XmWq*9tQR62|&ORKT={`<1|52 zNzl)O^ccPVcP4iA)u4_9tu&yz0O`43&x2*IGCn*)a$?76a%!UD+^#%`d|=b#1O^gL z#kP$4vL$+i_|2j*k*nxx_NA(hi?Y{kM^P|5ksZO5-nAxNF6~htDcwhJEz!S2@ax?D z4z2dqs*cB~>e&)hJrJAa-XX&chhl22cH+>rkPo58X;}+;qnB)BchWc=c9Od6J?JF+ zE!{`DMWeF>f$@sU2U*y&+}uW*s4o~PTbKI=YNe|$Ux=U2S_g(CVUj=pO=yzuiAX)k zpLCLZdXnFa+)3d{UYzYh@c7pp-_PH8yGj&R8uje95QR|x%6Nfx$D_xyDpJaEaf;P{gz3Kdqo^Q zUy#g3H>06$&Tx9GazvmqfvG+C4&SBf;x3reE-J4U2=RAI#_f+Z~wWc1Dk( zbP2ad;yZH-A%SermDj~<=VIS6B*UFPIo`612(kM|<5lgv2Y4@0o|g(#|76}pp1RI~ zt!hH*%ZX7d>mr0D2x~~pYcWwJodHJzfe$*~AQFBU4gu%`AfZS3_$`P>!Ci2QhyyMj zYP#;@GVBAXC3W4wZvjBu3y4E0qga7pwLBQ&;##51W~jBlZZiZPva7utmsV1e|KT zb10-mZ_8~2w%-$wEo6~19-9o2#kIrA;1Ehz)&hC&H83TS8>*ZNGXt-7WBIu^GXxQ;gSc|JB`BR6iAXhQ^uirx(pL*%J@!V^Y06* zghPh}@KHAJF9*lwzS~))#Wh1!%H!+E^=8wRY-JDKR_CSby@s87?@n5oLZK`YBp=>g z0-ob+r8q@+o^%^$XS_eukGeo^eIkV`DwNtW3Kfv}G7yxz}1{|EL89)~;WlY{;VizDbM8w-aMAqXzy-ret zQ?^c`Zk%|u`NjXWj9d|z{;CGX{WpKI_u&M8P?Jb=YLWU z2sHNHYlSy4SV$?TZ|SQiX~^5Cds%}kSd8H(enN2syL$`_EFJh`680yYkm zJ-SNsHK?BkT^I)bKo{n`3$O|jh7iR;bxQUB?u3WpXMBznR0VVXHES zhG%a~s<9u;K-cj@1%%7K`|2Yr4!>p$cL(lryNi1cN0&wW-8Rsw)zj_#fE~C@R0}zY z_%|R+EKBpRbuRS7EXiko8WLpJh?@jI2x&QSoKWZj?VjY47@VfViNkSUy_Ic=6%$F% z-?5>>=03)2PryTasY%s{?WFV0gCWCItSs_o=y#vsCdLyeq$HC&F+X?m$cX3BijnWr zy&#-Zvol5kBX?i`!h&CJcj zl@jIasvT`QToJA3*tQ{GGw`b9lDR%BxY>a~)Sh z3FU3^QZ&w=Lu3K|ZMb%u(3L6u#YiT~s8*qj7FN?hW$qDh-CRi;#7x&Q;JJDekxuc8 zq&M|8M61P2C5uB%RAB(3P;?*3O52j|62Y4u9TA5sV1Gf9!C6AkR~Isq7~Q(Tz{*Bx z$}vq~B7lh;F9cTOd^Exx=-V!M02k|UN#-c=xD3=U(`wucUUi(RefZ!3R@1Q%-8Zs4H+nx3sZ)aKkf24N?7%m{S z>_wdyk6fn1pephVYW_zh)sS?~|E%X%SxZ-;-BTcaVzaZPl}skD-J~}=&tyS_IbKq6 z1Lcr)Fu125LU|;n;8>Cy>ykV0_RFj~x*}|*p+xjmft<$+9C_89R_{X_wMX$h1^4Oj zl&(B?gA1a;jlL!*tlCvl*v@s(N6Kyg>rkAU%jBVF6BDJX6ca|>AdHAkd{dftm@{4jS_iltYDP{x4#o17}^)8=Qn^A@+%+w?&zauUx$NP zVHCb9g7F(Fu(C`#BHnNV@@z`U~?WRtw%|07HA?-l{N^^Chl(2t#uMdGYiKU&Hz3 z-8y2<2Xzyw1mz;6ZPpxm%w013k+Uew^>H&Hc*fcWOixwm)}APA5%a+X3@%lVL5Wr& zCAQLew1lkSo8d0_vK*htDl7esZYSM)@mBax5^Ab%fqUM{E@g&prDHg|fBIi)F3qV7b1 zwsmm$NLRgZ`I+MNU&6HDoV<+_ueD$!lmENK3{xUCmMjFFzAWz(<1lYxX2B^MdJ?(_ ztXsH@}}e`rJ$Ey|8XgPjGeig1YHUmEuj?n0KJ00YtT?x`+X?Os6|NfF4DmqlJCp zWsb@my|!HUNeOqKCi|KQg^dOo3e%mxGti=cqrM*a>cf*npeXbI8;>=d=l-X=4|(YE z<2vX$^gQ;Tt$IwN=Oj$iZ)fa5#1d=EOV|MV8bJyzZ7dZ%j|GoX zXnm9pK9$sG6>H07Z5gYc=nGzdF-9z=F8@yvjtB;$^B!D$9n|2N%g$kwfkkZaItbl( zH)Bi({J6UFdb2a<7{FA;oH&B9_an~v?FB5k z!PxPVZy#PmPZ*(-9>?uh^$nnKFQ@Ni%0>J!5YL>&ORi{JBdaJE;gF6RYZf(DKF%Mu zYf1~L@@J?rk5!U;fZ_F#n&ya~LeHc(_uSX3H00oZ!0ZK72V$TfK01EJ{s{mwm*F(xbjUI z>8|blv1smb8Ye(q_bf=%-VZ+64e86@3(Fyl;tCL5@+lU{6nE~x7`OG|F!*Gwr=CVM z1VoF}*s1NLu4zt}?q(|e1YqaVx7ZZxWz{PD7BOlSb5WUC=mr5;Oo~pj!XihIA5#&G zI8s&}5iTc^oIJq@bTN#4F?)JfBDDRLXo~KT2rST&0;~Tuh=?!@Q~N`D7$l%v@q&ak zO44wPgf#+64~Ynuqv1!0;0{d>aB!|(MW7r(GbAE{pc^D2f}lUagh+zOt4Ldw$p||m z4StsZ%c!d-;WFxWQhw4W6Z%eMM^sl0ttE~td&Q5!Ip_~BQ&A%N03<;T$03Q{K5!8a z!ku$&aj-aZVcnR*9lqGPm*(W1aOVbb@ZzK_(fcUdymqK_}yBA)M_Q5`tdI343a(3?|975&-$Tcz19rAF>(Gd{~|i>9&`{)U*$uQAhr(rA3EE%-yU@oMs=PyyrlRg9$thog}Fjv&ZA zNW;sBwq2c3DYreOA}xP7-PXKOPq$5{W4bK`wO9dZIB>+Njtra;4j_^UT3^w>$>z7Q z`K@bytDD~#{Duq?MBR;*q$1)(Bt0Hehx?sn3DT!7LR=7YSBRV6bwQ)guLQ@2(Z9$Y zN3|ms0o!%YP8?3WREJV>Y`dE~a3#bo@yal1T!TMI(1!_T>jagNpyW3gEMB>f8UEs~ zeC=*@33+Jxg*apg42Ja8H_p9!Qw)6727L|O?KcXzXg0y-N!a`4do?W=O7<>WUEJjw zHNHxnC?^*e1z`}`{&@R69MbM;h#Mer$5lE5J#_*CTOo`CqfhIW^`p_KF)u)D#T!@b zN7brh53)*G_$jzl0iH_>OjY0@>F(Nog}_3$`#5d^IKI*F2;tGXZ4%1hPd>5|Va&|x?EzP4nZ zBT`Meb0igcgLMQnQ-fLy-gIXRL5Bf?JfHa`5b8(j+V6mqkB<;)j{m*3{#MO#Gm%{^ zt_DyQU?z-_`M$X4WoA^*0!;p;cmk+A8&|;&1nk=Y{2{JO6XISB{L6BcdKn-L&EIPY z!3$so$X#G(Qq`Xzn%-yn*?ZQM^d7lhS(V#1r;g|ZzsHC>b&B>7Jk9NAY89Bvi% zM+blp#3Y!IBehxuAns$*?+l(18s*eogp=Hpwb^8oXx}~r-LEdR*hjJG4Z$2(4<)V^et#tw*MRR*V`zW)*D0ZkGZq809tRHRM^>THwuI z{Gp9K>hwVJn11%8PPx2))u{{b1y~C<3HYYuF&=y9ZG3tZYCEth&@&NE_+O??ewt90JOw8zSPS}~H3M)szcxl)VxyreiEOS;V7ko7J?>3Y9&5mN>Osy# zcC5OcrxI}<>i_eSG1EtT&i`8SF(4mW(#z_VXd~Ng9nfSB_^1xyk6hUO1s`l^t7sde z@WPZdB_yO(2y_38qq_HW3qsV%z=0W9FmPo)D$niQ`JbH`55?dUP8d!)0~#fOw|Hgv-`wA`$&&v{D@NA^vnf{I0P_zICPz_y~0W^c${_R|f#-OEN#@o(LOCTJb z3Cl>__9Z)qt4`8QVz=aDk98bf6Jo>8no_lDd<+^A!@2gTS`CtHyZjndRX{zQUkExQ z7@EJRL9qadQP@htb^*pVjAt9#u??F;drg;p9ncKeZ1kxwKNXMs5CA9A8E|8<+BieOMS~rMXOwjm>Wi4C`zI-TCo`K0d12&8+hM^xv`MJKdbKsC{BFSJ7D&K zlmEOiq{P3OR|gc=jQ^xiJ=;ha9!j{IoDDm-Rx43RI3J8DLR6?+WHWgnFt5$K4(+il ziE$J=;;In2i5~*DP0ObwUrE`v<`T(2k z;ny3(h}**TD&JyiK||KLRHPkBjG;QCiy(`dBVhKD|7`5ym}J&jRgx1u*|~?4V(&Jz z9dLwEs>Xs0jt_Rou3UOIq4M|M8=9b+Hk z6e=gG+c?=R1SK5b(6a4yzX28Y;T{ASSqEkP2O~ccIjSdU#<~G1NN*XrhR{j(e`J`03@E0w3diRlj?cLnN!UD$G@)t#EO>7->HaWWD?aIZjamzU&#Z|u{4#o~vlFiE@uim^Aod!(a zRKA(H<-|wsnYNKmE#3_*pc z=~|~*lssQpQsRzYqdFrmk7@RT>{Eg#whHlZLD?^1m#R$0rA~vnA)B>pE7`TL1rT}a zVsv5=`kYcLj_ZFrfrq}L5T~x0$Gf`6Fc#snJTni4T{e}W!mVZB78ZB#r&-3T$93e$>AwJ!D+J|P-UjLqK&Idj2ys}2X^2!Uh@AX?=KRH? z9r+2FkQSq}v2eisYbH!Z)Ws&~5KeT#n(8Hoz|7m5VfXs9OS?jxivYO4@v@OP`XEeg z!YfG<|50OXHyd3g--ly`f#)zAu!Ukb7g}JuE3O4QCm#*-hI( z`Ry-O$GX*wlGDDm&gyx*qJwjlD7a#4f!-$0@*Dk?fUW^wtcN4L4D5EEKx*u)+8)I# z7W|&nYj&rgL-^0Cm+;FSqpt~Y zrRX}0i((zpoy-)}A*kMC5=>;6$4c~(9gtBV#0r8g>Qrsb0V?YN;xOe}MN6)$6V*G= z_!HH!6($k?-|Ci@EM|R-dS64Ts^)-r?Ym=C4}x;L7r=?ZBNe;j+m4M})F7%frb~=f zw5%)WKkgzgR4#ztz~?VRuBT*n&GqGib;}21%Lhx_3W_A4z`jzQ1V;WI!EXf0ZAcXn zTrI&~@g_>Y8RkXjRc`?TlXPB%(mZ%7lkDUx&(?yY?TJPB@xr4Z){8fj@SIFm;KmqF z+L}2@p4CTi>AP2wH+p8gn%<>ck^Re4(1XF;3+id06j>YvMsWV4Q}ELs<~+72o!kMN z`~k%**~7g{Nb!xkLz%3?*AW3~DyyNXy4Y{pq1ckn=%}^lh(gJ*TJ;8f;#c?u zq=4Ev37TkFGs^ZI0uMwE=?88$^Xf|G_>sLM6rQ-~~IQ`99| zok-IpwI)eRJLy0(26h61wpFzSA$$DW)xxOq`m$}k_FYg0OiV23+H6{OpeytTH6XQn za${C{qfZ6*L#Gq3wxgO6o$GcPHR0Yo1VK-6aVew@^*>ZdD*rM+ii@dzQ%~Z%eB*p_ zB20y~;JENQTq&zC0rM$%jfd5{k^D+%2=)wj;A=eT8Rcz>L(O21sXj20jPOQZaTgRZ zL59~~DaU`oUEDj4k~?hkyTC>ti~3n;JYd~+4^}5k2q9A3+{8H7mXVS_)btat?uvLQ z3y-b;4(j{covu^A{;L`#$HUdgzuJ()j%yX(fqJ8DDydQUU@v{t42K2gcP@TIgy=?o zF#=g5Z$QSMjDmXLMMZTDc%inX>&ESg+^UvvXKxN%5mo&cX#>l!SqmN$4!@w87-qCD zhP}rdecDs77OWF6d=li~)@N~MTouj1#6UW3Z?3@J$m_o|QF$;0NA!eSFeQpF^zdx* z6W+9wagDCp+#7gb59zYe*8r8Npay|na%G0;@b}|ab^BK(0ujji8kV>N($<>I{gFku zlTC!s3wzVC6`0Z@E6_J4JFTj(wOjUWYhrkJ#p@p&uU^G+!;+&|a@mah7-sT_;Cu;A z1iDGso)ydkF9I`SGI1Y^Z3Q_A775O8o#LgcSRzH>@lC>u9@r(WM`^uK+QfL(iKPXm z01vhqQ$|QbX9l_l7k?!Ec}gPuNQHvJwM^Ix^g3(Z+b@2+wX?>Ea}A>4EF@kXYhI4L zL+Hg3)b1=FTwgx8G@{Gvp4O?Ptc}tlr^Ze)HKt-h<&2*by~CR}#BDvY1iRC$!2NOk z?5xkR8uv<6BS{aq_$5|C6^ryZB8Gj!t+Ihi^rUU}rDMzKP1`%ICqzhX*#s6OTJv9k zoiSwfEQI1SttZ#3*EwI6HAGx_;xD4VWd-iJOpd2y${h&SAt{TC2~arB#JVUPUu0hr zTLKC576R_rgGtvvKvvO2j7Wm7{8vt^h%ZeNeElSYxM&sL30##~@b_rz$u*f(wn+)u z*3wf_qH1gg_=gw@VkHMoc+$RwKM!yXHN`WCQ*X`+Oud|WegmIB%=2=SnZgPsna;#| zWPFADK6t_ndmJd`2@BxT5g>i6FIx8-pSk*Y#ms^$qrtLO*;UrF=)}x|-{IhL2i{ni z(kIYaV+T)?^3o#KS!V@K&T|EyEbCEO))#dL0^iY!1(!y%q(tD{kX_{r?q~qrkE%|` zat$&KT{sK-f-t>K>WxgmVL&=5Gj+!5i!{!=ch&s|q_Ke<0EG0H<;y^+$h&xTmG5a( zOmR$|z@iLk>1`~r<>$uvcx8lH@sMtQhM5Rt+<(G#pGvRr1T?HHskLA{t=a?cbD8L8 zkHi+Y3(~XWMwcJ&kE1`M;OkLvR-kQJUD**Hyfd;=?9N9rUC z)oa^t@}`we>5&!a92yiIBf4xVka}|Q@~(8)cGefwu`Z*Zj$f^03}6UUSzT}-Iy3D% z@(j#6sYXG-F}*ZJCI~~{fd8BV+-zs`*T3%gkIBO-YH7f&C+JQqhYl1Bz9>_XL5JI)8rOS5aziDU{@Ua(H9ac{4N z=wvJ!LJX3#e6qofcN$h<7xoR{Y7e^e|ATW3?BfKVMq}4#C`bjL-~yg$h_aqsjmIBY zy7F^?`1iY8WkYUB&_&L>3&0$*@)yt13u^t?XKzcxo**40D3FXFn(|zt zj5Mfeo@juhf=@sYk4tj==V;nJ9V#>^5QpJFie@Q=v-H4J0n!bXAJmjkl%8i-LLFj~ zo&N7r|5;s>@lcbA)->+}eE`#pxW&9gbcI`~or4;bDm&A-n)x*jqMEc2>Mcl2}G7&V9#_ z*IjTNc4Vz^3@rTbCyGL<-FWy2ji@l(c8$FmJW87xmxnXqitrD1Yogx5di;&ldf~vX zzV-ueplAHy#M(8mOArT&MK1uRMsi7}?XbSjqIw|G3tM>!LoL^haQ`1XRcZhVJvhOD zIWb<`J&q&R*6@cl8X0mh@<1F8VQ^s8Un0ue$y5-Qg|ax<=|q}L{X~6vsIF-UMBMjL zEfH=X9HANw!ZW~lABB3E#=YvmBs?%0n(jS5=+~OmtOgdq{umd{Vr%~?U1rx?Jdx_+ z*&tX*)1bNH=CIHGhwnmJx zyv80CbYQ8A1E+A2VvjG`qp1Af1!(J}kVEjMm$bpE>3*nZb+gj_FOEUSB{+u~QpgfK}z5;;4G;C$wMw zrDjw~V|V(Hq*)NDdE2u|1L6rCxjg?3-rV980*hSS~<5&aM$ z=VhXIj1Spzf_tICsUC>ro*>{Uft{fG@cvU(x&M^63N5kZ3lD z2h7Gz6%IP*5d6WT?Q783nT_M@kO<#3)*^8^j`4n4IM)#M#(5H^#JP1bC4R|{0C$C@ z!6#@quI>J;!dDQ@TPQJ}UTb0Rf_@}@q?!u*NwwxPr8uX38k!w4d z$2U0BU`q|D*&QMqBadVcY=?XpJGc^a-WjaZTqSpXFnxrqU&*k6WnLyqvUq-oI3x*; z9JWKP5N2Yi5gw%bS+@XV+>-btc+rZ1fxBiI^X~%ZH5%ACrq0q~~z2cmiQUYHF?u3M@ zF|Z?93OW)k1~>HSTZU0Y$r};71O})t88;??_KT zo^s>t6rZmlTV4((Tk4rdY7!T3zljq;XbM!u+%YU$~e9!8*>Fp}-<8 zZoEqQP;IG*1c+V&I^8+{LkD z5#~RuunH%?fXk~BScToWD(gg@!ilQsMD>|S*4HBFA?ucU+M8CYD*P;*D z)dH@wNK%$pi!egUu5jM9l6MY{hC^@4k)6o<1@q#z8eR>NEU%d?zHR_MUJfzzv3*CS=c z-HT|t;Tn4f#-8o^?kJ9&CO~S&x}x^5HyF#&i~Occb_yT3?&0eATTA%!fQuQ7)&-lv z=g@%10%4~nyeVTn zm2qF|IH|)%M!aVdfo>MChH*4_(daI}Y6f2Q;3v%f47=>swH5_;cgY4`g-)Q&fK@y9UYFIWiH*oZF=ptOlu# zmWQCSz#+^)M`r!iZNxeY6RC4gHLQ3Sq-!GCi^vW0sFpg}B_u-{u{-O{blZ?l+oPYc zM$SycXRmma%dOsQqQLvMN2CA;IVpX_BUd(OjHN6ed%_IuVe>7~pdbkJ;l<*9L6O=m z8w2RtX8O$bBd`P3UrRJvO*f|JWmU0SiDB`rf>`7R?y0yoTt_?*q%!myi-UoJ+tG}W z55k_r=|9ifXS@k#Heq>pD)B zSo*&~GpgV5XCPMckvjhyt=S}9oLR`+e$_2&akXB(ayNF0L3OmJ{CxUOEN2m?(Y;vj z&JP6FwBS+=VJwt&HJ+cYB_6avCsMHu$spXQjres&-*zR4ZhsMqOJ^{?)k%DzQ@r!L z)L^1e(8KvFPBt(*sNb{`Yz=GyApPg<)xggIR2Fbx_c8)ADuuIxEnEv{#ydyfL#y?@ z38jpA6%H0JRL!ef&~Ic6j?<%ydyPR`MX9QQ%8^+#K2$&af&QpA5YZP>S3xu@-g$+H zW)ETi7>sG1ZbVeH1Ud2BsUP=b$@#dReBM{Zd7`mAi_pQjZY&sO*EoCly0C+cN2+M6 zPHQRc_w#G&Tg?zR)$=GDVoxH_%0@A9?U^jA77l=%c4H~5#U8%STwm#WGkQXn6%UwN zGMt=Cco{{2j^&upX@Ii2vT*K20MU_%IHA!YnqumR!%$lD6-L62Tl zPorE_p?!RmXBjv5_&0|hMLaD}dD2!{4~vOi^eOeOF6SAIt1qfWQhGScdJ7q^2sq)} zE|FWsH#8YVnv7A*N}D0DfWE3`J@w9 zTgX_jkI+EErg#i&J2mtcn(9rf8I?_OU8mLD@t7+(4?MU-JC)q$-Y3T2k(A>i0tz@7zH1rz{t!to; zxLC0koUAJKeMUor6LpQgHPG!EIz>Yr1AWgxKh@A24c%#=iwtzJhF+_opBm_71D&R! z(Hi=af!=7KH*4sgno^^O478JhrfcZu8hW>Z)-%v18v2Na9xl|px^PmrV6ckR_f8GH z!Q}nLKo4kWFAZ&KpkEp2S`BTYp_L5uMFV|9L(kU`n$O&&IXTxr;mC#kYP*K+Gtk=% z^iBS{j};Se4ZU7Ne=*QR18t_ERWx*sfnGYU>l>}1hpI`9-ZRi&4HRCh$c?Wx zbg_Z1G|=@L`m~15GSHU|^eqj&PeX4v(D?@H*U)|%+SfpDH_(Y1+FV0Z4Yap`=4fcJ zs?glbKvN9#dJX+qL#rBSRRgW1p`U8##R-~|$Bt=koE77R1Y1K780hx~`lE*4p`n`% z^dkfPLPIk(^tG89^Wz5kjD|MS(6;k+`}z!YriPxYA~kyPMhzWmptox1Rt+6)%I#vH zxbn_6`k#ho8E8WTy-Gt1H1r39`ErS_Z$%BgRYPl=yn7AwcWga?`34QWc!#dhDg!On z&{`ULz(8L$(En*@Nu1PZw}BQKXh1{1*3i`kI^IC<*3d^Z^hpDC8z>a4q-v~&&M?sH z479z5cGS?@4Yay}!reOa#%kyQ13ht6bK@MB$3VjF%0lzu*L8ij8R%{e{a8bXK3iW_ zoKFmNxrPQb^vZah_elfAl}BP8r=hf&;$*wXrK=osI8$#Vuj`g20Fn&@72)d8oFhi=44+39i*WF4gJJGTN`Lc z4ZT%Eml$YG1BK@W*64Z-ooAq@;7^a-I1fbtpwSw7mx1mu(4RH*hZv#xMg#rKKv!t! z8ycE!pidbnErWo0nuaDDXug5Y(NK?uS_XQHfsWPCYc%xY?V20i478Vqo~tNSRWo^; z8fbG3E!NO=CNG|am-<%L&}TICT?0LcW+yik_M4!2qJ}w6J9Hq@x1hJIt9I}LP?hW;2WG{0k@OAYjE4Sh>P{RX(DIe=IivYp#L$i68W(h-p3Q9|cc zCQnTbX#mJ(Lj3z~h9qANP^b(~|6VZ)?nFZBviBhz>CBc};KdQHJq@Rbx_3o7qyp4%DEW52>+4YxrNvsr<1klKWW*2gfN|iBkKfI4xh_23OD{xV!0n=YQh@1Ge zsyYJLY0um#JMEE;d1}f1?YHNF@8mGDDz-mzQ;jo~x!uTsZjebN2M0odh?h}bfEK}- zFb3)m1Rg~IM1F<@49;V4le+d6l!MdrHPy6|Z8$UqAgm+G<&_v7g$zjI)*PB0Qrhqa zl<%QBANW8XAzZ90nUJBVbT)>-5Evom zC%SWQq<@`VJS=nCz5s5G0RVS#=~H4FbwN%Z6nigD-8Vd`1&9yq4LyEz|j zb1pp(was#*WzSb?z?ER=@Lm0i0IPPSs;=6tB*2ax{zF;SUT;Ref`_;jm(dwD8-z4q zQeXTyADVo3B5*SXAjYf4tzyTLX%iEj=_rDl+S5!w$pj7~fTlaOLz?bB6v2k8RVr7n z)CNF#qD+fPVk%gL zQ-C$(OU)`AhY;+=9{H7wYwz6gE*|;82?TU%z8h$-fllHxuG7fxX$}<@z8hiHk>88Q zTA08dNKV2LtkaVNa<8CEIMsp|zZ-fAX5!*^^Z&qey~cy1V4{dVTe1ule1(+Zs#0xt z+L>83+|j$mVppB*8zTzNl0Cw7Zx`0Bi}U=StQ()jX$9RYW=mfS;5aO`GcoY{6IS@? z33WZeVIv3~_p!-R&e+3T%VP()o2(7Dk;+Ce!l5No^kbC&cl9!U5r(U5+CHsaxs&s# z?Mjcq#IxYr3UdAKAT!j!xRn=qLN-!sohEUjb<9Kh3iC0%{+^79iZj&9=#X-tU67CW zRqTQLacmE#ju~PXjSCj=@j%BJ!;|pHx?Q8vk6==9#wi$zVhw>UZ~lHBd3pUAjt8&y z2HLy*L-C>sapDCIKU7^kKdKP563xzt$0~x-y_+hqyQLrvPN%Uj%zrBdzK-QlSvtN; z!J0$gcQ8^H-*;#0i0?c9E9d~M+^E|)3QDcQ3&5Zb<+7+6E4NDHtu>}8`_AYQ(*bFr zv7Jn)U*TQa_Ny=}J%%Zf3S55&ovJ!h0VXCEpK%?K<9gp?a*daR*Le{fl~HFE?M1Pn zi|~5%$wl}$wVt(>J2Yj)*Q-YpkM$U}aPJ2kJXpAbUoaqH9D~Qz%lJhNE`KjIaDg_& za&$FBCDeSAY6er`s33gIacR6@@lC);ZPO1L^W&-wzHtKniNz1&! zuw$=baY5WT9Sr8uB@VA?e!{{Fj>U^jmfL^8xjQ3tN7E|&7%hoWl2XtmkDf3Swtpv8 zzpath-CqrY!JmMn>Xgh$KR;3*%*A77B6PyuwAHgKJMB=7lChrUqAm^wn_&&{rhPxP zic=qe_Nz*IyI22FAum>{=WL+tgBb=sLwVT=u1j!Zz-@Q3@WjP3_v!<ez2RRW^P2(omg- zcd-F^!o))Xzn?g?}JWU3UU5L?MqwxsDVRbcfhD?1Brh%2kDDf*j zNQ_BGal}-c$aE+RQ!Egv0x2_2 zUMzVv!fucSy$Vc`@2i63M-YV?SC1$klvG!xh{sCT;wm~ofy<9s?kL=3SP%Q*^>E>R zGoUz`e_N~YGXKV<+v+FvU|o#LxI-`76yGggw_&#juiN7Yk?YQJBxVwV|CmT`W{QAo zkVbpuasyboZwWddpTG!7pG4Zn0r!w-4;Lj5+@!=izdr&oBycyFax0vp!7`*bLE0FiaWl52^nhCA#iN2JJ4)S$kBB}+ zm=94-NpCHUY-2+Gv}dAoCj#a1loA{Op56HP2cBOD6La9A0yK*p7jgGMxjl;!wrhbF zN#CXU8>YA(&JD-G`D+10PdSJmuYEh7k5(8?&x?s1I_ygB7Mz+mTcm(6btbHZ9=`_} zN}b8Len5G*h7R(&BZF6@tk{P)Vc5bJ(miio959$Q6XSV`0jTcxO1WF{g95v88pFW7 z{UaO?@)cs|+eaZEMo%WR%_wHFQ`10|oLbI4CUevofx1pXE(Ekab^xr^SO6*CdRt;e zq4(OuzS$A7?lYeAmm%$1i=IL|!{j5hNUQPLn2^im(@*gJJMWy0kYNAB%$PqYQ-&^% zmCs<^bp8O%xax!{EF0JNz5ZL1RDaND?k1gdHV8Z?49`hYZ2@;mvgFM5YJ6kL{F>M? zn*%>Cn1g#fwmwy|QqYte(moaFkmZgMhjS zIQ#FP2!NvFOkle{FP5fgQBOC;uA_R^*ecN2J2dp&@1a+K0I&>DWKEsIk~kllsX26q zzZwAa7UaJkAD-yb%Q!N>!svG!?-vDO#+mu63y=d6_QlD$lDj=@mL+adbFIzX4a9uT<1clpsAxNQ3z69!!O=yKnx3xRvb5nb(%1w`jP z^xH$MFCm7%CkU}qpJ@=$Ns7A61Rs*|2d#9+I%$WwK*#Pq*I-Nll=<5|AcbDkde`l^ z80y?=l3W2O{A5s~5I()LW@#5@-OH?Y!uM;XfId<{SEm_r3p@Xqui5zpl3~5cP)npJ z-sR+b&BXA$U@*^cEQOeiCCkz!$l6zH7Kwv=a7J*5`7nLlFz-1;@pk~y$h#FXney=p zu;XBzhT0Fl$g#2gK%&^)#Bs#k{iNuXnu+-hob;@Npo~j7+$_L$hm*R(5j8r`x_D!# zn>nn#d4(d=zl8=q@sq3 z-N7~Pf=kn;eI`#0K)hK_cV^Bjb1nyaZpi8ue+TS^Ooiyz>}X6PPa|vA!ipaMK3Ig^(=-u6k<$v#X8oNx_X7M7?T*4VRI68M%%T`y ztS4Apa>`TiExhYsNyNRN?OFa)XFh%4=Z*K0%84S0>vP8|jlI*{+I zpC!Dp3b%vYjD?e>K3G(Sv zO>hs&TKYZe5s2|!ojLbPyF9COO|-9SW^k2cD*>Ocw9B-zcesPAOI8&GEBNl5-O7I@ zb2*grQCTfFz->#W;qweV6oGPH5-4K_fu8ZE#GrFz2<=<9eGIR+T8QAf|N-N=2 z7@|>Q-K2|X-WF5>G|jQ>D-iZF7DD5?7xjojJy26rGy@fN7wp2t;iWCy!40U`t1)?J zGUr}yYZW~RLJ(h*4{QOj5H)uDzb`q4eeA*t7z%}}^3ajaXDu8Z%$)mIJ3gqewd6ED z+vZior@Q4DXBeiRl1%g?j@S09&V1m>y@DR=@o)6nV}taDb0%X5O^r8gzHp6q?gn*;}#b^+yc%Js$rmNPu>e z*H+5ZI?@U`!`18e!&gBxdkokjt{|z+`6vc1IhNzsS=g0sb7sXy&B1G$@MeEF?^^W^ z04QUylv?BO77e3Ho|T}@NhNCF8`-(CVQ*nC#2GNiQLBuwk; zu(`o@I#nX%9v2()&$)z$ch^tOXD|$1h?ORi<5$mNIP48vZJ*8LQ?S0#3B6h%!O)w# zN-u+Dj%>%vl2(>`hpcts+g z!ig}OT~bmR-Ej|=r{oLf&YhFuEU#Z?kAE2^yX3(_j!3)t_g_L`PFGrLmkfA1KmHy` z)R*`Z(YtyB8q&BRJM~2Bb&VnrMll=&MC9>EFwDnj26-6~trKc8twO3xf*KKQH*o=* zdLNXBT=4_O4tklau@g>c0r8G%T7@_IH}3l$V!gu`9@Suk%M{&^u2Y!6Zc+iLu~p!C z(aEZMi;?aeZww5Vr0j$P%Vh@Z3FO?NR{(P4J}T8nJ$$4+=81jI15@I(sqQw!Q?dOz zXIwjVvPaJvmlL@~vI?(4XG8SOCOWl|$jzb(e+oI_y%(>Q&wCe{ahP!yiM@fzknK+J zO8(o6DjVIfRX7TX+wY;)8Xh(FT8o-u4aCkHI#b+Qy8>hPJb94vS6neGNl(@b%z% z3TdE4ssCBwMA4`0aGbx`#G!eT2P;T;!l?-VO{{*=Ge`!$KF-v3n@P)r>35?=MC0-B z9^tx=&_L>_KQz}Xg($uS@nr8oP4T5Zn&JoRYp&m6)aoxe#isX7%;>D$1&2?-UesX$gN{YVbFchwPB@vcakN>6+*Rk1M_=VJP1?+cu z8u0EfQo$#X4GY=b0MZRDy2>TI_Jzd_`<~%ov>L|*=y03A;hDO&;T_4c;2%~n4#WeE z$kqy+W@^Vw(ge8j8{UGfB97R#e>mL)Rsv?Vu6~t{GSuLc# z4NAmeZSZH{{FO79ov?PPz#f)rbaf5`gjU?b*$_Tn0rbF;7U-#n1gu66=P__!y~qS1 zilkHU7%@!IZx0Jacq9qwifMJMsqzWl8dp|Hq5D7(8FMe`@dAWP79BM`U)5|FC2t7` z!Tp(FCMN(pH9b~aJKU9^`$mrL8_6d6HFO#6^3CUCZ=P(t9jtg7n&kEDs6&u}y4bv` z9z5p+Ss2SM@b>twzBEj2jkgHeI5~Fb9tTuDkUTlyO&*@(-w#P^BqgnUr+7Kq5ZiXQ zzp@O0pE2%)Xtfnzz}X>N_v4jyHLaf}0+pTG$V8v77oycFTu&C2n?1%Mu{SUQv&SFw z#B$d{-UgzIi38cu@$bt?I{`nvc<~j>mq>&_aIjyX+Zks9 zmOL_`7J+FKDf%Pr<*_hUk)SAVz=inY7R7MAHbIH^1GDHaO4m3w_-; z0&fN$VFO3#dw`OuzN(r5-y@+14~v7n62Zy-OUWENNF$$7IgA|pVIqH4;HgVz>4xyb zRu7$e4jA^DiQ0g+F^#)~jhk;8ckg1>EN`?lD=Zy&qzF&01KX`^M?U$FWTE|rXThWe zEr%*QE;5sRtb;u=Cc1tE6_3j>tr624Jtxf_ zmv=XhDbr)?`crUn^CSdhxYDeo4>IU^X7WE;&X+urzHj240#b`^k$={jEl&B_b};FM z)0+v2Rz2J~$?{K5v1(1guWrO%oO7_A-TY-Wuv>g-6b{0^5R>0iAG05u8FvZJ-l%o{ zm{ju+%qqGL$wCqf+m*hLC+*&NT-in+)q}CG{{cN1m0TxbrVqL`Z3%f~0 zma4^{d=Y%UHJjX8%iQ3V~jJY6Z!)um{ZOjuj@LU{Y%%$Dr()BUCo=E-{0>&EAtDw z(*qkq)33}ATsQtTOr7T*)FZTLb0eZ?93qFqG)mno1FX$b%6@igGtv9uHdqVRD-WeZ z20r>B88cM7Azlwexhi}6kx_3uex_3{K zE}UA?M5m$ChlXz@0hJBkoX0dL)TOfF8=jO<=Eqm`@O`7EMtvHULiDpHdT)FgY7Sb? zbraZ!hupv$epbBIYLWpn4NkA@b;ZV^aj zkaPEvg~2!+EjTgO&T*)^ z#%wkQ{Y`Y^2+Su;3x-6Ib_Vm73;*3YEQ4c#LH(NU92dIj5?7;bLqtCj>KtYv{Be;c z`o0iq$T*=vCgU(P(y$6*J>;y4S!V*DPXTpa+~vEbK+mtYVSc?G!Un_}c~7M{3irpb zl;&fSMZBJ5$He)Ew}$ki_EA-U8Ds*|MBzCT@DkRSCQ?>UwUG-;c#=uL0{X8><{4yw zHh8}l0q?89G27i4v=#07@_8<};WvTRwcHfhv9a$(4D#XCnW2kwiVUJhr=q)%%M+N2 z3FIG?NWk45tTBkj8bqb#2OFC>8|92bzIDAA~i1SLX{69Pnt1h|t#;Y)9d zV!?{SMM6iCAjf&w3yRnk8y1wJ0-*-53yKI*6`seVBfaGJ`ONO#U1IsZ@B6zyZui-p zot>SX-JP9nEPX14YR@-`j__JU&V5%zwgiz3UypG6Qpx;2qT#ji_QhH)Q2ph3`10X` zeP#kv(1%b3BCN1qddnKiewnv&Ea;Ko#9?>kb8pY$Z6Hlqe33aWhHEex|rHxzHJiviDnq) zIx9=ZAy~0h4`;y#tw@O*R*_I4s4?3*i`8k?*dK=|b2s4vSHu+J8gOWM-2&tGsT%W( z;HBig6~pyf!k%_kGc-W^T_i_G@tsmEFGiHuQaY|h+@pwt&?PW&$m2m=v_>u>K)%@8 z(|5i}>~gbA26lQNLY; z5QM3(3&V2pEPUM~l~zQ?`?3_=Z+e!cW$BV>|o1sL`7eiT2VlGs~B8s3brFWE0H1Dl zl^ofpyBdskK6Eu|w5!Kb^A-+t78OuNcsEGHKDoW};0|LexrhCEDr*AO@kQDs;J2-w=`^3z%Qp zstX!~E(lG2@v9O3k3B&a@>s&#D8HQn6yX1h06^tzL1cvg3s)k37d9PXd>hYrRe{eR zX9cqNA4UYb_p-vxKyNRRQ7W@Q;{$B8tqmi>r-2Y^*k&qrAy1}IlWi3+YNntXrFvV4 z@^!)X*f6oh+6EzFamOK^0!vX%#!b=eZCUiBXn8M}>=VCvh2`x-dFyt}ly@PTp4|pG z(5((u+24*x)nBeaTw|3#v@%wpeUD6GQYHfz@QehgW=%V57Qw$nI|8a(RQz1*;nDHv zf5#Kx9RNToWT+3e-y)6BT8{0AEX)UcOzVq>wj;Vb6m-@4(00`v+85Xd=}x9#M`o9i z9v9YmnX_RGV@r|WOqkUz@}2zU&KZ(XvRpb%`n4cq;@|0Qbfuhsiwx#Y-=(K zZO5^+p~MFXfORND3THWVKCuk(OSs`Nlv!P=<&Pp>#&JgXSrPMQ1a>EJnzDC6r(RCMV1;R#j38<`ENm1ghZBE!8|r7dn! zm@m+n=c!Jxkq+(^gZT?a#TpXtlmMgfuNkSTR2iuE+iXZ;N8BrxJ74x**CSt|ZiEO3 zmJq@D6sUOG1CF44PoGicn?Yd-c_!%KZy;}GXuVnpfy`az50gvmx`L{&#GWK{Komn1+D1#@*Q*y+$pe~sHkvbyGv$s3N@6o$k`Y@> zPg01sbSLiKF{Irgq^11yJWHib4tO68HP_BsiXzw@1;F_!0JI!Dj{!?b*vkaCNB}%> z168^AoKOvQa-uZUKL)7a^wUvx9G23>i~!pJ8?){(V>tT@b)Vo;!eS4XT6I8IZm&m+ z#&kAK2$B~!&)KI4!hDtlO-B;6mv||XCoL+N7+x=2gqrHcLk(=`G0S}D5|Zt^&;p>6 zC_Mrj3K53kz_Ol$@qqQ!3Yd-Blw3GOte)2edTEuZ5!+8FGdLsC7Lzq$u-+7+{Pv%} zVHk~$oAzW8=ah4l4m)Nqbqd@U^#y2L15nr=_Pr+3Rs)D=b-0O9b0jL|Qsxu#v_h0_ zc*+9Uubtj6g8s{ zlbW{H_n#xTR8T`1rXnr+Mr1Mt;#y}YMS8jw+8=kjj2IaD-v_ zC8b>^{VA%QmCUG{fYXdTu`12jCQ7BbKxlWB@Wm$3f2$7e1vxj3Xd0tn@roF{cBlYt zZdh{<M-E(1)f~s11CV)x6Cux(AfmP>9oO%TS zrfxL`^p`2X{WyrHYWH^dBC-r^SjqAMYuv3$mWP3ms4T-;(u?YUuY;M z>#9upBrpo37pknTv|~troZyRZH#PPNd?-XW1-ageKz6Y1AmmeskC~_1;{>z&0tN4n zVs2}-Cj6~{hs>XPN@)B9lWmazA+xPj5PG}wHlrFelB^@G z!+sv$F>|-=l2gv(DyQqf4V@G0Kl72(FSoH|DGx|aGMS%;=^N*Q?W{q}Y13AA035T< zW6OzIZ08!n3zFQuW0n+TE|RhydJ^-iNC+t_S5e=gB1OJhGj0mvtno9sn?|;QUV>3$ z@sq@%$GI&q8qf;5@hJAoVW*n^VmXs!zphLNJFMo2R@dO;+6V-Ow?;Q6D$vnl&`5li zXblgq#SRsA){}wOei&0w)gbSHAvDHx-wqK4!RRBh+pyG%k`xGmo+;5?E@*MX=Ak%E z#E|N2rT}uH0NIw8@ih?7(LMM=bV1=fv7)3Le@(HG_ox&@Z!pD>W?AM z0xYcH(RA|U$frQLR>j__Vy|PlRO|-EzK2*_ z?o}|~ASY!=A4D>4mcl!`tH_VA5D9}^gLtYH|Th^x6AXUj(^a^&qsVN zE=3k?zT{K5Tu|b4SQcgEdS=XfFIHp6)&nx`JpkTbrGpwri1c@-PS{WDgsouSP=>%> z$ig6vtZQLB=!h;l58I6LaJm75Z9l6{ha{9&+n$pmM$)tB+_?V#xYX%WM*m@*U-Su> zxrOmT&*z`3qQ7vRX3b`pKysIGM-~~B&Jh4(Z~Gb1%@Lo8k)(SXdX+aG6YM{oWfP)N z=qZ*ixog#r6rMB1Q;v`ZJH~8#TizG1XqQ6&hdQ{f1NF4C{TgOo~Gz z0UE>{5DXkm`=KpD`GBg@E3HjI@H{SZH1rk!fwz3l!yW8^*Ay{N#DOFt{Aw>>IA?A! zZDv3gw)Jg*9OT6&S4k}<1J=}h9LOL-S_oPwQg-au1dkD*$^_#C2Jxp_dvNTQ1P_x2 z&b_FZV5Tz14CV-OYe)&bctaS2i>qY2U_*OizTLrtd|1OXm@kMxCe|;%o(qbxPE(NG zCmAN?)GI0TVvS|dh}l)TlLqrZ-Z88ku8OnEMFcV@g&9sVlZUS4e3PTAJjhAxu zeQzaK4edqTZWPLFr-*9>;$+L`KxWO8fw8Op-q1sk#jlLo8SwrG<1b#QW#Fx;|fNUMu2kKKj0PY&XH-fE=AGs$}X#;EzjD@q@DjFVFAm++`6KwP0_NCf#uZ;V+tsKn~d z3F}9g{K&WO5h>TCvBPqemP?Mk()wX~cM8q3JTAO2Rz5$L?!~=ss&JI{MLk?%{CgK1 zokLRqwu?1X{Hv+U_&m{K&#Dg2gRmIo>xe`}`PUDY2F1T3wgt^UGpGT{)Maf2i=qtdKRC+38qzv2xC#Sl%vB6*3^dR#Vi|bSdTO^~ zpwvG)r6iv)@Fb~0HxCBp8wP^tT0<4lCwD2LZ{t7`@*KHE1z#0I^kaI}*Q*7pE6fqB zsXIBk|5cp6?yefi%Np}!3@*StPX}*A4H$M@tuY%>-HS<~VztxYhHfey8hZ<1NpGv@ z>!gwME^!3c*1_XqkZ**ZTgk4UiWR3ytll>;%IcZu9BJ6E%Idzr@)uS&-KAK4aWXMR zSlvKA|HA69Tgl#|zbjT>!>f8jp7lC-HN?g+WT8YwWp6X4@`@p+A?JU?Y>t}5C!*`vi4C_R1c&9?p^Y({oGIi%QddBdM-p$fgO{PX8o>K;&iJyjeD62_m7!>!Cxo}Ml$jn9^ zusG69iTg)86(!Tvv~G(I{sR?bD7h9GdC^U7#8fC}mLhx0ZtNdre=WkobgbkN_Ag)d zepjdmkfXbeG<+C%E3EmjFkqw`@5{a~xo{xmF{ZOz%`$l&iB5y&ioTWdYBB3T+Y$=I z^w7=UTkxV=n zMom;m(vL|R$fZqc?iraTa*ekmx+=^Qx8wLQhllReYrWQ=ch&|M^<;4qdI_ZgelJF( z^q(fvyf62xY}&6(Gq5|9f%EoC(E)Ql0R*HbGfGGY>$^8V$D_a?m5P2wG%juq1_@@m ziiLO(kd(F8`shX2D@)5r1>Ue-Y4=7-OeF^>%GpN&Hq42`k#^mps1exy?2e%J_lwniGJ#fgR|PSMBg$l@`lXo0)bNPy zG3J01Fv+PJ-Y>Q~GuYZGY8mVINm8A=qE^9nR^01gdKtiD1`K;z1ks!DLD3wj6wUSM zdPK+k@-?J-nOawLN?l79HZmuvOg=K1i2nWp;Rgt=%$pQI*}+IR2I(F{fb2-N%~iIo z6lh`;7Xw(^v33vMfhKkfG*sxc zJrsr?3azVdfZzczsCmJ{a$5Z-jV-(ID*u zBB1G8$44M~fuV$eRIMSzH-NC$D6nBlHNI0;008jw%gE$?0?fuANbRZsyrx#?6W|ev z^EkSy?rYsv=f$tF#9V^!yuKP zD-2&DQn#zN)`pjW;2A;SNvWm!(8HTlN2g96c&h8*W?Q0VEa3ul?6%xB?5`t(_bWZ3 zgZB*t)ykwCZj`}$Q_SFv`SlJNyiX2RoTBZB1GuY{_m|Vy)9?B~o>th)&~qIeE4;Ye ztWQW~*J4q=yqURq&K^|tlA()YOP}xAt%Gm=HCmL{stgCepQ$Gr{j=1+v_^0255RnF zO~s^dei0$StHGY;I;ds@tG`F=NN_2NrAC5IF*wbnGqyQG5-<+Hcx7GmL`;J`I>a={ zJ@BDD92_I%-w|CYLUX7!i{%FZ#P~Y}@HhaXEiU{b0NxRR#19lelIfw?;=W}3G|9pF zDC3R6>z1%uOB#a$+7m_T%)Cz_7zG5;90J7P1qLJMk?Bg*;NVY#!C_olo;9;OgoMiE zkC;}d1Ab@%vSL&OYs3A@+HexB$F7MyQS5h0q}cI74oZAJv)d2tNg0r!YKx_YZ*jwK zpA^Z8?(R?Z%+O?(+k*VfzzVOLq2DXlv!O?Nya zn0S!O7roF@YxC34xN;ZbkKNS3hyj7S(_#GV63G87 zL7w%yiWJEacIQh)Le+68Bn@EG(Q@o3MW@VcsZ`fasB0xYu<2V=8{0j^AS9N9y|Yr8mVBcrVzrRp&&oIYUp!>Z?}zanYivq}bjPZf+d*KAwD;p@CF@;_LC7^1 zSkQrQQ!3sGX)hRuyU&0`rK`Al4~i%*iVjl%Xik8A110g{LIIe$D>9A&pf&-%27qk( z#li_p(pY#xqs3#~%jzIQ^*YEoEqquGkglfSD@ z&U*VfWHJFt8H$ji;;u)W3_l^p-9r6tF#dA;_;5H0R}OT*1zC7M`$t|Lce-~^UU_Ui zv0lcG-iW^l@w~S5+n>x$FaBN}JJ6HK4IRhO%GB5)`z6)u)#f4@q;H$ReL1^vUYIBg zFADd?JnRR*FsK{UDfa7!pG{O9OZXn7NYW_=M{|T}beGQWVdMuq&G}Atz36o>;UsT) zLKWd+GAnt96F3?~J+A$QHx4 zxm(80MRat1-hNT+~mu^^Ik}x&m&PV&6j@JQ$6>6?#|fwMFMsKZ{q>bFTV_#93l9!~^5uQ02i}PJ0K-ihekcZF|5jB%h zoa4vl`4i&ugDJ;nW}&0OJzRIuMiBEopvw1kRleJ*@1z%X`peaKk->L9zeDae$Xk>@ zXr&=f(I&Q;w$|l8@@a8AkpNSPeE_wJYbvYP;rkeB%ixQYH`qQxp4a1)n73I~%YmD{ zTqOp72?w_SN%W{gBEV?PvA zFCR0S_aEVA7LhX67Vqpe*HbM3Cf+@cjzSqYpI#SLypq5WRM6mVD-PN4;B@Mv8`$F)}8ic#Ao+)Ajfx z0|UIFZBe!=;{7Ir8zLFx3&uz*&cSDFJIs{t2p8-W`HnW0iB;myAph9LcPF}-7%r-* zb4)Msjp#x~ld`GI&_Ngvu(S2y%Z(zQxWcp@~+4=a3?RP(5 zwCdb=W^N-seOUKHU%s2Sbzu#%jnI;~O)_`H$r>frFpGd3)wgbgnpfgJpYQRMZ<*~t zqxA+8UoqeZ*pY`4w^N(M$aCTe9=q~iZ4JSilH4p+yaIYSE+@!&4X6g&)}vsbG%9of z>|)Ku%3K=-{)X)8L+RV^#q3@#JG%<~NsJh#$!DY!TrcEAWatt3jMQl!Ke;>1B|4Sg z&B~H>@xsk4Wi-moinz`5;ID2afz?IB@TG!htQ4N8-cJv9`dlZ>oGRuJS#n z%J-})-_xsna}63Ce%9c>Pkq-g-#6kL99nXZ;m}6ufuqv3hAh&3Ktzbxd(}`}XhSZ% zUYV_fOq$I&Fh6u+s?}nR5p(}{gl_-oaQTzQAu#R^#kX z6e+vk6H=_*Af=rqMW`(9?7+_KZmC&i1;PHw*i?e&2L{IZ0{xS_1)BY0PiAs+E&mU- z#EueZf-)3;jmwLLkvSuYJFo>IR(GxkFdCO^-m0=}KeG{mt(B+^JB2Ah^I(F6Mx^3S z;fB^&jka7z+HiSS88+C|!j+`5TELeZzGtFfPJ(2&&-x9^QpEKEf^nnqnV#Oj;U1Xv zdU>}@cGqP8Mc#^d+7M{5>YMBh;wtda2yE#Yh3(LdzlU;ZuC+c{Q~3NHu;GatJhfqJ z);=htM1_7kNzg(s5=r8eyk}5;ml8gd9=OxU+J{UK?Srq!ic>+fXO!(3A;}}sgzWK> zBqHz3Qr_Y6U&$O+${M9yspkWaaEF^vfWukaqlXpjmjG3OEg*;%nM4o#2-h0rMIAgb zAQ^L;Uv4kV9ne^?Z7FlzFgBLXFkFiKoCdGAJ%Mrfg>H$cUv(YBV~h&o;(R>Afd-cH zlB9`O+Em6lm>A&xhCjZj<)|q0d#SzS;y=@td{pnPGnlSv{p4yjcsp9ssJ7pltCH1W zG8}#~90*^m@t8*LttfEBUMNWGj?4$}WYP-MZMj^6-TO8-%G(jWf4L}4hfrFptT=^% zQa7GW6Ce>{jV8Q2)V<=NlNHc2M_m$F1VQAy~(m{&WU=MQi}!j}S_<5O8r`3_kps;tk!c8Oqh-UB>v z!mY)}acN=zB2j!izVNQ{YP3zLZO=P^pu8Gxi__o4TV4$t3H0|e`NkDb`uk(~z7OA0 z?DA@2b(o1zFeSOX8c${_%u^8tuFRozB;MEwtquo#)b2X4HqeDSVQo>~odOMo2Xg73vNfaJLrcZ-LPQuHkcd#lsKcv8eeM8Qr8 zooU;;idrk~G#`1X!!^HCHP^gu=K}m%fZx8fEXg(hyY9%dTTNVA-6IbGN%^5}wXD;4 z`ENjfdk-WI9Zj%jf;^UyRVKLsHAEz*I#5{a0BEm_f}tcJ!w|Le8{DTISZ%#esFx_d zp_Gqq73HM!&B|<4ZL08jGEwwQ3XV(w<`0kzWf{d(d`w`{Snw(|wpt-JdRFreJ!Cnz zAm#?dcthZpYyN?StV_<d4*w=EPKF?&INX-$g$==dA@2MR?4u$0LmIERTe$c{HGj|I6Y+&O zXEe5&b^Qa8dM(L7H0l!#b`^5S#?4pA;gwP}O*VN~X^8pA1)7>d)6)Gy0I)fLOM>mYnR!m%}>b1L2dsMuyD{1T7HYA`pnpAo$5KD9P%y0kOGcHRnFrBJ^x zLi#%F+_ryWB|PnP;hBJBbQ_0UgO20D030i_6A{B3CWkdX%?pCw$0vJ}H<&zf;VUAZ zLV#+N0mwT7Cc}X#`sMl26BLC`pii8&yn(}hP653K$6CAP4C*nl&ul=lGP|Jzvfsd; zb@=X@=p9@o>DU4Xl$XKb2zeGRHc2#ul)>bP55D>5WJpbhe1(wQU}wG!)zIc{J+GXs zN=Or#!%^?}i3r2&A{{dgs0hfs1*Em~DD76`(%N_fxUvjq=4D$dz1*5w`h))hZ}H`E zg>J%NBXdF;U#FAJ{y_q9OEmhFk~p?-^uX!~QUYfv;Nbwqd1e6Hzk27BaQ)}6K{X^1t}OK9uJzySyYTs! ze0IoQTCvS>J}0Ph1@Hz<5CsYZO$H##t*GR_T?BRo>FBUmqQkZ(b*+u6D*htv^g}45 zYicpD*zH)hP|CO=(q`^nt2&#fi>1vx2?$i~Ba+=68nf&M2I}7s%0Z0zVIY(2?iU&+ zYRJAS@Du{ktX~<|z+qk9QJw)5^F}CV)cLd=Ko+cC7(KZr*wB8FQUQVC$!BaOy(g_B zzr3Ir3%(`iIj#@-cQ%xl0B1}k%kg9aHUPC6mCP&bxTl5gGif^+g$I(!eE=B>A`($5eHwXE4q!WYl4+E_ep zG8E}r$RNh-Kt1@)Qr7qFsBi2l28ZlJ4+~5xDD~fCsGR;_l*)1d2$imfUqfC(AZc3* zPAt)+dDrdsoilDFPW?c`=#1^B4RgT@4%`lUnvq4`;<3rqT+9k7Jsg|%cduXz2`eG&-f+Q#lIqJXB(7CHH83t-$W~w3c!Oj@Y zQ*IP?oQ18Arhxk%0yoYAgLQ5n_U6m&4X7bnXvk?X?e=cLO0cIO+_nITYOr=UG3*c6 zhVcb~R%y97H=4wPQB{!=HZ~=Ub4_=V+{XCB@R5v%z%GVvZfAo$FLRKKNFi@%D41*{ zLA`+BzRTE9&K-k=>JQ4*fUsba2={bE0_I^j=r+BaQ*}bcT1}Jwrmy`OP`Dar(H(;6 zAzSKmTh({*!r`2oIKK~hPOrkqdzDIB z1PE(>4d&RdbB4SG)6lkoucCLVJrI$(#p4|{U8QXZ_o%>C83ld-FpANfS)q>t5w#+d zwhs{~(_@B$5O#@RRxSHkL{MxWh3jQqStBptD;=z=ZqrrVBaa}=iQ4_<7)RJfdd5hg^`^ zO^<<76@60eKJai5JnwPJMb6%5gyYjzg5ygkNVT5;1X3JQ&V7UnC3m3xp|!edUwvX4Ix~vu6PNbFNinj_le910PPqUSkbG2KJVx^f=%ubv+F)24YSV zv2w6NVicap_DYBfM0W*_2jLK~el0ln#@w6ioq&euK1#|PY)a->Te=uwcCv~X)EvF( zmVV?&;=~tFN&@VC0szkufJPH}E%GeC)EpS22bzVwM?xaS%LkR1yrYtmG81&3Z!B0AiZ+UMkGr_FE^mR7ZZ$)1d8eW++$xbATod7v=}H zaYl&Qp-h{-GA-K=IzUYr7gt3lnm7=}=a+s(xj=+BaLRh@b8hGmItpal%|1g%ebAxp z?n8tQu-JM4bjSz{+Iaw=voOB9wh$DcD0fUWKCApF_?7@aLO# z2~b-B*v$y%9!h}c6~I;EBwnEasw)fvFj@gT0a}3JVM*#7s4$$k5*YdcK%ScpxpO}Q zV#<+y>6bVb6>`p0sS;JHjaNv%FEG_5NJXXNk!ST&*jv`0sj$YK#U?v3%K8x=hFBf- zr{E-{yIVhLl}}+Yf%Y%|SePYfvO&d8Om_IILo3S&)9QCuA%rnd4d2OR^c8EGI;>}4 z5vF|h1kS~$p$S5=zgh;#Ufn~~fhQ4;pa<$erjy!WBvIXo>crn-q+G*sj6Qh{S7~?l zE(wsyv~>A)=JOP0+()ymCaxQqp+2VK!*ZZ>5Da?|0->Mv)^YbB4*kUM&q_Zrlr+s4 zlZgIIPRl{3T*o1$A-8df)@e6p`ZD{t%1HH{O#})1&g~2i<~*a&wIaIffQ}?p0}v7} zAw(!M?LF$=<54g?&F4?F>aC-#06FDnA9Iy_@*=w4R|>Enm+NAsZ{U->&D{5HbC7l~ zY6uUfBGr~3a9S_qe7KL)jrT`2D*ZfKI=Ww9AT0_dYz`RTsF*%IQ@uL)E?ls@YrO`J z#Un77@w!T1p)X>P1N8yo3?<$UI?U|HRdOk*?FmxsM(}~2u81xO z*73>YDA8&oRHkh2EmXd`Ua6QZ6_u@lQ9mIZa@IP7SG@4fSICpP6mn)>66P-b1gHfG zsU)<~Y8g9v!M2smW&b2^_JMJAQI@iWO>sy0*d#vb6d#6t>tHSy@yLB|)1}_PYVXD0 zcxM#vit`=A)|$1L79@D%R`J27DYH}~xrL1kJ(l?KCk#HQ64QGj?(LtwzD3NfBhp%K+?FRyk*rhWCRsEWWodpKOA zn+(S~{oR=gyNCftVfs4wHD>U4;VYfGvUll@v??~CBO3Ls>WDg;Qr?m&Z9_l0V^kGy#nvKh*vl=rwZT z2p+6a{a4qOvNv>UcT79HxN!-G4sjkMA0n`X`Zs>C>x;Mn)pa4iGtC#ev%+rn4zL%8 zt9gr0RbY`AEe&tx-4Rz18da@x$!~?XdyDtR6}eVbe~gE23j)XNBqkswV~|mPC@qE( z9IitK)}j@Pl2t&ah`{751N&&JP|_{ruPH=?Uxra`LPhSRJ#qe-lHoadQ}N&_9>fSE ztBKw6LkT7uY#v(0DInT6^Xega72PO(JsW67Y$jlUlo`dXkDTtR2)|{0@ip4daPTxg zu#*LM1*Wrm3FgdtTmyYvIR)^zmOn)?ONkkq;#G1xX?Y z+Wa;~K-n#+K@bh*P6WjehZ!^}JiAO)Uk!*1*$P3DnC;y{c`eflVD`guR@^GbWA-An zG@!WxMP5Q{GX}Tq-?PhK71#9Jz=_;-J9CS7cIbk_r8x4YcEO9GqyZhdRNcIvNgyiG z{zC;@uOq~2%yO|I0P|Wh)4)%Oz|WOB$T{+;lzRT>DD~STR13cuKuGvFAOhu6Yx{5| zQ1Pnxyrx@MbvgT3$Eg!4>{B!~JGa&#DVNzt?~K~Vxiuy@$= z`=t%Bzdi9>F>p~R2iSWz)O7U2P~-mddg;IxWh?c{O`14CYeZ}z_mS5)@Lw+DzN@6q zyC~u&yDl7WXk^2Mw6;MRJ^Y5fgy8KwXVWtrept-9=sTMd{IFC`NOlySBbyI43ewC2 zN~PBfQU>gM2)PGbL}Sn{>)!3M6SL%P zMEL?gL!Z9mbSg64pH&8^x5(k2?1ZA z_Q)cXB_v5+vvpo^IiUkh2DOlDs<5{Rp@sjgp7zGWRB`zJ^`@x$Jwls;{WcGQJ$QqNNIT~NV!Roa{O9N%DyNm z3*oKGli4b_H2}98&8d=G1#;6oX#(67gj=^Np40%WBIL#xLdK|$>*OUM!eS~XfE;w@byHO4ySJ#!XGAjJej>_+y{iBCy)iQHuQETBtGTc) zn)$51$vlKG$n>QzQ-#JOw-cbrqgkS@PZuh37G5V^!T2UP;fh9f?tQu|7>;Q4PY-;+ zvVB!y{{atl@o5VD5i0)(rWIquy=cZ?A{yY6KNP5271Zr9P#+QMD53@1UU&fh^G5*3 z84%h&V6#|U`Sbpd1;`HvjVk668E@%c|DCev(9YBUB6L1Z^g)A9DjF_DO z3++n#+qoitm%h{-#t}SD%t=Bax<~iu>C&=4yqHti{noNk?3U$fs0?VUow}GQe_>`W zZFNr&kFq96Pcj+b1y~&S6^{i+n+wd92-9|ot+}#=)f(&5yRR`Bbd}~|pt4)xPsQ42 zShCW+pr$Fw2;~e$4SRW#-<7w)KCX&?8?lzzIiHYK)DueA`0GYjcA+=O$}R_J>;4C1 z)u;|&i}CGnl~ji}yc6l41e(53j;(%gVeU8dxVN~X!e70=z42o#9hG5X9ZEd+(=6K1 z4i~rKnmY!SukVI2>=KC2C*I2)@(?ll!j9O!M6LI`Mtq!l^ajXM1l>S;nxGrFT(T;- zvUY4%_vx%8PVV-KlJOrM*TY)yrsy5jT~C#{=7BJQ?%4<9SQI+V5UmvctTNH@jlwVb zKPYVt{oL~X#Q9rcLRMM0v1l>W`mL+tad1TA``!BSBdGUO|H%mODi6H+_qoKCb%A={ zk2O%=u;*Op4PxOZ8_gbVE9CBTV^f(xyEEbOR$vYAJHh_%C{Nb`$`}5M5~FCy9dA(n zGPn{-wIv+3te%#GlOfc4G6m^aBE=d;KGy32ez-axw?auYeQvef03DcBMnhRvh5gf~ z6fl%~`Oz-5vA{ib0h>%e+;?!wRlIQ?7Jz*T58-*6WXFRAxF6oPbVtIqNAv0vbTB}D zp>7FOvy0~dv~zG^LheI&I4RlTno>4jBzHX>T<+(ro!8J|UyUSS@e%|ERu^xdxYVkF z>0Uj@(j7J8)YmZo{+{?AQ>@)Q}25xDE|6-^}3BK$D zt|?c24ti^gGNEW5Rm;8$VayVay`26EyY3eZV(DkY#~cnUQtoWH;Mo&{nmX)+7MA=37ga06%y>|90sKy(Yo~dB(Qi2b; zn|-V0ORk6J)f2Oa{jhjipVh`nRs7wU_4RB4h~QW(32w08AbcoiA0BP9L-2ct9xPrO z=POU((`r~{VGy@Lj9slgjg4nfIa504-hf?8XvCYYKnumh_Em|81(gpMKaCH)Z%xLv$*}kT5_$zJ`dqC+bIa1&r=jQ77@n8?0i@0 zZa@?SZ-KV5K`mcdz#ntt?W>X!=P!K0>Y{@4ekL3eK=+Q6F_#y8NJ>NLx80oOurFky zPfcC1H1<3YbV9ead$8V;jCR+%;X4VdDZ6R7lPzRSBkKO1)TzR5mU z{$_9XI~AKVYb7l{!~NSZ2gdybHDX)~-m@S(_F=#BgCdOJ_v4O`47}Hl5gT3{ zg^4f1R2L&V!q9);U*U&W7d)-MX;cIC$%w(0@3{qRQ0k>}^ZfJ=oRNgE+4`l*!+ zDq~h5&qE!B*Pnh#yX-aA^FvY9750Je(Ib>%aN4&Bw}8?rYElp zwYr9a{Q5auK7=vMo{xmsP%xA^yn=$|hsMX*SF?n==x6g$bU)y^LT!Hvx-j`~1@MBv z2FgdO{fdMY#o6b<12OW&omnD8xV-!AH@AwMCj1Ke0)LXzfoTZx#huPwK%fGbcbi>= z5cD7R?9Ec@SLl@4-@)mLRx|%}CSp%zmC@FWoA^R*QK4XNHVO;39B?lib|NIm2`Ovj z6XzN;Wjci*$pM>sDDe@@2UYh)T^9AguE?st4`Y7uisXURU9Qu0jP2tM*E zHM&s!`VK@k{QF9)OsAw*Pdv^w@HHLQ_=e zK6I++336ov73x-6fOP~w*d9^X0CfEK@W&98xhDE; zMB@;0rvYdm8{xtpx1Vw7ZdVm#%-DUHk@3SfP_zx9vN4Sl*VtIHe0*2^-CIT<%b2&GHqqQWl){&~cq z{^y4QQoJnA&ZH-l82>^8$F*36X=op206LkEUX>kqfe^m!5%}wkibq8Rj-ik9T-gB9 zU%Usce=w}Wx;~Pv&F{(LS-Lk;ectTlg((wi=Wf(9huS9x%ZOhpHN-^@oL{YF zef2xGxMfoQ(s!6TnG0>?`nB zkl}55${T8x4dZq;F#TffJbelSNTJ+!n9M8HS*44!FL!jATB|y$72RnDjt9t=c|4jO zyo1Wygw&XFz~Vi_d+aQXV&iMdCUob#GZoyPz7J#7pWf_|sS~ik)Gfms7@52(0eTtE zH*B@;euX6}%JCJSj`P|5y+=yOA@wu zT8CetU4pw^OG4lod(J_qQ&|KdccGdeQ+K!qAIPvg)i?>kXd~*polMAh3`+Ks$i)}D z7ts58d2S;{gxo*%MrX4FQEIbY4G<`s?Fw)<9lBddIil=O8INbt2s;^m>+4ToydH*T zuFpDvCeAKaA}+UYZlB!VGS`K?_3oLqsfX1ZqX;#@k_Pbby$I;j1+WWM&xdvut^uv% z2B0oLb5G`Gvl<0>AWOp2TQj_LZ($1B#OJL=3}iJn=Gg`L>WkopnK;!$Qpi8=j20o`v+q{dvl0y%+*<$qU)ccZrdy$*xO z?eFSvB*S$b9`i^Y{_=~d!!G?R)#0d?e^ZBCsjpmzFW(fcLs_w2k`_~gALB#SENUp# zdJUhI>hK8!{7W5<)bOao=cfJt)FEo1T_>#j-I78AR@!@Za{`oAhNC@!Q&ccRp6`;R zFGjz2cd}@f96DuuwDRAQ{&*lpcJ{)FgE|Q7{=}{VUQ+MuC%y3FtX)}wMctm`jmO3Z z45b+1sc)7ZEvoKqH!^wL`5>jSN1HeJBF?|REUPL)f$8g`5}E@!UA;Kay=gJ2&3nB-*=5r^>9OCZEKAZtfJtu&*N1H z9CoB{hW)P~cpHWs4i~dtm*HX*dZY}{iY*8Hec z0X7Ei(+{9W1#d`ROp%rM8CT251B(J~%pO!54|gmZe*sJ>U8W_}_a58Yd0MS@7{%)Q zDwcJgn$Rx14b=YDk~BGB?;2>&#_lMbpT*HPC~KuhM<-%eN>N?xYMFpRsBeaAUi+4I z31Fz-w^yaClgv_PTYG!6exawu?uyzeB->yatRG(7E{eK=8ATRE{7q)k{25h;*&02`DW&)x8BBFiR2Y^vTju2xYnT8-P&a(R2T+BJN*h-pVmQd66=yPnl z)X%ODG&|%2n7tQjrM#VjjdSK2F1tt&44LK!QYzm%bdg z@8VD%x%?EH7$fuXrT6g5uKV^RNDUB$z=i`Uz~(CboQ)IP zeZ7I+NaSUyBBw9xZ1Mq9BmMT^3J_O?vp+#E!YEdsi$X#HVZ~v!><`%V$mYwKQFL!@1ALlwWyqE+ z2-T8&RD#0SqWd?)&lX^Nq0okP*%D|o$RC+mFXPf%tW_hUpXm(K*UZNzq~LJ$kZ7j%AVgxyQM{rk+BtGBL(-KH zj%>6=Z0JJTWUGcGyD0+-3N+`d@|zyD{%l;^5nhqo7pLh&k(4Q<43;h98_F!Q2x7K4 zlVS!;`GH$u{<`Sp$jZ?~P9&1@vv=d@4~BzetLMAWZ7_1}l$WmZ(P&b`PN=itg*aKa zNVb>+JL7hs@fqGwKj=7{M%TwYF+Ur4{g){+hs>+#2%nFUuhm7k2Zx&c^xYV!WZG8o zWf;>lB*wd?Z9sdKQtKUzQUE&(pxXVdg zT}{&=%FynTOROnBzsGbWyS&p97?yS+^hNcLRm-O7es zBa*!wO~<+PQPiX-0a|O)&Cz*tzDiCP3a33Edtu1NSsK~Pthql6VU~|ACGfKxP7gW9 z2Gu;gkv#_C5O=3fD7+!bpMyLaNAhr$IzR?y@|O!WK%DDZe9RAH#@LL4cxXrvvAEsV z1;0Vb5Q0(fsa%VwqHU~g1gb+h=Yf=PIXvgm1M5{z96a_HBn~%NbcO#F@#om|kE36& zaG;D6l{9_BsD%QScu6i83wsrG2el9q1$&EN=&ccdHm>jj*SrQ0g2_`FIF2Sf7S9Hk z_uEG+}DIf3vruJpB7#bIM^(Qx!pv z6UW~ZRKF5IZr8llDF4CI2999+`PhAGzxgC46yRS=Z`@h*NT47U6rd~m8ZddmglV}) z%c>RD%Prm(2SW1BDFXwC9=C@xStzlMA^tCnnAPQP7%>_+!H6Vb#O4l_8L`KFS7JmL zBq1Y`{*e)_nA2ADy*|*j;fZ*MDx=esv6{UMf!OD{ll}8w2tCp{mQc;0?@Ba-s@bJT zOa`?DgF0ueA%~hM4!sK?%^_4ZI8?3kIjdbymyu7dd8@9(Vj zoTuhNCFSZr`s-RS{m2mFPtMw`HHN)x6(e8#mFcZCvGPem>#RnzMg(O`jwFExFSYTD3pdU$4CryV)vM!E#)<2P29%5DueTK8$aegF-jP zwRaZQLAnXGawm;z6?bkS4ocrxjhi}Loe!&ty!BvxYOu5p_;b-=u&aM<3)PGU2*JH) zYkh$Rg-xP*TlfMMhIeaZfRDVx*q@&jwJSX^#ogeTFysdg#0-m-I-+Clf9r^v=KZxJ zawE)YA)23bM9VATd+T5EZS3?{d@B)Vb&SD>*80j)tTW1CL}PYiHzimo z=LsaSzf=Ax^sf-Vdkp+sN1J8{vN#G_NC=GY+qs39qB^Uyk2Px_NQHdy+eS^OKTKZdi&C)06AgpAK`Kg z1m#_?Y33N5+jM2_psY1nKj26-wnoFiVs9Pfgo0v!j!am^$&zbfE~x5Ul`2jV4h$mY zpsE>qY$n($0j^TfBtxEUInB70ws3B4u(07k=v!Go=*gq~Bg)UhXQDT!as&+tN|QCm zQ=f}yuF|IapiH&GpH=hD^zF+& z!3c^e^3kr)wHNQD%9i+j{q2$;Z+!#-b3VVtQo}6SEAg5Rn(P0&U_~@YO+LSwpCFhchpA|_Kx1dxVu0u z^?z4s6Zm5F>;^0d?xnfAxkIiP_B*!r+t}uy!X7 z?8a%X(Dm?9D+g9=*gD4U!dN&kTvy1b)dzcNa7#~8VQqDK3vX_XO2r}vEDY|pmx!pv z>naa+maCIsVnEtPke2gJS{y0j$*)B4D^*fDRLXO7${t85wh5hZ`#_y(Ou`IFSbni& znXD5wtdel{-z01(38$-s4Rpe-c`C;qR7;?yIPX*O%_Sc1KOr8K8E(t6G)7sjH<(?j zESN+=Q!ERkobHw??WG!bu}Z7>KozL2NgKmMEWHU`M^mLhO?29>CM`^^fp3lF{7={7 zgVK$A1M70Td$ao{dtKMxg!QqSGyrq^_IUIu*y^wmBg)~|?dKta-l5}hN_v%d>F#(d z3rkkm3#BYEg*B}UUp!NhTl`H$*q@i(Ic-9nf}yK(E6Tc+=Y35^^r*;gZa@ntx1 zk5=3N47Qcb!&7#$8}_v1eccebx#3wP@dh8_1xS#6R|!uw$dt6=JJpA{N_erk97E>Z z)E9h+?GJt6B!{cyA%Wg%EqN1aj2vuxNsy03eRB?m%|pDu0A8Cj7vIojSTR~|KLCGT z01XuF|28H#_H%CWTCJ^fW@X<8gy@1>UnJH2LQ7gh+F1RrO;kn5W+J9;0T27*I6w9X zzvei1{;TCBbsE*6`*6w$OnI#wHP8}-;9vOA1l~hrvTf@z;-3QL9b%@$7EV? zhGO|9SIMjRu2^TdeTjf#pKx$WgwcMeVOy;MTuBwSYNM8{hR`CTILu`z#R~xsvb?e2 zk^8>unY7%wlb{wbq*GI}yG3N!V<` zqXkc*+}#?;;nOz}Zf&0-YvcD0)obH>5opV@*z?aMaTd3M1?HYP`vvh~*GNu>-8j&3 z?kjTUrF1`nvHvMerrkX2d`R6`vp;JuSIufMTt;7`Fo_Z`1DsmGDU zE!OqVkeAEtVua?agVnTt0&X_pLOF}5$Amn$&u<{C*wTR=pdAz>oQN;#gYi%XSxiR} z8lLIH4i1>MR@ya*GvpqtFnlrx%w#o?WzPbQx)lSA_jdrsM2`7VOQA%cO7RX;K-%vT zV5X$qZ(Yook91!n6Cd{H@g>3Dr27j5z)8Y*uCAoXmXSU2&kInH;0>5PcmfJ>R++VR zg51VfUk&~X4!{n%rx7`ln1g+FY(=HD5y!KdIinc+jf!nxVp9<-mi6MClQ(5yr436m zm^=hMd+`Mx5477iu0uXfX6naO>hBo`BOuR!7Uz6cDc$=fU4lw?7t{4q=^l@!lPwWP zWNuXYLt$^A8QKQf`?HYs0QlHWs@0`3!F4@ceI*^#J`mDycPc2h-e}Kat_(kbpk0TM z$Da+Q+$l^9+U8>yTyaiyB85`phbVbYwpMuGsblcs4mhghJAmV&x8$Oz>or=g6crQg ziwf;xrUlvsFs5K3%FR39V401yTn38Cr3(niOr=4l%^g_^DQnAhH!9S982?+Xl>}9n?O}%$K2r>ZqC5vb4X*o`ZP9Z z6z8nHn&{gB{Zjad{>~`+(K7Juw^sFnaWRxr<0zULu8k|R65H`K!b-b@G=_3I9YpRu zRPJMdhPl5VZU0LsxQx@Z5ZcU9-r_zzy~FuIZEI7cM(`h8k(2aG)l z*k#F#k}>^=+*lQCt8$jNl!|j|V&5Dn`9>I2F-Vo*Emwp=7a0caqNL8LFAUlMIAKuu zIVd{kKm(?7e{qg%T`9GuJ|&!)?ICA6fHN<_N6y?3!Rsm+h~Z0fXLiq;h{l>?&cwd# zMaZDvvW3j>?Fenetglj8_W~+r{kDBR5I~J;QUV6GariHVy0*1JltDYCch9y?S#tNq z)J-&V2M#KBK)(&;bblR6{y3EnR-RoY3jmV!gQzZBt)VkTNYRKYNwSX%@>raZ%Nyk` zC#IZ^l$77xFCZC+k6Dx$m>1Zl6=-he+kuKtb`eJn85Fv{+6a@vHsvy5>?0pn$cHCw0y(|aYW&M7+}c=JGw7Vw zqq&}mhul{aZ^(0OHZiuf9&Upb>GyhOt;t<*MD!;2)A7&~LY{Au=;ZAB5d@AH3j*1@ zv`csJuC~O5^2E+^YxGP+ggl2&KzpNodYSd`GYJCm5+JMrDtdwpz!E)7MLVaiY3MkV z=SueUU6M+8_izTdk~2G~@ASU%UB_8II!gc(y8t0mq`L*8j=Q&tsgu}Cd(zv{0pjMl>LmJ9K)SdL@ah0@5s=(pOW-BfewPXN2St=$|yXjTFomIZw z@{LM&wpj!*A%-Rf)n&$$ZaitmbE)xMVmuccPbxj&jCRwt>#XD=ALm61&`lS`YYayh z&rLsq8pQ`ai(ASC3F6XO`20wk0mg;9jKz#AU!r9Uv|O5?AN|aT%~&2Z)P+^kq6g zyUr@#9{C2KXDI>cDKnlW#z<7iMO{X*tP#`7UP$XUCY+I3cOO}t0Knezt*!yI^!&AV_+&h#T_b~GrH ze#!YQ<9U-F4&MK~n_eePN%{LgjDGVup=c068L`#90sH;iTMI}nt zZl-pfRb03J_J~UioehROcz`|^j^<|y{Rp~72f^FU#4>4Hd`PRxrUx>l-AwH|tGFiK zBjLo*#$ae|JXaf!$9P&9&sE0L(s;7yLB86})ULCNtKyMgN(-GpyZ`!)(q;l&^TJI( zf-a8l-P900fyXQA`m12$~ZOa?+2W zccVcb`bp>ij|-HuqbM`!C(0_Wy9kCphKtJz9K`^79#k=Ix>&KIRDK> zp>7126pADaHpm94#5r^U)ZYNP>g3SRl8ehk{TM(`Ulrq|i$uNPXm&a2M^KNLAo>w> zy$K?Q0(=One0q?Xc2i=%k;oUD$ZHaHRf(K*0hDKeP`C6WC^sgEepb7<%)BE5=;>fQ z*BDQZ@w7LdcE;1zc(UmMzW?I7E5$RrHU>j$g~6@el-O?&-wM%@n!3s)YN-=x*ZB|M zCj4JrWYXMZf|AgWpk^^a^s|uv6BkKO^iw3mdsKUc!$F}ra{jMeX8X0l@Rh=lqurF)Z)Cd$(UELd zn?&U*k@J7$GTT)K!%E{>VLZ!?XPNOVHJ&o#SwauE{9jymrFiDM*kD+sFu1jw68nwf zeun5saTl6I3v?pwI{)F@g#R};B^D1d{nTWNlF`o+{tsPu>3;x^5@wdr10_{l1h|WJ zfVd2ptOLYFK;lC>KwJh)(gETkAbp|^(5|z}w@1DK=$Sx3dd3^iIO7>>JY$S!wDA-f z&nS98qINU2>#X9Mc#niLy9W)1LWLn+T*SEjIzU_mB#tzoBaG((1>)9jrgoiGTodn+ zaOQfy!7yB5NY`#k>^Ca*9z;i^>28zgE|tiw-AwH|tGFiKBjL>UPJ>~X@!VlNw;RuG z#&fIj3^krx=mBT6o2gxA6<5V0zm%JG0`30mH%faWz@@avgMI`Jj0vKjBH|oC4`8(W zPv0i||LRu7Ulou0@_eDgDK7cT^BE$<87d}SyD72XC{;S5Bh{K_5?!hixwY&3hi??Q(5ZI`AN}wuuB0^dqQdOc4DDN{R`hU-GYEJk=FYd=w~!ex!5gAb8Joi{bled5dGv*C6}f7lM(bBHl9PqbI^DW7|(v=*=Ia^=>gvV;<_uvGrK(o zLs(&OYd0nK8-=hC9TB$OCebdPNW0E|_%`AH%}t5LgG_gtOi?oWS;GIJ>z> z)rWAj+@;fxp!Z{f=ts~y(I7Yd2zo0fhgcHN#2E$|U0DTCK$(()!P18Z}K5Sx{^#4(ICg4#OOW&VBA^{R7 zB9X9Z#2|^F5+O>&2m=8on6R$`f*WqA2#IXUViI5+N4+BM>(#4X_v^kwLV$>P6;a%9 z0aQH40a1`mg!z8|?lVd7^1knPc^)!-x~r?Jx9aNZ>gt7jn8GU+pzy!^rh4D;-*|dQ z6E6%EaSY}0z)kVMf_PwlJTNaFm>Un=r~u{uN*X3jE7&Jl_7&tOW(V46$ePW9U~? z=ksAYtA`gTI7 zAX^a%bczQ~jt4r%16lFFN%263c;G|@!1rJMs<_NJh7&A?_DA}q+U-cLt9ILXrZzTH z>VNgCVq3>CWDo#-IzI+IEv_(8Exag#{&=dg9?geosZ<51^MCa#3@LF8&EtV)@j%mf zAUPgr5)T{|4wdvq%cO-}Vi^$#n4C2ATfk!_@eI0t`j{ z0<p?yWqd1+;mvs9jdII zADrU_)ou#c{R-P`MJO;U9+(*q%!miBj|a-)fzo(jx&q++KYr5>OILBz;ux;87}CAp z_6@(O`ZXl$pP}*Uc%~^{Chs@(fB25a|F6Fkaz(rlYN9YvlN&@StV;g>{2HHmDQjfj zoW6AjS4JXvACy}=tXK$npx_g#1*s;u3zRCU(J16^3J2bspEhbvHT>bV# zUZQM#BEiZ2>}pC+$#M?$PKS=P6yx;hT>!A?Th0${lZFNJL!LtWqgz$Ssw!|#w`8V}FR^OD6 z^uktS&^5M-)Iy%}q#H?co_fQ}#p9mJbx%AO-(!&RN4D02G|G(=I5C1@1{ zB@MR(CH1rfIUdOnwdR?_iOP?ow?wr$GEv9=g{be%H&ro2{rDq99b8vORBxOH#G&tKluNR@s@e0Fyg)1`4 zO;zJu`?{@?884G16gAh__e?vQnx;PYii=LiJA|% zTu?g)NOni!2$N}1HM<#9B`td-!*~4|x}D0n279tI{zc_8gWtd7hgzSVFQ=`X-AeV^ z1AOVaXmckIFq^389YjapG~+MXdGd18sN>G?8g(1q2mw-~9>mMu`Iutzs=IXy&RkBP zFH7`bXp3{qmc+O}gr2+nbJGJMH42l{K0Gnvedtcj!X5t%(E_o#F-=eZX-R$}d;9OM zb)FxIX{BmYkN?$qas2<P7I}t-2{|g6VVSEyvD7Quhzx`~OSKp6yYp0jf z=V**byOi(ipv2Uas7c%@GgSQebX1})tiPj7n4Et4v97Kf-{(EK#NcEfYy8NcPMGq) zG*>Y5uDvpcLEFLJw~d&%f@f^0UsnimFXw|)!!q}hsEXL^ckqMJ?<2kFGVcvzf5md8 zZFKL(xY*pc*>$+X>3PJL_wuvxx3SfG&=UK0m``zbd1w-EtTgGiRtr|V3JD%LW!;Jdh z7s_h*J16gcOn?`xOrum|7#!N$fp{*8Ic=4qD`VL1a(7_mX}Nfp=>VZrGDJd2ZN57Vm!5;DpNx)WxUSnIk@P(`(DQhN&=2=w541|* z_d60ce`^rFeiI3?q)+&A4qbsAdZeIM;T8i8s?tJ07iQq{Jd*d3eq^!?+00O;Abs$x z#s7@KKSH$#;rTlunE%JT z?&5ym(k^!`&Gb3%B-82)7=PuZrm{!4l2}Ya-`d2R4&t`Ai1z@aFS-jCr4r9_KexmtH2q zxmMpB_ZP`}oTSBW-HPLFX!on%g>6NnqDU}NL}kkZd@xY!>#S*kTd-`G3j_j5U=X~> z5@hljPs;7)M3Fup-brU%k&sILGIp00_jihRV|v}S6D7taRcJCy)?z=E+N`k^mT%{9+JI}!nh`q z$uGm~z8@I!_(3YQ%h20R%pGXA5vg2_y1}NdV%5|QsRzB(A5@N97!-%?+vslfe%n0B z?o@I*g;QJb__A=&WMT`?fT1|_elX`}TG>>sx4V_}A|7VKDqi5<7~OM1=|QjBD#>G{uC-0Yl_fJ)g5~{L~%ah(o|hb#;-`WJNtm3 zRwcSU2?!8}Pfg6L-$lGvP#kLFuiQ#XT|r-(52`6A4HfJzm^w^moVC$n`@6N`$;2mh zKSW)Y;jhRy>r39g+cK$Y%-KYmctD02@LRgWOy+Exd2=f=F9(1caK}!gkTd}c1&NwB}a5CCScQ7}CoRLFZ+bXE;&$8g5m^c0RA6 zd%0+wWIW1>z#hTQ(=vk9hZ+TQKEw^(#$U0T zs;aRjiBSd3Y%I>2R2WK*eq98|2S0CSxGIb>b3@h`Y8A}+t%T=Ts`oc4s@~Vg*|wi6 z>6AW3cl6J5Qs8vaLKvpnktU{%qs5^gOG00}0~sZn#*?^3l~F=jYKuvQ;a2@Zyr{I)fsx|iD7+4qMfKJO@k2E#$<@kfl{)*>J3a@^vBrB^W`YT#m z3GB>5i6|lgCp(c_vgT8T+$xRdnGE8)$p|}nx{*z_o>{`h&&8~Bxa5GX z_-|1#7`DURyuWHaB&QkpXS3!DQZvBZ|7!zD#SnWrejbrAzR;r0;26P}&RM77c6m|f z(TwPQ%o?1n1T}r|R-LfI*<*&1>4L(n9+g$JT86(;S1egOGjf?CFt$GsO?idh6g@s` z60aFD;%>;A6bL826)dmifsn>gZiQ2f^Vw0x>rv=p*r;1BCyQLm|IYB?Cudv1yj!Cr zSQSgSzw#mg8u&6C&S!CBRX?;xjm&$xZjRxtk-QCyGiXTd7-|A>f6msbu zEOji^YLg6IuvH5)uK034@LGqdxhk$r( zcY}&TJKes2@@t8GRY7}wQX<+D@s#r--52^RUsFyaGZ;>&cfK`1vfT%@@I?wzzs-Su zT-P6?HR^2dE6U(D4^o1pXyU zWCJPjVLlt&l^4Y8f=H+;4xh%Q$>PpeFf2a{=HofyK+s?EtCO9i-kQlIKDM~=l^G?W zE?MqDvgjS4-O3s%=qOqw21E6w(=653`5*}Xlk8q)3GQkM&Na2lcZQPAl0Aq-eg@c| z-u%!xDWSc>78{aLC=7H^%KVuL+(YgcdCE5e#}Eq+K8EaaeOC!1AH-q{Vz0KwpUcAS ztPBA_ULxw?+M$a2iYRv$Xx+AOX15K0%+31p*zq%tDUY?Ao>RV~S#DK%EYW{QRn3A} zB|Ti4Zl21RcJzWB*_oP}<*`Oeh0fPmXzi=)o?tzU9#kbCG6`b0y610};Ypq(W`wB{ zq9Wcx$l6uNanw9Z8GcG^C6h~zB z&%!OXyLg81F#VVMA%NLI_h&=Q`mjl7ff2t|z!M()gd zk-#u3@@2_lOc&Y*72@al;=H;C0^W1xk z7$X0fR~}S587XfbQp@PB?wI-*E8|ehBH~=vWa^Sdu9H0&TE~;CS`ve>Tj28@VxfAL z-ZQIXfCs^lGvC#Gtb3v3ZFmo~TJdU#yF|d{hZ_5D%P~my*hlQ%RZdU_-Z7K;YC)-? z#jK_>uTm#_4ej0!OMuAVJ)-DnxCQ$aXEkGr?GgJK=e&#MS94?O=Ss|s?_F-b9M2cM z$Fb!xdzY6C2E9S`mqBwc30zR#OTI{6_}bQ7DqgT{v1-9Thypvt5`Dt!74>B*Wn87P>rmTj6zh9NQB2y%yTwyY4uBJILExEQD6&KlRysaGC zfI3^oLj8i3B8FWj&2WR)3~&b#V%KLo_Aev}h@%;^{CujRYhK*?OKsn_>2qRAm-7TK zJ?{GCA`-~|qe+(l{~ratZz0NjEp`3cHrtMC=r8lChF3f5LtIT`n#@T)ug-U8`yiv3 zRx!xGb&ej!?vl+WdVpPH)LAPtamg7+{M`SA_=SQ06XI8g)*-&H4lznSExFXY)@g}C z42GabK60o`Fx(E$H+XD?8fRvG<_TnWlL6q#D6hH$p$gyWM%OV#88*k?;K5_2(+-m2 zW5FQI-J`c4oR-_0QB$=yrSja>kTjfzcML{mqLDtIXRg0;Zn_U?U2Vbp#skfRhKvO$ z;wHTF*=W-^Y4-kmByY#nnj$-#L-$MiFU5&L-XnIDtQ;9>WiqKK#^R`pB$c`Oc2L2Gj30>vn#6`(OLUO;VnX?jgRJ zM%hd(#_xoyj9%P4$^(bY(AL7R-MSr$VW`gA{p;pDo@n7C_MNN8zmxz(7V%iab_p?i zjQShCk}Z#v8(=MZ^1y5}14En_UPG>9xE?c>sSD4=HOROLZc%xfcPc=|zB!D@FRuLm zBl5T-6B+yBzaSDHkwcu+BT`tqD5ajlr`7bfA$n*EHj8N2kK}-!v>)`s&^~@R!RLMk zTyc&|v-UuCh{+46q;U5~YUnOkHOc(%TgV(KIN3^y6Il?Yr0=u`XCHT|96+81#0}{q zl78rG)8A(qxU-$uZs4v_5~XMlAcUr`eTiss!UV3Lop)U5K7hEPuQ z3X`*`a`tM<)GyCLSm{g1WG-ecl&=&JMw5wS^<4Qk+X~8k7r6_0d*uCx$nH}n03HLt z-39bpY7OYkwyp9*Fy}KPNxLVIhz6>St>aqQzBEv6RmZnU3P^I#7B;2M3Uc`Y`&r0Y4sWYrVW> zyS#lw?ndd}UNP{JYQ7_a&CX*)&;}nh7oIu2srvnw({LX;>mIa93%1K8i_HgBtZUxaJ8Y?ESYR-~G(cJ@4AYi0}E>(#6QWj~nO3{IBb?hC@S z&lde`be}ZHOmowG=xgs%gx5vwPx{Jq(Fn76>V5bQyXd0nCXewu1;Xg(fp8P|+M~%9 zDY$4GPFA8XB>0@CmgtzxZEfSb-AsJ?J&d+rQw?CMA`%tR9|$m%?@RPoo=$dW`4y+}Y;+ScRq%3TQNU1==P~P+}x$)rY1v9`anU9=Fpl z_8!MT(}45jFjm=ffu(sn5DO_iMM`^-Vjrj#gkJ^J_RkO%#UZ38_dcraBzh=tJjjR{ z){^{UAn*_RL&LKdQN>|zA9DsD%Y-3NJHTKn%K6=U1<`#O!f|YLoF%sv_5$eawtsP8dw3yLSpo8Ht$$QFmGMQM4U$>cQjmicE!@ zJ!pk2Z=OPXm4t^Py?!Lltjv3;TXA@jEre4C%KhF{7pd4?X=q2@eA^V!afB`6_e<1+ z=VUpL?=U^szZF%KX2PDh^LUNXp4;PcX9uYmEUe@ImqaD)fIxmz_3Lz4%~toh;}Ab% zW#s$Tr7fnIp^5&*O5s1Yxr>{55N`kt?dMz`px?}b@z`|mf-#X?gkhdh@E zk{YJD=X;?HcLx~(qRKdri|YyPka$ArMmuR0lbRTJC@xY^y2ebo?U&hZTDr{)t1=~G+VFlR zEOD9{&jsm=;JaxxEQ`F!>^!>)^8tJ8TiF{Z*1ZSygI2|Utvrzt&FnnHuX$^>p#2hb zC!ZE;3tBqQYQ>Y^G@6~z4!e)z?CB0*GDmB#TRKip6XGX2JlzwyXWoH=ALoNZI?!DB zEzdSInD>}Yr(c#6D=`mX=9JNE6)zndV%e4=PX z&`sudm0QEFtw|Gb+?^%13uC7S<|J~ViLg~NZ%dUCVdNBEw?bh)r4 zrGRQqf?;2BcyF-lJ6hX7ADt(fH8ozUe;6?OM6kARrg$F%WwDBjr2hS5(-_~k)ClC+ zsGn(xvTX21P90#Ad_CmR7FX+gUDC#qRzJYqt(}CW;D@^yodafSP$q3^jv=Ow98VLd zedB1`BPednhCm6&olF9Yl)rKV*^9%yX<1e%S+lTbaH|Z?_V2ATlr1=`6avsC&< zB_T_*CDb#Th<6N%6!%+7a)>T@s3(cnDbhPnvx#p*IUr*9&HLp*n?e(UG2G)1+i z3hCi<_GRL%3s)IQG&)TXM;K8qU=5zU2iefmA?008N!$ksEkgyN_IdXT4qJy|VFtwM z#H-hMt-5TLsp3(W$j4?;co?sZ_jD#@vwelbQK()Q?oQecD1`d|WUzWK(gt4->ezQxrl|du}i^IN2Qv zF#E};Yt*=-^a?WjZyneSxybtEz8yc~8raEJ=si*Jag&3VDha*iv-@ZVW~=AiHog<# z+?bPldb&9Dy?dqUacyj18me&ImGc!!h@@tUf=J%I6E$D*ouaK~Ey+7W;{>0aoN6RH z6@O3;s+43HG7jWdvO$+`-Ek9Q=unfMcoPhIOWH@KhTO;fNLV6ss8;$4rn;1u1*3eX zd%q*8NT;5uXI+=6`7Wh<`X^0b0z5P;zVN~cX|R)yL9p{muD`Oia)`zGD}R9WxMyD0 z5W@#QF>&>*&%LT!UE#~U!mCEw!sn1=3x8{Bec}CW;diL;Gi>2&-|z~5p}z3h1piw2 z6~r+|=c-6+Jo=W7Gnk#B%|`A0raq~eb3Vf7?!k3hE8h4;G*LG@A0?Y-*fVMlYr^F2 zb+veRscphPM%Y?pkYsD|`?dA8n0l0H!rK9MrW|jYFezS(hg5IdgjW#!Yc0kQM=gZ+ zf3L;qs>SI?tVPp2RNIUYX&6uVKN0O33!<%qj?^5JPW2G|@1(pb438fX!vzECN%?`Q z$?ygNH17Oi#8BMCkpN;x!vowR*~!X0-HZ($s1S!IQUzlyc9 zP#{BVY4BIRk6;8>Bz6blAJWyUXR0Z+7N81%@tGMa9S~B&y`;(Tq&UCYf6Fa=ltj)? z8yM+wf>~6sXQ-^`(E#E~1734D#Ptm!f=7br;z1PUzvsX0X8>vt~EUS}#GFtSJpwebbwq25oeAGdzlUpj!NiUgK%e?}>@` zTI3ZCp^wcF%KNpY*!~V@%x*ip=Fn*F8T_&R|BXU%9iYU)RVHExsB0yZy=Fe8veq^{ zo7uBGUpb~IMs@`350G-|tn>hQ$N-qgx+C11iE~EYhez#5(rNE{e7Q`3c7&dIwvd*X z5js?x?y>c0NhYnINi%Ny3+fB-Y~4};k$l~)r_dZhQ=2C8&Hlw-c^N>i{y@u3FMG5M zw*BoMTJ(64rf2#aOFHo+Of{kF?^LKNxw@C#nZs&8Lrf1Rz;AY_Q zDsC{$lX5+i=8%__t!L8TZ6MqmD}CHuyKkLl2m)?)ru!TpI@{vqygp|ksy!rZv;wDh zd6Pd82We~eNxau*BX3t^CmT7*w3oY^JRXiiXM$s09EbDKugzfRD~&Ajo>mKk1Us}y zUQ^*&?oScls{D;K%aiVj?`;@Q(J;&BId9X-UfC^`C+y{sL}SY@t1o}9%J1VK%Huvc651cogcG8>P=E7_(wwQ0jF(r(&BZ}ze)7ohv;%H?Gfo%A(1(fcPi;dU-kRfwpf3~BVe`)wFH6IuORN@1u71U zuSmgCi+&W@k&4)8RMVFzTnqyO5F$MQ@9ssY!_Oi*%4jOq4i%=ekL+266n?CL?FvL1=%ffQdGPFIdesV+uY)T2WZb~agQdB`wlNyL2=&| zs#Ly{N!e~EVXS|7!M#kQN5>~rlTDu)OAEg_!3-?7h}dR$8ju zFWR)>UfM>|O!=jwl=`qu?dYYx>*YLKX?G|MwsZNi|6n^CUNw!`-!>+rZ6(IQhV5K! z+0HZcTLTXEfcM!1lyISl_nFM6JKy_gYSXUsK6MAdy~O)yZqr73pRz}CgM9pzdvzy{ zLGgDbajz+oJ|3Bcr@WF&{V2u<=$_f&4EOW(ey-d!Vc~q!H=jCkqX5HJ|H239D*v*P zU@drjeOKmb;EmWk(rc@I^UcQhP!u)bm#q?w1~DvXaQa6l^`DgK^XC+1IpDW{i?e=p zr&5e}3ct%7!Q0b+ZFrf(JJ0gohS(ULZ8U?J>cAmPDzhQBL60Ywroa-jpP>%1#?j%+ zHK3d|FM3;5)&W+7vQvVwX4d{s>%9*Cf~fs6f;@xym|0%pXOGbq!BERAE)FGT>2^?t zpW8t)6~HtXi!0MKrjh)kQJ$5g`~OMf1i7%r zyxcM@!jQ#KI;48^lRg$y12rK)d>Vw!NmtyhA!Cjin z8v`Q)qxi)H63b+@aZu&=aGYE;`IvIW-%9NZ6~*DpS%fBKlqB#N2%EA4#W{zx4Y>79 zNDE+nRepf$Cwf8-ZP(roJVzV)s_*=BFb483t;Te}>;{I&cOjiL%(^1dH&$>6&>3w4 zi&0yxOf15@Ir^k(Dha_ohVfg3MSNMaqVOoxFnhHEIhGqBE_@ zB_Z_C#%$zfd8|IVE9r-&im8b9B4=J#$n*bc4?w z3{b!DrP$Jr$cioaLzVR}d>V*A`JeNyPb*{y{rxO^)9X%R^}S-TJ%*{^+ZabrBVdO{ zwnB6b{bOzuxUGobJ)IV2-fI1kT`Z4Pd2mDGYA1FJf10Js*vne$e|IrZG+A2t{>J`_ zC$Q}<-;ZT|5drrd{SiKzIjQQ{?Rb zwjbhRPgxdSHO$bRUi5-#_n$wPUHaM1mFul~Pl?v-PzPg9H+|b3VjGhrF~{f&>(8DN zNMI~`SFtbL=J)lk*bWC|6Ru-cf8`+n7_(WB#Qiu`{#`W#QJJt8pR07_y+zL(ir%;o zS#P^!z0W>ob<|)7$ON?T`P}RJ2)yGM3tn`g1?Tkli6g@Y419XFh5wref3`E@$lxsu z_&Zrcpe?{dtH0&$fqi8bGMF#X9i)w72Q!`hH>UUkV*}^2Gjq4Vx>!HI2+&w@dbvkO z&{Djd1l1PmX#fd3@dhw$;vQs_uwpHI&NM*stK5!YSKrgwuW%9VUk`WP|=^T&4Oz?ukE z?Bj9VwwVA+>^m#d$E(CuaQi;#MvM5^yyJX<3GPbth@KwzUSg!jE$eNSkXt}!m5|qt zV`_VP+ygC5_9*~6KQ%Q<$oY?Xs??>*VU>`R2>w+GIoQOZYi4CMd&x)&zxya* zm`QiSM&n$>w=v^mbI;UDx!7x==Ipo>Ng$GxT$8QGqN2?Rf4#zl$JOYGU0!g* za+jyL7i=>J5a{1o%?h~*NhnxRReF~A%n*ML_q6;uBeJIYa~{Z=Pn-F39?z0&>CbsK z>k;$&im}=E=e(0u#dk1Lk;N@bepyt4q0cowB)=PTV?I*4>VuN7>}IiXj(UcfZuct_ zH5t#GpqIs^2q+zF6z#Rnk|pQ{VkykknzH6a(!|p`TF3N27;3M&Qz$qJSB1ZoB5FrR zMwNXUuWrMQ3AyTOnFmMG@QIrX8AKca^TRD*raeb$Acl@*I-ZR7r^RIjv@RZ~jR%&+ z1FxFEe%6eWi|EkGHKiH$Xt;VyucG{qS$N-so$BAb;?Rduw~k^mSGf;v(xBeyo=7hJ zUd%7i%?6rI-unuj%u;dIM@Ff#)p>{$5qAqPl9>`_H8tziQh+7gM3jkLO0fJ7ZYH$h z%k}YRSr{3B5`Vsgk(kUekYFzqY^31$r|ZoZHoRyroSr?$uA7zTS+o<1A!)rkkRQ8x zeo8`oL3C^Rkitt{5ZB}}kCs{%G-yI5B;`AMJ}^9YG&sh&hvs7-#S@PI%<+}aX-9gm z(y>S(Bd!)RaMl9q-daN0b25FUrw7aDaCHP}s&A0dFtcs2bKl;w|m0;;J~} zBhbb9-}!IrOrR)|a&}41`@zt8y^9i76eoP{oUj%_5JP|$NLBH-J)3H(>906IGrA6c zP%TFP0qW>Kq5Oq|cmUwEtbAV3=dXBPFryekqc-( zB!z7a*izh6h;wRAGCocQZ>RmV33-J`_xwS^nAAqZ=&Wux-%cY;XeEb~ugoyM4Eos& z?FIv@gFhL*a6n48pJC6b6qm5tJz$WfZhrz~@dxcTbx|ASJ6|+48^Gw(Addb6`DDJY ztmCqe3_mQ$s%YhN7I4mDUi)pKa`t}3tzP$cFphP<-qhT;Y%~iPGo+>+prhiTg#wKX zNpRClAoZbXre&rd?X}Fcb7`6O8Mb9c3x>^J!GzqSYSCgrYS zl}N$DetRJF9@h^7)lORIv(P&KYa2d3#N~jdy9=9E^g#1^I|&&h2^JG)QcW*j!J?|DJ8G3{ttjUiPm0 zGo3ma1;C|~TlqL3M;O`7PKKs@*5+LLbcSil4YfAsx4=vOO6eCF8Y^S~rUc6Wm^bfE zs$50rrWi2uc#8`&ef_v; z-Nu;@63PkP=2hzUOIsmk@u3ds9LQjIHfKTf3-3~ab6wKW>dK`3Z02Wo=7x48>Dzr` zXq=G9(1JZ?!GvmbT9Lf1qj9bA@oGD5j+*TMjQ$u`GtT`5eiaz+pOhKL zc*|cgHn$k>6vlb=80UHz9{?jAmnTt-6MQ=De1MGLf0z`pU?s*R=6%h;gun%XiPm(j z?5BFIWprbMt*twJnDC}PyoiYtNx$~R4zMdH)&{XgItjNs$!MX2Z*^}4 zXe5j?bkH2X)0TUAca#?I0fpv(UT4ekBYNk8u64Jyc)+(M$MU6Of%e_epXtDF9;LJx z)EaiFU2Rk`I7JYj7XMe#Fc$W;I%LoZ;#3jZ@Z3+8v#Ks#bjcp11xu15MiMCGi$a@p zD>Th{^lnK#<{IJ2($TXWj#I?mJ;V9!kEXsDkE23F|2nd=DR*18y+ZSKiNy?3p~tY9 z<;EEZT0Pl{DN9RZ(V5oY{7L$q`9NW!6Y?kxRA z((fiN`ZEGmqfzQr*J*h)Jk3Zu%N3oKHS8htQS5(IV;8M3Fg9BxB0z!;G8EeOMazG8%Vc+sW0dk_$sy{I72heH&ZLUgOwp zNBiEC`Z{>=u707-i&?Kh5>`3hf`T? z#IRl@?p;Biw9*rT<+IXQP%8uv6Pmsgqrcnu0LNm3BlXt@HI}^Bd7BRxG5JpO1cT{P z1Clj!$QJuEM)=Iy|;sg4SuFetC^F-{aX89 zD{1S0Z9+RSh?Iq!SUS;irC#`KXCL?2QHlu|r%(Knk zIonFixXILFR>Kiy$^)ipCk(Ymm=l)Mv>V;cV>C(9d;O;6nS1~25pYpPlSQ|T%-u75 zC?h_Tjy#d#I>11}*Xq*N=9fDAsy8J!t!es6EIsiR_!!&u^wqst0kKEaUN>}k>%}wM zFCKF|)(zTdKQmTWCsrY5ZV{kR9w< z@Xa&~Au0;Si(CX8yE)0ZkW-gWu3~iTi*XYHG1FIeGVANoDq7_|*KeTHFZ#8mm;ZcA!fLiZQagfCZq)E zRdd6d!R5S{QfDoOb!vLieK};8!$mrtbHG^;;ry;Pv#dLeW*vFUUvcaN)0`doaatnF zmG_W)K;LmYcfmzAL$&*=nqNkd=LZFD3Y|E zm`I(UOFtN6UtM0q9pkJ1lnWTQ_EO@P67SY3H^{IEl9MHk%C2&=B)>|b_uo90`ULl` zcW$|m{R`C{(sicW^759x)rDC@XS&S@1tSw1tsaoj)qO_Mjc4mP!>u&oDJDFNFp8AM zSEwK@R$>&!$iRpp?$1IJ9?39I-*c^o`MZh;tv086EC?U3Q?*vOZxAiD-2r&RKIwMg z4a}$$8SE1~z$E&@Bz_c=V8&)vZ%H<+f<(=^>Fp5r37^+iT%##_q0!97dF>0Eeo>kg z819K6FCc;hhK*)`k6PwXf^#P4NQ|bg(cFslrAJFjd8(fQ85K=I2uy#~`!djEgWAS6 zOD12=Wkpa9b7(Pt4#Ep6{ONQ}t#dj0Vb(O&0sc@_o|u*3zD(tc!;Rl~1kEizqPP{; zTt0>>YlwIMsYLMY{m`!MJ^5y)U4FM5Tb95#5mEC7LOiPmBnjftislV zSq2{3chk2z^)gzC2Z%56l$O95t?m=V^~+^j2IoRHm?rrO7mLuL&2AxDw6_i{K|Ep>|ni*SV(%He6yL%3a*3p&?CjI}_`k#h>~U6nD{; zB4LkP#;?cX(ghaNcO>aMBqK;%1ym9~A}sAfb(aWtIPZh2U{;Hs=77kPGYe*u;C==MmY8c47FO7GcL;MH z{Gy4^)DP;03WEgFHmX#lARyoZ6Hdxgco5-!Z2XUr)e2P7X8ANDsBSZ%LRk76U)!ZVzQ5&qEXOs%AKak z4B_g5 zd^#8zv(`?}M6#eoTw|ecjzgdA{PY^o=c^R!>tw)0&&HVLw=CT#_YLdzH2h}ZhzYz3 z6E@f0%9wugp2f8@T2>p3!>)Q9zRucAa|4BNup-7a_)REWvssqK9;qgR{!X0GgY!bnIhf zc3&a_ajWIk>a+B9Kh;Yl{>n-sjqKvDSU?cZd<^=jigXgnSDfSaAxKQ4YwT%qTO%W< zKiy3VhrCoPD#P@~?N&x@zqe&;J1Du3;oT0VKcGD}M_QN{ZsKe3@mdp3puR0NNw8$# z7#+4H%@JFh5m|`{zWm@v@h&ZXp+7tsZ0r?UoNiJaG8hs_UPp*F?+O*)*sI z&UQ{?;CQ>?9OrMTiM}CM8g*GqdF9HWuiC+5Gg-Q`j94)ESUa-dn;N_W|A7EGFj=`hN`G^&Fc;=!=Mw{~}JYXqyC*Vg^n1f(Ju z-JXc!5eN_xnyzcQFL?1j_XASlGgsKb_6k3C{ZrQ=t8gr)G49@P5=Less|;jM>#w*@ z5%sc4$M+D(rWl~;5!!y`_C3ShKGY7gj zh;-MlP7y;qZz1#MLk9g}s(?3$K-Qzd)Aw9xk7KuXu*b3QkYtZzHx;N`?6sQidyS%T z4ZzMA4(`GHTcvj#+gdriYc){#*W=hvOf?21v;E2p4{j#|)$r7eevIg&>BkP|n62=q zE1UVy(I}=f#WGP>5W`ot{d$Ej=@Og+oH9xl%33bjU&AeDIK$1XAnbrRtz78kRvJHtf_bxicl^yYAW+dICtIWlrP1Y%rCV(+iK(%FzzRVUO%}ptdE9>2l zu7?!C#RYQr)>$@&UN-((-D|vv>|iJ{E50)ehPeRt&2$g6&NEt~VE2CKUItow9N=E9 z683&s6dIi74!;imwZ{FE@=?J4AlVBqH`5ujI{$x>72)NXB!%|7IY4T$JCR?8cTokP zTIa8%NOlVgA|`7eWKL}r?o-y8${NW#S4pA0?i`U4Nk83ue(v7Fr+6Gu?T9KpN`~#x z#vDwOCyEi7yHq=iNYbHO7;KA7ooV1m>O+>w(&6e?MGd3 zoX`CeH_&wKGdAt0c-n^(NZUjjIw1{|(1FqFb*ZKEp{?AsdHOdYGm!7Q?Qf08%yipVbi=8*#CUc(zILD&wXMhNR>0U+H1&-SQ8+<_zkV|i*4Uxg`!avI*5$2%b6V$ zO>#K@`4*MYtf>0m&&7g^9UQlibIX#CbGda@b* z{OdqtJ2-L#>k1EaASnirDFh1)C+7HKLwbZ(Ilmn;ge=dYM#@m#-{Njf25(UM7wf3i zsRfeizooicLuT3xz4!fyg&PN3W4`Z!L2r@*hz~b*66Eo|B12Wo`sQ|a%2isV;6kVq zsz&vwcwy%*A(7jY2L**yWbS&+rE8yH(M7v+3&2hvl1w(Tdql9=Drkyvt24?F@tt!4 zFeWSA>*Si;Wy9jL298o>cNNI*Zd*Q`Uk<8bTbJKR0gMA@SwCBuYxH+;)SIeEjcp3n4v;yRdVkREhJDOCHZwFuw= z#GME)GJzq6Dd}Qisrequw+2Ar;_+Gj-iuk%)Kx>e;;E1gk^ePf-TlC5Z< zrA67rB7oRiHiycfXrQbe>(j~aGH6__ys$qRvb>Ux)iH}R0W+8rwjBz!Qkq=Z} z1F^z=0084^114^EnududyPueN!}1N)hUF)_wIT#rCm8xw%TW?1kTF=5Jp+jp^m?d^ zZ}?E>9xkF-Mqi5HhwlV!ROE^TU1VrDnhdQ%SJC_>Juqg<#?}twxanP;smKBBRHW62 z^RMvW-iA+??~j#bz_X1;4(}ymwwq_OmBI|TRX=IArK+MI<)+_fBY3MRpi`1rFAz=KN(qPaSlr3!_5urNFg#65 zAGQ13u80}#AG9vrFoTr_f2NVVb+2%;tJ&zR`oYl3lq^efvUx&6biDYGnNx~vCu047 znGjL{-pxQ3RlSXC6`;ya*EK@5$FU6X;#|b4D(CvgjeTQuJW=P`sEfU*BJEuS&Jj!M zH8)XJ9aaO^4mhX4gK8#r%rC;xB@OIM-AQKdA}>pG z6n}5!0@e$wv*7N;5E?Ztp{#o_e7&8V8=Pm5h3mRzk)9s%&h5%FaNa56jNlmCWM#R3 zG)5DmvnP2IKH%JSD{pnsjOoExvN(L5)f_S6Y9%) zxuHnU4-~xH9=YMM0g^Ui&NIN&$qi+O9S)$Suv27_-l3+$BVK8?S#qTo94Y>1>~|$E z{QJ@R<<~vS{|ADD`SIn%v$$`KiW)ETCxQ6{;TDpw@$xE@;AdN^Aj)xxHsrGtE30nuoh^jr&K zqmDG^-#KS|DD{96BzzT;2H!!Yri=fK@lrcpaAgun8sO0seU9uHjuxz5Y>HcSmR(0@ znBsW4qJE|Q!9t{a5F-tQJBx7+L!8DBi)~ua|3bCq#rUWWRvaL9lhDMLv%Qu$`<^`>ZqQ$qo z)J)f6($_CCwAQTEGJdwR_7OGB`86Z0zYud)kBvdzBBUTpm#kH_^mvh^AvbdF<9Qd6 zegek&VDq}8QT&R;&4u6W`lTYHT?yU6<=2P>r=*760 zzjJG&__h5=dU^v0f5lpwXk2cU*``&wiSyLu?eCbbfzmj^y_#=okd%wx9{T3ySs)K3 zmCxo4ujhvvF=FvEM*K}B6$}GGE#7$ z0Ik;Fw`ekHyH+}pxN?%5K_>Ui!F(S}vCg^nyB8QPHd*st>dX*XoOkKyACun5w4+FMeC{-#B;97l_;# z^EHHg9LV@_c8c>RS>gs+5~f2&a-_kgN`5lNW>_Gp#7F;9;xLss+m_fYc=4R9b<~?n@w4X4a=gr%s2X#=Awf%53}PY_rgxAJ`G|NO9r>|?sC_W7K5|xYxx`*Z#E~f@Sck}{Qi9Nd&XY~-5+ysN#==LI>7Zcoj=NomATT^a-m+*^rX7DV-QWjsy z8+`J=Hsb*-_2x^P^tAx606RoR$~R^T_Ey6Frb$H$VhO9NZA&uAflg*v#_r7iA z3mvL( zz=A(?p?YqUz=EIkOj{t5cs85()P*ysABuMl4F0X_4+oL`Wvg<{SKQBOcPl~wQx2hk z3Fl6T&@wTn?{SjWRuzR7UC!uIy()Ly!W`v|m_i_e;kUuhkI{|0OKg;G!o^kJ*AnGS zD5JM_n8fZ`gTf3)LSMR90!c`#>lap+KPRY~Yq zv&dRI;P5#+oC^~5$P>4hi^>zY!;1-wd5|a=UN(l7DGuGGAI+CCIG^>n6T~m2do-9i z>HV%OhuvyFC>38bBo1P0HUcNOtnAfaG-f38z|L({GW4hW4wdC4H1}@(hUcaQBkc~j z$CGI9kfIM;&T_}n8T!Q8eI4Tyesww;3zP|wQ%6vgc2|Vk2NgD()XOu9Vw_WiCS}6B zaDbO>HXoRDj>4GJ+;x*h0xpEEN3@45l+*L_s0mlGFQd8AH*u?=D12Uq6z(lcbRHPk zvq)W?T$)tGp(PrUygt+`+P6478Bu(9Lzwwx{W&4Q6X9T?S_&#XvD2ai={m6ICW|Gp z$av$(%L56@_UB8b$Mp2DjSwP^^~*rz+G&zh}oKk*pcB?|F5hDNMi*beco z>Wqf(s*cXWs9`Kfp|cr9Slszee^5uKNrz$;XOnNLlNCA@hjx1EvK`Lzr|7}3)wDD+ z+u=u1x!a?;-a)9@`nzA=1jH>d;Wq zgI{wjr@2|n7)FsGffzHzj%5f>?w4jpGGleP+q38_x&Krx;EN@i=V&@*6D2|y-^(IL_?p2 zVHc`)W)Ne^&g~l(qE^CU0HgSYV8KM}ap?w2h3R zAO6m1{sFsQfG0}#Hd4wpJ2o=4y)!Og?qyRC+q`3%#)eFR7H=?iOvZXdH^OnXbf1Lf z(HZ!}5BKnzAsa$d%xm!Gl4(zYCwi35ZW_?;hAPg zCAn{&MAu-yc%5dIyhfSCOtnLj`+!Z{;3cZ5kvK9<3%>Z3*UD=KaGG+*_iE*J7|+Ev z>A^bc-s_)6+^yIwzK48J6#CZPO?Vs<4KC;Q1jf@+feDymR|PI`A1#y(AYQpc``kmy z5$>NB%pZ`Z_o~x}sLQ~kcyRpsxY0?>9PJcMeT?e6P91WDo?NIt3A7AeG6sfIeh!Am zvR?G`&1L7*e&+yoPDw?vRnd5DoE)68u}sAt(X*KMj<8MUO*9ZnajC*gJmzftzY zhbg)B@Jy9^1s%iBOZ?ceaMkfOalQd~?*lqmMo6KR`t#s61s#{els=yFI0PNBvE~%^X<)uygeX2J=y1HXby8 zQ4V|5e>cIn@>SiW^><-iH69njNcwZ1d)3Ggoy#{FY;Rl&ozMQ2&b#XAT;kC=+@mv% zBx{mT(tyq)aBD7$&Q)s-<_o}V=p3aSmd@A_=rq+ZQ}54qhR*Gud34?b^Kx4mI+J>T z&3-(Rx){=_$~e{Fo2QWABdI4}!`#*aXm>ZE?poOLhqhvt@ZL=??Ll~#-h5FiZ;r=uHfK9wC-hBF(pDtZwP{v^-**KsJzPG5~uc+HB!a!ZM&zeEqu3) zijL%MIYSaw52xqtmTMNlev_A((1c+%;W4- zP6-rusGEtK+c0jIjk}V#k-6JZ4Fl}7D@A3<^4Li`zQ|M^N!?+U+|wS_q10am4wipw zRhc&o?L?Fk2O!KLDDeVdBrk1<*2y8rb3AHP6rPzJm*$Yez#aMsI@^GkpDhU65)zI6 zf0Wog`MV576N&!_M#A{4OzHgT*HsdC>_|v1^N(euTe-!(@eb?|Sk+md;;VWx?APd2 zqi5no6Ur{;0rK-Ar5zKT4nHs`p$k2~9z82VY;|`%j@=o7&8wT7#_JjhyB?3rL~z@= zMKV`xG^2ye72RXok)7j2L6oXp^x zedSF3RMiCYYy7v~0gLfm!#+UJ9mF3c+&0T3Y}ILLxf1fcgvcZUJjepfEh81=)K$$Zh8C-I}1>%K^!UOaSf)@LOBvt_5*Crjd5Ywoaab*I_9+NMdb%0g-E6oy&z zg)dCj)ceUAH)ZYZUs~JS|61+dFWk3m-k0sqv*t(2zTKQ}J@p1GhL%1#NMixDDIJ;uYg>VCIdAQ`%J7R&k$ zA2Oz+dp|_@S{@e1uu8@a&&XIB7X>lKYI*s+lod345JlrQ78Fvqh z`<>IkJ^sJOZSU@4|7)BMn==+2=!oaW53cRu0Q(ZQt#!hXaZzf7<_w)&hhlpWMBCkY226o@lhi)EIT>V=R6uh z$#aH5lzJ%w4rBRx;-Ococ)3z9p-g8#`))RM?(>=yV#X6Mf~$SsB!$>o2ED)HPaP&< zr1_D7TRC~d(9ih<6EP}K3$j%o?ss6hQUb1_Y^>u;t3AAh7P z{%D1y#odB0c>DxL-NI8b8#~rUJiUO3Zv9X`c`s2F5-$$8pnd#hsUP*sRh9)Gto;3z zUlGfsO)HLEoe1WEq@d$Xak5E8my+V%dXI{1|HYfKjy?&!C&26!e<&LO_!%nq?Lza? zL5=||*mXQnKX(E5+e8GyN12zdo+K=Kerwl&o}=7F$`dXqA0#0sR@&CNFozSg$koZq z(kMmU><7ZVm;qyAl;r-YN|^J{nY8u#dt3UJUkJ4GW0nkV3a)=xGSzD5%tgi!<0aCa zX2(;T3$g_;;M@C z=VX)}Z7<&<_ONr=^$%8j84OGSS#jhlP7<%Cp|_XL^Mq=LrYO?;LB_7tJ9ovZb)9Kj z3^mcdz`UJ;HW>Oy4%RD9Wyk6TC&hcOI7zurIrm`21VAsQ>BVZ|DjHRe_AI2F?U_@6 zlTBsypa4zzQnqWOLUXc%5fGnE7MaI#qHV{ z;!DMk!g?m|?I4>F&jdqf&*ssvW9U^+Dmcuta(MXk+>MpnXJ*$nq3SW`n!3`vx0O4| zSxuaAu;j3|7tY_5Xl*+fIG@ofEb3h@G}Hf7Q^&KN8!ODf?M~a6)AXTETk;1Zb0o(( zH&aHaA9G_Cwi!vA#W`RxO`#aANHd~-nM4%!L!6rEJG z^H`$LgWe4$qNMbEX8^3G57G3(?O?-_(e}d2C)!?kriKko7Xy%Vt%{4}O(lt*f|Opx@eM?^GyKqu;O8kxG} z2DoqN0NmGXBxt&|EFLi(nl>4qspjh8JbRPf z7jYlUH%sU7&9CRgMQW-@Ql=Mdc+~jVxgAWU|Q+2ki`}3abz~Xr_R78*G>&EVUW$23*;*6V@ zpgOS4URY&sXIM-_F=_|X--KyaJ*JC=>FWjL?MS|E!t|lOFosFm@IH*!Zl1zoi?5E; zm^`1YUv5?0EQCyFvC7e9lZrw=$jkK$>yGmJ%wKW4nPNJxM0pfRzwS&a0rJvR=6i!Q zCCy3k#X;T>itlAk(mXy&vg?qp=#h_sUb5^tQ9AK_=M2%-jpxFsDSJ!yDG1WL9@k9# ze5=`-=@M3*c^e$gIemUAR;Yq_6$mZ#D<>H(z+vq&Yr^5}avlXbE9ZOd(pGniUelM_ zW!0L)+vUOYZM$fs9CI6KxEv?DM#@d(QInx>c#PqtJ-+*R?}NPd2=y@2$|Z)6vIamP zw>x>+V7honoz&>2o6ooc&7H$%{WT)wm+{bTjK!HR^Uh=@yr3B4(k#Cej*_<^6T`Rd z&c1m})HUi4{B@jRqJC7=Yl^b3XfwG4e-dKd|6}e=z@sX%hW}hbAc2GqNLZ95jV*>n zNGm7-B5CQsZRu8!MOJmheFHU7a77a|*QV8Rm>C>(#sx=IURM-E#B|8UzApl@xZX>% zZwV2|_d9jFlc3J{z2EcvpXcu*>3gf{R-LLkb+)QHXWtsxna!8bSASJ=_~;#Mz4kdz z)P%g2<}01CLE__#-MXpysgYiSzx|&TH)i?YbE1W}I|pER<+U*;``lkEFYF7bA$&Ad ztgN;{KX|!0dYqtyV%!|s+ijk>k$0@031$yuLU#S80t#dE=pE)i(oj5Oc75Fb0=kc@ z-FbtckXNV?larHJIFv6Ot+yV{kOvgR3_3{F~z{kIK0 zO8!WM*h|%^w>)KEO)zp4j+UhXeW7M{YIGySbSX;O)BED51jMQlTx4xa#ZPtLjcmvFjdb z*L{?_J;F1TTzYx5ZY)|2ZQ`*L%nf_yP}{h47rJrd@np^tC;iw`5fXL&4IdT1J@FMa zwm=5*Ix&NH3Es+KxnuQyJxCr&ax6?9LY_JaXkAGn^k=E4eCA!(adLy5KC2x#V1B%b zm2s4O4zFSb~$KZNHv*W`bKW!6M~n%D4G^D>ca`&&j96XxS(~ zudba&(RR$|o2tzy{0|Dqpe5V;$bgI*r$R)FEgT)qkRhslagWLqGU1rHgf(mZ2?;8C zaa_dGsH1(crXvl#qqsVZI{$ChKcpU!opcd%@e5UK8305T{TV z0sg1*63RYw3)Yrr5}`-Y%e4v!>t4yN^XfwJLX{x9OnVyXMic8*-i2$uKSnLn+3kdU z^)pvZmrXD6pXIqem(4J?k=DrFmd)yf^@)+5k*2Y~xzP8#43+|H+?+1&!5$>B@Hn_z zyIMDT2%5)k5&Agv5W#*uJk>KiU>pe;rssM+q+{SDLM5WGS#^>cYFCSsU;kt?`gP0% zCQet+@wi%lZAD#nP?8vuQFa^lHt6Vzpxkt1l`lvCRs7O?c%e-cU`kV=g*uX~(88M@ zQ4YSuN7%O@mRY<$KJK}}t>-n)Yco#+vqwOEk*1fWb1K{6-4N+an)i)1ar3mi*7F+i z(nl3YS$e-Jus--80$BFjB-)4ZtU-VxM5$s@%y;a$m$b!L`!p>Y;ex%IG+QWV>$VF? z;;1&w>JIN#)$Hom+r$+vk2d?2D$sW*%~D{C*lsCbgB1%hw(>cVg#xs@tf2`irUEI4 zGtK^m(B49^_Hgb3PBs z4f4TP1u+k5;zj1oBM^L1|9V&pNamD5yHN_orVD^O)Rn&59!C6dy=EU-v%o}aE=2>z zCSSKr))s2@<7hIFRr7eZ&sgTG+UV1EaB$~BPr?37&y3TTJDy$lPb@Hw&sQhq(Q+#E89RL4aO@Fj6e$KXIs!aD_wpmVq2}<7sPm5Vi@zBhk_bvN*wZQXqky$K*j%RIB$ z3nUvH&vA=S`duRKC66CceMqYVCF6ug4H`C$U){XtIWbv?hXb1nqH~B?J!PcWV$_)8CO8QAW);N3dP!)CvO|%&kk`5AP+ytp9AtoKI~0R-FM5Ia_^~6uHs%y<4e6o`2PX-h!HWa z6nFDIE01~{-L1gn^+Oz=cW5OSYPyhJrm`J9iGtDngg&{9yhdbQNLG0?3rk`5+J@D- z$iyFItlC^h_i)2%FQTD|#dY<19o<*gSKL)HK^#7&|G?FKYs0E9mh6&q>i(u--5HW^ zcS&I8^B?Q$?k1VkTVMB~hIMzBY`ec|IDq>|!@7G&eyr1~?lbju_moV#(*pOez`leL zv7vVKEY{~ZK5yNqXq2nh;vV1y^G)myqx&1I!=d~U9}AFp1Z(sA#d14$T62nkfYu{K zCa~FJJ1HloFzcqer!hP^3N!d?6-Ytut6pnM8T&6!iN;%6ETuyUuh?Sd>RTX&(DdX7 z)lxaDGE=-?h`g89dr@?EdZi2HczPK9B$PddXLGfYxE<|+`p(5c;MLZtl#}>5Y zRK3+*Qi&38{|tGHOgT+qRH>6|$i=@3F?kkBXTJ1pOc_RR53<88`h}U?r-i-P)qyY( zpxrx0m=AS^>x%GnyI8;qrg1)xb4^&z5;l#a-^M=H^Lwq*kt=Le?vWZ=!=TjuX;ad;4c>ML| zutIEtlaHDA2LVE<<+yTXGHLUP*Pu>WQxo&j`Ru7{VxSBK6nC*`PxlHO|Dv#L#o~X4 zV?SwKReQV4`+t%8L|Dro8+k(X@y?$!+DH!f3sXG0P=+4UUYR-6Uw5-*43=eex2W%J zH#0xk;)opqz{7A5RvD^_5GjgXF0AuKcG$5Poa1^0&Zh$W`*dZicN$kCXsu8ZT0 zGEQ8wAOT^Q$gbKAed8e1c)B19aRd;Xo62)6oQ3{R!=Cu-di;?SSb{feAG) zUX_s(Y;|WJI@u-Pp+$jy5GCcv&~MH@oE)Y)x{z{$g>9qd&_AyQB`_g9%*NV(IQtMv zn8VlB{lZM6UM9X~%FXX)2tL9{EFoX!$OQYfoprU)5(ref^4X#U^*phOEmi6E!P(p7 zSvConoH)xP6?7rUsnplbN>T-xbU*FcSA#%g>j^2fzp<3!3V=-Q2c%e^@h8}~=bsTm zLfJ1orp_&t-_s}#D_#}obsib4y-?pO7NL12r_P`#_Tr^iGcI!9G??-*1T&cC6S5h~ z-l`UCux~3~t?5)2Oc|;2FS7F&M)MD{#I14U(>>KCu_^xQ?xt;l?hGnfrGUzMtlc1_ z>tkNj{WGgbJ*k_L(jg*dqiwGzqrhjnk1LcY;-~*;On}kp@%(2dFo%go88 z@wk_t6l}_2|5CY7k>GwF6c^0t4@0&7Jy&q4Qcj;6SIlDww8I`bcYx}p<115Af=z;B zb?aFkW&80>x@m;v`E#%8{mQ&mS26O4mAlcQ^CUTCoTG|Y{*4QSXkx<)f$q33P?J$1qH0Z%HCn$ zgRI-`64Qa`dJ-{6?)IC1M+9dVK6*5%?r^jmb^G>i?G80zaz=`jT!>p8iUhy zxuZVGPAeQPX(I28Z!Wml4+Eo?^wa26Bx5PNE|l4xdQmF1zyCT z4Cac^>8U{Nh_V@3qP7OKM|zE;)Dm6!l*;p`)>s4jf4aIn3-(uA`(w(aIY>9 z5VGI*87URY9?%34=<4@{L#W-7%S3Kk`~mNww6|1|zL(2lB(?P@DYbjTV&Op4(3N!S zU+tU3sXelU6JM#;pP!^!e}NzKllH3hn`5o3qZyY-0SIOS4X=N%uHo#M!FETo-CQm; z)7;|Lc57=$Hr;mGgjnKRBw8I}Aas1d2BF6U0ik10e$3uB2z_ZWZ=6cb=f`YkCwHoc zjDu%=kB(KKxdF|(9MDw9I(D#Y?R&lY{BnNGZ`!GjNv%(eR>@EDW0u&-zpC~0C{^qC zjj44H$HO^cdOZh;-Nk4QoTDB?!>7R&k=8lRfjOyQ{~h83nN2NY`?2@^4E^*hb>)!o zP1!AOprKRI+whP#p=Hlp021iE1zhpKRo!z;3&O@Uvh0YS|v1L;n zLY)=4x}YM*4A89gAr*NRv4TIG}()^hp4lFBfTHxoGg#eYbBoO7JZ?R z(b#>Ah%&ppEWM9{y)Y_z3Dvm38+@p7u6uW`dplD7N5LBsiDFok>n!nJMlr8Z7JMRk zXlQ=xX>;$|>WwGM%TbnDp3v+jV-{AJo^V;yt3tCb*fBNTWjyAZEp^Y%)yg=bk5K($ zuJe!=_n^l9;0>Iu+C4P1pwYoKUxk5ijz`-X9MhUYuY1B3$+_AgKz`NhJd^7_?RA%F ztG&eYSncp?XFSgRZp&lr%XRM`5}Kd!q2sX=!6E6n+HnBy;0dp4Ms0IF+J3KdZ?5}0 z4pw>Hr-p=<^zUB&>`(#7qxT#m$>v68|7^^+*eeSiP`901Xk~uGUgDY37@q|l~ zfn%OWTj6zX&2?|iHMV-)KMxJf?W+BC$QkWW@V<=jT0q(Aah~zGYd!AOp;^tg&6#m$ zuJ)rhcw43?ytbQPh(}w?9EB^IdfgTs^g2&@-M($nKgJXJz!^vLllyjxA!t0JO;N6#QooHykc#YcdN{4%`w$i!V zz1{1s^#-5l{y7C%e~c>zrZtDear!4FioW;pGl@xfTvB8Q@G@;B&5V zZ()wx-h5G~lTz2~+F4_5cug~Hjl0TO;RzKs&G|H^SaOPhZL4uKT#=wvxoa3=#%9F$ z8@jrW(tLHeBuT4ruX7&ugce-!Ud88@eDW#~RT}HV#VxdT?y&QeC$wbHq}1M9BySF2 z9Wlzo%agURdyR8TXm;Aj*4{>dCIFNt*Z_nIeisX7Qs^2aHo7XSrq(zLdb(>E>r1=*>9u=3S_MmbE*RL@qt%9H zB|dfX{%5`J8n02UtpGDYz~p{Eo(Y#UXUTX0aJ_pSgTEylPSw`CE8T0fYPv8hT$!X* zvZ4lg_br{vN$GMo2r#R~O~bxC>DA6kFXx2I8*AkH+zO@YnVE++ zNzUsuM(P^(VXc|~4WU_%gpNgxC1+l^xVgJZJM69yjFfh=)@}MGT#@Q7(JE+sy-@<@ zHvaMD9Y0C!HQFf_jj>icYMce_s~>x_bGW3jw!$sA@7^lyhRd64JXg67=Y|$s{`cDM zgS{Fnka?w6tx6idb)I#gP0{v2Y+etSIJA zTNHmW7s|}<98b8Ec8}*K{p>aNGNe`OnV&aWCSK#wc1s^~wV%C7)w#ymp`qFR`yTE8 zmDjz;Q?&&ue9_ZSjI2^awjf*#8LmzWi*4;x)kbZNv|H{;I_beViUJ3W`*Oq9?z!4e zo^Ux0p7bUiyDF4?sr#76@FfB&WPUpozZtFndE+yYTw$u>Nz&cM-xkoF` z1r4;59-7G24tuq2-gp?|gj~auJjr8tQauxMjS;P*GJw_5(@62;BlQA{!G=(p9&gJm zqcJbqBFm2=6By>P_Ys7vhH(JYSZn?(Tf8ke3Pq+xdYL*B>16<3W^8dY-@zxGpqsv| z87GQVk+j!d=oN?HaXFXxIdwpQ=F~-p_ijKA9-Lkgz0F=R_#U-|D}H#812E7nYH%zk z^1cLWR$`DX`k?$g;qpDjE<){-0l66_9!CO>YmR%3qXF$ipxbeCa|%ZjwgQZKu*8;x zqPtnKJDs8cv88*!dQWs`+ua?82NaN(NeB+T#~&Kg(-?9OYQHgNYVcZ@ag7VHB}2(4 zUSmjx3X|P(NU9eH+g{_Ebc_oAbP_O%p~%+%YpqY6R68 zji>gn1N4GN?g5j$t|D}jV{a2~!3g_9FUn1Kt}vp+gzm`k;<1tt2p&hJ*BE>n=au;f zBHMC<zLac% zc)@>$R>AU^Yte6^1yrD^gLcX9K&v1{@}Lt@y%xyM|qfEEs*hTdZw&UM4IkmHNP;RKKSuuM3dF;wVK zqY90zph>S24d4*NzK&7?>n822;8$o#3n^xld)+@7r$898VHs2cxZ6D12{1}9XD4ht zMOBETbC!vtTiRg?BS5_o`Q|>aQ7l8f$6alJ&n7adM^jLP)_tS^^NOxwlzZH($P6!U zl&kHb@42DEOqK0k<#7|1ublq-8pI9(0r_%uLgAidgioKy-KV}f&?Wge#tZqMAL{lp^^g%%Qq zM#@q_1$XXTa={Cvu*5t`r*oYO5i(WT)yzHk<#C>{%NC~FWmhrp$ZQ_xX;rKY9AN%D z&ec*(sF_`?)RV-wNN)QN8&C zF_r~=P|z;uYFB+)IwnP>V>znmA9qX@`;UF9>yRu2uk_HU?-(7>f7Lk!tn`nb_^G;K z=l)yAhW}r7?7w%)o+`=!>?oN!qrPL(fw49Sv8kw^F?uplRbAh?|I)FWDEfcZF;(S% zoGp8*1X=>aGj$Eo5Mb}Hf&G0{yhl}C-@E_Pv8k%)A9svm|E*IB1qIkVR~`UQvvE+` zh3P8L+3axX<>MZlyO(fLY6El zaHDfj5sR@bLHK5Tp=%c3$1icc$PWTZhAQYj4haXxZ5l3xsT5#mQ;0PjEhH?5v9c+p zU1$~H&QUcj>223kDh$Pdpa@_6$u3n&v#+XB3wlW@DdIlO#H$h7D}@*k!0u+(aoPhw z#%ci=YNDW#q!_r;i`S`+Ud-j^_d%aW&L4t4pF#}%AAtUmD)s*kdJJyql^SO0zyc~g zcet0hWQVK)%uK)%Qhq(^Af5u)Sj*rSQvittYekjt@bUy0gA8I6D+^tv6cCz#^#PO; zu~P)5T(M^&V_)3?vq7R7^_XOot9>uHE4kxad$nTbC{$<{(Q33Z?U>iNuAOmLsxdyD za9g4##MFrELyb4{ik)37%5rf`ZcMY0J0gCl>%sWip&6XFpGurdV=$hD?(P}h*HIgp z!I6o9wn3X?EuL=GzR+XYak*KgxsD+SI2gCR!C_N#1-*w&r9Mx=shs@rC|9}NO3kZ| zaoNo{m>U|{GuNoe?RJ!?#)BTZ0G*6$drF(}6_n?Q!FspZhO&`wkk7~sA8g{idsnd@ z{;7$s9UL4=?%*?4(ULKFYE&swhfz5x8&k#UhG^nTk9Foa;%{~E$pdX2{DNM#4lb(; z9X$Cebnu0<>U3~!poo2kyXzvoTC8FF!Uqo7P=rq*qe#Ve#!RsG0#yl2$LBKGcm%k|6nK5NM z6D7M3N_L-^((Onk9#%`GbmJSQPU-GILuXaFSpoSDD-Uw?j9)g?L?vPjzZr)I5?x| z*7H&5Gf?O+5QV-s3Oxas7EbPkLf>lM zku{Ss@qCrT-yRK>fYV%mv9mm{GWpn9HY5U$mN<@YIX0(a#vXEn zPSEGwkN&#PFEtM-^k>M$JYFkDfGC(kv>jkT%6R7`R+sNfuv+yag@e<5U2VrSSUxv34y_>H&L@eY9? zn1Eb2lK)KHXDV8O0nRqiVSo#0eoS+|@IF~_nCT{9hMN>G&Q^Tk{q0zD9Jafg=L4AU z?&a(a_XNTR6Is!C>cO%%8jIbq&P4$+*mc5Sr!LNf4`r~tn-NYEyIiiEact)cTT~WE zrZJxPi7haNvnV`A2pTY{1r17LV#B-pVp>XdEPSvlt>C3^WV#M?MWb5@?FeuzXD|t7 zyP{m~6A{=Td?=O1(y-H}`@$xb<+hxTBBbbxdXzZl~@$$M3Wgsxdi)s^a`3vr?$bD(~gsNGU*t#1-d}9 z`q0#cbc(bHotcEObu!QU>Fli+F^W7N0KR*>(qbm9T?}}GibiKZ{H`ntbfcAibc~j| z2Ok|sLp|tIKl(&nxjku%XN$T5moSJt@1w5Ky=bo=ZS|(FlZVn!CeYnIgjVo4_+SqG zVk+`44UVMjWps%Yt6fn5e|dv>G*gRm`!b3=9%4=($I;H<99p}A+J;?AQ~juI>8!6I2}T%Yi}DzWg336>vU=;b(aBb*?pcWEKbKrpnbX4(M^XZc4QtKP895q*#M}#tNUYQV*{<&le8IGkQ(@?iIfE zD}3RViMqRlk!>1qZ`ZYbK9V(}tXw}$&-x|*10)UURMEA;d(u;V1*M7C8uuhu`wC9y zJF*uVCd;?Ly8SOwD#7+ZYdQhHV1HsGeSC$A2T>IfNJ&BXbV)&P13UwB!2 zpSD>>(Aep7b6N9*&siRDZ_=Hs1J39P6?f*?FtwXJzUn-r&06DR{G*4 zJB+8^-tn2GYg>^deA;OUuEVjDdeUZ}o20G4e@)1B0mm6x6Xdi9f=;^3ulAFl%S&IjUzhOtk(Nis`Qwq8 zboXw*aV)^pIydS`-*QS^*HmnGaEjq1mb6ZXeC|@85%D>X2i*Gu8q17XT+s@se8xU% zpshg2H4rxI*Y?VU>q!h&1>;}R-tRmnaDDyCwe3Hg`}ClfWMHdVAF^PzGd^d;pR|Kn z@oRv~5|UQG=e(fQ?{goAbNQUBR9)`UfVST!1G*Y)W1a5{xW83UZ4b!2@AOd-TY2kWw-|zl8khC!X$V=Kib?^4gH*|R4r~TrOce%tDO1PwLK#TZ{6Mk)v-BV`& zgwK7#rOrMoNq#x^>uhl|_$jO~HYE!mtd|JKhYU($BFpWu^opI`gV=RTu< zeO06Qx8}UpbenWF(eE~WvSip6+#PU!8%WwGL`o~cuV;Yr#;Sn(xX-!C=U(O0=q{9t zwN0!3q^-KU+y~0}IpgZw?ROuP_L)TPnE6BZ^hHG7EJ!ZM3>Jf6y4&;{--29z=Vn3S zq(jsV_UalP0C&ou5>={4OP5YTyPQzaO2@>Ls%uC5EEsK-oR?=U>u$(GHOpCM(>Tq# zU~See%OK zOJ}-WI_9Kyy3_Ot2|1;^*FgdxQWA~NxgN6CN=C!I6EyT`rE+YW)pKpA&xH^c8U>+N zcJR5E>5INwP`i2A9ymsydGgnM?=!Tj^Q}$SVj&wLWbV zi&jsngo*$*eckRCl;l&H5}$LWFVvxxLdaDe{7KtDlTO^kgE~Z!R4_Z>rtE&dwoYI+ zAo%Ni&Ygm{`r;|Zqet^5Eqy@fZU%rrXjt$Hf4{B6-xc7mo>byd;$O zHE8Tl+90SEFt#gHCat7x6rFubKK@&q0cStnHA9F(rVtA4N6?F{TVniy#jKof1=j?N z4}cP&2$RaPhLCL5lUTJSe(eBws5{sCL#|$+9Z+>pjKFeyr{Nz)BWZ!1xYK;wYX1N`uVN#2V-U>+y`=`6F1{UUz@12r*^WKtDKMy(Qq@ zB3PrrL(b^V@@qn^-GT(FP;7^9!^=C!5D4|}*+bY4(BUCFLlD5pI2F@oSYNc^ z>LdRQ-}%Z9f-1VtEr{vE8*b7O-MQYM1W%(Q@E}lJ7LY~6M?q8>Nduls&nsp9`87HZ z0a^)hg5#_SxP_|tv5GN=mwVTUZ8okG6Gf)_Np)2G$QJi=Ze zS^;BcK&a7Lm}qHRZa*;fjd4Dw7P_z|e)t2qhGdph)h0YDk>OCvYoJ z2|`y2Hi61e&nS7?r#PR1%m}9?jm2SeTi=q;Mz$>2UUcdqfBamRlGHU$1hZNZ4-nL0 znQNI_$So*COR#J=EF6K6SziOKK+>%M!343iEe%j9I)&i3pS2Geb8hx6d4Kn92d^7( zVT*L-V#OFGqQ5kT455WMPAYj4}z&Oa1V6&_PMaBf9yF+AoxH1xvLI&Vbaf*peVM z`DH>kY@}{|MMH8`^1{6AW6k%Jqtu*Bu9#YCVEse1e$v?!k)0340-zZhvI^J;t#Tqx zt%XkhR8Ky15%m%KMVe<3BW$CxY=(SvYDlmydZ*c6hW`1T%{to=A4%1Udy0fsPqR+w z8Wwl{B0)2iAem5(vrrDGhC)Cn%PJ5Kk}Tky5mF_KTs6$XhCrXG!>}lJO?f(g&xpw@ znh4q~QVk0~_Q3;VWJ^ht44McM;I)W@GMy}B<`T*UVFL4P=C?`l2S`mRxGluXr#NA0 zd!|{~U>$Bvy{|>H+q-pZ-H>PrZ3EScfkN0I@)HDvOO+yBQDu}FicW)wj3iSjQw%z| zL8r}8ih_)!Xb6lD&1N+S23EIHbPK6938AzE5$DI~{32B8?tS`Kr+aT0GPBvji*UyVx@6 zIRNZ~G4xV6wN^=1FhaGwotyQg2fCMzKYee{fKBTJ$;9R%Bn^h9SfU~)R2G#wK(u

B78%kS5IM!b;N7;r+GHXvRH{i5h@6e$Dz+XGCB zemEm22R0HPfrcpDI#=+3ctr(|GfMD)cy&l`h+w-We;6F~V6$5VqfDtcDrc26AYLM8 zMQMoAJU}v;N@ywQE{qX=C0t8MAcE5(dVx|({8-gM_PA6b81@D9i^cOkd3fNL&F*?g zsMlfDvQjKG)ZIGj1tEbYM#T@&Mg*yq_)(&02z2W=B1ox-AW9He4Gn>SbZ$twe){0R z&|b?+p0ZVaThJ05gj7`ngkYd{MoA!uQ&91rUr+VD5A9?$`;)R!6N}k+~v#oD!BtidPNJ62kPNhk`@G_Em-1rBMg-zhny$8 z)0RVQF@6O-kBah{OGh<%Ys5Vjf>Ppf%BFdkHf&@A!_+KOlcFI?vS*EoiVeI7Pa>Vz za!8#Ff-Z$vjo1ZbNcYaoP;23fwm1+)uXfiR+S1;9X}zFVhH6*@HeEZ>fHxwCAaF(H z5R758|E!b~rK)jGZkm}*X!jUn@)XO3ziiWHZ8gh<20?(|ILslb)AcUNwC04H6u&7OV zF8ol$t|-~krFqyX2&QyKC5OP&qjCsj>>N2{D|n>j5QRtJ+^X$s%KA2(&dw9>f>$Ll8ulSuQjL z>#;4B*m8)?xYke?^aG?r01?r|rc)3QgDA(Dtrh&8++{eW;qeq;Cm9Fe<}tRIy_AYwZe0qf9J z31Ly}dhq+n4_~|D)d_+&a^%F8z(AaH5lys3A=99oQq}9_5Uie?#W*4yv7s!Ygpkt> zgb>@d6=jTXE&623u_?V`RtP1_gM>o5W&nj?ptce`u;qCfURxfklS9PNC34qqjC z_6TLMtsqb>r8}Zbtg4ekpg_>;)pZya#jcxrw;eoX^4rq{e_m7#3&Zv`kh~P4?Wr?{ zfNbX&LqvA3!!eO)8(Kn88eJFu%DyF<6++h654?Z)jn>mD3!{1kNH&M|74@nSq7pND zDJ?Q8hlm^&l|#S#M<&a9nBM{?% zBa5u5qai0?9gypD7-QP>@=vPXI<>SRQ9+9tMKX6I-r6@X^L97OH$mI>G*Q4*T&Wc6PtY>3P;DKzdljg;m zuZW4WTp8$3C<8sFbs-qGLDe>ku*-u%RRr_~l8ESx$Suk~(jGB{T_0AIjbQ8{D!UhZ zJ)I(ZR&RNHJNv|#9}qsU8-K$llV1aC;SrFoO=A3(J=;RpHkl7P4E~9nYj5``DK~=1 zB!Vs?ypnQPqvcBflw7h35lHY$mY&!oM5bRnZT)=*pS<>QM2y+4J=7bpO(@tfovZvw zLbm)G+?R>Oydoz0W2$8tP)zjf799)7uBI}rDm@0N>pNuxW7kjZ@k;wXC!3wiIzbs^ z3+l_oQ`$c8-tC8*_~T!7nbH|0gRucETf$L$J>=&!n?1~!-Sh@FL+z%w`;s>LoHatK zgn1(VtU*dp=1z!_>Mc^s23C{c6~Ym{g=)Ev>P!DUr}E9R$0s*ad#k{|QjqMhK;Ybf zaIf}IzhkeIMJ3xl0XamVcqA;;&vuLxL=&C}31R=8#_C&^CIe2aeg^|d+nHjw-}r`Y zFC}z|nsNYhJxM~gboUW`=?5vNmNuR$4wOhK* zpR`41wIl)8kr+}h$6gOov;8jHX%U|&0fN`EBPvEf;8})2lYYt$IvCYmPLHv)l_ga= z=VZTqtG>AK@x~XPcz8#KAQoW$vRYmd)3!V!uajIY&pTi*b`M&y# zt0L#o_g7r1YwP{UnW+Hd7ok=f6`6o-LNT%->4+H(l`&k_Nfw*7ziwMmR0I)U#dv2_ zvlGcW<1|2bc1r25pqSwPS4);IPiwaHp97^C+?d4q-C!--0&Fs_@F%TPTZ&@kL*!Hx zlijp3wh~3oufrz=3b==~D1gu~TZ(LED#i}!L|_33XN}0(a5$mkU-?r~UoE}8<6vnw zT}?6+8NwdX5ftRO1vCptm?x3BWDg9G8E6a!P~wwzhj%ITVt!@&i6vbUwIa(J02Gi; z+pWkr%Q5VECCdTfTK04OT*7PS&L2a;%B4j+_b?pFlzm_$kEV32I@1~>xdmY(C9YLTK4?V@E~(yVq}8k3@F!Yh1-H)!m!lg1mr-`ru_I{nWv7#&@@^Vo4iTz zI!0y}-4|b6?Dxr7-Qp@57VO|2rcKP`{8u?X|Ig;+AK*TLoO`qH6X18yGdgyo;8(W^ zhRbb&QTpUB<>;xpP%xYe1!L8Pf>B&3$n`9mK@L*pITs49DfLaRlnVtx`A*#_P)~BL z;HgvcMgCPBm64kTw{EMgC1}l)Pjr^UOvb@4(#h2e4$M%O2|NX*Gt8?f64~LKyvi;i z*9YE`dSfU1M*rrJ^o(V{f0+2M?d-$E-=4Ak`$i8FKcCzy&YV8+*N2H07Stamet)x5 zE)Xo>hjY@yX)?2aF{rUPdH5c(zXA~!^AhKC|`%nZa`CifLetuVKap} zNPw$K%q^~oXSc2qhgE$im2#XL508C2Dz~nlnaa&pe|~UxdH9qnlSLVh2a3bU78JAo zLauV~mXDA7{6uz6$;CaT^@wDIvb#0L#TWrL?`oqQ{8RhZ4tzF|TnbvFRH=k`yVMd& zVbG`UPoz+Vs=EoX$ve2L#j0e=oi-Ss*i(f|u_7t?nktC{GI1~dA}<{N1Qr~MDTirK zNJ^;VYw-$}F|;D#)UV-7+=xB@i~yRDYF9jtiUk0-oq4}x;@M36o+=k`asz`l+CD2E zbsT2b*q9m#fMlM&0B4(fX=z!t8mwe0la_vv^yRi6yz%18adZ)%*T)_iBCp~NP?zAbu*9uUA}EP`3nMHd9ka3-CF>vj0S^}1Jx;fqa#z@Ep&LyxzUE%M zS%Tc=H_EG}^_&zYc?GW$eml*IQxMja%V+(~qwVd_-jjSL36INurZ5mJdYuI=@xs$Q zCuh9Jb9!ELU%~YBILFKz)c_2~iEDPjNrgWXsZ9cb;Ju$--$$(5NP&*a7e@B}Xe9Vl zM`4`$?ta{ST%W4^h5LgZKDfRe{N^)SK+E{9k>7|Oa0p_@Ax#g?SHHfj6}lsLqc3Zp z$MM$^pCfmjc_m*E87sTUM$+^+W|9gW~p<%itD4Ianr zQtN(z@C6@YLaSu;&n4g}%RaJ4JgS-t#^Uy`4ef^Qx-2iXQQZBX-(we}CRBP>&x?>exObvA}=Sy{=OVUoL(aYQ?*g{Ca zTic0m%4o|c6=atfwHdU0P=yTqYzh$zlv|UF>x!*y*hopkm(O?!;KiBcXh(I6R{R?2 zpwTs|?6h`%<&1NQ+SU?!+XiFm3*zS=#6*^sL69h`dPSjnVM++hW(A*<%A5EKOf6tE zk>Ik?+6Pgs565jXzOtO6$r%cmCXqD(LeP;XGR-uhQGP~mnq`8fR2TWs99T<(yNM8q zN17AI)cEwWWKn0CL)t1jcqefd1Go@x95CFTf`P97j=~Wn5uK*YF282CjAxeF#x9>l zCjGtrMfLMKQxp>ntkO?dETM$g5YKl3sB!?JIVMKK&C$u@To z_mvBIg$2BLR1I=clm+Zfg9Q+eGrRlzv^X=ofmMn3)~J(hR-PU`=QdxRA z7(w~LA5s3w{mkAtqNwV`5ao5rp`DvxKBUj{1dJVK^iJ*Wbd5)kB|JkAgmZs)E+QgfF1@-4bOcl)d9H z#f?YlK~v4TinJ&T6}f_`&Q+!Qex^!scZHF+Br+pw>*o)(^cAFW(&8hQxym}yAP$cya4~fwzp=#a-!Pv%g#g$y}8cKeFoE%Y`*tFjYLXp zN!J{zYyHeCwk8vYjf758b!{BdpLiWkaM-W%*f{zqA!#a|Dv;XM3(^>verDErvM|Rl zldS)-voxZZNmi!@SufdHm#;>!B`dx`*28wzUp}?hq}i&z!ijd)rhRtS>Sz{A>k4}$ z2TUQ1Mw5$`m_74^8nu+6htxt!ESA~IGUup|)G$LYEy~R3KRRzP(yZBWVNGmp%p9aD zOskQl4!!BJB{|OOq0$mg9#j>!Tdbzv97=^&s$Kj~&D+F97Dxyl>>^v$tv9(Ly>W#3 zMmKm!R>B5Yi}>eHc~`_mW1kxjVn2v{`-KhNiV^VW8;}f0Ht&^hFw;O(-s~=q%CBq= zBU>?E#qi2&O%=k0LfPX8ObsTE&mlOEf!dAr=8*a7D|}Vk9Ns7%hhr{?$Je;Q=+=)0T=X+|S&WDv-!`|Fnxqv|ICmTTr7URW{7a%*pS_Odi5hi8+TH>k+9t zls1d|+OZ(wj>0u0;z{4ypfdYTQJGij1y4$N<(7aK+3Txl?2?hNKA;T#W$_@#)%Vdd zY&XGo_TLU@j8;E2THJUdy2B_}Ye~wKBo+ag!`;RBPtS7=AHzgop@i~KSRj-O~?oZ1cHg1PPl=Toc(GT{{+*LS^2XAtQ}yNAUNg)pin2f z#O+bGHbqLbr-Y2FZg$}bLyuw{@zK;AGg+l7pVx)Y1FMA~tqiB$QRX)TKo9T{n_8rWdk2nX32d0)-ocNrwnaW+y{}O+&{^aVpk_=C5nQl)vv&vc!r z8kv#qC!dzsYQ*Yh1@GO*bl@aSP-2N9B&^<50cNkZdEP)GPh7)FBLGC!kB@r+?*NkQ z@I-w;+PIRgarvJ^mLnJH!KvWeG?$qm$WIf>V*p}som1^aEV>9WzRDsC$1B$Wo{2=z zV6A;9?7N^Bj>vt%`_y=hMpG280au7tJxEqWfdvOw0Xymfai}8s-v$_M5jZF+ftamS ziu<^H7fllv<7Z%>*tKK;E`CE}y3PZ>4!(kYaPIRQGs`5qP6o0*SIk!BH6&E3ImgVq z@HQAw8c?)Jf^WXY7vcypHSY?Ue@+>k6*77B9zoJl>j|=nH|Vz%&i8&^gvNm`p{Xw? zgVi@m^Q^aivMe*qizYQ!o)vRNUQpr9K_%p~UFQ2Y$E-7snU_HktQw?&25L$*6$OGi z9j{PY^ISEq8AP|r0ccAGn-Y-gtgf+oqJHyz{dY!>zdG5UpeIkon#WNoi z;0ki%G`Xq`UtgyAjPBM-No7}+0p0R1Fp4ODjOYzIBdQyt(ffOnRL)z`9Q1K&H`cEI9AM$dHXFYdcd(uC?G8^Abc2k?@OROYzCnJW3F~8?16GxzBG|J zHQ#QQBD2T`c^kj(Bq^88mzZ!W;%I&pt!}!MFm0lAL01Hgm3TN+@N|PeK5O;~`(9k_|pP1~An%ks+$J=I{fZ z_(F<>`nLYW7B;pg+0qaNmfGbOs&d(+kAIF&afn#sXE&QdLMq zNfm3xI%U68&-IBS_;w-o8@I4C>ZI_qc7gOd!_9U{u&0;0RS3v`s*hrZEgPigR9cMS zS!a0_ZAB=Nw6jUFnQ6z&BdVa%y6mt9OI4P_pI8Im5{$0ia%MNMoHPl~E_yQ+4wpbQ zs=}5kBu#?~zf)h=fwEs-l~G_AlX_5movOEM#ek&-EdS>KDhC!UAsu#W6;dpeQ2K+K z%CDS8yHsP~aM527oJ4 z=?_hS7BJ?qf%mhq0gWTRjv{iREP+0-PLi#8^8}LX6ZL{JdlVEU%!(+m9nX&^+nV&Q zKqXz06+=PkR;);c~tQv@+q+O|fM@E?mmpD4wpRl&u&R%LO=YJC33cPR3?6d|6H zIQ|GW^%YFRA?^(}!#gkLG(%uz)SA7o1}~l;^O+k@*9HjfA_!t8zgfdeZC)jlYSz9{ zQyW?8&pPvDZ#^{d0AwuAoN&9u&Z;nn^Hqea7%A_4r$+RR4{WLImjp)iInyoK1!i}C zFl|wN{u|c)itgOV+%$HHMOF_sc3Bzo>$_MoT`YHtOmOtY1@$;9sigu*>3!A zAIPZJQ-S*^C;>q{6Oln0{wR=eZXp_q!ej~Lrs_}=pb{N3JA5olk$hR)QAD>6;=e@f z)O?q$`07Zp1}9tVDJ&93G@bcY$x}#%$k7fg7`egvMU@Lg%Sk{MDOXU9>EmLutUl_4 z(Xo8N09Dc3Dm}M8eYl-|wMrirYga}|mG_XYL@PY*$nVYkVpvWWJ1Z<8lzn1;=Qu0a zNYohcRED9B0eL=^j;jz&La&djPe;eb>QHQ~toD@ijN-(^XwKG*WUnTjb0$dIc=ko0 zA!=1^eo6$DaZBw+_+BL9M4*tzd<@{oSSt`faNc7M!yCh8oY z=gM+tnPU%_>e35pYV!$Vp532bAjlp|6Ki)_1iM1^rFPODJ4vKybkRCQv1R9F=q%J# zrGx-Hyr3B+MuQ>sJ@HQ4IP>Veb%qPR7MpHn|MQ@zVw(7!uvh1dLTYprR4-+)>1A5O zUdBdbcZ2RmM-ZUY2&!*W|3CdUQW7ct&v0k_VcRO)kY{P_lc=kmJ_F4|3HM-C_)-$= zWWNE)h$kzwC~n-D+NWy!q_Us*>;zJ*WgwL>ghilbC?#G5XT*{oWtcr4mNemcg^QqJ zg1p1N5ahjER*`U*fxP;IlX!(~ApCccY!Uw93rwWBIbUK9DT6!w3lfRpK@Y}s@d^O2CAzNC`XXMj2v7M|k|LSyHOp_VP|B8w=;8$HmhUwj zw<~kn7i?f-2~e>lf%cz7qpdRuWQZ!CD#+W;^xUli5XydHM?MjpMH+&g1&ibYpJVaf z3m)t(8bGR!bP?4I^E&DUjsW!_Xvb)sA^s^cg?q=`pGxD|71yWHT2 zR8MWm^kj3)SS;c@EbP~DzRBBF)5`kllNddReBqxPv4>P^UOJ~nZRtR0R;l%Fhlew} z7qaSWi}M~ET;Uyfno5{ghQkvaoLXBvy}4O+J=GsTeZEnjyp0M)7GYy@cU{Hb{9PO#&Zm+$(3w#x& zXlu2WW1*})q5eYntvL0nq|RswVG;Y}v#MQIbI@C_+Ge%o*WRH@l>0vWDLGv+K3mH23nf=^FM3Y|NWUk zHG4|#%mWxALVb%xh7De6n#Gutdr(%Q%?W8wXw$P-+|du@YdpGDt=B{M6EimDj_z&Z z%)1`~JD)0*rlRtJRRRQV3NXv&5)o6;80oywmE5^CwaR0l)NYg?d)o>raMeNugp_}G zmYCuN`_uJ0+Z?93RKa&yrOCV~LnptjCDEL)R@}Yi6ISjAYZ(c!xMa0P`Mw=f*4iA$ zG-EJFSjm2-b!MEIMEAtv@K-5~rJXNQ`J%NfK6Ag;icwO?*5_B59n7^VQ<-$r<%WTA z@N1zhaL=@sD#B_cT~%`XOEtATbTDt=fzh>BEf8|jA!?^m61#{*5@|HbVMUfwVpKtC zdeq=6mY?A=!J(8(w<&s&^tOn2S!Tg6qJCPJk!e1BBa;~Fc$28?HXXh!x_1^{f=zUr zIYt%89uzHrpw?S2cwCI8?@57$w=taQFri0zL&RMOX%dU$?xo4n##&PO87gr6j=*R2 zB7=a_#g3MP+gX2=QVd}DOgxA5tx>!d)M|On3Tor?FR+@8!nhDhxNWQI%N=u-wqz}= zf%b*chTExMyep~wtv4jKV85;`+yh-$xy4GG!GPn6QwVnPu0)Y1^kqWl&F}iz(Z=uU z*Cy_f^8}$2t_5vlJ_z$$5X?Ct@QITsi4Wf(5je*~Cad&d-)78`s3O__coIKWSBLs2RmML6 z>rLJ?Ij50<>O-*cJXg9I5zC4I@^6Y_Ed~gZh1g|S|1MS#>>(|?M^g#nKI)<#52`@s zryT`i)CJOuT9We+fzMOnq(>cNE2-FiV!`vwUu%O5B<-?l>~=HE`0=u0#5kSTU5G4M z6?&-k2`Z(7Kx*!VN(mh43Zx9H9ngr?m_rT;`McE&OnB4YVR)#T@$VB>$a~dhmDNRM zWtkHst36rNa*rz&fDb%!0nb8>rf`xA-Oj3^X0$e|8O50I%1x?ZHdnCx-Y(zb%qnId(h3q8v) zSF*nc{c6eryAgt>2HtE#6z*u(TQ+u?O?jn}WY;Hx5HcMJg?(i@dR01JDR$(R6c*>V z;BAlNm9WDbE-cOi`#X;4R*x$AuLD}zl>24wGLBF|#|gywbBsF5SH~XWThQ!J6F{>a zKTKZNBxHyAEI(l$1+v-FnbuSW1uX^ z`Gd6+5~3b9%7e$z`$xyK*OF;nBqegeUrF5Rc=l8Bt=D6py)C6>VjR!>Sss~`e)DJ(FH+W?7i%VuiXLBgnLUu(FRi1;Uw5q^NShv)%@=z7^H`f6GZamTmFwBqd#cUU#0Dm7se@Ue0_&U~p&ob^sQ z)iO-W8G_zx9~uH273Kw!=s0xXsN)9H+FvOzQtfx@sdvK8!nWvZn|L#ynytXR#8VH( z5=p-^n(kETssnRL2Ph_9a2gV5?yQ@1(phyh2cb<(*!jMblH*v?V`W)GR_JutUYQRK zXS7nT0|L7)B_Hw#8RQewa;a9vrDWEXkn*2A!l{EaB*|FG8%))=lNAEfxRgfclrEIg zKO_3bN{^#7byup=E$?A^jK4&4W7AV!pLg%C^WIZ=Jz)iDZ_Mi)$Dt^F4psLov0fxc zjwm$K)aiv8EIm1g@aR$73~yc6p@ry$^1_AE(7WULxtykFnXD89BmCx}b${(0r7Ph@aDC>}(+-V#1;F zkSb2Q=7W!jpk17|9ZdieMVOV!Ie}32(pMP3AzWoa2t;oBm8E4ov3RX%G+?)-2Ztou z%3kBVW{7yHjWBjR*@Gz1!W46Cn6)>AZ{=d#?Pu>+m>0X1tf;|X5T+b0hoa&<)Xt2% z2eK!uU*u-JHc{GU$CB+YG3DX5Rt$&1`)`u?)aRXiN{%izl4h66dA>N&h(^d}==#?? z$3^a^{mRuMHfEZM@36&GP>p#BS#o8UUMTl1mGG3;G%|q(rvq%q$rR?}ReUTG)Plpz z&Xu|$2R3na8|pYx^0Rj3`Sjoui6@^G1vfpP0QA>OlS}zZ4_$ICWf(^}@aN5xE@B^( z@yA-!+}u#u6vjF|DPpRK^&D1K(k5f*himh)A6Y47M3G30#9AH{!>K zf-P(wo5U~j%2q(0!!~UYg60S7m@1<-1rYfnKgLYz^QBxUJ7AYt^Mc^)AoHJx$ojLYLd@zGOByJ^3jz>pM7sTTqILNbU18;n~Mls4>WU zzHY(?nS%~SCtS)Z?X`aYDt|(QjT?m(k#B4ZykLviqGR^#ARNHniDCW3w0yd|zOxL7 zTWMk}mQcbB0Rc)Ac@a#VCHA?zCKIfxA*e)k*44;Cy<%_qH_npBD7` z7CNgIZsZ^xcEDEh5=uF_TvoE2OYR$d&7OrUb8k<(iZaTWjX#kI7_3{pc@uNQhx}5p zeXd4eRR+*dVR)ZATp^7}3>g#s+>QL*Ms?VW;3*9HpzE5KT z38Jn(Aw}3uYdoDO<6l$9xr6fvDX~+A*1hTldz%o0ky19)cYnuvtmg>Q3Pm^i@&k#R zQK`D;eN4_W3^3P66cuhqCcT?%XLk6f-Ma;L<}XsKy;>fy3rwhx0gEezNO_RMbzE?e~`w&(o&!+K!;`EN0UF17fxneMRJ!xl< zq}c4HZaYPA*6J!5p|l&yGdRwoDy%aGsr)A7TW@StLDCz~7!LWl#lyMxr?)&a_-xmZ_3~K~eToF`ogFcRc%M}oW zd|y?4W)hV9{@g!)&(G(P%sJhsy1Tl%y1KSzv$tC8OkeF0TEFB?;T!lkDQzrTA-Er4 zOr9uW#3X-+el}J3pEb$H=_~x=_Ep-Yv2;lkBG8Lri+NuQv!>g^6YhZ*QP|hKErl=2 zD2sm%`i5?C_VC+T@tKS+W2V%G-n9q!-zB0FmLf{RRcfNqn{6@v{;=pKuLP*)T^>^6EfS>m9rql_1QFr&j7%C)_Q_DK>kz zO4s5Q3Bt^}&X%9%c5tIDKL!IuQ9>%bN~+)whA)`+w|vCeX#N=I+xweAvyAXiHe%*C zgFk|VlDTZvn?M5D8b}+KPX)YyP5cf%aGK& zc%P`ZMO2%@2SQaUs?F_;eQc-&k)|W+YaUb(&L1KSG?mo)Mv&AV=_7h0UrmhCd9z`P z0LW-#0elpe;S`&5EC8_qiU~@Vt=af^Qkct@+UVgC=0@IgpfH!!v2j~Nm^&_5{GAAM z7pgD^pNVCByi76oMqN|nF3r_JpPCTn=1|EBbN(9>!ra}qPPbt3g*kJOFv7@gvsHGh zL%UF0SG6=4xe(3TR`cO^Fs!6xHeqgm=j&P#*sdBK<@dK!F#h0`doIQe=RKCF{H>F} zv5b?Xb5?#i@w9B_5~pDy!Xs(h`CKR@)@zAKqe4}eRJKs3;{Pk8TcDw$djvJ3!>9kQ?iKxNT_zwWX1Vzk{=_XU#{lBPmBbp&vwEuq~LUth`W43nEIz*GRRp zH|)4G_zV>O_LiMrsayT7VlR{W&UP?t?T^9O1-Us+XPyCl85;*(6EWnlU<;3PrA~zR zSqtU-7)N=Tf+F({?#|8`Qi@Zl0`wQxT^6>Szggo&d z{4MbBw-8UbRl;XE?+h>>eAC>6E5GY|%%xwrZ-u-R#vRuilHcs!@eNGC;r8<*5}y~F zfAM+zEqXOqhJ(^@sZP;o+N5#d59q3$epidL-j6~^NI(23s4E-82S-cq4j&*!`-~T~ zH0vo_I+}|2>YI}0Qt9`TrBYT@Ulm?$D~$!jW_~)KQ!3hRrVw7{N1FZjo6;4@W{>q2 z1;}_-1OMa<6RaTqpANA16d18X$YdE8Z0N;gmFErv^gK^P;kmbT;~nX+%|&%q zRg1jV%PuW60hkFATU=UL*yS*7w6OavBEt4HQ#R}9uZN7qf}Im6{vvc-Wpp_9arNR( z4R>Y$N7#=#@sXgczy0V;J`#m{k(~4hON@yT-6n)+BhMbLxt#cS^gpz<;!;?=KJ?FEv@|nVyex!la3&K_ zGR#(AXE*-n0B&l^HIuqPzrw)_c2qy_tBTnE-dN7At}?U<@$ zmMRF55PI@?e3rjqb$oRNb5Xk{%#~E3+1f)|^6POOzushL^wO_oF5+$U!ab%m&6r0-AH@k@eU?B&!I}c%@g{(A9j^k9AQkE=<<@iP`PLfGm}# zn>M|YK2L3FI^9%;v0>p8a$Bwvbn?3;ViiKNAyXfVAWe-#!1`qY#I)F-1kz*>A7{0( zEu#rcn3YB-7{0q1ovidI0ndbip(@F`I7`oadN&a=JE4fVEH|yXNOX5LNU1XH^~8^2 zJ^p8Lo;8L5ZS%hblf@=8>I2C1PuLU^xzaiAeHwLnnoH1t$7Kw{ulH(*MgqA2OkVQ z-(M^a;*AsYUm!d$5Jn#q!nZBj{x1;10^!huLdf~~e}d3YAbfJl!A3D>hYKM<#)eo% z!$nL)ENdb$bb{T&)i-9rzkB#}t|5c}GRUph(7@D#Hqd$J|J2171VX_;1!b}89iL;qzI{RP6a2ZeC$7O~g;KPUV4>W=uQG(y0Zm-XjDh>a8oLWODO z{cZo#U>4QVm7^K~HCdL||L1>#Fkc{SERQ zf(%E$kp{a7kvnXYsj$C$6s%dIrytAhHeCRd=;vL39RXPduEAq6J6pZ_?GoP~^ZxmY zY(|AKNQWiIIafB(tsP}cQOiM+?SEFCVEgPM!Q52y(NoD?%jcg!P`xFwrekDyS3dK9 z4A0*;aT@mcF*6;l;t|UGh}>T{Ee(GCv>si`MBh5Ap#q|wR-}?Oj?svTO$MP z*K1Rx8+GF0KI(-;eiQXV5`B?)6EpmbeJ_he1vjNwVGj}46amF%%b}bUtcS4}&z))6 zEMfj$A%9&z;DhgIZ(hypZ!J66?614U-AiqF=A2Ab{AJ9b3pD@nb>J7Nx!BzG86wO- z*@J)n_&8eqo%EJw)d-1B6kV2@-G+1G$AOPmgEA#ZFg4JSnyaRP=tztJJJhYwyl{z( zVilFdmD0xEEpH66jbneqDeWs$^{W84mV*R&=_qSI3U@oe)j6_B9SG>|bF<(z4W+v4 z9u{vel5wo$E_pfQB;5t;k@Ar)!@B>ang1lOAP#Lo5t2g*(o(&->?I~PId@4VA*)+^ zBy!YVbFl;{a_{Fvozu2TRznb(|Xv7@)BpP@o0-IH4GwyND zQ99~f&Z!wbNWf`@9lv4oK^1G`DDqcJ$lN6P6-_K5XFQM2zXM* zS2wtvU8Ee)`T$i$UxJ>%dsFk?5C(FhNEfWG%z`>|-_c1Z|1deECqu%p3y+C54^$z2 zMRwqdQbCbjk4H=9x2ZhL1B}^h7HfSrP983m^7EBdDGVs@Pnnpr$X2|4XRn>+^(i z3EW;`PNd_qDd@6YQ6^GgP})NHJHm_#Q3!mXX02T!J7V9mG0xAoqi>02IL|jW<8_a8_O8mq&wRYorEP!vtZthW6NQk9d<>QaO+ss zZF8Wk;(R%~mi#JWB`g9TGtPM{yR-K3wY)}rE$24|vg)EYj}dq+C`Saf%gpK~<)ouN zyVffFCGLEzjCtbBMqUYl54o{Z6`AntRvzbK^>P!J!Z|hFFn6@$T4u;tMYp5rnM=qx zs45wo%{fWpJ=sh+2IA$&pb(+mkD*gbyAeuiH)_I%Bel&UbINUQ!iQeZ(Fyn-6OBOb zE}CVtlWk?Zv0n7X85e#bIHBcXa!OCaQwg#H7Ii_eQ&d5zobY#X= ziH>-Z9m%+Dkqo}rym_nJkw>3x+7Vg2(ZXSZg^g{kCmb_5i5XX9^rC|hhMpC4HK&_T zx+HN?X3m^~|19S&*_0aH$x<$(BjQ$&EiPY#u6TXg3-Vvk3l15wlbd)eUD6!(te-LG z%d{RIr`e(V8j_Ro`Yb`v{GS#m34#?fva$Kg%zCI;upmc7c8^tN*Q5jr_E7{C$x-p< z*8~OFuWkVa{~+G|T=)Oc{2eW4Vg5nEA;)8mv1jC#3bn8W5VvDS;!?|`<={Cjop9$) zk<(o}rz{U%CzFA&W*EdtmZ_933bQ3m!`lLam!Zk)UVAEw)3faz*}X$)a4{B zaG-Ww$SC7;6@rUR!_DsOZF%~D*-HvJPt8mSIqBvE`rr~uH^Gj15hlX%P;haL0xUAm z3BWWt@LrH+IcwrX2Zt!WwyREmBmH+yLhLEjrce(7*Dln27wYIlHwQF!vkBbo*CgSZ zSR=?|6*UW^CU(Wz6fE}w+Y~hd%~PO* zp+vWj*Sj!ZxyeQ4Jx?W3nSfgK>0h7@7pUKMN{-$_{pI{5)RSDOXD6Um{2l7ee}&pY zpkDu1sP9~vgt}Q|TPQXy0d>cdf9b34xo_7d@yX_<-u9-j5o~5b!#t*cu5O@jwognm zf<^i;GkZw~&sg$><>9!#V*cKF(DvMR)hn7;fA^<x&sx0Y9!6FYQl;Rb0 zTBp^RR~-<1Itn=@zVF7we?iCJ7R%(U#wF(+2Cp6#8a3T$G|r)$N@GIMa|3n>)*b259^ za$X_t!j!Jcm4#TcuBNNU`Kdc|CvT%ysXeQtPTqdTTcG?xKM(E`@;N2y38ZQ>l>G_K znez-KNUQY6kLB4PsbpJPLoNVbOCCgtm3FrsMD|+V@Z_r%ygFA4P}LdZHssv=x-@aN ztuL|85@1SYXSx=+22qI@NrWnoqM*{okchJfFizu5*%<|OAX~Bmvq-mBclC^*BMI(o z_TFbbg6?6)FqdSSv8n8&ECc+J)`)0QP$!WgYyG=E@;9vY7jKwWpw%WEj*){)qUqlT z_gvz+Ku9s%wrpPiPREk~AQtL`aZ-2C**UtEnzLS&jacRSLphE0enu#k8gdG2JFS%` zPCyQQXaR6#aP{V$uQ}}S1^T_j2?Ycq-X>&caFXtHWuUUBYmsj0`r0F}1*5*&qqT-$ zO%Ud!@n}A{6y^~td;+#pFFBY2ak5!Uiaf_*D%6fAQ7$b-Zh7R2Mn-ehi*}(sKvmh5 zIFF82Mq^cwd1jgbCTkBM31OKiJW4h;VWs`tZzssNLw@!9di2WK(Ot+ld^N6~Ja2x% zD}PY0%AYfE=Hw4sh5SjyHT&!z_jdg5ftoKH$v6uy^eVvu&btz(7%S{Zuj?y3hq^OTMCYB8u?x|V=_hTV&qQhzR?tbx zj-qUsnSainhOvNj-cD)e8D>6$iRPdwzoYIdEj<>=GM=clrmyzBjAPmK67q zpc)DX%t8!~wiBC02h*-&3(wdwjN>eG#}Nn+9daSu#?;Y6{L;M}e}unCyVTwN6^IUlsOp~|pS!cqAa z9K0Jzwl=>>YvCrXUDC9*ujeFMd-&*NYxY2!@!@+ySb9DtrzaL_AB zM&^OBdm0)k`-I{S7IclaU|j&ny}F#t ztjpuQq+X&g;s6yZIoY=M`Ye^;#626{-o3fB>AJUS%sx0wg(ZrVqeqOw$N0buZz=f5lYJV1#vF> zre|Q;H{>(e4@+13k?OM>6+7n3jWNd??~C zTz7Y^yM{L1FEr<_Aw7X*&03Ra?(v!bwYiS}y}5hGC7V0XZ7z~%ZgSJ+@bi$zSmAYS z^RluZUMAD#bbSR8EwZNi8{b9sp7B-4)~RL7oK)Y!4!$y8o+3XM3~MM~Fl@hb6LqCh zKD1ev>RipEd~_vG&76??bcH@$%u~0)K-O;PI_vSq08hBB0yV1pev;)vdY2*9#Y<`G{D&)Mh5 za^1xTEIr9FNW^idz(0R4VhqA33Jx*w=FIK5I7M2BYzRhHo4zp7)#aUXJn{TM@$T@5 zcz5>$-;LXK;R9bkOMGO~5?`359Cw6@plv;w=imSjUwv(q##6)WzM?|BgA_}(pq!V@ zU6djwNe@FOh&CY8KmQNuq3_1ibdz@DEwg`nw05I!!O6U{l{1_Qe#^TaQV*ycP9zwp zK8ujN0^XU+vqcaFIf5TfN?G^5+(wX!JOIiIOQ*B0r?;`3+E;V6pca`tGcf;8>A)Nu zA~H7Iqhz05ZT5`b(=c!~t{xuqe7i<;1S4F4vWnq8 zHBykVgDv<&ifgx3P1c^vr#|`}pJf(dPSFTRJE!z|%S-e+R``}~&2%0*$e{xs^JYep zCzYGhCe5*?LB6!t*KKcwIak`-I9;Mnet*D5Fn7M%)eKG8Yu4>b5gAvAEh*z#DMOCA z@TUf|bPDo|_ymZD0B+)EGuFroq@k8(#K1P5c(M61)XY=8S4H)#fBjqdM_;pOKK!Eu zrUJwFPLh|g!uy`L$yL96QyG32q&=jey(TD~h?Ts2mC*ib$tG$dN5Ep(i`1e{-g%-3 zlFq+!Hjx*JbBTFoJnHLhC@Iw%l|1A)SC9kJ34XF-P(!Mr4?Aonc4}bJqwu4 zqHs{D1{;=^qU_YFU7$J^d zKfj@*^Nf6`$5c`Cb`lO^4NdCXXf|&am;gG;iq%ymo113i~Pa~J&y}y3hE$WD5rXzB6`NOu6pKoR{WGn zLXfgi$}%#-a>07V+Rb#D{Vcj?VSth-J>b4G?-5_0?QMQLx|T{@K&;-_odm z|FX?U_h)#~^3C$nfitit_vQ;XsJTQEN}c^X5I7Qa4Ma|1FJ@0QL)=$wc$ITnBorqp z$Ex_Ff5~(yTRUkf5+VQ+UPE)a5jS#Hj*yC*9YBt0W#@hu>lSC!4lHxm*JeQ266Nod{Em11Ffo(`K z$8Ivt46$)*%7&vw1AN!-94UqVY?i@95+>3xMDCT3>YUU0Bj+#aych_@F*EXEpnDx2 zR=yi<5h@_%ef6yQf3^;9si@f)szl2(Ox&-^>04wF+Uv*;8DqvIYgNCaYe(d#YdJ&1 zq%pZ*Ngz{ahRir*<{H_^G*ay+eGVC3zMnU9w1@2bE1j=(mG!C+CakIu5(hGNbxeJ5 zv1nS5o}7o668Xee^Bnan3f9P-Ro&O(_369eLiT|`^|F+K)w3dhW`uvRew1jTAjmSE(x~)pNYBc9GUlrqq~J z>`#GUM|>tRG6cW%lsC-K;PWIX%eP=YpH=@e>I zM6B?^wd7jA1BpzV-7HBm)v|f4v=bdIhX;v^%I48IK@x?UJ=(L6`GHGdkU33cK zywMR-li%{6TScQ|3f4s49;cbbAGt+mA{~iDwxpE{HP&nF3g3cdd}A(X-Hgc_DRCIE zUg4p!tkwVS=uykPP{*m+=I2V0EOwbH>N-)r3Kak1t63;BrFV~zchz*FOc|{zH0sS8 zXUI1VrkwWy{6`BuqQD&bl5m-lPq;)tBuF6^A$IY|p84t_o_K-z1Tzx%=&Em$_CdQ; za_MLmx8m{3t7qU= zjL>E~UH~*(6q?ZZxk0@%+r3WT@P121h zWNr{Qmww)s4;`;TZza72MyznFZo~u3+?RC>k`yJqdVd4H(j3Jz-=fDJbM$F)@RU%w zjNLctS8V|=5`Msbb*O(rNt^uR48KUHbY$K%_~1vP zy~?^!zQpyI3lLdIjs6x@v@Me|QCj9$CYlDMb!A0j!Ok?}=nP${#F|qsHL$&k1kYJ$ zd~PZEK~+E*%QM;QlU%i~$H4=N8RsT|A(`u*jAik5V_`*;?!c_M_)eLlqi?4N56 zwE)TBq@b36ETFD*L4{v+hfh2qVT(hOj6`!NvGW%5W`wCo6{M{&m8Zy3+hoE#8_ylB zT#GbpFXrtU6B_c^?WT`B#0p1DvfItb7oco7G68ncp-AFqW{T){6%jgYl(CZjRCAU+ zmqMzl-^DemPJ!?#%9iLNi$Y^ zmoPs`*kY2$Y`nBl;hJf)|ug%Q7Xjy%l@3jW=olVX!6Kq|8lAV~>q!X+vRb-?l4I8Gl z?Z@Q#C@j#~d8Vc{=*Q3w3KgGc+K~j>y=Z~;y$+XJ%wC-2>z~06F&Jo~-OCq^*wP&O zB(SquHlR2GnENr!@iww)v2^~orx^{Yg6MrA=2+=@j@Wb%s^|$vxRvHBW(EDU9*i;haI-qS3U zQ@y8kxPLHuT6Vb@Q_4{=rj_~DRz!xURz${R$GeJ>F^|mCna#*g8!hQ(p-sIQTzva9 z1U^dieP}Wt&7B9-vc)<48;BTGY0)WqKp3W~HK24FW-^g))uhsjQ|y&{xy!PnUKXUnWFWxzP(mqzO428n!UI^t5+a#BVS@b zlAFw`2riDJg?r6bz4;;$AP7js$MD`Ck*i}_uTr%#dU!zMkV8+!mm(EhEtT!{8hav0yLhSoQLS37N`Dn&#G7N#=HfXn#{B^=Qdw z%M|qSNI~XiK*MB6W66?cGM5L0$JU!4{{czBJ6DV(yzzLYuU|%hv9qKJapdsJDntH( z!D)omj^CbAMi-2HU^>&RoPe(7JK=8C4b8$i+ys}3%pJ>mt5&CaJ>(-Rx36hKAL3_M zlWru9N|~XY_!m&wom7kLp~>AB*os93b>?LQGdoe)ruGU-Han0>?XHO;C|a_+4&FKQ zGUQ`Cuj@@$83>|Q^-90pu^2_x^|ZZId@ta2`e%aZlD-=j9o%VHIa_%_U3qjc2Bl5r z%6;FoQH=xn=adajt1BK0cRE_v3C-xwjBiYtbtwJcf#^*M@Y+h~F#C*{`!Yc_Yz zP_EfUq6X4ycTSfu#qt_O{d>B~2AJC(R}TCz(}bD5UWW>YN1_TDI82&wC(Y^|*<{7y z`P8IzdMb%^i~JL$xOjQ^2=PPIy)0YBTjpup8;W&Y)}d)ch1F)wPBpRVNgu z=ri{aTix>le>C+N*}wden%wm=*1CXoPl(R#V5Z#KR%x}Aje`X{E{|MRh_>rlV6f$B z?Tn|ltmMR!%Z#^5qS|=jlDf$8T6#|%Ag~v%_D_)UG0FL5^(|;l1Xj7G>hnKX?_5z- zuygS|-m;-crEGH}P<&^8Pooo9)|@Jet~?sO2@}w>GV+I5Gx#cBP?KZY>+-}v5pP2k z4z>gDknV>*P%HwDd^fh4qK38M!=SKrZ|Y*d;-`*~4w{IE{>@?=OFNf|6u*;Jw@*8= ztor0b!r3OPmohCT6Q=bciRkt?#TYP1_%yv$gIqWFUPHV~bVw(fM&BW0G3VdXmaPZJ zGt3#OQo$d+UXgr(|HO0`>n7wimx_ozf2B-R!c2mWc_bV<|1o&5I*K7a5#Ny2|2(Dg zDkxbbFW4fIlDnz`T;!gha1LYetYKoj6Hl21HO-Pv$G(~u_z;XC%@IZl^|M=gPnoaO zzXO>9LsyEKsZZsLc@y@JI#P&8anX04 z;-a66i|8Yab5uB$HH1M4!?*a}75}0r@!6_HE=taos>jKI>}o}zs(cD&rRUTte2X@j zeQDx7x5K>pC4+A*Z{oa34iG8flc#07E4;OiYx>T4&1#>X(s$ku=W5SJ-eOH%zdWqc z#UoP4x%vdR@+6+)>qZo;DO>bAmKn$ zu@A`Q1cjY#ooNQ<+jawWVp*5+o06;fjQ;u?ykn$rrKh>4-rIw3;SR$1V};9hqS#_= zPolyh_-VtaZXVxl%lI{Ur2JZ5v(8u3i_VEA?il_q4QcmEUM?jHq~_ndg@E_+#&}@{Bw~sd`pG z#J`v4CRQ@5cQCu!qu^wAIE`+S1L;PJ!E6GJCLHmVu_0lK9ef3Lf=OMZ4k#JH?19;onEh|hkNmSAfb_5N0(kHCj<5gReTCY z(n*#Af5CtvJ3{qJdkD9lm9SHBJNkqJ9ASNTx3kF2({;I#YyhmnT6w8zCyx38uZay9 z^HID|vZ~mi##5hU3aP?`NUaKKRU9=r;QTeQcp|B|Vq>c*JyEWCJ4+Y9zZJo8uyz*O znT|phNl;FjJXUYV`ty%4+|XXSYSqg%_wyk=a`s6#H2%wMFuV?C9{WASzl@>cFW3W$x)y_0|&)0|87z3rroHDSDRHQixW zhTMm1jR<-r{l)YuF*o#5bKH$c_SBqAC#8SQ!-alHH5zH|t4*PiMhm?2E4A(QajYYR{!pmG%Zh%?Un?XU&ZejP-ip>tne= zZc-Y011`vw7|iT4)!zCeU7R}vzFdc}llw}A8e{|Gt~p*ihX4e_L6;f2mYQ{ubU*g8 zyvy!mHBFIERamQD30ZxCZ^k(GI@htc>>TGL5?$+!8#-4?x2|ii6NGWtn?Yp%wAX`J zBjmA^kcx?yiiyTcY`P1t5$mAv0i6@~)}<~|fSlJwEhr}QH%LN>+E@D;Scbp8t$$!V zZz1alYD}0tI^$x0O%M z^B4c>yCy~0UF&6MM0Z^ab6n>>xZsZ+4VCdDX5O7hi+faRa? zhnewh5-T-&0Vi&SzM7x3l^y2YyO9NC>bXRqg}|t@cg0%m^!+dJtz>4?GS`#+R~fFN zF}Z>*hQ81O?RgbF-!qLXM6Y6ylNsdkrStan4z=LlapBD}ZW*Gz=1*irXP}U6EbG~; zl;Y-`Z?muMzgxHbc>*}*{g*BL<}#ZB>l<4*K?_T!+QJ(Bl6X8@cnXEi0Ifm4%-JU? z%A5?P@2nf&ZPXHQ^&!=;o{H_+W}|D#ivu&Y?Gl1huZh1<(Kqsu+&YA!OZ3b?X~o9p zKY1=YL*q!4E5rQzp3_whotYq=v3)X)fg_6kIaoqV!c*l;$I-p;+)>nmVRvx6ybq0& z8BX&pJg&56Tj+Fi!%4`}xY|Tk?kv9~C5PmL4Fn`oBmOxu#GL{&vc(@o&9ljTJM0k2 z9qoPU^DNJd^01^-z44t!g-WP^qePe~=pkPbs4JYSxCm)q8X26ax7~=~}EXTZdg_ zYVK6Z+f6$ruTZd@KX7OHMU1{9{xm^xvk1!W!T2xqRMdPQ{=%dbqqQZnog)DgV$YS~ zL(AsP_T-0sVi(?FwvWLDn6$DP;wreqJV0hJ9t9AHLtf%e1@XSVO!SUuKC-5r)zuR} z)jZy3PXjKwv&o~AS++BEMp|g2YME#}t)hmr1QT;AU0W4ss0*?(Gvk}f(4Tlh%t;c~ z>A=#-fa!8Bf8-|^U95Bekjx0TGWRn#-JvrLtJ-0_;@vcVrC{A2(zwP}skd=rW{ht- zF&%)@bYl8cC>>Y+eNx^Z!K74dQF89h$kSmxOb9p2yA!f6cTN$f!H1{nggilYG3Nbt zJH6ZJ2;VYyoTw9mQ;s_)C%SZx_5dv6_j|NViL8O~7Rn5uh>B@|c^Wv5+(n@z!d#&@ zdVIIA1&Im@-K(b~vNy<_lwVijDXZSRschcvVE9Nb%@y~W%y$7xSS~-HB=clxXT(oyeZcR_CEY;HmZFO{+3h=V|@ zxUSf&ED&HG5V(ms9i5UwE-YbW7NGg}KcrY#Vl*w<=ca)RLdP`3y{9agbxcE`VP*UY z?oz^^&05AL3G(nO3W&%ai6b1d*{pA(Yk<-HBOsT?bPq;YmaECK)>}G)_{C)yV`(ZF zCB1_*C%mQXaoO)SnP1)mLnsfMK$0YV<_q4(w*-p6^j*LCG0vhN`%^xyI?SGbmhmEx z@<|nWq#3yLD348#Ll3*pdL9!gx0)ACt^jpPk+e9}ygymj!gs?&U(wfWy+vZ*#^a$X zE*Yvf%W-!e$sZm*qzMS_9g$84BfFdSJC-%og1ml=EHCok0#fEU$6wt&CA?UqyPSj+ z9Y>#AGRW2+uk{C*3$#96P_K}?-TD{XWX$oHNM$)9T7LPhXvBGNrsw?5S4Ks z-^W*J>!Kq#JK6ef-Jtb0c(dSIFA`Y2h`Tjk>H9>&FU|%Z$ctFX8MY&r16H_3n2rc) zeYMX}?%b=D3Wu)b{1)x)UUl@@zmYX!lr&kQ8va4|D$i7VnoU+i|2V0sw%w3~XSfi}-a#qz9y@i(X1YF43l(SOV z&hjdjR%bJk9YqP;O|=+J8yxL)Q^kT#z2m7B3;Oq#3&Nk|?y}^fQjP>aQeRdwDg13w z!FnIXn2g&8i?@Z_$cW-y_)1Q+s4=#~4vzG9|Jp3uk^*v9 z#<|q}d&c`0r`Q7~S-9^ztTXW=@wU30>5X7oj}?xx(uDAWJk#}LL4&WB69(*y!@Q}*q1N7_|I)zPADoNRoXyuyKUgm9k$ia6#JaTly6cfIm5 zU09ofveOBdSau`p3MCnzV0`j~z1j`oqEO0Zxuqg@<>mh7fzXJrYKy3DBBAeWd>7zW!%LCo(r+pyF6#(ItMNs?j=xnjLgbX5 zsf3R^$J%;YHFQ3mofBa2bIR=oBj=J}gu$Ye-);nue2%9jgY72Ubz~W%4K%#>+oORL zOc;m|egt8E_f@|=8h=Ru@j0~|9^2sm9qwvETlY3#C+Q7K5~b3_?-9L3TeQM#>yAe~ zj@bQk(cpi04AX13;O5qoYvO?$7Z*-;f8QS)m=cUlZzvlThSxSgS!zzQyJ7E?vli0P96I#jo!8uB(_;1Q2 zD{?gI9+O_~y3D7X4%JqR88D;GrO;qP-G|gBoyatK{aij*=iu)Fg$v4{YPrCaB~T=! zUvjD?{hTwIB2nXWhgCT?&`w`^Z5SYllpt4sF`RA=l*}KQE=)upO>gH<`LS#fkf9Dg zPDnzJDqz&Encgm#@?M!LDJlC=-J=B%dmAiLCKd?W!roxj;ckjd=8nY?V9_GW>^Gf{7i^+Cg3gw$LN36v z^-iGSUAzNDRCtb8;P(I#BIaE`@j$+rGaOvNH-9cxzS#y~DprkiRui9Txo1)cY!t{H zqysw--fMs;5S~*AzS_7LI6*3M^%-oY9FCHc#1B{XixpXL*pwl?>X%&=Rwa{>XKvhJ zC&O2Bw*Z8#WP5zI!-W+fjpWIud>L2+b+kdE%?30ImY{t#6ZrzqTdn~O7XKb(gG%$= zFp76(Ez40Rf>Ufg?3}%-Pye0eli}1E{)T#g@xJg+&K8+lxKHu#bJ`B_EllsTv)rGO z6ZQtkJNjdwc>kQIMc3MWe=udMY5fh23WvN#`o-obI%SUQ=EWl^klB(8U13|Nrs)!i zd@hO^QMvKa0vU`P@QQ!;-7o?MMQi>0R7N|pbIy>>Lt6aSFNZ7K>sJJed*+t;?ys+? z+3Tyxr8+Ewn``?7yYDntZ1ORsr%*_wDA|P0}f=PZXu82$Evsa~Nsa3aiu88yu4&$ zWp%%{i84^bUk2sM<;8I}UF`_{h?~mT1-T8nNGprqt(rIxqD|w})ZviwqIH88oRQ)2 zRs1MX74gl&{6)N#Tx$H{YF^GRHeoI*YSFCaIp;kn$ADfpUZ9k)fUETa;^(yKT2)dE zq7+qQfByKRskxk|YxCtdfd&bnZss-?sc3>N&~)k?AjJ@=3@3gV0U~*tERhyL(S?OB z3*YGcT~wVo`ER0X?NWZTeBC0k+R2_TN|F1<=@tlA20UJd6 zJ%2R^B7PMkN0_rZsl5la5NXTVzR;%Co3~%%<==DWv2UQO$vSnB90KfpB;mIu@Y)TA zj-~$&?Kh}!;{SN7ziYjJ*Cv0f-Oekti9PBiM6I8Fz*FQCyeH zbAMK^h@YK9_0`}!k&=iATj5Vxt}?7I(w6Z!1Na97m(EoLJ3|z0SFc2&K}RqBkyL6@ z4b4bvsTeD{_&go(x?7S1uC0N^-lSj>eR8grM-JQ}y$d*`g*rsWCn;Ud=M6&G?*%(Y z3>ObzlV2?;AtFPm=*l-T4n{>b(o)Zf1v^Kg3g?^9xBzm*Lrw3#R? zQdKr5>v*Y|b0o3`G|=s^G^Wzk3!&O9i+rj~KyaaFQS%9IhgNivE}~WrMC!^T2$0)3 z7>J^i7CdJEWhf4j_tNPr?wy-*(me4a$o(xCtT#EIih5UX#9l>7P>;=xhFTi}5zAOC z*LNi1G?0QeGybXptm@5#OSl=)@DM|!7b(ejWo*dkbQ=Yr?Kbls^DcWCMilG3=ikDQ ziW=J6lX!IWWNu^0=hdW6^@BP|x5KUJV)e{CaZ-qkKuxnia-zL@@{SW$ zXnZC1YKexetYm*DiR#X9i^^zjZaf}{zLEPc0GONTu$3A@{jn&zTYbM{g?44DM__8S z86z-Fe@is6mwVz5(Du8}_wtwlbSU1Lr={{#AlW^mjqeD`H(h| z2>lbL`<*m8LXeyIa5EMAOS*z@Nhat$M`}agXxwlNG^Hb;5tIf;KxTMT4oO2^!u;Uf zJsy9>;eId{Z^jX)I*}RP!g~vN_TZCl_MZ;@uHo~3Z1Z>m%FR?IPx3kKt{qIfsGj!V z^IWhdei+BA2ec%sZUXSV``dPBcqtKrTHaQApp)6;BW5LdmZr&!aFS)&3C&zI7b~C8 zjA#krFFE8o&hf|kuX8#r21)vIZjQfsRgQCrzL0$UyV-US#Ac9)-=7zP0w?E?=RY`U zbv9wY&UcICLxD8V59Ug_$T8F3d~#-VV29=dvq`iTo7DnETK|3X=#cZO)*j56O;eqv z`Iqe;;4k1zHwP(iIo0lPZngmbYr|ypNcE@eaxS%?7{;(X?#PgYYBP>;&PX&6>tEZ4 z!8?QGMa=sQ^d}P>9atm_wI#2{j8ke}5r~c09pJF&4@76;NF&O<%==~I7^=5&rSiL6 zH0+(T`GBP#E9}CQxOctuGbb!1O7N2VbRU^;kH}}<9b>7;XbM-K;#L&zvQ|>Dl#1vl z-=hZQb0~*hi;DAzIqWn?X=&%@ZE1ev*Zs^pZ}N3BE$?-sEnin$f-`^nr@lX(KFULhC`itVc5-g3HbQSMVOECY7EB!%e$O%oU$}M zpK`K5^{NynT_!i?{g%~ZkAbnm>b^2YaVNgu4Hc@cRRTaOP~VdNka87q#cm32rof4W zPDqYh94aLvGU=9yW<7!R%mMDUx?SsWhK*&kWT?6h!S}~ZmLU6R((2>&`nH&tNxTJh zvO@81O_N-Mw|8jck=R3RYPi|D-EJ#~bNYK{v|{xYne%O8ruz{`Vy-zQCICI}icohr z#^bim+ge93yP7(|X!FXLpJ;_ca5oJ15WZNN9nHkggOH-R(l3W8nKj*RSV|# z`Pavr`y(H--%ig6rhHrWhM+p=3w$eSz_}+1-;$Bpr^fm}7VQ3ARewpG(7`Vml;pp; z{^EB+JwzRNOd#oPNJ;<%_}2vt@nd>M^7{ho+23$b69>IYd_nXOj#G@jXrgLh;I8>i z+$MXl#+_9!XTm(TTI++k<7pB@Gbv*kc1pTksh)j$4grrhiFE6WCqpRCxhHnGqmMCAr z9C*mw{^}uhYNQjcRrM*(SOtiiLo935VsRZCj(Nz(%RqGs&Q#@?>!}uuq&fN86uRB5 zCYYWg>x-=>%U|uVLJtYQ)I{^~nETRKdorj-CX?mCdEYr+9^s~G=5XyUzeKXSNsx7q z3|q_gZ;+wbvf3e17UZ8GapyYfGxMFTuoscAm<$5laY!4L?>t5wX(fAJRf;Fn%eSzq zS?RonhH&wGVt_I|(@qkxNc<&s3`go-wl#HU`N?gX`DzEk48;;!iGXr`Km(vtbDd*& z>*VtfVWcr{r`HelRDU;j**138EZ=ypt6!L&x9ppiDPQ;&c4{4eoWXr z-Qy}%T6_)8nB33ppAY%N_W0Zgt=h4$tD65BrfJoeDRg@uK0DE&qGX3o7M_j2fc^Fn zz0S4Rtgzp9rI2P0w}0C6hh_%bs_*o%&D?3L?xdLnZbGf)iuhlBJ?i5v;a^fDzB5<6 zlI8JT_lZ=jnaYegYxv2T)H@O$vO<3RG|>I=4$N5JJe>HkIPqga;>Xa$k1GZT zka%-q;!U%}kKu`;&50i!6Gcb%b{l?m!akGrT#Cn$ z&ZF?#!2m|uK91UE2QUrSZD3ts`*@c9bLid#brIoza#5$&RqU(wukZWUA7w`KkJqA zvqb`Z_ONaq|DAYI^h~A717FGAgk{TqNUKQM5tu;C0>FK*rC(YW?Q=cmBFV5z62ARq zTzV1hv%75BHsK?`J}1VhUF(EZ;n_kIz`Dii#C2}Q9@4AeDEI;+cTVCTMt0mVoz0#S zRl@2HPE|~`6x`G~9<5Vy$l07`ALhjG?64y?e;e^x)Fi+x5&jV?yuJYP5UVixqh1z2 zb{wkB`xS{3v9IP~5TSmDDhf%4k8(;62N>w=Apvb}mBKmW;#26(v2t3^J=oSvSb{T8AP2Ny}UKn{(F{ z-u(+lr(!n>Wx|%;q9u<^yWdw16YX{Cm2%>k^_9oG0M_KxCFvMqB_ExpU5Vz}Fx3I3 zE1hxHfC+{e7U3A1Z4C4Y-+zhM=I6BtK(WqG%7F``GkvkFr~f>XP8T-c$DfE}?md~4C-u)2k$bd!G!dhS zx$Sg$9i|Fu4fWg(|HY@h61zZab-AdyjvZbE%_70H(}96hV#m-fqjvj*k%+pys506k zi1sy5y$w&<9x|>gsf2Y$#)SdcG@#rUEn<=LA?r&zgslqCoKAcrPTz-HR~iPRt|g64 zJ|xv+B_7Mwe$hk20CV1Cj{nJS&DJ{JpMpBemuCvW6`7I)>WtAk8H+44n6Ge(&0z=B zDWs0C_TRE_z$>fQo)lsktMT&!zyI<9nUl+R9np7`fW=6?AT%^Z@)cDL5R5VHnKFDe zE{QA@Oo^|q$(1FM5-9%Kcf(*oS*$SM_G38x2#|RCrhhUXv>P}ZjAy>!6%$3NihfAU z1}GEqoiB-WYT{euT!gfN;ky2K$&e-S8orv{h5zV2% z{QNaJh19zjWIOrN9BFBq26A?4N3t)`F1hI%%-$E4#*Cgagu(@X9LW&OQMT_K1R7e34hn^i#;fM3 z3dVN<+x9Vekc6xV7^e*7I*D|}?IAr^#C?RwrCC%Ed%B*L7L*fQcf{okf(4yTSYICn z5b~Pa+n`rPpPF>*;cP~H5>5dJGy^$H5-QeA2jF_<620H~^?=TDsEez6?`K+F4*UAB z1%YaFJ9rpftk)g3&WESCbs&cCFQd}aRFb19dqW4+JL}9n7-eaE|GM@1t(7VYP7|>aHy;l3RXmGj? zNHsa_6UEHnRp*FCc$3*N5L|PVc+^?SckrtYVrreicq(6ovS4c2nm`I_HY4z`W(6&% zqNZALPPfgEK;I)`*2|M4`I-}ul6h$ zu+xQa$ywm5?adRzTl1;Wy0hk_|Y+ub$Kp?{+?z;j?<|Rl@sMbFjA-|#^oGMlYzD@b&2AC}07^|A@uvIDdR z>Ss`XT`;##IsPMgkhuP&k)g!8l~5gk6yeH^5Ef z@33Yh*=1H`le;UD`NH($k=g|vWg-U}Kn@@^?wvg#58YoWP4dynJNeJ8I#jgAE}?~6 zi{?Ru{u7UKCJ~NmcPu zq3f(QxJ223#G0{m7p`=NA^v@Smk~4$BQblrIn$wh>C8BZ5bYwY8Y4@u{x7&I03p=e zQJU^qJ!oP|{AhUdug@PZ>v4!wVCkbN1F5KyuC$eeU9q{_MUu$hC@xRgIFSlZfFVe5 zcbjB-PCI&(F|tn=C8Oc7akF!dZ>c+HQ;#r|z}B#OH>z^U!Si-0gA}u>dA~@G2YTb} zLUhA#`Hk#=^0!6{GT2ah++|APggy#ne>V`VYEES zY)5$2upkhFWlZc*kqt0Sv&xH82YGZ>8L>A}Dg_v3#jHw$0#EdqqFz@)*ux+GWNhaYr?@r{@!DoAEF#=E0PGo>z)a6BHCNaN3~sai<~;i_jE7<74Er#ehtX!ReK?ngb4_3SFp-Cerk8z~ z%)?}pV;`z`s50&Kp?aQ7K=e)dV?6fN0{cq-m~9}FCYu46>wL)UkaBk6p#H!`i_4}2KJ(Sad*Eu@uC9QPK+uq8yscQ5nGU?~Lc}q2#F!4<{`%Rbo zxLAg7Hd%)G;q@KqZjo7k-od{4HA~w$p_%$}@Sk||q)hYfE&*OgH z`+-3|1__{)A)g4?69sG!KatJ;;&oCfW4c?3%(vAiD=rJ$NF}dxW=n0cq<*jpSc^CS zhi`0Nf$=6*%w>OXFy(Iz4*q+CU$)gD|L|nG zHh7x|l(E8V+y-wru)zUM8r;i=e;MJw1l9o~{LHP?WQ3D!gQ-*6YJ<79!7R7I&P^Na z=RGMCh{6zJ2eu}B^C%1Govyl!vNjTDPtV^ahoMZ?C*zx-s-Jh8&jnO|AfO`w^o2B% z1<8iQ!zNa^ra^VpM^!3A&brzHp6UXgnFK6(J+I32M@cC!ZY!9pNjw4h$4W$Luss%|J*`Wx!oFHhj`2>VHRmVG*cHI4h%Z%Cuzbg?y(B~Vgf6Q2E9!^en~ai?0A zaAdnYg=dIo)I52C^x7)%_5%4(1={4R;qofyfLbTZD?RciYaJ)AN)M=oDR@@yWQITk zYd=7S@qloPP)@h>$`~_bQ=}zB?!L#A53;+1C!MO9Ty#j zyT3#Xf&goyz%G|fR}|gyYHwWnDpvT+aTZQLbEykwb<>ZMStca8 zM`<8R7MJ2dnV~`I>0ODmUn(a96l0><1SMCJ<^Z=g^v~;@*#an9@=+CM(^z2{`WJH& z=WglP`4k0WQ*#2bnc3!-^@79c&p7v^?<6^Z$TkVRS*Bi2gF8XPnNbG%LCD!5w)XIFu* zHpYX{Y4!o6%i{R!GPwBf1b&9R8g{^kW%BC4mC;*XjXt2(5wi9~NBCbCM|9}91S756 z?Wn$pF^7qLqpIoi_25{hu$ z9gL8E=DEx_Y$956ldaf8D*k?^oyKndhPV9@sn=mS1jPndC1O*lJ4u-^(38b`7a)rSZttavCO;XacVN;B<5$%({3E+v2D2dU7B-lXlkTGbCB zpZVo6@!(%Z_rk5>z1e^JVCycMSL_LOEkmg;Q})Hu|FENjh*;Lgc`#Q_bp4*eCzEJs zNoO#sNZ%o}NHvo~kyKgm1K%PH&^H8eak~@xd=B*%dTGAU^4dp(cn)RuX#4p{oThCz5l7%y@!_*cAK1k(KYL# zN%C9C$r4!y-WN~B#cThb&~f-Zga=gb-xumpy?=j*5qrBasOtTDL)|04Y8Q8+vT(ZJ zE-6R7ukkz1y7k^8KU(r*DL>qbUr0p{6>(b<8)vL!!i~p~cY52Tb{-Jwt05{#uy5XY z5}ZU!deH~hg8G;Nl$7%peAnVF{f?mx(jzCVOpOK52I?mxpPp-EYakO=1P z_&}^7ea%}V(WKZjly}4lT66&&ku$@iQY1@@`7UK@E<2XKqRD|3#ix_vex4MOFg^OZ z{))!*Pb8{;bi1$$GFQd&rXCV8G(=yQU%_at{)trUd+H~2w^kWqm1i=GjJ51aPV6;t zbY(;)mYgV0(|B^l#&NQ5i2g3=yDP3=7Kd}M7aTr$>0fYI^@QTknbHJ@eT>E7y;7FI z;qg$%-*7mipCan%_bm=*;)I>Rp|3!Ta?t1t4$qaMCO90{O>y`#=gR+o<8a?(uvYq` zi^CoAbOKNR9fx?iOLo1;-jLT!+}x61o6O`3!3}?V=)8=8Nqi*Idus_I5@$!(0<1eZ ziD+IdWda+v=#~ZZf@d+$9nkV+^F2*BRiMtyNfhQZDLgZcBX&YRCtv0_DQv1wRj@@( z3N6`H&&>3MPB9aa&RIkuTnnqnC*Ke{QH&a5pGP>IT_tahKrfMLZWOYjBCg(384p^I z_wE+^H9q9)>|M?>NLXM?IWw|v*qcy>*@pkiO(=7R`H5Vn4Wd#%YuJqAxay_|=3uYG z2p0LE;ii09-9S6^n!S>J59byx)I0Zx(ghPcT@)F8m~N12F^l4b?P4*f$#7lF&lG`C zago=gFoDH`;y;1K{KS_{QS2_>nWa*g1^!DDV6n0!b->`kmb!RQ8;q;cb?qwhZE|{= zSZHSIt}3OYn?O$3WM03MmjYb0>GE6tbLmDc@Dl3kk9zO?Cp)ItO(EC+7oGiI7_zL) zZEv2omTd2T!jsO8j3!4umYz1nQ&9p>O)-(g(?N0U=KU1ogNx(IO(0u5DH3XTF46*D z#qtCiGUXFlG?|G{;Jyh$IVde82t|h>U*($o9&}axmzh@J^s#o7n@nta6=7 z@;k~U(7<*&9Y5C@ z9DY*Ymo(4raiQJ2dc0Y~6+LnWSTV1p{x(t4_aAN^Hwa+Sk{5UuB2JiVEb+#?i@N7| zR(tP~+Wva)HS(ALAU?#}nD=f_K{6OYK|THaOB zsU6Jp(1NA z$VV^hu;2m%JHe8NdH$Pock;%>Qv_Stp%#bLRll9Ww44T->}?=Z&}3ej7V0nzldGsK zZrb)o!`NlJyGT8eX(aGWxC91|dhf7FcGUPImilp4o9pphH_L1r-Vf z3aE&11r%iwfeDcv!2*)s=gfSQrc~ee|31&(=aJ?+%bA%oGiT16Z3t2TKbUh^#_b_i zDK;C#D+yFtI+f{ykb|C$E^n&@;DG-e%TG{_^l`?3U0)1{58D5bpdfBh6PII|2Swu7 zg^p!-Qy%g4`rv9`!aQuqi<{&*zr~0?w7QIdQdou)?`6+QXRx?5yO6&&ce)1FCXbKe3%D#UkC!Gx ztJGKqAKY7MzeI}h-;Jd`V@3SnX)jMbVcLe6<69LAvZPi6N~m^Jsk9$`#02*Zwm?yV z5oq#wWDOHE7cNSRz<0Z=kN~2Tf&z;T4Z9|aLE=tjQD5)Qae@3!F(@$pw^FT5seY*% zR=Jz2+;k|1T+dFrV?!H*bwbP`;7)do!7rxk(fa0Yht=gn&DnR!*U)8s5!q0M-PFV< z8^O%XO$u}`tw(OAx{$Tp;q!B1X7w1tfIw@0b8pL>D$zad?~6sCKe1H*Nj}M+z_|&H zZNqpKQ0T4@Y6fY==itnLR1XjTQ%rx@;9M`|5T~*Iq0Tnr_U!#d zE&8o0B(FJnnh3GV*wn^$#AeI3oV}gB0VcH37Qcv(}IKSNTtIJ-#CN_Gb6t2QHh~FTv?)r(?hUjy`l2Jf#pJeJ$qS$%&a4+Z@B~H?C|(XS+z?^cD9E zrYi|)>RxuM*pT-jkD@Kp$lP^r9d{r8t}-#^`8O?1y#Y<4fPNrpd>V?z@tqp!I%9su zA!=K&vFf5hr4yNce5>Otq$ymWOL6Z5e))}gj`Z?b4UgqHMwj21%kSt8wpm4S$MD3} zx>lF3@BWAC4>6{2l0E*1X1wlvF_RIvpMS)f{Q*;K#q?snGN)|QP5nT#yU}OAGlj6l)>;P@meXG3;vJg%RhOJYu4%!NZJau4mv{j zIOv08G#FIHjq#kefHe?cbfkrDFKIHdTt}Ux{B6=dYEk-{U35+`y_!+40j-UxOaIlg zKZl0`0(URh)3p_pw*3{m4eay4CzF#d|8y>QEJ_jvi`5I<6z2pEXjt*kdklZoB&mJui7QbFWm)V`1MsEZKIC%s_eILyD^1yUal1zX7Aq>&l&1 z-W$)W3i!1cScxM5f6vu)iM0a`WG(P({7MxY)a}EOka-TE^8XR(Q61Wor2rb%Q1lP- zD+3tb}6wt8*;32q~IS@@NXn5wR?l zw^y?7{gKncccn<*y6WsaB0FuEh8@nlRnkA0sliWyG~%B%=mMuUSmny6&pvc$Ct=(s zB_HjDTy1Tr^mOaFVjTNR&n`AXS-HHuzVq6@Cr`Uv`&!LEMUT8y`rPG+t4a$C>WBO& zIP9r@;OJH#|4R!9sdXCp3({dWDk?7OdDCJD?Qt8WmOt z5BW#O5DCDSy>}5j!w$nP!%CkNCuNV_lH8re5KHykPp@Gge=hJ{S?E0HcKC}JbERw2 zI@gNbv0Q1q_2BF^LvMcqXoWFKGANS4g~xKm*Q~nGae0%(MT7t^kz%}+GVL7EY!cO0 zR;F0sVuBo6OVkj@<0~DDS4y3v+#yBY6$N;SBi_x7&0(d}HV&2yH(7Z1 zq1a&Ayzr)&3(6fpWX%dW^~N5OhJF=4)lgluKcR-f?W5t4oo(y;sf~s-{gXG`L0_s(!N!V zZGoVyseSxo)OWDn@N;w7d=lC44TW*|Jc`N8=AjCtQ0egS7<%YBZ|h5@Pt>xsMiAqey5(u6;=0QiB61-@@?I^S#e!JIV?cMAe1 z2S5N~xsv~b{J)3)Ihn?EVgwvB%a({?%1C+nZX(-aBg(~jKe7(Y2pIogv9 z8YscPl>Xl;jsD9@>@eX~K{{N1cfkfv7{rO`n>Lc+me@74+g#P;pn>3=>B=M$>4_OW zpe`!5^MP0k(enqgf_<5rCErV+jh)J7a>4l;^{B~xr#knSIl69^6I^&20S^wQNAtOD=Tf!TIVXN41Hq?dhynVrlOf zH(m@bXCn$!Ah7FNwYP@FTvvyb2S4{lX>;Hx1`LpMfsxBj9oi=TOqH@a_ibx^O*`YWO zlF9|H=+=Yj{e82$#HnAm32`45R~U1EkP|6+&g+{NH!GO*wZ0!7g+R^j9;XhsMq%@V zQv87kf2?-$1K&R1tQ6<1NyW*oNvk z(ue-evGL;&uJ6lO;q0Hzt`&R0^aO-Qx5oJ{41G1DWp3#lj1L{Tz7ZS?5`RWXL``BE z#07Uj8g+yNX2g3$4$gCx_Ks!d=38mMh+o#c1SWIEk=x7M3pqEF^HR9z_%r`J2s4E& z;$J-@<`WXPgiYiUA}V*lRAB-zs^e&&VqThKFr&l7uA_u){J50_2$Tn|28&fGHp=`~ zqc$^{#3_|Ke1Cb_uw@?NDc(37pVpLnBLc2XXzCJ3i_42&pBMjQ{OR~(@$2WE4zY%& zr{pbclDBX`Oy0tAb#fNYs++TLTI|9Bb_tW2-Da3jkIW%7t9JL$I#FrJyvaJaKxY>3 z!1WCE%nI^F6*@wqA`zdZ57%m=S?xYX7yQ-LTt~@xl7hh-pjD}&R`QKs@rRBV)LY-i zozs&%T_QrQ>?K;Lby1=3LQVuCh`JnQHS?K9WHUMMA7*qZQdASn?1WnMeo{JcYc zxEGlDmo?WgkHT+hm~8i9mL51&DkpX~m3Jdw01M*wIYKIDTfOZnz3nQ!EgLFiTl^3T zH2u~J2c97H+sjfV_W2=QJxevC`q)Tx1Xf=}5&jPv&%SSzp0Ct?Xu^Y-K*L4CQC4fh znE)thlo~vc{G&rkRSXdlijBFwEZMC02F4lZ6$2$$;KG#57{%$KWsf)I>M=AiGMQtQ zyI2l5-yjaE>1Y_%$;&tKo(3+ln?t`d6f!(kq(c2DfK*7HE#iD>t(!cV-du07%K;_M zSbX$NA7}LpWFBa$i%p_ug2{$8TI`oYiwFhFrNtT^3(}$)9WY)sFj`wMD!UtN0n_V# z9FcOC{51^eH3cHQ4ql|YlWO&PHlV`2&Kg%Cueu8p^alZ_l5MnFI)lU0*Msmt{>)9> zl+^&KfD}0M#Q|T~^aw7zP6pRoU0Soj|1PX<@F5G9 zWkV{~ux)BJcx&wjZKlB=3XG}Ul9sx`Hz%3~Kg^7fcp2fOs0LftZt&y4`6E0{%6TJv zPzu=taTAlYZJ*0D=(HNlt=ZtQ`>IFyt_3S;UkwW&s)mDQyng&N%4PINb!KA1Lzoz1O=91UdYVb9y zP}HL7Wg7gwpBdrrE%XTYU1LUg=!zO6eD0ssC>>Y3!6W<6AK{g?8~jQN*#j4kB@8X+ zLYZmMV>LLZW`muh8azP7^F}zgR)edpLQx|eYZ@G8jj)MnFxF}?p=N_$E~p;i%-Rh$ zs@34|+6@Nw=@G7c-|WK||DjR(=mdk(e_vh$rG-%q#?@}{8G(gWLA6!-9#YQR7BftP zL;9OJ%rFgJVl{YK%?7uZRgdrq3s%&&=vb@4yK6UC-!z!>rLiI|H@w{kkYmsd(N9|5>ZSudPB+BfQQuc-a86 z4_lZ98(IxEsoCJFdDSB-{Zt%!2dW2Pvmj^s z(_p1x13w#WMp)jd#t83!#du8 zQ!&Hta&DU|H?-Eqq??l3Z#oa+S`Evtf-@Iuj))-bY6S z^p-;J%7CXON>aw09jB2T^QsXWTtjDsc?Us#C&;+*t-04l`~k`)#+$O6&dG#0)#X-M z#3duO9766$U)I;~?)llkYebpg6*}$%x%THw?zUK&ZQet;j9jdi=cejt1+BJ7oQp95 z=N}DqC#s>|Wa_0_P(PD;)ldse?tBaC(>0-<{xwxY-E-=EsEtj%^*@=uUSAXH@p^hB z9WAJNHKG1`j^O?D)n5zh=cb<9f|^hh>N6&HOI?G3L+xu|VCQ=ATp1JSUJL3Crrt#s z)WuS-8Uy`IZixl;$(m3fv7qigX)wSt%vV8ldiKPdYpW%b^{z)EW_ zh+q{)RVp<0LkA>p*-?X&+n3`|0ea22MoNrQf_c{xul9=n*6i? zXIItLoAwme2SQsPY%Am}I6(*yA`v%55-&7GhTCKZI@N8aNDtbYhgoVklF$en3I-%B zmz*c1LDrL6*|({d!CLl~MB}V4kHz{Hrx?*>#%R3-tTomc;L&Ly7gV07Kz>m^I zFg-$g(@gG!kmP15kK`innwv5v@D#z%t+|QgEX|_8;8ZsoPB|nj$-)nldk^()92ri%DL&QBq>j zv=$xQpVP$sLkC2-n#nqJyG#jXOk9)Y=L~*!pNAh>&$|ODl#U!iX(ZH~gIB5trM$6a z5@nS(2<%p0>z~C5U3A~9DVw^FR;|G7$te90j%UAmIU1D3C(wShn2#P>>sBK$&eV&* zPy=+2-SNo56V#zEv5hL%rL`QGCiN2l>iZ=QJ+mD#Iz)}6Cv>T(<+Y-?Qe}P!M*rq5 zfSQne7NjmfGO@%Ah-#2Zeen0dv^=b*{h2NBed3sTVrvaPP8>76Btfq>P1Hf=JKbxsi*3gZs6foftR zna&craV}&HeFFtiT&YWQ5+jQqdn&wLqR@mF^7}prU(8UD|U?54BQ=ygjIHR7~iHTCrw$q;a)v? zocl9{bng07P?I_2_f%GbQ)OJNckxJZw z#BV1`Q0`zFJ~}*M?#tdP#sU7!bO5S7sT!J|rcw0r{C#_{Ze*3ICyqyYn8JRITQ2m` zPq^g$qKgfw=B7R;NMw;NiK;%jx_VQwvo`g4(S@o%jnPdhv0m2+OIT6( zp1mBd7g^soQZ4*`n<(4V?qf2A?!M~vX|y4=S(j?o7XT2W_zJaA(z#>65@#o5Y1CW} z5Vf{7#G0_3c!_08Gpu%|R6F-pz(-);*OuhvIr6JzV zPYML}kAd3nf4vwrtk}hS)ZLo9;*Y?}%{}NUt*Spa#h>sE998YNZ!nVxyde-Gh<>zA z^*kLu8kr}UAt3`_E`-4YT^?NrgB9PXuD*WVu-;j*tG;W}de@Ugm5J#c7(sp%|9?g= zywvZ^(e~Lz;G;|AXe%(e^ZzhM+tZRO+M`sK)`-zHJSw;|LfmaPz5!g7l22}6T!xIJ=03|8`3B5ND9G~|Xy($MY6DwO)n3LLH4 z$2q{HGN)%Lt7Ccl6CbuiNZB__b~lu>*KdFRba;QJ%jGSC!kg(8^gxa%ff=Xv-xg`T zqYyw)nV>HCOk2sz&hM@vXQ^w$kgtwFPN^8FKKn?Ql+!!WlpGW2Mp3?7nS$K-BCVgq zWYlV1-BI0eTixjGpGF#887srdQU`W193I;ELp?uI76Oy1P2W=@$LnWPNheqkF6pb) zzv+xY&2ExQ&xADX@>sy05n!zeKFV@GqXr2;fA&^kTD)t#2RM!I=Pl7NL5p;`Fw$i` z2?=WPtu+vqpx54aOo~M+>sifpmgZzClB>c~Aqd;brp5t55cZR>i@vNefw~ms`?o)I z!DxhSLk8cXDQHHE$ybMOi43EMx*I|v zb6_4K7kaUa$q<6uthA?%a6Nu|7psL)kron73z$gB+ULxC+d()=z4k9RO>r-`qDRH4 zxwI#y-R>L}dnaq0*7SMHw0+EKGt+h%Ijm?z753$4K?X};{|;`Jn*@=`U!M~E)G-8V zBgXk0E!N6!7ySFuB zbMfMxnoAPi07f_x8_F`=(oB~e%BMy^Cz3?wA+GM2PZY4ObLa-EA(~-H(MjY9dDS)) zn37t2jB0zcW^1*>hr8OfSsSdgsOwAb3=bnQ*ry0V6O$>4K+hA3p@-6iBKevy+4MHq zn4Wn)#4BBnONps~tF5+2lc9toGiY9kHzu$r%waxKBO^1B3A8P%UTRPjD=a}O5AqNy zU?T7N{EDc{m;G~heGDyXho(OFA4sb3Siz2h;|x~t{h^U=)6JqB%p|(sd^<4=8xGYNtu)M1o9aj@n=nFy@@*BEcnZn=I97j^{8( zUGL9}4_WV=h!1>dd>0!YR}r8XLv_b1GLwB%2oB-5zi?czs=p}| zC(@;cs?d~3l@iHwbqO9s8YV+ZQoRSug3}?^Qq<+Xhdt^}^|J7xY;yS>_cB=Kw|P`W zITu%j@_eb`b7Qqy_;#d!ZJ38D=%`qv(K&a0b>R=3PCQPPxikP*UKHu997wOs5ePxrtxgdm)Ve`*sJ;TO&__sYnryTNGX9Bx)Ti$gN6ktQ z-XVhH(|<&89KHlS2V5#MeejYcy=|IPe!3B4!JpYdy8CQXaGN1bswVH4L3(wAn#oAA zj;pL~3{lU(4rEj^Q`IwIU5$#X8`r#h8`JU?W`f&U2ow>B%(9%l9|_TBdbe*41D|&u zaL@(05OiH4(94Cv)2A;%CM5L5ihU8R;oisZI|tQ36P&X;^t+m2~J zN+<&&<&&NUYLW#RuStO{U|Up!ZE3+A<@gJj){!$zM>E5>%@#vkdgk4e=NsfI zL@-S_a8xck1slnseXObeHwn$ysXlz3^7B?nHQB1fPc`8(#Vk-qUL!!Hauv^G;mim(2>st{Sl>0(qq z1}G=FBQKjw5@hx>%pC#s0idY%CSSoTS%DX~lKX$cTB`7Vkw1BDRFws$O0lUjU8>Z@ zgP_pMt%!f{>&7M0YwmaQ9-#K{#-LZM7>~1Jy}**~juS}&`Xk~ISB3JOXC3~Xbjf#X z3b7gD#k9AyB0WeDJ6x$X??ugPsHtwGxCljsZ_6A-iq2&WMP!gPusVqck5zY4^PC*e z#oY?=T=zA}<27h(bxZfvcPvY&<9O-lv}SpDJ|J7DuBc+DCC@pp21VN-NGj*`s!-2c z;~aRA;~+Wd_t5GB+&K#CPD?77RGc6W;}p~-^ms8c@m$|XBLx8FI&3g`6rSF_2Y}$0 zY&eu_VwGz=tkq|WImkgW%cc_c>~mHG(=wPH+FX=)tSIe#AFQv=P2u9XU1)nmh$ry? zH%bLiiQ(P~QOlT^_Y#tG9YJSc|L{<5?qR#RA-lM&_WbHxR+5VhQ_HBxWG6Cauy z8^xkT7|D67Opo<*SK?Zcq!o5=e5eDhEeTO6oVM^Io%V`OV`Iq%LnCwvar&TjnTw8t zTTsW{c`D!82loi!3A}o7JQ&Qp?Y!LU_$GIu= zo+z!d2bdZosj)0pPfPYlk^-wI$Cv=Ouah|06$zDJ77hx92Lsc&2YYNa{sTO0#jALy zdVrtHnram8RP%u0@;#<&5cRidn{+Y=w{r7)tl{doqsw=ZoU;r)4=-TQrR~RPKVRzl zv$x1^C%?nt6XyNamDN+DL&EC|-%3m}I)rdn^%5(<#A{bn2Pqkxu>0 zPdZg%b!v>&DTC^2Jms2B32fb|Wp$+jNI6RTX;c~t3#d^R+Rg4QUx&=pZ~y!%Bh|Fn z7w1;O3;46`zfj^?DZ#tTn>*V^2bu_f;hyCI zluHt{wy@Co!b9{zc;KfNN_QY+v93@))!xwGzU)2#QJ=`civc%QyA9dkzZ#~9I=EYv% zXzpPALbTIhW^XThPm6L-G|{RR&94y?=M>j4mk)2O^@S#)SYu8mBoj9E4z$;W!&D!a zcQEX^902=CFZA}wk%wvgGhOOQ>>M;X_4SmFBLA0_p~ndSGd*q`@j<)h62;Is9t}}9 z34`tLH05}Ey0{Ilxb3b~8`JC0eBiK94A^^{8Y(>mY?^!trSq;)hpt2X$PBU)^mP4VMtxG#Ihkk4Of07||_*;s;B;63EZ*-xd* zU3Q1?DEH8AFi&nBLdRUrQAuthZNQn5B8~;Zg$yE!;8a`*rzZ{crmes`A{=<*sfF4{ z;oHCrL8ZByXws~MAXZOIHg1*$%d)6p=% zTf8$Y3H4B1-)Nx9-0p=Vja7G!*1_0Ds*@b8u!6Cg{jI)89F`p|5~%hBRo7JBZ-48E z=7UR5H{%J%4PV0yBkAW@QuArnaNRTd7~FBLn(K3f=RbRpPS=VKr&tb#MSgaBL<>@+ zJ`mo)TMl&B^w85aY;;=^RZ$E3as%~*c4W@7FL$m_=B7D%k{8g8_XDKh7 zeZ_mQJyb&q1|DVQ_^xd#ZwtdDK4}Vlx7#Y@RQu|4Z@Ejhk>%cyXm`Y$Kmmz>Pz*?M<<%!crd5p$ysrm@@Tx0aRlRxo=5p9 zCTfbsx|=ajqiT;H!-q?n@hk-1SiMz0Y6L1|_45!E96tn1eUvZJ1Lch%PajrNx9{4S z(oXdhBNhkTN2s9AUdxX2%}Q6penSuE&rT5h`KQ8SQ#|%k@5mh4b!QDyJFAkCUv9EjDQsm$dsO zZHJWxcuAWlY3r>t!)|4IByF*kCihOt-7aY_T4}-tEDeMLq^+~ktOia>+9y_8S5q!1X)jo5B_?gBr1`D1g(hu_q|LO_^l{_Q z{6^Alu+pA1lo3sxlt-Y1@zDavS(vq#T&rI6$k``m7rJJ-T zB<PG4AnHv0AmU-*j$QF_ruO^SwO_rQ@OjZKKakL zfFI}nS>7VFxrwM%sa6iDV_PPUnDUYN90v(ixrEG!QBN>c)PksL&ha*S<;#AtKL-fx zOyYUGn&^E|aIudGW9sr|-)ohNQxj?^FJ(#bymlvXcR1^Gpt2swR(>w!|%4s8QyQ{Y+vG z<&4F1r~sT{)@hZQMmb~j zlO-cdhX$Q4Bz|hayN!gUtk3Q7S3PCCnIqa+P?MffK9Ui~Gh$InCXE6I5iLPiHEWPv zYYE(|WR~F7I;ItKSayV0!{%baCV7_mK(6c?TYmB#MB>q#05=a-U?raoqa`dro>G=T1@<*l+o4+;aTCCcC*w|BPzz&v}O!cttvG0^Nk>4UN4=<+Ynf1Q_ZQ#;I)k;&E^w4tx-koe_}63O0u?vGqHgeqwyUa{Y8C8=K;1 zrKJr~cRUH+dfNBz?M&P!ED;@*_Dad)KP|}{ zD-l(MuR1e?kkeDuRZ`x$+S6N3=ZAmhUT0eBfViK#sr}uZdh)tSU$j{4V0YLm!w7|M zaWr$O`eXpT^v#;0xgTauxqU!pUVb9OG%fT^(|*(}+Mqnl)6Bh+mL~rfC9O6q&(cbi zE1!h$9#rp~;>F2hbRUewys49(mLyTjaEJ(LHt2eD2Z#7`S8Fqr`ox=9CI#pa+Lpi~ zSsL!fYVZ?8keQW6#lBN{#-y_h`Y_xcGGw@7Acbube#hds3eT?iuk_g%&}RCh{56&OMl~a>b)o%)#PR$& zu&@!)xmNIK|%)++p{_Nmzw1CpG4FWeipY*biLR+j`zK`(ItH&*_OU%u*5#ZNM}%2_cALE&ZE9+yA`57{ z$O3iciEo`>`P9A|l@}^1U-8$Ko4ppPoIBi7Se~K569L#BB#G+G<@8pVJr*}pR#y}& zNw@lOY)7-}M;5_kwsFhzC0qlxSY44ve%#+!xi25Q=I;cY5{4aIhB%bnWN zoAyL0fZ7>me@EDw%NeiVUkQrP2$8BqTcgE5VJ$+KL--b6?N=441xlMD*58F%k#{_2Rroy%- zjz}wUs(5u|lRf~VuL=Y zmaXLuj!^m@470f!AEZ~ZoSOr}3~a_6^zV>NS2l0U+logatv>LLuKMvdsp?ccP1O%& z+9fF5srj$-(`GA)apw>4yFUR?rD|J+m9DQLSRd#Lu}o{|%+S#nI1jlCxZq0T>-UO? zNn1<3%ULOpNxo*9972;KeqMXT9ICs6T&Xm)tx@KL!SychJ}9<2`gp|Dwyp58+-?Vc zjQ&CFdou+h^cwpOFcT)(NK(v^%l&0PH0E2L!nP*3MWKHp5zzOmHj5xI{%J`_!!@#w zzoHKv%%3njoNkg?LsKVLE0y!orU>2MBVsM?PL)!nQX+cyzPyQTSrTw$pU=J?uRsphF>>_Ps_AaWw_==zB(jj;U0Eiwu*nsk)OU2=&nRnWuJ$cKgMWPbV} zy1zTW&>KT8c}>-eJk`pNEt(*D0zV5HdfHpQg#S^Rt8`A1&9WB^@u<5%jCwdc@GmLM zYO@|M^7IH()iB2@JXZ}HQ-7=LztZcrA03g4cIhFO{Pv(*OY{h6h;9g#WTohZdV~+6 z`Lq&hnCo35%yC$dl~4S0Z!2h*Zu1KaU4=`|*pA36*p|imrkKcLF2F(!ZIgXOBrDVq z^hVo>=F);?Fee5iHIMY~j?y`?q7zQpatnSWmo8|4{H)XWQ|RT0|1{eYNQ zs0%(9W?VgSI6KnpvD=`ydyBtPaMEUuZ6zFMx>jijd?`rpSX2pFs za=8ZU!IK!WT(0K6B>kr=Vf5;u>SwT}6R>qm6|S+ZJ25-;3rc5TE!o6kcf%TSIKz%E z{g6vN&eQauoc_)CcebfMT4PFVTs>3K3d}GYhtam?9`yM&b8{+p+OR1+_(>3vmHMft zLCv)>vLYsM3T}7P&>YjyyZbuZ3bn{4=8Tp)MZ||i-U$rc36cX z$K#x&L4&uD23qi zLdn!fi-CC z+pmI=n&4KErdn2m^NpI}?;r9Gno)W>qxc$Y8Nsan$0u@beHCu+GVdVErQ(&#Wf^fD zo=MzCeI1Ub9vnmAWC%fcWk;@Q`UlF^-hr!t?yDav_tr1{Bg65)7A9G2=e{=wu|XJb z+j<(Q$4ZbFBbk<6hYIqrs%tt1v0Sj-QT7-$d0dVg!49=V$PmS@I-jT&U?t6B7_+7w zls2HrZ1Rd!wgIM5oXmH%uVbssw+R_x0Qqd~-jobOt#}Zqz9xu!np&;`o2!QwtgP)m z5FV}$M`;aNbi~4-%8N^g`8ZJd6Ktdw&jq*<{CW0@nFeoo&m9a!AE>wKs2sUKn1GG) zsCR!BCP?oC_uUdX>UK|RAWIj_zP&!g)SsDoiqqpeWgdh~)mc2tgeizo%+$dU21LC- zTQ05+uk510NBqOK$+DrCz|0iOxqf&iWSnu{46l**%54AK)gSqIgZFG zAUbKGFZ_hj2=>z0lpsSp?>zCL7RJB=+u}KcGd8K;SIOZ$hte?2y<>{GOXX+L11Ln) zGg;84NtDXLxC2GPpd_M7hK)K_-KdQ^@a1Ue>0M|fF=Ed_w1ME|ykM2X^dcWA^yQ~g z2eS|l@jF`np@U|+>S-ihx11~e+ZZNGgaRWykfX#Pew-jpZfy|m&=V!nGf=f~lqAE~ zVjAGfwi!6veh1l;4V(ZhE(4QmkAG@NJ&-L7k?bBvSxcQWhiM2$HL#|k{j9&6h9V4p z?n)7xhR8JZDwlpmrok0zB*r)(64MK@dlhFE$R3u%V^V^(?n%}xfFL~!cUIJ#1q`67 zX8{$7W#Ys%TrbnWH1h`LwMibV-lwPPH!!nr(9^({XsG=lINIf{^T;e{+NS5AKXV{H zN-a0Cxg>Py%2+)M+rMNM+F~JYobo_%LUAYmPbK`x*HmK`lih!6g(7>rN!8 ztos~B2u}b_XrbDf3~84waEJct=O`P^*WBJE zRb3rrt*tJ!ca)WrT2j^7QFasM0=c^I<&LuYbir|d0_Ax8iBEx)rZWdP%5a+09Vu{> zJ*3Cm-%*B~&laeoOB6ZEu%V>-LY+C#QPxZ{_iA7_ILgK|*tz$XRE>0$rAVJZ%Pyu zCOOL9qJ-mt(}Jo+dbm@~0O$58sk#|((g^_E#ILNxM;D5K(y=gJ5*J&ES6^#7cS2C~f|ZzYjY-@oiT7KH zy^2lZdP$saB|c##Vh~{qTu-7H2pm?n0jBJ;l01N9YjlUzzWydh@3IWZVH!2+zqWF2 zkvh#Khee`uKD2TMOU|E2A7$JVti+y@sC43C{x(Z7wwv}B_Jfr7*mHHEs?1Z%zphBiQYsK z10V8_phJ6k_=ki7v)EWNQ|(?e>+WGGbrn(3HPYmTD7@wcQ7@j;h#DmQsVDupNk9og z@YLkhB9%K2oWx2^)>gY$47ff5j^}{%YItz9nd459Nc3i%ID#+>(r2M;fz?*x4XaGa zx{~-liII&LeApBZ2thzX5?-1+)pu6Tw~{kka#%`wY@b>=pGeO2Rug^tnPv36Bo)tNn)0j_%!c#NDuFk#7nKjX;+)-Jj7xPG_n#0PHn1JcA+Fz{b2@J@lR8|yCnW9 ziTc?(FzX+BE^=trEiv#KOQ~g6)kiEWo@F3`H>|{8Pa1gpCGjyUam?=~ag!v@jc$Zz z3mp&i5+fJAcn{#q%w#<_*AZR^@DPKN-0hI6?OY0f9ja648<$gnM3!g8VbnsqRh$VQ}J6$qY^x|!px z^wB20ZD6yc`?B-S%IZpPRxVOrG1K&CUjG{_Ewd4tib;k3PNj}c@cy^6@Nah&VRV8^ zUt;~$+#HvKwroGL0QS$&ghl=d=U7( ze#Vlyt~3a*6J<&%5zyt<^Is!S^KKoY9_CQioZ~iGR;9BDEQH(v?tB>BUVqraDDJ$? zVp(~8;juM$F44Hz;o_EB9AOLT8vOR1nmadko)MY4l!QCKnSSqu2ntN z#C*H0p!Dky_c?8-sZB~FD3GA%aK4vl6f9zu!s1SpyQfp6`dW16xh)`E4OR^@?uG^P z&LVWR&AlX;wN!q%+OI5h{xU}q0qLZAQKUnxpC1Fsy2@d3!Z?%>0d;ZUPeC&(JrPFL zoi(x0yZiXd^sF#u(v(00sZ%;Dg)7->W;VL&i_)yF+K5$i=FJN9M}50Wmv)F> zc_SV$^bMdOcBfEL&7k)sCj6ABnb4vxk#vb~F*AXa`z&0J)Hk2%nLq+oJriB&fDq>D z$5O;7s*jdAjg69;1&aWZN|W*eW0I#`WG5{gqjyrXu#{>7T>D{Oly(W*ekrG0Y7sBE z$b0Sba(xjxlL6k2-?0WX3m#UqFdgYK>lX}G`^)RIne0iQA_K8rrHnPdD~#}yr7fC* zguI2pKTXaUg zk0jkC&&D`+73VWd=*8Xw9y>e;9cHa@(D?-Dn!NUczt81&fp?Bgqa;aHme8#4QAu4G zds(jro_B&93Y{nCmbm;K+0ME+t3W+itX1i7tF#>Mr}r&sJTRT4ap1yoY>OJG4s_tV zPj?YhB*x`*m7Y$`{UVimg^nU*y!(~=?(;@I@$xg=fAErECv(8QB-MlMpId%P+L*GxOgIR&gw{fQ5lEJ9`$*-EMYjswSKy$Jz zskvD=R*WS!A?xI%r$d1cqXxhI!G0YuFX$8Jc0y(6pVU-#05FI?@ZYUKAJ1v-iq_)a zBv<~Y-qcPIp9gx9Ewn+zNd!_6@&J=aB6L3$VNrUa&y%EDoIt26ISB#{%d`k7&av9(&Zb)c{5I(WCZB46q2B;`7m?z(VV zy@I-TCk404NAqb{3u{;C++J*lztaIE-u_*WYUwgMZj&(;1HW%cFML5?Dwuah$Xu@D znetr6;*F?FhSqV#tO+g>U@fLySl}B<{L#~OJdI%#p*^m+lk?uB;SvpbsDV7-Js`ij z1;}Z@DrDmqIyV$f)yq zjW@TXS5q54rdEZ7HMYB?f|PH##&)-q&!Yk&=|H}0ZyjFDjUw2Hk}}D~~kqSNZ%Bcb`@4vu#o8Fgd*}BRjD;DxGVN325v2C-$^=Wo?F! ziPqs0n}Rh?Y$*TVoY-v^$82x-qwKP_y-nf3)USURiF z=2&3v7$XQsI$xV`dx%|;-R`(vgjYksq&+nqL+5}HsIckd)*m`D7$h(70p&C6Nul7a z!j3(|C4i(E@K>llP^*Ls&Db8U`(&|1YqRRkSe5$)gRuoWnQY@mIz3)9d#{J9+*G5v zZ-NhoHAea*%jGD`vs^z!nymRM98{3K6Vu9GKXOvQ|j z;-WHI-fYoaP0iPsVLhuZi7+*uSRsZ4&cTdE%XzikjFDS&x5Bs}G5DUQdM__%1`Tkk zUYp_nu13v_lv};mI4oQj6a3^7h$~!)_zd+>O{8EeEzzn}_spWVwcc2cYS#$tk0DzY zZ}TRgRO`G`)UC3#d)hxKLrvJ`Jc}a{kfLA5Jw9{yoQu%v2C^wFqEL}0%F}p+!@NJ0 z@Mr==tX_0d`fJT>fhH!&DpjACXaXWRudY}u6%WcaAX4!s53jiWSR?5;fs+H3HIn)+ zq2k9ilHRUU)gyCL+-q zp+PMiFw*;gru4l9UJKKT6j>f<`3@5Ax|Tvx8_{7dmn4%Vz{^k`1PcVXj*0`d)jsUT ztqz8cpeT*x891zg!&hk|CKe7#m^qzS&0FixIAWX?xa`yVw&ilO3{36P2;W@mK+F98 zvs9ae$_eZE;#4`W7}Dq28Zh#2On;BP>*A&KQ{T&8jM03vR^@cm#)8`Hrc%8z1-8IH zF$OMRS|@g*Fvj`KP-tuNPSA+e?d51&9lTW65b# zS3`c)c6N=V)f*Zm@c68}oGX&&C~dQTybo2YJiu)3Q?eV`7Lil?Ko^PHBP;{TZ{N6- zlmC>{DaixRI4e&5wnrKfFKYY6FVS*HV&Q2jWbwVBU6yZhn9l2sCOzHVINa0MG!S=` zWp$fH^=!)ud6F9*s_G#Y+PB=a$HH$j!`MM&z9MzSt$8kwqRU_>8xZjfc3hzTb=W=h zF||LJ+7DA(49s#z@+jxJI_NN)^SuX;4RS%uMM^%@_FtT zJ=$i=+8E&12=HzImz%(7Fa*6UN-x4YwUGDJ)}S1AZYkiA(mAc=R)<5obIa_E0-_I! z+|)+gs>X2z-ll0nx3)@r2%H0);@RP`7kF0&R~9)}xK9RGMb67wD10_4jKayfXVEA; zHmJUBStHZW#bO=DvhY@3` zs@-~BT`&e>3k!Q#Q}RF)2hGKr=EMd(#{LneD?!b(rpNlj#EmJR(VJq=#wK$D?{O?{ax(UDM#V$K=>J6rDD&~q0<5x}_!@cag0Bc&%be z$yG%ANyf_3FF!q9Qr?i25&B(=957cz8}6)D6`z{lW2Go&5s!+Nx? zkxgb@qzFYFriX%%8wMn6gmhIc=l~`=+;BAWS1{?E>()xnukmv2+3vRI5)$Ts%DdVo z{>kRLWBiEnorj7qF1Yni%FfQVsZFEh-YU;A;%FuDgXg8yvju;IugN;g`U<;NHxch+ zPjLZiim5lbRA4r=_(lOYQ{ZdQ((sG zGa>%GxQ0LL0yk8#rcMUlT@o*59D!Fq;&WXflvR&Elsd@|Id^l1aD!3lIr3#}Ka*}D zNPP4+1xT_-lDywhzin3m6lksg@ZF3b<_+M4GlDDVmvfFyN9uU=MV9!91+vRA9?(he zOVVyha?XnLaLedz($`A*I?`3Wm+P}fcB&0O^0zWyR+4;=hQ&K`w7#u0`-@W@xj0%b zfpXeqePy>6UxjFeebFM!kdBZ`Z`#UD z{OnU&I3dmzM8gxp!^ohV;zj09exwT||BWKCkkv?p*dCKSHjIQh>rx5sB)l0^Pq&r1 z_g&~-R9p)69}VAqr}$Kg*iU^mjX{I{R*a_myVA=8^yT~Kg>;LQXIwp)u^g9gG(Fia zr4k!QO6|W*>>|Npzl8!!KDRs z)Ub1+9WX$zs|j@I`9Rs1#e?cUi6iX3XyHg}ye&r}OTy^kd~1Kvk-*#%T#+OGy)2$((c?gTduNmC>OdGn(W0 z*J{=5#`?DC7$kLxdfigq5mQBE2SE zE+9d5=k-D*G+CEZIB7n!CpNcFyeYvo)>J2RJsq~~>K@a?hRQ>DjG4G5 z+3)*Zh_HFPM3iWg zvee8F0e{QgqsT8IU&J(vV}uSGC)%HASXhiy>pHf6o>oOJ_=@xbE`|*Xu)`_zhD1By z&x}3NR6|J9Ks}cPXVc$LpJRLYGhNT5TEW!dZZ>1v9Qe0}o%!uyQ>k}Uvo~1HvTty{ zs8qvfRvgJ33x-xVJY6?DQX2lcv+4R<=VUaVHd^QeHRx&Dj;(InsYXfLtxemHS@kMx z)k6+|YPsH>yia6IbtK|_;_+u0g9P{SXCC>FAwEvEgANB)Ql`*1g$X;92f=FLjsLjD!Xwm?Cs(}Ff96Z>9O$E$}0!;7#qm$r#po&@5TuAP<5*G0cbWGd0t04?j zs9b|en^7!}j*}ao%RfC;Y_D=hz>itt@fN*WY!Ek)Ry zg^s)fxuNf^T{(n82|8d~tN3TY0e-futeu+nh^q8NH+W|)P;hsUm{S^c+(NZv0|7wZ z_oAU$9c)D5jO`df<2b^P0;DEvfPrA`6xMxO#i|)dV%J0%Na3X$hh;Qcr>W-&<}qxC z7;P<`6VbY2P44p5XNaEq_}tGwBa3o6hhn9Rvunk97UsU>poa75kuqZIw*FN&gz4g;jzq)}4NU%Q;A!C-Yyb#jUoaU9YG z|Fmfa7yAFYoX2JpohfnoD|&Swc`6l>?^IK<(!s+w@Qhv%-=L6ow{}VJPT!NyOA-^{ za7k=+E=gFsg69S*ftIKfyiMF>c(CK!&_--oC9ISE=+hed(yPonI2Rb`!9JXdT9Sv3 zvI$jzGo!Pv!*TVcsq;^IB+SZ=FLjRyek=mIjFXER^j#8X82iYh`mUBcUPYEK(ePOcwYdp6eBkS_mB4C)&ZSmA;)D_UoTmDlt96 zb!yTUr~U)4OUfZIp6#r++m=s}ljF*V9J{)$8fbny_R&#R{zOdfJZ+`ESk1KV%rnt(9LnraDlk0Mc3=2&>+jQ_8?tJLUzPH}s_yuobJd1R0`ol5&&ZQ9#S7<0VUqs(R z<3gJwq%FH(Ck>0Kp_qN zbrX{`k02Mpn3UNaT-+d zv}9=!A^{zI*^_qQ(AenN`gACJ6u*VSUgWB@_v5#L=D_h*BsG}q$go2DR8J z1vPa`XQb9~CsyX9>eWE-&QKj;;flO#L@@YGL?EZB`NdqIU?4zwk@pDRdr3o?ZWzqa z)HwD1%?)g0t}!Q=IKjbxZ0cqL%G@Drl`kj4A+!~ag_>$eCJahMX&CX9hFR*#WeFP* z%mIv!mk1}>(;j8@_=XT;oAr9KjqJJJ>Y9^Ww2gu#?8SpHAsqHFG6MLa+`;OF1am9D zR5L>G-ui|%M6`mDUWA|t3{z11VX z{~yNkJz_8E(Eo8P`W~1&qDZE;$h(R=;5Wo`Wp+2~ThMWZunfIPQHOS%RJ<5v)(=vLzd7&@fgmG{&EOD=8|6 zPjzM-yeEZqAVaQ6EQFK~<7|1{I=haStiC82>vG}hEj$xLL~(!@JlmoG*}Xsc%tNWR zae9ADz=3vrAUJ?FN-I+XcQTO-NUqNkK*%==#fNhY_OK0c z^3cTtiJ~@?dmFMNBPY?1KzPp>ko6SI9zv#Ytl}kbURf?0UD#dp#`o-;4Yp@*S|rV82V}r zp*wjxM|;r06ukVUQV&w+KTkge!AU3Qi`7jkfgGR*HcxBrPFNmF@>+^-yuVN!8u-b!&UQd2u8 zz{NQh?B*lSu}lY^I-KV{r~2L7(56NF!Xpy5egXma=Nvg#;GeL|QTC%yaewyjF6ig| zOmesdPnQC%_Ac^G;P!P`tSTrrS(rh8X7+=r(rMXOw_xHOkOfnK`y!S>PsbS_ed)8f zOBE98h2}=v3MP%{RS1saJZVL~qOL{0{J{mjA{W(CMxigv9i?^<*1IGf03o>RMXkx!OdIh+y(UV@>8yR|=Ceo+~RNP~BO#cq>Z{=pKkb+tPl=JV+>yc#V|qnFSd zi8O~8U~PefSm}+~nJd>fhf(Aa^UQlr>l|`JgpebA#9@_6O|h?Ia1s@bg}+7zaXbEU zQC&~h;AhzUi7#GidOLpZ9{R zxN3wa2bV^foKanwjv$`cht*T)?u)!PBn6twjl`c^05vSA%$Fx=kt<$S;JtkpZgFu% z-h5Ty#X9&BF?A7@d3w~%7-2-z;-2@LzVx%Uqt^8m;dOnPUf0k{?&(PK2nGH;gOTfB zk}){bOP}yIp4Ny+8u;}J>gyL{qHpL?Pt}gdk%0|*9ga_%xU?0&bQY-iPY1K6-Af_gSN9ybXo0%9>_bEi$6(-HyTyN~NO9ki*Xd5hnp z!3A`3SzqZXbR%uLc-OldTM6hNbb{xSWhlOMi8`i4H!I<=I(m<;!m!OnaAKkN=<$~Y zSaZ4|GvSc#oRs>+zeqzNURTXdieckG=tZ>Rxk%j{Cq&ZpepgddZ5w|B60d|yY+9ak zQ!y)fWo~FCD|r>EtYleU_@w7*I_+Fpd`*$#y>2Tcj@~f}B}k#%(E5VXKpYO*Gg=Jt z^@E|Ct z3>zt|RJJgVF5g41Q^2wGm*&%u5!{&+5c1Dw$~1j~IQCpXM_dy{vVd95Q(|Z;S_qbr zy<(A`(ss|H1k%eWOYM5cnpL<}BWk))$0PMEGp+1ze)l-X()}sZI*Y*b!(RYySPFcX zBN8Iq5*EVX7WrBfl+NPm_Diuu!K$YoRgl;rrs!`P4!%y@W6p0;KTQ0#rk3}qXW$&Zj4i=1H98bx<#iv7ZSAW>8EG%@^xht>%I!#z+s*8n$t= z!z$G$y=3n>7UW9FwU{Hacab5ry4?_$Vz->DJ=KP{SP?=_l)JDh%v*>jJj>jJH1VLj zL#VVwP(GYMK02TZs9LSA9IR1|YBEXPOHRlqgfpyUu zcZ$grfflPV%ulWld+GsfAe3p@-#o1YPBe~hV4YEpGOv{NCoa36MeJQ8!n8;0ARkA7 zb2s8^NIIhC|3})pfJaqbfBzFmBnt5a0gVb0G(wb%AfSvfnt_DOzzm>(prFu3F&Zxj z69Kt9Gs5X~kk)(Es;#ZHS}(QUm8%%=fKtl*V%j0%c!(L(;8?>=XeV1K{=`##Uh z^DsGQ?S0n1t-bbIYp=aFku@%Z-hQV%>6yrjL!5rUAO`#x=b?kD3|AW1|HfwOgez2A z@5UGg`7Sz6YXWZGE67;IRdn6pBt?rq3G504aOtgJ=^=R3HmB(TcF!1M0V4|vNQ4muKQpCLKSm-xS8W+dCyJ81c%dq zN9WYb4v0%kqRs9n)pVdcu*}3%^!0mgB83-e#{(7qd+ss3B6-o!^|!O$1aL=PIOz z-PQt`{IzsP?Wx7q(Y#q5xqr!+AhC2}+~#iaZ~JAh^FVS}4NqqY-n&%T=N3B$PZ#!& zgngioeK6P{tlHwRFLC4cNBqlHmA>#MjZM(`#Rbike#EH0x*s7^e%79zGMcY)2TW`e z80)+~l3}LS#p?b4-^+eFvf1NZ_TxVDab7^oN!AlD=5=zw34!yRBV%Sur2cT{GlXCh zDn*P9D`l4&GN>JsdbZXSv-+mC2wwL>YLjaUuJSFqavP*Ct)2>zQaoc>iU_8B1GGnRir(j=ek7rTu?Nn5Y z@6bn?yz_P%`j4a0ZgZ}_wW%qMLzMlcIT58`Et$djv>OA{J?HREZ~U}Z|Ag-}X8C3( z93yc?V0<*xSQcXT8{?z)I(4e{QKuz>hf8~~QVD7w&0IjNx$AM5+8ecr!9;@R`74PU zAd0ke1@iBJ4IwOB^7pXUpcTXN5S`IFP!cMndTaAgCb>Qm&aYvmP*%8Qp8*?`{cW-f z5=wVwHyhlg&dY#Y2^3MT>I|h1o0izuuHIsg3Y-^IIH!W6@#4hx1Jjz;*N+cpE+`CV zMj_R2!~sjbTRfsBI&T`p{!U_{YWpG(@qMZr;Wn+1wek$7jU;%#c&cNscFrYNB@!5c z9@bZJ{+Po-)OS4Ueh4wtDJFRu4hN-$_4$>IL6wb>;n2X=My$8r@jTO-Hp)=(hT)aC z_~B4iL0^}q$6|4e6i1EU^faSJRF)8?&y>4y_E|C>r8~tSHyplH8`c}$UFMucUV4v1 z1M}I(GF8fOXj{uq$i^{rwbYEP8Ozr(jGJ6YL0>{mZ^~*CwcpGRx@tkU(rJ*g6M|D$ zrM|Y7y`_ng8L5Vci0}~2Xf51m#8vdJNaEF6I=PhU_Hizyc;g-8_pLM5s}BQAuX~+K z$L-MhpKMo}asHPebbv=5ssO|$y-%v0wj?2+s?7X@M#T%IkET?tBC6)^{897B$aKQe z|3ZH~kg81VK$U%rmH*4zQ8u4VNvgd>%J>>CU|({FJ+uQCJ4_W z@I;<}^9}DGx2JcG)-{x7M(5ZgJ4bU?{ft}vmzk!RAqs?hV>DU4ukECW_4sOSEM^a5 z$T`Ar%7yu@>hach(&8bfY(@4gq9S`Qp&~Vefwy;NM&^W|=xE)irIDOhs_+eSUL3l= zX(e>gWKCFQzGb0~Xs|+Z4=;j$dIvLnOO3gz#N#NX@U6S>N@rDM6E=H+ow0vT{}tta z8-!IQ%J;kdke50dR_5IFHJxkceE|$Vvf^-Am#sY3qwn|qU_4R|^TOz4#+)4ni>y0` z(!6^Y=e*4Z7MA5d{ntk)(PWubW-3>@V4g~{M;2?l8!lvQI&e4|nCtBSiW}vzBhY8- zoPoXRZqGkYhOb=p+{+g;^#-DCDo(#H20R^ZZgv15{bhzJVJA?*s9lFO zJ{ZukRpcdn{_N%}3USQHocy@@^8s3f_iXNhp%MFoko{3)^_Oi!TuE4Mt(e?}68^Y* zCg#>{@*5Ag)jT?RnK-NAtIP8$1ef$B(H}F8Z@!vRvHk*08gIAsy z!xB)*X>Ds6r`1_%`_7<*NX>_4`EPr1k>}Bde8HuDzox#K!K)BtTaD8*RDj(NoJ>+} zSr4@mMTrA8U~?rSkRPOH7fg@V{m21J*%p!USRZ0JKSjtRmz_c%C%+38l&OomUz@oRsVgr#$aG$K3p(>D+Vdw$2|ldmD!3GgQP&pcw-;pxT!|+y`pi z8?xUrw?IVFgI;rvHDysR${6I8L1a4oWd@&^ZI$!+U~bg3%UB`uok(lU|Dbu{CYH7R zgX{0}zIuONGx0lr<2!dbc@q2DTB$2|aK7a8D0sqY)_86yMlRS>d0tL#{o#v#oMNRO zqrP4xj*i+tQi*2~w@qV3|DZz`%x_SyGB6AH8g8(nm);OyR-8>b!ghZ2lzsB1ISM|6 zqD>N-_TEj=%oSXRF+a!I10GMeu0P%@rM6Wm`vy^I&k=}S988-H&N}*tW1!-Cnwi6y zhf#ZGzWcOlYps~ors`MiDu=vUZ&@pTbRL6YC-x1j4>pdhNbZSWLWT)#Lqp!KFRZXu zaN%suk0$xJ#y-i-@rtUms&nhlGAXrfOiH}7-BzvHuF35WVdE&Tp6?=*W_x1{^=v%D zgZ~=yVirC`a6_G+;x?x)%zor?ks{K$Y}GwhbM3$75o6HzCG1+JS3RNfvePR@F;3|~ zmYiZ*?yNXjOt;m7b%kW+;S;?%i~~vphm{71PlNT#!f?Y-Lyye%hN1qp#!$1ZfdW(T zAqM!4-%E6c>aUXIesyu?!Y1ZJF?X~TTT9;u%`|SB&b5;3gc1GF?4-y+(32|rsXnX} za<)O$HC*l6BvpE9g2cuyr{0eqZBNhlSL;fs@I?HtV03!W z7v2uGgl%Z#-?WC!=Bc!R zr;^aiND2XV(P|n)9L@^_43WHWDo<5b&*&G|cA9Gx)1UwmKj%92u9M+j@O zolTHJ$ymq(#L}?UaV6nU*?dNB8(*?@C}IkSbVJ-np~Uuf;nm-? zt+G~hJsp{3?TlRXLR`bTF%-QR!((O>l;tQ(Mm*hv% z3y`-qS8Ej+S@wYsc4%4+*OVm`Fbi{}W>mfe zJ*0PO^C4TqaVDWD^6mN#PVT2{l2+w2dqV)$3Y=*e9 zY+lI_>*fN9M47(-{0Ji#d@;aLKbT*>Dqg9r3)K6(^A;h5qk%gYCh1!HJrWuZ^Zw!j zpQ-Kd$vq)y-m9yEI~H6@R9uXB<6@awWmMLH>*KKo3k_blus+JB=PiMG2z zLgg{MFwBG8Qx)ASdz{NKBtmO`zhckLy?LHtTOCF(rJa1{2|Wwy(OiI)M!+N_#FoA zQ%zIXmD2U~Uxk`hyXG#p8jiYt!d2+wVcfsWuWxH#l;6|n>49Dy(@Fhp~Yh;;BK0(D_ZWHeJQWuw<0Aczeqm${Z;;4~u>Hlf^?$f82zDc*de!5C9Uezpvys}DNJ zEY`s(Twrh%twnF&U~e)-Q*Kb@5^l$KoY#f4RjK`cUg{dZR1_&6Ys6kp?d|W-MxU79 zg8yxs;05MFy20@(bTa%VZf>}JF6Ju*_WlT@=z!LEyX2JG_3*7OO`muIVm zFxrLhO98;tHaMdMO(no5&*nw)XR+tb1aEWd;qY>qI5DyB>Ug)rzM6PZVqaB#yHKJk zkkj~XV&D1on?s4*lAK1|H3pT0+Sdnk4{v!->f_92UDs-u;s`1h=)-mLl*{ElqrMkk zOZsyNGR_u$vct?@`pmqWl>{PPrQx(cQeE73rY=@eQ5C6*mF!?1u8tp_a0XH>YsqSf zoHg@OxDM@X+JRN$-qn3z3eufo>5dW@u>HN;yjv+1>ZwvHX=7T}vOTbj7bADuI!uE% z&#h=!?7Ug25|ZGZ9dO&&CCx8$^5FNhT*2n8RQ_3lbZak2x>KnAc&`^Ys1a~xoaXmJ zfJ{}{4jQeb3E89}9>z~sA+57Y8?5!m;wCnTz8tKqeX?2KrygdJG`nY~Dy@(-PYbkC zXV3%DG^Xdn9WlT{D|vAnGq|5l?5XaLrn?+IR0q;@{ly&&32iD0kh9cEbpyTVcOsp(wXZ)c51^f5 zQ5bD9p2fjWdZd&xIY2-d!PD6FTKtOUlpl&lmlt60R+u5V@SMan^*%@z@#x(8vHHK; zZK0O)dUsHqD~$n7lqfLB6taIWTa zpAut_)a=3TGwP70E`o&JLP}@w9$k?o_y}&QB6#c)BKVErz3)Vcv=+wLz=)JMFPyAC z)%K~CoNw$daWbbnBfB*?_nuCtq}g+5$StAmfoVejkV$VR0Hy-w4ueyC@P+)8u+5QYxv6HllA|r{@a;)SOMlGDUI>6%cwsTu8l@E+?9ai@z{?@dpDH_`4*(0J(EiV_Q(0rOU`DtEkG?SQ?P~VE>3aI@Dzd*7;V> zu) z4dMg3J7j9qHYX9taepcNnS!2x5;-~USY8uQaE|B6=&h$L>4k zz2NW4of%x)S1u#Lu+3r5b_jnW?`3oeLMnH548Asxlh->>HD_C|@nK6(5I-=mdV-8} z3k!3DLdXW(IUuK@F_5bvSMutf#z^>|&g*wVz`5UiYNdK<(qy`vOy0!|aiAD=wxNv~ z4^lvr_syn$L_cPxLYJTL?3t9@&cZs9JnnL#NS}N`IU|*B-?9bq`MDY=ZffH&4sC%2 z951r8-4}ZDZt}s{n#kqc$Xs*7Xl#u531{n2Z<+H=H*=SI7@nHq4ADvR*6MPmx0!qpTw{ETQ^F-w4b5N2Y8lg zsEV#(Y9T8zg=?W?a$_aw2N>@+GQ2`uTo;F^rdG0z)x83bBspl9-A&@*gmf`IUY6Zr zSM&-j($cg<-H^#EIKO8gT@?Ju%Ip%LW=k}4_B)aEX^Z9u`dXN4frNDliO` z;hJ+QgFCE@47Zh;>Kusa!{_?2@*IZ8>;arXk7zz@Z5AQ9t9^|;_bizhfO*#y)4zjH zp%yU($=uwkPZQ-2za)8-n-}tylf5DDcHRwxVsY#U)t~iNzrZwW=LSr0?G<^&oUdck zyUTeba3HI`4-_`)c*gjCHN$8N@gg02mvw6`OS;DR)?S9hF4kec@`|p;VQLj)EdNex z=}k0@<>IZ4{Q@sax#YYZ8$(cY0hFnpSvrCoR`Ps)!>6oeZxBD6h3}Ig+7(DPGK)FQ z_kzq|+Gk=d&39g6s+$llizJ)ugF8x>BC^IVYsr`5m23*o$}Zz5npVPfJGN$f%D*s3$F?##~e9?!yXI6sqm z3H)mhNv;rW){kaHGhMDUe+F;6wwSkq4*JL*oSMe<)29191`+l+@LH6JB+bIbP#*f{ zIfIWg6ZafiPw%9zHe5gN**E(^_1u?|ng+o0g8y0hCyDzzrQV|E~jLV+t6R`8P0T169PIp}s_I5zPR6n1%yt@vvPhdp< zXlBxOo{a^9zr|o+T$|uiXbwvw{}D!!8 zQcPWK{0K8Mw-YzH7|Fuat^ALdwyGNkq|+ALzT2Z5rzN~Rj@oQ7TM_%Zd>Ibbw&-*8 zK)k~u0>HEj+vO9$@w5Im!iPH?SIl1>)!3{h|4md;~c@attDrwDVf@0 z%vRmQYYG1KYzSNpeDItpy#lMhZkw5`0NH~+!t1**th>c^94wl?CO>gl4!-sD z)^P3P$7A@O7b2T9r3cs4^kOftU?P7Kujbf;$Lc+}ap4Wv*(j_y-T}Y$Qt}sz!7yz( zOsm~{!?e8mSEes1tt`8w&#a!6_WM z7rHa3#n0%SO_2j5(Qr6WdbqL3ZKCK%rpgoS=I{ajaM|N4?iIXOXL8T2f>v+T6&Bi% zeVDn%W*gq5R!Y*eyBXmyJ0GLza{Gxsh^TO@Bz|_tt}RKd%fV>b z|Ahe5Uzr*7HN^$6=~kZdUFPckv7B$nQyQ|z^og+p6lp-=Lrxh^g=6Vjg0#+u_Gjn2 zqa;Qq%GW*)*SC^aQYD)33~$(c-427Vr=BPB#*v#i@7yVX5x4?Wp~as!S(>V^C_BESEQ@2vlC4%P#-rRlE!`P`3<%@Zz(_W;fd={bz zXA-kAeJ^Q1HqDz%4t1Nf5G%EqC%EY2osj^3e}Y%-)KyFoW4oS4()~(GWRp5)lSYzs zi;`-yN!MkQbP{Qal498;SxXE;S&_CMMbhcnBwbG748q3hoQo;KH+UrfEqPttJ(KcN&`dSymmZ@s3o?I&@yU$mh>DH<>M7b5wvR=?j9)?BFQD!ec|CP!o;O)m56+s9(-_U z&j4d=BLSrn<)1!exch8+1VhXM$Ul-k7O7S1B+i3cCx1hhkXb0x1^>QK>*OOqXfVz1 zdlir2yCQoJ1Pj)YX5IW12?Ovw$y>&2WiT(nAG28g_BJh+(^Ve-+`~X#ojLmq9NDcK zx2yXQrbl&@X!WWLe5q9$$qSgh(e&Um_{v!Q{>-Pb^d*Q?TmspPuW%tlq`DrF>Kchu zF$0>+$Vjfw%2eLtp)%D-U#2piRiU!Fl6ZN@uJdH7nf;&lWU4+bGL>s&svpRMOf?dj zs*e7bnnYWov?Yd{RwR8+#Gae)=7~-d<=6gMeVY^x+VD%>{x_K_mR?Yd0CX-{oQn!H zBPB^)1>JJ0hgeh4QC5;Vf;>o4!;z$h`;ydWo+LF<+0(<3qy`^GCSQ^ojwChQ__y`$ zvg1#Zq%O%1uV|5^e$3U8?oh)pUy>S&BsJWVr2axPV%#7VwF{osmuz4sC0#`tLY_SH zTeUOjSU0!l!N@dj7oIB9gRdrkXr(9CFqWMJWrx0QZ+}FasU$il9ZxgG#%q|YRhi*1 z=DcbWs_fkZ_QdMm?jOobXo}>lsZxa5y^*>Nh$pL=FA;0#kD5Wg>>oj#LEbN}3jQ?X zpH=prO1u2q2dQh_tNn3W24^F0?tIGk^oi_b_b0hD(-;*9XR28x4rH~TTN$#<6wUZ~ z=QHCQ+g4^ujvtvG_c^mfqiKlOoAE8jaXckvd1ZPA^LIv{S!Yz* zKU8JLeT4qO`8=zz@ek6ZlMiBJ(ibq>7M9_5 zI?~LP4r;s%<$s}mKbt8d3(YKv*rSTDXE9Sor!W1Ss$cqlnklFt81OK4@(ny|l9?-$ zN*IMmW?1uF`8SCHd}b<7aWj(;J=cZoN&h`pwlWl4{FKjGdox#F=bgDS?2l%yd;|*3 zci#RFbEOkmoPI}S=gKqKlcaRCo+{h^@Uy9s)j6&rvp-WlB+vgaQ(gxAe?L<^=9Bsx zP4VYTO5ba~+@kjyK)S_&eQFwkZ1du#*%Oe-!XaXyEv0cWtPP}fR&6; zVf80+oRJwChJ}&N0#?UioK_oF#Q#$M4{;vM)AY$J(0A$bBlXAb)})U|;x@{a91Or)*?EHwP8)8|8*s4k1*0$G;@n3&yCru> zlIY`vWhH6H_n{TqfgnWbl`-80<~RFnQnJnF_8m+T*C9FWsqgOz9E?YLPHCuYP9JN@ zZ}}Xby`h=-&iq);`jCBHY0M6>2&b?h=*PQ4!5ADH&n+`Lr?EY=4cVBk<=4{5eu+ z?ua@>+^XQizQ_JvljUnGDf@O<4iRpeg1q&pip|rI{XwODcy)Sm=l=}1CwKghdzG1y zT`@atvu?x-mr>s9oSx6u+J~h>k7TsnyFm9cGsPFqFj`oYWGqjQ18ufEodp7O!IwQD zYk8U~oSAO-jnq)|X;gEYg{A+^05v{qfT{{&fI7Dd15^kLlw9hble9EEbGDI|LdFEO zuL^l7lCzU+tN|6sOGFlIz%v7JDM;-j*r2#;06GunyXUIk6XoaLYt{g%Q8eas-u|~W zz_&qJWbtfJC+E^Ra+38Vr=w4lNXnWTY~25OId=%W}7WR3wgo%Gn~Yb+2JtO zpL{`O3ob7wq0*S9>vr{b_97M=S?MS$id>YVma=5%=uX`SGX4}OeckT z4rCUgH1u{}%E9g;;k1;hB=+H&{!1RD{r2XsiMHZNGi#ktp2=F@&e_ocD?hF{x$$1w zJV@+o&qiOS{NlEqM^+zb8%`X^w{F&c1X31`*KNal-;D-$S~o7oUJ%J45Do#w&73An zzv$551NAE9uUeh5q=RZ>$Z&6m*L{0Ue`?$SI0TUW-QCCEsPm zxz_f6`x}Zs45y4p@CR#2$ClzF+07J6U1u%Po~>VMe2BBo@=M&V4KubU{;R}EDsi{l zhN@#YF>7_>dhgHbRy@qe$;{O5X0dx$*bv_QYap!Ts{)key>kMG`rD;v7DFQij6b^; z+M=pd_i>SZ`C-2i3v7x{#75v8jwu;xj&o2oVAeWTCzjCDa_YHf+m6lqQC@gN;m)rWFW?PSzX=N0jyGM}Ih zY+s>A-;~kzRX~-wlYsIZ7rLR4ohY-%AI+RUq#nP|Zr5R*WGibu1iKcWK?U^}#cp{8eB zZy7NS-(Sss*FQOR!D$)OcSq6eXr^LFI5WEfm;E6^Z)7^kCcIg=VbLyu_y<*;vLDgr z&+M@tTKG_}@S)AE3)?fi!YOf1Q>TFQTb(qp^FCeXkJvr<9=MN-_q3=#)0P{GbceHf z;nS1ZwAwZAnDC~3Y}zhl*KAfHard+RrAQ5m#r=cva$NlrGx=ct!YN#Lg5<<3-V_eL zZ{2t%WrcHg>sYKp@lFn}$j;z!aJRLjNXbY~%;0#$nFIGS{gMMX!cvLBVRA{a4xciu zG-nA(@Ez!xrJKXSE!Gla!1T~JJ~&{jH>;lY{yfpDd1ik&%h!snWM72h76J5o<{nH< z7hYxh{*yrp2d}}0Ng{7FeSMK4^mtdWBm1noQyUy@%udsn$Sbk*Y;Li|!1gGUEt-Jg@dIASR|iIBCjSe0WiEc~|B2@@U5Rn8IKBvnS;mAz z+acAjAb&v*h&_x$m${2Q8XQQNZVHHHCilx>{f=b%Jc4W)?Xi!z3l))!Y%@9y2RC>? z1>Ix5*uobEN0j;!4l%OY$s3WW%tO}g^B1BQMtczTuJa^5R~1c|22TZ#1Yfa|95P{F zg@SeNkD#-tA)pbCNG4t?95}Y^jP`)4J1_K`X>i2HnD4vrJ@qBlk&qmlMRKN(WJG41 zsmNdds~sMa_8_T$#eECfU2c>Mw~Uf}e!GoRiHN@@ zR5yllHvi8c(fQ-cIZRi#-=S54zfLHOl%d^Qsf*dy#Qs@ApOxU;*P+B2-3|x>s+ili z=!EWBWI2QYkRtn@@;x3Aid?B;S=0JE#~8@k#>Hkx=V)wI4Cg>8WQ~ORs~L59?MkrL+0%B7owfVdh8#a=^Pm?qKJ4j#mMs+kqx8v zR`*^W4KpJu(%nb-Y`MXALKdIbaqp23 zm9phwLm$BQYR;QRF~d8DgEESx-pHqTHzK@obPeWVCGQd5yZF3W-a;-O!~X0l ziv6V`9BHVSD4t7PK4UTG<{g;STIWX?f*Iz~AY+81{AuB!qQ{i6n%cW!+2- zva!yGKkid6vJ1E-1sjuxVe$o1jJR{39x(K_axgJwSl`ct`-&HlL;w7%i}mOPp$;>A z4Z~)eH1ERWtm%WlVg66#e{DnJPUYYq2<|rz+6exvUS`t$H}~l;l#^?a#V|dik}=#%pVh|AHFB0ZYr)HNIBuv>j~i^*?$^4{t8s1?VjF8c_P^* zz_KWH?Pud(z_(saigP$eQ347A<+!|X@#5Z25*)Y;Oi_gz@1w5x88klT@h_c=$@>PO zKs?|mkUFOv&=6I^Czv#WS|(p~z-wT^h%;GOuGlPiB?wNAa-&INA3DddLlUO+ zj1KsK1SI@Z4@s!*6&v^ZY9}k$(BLceZ8`ez=693PV+|h^U7`?uWhN& zgwwooJH*Gf0H?4HReVRe5iMG0`3ayN;GsS;-jnl>K6tqGO!S!3vCQXyJ{DY4zoUg8 zg$Ht9zyO3?(b2`7p&7fnXsw`8XaZ>cH||*i`@SRpC2u6k3vWdBj`u`3ItZzz%MUEZ zNOLO}&q6UvkLKHkM3QDQEV>%*XY`*Hugp9k9)YAF#wL?D^EnAv!&z4E;!7?;i1ql- z$V`VzBblxnjljY+jYcTyCV|MGc3Ep#tM;V}IwmoFag_iU(FNavn}|MwojiJoWX5&I z1N5g+nUX(5dwd$s3__;qx<@B9Jz3%p;o!RXz0qKAD|wpv5MhS6TF69>&lr(ka&!3p zkI^2STD2ja)oQTUkpDMhX zt-vTMfQGHlF*BNq%+6Ls#!P=57@ZkcoHLqM#Cm)>(nw}q-^rqA&z?~II`^N58q1B2 z{u)mlI&#+>Dtt&Dl3)-ny8yXh1J+oHAK}dPp7gQlAoJdrJ`@bYeTW?BJcb#)s zIBDtwu^yjz>t$!0;LMt5C3mtunvL%*?od#ep5DeFMAGN^6<8tTjVchy%rO-(5{Hp_ zc8asnLcbQiuyGG4-KBhG`bX02YancC(G8R%g!mLn;2;bVvV<>g(6rxy6zg(U+%U(K zd~su_FiTWyMXwlJZ!pBQPa}&gV2BIi+zxRfub<6PpwS|(__?N?Ke?)(;+V|v7xs~v zxS^&$|9AgzeppTcTil1dZgTf{--fNq-R|FcreuT)A%dWCvji2X+sb;sJ4@B6Q_1Nn zUY1wl{*jq^ro@M0JwA@qy|28@qC~m!%8km?7Y~{5;T9`WG3vUkGALN~56o74E3W&8 z{28Xf0i?gQs8~Tn3m65rQY}d;ES03U988N zk(oi~M>8eg>WXQj^Bf)y99b;OuTQKC@Nou+pM(a zCSD1*Qsun)7b7hE!w^`7m6{@jha>-c7I}c=6tRKy@-Q`%4Kwwas#TWPy@M_he*m1G ze=EMNR;mIW9cAV9V2fXCBz^eLoLbF^i&<=x`;AVvr-W8hZh1`lwTwYl@?+CU=7`P- zGG`GJlNMSxAIB%|Y5bV_V;L_+4)XOWEBPckH1Z$nF@|=Q|MnagF-CVfhVfc&nW!w$ z^f1P2B;$1%4+ z=5d(l9E?ew|E_>{JzkAwx?T_4f4>Fc+hPmi)=xI$IZPucJl((}B#6<+!0J2S9<#T^ zdaNCp$(=;4R-4wBtPJ70FrP<5gAa^yjmR(wy0cvbsdhd#WI(k#U$-~>;H@` z9tsD4s9)>$0G01Atq?hi6|t=e^}w76Nwp=_)eO-1MX~rP^r?Fky(4>wHvy88+@pl{ z$anuK8NT^|E3q@eiw}-jOMQrKJ(qZA#wFBuA9ia7r~oDufzPd!!YsMJ z0h71hXkpNJdh1QSQ38v_Lebr5y-^39^Y)36qMs^UL?HoIcsB8g;2r0J$~{6D@b8}| z#Q};Fn-jYLE)4?o3U00hp#OH^f)1 znX45zsE+PSv$b9!WqB*G7OZS$RyxP#O0DEOOkcN=y20~L&*9YP z9TCG=V`z|(oY##ffjZ(b@)=KtnEj!9ANfs1jya?vUjYsi*#l!3oo^OTq6YrFHW&R* zwGj!F>Qr8TGyzrVes!0o#_!GAOw>msZM^Fjf$emGS=OPYaV041m3$6;6^pb8-SUIi zVl*INUW*Y%$*D4iSjlbsDdSf{<}V2HNP#az?N#1Y#E0;Q9&bnM-ph6e9e`SAr9NRi z+~ zW%?SH?;dIT-0&)Hp*sg>^v?f?5(B@-JJGr?ybrxHtkigY*oO~spbs@|b{89(;PGu^ zVL<Mz=haVz=OV5JWLo9V-L$TuKU9%lwQTK`l=g9n>>AFz==>>&^K>&Jn~FsOV>JlLK0 z0z1ivUGBj?yBnD0!>T=4|K9=o$(y-LdwH;VcLLkt!`gYU^|u4t;=^`$?d^OUFu7Qn z`gyS8zXBuTwShh4!J@YSyTgYq@nCoU23W#}UGBj?yBXNEKCIe<^+K{{^SUV54egUvseAo`zFsUDxp*jnFSc3*c|^J_77=AJ)!;m0kvHtPk74F0SfV1MFNM*5JWb{Q_8T zANG(3>o67AF+OYwu!*fT@D~3X^zX%A$!j*8ULq3{2j3TeEH?siNfwQoZa8lukNXm?p8Yx#*w|dB>`#CD{4He)oP6W8YVwAve;cpS%}o}qhdL)#q4+DM?~Srfuh`B~_6w-B3AigtNGcrH2c>lZ1q*>j05F4`xk> zmOLf=wcvk`Jm9atKZ~vY?~%kyB}2$fF1KM*V}a^h+iCDA;URox%AGZIV0{^uC*Pl2Ty~1VPZaGK*kL0Z#y~0gD)^mmDQ+kTn|1do((ktZ+wDpIh zz2cyxaMNBrB0XLUB|fQ$bI-&}B@*2O^;R@}x1M75JqF^}nDfk$9vee@UsanCzBO2U zQ_LG<_g7w{=@<3JAS8R~5U-cW9@3tKG&hu|JIDn(GsEP4@?v+rvnxvs>AGleXig1%stASk9N^b?7vKbzg|U zcM43PmGrQ%wuQsdv@z_*;1xA%s8Oy5(Jgv1qw-N#FR92&qQ!bLE&k3=^2j|Y?EfUCr~ZTpcA$1~>o` z8p#0@25)Mho}1yvOpTyvOckIO0DFkFvZd4tFx}(`PuHQ4>FR*{DT_K)Gvu0}S|}r2 z$0Leb_6bQw<7HmH3rWw+-*b~cFP}0=kJOsdacJZUaZV$NTc@T7Lp`1mINj|IB|e37 zbXISg&6eZTl|+aSr*rq}qQeLWjuu>iDR<+Ex~Ow?d_=@}(0-zMuUj6E(Rbl==e_2D zjdEkDSsgzqmL888?iG)JZj^4eA#G1QwyEU76}U z3C&{K|6(cqW0uOe6hY1l7KF=Q77$-RAkDKg!m?z*sf(oerPG?TW_2Es{V-aV5ztBw z0TN>f^yc>FCF{?#=amX8Sr4Fj-V)3}pJ)1h+>?3)AMs}E`3BbQ<*A||yi!?Fm0-4n z@0MnHjzMb35bJnDEFFS+*zH~Rx>V6j!LVX{VKEOTWoOQkA6E{O3&CvJpMXYm(#u%C zGt&l1yNYGk#lSzJ^4K{;qI!TF`xzqO|X^X_fjt;9uS<4R@|N@#6m@}qeBQ2HEMo^NFy`|AZug}aCTiji-k zdpS>RezCH%1d?O9!ToEq47xu-`Qh~VT3t#j#lxVgwMvi_jusmBt5cLLt!}fza+CHW*3?KbHRJ9@v%%j&JyljrAYosZ%vVYQL;bn3|(E3ZQGSNP>( zlB_WGT&Ag48#N1lwexiB&_&|1=`LZH{Vw>pUo3c=M0NI>hyv1kc6oZv~iOz3cBXHXz zZ17grhmGuvnphmEcSQ3U2f)(cldLE>n+Y$}h)w z5jKE_O^&`7ELM&y$?>(x(KipXMc4rPYt%D&aiXDw4bqtgFgS6S05jkv`_)DxAr?0` z=yPCL8&AN}5e<$2@68y9cq&Htl@ZzxZ5X23LHufO za8h3ydQoz66Hs@n*{*w9W6F^)LpoDwEc8&{fv|ywW~w=+49!6Egnhil%X$%H5BYtp zj!Ddh>`8jWONv4H$f1vVNl<=gC_qUOxkqSLEkRu7t77w&p(o}A%LR1)SMRG*5g7D< z@S`tr^)EG58Kf_L5M)EvdlOw8#_OlrQ0 zn1)8p#f4Q9dG7Qmmv@q4`oZG{b*iVw4d!_8a7#TsPVlk^ALDfn z=R{M_{lpKb8WrJfUJn_+*) z^yhPnAO%A+>e3taW`^>8FLh})RSh)SgZcIoFLk+>>UHledNVcu$ViVfnBGkN zKk!oT&!(zM_vy{FVwact7cW)1TqOO3-b`Db^HQJ4rmFeB)0_ErqnG-em+BFTaeFo` z>QJchdTv!V)wD=&rbS)6)b-g^5yl;QGcB5i9iDIB&Ze3c>CLq0S}*lIFV*i2y_s)6 zVV{6+w`WsLi}Yq%)aa!;*;Laay_puBve$gO%S&ZW(N|0KW?FQ*m%1;TYFeZ>)1n$P zFP@VM&@#ThLckWTmztkVH7(McY0-6N!Wv`8UkLfR6cnDGop{dE#9%*~Qnz^87_;h8 zcq*+d%;Ho3J*zj<%2&KpNqL5KnO5q}wDK&k9mPsz3FXZncR|eeBbCE5zF@a#1Eal| zev5F}YAW7i5(fG30<$4wPzz#EpQeI+-nMIJQ`4SxxMO?^%?%BB!mcMjwr{M(ELNP3@yYsO^PREr13lTp*^LTis*_AB+6B4oY_jhm^6?t%`K!O z5^I%5{hvHs6({HLlS~KUI$)*x=X0mQtBEg)MIkZdQ&o8wRmPP=s@@Cq2bDGF%m#zE zrMxkIdG)utUwwyvm(G76Z+zStB`+P2Ng#Cv0}$0(y+IF|zW>H7AL9gh@_F7FlmfOO zbj~KZP|Gm!`jXe{A=5XGEj%1-L`Ra2ha{X-I40ztpRQ5DZY#-+izIwwCGXcmrr;01 z6>8T?er^(w&AIcNgf8}MC47nagY*!^Uq4x-hs@yq?1{vLVX{d8=U9`_?G83yVk-O& zlYsef84pN3zhAEEQvI${plDaB1MP5+VQv>%>eX&8?GktS<$<1@sg@fN&NUX@IppI~9fChENml8)k zbG8(VD|(p0tsfRiA43;kIi5OWw%siIh_9Dk{jI2KcCk>%Y`fD*p6Hohk2E~cIAILY z)J!y#dgatV=#Hl#-J>O)f~`bac$Y&9pX3J)89x&3sWwt3sqq{+u-diUX-N{1 zmtkmatSnqi*GPJ>!&E>SdYKHZG>H$?-S(I6V-Q2s#@XoyW9f6FbTmO+?s!uA5V2Si zwe+=_>6fXmZ{J0;GF^t$X_U?{iVxzqINndvW&CWOOSvB%BWJun&;8>^hm1JM5jhN; zYvad{3XVgR++{@9MUcE_AaGAbzh+lG!A49JU0FykYK8QoR!A=r(iPO5Y4|EgUlr2H z-(`_1j)RZ%Uvwp7`3)2pMQ9e%8Lg1cXoYl!keYB@^015q*9d8Rb2Czg!$-PEGHRZP%DbLMFc@|Q6c~DbAMPX|>enfEcYt2ZT?IUh)Q`5X$_63a@c_6>V@xG={ z%Xlu0_b`2W8qfXXT}Wb<}UA=OwX-rd2&YVdC4L1v2Qs2=OqO8<40_xc2{i{7lr zRxp}=MEPR&qb5I#nes(=)n9*6bm#IBGC_-eaip0E-?(?Gz8>$$yvGOdLk31B6ur3< zH1yJXPGu_jGJf&|O|r5RtYjA++<$!q@Aj-WPcJeUZl!hF%Fvs;gcqYBWaanMFcxy? z!X4sGP7I`EW6lz9%CoM!XPG*&S~QrHiMVkoK0@`T$ zru(S}oQD-Ig)-<4mKi3!OwdT#6%6H{1RV=BoL(Vlv}{T~Y)nu^@-_nC-+*(ul3(8! zRe5P;pEE*HyMo4#E1B-DGrA@!-m!EaEgz%SO?5w2v(saZ;UswzgKRz(VT+mecV_~M z+AUrCu2cfOr!%tF+4zy6JvSmm1=?7tdv=Obugz)vSJq(d zj@59cudEH1)eo^!RkXo9oEmdpMD{{7w)l~;^o3JnWn*dQ)l@oakD&>w>lE|WT^PzSSd{Jlbm#Stf zQD@-_N@x~!R+21HXU;ke4t_E*do;FCT$zVxs|QiXT|_a_vZDzqC9~K?Ut?vFIvvM? z9=4ZDi1nIq7V57_IhQO^`(DYBS@H2nPClBnD@Y65Pe{1+zB)1cRZo*L#;i_L-8&cy z^T7~PjJK*yb!)TW?({x%VY+9kyBM(PHHD>vE*MiSah;J943b`kTqgGSimWrazm6Xf z4PHD11H0;m(RYYD%d2JcD9yH#$9SVO+xzlxkI>>BBkAj=M$2wm1Swz=F?km0#mg4( zvf-;{dSZFA3gG5uQ=TxbUB0~eoBV9bHQ%UZf4Q?cr9(Dlkx4NMomWo3Y|0o?qV}El zH-EDy+fPS$DNi(~6lK4u^!j_Ehw80eepfxg$Uv4A9(Z~dd^e0NYEP{7z?-t*YcU}P;L8nePFWd}@8f~vM-rQ2DpO{9Xi=*ldKO?6kAg1)Y= z=3sH5J5FynmdX>|xn8>$atlK#uwUoJtpkU@o}|K5jA#`UEm>4=?o*~xS5J?YU0hiI zxZ6(1(lctKWy4{iVXAI?J|pQl^wum0SAwj&;2;=0rUHLhB~Upak3ffqxz$dfn@q0r zrXneh!PQ-$e;SiE_7B62Ho>~wE#|}XpzMqKTggi0dX;WgiP5rZDzQm|caw0z%d(R| zE3B3LNIc(Oq!(}W-UMJK-*KOdbe6AbPT8c9660EFQpVt;;F-7H`Pfg`;D~<4zZ(Mh5zz7fsnchh^aap9mTk)~r>WqA*Mq@Ad< zzu-N+NO`qX?g$6_E;9)yDFMIyvccE?TN#ft#=&R2jC)MRi%mif2^{K+mKEG)J{&HF zn6|yrH<*m`OhR9i@q=htmp9D&43qJh1S(0^H%znk|BdQTgS>TEUUE|c(t!T1sTZ|8le#(Zxun#L%Qj4}=}30sAstlK2> za+O&QO}Qv8Cf86Uq@tQoqewOM@f|Y{`q{!6|F^ahjFP&{&Jf7W03X>GJ5R*oT=N?g0us5KR=7)F_yz`MKQ zQ_WEL#@tU-w8sW(-AP7x0Q}--0#}5y6uavq_1F!p_}7-wE;ei29SpoX9X#$l{T#*@ z?hHL02T{8t_*t%K`WtmuIM;LI$euldUf47NS!EUClBmfGYP~K#ibzF;M$CMkdUIqj zpUVH+JaTlwc$8+3F2HM|_SFHiS7~*u;uznsB97v{!rA@oUh7(QWb@gB)FYJt??+ z_ZyXU`94Nj?=)G>F$odpiA=%&d{BacK<6T^BZoHO<%WKla`)l~LrT+T_2+TCu>1Hl zE{l;MLamiLy;c66%D)$;h8)!Nw(|dq7ZbEYeP?zKcVzp|ohsrwg}St|FPQlJtRzdj z83N=$!>{NbV%2xDZvL&FGI__2pfTyL`-}#^Krq6QP^SkX+-1?{d5)HJ-si_(mnhfy zYqId&+#+Z6OLUQlA(|OWOE+t_WV&r=*V6HkbZ-vOZs%YqhgXL=Mx^be*pEdF#{bBu2zc$XM>o<&WfnK9cFc@Kt(6%!FVgidtb+Mu=5>u*; zSDE>bPyrk_2wPFh5%Gx}Efxz2=rpadmkGZ!>nT;!_nl5Yt%5R~3v#Y~0k|fKDay;A zEubAr!rMS!nlj3A#hHb~1bm}t4aY9ciA*?46XDx^QKL^H39KdbT2g(Z|Uy^Vv8Ib~Q#uXG+O zd!b}Xj>u>ZYBpkE1|KZp9lWYz!HeN;HB`+zlau^ftn3VypH?+o9?eWX3y!m%gNHAM zd+ej5Dn?{-KZ@8}BC8wo%>E=Qwiyu9XT`!Y+N4x&bzDPQt0Hy_eGrk8uyW^Q~$B3 ze<3Gxs)v|qMRa;1%uoj%(4SQl4$eZfzMYzcp6?G-_gnQhiUV_nt2^I~+8cKNN&672 zs~dFx2AX+b_amB7R>zUv^cm0ROs9zbuKNrkyH8Tw>hnL4lp!0BfVk6d3?+)9WbP>x zwYg7GYh3Dnzomq2Pk&WSo<#xfzU_?bZv9lyDEA|twEr(Y>>n_$1CAw<$<Y`;9{VBfF{Z*nd=0}$ou|2_cn0ZTRWQt);d+wFSgBX_!Ywn4 zcLdN4)3b`p2G=4bSc%ub5KZUR@xpQK>m&Bo2)F!>PIu0aow5bT!KS9Rcym8UE^Y65 zPUMO-XSWU8Q)J|dA8*#1o5`U?|BxG2mZzt1lBwXGpLzz!l<9nYI6Y`nG(8*>ITBY^ z_<~$L!Rr*lBbZ>avkM!a^45=rc5q{Ajzd%Vlqycrzc4LVhYONMvKc`)9zw6G0nMD` z4tznRL(IJNgd*+};Qxri%Jh_Cj-yY~&B@Um!afY69x8HfQF?A6MpS!lVcFcy_{~#f zqA5pi=HBjo@R6wfRhOQy|n%+ME= z5`1(sY3|*kFTaTd1GBrl1hg^9t|%o9BmdTTMa0(GQU0S;ogGehv4zy`b|ZgQ=zEB{5Xk_>9~AIaj;CO0hk%1clBcOY4AKEigzaK^+;A@ zNf}zqH9EzOpNRft!h%Eg1zGj)j#D{u*8>7KL#Ck#~#!Ln3 zyGJwgi#hJ=h0?vBS-|z}Ae^(Ag4wl}^z02EnV^1!9Ie8U-6vDL9?)yBCRl(1#n>&DjP0nIYo^Dlt zeJTHDhW~)`%g6UMdB-c-)L(&wC%3D;>fGiyvlr)k=@s!a(S^CADs%C6EA=LL*xGxF z63A-qqKc)@iQ4l~N2^&jt58RaV(ESn1@}S1J~R-o(TRT3B5%TCi6=hAyAP>4Q~4?J z#P?U)^sPM_WrEtH19qgD+4IA&=LqI10+biJ`)U6n!&L#f?sS|#h)bFs{nQbB>Tdl& zi{~QYO6P6-3*7*@t214Xs^WO@jaRZVO2V?A#H{b5TSAHM zeV}7?xvrYoA$Imh@Zv*;aHdUhFyhgPN;bO4yB291kkRQrUrMlp;dDU4>#B#&C9SSR zB9^(;Q6XvDs>xMlH6|o&4Iyb?;T-DR{wsBsY-lD|q9Ua7S?(zfRhdgUiJP;p%B~?K z?c6@_aj9SW=Z>Z!F+zyN(#ODE^4+%(DLB~mx8Z$cj=O;}1`sB5L(5f2tmwZX(M4PPY^JjcMn-Y&*xGp?ylIew=Ve0$g$t8%7mMub(;uIyv9BldQo)>b5#eQ*TAO_i_Xn9?p;}g;H(7ccuC>V%qgwX?8_&j;WrxtFt)}%N3>o3EP zG9^DBr}e{V%@WAT5J&v#bF0B3XDnN3;VLC-XSWiCME_6*oV=Os(OYDsagTll>|eobo=W}Y>gyGLaIWLs;! zO@TA0)Vt+Y`Ukmag%M=~GoJwuo-Hp60=jNmcSf@xjBbtHocdrT(}shU^mkdKsr`ga z#O`RBVI>|i^vVpx<0{1XCTkEIN?uqHXuL(Zm%K!rp}AiKUZp^j6d7+0EZc!{lmxVeY-!~)CkBCb*F~$3(s2s zap@vNIej2|ahFsrpWE?6Ou`n+Cu}+#K4Hf;I|F&*diFRh=sYyWFcp6O6Za$kzy2Tc zQ+b9D8Bk9Cd;b6CZcF z@RjLA-E8mO$lq4H*dCBe?o)URcoC7i)C7g|wO zg~JztoC|z;-vU%i-iiH=L+`t=0l^Na9|~@rVZcUroe59R)BJ^!ujrD1xQiUFH{3W+ zZiu09>HEbzm&71M z^!-ep%jBP#zDIc;NJRE_-Ka?Ar?J6k9IZy_27ez=%c%V1h?^dnjO_-ROMs@a)PN(c z1UvQwh7&T|Aw#-g2Q9SCY?U&BP$Lxlr)VcuZ5?*0zlWvZ$crwm`Af-5x(d z|Iv}V`L7(F%Kx4ZYA7~XtQJ8vSTz8fMiE=EExxZgw6lgz8HkLG=|z?C`NKM}F23KM z7#A;)QwllD5{vZlL%EFk1lhMq(l_U7$UY&*E}1tndBfM-1;S@mRZL!hAfWLcz-_mf zJA4Hc6`tr0$}&gcf|?m!8kn<%#BfzjaqQHv>}4Cjw(ljP zKR=`8`k{RTj^{0>h!@`(*eCQ+Hh-$K1XUac{+U^vI@V+MIj8EZu}Bi%*}6?=EOeTI!mZp z5c|M;{YW4DVmWbh0)r6R3fck_c~*Tbb0^dgn?TnN_2a;3wa*DBc!Nr~k62|F5tj zLvye~c>k6z;h3oNmqT=u&uck_l0?=ZHnSIXGUOf_u5?(`Im#cwe37UF{yxJp+h6KX z$+t9z-Y0tYwTQo7Cfaox4ENDCEQpo=OfA6z8c$pPB5e~5^h@8JRr)bgI!@_p*(kEd4Jotqm}^%?u$7DtV2BCDKLBR2V$6i!)NdK$Cw7QtkKxz zv$-6oV>hUGyFbR@U}mYczVKmx&Z2xuLsE9AbN|DZ2hu94%*R+~m!8&~DC;zHe)eH0&2jmPZ_Ldt?#!83u1o zFCJMn=wE@RM|q<G|jZ*(MunaFARW%4nt1cvh zY$vtOm^=zH-IeVHM-)VoXEGx1L@S9^Hs=wt66{RPy&q`OIakdME1(!epp#!L)G($C z?Q!J$Bvg3@#pamy5Ybf)g72Gd+H0Y`DcF7gf|JsvtRVp!oX)!LY$y&#QAFJya4({kk#5h$f@j`d(>tw}+2Q#YwTGaaVmDeV!w+i_}oSXs{JlNtA>Tl6}b z*OAHE&$ibmhkQm~V+Tdx3&98Z6`W&zHw{@%Li~HL;f}7*7;7VN+P}r87ryWYhk91=^E4_O|L;*Dd;}UY2!H(}2O~!3+PNy@*BzTyrNL~&t-Bi9I*ml~$)By&C z>L5K@s6(hO#@f#ynZrwrF`Ftnq+C)dzr#rHS{H$A))iqglSy13TpOt?K-_1>gDnGz4F$`&X z>TFy2O<9>oc2u4oWa0Q&l@H{5>TmkfPk$EkCu{dJ{FVe3U+|rS*lr2tlKElycOAp# zF%}6$EARF9V-@b9Tzvgq+@vqG_kUJ2p{sGki}rl~UxCUUYDV6Ar>7H_)l~>55&REp z#dw&>nc_v91H-sm(OKh^0&fj%nxoG{dQce8#uv?#r1C_*l6x~eomPK~Zx%0ynaEJi z?lff2TvGeubY}r_Cj(p!q7rKJkAj3j`WJcK9=Qf6dkhRE_YFCrRvVtYhGQU?R{NS5 zRg8W}rQx~)2B4-ZL8R0jszXuXo9@w*Jua26hMPCCmQifV z6|R~Oqw!k+n|-d%6U$=w(nP1C8KPwTa6SSUVQl7$j83UJG|VK8%@e_Qyd>)OJ5=@| zw|pjWm+#i|-1m4!FQv%verPEa+Z`Gm+CdbJa)-`=dG(DYBi$j}%eQhK#Wv|D2EI^osaSjirSEfTo=i2YkNVi{=;yKJ2z<_7u_fg`-EZEI1r1&)nbzkvome}|; z+X6QYUCIucyAjR`@f(uheN{c`0OlW&WL5@d9ZD8$fhK;0y*qu|W~Ge7Gh$roFB@?4aY?{ksj6YrMmTw2F`?2|g50mP3nu;vt8!w6Vwe#@#F&5+KN*$t_c`S9sg9XyuZ?thZX)ry$Jv z-~40b-w(`sjFk3~|5_d1^@Kn2H=Cr(i!4f-DTtRy#oYIr!{{4Xt0G-SD}N!xF$H|j zu$#Ok_b@Qc5DR@3X3`NtANBmsE>Uq0sW(U>u(8pRNY5lbyxlR*7?_G>Fq4}olW9Y$ zH=kIE4}3vSlgE22JIniaY?W7ic5Y5;B{0CwchxY6gP(6xTl4~Fzw15@Yo(n!<)qfK zZJ72_-GIQJhedCLv|{cK%$*f?p;h_Myu#w&hldF-b9QF8-y2Z@H?@}T*%&N8dAnV% z-|P{vkM@NVAoA)Wp1Yle>E-?r&|GwI4h#u<)BnKr?rsj6a z*(1JgyL?0b%XBc%MeA6}zsBiWY`o;a6SZ4SZfuU#1o||VPG=qieXP%fK%d)7A2RRv z8GkQ4NWiKP`*0kHX5fEEw!aZW?{Jbr{NWb z*X{!;q;7G0aB68ux~kpYux9%1vjdHakR6ya4{&*(g4PS~JWUNWDqJs5=VExM&TyI! zyn0;IhG{J|joh2u$hYYyPMvfR&f){38oJMp5>u@0t1OaD!ujaCwB$Bzh%bicuWMW# ze{W0RNVomXSe??NA$xX)MekY zp+KEJ(nk&>>}cIccwN07n&|fgo9I@z{H`9@m3DMp!rnJeOQvz@o8K^1fq|(`-@5oWGPCNjdkAdI3F4M z*Nt%#{cBGt%%toFQc_BEW;6CL2DzqJaqxY35s=JSWRWF%C(!=K*q6nw=Wn0bMH=#^ z>qIf@C}QaqLuqs$(3J>ujc6z%W+O@{7^F*$_>!3C7B@UFB6wbYGfbxmjR zauOTNY^4sMxY|$b%1wNjBU>uw(1_mmtgpSNN2o6C0iRF{JuTyS31U&RwQ$R1tb>$q z$ciO{@KF;9}0`Q`vjs1swgpB}LPz6F;tMMy@0#`&+m9b54Xz#MwO;YTolJMTh)( z4=3T1PJ@rBXWFCiF*r&>s8eraHV4g(gF6B`*lhY!t4NvayeA$hVrdw@c>r~C#I=00 zxS7N05Yn~u(%@TyB)uTjifT#w`qO=A;S+1S`ct(Cp&YmNj+HD;OTtoKs$I#p^bULSe1=tdF zyuOjrF8*L#|8S8#fHmz)=Oi;i00@R~%VREPRTQ9e(hK#(Lr(g&j`JjlQBbbvZWF+n z^5%4(tg+B~oxbDj*pwU3oW5g9EH-_|)Y!D?JI;+=Z}8#G1DlpkTU)uZ10S+Tb_YJZ z>C@|_;6rn6uyRX$nV3{RUf=W`WwAr1@8}advT;TH$<$(WSh66vWF8{P#IhU?_-Znp zoR>)1ry2fzMDPp_rcbo`_@2=s_c?hqCk`Z_9Xt0_qOJ^YOL+SXxjHcG?R~)aS3eCm z47xj#`+i&Y4bVIjA2spwfJxWZIrvXpP!R2mGwwR43-N4^_s-*bQTu&jQQ5B|a%B1& zoPK@ok-;Qt5{;eNJrdgoOo})Q6U_zT&YTp2x~xqeVp!|rfP%dyCpdjd+sN26=HmTv z{AIx-MBNEUaL}g>%b(ZLrp@%thF_7^G3g=FIiZG=IxfWLr#^X$Wprq5PR^Tsm|*Wn zP>`En7Q*c1q(mB!fF91)K@2yxzFMA`@mqUp>+8AMKr>JqOL7D|Uyp`2;bq?nUNu-eeeT8uOz;XI2Y%n z_5lQ?2a{b&L65-91YIx&1UU9CkiV*eP{Uxay4*}wY8CvdnU5gznKxsn_7zS#>{HGc ziG@Ys&iIB4VrYl3uGfr%`{n!?DB8=+!*8{fB`3fY6fWoXE9CUQkYt9CGqh1X_>nkH zMXBpE^*jFpN5jI=AUGOoINp05-V7W~ok5mMz~@2eDC`~(m{mp1Y?i^q5^$Pu2z87G zY&o4cbovQL;Al)OLy{Z23KbBZefcM56%9jYiFahw)+tx|Z=OqEyhTVphfax8AHd%Goxc8!?QKsYZMYOwEANb3I(mK& zA6oKC$&S~?t+A^*;60h>g-54PJjuhqcoIqh=2%-foG|EPv0w`Id_phyEnsO zg>CC-Ka@C%kvo_MF>38Z$AW-AHv>Lf_z41kE;)nA?xl$xAc4fj&ho_DpbTtY(CJ

7Z#_B8EEWL*&nthZxq;dJqD)&+jdAJx=auzr&4VtRKyAK#5$=*kql6SpBE=0PU(TyqA z7YVdV(p*wcD+whw6-&suB_oP5p6@OH%X+*{*|iXMF^3|{sddaxRC+aGcQ_C+EvdJG zgf)8k2yvAsV+V4kxXP87fgEhP%Ffp_Z*E0|3_!=aAmW8ULi-T?(adU2Jwm5tL;_7e z8JbRKQ%^!_>@c|3S;~?+Lj+Jx4V*ah(%sBU<9x=Gft?f%I?K$Mat6|jhLaZjFw>=h znLPklQ%Cd1%;J3%3p!~ti)Kq=7L&)Mj2K&+3jwximCYypDzb%J>SaeQZPaVj!7pH& zkFgy*{5o~8Z~_@J9lV38eKHks8(M8&rMS?m_W{`UM0g6=3)*SAaQ>)Jlk&+{G7a?;=VD>=P}p}6&Zz!W;{ zLL;G$Ek3B-{p>ntL6H63EQvbycL~K;ezKQY03VW5nNHVy7S%GckdHd_%<7Zf%Y#hE zq<)F!qDXGaG6t1K(AS1h$imcJ6g7wub}#kk^*`s=Fu8l73*%rIK#3CzaH2kQlzyL|+%)_SusVg&J~GwGga5YfFve)35sdamssD zM}mq2-(yDHiWlC1bzL1!p5UFZ8FA2gfRu`W0jU@DXC8S|Pw-^-<5Q(E#m($AnOVtc zKT4vlgi`WQirWl?&|mz*ozJI9o?WQJTR{cdsa|%N6xV*I>-^u=VQ!&2BygZSFtfL*ddAb^A2--=9ZK8$df_ zX(xVUoqHyYT*sy|Df87Ors?dNjJ@7T2fE(=WJFvprs~t-Os?7n!^J;bSvN? zQXv+Vlv|}`S=UN8a?R5#&MRsL}g(bbEsHV}f+gIF-26m);B_xTbq zOuN+$WDOHKC3!gv==$8rhnKGqe~IY{5r-4GNK9iqII(3gC%T~fF#Zk4j-t$|mD|th z0>zthoy6EC(otg>b-u|pH{~{Ye=5y4GJhkWUS~eGc;~8lZD0llSyq;>me;U?^g(uf zB9-Z>H(GZm&Sjs`HqMEVu*1Z!YrCV>)FMfVQ@#@|sDewYrQ4+jBu=;nC=`cjp#x1|)-ahktm-hArM$+9mZVlB5;F)!4i ztFnn^t16Cp0Ag{wQn%FvZg1hsVSM3n$v%8|wZ&imzMf4ycVX&e8lQ)l+|bV%>z5g1 zh$+sH?yT8?21^)SBVlw1DbaC(J~#*t^of*?3-rM?O`y+xrPC=R8T35!`%LLW=J#b^ zHvL9vGw+f*Z#KV3mf3%`;#hcj9BaU9p%Lw&|FI!3yYtHu9xc_7CU$YKmG~CAH@^^y zm+YA`Oq{u{Lytk{Q@$tqJr5T(DCM&$I?Z+%8G9S2fd%$lJB4%YKVH%{lRy65Z2m;& zSEu*6GI4VHaYAc!-n!K87kd7$+rmb-wXfp*4mt;JWpr zWmxPvG-$cOk;&4%&AsGE=c0F^Fw&5CFTM)7u$?z+P$B(LPNL{G2VI$%b-^YY(&KQJ z5@^d!rMld5J|P3m<(&`)cA2^ezSs*U!x3b_)a+=o6dNZ-Ss2NER#a6}ioIX(7t(M zJOil>^nlQeC!S;~Gg^q2=r7b&Ck^b9BIXb@=ekJ#E&^S-CWcFY-Nq!le~J$V*bS8B zzCl^;+a~KmN#^ zMpQa708<1>JpS_?y*TKV=j!20qXi>W!b*iqGkI()XFO} zs}_d=TqPZCYQO~2-(nH*;TN2nx(M*Z&xllxD~O*gxB|c>BuIa(dm>qsG_BAoH*pBpZe zjGq^3K@6zg>bxh$j-Ac~fr@WuQIEtBPMYLg06Qn3g*uQ7v9&gxg@y%GRpF0!+MD9S z@Ld2;DDva8BTz}cZVwwxyJel@_BeR4rfaydb_lt{p%$pRi1ieu*O`N>68zJ_SSFB? zqd(8_gq!E&|0A<%v}Az~J8k)kxR>TIOa(`m#)$URhfjA40vXe)&ptow(9dbD2D9S7 zt4-z~x(LfObhQr%qHBcxNr3m%cW^&8e`>WDq2y@b4FM}hrHTAp4aZ#5su4`Tku|#J zelY*;?%Iw)#GxVh=`-3n>KW@lr2tk>e=f#U2MH=N^CdXR-kN`2KBP9#bfc!&&A)Wv zkwTqF09H@L8J0vVsBeoa>i5Ljivr)%6jw9f4erv4ztqE%o0V8~E85U*D7?Ra} zT3bo;@z(@ICk5)^dMo{V9WczIL$Y)Y0Ei(dMzks`bv9q}geGh%i{#>Yl;_9+y?8jiQG7&#Zn`9HB{`_84CFGp2F8#Sey&lLYct+<7p|qzr`Qp*7kGJMCS{f+!Qe_Qog~Ype*d(SR%gPtvG{#4dLVr z0TXu4D{5O7O`?ZYn8RueSB@-;zpj$IV=qOMC$YX!1U6xLlGhHF-Ov3Y?$~`}&sYsC zcSwX6mV5sYPI#M|KnklEkIjW*WHgCrgye50&~zS;d*W}{Smj>{>x(jajUh@_Wp-%B zb5JZ2NzPUqT4=q(ir(|*{b0kAH)%KY-7&^xnfz8r9r2|eQN-~PJa^s&C{br%)HxyQ zTtX0EoadBMMP$M`AdXp;Xquu02e#S^7qjRBjk&t}s@!C(P;87_G~&Xn8Fnv->IA}? zm7#<-n3Z%U4ee)EQe?tqd$MZ6Uh zU<+~P1C3(Rwwi#|onr6ds(doQX&zLh%9U%~$bfj`uY)~@)(j4S0ZtqtocgynkqTn)5qT=y}RN~CS+=Ummn z$1TRYtS%?L2r+TAgrm+uQAl@d`eF8is7^vB6@*((GRDz3Xo@Be2pgx)r|{Km#;H$? zy#0Xm$C!Rj*23ddJ)-<5dskoSFb^}A$C(M0qH9bf-agnG z#>viU&O4MB%0YEFRe2MM`D&m;x0dZ;p*IaH6vG=G$+FX?QIk5Erpq@7Ghd*(i1T%% z>LMK3G<9JlLJeyKd2y&^u^^1F_4pW^%pbD5FCK2tyE{NKKKhZ*+jU1|WC)~nPLbuTvc zk2dvBrTSUz)%P*g*Qxe(s{L{*M?W#nf?0@?@aVtb#%zjRNj(wRR?SU1|BYf=qt3sR`G?u+;}2`coXFt1y~oA7As{u&@0~~O z#oblt-otyOf9b1*0r|j^irX~}KZ`W7x4t5^h*Uvs0!@o}zob@bW~0rPT`S=RMLwWW zBHFxaPKmX>=)`gCO|ok~fp$1;e@cdo%ueo-ob~1gKb%CNzyUvrcmnbpw1kB6V6jqc zN9s4C)HpK1?TP6A(^Lii&7@-Z`y#XPNveeU1~@l9>U;+dYl9AjoOh$nrl>rWf8l*U zQCP^BSyV(zvA7tmMKn#Sp;875#T$eODwb+tFX!2c(T$11ROys6BFk+OVQ6(Qqr!NTPYtF$Q9N*jiafXNxj zJ^-XxNg7lu*95rv^SSuW=u=>_0UFkoVRFKnfxO{8U%8TfBbCGWx}E_rWdnTe$ow%# zX)@<$XMQV_`4>R$$oym`vuu&tYnCdKsX4Vr<|&!Xp9vldM(k3Paq}J-PswEbuA|mi zjmh|BRz|!Ul#w&jU#xHq+y`|Oil?(^64I$6$hq7raU}<#nII-%axXl26<{D3f%}x( zuEAjx7mRnW%I~J)uHu+TvH>1_m?65NdmO*n4nV-se3n!(CnZfl?q5%8XKaQ=lE%lN zIdADW#|C-(&75`)Rxg?^AitS(Si;n^Bujp692cSyLYld6F*>;5ug|vNv`UmLdAC>> zbJl#>)LS@&dIL>HkHcQgRFNZ?3}?*&yCpxsnM41=$$o>yB6su8N9?&4(>HKm?DYCi9!B^s z_hiw^cLPnbc7hgW@mzt%n}93R>(t@u?S#c;k$2ufpWJy`JX> z{lCgDaCeeCR`PzHXR`_>pgQH05;nd5UjU&dj5wZE+QP`5%g(hNu(nBI^ zveW3#lTYglqYyY+-e$~;+BciZuVb~a7pa*)voyI9Eb)H7>^C{7!RnnBv*z_D#;Z+ZaFJ1byoU@xXrP(9u-k|&m)KS?ly|Q1#;Ix;K4o~~b zR)}%?i=3LbzkJS%8BBvVbQdm*@sySjw|1p8k=hQ+za^V)|6Avuo-q?(AUzuP>1P(%!Rp8AALCvm~o)O_7LsSst9`QrC}lapQ*PP~@k&N+3z+MIqT<=wxh{Kxkky}!uR+Ce9fKD01aA!lVK2d&ouuv1~3g1-W}2W_=g3cv_z4S(;eQ35iBQ{gX9`ZxCM;}##|A(%7Y&7x3>5tl$rMG6yjlDna*o~NR z)WE}dox#8IPu8yqy5LzQuunDny;#eVK;vikGU3QeXD{O+oE*ut+*lT>{48+GV!_~c zKbvcwPw-LCdLZ(|Lwv;Ckj-j&;Fd>t8Rh1k5?;{Sx!xVLH}Uh7D;^<7CzE3aIqKcm z-b|NGDkrHkK~AUrQJ*=;+0O(l*9N%!hB4fJjpOPHVcVr+sVnxKC}7w<3uQ3GoT&5# z+dgIxyYgf%`guWzj+w}C+zaX7bqp;58^M|P(<|qlUEaLMda(23_rLWX`kzD*6Q!w- zc*fUY3CuUKfR_d4YLmw=W=+)1`#EgFDqhYN1Y(pOfm<9i5mlp zQJk1kqRZ&ESn=6rFhw-lS|z__{WGkwH5yXxvrQ30AL4mbKkvWkL+~`jr>B+T>g?3h z#zZQwEQp7U06Z3q7{5|GVN>y@J2h0FyzFb5-w@+u((V!#$3-3PVE18G<%T+TK&0~W zg4jOo(`vLx;+w_freA{nXvH6kC)H!P6_#WXtBOz8cp*o+9K%|f9xNs7`uO#FWFZ=} zFS{&3r*r%@N&KS3&%eY#ALBKAA>JN61nRBa8Mj6qj%u(toZay2NIT%AgVg?-Rzbn~ zh7Q_NSz~Y7cYB=4e)xZ^XD5B9+BR;a7^^88#;mp-2+S|P}P*0Vg#ps|mimE&>VnBE}f3BQCA z3xsgdfV_294xEc~CWODozm;r8P4(~?Swju1pJermrX*DGWKzrxlUo=yCC^9Dmj5S` zajM36?`=!oU`yf#a}r5LbZUeZ$+KuPzZ)g-gh|O>V@Z~3c(_G2C{%K)-{y>+0wT~P zAyrDJv=H)42ILd&B|{LN5W--Uc!MkZSg^*fl=F5073Wy zr?1tS{EhrX-09M3g?wxdx$|DoBLW1s-ix@WEDJfOKts-&A9cbdA@}NsFy1Ct{p^T) z^F%6-xPKa}ALp!3Lo>c_R`br^rkkJo@e!wfH2DXIDtE-6=N`uhw(v2>NcXsWcWmNYqPf+7=8LyK z&D(qn&5z|zf@g%CHlN9WfRt}w!#Wv)a*;~Pb4)UtlkS2GC8BvS;T&Hg4c|tb*3g1h zbm`f4IQ)Kzlhi?P0k}I&*xiRmd1iO(+G^c%nfe16*xQ@=cvr;jzPJQ9!p_|3)E2>^ zX&?T3f5w8@ye>#}-V%B&n%pajiIJQhMe)BGb>>P^hj+&^esY3+*e+!<6>h*|+m145 zXrOVwJJ3w}ahyMuU6qPjE1p8Q!^G4peNxnUm#rc%+WEX98QWLxhz+QByS!IyKsn90 z=m@sd6(AMMi8j0)cA_{QtShD-D!K-t2ej_3rhES$Tzw(`Ac&xKp+NL3e*WX{^m zlb3s)^(Uf}dsq%C8*XGLeZGKdygT&GI8BK4MB$uK8g?rK0_VK*Z@Qk4oB4{{bx2v~ zU25`5!z|Xz=53p2^L}9RDuhAfcA3IqxHW{#u$9W}SNyu4`7dNP+PRG{k#}r{mLn7c ziBj?nXzAnd{7`j1FkU-|byl6y5^0VN=E{+|EL^iwEl&m?dmOGLCj`AQCV3fsMYr&? zRuDCrw;;qdJ^Yrl(=M$G&j&)^q%0eQ5k-O((#Q}_)%6m`aK4!aX)N8Nc^a8dUzEjZT8r{Ni1vl+qJ)6LIB zBz2>So<4SPv~qzK+;FqtE=MwKe5gx(-|M-6xH7*xK=E7H zQ@|yv^Hf=&4`xt(BBe#~ZXDik0whK_z1q&`RnUPe^N-B&&brp3>7g_x*+$4&XH=7u zU1o)0G~jx|AGSvJd7B-qAkOVz_1y~n?esWh4$3LLD9oqFU*ox}U(v=}1$Fh0f9KmS zt^~x*y{~VHMP&<(zKzjf)5W~u=^3lgd@&CEI}}l*yt>TVUtg2GDh#w}5Xkl+l|w*)VH0p06rTLB5JoMF&?SUsDr`my@FY* z>Pb}4Fom<$1dTtNX?%Ki<1q@h_qC(-*v!;Dq_m%p5YAsp+d8V1cppTVO`;aS$)GDdshF6fql91$xP;n}=>OxV;0SEd7JhZ7 zcBdq_IRWPjcFEKd>ay}h(I_V^bS-UTH`j(KJ(Wqo2CmPyM4!J;j#2(ocQKPmS2r?l$!d zKlKhj_4hXQlE0aDPw-PG`Kd)V)e9J~2fWb3Ky{{{`r{k}^Y@!g+1YDsYL%b*kxlLL z8&h^*p94(UUViGUCbj>@gH38*Gazg6zU=)%z!VXTJ8bc1x0}k}e#BP(g`YafrUqUx zsed}#4p+0EdZtZ%QG1nJk@f-mi=SF$Q$H*=O(*}<-2m3;r}na`@r|bJ)st-MC4Oof z22V`WCx@7_^^4A%ch?HACvm`7j4;Pe(I$*^~;_Hsvj@zWqR~CKXrsnUHFqJJ2l@zb-SP1 z-=@}yV7e6xF0o~=^-}{jwIyxJ*7z`w^;3Vi#f-*Mok84+un+U`e(Hxdb;2%FcHl*} zM?L-2S8VFDc8)fkX?wK$Dcj#aE7ho?JU~cpEroCVl&Q8{1v?jQP8elpV2Pi4zD+%& zher`@BkuCk9K6yT$v#5Q&Nx0w%9GH)pZe(=YLqN^thx&-xi?e3p0EXnV@< zP$>8HwS4i>zfY@m78{B+V|}qUNXgH=GtK4)pCVBPDqKK1W0xpYplLkMT#vTAD~@ac z?cpRr9br(0Q7YrM%M~zOOa=t8v)DKa(k`cbtLr=Vgp0{XFc(Z!#s|076%qPyDY>{n zOn1x3+k#t%>-#gN^eSW-@`m}lCq&z=hlfVhVZu&}+bw+3OnB2UQ?ooSG%mVXjkLgF zpO_PWK^$vrw1oZQ7GmYz2;j+Baehoz%Mp(~U6Z-V`zI|SLg;IaG+WfK8H+Sj5odE2 z$BS%&jb8qB(~qY9Of9J^^ygCkWKnzZdnYrkIvhf+)f70 zOQq%%yjZq#Vx1$&3%M|cQ@K91!MbkLtaHp1f3)<5IjU!Ak zFv$2PIgmgJ5Xq(i`YMCF#-q&>;}`37RtE=Te2P_7zi>G+{V!7*Ewp3lI5s(WFa)jnX>e3Qx{d2{D5BJifnh;)yfqtP#3zp-zL@^0q8*Na6~< z&_{ek>Huo(72seNKG~bi@!aG5VOnDf;v##YcU?#rw%EJX+%8$azKstn9eizk&SZq| zthnW$A|Wl_XOG$5CrE*Px&BUo7dIGr^m@m3*hR2dBAYGVqjiEX z`y8IfFspwDt68&XU#7rYn~V1DW=Zpn3#dIGh4|cHuI=z+I*Vb>WU#3~7q< zzozVmVZZJ`tzX~|rUiB2R27(ifGyBW0c-k~8R#SY0>^grp{q(f?w6Qjn#i7-^(Wgg z%IoOFvYCwQTRIK%Z<_Zx7Qh{p@%BAXz0dALaGdQ!jj7yh;=7v2S#q#g3^@_XAkiQ{ z=K<}I-%Uoq=t5)O#G$_idoLe`?;gy6eJ1(eYHKUX@lM(BYly!dZQ+;j*#jt@_V#uJRgZy^pbbmJMNAs53N)~*8gcG`jJ08*1f1c{avw7o z8Xtd0`Q{#^Ab5eMS%z=ggNW*R3w#gV*_cio4anaF^lj4Z8Zs5I367-nVHab zQm+!5>9xdb;gzxv(T+Zpd5_mfUqsIXy}yzu9aCP=iZU@r>fs&iHZX$|r-))cp!ri) zEsRK1e@_RH;pER1wEW}ocK^}9-p#`P4B5QlDcioDM`Q(@zZf1DncnR?`Xe45?3EZW zd)=W|B+8+NDzDId>T!krR1(5}%MLRTg_98Z%V$)mq0(DtK2_A(TmwT5CwL#3PyOrc zr@^5H{2n&*4dGLzEm;$$ z*J#p0SM#;-9+Pp_Z1c6!+sRmllgA>&o~JKXnR2d~Fzv}7G<7!HTbatZV3>`mX;&&eN(?oYD0Wh}oChreXzmvWBY8+$RaS;fqA9f=vY)2?4)8IS% z=g3ZTXKYOQ*0w(nv@OPJp}M~%iLqbjF!ltuQQUa*%V_glqPZV&0jzUN&19<1X_-D< zKm0S8(6wef9-ZCl@%MrHU(aG0JNf^1uY-5pU8l$00MCd<6OL3U*6d+na4 z2NbeB9#r)XegWsNT%E|pOUPmDGvQbiJLFapLe9bIRMv5A-e~W5(~BO5+}kbZO$F+J zf_LIs`X6rLLp6r4KIyjpjE{G|$(lcyH)H@-zt-)sKe@SF&CgX0gUB+PDhlotV_n$B zmRR0=O4NA=0RDZCsqD2s_0RFJu+rE)%QH|GGaPmyGGD%3p7Z~{L4U*;^y`+ByQyr0 ze(s0}y9c6A3(Q(}BXq!7h6Et&d>Lw4z>;WtT`PfG8*f7`V+=avFRAU-Mz9uNzrN$V zY`qgWTY{tg3MU;-WnpKjts=1${q{ZTkdR>hB~b44X2~NG)y}OfFBI+E9*b^2G-pZv z()nTp=Etv?aK)9AuE2osVlr5G^5;wxp7dBt7Jojn>>Cs7;nZ-6b(!f99=)~%8aJpP zI6Ol@2M600xTCqIWHEZrTEnleQddn#!j*x!`%)_8jwCwfjlDyJ)+9$@)=vKVAqmIK z><|e__!+@o$P>kbTo5~3%rHPCWKFUoTVSTVdWWuCLd)qKF_?)(IC%KAKiM#nA~;JFlD*tfI2to$(jRu*? zpXdez(O((O$sA)!Z{pG22LGh4WWp+D`VdC1y7AboKFA|f)FD(JM7L~SkgKF%)s%jL zS$_deXZKL|)O7`s+%JO8m@?B-YeK}8*8achXOwt{|3^Oqjos;ZRyU4^Qe<`gf=-x9 zHEswr{sMZ+y-Asj(aPX(UVI;RFQT0jJCm}yz^liOboy;ELAz2PklCzHLJgw*9LCXI zxX8Mp>7eDpH+fNIxn4|N#2{Q?M4yULWUEoI3wKBkmU=hI{ikG^_tt267#V5P0YIM0 zxgne3Ksa*UN{u&L=3h0F#BEcUpT-jz(s1%*_P5daR~%miF%^4i10P};!`=`<_n1|$ zN94L}3Ai1)Yz5}3;Y{#DqZ3qDc|mEcrp~zlFBb}ac2q`95+L{IIsqG86qU zn)^NZh<+ICN6cO+_z1gFCjxggc`2qZbxdurAQjcvCaRzNn59M-7T}Uqj~D~>7kaPWi*_V zGWMA5c;)Rp-bbj2(|;5NQa?hQlKHprXihxZX;OT0cFPo(J?fDh%)3neMA2ot~uhIE~}5Z9d6|@(R8@w zUU^Ed?#(wba&z=K+5bUk&6@*gO`Io8<2Lv^GeA8Vpw~4`2(~FUZ3;W%@xwtP_tDaU zd}ej_`Xe8M({~OaB55OT9Oj7>N}Ju5<3vrmD(yr$^GynrfW_w}jin{jlH(mabbA|9 zQ!H~zPRtuvvMTaCbs5U|?G(!9w!`tsAA#dX%23a{Ji{rScN@J_em>1t@-g&ObNnO- zn^p8arTyI$t(bj_cz|k0pJJwHMQ^I%)3y3^f&H}mPv%pDK8@s)C)1-^NE&(pg;Bm5 znotYT>#6pVEMaOGr0kn`&T4zynS86VV4un}V82P_-ga9gsN@@HLDg)Ttlm`|wx2}6 z)kiAnFa|JlBeYb>Fov2NqYWbEhIv$tH3An(TgPf}^E5>{-fQ7KMqm|b-Yalvb{^i< z>$O0@*!RpK;1M?Q*#EyI!AMV5@Xyo>?{cPN#P?Vca(csVXpjFy+dWpZ^T*f<8#pLkn_HI4EOmd zv~4+-Hid28M;a6VN@_c^uWhzz84pXu0roykyqwxpzeBVc47}MBi^0W0#2N>yApc4}hh@v-g{Xlv9VbCXv^0VSMhKn&6GsFVKj zxoDrSHC|W^5|#{ePgECn0Bp!Tv&}0Q41Brip-b|ra?Bove~xEuZg2ewq`4K3__eRG zucx13p?hDiPX7V3S*{E}d(P8BSF_^Wiu>jn%uD@&8Ct}W)aoG1@2;iLj73`NnVeUX z^K9^B_v!s}eD&?%`Kfu7Nj*79GrbNN7EGd^1(}s(Xa8A!dy+1vLMSdtpE;TT6qPV@ zxY%=h6sCae_#C{1xz*(~k~)X&HJb*J|AHAuX45ZW3bAm1d``pTydO$z%$3>3Muk3& z?KOSK(F5W?EXg}sMQU?$rk^mtUO|lsG;X8s8u0RFQ#kDWTb|!yhYg*6L+`c$NKr`- z&-le`M6=fNvZS`KZ2;M9A#8jO`l8z^o_8TJPS#6%Hc4+_X2Db;4vF zVFVP}ol*a1tiKDiBDjz};Bs4aCfQk%P480;8%42yT{E4kZ+{z`cIHAwY=5|qtR8#` zgZdJCxEAt*lrp@vvy3yzJTbMi)sa|!YV&daF*^O9c7E_=w)sP6vH=7dmy!!w{{6)w z|C#fL(%YI$`b#!_-_x?wL+&2>fxQ-JOYP7P{O5-n1*0F>NlE{ge&B0x>*4>_4;%`# zNMX#rFjP$^D<+Y1PyN7)`Ld^e;Q!YOf$0_Mkx>X7*!*7#fgF6@>L*`@_mQ=$gG2&a^#(IYB?m{Dd}wUTH$Z(5PKtSi~WX|qK@-r z>RpOt9pIr43lK&L2aoV|R1aF&TmFgFSdYh)mW70>=f$DTuvTaDC5%OfGccWJlui>~F@DZEoEKy65z!65+v}l^)o_tv<9qa_9R80o zYGeQuVu(5EX8{jAP>jkFb^1JYnjVakfe8bC^E^!zE=_Prn7`L3TpNv5tdE{veQIym zgGg!5x~8RZ{KYLI=r8BFdnd8KaFchZCZDd!UH7P0EPuY(L z0p+Yj9yAq8V>wuq-vGA1aSoy*F+R7HKE#&@DUwNhuOc2i?|k9q*&4>=ea6!ZvO70U zd5r!WD-Tiq@GRpI=g2LL8V$=oO0M~UZ;NQM0d@Te#bM0ON#xLUO$pB=i>=SWMj-$$Mms(P%Pk?gZEWeD4h8D#K7DM$jHR| zj+5Opa7OTL0N9KqC#Z;vRR}(uLV?Dg*xoT5%N7*J=I(&#_oPjdoIg`%o($nZ*&hW>`8oQO z>F~3B;snu`mLsO+N0nzaEf1#U!0ikD?#?hRYyAgi|FwXYIU&&UZTvOfPmFGJ)IF~x zQh82k=-L|%pZgDC3$~vdw_}^RQQ?G}F0h^NYe2B~B`2BA=Rf%^wjh;|A-ogu1@0eL zb1#L>pwoRLmcPafy5E1Gzy~9?C1JPA#!zLXG&Je9jJz2yBI5$?X=YyR3N++SVu$e> zsd2^(9_LEoz{u@kw_hsk_WW6kc5DcfbF3-X1QH4glc|q2P(sK+hHazq@D*`>l9pAU zjgl(Z3C8UICrIXR0!;(azTk8)yhVITkFx8eINiOpHhC5zorQR7JFB>^>fpfKW09vT zey=g_s{AC-bcv37Rn?P*%e&E;ylgdAq9uXG-;*_xob?W)ZDdBj0GfP(u@Df-a(D^0 zEn^nr3aQQ-VNY8a!HZSn_25S|*#!~MrNwZ*AoxbDM>oIf8)4co#`-Puq440@y$zmM zzAGHJV*Den0o&w2;V@N0Ou?J-nNK-ODhXF^xTaskjkiVn5Ls{|-a}8y4R8B`wJ96a zoba|!VRe6!A?e~A?>B6pQG&b3`$Y&ZQ)Pi!Pa}nhH2AJ$4d!CkKQ@YdBhk_+ykK2k z&>e;=(l>^P3e-FI4S9iI#}Wwn`kGsg17*>Kv7tVAGYK>)j7unCeV~Dtd71*b?g=NR zlz?_mnJMWXn|NdnWYQ3=)o5TD!W5XB)`Eq6ph5VCAxq&IZ%>v>63t4z8T*S!%%WA{ z8H-E?$eAG15~vXY!ZiU-kks#ECHS+Clad;{IyC!Tf!+5&8DQK8xwRtUjOVT*!KaRLWB=c*UAWj+neAgyB+P z?&~zhvgYwV7BSXr--|xDy@t+2xhyfEIF}pnBfkPElAS~DM2PhuehbTjTy zh66?F^oe`u-br4?S_xmQJ@S)4<1#aUe-fevx4o^6D9~sa@#H;YMOP(RU(N1@HfWJX zZq9?6x5_ESv7x#n2sF;M?2D3xS2UWYly|2|H%t@_tv62`_a+6JD4?mKT3nvkkbT#tNHCHgZ z5{mE_AiO_(uoq=y|L6|S&H zFgK@|96g>Thc;Col?-F}vZm@qnl$rc_pm}i|Et~eFRWztuOWBH~_>HrMnb9ONx&HpSX<5n`XT znQ}wAhZ5fxg)2Xae;9R^m>nma_-=R9`Eitc4!g{@RrPM3i5uG3l|F@&Twx=l?~JW! zDu)kZgC?5|A~Sm#gn@sV227lV&hyw>(1Y(Oc82)Uw%C*_1%>xOcP zSkNgjYw=~esKsg0Z>U;a#t{%;u<79$y>k**iazz8xyGkYAy)f-k;;aGK;t+x(saT& zc&SjOFLtJ_fyS{^A&{Jl81Z(mY0?vJ10pz!RGP}OXe#EOsg1@ROs}rAm#Wq=qpJFq z8g}BQ=S9!P@TY_4*Z3AOQiHD2O6Ah<11*us!DLtV;jAHUy5HO@;D1#Mmv9a#`CQ=u zcHGMT9MAT`g=?Vc-9kWjh%evfeX|_5U5}H+$o~n71~(_}b>>y#WXwtkC$EO8Uk^98 zfpV;bi43E^61vh|%a}FFWsGrnY8bPSivo&WQ)`squc=siJ5D*$fu@A5s&_|KvV96P zzF;yhW+``1%X%?1X;JDveS;qwo2p`tEsbA40a5{3Nj<9Ru=_d8=tR!Efsk`(#KFJi zwm{TvLKq$k2p($6G5~Z-u7WM_rr+uX=s6$7n<m>7HoiXT%2osvdAO7oVmx&Hy+z@ufSo3fQ*MAdqg0R##82%_-0+QnyEMi zFVkmj=4C~0pzD~TEjYaEq%3%(DpGcl7tm9L$kLUixwV0=^_%%K%;etx*SVqT%Duu+ zq1DQLmU54Q0)2t{E3nc0@sz5e4Opu6HEQFc(i(thGljlUp$nS>U8k5N&U*su>Se0d z=Gdwn*DvGGH3LQaBBdz0TzN_>RV>Z*J5%3EqxyWIp|0GLkzh$`vx*Ph9O&9$fNGim zszZFmX`wH;Zg+S>3v|BAP6&Gx%h1OD2AjBAKRV=;N1aZoo_q-6nkL+^CFJ}NTJU8a zgrUna`UaMCY2~*LjfDZ6VY@W*GQvHzkmJZlfjO`7rh>6#BfHjutZ3-0*cp@yHr&ergP@A5__8wY7{Mke1F)XFdH zX}^)l`vx`hJi;BaccgM{z`X}1oTxCd{YSVxJJ(m;H^>Z(o8Yr1;g)p}5!>+2-F~O9 z_*KJ$X_!r5cevpP)b5Den^#Zp8?{^yi`>dD0<2j7k;#`!n|Z+%z)=cEKIbdOGl(;u z*#1#B?|_k&jfM{o%=wqb84tp9C(w5KCTwh`Uj(BkMxQBjRD~o7Mz#0Y$4GJePkVkp z_|G9X+}D7C?b z#qoG4mU~hG=0ZGT`P%N|`$)!L^Lo-B84l?QSK*-ShFdh{EU4=_pEUjfvZOo4#`8p< z-2VS)5NH*5Op+8%P6;-61+$2nbRx;7GaP}Qf)mNO`i;0&HWYH-up*bNvx*4c=m>|W zhv@wnv@vn;0TQ4r62Rb|i%%~u#L6Lz||39dgz4S89{|872 zfMho8>}9C*o;43zD>ar?6{^|_ZSwr8PtR2S^2Hi(s>-NK#8fNfU*grUlebp0@IR}b zW~#QbiKoOb1NF{@KGbKLsx8#4Sjokxnx{b5(d|`-kbw*gG_F~1_nN_}6Z~pk&Q!C% zsRn5j5T0XWN&{}hosb(ifOp*ftFq5WC5Nn+A3=Nx+rfp|@YZrMkhy`~$FKP8OvSH( zy9OvK21<2QF^g#_Z~*UtuH%XbZI64?A)h14%&OJNs`VvfYHi4>6*${#KbW_i=2DkJ}*L^I$etvsxwfclfjU%pBP`f^`5nTqcA52onJbvcu!EoJ z@59?Zj^=(Vf2#{N<%K7#-5zx(!F1%ALXlj~@CyU?qk@FW&jZc11q%X=GdQD%6T9F! zCh^4TK=#sOb7=X-u=6vK=0cG3aGxKz{DkM&2+y%3H}M@S`611+E~PKhxiwa9c@DPi zK$yb^;TJ7yp)Gu=VLRA?>o;4rg2&+*USgBad3kKqK_=gZ@A$q(oI}0X4mcZu1w=3%!_D^#2a@}&WtL?S zs{}`~EYS7*fnrwbOT>^2ZMD5=??ip8dScQ}F+9ir0$&g{U16AxEoK_SiLZA@C+uVj zFI$rn&HW`>xg~}m9Zf!oUmX15I)jK)kdcVLQL5pNxVQfod6v`>@@(be5u&_l6a8Q0 z`6mxlYT(9cTgW{xH-vYZ5C_UovazSO%ur?HkY;`Z?yc~!siJYv0=Mw|Q}F`14NEGE zl%tan}B1ay~+X2Y{&E+tY|sw9_vC*y9= zS)A%(z|$gn=otfV0Ur|YwWW4x1l;OtCh7)DVu_qJUEr*E(pf6N_LWMUD}KjHo0`55 z5q1mD8p9y$#UNO5MCv;nd>S5VyD<>@Fq*tqq8}57q!X2d<-_F=lqPy^>2iT;fS4W|xM z>=yFYO^ipHSum6C*;|c`QYXd1U4M9p8_25pl`yV!a)wf#5DDqo^3q) zE9gh+eui;mGVg0>&(}zuWxyzpwV&`=8WGoht#kL#gVEafy`gwm?jVJb&urYmFgUu+ z3NbkB=HbdklfF)E5lD zlGv~Q4KI%5G+x2>TGeaIKqLsaH(Po7c>)86b7xyuT8SN-N!I-3+-vVN%{ z9W|fp?)S2!6ZjK3J!mclKOYAhCeMB8=lbHg502T~yerH}a%eHKMQ3Pn6|CX=T3wR4 zR^Esd=l38V(u_j~088Uxy!hh5Sg1oZ7#mXS{Ajg>aLZeje-xSNR;-6ydD-g&ZcZ~! z8SQ;&7xf3Hfbusu9CyXbhZJ1mFT3>vnFNysB@pT^XQB~>RPpCnN6QmdwG)PboaY2m|LoksE}7m;pQ~$fMmwcS(CNAAHe&TPiu#5lmUOuD%}MALd!b#+cU86ZvCVq zuQfh${X*!22xZ?BfC^!lb1 zoDEvhvLFZD6n~_!xUY4iOC8KOlAIcrSU9sLRgbrBE8e;%js;=2!AL|PZc#Q;MEpP` z)+2As^q&70&Hu3V^K`rr(|O`7Sj_$mir?ichU6s=B6__h0OGex@q0Z3C^&QTC4XS_ zfcD*kb!((X&~g>z+jNF5koAh*>t;L7XUbvx6`d+85tu%=py8qF)REh0=`r-!=u!e%k1VYj1Red2lIC9^ibt3SsNTOBxskzh@WJJ>rH_;8qveL2#x)+R zI)$p1voG;M_^q{f?iPx3ZnX8zeb|KMuV2gS^!f-BvR9*Uz_!)z#XmU+vd(a6ImoiS z<)bC(`Ff-Fa8YR_Wgs+yX+6o0R*`q~S-*mx0sW>relP|Byqe{3$aihdh~Xd2l@A^; z1Wd}Xv{3AFEd%Fe?sg#@Np9;yAyMR7OlCaWuiovafKC>a5{f-JRVzwX$BN3Y_mkW9 z8Wq4!r!5T_zI}nvImv1})A0_gRe3O-bJ!nj^!_oO&)whn*;19N?BTSC59the=Us+lbijAVod%iF`cojGmfe2@Y*oPsilLJ+` z`8g`Xdvo$(<;0ti*$oY>o5AI;LKdP~NRQ^ON2RYzDT|;k>TJpR_I@(EKlji9FKx#L zG1euTJf8yy8ykCDe9OQzqvXh)`S0;-kPmpC@;a78^NH>4Fb`)~7jkN{FINi(uOs(v zN>|Y1B`M+9kPg^MnVS7TBvrc>7Xn&9aZC3!zM1`9{?WWExj=j%8#c$`i2a;&u|;_e zUn0oKuisdp;}8ei{5zo~*5U_VZ?{?|>!8fjpwP3h*1C!0K!s|uillAgS}JiClnaEz zWJWfOiNC~fxb&a^yqLz&dD6NYYnWT@y|&4y#-;ZJxw$Mdi+eBygoz+i*hh8z@H?T{@YFfYr6` zZaEA^wu!sfB_^#^E>q6Gc#%YpNRllMvUmpLk8x5u7ssMxUe{etI!3B(MmtS^#uOR4 zj9a#F8-&D7bO9Ok$z$`jqsJ(_J`s08rs471ZEGw^uV5s9BVZqsI`|N85F{>?e3eP6 z7ir3gy{PBE03&YGT@}!&?Dse5mf>-i#4#d72XI`vz&X^LYuVZA%7OJaGy)P`d|OMh zE_s6>Y4=VjD<^kZKew+adO5vWPT^2o@dN&B{$Z0VRd&c8iU)@QdFP#qpE2MF638N^ zRyjS{8X}N86gyRek)utCXeGb9AA(vOoZS7Kr80a+qI829h}SHiBvIe$b=h4vIIl7- zXXs?jNbCaWcn|ERZ4ir3d4-Q%TFwvo1-KTjQ}yI&P%!Cqt~Q6J>#gx`sBxvY4+fZo zmDuIXT)WLe=B5OQ*u54y)@=FoH@)wlXuB`8^%kwi&IfAn$kdC-netl1*e8O^B9)@) zx4xloHmgSRV6c^Y33P;xe($|)a9+!m-f3kW^I9%P@$@_Mkw1cIIsCBr-(z9OTxp=W zCOhuJQ#StBZU?lZyg}HsMZtTuy9l^g3f1gukODWqWnEQO&vX2K4iPwS&4Q_d_`PSpBX z1OG=9gUuj55{?8@@22}{7>B%O1XmGKlbMwFbtigDbll5k#zLNF?XbS9y*;v%i}!EK z+f1KIR{DPh9*GZuoHw?&v7Utosebd|cB@B~Fr_^IpCCK)_y5LUp&dsY*LdSOpql0Y z-LeDU`tSC6eiI+E)7#9SHVljON@i89?8rX%L?Uk{k_#H!KlIM)1$&sjGLjkrN|$}rfmBViUN82Jr?_tV%TVer|4bP|e-f`8Jy(-|vU0%`f-z8p$9pN9HDXRv zG$eBKaC@$IVNh1%%08^1?6jb4s4MHVc0istJ}B$s$~L@BS!GaG;L7&AoVtU8vY(H( zKF(N3S+AgMtt)$s!?9j|Q1-GbYsVol@4LO+_#SsQ056U{Yvcm|Y_67uHKa4jRTt--~*F7lv*pk&I6Y1lX1!Z}zY$G97UcaF1+j@gRJ0iBc z!l3MZS9Sn9bl#S}4hGM;vaKKS-B&@`gRX4GDSY=vPdB4u|4 zWeu+E48r}rYl5Wy4+BpN`|ZsXP(Wwk+Bkt;j$ zRq754%6{dXHMF(i7{2QplzFafB>JD%B`AB_m0gaL*86WC2ZLppeHn($? zZ^>5P+Cy!x@hXGLlU(JcWmfq|drQxCRL*ymr)Mj_8C1>-Di^!T%a5e8#@k4(PF9Oy zp41h9?31lLj%W3}Vv-y7M;sq#&Og^$&l7fX+pOfna1Ef<`KvQ?KA6|4d|6QWCRcgi z@m6^uY6b()DQVd!2C#dxl~1?VYrIHM`D9o5^LndXmabe!=xWn5}%XyH|7P*?f- zaaK7&ZZKQ!93&Z;t$c>PCLiepH|L+o3c{Qxs`77;q==8b{A}gCpz=*Y<;Ps*kn8#W zlYWaN&!MaZ09(v!A<4->eP1u|egZ z#=ALpb(QybmHVVC=OD>$*~;631lErQm0#r~K1j0Qi2sEob2@`0ALG{!)_E4sTIUOc z%2&F|cSmiVl~-E<8@Zh1=4|Eqpyv@m<%zEHX|8f-d)t7x(l8dsH^Mamt4J!Y^RbGq6%Tm4BAz0@ePEwhz z{D1a(jrZYLH|GtOCt=7-j`c1~v!NVLl9#P~y1icG{XMAssH;5L_55@DpywP;vSL{V zVDH##{H;Od*{*WL_58DOxVA`gSx)7E0rn3nSGdX@T;(fW8y27A56J2R*p zbCoOA^KZn>VzN(rf5}#!XW6LNcz*~gk9L*UV(rlLOq$*iNhW10M}nR=un8nRY$sRw zDW}A}M@CEr&_*XI&Q^ZhUa#>U4JyA#?866tNehg{8gG6 zQ|rAmfRym)0(&nP z)d~Cx+?pf!8Crf-QkrQJN>WmbqHx#xxIAYdwJ?p3ZV-_h)t0 zA&C}kn}k5`$_cnP@?5GT{48TH#r2cw*m;Tm6y~*~O#&{+sH(GQa)q(Sfm@%hzp;lY zL+$j!C)PRVPLgFmmkO5f-wqudJfE>Ku4Es9U8J&4?DhKUV3VX_rZ<=m(>BtVWLh`_ z$5`P~`;5@8b!P}WZzmabB=LSU@eMFNW0;0f!G%P)pt?>yk?c(k2L*uborT;G^_$}o zJCCDYf&ceL|4L;_G4~lE5)9Wxo+&Xbp0t9-r~Oy>fZcelOYc@~?k*$ElEa(>%dqSe z5|&=`efgHDZSAj_Kg%{ws&c)OW1f6mX7S_Ow&Ugfi998|e1O?_w;igN9bZu;!Si$X zuKVU)^1Oxa-CgN-tL$A_&rQ1kjLOBd4H(*6>m9ESK8lZ24>$5N*+QcfM z(JR_B)QzwN0yKY1((C6hADpMXqtgMs*M7?;CP;!=i)nR7-kWu@;XEx&a>yRLXy$AO zjF!Qi>^OKN2RcaP72By3&&{}p1m&HubZo7xa`Y`~ZoV6$}T0W7CBNlx9g zsZ}FgJS(3jG}p~tc$2F9|i944A-=tvuU9ImuXEsLj6 z>h^39jNUK5(%`C2e1dQOdkx($6lHR3mc`$rz_lIQ3J~4N>pg`ZSTW`#hZ$3Ih20YW zkfvTSWT!&y^(pLwhq|tMxPdpQ<80!cGmRx_C=4ej*K5B&zKwMxn(l17ZLt%b)g|}V zd8OD^Ug)N2+{0|~ysgs>c$1ff7Jo!r{9U*J(rT!Q*e-xhYxzS(YfB6eS3rAsf}5N- zr8{~}BstQ1gF}q|T-wc{$R*JP?rkyozQO_pdk})=oiA)uFj`Hr?9v6~#}v*j7uky+ z37Q0W5y48VX{&Cjto&qWAzN$10nzdyKjK6V#V-M1%|o81JfF-xt2@3&DV(d-+i&Y8 z(^KZFx#s7>wxjK?JZ6*GCKMYd6edeON7sdt*Z!49n7|Q=xlxi&t6PsDj+TvE zxFM@&e)Kiotg8(^vF>Vu^}Xk(O|5L%llg5cwE2Z%NAVmj?^tMav{Mg5-sc@#F+=s`>W=HngLxhSrm|46V#@ui zt^L^r)f*Sw!(y>NG=n5Zr0*!Q1D3~&oab-NgA;@npRYHmvrB_aUF*H;HbP5W2H(6s zpG>x)#oL9HY>fp;unN6}gY(D;#n!Q|z|_?i90%X@ZZvfy5{cb*{X;*@X443oCS2lt z;y=<(zyEZkwd6tJ)FteTRv6jsCqhc~sz;GiQ0G9AU`d7qPiHt85-c$i{8@A=B0mf) z5(!S?=#=(1)ae-_eBo~-za>I0iun7;@w(KA3Wz^E2&z-Azxi7oaUR6SWNWe>ZC7=T zH&rCRinJl3&^2f?!oOng2$4Op)ST^#mX^R-{&66i=>grc$72JEHFw~LA@~xYCjS-~ zPkciteiNgh)+}m872;V)&ql}JvI8pOj{kulRyS@3nfGR0mZX__bH~rRvh3dR0k4Ta zNb`=B-}QZ#m0zi~)Pc%J#1BX=%;o7qfD$$cDKWabUL3Nyvbv9P@2|8R&6R`Q(>7b2 zvfexyFYg7QSMdQv@wwDIh(f&lj$r{Wziyk*wQS=aj+Zaxn=~&!&_+7%(Nf)hg>{d=9*<$a*cR+BEI!~6J>zWZq|JT;-M&bvI zR2;%y5mJ<;Il9l}+j8{X;WZg_avc47Et=5jL%tR|b^0OT=mUkospEIx=w0Y1%h3lR zdRA%kKh4mKjb0o>KT0~JW-$mzI)+{^y#QR31MOXrO&m-j5ba-AUc8U;1AX9dKd#(7L7g~eO@bRn|i7X!n zeA0gavlq!9)$({{{S6nMDP_8o#zXSh>hX8TSJ%F4HR&Qs?=oNVddHn{aBP3yDU9de;0UV zgoC2GTb&ah14FUD1LsI0q+puZEG<5xhaVB3N|diRQcI`R)Tv2&H96mKCR*v+&Fq$f zdQz&B7dh^?5oZlj6V6QABS~7)3JA_kLyysnPi$$^rf@5JGg&=0N*e@XR4vgzW}ugD=VJ zp(pR3UvDxAjPP221X+pEiI;quv1hwW7lY77sK00|D{>M<~opwtW)_hMmhS6{r8+M%r_y6AZ?E&yy0Y z&^n0noWn(wrsC=ntR<;{L&3HPp!sjieew|)jFN}HZU?J=;emM`}=88OwJK*L2 zh32_D6h(9mq1M%{WPD-u{Tl>J41%Q2vvY!sVufz&)n5(FB==6}B02vj@edp$#sMf( z@Qx9+g}WWCK3?Y&@5NhaFa%6zq7`egNJne?R(Cv|a%c^Hs^Fp(%^i(8iXn9ic;P?-Mm@ndI@lO10 zE^dh=L!C-;0urLh7`qQxMU>p(J>q90iFdq>)|58Q8^%IyQc)%ltuBbsapOKR+O^EO z%ICX=!QtjwTsh6pA-Hn$v1=4x?w~(p_-(YJtiH9sDIUgeLvcldI}LO6mjbBUvwswD zn6~r5us5q8mjL4k*>ehaRh&r1&igcEv9stjmJ5EpWPFjg z_7MOJjZw&lM6cTA5t5QJ?K*mSXdVpfiS7L%g6FjgaFj9dZNCJ$4Siizq3Fv^-nxan z6Gy%SoU;6EK=Q(;V?|3E5yK&#7|a}Ff{0C@w(TcH23?qJceD+uW%&1+kKS=Q%j zo-*ROj|AWU%>PU7WI;%MWESe%rb)(^XrVRrkEJ?SbUfCeBYQ@SE!=FGJoC6xlFvD` zGv=SljG%;3N>kUpuVVdJSi)!Hk`0ACp^t9nE6E0uYlI~@nPb%gmS|nqR8J9}V>SNC^9$rIH>EPGulUmni@x!1t zf+4W4g=iA;R=2(vc>g&LPvh#Q%sNa5c-oCTj{m#y4CS@8v_ZrFA1$@m@M$T|)@G~! z^)Lfjjplj)&0m*!=2A2y_mD=Zo~bdV@@AJ!tCi^C?wQx(BpF!dpH35Lc>j;i;t_Rm zGEKto19;aCV|;M9?PwibqwDX(C*fpmmHIIF-pS{k$6tM)M!y zjpS3oCP}&3r{EMXyk!8Aa02MQLi&)Sp2zu_p`LqqaKvL*@>Cv&pdI%701-}j)(^!T zf2#jO>qrk_FuHb3zukgxW!WA7OPoG`dKff;u@OwcjKQ7eoMY0I@4j*^2|>Wzlr`2 zenUMA+{&<&nYd)or#Ex)UewMnD&IX8YAymSKhFQm z;F1~G;&G|GbA74gJW`3b?CVRU6UM` zV@Y3&_IoYN5goj3&qkClnCwY0yZ>1{q@H@wWZ(Ix(-Fo!XLAwBf1_fgJj9`U4KH;u))vm5xht`S%?osfh=nBH`B< zo9c5zy49Qks@2U}3XSCfaSt_isAl6DTOxM@Iz%x^5ju`ZUAz|#z3nu1bCg?c`n||( zkIu|ijosrKYnTyE&Ebd;Do-TwgSi*2)gjC7gIY z%lg@?wN2lSUHQzZhjAqg%g}Ewdaq?ULJQw!VKcb>idG1$bGV&3O?C&x4iCQ-hGbCt zQ$3|B4?Q=H+M(EFK|Of}=nb6ir0odPA4;J6xu0WDg0FFOZ5$#Q^d`gF4w#7LpuLfX z9!lOe7#5YYCc%1;e44mOJ13O$5_u4;%J%&w9cqN}1G?;sAKAoCAa8!a)G^cOqE_!L>82pcbpdAZkjhj2+ZhOy6~!aXKO#S$)J50F&peLZF5` zF}c89WvjiTuLZ7g*z1{c?a>hV=GxB1@XH-9n!tE%>EAGjO-8Q#X6Zi{o@(ji9T~8H z&tTZ{lK+h+W_|lB`65_vx20k%XRsLi6O6>2|7$)=de$TQ)P@qjiC6xYJllIJazaAZ zJ4o!^kN?9^RthcmgM{M!)!M$+T9aU*x4AT=lt#Lh{t=MS9CZHOLdWcVdfGCnm_YJm zm+w#Bz|?uA95FKD9;*F{5_1kZKgPXlWpdy*Fxw%%OzHGW%!#O;NzF5c=KY=-`;|j^ z7rxRCS_sIo4le|aYBDENZj)iP{FA5_a3o#JWIH+K8deXQ${o>gu~r+_Kn)A@kmIsf zFf6xu;f6I();}%)_MVu(;ItZ7M;mmlf_f&FlhvlrljNd{_Io~@8c`KV9lX_4c(J>S zsV7%69^Os<8FJA$wrNaXu(Lrvr58D1tI{ADsnXUmKI~LR>YcsD!LCHG8|VgUE{fqA zWEF{0psqQG>_*BMZ-R`2;! zc!(gd6R$bJHn9T11{*Os*>6pBR%>xx{(EW38~<4D$d8OjbZami*f>892XSM+3lj<$ zY2_e@aM*VYS8n+Ezoon0G&H9jOd|X@6tuz#G;^PRpBx$fwKjC~u z{)PN9h3s6dc;Xv>BnAF-M&Qop<>-^#%NhiGmfsQE3r&|^{WS

    mVG@R~pjK2^aY8M-%DF>D;}@EMzzS1nn;3`DP){9wr3ceUK2; zlcT+byaw(eNxI!ae*B7XtUI=!76pP$gvZE;umV`Cxa4_JF+bd=a82-=F$WeMPK8`+rTq1| ziUC1jXP;dPQoRY%rHtV>Vqv#}iE<-A%Fci9Kn()6;lb#ilUo?%!F(L!19HKOFZblO zcexvK=@_}he63I)*^he|gskH32qYU)gC=v>h4mIMFSM4EL5exa>wE^hh0Qmy2%6;N zpQFz@-Okbq8w~NM2$9C2Mj8 z<11o(8me*pqyvjOd82Gq2(edNKWaDrxC7r%#_d+Kp1|Bsjnkazr^dVppR!Mc9XMwI zQRLK!b&$DRXbl3kKDy8ov>)td*7UFZX-hu7K+(0UlGL_fU5i5a6!_W}^N=>rTOa}g zXJ+G^Aj@|H$V!S~pIKp=?0V#u5BkU7)1{Og`Wy)!hO+$acI*|B&#ZGiv17D;IQbWK73@0K@F!r49|vSZ`R5RA zhv8JV=O{N^qU;yhaCCq<2(`mM%2RT_SJ9A*4x0Jkln#kVDYS% zU=gsZmg}MJdS%Cfa~XpMA|G@~fdQWUCxb@S-~WaC#_og?`#Xl687cCI)49`_K5Pw= zI)1?k{|hB_%=EZ@5e*KB=kIo``i;hUsUI(8-bN@>s>| z*s6Gnfbcc1kWNZJ>~1#>QP^r#f%Vs zWHw?y7^H`?1KBp_Gz}pwZH#do=-pB)CYsrj@A5;sPobvLc@2N;12a-?_iD}KB|T|3 zkMqy_m3iF#yyn4H0vqx+%TL%|#GBf74lN?yvI?{4m*4|zUG&%4Y@UesfJ(yVs7;KF z<_L_V7~blit<#l3ndO+K-RV>vz(~RDPW;LGF2LwBPSGrW^lRU>)&0Li#BHnli^*2K z_ncdMoo7Yb1fSvT;W%^Sk38G}1Ch*7fnq`~^$_KZ1S`lr(7kUK6kAqQr_W zqMtOetYN3`v2eeO7!1I!;ZM*W=y~zbyPl*m^1m5rTZ!eJOFh1k^@Cu8XT|!Mp-OUw z*NY1q={oO&78mU#0v<9pP|rzHTOM;nHjw!gZ8q>}p^Bo-&<4PsMtm3Arr0iTN38U} zz>Z0-pS^E&Jr1isj}|LFPdWlo3qP`{W7Z!3ZA*?|QL^99YP~X4kFLi$Q2DX^XPvi< z)Zx=Ek;FA5`>4;GspGk%!AYKotVf)c$UtH0noDvF3KXCio3In~lP~tBTyf$E;jyX7J&W}QpD5%uJc-Cr76xxZo6M;gw4deTmF`8V}#H5br_&9eDv zkGshHxIbfRA3icmqLP8AF+lE))@%|b9Y_MHfy3?KrXphqF%f5XTr7T)e^wlc4uhW| z>V}>Alh>foq&|DrJCxZH2lTYm965T~Z<9Mo!R#sFnVowYzi@HNYW7?Ff0fz}CsEg) z{Jb$nY^W_6Wj7qkbOMV|m|505#Q2Tboy#J$*dfrCI7bh0J%6BL3XH^R$G0*WWCWv9 zj2F>#@|_v-mjx9tDKCii5f_5#3A^Ts#hQ-Lk+4gn(R>d8lxQRQ83pEa)BeJi0ZIOt z$;jpR9)yvj$Cv0)hL-A#8j`HZ9+fOZeri7_&~DgMn$ua#36jd0A#8!aHZ5a6VLDO# zSqyBtG^Vw>UG?u*-Vq(aS*^d0mTu7L$OsFI)-a{$M{d>ec+Q3!t*rZ?La_ zD2#ehPugMBJnDaeQHW!AKB?ha2ES2<;Q^nu^=@2T_TN- zC3;gnY_#4ty`@QgyZUE^EfB5aIWbm?FF4(fU}X5L!JHxuy8|YrfWiZpuG6@EX54MV zTvk(`vHR8Xz7#8V!p0}0*W~R8EJA_+8wKXfz^^8C;|mvRF9crcz{9|zn>B;43v#wt zyTPm-#83q#Sy=a&iv=D;rfiSL zw>P6;`wfrrBoFT|4TGFB3nB>C1?_*BF`7YrW~A1eOoh29IG8I(Ud_(bAGZp>y(dPy zMU6cHqau5!jwAVTe1kAE7zRWhwVmu$3e_T6Wqdr&^I~Uk4msbH6-)l4#NH0%WVLHb zoxvSctw2BPJk{sMj}E*nJyN`9xE)X7E0UwVrC`9p!(cNoi~Bx_XgGa<;v!VCvX4gFE(QA#59a#PBsTiaDhR*W@kC)744*u)Mn}s^x z^9P*54`_6KN24Lm;*XtUP?KoJ0~ouVe?E11a#~fi`akKScf$?bKJWkP*1!djF&7~m z8;@LKHwT9fg6M#=F=qmJfKz~nQR_obSCB%^vSpI_*Bz_q@?(viUBK|yFTp(F&GBbM zI-|cCs|<(33=}j-;9KkL!!aX z+Cb20#q!*?L@eU=*LuF=?nfX|3yF|8)c*?zMF0wS6SEP0Xmr=(dvC{tXlIz|CfObU z;M8N`n9}HD6<2}8@3ZlxVLRRCgR(I%3`&VJrKhG-C4(`@;*81BBBs@%P`ZQ2vZ6+-s>0EPF_U zjJT|}FeXKe^z+&1V8b{6IS$a*B()i0Ive4}%4Ss?=XGRB8YktOiDrD1e;O~$IiZ@g z`q1q2^5m+F%0+p*c@a~%byovjU>F(3!^Z36qMXnFg4Rm{Y|QEvv=3F#MaVkD-*D~m z+Dix1(lc`$Ke@eq#YprW9ubnMlZ-?!XPTmN*&NaWTWzN3klheX!j)B5;)nAVrcK4a8<-5vq*5RoTxDMS3#ak{Iv*0Qil-SgQjw(_?C7H@2$Gl%VTY`P-miV7qLd` zb2%{&y(&%Fds@$a%|q0XIZT^a<20)%5nlNJOsx(mg7D_*`s5xB$LV&H6jj`Ni8|%e zyBwN8Lc)Oro|PNbFJ^5t=;HYek}JdhAeol>FHY4TsovOpMxHa;fWKZO#nU0z8Ug`| z0*+@@Hd-|8OZ&eoGvD)9@bz}W>g<{fA&J_j7)?W7yQpoX7#Wpb+M4 zBwKAZ34WFj2QAwA*N+!JM@jnS46( z>cR92reM<;@EeKa;1aC(dH*-#qedwvwI z3L5L;spO56nZ+0l&FLKFPpFMryls`neViG0TWK!GjJvHKfUbD}!wxK}%7RYrXABX6 zMTb$-ViZ@QnhKwLH*8miina(+^dAUvSRaERjkw-g5oGAOSp?}t>DLit%Q3qhvvr!9i`%`BsO*bYGsrT#w#kp_62i!*j^)~J;Xvk;WuW4Z?HKvRxu8yqX6Iu<))&7+`I8w2} zpL`E1O~g8TtNcf$#z@2s9YpCX8O=w=o)0_M&gsM$YyQ6C%NJUlK!` z24wcga5H0csLz|sln4Qa#@`B5GGuWo#h1Vhk}+**6IN&3bx|VKG!8Z)k_w&{Sy~sf z;0XO4?=NIcjZn(hQ~hhnKVq1lp7#GJ`xEdes_g$CPe=j*!Hxd3q_tbR z0YwxQ6b&*cE+8F<$P!JrP&S3=;5s@wE+g)a3xXSj2ocU9( z(C_E}{COVIRi|#G%Cx8e#Y!@2e-wINEowW!fH&p5l@_nB!cI z^GP#DmmE&KSe;V0eevC4bVLXsmrD^Oka-5vRwzT5pUT83enjd@>ttdK-!b%04_k4- zY&x=+7H&ce3+RexgH44RT+#|jMewji8X+b&PXqvLjXi59foWUpZmtb3GQ7{fbO?kb zjf-27FLd+}$HN()&V<^UbU1g);K5%@md{+77MAq2C(h-wMryiq6C_yK?(_|Wie|Fi zX?Zv|^1$tYf8nXjfisaEU*7&`0_3!_7o#wXJSAAa8@5)Nr2H%Db}~Cd)ggi#!$yRf zQ@n^ev<2rKel=4>0?#oi_>Ft7x4PU1smK?bgn|=MbW@fYk~hqoOw+lbhdl0sM6Hm| zf|lzh*Jhvjs+7a7w%p?C-tk-iA(aV>?_m$KeIQu&l`pmse{b0J!TYzWHMOKj5zH_5gG-c9kghK#Gn#I7QK;Sa6F1%8Rtx=;2BG z_ZAzWFTR15p|B`-e>8TCTLd7FH>tlTa>>Co5}wa}UJ4HKnCx4Jk|W$x0v5l+C)8^Y?|E?w5xX7UHX}J!$_gaZp3s(t5fk# zbvyAUJOTeSeh!pR`_cke`PBHCIA&w0+L!%boAq5ZE+#kMWe5uzG8jV2T3@>SehpZa z`Ebx|ug&;yN(3je&6vgISJ5s(`p-b^EYu1}`?#_Wl4s*k<`z6?B0(EzTn4Gx`=PPWwB6ep&qpoMrQ$Ujap36!}n>VA}(sRIR6-f7HPDGr>&*Y<%bz* zbUbcDL+*{zHT!hdt++z$T3!8xG|JlMNm}?kh8sF@FfFAe`v^iiMMbzn}yS*KP`s7Rq~UL9@wx*ed%HPlDWZT7x2vJ0kdj4 zbuxYVr|Ao3HAiMOC=B8={x+>^%|$`-zw<;)uk3APV?%q+z(T{Pr}$)-Fjx1(aGm>ctkf?T*U|!6LUwM_ubXV~xHDxne8#i3leObIg6l|pe#3^*<7yr?-fCxQYm(D|?Ht7mxyAQK6yWX0jg6${*kYTOr$oTx8R<}>zbDDQZ-_X#FzgC_D8>rpG>e+TeC5&uX3DXNJKB)P&P{%(KduMiIq)xoMa z>13#d7;mtwA7<1yB>$KBxq6W+QZ6w8=VDJ<9q#r;sMk*4j5`IEbf2ssC0nY4E^}xT z{(p5cz+34~UPCM%o!s@SI@x72Kc1rkrRw3=)yZ%5sFgA78Q?O81-%$2dIhO_^G@}$ zx0(3Pgz0(oG{b7tdR}(>_MVqj=cviUWT`kHL(zr#&*e#~$?^9gtsux#tNt|cLgzuP zzjE52zVgNy|F5sy%9He04#eBRS1R=tOku)17XvYn`HXiDD;JYp{*zN+wirbU{|7c%>Y3D^)qp@IuAjtQbghe;LhiGUNXLB@>93! zW;aTiZW2N1ERmnaXvBo0!x&vHGpykR%>0J zn9E#Gj3V4=b~;GpwZ5itIi_)?yM9*V%x0*7Vi6vnuf}~3v^DDg54dLsPB#wRufJW5 z^U=5giH;4z{Y=Fnlij%S2jrW{CB#Cx=E)XQj>_7|yJDt<8j*t#3`gGl2cJjp9bwwm zOVmDKokkij)3&or+cKl?yO_33X}v7F9BZ03mCCiTz(gGUpXBnaj(ac5vuNL6FUv>a zQ>g_LyPX>-$%~Ge?Wg(4uyGD~RpWUlz}7a-%YpADW-`||%+hdnHEj+hx^Hj(F@wjZ z)G%aBThtRljX{NK)^JV<9E@u@EcWG~Zs>?6y~ct2`qKDfEyy%W4^7H-)UNoBw==cOKtssF{k{T)(Gp1 z<;Yp}7tHDZ)KlgRFk}BWHPh~N5Z@1OgM&;DGF<~d0sj-L+BQ?|$Ap-{s{T)O zxv<0D=rZ+hLYMzP6|K{8DuO)K>r4l(?E*z`FCF z<4DI-*S8pPV^wbmz2?N8fC`FSY_KTh>UymG-d(zj@_)So{y}HcD?l-7l}{Txc2YT%&>B3te`_- zV(DT#`|z4kMAw5b#5Whqq+>oDu{stT%MJ#@HUt#SP*^J34WxLTk+R7Jq?K9Vo5vGp z_2(vE>HoD*){onJq3k~8uO}t#i@5~hboHNoF=eQ)%u@XKA^mSweO-h8IJpJSI7wov1evh2B&cQLaY>WfR3|?NdHS9#G$1^;}wb^t8doANxJJUvTU6xipD? zg={TgDaf|ynHAaM$gp|BdQGzZv26{p!CT^sovZ=HJOcwMU299}T%9cZ){W$o52hNE z%@B4pl{R`0`;$!7&{q@g&?hg4R$#4_RWNZOcrpoNw6GkG=KLr*sGDN+;AK3 zOxTT<1fjQ!OkJcB#GYEK=KJC@9M-*y*03e&lP2=I0mojTP9ZGva61_a(22Ayyr>&Z1Mr3k)EjbCPE>LV)J*Rzg>RgCy+`nN37sI&udkD zs)wz-D~|&An4r4}3noZ6UbxZMj4OfCBq-Ucq{tt+_ z=daKN)2xX0=ZxlC@)w?vPGxu8!Bm9Ofokt<^kmamh%b35S3Y;sP?0NOBch?GnbDi+ za`L6=bWWWI%V#`+nBpgM8})qUT!i=m2mir054jzO==&slQ!Js@56%7gprc93mYYlN zH<$GNDFIx)wOlFS!eyY!uy&+?TgLu7ei;a7^Z%4seU(#%@qtmfZ;ct-JM~l5`3;5&>XBTJ5@w0_3cFE*SK112`en7z1KbhZ3xeo}Om{qaMdRsZe; z*cyW_2kv3e5HK%7yz^nvn5gO)^}t$1S}~F%rP?(bWgk|4Chc;nxW?X1b00LRro!Ae zeeXgZXlVMyy?Ur21^ahH)5=JqpO=IR(hQ-fEG}|xU>ulv=Qm8;`aQK#BTn?0R+8bE zVGbepH{ZR67D?5EMr)0kU5&ojO>`Z%{OhaKwKid!BMI%pp5=}c=wXLS!O;&%Hj#H1 zu-5U6OW!nKM6+`)dd9n4_=vQbl(n|F~F76mzs1QdoNObMDX043kCo}%zOo}G*!a!Rkg^D$)DNNk~PpkF) zuyBcLn*2(aJtUsA5f36Hl}?7rNUs$857K+cY9MO~J;d;ZVy$Ig%OIPuj^x7=<&O~9 zvv!&3Ca3@R=(I43S^q^p#%7j$;#uXCXkCz?VxE$BJ7SZ>5HzX?T7HryLEVnUFe{_$ z*uQ-32E7JyRYdF?*>vjPAg4|257Vlx$-aO2&S`20i?SkYPfG@^vy+N=jlJc)lmD0R z>?+Ya!eo2WnxJ*&n!262oMGs_^XW5x)unp#4)R~vp1Lw4=kb!}vqont zKT}-Gtpx{MVnhr#;5%7oDs*ocN8A&~N!4ASPZTqIGRvB(KaN;Qt0{~J=49-(;Wlz0759(W657z@Kt1!SXdU}9jHao2{+w&DoIECJR&tp{LL8H>Uf zTkw*3nKLoc3-g>}rK3K>tWA7e?)Cny4 zi@(y6$Q#U0I_~cg!rv+rd3}JTY$%lyhbpZ_hBR~_{Kqq;h(DJfGEIzGWY$pD&O~O8 zXMEw$UN74si1R3sm;CbhEax6piTB{EmdiW87RNRLO%oY1_weI)3N!-cukad;e2$-h zn~K<^r`?*bbvp-Wdc&`AXWqtJnVWspwl39EMgHpy9w_@$nx6&xlzxn-lXGcUM3KY| z9Nxli|IfKm1P~`pBgM7BlX*tOI?c7Kt!^viF*fT9*)jcg_KZ2jv#HoKj>$+?%dL`! z8)J1P$1Vy$E1vIobQX6r^cVwLsO&s4)Vpu{;^W;06onF#3Sj@HA@Siv>Q|PXP4c#T z+q=17XY*S(t!qDD{8p~uTOAJWW@KMnU?ztYI#JhR%~*|O?KkdcxV0)%w8lapXJywQ z@y)u6hG@&dTs5z%Q5z&e-RjBr6S3Y=+O|-leaM=nO%()XN3Z7CPtGmTs9c0!{NJ?( z-X?lpByl;TWE{SJc5UA}!#v1|au!AiIaTLu4=M~sdD@T?$t~ z{EsIQXWzv=kYSM=HCwaEoIj3}8MS3kv)P%u3wG;e(XB~#SSSTV-V14}xWX<(F;Lc8 za<-do5Jr#*GLIUzP}O_Nql*9$gzBf#%+_cfze{Q^(s^AQw8w4;aag?_Cf#m2)$UMS zCBr_U2&q=iAzfZFQdQ4aINT|fEjO4v#|k%4f<(F{1d10a?8Qt~qGzP)8#j?|GXESa z`n4%ky&C(!i4lpEU6JpL=@3?OJsIaH`|KIMWd$a#4YNPlDI?oi|ihsPtx+7!g#V;-Rvr!{2n1+e~7&59RC=<9t`2I<=Ek< zIgAQ({CQ7~MoaW7a&0bd7Grr?4WYW6ZEOnGvq(kqF<9gF=IbXwi?{gW`YTiD?{B`L zBzP~G((0Cy?`EZzLcm%Rs{bJeX@y0h&Ie_>4tM*~f1dw>!O1&sLOwvjq-G~h0J=r~ z?O`nf6Uu1v`cbWwB#yyGjQ>;O%3KyXS%f!;jm7ufY&=y*F4X{6FpiT+FIYECr#U|K z2=;=+*g>&jN^)9e4phikQg$VyOF-z9|yw?JoYcH zf?mc?5|sF2A7F<{C5UG-M`Eytf@00@sn20cj$d+uM%l~D{9hu%6maJas+0Jy=y_`M zkC1G^VPZ)8y7Gh2Af&@5aWgA5K?Fm1>@-Mde9^Q6eDTvxlG05|V5ypRSZB%|w&W8d zr@h6GKXD2=v|{nvBI;EpN*@Op@p5-+{})_ogURc?jJS(SUV+a5(!Lzpz$nxx0u;rw_)Poy4qS`>|#44(d99~3gTRgh|CeC|C>vD z6jWGI$AcJ)0D=@!f^kYx~dBp0iY`^h;QX-hRXNLNsrKxDT z_`v`ZrBheHX)k^l1nrYI=`NAF>21^(SFmnTLXtg}YgfN?5=nbP<)@`Q!PUf|n2cR{ z+K_oMtInK7_yKv1+18FdC2U^^{vl=X(cvf>p+p5Vpb-M#3^01fMtHtDThq#0A{ClZ zso6{0t7aLkQU^=S_=MR5OH7~3Lm5L;mAB`-7-^old+1|fN$I`(r1Jn0Ywv$ZI^k5H zt)+qp@YYfRox5y8N-0FcZ&F^BM(43Do`eNmDt~T24LgSfncW@lLc0{`6j7|PoGR4^ z>bx#Pl(RFwTEaj+iW{X1=o=%rQs! zs5lsIio2%LbomP@@1mjh55}*CZbDy8SP11&QVhl=*Qd6;k>B)J!}sn zYyKa;ISnLRoZzo};}zK@YO3>NU;E-k^woH3mH0VqeLK)Do`7LI;BIpoXpek6E7a|y z020qkUJBK3%L$a#`(`@u70%L_z=2Q?f=o}SM)*q&&T>`_Uqt+ha)p*^*lV!rd9&!0 z%G~+r0rlEt{3#|9soLN%5HXMXpCnZo1`_V}yUde=lS8Uq=8Q9XW=dtR zpl?vnRgN4WCLgU`zH*fUhzIoy5yBdugW3Vr=!j?37r#)75c#K@OTenb=JKO+oSapd z=m>V@SXU=e)=I!)|5e_N`$8W{Cd4^gA`XXun0tvK>lU!@GijR&=c>Y8ticD%Xyb}otGmUwS_Im+x*jxtdS+YRDQW}k+U zhegjeh?p1F?S3gt?9U=92`Bs*=V2Zuq9{~XGc>FEykKJ72$P}hyQ5V?uNxKR+|h!{OrprL&ijrHvpqFLz^M$VEOM5|%41VH%!`Ws0w z3bxwi4-#SR621rj!O9?Czg<2DSaKG(2$I}ht=nn3#df6OHzqI>q1V^ASPoW%lo=&^ zI3awT!sCr%n8W)>=sTf*)ibZOkgbF7g)ShrQ}vf$P`S+{4P8wd6R-DxHenJBlgplD8ZlWS8z)278l! zXARg^nK&e!UH*Jjw?z3jdYVeY&T@4#Hy_C}NpYCZ$q&l1q@u$XT<4}2Bkep^Tio_W zj42xc|M&uNgjfprM|#a2x=yS^lLw_7qqSz(5e_rMl3123a5H#DI&vtKgfl@KDj-kMB0W1pf4Y zO>|-kChaSc_M3IYrU=mRV>Qla=OaAzxBE^I`GNA7yvH0+6}Em1cKbZY2?qYM-+lIZ z(6xbfF5K#xI0O6(>QUcfRYoRD@-?;7W(YJnRZ^L-#poi|_n~epLS$49lzlw;J?e8h zQ3*MhKA53to_~@oDd)Vyw0S-R*I#faaN{p1cdbrPK_fIcS#-!HcSTg69{RDr8*+Cr z4os;+ngL(DRJLL>V|JIb=kIUG-K*rpNR@CpQ()AIDgT05d|G#r#0b{iXwKzizel1! ziwTi;mZ8-BS=!v(#tnR9sOFIb72{dJgGWTH!J~0avY-dE+|%oC0RlL-;OIVHFwBK| zHzV`F^+&jMu^b8}v(mbZ)Jhvag8O}RpXc6>=6=mh+;6VZ^P7f_3K)V}HG=b=M_2mx ze=!HiE%^`Q;hygO8kf$o$F^dih<}qc27_>EfYu8T zI~X$d`G}v~zP%k7cr6k&C!nFG84SiwI?_0P6b89ZN|R%*%u(9X6=S337@hKc98>96$`|geSzPzKMZ8=2v{GYI}YP3$0G8~JRa6$Z8xTLn@jBD(UW<3 zKsJb_LM14b6(s@OaDmmt{syv_Yp)wKGOV&}v2SJ*l~-Ez`kFF&46BIY{tIyK!BP&| z;D8Ozaao_8{ca_6Xc)VZX3F1eeOYOJ*fe?6>`H4HwFk3J?c4Q^S3M2b*Pp$t5?|ZQ zxV~WON1U|B=F%0vFR)!N=F%MwV%XyroqVY&Mwzy1b6eH!mCb&~ExDUEPd<(InjVJj zv)cIW%bW-M%d@!3WM7X^UXW3c4~H(-M0gGBS=uS9;L;e z#ttP3g*JD-d6%}y9>(Ey_JG{lsX0)Xi9j_9ZkeleCyy&fkz_}UNBC=RY^Kn8fSbO| zu-3``0XhwH&gIiC8JAYUO7J(Y$iS$9U{6p=Q2gm_kTr8Ml#vq5@lZx$aPHuViAPkJ zaC{PX!Ni#$|I5X-3@-a?4gZAzD}Sm$?rY~#Fr!7vFHm}4j^x_|1Bol#ld5KSmw@}{ zBq-U>Ynd4H7(#*A(H2AgV!qV!JP{zscxW)VwYx+8) zJF*v=*b=A1Y`)}P9&IieWH*`bhi2fV#$}B7)<-2hT*UaPd$I-TG1tyoXj-zJadb&0 za{Za{1j+`!jLX`{p|Y=Ws6^RUb=cjn9u1&R^7D^9=c1_98^^OL;M8vJ)`bcYni6+6 zl=MMsPqO`0V!au)mry$;O7!rT(R)4I$9%ecBwvzuibC2CLA=Sn3V1prd1zif=5x(< zf%6O+RIA~s;GLUF;3K}+SVP=6q$V-Gcf?w`06B@l6II^6u^rYdU;LtC*<&D!=Gd}i zn`aG+D3M&R3eHdkn3Q$U4Rsj)=Zkgcr4ZF2qGu(acWa7url^y{6YSNlEMOE?r+Q;^ zGgQx*=OkF1KTGH?b&t`|93=8$i(K1l1r{1F)In|Qw zI1wgsphEJiPY5PXm-=A5|ELbLHEt+d(zB#D8#pqp3y&4o^1KdAk{{O@OYJ(>7ST=R zoxbQavUpfKQ^Vj4vRT;i%}2mzwcS*b1_zC3PdFP&nyBS)qQzbiy2?=i)OhXWo6(FX zpKmqeqh`B{08#B>-eLDOK{CT}T(>oXk3n}gWlLKnyFSYuc~hf~Y!ptGJz%=vyX6mw z6!w(n;0bRS2+R!Q{*K^QPT5$&**3d*dd+89&gpRFAcrwnUG8VrhyK#4L9=y6nA!b* zUQcH^HY{c11bdJ95K#-2ozvJ6A7OT=nHSYcnCq>ZMES1uMrPm4Pso~<@2o^-7s>=? zrcef&;u>eTaE7sL$#+i%tEU+MTFw_hm&53vBp=wCh;?Z)V(E~wVGOQy>6)CLe)6~$o<`k^VF(X9V#mEE6n zPFR^C>-=4Xa)*GS6V`x7Yr<6@*EMmpAagpa4_`}JDprkadb8F2aP|CxGA zsCR}T-a-#(?iWY8AMheIOI12iy8U$&5P4tU(d3BiyY%&{o5e@z2V2;r*|EO&I^<|F zFDQFv{59x>+pzDX`6Wu9P1Si%>pHu%)XAxmSW%ASP5Sc`uQ5_u|L&F3nQ_$Eb##Au zaw|{L_8ECFovWbBQgxkcx$sQF#v1J03&6jyfHmb@c1Nn}02SznaG|21Y&gxhblRaz z3#K)$>GQxp-R-vEwi&4ka;X49S3tW`*yLQpQyS-BC*|jRemwsi0~bjQrNTycxeFl{ z%@?M4IV0UqnGtqu9;CFj!gkt=*qm%%C?A0t|F@khC}3#hDrfhK^yPh@>XK#Z{5ZWe z{c^cSg)jCu23X|4EPDI8Bpo*)9^R1(dO3N05qP1kNBUfi)3IKS=}*GP59_v*G`enk z5xcsm2TJd4Hj5H#wG9q0(=cY?RyJXd^mCe_*$I5E?`ak{>vzC_^Rv^qja4xYkP#;1iC)F%=WDZ13pbI*PzGD#vev_i$$;E=w7e9nUzPDEb2sgCad|#kZ8L#TEgY7r zReR9QiET9c7M1O_);R`ti2j6b+)5y*;xVF61RM7hJYcQ5R{=tsC;z&SLUyOaACdf7 zmb{=*S%a%jGy|-ew1ABV%_QUryJ3s%Z!9HgMa~|#T zWL8$2tSA!hf}UuSu;T-8)3{UNN`}cXtFe8Eam+kWEC{uMb%5nMxnhj$OdeH!yUC{T zNvi`D5gUd>ypWQ14NUaKnU+3>u}1k1qZJ5A1d-@KMOMMiy$)1d7A@j@bSwutP;nr2 zyQzyN5F2O?RAdmXcionT?O`kPonc=xIHKn+&a% zN@4O{pY6)>k-}%^1s*y&%le8NRILm^>At7^Y*FfG4aVn*AxmhfeQRDF5 z<~_4fk_{S-QfS9d;F$EZZ z+pg=x3{BAXTqjEp6YcPxXi(Uat&=7%7>`O2o51_-$qN;}_*L9EwM8&E2;}SYRdc7) z<|-5yj0i#t&sppWXB8y7I^Kw3$s57#om9CRcI}8z7v&}NeKH&@~9QA=I`3;DJ83)yOro3T@kl@73F(rEO2=rNMejP+&2XBzv7gK!s<>u zC=vF=-%xk#^6j|?(BU`CyqI_&d0W-6tFV|W{zXg0zi80Zd@v<7Y1ZOii^OF8 zdnCuJE6Rbi6m$`%-A|KIMapwC?^~0Jd(>c|Fz3A!Q>CshI7A8o(i0R8s^*J-z{{aA zTecZFIg51c4@6FlD};)W|5ke(ja72Mq4+_~5zCySyda|~Ng z%|bVYgm~u+6-5taWqh>F%T|_r_%W9jLVy-kw}XvM`xd$>#Vf>DB*q?hqxnT<{SWfJ zAYELD(<%LAI#1jf8+s_+*-HZ8Tu&t;$VQ6EPx*rkt7LVbe#-!LB`B2KyhhAq0kXUw{R zqI}>KK427o54=ASPP}?XHtJc~I6_<2Q_eY}kUTxN%Ug%Y9iQF>0*`z4XT;V7rxcwv zLM`xB8sDiKKtizIXxXzDF#=eu#|GI%iro{KdJ81RgKq_LUfH5YT^B5|dcRXp;<>kyoql@kdUJ#SQOQV(6|6VY52`xHn+Ic34^}r10Wb8RhzHDxb0yvs zVq-BOwn16xZ&kis=$a`O3T+d!B;Mz3oH|4Kb$e z&Ml^R&VHx^d)XIyH9L=fZ*clkerT9+QS&>3veTUFu$i&`hc*4$%Ae^{c$a1}d^OJG z8J_*l;GWAxUFxbr10D)z!heCD}nXV!dPPRM^Rrraq(L^ll+YZ@->y_AfMB7O;PhH@~1} zA$`giV{`PvO!kbj1)d<>rhMmU6GLk1v{^`MPHuI~>-$^VHxtZYcEzLnjA0I(rvbI# zziV#fi~qdW>#FIbzxf{VT+?u8>OV6dHGkwnM*~d>MZf(na@d?))h0tIdh<@#Ucy>T zn@pD8;8i8YVc5D`*$0$018w-d_k{%j1FbVdBh~i}Nv>ok1#+dEHHA5e-IZ!B z4njCxi`A&#YnnHs79BNfS2Ho>Mr>h=<5r^Gts>_i$S)#JZ;o?+U*VS;Po_pAuWCfW ztf?+3B5}4v<8`y(B#8+X_-PgC1%*$}`Oc+8+aOP(rck0A4JC>%1ri~aBx1j!Ek&WK z;WgBWfEX=i+ssWILUL1)hI%wP&3ba?6+xhl#r^HvJ-kFbV<1Rh;kRMV46v^`0t3#- z;-DBC+=Ju96uQ@gFZe-U1_1}jpwuF-q|gZc8JQ*=X{O;kIhKL3%l!{@%}Unn+d&TF zSiZJm@OF{D=sb~@FpeQyM|-Vs?#S-zey%1lI<^Z1vVL1Vf*MJ*%SsG3B)>x<^n$-;c;5yulyg3NB^*n^@s zjADtVzr;Q-S_1jM&n9wY2Ja{_7J=fmP$}nD zm;5?|@n?B;JwrR(ebKMI@aHeh14cpT+FRY<;Oz8^chfaJY6o>kqa!DJ3EH_cy8bV5 z;ri>S+)QT*Bb?rV6pi0%d7jAdrc3-?FOl)h>IQ;2Ge1@ph$U&tBt~B0JOuir;U?;) z!bt#2?ayLzf4^33Wr&X?h zYX14IBr(V=e6}L=PCoFhD@Gi$i)K+rktWU=JBd5H$HLp76I3|JadrYog>FRNrGJ54 zo57WoW6eb)2VaaUvBTn05<8d9wT!wr2zK6VObv!5?448nhDZ(%VAgJ3PduVA0WxI+ zAp;wNiPJJ(WHs`p9R zKow@=1sL4Grg@y+DCJ?wBb!t>v#5bp@zd?}ikZZoK?BUS`w6~l6(mvKbZU=d=@3Lt90 zjr(2Wrj_jS>tB6NBqR)r6 zf}S+Xct%}j*K{j|H?p8jNB`vM+=~C>FFl=a^B~2(Px{$q-$(4safe18p8F_3)%SW; zW7axs^@q|z54-nU`Oo__yLrHj-orN0texuH(4HovjxWA=vlyN%QDy&h)3(=mX?I{s zu->I6cKh=ko0W*h7oP$@ao*uBOdcN7mGstf=dtJL0}&!KuH?dPvy7fBxMYupjx8-7 z%43KvjsC$lZU2TJ*DX8XYBS5u`N)-8pr1o-Jy+NlP&k1mPj`Q_{+FpAc!lnbyAPL} z(RklE2g>Dss)lc5kaRhy47rd@;8Ku@ZpD&SPlNmOEo`t`Sz$^+Lj98Bfb_=<;vwQ_ z$cLA%NKUU*LeC3jDB1UC#J5cO%}9QOpr8h^UUStI0_s@D$pJsNcZY|~rwu-9;W$2O z;&OAgXrxEV43=9g?#>!b;-btgj_x07JZl3}L{ymUllRhO{a&^#-Mn`ZGgI-(mDbWU zL3Ku_2BnIZ-2J64LK?E_`QJrUw=(=G| z<%JNH9r@9={4LVSWeCJ<(7^m69cU1gG${)V(i9p|$Xe|?kQ?^0OiemHu|l|oT7sQc zJBJwPlmhbN5b}n8eE$RR^u*-XcGWdc^G&~H6OH5i+D!!n*lp`q+qYeoFE)`PY*Zrw zF&EBE{zwQ~HY$xmo!}N;mwJIsEmNH0^>;NmlNY4R{4Z)Qoi}fBYnnz)E`CYt2!6jC z@3n8%-KhCcU)225wCZtMDPZR`U+i)oFnibBiy_JGawR{B!?7{xD6f}SN*fpe$c_2$ zCQ|`Exf3dtB*Nr$aFv2BDcs2~IL4*+ZYap%=^Bba9fIwM%ny zWT9+HgCb!m`POT)z`02~XmR{R%0d+v@l5}uE7XXq*XYi5S;2z8T({X|t$x|F%`kFa z5|9&b7in#&?+HpW8C$<#znY!bbGhjB8O;S)YZg&qsw5OU%5(;c{E2a0Z~iH3O-@#I zKOy5g6pAma4yNii=h~3{@vFD8A==50_?l=@O}Zw?I8K+BnB&vwN&DYZm4Nr=gI+ts zw%f<1|9O4^v4s^6x$CXQ7!Fa_-oM+ZYfJgbP}jze)C4^XFf}diL`??%XLZf)BXzAG z{Ya^6t;k0OQ)1}lO(2vA@{X~XU03ESQZ+5fjJwW7w%=oE{M(=P#+%M$Bm{ZHx%_EO zhA6qdN-M{cHW=DecqePmsoTxUjNiXj1eeSs_^SKc-4D=uxQZu8tz&3w0MFcM%rLX)HkD~&Q24Zf5=udm>L;p(IrP^(@0Tb8mmD_`He_1m*;OGXtJY)8 zSXe|I&i-TE`78LH1{y&dO*XmF@4LQg4Ldq?F=m7Qj^7|bfgrxBHKTk>f63<0Pi%_V zZ_FB#292w#4Xpn;sr7#ht@m=#R!jBnOS`q?BiViv*+g8!2707iw61*LO~M`Ls7y0s z-{CvgE?naZ`f^%Mdc>Q9w4UXed)FriZo>y?Z8MT}I8px21xIFC^$gZlYLU{gU=bXmI9w`tew=?R_$N5-O6%?&$=kmY+n2OaKIfiCew2U6cB) zLF@8e%2$d{$o~dYjwf~lJkII1hlckl4dd@4sLs8)o*O=K_bpi3?JoU8oEzi|eBU(N zIm&&0geiQOdvhi?eETZ5@Zl5=`T{$gqVEkq_n#lKK4o{&=D|U0XE3oaAHrw2UXN_- zd)DX8pJ~Azs++{?BfN+4HtzjU_nwnyt)JY2e`J}0gS-bsf_o3XbRT4;s$a(aB|HsU z$>IKC9Jic!J@rz1???bpPZxXW&8XlNaGOn%iqp)3Dm|8a=AsxHRv2F0x-Q(hGKvqi=^q1@_^xG4EQ7cxnD6ca`oUH|o;7qXB1Kd&Sw z>Qq2oY~Vz0(S%R=yHM4+mtkwoB@5ScYi87KkC`7nKR8(T3*r>fzSAiXRJe+@(dGE~ zS1Z&QHW8RDv%5?I?3{`J)N-kC-PrTsP1x?S??+?vYK?lBce!azbyi|(VO?XkHMP*+r?>C+m8$PAn!`3bb7SqcXB*AzKjwTpPqVk-_)yREw74sgw3l}=CeUQ z+h3n`-|e4zcLh()XLoB}-Oq-~))S}0y>kUmGcZ>B)uv?6INNO)i{5;o4TV4kKF}{8 z>4PL@+v(4ZK)Iqrk9sv%&WM1Gm(Z+B=+S#XIY5aX4OX*q%pg>{t%;5g+Sj*X09tzS zAjKoL+Y5Fb75M9R@R82uBRe`o&*X)i${9n5rd;ofDbOwTrSYROzhcIG0Jb~xxz8x4 z&c}}dp_T@+I@B%W=v}-_)@t~4$K4p23zhv6yV_yWvrMbmf&IYYkYyfwiyt%n) znbVENWuOcCyx*)Z@lX~Oj+nziX;Md@i5fOtV@bL$Yjeoj9kSjF+BXzKcrc$VPF~q( zvsBQya5rpS$$}ysoJ-i+f$T~a&1cQFbYjL6V+s@lHmj7iX-%{!trpD zfOWf}&Ig*ucx%e8Z-JA$a*x55e&qTc-2!JN|774ebu#OLn|xs4gL_BHuBQAyxugC( zH-ZkVtB9110e+DC-L}5izfJj@f)Vpw58lo<>sGy)=*Pi;`mP7hkKRDR*EPxIsbt#u75Kg{|+B7IV_{MaaHkP{^JH z@H^nis3wY5MmwikNc?c!?zX<$5zu+^c77%*x2~|aDCq)KU{4%GeL;JgzLjKmg$rkY zFyE?4eJj5OJ`T7VX4)XiZ|Wg-063-@7rs*P>uE-a`a`C2Q+|=)?BwLTW9eYJ{<$eQ zTfEbge|wI;dWt$j&x0*JpQO^tK4<)aX?(=}ER-y%dyV5R5m2sy2P|lxRSQb)LUiCH zEBmY+!TRsohszdC;o!e{opWn<937onyQ65r75>+llQ#URCFbzgQ2qBEkhR)}%D(m4 zbE)iX-@LBf{k1!fo_Juy_vlwQ__;2c*dffd@0Kont}*@qf0 z>{b7o<5{UJWSMc}9`J|Ljer?A2|^@NsRMi3k$kqpC{L%z56$bJC1$Vzmc zf98zNoROQ-ohx_buoREM|_P`16mD62to(GO*)I{kl4?NrhKZgC+v$w6kj(%ENe zHDfReh-)@InK?y)L>Sk1e%2T!rW6_7QXySoir6hX)uWf)N3O40yuIbE z``kyp&0xEnW6B_NGf^K-Oc`o!E^%*0n44?do6+XxI&(9mC}=;ZxB;YYcJ+{$YElKE zPX%^mujZVy3u=xJ-W#@GUQ$a5FJ7S!Z$~?%8bT^74o?f0ZLIz=OgsS@7OOk@D?(*oPaqBNUN3&F7lUQPgA+b%`W|H|Mu*a46h%64hpzf1`l7MP_~yfaG$t7! z8<55#+BJjN82>?Pm~mEss%DI;FXTewQ+);(8k_3#xzM;&Ure}JYD`E474#=6I+LQK zLT0cWe}O*-(ZE^reVzD$+F$`8{4@VHFyZvw9p7{fqI z>9qnk3S6BD{#4)>1n!XuULx>)0-urzepTRG1TN16KOyiKfqj|adj&pU;A1nvmcZo# z7iEHP6!=(yNv_w@?$HAK1n!>+9xCuQnC*hfOmKgJzY{n&6I>?na)B?*1eXZ>lE9>& zYx!iMzz+y~RVH{Jf#U+7oeAFc0pRNdJ~I>ii@+BOJTVjet-uum)1{VAejso+flD&M zZwb7=z~mBZDgCU#JM#gP+p7iqfWT`722oqU34z}i_~K0PB!TA%e0e7L8i5}YxHJ=d zg}^fe9-9e1S77D;T`(dOTq*Fy0xR?)jj?|hxSznX4Wz**3EW*^aJ&V4xWHWmj%0!l z5O@zAUl7U!=Loz`;Hxvin-EHp9|?R~Ciq8zHF^uK$pn8U@FN0u&ji0Ca6;fgnc(>X zM+L6P1V1V8aDh+F1m7p{SpqYkExnu}@JRv(Gr|9!%}PB3{|$ky)sJ=R_ULS zV-{SJ39c0Q6M?&Cf_n@6hQJJNOG`=weq3PXu}#m;!2-_`cw{DcAAu(dJRuXj`#r!T z1eOaTUCD0(hXn4K30@`e$pULbNxJk00v{@H=S=Wh0_O@G&ICU%@UIM$3F}B#@*jae z7x>7`(z6AYhhf3rGQm>?o-43?kLgOT6Zj5+2?B2cj}&;4z{4}a=L=jVFq^hoN=F0^ z3tW{6?kjMqz$avaPZ9WVfzQeWA0==*fdiS~{RG|sv0HFsCOBK*F9kj+6TInNz>5Vw zClkC@;HL#XC=>jdz;gsXFB7~(;3)zhmI4?& zRN!j_mdKu7J5>S?5qM}Oc(A~y3w%K)I3VzG0w0kHK26{Pfse`rA1Cm3Wa9;cGr@W`f%b{I0;GGr`-J0e)HFD>A{q3jCnJmt}Zlqv2V{aj6Zm?8TQRSt0?SRc zpg6PiJc0cJADjt(Mqnu?3x;HZ9}rl(BonS8?pNpUEl)MKJoR*8ON*0&omJ4R&tW=-1ETiS1~>t-KWI>*5Et{G_5cBFAP~b z{G6bEbL0G(GzVvaXUrrV^6U*n#F$(|7+DJ{6QfBVUs;TfHyYb;SYh6i$<7CAnY zxHdmjMH1FAjV0lnKw%&eEC{ee_x84w4U~=T?3=~e-#EWHcW-E6pxe8ls)j_@I&E$> zb`?$zF9=l;gJUhN|Lur!RHv!}>OoxJ}=z`(axB?tH6j@y$zQ zqB5s+RJ|V@v%Ms2KX7^$0X~W-$PdI&yMKWcRATzC+m`LSy+5+R>%4*4pWAqZTjNdg zzgZ%n%eS@F>f}6b?cvxj@}9Zu=&bstoJzavp`;F5iAGouApv7-iTQF6UxHGB^&+2c zzVYteB)B?4R=eUNduTRx`OcVwBKFn!;W3L!B00qH`e|R`D8E%g7;8bOu|o0>(Nv?^ zwOP~I-&i4>&jMa#eiLmh#@38;hX^rzMd2~4O8VR9cU>7NbJ%G>p`g{dcw?n+s{CX2 zMahtLh`-UF)Z3N513MQ7eEXfd!5MwI+~xL$9?9Q_Vc+qGGhy-%wC0-Q~@J6gt^YEnE>g4!b1AC-yj#r`d~ zzQ+7yN88u#7|R*WP$IA<(CsH@38or;Xb6O z@x@=DfWwNnGx__gcGIrl^&V(nj{)GGMUVzj}$P0`zDjeY;K+}m9J^JKQXAp z-)B&F-}K+P_gf!V+Sh!QAIYu{Sfj~Y!`T{*LI8J)oS_<#M3?**@PXpX!#Klcsay&u zzPKoq2o##tIuD3HF@=CS>>>jR2&yh*;q)YIL`x<1BK4sM-!lAYlEHNSO2}8F$zY-t zX=$#(1yZM8vq*oIkvFxXfH~=3;Cd_j3@wgcVht_kBqyu@J^k~u&`T=q?%%4V^*`hW zx+U#PdWN#!w}xVct>_-MM$?ago}$I$223z$&JH@GlpGQ3BL4Sxmy=V?Ba$p)sw@jy zHQ*7b>5KpvNkodmR;P~7vz<`A_&aMZ)achr$Z~pcLlD8L=eQ-pO(Vki^bYh|=C6CF zxmUB{f+qCzEp{IoZ_&G$m31? zCI|Cb<$tFF-+t4IDy@N?gA1O1%Kc})M!Jvi33>ZbfVs^lAAWAw_k4Y0MR9X8HChz} z$W18--5ki*m+Xn!xyCG47s#)5h#6ipW-Fid#;h$chg3R@aNbJ*WfZ30e*SlQRQHW} zbNSIfvpt?sN&f2~4Tl+@dSg~hPkW<}3REANA6=uGxURQMiO z&U)~t($3|irbWJ627xQiVZt8kYt9nkdFbXJvNo{b6XT1@KKIpqCiEGzv?rapTlHA0 z&PjA^7wq;y@MeZCC(AeeB}xd9V^47rNc1Z{)9$z=Ukwxy4VO7Lbd6lK>|p`yQ$wWZ z4Z&Kb-;bw38wfZJ2Tyc)Gw=GQ$Ixm6y0<(1e$mmK<(|yZp!E!7FXB}3dKvV4!!Zg} zL+moX5lLJH@za@2Q!onOC~`*zc^)W+08CzQ*fN-Fusiw9Q8Fx(LiidFp#yBas_b*v zGf#tcb+1CtB4ZlV)u#avNy$gsg}W^e-b`cF)w@(eU7ab?m#D@~cuL43EwMsFE`nnE z-QBhG*@P5H1UPyNHAERUCE-a?9;z9 z4sDD&xVCXAj-tkJVhr0rvHFdq&Oo0?f$!Ffxx&v@Vh+UFf+`!a#uN!)%38xqApB56 zM&d3WR`walI|;6W7SYd%3ybiH7d3s2c~d@IE%s!rC0>LD`Ib(_*u>o#PpF46U0UMW9@AsNtZbgVuH#9Mqdw{`yGM zB8q%Wk+9XdIQegig%h2NMHfEZCOSiLiHu7qt_O%-&g(o-H!!|4VGFqnBt~==Ll2aF z=Zp7cNFoU%t%a&Sc6lp-SD~B>3KJdtme0A9H|EO>=*%!R_1HF7^bLgh)bt+&6v9qn z$aV4=2wFfu$(5Xo{3M_-j34e7yFlEpnB9uS#e|=CB!yx}U34xU1;^w63PLYK3Oqlb z@bgB+7iR_fj4O^-1*~z!e!O)d+uw7c2rr9&Gf$cPpK`l>7_cXR@bK?(Ow*0sLslq1 z=<;y!=D1HJxOuB60f|r)^@CuToVQHjgt|*~EpGZel<>nQdWNiVoaG^gSXZ7yADO~& zrUyN?JW+z9)MIX{rf@F~M+S0e0a9hy>avW&$+i1xW(43fWTMxZMz5>UBr|Kx&@77Y z#n9INB}4npV`zSsec0-81Mlg03Wh8I)&@F@7M)N&7%)U3KloOPwFT>&bA!I;_aO2a zM#eA|rI?tREY3Ayl%OVbA@5MV&&b940Yk>r+T-vHZeU`+{a0MP$2xxKMG^cf$)*08 zeNd_4{h~mf?@BrfAdX!3fyC&NK%ddbU0Wy|u$;5)k(*t)Yec5pdb6t9EtGGFDRO>0D5j_qn8Oyg~jN8 z;Laf`)!jGyYB(S`e&rh3B zX^+TZ7v(Ph&D#y2pFJqI?)zOx+BJjXD{8`(W#9W|4Wzt?VX$g(c+8p-Lk+v}^^hHg z8s5C!aJO66z(t&Ke9QlqOk}S~{~=@gl$Wws_)+z$H_n&G##ywfxp|=q5J^jkru;zm zS~3zwEB$3}Pu}Q^r^W8BG`qCdKNY5XYH{)u@!0l!Il;^US(TnO$&Vi-_{Yrw?P`FkxOEc zN4T{75TOS`-k?8gDD!z|FM|I6TSFNHDgWBjQ0}Dy-+q_sDpH&7V%*ZC2*oX^lY#K1DXV-PqmVfi9!yg&~)-;wn!094^luS?)e zmkX%GXqueLb&je|^a&$^x@)z4XRKt>yv#BUHX`pp_3L^RP%zu22G{?_U?qklsN{Ve zL9CRFolR<6RpGE zdx-9Z=@H477jeyTC}sf=2a5o`g;SHPOQBPTJ;pP)EkFF4x&I9#9sBnP#msI9HY)U} zxgjHff%tyxdmNv0Ubg=Nj(GHpdzK=AQDX)ag{*HlnVA-S89MSkk~)kG+1*JReQkcE zY)Leafvr9q(;);RnPE%G_`vLNpB{JI4LZhx&@7@cvZ7Piu5?t$dUt?*&Er{-Bbzu> z?h-)jv~7e~^yu@wnj>XvqI01E7xJdw@x^5C4V7{Hjl!P{^J?HCRPW>sw1;)e9%y%e zI%q8qANh08?t30VUd;U{)#i&0*VswR+T1jkc#V*9>%S)Y$q^GC5h>I>7ia?`;#Y2Q zGXD?ROBWk*`h>n`cZ$EIjDSG|gj|y(3-N(hgb>)2cAGNg=U#D?La;djWe#CM$D13>%`lg7_R6|Xw3{ONd zScw2fe3)tN#lN?-c7I+ZEbfap75q79uhxns7ploczIYA=X=|S@?;Xj$msES-_*m^_ z54yJOq5GRn;-YC_nBKM40cD`fr#lwAdEa;{_EBW2=K?d9o+mmME51LzsOH8Xr>gJPStrLe^>F*;WrDu(DE+NoQZpddKD(L_BJM z-Th%lk(TB`Kmp@f20zL_xe@)@SRFks%{a)V)|&h zY*4`rt^)Rw<-}TQ$TGU<%{+Y!COQ{8WcQ>RQv*~V>(^d2?0iC-0o~MUDXdW`5X7B} znEjE5CDCq-3c=;DCNpA0%6OPEjw_AWY<`_L1lE_}X6mULFQd($CkBc|u~u`^^gzby zRD5!J{|e_<+;J2~DPByj2ZQ=UXczkxf`;5#4bm0dlPNGH8|-A$Lt!ux7#g(tVRs`T z!hZe0f=CHl61*gMxtwzGp?nNC7G(Km-7C7uo)Xg034h1Uq{g`&Fm_zk|##5Snf=z#l0o1XpCr}J z$6@Wa=-0B}V(=Pwzr`imZxKp#3(LA^trX>IkXC&5(OFK7$c0_L=(_#0l38bIW#yHr zE&AN)>WZkwy5wg$rU_9F$HP9)SA%t?2~}%+@nRLT^Ullod@#X>`mfQzVA@EAt)oyE z!`7&LcV|({c4ZUqCkIQo>cT?5j5_Rzj&1m0GbabH@Xc7q1^dYYI$h{}Yje=r=@;p) z+mSu(CfC1UoDAGolQ--p%oO!O>=G;y0?>$(oVCfD+ZU;bW+NsKE{u%%sw7~anH|o# zq^Q@TN~@n-2Pd#+6D$6hf`Bz1n?*m*X3?)mGm)b84asL;+uhvsEDfy%JZ+%gJ|_ps z+`BzGV4suArjT8d(En!oz(o%({AJ6g&TuZKcdi;?L~wuE$5TEFxH9-g=Mah+CB!^y zY6twc>}WfG+456>#N-da& zfn_A6h&SzsuTERi%nk_OtJO7O`^uV8b$be;Kk>I+bQ+=fb0|+7=>z-(kZua6;GAlf zBg^&G)YTMbO$|yI9p$ep%dRdDVKFqOul312NRGn1GCJjku_l`gNM@kYi>OgCb1S`4 zM^MUN*Vv}%-r&vH<+8Jyeh=G^Kg`TH+70E#SDIZ28RNC(J;p1(rKTWQHnCu82eyoF zg*Y*Gr~?BBvV!c=Hu?^`f3rsK(FUr_aBGb(W3Q#$CW_;4&}htLxDyj+_GZaM+cd8k zWX$-<&U4#mUCexFtDMIEp%*Zap`AscTC0;k{WqJQtWN&>od1unGl7q)I{*Fzf^j;MFqkZ z(1L&#Tq@vxuTgPjhrHk4Id_s^`+xm>BzNvP_nhZE=Q+=L&a<6;&9Ia0<0p7k?KQ!~ zPv9o)%O;W3kG}1(00x=w%-c@4!_(IsAPxWtuYq4gat(V3p&`@x^x=wO>r#{+RDj=i zYEVI6EOaZ_h0%h^EtGxeIX1y?-{a=|Orf8T^J7Os11Zc#GCaJRUq*%b6c1)EwieYO z_pJ5ppznwcBhD$ETa=ihU^wF3jDV8qKl)_}3^VCvFntpEd(I`24x*eCwc9FK?rNq> ze%*R!GGU9J7Xk)6EgM2-=&B9Jp0A=-|5t_GB$TOpu-al^%m=(kB=eTq$@Kr%aqWO( z7huqE419#sQ@IdhSU6HPHUHKNg3hDM@$DzT4Kq)QDv*5zpCvCo2`N?qewnVRe=ro% z?_?&z=}rijjb%{PR1K%jlU6wyfwsJVKt?v6kB0iIG;`c#G@>k!rXgmgoQ9%hReY|P z6p0_l#f1ATLN2BaAw=LGE0O32iA1f;QSG;x1-FiKF!QjxyP1cYkVQp%5wtaY|Kf@E zgz_-&lJ%uRyXzqypv#+qZrL~sosi;PoLwK}WGhNcwSkSvFmJI(dDj4k?;mShZeFji zOcL{&i)8-|CU&)R=B|H3flfsHDIvKTHKgaArBP(<`E=Zqj=kGx+K2M0r`rd-iTsWr zGbOGn$Z1N`DW0{JC$uwD)`V<}t$(Zg_eb{=-b8cE(3hj(LbVNtG92`8eC^#r@AJjY zj#8|#dk+-NN8`Q7(0$FV{sb4HhRnM7zDWdZa11Ma$nWLr+yBybB;8>1aZmY^zr)t% zv0zfG-23+IW#d_CDC=t8A)KGxO%d9hr9N@$p9igxbPoa?SOHJ)qh-8{`}0e%KhAo_ zU%|A{7fAi98hUOCSM6{=`=@qV8)i90g~V;SIp48a9rOkM3)|gG_Srowx#e}WV%-|H zyN@4g{ECyy_13Jf73PVP7#k>$5tv#Q#^e=3w~hkJshR10%ZoOO;98>5gwMG$o>;;C zNj@#zNS&_F+d99+Sxz55x8S7qUpZjIM^2<(F8T7BRd48Ki z{4z&n;@p(2f5oXsye~>ipu={ zrug4&^2;7onGfe^zig3TmY~cGyuvT|gI{oUn+DJJ%aVTCh&BzL;Fn$Hm&pNgQ9#6# z>3x`A7W2!xwJF>AcRTP>zibZ{o<&%J_1$NFSy#Voiz!2?PuW|3+3ut4z?a!F_O49t zC4Sile%VuP%Kqw?HTY$Bnli(}df)GtJ?WQStFm&7=!9RE^2;t%S)DDr&M%wlmz`_c z1k=FsP=g)c1%8=sCpU-=`XBZ23v$dyh32DDzpRH}_9^&S6eu@k$N6Oezif>uvs84L zU-k(eg`$SP*)kX>kxb88Kvw%@zqVz$D)aoZXZ*4n$_$Y2`31l63og}1z@d*;_+``m zvT~ILZQ1jF*?7OKNM(a;*<*g$K))GrfEI zW!z?Kz6+_$xSwWvALExz^2>@tH+~t?e_RFN}Sp;1A?iRnS$}c-ZW#zVPs$VwLFOvggb_GrF%TD*pegJ-BkD$$A zewpK!y<^G%l(Ig4+4ng4XzZ_=GRt*N^~*Nj+8~ z^Ojou_upF-a9tnezig@EK0}qC*(&G%hAOh3s`mjcRo+sS+ijHvcTxrCB2(p9s?hQ| zAtP&FSIYe=>F!F<98Px^AAgS{7MkcgFF1`T%}(y&-)mRwa4SC({qNIDtIFiWd08Lw ztEA7o>b@cb&f~79L&i0nUo^jBBq?`N$MrYA&fEmXwRfkkxIUJ8vJR;DGoL^1?|#Eh zobJ80k~43n>j2Y6|IcImN_E}F;KQljzoaP|-e`WE`8YM*$$VupQMzaB_(9-Jn4@01 zj<8jm{%YtL@0)(S$Z*|1-*Qst=;O0hDL`{n%QIfYh{fgy4J#Q20u%84$h^BSMibLLn4U0(S2;H;t*GI zWSA2DzZA4IBLV3gRvuLH*|~-YClT_9cuwA&4<17QmFYS$sKeCxZtZy8>*drxw@fmc z+^uk?C{g#t?y@szLgpWmMcj`aFDowAtFG~4q|By%nNG0){5!s@%1mdh+jZ2FzN*Ar zhtSzHmCbymAKaJynfnztm1ILJ2OA(dA0wFMV3S%W_l3ODr7<+xsgD@WU!O)ses3(D zko6N$(y4E502rAAKHz~ND*f@-mU0JOD;d=EZ)C=2RMpCW_vlf^I5UASnqL|`P!$vZ z>~?$}x%@uK<;q8zzOqo0Ud~K1flO+rA~S*AP#HQsF?$4^CAck^sZ93fD=CyNNAlCW zffa>JWsz4-afP(Z!OV@#$sM(~l)hj36_=eBl{ZBU{V88vG2f;djnC=ZPz~4iif{Z1 zX#jF?>if|~Bo+D~&oo4kDfe4rjv0g&#;xrcsoEN@+8;CajZE&6k(quC=phE?);t+Z zZKX0g94dOVHw!w+--kVrBk;T@rCF7!b#kIFdY8kOf+OVohmqv$K!SKDe}%hLR)&9x z4}yuWp!>U)P**gI?~}WT&D+3vuTjX#;ZEec-Oa%-SRM7_eGI1{<$41Xp-jJX$_~sL zgv;!?@}ZKiB)$tfBUQ^&%IYkASNj_a-Ct$f)%Q#uMwU03ur8otZ65w0O)ndKcm+C% zWjcRV2Ma>8UCzX?Ad`y7^^yR3&;iusbAZzPv^zigOWbIYOSQro;9;#g{eLXr+G__IT1dUPmweZW#$t5eq!Fv>k8CRch**nwDAh}5D z_4+k4ZjNRq=E|(phloynDe6QAyA&$dud;V;eA}c!oxr*_>9?Sh$mytF8xFK{7hYm^ zo;zg8Y9Z@ng@Hu6el(3q$%1ORT->9Ljw|&h(doDL2fW-qQe52jKD?;Bj!RtT{*fpA zcsGx%>Tz!@hpU%9Mt>7>7Kia1TQldwrJ%!lD4=-(EDG8MQ>$^mREclfcarx}L@VX( zaZe1+`Pi22t)q&QyqU7PJ%nM)38Ze%4_J426KwdfQ^Q9`1ryr_hBLLf4Idsd=c7P? z`@tZLKEXL32EJ(@pnGJ6(4nsfb5;j))`wDfPPIWBRy2dQ<<8uPH19H$+5JY~{|_kN zu~3d{SCM)zIOih^rBQ++aTqAKIrW2h0Gfu6It3FS1KW^X3kKkQ19s`MPtNOuGF^^I zbvp6n}4$UgJ9k6yPeyw2h|!fBe%mI(stOi`_o|$$#lsH=4=dQd&=2m z`!Hm~G|v4ai^RjL>0oj%2g=6q=}@+)slJo}MN$L2zfdo$4>_IviAqhMGijAM6{!t$ zZPBs2j#+Y&zY-QcI)ohG76l(I6|2+WLx*lmb@Pgu~f7R+LLpKitm=Xg-*94Cd$`W2f`!H^y`w>cL2?@NPWJM* z**w?ne)OhmAS2OGG&91dA(^Q9gmT_Z9pRlO;NaivPHTv3H@ELS{fga8i*a3vAEq7E zsej76yPSmB_U^7p{1zU{qv`9o1anLYNu!RS!|aqtGea5ZtwrNxY|=o-G0?UPl7xI{ zn^lE;Bb_frp!H&ch0WGUK99qZ_r)SvVn*6#$joP~69T<5(sLKLQOC-b%{qDzX)N;X z8nT9icqf|M@dujYhBKO@_Z<9Bo7NetYVrzNHe0WB&${kpv|jiWw299SrXY!vZRT^d z(Lginbs=X-$6%&T=^rL1cHwe!KhQ!SYe}BrELa_`>Tzc{=dI?C+iU=`C(JH%7w`$% zs>GLdzV)b8N1473?K$t88k$bXY1*(iTOqrCRcK;F{DSoKg2VwP=Xh2L29DDA69?MW zKB->FGE?cs=qW^;0K8BM0usC0*WMAuv!1LnGh8`Ig~+0dSp&BttU$TWJhJy9MNOc> zD~~~Du966Z(pQ)|Us7jZ2WOr<%COtf!@||7<1%Tu0p~0yJ}0sJ5NF;=R0t|^7MFsz zv5^!86D!aJ_qqq``c7BYYQdy#U3@^d&3@P9K?3&K1(^9hG4wdQaPpjiheHUX;Z<&u zcd6xxSp_-HycIla43mR%ZZ19?zs#G9YdX4Byaj?;1?`-9B%-9o7}dC?QZ;U=q((4X zqrEe)&eWKo8Z&38#>^SiI3ZgDL`>vagPc5Pp%8I6@S6v!?SddoSK#72Qw?C|{8PA4 zHS<8V4cTg7gbN#i=nD1sY7xwvijj~> z>Bn2`x#S~s*Ud%fkPLX_LP#4=G9`q>7h<_v|BTgix(j}6HU`ivKd+iw2T+M!N6$Vv zTD`iHl-r?{c^A!jGqs?e!qnZ^ZUU(X?%?0$EdBnq8RYEshwasUZli<#$tNF8^bHwO zr9BvK*w!A`;XD3DU~ow<+eDSa)xv=aLdUds5JX8Ii#E}Io_Zo7wO!zm1N?PK@= zPx7lmG5NS@)m&4$Bb<8Ez5L@Ja3?GjQ%_TA$bES=EDS^I~#sO$bcyAC?Qe zY0sg(DCrT6t2gcSu&KvH< zH*pbHnm6WT^s*3Lk#AyRID4_d=7n?KFiDIwKVwK7)KNCDaN@IFFasnv61*$TU8c(+ z;l4*tKS}xE!tDn$^px7dLdg|u41H-$l%W<78%SJ38&K4NGx&hJu}1fSG1nIw5XdrS z`c`aC&bn~>a6woxWU19P&v+%&h+_P1dUn3G1(dEay1lJ&qbs=V3n$?H{O*11@yNT8 z)SIy5UeL15MI#%W9RR?yby;+~kAL|?vwQ7FzI}P`KTxADa84?`}_0EB{wx!dk;`R|K@{^Z9cgB;190R2e>zpkWMzW z`zVcOYOrwCVnhAWU0a{+Djtrc&-Hsb&wk|8pRLBshnJeCmHAi_Ytj~TAe&<(f^)0* z>&2etq}PeM-p-eN_=e`U^{A4)`QYCiQU-dx!Hzkvp|o)7pG3IMDhjf=#Kb%an<@5_ zf3p>Ul0saV`iR1~910n$C70)$%minIs#~r5b~KZjp*L)*#72$b%V9KiOQmz*UY5c=yjz$DX^O3V~3^h-LEkZPv5!^ z7gxeENZ$_a1KA)08JD=GZ?KR()E2V4^uqwuyk0Wj(HwxX&DK0EFJ%0qt|*7*u*Y96 z|E`hvKYUhk=sG7E7f|NbL4{FZ_0(^UdEyPy+*~dXsF@4~6i3rDE1hna%WJ4=92ud* zI1}O($ZA6FYMiv zntPm++PNd?2=(5?qsoKVwcT>CN2~qN3(6A-{(CYJ=0=F*33?)NIqOH?<@3`Wxkb85 zGDTL%JHSbHV;l1p@vS^zS+iqE_SyBfdkX_ZYSEAPf+=DJDEy=(Qsoiw6kG_x*fekI z63KMpwEeYv@h>_Af;)!v01nhsxK-D7VNbR7_#m9KotnT>Tkf7kRTGQgi@*u$OZ5Si zpA)r(*0AkFG=E0!*eM1de|}y-4rSM96})J+OSWYf{fF|R4Kb+=^*Vdjb9wxF~_tW9~nt3 zH0{PKQ0l&RrWA4Kxc8||NDcoZMTTs6jvmQ4#!B1!dS|JX>y>k?sW%Z|)T<3ceDOKn zVpU7rQXHr`m0dgM=Yr2k5H~v;{&-zsqM7O6 z4*Dn0nN{rYyqxH3E|4IQEuSF06GAzS8=BpTiUiPA7$E3EOo%agS!D(t;P*T&L@qc) z2pce(?{A|mbbF;=C2A2`lW!S5aubYmtNY-w)U^5C>O0X2WL{`ZC}s+xIi7p+2%}=D z{|Y5s%BBAtH?gwahF#$YW2yLxyjayF`ki@zT&?roqvdGTztuHvtoN{MwvXm~ZJ?-0 zq-*n0k1zQX)AN=4%}a*_n#LVGej`#R`{VATEkn@lIY!XgG2+F>`Q9XV{V*v9pJI7^^{}irmTM<6kkC!RqRYUTg({Sc#b#V5nQ>VMg8QZ0YaQ3@eD?_)`m>70v*9&DPBpb-^{L+mT>7iY;s+kz{n{A+*{sPquhU!n^3$o{{ zDfQ&u+SPAr8U^HnC8%>OjWh<+)AJAJZr&drp$3rMT+#|zf79H5k@H0uD+S;26sO$9 zP;3BlCZl>AdalR}KM*Qg>)iDVxO$8JM8m|p`{76??q=urX^>~0QIKZQ#}2V=sKOij7_#-I5u zT)%+HF(?m2(rMr_bu>R?Fd9?$h8gZ}#CZH=KG&2fY!-TYVKptfKiuRuQ%zY#dSn-f zDgOcvf6deI9Ve8LYgm@KU%wa#Oq>K>y#we_z*WH#L?XimgPDr`;ml2y5A6i+TM0XK z|DYyQx%QmXMO{{{)$)iCXBEqgKI;zF$uV&6y>QRfqI{$y&}APW9C&kZ&R(SSUnUY6SU0Acg(Q<<8SM-yWJM^p$ z%;`j;vW4q@jFuTR_Rx)v;srXVqn3{kepCY_o1D;h2PVGGMZY);(B*` z>n6OGjf+K5(f7InC$j-A4NZxPtG_Ldw4Yw+o|zrN6I6^;9SFj9OfYXMv@fq|HH58M ztjPSbIac*Hfl2nOozpZ=mX%cVA+gG+DYwR|{u8NgqRMrJ+AQ7mW(k=*t@Rh&P7QY%KK$_!d0{+A%Crc&4oq9%PKW zVz`{~EJW{$^Cb2$b1CAXTgjn;@SiNy{}2#RgBOIlw8)Gc!wm@T!wM40uEKd#Gxwh( zsZ{j6792i;ay?eX&BE3Bk?LiPbx5I!4~V8`Fd`OYBt1J^!BG<$>xY^Vt16cYaf~l; zkL+%(v6`*krhha=tK4C|=>uX!OjZPlNZVR*pC?a7c1#^KCK*0(alR5hxz8|m1nW>b z2$}8zuP^Z`n#KqY(=B8kG1y+ykmpsH5r*$8;tTxc)gHjPw<>7?Af_;J4><*WSvZ<$|{`}Y5{ zLI!f;_1B*}Eo*-K!rwCf?~VA+W$O3;Tz41$MdK5OSmIxT=vFKgyPdM#B)I^+ zF%O_SNhJb3Q!~ST_F9$Mv&2c1wan7<-_V|WH^pXDhfvmvYN?rpYHtvM?3!a~7%bIbZPF9Yu=<_F;S@i9MR0h(2YMZt#FEy_2!D zj&72M|W_Ml9!f1?x(G(BVP1!{V7F}x_y^oXR?K+^+@A^t$q z{KYKIKvQZllo@Ec4QZ>+9h}cEN#=;iL1e-%;8=TB7DsmPaLDk)bRQ;Z@ z8g&vnKsZ0U*EMkWtfX5G1V$Nxn4D-p*80|F0_)d9XZOUee5d{xCVF!J<7sL>Fu+Rw zECGaGke!2aGlW{Fp<+bhVb$+qw{QBqm9=bz?4%&eEB%T&KcDvA2A~ju=ESyt5KX<+ zx4K}~CE=>IW)0zN5=IUPlT^iB*-KzHm{?|jgl zn-@NLNWeP|@iLlf3ZZF2#nl|=!kvMlODXOlyNc8|<4I32!N|tV*^nNuKh~>=Vt+Ed z_fN#gF?G`q#z9<@+M@kKVUg$DEjMMR;H-DFhHI)tqinYZ;?JqAd?{W>7KF(NBT1fv z;|;}==Nvc$Y$}V5YzX9!H>bs}6ZAdu0xlv|h>5}UqRz6G(W=!EY^1U34~wHY#2yWxQMt8py2`7oZO59wlV6#N-N9ExXDw2PHh>_^I`mnq z@X-fwBUK}fUWLI#s~d`kWnSuFRCvGBvhjJd+%+Rrvx!5zlALjG3h?2XmpWa>j_$^< z)`VHk;YzboHJ$d%&K|AaM0@f4cifRwG6-5l4e3O*SS?S`)R3B`Ia2)wY4_?6Q|GL) zpz{8aG2M+nF2diGiLN>S`Ce!b4Ix$46-qe94R8~~v%)7VkP~FQu)fg>zmsL`$ z9+<;9U%M-{^&`bGWl-gLrqCBd;>DHjQ&-V&=Jy(zZ2ERqk2JLa7*N^ojE$7@LOxfH zrH3H{4w4KQ!w9kZk3zU1@*#QNuV_RVqi3^X-3WbW!FmmVvtVg&JClwLhMu6HmEC+{ z*y}a~>2$1DdQ6GoG5((KK7A+OaHLxY#beUH2Aa!CHH1>32k^d^1fC_;*QcmD$l>K~@T2VF=a_B`Yv zm~I6ASV@hxOn2J^b4c@U2fCIO`cje1Snd;7v@FtOh$9$u{>B&j!wP!y=SOBJacQ0 zV)k0^0nUraw(Xs}_Zg|K+%lqn{8j=0>~a|AW3sZ%eV7+$jq*VvULoA%G=~?;e-ce? zGYEjtkh{C`Hj=*9l6Ot9yOgz)MQbnm#bkny$n+T+!xN^=9$*VhnbkSYS!IAuQf#nJ za8?UJ!LNOV2*~&DhUp~sbaaxlAJUr4&RDSx{2HIP2$^WP&b(Y8*>3+pSu3uL z0D1()rrYHI?AF@>9dm!NbJ)+>EWRsUTKW|mdGOat>p9TPnTnO;5bq!j_I%vv51r&& zR?}1qhr=lcQyZLSKqP(rxNz!h3~uG&tg)@Fu}=DA={50-LJ8yDz=rGfz*f*kLq6X) zg%=y6skST9QV^@BO&S(S2rjXMiy(q9NlJDM#uX;{uqw5$&QHwDJKC9hEHh?&1-ica zd;FZLI^7n|*>I3a1eX_!5xjH}@d-{(tSD}iPq?ML^8YIDaOBA1z;{l*-_G@_m&S8| z`~Z_rqJZ3@%Jdmd^>#-PY3pBzXnUDFf5+`l13yCH-TaBB=Z)iYV?on=Vcn7=L+$KH z-(Q#CcMSo-lnt%vb|-1SqgMI2U@`&)9wa0hDN{u8zo2I6nXbsfEtIr6QHnLzFZ zO{ZD=JV}P#2QJ^&>@A1eVF@N5w0`|VzY-mdCmTBx+mk0RCHG&WqXGAO5h1RSBs!#3FUDB0f55m#X5%iJe%;!~@6UX<^aY=d;b zG{4<9mjBSqY#^^2LVpov6HcFbMT}%anYz`~i)Jdn38y-Y;!O+*IaS=08%sqBBMFZv z>DOz%$F!ag?$Hx14X4L(YvpkdUT)?w%sd`fq=r)C$ayD71KwK06B+e6`9!74OGzj= zML%9pk+Ybe)2qOUvUr$NkbJdIuH%jMQkunhF-cwqpzeT$dx=Xm-!0+0f%>jmKh@|4 zheju4e|`Ttt8t{MsxC%fPt|h$r1hN(XyzP#NQv)0r0-7Fub26i*01jTvi`b3d_X%7 ze3;Xk%~WkXRS52%^W{4XZ}NO+!Qq)X8^JMWg;SilQ<*hq!8ehre$V6H)cpDU#LNQn za!f1aE_Kda(bV@%YuhZ}$@9~L3yh=vGB#<|Yx+-{V(ed(>$NC*8GFJ2$4O3+TAJ|S zl zh21&X*%Z$r=E2@Wh3PH4bF2HO7Zj~)%@-x?m8$}0npB{8inzmH{{b1c*`2mt!=u^? zxA<^feoGjyE}dX%WFT-yov7znetHyQ?B^Wn+za-K7cUFXSs_$Lt;?04`k=+$V-kFF ze#91#TLhT9j&bJRcAw7BT`PWTOu4J^55F^(^Op4wZ^3uHd~73`n~F8zCS9P}#Munf z!@HmyV+%KN8eE^0@1BbR3{br5Sx*P!Ka?JYzERcyIRI9qrGMtadH1tdf1vFv z0;ETU8IUG5^=89%Y|L+BW8U+QKC;`{i)?KgVNg+MxJJIpaFl>5Qne3%uO-n;?!S3Y zyF@mV?l1=_g*A^+LnBCT=If|z>Ewkt!I@i)&gWzB-x}C}W<)N{hC!coju0v<&)EBa5bZWO}2L{1W@Bjz)^9}s=$VlBqK*L)|2A~)_zxBDx5&0(X`_>^2LCTC+>79TH+)L@~g!Jsu2I|Nyt zq)>=t2K9;bMFl@ME!c#`SbLbfQ~&pSC2dTk+Ff!5BgQF-W8&kU6^VFyq| z=zOfl?$p*9FMV?fsk5LgGOrfBC|8*5(Eo9dxH_0|do0q><78@+H`X+9nG|s^+uh~I z*|oVCWg^@HP2lsJflrGhMlBON7Dm0UWQ&c81P+kr-B2ASZ}gKTGJB(q=dwZEN>k+} zp>(8IC>1sX)-Wr|{^Mjae1Y?f<|Whby9<)dZHQvm#u3}L6UR*po%ZBJZE+`Z|B9>;rZ8O-lBTBmmCz&t(W}99jN{!HYb!FU3HWv` zmJT{IsfW0SjknbEn`PhjCnMv`W) z`)dx*xVEz4q07v7-=>lrqVk2kD6N>-WPNX)IngCb)0c^#UF^OiMY|SG7ihX7^-)M# zhhW1O>=7SHyAa&4CsOr#MGE!|Cs~&lDyu4R?hc8-;jS9x{j`uhDZR-|h*HeLL>$d` z3*K&}(RCczexjns#wCI+aJn(ssc|e!)bvkLFP0`fc&7JFtIXit9Dm%$>x`f2UGyq3 zyKLNFG*}g69}SYCfJs+sr|MXn_uKe2NKC1J#PA&^3@z|YQUDz2gxS$j$N`bSQHM!5 zXN$@j1vFJ9% zBUS5kXNK{&L@Q=YA;R4y##6_x_1{oAl=>#PP@P3JL8v?f_dy$?I6^-@9+sfx` z&WKUlq(OoO(mNJ{V)s0BCZ>n)+17j^2ojbMv~Q?wW(PnlVmA+X57LY|qq%oW!fzWEG{1#X zWzY!X3LmVDRIQ2;|G`kAnJ#st(!;y{B5^$T*D_@LI`rsWcEhQJ%PMGf$8CS5J-Ej+ z+H95hd`F%C=C@}1ZFbKdn?uRv+L;g)-()e$5MR-^2CMT%U}#GE8LDnZH<4xL2%;?U z;Q|MCSLsfW-a>QtkSQ21S!Gml;)^5j6g$b8%Y7|Kif3;AjXcG^!&DOQ9nd~l&HL6p z1#^pb6nE**%x+@Zi%bi549%dL1) z=(utyuOgY39|H@(>iz=-qG|VAMrO7$W7J$0B1dL=-W}AcNZ*b>1ovCD0b+%wV#7WF zfW^PfP}=}-`)Xz)+fnVe0>aK{7Zn{6{b> zV55h&4Z!7sK^UkXbua*rNE!$xngqaJ0O+oY&Vsjugsv8VTnj+Cc3=Q_F1rlAL`(Hw zNGPTj0Gz=e_uMuBETJ+05E8tR0ci5+<@qU8i)H5MPvUE4`v4H!M@?tJsy4Hb;EjBw zUSUB(-E7sd>bGDBMwO9!L*u51grwgXchz`NB ze>wB|0#>AYQ>8!~Nf2=)vHY-Dh7cJI$XU=Qn3?{a1@H%$G5|(I=Pjg?RIX|7T@|ta zl3#}>vnL8=vrssR%RJA^U^-ZCb}(~bybNXmgAbd*U_sA}Mb|vNz&-ZQAX_V9E}tmb zy~YCr>r;`g=qF=YzBMRIgSpu-jH{ON1%iUNQTrIyc(*$#YsBQVCE<@mbl)sLXePBA?s5_(xoLRyybKACuqvc)(#d2tlv@CAIy zcR-KzTgp4iN^##Y*et6x42TP@bvk>n)a>%!Gr+ebP>8io`M=2rpYCP%j$>2whwzJ# zSc&L9tdE#nw0$MwdO$^DSm{9$*W>6ih9IN=pW?cAw-_-STEKHO5&i-4N0g0XZ9?o> zfPv~4GF*}mn{jIT*%PJN;S9i^KW+g&@&6dmSd1K+)%WR(77DrQ0;b5*5l-?A7{1>g z@8FOi-~qqA4d}$P2rAv-W9PIAyMxTrHU`NIGoW%{F~H8@Z3KBFjAekEQi|m$L+(N> zFqeUUERFE03Hp+qnSAu_U9YPZe@_mXkJ;^6L(xoaaYfmWv(F9ETE8HK)JL#|H3vrK z{^)Ewi-+)$&45tCK}f?q2C$`Q^?eiJ+?5D;*F(&xz1-9448clj##FXJh8UxoXY0h3 zbstJ`GY|sK0Fon^QbN;hO@88*a&AFUN<3+N!>Mm&iC*RIDV5e8(=K;FB=|@YGQ(xN zhNg3ihNYrpK4%4bXR+?&^$KfCgK2A+p;rtuw+GdrjVKyO8yxrw!EGdiLEsC_Q3j$@ z)OdOXQt3rHT%gr9lXckPxn|Twng@K#1Wrif zuPjL4uB`q{jliAGxJ5qO-D-dO22xwNwC;HfO&5d+1K{(@##Ve5Qct(bYZ2JPiS?Z; zceqTA#^rx#(m8CG-8v4dCl}-7hfUwR88`zE*VSb`dZb*JT02;`; zSJ8}m3OW_vJ!BjgckY$1kEUauFDu3H=E=%Rx?sRRV94>-Ej48plUGv~wGt{Gz9517 zlH~{_{x(7&v>do;Si+G`3UwFQ^*oFzL1R!@L28uD5`7%wmL^D@2;Ept{D>fUGsn{8 zzB!16oTt!$D6HfM_@QMqjUg>}_xM$!0LxZ4Q>g7p;(9?v>HDFw_nf<5qH$>qqO6%m zc@8W4es5ojMv_Zz=VdTGq+d9-t|EOcj9*Af(I`n$)Wg3TIx*96VXjjV4jHlpx7h)A znhCh_pIV>nfbyVCkL;Bm%iSyAM6uFpu6$#-c-14-swkVXniRn_x_6#w_>GrF;uBW9 zaV$kW=wrzmh#lN}BS%99CUZZt&!OOtSc(k3x6rB2)bxRd4<2Atp)H(5W{W>H)JdJV zJjdh`&=}d#2Qp*>{ktpthVC;B*=tU))vc-M>&)sNsrojEy%xR5PAmgs^N11pCa>v} zV0t)Ad{iG_|CeI&zo zDym5B3Z=gFeh)+N&Hz^LRQ(g>pK#{P55FccW~TS;RW>FF9sA}B_QQiVdO0w6><2$w zavD2PUu2)z(2L;}T8TNhR~R5(Gn`aX%orPb0H5i8Ic z$L-XvNNrR2Pesl)d$QjZt6I%Wt>vhAMZv8N#<#AIRs@PUcaoi%cwu9pn<3sQd7Q!w z^Wmh0B`wcdsh4}F)3mi05yHD;NI|=r6T|Im^AokQ(uMef2xH7o1M1sY=|o>lCu*<< z%4F@qOA@NgL&#Zy&En^~4Ax)yrHqqh?!82ulWP}T45Z%z@9pQ6M!X~Aij#H0nDq)s z(cAxDJNZn3t%v~FG$-(xcFm@(SZeH-hmFPKf>!W(nq-=K`Vmx*ezP%d*7Y9;@#rkD z-<+%Gm+kXuJhwD9E3(fw`Ut3BOjgqZ%1J8mfb3silH7DW&)BRwn(p>Y+lQ`i`_k{7 zOY94e7uo*Lq`h#J?dU{3J4@`3F8sjnZVb&qi;FH3R{FTLT*t&l)@+ntbxaZQ)Tw`u z@~t~su#h@29nuzr>zBsQGg-5gbm=mu=jfvAou0od(q~T3CyM5qzt0psX8yiZ^ql$o zYSB{u8VlB#*(?X0KBGLii5L^$ix|CB!XcUDv!RBr%0<`4{l#Ct(tn_Y3N91$#s=Y{X~qZ@YLE|F(~h;NRSM z8UJ>OlTdfhA#o?nDuTCM(oQ&CMLG@hvx%?U{46@SbpOGnq?2x2S@(Ih`PP`UX*pW9 zu4dLH7{}W%q;UH9aApLZw#N)u&h0ThTm^fO!JbQMf5o4|K+R43=>vp!3KNN&D|sZ8 zN{>@`#5Bd@KkN9zuT}QfgZ9^Z`jvT}BOejc>o5vyY{Z+J9_bTL>5EC7)}AlNv1|3=*dpqGUcN;)`$e!Fb9U;Cplmv=_f7cg<{Xh3&>cJbT)ryW`SDW;2uw5WtT zlhP{H<7+kJ9cG~G_S28O8Afx;1tO{+rrRE#sXxfPKSD{}EE+nQv3mQ_s)f_tehQ{{ zBK4!`<287VF6Pgf*JSDs_$ki36}+duadL0P*s(@`XIB2YnLjq0g<%skunDKHo??74 z@1_J73Zl+gB)91Ysnhes=f+RK!5|%jb*zc^Fs#0LgKFI|!+avvpM$E6PpQmuRDBaa z8{u;2Hg~TizQol>PROU3M&RqARFm8LPd}indHKh1I)6v##vHuFegjPh>-JA_>Q7=R za4{nQU^zstGBQWpx$t-V#I#KpPXyfCG? zqHLv;T(36>NQwRJo!jR!i$Q1cbkdJ}9Y2$Otlg;70_npT*`XEnpU^ig!<^l|#Q}D) zSo*Bs;eFy)dxtYpS{>Q0qRgu~g|6F^+UJ^M_l3yLo}7J)M1|_TL!HH!o^YU|tjS4U z!z;9XPhN;Pf!hjO+mWXEUNqm5FDKJ=j=JH7j z3_XqhpBx$EL2t2j?P%jsc01R?VWJLc_4RLmPQJad8>Q&?PX(O^mImwI37E?ychj2M zU6gH?vw`dV*)$!0QcZKkt8Kk>&G^!dt$}}vJr+Mre7v=8?--lm9Jb9Qp;KCX9ob@L zlN`%49$6o5#ZoiKsn?a~;G7>95+9}cWcF$*!)DHq85vgy5wF0V{=`AEw^Vr3@kO&2 z(ixk{%Iz2`bDdnC33XJYy zCF+@K1wLw=>b0AK2`Nr}O0>{$1-ztd{tGjpa zZ+fa~{3I}RvE*MXFa^asv?>nCaZj4GADmqxq3b^d;P6-XX=mp+-eK2veY`Y&JZqu{ zA0w$q+R8QlLVQ}f(fyQrX!LQ*{T^id;7~HyPwRoY_)O3xZ!ifQC+c|-DA9kjQ6h7l z>?WuF=X#COGM1T~6Dj-G>~q2!cJuev)1#@Xd~-Dmdw#+2)EVFUc`Kg*Z>^UFkORJ) zli#P@`qe&?*q1lEiW%T`?DpZEnO${EzGAV}Yu2Eq&*-*A_IRi%1zr>4ed(O?(-l@Fv^C>9-1O2WsNf^=> z(a8up8A2yROeflJk`K_GNqF)H9*mR1QXa_152bgh-#9qT$)X~ zGCoWqrE#FG<_vc3nsa5fO3G#d*J~4G)K>P>`mp0Y(Tw-2`0HjN)o@RC1!LS!vbm-O zXr{>t!WeZ&uoUei4!O)M+w=G;o412gfZ9kbE}>QcvnszhI`8+y9?o(9y-o+g>iDosQtod-Nu|0YLdmN?Y|+OJ0;%)yOWx zGM&~{D8Bn^cil0Ek{V^GQlrR$kzfrd8j{DwgBrnwf~_S*${s>P%iV|h#JfY4ZI-~b zJpTVuq=Y$)W{GrI$`X`_RSCuzKfXsv<(8DX7)EOn_Q@)d=iSBI&!6XU*BIDf*sIoOwD` z@ZvaLtgw%T;802}^miqZRXJz-~ zhgBd;(oT08CNc8V!LmOmVe=JmSYtg}!HaEKp@92(CMS01lgUZnAvw)FGT?skp7uS> zrn=f42MxZrPNCnVu`kdlPNQnH>1p5=v|tzy4?xEW(+)Y=8FI!3-`*HXS=h4sLFKp! zu}oyPCXyBZPkySiyXeeh_DWxo@BRunvZ~W*6fh8-vLD6@q;VPDiUxWoumgoB2PQ%m zO4mdZud^3dl&!8Uj8%OYC1WYUZn5ewi=*wU3)vO&zI!&ja|21!n%)m9C1bNYbc~MJ z*&!dI&NAi1Pka^OuG$dCwrSLK}5^jdILc{BJm{$KZkUEx!zA$Oh8*lRG9fl=*~8p~*{Pjti}W1OLZ z)E+bcAWwL@e44@2IC-17=fA7@l5HeD-ON4yK5ABROrG*VNkZEa%~V&aiA!0PA_O&a z9L+#o<9v51j9{kqL(J=$<8+gd`=58RtvmJqxlv0(1`4+;Jw!|oBus1e-l7B#(7ErO z6mXVo0Qse#u&{p^DZYNbJA(_H^&NDiGsk8A>wI3D7Mq@7=En13Z)(6` zya7`WY3uzDzmfEUM1lQ5Or$^!ruHiBi$HL<{N3`aqs;8E!9jC2WsSD2{PD<-qlJpp zn`2TxnxsCXfcnq=SKP&`hUA7uKA+S$%XHf8w)Agr_zYfPx_|kGADdI(OX6I^6B3f) zo1){?zk{iNxg-6CyV^)+B_v3V!|XVv1TNYO&57b9+wP#`Uz9wZ>Cp8=*mv1bY<+jB z<-Wat_`7zm2lYe!{Y?)naO-!1ufXs0!stB|=0jW_1em*Yr#bX5O9Cz)Ef?4cHD%c zba!cg7+F{@x%KDp1g|1fchNs`!F4S4k;$`tIPWE4+;KE59dxJm+*hckcpu_WmTcx$ zt%fh#56zrWG(6pJyAnG_%Np^*qT48!4shCtrH_cFNCd`VdvG*0tsvN#`z$SppBIO@ zk55BR9wm4i2aBcs_c^Z3gioFyN>3^d^__Ia^ix8qErTX zgKfSb3M zK)jBkRuGMmMe-Hg^dam)5zc=`O^G6u+?OaqD@i{bN%SkybznQ+@roH)G~Hosv45NV zU+5wx&6B3MKj?~K@(L!VCX890g%XUW1rLuw*5c^qAo$Bor%_shN3^MmgUFyzNFnnueQNw-~8AJ>7TAeI4z99MT!3ndOuLtgAVot|@K) z=Sh5J9L5$00=Z0k*LP0UbCrhtG8$-BkG}U1)bnnb zrOtpxBtTA5b@HTg#6bJsnFd4Y<9>TeabThNT?HhOyq^b)XZ)bj{WBEmo@!+`>7kw5 z9sE1tQ|AP2ZI@0ZkZN>M51AK*@(I)@uXk#*xu&{$O^LmjT5A_&_YQup3YERdz0~ib zZ1C3&?inS~)I@yZ8*hBezn8iL?bv&%+w~125PXjk}M?kHD>N2S6ONFrEJ+DE~vI!8x}`s zTs-nTz#_5*;y#flYUG!`AMAVltTVz@Z;_WlN`-WM6N2^^?(qy6xnmlp(5C;W0oA6F znzg1;?>epWOowoUcpc!t3^tO-aO&g5aw{WF>Rj`4D=NtTJVl&}HKj}48Qc!^%Z@R|`G(AQ)!%oO2mjVyBpyt8^pXu2Ign=RtZeg9&wJ>De56GaV-CIbz=j_1vFl@i+fs2<7)>BTUTx<^awk8cTYl z3kh#Sx$q4{q4xeDT1ov3^}{x}t{YBqD{f=OHb%j!EYxmNKbbEp(sva-$UD@8rOw^I zW+IVS$z<{S87fTKLL|NNXj#)V?v?Ee`;BvljL-ptQ1Hj+=4p8zHE!GzPRt6!9;BKi zQMhHOQes3*9^6yR>#4>5?RUDufWt4gPi2t*x%o9q+ASw3^*NGirTL-!jr`Dgn;WjH zwOnU$DCWn__Qw?S<6QFt@6^Q1>w$p;vp^_|SoQTzy{4Q|$4?2SUos;`_>LrAEqY7? z38w73cpfA;PvJBOL|JoeoBb>6;pTM$$p|9-PA0z1U^Ct$n7-Gxy9Y#uX+ox{lzk@z z`Z$f0ec;S}iAnM{@K3aCYS@{J?U{Bc}j- z3GX`4@yp^XV}DqD8GBI{Uo}^^!B@oou=qN}{P6Kr^K;=z_;QkO0~tm;7W`Hc_j@b( zY4zLbuD5OL8JM%Dk=|xs5>9un<|pF}r_TH(PpPg|{2NZi3YuPq*!ob*(ulDN`aISU zIi;1YrIjLC-+9n8`Qs7tw{cs5cMT=u5cw@b@%DA;s3D^YvL1-uJ_G5jVr5U&h2h=@ zyzoUw?`vv%1Fvc;?_XrR&g2#VQ-tG5-p4pF%6AGNn;1SE_dm|?#iOp|TSDO&(em2C zxJk_?&5RP{Dtd)$;CG1=dX0YN^QkPgM}vny0A3cW@buputJ;7+z*~8tzBK{-0c=`+ zMYW+F2}eu_-|-H| zoq~wD5Z)vWwl}ourUtF4PKL6E_p$djd`)#2IpQx%=Q;E3@Dj;_+~LrZ+OL^c1fmi)Bj71_r^c)KEYy7J01&>CsnWzG2k1#YKv@lyjNLdS_R#DI^GS*5 z77YegIxzEcl;)+T6D2LfB2KOk^AkT(po>q;WPoNs$hzf_ZFcH602uIwIeF+6w`SCD zHLqrhNyLv5E=|*w*%^Y8#u23>Y5?l`$4u=}@QOBEY37ucK}@@zrfYgY8Z!blgW(n9 zy?z@GB7yl>phY&d+iGTn(p?9=d6Mj3wI}LAiBPKR+5C17pDlVP*jYjk;m^`Y`vMKP&kXW;fjWE}DTA3d%(o-_X&bVQ zu?4&-lpjQIE!l>3jAoJ4|LqD6Tqyk{nY_<)>ff5#mw3{_S`3cN)j_ff*?_!65uBAWn7k3dYn z68+|{br#yWjDLuwINlJ?Avrtd{MEjgC#d>%?(+wn2s#p*z`PHPr0dP5WZ2nK>Y2oW zQs*_VH@v?7a($)Ke-?~32$_$pTXr;Z1oXnFwz&PsgkZH|im%6yKs0zcD??ia;ml&e zO=)-KZBpBGIKc7NbiDX-;fr1y-97c$7HSsjK8618s#5AbwLajUy%G)YxLef|r?=w> z$`}LQd78%5_uen~t8De*Xl5E34jA+%YBD0L$n!^(lqawEBqat@9ML1Dds6VC<2r_1 zNWQ*`YmvfvS?nd3v4p+h)bs}Mk8fW&NFTs{JcNmu?i+xZf{)a2`q4a~jHLD(Edb|7 zBR*t~%O4^o#XC;2qvV=IZT{sX)}Ur^$L%_9^7+^NN{=WArth!g2_4gf6N>|-8|)Da zf3rw5(_?3_>}V&sLOt}YM&TiwWGr2Un2P}S0qvOd{gDL5;AnbbzDmX1KURZf6Zk+W zyAaCM`?EX-cO?DZAJeb&ed?O)S?GOh;laIgIoQs=bUs}zn9ir_Nr`uKO#Xx)61wGJ z5lg?UuB}qP{!q{&G5kd63TK3OvZTd*;%$h_oYx6H{Q}YnHgB@j_+phPWqH#|D{l}T zESq)<%(Bo_q?3X<1kji~P6OzQ(gv=D(z$eGrb}daCikt7+y|FXIowy-ZU3RC^f(Lm zE`E?g--mmFMH$wcfg&5_i*$!PZ0S%^H#cez4QV1Lz$QKTY}1e-UTX`sH7oGGHd^H(kO3~kh$C(=nKc}YrqPEh~RC^|5!3`TM_m_?dbrH$5QFWc7bymwnYkYJci` zG=7Ft22*dR*18uzY`J8Y#z0riL{{r(>?T>IU-taTu~nCts5vX`;`d1QS}{h}TiN=W z0Wkl~&(S z(exeTK&HtjvVh-yJ`tPeE7JFZf_QnvggxqP%tl!f*@1MQhJpxbbnhaQ626)TPSP#a=2R(@Q388^BF&&V`=yZ-PH zE?X}s(Fly!7-ud$)Fc{tgu6!Z9Z#nMj6}{tox^3X$M0zR%C09aK=>!0)~q}TkY_Iw z+WK!lLV)aclAD;*Eb6kC)oIMk?^wG-?U(TUwpA)5z8q&6<7{A@-*$&eqb`@$HIi5& z7Riymk|A^V$B*SF;h}aDE3>+lH(thLaK~JqJ6)(>=>>*6kp&@hX0Qj`>Dw%C><4cQ z?GFUYi0VC+s*%3qXze&Xp_CFaBun@a8KW`pq7m8GTf{aM@-jqwBbR?~hFrwfCKlnJUb!o8i>y z+^R8&SBVwl2vzvsET8>;I6JPUqZ}Qs5RaM+HNAaP-^Z+gdvtg<(c&uwf8VZ;JWvSj zn5%O&-e_h5$B+glffM+!l`yZyif_=`v3S5-#Wwi^a0W%<`&l%d7fW57=WT_eE&nAI zMzj0r-^5RCP|&-4*@Dj~SCL?2H}s-d^ryvLMFGhh|BIToprCC_P3!pW-0?ebXQ*k3 zDW0mYYl@|K___P+%@T{Oj`AYVM|MH58OUimA0vh9G1sTH)A-Vo z%0yslrL}xRN-ea;JsoXnZ8gCWj0MmAHncWNpJ1>(@#}+V4Kk}YR&`0hT+s_+JD+wa zDo>b0eLLnPR(2N4g^`XRF*|zgUN1ya-^Wf|&el7gmKuDDMG?x!EjjBJNxM6#TXlC?)oLHvI3(?dj763_1ZlbvjBwJ?zAnyHMvw^a+J~!KnNgP(z z;M6Y^sn7(e47%rx$aJ}%+y~JUmmxu2$QpXLrHMdYq>j=}kVfMT=MMSM+%{J~NBdT$b9Y{AS0v7% z2YC;@tIguC1L#2M8lu-(*B64ccft+321ZJkM|v*Dll&4c83-NSF_PG}E1J{D3XUJE zy+Et?P$Mu60A#}nerX=qU_Q`NCv$cDYws|1Bd`7)i*mVa`9M=jCQ`Izy))f+Uh zA;zEb!n`qZ=|+{0!HdH%eedUbpXu-}y6_eweTj;!lm*;Bx9kHM?Ab z=H*g9A|BO6{32cH12Vz@qBN!ALYWy*z-ZjdenJL|X8#XpWN9jv*G360GWbGx?Mz!n z5cG-+8}QyT!>89zm>$!sp}Qj0WH89kTxpx_7E2R(UPOI`!gzr@*zXn<81F0+ij8xZ zy6uB^y1L!0$+k;ADd%RxiO9+*s)ZMaGob+(rn@{BJ#k|+i=n~7iMV<4Y`*q30guAn zF#fzBb1J^KJUnE0_R#%VQ8M#QsM5#c^jj8mAA@O&*3#~-{YCjRvN zt zMZePjFv9#yj+~w2`3Unn=X|a+J9%(sv&l9D%g*K+2Wm$8UP5C!2Vk~FriE0V=Z9gw zH?o8xEbqJ>Nnfb{kT=nEMnb(gxozOVxdZERAeQql_F4168Aa@UgtjT+wRMN(;ZGF# zau(-HW132R=O3qDWYgnT8v4G&92XL1Q-7X`vYYnvd`m{}ZCRxmpFsoeU5_JJ@=?82 zMb1EK^T8ZogDC{n46yIWh!tINkez9b*BL zbMPIv3o90~?per^lWExd72n$H7`X{ImU=7Fuy{>9sv^))k5zS6JtI9GB0}fna%#>pc-PQ zmC`~xD`>9iPTe`>^4|>ND(B8q^&LhC=>6N)*VFY{Cjf6a{T(ddpeYu9rPI#pU$q#1 zu=>};mxU~dLRJIpvI`-QGfLbwS7?f~O>30vg&kb$3fzWkv`1a4_bA5}>b<3jDeJZ4 zDdY|zH+@lwawWTocdZqCKJ~O6nnE&8yT?EvzJ_(WGBwzO=O3e00kj5vDA$Ol5D&Jx=a3SPMvNADxtsh#f@RHC?a{cpq9bH&7CJD$M%lkmNyiVR}$`ICDuc6E*J%%22K-TTC9_p@$+bRha6?;uEP}`jyE(Lh<#* zByd*wT$MNGNX0XY%q`(cpnd#u3Myw%utx>8*@E2oa0(vOtWmaCWlFkg zKI#zvMesEw?m(>i$Ks*Rf_^1bX;u}b^rZ?B4TMIyv$yK_l?!a5o_sejS4$6mAi7Ia zRmak+_^Sg}0qvQGVm)B0<9sz-XHMH&rN>r<7R7LcC^`OG;T_aAM+I#v%%R zn`hB*_ih-2ZL^RPchT*3A@yKa%`M^|WHppm|i93NSF?4jxA^=_outaYr zhX8X!GH-PDnx9|OW(biFA~ciqkkFHEnWXpMwv%)$-{VcsB!x6dH^WHaCT_pK`7=fG zR4~+ zYg`E|cTb*%qeXE(L&xFq^<$JNU?2q8QwX_Y1l*kzTwrRTnTuQ}c@t8w_Y(h5ph=R^ z3_Y!r45{E@ThQE%#m=LmNoVs{z9+a68ejM($;*C_m|2H#vYQgT%WeTlC04^7$OQyC z*-Iz#fYP-F$u1Om=kkyF(xx{mGauV!oDAA;70e)SP0^Jyy^Js2F@e0OxW5J`1}Ax^ zm_=^s!aIad1gOrGVe%M6t9NnVMX!NSfm~kQo^RP1yGlDX3@?VXls_zeYo9!K-d~w5 zBjanqgw0ltxBhm5wA^KD0G0c-P22B5a_Zxs{QX~jtlVh(uy*okEqx5gb5Ha8ko|Ks zu8|R^cEe<66Xz8poxLSu4m-7*{BDP<+iJgC6M4rH#;kHnd5}9-<5OiB$@KoP;55>E zU0p1Od2&9cA`(*oBB^W0fa@ZD6=4u8Jirmf5xel6-@&2HSwbxa`2{Pt~J^s~RNY*TKBR`VhFXkZYYp z{~*x5?WnhZv~QKVugxCL$%d7@6D7?HJ4jKU+QbnN?vUxa=EVF!W$99){857NuGoDZ z9qlC-Yp~A_lY)$K`sxt)i8~E6v091`77*~iNbSYkL^sGyy^0nWsv5^?7?AMyL7l;0 zyr-ljnxnuRo=BF-f=qQ0zn4H15VOzoIej5gNLD|4u;Y+Ds3rPev8<j`5w=3yIk@FM8+Eg5;;A5&EC0A{kVuM=%2esA+K|> zTiGh-E}i79!6MQB{KaNyM$?(;zkmCQ0rx?YAzBMMAkrSPCFlj`70A&zIfOIUkTKc( zO84ITnjpkLKcx^C-f~(FxZMvoP`+b}kREaPE_QS5+eK~Pmbgu5Kk))7rV3s!YiefW zqk?D5|0C^9;G?R}|Njgm5CptIK~SIujU}m|phSrwnt=)2ff*JNK}4a_S`-(=8D(G0 zjOIEFPdF0EQy?bm88idJQlfD59yu&99Ry+j2CS%v)HpL1uDpzZ(v|Gj>`UWPm8 zo_p5kJm=XTBFSE^f%~>FVF4A~-x3X9gS}t1 znJb~2jA`wsAXF@1FD9nG)XkqVCileaGA!*&d=7A3rRFf?=snSZqm?j$wNs^+m~sMc zb3aZo;OIE&urqylbM?p8pFI6($)Akgbx&iZK+bUjM4TRo<@Vk}fE(YYrS}$Dw7k=B{*C_A=MW9=GXHBy zKW&HG2EG2_o3%tY7O*@Q&NQgTO0GBW-A$q^-{&!LIv;2RL9_nfLcu9W zS2#UmxZ9oikkho2vz+**R5;Xp&aFZmCNN=o+BCNX6_i3#+z0CtSgy1}3&+!gZXJ_w zCcX4w;KcnXu9|cGnmbjqKF?Yx^F^h+Qf)i^iU?!(S!yc(tlA`BAXOn?TUZNQQ8)O4 zn3(9q^?cAi>?gr03~5O2Og=MS2O2Qnwhsc=8g^iLRoTW_Z+L4t(AcKfZN;Y8PnWc>KXE1U6PWKiG3TW zKEf(oLRBm>P1SJv#fLSw4`M1~xT;-++l?k?R+hym<^waYlwD}2mzu?Rj1N@T428lW%zi30b z;UG3cWi!Y?uucUNiT%ww_(=k%77`jV2g^U+2-w^!=4K#`cyDaQTgpcuXcJM956Hch zU-wUu@^$zv9JD9bn}nuB#A{7*drePGyKf;*Gb_dqhJ(F$`RGow)_FzrLcligP+555 zN*fB_Za$;X|N2yoibF7tHZ(oQZv#S3#c)sUwVEpfq4KdrAoDgsL{%-q-(FZ2FC)c4+k4@szOo0Yyr6z4>8V3S^yNtztV)909p#_Y(%-89S8 ze#oyw4nMchJ(jMW<(_yfbMTK4n}Oei?J~>hJ!QaTbfNd>?85^*klX4cYMArv+Y3qF zjR40^U0&e4W(N*cf5EZ-iamV-4(>kw4H*S6cfwTzcaD^muZe)Dam zSCRfvF^0-*=XlgG@e#63Sk)zwQB`p%YKYW!ya%=8*Z8AV#Y9d zq0s9X)B@xUK8_E|`sc?j3?(kR34-OW=3WbZm2Nx7Z**%<)2Q^Je^8q)n?ReQcilP= zd_%i$^8!i5@Ah;D4dfze$gbcQ6nZPP$-KF(;q&qS z=hg1KY!k(5;`vMymp4?rFEg6FoO!gG8Pnw&Q;NC@TtUOZz15;z0mI>lC*am+{eK7@}x$i~wp~N3cShc~E?HPbw5{wCqUiPim>|ovggIi8=BZ z(u51{3lYRD9LN133Hg=t`wjsxWuFlu))rS4zrKKW5ElCeP9CEQmg36!(~5@#;>T7` z6R-R&@5QH9*CVo(y%(>CtC=f#Q9W%EPXimOG>;AE^)$Y2SN(#d=Dc0q@HRJgmYw%7 zrGEa=%y5R9b~;h>6jO60H=%MBE+c>E*7#=9>S&ycxTm9KC@LH=^5bopH7m8afMzoI^Je6m256dMm%xz74l6Tfnvjr@X*Ws=c~R% zY&vVieo3R<*IL-t01LukQ_xK+)kIj+5$gdp5Cs?thRCy1X z6B>qdm0vkGHo_#Cui|0{b=1Z2#5li-@(w)%A#i zA4UA=?c`4+H37mXo4zKRsnkS#wOE9e@JF!h5Kh7Rj@)sA3ozge)ASc?h&g3(2GDzL z(8!W?&~i-0DX6P_5L9X|jlnyNh%Ay?EINnI{Z@2D&C723DjjRVnc3;i`8_{G=9}d> zmJT#s9dxjf7Z4)PRvnqCDlKJv6XLlCS%cp1Mx1qO=x#Jvrj6GrjZR$0=i-gP8*iFb zH2DatT6vslD)>^ptFWqe-~5%4^^Ks^qyjfHy@X!|tF84qGnWiTPoBZEr#uuAMc&EY z>ED+!|Ax(^f&XY~GGu<~q+{{XW#iqe{T{R(e!u7N#=XcdCDuq2WF>!@3_qT+nSe}e zNG$SvzaQ%7%_$m>@-mXX683y|{MvbQP8%Qpor&j<5TRg=Qr>V0zH@Y(KI84r`)@8i z(U15+B~xR0aNZ^&`C({&7yCnv{r^@jorV@x1m|t$J#;ErI|RpEl%Kfy7aBoHY>o~R z$7XoO-j(D2D$PVvxA-myK={-8oo>5#EMGf2Uf~!2)<(-P-V|@<_MAWg6m1hU*#tUS z51k!jF|Gj3M_0t+n}lYQvqsAxw`a>`BrucM(BDJ|37a1+J3PBgEg%JQ&l(O?W~>fs z-QUQ*1|nff)8QwBf;hD2#*YeG^g+>^XqAXZ!k`cJ>HlK9<_&i})w8|zLHq}$3((F_|fw{YXAE-Qa**GdZ2Bj z!E)Y8_e>Zn`8v!>X0S7n)O8vA>}$-gn=!%a@DAja;@~Bxa_5;ico<<5ZX(=m9cgZE z3a5MQV{MEfcB^pNkEE#A9^C*8~zme3jM*p!wlT3HW z^|~Ws4WPq-g)=zKm?1<`gl6G-8324PINw*MHKM3dxnbkpH-g4rV6E7cRehVnNTWTVu|0jej6o#7i7oTvk1b_GbgxO$_4lLKHfd*w)PwoUlltJrqmLucVEt?w?|FP*H%zZ9N^Q2LY_M{0( zjc_7j11g4((FR}~`kfY*K!j%X%Q!Pu@{k&L)`h#R9+u9%X$Y!n?S-}>F&&IzZ0@G; z5J~0D|A?i9TuGe6y5L@&V~~9NIH! zAX@ewra)@ReEA4pk{Q-YysyR-o$%YdXnNc^YU!U5XP4cLXv*y?0gewFnh_#5&1}^b zWSZHOZDx(`N7@*izUCaPlw<#*(eLeUuj5dBA&uy02)V~bhs)rp9;AUNCKN?}D-ueD z%Rmz^Wm@^!NP1`_+4q%&=@fmEp6-j&S=rJCw3JeK14cYj;~` zPd?Hq5&*VF`*6~ddSS=oVlnw_P4_$5^d{YRq7~`9B|!97NGHAN7(N~KAI2bp3e<&a zpx%35Yoy(NOXm!$(agFx&oqtws!T>vq(Vp=qfMbA?C?9%Pw?2$^M3at0S zRL@vPGG9b2OkC*LN<3d`P`w0@wr?5YtO_|~+CU=adMGhX0UC1=QuykmJ}5k`F=wvdKq{y|GwJuN`b)tRXln|4H1?o z+Qz_5fCLO4gx|G-Q2n36$eL*I=5lg7l7B7}R{p2+kxs<@i}>kfm{|1D^ojgp4D1G& z_+ko0EstqqSLVDmGDPS&vxX>Zi|NrlkIP=$b!rpS)&?6kqo+lc=!`*0YrLm@Qb^Gh z=J7Q&S>9i_ch3P#jLljK#_I}h({Zeptn!wg&4{@8PLojzaPX~L{yXI@`AjHG%+Q`g}<57w#?c2;kXs=ahuds8le)ZWmVwADlXLp7g1m>8P+}Uy3`?wD)V!EaWJseyp~0nwdCfK!1uDHNyv1T z)(8EniWpY(F*d%s}x*`dI@)%)|U#5})X ze4i>ai&;0)U8%;`DmICcoa0Y!);K11t|oR&Y=kD&w<@r|s%+J)5I^RYRl98>>0#%E z0tXR%#(GgU`n;;Z3YK-&sbK;=lE`g(J!+Cn1&iM&Rs}Zmel|gszybKLIipZv%7yK> zy(J9JyKNz4bH;@&oPrXIZu8aDao2W!Ar{>kpC5J%c*VOK!s@?p0RH_N|H$h|%fc^^ znt%PF8&tz67T|Zn+!k*d;n}F^J?UvvBWHP+i*ytQBMXE(A9FpO;^%wm6hnKy3XtX4 z_uKpUIdhU?Mnnh(MItF2<}w=EhuG#S=MZ2heU4SrmIjF{e~c32Es#QzA7cZ7&9d#W z5<9ucD45zTur64($66p0UDm6AbM}-Pz@4JQQlo!vV7007vj2NVUf>`VfIMj!_+)U4 zVBotdb@D%c!Pu6-6;%K|$Ny$%rL2B7ylzhLSyL~T(Uqy0AexAw;Y z^i}ya`nL(J2|}1HDD{8OXlup+^cDG^3_dm}JsWKt?-z`<{Bf}Q0sou91;M~tQ$=P8 z8b>=bj#fnHpg{9Kt}3twh_e=~^J`2!KJYzrAyKZbT|Sx7J`|`8>Y&MRObgT@njU-t ztCk+L$LKch@|(Kwgka#Hj(pna{^wBYo_I*U=POK*&&4C$t2g-ESEn<7s2_{zF9Lr! zn!A_z@;8$@;J@K(8I!uVU-qByHHOMiwi7b=TEQa!3%;(NJ3J@eCUx^Pg-7x9S!&R% zCiXVQF~Ru(zl6Dy+%rD=vEE!yLf?}FiU$x{yMqq=SWgH6uJ#E8$hK+}ufST;3!k|K z;_o=4m7K@r%Cnug)Gx4`0WhJmx2y$&{es~D-8=#VP3k9~L z`?V4sDX^9d2j<=}6=XSWBklXM1q@tueGRV*uVh^b3X`QfZJ4k2zdLKUofFfRbad6;!z6dvaQAlxS`5Wk3ic2^EzpMPq6~uRUgWa%}{Rz zRe|Nan{_QehPN}5YZaTok1>FkCN^s%KPDV+CUs0~AR`8<)|LG*tDJJXLv!Yl7YeM3MdX(d=QIvYS|L;TKeq_Y8jXOfrI z4lsR*EKVKM)%h?64LDE^?Vb1&AbF6C8ztMc#`TRhCaUlYP@bc}sLsRWrMM^%jF~q2E zjt)+VCk%IeiXI3D8F4P%7AEL}rj&S-zJexDRXeq2BvwsnO2Y=urm4EmjQk*6J=4yK z9nT7IM{TWNJk5Mno-=oqzc3hf)ebNb@NDM<7KXJN!cyW7{?o5M=V-KoKsQ>$2Jg4h5iNJVOr4=u#%Tv<}I(>gLKYDW;dLiGb8w)t0Oh7(4M@N zc#&@~F|>eoK}fmz2$(9(R>&Dr5Go(i-lvA7go{m<&x3I|dWhDm%IyN{R^w2Ux)g)l zEwLM;&VWLuR&44yV~Pp;2Ecq1Yac3)7Tk0!G2tj01;aJ6uIMf-+I^oTQQTj1E2QjODAY6L{yHsCrJ`MLgFbO(2>pf*t@gHkji> zyZW&z2L_T2GqcjS*NIY&&OIN}70>rnHz z-9vHRzZgkIN+o!|rx>+sEBbFD^DvHYKZ$qz!o|!94g}^9NuaK={Qn;B_$Tt`GnL>8 zCLm#q22rq+eok>U1?xq$rnoba@``Im6*8YHsDgL&Yw!;dHh4_uM??b zCEnnNFVdc8C2pXyA9A7+*Lr?H-p!cHAuZcrC4vC7X#$&x(*)ekCs}#qYLpb&grC*C zQb_LAK$+V$7sec9z)$j!30;V7ouIE32Qblsr?At5S;RAkGi#GB6dy9Wq zZtvb?oNw~Ogd~zUn*pz#lNs=0eq-9HcncyQbK_GRha5(3Ucji0ohfT~FO8T$;u`hu zn(Le>t|OzqQ>`jK%wKpgV$kGZ@C7Ma!4Q2AknB?cH<@IZ*eOl+-q6P|TJ%--=M4Xd zwYia>JU8PI0L6)w8RQJwQ>7%s8JQpVxH9HZLo0R~H6r9VNyvi2`<_&=VP`A$ zV|-2xzEM_WQ``>lC#O%N-)=Xf2!zBB`^3XzN4i(O3g@`1e_-LY!ZWdDDDe z6FSM_)(JXu@TP06WLPry2&aw@1GWAI@$qT8bt^VrT!LupWbvf3O9`i1hE3cG&Q$U% zbMUT*#g6y#v3C|g>c3>ky#1_)X)KBUmKDuJZG^{X@!GRHidf_>LA(>JzOGZiyO;KW zk;i5MBa&iPD@zH zcIZ@0#Go%^%hHL*;Aj)Dm}qN$Xxu{xA&Lo1bIm)#Un1xL)rwzm0g_`VLJq>zP#Y{x zewBeX+JnaYDn- zrO#sNgbbhfe@MCIL3sM8ef8c|;GiZ)6#1Gk_auEOth&j7lTJ=@9gnEAIAKVrWfBg- zBVQ)MJZIKUUaZaO-7MXAngARE$`jOgpYf|<>H5yl(mgHgmmZN_x{IXfF3Bw2G+>BD zcu-Y{p-z12CQBD;T)H`z{)eUeD_{L&>3(_Y(MuOQ$-rot#^^6wQf8W72i40I6!S8J zG-grExh$A2=)Ay{!(Aqu%Cqz(00HF?2W%wP2In-~fvGvbSpieY znsADgi{!;YBK9uD#s&mDF^6`pM6or8wg#G$AR`b(O~07Eq16Yot?C{$txBC_@IC$> zO|)7*{uX?UQeVl>tpw)RELe{s;6s69<#nZ{Ao!h*2j z=93`}|H7Z9yocbc!9KOvZqPQI#4Ixj9}=gV=R*?m4>uxw;(%5;Z;FWM542!0JE& zw{gdPC;LkYqdztoWX0PzN)OS0kpHfu$$!_*PUoT^ml>B#a4S10vr*e?M4hHfPySN= z{AF&}`}{n04gORk-1COf1ATSWB@sVK_63+cKrX$YL62SOZrUZofw3zyzngU5VE@rz zFPUY@gDKp(Nb}5|wJ`!C%x3-2&M$@iJ(&_zG6>dU&P7<`;V#S-6q$UaSP9i4fg|qm z*9nYPYh+bqy*IMY`}ZlRe;er^?}M;=4O|hW@&Wgrhh_Xl1aM+LSC#iEn)S5L#x?a{ z=>1LIjs|@Zkw}-x%PrsEnRMxI#v9RYr5y)Mro7qjBWP~42c0^IFxY$4{_N3T~1tI z=-540;sJq=C=rVa7%R89@f1>%xPF|%k+z+l%AVb5N1SH1Gjj@59@Nm<7#0HL$iem6pcYV;tbW{-}yydnS1R3LtS8#9RvL?i%l}4i%n#C z8{^e7p!ih96WkG-XB>xg3jllbCR?8>qJFDaq z@D;KFg-qip<(_&f$Z}*>;H64vCN3go;Za~DtjF+=$Q5e0P}JHX5^gCa82uNtV66S1S~{HDy?fH zwXWNTXi8X@-ffy_t|s(thpuV`)1l)tP2?4rCf3+i)e2Hi(GpEOK@$WUq?K>qH8xOO z3veg%n5Yz$e!jO0IP&OD8QJvzmAa7JMhREPG2OwgDG@x~PABeyKSC_0JG{0^vJsq$ ziR(R`A{6n(wx{Q}_Fw z<(rQ-if&U1*guf{g0tk_xxOI+Zlbrwhh#8gFJEXSPSy(3a~emHOfcKmh;*f<#0HE~ zAL$PBj?==LpjYpm+Db#LS*0rJ4pST7yqJ0OH}j@6be`Y;@lE=lVTNb~B0a|XQ=MRn zcDhC;;;i&(%EWmp^%-}0Sa_|?5iVWxkl*I3Z_AqBSLw>y9iBa-qGd`;PxPmBP-9mc zES}|)`5W{qUHaE{Z1Bv6HsaF(u*ft~KIgf=xx-w@sQyJLY|% zlDX56l9^O&R$^}_(;YICbm^{hEx~XvM6&+tSjPw*U!)G8^+0I2Skp2Lq}HVXZ8&Z z^cEKe79QfddYXkhWT-1NQM&7$H$%>AZh_wxd16crXm{U`t~LjsVE}%T(LQBkVKp@u zkU2$yIypHb zD>-tKHk%%74cM&%GYhz;tFWlPI>#My>Jd=ls=}j%oelUzLd!_%VS!q3 z{^$HfAN)J!A0eeE+uW({!J`l)C}nUa6axt0cr8?FDqDFGL}t3*Ic`tlsQSUcNEXmu zZ_OL+b{iNR1f!|j{Jyr)7fmSLi}GJClRsu8HMrQu7h-|vHNr4fV#iG2^Em}M@!65| zm_UShybCt-j_7-rhL?ZZd|0|4Sw+zQ$4B$NnR%}?UBl9|+F>xQ7r?`^=BCj{%!^1n zV0^l4pttjzV3@oQnD_JbJ3Vy*c!zIijLC_8Ue^NOas^(MghNMIZZSk@wyO_duX`aA zj3|gTErhF=H6sBU3SBHdr|$=cT_9NFNbUFk9SrkjPBf;2sHFD>s=o|`@;|^8igVOR zABNxApV>`rLbH+D)ltFW+D>B+=}zzF@2IakF!Bf|7eP32v7LPgacp==uPs0?c>w+s z-ud+4-G980I&x&&*%iDPAFkZsC_Av<&~qk%I1!6dlPV*rNsz%4`r3$Wim?~Lh_a!6A&JhY^d6l1#pv^kp*iU*zPh0#=R@#0ZumwPLb(?s3X%%Ls@!LQ}m zB`am*ap&#F=Say43*1i%v_-Gk%umfNyq4y~ z(GYpOPN2p0#-+JPUVL_0owaC}0#W0?!xXSfjgSS_I0x)??>h9n3Ik>zAd;TBUuq5X{C0ZS{%GI} z1#y?hUatr~vS<#lhyEvUU?4N%d=qt!gq^o!oDfZ&sO^QT2lS%OKGrBq)Q~N{zJ>kE zXa7(an$44Bn9b|ZN|f;m0bN3B3ka$^-WQwGpUvxS3My`xuFbQ{4zhVBYf|E)0>HEE zVEk%%lxe9u5gYus`w9C7Vi~iHBOc42)mr>cXc-HxYo`jilwV(%lXk;1qQ%PZncgX9uG*E}KdX&+gLa$|!SW36$CTM__%N&;ddM z>!IbwiFj|gcjWSWsoUpP3U>x2+ZAbby#5fkW=RQ-zeefQwj{ zXkcrk#2}&T`4p@gPS2t5bHd71zccIpMYph2B7Eo0Vz;zKpehz2 zg55h2jQ|1kw!mNnlN@h|q|1(Hw*MRNcHIs&w=p_yZGXibmgO5;d9(XrsJ_*mSfXPH z-VLk_2Uh!C8*PpF*@1k?qIvJq4))RB@A)_Cd@Wzw0#X2LED(xc>zpI13(Lh(*=*}U zUpoHjA=m&uw9H%`;N%i^(5#9gb+Xb%qB{VTX|9kr+N!V35!%w?6@*K;VcQoGR-4lv zX31Ho{lteY+8=g~jilr7--7%3d?tLvr73)rj^a~n98PDmxJa{cV{zx2SWUD^M*x6T zQ^7~LPzZ-~vP)3b|B)pI;(riO8YDv!k?_8cqJeEhp$(^}apq!LaoTWu7qen)kq>N% zPJ;6!XThK*MLCS7M;TlYO-}(A6x*^mO?;r15$&>{8-*IGkMQK{3fa(TdMJ@h}{9{oJPm zwrNMrjgNg3xp_@!kznA;%PAmLd_hl@vXVbyO#rN;M|j!{l#JzDUEy zq=^4R1EEyjgbB)U2Wj?^o&5e1T z7E0Z%7L{Vpu73Gp9$}hHQLL5B2^QW@iO;^QM1NBsTlE+G-1XGt3VW?SpsqEiM0_lF8qp9|s(y<&hu(B_pF_c5r z{w>Ji8;o(=kOHGE^#|tld89>-(eIKU;Au%-5P6_T77uHphJ^8q52wOVkK%=b7{=Xz zVcaLZA_0gi(qTFy3UEA;;(<+d1{9PJz$or-OkBe0*=%p2(Xzfld%^M`HgU4*WJ+{{ z$mv@uk|n)R1**$(^RKa^i=7fIuPnH!3nq8yitIw{Fl854Evvn)NPI4RxCNUzvKX7U zLI8(uuwMENjfb5}i#_ZE=%w`Hr@G=h#gV zXE4o~QOC#QC5$%2gA z%RQm7v0s|TAX?2eW8CD3?`&G^A`+h#w^$1gwcsKn?@U?-4zx%rK?&5?9d4N2C#oSn zz`w4Naeq*q8wT|c)5J0>z144`wsD63pysPF@NQ-#qEMkJ_{vJAbz;-Gj3+b02^1qn zVUly7GepLQu(O$_BXMd2ow2&xTIe~+Snx@ z+wUB3|sE@C~Yb8(z`v}c@0PD?kG|+pCu`x6h-yyr~ zjai-f0}DzIwl!=Mw1`~-O5fn2PK9*yXSD-KB2*q z7GwZuCa+@;L&bK+U{gK9&2e((+=6R=UA67J;r&+FUwJ6s@G$DG7aSQ9wJ9L|>hUaj zUh^v&Yzj!H>T>~-KsmG02XZs8be}oH%j^DT)_bE@!|#Z*!rlB>wms(;93=03KHwVj zCYks6w0`A2Z>o6zq-2!QM!(1sn^2{`>)^yf^aq?BxT2>Gp4OBClQcahEQlB`QA@0dEFb|VKWqWkOZh+BWP*O;mn5``bJkLhXo z>$#@(0&jnFmZAFPHH=JV#b8>lU{by^F}FVl(s+(tY^Oa z8j7govT0oFOwAA^%+K-X$E-BPWFfnS8ubGHX{0}n!cQ3c2QP{`tH4k1gP-1zqz@j& z;HSGXQ2wpk>RF3A1X zed&NsaT}Nr)$$MV{$?DWt~gfp1ZN|pC0(}e;T;q+GE}Q`9}bs+lbQU-VQc7qyW|bK z#hu`|iv>rp<+Wwpia3W%>{N0F zXssUlQ!DODg8*pvKJRV+Nab)Gvi@uF87%<1@1I{!bj>Qd_eE4Z5UWjfs0RK6p(H zVlwjY;0RF>XPS<78_=BAsZo zA6e$RK{?xv6^Rl!aY2-x4_?iE*`7o1@nVb&X38f{E%O51K|M^h8sq^Kg zlbUP0D+z}q40JUex-H(KqjcUwO&cct!_M3DIUv4lG7;3sOso{CsTt}?W~i%;0d)gC z;=B1)f6WXP^h}eS7rW3--M5h^Y3BykZ}4@Vevm9bTG>LIq$75Pb=o2qvmWbIb6av5_OLfF#+ zD=~bV9~oKBO}3Ph$vJ$7_;NAWu+f_d$?&9YQXdfM*o`<)x2+@2h^UgK%nsEpH29-*g4R-5RKmk#~{CN zGM?U?WJIuazen>qr;Q@fnf8lg57TGe^JGN`#Lhr!NhGJa=GD!`afqv}ZoX#9t;3!9 zD8#g8JGa2Q;^a0Qxr*yXb_R#dEO5_?NL91S0KjrpBCR5sCMRDF+9TeLk;g#{;6Mel zViU=9K!LlC$fOyuXmTKX;??lsQRf}ig0ru;f7*kivgdqa7t6OCA_{;0bHa^BCqsn9>FbWhZDM0QV-1IFTpQSO;_9wItcdO7@WV& z&(GYZvVU2Zx2@zIR3S~{+u`!5G$#M_a0;1#*d-$nCb4uR^}l{(B!me=)xAW1-v;a7 z&mOdV#)#4W^N$GQbiHq+Y$Z-eT}?RqfOB=HVA<)`g6Dn&;<=fTj@Lp-%{uuoqR(F< zoeCOxvN;yXLe|DudNIGbs&H|yaM|lta-x{`JrB;Ql5HKCJ9n-1Ib`(FX z?P%NTLT$$x1i?aH>CEmFEHASboyM@tc7UKbTHkd2PurpUiS>nIEPZOWDhy{kzQ?^uZ79DLMF>;CzLvE@nRrz{*V4{ zT&%ID@)9aT*v)oK=P~4*b~XRVzs|@Xra+58bN+>YeEb=~_z9)R1o^pPIy-f^&PwtF zdW)tmnPQm#0|JN?@o2eJeEZv-^%xk$p~&X2&d5)b%jTSGNRxXHXwzhEyD<~L&`zBl zNnZ}>)7iAOSQVVJ^&UO6;_-;kxRp{di&ua`tfXT7v(5b{u+1sf@G6=^INBt?Qxgos znh+M|W^#C5+{@ddCL3vKU;fhdk_WY&dFKt+%~Br1&@X6Q4Tp8AD$lnTy<*PJwTR2U zu@)LbQ9^iy%b>=r#2HLo3{Z=F8Q8?9`eN_Dn!Sh@W-19+sM2;c2ULnu2`_A8N0N#c z`_a)eRb}_3s!RN;r})82e~6tpI2HS$QyA4yMnL0%KdvFClfm1EJV*u%V??WTcia128>Mvu)CK* zzjpum`XQs5B+1<%JJn8toC_Ghn9Bp4))iLrdn1@3(Al>97WT}0-}-FoKHwk)$*|E*UmGy?V2tePKXeD|0IcdcvI$fKMB}mcm7CWIYgHL& zK)%#}z))wIXG~h(^-k4e>TJ1R(QVyj>bAg6UmfsPnKzv&{ee=mjDJ&+G2};d%x5zT zWmOuKjsM>$hyds5yjp4ptFLB(XZqc^9Y}lay{IqJ73b7Jq~=z^qKG2MyJEa97^tRl zQ828>BFVh~2LdkrFH+_rSd{F=wTw@8KuWL?CM5GFez|E{6aQ@}b^y*~-g|5Eo% zc=EbVc3@4Y9CE8zZIn;pmidLAQez6dLOpQMyr>@16%X!UOp8|=+rPva44Ol}f}ll< zF>TL}9phDT(HMHk0N2XKGtwj;jPV4|M&1R2tM`h^>-npJ%mq2ii(8o=|IJkvCA;h$ z>$Wkb0&>gh#z^cVU$SJKjV8f&f2iceUi$kfQ#i8<{J#PLbkyr<%$8FEI1N%Zo(usDemU9eXDinVR?@nXW4k+J4qcXtQ& z62GGp9QvQ&7j{oU)KI`{lkyDIZ^1gjMVAb-Ft|> z%c*W**A4{bP_H`ArXE+&zWTm#f&3KU8?5k$d({LO zQJ$a4^(su|J$)CKya{bfewu-&-Cp7YyzwC@k^J6Klw8SmzX$qA6^`VSsM zWnCZRpGxZwweEAb9ICI)yMwy=&p4eT<6?a}%+U9`>kAorM~wq;Zdz9{&tlBNmwoBApQkEx1YyimHQHsQ&;kpvK={mWLst8iq8@AnuN6v<(n zyp~jCqta6YL1%p^J)+*8yqC}bt3!kgMIJM`o*%?xS|73ceQ6^0p!33u$Mw_owfVf>TMBamVBc(cn=$$wr_Je*DA}3W3SBA~wrA^fN#Htskk< zGcU>TB*%$INw@vWE4nVklNkES=Sej6Mm#W74gS1a7+BtK_Y0^SV*Q{ zSc~4@B_f!s+2=`IG62ek>L*Oe5Y`gXmiw^n<5O`~OHF|aUWYTKp;Sfk&TOE9BTh@7 zDH#b3{%wGLq<+*`Ialro3nO_S!kqCl0zWR*=Ww!}VrO zhe9HQ4_9g&>C+ZGuXC~8N`A;}n-Wt3_k|)2kGcSI%>C<4Qn@d`fM4mrbT8Khp<}%8g|QeSd;hhlFm<{@e5L9{3>Bq5$9eMbNN|Rwq8|fQui3wCN;#*KqbrY|vEWJ8&is3)>NhCM@-pK>|C`Meu8p8mwEIz2DPaTfyeyqOoIuLf&+)0#p@xy2`nc#Xieg^d6Hl zDGLolv7M&m#oNg)i|5B~ko0XSBpZCf)jl(^EK>oJiVDM3;Xc1Y*k>dj%2dd35~dmg ztex%+ezgWBV)~F&jLcu<@q8SNviQoto1JQ1eHGN~k6N0>wbA~tkCUWNNGP2{G*zrY zCr0a!S(misk0EX(X^$LtyVW1oq7$>^h$uq71XRVD232XU^6H*nU05Pzme^@xO$h_t0W28%dIw{F-O&q80sgAK zaCUehA+NSs$ysy?%ezw|?ArelZmOLLH$|RQ9SS^~D!OSnGIG!z>E5^*c`(-_2OsJv zJ4y{k)@h7JgOcke*8cuz^y8?JjA10-?=Y0=Z<~z7PN8ob4)*Aep;VuQZVob2~dDGPm<)PO@Z2kYTUQ6}(}`*Nn2gw;0E>jCeqz?Xs1z z-v#&1lX?Qe_*MRirWTJ8BG4w?tOxF5Bp`FflN-KM?7T3s++rhnE{vVQbF zpns=Z*G`wAuEJLDc=t(G%r5X zpIp4gJA;bR)I1#~gW<#m$p#{^lrQVP0OE-ZO1xIAzM-%NdEKD2lNmF4&yQzi(6@w~DI^;~tA%2%PlhIKbRPccdC?f|Rdd1@3-zEV zKB@lPa?4_f8Y_)*tXaJu{Vg?PQW$0$BdXyKW`Ec)i$tn;8MAb_5E4^4q z5ZVR&LRdYrlIzc@4X4g_i;-7|e~R^BUs=jay$|5ipi^HhmB^ z-izI_`{7WXowZCFYyi|ayD3PA^6Cj&SSWQT^vCC0gY$lj5K{}3VJ*2VCxPgDEX2Iq zWzX(2GYh2;2~T_@oUUwUTTdUb zi7ZNl$7Yd?M@S9!!4 zt;{1fa9+e&NgG2MiD^TqS#8Jw##+)>(Sv`|ibD-J*14PFCjg^wY(WuO)_(2OR&|#E zfBKAd?KQBx%aXa@`>R`9m2(Q;$fCScaNcYk0#UH6_+Tq>h$qD^5bip&Z60L(8F~Be zv~sM}Ni471<;6qwb?0k9407sY{;0T&-UyX~c z&K5?~EVxuMh2Ts2TX!e6So03`1S}PJHAxKmHh@YRbYaqPv9GrF^^>~Bmp zSX3u9**YAwujag2D`P-_)D_lk8qnp!K)P-}~Q{wu?Gu7s>Qlnb}&HSlLb8~gJc z={4m|vdcuM$>&ifEHCgc3xCyiUBW*9Dr&vo!DDmWIVJ|Xv=HwN%v_G2=re*6LUt9z zMWQW_UrY7RugQ>Wml<-6hM;UjJbJ%s33Zh&J@lqembK3D$+CM)JEgPKE@QliF=oiJ zvJ6c$A~!>pS;@|Tiud?7LB(=E7~;IxlUNOq;AEqfyo*is9^$RL?ytsFPJ~rc%1qAx zW>@6r0A&U_*Lzu(pu_jl#KU@CtxTd7agzcoh9O2%0j;0pRqmf^dr?(M zj-Os{?&(OGqWpwFM_Ms}C#P*Ge{a&ygwdtmdXzyv<7z?5U zCg_8xL*kmGyBKGHpArO3=z**yOELKhL2kpL)SxW?1i0F%nkSgWzU7c>fXX6M^sMML zb28@gfT-$`b0i+>QKV|~U{kh3D`eETOaL`*gN@ZZ8cCS9I7L?1*T*iAI>E@m%C6~d{02W8&}k4b1Z(u89wysmdJfmt0&Vrtz}r@h*xx496ZbbmnlqE(%qo`| z|4@b#PUY^etO2i^@l5#72ygu&vrKt^9{X9J^^66{^du)>p<~eqvb$t(xY7~ zYMmo~3c|hS9Ezn$lGJ4D$M6I4RpK-iy$xrvi!3p)5dq=R^1QQ}73NsU<7hR4Hx~A> zvTwm;QXZ(r$oEV4q3fE9C92s(OM(qqb7$X8yrDT4ui`mb3*XnyV)2IN8@9l(9eEo=tY0w5TGGQ5glevCC@IR6T+#4Rzf3bT{E|o+KtD_SZtepbVI0*Uy#vaIyBiE|we=z%_ja4)FQ1(Y3i_!g<{nf>)8GG3LFe77BN3y?)XfQJ( z`j6kvz9|psus>;Cv3gzUzW0@=^o?G7u1F%AledhC&8=Ih3ZEMrc^tcZPJX2^H&zU&}zs|60u>ld_aI?nOc%N<32y8BtwXseDjAl78 zkk2#O8zgDOjOmJv)+B~5n3UD#U%S>e(Jxuy4+uGt*}V8p@9S+wiSNO ztI}(LTDFR@S+cRU_Zgc&1JR*IWt{G@YpJV#Qo+r0lMgs#M+H4n&zDpVe&_QNnUC6jloe( z0{V$et*$t|q9ljWoXnL5Wp>kKMYbBq`EpfIU9sRg6tPy~K7OV0rtw2kpyCLs0W2`@ zO&-JTj8;h-bM{`*h}WWo`oT*6nU#R3-Da%hYO{8X1az9Y4F!f9aj+1to~cX)1GZ^O zRjvzeH>Ei=f1!n10Iud;`auO?Md|w_lrL85!@RIFu^As=NgCRL0m*?vdmY zyi8@jwc~|6hMdv?{1b6dfN94jQj4p;`@xeL!X>Pi2zcUtso0R*4FMGqSkKc)!7oSy z;0JcDm2CQ}{n^-jc5N~X^>)dD`OBA4dAZkoxBaBmZV1HtpoUUQ<(+! zjm)x(d+71(;{Kf$GLh9j*rUb0hHSoC-6{?5|7dlkU1pzHU87rjN?R(GEpC9v_-fiN zrpN@tSCJe%AEqc(ar}7@&@z3%f^?v1rfoh&m{>wYA1Q-~uGqz26LL{YDlI5W`rdvm zX$uDKFX@E$^&~E)>5^8mo)t^~h&lUn%NrqXHJxFIjVao+N`SsEKcOCW`pfNnXV=c# zr6T@g3x2xRWdCw*EcPDNos`aNdy^!V9FOTT$2gZkNa0B|4GvDw=QIhJ^WA%#6tLIa z&8$6>NFJtbLnH2m6!4bu8qkIM6ZM98*2Grl*?V2|LAs*Fz;-$ApD^}Sf_u#bz7kdFy`r|V#ii>5?2wA9R4m%k@7#9-HgC@J z-xQiRS+=CfN9l?iHp6q?{*G2QZ_8C6BaJKKkE@Fr1wGKGED9Q$^!~Mh8~PLd`6Cl= zayJQIj$+B3!^Rl|rhcD)X%gi&r*jUWBW>`Z?8C8%0)s2uLoaQU9tyJ$`Fe2g`nzvB zzTK=X9`Qf%8o}i5IfF#TV;VRN)i(iqSZ3_r~X>?37I|b_uOIVdJCb936Fvut-%LG6d}x4q9g5B_1=QtpP>b7{#U&& zIZJeW-$?pGWLq{z;5%zcpK~i~dR6vaN1hqMetPB++mL$y;1_}*NCMVYpCD(mn?ZT^ z=~P)WB@pWSiIu4EzrgoO#NnLG7tOL?kf+^lp8cYk`J%P|#gG0Mk<1s(#d@fZT)xO9 zw@&Vfp_0+t{LmY_b=cVis8mG!>wSj2hPyB45t0+Nd*D|XiK#;=myE$2dWIM{KN zcyYY{JM4Zy0+mtu@oqv$@ivAZ6VMsA`YKtBxphoYKr(-o02wbFziA}a-VZu1D8TL1 zts?qFF#)1^?}9iKa5g;%#sFxS?{J|F_$fEQC+;VAH-Wok4Q+V4-q9z7PXM;WOIDgY zPO?V!x!+0soqqR+0NE;-<42AxE$}EwZou{Mp19K&{s`|Jzs9VMp|%JHqElWu$jO~A`DJ+iex1qqPH(6vwepG&z8CgS zuctN6q(R_%C{``sgG*=jDeY0indPC7ap!o-x#q3ax9P5jPl9+!m+pN@v}?IL^nAmi z%d5SKznz!|<8XOM`SVV%O2FKU<#ylRyj!AIkQ+?I@IaxJL^GLV%)QU&{oG3+V8;jN z9qtvI{j&5X5Mv9Hr{>Hn?-je!p7=hPU36scWua7$dr>_MUug-nj~_O%%i>^J-R#~~ zh!Ga{(`{@$y&@9WC=ci$@m}gSRUgie&8|LN5W6fLKT>_c!w7h5am9Uc?AfxpQ0pJt3aA=p2ar-)PcvT!GZ{3?M^n4~fF8oM95 z{bfntdG)4Vm1kcif5nrvWG1bT3{-zb>-DsIyQ(@HsQsX7qtv9%5U^9;LVh!eW~Ma) zR@Ye8!f#n@#*GEl+mb}|Ah)&&oM8Gk@kII9??6h4{_#hxB{!7`a9Qp%%`J$&a63~iZdI@hrW;Cg0~=xwitHL#6OkQ zXF9L><-*rQB~H%ay(GkbL`Rcq#(jT%BX4wz-lr>`9R(SA?emn24E?qFs6j(-vc3^a z31gkgLiPkG$1=uen4p2s(z9x#Ohaw2>fFdMi3vAJ-xQorwlDZ%wIWtU#_pU|yExw` zBMS)&tM6gNOzeRyej43-S;nSSNqgIZ$TA@-=YL(Jain{eoFF;xmC!knqAALTIYCHK z>1&wwHANHLX*E8rlvMkKW||UBUx%0NEGlBO;drfR<|S3!SpaV%c^Y=^q{q&g8h6UUZ?jmxE*B?Lie-_xAocBp3Mg7UdLCU&vwG)nXIEG zrrTxP2-*$(8HXWk5lyv=I{hK$P~SZOGDb(mH4`Q0i)tONjfAv-o{BnTIr^IL zc%=CroZPVcSI zMPw#6nJ8tts9LE01O?#@+as;kK-ro)Hg%fE=IVX(|MY%1?{j10^**=hduDu)Ho{g2 z9X}%Jir~^b8i$&Rw9S$CW{$n#GrJYhtF}Z+-tc?dASU3BuKXaBk)(nECEw3}PjSxj z!_GHE6+`PHgT>aL<2bsA6pc1w_;(UjNjTGg$+sKAv z3(_9QH~Q_GbKc3=FH<|3o{2fxkJiGGup;3ZPru@`Q zIq5?sm!N*GDVJzP?ZEav?3d@6^4$Qu%1IZh<3;@zrhH4LyhSWVSxZw^n<;At76NUx;Of5mKDXnZnEQxP5jo} zqDTq4L-<)skGBux$FHSNf9vk^Ta8t;pw(yd<0EzCL89%vUg9h_PhxYs;Gsf7L8G9Ab8{1KGBof%^eoGP@ zbNznBR%n~T)%!UuI(($g>NLd3C9*yIg0#%taCc+<=1he-yJ@mn;@J5`8Yqy=2cSqQ zu9_2O>KC;r>Teb`vDAOlOmEKAlC|IyRxI(b|Dw5GoWLiu%qK7A#~^Xk;$D}+wg3J)RiO+^vc_U2NR)Gf!`zW=C?C9fg=CarUc?x2R19AxHI zRp2e5sI?j_G(mt_tZWX?f+ryOyHxc&cC0KM1q1hC}>2ep+*df3M!fy z&Qq{a4+lrJlw?e4K`~)F?-wnnO$6 z`+dE4wM)VN4t(E%QWdqd^9fKs*9Rv6Om3wX!4mh@E^mWl;`T<**oW!?EMcHfNJ{Yt zK_MgPHeNMO=br?>+snCQ#^d6caFX(yX2`jt88Ub2VM_wK#lR4L(snbvJnu-lMIss- zn9rF3TQy$5Xn_OH3~(SGu0B!&NEK&e2VMEssV9yiI-;nA>P!pa`fNw{U2IdeNoM85 zMEv+=Ez3zP3uUav^((mGaxm^43pm{Vx$^Gh)3mtVK#+TzX$yE!JEu}HYIyEl`HCc= zgN%<+tTwxsN3gF31jOUgfm7#z!6ZD55$m$|8q40tOvO-V_kjlY+QwD@xJZ=*8#~-% z=&*InMmJ?2E9t$+92giyVt_VuXkoegIc#Y4rSfI$oWv1gOR*(`SugKl(xg45AFT1D zf$do1`&i>IBG&h zW%*tOy$737q+vq+^vzN~zA|2#(3tt3dVc{HGQNy+siXRjJ&Zl*@)Pe*jL`cXf54gE zXG{4}%wRYSjRL;b_%ar~^J)vBT5Z2+{1DmixNjh<<8&lCH_3pKeH@RH^rdofB-JKl zEL`WP|BE*NOl#7Hn4gmeE;oWyECR%U%TF9K!P|fOA$jAy{tMwf%D;^CZ=&|W;EiRp z&c;js#t(YI`mLwkISu8LV@?BW6bIo)($|0+#9$W_fnw9Q z4D6Sx;&giIVjfMr4yDSrNJ+j8{85JXkN4$4`sM4OgQ{=pV{*rWn`E$cMZD+AVa`=Q zqk%pkO+-mm`u?R9H4448I{(Ie`>NaN|@IQYScQ-YBC6M2-^9bps$%3}66u=LR<`-6Fx!!0a# zpTUquHR9|?U7|Wif|4* zFn>5L;bbS)b5+dgK5eMe=l!6W#8sw=rNUjNa|Nz6O}t4He*LRd|EfQOyZvz#Y8=Z< zd>KSwCr7GwS&g%^4Tz$OR()BI?ZCR26@I6AaqFB7x0YaHfd4yQjDK@3Yputeh7%?e z42R8(rmsfdC!UFV1ApLBRdY_c61yGXs(y^@@B~wdq2T_yiQ1)Zu&WbTnkgh&Y7OH6 zXn>`zkG(2bddo_Es|y@>5-_2eQd~b7rCH^XF0VQxP!jw>)vBhRvNDk zZ59B)9(WSbf1ZQI!1f*7^}8DD-jy|TKFX^RSN0%SnG${9Iy~<=!*ErJkah3U(3}l< zwT+-KvG|Njc@y?@h>nAmA|{}LM{lZz=M??k66{vGFJa>*IJ%5v_lI~^!4D#6(U?5r zd#7fgxbMCc8^*CQf#qh~;ofU!WUTkn^7n>Z^JbBw^)zNoqu~zqp#?BJHw_f8qy#os zpYF(F_trI1W9CZ|5?n0n#=xEZDKz0pvnOYX3}DL7J!%j*S5U)8sbOcj-!NC)cU$lM zeXX0z&}v+xOX(dmR3HBibHaUeR{p>|V?FWqe&%e>%a>8v^=hf~w886o%>KxmM>R8? z-?tgg8j^J>Fph)%uwYYscT9J${+=1@Vky(WDsZW!fz56^cY`mSuFnlcChMGX{dgeK z={l7uMAIiJ&~v2f9h``m!JsY!9y}J)v1`Gwi6a!ZBQED3Rnsf1)Q%UpndR*XG^u~R zcUeMCtkpPFgO`bawN|!;K^PVB2*}>-TQHTfoXuPFO53Su7DiDoGnFeKV7#YkN@qVM z`4)I72H^l|A~wcAgXFZPJ->)e{kSxm|EgA-6*jOtSqt%|1_EaNmeS|AAOy3 zs#?bO6?qzw(5yxyK-js`oedBgH$YptwmtJ;O07d&^Qr0DetD=9KFw@)yyvIUsq3Jn z*AUKyjpj~z(p>t8hu-me&^xA~(!n(&f4MDEjP?99GWAm`zn($NsXuDU-Kdsp-;&X1 zor3=!t_JQy5k~&S3U)K5zE0QlZ1HVH!)^wZ6S^7{cK|%Jo?l#>Gsj6F-`SwiXF&nP z_>GuQxrdt{P$uAqXI6p0s<;#-rBM)NVDXDMt5Ol^`9{nsoHoKK$CAmH{c?ZG6IB7& zES~(iqCFaUs z1AhkP@547B$7(EkUZhA-=VN@yej_?g%;DNn=&}cgGTXqODQ5ToqEwm?IVPcRlK@AN z?ql|)=oJ~;PVR~Qh=6;s-f3qC$D8x9oU#SAC9E3`PNcK`%;*9;L2tnP4BnlIy) z^277N7V515q^~Rytd3W33zo20+7JtbScj))ZU*;#5lzo7b$|Ul*OJz-h}Dl_t0vy+ zcs;^6nHhC`t5N)-XnIa|#h(#m5HE8@X2oAYaZ$XS>K+fgs}=vnb7sYVb9Auc_mdfu zZj#gWwys zFe_edydTX9Ry?(UCG=i=mU>(##gN}3<;{Kjh)$W)>8{bb&IRX*PFWZ~iD?LoG4JfL ze)&G$ejBaS1S%L9_W|4~iq6ytl1afef=s+N2Ph#n! zFV7b}Mtq|Z@xv6krST|c4i(iO-LwIJm-qFas|mprrN#F|o$(H5gJHbOI6Ef4Z#Zyd zbgFtiV~E4AX*>;*GEM}FWu6GKDr9Bd7b~<<&sVr)YT{1$-pq-FAQmyJQ9?)t?fi{) z%%~R7jv5)}j5!A6)%};>Z3Ji&&aaQav1{xtgzn5j0ff`;k7$i)X{Xg#8MHLQ`SD>O z;qDKJo!xT{u5&M7I3S3iCFr}|^~=4Fh0kOh-1ivY=YF$tbW>TPrGGQiM(Gz~;$A|{(6(iDD z{o6lSvr66CXM=+!RV@|Qp)&hqkO_e+17s%E$Y?s5JzA4QQafEwSmqxsbXn1665Ln! z2(8!As$@JMvv8N-Xr0SyN=FpjytWp=v%B_IR>r3NNPfVO!5%xna;?Y=O0_Ttqa})R z&K8(VZjmrw{)ezxQCDX7(#c}Z=@8%#xCX4X`+a9mJp2hgBaD~%j zq^=aFe*w%^DO2R*3o&;w=Fpq2=;zSO$X**!2;9Ez??_@Go&teH^#srQ$)%r%o)Sb| zTS7b?NHFI&=IWgd=O`iZ6%ljL6miEq4YeZ%obQFkZJa0oB}Xe7F?WbmxYS8kMr$yR zykQgL7h+EPGlv}wh`D-1YctILaEeegGoc>b{Fk&zbfKLxn*<@JKFX}dEpW`db7>*! zgbfyaQcj3_z7d)FZfPukUo1VrM}UhdhhUE=G2T~VVF`_X#4yY=pi09CX&8bIXEr1y zk~YoH3Fk>;&Il^6QK)X#BV6v-C7b08Lh2alEv1(~JB5c6- zb>wz7eW*)4H+-NhGtKKQ0^{s+@gF~=4{ZN@gRqG}By4zu`{N(s;g6n#o%R#`@$_dOe?k`$@<}YMO!$k8 zA1fr5OA|+Ctr(7rRIRe^5C_JXKfNmo%ihzk#hrMK9>M5dnKSnLzlmgN8t(iU(dL{f zdND8Ki5#o?#7h1{$KQSx;^mPCu;FluHvQ64Yl*`>8Jq|FZhM!tXclToEt3+1jmLO| zsx|YgY2o^G<*I^7!DKvzV+~nLUZ%lV)s`Dh4_iwvZddD!UK^UTulC2kDzmG#q`tsuSj#T7M&?#-Ge7;A(e z)tdJaN6Y9+t}6IpreSf*v?lEm9axXYy=H#%vDyq%_Byeu?d(5mYW6?kjAh;v_Jro_ zXZwGx!)+~E9<4ibZY=+emTg0wGX}o3vZ|ncW`IwbpXB(f_o(@KSmx*5{7kxMdtJ?>J7$s_NcR3rciKavvo$u~Q+OY! zui5{GkaGIh7YWGDaqr_s^G>0t_({@kO6{3`HjX|^8u}qlJ6m>`V+Wj=H$RLqUAjqSN2*AX=#^u;%(aP(b{f2XqRA3&H&mZ#swkv-T`dlM6{jv z8XpnoW=P?E62Un_OYwB!H&H^aw{3GZ)7DtOi55gMZT61jzi-W-FBpVTK5w=fV}Mcn z6|YWcp_P6V!NfBmHAzJfNzWh6hp)6_7FBi{s@4Rgi7JNJoK+mF9{9v65%|Q(dIQGKcNg+)`ohm_Ni6?;_J&vHF%c{LjqdaLchZmo`_8lZ@MleYuGqV@f^B!3 z?O31(YkE{kJfB0b4l1D=cQhQ`kM%g6AEkaAX~Dc2HRKFsFV;k=JnQx;EMZ3e!Hsw2 z6dxmqg^2PCZ9=c{7^pEi{&&dD2M+jAr-g@wuHt&)7bUl%c;PcZfX=VEMd6D;&ovEA zpXo(WBk{uouY%&+=XQ8jN^wi$`Cmt?KDQjhJ=Nn`Vk4w@`HAcK+b(gIBKrE=G1`38 zv^gzk^9!{ZNngTSV-OZkH$<}lY2K%aTCP+taB!2-m=&su~y9))wp zTNI3!DKh5lEajU!ibvg`Qz{-raF!kKn~RtwJ(<cvdF1m~~X1`)u|@sX9MvYNs3mW7g|0EQ9(5vBq7XEva)&rQ%= zPWvrx9SK^wI61-Sjb;pP{aqJ-5ZmXJ zyv}NUH75KTy8K*-PsT4Y;F}BZU7#TS?J|>Iq9@LusgPSfcR&91)OYq{Jqfw{eY*s9 z-eM69xG8w-e*G($6wdmHbu+P8V+8d0ph$`WtK?Wgyod;zyHOka|FnS@E^=uWu`J+H z|E^Ar;X%Pde(6CcV1^APa!Xof46^?HXAF-XdKV0?%fXVVE!xDE--%N(&aB4;eZ8v~ zo48C6yyd?KhZ9#78@Y71_D}|=f_xb*Vry00O+^VUczhY-I8QE(q|_=Qf1Y;-WpuvH zW#ko`(e)T@Mpy0b?<5&s0-5xamyyS9P3@Jj`4Li1GjMNfT(EAgcbGuT4A z=y&{}XyV7XXNu0XtqNFv3O}P`H%Rm=u>OL1h_D1@^IJ#_#V!8VFb8*8G-Rv_JMRvPPBR z^QBBqX!1ho@jy(c-P;sFphEFOW9jZtb%Pl+TyZ}rmpOQu_@Om0ZIxS7;>o0?4S3do z#7+aV1TItITl&soye~QIW)HlHkLfm;SV4GB20WNSa^NpEXkdJWr?(PLl73got~Y&u zyw2Om^ZHPZB{b7zs}s}Eorm{h)~S9na5F|N*gfL>V?3(sco8|&?f3+EDI2e|2if`F zMg~V?o3Ouz8{3)D@y`S}zJoWhWO?3T<=aJ_)$v1SfQr{S-NfrG+JAPNc%3?Nxjgki z5hw8EZMLiBVXy`9(E8GLn|NqXJ`a!39O2fbZ^}Eu6wAg#tFWi^$vXNr*FL#=&U+SZFSi;uS+YB^SJq>Xj#S*+28SJvTpQu-$i8rA z#0G0m=BGRzVG7ZTMYssSBHazCaxo|c`trAcIjWxGT?1w^zxN#n&A{5W=;=#&jHM^{ z6@i?ZNi~#9q+hTTglLc6iH-x?EBDX!dCZy~{qQxJJ?02(VS8ig-eUN43#I>YAH5_G z5~=uyG=_?5$Tmr0r!6qboA$Q2Glc6~N+xnXH8eeQml<`VtAXkqG>+kmQU_HkkrxAZ^Ve0@O4MWU`}?D|~yTbw_`V$|*Xz_lbFf<_>u1O`d+%9c4y5<}A+A=HMI4z;^)8>7 ztKpAwq+1e4u~TI#VTQ^Gufc27SlIUU=rNe+v$5(LNI_S9u3@64Ct4?Zo%nEyDx@G4j;W7Gf?Yg`*C{oG|Ya zDS1r*Vz$-~H-V)EG8n@PRW6HSHLb5PM))jlSH>3{1K^;2)+FDAzB0LG>cA{Lxg&~=Ct+;dng+Kn*WWpWGX8CT zJO_m@1VY~L59(AD!Qt1M8|3G|7IiZYDuXNa0rO+prG_bz)DLIiOG~;*J1*32e~y1y z(I_*>w|9%#@;j9zqJf&2$2aVlOgzZIx9n4k4D!PzDA_Q}7(Xfl#b6eNH;_-+aG)ls zn4F-hMorNV>Z-mTGd=7z=ZoH3q;t}lSFJb{ zB%+&eVYy*b+(@FiLS?vsg>w}3@cA;`r(euIMWn!0yrMj0IAbwqJR3IUVBFq`N3e2~ zV&w_#2jyXVb9DVCd}V>NG%;(@@Dnf+!2l}W;T5di4^+M%vuasa z-r?(*GyU7jS7BAWNr8gIRn$H)H*>UZ7&r!4YM#)>@!oqgZLEUPx@-3Pnf-@1dR0+N)cO;Ftz%A~fWBA)H5$10};!OS) zat$R97A3my_Ymw;X~%eOr55^|==`gG;z!x9@!2m>Q;-`*n4Op%t}IEML+Sj)nfz^+ zIF-Kzi35DXqw(7_mv^T#lhe{$!OW6rCG z+T*{}am?LRZ}H0=a_p`CWBi%rMIV%=zZ;=!A=7>j^zJX)>)9_kiY4m>{>d4Q=qL2A zTS*m2$;0)Fp+GU5(t5!Aq2tN5r;Su!GV6pQr zOOh*|()GwbIQ{LL<;e_{n+rdi}AN-{8;9;9lv_WI-q-ug>g3&972f!riT%+;G%RO@NOy2tATKzH3Q$EH` z6%1mK8f1QF_)fsBMn?%$L~OaT6zwSJQCW5s7(RJ+$zXJF>hdo}1&=A6O9th<(p zSh{Oz@iI)xSP^*u5S??F)>8g*xqZ{uL80vd)r%sa(M}u%P76_wH;ROT0|?nNN`zZt zL=4sx8U>eftts|{iR;%cad9;HY+q)YmlzFcblPTZV?Ve3lR9|R_VLMM_Xu3nvQNZJ zY3RsHV&Ko;6_w-j*Cu87Ycs39mBU*VXW^1JSL7w$hTyr}lr`L@eG^qS3V@9C%Qmme zL7|}w15A=dqp02Xe%+cskWohMH45F;c)q@q`=D=R8x?h~KNxqeZ{ZRP9|2|gRVeul zWXceTAg-HpLeFb{SXYPbZ?LrHDaA?rElJj*q1cuEGrIm?u?}0~&W{dMz5WbCqn%CPhuS~sQu};m9Idpd_sYOL>;vYax?d5GYb=V`x#n@X?Co`hqy%CXL8hW zQKC=i`GCmj*ezuEfqv^evg5r!$~3VY8t3ELFw4W`n`rB*BXD@}r{%CMmm(87eMse1 z#{mexXIi?qkCV@z^)7BZpl{9-c1BVisrsT}J!{3|8{9(m+Lv?aoSgL*I4~pD@(whV zcyh113z@$b`Qk#cvfx3b@Z`$F+jw9E+!_{Hi%vqGcFt*uCt}XD7N`ZdvHMvsgSKia z`?OE|!e3=cRQ06(m2DbTiTAN=z z`304Vj`1t8sy7l1gDU%2oRl_Y4luhY9>^gOE%sMlUHB<_(ZWPrJ{0*`sGn#6t@VEv z=_dx^%lw~*=;t9Qhx$L;>nEf3HC{8l*Uy_6tX1Y&HyQWW7{Q)Xm8i(&T(<9)vl)U zOg7AyCM-2PYL`oY_C{$1?Nq6+4N%3fEOd!p%nF&k*5QiSJG~8HV#FW>Ztusohx4C ziJ8D_H%?95x%pcFKiU=ft`5Ze^rM=If6OcFNlXvpoxVDOuTguam@hPUB*h z7X|EALvwgRG#VxcT#1{W%v63Ps2r(cJFLdL_!-OJ!ym$lwJL!`ogj(5s9G=P+-A;X!fa*x6sgdXJ)U6 zO*t9K6Cd~u-Rm<-hudfFh(Pscu7c%PEX12iblJBBXsi^^u9I0qaz#O8@4K<8ZPvWq z3n0hyKl8axpC>Mj!p<=NS#dlG-1;urlIJelR*)F(K6MX_b&=06ksLunfXh!L8+B#g zjnv7~bNCLWi$fxN--=efX5D&cYZuKAVQXQy6wQD4|1tiD74J$!5$&Cc0s|2IQ`5os z@XxP()TgcB_+~{HO?ig_1~8}%B?v|Mef;yR_4^gA_CBW1So$Xr(TL-X_PzZ{H2-sJ zetXmZE(HP8-4+*|)=yv~QtRrWq7ac|v)~TKn>5Rj+Uba1lWv}K0|WaM?&qQR0qH`tI^kl{*nI&(Ov%M;|NHsZ$H}R&r8A(;AA4YrT(F(>+ zeZ%=*NA5%8a}@%e>no!s^5+#);oX3ukbB>JBn_r1sS9^A4WI2PNJ7wN2>p?vWi%Gf zUmC9))g^LYEPrh*{eT%-w0Z_|)T3#x9|G2mZnypU%rY35#4S0~V}H=(dK)1JRH1?< z7{;j8`1b9Bxp!OM;Vt=-fJ@3PPAULuQb&^ItT zV!x$owpAZ0sGax-FL|=>o=5^XgF-Evq>q&4Hwa!4hn=WbY2zSmx$flqA>vF7LSJJ8 zwai;dQ7mgz9i#SvXdC^b7OF+1RAn^TI;#=PqYN7~EZF0Mn!h#8QbQlk|3Dv0*f#w) z8`W2lFn6p_>PsJFQn9JS4CdCqkcz`BLL7`e0cVNo$GY_gG+7JXE`I15#E;9qC^+94 zaMrLi?u1aMM}Q45Q3k`OVzDZCpG3qFv9Ox(#VC^RM(q*BICnrJK2Z)s6P(x7o@8QY zpI@p`gqqGT6Vz)JO5FG_&_eJf_2|{>MCLcSl_Z8%g-`Y}$Y z$Il0wR;{VW8SxwSC(8mv)uOie7O>-2gEV+{NgumS{gy3oR7Jrc>90?X_)aw6Zk#t!Gixf{EEo(6KlG?)AAs%=Q@xW{P+;~OU zUPV0cop_cYUh#NX2Pp0}9ipWP1P*ib9Vk3JrgcZ#!I@ad%vSj8a(RF{j%8Wf&Mnvf zSPhsO(^UJ~5>E!-Ij7gyn>d}W93tl;1lFA;n5tT1r4oD?yPD>E&UXrl`@PJXCpQE5 zS95>WD7}#;X$Cb;umLWoQw0rpg|tmTlbPKl=k3DHfMnqXCJ;%>O-BU3r@Ub<6o8~} z0)J001*F2xxE3)9!U*1LRpCMMN2CYfLUq7@3ki;8oX^C(f(2uOlWnylCg9 zzKi{s;N7(iK=C$Wf`3r!Od}-teu@zid=w$UFCrv3&nOoAbXG5(#)F>IX)*>!Fw5jm z&pXqQ-UEMi%!!Jaa zu{4BEV$AMTLRpp~RK}FLM4rpU2v^$#wu+qGb$hAUdJ*owrE*F`?C?z7a2z&%quAlm zxix<5@T;|bevtGaUJhPXSr=on3eRzDU++Z{u&HEfMfa#-5f>w<#^@`|N)A#RPXA-h z7Ch}q3=$h_<^j0^sXq(9f?uxG>W;(hqKr7It-k#hG%&qW>*b@SW=h1uUSxL_HSV~=_R9-*nfqRceWGp?a$kZ?U8f7)=~;l(|L~*20iGtc&sCz}05WKZSM->Mz&FVj)%j1LJ=hsd~@4<8Y-u zmqBKc`~#7y*R5N>PU)!5I>Bv**hLB5tU{iO5ra zl}iTe^nYva;dy~h^=H@=Ls7bgIJ^>|{?v5xG3>o3DCEu>;`78uE7>PKB{YCFh=>61 zQ9k*aL$3tXI#3V3KEX7$+fX8BneY8egm^Fnf{JsO-sjWn-E-)5W5yy|aieZzb>_+m zVu<Xj4RIUhGF3V}TDG$M~c)|oH1sS#tO`4S_`}9=Qe%&WD zVceRx2eV0)vWV`a8M1XYVT~GVN#Mn6&HqfG0j>lAMV#w)Mx0RyEy2g>oA{SkOZv*4 zJgwYoXBmy0dSck@5)6WuuvVJHS_jF;GXp_U6p*dSKenK^by zPEqV}N68XJ&9FX+!xeoi^)VRQdy@LbvjU-gP&e4dK>u<#18sbf3~z=0IQ$9TyBYsS zADLR`xB6Fj%}-G;+H+FtKYxyOgA5or(pbjA?v|+#n)~&)%SjGSdPS->036zO37C~z22K+se|6a@XHqF(D zmvLo&IDHu2p?g}5QO3+yFJe^cEBVc7H~s)kKW>P_;#?9&0A1=YqoxFXA4v`_=4>g~ zKU+G}%he#2r@>JtyLox~jO4-7KojhAa#gX7%Tor8D7+GjsN;R}^RWGT@{|3z&%PmX zNNCPCH`gy9?KEoyjrZ{pSYPgN@6d$}WUnv+ zXfqcRTgm^LLCfhQ#RMBkrR2WTZah5sax~iJV@61_o#V=3)#z;TKmb!v^ zV6KV+tTF2@F^4U2Ue?obgizz?lJM9Jn{u7zybP8OjI!m*3P;B>#DrLMBlgj;G=My^ zcw(-=hxezvYyDSw)Th7jp|7nxsxu4yzsRG^v(_1Sc{`@?(8c}~u%8Q@HD{(!e-hN= zvSt6%@L7;6r-91z9{dJ>kJ{iDsKOW_4JOv&8#%d}SH_xzudhzlB{e6kOXvw!Q$DLSS=`b6C+!xs5b zD~5414*YGq6Z4XN?&U)6xHcdIh$pseO98|{sXMwJ4oT7|5Z|!LO7)^`>bwpXlX4a$ z7&yg3MQu|o9Rf8CMzyN1iPU~Q`flwx#xr;V{Hx&_?1PM?n1nJE;qns~$J24X@+mT` zf|PcNVe#}>zS@y8x-TgOi7Fgl@Kr#{#7a^M6DK~)yBQ>(SP24xvo6M(omgHPtJ+lW zr*J)#ul2YWTZ_#z?KDjT=ZSEsRV?{g z0ZWV`cpOnS3XQQ>DYiOszoPIcX6a{W8aM?DsEc%)emJLGLI?pm<5A z6TcI`G6O)Vml%uJgU%z*GkpDbT%_mv2&y}&r5eVP_x#k^=7i(#_14Fg>~!sRh6s~o z=H882e;o=F>w+@4q@4+n%A?5^FAK#P0_~Fg$;B;vp0i{(yV1L zlK0epEn!&cJ?=lq`Z+gjN*0~U7e`mf6vSdR{)tlFYcm+L0Ul{>-dUqjr9L;rZsIim zL_~tE4LdW~*%!^ucBxIZ%mx7gWbWPrMXWPlzJAHE960PjeUOA|GS02Y_1c+?+K7&I z>Oy{at8O-EZA4Q^o_pCpG}EFr>x6gN$v$RatGqe;Xb!MQ89B}=-JTH&QIu?_|4{@u zVzWiW6?8M9Y75jw@-_L6_kP2-k=bjEXJp3cUC2g&2Z9rW+R8m<&KkehQF+z zA7$Fn1BQg8$jE@-A9JqRWL_3b#*l*l-Pk2Ly9~HHe-l#!O`N9FmGi;$}Fzcc(Xai3fQ*&CjaeFe<XZH%?c)655+E852&Np; z=t$3^(^(_YE*c-RFCT9m)ur6(JQk1yul|Le{$u?lEV!Qb4F^syYV$}*8S^W@Glc^- z5?(0%`$I=A&jmhb){Mg?G(`J6Das!XSVE;x3j1(?P?nqIYHfv@b zg=5#waWmS~Wz^H4Qd7+b&kc>q@84%@7~Z^uea%01O3j6q<68Ek)nnTtjm&R@*#UhXvs!<)#u zMpxS#thI6mPQMO}-|uh!Q`9aR@rI8wQZEV#&%ZG^EmrE6`g8`~tC0s(6Ja`KBL&Zp z5y{Ynu+YcF?5W?CM8!<3!a54Te}Za|gQ5)8U_2KH6y4kRw0O9;YnAV{kw^Hq_4OJA zkir|$HjWYVwNTU!6woPSa8Yb}X`{r**c zgFL`pN&4VqpN9#&aOS2~!Wp93+@V9AF1x=5Ox(^t4JhcCGZ?Hnh@qhHnEppAEJV}9 zMxU|C-<-j249;-6U(6=fi!K8kDwPFPF%AZnMi|to8_EAf#$8yS2xk9 zS>s`5J_Y%nSdgt`OswbD7`{DHKO%dm)A19U%!@3}k~4Uvz4d(6bc#ltq|l`-9r7#4 zn2}{s>#tu8b!HyrlQNsew~{h$Lc7tU2Q@&_#yMh>pa$TjKJ*Rj%M;E0UG)C$UaZG- z=$EVcf_BmW3U!*g#x!!mvrlPDedl8Fw0wbIyU7^D;$19eS4Gnj4cuJph16d4z}_ig zsWUjWF+4dmo@7wWSWa48B*hG)o(FFF`(L0l?6+{XVCaiSIve^zS}kas%2jFOP7x&O zJ2h_>HgD zn6!TG!zb+q7icBrOCFQ1EjPd>dWt{axt}upx_8Xu73;EqVV53KhC*sz_ja7Wh?pv2 zsgg5FG3Tr+vl`!Ip|1#`0Xx0eYP_3PL&+D)7m^5&AFb)2b^MZc!~J%iJb(H8zSk%i zO+Q^O`VX@m_tM`77qP|_<=Ak^l*!D_`TI`Qt!b}IZUs|706-ClE(R4xqoAR$?6-^E ztN+w`&0lRwNbYT~avxiLkmuO2F5&cG_`M!C!WLtHOt`EPw*s1K!bRUXUt)|;*QnMU zqF((GccV>NMRMcAKA+y#J%DG^%#aRT&yeKmzQsMubZ5GX+5ZlWQr99jwyV46z=fd+ zp^42-)Va${KI%MeR!iL-3y&}8F7++D?fs5sTc0%l3%!?|T?71{zlk-=tH0S?cY53r zfon!d#o z6UOLgZ!-rGVxay#@+yLhzj=-O(!~5cZ>A2vNEfaw@hktB198$nK>0uxmZ%$uQ&I-d zUpo)GV)XM$%>>R>a6o=YzSrk?&_dVbC1}ErCYHDWVa0VCkvy}B;#v)mx$`$^I4jBw zds6DV&cLEx@P8I+B*D|ip;|1B3sw1{-qv!rly(hZCCCt&w}7#Od^Y!MNp40d@oB~q zF_)h0f3-_o$M1p!aPv)JBE;V!%%PIs9Fo9m!01u~?Gbn&5gT8K>BN569EQd5H6Kmr zZ-n=={t|wKGbu5ph>a4NFD*eGK%DU*~Uv^>dA!B>=8Mv4NX-%nD+0Hes|zt_?< zi**xju+Arv+=VcMuEEP9a~Dur1e~p=K~LczB(%K z&7@oi25y4HG3~hD*G}6uvh_`S$wXUzq2iZuEBt@;*GIXZ%E1}+S*5)E32oU4@b`S}DbNyf2;m$9 zc0;bw(MQcHkdywvR-4CrhLPe4|r88dL$>AFs(LnR4{r0%45M3mCU zI*OKMm27p6us;DPi@zA~Gj~5)mglaG0OyQ!at=Rp`X;et1%cdPWi$_F6##PG2V!2F zZ}zoPf4f@uWzp+Tv|e@J@uL~F;o6l%x#(coW;MEe!KZ&@bzV!{q5deW#(J6zKyi7j z`h-Ip?E0^$|< zz7giAA^pI z4xSh3w=>dvCqn;F#c(H<*FKuu{B~|^c`Szg;)pB4T`V+q4cVz72 z1N+cl%=C}RF->_=TJeQCphIIDT7hk-k?t7V&>V!lFk*kL5Ra|$z5moF>}UG^9(}^H zpi4j|=UI6dOt`9jy`ZE}6@bHFPV-*vo6|r4G0sN%EO+&4UuOFX$!^D< z$o8CpcW*62P(7nKaf%p1PS=Z<@?v!QpEH$rC`p3yX6XMF3cXVfatfVP=0 zH#7Jd4!zw(Y=K8_l+5sCc>gP`RL84yIbm-r4%!9q0#HHw71e!*z3I`HYMM7hl8Z%R zy8X&vX*!)2 zZz)4SKMZhl z$r;mu5tPX#Tm}Z~A7AdE)mSYU$Hmhyn%-C zp9;%>Z$9aT9N~gr>Id2%Ced*H2K|&3Y8rg4QV_FPjSul*M)W6r7$|Lg*n9>m0$ShP zL40*3m_$U2bHa+>@-$;`^0mT9`t8Y#rTRa{0#~TbI2O}N&Nvd?weslw85_kpfLn7} z>w)JOM9{vgKQnMmKaR>mvF4ThJ%mHXgMoK84dGbZGnOq**Fwh20ezY6lnceNE6nTb z2nxa2m1RrDehprSQP4b=l{J?wPb%WZmg1IBkSZ;w3POxGDRrJLF~o z$K|-3DExOcIlYAY;vte^4)NOYR(HGDWFwr86Qj#E7YuU>59P^Ol2<>GeBx;Bx9)Jr zC)mcJT=&WtL?msH(RmXxMiA8mbT*U55E+P$+5>GLvRiGwbA_H&<4dDO&%Dx}yyK z`aXSu%4z=90)ZoL^?%nF@U46OGbFhE$V%l+e;2+*Wk@P$<#9%%V zAnGVyMkUYckj3nyn6Yn@wVYSj&`b3+7Psm)Mn-8_9?#Ywlg}9oK&$aOe)Zeqc9JS0C1wFc9jz+rgk zSq6q%t7=daK#!Bgudnc1RrRwX80K?d83pN$nFXVX9E6PgTSk!}fR?pJi>dk9X#NWN zO{{EQ8ptOYJYtLzme4&e;qMk=3@xcI#>i=o_4VI_1{~6pOYIFZ*|Cj%QQ!z=ioy-d z-7NQ3wK!f1S9kr}j$U}1>P3sObdRU`G0f?HK?LJOJVU{&IX2S!t;k$F!xf+-t$qzS z>5h*g>5ka#P55Qu*oR09@g}w!w`z1!5JEA1R%Qj+{u&e4ctjZeiqfGO66 zkLE2|`Xtl2_sGNh#P1KKuS21^=9RTKA|{E|&ix+yH7_giozHpkNq2 zwN@a$uEVER9X_>mjfJEDFbZA*aL7r3>w!Nl+~wc$5??S4D$FrnN=YeLDnVtT++h+J zVM1a+xcVn0)6U{TBkRI4Sm|YAB6j`ibC`&k_W_BbcnTR~1p>$oU5Ppw~*%wjFa5w7D!y{Is+P8bm(m!%#bwim^{dEUi29r1I z&nixKzV~m#M(cE$x~Rk8*#D+&W? zUBrrh0E$5s{{!pxe;ZJQHSe)J!<|I?@)*9BZa*3BP^|ZIYks#MOZ%fcKVjh4Sa%Z- zWPd)YYA=V&2y65-qW$VL(K8r{n0MeNi)_^hZY z!x!*t8o`pBc`CNr9ecP}BB;1(o{_nMYJVv$^!q%RY2yC>W2Q&8&eWJb443IcVmc&E z&!gMSGHzpb@O`sZiY4K4Rc^dkHNQGL@Y008 zHs5gb#l$y3*UBi|I$<;;-Oau7+~mk$-l(vhHQXMsOIj7il~c|D;P+XJegHH4A5>wL zJ_5Gc`?J{-Q+y>SEvGo^6T$my#J-XL8%jKh_}gT&9_fKsdcD+4{fnWOPGAN4eMn>s zZx<~&9gn~oVqS%I+=dt=`uc;TrA4mz*v0}PRYZ&48<*kv>! zO87Gt(+6|^)mB|vo~Ws{FGV?tC?wAGjOjy{PqdYxW>TVO=ebDX@LmM>%XCMq>Lg z60l%SKt&~c633j!7(Glc_ReC~k>tSNxxMr(ly7IFPM7 z4;B;}6H`cYlOwcJWcb#sS4`F#4JZ%UOJiI%@P*pzaA*{fWYG!K021+<)^xtHdI-(k zpE}=MMHY_J^6hG2kr;+!Z zNE47U9qB{j4-|BDqu zwX6^Z{eP?w-rL6Ql)siOd515ruYmPb`ph{4|BF1J>HJMhk)!0hJsW54uhi=LGkA0P z4n^zZ=zrX2^gmAb^*{K5{>Lx>L;u6b44TB|&&Uj#pPT3%LP4WI_Ne#7#oF+}o49Pz zjDyT+>lm>4o}9spp7G;R1p1Ou`!$IJMn~p;HeVx74t)ME>{lRJb|#hA^jm873yO2s zpYS>xKb?+w23~94u{*gIa4RFI|8efl>AFCzzQ#w?%E=CbCqo~#8 zguW*uH{_4ZOo?TKkPnXGRX+jPKo@#)r^n_c_aRyxOJ~+CFO!noS5SWg!ZK0R?qyg= z7_yXXJ^44ff7L0lH`1H?Hp=p1H`8THVwzio;|~2Csh>sewEKjNCzW3-IgSKBGg%?u zmu_V43N3BS=R%oR!Q|!1edr531DyaHr`?_ArCb;C86W4l-+Z;7#;z*o9sYR&Pxd=- zX6@cJthK}XJDTsz&_8&-s9&6!!47)T3_hkqV}k6-<0`5$Xk>$vz|;L!8$pRK{Klsz z^6IHR+;ViTV|B}hrN^woz~_PrV*^vPh z;)1ygUSqZ`SjVxQqK z85{kI5B@2rID7)b{GJ2S=d_#ylG=zM**l)1QR0L00z5q5LjC~C<09)AHrOBZrE9H= z`|y}uEL4|rvq322bWAM`15&7aH?XE*_~581P`Xx3z*XuVc97a(XC=!BV;NYY#=C@t zL1losFpsXYWGYu?sdK>qMpM1DXB%~uMUyfsybg4wKR#uxxUyd z)>70R0Nd{8<~qkMYKKKfAf)|DJB}`t(|_t9miPm%d@~2_h@c3?A@VL(-jde5M8Z$M zX*sHcziUk=Uid{^eWuQts&iXwUShPLzOIEGxxO_WE#+*TmC75Jlb7m1UTAK|a-`#V zc+&Ue`!WEHer&VJs>#c1-U`9~;8xv+r$_x@&}nVoEvo*Uz}jmks2DRINq$OyPF9W| zui9Y2j1szD(-rRDs|5*CI80o@!=LqAwy7*)7lhJB;S7v(d*c`>Tld?8sv}$2?{mX; zegzN?r8|~ay*{fy3y#tHx5c&T3ptZZt<>L5=Gn=EofE^82fHLr2pQf6lM37KHoxTd zITRg7d0}rgDMLZm9Oo2OQ8M9Jt@eErhLY7fUCU~%+NS0grFN&0{zW{lL+gL9z_D!F zB?k`&ULm>!QUF&NINdi&aH^j`O|gbGjmd@KeWOy!uPrQ99KQn~1KsrIZh(&>wuPQJ zFOZ!+v-&OOau{x@$w=N^nW0o0MyT3(x`@R~&WNI+3b~UgS?I!o? zU@X8ab#-893>T<$OQ}!KGY;U~$oH3e)i3q+(h|nF-TmSBm-?gMX9%ewBh0FJ(yhoSJzC9y$ zi**Ur(}Y(Wxo+ZX$BTz(_ZpB?V655qGS+~p75M}Lke_(>g) z^y~_IMxj8}`vt%It@a=L_*uQ%TwH@_iBL@L^#q^Kv@?!&nio;Dzgj?9`VK`R^J@50 zOYoCn{L2`6fBU`5b3c@@0I5GJL%lMSZ z03955uJF;HTgSEJ(2jZDu~fq|`F~X=9J^e3EwQ;v@)CoMn8Mzlyp=KL<-3REQx|Ozz}Wrcpj|CA z_f(jjvA087^d>X;CsU9UuP3~>Z5ONG5BgwXmQa(^hyyQ0Xin&**d06s#)&~MR^#g$ zNE2BQE`0dL(e7_6gn$AM<-ekcJ9)54L0|zO*s7H7U+@?=TGL>ND&V4NaHal@*(c-q zq{dKFV?i7y$d9*}FF}VZ9HkZ-_!rU>;%RV6Q_TQ1ks+~MVA;CmucSa?S*b+=?SRbs zqH(JdeosOM11uv!ODPI%8AP7DuODs8 zY>($WuA|6S)OL}OXsZU&xw)i@XU8iC!Q2IXYekvJf|Tu}XkdUVezQD@`JsiOVGIE? z9&ky{Vmb?k^Oc#!(}(y2=3SRZF0S1A`nf#0HoEr&xexid?W_fod{CEbnfUHO?mR!Y zz*_K?;5A*(^{~;MU~&^vXtmH<@PVHLO5f<#_&LMLDY6zU^>g|vr5 z^dd=l&4-6{IaK|O(Jv%5UI*PQ+G}Mo@-@w%9#h$155DAvB$vZ~VhGA=Gv%*XXed=9 z+sYJMLov20_2C6vf#K0wZbR)EYoV7&450}e!a0s{2NtdSiZ-!3+Fk6av^1?2kt7sEfa-_2az<}t)`6eW7O!3c9Dbu{;K!a(rsGPv{Z z{DCr*w&u^EU$g*uvC9_dJ^g~nn*WGC*p$s%T9xB2R+`gk!SpiD!A)iE-DhahA`Yy^ zktVPI^~%dI>Lr<+Iv$%Z_#acz2$jN7MyQwJ(l!n5%cA7D_IWv{`ioJ>Kw8|#%1v5@ z>EHuD?dN`4Uz4_qw6J|cC9N7FeokF@8{aXtC?pGYIxK?YZwTvkiEg#&2{nc~m(;B` z{nJWwfL`PW_1=fi4AjGECGhPN!~L}}TI%-2MB9;{t6L~(NEPV`93clLfbGZbc?0&f z>|MLeJ&t1-n2~cH&xfY*xm#S};0G!#1NCSRO9KutUSSKkXY`wDk4%f)P;KbttX_9}N=f1`|-l8YO( zKs)APe1?w9K$kdU0q{AlNf0f%$8cen#1yAY9a>__TX%eSp~bvQ#5PJI~? z+dYeR1>vWV7zl!o;M2eUzGk6#)oNLC_{}lr(ee@qKI2V<#&uA3aL3Wi9j}ehUiJT? z+8@NT=bPllQ_VKbL`TcsVXDa)Q}xhYElW<*s8dU$RxL3fcDP3m=C0E^7N~3I+WbiG zFN}A!1$y5`U@3T~@X-&ab^PP0l((HeQQMtaaJ(Nx%^Pj-neI*r_OcmJ|0pXM-FfFGlugSV!fE*_*k z`6{yC_Jj991z)Ew7Bg7=H(OR_jWtTLcY}`e-Lt?j5?`Lq4K%%fq^db;yO_F{#;oDX zn>$3(9XFy2w-LnCj3j#bEWDoK7^2_O;dXgbCV&M7`Z}NRvpz-)@s)nnMpS^J* zM^+Dqe{|0U2k$e=wyvl946Ul$OU%mMfQAjoQ`Ao=eHQYW85w|>9CUG(=>mYbECUd= zK~8r+r(MJO8GtxB$Z6;26f~R?dQkuot;0E3;-CSv_CcAC+iU160f-I<30l}F8vRVqV zSq8`eG2iwy*)S7azQC2{PU>8AS~jk%g9;^# zV4|tm#bC}X-a<;shgNOX*|RsY-Uz-q$Fj-Iz>(G?TK6_J8T9&McXm*7oT(`|BH$q` z8Sn@ihp!EVa)2#YOC)Hg0T_r@I3?`y=lX^xeEN3yG)GnHvXw zG_$V?gP94aN;PlRv~Q64o}UTcArq%RE#67|$sNjKh7v-G6sp<-)0UxR=f8sa2dHf! z&1nA3uVu!NY9tAe1)Sk#Ap)AOB55mxMWKqo%7%)w9fdlK)V=DD{>@_d>92ht zNK*sFt>!mttIxKa-IU5M6`-<&NoX9Gn{lvI264E*50ZzQ?xtp^wmG7y#hDtfr@t|^ zc-=qgkz4JfVs}u`<1wZ!faf()L(-?A7GcqNN?S0cRNGSv=)#YtRr5f8K1YA3cB*yEIC>6 zG5!AYwTx$lv|+bX%HJ?hb9xp^3kTf|05#-Y;uQbk&PfYPK6+EBd+p|={Lp0bI$Gk#*$Lrqn zSNdJni*adT>diQs^1TxQdJaQvb)Ry}X`*!Y1bkKOJ`|Le^_{Mz<_(1SB4hy%IEj7= zL)gWKs#>huQ((T_WA*6}!AbjCTx~hkrqPe$>BL6X%-l*oOb-fL_k=b@S8>`?ukiy` zo&j)B&KlED$8x}CR*x*myj|?Rv)c#nCnztFdCQD%Uc3&%Gl0jx2Ssd(gm}_;Lm;Ab zc+AmxbdM&!$DE4$_>R}T>(_j**qv@_CPo{G`UoyzCv(JG(Z&%BE0k^@()=b;Ia>FQ z50X@__A4J{2J{xm?nNx0fI@cID3@MK0T*eVNyD~8X_H8!p9H@$gZe9g=p$H;7YF)C zQzNm&AC%c)VkQ{kW&<268abLq#0azxm{;BOU;F_%(=_h(s57Gc5A6~)5Yz*ES@<-h zDTpF?tCxJS56(DBQ%*gsCy&wc+&g9t?1^-VJNMm=23TJDii2}1?NGme+xkDCe)EzX z3f%Am@$IEx65>^aMj0gDxmIt7U3w(4AjMsbBe!EhFc7@3y~R)ViF0olw2BNM{i1i? zwYvqr8E?jfxl?=8>}r`1&d4gW{sjW9>*D0OM_IR)GdUPoiUO`Um2O+|iz^1DwI-&++=w{{h=&PyI0= zUj%zNZMB_WupqxRRJs<#`;0u z7Nh)G4W`c%7v^C;GaxWAC_BKkcpgurMnBigc3_bE13#DPUg77O=^hv4=J~nIS4)=x zhu}fUHNH2X|2Kqkz`^TI*@fnwk-e~H^(^_pIt$|a9wQ`VHn;;l>AE(T^;Uj_VAVpD zD_{XdTj3*a2u+|64(*(v*~?6~A~fssW`FEGN*%f_G2v$vFzXLVbx$cPD7CXj^K2q$BF)O_KrIr<+Nw8a zt^@o<+%dpEK1~QVqIt0@qJ%`lwo5xv%%Wq+wZNiH^ff8|nke zE&Jz>3NLlrX#qp_ics=j`L4X~tD$RYC?IS;QkzhFXd&LNxKgOU6@65}F{~}tt0vW` zd}~o8?4LG!O>*=8rJuG7ci58LlD}+AyAEqY^GAj~o0idT>2!MqtKQg!DPf|=aIWMq zgPY2H*awT?YEnL4WS4L04AJfV=r=sYle!o^y5LkV^X? z)t+*Cl7!Keb^2%x4Ot1aKqGAC*47YSrj8*ii3KIBjPrnEcSVr-7@6YjSPR~xAKcmM zSW3UceK;tQREZavMqX|8sh0Dkiq&DtDVTf%8d3N!C_3IW2BGwh9Nzxcz_>{A5$VM1 z5I<+R!-ImSQP2=W_6~4#>P{NZENG#XO7hb^J}6p9QS)ZE77fNLZgSK9rCz(x{K4Vm z=KN5H*KiyO!0$1vp8At`56wJ@xq6qHbGzExF2Dx;y#qDDTBKTN)%)8(!R*=1Z$;}4 zGKm&*RXydGXuM0AdNn=aK4g;WAMoao7D*RPv9-C=Fhn#P?2GBHB9BmpUN^7}Wd@is ztpJc}VI0{N@F#o!AV0A`Fh;^+#vgYG4k>+4d`1ZZ z*LbCF`)p*c5|-M+CPvC4$UQe~WT2UTqhAnLxL%^sEi(*d6-s}`ki=G6bhPH;Gxeuy zjsSS3Uw3LC$a8aM`Lc%dm4jrqo@WC@M-EZr7@?J12k9p|Y}XHeCtibPzosZ`2(+w; zA6JF`xF(j#`zI6A7-r9-#oU##+(wBRO8WTx>91*v%qz%La&D0PF3C90N{p-yJy+Ow zs$6F5mWaQjdwmbXr}c9NNzoF;AImwVeuxd9eRuizVD zKtWF8H;nq{=ln%_{8k?_*PD9AN$Nk%&s7+mwx+=}C5Y7B?h&Uk(oDa#3)H|4_l#5J zz})x8aF}YcWpn)%*}$g)c zoKR{wWpHp<5UU)xuSzcKdYMZ2irA*Ecl6lr^ZoNlwz%5h!-rCP&d~9ScbWfLPD9(A z_Mxb8{hF4m4hyY8g8MNIxoWk*TwXoNUW)oYJ!R~tWa6`P*-6@7W7LYFK+VS%di*-d zzhfs@=~{wW;%E&Y$pu9sVcj-nvWadlR;iv`$b98qXPP)MH!;%a0Vwo=T$>dnXY_9q zN-v?;gm$5{AJXBkp=YH+q??0WRA)+Sbf75q%iAQX`0W=W_D-nV9cb9-f4UD|1w(Zqg3Hj-DrXcd#?LOVZKV2#D^KeXF z6xp>3Yw%`e^Zq4dpa%JsSwSbi01U_j|iQ2CMavTdVjayeL-DG~5HUFwYn^yR* zhqM&=+x1^&=~@-ej3ZR+lBY~PLOq=0Ml-|K$L(_nd~@UTmid3i15ta}ck#@yh0)CK zr+|~9nct1mfA)0-Si*A2i*=ZWh`Gt_yDAHSq11Gy;unOR_}2ZX z++T?|NH(jSmbH)fwwZ9VriIr}i3t*0EpS$znzb@Bz{;IIB#^)1y=JMJ$y}+Uoj5|$ z^dQnlLUQ*+yU4r&rQ^vkdqYH5scRT}?m&|^GH+IC?)cg*I-yhGTyrWVAblT9Ojcgg zc<@avB-O0EU&be1FlSx|_o1E~a-(8W`yk}wA@lZ*PfSj&OWc*cHFjIexGwc?F1H67 z0lVMD_@ybHyFW^pkOlktvqYKNTtw|JZqds5+A0@Oeoa;BczKD-OzVM$y?bvjVbfV% zQMcdbUUDLqV5d_^UYr_lHRm~Q*K?$$f>7go?f$vtR7>^Fz3OSOs_7sxeq^=%1IiJ7 z)?LjBj+IUb@X*+2j5_AH3y{kTiGSsuTP_p}9excX6_k!NDtJUpHgqD`Yqy&-igO4x zz|C88d@iuERdV=WtuOLSoEgAu_|fPcS`p>yMN?CD4QgvkGbr#b+j!jYW zERxPJ4ex6+=SWszeL z=6PlF40am&HBn=H_wiyV@T8v9oGpGe=leKw9nBk7U1VgO$)64nrLQ{8n8|+rd%X`r z=_@$5h_UY#rQPQp?SU^pO?<71YFeSiJ!smyfj`M68YrUyL)577y+JdIYyp!DH~dJh zDb$ui)k39TxzmCo(oLkuNfe3l0`NQA#4}0|GM!X)=ep+0B-5WDD|w+5PrPfpRC^74B>G~`SgGf*v8-&ymXc!FlJG+*(Oo$ zBQIBiLzvPV@xl0Q?zA&d)!@n<@V!MKW053RssQ#^aI|yWp7fd3YGK=28-QcK3kEZL zN`~N#v##q~ja+4V!62H^I9E?H%4dB{T1}a`VoLQvyw_79weO%4t71>>kyho@qS+Vu z2X~8=8JpW@-Gvu>Ta|4Nv%8LJ%D`L(_@f?hZNr`D_0g-* zA#~jMVXNbT|5M}tnL@>JTZOH6+#a8LX7sNv&zKQiD@}Fzvej4rhp~0NhrQvw)XLyV%Ef1wW6Edo6 zXd6dW1h0bllcN*D|5b$+96>{R15`ybeLqrhE?x9o+(o)}(%VgV;*dQLk|5smo8g(h z9)a2Ld#GK*ntFieZapTjdqScWj<&rXlj0BMvu^Pxn#cwsmQeQo5lkBl~$@9vhxY|+2o%eIqsJTWMc9ib}BcX zAJ-3YEcs>_?mNf%)UFGq8fnU3J}l|X@?nA+4lR1cAfRe=(VhN=^ZY0Esne~s z=1I`&Q|%LuML)tX9))GBgByv}c$~Z#4&SCkp4@Ml(!}5GLU4hrDHY9h`6yz-$s03{ zVNT!IfPP$Xn_(OzZC zO%)2*!Gq<-b`{WcpZblYkNgsvoK>!;>i80%JC?g$AXVN1*?k+CXQPZ6%iI&XamLN5 zG{;;Gl!q2g5)0+8^!Tgw6(ci_-<^2H=b^NG4k4EWLcvrSjR&gs$ji_LA5C>#Prgl= z_c9v=Y3@g&+!#f$x?se~%?Bg;qJJEkh;#WK0YDge^LY><=AVZZ$NuU(Y*GuYPJ%y) z(4r?G$-G7lKswd$c=dc6FiPz_Nox|o#YP4%%<66N@7R?Ti`n%8GxxY7cQzRad&neS zgYhuQp6g=2JR!dg)Zv1LN690^m)JzXy` zVI`9vmB}n!cvq`4gUfTbDjS^Lo3R;`C5&nCeANC_H$<4xn}sRzPNf?1YhQY^o|Ohx zc0n&P{iY4xRV0Xav9P{3lRxmXh}vJrb75W(I}f%fD~6!WbVN`HJSWnIhEc{LxiLYTD#&K^Vi6Z}t_DG1o7&5*;3 zVVPJDN_WyNe2`R>@j@#a_2OZfM2|hvYt*?j+Km3W^Dkk_v`JLp^{mHtai^2Ce{n`g z<#SLr#6{n}rUceHm&=fx*O}1`l^e!J(A~bK+O10jvHeSSHU)tob>I7^(3{KjFpT_^ zF&RhIjq7mV8^a_i(lolf@lP%Dm0S~I%if9UKw=vFI*1%`fp7RiBrPh3Yfw2{qYDZ7 zq@ks->a2ySoMot6reG*l&JG!IjO2A1*vTd%F^)tQbXRRVQ@imQez245z$OG%eq1)&W5~Vk9#XjIJeL4DffnW&AqefGa+asFLgxs?R0_wp?3}Q4orZ z7EiP$?gK~c$I8n*a%y`W@WAQ#t)b)ls(r+?+Y=|5F=w?reNuNKttszgpDR_cK*VnC z8&LHh0Bh>+RP{F|r{dk&%QXY>%XzsZ^409-7Rp~w2x@xN{O^E1#$N3CA9W9BiuV+R zFh0o)ysd3X&R8y^c^@2;<2tVdT%|&~KsxjKTVXLxXHv%J^JbYfe@0By%wjwuYeet4WIaig%F^jtGO%fpZ zZUV*aCuf=wb*C3lEuI--qD9~K7)3^8x>QB$zG%y_>xR zage=%Z1@aqO-GlT?902Bqben}9Q0Oifcgc^`VOTT3q~L8Tx0_A`5=9!kJG*UIS!DZ z5{4G3)FbfjIv~Y=WJoBLqH%md#Qz|67Y5r~L?DUZ@i;7d-Iw7LL^SF3)cZsMMsb@l zQhPAk-ru_Epj3|X^h(^}9@;4Yq!)x1{j{?&a-lI>7A@WrtT+0mYvcAB?+O6b?7z7d zjA7vkDDat~eBjn=pQxAa4&iSY&Bytf>gTa+<~q`1Y*&pl1LFo3RePp=IF4~&c2Yj6((GCu#Hb1C)_$^A^cmIWqCJO1^s%+=J(jLwp4 zkFVvUY^Rzz2oha8PbxPs))}9;iHh>mC&3Or{0bF7)9Wlm>i{ zRHLZwaj5ZPHV40GiyskOg!SJg(joTqCfkc7BVAO?Bxc#z+%5JAoo^ zRbz?N7@G0KTgRzZqI8lT1`C(-1$7nXy~JyLS77n_`)cuMuj2Q;Zi4t!FHA z9TWXFbX$8$49{G1ZH`s96_;3fC*Z{0FZQKhj0L6G=X`*1PT-ga+1g9O1+*m&Y3Q~! zrtC#0fGhlfzv;28H$lVvfC7|w(3BVfs;H-p^FS5vBtG}gBY1*y5{FeHI%tCM@T4gG zK>Zl!_7<#y=GgEKvG&9dd_o7!hp}XV52k1>;nk(dy%fnb5VP=NgGnU0O24I})`PX> zauNojg8e-VRKt8W{qwS8>%3a+C3uO^yqXhMF^1ueptmeUPwT;2XLm+a*aRsojuRT-goQH(>AxFW`8`?mCW#?Xh zhJzS`ds90t1}qGN4!5)3-uD28%G(>f0vrEk+%IkKL|fEbr-BB6L^~`A8d}%gUmSDEOn%NMaqiQ!o-{ciO2J#MAX?k94eTp$ z=G_9Z;Ck9ztzcJ?{ zN?Du@p<-x_*~}S~4U5#WXR{c+4LgD9>+p^&_qy`^2mUjOvL zCq<%npDJo}ca+Mo)tEe*?+u<58x5=ovK|haGI6DS2vU3Q;%pZy4eAlZ`H;2oP=EMR zA15Ngfsl38A5CUT+^yy=s{N*Fd$(%7+)qnzwYD@p(=0 zd8I59ymqLwn{;$D#bfKjXpl$k2GD?L4N(mrVhDN!FKv__Ln}|rI z+={D960xXcdZ}0a?{H!O-|&m&tbzQpL@&N!SKaS_mnXVBuQ!pRhQ=yB45b#B^lMg> z)PBQ#;)f)6F62qfIOm#i>Lkiwoz=(0f{+ymMPqC^XHD`{URw7X2%pU%1Bu$iCs5*4 zKanlJ*g$@I2?>FFf0#YSHGaI}o5XFg%HBP?;yyTOq~AN-g&i4Qpe43nL*)un^bfO& z!B8{;bHmp7Bj_58Ko7uJb_Cyt1v9f-MsSE3fj2#%Cc)vK%=UqOfzA_L{>I-cv<=dj zl-vrnjwZw?=XMbi9ZT)j%aph%J3yUcR>;T=e>J|@{!k#T6qcLFMp~g!zaHOpN=TZB z2iXgv!5kUxSN$@PlQ{Rw@pOuwgQ`Oz3F z+?yW3B?%49V@?Mg0M-_#cJ_A+Cy`GGnWhFDQ@1LCe|E(QG1>wWV;T>5XsMe1H#WTw zb19DZ4l|up8&Y9Ro7B@u_3_;=9J*|3cT%|vnrcD&Gt2ekWPbEFi{VlZ&)9|y*z%qW zY9`)p+-lBewk1W%QN6@l-jh1jS$b3aqkVx1bf8Q%F2;5XPxmSOnFrvMF>v*X2(e9?*zLg^U9k35f%j*s2XqTRnAp6L~hW_rO- zHU5|r?JXYJ>1Winb1jF5x&GFCK*s8r$DF?u@ojX)M`@-oSBW}1k%$P<%BhU{M6DFw z-_mmFX;t={ek7+N6($vZ)>#(@Za@BPxG{*UGKB@1Fn$}V%9u3t#=!Gc#74a3yK+eO zK8pk%?Lsk%cOs!w7qw;r{!C#p2>hb}W5f|d3_7JDly2lQK+|i)P{6(34>T5apxmZ4 z5Ni*#WVvy>Pt@VbdLxrEN|Igm8cnVm*Xs3xVl#vp^oE>ka<9gmaU=D}Ei=)rplgjc zOFcD9z)ZM4x9Dt~W?^E=8Lw(^g3Qge+P&-*IG>UQt0MOo&or7QNS)4HbNrVmXg4m` zcQUZ>4rU&$P8;}_a98Gre=m4pD13z1gJRA06vcmXM7`m#OMdfH&$8jMiYwTGI_@0e z)OOD$DBNPpUTby#4%sFm>t4((jvx~c)8cLEm0V)>0^`EeZpPu)|3$!O9C@WKX8BnJ ztM2RkSSHqqeL+|g&vdk5*O_KJs@b;#kn#h(3#?L)Hn(=R0I>pE5imEP5re>~^YVvf zu4zwuZ^PTn(8p_5_a9(voI`VX#e^7xoio{jmz26hO>x7AqxK$ufR~ykVf^BNgeIeP zZa(uR)DrWJ5iO`Y&KAzS7T!@Wu!G+eYqYYq&g_X-NP|nv&sIw=dQxf@dzz9n15aKCU~_P537O&_+S*vFwhjLfoB)E|Vq}Q| zE`W;VcQ0=vD#wu0de-3_0<|rH4X^{fjPZpIOQ!IXJEY90z}tz!=7T?2OJfdxVHaqy zXvN#L=VcGQK_sbMBdijH%5#S-#?S_tf+KE?gCUIJjmbi~7b^*f<+d_~SF87M<7v(K z)Rb>&gbAP^{QV?DZ z$jH5SYw-kT?}C%;rIoL;VOIC?6?RQh8g z!+5+ejkr^{b7qbl;hlLf#9B^URlUeKy-UqKLioNF{x{!Q{Lcq?u*k>tx&;zP`Zs{3 zoQm2o?01e|8;vjAPKxAT>zYlDI3bDjhn22dX9yX1+-i>rQJaewWRlk!eMEBj+WO$Fc zdtxX)7*ur$<>mAe5MJ$#QavA={7JWtaRRT=Js6A*f}R7 z@ka+_@ad0^fvFIqvpD~HRGepe0Zc=w9;O%cFbpi-{;OW5nKhsnqby?w*)t}q>(+Y(f z`&@MM4Fx<3i?f+yIHm+Gbo$-MFDBv6=*+!uUMq}aId^+>euILMM4YN{WquE6n^5u*bNC9&MCfEX zS1AN#-RGFlb?g-D`Ia@G`Lrb#$t{#JgPg7(&d37Ak&fEaKp)`OV@ZslUzE5>4=P1_ zpJygauiv)rb!51*_{ra?R~a~4PT@cJz*E#YQTr#WZku}T6Yu$2bUtvRUOQ%cZEJcR zN3RzKz4Bm|n>6)W`#br%k2)jKLmO2RBXmf_nG&`pZ!WiTU&j`NxF92OxH=r-aWLA% z^1wWtxFExc3$o8Nq43b__|+Vm<1{w0rqUudNH1-t|Bec!W-*GGJv6KYm1X{*!r?r< z)+Mg1u8bDlG|3{=h9x1ji4D>UxJ92toN?TTh3V5352cKFC`ZMdE_0MJ}m7B!)g0w0x0WhuF5W~an zvY&rw(e&;8Hrct}euJ%6i3q!p=sU*s6WmB(SiV2QhYE&AGZtXxWK$H-PGhLlazz&fyMHw~xZnfqTrC?IE zy+gMgBD^`ppUl)u2ENaKG5b8ckvU6rmaNL+t5t;1iF4&*{^o|-qnv?l&)^I-5&nHg zu03#P$xEz}2Ym))6_knLhTM=m9?d1OL$|FW0XIJemR>7C3!Za%R4AKY3>_m3eL| zj`ZqKYA+8N-#B;C1l9s^n$`2eM{*N46G36#3^t~5uO!(!k0gbnZLsX(OODI39pm-iSxz6qe*nT~gQx@<07xf}6iEAGaMJ)(*H#i#{u59= zO(4&62DYbN@fT8(j-o3=Xi#(03dJH~?=+UGJl{nEkFenyK%gUgI#6lD)h7{SgJh9yr7F zN1%nMZ}&uhgPG^^hd{5}lbPTdW(vfOm)<$J2@;pBI@Ds4C|{PGZFkwA37T+>@ro;{ zYcnx6Bpi3HD-)k%q2^B1tIMOAF$H+sUH156=HJ2b%tfDuobQ8cVw(s`v+~6ix7}P2yVqwoQ!XZ(iao{^lpj_)A#h82%u2U!^}|p2UrV zAi_bU5~RfLCxBg8iS)fei5PC8gb34DHYZ$70RHDqr5+X)LH6@b%;ARlt)Oz5wc6-sS36>2o; zX~bUJV|ZYcx9l4Y9}I>T<(pS9wai>Q8u|-MSVha}$mpfnt^rM_ilSeS3VUKg=`aw~ zGv@T#t2OW!gsvzbw`YgFw{@Md41|A``J~U{HVuilAcikY@|T0f;RR{>T)=5aIjb5V{}mYoiXsOv0{c~Qvj0p;u7=Z2751A1av)%!)gb#d z-oRXs2gRI&<8}lLGzRRK_i+~crIUIFY}U)ntN<24|I13=Bu)a3pJUD`kcPixVI^+g zU|=OmoHQ?D-uk%rbPj!0V~aXy)@mmp2U#KblVNY4o`M_N^W=D!Kh5_E_wzmuQlgNU ziX-#HSY1F1JUqU(czA?T+i1?o-^hpV?xU4g+4Xn|sz%pcZA5>I(JYOUOz}xW%J7T4 zW4WFGAT_L|$Y)k49Q+`B>dmMk92_iwulB^y&CXoFf*N*%)pM^^Kd3+bu(Y>4rT_Vt z^ybxhZFgFahdL=}>7X*MM+lSpL1Fm3D)>DAaWp_Y=Q1BSSO4h4jafhFJKcg@>;D7G zF{;bxM)34lE+gz_?IYvP5s-|04JxON<+P!Hmc3q6 zns@Tw;}f13;qJTkY^!AwO5)>Rx%#-BkKlfXpR%Rt59L}Yln2zn@&dt#g&tRxKX}_J zv+Cz%C&19#G1gYYfqXCbCNT|&gZmx{!}601W{jFa2J`V`GK3U`#@MKcA|!=U11az1 z_cp^sb&xN!B?IrRF3Wi#Oi??(NK;zM{;XqP2x1Jj>#seDWzON^dpp>VohkM=rsmrpmqG5~n zH9-|k17mPM7=PNvX0%xf^y*UvBEKo)pm%Za%^uPd*%yptIcRkpN&S=NM*TcJ=JZdH z2Bp?>@BBJ7;6oGfuBwzNT|{#e=d-G!lM)LgdiQN&>PW*76Mn)P_vLK?PEbLiG2Z~@ zZNB2rI6STI!TlD<8Re}XLg~MNAzuzl|WK%{0(E&T$ z4=d?>^pzymZ9q_H+$k$xX!nmm9#W1zy z&yXhOmbmAt343U{sdprQ0Ac0d*-a@eME`oG3r@Bn)ktzYn9u&eH-&$U!TwGg3H zM=HAO{1o^Kq-;`;^+WrsHqM61j2uqSIz&oOUB-wJ&${PrY+3F?1ImT~)|5B!RND0OrEx`}Z-Ps^fq)ksw_bz9Xl z+l-Y`r0I6^uwz*b$gw!-6u8lQi4|%o^p1%!-uS8abRJj3dh%L59yyO7moWaa*Jh)dnY*(}4cLB>^(G*W$a|#CBNO zXP42fyQ9=c?7rsz7FG+&pc%2lqiBuT8z1Ib$!*|H1L_gO)&9d*v3x`u0RADX&n6^$ zjrVZ%tZCrt+46z8dZt8zt7i@+o&59p)Iv{VW@uL3vQG=mk3X4(7V&8oy6e+tHe>qZ z)IxyiyAb<1mKok(D{Zj67)d$8^5O;!FZ2EZ=53}2U_9>Ul|Nd30jG^~FTMq_k(iB} z5<`)`ff6^nkK(w=2ww<7ZyCbQSHQwk1Xmxrinfb+3HNFK2PX^V_Z#^!D4*x`5)13w z4S4_h)O`qDWZG@~>+{+#OKzPi&MTUAQ0V^Uc-mdrBEM|84}!qT^S?3o9#(! zt!f2HLX{wvU5VqhkxvR=&R?Ygv^sIE4$PHH;1_&qP@S}a!)OnvWBAbm zADX-fA5r4MN-Qn?-A24l^xiWg%zI~hqC0zWn;mB18IX9~BQEq+p9%zCecA$+#NcXO z;3Nt_^`Koxamd749MeOJwyf$q-d}(oLtmH`j@vtSJ}wFaXycd;FZvI&Way#A#SGN& zssNMc?`KSg#<`=7aMK6wE}$B8UVD(vNuvYxPhQnKE!JAoBgt{=5+r%{Rw>fOHkNxA_aG?nH?Fhb|izx15h;eH&&{mTbxmEm)NM zucqf&2eH@6R{Hd4_WbBvL+b-Y-;^18IMaJRV!*gGk3_wKf;LR!dX$KNKHqWY7J+$W z_j0{Xo_QV#{ta1jF%7ol}u_`0qlsf^*uH?@`Tc*Xuu>JtO-5Szu`k6gH^z6>lfzhNFL7)l)l zqKZ1Ve8`8HLI)u(kn{R1k=e+OVfKx)9PRQvxU#ID<=oSt8kJq&GEE+-G;MY#EiR66 z>emRmn%?!rPrPg>9fRIu4g2l}6We*EzULA=M@=&wm#PWB<9a_Z?MQlt=7l*4Q$-Gw z&uSOzwzXlw+{9pl5$>UwVww4)>=2dKC-Y=AV@@eb zl)Xw>ffIk(X)xq@6vPr)MyI;VzBn=0P!-Q>x%hrJ_)bt9WboiQ;T*TyvMimPctz|F zib6-j3Nh{p(2!zhV1o3c3dB~QV?=CawWD|qPHD(00mN;*hd8IkZ61dv{PFz_H03=4 zJqqbKufG;3E|~Ror=Hm_MTx;-5A9Y4Yj)opG$S!gD_Fg^)sz{|3v6IPS3_pp{gN&P z7-zhK&T@Co-Ph#FzRGF~$B`QkP7m+70q_8IsMm`fqQfNe!AwkxFy|r?w5+LI(>#wZ zblak062(qyBP+^S#Ydq4>!OG(G-wQDu#TqmthBnN&HDW*=N6^rM7~V01ZAJ`o zspTUDC_r(1o>80W9b#N%3Kt*6!owjIIp6P$7e(b&xHIMuZqa~VI!$BV1mEDNyE}oEb^Y+hfj~>E8-$C+$H>tCTEY#sl7b1%kVhDwRkJr60LiiD^>?% znYQjKDjm49msR;YJ5kUvi0sxF+6NrLn|He3-CFv|N&M9i;+ewLM;Z`{y^$1Q9jl}AG2phj*exiYulkzblFIA5pY zZ>C$REkUeUJ_+zBX+^ci=)Zzc>aRbs?#RcbZ{hAB%%G-(L2%@fKrBtIO;O`QsO{8&>qA(giFMM)jZOSoE)IiN15}U3Rpj1>JS;mx9Aaxw zy5XKNqZ=JKj|+!Ve=!L#&Dr{;&j}6sRzb%p9a1NhZZIW~Dm#d~Wp613dCS;hj{+-Q zvp3u#*WpQ|kaRuQV|iC%)wyknjgA}h<+;fQggUa&2J7S|rt!B;Vj_RrCeG$>UZR4( z`3bbq_Z)<*GdZJx!#7d)lf*gx=dHRGT0Uh-(&}^RfuE5BKQBA*(~t>j3aT*OnNi8u zfF30x4}! zI$|wgsNqRun4PyK%DQrx3UX?%Lklqg0{g@)Ok>w^J=a}u}4oEx~#56YDn zj}dlXjOESP7)?q})v@Krh_4OV`1RntPeGvuSs{2a)9q-c>pe_5B8p#k=3Vx` zq~B`V;q7wZzIh`}`G4=XrzsDva9=|k3G{;Q?=GRD9afTa4N(Z1b2pc=Uk1qp;_h-& z<|n1Womk_>#oSi`8d!LkGK~(>{vY+rmE9vW2`e}dKP-Ams_1A=Q0-pK*>YQ8_pU%%IQ zy6UC&+b{_K^xubhAP+$ZAmTQPvm597vUvBeV#sdvp26*~Gqb>|*o{pEHV$xQR)Ove z=PGb>*>WE_g`G|=qm7N&SXd$!4iyflILv_h10Io?)#2X<8%m9!QDzH@Rtpu(SkEQXf)t_r z7COI5^}~1&?u&K*;=mOPr3y_8J8D;WzZLFK-|wgiK?QK@PvkiaFU>@Z&N8z}pFv^c zWzf{ndpW4VyXm5AwRtVoNb{9UxtM+t1a~w2(wiJ@nz|PSn$&)m)h%@yhU$_ZO0Q5| zd21Yz$$K?!e~#8Xh@tyyQS{;BxnD<}8Ri+Bx`%R6`@^_AD81`7@+FSP%?1SWT7f{@ z|FEj*Nm@*Mr8mZ}=EK}JL4_6+z+o}Fqk@MrTrLiH=lESp`-v{$VFW&umP-l9$c;$$ z=8z3MHS;@kehlU1M{3N&bKy=sQ+f}}53?Zqtrxd2P5#YA*b=r-SY2?xDR4s=2PWOD z`Foq#;gFF()MT#IBYb@kN^5oBv>lwmR^92lg`vT={3VpSi;{|3WDxz~F@!uuo6I2A z`XTa)*5q`3wsZF!r}%IDMFYm}`Y=BT47K)G^$(?oGD<7cdnNTLQpB`Dyw`I2PLe0=IO;BO`*iFFNa1n9p~Ab*U_ukfRI-mqGbbZm#{vhioBD(1 zG;qgZkbc1n(sH`-@@w!J<&#`Y|5)Oa<92OPP3&D?``747}zYRDq&Jp-!WVBK1-=A>hEJrHQH<`%-M|pPVnF! zESCPetrG{fBUPGR(U$^+rN^wgro3qA$$gRfNNH0OZQ0Yq*8Cv_S_bbSwjnw2v+dn*XXo_tm?1#D8k4&%aT9@z1;2G(48|DZW7UrEM8zIB-+TOo7Ar zh*hke`wwGz-S}#?Q+NagBQG*=yz`t4qvAWo{&v`1Ch zc$Se{&bR_ds+dIY`*P z1VJ9WXwc62<<_f9__ZEjj@r}9;`XJSv&aOJ000!nWMS)-e=857=kvO4Yfjv!f{Nt8 zV;MFlPUn_M9L`tsi8JTRoH%p#qC7o;tNb4>;e0^OpJ!9T>3RZrk(UMT=}onB ztKnHW1CLXAVAkEbApr0T7-ME8=pz4MmA5zHU1jnI@T0n-#>R`{#^~gAz)4+A z#fQ<*&>te)Z&tldFUNcZ+=v^(H5Fgn_@2F~nt5KR9`;du4{iRPFYh*nQ=KV2s=DHf z&@BmYyZ7!lBCzMF1$Zao{wMnRgy6`c*|>|SUe9}L1%twN3utd`K#Ur4faj2uwZ*uY zlfqtG1yJC~!ysOhs|s4l7tQM2$Y7Iavi(=_!G)rz%ejWp-&u z6W=tu&^noSX2PJ+WIaAYC zB^Do8;;U2qUe=oud6jKv-_WXubwTbwOm058qg&-Z9^}g5bEf7Xa{IN)&6wQ8jO6Ne zl_$*V_JrkN^MnAB+Bfrm+Xw#?vV0`aX5&+{YN>! zjHZfow1u+&7yA!hRWx91K;|A`1G1&n{$}kz^8a7#Kjg;GtgKgT@Gjl~)9sXo&?M^w z+}p)n`ERBn*Qk_CL&O~hG;7Xd(d+-SX-H4OG`3+S&WL z*R#+zDR3ivLeDd}sUSJtWm^z>AS)fq)@lA8lKS-619*Iu#ga={MV!k>>& zvSQW|76TUN0k03*OU^9G2c72Wu*u0zlbemFcqR>*+yx~r!^X!$rtnBJ?p`lgxoI3C){DxUiXmH*GsBnTCb%*He)f<6st^WDbj$W@`o`>!D9F+V47jzvzHa%KfNij_o*`EyQ+yaE`qfS;00xOXBgj|<9NLm59f$hA4M zHv9BDQvzxIl^R51S#~A=d#fqlia+PCcjusuGTM-al39~AU+RH&1&yL*v3NIYLTKR3 zMWtXWqKZz$fp`<)8ow=YHzI=4yuSt?5LvuvkW>A59=Bj}Py5h&hLWr%|I@cT&deDhvW>KnD<93_q(Qs z?CDaZbhUN0>3?dG@p`0AE{4Bu(1XA0pHMD{vB7!b2&+;*D#0)JkM(#)Cy}mK$`FhX zO}#B9AEc2KwW)D>AxpTLF9mQ&4k*o;Q+@ydvo9O{Re2A4W5lBBKrAsi#jC08TRLmA zcBI<=df9JD4W;@sBCj1c-m>5D?Fln4V_|YUi(gF7fYr@GLk=0l*{UWMMFb<9(1KZC zXle6zzj{c{AgAwB-eFXNO^YQL)>-3S%6etD zqaMR!XtFIDzYz+0Q@|3KAp%xBj9vPvc20Rw5BOzQQJEEs$m*443Z1>tEu%rIKY->Q z?zjkhX#m|TaIg2_1b(E2omSU#cY@S_UBh`R6caWm=K@D7OG$vQPnIqt4;j zXCaqwkk+h6E$js;-rY5LUB3&*6 zx^vt+S!O;I%9=5ucXgABEZtuF@?Z$B z@K$c2JJv17`wQEz8$0eC8A;BR%xfKa0t55Tna@Gdte*=!QNAwd9;=F13vO^In&VC2 z7YqA`->|T-uc>hk)J~dDRCW5iqqtE90w5HN+)GYxlXLzhUNNJ<%Pbcc(6p3!Ry)T; z=6{(J);aq*_lRA|Z9gT~9OdPy?cf4FH5kLt;9Qvpwuf8pre#$c2WtLJ%LeJ!Tl~@? za&vZ2J#h9azNblS{FWbq>ioV|F#j&@8MW++u$etG%WkASEx?r%{XKcjuV4=KpMwg| zlGbM(j_Z~|W|-3bgVOhr69-sm0cv|B`~~cwSZw>Uk<6;Zoe;E;Fy+Sks@b^l7-`vn zB{M)Re5B`6YT22Z*pDompy@?jAl|2~7U%Gwr4xej8Qz%pk~Zlqm3@htz6k?xGj+4@ z#udh_lh2F!3?klU-r!cWCK897wNgajWjK}8qP)X0^Ga`gOVcsr(*&B8`MCG8h8IyM zV@)U5LhhYcmZk3ybz;{M&vDNMFx1jQT2dSV|`IcHzeV{DjC4x>`rRQM^?D z-*FkJ(laFLa*SkZnyjVHtXuLhfKN6-Iu&g))a_TqMs+&B&`dhDEA%VQq@uw!!XDQL z0Qu7;e&!`)nsHZX-0CMridLk?-YI+nJJR5js6A+8O~sG1x@qrf@a2>03c!0(R@718 zip}wEXMQ|#OQK@fken#($O(sQ^U3#=LYCJNg5Xtlbp{2YC@QZdfN!JbWhzjX0KQLA zR^wR3>7c8|j}XC>K7lik4`d`{=TGb8+aaVMq9SdbL|YW;rXqbGHbt(@7Qu2sMLK9) zEaeytgTXFhuym3BjBm2P;lC&-gcri8KZbL%5bYk+#xEvNSM4cpZ;f3C9e#N)^iq}vPKUIax`Sf+6?9EM3aLpH3iTmHG zI4*L&cMdfpPLcOJtx<4AE&0x0p9h5!+2R~;FtB$ZeJO6Cbd1E0&vach92x>XZvll7 z6K|=Vr7JQ0ioOJ$a7_zZnIkN|#WzQ|+vH1Hho!;kR>dWdGoLFBEy9^^Fd(R}X^6{8 za`2`n&XSxKS}>ZVM*v?Q)+S~bPX1jXvtejc!4++MALlE`?e_P1N7`Q5p2ds%jp;Yh z!}o84hSTA6()f^kL2YplR)==}aKEkEXJF8wfS-TZ6sBgB^y|PcQqmuFsu@{r@vo-# zj(4;;Mw!AXJE@W>KJ;;;v;X@OvO4?Ycpk9Jr>KI-+zLG#>iyPG%{e$-R9Dib$h)Vu{!$Ne{e5 zD`_~20Z3*D8j%v zcvqX<;r++7VHWK_EVTG2b(P8Aboy~Q^9MZgHq>fpaXZptm0icLm6E6~f42X~{xHg% zj$gwP1RA@1oy>bA*U-Dd4H4%FvmP>SdF25T5HXk(_c0`a*^mIX3Sx$9l+4|cFZwE+^GOXfjn7vBZG9@AO}vo4f+t4S ze~*HNY$q$re?uSlg&Ij@dYX4eFS5J{f282^z%=!|s`2k;k>o%659p+dxKBJ3asJy( zw6WDhO-kZ-SfkwPK66^WX6kifq1ESJk;Q6aeWR;WnIOM$bE|l`0i(P>`qPu@(Y+@Q zMAj&|U(;&V|BmJO${a{m=#CZb*YmrRNv&(Ymfva8t3r3wx8K9Zlr`pKbya~|+n(>I zC|G+mk0v7<#evE0zj2xGZ_oaz&vKLQ{z<8i(BC)>EG3gsK8|HDp|qkVL*N$mAyV27_b!}MWoi7- z`)qZ^_Sr|H#?@HjO0~zF?8f)lRJ$6T4_#%S3Wv*%=QB+MA~MH4h3*2qWIZmuT~xrY z1wXG0otuC8Q->gb8=D_9|DU`zbZZ4UATj6Lux4Z0U@5*ij%aE+@Yo7S81nZkxVGZfa(Oz2WoyFx>$?04--E#b9HN#i>GAE;mlZ=x;l>a3{0 zI=al(?LpmOF^;7L?+9voFHzLE9y9Xg3C2m=t_6=WY;1UhaU>DLJujHnSE@lPs~_9p zBXPVpf%UTCmqV%7l~Y-Klr}d#OCM;=7q}gujLp5{oE?@PMg=*2Fc(8bZil;>AkYvl zms(ITSP=_H7t~(k47{oDaX2ChrSb$8(zS;=n167Xvt@sg2y-TD`3UT9&c~kSPAU=$ zv41pKkyHB(j$HU=^wm_P`5s6CWhxPPY8|0fORk#W->QsaTp57*I}IsQ*oB6XcOhWE zDZg^c`ox8njnd)@rrJhLeHrL4KVfwKcet-CKiFJWhpSEJl}Q?}rncD~yz1bb#)Waa zgT@ulj4sgF;`Y0tv~jsQPSBQg`#0W+w{#mtN%xAc z_cnQwXN&%5wv_h;XXx>WX;QJf_82xK97MH2mDo8a><^R3tywC<9Zn=~Ch|V6OoO*Ao+9uRdR*lF!m>7$thqwj){fA{2 zMtaX!$rsjAG>7Ps(s0iVeB)~~+|dvDivK9`VmtC2t`2b?FQ{m(JZ1RPnR)wA{quW0 zS!e6OGIU@J84~42)3|o&uKAD9UpSQZ)uHxSb!+oX?R1Qd^!B`?Ce}7J{y|nZj1>`zNNy|Jt*+$zBa0a7S-(cXj`XfN)WRyQ1-H$>&=SA<(~5%sMIG2f8X!#4AHL1wP44jvyQM3~g8wrxsLB?HEeQevr7m zIE!G|3n!WZ#^K3zxe0a3wm71s9v1((AELrrz3|Fh^HQ(2ugjZv2>$Y}D63IiCE8F7 zyQ6=#AqSA_Rd^OinWB{Dh+W(A#;kV!Dd`!!<|3X`M}lE~wf8A%=u#?tp)t(;PxvG` zxjfa>i4b~EMhWFn`8tGkez(YmklqIKKah8BLe zUa{ZVq_6mqyUU*1F=qeb{{BMwn_iB&5vi6v7^c}z!!iTPF^%u}gZtnEIOH>~{>KgEUhT7dex261We<(n zrf$oae1eZH@V1MbOAdSmT9|u!Gaa!4Px1A?YZ(m<^9svQ#C_!ctdh}O24$3t+GQi> zqGB|TGN>3qsn-uJs-o#;1*20bUc<@3tJ%2}3iOMHqFyu}?)FkDi zy)2Og0QMYIi-sD_BC{oUHDUV@&v2C#HbMIvZHS9WL)EH`&U3txd{jIAUL!N|ssO)6 z63cn~sFvZ-z&bLFlF_^;0wtsO-eOuH04Dq(neapUG{w+^tH}(Nl2Nt&QPkdRP5vIO zp*1KO#Y011^OcNXLh7PlQ696;M#*U3jqlaiyQ=MhXVXe_@mOd>Ph-`r*BYuu~Q8@OJob%^I$+%*HR*G{lIo-_PnjgKno`GCQfn zePNdA3}@^-IK|8bi^|mvCTGs{68Gc#gSYW#%my4yY_)wIl3%n-?nBV`rqVJ4&-oML z+|99xdCx_2_e7c+TI9LnXc3*K#<2+hht{NsTrTqD{=CqF9$GM*Y(df72|0ksrN$|J z%#HeNm4#n3Nt?~xCWg@}I6p!zGg(C65zS1pU*bz^T5_NKk6F3zH*L+@-%2jD!+i)*gFlQs z%8LE)1|xyfnFt2Nm|I>$-a(Asm+!uG0R@b3X@@(ZxseA*R3i)c-(F#FZhSdAo|mp5 zsqrmefHb)*yL2bNsl6{gh7ramc6ltG`);J^i4BJr~$~?{kM@O3|f4*EbA#~ym~v2&SP?h*U&DHv`oKW`pLww z>z1#=w0n3pc3Tnj2&?ULN@}oxxos`Y8FF22k0VX*$DQM#Af>HZB9Hi?l&vusPEpi! zG8yIe9u*jABNHi)ReTmo&ooOiyaBHTEvz9Mx>fEX2fL_Ffn{dGrVAKDq4WTC3WXgt zZjb$#TvPqa@P6j$^LuXL7ks6#nf1L|7Q+VG!YHbp;_LSu57Pe$bPW&YpBkf6Jdrff zw_C!<;G`?rbrdPpcJUx(*iiG>wWK$g9!$WBJ6=~*^DIzj`5(eoWpDEQ*W$7ZQ_VmI z<6rct=3N-69IE7nwVzw48(=Wk`4(8C9ZQ6;`&^ixxVr)ru<)NX) zXR4GQPI930pLRlX^>^Y`{c;A2n1Ax=UqCokxr}Dg-}{}LWzRC{;x^zjnWU+%ED3(K z6M|6lbTVuE2lK@`fx(YZOsLw!d_A+@;#mcE|NeQ0)x)MVyZ4oUPN#6ouH$pCA2Hi; zoM~p@dnCc^V;f%MmtFWWe=QqU03_=ShF-ck%=y_b2fSuC4tgyc1KYSKp~S~~kI!-6 zd=yqa?oP(&!o_qJCHj=N4txht)}GR5yLl5@LBX4?f# z10U7SJ?4a0@pc`U!@XPG=ffW6gyU4+%6+@KvKx-;)IcTts`MZA3)d{(1NydSxg)XNhW5U| zs@u~6!(CKYFIb7avArQOHfrw*D2e&sl$Zl3+V6U_5{Y2sEYXW;{^$J3YsN5*iw$1pzY z_-)pIR;KfIddh*Dp8>wilk%WDMM278R%UWTDE$Pza6`^(U`t_NvnY(>2;*2>2ML$9 zmJR*T$-F{wL<6q^8zi*)@yJ;+XI56rBGrd>OqM)#2L4W*Ibmtp^2Wt2C|=9t0S5lF zGXw_s@&qRQ^6NPmtJj~~CQdP;w}=t_k8oleeL3+D^}r<|E9JRz;>NS>*t;e1iqB_l zHv9uDkU=MZpT`8vQa)@DdygLF#&-Om&$Y2cP@YkO^G|z5Nu;uWXz>IsB9UK==*S?G zzJ#LyuU{9Sbx&v1pmoQbR(X}^cNx(cVon4Bz;_V6YNAB|aGcOn;HkR>x8QMCSYBSc ziFFYmc*VZpnUm;JKZRXCfcd|K;=`P3G5@)H#u=a} zgH^4kdo%O@x?#?_V*aCfahfS1Px;%Z+06WpMKg2Su$B8LwJ!01#XXZ~Vg8?VHa)QL z%M-WCyHxEqzw|6hbF(8?^6)p=qMyd}K%3(KFA$h#`fcha{{P>7{{K|13;h45#PhpS zADOO4hQ%)Li*njjtVFsrCiDIdC^aRHjW`PoOWb9vUwwcP!}~WW@cx&F7CoT$=daRf ziut^UtSxH``FG_K@ydrWz$`t5Imx4p$Q%^kXK`Da2YLm%-ZDTP=(P;!^jXd82v>?i zo&V+EAjQ{?(WY0i>FZ%yY6RI$#{-z7Ykg*`Ydyqm!G;g#TE`V+R@9T}aaV8jyM6nG zVEaR9xrO&SABbJx*I3T=oEJ84)GA?d#oJyKQoj&MW)f}mQ8~FFajbtC`UkqlT}E=a zaFA+=R}@MOr=ZNQV;D$ol-|foEQ|hiv${9Rn^JnNn#v3?*dhA-2Y&9{SVetk!R5*l zssJt6=Y#?7^FaWSFy5y>jb>)G^&RAP<5J5~S!-7chZJ8#VW8JiAT8f9Zh$mKg5GbC zDu8bUyG>38&(M{ppvS(*E=p+c^4&zKH5)~nZDpO|#jb-!f* zFPmh&sm2_!Tf%rqSoeJuvc0JNQQU6eh4B9SVf4B({oY&xX<)C2jFsb@v)pB)(wbt@)LJRKc4w@k5-tLx|d;3`X z!apz%4Cc{xpV;d;D|dg>R-ZoOeM&Ffsat6sP6t6SPS+fsS?|9$%Z~K<6Gm;g+Wt{Y z=yb26I7)ig5N*@BvA|sr6yAq&JBpsUi77lg{oE-RVQa@HenVfVC81P#WKdy|sjvzSkZrZ^@CNo-m09{6 zTV8V?Ryz~J>JDygD19C}tJTg)k>qyxXZv$Qx8B)}MP#;X-u7pE25lO38zttRrAKKi zRs93qkEVo?86B|;hqkliB+k+08|^0H6f7Z&W%FNv^cb(`zaFKjMb>AcLu@e?F)iTa zKDmu;9{eNF?ConfU3mJ#wr0&|7N*ZkG!;!dD$#VL|N1KcqeK(S`YkX(AmCyYS;SUE z<7uF6-0OC@uVFatnO=TvOfNMTjTJ@|Qu{a`2^Iry!$7WQ=7X$yHQritW!kTt-$C6q zYe{m-oN!(ZUyAhQUGK|e`z3cCSN>YX1$QEvG9&Gmym`F>hR2N7yg@-A}qsi8By&gPQUjFU~Y1A`>w&$G}cw#jQSPP+{C#`$GLyd2@m zc$6Ipq^L~Hm;G&1y&*f$Amk-}s!_G4V`8I`{S)}+-IxkH(<;rx+{*PQtK6%oCra=S z?RClVpcDKFl@2eowuGWBBZ{0iKeYAKP*3w*{;{LxGA^m>yl(6;SP#+$u*lGtziJDd zOo6Or;GB=Mo(Sq%zb|c$BIP&~yZ`uC`{^ed0K3_c*~dXm5imj_^o0rPD^i=x#}*ly z9NFp5ac+EFEW;Xu&iP%5Nu_BsC(JEQ6}E($hW1@ZvcP)ROWG& zZ95B64mz@`a|M3LU~XK(@(M4#@ojO+Fc36^c{Q#vtKEw+?KEhav(i3*n$C|avd?QB zhi9q`$mE0QDx7Tmx2&ixT*r-Lv}k%CQ16YY<}9yMHK*Cx=*|~8x*HVS)7ska14bu}xEFWt|M2ug! z_%iNP7~wRYZu{gnRZcI%crtO-I3hG%R$^j{vU0t^ji6R;21}^9Gqk{34&%LLXu;$W z?msXkG+cz(`}%gm%HL8O2AzwaX$PghkZk7tj3fNiyo}GmMd0wY+BifnIqdEpR|LUtH*3Ob4%k%Hjf#u5NWcOfQ3G}!Df1pOadrN8OV{mnwH729MsEk{`rZ? zX364M*4v=MPs_95zuIxVXKjriHK(()Vpiz9MuG_og4a+#F(KM+PT|2!=^soAgFT)H zYxIAOoeF2*DRvs#DvLtAr2|0l1eSL?D==|{Rw7yx0%!JsGY>z*Su!&cocRQtSy7!D zF%I{DNm#4nq(4D44<-uZI~tEGYx;tmYVHWiOekdVH9wlQkxA|W&kXz~Hgm-kX?zW$ z&mIZW69Q3#=^mO#s?~myoLE4ww*@P<`7^g4rY45tW%JYUbnH9J500W1Z%J0OrE69D zlC*rVH^!z@?HGSYBJ0%nG@D2vUUrGSgo7H-q9*os#CDgewI_R|dy7-|g{|uw_ItWx zDDEX*{qK<$U^OLsa(caB77{XgtJl(VViB~z|1uA95;a?HlZpD|SxwZXAVU95pLdtu z^g6m!O@l_Ec&DI5Rw+WS8+l3R8r)f^bm7kZ-6XbV>!GfIX;*mfGS`YXZcxa{Y_dZc zyNrFu;!ce93D@j*e!E6SXLjexrFeN$fN7d0tD#sB?bI{?5ge|WJucA%9QLF0*bjuz|Z_V%aQ0MhMCi8-1HviOmjP2}cuf)Cwz47>$LJK9^2 zdBe~?mo39!cre{2kl0-`ts}oMipK%7Ji#d{u8NtJ$`|ac2wQvcEIw1S^*f~tWeuxv z6V(%fS0W0tehYaweVfID;jYa+$Kp1Qrni=B%WiFTz3hxd$ZO9ULq^E^Ks*$J3XDlW z;hI69?rx^+*{6iMQ$gLW`C;D8a6Y0oaGR;Fc*CFR!jy(<#79^&{C1e0tIm5UBz{ylKLs4$ zX-59hzvWIfm!b##nV-mtMf0Z1x>X}`mc;65frB#6CmDDIN=)#;Jr zsw-Cd7r(-Gp|FV2)G9F~0~2#P6C3t(Dp)>(zB`o`J?#E)I`gB|O%EEUVO~ku%p`ms zUJpT+L%#3Q*Gxz><@y&rN^63hRR$_YPFj97RhgyKT!eaRE;tykg8y3Oeo7aWys+MO z^(Tf9cu_Rnoeyh9NU>)+OAJ3(Mgs4V4#DcO)ex*@)GyGH!*%T+M3|_j!mH;|>M_ez z_NJ%bJ6 zuOPM|+h#9S?kSCCOoZv<{{m<4!fnTI2MImV8p&^;Mgg5%8eNv$lw{g-wvse2oTLsjpcwC|d) z1vgyRNC4fOw9i1mW2Wzyce&8Vnq4DCiFIc$4_029Gxs-^4d9#x8*nsSi@JG3Tag6V zfDeCm7Ta{2&G08O&=P1m%#*a}HV@P7jVtK(O?1{Uxmw901J{I#r;x^N2w3@gbJ|Tz z8Hc)Nixc^S1+^kmG4XY@yWaFK?`V1(31-dqKA-F{IHRnK>@l1D@#k~OjcR&4p}SbjAslWOtTarhf;Z)W1d>De$~ za2tldo<71I!di+u3fHiMT(JV4Vs2p$@hk;sKy$vUGuVE9f}Mv#({C$~SimN(Rr!%_Jr4!RF%DpwwKfRT^<^_(yt7A(#A8pH ztx9nMnuTw*Ui}Y~Xv7*qoN5|t&T+Aq@lh{JvSxpFeIC)R#%M!xNE08x@4fAa79!ROQ$LI8IgYZNfawJU%qBc+_70fm@8 z&Di@}iwwJF5`9ufaOYA)gxuQm&>H4qbB@$L1d95|Qu5=zE&NmKA?=z9$4I<>h z`PCJJ%XHNTvRjmD+)HqcOl_{}g=<{rgq@Ph9<_(CL(Xd)z&d+Em}UQLKCfK|H5O(` z!lNK2hT$IrAZJfz&galnEzm4T3#>skw`DB8k?|pj1_59=uh@VP1`dOJGY?yEP3a$q zS;}aH2!aN54$H43yK}c!kBlM>Q?E%dOG9AiZDrr8VCMH3L zmGqSl@*wOv!lS+S?_&Z3$TDVVryj0xd34RDs*X{|s*WlFu_geap0H1yrX zTK6aa2w(}R4Oz948Hr^)5#|pZ-ou9Dr+;w#mwOVygA7wLL-%Y300_EkwB!9AYB!I$#}bi+j&W-Fu+VwXoLFOLs{f7uX}ufP(o zGC7t~S&GWS3c``;(Czsa6mWL0HS?4F1iT^J#bh%}m1xT2woBg3={aLDJJacX=u|5? z{Qf>Mr;@@4kJ4wk9m-~kUU^;YPI=ss8Yz}Iz{Aqk_435JXv)u{DLrkH)+ZzJ0@49xx7p`pCg zX5{;A>;Kj8CrG@aaib^V2`sn+DfCvBHUP+@cT zj%nS!U(r2A?EV|NthSRcFrB7tPX+n+ji!p^0NMbEK4aoMuIKC%J+9}p*=CDR{z0da zOrK{Cmm&Yg>huxVKqGv|sJUY)8d542?R5tKLsjUm$2Cr+o;f2!gV|40tKBZtp`QQN z>-lY{rw(Om9=7VDjZ%kd4nUpv3qhRqR*PDD0R;_x)^?l4$S%j5l}X-)7?r^s zZ3fff`nNbeUg0fX3a(T0@HcRSEI*L*(Dutbb=7_dzu5uK08X?YL%6PZx`l9s5r`By zXRNd<@CLb?yK!rYZDX$vRrT%?+W4;+cv-WROw3$aaz$8XxH<{WV;Nl7QDt!Kx?MB-|GGIIY z4%yI8{m;tIIZpXRskREv*kBALxj@{)%fHD|-sJr5NCVw&07jtsXu1m6bC3M%58hF- zPPGZQA3O@&&My^#Il|kpoueMsM+-D zpz}c01oWx?sP0c0}RwnS6A{8j*i9Ir?sk8RXX2FrR$w2{Xv( zo8BN#0$SZ4pgY+9)E8pGy)AKuJc-x17XyBVJgjoRxL-&1DIDmm4C^od+e(IY9bgu& z5teg2;Yfbi$94_>LZw#x<^GNG?qUkLr!x>D=WwuZinTX%s+a%9ktCXy+`;se@ZvtB zpimU>4l^)xWF|rE_~oI*dhB0zmDhj&SZLQ0HTo|I~(sibYosH{g93M zllE4`Lz7z-526NUrFo zLaE0U#|t=AaE}f5uF`gLqdL)01@CSCBDNEn<}qdXSYpgls|2L%q0}svn{drK-caQz zszmM1i;WNXtL92lBYK;fw<%+DO|7O=_7jSl5kCe$nrUSvxT}W>biltVyk)hgb9ef(40-9P7Nq? z4^uWmnS?4kysR8}?sqsx?Jvd8n~)tqF&1avJYJ%+?BDLibjz5UeE_Ke@RGEw`2@&* zFmA(K?se$Jrnzn+|lk^fI>2OrOk`*4(6HH}-X_*}lrSJab03}6 z*V#6hK?^D~rYUY$;dblo&dwJ|#*Q;~9N+4$);GXPF|AYE6)HscAGm*9qX`>b2#&XB zbmAoB-u!y24iH}fe;4BsmYA>W?e<~BO~8-q!AFeI986-j5@Ff|{J?JU@jMJ0QiIi+-sQ*218%U7DSfhJ=8H<69x6l~- zHn~Uf1HH)(=RyWOrJ8@4TZJm`!hmDhrYn zqrHz0b^{I8*g!HSh~#9?MPiMr&vc4*DS_6@wp%w0g1;x$A17Bsr1iJdd&Vb|?}A$* zp*BZ@fOB9%7;sS6ZiRJyraWH56`TEwZM<|8awg_02RZHrK1TH>R^53qidZ**NdmQ0nb4NV??z z`tvg>xU}}7Vmu8OO*{f(c>s^lN20#Z@CX(C>$!fpt~tiVLEPuhTqTy8QE4+E<9*qT z-jMpwKemKn%w-sj7Wgtcpsl)Nb4GH9uOX&u{ zk=TodIe7<8OuG;IlaGly353+}c~p7`fT-K;Xp?X=nVwr;JdVKg^wAr431>mZ9i|z# zy^=FMUm}ZHYu8UzB)oPvP^rat57JOL&6#Uk0;F}f^9p}OSgQ9y0jkM0y)ASnng+Hz zC+Eu@*eEWayWRRYZ>3JRee=NYS!r*`&GaAs97n-Vy0xV{ou1#@&7g=p<=m4f>0Zl^ zovM3R8V5T2@|Tsa0`?y|UL9gM*Ob>A&N6$PF(3g9w29HqHK*7k%|fa*R~cywa(?yt zJ~5wllS#!NGgI*73*zn#)DwXuyZ`awxz-;nFGp~`6n7$t13Fo?W_tehW7$^kQ#7OW z+!CjH6vteFQg40c(yFX8i+3%J>{2-Pu)sj@gfjop+;n{;VK~^KMVDII&+ZrD)PA_v zpFD5;J_YqUKO;z8iLk|Unf~K%OQ5B4nU;65=Q6z?^4IK9xt*8RF4K=6==RGl({jYN zF4OWYXm**NgUSsJ5c~II)>#*(u~=zxnf?+W&N}mOZ7Yf2%3m8Y;I#d)#b`Hrn^s{o zb^PxFz8Z@TE;a@d`uuCRcBig4{R0sE1#jvV3{?i{I_d|S{u9*|S2*2G@^+L5kcV2u zdGZ3rACV9?)9st%_Hp1J3(xW?!KE}=iMcxt666Ig{9DF`j%#ZuU5yh~wGF$MVuzT! zHA0f{t!}8^N-sgn)6m2#-rn_h@S4~S_bufth9rTMNzy%c_*0|+kw#xKZF zonEdPLXt}7mfy!C@i$CTtery8EZ$*&Q_&IUGh1lf@Db}@O*C1yQvBo~anea5KW7mJ zvMx3pqVcpI%~DOrg=-oc_#r;;*QicuK5(n{*PhE83M_N#uYH3)*|Ux)1jmaK19BTp zvboUw1s>BqXFrFJ^?@e3>tABLWYYyxNkRaMkM%^JQa#7A(KL5_)(SWb_)bk}Itp;~DeEE}=4RW9 z?=j}3hGra=s_pEppn5a@hEvB7-v~H~0w)MFJp83sqv(}IV+a8pyE}v*2tgrd`ashy zrQ-30h1H8($LWW$#?TeK${GI@cke6^6cYVp*!3Qc!{Pb8SbXG$5NE5 z@G#;Jn899`uvI^HmjDa`Ur;Js(*`Q%WDA*@KJ#IjBNbEG;kr>1qBzA2IF`re9j*f} zHe^6h7s+Khy|%MkPN4k#z`{=yH6S|KE#}(MK;^OE9?KuD96)97=_!3)k-M1(4X_{b zeW1b_{#kyGTcrL8L^isL>B)3+{_tWpn79c{nX@UPgQLm0#c|l~Y-|Aq9gyLP(@iz4 zRi`uk|M)T^{k)3}Be0h;?$>02EG&ihlI zp$iXd%a*_|A+l{=4=dub_xM1oqj2Qn3F06MZla03O|h;`W04xx$B2MDf{>OhUi?%` zXb_r;4mfoevI+_W+p9U&Y17%J3_&uzGBIw42vv0I^h|d{xvbEYxe>n3@~P2Xu_K%_ zxvd+?d4U9TcOkrc>@JP4!a1j2%va2DUOEQX&rJUZzgNp2J0DNiTr2EK>@@=i+Z_y) zJaik&)+O#ED{ni;$lFXD>lXQn307xf;n?8U@nXZiwR*m;xB0|o16K=NV<(Azr(V8nhD)p97vZXx6SKDh`?~_ zAnvg*jl%TjgHX-e(ex!7kY)8NJvw$g=bws?;$Itb{WD5$^&aK*Cc=O-X#)WQg$rkA z#7wR=5>-?WM!Vn)S*B@0$fg`Ul>_cTft+>Q+%7hMNV@bCN8Kj9NUX>-8%sqwdYUl2 zZqtpe<iU|-q_-eK{zcXRHuyr8$&6H(2y?G}yp6T;8 zk>E|=xboU*e32DKFk0C7p6G{sZFfX6qkM{SanO^_nn~-6BXdbK^-VNA57QGjeEfg7 zNZDS0ybo#!=d&$V5UE%b??QWhe|8=9NzL@Avgg95SjgJPMfxgEz~&JE8k#xrubhb^ zzD@F7H&`aJeTb@>O;imkD~6N+dMMI+qHSG$@*VQiw3r&5j49-JULw8AYQ z>gc2Ru3S+8HQf+8-jSLt;@eHkXVvDfy$C1`r#=9RsWkpicley9(EZlb7U#xqUK7gN=lscH-z|MsC1?@wM&e&&{onm=&Mc-AVhL?(_D z>0HPBLE|&TmC_E8U0;J|3XbB5^Ce6LOv!NP=$r;{Yz*oeZzuwEL+^#LRF_9 zwD~`eZwgrZQHnzWo8v@YFd~?~RYs&ydR?6wb6ip5KVM$JyOS3*$i=|{wBDbhzyBYC z2ioY5k<{*%(1C%>OkZlcjsEzUs!5++7M{F?wHz?Qi~PutjY}1=8>58PflD3EZOG=@ zRDaz<${2=o77soN_#&x}r6z{sJI?ZUAh$YrJ|A`EH~el#yt)P!DT?D}!Z{KTZkUba zXoNUREqx?P1q0*sc$#`7li=sfyc8UGRO0^gKz1svGif13H%Q{f97=WgSf5jb&$Xc; z5d3CY%~2QK9sSWcQgqfV0lnKH3y`qMPxBjUTeV6uM^$(TSgDH9oC^ zl`(odPlqdIu${E-F7!WO|M`i#JWa7k|Co6Y-`1+E7Jt$=ICBF)DCdt}Ni za5)yZG>Bm4LX=PE7t#kCobw*)`o)l_fE}Q2LZU`+)gPRS9aH2EW6B~mJEN0#77r(s zqRxG6Y zU7jfZ^<~UFRO4TZM0EOtVB%1!6DODm48_Rv29%>2D$YJ4|z;YxSr1s-V^R*^U zPuFN_zj-E&g^uqyWWGcTCbd^!^X>IgACFkB=_zmW_Xw6wjllX9>uH33Ca{0ToL3Li z;nsL(jxf`=tsOSB42?q0W9DP9bdvdU3bP|uzq$cDU{QuwTb^^*nL-6>Gg->`f(ad0 zCO^ypZ&+$u;M*P|cju@v@DWfX^MH8y`uXxF;aq zj*4)XM&W_Ku}QibW7Q9yyJ_PNoqki%KU%YQ8G}V0fnVt7tmEG?1<;1W*=V%zA{=lZv?T z4<|38`$jTxEY$ONHg;XQaBjZNn*Jr>Bm(Lcc@bFNinXz_aAIxo&gz`}_{WA@^}WT1 zNX3r$&(D)IAK)Io;!6;%s8Cb0nphZ@70x>+TC;y>CVzf7 z(;h3sa%>{Z@k}b(nTNvgBJh_0F8TgvrsoG1 za$r<2ja73Bs2N{6F0kTY5l6nU)tRAr;hH^CtS!#N*+Li6j|=*jP4*4T#EMI+ z>rSG|Si!_VYK9LXSfJwa!ui+9BB&^mso5Q>7%xsx##;+U_oMD4$SmBF33F13e~)+0mZ@HK^{m#T-YC?9f{v|iDUl^;HR?E9IU}W0D>+;W; zRnxMpkz_ri)+03y(TX+mZ_+r6hh}0unIEwXwVdJUb=?%&HHD1oP9+*oXZ z8Wi^snT#MUbI6!%^>3nmoOi!)dW1$ZG!yI!Y1NRUvGdVE+s!#p5v7hZ<8an`$xqNk1OkT}Dw~kmdJc!wWo3eA zOAL#KXr?Z6(gB83kWQj!a}t%MZ3IiM@}1%IF!nVf1#iHelKaZ=2VKLST2j8#Sx$@#%hc7qIO1JEku90dGCIk~YRCV;UIFiybz~&CMc4~LmsLAWrKnN1WSoG zOCHV-`hy3mQ@aC+b@>k$R{MhupiN?ZVPY#0$b;(w>0d=L_#))v*1Vi{uLz=yzqv;{p8DbEhk`hi2DMs^9x1MKUp)j>Q z@n#hX)dv8tLTyHq1Op>*A(w>aHI_T;TRMtCSo1(Nj@ErRb#PQiIkw$7<$AfhH<;JhuQ_NV(nkvQwQtt(p1yQw`qcMy-^cKD$M=S(#%w7LBcF&M7x~h}(K{R-7~nzQ zGZOpy@#;%^&KKiLS9cfrLa2O9>MWRt&ur>vLtWJjQFTw@Br%&4aY&gB_Ud*N!0GIU7XN2?!6|EZ8!3-)Mahj(># zVK}t`4zJ2EfKynG>h!om_&@CNiP%tARpJ2?QULf66Q~{|eBNwxP}M53T8VCs!vleVV1e;(uDL|7k3_*1wYV&*VU;<*w5B zj~4zSS)W;|y)w1dAKtLy;BhOyEncy=(6?jPW_GjpGWq8Od%WjsXsI&xM8ci2U{7vp zPvZR?c0@&1lgh0%EKTYzVl*4Mg@vc81x;78EbJ9LUFEO6nkI%m-mwsjj=Rp@Dfr^!>2C!^{yI;Fs%Y23fw1!Cig{e=6*ey;iO)ar> z<*Ksut$K2=`T>uhRDIwDP5qsLUHdW}b3ntwO#Wd+bIm+I%fdIFS22HSdDGhbc};7B z^K`9CgRcTII-eMI@I863FA}#E{fV+ZdMAD+ka}AoSrCT$>RBL|p*?D4UObpux7@V1 z>!6xj5%9eotVG1(&y+Guvu6vb)adD@!MZlE4r>Sbni>xuur_~C&VaQ+UKHsdSg~UM zH=om{MB`Z=>1i!ZqI` zF(`H(+iBT_JFgX!%L&aiBi4qJ%AZ;l$c*65b#}*ajrg{g8Cs4ShMzm46kd*vufc66 zSCRGZcBa_~kPQ33(d`Y#t&e2A7ZeXW+Aspd2CqOetx>f8a=151# zjT=wIbH+ukMLu-V;1VEfvO9O1Y{LryVC;#+Zk@)DF-~z$gStdh9|nvUi+dC;CSEUc ze`4&KwWs)b3yg1iy*xx$Xdlz!A*ts-eJX<+#7%Y1am~J%5&KUhdp=aMyd8 zIfI*eyz4$lNwZd(d}LZzaq`|2fPCTjhA)&9sTypOpF zbcgfik9K(l-v)rqLYkG;5>ukD*1F1_qIuq(7eCBQu{)c#QIojS`TH!LYjXd@Q{V;D zN3e&rs_(G@FB5kp>2B!^lvvm3C#IhhXYwPD@Oo-(-f%x>b(5?1|+O&E9SIPdXSFh7~j9<&%qA7SiI4w(MJZoL&sA0EnFW7zJ+|2CMZ zc0Z#F?tf7Kr%ymhkA^a-c*{2bLDNnpUj0gMWT{!$@wYL~W}k>=Q-|jBiFknc61SRG z?S7pp=#0-~h)^!c2~>#cj;oZd^+Y6{7tYKwU0xDpZUPbu*6eu~KEFL^4o zz&({rtrpklx6>mgN#2FX=2*BD?r~JFGd(Z472H+Ojc zo2IKdp}@V&?`PrkqQa(|w~{3brxgrFig}r3E|tP`qkA7o*5<*dkw$5P97KW)r^mn1 z0>|zt6fxZT+wK%HDx3iTD(2nY$S1ac`%V<_?|FCT)ll2VJT*t)AH$Lua7lg=({V+y zV5#7o-uRfjr`oYUdfL*0_)*}PCOUSUCO|K%Ml9C|nfKLOkj5T1b2q*z=dD`Rvfp#) zGMt^1LxHnlT8th^SKS~|m33WV9rMWnN{r0K&g%jr=v*P(%ZcS5$PT?V1B3m1&D%)C zMny9Dy8_7H;%BiFz`HonnzP$S)6t^p%y|T5zo7l_ROwsr*Pd{`m3^PBeEtKCuN!y* zD;Y6It?g9gNQz{duXulcqVb>5uhw^?8T!adyO_^q@g4g_4TNBXz{wAh%5IIL(bZOn z&6%aCnydxgU^Kj3J?PzJ>S_0f$f5!_FYCo^>=2h`Rp;?djW2`~FP5&Oshk+*_M;)o z9X_57gl76&dRohEcNNb1kM|l+oaM?d^NpNX58W+6n5A`rom`hjN=f`lyoy3AUO_#x zm-BFpfx351hp^#xQ8z+X1G8Re?(_=VX}T+;x+Fe3O^x?hdDCcU+ExrNS$%APtS3z< z1@#@i6#)}?!u@FtgSXNoSc?jqBLQ|Zy{z3#QPyszCBS#Lbk_^!mngt@Clla%1TW{i zzlSHY?Vax)%QIG3TRE9J(G+ST#pg$3<%Mf+T;7VJApbu*QuDQLyV;Khz?fxlR=tS- zhZRO)5o%wgW8w63nv-2dsl_RpDGR6nK2C&2(hvFAWJ9``7#)$y3yNcdxQLKr+8he< zUZOji59Y?E@?1s~?Zm;n*d;voj{PR^tOWI91i4%_Hhfs+_%+4x9a(yv_^KpU8BM3O zt};*Sg;D_y*AGul_G;TiL^@g!vIK9wTS0Dd1b)2d? zJfn0V+p$WnRZD@sFx{o21JWHIQv-C{e&n*OOwavON^%<4a)&^I?)#J47Obu_wLX+i zi6N0GRp+qQLI$9XZmZ?f`J}RISS4#^zt)hc<^lVYhpXPR_~E{3&t#&|Y?s9h zOss&t;IzbF+tws-2gp%zZ&Q%}gz#zSb)F1IE;;5*i)O~0fXK=w zmy?{QWUfjYa{{^rn|y2tk1{0(qnQyCfExNt&)=fwhxG&{i{U!w)xmD0=$IOhMVJFmN0c%0gU(#_406OriSXnHnNFg7Pgn zNb_4WlCS&B5fx4msT@ZFXMZm_Ond6Ia zX4p@u9SincKeHHV(fe%Rk5(R$Gpwe|onharxc>kT*_QjA>%V@hVcpl{MJv9Yzlg4s z?*w15-r~qtSlz>^PUSzzn7f=FSg^01zxKCqAv{qI(?&3MEwp9l{5N%>ac|;peNJbL z*iHf+s7Iu#TBoYX799$(ju{XL)a z{fouP`j^ct^X-V#)DK9HPv)dH_jsjlEc(MeZTyQD@R@;){X@{VAyV_+09!$i&2?k{ zK!(D3HGG_MV$kP=YIdZ@KkXGhk#C318_c)CCn19g*VLyYRQ!s{oy52H^G*mnj}k3s zkbl|uV@j)i??r2z0qNMIXzhEvn)+@)9jROznTvDJoDT#@8()S~<@EhJ02G_oY6ZDp zCUfF3QXuE_o9%N zJ$Q_d%LqbuRM+LV<+=e<#vhZ5Zh^23iIsWPbzRzqfulbMu%oN_4i4J+D_5Rt`Z+JG zb-IHIN<7Z|{mMB;_+mw*k<<>mdj>A#N{ER|6F{PG;DR~5@}~Kd@&QsM*f&PHPe%kP+8h!7Dtuh?4X+YseOsn8wabiWl&@dhJCs7+qM>8CoU9siz1qn9(W zp*p`^yDY@HDQaIjG&8<`u7K7^Bg_2D@|dkNB)%w8wYyd2ocdy?OLlopYXJmU4c0(0}{N1)Fnfu_bt%4r;oytuU2~%4jHye*vTNb#=-!`-9hhu)hy@ z_%&@~X*j+#WQ6X46`vs@+gG*V;MK7%{LYLWoe3>Q)HvgagfnFzn`mA?iVfZzt$CH{ z$oD4>B1e@YN8%u>dIJyru8tKocjYSlZIO-)X6F26{`|hoxSIkiJ}nITHZItg@4udV zUvm1*^w$pK?|{shWAju|vVk}D*SbIP*9P@sO+r%DR8`f*n}=F=dfky4y4#yZvg6G* zZpR%#yLw|Us%fEbzD=OJf5|Np%oI}4BPlEhlTqkjmb>B;%znD~24&jc!(5{`1|8lG z_E-m6NjzENU(%g{vMzPUnUPQQCw)9bGedw^$Xz&ud^#O_aetuV)LMUXFWjk_-AL+P zSev#|bxzV=V&=TWti1E(WbxdHL_mC7ElW?b@5=kk!GZYn5s7SVJn-qm4AoHNcQHeC1hp4 z`LmZ*lJKkB{&H4G)84$;WcA>1W#imFzu0C~jSXJoTwu00KNi%$OTw9}JG{)40l0&F z{$)ptma4=C4cYWt*NW$J8dAT5d(fygz_IOx*pi_{7z)(Bp{bjtnV5R$oK|ViC~-!0 z6Gh<3%qdgaJfE7lshaLt{$&2)qUAl9ml>D+fQ4q4QOkw85nV36**W-o2nUj71Jj@o zoLK$Z{NhVwKE0x;u&QoOac-b4R?H}X!-t6+#%@po4@nUl{q9hBbmxMahtSJ@*UlW@ zZ`Sk)U_tUb%<_k2#+;EWnrMQPK=%$Cp6Sw-i9IC%Uj z$8-9fJEJC2vu$AAslD8nsiaj4{g(KXcOF_Vr~O~7_x(sFa%^5|-w9oA8j#l|_46iX z-jmufacSlPU#MmqioQTyey?c7S^^9Jo2if7JQ@+dg?5MaWVv?wmpqOL*8PB=7WoAI z9n)5z8f@zeWX`(@V&K~VEtqi>48_a=nG*}m`Mh?gzj{sRjzB|xt)|_bg%H`3N5NlL zyyIVTGn4J^=cmeXw`&8DTUOkzFKl@Ur+}sFfz;qYd3L zJIi=vkkIYcWZIoW)2u?Ve;JBKvC1B5XFDUauCxMEtNzxEs&B@L>is8pok;CQ!9!wcST(=Z>OYtxXGSKBm5 z>j!hzoGFq0as?m zv_N829N^*9S90wxNdYu>6P|FnY!Cnl9Jb`=_1bVxD8m z8m5r*9-A5N4?R}v)?GGmEA7)2xX!Jg!S0zfyScH-{%2+uP2^z8A@mI=`5VjL7Q=TW zZ@L(M&`7>#^Az+yLtWv-w%oM?^Dk-06Z?r()X)O4XuUK;hJqfB?$P;}1BP*m;Sm1m z#F8B6%5C^bheq7j=i2N7Kw% z=F#*+>Y;!+q_{nJ%uc4aNbiEVfoDxG4>uV>wH#OG8qPOc5ofnLZ@B;7p&8i=4ZeVt zIpotFd@@s~v)IkQ`@YEp`Y}=w=f{%PB8QM3 z!$=Ri(16LDESO~byyC0EOyu;vW@kK!7Tv`nVCgQ81S-aN#KURj1s2&47+Cc$8<-1< zbNU)C8Nq17cbXq zBIm(xeXxeBj6&@dFO~bfJ~l!N|1-luWN_4Z`B-5wASNtME1&V_UeA;N8LAc%PXvQ6 z(b$A86H^0qaLVuHMwl1CJ}*1!wYj+gQC*Ld>B(n0=)B%gppF<{6wFzUt|`&Ryb)}D zp}URFUE#D_`?Dq7LBLV>vGEc#H8_(eo1wV9AqvhB@3QV^2qaIf0_bF~;5@$pko-MQ zXeJ7?-LZgUCA=v3Zj=2$e-_Mn?eA>>b$CPP;y0=5+e5k2iAzVL;6%HT=`edfGC}$L zy#sAJx|F!!BQLeb>SF3aBZsiW=nnRc__+-?!%BOf)hRNP=3?c15_6 z35WGH6_Rm3&XnR@Dn*gMb`LLz&6+kyW@dsJG-#FaOV&l|*40K=ELwj`f9+o7$0|q? z=Wr-7u!s&!LKs_6g3obEYzt6DR(0S0= z`UQ3-AEu&`IM%A)gjc-A#nWnZ8#wQNqGGmt4Lm}1D(MSVtW(=D?u5VqHq+sW35+h& zr|+*@E6eU)wO0jXKCHlV89a-0bRMq?yp>4KP$Mx?;NLy}m8bU}pW|%E0bS7PaX)R= z#fe*SjWdfFMo+nH9HAtPNrlS%$$-E(Q?Th0`r@gU|LYC8=Oh0Chz(TLo&~;{#?u3e zvBZG0$u+Zu$yfSX{-0JBs@Oe$HV42+y3@gH5z6BjIV{t@4bhQ+31T7LuZ19-dZ=R0 z{1L-4{w8?jQ=UHkCX`xdd@a+FHrQw6 z<953~ezkNJe4YELzOu4t;pFq}SXBbdVA@P_k@!QmUjYdY3NeJcY^pIZH;)YI?# zBd_j+R&wJSQx(7?q8g8g`A^A=k7-0^d01+*T!x3!Kw!aTB?ns&(uxIJ<`eFtRec|~ zs&C%Excjuo7z?(V|73kIQ`e+ysg*&0cwJz{-s1u*zAg@|*n`w-LtxkY{$;xdX7Y#h z*zH@@TyyLngrF?d`d~WLD*5K6sm-wKivkN^*RNOjro^{Xn1I~G2hgH8)MuA-1JjgR z!N%)D_~!3-71MV!wBDZ_&FE6=++Xvj)p%COvbkEioth$ciuVs>OV3AeV0W!Rt!;tRWg>hSE z{J^FjZ@WuLl%Z8WYiRX8ufJqgwSx52x>cFcc|-w=yy8l;X$`R+QU#c1-OFFi=l_5G zdAuFe^n5q(ZitVenvrooG&MGQ_C}ulffohT`G}QPB!OWv3x>jH&WG+;3U31)p(eHr z&u0iMtUfE&6=M0#rDR0DOR#{>?|2F=f9>~UMP3_`{h2@m{uQzW*%fQ&Z2Gh4X6!SX z7@{W9eVy;;n-~KdoO9o$e5U_^C&`Qj`?aTlHO6Tm_LLW6;BNd|hKU(j0H$qxaOKL4~o2?A6X$u>9IOWp&ZY)PiS2wb?A$ z-PtY)?CM2??XWt|HeazP)+dzk`K&rBH3a%{C3{r*^hjn9+eBN8tmCh_JI~Q>WurR8 z?|1j}%G^Aq_ymtP%X72-sIorLhToSN3Oz0ZttssNy>}-6&e%D!FM|UPDMl$E4+pH^ zq_BWkC}o3_b~}nvkyJa@=EE6y$zyhbA%kc7FnBAqo9b~{yg}6z*XiCvYf+>@jD#zMRSP)2%&*F^O;j3S}Ug~hX`?)7;P#o@zN|RCq?JU>U|6sMjt;B4vclnzUd zoT$g%V0V0trElJRM>Qw4-#|2&vl4X_H~T{D{6H=d*8c(&I6$pI!AMHO^<37F3&Lh!CtFaxH8b4qs{jB<)b zx@BZv951%YE7A$WYKzQ)>IIiQA_ewbsvlFjRdy-{OqF9RU8C!`h%_mf*W-f9!~uYE z7u}NKeaa-MMx16?c@qGOm1n}@9YTpU2;R+0?1wN^+b(h;(-^_SQ+9(mEhq$)BH=2x zFty&+mO(RyV+-PDvJ+MI&-W=FQLwbogq7{6Iw}jUJW*CEZF$K|QaIaf_jDuh)MaFi zq)Hpq>u%NQdo3B)4ykmHE^px`Oqi`oWWfJMxR##!#Yp~ z-J}@=KH5WWMFqPSR%^BsO0^{(3!@9CstTKK6>f(Tm05%rjxRZm@>%PxY-}b&3#=*E zR=^ryVR!M&xmxIpBo8h0F$eL$Q6%Rpi;;f}3+ zS;3|r7=KLG;WW=g*gPiq~mr=31h#!LCn1>b_VCye z!P>mT1Bq|gS-Rc9yQZi_)5FXdsAmeC^Uq~Z)?}z%y1`d9T4IfXmvZ9$#bQ4mDNUE^ zQwguErc@Dv*%Q}IA;gvqeU~l|W(H&QX|_Q{cQ`U&;sq+IX`&$))0Th@i0ArH&)C+O zC(}_)5$g+8Fb;3o_S9keO0xTy%HU&%iEoKHitaxvQvOAx#}{G$<5LJ7F{XdpaN^V5 zQQuqctq2pKhUY#6^)MvQpCn#KllvQfn)y|=B?GmVQJX$sCDbz9R?S~#x{Qd%f}Q}) z!k=Kw^t(0usbCiV)Oo^t8H}d1E`v17(>&mFe!0aSrpy-QE_7bHJxfD6XC^nfa>YNG*FM-(>iG43QGgGOkoKcwbB)7vd`+X|Ehn|d!Y6N3ty z;xuj?+zg}&+9qg8|NqrGUPM(zevSz@-KM$xh4H=2jA!Aa75|v{ri@(>+sCN*PMI`H zA_DI?&?MX;&P#r~i%&7{y=1(a?5<}Y=DA`y;(m6BvQ|3;94EIO6gzE>rKsgfJ~CIG%DP&d zVC``cAgAKnxz(ZcDbV)b@}OA!J0)VVCcW;8PxC{mQ--F-cLeayFCNZANvl2)7Xf?8 zeNIQZ*0?NEv2E`0B01czOmymgCFbyrg$r?WrV+Uk7J>`hJkChx)i5Loiu#o->Id>p z=u*ICJJ{;cFHgRApuB1HNT=5}VwS1bZx7XyJvs?IF;@QD;)ipK#CvunRz=cRO0Gc2 z{kbj`77s~x+=@U4_V~=4d7W{#8QTpTa?pE=)3w0Lr{+*0;pBaduN<@%p+mw)i)n58dV1(yb}P(H3iUti;^&TA{D>LQ8rS_^BWuO`sb z0b?YZHJ-i**zH>F4maO5z=|@UUTQZk!8p2pJ9Yz!3B~&r2Fh0jb!kr(73F1c1 zs{-j;N*B;CnfPaVKKdXLaNk+@rGf)uTswo1BL{K<6-Dtu;mUD^@hXMA5Cjd83~%Rk z9%t(}(|Mf&XqxF&le$t$hktYF({?+vSRhNr{grdt8M0QwSaVx^8RrOQb~VfPvCZY~ z{yhR`?|u+_o+P8R z$;BjaB`Pc_tG`fR3HC0g|3YT`1m6)k&k0F>crf6<9kKrVIn}l6;{zk9>N0wzkfA@h zCT4Qo8K&r!a1&>T#0Y{YM+pgcYXpQvT1tS;GHL=+%{zL&{~2nQHSb6aU={xjBLkR% z-pv^~g5U1d{$Uu&!F{bD$l3!d7!9azP6l0=<1qfsA||z{cmS1ZO}cubzwzD1C&Lh? zPOt*b?2#ZV1vKqbcs2#!F$I}-W}v_sM=}wVK!xp2glE%`&S%-G$a89|YRaHYY$eT&CGRuY#*$6UfruvA{%*7NZpn7L z$(D8Mo^5vxPXl^)$K4=V8W-9^^TPE6qzW`?qIHNy9Ej3 zccc}FtU}$o-W)P$u)hO7;pxiq51daY?`vxO#QkaTAu;_uu}l#IV!ZX(IE$z({3-`c zU3zRuMW?s_r(2Hy`UMOQp*Jx*J8UGu$kx;CabDUS?eR*OkSR@hIWsThm>oOUxR<1U z9pVBGx!WJXLd8aco+yJDZ~7wcmF;v6og+Py|7%DH@dJh#8A;&}{&_+6LYRd@|Is}8 zdklu^LpV~`6g9r6Iy9@U(+2ZVgdWlnM!9o2nhF#J>1=rN=H4OStJ?Os{(~fRQs{gF z$mN5?>k3v5?C7ukUq%8y>_aA&1(=Hf?qKEMBLDRg1K|q6Ol*uz52d76;6?dTiiI^D z0NWYs7_1yrG~#sG}8KurGbLHQRO5%Y#yXgRF z;3$$Bjj!CsSbjKl9;Lc2;+rd3YADrzHqVWymCiTqY6j!s6dkagjbBKV$dIZ^bq(kb zOiOBd33BxO947}*XvV>wNAlU{CM1&Bz_ex8bY@9qpQbxl)5IfcFx?@Se38j(d%Gu? zcgD+D8}FbiJIK~}Lo0M={frY+Y6lo3P|V+IlGRRgG!M zDlo1#<7Wk~MDMKF6sJN0f`}D7UXY!>E64o#r?T)pwl$PaNJJE_s4dlB!oKiD!nS5X z(rv0Oy+~ipes(?Yvj#_fPHes>bekE$eZJ`(wu9s4VWw?5H+C(FV7^VqBZV%^;{#w! zK)+^BQ#t8@G9}T}E8!J;&x)46747j>#Q*s4GNSi%Ml$>5?yzr%8av8s?6Bs>o@!~V zL+i%wrm=V@HH7x5r76vy5{bcJZe-Yw2cFaXxoNBUE^SmgR1)&NuO2Uc6`)95gADHk zEVG~9zlFVsrGDf8gwY#UMO8dM$wP1LLqY#s?il(gTRrZ*Yu@m|Nl}{u{Txp zQvsm41XcDCiEEHMo)Ad~CJar_Dfll%w?R-e!I-aNi z9`6^s{fJk=L|Xx7Y44MgFb;91f}8$jE9mrJDwyn5&`lLAT(OSabOckjoA_XY&zb4# zgTGP{{pf0{>27FTD5Z<#>>LuUM65LvDa#xU=|B7xM~|`()#gv}XWg?%X|1zMZStr$ zq+(E2gh(B_Eqjb1<%-JpFZ5^BR5yA+tEhZioF3dJJoQlu72&#I;x? zV5@lOK`AqqU(0+?KGCGT(atOJ~IFENp>@M&p z#WUjHML%O4qMzk+YNh-xYhMoh(6oG!*Mp_wY{woTQCh*qJAz#4KGFL+&HI|j}Dc+wMgZuR6O(-Ze<56J@TCA_!2H-*0ukHu@PT^!#y7n>^S> zV{>skg|zp`ZDU8S&Y^4Uhk5oxnX}oXdL|l^Y-(?3HK{srm`X<3)V|JRCRKa9Nk7A; zS2?$tIVF20g;E>{WGjuMa9fY;wMm`zbkOlG(>q31OyRnbR$jdQ}mN`P$jGzJRaXc8VYED{~3pqgNAupp{cA z+t_X{pfEI;kRYjVx#U1daV#l~o7lh2cS#p?CsoULg@JE;I{SSn-@}=6Tb19e@3-+e%UKK%J`nFsr|iIY<}fml;|I95-8qH? z9Y0Ps@g>DA7kF7pvRQuI{~c`f7vze6)0*FaLE|(l79;me1F7O<4QuZ4-!D+GRROs| z|A#~FPlj|_k8Eguf6BtmY!cyZ(2G5D#Bf#o(xyIPQ%kf?wSVR1eB+<2{j+In3w(Ke z8oC6sG=2a-l<7ZZSNk02f%P20h0VI76HC)o&hacC97Db=M0D)U^ndiQV{rCroRaj>^qd)`ZjpLHD z;@AKgZO0-!mhjKE);=e*ddJj-9(+=Grk8Aj1>#f}?NF=j(6f9+uUvfing zy@Q?yr~ID>v|)2)6@7m(BlOpPNk<}Br37{^5Hc;`{djstnRRselekuyzjAej#!tyZ14mN6xn{wpZpbZX$) z`#4b_$9P=Fa9$b;p1+Yc%qkupCP0~UeLEx@S0BdD6&?6#;H>McjHv$Y&J9y|xpgK# zTW6b;>=7i>wbwb_bDV#$7dxGf=Vxk~pV@kPP)|o@_?dGPKM&o)&-ZuobHQEw+@YUU z_w#d*(;8j=Y9Q8T7yk{RS;2=?$%vNRa)!3fPHT?*4UEMZ&QEzd5_X+oUMt6)e}VfM zDbTCY244p-DzjIo_BA`gk5U>&5{GRzLdw`#t*qEaY?T$8Klxkb$#m#`W(jAvDy#ha z??IUVAJX0hJgVyY|4x8l6mX(~MkNq6AhDpK8cPg;3=o`=89@+45Jf=?;++{qK`thv zoQ_BEQg5~1YHO`+)n8i$FOU!<-m7>6DS}td7zF_ZL?!R%yUt89p}+tCectEgdB~iz z_dff&_S$Q&tHGn#VZHcFj5Ml;3%dR*MQ-rH{6K3I5ziWLZ3ooMfdrO8J}S*P+757U zQdaz6(Ia#Q1bX`aAO8}f3f`IXgZxjY1Hoc9zyfFVDKN))|81p3P7Y z2qQy?sz9eqE2c@K9vu9l=rTMCRBW*>hzFpiprjIV zHzC)`fkQ;wqt(QfG+~+$GVA{dQADXbMc|>&fOeHJiu$RsC;m- z{A3iFpNxLIOit~qYR8^p{U@isBF)rO-EK>M#n5yMt#~E2$TmvHn@nn;epVKu2P4lN z^5~cDKj`^bqX0wNmzGMT;!1lNEfHC2q@ESRn9rKu6ZI8rVc!0a?m70?;PLsbt@=Af z=37QF)I?8Yv}*!&vZXfG=r-S1N}w*p{NO5zt8IM@mkFk{%DpoCGH&qb_9*>_j*Id6 z@!8a1%1(qt`|gT1-21aCcf0*iT}+Kq2JxNShH4MU^{x7;-D`XXj3LBT7a7E+NZ&0v zxHfs~_5k2Ur}>VWR;A8jR(yH@48e(Y>Ns;K@asLGo`a^tkTuj3J4MxV7Jf{Jj*}~Y zUqtpxzD7oyZ}L1U@Y!}yjTQtEoOeJ{sBQ?L9Lk9g%NkX$)=|ZNfuQ(!MwGrZI>8u% z_|awUB1os>H)o>n;i~U&v;368`?YgTtzR+FvE9U{D_R?J0a5pwfyTA@-a{yf>u_Cv zs+>b`IYxKfFLIPUV64@dhu{i?L8No}y(&{cDv~1<8*+>H#6wogZ|eikwsI31YcW+# z+z!LrT6~@cKveyJqF;>aowH)PASyYw!27vc-^OoBO;Cy2`Gss|pH8_Jns!%gmVJIC z^>{l@@;f`3myEj^-@^4JQgU8mbljn~c(0|)O|i!5^GQ6oORt)41X`A@#HHW!oSeRXUnqibfVwxOh02$mJo(!)!J zt=owO$}T25-LEUC&nnq;isss;FHpCUml4TR5mDAue1GEugu;$yAdmErW-@kva#6)K2;`uKKk7ASRQYn!q32lJxl{`^qify4#N|b>?^r-mY3SB-Pvu z38b3GGTu}%U*X5CASc$_SSc!SlwWsUC$%b|NVV{}ElFxFA2E)V1Fe-#-(`ZcKYN#H zxyvvr^~TBKF8tgfUSfo9>%MP;xhiz{8YX;jqKt=8J!I_L#`$6BGjlO`7uglLpf%g&V_vVA!5xn?k z3BD+xh4dRRrt0{dan0B^2rK`~R&zBt?qz%e{uG3sNlqzjswp6yv$dwseql6x6sj!h zoR5UGDO6E3Ac$?l3Lbju;a>xHjbMgnH1BO`CLB>!OEqYlQ##GA_~w^_ys96R+LZS( zKYpF91wQu+yzLiw^S=u`rvgoRPo{Ikn{4D*1=DYm2zfBe*KWAgP<)pM*Q6m|`w19N`a`lbj8*a%AN*sP zJnm@uX;GEVy10^>vV~KV{S<%dY8&B+wEJMhe|>PmejkJ@{sS7O>81O9Dqn}~+uEiM zy0fJ>;J25Y&vfi;>YSciY+Y5NX=J`77~kjxrKn#`Bh%ls>z`3e?FOW+-r9hBep%DV zGONv(&&96I?ow=OR>_b?lK7QvtE#E^glx+D0A7~K3LyPYSi#5#ZKjvZd_pYTo zFs&)TR9@B6>VbV~8Q|vpiVq0t!Sgc40X{KFG1rPScOIVOz3>YWLV34Z#q}}v_a;&2 zpC6E^KGsi$O{(3!rmgBctGWzjXKIf>nL1GKZq-JkFo$~I<5(2TkmsHE#9rCEjK4`* zZTaEa>!DbVL`q#UpcsN~tL`r0F@^-E(>FA{r|^?C19AyiTw8V70$utNe?LjBWYkZZ z^2U-3L@{bMqi{@>oS0au>%j0{w+!d)VeN?-;*rbJ7&B-5Dd*%qf z^N1;&;f+nFy#K-nOZaj;4`AK3jhdCM5Vya-hPF`Gg~-ky$E#Qrw^ zBa=;R8dSRSQ9eSG1|J1W&^!J@84^M3CrS`ze z-@-#ieP%Atp=l$i*|_b*4NhLx0RJzZ?_G1e9XxdtDY9aO<_FUDZ+H8N!}T2;ZQ_q4 zuNfl)H0KJTg?=lhSjj8LsPv{g{n9wmdnfE2@~Ku=dHoplhx#evc{ktl!Dp9@G|ia7@wW^5uHC%Oar zZ#$f`C(Ao0^_O2b(r{3qP8QSRfGq1MT;X5v<=tN%e0??pRMzpP&7%Uf~lvTTalWO_xw#yx9l=97dPz2%=6mBj5be zlL0js4=^qS@8>ED2l1`cO?SG9GA&F0@!Y@ROXD<%u_R&ztfk!tYt{WZqE)ZW&$d_w??IpiSj=% z`EL1%^V%-9NaE^|#MSR!mpY89)BJwmGv39o$Mo0tx2J7$UC51acPW5jYPTHz4sQ+71X+O@-V=aKdo!82qp7goW+c4 z*GqC25pUG(F>yX}45!B<%0g#09n4Q0KN#`JGp^Fr{q#^0ZO{7HP!8|KUHP*tkq`%imh#-$;gfC5yG-c~v*) z14#I}il*!K6If4fTq(;V@9Iysb8g923yBUn@HhW-5xO_-9Z!=Tc5nC=*u0CBkh|~| zQX(xjfZT8)&udys!N_)CR5C^8AxAE}UO-fiX^Z;uksOo>>y8yC00o z$pxbeN$e!3;AnDc8K4SRkQW3>;AH7W${Rp2xLY&j_Id9S$*L}ak#>J4eSa-h+WVji ze#+C^jl9)}W!4#y#FR36-aWMk^tdyS_=OdZ7AtgLsA4!3OotSgzQi$aNkcR0mYB*P z?28usj8@=;Q9d0kBzT~%3J7V_no|r|84Kh?x=o{Dtkz|fj^A2e(<$OyMj##MloHc+ z3I+~=$A!R)*k&=5FP%74@xH)q>lqjqd$dF@X9;^Q7)?|s#Ed%w?gIL%FlDu?LnGSx z&0}3m!@#Rr0GNr?SvW7Z<$^%s9D2m#J{}_iX16?Yw!|&}_D*y3_5G{-j~uAB57YAK6ASj&6J2S%B+2(1bS@~9dRy4N+*%` zYb0ku=u*0G*S-vVK%2-3uS8nafy4|32suPbt)EgSyU}p+AIALzGbEnTa#a?+w5N#u za{;@+79!-<*lodD_E0>ga4XWdi?ALHtt!$a2Oi{F3@<@QTKDiwQi4XoL&02=EiF!VFr zd8c9mcFJ>*Kha~}AXeRUs;!sd0k99G9H?rc^Vo%qk#5!P2L1RIOj36$-e$vUzoDAm z@Ks*KJ@0U!8hZ;IhyEkttcx7;3iVf0>$iSqMYbCfSFiulTG>NXW*t0Vdx&8bgc`Qx zo#{^RO?CV5zxF@`d+}pl!{Y+piL<+SqF`J-n?hxFnFu-C{T}``)5FoK)B**^x)6J= z=YxdEu<>@+c$vn`ct7!n8DE(SP+R&uHb=3jEW@OJH+{)SC}@VMVQ=z&?rHk!i5%;B zyfAeFpJpw=Qs{1fr{4Jq2nd5?^9gw;haKAaALB1NAyB8`EhTC8@($OyfWK=&G*2#y}*%{M4~LwH?T~ zqrgTtaW}(A9Y#(K-hdbF8`DTl%t7EmbU0Rra7;C{!{jM2V!qAjxurT{i>&iV>NS<) zkgX}Ik1l%bwx*J_yFe}zX0ZZA@6XE5pQVwHU3e1p=Df!C9En0UiQAR=#W7%WHywMjl(6}}p^81_%11dvLaJMum`e5XA!l=7g(&i9J=8kHaWN9sIM zAQGi4NW38QbcEkh6-ssU4u1P5GI2dDhyqdJ&-mXzWiGD?1k5d|KQLC?S!x2wL_w%R zvw1Hg&}>3w%*o6q$_v;>$tthdVBdS{Q-3z?T7;7u%_bA6Ld<3v?$^{pl&Wx#acXYZ zJ>?%EXNPvyti6SiTtbte?xcPnOu2{ybRd{W>_MqRx}tIq75LU4UoL$ z&{1u0qc)U}Rc@Kq9~W}fYU2l10IZE=t|$v@=SF}qZ9Dq@&BXi=v?K^zv?kyljmSzZ7uJp!(GHMeyWj^vS`u3<8zviU~d6#dGmPjj>kr{cEAiC zxx81+RA0ilOCC_=}_LijT2SV!93k)V`;V(TBy< z3ne)p-@Bbka$%H}nZK%V?fim$VdsiLfyCQDFcO@%s7MWg)0xYQ`KE6ah97r=EBGI0 zS!In)2$A;uwX7+ufs0sEIj;Y8eMdMS;sW}s{7CMmpd;3zjL4xg5s=AOSejmuTkME4 zx-g7Mg^AO%>eBh|6WY+cwKmSm4r7Ev4 zj#cxHRC%2L-YGQT1#=p+cQ^{`Y|IQ?~t}sey zfm;p(B%mvurie9!q1I;xG=Vq?4j*B)fyymo zDq#5lctC7IVeJ(K45ZOgdTt6iJxjQwNyojb9=exY61ePD{;%bh7A^=JGOhqOwbYeU zQ!x{Q`B?SEnThieI2rEMWy^E8B#(3l%lR|F_eG}>r9Z&HEsN&vrdYqA6YdWOmR(^B zWQM~@fFSkN(e18B&=-|e;bz@!KGQ&8{evX7N(}n9NoMy`Ck1^AkeB+93YI4G&g{^e zof^v*2l;xs6?R7!8=iVjl!IG7>2GYHrnJ~#;^v5R6-+X~G~!H!6ti0~fuL|shDVZ8 zx&v4re3Wlfn)}%mf-3;daUy0k7mH#V3{fqbJ`M7h{dV zS~Phzy!Os;zw=WM=-8>PntMqfX|vr)X>3uh;0*`%LBuKp2`PtBi^*t1$siO=*OKMO zGMgaD(<$a%^rnaz^yZCXjvj?n7xLNZj8M#%b=Kw`OgXLyBlFo})mjMO&@zyiLg^6% z9Sk|;T`1%0Y>X@(;Y{eRtMf0azPVV845C}Tkd`t21%J|=FmeAe*c3#EoVuLri$jZ$ z%CXR?m5kgJOq@M(6kM1Z#pr6Qo~0Iu$QxK9h`Y0ZLHRnXov-*~5{<*3nog16^MH3w zvg&CNRa4%4QkV^z?>3aj(0$?Edn>B$C2zs>fkHsy?eHEwuTYF;@Xs zj7WC-dyir~Kj0}@_C?aC7~yogpERAt4Pj?*n5fAZ(*=`biz|C1U=RjsrLq%A<}DVt zkUMMG`9YrLIGS>zrR#;?S~`N?8Ym?>P@R~ssAE73PS_lFZs3e!NriHQrucj=*d1j& zToavvDcmUjpIe~TAll1_JVQh;u>Cb8%_)dFV+!%H6LjVj1S{uY4OdSswJjjNi`7_i z3Jgy3)ty2WZ{o~DkdF`nCu;vY z!H0Y@c>_rOgk1NOq0wZg-8kFh_=F>nF;(O-&AcleX48uo23VCe`#P03FZC+lVqdu3 z>LviQat{l`<=zWH1Wnbp8(9&}d8|{^8P+cX{YRcTGNC2jtBZ-a2l{4FuF=O7GAAmq zug9w7^_t0Kh-JX^5Mpj_#5q^%L!BGq)4?x2DP@S%dQAge)Od0b9%_ocf4?UbifoY% zel{LpYyza2+1df`O$7<1T7;vMBR9i(hnDMrLAOVjAS*_0R=LzZ@&Nz@g`@g4-w|<& zR`6MKUDk3h#bMZ4wR^e*ZaJ!lD4rhPN3a{Gb-gK+`%cRz%jEcMQsc5g{!fMdD6XJR z>Vx3BYFswlFJ9^Y^z?s5`#(|t=S=@+l>Za4AKTAHzq?iA z|8$JJN$xz$>+*N#bh${vout83t! zPW@&#{K4SaIEQ)sbnp+FyhVTF0pN%Y2{{|n`jK#XLdm0rEb_dehe>Q-(@&tC=-yq| z3hopez2ix*9Wa%Tkx6Wbzm5vcL|*hSqllm3pJwFfAS!dF_5hm^VUGu&XLCl`m$T?) zpaAL|`kKX^#o}Jcst(_*8pBE2=dzOE%MnS#mFSD%qRPB$p;t4)u|y=q5Q7`!aY{VW z-yTMH<>nJ8#9|4Hqb^8u7@Kdg)TX?PT0iv`T~{EqiMNiw(59S7Lsw5DO*3&}h;y`$ znIMk=X^!D?5uiqmq}w=N;2Qc!^UF?=WT%1AEX>f6Qf8#Nmue;N!Pb@)or5j%+Spi3 z5CU~yk;4dM$0QRv2+WPvOAqsu<5&Wu6C`d=x+&byUZr-a@K<6A7`ORFgQw)g64csm z?oymX-R|I{@j!>J_8CxVY^w~CfNzktXGUMw!F2)#%c<+BMEs+?3N}s1COy_Y`CIGK z-hm7H)~?ER?9G&yb+Z0m7)?V++{shwxI>XwM~Dzj&lx3)_! zBnQG;BRK}H!YVq1q4av+P{+1(mZo79OG=!u!Q$vV`CjP`1d- z%Im=Ht9UTw0G9q5Stjd6#W=3{9Y^uCvBN5_F$?K&@L7hDGrYL@b6K(_=S*_c_!jNi=8ej7t zLQd6sK5PE14;!gBMt5o#pBK_7m#vwmKG>++M*16-z-mo-Cye%A*rl4(ZQQ9)YkTEF z!~kxePbdjI;IYbGA^{+7XRrxg)~4k4R;cs?V%nqRwzVUq=(oB?)qY7dgdHrGAC7Ug z|HKtcOK4#j=kvEyYzlw#VuShHId*iYViGb3U6Dhzr$K#R#}gM9xK=1UE9;Whtn>EI z8r7N=+&`>wSA#RY-PN<}u%Cbk@}uTFX4c z6G5pW?$|7VC9AVJPB-v_<2*^84wi88{p?nCGS{2Z;m@7&(Ax=&znPDXUn@|DWh;_boR4% zfVsl8&nt}*pX<%Np9j%^#DCIQX6s+)|w0z@*DlXhj>S{5w=i z8bJ2Il|5Ab42EaqbugfYv%btU9LbHlXKGjy-gmvlK2a^-w${lsbw$(1Fq>4v~B3bu3 zJ#6E!`wdD^X>0Q{Rnt16 zKU$^!1l-57aXPX`^iS2{Vs^^}#BT^EXTy};6}y_7Y5S4i>X!>?D2bu0STL9C`Zfs$ z6UC5AlM&RWP43O#vjEaoSFzOn)lD1=p7T1c-k$yK=kPt7S8-k>d!)_~asxv--9FZ1 zdg2m4fylC2`yYL=bX4u&_j2M`xnfA}3>@WjeXsegZ2Bw{8rB$XI}Iys=pw@7ed+wY zD?;Qm-X)*oXa{G#Z@DOktP{zM>0{ekiGLIvk1CGTbusm8(gBpG-tf|Ayr)ssHR%SL#*!ebG`bxOgD-1Vy9r3=<6y`1(tlbQ9-KZ5c zE|<)}eg2j*P3pc$OeEa zZ#uTf@xfqgWD1}9I}L2rVkREPQp}6 zm_)+VoY*)L=2$|QggH6br@D?+s%px^waX0|#(qJv`k)>su<{4WH^KrlNzEGo{iN3W zTO;&PkL{el5r2Pt$)i7KeA$g{)x6sKOa1ni=9e<`ay$O>AWq>Yxu|j7gef39%1Ka0 zx*rLK;(2tzEnoW+9(B;X92StYfe7ysR&s%eTJQn4{HVhu>)VYFqkJRay&o>6pxbT2 z^9x?=dW2)u2Xft>)7-Z8j5CIq!qZj?bwbMB^v>jo+lk(LGhm zYZwb#eeuQ0;)bkr@LT*9DmugJE8nw9CL6k%#fCxgE~d61UhS}v2Y1ENp#DPROg4# z+Ua^1GV(ff(sJ~8p@oue`8&(}+J2%38c7ez(fK|QfzWj!)u!A217-8fF z!)|CQn22x3OcY)HCHQ|P(g3mnOYRZsI~ps+pha#rvg%?ea}MR>z-?&%vY0ALDVR&d z2$hyu;@i&B*&+O!&r&8cBK7gMX+ry`I0R3xxzPLB;z7jEio(saU5u+0J{R8=pBpBs z-sH`J0jWhFO7rMbm%xCik@2@?IX|{q0avB`mRrC|$$K#>jDL!{m*X|2Jn@4pwzhfy zS|#nH*yAK^dCuPrkNQb>ai2c=QdWN`T`y4E6gPD4_E`C|YP8<6ACZdE3rDtxsf~FV z|F_ONsHdl1*44p$`dFt>RRM39+uq8mcixqx{|>$Y^yQF6EtFX6!~|10w|p#}QgXL+ zvb^dOzvZTx+6CIt2kUc#OT+hWkK?lY_NO)0x5XcK(gwf-BaoPiscL6?s?@GEUTTbf z8UM3BB!7BP*1o{Cr0w^Y4o>fjo2UtFm1`XsTtW|~kmC2i6Ii=F9{I_3#yy$(y7)We zR+V$hyX^7N*xc-9GB|8PrkSWU)8qab8YeV*Vq$A6H~Fnp`mMaU+plh3x|NqZ(hA7y zYavZ3VYk|SK;TaTxNMG|e;(h-fDAyEQ~TdA#nDLDZN+$!-9{&0sB zwr2mEZ0vT}`84yXl|64rSrpZ6`?_A)aMz8pp@hA=!Kp^!C1*~gQ0&vJ(fj&3jk(0n zDPJoYQExz>9-BSGuA9@4E=Od+g*TR<^49`{@hFY8?|6g{)NqLwc2I6$$y-`5x5s6B zxr<8ocbm7p(SSDbkaAG5j3jT;DV&nyf>wGh;=GNYl2n{VY<^J!6d{W_-aaf5v~zZ8zhYIZM1D3{jiq?xmWw0>8c6Hvb{#hj#P79DfZI`T%-K zVTtQ|=G1|y714*)Z=9Z9{oCf!!PI>;Yy3lK_n~aZ_)vH@g`vvf#Q~U60*^5qCgX^S z3Fie)U*)Lc**!w;)g;a)QIRSS#CgHJQyQ1$P+#*7Z$9V}-ixfgTVk(!SMo}s=JjoN z2AHrvddCj{)z!|$%oD$2o4kHpg+LH6VNdY6hy^T>?{0Avx1LRtL&;~V0g{$4Wgq5N z<=+M~_bl(7H~iT+YE@UEEswLQ2!qz)cM2hCjo=Zr7LR z4dZX~zRk<@rI_*)#mQW%iy13hIIzVVSdVTnBLFp57tm;qaj&t$a`p^tfnN}6@Os5} zho-@T-sW{m7v@d|cibIQ=wNTTs?(xR=uD;Iybc(;vLIUVy>I6P=AM6ka9J67Vdr!9!@zn#HsUl$HoNnW&xbw-tA!pE|wjQee z!z2Vh^`CHK=P^}L=ga1;jc7fkhVdqSB5eTn+boE=ZQi~+O&AfV+phjrU>cOTYo8py z!&Vhl3XQn<17)F)$Hptb>ZOf8=te4##Jm%~zzx*bGGljE&`Qr`TZr=EVqdzqfM?ZgDS(`w|hIqqS>b;U6pwkc6jwW$!}( zA#}mh`I+)!VEOsIgxC{*XnjB2h+COxD?gGvTvEow6`6NAPrHdx8T^@=b2Xzo6Lxh^ z53_JY8F1{ljog4ALDrO}e?w{9D3n=0@7IJRuMSZpkob$eoHf;2eBO$kHOHEJ&2sM3 zmD0NRksjP%! zgJm|yua?YHt=bNJIT2xRcdGSAWgpWlCV_mcX0PJoo?G={>X7|EQsRwvpr~X>B~w;1 zWQn;XYP?;)vc!&-*w!ISJds3=PeX`mu~g>|B5Si|#1iGqFf$WosvmKVPCaBLzYb}O zGqI=(eA%i`9w;3_JP`gAbI~dDehVVyEVP_4 zGjv%h4%Oa(zlq<<0R)>}C_pP-eis+dRHw7#5ukBtXxZp_qeq`cPVgM<np+ZH&g$#m0_g}t@elxmdF>ai zcmq#T6pU1S8d&l*A9~4W%?>27Nj-4$dLAI+2DxMH>Ny$A8N8semHrco4Ecm1eOU+* zdJ=sBy@Nc}ea?N1WVQto&r)CbMZXtx!|sIg@zVOO#_^IGMI5DJF@y^B%)OC2ndUsx zX$?s6nOtc=%Q%63zgR9r&U8EdGE%W--tp1W@4^-D&Fig;M_D9yxA*M7wxg-O7UkW* zU8`z@e2$?f3~x&3|9yC^`r_~lWUe0euMp`WK1%mwFqbN)mbO7rR?N@Zf1|8?dBOaL zal`RVVU4o~_XO#ia<3zha1f7cipb3eZTDcF_znU?5Ei`e)q8fd&_)NQaXF8n+T~@; zCT~yjhGpK6R|qwbHWEn4rVw!6I~*QY+8GYEz9eFE73BxX{8UrTaN25}&m(-bbgftU zwR%meb+su{B`)Gi=+nojHJtnf)-;lHs5)x3OLHqIX^{%z7et(|{5!L+v41pS*Tn?L zXNYwt&;TlDzuJmbP;!X3=}?(F2I?kj^2G^o%Lo6ifoj$RN|1xr5TKDHi|Da^O%l_IbK{S8X=o5Ue zBiz4%o+|d(wCcbL`1em!5Mu*z(i&Rw5ok-eqP=O^pFgGS^?vpb1@L^7E$K{P1=>JA z7J92xg0G!*sub+MIzO=DM?9N7VHq{h-!;X7x({~&Cq|f$FRWfXsI5-I0P4f*sL#9B zTKmXvZEu0|wsM9AR_yi8OShsX{gw`HYiTH%T+0e0l~ZWxHd+e1lOceo6b2V})Ml2)hp)fws8d z$5$D4L*nlw69iD` zC{~c9S7B#Qc+qBGaqB8vX1`G-9Rx{t&_Vb&FWgYC4Ld)E8$RiDX0rQ%(b9LriUxxl z^h+g@zF0hof-sRpLr(87`UlV)u|tDM?_;BxU@T4j@uHO_@5wdIs6tDFfS9rWH;?GS zKxs83=19niAZ*ms#wYNcDIbrV)kv-Re?~zK>OZ60o|DaasM`~La6Go5@ijtWZ;|r{ zO^0I?{?JDu>VJ$Oq2GZ&bOhi?)0y`3*fKShA%y*M!UORM8ebcWqkL4X7nKV;WBO(J zLqD_CANqu1IXC}b{Gm&y%%uJMgkX|IcMb+^HD#pzlWBQ_{Gp>u;1pd)Ug`tNwS0=k z(oEi(Zxf41E$c+7#+Pn!y1Q}Qr&T|mrpPT~V zAPb;e=ap1*d-3UnNJ*J#t?vAb4%|V?EPuYgzzD8%F~59t9yU!Gjn+C2PSxA~ zwqr+&ci^MCkbcR1#yHP1$?mjtA-A~ipJT4)>Mo0T| zKgc&qM9k2SZEF*%SQDr_oyII&W+-{-4CDM=NecQ?+!27{i}aG%HRo#Xkr*j6ZHM_$ zp|$(uc*wv1x#i1&1bDd}cCdiSAz=j7$ps;ATvz|BYT3?^?w-a~xycXgW6^NpM<}cx z3xj+2ab;OoP^Rs{2q8k)NZcAAw)`?`f6kp+qI{HJ)yLGu09l%UX)!#Fg$XiptERr! zizD>me9(~hz`bd-_fPs^Rwip`)cam@44VPrPBt@DY{a_XP!ey(`MVbBGPKZlXdflU zj`8ANczT~KcIQWY{6m*EGGDv!0aSZpZu}}1Wg`^k=`gTD#6A2ZJ_IL>3tlYBiN_GF zUD2NnCRUeIHI2uJb51^-viS2lGFWe)Kupc~8aK`vdv684f<;-;jN_?vTk*TH@V}o) z95tq+`8mk~O0O`XRVqqKFzv8c7-}{Ms}@w z8zw->@rKCc(Y z&k0)2B)udTaQCR1@q%)tAHvHYEH|Y}E2~ymPM!Y}wwH}!gjZJmN5v91tG~(l>;`%z zXB#89;fpRcHCj}Sz3z{s*Zp_oivM)nWiWF@Oo-J3sZdQnR*t0}@4m-HN*H-`1=dxy zmv`mr##~Gfo$Sv{AknEamDm=`*kc0y3;9@G3R?eqi3e6$71K-NOEsDlHA9$7rg5bv z*eg>*n9KOA-j+U%rsiuDbyU^;Vf@Uc_XNs8iagOrOBmG!GO zc+%)Cba7^Uz9chHSIiWwoULKZ{FA5}blz$kOTvq&Up=qR^nABhO3!6@V-)!(Obg_v zdAJY(I`7HtHp8j=_z?UE8Qaf0;y7HnvPLaK1-X8puA87&8xK0?+@vr~`iSTdy~WIdNs}A6MS5AG!a5zo&O?l-s|6<}l3jkchK29cMPb*LHuyzGRuec(#_jvg$H>c~9_C zS+(Uk!WFpX{X)Gd@PPnW-f`aaA$gKYCW3m&%<;7v#5DCMPyLxT7p%S2TTiSGp0TXh z>b>bd8$dMp&oEH8djHaM8Z3?km@)v-&3`sfkew;%khojD?;*8#uGVwPf97o3>TUF& z$LRST|9PUGU)J-qxl{EV_S>4F=f9Dj;!@R$uQE{o8o;(vTEyOIkHNe9BSp0Q8ygv} zFruneDNo#NCAsXvoPIW;zGV} zqo#{^Yo(^wBy2Ps))+xtenlOJ}{w;a3?pXY8bJl?V2Y6WdYYnL44}z_)R^;=d z;;>J0Qg_*6q%j2Ft-MS{4^~ly-O&Mj1R%=!6mIx=D18kLY>Jd_i@2w}5Fz?%P4dj{ zquj^vUn`wj+k;>hIm7c@qV4`+powzMpu z;g>(`h${3J(i^q~(rM3-7OnVs!J9S7OS=1+>70AW2{wEk0G3&Qh*e!-am$XsFO6-P zfJK`!pD9}z*1*ai)f@=Na#1Q)03v|{Dc+CUfuNiD!k^`J-)M2tv%I8d=eAjXG~v(k zVrr0{u;*INK%jM0=uH*c49i^%xYf1W2IF(B$+V=d{KuOk~ z|8=SYl;9PyKsy2j!wV$%SaApq`)mBKmpf^#Y~8*}QF-mFP@-Bk0DQ;aL%ZLJxEE?t zVyCXWMBOc+yJ_H$4VBZ#!$?qhP34!~ZvlGVysF=`d1e5GZLobk)n~!>CC$t5CdMsN zSH)hS#ISq5T`_AG_ia|gI44Qzf-lLIjDA4PTr73xd_W!L&MCCE2EX?EW{6tTklAw%E<#0(F1tBI;EbOhfWw-r}-zxyRejBf&XjhPT*l9*@Cf>87 z*T*V}UV^69@%Gl6g=Lt+c!xICh(gRs9cVqzT*SLlC~JDBrQ*zfh)aCPF7c0SQRm0m zJ$+5bGgHXI8205pW`A5Odr)`N6G(`yE{XHC2xC?bQBK)I+60@td_c*!y8At~VR36) z_I5qXoAXw0%T|9(4`I{jLsk6f)YX=6w85`9Vbh1s*Z^wAmrC{?UZw(=dG zp-9x7O_$?3y@sUb(4cyTXWOkS<+Rtk`$1OlLXz87@NvEh69+d0*MSurYyuY_+r}oY zoU5;<@>M&nm%@lUojVj5vS0I2x}FqkG!6%YOV20#yMTr@AH$C?A?Hs2${DU$rt!!(@PHv@cnWIcPt?s|$0!O74a-7~RH9h~y5 z@#K#v)mK*Yt#wxdoNaZ_MlCd(UNE+J)iY4+hoLO`d9iNBRU7T+oczW#ucej0nWg{6 z1#8jQi`Bo{eFp??J5lFF?Y_YLN&M{{xNV~Vvv%Lmz-?dqPYVLKJgEe-aACj%`MPH6}R%JQnbv!pq0J} z%WD|ii|g$3${wG8*Eh$vM>hUJ@^Gp4N1cY~G4J3#L1uW7+;t(M*5?pNsdl*BHOYnq zVsUsOq}Li%AO8!XN`0sF+}0PZ)|_SY#@ki{dwM=eT?b^Q=}`{7g8&Lp>j!7JgB?`) z^Fqh$$!<5cIOL8Pg4OstxSLu=(QrfRp#91O>ZVd<7>S?iq5$q51~31wLX{M1UN6Do zzwQxdAvKx=#IET%Uubi#zo}b5iESChmZ86?E81mLTZaCo6r#%yyz(VaspX(O?(F?S z?&*Uf6`OI2B|K=p_U9G^>fTdn=ZodVd~|doyF`Qr~7z;cOU!DBm<4O+iT>SRBY@4|9*@%UbJ*`xZ>@3y(1N0 z%sYzHC{+4kq+&}r5cw%o`h)7q3*~NV{s%XIZf37u-!+t*QulH~fzZE=J$-}LD);N4 z@u#EWPfJ6_XP=+!+jPTvDHy>8wyVsXy${dmz>SbMrvdJF0-vK@OK^a|N2!ERql5d= zP2Q-5yKvS*V%GLwiZdQkprEd$ps(Bwn~M3|DQ6e@Dy5aS+f59T9M2?MCL1LPOC
    Bg!`p%`c<7$jM{kf~rB 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 z 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

    FLw#b-qYE2PP~n@oz7>pE4!{u;ru$2zrc?w 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

    >{_-1P)fd3K9kFaLRh?m5Rz}gD>F%e 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ℜ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 z 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�-J)d&imxy9M$tPnb##q8T04CkT zfN5X=1`Y?nOvW2~j#<0bsZi*qFy>N}9CUyPN3O1#yyH?=DfjCr#|@X_I6Zv;@nZHi_|0 z+2Va1qJT-)h3>9VO2t!Cuz?+~4O33WYM^Vm;r{ZFF>)`=K?VO{sxe3b%Kb5tuZb+~|P zvoagn5fmvxfn@yZ450{=ZlpvM6qu5JcvFL2lgBbTYnjid%34Z-u`urz37g zrVb_(=O{4?GxZ}6OQXBdO1*z4AG7JWghzt#7j`y=UuFA(X%F#}CCurg$4LgqQPNX+ z(gCM&A=s3mZJ?@AQ+M8t z_Z-zt=7(9{rxoOm%}+XS_2IipKnV|tS9U<3KL8lgZEH9@Gxiv|U%QZ-@=9m-QNAMnM8|6;W!J|Ur#=X3r287dmPUl+Du87EghNXX z9e0RY%a1AxQD0*-x`!I8ofWmkjW4J9an`&x&N$m6qoXUZ(~#7!%V0;AGgecokGnRx zy4*@Ysx&|W^HO7WHQu`#?_IM(aF;UX{5qBh77%}IoLYLU-{W+MR8VNV1<-)l1>?YV z?qEQw=^#;J99_rhb@m0!m$ZKqBZkR6vrPeRu4xh=KzB5P+W-izwL3{Zo`(s5XhLU| zRCd0TB0yE{7QzIzo@T$1E4FzIuqb=8I03G=gb6eVNB6sM4dsWh#aSIX7;BNx#s{EA zXUbB89kwZI9?lHolwxb(q>am5RI{HLw^_T|h{n0Kv=s=5gDp2ek!okIl@wO|iOZyY z0cPOG!0Kfnn;^IY+5z6;@g7!4W*3rHhj;9HbbgQw=M#5T>ef0fWAj{dYpM`5%h3dA zl9M9}7XpYwaG6PG19t&d1P!S6HM+ri0oj@C^!o0Rfb74kr|>{0F&)$PD|p4 za)qTa+{UUn(!m&sxrtPmo>ElStR=dE8@CCHa1PMNa2R9AL$kRQ5+EG}m+a+-)5Bnc87Ane7nVk3#{^g+^66T*{|rVZYNv&9gGIO2{8emJ$#SYsW1u*G|%QRssG1?cFg|ukEcn8@c+q3m8aNNyERLLv87z~Ok7) zb?xcmI_7!yJl%YSXxyU!mH~?xmSB?Cfda53Kru`nASjY!g`fayjgA##9Vh^&1WpPl zHU|oTVsn%LC?d|K>@|%KmkxZ4io^+$btfjEy=H-0Fnnmfs;lm zJq{NDrNrR^z>!8!K8vMu>|?h>Ot{ z5it~3;Ph>jAZCEcawWFWPuAcSVAM}w9r{)s?%QX6xDDNw(6XX)h02z&AW&qHhF5pK zCKnN>3gTozoG6H;<5TL`lvGn0hkk zyeA{Cpr)BI`U*d;g-|WMiH3ZFkm2nv7Fd*N+k>%E)Iv2=bIs-e@EVy9Q6}d+hBLvu^SvFvnXCz}8rvR+65i zlyz5Y5OuCQwL6G`sdy;^4soun>MMw{RPp)>B47D%q7rJ+{%Qgvu8)x8jBbo{I7gG9 z$-v+c)?q{I@V-Nu_vWs7?+AvcBN&~IU~oF(ctJ2UoeS;Y2v}u7F*3cI2zJ_r&LU7HW*UH^GV4(hDL+Di_N=%RdBNWlCd6!qj;<==5Uh9^P_bc-j0~cIwn%9ZS~} z=tr~&T}sJC;5vql0+1z(j$vpBp+pPV259%swWB-gs-+(22IQ{U*?sGqG% zQ^Vn1q2;c&vZ4gkq`-qwKfaDJpSZI<>Tjv`st<_ZJ*zg$fT_}VNguUj+O8{JkIobK z&!Cqu+?fMeCoc*Sl~kw>HSt(=r!|l`ykM;&n^qU#4glqm3!{$uy+a8*IV`8smFCT; zgWMk=HrGkk6$)MgJsBbAV7hXkmDH9N7Huv!MYd@{p?!l!bE++zjhs4KT%CCpHc56t z(k@nQpjc5knTw)Q+oSZO@CRkBF-hM^vN4^!ve!9vUQXi6iB{cnCL6J3c?ld2pNupj8e&S0LowT9DqXF-cI5igv?4)GlU(DkxEm`h3N_lI$93P%doPx z={kd`T04IR1HOSyIGUpkNG-f=28;5sa%kB>YJ=nqwI(R#g@lr}5ZX7G*upM5cN=MI z-fA|ral2xnoZl$BQnF=i6=w1Jajq;tslUV4VitoHS{5tY0i-IA zaMYkci*Cp+VrU3)ALNrG^y<*d>uM1&!IR(?KrNY)o|e`&*N)c&)y*IQ^0mPORB-kc z?er?f`Kq&0m>vVW%VN<^4NyCIAhcv%R>Shih@f?&Hq6MF+ETbO!ZO1lrNkih7)WrH zp(j;tfXX5bF+n+Y*?6=Q<&v~&f-abZGO}23n1&=!fcaLYIn&-AwNX%mK==Vr(F$3I zQEFuiC?!hBAwwn}ssQoux`4&y##i(+0Cx%jYsr*WIY6^lQY?Uy>kVz^hWLVl@FA4k zJ6zI$_zp#b4UIdqaqBEglXgbD2#{Z$ovn1#0Y))v+wrrb7N0z{vb|9&ehKsZh&&OH z<=rhITPVPBe_uCjtg&MCP+ZG-eA8KJW^=X{^VF`K0O~DM*c+#;+ljUbYzFvK2(q%>?!G`_T7~T zTTX?iRqDak`WlG1HdA&S2cq1CNkLI^ocJzFcP9l6OTd*2>@}Dn)V$Lz8`(IbSES%I zUt=Im=~E?|Q^wStPMFe5CHi=YrjMp!7%NknW5<+UDA6>7xDDiz^WsR*Y^MJQ!;vQ&hVW%p7MPL_&rvQ&hVr6Qax72#y52q#NLI9Vz} zDfttnBAh4{;Y2x1sR$=ZML1C^!iiE5PLzspqEv(vr6QD)Tq+e|sZ@leQW2KQ;Y&r3 z=&9)ErBV@=N<~;I6+t4XY4{l_F;w9!LDZBk72$ZP2og+9!<33}yi^1Us-|H|MUdEP z>OLbSx+hGxt&vB=nl5Gb1JVDx8s_&gfDRkf_ew z%e}I=R4UM7xnCB`9kW>OnZ~kjaYu>fndI5DVGKjeaWX>8btObpK@ss-j{sJr9oG- zWwpFiR?BN;wak{QWwu-`v*l`;EmzBIxmsq+)iPV=N^I6PWwu-`v*l`;EmzCyX0^bhKgjUOJxmsRCtK2ZoDq3dC)iPVI zmKRbk31?BtY`I!y%hfVlu9n$ywak{QWwu-`v*jxHm!*8lGo1U(l23^arh72kg25h) zwVg7|l25s9bhkP! zO_?nRB`d;kelg`VL!5eaO5)i8!M@H>Ej}?FgYDSs&Tu-p=`@3@eQ)ovT)DcCEt4QW zsmJ4bf(SN|EH#PIG`lit@;Uzq&xV99jbT2JSV#9+#3Qy&)k3?YMm8Lzl7}`B^k=$7 zXqcb|xCMy<9dZ;o>ccf5lSy%J!qJVVIe0|FB0FjyCz9lzpLFW^^P~DO=ZTGt6ls#z zK?F|_=Oc)a;08SfN`j&S?;3F3kBDFD)u2v{K7?-k90?wwBY+|Yr%hCnF<@R6KuC=? zQ40q?f9nXLm4yB$1P($iuKPhK5dbm0jqEZIZ7jhy>I`7MMF1v}=nL%t>*j>LiwYNw zCoCO25dvNPx3e1}$(QfS1muJ1NQ4uwG%eS(TQ`}76dFcx+L0dWC~hH5r#fmq#6$v^ zi=%j1QVn#-m8GN!k{=*Gi0M4^@pz2_>A%WO5G8~9@Mu9ia6!CpK|F6kyiSVL0LG{k zsYgr$d1MYIDV~}EC_O~>Oftpiig+6UfgloK6olOifFOI9FtGrZtHT`Wa5LdS64do& z0r0)CzI7RLxdV89$ffEKC93|#A$l)30r!ViBde0DlU2%UHKe*$SSzI~p%u{z=1K-s zAdJd6WWWL_EN>=ol_V#43_wnBE5Hs0jI0eXv0HbVR#z_bQg0NEhBTrtFp?##$o>F& zF$k<2^!N*4Spd_EcZV6_8qjbtAoCh98TR=c^df4e+}H^24*T)d;YzZPnG9F9hx&M8 zqH8Fo8wf5*O;Tx5>l1#WqMQY(av~1ZT>@{_^wD|afgTjbhba&C@QpvEQJ-6h*f5%V+o4Wgu{=(B;M7TiyPSacDgEY?B?Aj#opIxMsLb%J0l7qjn^e$X? z`1IUmOOT**(mhBJhSz=U62>Lr0+kD0&hk*$Sb82sYVOL$rDAtB?sbB)Lr_>3rks2VQU&~g&qr5qthuM^w{xm3!5-8GQhuK+YG#YMOPWtQt26m9M+L#`vz7l4yt``S=5 zyrt(GsA5ZBZv(dwZ1Op+DT_dI*&|7(LGJj)ArR^EW!_LiU8bnkOGCTBVYb{&J}N?= zYIFGuOVSrkP^R_3=!BA$kFe2ufp8b6BBq)0l^*hDm6)N~Ogy#2XVPd$@V*i5!$c1v zz#IsoeG3$iMWK4wKY%22YhB$lJ9FrapbjazI#ATX@de2K(^UYh4@reZ6UQ{$tx zV2ZvWblWjZR>T35l(f;gK<7cZEUQ?J?IHS=+?=7g;L>VrG<4O9U>QQ;*h1le+K))K z1(GRyQY_awc1edF5!b4U2_Y^cLJ1%aPuB<#xA_tgXT8=iY+fVK6}}u9Z4KiMn%eJk zz|a&%P#p~<1e+cJ27MFG07f3{m{*xwtN9wn0SFT1D?|E8@wDUvFeI;a9x&b(P>=75kdt{rbTB;B02AQ$ zc1tu5-6;~f$m{_3A-E3%VwN$?Q3i2xp`Z#U64uTgh^gmXX>yChG~km#ywHe-F~~^- zi5m9yJ*;ev$2Y-Eb^%>V3n7&aPaFu#Lbj^nWcB*0YNo1W+@S+-R-IV7SF>xYtm3QL zi>s{Gt2j_BlzUcLg0Z9Fv&yo&%G$ar9Up*P*h>H~lT}>~1Jv-kA_l1Gb@EtcTe}K_ zT;<|%RhNDMjCtdW<~K8ARqp(w$Gpm%B6Hq!NNxh5Rxv6tUl*#0g!Wf?6m2y|LBGwG z00Kkt;}=)sCM=d$vE&jjD>->+7y?cRED0PJI3}h*L28^$PzKRwB0~%F|#Qw+K$UiEohcYmI7N8vGuh#2TRWs)Zoz{y2}U-zMfEW_^2v| zy*m-&-n>oa1w~W_P<9>VFOW9PgRFZAL#y#T)`q@D zhR9YZl0I^lKZpg>Ve+%a1|AGDwj?(N(--*0j-^TYsH~I0wLzapq_i;du%uGq(*SgG z`t*lB5&-mQL!ihG03|EH05*Y8L&}|$9z7>MGE!(g7a+JT(6|g#98TLA>9IvYfWc?d zA{AmXNO_~r)Ex(~uR*#_@l(`7IVlj&QBna(;Zf~c1ThZ*{vv$l@x&i$rYf(qN~db{+B{N}ps5=K%W^DR=5gWt23V!4f} zb8y?%Vw+uZ^GUc(wsUveWk(m9QO;IQu~2!E&vk$rfdMawLDQ8_l28iO0ZmkI3y8#P zaA_cJPd73-vGP?pM*Iv8%`T9SYWQ#ydB-l4!YRzPLAxI(mnlA~W~A3LPatca!2a| z)*!A6X-QCAEQ5s9;OIMKN;UMcmQnf)R*Ly_OVaP(1f*(Ijts0P!mt3pO0i!@6gFUY zFOBGepdvhWg-suw36g^d5Z|yuXlSW_V5&OKs7s8r-$Q6s
      mG)t41;DTu66;3vwg-Tqg4fW$+|cI@!th1IFbXxPo-X5L z=_p>H5P@D1P!qVAxZW0?!wKU){)K3*^OYz)4#isC>S(}#_0+FQg^M+wMx?qFwQIfX zCPDyVj45P8#tcmcu3RxFHxWaf-piZ%L>yw!-q2ghP#oWFfFn1y2?A!uDK6T8s!1=7 zHIDfT&3e9@HQL(b#oeozuD;YWtoG{0d_W@vU(hSeQ_@cy^P#8oN-3XU6T|OlMJPC` zcx!Z76E4du!x(}zObc~@s;TPA$g{OtJ6>$GEoBfj87xi)I+y_#0368xrvy$4oDf(N zI4;0pOi)1a^tg-QB-^0r;dWb@n`PN)gcpFV?6e;C5!_=>q!Wqchy(Tl?Q9qMTZ;o5fras8*C!hbAcaFVK#&2O zJ-4BPbf4H0(nx9|OdVmUrc39EJ+R)R6Ph2$+qi|RoGk8n0CP8GxOD8f2~N9N#s_>^ z(b#CaXS|aHsI#4Bb)00VQ6lRRpd@|Xa{;ozpmhn>^B+QFwoOBjyL$^@7t98R7>2PX zOi#frgtmIrAnQ%gq-S{mwx~HeqgiKsEwg811h?23)|LDUuZK@E7=G3!PWx)so~YHC z_=Ya?JZgM3leKvv{}0Wft01(Y6lEdVPAH!c~n7x^QB0Vr)G zFaV{o$bF5Vz;Pp$9?}?qQbHO7u#}L=WXNO`2+0gUDIu8wC?#Yv0L4Zo15j+HK7d!J z1XX&;X9NK-1Ax+i831_PSdiWas@4luYdw1-TJc~Q04p(U15&xcFaXL;3`20Dhj0OU z_LfI*CVjom4Hye>02UM&2w>P&0nCFlF{C_nDi##Ln2pm*UypPHlop$F0NZatM~lRm zn=)oE08n5i0$|eDbHyxLxa)ml0Pgl0v7n>nfKiru0vI57&P)JB7QvgHEO`o{qHLu5 z7=_}+OO91w6~SKZ6*M2pgUG7CajCzN9AMhe$qq&yG8g(A4J8ZX?n}}Dz;LVtUAPj;n0B#*tzkaTj5s0a?eD)=q=;w^QzTRL`0F7ip=d>W20W8S( z&xQ=1GTXoP(@ox3{}x2!LvdD zHNRxFecinQXhOSt6BJ1+a`)EP-5Wr@*221bgOo3+=)w&k8||#SHh{va>$zg<>xvB^ zCyhk*LwyeQeMMtobdAfRM&yiF4Ge0HD2NMkmW4;~?F&aka=@?v@%p>o*%%#&*a7tD zGkQ-Y;=+v*YCL7SEwily)An( z^03JR6sbYILNl(Y$5zzrD{9*nHN%RGc)^Nz!HW36ip_tZIcN|-3t%+hxbo!LUmmWP z0~k2zD+zYHv|KvO`B18_lyoc>)F*3(ZW@%(*b}?pZA=p}zoBPP!iT!rPZ4VIAqg~l zUBhITBJt*fY#NO{56`u+?{Snq>ww}G6WCG&h0##vSlur~7v7>XwiQfXb|iNmCXDbg zBtB3L6ohSeDU=szI4^fFZ$H+z<)re|Ce$RkMcretqEwGeMLz8SmTSugrj3}q0f(|@H`(5yl7}aNtE}S`^hy5UdYYrZpy&OF@J^J8@1H-eM^T4|6ZQF)3`wq*<9)n)QC2qozE@@ZS<2Dl@ z;$jb0+shl2yMdk>q}6vGR)v6Kt9PR}n(zx?lb(voj)q_D(fS~`13;{(q#%e2#2YR| zEmbMDEDUZCVkkse`m7uaOBorfz8O0l>p&!^S{Vh&fK8Cz*4P$hk}FT>Wj{|| z751-AiF7qrT3u2>5ZsF?RWt8i1Hw=l4B#%+5&}}WYdv=7S9?tCNsqp{+GG0l@PoVb zU@pD%fU`DxyB;?Qb^EOcQL>`t(S!4_+LEpmIOh4JjoXiXv&hM`AB;eZ%%^lTH_JF5 z4C^%qf+6Qq4*WbB;0gu{R}pFv)Sjq^l%Js0&KkSvMTGq-7gsUG;_WOmEuoU#)?6Rj zE#l787IC-2mmGY+VpVrOB*fKBZXgOds2(`ik;QmU zJP3%`jwJ;`NpSxPpsop$DJ1|Rkh;_!>;^V+4kAdgv>bylMAp%P#fBoeQ-q;C;JjV& zL*R3@hx9}dy8IN~3m~-QdP&MzSwbKQ?1kBk^0Rbyts|UV5|k6tCqV%BUwpLTCrP*x=5M7PUv;iE)z%lEKm`U&X z9&-N602A_q`CT!o8w+7LHgPAV+hEN@X-^<`XNIaU@ra)xLwqM*f@02a3)o)PH~Jz- zJ|4jcG!FTw7zs9L%#W%O3|&l4M8YXQfdKA^UxDT+oDk&`QW~JD5ao{%9vY-sc@n`t zydq~DkkVU#`=+bF>v|GkLmi^Je$1d~dPl1P_n$Oj+>cR!QVqKt7AYGS zxBwBTDf4T9`q4FIf>2pMcLM?c;JzGu|<1H2#t~&cl=0lzWRl^qpGVuhuyq7l86xk(4Bj zD_I?Y*473FKufcM!B^Xup0+W7QvxRiP6#XsDBdyjj06>&tqVYD=uLP*0j0qP27oj| zP(UfMjR7bnN#+Cvl!n9_f&xl|Gj30t8GvF-FIrFlxtX8<8zMmgrNov7ut@U(8J$3e z0g$EvvR(ii22};U{Vph=JSF`Q1h5eTC}+;a0OiI;2%vl<))y2|Y`xqrXs*>s_hVf& zsk`ioy6lR&+H32B!8wu`g&MgR=!3jo9K4Qg8im~`RM+=pP41iZPcZ3{q7 zuwej3SK>m*Ad#u#PBn`ZaB9a9ZpyIP8129bt-2vq1Wl8QPPxzA5^hF4$ix>isQNnMAb(IK= zGosD6TBmL~>NRQ31SuH4j=#@M;7wZK}yJ05~yD!;!EX*(c^nqJE!O7hm zH?1lp@Sw{N)Fw2C{6Pdj;}@XI4G^p7J_yqN>Ztsdd6*=dm-aZZvFI@{N1%M}2t77& z!AR4zQI{#Cbj^)$s^%g|kLwix>n1w3wwVSm%OVZeE^pQW=(EP9ZezO}Z{F0Msf2f3 z7R_$_5*L#iTrs}~@Ge)iEX54fIC3F*?EeC08mC^CO|is~B{A z1EmaFH!!T4f)t~LuB+hlT<)$xDKqv00A*H(DQM*vr6|a!i}N@ZtD0rPB{04$ItUG6 z*b*HA=WU|;&rLe~dLiQvgvl_>GCIG5M86{mJ^({MWcwg_VD$TH0D0>d)&TO+Z>)9r zO|=fcnbzS4(K`AOGyulD@mq5LhSEeOU@FLS@b$b|6rsnU$IOFzs}TOAY`JJL!6U`T;WQUQv{ z+gKf4+5;vXl`&gGZqL_PghT_00S)4ntdVdM?*h54exZ}CB90;&SF{R3y)x7m1v#cu zYURe^(e&{>vDL|XmoW8QjcQm<^&1^FQJw5^2OiML2thM*#mRkX2mLnyp`GLu1Va+7~JWb4a5_XbT86>9E`EphVU|Iiu52O?g#}kAyIj4y@jPyWlm(yv=kcy!6yN zxx<>bIFYNjxbv8|6xv?iTAazudbz`z(t&$-^nU)5Lm$LKJrPJ#Rz?m#%BCiV;s9WW z1ks$wG$Le{VbwrFZE1j3^Yu0xRbUFeD+2Qxz?hn9+oW}tXF zS*7T%rMn$duoBz>;K)yzDhIw65tPmaNWuA#bJD5v^Us?xt>jk(VJwuHp8yA}ZD<&x zxMFZu(k-kgE!pIT;ImBZG1mwh)uPPK2P83HIJsZUrtx3{YEDOxj6@@X1V&6sL4>XV z+ek<*pj<)jvxUIJH08-L+9zZJE$OEdA6_%0} zxZw)3fIBO8WJ53;UBCc<%pZ^d*p;p137;ddnPd+Q=yo4SZZK+Bq6OL>3PL+k~$iQ zx~Wwm_>=(TzX9N$o8Rcj(1S&@6iOA+6M}-IBrAJI`i0)tZyVQBil$##=!5y`)lFQv zPKTFU>dTx~T}u39hQ9B>SHw-0zua_N$8x_#u)dA3zKyWHjj+Cr zu)dA3zKyWHjj+Cru)dA3zV)!aNR`WFu7~xlhxM(8^+oa@#0~3P59?bG>&x4aLENyu z^{~E3yMw@Ced}R;>tTI0<4c{wAvmyx^^L>&vIhwQhxLua`o>{>9TZ!N5EEv#=XtZyx>Z!N5EEvzrEY?VvJJ6nM@tS@hE1-@Z@d2cK5 z4eQIBTY+y_U*6p+`<@Exdn&B&sj$AM!uoQ#9wZgkmv_7Z->|;C&p+GmQzL&A6Ucs@>W>j8`hWi!UEs0zPuS0 z_=ff6-LSwntS@hem3`5+4Xj~}(7z3Q!y570Sl}Dhh!@8K->^m}!Wx|jYjh&4(TT7| zygnAB9M)(l?7^k52baS7E`{}73hT?QNjW3lGYhO?eRrN3%MYvu)f^p1-@Z@x#0_Z!}@aT7x;$t<@PV|4eQHY zVBj0pm)p3qFV}K`HLMY`6emk z4WV>k4eDE44(eN54(eN54(eN54(eN54(eN54(eN54(eN54(eN54(eN54(eN54(eN5 z4(eN54(pqQ^+ifluCHVpvmJ6R3G17L_2uVSg1}*Yld!%?Sl=Y9ZxYrw3G17L^-aS1 zCSiS(u)fW(zLIp5D%cF`+YIa54C~9iTo5;`Z!@fKGpuhjtSI`9qa%Zt>3 zZ&+Vmr4D?<`tmY$;2YML*Qv|C;Tg6To?&a@8MYRlVQb+TwicdYYbV3;9G+ooym%cH zBdjm4UI)HmeR=sh@D1zB>(_yASYN&#E&HAb*Q4-+TjRshAaKZ^(ZC3N!}^{G`!_t{ z)_7Grh#S^7JmJ>D6K?JQYwt{e>^|-~KPJw>#39GBY)Cj>LKfSJXY$@$>X&4L|L>d^ z&E+{{jRIwjW=7KB(Tp-PvL&iu-)^X_aK;?V23RO(i`}JwfMYu(sZA|XEJe~<8Y_~# ziBc>GScu61j>!RAIrj7I*Z2MVb^p6#3AY$axW%D3zp;c{ zj3wM+Ea4Vo3AY$axW!n)EyfaVkvqFPa}-OsMegm6xN&~DyF22>^*b2XZ!F;!`F^_7 zZ!F;!V+pqyOSr{Y!Y#%UZZVc{i+rOUMHJ_k@3tduoL|1>j<|7tIWrn@P*qG;73-^UDd>h#TjZldcgr&M)U) zJGq=ajf`>sjpg5BEdLh!;{F@UzeUd0Mj^-f<)m%Ijq}Ut+lU+Im$SGLH_k67b0cn? z-&p=F7UKNI@^7&ak5{q$TjV5dXGF35TjU&9#EtXI$>4|^=a+NB5jV~+XMrPboL^1` zcXDI-w;0R6#aR9=a$!K^H?H4&oZndfEpl)n@*C%u!weBO&MyZXB5s^t4n0KNIKSN1 z-sTqb(I8_gk;c`OqkRoij0CYAl}Ht~V<-}Nb__->XGbAwW;^oH>UyXa)!@*MsN)O` zMLL>M(8_70glyNAr3NM zkI3BFNo4Mr7!O#Hb1W5pT#NOLB0=TsOa!EiJ#PeqOWGxiJ#Peq<#}WssBg;Cw@}@kqS=yr2eD1UHqi} zBQ>1(N&QEPIQWq!PUfWkBVC;MN&QFKIPsJEkCt)bC-om`OWG{!H+a`GAH#P>FUH!>Oa!fiJ#Peq^}b{ssBi0Cw@}@k^@*ROVTyB7Rc;(a9F^llqS|fAIPK zysaycIjR3hharAa|B?Dn{G|S)12Ezz^&cI95kIN_=n#zfN&QF1V8l=AKT^8DkMub* zC;3n0|5zC)_euT}`M;RR|FJ?){*(MC@_#Xr|BH$IUrglxVj}++6ZyZG$p6Je{*P6I zq%V>GW96XuN&XZ0znIAXv4T+kll&+0e=(8&QSHU;Gm-y`iTqzo5kGGKu}%(t zBLAC2{x^yIZxZ?6B=WyWbqH;Mdj68YaG^1n&sf0M}nCXxS5BLAC2{x^yIZxZ?6B=WyWbqH;Mdj68YaG^1n&sf0M}nCXxS5BLAC2{x^yIZxZ?6B=WyWbq zH;Mdj68YaG^1n&sf0M}nCXxS5BLAC2{x^yIZxZ?6B=WyWbqH;Mdj z68YaG^1n&sf0M}nCXxS5BLAC2{x^yIZxZ?6B=WyWbqH;Mdj68YaG z^1n&sf0M}nCXxS5BLAC2{x^yIZxZ?6B=WyWA0NXnV6dI1nd4_A)?#s#>&PEbJ0bzqORA!PgN3J{7rPZ~X? zDp#O1dbnmmf_YEY9Nx2MPu)U+2?2h$%j$N1)BK=k?OV)KjZ4Ga;>Cpzyz0iK2N<{@ z&)#_9Nc*bel(%5N&N8_oYq!tv<0_u!@UG@CH&cxP6&Dwo=>}$pPl6!1Mb*MhUJUDlYH(8* z2=NJUKR+;~R^d69;mO%uV~rET6Ux{G@emzX%kc`?6nP(?xyTdX*pBm}u%xG005WPw;vzk3){q44Y((<29BZXuH6-a8H-KCbquv_;a4(ji z01RFl;K6xWDIHw*Z%J{vH4C8B*t`A(Me?(DN}=w=2k=uWb?GWVRyLLPes$5Z;3uq9 z={9VD+>t8X_Y1(v-uNph5-t~o4eKgjfVehkK#}6wq}ls?MazPplBPR$0sNFI-9-zK zl}(ks*j2PF_^H%%4=aG5(x;n70kR@}JDbCeJ&eG5n02!85O>OPJN7VFr}CI4msL$w zD?K0PN*?@iBhRqAp5Bq}+8;-|YkwT=E<<#*y9`eB8319}w!*F#NdPv7^^EN>w~h{T zjo`3u3BUvkG@iabugz zkmF;gMCkI<;S$e~^i-#^sS_ek=(&4c1ItaixXxxKru|zl!@A)Qn`v23XCE8p;;D-O z-G_(GB=>WYS&KJ8yrH0e$KB^#IV+0_ccYQdZ1N;muW|LW6I)w%%IM%+;0+DScQ#{CrcE018V&2SN;$DnHc8~}oA z+y&|N8Cm7(+~%;>S%4h^XeR^(1_cHL`UUy~7=!G!uXPq%bzQ>_;Md?+dz8n8su0k4bY`TFFe^-24-28dTNbTutS z$7||!dQY|qQ5DE|o0clFRCUr_i`-#wP6!v5m$_eUSCvOrPg3? zsdHs>`3!A|FbIl1N*o#BmFL%FE3 z&TAcVIa)mjfZ*C_-kDHXx+Sd6TOfb|eh=2U-n6dag@;x!S6EZoF~f{{ntDqXX*?b0<&+cNc_2JcuA6;08S z)Se#H05F;hS{{SCXn?h~mex*%WlPJxssYJhE(l;$2FSa$V{qp+F&P=$0VIs)FX~*3 zO0t2|Y8e)93Q@Lt&?1{v!Rf7QY_Ap>saJWXN~vI^uUAWt@FO`H1;A~Db={j?FY~Z_y-d_4byhu4!5bjD zLs>6q)l1E~#?TpV%vCS*<0%-Lmu_@NI2mTZ*YF#t(-6eRU8jmbSDnr%Rhh2Y1KR8U zz_c-LC#{~WXBW7PlBpV4=Uz)tq_h$qJY_PUTK%c_`OGRJf_py4$ zZM5NK;Ce;@U`sQ}h)9i->qw~?ZXvB__t*2N7y%?Dk1`QZ5Xb}2$msz2%cJNQ6rZ^S zU{kviLSY*{$Sk;&N$H|>N zNp7{L*`Kx~*G%?Q&s%bY7giuH@?%n`$JZF=TnAw(#ci2_R|JTaOEoVEaPO~r5F|H- z0?Vu(ECNz0Jm|nehn6f#Ph$$pz>nwE=npk+=&teff`T96nFtqJvxcqgSm6?z-jJZ2 z?Nbs!u6bv$fuLv{TSd?75pu(b56}~dHI}e5B!~CKCwnS-ZCM6V%%F2pgsUK}YB62r zSMM1VS?Bo}i~1evbBM+QPvA5^aI@Cf%_^7to`=H3gY>o(d9<&lrx^kKrnuyqNzfAy z3s+GL2@9O5H72Jn17tc}9bGup2y*<7kr1;5f!ZABvZkrpT-i);wGRIL8lv zwA}_1MwPd3JVElE4H*!DBU?ONAxa4YuL$s5o<$bgCW9qw60WFa7OxGn$Ujk2o@>Ow z+gpuYN(e8C6NcshW!K^pwd(kp8p~&XtR-r8Y^P~)@jQ}*MHp1q_U`c-E9hEv_)&gf zJZkj~B+e?CQ#Y)ZCRtP|9y?@vd=cX$4akq}H72o|=Y#CE)-pqET5P!|AR#8F&eoXf zYP$axP@Uw_@JkRFc(sd|Pz|YzQ2u(e1k6lW%XF_di++W`i`f@}Tq3MeXVKI%B-^T5 zkPt0Mh)NKm3WTTtmc6WjLFC$w=D=o6BRK$PjO1ktOBPxl-ZZw?G`0hb0Ark>0Ark> z0OP!%0140t4^XI#@BoFu2oGQfSWv)*#5fPBFc{$h^34bjkZ--kBPgIS80P`<%{UK` zKSp?f{4v4<^Ab$mBB|!oCW`qaGA0s?K{utE(^2c}% zkUz$AfPCo19KmLF&zUMO(SWO))jiGXY^gd+_h>*_!K%8O7dm@J#y{Z*q}DS~JgF&N zv!!_Cqq=*72N=dKS9t`Yy897C%BkroC=d{_j80&km-SFaLT^@gGZxA@Drw#)!3(nq zg0XHaZZaZ`_wZJjM)WHf9;wcZR4-O%dS+mu7zUsj7Id`8tXZArDM9)fH|1B)@%R@n z$_X-F%r>i2JdHJjBD5+|k#!7Fd+H=hGfrS>;YP_!4d2g?qOVSIH$F+??G+$TUrzRv zcu9+VPflWPu#VwP0DjrjDV{A=e^+5ZRhokI9bM=Qsh7Mgx@2LqIx*GYaUG90x!ay8 zYGSzY5L6|jLuJ@eB*+f3<0%%#k7jjzWOz4lxLM@^zA6`9qpu)k?s;p?Xn2{Nr7t;5 zUSt*==6y%*xffZToM3j4H#>Kow%D33R>w%S^)P;^%H`2W>s*rTZgJ*TXFWWqM6-P` zhBv)`DwyqJzJWSPRcU5q<%_AzOZOTX9A!O}F=Ugw8-(p7Z(OC>;ck7BX-yTY52%h{f+N%% zdWc;REiRhXoy_&THS0)>dR&p`Dy&)8I#=ITEnh~B(X47tAVoq~EM!7(_>x1;q?y5* znHs9d5>*7HPgU!)#KA*J+M(fr9m=at%L$#W%DwrRct6hdK^~r!%zKVE=?IL&d)4w8 zxQ@v-Ms;$N2kI_McQQP3#iFL*8Gy{Atk2m1V%n>sXdn$-m9E>;t69n6`>Pt;06PR| z>I`V4K*JiKUw~Gm0S%A`FrWb#6a@w3kzoxmC?J0fYXJFEH?j*?K}d!*fc)uMF+l-_ zWkCUj!4L;f7z}Ozg~8y)x6Eud2@2R0Fwj9N3J>JM6U2)U`6-W12O=eNKj_|WYw&Es)EX-GW$qHFWdvpRCvLrqM%slR=9G0m)b^U zSCeN98;m5pDD1{Jh(1h=i##fLsUkTPz;A3opkLqte&A7lBmB-)c=4vfTiF#LJ2wD+ zAFVuEp#xMVuT;1V9z1it!rSab1=M>!6_$J_E0Yox1y2FaRVK3;n&&03lzN~VtIyNm zG8$9)wS>nf2x9DZ6f-b9bxA}|c(+LgSc+kMrZjCK>nRy{?NSRlPih$O);MDQSm~Jv zghH@3q9UAh+p=qg{$vx55sD#||-@+2&RVAu*>pi;(O>D-x){^Ef28 z&}x5Bf!7M4oy%$DtStt@Z~Mc&)J7MObLHKd9iYm7sR<6j`X$LpIo$p3Ph{m+U>8< zYJX7eFgpsb-TvgF6;wNY9R=8Kf4J>PqRK5apN{rt9koMjQGxI{jC_UG&<7QGtu(hs zzd~#Hg9`3i>1~gIh1L)V)efg4t?f}zE?PkaqE(*lk+9Gj3ZdFzc9ds(G?a^0Q0??} zlxKTH#BE0sJIhmyR%t-DLu^rjXf@3C$XI9%jZp3Mb~MiR=qMMhpxWu}XrS#861N@c zRl5xJFGef9?N*9)E9`c;b~M!XNJ-coMXUDN4!hme+oPr2cOl zvBGOwyWj~4^25Q~nw7>bl((lqyf>w_wIeo#?UszQsz?~XO3#-%e9|Fjl7GE~F1;T@ z-t;3CdpN+VMnt0e;3Z+ifyNYyXCDzNG|dSUOAHH1x)Zt5xTh!Ltm!>=}OLm zU@k+YLHr2YO0?p2q=J=CO4#W*JU(NMqod>G0^JAOs}`RJYNu3bv;_Xy1w#1&#z}2@ zoLxNpNKBVN(?HwG+vn5d*;7^=!=Oj5IK-fcok5i+QY7frT`iy+olz^O)EOO0kBrq&RMF->L3Szc(tvHXfN#$h)p}1hz=ciECX(Y zGBtd8LGxFs&Z0sXf7IhkJl)0aT&23JK^dVoL9Jxn5w5LK+}PNeoqmpl-P$f+kWO8@Jn;>ijMGBBVAbL5dm?Q`#cQ(&&7Y z$dTz20-~fb1F1`)?@&zk{brjUM-du@VmZ7vrr`odJNYR`Do|c#t732W6l}My>QOO5 zWb1<{ZBT5BSE{^bX0uK0h~`*{2*v{Il&ZwQmL*6!E9I!1&7z~PRC#j^C7EgjHB7T& zH3*fN-B4_n0oWuH6rd?Ol>yMvvfw8*0wbn?>@}oS*iZq;j!h8&Th@Y(7RgTy$JV-D zXRAnK2O#C_8dp4nlfOKW>$JWQa;8pFLHiFN>lJcU$!Jv~hphoGAFX6pD&cT~2(rdo z%asg8BNPhNlY%18@T*!|PDoTZYvWYBfjSNGFh9;40QoXbuwRhhM9%~-j9c6|E5unc z;+^Oz&CpSWt5{!P!&~oNf@n0ncgaur$~c|`fXxu1XFNTDPdldAaR=43<{;mDgskB* zHZ}~`o7Abui1xxNI>!UBaSd~dhSM=9T`3$#L)B>z8D|INP6q-U!asxU6z}pWZT0L^ z0wXrxq}Vq2tO})~r1Y{WC5sLW$`9=z5Fq&j)-0qFp>Yi?TgV^=P3S-Y7ASeXJpxdy zdA>3NkkUNg69Mu7zU%=Q0r}8^DbLqB02G3P0`fP=HxYsY@;#WRfdQ4Q!Mu_+$SHh5 zC2Noq_kv2+AgAjEt$)SIE)4)xo}hqY)-H{pfZ}I=2B4VPp8+UN_Gc>Ep8?39#0Y6G z0rDXgrl5fQp~3{P;~^*@-zYKx^4(|4NlRxbG64z$MJ7PLQDg$-4;5xbDolXPmeksXVkvC~4;lNMU5Q;?cQWdP#`^|F90_>qp83uP%e0WuKx%=z+6iGdG@ z_YAc=JDHdq^+ZWH0*uUzssuSd zAxP^RXK^VTjnU&!N$~$jwh>(zh3#J++ku?pc7Oe#reqiLO@$yKu#^hrWLkK2; zkMYY$QAShe2x_yeu0dcbH3%}=m@LnX>HtWa{G_$Cb+jo`2VETo(K0otXq92#6z%DF zz1ms0gUXK96Qh(A=plfjW9b3l*DMcfoEz3aH*7~bphz4HYk1Mn6@s5*cQX@P>K zmWLnVL#k<>o*uQiM#Q0HI&GBGa@(qtAG7df-(Ur6`%y^6GpxmgATKiui5?kwRFI|t z6xewdAm2UVjn`n7wF*nzGayLZvf3|mzFS}yJK-S3#JU!s;;YKazPT%GUzUeZzN>0j zz=zAjB!@L2z@I~f;t(FYU?K#VnF78nBL_e1&=*MSMnF-rK2!KwrU?p+@-DfM-4J(# z(yuj^;FwA}Q;!?YPV$3LmLyAANJ2TGrSvFIRR$Vc44oB~So#X-u(j3IHb2RMFJ^)t zL!!~>0WpcNF?PyPCmpg5CS?aTGSyI5tu|Qn0@a7XF(4c&jd52x_ z;c_(_p*d-;)F7iyW1F~bjJAGVMFGW8p#T92A3>;GwpQ5as4c*r=_sorbzr`nNxt6% zjj)?22m1bx<8-!ORjuqXvu#}FOc6SX4429TXCIJiPl0tdVe$%u^{loY-4 z(7gcZ zF9U_$@(S&&&cT2h9b*VWI#>QiX7Qxq+?H;(lP%nG#<>uSnSraSu&jb3+dy>$seGcW zH6fs1pih8dR%^nt)`S2futQ)-U{F9lSQ7%|gEb+5^{*g+l_5a>SQ7%|k2N7c{#X+N z_(EBbX#^;rtP%l))bTERxrfDYxrZc__VA{2Y0vQ4(k$XTt3<@Gv7Q;S$b_72madd` z*PA7lge9HZ1n|q6rI{IC&mIO}F5zWXUw@QlCLiV32XPT0Yd4r-5x{SB;!0^|WQLG{ zrvW2VI;bb&k^mKN5us7crJ0?xfZ@i;(hTz#LtAO4%!d}u5=)Iq;7($g+L^ttG*y|o z#1S^71b7B*-WZ$lpqz^HoK5!hkIS+=vR6fXp8NY0a9txSohNJy?KkG*Us4USc(knp9FCS5;Tb+T>*ggf-ES)VeFPEgk0`MaU<{;wLECb-j32#7?(`T~`10{y`5|zsF1(?-o z+Ul6cn;7v~MF#{p!4G`7gl>uws!s0A#%SfN+bJ3*f)aAiLmYYMh#Oo=6&nCY?1Oo7583d4()L}rNbG8Dan2|qjWVzAPgGOWTq6*V+6;?{Hh!ir;~yF z&XrhT&}bl8R8tO|8x){3jE~x{vM^*C1yRum^aDMej>P72|(xtu0#Uhw!q~_ z0NfV1=BQNIAt&r<0SLXY<9GSV?T!cd$tjlu0pyfxdH`~Y`U^lp3aGyT@-T#lq5JvC zJ<6|AVerHJK>?)~KuJ*@6b4DGszBT2fDiDKH3eRvzzh9YP}Bt#b%CNT zP}GIK5ApjTKbU9tB*;7tafP3}8eVQ9DHK5H%!fNYtRHK~aQ~e~=%c=$ja5sdzA>66lq&5WLatN10xTM(fgSZ^Tr5bL25T}C#IY^L$IHbh#gE&;1%@5!b z4Ih9&DZTsvE@`!ST5X;}%u|T@euC`BVLw6k<4z4YPpRhnaYUi!Db#!)jwskX1)J}~ z5rv!YBQOeBOEUltDPb+l090(gfYSm_3pg#{w1CqBK^AaXz@?gQK2Nx6zWF?Xs|n}x zcvCaZ=kc58(72!g!KhK^sSPdE00gMUou_WJJOjKxqNo(cQi;$YUL3{w0A>Z&BU^e{-0uBeKi33cQ?dPd%NQ`q3SB_Z= z5(wYH>o_xWbT)aOi7cDJ{Z94;@xD_j2lAmEm^CaF9|*$JUL_kJ?u$_o?GHvVHQ=#} z24o9|1+U#A$tIEXr0(*>ro#_`&hWz;l9ob^O>$d`k_qD2yu%7pW{%B!!ed2a3nwE> zEbz)bTKXvv7JEd}L)#n;M72uoa;%+wIWjOYvni==1d6iRZ4cMeB-#eYV0XTH$=X86 z3jLMS2Okgu_z(@mH=U%R@hQHoR}j@1yi_MB2#)vDRE2AR zY*s}mL<;zx0=j2}PhJ%-9kM!wQpWbkoqV!AO5@(i!jdoeRX)HInLf3aW2-5BIO7!l zCd)*W=&VfEpW_%Q12aeazV#)XAzNvef zY}E;|$pP)^#jG}!Ww$|7HFPnA3j56k?M>BK<2&fFvzh-^0Nms|SbkHI&?amTgq8(A zs*Fz?dM2!j2@>N@etZ?Y3503|LyoGIwo4-766EYuR@p^cfyvgm!0D{QwL{u_fj~c% zRhTW<1ZWA~9pBOSSh^{A3N;{~y)X>WZ^%1l^=EcR9%4Z}Hh%j^m8CRJ(L$3A*SAo1 zsqvw~;;T<4O4L~Duh#4kOwu%ZB`eL+g3o0oN#i!TB^p0EkIk$kO#p+p`b;UapG;(> z`!g@bfSVaw@hl_Y>}~v5mpez9HViXh!NR;CD`r7s;SLLjEF83Oz`}kDtzQ<=a&P^z z2!Uo5B53`xlooCMvX~Za{j!`EZT+&KhGtnJX#MJ&T^ZP}w8%mm5=#=<*pOI^h_)fI z49W5=hn{8 zeMLfnvENIUxw}))(u!6ehV{&x@0l1z+Co)6J9AQy`KgBwK~DBCe_j+AX?At|O&?rZ z&mCG_II_H$JG{1f*V4k`dTwrhesO(0x3QYrIJlUbSy`H2U0BSO56-R4&2KEOH(vk$uZc^Tc^9 zH+R?E((>Fs-0ojnJ(SzOarjMdd?T>;h|5#&>e_)f_I7tGA-)iftdnSEdwuTEVr~UM zd8{nt){h<9x4H~XDs6rja{G?SVYe?Ir7c%ex@xOyhvqhNE?GCNFD`Z;+^{jSJyVy-?*_hFknH?K<~kgL(4b!wrZ%ee#qP0^4)XCKp|hfxyHs*)f@32Tm+~b zy7wPhnctwGTxHC!9y+|dcr>?igNx-e2$)Q_%@G!{4*|pKj--~ z-hS5OAMp71zRTrf;HPpnyf6Q;Ge7tf&OGPcEqHv;}Yaai1kAK7C_j!Du#}9b?yB>eoq>JyG%_WZ83nBV33_xk>E@9wz$HNW8e|CZ-BXJh{H#~uI2eE9i~$KmHZ|A#&Q z@P(Lv>tFitJ^zMJy7t}5-(NWXFL?grN%&o!|JWB?xc6pp`fmNa^Z)f1x$?VjIgY>i zImh4c^Y^le*#FhfI)12!t?*4Rcm3({?}+_3|DW^U}GzBJ}{dA{-d z3$Kg$$N$EK|5nex^}3kf^!$eBUrqeq>iIW&{^Q>o`_FlPuje0rVa&h!*Dn4YpLONh z^{SYEujl`g_y37Ef3q#ZP`|f&{#XBpE599YiTPK*;=+GmhpWGPzby`bujjunr0=_9 ze$Ml+_w&o+N&b(2*@b_D=TCfZ;@|WCrRQHr_{YEG{15o}UzUsg-{bj3&+qDv`FlP8 zxaU9q?J+;+`QPXH_uLTkuYS?R|2EG*p7_7l^S5fZ!9LVSFbDkdkGgufDOh-%>%Aed zd3KG*U+|Kr4Dhqpz2GVBuhsJEw;Sv9__sNHt^WR@-5ou;eq`UqvBQg6dCslm7LU$p zc@jziwURpVj18F%O-0nNFw7jvz(iV@b%Ct5u_jYeAu5aAI`g?w@_wX^+#BLe7 z!Rm8vZ6PNv0W57S=GGS9ab#%?pGzBZwz{^!qMYE}(r#n1x4W@FcWm`YZeexh#*N(k z+}dJpZr|#W4I83Q;`%LiUF)1It!yl^$R&)mBQD;FV;cuqTeoAq#fCUf9@iEXkyg6S zQSR;~+!Fezl_S>Q*eaCX`E8b$T{nEJiU zZ$5qgZ{8yR+ogZM&*xyt)n7>WcK!l?EBDJj+GQRX_hWwC}APJSVByI?!iaKd*K5lk<4%n4X$*{O_rL{_uIPi4yR{U;6c( zw_SeJjdy=~Hg*?0ee~^a-4M*FyZ8Su=kG^64(_(X{dpH&bIIXvTp!0fa~Pe@BJ&Q@ zp8*9v%VcwWr+&Hr4W~7Cs1DXQwnp-}? zbjED!o#s~e=MFNdEiBG29hzIt;cjUo=ryj<|GuS_xwT_f{}f_&IrjeNm|K~XiAk!V zj*P^vpIk~U|0|1k=eWM4r{4^8`hRn7^{{j^R$aBUvb3=@x4iU2D86jLge-0y%ag(% zEc88_;s0~}+4eV=3-#C6_uSSWM`E7K`qN_cZ_@>@vH$%2_rG>Vw(cBs$FzUgvhDDj z%VjK}eHRP^%G}z4BZn4OHo6bZ9n0-&DaIGxoLkzDwsmnG-YsrSbE6f#6%{uw=ZSr~CFp?KKRtAq?dp1peeU^sI}5ei$o4ms7${OZca z91_sV>PinMBN~nN9>n@B-AgO;%SRSe*(*m>Jp>V^h1NjrBG27KmEYXm-5Tu>;9Awu zo}+yHxb4kNE*@TF0$V`3TGqC}+G4k)GG-?lhqA`lP1FcUa?lQgB+uh-1>HP*8x)MV z+C~VCE>0rD&P{KJ^#fbC5sggk)UqQ$m}>{@PDs0{k|9^RHZ{J;advYzC47jvbBlIF z?@-w6RJgEQ`*0&jj=d6Fi-b9UWZBL0i@DLtAl0xqf9LvL9Bt zY`cuwB(1-xySvo}dU|q>b`i}VSzA+i`UdGH#g*LL!U8j;X3fy(+U>B%w`4n0C;si) zp*!jmPv?Bw_TL@4Z!45|p%9{PS5K?qc_P~1k>)_-j?24b^0obQ^Q@j$a~xA(&7`7b zLu!5QK1>9tpM5}~$ z=MoAFnpC0B^blWnXmSMEduQ)lZ*K2iwj~cNt{rAQxzRCpKB)&Pc%M>oWLq>5Q;rNn z9Z7FBF_(1OZ!K)B(SkcWYDa5`JLfHQfOLtcuButZ?!7K_Q`D^v-;?9h^+^s?bFuV_7wouPFI#*XnxO zEL-L~A9wP^Pro77&$JwPBIxyax#Z)&&O5y7aWDt}yRXq*aDS~h>@Qxo z%jGNVFCM?q$%{vxaOHEO&v)QAJ^wDBt_$yX@_XRl>iM_zIsN(9rd{~0e0lzM=?wMr zo@=b1UwDq|r|A>i%7-_n^|O^b@BErksGqkc_0zmLuAh%2^>eG|hx+NdC$66^&kyx8 zHWSy+@tb0O&Q=Ky?f39D^%LsxN4Ieo`q_`a<>~ubDDUm!-74MJY9Gq~QC~i-_Wg#q z{D0Y}H}JbWKa_v&-EsLJf4%b`%6}v2A5G8SE}fzL|I0Sz8p{8Nws9BA{|#@ww({RD z-tE%A9Y2&$eeZ5B2-oiT|AEZvGZvXjtKA^q6|NSS=Z+6A}u50X{={VI&rw{kBVBzt7_q%dRteZ1 z-tX%BR*#SOIrDR#@MjpoR`v7lf3BZrShCNK_y6YW;n%<6_LoBc%=z*81D^lxug3i2 zetz996mA-s^Fay?=DZt?K7}|6D)+DC+8fuZQP- zKJI@_zy8`Tkz3X8M|`+PlK>v~>$t@FIX6Dj`SI#dG9RYq=R600w+KT0PX1q4zoEWv z?M?EtMexCW%0A`m_h7>K`sZ9ciS=`C4E1~b8uOJVXs?OOqq!qCr|$ou%#S_a=>s-> z`KRd!{9wQ9bFZ=FC%19GRd|2Ag})H~OTU)0!NES@XN$il*(VR-r}D@8;ky@lQT`p7-?&5cn^5epA6Z6OYENYy zSln1Y{Cd=(Yn*S=M-KL5fwsQWZxFc;CQuzNqKV}!mQ@@Vdp3^fDA?-$?i|Y5!*}p8 z!7U(S5n5i~m_K+x)S;yni{_3Bp^UXgcBJPwWU+YkFo(TF?O&clVUOnSuBADv6!pry zxefNr*~8JN3g`#;5ALH29T_}*1kf+q}+YGV=;kJjCmpFm8y8jMz&})3W(cXs= zBWzZ*K0e5;ADmm@V4Jpc?&c6$`|}O&U+PZwB7!#9o>S}Xo{Q$qQ{L3flmBpID?yH2Er{~f zAkx)C$2Ql~rutY*hkan_uEiDU+1G3(JsU;QxqqCEPW) zwzPU=UEk~+4PV))-`WM!RuLby&;n_Sz6-EDz`+S=p*wooEo`3H-Mg?guzz)V z*`1bTd*YDqfYQxBxqaEKj=gVj#ol}HtvBBMrf%wmHgIj#u1eg#ihJ#MK|Wl2SQ47v zd)VgF?-*zwSed4ODEHd5Vb6imV&`n}==`GHT2=|O+oJ6VuY>vv*h_4sou zE2|sYLf*f6WM!eZTiYxL=hSysWxKWt*OB&nCP?k(*u59`9>1{L%{#C5__ZFt#pANaKj3lKw>kHV-kkU56>r|_%}2et8T^HT*yG23GH0n< z{?yGIkNNLbe$2c7)E_$gzx;LQ&UZ{4|jgZIDpwphP*Z|&OF7a@HEKAfNP^JmE5cKiqZdSUN~ zn^%K=@6A5EDUYvTjMMiXUq2z9t?;Y-d9KZ$iO-D%_t$wM_ujr>W${F-;>FSYbHS+_67%}a384VFKLySm)? zbY0hrto`+bdcAf30^BNWxz(RSSB%%o%>Qi@vTgqbecdZ3o_@S?p>=hA>ovN2*)_Vm zVH~Cbsb(%3*jLcWDeE{Jq=w z3*qnH#@$xoZ*1c)q~q{4x?8?RcL%T0-NH8R(tHJTXfMre{D=G<-Ns!AH^ndZ>mc84 zTkPvXz}_az?=1%jkvQ*ivwqvrHzf|9mzZDCc512X6Mk411!r#8EttBxo}ai6--`zq z-x3IqL-_{l7Y4%P7bWH=8GU?zU3a{zieZkv@a{E%^E|<6q<@Yxo?iBB>x4C#jc%j_B-r-JleXnm%SA6=givGe^ zDSKyJc;Wk`+_W>N?J4q%;dw)Def!XBUTaw|@#b&!_|+bNkH@d~xZv@S$8YxdR*$P5 z@AUXKk9T?ec8@>r@v_g~oHy?e_8z~(;~)0!e$)4tW8VJXdHfR||E$Nq=<$0!{tb^G z^!S9wr#-&t@$Y;5X^)@q_^Td&zfZ?ekDu@HD?I*Ak8kvNhsQOKcX_49_gk&g?|P%F*I)Mj|7#M?J^p+DR}8z6BJGdA?cIOoYp$oK z*`%Uh?amWO}cue7g#&o)r*k7*n42*NUuHtUFca_ z@K#(m*0V8ppv&@fZoI2^;n)h7jySxr<~aV!lgMxfytT#UIeGBZ;pGiQxdeplJ+KO~ zv3L~7k;o++t?H(guHMCickEw7adyYS1p)~s=k<=cwY53&ARmF2FU3UwHeGXvmgXUB zSo_x3ZL;`+l`>dfTDkKnE97r!saB&lr7>84zwU{Ezr$I#VA}pP9%@br{!e-$;McW$ zIwEb~-<$Y<%=-^Gw~hY+_+S3UH&#RZFY+BM;4gXsEZnb>&UbIczsex^?{eLxE8zQn z#U()Vfbt&vztUNE)xe4}_`mP0V+8!gL*AiBY59#||Ba6H|Jn}cBH-7(#CZ(q5Ag*2 zcGzF<@PDS_{0BVYBUJkJ3+Z>Ec6I$Agz6-O_sW{{5OCL;E6@;s@E`KO;r*9#Fk~ z@ME{%5Tdd;7n!=c-iuk~7yLiw$LD~5|3d5`jsJHN|7Tv*V?6|X-|=|H3*{C<{V;^q ztijcVt9i@1aKYi!eJ}@n3fC{~@c&8g|C86~|Ks>o{zLp%z5lD;|9we?<#5~i_Xil% zc7y+WzTnb*&mTE_t1sBlzC*f0uU&cA`*h;JtE+$we~&x= zXJ+FSf6g-7;Pa45Q%L{SS+}k|({MQd=Fl>npPWN2_ym;FpZ*2^8lOA=()QDt_fB_T N_3;O}BJk3<{y(7Av$FsI literal 0 HcmV?d00001 diff --git a/venv2/bin/wheel b/venv2/bin/wheel new file mode 100755 index 0000000..34ec289 --- /dev/null +++ b/venv2/bin/wheel @@ -0,0 +1,10 @@ +#!/home/sean/Downloads/BMS-View/venv2/bin/python2.7 +# -*- coding: utf-8 -*- +import re +import sys + +from wheel.cli import main + +if __name__ == '__main__': + sys.argv[0] = re.sub(r'(-script\.pyw?|\.exe)?$', '', sys.argv[0]) + sys.exit(main()) diff --git a/venv2/lib/python2.7/LICENSE.txt b/venv2/lib/python2.7/LICENSE.txt new file mode 120000 index 0000000..fad3dca --- /dev/null +++ b/venv2/lib/python2.7/LICENSE.txt @@ -0,0 +1 @@ +/usr/lib/python2.7/LICENSE.txt \ No newline at end of file diff --git a/venv2/lib/python2.7/UserDict.py b/venv2/lib/python2.7/UserDict.py new file mode 120000 index 0000000..1dcde33 --- /dev/null +++ b/venv2/lib/python2.7/UserDict.py @@ -0,0 +1 @@ +/usr/lib/python2.7/UserDict.py \ No newline at end of file diff --git a/venv2/lib/python2.7/_abcoll.py b/venv2/lib/python2.7/_abcoll.py new file mode 120000 index 0000000..e39c38d --- /dev/null +++ b/venv2/lib/python2.7/_abcoll.py @@ -0,0 +1 @@ +/usr/lib/python2.7/_abcoll.py \ No newline at end of file diff --git a/venv2/lib/python2.7/_weakrefset.py b/venv2/lib/python2.7/_weakrefset.py new file mode 120000 index 0000000..a3c1cd4 --- /dev/null +++ b/venv2/lib/python2.7/_weakrefset.py @@ -0,0 +1 @@ +/usr/lib/python2.7/_weakrefset.py \ No newline at end of file diff --git a/venv2/lib/python2.7/abc.py b/venv2/lib/python2.7/abc.py new file mode 120000 index 0000000..cb3e5d1 --- /dev/null +++ b/venv2/lib/python2.7/abc.py @@ -0,0 +1 @@ +/usr/lib/python2.7/abc.py \ No newline at end of file diff --git a/venv2/lib/python2.7/codecs.py b/venv2/lib/python2.7/codecs.py new file mode 120000 index 0000000..50169dc --- /dev/null +++ b/venv2/lib/python2.7/codecs.py @@ -0,0 +1 @@ +/usr/lib/python2.7/codecs.py \ No newline at end of file diff --git a/venv2/lib/python2.7/copy_reg.py b/venv2/lib/python2.7/copy_reg.py new file mode 120000 index 0000000..5dc0af3 --- /dev/null +++ b/venv2/lib/python2.7/copy_reg.py @@ -0,0 +1 @@ +/usr/lib/python2.7/copy_reg.py \ No newline at end of file diff --git a/venv2/lib/python2.7/distutils/__init__.py b/venv2/lib/python2.7/distutils/__init__.py new file mode 100644 index 0000000..b9b0f24 --- /dev/null +++ b/venv2/lib/python2.7/distutils/__init__.py @@ -0,0 +1,134 @@ +import os +import sys +import warnings + +# opcode is not a virtualenv module, so we can use it to find the stdlib +# Important! To work on pypy, this must be a module that resides in the +# lib-python/modified-x.y.z directory +import opcode + +dirname = os.path.dirname + +distutils_path = os.path.join(os.path.dirname(opcode.__file__), "distutils") +if os.path.normpath(distutils_path) == os.path.dirname(os.path.normpath(__file__)): + warnings.warn("The virtualenv distutils package at %s appears to be in the same location as the system distutils?") +else: + __path__.insert(0, distutils_path) # noqa: F821 + if sys.version_info < (3, 4): + import imp + + real_distutils = imp.load_module("_virtualenv_distutils", None, distutils_path, ("", "", imp.PKG_DIRECTORY)) + else: + import importlib.machinery + + distutils_path = os.path.join(distutils_path, "__init__.py") + loader = importlib.machinery.SourceFileLoader("_virtualenv_distutils", distutils_path) + if sys.version_info < (3, 5): + import types + + real_distutils = types.ModuleType(loader.name) + else: + import importlib.util + + spec = importlib.util.spec_from_loader(loader.name, loader) + real_distutils = importlib.util.module_from_spec(spec) + loader.exec_module(real_distutils) + + # Copy the relevant attributes + try: + __revision__ = real_distutils.__revision__ + except AttributeError: + pass + __version__ = real_distutils.__version__ + +from distutils import dist, sysconfig # isort:skip + +try: + basestring +except NameError: + basestring = str + +# patch build_ext (distutils doesn't know how to get the libs directory +# path on windows - it hardcodes the paths around the patched sys.prefix) + +if sys.platform == "win32": + from distutils.command.build_ext import build_ext as old_build_ext + + class build_ext(old_build_ext): + def finalize_options(self): + if self.library_dirs is None: + self.library_dirs = [] + elif isinstance(self.library_dirs, basestring): + self.library_dirs = self.library_dirs.split(os.pathsep) + + self.library_dirs.insert(0, os.path.join(sys.real_prefix, "Libs")) + old_build_ext.finalize_options(self) + + from distutils.command import build_ext as build_ext_module + + build_ext_module.build_ext = build_ext + +# distutils.dist patches: + +old_find_config_files = dist.Distribution.find_config_files + + +def find_config_files(self): + found = old_find_config_files(self) + if os.name == "posix": + user_filename = ".pydistutils.cfg" + else: + user_filename = "pydistutils.cfg" + user_filename = os.path.join(sys.prefix, user_filename) + if os.path.isfile(user_filename): + for item in list(found): + if item.endswith("pydistutils.cfg"): + found.remove(item) + found.append(user_filename) + return found + + +dist.Distribution.find_config_files = find_config_files + +# distutils.sysconfig patches: + +old_get_python_inc = sysconfig.get_python_inc + + +def sysconfig_get_python_inc(plat_specific=0, prefix=None): + if prefix is None: + prefix = sys.real_prefix + return old_get_python_inc(plat_specific, prefix) + + +sysconfig_get_python_inc.__doc__ = old_get_python_inc.__doc__ +sysconfig.get_python_inc = sysconfig_get_python_inc + +old_get_python_lib = sysconfig.get_python_lib + + +def sysconfig_get_python_lib(plat_specific=0, standard_lib=0, prefix=None): + if standard_lib and prefix is None: + prefix = sys.real_prefix + return old_get_python_lib(plat_specific, standard_lib, prefix) + + +sysconfig_get_python_lib.__doc__ = old_get_python_lib.__doc__ +sysconfig.get_python_lib = sysconfig_get_python_lib + +old_get_config_vars = sysconfig.get_config_vars + + +def sysconfig_get_config_vars(*args): + real_vars = old_get_config_vars(*args) + if sys.platform == "win32": + lib_dir = os.path.join(sys.real_prefix, "libs") + if isinstance(real_vars, dict) and "LIBDIR" not in real_vars: + real_vars["LIBDIR"] = lib_dir # asked for all + elif isinstance(real_vars, list) and "LIBDIR" in args: + real_vars = real_vars + [lib_dir] # asked for list + return real_vars + + +sysconfig_get_config_vars.__doc__ = old_get_config_vars.__doc__ +sysconfig.get_config_vars = sysconfig_get_config_vars diff --git a/venv2/lib/python2.7/distutils/distutils.cfg b/venv2/lib/python2.7/distutils/distutils.cfg new file mode 100644 index 0000000..1af230e --- /dev/null +++ b/venv2/lib/python2.7/distutils/distutils.cfg @@ -0,0 +1,6 @@ +# This is a config file local to this virtualenv installation +# You may include options that will be used by all distutils commands, +# and by easy_install. For instance: +# +# [easy_install] +# find_links = http://mylocalsite diff --git a/venv2/lib/python2.7/encodings b/venv2/lib/python2.7/encodings new file mode 120000 index 0000000..1250ad8 --- /dev/null +++ b/venv2/lib/python2.7/encodings @@ -0,0 +1 @@ +/usr/lib/python2.7/encodings \ No newline at end of file diff --git a/venv2/lib/python2.7/fnmatch.py b/venv2/lib/python2.7/fnmatch.py new file mode 120000 index 0000000..ec3e10c --- /dev/null +++ b/venv2/lib/python2.7/fnmatch.py @@ -0,0 +1 @@ +/usr/lib/python2.7/fnmatch.py \ No newline at end of file diff --git a/venv2/lib/python2.7/genericpath.py b/venv2/lib/python2.7/genericpath.py new file mode 120000 index 0000000..cb8897c --- /dev/null +++ b/venv2/lib/python2.7/genericpath.py @@ -0,0 +1 @@ +/usr/lib/python2.7/genericpath.py \ No newline at end of file diff --git a/venv2/lib/python2.7/lib-dynload b/venv2/lib/python2.7/lib-dynload new file mode 120000 index 0000000..c706a1e --- /dev/null +++ b/venv2/lib/python2.7/lib-dynload @@ -0,0 +1 @@ +/usr/lib/python2.7/lib-dynload \ No newline at end of file diff --git a/venv2/lib/python2.7/linecache.py b/venv2/lib/python2.7/linecache.py new file mode 120000 index 0000000..943c429 --- /dev/null +++ b/venv2/lib/python2.7/linecache.py @@ -0,0 +1 @@ +/usr/lib/python2.7/linecache.py \ No newline at end of file diff --git a/venv2/lib/python2.7/locale.py b/venv2/lib/python2.7/locale.py new file mode 120000 index 0000000..92c243c --- /dev/null +++ b/venv2/lib/python2.7/locale.py @@ -0,0 +1 @@ +/usr/lib/python2.7/locale.py \ No newline at end of file diff --git a/venv2/lib/python2.7/no-global-site-packages.txt b/venv2/lib/python2.7/no-global-site-packages.txt new file mode 100644 index 0000000..e69de29 diff --git a/venv2/lib/python2.7/ntpath.py b/venv2/lib/python2.7/ntpath.py new file mode 120000 index 0000000..5659ae1 --- /dev/null +++ b/venv2/lib/python2.7/ntpath.py @@ -0,0 +1 @@ +/usr/lib/python2.7/ntpath.py \ No newline at end of file diff --git a/venv2/lib/python2.7/orig-prefix.txt b/venv2/lib/python2.7/orig-prefix.txt new file mode 100644 index 0000000..e25db58 --- /dev/null +++ b/venv2/lib/python2.7/orig-prefix.txt @@ -0,0 +1 @@ +/usr \ No newline at end of file diff --git a/venv2/lib/python2.7/os.py b/venv2/lib/python2.7/os.py new file mode 120000 index 0000000..950fc8d --- /dev/null +++ b/venv2/lib/python2.7/os.py @@ -0,0 +1 @@ +/usr/lib/python2.7/os.py \ No newline at end of file diff --git a/venv2/lib/python2.7/posixpath.py b/venv2/lib/python2.7/posixpath.py new file mode 120000 index 0000000..30cb8ca --- /dev/null +++ b/venv2/lib/python2.7/posixpath.py @@ -0,0 +1 @@ +/usr/lib/python2.7/posixpath.py \ No newline at end of file diff --git a/venv2/lib/python2.7/re.py b/venv2/lib/python2.7/re.py new file mode 120000 index 0000000..56a0731 --- /dev/null +++ b/venv2/lib/python2.7/re.py @@ -0,0 +1 @@ +/usr/lib/python2.7/re.py \ No newline at end of file diff --git a/venv2/lib/python2.7/site-packages/easy_install.py b/venv2/lib/python2.7/site-packages/easy_install.py new file mode 100644 index 0000000..d87e984 --- /dev/null +++ b/venv2/lib/python2.7/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/venv2/lib/python2.7/site-packages/pip-19.1.1.dist-info/INSTALLER b/venv2/lib/python2.7/site-packages/pip-19.1.1.dist-info/INSTALLER new file mode 100644 index 0000000..a1b589e --- /dev/null +++ b/venv2/lib/python2.7/site-packages/pip-19.1.1.dist-info/INSTALLER @@ -0,0 +1 @@ +pip diff --git a/venv2/lib/python2.7/site-packages/pip-19.1.1.dist-info/LICENSE.txt b/venv2/lib/python2.7/site-packages/pip-19.1.1.dist-info/LICENSE.txt new file mode 100644 index 0000000..737fec5 --- /dev/null +++ b/venv2/lib/python2.7/site-packages/pip-19.1.1.dist-info/LICENSE.txt @@ -0,0 +1,20 @@ +Copyright (c) 2008-2019 The pip developers (see AUTHORS.txt file) + +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/venv2/lib/python2.7/site-packages/pip-19.1.1.dist-info/METADATA b/venv2/lib/python2.7/site-packages/pip-19.1.1.dist-info/METADATA new file mode 100644 index 0000000..d7aa9a4 --- /dev/null +++ b/venv2/lib/python2.7/site-packages/pip-19.1.1.dist-info/METADATA @@ -0,0 +1,75 @@ +Metadata-Version: 2.1 +Name: pip +Version: 19.1.1 +Summary: The PyPA recommended tool for installing Python packages. +Home-page: https://pip.pypa.io/ +Author: The pip developers +Author-email: pypa-dev@groups.google.com +License: MIT +Keywords: distutils easy_install egg setuptools wheel 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 +Classifier: Programming Language :: Python :: 2 +Classifier: Programming Language :: Python :: 2.7 +Classifier: Programming Language :: Python :: 3 +Classifier: Programming Language :: Python :: 3.4 +Classifier: Programming Language :: Python :: 3.5 +Classifier: Programming Language :: Python :: 3.6 +Classifier: Programming Language :: Python :: 3.7 +Classifier: Programming Language :: Python :: Implementation :: CPython +Classifier: Programming Language :: Python :: Implementation :: PyPy +Requires-Python: >=2.7,!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.* + +pip - The Python Package Installer +================================== + +.. image:: https://img.shields.io/pypi/v/pip.svg + :target: https://pypi.org/project/pip/ + +.. image:: https://readthedocs.org/projects/pip/badge/?version=latest + :target: https://pip.pypa.io/en/latest + +pip is the `package installer`_ for Python. You can use pip to install packages from the `Python Package Index`_ and other indexes. + +Please take a look at our documentation for how to install and use pip: + +* `Installation`_ +* `Usage`_ +* `Release notes`_ + +If you find bugs, need help, or want to talk to the developers please use our mailing lists or chat rooms: + +* `Issue tracking`_ +* `Discourse channel`_ +* `User IRC`_ + +If you want to get involved head over to GitHub to get the source code and feel free to jump on the developer mailing lists and chat rooms: + +* `GitHub page`_ +* `Dev mailing list`_ +* `Dev IRC`_ + +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`_. + +.. _package installer: https://packaging.python.org/en/latest/current/ +.. _Python Package Index: https://pypi.org +.. _Installation: https://pip.pypa.io/en/stable/installing.html +.. _Usage: https://pip.pypa.io/en/stable/ +.. _Release notes: https://pip.pypa.io/en/stable/news.html +.. _GitHub page: https://github.com/pypa/pip +.. _Issue tracking: https://github.com/pypa/pip/issues +.. _Discourse channel: https://discuss.python.org/c/packaging +.. _Dev mailing list: https://groups.google.com/forum/#!forum/pypa-dev +.. _User IRC: https://webchat.freenode.net/?channels=%23pypa +.. _Dev IRC: https://webchat.freenode.net/?channels=%23pypa-dev +.. _PyPA Code of Conduct: https://www.pypa.io/en/latest/code-of-conduct/ + + diff --git a/venv2/lib/python2.7/site-packages/pip-19.1.1.dist-info/RECORD b/venv2/lib/python2.7/site-packages/pip-19.1.1.dist-info/RECORD new file mode 100644 index 0000000..51ad87e --- /dev/null +++ b/venv2/lib/python2.7/site-packages/pip-19.1.1.dist-info/RECORD @@ -0,0 +1,618 @@ +../../../bin/pip,sha256=6WLnsIaScsFb417DvpIGBhQzLrKBPj2uJBVGEV5NOmI,248 +../../../bin/pip2,sha256=6WLnsIaScsFb417DvpIGBhQzLrKBPj2uJBVGEV5NOmI,248 +../../../bin/pip2.7,sha256=6WLnsIaScsFb417DvpIGBhQzLrKBPj2uJBVGEV5NOmI,248 +pip-19.1.1.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4 +pip-19.1.1.dist-info/LICENSE.txt,sha256=W6Ifuwlk-TatfRU2LR7W1JMcyMj5_y1NkRkOEJvnRDE,1090 +pip-19.1.1.dist-info/METADATA,sha256=bLFqR8At4rsCvM1hJTM98bOrT26dNZLeTLo_0PYIZCs,2892 +pip-19.1.1.dist-info/RECORD,, +pip-19.1.1.dist-info/WHEEL,sha256=HX-v9-noUkyUoxyZ1PMSuS7auUxDAR4VBdoYLqD0xws,110 +pip-19.1.1.dist-info/entry_points.txt,sha256=S_zfxY25QtQDVY1BiLAmOKSkkI5llzCKPLiYOSEupsY,98 +pip-19.1.1.dist-info/top_level.txt,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4 +pip/__init__.py,sha256=A9ZYTNji94NmcmoIbn-XLsEni4ac8BoiAt7guiAW27g,23 +pip/__init__.pyc,, +pip/__main__.py,sha256=L3IHqBeasELUHvwy5CT_izVEMhM12tve289qut49DvU,623 +pip/__main__.pyc,, +pip/_internal/__init__.py,sha256=Di4rpoQhA2Kx0Dm0THczf61CDOXcPokR2FIpyKkRm1Y,2870 +pip/_internal/__init__.pyc,, +pip/_internal/build_env.py,sha256=ZUpR-00pxQc-Mw750jyBVA-oWb4WHtpK63pfiTHd8qU,7392 +pip/_internal/build_env.pyc,, +pip/_internal/cache.py,sha256=PQesJm1JrjgAya3VyV6qkbnfACG23c1wQ7PaSw_0F-Y,7656 +pip/_internal/cache.pyc,, +pip/_internal/cli/__init__.py,sha256=FkHBgpxxb-_gd6r1FjnNhfMOzAUYyXoXKJ6abijfcFU,132 +pip/_internal/cli/__init__.pyc,, +pip/_internal/cli/autocompletion.py,sha256=ptvsMdGjq42pzoY4skABVF43u2xAtLJlXAulPi-A10Y,6083 +pip/_internal/cli/autocompletion.pyc,, +pip/_internal/cli/base_command.py,sha256=2SbVZ2o3KmVdeJlK4l1lP6y_ZMFZ8VkDp9ns1ByZ-xM,12629 +pip/_internal/cli/base_command.pyc,, +pip/_internal/cli/cmdoptions.py,sha256=IA1XAgTJ1oWXp1oF3CkE8mpUx8vqIysyd7hlEZk7wWE,23813 +pip/_internal/cli/cmdoptions.pyc,, +pip/_internal/cli/main_parser.py,sha256=YH_w_hApq2pyS_G5tZlbIe7g_HUIcKE820jhnv0piVA,3002 +pip/_internal/cli/main_parser.pyc,, +pip/_internal/cli/parser.py,sha256=VZKUKJPbU6I2cHPLDOikin-aCx7OvLcZ3fzYp3xytd8,9378 +pip/_internal/cli/parser.pyc,, +pip/_internal/cli/status_codes.py,sha256=F6uDG6Gj7RNKQJUDnd87QKqI16Us-t-B0wPF_4QMpWc,156 +pip/_internal/cli/status_codes.pyc,, +pip/_internal/commands/__init__.py,sha256=7fscm9bUGJInZwWY7VjH2pPC7HUWQWyjYMEIB33L9CY,2223 +pip/_internal/commands/__init__.pyc,, +pip/_internal/commands/check.py,sha256=liigNVif0iz2mBfhvsajrLZT5zM5KIvgmKvhAW91EzA,1430 +pip/_internal/commands/check.pyc,, +pip/_internal/commands/completion.py,sha256=hqvCvoxsIHjysiD7olHKTqK2lzE1_lS6LWn69kN5qyI,2929 +pip/_internal/commands/completion.pyc,, +pip/_internal/commands/configuration.py,sha256=Il76e-ERfEHtW2GbV1aTR6o-xurh-LVv2NCea7erP4s,8020 +pip/_internal/commands/configuration.pyc,, +pip/_internal/commands/download.py,sha256=XPe3Kuj9iZfXwOiJq70mYVYNZD5lJCLnGT_C61cOsKw,6623 +pip/_internal/commands/download.pyc,, +pip/_internal/commands/freeze.py,sha256=VvS3G0wrm_9BH3B7Ex5msLL_1UQTtCq5G8dDI63Iemo,3259 +pip/_internal/commands/freeze.pyc,, +pip/_internal/commands/hash.py,sha256=K1JycsD-rpjqrRcL_ijacY9UKmI82pQcLYq4kCM4Pv0,1681 +pip/_internal/commands/hash.pyc,, +pip/_internal/commands/help.py,sha256=MwBhPJpW1Dt3GfJV3V8V6kgAy_pXT0jGrZJB1wCTW-E,1090 +pip/_internal/commands/help.pyc,, +pip/_internal/commands/install.py,sha256=vSb8TfUHwz3HJ5cA-JnPP3X84ONQ0wkCztw0pHFbmsc,22841 +pip/_internal/commands/install.pyc,, +pip/_internal/commands/list.py,sha256=EtKEUotPbLCzzcWrZqEiEXi4ic00dgUOKUeNU0y7Pfg,10166 +pip/_internal/commands/list.pyc,, +pip/_internal/commands/search.py,sha256=sLZ9icKMEEGekHvzRRZMiTd1zCFIZeDptyyU1mQCYzk,4728 +pip/_internal/commands/search.pyc,, +pip/_internal/commands/show.py,sha256=bE-ucu8fAjTTENpRRKhwD3QSWR8Rss7YgKAbMJoxock,6273 +pip/_internal/commands/show.pyc,, +pip/_internal/commands/uninstall.py,sha256=h0gfPF5jylDESx_IHgF6bZME7QAEOHzQHdn65GP-jrE,2963 +pip/_internal/commands/uninstall.pyc,, +pip/_internal/commands/wheel.py,sha256=7MNPZqK9WWxZC3TgzvMBH-RPRlOFLpwq927lkzUiUjI,7167 +pip/_internal/commands/wheel.pyc,, +pip/_internal/configuration.py,sha256=xit1f8ZdnHz-AHzWAKTUa_gZzlIIM-5AgK30glB71ik,13115 +pip/_internal/configuration.pyc,, +pip/_internal/download.py,sha256=0_8jUtL4SLZe4qo8Fe66qkbO69gUG-IPbp932WOHyFs,35147 +pip/_internal/download.pyc,, +pip/_internal/exceptions.py,sha256=ryXWmEJpSHr3yXXk1efWHSzQwAjBdBJiKR5kCQvjgPA,9117 +pip/_internal/exceptions.pyc,, +pip/_internal/index.py,sha256=BO6jpJRvb_6HaS4meE8Lj_lyAX7IRP6pRof-zG-_hYw,42268 +pip/_internal/index.pyc,, +pip/_internal/locations.py,sha256=-N3f1I24gcPTdW52onPnySDoQUIn_TtPYV99WwsjxRE,6932 +pip/_internal/locations.pyc,, +pip/_internal/models/__init__.py,sha256=3DHUd_qxpPozfzouoqa9g9ts1Czr5qaHfFxbnxriepM,63 +pip/_internal/models/__init__.pyc,, +pip/_internal/models/candidate.py,sha256=_IzS-yw0h2UHfZALsZ2mxkeGNSNuNGspjSn2JfZ-ZHM,1045 +pip/_internal/models/candidate.pyc,, +pip/_internal/models/format_control.py,sha256=ap8Swa26ocSXBxIuCvaDBRZjxdKUFuwC-bfqXQHWtKw,2250 +pip/_internal/models/format_control.pyc,, +pip/_internal/models/index.py,sha256=K59A8-hVhBM20Xkahr4dTwP7OjkJyEqXH11UwHFVgqM,1060 +pip/_internal/models/index.pyc,, +pip/_internal/models/link.py,sha256=Hqu72UgH266njr2z2kTnIG-sQiPtjENDbPqXFzT-84s,4783 +pip/_internal/models/link.pyc,, +pip/_internal/operations/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +pip/_internal/operations/__init__.pyc,, +pip/_internal/operations/check.py,sha256=CZHaksHKUMo58wGHGhhKlPrpcRMHObouzzyd9pMRFt8,5109 +pip/_internal/operations/check.pyc,, +pip/_internal/operations/freeze.py,sha256=_Ffl2HpoWLLHaiB1XyTjKIA3QWC8H-E1ib6xJgX_mRE,9279 +pip/_internal/operations/freeze.pyc,, +pip/_internal/operations/prepare.py,sha256=jQ6FaxA2u_MYnDQxcu6YCfmJ9sfhqrKN9vdd4-5N6lU,16931 +pip/_internal/operations/prepare.pyc,, +pip/_internal/pep425tags.py,sha256=jyBorYxepVJeHNo69TE-3UxJXsGO2XvGxveSWQDi_Wo,13128 +pip/_internal/pep425tags.pyc,, +pip/_internal/pyproject.py,sha256=OlCw7pSqST68hUF_eV-YVaqJ4I7z_ROJwvgra-2C_5I,6464 +pip/_internal/pyproject.pyc,, +pip/_internal/req/__init__.py,sha256=Y2SjAuMFsSt3dkiK8kkiQAfv8sHrjl0PAT63FKFT0tM,2364 +pip/_internal/req/__init__.pyc,, +pip/_internal/req/constructors.py,sha256=4KlO_-G89XYZW1VnzHwV34oCvR6ilmxFqSSGLgv4PEw,11399 +pip/_internal/req/constructors.pyc,, +pip/_internal/req/req_file.py,sha256=mZoQvhHZJwgljrXDH8CLXaNoUK4sM8_bRfK9Po2fM80,13714 +pip/_internal/req/req_file.pyc,, +pip/_internal/req/req_install.py,sha256=SGJLNpyZwzOL_EqfviIvAoZgxAoivxmFBN1cOU7DX9s,40061 +pip/_internal/req/req_install.pyc,, +pip/_internal/req/req_set.py,sha256=PaDc5EswLQhxBMFbuKbJ0frZbMNKocmA8OGqIWT-9EY,7860 +pip/_internal/req/req_set.pyc,, +pip/_internal/req/req_tracker.py,sha256=wBpDzSDSYwpUfW4K43NrEOCCp1r6stuubfLc65Y95EM,3129 +pip/_internal/req/req_tracker.pyc,, +pip/_internal/req/req_uninstall.py,sha256=rVOk8BRM_L9rsUUr9lmkV6Lm9N1Os7TEIDir6tT1Q7U,23105 +pip/_internal/req/req_uninstall.pyc,, +pip/_internal/resolve.py,sha256=rZDMkyhUIbaytY9KsYQVez8nl21xLXgaDt8xuHvyGMI,15128 +pip/_internal/resolve.pyc,, +pip/_internal/utils/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +pip/_internal/utils/__init__.pyc,, +pip/_internal/utils/appdirs.py,sha256=r9i0BZLK9KcvrzI5tqlw8ehRTtSehWGERFLy7YppG3g,9398 +pip/_internal/utils/appdirs.pyc,, +pip/_internal/utils/compat.py,sha256=Q0cmYGBaKB4tV5E0CG11339YLxuCaqdUCSJd9vinMc8,8551 +pip/_internal/utils/compat.pyc,, +pip/_internal/utils/deprecation.py,sha256=HYUvfCZUxwLIFlYHeZM3VmuFhXTdlw0rk4-cC14bKw4,3080 +pip/_internal/utils/deprecation.pyc,, +pip/_internal/utils/encoding.py,sha256=tudXCoAPe9fZvNK4cmWQs2frREZ-QuGCwF_SlTyz6cI,1218 +pip/_internal/utils/encoding.pyc,, +pip/_internal/utils/filesystem.py,sha256=ojaIDvOFOtkpKme5se6X2N8ARmQxu8cxvaaI-NFqVtk,990 +pip/_internal/utils/filesystem.pyc,, +pip/_internal/utils/glibc.py,sha256=9_1wY9Lmca4xzOQDF1A6ITW_N80j_BXwN7Sx7vBqP4k,3282 +pip/_internal/utils/glibc.pyc,, +pip/_internal/utils/hashes.py,sha256=_6l8M_nqmLZjs-o2lP2rB7ZleAT05WOuqgwBa8uFYR8,3527 +pip/_internal/utils/hashes.pyc,, +pip/_internal/utils/logging.py,sha256=qSYK7Wf_oiCr1VR4qvAq_TtpY3SIKSlVDgnAeQqSjHc,11671 +pip/_internal/utils/logging.pyc,, +pip/_internal/utils/misc.py,sha256=QsYl2j5rU5Mh_CEsSf1W8GB6HvPOfioj30Emqz7mg7s,32434 +pip/_internal/utils/misc.pyc,, +pip/_internal/utils/models.py,sha256=DQYZSRhjvSdDTAaJLLCpDtxAn1S_-v_8nlNjv4T2jwY,1042 +pip/_internal/utils/models.pyc,, +pip/_internal/utils/outdated.py,sha256=sEe1l902jiOl3tArf6omqVd44s4BT9DOk3a3CXtJNNI,5868 +pip/_internal/utils/outdated.pyc,, +pip/_internal/utils/packaging.py,sha256=frr5M-ORe30rRKbH6pwqp_jkX1T8rx-UGDlj8vG5HII,2743 +pip/_internal/utils/packaging.pyc,, +pip/_internal/utils/setuptools_build.py,sha256=0blfscmNJW_iZ5DcswJeDB_PbtTEjfK9RL1R1WEDW2E,278 +pip/_internal/utils/setuptools_build.pyc,, +pip/_internal/utils/temp_dir.py,sha256=0Xq5ZlOd2OOeHwKM6hGy66gnMGAbyhio7DtjLHd7DFg,5339 +pip/_internal/utils/temp_dir.pyc,, +pip/_internal/utils/typing.py,sha256=bF73ImJzIaxLLEVwfEaSJzFGqV9LaxkQBvDULIyr1jI,1125 +pip/_internal/utils/typing.pyc,, +pip/_internal/utils/ui.py,sha256=yRqmi2V4OeTYP9SnXhuXKlyRx8xJ79AjLRgvLCZ4E1E,13812 +pip/_internal/utils/ui.pyc,, +pip/_internal/vcs/__init__.py,sha256=kgwOhkt6ddbKosZytktLydMUb_e-JpXMstJF9YpSOwM,19205 +pip/_internal/vcs/__init__.pyc,, +pip/_internal/vcs/bazaar.py,sha256=3lA6CKyrYpoJuxjTa8oTozszNcqdYILqEhKLJ41RMug,3231 +pip/_internal/vcs/bazaar.pyc,, +pip/_internal/vcs/git.py,sha256=x0PQDgzWho544gfqtE9CXoUkN34zE4YkpgrbTcWEte4,12960 +pip/_internal/vcs/git.pyc,, +pip/_internal/vcs/mercurial.py,sha256=AbH4IWdLCsjUxFnIHRvijV-7SNhfjDZJJFzQ1e_b48Q,3355 +pip/_internal/vcs/mercurial.pyc,, +pip/_internal/vcs/subversion.py,sha256=cMH4MEobSj68gaSiBD53KXVJnEbhtuUJjL8U1Kk_v70,8167 +pip/_internal/vcs/subversion.pyc,, +pip/_internal/wheel.py,sha256=-MijhN1BIuaQkb3909smA63wkbvdRrdtr1CUDxnQTqA,41189 +pip/_internal/wheel.pyc,, +pip/_vendor/__init__.py,sha256=iip2nWwH_riYqnDnM0q4BJFrWE-XWjYfxCejJKct0WM,4654 +pip/_vendor/__init__.pyc,, +pip/_vendor/appdirs.py,sha256=BENKsvcA08IpccD9345-rMrg3aXWFA1q6BFEglnHg6I,24547 +pip/_vendor/appdirs.pyc,, +pip/_vendor/cachecontrol/__init__.py,sha256=6cRPchVqkAkeUtYTSW8qCetjSqJo-GxP-n4VMVDbvmc,302 +pip/_vendor/cachecontrol/__init__.pyc,, +pip/_vendor/cachecontrol/_cmd.py,sha256=URGE0KrA87QekCG3SGPatlSPT571dZTDjNa-ZXX3pDc,1295 +pip/_vendor/cachecontrol/_cmd.pyc,, +pip/_vendor/cachecontrol/adapter.py,sha256=eBGAtVNRZgtl_Kj5JV54miqL9YND-D0JZPahwY8kFtY,4863 +pip/_vendor/cachecontrol/adapter.pyc,, +pip/_vendor/cachecontrol/cache.py,sha256=1fc4wJP8HYt1ycnJXeEw5pCpeBL2Cqxx6g9Fb0AYDWQ,805 +pip/_vendor/cachecontrol/cache.pyc,, +pip/_vendor/cachecontrol/caches/__init__.py,sha256=-gHNKYvaeD0kOk5M74eOrsSgIKUtC6i6GfbmugGweEo,86 +pip/_vendor/cachecontrol/caches/__init__.pyc,, +pip/_vendor/cachecontrol/caches/file_cache.py,sha256=8vrSzzGcdfEfICago1uSFbkumNJMGLbCdEkXsmUIExw,4177 +pip/_vendor/cachecontrol/caches/file_cache.pyc,, +pip/_vendor/cachecontrol/caches/redis_cache.py,sha256=HxelMpNCo-dYr2fiJDwM3hhhRmxUYtB5tXm1GpAAT4Y,856 +pip/_vendor/cachecontrol/caches/redis_cache.pyc,, +pip/_vendor/cachecontrol/compat.py,sha256=kHNvMRdt6s_Xwqq_9qJmr9ou3wYMOMUMxPPcwNxT8Mc,695 +pip/_vendor/cachecontrol/compat.pyc,, +pip/_vendor/cachecontrol/controller.py,sha256=U7g-YwizQ2O5NRgK_MZreF1ntM4E49C3PuF3od-Vwz4,13698 +pip/_vendor/cachecontrol/controller.pyc,, +pip/_vendor/cachecontrol/filewrapper.py,sha256=vACKO8Llzu_ZWyjV1Fxn1MA4TGU60N5N3GSrAFdAY2Q,2533 +pip/_vendor/cachecontrol/filewrapper.pyc,, +pip/_vendor/cachecontrol/heuristics.py,sha256=BFGHJ3yQcxvZizfo90LLZ04T_Z5XSCXvFotrp7Us0sc,4070 +pip/_vendor/cachecontrol/heuristics.pyc,, +pip/_vendor/cachecontrol/serialize.py,sha256=GebE34fgToyWwAsRPguh8hEPN6CqoG-5hRMXRsjVABQ,6954 +pip/_vendor/cachecontrol/serialize.pyc,, +pip/_vendor/cachecontrol/wrapper.py,sha256=sfr9YHWx-5TwNz1H5rT6QOo8ggII6v3vbEDjQFwR6wc,671 +pip/_vendor/cachecontrol/wrapper.pyc,, +pip/_vendor/certifi/__init__.py,sha256=-M1moep9D6jZnOAT0qnM1dpoEHMPage7Osv5xLRLyFg,52 +pip/_vendor/certifi/__init__.pyc,, +pip/_vendor/certifi/__main__.py,sha256=NaCn6WtWME-zzVWQ2j4zFyl8cY4knDa9CwtHNIeFPhM,53 +pip/_vendor/certifi/__main__.pyc,, +pip/_vendor/certifi/cacert.pem,sha256=OXuDPlrPiaJwm5ZEAamspo0ktiNJtyu-OGhOWGqgeic,284518 +pip/_vendor/certifi/core.py,sha256=EuFc2BsToG5O1-qsx4BSjQ1r1-7WRtH87b1WflZOWhI,218 +pip/_vendor/certifi/core.pyc,, +pip/_vendor/chardet/__init__.py,sha256=YsP5wQlsHJ2auF1RZJfypiSrCA7_bQiRm3ES_NI76-Y,1559 +pip/_vendor/chardet/__init__.pyc,, +pip/_vendor/chardet/big5freq.py,sha256=D_zK5GyzoVsRes0HkLJziltFQX0bKCLOrFe9_xDvO_8,31254 +pip/_vendor/chardet/big5freq.pyc,, +pip/_vendor/chardet/big5prober.py,sha256=kBxHbdetBpPe7xrlb-e990iot64g_eGSLd32lB7_h3M,1757 +pip/_vendor/chardet/big5prober.pyc,, +pip/_vendor/chardet/chardistribution.py,sha256=3woWS62KrGooKyqz4zQSnjFbJpa6V7g02daAibTwcl8,9411 +pip/_vendor/chardet/chardistribution.pyc,, +pip/_vendor/chardet/charsetgroupprober.py,sha256=6bDu8YIiRuScX4ca9Igb0U69TA2PGXXDej6Cc4_9kO4,3787 +pip/_vendor/chardet/charsetgroupprober.pyc,, +pip/_vendor/chardet/charsetprober.py,sha256=KSmwJErjypyj0bRZmC5F5eM7c8YQgLYIjZXintZNstg,5110 +pip/_vendor/chardet/charsetprober.pyc,, +pip/_vendor/chardet/cli/__init__.py,sha256=AbpHGcgLb-kRsJGnwFEktk7uzpZOCcBY74-YBdrKVGs,1 +pip/_vendor/chardet/cli/__init__.pyc,, +pip/_vendor/chardet/cli/chardetect.py,sha256=DI8dlV3FBD0c0XA_y3sQ78z754DUv1J8n34RtDjOXNw,2774 +pip/_vendor/chardet/cli/chardetect.pyc,, +pip/_vendor/chardet/codingstatemachine.py,sha256=VYp_6cyyki5sHgXDSZnXW4q1oelHc3cu9AyQTX7uug8,3590 +pip/_vendor/chardet/codingstatemachine.pyc,, +pip/_vendor/chardet/compat.py,sha256=PKTzHkSbtbHDqS9PyujMbX74q1a8mMpeQTDVsQhZMRw,1134 +pip/_vendor/chardet/compat.pyc,, +pip/_vendor/chardet/cp949prober.py,sha256=TZ434QX8zzBsnUvL_8wm4AQVTZ2ZkqEEQL_lNw9f9ow,1855 +pip/_vendor/chardet/cp949prober.pyc,, +pip/_vendor/chardet/enums.py,sha256=Aimwdb9as1dJKZaFNUH2OhWIVBVd6ZkJJ_WK5sNY8cU,1661 +pip/_vendor/chardet/enums.pyc,, +pip/_vendor/chardet/escprober.py,sha256=kkyqVg1Yw3DIOAMJ2bdlyQgUFQhuHAW8dUGskToNWSc,3950 +pip/_vendor/chardet/escprober.pyc,, +pip/_vendor/chardet/escsm.py,sha256=RuXlgNvTIDarndvllNCk5WZBIpdCxQ0kcd9EAuxUh84,10510 +pip/_vendor/chardet/escsm.pyc,, +pip/_vendor/chardet/eucjpprober.py,sha256=iD8Jdp0ISRjgjiVN7f0e8xGeQJ5GM2oeZ1dA8nbSeUw,3749 +pip/_vendor/chardet/eucjpprober.pyc,, +pip/_vendor/chardet/euckrfreq.py,sha256=-7GdmvgWez4-eO4SuXpa7tBiDi5vRXQ8WvdFAzVaSfo,13546 +pip/_vendor/chardet/euckrfreq.pyc,, +pip/_vendor/chardet/euckrprober.py,sha256=MqFMTQXxW4HbzIpZ9lKDHB3GN8SP4yiHenTmf8g_PxY,1748 +pip/_vendor/chardet/euckrprober.pyc,, +pip/_vendor/chardet/euctwfreq.py,sha256=No1WyduFOgB5VITUA7PLyC5oJRNzRyMbBxaKI1l16MA,31621 +pip/_vendor/chardet/euctwfreq.pyc,, +pip/_vendor/chardet/euctwprober.py,sha256=13p6EP4yRaxqnP4iHtxHOJ6R2zxHq1_m8hTRjzVZ95c,1747 +pip/_vendor/chardet/euctwprober.pyc,, +pip/_vendor/chardet/gb2312freq.py,sha256=JX8lsweKLmnCwmk8UHEQsLgkr_rP_kEbvivC4qPOrlc,20715 +pip/_vendor/chardet/gb2312freq.pyc,, +pip/_vendor/chardet/gb2312prober.py,sha256=gGvIWi9WhDjE-xQXHvNIyrnLvEbMAYgyUSZ65HUfylw,1754 +pip/_vendor/chardet/gb2312prober.pyc,, +pip/_vendor/chardet/hebrewprober.py,sha256=c3SZ-K7hvyzGY6JRAZxJgwJ_sUS9k0WYkvMY00YBYFo,13838 +pip/_vendor/chardet/hebrewprober.pyc,, +pip/_vendor/chardet/jisfreq.py,sha256=vpmJv2Bu0J8gnMVRPHMFefTRvo_ha1mryLig8CBwgOg,25777 +pip/_vendor/chardet/jisfreq.pyc,, +pip/_vendor/chardet/jpcntx.py,sha256=PYlNqRUQT8LM3cT5FmHGP0iiscFlTWED92MALvBungo,19643 +pip/_vendor/chardet/jpcntx.pyc,, +pip/_vendor/chardet/langbulgarianmodel.py,sha256=1HqQS9Pbtnj1xQgxitJMvw8X6kKr5OockNCZWfEQrPE,12839 +pip/_vendor/chardet/langbulgarianmodel.pyc,, +pip/_vendor/chardet/langcyrillicmodel.py,sha256=LODajvsetH87yYDDQKA2CULXUH87tI223dhfjh9Zx9c,17948 +pip/_vendor/chardet/langcyrillicmodel.pyc,, +pip/_vendor/chardet/langgreekmodel.py,sha256=8YAW7bU8YwSJap0kIJSbPMw1BEqzGjWzqcqf0WgUKAA,12688 +pip/_vendor/chardet/langgreekmodel.pyc,, +pip/_vendor/chardet/langhebrewmodel.py,sha256=JSnqmE5E62tDLTPTvLpQsg5gOMO4PbdWRvV7Avkc0HA,11345 +pip/_vendor/chardet/langhebrewmodel.pyc,, +pip/_vendor/chardet/langhungarianmodel.py,sha256=RhapYSG5l0ZaO-VV4Fan5sW0WRGQqhwBM61yx3yxyOA,12592 +pip/_vendor/chardet/langhungarianmodel.pyc,, +pip/_vendor/chardet/langthaimodel.py,sha256=8l0173Gu_W6G8mxmQOTEF4ls2YdE7FxWf3QkSxEGXJQ,11290 +pip/_vendor/chardet/langthaimodel.pyc,, +pip/_vendor/chardet/langturkishmodel.py,sha256=W22eRNJsqI6uWAfwXSKVWWnCerYqrI8dZQTm_M0lRFk,11102 +pip/_vendor/chardet/langturkishmodel.pyc,, +pip/_vendor/chardet/latin1prober.py,sha256=S2IoORhFk39FEFOlSFWtgVybRiP6h7BlLldHVclNkU8,5370 +pip/_vendor/chardet/latin1prober.pyc,, +pip/_vendor/chardet/mbcharsetprober.py,sha256=AR95eFH9vuqSfvLQZN-L5ijea25NOBCoXqw8s5O9xLQ,3413 +pip/_vendor/chardet/mbcharsetprober.pyc,, +pip/_vendor/chardet/mbcsgroupprober.py,sha256=h6TRnnYq2OxG1WdD5JOyxcdVpn7dG0q-vB8nWr5mbh4,2012 +pip/_vendor/chardet/mbcsgroupprober.pyc,, +pip/_vendor/chardet/mbcssm.py,sha256=SY32wVIF3HzcjY3BaEspy9metbNSKxIIB0RKPn7tjpI,25481 +pip/_vendor/chardet/mbcssm.pyc,, +pip/_vendor/chardet/sbcharsetprober.py,sha256=LDSpCldDCFlYwUkGkwD2oFxLlPWIWXT09akH_2PiY74,5657 +pip/_vendor/chardet/sbcharsetprober.pyc,, +pip/_vendor/chardet/sbcsgroupprober.py,sha256=1IprcCB_k1qfmnxGC6MBbxELlKqD3scW6S8YIwdeyXA,3546 +pip/_vendor/chardet/sbcsgroupprober.pyc,, +pip/_vendor/chardet/sjisprober.py,sha256=IIt-lZj0WJqK4rmUZzKZP4GJlE8KUEtFYVuY96ek5MQ,3774 +pip/_vendor/chardet/sjisprober.pyc,, +pip/_vendor/chardet/universaldetector.py,sha256=qL0174lSZE442eB21nnktT9_VcAye07laFWUeUrjttY,12485 +pip/_vendor/chardet/universaldetector.pyc,, +pip/_vendor/chardet/utf8prober.py,sha256=IdD8v3zWOsB8OLiyPi-y_fqwipRFxV9Nc1eKBLSuIEw,2766 +pip/_vendor/chardet/utf8prober.pyc,, +pip/_vendor/chardet/version.py,sha256=sp3B08mrDXB-pf3K9fqJ_zeDHOCLC8RrngQyDFap_7g,242 +pip/_vendor/chardet/version.pyc,, +pip/_vendor/colorama/__init__.py,sha256=lJdY6COz9uM_pXwuk9oLr0fp8H8q2RrUqN16GKabvq4,239 +pip/_vendor/colorama/__init__.pyc,, +pip/_vendor/colorama/ansi.py,sha256=Fi0un-QLqRm-v7o_nKiOqyC8PapBJK7DLV_q9LKtTO0,2524 +pip/_vendor/colorama/ansi.pyc,, +pip/_vendor/colorama/ansitowin32.py,sha256=u8QaqdqS_xYSfNkPM1eRJLHz6JMWPodaJaP0mxgHCDc,10462 +pip/_vendor/colorama/ansitowin32.pyc,, +pip/_vendor/colorama/initialise.py,sha256=PprovDNxMTrvoNHFcL2NZjpH2XzDc8BLxLxiErfUl4k,1915 +pip/_vendor/colorama/initialise.pyc,, +pip/_vendor/colorama/win32.py,sha256=bJ8Il9jwaBN5BJ8bmN6FoYZ1QYuMKv2j8fGrXh7TJjw,5404 +pip/_vendor/colorama/win32.pyc,, +pip/_vendor/colorama/winterm.py,sha256=2y_2b7Zsv34feAsP67mLOVc-Bgq51mdYGo571VprlrM,6438 +pip/_vendor/colorama/winterm.pyc,, +pip/_vendor/distlib/__init__.py,sha256=7uthK6m96pTekk8hjlT-MybcwYmmxwP8gEOxXVg1f2s,581 +pip/_vendor/distlib/__init__.pyc,, +pip/_vendor/distlib/_backport/__init__.py,sha256=bqS_dTOH6uW9iGgd0uzfpPjo6vZ4xpPZ7kyfZJ2vNaw,274 +pip/_vendor/distlib/_backport/__init__.pyc,, +pip/_vendor/distlib/_backport/misc.py,sha256=KWecINdbFNOxSOP1fGF680CJnaC6S4fBRgEtaYTw0ig,971 +pip/_vendor/distlib/_backport/misc.pyc,, +pip/_vendor/distlib/_backport/shutil.py,sha256=VW1t3uYqUjWZH7jV-6QiimLhnldoV5uIpH4EuiT1jfw,25647 +pip/_vendor/distlib/_backport/shutil.pyc,, +pip/_vendor/distlib/_backport/sysconfig.cfg,sha256=swZKxq9RY5e9r3PXCrlvQPMsvOdiWZBTHLEbqS8LJLU,2617 +pip/_vendor/distlib/_backport/sysconfig.py,sha256=JdJ9ztRy4Hc-b5-VS74x3nUtdEIVr_OBvMsIb8O2sjc,26964 +pip/_vendor/distlib/_backport/sysconfig.pyc,, +pip/_vendor/distlib/_backport/tarfile.py,sha256=Ihp7rXRcjbIKw8COm9wSePV9ARGXbSF9gGXAMn2Q-KU,92628 +pip/_vendor/distlib/_backport/tarfile.pyc,, +pip/_vendor/distlib/compat.py,sha256=xdNZmqFN5HwF30HjRn5M415pcC2kgXRBXn767xS8v-M,41404 +pip/_vendor/distlib/compat.pyc,, +pip/_vendor/distlib/database.py,sha256=-KJH63AJ7hqjLtGCwOTrionhKr2Vsytdwkjyo8UdEco,51029 +pip/_vendor/distlib/database.pyc,, +pip/_vendor/distlib/index.py,sha256=Dd1kIV06XIdynNpKxHMMRRIKsXuoUsG7QIzntfVtZCI,21073 +pip/_vendor/distlib/index.pyc,, +pip/_vendor/distlib/locators.py,sha256=S9G2IsZp0RnMMbXGrT-gu7892pNpy1XMlUEuUHX3OI8,51828 +pip/_vendor/distlib/locators.pyc,, +pip/_vendor/distlib/manifest.py,sha256=nQEhYmgoreaBZzyFzwYsXxJARu3fo4EkunU163U16iE,14811 +pip/_vendor/distlib/manifest.pyc,, +pip/_vendor/distlib/markers.py,sha256=6Ac3cCfFBERexiESWIOXmg-apIP8l2esafNSX3KMy-8,4387 +pip/_vendor/distlib/markers.pyc,, +pip/_vendor/distlib/metadata.py,sha256=BNCnpRfFVslyZcosr4vnE_YbkRb3TNxXtk7TrDszJdc,40172 +pip/_vendor/distlib/metadata.pyc,, +pip/_vendor/distlib/resources.py,sha256=2FGv0ZHF14KXjLIlL0R991lyQQGcewOS4mJ-5n-JVnc,10766 +pip/_vendor/distlib/resources.pyc,, +pip/_vendor/distlib/scripts.py,sha256=NYqRJ2uuEuJwr_NNLzWH0m_s_YsobDFQb6HqxuQ2Sew,16638 +pip/_vendor/distlib/scripts.pyc,, +pip/_vendor/distlib/t32.exe,sha256=ftub1bsSPUCOnBn-eCtcarKTk0N0CBEP53BumkIxWJE,92672 +pip/_vendor/distlib/t64.exe,sha256=iChOG627LWTHY8-jzSwlo9SYU5a-0JHwQu4AqDz8I68,102400 +pip/_vendor/distlib/util.py,sha256=gwKL5geJKmtR4GeIUnoMAWjsPPG3tVP_mFxw_Sx-isc,59681 +pip/_vendor/distlib/util.pyc,, +pip/_vendor/distlib/version.py,sha256=_n7F6juvQGAcn769E_SHa7fOcf5ERlEVymJ_EjPRwGw,23391 +pip/_vendor/distlib/version.pyc,, +pip/_vendor/distlib/w32.exe,sha256=NPYPpt7PIjVqABEu1CzabbDyHHkJpuw-_qZq_48H0j0,89088 +pip/_vendor/distlib/w64.exe,sha256=Yb-qr1OQEzL8KRGTk-XHUZDwMSljfQeZnVoTk-K4e7E,99328 +pip/_vendor/distlib/wheel.py,sha256=gV53KDG7BgbxsdeKjnATbP47gTEJRNylcIeE1TFin1o,39880 +pip/_vendor/distlib/wheel.pyc,, +pip/_vendor/distro.py,sha256=X2So5kjrRKyMbQJ90Xgy93HU5eFtujCzKaYNeoy1k1c,43251 +pip/_vendor/distro.pyc,, +pip/_vendor/html5lib/__init__.py,sha256=Ztrn7UvF-wIFAgRBBa0ML-Gu5AffH3BPX_INJx4SaBI,1162 +pip/_vendor/html5lib/__init__.pyc,, +pip/_vendor/html5lib/_ihatexml.py,sha256=3LBtJMlzgwM8vpQiU1TvGmEEmNH72sV0yD8yS53y07A,16705 +pip/_vendor/html5lib/_ihatexml.pyc,, +pip/_vendor/html5lib/_inputstream.py,sha256=bPUWcAfJScK4xkjQQaG_HsI2BvEVbFvI0AsodDYPQj0,32552 +pip/_vendor/html5lib/_inputstream.pyc,, +pip/_vendor/html5lib/_tokenizer.py,sha256=YAaOEBD6qc5ISq9Xt9Nif1OFgcybTTfMdwqBkZhpAq4,76580 +pip/_vendor/html5lib/_tokenizer.pyc,, +pip/_vendor/html5lib/_trie/__init__.py,sha256=8VR1bcgD2OpeS2XExpu5yBhP_Q1K-lwKbBKICBPf1kU,289 +pip/_vendor/html5lib/_trie/__init__.pyc,, +pip/_vendor/html5lib/_trie/_base.py,sha256=uJHVhzif9S0MJXgy9F98iEev5evi_rgUk5BmEbUSp8c,930 +pip/_vendor/html5lib/_trie/_base.pyc,, +pip/_vendor/html5lib/_trie/datrie.py,sha256=EQpqSfkZRuTbE-DuhW7xMdVDxdZNZ0CfmnYfHA_3zxM,1178 +pip/_vendor/html5lib/_trie/datrie.pyc,, +pip/_vendor/html5lib/_trie/py.py,sha256=wXmQLrZRf4MyWNyg0m3h81m9InhLR7GJ002mIIZh-8o,1775 +pip/_vendor/html5lib/_trie/py.pyc,, +pip/_vendor/html5lib/_utils.py,sha256=ismpASeqa2jqEPQjHUj8vReAf7yIoKnvLN5fuOw6nv0,4015 +pip/_vendor/html5lib/_utils.pyc,, +pip/_vendor/html5lib/constants.py,sha256=4lmZWLtEPRLnl8NzftOoYTJdo6jpeMtP6dqQC0g_bWQ,83518 +pip/_vendor/html5lib/constants.pyc,, +pip/_vendor/html5lib/filters/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +pip/_vendor/html5lib/filters/__init__.pyc,, +pip/_vendor/html5lib/filters/alphabeticalattributes.py,sha256=lViZc2JMCclXi_5gduvmdzrRxtO5Xo9ONnbHBVCsykU,919 +pip/_vendor/html5lib/filters/alphabeticalattributes.pyc,, +pip/_vendor/html5lib/filters/base.py,sha256=z-IU9ZAYjpsVsqmVt7kuWC63jR11hDMr6CVrvuao8W0,286 +pip/_vendor/html5lib/filters/base.pyc,, +pip/_vendor/html5lib/filters/inject_meta_charset.py,sha256=egDXUEHXmAG9504xz0K6ALDgYkvUrC2q15YUVeNlVQg,2945 +pip/_vendor/html5lib/filters/inject_meta_charset.pyc,, +pip/_vendor/html5lib/filters/lint.py,sha256=jk6q56xY0ojiYfvpdP-OZSm9eTqcAdRqhCoPItemPYA,3643 +pip/_vendor/html5lib/filters/lint.pyc,, +pip/_vendor/html5lib/filters/optionaltags.py,sha256=8lWT75J0aBOHmPgfmqTHSfPpPMp01T84NKu0CRedxcE,10588 +pip/_vendor/html5lib/filters/optionaltags.pyc,, +pip/_vendor/html5lib/filters/sanitizer.py,sha256=4ON02KNjuqda1lCw5_JCUZxb0BzWR5M7ON84dtJ7dm0,26248 +pip/_vendor/html5lib/filters/sanitizer.pyc,, +pip/_vendor/html5lib/filters/whitespace.py,sha256=8eWqZxd4UC4zlFGW6iyY6f-2uuT8pOCSALc3IZt7_t4,1214 +pip/_vendor/html5lib/filters/whitespace.pyc,, +pip/_vendor/html5lib/html5parser.py,sha256=g5g2ezkusHxhi7b23vK_-d6K6BfIJRbqIQmvQ9z4EgI,118963 +pip/_vendor/html5lib/html5parser.pyc,, +pip/_vendor/html5lib/serializer.py,sha256=yfcfBHse2wDs6ojxn-kieJjLT5s1ipilQJ0gL3-rJis,15758 +pip/_vendor/html5lib/serializer.pyc,, +pip/_vendor/html5lib/treeadapters/__init__.py,sha256=A0rY5gXIe4bJOiSGRO_j_tFhngRBO8QZPzPtPw5dFzo,679 +pip/_vendor/html5lib/treeadapters/__init__.pyc,, +pip/_vendor/html5lib/treeadapters/genshi.py,sha256=CH27pAsDKmu4ZGkAUrwty7u0KauGLCZRLPMzaO3M5vo,1715 +pip/_vendor/html5lib/treeadapters/genshi.pyc,, +pip/_vendor/html5lib/treeadapters/sax.py,sha256=BKS8woQTnKiqeffHsxChUqL4q2ZR_wb5fc9MJ3zQC8s,1776 +pip/_vendor/html5lib/treeadapters/sax.pyc,, +pip/_vendor/html5lib/treebuilders/__init__.py,sha256=AysSJyvPfikCMMsTVvaxwkgDieELD5dfR8FJIAuq7hY,3592 +pip/_vendor/html5lib/treebuilders/__init__.pyc,, +pip/_vendor/html5lib/treebuilders/base.py,sha256=wQGp5yy22TNG8tJ6aREe4UUeTR7A99dEz0BXVaedWb4,14579 +pip/_vendor/html5lib/treebuilders/base.pyc,, +pip/_vendor/html5lib/treebuilders/dom.py,sha256=SY3MsijXyzdNPc8aK5IQsupBoM8J67y56DgNtGvsb9g,8835 +pip/_vendor/html5lib/treebuilders/dom.pyc,, +pip/_vendor/html5lib/treebuilders/etree.py,sha256=aqIBOGj_dFYqBURIcTegGNBhAIJOw5iFDHb4jrkYH-8,12764 +pip/_vendor/html5lib/treebuilders/etree.pyc,, +pip/_vendor/html5lib/treebuilders/etree_lxml.py,sha256=9V0dXxbJYYq-Skgb5-_OL2NkVYpjioEb4CHajo0e9yI,14122 +pip/_vendor/html5lib/treebuilders/etree_lxml.pyc,, +pip/_vendor/html5lib/treewalkers/__init__.py,sha256=yhXxHpjlSqfQyUag3v8-vWjMPriFBU8YRAPNpDgBTn8,5714 +pip/_vendor/html5lib/treewalkers/__init__.pyc,, +pip/_vendor/html5lib/treewalkers/base.py,sha256=ouiOsuSzvI0KgzdWP8PlxIaSNs9falhbiinAEc_UIJY,7476 +pip/_vendor/html5lib/treewalkers/base.pyc,, +pip/_vendor/html5lib/treewalkers/dom.py,sha256=EHyFR8D8lYNnyDU9lx_IKigVJRyecUGua0mOi7HBukc,1413 +pip/_vendor/html5lib/treewalkers/dom.pyc,, +pip/_vendor/html5lib/treewalkers/etree.py,sha256=sz1o6mmE93NQ53qJFDO7HKyDtuwgK-Ay3qSFZPC6u00,4550 +pip/_vendor/html5lib/treewalkers/etree.pyc,, +pip/_vendor/html5lib/treewalkers/etree_lxml.py,sha256=sY6wfRshWTllu6n48TPWpKsQRPp-0CQrT0hj_AdzHSU,6309 +pip/_vendor/html5lib/treewalkers/etree_lxml.pyc,, +pip/_vendor/html5lib/treewalkers/genshi.py,sha256=4D2PECZ5n3ZN3qu3jMl9yY7B81jnQApBQSVlfaIuYbA,2309 +pip/_vendor/html5lib/treewalkers/genshi.pyc,, +pip/_vendor/idna/__init__.py,sha256=9Nt7xpyet3DmOrPUGooDdAwmHZZu1qUAy2EaJ93kGiQ,58 +pip/_vendor/idna/__init__.pyc,, +pip/_vendor/idna/codec.py,sha256=lvYb7yu7PhAqFaAIAdWcwgaWI2UmgseUua-1c0AsG0A,3299 +pip/_vendor/idna/codec.pyc,, +pip/_vendor/idna/compat.py,sha256=R-h29D-6mrnJzbXxymrWUW7iZUvy-26TQwZ0ij57i4U,232 +pip/_vendor/idna/compat.pyc,, +pip/_vendor/idna/core.py,sha256=JDCZZ_PLESqIgEbU8mPyoEufWwoOiIqygA17-QZIe3s,11733 +pip/_vendor/idna/core.pyc,, +pip/_vendor/idna/idnadata.py,sha256=HXaPFw6_YAJ0qppACPu0YLAULtRs3QovRM_CCZHGdY0,40899 +pip/_vendor/idna/idnadata.pyc,, +pip/_vendor/idna/intranges.py,sha256=TY1lpxZIQWEP6tNqjZkFA5hgoMWOj1OBmnUG8ihT87E,1749 +pip/_vendor/idna/intranges.pyc,, +pip/_vendor/idna/package_data.py,sha256=kIzeKKXEouXLR4srqwf9Q3zv-NffKSOz5aSDOJARPB0,21 +pip/_vendor/idna/package_data.pyc,, +pip/_vendor/idna/uts46data.py,sha256=oLyNZ1pBaiBlj9zFzLFRd_P7J8MkRcgDisjExZR_4MY,198292 +pip/_vendor/idna/uts46data.pyc,, +pip/_vendor/ipaddress.py,sha256=2OgbkeAD2rLkcXqbcvof3J5R7lRwjNLoBySyTkBtKnc,79852 +pip/_vendor/ipaddress.pyc,, +pip/_vendor/lockfile/__init__.py,sha256=Tqpz90DwKYfhPsfzVOJl84TL87pdFE5ePNHdXAxs4Tk,9371 +pip/_vendor/lockfile/__init__.pyc,, +pip/_vendor/lockfile/linklockfile.py,sha256=C7OH3H4GdK68u4FQgp8fkP2kO4fyUTSyj3X6blgfobc,2652 +pip/_vendor/lockfile/linklockfile.pyc,, +pip/_vendor/lockfile/mkdirlockfile.py,sha256=e3qgIL-etZMLsS-3ft19iW_8IQ360HNkGOqE3yBKsUw,3096 +pip/_vendor/lockfile/mkdirlockfile.pyc,, +pip/_vendor/lockfile/pidlockfile.py,sha256=ukH9uk6NFuxyVmG5QiWw4iKq3fT7MjqUguX95avYPIY,6090 +pip/_vendor/lockfile/pidlockfile.pyc,, +pip/_vendor/lockfile/sqlitelockfile.py,sha256=o2TMkMRY0iwn-iL1XMRRIFStMUkS4i3ajceeYNntKFg,5506 +pip/_vendor/lockfile/sqlitelockfile.pyc,, +pip/_vendor/lockfile/symlinklockfile.py,sha256=ABwXXmvTHvCl5viPblShL3PG-gGsLiT1roAMfDRwhi8,2616 +pip/_vendor/lockfile/symlinklockfile.pyc,, +pip/_vendor/msgpack/__init__.py,sha256=y0bk2YbzK6J2e0J_dyreN6nD7yM2IezT6m_tU2h-Mdg,1677 +pip/_vendor/msgpack/__init__.pyc,, +pip/_vendor/msgpack/_version.py,sha256=dN7wVIjbyuQIJ35B2o6gymQNDLPlj_7-uTfgCv7KErM,20 +pip/_vendor/msgpack/_version.pyc,, +pip/_vendor/msgpack/exceptions.py,sha256=lPkAi_u12NlFajDz4FELSHEdfU8hrR3zeTvKX8aQuz4,1056 +pip/_vendor/msgpack/exceptions.pyc,, +pip/_vendor/msgpack/fallback.py,sha256=h0ll8xnq12mI9PuQ9Qd_Ihtt08Sp8L0JqhG9KY8Vyjk,36411 +pip/_vendor/msgpack/fallback.pyc,, +pip/_vendor/packaging/__about__.py,sha256=Wg0-hNgTU2_lBZcGBh5pm1R9yroQ3rv-X0rig8KjA6o,744 +pip/_vendor/packaging/__about__.pyc,, +pip/_vendor/packaging/__init__.py,sha256=6enbp5XgRfjBjsI9-bn00HjHf5TH21PDMOKkJW8xw-w,562 +pip/_vendor/packaging/__init__.pyc,, +pip/_vendor/packaging/_compat.py,sha256=Ugdm-qcneSchW25JrtMIKgUxfEEBcCAz6WrEeXeqz9o,865 +pip/_vendor/packaging/_compat.pyc,, +pip/_vendor/packaging/_structures.py,sha256=pVd90XcXRGwpZRB_qdFuVEibhCHpX_bL5zYr9-N0mc8,1416 +pip/_vendor/packaging/_structures.pyc,, +pip/_vendor/packaging/markers.py,sha256=-QjvJkhSJBxBogO9J_EpPQudHaaLV3rgVYsBDqn-ZLc,8234 +pip/_vendor/packaging/markers.pyc,, +pip/_vendor/packaging/requirements.py,sha256=grcnFU8x7KD230JaFLXtWl3VClLuOmsOy4c-m55tOWs,4700 +pip/_vendor/packaging/requirements.pyc,, +pip/_vendor/packaging/specifiers.py,sha256=0ZzQpcUnvrQ6LjR-mQRLzMr8G6hdRv-mY0VSf_amFtI,27778 +pip/_vendor/packaging/specifiers.pyc,, +pip/_vendor/packaging/utils.py,sha256=VaTC0Ei7zO2xl9ARiWmz2YFLFt89PuuhLbAlXMyAGms,1520 +pip/_vendor/packaging/utils.pyc,, +pip/_vendor/packaging/version.py,sha256=Npdwnb8OHedj_2L86yiUqscujb7w_i5gmSK1PhOAFzg,11978 +pip/_vendor/packaging/version.pyc,, +pip/_vendor/pep517/__init__.py,sha256=nOY747zTld3oTdEetBG6DWxEcZXTeOQk0aHvbR-sa5w,84 +pip/_vendor/pep517/__init__.pyc,, +pip/_vendor/pep517/_in_process.py,sha256=xMY2kLutkjCti5WqTmKOLRRL3o8Ds_k-fObFyuMv1tk,6061 +pip/_vendor/pep517/_in_process.pyc,, +pip/_vendor/pep517/build.py,sha256=-n8PT-ugS1TdqoTUY1vatDQjrLtx48K_-Quu2MuQBiA,2699 +pip/_vendor/pep517/build.pyc,, +pip/_vendor/pep517/check.py,sha256=Lu7nMdYu1JVV58fE3hv-d_avTy5h0yO9LsIzAt82Clk,5885 +pip/_vendor/pep517/check.pyc,, +pip/_vendor/pep517/colorlog.py,sha256=Tk9AuYm_cLF3BKTBoSTJt9bRryn0aFojIQOwbfVUTxQ,4098 +pip/_vendor/pep517/colorlog.pyc,, +pip/_vendor/pep517/compat.py,sha256=4SFG4QN-cNj8ebSa0wV0HUtEEQWwmbok2a0uk1gYEOM,631 +pip/_vendor/pep517/compat.pyc,, +pip/_vendor/pep517/envbuild.py,sha256=9-u4KffexPMEm52rTaIjEOxsCAd2DMByxzv5H566QLw,5763 +pip/_vendor/pep517/envbuild.pyc,, +pip/_vendor/pep517/wrappers.py,sha256=9dZn-q7F5KyQKUJMie2uKwur2FG0CLXz_kLZzkJOhZc,5912 +pip/_vendor/pep517/wrappers.pyc,, +pip/_vendor/pkg_resources/__init__.py,sha256=ZVHzk7ZiFIIgE2RTJj8F7wwjdMGrAngMWtQo-rGNsm4,107910 +pip/_vendor/pkg_resources/__init__.pyc,, +pip/_vendor/pkg_resources/py31compat.py,sha256=CRk8fkiPRDLsbi5pZcKsHI__Pbmh_94L8mr9Qy9Ab2U,562 +pip/_vendor/pkg_resources/py31compat.pyc,, +pip/_vendor/progress/__init__.py,sha256=fcbQQXo5np2CoQyhSH5XprkicwLZNLePR3uIahznSO0,4857 +pip/_vendor/progress/__init__.pyc,, +pip/_vendor/progress/bar.py,sha256=QuDuVNcmXgpxtNtxO0Fq72xKigxABaVmxYGBw4J3Z_E,2854 +pip/_vendor/progress/bar.pyc,, +pip/_vendor/progress/counter.py,sha256=MznyBrvPWrOlGe4MZAlGUb9q3aODe6_aNYeAE_VNoYA,1372 +pip/_vendor/progress/counter.pyc,, +pip/_vendor/progress/spinner.py,sha256=k8JbDW94T0-WXuXfxZIFhdoNPYp3jfnpXqBnfRv5fGs,1380 +pip/_vendor/progress/spinner.pyc,, +pip/_vendor/pyparsing.py,sha256=sxGUe_YcWBB5ZoHec0m1iJtgcj4iKv_SGfdA_zVCYII,245385 +pip/_vendor/pyparsing.pyc,, +pip/_vendor/pytoml/__init__.py,sha256=W_SKx36Hsew-Fty36BOpreLm4uF4V_Tgkm_z9rIoOE8,127 +pip/_vendor/pytoml/__init__.pyc,, +pip/_vendor/pytoml/core.py,sha256=9CrLLTs1PdWjEwRnYzt_i4dhHcZvGxs_GsMlYAX3iY4,509 +pip/_vendor/pytoml/core.pyc,, +pip/_vendor/pytoml/parser.py,sha256=2tDXkldqPQJhyadXzL2rGhVbjUyBNeXXhaEfncHl2iQ,10326 +pip/_vendor/pytoml/parser.pyc,, +pip/_vendor/pytoml/test.py,sha256=2nQs4aX3XQEaaQCx6x_OJTS2Hb0_IiTZRqNOeDmLCzo,1021 +pip/_vendor/pytoml/test.pyc,, +pip/_vendor/pytoml/utils.py,sha256=JCLHx77Hu1R3F-bRgiROIiKyCzLwyebnp5P35cRJxWs,1665 +pip/_vendor/pytoml/utils.pyc,, +pip/_vendor/pytoml/writer.py,sha256=WbNNQg3sh_V-s3kt88LkNNbxEq6pPDdhRE-daJzArcI,3198 +pip/_vendor/pytoml/writer.pyc,, +pip/_vendor/requests/__init__.py,sha256=ZI8kbaEzLAxsqex3MmMPr-v24d1RfZbNAOY8fUxg2Xw,4074 +pip/_vendor/requests/__init__.pyc,, +pip/_vendor/requests/__version__.py,sha256=8KG3anaNCi-PEclPPOHJ_cv1udY_L1_njVr84gRZ9HM,436 +pip/_vendor/requests/__version__.pyc,, +pip/_vendor/requests/_internal_utils.py,sha256=Zx3PnEUccyfsB-ie11nZVAW8qClJy0gx1qNME7rgT18,1096 +pip/_vendor/requests/_internal_utils.pyc,, +pip/_vendor/requests/adapters.py,sha256=e-bmKEApNVqFdylxuMJJfiaHdlmS_zhWhIMEzlHvGuc,21548 +pip/_vendor/requests/adapters.pyc,, +pip/_vendor/requests/api.py,sha256=hWZgfD7OriCZFOnpeq0bv2pbXDl8YXfxDwAcU036qDs,6253 +pip/_vendor/requests/api.pyc,, +pip/_vendor/requests/auth.py,sha256=QB2-cSUj1jrvWZfPXttsZpyAacQgtKLVk14vQW9TpSE,10206 +pip/_vendor/requests/auth.pyc,, +pip/_vendor/requests/certs.py,sha256=nXRVq9DtGmv_1AYbwjTu9UrgAcdJv05ZvkNeaoLOZxY,465 +pip/_vendor/requests/certs.pyc,, +pip/_vendor/requests/compat.py,sha256=FZX4Q_EMKiMnhZpZ3g_gOsT-j2ca9ij2gehDx1cwYeo,1941 +pip/_vendor/requests/compat.pyc,, +pip/_vendor/requests/cookies.py,sha256=Y-bKX6TvW3FnYlE6Au0SXtVVWcaNdFvuAwQxw-G0iTI,18430 +pip/_vendor/requests/cookies.pyc,, +pip/_vendor/requests/exceptions.py,sha256=-mLam3TAx80V09EaH3H-ZxR61eAVuLRZ8zgBBSLjK44,3197 +pip/_vendor/requests/exceptions.pyc,, +pip/_vendor/requests/help.py,sha256=SJPVcoXeo7KfK4AxJN5eFVQCjr0im87tU2n7ubLsksU,3578 +pip/_vendor/requests/help.pyc,, +pip/_vendor/requests/hooks.py,sha256=QReGyy0bRcr5rkwCuObNakbYsc7EkiKeBwG4qHekr2Q,757 +pip/_vendor/requests/hooks.pyc,, +pip/_vendor/requests/models.py,sha256=6s-37iAqXVptq8z7U_LoH_pbIPrCQUm_Z8QuIGE29Q0,34275 +pip/_vendor/requests/models.pyc,, +pip/_vendor/requests/packages.py,sha256=njJmVifY4aSctuW3PP5EFRCxjEwMRDO6J_feG2dKWsI,695 +pip/_vendor/requests/packages.pyc,, +pip/_vendor/requests/sessions.py,sha256=DjbCotDW6xSAaBsjbW-L8l4N0UcwmrxVNgSrZgIjGWM,29332 +pip/_vendor/requests/sessions.pyc,, +pip/_vendor/requests/status_codes.py,sha256=XWlcpBjbCtq9sSqpH9_KKxgnLTf9Z__wCWolq21ySlg,4129 +pip/_vendor/requests/status_codes.pyc,, +pip/_vendor/requests/structures.py,sha256=zoP8qly2Jak5e89HwpqjN1z2diztI-_gaqts1raJJBc,2981 +pip/_vendor/requests/structures.pyc,, +pip/_vendor/requests/utils.py,sha256=LtPJ1db6mJff2TJSJWKi7rBpzjPS3mSOrjC9zRhoD3A,30049 +pip/_vendor/requests/utils.pyc,, +pip/_vendor/retrying.py,sha256=k3fflf5_Mm0XcIJYhB7Tj34bqCCPhUDkYbx1NvW2FPE,9972 +pip/_vendor/retrying.pyc,, +pip/_vendor/six.py,sha256=h9jch2pS86y4R36pKRS3LOYUCVFNIJMRwjZ4fJDtJ44,32452 +pip/_vendor/six.pyc,, +pip/_vendor/urllib3/__init__.py,sha256=EZviRQA_iuL_94EeJHY4JAArRXbRCkAzA0HH9iXZ15s,2722 +pip/_vendor/urllib3/__init__.pyc,, +pip/_vendor/urllib3/_collections.py,sha256=-CAKsDE-WdubAjlBSZLx7b0e7WKenaNGwWvGLDEF1TM,10746 +pip/_vendor/urllib3/_collections.pyc,, +pip/_vendor/urllib3/connection.py,sha256=KLFvknLgllcMkgJ-zUsFjCzOt9P03fDoIpTPz_vqXCw,13839 +pip/_vendor/urllib3/connection.pyc,, +pip/_vendor/urllib3/connectionpool.py,sha256=rgc_3D0VsD5VDxr4KzzA8Plee0Rmerm5WKb71FcxWu8,35097 +pip/_vendor/urllib3/connectionpool.pyc,, +pip/_vendor/urllib3/contrib/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +pip/_vendor/urllib3/contrib/__init__.pyc,, +pip/_vendor/urllib3/contrib/_appengine_environ.py,sha256=lhYXvB5_oGKSeurX7za3XhcGyERvNjXRQ3eJp2GmQ3M,717 +pip/_vendor/urllib3/contrib/_appengine_environ.pyc,, +pip/_vendor/urllib3/contrib/_securetransport/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +pip/_vendor/urllib3/contrib/_securetransport/__init__.pyc,, +pip/_vendor/urllib3/contrib/_securetransport/bindings.py,sha256=x2kLSh-ASZKsun0FxtraBuLVe3oHuth4YW6yZ5Vof-w,17560 +pip/_vendor/urllib3/contrib/_securetransport/bindings.pyc,, +pip/_vendor/urllib3/contrib/_securetransport/low_level.py,sha256=Umy5u-3Z957GirdapnicXVOpHaM4xdOZABJuJxfaeJA,12162 +pip/_vendor/urllib3/contrib/_securetransport/low_level.pyc,, +pip/_vendor/urllib3/contrib/appengine.py,sha256=VvDpkc5gf9dTXNxXmyG1mPdON_3DrYG_eW4uOqN98oQ,10938 +pip/_vendor/urllib3/contrib/appengine.pyc,, +pip/_vendor/urllib3/contrib/ntlmpool.py,sha256=5ZpMF7N9B6NEjVU-r-xjDOV_-hkNvsDoNc84J2yqauI,4459 +pip/_vendor/urllib3/contrib/ntlmpool.pyc,, +pip/_vendor/urllib3/contrib/pyopenssl.py,sha256=-kI_9y99Iwybv6Wy8IF8PugVl61BeMBEEqGwrDYNCuc,15823 +pip/_vendor/urllib3/contrib/pyopenssl.pyc,, +pip/_vendor/urllib3/contrib/securetransport.py,sha256=BqXSlChN9_hjCWgyN6JdcgvBUdc37QCCX4u3_8zE_9o,30309 +pip/_vendor/urllib3/contrib/securetransport.pyc,, +pip/_vendor/urllib3/contrib/socks.py,sha256=Iom0snbHkCuZbZ7Sle2Kueha1W0jYAJ0SyCOtePLaio,6391 +pip/_vendor/urllib3/contrib/socks.pyc,, +pip/_vendor/urllib3/exceptions.py,sha256=rFeIfBNKC8KJ61ux-MtJyJlEC9G9ggkmCeF751JwVR4,6604 +pip/_vendor/urllib3/exceptions.pyc,, +pip/_vendor/urllib3/fields.py,sha256=D_TE_SK15YatdbhWDMN0OE3X6UCJn1RTkANINCYOobE,5943 +pip/_vendor/urllib3/fields.pyc,, +pip/_vendor/urllib3/filepost.py,sha256=40CROlpRKVBpFUkD0R6wJf_PpvbcRQRFUu0OOQlFkKM,2436 +pip/_vendor/urllib3/filepost.pyc,, +pip/_vendor/urllib3/packages/__init__.py,sha256=nlChrGzkjCkmhCX9HrF_qHPUgosfsPQkVIJxiiLhk9g,109 +pip/_vendor/urllib3/packages/__init__.pyc,, +pip/_vendor/urllib3/packages/backports/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +pip/_vendor/urllib3/packages/backports/__init__.pyc,, +pip/_vendor/urllib3/packages/backports/makefile.py,sha256=so2z9BiNM8kh38Ve5tomQP_mp2_ubEqzdlCpLZKzzCI,1456 +pip/_vendor/urllib3/packages/backports/makefile.pyc,, +pip/_vendor/urllib3/packages/six.py,sha256=A6hdJZVjI3t_geebZ9BzUvwRrIXo0lfwzQlM2LcKyas,30098 +pip/_vendor/urllib3/packages/six.pyc,, +pip/_vendor/urllib3/packages/ssl_match_hostname/__init__.py,sha256=WBVbxQBojNAxfZwNavkox3BgJiMA9BJmm-_fwd0jD_o,688 +pip/_vendor/urllib3/packages/ssl_match_hostname/__init__.pyc,, +pip/_vendor/urllib3/packages/ssl_match_hostname/_implementation.py,sha256=E-9J-kAaUn76WMZ4PpzKUxM4C3yjY7mopOpbPIy3Dso,5700 +pip/_vendor/urllib3/packages/ssl_match_hostname/_implementation.pyc,, +pip/_vendor/urllib3/poolmanager.py,sha256=csE6Bh6L0FJ3iNOHk2z8KhMT8Eiq976b6pk8I6vrOC8,16853 +pip/_vendor/urllib3/poolmanager.pyc,, +pip/_vendor/urllib3/request.py,sha256=OfelFYzPnxGlU3amEz9uBLjCBOriwgJh4QC_aW9SF3U,5991 +pip/_vendor/urllib3/request.pyc,, +pip/_vendor/urllib3/response.py,sha256=ta1jp4B5PGBWzoAV1s48WLuHCRICQnK7F9m_kyK4Z8g,25609 +pip/_vendor/urllib3/response.pyc,, +pip/_vendor/urllib3/util/__init__.py,sha256=6Ran4oAVIy40Cu_oEPWnNV9bwF5rXx6G1DUZ7oehjPY,1044 +pip/_vendor/urllib3/util/__init__.pyc,, +pip/_vendor/urllib3/util/connection.py,sha256=-AyqcRTuNUHuo5ndtsU0Og_nMyCGATC-kYqOUdBHwIQ,4639 +pip/_vendor/urllib3/util/connection.pyc,, +pip/_vendor/urllib3/util/queue.py,sha256=myTX3JDHntglKQNBf3b6dasHH-uF-W59vzGSQiFdAfI,497 +pip/_vendor/urllib3/util/queue.pyc,, +pip/_vendor/urllib3/util/request.py,sha256=H5_lrHvtwl2U2BbT1UYN9HpruNc1gsNFlz2njQmhPrQ,3705 +pip/_vendor/urllib3/util/request.pyc,, +pip/_vendor/urllib3/util/response.py,sha256=028PNXDZhwBtnm2uXvnAHi_l9_AAGrAMH2Igh2AbgWg,2586 +pip/_vendor/urllib3/util/response.pyc,, +pip/_vendor/urllib3/util/retry.py,sha256=kFQTesNiwPp6ZeQo9VHeUO7b8qA-_l3BnErCAOEPo4Q,15105 +pip/_vendor/urllib3/util/retry.pyc,, +pip/_vendor/urllib3/util/ssl_.py,sha256=4qqBDM82bufhqqEd0b-99sObz95XmEVEXDVi5iAyCeE,13172 +pip/_vendor/urllib3/util/ssl_.pyc,, +pip/_vendor/urllib3/util/timeout.py,sha256=7lHNrgL5YH2cI1j-yZnzV_J8jBlRVdmFhQaNyM1_2b8,9757 +pip/_vendor/urllib3/util/timeout.pyc,, +pip/_vendor/urllib3/util/url.py,sha256=qCY_HHUXvo05wAsEERALgExtlgxLnAHSQ7ce1b-g3SM,6487 +pip/_vendor/urllib3/util/url.pyc,, +pip/_vendor/urllib3/util/wait.py,sha256=p4BZo_Ukp5JF0Dn6jro7cUfqIjnU6WFtuoA6poaV5Jk,5403 +pip/_vendor/urllib3/util/wait.pyc,, +pip/_vendor/webencodings/__init__.py,sha256=qOBJIuPy_4ByYH6W_bNgJF-qYQ2DoU-dKsDu5yRWCXg,10579 +pip/_vendor/webencodings/__init__.pyc,, +pip/_vendor/webencodings/labels.py,sha256=4AO_KxTddqGtrL9ns7kAPjb0CcN6xsCIxbK37HY9r3E,8979 +pip/_vendor/webencodings/labels.pyc,, +pip/_vendor/webencodings/mklabels.py,sha256=GYIeywnpaLnP0GSic8LFWgd0UVvO_l1Nc6YoF-87R_4,1305 +pip/_vendor/webencodings/mklabels.pyc,, +pip/_vendor/webencodings/tests.py,sha256=OtGLyjhNY1fvkW1GvLJ_FV9ZoqC9Anyjr7q3kxTbzNs,6563 +pip/_vendor/webencodings/tests.pyc,, +pip/_vendor/webencodings/x_user_defined.py,sha256=yOqWSdmpytGfUgh_Z6JYgDNhoc-BAHyyeeT15Fr42tM,4307 +pip/_vendor/webencodings/x_user_defined.pyc,, diff --git a/venv2/lib/python2.7/site-packages/pip-19.1.1.dist-info/WHEEL b/venv2/lib/python2.7/site-packages/pip-19.1.1.dist-info/WHEEL new file mode 100644 index 0000000..c8240f0 --- /dev/null +++ b/venv2/lib/python2.7/site-packages/pip-19.1.1.dist-info/WHEEL @@ -0,0 +1,6 @@ +Wheel-Version: 1.0 +Generator: bdist_wheel (0.33.1) +Root-Is-Purelib: true +Tag: py2-none-any +Tag: py3-none-any + diff --git a/venv2/lib/python2.7/site-packages/pip-19.1.1.dist-info/entry_points.txt b/venv2/lib/python2.7/site-packages/pip-19.1.1.dist-info/entry_points.txt new file mode 100644 index 0000000..f5809cb --- /dev/null +++ b/venv2/lib/python2.7/site-packages/pip-19.1.1.dist-info/entry_points.txt @@ -0,0 +1,5 @@ +[console_scripts] +pip = pip._internal:main +pip3 = pip._internal:main +pip3.7 = pip._internal:main + diff --git a/venv2/lib/python2.7/site-packages/pip-19.1.1.dist-info/top_level.txt b/venv2/lib/python2.7/site-packages/pip-19.1.1.dist-info/top_level.txt new file mode 100644 index 0000000..a1b589e --- /dev/null +++ b/venv2/lib/python2.7/site-packages/pip-19.1.1.dist-info/top_level.txt @@ -0,0 +1 @@ +pip diff --git a/venv2/lib/python2.7/site-packages/pip/__init__.py b/venv2/lib/python2.7/site-packages/pip/__init__.py new file mode 100644 index 0000000..5d05da3 --- /dev/null +++ b/venv2/lib/python2.7/site-packages/pip/__init__.py @@ -0,0 +1 @@ +__version__ = "19.1.1" diff --git a/venv2/lib/python2.7/site-packages/pip/__main__.py b/venv2/lib/python2.7/site-packages/pip/__main__.py new file mode 100644 index 0000000..0c223f8 --- /dev/null +++ b/venv2/lib/python2.7/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) + +from pip._internal import main as _main # isort:skip # noqa + +if __name__ == '__main__': + sys.exit(_main()) diff --git a/venv2/lib/python2.7/site-packages/pip/_internal/__init__.py b/venv2/lib/python2.7/site-packages/pip/_internal/__init__.py new file mode 100644 index 0000000..9c1637c --- /dev/null +++ b/venv2/lib/python2.7/site-packages/pip/_internal/__init__.py @@ -0,0 +1,78 @@ +#!/usr/bin/env python +from __future__ import absolute_import + +import locale +import logging +import os +import warnings + +import sys + +# 2016-06-17 barry@debian.org: urllib3 1.14 added optional support for socks, +# but if invoked (i.e. imported), it will issue a warning to stderr if socks +# isn't available. requests unconditionally imports urllib3's socks contrib +# module, triggering this warning. The warning breaks DEP-8 tests (because of +# the stderr output) and is just plain annoying in normal usage. I don't want +# to add socks as yet another dependency for pip, nor do I want to allow-stderr +# in the DEP-8 tests, so just suppress the warning. pdb tells me this has to +# be done before the import of pip.vcs. +from pip._vendor.urllib3.exceptions import DependencyWarning +warnings.filterwarnings("ignore", category=DependencyWarning) # noqa + +# We want to inject the use of SecureTransport as early as possible so that any +# references or sessions or what have you are ensured to have it, however we +# only want to do this in the case that we're running on macOS and the linked +# OpenSSL is too old to handle TLSv1.2 +try: + import ssl +except ImportError: + pass +else: + # Checks for OpenSSL 1.0.1 on MacOS + if sys.platform == "darwin" and ssl.OPENSSL_VERSION_NUMBER < 0x1000100f: + try: + from pip._vendor.urllib3.contrib import securetransport + except (ImportError, OSError): + pass + else: + securetransport.inject_into_urllib3() + +from pip._internal.cli.autocompletion import autocomplete +from pip._internal.cli.main_parser import parse_command +from pip._internal.commands import commands_dict +from pip._internal.exceptions import PipError +from pip._internal.utils import deprecation +from pip._internal.vcs import git, mercurial, subversion, bazaar # noqa +from pip._vendor.urllib3.exceptions import InsecureRequestWarning + +logger = logging.getLogger(__name__) + +# Hide the InsecureRequestWarning from urllib3 +warnings.filterwarnings("ignore", category=InsecureRequestWarning) + + +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 = parse_command(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._internal.utils.encoding.auto_decode + try: + locale.setlocale(locale.LC_ALL, '') + except locale.Error as e: + # setlocale can apparently crash if locale are uninitialized + logger.debug("Ignoring error %s when setting locale", e) + command = commands_dict[cmd_name](isolated=("--isolated" in cmd_args)) + return command.main(cmd_args) diff --git a/venv2/lib/python2.7/site-packages/pip/_internal/build_env.py b/venv2/lib/python2.7/site-packages/pip/_internal/build_env.py new file mode 100644 index 0000000..89830e7 --- /dev/null +++ b/venv2/lib/python2.7/site-packages/pip/_internal/build_env.py @@ -0,0 +1,215 @@ +"""Build Environment used for isolation during sdist building +""" + +import logging +import os +import sys +import textwrap +from collections import OrderedDict +from distutils.sysconfig import get_python_lib +from sysconfig import get_paths + +from pip._vendor.pkg_resources import Requirement, VersionConflict, WorkingSet + +from pip import __file__ as pip_location +from pip._internal.utils.misc import call_subprocess +from pip._internal.utils.temp_dir import TempDirectory +from pip._internal.utils.typing import MYPY_CHECK_RUNNING +from pip._internal.utils.ui import open_spinner + +if MYPY_CHECK_RUNNING: + from typing import Tuple, Set, Iterable, Optional, List + from pip._internal.index import PackageFinder + +logger = logging.getLogger(__name__) + + +class _Prefix: + + def __init__(self, path): + # type: (str) -> None + self.path = path + self.setup = False + self.bin_dir = get_paths( + 'nt' if os.name == 'nt' else 'posix_prefix', + vars={'base': path, 'platbase': path} + )['scripts'] + # Note: prefer distutils' sysconfig to get the + # library paths so PyPy is correctly supported. + purelib = get_python_lib(plat_specific=False, prefix=path) + platlib = get_python_lib(plat_specific=True, prefix=path) + if purelib == platlib: + self.lib_dirs = [purelib] + else: + self.lib_dirs = [purelib, platlib] + + +class BuildEnvironment(object): + """Creates and manages an isolated environment to install build deps + """ + + def __init__(self): + # type: () -> None + self._temp_dir = TempDirectory(kind="build-env") + self._temp_dir.create() + + self._prefixes = OrderedDict(( + (name, _Prefix(os.path.join(self._temp_dir.path, name))) + for name in ('normal', 'overlay') + )) + + self._bin_dirs = [] # type: List[str] + self._lib_dirs = [] # type: List[str] + for prefix in reversed(list(self._prefixes.values())): + self._bin_dirs.append(prefix.bin_dir) + self._lib_dirs.extend(prefix.lib_dirs) + + # Customize site to: + # - ensure .pth files are honored + # - prevent access to system site packages + system_sites = { + os.path.normcase(site) for site in ( + get_python_lib(plat_specific=False), + get_python_lib(plat_specific=True), + ) + } + self._site_dir = os.path.join(self._temp_dir.path, 'site') + if not os.path.exists(self._site_dir): + os.mkdir(self._site_dir) + with open(os.path.join(self._site_dir, 'sitecustomize.py'), 'w') as fp: + fp.write(textwrap.dedent( + ''' + import os, site, sys + + # First, drop system-sites related paths. + original_sys_path = sys.path[:] + known_paths = set() + for path in {system_sites!r}: + site.addsitedir(path, known_paths=known_paths) + system_paths = set( + os.path.normcase(path) + for path in sys.path[len(original_sys_path):] + ) + original_sys_path = [ + path for path in original_sys_path + if os.path.normcase(path) not in system_paths + ] + sys.path = original_sys_path + + # Second, add lib directories. + # ensuring .pth file are processed. + for path in {lib_dirs!r}: + assert not path in sys.path + site.addsitedir(path) + ''' + ).format(system_sites=system_sites, lib_dirs=self._lib_dirs)) + + def __enter__(self): + self._save_env = { + name: os.environ.get(name, None) + for name in ('PATH', 'PYTHONNOUSERSITE', 'PYTHONPATH') + } + + path = self._bin_dirs[:] + old_path = self._save_env['PATH'] + if old_path: + path.extend(old_path.split(os.pathsep)) + + pythonpath = [self._site_dir] + + os.environ.update({ + 'PATH': os.pathsep.join(path), + 'PYTHONNOUSERSITE': '1', + 'PYTHONPATH': os.pathsep.join(pythonpath), + }) + + def __exit__(self, exc_type, exc_val, exc_tb): + for varname, old_value in self._save_env.items(): + if old_value is None: + os.environ.pop(varname, None) + else: + os.environ[varname] = old_value + + def cleanup(self): + # type: () -> None + self._temp_dir.cleanup() + + def check_requirements(self, reqs): + # type: (Iterable[str]) -> Tuple[Set[Tuple[str, str]], Set[str]] + """Return 2 sets: + - conflicting requirements: set of (installed, wanted) reqs tuples + - missing requirements: set of reqs + """ + missing = set() + conflicting = set() + if reqs: + ws = WorkingSet(self._lib_dirs) + for req in reqs: + try: + if ws.find(Requirement.parse(req)) is None: + missing.add(req) + except VersionConflict as e: + conflicting.add((str(e.args[0].as_requirement()), + str(e.args[1]))) + return conflicting, missing + + def install_requirements( + self, + finder, # type: PackageFinder + requirements, # type: Iterable[str] + prefix_as_string, # type: str + message # type: Optional[str] + ): + # type: (...) -> None + prefix = self._prefixes[prefix_as_string] + assert not prefix.setup + prefix.setup = True + if not requirements: + return + args = [ + sys.executable, os.path.dirname(pip_location), 'install', + '--ignore-installed', '--no-user', '--prefix', prefix.path, + '--no-warn-script-location', + ] # type: List[str] + if logger.getEffectiveLevel() <= logging.DEBUG: + args.append('-v') + for format_control in ('no_binary', 'only_binary'): + formats = getattr(finder.format_control, format_control) + args.extend(('--' + format_control.replace('_', '-'), + ','.join(sorted(formats or {':none:'})))) + if finder.index_urls: + args.extend(['-i', finder.index_urls[0]]) + for extra_index in finder.index_urls[1:]: + args.extend(['--extra-index-url', extra_index]) + else: + args.append('--no-index') + for link in finder.find_links: + args.extend(['--find-links', link]) + for _, host, _ in finder.secure_origins: + args.extend(['--trusted-host', host]) + if finder.allow_all_prereleases: + args.append('--pre') + args.append('--') + args.extend(requirements) + with open_spinner(message) as spinner: + call_subprocess(args, spinner=spinner) + + +class NoOpBuildEnvironment(BuildEnvironment): + """A no-op drop-in replacement for BuildEnvironment + """ + + def __init__(self): + pass + + def __enter__(self): + pass + + def __exit__(self, exc_type, exc_val, exc_tb): + pass + + def cleanup(self): + pass + + def install_requirements(self, finder, requirements, prefix, message): + raise NotImplementedError() diff --git a/venv2/lib/python2.7/site-packages/pip/_internal/cache.py b/venv2/lib/python2.7/site-packages/pip/_internal/cache.py new file mode 100644 index 0000000..9379343 --- /dev/null +++ b/venv2/lib/python2.7/site-packages/pip/_internal/cache.py @@ -0,0 +1,224 @@ +"""Cache Management +""" + +import errno +import hashlib +import logging +import os + +from pip._vendor.packaging.utils import canonicalize_name + +from pip._internal.download import path_to_url +from pip._internal.models.link import Link +from pip._internal.utils.compat import expanduser +from pip._internal.utils.temp_dir import TempDirectory +from pip._internal.utils.typing import MYPY_CHECK_RUNNING +from pip._internal.wheel import InvalidWheelFilename, Wheel + +if MYPY_CHECK_RUNNING: + from typing import Optional, Set, List, Any + from pip._internal.index import FormatControl + +logger = logging.getLogger(__name__) + + +class Cache(object): + """An abstract class - provides cache directories for data from links + + + :param cache_dir: The root of the cache. + :param format_control: An object of FormatControl class to limit + binaries being read from the cache. + :param allowed_formats: which formats of files the cache should store. + ('binary' and 'source' are the only allowed values) + """ + + def __init__(self, cache_dir, format_control, allowed_formats): + # type: (str, FormatControl, Set[str]) -> None + super(Cache, self).__init__() + self.cache_dir = expanduser(cache_dir) if cache_dir else None + self.format_control = format_control + self.allowed_formats = allowed_formats + + _valid_formats = {"source", "binary"} + assert self.allowed_formats.union(_valid_formats) == _valid_formats + + def _get_cache_path_parts(self, link): + # type: (Link) -> List[str] + """Get parts of part that must be os.path.joined with cache_dir + """ + + # 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:]] + + return parts + + def _get_candidates(self, link, package_name): + # type: (Link, Optional[str]) -> List[Any] + can_not_cache = ( + not self.cache_dir or + not package_name or + not link + ) + if can_not_cache: + return [] + + canonical_name = canonicalize_name(package_name) + formats = self.format_control.get_allowed_formats( + canonical_name + ) + if not self.allowed_formats.intersection(formats): + return [] + + root = self.get_path_for_link(link) + try: + return os.listdir(root) + except OSError as err: + if err.errno in {errno.ENOENT, errno.ENOTDIR}: + return [] + raise + + def get_path_for_link(self, link): + # type: (Link) -> str + """Return a directory to store cached items in for link. + """ + raise NotImplementedError() + + def get(self, link, package_name): + # type: (Link, Optional[str]) -> Link + """Returns a link to a cached item if it exists, otherwise returns the + passed link. + """ + raise NotImplementedError() + + def _link_for_candidate(self, link, candidate): + # type: (Link, str) -> Link + root = self.get_path_for_link(link) + path = os.path.join(root, candidate) + + return Link(path_to_url(path)) + + def cleanup(self): + # type: () -> None + pass + + +class SimpleWheelCache(Cache): + """A cache of wheels for future installs. + """ + + def __init__(self, cache_dir, format_control): + # type: (str, FormatControl) -> None + super(SimpleWheelCache, self).__init__( + cache_dir, format_control, {"binary"} + ) + + def get_path_for_link(self, link): + # type: (Link) -> str + """Return a directory to store cached wheels 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 link: The link of the sdist for which this will cache wheels. + """ + parts = self._get_cache_path_parts(link) + + # Store wheels within the root cache_dir + return os.path.join(self.cache_dir, "wheels", *parts) + + def get(self, link, package_name): + # type: (Link, Optional[str]) -> Link + candidates = [] + + for wheel_name in self._get_candidates(link, package_name): + 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 + + return self._link_for_candidate(link, min(candidates)[1]) + + +class EphemWheelCache(SimpleWheelCache): + """A SimpleWheelCache that creates it's own temporary cache directory + """ + + def __init__(self, format_control): + # type: (FormatControl) -> None + self._temp_dir = TempDirectory(kind="ephem-wheel-cache") + self._temp_dir.create() + + super(EphemWheelCache, self).__init__( + self._temp_dir.path, format_control + ) + + def cleanup(self): + # type: () -> None + self._temp_dir.cleanup() + + +class WheelCache(Cache): + """Wraps EphemWheelCache and SimpleWheelCache into a single Cache + + This Cache allows for gracefully degradation, using the ephem wheel cache + when a certain link is not found in the simple wheel cache first. + """ + + def __init__(self, cache_dir, format_control): + # type: (str, FormatControl) -> None + super(WheelCache, self).__init__( + cache_dir, format_control, {'binary'} + ) + self._wheel_cache = SimpleWheelCache(cache_dir, format_control) + self._ephem_cache = EphemWheelCache(format_control) + + def get_path_for_link(self, link): + # type: (Link) -> str + return self._wheel_cache.get_path_for_link(link) + + def get_ephem_path_for_link(self, link): + # type: (Link) -> str + return self._ephem_cache.get_path_for_link(link) + + def get(self, link, package_name): + # type: (Link, Optional[str]) -> Link + retval = self._wheel_cache.get(link, package_name) + if retval is link: + retval = self._ephem_cache.get(link, package_name) + return retval + + def cleanup(self): + # type: () -> None + self._wheel_cache.cleanup() + self._ephem_cache.cleanup() diff --git a/venv2/lib/python2.7/site-packages/pip/_internal/cli/__init__.py b/venv2/lib/python2.7/site-packages/pip/_internal/cli/__init__.py new file mode 100644 index 0000000..e589bb9 --- /dev/null +++ b/venv2/lib/python2.7/site-packages/pip/_internal/cli/__init__.py @@ -0,0 +1,4 @@ +"""Subpackage containing all of pip's command line interface related code +""" + +# This file intentionally does not import submodules diff --git a/venv2/lib/python2.7/site-packages/pip/_internal/cli/autocompletion.py b/venv2/lib/python2.7/site-packages/pip/_internal/cli/autocompletion.py new file mode 100644 index 0000000..0a04199 --- /dev/null +++ b/venv2/lib/python2.7/site-packages/pip/_internal/cli/autocompletion.py @@ -0,0 +1,152 @@ +"""Logic that powers autocompletion installed by ``pip completion``. +""" + +import optparse +import os +import sys + +from pip._internal.cli.main_parser import create_main_parser +from pip._internal.commands import commands_dict, get_summaries +from pip._internal.utils.misc import get_installed_distributions + + +def autocomplete(): + """Entry Point for completion of main and subcommand options. + """ + # 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 show and uninstall + should_list_installed = ( + subcommand_name in ['show', 'uninstall'] and + not current.startswith('-') + ) + if should_list_installed: + 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]() + + for opt in subcommand.parser.option_list_all: + if opt.help != optparse.SUPPRESS_HELP: + for opt_str in opt._long_opts + opt._short_opts: + options.append((opt_str, opt.nargs)) + + # 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)] + # get completion type given cwords and available subcommand options + completion_type = get_path_completion_type( + cwords, cword, subcommand.parser.option_list_all, + ) + # get completion files and directories if ``completion_type`` is + # ````, ```` or ```` + if completion_type: + options = auto_complete_paths(current, completion_type) + options = ((opt, 0) for opt in options) + for option in options: + opt_label = option[0] + # append '=' to options which require args + if option[1] and option[0][:2] == "--": + opt_label += '=' + print(opt_label) + else: + # show main parser options only when necessary + + 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) + if current.startswith('-'): + for opt in opts: + if opt.help != optparse.SUPPRESS_HELP: + subcommands += opt._long_opts + opt._short_opts + else: + # get completion type given cwords and all available options + completion_type = get_path_completion_type(cwords, cword, opts) + if completion_type: + subcommands = auto_complete_paths(current, completion_type) + + print(' '.join([x for x in subcommands if x.startswith(current)])) + sys.exit(1) + + +def get_path_completion_type(cwords, cword, opts): + """Get the type of path completion (``file``, ``dir``, ``path`` or None) + + :param cwords: same as the environmental variable ``COMP_WORDS`` + :param cword: same as the environmental variable ``COMP_CWORD`` + :param opts: The available options to check + :return: path completion type (``file``, ``dir``, ``path`` or None) + """ + if cword < 2 or not cwords[cword - 2].startswith('-'): + return + for opt in opts: + if opt.help == optparse.SUPPRESS_HELP: + continue + for o in str(opt).split('/'): + if cwords[cword - 2].split('=')[0] == o: + if not opt.metavar or any( + x in ('path', 'file', 'dir') + for x in opt.metavar.split('/')): + return opt.metavar + + +def auto_complete_paths(current, completion_type): + """If ``completion_type`` is ``file`` or ``path``, list all regular files + and directories starting with ``current``; otherwise only list directories + starting with ``current``. + + :param current: The word to be completed + :param completion_type: path completion type(`file`, `path` or `dir`)i + :return: A generator of regular files and/or directories + """ + directory, filename = os.path.split(current) + current_path = os.path.abspath(directory) + # Don't complete paths if they can't be accessed + if not os.access(current_path, os.R_OK): + return + filename = os.path.normcase(filename) + # list all files that start with ``filename`` + file_list = (x for x in os.listdir(current_path) + if os.path.normcase(x).startswith(filename)) + for f in file_list: + opt = os.path.join(current_path, f) + comp_file = os.path.normcase(os.path.join(directory, f)) + # complete regular files when there is not ```` after option + # complete directories when there is ````, ```` or + # ````after option + if completion_type != 'dir' and os.path.isfile(opt): + yield comp_file + elif os.path.isdir(opt): + yield os.path.join(comp_file, '') diff --git a/venv2/lib/python2.7/site-packages/pip/_internal/cli/base_command.py b/venv2/lib/python2.7/site-packages/pip/_internal/cli/base_command.py new file mode 100644 index 0000000..f6108c9 --- /dev/null +++ b/venv2/lib/python2.7/site-packages/pip/_internal/cli/base_command.py @@ -0,0 +1,340 @@ +"""Base Command class, and related routines""" +from __future__ import absolute_import, print_function + +import logging +import logging.config +import optparse +import os +import platform +import sys +import traceback + +from pip._internal.cli import cmdoptions +from pip._internal.cli.parser import ( + ConfigOptionParser, UpdatingDefaultsHelpFormatter, +) +from pip._internal.cli.status_codes import ( + ERROR, PREVIOUS_BUILD_DIR_ERROR, SUCCESS, UNKNOWN_ERROR, + VIRTUALENV_NOT_FOUND, +) +from pip._internal.download import PipSession +from pip._internal.exceptions import ( + BadCommand, CommandError, InstallationError, PreviousBuildDirError, + UninstallationError, +) +from pip._internal.index import PackageFinder +from pip._internal.locations import running_under_virtualenv +from pip._internal.req.constructors import ( + install_req_from_editable, install_req_from_line, +) +from pip._internal.req.req_file import parse_requirements +from pip._internal.utils.deprecation import deprecated +from pip._internal.utils.logging import BrokenStdoutLoggingError, setup_logging +from pip._internal.utils.misc import ( + get_prog, normalize_path, redact_password_from_url, +) +from pip._internal.utils.outdated import pip_version_check +from pip._internal.utils.typing import MYPY_CHECK_RUNNING + +if MYPY_CHECK_RUNNING: + from typing import Optional, List, Tuple, Any + from optparse import Values + from pip._internal.cache import WheelCache + from pip._internal.req.req_set import RequirementSet + +__all__ = ['Command'] + +logger = logging.getLogger(__name__) + + +class Command(object): + name = None # type: Optional[str] + usage = None # type: Optional[str] + ignore_require_venv = False # type: bool + + def __init__(self, isolated=False): + # type: (bool) -> None + 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 run(self, options, args): + # type: (Values, List[Any]) -> Any + raise NotImplementedError + + def _build_session(self, options, retries=None, timeout=None): + # type: (Values, Optional[int], Optional[int]) -> PipSession + 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): + # type: (List[str]) -> Tuple + # factored out for testability + return self.parser.parse_args(args) + + def main(self, args): + # type: (List[str]) -> int + options, args = self.parse_args(args) + + # Set verbosity so that it can be used elsewhere. + self.verbosity = options.verbose - options.quiet + + level_number = setup_logging( + verbosity=self.verbosity, + no_color=options.no_color, + user_log_file=options.log, + ) + + if sys.version_info[:2] == (3, 4): + deprecated( + "Python 3.4 support has been deprecated. pip 19.1 will be the " + "last one supporting it. Please upgrade your Python as Python " + "3.4 won't be maintained after March 2019 (cf PEP 429).", + replacement=None, + gone_in='19.2', + ) + elif sys.version_info[:2] == (2, 7): + message = ( + "A future version of pip will drop support for Python 2.7." + ) + if platform.python_implementation() == "CPython": + message = ( + "Python 2.7 will reach the end of its life on January " + "1st, 2020. Please upgrade your Python as Python 2.7 " + "won't be maintained after that date. " + ) + message + deprecated(message, replacement=None, gone_in=None) + + # TODO: Try to get these passing down from the command? + # without resorting to os.environ to hold these. + # This also affects isolated builds and it should. + + 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 and not self.ignore_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('%s', exc) + logger.debug('Exception information:', exc_info=True) + + return ERROR + except BrokenStdoutLoggingError: + # Bypass our logger and write any remaining messages to stderr + # because stdout no longer works. + print('ERROR: Pipe to stdout was broken', file=sys.stderr) + if level_number <= logging.DEBUG: + traceback.print_exc(file=sys.stderr) + + return ERROR + except KeyboardInterrupt: + logger.critical('Operation cancelled by user') + logger.debug('Exception information:', exc_info=True) + + return ERROR + except BaseException: + logger.critical('Exception:', exc_info=True) + + return UNKNOWN_ERROR + finally: + allow_version_check = ( + # Does this command have the index_group options? + hasattr(options, "no_index") and + # Is this command allowed to perform this check? + not (options.disable_pip_version_check or options.no_index) + ) + # Check if we're using the latest version of pip available + if allow_version_check: + session = self._build_session( + options, + retries=0, + timeout=min(5, options.timeout) + ) + with session: + pip_version_check(session, options) + + # Shutdown the logging module + logging.shutdown() + + return SUCCESS + + +class RequirementCommand(Command): + + @staticmethod + def populate_requirement_set(requirement_set, # type: RequirementSet + args, # type: List[str] + options, # type: Values + finder, # type: PackageFinder + session, # type: PipSession + name, # type: str + wheel_cache # type: Optional[WheelCache] + ): + # type: (...) -> None + """ + Marshal cmd line args into a requirement set. + """ + # NOTE: As a side-effect, options.require_hashes and + # requirement_set.require_hashes may be updated + + for filename in options.constraints: + for req_to_add in parse_requirements( + filename, + constraint=True, finder=finder, options=options, + session=session, wheel_cache=wheel_cache): + req_to_add.is_direct = True + requirement_set.add_requirement(req_to_add) + + for req in args: + req_to_add = install_req_from_line( + req, None, isolated=options.isolated_mode, + use_pep517=options.use_pep517, + wheel_cache=wheel_cache + ) + req_to_add.is_direct = True + requirement_set.add_requirement(req_to_add) + + for req in options.editables: + req_to_add = install_req_from_editable( + req, + isolated=options.isolated_mode, + use_pep517=options.use_pep517, + wheel_cache=wheel_cache + ) + req_to_add.is_direct = True + requirement_set.add_requirement(req_to_add) + + for filename in options.requirements: + for req_to_add in parse_requirements( + filename, + finder=finder, options=options, session=session, + wheel_cache=wheel_cache, + use_pep517=options.use_pep517): + req_to_add.is_direct = True + requirement_set.add_requirement(req_to_add) + # 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 options.requirements): + opts = {'name': name} + if options.find_links: + raise CommandError( + '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: + raise CommandError( + 'You must give at least one requirement to %(name)s ' + '(see "pip help %(name)s")' % opts) + + def _build_package_finder( + self, + options, # type: Values + session, # type: PipSession + platform=None, # type: Optional[str] + python_versions=None, # type: Optional[List[str]] + abi=None, # type: Optional[str] + implementation=None # type: Optional[str] + ): + # type: (...) -> PackageFinder + """ + Create a package finder appropriate to this requirement command. + """ + index_urls = [options.index_url] + options.extra_index_urls + if options.no_index: + logger.debug( + 'Ignoring indexes: %s', + ','.join(redact_password_from_url(url) for url in 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, + session=session, + platform=platform, + versions=python_versions, + abi=abi, + implementation=implementation, + prefer_binary=options.prefer_binary, + ) diff --git a/venv2/lib/python2.7/site-packages/pip/_internal/cli/cmdoptions.py b/venv2/lib/python2.7/site-packages/pip/_internal/cli/cmdoptions.py new file mode 100644 index 0000000..a09e38f --- /dev/null +++ b/venv2/lib/python2.7/site-packages/pip/_internal/cli/cmdoptions.py @@ -0,0 +1,809 @@ +""" +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 + +import textwrap +import warnings +from distutils.util import strtobool +from functools import partial +from optparse import SUPPRESS_HELP, Option, OptionGroup + +from pip._internal.exceptions import CommandError +from pip._internal.locations import USER_CACHE_DIR, src_prefix +from pip._internal.models.format_control import FormatControl +from pip._internal.models.index import PyPI +from pip._internal.utils.hashes import STRONG_HASHES +from pip._internal.utils.typing import MYPY_CHECK_RUNNING +from pip._internal.utils.ui import BAR_TYPES + +if MYPY_CHECK_RUNNING: + from typing import Any, Callable, Dict, Optional + from optparse import OptionParser, Values + from pip._internal.cli.parser import ConfigOptionParser + + +def raise_option_error(parser, option, msg): + """ + Raise an option parsing error using parser.error(). + + Args: + parser: an OptionParser instance. + option: an Option instance. + msg: the error text. + """ + msg = '{} error: {}'.format(option, msg) + msg = textwrap.fill(' '.join(msg.split())) + parser.error(msg) + + +def make_option_group(group, parser): + # type: (Dict[str, Any], ConfigOptionParser) -> OptionGroup + """ + 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 check_install_build_global(options, check_options=None): + # type: (Values, Optional[Values]) -> 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 + control.disallow_binaries() + warnings.warn( + 'Disabling all use of wheels due to the use of --build-options ' + '/ --global-options / --install-options.', stacklevel=2, + ) + + +def check_dist_restriction(options, check_target=False): + # type: (Values, bool) -> None + """Function for determining if custom platform options are allowed. + + :param options: The OptionParser options. + :param check_target: Whether or not to check if --target is being used. + """ + dist_restriction_set = any([ + options.python_version, + options.platform, + options.abi, + options.implementation, + ]) + + binary_only = FormatControl(set(), {':all:'}) + sdist_dependencies_allowed = ( + options.format_control != binary_only and + not options.ignore_dependencies + ) + + # Installations or downloads using dist restrictions must not combine + # source distributions and dist-specific wheels, as they are not + # guaranteed to be locally compatible. + if dist_restriction_set and sdist_dependencies_allowed: + raise CommandError( + "When restricting platform and interpreter constraints using " + "--python-version, --platform, --abi, or --implementation, " + "either --no-deps must be set, or --only-binary=:all: must be " + "set and --no-binary must not be set (or must be set to " + ":none:)." + ) + + if check_target: + if dist_restriction_set and not options.target_dir: + raise CommandError( + "Can not use any platform or abi specific options unless " + "installing via '--target'" + ) + + +########### +# options # +########### + +help_ = partial( + Option, + '-h', '--help', + dest='help', + action='help', + help='Show help.', +) # type: Callable[..., Option] + +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." + ), +) # type: Callable[..., Option] + +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 +) # type: Callable[..., Option] + +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.' +) # type: Callable[..., Option] + +no_color = partial( + Option, + '--no-color', + dest='no_color', + action='store_true', + default=False, + help="Suppress colored output", +) # type: Callable[..., Option] + +version = partial( + Option, + '-V', '--version', + dest='version', + action='store_true', + help='Show version and exit.', +) # type: Callable[..., Option] + +quiet = partial( + Option, + '-q', '--quiet', + dest='quiet', + action='count', + default=0, + help=( + 'Give less output. Option is additive, and can be used up to 3' + ' times (corresponding to WARNING, ERROR, and CRITICAL logging' + ' levels).' + ), +) # type: Callable[..., Option] + +progress_bar = partial( + Option, + '--progress-bar', + dest='progress_bar', + type='choice', + choices=list(BAR_TYPES.keys()), + default='on', + help=( + 'Specify type of progress to be displayed [' + + '|'.join(BAR_TYPES.keys()) + '] (default: %default)' + ), +) # type: Callable[..., Option] + +log = partial( + Option, + "--log", "--log-file", "--local-log", + dest="log", + metavar="path", + help="Path to a verbose appending log." +) # type: Callable[..., Option] + +no_input = partial( + Option, + # Don't ask for input + '--no-input', + dest='no_input', + action='store_true', + default=False, + help=SUPPRESS_HELP +) # type: Callable[..., Option] + +proxy = partial( + Option, + '--proxy', + dest='proxy', + type='str', + default='', + help="Specify a proxy in the form [user:passwd@]proxy.server:port." +) # type: Callable[..., Option] + +retries = partial( + Option, + '--retries', + dest='retries', + type='int', + default=5, + help="Maximum number of retries each connection should attempt " + "(default %default times).", +) # type: Callable[..., Option] + +timeout = partial( + Option, + '--timeout', '--default-timeout', + metavar='sec', + dest='timeout', + type='float', + default=15, + help='Set the socket timeout (default %default seconds).', +) # type: Callable[..., Option] + +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, +) # type: Callable[..., Option] + + +def exists_action(): + # type: () -> Option + return Option( + # Option when path already exist + '--exists-action', + dest='exists_action', + type='choice', + choices=['s', 'i', 'w', 'b', 'a'], + default=[], + action='append', + metavar='action', + help="Default action when a path already exists: " + "(s)witch, (i)gnore, (w)ipe, (b)ackup, (a)bort.", + ) + + +cert = partial( + Option, + '--cert', + dest='cert', + type='str', + metavar='path', + help="Path to alternate CA bundle.", +) # type: Callable[..., Option] + +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.", +) # type: Callable[..., Option] + +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). " + "This should point to a repository compliant with PEP 503 " + "(the simple repository API) or a local directory laid out " + "in the same format.", +) # type: Callable[..., Option] + + +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. Should follow the same rules as " + "--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).', +) # type: Callable[..., Option] + + +def find_links(): + # type: () -> Option + 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 trusted_host(): + # type: () -> Option + 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.", + ) + + +def constraints(): + # type: () -> Option + 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(): + # type: () -> Option + 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(): + # type: () -> Option + 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".' +) # type: Callable[..., Option] + + +def _get_format_control(values, option): + # type: (Values, Option) -> Any + """Get a format_control object.""" + return getattr(values, option.dest) + + +def _handle_no_binary(option, opt_str, value, parser): + # type: (Option, str, str, OptionParser) -> None + existing = _get_format_control(parser.values, option) + FormatControl.handle_mutual_excludes( + value, existing.no_binary, existing.only_binary, + ) + + +def _handle_only_binary(option, opt_str, value, parser): + # type: (Option, str, str, OptionParser) -> None + existing = _get_format_control(parser.values, option) + FormatControl.handle_mutual_excludes( + value, existing.only_binary, existing.no_binary, + ) + + +def no_binary(): + # type: () -> Option + format_control = FormatControl(set(), set()) + return Option( + "--no-binary", dest="format_control", action="callback", + callback=_handle_no_binary, type="str", + default=format_control, + 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(): + # type: () -> Option + format_control = FormatControl(set(), set()) + return Option( + "--only-binary", dest="format_control", action="callback", + callback=_handle_only_binary, type="str", + default=format_control, + 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.", + ) + + +platform = partial( + Option, + '--platform', + dest='platform', + metavar='platform', + default=None, + help=("Only use wheels compatible with . " + "Defaults to the platform of the running system."), +) # type: Callable[..., Option] + + +python_version = partial( + Option, + '--python-version', + dest='python_version', + metavar='python_version', + default=None, + help=("Only use wheels compatible with Python " + "interpreter version . If not specified, then the " + "current system interpreter minor version is used. A major " + "version (e.g. '2') can be specified to match all " + "minor revs of that major version. A minor version " + "(e.g. '34') can also be specified."), +) # type: Callable[..., Option] + + +implementation = partial( + Option, + '--implementation', + dest='implementation', + metavar='implementation', + default=None, + help=("Only use wheels compatible with Python " + "implementation , e.g. 'pp', 'jy', 'cp', " + " or 'ip'. If not specified, then the current " + "interpreter implementation is used. Use 'py' to force " + "implementation-agnostic wheels."), +) # type: Callable[..., Option] + + +abi = partial( + Option, + '--abi', + dest='abi', + metavar='abi', + default=None, + help=("Only use wheels compatible with Python " + "abi , e.g. 'pypy_41'. If not specified, then the " + "current interpreter abi tag is used. Generally " + "you will need to specify --implementation, " + "--platform, and --python-version when using " + "this option."), +) # type: Callable[..., Option] + + +def prefer_binary(): + # type: () -> Option + return Option( + "--prefer-binary", + dest="prefer_binary", + action="store_true", + default=False, + help="Prefer older binary packages over newer source packages." + ) + + +cache_dir = partial( + Option, + "--cache-dir", + dest="cache_dir", + default=USER_CACHE_DIR, + metavar="dir", + help="Store the cache data in ." +) # type: Callable[..., Option] + + +def no_cache_dir_callback(option, opt, value, parser): + """ + Process a value provided for the --no-cache-dir option. + + This is an optparse.Option callback for the --no-cache-dir option. + """ + # The value argument will be None if --no-cache-dir is passed via the + # command-line, since the option doesn't accept arguments. However, + # the value can be non-None if the option is triggered e.g. by an + # environment variable, like PIP_NO_CACHE_DIR=true. + if value is not None: + # Then parse the string value to get argument error-checking. + try: + strtobool(value) + except ValueError as exc: + raise_option_error(parser, option=option, msg=str(exc)) + + # Originally, setting PIP_NO_CACHE_DIR to a value that strtobool() + # converted to 0 (like "false" or "no") caused cache_dir to be disabled + # rather than enabled (logic would say the latter). Thus, we disable + # the cache directory not just on values that parse to True, but (for + # backwards compatibility reasons) also on values that parse to False. + # In other words, always set it to False if the option is provided in + # some (valid) form. + parser.values.cache_dir = False + + +no_cache = partial( + Option, + "--no-cache-dir", + dest="cache_dir", + action="callback", + callback=no_cache_dir_callback, + help="Disable the cache.", +) # type: Callable[..., Option] + +no_deps = partial( + Option, + '--no-deps', '--no-dependencies', + dest='ignore_dependencies', + action='store_true', + default=False, + help="Don't install package dependencies.", +) # type: Callable[..., Option] + +build_dir = partial( + Option, + '-b', '--build', '--build-dir', '--build-directory', + dest='build_dir', + metavar='dir', + help='Directory to unpack packages into and build in. Note that ' + 'an initial build still takes place in a temporary directory. ' + 'The location of temporary directories can be controlled by setting ' + 'the TMPDIR environment variable (TEMP on Windows) appropriately. ' + 'When passed, build directories are not cleaned in case of failures.' +) # type: Callable[..., Option] + +ignore_requires_python = partial( + Option, + '--ignore-requires-python', + dest='ignore_requires_python', + action='store_true', + help='Ignore the Requires-Python information.' +) # type: Callable[..., Option] + +no_build_isolation = partial( + Option, + '--no-build-isolation', + dest='build_isolation', + action='store_false', + default=True, + help='Disable isolation when building a modern source distribution. ' + 'Build dependencies specified by PEP 518 must be already installed ' + 'if this option is used.' +) # type: Callable[..., Option] + + +def no_use_pep517_callback(option, opt, value, parser): + """ + Process a value provided for the --no-use-pep517 option. + + This is an optparse.Option callback for the no_use_pep517 option. + """ + # Since --no-use-pep517 doesn't accept arguments, the value argument + # will be None if --no-use-pep517 is passed via the command-line. + # However, the value can be non-None if the option is triggered e.g. + # by an environment variable, for example "PIP_NO_USE_PEP517=true". + if value is not None: + msg = """A value was passed for --no-use-pep517, + probably using either the PIP_NO_USE_PEP517 environment variable + or the "no-use-pep517" config file option. Use an appropriate value + of the PIP_USE_PEP517 environment variable or the "use-pep517" + config file option instead. + """ + raise_option_error(parser, option=option, msg=msg) + + # Otherwise, --no-use-pep517 was passed via the command-line. + parser.values.use_pep517 = False + + +use_pep517 = partial( + Option, + '--use-pep517', + dest='use_pep517', + action='store_true', + default=None, + help='Use PEP 517 for building source distributions ' + '(use --no-use-pep517 to force legacy behaviour).' +) # type: Any + +no_use_pep517 = partial( + Option, + '--no-use-pep517', + dest='use_pep517', + action='callback', + callback=no_use_pep517_callback, + default=None, + help=SUPPRESS_HELP +) # type: Any + +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.", +) # type: Callable[..., Option] + +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.", +) # type: Callable[..., Option] + +no_clean = partial( + Option, + '--no-clean', + action='store_true', + default=False, + help="Don't clean up build directories." +) # type: Callable[..., Option] + +pre = partial( + Option, + '--pre', + action='store_true', + default=False, + help="Include pre-release and development versions. By default, " + "pip only finds stable versions.", +) # type: Callable[..., Option] + +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.", +) # type: Callable[..., Option] + + +# Deprecated, Remove later +always_unzip = partial( + Option, + '-Z', '--always-unzip', + dest='always_unzip', + action='store_true', + help=SUPPRESS_HELP, +) # type: Callable[..., Option] + + +def _merge_hash(option, opt_str, value, parser): + # type: (Option, str, str, OptionParser) -> None + """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...', +) # type: Callable[..., Option] + + +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.', +) # type: Callable[..., Option] + + +########## +# groups # +########## + +general_group = { + 'name': 'General Options', + 'options': [ + help_, + isolated_mode, + require_virtualenv, + verbose, + version, + quiet, + log, + no_input, + proxy, + retries, + timeout, + skip_requirements_regex, + exists_action, + trusted_host, + cert, + client_cert, + cache_dir, + no_cache, + disable_pip_version_check, + no_color, + ] +} # type: Dict[str, Any] + +index_group = { + 'name': 'Package Index Options', + 'options': [ + index_url, + extra_index_url, + no_index, + find_links, + ] +} # type: Dict[str, Any] diff --git a/venv2/lib/python2.7/site-packages/pip/_internal/cli/main_parser.py b/venv2/lib/python2.7/site-packages/pip/_internal/cli/main_parser.py new file mode 100644 index 0000000..767f35d --- /dev/null +++ b/venv2/lib/python2.7/site-packages/pip/_internal/cli/main_parser.py @@ -0,0 +1,104 @@ +"""A single place for constructing and exposing the main parser +""" + +import os +import sys + +from pip import __version__ +from pip._internal.cli import cmdoptions +from pip._internal.cli.parser import ( + ConfigOptionParser, UpdatingDefaultsHelpFormatter, +) +from pip._internal.commands import ( + commands_dict, get_similar_commands, get_summaries, +) +from pip._internal.exceptions import CommandError +from pip._internal.utils.misc import get_prog +from pip._internal.utils.typing import MYPY_CHECK_RUNNING + +if MYPY_CHECK_RUNNING: + from typing import Tuple, List + + +__all__ = ["create_main_parser", "parse_command"] + + +def create_main_parser(): + # type: () -> ConfigOptionParser + """Creates and returns the main parser for pip's CLI + """ + + 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.abspath(os.path.join( + os.path.dirname(__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) + + # so the help formatter knows + parser.main = True # type: ignore + + # 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 parse_command(args): + # type: (List[str]) -> Tuple[str, List[str]] + 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) # type: ignore + 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 diff --git a/venv2/lib/python2.7/site-packages/pip/_internal/cli/parser.py b/venv2/lib/python2.7/site-packages/pip/_internal/cli/parser.py new file mode 100644 index 0000000..e1eaac4 --- /dev/null +++ b/venv2/lib/python2.7/site-packages/pip/_internal/cli/parser.py @@ -0,0 +1,261 @@ +"""Base option parser setup""" +from __future__ import absolute_import + +import logging +import optparse +import sys +import textwrap +from distutils.util import strtobool + +from pip._vendor.six import string_types + +from pip._internal.cli.status_codes import UNKNOWN_ERROR +from pip._internal.configuration import Configuration, ConfigurationError +from pip._internal.utils.compat import get_terminal_size + +logger = logging.getLogger(__name__) + + +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""" + + def __init__(self, *args, **kwargs): + self.name = kwargs.pop('name') + + isolated = kwargs.pop("isolated", False) + self.config = Configuration(isolated) + + assert self.name + optparse.OptionParser.__init__(self, *args, **kwargs) + + 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 _get_ordered_configuration_items(self): + # Configuration gives keys in an unordered manner. Order them. + override_order = ["global", self.name, ":env:"] + + # Pool the options into different groups + section_items = {name: [] for name in override_order} + for section_key, val in self.config.items(): + # ignore empty values + if not val: + logger.debug( + "Ignoring configuration key '%s' as it's value is empty.", + section_key + ) + continue + + section, key = section_key.split(".", 1) + if section in override_order: + section_items[section].append((key, val)) + + # Yield each group in their override order + for section in override_order: + for key, val in section_items[section]: + yield key, val + + 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).""" + + # Accumulate complex default state. + self.values = optparse.Values(self.defaults) + late_eval = set() + # Then set the options with those values + for key, val in self._get_ordered_configuration_items(): + # '--' because configuration supports only long names + 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'): + try: + val = strtobool(val) + except ValueError: + error_msg = invalid_config_error_message( + option.action, key, val + ) + self.error(error_msg) + + 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 get_default_values(self): + """Overriding 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) + + # Load the configuration, or error out in case of an error + try: + self.config.load() + except ConfigurationError as err: + self.exit(UNKNOWN_ERROR, str(err)) + + 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(UNKNOWN_ERROR, "%s\n" % msg) + + +def invalid_config_error_message(action, key, val): + """Returns a better error message when invalid configuration option + is provided.""" + if action in ('store_true', 'store_false'): + return ("{0} is not a valid value for {1} option, " + "please specify a boolean value like yes/no, " + "true/false or 1/0 instead.").format(val, key) + + return ("{0} is not a valid value for {1} option, " + "please specify a numerical value like 1/0 " + "instead.").format(val, key) diff --git a/venv2/lib/python2.7/site-packages/pip/_internal/cli/status_codes.py b/venv2/lib/python2.7/site-packages/pip/_internal/cli/status_codes.py new file mode 100644 index 0000000..275360a --- /dev/null +++ b/venv2/lib/python2.7/site-packages/pip/_internal/cli/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/venv2/lib/python2.7/site-packages/pip/_internal/commands/__init__.py b/venv2/lib/python2.7/site-packages/pip/_internal/commands/__init__.py new file mode 100644 index 0000000..2e90db3 --- /dev/null +++ b/venv2/lib/python2.7/site-packages/pip/_internal/commands/__init__.py @@ -0,0 +1,79 @@ +""" +Package containing all pip commands +""" +from __future__ import absolute_import + +from pip._internal.commands.completion import CompletionCommand +from pip._internal.commands.configuration import ConfigurationCommand +from pip._internal.commands.download import DownloadCommand +from pip._internal.commands.freeze import FreezeCommand +from pip._internal.commands.hash import HashCommand +from pip._internal.commands.help import HelpCommand +from pip._internal.commands.list import ListCommand +from pip._internal.commands.check import CheckCommand +from pip._internal.commands.search import SearchCommand +from pip._internal.commands.show import ShowCommand +from pip._internal.commands.install import InstallCommand +from pip._internal.commands.uninstall import UninstallCommand +from pip._internal.commands.wheel import WheelCommand + +from pip._internal.utils.typing import MYPY_CHECK_RUNNING + +if MYPY_CHECK_RUNNING: + from typing import List, Type + from pip._internal.cli.base_command import Command + +commands_order = [ + InstallCommand, + DownloadCommand, + UninstallCommand, + FreezeCommand, + ListCommand, + ShowCommand, + CheckCommand, + ConfigurationCommand, + SearchCommand, + WheelCommand, + HashCommand, + CompletionCommand, + HelpCommand, +] # type: List[Type[Command]] + +commands_dict = {c.name: c for c in commands_order} + + +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/venv2/lib/python2.7/site-packages/pip/_internal/commands/check.py b/venv2/lib/python2.7/site-packages/pip/_internal/commands/check.py new file mode 100644 index 0000000..801cecc --- /dev/null +++ b/venv2/lib/python2.7/site-packages/pip/_internal/commands/check.py @@ -0,0 +1,41 @@ +import logging + +from pip._internal.cli.base_command import Command +from pip._internal.operations.check import ( + check_package_set, create_package_set_from_installed, +) + +logger = logging.getLogger(__name__) + + +class CheckCommand(Command): + """Verify installed packages have compatible dependencies.""" + name = 'check' + usage = """ + %prog [options]""" + summary = 'Verify installed packages have compatible dependencies.' + + def run(self, options, args): + package_set, parsing_probs = create_package_set_from_installed() + missing, conflicting = check_package_set(package_set) + + for project_name in missing: + version = package_set[project_name].version + for dependency in missing[project_name]: + logger.info( + "%s %s requires %s, which is not installed.", + project_name, version, dependency[0], + ) + + for project_name in conflicting: + version = package_set[project_name].version + for dep_name, dep_version, req in conflicting[project_name]: + logger.info( + "%s %s has requirement %s, but you have %s %s.", + project_name, version, req, dep_name, dep_version, + ) + + if missing or conflicting or parsing_probs: + return 1 + else: + logger.info("No broken requirements found.") diff --git a/venv2/lib/python2.7/site-packages/pip/_internal/commands/completion.py b/venv2/lib/python2.7/site-packages/pip/_internal/commands/completion.py new file mode 100644 index 0000000..2fcdd39 --- /dev/null +++ b/venv2/lib/python2.7/site-packages/pip/_internal/commands/completion.py @@ -0,0 +1,94 @@ +from __future__ import absolute_import + +import sys +import textwrap + +from pip._internal.cli.base_command import Command +from pip._internal.utils.misc import get_prog + +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 %(prog)s + """, + '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 %(prog)s + """, + 'fish': """ + function __fish_complete_pip + set -lx COMP_WORDS (commandline -o) "" + set -lx COMP_CWORD ( \\ + math (contains -i -- (commandline -t) $COMP_WORDS)-1 \\ + ) + set -lx PIP_AUTO_COMPLETE 1 + string split \\ -- (eval $COMP_WORDS[1]) + end + complete -fa "(__fish_complete_pip)" -c %(prog)s + """, +} + + +class CompletionCommand(Command): + """A helper command to be used for command completion.""" + name = 'completion' + summary = 'A helper command used for command completion.' + ignore_require_venv = True + + 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') + cmd_opts.add_option( + '--fish', '-f', + action='store_const', + const='fish', + dest='shell', + help='Emit completion code for fish') + + 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 = textwrap.dedent( + COMPLETION_SCRIPTS.get(options.shell, '') % { + 'prog': get_prog(), + } + ) + 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/venv2/lib/python2.7/site-packages/pip/_internal/commands/configuration.py b/venv2/lib/python2.7/site-packages/pip/_internal/commands/configuration.py new file mode 100644 index 0000000..950e205 --- /dev/null +++ b/venv2/lib/python2.7/site-packages/pip/_internal/commands/configuration.py @@ -0,0 +1,253 @@ +import logging +import os +import subprocess + +from pip._internal.cli.base_command import Command +from pip._internal.cli.status_codes import ERROR, SUCCESS +from pip._internal.configuration import Configuration, kinds +from pip._internal.exceptions import PipError +from pip._internal.locations import running_under_virtualenv, site_config_file +from pip._internal.utils.deprecation import deprecated +from pip._internal.utils.misc import get_prog + +logger = logging.getLogger(__name__) + + +class ConfigurationCommand(Command): + """Manage local and global configuration. + + Subcommands: + + list: List the active configuration (or from the file specified) + edit: Edit the configuration file in an editor + get: Get the value associated with name + set: Set the name=value + unset: Unset the value associated with name + + If none of --user, --global and --site are passed, a virtual + environment configuration file is used if one is active and the file + exists. Otherwise, all modifications happen on the to the user file by + default. + """ + + name = 'config' + usage = """ + %prog [] list + %prog [] [--editor ] edit + + %prog [] get name + %prog [] set name value + %prog [] unset name + """ + + summary = "Manage local and global configuration." + + def __init__(self, *args, **kwargs): + super(ConfigurationCommand, self).__init__(*args, **kwargs) + + self.configuration = None + + self.cmd_opts.add_option( + '--editor', + dest='editor', + action='store', + default=None, + help=( + 'Editor to use to edit the file. Uses VISUAL or EDITOR ' + 'environment variables if not provided.' + ) + ) + + self.cmd_opts.add_option( + '--global', + dest='global_file', + action='store_true', + default=False, + help='Use the system-wide configuration file only' + ) + + self.cmd_opts.add_option( + '--user', + dest='user_file', + action='store_true', + default=False, + help='Use the user configuration file only' + ) + + self.cmd_opts.add_option( + '--site', + dest='site_file', + action='store_true', + default=False, + help='Use the current environment configuration file only' + ) + + self.cmd_opts.add_option( + '--venv', + dest='venv_file', + action='store_true', + default=False, + help=( + '[Deprecated] Use the current environment configuration ' + 'file in a virtual environment only' + ) + ) + + self.parser.insert_option_group(0, self.cmd_opts) + + def run(self, options, args): + handlers = { + "list": self.list_values, + "edit": self.open_in_editor, + "get": self.get_name, + "set": self.set_name_value, + "unset": self.unset_name + } + + # Determine action + if not args or args[0] not in handlers: + logger.error("Need an action ({}) to perform.".format( + ", ".join(sorted(handlers))) + ) + return ERROR + + action = args[0] + + # Determine which configuration files are to be loaded + # Depends on whether the command is modifying. + try: + load_only = self._determine_file( + options, need_value=(action in ["get", "set", "unset", "edit"]) + ) + except PipError as e: + logger.error(e.args[0]) + return ERROR + + # Load a new configuration + self.configuration = Configuration( + isolated=options.isolated_mode, load_only=load_only + ) + self.configuration.load() + + # Error handling happens here, not in the action-handlers. + try: + handlers[action](options, args[1:]) + except PipError as e: + logger.error(e.args[0]) + return ERROR + + return SUCCESS + + def _determine_file(self, options, need_value): + # Convert legacy venv_file option to site_file or error + if options.venv_file and not options.site_file: + if running_under_virtualenv(): + options.site_file = True + deprecated( + "The --venv option has been deprecated.", + replacement="--site", + gone_in="19.3", + ) + else: + raise PipError( + "Legacy --venv option requires a virtual environment. " + "Use --site instead." + ) + + file_options = [key for key, value in ( + (kinds.USER, options.user_file), + (kinds.GLOBAL, options.global_file), + (kinds.SITE, options.site_file), + ) if value] + + if not file_options: + if not need_value: + return None + # Default to user, unless there's a site file. + elif os.path.exists(site_config_file): + return kinds.SITE + else: + return kinds.USER + elif len(file_options) == 1: + return file_options[0] + + raise PipError( + "Need exactly one file to operate upon " + "(--user, --site, --global) to perform." + ) + + def list_values(self, options, args): + self._get_n_args(args, "list", n=0) + + for key, value in sorted(self.configuration.items()): + logger.info("%s=%r", key, value) + + def get_name(self, options, args): + key = self._get_n_args(args, "get [name]", n=1) + value = self.configuration.get_value(key) + + logger.info("%s", value) + + def set_name_value(self, options, args): + key, value = self._get_n_args(args, "set [name] [value]", n=2) + self.configuration.set_value(key, value) + + self._save_configuration() + + def unset_name(self, options, args): + key = self._get_n_args(args, "unset [name]", n=1) + self.configuration.unset_value(key) + + self._save_configuration() + + def open_in_editor(self, options, args): + editor = self._determine_editor(options) + + fname = self.configuration.get_file_to_edit() + if fname is None: + raise PipError("Could not determine appropriate file.") + + try: + subprocess.check_call([editor, fname]) + except subprocess.CalledProcessError as e: + raise PipError( + "Editor Subprocess exited with exit code {}" + .format(e.returncode) + ) + + def _get_n_args(self, args, example, n): + """Helper to make sure the command got the right number of arguments + """ + if len(args) != n: + msg = ( + 'Got unexpected number of arguments, expected {}. ' + '(example: "{} config {}")' + ).format(n, get_prog(), example) + raise PipError(msg) + + if n == 1: + return args[0] + else: + return args + + def _save_configuration(self): + # We successfully ran a modifying command. Need to save the + # configuration. + try: + self.configuration.save() + except Exception: + logger.error( + "Unable to save configuration. Please report this as a bug.", + exc_info=1 + ) + raise PipError("Internal Error.") + + def _determine_editor(self, options): + if options.editor is not None: + return options.editor + elif "VISUAL" in os.environ: + return os.environ["VISUAL"] + elif "EDITOR" in os.environ: + return os.environ["EDITOR"] + else: + raise PipError("Could not determine editor to use.") diff --git a/venv2/lib/python2.7/site-packages/pip/_internal/commands/download.py b/venv2/lib/python2.7/site-packages/pip/_internal/commands/download.py new file mode 100644 index 0000000..a57e4bc --- /dev/null +++ b/venv2/lib/python2.7/site-packages/pip/_internal/commands/download.py @@ -0,0 +1,176 @@ +from __future__ import absolute_import + +import logging +import os + +from pip._internal.cli import cmdoptions +from pip._internal.cli.base_command import RequirementCommand +from pip._internal.operations.prepare import RequirementPreparer +from pip._internal.req import RequirementSet +from pip._internal.req.req_tracker import RequirementTracker +from pip._internal.resolve import Resolver +from pip._internal.utils.filesystem import check_path_owner +from pip._internal.utils.misc import ensure_dir, normalize_path +from pip._internal.utils.temp_dir import TempDirectory + +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] ... + %prog [options] ... + %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.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.prefer_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(cmdoptions.progress_bar()) + cmd_opts.add_option(cmdoptions.no_build_isolation()) + cmd_opts.add_option(cmdoptions.use_pep517()) + cmd_opts.add_option(cmdoptions.no_use_pep517()) + + cmd_opts.add_option( + '-d', '--dest', '--destination-dir', '--destination-directory', + dest='download_dir', + metavar='dir', + default=os.curdir, + help=("Download packages into ."), + ) + + cmd_opts.add_option(cmdoptions.platform()) + cmd_opts.add_option(cmdoptions.python_version()) + cmd_opts.add_option(cmdoptions.implementation()) + cmd_opts.add_option(cmdoptions.abi()) + + 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): + options.ignore_installed = True + # editable doesn't really make sense for `pip download`, but the bowels + # of the RequirementSet code require that property. + options.editables = [] + + if options.python_version: + python_versions = [options.python_version] + else: + python_versions = None + + cmdoptions.check_dist_restriction(options) + + 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=options, + session=session, + platform=options.platform, + python_versions=python_versions, + abi=options.abi, + implementation=options.implementation, + ) + 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 RequirementTracker() as req_tracker, TempDirectory( + options.build_dir, delete=build_delete, kind="download" + ) as directory: + + requirement_set = RequirementSet( + require_hashes=options.require_hashes, + ) + self.populate_requirement_set( + requirement_set, + args, + options, + finder, + session, + self.name, + None + ) + + preparer = RequirementPreparer( + build_dir=directory.path, + src_dir=options.src_dir, + download_dir=options.download_dir, + wheel_download_dir=None, + progress_bar=options.progress_bar, + build_isolation=options.build_isolation, + req_tracker=req_tracker, + ) + + resolver = Resolver( + preparer=preparer, + finder=finder, + session=session, + wheel_cache=None, + use_user_site=False, + upgrade_strategy="to-satisfy-only", + force_reinstall=False, + ignore_dependencies=options.ignore_dependencies, + ignore_requires_python=False, + ignore_installed=True, + isolated=options.isolated_mode, + ) + resolver.resolve(requirement_set) + + 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/venv2/lib/python2.7/site-packages/pip/_internal/commands/freeze.py b/venv2/lib/python2.7/site-packages/pip/_internal/commands/freeze.py new file mode 100644 index 0000000..dc9c53a --- /dev/null +++ b/venv2/lib/python2.7/site-packages/pip/_internal/commands/freeze.py @@ -0,0 +1,96 @@ +from __future__ import absolute_import + +import sys + +from pip._internal.cache import WheelCache +from pip._internal.cli.base_command import Command +from pip._internal.models.format_control import FormatControl +from pip._internal.operations.freeze import freeze +from pip._internal.utils.compat import stdlib_pkgs + +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='requirements', + action='append', + default=[], + metavar='file', + help="Use the order in the given requirements file and its " + "comments when generating output. This option can be " + "used multiple times.") + 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.cmd_opts.add_option( + '--exclude-editable', + dest='exclude_editable', + action='store_true', + help='Exclude editable package from output.') + + self.parser.insert_option_group(0, self.cmd_opts) + + def run(self, options, args): + format_control = 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.requirements, + 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, + exclude_editable=options.exclude_editable, + ) + + try: + for line in freeze(**freeze_kwargs): + sys.stdout.write(line + '\n') + finally: + wheel_cache.cleanup() diff --git a/venv2/lib/python2.7/site-packages/pip/_internal/commands/hash.py b/venv2/lib/python2.7/site-packages/pip/_internal/commands/hash.py new file mode 100644 index 0000000..423440e --- /dev/null +++ b/venv2/lib/python2.7/site-packages/pip/_internal/commands/hash.py @@ -0,0 +1,57 @@ +from __future__ import absolute_import + +import hashlib +import logging +import sys + +from pip._internal.cli.base_command import Command +from pip._internal.cli.status_codes import ERROR +from pip._internal.utils.hashes import FAVORITE_HASH, STRONG_HASHES +from pip._internal.utils.misc import read_chunks + +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.' + ignore_require_venv = True + + 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/venv2/lib/python2.7/site-packages/pip/_internal/commands/help.py b/venv2/lib/python2.7/site-packages/pip/_internal/commands/help.py new file mode 100644 index 0000000..49a81cb --- /dev/null +++ b/venv2/lib/python2.7/site-packages/pip/_internal/commands/help.py @@ -0,0 +1,37 @@ +from __future__ import absolute_import + +from pip._internal.cli.base_command import Command +from pip._internal.cli.status_codes import SUCCESS +from pip._internal.exceptions import CommandError + + +class HelpCommand(Command): + """Show help for commands""" + name = 'help' + usage = """ + %prog """ + summary = 'Show help for commands.' + ignore_require_venv = True + + def run(self, options, args): + from pip._internal.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/venv2/lib/python2.7/site-packages/pip/_internal/commands/install.py b/venv2/lib/python2.7/site-packages/pip/_internal/commands/install.py new file mode 100644 index 0000000..c13da44 --- /dev/null +++ b/venv2/lib/python2.7/site-packages/pip/_internal/commands/install.py @@ -0,0 +1,587 @@ +from __future__ import absolute_import + +import errno +import logging +import operator +import os +import shutil +from optparse import SUPPRESS_HELP + +from pip._vendor import pkg_resources + +from pip._internal.cache import WheelCache +from pip._internal.cli import cmdoptions +from pip._internal.cli.base_command import RequirementCommand +from pip._internal.cli.status_codes import ERROR +from pip._internal.exceptions import ( + CommandError, InstallationError, PreviousBuildDirError, +) +from pip._internal.locations import distutils_scheme, virtualenv_no_global +from pip._internal.operations.check import check_install_conflicts +from pip._internal.operations.prepare import RequirementPreparer +from pip._internal.req import RequirementSet, install_given_reqs +from pip._internal.req.req_tracker import RequirementTracker +from pip._internal.resolve import Resolver +from pip._internal.utils.filesystem import check_path_owner +from pip._internal.utils.misc import ( + ensure_dir, get_installed_version, + protect_pip_from_modification_on_windows, +) +from pip._internal.utils.temp_dir import TempDirectory +from pip._internal.wheel import WheelBuilder + +logger = logging.getLogger(__name__) + + +def is_wheel_installed(): + """ + Return whether the wheel package is installed. + """ + try: + import wheel # noqa: F401 + except ImportError: + return False + + return True + + +def build_wheels(builder, pep517_requirements, legacy_requirements, session): + """ + Build wheels for requirements, depending on whether wheel is installed. + """ + # We don't build wheels for legacy requirements if wheel is not installed. + should_build_legacy = is_wheel_installed() + + # Always build PEP 517 requirements + build_failures = builder.build( + pep517_requirements, + session=session, autobuilding=True + ) + + if should_build_legacy: + # We don't care about failures building legacy + # requirements, as we'll fall through to a direct + # install for those. + builder.build( + legacy_requirements, + session=session, autobuilding=True + ) + + return build_failures + + +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) + + cmd_opts = self.cmd_opts + + cmd_opts.add_option(cmdoptions.requirements()) + cmd_opts.add_option(cmdoptions.constraints()) + cmd_opts.add_option(cmdoptions.no_deps()) + cmd_opts.add_option(cmdoptions.pre()) + + cmd_opts.add_option(cmdoptions.editable()) + 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(cmdoptions.platform()) + cmd_opts.add_option(cmdoptions.python_version()) + cmd_opts.add_option(cmdoptions.implementation()) + cmd_opts.add_option(cmdoptions.abi()) + + cmd_opts.add_option( + '--user', + dest='use_user_site', + action='store_true', + 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.)") + cmd_opts.add_option( + '--no-user', + dest='use_user_site', + action='store_false', + help=SUPPRESS_HELP) + 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(cmdoptions.build_dir()) + + 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. The handling of dependencies depends on the ' + 'upgrade-strategy used.' + ) + + cmd_opts.add_option( + '--upgrade-strategy', + dest='upgrade_strategy', + default='only-if-needed', + choices=['only-if-needed', 'eager'], + help='Determines how dependency upgrading should be handled ' + '[default: %default]. ' + '"eager" - dependencies are upgraded regardless of ' + 'whether the currently installed version satisfies the ' + 'requirements of the upgraded package(s). ' + '"only-if-needed" - are upgraded only when they do not ' + 'satisfy the requirements of the upgraded package(s).' + ) + + cmd_opts.add_option( + '--force-reinstall', + dest='force_reinstall', + action='store_true', + help='Reinstall all packages even if they are already ' + 'up-to-date.') + + cmd_opts.add_option( + '-I', '--ignore-installed', + dest='ignore_installed', + action='store_true', + help='Ignore the installed packages (reinstalling instead).') + + cmd_opts.add_option(cmdoptions.ignore_requires_python()) + cmd_opts.add_option(cmdoptions.no_build_isolation()) + cmd_opts.add_option(cmdoptions.use_pep517()) + cmd_opts.add_option(cmdoptions.no_use_pep517()) + + cmd_opts.add_option(cmdoptions.install_options()) + cmd_opts.add_option(cmdoptions.global_options()) + + cmd_opts.add_option( + "--compile", + action="store_true", + dest="compile", + default=True, + help="Compile Python source files to bytecode", + ) + + cmd_opts.add_option( + "--no-compile", + action="store_false", + dest="compile", + help="Do not compile Python source files to bytecode", + ) + + cmd_opts.add_option( + "--no-warn-script-location", + action="store_false", + dest="warn_script_location", + default=True, + help="Do not warn when installing scripts outside PATH", + ) + cmd_opts.add_option( + "--no-warn-conflicts", + action="store_false", + dest="warn_about_conflicts", + default=True, + help="Do not warn about broken dependencies", + ) + + cmd_opts.add_option(cmdoptions.no_binary()) + cmd_opts.add_option(cmdoptions.only_binary()) + cmd_opts.add_option(cmdoptions.prefer_binary()) + cmd_opts.add_option(cmdoptions.no_clean()) + cmd_opts.add_option(cmdoptions.require_hashes()) + cmd_opts.add_option(cmdoptions.progress_bar()) + + 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.check_install_build_global(options) + upgrade_strategy = "to-satisfy-only" + if options.upgrade: + upgrade_strategy = options.upgrade_strategy + + if options.build_dir: + options.build_dir = os.path.abspath(options.build_dir) + + cmdoptions.check_dist_restriction(options, check_target=True) + + if options.python_version: + python_versions = [options.python_version] + else: + python_versions = None + + 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=') + + target_temp_dir = TempDirectory(kind="target") + if options.target_dir: + options.ignore_installed = True + 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." + ) + + # Create a target directory for using with the target option + target_temp_dir.create() + install_options.append('--home=' + target_temp_dir.path) + + global_options = options.global_options or [] + + with self._build_session(options) as session: + finder = self._build_package_finder( + options=options, + session=session, + platform=options.platform, + python_versions=python_versions, + abi=options.abi, + implementation=options.implementation, + ) + 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 RequirementTracker() as req_tracker, TempDirectory( + options.build_dir, delete=build_delete, kind="install" + ) as directory: + requirement_set = RequirementSet( + require_hashes=options.require_hashes, + check_supported_wheels=not options.target_dir, + ) + + try: + self.populate_requirement_set( + requirement_set, args, options, finder, session, + self.name, wheel_cache + ) + preparer = RequirementPreparer( + build_dir=directory.path, + src_dir=options.src_dir, + download_dir=None, + wheel_download_dir=None, + progress_bar=options.progress_bar, + build_isolation=options.build_isolation, + req_tracker=req_tracker, + ) + + resolver = Resolver( + preparer=preparer, + finder=finder, + session=session, + wheel_cache=wheel_cache, + use_user_site=options.use_user_site, + upgrade_strategy=upgrade_strategy, + force_reinstall=options.force_reinstall, + ignore_dependencies=options.ignore_dependencies, + ignore_requires_python=options.ignore_requires_python, + ignore_installed=options.ignore_installed, + isolated=options.isolated_mode, + use_pep517=options.use_pep517 + ) + resolver.resolve(requirement_set) + + protect_pip_from_modification_on_windows( + modifying_pip=requirement_set.has_requirement("pip") + ) + + # Consider legacy and PEP517-using requirements separately + legacy_requirements = [] + pep517_requirements = [] + for req in requirement_set.requirements.values(): + if req.use_pep517: + pep517_requirements.append(req) + else: + legacy_requirements.append(req) + + wheel_builder = WheelBuilder( + finder, preparer, wheel_cache, + build_options=[], global_options=[], + ) + + build_failures = build_wheels( + builder=wheel_builder, + pep517_requirements=pep517_requirements, + legacy_requirements=legacy_requirements, + session=session, + ) + + # If we're using PEP 517, we cannot do a direct install + # so we fail here. + if build_failures: + raise InstallationError( + "Could not build wheels for {} which use" + " PEP 517 and cannot be installed directly".format( + ", ".join(r.name for r in build_failures))) + + to_install = resolver.get_installation_order( + requirement_set + ) + + # Consistency Checking of the package set we're installing. + should_warn_about_conflicts = ( + not options.ignore_dependencies and + options.warn_about_conflicts + ) + if should_warn_about_conflicts: + self._warn_about_conflicts(to_install) + + # Don't warn about script install locations if + # --target has been specified + warn_script_location = options.warn_script_location + if options.target_dir: + warn_script_location = False + + installed = install_given_reqs( + to_install, + install_options, + global_options, + root=options.root_path, + home=target_temp_dir.path, + prefix=options.prefix_path, + pycompile=options.compile, + warn_script_location=warn_script_location, + use_user_site=options.use_user_site, + ) + + lib_locations = get_lib_location_guesses( + user=options.use_user_site, + home=target_temp_dir.path, + root=options.root_path, + prefix=options.prefix_path, + isolated=options.isolated_mode, + ) + working_set = pkg_resources.WorkingSet(lib_locations) + + reqs = sorted(installed, key=operator.attrgetter('name')) + items = [] + for req in reqs: + item = req.name + try: + installed_version = get_installed_version( + req.name, working_set=working_set + ) + if installed_version: + item += '-' + installed_version + except Exception: + pass + items.append(item) + installed = ' '.join(items) + if installed: + logger.info('Successfully installed %s', installed) + except EnvironmentError as error: + show_traceback = (self.verbosity >= 1) + + message = create_env_error_message( + error, show_traceback, options.use_user_site, + ) + logger.error(message, exc_info=show_traceback) + + return ERROR + except PreviousBuildDirError: + options.no_clean = True + raise + finally: + # Clean up + if not options.no_clean: + requirement_set.cleanup_files() + wheel_cache.cleanup() + + if options.target_dir: + self._handle_target_dir( + options.target_dir, target_temp_dir, options.upgrade + ) + return requirement_set + + def _handle_target_dir(self, target_dir, target_temp_dir, upgrade): + ensure_dir(target_dir) + + # Checking both purelib and platlib directories for installed + # packages to be moved to target directory + lib_dir_list = [] + + with target_temp_dir: + # Checking both purelib and platlib directories for installed + # packages to be moved to target directory + scheme = distutils_scheme('', home=target_temp_dir.path) + purelib_dir = scheme['purelib'] + platlib_dir = scheme['platlib'] + data_dir = scheme['data'] + + if os.path.exists(purelib_dir): + lib_dir_list.append(purelib_dir) + if os.path.exists(platlib_dir) and platlib_dir != purelib_dir: + lib_dir_list.append(platlib_dir) + if os.path.exists(data_dir): + lib_dir_list.append(data_dir) + + for lib_dir in lib_dir_list: + for item in os.listdir(lib_dir): + if lib_dir == data_dir: + ddir = os.path.join(data_dir, item) + if any(s.startswith(ddir) for s in lib_dir_list[:-1]): + continue + target_item_dir = os.path.join(target_dir, item) + if os.path.exists(target_item_dir): + if not 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 + ) + + def _warn_about_conflicts(self, to_install): + try: + package_set, _dep_info = check_install_conflicts(to_install) + except Exception: + logger.error("Error checking for conflicts.", exc_info=True) + return + missing, conflicting = _dep_info + + # NOTE: There is some duplication here from pip check + for project_name in missing: + version = package_set[project_name][0] + for dependency in missing[project_name]: + logger.critical( + "%s %s requires %s, which is not installed.", + project_name, version, dependency[1], + ) + + for project_name in conflicting: + version = package_set[project_name][0] + for dep_name, dep_version, req in conflicting[project_name]: + logger.critical( + "%s %s has requirement %s, but you'll have %s %s which is " + "incompatible.", + project_name, version, req, dep_name, dep_version, + ) + + +def get_lib_location_guesses(*args, **kwargs): + scheme = distutils_scheme('', *args, **kwargs) + return [scheme['purelib'], scheme['platlib']] + + +def create_env_error_message(error, show_traceback, using_user_site): + """Format an error message for an EnvironmentError + + It may occur anytime during the execution of the install command. + """ + parts = [] + + # Mention the error if we are not going to show a traceback + parts.append("Could not install packages due to an EnvironmentError") + if not show_traceback: + parts.append(": ") + parts.append(str(error)) + else: + parts.append(".") + + # Spilt the error indication from a helper message (if any) + parts[-1] += "\n" + + # Suggest useful actions to the user: + # (1) using user site-packages or (2) verifying the permissions + if error.errno == errno.EACCES: + user_option_part = "Consider using the `--user` option" + permissions_part = "Check the permissions" + + if not using_user_site: + parts.extend([ + user_option_part, " or ", + permissions_part.lower(), + ]) + else: + parts.append(permissions_part) + parts.append(".\n") + + return "".join(parts).strip() + "\n" diff --git a/venv2/lib/python2.7/site-packages/pip/_internal/commands/list.py b/venv2/lib/python2.7/site-packages/pip/_internal/commands/list.py new file mode 100644 index 0000000..d70782d --- /dev/null +++ b/venv2/lib/python2.7/site-packages/pip/_internal/commands/list.py @@ -0,0 +1,302 @@ +from __future__ import absolute_import + +import json +import logging + +from pip._vendor import six +from pip._vendor.six.moves import zip_longest + +from pip._internal.cli import cmdoptions +from pip._internal.cli.base_command import Command +from pip._internal.exceptions import CommandError +from pip._internal.index import PackageFinder +from pip._internal.utils.misc import ( + dist_is_editable, get_installed_distributions, +) +from pip._internal.utils.packaging import get_installer + +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."), + ) + + cmd_opts.add_option( + '--format', + action='store', + dest='list_format', + default="columns", + choices=('columns', 'freeze', 'json'), + help="Select the output format among: columns (default), freeze, " + "or json", + ) + + cmd_opts.add_option( + '--not-required', + action='store_true', + dest='not_required', + help="List packages that are not dependencies of " + "installed packages.", + ) + + cmd_opts.add_option( + '--exclude-editable', + action='store_false', + dest='include_editable', + help='Exclude editable package from output.', + ) + cmd_opts.add_option( + '--include-editable', + action='store_true', + dest='include_editable', + help='Include editable package from output.', + default=True, + ) + 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 _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, + session=session, + ) + + def run(self, options, args): + if options.outdated and options.uptodate: + raise CommandError( + "Options --outdated and --uptodate cannot be combined.") + + packages = get_installed_distributions( + local_only=options.local, + user_only=options.user, + editables_only=options.editable, + include_editables=options.include_editable, + ) + + # get_not_required must be called firstly in order to find and + # filter out all dependencies correctly. Otherwise a package + # can't be identified as requirement because some parent packages + # could be filtered out before. + if options.not_required: + packages = self.get_not_required(packages, options) + + if options.outdated: + packages = self.get_outdated(packages, options) + elif options.uptodate: + packages = self.get_uptodate(packages, options) + + self.output_package_listing(packages, options) + + def get_outdated(self, packages, options): + return [ + dist for dist in self.iter_packages_latest_infos(packages, options) + if dist.latest_version > dist.parsed_version + ] + + def get_uptodate(self, packages, options): + return [ + dist for dist in self.iter_packages_latest_infos(packages, options) + if dist.latest_version == dist.parsed_version + ] + + def get_not_required(self, packages, options): + dep_keys = set() + for dist in packages: + dep_keys.update(requirement.key for requirement in dist.requires()) + return {pkg for pkg in packages if pkg.key not in dep_keys} + + def iter_packages_latest_infos(self, packages, options): + index_urls = [options.index_url] + options.extra_index_urls + if options.no_index: + logger.debug('Ignoring indexes: %s', ','.join(index_urls)) + index_urls = [] + + with self._build_session(options) as session: + finder = self._build_package_finder(options, index_urls, session) + + for dist in 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] + + evaluator = finder.candidate_evaluator + best_candidate = evaluator.get_best_candidate(all_candidates) + if best_candidate is None: + continue + + remote_version = best_candidate.version + if best_candidate.location.is_wheel: + typ = 'wheel' + else: + typ = 'sdist' + # This is dirty but makes the rest of the code much cleaner + dist.latest_version = remote_version + dist.latest_filetype = typ + yield dist + + def output_package_listing(self, packages, options): + packages = sorted( + packages, + key=lambda dist: dist.project_name.lower(), + ) + if options.list_format == 'columns' and packages: + data, header = format_for_columns(packages, options) + self.output_package_listing_columns(data, header) + elif options.list_format == 'freeze': + for dist in packages: + if options.verbose >= 1: + logger.info("%s==%s (%s)", dist.project_name, + dist.version, dist.location) + else: + logger.info("%s==%s", dist.project_name, dist.version) + elif options.list_format == 'json': + logger.info(format_for_json(packages, options)) + + def output_package_listing_columns(self, data, header): + # insert the header first: we need to know the size of column names + if len(data) > 0: + data.insert(0, header) + + pkg_strings, sizes = tabulate(data) + + # Create and add a separator. + if len(data) > 0: + pkg_strings.insert(1, " ".join(map(lambda x: '-' * x, sizes))) + + for val in pkg_strings: + logger.info(val) + + +def tabulate(vals): + # From pfmoore on GitHub: + # https://github.com/pypa/pip/issues/3651#issuecomment-216932564 + assert len(vals) > 0 + + sizes = [0] * max(len(x) for x in vals) + for row in vals: + sizes = [max(s, len(str(c))) for s, c in zip_longest(sizes, row)] + + result = [] + for row in vals: + display = " ".join([str(c).ljust(s) if c is not None else '' + for s, c in zip_longest(sizes, row)]) + result.append(display) + + return result, sizes + + +def format_for_columns(pkgs, options): + """ + Convert the package data into something usable + by output_package_listing_columns. + """ + running_outdated = options.outdated + # Adjust the header for the `pip list --outdated` case. + if running_outdated: + header = ["Package", "Version", "Latest", "Type"] + else: + header = ["Package", "Version"] + + data = [] + if options.verbose >= 1 or any(dist_is_editable(x) for x in pkgs): + header.append("Location") + if options.verbose >= 1: + header.append("Installer") + + for proj in pkgs: + # if we're working on the 'outdated' list, separate out the + # latest_version and type + row = [proj.project_name, proj.version] + + if running_outdated: + row.append(proj.latest_version) + row.append(proj.latest_filetype) + + if options.verbose >= 1 or dist_is_editable(proj): + row.append(proj.location) + if options.verbose >= 1: + row.append(get_installer(proj)) + + data.append(row) + + return data, header + + +def format_for_json(packages, options): + data = [] + for dist in packages: + info = { + 'name': dist.project_name, + 'version': six.text_type(dist.version), + } + if options.verbose >= 1: + info['location'] = dist.location + info['installer'] = get_installer(dist) + if options.outdated: + info['latest_version'] = six.text_type(dist.latest_version) + info['latest_filetype'] = dist.latest_filetype + data.append(info) + return json.dumps(data) diff --git a/venv2/lib/python2.7/site-packages/pip/_internal/commands/search.py b/venv2/lib/python2.7/site-packages/pip/_internal/commands/search.py new file mode 100644 index 0000000..c157a31 --- /dev/null +++ b/venv2/lib/python2.7/site-packages/pip/_internal/commands/search.py @@ -0,0 +1,135 @@ +from __future__ import absolute_import + +import logging +import sys +import textwrap +from collections import OrderedDict + +from pip._vendor import pkg_resources +from pip._vendor.packaging.version import parse as parse_version +# NOTE: XMLRPC Client is not annotated in typeshed as on 2017-07-17, which is +# why we ignore the type on this import +from pip._vendor.six.moves import xmlrpc_client # type: ignore + +from pip._internal.cli.base_command import Command +from pip._internal.cli.status_codes import NO_MATCHES_FOUND, SUCCESS +from pip._internal.download import PipXmlrpcTransport +from pip._internal.exceptions import CommandError +from pip._internal.models.index import PyPI +from pip._internal.utils.compat import get_terminal_size +from pip._internal.utils.logging import indent_log + +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.' + ignore_require_venv = True + + def __init__(self, *args, **kw): + super(SearchCommand, self).__init__(*args, **kw) + self.cmd_opts.add_option( + '-i', '--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 = OrderedDict() + for hit in hits: + name = hit['name'] + summary = hit['summary'] + version = hit['version'] + + if name not in packages.keys(): + packages[name] = { + 'name': name, + 'summary': summary, + 'versions': [version], + } + 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 + + return list(packages.values()) + + +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(highest_version(hit.get('versions', ['-']))) + 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 '' + latest = highest_version(hit.get('versions', ['-'])) + if terminal_width is not None: + target_width = terminal_width - name_column_width - 5 + if target_width > 10: + # wrap and indent summary to fit terminal + summary = textwrap.wrap(summary, target_width) + summary = ('\n' + ' ' * (name_column_width + 3)).join(summary) + + line = '%-*s - %s' % (name_column_width, + '%s (%s)' % (name, latest), summary) + try: + logger.info(line) + if name in installed_packages: + dist = pkg_resources.get_distribution(name) + with indent_log(): + 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 max(versions, key=parse_version) diff --git a/venv2/lib/python2.7/site-packages/pip/_internal/commands/show.py b/venv2/lib/python2.7/site-packages/pip/_internal/commands/show.py new file mode 100644 index 0000000..a18a902 --- /dev/null +++ b/venv2/lib/python2.7/site-packages/pip/_internal/commands/show.py @@ -0,0 +1,168 @@ +from __future__ import absolute_import + +import logging +import os +from email.parser import FeedParser + +from pip._vendor import pkg_resources +from pip._vendor.packaging.utils import canonicalize_name + +from pip._internal.cli.base_command import Command +from pip._internal.cli.status_codes import ERROR, SUCCESS + +logger = logging.getLogger(__name__) + + +class ShowCommand(Command): + """ + Show information about one or more installed packages. + + The output is in RFC-compliant mail header format. + """ + name = 'show' + usage = """ + %prog [options] ...""" + summary = 'Show information about installed packages.' + ignore_require_venv = True + + 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, list_files=options.files, verbose=options.verbose): + 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 = {} + for p in pkg_resources.working_set: + installed[canonicalize_name(p.project_name)] = p + + query_names = [canonicalize_name(name) 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 + + if dist.has_metadata('INSTALLER'): + for line in dist.get_metadata_lines('INSTALLER'): + if line.strip(): + package['installer'] = line.strip() + break + + # @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 cannot deal with repeated headers + classifiers = [] + for line in metadata.splitlines(): + 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_files=False, verbose=False): + """ + Print the informations from installed distributions found. + """ + results_printed = False + for i, dist in enumerate(distributions): + results_printed = True + if i > 0: + logger.info("---") + + name = dist.get('name', '') + required_by = [ + pkg.project_name for pkg in pkg_resources.working_set + if name in [required.name for required in pkg.requires()] + ] + + logger.info("Name: %s", name) + logger.info("Version: %s", dist.get('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', '')) + logger.info("License: %s", dist.get('license', '')) + logger.info("Location: %s", dist.get('location', '')) + logger.info("Requires: %s", ', '.join(dist.get('requires', []))) + logger.info("Required-by: %s", ', '.join(required_by)) + + if verbose: + logger.info("Metadata-Version: %s", + dist.get('metadata-version', '')) + logger.info("Installer: %s", dist.get('installer', '')) + logger.info("Classifiers:") + for classifier in dist.get('classifiers', []): + logger.info(" %s", classifier) + logger.info("Entry-points:") + for entry in dist.get('entry_points', []): + logger.info(" %s", entry.strip()) + if list_files: + logger.info("Files:") + for line in dist.get('files', []): + logger.info(" %s", line.strip()) + if "files" not in dist: + logger.info("Cannot locate installed-files.txt") + return results_printed diff --git a/venv2/lib/python2.7/site-packages/pip/_internal/commands/uninstall.py b/venv2/lib/python2.7/site-packages/pip/_internal/commands/uninstall.py new file mode 100644 index 0000000..0cd6f54 --- /dev/null +++ b/venv2/lib/python2.7/site-packages/pip/_internal/commands/uninstall.py @@ -0,0 +1,78 @@ +from __future__ import absolute_import + +from pip._vendor.packaging.utils import canonicalize_name + +from pip._internal.cli.base_command import Command +from pip._internal.exceptions import InstallationError +from pip._internal.req import parse_requirements +from pip._internal.req.constructors import install_req_from_line +from pip._internal.utils.misc import protect_pip_from_modification_on_windows + + +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: + reqs_to_uninstall = {} + for name in args: + req = install_req_from_line( + name, isolated=options.isolated_mode, + ) + if req.name: + reqs_to_uninstall[canonicalize_name(req.name)] = req + for filename in options.requirements: + for req in parse_requirements( + filename, + options=options, + session=session): + if req.name: + reqs_to_uninstall[canonicalize_name(req.name)] = req + if not reqs_to_uninstall: + raise InstallationError( + 'You must give at least one requirement to %(name)s (see ' + '"pip help %(name)s")' % dict(name=self.name) + ) + + protect_pip_from_modification_on_windows( + modifying_pip="pip" in reqs_to_uninstall + ) + + for req in reqs_to_uninstall.values(): + uninstall_pathset = req.uninstall( + auto_confirm=options.yes, verbose=self.verbosity > 0, + ) + if uninstall_pathset: + uninstall_pathset.commit() diff --git a/venv2/lib/python2.7/site-packages/pip/_internal/commands/wheel.py b/venv2/lib/python2.7/site-packages/pip/_internal/commands/wheel.py new file mode 100644 index 0000000..cd72a3d --- /dev/null +++ b/venv2/lib/python2.7/site-packages/pip/_internal/commands/wheel.py @@ -0,0 +1,186 @@ +# -*- coding: utf-8 -*- +from __future__ import absolute_import + +import logging +import os + +from pip._internal.cache import WheelCache +from pip._internal.cli import cmdoptions +from pip._internal.cli.base_command import RequirementCommand +from pip._internal.exceptions import CommandError, PreviousBuildDirError +from pip._internal.operations.prepare import RequirementPreparer +from pip._internal.req import RequirementSet +from pip._internal.req.req_tracker import RequirementTracker +from pip._internal.resolve import Resolver +from pip._internal.utils.temp_dir import TempDirectory +from pip._internal.wheel import WheelBuilder + +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: https://wheel.readthedocs.io/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.no_binary()) + cmd_opts.add_option(cmdoptions.only_binary()) + cmd_opts.add_option(cmdoptions.prefer_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.no_build_isolation()) + cmd_opts.add_option(cmdoptions.use_pep517()) + cmd_opts.add_option(cmdoptions.no_use_pep517()) + 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.ignore_requires_python()) + cmd_opts.add_option(cmdoptions.no_deps()) + cmd_opts.add_option(cmdoptions.build_dir()) + cmd_opts.add_option(cmdoptions.progress_bar()) + + 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 run(self, options, args): + cmdoptions.check_install_build_global(options) + + index_urls = [options.index_url] + options.extra_index_urls + if options.no_index: + logger.debug('Ignoring indexes: %s', ','.join(index_urls)) + index_urls = [] + + if options.build_dir: + options.build_dir = os.path.abspath(options.build_dir) + + options.src_dir = os.path.abspath(options.src_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 RequirementTracker() as req_tracker, TempDirectory( + options.build_dir, delete=build_delete, kind="wheel" + ) as directory: + + requirement_set = RequirementSet( + require_hashes=options.require_hashes, + ) + + try: + self.populate_requirement_set( + requirement_set, args, options, finder, session, + self.name, wheel_cache + ) + + preparer = RequirementPreparer( + build_dir=directory.path, + src_dir=options.src_dir, + download_dir=None, + wheel_download_dir=options.wheel_dir, + progress_bar=options.progress_bar, + build_isolation=options.build_isolation, + req_tracker=req_tracker, + ) + + resolver = Resolver( + preparer=preparer, + finder=finder, + session=session, + wheel_cache=wheel_cache, + use_user_site=False, + upgrade_strategy="to-satisfy-only", + force_reinstall=False, + ignore_dependencies=options.ignore_dependencies, + ignore_requires_python=options.ignore_requires_python, + ignore_installed=True, + isolated=options.isolated_mode, + use_pep517=options.use_pep517 + ) + resolver.resolve(requirement_set) + + # build wheels + wb = WheelBuilder( + finder, preparer, wheel_cache, + build_options=options.build_options or [], + global_options=options.global_options or [], + no_clean=options.no_clean, + ) + build_failures = wb.build( + requirement_set.requirements.values(), session=session, + ) + if len(build_failures) != 0: + 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() + wheel_cache.cleanup() diff --git a/venv2/lib/python2.7/site-packages/pip/_internal/configuration.py b/venv2/lib/python2.7/site-packages/pip/_internal/configuration.py new file mode 100644 index 0000000..b199fa7 --- /dev/null +++ b/venv2/lib/python2.7/site-packages/pip/_internal/configuration.py @@ -0,0 +1,384 @@ +"""Configuration management setup + +Some terminology: +- name + As written in config files. +- value + Value associated with a name +- key + Name combined with it's section (section.name) +- variant + A single word describing where the configuration key-value pair came from +""" + +import locale +import logging +import os + +from pip._vendor.six.moves import configparser + +from pip._internal.exceptions import ( + ConfigurationError, ConfigurationFileCouldNotBeLoaded, +) +from pip._internal.locations import ( + global_config_files, legacy_config_file, new_config_file, site_config_file, +) +from pip._internal.utils.misc import ensure_dir, enum +from pip._internal.utils.typing import MYPY_CHECK_RUNNING + +if MYPY_CHECK_RUNNING: + from typing import ( + Any, Dict, Iterable, List, NewType, Optional, Tuple + ) + + RawConfigParser = configparser.RawConfigParser # Shorthand + Kind = NewType("Kind", str) + +logger = logging.getLogger(__name__) + + +# NOTE: Maybe use the optionx attribute to normalize keynames. +def _normalize_name(name): + # type: (str) -> str + """Make a name consistent regardless of source (environment or file) + """ + name = name.lower().replace('_', '-') + if name.startswith('--'): + name = name[2:] # only prefer long opts + return name + + +def _disassemble_key(name): + # type: (str) -> List[str] + return name.split(".", 1) + + +# The kinds of configurations there are. +kinds = enum( + USER="user", # User Specific + GLOBAL="global", # System Wide + SITE="site", # [Virtual] Environment Specific + ENV="env", # from PIP_CONFIG_FILE + ENV_VAR="env-var", # from Environment Variables +) + + +class Configuration(object): + """Handles management of configuration. + + Provides an interface to accessing and managing configuration files. + + This class converts provides an API that takes "section.key-name" style + keys and stores the value associated with it as "key-name" under the + section "section". + + This allows for a clean interface wherein the both the section and the + key-name are preserved in an easy to manage form in the configuration files + and the data stored is also nice. + """ + + def __init__(self, isolated, load_only=None): + # type: (bool, Kind) -> None + super(Configuration, self).__init__() + + _valid_load_only = [kinds.USER, kinds.GLOBAL, kinds.SITE, None] + if load_only not in _valid_load_only: + raise ConfigurationError( + "Got invalid value for load_only - should be one of {}".format( + ", ".join(map(repr, _valid_load_only[:-1])) + ) + ) + self.isolated = isolated # type: bool + self.load_only = load_only # type: Optional[Kind] + + # The order here determines the override order. + self._override_order = [ + kinds.GLOBAL, kinds.USER, kinds.SITE, kinds.ENV, kinds.ENV_VAR + ] + + self._ignore_env_names = ["version", "help"] + + # Because we keep track of where we got the data from + self._parsers = { + variant: [] for variant in self._override_order + } # type: Dict[Kind, List[Tuple[str, RawConfigParser]]] + self._config = { + variant: {} for variant in self._override_order + } # type: Dict[Kind, Dict[str, Any]] + self._modified_parsers = [] # type: List[Tuple[str, RawConfigParser]] + + def load(self): + # type: () -> None + """Loads configuration from configuration files and environment + """ + self._load_config_files() + if not self.isolated: + self._load_environment_vars() + + def get_file_to_edit(self): + # type: () -> Optional[str] + """Returns the file with highest priority in configuration + """ + assert self.load_only is not None, \ + "Need to be specified a file to be editing" + + try: + return self._get_parser_to_modify()[0] + except IndexError: + return None + + def items(self): + # type: () -> Iterable[Tuple[str, Any]] + """Returns key-value pairs like dict.items() representing the loaded + configuration + """ + return self._dictionary.items() + + def get_value(self, key): + # type: (str) -> Any + """Get a value from the configuration. + """ + try: + return self._dictionary[key] + except KeyError: + raise ConfigurationError("No such key - {}".format(key)) + + def set_value(self, key, value): + # type: (str, Any) -> None + """Modify a value in the configuration. + """ + self._ensure_have_load_only() + + fname, parser = self._get_parser_to_modify() + + if parser is not None: + section, name = _disassemble_key(key) + + # Modify the parser and the configuration + if not parser.has_section(section): + parser.add_section(section) + parser.set(section, name, value) + + self._config[self.load_only][key] = value + self._mark_as_modified(fname, parser) + + def unset_value(self, key): + # type: (str) -> None + """Unset a value in the configuration. + """ + self._ensure_have_load_only() + + if key not in self._config[self.load_only]: + raise ConfigurationError("No such key - {}".format(key)) + + fname, parser = self._get_parser_to_modify() + + if parser is not None: + section, name = _disassemble_key(key) + + # Remove the key in the parser + modified_something = False + if parser.has_section(section): + # Returns whether the option was removed or not + modified_something = parser.remove_option(section, name) + + if modified_something: + # name removed from parser, section may now be empty + section_iter = iter(parser.items(section)) + try: + val = next(section_iter) + except StopIteration: + val = None + + if val is None: + parser.remove_section(section) + + self._mark_as_modified(fname, parser) + else: + raise ConfigurationError( + "Fatal Internal error [id=1]. Please report as a bug." + ) + + del self._config[self.load_only][key] + + def save(self): + # type: () -> None + """Save the current in-memory state. + """ + self._ensure_have_load_only() + + for fname, parser in self._modified_parsers: + logger.info("Writing to %s", fname) + + # Ensure directory exists. + ensure_dir(os.path.dirname(fname)) + + with open(fname, "w") as f: + parser.write(f) + + # + # Private routines + # + + def _ensure_have_load_only(self): + # type: () -> None + if self.load_only is None: + raise ConfigurationError("Needed a specific file to be modifying.") + logger.debug("Will be working with %s variant only", self.load_only) + + @property + def _dictionary(self): + # type: () -> Dict[str, Any] + """A dictionary representing the loaded configuration. + """ + # NOTE: Dictionaries are not populated if not loaded. So, conditionals + # are not needed here. + retval = {} + + for variant in self._override_order: + retval.update(self._config[variant]) + + return retval + + def _load_config_files(self): + # type: () -> None + """Loads configuration from configuration files + """ + config_files = dict(self._iter_config_files()) + if config_files[kinds.ENV][0:1] == [os.devnull]: + logger.debug( + "Skipping loading configuration files due to " + "environment's PIP_CONFIG_FILE being os.devnull" + ) + return + + for variant, files in config_files.items(): + for fname in files: + # If there's specific variant set in `load_only`, load only + # that variant, not the others. + if self.load_only is not None and variant != self.load_only: + logger.debug( + "Skipping file '%s' (variant: %s)", fname, variant + ) + continue + + parser = self._load_file(variant, fname) + + # Keeping track of the parsers used + self._parsers[variant].append((fname, parser)) + + def _load_file(self, variant, fname): + # type: (Kind, str) -> RawConfigParser + logger.debug("For variant '%s', will try loading '%s'", variant, fname) + parser = self._construct_parser(fname) + + for section in parser.sections(): + items = parser.items(section) + self._config[variant].update(self._normalized_keys(section, items)) + + return parser + + def _construct_parser(self, fname): + # type: (str) -> RawConfigParser + parser = configparser.RawConfigParser() + # If there is no such file, don't bother reading it but create the + # parser anyway, to hold the data. + # Doing this is useful when modifying and saving files, where we don't + # need to construct a parser. + if os.path.exists(fname): + try: + parser.read(fname) + except UnicodeDecodeError: + # See https://github.com/pypa/pip/issues/4963 + raise ConfigurationFileCouldNotBeLoaded( + reason="contains invalid {} characters".format( + locale.getpreferredencoding(False) + ), + fname=fname, + ) + except configparser.Error as error: + # See https://github.com/pypa/pip/issues/4893 + raise ConfigurationFileCouldNotBeLoaded(error=error) + return parser + + def _load_environment_vars(self): + # type: () -> None + """Loads configuration from environment variables + """ + self._config[kinds.ENV_VAR].update( + self._normalized_keys(":env:", self._get_environ_vars()) + ) + + def _normalized_keys(self, section, items): + # type: (str, Iterable[Tuple[str, Any]]) -> Dict[str, Any] + """Normalizes items to construct a dictionary with normalized keys. + + This routine is where the names become keys and are made the same + regardless of source - configuration files or environment. + """ + normalized = {} + for name, val in items: + key = section + "." + _normalize_name(name) + normalized[key] = val + return normalized + + def _get_environ_vars(self): + # type: () -> Iterable[Tuple[str, str]] + """Returns a generator with all environmental vars with prefix PIP_""" + for key, val in os.environ.items(): + should_be_yielded = ( + key.startswith("PIP_") and + key[4:].lower() not in self._ignore_env_names + ) + if should_be_yielded: + yield key[4:].lower(), val + + # XXX: This is patched in the tests. + def _iter_config_files(self): + # type: () -> Iterable[Tuple[Kind, List[str]]] + """Yields variant and configuration files associated with it. + + This should be treated like items of a dictionary. + """ + # SMELL: Move the conditions out of this function + + # environment variables have the lowest priority + config_file = os.environ.get('PIP_CONFIG_FILE', None) + if config_file is not None: + yield kinds.ENV, [config_file] + else: + yield kinds.ENV, [] + + # at the base we have any global configuration + yield kinds.GLOBAL, list(global_config_files) + + # per-user configuration next + should_load_user_config = not self.isolated and not ( + config_file and os.path.exists(config_file) + ) + if should_load_user_config: + # The legacy config file is overridden by the new config file + yield kinds.USER, [legacy_config_file, new_config_file] + + # finally virtualenv configuration first trumping others + yield kinds.SITE, [site_config_file] + + def _get_parser_to_modify(self): + # type: () -> Tuple[str, RawConfigParser] + # Determine which parser to modify + parsers = self._parsers[self.load_only] + if not parsers: + # This should not happen if everything works correctly. + raise ConfigurationError( + "Fatal Internal error [id=2]. Please report as a bug." + ) + + # Use the highest priority parser. + return parsers[-1] + + # XXX: This is patched in the tests. + def _mark_as_modified(self, fname, parser): + # type: (str, RawConfigParser) -> None + file_parser_tuple = (fname, parser) + if file_parser_tuple not in self._modified_parsers: + self._modified_parsers.append(file_parser_tuple) diff --git a/venv2/lib/python2.7/site-packages/pip/_internal/download.py b/venv2/lib/python2.7/site-packages/pip/_internal/download.py new file mode 100644 index 0000000..2683cf0 --- /dev/null +++ b/venv2/lib/python2.7/site-packages/pip/_internal/download.py @@ -0,0 +1,979 @@ +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 + +from pip._vendor import requests, six, urllib3 +from pip._vendor.cachecontrol import CacheControlAdapter +from pip._vendor.cachecontrol.caches import FileCache +from pip._vendor.lockfile import LockError +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.utils import get_netrc_auth +# NOTE: XMLRPC Client is not annotated in typeshed as on 2017-07-17, which is +# why we ignore the type on this import +from pip._vendor.six.moves import xmlrpc_client # type: ignore +from pip._vendor.six.moves.urllib import parse as urllib_parse +from pip._vendor.six.moves.urllib import request as urllib_request +from pip._vendor.urllib3.util import IS_PYOPENSSL + +import pip +from pip._internal.exceptions import HashMismatch, InstallationError +from pip._internal.locations import write_delete_marker_file +from pip._internal.models.index import PyPI +from pip._internal.utils.encoding import auto_decode +from pip._internal.utils.filesystem import check_path_owner +from pip._internal.utils.glibc import libc_ver +from pip._internal.utils.misc import ( + ARCHIVE_EXTENSIONS, ask_path_exists, backup_dir, consume, display_path, + format_size, get_installed_version, rmtree, split_auth_from_netloc, + splitext, unpack_file, +) +from pip._internal.utils.temp_dir import TempDirectory +from pip._internal.utils.typing import MYPY_CHECK_RUNNING +from pip._internal.utils.ui import DownloadProgressProvider +from pip._internal.vcs import vcs + +if MYPY_CHECK_RUNNING: + from typing import ( + Optional, Tuple, Dict, IO, Text, Union + ) + from pip._internal.models.link import Link + from pip._internal.utils.hashes import Hashes + from pip._internal.vcs import AuthInfo + +try: + import ssl # noqa +except ImportError: + ssl = None + +HAS_TLS = (ssl is not None) or IS_PYOPENSSL + +__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__) + + +# These are environment variables present when running under various +# CI systems. For each variable, some CI systems that use the variable +# are indicated. The collection was chosen so that for each of a number +# of popular systems, at least one of the environment variables is used. +# This list is used to provide some indication of and lower bound for +# CI traffic to PyPI. Thus, it is okay if the list is not comprehensive. +# For more background, see: https://github.com/pypa/pip/issues/5499 +CI_ENVIRONMENT_VARIABLES = ( + # Azure Pipelines + 'BUILD_BUILDID', + # Jenkins + 'BUILD_ID', + # AppVeyor, CircleCI, Codeship, Gitlab CI, Shippable, Travis CI + 'CI', +) + + +def looks_like_ci(): + # type: () -> bool + """ + Return whether it looks like pip is running under CI. + """ + # We don't use the method of checking for a tty (e.g. using isatty()) + # because some CI systems mimic a tty (e.g. Travis CI). Thus that + # method doesn't provide definitive information in either direction. + return any(name in os.environ for name in CI_ENVIRONMENT_VARIABLES) + + +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"): + from pip._vendor import distro + distro_infos = dict(filter( + lambda x: x[1], + zip(["name", "version", "id"], distro.linux_distribution()), + )) + libc = dict(filter( + lambda x: x[1], + zip(["lib", "version"], libc_ver()), + )) + if libc: + distro_infos["libc"] = libc + if distro_infos: + data["distro"] = distro_infos + + if sys.platform.startswith("darwin") and platform.mac_ver()[0]: + data["distro"] = {"name": "macOS", "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() + + if HAS_TLS: + data["openssl_version"] = ssl.OPENSSL_VERSION + + setuptools_version = get_installed_version("setuptools") + if setuptools_version is not None: + data["setuptools_version"] = setuptools_version + + # Use None rather than False so as not to give the impression that + # pip knows it is not being run under CI. Rather, it is a null or + # inconclusive result. Also, we include some value rather than no + # value to make it easier to know that the check has been run. + data["ci"] = True if looks_like_ci() else None + + user_data = os.environ.get("PIP_USER_AGENT_USER_DATA") + if user_data is not None: + data["user_data"] = user_data + + 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): + # type: (bool) -> None + self.prompting = prompting + self.passwords = {} # type: Dict[str, AuthInfo] + + def __call__(self, req): + parsed = urllib_parse.urlparse(req.url) + + # Split the credentials from the netloc. + netloc, url_user_password = split_auth_from_netloc(parsed.netloc) + + # 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)) + + # Use the credentials embedded in the url if we have none stored + if username is None: + username, password = url_user_password + + # Get creds from netrc if we still don't have them + if username is None and password is None: + netrc_auth = get_netrc_auth(req.url) + username, password = netrc_auth if netrc_auth else (None, None) + + 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 simply 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) + req.register_hook("response", self.warn_on_401) + + # Send our new request + new_resp = resp.connection.send(req, **kwargs) + new_resp.history.append(resp) + + return new_resp + + def warn_on_401(self, resp, **kwargs): + # warn user that they provided incorrect credentials + if resp.status_code == 401: + logger.warning('401 Error, Credentials not correct for %s', + resp.request.url) + + +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 # type: Optional[int] + + 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 interrupted in some way. A 503 error in general + # is typically considered a transient error so we'll go ahead and + # retry it. + # A 500 may indicate transient error in Amazon S3 + # A 520 or 527 - may indicate transient error in CloudFlare + status_forcelist=[500, 503, 520, 527], + + # 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://{}/".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): + # type: (str, Optional[str], Optional[PipSession]) -> Tuple[str, Text] + """Gets the content of a file; it may be a filename, file: URL, or + http: URL. Returns (location, content). Content is unicode. + + :param url: File path or url. + :param comes_from: Origin description of requirements. + :param session: Instance of pip.download.PipSession. + """ + 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): + # type: (Union[str, Text]) -> bool + """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): + # type: (str) -> str + """ + 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 not netloc or netloc == 'localhost': + # According to RFC 8089, same as empty authority. + netloc = '' + elif sys.platform == 'win32': + # If we have a UNC path, prepend UNC share notation. + netloc = '\\\\' + netloc + else: + raise ValueError( + 'non-local file URIs are not supported on this platform: %r' + % url + ) + + path = urllib_request.url2pathname(netloc + path) + return path + + +def path_to_url(path): + # type: (Union[str, Text]) -> str + """ + 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): + # type: (str) -> bool + """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): + # type: (Link) -> bool + return bool(_get_used_vcs_backend(link)) + + +def is_file_url(link): + # type: (Link) -> bool + return link.url.lower().startswith('file:') + + +def is_dir_url(link): + # type: (Link) -> bool + """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, # type: Response + link, # type: Link + content_file, # type: IO + hashes, # type: Hashes + progress_bar # type: str +): + # type: (...) -> None + 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 + progress_indicator = DownloadProgressProvider(progress_bar, + max=total_length) + if total_length: + logger.info("Downloading %s (%s)", url, format_size(total_length)) + else: + logger.info("Downloading %s", url) + 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, (a)abort' % + display_path(download_location), ('i', 'w', 'b', 'a')) + 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) + elif response == 'a': + sys.exit(-1) + if copy: + shutil.copy(filename, download_location) + logger.info('Saved %s', display_path(download_location)) + + +def unpack_http_url( + link, # type: Link + location, # type: str + download_dir=None, # type: Optional[str] + session=None, # type: Optional[PipSession] + hashes=None, # type: Optional[Hashes] + progress_bar="on" # type: str +): + # type: (...) -> None + if session is None: + raise TypeError( + "unpack_http_url() missing 1 required keyword argument: 'session'" + ) + + with TempDirectory(kind="unpack") as temp_dir: + # 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.path, + hashes, + progress_bar) + + # 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) + + +def unpack_file_url( + link, # type: Link + location, # type: str + download_dir=None, # type: Optional[str] + hashes=None # type: Optional[Hashes] +): + # type: (...) -> 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 embedded 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) + + +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, # type: Optional[Link] + location, # type: Optional[str] + download_dir=None, # type: Optional[str] + only_download=False, # type: bool + session=None, # type: Optional[PipSession] + hashes=None, # type: Optional[Hashes] + progress_bar="on" # type: str +): + # type: (...) -> 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, + progress_bar=progress_bar + ) + if only_download: + write_delete_marker_file(location) + + +def _download_http_url( + link, # type: Link + session, # type: PipSession + temp_dir, # type: str + hashes, # type: Hashes + progress_bar # type: str +): + # type: (...) -> Tuple[str, str] + """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, progress_bar) + return file_path, content_type + + +def _check_download_dir(link, download_dir, hashes): + # type: (Link, str, Hashes) -> Optional[str] + """ 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/venv2/lib/python2.7/site-packages/pip/_internal/exceptions.py b/venv2/lib/python2.7/site-packages/pip/_internal/exceptions.py new file mode 100644 index 0000000..7b291a1 --- /dev/null +++ b/venv2/lib/python2.7/site-packages/pip/_internal/exceptions.py @@ -0,0 +1,274 @@ +"""Exceptions used throughout package""" +from __future__ import absolute_import + +from itertools import chain, groupby, repeat + +from pip._vendor.six import iteritems + +from pip._internal.utils.typing import MYPY_CHECK_RUNNING + +if MYPY_CHECK_RUNNING: + from typing import Optional + from pip._internal.req.req_install import InstallRequirement + + +class PipError(Exception): + """Base pip exception""" + + +class ConfigurationError(PipError): + """General exception in configuration""" + + +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 # type: Optional[InstallRequirement] + 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): + # Dodge circular import. + from pip._internal.utils.hashes import FAVORITE_HASH + + 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) + + +class UnsupportedPythonVersion(InstallationError): + """Unsupported python version according to Requires-Python package + metadata.""" + + +class ConfigurationFileCouldNotBeLoaded(ConfigurationError): + """When there are errors while loading a configuration file + """ + + def __init__(self, reason="could not be loaded", fname=None, error=None): + super(ConfigurationFileCouldNotBeLoaded, self).__init__(error) + self.reason = reason + self.fname = fname + self.error = error + + def __str__(self): + if self.fname is not None: + message_part = " in {}.".format(self.fname) + else: + assert self.error is not None + message_part = ".\n{}\n".format(self.error.message) + return "Configuration file {}{}".format(self.reason, message_part) diff --git a/venv2/lib/python2.7/site-packages/pip/_internal/index.py b/venv2/lib/python2.7/site-packages/pip/_internal/index.py new file mode 100644 index 0000000..ff614b3 --- /dev/null +++ b/venv2/lib/python2.7/site-packages/pip/_internal/index.py @@ -0,0 +1,1108 @@ +"""Routines related to PyPI, indexes""" +from __future__ import absolute_import + +import cgi +import itertools +import logging +import mimetypes +import os +import posixpath +import re +import sys +from collections import namedtuple + +from pip._vendor import html5lib, requests, six +from pip._vendor.distlib.compat import unescape +from pip._vendor.packaging import specifiers +from pip._vendor.packaging.utils import canonicalize_name +from pip._vendor.packaging.version import parse as parse_version +from pip._vendor.requests.exceptions import HTTPError, RetryError, SSLError +from pip._vendor.six.moves.urllib import parse as urllib_parse +from pip._vendor.six.moves.urllib import request as urllib_request + +from pip._internal.download import HAS_TLS, is_url, path_to_url, url_to_path +from pip._internal.exceptions import ( + BestVersionAlreadyInstalled, DistributionNotFound, InvalidWheelFilename, + UnsupportedWheel, +) +from pip._internal.models.candidate import InstallationCandidate +from pip._internal.models.format_control import FormatControl +from pip._internal.models.index import PyPI +from pip._internal.models.link import Link +from pip._internal.pep425tags import get_supported +from pip._internal.utils.compat import ipaddress +from pip._internal.utils.logging import indent_log +from pip._internal.utils.misc import ( + ARCHIVE_EXTENSIONS, SUPPORTED_EXTENSIONS, WHEEL_EXTENSION, normalize_path, + redact_password_from_url, +) +from pip._internal.utils.packaging import check_requires_python +from pip._internal.utils.typing import MYPY_CHECK_RUNNING +from pip._internal.wheel import Wheel + +if MYPY_CHECK_RUNNING: + from logging import Logger + from typing import ( + Tuple, Optional, Any, List, Union, Callable, Set, Sequence, + Iterable, MutableMapping + ) + from pip._vendor.packaging.version import _BaseVersion + from pip._vendor.requests import Response + from pip._internal.pep425tags import Pep425Tag + from pip._internal.req import InstallRequirement + from pip._internal.download import PipSession + + SecureOrigin = Tuple[str, str, Optional[str]] + BuildTag = Tuple[Any, ...] # either empty tuple or Tuple[int, str] + CandidateSortingKey = Tuple[int, _BaseVersion, BuildTag, Optional[int]] + + +__all__ = ['FormatControl', 'FoundCandidates', '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", "*", "*"), +] # type: List[SecureOrigin] + + +logger = logging.getLogger(__name__) + + +def _match_vcs_scheme(url): + # type: (str) -> Optional[str] + """Look for VCS schemes in the URL. + + Returns the matched VCS scheme, or None if there's no match. + """ + from pip._internal.vcs import VcsSupport + for scheme in VcsSupport.schemes: + if url.lower().startswith(scheme) and url[len(scheme)] in '+:': + return scheme + return None + + +def _is_url_like_archive(url): + # type: (str) -> bool + """Return whether the URL looks like an archive. + """ + filename = Link(url).filename + for bad_ext in ARCHIVE_EXTENSIONS: + if filename.endswith(bad_ext): + return True + return False + + +class _NotHTML(Exception): + def __init__(self, content_type, request_desc): + # type: (str, str) -> None + super(_NotHTML, self).__init__(content_type, request_desc) + self.content_type = content_type + self.request_desc = request_desc + + +def _ensure_html_header(response): + # type: (Response) -> None + """Check the Content-Type header to ensure the response contains HTML. + + Raises `_NotHTML` if the content type is not text/html. + """ + content_type = response.headers.get("Content-Type", "") + if not content_type.lower().startswith("text/html"): + raise _NotHTML(content_type, response.request.method) + + +class _NotHTTP(Exception): + pass + + +def _ensure_html_response(url, session): + # type: (str, PipSession) -> None + """Send a HEAD request to the URL, and ensure the response contains HTML. + + Raises `_NotHTTP` if the URL is not available for a HEAD request, or + `_NotHTML` if the content type is not text/html. + """ + scheme, netloc, path, query, fragment = urllib_parse.urlsplit(url) + if scheme not in {'http', 'https'}: + raise _NotHTTP() + + resp = session.head(url, allow_redirects=True) + resp.raise_for_status() + + _ensure_html_header(resp) + + +def _get_html_response(url, session): + # type: (str, PipSession) -> Response + """Access an HTML page with GET, and return the response. + + This consists of three parts: + + 1. If the URL looks suspiciously like an archive, send a HEAD first to + check the Content-Type is HTML, to avoid downloading a large file. + Raise `_NotHTTP` if the content type cannot be determined, or + `_NotHTML` if it is not HTML. + 2. Actually perform the request. Raise HTTP exceptions on network failures. + 3. Check the Content-Type header to make sure we got HTML, and raise + `_NotHTML` otherwise. + """ + if _is_url_like_archive(url): + _ensure_html_response(url, session=session) + + logger.debug('Getting page %s', redact_password_from_url(url)) + + resp = session.get( + url, + headers={ + "Accept": "text/html", + # We don't want to blindly returned cached data for + # /simple/, because authors generally expecting that + # twine upload && pip install will function, but if + # they've done a pip install in the last ~10 minutes + # it won't. Thus by setting this to zero we will not + # blindly use any cached data, however the benefit of + # using max-age=0 instead of no-cache, is that we will + # still support conditional requests, so we will still + # minimize traffic sent in cases where the page hasn't + # changed at all, we will just always incur the round + # trip for the conditional GET now instead of only + # once per 10 minutes. + # For more information, please see pypa/pip#5670. + "Cache-Control": "max-age=0", + }, + ) + 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. + _ensure_html_header(resp) + + return resp + + +def _handle_get_page_fail( + link, # type: Link + reason, # type: Union[str, Exception] + meth=None # type: Optional[Callable[..., None]] +): + # type: (...) -> None + if meth is None: + meth = logger.debug + meth("Could not fetch URL %s: %s - skipping", link, reason) + + +def _get_html_page(link, session=None): + # type: (Link, Optional[PipSession]) -> Optional[HTMLPage] + if session is None: + raise TypeError( + "_get_html_page() missing 1 required keyword argument: 'session'" + ) + + url = link.url.split('#', 1)[0] + + # Check for VCS schemes that do not support lookup as web pages. + vcs_scheme = _match_vcs_scheme(url) + if vcs_scheme: + logger.debug('Cannot look at %s URL %s', vcs_scheme, link) + return None + + # Tack index.html onto file:// URLs that point to directories + scheme, _, path, _, _, _ = 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) + + try: + resp = _get_html_response(url, session=session) + except _NotHTTP: + logger.debug( + 'Skipping page %s because it looks like an archive, and cannot ' + 'be checked by HEAD.', link, + ) + except _NotHTML as exc: + logger.debug( + 'Skipping page %s because the %s request got Content-Type: %s', + link, exc.request_desc, exc.content_type, + ) + except HTTPError as exc: + _handle_get_page_fail(link, exc) + except RetryError as exc: + _handle_get_page_fail(link, exc) + except SSLError as exc: + reason = "There was a problem confirming the ssl certificate: " + reason += str(exc) + _handle_get_page_fail(link, reason, meth=logger.info) + except requests.ConnectionError as exc: + _handle_get_page_fail(link, "connection error: %s" % exc) + except requests.Timeout: + _handle_get_page_fail(link, "timed out") + else: + return HTMLPage(resp.content, resp.url, resp.headers) + return None + + +class CandidateEvaluator(object): + + def __init__( + self, + valid_tags, # type: List[Pep425Tag] + prefer_binary=False # type: bool + + ): + # type: (...) -> None + self._prefer_binary = prefer_binary + self._valid_tags = valid_tags + + def is_wheel_supported(self, wheel): + # type: (Wheel) -> bool + return wheel.supported(self._valid_tags) + + def _sort_key(self, candidate): + # type: (InstallationCandidate) -> CandidateSortingKey + """ + 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(self._valid_tags) + 3. source archives + If prefer_binary was set, then all wheels are sorted above sources. + 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(self._valid_tags) + build_tag = tuple() # type: BuildTag + binary_preference = 0 + if candidate.location.is_wheel: + # can raise InvalidWheelFilename + wheel = Wheel(candidate.location.filename) + if not wheel.supported(self._valid_tags): + raise UnsupportedWheel( + "%s is not a supported wheel for this platform. It " + "can't be sorted." % wheel.filename + ) + if self._prefer_binary: + binary_preference = 1 + pri = -(wheel.support_index_min(self._valid_tags)) + if wheel.build_tag is not None: + match = re.match(r'^(\d+)(.*)$', wheel.build_tag) + build_tag_groups = match.groups() + build_tag = (int(build_tag_groups[0]), build_tag_groups[1]) + else: # sdist + pri = -(support_num) + return (binary_preference, candidate.version, build_tag, pri) + + def get_best_candidate(self, candidates): + # type: (List[InstallationCandidate]) -> InstallationCandidate + """ + Return the best candidate per the instance's sort order, or None if + no candidates are given. + """ + if not candidates: + return None + + return max(candidates, key=self._sort_key) + + +class FoundCandidates(object): + """A collection of candidates, returned by `PackageFinder.find_candidates`. + + This class is only intended to be instantiated by PackageFinder through + the `from_specifier()` constructor. + + Arguments: + + * `candidates`: A sequence of all available candidates found. + * `specifier`: Specifier to filter applicable versions. + * `prereleases`: Whether prereleases should be accounted. Pass None to + infer from the specifier. + * `evaluator`: A CandidateEvaluator object to sort applicable candidates + by order of preference. + """ + + def __init__( + self, + candidates, # type: List[InstallationCandidate] + versions, # type: Set[str] + evaluator, # type: CandidateEvaluator + ): + # type: (...) -> None + self._candidates = candidates + self._evaluator = evaluator + self._versions = versions + + @classmethod + def from_specifier( + cls, + candidates, # type: List[InstallationCandidate] + specifier, # type: specifiers.BaseSpecifier + prereleases, # type: Optional[bool] + evaluator, # type: CandidateEvaluator + ): + # type: (...) -> FoundCandidates + versions = { + str(v) for v in 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 candidates), + prereleases=prereleases, + ) + } + return cls(candidates, versions, evaluator) + + def iter_all(self): + # type: () -> Iterable[InstallationCandidate] + """Iterate through all candidates. + """ + return iter(self._candidates) + + def iter_applicable(self): + # type: () -> Iterable[InstallationCandidate] + """Iterate through candidates matching the versions associated with + this instance. + """ + # Again, converting version to str to deal with debundling. + return (c for c in self.iter_all() if str(c.version) in self._versions) + + def get_best(self): + # type: () -> Optional[InstallationCandidate] + """Return the best candidate available, or None if no applicable + candidates are found. + """ + candidates = list(self.iter_applicable()) + return self._evaluator.get_best_candidate(candidates) + + +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, # type: List[str] + index_urls, # type: List[str] + allow_all_prereleases=False, # type: bool + trusted_hosts=None, # type: Optional[Iterable[str]] + session=None, # type: Optional[PipSession] + format_control=None, # type: Optional[FormatControl] + platform=None, # type: Optional[str] + versions=None, # type: Optional[List[str]] + abi=None, # type: Optional[str] + implementation=None, # type: Optional[str] + prefer_binary=False # type: bool + ): + # type: (...) -> 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. + :param platform: A string or None. If None, searches for packages + that are supported by the current system. Otherwise, will find + packages that can be built on the platform passed in. These + packages will only be downloaded for distribution: they will + not be built locally. + :param versions: A list of strings or None. This is passed directly + to pep425tags.py in the get_supported() method. + :param abi: A string or None. This is passed directly + to pep425tags.py in the get_supported() method. + :param implementation: A string or None. This is passed directly + to pep425tags.py in the get_supported() method. + :param prefer_binary: Whether to prefer an old, but valid, binary + dist over a new source dist. + """ + 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 = [] # type: List[str] + 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 + + # These are boring links that have already been logged somehow: + self.logged_links = set() # type: Set[Link] + + 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 []) + ] # type: List[SecureOrigin] + + # Do we want to allow _all_ pre-releases? + self.allow_all_prereleases = allow_all_prereleases + + # The Session we'll use to make requests + self.session = session + + # The valid tags to check potential found wheel candidates against + valid_tags = get_supported( + versions=versions, + platform=platform, + abi=abi, + impl=implementation, + ) + self.candidate_evaluator = CandidateEvaluator( + valid_tags=valid_tags, prefer_binary=prefer_binary, + ) + + # 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 get_formatted_locations(self): + # type: () -> str + lines = [] + if self.index_urls and self.index_urls != [PyPI.simple_url]: + lines.append( + "Looking in indexes: {}".format(", ".join( + redact_password_from_url(url) for url in self.index_urls)) + ) + if self.find_links: + lines.append( + "Looking in links: {}".format(", ".join(self.find_links)) + ) + return "\n".join(lines) + + @staticmethod + def _sort_locations(locations, expand_dir=False): + # type: (Sequence[str], bool) -> Tuple[List[str], List[str]] + """ + 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) + else: + logger.warning( + "Path '{0}' is ignored: " + "it is a directory.".format(path), + ) + 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 _validate_secure_origin(self, logger, location): + # type: (Logger, Link) -> bool + # 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) + # setting secure_origin[1] to proper Union[bytes, str] + # creates problems in other places + else secure_origin[1].decode("utf8") # type: ignore + ) + 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 we " + "recommend you use HTTPS instead, otherwise you may silence " + "this warning and allow it anyway with '--trusted-host %s'.", + parsed.hostname, + parsed.hostname, + ) + + return False + + def _get_index_urls_locations(self, project_name): + # type: (str) -> List[str] + """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): + # type: (str) -> List[Optional[InstallationCandidate]] + """Find all available InstallationCandidate for project_name + + This checks index_urls and find_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, + ) + + file_locations = (Link(url) for url in itertools.chain( + index_file_loc, fl_file_loc, + )) + + # We trust every url that the user has given us whether it was given + # via --index-url or --find-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), + ) + 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 = self.format_control.get_allowed_formats(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.iter_links(), search) + ) + + 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 + + def find_candidates( + self, + project_name, # type: str + specifier=None, # type: Optional[specifiers.BaseSpecifier] + ): + """Find matches for the given project and specifier. + + If given, `specifier` should implement `filter` to allow version + filtering (e.g. ``packaging.specifiers.SpecifierSet``). + + Returns a `FoundCandidates` instance. + """ + if specifier is None: + specifier = specifiers.SpecifierSet() + return FoundCandidates.from_specifier( + self.find_all_candidates(project_name), + specifier=specifier, + prereleases=(self.allow_all_prereleases or None), + evaluator=self.candidate_evaluator, + ) + + def find_requirement(self, req, upgrade): + # type: (InstallRequirement, bool) -> Optional[Link] + """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 + """ + candidates = self.find_candidates(req.name, req.specifier) + best_candidate = candidates.get_best() + + installed_version = None # type: Optional[_BaseVersion] + if req.satisfied_by is not None: + installed_version = parse_version(req.satisfied_by.version) + + def _format_versions(cand_iter): + # This repeated parse_version and str() conversion is needed to + # handle different vendoring sources from pip and pkg_resources. + # If we stop using the pkg_resources provided specifier and start + # using our own, we can drop the cast to str(). + return ", ".join(sorted( + {str(c.version) for c in cand_iter}, + key=parse_version, + )) or "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, + _format_versions(candidates.iter_all()), + ) + + 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, + _format_versions(candidates.iter_applicable()), + ) + raise BestVersionAlreadyInstalled + + logger.debug( + 'Using version %s (newest of versions: %s)', + best_candidate.version, + _format_versions(candidates.iter_applicable()), + ) + return best_candidate.location + + def _get_pages(self, locations, project_name): + # type: (Iterable[Link], str) -> Iterable[HTMLPage] + """ + Yields (page, page_url) from the given locations, skipping + locations that have errors. + """ + seen = set() # type: Set[Link] + for location in locations: + if location in seen: + continue + seen.add(location) + + page = _get_html_page(location, session=self.session) + if page is None: + continue + + yield page + + _py_version_re = re.compile(r'-py([123]\.?[0-9]?)$') + + def _sort_links(self, links): + # type: (Iterable[Link]) -> List[Link] + """ + Returns elements of links in order, non-egg links first, egg links + second, while eliminating duplicates + """ + eggs, no_eggs = [], [] + seen = set() # type: Set[Link] + 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, # type: Iterable[Link] + search # type: Search + ): + # type: (...) -> List[Optional[InstallationCandidate]] + 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): + # type: (Link, str) -> None + 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): + # type: (Link, Search) -> Optional[InstallationCandidate] + """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 None + if ext not in SUPPORTED_EXTENSIONS: + self._log_skipped_link( + link, 'unsupported archive format: %s' % ext, + ) + return None + if "binary" not in search.formats and ext == WHEEL_EXTENSION: + self._log_skipped_link( + link, 'No binaries permitted for %s' % search.supplied, + ) + return None + if "macosx10" in link.path and ext == '.zip': + self._log_skipped_link(link, 'macosx10 one') + return None + if ext == WHEEL_EXTENSION: + try: + wheel = Wheel(link.filename) + except InvalidWheelFilename: + self._log_skipped_link(link, 'invalid wheel filename') + return None + if canonicalize_name(wheel.name) != search.canonical: + self._log_skipped_link( + link, 'wrong project name (not %s)' % search.supplied) + return None + + if not self.candidate_evaluator.is_wheel_supported(wheel): + self._log_skipped_link( + link, 'it is not compatible with this Python') + return None + + 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_EXTENSION: + self._log_skipped_link( + link, 'No sources permitted for %s' % search.supplied, + ) + return None + + if not version: + version = _egg_info_matches(egg_info, search.canonical) + if not version: + self._log_skipped_link( + link, 'Missing project version for %s' % search.supplied) + return None + + 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 None + try: + support_this_python = check_requires_python(link.requires_python) + except specifiers.InvalidSpecifier: + logger.debug("Package %s has an invalid Requires-Python entry: %s", + link.filename, link.requires_python) + support_this_python = True + + if not support_this_python: + logger.debug("The package %s is incompatible with the python " + "version in use. Acceptable python versions are: %s", + link, link.requires_python) + return None + logger.debug('Found link %s, version: %s', link, version) + + return InstallationCandidate(search.supplied, version, link) + + +def _find_name_version_sep(egg_info, canonical_name): + # type: (str, str) -> int + """Find the separator's index based on the package's canonical name. + + `egg_info` must be an egg info string for the given package, and + `canonical_name` must be the package's canonical name. + + This function is needed since the canonicalized name does not necessarily + have the same length as the egg info's name part. An example:: + + >>> egg_info = 'foo__bar-1.0' + >>> canonical_name = 'foo-bar' + >>> _find_name_version_sep(egg_info, canonical_name) + 8 + """ + # Project name and version must be separated by one single dash. Find all + # occurrences of dashes; if the string in front of it matches the canonical + # name, this is the one separating the name and version parts. + for i, c in enumerate(egg_info): + if c != "-": + continue + if canonicalize_name(egg_info[:i]) == canonical_name: + return i + raise ValueError("{} does not match {}".format(egg_info, canonical_name)) + + +def _egg_info_matches(egg_info, canonical_name): + # type: (str, str) -> Optional[str] + """Pull the version part out of a string. + + :param egg_info: The string to parse. E.g. foo-2.1 + :param canonical_name: The canonicalized name of the package this + belongs to. + """ + try: + version_start = _find_name_version_sep(egg_info, canonical_name) + 1 + except ValueError: + return None + version = egg_info[version_start:] + if not version: + return None + return version + + +def _determine_base_url(document, page_url): + """Determine the HTML document's base URL. + + This looks for a ```` tag in the HTML document. If present, its href + attribute denotes the base URL of anchor tags in the document. If there is + no such tag (or if it does not have a valid href attribute), the HTML + file's URL is used as the base URL. + + :param document: An HTML document representation. The current + implementation expects the result of ``html5lib.parse()``. + :param page_url: The URL of the HTML document. + """ + for base in document.findall(".//base"): + href = base.get("href") + if href is not None: + return href + return page_url + + +def _get_encoding_from_headers(headers): + """Determine if we have any encoding information in our headers. + """ + if headers and "Content-Type" in headers: + content_type, params = cgi.parse_header(headers["Content-Type"]) + if "charset" in params: + return params['charset'] + return None + + +def _clean_link(url): + # type: (str) -> str + """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).""" + # Split the URL into parts according to the general structure + # `scheme://netloc/path;parameters?query#fragment`. Note that the + # `netloc` can be empty and the URI will then refer to a local + # filesystem path. + result = urllib_parse.urlparse(url) + # In both cases below we unquote prior to quoting to make sure + # nothing is double quoted. + if result.netloc == "": + # On Windows the path part might contain a drive letter which + # should not be quoted. On Linux where drive letters do not + # exist, the colon should be quoted. We rely on urllib.request + # to do the right thing here. + path = urllib_request.pathname2url( + urllib_request.url2pathname(result.path)) + else: + # In addition to the `/` character we protect `@` so that + # revision strings in VCS URLs are properly parsed. + path = urllib_parse.quote(urllib_parse.unquote(result.path), safe="/@") + return urllib_parse.urlunparse(result._replace(path=path)) + + +class HTMLPage(object): + """Represents one page, along with its URL""" + + def __init__(self, content, url, headers=None): + # type: (bytes, str, MutableMapping[str, str]) -> None + self.content = content + self.url = url + self.headers = headers + + def __str__(self): + return redact_password_from_url(self.url) + + def iter_links(self): + # type: () -> Iterable[Link] + """Yields all links in the page""" + document = html5lib.parse( + self.content, + transport_encoding=_get_encoding_from_headers(self.headers), + namespaceHTMLElements=False, + ) + base_url = _determine_base_url(document, self.url) + for anchor in document.findall(".//a"): + if anchor.get("href"): + href = anchor.get("href") + url = _clean_link(urllib_parse.urljoin(base_url, href)) + pyrequire = anchor.get('data-requires-python') + pyrequire = unescape(pyrequire) if pyrequire else None + yield Link(url, self.url, requires_python=pyrequire) + + +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/venv2/lib/python2.7/site-packages/pip/_internal/locations.py b/venv2/lib/python2.7/site-packages/pip/_internal/locations.py new file mode 100644 index 0000000..10860f7 --- /dev/null +++ b/venv2/lib/python2.7/site-packages/pip/_internal/locations.py @@ -0,0 +1,211 @@ +"""Locations where we look for configs, install stuff, etc""" +from __future__ import absolute_import + +import os +import os.path +import platform +import site +import sys +import sysconfig +from distutils import sysconfig as distutils_sysconfig +from distutils.command.install import SCHEME_KEYS # type: ignore + +from pip._internal.utils import appdirs +from pip._internal.utils.compat import WINDOWS, expanduser +from pip._internal.utils.typing import MYPY_CHECK_RUNNING + +if MYPY_CHECK_RUNNING: + from typing import Any, Union, Dict, List, Optional + + +# 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): + # type: (str) -> None + """ + 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(): + # type: () -> bool + """ + 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(): + # type: () -> bool + """ + 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 + else: + return False + + +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 macOS + 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_path("purelib") # type: Optional[str] + +# This is because of a bug in PyPy's sysconfig module, see +# https://bitbucket.org/pypy/pypy/issues/2506/sysconfig-returns-incorrect-paths +# for more information. +if platform.python_implementation().lower() == "pypy": + site_packages = distutils_sysconfig.get_python_lib() +try: + # Use getusersitepackages if this is present, as it ensures that the + # value is initialised properly. + user_site = site.getusersitepackages() +except AttributeError: + 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 macOS 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' + +global_config_files = [ + os.path.join(path, config_basename) + for path in appdirs.site_config_dirs('pip') +] + +site_config_file = os.path.join(sys.prefix, config_basename) +new_config_file = os.path.join(appdirs.user_config_dir("pip"), config_basename) + + +def distutils_scheme(dist_name, user=False, home=None, root=None, + isolated=False, prefix=None): + # type:(str, bool, str, str, bool, str) -> dict + """ + 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} # type: Dict[str, Union[str, List[str]]] + dist_args.update(extra_dist_args) + + d = Distribution(dist_args) + # Ignoring, typeshed issue reported python/typeshed/issues/2567 + d.parse_config_files() + # NOTE: Ignoring type since mypy can't find attributes on 'Command' + i = d.get_command_obj('install', create=True) # type: Any + assert i is not None + # 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={} prefix={}".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 + + # Ignoring, typeshed issue reported python/typeshed/issues/2567 + if 'install_lib' in d.get_option_dict('install'): # type: ignore + 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/venv2/lib/python2.7/site-packages/pip/_internal/models/__init__.py b/venv2/lib/python2.7/site-packages/pip/_internal/models/__init__.py new file mode 100644 index 0000000..7855226 --- /dev/null +++ b/venv2/lib/python2.7/site-packages/pip/_internal/models/__init__.py @@ -0,0 +1,2 @@ +"""A package that contains models that represent entities. +""" diff --git a/venv2/lib/python2.7/site-packages/pip/_internal/models/candidate.py b/venv2/lib/python2.7/site-packages/pip/_internal/models/candidate.py new file mode 100644 index 0000000..b66c365 --- /dev/null +++ b/venv2/lib/python2.7/site-packages/pip/_internal/models/candidate.py @@ -0,0 +1,31 @@ +from pip._vendor.packaging.version import parse as parse_version + +from pip._internal.utils.models import KeyBasedCompareMixin +from pip._internal.utils.typing import MYPY_CHECK_RUNNING + +if MYPY_CHECK_RUNNING: + from pip._vendor.packaging.version import _BaseVersion + from pip._internal.models.link import Link + from typing import Any + + +class InstallationCandidate(KeyBasedCompareMixin): + """Represents a potential "candidate" for installation. + """ + + def __init__(self, project, version, location): + # type: (Any, str, Link) -> None + self.project = project + self.version = parse_version(version) # type: _BaseVersion + self.location = location + + super(InstallationCandidate, self).__init__( + key=(self.project, self.version, self.location), + defining_class=InstallationCandidate + ) + + def __repr__(self): + # type: () -> str + return "".format( + self.project, self.version, self.location, + ) diff --git a/venv2/lib/python2.7/site-packages/pip/_internal/models/format_control.py b/venv2/lib/python2.7/site-packages/pip/_internal/models/format_control.py new file mode 100644 index 0000000..53138e4 --- /dev/null +++ b/venv2/lib/python2.7/site-packages/pip/_internal/models/format_control.py @@ -0,0 +1,73 @@ +from pip._vendor.packaging.utils import canonicalize_name + +from pip._internal.utils.typing import MYPY_CHECK_RUNNING + +if MYPY_CHECK_RUNNING: + from typing import Optional, Set, FrozenSet + + +class FormatControl(object): + """Helper for managing formats from which a package can be installed. + """ + + def __init__(self, no_binary=None, only_binary=None): + # type: (Optional[Set], Optional[Set]) -> None + if no_binary is None: + no_binary = set() + if only_binary is None: + only_binary = set() + + self.no_binary = no_binary + self.only_binary = only_binary + + def __eq__(self, other): + return self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not self.__eq__(other) + + def __repr__(self): + return "{}({}, {})".format( + self.__class__.__name__, + self.no_binary, + self.only_binary + ) + + @staticmethod + def handle_mutual_excludes(value, target, other): + # type: (str, Optional[Set], Optional[Set]) -> None + new = value.split(',') + while ':all:' in new: + other.clear() + target.clear() + target.add(':all:') + del new[:new.index(':all:') + 1] + # Without a none, we want to discard everything as :all: covers it + if ':none:' not in new: + return + for name in new: + if name == ':none:': + target.clear() + continue + name = canonicalize_name(name) + other.discard(name) + target.add(name) + + def get_allowed_formats(self, canonical_name): + # type: (str) -> FrozenSet + result = {"binary", "source"} + if canonical_name in self.only_binary: + result.discard('source') + elif canonical_name in self.no_binary: + result.discard('binary') + elif ':all:' in self.only_binary: + result.discard('source') + elif ':all:' in self.no_binary: + result.discard('binary') + return frozenset(result) + + def disallow_binaries(self): + # type: () -> None + self.handle_mutual_excludes( + ':all:', self.no_binary, self.only_binary, + ) diff --git a/venv2/lib/python2.7/site-packages/pip/_internal/models/index.py b/venv2/lib/python2.7/site-packages/pip/_internal/models/index.py new file mode 100644 index 0000000..ead1efb --- /dev/null +++ b/venv2/lib/python2.7/site-packages/pip/_internal/models/index.py @@ -0,0 +1,31 @@ +from pip._vendor.six.moves.urllib import parse as urllib_parse + + +class PackageIndex(object): + """Represents a Package Index and provides easier access to endpoints + """ + + def __init__(self, url, file_storage_domain): + # type: (str, str) -> None + super(PackageIndex, self).__init__() + self.url = url + self.netloc = urllib_parse.urlsplit(url).netloc + self.simple_url = self._url_for_path('simple') + self.pypi_url = self._url_for_path('pypi') + + # This is part of a temporary hack used to block installs of PyPI + # packages which depend on external urls only necessary until PyPI can + # block such packages themselves + self.file_storage_domain = file_storage_domain + + def _url_for_path(self, path): + # type: (str) -> str + return urllib_parse.urljoin(self.url, path) + + +PyPI = PackageIndex( + 'https://pypi.org/', file_storage_domain='files.pythonhosted.org' +) +TestPyPI = PackageIndex( + 'https://test.pypi.org/', file_storage_domain='test-files.pythonhosted.org' +) diff --git a/venv2/lib/python2.7/site-packages/pip/_internal/models/link.py b/venv2/lib/python2.7/site-packages/pip/_internal/models/link.py new file mode 100644 index 0000000..2f42076 --- /dev/null +++ b/venv2/lib/python2.7/site-packages/pip/_internal/models/link.py @@ -0,0 +1,163 @@ +import posixpath +import re + +from pip._vendor.six.moves.urllib import parse as urllib_parse + +from pip._internal.download import path_to_url +from pip._internal.utils.misc import ( + WHEEL_EXTENSION, redact_password_from_url, splitext, +) +from pip._internal.utils.models import KeyBasedCompareMixin +from pip._internal.utils.typing import MYPY_CHECK_RUNNING + +if MYPY_CHECK_RUNNING: + from typing import Optional, Tuple, Union + from pip._internal.index import HTMLPage + + +class Link(KeyBasedCompareMixin): + """Represents a parsed link from a Package Index's simple URL + """ + + def __init__(self, url, comes_from=None, requires_python=None): + # type: (str, Optional[Union[str, HTMLPage]], Optional[str]) -> None + """ + url: + url of the resource pointed to (href of the link) + comes_from: + instance of HTMLPage where the link was found, or string. + requires_python: + String containing the `Requires-Python` metadata field, specified + in PEP 345. This may be specified by a data-requires-python + attribute in the HTML link tag, as described in PEP 503. + """ + + # url can be a UNC windows share + if url.startswith('\\\\'): + url = path_to_url(url) + + self.url = url + self.comes_from = comes_from + self.requires_python = requires_python if requires_python else None + + super(Link, self).__init__( + key=(self.url), + defining_class=Link + ) + + def __str__(self): + if self.requires_python: + rp = ' (requires-python:%s)' % self.requires_python + else: + rp = '' + if self.comes_from: + return '%s (from %s)%s' % (redact_password_from_url(self.url), + self.comes_from, rp) + else: + return redact_password_from_url(str(self.url)) + + def __repr__(self): + return '' % self + + @property + def filename(self): + # type: () -> str + _, 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): + # type: () -> str + return urllib_parse.urlsplit(self.url)[0] + + @property + def netloc(self): + # type: () -> str + return urllib_parse.urlsplit(self.url)[1] + + @property + def path(self): + # type: () -> str + return urllib_parse.unquote(urllib_parse.urlsplit(self.url)[2]) + + def splitext(self): + # type: () -> Tuple[str, str] + return splitext(posixpath.basename(self.path.rstrip('/'))) + + @property + def ext(self): + # type: () -> str + return self.splitext()[1] + + @property + def url_without_fragment(self): + # type: () -> str + 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): + # type: () -> Optional[str] + 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): + # type: () -> Optional[str] + 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): + # type: () -> Optional[str] + match = self._hash_re.search(self.url) + if match: + return match.group(2) + return None + + @property + def hash_name(self): + # type: () -> Optional[str] + match = self._hash_re.search(self.url) + if match: + return match.group(1) + return None + + @property + def show_url(self): + # type: () -> Optional[str] + return posixpath.basename(self.url.split('#', 1)[0].split('?', 1)[0]) + + @property + def is_wheel(self): + # type: () -> bool + return self.ext == WHEEL_EXTENSION + + @property + def is_artifact(self): + # type: () -> bool + """ + 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._internal.vcs import vcs + + if self.scheme in vcs.all_schemes: + return False + + return True diff --git a/venv2/lib/python2.7/site-packages/pip/_internal/operations/__init__.py b/venv2/lib/python2.7/site-packages/pip/_internal/operations/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/venv2/lib/python2.7/site-packages/pip/_internal/operations/check.py b/venv2/lib/python2.7/site-packages/pip/_internal/operations/check.py new file mode 100644 index 0000000..920df5d --- /dev/null +++ b/venv2/lib/python2.7/site-packages/pip/_internal/operations/check.py @@ -0,0 +1,155 @@ +"""Validation of dependencies of packages +""" + +import logging +from collections import namedtuple + +from pip._vendor.packaging.utils import canonicalize_name +from pip._vendor.pkg_resources import RequirementParseError + +from pip._internal.operations.prepare import make_abstract_dist +from pip._internal.utils.misc import get_installed_distributions +from pip._internal.utils.typing import MYPY_CHECK_RUNNING + +logger = logging.getLogger(__name__) + +if MYPY_CHECK_RUNNING: + from pip._internal.req.req_install import InstallRequirement + from typing import ( + Any, Callable, Dict, Optional, Set, Tuple, List + ) + + # Shorthands + PackageSet = Dict[str, 'PackageDetails'] + Missing = Tuple[str, Any] + Conflicting = Tuple[str, str, Any] + + MissingDict = Dict[str, List[Missing]] + ConflictingDict = Dict[str, List[Conflicting]] + CheckResult = Tuple[MissingDict, ConflictingDict] + +PackageDetails = namedtuple('PackageDetails', ['version', 'requires']) + + +def create_package_set_from_installed(**kwargs): + # type: (**Any) -> Tuple[PackageSet, bool] + """Converts a list of distributions into a PackageSet. + """ + # Default to using all packages installed on the system + if kwargs == {}: + kwargs = {"local_only": False, "skip": ()} + + package_set = {} + problems = False + for dist in get_installed_distributions(**kwargs): + name = canonicalize_name(dist.project_name) + try: + package_set[name] = PackageDetails(dist.version, dist.requires()) + except RequirementParseError as e: + # Don't crash on broken metadata + logging.warning("Error parsing requirements for %s: %s", name, e) + problems = True + return package_set, problems + + +def check_package_set(package_set, should_ignore=None): + # type: (PackageSet, Optional[Callable[[str], bool]]) -> CheckResult + """Check if a package set is consistent + + If should_ignore is passed, it should be a callable that takes a + package name and returns a boolean. + """ + if should_ignore is None: + def should_ignore(name): + return False + + missing = dict() + conflicting = dict() + + for package_name in package_set: + # Info about dependencies of package_name + missing_deps = set() # type: Set[Missing] + conflicting_deps = set() # type: Set[Conflicting] + + if should_ignore(package_name): + continue + + for req in package_set[package_name].requires: + name = canonicalize_name(req.project_name) # type: str + + # Check if it's missing + if name not in package_set: + missed = True + if req.marker is not None: + missed = req.marker.evaluate() + if missed: + missing_deps.add((name, req)) + continue + + # Check if there's a conflict + version = package_set[name].version # type: str + if not req.specifier.contains(version, prereleases=True): + conflicting_deps.add((name, version, req)) + + if missing_deps: + missing[package_name] = sorted(missing_deps, key=str) + if conflicting_deps: + conflicting[package_name] = sorted(conflicting_deps, key=str) + + return missing, conflicting + + +def check_install_conflicts(to_install): + # type: (List[InstallRequirement]) -> Tuple[PackageSet, CheckResult] + """For checking if the dependency graph would be consistent after \ + installing given requirements + """ + # Start from the current state + package_set, _ = create_package_set_from_installed() + # Install packages + would_be_installed = _simulate_installation_of(to_install, package_set) + + # Only warn about directly-dependent packages; create a whitelist of them + whitelist = _create_whitelist(would_be_installed, package_set) + + return ( + package_set, + check_package_set( + package_set, should_ignore=lambda name: name not in whitelist + ) + ) + + +def _simulate_installation_of(to_install, package_set): + # type: (List[InstallRequirement], PackageSet) -> Set[str] + """Computes the version of packages after installing to_install. + """ + + # Keep track of packages that were installed + installed = set() + + # Modify it as installing requirement_set would (assuming no errors) + for inst_req in to_install: + dist = make_abstract_dist(inst_req).dist() + name = canonicalize_name(dist.key) + package_set[name] = PackageDetails(dist.version, dist.requires()) + + installed.add(name) + + return installed + + +def _create_whitelist(would_be_installed, package_set): + # type: (Set[str], PackageSet) -> Set[str] + packages_affected = set(would_be_installed) + + for package_name in package_set: + if package_name in packages_affected: + continue + + for req in package_set[package_name].requires: + if canonicalize_name(req.name) in packages_affected: + packages_affected.add(package_name) + break + + return packages_affected diff --git a/venv2/lib/python2.7/site-packages/pip/_internal/operations/freeze.py b/venv2/lib/python2.7/site-packages/pip/_internal/operations/freeze.py new file mode 100644 index 0000000..0c4c761 --- /dev/null +++ b/venv2/lib/python2.7/site-packages/pip/_internal/operations/freeze.py @@ -0,0 +1,247 @@ +from __future__ import absolute_import + +import collections +import logging +import os +import re + +from pip._vendor import six +from pip._vendor.packaging.utils import canonicalize_name +from pip._vendor.pkg_resources import RequirementParseError + +from pip._internal.exceptions import BadCommand, InstallationError +from pip._internal.req.constructors import ( + install_req_from_editable, install_req_from_line, +) +from pip._internal.req.req_file import COMMENT_RE +from pip._internal.utils.misc import ( + dist_is_editable, get_installed_distributions, +) +from pip._internal.utils.typing import MYPY_CHECK_RUNNING + +if MYPY_CHECK_RUNNING: + from typing import ( + Iterator, Optional, List, Container, Set, Dict, Tuple, Iterable, Union + ) + from pip._internal.cache import WheelCache + from pip._vendor.pkg_resources import ( + Distribution, Requirement + ) + + RequirementInfo = Tuple[Optional[Union[str, Requirement]], bool, List[str]] + + +logger = logging.getLogger(__name__) + + +def freeze( + requirement=None, # type: Optional[List[str]] + find_links=None, # type: Optional[List[str]] + local_only=None, # type: Optional[bool] + user_only=None, # type: Optional[bool] + skip_regex=None, # type: Optional[str] + isolated=False, # type: bool + wheel_cache=None, # type: Optional[WheelCache] + exclude_editable=False, # type: bool + skip=() # type: Container[str] +): + # type: (...) -> Iterator[str] + find_links = find_links or [] + skip_match = None + + if skip_regex: + skip_match = re.compile(skip_regex).search + + for link in find_links: + yield '-f %s' % link + installations = {} # type: Dict[str, FrozenRequirement] + for dist in get_installed_distributions(local_only=local_only, + skip=(), + user_only=user_only): + try: + req = FrozenRequirement.from_dist(dist) + except RequirementParseError: + logger.warning( + "Could not parse requirement: %s", + dist.project_name + ) + continue + if exclude_editable and req.editable: + continue + installations[req.name] = req + + if requirement: + # the options that don't get turned into an InstallRequirement + # should only be emitted once, even if the same option is in multiple + # requirements files, so we need to keep track of what has been emitted + # so that we don't emit it again if it's seen again + emitted_options = set() # type: Set[str] + # keep track of which files a requirement is in so that we can + # give an accurate warning if a requirement appears multiple times. + req_files = collections.defaultdict(list) # type: Dict[str, List[str]] + for req_file_path in requirement: + with open(req_file_path) 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'))): + line = line.rstrip() + if line not in emitted_options: + emitted_options.add(line) + yield line + 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 = install_req_from_editable( + line, + isolated=isolated, + wheel_cache=wheel_cache, + ) + else: + line_req = install_req_from_line( + COMMENT_RE.sub('', line).strip(), + isolated=isolated, + wheel_cache=wheel_cache, + ) + + if not line_req.name: + logger.info( + "Skipping line in requirement file [%s] because " + "it's not clear what it would install: %s", + req_file_path, line.strip(), + ) + logger.info( + " (add #egg=PackageName to the URL to avoid" + " this warning)" + ) + elif line_req.name not in installations: + # either it's not installed, or it is installed + # but has been processed already + if not req_files[line_req.name]: + logger.warning( + "Requirement file [%s] contains %s, but " + "package %r is not installed", + req_file_path, + COMMENT_RE.sub('', line).strip(), line_req.name + ) + else: + req_files[line_req.name].append(req_file_path) + else: + yield str(installations[line_req.name]).rstrip() + del installations[line_req.name] + req_files[line_req.name].append(req_file_path) + + # Warn about requirements that were included multiple times (in a + # single requirements file or in different requirements files). + for name, files in six.iteritems(req_files): + if len(files) > 1: + logger.warning("Requirement %s included multiple times [%s]", + name, ', '.join(sorted(set(files)))) + + 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() + + +def get_requirement_info(dist): + # type: (Distribution) -> RequirementInfo + """ + Compute and return values (req, editable, comments) for use in + FrozenRequirement.from_dist(). + """ + if not dist_is_editable(dist): + return (None, False, []) + + location = os.path.normcase(os.path.abspath(dist.location)) + + from pip._internal.vcs import vcs, RemoteNotFoundError + vc_type = vcs.get_backend_type(location) + + if not vc_type: + req = dist.as_requirement() + logger.debug( + 'No VCS found for editable requirement {!r} in: {!r}', req, + location, + ) + comments = [ + '# Editable install with no version control ({})'.format(req) + ] + return (location, True, comments) + + try: + req = vc_type.get_src_requirement(location, dist.project_name) + except RemoteNotFoundError: + req = dist.as_requirement() + comments = [ + '# Editable {} install with no remote ({})'.format( + vc_type.__name__, req, + ) + ] + return (location, True, comments) + + except BadCommand: + logger.warning( + 'cannot determine version of editable source in %s ' + '(%s command not found in path)', + location, + vc_type.name, + ) + return (None, True, []) + + except InstallationError as exc: + logger.warning( + "Error when trying to get requirement for VCS system %s, " + "falling back to uneditable format", exc + ) + else: + if req is not None: + return (req, True, []) + + logger.warning( + 'Could not determine repository location of %s', location + ) + comments = ['## !! Could not determine repository location'] + + return (None, False, comments) + + +class FrozenRequirement(object): + def __init__(self, name, req, editable, comments=()): + # type: (str, Union[str, Requirement], bool, Iterable[str]) -> None + self.name = name + self.req = req + self.editable = editable + self.comments = comments + + @classmethod + def from_dist(cls, dist): + # type: (Distribution) -> FrozenRequirement + req, editable, comments = get_requirement_info(dist) + if req is None: + req = dist.as_requirement() + + return cls(dist.project_name, req, editable, comments=comments) + + def __str__(self): + req = self.req + if self.editable: + req = '-e %s' % req + return '\n'.join(list(self.comments) + [str(req)]) + '\n' diff --git a/venv2/lib/python2.7/site-packages/pip/_internal/operations/prepare.py b/venv2/lib/python2.7/site-packages/pip/_internal/operations/prepare.py new file mode 100644 index 0000000..077a985 --- /dev/null +++ b/venv2/lib/python2.7/site-packages/pip/_internal/operations/prepare.py @@ -0,0 +1,426 @@ +"""Prepares a distribution for installation +""" + +import logging +import os + +from pip._vendor import pkg_resources, requests + +from pip._internal.build_env import BuildEnvironment +from pip._internal.download import ( + is_dir_url, is_file_url, is_vcs_url, unpack_url, url_to_path, +) +from pip._internal.exceptions import ( + DirectoryUrlHashUnsupported, HashUnpinned, InstallationError, + PreviousBuildDirError, VcsHashUnsupported, +) +from pip._internal.utils.compat import expanduser +from pip._internal.utils.hashes import MissingHashes +from pip._internal.utils.logging import indent_log +from pip._internal.utils.misc import display_path, normalize_path +from pip._internal.utils.typing import MYPY_CHECK_RUNNING + +if MYPY_CHECK_RUNNING: + from typing import Any, Optional + from pip._internal.req.req_install import InstallRequirement + from pip._internal.index import PackageFinder + from pip._internal.download import PipSession + from pip._internal.req.req_tracker import RequirementTracker + +logger = logging.getLogger(__name__) + + +def make_abstract_dist(req): + # type: (InstallRequirement) -> DistAbstraction + """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.editable: + return IsSDist(req) + elif req.link and req.link.is_wheel: + return IsWheel(req) + else: + return IsSDist(req) + + +class DistAbstraction(object): + """Abstracts out the wheel vs non-wheel Resolver.resolve() 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): + # type: (InstallRequirement) -> None + self.req = req # type: InstallRequirement + + def dist(self): + # type: () -> Any + """Return a setuptools Dist object.""" + raise NotImplementedError + + def prep_for_dist(self, finder, build_isolation): + # type: (PackageFinder, bool) -> Any + """Ensure that we can get a Dist for this requirement.""" + raise NotImplementedError + + +class IsWheel(DistAbstraction): + + def dist(self): + # type: () -> pkg_resources.Distribution + return list(pkg_resources.find_distributions( + self.req.source_dir))[0] + + def prep_for_dist(self, finder, build_isolation): + # type: (PackageFinder, bool) -> Any + # FIXME:https://github.com/pypa/pip/issues/1112 + pass + + +class IsSDist(DistAbstraction): + + def dist(self): + return self.req.get_dist() + + def _raise_conflicts(self, conflicting_with, conflicting_reqs): + conflict_messages = [ + '%s is incompatible with %s' % (installed, wanted) + for installed, wanted in sorted(conflicting_reqs) + ] + raise InstallationError( + "Some build dependencies for %s conflict with %s: %s." % ( + self.req, conflicting_with, ', '.join(conflict_messages)) + ) + + def install_backend_dependencies(self, finder): + # type: (PackageFinder) -> None + """ + Install any extra build dependencies that the backend requests. + + :param finder: a PackageFinder object. + """ + req = self.req + with req.build_env: + # We need to have the env active when calling the hook. + req.spin_message = "Getting requirements to build wheel" + reqs = req.pep517_backend.get_requires_for_build_wheel() + conflicting, missing = req.build_env.check_requirements(reqs) + if conflicting: + self._raise_conflicts("the backend dependencies", conflicting) + req.build_env.install_requirements( + finder, missing, 'normal', + "Installing backend dependencies" + ) + + def prep_for_dist(self, finder, build_isolation): + # type: (PackageFinder, bool) -> None + # Prepare for building. We need to: + # 1. Load pyproject.toml (if it exists) + # 2. Set up the build environment + + self.req.load_pyproject_toml() + should_isolate = self.req.use_pep517 and build_isolation + + if should_isolate: + # Isolate in a BuildEnvironment and install the build-time + # requirements. + self.req.build_env = BuildEnvironment() + self.req.build_env.install_requirements( + finder, self.req.pyproject_requires, 'overlay', + "Installing build dependencies" + ) + conflicting, missing = self.req.build_env.check_requirements( + self.req.requirements_to_check + ) + if conflicting: + self._raise_conflicts("PEP 517/518 supported requirements", + conflicting) + if missing: + logger.warning( + "Missing build requirements in pyproject.toml for %s.", + self.req, + ) + logger.warning( + "The project does not specify a build backend, and " + "pip cannot fall back to setuptools without %s.", + " and ".join(map(repr, sorted(missing))) + ) + + # Install any extra build dependencies that the backend requests. + # This must be done in a second pass, as the pyproject.toml + # dependencies must be installed before we can call the backend. + self.install_backend_dependencies(finder=finder) + + self.req.prepare_metadata() + self.req.assert_source_matches_version() + + +class Installed(DistAbstraction): + + def dist(self): + # type: () -> pkg_resources.Distribution + return self.req.satisfied_by + + def prep_for_dist(self, finder, build_isolation): + # type: (PackageFinder, bool) -> Any + pass + + +class RequirementPreparer(object): + """Prepares a Requirement + """ + + def __init__( + self, + build_dir, # type: str + download_dir, # type: Optional[str] + src_dir, # type: str + wheel_download_dir, # type: Optional[str] + progress_bar, # type: str + build_isolation, # type: bool + req_tracker # type: RequirementTracker + ): + # type: (...) -> None + super(RequirementPreparer, self).__init__() + + self.src_dir = src_dir + self.build_dir = build_dir + self.req_tracker = req_tracker + + # Where still packed archives should be written to. If None, they are + # not saved, and are deleted immediately after unpacking. + self.download_dir = 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. + if wheel_download_dir: + wheel_download_dir = normalize_path(wheel_download_dir) + self.wheel_download_dir = wheel_download_dir + + # NOTE + # 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.progress_bar = progress_bar + + # Is build isolation allowed? + self.build_isolation = build_isolation + + @property + def _download_should_save(self): + # type: () -> bool + # TODO: Modify to reduce indentation needed + 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 prepare_linked_requirement( + self, + req, # type: InstallRequirement + session, # type: PipSession + finder, # type: PackageFinder + upgrade_allowed, # type: bool + require_hashes # type: bool + ): + # type: (...) -> DistAbstraction + """Prepare a requirement that would be obtained from req.link + """ + # TODO: Breakup into smaller functions + if req.link and req.link.scheme == 'file': + path = url_to_path(req.link.url) + logger.info('Processing %s', display_path(path)) + else: + logger.info('Collecting %s', req) + + with indent_log(): + # @@ 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.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.source_dir` + # package unpacked in `req.source_dir` + if os.path.exists(os.path.join(req.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, req.source_dir) + ) + req.populate_link(finder, upgrade_allowed, require_hashes) + + # We can't hit this spot and have populate_link return None. + # req.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.link + link = req.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.original_link and not req.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.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.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.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.link, req.source_dir, + download_dir, autodelete_unpacked, + session=session, hashes=hashes, + progress_bar=self.progress_bar + ) + except requests.HTTPError as exc: + logger.critical( + 'Could not install requirement %s because of error %s', + req, + exc, + ) + raise InstallationError( + 'Could not install requirement %s because of HTTP ' + 'error %s for URL %s' % + (req, exc, req.link) + ) + abstract_dist = make_abstract_dist(req) + with self.req_tracker.track(req): + abstract_dist.prep_for_dist(finder, self.build_isolation) + if self._download_should_save: + # Make a .zip of the source_dir we already created. + if not req.link.is_artifact: + req.archive(self.download_dir) + return abstract_dist + + def prepare_editable_requirement( + self, + req, # type: InstallRequirement + require_hashes, # type: bool + use_user_site, # type: bool + finder # type: PackageFinder + ): + # type: (...) -> DistAbstraction + """Prepare an editable requirement + """ + assert req.editable, "cannot prepare a non-editable req as editable" + + logger.info('Obtaining %s', req) + + with indent_log(): + if require_hashes: + raise InstallationError( + 'The editable requirement %s cannot be installed when ' + 'requiring hashes, because there is no single file to ' + 'hash.' % req + ) + req.ensure_has_source_dir(self.src_dir) + req.update_editable(not self._download_should_save) + + abstract_dist = make_abstract_dist(req) + with self.req_tracker.track(req): + abstract_dist.prep_for_dist(finder, self.build_isolation) + + if self._download_should_save: + req.archive(self.download_dir) + req.check_if_exists(use_user_site) + + return abstract_dist + + def prepare_installed_requirement(self, req, require_hashes, skip_reason): + # type: (InstallRequirement, bool, Optional[str]) -> DistAbstraction + """Prepare an already-installed requirement + """ + assert req.satisfied_by, "req should have been satisfied but isn't" + assert skip_reason is not None, ( + "did not get skip reason skipped but req.satisfied_by " + "is set to %r" % (req.satisfied_by,) + ) + logger.info( + 'Requirement %s: %s (%s)', + skip_reason, req, req.satisfied_by.version + ) + with indent_log(): + 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) + + return abstract_dist diff --git a/venv2/lib/python2.7/site-packages/pip/_internal/pep425tags.py b/venv2/lib/python2.7/site-packages/pip/_internal/pep425tags.py new file mode 100644 index 0000000..3b68f28 --- /dev/null +++ b/venv2/lib/python2.7/site-packages/pip/_internal/pep425tags.py @@ -0,0 +1,381 @@ +"""Generate and work with PEP 425 Compatibility Tags.""" +from __future__ import absolute_import + +import distutils.util +import logging +import platform +import re +import sys +import sysconfig +import warnings +from collections import OrderedDict + +import pip._internal.utils.glibc +from pip._internal.utils.compat import get_extension_suffixes +from pip._internal.utils.typing import MYPY_CHECK_RUNNING + +if MYPY_CHECK_RUNNING: + from typing import ( + Tuple, Callable, List, Optional, Union, Dict + ) + + Pep425Tag = Tuple[str, str, str] + +logger = logging.getLogger(__name__) + +_osx_arch_pat = re.compile(r'(.+)_(\d+)_(\d+)_(.+)') + + +def get_config_var(var): + # type: (str) -> Optional[str] + try: + return sysconfig.get_config_var(var) + except IOError as e: # Issue #1074 + warnings.warn("{}".format(e), RuntimeWarning) + return None + + +def get_abbr_impl(): + # type: () -> str + """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(): + # type: () -> str + """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(): + # type: () -> Tuple[int, ...] + """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 + # attrs exist only on pypy + return (sys.version_info[0], + sys.pypy_version_info.major, # type: ignore + sys.pypy_version_info.minor) # type: ignore + else: + return sys.version_info[0], sys.version_info[1] + + +def get_impl_tag(): + # type: () -> str + """ + Returns the Tag for this specific implementation. + """ + return "{}{}".format(get_abbr_impl(), get_impl_ver()) + + +def get_flag(var, fallback, expected=True, warn=True): + # type: (str, Callable[..., bool], Union[bool, int], bool) -> bool + """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(): + # type: () -> Optional[str] + """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(): + # type: () -> bool + return sys.maxsize == 2147483647 + + +def get_platform(): + # type: () -> str + """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 significantly 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_{}_{}_{}'.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(): + # type: () -> bool + # 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 pip._internal.utils.glibc.have_compatible_glibc(2, 5) + + +def is_manylinux2010_compatible(): + # type: () -> bool + # 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.manylinux2010_compatible) + except (ImportError, AttributeError): + # Fall through to heuristic check below + pass + + # Check glibc version. CentOS 6 uses glibc 2.12. + return pip._internal.utils.glibc.have_compatible_glibc(2, 12) + + +def get_darwin_arches(major, minor, machine): + # type: (int, int, str) -> List[str] + """Return a list of supported arches (including group arches) for + the given major, minor and machine architecture of an macOS machine. + """ + arches = [] + + def _supports_arch(major, minor, arch): + # type: (int, int, str) -> bool + # Looking at the application support for macOS 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")), + ]) # type: Dict[str, Tuple[str, ...]] + + 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_all_minor_versions_as_strings(version_info): + # type: (Tuple[int, ...]) -> List[str] + versions = [] + 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,)))) + return versions + + +def get_supported( + versions=None, # type: Optional[List[str]] + noarch=False, # type: bool + platform=None, # type: Optional[str] + impl=None, # type: Optional[str] + abi=None # type: Optional[str] +): + # type: (...) -> List[Pep425Tag] + """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. + :param platform: specify the exact platform you want valid + tags for, or None. If None, use the local system platform. + :param impl: specify the exact implementation you want valid + tags for, or None. If None, use the local interpreter impl. + :param abi: specify the exact abi you want valid + tags for, or None. If None, use the local interpreter abi. + """ + supported = [] + + # Versions must be given with respect to the preference + if versions is None: + version_info = get_impl_version_info() + versions = get_all_minor_versions_as_strings(version_info) + + impl = impl or get_abbr_impl() + + abis = [] # type: List[str] + + abi = abi or get_abi_tag() + if abi: + abis[0:0] = [abi] + + abi3s = set() + for suffix in get_extension_suffixes(): + if suffix.startswith('.abi'): + abi3s.add(suffix.split('.', 2)[1]) + + abis.extend(sorted(list(abi3s))) + + abis.append('none') + + if not noarch: + arch = platform or get_platform() + arch_prefix, arch_sep, arch_suffix = arch.partition('_') + if arch.startswith('macosx'): + # 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 = '{}_{}_%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 arch_prefix == 'manylinux2010': + # manylinux1 wheels run on most manylinux2010 systems with the + # exception of wheels depending on ncurses. PEP 571 states + # manylinux1 wheels should be considered manylinux2010 wheels: + # https://www.python.org/dev/peps/pep-0571/#backwards-compatibility-with-manylinux1-wheels + arches = [arch, 'manylinux1' + arch_sep + arch_suffix] + elif platform is None: + arches = [] + if is_manylinux2010_compatible(): + arches.append('manylinux2010' + arch_sep + arch_suffix) + if is_manylinux1_compatible(): + arches.append('manylinux1' + arch_sep + arch_suffix) + arches.append(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)) + + # abi3 modules compatible with older version of Python + for version in versions[1:]: + # abi3 was introduced in Python 3.2 + if version in {'31', '30'}: + break + for abi in abi3s: # empty set if not Python 3 + for arch in arches: + supported.append(("%s%s" % (impl, version), 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 + + +implementation_tag = get_impl_tag() diff --git a/venv2/lib/python2.7/site-packages/pip/_internal/pyproject.py b/venv2/lib/python2.7/site-packages/pip/_internal/pyproject.py new file mode 100644 index 0000000..43efbed --- /dev/null +++ b/venv2/lib/python2.7/site-packages/pip/_internal/pyproject.py @@ -0,0 +1,171 @@ +from __future__ import absolute_import + +import io +import os +import sys + +from pip._vendor import pytoml, six + +from pip._internal.exceptions import InstallationError +from pip._internal.utils.typing import MYPY_CHECK_RUNNING + +if MYPY_CHECK_RUNNING: + from typing import Any, Tuple, Optional, List + + +def _is_list_of_str(obj): + # type: (Any) -> bool + return ( + isinstance(obj, list) and + all(isinstance(item, six.string_types) for item in obj) + ) + + +def make_pyproject_path(setup_py_dir): + # type: (str) -> str + path = os.path.join(setup_py_dir, 'pyproject.toml') + + # Python2 __file__ should not be unicode + if six.PY2 and isinstance(path, six.text_type): + path = path.encode(sys.getfilesystemencoding()) + + return path + + +def load_pyproject_toml( + use_pep517, # type: Optional[bool] + pyproject_toml, # type: str + setup_py, # type: str + req_name # type: str +): + # type: (...) -> Optional[Tuple[List[str], str, List[str]]] + """Load the pyproject.toml file. + + Parameters: + use_pep517 - Has the user requested PEP 517 processing? None + means the user hasn't explicitly specified. + pyproject_toml - Location of the project's pyproject.toml file + setup_py - Location of the project's setup.py file + req_name - The name of the requirement we're processing (for + error reporting) + + Returns: + None if we should use the legacy code path, otherwise a tuple + ( + requirements from pyproject.toml, + name of PEP 517 backend, + requirements we should check are installed after setting + up the build environment + ) + """ + has_pyproject = os.path.isfile(pyproject_toml) + has_setup = os.path.isfile(setup_py) + + if has_pyproject: + with io.open(pyproject_toml, encoding="utf-8") as f: + pp_toml = pytoml.load(f) + build_system = pp_toml.get("build-system") + else: + build_system = None + + # The following cases must use PEP 517 + # We check for use_pep517 being non-None and falsey because that means + # the user explicitly requested --no-use-pep517. The value 0 as + # opposed to False can occur when the value is provided via an + # environment variable or config file option (due to the quirk of + # strtobool() returning an integer in pip's configuration code). + if has_pyproject and not has_setup: + if use_pep517 is not None and not use_pep517: + raise InstallationError( + "Disabling PEP 517 processing is invalid: " + "project does not have a setup.py" + ) + use_pep517 = True + elif build_system and "build-backend" in build_system: + if use_pep517 is not None and not use_pep517: + raise InstallationError( + "Disabling PEP 517 processing is invalid: " + "project specifies a build backend of {} " + "in pyproject.toml".format( + build_system["build-backend"] + ) + ) + use_pep517 = True + + # If we haven't worked out whether to use PEP 517 yet, + # and the user hasn't explicitly stated a preference, + # we do so if the project has a pyproject.toml file. + elif use_pep517 is None: + use_pep517 = has_pyproject + + # At this point, we know whether we're going to use PEP 517. + assert use_pep517 is not None + + # If we're using the legacy code path, there is nothing further + # for us to do here. + if not use_pep517: + return None + + if build_system is None: + # Either the user has a pyproject.toml with no build-system + # section, or the user has no pyproject.toml, but has opted in + # explicitly via --use-pep517. + # In the absence of any explicit backend specification, we + # assume the setuptools backend that most closely emulates the + # traditional direct setup.py execution, and require wheel and + # a version of setuptools that supports that backend. + + build_system = { + "requires": ["setuptools>=40.8.0", "wheel"], + "build-backend": "setuptools.build_meta:__legacy__", + } + + # If we're using PEP 517, we have build system information (either + # from pyproject.toml, or defaulted by the code above). + # Note that at this point, we do not know if the user has actually + # specified a backend, though. + assert build_system is not None + + # Ensure that the build-system section in pyproject.toml conforms + # to PEP 518. + error_template = ( + "{package} has a pyproject.toml file that does not comply " + "with PEP 518: {reason}" + ) + + # Specifying the build-system table but not the requires key is invalid + if "requires" not in build_system: + raise InstallationError( + error_template.format(package=req_name, reason=( + "it has a 'build-system' table but not " + "'build-system.requires' which is mandatory in the table" + )) + ) + + # Error out if requires is not a list of strings + requires = build_system["requires"] + if not _is_list_of_str(requires): + raise InstallationError(error_template.format( + package=req_name, + reason="'build-system.requires' is not a list of strings.", + )) + + backend = build_system.get("build-backend") + check = [] # type: List[str] + if backend is None: + # If the user didn't specify a backend, we assume they want to use + # the setuptools backend. But we can't be sure they have included + # a version of setuptools which supplies the backend, or wheel + # (which is needed by the backend) in their requirements. So we + # make a note to check that those requirements are present once + # we have set up the environment. + # This is quite a lot of work to check for a very specific case. But + # the problem is, that case is potentially quite common - projects that + # adopted PEP 518 early for the ability to specify requirements to + # execute setup.py, but never considered needing to mention the build + # tools themselves. The original PEP 518 code had a similar check (but + # implemented in a different way). + backend = "setuptools.build_meta:__legacy__" + check = ["setuptools>=40.8.0", "wheel"] + + return (requires, backend, check) diff --git a/venv2/lib/python2.7/site-packages/pip/_internal/req/__init__.py b/venv2/lib/python2.7/site-packages/pip/_internal/req/__init__.py new file mode 100644 index 0000000..c39f63f --- /dev/null +++ b/venv2/lib/python2.7/site-packages/pip/_internal/req/__init__.py @@ -0,0 +1,78 @@ +from __future__ import absolute_import + +import logging + +from .req_install import InstallRequirement +from .req_set import RequirementSet +from .req_file import parse_requirements +from pip._internal.utils.logging import indent_log +from pip._internal.utils.typing import MYPY_CHECK_RUNNING + +if MYPY_CHECK_RUNNING: + from typing import Any, List, Sequence + +__all__ = [ + "RequirementSet", "InstallRequirement", + "parse_requirements", "install_given_reqs", +] + +logger = logging.getLogger(__name__) + + +def install_given_reqs( + to_install, # type: List[InstallRequirement] + install_options, # type: List[str] + global_options=(), # type: Sequence[str] + *args, # type: Any + **kwargs # type: Any +): + # type: (...) -> List[InstallRequirement] + """ + Install everything in the given list. + + (to be called after having downloaded and unpacked the packages) + """ + + 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(): + uninstalled_pathset = requirement.uninstall( + auto_confirm=True + ) + try: + requirement.install( + install_options, + global_options, + *args, + **kwargs + ) + except Exception: + should_rollback = ( + requirement.conflicts_with and + not requirement.install_succeeded + ) + # if install did not succeed, rollback previous uninstall + if should_rollback: + uninstalled_pathset.rollback() + raise + else: + should_commit = ( + requirement.conflicts_with and + requirement.install_succeeded + ) + if should_commit: + uninstalled_pathset.commit() + requirement.remove_temporary_source() + + return to_install diff --git a/venv2/lib/python2.7/site-packages/pip/_internal/req/constructors.py b/venv2/lib/python2.7/site-packages/pip/_internal/req/constructors.py new file mode 100644 index 0000000..0f18b6a --- /dev/null +++ b/venv2/lib/python2.7/site-packages/pip/_internal/req/constructors.py @@ -0,0 +1,340 @@ +"""Backing implementation for InstallRequirement's various constructors + +The idea here is that these formed a major chunk of InstallRequirement's size +so, moving them and support code dedicated to them outside of that class +helps creates for better understandability for the rest of the code. + +These are meant to be used elsewhere within pip to create instances of +InstallRequirement. +""" + +import logging +import os +import re + +from pip._vendor.packaging.markers import Marker +from pip._vendor.packaging.requirements import InvalidRequirement, Requirement +from pip._vendor.packaging.specifiers import Specifier +from pip._vendor.pkg_resources import RequirementParseError, parse_requirements + +from pip._internal.download import ( + is_archive_file, is_url, path_to_url, url_to_path, +) +from pip._internal.exceptions import InstallationError +from pip._internal.models.index import PyPI, TestPyPI +from pip._internal.models.link import Link +from pip._internal.pyproject import make_pyproject_path +from pip._internal.req.req_install import InstallRequirement +from pip._internal.utils.misc import is_installable_dir +from pip._internal.utils.typing import MYPY_CHECK_RUNNING +from pip._internal.vcs import vcs +from pip._internal.wheel import Wheel + +if MYPY_CHECK_RUNNING: + from typing import ( + Any, Dict, Optional, Set, Tuple, Union, + ) + from pip._internal.cache import WheelCache + + +__all__ = [ + "install_req_from_editable", "install_req_from_line", + "parse_editable" +] + +logger = logging.getLogger(__name__) +operators = Specifier._operators.keys() + + +def _strip_extras(path): + # type: (str) -> Tuple[str, Optional[str]] + 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 + + +def parse_editable(editable_req): + # type: (str) -> Tuple[Optional[str], str, Optional[Set[str]]] + """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] + """ + + url = editable_req + + # If a file path is specified with extras, strip off the extras. + url_no_extras, extras = _strip_extras(url) + + if os.path.isdir(url_no_extras): + if not os.path.exists(os.path.join(url_no_extras, 'setup.py')): + msg = ( + 'File "setup.py" not found. Directory cannot be installed ' + 'in editable mode: {}'.format(os.path.abspath(url_no_extras)) + ) + pyproject_path = make_pyproject_path(url_no_extras) + if os.path.isfile(pyproject_path): + msg += ( + '\n(A "pyproject.toml" file was found, but editable ' + 'mode currently requires a setup.py based build.)' + ) + raise InstallationError(msg) + + # 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.lower()).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: + 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: + raise InstallationError( + "Could not detect requirement name for '%s', please specify one " + "with #egg=your_package_name" % editable_req + ) + return package_name, url, None + + +def deduce_helpful_msg(req): + # type: (str) -> str + """Returns helpful msg in case requirements file does not exist, + or cannot be parsed. + + :params req: Requirements file path + """ + msg = "" + if os.path.exists(req): + msg = " It does exist." + # Try to parse and check if it is a requirements file. + try: + with open(req, 'r') as fp: + # parse first line only + next(parse_requirements(fp.read())) + msg += " The argument you provided " + \ + "(%s) appears to be a" % (req) + \ + " requirements file. If that is the" + \ + " case, use the '-r' flag to install" + \ + " the packages specified within it." + except RequirementParseError: + logger.debug("Cannot parse '%s' as requirements \ + file" % (req), exc_info=True) + else: + msg += " File '%s' does not exist." % (req) + return msg + + +# ---- The actual constructors follow ---- + + +def install_req_from_editable( + editable_req, # type: str + comes_from=None, # type: Optional[str] + use_pep517=None, # type: Optional[bool] + isolated=False, # type: bool + options=None, # type: Optional[Dict[str, Any]] + wheel_cache=None, # type: Optional[WheelCache] + constraint=False # type: bool +): + # type: (...) -> InstallRequirement + name, url, extras_override = parse_editable(editable_req) + if url.startswith('file:'): + source_dir = url_to_path(url) + else: + source_dir = None + + if name is not None: + try: + req = Requirement(name) + except InvalidRequirement: + raise InstallationError("Invalid requirement: '%s'" % name) + else: + req = None + return InstallRequirement( + req, comes_from, source_dir=source_dir, + editable=True, + link=Link(url), + constraint=constraint, + use_pep517=use_pep517, + isolated=isolated, + options=options if options else {}, + wheel_cache=wheel_cache, + extras=extras_override or (), + ) + + +def install_req_from_line( + name, # type: str + comes_from=None, # type: Optional[Union[str, InstallRequirement]] + use_pep517=None, # type: Optional[bool] + isolated=False, # type: bool + options=None, # type: Optional[Dict[str, Any]] + wheel_cache=None, # type: Optional[WheelCache] + constraint=False # type: bool +): + # type: (...) -> InstallRequirement + """Creates an InstallRequirement from a name, which might be a + requirement, directory containing 'setup.py', filename, or URL. + """ + if is_url(name): + marker_sep = '; ' + else: + marker_sep = ';' + if marker_sep in name: + name, markers_as_string = name.split(marker_sep, 1) + markers_as_string = markers_as_string.strip() + if not markers_as_string: + markers = None + else: + markers = Marker(markers_as_string) + else: + markers = None + name = name.strip() + req_as_string = None + path = os.path.normpath(os.path.abspath(name)) + link = None + extras_as_string = None + + if is_url(name): + link = Link(name) + else: + p, extras_as_string = _strip_extras(path) + looks_like_dir = os.path.isdir(p) and ( + os.path.sep in name or + (os.path.altsep is not None and os.path.altsep in name) or + name.startswith('.') + ) + if looks_like_dir: + if not is_installable_dir(p): + raise InstallationError( + "Directory %r is not installable. Neither 'setup.py' " + "nor 'pyproject.toml' 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 + req_as_string = "%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_as_string = link.egg_fragment + + # a requirement specifier + else: + req_as_string = name + + if extras_as_string: + extras = Requirement("placeholder" + extras_as_string.lower()).extras + else: + extras = () + if req_as_string is not None: + try: + req = Requirement(req_as_string) + except InvalidRequirement: + if os.path.sep in req_as_string: + add_msg = "It looks like a path." + add_msg += deduce_helpful_msg(req_as_string) + elif ('=' in req_as_string and + not any(op in req_as_string for op in operators)): + add_msg = "= is not a valid operator. Did you mean == ?" + else: + add_msg = "" + raise InstallationError( + "Invalid requirement: '%s'\n%s" % (req_as_string, add_msg) + ) + else: + req = None + + return InstallRequirement( + req, comes_from, link=link, markers=markers, + use_pep517=use_pep517, isolated=isolated, + options=options if options else {}, + wheel_cache=wheel_cache, + constraint=constraint, + extras=extras, + ) + + +def install_req_from_req_string( + req_string, # type: str + comes_from=None, # type: Optional[InstallRequirement] + isolated=False, # type: bool + wheel_cache=None, # type: Optional[WheelCache] + use_pep517=None # type: Optional[bool] +): + # type: (...) -> InstallRequirement + try: + req = Requirement(req_string) + except InvalidRequirement: + raise InstallationError("Invalid requirement: '%s'" % req_string) + + domains_not_allowed = [ + PyPI.file_storage_domain, + TestPyPI.file_storage_domain, + ] + if (req.url and comes_from and comes_from.link and + comes_from.link.netloc in domains_not_allowed): + # Explicitly disallow pypi packages that depend on external urls + raise InstallationError( + "Packages installed from PyPI cannot depend on packages " + "which are not also hosted on PyPI.\n" + "%s depends on %s " % (comes_from.name, req) + ) + + return InstallRequirement( + req, comes_from, isolated=isolated, wheel_cache=wheel_cache, + use_pep517=use_pep517 + ) diff --git a/venv2/lib/python2.7/site-packages/pip/_internal/req/req_file.py b/venv2/lib/python2.7/site-packages/pip/_internal/req/req_file.py new file mode 100644 index 0000000..270b75c --- /dev/null +++ b/venv2/lib/python2.7/site-packages/pip/_internal/req/req_file.py @@ -0,0 +1,383 @@ +""" +Requirements file parsing +""" + +from __future__ import absolute_import + +import optparse +import os +import re +import shlex +import sys + +from pip._vendor.six.moves import filterfalse +from pip._vendor.six.moves.urllib import parse as urllib_parse + +from pip._internal.cli import cmdoptions +from pip._internal.download import get_file_content +from pip._internal.exceptions import RequirementsFileParseError +from pip._internal.req.constructors import ( + install_req_from_editable, install_req_from_line, +) +from pip._internal.utils.typing import MYPY_CHECK_RUNNING + +if MYPY_CHECK_RUNNING: + from typing import ( + Any, Callable, Iterator, List, NoReturn, Optional, Text, Tuple, + ) + from pip._internal.req import InstallRequirement + from pip._internal.cache import WheelCache + from pip._internal.index import PackageFinder + from pip._internal.download import PipSession + + ReqFileLines = Iterator[Tuple[int, Text]] + +__all__ = ['parse_requirements'] + +SCHEME_RE = re.compile(r'^(http|https|file):', re.I) +COMMENT_RE = re.compile(r'(^|\s)+#.*$') + +# Matches environment variable-style values in '${MY_VARIABLE_1}' with the +# variable name consisting of only uppercase letters, digits or the '_' +# (underscore). This follows the POSIX standard defined in IEEE Std 1003.1, +# 2013 Edition. +ENV_VAR_RE = re.compile(r'(?P\$\{(?P[A-Z0-9_]+)\})') + +SUPPORTED_OPTIONS = [ + cmdoptions.constraints, + cmdoptions.editable, + cmdoptions.requirements, + cmdoptions.no_index, + cmdoptions.index_url, + cmdoptions.find_links, + cmdoptions.extra_index_url, + cmdoptions.always_unzip, + cmdoptions.no_binary, + cmdoptions.only_binary, + cmdoptions.pre, + cmdoptions.trusted_host, + cmdoptions.require_hashes, +] # type: List[Callable[..., optparse.Option]] + +# options to be passed to requirements +SUPPORTED_OPTIONS_REQ = [ + cmdoptions.install_options, + cmdoptions.global_options, + cmdoptions.hash, +] # type: List[Callable[..., optparse.Option]] + +# the 'dest' string values +SUPPORTED_OPTIONS_REQ_DEST = [str(o().dest) for o in SUPPORTED_OPTIONS_REQ] + + +def parse_requirements( + filename, # type: str + finder=None, # type: Optional[PackageFinder] + comes_from=None, # type: Optional[str] + options=None, # type: Optional[optparse.Values] + session=None, # type: Optional[PipSession] + constraint=False, # type: bool + wheel_cache=None, # type: Optional[WheelCache] + use_pep517=None # type: Optional[bool] +): + # type: (...) -> Iterator[InstallRequirement] + """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 + :param use_pep517: Value of the --use-pep517 option. + """ + 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, + use_pep517=use_pep517, constraint=constraint) + for req in req_iter: + yield req + + +def preprocess(content, options): + # type: (Text, Optional[optparse.Values]) -> ReqFileLines + """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) # type: ReqFileLines + lines_enum = join_lines(lines_enum) + lines_enum = ignore_comments(lines_enum) + lines_enum = skip_regex(lines_enum, options) + lines_enum = expand_env_variables(lines_enum) + return lines_enum + + +def process_line( + line, # type: Text + filename, # type: str + line_number, # type: int + finder=None, # type: Optional[PackageFinder] + comes_from=None, # type: Optional[str] + options=None, # type: Optional[optparse.Values] + session=None, # type: Optional[PipSession] + wheel_cache=None, # type: Optional[WheelCache] + use_pep517=None, # type: Optional[bool] + constraint=False # type: bool +): + # type: (...) -> Iterator[InstallRequirement] + """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(line) + defaults = parser.get_default_values() + defaults.index_url = None + if finder: + defaults.format_control = finder.format_control + args_str, options_str = break_args_options(line) + # Prior to 2.7.3, shlex cannot deal with unicode entries + if sys.version_info < (2, 7, 3): + # https://github.com/python/mypy/issues/1174 + options_str = options_str.encode('utf8') # type: ignore + # https://github.com/python/mypy/issues/1174 + opts, _ = parser.parse_args( + shlex.split(options_str), defaults) # type: ignore + + # 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 install_req_from_line( + args_str, line_comes_from, constraint=constraint, + use_pep517=use_pep517, + isolated=isolated, options=req_options, wheel_cache=wheel_cache + ) + + # yield an editable requirement + elif opts.editables: + isolated = options.isolated_mode if options else False + yield install_req_from_editable( + opts.editables[0], comes_from=line_comes_from, + use_pep517=use_pep517, + constraint=constraint, 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? + parsed_reqs = parse_requirements( + req_path, finder, comes_from, options, session, + constraint=nested_constraint, wheel_cache=wheel_cache + ) + for req in parsed_reqs: + yield req + + # percolate hash-checking option upward + elif opts.require_hashes: + options.require_hashes = opts.require_hashes + + # set finder options + elif finder: + if opts.index_url: + finder.index_urls = [opts.index_url] + 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.trusted_hosts: + finder.secure_origins.extend( + ("*", host, "*") for host in opts.trusted_hosts) + + +def break_args_options(line): + # type: (Text) -> Tuple[str, Text] + """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) # type: ignore + + +def build_parser(line): + # type: (Text) -> optparse.OptionParser + """ + 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): + # type: (Any, str) -> NoReturn + # add offending line + msg = 'Invalid requirement: %s\n%s' % (line, msg) + raise RequirementsFileParseError(msg) + # NOTE: mypy disallows assigning to a method + # https://github.com/python/mypy/issues/2427 + parser.exit = parser_exit # type: ignore + + return parser + + +def join_lines(lines_enum): + # type: (ReqFileLines) -> ReqFileLines + """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 = [] # type: List[Text] + 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): + # type: (ReqFileLines) -> ReqFileLines + """ + 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): + # type: (ReqFileLines, Optional[optparse.Values]) -> ReqFileLines + """ + 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 + + +def expand_env_variables(lines_enum): + # type: (ReqFileLines) -> ReqFileLines + """Replace all environment variables that can be retrieved via `os.getenv`. + + The only allowed format for environment variables defined in the + requirement file is `${MY_VARIABLE_1}` to ensure two things: + + 1. Strings that contain a `$` aren't accidentally (partially) expanded. + 2. Ensure consistency across platforms for requirement files. + + These points are the result of a discussion on the `github pull + request #3514 `_. + + Valid characters in variable names follow the `POSIX standard + `_ and are limited + to uppercase letter, digits and the `_` (underscore). + """ + for line_number, line in lines_enum: + for env_var, var_name in ENV_VAR_RE.findall(line): + value = os.getenv(var_name) + if not value: + continue + + line = line.replace(env_var, value) + + yield line_number, line diff --git a/venv2/lib/python2.7/site-packages/pip/_internal/req/req_install.py b/venv2/lib/python2.7/site-packages/pip/_internal/req/req_install.py new file mode 100644 index 0000000..25a692e --- /dev/null +++ b/venv2/lib/python2.7/site-packages/pip/_internal/req/req_install.py @@ -0,0 +1,1028 @@ +from __future__ import absolute_import + +import logging +import os +import shutil +import sys +import sysconfig +import zipfile +from distutils.util import change_root + +from pip._vendor import pkg_resources, six +from pip._vendor.packaging.requirements import Requirement +from pip._vendor.packaging.utils import canonicalize_name +from pip._vendor.packaging.version import Version +from pip._vendor.packaging.version import parse as parse_version +from pip._vendor.pep517.wrappers import Pep517HookCaller + +from pip._internal import wheel +from pip._internal.build_env import NoOpBuildEnvironment +from pip._internal.exceptions import InstallationError +from pip._internal.locations import ( + PIP_DELETE_MARKER_FILENAME, running_under_virtualenv, +) +from pip._internal.models.link import Link +from pip._internal.pyproject import load_pyproject_toml, make_pyproject_path +from pip._internal.req.req_uninstall import UninstallPathSet +from pip._internal.utils.compat import native_str +from pip._internal.utils.hashes import Hashes +from pip._internal.utils.logging import indent_log +from pip._internal.utils.misc import ( + _make_build_dir, ask_path_exists, backup_dir, call_subprocess, + display_path, dist_in_site_packages, dist_in_usersite, ensure_dir, + get_installed_version, redact_password_from_url, rmtree, +) +from pip._internal.utils.packaging import get_metadata +from pip._internal.utils.setuptools_build import SETUPTOOLS_SHIM +from pip._internal.utils.temp_dir import TempDirectory +from pip._internal.utils.typing import MYPY_CHECK_RUNNING +from pip._internal.utils.ui import open_spinner +from pip._internal.vcs import vcs +from pip._internal.wheel import move_wheel_files + +if MYPY_CHECK_RUNNING: + from typing import ( + Any, Dict, Iterable, List, Mapping, Optional, Sequence, Union, + ) + from pip._internal.build_env import BuildEnvironment + from pip._internal.cache import WheelCache + from pip._internal.index import PackageFinder + from pip._vendor.pkg_resources import Distribution + from pip._vendor.packaging.specifiers import SpecifierSet + from pip._vendor.packaging.markers import Marker + + +logger = logging.getLogger(__name__) + + +class InstallRequirement(object): + """ + Represents something that may be installed later on, may have information + about where to fetch the relevant requirement and also contains logic for + installing the said requirement. + """ + + def __init__( + self, + req, # type: Optional[Requirement] + comes_from, # type: Optional[Union[str, InstallRequirement]] + source_dir=None, # type: Optional[str] + editable=False, # type: bool + link=None, # type: Optional[Link] + update=True, # type: bool + markers=None, # type: Optional[Marker] + use_pep517=None, # type: Optional[bool] + isolated=False, # type: bool + options=None, # type: Optional[Dict[str, Any]] + wheel_cache=None, # type: Optional[WheelCache] + constraint=False, # type: bool + extras=() # type: Iterable[str] + ): + # type: (...) -> None + assert req is None or isinstance(req, Requirement), req + self.req = req + self.comes_from = comes_from + self.constraint = constraint + if source_dir is not None: + self.source_dir = os.path.normpath(os.path.abspath(source_dir)) + else: + self.source_dir = None + self.editable = editable + + self._wheel_cache = wheel_cache + if link is None and req and req.url: + # PEP 508 URL requirement + link = Link(req.url) + self.link = self.original_link = link + + if extras: + self.extras = extras + elif req: + self.extras = { + pkg_resources.safe_extra(extra) for extra in req.extras + } + else: + self.extras = set() + if markers is None and req: + markers = req.marker + self.markers = markers + + self._egg_info_path = None # type: Optional[str] + # 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 = TempDirectory(kind="req-build") + # 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 # type: Optional[str] + # True if the editable should be updated: + self.update = update + # Set to True after successful installation + self.install_succeeded = None # type: Optional[bool] + # UninstallPathSet of uninstalled distribution (for possible rollback) + self.uninstalled_pathset = None + self.options = options if options else {} + # Set to True after successful preparation of this requirement + self.prepared = False + self.is_direct = False + + self.isolated = isolated + self.build_env = NoOpBuildEnvironment() # type: BuildEnvironment + + # For PEP 517, the directory where we request the project metadata + # gets stored. We need this to pass to build_wheel, so the backend + # can ensure that the wheel matches the metadata (see the PEP for + # details). + self.metadata_directory = None # type: Optional[str] + + # The static build requirements (from pyproject.toml) + self.pyproject_requires = None # type: Optional[List[str]] + + # Build requirements that we will check are available + self.requirements_to_check = [] # type: List[str] + + # The PEP 517 backend we should use to build the project + self.pep517_backend = None # type: Optional[Pep517HookCaller] + + # Are we using PEP 517 for this requirement? + # After pyproject.toml has been loaded, the only valid values are True + # and False. Before loading, None is valid (meaning "use the default"). + # Setting an explicit value before loading pyproject.toml is supported, + # but after loading this flag should be treated as read only. + self.use_pep517 = use_pep517 + + def __str__(self): + # type: () -> str + if self.req: + s = str(self.req) + if self.link: + s += ' from %s' % redact_password_from_url(self.link.url) + elif self.link: + s = redact_password_from_url(self.link.url) + else: + s = '' + 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): + # type: () -> str + return '<%s object: %s editable=%r>' % ( + self.__class__.__name__, str(self), self.editable) + + def populate_link(self, finder, upgrade, require_hashes): + # type: (PackageFinder, bool, bool) -> None + """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.get(self.link, self.name) + if old_link != self.link: + logger.debug('Using cached wheel link: %s', self.link) + + # Things that are valid for all kinds of requirements? + @property + def name(self): + # type: () -> Optional[str] + if self.req is None: + return None + return native_str(pkg_resources.safe_name(self.req.name)) + + @property + def specifier(self): + # type: () -> SpecifierSet + return self.req.specifier + + @property + def is_pinned(self): + # type: () -> bool + """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 {'==', '==='}) + + @property + def installed_version(self): + # type: () -> Optional[str] + return get_installed_version(self.name) + + def match_markers(self, extras_requested=None): + # type: (Optional[Iterable[str]]) -> bool + if not extras_requested: + # Provide an extra to safely evaluate the markers + # without matching any extra + extras_requested = ('',) + if self.markers is not None: + return any( + self.markers.evaluate({'extra': extra}) + for extra in extras_requested) + else: + return True + + @property + def has_hash_options(self): + # type: () -> bool + """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): + # type: (bool) -> Hashes + """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 from_path(self): + # type: () -> Optional[str] + """Format a nice indicator to show where this "comes from" + """ + 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): + # type: (str) -> Optional[str] + assert build_dir is not None + if self._temp_build_dir.path is not None: + return self._temp_build_dir.path + 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 + # Some systems have /tmp as a symlink which confuses custom + # builds (such as numpy). Thus, we ensure that the real path + # is returned. + self._temp_build_dir.create() + self._ideal_build_dir = build_dir + + return self._temp_build_dir.path + 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): + # type: () -> None + """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.run_egg_info 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.path + assert (self._ideal_build_dir is not None and + self._ideal_build_dir.path) # type: ignore + old_location = self._temp_build_dir.path + self._temp_build_dir.path = 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.path = new_location + self._ideal_build_dir = None + self.source_dir = os.path.normpath(os.path.abspath(new_location)) + self._egg_info_path = None + + # Correct the metadata directory, if it exists + if self.metadata_directory: + old_meta = self.metadata_directory + rel = os.path.relpath(old_meta, start=old_location) + new_meta = os.path.join(new_location, rel) + new_meta = os.path.normpath(os.path.abspath(new_meta)) + self.metadata_directory = new_meta + + def remove_temporary_source(self): + # type: () -> None + """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 + self._temp_build_dir.cleanup() + self.build_env.cleanup() + + def check_if_exists(self, use_user_site): + # type: (bool) -> bool + """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. + no_marker = Requirement(str(self.req)) + no_marker.marker = None + self.satisfied_by = pkg_resources.get_distribution(str(no_marker)) + if self.editable and self.satisfied_by: + self.conflicts_with = self.satisfied_by + # when installing editables, nothing pre-existing should ever + # satisfy + self.satisfied_by = None + return True + except pkg_resources.DistributionNotFound: + return False + except pkg_resources.VersionConflict: + existing_dist = pkg_resources.get_distribution( + self.req.name + ) + if 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 + + # Things valid for wheels + @property + def is_wheel(self): + # type: () -> bool + if not self.link: + return False + return self.link.is_wheel + + def move_wheel_files( + self, + wheeldir, # type: str + root=None, # type: Optional[str] + home=None, # type: Optional[str] + prefix=None, # type: Optional[str] + warn_script_location=True, # type: bool + use_user_site=False, # type: bool + pycompile=True # type: bool + ): + # type: (...) -> None + move_wheel_files( + self.name, self.req, wheeldir, + user=use_user_site, + home=home, + root=root, + prefix=prefix, + pycompile=pycompile, + isolated=self.isolated, + warn_script_location=warn_script_location, + ) + + # Things valid for sdists + @property + def setup_py_dir(self): + # type: () -> str + return os.path.join( + self.source_dir, + self.link and self.link.subdirectory_fragment or '') + + @property + def setup_py(self): + # type: () -> str + assert self.source_dir, "No source dir for %s" % self + + 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 + + @property + def pyproject_toml(self): + # type: () -> str + assert self.source_dir, "No source dir for %s" % self + + return make_pyproject_path(self.setup_py_dir) + + def load_pyproject_toml(self): + # type: () -> None + """Load the pyproject.toml file. + + After calling this routine, all of the attributes related to PEP 517 + processing for this requirement have been set. In particular, the + use_pep517 attribute can be used to determine whether we should + follow the PEP 517 or legacy (setup.py) code path. + """ + pep517_data = load_pyproject_toml( + self.use_pep517, + self.pyproject_toml, + self.setup_py, + str(self) + ) + + if pep517_data is None: + self.use_pep517 = False + else: + self.use_pep517 = True + requires, backend, check = pep517_data + self.requirements_to_check = check + self.pyproject_requires = requires + self.pep517_backend = Pep517HookCaller(self.setup_py_dir, backend) + + # Use a custom function to call subprocesses + self.spin_message = "" + + def runner( + cmd, # type: List[str] + cwd=None, # type: Optional[str] + extra_environ=None # type: Optional[Mapping[str, Any]] + ): + # type: (...) -> None + with open_spinner(self.spin_message) as spinner: + call_subprocess( + cmd, + cwd=cwd, + extra_environ=extra_environ, + spinner=spinner + ) + self.spin_message = "" + + self.pep517_backend._subprocess_runner = runner + + def prepare_metadata(self): + # type: () -> None + """Ensure that project metadata is available. + + Under PEP 517, call the backend hook to prepare the metadata. + Under legacy processing, call setup.py egg-info. + """ + assert self.source_dir + + with indent_log(): + if self.use_pep517: + self.prepare_pep517_metadata() + else: + self.run_egg_info() + + if not self.req: + if isinstance(parse_version(self.metadata["Version"]), Version): + op = "==" + else: + op = "===" + self.req = Requirement( + "".join([ + self.metadata["Name"], + op, + self.metadata["Version"], + ]) + ) + self._correct_build_location() + else: + metadata_name = canonicalize_name(self.metadata["Name"]) + if canonicalize_name(self.req.name) != metadata_name: + logger.warning( + 'Generating metadata for package %s ' + 'produced metadata for project name %s. Fix your ' + '#egg=%s fragments.', + self.name, metadata_name, self.name + ) + self.req = Requirement(metadata_name) + + def prepare_pep517_metadata(self): + # type: () -> None + assert self.pep517_backend is not None + + metadata_dir = os.path.join( + self.setup_py_dir, + 'pip-wheel-metadata' + ) + ensure_dir(metadata_dir) + + with self.build_env: + # Note that Pep517HookCaller implements a fallback for + # prepare_metadata_for_build_wheel, so we don't have to + # consider the possibility that this hook doesn't exist. + backend = self.pep517_backend + self.spin_message = "Preparing wheel metadata" + distinfo_dir = backend.prepare_metadata_for_build_wheel( + metadata_dir + ) + + self.metadata_directory = os.path.join(metadata_dir, distinfo_dir) + + def run_egg_info(self): + # type: () -> None + 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, + ) + 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 = [] # type: List[str] + 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'] + with self.build_env: + call_subprocess( + egg_info_cmd + egg_base_option, + cwd=self.setup_py_dir, + command_desc='python setup.py egg_info') + + @property + def egg_info_path(self): + # type: () -> str + 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.lexists( + 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( + "Files/directories not found in %s" % base + ) + # 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 self._egg_info_path + + @property + def metadata(self): + # type: () -> Any + if not hasattr(self, '_metadata'): + self._metadata = get_metadata(self.get_dist()) + + return self._metadata + + def get_dist(self): + # type: () -> Distribution + """Return a pkg_resources.Distribution for this requirement""" + if self.metadata_directory: + base_dir, distinfo = os.path.split(self.metadata_directory) + metadata = pkg_resources.PathMetadata( + base_dir, self.metadata_directory + ) + dist_name = os.path.splitext(distinfo)[0] + typ = pkg_resources.DistInfoDistribution + else: + egg_info = self.egg_info_path.rstrip(os.path.sep) + 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] + # https://github.com/python/mypy/issues/1174 + typ = pkg_resources.Distribution # type: ignore + + return typ( + base_dir, + project_name=dist_name, + metadata=metadata, + ) + + def assert_source_matches_version(self): + # type: () -> None + assert self.source_dir + version = self.metadata['version'] + if self.req.specifier and version not in self.req.specifier: + logger.warning( + 'Requested %s, but installing version %s', + self, + version, + ) + else: + logger.debug( + 'Source in %s has version %s, which satisfies requirement %s', + display_path(self.source_dir), + version, + self, + ) + + # For both source distributions and editables + def ensure_has_source_dir(self, parent_dir): + # type: (str) -> str + """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 + + # For editable installations + def install_editable( + self, + install_options, # type: List[str] + global_options=(), # type: Sequence[str] + prefix=None # type: Optional[str] + ): + # type: (...) -> 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={}'.format(prefix)] + install_options = list(install_options) + prefix_param + + with indent_log(): + # FIXME: should we do --install-headers here too? + with self.build_env: + call_subprocess( + [ + sys.executable, + '-c', + SETUPTOOLS_SHIM % self.setup_py + ] + + list(global_options) + + ['develop', '--no-deps'] + + list(install_options), + + cwd=self.setup_py_dir, + ) + + self.install_succeeded = True + + def update_editable(self, obtain=True): + # type: (bool) -> None + 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)) + + # Top-level Actions + def uninstall(self, auto_confirm=False, verbose=False, + use_user_site=False): + # type: (bool, bool, bool) -> Optional[UninstallPathSet] + """ + 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(use_user_site): + logger.warning("Skipping %s as it is not installed.", self.name) + return None + dist = self.satisfied_by or self.conflicts_with + + uninstalled_pathset = UninstallPathSet.from_dist(dist) + uninstalled_pathset.remove(auto_confirm, verbose) + return uninstalled_pathset + + def _clean_zip_name(self, name, prefix): # only used by archive. + # type: (str, str) -> str + 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 _get_archive_name(self, path, parentdir, rootdir): + # type: (str, str, str) -> str + path = os.path.join(parentdir, path) + name = self._clean_zip_name(path, rootdir) + return self.name + '/' + name + + # TODO: Investigate if this should be kept in InstallRequirement + # Seems to be used only when VCS + downloads + def archive(self, build_dir): + # type: (str) -> None + assert self.source_dir + create_archive = True + archive_name = '%s-%s.zip' % (self.name, self.metadata["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, (a)bort ' % + display_path(archive_path), ('i', 'w', 'b', 'a')) + 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) + elif response == 'a': + sys.exit(-1) + 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: + dir_arcname = self._get_archive_name(dirname, + parentdir=dirpath, + rootdir=dir) + zipdir = zipfile.ZipInfo(dir_arcname + '/') + zipdir.external_attr = 0x1ED << 16 # 0o755 + zip.writestr(zipdir, '') + for filename in filenames: + if filename == PIP_DELETE_MARKER_FILENAME: + continue + file_arcname = self._get_archive_name(filename, + parentdir=dirpath, + rootdir=dir) + filename = os.path.join(dirpath, filename) + zip.write(filename, file_arcname) + zip.close() + logger.info('Saved %s', display_path(archive_path)) + + def install( + self, + install_options, # type: List[str] + global_options=None, # type: Optional[Sequence[str]] + root=None, # type: Optional[str] + home=None, # type: Optional[str] + prefix=None, # type: Optional[str] + warn_script_location=True, # type: bool + use_user_site=False, # type: bool + pycompile=True # type: bool + ): + # type: (...) -> None + global_options = global_options if global_options is not None else [] + if self.editable: + self.install_editable( + install_options, global_options, prefix=prefix, + ) + return + if self.is_wheel: + version = wheel.wheel_version(self.source_dir) + wheel.check_compatibility(version, self.name) + + self.move_wheel_files( + self.source_dir, root=root, prefix=prefix, home=home, + warn_script_location=warn_script_location, + use_user_site=use_user_site, pycompile=pycompile, + ) + 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 = list(global_options) + \ + self.options.get('global_options', []) + install_options = list(install_options) + \ + self.options.get('install_options', []) + + if self.isolated: + # https://github.com/python/mypy/issues/1174 + global_options = global_options + ["--no-user-cfg"] # type: ignore + + with TempDirectory(kind="record") as temp_dir: + record_filename = os.path.join(temp_dir.path, 'install-record.txt') + install_args = self.get_install_args( + global_options, record_filename, root, prefix, pycompile, + ) + msg = 'Running setup.py install for %s' % (self.name,) + with open_spinner(msg) as spinner: + with indent_log(): + with self.build_env: + call_subprocess( + install_args + install_options, + cwd=self.setup_py_dir, + spinner=spinner, + ) + + if not os.path.exists(record_filename): + logger.debug('Record file %s not found', record_filename) + return + self.install_succeeded = True + + def prepend_root(path): + # type: (str) -> str + 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) + ) + new_lines.sort() + ensure_dir(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') + + def get_install_args( + self, + global_options, # type: Sequence[str] + record_filename, # type: str + root, # type: Optional[str] + prefix, # type: Optional[str] + pycompile # type: bool + ): + # type: (...) -> List[str] + 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] + 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 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)] + + return install_args diff --git a/venv2/lib/python2.7/site-packages/pip/_internal/req/req_set.py b/venv2/lib/python2.7/site-packages/pip/_internal/req/req_set.py new file mode 100644 index 0000000..d1966a4 --- /dev/null +++ b/venv2/lib/python2.7/site-packages/pip/_internal/req/req_set.py @@ -0,0 +1,193 @@ +from __future__ import absolute_import + +import logging +from collections import OrderedDict + +from pip._internal.exceptions import InstallationError +from pip._internal.utils.logging import indent_log +from pip._internal.utils.typing import MYPY_CHECK_RUNNING +from pip._internal.wheel import Wheel + +if MYPY_CHECK_RUNNING: + from typing import Dict, Iterable, List, Optional, Tuple + from pip._internal.req.req_install import InstallRequirement + + +logger = logging.getLogger(__name__) + + +class RequirementSet(object): + + def __init__(self, require_hashes=False, check_supported_wheels=True): + # type: (bool, bool) -> None + """Create a RequirementSet. + """ + + self.requirements = OrderedDict() # type: Dict[str, InstallRequirement] # noqa: E501 + self.require_hashes = require_hashes + self.check_supported_wheels = check_supported_wheels + + # Mapping of alias: real_name + self.requirement_aliases = {} # type: Dict[str, str] + self.unnamed_requirements = [] # type: List[InstallRequirement] + self.successfully_downloaded = [] # type: List[InstallRequirement] + self.reqs_to_cleanup = [] # type: List[InstallRequirement] + + def __str__(self): + # type: () -> str + 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): + # type: () -> str + 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, # type: InstallRequirement + parent_req_name=None, # type: Optional[str] + extras_requested=None # type: Optional[Iterable[str]] + ): + # type: (...) -> Tuple[List[InstallRequirement], Optional[InstallRequirement]] # noqa: E501 + """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. + :param extras_requested: an iterable of extras used to evaluate the + environment markers. + :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 the markers do not match, ignore this requirement. + if not install_req.match_markers(extras_requested): + logger.info( + "Ignoring %s: markers '%s' don't match your environment", + name, install_req.markers, + ) + return [], None + + # If the wheel is not supported, raise an error. + # Should check this after filtering out based on environment markers to + # allow specifying different wheels based on the environment/OS, in a + # single requirements file. + if install_req.link and install_req.link.is_wheel: + wheel = Wheel(install_req.link.filename) + if self.check_supported_wheels and not wheel.supported(): + raise InstallationError( + "%s is not a supported wheel on this platform." % + wheel.filename + ) + + # This next bit is really a sanity check. + assert install_req.is_direct == (parent_req_name is None), ( + "a direct req shouldn't have a parent and also, " + "a non direct req should have a parent" + ) + + # Unnamed requirements are scanned again and the requirement won't be + # added as a dependency until after scanning. + if not name: + # url or path requirement w/o an egg fragment + self.unnamed_requirements.append(install_req) + return [install_req], None + + try: + existing_req = self.get_requirement(name) + except KeyError: + existing_req = None + + has_conflicting_requirement = ( + parent_req_name is None and + existing_req and + not existing_req.constraint and + existing_req.extras == install_req.extras and + existing_req.req.specifier != install_req.req.specifier + ) + if has_conflicting_requirement: + raise InstallationError( + "Double requirement given: %s (already in %s, name=%r)" + % (install_req, existing_req, name) + ) + + # When no existing requirement exists, add the requirement as a + # dependency and it will be scanned again after. + if not existing_req: + self.requirements[name] = install_req + # FIXME: what about other normalizations? E.g., _ vs. -? + if name.lower() != name: + self.requirement_aliases[name.lower()] = name + # We'd want to rescan this requirements later + return [install_req], install_req + + # Assume there's no need to scan, and that we've already + # encountered this for scanning. + if install_req.constraint or not existing_req.constraint: + return [], existing_req + + does_not_satisfy_constraint = ( + install_req.link and + not ( + existing_req.link and + install_req.link.path == existing_req.link.path + ) + ) + if does_not_satisfy_constraint: + 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) | set(install_req.extras) + )) + logger.debug( + "Setting %s extras to: %s", + existing_req, existing_req.extras, + ) + # Return the existing requirement for addition to the parent and + # scanning again. + return [existing_req], existing_req + + def has_requirement(self, project_name): + # type: (str) -> bool + 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 + + def get_requirement(self, project_name): + # type: (str) -> InstallRequirement + 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 cleanup_files(self): + # type: () -> None + """Clean up files, remove builds.""" + logger.debug('Cleaning up...') + with indent_log(): + for req in self.reqs_to_cleanup: + req.remove_temporary_source() diff --git a/venv2/lib/python2.7/site-packages/pip/_internal/req/req_tracker.py b/venv2/lib/python2.7/site-packages/pip/_internal/req/req_tracker.py new file mode 100644 index 0000000..e36a3f6 --- /dev/null +++ b/venv2/lib/python2.7/site-packages/pip/_internal/req/req_tracker.py @@ -0,0 +1,96 @@ +from __future__ import absolute_import + +import contextlib +import errno +import hashlib +import logging +import os + +from pip._internal.utils.temp_dir import TempDirectory +from pip._internal.utils.typing import MYPY_CHECK_RUNNING + +if MYPY_CHECK_RUNNING: + from types import TracebackType + from typing import Iterator, Optional, Set, Type + from pip._internal.req.req_install import InstallRequirement + from pip._internal.models.link import Link + +logger = logging.getLogger(__name__) + + +class RequirementTracker(object): + + def __init__(self): + # type: () -> None + self._root = os.environ.get('PIP_REQ_TRACKER') + if self._root is None: + self._temp_dir = TempDirectory(delete=False, kind='req-tracker') + self._temp_dir.create() + self._root = os.environ['PIP_REQ_TRACKER'] = self._temp_dir.path + logger.debug('Created requirements tracker %r', self._root) + else: + self._temp_dir = None + logger.debug('Re-using requirements tracker %r', self._root) + self._entries = set() # type: Set[InstallRequirement] + + def __enter__(self): + # type: () -> RequirementTracker + return self + + def __exit__( + self, + exc_type, # type: Optional[Type[BaseException]] + exc_val, # type: Optional[BaseException] + exc_tb # type: Optional[TracebackType] + ): + # type: (...) -> None + self.cleanup() + + def _entry_path(self, link): + # type: (Link) -> str + hashed = hashlib.sha224(link.url_without_fragment.encode()).hexdigest() + return os.path.join(self._root, hashed) + + def add(self, req): + # type: (InstallRequirement) -> None + link = req.link + info = str(req) + entry_path = self._entry_path(link) + try: + with open(entry_path) as fp: + # Error, these's already a build in progress. + raise LookupError('%s is already being built: %s' + % (link, fp.read())) + except IOError as e: + if e.errno != errno.ENOENT: + raise + assert req not in self._entries + with open(entry_path, 'w') as fp: + fp.write(info) + self._entries.add(req) + logger.debug('Added %s to build tracker %r', req, self._root) + + def remove(self, req): + # type: (InstallRequirement) -> None + link = req.link + self._entries.remove(req) + os.unlink(self._entry_path(link)) + logger.debug('Removed %s from build tracker %r', req, self._root) + + def cleanup(self): + # type: () -> None + for req in set(self._entries): + self.remove(req) + remove = self._temp_dir is not None + if remove: + self._temp_dir.cleanup() + logger.debug('%s build tracker %r', + 'Removed' if remove else 'Cleaned', + self._root) + + @contextlib.contextmanager + def track(self, req): + # type: (InstallRequirement) -> Iterator[None] + self.add(req) + yield + self.remove(req) diff --git a/venv2/lib/python2.7/site-packages/pip/_internal/req/req_uninstall.py b/venv2/lib/python2.7/site-packages/pip/_internal/req/req_uninstall.py new file mode 100644 index 0000000..733301c --- /dev/null +++ b/venv2/lib/python2.7/site-packages/pip/_internal/req/req_uninstall.py @@ -0,0 +1,633 @@ +from __future__ import absolute_import + +import csv +import functools +import logging +import os +import sys +import sysconfig + +from pip._vendor import pkg_resources + +from pip._internal.exceptions import UninstallationError +from pip._internal.locations import bin_py, bin_user +from pip._internal.utils.compat import WINDOWS, cache_from_source, uses_pycache +from pip._internal.utils.logging import indent_log +from pip._internal.utils.misc import ( + FakeFile, ask, dist_in_usersite, dist_is_local, egg_link_path, is_local, + normalize_path, renames, rmtree, +) +from pip._internal.utils.temp_dir import AdjacentTempDirectory, TempDirectory +from pip._internal.utils.typing import MYPY_CHECK_RUNNING + +if MYPY_CHECK_RUNNING: + from typing import ( + Any, Callable, Dict, Iterable, Iterator, List, Optional, Set, Tuple, + ) + from pip._vendor.pkg_resources import Distribution + +logger = logging.getLogger(__name__) + + +def _script_names(dist, script_name, is_gui): + # type: (Distribution, str, bool) -> List[str] + """Create the fully qualified name of the files created by + {console,gui}_scripts for the given ``dist``. + Returns the list of file names + """ + if dist_in_usersite(dist): + bin_dir = bin_user + else: + bin_dir = bin_py + exe_name = os.path.join(bin_dir, script_name) + paths_to_remove = [exe_name] + if WINDOWS: + paths_to_remove.append(exe_name + '.exe') + paths_to_remove.append(exe_name + '.exe.manifest') + if is_gui: + paths_to_remove.append(exe_name + '-script.pyw') + else: + paths_to_remove.append(exe_name + '-script.py') + return paths_to_remove + + +def _unique(fn): + # type: (Callable) -> Callable[..., Iterator[Any]] + @functools.wraps(fn) + def unique(*args, **kw): + # type: (Any, Any) -> Iterator[Any] + seen = set() # type: Set[Any] + for item in fn(*args, **kw): + if item not in seen: + seen.add(item) + yield item + return unique + + +@_unique +def uninstallation_paths(dist): + # type: (Distribution) -> Iterator[str] + """ + Yield all the uninstallation paths for dist based on RECORD-without-.py[co] + + Yield paths to all the files in RECORD. For each .py file in RECORD, add + the .pyc and .pyo in the same directory. + + UninstallPathSet.add() takes care of the __pycache__ .py[co]. + """ + 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 + path = os.path.join(dn, base + '.pyo') + yield path + + +def compact(paths): + # type: (Iterable[str]) -> Set[str] + """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.""" + + sep = os.path.sep + short_paths = set() # type: Set[str] + for path in sorted(paths, key=len): + should_skip = any( + path.startswith(shortpath.rstrip("*")) and + path[len(shortpath.rstrip("*").rstrip(sep))] == sep + for shortpath in short_paths + ) + if not should_skip: + short_paths.add(path) + return short_paths + + +def compress_for_rename(paths): + # type: (Iterable[str]) -> Set[str] + """Returns a set containing the paths that need to be renamed. + + This set may include directories when the original sequence of paths + included every file on disk. + """ + case_map = dict((os.path.normcase(p), p) for p in paths) + remaining = set(case_map) + unchecked = sorted(set(os.path.split(p)[0] + for p in case_map.values()), key=len) + wildcards = set() # type: Set[str] + + def norm_join(*a): + # type: (str) -> str + return os.path.normcase(os.path.join(*a)) + + for root in unchecked: + if any(os.path.normcase(root).startswith(w) + for w in wildcards): + # This directory has already been handled. + continue + + all_files = set() # type: Set[str] + all_subdirs = set() # type: Set[str] + for dirname, subdirs, files in os.walk(root): + all_subdirs.update(norm_join(root, dirname, d) + for d in subdirs) + all_files.update(norm_join(root, dirname, f) + for f in files) + # If all the files we found are in our remaining set of files to + # remove, then remove them from the latter set and add a wildcard + # for the directory. + if not (all_files - remaining): + remaining.difference_update(all_files) + wildcards.add(root + os.sep) + + return set(map(case_map.__getitem__, remaining)) | wildcards + + +def compress_for_output_listing(paths): + # type: (Iterable[str]) -> Tuple[Set[str], Set[str]] + """Returns a tuple of 2 sets of which paths to display to user + + The first set contains paths that would be deleted. Files of a package + are not added and the top-level directory of the package has a '*' added + at the end - to signify that all it's contents are removed. + + The second set contains files that would have been skipped in the above + folders. + """ + + will_remove = set(paths) + will_skip = set() + + # Determine folders and files + folders = set() + files = set() + for path in will_remove: + if path.endswith(".pyc"): + continue + if path.endswith("__init__.py") or ".dist-info" in path: + folders.add(os.path.dirname(path)) + files.add(path) + + # probably this one https://github.com/python/mypy/issues/390 + _normcased_files = set(map(os.path.normcase, files)) # type: ignore + + folders = compact(folders) + + # This walks the tree using os.walk to not miss extra folders + # that might get added. + for folder in folders: + for dirpath, _, dirfiles in os.walk(folder): + for fname in dirfiles: + if fname.endswith(".pyc"): + continue + + file_ = os.path.join(dirpath, fname) + if (os.path.isfile(file_) and + os.path.normcase(file_) not in _normcased_files): + # We are skipping this file. Add it to the set. + will_skip.add(file_) + + will_remove = files | { + os.path.join(folder, "*") for folder in folders + } + + return will_remove, will_skip + + +class StashedUninstallPathSet(object): + """A set of file rename operations to stash files while + tentatively uninstalling them.""" + def __init__(self): + # type: () -> None + # Mapping from source file root to [Adjacent]TempDirectory + # for files under that directory. + self._save_dirs = {} # type: Dict[str, TempDirectory] + # (old path, new path) tuples for each move that may need + # to be undone. + self._moves = [] # type: List[Tuple[str, str]] + + def _get_directory_stash(self, path): + # type: (str) -> str + """Stashes a directory. + + Directories are stashed adjacent to their original location if + possible, or else moved/copied into the user's temp dir.""" + + try: + save_dir = AdjacentTempDirectory(path) # type: TempDirectory + save_dir.create() + except OSError: + save_dir = TempDirectory(kind="uninstall") + save_dir.create() + self._save_dirs[os.path.normcase(path)] = save_dir + + return save_dir.path + + def _get_file_stash(self, path): + # type: (str) -> str + """Stashes a file. + + If no root has been provided, one will be created for the directory + in the user's temp directory.""" + path = os.path.normcase(path) + head, old_head = os.path.dirname(path), None + save_dir = None + + while head != old_head: + try: + save_dir = self._save_dirs[head] + break + except KeyError: + pass + head, old_head = os.path.dirname(head), head + else: + # Did not find any suitable root + head = os.path.dirname(path) + save_dir = TempDirectory(kind='uninstall') + save_dir.create() + self._save_dirs[head] = save_dir + + relpath = os.path.relpath(path, head) + if relpath and relpath != os.path.curdir: + return os.path.join(save_dir.path, relpath) + return save_dir.path + + def stash(self, path): + # type: (str) -> str + """Stashes the directory or file and returns its new location. + """ + if os.path.isdir(path): + new_path = self._get_directory_stash(path) + else: + new_path = self._get_file_stash(path) + + self._moves.append((path, new_path)) + if os.path.isdir(path) and os.path.isdir(new_path): + # If we're moving a directory, we need to + # remove the destination first or else it will be + # moved to inside the existing directory. + # We just created new_path ourselves, so it will + # be removable. + os.rmdir(new_path) + renames(path, new_path) + return new_path + + def commit(self): + # type: () -> None + """Commits the uninstall by removing stashed files.""" + for _, save_dir in self._save_dirs.items(): + save_dir.cleanup() + self._moves = [] + self._save_dirs = {} + + def rollback(self): + # type: () -> None + """Undoes the uninstall by moving stashed files back.""" + for p in self._moves: + logging.info("Moving to %s\n from %s", *p) + + for new_path, path in self._moves: + try: + logger.debug('Replacing %s from %s', new_path, path) + if os.path.isfile(new_path): + os.unlink(new_path) + elif os.path.isdir(new_path): + rmtree(new_path) + renames(path, new_path) + except OSError as ex: + logger.error("Failed to restore %s", new_path) + logger.debug("Exception: %s", ex) + + self.commit() + + @property + def can_rollback(self): + # type: () -> bool + return bool(self._moves) + + +class UninstallPathSet(object): + """A set of file paths to be removed in the uninstallation of a + requirement.""" + def __init__(self, dist): + # type: (Distribution) -> None + self.paths = set() # type: Set[str] + self._refuse = set() # type: Set[str] + self.pth = {} # type: Dict[str, UninstallPthEntries] + self.dist = dist + self._moved_paths = StashedUninstallPathSet() + + def _permitted(self, path): + # type: (str) -> bool + """ + Return True if the given path is one we are permitted to + remove/modify, False otherwise. + + """ + return is_local(path) + + def add(self, path): + # type: (str) -> None + 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): + # type: (str, str) -> None + 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 remove(self, auto_confirm=False, verbose=False): + # type: (bool, bool) -> None + """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 + + dist_name_version = ( + self.dist.project_name + "-" + self.dist.version + ) + logger.info('Uninstalling %s:', dist_name_version) + + with indent_log(): + if auto_confirm or self._allowed_to_proceed(verbose): + moved = self._moved_paths + + for_rename = compress_for_rename(self.paths) + + for path in sorted(compact(for_rename)): + moved.stash(path) + logger.debug('Removing file or directory %s', path) + + for pth in self.pth.values(): + pth.remove() + + logger.info('Successfully uninstalled %s', dist_name_version) + + def _allowed_to_proceed(self, verbose): + # type: (bool) -> bool + """Display which files would be deleted and prompt for confirmation + """ + + def _display(msg, paths): + # type: (str, Iterable[str]) -> None + if not paths: + return + + logger.info(msg) + with indent_log(): + for path in sorted(compact(paths)): + logger.info(path) + + if not verbose: + will_remove, will_skip = compress_for_output_listing(self.paths) + else: + # In verbose mode, display all the files that are going to be + # deleted. + will_remove = set(self.paths) + will_skip = set() + + _display('Would remove:', will_remove) + _display('Would not remove (might be manually added):', will_skip) + _display('Would not remove (outside of prefix):', self._refuse) + if verbose: + _display('Will actually move:', compress_for_rename(self.paths)) + + return ask('Proceed (y/n)? ', ('y', 'n')) == 'y' + + def rollback(self): + # type: () -> None + """Rollback the changes previously made by remove().""" + if not self._moved_paths.can_rollback: + logger.error( + "Can't roll back %s; was not uninstalled", + self.dist.project_name, + ) + return + logger.info('Rolling back uninstall of %s', self.dist.project_name) + self._moved_paths.rollback() + for pth in self.pth.values(): + pth.rollback() + + def commit(self): + # type: () -> None + """Remove temporary save dir: rollback will no longer be possible.""" + self._moved_paths.commit() + + @classmethod + def from_dist(cls, dist): + # type: (Distribution) -> UninstallPathSet + 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, + ) + return cls(dist) + + if dist_path in {p for p in {sysconfig.get_path("stdlib"), + sysconfig.get_path("platstdlib")} + if p}: + logger.info( + "Not uninstalling %s at %s, as it is in the standard library.", + dist.key, + dist_path, + ) + return cls(dist) + + paths_to_remove = cls(dist) + develop_egg_link = egg_link_path(dist) + develop_egg_link_egg_info = '{}.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: + raise UninstallationError( + "Cannot uninstall {!r}. It is a distutils installed project " + "and thus we cannot accurately determine which files belong " + "to it which would lead to only a partial uninstall.".format( + dist.project_name, + ) + ) + + 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 egg_info_exists and dist.egg_info.endswith('.dist-info'): + for path in uninstallation_paths(dist): + paths_to_remove.add(path) + + 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, dist.project_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) + + 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 + _scripts_to_remove = [] + console_scripts = dist.get_entry_map(group='console_scripts') + for name in console_scripts.keys(): + _scripts_to_remove.extend(_script_names(dist, name, False)) + # find gui_scripts + gui_scripts = dist.get_entry_map(group='gui_scripts') + for name in gui_scripts.keys(): + _scripts_to_remove.extend(_script_names(dist, name, True)) + + for s in _scripts_to_remove: + paths_to_remove.add(s) + + return paths_to_remove + + +class UninstallPthEntries(object): + def __init__(self, pth_file): + # type: (str) -> None + 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() # type: Set[str] + self._saved_lines = None # type: Optional[List[bytes]] + + def add(self, entry): + # type: (str) -> None + 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): + # type: () -> None + 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' + # handle missing trailing newline + if lines and not lines[-1].endswith(endline.encode("utf-8")): + lines[-1] = lines[-1] + endline.encode("utf-8") + 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): + # type: () -> bool + 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/venv2/lib/python2.7/site-packages/pip/_internal/resolve.py b/venv2/lib/python2.7/site-packages/pip/_internal/resolve.py new file mode 100644 index 0000000..f49667b --- /dev/null +++ b/venv2/lib/python2.7/site-packages/pip/_internal/resolve.py @@ -0,0 +1,393 @@ +"""Dependency Resolution + +The dependency resolution in pip is performed as follows: + +for top-level requirements: + a. only one spec allowed per project, regardless of conflicts or not. + otherwise a "double requirement" exception is raised + b. they override sub-dependency requirements. +for sub-dependencies + a. "first found, wins" (where the order is breadth first) +""" + +import logging +from collections import defaultdict +from itertools import chain + +from pip._internal.exceptions import ( + BestVersionAlreadyInstalled, DistributionNotFound, HashError, HashErrors, + UnsupportedPythonVersion, +) +from pip._internal.req.constructors import install_req_from_req_string +from pip._internal.utils.logging import indent_log +from pip._internal.utils.misc import dist_in_usersite, ensure_dir +from pip._internal.utils.packaging import check_dist_requires_python +from pip._internal.utils.typing import MYPY_CHECK_RUNNING + +if MYPY_CHECK_RUNNING: + from typing import Optional, DefaultDict, List, Set + from pip._internal.download import PipSession + from pip._internal.req.req_install import InstallRequirement + from pip._internal.index import PackageFinder + from pip._internal.req.req_set import RequirementSet + from pip._internal.operations.prepare import ( + DistAbstraction, RequirementPreparer + ) + from pip._internal.cache import WheelCache + +logger = logging.getLogger(__name__) + + +class Resolver(object): + """Resolves which packages need to be installed/uninstalled to perform \ + the requested operation without breaking the requirements of any package. + """ + + _allowed_strategies = {"eager", "only-if-needed", "to-satisfy-only"} + + def __init__( + self, + preparer, # type: RequirementPreparer + session, # type: PipSession + finder, # type: PackageFinder + wheel_cache, # type: Optional[WheelCache] + use_user_site, # type: bool + ignore_dependencies, # type: bool + ignore_installed, # type: bool + ignore_requires_python, # type: bool + force_reinstall, # type: bool + isolated, # type: bool + upgrade_strategy, # type: str + use_pep517=None # type: Optional[bool] + ): + # type: (...) -> None + super(Resolver, self).__init__() + assert upgrade_strategy in self._allowed_strategies + + self.preparer = preparer + self.finder = finder + self.session = session + + # NOTE: This would eventually be replaced with a cache that can give + # information about both sdist and wheels transparently. + self.wheel_cache = wheel_cache + + # This is set in resolve + self.require_hashes = None # type: Optional[bool] + + self.upgrade_strategy = upgrade_strategy + self.force_reinstall = force_reinstall + self.isolated = isolated + self.ignore_dependencies = ignore_dependencies + self.ignore_installed = ignore_installed + self.ignore_requires_python = ignore_requires_python + self.use_user_site = use_user_site + self.use_pep517 = use_pep517 + + self._discovered_dependencies = \ + defaultdict(list) # type: DefaultDict[str, List] + + def resolve(self, requirement_set): + # type: (RequirementSet) -> None + """Resolve what operations need to be done + + As a side-effect of this method, the packages (and their dependencies) + are downloaded, unpacked and prepared for installation. This + preparation is done by ``pip.operations.prepare``. + + Once PyPI has static dependency metadata available, it would be + possible to move the preparation to become a step separated from + dependency resolution. + """ + # make the wheelhouse + if self.preparer.wheel_download_dir: + ensure_dir(self.preparer.wheel_download_dir) + + # If any top-level requirement has a hash specified, enter + # hash-checking mode, which requires hashes from all. + root_reqs = ( + requirement_set.unnamed_requirements + + list(requirement_set.requirements.values()) + ) + self.require_hashes = ( + requirement_set.require_hashes or + any(req.has_hash_options for req in root_reqs) + ) + + # Display where finder is looking for packages + locations = self.finder.get_formatted_locations() + if locations: + logger.info(locations) + + # 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 = [] # type: List[InstallRequirement] + hash_errors = HashErrors() + for req in chain(root_reqs, discovered_reqs): + try: + discovered_reqs.extend( + self._resolve_one(requirement_set, req) + ) + except HashError as exc: + exc.req = req + hash_errors.append(exc) + + if hash_errors: + raise hash_errors + + def _is_upgrade_allowed(self, req): + # type: (InstallRequirement) -> bool + if self.upgrade_strategy == "to-satisfy-only": + return False + elif self.upgrade_strategy == "eager": + return True + else: + assert self.upgrade_strategy == "only-if-needed" + return req.is_direct + + def _set_req_to_reinstall(self, req): + # type: (InstallRequirement) -> None + """ + Set a requirement to be installed. + """ + # Don't uninstall the conflict if doing a user install and the + # conflict is not a user install. + if not self.use_user_site or dist_in_usersite(req.satisfied_by): + req.conflicts_with = req.satisfied_by + req.satisfied_by = None + + # XXX: Stop passing requirement_set for options + def _check_skip_installed(self, req_to_install): + # type: (InstallRequirement) -> Optional[str] + """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. + """ + if self.ignore_installed: + return None + + req_to_install.check_if_exists(self.use_user_site) + if not req_to_install.satisfied_by: + return None + + if self.force_reinstall: + self._set_req_to_reinstall(req_to_install) + return None + + if not self._is_upgrade_allowed(req_to_install): + if self.upgrade_strategy == "only-if-needed": + return 'already satisfied, skipping upgrade' + return 'already satisfied' + + # Check for the possibility of an upgrade. For link-based + # requirements we have to pull the tree down and inspect to assess + # the version #, so it's handled way down. + if not req_to_install.link: + try: + self.finder.find_requirement(req_to_install, upgrade=True) + except BestVersionAlreadyInstalled: + # Then the best version is installed. + return 'already up-to-date' + 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 + + self._set_req_to_reinstall(req_to_install) + return None + + def _get_abstract_dist_for(self, req): + # type: (InstallRequirement) -> DistAbstraction + """Takes a InstallRequirement and returns a single AbstractDist \ + representing a prepared variant of the same. + """ + assert self.require_hashes is not None, ( + "require_hashes should have been set in Resolver.resolve()" + ) + + if req.editable: + return self.preparer.prepare_editable_requirement( + req, self.require_hashes, self.use_user_site, self.finder, + ) + + # satisfied_by is only evaluated by calling _check_skip_installed, + # so it must be None here. + assert req.satisfied_by is None + skip_reason = self._check_skip_installed(req) + + if req.satisfied_by: + return self.preparer.prepare_installed_requirement( + req, self.require_hashes, skip_reason + ) + + upgrade_allowed = self._is_upgrade_allowed(req) + abstract_dist = self.preparer.prepare_linked_requirement( + req, self.session, self.finder, upgrade_allowed, + self.require_hashes + ) + + # NOTE + # The following portion is for determining if a certain package is + # going to be re-installed/upgraded or not and reporting to the user. + # This should probably get cleaned up in a future refactor. + + # req.req is only avail after unpack for URL + # pkgs repeat check_if_exists to uninstall-on-upgrade + # (#14) + if not self.ignore_installed: + req.check_if_exists(self.use_user_site) + + if req.satisfied_by: + should_modify = ( + self.upgrade_strategy != "to-satisfy-only" or + self.force_reinstall or + self.ignore_installed or + req.link.scheme == 'file' + ) + if should_modify: + self._set_req_to_reinstall(req) + else: + logger.info( + 'Requirement already satisfied (use --upgrade to upgrade):' + ' %s', req, + ) + + return abstract_dist + + def _resolve_one( + self, + requirement_set, # type: RequirementSet + req_to_install # type: InstallRequirement + ): + # type: (...) -> List[InstallRequirement] + """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 + + # register tmp src for cleanup in case something goes wrong + requirement_set.reqs_to_cleanup.append(req_to_install) + + abstract_dist = self._get_abstract_dist_for(req_to_install) + + # Parse and return dependencies + dist = abstract_dist.dist() + try: + check_dist_requires_python(dist) + except UnsupportedPythonVersion as err: + if self.ignore_requires_python: + logger.warning(err.args[0]) + else: + raise + + more_reqs = [] # type: List[InstallRequirement] + + def add_req(subreq, extras_requested): + sub_install_req = install_req_from_req_string( + str(subreq), + req_to_install, + isolated=self.isolated, + wheel_cache=self.wheel_cache, + use_pep517=self.use_pep517 + ) + parent_req_name = req_to_install.name + to_scan_again, add_to_parent = requirement_set.add_requirement( + sub_install_req, + parent_req_name=parent_req_name, + extras_requested=extras_requested, + ) + if parent_req_name and add_to_parent: + self._discovered_dependencies[parent_req_name].append( + add_to_parent + ) + more_reqs.extend(to_scan_again) + + with indent_log(): + # We add req_to_install before its dependencies, so that we + # can refer to it when adding dependencies. + if not requirement_set.has_requirement(req_to_install.name): + # 'unnamed' requirements will get added here + req_to_install.is_direct = True + requirement_set.add_requirement( + req_to_install, parent_req_name=None, + ) + + if not self.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, extras_requested=available_requested) + + 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. + requirement_set.successfully_downloaded.append(req_to_install) + + return more_reqs + + def get_installation_order(self, req_set): + # type: (RequirementSet) -> List[InstallRequirement] + """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() # type: Set[InstallRequirement] + + 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._discovered_dependencies[req.name]: + schedule(dep) + order.append(req) + + for install_req in req_set.requirements.values(): + schedule(install_req) + return order diff --git a/venv2/lib/python2.7/site-packages/pip/_internal/utils/__init__.py b/venv2/lib/python2.7/site-packages/pip/_internal/utils/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/venv2/lib/python2.7/site-packages/pip/_internal/utils/appdirs.py b/venv2/lib/python2.7/site-packages/pip/_internal/utils/appdirs.py new file mode 100644 index 0000000..fb26111 --- /dev/null +++ b/venv2/lib/python2.7/site-packages/pip/_internal/utils/appdirs.py @@ -0,0 +1,268 @@ +""" +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._vendor.six import PY2, text_type + +from pip._internal.utils.compat import WINDOWS, expanduser +from pip._internal.utils.typing import MYPY_CHECK_RUNNING + +if MYPY_CHECK_RUNNING: + from typing import List + + +def user_cache_dir(appname): + # type: (str) -> str + r""" + Return full path to the user-specific cache dir for this application. + + "appname" is the name of application. + + Typical user cache directories are: + macOS: ~/Library/Caches/ + Unix: ~/.cache/ (XDG default) + Windows: C:\Users\\AppData\Local\\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\\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")) + + # When using Python 2, return paths as bytes on Windows like we do on + # other operating systems. See helper function docs for more details. + if PY2 and isinstance(path, text_type): + path = _win_path_to_bytes(path) + + # 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): + # type: (str, bool) -> str + r""" + 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 + + for a discussion of issues. + + Typical user data directories are: + macOS: ~/Library/Application Support/ + if it exists, else ~/.config/ + Unix: ~/.local/share/ # or in + $XDG_DATA_HOME, if defined + Win XP (not roaming): C:\Documents and Settings\\ ... + ...Application Data\ + Win XP (roaming): C:\Documents and Settings\\Local ... + ...Settings\Application Data\ + Win 7 (not roaming): C:\\Users\\AppData\Local\ + Win 7 (roaming): C:\\Users\\AppData\Roaming\ + + For Unix, we follow the XDG spec and support $XDG_DATA_HOME. + That means, by default "~/.local/share/". + """ + 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, + ) if os.path.isdir(os.path.join( + expanduser('~/Library/Application Support/'), + appname, + ) + ) else os.path.join( + expanduser('~/.config/'), + appname, + ) + else: + path = os.path.join( + os.getenv('XDG_DATA_HOME', expanduser("~/.local/share")), + appname, + ) + + return path + + +def user_config_dir(appname, roaming=True): + # type: (str, bool) -> str + """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 + + for a discussion of issues. + + Typical user data directories are: + macOS: same as user_data_dir + Unix: ~/.config/ + Win *: same as user_data_dir + + For Unix, we follow the XDG spec and support $XDG_CONFIG_HOME. + That means, by default "~/.config/". + """ + 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 +def site_config_dirs(appname): + # type: (str) -> List[str] + r"""Return a list of potential user-shared config dirs for this application. + + "appname" is the name of application. + + Typical user config directories are: + macOS: /Library/Application Support// + Unix: /etc or $XDG_CONFIG_DIRS[i]// for each value in + $XDG_CONFIG_DIRS + Win XP: C:\Documents and Settings\All Users\Application ... + ...Data\\ + Vista: (Fail! "C:\ProgramData" is a hidden *system* directory + on Vista.) + Win 7: Hidden, but writeable on Win 7: + C:\ProgramData\\ + """ + 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): + # type: (str) -> str + """ + 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): + # type: (str) -> str + 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 + # . + 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 + + +def _win_path_to_bytes(path): + """Encode Windows paths to bytes. Only used on Python 2. + + Motivation is to be consistent with other operating systems where paths + are also returned as bytes. This avoids problems mixing bytes and Unicode + elsewhere in the codebase. For more details and discussion see + . + + If encoding using ASCII and MBCS fails, return the original Unicode path. + """ + for encoding in ('ASCII', 'MBCS'): + try: + return path.encode(encoding) + except (UnicodeEncodeError, LookupError): + pass + return path diff --git a/venv2/lib/python2.7/site-packages/pip/_internal/utils/compat.py b/venv2/lib/python2.7/site-packages/pip/_internal/utils/compat.py new file mode 100644 index 0000000..845436e --- /dev/null +++ b/venv2/lib/python2.7/site-packages/pip/_internal/utils/compat.py @@ -0,0 +1,264 @@ +"""Stuff that differs in different Python versions and platform +distributions.""" +from __future__ import absolute_import, division + +import codecs +import locale +import logging +import os +import shutil +import sys + +from pip._vendor.six import text_type + +from pip._internal.utils.typing import MYPY_CHECK_RUNNING + +if MYPY_CHECK_RUNNING: + from typing import Tuple, Text + +try: + import ipaddress +except ImportError: + try: + from pip._vendor import ipaddress # type: ignore + except ImportError: + import ipaddr as ipaddress # type: ignore + ipaddress.ip_address = ipaddress.IPAddress # type: ignore + ipaddress.ip_network = ipaddress.IPNetwork # type: ignore + + +__all__ = [ + "ipaddress", "uses_pycache", "console_to_str", "native_str", + "get_path_uid", "stdlib_pkgs", "WINDOWS", "samefile", "get_terminal_size", + "get_extension_suffixes", +] + + +logger = logging.getLogger(__name__) + +if sys.version_info >= (3, 4): + uses_pycache = True + from importlib.util import cache_from_source +else: + import imp + + try: + cache_from_source = imp.cache_from_source # type: ignore + except AttributeError: + # does not use __pycache__ + cache_from_source = None + + uses_pycache = cache_from_source is not None + + +if sys.version_info >= (3, 5): + backslashreplace_decode = "backslashreplace" +else: + # In version 3.4 and older, backslashreplace exists + # but does not support use for decoding. + # We implement our own replace handler for this + # situation, so that we can consistently use + # backslash replacement for all versions. + def backslashreplace_decode_fn(err): + raw_bytes = (err.object[i] for i in range(err.start, err.end)) + if sys.version_info[0] == 2: + # Python 2 gave us characters - convert to numeric bytes + raw_bytes = (ord(b) for b in raw_bytes) + return u"".join(u"\\x%x" % c for c in raw_bytes), err.end + codecs.register_error( + "backslashreplace_decode", + backslashreplace_decode_fn, + ) + backslashreplace_decode = "backslashreplace_decode" + + +def console_to_str(data): + # type: (bytes) -> Text + """Return a string, safe for output, of subprocess output. + + We assume the data is in the locale preferred encoding. + If it won't decode properly, we warn the user but decode as + best we can. + + We also ensure that the output can be safely written to + standard output without encoding errors. + """ + + # First, get the encoding we assume. This is the preferred + # encoding for the locale, unless that is not found, or + # it is ASCII, in which case assume UTF-8 + encoding = locale.getpreferredencoding() + if (not encoding) or codecs.lookup(encoding).name == "ascii": + encoding = "utf-8" + + # Now try to decode the data - if we fail, warn the user and + # decode with replacement. + try: + decoded_data = data.decode(encoding) + except UnicodeDecodeError: + logger.warning( + "Subprocess output does not appear to be encoded as %s", + encoding, + ) + decoded_data = data.decode(encoding, errors=backslashreplace_decode) + + # Make sure we can print the output, by encoding it to the output + # encoding with replacement of unencodable characters, and then + # decoding again. + # We use stderr's encoding because it's less likely to be + # redirected and if we don't find an encoding we skip this + # step (on the assumption that output is wrapped by something + # that won't fail). + # The double getattr is to deal with the possibility that we're + # being called in a situation where sys.__stderr__ doesn't exist, + # or doesn't have an encoding attribute. Neither of these cases + # should occur in normal pip use, but there's no harm in checking + # in case people use pip in (unsupported) unusual situations. + output_encoding = getattr(getattr(sys, "__stderr__", None), + "encoding", None) + + if output_encoding: + output_encoded = decoded_data.encode( + output_encoding, + errors="backslashreplace" + ) + decoded_data = output_encoded.decode(output_encoding) + + return decoded_data + + +if sys.version_info >= (3,): + def native_str(s, replace=False): + # type: (str, bool) -> str + if isinstance(s, bytes): + return s.decode('utf-8', 'replace' if replace else 'strict') + return s + +else: + def native_str(s, replace=False): + # type: (str, bool) -> str + # Replace is ignored -- unicode to UTF-8 can't fail + if isinstance(s, text_type): + return s.encode('utf-8') + return s + + +def get_path_uid(path): + # type: (str) -> int + """ + 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 vulnerability, 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 + + +if sys.version_info >= (3, 4): + from importlib.machinery import EXTENSION_SUFFIXES + + def get_extension_suffixes(): + return EXTENSION_SUFFIXES +else: + from imp import get_suffixes + + def get_extension_suffixes(): + return [suffix[0] for suffix in get_suffixes()] + + +def expanduser(path): + # type: (str) -> str + """ + Expand ~ and ~user constructions. + + Includes a workaround for https://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", "argparse"} + + +# windows detection, covers cpython and ironpython +WINDOWS = (sys.platform.startswith("win") or + (sys.platform == 'cli' and os.name == 'nt')) + + +def samefile(file1, file2): + # type: (str, str) -> bool + """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 + + +if hasattr(shutil, 'get_terminal_size'): + def get_terminal_size(): + # type: () -> Tuple[int, int] + """ + Returns a tuple (x, y) representing the width(x) and the height(y) + in characters of the terminal window. + """ + return tuple(shutil.get_terminal_size()) # type: ignore +else: + def get_terminal_size(): + # type: () -> Tuple[int, int] + """ + Returns a tuple (x, y) representing the width(x) and the height(y) + in characters of the terminal window. + """ + def ioctl_GWINSZ(fd): + try: + import fcntl + import termios + import struct + cr = struct.unpack_from( + 'hh', + fcntl.ioctl(fd, termios.TIOCGWINSZ, '12345678') + ) + except Exception: + 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 Exception: + pass + if not cr: + cr = (os.environ.get('LINES', 25), os.environ.get('COLUMNS', 80)) + return int(cr[1]), int(cr[0]) diff --git a/venv2/lib/python2.7/site-packages/pip/_internal/utils/deprecation.py b/venv2/lib/python2.7/site-packages/pip/_internal/utils/deprecation.py new file mode 100644 index 0000000..8c896f8 --- /dev/null +++ b/venv2/lib/python2.7/site-packages/pip/_internal/utils/deprecation.py @@ -0,0 +1,93 @@ +""" +A module that implements tooling to enable easy warnings about deprecations. +""" +from __future__ import absolute_import + +import logging +import warnings + +from pip._vendor.packaging.version import parse + +from pip import __version__ as current_version +from pip._internal.utils.typing import MYPY_CHECK_RUNNING + +if MYPY_CHECK_RUNNING: + from typing import Any, Optional + + +DEPRECATION_MSG_PREFIX = "DEPRECATION: " + + +class PipDeprecationWarning(Warning): + pass + + +_original_showwarning = None # type: Any + + +# Warnings <-> Logging Integration +def _showwarning(message, category, filename, lineno, file=None, line=None): + if file is not None: + if _original_showwarning is not None: + _original_showwarning( + message, category, filename, lineno, file, line, + ) + elif issubclass(category, PipDeprecationWarning): + # We use a specially named logger which will handle all of the + # deprecation messages for pip. + logger = logging.getLogger("pip._internal.deprecations") + logger.warning(message) + else: + _original_showwarning( + message, category, filename, lineno, file, line, + ) + + +def install_warning_logger(): + # type: () -> None + # Enable our Deprecation Warnings + warnings.simplefilter("default", PipDeprecationWarning, append=True) + + global _original_showwarning + + if _original_showwarning is None: + _original_showwarning = warnings.showwarning + warnings.showwarning = _showwarning + + +def deprecated(reason, replacement, gone_in, issue=None): + # type: (str, Optional[str], Optional[str], Optional[int]) -> None + """Helper to deprecate existing functionality. + + reason: + Textual reason shown to the user about why this functionality has + been deprecated. + replacement: + Textual suggestion shown to the user about what alternative + functionality they can use. + gone_in: + The version of pip does this functionality should get removed in. + Raises errors if pip's current version is greater than or equal to + this. + issue: + Issue number on the tracker that would serve as a useful place for + users to find related discussion and provide feedback. + + Always pass replacement, gone_in and issue as keyword arguments for clarity + at the call site. + """ + + # Construct a nice message. + # This is purposely eagerly formatted as we want it to appear as if someone + # typed this entire message out. + message = DEPRECATION_MSG_PREFIX + reason + if replacement is not None: + message += " A possible replacement is {}.".format(replacement) + if issue is not None: + url = "https://github.com/pypa/pip/issues/" + str(issue) + message += " You can find discussion regarding this at {}.".format(url) + + # Raise as an error if it has to be removed. + if gone_in is not None and parse(current_version) >= parse(gone_in): + raise PipDeprecationWarning(message) + warnings.warn(message, category=PipDeprecationWarning, stacklevel=2) diff --git a/venv2/lib/python2.7/site-packages/pip/_internal/utils/encoding.py b/venv2/lib/python2.7/site-packages/pip/_internal/utils/encoding.py new file mode 100644 index 0000000..30139f2 --- /dev/null +++ b/venv2/lib/python2.7/site-packages/pip/_internal/utils/encoding.py @@ -0,0 +1,39 @@ +import codecs +import locale +import re +import sys + +from pip._internal.utils.typing import MYPY_CHECK_RUNNING + +if MYPY_CHECK_RUNNING: + from typing import List, Tuple, Text + +BOMS = [ + (codecs.BOM_UTF8, 'utf-8'), + (codecs.BOM_UTF16, 'utf-16'), + (codecs.BOM_UTF16_BE, 'utf-16-be'), + (codecs.BOM_UTF16_LE, 'utf-16-le'), + (codecs.BOM_UTF32, 'utf-32'), + (codecs.BOM_UTF32_BE, 'utf-32-be'), + (codecs.BOM_UTF32_LE, 'utf-32-le'), +] # type: List[Tuple[bytes, Text]] + +ENCODING_RE = re.compile(br'coding[:=]\s*([-\w.]+)') + + +def auto_decode(data): + # type: (bytes) -> Text + """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) or sys.getdefaultencoding(), + ) diff --git a/venv2/lib/python2.7/site-packages/pip/_internal/utils/filesystem.py b/venv2/lib/python2.7/site-packages/pip/_internal/utils/filesystem.py new file mode 100644 index 0000000..1e6b033 --- /dev/null +++ b/venv2/lib/python2.7/site-packages/pip/_internal/utils/filesystem.py @@ -0,0 +1,30 @@ +import os +import os.path + +from pip._internal.utils.compat import get_path_uid + + +def check_path_owner(path): + # type: (str) -> bool + # 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) + return False # assume we don't own the path diff --git a/venv2/lib/python2.7/site-packages/pip/_internal/utils/glibc.py b/venv2/lib/python2.7/site-packages/pip/_internal/utils/glibc.py new file mode 100644 index 0000000..5bea655 --- /dev/null +++ b/venv2/lib/python2.7/site-packages/pip/_internal/utils/glibc.py @@ -0,0 +1,93 @@ +from __future__ import absolute_import + +import ctypes +import re +import warnings + +from pip._internal.utils.typing import MYPY_CHECK_RUNNING + +if MYPY_CHECK_RUNNING: + from typing import Optional, Tuple + + +def glibc_version_string(): + # type: () -> Optional[str] + "Returns glibc version string, or None if not using glibc." + + # 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 None + + # 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") + + return version_str + + +# Separated out from have_compatible_glibc for easier unit testing +def check_glibc_version(version_str, required_major, minimum_minor): + # type: (str, int, int) -> bool + # Parse string and check against requested version. + # + # We use a regexp instead of str.split because we want to discard any + # random junk that might come after the minor version -- this might happen + # in patched/forked versions of glibc (e.g. Linaro's version of glibc + # uses version strings like "2.20-2014.11"). See gh-3588. + m = re.match(r"(?P[0-9]+)\.(?P[0-9]+)", version_str) + if not m: + warnings.warn("Expected glibc version with 2 components major.minor," + " got: %s" % version_str, RuntimeWarning) + return False + return (int(m.group("major")) == required_major and + int(m.group("minor")) >= minimum_minor) + + +def have_compatible_glibc(required_major, minimum_minor): + # type: (int, int) -> bool + version_str = glibc_version_string() # type: Optional[str] + if version_str is None: + return False + return check_glibc_version(version_str, required_major, minimum_minor) + + +# platform.libc_ver regularly returns completely nonsensical glibc +# versions. E.g. on my computer, platform says: +# +# ~$ python2.7 -c 'import platform; print(platform.libc_ver())' +# ('glibc', '2.7') +# ~$ python3.5 -c 'import platform; print(platform.libc_ver())' +# ('glibc', '2.9') +# +# But the truth is: +# +# ~$ ldd --version +# ldd (Debian GLIBC 2.22-11) 2.22 +# +# This is unfortunate, because it means that the linehaul data on libc +# versions that was generated by pip 8.1.2 and earlier is useless and +# misleading. Solution: instead of using platform, use our code that actually +# works. +def libc_ver(): + # type: () -> Tuple[str, str] + """Try to determine the glibc version + + Returns a tuple of strings (lib, version) which default to empty strings + in case the lookup fails. + """ + glibc_version = glibc_version_string() + if glibc_version is None: + return ("", "") + else: + return ("glibc", glibc_version) diff --git a/venv2/lib/python2.7/site-packages/pip/_internal/utils/hashes.py b/venv2/lib/python2.7/site-packages/pip/_internal/utils/hashes.py new file mode 100644 index 0000000..a714206 --- /dev/null +++ b/venv2/lib/python2.7/site-packages/pip/_internal/utils/hashes.py @@ -0,0 +1,115 @@ +from __future__ import absolute_import + +import hashlib + +from pip._vendor.six import iteritems, iterkeys, itervalues + +from pip._internal.exceptions import ( + HashMismatch, HashMissing, InstallationError, +) +from pip._internal.utils.misc import read_chunks +from pip._internal.utils.typing import MYPY_CHECK_RUNNING + +if MYPY_CHECK_RUNNING: + from typing import ( + Dict, List, BinaryIO, NoReturn, Iterator + ) + from pip._vendor.six import PY3 + if PY3: + from hashlib import _Hash + else: + from hashlib import _hash as _Hash + + +# 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): + # type: (Dict[str, List[str]]) -> 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): + # type: (Iterator[bytes]) -> None + """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): + # type: (Dict[str, _Hash]) -> NoReturn + raise HashMismatch(self._allowed, gots) + + def check_against_file(self, file): + # type: (BinaryIO) -> None + """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): + # type: (str) -> None + with open(path, 'rb') as file: + return self.check_against_file(file) + + def __nonzero__(self): + # type: () -> bool + """Return whether I know any known-good hashes.""" + return bool(self._allowed) + + def __bool__(self): + # type: () -> bool + 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): + # type: () -> None + """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): + # type: (Dict[str, _Hash]) -> NoReturn + raise HashMissing(gots[FAVORITE_HASH].hexdigest()) diff --git a/venv2/lib/python2.7/site-packages/pip/_internal/utils/logging.py b/venv2/lib/python2.7/site-packages/pip/_internal/utils/logging.py new file mode 100644 index 0000000..a28e88c --- /dev/null +++ b/venv2/lib/python2.7/site-packages/pip/_internal/utils/logging.py @@ -0,0 +1,371 @@ +from __future__ import absolute_import + +import contextlib +import errno +import logging +import logging.handlers +import os +import sys +from logging import Filter + +from pip._vendor.six import PY2 + +from pip._internal.utils.compat import WINDOWS +from pip._internal.utils.deprecation import DEPRECATION_MSG_PREFIX +from pip._internal.utils.misc import ensure_dir, subprocess_logger + +try: + import threading +except ImportError: + import dummy_threading as threading # type: ignore + + +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 + + +class BrokenStdoutLoggingError(Exception): + """ + Raised if BrokenPipeError occurs for the stdout stream while logging. + """ + pass + + +# BrokenPipeError does not exist in Python 2 and, in addition, manifests +# differently in Windows and non-Windows. +if WINDOWS: + # In Windows, a broken pipe can show up as EINVAL rather than EPIPE: + # https://bugs.python.org/issue19612 + # https://bugs.python.org/issue30418 + if PY2: + def _is_broken_pipe_error(exc_class, exc): + """See the docstring for non-Windows Python 3 below.""" + return (exc_class is IOError and + exc.errno in (errno.EINVAL, errno.EPIPE)) + else: + # In Windows, a broken pipe IOError became OSError in Python 3. + def _is_broken_pipe_error(exc_class, exc): + """See the docstring for non-Windows Python 3 below.""" + return ((exc_class is BrokenPipeError) or # noqa: F821 + (exc_class is OSError and + exc.errno in (errno.EINVAL, errno.EPIPE))) +elif PY2: + def _is_broken_pipe_error(exc_class, exc): + """See the docstring for non-Windows Python 3 below.""" + return (exc_class is IOError and exc.errno == errno.EPIPE) +else: + # Then we are in the non-Windows Python 3 case. + def _is_broken_pipe_error(exc_class, exc): + """ + Return whether an exception is a broken pipe error. + + Args: + exc_class: an exception class. + exc: an exception instance. + """ + return (exc_class is BrokenPipeError) # noqa: F821 + + +@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 __init__(self, *args, **kwargs): + """ + A logging.Formatter that obeys the indent_log() context manager. + + :param add_timestamp: A bool indicating output lines should be prefixed + with their record's timestamp. + """ + self.add_timestamp = kwargs.pop("add_timestamp", False) + super(IndentingFormatter, self).__init__(*args, **kwargs) + + def get_message_start(self, formatted, levelno): + """ + Return the start of the formatted log message (not counting the + prefix to add to each line). + """ + if levelno < logging.WARNING: + return '' + if formatted.startswith(DEPRECATION_MSG_PREFIX): + # Then the message already has a prefix. We don't want it to + # look like "WARNING: DEPRECATION: ...." + return '' + if levelno < logging.ERROR: + return 'WARNING: ' + + return 'ERROR: ' + + def format(self, record): + """ + Calls the standard formatter, but will indent all of the log messages + by our current indentation level. + """ + formatted = super(IndentingFormatter, self).format(record) + message_start = self.get_message_start(formatted, record.levelno) + formatted = message_start + formatted + + prefix = '' + if self.add_timestamp: + prefix = self.formatTime(record, "%Y-%m-%dT%H:%M:%S ") + prefix += " " * get_indentation() + formatted = "".join([ + prefix + 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, no_color=None): + logging.StreamHandler.__init__(self, stream) + self._no_color = no_color + + if WINDOWS and colorama: + self.stream = colorama.AnsiToWin32(self.stream) + + def _using_stdout(self): + """ + Return whether the handler is using sys.stdout. + """ + if WINDOWS and colorama: + # Then self.stream is an AnsiToWin32 object. + return self.stream.wrapped is sys.stdout + + return self.stream is sys.stdout + + def should_color(self): + # Don't colorize things if we do not have colorama or if told not to + if not colorama or self._no_color: + 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 ANSI 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 + + # The logging module says handleError() can be customized. + def handleError(self, record): + exc_class, exc = sys.exc_info()[:2] + # If a broken pipe occurred while calling write() or flush() on the + # stdout stream in logging's Handler.emit(), then raise our special + # exception so we can handle it in main() instead of logging the + # broken pipe error and continuing. + if (exc_class and self._using_stdout() and + _is_broken_pipe_error(exc_class, exc)): + raise BrokenStdoutLoggingError() + + return super(ColorizedStreamHandler, self).handleError(record) + + +class BetterRotatingFileHandler(logging.handlers.RotatingFileHandler): + + def _open(self): + ensure_dir(os.path.dirname(self.baseFilename)) + return logging.handlers.RotatingFileHandler._open(self) + + +class MaxLevelFilter(Filter): + + def __init__(self, level): + self.level = level + + def filter(self, record): + return record.levelno < self.level + + +class ExcludeLoggerFilter(Filter): + + """ + A logging Filter that excludes records from a logger (or its children). + """ + + def filter(self, record): + # The base Filter class allows only records from a logger (or its + # children). + return not super(ExcludeLoggerFilter, self).filter(record) + + +def setup_logging(verbosity, no_color, user_log_file): + """Configures and sets up all of the logging + + Returns the requested logging level, as its integer value. + """ + + # Determine the level to be logging at. + if verbosity >= 1: + level = "DEBUG" + elif verbosity == -1: + level = "WARNING" + elif verbosity == -2: + level = "ERROR" + elif verbosity <= -3: + level = "CRITICAL" + else: + level = "INFO" + + level_number = getattr(logging, level) + + # The "root" logger should match the "console" level *unless* we also need + # to log to a user log file. + include_user_log = user_log_file is not None + if include_user_log: + additional_log_file = user_log_file + root_level = "DEBUG" + else: + additional_log_file = "/dev/null" + root_level = level + + # Disable any logging besides WARNING unless we have DEBUG level logging + # enabled for vendored libraries. + vendored_log_level = "WARNING" if level in ["INFO", "ERROR"] else "DEBUG" + + # Shorthands for clarity + log_streams = { + "stdout": "ext://sys.stdout", + "stderr": "ext://sys.stderr", + } + handler_classes = { + "stream": "pip._internal.utils.logging.ColorizedStreamHandler", + "file": "pip._internal.utils.logging.BetterRotatingFileHandler", + } + handlers = ["console", "console_errors", "console_subprocess"] + ( + ["user_log"] if include_user_log else [] + ) + + logging.config.dictConfig({ + "version": 1, + "disable_existing_loggers": False, + "filters": { + "exclude_warnings": { + "()": "pip._internal.utils.logging.MaxLevelFilter", + "level": logging.WARNING, + }, + "restrict_to_subprocess": { + "()": "logging.Filter", + "name": subprocess_logger.name, + }, + "exclude_subprocess": { + "()": "pip._internal.utils.logging.ExcludeLoggerFilter", + "name": subprocess_logger.name, + }, + }, + "formatters": { + "indent": { + "()": IndentingFormatter, + "format": "%(message)s", + }, + "indent_with_timestamp": { + "()": IndentingFormatter, + "format": "%(message)s", + "add_timestamp": True, + }, + }, + "handlers": { + "console": { + "level": level, + "class": handler_classes["stream"], + "no_color": no_color, + "stream": log_streams["stdout"], + "filters": ["exclude_subprocess", "exclude_warnings"], + "formatter": "indent", + }, + "console_errors": { + "level": "WARNING", + "class": handler_classes["stream"], + "no_color": no_color, + "stream": log_streams["stderr"], + "filters": ["exclude_subprocess"], + "formatter": "indent", + }, + # A handler responsible for logging to the console messages + # from the "subprocessor" logger. + "console_subprocess": { + "level": level, + "class": handler_classes["stream"], + "no_color": no_color, + "stream": log_streams["stderr"], + "filters": ["restrict_to_subprocess"], + "formatter": "indent", + }, + "user_log": { + "level": "DEBUG", + "class": handler_classes["file"], + "filename": additional_log_file, + "delay": True, + "formatter": "indent_with_timestamp", + }, + }, + "root": { + "level": root_level, + "handlers": handlers, + }, + "loggers": { + "pip._vendor": { + "level": vendored_log_level + } + }, + }) + + return level_number diff --git a/venv2/lib/python2.7/site-packages/pip/_internal/utils/misc.py b/venv2/lib/python2.7/site-packages/pip/_internal/utils/misc.py new file mode 100644 index 0000000..ca7a529 --- /dev/null +++ b/venv2/lib/python2.7/site-packages/pip/_internal/utils/misc.py @@ -0,0 +1,1011 @@ +from __future__ import absolute_import + +import contextlib +import errno +import io +# we have a submodule named 'logging' which would shadow this if we used the +# regular name: +import logging as std_logging +import os +import posixpath +import re +import shutil +import stat +import subprocess +import sys +import tarfile +import zipfile +from collections import deque + +from pip._vendor import pkg_resources +# NOTE: retrying is not annotated in typeshed as on 2017-07-17, which is +# why we ignore the type on this import. +from pip._vendor.retrying import retry # type: ignore +from pip._vendor.six import PY2 +from pip._vendor.six.moves import input, shlex_quote +from pip._vendor.six.moves.urllib import parse as urllib_parse +from pip._vendor.six.moves.urllib.parse import unquote as urllib_unquote + +from pip._internal.exceptions import CommandError, InstallationError +from pip._internal.locations import ( + running_under_virtualenv, site_packages, user_site, virtualenv_no_global, + write_delete_marker_file, +) +from pip._internal.utils.compat import ( + WINDOWS, console_to_str, expanduser, stdlib_pkgs, +) +from pip._internal.utils.typing import MYPY_CHECK_RUNNING + +if PY2: + from io import BytesIO as StringIO +else: + from io import StringIO + +if MYPY_CHECK_RUNNING: + from typing import ( + Optional, Tuple, Iterable, List, Match, Union, Any, Mapping, Text, + AnyStr, Container + ) + from pip._vendor.pkg_resources import Distribution + from pip._internal.models.link import Link + from pip._internal.utils.ui import SpinnerInterface + + +__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_prog', + 'unzip_file', 'untar_file', 'unpack_file', 'call_subprocess', + 'captured_stdout', 'ensure_dir', + 'ARCHIVE_EXTENSIONS', 'SUPPORTED_EXTENSIONS', 'WHEEL_EXTENSION', + 'get_installed_version', 'remove_auth_from_url'] + + +logger = std_logging.getLogger(__name__) +subprocess_logger = std_logging.getLogger('pip.subprocessor') + +LOG_DIVIDER = '----------------------------------------' + +WHEEL_EXTENSION = '.whl' +BZ2_EXTENSIONS = ('.tar.bz2', '.tbz') +XZ_EXTENSIONS = ('.tar.xz', '.txz', '.tlz', '.tar.lz', '.tar.lzma') +ZIP_EXTENSIONS = ('.zip', WHEEL_EXTENSION) +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 ensure_dir(path): + # type: (AnyStr) -> None + """os.path.makedirs without EEXIST.""" + try: + os.makedirs(path) + except OSError as e: + if e.errno != errno.EEXIST: + raise + + +def get_prog(): + # type: () -> str + try: + prog = os.path.basename(sys.argv[0]) + if prog in ('__main__.py', '-c'): + return "%s -m pip" % sys.executable + else: + return prog + 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): + # type: (str, bool) -> None + 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): + # type: (Union[str, Text]) -> str + """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'): + # type: (str, str) -> str + """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): + # type: (str, Iterable[str]) -> str + for action in os.environ.get('PIP_EXISTS_ACTION', '').split(): + if action in options: + return action + return ask(message, options) + + +def ask(message, options): + # type: (str, Iterable[str]) -> str + """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): + # type: (float) -> str + 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): + # type: (str) -> bool + """Is path is a directory containing setup.py or pyproject.toml? + """ + if not os.path.isdir(path): + return False + setup_py = os.path.join(path, 'setup.py') + if os.path.isfile(setup_py): + return True + pyproject_toml = os.path.join(path, 'pyproject.toml') + if os.path.isfile(pyproject_toml): + return True + return False + + +def is_svn_page(html): + # type: (Union[str, Text]) -> Optional[Match[Union[str, Text]]] + """ + 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): + # type: (str) -> Text + 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): + # type: (Union[str, Text]) -> List[Union[str, Text]] + 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): + # type: (Iterable[Union[str, Text]]) -> bool + """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): + # type: (str, bool) -> str + """ + 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): + # type: (str) -> Tuple[str, str] + """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): + # type: (str, str) -> None + """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): + # type: (str) -> bool + """ + Return True if path is within sys.prefix, if we're running in a virtualenv. + + If we're not in a virtualenv, all paths are considered "local." + + """ + if not running_under_virtualenv(): + return True + return normalize_path(path).startswith(normalize_path(sys.prefix)) + + +def dist_is_local(dist): + # type: (Distribution) -> bool + """ + Return True if given Distribution object is installed locally + (i.e. within current virtualenv). + + Always True if we're not in a virtualenv. + + """ + return is_local(dist_location(dist)) + + +def dist_in_usersite(dist): + # type: (Distribution) -> bool + """ + 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): + # type: (Distribution) -> bool + """ + Return True if given Distribution is installed in + sysconfig.get_python_lib(). + """ + return normalize_path( + dist_location(dist) + ).startswith(normalize_path(site_packages)) + + +def dist_is_editable(dist): + # type: (Distribution) -> bool + """ + Return True if given Distribution is 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): + # type: (bool, Container[str], bool, bool, bool) -> List[Distribution] + """ + 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 ``include_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 + + # because of pkg_resources vendoring, mypy cannot find stub in typeshed + return [d for d in pkg_resources.working_set # type: ignore + 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): + # type: (Distribution) -> Optional[str] + """ + 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 + return None + + +def dist_location(dist): + # type: (Distribution) -> str + """ + 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 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): + # type: (str, str, bool) -> None + """ + 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 + 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) + # Don't use read() to avoid allocating an arbitrarily large + # chunk of memory for the file's content + fp = zip.open(name) + try: + with open(fn, 'wb') as destfp: + shutil.copyfileobj(fp, destfp) + 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): + # type: (str, str) -> None + """ + 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: + leading = has_leading_dir([ + member.name for member in tar.getmembers() + ]) + for member in tar.getmembers(): + fn = member.name + if leading: + # https://github.com/python/mypy/issues/1174 + fn = split_leading_dir(fn)[1] # type: ignore + path = os.path.join(location, fn) + if member.isdir(): + ensure_dir(path) + elif member.issym(): + try: + # https://github.com/python/typeshed/issues/2673 + tar._extract_member(member, path) # type: ignore + 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) + # https://github.com/python/typeshed/issues/2673 + tar.utime(member, path) # type: ignore + # 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, # type: str + location, # type: str + content_type, # type: Optional[str] + link # type: Optional[Link] +): + # type: (...) -> None + 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._internal.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 format_command_args(args): + # type: (List[str]) -> str + """ + Format command arguments for display. + """ + return ' '.join(shlex_quote(arg) for arg in args) + + +def call_subprocess( + cmd, # type: List[str] + show_stdout=False, # type: bool + cwd=None, # type: Optional[str] + on_returncode='raise', # type: str + extra_ok_returncodes=None, # type: Optional[Iterable[int]] + command_desc=None, # type: Optional[str] + extra_environ=None, # type: Optional[Mapping[str, Any]] + unset_environ=None, # type: Optional[Iterable[str]] + spinner=None # type: Optional[SpinnerInterface] +): + # type: (...) -> Optional[Text] + """ + Args: + show_stdout: if true, use INFO to log the subprocess's stderr and + stdout streams. Otherwise, use DEBUG. Defaults to False. + extra_ok_returncodes: an iterable of integer return codes that are + acceptable, in addition to 0. Defaults to None, which means []. + unset_environ: an iterable of environment variable names to unset + prior to calling subprocess.Popen(). + """ + if extra_ok_returncodes is None: + extra_ok_returncodes = [] + if unset_environ is None: + unset_environ = [] + # Most places in pip use show_stdout=False. What this means is-- + # + # - We connect the child's output (combined stderr and stdout) to a + # single pipe, which we read. + # - We log this output to stderr at DEBUG level as it is received. + # - If DEBUG logging isn't enabled (e.g. if --verbose logging wasn't + # requested), then we show a spinner so the user can still see the + # subprocess is in progress. + # - If the subprocess exits with an error, we log the output to stderr + # at ERROR level if it hasn't already been displayed to the console + # (e.g. if --verbose logging wasn't enabled). This way we don't log + # the output to the console twice. + # + # If show_stdout=True, then the above is still done, but with DEBUG + # replaced by INFO. + if show_stdout: + # Then log the subprocess output at INFO level. + log_subprocess = subprocess_logger.info + used_level = std_logging.INFO + else: + # Then log the subprocess output using DEBUG. This also ensures + # it will be logged to the log file (aka user_log), if enabled. + log_subprocess = subprocess_logger.debug + used_level = std_logging.DEBUG + + # Whether the subprocess will be visible in the console. + showing_subprocess = subprocess_logger.getEffectiveLevel() <= used_level + + # Only use the spinner if we're not showing the subprocess output + # and we have a spinner. + use_spinner = not showing_subprocess and spinner is not None + + if command_desc is None: + command_desc = format_command_args(cmd) + + log_subprocess("Running command %s", command_desc) + env = os.environ.copy() + if extra_environ: + env.update(extra_environ) + for name in unset_environ: + env.pop(name, None) + try: + proc = subprocess.Popen( + cmd, stderr=subprocess.STDOUT, stdin=subprocess.PIPE, + stdout=subprocess.PIPE, cwd=cwd, env=env, + ) + proc.stdin.close() + except Exception as exc: + subprocess_logger.critical( + "Error %s while executing command %s", exc, command_desc, + ) + raise + all_output = [] + while True: + line = console_to_str(proc.stdout.readline()) + if not line: + break + line = line.rstrip() + all_output.append(line + '\n') + + # Show the line immediately. + log_subprocess(line) + # Update the spinner. + if use_spinner: + spinner.spin() + try: + proc.wait() + finally: + if proc.stdout: + proc.stdout.close() + proc_had_error = ( + proc.returncode and proc.returncode not in extra_ok_returncodes + ) + if use_spinner: + if proc_had_error: + spinner.finish("error") + else: + spinner.finish("done") + if proc_had_error: + if on_returncode == 'raise': + if not showing_subprocess: + # Then the subprocess streams haven't been logged to the + # console yet. + subprocess_logger.error( + 'Complete output from command %s:', command_desc, + ) + # The all_output value already ends in a newline. + subprocess_logger.error(''.join(all_output) + LOG_DIVIDER) + raise InstallationError( + 'Command "%s" failed with error code %s in %s' + % (command_desc, proc.returncode, cwd)) + elif on_returncode == 'warn': + subprocess_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)) + return ''.join(all_output) + + +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') + + +def captured_stderr(): + """ + See captured_stdout(). + """ + return captured_output('stderr') + + +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, working_set=None): + """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) + + if working_set is None: + # 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) + + +# Simulates an enum +def enum(*sequential, **named): + enums = dict(zip(sequential, range(len(sequential))), **named) + reverse = {value: key for key, value in enums.items()} + enums['reverse_mapping'] = reverse + return type('Enum', (), enums) + + +def split_auth_from_netloc(netloc): + """ + Parse out and remove the auth information from a netloc. + + Returns: (netloc, (username, password)). + """ + if '@' not in netloc: + return netloc, (None, None) + + # Split from the right because that's how urllib.parse.urlsplit() + # behaves if more than one @ is present (which can be checked using + # the password attribute of urlsplit()'s return value). + auth, netloc = netloc.rsplit('@', 1) + if ':' in auth: + # Split from the left because that's how urllib.parse.urlsplit() + # behaves if more than one : is present (which again can be checked + # using the password attribute of the return value) + user_pass = auth.split(':', 1) + else: + user_pass = auth, None + + user_pass = tuple( + None if x is None else urllib_unquote(x) for x in user_pass + ) + + return netloc, user_pass + + +def redact_netloc(netloc): + # type: (str) -> str + """ + Replace the password in a netloc with "****", if it exists. + + For example, "user:pass@example.com" returns "user:****@example.com". + """ + netloc, (user, password) = split_auth_from_netloc(netloc) + if user is None: + return netloc + password = '' if password is None else ':****' + return '{user}{password}@{netloc}'.format(user=urllib_parse.quote(user), + password=password, + netloc=netloc) + + +def _transform_url(url, transform_netloc): + purl = urllib_parse.urlsplit(url) + netloc = transform_netloc(purl.netloc) + # stripped url + url_pieces = ( + purl.scheme, netloc, purl.path, purl.query, purl.fragment + ) + surl = urllib_parse.urlunsplit(url_pieces) + return surl + + +def _get_netloc(netloc): + return split_auth_from_netloc(netloc)[0] + + +def remove_auth_from_url(url): + # type: (str) -> str + # Return a copy of url with 'username:password@' removed. + # username/pass params are passed to subversion through flags + # and are not recognized in the url. + return _transform_url(url, _get_netloc) + + +def redact_password_from_url(url): + # type: (str) -> str + """Replace the password in a given url with ****.""" + return _transform_url(url, redact_netloc) + + +def protect_pip_from_modification_on_windows(modifying_pip): + """Protection of pip.exe from modification on Windows + + On Windows, any operation modifying pip should be run as: + python -m pip ... + """ + pip_names = [ + "pip.exe", + "pip{}.exe".format(sys.version_info[0]), + "pip{}.{}.exe".format(*sys.version_info[:2]) + ] + + # See https://github.com/pypa/pip/issues/1299 for more discussion + should_show_use_python_msg = ( + modifying_pip and + WINDOWS and + os.path.basename(sys.argv[0]) in pip_names + ) + + if should_show_use_python_msg: + new_command = [ + sys.executable, "-m", "pip" + ] + sys.argv[1:] + raise CommandError( + 'To modify pip, please run the following command:\n{}' + .format(" ".join(new_command)) + ) diff --git a/venv2/lib/python2.7/site-packages/pip/_internal/utils/models.py b/venv2/lib/python2.7/site-packages/pip/_internal/utils/models.py new file mode 100644 index 0000000..d5cb80a --- /dev/null +++ b/venv2/lib/python2.7/site-packages/pip/_internal/utils/models.py @@ -0,0 +1,40 @@ +"""Utilities for defining models +""" + +import operator + + +class KeyBasedCompareMixin(object): + """Provides comparision capabilities that is based on a key + """ + + def __init__(self, key, defining_class): + self._compare_key = key + self._defining_class = defining_class + + def __hash__(self): + return hash(self._compare_key) + + def __lt__(self, other): + return self._compare(other, operator.__lt__) + + def __le__(self, other): + return self._compare(other, operator.__le__) + + def __gt__(self, other): + return self._compare(other, operator.__gt__) + + def __ge__(self, other): + return self._compare(other, operator.__ge__) + + def __eq__(self, other): + return self._compare(other, operator.__eq__) + + def __ne__(self, other): + return self._compare(other, operator.__ne__) + + def _compare(self, other, method): + if not isinstance(other, self._defining_class): + return NotImplemented + + return method(self._compare_key, other._compare_key) diff --git a/venv2/lib/python2.7/site-packages/pip/_internal/utils/outdated.py b/venv2/lib/python2.7/site-packages/pip/_internal/utils/outdated.py new file mode 100644 index 0000000..3b58cd5 --- /dev/null +++ b/venv2/lib/python2.7/site-packages/pip/_internal/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, pkg_resources +from pip._vendor.packaging import version as packaging_version + +from pip._internal.index import PackageFinder +from pip._internal.utils.compat import WINDOWS +from pip._internal.utils.filesystem import check_path_owner +from pip._internal.utils.misc import ensure_dir, get_installed_version +from pip._internal.utils.typing import MYPY_CHECK_RUNNING + +if MYPY_CHECK_RUNNING: + import optparse + from typing import Any, Dict + from pip._internal.download import PipSession + + +SELFCHECK_DATE_FMT = "%Y-%m-%dT%H:%M:%SZ" + + +logger = logging.getLogger(__name__) + + +class SelfCheckState(object): + def __init__(self, cache_dir): + # type: (str) -> None + self.state = {} # type: Dict[str, Any] + self.statefile_path = None + + # Try to load the existing state + if cache_dir: + self.statefile_path = os.path.join(cache_dir, "selfcheck.json") + try: + with open(self.statefile_path) as statefile: + self.state = json.load(statefile)[sys.prefix] + except (IOError, ValueError, KeyError): + # Explicitly suppressing exceptions, since we don't want to + # error out if the cache file is invalid. + pass + + def save(self, pypi_version, current_time): + # type: (str, datetime.datetime) -> None + # If we do not have a path to cache in, don't bother saving. + if not self.statefile_path: + return + + # 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 was_installed_by_pip(pkg): + # type: (str) -> bool + """Checks whether pkg was installed by pip + + This is used not to display the upgrade message when pip is in fact + installed by system package manager, such as dnf on Fedora. + """ + try: + dist = pkg_resources.get_distribution(pkg) + return (dist.has_metadata('INSTALLER') and + 'pip' in dist.get_metadata_lines('INSTALLER')) + except pkg_resources.DistributionNotFound: + return False + + +def pip_version_check(session, options): + # type: (PipSession, optparse.Values) -> None + """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 not installed_version: + return + + pip_version = packaging_version.parse(installed_version) + pypi_version = None + + try: + state = SelfCheckState(cache_dir=options.cache_dir) + + 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 (current_time - last_check).total_seconds() < 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: + # Lets use PackageFinder to see what the latest pip version is + finder = PackageFinder( + find_links=options.find_links, + index_urls=[options.index_url] + options.extra_index_urls, + allow_all_prereleases=False, # Explicitly set to False + trusted_hosts=options.trusted_hosts, + session=session, + ) + candidate = finder.find_candidates("pip").get_best() + if candidate is None: + return + pypi_version = str(candidate.version) + + # 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 and + was_installed_by_pip('pip')): + # 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/venv2/lib/python2.7/site-packages/pip/_internal/utils/packaging.py b/venv2/lib/python2.7/site-packages/pip/_internal/utils/packaging.py new file mode 100644 index 0000000..449f3fd --- /dev/null +++ b/venv2/lib/python2.7/site-packages/pip/_internal/utils/packaging.py @@ -0,0 +1,85 @@ +from __future__ import absolute_import + +import logging +import sys +from email.parser import FeedParser + +from pip._vendor import pkg_resources +from pip._vendor.packaging import specifiers, version + +from pip._internal import exceptions +from pip._internal.utils.misc import display_path +from pip._internal.utils.typing import MYPY_CHECK_RUNNING + +if MYPY_CHECK_RUNNING: + from typing import Optional + from email.message import Message + from pip._vendor.pkg_resources import Distribution + + +logger = logging.getLogger(__name__) + + +def check_requires_python(requires_python): + # type: (Optional[str]) -> bool + """ + Check if the python version in use match the `requires_python` specifier. + + Returns `True` if the version of python in use matches the requirement. + Returns `False` if the version of python in use does not matches the + requirement. + + Raises an InvalidSpecifier if `requires_python` have an invalid format. + """ + if requires_python is None: + # The package provides no information + return True + requires_python_specifier = specifiers.SpecifierSet(requires_python) + + # We only use major.minor.micro + python_version = version.parse('.'.join(map(str, sys.version_info[:3]))) + return python_version in requires_python_specifier + + +def get_metadata(dist): + # type: (Distribution) -> Message + if (isinstance(dist, pkg_resources.DistInfoDistribution) and + dist.has_metadata('METADATA')): + metadata = dist.get_metadata('METADATA') + elif dist.has_metadata('PKG-INFO'): + metadata = dist.get_metadata('PKG-INFO') + else: + logger.warning("No metadata found in %s", display_path(dist.location)) + metadata = '' + + feed_parser = FeedParser() + feed_parser.feed(metadata) + return feed_parser.close() + + +def check_dist_requires_python(dist): + pkg_info_dict = get_metadata(dist) + requires_python = pkg_info_dict.get('Requires-Python') + try: + if not check_requires_python(requires_python): + raise exceptions.UnsupportedPythonVersion( + "%s requires Python '%s' but the running Python is %s" % ( + dist.project_name, + requires_python, + '.'.join(map(str, sys.version_info[:3])),) + ) + except specifiers.InvalidSpecifier as e: + logger.warning( + "Package %s has an invalid Requires-Python entry %s - %s", + dist.project_name, requires_python, e, + ) + return + + +def get_installer(dist): + # type: (Distribution) -> str + if dist.has_metadata('INSTALLER'): + for line in dist.get_metadata_lines('INSTALLER'): + if line.strip(): + return line.strip() + return '' diff --git a/venv2/lib/python2.7/site-packages/pip/_internal/utils/setuptools_build.py b/venv2/lib/python2.7/site-packages/pip/_internal/utils/setuptools_build.py new file mode 100644 index 0000000..03973e9 --- /dev/null +++ b/venv2/lib/python2.7/site-packages/pip/_internal/utils/setuptools_build.py @@ -0,0 +1,8 @@ +# Shim to wrap setup.py invocation with setuptools +SETUPTOOLS_SHIM = ( + "import setuptools, tokenize;__file__=%r;" + "f=getattr(tokenize, 'open', open)(__file__);" + "code=f.read().replace('\\r\\n', '\\n');" + "f.close();" + "exec(compile(code, __file__, 'exec'))" +) diff --git a/venv2/lib/python2.7/site-packages/pip/_internal/utils/temp_dir.py b/venv2/lib/python2.7/site-packages/pip/_internal/utils/temp_dir.py new file mode 100644 index 0000000..2c81ad5 --- /dev/null +++ b/venv2/lib/python2.7/site-packages/pip/_internal/utils/temp_dir.py @@ -0,0 +1,155 @@ +from __future__ import absolute_import + +import errno +import itertools +import logging +import os.path +import tempfile + +from pip._internal.utils.misc import rmtree + +logger = logging.getLogger(__name__) + + +class TempDirectory(object): + """Helper class that owns and cleans up a temporary directory. + + This class can be used as a context manager or as an OO representation of a + temporary directory. + + Attributes: + path + Location to the created temporary directory or None + delete + Whether the directory should be deleted when exiting + (when used as a contextmanager) + + Methods: + create() + Creates a temporary directory and stores its path in the path + attribute. + cleanup() + Deletes the temporary directory and sets path attribute to None + + When used as a context manager, a temporary directory is created on + entering the context and, if the delete attribute is True, on exiting the + context the created directory is deleted. + """ + + def __init__(self, path=None, delete=None, kind="temp"): + super(TempDirectory, self).__init__() + + if path is None and delete is None: + # If we were not given an explicit directory, and we were not given + # an explicit delete option, then we'll default to deleting. + delete = True + + self.path = path + self.delete = delete + self.kind = kind + + def __repr__(self): + return "<{} {!r}>".format(self.__class__.__name__, self.path) + + def __enter__(self): + self.create() + return self + + def __exit__(self, exc, value, tb): + if self.delete: + self.cleanup() + + def create(self): + """Create a temporary directory and store its path in self.path + """ + if self.path is not None: + logger.debug( + "Skipped creation of temporary directory: {}".format(self.path) + ) + return + # 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. + self.path = os.path.realpath( + tempfile.mkdtemp(prefix="pip-{}-".format(self.kind)) + ) + logger.debug("Created temporary directory: {}".format(self.path)) + + def cleanup(self): + """Remove the temporary directory created and reset state + """ + if self.path is not None and os.path.exists(self.path): + rmtree(self.path) + self.path = None + + +class AdjacentTempDirectory(TempDirectory): + """Helper class that creates a temporary directory adjacent to a real one. + + Attributes: + original + The original directory to create a temp directory for. + path + After calling create() or entering, contains the full + path to the temporary directory. + delete + Whether the directory should be deleted when exiting + (when used as a contextmanager) + + """ + # The characters that may be used to name the temp directory + # We always prepend a ~ and then rotate through these until + # a usable name is found. + # pkg_resources raises a different error for .dist-info folder + # with leading '-' and invalid metadata + LEADING_CHARS = "-~.=%0123456789" + + def __init__(self, original, delete=None): + super(AdjacentTempDirectory, self).__init__(delete=delete) + self.original = original.rstrip('/\\') + + @classmethod + def _generate_names(cls, name): + """Generates a series of temporary names. + + The algorithm replaces the leading characters in the name + with ones that are valid filesystem characters, but are not + valid package names (for both Python and pip definitions of + package). + """ + for i in range(1, len(name)): + for candidate in itertools.combinations_with_replacement( + cls.LEADING_CHARS, i - 1): + new_name = '~' + ''.join(candidate) + name[i:] + if new_name != name: + yield new_name + + # If we make it this far, we will have to make a longer name + for i in range(len(cls.LEADING_CHARS)): + for candidate in itertools.combinations_with_replacement( + cls.LEADING_CHARS, i): + new_name = '~' + ''.join(candidate) + name + if new_name != name: + yield new_name + + def create(self): + root, name = os.path.split(self.original) + for candidate in self._generate_names(name): + path = os.path.join(root, candidate) + try: + os.mkdir(path) + except OSError as ex: + # Continue if the name exists already + if ex.errno != errno.EEXIST: + raise + else: + self.path = os.path.realpath(path) + break + + if not self.path: + # Final fallback on the default behavior. + self.path = os.path.realpath( + tempfile.mkdtemp(prefix="pip-{}-".format(self.kind)) + ) + logger.debug("Created temporary directory: {}".format(self.path)) diff --git a/venv2/lib/python2.7/site-packages/pip/_internal/utils/typing.py b/venv2/lib/python2.7/site-packages/pip/_internal/utils/typing.py new file mode 100644 index 0000000..10170ce --- /dev/null +++ b/venv2/lib/python2.7/site-packages/pip/_internal/utils/typing.py @@ -0,0 +1,29 @@ +"""For neatly implementing static typing in pip. + +`mypy` - the static type analysis tool we use - uses the `typing` module, which +provides core functionality fundamental to mypy's functioning. + +Generally, `typing` would be imported at runtime and used in that fashion - +it acts as a no-op at runtime and does not have any run-time overhead by +design. + +As it turns out, `typing` is not vendorable - it uses separate sources for +Python 2/Python 3. Thus, this codebase can not expect it to be present. +To work around this, mypy allows the typing import to be behind a False-y +optional to prevent it from running at runtime and type-comments can be used +to remove the need for the types to be accessible directly during runtime. + +This module provides the False-y guard in a nicely named fashion so that a +curious maintainer can reach here to read this. + +In pip, all static-typing related imports should be guarded as follows: + + from pip._internal.utils.typing import MYPY_CHECK_RUNNING + + if MYPY_CHECK_RUNNING: + from typing import ... + +Ref: https://github.com/python/mypy/issues/3216 +""" + +MYPY_CHECK_RUNNING = False diff --git a/venv2/lib/python2.7/site-packages/pip/_internal/utils/ui.py b/venv2/lib/python2.7/site-packages/pip/_internal/utils/ui.py new file mode 100644 index 0000000..0902ce7 --- /dev/null +++ b/venv2/lib/python2.7/site-packages/pip/_internal/utils/ui.py @@ -0,0 +1,424 @@ +from __future__ import absolute_import, division + +import contextlib +import itertools +import logging +import sys +import time +from signal import SIGINT, default_int_handler, signal + +from pip._vendor import six +from pip._vendor.progress import HIDE_CURSOR, SHOW_CURSOR +from pip._vendor.progress.bar import Bar, FillingCirclesBar, IncrementalBar +from pip._vendor.progress.spinner import Spinner + +from pip._internal.utils.compat import WINDOWS +from pip._internal.utils.logging import get_indentation +from pip._internal.utils.misc import format_size +from pip._internal.utils.typing import MYPY_CHECK_RUNNING + +if MYPY_CHECK_RUNNING: + from typing import Any, Iterator, IO + +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) # type: Any + + +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 SilentBar(Bar): + + def update(self): + pass + + +class BlueEmojiBar(IncrementalBar): + + suffix = "%(percent)d%%" + bar_prefix = " " + bar_suffix = " " + phases = (u"\U0001F539", u"\U0001F537", u"\U0001F535") # type: Any + + +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 needs 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 BaseDownloadProgressBar(WindowsMixin, InterruptibleMixin, + DownloadProgressMixin): + + file = sys.stdout + message = "%(percent)d%%" + suffix = "%(downloaded)s %(download_speed)s %(pretty_eta)s" + +# NOTE: The "type: ignore" comments on the following classes are there to +# work around https://github.com/python/typing/issues/241 + + +class DefaultDownloadProgressBar(BaseDownloadProgressBar, + _BaseBar): + pass + + +class DownloadSilentBar(BaseDownloadProgressBar, SilentBar): # type: ignore + pass + + +class DownloadIncrementalBar(BaseDownloadProgressBar, # type: ignore + IncrementalBar): + pass + + +class DownloadFillingCirclesBar(BaseDownloadProgressBar, # type: ignore + FillingCirclesBar): + pass + + +class DownloadBlueEmojiProgressBar(BaseDownloadProgressBar, # type: ignore + BlueEmojiBar): + pass + + +class DownloadProgressSpinner(WindowsMixin, InterruptibleMixin, + DownloadProgressMixin, 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) + + +BAR_TYPES = { + "off": (DownloadSilentBar, DownloadSilentBar), + "on": (DefaultDownloadProgressBar, DownloadProgressSpinner), + "ascii": (DownloadIncrementalBar, DownloadProgressSpinner), + "pretty": (DownloadFillingCirclesBar, DownloadProgressSpinner), + "emoji": (DownloadBlueEmojiProgressBar, DownloadProgressSpinner) +} + + +def DownloadProgressProvider(progress_bar, max=None): + if max is None or max == 0: + return BAR_TYPES[progress_bar][1]().iter + else: + return BAR_TYPES[progress_bar][0](max=max).iter + + +################################################################ +# 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): + # type: (IO) -> Iterator[None] + # 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): + # type: (float) -> None + self._min_update_interval_seconds = min_update_interval_seconds + self._last_update = 0 # type: float + + def ready(self): + # type: () -> bool + now = time.time() + delta = now - self._last_update + return delta >= self._min_update_interval_seconds + + def reset(self): + # type: () -> None + self._last_update = time.time() + + +class SpinnerInterface(object): + def spin(self): + # type: () -> None + raise NotImplementedError() + + def finish(self, final_status): + # type: (str) -> None + raise NotImplementedError() + + +class InteractiveSpinner(SpinnerInterface): + 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): + # type: () -> None + if self._finished: + return + if not self._rate_limiter.ready(): + return + self._write(next(self._spin_cycle)) + + def finish(self, final_status): + # type: (str) -> None + 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(SpinnerInterface): + def __init__(self, message, min_update_interval_seconds=60): + # type: (str, float) -> None + 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): + # type: () -> None + if self._finished: + return + if not self._rate_limiter.ready(): + return + self._update("still running...") + + def finish(self, final_status): + # type: (str) -> None + if self._finished: + return + self._update("finished with status '%s'" % (final_status,)) + self._finished = True + + +@contextlib.contextmanager +def open_spinner(message): + # type: (str) -> Iterator[SpinnerInterface] + # 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) # type: SpinnerInterface + 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/venv2/lib/python2.7/site-packages/pip/_internal/vcs/__init__.py b/venv2/lib/python2.7/site-packages/pip/_internal/vcs/__init__.py new file mode 100644 index 0000000..6fca079 --- /dev/null +++ b/venv2/lib/python2.7/site-packages/pip/_internal/vcs/__init__.py @@ -0,0 +1,604 @@ +"""Handles all VCS (version control) support""" +from __future__ import absolute_import + +import errno +import logging +import os +import shutil +import sys + +from pip._vendor import pkg_resources +from pip._vendor.six.moves.urllib import parse as urllib_parse + +from pip._internal.exceptions import BadCommand +from pip._internal.utils.misc import ( + ask_path_exists, backup_dir, call_subprocess, display_path, rmtree, +) +from pip._internal.utils.typing import MYPY_CHECK_RUNNING + +if MYPY_CHECK_RUNNING: + from typing import ( + Any, Dict, Iterable, List, Mapping, Optional, Text, Tuple, Type + ) + from pip._internal.utils.ui import SpinnerInterface + + AuthInfo = Tuple[Optional[str], Optional[str]] + +__all__ = ['vcs'] + + +logger = logging.getLogger(__name__) + + +def make_vcs_requirement_url(repo_url, rev, project_name, subdir=None): + """ + Return the URL for a VCS requirement. + + Args: + repo_url: the remote VCS url, with any needed VCS prefix (e.g. "git+"). + project_name: the (unescaped) project name. + """ + egg_project_name = pkg_resources.to_filename(project_name) + req = '{}@{}#egg={}'.format(repo_url, rev, egg_project_name) + if subdir: + req += '&subdirectory={}'.format(subdir) + + return req + + +class RemoteNotFoundError(Exception): + pass + + +class RevOptions(object): + + """ + Encapsulates a VCS-specific revision to install, along with any VCS + install options. + + Instances of this class should be treated as if immutable. + """ + + def __init__( + self, + vc_class, # type: Type[VersionControl] + rev=None, # type: Optional[str] + extra_args=None, # type: Optional[List[str]] + ): + # type: (...) -> None + """ + Args: + vc_class: a VersionControl subclass. + rev: the name of the revision to install. + extra_args: a list of extra options. + """ + if extra_args is None: + extra_args = [] + + self.extra_args = extra_args + self.rev = rev + self.vc_class = vc_class + + def __repr__(self): + return '<RevOptions {}: rev={!r}>'.format(self.vc_class.name, self.rev) + + @property + def arg_rev(self): + # type: () -> Optional[str] + if self.rev is None: + return self.vc_class.default_arg_rev + + return self.rev + + def to_args(self): + # type: () -> List[str] + """ + Return the VCS-specific command arguments. + """ + args = [] # type: List[str] + rev = self.arg_rev + if rev is not None: + args += self.vc_class.get_base_rev_args(rev) + args += self.extra_args + + return args + + def to_display(self): + # type: () -> str + if not self.rev: + return '' + + return ' (to revision {})'.format(self.rev) + + def make_new(self, rev): + # type: (str) -> RevOptions + """ + Make a copy of the current instance, but with a new rev. + + Args: + rev: the name of the revision for the new object. + """ + return self.vc_class.make_rev_options(rev, extra_args=self.extra_args) + + +class VcsSupport(object): + _registry = {} # type: Dict[str, Type[VersionControl]] + schemes = ['ssh', 'git', 'hg', 'bzr', 'sftp', 'svn'] + + def __init__(self): + # type: () -> None + # 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): + # type: () -> List[Type[VersionControl]] + return list(self._registry.values()) + + @property + def dirnames(self): + # type: () -> List[str] + return [backend.dirname for backend in self.backends] + + @property + def all_schemes(self): + # type: () -> List[str] + schemes = [] # type: List[str] + for backend in self.backends: + schemes.extend(backend.schemes) + return schemes + + def register(self, cls): + # type: (Type[VersionControl]) -> None + 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): + # type: (Optional[Type[VersionControl]], Optional[str]) -> 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_type(self, location): + # type: (str) -> Optional[Type[VersionControl]] + """ + Return the type of the version control backend if found at given + location, e.g. vcs.get_backend_type('/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 + return None + + def get_backend(self, name): + # type: (str) -> Optional[Type[VersionControl]] + name = name.lower() + if name in self._registry: + return self._registry[name] + return None + + +vcs = VcsSupport() + + +class VersionControl(object): + name = '' + dirname = '' + repo_name = '' + # List of supported schemes for this Version Control + schemes = () # type: Tuple[str, ...] + # Iterable of environment variable names to pass to call_subprocess(). + unset_environ = () # type: Tuple[str, ...] + default_arg_rev = None # type: Optional[str] + + @classmethod + def should_add_vcs_url_prefix(cls, remote_url): + """ + Return whether the vcs prefix (e.g. "git+") should be added to a + repository's remote url when used in a requirement. + """ + return not remote_url.lower().startswith('{}:'.format(cls.name)) + + @classmethod + def get_subdirectory(cls, repo_dir): + """ + Return the path to setup.py, relative to the repo root. + """ + return None + + @classmethod + def get_requirement_revision(cls, repo_dir): + """ + Return the revision string that should be used in a requirement. + """ + return cls.get_revision(repo_dir) + + @classmethod + def get_src_requirement(cls, repo_dir, project_name): + """ + Return the requirement string to use to redownload the files + currently at the given repository directory. + + Args: + project_name: the (unescaped) project name. + + The return value has a form similar to the following: + + {repository_url}@{revision}#egg={project_name} + """ + repo_url = cls.get_remote_url(repo_dir) + if repo_url is None: + return None + + if cls.should_add_vcs_url_prefix(repo_url): + repo_url = '{}+{}'.format(cls.name, repo_url) + + revision = cls.get_requirement_revision(repo_dir) + subdir = cls.get_subdirectory(repo_dir) + req = make_vcs_requirement_url(repo_url, revision, project_name, + subdir=subdir) + + return req + + def __init__(self, url=None, *args, **kwargs): + self.url = url + super(VersionControl, self).__init__(*args, **kwargs) + + @staticmethod + def get_base_rev_args(rev): + """ + Return the base revision arguments for a vcs command. + + Args: + rev: the name of a revision to install. Cannot be None. + """ + raise NotImplementedError + + @classmethod + def make_rev_options(cls, rev=None, extra_args=None): + # type: (Optional[str], Optional[List[str]]) -> RevOptions + """ + Return a RevOptions object. + + Args: + rev: the name of a revision to install. + extra_args: a list of extra options. + """ + return RevOptions(cls, rev, extra_args=extra_args) + + @classmethod + def _is_local_repository(cls, repo): + # type: (str) -> bool + """ + posix absolute paths start with os.path.sep, + win32 ones start with drive (like c:\\folder) + """ + drive, tail = os.path.splitdrive(repo) + return repo.startswith(os.path.sep) or bool(drive) + + 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 + + @classmethod + def get_netloc_and_auth(cls, netloc, scheme): + """ + Parse the repository URL's netloc, and return the new netloc to use + along with auth information. + + Args: + netloc: the original repository URL netloc. + scheme: the repository URL's scheme without the vcs prefix. + + This is mainly for the Subversion class to override, so that auth + information can be provided via the --username and --password options + instead of through the URL. For other subclasses like Git without + such an option, auth information must stay in the URL. + + Returns: (netloc, (username, password)). + """ + return netloc, (None, None) + + @classmethod + def get_url_rev_and_auth(cls, url): + # type: (str) -> Tuple[str, Optional[str], AuthInfo] + """ + Parse the repository URL to use, and return the URL, revision, + and auth info to use. + + Returns: (url, rev, (username, password)). + """ + scheme, netloc, path, query, frag = urllib_parse.urlsplit(url) + if '+' not in scheme: + raise ValueError( + "Sorry, {!r} is a malformed VCS url. " + "The format is <vcs>+<protocol>://<url>, " + "e.g. svn+http://myrepo/svn/MyApp#egg=MyApp".format(url) + ) + # Remove the vcs prefix. + scheme = scheme.split('+', 1)[1] + netloc, user_pass = cls.get_netloc_and_auth(netloc, scheme) + rev = None + if '@' in path: + path, rev = path.rsplit('@', 1) + url = urllib_parse.urlunsplit((scheme, netloc, path, query, '')) + return url, rev, user_pass + + @staticmethod + def make_rev_args(username, password): + """ + Return the RevOptions "extra arguments" to use in obtain(). + """ + return [] + + def get_url_rev_options(self, url): + # type: (str) -> Tuple[str, RevOptions] + """ + Return the URL and RevOptions object to use in obtain() and in + some cases export(), as a tuple (url, rev_options). + """ + url, rev, user_pass = self.get_url_rev_and_auth(url) + username, password = user_pass + extra_args = self.make_rev_args(username, password) + rev_options = self.make_rev_options(rev, extra_args=extra_args) + + return url, rev_options + + @staticmethod + def normalize_url(url): + # type: (str) -> str + """ + Normalize a URL for comparison by unquoting it and removing any + trailing slash. + """ + return urllib_parse.unquote(url).rstrip('/') + + @classmethod + def compare_urls(cls, url1, url2): + # type: (str, str) -> bool + """ + Compare two repo URLs for identity, ignoring incidental differences. + """ + return (cls.normalize_url(url1) == cls.normalize_url(url2)) + + @classmethod + def fetch_new(cls, dest, url, rev_options): + """ + Fetch a revision from a repository, in the case that this is the + first fetch from the repository. + + Args: + dest: the directory to fetch the repository to. + rev_options: a RevOptions object. + """ + raise NotImplementedError + + def switch(self, dest, url, rev_options): + """ + Switch the repo at ``dest`` to point to ``URL``. + + Args: + rev_options: a RevOptions object. + """ + raise NotImplementedError + + def update(self, dest, url, rev_options): + """ + Update an already-existing repo to the given ``rev_options``. + + Args: + rev_options: a RevOptions object. + """ + raise NotImplementedError + + @classmethod + def is_commit_id_equal(cls, dest, name): + """ + Return whether the id of the current commit equals the given name. + + Args: + dest: the repository directory. + name: a string name. + """ + raise NotImplementedError + + def obtain(self, dest): + # type: (str) -> None + """ + Install or update in editable mode the package represented by this + VersionControl object. + + Args: + dest: the repository directory in which to install or update. + """ + url, rev_options = self.get_url_rev_options(self.url) + + if not os.path.exists(dest): + self.fetch_new(dest, url, rev_options) + return + + rev_display = rev_options.to_display() + if self.is_repository_directory(dest): + existing_url = self.get_remote_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.is_commit_id_equal(dest, rev_options.rev): + logger.info( + 'Updating %s %s%s', + display_path(dest), + self.repo_name, + rev_display, + ) + self.update(dest, url, rev_options) + else: + logger.info('Skipping because already up-to-date.') + return + + 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, + ) + # https://github.com/python/mypy/issues/1174 + prompt = ('(i)gnore, (w)ipe, (b)ackup ', # type: ignore + ('i', 'w', 'b')) + + 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 == 'a': + sys.exit(-1) + + if response == 'w': + logger.warning('Deleting %s', display_path(dest)) + rmtree(dest) + self.fetch_new(dest, url, rev_options) + return + + if response == 'b': + dest_dir = backup_dir(dest) + logger.warning( + 'Backing up %s to %s', display_path(dest), dest_dir, + ) + shutil.move(dest, dest_dir) + self.fetch_new(dest, url, rev_options) + return + + # Do nothing if the response is "i". + 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) + + def unpack(self, location): + # type: (str) -> None + """ + Clean up current location and download the url repository + (and vcs infos) into location + """ + if os.path.exists(location): + rmtree(location) + self.obtain(location) + + @classmethod + def get_remote_url(cls, location): + """ + Return the url used at location + + Raises RemoteNotFoundError if the repository does not have a remote + url configured. + """ + raise NotImplementedError + + @classmethod + def get_revision(cls, location): + """ + Return the current commit id of the files at the given location. + """ + raise NotImplementedError + + @classmethod + def run_command( + cls, + cmd, # type: List[str] + show_stdout=True, # type: bool + cwd=None, # type: Optional[str] + on_returncode='raise', # type: str + extra_ok_returncodes=None, # type: Optional[Iterable[int]] + command_desc=None, # type: Optional[str] + extra_environ=None, # type: Optional[Mapping[str, Any]] + spinner=None # type: Optional[SpinnerInterface] + ): + # type: (...) -> Optional[Text] + """ + 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 = [cls.name] + cmd + try: + return call_subprocess(cmd, show_stdout, cwd, + on_returncode=on_returncode, + extra_ok_returncodes=extra_ok_returncodes, + command_desc=command_desc, + extra_environ=extra_environ, + unset_environ=cls.unset_environ, + spinner=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 - do you have ' + '%r installed and in your ' + 'PATH?' % (cls.name, cls.name)) + else: + raise # re-raise exception if a different error occurred + + @classmethod + def is_repository_directory(cls, path): + # type: (str) -> bool + """ + Return whether a directory path is a repository directory. + """ + logger.debug('Checking in %s for %s (%s)...', + path, cls.dirname, cls.name) + return os.path.exists(os.path.join(path, cls.dirname)) + + @classmethod + def controls_location(cls, location): + # type: (str) -> bool + """ + Check if a location is controlled by the vcs. + It is meant to be overridden to implement smarter detection + mechanisms for specific vcs. + + This can do more than is_repository_directory() alone. For example, + the Git override checks that Git is actually available. + """ + return cls.is_repository_directory(location) diff --git a/venv2/lib/python2.7/site-packages/pip/_internal/vcs/bazaar.py b/venv2/lib/python2.7/site-packages/pip/_internal/vcs/bazaar.py new file mode 100644 index 0000000..3587b3d --- /dev/null +++ b/venv2/lib/python2.7/site-packages/pip/_internal/vcs/bazaar.py @@ -0,0 +1,103 @@ +from __future__ import absolute_import + +import logging +import os + +from pip._vendor.six.moves.urllib import parse as urllib_parse + +from pip._internal.download import path_to_url +from pip._internal.utils.misc import display_path, rmtree +from pip._internal.vcs import VersionControl, vcs + +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) + # This is only needed for python <2.7.5 + # 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']) + + @staticmethod + def get_base_rev_args(rev): + return ['-r', rev] + + def export(self, location): + """ + Export the Bazaar repository at the url to the destination location + """ + # Remove the location to make sure Bazaar can export it correctly + if os.path.exists(location): + rmtree(location) + + url, rev_options = self.get_url_rev_options(self.url) + self.run_command( + ['export', location, url] + rev_options.to_args(), + show_stdout=False, + ) + + @classmethod + def fetch_new(cls, dest, url, rev_options): + rev_display = rev_options.to_display() + logger.info( + 'Checking out %s%s to %s', + url, + rev_display, + display_path(dest), + ) + cmd_args = ['branch', '-q'] + rev_options.to_args() + [url, dest] + cls.run_command(cmd_args) + + def switch(self, dest, url, rev_options): + self.run_command(['switch', url], cwd=dest) + + def update(self, dest, url, rev_options): + cmd_args = ['pull', '-q'] + rev_options.to_args() + self.run_command(cmd_args, cwd=dest) + + @classmethod + def get_url_rev_and_auth(cls, url): + # hotfix the URL scheme after removing bzr+ from bzr+ssh:// readd it + url, rev, user_pass = super(Bazaar, cls).get_url_rev_and_auth(url) + if url.startswith('ssh://'): + url = 'bzr+' + url + return url, rev, user_pass + + @classmethod + def get_remote_url(cls, location): + urls = cls.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 cls._is_local_repository(repo): + return path_to_url(repo) + return repo + return None + + @classmethod + def get_revision(cls, location): + revision = cls.run_command( + ['revno'], show_stdout=False, cwd=location, + ) + return revision.splitlines()[-1] + + @classmethod + def is_commit_id_equal(cls, dest, name): + """Always assume the versions don't match""" + return False + + +vcs.register(Bazaar) diff --git a/venv2/lib/python2.7/site-packages/pip/_internal/vcs/git.py b/venv2/lib/python2.7/site-packages/pip/_internal/vcs/git.py new file mode 100644 index 0000000..35ea930 --- /dev/null +++ b/venv2/lib/python2.7/site-packages/pip/_internal/vcs/git.py @@ -0,0 +1,362 @@ +from __future__ import absolute_import + +import logging +import os.path +import re + +from pip._vendor.packaging.version import parse as parse_version +from pip._vendor.six.moves.urllib import parse as urllib_parse +from pip._vendor.six.moves.urllib import request as urllib_request + +from pip._internal.exceptions import BadCommand +from pip._internal.utils.compat import samefile +from pip._internal.utils.misc import display_path, redact_password_from_url +from pip._internal.utils.temp_dir import TempDirectory +from pip._internal.vcs import RemoteNotFoundError, VersionControl, vcs + +urlsplit = urllib_parse.urlsplit +urlunsplit = urllib_parse.urlunsplit + + +logger = logging.getLogger(__name__) + + +HASH_REGEX = re.compile('[a-fA-F0-9]{40}') + + +def looks_like_hash(sha): + return bool(HASH_REGEX.match(sha)) + + +class Git(VersionControl): + name = 'git' + dirname = '.git' + repo_name = 'clone' + schemes = ( + 'git', 'git+http', 'git+https', 'git+ssh', 'git+git', 'git+file', + ) + # Prevent the user's environment variables from interfering with pip: + # https://github.com/pypa/pip/issues/1130 + unset_environ = ('GIT_DIR', 'GIT_WORK_TREE') + default_arg_rev = 'HEAD' + + def __init__(self, url=None, *args, **kwargs): + + # Works around an apparent Git bug + # (see https://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) + + @staticmethod + def get_base_rev_args(rev): + return [rev] + + def get_git_version(self): + VERSION_PFX = 'git version ' + version = self.run_command(['version'], show_stdout=False) + if version.startswith(VERSION_PFX): + version = version[len(VERSION_PFX):].split()[0] + else: + version = '' + # get first 3 positions of the git version because + # on windows it is x.y.z.windows.t, and this parses as + # LegacyVersion which always smaller than a Version. + version = '.'.join(version.split('.')[:3]) + return parse_version(version) + + @classmethod + def get_current_branch(cls, location): + """ + Return the current branch, or None if HEAD isn't at a branch + (e.g. detached HEAD). + """ + # git-symbolic-ref exits with empty stdout if "HEAD" is a detached + # HEAD rather than a symbolic ref. In addition, the -q causes the + # command to exit with status code 1 instead of 128 in this case + # and to suppress the message to stderr. + args = ['symbolic-ref', '-q', 'HEAD'] + output = cls.run_command( + args, extra_ok_returncodes=(1, ), show_stdout=False, cwd=location, + ) + ref = output.strip() + + if ref.startswith('refs/heads/'): + return ref[len('refs/heads/'):] + + return None + + def export(self, location): + """Export the Git repository at the url to the destination location""" + if not location.endswith('/'): + location = location + '/' + + with TempDirectory(kind="export") as temp_dir: + self.unpack(temp_dir.path) + self.run_command( + ['checkout-index', '-a', '-f', '--prefix', location], + show_stdout=False, cwd=temp_dir.path + ) + + @classmethod + def get_revision_sha(cls, dest, rev): + """ + Return (sha_or_none, is_branch), where sha_or_none is a commit hash + if the revision names a remote branch or tag, otherwise None. + + Args: + dest: the repository directory. + rev: the revision name. + """ + # Pass rev to pre-filter the list. + output = cls.run_command(['show-ref', rev], cwd=dest, + show_stdout=False, on_returncode='ignore') + refs = {} + for line in output.strip().splitlines(): + try: + sha, ref = line.split() + except ValueError: + # Include the offending line to simplify troubleshooting if + # this error ever occurs. + raise ValueError('unexpected show-ref line: {!r}'.format(line)) + + refs[ref] = sha + + branch_ref = 'refs/remotes/origin/{}'.format(rev) + tag_ref = 'refs/tags/{}'.format(rev) + + sha = refs.get(branch_ref) + if sha is not None: + return (sha, True) + + sha = refs.get(tag_ref) + + return (sha, False) + + @classmethod + def resolve_revision(cls, dest, url, rev_options): + """ + Resolve a revision to a new RevOptions object with the SHA1 of the + branch, tag, or ref if found. + + Args: + rev_options: a RevOptions object. + """ + rev = rev_options.arg_rev + sha, is_branch = cls.get_revision_sha(dest, rev) + + if sha is not None: + rev_options = rev_options.make_new(sha) + rev_options.branch_name = rev if is_branch else None + + return rev_options + + # Do not show a warning for the common case of something that has + # the form of a Git commit hash. + if not looks_like_hash(rev): + logger.warning( + "Did not find branch or tag '%s', assuming revision or ref.", + rev, + ) + + if not rev.startswith('refs/'): + return rev_options + + # If it looks like a ref, we have to fetch it explicitly. + cls.run_command( + ['fetch', '-q', url] + rev_options.to_args(), + cwd=dest, + ) + # Change the revision to the SHA of the ref we fetched + sha = cls.get_revision(dest, rev='FETCH_HEAD') + rev_options = rev_options.make_new(sha) + + return rev_options + + @classmethod + def is_commit_id_equal(cls, dest, name): + """ + Return whether the current commit hash equals the given name. + + Args: + dest: the repository directory. + name: a string name. + """ + if not name: + # Then avoid an unnecessary subprocess call. + return False + + return cls.get_revision(dest) == name + + @classmethod + def fetch_new(cls, dest, url, rev_options): + rev_display = rev_options.to_display() + logger.info( + 'Cloning %s%s to %s', redact_password_from_url(url), + rev_display, display_path(dest), + ) + cls.run_command(['clone', '-q', url, dest]) + + if rev_options.rev: + # Then a specific revision was requested. + rev_options = cls.resolve_revision(dest, url, rev_options) + branch_name = getattr(rev_options, 'branch_name', None) + if branch_name is None: + # Only do a checkout if the current commit id doesn't match + # the requested revision. + if not cls.is_commit_id_equal(dest, rev_options.rev): + cmd_args = ['checkout', '-q'] + rev_options.to_args() + cls.run_command(cmd_args, cwd=dest) + elif cls.get_current_branch(dest) != branch_name: + # Then a specific branch was requested, and that branch + # is not yet checked out. + track_branch = 'origin/{}'.format(branch_name) + cmd_args = [ + 'checkout', '-b', branch_name, '--track', track_branch, + ] + cls.run_command(cmd_args, cwd=dest) + + #: repo may contain submodules + cls.update_submodules(dest) + + def switch(self, dest, url, rev_options): + self.run_command(['config', 'remote.origin.url', url], cwd=dest) + cmd_args = ['checkout', '-q'] + rev_options.to_args() + self.run_command(cmd_args, cwd=dest) + + self.update_submodules(dest) + + def update(self, dest, url, rev_options): + # First fetch changes from the default remote + if self.get_git_version() >= parse_version('1.9.0'): + # fetch tags in addition to everything else + self.run_command(['fetch', '-q', '--tags'], cwd=dest) + else: + self.run_command(['fetch', '-q'], cwd=dest) + # Then reset to wanted revision (maybe even origin/master) + rev_options = self.resolve_revision(dest, url, rev_options) + cmd_args = ['reset', '--hard', '-q'] + rev_options.to_args() + self.run_command(cmd_args, cwd=dest) + #: update submodules + self.update_submodules(dest) + + @classmethod + def get_remote_url(cls, location): + """ + Return URL of the first remote encountered. + + Raises RemoteNotFoundError if the repository does not have a remote + url configured. + """ + # We need to pass 1 for extra_ok_returncodes since the command + # exits with return code 1 if there are no matching lines. + stdout = cls.run_command( + ['config', '--get-regexp', r'remote\..*\.url'], + extra_ok_returncodes=(1, ), show_stdout=False, cwd=location, + ) + remotes = stdout.splitlines() + try: + found_remote = remotes[0] + except IndexError: + raise RemoteNotFoundError + + for remote in remotes: + if remote.startswith('remote.origin.url '): + found_remote = remote + break + url = found_remote.split(' ')[1] + return url.strip() + + @classmethod + def get_revision(cls, location, rev=None): + if rev is None: + rev = 'HEAD' + current_rev = cls.run_command( + ['rev-parse', rev], show_stdout=False, cwd=location, + ) + return current_rev.strip() + + @classmethod + def get_subdirectory(cls, location): + # find the repo root + git_dir = cls.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) + + @classmethod + def get_url_rev_and_auth(cls, url): + """ + Prefixes stub URLs like 'user@hostname:user/repo.git' with 'ssh://'. + That's required because although they use SSH they sometimes don'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 url: + assert 'file:' not in url + url = url.replace('git+', 'git+ssh://') + url, rev, user_pass = super(Git, cls).get_url_rev_and_auth(url) + url = url.replace('ssh://', '') + else: + url, rev, user_pass = super(Git, cls).get_url_rev_and_auth(url) + + return url, rev, user_pass + + @classmethod + def update_submodules(cls, location): + if not os.path.exists(os.path.join(location, '.gitmodules')): + return + cls.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/venv2/lib/python2.7/site-packages/pip/_internal/vcs/mercurial.py b/venv2/lib/python2.7/site-packages/pip/_internal/vcs/mercurial.py new file mode 100644 index 0000000..81edc4a --- /dev/null +++ b/venv2/lib/python2.7/site-packages/pip/_internal/vcs/mercurial.py @@ -0,0 +1,105 @@ +from __future__ import absolute_import + +import logging +import os + +from pip._vendor.six.moves import configparser + +from pip._internal.download import path_to_url +from pip._internal.utils.misc import display_path +from pip._internal.utils.temp_dir import TempDirectory +from pip._internal.vcs import VersionControl, vcs + +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') + + @staticmethod + def get_base_rev_args(rev): + return [rev] + + def export(self, location): + """Export the Hg repository at the url to the destination location""" + with TempDirectory(kind="export") as temp_dir: + self.unpack(temp_dir.path) + + self.run_command( + ['archive', location], show_stdout=False, cwd=temp_dir.path + ) + + @classmethod + def fetch_new(cls, dest, url, rev_options): + rev_display = rev_options.to_display() + logger.info( + 'Cloning hg %s%s to %s', + url, + rev_display, + display_path(dest), + ) + cls.run_command(['clone', '--noupdate', '-q', url, dest]) + cmd_args = ['update', '-q'] + rev_options.to_args() + cls.run_command(cmd_args, cwd=dest) + + 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: + cmd_args = ['update', '-q'] + rev_options.to_args() + self.run_command(cmd_args, cwd=dest) + + def update(self, dest, url, rev_options): + self.run_command(['pull', '-q'], cwd=dest) + cmd_args = ['update', '-q'] + rev_options.to_args() + self.run_command(cmd_args, cwd=dest) + + @classmethod + def get_remote_url(cls, location): + url = cls.run_command( + ['showconfig', 'paths.default'], + show_stdout=False, cwd=location).strip() + if cls._is_local_repository(url): + url = path_to_url(url) + return url.strip() + + @classmethod + def get_revision(cls, location): + """ + Return the repository-local changeset revision number, as an integer. + """ + current_revision = cls.run_command( + ['parents', '--template={rev}'], + show_stdout=False, cwd=location).strip() + return current_revision + + @classmethod + def get_requirement_revision(cls, location): + """ + Return the changeset identification hash, as a 40-character + hexadecimal string + """ + current_rev_hash = cls.run_command( + ['parents', '--template={node}'], + show_stdout=False, cwd=location).strip() + return current_rev_hash + + @classmethod + def is_commit_id_equal(cls, dest, name): + """Always assume the versions don't match""" + return False + + +vcs.register(Mercurial) diff --git a/venv2/lib/python2.7/site-packages/pip/_internal/vcs/subversion.py b/venv2/lib/python2.7/site-packages/pip/_internal/vcs/subversion.py new file mode 100644 index 0000000..01bb161 --- /dev/null +++ b/venv2/lib/python2.7/site-packages/pip/_internal/vcs/subversion.py @@ -0,0 +1,234 @@ +from __future__ import absolute_import + +import logging +import os +import re + +from pip._internal.utils.logging import indent_log +from pip._internal.utils.misc import ( + display_path, rmtree, split_auth_from_netloc, +) +from pip._internal.utils.typing import MYPY_CHECK_RUNNING +from pip._internal.vcs import VersionControl, vcs + +_svn_xml_url_re = re.compile('url="([^"]+)"') +_svn_rev_re = re.compile(r'committed-rev="(\d+)"') +_svn_info_xml_rev_re = re.compile(r'\s*revision="(\d+)"') +_svn_info_xml_url_re = re.compile(r'<url>(.*)</url>') + + +if MYPY_CHECK_RUNNING: + from typing import Optional, Tuple + +logger = logging.getLogger(__name__) + + +class Subversion(VersionControl): + name = 'svn' + dirname = '.svn' + repo_name = 'checkout' + schemes = ('svn', 'svn+ssh', 'svn+http', 'svn+https', 'svn+svn') + + @classmethod + def should_add_vcs_url_prefix(cls, remote_url): + return True + + @staticmethod + def get_base_rev_args(rev): + return ['-r', rev] + + def get_vcs_version(self): + # type: () -> Optional[Tuple[int, ...]] + """Return the version of the currently installed Subversion client. + + :return: A tuple containing the parts of the version information or + ``None`` if the version returned from ``svn`` could not be parsed. + :raises: BadCommand: If ``svn`` is not installed. + """ + # Example versions: + # svn, version 1.10.3 (r1842928) + # compiled Feb 25 2019, 14:20:39 on x86_64-apple-darwin17.0.0 + # svn, version 1.7.14 (r1542130) + # compiled Mar 28 2018, 08:49:13 on x86_64-pc-linux-gnu + version_prefix = 'svn, version ' + version = self.run_command(['--version'], show_stdout=False) + if not version.startswith(version_prefix): + return None + + version = version[len(version_prefix):].split()[0] + version_list = version.split('.') + try: + parsed_version = tuple(map(int, version_list)) + except ValueError: + return None + + if not parsed_version: + return None + + return parsed_version + + def export(self, location): + """Export the svn repository at the url to the destination location""" + url, rev_options = self.get_url_rev_options(self.url) + + 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) + cmd_args = ['export'] + rev_options.to_args() + [url, location] + self.run_command(cmd_args, show_stdout=False) + + @classmethod + def fetch_new(cls, dest, url, rev_options): + rev_display = rev_options.to_display() + logger.info( + 'Checking out %s%s to %s', + url, + rev_display, + display_path(dest), + ) + cmd_args = ['checkout', '-q'] + rev_options.to_args() + [url, dest] + cls.run_command(cmd_args) + + def switch(self, dest, url, rev_options): + cmd_args = ['switch'] + rev_options.to_args() + [url, dest] + self.run_command(cmd_args) + + def update(self, dest, url, rev_options): + cmd_args = ['update'] + rev_options.to_args() + [dest] + self.run_command(cmd_args) + + @classmethod + def get_revision(cls, 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 cls.dirname not in dirs: + dirs[:] = [] + continue # no sense walking uncontrolled subdirs + dirs.remove(cls.dirname) + entries_fn = os.path.join(base, cls.dirname, 'entries') + if not os.path.exists(entries_fn): + # FIXME: should we warn? + continue + + dirurl, localrev = cls._get_svn_url_rev(base) + + if base == location: + base = dirurl + '/' # save the root url + elif not dirurl or not dirurl.startswith(base): + dirs[:] = [] + continue # not part of the same svn tree, skip it + revision = max(revision, localrev) + return revision + + @classmethod + def get_netloc_and_auth(cls, netloc, scheme): + """ + This override allows the auth information to be passed to svn via the + --username and --password options instead of via the URL. + """ + if scheme == 'ssh': + # The --username and --password options can't be used for + # svn+ssh URLs, so keep the auth information in the URL. + return super(Subversion, cls).get_netloc_and_auth(netloc, scheme) + + return split_auth_from_netloc(netloc) + + @classmethod + def get_url_rev_and_auth(cls, url): + # hotfix the URL scheme after removing svn+ from svn+ssh:// readd it + url, rev, user_pass = super(Subversion, cls).get_url_rev_and_auth(url) + if url.startswith('ssh://'): + url = 'svn+' + url + return url, rev, user_pass + + @staticmethod + def make_rev_args(username, password): + extra_args = [] + if username: + extra_args += ['--username', username] + if password: + extra_args += ['--password', password] + + return extra_args + + @classmethod + def get_remote_url(cls, 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 cls._get_svn_url_rev(location)[0] + + @classmethod + def _get_svn_url_rev(cls, location): + from pip._internal.exceptions import InstallationError + + entries_path = os.path.join(location, cls.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 = cls.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 + + @classmethod + def is_commit_id_equal(cls, dest, name): + """Always assume the versions don't match""" + return False + + +vcs.register(Subversion) diff --git a/venv2/lib/python2.7/site-packages/pip/_internal/wheel.py b/venv2/lib/python2.7/site-packages/pip/_internal/wheel.py new file mode 100644 index 0000000..1bdbe93 --- /dev/null +++ b/venv2/lib/python2.7/site-packages/pip/_internal/wheel.py @@ -0,0 +1,1098 @@ +""" +Support for installing and building the "wheel" binary package format. +""" +from __future__ import absolute_import + +import collections +import compileall +import csv +import hashlib +import logging +import os.path +import re +import shutil +import stat +import sys +import warnings +from base64 import urlsafe_b64encode +from email.parser import Parser + +from pip._vendor import pkg_resources +from pip._vendor.distlib.scripts import ScriptMaker +from pip._vendor.packaging.utils import canonicalize_name +from pip._vendor.six import StringIO + +from pip._internal import pep425tags +from pip._internal.download import path_to_url, unpack_url +from pip._internal.exceptions import ( + InstallationError, InvalidWheelFilename, UnsupportedWheel, +) +from pip._internal.locations import ( + PIP_DELETE_MARKER_FILENAME, distutils_scheme, +) +from pip._internal.models.link import Link +from pip._internal.utils.logging import indent_log +from pip._internal.utils.misc import ( + LOG_DIVIDER, call_subprocess, captured_stdout, ensure_dir, + format_command_args, read_chunks, +) +from pip._internal.utils.setuptools_build import SETUPTOOLS_SHIM +from pip._internal.utils.temp_dir import TempDirectory +from pip._internal.utils.typing import MYPY_CHECK_RUNNING +from pip._internal.utils.ui import open_spinner + +if MYPY_CHECK_RUNNING: + from typing import ( + Dict, List, Optional, Sequence, Mapping, Tuple, IO, Text, Any, Iterable + ) + from pip._vendor.packaging.requirements import Requirement + from pip._internal.req.req_install import InstallRequirement + from pip._internal.download import PipSession + from pip._internal.index import FormatControl, PackageFinder + from pip._internal.operations.prepare import ( + RequirementPreparer + ) + from pip._internal.cache import WheelCache + from pip._internal.pep425tags import Pep425Tag + + InstalledCSVRow = Tuple[str, ...] + + +VERSION_COMPATIBLE = (1, 0) + + +logger = logging.getLogger(__name__) + + +def normpath(src, p): + return os.path.relpath(src, p).replace(os.path.sep, '/') + + +def rehash(path, blocksize=1 << 20): + # type: (str, int) -> Tuple[str, str] + """Return (hash, length) for path using hashlib.sha256()""" + h = hashlib.sha256() + 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('=') + # unicode/str python2 issues + return (digest, str(length)) # type: ignore + + +def open_for_csv(name, mode): + # type: (str, Text) -> IO + if sys.version_info[0] < 3: + nl = {} # type: Dict[str, Any] + bin = 'b' + else: + nl = {'newline': ''} # type: Dict[str, Any] + bin = '' + return open(name, mode + bin, **nl) + + +def replace_python_tag(wheelname, new_tag): + # type: (str, str) -> str + """Replace the Python tag in a wheel file name with a new value. + """ + parts = wheelname.split('-') + parts[-3] = new_tag + return '-'.join(parts) + + +def fix_script(path): + # type: (str) -> Optional[bool] + """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 + return None + + +dist_info_re = re.compile(r"""^(?P<namever>(?P<name>.+?)(-(?P<ver>.+?))?) + \.dist-info$""", re.VERBOSE) + + +def root_is_purelib(name, wheeldir): + # type: (str, str) -> bool + """ + 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): + # type: (str) -> Tuple[Dict[str, str], Dict[str, str]] + 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) + + # get the entry points and then the script names + entry_points = pkg_resources.EntryPoint.parse_map(data) + console = entry_points.get('console_scripts', {}) + gui = entry_points.get('gui_scripts', {}) + + def _split_ep(s): + """get the string representation of EntryPoint, remove space and split + on '='""" + return str(s).replace(" ", "").split("=") + + # convert the EntryPoint objects into strings with module:function + console = dict(_split_ep(v) for v in console.values()) + gui = dict(_split_ep(v) for v in gui.values()) + return console, gui + + +def message_about_scripts_not_on_PATH(scripts): + # type: (Sequence[str]) -> Optional[str] + """Determine if any scripts are not on PATH and format a warning. + + Returns a warning message if one or more scripts are not on PATH, + otherwise None. + """ + if not scripts: + return None + + # Group scripts by the path they were installed in + grouped_by_dir = collections.defaultdict(set) # type: Dict[str, set] + for destfile in scripts: + parent_dir = os.path.dirname(destfile) + script_name = os.path.basename(destfile) + grouped_by_dir[parent_dir].add(script_name) + + # We don't want to warn for directories that are on PATH. + not_warn_dirs = [ + os.path.normcase(i).rstrip(os.sep) for i in + os.environ.get("PATH", "").split(os.pathsep) + ] + # If an executable sits with sys.executable, we don't warn for it. + # This covers the case of venv invocations without activating the venv. + not_warn_dirs.append(os.path.normcase(os.path.dirname(sys.executable))) + warn_for = { + parent_dir: scripts for parent_dir, scripts in grouped_by_dir.items() + if os.path.normcase(parent_dir) not in not_warn_dirs + } + if not warn_for: + return None + + # Format a message + msg_lines = [] + for parent_dir, scripts in warn_for.items(): + sorted_scripts = sorted(scripts) # type: List[str] + if len(sorted_scripts) == 1: + start_text = "script {} is".format(sorted_scripts[0]) + else: + start_text = "scripts {} are".format( + ", ".join(sorted_scripts[:-1]) + " and " + sorted_scripts[-1] + ) + + msg_lines.append( + "The {} installed in '{}' which is not on PATH." + .format(start_text, parent_dir) + ) + + last_line_fmt = ( + "Consider adding {} to PATH or, if you prefer " + "to suppress this warning, use --no-warn-script-location." + ) + if len(msg_lines) == 1: + msg_lines.append(last_line_fmt.format("this directory")) + else: + msg_lines.append(last_line_fmt.format("these directories")) + + # Returns the formatted multiline message + return "\n".join(msg_lines) + + +def sorted_outrows(outrows): + # type: (Iterable[InstalledCSVRow]) -> List[InstalledCSVRow] + """ + Return the given rows of a RECORD file in sorted order. + + Each row is a 3-tuple (path, hash, size) and corresponds to a record of + a RECORD file (see PEP 376 and PEP 427 for details). For the rows + passed to this function, the size can be an integer as an int or string, + or the empty string. + """ + # Normally, there should only be one row per path, in which case the + # second and third elements don't come into play when sorting. + # However, in cases in the wild where a path might happen to occur twice, + # we don't want the sort operation to trigger an error (but still want + # determinism). Since the third element can be an int or string, we + # coerce each element to a string to avoid a TypeError in this case. + # For additional background, see-- + # https://github.com/pypa/pip/issues/5868 + return sorted(outrows, key=lambda row: tuple(str(x) for x in row)) + + +def get_csv_rows_for_installed( + old_csv_rows, # type: Iterable[List[str]] + installed, # type: Dict[str, str] + changed, # type: set + generated, # type: List[str] + lib_dir, # type: str +): + # type: (...) -> List[InstalledCSVRow] + """ + :param installed: A map from archive RECORD path to installation RECORD + path. + """ + installed_rows = [] # type: List[InstalledCSVRow] + for row in old_csv_rows: + if len(row) > 3: + logger.warning( + 'RECORD line has more than three elements: {}'.format(row) + ) + # Make a copy because we are mutating the row. + row = list(row) + old_path = row[0] + new_path = installed.pop(old_path, old_path) + row[0] = new_path + if new_path in changed: + digest, length = rehash(new_path) + row[1] = digest + row[2] = length + installed_rows.append(tuple(row)) + for f in generated: + digest, length = rehash(f) + installed_rows.append((normpath(f, lib_dir), digest, str(length))) + for f in installed: + installed_rows.append((installed[f], '', '')) + return installed_rows + + +def move_wheel_files( + name, # type: str + req, # type: Requirement + wheeldir, # type: str + user=False, # type: bool + home=None, # type: Optional[str] + root=None, # type: Optional[str] + pycompile=True, # type: bool + scheme=None, # type: Optional[Mapping[str, str]] + isolated=False, # type: bool + prefix=None, # type: Optional[str] + warn_script_location=True # type: bool +): + # type: (...) -> None + """Install a wheel""" + # TODO: Investigate and break this up. + # TODO: Look into moving this into a dedicated class for representing an + # installation. + + 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 = [] # type: List[str] + 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 = {} # type: Dict[str, str] + changed = set() + generated = [] # type: List[str] + + # 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 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 + canonicalize_name(s).startswith( + canonicalize_name(req.name))): + assert not info_dir, ('Multiple .dist-info directories: ' + + destsubdir + ', ' + + ', '.join(info_dir)) + 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) + + # copyfile (called below) truncates the destination if it + # exists and then writes the new contents. This is fine in most + # cases, but can cause a segfault if pip has loaded a shared + # object (e.g. from pyopenssl through its vendored urllib3) + # Since the shared object is mmap'd an attempt to call a + # symbol in it will then cause a segfault. Unlinking the file + # allows writing of new contents while allowing the process to + # continue to use the old copy. + if os.path.exists(destfile): + os.unlink(destfile) + + # 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 = {''} + + # 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, + } + # ignore type, because mypy disallows assigning to a method, + # see https://github.com/python/mypy/issues/2427 + maker._get_script_text = _get_script_text # type: ignore + maker.script_template = r"""# -*- 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 Metadata 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_console_scripts = maker.make_multiple( + ['%s = %s' % kv for kv in console.items()] + ) + generated.extend(generated_console_scripts) + + if warn_script_location: + msg = message_about_scripts_not_on_PATH(generated_console_scripts) + if msg is not None: + logger.warning(msg) + + 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) + outrows = get_csv_rows_for_installed( + reader, installed=installed, changed=changed, + generated=generated, lib_dir=lib_dir, + ) + writer = csv.writer(record_out) + # Sort to simplify testing. + for row in sorted_outrows(outrows): + writer.writerow(row) + shutil.move(temp_record, record) + + +def wheel_version(source_dir): + # type: (Optional[str]) -> Optional[Tuple[int, ...]] + """ + Return the Wheel-Version of an extracted wheel, if possible. + + Otherwise, return None 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 Exception: + return None + + +def check_compatibility(version, name): + # type: (Optional[Tuple[int, ...]], str) -> None + """ + 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 class into the models sub-package + # TODO: Maybe move the install code into this class + + wheel_file_re = re.compile( + r"""^(?P<namever>(?P<name>.+?)-(?P<ver>.*?)) + ((-(?P<build>\d[^-]*?))?-(?P<pyver>.+?)-(?P<abi>.+?)-(?P<plat>.+?) + \.whl|\.dist-info)$""", + re.VERBOSE + ) + + def __init__(self, filename): + # type: (str) -> None + """ + :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.build_tag = wheel_info.group('build') + 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 = { + (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): + # type: (Optional[List[Pep425Tag]]) -> Optional[int] + """ + 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.get_supported() + 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): + # type: (Optional[List[Pep425Tag]]) -> bool + """Is this wheel supported on this system?""" + if tags is None: # for mock + tags = pep425tags.get_supported() + return bool(set(tags).intersection(self.file_tags)) + + +def _contains_egg_info( + s, _egg_info_re=re.compile(r'([a-z0-9_.]+)-([a-z0-9_.!+-]+)', re.I)): + """Determine whether the string looks like an egg_info. + + :param s: The string to parse. E.g. foo-2.1 + """ + return bool(_egg_info_re.search(s)) + + +def should_use_ephemeral_cache( + req, # type: InstallRequirement + format_control, # type: FormatControl + autobuilding, # type: bool + cache_available # type: bool +): + # type: (...) -> Optional[bool] + """ + Return whether to build an InstallRequirement object using the + ephemeral cache. + + :param cache_available: whether a cache directory is available for the + autobuilding=True case. + + :return: True or False to build the requirement with ephem_cache=True + or False, respectively; or None not to build the requirement. + """ + if req.constraint: + return None + if req.is_wheel: + if not autobuilding: + logger.info( + 'Skipping %s, due to already being wheel.', req.name, + ) + return None + if not autobuilding: + return False + + if req.editable or not req.source_dir: + return None + + if req.link and not req.link.is_artifact: + # VCS checkout. Build wheel just for this run. + return True + + if "binary" not in format_control.get_allowed_formats( + canonicalize_name(req.name)): + logger.info( + "Skipping bdist_wheel for %s, due to binaries " + "being disabled for it.", req.name, + ) + return None + + link = req.link + base, ext = link.splitext() + if cache_available and _contains_egg_info(base): + return False + + # Otherwise, build the wheel just for this run using the ephemeral + # cache since we are either in the case of e.g. a local directory, or + # no cache directory is available to use. + return True + + +def format_command_result( + command_args, # type: List[str] + command_output, # type: str +): + # type: (...) -> str + """ + Format command information for logging. + """ + command_desc = format_command_args(command_args) + text = 'Command arguments: {}\n'.format(command_desc) + + if not command_output: + text += 'Command output: None' + elif logger.getEffectiveLevel() > logging.DEBUG: + text += 'Command output: [use --verbose to show]' + else: + if not command_output.endswith('\n'): + command_output += '\n' + text += 'Command output:\n{}{}'.format(command_output, LOG_DIVIDER) + + return text + + +def get_legacy_build_wheel_path( + names, # type: List[str] + temp_dir, # type: str + req, # type: InstallRequirement + command_args, # type: List[str] + command_output, # type: str +): + # type: (...) -> Optional[str] + """ + Return the path to the wheel in the temporary build directory. + """ + # Sort for determinism. + names = sorted(names) + if not names: + msg = ( + 'Legacy build of wheel for {!r} created no files.\n' + ).format(req.name) + msg += format_command_result(command_args, command_output) + logger.warning(msg) + return None + + if len(names) > 1: + msg = ( + 'Legacy build of wheel for {!r} created more than one file.\n' + 'Filenames (choosing first): {}\n' + ).format(req.name, names) + msg += format_command_result(command_args, command_output) + logger.warning(msg) + + return os.path.join(temp_dir, names[0]) + + +class WheelBuilder(object): + """Build wheels from a RequirementSet.""" + + def __init__( + self, + finder, # type: PackageFinder + preparer, # type: RequirementPreparer + wheel_cache, # type: WheelCache + build_options=None, # type: Optional[List[str]] + global_options=None, # type: Optional[List[str]] + no_clean=False # type: bool + ): + # type: (...) -> None + self.finder = finder + self.preparer = preparer + self.wheel_cache = wheel_cache + + self._wheel_dir = preparer.wheel_download_dir + + self.build_options = build_options or [] + self.global_options = global_options or [] + self.no_clean = no_clean + + 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. + """ + # Install build deps into temporary directory (PEP 518) + with req.build_env: + return self._build_one_inside_env(req, output_dir, + python_tag=python_tag) + + def _build_one_inside_env(self, req, output_dir, python_tag=None): + with TempDirectory(kind="wheel") as temp_dir: + if req.use_pep517: + builder = self._build_one_pep517 + else: + builder = self._build_one_legacy + wheel_path = builder(req, temp_dir.path, python_tag=python_tag) + if wheel_path is not None: + wheel_name = os.path.basename(wheel_path) + dest_path = os.path.join(output_dir, wheel_name) + try: + shutil.move(wheel_path, dest_path) + logger.info('Stored in directory: %s', output_dir) + return dest_path + except Exception: + pass + # Ignore return, we can't do anything else useful. + self._clean_one(req) + return None + + def _base_setup_args(self, req): + # NOTE: Eventually, we'd want to also -S to the flags here, when we're + # isolating. Currently, it breaks Python in virtualenvs, because it + # relies on site.py to find parts of the standard library outside the + # virtualenv. + return [ + sys.executable, '-u', '-c', + SETUPTOOLS_SHIM % req.setup_py + ] + list(self.global_options) + + def _build_one_pep517(self, req, tempd, python_tag=None): + """Build one InstallRequirement using the PEP 517 build process. + + Returns path to wheel if successfully built. Otherwise, returns None. + """ + assert req.metadata_directory is not None + if self.build_options: + # PEP 517 does not support --build-options + logger.error('Cannot build wheel for %s using PEP 517 when ' + '--build-options is present' % (req.name,)) + return None + try: + req.spin_message = 'Building wheel for %s (PEP 517)' % (req.name,) + logger.debug('Destination directory: %s', tempd) + wheel_name = req.pep517_backend.build_wheel( + tempd, + metadata_directory=req.metadata_directory + ) + if python_tag: + # General PEP 517 backends don't necessarily support + # a "--python-tag" option, so we rename the wheel + # file directly. + new_name = replace_python_tag(wheel_name, python_tag) + os.rename( + os.path.join(tempd, wheel_name), + os.path.join(tempd, new_name) + ) + # Reassign to simplify the return at the end of function + wheel_name = new_name + except Exception: + logger.error('Failed building wheel for %s', req.name) + return None + return os.path.join(tempd, wheel_name) + + def _build_one_legacy(self, req, tempd, python_tag=None): + """Build one InstallRequirement using the "legacy" build process. + + Returns path to wheel if successfully built. Otherwise, returns None. + """ + base_args = self._base_setup_args(req) + + spin_message = 'Building wheel for %s (setup.py)' % (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: + output = call_subprocess(wheel_args, cwd=req.setup_py_dir, + spinner=spinner) + except Exception: + spinner.finish("error") + logger.error('Failed building wheel for %s', req.name) + return None + names = os.listdir(tempd) + wheel_path = get_legacy_build_wheel_path( + names=names, + temp_dir=tempd, + req=req, + command_args=wheel_args, + command_output=output, + ) + return wheel_path + + 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) + return True + except Exception: + logger.error('Failed cleaning build dir for %s', req.name) + return False + + def build( + self, + requirements, # type: Iterable[InstallRequirement] + session, # type: PipSession + autobuilding=False # type: bool + ): + # type: (...) -> List[InstallRequirement] + """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. + """ + buildset = [] + format_control = self.finder.format_control + # Whether a cache directory is available for autobuilding=True. + cache_available = bool(self._wheel_dir or self.wheel_cache.cache_dir) + + for req in requirements: + ephem_cache = should_use_ephemeral_cache( + req, format_control=format_control, autobuilding=autobuilding, + cache_available=cache_available, + ) + if ephem_cache is None: + continue + + buildset.append((req, ephem_cache)) + + if not buildset: + return [] + + # Is any wheel build not using the ephemeral cache? + if any(not ephem_cache for _, ephem_cache in buildset): + have_directory_for_build = self._wheel_dir or ( + autobuilding and self.wheel_cache.cache_dir + ) + assert have_directory_for_build + + # TODO by @pradyunsg + # Should break up this method into 2 separate methods. + + # Build the wheels. + logger.info( + 'Building wheels for collected packages: %s', + ', '.join([req.name for (req, _) in buildset]), + ) + _cache = self.wheel_cache # shorter name + with indent_log(): + build_success, build_failure = [], [] + for req, ephem in buildset: + python_tag = None + if autobuilding: + python_tag = pep425tags.implementation_tag + if ephem: + output_dir = _cache.get_ephem_path_for_link(req.link) + else: + output_dir = _cache.get_path_for_link(req.link) + try: + ensure_dir(output_dir) + except OSError as e: + logger.warning("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.preparer.build_dir + ) + # Update the link for this. + req.link = 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=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 a list of requirements that failed to build + return build_failure diff --git a/venv2/lib/python2.7/site-packages/pip/_vendor/__init__.py b/venv2/lib/python2.7/site-packages/pip/_vendor/__init__.py new file mode 100644 index 0000000..c1d9508 --- /dev/null +++ b/venv2/lib/python2.7/site-packages/pip/_vendor/__init__.py @@ -0,0 +1,109 @@ +""" +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 = False + +# 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.dirname(__file__)) + + +# 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__(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("distro") + vendored("html5lib") + vendored("lockfile") + vendored("six") + vendored("six.moves") + vendored("six.moves.urllib") + vendored("six.moves.urllib.parse") + vendored("packaging") + vendored("packaging.version") + vendored("packaging.specifiers") + vendored("pep517") + vendored("pkg_resources") + vendored("progress") + vendored("pytoml") + vendored("retrying") + vendored("requests") + vendored("requests.exceptions") + 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") + vendored("requests.packages.urllib3.packages.ordered_dict") + vendored("requests.packages.urllib3.packages.six") + 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") + vendored("urllib3") diff --git a/venv2/lib/python2.7/site-packages/pip/_vendor/appdirs.py b/venv2/lib/python2.7/site-packages/pip/_vendor/appdirs.py new file mode 100644 index 0000000..2bd3911 --- /dev/null +++ b/venv2/lib/python2.7/site-packages/pip/_vendor/appdirs.py @@ -0,0 +1,604 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +# Copyright (c) 2005-2010 ActiveState Software Inc. +# Copyright (c) 2013 Eddy Petrișor + +"""Utilities for determining application-specific dirs. + +See <http://github.com/ActiveState/appdirs> for details and usage. +""" +# Dev Notes: +# - MSDN on where to store app data files: +# http://support.microsoft.com/default.aspx?scid=kb;en-us;310294#XSLTH3194121123120121120120 +# - Mac OS X: http://developer.apple.com/documentation/MacOSX/Conceptual/BPFileSystem/index.html +# - XDG spec for Un*x: http://standards.freedesktop.org/basedir-spec/basedir-spec-latest.html + +__version_info__ = (1, 4, 3) +__version__ = '.'.join(map(str, __version_info__)) + + +import sys +import os + +PY3 = sys.version_info[0] == 3 + +if PY3: + unicode = str + +if sys.platform.startswith('java'): + import platform + os_name = platform.java_ver()[3][0] + if os_name.startswith('Windows'): # "Windows XP", "Windows 7", etc. + system = 'win32' + elif os_name.startswith('Mac'): # "Mac OS X", etc. + system = 'darwin' + else: # "Linux", "SunOS", "FreeBSD", etc. + # Setting this to "linux2" is not ideal, but only Windows or Mac + # are actually checked for and the rest of the module expects + # *sys.platform* style strings. + system = 'linux2' +else: + system = sys.platform + + + +def user_data_dir(appname=None, appauthor=None, version=None, roaming=False): + r"""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. + "appauthor" (only used on Windows) is the name of the + appauthor or distributing body for this application. Typically + it is the owning company name. This falls back to appname. You may + pass False to disable it. + "version" is an optional version path element to append to the + path. You might want to use this if you want multiple versions + of your app to be able to run independently. If used, this + would typically be "<major>.<minor>". + Only applied when appname is present. + "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\<AppAuthor>\<AppName> + Win XP (roaming): C:\Documents and Settings\<username>\Local Settings\Application Data\<AppAuthor>\<AppName> + Win 7 (not roaming): C:\Users\<username>\AppData\Local\<AppAuthor>\<AppName> + Win 7 (roaming): C:\Users\<username>\AppData\Roaming\<AppAuthor>\<AppName> + + For Unix, we follow the XDG spec and support $XDG_DATA_HOME. + That means, by default "~/.local/share/<AppName>". + """ + if system == "win32": + if appauthor is None: + appauthor = appname + const = roaming and "CSIDL_APPDATA" or "CSIDL_LOCAL_APPDATA" + path = os.path.normpath(_get_win_folder(const)) + if appname: + if appauthor is not False: + path = os.path.join(path, appauthor, appname) + else: + path = os.path.join(path, appname) + elif system == 'darwin': + path = os.path.expanduser('~/Library/Application Support/') + if appname: + path = os.path.join(path, appname) + else: + path = os.getenv('XDG_DATA_HOME', os.path.expanduser("~/.local/share")) + if appname: + path = os.path.join(path, appname) + if appname and version: + path = os.path.join(path, version) + return path + + +def site_data_dir(appname=None, appauthor=None, version=None, multipath=False): + r"""Return full path to the user-shared data dir for this application. + + "appname" is the name of application. + If None, just the system directory is returned. + "appauthor" (only used on Windows) is the name of the + appauthor or distributing body for this application. Typically + it is the owning company name. This falls back to appname. You may + pass False to disable it. + "version" is an optional version path element to append to the + path. You might want to use this if you want multiple versions + of your app to be able to run independently. If used, this + would typically be "<major>.<minor>". + Only applied when appname is present. + "multipath" is an optional parameter only applicable to *nix + which indicates that the entire list of data dirs should be + returned. By default, the first item from XDG_DATA_DIRS is + returned, or '/usr/local/share/<AppName>', + if XDG_DATA_DIRS is not set + + Typical site data directories are: + Mac OS X: /Library/Application Support/<AppName> + Unix: /usr/local/share/<AppName> or /usr/share/<AppName> + Win XP: C:\Documents and Settings\All Users\Application Data\<AppAuthor>\<AppName> + Vista: (Fail! "C:\ProgramData" is a hidden *system* directory on Vista.) + Win 7: C:\ProgramData\<AppAuthor>\<AppName> # Hidden, but writeable on Win 7. + + For Unix, this is using the $XDG_DATA_DIRS[0] default. + + WARNING: Do not use this on Windows. See the Vista-Fail note above for why. + """ + if system == "win32": + if appauthor is None: + appauthor = appname + path = os.path.normpath(_get_win_folder("CSIDL_COMMON_APPDATA")) + if appname: + if appauthor is not False: + path = os.path.join(path, appauthor, appname) + else: + path = os.path.join(path, appname) + elif system == 'darwin': + path = os.path.expanduser('/Library/Application Support') + if appname: + path = os.path.join(path, appname) + else: + # XDG default for $XDG_DATA_DIRS + # only first, if multipath is False + path = os.getenv('XDG_DATA_DIRS', + os.pathsep.join(['/usr/local/share', '/usr/share'])) + pathlist = [os.path.expanduser(x.rstrip(os.sep)) for x in path.split(os.pathsep)] + if appname: + if version: + appname = os.path.join(appname, version) + pathlist = [os.sep.join([x, appname]) for x in pathlist] + + if multipath: + path = os.pathsep.join(pathlist) + else: + path = pathlist[0] + return path + + if appname and version: + path = os.path.join(path, version) + return path + + +def user_config_dir(appname=None, appauthor=None, version=None, roaming=False): + r"""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. + "appauthor" (only used on Windows) is the name of the + appauthor or distributing body for this application. Typically + it is the owning company name. This falls back to appname. You may + pass False to disable it. + "version" is an optional version path element to append to the + path. You might want to use this if you want multiple versions + of your app to be able to run independently. If used, this + would typically be "<major>.<minor>". + Only applied when appname is present. + "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 config directories are: + Mac OS X: same as user_data_dir + Unix: ~/.config/<AppName> # or in $XDG_CONFIG_HOME, if defined + Win *: same as user_data_dir + + For Unix, we follow the XDG spec and support $XDG_CONFIG_HOME. + That means, by default "~/.config/<AppName>". + """ + if system in ["win32", "darwin"]: + path = user_data_dir(appname, appauthor, None, roaming) + else: + path = os.getenv('XDG_CONFIG_HOME', os.path.expanduser("~/.config")) + if appname: + path = os.path.join(path, appname) + if appname and version: + path = os.path.join(path, version) + return path + + +def site_config_dir(appname=None, appauthor=None, version=None, multipath=False): + r"""Return full path to the user-shared data dir for this application. + + "appname" is the name of application. + If None, just the system directory is returned. + "appauthor" (only used on Windows) is the name of the + appauthor or distributing body for this application. Typically + it is the owning company name. This falls back to appname. You may + pass False to disable it. + "version" is an optional version path element to append to the + path. You might want to use this if you want multiple versions + of your app to be able to run independently. If used, this + would typically be "<major>.<minor>". + Only applied when appname is present. + "multipath" is an optional parameter only applicable to *nix + which indicates that the entire list of config dirs should be + returned. By default, the first item from XDG_CONFIG_DIRS is + returned, or '/etc/xdg/<AppName>', if XDG_CONFIG_DIRS is not set + + Typical site config directories are: + Mac OS X: same as site_data_dir + Unix: /etc/xdg/<AppName> or $XDG_CONFIG_DIRS[i]/<AppName> for each value in + $XDG_CONFIG_DIRS + Win *: same as site_data_dir + Vista: (Fail! "C:\ProgramData" is a hidden *system* directory on Vista.) + + For Unix, this is using the $XDG_CONFIG_DIRS[0] default, if multipath=False + + WARNING: Do not use this on Windows. See the Vista-Fail note above for why. + """ + if system in ["win32", "darwin"]: + path = site_data_dir(appname, appauthor) + if appname and version: + path = os.path.join(path, version) + else: + # XDG default for $XDG_CONFIG_DIRS + # only first, if multipath is False + path = os.getenv('XDG_CONFIG_DIRS', '/etc/xdg') + pathlist = [os.path.expanduser(x.rstrip(os.sep)) for x in path.split(os.pathsep)] + if appname: + if version: + appname = os.path.join(appname, version) + pathlist = [os.sep.join([x, appname]) for x in pathlist] + + if multipath: + path = os.pathsep.join(pathlist) + else: + path = pathlist[0] + return path + + +def user_cache_dir(appname=None, appauthor=None, version=None, opinion=True): + r"""Return full path to the user-specific cache dir for this application. + + "appname" is the name of application. + If None, just the system directory is returned. + "appauthor" (only used on Windows) is the name of the + appauthor or distributing body for this application. Typically + it is the owning company name. This falls back to appname. You may + pass False to disable it. + "version" is an optional version path element to append to the + path. You might want to use this if you want multiple versions + of your app to be able to run independently. If used, this + would typically be "<major>.<minor>". + Only applied when appname is present. + "opinion" (boolean) can be False to disable the appending of + "Cache" to the base app data dir for Windows. See + discussion below. + + Typical user cache directories are: + Mac OS X: ~/Library/Caches/<AppName> + Unix: ~/.cache/<AppName> (XDG default) + Win XP: C:\Documents and Settings\<username>\Local Settings\Application Data\<AppAuthor>\<AppName>\Cache + Vista: C:\Users\<username>\AppData\Local\<AppAuthor>\<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` above). 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. + This can be disabled with the `opinion=False` option. + """ + if system == "win32": + if appauthor is None: + appauthor = appname + path = os.path.normpath(_get_win_folder("CSIDL_LOCAL_APPDATA")) + if appname: + if appauthor is not False: + path = os.path.join(path, appauthor, appname) + else: + path = os.path.join(path, appname) + if opinion: + path = os.path.join(path, "Cache") + elif system == 'darwin': + path = os.path.expanduser('~/Library/Caches') + if appname: + path = os.path.join(path, appname) + else: + path = os.getenv('XDG_CACHE_HOME', os.path.expanduser('~/.cache')) + if appname: + path = os.path.join(path, appname) + if appname and version: + path = os.path.join(path, version) + return path + + +def user_state_dir(appname=None, appauthor=None, version=None, roaming=False): + r"""Return full path to the user-specific state dir for this application. + + "appname" is the name of application. + If None, just the system directory is returned. + "appauthor" (only used on Windows) is the name of the + appauthor or distributing body for this application. Typically + it is the owning company name. This falls back to appname. You may + pass False to disable it. + "version" is an optional version path element to append to the + path. You might want to use this if you want multiple versions + of your app to be able to run independently. If used, this + would typically be "<major>.<minor>". + Only applied when appname is present. + "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 state directories are: + Mac OS X: same as user_data_dir + Unix: ~/.local/state/<AppName> # or in $XDG_STATE_HOME, if defined + Win *: same as user_data_dir + + For Unix, we follow this Debian proposal <https://wiki.debian.org/XDGBaseDirectorySpecification#state> + to extend the XDG spec and support $XDG_STATE_HOME. + + That means, by default "~/.local/state/<AppName>". + """ + if system in ["win32", "darwin"]: + path = user_data_dir(appname, appauthor, None, roaming) + else: + path = os.getenv('XDG_STATE_HOME', os.path.expanduser("~/.local/state")) + if appname: + path = os.path.join(path, appname) + if appname and version: + path = os.path.join(path, version) + return path + + +def user_log_dir(appname=None, appauthor=None, version=None, opinion=True): + r"""Return full path to the user-specific log dir for this application. + + "appname" is the name of application. + If None, just the system directory is returned. + "appauthor" (only used on Windows) is the name of the + appauthor or distributing body for this application. Typically + it is the owning company name. This falls back to appname. You may + pass False to disable it. + "version" is an optional version path element to append to the + path. You might want to use this if you want multiple versions + of your app to be able to run independently. If used, this + would typically be "<major>.<minor>". + Only applied when appname is present. + "opinion" (boolean) can be False to disable the appending of + "Logs" to the base app data dir for Windows, and "log" to the + base cache dir for Unix. See discussion below. + + Typical user log directories are: + Mac OS X: ~/Library/Logs/<AppName> + Unix: ~/.cache/<AppName>/log # or under $XDG_CACHE_HOME if defined + Win XP: C:\Documents and Settings\<username>\Local Settings\Application Data\<AppAuthor>\<AppName>\Logs + Vista: C:\Users\<username>\AppData\Local\<AppAuthor>\<AppName>\Logs + + On Windows the only suggestion in the MSDN docs is that local settings + go in the `CSIDL_LOCAL_APPDATA` directory. (Note: I'm interested in + examples of what some windows apps use for a logs dir.) + + OPINION: This function appends "Logs" to the `CSIDL_LOCAL_APPDATA` + value for Windows and appends "log" to the user cache dir for Unix. + This can be disabled with the `opinion=False` option. + """ + if system == "darwin": + path = os.path.join( + os.path.expanduser('~/Library/Logs'), + appname) + elif system == "win32": + path = user_data_dir(appname, appauthor, version) + version = False + if opinion: + path = os.path.join(path, "Logs") + else: + path = user_cache_dir(appname, appauthor, version) + version = False + if opinion: + path = os.path.join(path, "log") + if appname and version: + path = os.path.join(path, version) + return path + + +class AppDirs(object): + """Convenience wrapper for getting application dirs.""" + def __init__(self, appname=None, appauthor=None, version=None, + roaming=False, multipath=False): + self.appname = appname + self.appauthor = appauthor + self.version = version + self.roaming = roaming + self.multipath = multipath + + @property + def user_data_dir(self): + return user_data_dir(self.appname, self.appauthor, + version=self.version, roaming=self.roaming) + + @property + def site_data_dir(self): + return site_data_dir(self.appname, self.appauthor, + version=self.version, multipath=self.multipath) + + @property + def user_config_dir(self): + return user_config_dir(self.appname, self.appauthor, + version=self.version, roaming=self.roaming) + + @property + def site_config_dir(self): + return site_config_dir(self.appname, self.appauthor, + version=self.version, multipath=self.multipath) + + @property + def user_cache_dir(self): + return user_cache_dir(self.appname, self.appauthor, + version=self.version) + + @property + def user_state_dir(self): + return user_state_dir(self.appname, self.appauthor, + version=self.version) + + @property + def user_log_dir(self): + return user_log_dir(self.appname, self.appauthor, + version=self.version) + + +#---- internal support stuff + +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. + """ + if PY3: + import winreg as _winreg + else: + 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" + ) + dir, type = _winreg.QueryValueEx(key, shell_folder_name) + return dir + + +def _get_win_folder_with_pywin32(csidl_name): + from win32com.shell import shellcon, shell + dir = shell.SHGetFolderPath(0, getattr(shellcon, csidl_name), 0, 0) + # Try to make this a unicode path because SHGetFolderPath does + # not return unicode strings when there is unicode data in the + # path. + try: + dir = unicode(dir) + + # 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 dir: + if ord(c) > 255: + has_high_char = True + break + if has_high_char: + try: + import win32api + dir = win32api.GetShortPathName(dir) + except ImportError: + pass + except UnicodeError: + pass + return dir + + +def _get_win_folder_with_ctypes(csidl_name): + import ctypes + + 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 + +def _get_win_folder_with_jna(csidl_name): + import array + from com.sun import jna + from com.sun.jna.platform import win32 + + buf_size = win32.WinDef.MAX_PATH * 2 + buf = array.zeros('c', buf_size) + shell = win32.Shell32.INSTANCE + shell.SHGetFolderPath(None, getattr(win32.ShlObj, csidl_name), None, win32.ShlObj.SHGFP_TYPE_CURRENT, buf) + dir = jna.Native.toString(buf.tostring()).rstrip("\0") + + # 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 dir: + if ord(c) > 255: + has_high_char = True + break + if has_high_char: + buf = array.zeros('c', buf_size) + kernel = win32.Kernel32.INSTANCE + if kernel.GetShortPathName(dir, buf, buf_size): + dir = jna.Native.toString(buf.tostring()).rstrip("\0") + + return dir + +if system == "win32": + try: + from ctypes import windll + _get_win_folder = _get_win_folder_with_ctypes + except ImportError: + try: + import com.sun.jna + _get_win_folder = _get_win_folder_with_jna + except ImportError: + _get_win_folder = _get_win_folder_from_registry + + +#---- self test code + +if __name__ == "__main__": + appname = "MyApp" + appauthor = "MyCompany" + + props = ("user_data_dir", + "user_config_dir", + "user_cache_dir", + "user_state_dir", + "user_log_dir", + "site_data_dir", + "site_config_dir") + + print("-- app dirs %s --" % __version__) + + print("-- app dirs (with optional 'version')") + dirs = AppDirs(appname, appauthor, version="1.0") + for prop in props: + print("%s: %s" % (prop, getattr(dirs, prop))) + + print("\n-- app dirs (without optional 'version')") + dirs = AppDirs(appname, appauthor) + for prop in props: + print("%s: %s" % (prop, getattr(dirs, prop))) + + print("\n-- app dirs (without optional 'appauthor')") + dirs = AppDirs(appname) + for prop in props: + print("%s: %s" % (prop, getattr(dirs, prop))) + + print("\n-- app dirs (with disabled 'appauthor')") + dirs = AppDirs(appname, appauthor=False) + for prop in props: + print("%s: %s" % (prop, getattr(dirs, prop))) diff --git a/venv2/lib/python2.7/site-packages/pip/_vendor/cachecontrol/__init__.py b/venv2/lib/python2.7/site-packages/pip/_vendor/cachecontrol/__init__.py new file mode 100644 index 0000000..8fdee66 --- /dev/null +++ b/venv2/lib/python2.7/site-packages/pip/_vendor/cachecontrol/__init__.py @@ -0,0 +1,11 @@ +"""CacheControl import Interface. + +Make it easy to import from cachecontrol without long namespaces. +""" +__author__ = "Eric Larson" +__email__ = "eric@ionrock.org" +__version__ = "0.12.5" + +from .wrapper import CacheControl +from .adapter import CacheControlAdapter +from .controller import CacheController diff --git a/venv2/lib/python2.7/site-packages/pip/_vendor/cachecontrol/_cmd.py b/venv2/lib/python2.7/site-packages/pip/_vendor/cachecontrol/_cmd.py new file mode 100644 index 0000000..f1e0ad9 --- /dev/null +++ b/venv2/lib/python2.7/site-packages/pip/_vendor/cachecontrol/_cmd.py @@ -0,0 +1,57 @@ +import logging + +from pip._vendor import requests + +from pip._vendor.cachecontrol.adapter import CacheControlAdapter +from pip._vendor.cachecontrol.cache import DictCache +from pip._vendor.cachecontrol.controller import logger + +from argparse import ArgumentParser + + +def setup_logging(): + logger.setLevel(logging.DEBUG) + handler = logging.StreamHandler() + logger.addHandler(handler) + + +def get_session(): + adapter = CacheControlAdapter( + DictCache(), cache_etags=True, serializer=None, heuristic=None + ) + sess = requests.Session() + sess.mount("http://", adapter) + sess.mount("https://", adapter) + + sess.cache_controller = adapter.controller + return sess + + +def get_args(): + parser = ArgumentParser() + parser.add_argument("url", help="The URL to try and cache") + return parser.parse_args() + + +def main(args=None): + args = get_args() + sess = get_session() + + # Make a request to get a response + resp = sess.get(args.url) + + # Turn on logging + setup_logging() + + # try setting the cache + sess.cache_controller.cache_response(resp.request, resp.raw) + + # Now try to get it + if sess.cache_controller.cached_request(resp.request): + print("Cached!") + else: + print("Not cached :(") + + +if __name__ == "__main__": + main() diff --git a/venv2/lib/python2.7/site-packages/pip/_vendor/cachecontrol/adapter.py b/venv2/lib/python2.7/site-packages/pip/_vendor/cachecontrol/adapter.py new file mode 100644 index 0000000..780eb28 --- /dev/null +++ b/venv2/lib/python2.7/site-packages/pip/_vendor/cachecontrol/adapter.py @@ -0,0 +1,133 @@ +import types +import functools +import zlib + +from pip._vendor.requests.adapters import HTTPAdapter + +from .controller import CacheController +from .cache import DictCache +from .filewrapper import CallbackFileWrapper + + +class CacheControlAdapter(HTTPAdapter): + invalidating_methods = {"PUT", "DELETE"} + + def __init__( + self, + cache=None, + cache_etags=True, + controller_class=None, + serializer=None, + heuristic=None, + cacheable_methods=None, + *args, + **kw + ): + super(CacheControlAdapter, self).__init__(*args, **kw) + self.cache = cache or DictCache() + self.heuristic = heuristic + self.cacheable_methods = cacheable_methods or ("GET",) + + controller_factory = controller_class or CacheController + self.controller = controller_factory( + self.cache, cache_etags=cache_etags, serializer=serializer + ) + + def send(self, request, cacheable_methods=None, **kw): + """ + Send a request. Use the request information to see if it + exists in the cache and cache the response if we need to and can. + """ + cacheable = cacheable_methods or self.cacheable_methods + if request.method in cacheable: + try: + cached_response = self.controller.cached_request(request) + except zlib.error: + cached_response = None + if cached_response: + return self.build_response(request, cached_response, from_cache=True) + + # check for etags and add headers if appropriate + request.headers.update(self.controller.conditional_headers(request)) + + resp = super(CacheControlAdapter, self).send(request, **kw) + + return resp + + def build_response( + self, request, response, from_cache=False, cacheable_methods=None + ): + """ + Build a response by making a request or using the cache. + + This will end up calling send and returning a potentially + cached response + """ + cacheable = cacheable_methods or self.cacheable_methods + if not from_cache and request.method in cacheable: + # Check for any heuristics that might update headers + # before trying to cache. + if self.heuristic: + response = self.heuristic.apply(response) + + # apply any expiration heuristics + if response.status == 304: + # We must have sent an ETag request. This could mean + # that we've been expired already or that we simply + # have an etag. In either case, we want to try and + # update the cache if that is the case. + cached_response = self.controller.update_cached_response( + request, response + ) + + if cached_response is not response: + from_cache = True + + # We are done with the server response, read a + # possible response body (compliant servers will + # not return one, but we cannot be 100% sure) and + # release the connection back to the pool. + response.read(decode_content=False) + response.release_conn() + + response = cached_response + + # We always cache the 301 responses + elif response.status == 301: + self.controller.cache_response(request, response) + else: + # Wrap the response file with a wrapper that will cache the + # response when the stream has been consumed. + response._fp = CallbackFileWrapper( + response._fp, + functools.partial( + self.controller.cache_response, request, response + ), + ) + if response.chunked: + super_update_chunk_length = response._update_chunk_length + + def _update_chunk_length(self): + super_update_chunk_length() + if self.chunk_left == 0: + self._fp._close() + + response._update_chunk_length = types.MethodType( + _update_chunk_length, response + ) + + resp = super(CacheControlAdapter, self).build_response(request, response) + + # See if we should invalidate the cache. + if request.method in self.invalidating_methods and resp.ok: + cache_url = self.controller.cache_url(request.url) + self.cache.delete(cache_url) + + # Give the request a from_cache attr to let people use it + resp.from_cache = from_cache + + return resp + + def close(self): + self.cache.close() + super(CacheControlAdapter, self).close() diff --git a/venv2/lib/python2.7/site-packages/pip/_vendor/cachecontrol/cache.py b/venv2/lib/python2.7/site-packages/pip/_vendor/cachecontrol/cache.py new file mode 100644 index 0000000..94e0773 --- /dev/null +++ b/venv2/lib/python2.7/site-packages/pip/_vendor/cachecontrol/cache.py @@ -0,0 +1,39 @@ +""" +The cache object API for implementing caches. The default is a thread +safe in-memory dictionary. +""" +from threading import Lock + + +class BaseCache(object): + + def get(self, key): + raise NotImplementedError() + + def set(self, key, value): + raise NotImplementedError() + + def delete(self, key): + raise NotImplementedError() + + def close(self): + pass + + +class DictCache(BaseCache): + + def __init__(self, init_dict=None): + self.lock = Lock() + self.data = init_dict or {} + + def get(self, key): + return self.data.get(key, None) + + def set(self, key, value): + with self.lock: + self.data.update({key: value}) + + def delete(self, key): + with self.lock: + if key in self.data: + self.data.pop(key) diff --git a/venv2/lib/python2.7/site-packages/pip/_vendor/cachecontrol/caches/__init__.py b/venv2/lib/python2.7/site-packages/pip/_vendor/cachecontrol/caches/__init__.py new file mode 100644 index 0000000..0e1658f --- /dev/null +++ b/venv2/lib/python2.7/site-packages/pip/_vendor/cachecontrol/caches/__init__.py @@ -0,0 +1,2 @@ +from .file_cache import FileCache # noqa +from .redis_cache import RedisCache # noqa diff --git a/venv2/lib/python2.7/site-packages/pip/_vendor/cachecontrol/caches/file_cache.py b/venv2/lib/python2.7/site-packages/pip/_vendor/cachecontrol/caches/file_cache.py new file mode 100644 index 0000000..1ba0080 --- /dev/null +++ b/venv2/lib/python2.7/site-packages/pip/_vendor/cachecontrol/caches/file_cache.py @@ -0,0 +1,146 @@ +import hashlib +import os +from textwrap import dedent + +from ..cache import BaseCache +from ..controller import CacheController + +try: + FileNotFoundError +except NameError: + # py2.X + FileNotFoundError = (IOError, OSError) + + +def _secure_open_write(filename, fmode): + # We only want to write to this file, so open it in write only mode + flags = os.O_WRONLY + + # os.O_CREAT | os.O_EXCL will fail if the file already exists, so we only + # will open *new* files. + # We specify this because we want to ensure that the mode we pass is the + # mode of the file. + flags |= os.O_CREAT | os.O_EXCL + + # Do not follow symlinks to prevent someone from making a symlink that + # we follow and insecurely open a cache file. + if hasattr(os, "O_NOFOLLOW"): + flags |= os.O_NOFOLLOW + + # On Windows we'll mark this file as binary + if hasattr(os, "O_BINARY"): + flags |= os.O_BINARY + + # Before we open our file, we want to delete any existing file that is + # there + try: + os.remove(filename) + except (IOError, OSError): + # The file must not exist already, so we can just skip ahead to opening + pass + + # Open our file, the use of os.O_CREAT | os.O_EXCL will ensure that if a + # race condition happens between the os.remove and this line, that an + # error will be raised. Because we utilize a lockfile this should only + # happen if someone is attempting to attack us. + fd = os.open(filename, flags, fmode) + try: + return os.fdopen(fd, "wb") + + except: + # An error occurred wrapping our FD in a file object + os.close(fd) + raise + + +class FileCache(BaseCache): + + def __init__( + self, + directory, + forever=False, + filemode=0o0600, + dirmode=0o0700, + use_dir_lock=None, + lock_class=None, + ): + + if use_dir_lock is not None and lock_class is not None: + raise ValueError("Cannot use use_dir_lock and lock_class together") + + try: + from pip._vendor.lockfile import LockFile + from pip._vendor.lockfile.mkdirlockfile import MkdirLockFile + except ImportError: + notice = dedent( + """ + NOTE: In order to use the FileCache you must have + lockfile installed. You can install it via pip: + pip install lockfile + """ + ) + raise ImportError(notice) + + else: + if use_dir_lock: + lock_class = MkdirLockFile + + elif lock_class is None: + lock_class = LockFile + + self.directory = directory + self.forever = forever + self.filemode = filemode + self.dirmode = dirmode + self.lock_class = lock_class + + @staticmethod + def encode(x): + return hashlib.sha224(x.encode()).hexdigest() + + def _fn(self, name): + # NOTE: This method should not change as some may depend on it. + # See: https://github.com/ionrock/cachecontrol/issues/63 + hashed = self.encode(name) + parts = list(hashed[:5]) + [hashed] + return os.path.join(self.directory, *parts) + + def get(self, key): + name = self._fn(key) + try: + with open(name, "rb") as fh: + return fh.read() + + except FileNotFoundError: + return None + + def set(self, key, value): + name = self._fn(key) + + # Make sure the directory exists + try: + os.makedirs(os.path.dirname(name), self.dirmode) + except (IOError, OSError): + pass + + with self.lock_class(name) as lock: + # Write our actual file + with _secure_open_write(lock.path, self.filemode) as fh: + fh.write(value) + + def delete(self, key): + name = self._fn(key) + if not self.forever: + try: + os.remove(name) + except FileNotFoundError: + pass + + +def url_to_file_path(url, filecache): + """Return the file cache path based on the URL. + + This does not ensure the file exists! + """ + key = CacheController.cache_url(url) + return filecache._fn(key) diff --git a/venv2/lib/python2.7/site-packages/pip/_vendor/cachecontrol/caches/redis_cache.py b/venv2/lib/python2.7/site-packages/pip/_vendor/cachecontrol/caches/redis_cache.py new file mode 100644 index 0000000..ed705ce --- /dev/null +++ b/venv2/lib/python2.7/site-packages/pip/_vendor/cachecontrol/caches/redis_cache.py @@ -0,0 +1,33 @@ +from __future__ import division + +from datetime import datetime +from pip._vendor.cachecontrol.cache import BaseCache + + +class RedisCache(BaseCache): + + def __init__(self, conn): + self.conn = conn + + def get(self, key): + return self.conn.get(key) + + def set(self, key, value, expires=None): + if not expires: + self.conn.set(key, value) + else: + expires = expires - datetime.utcnow() + self.conn.setex(key, int(expires.total_seconds()), value) + + def delete(self, key): + self.conn.delete(key) + + def clear(self): + """Helper for clearing all the keys in a database. Use with + caution!""" + for key in self.conn.keys(): + self.conn.delete(key) + + def close(self): + """Redis uses connection pooling, no need to close the connection.""" + pass diff --git a/venv2/lib/python2.7/site-packages/pip/_vendor/cachecontrol/compat.py b/venv2/lib/python2.7/site-packages/pip/_vendor/cachecontrol/compat.py new file mode 100644 index 0000000..33b5aed --- /dev/null +++ b/venv2/lib/python2.7/site-packages/pip/_vendor/cachecontrol/compat.py @@ -0,0 +1,29 @@ +try: + from urllib.parse import urljoin +except ImportError: + from urlparse import urljoin + + +try: + import cPickle as pickle +except ImportError: + import pickle + + +# Handle the case where the requests module has been patched to not have +# urllib3 bundled as part of its source. +try: + from pip._vendor.requests.packages.urllib3.response import HTTPResponse +except ImportError: + from pip._vendor.urllib3.response import HTTPResponse + +try: + from pip._vendor.requests.packages.urllib3.util import is_fp_closed +except ImportError: + from pip._vendor.urllib3.util import is_fp_closed + +# Replicate some six behaviour +try: + text_type = unicode +except NameError: + text_type = str diff --git a/venv2/lib/python2.7/site-packages/pip/_vendor/cachecontrol/controller.py b/venv2/lib/python2.7/site-packages/pip/_vendor/cachecontrol/controller.py new file mode 100644 index 0000000..1b2b943 --- /dev/null +++ b/venv2/lib/python2.7/site-packages/pip/_vendor/cachecontrol/controller.py @@ -0,0 +1,367 @@ +""" +The httplib2 algorithms ported for use with requests. +""" +import logging +import re +import calendar +import time +from email.utils import parsedate_tz + +from pip._vendor.requests.structures import CaseInsensitiveDict + +from .cache import DictCache +from .serialize import Serializer + + +logger = logging.getLogger(__name__) + +URI = re.compile(r"^(([^:/?#]+):)?(//([^/?#]*))?([^?#]*)(\?([^#]*))?(#(.*))?") + + +def parse_uri(uri): + """Parses a URI using the regex given in Appendix B of RFC 3986. + + (scheme, authority, path, query, fragment) = parse_uri(uri) + """ + groups = URI.match(uri).groups() + return (groups[1], groups[3], groups[4], groups[6], groups[8]) + + +class CacheController(object): + """An interface to see if request should cached or not. + """ + + def __init__( + self, cache=None, cache_etags=True, serializer=None, status_codes=None + ): + self.cache = cache or DictCache() + self.cache_etags = cache_etags + self.serializer = serializer or Serializer() + self.cacheable_status_codes = status_codes or (200, 203, 300, 301) + + @classmethod + def _urlnorm(cls, uri): + """Normalize the URL to create a safe key for the cache""" + (scheme, authority, path, query, fragment) = parse_uri(uri) + if not scheme or not authority: + raise Exception("Only absolute URIs are allowed. uri = %s" % uri) + + scheme = scheme.lower() + authority = authority.lower() + + if not path: + path = "/" + + # Could do syntax based normalization of the URI before + # computing the digest. See Section 6.2.2 of Std 66. + request_uri = query and "?".join([path, query]) or path + defrag_uri = scheme + "://" + authority + request_uri + + return defrag_uri + + @classmethod + def cache_url(cls, uri): + return cls._urlnorm(uri) + + def parse_cache_control(self, headers): + known_directives = { + # https://tools.ietf.org/html/rfc7234#section-5.2 + "max-age": (int, True), + "max-stale": (int, False), + "min-fresh": (int, True), + "no-cache": (None, False), + "no-store": (None, False), + "no-transform": (None, False), + "only-if-cached": (None, False), + "must-revalidate": (None, False), + "public": (None, False), + "private": (None, False), + "proxy-revalidate": (None, False), + "s-maxage": (int, True), + } + + cc_headers = headers.get("cache-control", headers.get("Cache-Control", "")) + + retval = {} + + for cc_directive in cc_headers.split(","): + if not cc_directive.strip(): + continue + + parts = cc_directive.split("=", 1) + directive = parts[0].strip() + + try: + typ, required = known_directives[directive] + except KeyError: + logger.debug("Ignoring unknown cache-control directive: %s", directive) + continue + + if not typ or not required: + retval[directive] = None + if typ: + try: + retval[directive] = typ(parts[1].strip()) + except IndexError: + if required: + logger.debug( + "Missing value for cache-control " "directive: %s", + directive, + ) + except ValueError: + logger.debug( + "Invalid value for cache-control directive " "%s, must be %s", + directive, + typ.__name__, + ) + + return retval + + def cached_request(self, request): + """ + Return a cached response if it exists in the cache, otherwise + return False. + """ + cache_url = self.cache_url(request.url) + logger.debug('Looking up "%s" in the cache', cache_url) + cc = self.parse_cache_control(request.headers) + + # Bail out if the request insists on fresh data + if "no-cache" in cc: + logger.debug('Request header has "no-cache", cache bypassed') + return False + + if "max-age" in cc and cc["max-age"] == 0: + logger.debug('Request header has "max_age" as 0, cache bypassed') + return False + + # Request allows serving from the cache, let's see if we find something + cache_data = self.cache.get(cache_url) + if cache_data is None: + logger.debug("No cache entry available") + return False + + # Check whether it can be deserialized + resp = self.serializer.loads(request, cache_data) + if not resp: + logger.warning("Cache entry deserialization failed, entry ignored") + return False + + # If we have a cached 301, return it immediately. We don't + # need to test our response for other headers b/c it is + # intrinsically "cacheable" as it is Permanent. + # See: + # https://tools.ietf.org/html/rfc7231#section-6.4.2 + # + # Client can try to refresh the value by repeating the request + # with cache busting headers as usual (ie no-cache). + if resp.status == 301: + msg = ( + 'Returning cached "301 Moved Permanently" response ' + "(ignoring date and etag information)" + ) + logger.debug(msg) + return resp + + headers = CaseInsensitiveDict(resp.headers) + if not headers or "date" not in headers: + if "etag" not in headers: + # Without date or etag, the cached response can never be used + # and should be deleted. + logger.debug("Purging cached response: no date or etag") + self.cache.delete(cache_url) + logger.debug("Ignoring cached response: no date") + return False + + now = time.time() + date = calendar.timegm(parsedate_tz(headers["date"])) + current_age = max(0, now - date) + logger.debug("Current age based on date: %i", current_age) + + # TODO: There is an assumption that the result will be a + # urllib3 response object. This may not be best since we + # could probably avoid instantiating or constructing the + # response until we know we need it. + resp_cc = self.parse_cache_control(headers) + + # determine freshness + freshness_lifetime = 0 + + # Check the max-age pragma in the cache control header + if "max-age" in resp_cc: + freshness_lifetime = resp_cc["max-age"] + logger.debug("Freshness lifetime from max-age: %i", freshness_lifetime) + + # If there isn't a max-age, check for an expires header + elif "expires" in headers: + expires = parsedate_tz(headers["expires"]) + if expires is not None: + expire_time = calendar.timegm(expires) - date + freshness_lifetime = max(0, expire_time) + logger.debug("Freshness lifetime from expires: %i", freshness_lifetime) + + # Determine if we are setting freshness limit in the + # request. Note, this overrides what was in the response. + if "max-age" in cc: + freshness_lifetime = cc["max-age"] + logger.debug( + "Freshness lifetime from request max-age: %i", freshness_lifetime + ) + + if "min-fresh" in cc: + min_fresh = cc["min-fresh"] + # adjust our current age by our min fresh + current_age += min_fresh + logger.debug("Adjusted current age from min-fresh: %i", current_age) + + # Return entry if it is fresh enough + if freshness_lifetime > current_age: + logger.debug('The response is "fresh", returning cached response') + logger.debug("%i > %i", freshness_lifetime, current_age) + return resp + + # we're not fresh. If we don't have an Etag, clear it out + if "etag" not in headers: + logger.debug('The cached response is "stale" with no etag, purging') + self.cache.delete(cache_url) + + # return the original handler + return False + + def conditional_headers(self, request): + cache_url = self.cache_url(request.url) + resp = self.serializer.loads(request, self.cache.get(cache_url)) + new_headers = {} + + if resp: + headers = CaseInsensitiveDict(resp.headers) + + if "etag" in headers: + new_headers["If-None-Match"] = headers["ETag"] + + if "last-modified" in headers: + new_headers["If-Modified-Since"] = headers["Last-Modified"] + + return new_headers + + def cache_response(self, request, response, body=None, status_codes=None): + """ + Algorithm for caching requests. + + This assumes a requests Response object. + """ + # From httplib2: Don't cache 206's since we aren't going to + # handle byte range requests + cacheable_status_codes = status_codes or self.cacheable_status_codes + if response.status not in cacheable_status_codes: + logger.debug( + "Status code %s not in %s", response.status, cacheable_status_codes + ) + return + + response_headers = CaseInsensitiveDict(response.headers) + + # If we've been given a body, our response has a Content-Length, that + # Content-Length is valid then we can check to see if the body we've + # been given matches the expected size, and if it doesn't we'll just + # skip trying to cache it. + if ( + body is not None + and "content-length" in response_headers + and response_headers["content-length"].isdigit() + and int(response_headers["content-length"]) != len(body) + ): + return + + cc_req = self.parse_cache_control(request.headers) + cc = self.parse_cache_control(response_headers) + + cache_url = self.cache_url(request.url) + logger.debug('Updating cache with response from "%s"', cache_url) + + # Delete it from the cache if we happen to have it stored there + no_store = False + if "no-store" in cc: + no_store = True + logger.debug('Response header has "no-store"') + if "no-store" in cc_req: + no_store = True + logger.debug('Request header has "no-store"') + if no_store and self.cache.get(cache_url): + logger.debug('Purging existing cache entry to honor "no-store"') + self.cache.delete(cache_url) + if no_store: + return + + # If we've been given an etag, then keep the response + if self.cache_etags and "etag" in response_headers: + logger.debug("Caching due to etag") + self.cache.set( + cache_url, self.serializer.dumps(request, response, body=body) + ) + + # Add to the cache any 301s. We do this before looking that + # the Date headers. + elif response.status == 301: + logger.debug("Caching permanant redirect") + self.cache.set(cache_url, self.serializer.dumps(request, response)) + + # Add to the cache if the response headers demand it. If there + # is no date header then we can't do anything about expiring + # the cache. + elif "date" in response_headers: + # cache when there is a max-age > 0 + if "max-age" in cc and cc["max-age"] > 0: + logger.debug("Caching b/c date exists and max-age > 0") + self.cache.set( + cache_url, self.serializer.dumps(request, response, body=body) + ) + + # If the request can expire, it means we should cache it + # in the meantime. + elif "expires" in response_headers: + if response_headers["expires"]: + logger.debug("Caching b/c of expires header") + self.cache.set( + cache_url, self.serializer.dumps(request, response, body=body) + ) + + def update_cached_response(self, request, response): + """On a 304 we will get a new set of headers that we want to + update our cached value with, assuming we have one. + + This should only ever be called when we've sent an ETag and + gotten a 304 as the response. + """ + cache_url = self.cache_url(request.url) + + cached_response = self.serializer.loads(request, self.cache.get(cache_url)) + + if not cached_response: + # we didn't have a cached response + return response + + # Lets update our headers with the headers from the new request: + # http://tools.ietf.org/html/draft-ietf-httpbis-p4-conditional-26#section-4.1 + # + # The server isn't supposed to send headers that would make + # the cached body invalid. But... just in case, we'll be sure + # to strip out ones we know that might be problmatic due to + # typical assumptions. + excluded_headers = ["content-length"] + + cached_response.headers.update( + dict( + (k, v) + for k, v in response.headers.items() + if k.lower() not in excluded_headers + ) + ) + + # we want a 200 b/c we have content via the cache + cached_response.status = 200 + + # update our cache + self.cache.set(cache_url, self.serializer.dumps(request, cached_response)) + + return cached_response diff --git a/venv2/lib/python2.7/site-packages/pip/_vendor/cachecontrol/filewrapper.py b/venv2/lib/python2.7/site-packages/pip/_vendor/cachecontrol/filewrapper.py new file mode 100644 index 0000000..30ed4c5 --- /dev/null +++ b/venv2/lib/python2.7/site-packages/pip/_vendor/cachecontrol/filewrapper.py @@ -0,0 +1,80 @@ +from io import BytesIO + + +class CallbackFileWrapper(object): + """ + Small wrapper around a fp object which will tee everything read into a + buffer, and when that file is closed it will execute a callback with the + contents of that buffer. + + All attributes are proxied to the underlying file object. + + This class uses members with a double underscore (__) leading prefix so as + not to accidentally shadow an attribute. + """ + + def __init__(self, fp, callback): + self.__buf = BytesIO() + self.__fp = fp + self.__callback = callback + + def __getattr__(self, name): + # The vaguaries of garbage collection means that self.__fp is + # not always set. By using __getattribute__ and the private + # name[0] allows looking up the attribute value and raising an + # AttributeError when it doesn't exist. This stop thigns from + # infinitely recursing calls to getattr in the case where + # self.__fp hasn't been set. + # + # [0] https://docs.python.org/2/reference/expressions.html#atom-identifiers + fp = self.__getattribute__("_CallbackFileWrapper__fp") + return getattr(fp, name) + + def __is_fp_closed(self): + try: + return self.__fp.fp is None + + except AttributeError: + pass + + try: + return self.__fp.closed + + except AttributeError: + pass + + # We just don't cache it then. + # TODO: Add some logging here... + return False + + def _close(self): + if self.__callback: + self.__callback(self.__buf.getvalue()) + + # We assign this to None here, because otherwise we can get into + # really tricky problems where the CPython interpreter dead locks + # because the callback is holding a reference to something which + # has a __del__ method. Setting this to None breaks the cycle + # and allows the garbage collector to do it's thing normally. + self.__callback = None + + def read(self, amt=None): + data = self.__fp.read(amt) + self.__buf.write(data) + if self.__is_fp_closed(): + self._close() + + return data + + def _safe_read(self, amt): + data = self.__fp._safe_read(amt) + if amt == 2 and data == b"\r\n": + # urllib executes this read to toss the CRLF at the end + # of the chunk. + return data + + self.__buf.write(data) + if self.__is_fp_closed(): + self._close() + + return data diff --git a/venv2/lib/python2.7/site-packages/pip/_vendor/cachecontrol/heuristics.py b/venv2/lib/python2.7/site-packages/pip/_vendor/cachecontrol/heuristics.py new file mode 100644 index 0000000..6c0e979 --- /dev/null +++ b/venv2/lib/python2.7/site-packages/pip/_vendor/cachecontrol/heuristics.py @@ -0,0 +1,135 @@ +import calendar +import time + +from email.utils import formatdate, parsedate, parsedate_tz + +from datetime import datetime, timedelta + +TIME_FMT = "%a, %d %b %Y %H:%M:%S GMT" + + +def expire_after(delta, date=None): + date = date or datetime.utcnow() + return date + delta + + +def datetime_to_header(dt): + return formatdate(calendar.timegm(dt.timetuple())) + + +class BaseHeuristic(object): + + def warning(self, response): + """ + Return a valid 1xx warning header value describing the cache + adjustments. + + The response is provided too allow warnings like 113 + http://tools.ietf.org/html/rfc7234#section-5.5.4 where we need + to explicitly say response is over 24 hours old. + """ + return '110 - "Response is Stale"' + + def update_headers(self, response): + """Update the response headers with any new headers. + + NOTE: This SHOULD always include some Warning header to + signify that the response was cached by the client, not + by way of the provided headers. + """ + return {} + + def apply(self, response): + updated_headers = self.update_headers(response) + + if updated_headers: + response.headers.update(updated_headers) + warning_header_value = self.warning(response) + if warning_header_value is not None: + response.headers.update({"Warning": warning_header_value}) + + return response + + +class OneDayCache(BaseHeuristic): + """ + Cache the response by providing an expires 1 day in the + future. + """ + + def update_headers(self, response): + headers = {} + + if "expires" not in response.headers: + date = parsedate(response.headers["date"]) + expires = expire_after(timedelta(days=1), date=datetime(*date[:6])) + headers["expires"] = datetime_to_header(expires) + headers["cache-control"] = "public" + return headers + + +class ExpiresAfter(BaseHeuristic): + """ + Cache **all** requests for a defined time period. + """ + + def __init__(self, **kw): + self.delta = timedelta(**kw) + + def update_headers(self, response): + expires = expire_after(self.delta) + return {"expires": datetime_to_header(expires), "cache-control": "public"} + + def warning(self, response): + tmpl = "110 - Automatically cached for %s. Response might be stale" + return tmpl % self.delta + + +class LastModified(BaseHeuristic): + """ + If there is no Expires header already, fall back on Last-Modified + using the heuristic from + http://tools.ietf.org/html/rfc7234#section-4.2.2 + to calculate a reasonable value. + + Firefox also does something like this per + https://developer.mozilla.org/en-US/docs/Web/HTTP/Caching_FAQ + http://lxr.mozilla.org/mozilla-release/source/netwerk/protocol/http/nsHttpResponseHead.cpp#397 + Unlike mozilla we limit this to 24-hr. + """ + cacheable_by_default_statuses = { + 200, 203, 204, 206, 300, 301, 404, 405, 410, 414, 501 + } + + def update_headers(self, resp): + headers = resp.headers + + if "expires" in headers: + return {} + + if "cache-control" in headers and headers["cache-control"] != "public": + return {} + + if resp.status not in self.cacheable_by_default_statuses: + return {} + + if "date" not in headers or "last-modified" not in headers: + return {} + + date = calendar.timegm(parsedate_tz(headers["date"])) + last_modified = parsedate(headers["last-modified"]) + if date is None or last_modified is None: + return {} + + now = time.time() + current_age = max(0, now - date) + delta = date - calendar.timegm(last_modified) + freshness_lifetime = max(0, min(delta / 10, 24 * 3600)) + if freshness_lifetime <= current_age: + return {} + + expires = date + freshness_lifetime + return {"expires": time.strftime(TIME_FMT, time.gmtime(expires))} + + def warning(self, resp): + return None diff --git a/venv2/lib/python2.7/site-packages/pip/_vendor/cachecontrol/serialize.py b/venv2/lib/python2.7/site-packages/pip/_vendor/cachecontrol/serialize.py new file mode 100644 index 0000000..ec43ff2 --- /dev/null +++ b/venv2/lib/python2.7/site-packages/pip/_vendor/cachecontrol/serialize.py @@ -0,0 +1,186 @@ +import base64 +import io +import json +import zlib + +from pip._vendor import msgpack +from pip._vendor.requests.structures import CaseInsensitiveDict + +from .compat import HTTPResponse, pickle, text_type + + +def _b64_decode_bytes(b): + return base64.b64decode(b.encode("ascii")) + + +def _b64_decode_str(s): + return _b64_decode_bytes(s).decode("utf8") + + +class Serializer(object): + + def dumps(self, request, response, body=None): + response_headers = CaseInsensitiveDict(response.headers) + + if body is None: + body = response.read(decode_content=False) + + # NOTE: 99% sure this is dead code. I'm only leaving it + # here b/c I don't have a test yet to prove + # it. Basically, before using + # `cachecontrol.filewrapper.CallbackFileWrapper`, + # this made an effort to reset the file handle. The + # `CallbackFileWrapper` short circuits this code by + # setting the body as the content is consumed, the + # result being a `body` argument is *always* passed + # into cache_response, and in turn, + # `Serializer.dump`. + response._fp = io.BytesIO(body) + + # NOTE: This is all a bit weird, but it's really important that on + # Python 2.x these objects are unicode and not str, even when + # they contain only ascii. The problem here is that msgpack + # understands the difference between unicode and bytes and we + # have it set to differentiate between them, however Python 2 + # doesn't know the difference. Forcing these to unicode will be + # enough to have msgpack know the difference. + data = { + u"response": { + u"body": body, + u"headers": dict( + (text_type(k), text_type(v)) for k, v in response.headers.items() + ), + u"status": response.status, + u"version": response.version, + u"reason": text_type(response.reason), + u"strict": response.strict, + u"decode_content": response.decode_content, + } + } + + # Construct our vary headers + data[u"vary"] = {} + if u"vary" in response_headers: + varied_headers = response_headers[u"vary"].split(",") + for header in varied_headers: + header = text_type(header).strip() + header_value = request.headers.get(header, None) + if header_value is not None: + header_value = text_type(header_value) + data[u"vary"][header] = header_value + + return b",".join([b"cc=4", msgpack.dumps(data, use_bin_type=True)]) + + def loads(self, request, data): + # Short circuit if we've been given an empty set of data + if not data: + return + + # Determine what version of the serializer the data was serialized + # with + try: + ver, data = data.split(b",", 1) + except ValueError: + ver = b"cc=0" + + # Make sure that our "ver" is actually a version and isn't a false + # positive from a , being in the data stream. + if ver[:3] != b"cc=": + data = ver + data + ver = b"cc=0" + + # Get the version number out of the cc=N + ver = ver.split(b"=", 1)[-1].decode("ascii") + + # Dispatch to the actual load method for the given version + try: + return getattr(self, "_loads_v{}".format(ver))(request, data) + + except AttributeError: + # This is a version we don't have a loads function for, so we'll + # just treat it as a miss and return None + return + + def prepare_response(self, request, cached): + """Verify our vary headers match and construct a real urllib3 + HTTPResponse object. + """ + # Special case the '*' Vary value as it means we cannot actually + # determine if the cached response is suitable for this request. + if "*" in cached.get("vary", {}): + return + + # Ensure that the Vary headers for the cached response match our + # request + for header, value in cached.get("vary", {}).items(): + if request.headers.get(header, None) != value: + return + + body_raw = cached["response"].pop("body") + + headers = CaseInsensitiveDict(data=cached["response"]["headers"]) + if headers.get("transfer-encoding", "") == "chunked": + headers.pop("transfer-encoding") + + cached["response"]["headers"] = headers + + try: + body = io.BytesIO(body_raw) + except TypeError: + # This can happen if cachecontrol serialized to v1 format (pickle) + # using Python 2. A Python 2 str(byte string) will be unpickled as + # a Python 3 str (unicode string), which will cause the above to + # fail with: + # + # TypeError: 'str' does not support the buffer interface + body = io.BytesIO(body_raw.encode("utf8")) + + return HTTPResponse(body=body, preload_content=False, **cached["response"]) + + def _loads_v0(self, request, data): + # The original legacy cache data. This doesn't contain enough + # information to construct everything we need, so we'll treat this as + # a miss. + return + + def _loads_v1(self, request, data): + try: + cached = pickle.loads(data) + except ValueError: + return + + return self.prepare_response(request, cached) + + def _loads_v2(self, request, data): + try: + cached = json.loads(zlib.decompress(data).decode("utf8")) + except (ValueError, zlib.error): + return + + # We need to decode the items that we've base64 encoded + cached["response"]["body"] = _b64_decode_bytes(cached["response"]["body"]) + cached["response"]["headers"] = dict( + (_b64_decode_str(k), _b64_decode_str(v)) + for k, v in cached["response"]["headers"].items() + ) + cached["response"]["reason"] = _b64_decode_str(cached["response"]["reason"]) + cached["vary"] = dict( + (_b64_decode_str(k), _b64_decode_str(v) if v is not None else v) + for k, v in cached["vary"].items() + ) + + return self.prepare_response(request, cached) + + def _loads_v3(self, request, data): + # Due to Python 2 encoding issues, it's impossible to know for sure + # exactly how to load v3 entries, thus we'll treat these as a miss so + # that they get rewritten out as v4 entries. + return + + def _loads_v4(self, request, data): + try: + cached = msgpack.loads(data, encoding="utf-8") + except ValueError: + return + + return self.prepare_response(request, cached) diff --git a/venv2/lib/python2.7/site-packages/pip/_vendor/cachecontrol/wrapper.py b/venv2/lib/python2.7/site-packages/pip/_vendor/cachecontrol/wrapper.py new file mode 100644 index 0000000..265bfc8 --- /dev/null +++ b/venv2/lib/python2.7/site-packages/pip/_vendor/cachecontrol/wrapper.py @@ -0,0 +1,29 @@ +from .adapter import CacheControlAdapter +from .cache import DictCache + + +def CacheControl( + sess, + cache=None, + cache_etags=True, + serializer=None, + heuristic=None, + controller_class=None, + adapter_class=None, + cacheable_methods=None, +): + + cache = cache or DictCache() + adapter_class = adapter_class or CacheControlAdapter + adapter = adapter_class( + cache, + cache_etags=cache_etags, + serializer=serializer, + heuristic=heuristic, + controller_class=controller_class, + cacheable_methods=cacheable_methods, + ) + sess.mount("http://", adapter) + sess.mount("https://", adapter) + + return sess diff --git a/venv2/lib/python2.7/site-packages/pip/_vendor/certifi/__init__.py b/venv2/lib/python2.7/site-packages/pip/_vendor/certifi/__init__.py new file mode 100644 index 0000000..632db8e --- /dev/null +++ b/venv2/lib/python2.7/site-packages/pip/_vendor/certifi/__init__.py @@ -0,0 +1,3 @@ +from .core import where + +__version__ = "2019.03.09" diff --git a/venv2/lib/python2.7/site-packages/pip/_vendor/certifi/__main__.py b/venv2/lib/python2.7/site-packages/pip/_vendor/certifi/__main__.py new file mode 100644 index 0000000..ae2aff5 --- /dev/null +++ b/venv2/lib/python2.7/site-packages/pip/_vendor/certifi/__main__.py @@ -0,0 +1,2 @@ +from pip._vendor.certifi import where +print(where()) diff --git a/venv2/lib/python2.7/site-packages/pip/_vendor/certifi/cacert.pem b/venv2/lib/python2.7/site-packages/pip/_vendor/certifi/cacert.pem new file mode 100644 index 0000000..84636dd --- /dev/null +++ b/venv2/lib/python2.7/site-packages/pip/_vendor/certifi/cacert.pem @@ -0,0 +1,4658 @@ + +# Issuer: CN=GlobalSign Root CA O=GlobalSign nv-sa OU=Root CA +# Subject: CN=GlobalSign Root CA O=GlobalSign nv-sa OU=Root CA +# Label: "GlobalSign Root CA" +# Serial: 4835703278459707669005204 +# MD5 Fingerprint: 3e:45:52:15:09:51:92:e1:b7:5d:37:9f:b1:87:29:8a +# SHA1 Fingerprint: b1:bc:96:8b:d4:f4:9d:62:2a:a8:9a:81:f2:15:01:52:a4:1d:82:9c +# SHA256 Fingerprint: eb:d4:10:40:e4:bb:3e:c7:42:c9:e3:81:d3:1e:f2:a4:1a:48:b6:68:5c:96:e7:ce:f3:c1:df:6c:d4:33:1c:99 +-----BEGIN CERTIFICATE----- +MIIDdTCCAl2gAwIBAgILBAAAAAABFUtaw5QwDQYJKoZIhvcNAQEFBQAwVzELMAkG +A1UEBhMCQkUxGTAXBgNVBAoTEEdsb2JhbFNpZ24gbnYtc2ExEDAOBgNVBAsTB1Jv +b3QgQ0ExGzAZBgNVBAMTEkdsb2JhbFNpZ24gUm9vdCBDQTAeFw05ODA5MDExMjAw +MDBaFw0yODAxMjgxMjAwMDBaMFcxCzAJBgNVBAYTAkJFMRkwFwYDVQQKExBHbG9i +YWxTaWduIG52LXNhMRAwDgYDVQQLEwdSb290IENBMRswGQYDVQQDExJHbG9iYWxT +aWduIFJvb3QgQ0EwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDaDuaZ +jc6j40+Kfvvxi4Mla+pIH/EqsLmVEQS98GPR4mdmzxzdzxtIK+6NiY6arymAZavp +xy0Sy6scTHAHoT0KMM0VjU/43dSMUBUc71DuxC73/OlS8pF94G3VNTCOXkNz8kHp +1Wrjsok6Vjk4bwY8iGlbKk3Fp1S4bInMm/k8yuX9ifUSPJJ4ltbcdG6TRGHRjcdG +snUOhugZitVtbNV4FpWi6cgKOOvyJBNPc1STE4U6G7weNLWLBYy5d4ux2x8gkasJ +U26Qzns3dLlwR5EiUWMWea6xrkEmCMgZK9FGqkjWZCrXgzT/LCrBbBlDSgeF59N8 +9iFo7+ryUp9/k5DPAgMBAAGjQjBAMA4GA1UdDwEB/wQEAwIBBjAPBgNVHRMBAf8E +BTADAQH/MB0GA1UdDgQWBBRge2YaRQ2XyolQL30EzTSo//z9SzANBgkqhkiG9w0B +AQUFAAOCAQEA1nPnfE920I2/7LqivjTFKDK1fPxsnCwrvQmeU79rXqoRSLblCKOz +yj1hTdNGCbM+w6DjY1Ub8rrvrTnhQ7k4o+YviiY776BQVvnGCv04zcQLcFGUl5gE +38NflNUVyRRBnMRddWQVDf9VMOyGj/8N7yy5Y0b2qvzfvGn9LhJIZJrglfCm7ymP +AbEVtQwdpf5pLGkkeB6zpxxxYu7KyJesF12KwvhHhm4qxFYxldBniYUr+WymXUad +DKqC5JlR3XC321Y9YeRq4VzW9v493kHMB65jUr9TU/Qr6cf9tveCX4XSQRjbgbME +HMUfpIBvFSDJ3gyICh3WZlXi/EjJKSZp4A== +-----END CERTIFICATE----- + +# Issuer: CN=GlobalSign O=GlobalSign OU=GlobalSign Root CA - R2 +# Subject: CN=GlobalSign O=GlobalSign OU=GlobalSign Root CA - R2 +# Label: "GlobalSign Root CA - R2" +# Serial: 4835703278459682885658125 +# MD5 Fingerprint: 94:14:77:7e:3e:5e:fd:8f:30:bd:41:b0:cf:e7:d0:30 +# SHA1 Fingerprint: 75:e0:ab:b6:13:85:12:27:1c:04:f8:5f:dd:de:38:e4:b7:24:2e:fe +# SHA256 Fingerprint: ca:42:dd:41:74:5f:d0:b8:1e:b9:02:36:2c:f9:d8:bf:71:9d:a1:bd:1b:1e:fc:94:6f:5b:4c:99:f4:2c:1b:9e +-----BEGIN CERTIFICATE----- +MIIDujCCAqKgAwIBAgILBAAAAAABD4Ym5g0wDQYJKoZIhvcNAQEFBQAwTDEgMB4G +A1UECxMXR2xvYmFsU2lnbiBSb290IENBIC0gUjIxEzARBgNVBAoTCkdsb2JhbFNp +Z24xEzARBgNVBAMTCkdsb2JhbFNpZ24wHhcNMDYxMjE1MDgwMDAwWhcNMjExMjE1 +MDgwMDAwWjBMMSAwHgYDVQQLExdHbG9iYWxTaWduIFJvb3QgQ0EgLSBSMjETMBEG +A1UEChMKR2xvYmFsU2lnbjETMBEGA1UEAxMKR2xvYmFsU2lnbjCCASIwDQYJKoZI +hvcNAQEBBQADggEPADCCAQoCggEBAKbPJA6+Lm8omUVCxKs+IVSbC9N/hHD6ErPL +v4dfxn+G07IwXNb9rfF73OX4YJYJkhD10FPe+3t+c4isUoh7SqbKSaZeqKeMWhG8 +eoLrvozps6yWJQeXSpkqBy+0Hne/ig+1AnwblrjFuTosvNYSuetZfeLQBoZfXklq +tTleiDTsvHgMCJiEbKjNS7SgfQx5TfC4LcshytVsW33hoCmEofnTlEnLJGKRILzd +C9XZzPnqJworc5HGnRusyMvo4KD0L5CLTfuwNhv2GXqF4G3yYROIXJ/gkwpRl4pa +zq+r1feqCapgvdzZX99yqWATXgAByUr6P6TqBwMhAo6CygPCm48CAwEAAaOBnDCB +mTAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUm+IH +V2ccHsBqBt5ZtJot39wZhi4wNgYDVR0fBC8wLTAroCmgJ4YlaHR0cDovL2NybC5n +bG9iYWxzaWduLm5ldC9yb290LXIyLmNybDAfBgNVHSMEGDAWgBSb4gdXZxwewGoG +3lm0mi3f3BmGLjANBgkqhkiG9w0BAQUFAAOCAQEAmYFThxxol4aR7OBKuEQLq4Gs +J0/WwbgcQ3izDJr86iw8bmEbTUsp9Z8FHSbBuOmDAGJFtqkIk7mpM0sYmsL4h4hO +291xNBrBVNpGP+DTKqttVCL1OmLNIG+6KYnX3ZHu01yiPqFbQfXf5WRDLenVOavS +ot+3i9DAgBkcRcAtjOj4LaR0VknFBbVPFd5uRHg5h6h+u/N5GJG79G+dwfCMNYxd +AfvDbbnvRG15RjF+Cv6pgsH/76tuIMRQyV+dTZsXjAzlAcmgQWpzU/qlULRuJQ/7 +TBj0/VLZjmmx6BEP3ojY+x1J96relc8geMJgEtslQIxq/H5COEBkEveegeGTLg== +-----END CERTIFICATE----- + +# Issuer: CN=VeriSign Class 3 Public Primary Certification Authority - G3 O=VeriSign, Inc. OU=VeriSign Trust Network/(c) 1999 VeriSign, Inc. - For authorized use only +# Subject: CN=VeriSign Class 3 Public Primary Certification Authority - G3 O=VeriSign, Inc. OU=VeriSign Trust Network/(c) 1999 VeriSign, Inc. - For authorized use only +# Label: "Verisign Class 3 Public Primary Certification Authority - G3" +# Serial: 206684696279472310254277870180966723415 +# MD5 Fingerprint: cd:68:b6:a7:c7:c4:ce:75:e0:1d:4f:57:44:61:92:09 +# SHA1 Fingerprint: 13:2d:0d:45:53:4b:69:97:cd:b2:d5:c3:39:e2:55:76:60:9b:5c:c6 +# SHA256 Fingerprint: eb:04:cf:5e:b1:f3:9a:fa:76:2f:2b:b1:20:f2:96:cb:a5:20:c1:b9:7d:b1:58:95:65:b8:1c:b9:a1:7b:72:44 +-----BEGIN CERTIFICATE----- +MIIEGjCCAwICEQCbfgZJoz5iudXukEhxKe9XMA0GCSqGSIb3DQEBBQUAMIHKMQsw +CQYDVQQGEwJVUzEXMBUGA1UEChMOVmVyaVNpZ24sIEluYy4xHzAdBgNVBAsTFlZl +cmlTaWduIFRydXN0IE5ldHdvcmsxOjA4BgNVBAsTMShjKSAxOTk5IFZlcmlTaWdu +LCBJbmMuIC0gRm9yIGF1dGhvcml6ZWQgdXNlIG9ubHkxRTBDBgNVBAMTPFZlcmlT +aWduIENsYXNzIDMgUHVibGljIFByaW1hcnkgQ2VydGlmaWNhdGlvbiBBdXRob3Jp +dHkgLSBHMzAeFw05OTEwMDEwMDAwMDBaFw0zNjA3MTYyMzU5NTlaMIHKMQswCQYD +VQQGEwJVUzEXMBUGA1UEChMOVmVyaVNpZ24sIEluYy4xHzAdBgNVBAsTFlZlcmlT +aWduIFRydXN0IE5ldHdvcmsxOjA4BgNVBAsTMShjKSAxOTk5IFZlcmlTaWduLCBJ +bmMuIC0gRm9yIGF1dGhvcml6ZWQgdXNlIG9ubHkxRTBDBgNVBAMTPFZlcmlTaWdu +IENsYXNzIDMgUHVibGljIFByaW1hcnkgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkg +LSBHMzCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAMu6nFL8eB8aHm8b +N3O9+MlrlBIwT/A2R/XQkQr1F8ilYcEWQE37imGQ5XYgwREGfassbqb1EUGO+i2t +KmFZpGcmTNDovFJbcCAEWNF6yaRpvIMXZK0Fi7zQWM6NjPXr8EJJC52XJ2cybuGu +kxUccLwgTS8Y3pKI6GyFVxEa6X7jJhFUokWWVYPKMIno3Nij7SqAP395ZVc+FSBm +CC+Vk7+qRy+oRpfwEuL+wgorUeZ25rdGt+INpsyow0xZVYnm6FNcHOqd8GIWC6fJ +Xwzw3sJ2zq/3avL6QaaiMxTJ5Xpj055iN9WFZZ4O5lMkdBteHRJTW8cs54NJOxWu +imi5V5cCAwEAATANBgkqhkiG9w0BAQUFAAOCAQEAERSWwauSCPc/L8my/uRan2Te +2yFPhpk0djZX3dAVL8WtfxUfN2JzPtTnX84XA9s1+ivbrmAJXx5fj267Cz3qWhMe +DGBvtcC1IyIuBwvLqXTLR7sdwdela8wv0kL9Sd2nic9TutoAWii/gt/4uhMdUIaC +/Y4wjylGsB49Ndo4YhYYSq3mtlFs3q9i6wHQHiT+eo8SGhJouPtmmRQURVyu565p +F4ErWjfJXir0xuKhXFSbplQAz/DxwceYMBo7Nhbbo27q/a2ywtrvAkcTisDxszGt +TxzhT5yvDwyd93gN2PQ1VoDat20Xj50egWTh/sVFuq1ruQp6Tk9LhO5L8X3dEQ== +-----END CERTIFICATE----- + +# Issuer: CN=Entrust.net Certification Authority (2048) O=Entrust.net OU=www.entrust.net/CPS_2048 incorp. by ref. (limits liab.)/(c) 1999 Entrust.net Limited +# Subject: CN=Entrust.net Certification Authority (2048) O=Entrust.net OU=www.entrust.net/CPS_2048 incorp. by ref. (limits liab.)/(c) 1999 Entrust.net Limited +# Label: "Entrust.net Premium 2048 Secure Server CA" +# Serial: 946069240 +# MD5 Fingerprint: ee:29:31:bc:32:7e:9a:e6:e8:b5:f7:51:b4:34:71:90 +# SHA1 Fingerprint: 50:30:06:09:1d:97:d4:f5:ae:39:f7:cb:e7:92:7d:7d:65:2d:34:31 +# SHA256 Fingerprint: 6d:c4:71:72:e0:1c:bc:b0:bf:62:58:0d:89:5f:e2:b8:ac:9a:d4:f8:73:80:1e:0c:10:b9:c8:37:d2:1e:b1:77 +-----BEGIN CERTIFICATE----- +MIIEKjCCAxKgAwIBAgIEOGPe+DANBgkqhkiG9w0BAQUFADCBtDEUMBIGA1UEChML +RW50cnVzdC5uZXQxQDA+BgNVBAsUN3d3dy5lbnRydXN0Lm5ldC9DUFNfMjA0OCBp +bmNvcnAuIGJ5IHJlZi4gKGxpbWl0cyBsaWFiLikxJTAjBgNVBAsTHChjKSAxOTk5 +IEVudHJ1c3QubmV0IExpbWl0ZWQxMzAxBgNVBAMTKkVudHJ1c3QubmV0IENlcnRp +ZmljYXRpb24gQXV0aG9yaXR5ICgyMDQ4KTAeFw05OTEyMjQxNzUwNTFaFw0yOTA3 +MjQxNDE1MTJaMIG0MRQwEgYDVQQKEwtFbnRydXN0Lm5ldDFAMD4GA1UECxQ3d3d3 +LmVudHJ1c3QubmV0L0NQU18yMDQ4IGluY29ycC4gYnkgcmVmLiAobGltaXRzIGxp +YWIuKTElMCMGA1UECxMcKGMpIDE5OTkgRW50cnVzdC5uZXQgTGltaXRlZDEzMDEG +A1UEAxMqRW50cnVzdC5uZXQgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkgKDIwNDgp +MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEArU1LqRKGsuqjIAcVFmQq +K0vRvwtKTY7tgHalZ7d4QMBzQshowNtTK91euHaYNZOLGp18EzoOH1u3Hs/lJBQe +sYGpjX24zGtLA/ECDNyrpUAkAH90lKGdCCmziAv1h3edVc3kw37XamSrhRSGlVuX +MlBvPci6Zgzj/L24ScF2iUkZ/cCovYmjZy/Gn7xxGWC4LeksyZB2ZnuU4q941mVT +XTzWnLLPKQP5L6RQstRIzgUyVYr9smRMDuSYB3Xbf9+5CFVghTAp+XtIpGmG4zU/ +HoZdenoVve8AjhUiVBcAkCaTvA5JaJG/+EfTnZVCwQ5N328mz8MYIWJmQ3DW1cAH +4QIDAQABo0IwQDAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB/zAdBgNV +HQ4EFgQUVeSB0RGAvtiJuQijMfmhJAkWuXAwDQYJKoZIhvcNAQEFBQADggEBADub +j1abMOdTmXx6eadNl9cZlZD7Bh/KM3xGY4+WZiT6QBshJ8rmcnPyT/4xmf3IDExo +U8aAghOY+rat2l098c5u9hURlIIM7j+VrxGrD9cv3h8Dj1csHsm7mhpElesYT6Yf +zX1XEC+bBAlahLVu2B064dae0Wx5XnkcFMXj0EyTO2U87d89vqbllRrDtRnDvV5b +u/8j72gZyxKTJ1wDLW8w0B62GqzeWvfRqqgnpv55gcR5mTNXuhKwqeBCbJPKVt7+ +bYQLCIt+jerXmCHG8+c8eS9enNFMFY3h7CI3zJpDC5fcgJCNs2ebb0gIFVbPv/Er +fF6adulZkMV8gzURZVE= +-----END CERTIFICATE----- + +# Issuer: CN=Baltimore CyberTrust Root O=Baltimore OU=CyberTrust +# Subject: CN=Baltimore CyberTrust Root O=Baltimore OU=CyberTrust +# Label: "Baltimore CyberTrust Root" +# Serial: 33554617 +# MD5 Fingerprint: ac:b6:94:a5:9c:17:e0:d7:91:52:9b:b1:97:06:a6:e4 +# SHA1 Fingerprint: d4:de:20:d0:5e:66:fc:53:fe:1a:50:88:2c:78:db:28:52:ca:e4:74 +# SHA256 Fingerprint: 16:af:57:a9:f6:76:b0:ab:12:60:95:aa:5e:ba:de:f2:2a:b3:11:19:d6:44:ac:95:cd:4b:93:db:f3:f2:6a:eb +-----BEGIN CERTIFICATE----- +MIIDdzCCAl+gAwIBAgIEAgAAuTANBgkqhkiG9w0BAQUFADBaMQswCQYDVQQGEwJJ +RTESMBAGA1UEChMJQmFsdGltb3JlMRMwEQYDVQQLEwpDeWJlclRydXN0MSIwIAYD +VQQDExlCYWx0aW1vcmUgQ3liZXJUcnVzdCBSb290MB4XDTAwMDUxMjE4NDYwMFoX +DTI1MDUxMjIzNTkwMFowWjELMAkGA1UEBhMCSUUxEjAQBgNVBAoTCUJhbHRpbW9y +ZTETMBEGA1UECxMKQ3liZXJUcnVzdDEiMCAGA1UEAxMZQmFsdGltb3JlIEN5YmVy +VHJ1c3QgUm9vdDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAKMEuyKr +mD1X6CZymrV51Cni4eiVgLGw41uOKymaZN+hXe2wCQVt2yguzmKiYv60iNoS6zjr +IZ3AQSsBUnuId9Mcj8e6uYi1agnnc+gRQKfRzMpijS3ljwumUNKoUMMo6vWrJYeK +mpYcqWe4PwzV9/lSEy/CG9VwcPCPwBLKBsua4dnKM3p31vjsufFoREJIE9LAwqSu +XmD+tqYF/LTdB1kC1FkYmGP1pWPgkAx9XbIGevOF6uvUA65ehD5f/xXtabz5OTZy +dc93Uk3zyZAsuT3lySNTPx8kmCFcB5kpvcY67Oduhjprl3RjM71oGDHweI12v/ye +jl0qhqdNkNwnGjkCAwEAAaNFMEMwHQYDVR0OBBYEFOWdWTCCR1jMrPoIVDaGezq1 +BE3wMBIGA1UdEwEB/wQIMAYBAf8CAQMwDgYDVR0PAQH/BAQDAgEGMA0GCSqGSIb3 +DQEBBQUAA4IBAQCFDF2O5G9RaEIFoN27TyclhAO992T9Ldcw46QQF+vaKSm2eT92 +9hkTI7gQCvlYpNRhcL0EYWoSihfVCr3FvDB81ukMJY2GQE/szKN+OMY3EU/t3Wgx +jkzSswF07r51XgdIGn9w/xZchMB5hbgF/X++ZRGjD8ACtPhSNzkE1akxehi/oCr0 +Epn3o0WC4zxe9Z2etciefC7IpJ5OCBRLbf1wbWsaY71k5h+3zvDyny67G7fyUIhz +ksLi4xaNmjICq44Y3ekQEe5+NauQrz4wlHrQMz2nZQ/1/I6eYs9HRCwBXbsdtTLS +R9I4LtD+gdwyah617jzV/OeBHRnDJELqYzmp +-----END CERTIFICATE----- + +# Issuer: CN=AddTrust External CA Root O=AddTrust AB OU=AddTrust External TTP Network +# Subject: CN=AddTrust External CA Root O=AddTrust AB OU=AddTrust External TTP Network +# Label: "AddTrust External Root" +# Serial: 1 +# MD5 Fingerprint: 1d:35:54:04:85:78:b0:3f:42:42:4d:bf:20:73:0a:3f +# SHA1 Fingerprint: 02:fa:f3:e2:91:43:54:68:60:78:57:69:4d:f5:e4:5b:68:85:18:68 +# SHA256 Fingerprint: 68:7f:a4:51:38:22:78:ff:f0:c8:b1:1f:8d:43:d5:76:67:1c:6e:b2:bc:ea:b4:13:fb:83:d9:65:d0:6d:2f:f2 +-----BEGIN CERTIFICATE----- +MIIENjCCAx6gAwIBAgIBATANBgkqhkiG9w0BAQUFADBvMQswCQYDVQQGEwJTRTEU +MBIGA1UEChMLQWRkVHJ1c3QgQUIxJjAkBgNVBAsTHUFkZFRydXN0IEV4dGVybmFs +IFRUUCBOZXR3b3JrMSIwIAYDVQQDExlBZGRUcnVzdCBFeHRlcm5hbCBDQSBSb290 +MB4XDTAwMDUzMDEwNDgzOFoXDTIwMDUzMDEwNDgzOFowbzELMAkGA1UEBhMCU0Ux +FDASBgNVBAoTC0FkZFRydXN0IEFCMSYwJAYDVQQLEx1BZGRUcnVzdCBFeHRlcm5h +bCBUVFAgTmV0d29yazEiMCAGA1UEAxMZQWRkVHJ1c3QgRXh0ZXJuYWwgQ0EgUm9v +dDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBALf3GjPm8gAELTngTlvt +H7xsD821+iO2zt6bETOXpClMfZOfvUq8k+0DGuOPz+VtUFrWlymUWoCwSXrbLpX9 +uMq/NzgtHj6RQa1wVsfwTz/oMp50ysiQVOnGXw94nZpAPA6sYapeFI+eh6FqUNzX +mk6vBbOmcZSccbNQYArHE504B4YCqOmoaSYYkKtMsE8jqzpPhNjfzp/haW+710LX +a0Tkx63ubUFfclpxCDezeWWkWaCUN/cALw3CknLa0Dhy2xSoRcRdKn23tNbE7qzN +E0S3ySvdQwAl+mG5aWpYIxG3pzOPVnVZ9c0p10a3CitlttNCbxWyuHv77+ldU9U0 +WicCAwEAAaOB3DCB2TAdBgNVHQ4EFgQUrb2YejS0Jvf6xCZU7wO94CTLVBowCwYD +VR0PBAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8wgZkGA1UdIwSBkTCBjoAUrb2YejS0 +Jvf6xCZU7wO94CTLVBqhc6RxMG8xCzAJBgNVBAYTAlNFMRQwEgYDVQQKEwtBZGRU +cnVzdCBBQjEmMCQGA1UECxMdQWRkVHJ1c3QgRXh0ZXJuYWwgVFRQIE5ldHdvcmsx +IjAgBgNVBAMTGUFkZFRydXN0IEV4dGVybmFsIENBIFJvb3SCAQEwDQYJKoZIhvcN +AQEFBQADggEBALCb4IUlwtYj4g+WBpKdQZic2YR5gdkeWxQHIzZlj7DYd7usQWxH +YINRsPkyPef89iYTx4AWpb9a/IfPeHmJIZriTAcKhjW88t5RxNKWt9x+Tu5w/Rw5 +6wwCURQtjr0W4MHfRnXnJK3s9EK0hZNwEGe6nQY1ShjTK3rMUUKhemPR5ruhxSvC +Nr4TDea9Y355e6cJDUCrat2PisP29owaQgVR1EX1n6diIWgVIEM8med8vSTYqZEX +c4g/VhsxOBi0cQ+azcgOno4uG+GMmIPLHzHxREzGBHNJdmAPx/i9F4BrLunMTA5a +mnkPIAou1Z5jJh5VkpTYghdae9C8x49OhgQ= +-----END CERTIFICATE----- + +# Issuer: CN=Entrust Root Certification Authority O=Entrust, Inc. OU=www.entrust.net/CPS is incorporated by reference/(c) 2006 Entrust, Inc. +# Subject: CN=Entrust Root Certification Authority O=Entrust, Inc. OU=www.entrust.net/CPS is incorporated by reference/(c) 2006 Entrust, Inc. +# Label: "Entrust Root Certification Authority" +# Serial: 1164660820 +# MD5 Fingerprint: d6:a5:c3:ed:5d:dd:3e:00:c1:3d:87:92:1f:1d:3f:e4 +# SHA1 Fingerprint: b3:1e:b1:b7:40:e3:6c:84:02:da:dc:37:d4:4d:f5:d4:67:49:52:f9 +# SHA256 Fingerprint: 73:c1:76:43:4f:1b:c6:d5:ad:f4:5b:0e:76:e7:27:28:7c:8d:e5:76:16:c1:e6:e6:14:1a:2b:2c:bc:7d:8e:4c +-----BEGIN CERTIFICATE----- +MIIEkTCCA3mgAwIBAgIERWtQVDANBgkqhkiG9w0BAQUFADCBsDELMAkGA1UEBhMC +VVMxFjAUBgNVBAoTDUVudHJ1c3QsIEluYy4xOTA3BgNVBAsTMHd3dy5lbnRydXN0 +Lm5ldC9DUFMgaXMgaW5jb3Jwb3JhdGVkIGJ5IHJlZmVyZW5jZTEfMB0GA1UECxMW +KGMpIDIwMDYgRW50cnVzdCwgSW5jLjEtMCsGA1UEAxMkRW50cnVzdCBSb290IENl +cnRpZmljYXRpb24gQXV0aG9yaXR5MB4XDTA2MTEyNzIwMjM0MloXDTI2MTEyNzIw +NTM0MlowgbAxCzAJBgNVBAYTAlVTMRYwFAYDVQQKEw1FbnRydXN0LCBJbmMuMTkw +NwYDVQQLEzB3d3cuZW50cnVzdC5uZXQvQ1BTIGlzIGluY29ycG9yYXRlZCBieSBy +ZWZlcmVuY2UxHzAdBgNVBAsTFihjKSAyMDA2IEVudHJ1c3QsIEluYy4xLTArBgNV +BAMTJEVudHJ1c3QgUm9vdCBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTCCASIwDQYJ +KoZIhvcNAQEBBQADggEPADCCAQoCggEBALaVtkNC+sZtKm9I35RMOVcF7sN5EUFo +Nu3s/poBj6E4KPz3EEZmLk0eGrEaTsbRwJWIsMn/MYszA9u3g3s+IIRe7bJWKKf4 +4LlAcTfFy0cOlypowCKVYhXbR9n10Cv/gkvJrT7eTNuQgFA/CYqEAOwwCj0Yzfv9 +KlmaI5UXLEWeH25DeW0MXJj+SKfFI0dcXv1u5x609mhF0YaDW6KKjbHjKYD+JXGI +rb68j6xSlkuqUY3kEzEZ6E5Nn9uss2rVvDlUccp6en+Q3X0dgNmBu1kmwhH+5pPi +94DkZfs0Nw4pgHBNrziGLp5/V6+eF67rHMsoIV+2HNjnogQi+dPa2MsCAwEAAaOB +sDCBrTAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB/zArBgNVHRAEJDAi +gA8yMDA2MTEyNzIwMjM0MlqBDzIwMjYxMTI3MjA1MzQyWjAfBgNVHSMEGDAWgBRo +kORnpKZTgMeGZqTx90tD+4S9bTAdBgNVHQ4EFgQUaJDkZ6SmU4DHhmak8fdLQ/uE +vW0wHQYJKoZIhvZ9B0EABBAwDhsIVjcuMTo0LjADAgSQMA0GCSqGSIb3DQEBBQUA +A4IBAQCT1DCw1wMgKtD5Y+iRDAUgqV8ZyntyTtSx29CW+1RaGSwMCPeyvIWonX9t +O1KzKtvn1ISMY/YPyyYBkVBs9F8U4pN0wBOeMDpQ47RgxRzwIkSNcUesyBrJ6Zua +AGAT/3B+XxFNSRuzFVJ7yVTav52Vr2ua2J7p8eRDjeIRRDq/r72DQnNSi6q7pynP +9WQcCk3RvKqsnyrQ/39/2n3qse0wJcGE2jTSW3iDVuycNsMm4hH2Z0kdkquM++v/ +eu6FSqdQgPCnXEqULl8FmTxSQeDNtGPPAUO6nIPcj2A781q0tHuu2guQOHXvgR1m +0vdXcDazv/wor3ElhVsT/h5/WrQ8 +-----END CERTIFICATE----- + +# Issuer: CN=GeoTrust Global CA O=GeoTrust Inc. +# Subject: CN=GeoTrust Global CA O=GeoTrust Inc. +# Label: "GeoTrust Global CA" +# Serial: 144470 +# MD5 Fingerprint: f7:75:ab:29:fb:51:4e:b7:77:5e:ff:05:3c:99:8e:f5 +# SHA1 Fingerprint: de:28:f4:a4:ff:e5:b9:2f:a3:c5:03:d1:a3:49:a7:f9:96:2a:82:12 +# SHA256 Fingerprint: ff:85:6a:2d:25:1d:cd:88:d3:66:56:f4:50:12:67:98:cf:ab:aa:de:40:79:9c:72:2d:e4:d2:b5:db:36:a7:3a +-----BEGIN CERTIFICATE----- +MIIDVDCCAjygAwIBAgIDAjRWMA0GCSqGSIb3DQEBBQUAMEIxCzAJBgNVBAYTAlVT +MRYwFAYDVQQKEw1HZW9UcnVzdCBJbmMuMRswGQYDVQQDExJHZW9UcnVzdCBHbG9i +YWwgQ0EwHhcNMDIwNTIxMDQwMDAwWhcNMjIwNTIxMDQwMDAwWjBCMQswCQYDVQQG +EwJVUzEWMBQGA1UEChMNR2VvVHJ1c3QgSW5jLjEbMBkGA1UEAxMSR2VvVHJ1c3Qg +R2xvYmFsIENBMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA2swYYzD9 +9BcjGlZ+W988bDjkcbd4kdS8odhM+KhDtgPpTSEHCIjaWC9mOSm9BXiLnTjoBbdq +fnGk5sRgprDvgOSJKA+eJdbtg/OtppHHmMlCGDUUna2YRpIuT8rxh0PBFpVXLVDv +iS2Aelet8u5fa9IAjbkU+BQVNdnARqN7csiRv8lVK83Qlz6cJmTM386DGXHKTubU +1XupGc1V3sjs0l44U+VcT4wt/lAjNvxm5suOpDkZALeVAjmRCw7+OC7RHQWa9k0+ +bw8HHa8sHo9gOeL6NlMTOdReJivbPagUvTLrGAMoUgRx5aszPeE4uwc2hGKceeoW +MPRfwCvocWvk+QIDAQABo1MwUTAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBTA +ephojYn7qwVkDBF9qn1luMrMTjAfBgNVHSMEGDAWgBTAephojYn7qwVkDBF9qn1l +uMrMTjANBgkqhkiG9w0BAQUFAAOCAQEANeMpauUvXVSOKVCUn5kaFOSPeCpilKIn +Z57QzxpeR+nBsqTP3UEaBU6bS+5Kb1VSsyShNwrrZHYqLizz/Tt1kL/6cdjHPTfS +tQWVYrmm3ok9Nns4d0iXrKYgjy6myQzCsplFAMfOEVEiIuCl6rYVSAlk6l5PdPcF +PseKUgzbFbS9bZvlxrFUaKnjaZC2mqUPuLk/IH2uSrW4nOQdtqvmlKXBx4Ot2/Un +hw4EbNX/3aBd7YdStysVAq45pmp06drE57xNNB6pXE0zX5IJL4hmXXeXxx12E6nV +5fEWCRE11azbJHFwLJhWC9kXtNHjUStedejV0NxPNO3CBWaAocvmMw== +-----END CERTIFICATE----- + +# Issuer: CN=GeoTrust Universal CA O=GeoTrust Inc. +# Subject: CN=GeoTrust Universal CA O=GeoTrust Inc. +# Label: "GeoTrust Universal CA" +# Serial: 1 +# MD5 Fingerprint: 92:65:58:8b:a2:1a:31:72:73:68:5c:b4:a5:7a:07:48 +# SHA1 Fingerprint: e6:21:f3:35:43:79:05:9a:4b:68:30:9d:8a:2f:74:22:15:87:ec:79 +# SHA256 Fingerprint: a0:45:9b:9f:63:b2:25:59:f5:fa:5d:4c:6d:b3:f9:f7:2f:f1:93:42:03:35:78:f0:73:bf:1d:1b:46:cb:b9:12 +-----BEGIN CERTIFICATE----- +MIIFaDCCA1CgAwIBAgIBATANBgkqhkiG9w0BAQUFADBFMQswCQYDVQQGEwJVUzEW +MBQGA1UEChMNR2VvVHJ1c3QgSW5jLjEeMBwGA1UEAxMVR2VvVHJ1c3QgVW5pdmVy +c2FsIENBMB4XDTA0MDMwNDA1MDAwMFoXDTI5MDMwNDA1MDAwMFowRTELMAkGA1UE +BhMCVVMxFjAUBgNVBAoTDUdlb1RydXN0IEluYy4xHjAcBgNVBAMTFUdlb1RydXN0 +IFVuaXZlcnNhbCBDQTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAKYV +VaCjxuAfjJ0hUNfBvitbtaSeodlyWL0AG0y/YckUHUWCq8YdgNY96xCcOq9tJPi8 +cQGeBvV8Xx7BDlXKg5pZMK4ZyzBIle0iN430SppyZj6tlcDgFgDgEB8rMQ7XlFTT +QjOgNB0eRXbdT8oYN+yFFXoZCPzVx5zw8qkuEKmS5j1YPakWaDwvdSEYfyh3peFh +F7em6fgemdtzbvQKoiFs7tqqhZJmr/Z6a4LauiIINQ/PQvE1+mrufislzDoR5G2v +c7J2Ha3QsnhnGqQ5HFELZ1aD/ThdDc7d8Lsrlh/eezJS/R27tQahsiFepdaVaH/w +mZ7cRQg+59IJDTWU3YBOU5fXtQlEIGQWFwMCTFMNaN7VqnJNk22CDtucvc+081xd +VHppCZbW2xHBjXWotM85yM48vCR85mLK4b19p71XZQvk/iXttmkQ3CgaRr0BHdCX +teGYO8A3ZNY9lO4L4fUorgtWv3GLIylBjobFS1J72HGrH4oVpjuDWtdYAVHGTEHZ +f9hBZ3KiKN9gg6meyHv8U3NyWfWTehd2Ds735VzZC1U0oqpbtWpU5xPKV+yXbfRe +Bi9Fi1jUIxaS5BZuKGNZMN9QAZxjiRqf2xeUgnA3wySemkfWWspOqGmJch+RbNt+ +nhutxx9z3SxPGWX9f5NAEC7S8O08ni4oPmkmM8V7AgMBAAGjYzBhMA8GA1UdEwEB +/wQFMAMBAf8wHQYDVR0OBBYEFNq7LqqwDLiIJlF0XG0D08DYj3rWMB8GA1UdIwQY +MBaAFNq7LqqwDLiIJlF0XG0D08DYj3rWMA4GA1UdDwEB/wQEAwIBhjANBgkqhkiG +9w0BAQUFAAOCAgEAMXjmx7XfuJRAyXHEqDXsRh3ChfMoWIawC/yOsjmPRFWrZIRc +aanQmjg8+uUfNeVE44B5lGiku8SfPeE0zTBGi1QrlaXv9z+ZhP015s8xxtxqv6fX +IwjhmF7DWgh2qaavdy+3YL1ERmrvl/9zlcGO6JP7/TG37FcREUWbMPEaiDnBTzyn +ANXH/KttgCJwpQzgXQQpAvvLoJHRfNbDflDVnVi+QTjruXU8FdmbyUqDWcDaU/0z +uzYYm4UPFd3uLax2k7nZAY1IEKj79TiG8dsKxr2EoyNB3tZ3b4XUhRxQ4K5RirqN +Pnbiucon8l+f725ZDQbYKxek0nxru18UGkiPGkzns0ccjkxFKyDuSN/n3QmOGKja +QI2SJhFTYXNd673nxE0pN2HrrDktZy4W1vUAg4WhzH92xH3kt0tm7wNFYGm2DFKW +koRepqO1pD4r2czYG0eq8kTaT/kD6PAUyz/zg97QwVTjt+gKN02LIFkDMBmhLMi9 +ER/frslKxfMnZmaGrGiR/9nmUxwPi1xpZQomyB40w11Re9epnAahNt3ViZS82eQt +DF4JbAiXfKM9fJP/P6EUp8+1Xevb2xzEdt+Iub1FBZUbrvxGakyvSOPOrg/Sfuvm +bJxPgWp6ZKy7PtXny3YuxadIwVyQD8vIP/rmMuGNG2+k5o7Y+SlIis5z/iw= +-----END CERTIFICATE----- + +# Issuer: CN=GeoTrust Universal CA 2 O=GeoTrust Inc. +# Subject: CN=GeoTrust Universal CA 2 O=GeoTrust Inc. +# Label: "GeoTrust Universal CA 2" +# Serial: 1 +# MD5 Fingerprint: 34:fc:b8:d0:36:db:9e:14:b3:c2:f2:db:8f:e4:94:c7 +# SHA1 Fingerprint: 37:9a:19:7b:41:85:45:35:0c:a6:03:69:f3:3c:2e:af:47:4f:20:79 +# SHA256 Fingerprint: a0:23:4f:3b:c8:52:7c:a5:62:8e:ec:81:ad:5d:69:89:5d:a5:68:0d:c9:1d:1c:b8:47:7f:33:f8:78:b9:5b:0b +-----BEGIN CERTIFICATE----- +MIIFbDCCA1SgAwIBAgIBATANBgkqhkiG9w0BAQUFADBHMQswCQYDVQQGEwJVUzEW +MBQGA1UEChMNR2VvVHJ1c3QgSW5jLjEgMB4GA1UEAxMXR2VvVHJ1c3QgVW5pdmVy +c2FsIENBIDIwHhcNMDQwMzA0MDUwMDAwWhcNMjkwMzA0MDUwMDAwWjBHMQswCQYD +VQQGEwJVUzEWMBQGA1UEChMNR2VvVHJ1c3QgSW5jLjEgMB4GA1UEAxMXR2VvVHJ1 +c3QgVW5pdmVyc2FsIENBIDIwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoIC +AQCzVFLByT7y2dyxUxpZKeexw0Uo5dfR7cXFS6GqdHtXr0om/Nj1XqduGdt0DE81 +WzILAePb63p3NeqqWuDW6KFXlPCQo3RWlEQwAx5cTiuFJnSCegx2oG9NzkEtoBUG +FF+3Qs17j1hhNNwqCPkuwwGmIkQcTAeC5lvO0Ep8BNMZcyfwqph/Lq9O64ceJHdq +XbboW0W63MOhBW9Wjo8QJqVJwy7XQYci4E+GymC16qFjwAGXEHm9ADwSbSsVsaxL +se4YuU6W3Nx2/zu+z18DwPw76L5GG//aQMJS9/7jOvdqdzXQ2o3rXhhqMcceujwb +KNZrVMaqW9eiLBsZzKIC9ptZvTdrhrVtgrrY6slWvKk2WP0+GfPtDCapkzj4T8Fd +IgbQl+rhrcZV4IErKIM6+vR7IVEAvlI4zs1meaj0gVbi0IMJR1FbUGrP20gaXT73 +y/Zl92zxlfgCOzJWgjl6W70viRu/obTo/3+NjN8D8WBOWBFM66M/ECuDmgFz2ZRt +hAAnZqzwcEAJQpKtT5MNYQlRJNiS1QuUYbKHsu3/mjX/hVTK7URDrBs8FmtISgoc +QIgfksILAAX/8sgCSqSqqcyZlpwvWOB94b67B9xfBHJcMTTD7F8t4D1kkCLm0ey4 +Lt1ZrtmhN79UNdxzMk+MBB4zsslG8dhcyFVQyWi9qLo2CQIDAQABo2MwYTAPBgNV +HRMBAf8EBTADAQH/MB0GA1UdDgQWBBR281Xh+qQ2+/CfXGJx7Tz0RzgQKzAfBgNV +HSMEGDAWgBR281Xh+qQ2+/CfXGJx7Tz0RzgQKzAOBgNVHQ8BAf8EBAMCAYYwDQYJ +KoZIhvcNAQEFBQADggIBAGbBxiPz2eAubl/oz66wsCVNK/g7WJtAJDday6sWSf+z +dXkzoS9tcBc0kf5nfo/sm+VegqlVHy/c1FEHEv6sFj4sNcZj/NwQ6w2jqtB8zNHQ +L1EuxBRa3ugZ4T7GzKQp5y6EqgYweHZUcyiYWTjgAA1i00J9IZ+uPTqM1fp3DRgr +Fg5fNuH8KrUwJM/gYwx7WBr+mbpCErGR9Hxo4sjoryzqyX6uuyo9DRXcNJW2GHSo +ag/HtPQTxORb7QrSpJdMKu0vbBKJPfEncKpqA1Ihn0CoZ1Dy81of398j9tx4TuaY +T1U6U+Pv8vSfx3zYWK8pIpe44L2RLrB27FcRz+8pRPPphXpgY+RdM4kX2TGq2tbz +GDVyz4crL2MjhF2EjD9XoIj8mZEoJmmZ1I+XRL6O1UixpCgp8RW04eWe3fiPpm8m +1wk8OhwRDqZsN/etRIcsKMfYdIKz0G9KV7s1KSegi+ghp4dkNl3M2Basx7InQJJV +OCiNUW7dFGdTbHFcJoRNdVq2fmBWqU2t+5sel/MN2dKXVHfaPRK34B7vCAas+YWH +6aLcr34YEoP9VhdBLtUpgn2Z9DH2canPLAEnpQW5qrJITirvn5NSUZU8UnOOVkwX +QMAJKOSLakhT2+zNVVXxxvjpoixMptEmX36vWkzaH6byHCx+rgIW0lbQL1dTR+iS +-----END CERTIFICATE----- + +# Issuer: CN=AAA Certificate Services O=Comodo CA Limited +# Subject: CN=AAA Certificate Services O=Comodo CA Limited +# Label: "Comodo AAA Services root" +# Serial: 1 +# MD5 Fingerprint: 49:79:04:b0:eb:87:19:ac:47:b0:bc:11:51:9b:74:d0 +# SHA1 Fingerprint: d1:eb:23:a4:6d:17:d6:8f:d9:25:64:c2:f1:f1:60:17:64:d8:e3:49 +# SHA256 Fingerprint: d7:a7:a0:fb:5d:7e:27:31:d7:71:e9:48:4e:bc:de:f7:1d:5f:0c:3e:0a:29:48:78:2b:c8:3e:e0:ea:69:9e:f4 +-----BEGIN CERTIFICATE----- +MIIEMjCCAxqgAwIBAgIBATANBgkqhkiG9w0BAQUFADB7MQswCQYDVQQGEwJHQjEb +MBkGA1UECAwSR3JlYXRlciBNYW5jaGVzdGVyMRAwDgYDVQQHDAdTYWxmb3JkMRow +GAYDVQQKDBFDb21vZG8gQ0EgTGltaXRlZDEhMB8GA1UEAwwYQUFBIENlcnRpZmlj +YXRlIFNlcnZpY2VzMB4XDTA0MDEwMTAwMDAwMFoXDTI4MTIzMTIzNTk1OVowezEL +MAkGA1UEBhMCR0IxGzAZBgNVBAgMEkdyZWF0ZXIgTWFuY2hlc3RlcjEQMA4GA1UE +BwwHU2FsZm9yZDEaMBgGA1UECgwRQ29tb2RvIENBIExpbWl0ZWQxITAfBgNVBAMM +GEFBQSBDZXJ0aWZpY2F0ZSBTZXJ2aWNlczCCASIwDQYJKoZIhvcNAQEBBQADggEP +ADCCAQoCggEBAL5AnfRu4ep2hxxNRUSOvkbIgwadwSr+GB+O5AL686tdUIoWMQua +BtDFcCLNSS1UY8y2bmhGC1Pqy0wkwLxyTurxFa70VJoSCsN6sjNg4tqJVfMiWPPe +3M/vg4aijJRPn2jymJBGhCfHdr/jzDUsi14HZGWCwEiwqJH5YZ92IFCokcdmtet4 +YgNW8IoaE+oxox6gmf049vYnMlhvB/VruPsUK6+3qszWY19zjNoFmag4qMsXeDZR +rOme9Hg6jc8P2ULimAyrL58OAd7vn5lJ8S3frHRNG5i1R8XlKdH5kBjHYpy+g8cm +ez6KJcfA3Z3mNWgQIJ2P2N7Sw4ScDV7oL8kCAwEAAaOBwDCBvTAdBgNVHQ4EFgQU +oBEKIz6W8Qfs4q8p74Klf9AwpLQwDgYDVR0PAQH/BAQDAgEGMA8GA1UdEwEB/wQF +MAMBAf8wewYDVR0fBHQwcjA4oDagNIYyaHR0cDovL2NybC5jb21vZG9jYS5jb20v +QUFBQ2VydGlmaWNhdGVTZXJ2aWNlcy5jcmwwNqA0oDKGMGh0dHA6Ly9jcmwuY29t +b2RvLm5ldC9BQUFDZXJ0aWZpY2F0ZVNlcnZpY2VzLmNybDANBgkqhkiG9w0BAQUF +AAOCAQEACFb8AvCb6P+k+tZ7xkSAzk/ExfYAWMymtrwUSWgEdujm7l3sAg9g1o1Q +GE8mTgHj5rCl7r+8dFRBv/38ErjHT1r0iWAFf2C3BUrz9vHCv8S5dIa2LX1rzNLz +Rt0vxuBqw8M0Ayx9lt1awg6nCpnBBYurDC/zXDrPbDdVCYfeU0BsWO/8tqtlbgT2 +G9w84FoVxp7Z8VlIMCFlA2zs6SFz7JsDoeA3raAVGI/6ugLOpyypEBMs1OUIJqsi +l2D4kF501KKaU73yqWjgom7C12yxow+ev+to51byrvLjKzg6CYG1a4XXvi3tPxq3 +smPi9WIsgtRqAEFQ8TmDn5XpNpaYbg== +-----END CERTIFICATE----- + +# Issuer: CN=QuoVadis Root Certification Authority O=QuoVadis Limited OU=Root Certification Authority +# Subject: CN=QuoVadis Root Certification Authority O=QuoVadis Limited OU=Root Certification Authority +# Label: "QuoVadis Root CA" +# Serial: 985026699 +# MD5 Fingerprint: 27:de:36:fe:72:b7:00:03:00:9d:f4:f0:1e:6c:04:24 +# SHA1 Fingerprint: de:3f:40:bd:50:93:d3:9b:6c:60:f6:da:bc:07:62:01:00:89:76:c9 +# SHA256 Fingerprint: a4:5e:de:3b:bb:f0:9c:8a:e1:5c:72:ef:c0:72:68:d6:93:a2:1c:99:6f:d5:1e:67:ca:07:94:60:fd:6d:88:73 +-----BEGIN CERTIFICATE----- +MIIF0DCCBLigAwIBAgIEOrZQizANBgkqhkiG9w0BAQUFADB/MQswCQYDVQQGEwJC +TTEZMBcGA1UEChMQUXVvVmFkaXMgTGltaXRlZDElMCMGA1UECxMcUm9vdCBDZXJ0 +aWZpY2F0aW9uIEF1dGhvcml0eTEuMCwGA1UEAxMlUXVvVmFkaXMgUm9vdCBDZXJ0 +aWZpY2F0aW9uIEF1dGhvcml0eTAeFw0wMTAzMTkxODMzMzNaFw0yMTAzMTcxODMz +MzNaMH8xCzAJBgNVBAYTAkJNMRkwFwYDVQQKExBRdW9WYWRpcyBMaW1pdGVkMSUw +IwYDVQQLExxSb290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5MS4wLAYDVQQDEyVR +dW9WYWRpcyBSb290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5MIIBIjANBgkqhkiG +9w0BAQEFAAOCAQ8AMIIBCgKCAQEAv2G1lVO6V/z68mcLOhrfEYBklbTRvM16z/Yp +li4kVEAkOPcahdxYTMukJ0KX0J+DisPkBgNbAKVRHnAEdOLB1Dqr1607BxgFjv2D +rOpm2RgbaIr1VxqYuvXtdj182d6UajtLF8HVj71lODqV0D1VNk7feVcxKh7YWWVJ +WCCYfqtffp/p1k3sg3Spx2zY7ilKhSoGFPlU5tPaZQeLYzcS19Dsw3sgQUSj7cug +F+FxZc4dZjH3dgEZyH0DWLaVSR2mEiboxgx24ONmy+pdpibu5cxfvWenAScOospU +xbF6lR1xHkopigPcakXBpBlebzbNw6Kwt/5cOOJSvPhEQ+aQuwIDAQABo4ICUjCC +Ak4wPQYIKwYBBQUHAQEEMTAvMC0GCCsGAQUFBzABhiFodHRwczovL29jc3AucXVv +dmFkaXNvZmZzaG9yZS5jb20wDwYDVR0TAQH/BAUwAwEB/zCCARoGA1UdIASCAREw +ggENMIIBCQYJKwYBBAG+WAABMIH7MIHUBggrBgEFBQcCAjCBxxqBxFJlbGlhbmNl +IG9uIHRoZSBRdW9WYWRpcyBSb290IENlcnRpZmljYXRlIGJ5IGFueSBwYXJ0eSBh +c3N1bWVzIGFjY2VwdGFuY2Ugb2YgdGhlIHRoZW4gYXBwbGljYWJsZSBzdGFuZGFy +ZCB0ZXJtcyBhbmQgY29uZGl0aW9ucyBvZiB1c2UsIGNlcnRpZmljYXRpb24gcHJh +Y3RpY2VzLCBhbmQgdGhlIFF1b1ZhZGlzIENlcnRpZmljYXRlIFBvbGljeS4wIgYI +KwYBBQUHAgEWFmh0dHA6Ly93d3cucXVvdmFkaXMuYm0wHQYDVR0OBBYEFItLbe3T +KbkGGew5Oanwl4Rqy+/fMIGuBgNVHSMEgaYwgaOAFItLbe3TKbkGGew5Oanwl4Rq +y+/foYGEpIGBMH8xCzAJBgNVBAYTAkJNMRkwFwYDVQQKExBRdW9WYWRpcyBMaW1p +dGVkMSUwIwYDVQQLExxSb290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5MS4wLAYD +VQQDEyVRdW9WYWRpcyBSb290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5ggQ6tlCL +MA4GA1UdDwEB/wQEAwIBBjANBgkqhkiG9w0BAQUFAAOCAQEAitQUtf70mpKnGdSk +fnIYj9lofFIk3WdvOXrEql494liwTXCYhGHoG+NpGA7O+0dQoE7/8CQfvbLO9Sf8 +7C9TqnN7Az10buYWnuulLsS/VidQK2K6vkscPFVcQR0kvoIgR13VRH56FmjffU1R +cHhXHTMe/QKZnAzNCgVPx7uOpHX6Sm2xgI4JVrmcGmD+XcHXetwReNDWXcG31a0y +mQM6isxUJTkxgXsTIlG6Rmyhu576BGxJJnSP0nPrzDCi5upZIof4l/UO/erMkqQW +xFIY6iHOsfHmhIHluqmGKPJDWl0Snawe2ajlCmqnf6CHKc/yiU3U7MXi5nrQNiOK +SnQ2+Q== +-----END CERTIFICATE----- + +# Issuer: CN=QuoVadis Root CA 2 O=QuoVadis Limited +# Subject: CN=QuoVadis Root CA 2 O=QuoVadis Limited +# Label: "QuoVadis Root CA 2" +# Serial: 1289 +# MD5 Fingerprint: 5e:39:7b:dd:f8:ba:ec:82:e9:ac:62:ba:0c:54:00:2b +# SHA1 Fingerprint: ca:3a:fb:cf:12:40:36:4b:44:b2:16:20:88:80:48:39:19:93:7c:f7 +# SHA256 Fingerprint: 85:a0:dd:7d:d7:20:ad:b7:ff:05:f8:3d:54:2b:20:9d:c7:ff:45:28:f7:d6:77:b1:83:89:fe:a5:e5:c4:9e:86 +-----BEGIN CERTIFICATE----- +MIIFtzCCA5+gAwIBAgICBQkwDQYJKoZIhvcNAQEFBQAwRTELMAkGA1UEBhMCQk0x +GTAXBgNVBAoTEFF1b1ZhZGlzIExpbWl0ZWQxGzAZBgNVBAMTElF1b1ZhZGlzIFJv +b3QgQ0EgMjAeFw0wNjExMjQxODI3MDBaFw0zMTExMjQxODIzMzNaMEUxCzAJBgNV +BAYTAkJNMRkwFwYDVQQKExBRdW9WYWRpcyBMaW1pdGVkMRswGQYDVQQDExJRdW9W +YWRpcyBSb290IENBIDIwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQCa +GMpLlA0ALa8DKYrwD4HIrkwZhR0In6spRIXzL4GtMh6QRr+jhiYaHv5+HBg6XJxg +Fyo6dIMzMH1hVBHL7avg5tKifvVrbxi3Cgst/ek+7wrGsxDp3MJGF/hd/aTa/55J +WpzmM+Yklvc/ulsrHHo1wtZn/qtmUIttKGAr79dgw8eTvI02kfN/+NsRE8Scd3bB +rrcCaoF6qUWD4gXmuVbBlDePSHFjIuwXZQeVikvfj8ZaCuWw419eaxGrDPmF60Tp ++ARz8un+XJiM9XOva7R+zdRcAitMOeGylZUtQofX1bOQQ7dsE/He3fbE+Ik/0XX1 +ksOR1YqI0JDs3G3eicJlcZaLDQP9nL9bFqyS2+r+eXyt66/3FsvbzSUr5R/7mp/i +Ucw6UwxI5g69ybR2BlLmEROFcmMDBOAENisgGQLodKcftslWZvB1JdxnwQ5hYIiz +PtGo/KPaHbDRsSNU30R2be1B2MGyIrZTHN81Hdyhdyox5C315eXbyOD/5YDXC2Og +/zOhD7osFRXql7PSorW+8oyWHhqPHWykYTe5hnMz15eWniN9gqRMgeKh0bpnX5UH +oycR7hYQe7xFSkyyBNKr79X9DFHOUGoIMfmR2gyPZFwDwzqLID9ujWc9Otb+fVuI +yV77zGHcizN300QyNQliBJIWENieJ0f7OyHj+OsdWwIDAQABo4GwMIGtMA8GA1Ud +EwEB/wQFMAMBAf8wCwYDVR0PBAQDAgEGMB0GA1UdDgQWBBQahGK8SEwzJQTU7tD2 +A8QZRtGUazBuBgNVHSMEZzBlgBQahGK8SEwzJQTU7tD2A8QZRtGUa6FJpEcwRTEL +MAkGA1UEBhMCQk0xGTAXBgNVBAoTEFF1b1ZhZGlzIExpbWl0ZWQxGzAZBgNVBAMT +ElF1b1ZhZGlzIFJvb3QgQ0EgMoICBQkwDQYJKoZIhvcNAQEFBQADggIBAD4KFk2f +BluornFdLwUvZ+YTRYPENvbzwCYMDbVHZF34tHLJRqUDGCdViXh9duqWNIAXINzn +g/iN/Ae42l9NLmeyhP3ZRPx3UIHmfLTJDQtyU/h2BwdBR5YM++CCJpNVjP4iH2Bl +fF/nJrP3MpCYUNQ3cVX2kiF495V5+vgtJodmVjB3pjd4M1IQWK4/YY7yarHvGH5K +WWPKjaJW1acvvFYfzznB4vsKqBUsfU16Y8Zsl0Q80m/DShcK+JDSV6IZUaUtl0Ha +B0+pUNqQjZRG4T7wlP0QADj1O+hA4bRuVhogzG9Yje0uRY/W6ZM/57Es3zrWIozc +hLsib9D45MY56QSIPMO661V6bYCZJPVsAfv4l7CUW+v90m/xd2gNNWQjrLhVoQPR +TUIZ3Ph1WVaj+ahJefivDrkRoHy3au000LYmYjgahwz46P0u05B/B5EqHdZ+XIWD +mbA4CD/pXvk1B+TJYm5Xf6dQlfe6yJvmjqIBxdZmv3lh8zwc4bmCXF2gw+nYSL0Z +ohEUGW6yhhtoPkg3Goi3XZZenMfvJ2II4pEZXNLxId26F0KCl3GBUzGpn/Z9Yr9y +4aOTHcyKJloJONDO1w2AFrR4pTqHTI2KpdVGl/IsELm8VCLAAVBpQ570su9t+Oza +8eOx79+Rj1QqCyXBJhnEUhAFZdWCEOrCMc0u +-----END CERTIFICATE----- + +# Issuer: CN=QuoVadis Root CA 3 O=QuoVadis Limited +# Subject: CN=QuoVadis Root CA 3 O=QuoVadis Limited +# Label: "QuoVadis Root CA 3" +# Serial: 1478 +# MD5 Fingerprint: 31:85:3c:62:94:97:63:b9:aa:fd:89:4e:af:6f:e0:cf +# SHA1 Fingerprint: 1f:49:14:f7:d8:74:95:1d:dd:ae:02:c0:be:fd:3a:2d:82:75:51:85 +# SHA256 Fingerprint: 18:f1:fc:7f:20:5d:f8:ad:dd:eb:7f:e0:07:dd:57:e3:af:37:5a:9c:4d:8d:73:54:6b:f4:f1:fe:d1:e1:8d:35 +-----BEGIN CERTIFICATE----- +MIIGnTCCBIWgAwIBAgICBcYwDQYJKoZIhvcNAQEFBQAwRTELMAkGA1UEBhMCQk0x +GTAXBgNVBAoTEFF1b1ZhZGlzIExpbWl0ZWQxGzAZBgNVBAMTElF1b1ZhZGlzIFJv +b3QgQ0EgMzAeFw0wNjExMjQxOTExMjNaFw0zMTExMjQxOTA2NDRaMEUxCzAJBgNV +BAYTAkJNMRkwFwYDVQQKExBRdW9WYWRpcyBMaW1pdGVkMRswGQYDVQQDExJRdW9W +YWRpcyBSb290IENBIDMwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDM +V0IWVJzmmNPTTe7+7cefQzlKZbPoFog02w1ZkXTPkrgEQK0CSzGrvI2RaNggDhoB +4hp7Thdd4oq3P5kazethq8Jlph+3t723j/z9cI8LoGe+AaJZz3HmDyl2/7FWeUUr +H556VOijKTVopAFPD6QuN+8bv+OPEKhyq1hX51SGyMnzW9os2l2ObjyjPtr7guXd +8lyyBTNvijbO0BNO/79KDDRMpsMhvVAEVeuxu537RR5kFd5VAYwCdrXLoT9Cabwv +vWhDFlaJKjdhkf2mrk7AyxRllDdLkgbvBNDInIjbC3uBr7E9KsRlOni27tyAsdLT +mZw67mtaa7ONt9XOnMK+pUsvFrGeaDsGb659n/je7Mwpp5ijJUMv7/FfJuGITfhe +btfZFG4ZM2mnO4SJk8RTVROhUXhA+LjJou57ulJCg54U7QVSWllWp5f8nT8KKdjc +T5EOE7zelaTfi5m+rJsziO+1ga8bxiJTyPbH7pcUsMV8eFLI8M5ud2CEpukqdiDt +WAEXMJPpGovgc2PZapKUSU60rUqFxKMiMPwJ7Wgic6aIDFUhWMXhOp8q3crhkODZ +c6tsgLjoC2SToJyMGf+z0gzskSaHirOi4XCPLArlzW1oUevaPwV/izLmE1xr/l9A +4iLItLRkT9a6fUg+qGkM17uGcclzuD87nSVL2v9A6wIDAQABo4IBlTCCAZEwDwYD +VR0TAQH/BAUwAwEB/zCB4QYDVR0gBIHZMIHWMIHTBgkrBgEEAb5YAAMwgcUwgZMG +CCsGAQUFBwICMIGGGoGDQW55IHVzZSBvZiB0aGlzIENlcnRpZmljYXRlIGNvbnN0 +aXR1dGVzIGFjY2VwdGFuY2Ugb2YgdGhlIFF1b1ZhZGlzIFJvb3QgQ0EgMyBDZXJ0 +aWZpY2F0ZSBQb2xpY3kgLyBDZXJ0aWZpY2F0aW9uIFByYWN0aWNlIFN0YXRlbWVu +dC4wLQYIKwYBBQUHAgEWIWh0dHA6Ly93d3cucXVvdmFkaXNnbG9iYWwuY29tL2Nw +czALBgNVHQ8EBAMCAQYwHQYDVR0OBBYEFPLAE+CCQz777i9nMpY1XNu4ywLQMG4G +A1UdIwRnMGWAFPLAE+CCQz777i9nMpY1XNu4ywLQoUmkRzBFMQswCQYDVQQGEwJC +TTEZMBcGA1UEChMQUXVvVmFkaXMgTGltaXRlZDEbMBkGA1UEAxMSUXVvVmFkaXMg +Um9vdCBDQSAzggIFxjANBgkqhkiG9w0BAQUFAAOCAgEAT62gLEz6wPJv92ZVqyM0 +7ucp2sNbtrCD2dDQ4iH782CnO11gUyeim/YIIirnv6By5ZwkajGxkHon24QRiSem +d1o417+shvzuXYO8BsbRd2sPbSQvS3pspweWyuOEn62Iix2rFo1bZhfZFvSLgNLd ++LJ2w/w4E6oM3kJpK27zPOuAJ9v1pkQNn1pVWQvVDVJIxa6f8i+AxeoyUDUSly7B +4f/xI4hROJ/yZlZ25w9Rl6VSDE1JUZU2Pb+iSwwQHYaZTKrzchGT5Or2m9qoXadN +t54CrnMAyNojA+j56hl0YgCUyyIgvpSnWbWCar6ZeXqp8kokUvd0/bpO5qgdAm6x +DYBEwa7TIzdfu4V8K5Iu6H6li92Z4b8nby1dqnuH/grdS/yO9SbkbnBCbjPsMZ57 +k8HkyWkaPcBrTiJt7qtYTcbQQcEr6k8Sh17rRdhs9ZgC06DYVYoGmRmioHfRMJ6s +zHXug/WwYjnPbFfiTNKRCw51KBuav/0aQ/HKd/s7j2G4aSgWQgRecCocIdiP4b0j +Wy10QJLZYxkNc91pvGJHvOB0K7Lrfb5BG7XARsWhIstfTsEokt4YutUqKLsRixeT +mJlglFwjz1onl14LBQaTNx47aTbrqZ5hHY8y2o4M1nQ+ewkk2gF3R8Q7zTSMmfXK +4SVhM7JZG+Ju1zdXtg2pEto= +-----END CERTIFICATE----- + +# Issuer: O=SECOM Trust.net OU=Security Communication RootCA1 +# Subject: O=SECOM Trust.net OU=Security Communication RootCA1 +# Label: "Security Communication Root CA" +# Serial: 0 +# MD5 Fingerprint: f1:bc:63:6a:54:e0:b5:27:f5:cd:e7:1a:e3:4d:6e:4a +# SHA1 Fingerprint: 36:b1:2b:49:f9:81:9e:d7:4c:9e:bc:38:0f:c6:56:8f:5d:ac:b2:f7 +# SHA256 Fingerprint: e7:5e:72:ed:9f:56:0e:ec:6e:b4:80:00:73:a4:3f:c3:ad:19:19:5a:39:22:82:01:78:95:97:4a:99:02:6b:6c +-----BEGIN CERTIFICATE----- +MIIDWjCCAkKgAwIBAgIBADANBgkqhkiG9w0BAQUFADBQMQswCQYDVQQGEwJKUDEY +MBYGA1UEChMPU0VDT00gVHJ1c3QubmV0MScwJQYDVQQLEx5TZWN1cml0eSBDb21t +dW5pY2F0aW9uIFJvb3RDQTEwHhcNMDMwOTMwMDQyMDQ5WhcNMjMwOTMwMDQyMDQ5 +WjBQMQswCQYDVQQGEwJKUDEYMBYGA1UEChMPU0VDT00gVHJ1c3QubmV0MScwJQYD +VQQLEx5TZWN1cml0eSBDb21tdW5pY2F0aW9uIFJvb3RDQTEwggEiMA0GCSqGSIb3 +DQEBAQUAA4IBDwAwggEKAoIBAQCzs/5/022x7xZ8V6UMbXaKL0u/ZPtM7orw8yl8 +9f/uKuDp6bpbZCKamm8sOiZpUQWZJtzVHGpxxpp9Hp3dfGzGjGdnSj74cbAZJ6kJ +DKaVv0uMDPpVmDvY6CKhS3E4eayXkmmziX7qIWgGmBSWh9JhNrxtJ1aeV+7AwFb9 +Ms+k2Y7CI9eNqPPYJayX5HA49LY6tJ07lyZDo6G8SVlyTCMwhwFY9k6+HGhWZq/N +QV3Is00qVUarH9oe4kA92819uZKAnDfdDJZkndwi92SL32HeFZRSFaB9UslLqCHJ +xrHty8OVYNEP8Ktw+N/LTX7s1vqr2b1/VPKl6Xn62dZ2JChzAgMBAAGjPzA9MB0G +A1UdDgQWBBSgc0mZaNyFW2XjmygvV5+9M7wHSDALBgNVHQ8EBAMCAQYwDwYDVR0T +AQH/BAUwAwEB/zANBgkqhkiG9w0BAQUFAAOCAQEAaECpqLvkT115swW1F7NgE+vG +kl3g0dNq/vu+m22/xwVtWSDEHPC32oRYAmP6SBbvT6UL90qY8j+eG61Ha2POCEfr +Uj94nK9NrvjVT8+amCoQQTlSxN3Zmw7vkwGusi7KaEIkQmywszo+zenaSMQVy+n5 +Bw+SUEmK3TGXX8npN6o7WWWXlDLJs58+OmJYxUmtYg5xpTKqL8aJdkNAExNnPaJU +JRDL8Try2frbSVa7pv6nQTXD4IhhyYjH3zYQIphZ6rBK+1YWc26sTfcioU+tHXot +RSflMMFe8toTyyVCUZVHA4xsIcx0Qu1T/zOLjw9XARYvz6buyXAiFL39vmwLAw== +-----END CERTIFICATE----- + +# Issuer: CN=Sonera Class2 CA O=Sonera +# Subject: CN=Sonera Class2 CA O=Sonera +# Label: "Sonera Class 2 Root CA" +# Serial: 29 +# MD5 Fingerprint: a3:ec:75:0f:2e:88:df:fa:48:01:4e:0b:5c:48:6f:fb +# SHA1 Fingerprint: 37:f7:6d:e6:07:7c:90:c5:b1:3e:93:1a:b7:41:10:b4:f2:e4:9a:27 +# SHA256 Fingerprint: 79:08:b4:03:14:c1:38:10:0b:51:8d:07:35:80:7f:fb:fc:f8:51:8a:00:95:33:71:05:ba:38:6b:15:3d:d9:27 +-----BEGIN CERTIFICATE----- +MIIDIDCCAgigAwIBAgIBHTANBgkqhkiG9w0BAQUFADA5MQswCQYDVQQGEwJGSTEP +MA0GA1UEChMGU29uZXJhMRkwFwYDVQQDExBTb25lcmEgQ2xhc3MyIENBMB4XDTAx +MDQwNjA3Mjk0MFoXDTIxMDQwNjA3Mjk0MFowOTELMAkGA1UEBhMCRkkxDzANBgNV +BAoTBlNvbmVyYTEZMBcGA1UEAxMQU29uZXJhIENsYXNzMiBDQTCCASIwDQYJKoZI +hvcNAQEBBQADggEPADCCAQoCggEBAJAXSjWdyvANlsdE+hY3/Ei9vX+ALTU74W+o +Z6m/AxxNjG8yR9VBaKQTBME1DJqEQ/xcHf+Js+gXGM2RX/uJ4+q/Tl18GybTdXnt +5oTjV+WtKcT0OijnpXuENmmz/V52vaMtmdOQTiMofRhj8VQ7Jp12W5dCsv+u8E7s +3TmVToMGf+dJQMjFAbJUWmYdPfz56TwKnoG4cPABi+QjVHzIrviQHgCWctRUz2Ej +vOr7nQKV0ba5cTppCD8PtOFCx4j1P5iop7oc4HFx71hXgVB6XGt0Rg6DA5jDjqhu +8nYybieDwnPz3BjotJPqdURrBGAgcVeHnfO+oJAjPYok4doh28MCAwEAAaMzMDEw +DwYDVR0TAQH/BAUwAwEB/zARBgNVHQ4ECgQISqCqWITTXjwwCwYDVR0PBAQDAgEG +MA0GCSqGSIb3DQEBBQUAA4IBAQBazof5FnIVV0sd2ZvnoiYw7JNn39Yt0jSv9zil +zqsWuasvfDXLrNAPtEwr/IDva4yRXzZ299uzGxnq9LIR/WFxRL8oszodv7ND6J+/ +3DEIcbCdjdY0RzKQxmUk96BKfARzjzlvF4xytb1LyHr4e4PDKE6cCepnP7JnBBvD +FNr450kkkdAdavphOe9r5yF1BgfYErQhIHBCcYHaPJo2vqZbDWpsmh+Re/n570K6 +Tk6ezAyNlNzZRZxe7EJQY670XcSxEtzKO6gunRRaBXW37Ndj4ro1tgQIkejanZz2 +ZrUYrAqmVCY0M9IbwdR/GjqOC6oybtv8TyWf2TLHllpwrN9M +-----END CERTIFICATE----- + +# Issuer: CN=XRamp Global Certification Authority O=XRamp Security Services Inc OU=www.xrampsecurity.com +# Subject: CN=XRamp Global Certification Authority O=XRamp Security Services Inc OU=www.xrampsecurity.com +# Label: "XRamp Global CA Root" +# Serial: 107108908803651509692980124233745014957 +# MD5 Fingerprint: a1:0b:44:b3:ca:10:d8:00:6e:9d:0f:d8:0f:92:0a:d1 +# SHA1 Fingerprint: b8:01:86:d1:eb:9c:86:a5:41:04:cf:30:54:f3:4c:52:b7:e5:58:c6 +# SHA256 Fingerprint: ce:cd:dc:90:50:99:d8:da:df:c5:b1:d2:09:b7:37:cb:e2:c1:8c:fb:2c:10:c0:ff:0b:cf:0d:32:86:fc:1a:a2 +-----BEGIN CERTIFICATE----- +MIIEMDCCAxigAwIBAgIQUJRs7Bjq1ZxN1ZfvdY+grTANBgkqhkiG9w0BAQUFADCB +gjELMAkGA1UEBhMCVVMxHjAcBgNVBAsTFXd3dy54cmFtcHNlY3VyaXR5LmNvbTEk +MCIGA1UEChMbWFJhbXAgU2VjdXJpdHkgU2VydmljZXMgSW5jMS0wKwYDVQQDEyRY +UmFtcCBHbG9iYWwgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwHhcNMDQxMTAxMTcx +NDA0WhcNMzUwMTAxMDUzNzE5WjCBgjELMAkGA1UEBhMCVVMxHjAcBgNVBAsTFXd3 +dy54cmFtcHNlY3VyaXR5LmNvbTEkMCIGA1UEChMbWFJhbXAgU2VjdXJpdHkgU2Vy +dmljZXMgSW5jMS0wKwYDVQQDEyRYUmFtcCBHbG9iYWwgQ2VydGlmaWNhdGlvbiBB +dXRob3JpdHkwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCYJB69FbS6 +38eMpSe2OAtp87ZOqCwuIR1cRN8hXX4jdP5efrRKt6atH67gBhbim1vZZ3RrXYCP +KZ2GG9mcDZhtdhAoWORlsH9KmHmf4MMxfoArtYzAQDsRhtDLooY2YKTVMIJt2W7Q +DxIEM5dfT2Fa8OT5kavnHTu86M/0ay00fOJIYRyO82FEzG+gSqmUsE3a56k0enI4 +qEHMPJQRfevIpoy3hsvKMzvZPTeL+3o+hiznc9cKV6xkmxnr9A8ECIqsAxcZZPRa +JSKNNCyy9mgdEm3Tih4U2sSPpuIjhdV6Db1q4Ons7Be7QhtnqiXtRYMh/MHJfNVi +PvryxS3T/dRlAgMBAAGjgZ8wgZwwEwYJKwYBBAGCNxQCBAYeBABDAEEwCwYDVR0P +BAQDAgGGMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFMZPoj0GY4QJnM5i5ASs +jVy16bYbMDYGA1UdHwQvMC0wK6ApoCeGJWh0dHA6Ly9jcmwueHJhbXBzZWN1cml0 +eS5jb20vWEdDQS5jcmwwEAYJKwYBBAGCNxUBBAMCAQEwDQYJKoZIhvcNAQEFBQAD +ggEBAJEVOQMBG2f7Shz5CmBbodpNl2L5JFMn14JkTpAuw0kbK5rc/Kh4ZzXxHfAR +vbdI4xD2Dd8/0sm2qlWkSLoC295ZLhVbO50WfUfXN+pfTXYSNrsf16GBBEYgoyxt +qZ4Bfj8pzgCT3/3JknOJiWSe5yvkHJEs0rnOfc5vMZnT5r7SHpDwCRR5XCOrTdLa +IR9NmXmd4c8nnxCbHIgNsIpkQTG4DmyQJKSbXHGPurt+HBvbaoAPIbzp26a3QPSy +i6mx5O+aGtA9aZnuqCij4Tyz8LIRnM98QObd50N9otg6tamN8jSZxNQQ4Qb9CYQQ +O+7ETPTsJ3xCwnR8gooJybQDJbw= +-----END CERTIFICATE----- + +# Issuer: O=The Go Daddy Group, Inc. OU=Go Daddy Class 2 Certification Authority +# Subject: O=The Go Daddy Group, Inc. OU=Go Daddy Class 2 Certification Authority +# Label: "Go Daddy Class 2 CA" +# Serial: 0 +# MD5 Fingerprint: 91:de:06:25:ab:da:fd:32:17:0c:bb:25:17:2a:84:67 +# SHA1 Fingerprint: 27:96:ba:e6:3f:18:01:e2:77:26:1b:a0:d7:77:70:02:8f:20:ee:e4 +# SHA256 Fingerprint: c3:84:6b:f2:4b:9e:93:ca:64:27:4c:0e:c6:7c:1e:cc:5e:02:4f:fc:ac:d2:d7:40:19:35:0e:81:fe:54:6a:e4 +-----BEGIN CERTIFICATE----- +MIIEADCCAuigAwIBAgIBADANBgkqhkiG9w0BAQUFADBjMQswCQYDVQQGEwJVUzEh +MB8GA1UEChMYVGhlIEdvIERhZGR5IEdyb3VwLCBJbmMuMTEwLwYDVQQLEyhHbyBE +YWRkeSBDbGFzcyAyIENlcnRpZmljYXRpb24gQXV0aG9yaXR5MB4XDTA0MDYyOTE3 +MDYyMFoXDTM0MDYyOTE3MDYyMFowYzELMAkGA1UEBhMCVVMxITAfBgNVBAoTGFRo +ZSBHbyBEYWRkeSBHcm91cCwgSW5jLjExMC8GA1UECxMoR28gRGFkZHkgQ2xhc3Mg +MiBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTCCASAwDQYJKoZIhvcNAQEBBQADggEN +ADCCAQgCggEBAN6d1+pXGEmhW+vXX0iG6r7d/+TvZxz0ZWizV3GgXne77ZtJ6XCA +PVYYYwhv2vLM0D9/AlQiVBDYsoHUwHU9S3/Hd8M+eKsaA7Ugay9qK7HFiH7Eux6w +wdhFJ2+qN1j3hybX2C32qRe3H3I2TqYXP2WYktsqbl2i/ojgC95/5Y0V4evLOtXi +EqITLdiOr18SPaAIBQi2XKVlOARFmR6jYGB0xUGlcmIbYsUfb18aQr4CUWWoriMY +avx4A6lNf4DD+qta/KFApMoZFv6yyO9ecw3ud72a9nmYvLEHZ6IVDd2gWMZEewo+ +YihfukEHU1jPEX44dMX4/7VpkI+EdOqXG68CAQOjgcAwgb0wHQYDVR0OBBYEFNLE +sNKR1EwRcbNhyz2h/t2oatTjMIGNBgNVHSMEgYUwgYKAFNLEsNKR1EwRcbNhyz2h +/t2oatTjoWekZTBjMQswCQYDVQQGEwJVUzEhMB8GA1UEChMYVGhlIEdvIERhZGR5 +IEdyb3VwLCBJbmMuMTEwLwYDVQQLEyhHbyBEYWRkeSBDbGFzcyAyIENlcnRpZmlj +YXRpb24gQXV0aG9yaXR5ggEAMAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQEFBQAD +ggEBADJL87LKPpH8EsahB4yOd6AzBhRckB4Y9wimPQoZ+YeAEW5p5JYXMP80kWNy +OO7MHAGjHZQopDH2esRU1/blMVgDoszOYtuURXO1v0XJJLXVggKtI3lpjbi2Tc7P +TMozI+gciKqdi0FuFskg5YmezTvacPd+mSYgFFQlq25zheabIZ0KbIIOqPjCDPoQ +HmyW74cNxA9hi63ugyuV+I6ShHI56yDqg+2DzZduCLzrTia2cyvk0/ZM/iZx4mER +dEr/VxqHD3VILs9RaRegAhJhldXRQLIQTO7ErBBDpqWeCtWVYpoNz4iCxTIM5Cuf +ReYNnyicsbkqWletNw+vHX/bvZ8= +-----END CERTIFICATE----- + +# Issuer: O=Starfield Technologies, Inc. OU=Starfield Class 2 Certification Authority +# Subject: O=Starfield Technologies, Inc. OU=Starfield Class 2 Certification Authority +# Label: "Starfield Class 2 CA" +# Serial: 0 +# MD5 Fingerprint: 32:4a:4b:bb:c8:63:69:9b:be:74:9a:c6:dd:1d:46:24 +# SHA1 Fingerprint: ad:7e:1c:28:b0:64:ef:8f:60:03:40:20:14:c3:d0:e3:37:0e:b5:8a +# SHA256 Fingerprint: 14:65:fa:20:53:97:b8:76:fa:a6:f0:a9:95:8e:55:90:e4:0f:cc:7f:aa:4f:b7:c2:c8:67:75:21:fb:5f:b6:58 +-----BEGIN CERTIFICATE----- +MIIEDzCCAvegAwIBAgIBADANBgkqhkiG9w0BAQUFADBoMQswCQYDVQQGEwJVUzEl +MCMGA1UEChMcU3RhcmZpZWxkIFRlY2hub2xvZ2llcywgSW5jLjEyMDAGA1UECxMp +U3RhcmZpZWxkIENsYXNzIDIgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwHhcNMDQw +NjI5MTczOTE2WhcNMzQwNjI5MTczOTE2WjBoMQswCQYDVQQGEwJVUzElMCMGA1UE +ChMcU3RhcmZpZWxkIFRlY2hub2xvZ2llcywgSW5jLjEyMDAGA1UECxMpU3RhcmZp +ZWxkIENsYXNzIDIgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwggEgMA0GCSqGSIb3 +DQEBAQUAA4IBDQAwggEIAoIBAQC3Msj+6XGmBIWtDBFk385N78gDGIc/oav7PKaf +8MOh2tTYbitTkPskpD6E8J7oX+zlJ0T1KKY/e97gKvDIr1MvnsoFAZMej2YcOadN ++lq2cwQlZut3f+dZxkqZJRRU6ybH838Z1TBwj6+wRir/resp7defqgSHo9T5iaU0 +X9tDkYI22WY8sbi5gv2cOj4QyDvvBmVmepsZGD3/cVE8MC5fvj13c7JdBmzDI1aa +K4UmkhynArPkPw2vCHmCuDY96pzTNbO8acr1zJ3o/WSNF4Azbl5KXZnJHoe0nRrA +1W4TNSNe35tfPe/W93bC6j67eA0cQmdrBNj41tpvi/JEoAGrAgEDo4HFMIHCMB0G +A1UdDgQWBBS/X7fRzt0fhvRbVazc1xDCDqmI5zCBkgYDVR0jBIGKMIGHgBS/X7fR +zt0fhvRbVazc1xDCDqmI56FspGowaDELMAkGA1UEBhMCVVMxJTAjBgNVBAoTHFN0 +YXJmaWVsZCBUZWNobm9sb2dpZXMsIEluYy4xMjAwBgNVBAsTKVN0YXJmaWVsZCBD +bGFzcyAyIENlcnRpZmljYXRpb24gQXV0aG9yaXR5ggEAMAwGA1UdEwQFMAMBAf8w +DQYJKoZIhvcNAQEFBQADggEBAAWdP4id0ckaVaGsafPzWdqbAYcaT1epoXkJKtv3 +L7IezMdeatiDh6GX70k1PncGQVhiv45YuApnP+yz3SFmH8lU+nLMPUxA2IGvd56D +eruix/U0F47ZEUD0/CwqTRV/p2JdLiXTAAsgGh1o+Re49L2L7ShZ3U0WixeDyLJl +xy16paq8U4Zt3VekyvggQQto8PT7dL5WXXp59fkdheMtlb71cZBDzI0fmgAKhynp +VSJYACPq4xJDKVtHCN2MQWplBqjlIapBtJUhlbl90TSrE9atvNziPTnNvT51cKEY +WQPJIrSPnNVeKtelttQKbfi3QBFGmh95DmK/D5fs4C8fF5Q= +-----END CERTIFICATE----- + +# Issuer: O=Government Root Certification Authority +# Subject: O=Government Root Certification Authority +# Label: "Taiwan GRCA" +# Serial: 42023070807708724159991140556527066870 +# MD5 Fingerprint: 37:85:44:53:32:45:1f:20:f0:f3:95:e1:25:c4:43:4e +# SHA1 Fingerprint: f4:8b:11:bf:de:ab:be:94:54:20:71:e6:41:de:6b:be:88:2b:40:b9 +# SHA256 Fingerprint: 76:00:29:5e:ef:e8:5b:9e:1f:d6:24:db:76:06:2a:aa:ae:59:81:8a:54:d2:77:4c:d4:c0:b2:c0:11:31:e1:b3 +-----BEGIN CERTIFICATE----- +MIIFcjCCA1qgAwIBAgIQH51ZWtcvwgZEpYAIaeNe9jANBgkqhkiG9w0BAQUFADA/ +MQswCQYDVQQGEwJUVzEwMC4GA1UECgwnR292ZXJubWVudCBSb290IENlcnRpZmlj +YXRpb24gQXV0aG9yaXR5MB4XDTAyMTIwNTEzMjMzM1oXDTMyMTIwNTEzMjMzM1ow +PzELMAkGA1UEBhMCVFcxMDAuBgNVBAoMJ0dvdmVybm1lbnQgUm9vdCBDZXJ0aWZp +Y2F0aW9uIEF1dGhvcml0eTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIB +AJoluOzMonWoe/fOW1mKydGGEghU7Jzy50b2iPN86aXfTEc2pBsBHH8eV4qNw8XR +IePaJD9IK/ufLqGU5ywck9G/GwGHU5nOp/UKIXZ3/6m3xnOUT0b3EEk3+qhZSV1q +gQdW8or5BtD3cCJNtLdBuTK4sfCxw5w/cP1T3YGq2GN49thTbqGsaoQkclSGxtKy +yhwOeYHWtXBiCAEuTk8O1RGvqa/lmr/czIdtJuTJV6L7lvnM4T9TjGxMfptTCAts +F/tnyMKtsc2AtJfcdgEWFelq16TheEfOhtX7MfP6Mb40qij7cEwdScevLJ1tZqa2 +jWR+tSBqnTuBto9AAGdLiYa4zGX+FVPpBMHWXx1E1wovJ5pGfaENda1UhhXcSTvx +ls4Pm6Dso3pdvtUqdULle96ltqqvKKyskKw4t9VoNSZ63Pc78/1Fm9G7Q3hub/FC +VGqY8A2tl+lSXunVanLeavcbYBT0peS2cWeqH+riTcFCQP5nRhc4L0c/cZyu5SHK +YS1tB6iEfC3uUSXxY5Ce/eFXiGvviiNtsea9P63RPZYLhY3Naye7twWb7LuRqQoH +EgKXTiCQ8P8NHuJBO9NAOueNXdpm5AKwB1KYXA6OM5zCppX7VRluTI6uSw+9wThN +Xo+EHWbNxWCWtFJaBYmOlXqYwZE8lSOyDvR5tMl8wUohAgMBAAGjajBoMB0GA1Ud +DgQWBBTMzO/MKWCkO7GStjz6MmKPrCUVOzAMBgNVHRMEBTADAQH/MDkGBGcqBwAE +MTAvMC0CAQAwCQYFKw4DAhoFADAHBgVnKgMAAAQUA5vwIhP/lSg209yewDL7MTqK +UWUwDQYJKoZIhvcNAQEFBQADggIBAECASvomyc5eMN1PhnR2WPWus4MzeKR6dBcZ +TulStbngCnRiqmjKeKBMmo4sIy7VahIkv9Ro04rQ2JyftB8M3jh+Vzj8jeJPXgyf +qzvS/3WXy6TjZwj/5cAWtUgBfen5Cv8b5Wppv3ghqMKnI6mGq3ZW6A4M9hPdKmaK +ZEk9GhiHkASfQlK3T8v+R0F2Ne//AHY2RTKbxkaFXeIksB7jSJaYV0eUVXoPQbFE +JPPB/hprv4j9wabak2BegUqZIJxIZhm1AHlUD7gsL0u8qV1bYH+Mh6XgUmMqvtg7 +hUAV/h62ZT/FS9p+tXo1KaMuephgIqP0fSdOLeq0dDzpD6QzDxARvBMB1uUO07+1 +EqLhRSPAzAhuYbeJq4PjJB7mXQfnHyA+z2fI56wwbSdLaG5LKlwCCDTb+HbkZ6Mm +nD+iMsJKxYEYMRBWqoTvLQr/uB930r+lWKBi5NdLkXWNiYCYfm3LU05er/ayl4WX +udpVBrkk7tfGOB5jGxI7leFYrPLfhNVfmS8NVVvmONsuP3LpSIXLuykTjx44Vbnz +ssQwmSNOXfJIoRIM3BKQCZBUkQM8R+XVyWXgt0t97EfTsws+rZ7QdAAO671RrcDe +LMDDav7v3Aun+kbfYNucpllQdSNpc5Oy+fwC00fmcc4QAu4njIT/rEUNE1yDMuAl +pYYsfPQS +-----END CERTIFICATE----- + +# Issuer: CN=DigiCert Assured ID Root CA O=DigiCert Inc OU=www.digicert.com +# Subject: CN=DigiCert Assured ID Root CA O=DigiCert Inc OU=www.digicert.com +# Label: "DigiCert Assured ID Root CA" +# Serial: 17154717934120587862167794914071425081 +# MD5 Fingerprint: 87:ce:0b:7b:2a:0e:49:00:e1:58:71:9b:37:a8:93:72 +# SHA1 Fingerprint: 05:63:b8:63:0d:62:d7:5a:bb:c8:ab:1e:4b:df:b5:a8:99:b2:4d:43 +# SHA256 Fingerprint: 3e:90:99:b5:01:5e:8f:48:6c:00:bc:ea:9d:11:1e:e7:21:fa:ba:35:5a:89:bc:f1:df:69:56:1e:3d:c6:32:5c +-----BEGIN CERTIFICATE----- +MIIDtzCCAp+gAwIBAgIQDOfg5RfYRv6P5WD8G/AwOTANBgkqhkiG9w0BAQUFADBl +MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3 +d3cuZGlnaWNlcnQuY29tMSQwIgYDVQQDExtEaWdpQ2VydCBBc3N1cmVkIElEIFJv +b3QgQ0EwHhcNMDYxMTEwMDAwMDAwWhcNMzExMTEwMDAwMDAwWjBlMQswCQYDVQQG +EwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3d3cuZGlnaWNl +cnQuY29tMSQwIgYDVQQDExtEaWdpQ2VydCBBc3N1cmVkIElEIFJvb3QgQ0EwggEi +MA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCtDhXO5EOAXLGH87dg+XESpa7c +JpSIqvTO9SA5KFhgDPiA2qkVlTJhPLWxKISKityfCgyDF3qPkKyK53lTXDGEKvYP +mDI2dsze3Tyoou9q+yHyUmHfnyDXH+Kx2f4YZNISW1/5WBg1vEfNoTb5a3/UsDg+ +wRvDjDPZ2C8Y/igPs6eD1sNuRMBhNZYW/lmci3Zt1/GiSw0r/wty2p5g0I6QNcZ4 +VYcgoc/lbQrISXwxmDNsIumH0DJaoroTghHtORedmTpyoeb6pNnVFzF1roV9Iq4/ +AUaG9ih5yLHa5FcXxH4cDrC0kqZWs72yl+2qp/C3xag/lRbQ/6GW6whfGHdPAgMB +AAGjYzBhMA4GA1UdDwEB/wQEAwIBhjAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQW +BBRF66Kv9JLLgjEtUYunpyGd823IDzAfBgNVHSMEGDAWgBRF66Kv9JLLgjEtUYun +pyGd823IDzANBgkqhkiG9w0BAQUFAAOCAQEAog683+Lt8ONyc3pklL/3cmbYMuRC +dWKuh+vy1dneVrOfzM4UKLkNl2BcEkxY5NM9g0lFWJc1aRqoR+pWxnmrEthngYTf +fwk8lOa4JiwgvT2zKIn3X/8i4peEH+ll74fg38FnSbNd67IJKusm7Xi+fT8r87cm +NW1fiQG2SVufAQWbqz0lwcy2f8Lxb4bG+mRo64EtlOtCt/qMHt1i8b5QZ7dsvfPx +H2sMNgcWfzd8qVttevESRmCD1ycEvkvOl77DZypoEd+A5wwzZr8TDRRu838fYxAe ++o0bJW1sj6W3YQGx0qMmoRBxna3iw/nDmVG3KwcIzi7mULKn+gpFL6Lw8g== +-----END CERTIFICATE----- + +# Issuer: CN=DigiCert Global Root CA O=DigiCert Inc OU=www.digicert.com +# Subject: CN=DigiCert Global Root CA O=DigiCert Inc OU=www.digicert.com +# Label: "DigiCert Global Root CA" +# Serial: 10944719598952040374951832963794454346 +# MD5 Fingerprint: 79:e4:a9:84:0d:7d:3a:96:d7:c0:4f:e2:43:4c:89:2e +# SHA1 Fingerprint: a8:98:5d:3a:65:e5:e5:c4:b2:d7:d6:6d:40:c6:dd:2f:b1:9c:54:36 +# SHA256 Fingerprint: 43:48:a0:e9:44:4c:78:cb:26:5e:05:8d:5e:89:44:b4:d8:4f:96:62:bd:26:db:25:7f:89:34:a4:43:c7:01:61 +-----BEGIN CERTIFICATE----- +MIIDrzCCApegAwIBAgIQCDvgVpBCRrGhdWrJWZHHSjANBgkqhkiG9w0BAQUFADBh +MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3 +d3cuZGlnaWNlcnQuY29tMSAwHgYDVQQDExdEaWdpQ2VydCBHbG9iYWwgUm9vdCBD +QTAeFw0wNjExMTAwMDAwMDBaFw0zMTExMTAwMDAwMDBaMGExCzAJBgNVBAYTAlVT +MRUwEwYDVQQKEwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5j +b20xIDAeBgNVBAMTF0RpZ2lDZXJ0IEdsb2JhbCBSb290IENBMIIBIjANBgkqhkiG +9w0BAQEFAAOCAQ8AMIIBCgKCAQEA4jvhEXLeqKTTo1eqUKKPC3eQyaKl7hLOllsB +CSDMAZOnTjC3U/dDxGkAV53ijSLdhwZAAIEJzs4bg7/fzTtxRuLWZscFs3YnFo97 +nh6Vfe63SKMI2tavegw5BmV/Sl0fvBf4q77uKNd0f3p4mVmFaG5cIzJLv07A6Fpt +43C/dxC//AH2hdmoRBBYMql1GNXRor5H4idq9Joz+EkIYIvUX7Q6hL+hqkpMfT7P +T19sdl6gSzeRntwi5m3OFBqOasv+zbMUZBfHWymeMr/y7vrTC0LUq7dBMtoM1O/4 +gdW7jVg/tRvoSSiicNoxBN33shbyTApOB6jtSj1etX+jkMOvJwIDAQABo2MwYTAO +BgNVHQ8BAf8EBAMCAYYwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUA95QNVbR +TLtm8KPiGxvDl7I90VUwHwYDVR0jBBgwFoAUA95QNVbRTLtm8KPiGxvDl7I90VUw +DQYJKoZIhvcNAQEFBQADggEBAMucN6pIExIK+t1EnE9SsPTfrgT1eXkIoyQY/Esr +hMAtudXH/vTBH1jLuG2cenTnmCmrEbXjcKChzUyImZOMkXDiqw8cvpOp/2PV5Adg +06O/nVsJ8dWO41P0jmP6P6fbtGbfYmbW0W5BjfIttep3Sp+dWOIrWcBAI+0tKIJF +PnlUkiaY4IBIqDfv8NZ5YBberOgOzW6sRBc4L0na4UU+Krk2U886UAb3LujEV0ls +YSEY1QSteDwsOoBrp+uvFRTp2InBuThs4pFsiv9kuXclVzDAGySj4dzp30d8tbQk +CAUw7C29C79Fv1C5qfPrmAESrciIxpg0X40KPMbp1ZWVbd4= +-----END CERTIFICATE----- + +# Issuer: CN=DigiCert High Assurance EV Root CA O=DigiCert Inc OU=www.digicert.com +# Subject: CN=DigiCert High Assurance EV Root CA O=DigiCert Inc OU=www.digicert.com +# Label: "DigiCert High Assurance EV Root CA" +# Serial: 3553400076410547919724730734378100087 +# MD5 Fingerprint: d4:74:de:57:5c:39:b2:d3:9c:85:83:c5:c0:65:49:8a +# SHA1 Fingerprint: 5f:b7:ee:06:33:e2:59:db:ad:0c:4c:9a:e6:d3:8f:1a:61:c7:dc:25 +# SHA256 Fingerprint: 74:31:e5:f4:c3:c1:ce:46:90:77:4f:0b:61:e0:54:40:88:3b:a9:a0:1e:d0:0b:a6:ab:d7:80:6e:d3:b1:18:cf +-----BEGIN CERTIFICATE----- +MIIDxTCCAq2gAwIBAgIQAqxcJmoLQJuPC3nyrkYldzANBgkqhkiG9w0BAQUFADBs +MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3 +d3cuZGlnaWNlcnQuY29tMSswKQYDVQQDEyJEaWdpQ2VydCBIaWdoIEFzc3VyYW5j +ZSBFViBSb290IENBMB4XDTA2MTExMDAwMDAwMFoXDTMxMTExMDAwMDAwMFowbDEL +MAkGA1UEBhMCVVMxFTATBgNVBAoTDERpZ2lDZXJ0IEluYzEZMBcGA1UECxMQd3d3 +LmRpZ2ljZXJ0LmNvbTErMCkGA1UEAxMiRGlnaUNlcnQgSGlnaCBBc3N1cmFuY2Ug +RVYgUm9vdCBDQTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAMbM5XPm ++9S75S0tMqbf5YE/yc0lSbZxKsPVlDRnogocsF9ppkCxxLeyj9CYpKlBWTrT3JTW +PNt0OKRKzE0lgvdKpVMSOO7zSW1xkX5jtqumX8OkhPhPYlG++MXs2ziS4wblCJEM +xChBVfvLWokVfnHoNb9Ncgk9vjo4UFt3MRuNs8ckRZqnrG0AFFoEt7oT61EKmEFB +Ik5lYYeBQVCmeVyJ3hlKV9Uu5l0cUyx+mM0aBhakaHPQNAQTXKFx01p8VdteZOE3 +hzBWBOURtCmAEvF5OYiiAhF8J2a3iLd48soKqDirCmTCv2ZdlYTBoSUeh10aUAsg +EsxBu24LUTi4S8sCAwEAAaNjMGEwDgYDVR0PAQH/BAQDAgGGMA8GA1UdEwEB/wQF +MAMBAf8wHQYDVR0OBBYEFLE+w2kD+L9HAdSYJhoIAu9jZCvDMB8GA1UdIwQYMBaA +FLE+w2kD+L9HAdSYJhoIAu9jZCvDMA0GCSqGSIb3DQEBBQUAA4IBAQAcGgaX3Nec +nzyIZgYIVyHbIUf4KmeqvxgydkAQV8GK83rZEWWONfqe/EW1ntlMMUu4kehDLI6z +eM7b41N5cdblIZQB2lWHmiRk9opmzN6cN82oNLFpmyPInngiK3BD41VHMWEZ71jF +hS9OMPagMRYjyOfiZRYzy78aG6A9+MpeizGLYAiJLQwGXFK3xPkKmNEVX58Svnw2 +Yzi9RKR/5CYrCsSXaQ3pjOLAEFe4yHYSkVXySGnYvCoCWw9E1CAx2/S6cCZdkGCe +vEsXCS+0yx5DaMkHJ8HSXPfqIbloEpw8nL+e/IBcm2PN7EeqJSdnoDfzAIJ9VNep ++OkuE6N36B9K +-----END CERTIFICATE----- + +# Issuer: CN=Class 2 Primary CA O=Certplus +# Subject: CN=Class 2 Primary CA O=Certplus +# Label: "Certplus Class 2 Primary CA" +# Serial: 177770208045934040241468760488327595043 +# MD5 Fingerprint: 88:2c:8c:52:b8:a2:3c:f3:f7:bb:03:ea:ae:ac:42:0b +# SHA1 Fingerprint: 74:20:74:41:72:9c:dd:92:ec:79:31:d8:23:10:8d:c2:81:92:e2:bb +# SHA256 Fingerprint: 0f:99:3c:8a:ef:97:ba:af:56:87:14:0e:d5:9a:d1:82:1b:b4:af:ac:f0:aa:9a:58:b5:d5:7a:33:8a:3a:fb:cb +-----BEGIN CERTIFICATE----- +MIIDkjCCAnqgAwIBAgIRAIW9S/PY2uNp9pTXX8OlRCMwDQYJKoZIhvcNAQEFBQAw +PTELMAkGA1UEBhMCRlIxETAPBgNVBAoTCENlcnRwbHVzMRswGQYDVQQDExJDbGFz +cyAyIFByaW1hcnkgQ0EwHhcNOTkwNzA3MTcwNTAwWhcNMTkwNzA2MjM1OTU5WjA9 +MQswCQYDVQQGEwJGUjERMA8GA1UEChMIQ2VydHBsdXMxGzAZBgNVBAMTEkNsYXNz +IDIgUHJpbWFyeSBDQTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBANxQ +ltAS+DXSCHh6tlJw/W/uz7kRy1134ezpfgSN1sxvc0NXYKwzCkTsA18cgCSR5aiR +VhKC9+Ar9NuuYS6JEI1rbLqzAr3VNsVINyPi8Fo3UjMXEuLRYE2+L0ER4/YXJQyL +kcAbmXuZVg2v7tK8R1fjeUl7NIknJITesezpWE7+Tt9avkGtrAjFGA7v0lPubNCd +EgETjdyAYveVqUSISnFOYFWe2yMZeVYHDD9jC1yw4r5+FfyUM1hBOHTE4Y+L3yas +H7WLO7dDWWuwJKZtkIvEcupdM5i3y95ee++U8Rs+yskhwcWYAqqi9lt3m/V+llU0 +HGdpwPFC40es/CgcZlUCAwEAAaOBjDCBiTAPBgNVHRMECDAGAQH/AgEKMAsGA1Ud +DwQEAwIBBjAdBgNVHQ4EFgQU43Mt38sOKAze3bOkynm4jrvoMIkwEQYJYIZIAYb4 +QgEBBAQDAgEGMDcGA1UdHwQwMC4wLKAqoCiGJmh0dHA6Ly93d3cuY2VydHBsdXMu +Y29tL0NSTC9jbGFzczIuY3JsMA0GCSqGSIb3DQEBBQUAA4IBAQCnVM+IRBnL39R/ +AN9WM2K191EBkOvDP9GIROkkXe/nFL0gt5o8AP5tn9uQ3Nf0YtaLcF3n5QRIqWh8 +yfFC82x/xXp8HVGIutIKPidd3i1RTtMTZGnkLuPT55sJmabglZvOGtd/vjzOUrMR +FcEPF80Du5wlFbqidon8BvEY0JNLDnyCt6X09l/+7UCmnYR0ObncHoUW2ikbhiMA +ybuJfm6AiB4vFLQDJKgybwOaRywwvlbGp0ICcBvqQNi6BQNwB6SW//1IMwrh3KWB +kJtN3X3n57LNXMhqlfil9o3EXXgIvnsG1knPGTZQIy4I5p4FTUcY1Rbpsda2ENW7 +l7+ijrRU +-----END CERTIFICATE----- + +# Issuer: CN=DST Root CA X3 O=Digital Signature Trust Co. +# Subject: CN=DST Root CA X3 O=Digital Signature Trust Co. +# Label: "DST Root CA X3" +# Serial: 91299735575339953335919266965803778155 +# MD5 Fingerprint: 41:03:52:dc:0f:f7:50:1b:16:f0:02:8e:ba:6f:45:c5 +# SHA1 Fingerprint: da:c9:02:4f:54:d8:f6:df:94:93:5f:b1:73:26:38:ca:6a:d7:7c:13 +# SHA256 Fingerprint: 06:87:26:03:31:a7:24:03:d9:09:f1:05:e6:9b:cf:0d:32:e1:bd:24:93:ff:c6:d9:20:6d:11:bc:d6:77:07:39 +-----BEGIN CERTIFICATE----- +MIIDSjCCAjKgAwIBAgIQRK+wgNajJ7qJMDmGLvhAazANBgkqhkiG9w0BAQUFADA/ +MSQwIgYDVQQKExtEaWdpdGFsIFNpZ25hdHVyZSBUcnVzdCBDby4xFzAVBgNVBAMT +DkRTVCBSb290IENBIFgzMB4XDTAwMDkzMDIxMTIxOVoXDTIxMDkzMDE0MDExNVow +PzEkMCIGA1UEChMbRGlnaXRhbCBTaWduYXR1cmUgVHJ1c3QgQ28uMRcwFQYDVQQD +Ew5EU1QgUm9vdCBDQSBYMzCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEB +AN+v6ZdQCINXtMxiZfaQguzH0yxrMMpb7NnDfcdAwRgUi+DoM3ZJKuM/IUmTrE4O +rz5Iy2Xu/NMhD2XSKtkyj4zl93ewEnu1lcCJo6m67XMuegwGMoOifooUMM0RoOEq +OLl5CjH9UL2AZd+3UWODyOKIYepLYYHsUmu5ouJLGiifSKOeDNoJjj4XLh7dIN9b +xiqKqy69cK3FCxolkHRyxXtqqzTWMIn/5WgTe1QLyNau7Fqckh49ZLOMxt+/yUFw +7BZy1SbsOFU5Q9D8/RhcQPGX69Wam40dutolucbY38EVAjqr2m7xPi71XAicPNaD +aeQQmxkqtilX4+U9m5/wAl0CAwEAAaNCMEAwDwYDVR0TAQH/BAUwAwEB/zAOBgNV +HQ8BAf8EBAMCAQYwHQYDVR0OBBYEFMSnsaR7LHH62+FLkHX/xBVghYkQMA0GCSqG +SIb3DQEBBQUAA4IBAQCjGiybFwBcqR7uKGY3Or+Dxz9LwwmglSBd49lZRNI+DT69 +ikugdB/OEIKcdBodfpga3csTS7MgROSR6cz8faXbauX+5v3gTt23ADq1cEmv8uXr +AvHRAosZy5Q6XkjEGB5YGV8eAlrwDPGxrancWYaLbumR9YbK+rlmM6pZW87ipxZz +R8srzJmwN0jP41ZL9c8PDHIyh8bwRLtTcm1D9SZImlJnt1ir/md2cXjbDaJWFBM5 +JDGFoqgCWjBH4d1QB7wCCZAA62RjYJsWvIjJEubSfZGL+T0yjWW06XyxV3bqxbYo +Ob8VZRzI9neWagqNdwvYkQsEjgfbKbYK7p2CNTUQ +-----END CERTIFICATE----- + +# Issuer: CN=SwissSign Gold CA - G2 O=SwissSign AG +# Subject: CN=SwissSign Gold CA - G2 O=SwissSign AG +# Label: "SwissSign Gold CA - G2" +# Serial: 13492815561806991280 +# MD5 Fingerprint: 24:77:d9:a8:91:d1:3b:fa:88:2d:c2:ff:f8:cd:33:93 +# SHA1 Fingerprint: d8:c5:38:8a:b7:30:1b:1b:6e:d4:7a:e6:45:25:3a:6f:9f:1a:27:61 +# SHA256 Fingerprint: 62:dd:0b:e9:b9:f5:0a:16:3e:a0:f8:e7:5c:05:3b:1e:ca:57:ea:55:c8:68:8f:64:7c:68:81:f2:c8:35:7b:95 +-----BEGIN CERTIFICATE----- +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 +-----END CERTIFICATE----- + +# Issuer: CN=SwissSign Silver CA - G2 O=SwissSign AG +# Subject: CN=SwissSign Silver CA - G2 O=SwissSign AG +# Label: "SwissSign Silver CA - G2" +# Serial: 5700383053117599563 +# MD5 Fingerprint: e0:06:a1:c9:7d:cf:c9:fc:0d:c0:56:75:96:d8:62:13 +# SHA1 Fingerprint: 9b:aa:e5:9f:56:ee:21:cb:43:5a:be:25:93:df:a7:f0:40:d1:1d:cb +# SHA256 Fingerprint: be:6c:4d:a2:bb:b9:ba:59:b6:f3:93:97:68:37:42:46:c3:c0:05:99:3f:a9:8f:02:0d:1d:ed:be:d4:8a:81:d5 +-----BEGIN CERTIFICATE----- +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 +-----END CERTIFICATE----- + +# Issuer: CN=GeoTrust Primary Certification Authority O=GeoTrust Inc. +# Subject: CN=GeoTrust Primary Certification Authority O=GeoTrust Inc. +# Label: "GeoTrust Primary Certification Authority" +# Serial: 32798226551256963324313806436981982369 +# MD5 Fingerprint: 02:26:c3:01:5e:08:30:37:43:a9:d0:7d:cf:37:e6:bf +# SHA1 Fingerprint: 32:3c:11:8e:1b:f7:b8:b6:52:54:e2:e2:10:0d:d6:02:90:37:f0:96 +# SHA256 Fingerprint: 37:d5:10:06:c5:12:ea:ab:62:64:21:f1:ec:8c:92:01:3f:c5:f8:2a:e9:8e:e5:33:eb:46:19:b8:de:b4:d0:6c +-----BEGIN CERTIFICATE----- +MIIDfDCCAmSgAwIBAgIQGKy1av1pthU6Y2yv2vrEoTANBgkqhkiG9w0BAQUFADBY +MQswCQYDVQQGEwJVUzEWMBQGA1UEChMNR2VvVHJ1c3QgSW5jLjExMC8GA1UEAxMo +R2VvVHJ1c3QgUHJpbWFyeSBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTAeFw0wNjEx +MjcwMDAwMDBaFw0zNjA3MTYyMzU5NTlaMFgxCzAJBgNVBAYTAlVTMRYwFAYDVQQK +Ew1HZW9UcnVzdCBJbmMuMTEwLwYDVQQDEyhHZW9UcnVzdCBQcmltYXJ5IENlcnRp +ZmljYXRpb24gQXV0aG9yaXR5MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKC +AQEAvrgVe//UfH1nrYNke8hCUy3f9oQIIGHWAVlqnEQRr+92/ZV+zmEwu3qDXwK9 +AWbK7hWNb6EwnL2hhZ6UOvNWiAAxz9juapYC2e0DjPt1befquFUWBRaa9OBesYjA +ZIVcFU2Ix7e64HXprQU9nceJSOC7KMgD4TCTZF5SwFlwIjVXiIrxlQqD17wxcwE0 +7e9GceBrAqg1cmuXm2bgyxx5X9gaBGgeRwLmnWDiNpcB3841kt++Z8dtd1k7j53W +kBWUvEI0EME5+bEnPn7WinXFsq+W06Lem+SYvn3h6YGttm/81w7a4DSwDRp35+MI +mO9Y+pyEtzavwt+s0vQQBnBxNQIDAQABo0IwQDAPBgNVHRMBAf8EBTADAQH/MA4G +A1UdDwEB/wQEAwIBBjAdBgNVHQ4EFgQULNVQQZcVi/CPNmFbSvtr2ZnJM5IwDQYJ +KoZIhvcNAQEFBQADggEBAFpwfyzdtzRP9YZRqSa+S7iq8XEN3GHHoOo0Hnp3DwQ1 +6CePbJC/kRYkRj5KTs4rFtULUh38H2eiAkUxT87z+gOneZ1TatnaYzr4gNfTmeGl +4b7UVXGYNTq+k+qurUKykG/g/CFNNWMziUnWm07Kx+dOCQD32sfvmWKZd7aVIl6K +oKv0uHiYyjgZmclynnjNS6yvGaBzEi38wkG6gZHaFloxt/m0cYASSJlyc1pZU8Fj +UjPtp8nSOQJw+uCxQmYpqptR7TBUIhRf2asdweSU8Pj1K/fqynhG1riR/aYNKxoU +AT6A8EKglQdebc3MS6RFjasS6LPeWuWgfOgPIh1a6Vk= +-----END CERTIFICATE----- + +# Issuer: CN=thawte Primary Root CA O=thawte, Inc. OU=Certification Services Division/(c) 2006 thawte, Inc. - For authorized use only +# Subject: CN=thawte Primary Root CA O=thawte, Inc. OU=Certification Services Division/(c) 2006 thawte, Inc. - For authorized use only +# Label: "thawte Primary Root CA" +# Serial: 69529181992039203566298953787712940909 +# MD5 Fingerprint: 8c:ca:dc:0b:22:ce:f5:be:72:ac:41:1a:11:a8:d8:12 +# SHA1 Fingerprint: 91:c6:d6:ee:3e:8a:c8:63:84:e5:48:c2:99:29:5c:75:6c:81:7b:81 +# SHA256 Fingerprint: 8d:72:2f:81:a9:c1:13:c0:79:1d:f1:36:a2:96:6d:b2:6c:95:0a:97:1d:b4:6b:41:99:f4:ea:54:b7:8b:fb:9f +-----BEGIN CERTIFICATE----- +MIIEIDCCAwigAwIBAgIQNE7VVyDV7exJ9C/ON9srbTANBgkqhkiG9w0BAQUFADCB +qTELMAkGA1UEBhMCVVMxFTATBgNVBAoTDHRoYXd0ZSwgSW5jLjEoMCYGA1UECxMf +Q2VydGlmaWNhdGlvbiBTZXJ2aWNlcyBEaXZpc2lvbjE4MDYGA1UECxMvKGMpIDIw +MDYgdGhhd3RlLCBJbmMuIC0gRm9yIGF1dGhvcml6ZWQgdXNlIG9ubHkxHzAdBgNV +BAMTFnRoYXd0ZSBQcmltYXJ5IFJvb3QgQ0EwHhcNMDYxMTE3MDAwMDAwWhcNMzYw +NzE2MjM1OTU5WjCBqTELMAkGA1UEBhMCVVMxFTATBgNVBAoTDHRoYXd0ZSwgSW5j +LjEoMCYGA1UECxMfQ2VydGlmaWNhdGlvbiBTZXJ2aWNlcyBEaXZpc2lvbjE4MDYG +A1UECxMvKGMpIDIwMDYgdGhhd3RlLCBJbmMuIC0gRm9yIGF1dGhvcml6ZWQgdXNl +IG9ubHkxHzAdBgNVBAMTFnRoYXd0ZSBQcmltYXJ5IFJvb3QgQ0EwggEiMA0GCSqG +SIb3DQEBAQUAA4IBDwAwggEKAoIBAQCsoPD7gFnUnMekz52hWXMJEEUMDSxuaPFs +W0hoSVk3/AszGcJ3f8wQLZU0HObrTQmnHNK4yZc2AreJ1CRfBsDMRJSUjQJib+ta +3RGNKJpchJAQeg29dGYvajig4tVUROsdB58Hum/u6f1OCyn1PoSgAfGcq/gcfomk +6KHYcWUNo1F77rzSImANuVud37r8UVsLr5iy6S7pBOhih94ryNdOwUxkHt3Ph1i6 +Sk/KaAcdHJ1KxtUvkcx8cXIcxcBn6zL9yZJclNqFwJu/U30rCfSMnZEfl2pSy94J +NqR32HuHUETVPm4pafs5SSYeCaWAe0At6+gnhcn+Yf1+5nyXHdWdAgMBAAGjQjBA +MA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgEGMB0GA1UdDgQWBBR7W0XP +r87Lev0xkhpqtvNG61dIUDANBgkqhkiG9w0BAQUFAAOCAQEAeRHAS7ORtvzw6WfU +DW5FvlXok9LOAz/t2iWwHVfLHjp2oEzsUHboZHIMpKnxuIvW1oeEuzLlQRHAd9mz +YJ3rG9XRbkREqaYB7FViHXe4XI5ISXycO1cRrK1zN44veFyQaEfZYGDm/Ac9IiAX +xPcW6cTYcvnIc3zfFi8VqT79aie2oetaupgf1eNNZAqdE8hhuvU5HIe6uL17In/2 +/qxAeeWsEG89jxt5dovEN7MhGITlNgDrYyCZuen+MwS7QcjBAvlEYyCegc5C09Y/ +LHbTY5xZ3Y+m4Q6gLkH3LpVHz7z9M/P2C2F+fpErgUfCJzDupxBdN49cOSvkBPB7 +jVaMaA== +-----END CERTIFICATE----- + +# Issuer: CN=VeriSign Class 3 Public Primary Certification Authority - G5 O=VeriSign, Inc. OU=VeriSign Trust Network/(c) 2006 VeriSign, Inc. - For authorized use only +# Subject: CN=VeriSign Class 3 Public Primary Certification Authority - G5 O=VeriSign, Inc. OU=VeriSign Trust Network/(c) 2006 VeriSign, Inc. - For authorized use only +# Label: "VeriSign Class 3 Public Primary Certification Authority - G5" +# Serial: 33037644167568058970164719475676101450 +# MD5 Fingerprint: cb:17:e4:31:67:3e:e2:09:fe:45:57:93:f3:0a:fa:1c +# SHA1 Fingerprint: 4e:b6:d5:78:49:9b:1c:cf:5f:58:1e:ad:56:be:3d:9b:67:44:a5:e5 +# SHA256 Fingerprint: 9a:cf:ab:7e:43:c8:d8:80:d0:6b:26:2a:94:de:ee:e4:b4:65:99:89:c3:d0:ca:f1:9b:af:64:05:e4:1a:b7:df +-----BEGIN CERTIFICATE----- +MIIE0zCCA7ugAwIBAgIQGNrRniZ96LtKIVjNzGs7SjANBgkqhkiG9w0BAQUFADCB +yjELMAkGA1UEBhMCVVMxFzAVBgNVBAoTDlZlcmlTaWduLCBJbmMuMR8wHQYDVQQL +ExZWZXJpU2lnbiBUcnVzdCBOZXR3b3JrMTowOAYDVQQLEzEoYykgMjAwNiBWZXJp +U2lnbiwgSW5jLiAtIEZvciBhdXRob3JpemVkIHVzZSBvbmx5MUUwQwYDVQQDEzxW +ZXJpU2lnbiBDbGFzcyAzIFB1YmxpYyBQcmltYXJ5IENlcnRpZmljYXRpb24gQXV0 +aG9yaXR5IC0gRzUwHhcNMDYxMTA4MDAwMDAwWhcNMzYwNzE2MjM1OTU5WjCByjEL +MAkGA1UEBhMCVVMxFzAVBgNVBAoTDlZlcmlTaWduLCBJbmMuMR8wHQYDVQQLExZW +ZXJpU2lnbiBUcnVzdCBOZXR3b3JrMTowOAYDVQQLEzEoYykgMjAwNiBWZXJpU2ln +biwgSW5jLiAtIEZvciBhdXRob3JpemVkIHVzZSBvbmx5MUUwQwYDVQQDEzxWZXJp +U2lnbiBDbGFzcyAzIFB1YmxpYyBQcmltYXJ5IENlcnRpZmljYXRpb24gQXV0aG9y +aXR5IC0gRzUwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCvJAgIKXo1 +nmAMqudLO07cfLw8RRy7K+D+KQL5VwijZIUVJ/XxrcgxiV0i6CqqpkKzj/i5Vbex +t0uz/o9+B1fs70PbZmIVYc9gDaTY3vjgw2IIPVQT60nKWVSFJuUrjxuf6/WhkcIz +SdhDY2pSS9KP6HBRTdGJaXvHcPaz3BJ023tdS1bTlr8Vd6Gw9KIl8q8ckmcY5fQG +BO+QueQA5N06tRn/Arr0PO7gi+s3i+z016zy9vA9r911kTMZHRxAy3QkGSGT2RT+ +rCpSx4/VBEnkjWNHiDxpg8v+R70rfk/Fla4OndTRQ8Bnc+MUCH7lP59zuDMKz10/ +NIeWiu5T6CUVAgMBAAGjgbIwga8wDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8E +BAMCAQYwbQYIKwYBBQUHAQwEYTBfoV2gWzBZMFcwVRYJaW1hZ2UvZ2lmMCEwHzAH +BgUrDgMCGgQUj+XTGoasjY5rw8+AatRIGCx7GS4wJRYjaHR0cDovL2xvZ28udmVy +aXNpZ24uY29tL3ZzbG9nby5naWYwHQYDVR0OBBYEFH/TZafC3ey78DAJ80M5+gKv +MzEzMA0GCSqGSIb3DQEBBQUAA4IBAQCTJEowX2LP2BqYLz3q3JktvXf2pXkiOOzE +p6B4Eq1iDkVwZMXnl2YtmAl+X6/WzChl8gGqCBpH3vn5fJJaCGkgDdk+bW48DW7Y +5gaRQBi5+MHt39tBquCWIMnNZBU4gcmU7qKEKQsTb47bDN0lAtukixlE0kF6BWlK +WE9gyn6CagsCqiUXObXbf+eEZSqVir2G3l6BFoMtEMze/aiCKm0oHw0LxOXnGiYZ +4fQRbxC1lfznQgUy286dUV4otp6F01vvpX1FQHKOtw5rDgb7MzVIcbidJ4vEZV8N +hnacRHr2lVz2XTIIM6RUthg/aFzyQkqFOFSDX9HoLPKsEdao7WNq +-----END CERTIFICATE----- + +# Issuer: CN=SecureTrust CA O=SecureTrust Corporation +# Subject: CN=SecureTrust CA O=SecureTrust Corporation +# Label: "SecureTrust CA" +# Serial: 17199774589125277788362757014266862032 +# MD5 Fingerprint: dc:32:c3:a7:6d:25:57:c7:68:09:9d:ea:2d:a9:a2:d1 +# SHA1 Fingerprint: 87:82:c6:c3:04:35:3b:cf:d2:96:92:d2:59:3e:7d:44:d9:34:ff:11 +# SHA256 Fingerprint: f1:c1:b5:0a:e5:a2:0d:d8:03:0e:c9:f6:bc:24:82:3d:d3:67:b5:25:57:59:b4:e7:1b:61:fc:e9:f7:37:5d:73 +-----BEGIN CERTIFICATE----- +MIIDuDCCAqCgAwIBAgIQDPCOXAgWpa1Cf/DrJxhZ0DANBgkqhkiG9w0BAQUFADBI +MQswCQYDVQQGEwJVUzEgMB4GA1UEChMXU2VjdXJlVHJ1c3QgQ29ycG9yYXRpb24x +FzAVBgNVBAMTDlNlY3VyZVRydXN0IENBMB4XDTA2MTEwNzE5MzExOFoXDTI5MTIz +MTE5NDA1NVowSDELMAkGA1UEBhMCVVMxIDAeBgNVBAoTF1NlY3VyZVRydXN0IENv +cnBvcmF0aW9uMRcwFQYDVQQDEw5TZWN1cmVUcnVzdCBDQTCCASIwDQYJKoZIhvcN +AQEBBQADggEPADCCAQoCggEBAKukgeWVzfX2FI7CT8rU4niVWJxB4Q2ZQCQXOZEz +Zum+4YOvYlyJ0fwkW2Gz4BERQRwdbvC4u/jep4G6pkjGnx29vo6pQT64lO0pGtSO +0gMdA+9tDWccV9cGrcrI9f4Or2YlSASWC12juhbDCE/RRvgUXPLIXgGZbf2IzIao +wW8xQmxSPmjL8xk037uHGFaAJsTQ3MBv396gwpEWoGQRS0S8Hvbn+mPeZqx2pHGj +7DaUaHp3pLHnDi+BeuK1cobvomuL8A/b01k/unK8RCSc43Oz969XL0Imnal0ugBS +8kvNU3xHCzaFDmapCJcWNFfBZveA4+1wVMeT4C4oFVmHursCAwEAAaOBnTCBmjAT +BgkrBgEEAYI3FAIEBh4EAEMAQTALBgNVHQ8EBAMCAYYwDwYDVR0TAQH/BAUwAwEB +/zAdBgNVHQ4EFgQUQjK2FvoE/f5dS3rD/fdMQB1aQ68wNAYDVR0fBC0wKzApoCeg +JYYjaHR0cDovL2NybC5zZWN1cmV0cnVzdC5jb20vU1RDQS5jcmwwEAYJKwYBBAGC +NxUBBAMCAQAwDQYJKoZIhvcNAQEFBQADggEBADDtT0rhWDpSclu1pqNlGKa7UTt3 +6Z3q059c4EVlew3KW+JwULKUBRSuSceNQQcSc5R+DCMh/bwQf2AQWnL1mA6s7Ll/ +3XpvXdMc9P+IBWlCqQVxyLesJugutIxq/3HcuLHfmbx8IVQr5Fiiu1cprp6poxkm +D5kuCLDv/WnPmRoJjeOnnyvJNjR7JLN4TJUXpAYmHrZkUjZfYGfZnMUFdAvnZyPS +CPyI6a6Lf+Ew9Dd+/cYy2i2eRDAwbO4H3tI0/NL/QPZL9GZGBlSm8jIKYyYwa5vR +3ItHuuG51WLQoqD0ZwV4KWMabwTW+MZMo5qxN7SN5ShLHZ4swrhovO0C7jE= +-----END CERTIFICATE----- + +# Issuer: CN=Secure Global CA O=SecureTrust Corporation +# Subject: CN=Secure Global CA O=SecureTrust Corporation +# Label: "Secure Global CA" +# Serial: 9751836167731051554232119481456978597 +# MD5 Fingerprint: cf:f4:27:0d:d4:ed:dc:65:16:49:6d:3d:da:bf:6e:de +# SHA1 Fingerprint: 3a:44:73:5a:e5:81:90:1f:24:86:61:46:1e:3b:9c:c4:5f:f5:3a:1b +# SHA256 Fingerprint: 42:00:f5:04:3a:c8:59:0e:bb:52:7d:20:9e:d1:50:30:29:fb:cb:d4:1c:a1:b5:06:ec:27:f1:5a:de:7d:ac:69 +-----BEGIN CERTIFICATE----- +MIIDvDCCAqSgAwIBAgIQB1YipOjUiolN9BPI8PjqpTANBgkqhkiG9w0BAQUFADBK +MQswCQYDVQQGEwJVUzEgMB4GA1UEChMXU2VjdXJlVHJ1c3QgQ29ycG9yYXRpb24x +GTAXBgNVBAMTEFNlY3VyZSBHbG9iYWwgQ0EwHhcNMDYxMTA3MTk0MjI4WhcNMjkx +MjMxMTk1MjA2WjBKMQswCQYDVQQGEwJVUzEgMB4GA1UEChMXU2VjdXJlVHJ1c3Qg +Q29ycG9yYXRpb24xGTAXBgNVBAMTEFNlY3VyZSBHbG9iYWwgQ0EwggEiMA0GCSqG +SIb3DQEBAQUAA4IBDwAwggEKAoIBAQCvNS7YrGxVaQZx5RNoJLNP2MwhR/jxYDiJ +iQPpvepeRlMJ3Fz1Wuj3RSoC6zFh1ykzTM7HfAo3fg+6MpjhHZevj8fcyTiW89sa +/FHtaMbQbqR8JNGuQsiWUGMu4P51/pinX0kuleM5M2SOHqRfkNJnPLLZ/kG5VacJ +jnIFHovdRIWCQtBJwB1g8NEXLJXr9qXBkqPFwqcIYA1gBBCWeZ4WNOaptvolRTnI +HmX5k/Wq8VLcmZg9pYYaDDUz+kulBAYVHDGA76oYa8J719rO+TMg1fW9ajMtgQT7 +sFzUnKPiXB3jqUJ1XnvUd+85VLrJChgbEplJL4hL/VBi0XPnj3pDAgMBAAGjgZ0w +gZowEwYJKwYBBAGCNxQCBAYeBABDAEEwCwYDVR0PBAQDAgGGMA8GA1UdEwEB/wQF +MAMBAf8wHQYDVR0OBBYEFK9EBMJBfkiD2045AuzshHrmzsmkMDQGA1UdHwQtMCsw +KaAnoCWGI2h0dHA6Ly9jcmwuc2VjdXJldHJ1c3QuY29tL1NHQ0EuY3JsMBAGCSsG +AQQBgjcVAQQDAgEAMA0GCSqGSIb3DQEBBQUAA4IBAQBjGghAfaReUw132HquHw0L +URYD7xh8yOOvaliTFGCRsoTciE6+OYo68+aCiV0BN7OrJKQVDpI1WkpEXk5X+nXO +H0jOZvQ8QCaSmGwb7iRGDBezUqXbpZGRzzfTb+cnCDpOGR86p1hcF895P4vkp9Mm +I50mD1hp/Ed+stCNi5O/KU9DaXR2Z0vPB4zmAve14bRDtUstFJ/53CYNv6ZHdAbY +iNE6KTCEztI5gGIbqMdXSbxqVVFnFUq+NQfk1XWYN3kwFNspnWzFacxHVaIw98xc +f8LDmBxrThaA63p4ZUWiABqvDA1VZDRIuJK58bRQKfJPIx/abKwfROHdI3hRW8cW +-----END CERTIFICATE----- + +# Issuer: CN=COMODO Certification Authority O=COMODO CA Limited +# Subject: CN=COMODO Certification Authority O=COMODO CA Limited +# Label: "COMODO Certification Authority" +# Serial: 104350513648249232941998508985834464573 +# MD5 Fingerprint: 5c:48:dc:f7:42:72:ec:56:94:6d:1c:cc:71:35:80:75 +# SHA1 Fingerprint: 66:31:bf:9e:f7:4f:9e:b6:c9:d5:a6:0c:ba:6a:be:d1:f7:bd:ef:7b +# SHA256 Fingerprint: 0c:2c:d6:3d:f7:80:6f:a3:99:ed:e8:09:11:6b:57:5b:f8:79:89:f0:65:18:f9:80:8c:86:05:03:17:8b:af:66 +-----BEGIN CERTIFICATE----- +MIIEHTCCAwWgAwIBAgIQToEtioJl4AsC7j41AkblPTANBgkqhkiG9w0BAQUFADCB +gTELMAkGA1UEBhMCR0IxGzAZBgNVBAgTEkdyZWF0ZXIgTWFuY2hlc3RlcjEQMA4G +A1UEBxMHU2FsZm9yZDEaMBgGA1UEChMRQ09NT0RPIENBIExpbWl0ZWQxJzAlBgNV +BAMTHkNPTU9ETyBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTAeFw0wNjEyMDEwMDAw +MDBaFw0yOTEyMzEyMzU5NTlaMIGBMQswCQYDVQQGEwJHQjEbMBkGA1UECBMSR3Jl +YXRlciBNYW5jaGVzdGVyMRAwDgYDVQQHEwdTYWxmb3JkMRowGAYDVQQKExFDT01P +RE8gQ0EgTGltaXRlZDEnMCUGA1UEAxMeQ09NT0RPIENlcnRpZmljYXRpb24gQXV0 +aG9yaXR5MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA0ECLi3LjkRv3 +UcEbVASY06m/weaKXTuH+7uIzg3jLz8GlvCiKVCZrts7oVewdFFxze1CkU1B/qnI +2GqGd0S7WWaXUF601CxwRM/aN5VCaTwwxHGzUvAhTaHYujl8HJ6jJJ3ygxaYqhZ8 +Q5sVW7euNJH+1GImGEaaP+vB+fGQV+useg2L23IwambV4EajcNxo2f8ESIl33rXp ++2dtQem8Ob0y2WIC8bGoPW43nOIv4tOiJovGuFVDiOEjPqXSJDlqR6sA1KGzqSX+ +DT+nHbrTUcELpNqsOO9VUCQFZUaTNE8tja3G1CEZ0o7KBWFxB3NH5YoZEr0ETc5O +nKVIrLsm9wIDAQABo4GOMIGLMB0GA1UdDgQWBBQLWOWLxkwVN6RAqTCpIb5HNlpW +/zAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB/zBJBgNVHR8EQjBAMD6g +PKA6hjhodHRwOi8vY3JsLmNvbW9kb2NhLmNvbS9DT01PRE9DZXJ0aWZpY2F0aW9u +QXV0aG9yaXR5LmNybDANBgkqhkiG9w0BAQUFAAOCAQEAPpiem/Yb6dc5t3iuHXIY +SdOH5EOC6z/JqvWote9VfCFSZfnVDeFs9D6Mk3ORLgLETgdxb8CPOGEIqB6BCsAv +IC9Bi5HcSEW88cbeunZrM8gALTFGTO3nnc+IlP8zwFboJIYmuNg4ON8qa90SzMc/ +RxdMosIGlgnW2/4/PEZB31jiVg88O8EckzXZOFKs7sjsLjBOlDW0JB9LeGna8gI4 +zJVSk/BwJVmcIGfE7vmLV2H0knZ9P4SNVbfo5azV8fUZVqZa+5Acr5Pr5RzUZ5dd +BA6+C4OmF4O5MBKgxTMVBbkN+8cFduPYSo38NBejxiEovjBFMR7HeL5YYTisO+IB +ZQ== +-----END CERTIFICATE----- + +# Issuer: CN=Network Solutions Certificate Authority O=Network Solutions L.L.C. +# Subject: CN=Network Solutions Certificate Authority O=Network Solutions L.L.C. +# Label: "Network Solutions Certificate Authority" +# Serial: 116697915152937497490437556386812487904 +# MD5 Fingerprint: d3:f3:a6:16:c0:fa:6b:1d:59:b1:2d:96:4d:0e:11:2e +# SHA1 Fingerprint: 74:f8:a3:c3:ef:e7:b3:90:06:4b:83:90:3c:21:64:60:20:e5:df:ce +# SHA256 Fingerprint: 15:f0:ba:00:a3:ac:7a:f3:ac:88:4c:07:2b:10:11:a0:77:bd:77:c0:97:f4:01:64:b2:f8:59:8a:bd:83:86:0c +-----BEGIN CERTIFICATE----- +MIID5jCCAs6gAwIBAgIQV8szb8JcFuZHFhfjkDFo4DANBgkqhkiG9w0BAQUFADBi +MQswCQYDVQQGEwJVUzEhMB8GA1UEChMYTmV0d29yayBTb2x1dGlvbnMgTC5MLkMu +MTAwLgYDVQQDEydOZXR3b3JrIFNvbHV0aW9ucyBDZXJ0aWZpY2F0ZSBBdXRob3Jp +dHkwHhcNMDYxMjAxMDAwMDAwWhcNMjkxMjMxMjM1OTU5WjBiMQswCQYDVQQGEwJV +UzEhMB8GA1UEChMYTmV0d29yayBTb2x1dGlvbnMgTC5MLkMuMTAwLgYDVQQDEydO +ZXR3b3JrIFNvbHV0aW9ucyBDZXJ0aWZpY2F0ZSBBdXRob3JpdHkwggEiMA0GCSqG +SIb3DQEBAQUAA4IBDwAwggEKAoIBAQDkvH6SMG3G2I4rC7xGzuAnlt7e+foS0zwz +c7MEL7xxjOWftiJgPl9dzgn/ggwbmlFQGiaJ3dVhXRncEg8tCqJDXRfQNJIg6nPP +OCwGJgl6cvf6UDL4wpPTaaIjzkGxzOTVHzbRijr4jGPiFFlp7Q3Tf2vouAPlT2rl +mGNpSAW+Lv8ztumXWWn4Zxmuk2GWRBXTcrA/vGp97Eh/jcOrqnErU2lBUzS1sLnF +BgrEsEX1QV1uiUV7PTsmjHTC5dLRfbIR1PtYMiKagMnc/Qzpf14Dl847ABSHJ3A4 +qY5usyd2mFHgBeMhqxrVhSI8KbWaFsWAqPS7azCPL0YCorEMIuDTAgMBAAGjgZcw +gZQwHQYDVR0OBBYEFCEwyfsA106Y2oeqKtCnLrFAMadMMA4GA1UdDwEB/wQEAwIB +BjAPBgNVHRMBAf8EBTADAQH/MFIGA1UdHwRLMEkwR6BFoEOGQWh0dHA6Ly9jcmwu +bmV0c29sc3NsLmNvbS9OZXR3b3JrU29sdXRpb25zQ2VydGlmaWNhdGVBdXRob3Jp +dHkuY3JsMA0GCSqGSIb3DQEBBQUAA4IBAQC7rkvnt1frf6ott3NHhWrB5KUd5Oc8 +6fRZZXe1eltajSU24HqXLjjAV2CDmAaDn7l2em5Q4LqILPxFzBiwmZVRDuwduIj/ +h1AcgsLj4DKAv6ALR8jDMe+ZZzKATxcheQxpXN5eNK4CtSbqUN9/GGUsyfJj4akH +/nxxH2szJGoeBfcFaMBqEssuXmHLrijTfsK0ZpEmXzwuJF/LWA/rKOyvEZbz3Htv +wKeI8lN3s2Berq4o2jUsbzRF0ybh3uxbTydrFny9RAQYgrOJeRcQcT16ohZO9QHN +pGxlaKFJdlxDydi8NmdspZS11My5vWo1ViHe2MPr+8ukYEywVaCge1ey +-----END CERTIFICATE----- + +# Issuer: CN=COMODO ECC Certification Authority O=COMODO CA Limited +# Subject: CN=COMODO ECC Certification Authority O=COMODO CA Limited +# Label: "COMODO ECC Certification Authority" +# Serial: 41578283867086692638256921589707938090 +# MD5 Fingerprint: 7c:62:ff:74:9d:31:53:5e:68:4a:d5:78:aa:1e:bf:23 +# SHA1 Fingerprint: 9f:74:4e:9f:2b:4d:ba:ec:0f:31:2c:50:b6:56:3b:8e:2d:93:c3:11 +# SHA256 Fingerprint: 17:93:92:7a:06:14:54:97:89:ad:ce:2f:8f:34:f7:f0:b6:6d:0f:3a:e3:a3:b8:4d:21:ec:15:db:ba:4f:ad:c7 +-----BEGIN CERTIFICATE----- +MIICiTCCAg+gAwIBAgIQH0evqmIAcFBUTAGem2OZKjAKBggqhkjOPQQDAzCBhTEL +MAkGA1UEBhMCR0IxGzAZBgNVBAgTEkdyZWF0ZXIgTWFuY2hlc3RlcjEQMA4GA1UE +BxMHU2FsZm9yZDEaMBgGA1UEChMRQ09NT0RPIENBIExpbWl0ZWQxKzApBgNVBAMT +IkNPTU9ETyBFQ0MgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwHhcNMDgwMzA2MDAw +MDAwWhcNMzgwMTE4MjM1OTU5WjCBhTELMAkGA1UEBhMCR0IxGzAZBgNVBAgTEkdy +ZWF0ZXIgTWFuY2hlc3RlcjEQMA4GA1UEBxMHU2FsZm9yZDEaMBgGA1UEChMRQ09N +T0RPIENBIExpbWl0ZWQxKzApBgNVBAMTIkNPTU9ETyBFQ0MgQ2VydGlmaWNhdGlv +biBBdXRob3JpdHkwdjAQBgcqhkjOPQIBBgUrgQQAIgNiAAQDR3svdcmCFYX7deSR +FtSrYpn1PlILBs5BAH+X4QokPB0BBO490o0JlwzgdeT6+3eKKvUDYEs2ixYjFq0J +cfRK9ChQtP6IHG4/bC8vCVlbpVsLM5niwz2J+Wos77LTBumjQjBAMB0GA1UdDgQW +BBR1cacZSBm8nZ3qQUfflMRId5nTeTAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/ +BAUwAwEB/zAKBggqhkjOPQQDAwNoADBlAjEA7wNbeqy3eApyt4jf/7VGFAkK+qDm +fQjGGoe9GKhzvSbKYAydzpmfz1wPMOG+FDHqAjAU9JM8SaczepBGR7NjfRObTrdv +GDeAU/7dIOA1mjbRxwG55tzd8/8dLDoWV9mSOdY= +-----END CERTIFICATE----- + +# Issuer: CN=OISTE WISeKey Global Root GA CA O=WISeKey OU=Copyright (c) 2005/OISTE Foundation Endorsed +# Subject: CN=OISTE WISeKey Global Root GA CA O=WISeKey OU=Copyright (c) 2005/OISTE Foundation Endorsed +# Label: "OISTE WISeKey Global Root GA CA" +# Serial: 86718877871133159090080555911823548314 +# MD5 Fingerprint: bc:6c:51:33:a7:e9:d3:66:63:54:15:72:1b:21:92:93 +# SHA1 Fingerprint: 59:22:a1:e1:5a:ea:16:35:21:f8:98:39:6a:46:46:b0:44:1b:0f:a9 +# SHA256 Fingerprint: 41:c9:23:86:6a:b4:ca:d6:b7:ad:57:80:81:58:2e:02:07:97:a6:cb:df:4f:ff:78:ce:83:96:b3:89:37:d7:f5 +-----BEGIN CERTIFICATE----- +MIID8TCCAtmgAwIBAgIQQT1yx/RrH4FDffHSKFTfmjANBgkqhkiG9w0BAQUFADCB +ijELMAkGA1UEBhMCQ0gxEDAOBgNVBAoTB1dJU2VLZXkxGzAZBgNVBAsTEkNvcHly +aWdodCAoYykgMjAwNTEiMCAGA1UECxMZT0lTVEUgRm91bmRhdGlvbiBFbmRvcnNl +ZDEoMCYGA1UEAxMfT0lTVEUgV0lTZUtleSBHbG9iYWwgUm9vdCBHQSBDQTAeFw0w +NTEyMTExNjAzNDRaFw0zNzEyMTExNjA5NTFaMIGKMQswCQYDVQQGEwJDSDEQMA4G +A1UEChMHV0lTZUtleTEbMBkGA1UECxMSQ29weXJpZ2h0IChjKSAyMDA1MSIwIAYD +VQQLExlPSVNURSBGb3VuZGF0aW9uIEVuZG9yc2VkMSgwJgYDVQQDEx9PSVNURSBX +SVNlS2V5IEdsb2JhbCBSb290IEdBIENBMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8A +MIIBCgKCAQEAy0+zAJs9Nt350UlqaxBJH+zYK7LG+DKBKUOVTJoZIyEVRd7jyBxR +VVuuk+g3/ytr6dTqvirdqFEr12bDYVxgAsj1znJ7O7jyTmUIms2kahnBAbtzptf2 +w93NvKSLtZlhuAGio9RN1AU9ka34tAhxZK9w8RxrfvbDd50kc3vkDIzh2TbhmYsF +mQvtRTEJysIA2/dyoJaqlYfQjse2YXMNdmaM3Bu0Y6Kff5MTMPGhJ9vZ/yxViJGg +4E8HsChWjBgbl0SOid3gF27nKu+POQoxhILYQBRJLnpB5Kf+42TMwVlxSywhp1t9 +4B3RLoGbw9ho972WG6xwsRYUC9tguSYBBQIDAQABo1EwTzALBgNVHQ8EBAMCAYYw +DwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUswN+rja8sHnR3JQmthG+IbJphpQw +EAYJKwYBBAGCNxUBBAMCAQAwDQYJKoZIhvcNAQEFBQADggEBAEuh/wuHbrP5wUOx +SPMowB0uyQlB+pQAHKSkq0lPjz0e701vvbyk9vImMMkQyh2I+3QZH4VFvbBsUfk2 +ftv1TDI6QU9bR8/oCy22xBmddMVHxjtqD6wU2zz0c5ypBd8A3HR4+vg1YFkCExh8 +vPtNsCBtQ7tgMHpnM1zFmdH4LTlSc/uMqpclXHLZCB6rTjzjgTGfA6b7wP4piFXa +hNVQA7bihKOmNqoROgHhGEvWRGizPflTdISzRpFGlgC3gCy24eMQ4tui5yiPAZZi +Fj4A4xylNoEYokxSdsARo27mHbrjWr42U8U+dY+GaSlYU7Wcu2+fXMUY7N0v4ZjJ +/L7fCg0= +-----END CERTIFICATE----- + +# Issuer: CN=Certigna O=Dhimyotis +# Subject: CN=Certigna O=Dhimyotis +# Label: "Certigna" +# Serial: 18364802974209362175 +# MD5 Fingerprint: ab:57:a6:5b:7d:42:82:19:b5:d8:58:26:28:5e:fd:ff +# SHA1 Fingerprint: b1:2e:13:63:45:86:a4:6f:1a:b2:60:68:37:58:2d:c4:ac:fd:94:97 +# SHA256 Fingerprint: e3:b6:a2:db:2e:d7:ce:48:84:2f:7a:c5:32:41:c7:b7:1d:54:14:4b:fb:40:c1:1f:3f:1d:0b:42:f5:ee:a1:2d +-----BEGIN CERTIFICATE----- +MIIDqDCCApCgAwIBAgIJAP7c4wEPyUj/MA0GCSqGSIb3DQEBBQUAMDQxCzAJBgNV +BAYTAkZSMRIwEAYDVQQKDAlEaGlteW90aXMxETAPBgNVBAMMCENlcnRpZ25hMB4X +DTA3MDYyOTE1MTMwNVoXDTI3MDYyOTE1MTMwNVowNDELMAkGA1UEBhMCRlIxEjAQ +BgNVBAoMCURoaW15b3RpczERMA8GA1UEAwwIQ2VydGlnbmEwggEiMA0GCSqGSIb3 +DQEBAQUAA4IBDwAwggEKAoIBAQDIaPHJ1tazNHUmgh7stL7qXOEm7RFHYeGifBZ4 +QCHkYJ5ayGPhxLGWkv8YbWkj4Sti993iNi+RB7lIzw7sebYs5zRLcAglozyHGxny +gQcPOJAZ0xH+hrTy0V4eHpbNgGzOOzGTtvKg0KmVEn2lmsxryIRWijOp5yIVUxbw +zBfsV1/pogqYCd7jX5xv3EjjhQsVWqa6n6xI4wmy9/Qy3l40vhx4XUJbzg4ij02Q +130yGLMLLGq/jj8UEYkgDncUtT2UCIf3JR7VsmAA7G8qKCVuKj4YYxclPz5EIBb2 +JsglrgVKtOdjLPOMFlN+XPsRGgjBRmKfIrjxwo1p3Po6WAbfAgMBAAGjgbwwgbkw +DwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUGu3+QTmQtCRZvgHyUtVF9lo53BEw +ZAYDVR0jBF0wW4AUGu3+QTmQtCRZvgHyUtVF9lo53BGhOKQ2MDQxCzAJBgNVBAYT +AkZSMRIwEAYDVQQKDAlEaGlteW90aXMxETAPBgNVBAMMCENlcnRpZ25hggkA/tzj +AQ/JSP8wDgYDVR0PAQH/BAQDAgEGMBEGCWCGSAGG+EIBAQQEAwIABzANBgkqhkiG +9w0BAQUFAAOCAQEAhQMeknH2Qq/ho2Ge6/PAD/Kl1NqV5ta+aDY9fm4fTIrv0Q8h +bV6lUmPOEvjvKtpv6zf+EwLHyzs+ImvaYS5/1HI93TDhHkxAGYwP15zRgzB7mFnc +fca5DClMoTOi62c6ZYTTluLtdkVwj7Ur3vkj1kluPBS1xp81HlDQwY9qcEQCYsuu +HWhBp6pX6FOqB9IG9tUUBguRA3UsbHK1YZWaDYu5Def131TN3ubY1gkIl2PlwS6w +t0QmwCbAr1UwnjvVNioZBPRcHv/PLLf/0P2HQBHVESO7SMAhqaQoLf0V+LBOK/Qw +WyH8EZE0vkHve52Xdf+XlcCWWC/qu0bXu+TZLg== +-----END CERTIFICATE----- + +# Issuer: CN=Deutsche Telekom Root CA 2 O=Deutsche Telekom AG OU=T-TeleSec Trust Center +# Subject: CN=Deutsche Telekom Root CA 2 O=Deutsche Telekom AG OU=T-TeleSec Trust Center +# Label: "Deutsche Telekom Root CA 2" +# Serial: 38 +# MD5 Fingerprint: 74:01:4a:91:b1:08:c4:58:ce:47:cd:f0:dd:11:53:08 +# SHA1 Fingerprint: 85:a4:08:c0:9c:19:3e:5d:51:58:7d:cd:d6:13:30:fd:8c:de:37:bf +# SHA256 Fingerprint: b6:19:1a:50:d0:c3:97:7f:7d:a9:9b:cd:aa:c8:6a:22:7d:ae:b9:67:9e:c7:0b:a3:b0:c9:d9:22:71:c1:70:d3 +-----BEGIN CERTIFICATE----- +MIIDnzCCAoegAwIBAgIBJjANBgkqhkiG9w0BAQUFADBxMQswCQYDVQQGEwJERTEc +MBoGA1UEChMTRGV1dHNjaGUgVGVsZWtvbSBBRzEfMB0GA1UECxMWVC1UZWxlU2Vj +IFRydXN0IENlbnRlcjEjMCEGA1UEAxMaRGV1dHNjaGUgVGVsZWtvbSBSb290IENB +IDIwHhcNOTkwNzA5MTIxMTAwWhcNMTkwNzA5MjM1OTAwWjBxMQswCQYDVQQGEwJE +RTEcMBoGA1UEChMTRGV1dHNjaGUgVGVsZWtvbSBBRzEfMB0GA1UECxMWVC1UZWxl +U2VjIFRydXN0IENlbnRlcjEjMCEGA1UEAxMaRGV1dHNjaGUgVGVsZWtvbSBSb290 +IENBIDIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCrC6M14IspFLEU +ha88EOQ5bzVdSq7d6mGNlUn0b2SjGmBmpKlAIoTZ1KXleJMOaAGtuU1cOs7TuKhC +QN/Po7qCWWqSG6wcmtoIKyUn+WkjR/Hg6yx6m/UTAtB+NHzCnjwAWav12gz1Mjwr +rFDa1sPeg5TKqAyZMg4ISFZbavva4VhYAUlfckE8FQYBjl2tqriTtM2e66foai1S +NNs671x1Udrb8zH57nGYMsRUFUQM+ZtV7a3fGAigo4aKSe5TBY8ZTNXeWHmb0moc +QqvF1afPaA+W5OFhmHZhyJF81j4A4pFQh+GdCuatl9Idxjp9y7zaAzTVjlsB9WoH +txa2bkp/AgMBAAGjQjBAMB0GA1UdDgQWBBQxw3kbuvVT1xfgiXotF2wKsyudMzAP +BgNVHRMECDAGAQH/AgEFMA4GA1UdDwEB/wQEAwIBBjANBgkqhkiG9w0BAQUFAAOC +AQEAlGRZrTlk5ynrE/5aw4sTV8gEJPB0d8Bg42f76Ymmg7+Wgnxu1MM9756Abrsp +tJh6sTtU6zkXR34ajgv8HzFZMQSyzhfzLMdiNlXiItiJVbSYSKpk+tYcNthEeFpa +IzpXl/V6ME+un2pMSyuOoAPjPuCp1NJ70rOo4nI8rZ7/gFnkm0W09juwzTkZmDLl +6iFhkOQxIY40sfcvNUqFENrnijchvllj4PKFiDFT1FQUhXB59C4Gdyd1Lx+4ivn+ +xbrYNuSD7Odlt79jWvNGr4GUN9RBjNYj1h7P9WgbRGOiWrqnNVmh5XAFmw4jV5mU +Cm26OWMohpLzGITY+9HPBVZkVw== +-----END CERTIFICATE----- + +# Issuer: CN=Cybertrust Global Root O=Cybertrust, Inc +# Subject: CN=Cybertrust Global Root O=Cybertrust, Inc +# Label: "Cybertrust Global Root" +# Serial: 4835703278459682877484360 +# MD5 Fingerprint: 72:e4:4a:87:e3:69:40:80:77:ea:bc:e3:f4:ff:f0:e1 +# SHA1 Fingerprint: 5f:43:e5:b1:bf:f8:78:8c:ac:1c:c7:ca:4a:9a:c6:22:2b:cc:34:c6 +# SHA256 Fingerprint: 96:0a:df:00:63:e9:63:56:75:0c:29:65:dd:0a:08:67:da:0b:9c:bd:6e:77:71:4a:ea:fb:23:49:ab:39:3d:a3 +-----BEGIN CERTIFICATE----- +MIIDoTCCAomgAwIBAgILBAAAAAABD4WqLUgwDQYJKoZIhvcNAQEFBQAwOzEYMBYG +A1UEChMPQ3liZXJ0cnVzdCwgSW5jMR8wHQYDVQQDExZDeWJlcnRydXN0IEdsb2Jh +bCBSb290MB4XDTA2MTIxNTA4MDAwMFoXDTIxMTIxNTA4MDAwMFowOzEYMBYGA1UE +ChMPQ3liZXJ0cnVzdCwgSW5jMR8wHQYDVQQDExZDeWJlcnRydXN0IEdsb2JhbCBS +b290MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA+Mi8vRRQZhP/8NN5 +7CPytxrHjoXxEnOmGaoQ25yiZXRadz5RfVb23CO21O1fWLE3TdVJDm71aofW0ozS +J8bi/zafmGWgE07GKmSb1ZASzxQG9Dvj1Ci+6A74q05IlG2OlTEQXO2iLb3VOm2y +HLtgwEZLAfVJrn5GitB0jaEMAs7u/OePuGtm839EAL9mJRQr3RAwHQeWP032a7iP +t3sMpTjr3kfb1V05/Iin89cqdPHoWqI7n1C6poxFNcJQZZXcY4Lv3b93TZxiyWNz +FtApD0mpSPCzqrdsxacwOUBdrsTiXSZT8M4cIwhhqJQZugRiQOwfOHB3EgZxpzAY +XSUnpQIDAQABo4GlMIGiMA4GA1UdDwEB/wQEAwIBBjAPBgNVHRMBAf8EBTADAQH/ +MB0GA1UdDgQWBBS2CHsNesysIEyGVjJez6tuhS1wVzA/BgNVHR8EODA2MDSgMqAw +hi5odHRwOi8vd3d3Mi5wdWJsaWMtdHJ1c3QuY29tL2NybC9jdC9jdHJvb3QuY3Js +MB8GA1UdIwQYMBaAFLYIew16zKwgTIZWMl7Pq26FLXBXMA0GCSqGSIb3DQEBBQUA +A4IBAQBW7wojoFROlZfJ+InaRcHUowAl9B8Tq7ejhVhpwjCt2BWKLePJzYFa+HMj +Wqd8BfP9IjsO0QbE2zZMcwSO5bAi5MXzLqXZI+O4Tkogp24CJJ8iYGd7ix1yCcUx +XOl5n4BHPa2hCwcUPUf/A2kaDAtE52Mlp3+yybh2hO0j9n0Hq0V+09+zv+mKts2o +omcrUtW3ZfA5TGOgkXmTUg9U3YO7n9GPp1Nzw8v/MOx8BLjYRB+TX3EJIrduPuoc +A06dGiBh+4E37F78CkWr1+cXVdCg6mCbpvbjjFspwgZgFJ0tl0ypkxWdYcQBX0jW +WL1WMRJOEcgh4LMRkWXbtKaIOM5V +-----END CERTIFICATE----- + +# Issuer: O=Chunghwa Telecom Co., Ltd. OU=ePKI Root Certification Authority +# Subject: O=Chunghwa Telecom Co., Ltd. OU=ePKI Root Certification Authority +# Label: "ePKI Root Certification Authority" +# Serial: 28956088682735189655030529057352760477 +# MD5 Fingerprint: 1b:2e:00:ca:26:06:90:3d:ad:fe:6f:15:68:d3:6b:b3 +# SHA1 Fingerprint: 67:65:0d:f1:7e:8e:7e:5b:82:40:a4:f4:56:4b:cf:e2:3d:69:c6:f0 +# SHA256 Fingerprint: c0:a6:f4:dc:63:a2:4b:fd:cf:54:ef:2a:6a:08:2a:0a:72:de:35:80:3e:2f:f5:ff:52:7a:e5:d8:72:06:df:d5 +-----BEGIN CERTIFICATE----- +MIIFsDCCA5igAwIBAgIQFci9ZUdcr7iXAF7kBtK8nTANBgkqhkiG9w0BAQUFADBe +MQswCQYDVQQGEwJUVzEjMCEGA1UECgwaQ2h1bmdod2EgVGVsZWNvbSBDby4sIEx0 +ZC4xKjAoBgNVBAsMIWVQS0kgUm9vdCBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTAe +Fw0wNDEyMjAwMjMxMjdaFw0zNDEyMjAwMjMxMjdaMF4xCzAJBgNVBAYTAlRXMSMw +IQYDVQQKDBpDaHVuZ2h3YSBUZWxlY29tIENvLiwgTHRkLjEqMCgGA1UECwwhZVBL +SSBSb290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5MIICIjANBgkqhkiG9w0BAQEF +AAOCAg8AMIICCgKCAgEA4SUP7o3biDN1Z82tH306Tm2d0y8U82N0ywEhajfqhFAH +SyZbCUNsIZ5qyNUD9WBpj8zwIuQf5/dqIjG3LBXy4P4AakP/h2XGtRrBp0xtInAh +ijHyl3SJCRImHJ7K2RKilTza6We/CKBk49ZCt0Xvl/T29de1ShUCWH2YWEtgvM3X +DZoTM1PRYfl61dd4s5oz9wCGzh1NlDivqOx4UXCKXBCDUSH3ET00hl7lSM2XgYI1 +TBnsZfZrxQWh7kcT1rMhJ5QQCtkkO7q+RBNGMD+XPNjX12ruOzjjK9SXDrkb5wdJ +fzcq+Xd4z1TtW0ado4AOkUPB1ltfFLqfpo0kR0BZv3I4sjZsN/+Z0V0OWQqraffA +sgRFelQArr5T9rXn4fg8ozHSqf4hUmTFpmfwdQcGlBSBVcYn5AGPF8Fqcde+S/uU +WH1+ETOxQvdibBjWzwloPn9s9h6PYq2lY9sJpx8iQkEeb5mKPtf5P0B6ebClAZLS +nT0IFaUQAS2zMnaolQ2zepr7BxB4EW/hj8e6DyUadCrlHJhBmd8hh+iVBmoKs2pH +dmX2Os+PYhcZewoozRrSgx4hxyy/vv9haLdnG7t4TY3OZ+XkwY63I2binZB1NJip +NiuKmpS5nezMirH4JYlcWrYvjB9teSSnUmjDhDXiZo1jDiVN1Rmy5nk3pyKdVDEC +AwEAAaNqMGgwHQYDVR0OBBYEFB4M97Zn8uGSJglFwFU5Lnc/QkqiMAwGA1UdEwQF +MAMBAf8wOQYEZyoHAAQxMC8wLQIBADAJBgUrDgMCGgUAMAcGBWcqAwAABBRFsMLH +ClZ87lt4DJX5GFPBphzYEDANBgkqhkiG9w0BAQUFAAOCAgEACbODU1kBPpVJufGB +uvl2ICO1J2B01GqZNF5sAFPZn/KmsSQHRGoqxqWOeBLoR9lYGxMqXnmbnwoqZ6Yl +PwZpVnPDimZI+ymBV3QGypzqKOg4ZyYr8dW1P2WT+DZdjo2NQCCHGervJ8A9tDkP +JXtoUHRVnAxZfVo9QZQlUgjgRywVMRnVvwdVxrsStZf0X4OFunHB2WyBEXYKCrC/ +gpf36j36+uwtqSiUO1bd0lEursC9CBWMd1I0ltabrNMdjmEPNXubrjlpC2JgQCA2 +j6/7Nu4tCEoduL+bXPjqpRugc6bY+G7gMwRfaKonh+3ZwZCc7b3jajWvY9+rGNm6 +5ulK6lCKD2GTHuItGeIwlDWSXQ62B68ZgI9HkFFLLk3dheLSClIKF5r8GrBQAuUB +o2M3IUxExJtRmREOc5wGj1QupyheRDmHVi03vYVElOEMSyycw5KFNGHLD7ibSkNS +/jQ6fbjpKdx2qcgw+BRxgMYeNkh0IkFch4LoGHGLQYlE535YW6i4jRPpp2zDR+2z +Gp1iro2C6pSe3VkQw63d4k3jMdXH7OjysP6SHhYKGvzZ8/gntsm+HbRsZJB/9OTE +W9c3rkIO3aQab3yIVMUWbuF6aC74Or8NpDyJO3inTmODBCEIZ43ygknQW/2xzQ+D +hNQ+IIX3Sj0rnP0qCglN6oH4EZw= +-----END CERTIFICATE----- + +# Issuer: O=certSIGN OU=certSIGN ROOT CA +# Subject: O=certSIGN OU=certSIGN ROOT CA +# Label: "certSIGN ROOT CA" +# Serial: 35210227249154 +# MD5 Fingerprint: 18:98:c0:d6:e9:3a:fc:f9:b0:f5:0c:f7:4b:01:44:17 +# SHA1 Fingerprint: fa:b7:ee:36:97:26:62:fb:2d:b0:2a:f6:bf:03:fd:e8:7c:4b:2f:9b +# SHA256 Fingerprint: ea:a9:62:c4:fa:4a:6b:af:eb:e4:15:19:6d:35:1c:cd:88:8d:4f:53:f3:fa:8a:e6:d7:c4:66:a9:4e:60:42:bb +-----BEGIN CERTIFICATE----- +MIIDODCCAiCgAwIBAgIGIAYFFnACMA0GCSqGSIb3DQEBBQUAMDsxCzAJBgNVBAYT +AlJPMREwDwYDVQQKEwhjZXJ0U0lHTjEZMBcGA1UECxMQY2VydFNJR04gUk9PVCBD +QTAeFw0wNjA3MDQxNzIwMDRaFw0zMTA3MDQxNzIwMDRaMDsxCzAJBgNVBAYTAlJP +MREwDwYDVQQKEwhjZXJ0U0lHTjEZMBcGA1UECxMQY2VydFNJR04gUk9PVCBDQTCC +ASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBALczuX7IJUqOtdu0KBuqV5Do +0SLTZLrTk+jUrIZhQGpgV2hUhE28alQCBf/fm5oqrl0Hj0rDKH/v+yv6efHHrfAQ +UySQi2bJqIirr1qjAOm+ukbuW3N7LBeCgV5iLKECZbO9xSsAfsT8AzNXDe3i+s5d +RdY4zTW2ssHQnIFKquSyAVwdj1+ZxLGt24gh65AIgoDzMKND5pCCrlUoSe1b16kQ +OA7+j0xbm0bqQfWwCHTD0IgztnzXdN/chNFDDnU5oSVAKOp4yw4sLjmdjItuFhwv +JoIQ4uNllAoEwF73XVv4EOLQunpL+943AAAaWyjj0pxzPjKHmKHJUS/X3qwzs08C +AwEAAaNCMEAwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAcYwHQYDVR0O +BBYEFOCMm9slSbPxfIbWskKHC9BroNnkMA0GCSqGSIb3DQEBBQUAA4IBAQA+0hyJ +LjX8+HXd5n9liPRyTMks1zJO890ZeUe9jjtbkw9QSSQTaxQGcu8J06Gh40CEyecY +MnQ8SG4Pn0vU9x7Tk4ZkVJdjclDVVc/6IJMCopvDI5NOFlV2oHB5bc0hH88vLbwZ +44gx+FkagQnIl6Z0x2DEW8xXjrJ1/RsCCdtZb3KTafcxQdaIOL+Hsr0Wefmq5L6I +Jd1hJyMctTEHBDa0GpC9oHRxUIltvBTjD4au8as+x6AJzKNI0eDbZOeStc+vckNw +i/nDhDwTqn6Sm1dTk/pwwpEOMfmbZ13pljheX7NzTogVZ96edhBiIL5VaZVDADlN +9u6wWk5JRFRYX0KD +-----END CERTIFICATE----- + +# Issuer: CN=GeoTrust Primary Certification Authority - G3 O=GeoTrust Inc. OU=(c) 2008 GeoTrust Inc. - For authorized use only +# Subject: CN=GeoTrust Primary Certification Authority - G3 O=GeoTrust Inc. OU=(c) 2008 GeoTrust Inc. - For authorized use only +# Label: "GeoTrust Primary Certification Authority - G3" +# Serial: 28809105769928564313984085209975885599 +# MD5 Fingerprint: b5:e8:34:36:c9:10:44:58:48:70:6d:2e:83:d4:b8:05 +# SHA1 Fingerprint: 03:9e:ed:b8:0b:e7:a0:3c:69:53:89:3b:20:d2:d9:32:3a:4c:2a:fd +# SHA256 Fingerprint: b4:78:b8:12:25:0d:f8:78:63:5c:2a:a7:ec:7d:15:5e:aa:62:5e:e8:29:16:e2:cd:29:43:61:88:6c:d1:fb:d4 +-----BEGIN CERTIFICATE----- +MIID/jCCAuagAwIBAgIQFaxulBmyeUtB9iepwxgPHzANBgkqhkiG9w0BAQsFADCB +mDELMAkGA1UEBhMCVVMxFjAUBgNVBAoTDUdlb1RydXN0IEluYy4xOTA3BgNVBAsT +MChjKSAyMDA4IEdlb1RydXN0IEluYy4gLSBGb3IgYXV0aG9yaXplZCB1c2Ugb25s +eTE2MDQGA1UEAxMtR2VvVHJ1c3QgUHJpbWFyeSBDZXJ0aWZpY2F0aW9uIEF1dGhv +cml0eSAtIEczMB4XDTA4MDQwMjAwMDAwMFoXDTM3MTIwMTIzNTk1OVowgZgxCzAJ +BgNVBAYTAlVTMRYwFAYDVQQKEw1HZW9UcnVzdCBJbmMuMTkwNwYDVQQLEzAoYykg +MjAwOCBHZW9UcnVzdCBJbmMuIC0gRm9yIGF1dGhvcml6ZWQgdXNlIG9ubHkxNjA0 +BgNVBAMTLUdlb1RydXN0IFByaW1hcnkgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkg +LSBHMzCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBANziXmJYHTNXOTIz ++uvLh4yn1ErdBojqZI4xmKU4kB6Yzy5jK/BGvESyiaHAKAxJcCGVn2TAppMSAmUm +hsalifD614SgcK9PGpc/BkTVyetyEH3kMSj7HGHmKAdEc5IiaacDiGydY8hS2pgn +5whMcD60yRLBxWeDXTPzAxHsatBT4tG6NmCUgLthY2xbF37fQJQeqw3CIShwiP/W +JmxsYAQlTlV+fe+/lEjetx3dcI0FX4ilm/LC7urRQEFtYjgdVgbFA0dRIBn8exAL +DmKudlW/X3e+PkkBUz2YJQN2JFodtNuJ6nnltrM7P7pMKEF/BqxqjsHQ9gUdfeZC +huOl1UcCAwEAAaNCMEAwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYw +HQYDVR0OBBYEFMR5yo6hTgMdHNxr2zFblD4/MH8tMA0GCSqGSIb3DQEBCwUAA4IB +AQAtxRPPVoB7eni9n64smefv2t+UXglpp+duaIy9cr5HqQ6XErhK8WTTOd8lNNTB +zU6B8A8ExCSzNJbGpqow32hhc9f5joWJ7w5elShKKiePEI4ufIbEAp7aDHdlDkQN +kv39sxY2+hENHYwOB4lqKVb3cvTdFZx3NWZXqxNT2I7BQMXXExZacse3aQHEerGD +AWh9jUGhlBjBJVz88P6DAod8DQ3PLghcSkANPuyBYeYk28rgDi0Hsj5W3I31QYUH +SJsMC8tJP33st/3LjWeJGqvtux6jAAgIFyqCXDFdRootD4abdNlF+9RAsXqqaC2G +spki4cErx5z481+oghLrGREt +-----END CERTIFICATE----- + +# Issuer: CN=thawte Primary Root CA - G2 O=thawte, Inc. OU=(c) 2007 thawte, Inc. - For authorized use only +# Subject: CN=thawte Primary Root CA - G2 O=thawte, Inc. OU=(c) 2007 thawte, Inc. - For authorized use only +# Label: "thawte Primary Root CA - G2" +# Serial: 71758320672825410020661621085256472406 +# MD5 Fingerprint: 74:9d:ea:60:24:c4:fd:22:53:3e:cc:3a:72:d9:29:4f +# SHA1 Fingerprint: aa:db:bc:22:23:8f:c4:01:a1:27:bb:38:dd:f4:1d:db:08:9e:f0:12 +# SHA256 Fingerprint: a4:31:0d:50:af:18:a6:44:71:90:37:2a:86:af:af:8b:95:1f:fb:43:1d:83:7f:1e:56:88:b4:59:71:ed:15:57 +-----BEGIN CERTIFICATE----- +MIICiDCCAg2gAwIBAgIQNfwmXNmET8k9Jj1Xm67XVjAKBggqhkjOPQQDAzCBhDEL +MAkGA1UEBhMCVVMxFTATBgNVBAoTDHRoYXd0ZSwgSW5jLjE4MDYGA1UECxMvKGMp +IDIwMDcgdGhhd3RlLCBJbmMuIC0gRm9yIGF1dGhvcml6ZWQgdXNlIG9ubHkxJDAi +BgNVBAMTG3RoYXd0ZSBQcmltYXJ5IFJvb3QgQ0EgLSBHMjAeFw0wNzExMDUwMDAw +MDBaFw0zODAxMTgyMzU5NTlaMIGEMQswCQYDVQQGEwJVUzEVMBMGA1UEChMMdGhh +d3RlLCBJbmMuMTgwNgYDVQQLEy8oYykgMjAwNyB0aGF3dGUsIEluYy4gLSBGb3Ig +YXV0aG9yaXplZCB1c2Ugb25seTEkMCIGA1UEAxMbdGhhd3RlIFByaW1hcnkgUm9v +dCBDQSAtIEcyMHYwEAYHKoZIzj0CAQYFK4EEACIDYgAEotWcgnuVnfFSeIf+iha/ +BebfowJPDQfGAFG6DAJSLSKkQjnE/o/qycG+1E3/n3qe4rF8mq2nhglzh9HnmuN6 +papu+7qzcMBniKI11KOasf2twu8x+qi58/sIxpHR+ymVo0IwQDAPBgNVHRMBAf8E +BTADAQH/MA4GA1UdDwEB/wQEAwIBBjAdBgNVHQ4EFgQUmtgAMADna3+FGO6Lts6K +DPgR4bswCgYIKoZIzj0EAwMDaQAwZgIxAN344FdHW6fmCsO99YCKlzUNG4k8VIZ3 +KMqh9HneteY4sPBlcIx/AlTCv//YoT7ZzwIxAMSNlPzcU9LcnXgWHxUzI1NS41ox +XZ3Krr0TKUQNJ1uo52icEvdYPy5yAlejj6EULg== +-----END CERTIFICATE----- + +# Issuer: CN=thawte Primary Root CA - G3 O=thawte, Inc. OU=Certification Services Division/(c) 2008 thawte, Inc. - For authorized use only +# Subject: CN=thawte Primary Root CA - G3 O=thawte, Inc. OU=Certification Services Division/(c) 2008 thawte, Inc. - For authorized use only +# Label: "thawte Primary Root CA - G3" +# Serial: 127614157056681299805556476275995414779 +# MD5 Fingerprint: fb:1b:5d:43:8a:94:cd:44:c6:76:f2:43:4b:47:e7:31 +# SHA1 Fingerprint: f1:8b:53:8d:1b:e9:03:b6:a6:f0:56:43:5b:17:15:89:ca:f3:6b:f2 +# SHA256 Fingerprint: 4b:03:f4:58:07:ad:70:f2:1b:fc:2c:ae:71:c9:fd:e4:60:4c:06:4c:f5:ff:b6:86:ba:e5:db:aa:d7:fd:d3:4c +-----BEGIN CERTIFICATE----- +MIIEKjCCAxKgAwIBAgIQYAGXt0an6rS0mtZLL/eQ+zANBgkqhkiG9w0BAQsFADCB +rjELMAkGA1UEBhMCVVMxFTATBgNVBAoTDHRoYXd0ZSwgSW5jLjEoMCYGA1UECxMf +Q2VydGlmaWNhdGlvbiBTZXJ2aWNlcyBEaXZpc2lvbjE4MDYGA1UECxMvKGMpIDIw +MDggdGhhd3RlLCBJbmMuIC0gRm9yIGF1dGhvcml6ZWQgdXNlIG9ubHkxJDAiBgNV +BAMTG3RoYXd0ZSBQcmltYXJ5IFJvb3QgQ0EgLSBHMzAeFw0wODA0MDIwMDAwMDBa +Fw0zNzEyMDEyMzU5NTlaMIGuMQswCQYDVQQGEwJVUzEVMBMGA1UEChMMdGhhd3Rl +LCBJbmMuMSgwJgYDVQQLEx9DZXJ0aWZpY2F0aW9uIFNlcnZpY2VzIERpdmlzaW9u +MTgwNgYDVQQLEy8oYykgMjAwOCB0aGF3dGUsIEluYy4gLSBGb3IgYXV0aG9yaXpl +ZCB1c2Ugb25seTEkMCIGA1UEAxMbdGhhd3RlIFByaW1hcnkgUm9vdCBDQSAtIEcz +MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAsr8nLPvb2FvdeHsbnndm +gcs+vHyu86YnmjSjaDFxODNi5PNxZnmxqWWjpYvVj2AtP0LMqmsywCPLLEHd5N/8 +YZzic7IilRFDGF/Eth9XbAoFWCLINkw6fKXRz4aviKdEAhN0cXMKQlkC+BsUa0Lf +b1+6a4KinVvnSr0eAXLbS3ToO39/fR8EtCab4LRarEc9VbjXsCZSKAExQGbY2SS9 +9irY7CFJXJv2eul/VTV+lmuNk5Mny5K76qxAwJ/C+IDPXfRa3M50hqY+bAtTyr2S +zhkGcuYMXDhpxwTWvGzOW/b3aJzcJRVIiKHpqfiYnODz1TEoYRFsZ5aNOZnLwkUk +OQIDAQABo0IwQDAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIBBjAdBgNV +HQ4EFgQUrWyqlGCc7eT/+j4KdCtjA/e2Wb8wDQYJKoZIhvcNAQELBQADggEBABpA +2JVlrAmSicY59BDlqQ5mU1143vokkbvnRFHfxhY0Cu9qRFHqKweKA3rD6z8KLFIW +oCtDuSWQP3CpMyVtRRooOyfPqsMpQhvfO0zAMzRbQYi/aytlryjvsvXDqmbOe1bu +t8jLZ8HJnBoYuMTDSQPxYA5QzUbF83d597YV4Djbxy8ooAw/dyZ02SUS2jHaGh7c +KUGRIjxpp7sC8rZcJwOJ9Abqm+RyguOhCcHpABnTPtRwa7pxpqpYrvS76Wy274fM +m7v/OeZWYdMKp8RcTGB7BXcmer/YB1IsYvdwY9k5vG8cwnncdimvzsUsZAReiDZu +MdRAGmI0Nj81Aa6sY6A= +-----END CERTIFICATE----- + +# Issuer: CN=GeoTrust Primary Certification Authority - G2 O=GeoTrust Inc. OU=(c) 2007 GeoTrust Inc. - For authorized use only +# Subject: CN=GeoTrust Primary Certification Authority - G2 O=GeoTrust Inc. OU=(c) 2007 GeoTrust Inc. - For authorized use only +# Label: "GeoTrust Primary Certification Authority - G2" +# Serial: 80682863203381065782177908751794619243 +# MD5 Fingerprint: 01:5e:d8:6b:bd:6f:3d:8e:a1:31:f8:12:e0:98:73:6a +# SHA1 Fingerprint: 8d:17:84:d5:37:f3:03:7d:ec:70:fe:57:8b:51:9a:99:e6:10:d7:b0 +# SHA256 Fingerprint: 5e:db:7a:c4:3b:82:a0:6a:87:61:e8:d7:be:49:79:eb:f2:61:1f:7d:d7:9b:f9:1c:1c:6b:56:6a:21:9e:d7:66 +-----BEGIN CERTIFICATE----- +MIICrjCCAjWgAwIBAgIQPLL0SAoA4v7rJDteYD7DazAKBggqhkjOPQQDAzCBmDEL +MAkGA1UEBhMCVVMxFjAUBgNVBAoTDUdlb1RydXN0IEluYy4xOTA3BgNVBAsTMChj +KSAyMDA3IEdlb1RydXN0IEluYy4gLSBGb3IgYXV0aG9yaXplZCB1c2Ugb25seTE2 +MDQGA1UEAxMtR2VvVHJ1c3QgUHJpbWFyeSBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0 +eSAtIEcyMB4XDTA3MTEwNTAwMDAwMFoXDTM4MDExODIzNTk1OVowgZgxCzAJBgNV +BAYTAlVTMRYwFAYDVQQKEw1HZW9UcnVzdCBJbmMuMTkwNwYDVQQLEzAoYykgMjAw +NyBHZW9UcnVzdCBJbmMuIC0gRm9yIGF1dGhvcml6ZWQgdXNlIG9ubHkxNjA0BgNV +BAMTLUdlb1RydXN0IFByaW1hcnkgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkgLSBH +MjB2MBAGByqGSM49AgEGBSuBBAAiA2IABBWx6P0DFUPlrOuHNxFi79KDNlJ9RVcL +So17VDs6bl8VAsBQps8lL33KSLjHUGMcKiEIfJo22Av+0SbFWDEwKCXzXV2juLal +tJLtbCyf691DiaI8S0iRHVDsJt/WYC69IaNCMEAwDwYDVR0TAQH/BAUwAwEB/zAO +BgNVHQ8BAf8EBAMCAQYwHQYDVR0OBBYEFBVfNVdRVfslsq0DafwBo/q+EVXVMAoG +CCqGSM49BAMDA2cAMGQCMGSWWaboCd6LuvpaiIjwH5HTRqjySkwCY/tsXzjbLkGT +qQ7mndwxHLKgpxgceeHHNgIwOlavmnRs9vuD4DPTCF+hnMJbn0bWtsuRBmOiBucz +rD6ogRLQy7rQkgu2npaqBA+K +-----END CERTIFICATE----- + +# Issuer: CN=VeriSign Universal Root Certification Authority O=VeriSign, Inc. OU=VeriSign Trust Network/(c) 2008 VeriSign, Inc. - For authorized use only +# Subject: CN=VeriSign Universal Root Certification Authority O=VeriSign, Inc. OU=VeriSign Trust Network/(c) 2008 VeriSign, Inc. - For authorized use only +# Label: "VeriSign Universal Root Certification Authority" +# Serial: 85209574734084581917763752644031726877 +# MD5 Fingerprint: 8e:ad:b5:01:aa:4d:81:e4:8c:1d:d1:e1:14:00:95:19 +# SHA1 Fingerprint: 36:79:ca:35:66:87:72:30:4d:30:a5:fb:87:3b:0f:a7:7b:b7:0d:54 +# SHA256 Fingerprint: 23:99:56:11:27:a5:71:25:de:8c:ef:ea:61:0d:df:2f:a0:78:b5:c8:06:7f:4e:82:82:90:bf:b8:60:e8:4b:3c +-----BEGIN CERTIFICATE----- +MIIEuTCCA6GgAwIBAgIQQBrEZCGzEyEDDrvkEhrFHTANBgkqhkiG9w0BAQsFADCB +vTELMAkGA1UEBhMCVVMxFzAVBgNVBAoTDlZlcmlTaWduLCBJbmMuMR8wHQYDVQQL +ExZWZXJpU2lnbiBUcnVzdCBOZXR3b3JrMTowOAYDVQQLEzEoYykgMjAwOCBWZXJp +U2lnbiwgSW5jLiAtIEZvciBhdXRob3JpemVkIHVzZSBvbmx5MTgwNgYDVQQDEy9W +ZXJpU2lnbiBVbml2ZXJzYWwgUm9vdCBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTAe +Fw0wODA0MDIwMDAwMDBaFw0zNzEyMDEyMzU5NTlaMIG9MQswCQYDVQQGEwJVUzEX +MBUGA1UEChMOVmVyaVNpZ24sIEluYy4xHzAdBgNVBAsTFlZlcmlTaWduIFRydXN0 +IE5ldHdvcmsxOjA4BgNVBAsTMShjKSAyMDA4IFZlcmlTaWduLCBJbmMuIC0gRm9y +IGF1dGhvcml6ZWQgdXNlIG9ubHkxODA2BgNVBAMTL1ZlcmlTaWduIFVuaXZlcnNh +bCBSb290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5MIIBIjANBgkqhkiG9w0BAQEF +AAOCAQ8AMIIBCgKCAQEAx2E3XrEBNNti1xWb/1hajCMj1mCOkdeQmIN65lgZOIzF +9uVkhbSicfvtvbnazU0AtMgtc6XHaXGVHzk8skQHnOgO+k1KxCHfKWGPMiJhgsWH +H26MfF8WIFFE0XBPV+rjHOPMee5Y2A7Cs0WTwCznmhcrewA3ekEzeOEz4vMQGn+H +LL729fdC4uW/h2KJXwBL38Xd5HVEMkE6HnFuacsLdUYI0crSK5XQz/u5QGtkjFdN +/BMReYTtXlT2NJ8IAfMQJQYXStrxHXpma5hgZqTZ79IugvHw7wnqRMkVauIDbjPT +rJ9VAMf2CGqUuV/c4DPxhGD5WycRtPwW8rtWaoAljQIDAQABo4GyMIGvMA8GA1Ud +EwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgEGMG0GCCsGAQUFBwEMBGEwX6FdoFsw +WTBXMFUWCWltYWdlL2dpZjAhMB8wBwYFKw4DAhoEFI/l0xqGrI2Oa8PPgGrUSBgs +exkuMCUWI2h0dHA6Ly9sb2dvLnZlcmlzaWduLmNvbS92c2xvZ28uZ2lmMB0GA1Ud +DgQWBBS2d/ppSEefUxLVwuoHMnYH0ZcHGTANBgkqhkiG9w0BAQsFAAOCAQEASvj4 +sAPmLGd75JR3Y8xuTPl9Dg3cyLk1uXBPY/ok+myDjEedO2Pzmvl2MpWRsXe8rJq+ +seQxIcaBlVZaDrHC1LGmWazxY8u4TB1ZkErvkBYoH1quEPuBUDgMbMzxPcP1Y+Oz +4yHJJDnp/RVmRvQbEdBNc6N9Rvk97ahfYtTxP/jgdFcrGJ2BtMQo2pSXpXDrrB2+ +BxHw1dvd5Yzw1TKwg+ZX4o+/vqGqvz0dtdQ46tewXDpPaj+PwGZsY6rp2aQW9IHR +lRQOfc2VNNnSj3BzgXucfr2YYdhFh5iQxeuGMMY1v/D/w1WIg0vvBZIGcfK4mJO3 +7M2CYfE45k+XmCpajQ== +-----END CERTIFICATE----- + +# Issuer: CN=VeriSign Class 3 Public Primary Certification Authority - G4 O=VeriSign, Inc. OU=VeriSign Trust Network/(c) 2007 VeriSign, Inc. - For authorized use only +# Subject: CN=VeriSign Class 3 Public Primary Certification Authority - G4 O=VeriSign, Inc. OU=VeriSign Trust Network/(c) 2007 VeriSign, Inc. - For authorized use only +# Label: "VeriSign Class 3 Public Primary Certification Authority - G4" +# Serial: 63143484348153506665311985501458640051 +# MD5 Fingerprint: 3a:52:e1:e7:fd:6f:3a:e3:6f:f3:6f:99:1b:f9:22:41 +# SHA1 Fingerprint: 22:d5:d8:df:8f:02:31:d1:8d:f7:9d:b7:cf:8a:2d:64:c9:3f:6c:3a +# SHA256 Fingerprint: 69:dd:d7:ea:90:bb:57:c9:3e:13:5d:c8:5e:a6:fc:d5:48:0b:60:32:39:bd:c4:54:fc:75:8b:2a:26:cf:7f:79 +-----BEGIN CERTIFICATE----- +MIIDhDCCAwqgAwIBAgIQL4D+I4wOIg9IZxIokYesszAKBggqhkjOPQQDAzCByjEL +MAkGA1UEBhMCVVMxFzAVBgNVBAoTDlZlcmlTaWduLCBJbmMuMR8wHQYDVQQLExZW +ZXJpU2lnbiBUcnVzdCBOZXR3b3JrMTowOAYDVQQLEzEoYykgMjAwNyBWZXJpU2ln +biwgSW5jLiAtIEZvciBhdXRob3JpemVkIHVzZSBvbmx5MUUwQwYDVQQDEzxWZXJp +U2lnbiBDbGFzcyAzIFB1YmxpYyBQcmltYXJ5IENlcnRpZmljYXRpb24gQXV0aG9y +aXR5IC0gRzQwHhcNMDcxMTA1MDAwMDAwWhcNMzgwMTE4MjM1OTU5WjCByjELMAkG +A1UEBhMCVVMxFzAVBgNVBAoTDlZlcmlTaWduLCBJbmMuMR8wHQYDVQQLExZWZXJp +U2lnbiBUcnVzdCBOZXR3b3JrMTowOAYDVQQLEzEoYykgMjAwNyBWZXJpU2lnbiwg +SW5jLiAtIEZvciBhdXRob3JpemVkIHVzZSBvbmx5MUUwQwYDVQQDEzxWZXJpU2ln +biBDbGFzcyAzIFB1YmxpYyBQcmltYXJ5IENlcnRpZmljYXRpb24gQXV0aG9yaXR5 +IC0gRzQwdjAQBgcqhkjOPQIBBgUrgQQAIgNiAASnVnp8Utpkmw4tXNherJI9/gHm +GUo9FANL+mAnINmDiWn6VMaaGF5VKmTeBvaNSjutEDxlPZCIBIngMGGzrl0Bp3ve +fLK+ymVhAIau2o970ImtTR1ZmkGxvEeA3J5iw/mjgbIwga8wDwYDVR0TAQH/BAUw +AwEB/zAOBgNVHQ8BAf8EBAMCAQYwbQYIKwYBBQUHAQwEYTBfoV2gWzBZMFcwVRYJ +aW1hZ2UvZ2lmMCEwHzAHBgUrDgMCGgQUj+XTGoasjY5rw8+AatRIGCx7GS4wJRYj +aHR0cDovL2xvZ28udmVyaXNpZ24uY29tL3ZzbG9nby5naWYwHQYDVR0OBBYEFLMW +kf3upm7ktS5Jj4d4gYDs5bG1MAoGCCqGSM49BAMDA2gAMGUCMGYhDBgmYFo4e1ZC +4Kf8NoRRkSAsdk1DPcQdhCPQrNZ8NQbOzWm9kA3bbEhCHQ6qQgIxAJw9SDkjOVga +FRJZap7v1VmyHVIsmXHNxynfGyphe3HR3vPA5Q06Sqotp9iGKt0uEA== +-----END CERTIFICATE----- + +# Issuer: CN=NetLock Arany (Class Gold) F\u0151tan\xfas\xedtv\xe1ny O=NetLock Kft. OU=Tan\xfas\xedtv\xe1nykiad\xf3k (Certification Services) +# Subject: CN=NetLock Arany (Class Gold) F\u0151tan\xfas\xedtv\xe1ny O=NetLock Kft. OU=Tan\xfas\xedtv\xe1nykiad\xf3k (Certification Services) +# Label: "NetLock Arany (Class Gold) F\u0151tan\xfas\xedtv\xe1ny" +# Serial: 80544274841616 +# MD5 Fingerprint: c5:a1:b7:ff:73:dd:d6:d7:34:32:18:df:fc:3c:ad:88 +# SHA1 Fingerprint: 06:08:3f:59:3f:15:a1:04:a0:69:a4:6b:a9:03:d0:06:b7:97:09:91 +# SHA256 Fingerprint: 6c:61:da:c3:a2:de:f0:31:50:6b:e0:36:d2:a6:fe:40:19:94:fb:d1:3d:f9:c8:d4:66:59:92:74:c4:46:ec:98 +-----BEGIN CERTIFICATE----- +MIIEFTCCAv2gAwIBAgIGSUEs5AAQMA0GCSqGSIb3DQEBCwUAMIGnMQswCQYDVQQG +EwJIVTERMA8GA1UEBwwIQnVkYXBlc3QxFTATBgNVBAoMDE5ldExvY2sgS2Z0LjE3 +MDUGA1UECwwuVGFuw7pzw610dsOhbnlraWFkw7NrIChDZXJ0aWZpY2F0aW9uIFNl +cnZpY2VzKTE1MDMGA1UEAwwsTmV0TG9jayBBcmFueSAoQ2xhc3MgR29sZCkgRsWR +dGFuw7pzw610dsOhbnkwHhcNMDgxMjExMTUwODIxWhcNMjgxMjA2MTUwODIxWjCB +pzELMAkGA1UEBhMCSFUxETAPBgNVBAcMCEJ1ZGFwZXN0MRUwEwYDVQQKDAxOZXRM +b2NrIEtmdC4xNzA1BgNVBAsMLlRhbsO6c8OtdHbDoW55a2lhZMOzayAoQ2VydGlm +aWNhdGlvbiBTZXJ2aWNlcykxNTAzBgNVBAMMLE5ldExvY2sgQXJhbnkgKENsYXNz +IEdvbGQpIEbFkXRhbsO6c8OtdHbDoW55MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8A +MIIBCgKCAQEAxCRec75LbRTDofTjl5Bu0jBFHjzuZ9lk4BqKf8owyoPjIMHj9DrT +lF8afFttvzBPhCf2nx9JvMaZCpDyD/V/Q4Q3Y1GLeqVw/HpYzY6b7cNGbIRwXdrz +AZAj/E4wqX7hJ2Pn7WQ8oLjJM2P+FpD/sLj916jAwJRDC7bVWaaeVtAkH3B5r9s5 +VA1lddkVQZQBr17s9o3x/61k/iCa11zr/qYfCGSji3ZVrR47KGAuhyXoqq8fxmRG +ILdwfzzeSNuWU7c5d+Qa4scWhHaXWy+7GRWF+GmF9ZmnqfI0p6m2pgP8b4Y9VHx2 +BJtr+UBdADTHLpl1neWIA6pN+APSQnbAGwIDAKiLo0UwQzASBgNVHRMBAf8ECDAG +AQH/AgEEMA4GA1UdDwEB/wQEAwIBBjAdBgNVHQ4EFgQUzPpnk/C2uNClwB7zU/2M +U9+D15YwDQYJKoZIhvcNAQELBQADggEBAKt/7hwWqZw8UQCgwBEIBaeZ5m8BiFRh +bvG5GK1Krf6BQCOUL/t1fC8oS2IkgYIL9WHxHG64YTjrgfpioTtaYtOUZcTh5m2C ++C8lcLIhJsFyUR+MLMOEkMNaj7rP9KdlpeuY0fsFskZ1FSNqb4VjMIDw1Z4fKRzC +bLBQWV2QWzuoDTDPv31/zvGdg73JRm4gpvlhUbohL3u+pRVjodSVh/GeufOJ8z2F +uLjbvrW5KfnaNwUASZQDhETnv0Mxz3WLJdH0pmT1kvarBes96aULNmLazAZfNou2 +XjG4Kvte9nHfRCaexOYNkbQudZWAUWpLMKawYqGT8ZvYzsRjdT9ZR7E= +-----END CERTIFICATE----- + +# Issuer: CN=Staat der Nederlanden Root CA - G2 O=Staat der Nederlanden +# Subject: CN=Staat der Nederlanden Root CA - G2 O=Staat der Nederlanden +# Label: "Staat der Nederlanden Root CA - G2" +# Serial: 10000012 +# MD5 Fingerprint: 7c:a5:0f:f8:5b:9a:7d:6d:30:ae:54:5a:e3:42:a2:8a +# SHA1 Fingerprint: 59:af:82:79:91:86:c7:b4:75:07:cb:cf:03:57:46:eb:04:dd:b7:16 +# SHA256 Fingerprint: 66:8c:83:94:7d:a6:3b:72:4b:ec:e1:74:3c:31:a0:e6:ae:d0:db:8e:c5:b3:1b:e3:77:bb:78:4f:91:b6:71:6f +-----BEGIN CERTIFICATE----- +MIIFyjCCA7KgAwIBAgIEAJiWjDANBgkqhkiG9w0BAQsFADBaMQswCQYDVQQGEwJO +TDEeMBwGA1UECgwVU3RhYXQgZGVyIE5lZGVybGFuZGVuMSswKQYDVQQDDCJTdGFh +dCBkZXIgTmVkZXJsYW5kZW4gUm9vdCBDQSAtIEcyMB4XDTA4MDMyNjExMTgxN1oX +DTIwMDMyNTExMDMxMFowWjELMAkGA1UEBhMCTkwxHjAcBgNVBAoMFVN0YWF0IGRl +ciBOZWRlcmxhbmRlbjErMCkGA1UEAwwiU3RhYXQgZGVyIE5lZGVybGFuZGVuIFJv +b3QgQ0EgLSBHMjCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAMVZ5291 +qj5LnLW4rJ4L5PnZyqtdj7U5EILXr1HgO+EASGrP2uEGQxGZqhQlEq0i6ABtQ8Sp +uOUfiUtnvWFI7/3S4GCI5bkYYCjDdyutsDeqN95kWSpGV+RLufg3fNU254DBtvPU +Z5uW6M7XxgpT0GtJlvOjCwV3SPcl5XCsMBQgJeN/dVrlSPhOewMHBPqCYYdu8DvE +pMfQ9XQ+pV0aCPKbJdL2rAQmPlU6Yiile7Iwr/g3wtG61jj99O9JMDeZJiFIhQGp +5Rbn3JBV3w/oOM2ZNyFPXfUib2rFEhZgF1XyZWampzCROME4HYYEhLoaJXhena/M +UGDWE4dS7WMfbWV9whUYdMrhfmQpjHLYFhN9C0lK8SgbIHRrxT3dsKpICT0ugpTN +GmXZK4iambwYfp/ufWZ8Pr2UuIHOzZgweMFvZ9C+X+Bo7d7iscksWXiSqt8rYGPy +5V6548r6f1CGPqI0GAwJaCgRHOThuVw+R7oyPxjMW4T182t0xHJ04eOLoEq9jWYv +6q012iDTiIJh8BIitrzQ1aTsr1SIJSQ8p22xcik/Plemf1WvbibG/ufMQFxRRIEK +eN5KzlW/HdXZt1bv8Hb/C3m1r737qWmRRpdogBQ2HbN/uymYNqUg+oJgYjOk7Na6 +B6duxc8UpufWkjTYgfX8HV2qXB72o007uPc5AgMBAAGjgZcwgZQwDwYDVR0TAQH/ +BAUwAwEB/zBSBgNVHSAESzBJMEcGBFUdIAAwPzA9BggrBgEFBQcCARYxaHR0cDov +L3d3dy5wa2lvdmVyaGVpZC5ubC9wb2xpY2llcy9yb290LXBvbGljeS1HMjAOBgNV +HQ8BAf8EBAMCAQYwHQYDVR0OBBYEFJFoMocVHYnitfGsNig0jQt8YojrMA0GCSqG +SIb3DQEBCwUAA4ICAQCoQUpnKpKBglBu4dfYszk78wIVCVBR7y29JHuIhjv5tLyS +CZa59sCrI2AGeYwRTlHSeYAz+51IvuxBQ4EffkdAHOV6CMqqi3WtFMTC6GY8ggen +5ieCWxjmD27ZUD6KQhgpxrRW/FYQoAUXvQwjf/ST7ZwaUb7dRUG/kSS0H4zpX897 +IZmflZ85OkYcbPnNe5yQzSipx6lVu6xiNGI1E0sUOlWDuYaNkqbG9AclVMwWVxJK +gnjIFNkXgiYtXSAfea7+1HAWFpWD2DU5/1JddRwWxRNVz0fMdWVSSt7wsKfkCpYL ++63C4iWEst3kvX5ZbJvw8NjnyvLplzh+ib7M+zkXYT9y2zqR2GUBGR2tUKRXCnxL +vJxxcypFURmFzI79R6d0lR2o0a9OF7FpJsKqeFdbxU2n5Z4FF5TKsl+gSRiNNOkm +bEgeqmiSBeGCc1qb3AdbCG19ndeNIdn8FCCqwkXfP+cAslHkwvgFuXkajDTznlvk +N1trSt8sV4pAWja63XVECDdCcAz+3F4hoKOKwJCcaNpQ5kUQR3i2TtJlycM33+FC +Y7BXN0Ute4qcvwXqZVUz9zkQxSgqIXobisQk+T8VyJoVIPVVYpbtbZNQvOSqeK3Z +ywplh6ZmwcSBo3c6WB4L7oOLnR7SUqTMHW+wmG2UMbX4cQrcufx9MmDm66+KAQ== +-----END CERTIFICATE----- + +# Issuer: CN=Hongkong Post Root CA 1 O=Hongkong Post +# Subject: CN=Hongkong Post Root CA 1 O=Hongkong Post +# Label: "Hongkong Post Root CA 1" +# Serial: 1000 +# MD5 Fingerprint: a8:0d:6f:39:78:b9:43:6d:77:42:6d:98:5a:cc:23:ca +# SHA1 Fingerprint: d6:da:a8:20:8d:09:d2:15:4d:24:b5:2f:cb:34:6e:b2:58:b2:8a:58 +# SHA256 Fingerprint: f9:e6:7d:33:6c:51:00:2a:c0:54:c6:32:02:2d:66:dd:a2:e7:e3:ff:f1:0a:d0:61:ed:31:d8:bb:b4:10:cf:b2 +-----BEGIN CERTIFICATE----- +MIIDMDCCAhigAwIBAgICA+gwDQYJKoZIhvcNAQEFBQAwRzELMAkGA1UEBhMCSEsx +FjAUBgNVBAoTDUhvbmdrb25nIFBvc3QxIDAeBgNVBAMTF0hvbmdrb25nIFBvc3Qg +Um9vdCBDQSAxMB4XDTAzMDUxNTA1MTMxNFoXDTIzMDUxNTA0NTIyOVowRzELMAkG +A1UEBhMCSEsxFjAUBgNVBAoTDUhvbmdrb25nIFBvc3QxIDAeBgNVBAMTF0hvbmdr +b25nIFBvc3QgUm9vdCBDQSAxMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKC +AQEArP84tulmAknjorThkPlAj3n54r15/gK97iSSHSL22oVyaf7XPwnU3ZG1ApzQ +jVrhVcNQhrkpJsLj2aDxaQMoIIBFIi1WpztUlVYiWR8o3x8gPW2iNr4joLFutbEn +PzlTCeqrauh0ssJlXI6/fMN4hM2eFvz1Lk8gKgifd/PFHsSaUmYeSF7jEAaPIpjh +ZY4bXSNmO7ilMlHIhqqhqZ5/dpTCpmy3QfDVyAY45tQM4vM7TG1QjMSDJ8EThFk9 +nnV0ttgCXjqQesBCNnLsak3c78QA3xMYV18meMjWCnl3v/evt3a5pQuEF10Q6m/h +q5URX208o1xNg1vysxmKgIsLhwIDAQABoyYwJDASBgNVHRMBAf8ECDAGAQH/AgED +MA4GA1UdDwEB/wQEAwIBxjANBgkqhkiG9w0BAQUFAAOCAQEADkbVPK7ih9legYsC +mEEIjEy82tvuJxuC52pF7BaLT4Wg87JwvVqWuspube5Gi27nKi6Wsxkz67SfqLI3 +7piol7Yutmcn1KZJ/RyTZXaeQi/cImyaT/JaFTmxcdcrUehtHJjA2Sr0oYJ71clB +oiMBdDhViw+5LmeiIAQ32pwL0xch4I+XeTRvhEgCIDMb5jREn5Fw9IBehEPCKdJs +EhTkYY2sEJCehFC78JZvRZ+K88psT/oROhUVRsPNH4NbLUES7VBnQRM9IauUiqpO +fMGx+6fWtScvl6tu4B3i0RwsH0Ti/L6RoZz71ilTc4afU9hDDl3WY4JxHYB0yvbi +AmvZWg== +-----END CERTIFICATE----- + +# Issuer: CN=SecureSign RootCA11 O=Japan Certification Services, Inc. +# Subject: CN=SecureSign RootCA11 O=Japan Certification Services, Inc. +# Label: "SecureSign RootCA11" +# Serial: 1 +# MD5 Fingerprint: b7:52:74:e2:92:b4:80:93:f2:75:e4:cc:d7:f2:ea:26 +# SHA1 Fingerprint: 3b:c4:9f:48:f8:f3:73:a0:9c:1e:bd:f8:5b:b1:c3:65:c7:d8:11:b3 +# SHA256 Fingerprint: bf:0f:ee:fb:9e:3a:58:1a:d5:f9:e9:db:75:89:98:57:43:d2:61:08:5c:4d:31:4f:6f:5d:72:59:aa:42:16:12 +-----BEGIN CERTIFICATE----- +MIIDbTCCAlWgAwIBAgIBATANBgkqhkiG9w0BAQUFADBYMQswCQYDVQQGEwJKUDEr +MCkGA1UEChMiSmFwYW4gQ2VydGlmaWNhdGlvbiBTZXJ2aWNlcywgSW5jLjEcMBoG +A1UEAxMTU2VjdXJlU2lnbiBSb290Q0ExMTAeFw0wOTA0MDgwNDU2NDdaFw0yOTA0 +MDgwNDU2NDdaMFgxCzAJBgNVBAYTAkpQMSswKQYDVQQKEyJKYXBhbiBDZXJ0aWZp +Y2F0aW9uIFNlcnZpY2VzLCBJbmMuMRwwGgYDVQQDExNTZWN1cmVTaWduIFJvb3RD +QTExMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA/XeqpRyQBTvLTJsz +i1oURaTnkBbR31fSIRCkF/3frNYfp+TbfPfs37gD2pRY/V1yfIw/XwFndBWW4wI8 +h9uuywGOwvNmxoVF9ALGOrVisq/6nL+k5tSAMJjzDbaTj6nU2DbysPyKyiyhFTOV +MdrAG/LuYpmGYz+/3ZMqg6h2uRMft85OQoWPIucuGvKVCbIFtUROd6EgvanyTgp9 +UK31BQ1FT0Zx/Sg+U/sE2C3XZR1KG/rPO7AxmjVuyIsG0wCR8pQIZUyxNAYAeoni +8McDWc/V1uinMrPmmECGxc0nEovMe863ETxiYAcjPitAbpSACW22s293bzUIUPsC +h8U+iQIDAQABo0IwQDAdBgNVHQ4EFgQUW/hNT7KlhtQ60vFjmqC+CfZXt94wDgYD +VR0PAQH/BAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQEFBQADggEB +AKChOBZmLqdWHyGcBvod7bkixTgm2E5P7KN/ed5GIaGHd48HCJqypMWvDzKYC3xm +KbabfSVSSUOrTC4rbnpwrxYO4wJs+0LmGJ1F2FXI6Dvd5+H0LgscNFxsWEr7jIhQ +X5Ucv+2rIrVls4W6ng+4reV6G4pQOh29Dbx7VFALuUKvVaAYga1lme++5Jy/xIWr +QbJUb9wlze144o4MjQlJ3WN7WmmWAiGovVJZ6X01y8hSyn+B/tlr0/cR7SXf+Of5 +pPpyl4RTDaXQMhhRdlkUbA/r7F+AjHVDg8OFmP9Mni0N5HeDk061lgeLKBObjBmN +QSdJQO7e5iNEOdyhIta6A/I= +-----END CERTIFICATE----- + +# Issuer: CN=Microsec e-Szigno Root CA 2009 O=Microsec Ltd. +# Subject: CN=Microsec e-Szigno Root CA 2009 O=Microsec Ltd. +# Label: "Microsec e-Szigno Root CA 2009" +# Serial: 14014712776195784473 +# MD5 Fingerprint: f8:49:f4:03:bc:44:2d:83:be:48:69:7d:29:64:fc:b1 +# SHA1 Fingerprint: 89:df:74:fe:5c:f4:0f:4a:80:f9:e3:37:7d:54:da:91:e1:01:31:8e +# SHA256 Fingerprint: 3c:5f:81:fe:a5:fa:b8:2c:64:bf:a2:ea:ec:af:cd:e8:e0:77:fc:86:20:a7:ca:e5:37:16:3d:f3:6e:db:f3:78 +-----BEGIN CERTIFICATE----- +MIIECjCCAvKgAwIBAgIJAMJ+QwRORz8ZMA0GCSqGSIb3DQEBCwUAMIGCMQswCQYD +VQQGEwJIVTERMA8GA1UEBwwIQnVkYXBlc3QxFjAUBgNVBAoMDU1pY3Jvc2VjIEx0 +ZC4xJzAlBgNVBAMMHk1pY3Jvc2VjIGUtU3ppZ25vIFJvb3QgQ0EgMjAwOTEfMB0G +CSqGSIb3DQEJARYQaW5mb0BlLXN6aWduby5odTAeFw0wOTA2MTYxMTMwMThaFw0y +OTEyMzAxMTMwMThaMIGCMQswCQYDVQQGEwJIVTERMA8GA1UEBwwIQnVkYXBlc3Qx +FjAUBgNVBAoMDU1pY3Jvc2VjIEx0ZC4xJzAlBgNVBAMMHk1pY3Jvc2VjIGUtU3pp +Z25vIFJvb3QgQ0EgMjAwOTEfMB0GCSqGSIb3DQEJARYQaW5mb0BlLXN6aWduby5o +dTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAOn4j/NjrdqG2KfgQvvP +kd6mJviZpWNwrZuuyjNAfW2WbqEORO7hE52UQlKavXWFdCyoDh2Tthi3jCyoz/tc +cbna7P7ofo/kLx2yqHWH2Leh5TvPmUpG0IMZfcChEhyVbUr02MelTTMuhTlAdX4U +fIASmFDHQWe4oIBhVKZsTh/gnQ4H6cm6M+f+wFUoLAKApxn1ntxVUwOXewdI/5n7 +N4okxFnMUBBjjqqpGrCEGob5X7uxUG6k0QrM1XF+H6cbfPVTbiJfyyvm1HxdrtbC +xkzlBQHZ7Vf8wSN5/PrIJIOV87VqUQHQd9bpEqH5GoP7ghu5sJf0dgYzQ0mg/wu1 ++rUCAwEAAaOBgDB+MA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgEGMB0G +A1UdDgQWBBTLD8bfQkPMPcu1SCOhGnqmKrs0aDAfBgNVHSMEGDAWgBTLD8bfQkPM +Pcu1SCOhGnqmKrs0aDAbBgNVHREEFDASgRBpbmZvQGUtc3ppZ25vLmh1MA0GCSqG +SIb3DQEBCwUAA4IBAQDJ0Q5eLtXMs3w+y/w9/w0olZMEyL/azXm4Q5DwpL7v8u8h +mLzU1F0G9u5C7DBsoKqpyvGvivo/C3NqPuouQH4frlRheesuCDfXI/OMn74dseGk +ddug4lQUsbocKaQY9hK6ohQU4zE1yED/t+AFdlfBHFny+L/k7SViXITwfn4fs775 +tyERzAMBVnCnEJIeGzSBHq2cGsMEPO0CYdYeBvNfOofyK/FFh+U9rNHHV4S9a67c +2Pm2G2JwCz02yULyMtd6YebS2z3PyKnJm9zbWETXbzivf3jTo60adbocwTZ8jx5t +HMN1Rq41Bab2XD0h7lbwyYIiLXpUq3DDfSJlgnCW +-----END CERTIFICATE----- + +# Issuer: CN=GlobalSign O=GlobalSign OU=GlobalSign Root CA - R3 +# Subject: CN=GlobalSign O=GlobalSign OU=GlobalSign Root CA - R3 +# Label: "GlobalSign Root CA - R3" +# Serial: 4835703278459759426209954 +# MD5 Fingerprint: c5:df:b8:49:ca:05:13:55:ee:2d:ba:1a:c3:3e:b0:28 +# SHA1 Fingerprint: d6:9b:56:11:48:f0:1c:77:c5:45:78:c1:09:26:df:5b:85:69:76:ad +# SHA256 Fingerprint: cb:b5:22:d7:b7:f1:27:ad:6a:01:13:86:5b:df:1c:d4:10:2e:7d:07:59:af:63:5a:7c:f4:72:0d:c9:63:c5:3b +-----BEGIN CERTIFICATE----- +MIIDXzCCAkegAwIBAgILBAAAAAABIVhTCKIwDQYJKoZIhvcNAQELBQAwTDEgMB4G +A1UECxMXR2xvYmFsU2lnbiBSb290IENBIC0gUjMxEzARBgNVBAoTCkdsb2JhbFNp +Z24xEzARBgNVBAMTCkdsb2JhbFNpZ24wHhcNMDkwMzE4MTAwMDAwWhcNMjkwMzE4 +MTAwMDAwWjBMMSAwHgYDVQQLExdHbG9iYWxTaWduIFJvb3QgQ0EgLSBSMzETMBEG +A1UEChMKR2xvYmFsU2lnbjETMBEGA1UEAxMKR2xvYmFsU2lnbjCCASIwDQYJKoZI +hvcNAQEBBQADggEPADCCAQoCggEBAMwldpB5BngiFvXAg7aEyiie/QV2EcWtiHL8 +RgJDx7KKnQRfJMsuS+FggkbhUqsMgUdwbN1k0ev1LKMPgj0MK66X17YUhhB5uzsT +gHeMCOFJ0mpiLx9e+pZo34knlTifBtc+ycsmWQ1z3rDI6SYOgxXG71uL0gRgykmm +KPZpO/bLyCiR5Z2KYVc3rHQU3HTgOu5yLy6c+9C7v/U9AOEGM+iCK65TpjoWc4zd +QQ4gOsC0p6Hpsk+QLjJg6VfLuQSSaGjlOCZgdbKfd/+RFO+uIEn8rUAVSNECMWEZ +XriX7613t2Saer9fwRPvm2L7DWzgVGkWqQPabumDk3F2xmmFghcCAwEAAaNCMEAw +DgYDVR0PAQH/BAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFI/wS3+o +LkUkrk1Q+mOai97i3Ru8MA0GCSqGSIb3DQEBCwUAA4IBAQBLQNvAUKr+yAzv95ZU +RUm7lgAJQayzE4aGKAczymvmdLm6AC2upArT9fHxD4q/c2dKg8dEe3jgr25sbwMp +jjM5RcOO5LlXbKr8EpbsU8Yt5CRsuZRj+9xTaGdWPoO4zzUhw8lo/s7awlOqzJCK +6fBdRoyV3XpYKBovHd7NADdBj+1EbddTKJd+82cEHhXXipa0095MJ6RMG3NzdvQX +mcIfeg7jLQitChws/zyrVQ4PkX4268NXSb7hLi18YIvDQVETI53O9zJrlAGomecs +Mx86OyXShkDOOyyGeMlhLxS67ttVb9+E7gUJTb0o2HLO02JQZR7rkpeDMdmztcpH +WD9f +-----END CERTIFICATE----- + +# Issuer: CN=Autoridad de Certificacion Firmaprofesional CIF A62634068 +# Subject: CN=Autoridad de Certificacion Firmaprofesional CIF A62634068 +# Label: "Autoridad de Certificacion Firmaprofesional CIF A62634068" +# Serial: 6047274297262753887 +# MD5 Fingerprint: 73:3a:74:7a:ec:bb:a3:96:a6:c2:e4:e2:c8:9b:c0:c3 +# SHA1 Fingerprint: ae:c5:fb:3f:c8:e1:bf:c4:e5:4f:03:07:5a:9a:e8:00:b7:f7:b6:fa +# SHA256 Fingerprint: 04:04:80:28:bf:1f:28:64:d4:8f:9a:d4:d8:32:94:36:6a:82:88:56:55:3f:3b:14:30:3f:90:14:7f:5d:40:ef +-----BEGIN CERTIFICATE----- +MIIGFDCCA/ygAwIBAgIIU+w77vuySF8wDQYJKoZIhvcNAQEFBQAwUTELMAkGA1UE +BhMCRVMxQjBABgNVBAMMOUF1dG9yaWRhZCBkZSBDZXJ0aWZpY2FjaW9uIEZpcm1h +cHJvZmVzaW9uYWwgQ0lGIEE2MjYzNDA2ODAeFw0wOTA1MjAwODM4MTVaFw0zMDEy +MzEwODM4MTVaMFExCzAJBgNVBAYTAkVTMUIwQAYDVQQDDDlBdXRvcmlkYWQgZGUg +Q2VydGlmaWNhY2lvbiBGaXJtYXByb2Zlc2lvbmFsIENJRiBBNjI2MzQwNjgwggIi +MA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDKlmuO6vj78aI14H9M2uDDUtd9 +thDIAl6zQyrET2qyyhxdKJp4ERppWVevtSBC5IsP5t9bpgOSL/UR5GLXMnE42QQM +cas9UX4PB99jBVzpv5RvwSmCwLTaUbDBPLutN0pcyvFLNg4kq7/DhHf9qFD0sefG +L9ItWY16Ck6WaVICqjaY7Pz6FIMMNx/Jkjd/14Et5cS54D40/mf0PmbR0/RAz15i +NA9wBj4gGFrO93IbJWyTdBSTo3OxDqqHECNZXyAFGUftaI6SEspd/NYrspI8IM/h +X68gvqB2f3bl7BqGYTM+53u0P6APjqK5am+5hyZvQWyIplD9amML9ZMWGxmPsu2b +m8mQ9QEM3xk9Dz44I8kvjwzRAv4bVdZO0I08r0+k8/6vKtMFnXkIoctXMbScyJCy +Z/QYFpM6/EfY0XiWMR+6KwxfXZmtY4laJCB22N/9q06mIqqdXuYnin1oKaPnirja +EbsXLZmdEyRG98Xi2J+Of8ePdG1asuhy9azuJBCtLxTa/y2aRnFHvkLfuwHb9H/T +KI8xWVvTyQKmtFLKbpf7Q8UIJm+K9Lv9nyiqDdVF8xM6HdjAeI9BZzwelGSuewvF +6NkBiDkal4ZkQdU7hwxu+g/GvUgUvzlN1J5Bto+WHWOWk9mVBngxaJ43BjuAiUVh +OSPHG0SjFeUc+JIwuwIDAQABo4HvMIHsMBIGA1UdEwEB/wQIMAYBAf8CAQEwDgYD +VR0PAQH/BAQDAgEGMB0GA1UdDgQWBBRlzeurNR4APn7VdMActHNHDhpkLzCBpgYD +VR0gBIGeMIGbMIGYBgRVHSAAMIGPMC8GCCsGAQUFBwIBFiNodHRwOi8vd3d3LmZp +cm1hcHJvZmVzaW9uYWwuY29tL2NwczBcBggrBgEFBQcCAjBQHk4AUABhAHMAZQBv +ACAAZABlACAAbABhACAAQgBvAG4AYQBuAG8AdgBhACAANAA3ACAAQgBhAHIAYwBl +AGwAbwBuAGEAIAAwADgAMAAxADcwDQYJKoZIhvcNAQEFBQADggIBABd9oPm03cXF +661LJLWhAqvdpYhKsg9VSytXjDvlMd3+xDLx51tkljYyGOylMnfX40S2wBEqgLk9 +am58m9Ot/MPWo+ZkKXzR4Tgegiv/J2Wv+xYVxC5xhOW1//qkR71kMrv2JYSiJ0L1 +ILDCExARzRAVukKQKtJE4ZYm6zFIEv0q2skGz3QeqUvVhyj5eTSSPi5E6PaPT481 +PyWzOdxjKpBrIF/EUhJOlywqrJ2X3kjyo2bbwtKDlaZmp54lD+kLM5FlClrD2VQS +3a/DTg4fJl4N3LON7NWBcN7STyQF82xO9UxJZo3R/9ILJUFI/lGExkKvgATP0H5k +SeTy36LssUzAKh3ntLFlosS88Zj0qnAHY7S42jtM+kAiMFsRpvAFDsYCA0irhpuF +3dvd6qJ2gHN99ZwExEWN57kci57q13XRcrHedUTnQn3iV2t93Jm8PYMo6oCTjcVM +ZcFwgbg4/EMxsvYDNEeyrPsiBsse3RdHHF9mudMaotoRsaS8I8nkvof/uZS2+F0g +StRf571oe2XyFR7SOqkt6dhrJKyXWERHrVkY8SFlcN7ONGCoQPHzPKTDKCOM/icz +Q0CgFzzr6juwcqajuUpLXhZI9LK8yIySxZ2frHI2vDSANGupi5LAuBft7HZT9SQB +jLMi6Et8Vcad+qMUu2WFbm5PEn4KPJ2V +-----END CERTIFICATE----- + +# Issuer: CN=Izenpe.com O=IZENPE S.A. +# Subject: CN=Izenpe.com O=IZENPE S.A. +# Label: "Izenpe.com" +# Serial: 917563065490389241595536686991402621 +# MD5 Fingerprint: a6:b0:cd:85:80:da:5c:50:34:a3:39:90:2f:55:67:73 +# SHA1 Fingerprint: 2f:78:3d:25:52:18:a7:4a:65:39:71:b5:2c:a2:9c:45:15:6f:e9:19 +# SHA256 Fingerprint: 25:30:cc:8e:98:32:15:02:ba:d9:6f:9b:1f:ba:1b:09:9e:2d:29:9e:0f:45:48:bb:91:4f:36:3b:c0:d4:53:1f +-----BEGIN CERTIFICATE----- +MIIF8TCCA9mgAwIBAgIQALC3WhZIX7/hy/WL1xnmfTANBgkqhkiG9w0BAQsFADA4 +MQswCQYDVQQGEwJFUzEUMBIGA1UECgwLSVpFTlBFIFMuQS4xEzARBgNVBAMMCkl6 +ZW5wZS5jb20wHhcNMDcxMjEzMTMwODI4WhcNMzcxMjEzMDgyNzI1WjA4MQswCQYD +VQQGEwJFUzEUMBIGA1UECgwLSVpFTlBFIFMuQS4xEzARBgNVBAMMCkl6ZW5wZS5j +b20wggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDJ03rKDx6sp4boFmVq +scIbRTJxldn+EFvMr+eleQGPicPK8lVx93e+d5TzcqQsRNiekpsUOqHnJJAKClaO +xdgmlOHZSOEtPtoKct2jmRXagaKH9HtuJneJWK3W6wyyQXpzbm3benhB6QiIEn6H +LmYRY2xU+zydcsC8Lv/Ct90NduM61/e0aL6i9eOBbsFGb12N4E3GVFWJGjMxCrFX +uaOKmMPsOzTFlUFpfnXCPCDFYbpRR6AgkJOhkEvzTnyFRVSa0QUmQbC1TR0zvsQD +yCV8wXDbO/QJLVQnSKwv4cSsPsjLkkxTOTcj7NMB+eAJRE1NZMDhDVqHIrytG6P+ +JrUV86f8hBnp7KGItERphIPzidF0BqnMC9bC3ieFUCbKF7jJeodWLBoBHmy+E60Q +rLUk9TiRodZL2vG70t5HtfG8gfZZa88ZU+mNFctKy6lvROUbQc/hhqfK0GqfvEyN +BjNaooXlkDWgYlwWTvDjovoDGrQscbNYLN57C9saD+veIR8GdwYDsMnvmfzAuU8L +hij+0rnq49qlw0dpEuDb8PYZi+17cNcC1u2HGCgsBCRMd+RIihrGO5rUD8r6ddIB +QFqNeb+Lz0vPqhbBleStTIo+F5HUsWLlguWABKQDfo2/2n+iD5dPDNMN+9fR5XJ+ +HMh3/1uaD7euBUbl8agW7EekFwIDAQABo4H2MIHzMIGwBgNVHREEgagwgaWBD2lu +Zm9AaXplbnBlLmNvbaSBkTCBjjFHMEUGA1UECgw+SVpFTlBFIFMuQS4gLSBDSUYg +QTAxMzM3MjYwLVJNZXJjLlZpdG9yaWEtR2FzdGVpeiBUMTA1NSBGNjIgUzgxQzBB +BgNVBAkMOkF2ZGEgZGVsIE1lZGl0ZXJyYW5lbyBFdG9yYmlkZWEgMTQgLSAwMTAx +MCBWaXRvcmlhLUdhc3RlaXowDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMC +AQYwHQYDVR0OBBYEFB0cZQ6o8iV7tJHP5LGx5r1VdGwFMA0GCSqGSIb3DQEBCwUA +A4ICAQB4pgwWSp9MiDrAyw6lFn2fuUhfGI8NYjb2zRlrrKvV9pF9rnHzP7MOeIWb +laQnIUdCSnxIOvVFfLMMjlF4rJUT3sb9fbgakEyrkgPH7UIBzg/YsfqikuFgba56 +awmqxinuaElnMIAkejEWOVt+8Rwu3WwJrfIxwYJOubv5vr8qhT/AQKM6WfxZSzwo +JNu0FXWuDYi6LnPAvViH5ULy617uHjAimcs30cQhbIHsvm0m5hzkQiCeR7Csg1lw +LDXWrzY0tM07+DKo7+N4ifuNRSzanLh+QBxh5z6ikixL8s36mLYp//Pye6kfLqCT +VyvehQP5aTfLnnhqBbTFMXiJ7HqnheG5ezzevh55hM6fcA5ZwjUukCox2eRFekGk +LhObNA5me0mrZJfQRsN5nXJQY6aYWwa9SG3YOYNw6DXwBdGqvOPbyALqfP2C2sJb +UjWumDqtujWTI6cfSN01RpiyEGjkpTHCClguGYEQyVB1/OpaFs4R1+7vUIgtYf8/ +QnMFlEPVjjxOAToZpR9GTnfQXeWBIiGH/pR9hNiTrdZoQ0iy2+tzJOeRf1SktoA+ +naM8THLCV8Sg1Mw4J87VBp6iSNnpn86CcDaTmjvfliHjWbcM2pE38P1ZWrOZyGls +QyYBNWNgVYkDOnXYukrZVP/u3oDYLdE41V4tC5h9Pmzb/CaIxw== +-----END CERTIFICATE----- + +# Issuer: CN=Chambers of Commerce Root - 2008 O=AC Camerfirma S.A. +# Subject: CN=Chambers of Commerce Root - 2008 O=AC Camerfirma S.A. +# Label: "Chambers of Commerce Root - 2008" +# Serial: 11806822484801597146 +# MD5 Fingerprint: 5e:80:9e:84:5a:0e:65:0b:17:02:f3:55:18:2a:3e:d7 +# SHA1 Fingerprint: 78:6a:74:ac:76:ab:14:7f:9c:6a:30:50:ba:9e:a8:7e:fe:9a:ce:3c +# SHA256 Fingerprint: 06:3e:4a:fa:c4:91:df:d3:32:f3:08:9b:85:42:e9:46:17:d8:93:d7:fe:94:4e:10:a7:93:7e:e2:9d:96:93:c0 +-----BEGIN CERTIFICATE----- +MIIHTzCCBTegAwIBAgIJAKPaQn6ksa7aMA0GCSqGSIb3DQEBBQUAMIGuMQswCQYD +VQQGEwJFVTFDMEEGA1UEBxM6TWFkcmlkIChzZWUgY3VycmVudCBhZGRyZXNzIGF0 +IHd3dy5jYW1lcmZpcm1hLmNvbS9hZGRyZXNzKTESMBAGA1UEBRMJQTgyNzQzMjg3 +MRswGQYDVQQKExJBQyBDYW1lcmZpcm1hIFMuQS4xKTAnBgNVBAMTIENoYW1iZXJz +IG9mIENvbW1lcmNlIFJvb3QgLSAyMDA4MB4XDTA4MDgwMTEyMjk1MFoXDTM4MDcz +MTEyMjk1MFowga4xCzAJBgNVBAYTAkVVMUMwQQYDVQQHEzpNYWRyaWQgKHNlZSBj +dXJyZW50IGFkZHJlc3MgYXQgd3d3LmNhbWVyZmlybWEuY29tL2FkZHJlc3MpMRIw +EAYDVQQFEwlBODI3NDMyODcxGzAZBgNVBAoTEkFDIENhbWVyZmlybWEgUy5BLjEp +MCcGA1UEAxMgQ2hhbWJlcnMgb2YgQ29tbWVyY2UgUm9vdCAtIDIwMDgwggIiMA0G +CSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQCvAMtwNyuAWko6bHiUfaN/Gh/2NdW9 +28sNRHI+JrKQUrpjOyhYb6WzbZSm891kDFX29ufyIiKAXuFixrYp4YFs8r/lfTJq +VKAyGVn+H4vXPWCGhSRv4xGzdz4gljUha7MI2XAuZPeEklPWDrCQiorjh40G072Q +DuKZoRuGDtqaCrsLYVAGUvGef3bsyw/QHg3PmTA9HMRFEFis1tPo1+XqxQEHd9ZR +5gN/ikilTWh1uem8nk4ZcfUyS5xtYBkL+8ydddy/Js2Pk3g5eXNeJQ7KXOt3EgfL +ZEFHcpOrUMPrCXZkNNI5t3YRCQ12RcSprj1qr7V9ZS+UWBDsXHyvfuK2GNnQm05a +Sd+pZgvMPMZ4fKecHePOjlO+Bd5gD2vlGts/4+EhySnB8esHnFIbAURRPHsl18Tl +UlRdJQfKFiC4reRB7noI/plvg6aRArBsNlVq5331lubKgdaX8ZSD6e2wsWsSaR6s ++12pxZjptFtYer49okQ6Y1nUCyXeG0+95QGezdIp1Z8XGQpvvwyQ0wlf2eOKNcx5 +Wk0ZN5K3xMGtr/R5JJqyAQuxr1yW84Ay+1w9mPGgP0revq+ULtlVmhduYJ1jbLhj +ya6BXBg14JC7vjxPNyK5fuvPnnchpj04gftI2jE9K+OJ9dC1vX7gUMQSibMjmhAx +hduub+84Mxh2EQIDAQABo4IBbDCCAWgwEgYDVR0TAQH/BAgwBgEB/wIBDDAdBgNV +HQ4EFgQU+SSsD7K1+HnA+mCIG8TZTQKeFxkwgeMGA1UdIwSB2zCB2IAU+SSsD7K1 ++HnA+mCIG8TZTQKeFxmhgbSkgbEwga4xCzAJBgNVBAYTAkVVMUMwQQYDVQQHEzpN +YWRyaWQgKHNlZSBjdXJyZW50IGFkZHJlc3MgYXQgd3d3LmNhbWVyZmlybWEuY29t +L2FkZHJlc3MpMRIwEAYDVQQFEwlBODI3NDMyODcxGzAZBgNVBAoTEkFDIENhbWVy +ZmlybWEgUy5BLjEpMCcGA1UEAxMgQ2hhbWJlcnMgb2YgQ29tbWVyY2UgUm9vdCAt +IDIwMDiCCQCj2kJ+pLGu2jAOBgNVHQ8BAf8EBAMCAQYwPQYDVR0gBDYwNDAyBgRV +HSAAMCowKAYIKwYBBQUHAgEWHGh0dHA6Ly9wb2xpY3kuY2FtZXJmaXJtYS5jb20w +DQYJKoZIhvcNAQEFBQADggIBAJASryI1wqM58C7e6bXpeHxIvj99RZJe6dqxGfwW +PJ+0W2aeaufDuV2I6A+tzyMP3iU6XsxPpcG1Lawk0lgH3qLPaYRgM+gQDROpI9CF +5Y57pp49chNyM/WqfcZjHwj0/gF/JM8rLFQJ3uIrbZLGOU8W6jx+ekbURWpGqOt1 +glanq6B8aBMz9p0w8G8nOSQjKpD9kCk18pPfNKXG9/jvjA9iSnyu0/VU+I22mlaH +FoI6M6taIgj3grrqLuBHmrS1RaMFO9ncLkVAO+rcf+g769HsJtg1pDDFOqxXnrN2 +pSB7+R5KBWIBpih1YJeSDW4+TTdDDZIVnBgizVGZoCkaPF+KMjNbMMeJL0eYD6MD +xvbxrN8y8NmBGuScvfaAFPDRLLmF9dijscilIeUcE5fuDr3fKanvNFNb0+RqE4QG +tjICxFKuItLcsiFCGtpA8CnJ7AoMXOLQusxI0zcKzBIKinmwPQN/aUv0NCB9szTq +jktk9T79syNnFQ0EuPAtwQlRPLJsFfClI9eDdOTlLsn+mCdCxqvGnrDQWzilm1De +fhiYtUU79nm06PcaewaD+9CL2rvHvRirCG88gGtAPxkZumWK5r7VXNM21+9AUiRg +OGcEMeyP84LG3rlV8zsxkVrctQgVrXYlCg17LofiDKYGvCYQbTed7N14jHyAxfDZ +d0jQ +-----END CERTIFICATE----- + +# Issuer: CN=Global Chambersign Root - 2008 O=AC Camerfirma S.A. +# Subject: CN=Global Chambersign Root - 2008 O=AC Camerfirma S.A. +# Label: "Global Chambersign Root - 2008" +# Serial: 14541511773111788494 +# MD5 Fingerprint: 9e:80:ff:78:01:0c:2e:c1:36:bd:fe:96:90:6e:08:f3 +# SHA1 Fingerprint: 4a:bd:ee:ec:95:0d:35:9c:89:ae:c7:52:a1:2c:5b:29:f6:d6:aa:0c +# SHA256 Fingerprint: 13:63:35:43:93:34:a7:69:80:16:a0:d3:24:de:72:28:4e:07:9d:7b:52:20:bb:8f:bd:74:78:16:ee:be:ba:ca +-----BEGIN CERTIFICATE----- +MIIHSTCCBTGgAwIBAgIJAMnN0+nVfSPOMA0GCSqGSIb3DQEBBQUAMIGsMQswCQYD +VQQGEwJFVTFDMEEGA1UEBxM6TWFkcmlkIChzZWUgY3VycmVudCBhZGRyZXNzIGF0 +IHd3dy5jYW1lcmZpcm1hLmNvbS9hZGRyZXNzKTESMBAGA1UEBRMJQTgyNzQzMjg3 +MRswGQYDVQQKExJBQyBDYW1lcmZpcm1hIFMuQS4xJzAlBgNVBAMTHkdsb2JhbCBD +aGFtYmVyc2lnbiBSb290IC0gMjAwODAeFw0wODA4MDExMjMxNDBaFw0zODA3MzEx +MjMxNDBaMIGsMQswCQYDVQQGEwJFVTFDMEEGA1UEBxM6TWFkcmlkIChzZWUgY3Vy +cmVudCBhZGRyZXNzIGF0IHd3dy5jYW1lcmZpcm1hLmNvbS9hZGRyZXNzKTESMBAG +A1UEBRMJQTgyNzQzMjg3MRswGQYDVQQKExJBQyBDYW1lcmZpcm1hIFMuQS4xJzAl +BgNVBAMTHkdsb2JhbCBDaGFtYmVyc2lnbiBSb290IC0gMjAwODCCAiIwDQYJKoZI +hvcNAQEBBQADggIPADCCAgoCggIBAMDfVtPkOpt2RbQT2//BthmLN0EYlVJH6xed +KYiONWwGMi5HYvNJBL99RDaxccy9Wglz1dmFRP+RVyXfXjaOcNFccUMd2drvXNL7 +G706tcuto8xEpw2uIRU/uXpbknXYpBI4iRmKt4DS4jJvVpyR1ogQC7N0ZJJ0YPP2 +zxhPYLIj0Mc7zmFLmY/CDNBAspjcDahOo7kKrmCgrUVSY7pmvWjg+b4aqIG7HkF4 +ddPB/gBVsIdU6CeQNR1MM62X/JcumIS/LMmjv9GYERTtY/jKmIhYF5ntRQOXfjyG +HoiMvvKRhI9lNNgATH23MRdaKXoKGCQwoze1eqkBfSbW+Q6OWfH9GzO1KTsXO0G2 +Id3UwD2ln58fQ1DJu7xsepeY7s2MH/ucUa6LcL0nn3HAa6x9kGbo1106DbDVwo3V +yJ2dwW3Q0L9R5OP4wzg2rtandeavhENdk5IMagfeOx2YItaswTXbo6Al/3K1dh3e +beksZixShNBFks4c5eUzHdwHU1SjqoI7mjcv3N2gZOnm3b2u/GSFHTynyQbehP9r +6GsaPMWis0L7iwk+XwhSx2LE1AVxv8Rk5Pihg+g+EpuoHtQ2TS9x9o0o9oOpE9Jh +wZG7SMA0j0GMS0zbaRL/UJScIINZc+18ofLx/d33SdNDWKBWY8o9PeU1VlnpDsog +zCtLkykPAgMBAAGjggFqMIIBZjASBgNVHRMBAf8ECDAGAQH/AgEMMB0GA1UdDgQW +BBS5CcqcHtvTbDprru1U8VuTBjUuXjCB4QYDVR0jBIHZMIHWgBS5CcqcHtvTbDpr +ru1U8VuTBjUuXqGBsqSBrzCBrDELMAkGA1UEBhMCRVUxQzBBBgNVBAcTOk1hZHJp +ZCAoc2VlIGN1cnJlbnQgYWRkcmVzcyBhdCB3d3cuY2FtZXJmaXJtYS5jb20vYWRk +cmVzcykxEjAQBgNVBAUTCUE4Mjc0MzI4NzEbMBkGA1UEChMSQUMgQ2FtZXJmaXJt +YSBTLkEuMScwJQYDVQQDEx5HbG9iYWwgQ2hhbWJlcnNpZ24gUm9vdCAtIDIwMDiC +CQDJzdPp1X0jzjAOBgNVHQ8BAf8EBAMCAQYwPQYDVR0gBDYwNDAyBgRVHSAAMCow +KAYIKwYBBQUHAgEWHGh0dHA6Ly9wb2xpY3kuY2FtZXJmaXJtYS5jb20wDQYJKoZI +hvcNAQEFBQADggIBAICIf3DekijZBZRG/5BXqfEv3xoNa/p8DhxJJHkn2EaqbylZ +UohwEurdPfWbU1Rv4WCiqAm57OtZfMY18dwY6fFn5a+6ReAJ3spED8IXDneRRXoz +X1+WLGiLwUePmJs9wOzL9dWCkoQ10b42OFZyMVtHLaoXpGNR6woBrX/sdZ7LoR/x +fxKxueRkf2fWIyr0uDldmOghp+G9PUIadJpwr2hsUF1Jz//7Dl3mLEfXgTpZALVz +a2Mg9jFFCDkO9HB+QHBaP9BrQql0PSgvAm11cpUJjUhjxsYjV5KTXjXBjfkK9yyd +Yhz2rXzdpjEetrHHfoUm+qRqtdpjMNHvkzeyZi99Bffnt0uYlDXA2TopwZ2yUDMd +SqlapskD7+3056huirRXhOukP9DuqqqHW2Pok+JrqNS4cnhrG+055F3Lm6qH1U9O +AP7Zap88MQ8oAgF9mOinsKJknnn4SPIVqczmyETrP3iZ8ntxPjzxmKfFGBI/5rso +M0LpRQp8bfKGeS/Fghl9CYl8slR2iK7ewfPM4W7bMdaTrpmg7yVqc5iJWzouE4ge +v8CSlDQb4ye3ix5vQv/n6TebUB0tovkC7stYWDpxvGjjqsGvHCgfotwjZT+B6q6Z +09gwzxMNTxXJhLynSC34MCN32EZLeW32jO06f2ARePTpm67VVMB0gNELQp/B +-----END CERTIFICATE----- + +# Issuer: CN=Go Daddy Root Certificate Authority - G2 O=GoDaddy.com, Inc. +# Subject: CN=Go Daddy Root Certificate Authority - G2 O=GoDaddy.com, Inc. +# Label: "Go Daddy Root Certificate Authority - G2" +# Serial: 0 +# MD5 Fingerprint: 80:3a:bc:22:c1:e6:fb:8d:9b:3b:27:4a:32:1b:9a:01 +# SHA1 Fingerprint: 47:be:ab:c9:22:ea:e8:0e:78:78:34:62:a7:9f:45:c2:54:fd:e6:8b +# SHA256 Fingerprint: 45:14:0b:32:47:eb:9c:c8:c5:b4:f0:d7:b5:30:91:f7:32:92:08:9e:6e:5a:63:e2:74:9d:d3:ac:a9:19:8e:da +-----BEGIN CERTIFICATE----- +MIIDxTCCAq2gAwIBAgIBADANBgkqhkiG9w0BAQsFADCBgzELMAkGA1UEBhMCVVMx +EDAOBgNVBAgTB0FyaXpvbmExEzARBgNVBAcTClNjb3R0c2RhbGUxGjAYBgNVBAoT +EUdvRGFkZHkuY29tLCBJbmMuMTEwLwYDVQQDEyhHbyBEYWRkeSBSb290IENlcnRp +ZmljYXRlIEF1dGhvcml0eSAtIEcyMB4XDTA5MDkwMTAwMDAwMFoXDTM3MTIzMTIz +NTk1OVowgYMxCzAJBgNVBAYTAlVTMRAwDgYDVQQIEwdBcml6b25hMRMwEQYDVQQH +EwpTY290dHNkYWxlMRowGAYDVQQKExFHb0RhZGR5LmNvbSwgSW5jLjExMC8GA1UE +AxMoR28gRGFkZHkgUm9vdCBDZXJ0aWZpY2F0ZSBBdXRob3JpdHkgLSBHMjCCASIw +DQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAL9xYgjx+lk09xvJGKP3gElY6SKD +E6bFIEMBO4Tx5oVJnyfq9oQbTqC023CYxzIBsQU+B07u9PpPL1kwIuerGVZr4oAH +/PMWdYA5UXvl+TW2dE6pjYIT5LY/qQOD+qK+ihVqf94Lw7YZFAXK6sOoBJQ7Rnwy +DfMAZiLIjWltNowRGLfTshxgtDj6AozO091GB94KPutdfMh8+7ArU6SSYmlRJQVh +GkSBjCypQ5Yj36w6gZoOKcUcqeldHraenjAKOc7xiID7S13MMuyFYkMlNAJWJwGR +tDtwKj9useiciAF9n9T521NtYJ2/LOdYq7hfRvzOxBsDPAnrSTFcaUaz4EcCAwEA +AaNCMEAwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwHQYDVR0OBBYE +FDqahQcQZyi27/a9BUFuIMGU2g/eMA0GCSqGSIb3DQEBCwUAA4IBAQCZ21151fmX +WWcDYfF+OwYxdS2hII5PZYe096acvNjpL9DbWu7PdIxztDhC2gV7+AJ1uP2lsdeu +9tfeE8tTEH6KRtGX+rcuKxGrkLAngPnon1rpN5+r5N9ss4UXnT3ZJE95kTXWXwTr +gIOrmgIttRD02JDHBHNA7XIloKmf7J6raBKZV8aPEjoJpL1E/QYVN8Gb5DKj7Tjo +2GTzLH4U/ALqn83/B2gX2yKQOC16jdFU8WnjXzPKej17CuPKf1855eJ1usV2GDPO +LPAvTK33sefOT6jEm0pUBsV/fdUID+Ic/n4XuKxe9tQWskMJDE32p2u0mYRlynqI +4uJEvlz36hz1 +-----END CERTIFICATE----- + +# Issuer: CN=Starfield Root Certificate Authority - G2 O=Starfield Technologies, Inc. +# Subject: CN=Starfield Root Certificate Authority - G2 O=Starfield Technologies, Inc. +# Label: "Starfield Root Certificate Authority - G2" +# Serial: 0 +# MD5 Fingerprint: d6:39:81:c6:52:7e:96:69:fc:fc:ca:66:ed:05:f2:96 +# SHA1 Fingerprint: b5:1c:06:7c:ee:2b:0c:3d:f8:55:ab:2d:92:f4:fe:39:d4:e7:0f:0e +# SHA256 Fingerprint: 2c:e1:cb:0b:f9:d2:f9:e1:02:99:3f:be:21:51:52:c3:b2:dd:0c:ab:de:1c:68:e5:31:9b:83:91:54:db:b7:f5 +-----BEGIN CERTIFICATE----- +MIID3TCCAsWgAwIBAgIBADANBgkqhkiG9w0BAQsFADCBjzELMAkGA1UEBhMCVVMx +EDAOBgNVBAgTB0FyaXpvbmExEzARBgNVBAcTClNjb3R0c2RhbGUxJTAjBgNVBAoT +HFN0YXJmaWVsZCBUZWNobm9sb2dpZXMsIEluYy4xMjAwBgNVBAMTKVN0YXJmaWVs +ZCBSb290IENlcnRpZmljYXRlIEF1dGhvcml0eSAtIEcyMB4XDTA5MDkwMTAwMDAw +MFoXDTM3MTIzMTIzNTk1OVowgY8xCzAJBgNVBAYTAlVTMRAwDgYDVQQIEwdBcml6 +b25hMRMwEQYDVQQHEwpTY290dHNkYWxlMSUwIwYDVQQKExxTdGFyZmllbGQgVGVj +aG5vbG9naWVzLCBJbmMuMTIwMAYDVQQDEylTdGFyZmllbGQgUm9vdCBDZXJ0aWZp +Y2F0ZSBBdXRob3JpdHkgLSBHMjCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoC +ggEBAL3twQP89o/8ArFvW59I2Z154qK3A2FWGMNHttfKPTUuiUP3oWmb3ooa/RMg +nLRJdzIpVv257IzdIvpy3Cdhl+72WoTsbhm5iSzchFvVdPtrX8WJpRBSiUZV9Lh1 +HOZ/5FSuS/hVclcCGfgXcVnrHigHdMWdSL5stPSksPNkN3mSwOxGXn/hbVNMYq/N +Hwtjuzqd+/x5AJhhdM8mgkBj87JyahkNmcrUDnXMN/uLicFZ8WJ/X7NfZTD4p7dN +dloedl40wOiWVpmKs/B/pM293DIxfJHP4F8R+GuqSVzRmZTRouNjWwl2tVZi4Ut0 +HZbUJtQIBFnQmA4O5t78w+wfkPECAwEAAaNCMEAwDwYDVR0TAQH/BAUwAwEB/zAO +BgNVHQ8BAf8EBAMCAQYwHQYDVR0OBBYEFHwMMh+n2TB/xH1oo2Kooc6rB1snMA0G +CSqGSIb3DQEBCwUAA4IBAQARWfolTwNvlJk7mh+ChTnUdgWUXuEok21iXQnCoKjU +sHU48TRqneSfioYmUeYs0cYtbpUgSpIB7LiKZ3sx4mcujJUDJi5DnUox9g61DLu3 +4jd/IroAow57UvtruzvE03lRTs2Q9GcHGcg8RnoNAX3FWOdt5oUwF5okxBDgBPfg +8n/Uqgr/Qh037ZTlZFkSIHc40zI+OIF1lnP6aI+xy84fxez6nH7PfrHxBy22/L/K +pL/QlwVKvOoYKAKQvVR4CSFx09F9HdkWsKlhPdAKACL8x3vLCWRFCztAgfd9fDL1 +mMpYjn0q7pBZc2T5NnReJaH1ZgUufzkVqSr7UIuOhWn0 +-----END CERTIFICATE----- + +# Issuer: CN=Starfield Services Root Certificate Authority - G2 O=Starfield Technologies, Inc. +# Subject: CN=Starfield Services Root Certificate Authority - G2 O=Starfield Technologies, Inc. +# Label: "Starfield Services Root Certificate Authority - G2" +# Serial: 0 +# MD5 Fingerprint: 17:35:74:af:7b:61:1c:eb:f4:f9:3c:e2:ee:40:f9:a2 +# SHA1 Fingerprint: 92:5a:8f:8d:2c:6d:04:e0:66:5f:59:6a:ff:22:d8:63:e8:25:6f:3f +# SHA256 Fingerprint: 56:8d:69:05:a2:c8:87:08:a4:b3:02:51:90:ed:cf:ed:b1:97:4a:60:6a:13:c6:e5:29:0f:cb:2a:e6:3e:da:b5 +-----BEGIN CERTIFICATE----- +MIID7zCCAtegAwIBAgIBADANBgkqhkiG9w0BAQsFADCBmDELMAkGA1UEBhMCVVMx +EDAOBgNVBAgTB0FyaXpvbmExEzARBgNVBAcTClNjb3R0c2RhbGUxJTAjBgNVBAoT +HFN0YXJmaWVsZCBUZWNobm9sb2dpZXMsIEluYy4xOzA5BgNVBAMTMlN0YXJmaWVs +ZCBTZXJ2aWNlcyBSb290IENlcnRpZmljYXRlIEF1dGhvcml0eSAtIEcyMB4XDTA5 +MDkwMTAwMDAwMFoXDTM3MTIzMTIzNTk1OVowgZgxCzAJBgNVBAYTAlVTMRAwDgYD +VQQIEwdBcml6b25hMRMwEQYDVQQHEwpTY290dHNkYWxlMSUwIwYDVQQKExxTdGFy +ZmllbGQgVGVjaG5vbG9naWVzLCBJbmMuMTswOQYDVQQDEzJTdGFyZmllbGQgU2Vy +dmljZXMgUm9vdCBDZXJ0aWZpY2F0ZSBBdXRob3JpdHkgLSBHMjCCASIwDQYJKoZI +hvcNAQEBBQADggEPADCCAQoCggEBANUMOsQq+U7i9b4Zl1+OiFOxHz/Lz58gE20p +OsgPfTz3a3Y4Y9k2YKibXlwAgLIvWX/2h/klQ4bnaRtSmpDhcePYLQ1Ob/bISdm2 +8xpWriu2dBTrz/sm4xq6HZYuajtYlIlHVv8loJNwU4PahHQUw2eeBGg6345AWh1K +Ts9DkTvnVtYAcMtS7nt9rjrnvDH5RfbCYM8TWQIrgMw0R9+53pBlbQLPLJGmpufe +hRhJfGZOozptqbXuNC66DQO4M99H67FrjSXZm86B0UVGMpZwh94CDklDhbZsc7tk +6mFBrMnUVN+HL8cisibMn1lUaJ/8viovxFUcdUBgF4UCVTmLfwUCAwEAAaNCMEAw +DwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwHQYDVR0OBBYEFJxfAN+q +AdcwKziIorhtSpzyEZGDMA0GCSqGSIb3DQEBCwUAA4IBAQBLNqaEd2ndOxmfZyMI +bw5hyf2E3F/YNoHN2BtBLZ9g3ccaaNnRbobhiCPPE95Dz+I0swSdHynVv/heyNXB +ve6SbzJ08pGCL72CQnqtKrcgfU28elUSwhXqvfdqlS5sdJ/PHLTyxQGjhdByPq1z +qwubdQxtRbeOlKyWN7Wg0I8VRw7j6IPdj/3vQQF3zCepYoUz8jcI73HPdwbeyBkd +iEDPfUYd/x7H4c7/I9vG+o1VTqkC50cRRj70/b17KSa7qWFiNyi2LSr2EIZkyXCn +0q23KXB56jzaYyWf/Wi3MOxw+3WKt21gZ7IeyLnp2KhvAotnDU0mV3HaIPzBSlCN +sSi6 +-----END CERTIFICATE----- + +# Issuer: CN=AffirmTrust Commercial O=AffirmTrust +# Subject: CN=AffirmTrust Commercial O=AffirmTrust +# Label: "AffirmTrust Commercial" +# Serial: 8608355977964138876 +# MD5 Fingerprint: 82:92:ba:5b:ef:cd:8a:6f:a6:3d:55:f9:84:f6:d6:b7 +# SHA1 Fingerprint: f9:b5:b6:32:45:5f:9c:be:ec:57:5f:80:dc:e9:6e:2c:c7:b2:78:b7 +# SHA256 Fingerprint: 03:76:ab:1d:54:c5:f9:80:3c:e4:b2:e2:01:a0:ee:7e:ef:7b:57:b6:36:e8:a9:3c:9b:8d:48:60:c9:6f:5f:a7 +-----BEGIN CERTIFICATE----- +MIIDTDCCAjSgAwIBAgIId3cGJyapsXwwDQYJKoZIhvcNAQELBQAwRDELMAkGA1UE +BhMCVVMxFDASBgNVBAoMC0FmZmlybVRydXN0MR8wHQYDVQQDDBZBZmZpcm1UcnVz +dCBDb21tZXJjaWFsMB4XDTEwMDEyOTE0MDYwNloXDTMwMTIzMTE0MDYwNlowRDEL +MAkGA1UEBhMCVVMxFDASBgNVBAoMC0FmZmlybVRydXN0MR8wHQYDVQQDDBZBZmZp +cm1UcnVzdCBDb21tZXJjaWFsMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKC +AQEA9htPZwcroRX1BiLLHwGy43NFBkRJLLtJJRTWzsO3qyxPxkEylFf6EqdbDuKP +Hx6GGaeqtS25Xw2Kwq+FNXkyLbscYjfysVtKPcrNcV/pQr6U6Mje+SJIZMblq8Yr +ba0F8PrVC8+a5fBQpIs7R6UjW3p6+DM/uO+Zl+MgwdYoic+U+7lF7eNAFxHUdPAL +MeIrJmqbTFeurCA+ukV6BfO9m2kVrn1OIGPENXY6BwLJN/3HR+7o8XYdcxXyl6S1 +yHp52UKqK39c/s4mT6NmgTWvRLpUHhwwMmWd5jyTXlBOeuM61G7MGvv50jeuJCqr +VwMiKA1JdX+3KNp1v47j3A55MQIDAQABo0IwQDAdBgNVHQ4EFgQUnZPGU4teyq8/ +nx4P5ZmVvCT2lI8wDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwDQYJ +KoZIhvcNAQELBQADggEBAFis9AQOzcAN/wr91LoWXym9e2iZWEnStB03TX8nfUYG +XUPGhi4+c7ImfU+TqbbEKpqrIZcUsd6M06uJFdhrJNTxFq7YpFzUf1GO7RgBsZNj +vbz4YYCanrHOQnDiqX0GJX0nof5v7LMeJNrjS1UaADs1tDvZ110w/YETifLCBivt +Z8SOyUOyXGsViQK8YvxO8rUzqrJv0wqiUOP2O+guRMLbZjipM1ZI8W0bM40NjD9g +N53Tym1+NH4Nn3J2ixufcv1SNUFFApYvHLKac0khsUlHRUe072o0EclNmsxZt9YC +nlpOZbWUrhvfKbAW8b8Angc6F2S1BLUjIZkKlTuXfO8= +-----END CERTIFICATE----- + +# Issuer: CN=AffirmTrust Networking O=AffirmTrust +# Subject: CN=AffirmTrust Networking O=AffirmTrust +# Label: "AffirmTrust Networking" +# Serial: 8957382827206547757 +# MD5 Fingerprint: 42:65:ca:be:01:9a:9a:4c:a9:8c:41:49:cd:c0:d5:7f +# SHA1 Fingerprint: 29:36:21:02:8b:20:ed:02:f5:66:c5:32:d1:d6:ed:90:9f:45:00:2f +# SHA256 Fingerprint: 0a:81:ec:5a:92:97:77:f1:45:90:4a:f3:8d:5d:50:9f:66:b5:e2:c5:8f:cd:b5:31:05:8b:0e:17:f3:f0:b4:1b +-----BEGIN CERTIFICATE----- +MIIDTDCCAjSgAwIBAgIIfE8EORzUmS0wDQYJKoZIhvcNAQEFBQAwRDELMAkGA1UE +BhMCVVMxFDASBgNVBAoMC0FmZmlybVRydXN0MR8wHQYDVQQDDBZBZmZpcm1UcnVz +dCBOZXR3b3JraW5nMB4XDTEwMDEyOTE0MDgyNFoXDTMwMTIzMTE0MDgyNFowRDEL +MAkGA1UEBhMCVVMxFDASBgNVBAoMC0FmZmlybVRydXN0MR8wHQYDVQQDDBZBZmZp +cm1UcnVzdCBOZXR3b3JraW5nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKC +AQEAtITMMxcua5Rsa2FSoOujz3mUTOWUgJnLVWREZY9nZOIG41w3SfYvm4SEHi3y +YJ0wTsyEheIszx6e/jarM3c1RNg1lho9Nuh6DtjVR6FqaYvZ/Ls6rnla1fTWcbua +kCNrmreIdIcMHl+5ni36q1Mr3Lt2PpNMCAiMHqIjHNRqrSK6mQEubWXLviRmVSRL +QESxG9fhwoXA3hA/Pe24/PHxI1Pcv2WXb9n5QHGNfb2V1M6+oF4nI979ptAmDgAp +6zxG8D1gvz9Q0twmQVGeFDdCBKNwV6gbh+0t+nvujArjqWaJGctB+d1ENmHP4ndG +yH329JKBNv3bNPFyfvMMFr20FQIDAQABo0IwQDAdBgNVHQ4EFgQUBx/S55zawm6i +QLSwelAQUHTEyL0wDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwDQYJ +KoZIhvcNAQEFBQADggEBAIlXshZ6qML91tmbmzTCnLQyFE2npN/svqe++EPbkTfO +tDIuUFUaNU52Q3Eg75N3ThVwLofDwR1t3Mu1J9QsVtFSUzpE0nPIxBsFZVpikpzu +QY0x2+c06lkh1QF612S4ZDnNye2v7UsDSKegmQGA3GWjNq5lWUhPgkvIZfFXHeVZ +Lgo/bNjR9eUJtGxUAArgFU2HdW23WJZa3W3SAKD0m0i+wzekujbgfIeFlxoVot4u +olu9rxj5kFDNcFn4J2dHy8egBzp90SxdbBk6ZrV9/ZFvgrG+CJPbFEfxojfHRZ48 +x3evZKiT3/Zpg4Jg8klCNO1aAFSFHBY2kgxc+qatv9s= +-----END CERTIFICATE----- + +# Issuer: CN=AffirmTrust Premium O=AffirmTrust +# Subject: CN=AffirmTrust Premium O=AffirmTrust +# Label: "AffirmTrust Premium" +# Serial: 7893706540734352110 +# MD5 Fingerprint: c4:5d:0e:48:b6:ac:28:30:4e:0a:bc:f9:38:16:87:57 +# SHA1 Fingerprint: d8:a6:33:2c:e0:03:6f:b1:85:f6:63:4f:7d:6a:06:65:26:32:28:27 +# SHA256 Fingerprint: 70:a7:3f:7f:37:6b:60:07:42:48:90:45:34:b1:14:82:d5:bf:0e:69:8e:cc:49:8d:f5:25:77:eb:f2:e9:3b:9a +-----BEGIN CERTIFICATE----- +MIIFRjCCAy6gAwIBAgIIbYwURrGmCu4wDQYJKoZIhvcNAQEMBQAwQTELMAkGA1UE +BhMCVVMxFDASBgNVBAoMC0FmZmlybVRydXN0MRwwGgYDVQQDDBNBZmZpcm1UcnVz +dCBQcmVtaXVtMB4XDTEwMDEyOTE0MTAzNloXDTQwMTIzMTE0MTAzNlowQTELMAkG +A1UEBhMCVVMxFDASBgNVBAoMC0FmZmlybVRydXN0MRwwGgYDVQQDDBNBZmZpcm1U +cnVzdCBQcmVtaXVtMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAxBLf +qV/+Qd3d9Z+K4/as4Tx4mrzY8H96oDMq3I0gW64tb+eT2TZwamjPjlGjhVtnBKAQ +JG9dKILBl1fYSCkTtuG+kU3fhQxTGJoeJKJPj/CihQvL9Cl/0qRY7iZNyaqoe5rZ ++jjeRFcV5fiMyNlI4g0WJx0eyIOFJbe6qlVBzAMiSy2RjYvmia9mx+n/K+k8rNrS +s8PhaJyJ+HoAVt70VZVs+7pk3WKL3wt3MutizCaam7uqYoNMtAZ6MMgpv+0GTZe5 +HMQxK9VfvFMSF5yZVylmd2EhMQcuJUmdGPLu8ytxjLW6OQdJd/zvLpKQBY0tL3d7 +70O/Nbua2Plzpyzy0FfuKE4mX4+QaAkvuPjcBukumj5Rp9EixAqnOEhss/n/fauG +V+O61oV4d7pD6kh/9ti+I20ev9E2bFhc8e6kGVQa9QPSdubhjL08s9NIS+LI+H+S +qHZGnEJlPqQewQcDWkYtuJfzt9WyVSHvutxMAJf7FJUnM7/oQ0dG0giZFmA7mn7S +5u046uwBHjxIVkkJx0w3AJ6IDsBz4W9m6XJHMD4Q5QsDyZpCAGzFlH5hxIrff4Ia +C1nEWTJ3s7xgaVY5/bQGeyzWZDbZvUjthB9+pSKPKrhC9IK31FOQeE4tGv2Bb0TX +OwF0lkLgAOIua+rF7nKsu7/+6qqo+Nz2snmKtmcCAwEAAaNCMEAwHQYDVR0OBBYE +FJ3AZ6YMItkm9UWrpmVSESfYRaxjMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/ +BAQDAgEGMA0GCSqGSIb3DQEBDAUAA4ICAQCzV00QYk465KzquByvMiPIs0laUZx2 +KI15qldGF9X1Uva3ROgIRL8YhNILgM3FEv0AVQVhh0HctSSePMTYyPtwni94loMg +Nt58D2kTiKV1NpgIpsbfrM7jWNa3Pt668+s0QNiigfV4Py/VpfzZotReBA4Xrf5B +8OWycvpEgjNC6C1Y91aMYj+6QrCcDFx+LmUmXFNPALJ4fqENmS2NuB2OosSw/WDQ +MKSOyARiqcTtNd56l+0OOF6SL5Nwpamcb6d9Ex1+xghIsV5n61EIJenmJWtSKZGc +0jlzCFfemQa0W50QBuHCAKi4HEoCChTQwUHK+4w1IX2COPKpVJEZNZOUbWo6xbLQ +u4mGk+ibyQ86p3q4ofB4Rvr8Ny/lioTz3/4E2aFooC8k4gmVBtWVyuEklut89pMF +u+1z6S3RdTnX5yTb2E5fQ4+e0BQ5v1VwSJlXMbSc7kqYA5YwH2AG7hsj/oFgIxpH +YoWlzBk0gG+zrBrjn/B7SK3VAdlntqlyk+otZrWyuOQ9PLLvTIzq6we/qzWaVYa8 +GKa1qF60g2xraUDTn9zxw2lrueFtCfTxqlB2Cnp9ehehVZZCmTEJ3WARjQUwfuaO +RtGdFNrHF+QFlozEJLUbzxQHskD4o55BhrwE0GuWyCqANP2/7waj3VjFhT0+j/6e +KeC2uAloGRwYQw== +-----END CERTIFICATE----- + +# Issuer: CN=AffirmTrust Premium ECC O=AffirmTrust +# Subject: CN=AffirmTrust Premium ECC O=AffirmTrust +# Label: "AffirmTrust Premium ECC" +# Serial: 8401224907861490260 +# MD5 Fingerprint: 64:b0:09:55:cf:b1:d5:99:e2:be:13:ab:a6:5d:ea:4d +# SHA1 Fingerprint: b8:23:6b:00:2f:1d:16:86:53:01:55:6c:11:a4:37:ca:eb:ff:c3:bb +# SHA256 Fingerprint: bd:71:fd:f6:da:97:e4:cf:62:d1:64:7a:dd:25:81:b0:7d:79:ad:f8:39:7e:b4:ec:ba:9c:5e:84:88:82:14:23 +-----BEGIN CERTIFICATE----- +MIIB/jCCAYWgAwIBAgIIdJclisc/elQwCgYIKoZIzj0EAwMwRTELMAkGA1UEBhMC +VVMxFDASBgNVBAoMC0FmZmlybVRydXN0MSAwHgYDVQQDDBdBZmZpcm1UcnVzdCBQ +cmVtaXVtIEVDQzAeFw0xMDAxMjkxNDIwMjRaFw00MDEyMzExNDIwMjRaMEUxCzAJ +BgNVBAYTAlVTMRQwEgYDVQQKDAtBZmZpcm1UcnVzdDEgMB4GA1UEAwwXQWZmaXJt +VHJ1c3QgUHJlbWl1bSBFQ0MwdjAQBgcqhkjOPQIBBgUrgQQAIgNiAAQNMF4bFZ0D +0KF5Nbc6PJJ6yhUczWLznCZcBz3lVPqj1swS6vQUX+iOGasvLkjmrBhDeKzQN8O9 +ss0s5kfiGuZjuD0uL3jET9v0D6RoTFVya5UdThhClXjMNzyR4ptlKymjQjBAMB0G +A1UdDgQWBBSaryl6wBE1NSZRMADDav5A1a7WPDAPBgNVHRMBAf8EBTADAQH/MA4G +A1UdDwEB/wQEAwIBBjAKBggqhkjOPQQDAwNnADBkAjAXCfOHiFBar8jAQr9HX/Vs +aobgxCd05DhT1wV/GzTjxi+zygk8N53X57hG8f2h4nECMEJZh0PUUd+60wkyWs6I +flc9nF9Ca/UHLbXwgpP5WW+uZPpY5Yse42O+tYHNbwKMeQ== +-----END CERTIFICATE----- + +# Issuer: CN=Certum Trusted Network CA O=Unizeto Technologies S.A. OU=Certum Certification Authority +# Subject: CN=Certum Trusted Network CA O=Unizeto Technologies S.A. OU=Certum Certification Authority +# Label: "Certum Trusted Network CA" +# Serial: 279744 +# MD5 Fingerprint: d5:e9:81:40:c5:18:69:fc:46:2c:89:75:62:0f:aa:78 +# SHA1 Fingerprint: 07:e0:32:e0:20:b7:2c:3f:19:2f:06:28:a2:59:3a:19:a7:0f:06:9e +# SHA256 Fingerprint: 5c:58:46:8d:55:f5:8e:49:7e:74:39:82:d2:b5:00:10:b6:d1:65:37:4a:cf:83:a7:d4:a3:2d:b7:68:c4:40:8e +-----BEGIN CERTIFICATE----- +MIIDuzCCAqOgAwIBAgIDBETAMA0GCSqGSIb3DQEBBQUAMH4xCzAJBgNVBAYTAlBM +MSIwIAYDVQQKExlVbml6ZXRvIFRlY2hub2xvZ2llcyBTLkEuMScwJQYDVQQLEx5D +ZXJ0dW0gQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkxIjAgBgNVBAMTGUNlcnR1bSBU +cnVzdGVkIE5ldHdvcmsgQ0EwHhcNMDgxMDIyMTIwNzM3WhcNMjkxMjMxMTIwNzM3 +WjB+MQswCQYDVQQGEwJQTDEiMCAGA1UEChMZVW5pemV0byBUZWNobm9sb2dpZXMg +Uy5BLjEnMCUGA1UECxMeQ2VydHVtIENlcnRpZmljYXRpb24gQXV0aG9yaXR5MSIw +IAYDVQQDExlDZXJ0dW0gVHJ1c3RlZCBOZXR3b3JrIENBMIIBIjANBgkqhkiG9w0B +AQEFAAOCAQ8AMIIBCgKCAQEA4/t9o3K6wvDJFIf1awFO4W5AB7ptJ11/91sts1rH +UV+rpDKmYYe2bg+G0jACl/jXaVehGDldamR5xgFZrDwxSjh80gTSSyjoIF87B6LM +TXPb865Px1bVWqeWifrzq2jUI4ZZJ88JJ7ysbnKDHDBy3+Ci6dLhdHUZvSqeexVU +BBvXQzmtVSjF4hq79MDkrjhJM8x2hZ85RdKknvISjFH4fOQtf/WsX+sWn7Et0brM +kUJ3TCXJkDhv2/DM+44el1k+1WBO5gUo7Ul5E0u6SNsv+XLTOcr+H9g0cvW0QM8x +AcPs3hEtF10fuFDRXhmnad4HMyjKUJX5p1TLVIZQRan5SQIDAQABo0IwQDAPBgNV +HRMBAf8EBTADAQH/MB0GA1UdDgQWBBQIds3LB/8k9sXN7buQvOKEN0Z19zAOBgNV +HQ8BAf8EBAMCAQYwDQYJKoZIhvcNAQEFBQADggEBAKaorSLOAT2mo/9i0Eidi15y +sHhE49wcrwn9I0j6vSrEuVUEtRCjjSfeC4Jj0O7eDDd5QVsisrCaQVymcODU0HfL +I9MA4GxWL+FpDQ3Zqr8hgVDZBqWo/5U30Kr+4rP1mS1FhIrlQgnXdAIv94nYmem8 +J9RHjboNRhx3zxSkHLmkMcScKHQDNP8zGSal6Q10tz6XxnboJ5ajZt3hrvJBW8qY +VoNzcOSGGtIxQbovvi0TWnZvTuhOgQ4/WwMioBK+ZlgRSssDxLQqKi2WF+A5VLxI +03YnnZotBqbJ7DnSq9ufmgsnAjUpsUCV5/nonFWIGUbWtzT1fs45mtk48VH3Tyw= +-----END CERTIFICATE----- + +# Issuer: CN=TWCA Root Certification Authority O=TAIWAN-CA OU=Root CA +# Subject: CN=TWCA Root Certification Authority O=TAIWAN-CA OU=Root CA +# Label: "TWCA Root Certification Authority" +# Serial: 1 +# MD5 Fingerprint: aa:08:8f:f6:f9:7b:b7:f2:b1:a7:1e:9b:ea:ea:bd:79 +# SHA1 Fingerprint: cf:9e:87:6d:d3:eb:fc:42:26:97:a3:b5:a3:7a:a0:76:a9:06:23:48 +# SHA256 Fingerprint: bf:d8:8f:e1:10:1c:41:ae:3e:80:1b:f8:be:56:35:0e:e9:ba:d1:a6:b9:bd:51:5e:dc:5c:6d:5b:87:11:ac:44 +-----BEGIN CERTIFICATE----- +MIIDezCCAmOgAwIBAgIBATANBgkqhkiG9w0BAQUFADBfMQswCQYDVQQGEwJUVzES +MBAGA1UECgwJVEFJV0FOLUNBMRAwDgYDVQQLDAdSb290IENBMSowKAYDVQQDDCFU +V0NBIFJvb3QgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwHhcNMDgwODI4MDcyNDMz +WhcNMzAxMjMxMTU1OTU5WjBfMQswCQYDVQQGEwJUVzESMBAGA1UECgwJVEFJV0FO +LUNBMRAwDgYDVQQLDAdSb290IENBMSowKAYDVQQDDCFUV0NBIFJvb3QgQ2VydGlm +aWNhdGlvbiBBdXRob3JpdHkwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIB +AQCwfnK4pAOU5qfeCTiRShFAh6d8WWQUe7UREN3+v9XAu1bihSX0NXIP+FPQQeFE +AcK0HMMxQhZHhTMidrIKbw/lJVBPhYa+v5guEGcevhEFhgWQxFnQfHgQsIBct+HH +K3XLfJ+utdGdIzdjp9xCoi2SBBtQwXu4PhvJVgSLL1KbralW6cH/ralYhzC2gfeX +RfwZVzsrb+RH9JlF/h3x+JejiB03HFyP4HYlmlD4oFT/RJB2I9IyxsOrBr/8+7/z +rX2SYgJbKdM1o5OaQ2RgXbL6Mv87BK9NQGr5x+PvI/1ry+UPizgN7gr8/g+YnzAx +3WxSZfmLgb4i4RxYA7qRG4kHAgMBAAGjQjBAMA4GA1UdDwEB/wQEAwIBBjAPBgNV +HRMBAf8EBTADAQH/MB0GA1UdDgQWBBRqOFsmjd6LWvJPelSDGRjjCDWmujANBgkq +hkiG9w0BAQUFAAOCAQEAPNV3PdrfibqHDAhUaiBQkr6wQT25JmSDCi/oQMCXKCeC +MErJk/9q56YAf4lCmtYR5VPOL8zy2gXE/uJQxDqGfczafhAJO5I1KlOy/usrBdls +XebQ79NqZp4VKIV66IIArB6nCWlWQtNoURi+VJq/REG6Sb4gumlc7rh3zc5sH62D +lhh9DrUUOYTxKOkto557HnpyWoOzeW/vtPzQCqVYT0bf+215WfKEIlKuD8z7fDvn +aspHYcN6+NOSBB+4IIThNlQWx0DeO4pz3N/GCUzf7Nr/1FNCocnyYh0igzyXxfkZ +YiesZSLX0zzG5Y6yU8xJzrww/nsOM5D77dIUkR8Hrw== +-----END CERTIFICATE----- + +# Issuer: O=SECOM Trust Systems CO.,LTD. OU=Security Communication RootCA2 +# Subject: O=SECOM Trust Systems CO.,LTD. OU=Security Communication RootCA2 +# Label: "Security Communication RootCA2" +# Serial: 0 +# MD5 Fingerprint: 6c:39:7d:a4:0e:55:59:b2:3f:d6:41:b1:12:50:de:43 +# SHA1 Fingerprint: 5f:3b:8c:f2:f8:10:b3:7d:78:b4:ce:ec:19:19:c3:73:34:b9:c7:74 +# SHA256 Fingerprint: 51:3b:2c:ec:b8:10:d4:cd:e5:dd:85:39:1a:df:c6:c2:dd:60:d8:7b:b7:36:d2:b5:21:48:4a:a4:7a:0e:be:f6 +-----BEGIN CERTIFICATE----- +MIIDdzCCAl+gAwIBAgIBADANBgkqhkiG9w0BAQsFADBdMQswCQYDVQQGEwJKUDEl +MCMGA1UEChMcU0VDT00gVHJ1c3QgU3lzdGVtcyBDTy4sTFRELjEnMCUGA1UECxMe +U2VjdXJpdHkgQ29tbXVuaWNhdGlvbiBSb290Q0EyMB4XDTA5MDUyOTA1MDAzOVoX +DTI5MDUyOTA1MDAzOVowXTELMAkGA1UEBhMCSlAxJTAjBgNVBAoTHFNFQ09NIFRy +dXN0IFN5c3RlbXMgQ08uLExURC4xJzAlBgNVBAsTHlNlY3VyaXR5IENvbW11bmlj +YXRpb24gUm9vdENBMjCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBANAV +OVKxUrO6xVmCxF1SrjpDZYBLx/KWvNs2l9amZIyoXvDjChz335c9S672XewhtUGr +zbl+dp+++T42NKA7wfYxEUV0kz1XgMX5iZnK5atq1LXaQZAQwdbWQonCv/Q4EpVM +VAX3NuRFg3sUZdbcDE3R3n4MqzvEFb46VqZab3ZpUql6ucjrappdUtAtCms1FgkQ +hNBqyjoGADdH5H5XTz+L62e4iKrFvlNVspHEfbmwhRkGeC7bYRr6hfVKkaHnFtWO +ojnflLhwHyg/i/xAXmODPIMqGplrz95Zajv8bxbXH/1KEOtOghY6rCcMU/Gt1SSw +awNQwS08Ft1ENCcadfsCAwEAAaNCMEAwHQYDVR0OBBYEFAqFqXdlBZh8QIH4D5cs +OPEK7DzPMA4GA1UdDwEB/wQEAwIBBjAPBgNVHRMBAf8EBTADAQH/MA0GCSqGSIb3 +DQEBCwUAA4IBAQBMOqNErLlFsceTfsgLCkLfZOoc7llsCLqJX2rKSpWeeo8HxdpF +coJxDjrSzG+ntKEju/Ykn8sX/oymzsLS28yN/HH8AynBbF0zX2S2ZTuJbxh2ePXc +okgfGT+Ok+vx+hfuzU7jBBJV1uXk3fs+BXziHV7Gp7yXT2g69ekuCkO2r1dcYmh8 +t/2jioSgrGK+KwmHNPBqAbubKVY8/gA3zyNs8U6qtnRGEmyR7jTV7JqR50S+kDFy +1UkC9gLl9B/rfNmWVan/7Ir5mUf/NVoCqgTLiluHcSmRvaS0eg29mvVXIwAHIRc/ +SjnRBUkLp7Y3gaVdjKozXoEofKd9J+sAro03 +-----END CERTIFICATE----- + +# Issuer: CN=Hellenic Academic and Research Institutions RootCA 2011 O=Hellenic Academic and Research Institutions Cert. Authority +# Subject: CN=Hellenic Academic and Research Institutions RootCA 2011 O=Hellenic Academic and Research Institutions Cert. Authority +# Label: "Hellenic Academic and Research Institutions RootCA 2011" +# Serial: 0 +# MD5 Fingerprint: 73:9f:4c:4b:73:5b:79:e9:fa:ba:1c:ef:6e:cb:d5:c9 +# SHA1 Fingerprint: fe:45:65:9b:79:03:5b:98:a1:61:b5:51:2e:ac:da:58:09:48:22:4d +# SHA256 Fingerprint: bc:10:4f:15:a4:8b:e7:09:dc:a5:42:a7:e1:d4:b9:df:6f:05:45:27:e8:02:ea:a9:2d:59:54:44:25:8a:fe:71 +-----BEGIN CERTIFICATE----- +MIIEMTCCAxmgAwIBAgIBADANBgkqhkiG9w0BAQUFADCBlTELMAkGA1UEBhMCR1Ix +RDBCBgNVBAoTO0hlbGxlbmljIEFjYWRlbWljIGFuZCBSZXNlYXJjaCBJbnN0aXR1 +dGlvbnMgQ2VydC4gQXV0aG9yaXR5MUAwPgYDVQQDEzdIZWxsZW5pYyBBY2FkZW1p +YyBhbmQgUmVzZWFyY2ggSW5zdGl0dXRpb25zIFJvb3RDQSAyMDExMB4XDTExMTIw +NjEzNDk1MloXDTMxMTIwMTEzNDk1MlowgZUxCzAJBgNVBAYTAkdSMUQwQgYDVQQK +EztIZWxsZW5pYyBBY2FkZW1pYyBhbmQgUmVzZWFyY2ggSW5zdGl0dXRpb25zIENl +cnQuIEF1dGhvcml0eTFAMD4GA1UEAxM3SGVsbGVuaWMgQWNhZGVtaWMgYW5kIFJl +c2VhcmNoIEluc3RpdHV0aW9ucyBSb290Q0EgMjAxMTCCASIwDQYJKoZIhvcNAQEB +BQADggEPADCCAQoCggEBAKlTAOMupvaO+mDYLZU++CwqVE7NuYRhlFhPjz2L5EPz +dYmNUeTDN9KKiE15HrcS3UN4SoqS5tdI1Q+kOilENbgH9mgdVc04UfCMJDGFr4PJ +fel3r+0ae50X+bOdOFAPplp5kYCvN66m0zH7tSYJnTxa71HFK9+WXesyHgLacEns +bgzImjeN9/E2YEsmLIKe0HjzDQ9jpFEw4fkrJxIH2Oq9GGKYsFk3fb7u8yBRQlqD +75O6aRXxYp2fmTmCobd0LovUxQt7L/DICto9eQqakxylKHJzkUOap9FNhYS5qXSP +FEDH3N6sQWRstBmbAmNtJGSPRLIl6s5ddAxjMlyNh+UCAwEAAaOBiTCBhjAPBgNV +HRMBAf8EBTADAQH/MAsGA1UdDwQEAwIBBjAdBgNVHQ4EFgQUppFC/RNhSiOeCKQp +5dgTBCPuQSUwRwYDVR0eBEAwPqA8MAWCAy5ncjAFggMuZXUwBoIELmVkdTAGggQu +b3JnMAWBAy5ncjAFgQMuZXUwBoEELmVkdTAGgQQub3JnMA0GCSqGSIb3DQEBBQUA +A4IBAQAf73lB4XtuP7KMhjdCSk4cNx6NZrokgclPEg8hwAOXhiVtXdMiKahsog2p +6z0GW5k6x8zDmjR/qw7IThzh+uTczQ2+vyT+bOdrwg3IBp5OjWEopmr95fZi6hg8 +TqBTnbI6nOulnJEWtk2C4AwFSKls9cz4y51JtPACpf1wA+2KIaWuE4ZJwzNzvoc7 +dIsXRSZMFpGD/md9zU1jZ/rzAxKWeAaNsWftjj++n08C9bMJL/NMh98qy5V8Acys +Nnq/onN694/BtZqhFLKPM58N7yLcZnuEvUUXBj08yrl3NI/K6s8/MT7jiOOASSXI +l7WdmplNsDz4SgCbZN2fOUvRJ9e4 +-----END CERTIFICATE----- + +# Issuer: CN=Actalis Authentication Root CA O=Actalis S.p.A./03358520967 +# Subject: CN=Actalis Authentication Root CA O=Actalis S.p.A./03358520967 +# Label: "Actalis Authentication Root CA" +# Serial: 6271844772424770508 +# MD5 Fingerprint: 69:c1:0d:4f:07:a3:1b:c3:fe:56:3d:04:bc:11:f6:a6 +# SHA1 Fingerprint: f3:73:b3:87:06:5a:28:84:8a:f2:f3:4a:ce:19:2b:dd:c7:8e:9c:ac +# SHA256 Fingerprint: 55:92:60:84:ec:96:3a:64:b9:6e:2a:be:01:ce:0b:a8:6a:64:fb:fe:bc:c7:aa:b5:af:c1:55:b3:7f:d7:60:66 +-----BEGIN CERTIFICATE----- +MIIFuzCCA6OgAwIBAgIIVwoRl0LE48wwDQYJKoZIhvcNAQELBQAwazELMAkGA1UE +BhMCSVQxDjAMBgNVBAcMBU1pbGFuMSMwIQYDVQQKDBpBY3RhbGlzIFMucC5BLi8w +MzM1ODUyMDk2NzEnMCUGA1UEAwweQWN0YWxpcyBBdXRoZW50aWNhdGlvbiBSb290 +IENBMB4XDTExMDkyMjExMjIwMloXDTMwMDkyMjExMjIwMlowazELMAkGA1UEBhMC +SVQxDjAMBgNVBAcMBU1pbGFuMSMwIQYDVQQKDBpBY3RhbGlzIFMucC5BLi8wMzM1 +ODUyMDk2NzEnMCUGA1UEAwweQWN0YWxpcyBBdXRoZW50aWNhdGlvbiBSb290IENB +MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAp8bEpSmkLO/lGMWwUKNv +UTufClrJwkg4CsIcoBh/kbWHuUA/3R1oHwiD1S0eiKD4j1aPbZkCkpAW1V8IbInX +4ay8IMKx4INRimlNAJZaby/ARH6jDuSRzVju3PvHHkVH3Se5CAGfpiEd9UEtL0z9 +KK3giq0itFZljoZUj5NDKd45RnijMCO6zfB9E1fAXdKDa0hMxKufgFpbOr3JpyI/ +gCczWw63igxdBzcIy2zSekciRDXFzMwujt0q7bd9Zg1fYVEiVRvjRuPjPdA1Yprb +rxTIW6HMiRvhMCb8oJsfgadHHwTrozmSBp+Z07/T6k9QnBn+locePGX2oxgkg4YQ +51Q+qDp2JE+BIcXjDwL4k5RHILv+1A7TaLndxHqEguNTVHnd25zS8gebLra8Pu2F +be8lEfKXGkJh90qX6IuxEAf6ZYGyojnP9zz/GPvG8VqLWeICrHuS0E4UT1lF9gxe +KF+w6D9Fz8+vm2/7hNN3WpVvrJSEnu68wEqPSpP4RCHiMUVhUE4Q2OM1fEwZtN4F +v6MGn8i1zeQf1xcGDXqVdFUNaBr8EBtiZJ1t4JWgw5QHVw0U5r0F+7if5t+L4sbn +fpb2U8WANFAoWPASUHEXMLrmeGO89LKtmyuy/uE5jF66CyCU3nuDuP/jVo23Eek7 +jPKxwV2dpAtMK9myGPW1n0sCAwEAAaNjMGEwHQYDVR0OBBYEFFLYiDrIn3hm7Ynz +ezhwlMkCAjbQMA8GA1UdEwEB/wQFMAMBAf8wHwYDVR0jBBgwFoAUUtiIOsifeGbt +ifN7OHCUyQICNtAwDgYDVR0PAQH/BAQDAgEGMA0GCSqGSIb3DQEBCwUAA4ICAQAL +e3KHwGCmSUyIWOYdiPcUZEim2FgKDk8TNd81HdTtBjHIgT5q1d07GjLukD0R0i70 +jsNjLiNmsGe+b7bAEzlgqqI0JZN1Ut6nna0Oh4lScWoWPBkdg/iaKWW+9D+a2fDz +WochcYBNy+A4mz+7+uAwTc+G02UQGRjRlwKxK3JCaKygvU5a2hi/a5iB0P2avl4V +SM0RFbnAKVy06Ij3Pjaut2L9HmLecHgQHEhb2rykOLpn7VU+Xlff1ANATIGk0k9j +pwlCCRT8AKnCgHNPLsBA2RF7SOp6AsDT6ygBJlh0wcBzIm2Tlf05fbsq4/aC4yyX +X04fkZT6/iyj2HYauE2yOE+b+h1IYHkm4vP9qdCa6HCPSXrW5b0KDtst842/6+Ok +fcvHlXHo2qN8xcL4dJIEG4aspCJTQLas/kx2z/uUMsA1n3Y/buWQbqCmJqK4LL7R +K4X9p2jIugErsWx0Hbhzlefut8cl8ABMALJ+tguLHPPAUJ4lueAI3jZm/zel0btU +ZCzJJ7VLkn5l/9Mt4blOvH+kQSGQQXemOR/qnuOf0GZvBeyqdn6/axag67XH/JJU +LysRJyU3eExRarDzzFhdFPFqSBX/wge2sY0PjlxQRrM9vwGYT7JZVEc+NHt4bVaT +LnPqZih4zR0Uv6CPLy64Lo7yFIrM6bV8+2ydDKXhlg== +-----END CERTIFICATE----- + +# Issuer: O=Trustis Limited OU=Trustis FPS Root CA +# Subject: O=Trustis Limited OU=Trustis FPS Root CA +# Label: "Trustis FPS Root CA" +# Serial: 36053640375399034304724988975563710553 +# MD5 Fingerprint: 30:c9:e7:1e:6b:e6:14:eb:65:b2:16:69:20:31:67:4d +# SHA1 Fingerprint: 3b:c0:38:0b:33:c3:f6:a6:0c:86:15:22:93:d9:df:f5:4b:81:c0:04 +# SHA256 Fingerprint: c1:b4:82:99:ab:a5:20:8f:e9:63:0a:ce:55:ca:68:a0:3e:da:5a:51:9c:88:02:a0:d3:a6:73:be:8f:8e:55:7d +-----BEGIN CERTIFICATE----- +MIIDZzCCAk+gAwIBAgIQGx+ttiD5JNM2a/fH8YygWTANBgkqhkiG9w0BAQUFADBF +MQswCQYDVQQGEwJHQjEYMBYGA1UEChMPVHJ1c3RpcyBMaW1pdGVkMRwwGgYDVQQL +ExNUcnVzdGlzIEZQUyBSb290IENBMB4XDTAzMTIyMzEyMTQwNloXDTI0MDEyMTEx +MzY1NFowRTELMAkGA1UEBhMCR0IxGDAWBgNVBAoTD1RydXN0aXMgTGltaXRlZDEc +MBoGA1UECxMTVHJ1c3RpcyBGUFMgUm9vdCBDQTCCASIwDQYJKoZIhvcNAQEBBQAD +ggEPADCCAQoCggEBAMVQe547NdDfxIzNjpvto8A2mfRC6qc+gIMPpqdZh8mQRUN+ +AOqGeSoDvT03mYlmt+WKVoaTnGhLaASMk5MCPjDSNzoiYYkchU59j9WvezX2fihH +iTHcDnlkH5nSW7r+f2C/revnPDgpai/lkQtV/+xvWNUtyd5MZnGPDNcE2gfmHhjj +vSkCqPoc4Vu5g6hBSLwacY3nYuUtsuvffM/bq1rKMfFMIvMFE/eC+XN5DL7XSxzA +0RU8k0Fk0ea+IxciAIleH2ulrG6nS4zto3Lmr2NNL4XSFDWaLk6M6jKYKIahkQlB +OrTh4/L68MkKokHdqeMDx4gVOxzUGpTXn2RZEm0CAwEAAaNTMFEwDwYDVR0TAQH/ +BAUwAwEB/zAfBgNVHSMEGDAWgBS6+nEleYtXQSUhhgtx67JkDoshZzAdBgNVHQ4E +FgQUuvpxJXmLV0ElIYYLceuyZA6LIWcwDQYJKoZIhvcNAQEFBQADggEBAH5Y//01 +GX2cGE+esCu8jowU/yyg2kdbw++BLa8F6nRIW/M+TgfHbcWzk88iNVy2P3UnXwmW +zaD+vkAMXBJV+JOCyinpXj9WV4s4NvdFGkwozZ5BuO1WTISkQMi4sKUraXAEasP4 +1BIy+Q7DsdwyhEQsb8tGD+pmQQ9P8Vilpg0ND2HepZ5dfWWhPBfnqFVO76DH7cZE +f1T1o+CP8HxVIo8ptoGj4W1OLBuAZ+ytIJ8MYmHVl/9D7S3B2l0pKoU/rGXuhg8F +jZBf3+6f9L/uHfuY5H+QK4R4EA5sSVPvFVtlRkpdr7r7OnIdzfYliB6XzCGcKQEN +ZetX2fNXlrtIzYE= +-----END CERTIFICATE----- + +# Issuer: CN=Buypass Class 2 Root CA O=Buypass AS-983163327 +# Subject: CN=Buypass Class 2 Root CA O=Buypass AS-983163327 +# Label: "Buypass Class 2 Root CA" +# Serial: 2 +# MD5 Fingerprint: 46:a7:d2:fe:45:fb:64:5a:a8:59:90:9b:78:44:9b:29 +# SHA1 Fingerprint: 49:0a:75:74:de:87:0a:47:fe:58:ee:f6:c7:6b:eb:c6:0b:12:40:99 +# SHA256 Fingerprint: 9a:11:40:25:19:7c:5b:b9:5d:94:e6:3d:55:cd:43:79:08:47:b6:46:b2:3c:df:11:ad:a4:a0:0e:ff:15:fb:48 +-----BEGIN CERTIFICATE----- +MIIFWTCCA0GgAwIBAgIBAjANBgkqhkiG9w0BAQsFADBOMQswCQYDVQQGEwJOTzEd +MBsGA1UECgwUQnV5cGFzcyBBUy05ODMxNjMzMjcxIDAeBgNVBAMMF0J1eXBhc3Mg +Q2xhc3MgMiBSb290IENBMB4XDTEwMTAyNjA4MzgwM1oXDTQwMTAyNjA4MzgwM1ow +TjELMAkGA1UEBhMCTk8xHTAbBgNVBAoMFEJ1eXBhc3MgQVMtOTgzMTYzMzI3MSAw +HgYDVQQDDBdCdXlwYXNzIENsYXNzIDIgUm9vdCBDQTCCAiIwDQYJKoZIhvcNAQEB +BQADggIPADCCAgoCggIBANfHXvfBB9R3+0Mh9PT1aeTuMgHbo4Yf5FkNuud1g1Lr +6hxhFUi7HQfKjK6w3Jad6sNgkoaCKHOcVgb/S2TwDCo3SbXlzwx87vFKu3MwZfPV +L4O2fuPn9Z6rYPnT8Z2SdIrkHJasW4DptfQxh6NR/Md+oW+OU3fUl8FVM5I+GC91 +1K2GScuVr1QGbNgGE41b/+EmGVnAJLqBcXmQRFBoJJRfuLMR8SlBYaNByyM21cHx +MlAQTn/0hpPshNOOvEu/XAFOBz3cFIqUCqTqc/sLUegTBxj6DvEr0VQVfTzh97QZ +QmdiXnfgolXsttlpF9U6r0TtSsWe5HonfOV116rLJeffawrbD02TTqigzXsu8lkB +arcNuAeBfos4GzjmCleZPe4h6KP1DBbdi+w0jpwqHAAVF41og9JwnxgIzRFo1clr +Us3ERo/ctfPYV3Me6ZQ5BL/T3jjetFPsaRyifsSP5BtwrfKi+fv3FmRmaZ9JUaLi +FRhnBkp/1Wy1TbMz4GHrXb7pmA8y1x1LPC5aAVKRCfLf6o3YBkBjqhHk/sM3nhRS +P/TizPJhk9H9Z2vXUq6/aKtAQ6BXNVN48FP4YUIHZMbXb5tMOA1jrGKvNouicwoN +9SG9dKpN6nIDSdvHXx1iY8f93ZHsM+71bbRuMGjeyNYmsHVee7QHIJihdjK4TWxP +AgMBAAGjQjBAMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFMmAd+BikoL1Rpzz +uvdMw964o605MA4GA1UdDwEB/wQEAwIBBjANBgkqhkiG9w0BAQsFAAOCAgEAU18h +9bqwOlI5LJKwbADJ784g7wbylp7ppHR/ehb8t/W2+xUbP6umwHJdELFx7rxP462s +A20ucS6vxOOto70MEae0/0qyexAQH6dXQbLArvQsWdZHEIjzIVEpMMpghq9Gqx3t +OluwlN5E40EIosHsHdb9T7bWR9AUC8rmyrV7d35BH16Dx7aMOZawP5aBQW9gkOLo ++fsicdl9sz1Gv7SEr5AcD48Saq/v7h56rgJKihcrdv6sVIkkLE8/trKnToyokZf7 +KcZ7XC25y2a2t6hbElGFtQl+Ynhw/qlqYLYdDnkM/crqJIByw5c/8nerQyIKx+u2 +DISCLIBrQYoIwOula9+ZEsuK1V6ADJHgJgg2SMX6OBE1/yWDLfJ6v9r9jv6ly0Us +H8SIU653DtmadsWOLB2jutXsMq7Aqqz30XpN69QH4kj3Io6wpJ9qzo6ysmD0oyLQ +I+uUWnpp3Q+/QFesa1lQ2aOZ4W7+jQF5JyMV3pKdewlNWudLSDBaGOYKbeaP4NK7 +5t98biGCwWg5TbSYWGZizEqQXsP6JwSxeRV0mcy+rSDeJmAc61ZRpqPq5KM/p/9h +3PFaTWwyI0PurKju7koSCTxdccK+efrCh2gdC/1cacwG0Jp9VJkqyTkaGa9LKkPz +Y11aWOIv4x3kqdbQCtCev9eBCfHJxyYNrJgWVqA= +-----END CERTIFICATE----- + +# Issuer: CN=Buypass Class 3 Root CA O=Buypass AS-983163327 +# Subject: CN=Buypass Class 3 Root CA O=Buypass AS-983163327 +# Label: "Buypass Class 3 Root CA" +# Serial: 2 +# MD5 Fingerprint: 3d:3b:18:9e:2c:64:5a:e8:d5:88:ce:0e:f9:37:c2:ec +# SHA1 Fingerprint: da:fa:f7:fa:66:84:ec:06:8f:14:50:bd:c7:c2:81:a5:bc:a9:64:57 +# SHA256 Fingerprint: ed:f7:eb:bc:a2:7a:2a:38:4d:38:7b:7d:40:10:c6:66:e2:ed:b4:84:3e:4c:29:b4:ae:1d:5b:93:32:e6:b2:4d +-----BEGIN CERTIFICATE----- +MIIFWTCCA0GgAwIBAgIBAjANBgkqhkiG9w0BAQsFADBOMQswCQYDVQQGEwJOTzEd +MBsGA1UECgwUQnV5cGFzcyBBUy05ODMxNjMzMjcxIDAeBgNVBAMMF0J1eXBhc3Mg +Q2xhc3MgMyBSb290IENBMB4XDTEwMTAyNjA4Mjg1OFoXDTQwMTAyNjA4Mjg1OFow +TjELMAkGA1UEBhMCTk8xHTAbBgNVBAoMFEJ1eXBhc3MgQVMtOTgzMTYzMzI3MSAw +HgYDVQQDDBdCdXlwYXNzIENsYXNzIDMgUm9vdCBDQTCCAiIwDQYJKoZIhvcNAQEB +BQADggIPADCCAgoCggIBAKXaCpUWUOOV8l6ddjEGMnqb8RB2uACatVI2zSRHsJ8Y +ZLya9vrVediQYkwiL944PdbgqOkcLNt4EemOaFEVcsfzM4fkoF0LXOBXByow9c3E +N3coTRiR5r/VUv1xLXA+58bEiuPwKAv0dpihi4dVsjoT/Lc+JzeOIuOoTyrvYLs9 +tznDDgFHmV0ST9tD+leh7fmdvhFHJlsTmKtdFoqwNxxXnUX/iJY2v7vKB3tvh2PX +0DJq1l1sDPGzbjniazEuOQAnFN44wOwZZoYS6J1yFhNkUsepNxz9gjDthBgd9K5c +/3ATAOux9TN6S9ZV+AWNS2mw9bMoNlwUxFFzTWsL8TQH2xc519woe2v1n/MuwU8X +KhDzzMro6/1rqy6any2CbgTUUgGTLT2G/H783+9CHaZr77kgxve9oKeV/afmiSTY +zIw0bOIjL9kSGiG5VZFvC5F5GQytQIgLcOJ60g7YaEi7ghM5EFjp2CoHxhLbWNvS +O1UQRwUVZ2J+GGOmRj8JDlQyXr8NYnon74Do29lLBlo3WiXQCBJ31G8JUJc9yB3D +34xFMFbG02SrZvPAXpacw8Tvw3xrizp5f7NJzz3iiZ+gMEuFuZyUJHmPfWupRWgP +K9Dx2hzLabjKSWJtyNBjYt1gD1iqj6G8BaVmos8bdrKEZLFMOVLAMLrwjEsCsLa3 +AgMBAAGjQjBAMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFEe4zf/lb+74suwv +Tg75JbCOPGvDMA4GA1UdDwEB/wQEAwIBBjANBgkqhkiG9w0BAQsFAAOCAgEAACAj +QTUEkMJAYmDv4jVM1z+s4jSQuKFvdvoWFqRINyzpkMLyPPgKn9iB5btb2iUspKdV +cSQy9sgL8rxq+JOssgfCX5/bzMiKqr5qb+FJEMwx14C7u8jYog5kV+qi9cKpMRXS +IGrs/CIBKM+GuIAeqcwRpTzyFrNHnfzSgCHEy9BHcEGhyoMZCCxt8l13nIoUE9Q2 +HJLw5QY33KbmkJs4j1xrG0aGQ0JfPgEHU1RdZX33inOhmlRaHylDFCfChQ+1iHsa +O5S3HWCntZznKWlXWpuTekMwGwPXYshApqr8ZORK15FTAaggiG6cX0S5y2CBNOxv +033aSF/rtJC8LakcC6wc1aJoIIAE1vyxjy+7SjENSoYc6+I2KSb12tjE8nVhz36u +dmNKekBlk4f4HoCMhuWG1o8O/FMsYOgWYRqiPkN7zTlgVGr18okmAWiDSKIz6MkE +kbIRNBE+6tBDGR8Dk5AM/1E9V/RBbuHLoL7ryWPNbczk+DaqaJ3tvV2XcEQNtg41 +3OEMXbugUZTLfhbrES+jkkXITHHZvMmZUldGL1DPvTVp9D0VzgalLA8+9oG6lLvD +u79leNKGef9JOxqDDPDeeOzI8k1MGt6CKfjBWtrt7uYnXuhF0J0cUahoq0Tj0Itq +4/g7u9xN12TyUb7mqqta6THuBrxzvxNiCp/HuZc= +-----END CERTIFICATE----- + +# Issuer: CN=T-TeleSec GlobalRoot Class 3 O=T-Systems Enterprise Services GmbH OU=T-Systems Trust Center +# Subject: CN=T-TeleSec GlobalRoot Class 3 O=T-Systems Enterprise Services GmbH OU=T-Systems Trust Center +# Label: "T-TeleSec GlobalRoot Class 3" +# Serial: 1 +# MD5 Fingerprint: ca:fb:40:a8:4e:39:92:8a:1d:fe:8e:2f:c4:27:ea:ef +# SHA1 Fingerprint: 55:a6:72:3e:cb:f2:ec:cd:c3:23:74:70:19:9d:2a:be:11:e3:81:d1 +# SHA256 Fingerprint: fd:73:da:d3:1c:64:4f:f1:b4:3b:ef:0c:cd:da:96:71:0b:9c:d9:87:5e:ca:7e:31:70:7a:f3:e9:6d:52:2b:bd +-----BEGIN CERTIFICATE----- +MIIDwzCCAqugAwIBAgIBATANBgkqhkiG9w0BAQsFADCBgjELMAkGA1UEBhMCREUx +KzApBgNVBAoMIlQtU3lzdGVtcyBFbnRlcnByaXNlIFNlcnZpY2VzIEdtYkgxHzAd +BgNVBAsMFlQtU3lzdGVtcyBUcnVzdCBDZW50ZXIxJTAjBgNVBAMMHFQtVGVsZVNl +YyBHbG9iYWxSb290IENsYXNzIDMwHhcNMDgxMDAxMTAyOTU2WhcNMzMxMDAxMjM1 +OTU5WjCBgjELMAkGA1UEBhMCREUxKzApBgNVBAoMIlQtU3lzdGVtcyBFbnRlcnBy +aXNlIFNlcnZpY2VzIEdtYkgxHzAdBgNVBAsMFlQtU3lzdGVtcyBUcnVzdCBDZW50 +ZXIxJTAjBgNVBAMMHFQtVGVsZVNlYyBHbG9iYWxSb290IENsYXNzIDMwggEiMA0G +CSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC9dZPwYiJvJK7genasfb3ZJNW4t/zN +8ELg63iIVl6bmlQdTQyK9tPPcPRStdiTBONGhnFBSivwKixVA9ZIw+A5OO3yXDw/ +RLyTPWGrTs0NvvAgJ1gORH8EGoel15YUNpDQSXuhdfsaa3Ox+M6pCSzyU9XDFES4 +hqX2iys52qMzVNn6chr3IhUciJFrf2blw2qAsCTz34ZFiP0Zf3WHHx+xGwpzJFu5 +ZeAsVMhg02YXP+HMVDNzkQI6pn97djmiH5a2OK61yJN0HZ65tOVgnS9W0eDrXltM +EnAMbEQgqxHY9Bn20pxSN+f6tsIxO0rUFJmtxxr1XV/6B7h8DR/Wgx6zAgMBAAGj +QjBAMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgEGMB0GA1UdDgQWBBS1 +A/d2O2GCahKqGFPrAyGUv/7OyjANBgkqhkiG9w0BAQsFAAOCAQEAVj3vlNW92nOy +WL6ukK2YJ5f+AbGwUgC4TeQbIXQbfsDuXmkqJa9c1h3a0nnJ85cp4IaH3gRZD/FZ +1GSFS5mvJQQeyUapl96Cshtwn5z2r3Ex3XsFpSzTucpH9sry9uetuUg/vBa3wW30 +6gmv7PO15wWeph6KU1HWk4HMdJP2udqmJQV0eVp+QD6CSyYRMG7hP0HHRwA11fXT +91Q+gT3aSWqas+8QPebrb9HIIkfLzM8BMZLZGOMivgkeGj5asuRrDFR6fUNOuIml +e9eiPZaGzPImNC1qkp2aGtAw4l1OBLBfiyB+d8E9lYLRRpo7PHi4b6HQDWSieB4p +TpPDpFQUWw== +-----END CERTIFICATE----- + +# Issuer: CN=EE Certification Centre Root CA O=AS Sertifitseerimiskeskus +# Subject: CN=EE Certification Centre Root CA O=AS Sertifitseerimiskeskus +# Label: "EE Certification Centre Root CA" +# Serial: 112324828676200291871926431888494945866 +# MD5 Fingerprint: 43:5e:88:d4:7d:1a:4a:7e:fd:84:2e:52:eb:01:d4:6f +# SHA1 Fingerprint: c9:a8:b9:e7:55:80:5e:58:e3:53:77:a7:25:eb:af:c3:7b:27:cc:d7 +# SHA256 Fingerprint: 3e:84:ba:43:42:90:85:16:e7:75:73:c0:99:2f:09:79:ca:08:4e:46:85:68:1f:f1:95:cc:ba:8a:22:9b:8a:76 +-----BEGIN CERTIFICATE----- +MIIEAzCCAuugAwIBAgIQVID5oHPtPwBMyonY43HmSjANBgkqhkiG9w0BAQUFADB1 +MQswCQYDVQQGEwJFRTEiMCAGA1UECgwZQVMgU2VydGlmaXRzZWVyaW1pc2tlc2t1 +czEoMCYGA1UEAwwfRUUgQ2VydGlmaWNhdGlvbiBDZW50cmUgUm9vdCBDQTEYMBYG +CSqGSIb3DQEJARYJcGtpQHNrLmVlMCIYDzIwMTAxMDMwMTAxMDMwWhgPMjAzMDEy +MTcyMzU5NTlaMHUxCzAJBgNVBAYTAkVFMSIwIAYDVQQKDBlBUyBTZXJ0aWZpdHNl +ZXJpbWlza2Vza3VzMSgwJgYDVQQDDB9FRSBDZXJ0aWZpY2F0aW9uIENlbnRyZSBS +b290IENBMRgwFgYJKoZIhvcNAQkBFglwa2lAc2suZWUwggEiMA0GCSqGSIb3DQEB +AQUAA4IBDwAwggEKAoIBAQDIIMDs4MVLqwd4lfNE7vsLDP90jmG7sWLqI9iroWUy +euuOF0+W2Ap7kaJjbMeMTC55v6kF/GlclY1i+blw7cNRfdCT5mzrMEvhvH2/UpvO +bntl8jixwKIy72KyaOBhU8E2lf/slLo2rpwcpzIP5Xy0xm90/XsY6KxX7QYgSzIw +WFv9zajmofxwvI6Sc9uXp3whrj3B9UiHbCe9nyV0gVWw93X2PaRka9ZP585ArQ/d +MtO8ihJTmMmJ+xAdTX7Nfh9WDSFwhfYggx/2uh8Ej+p3iDXE/+pOoYtNP2MbRMNE +1CV2yreN1x5KZmTNXMWcg+HCCIia7E6j8T4cLNlsHaFLAgMBAAGjgYowgYcwDwYD +VR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwHQYDVR0OBBYEFBLyWj7qVhy/ +zQas8fElyalL1BSZMEUGA1UdJQQ+MDwGCCsGAQUFBwMCBggrBgEFBQcDAQYIKwYB +BQUHAwMGCCsGAQUFBwMEBggrBgEFBQcDCAYIKwYBBQUHAwkwDQYJKoZIhvcNAQEF +BQADggEBAHv25MANqhlHt01Xo/6tu7Fq1Q+e2+RjxY6hUFaTlrg4wCQiZrxTFGGV +v9DHKpY5P30osxBAIWrEr7BSdxjhlthWXePdNl4dp1BUoMUq5KqMlIpPnTX/dqQG +E5Gion0ARD9V04I8GtVbvFZMIi5GQ4okQC3zErg7cBqklrkar4dBGmoYDQZPxz5u +uSlNDUmJEYcyW+ZLBMjkXOZ0c5RdFpgTlf7727FE5TpwrDdr5rMzcijJs1eg9gIW +iAYLtqZLICjU3j2LrTcFU3T+bsy8QxdxXvnFzBqpYe73dgzzcvRyrc9yAjYHR8/v +GVCJYMzpJJUPwssd8m92kMfMdcGWxZ0= +-----END CERTIFICATE----- + +# Issuer: CN=D-TRUST Root Class 3 CA 2 2009 O=D-Trust GmbH +# Subject: CN=D-TRUST Root Class 3 CA 2 2009 O=D-Trust GmbH +# Label: "D-TRUST Root Class 3 CA 2 2009" +# Serial: 623603 +# MD5 Fingerprint: cd:e0:25:69:8d:47:ac:9c:89:35:90:f7:fd:51:3d:2f +# SHA1 Fingerprint: 58:e8:ab:b0:36:15:33:fb:80:f7:9b:1b:6d:29:d3:ff:8d:5f:00:f0 +# SHA256 Fingerprint: 49:e7:a4:42:ac:f0:ea:62:87:05:00:54:b5:25:64:b6:50:e4:f4:9e:42:e3:48:d6:aa:38:e0:39:e9:57:b1:c1 +-----BEGIN CERTIFICATE----- +MIIEMzCCAxugAwIBAgIDCYPzMA0GCSqGSIb3DQEBCwUAME0xCzAJBgNVBAYTAkRF +MRUwEwYDVQQKDAxELVRydXN0IEdtYkgxJzAlBgNVBAMMHkQtVFJVU1QgUm9vdCBD +bGFzcyAzIENBIDIgMjAwOTAeFw0wOTExMDUwODM1NThaFw0yOTExMDUwODM1NTha +ME0xCzAJBgNVBAYTAkRFMRUwEwYDVQQKDAxELVRydXN0IEdtYkgxJzAlBgNVBAMM +HkQtVFJVU1QgUm9vdCBDbGFzcyAzIENBIDIgMjAwOTCCASIwDQYJKoZIhvcNAQEB +BQADggEPADCCAQoCggEBANOySs96R+91myP6Oi/WUEWJNTrGa9v+2wBoqOADER03 +UAifTUpolDWzU9GUY6cgVq/eUXjsKj3zSEhQPgrfRlWLJ23DEE0NkVJD2IfgXU42 +tSHKXzlABF9bfsyjxiupQB7ZNoTWSPOSHjRGICTBpFGOShrvUD9pXRl/RcPHAY9R +ySPocq60vFYJfxLLHLGvKZAKyVXMD9O0Gu1HNVpK7ZxzBCHQqr0ME7UAyiZsxGsM +lFqVlNpQmvH/pStmMaTJOKDfHR+4CS7zp+hnUquVH+BGPtikw8paxTGA6Eian5Rp +/hnd2HN8gcqW3o7tszIFZYQ05ub9VxC1X3a/L7AQDcUCAwEAAaOCARowggEWMA8G +A1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFP3aFMSfMN4hvR5COfyrYyNJ4PGEMA4G +A1UdDwEB/wQEAwIBBjCB0wYDVR0fBIHLMIHIMIGAoH6gfIZ6bGRhcDovL2RpcmVj +dG9yeS5kLXRydXN0Lm5ldC9DTj1ELVRSVVNUJTIwUm9vdCUyMENsYXNzJTIwMyUy +MENBJTIwMiUyMDIwMDksTz1ELVRydXN0JTIwR21iSCxDPURFP2NlcnRpZmljYXRl +cmV2b2NhdGlvbmxpc3QwQ6BBoD+GPWh0dHA6Ly93d3cuZC10cnVzdC5uZXQvY3Js +L2QtdHJ1c3Rfcm9vdF9jbGFzc18zX2NhXzJfMjAwOS5jcmwwDQYJKoZIhvcNAQEL +BQADggEBAH+X2zDI36ScfSF6gHDOFBJpiBSVYEQBrLLpME+bUMJm2H6NMLVwMeni +acfzcNsgFYbQDfC+rAF1hM5+n02/t2A7nPPKHeJeaNijnZflQGDSNiH+0LS4F9p0 +o3/U37CYAqxva2ssJSRyoWXuJVrl5jLn8t+rSfrzkGkj2wTZ51xY/GXUl77M/C4K +zCUqNQT4YJEVdT1B/yMfGchs64JTBKbkTCJNjYy6zltz7GRUUG3RnFX7acM2w4y8 +PIWmawomDeCTmGCufsYkl4phX5GOZpIJhzbNi5stPvZR1FDUWSi9g/LMKHtThm3Y +Johw1+qRzT65ysCQblrGXnRl11z+o+I= +-----END CERTIFICATE----- + +# Issuer: CN=D-TRUST Root Class 3 CA 2 EV 2009 O=D-Trust GmbH +# Subject: CN=D-TRUST Root Class 3 CA 2 EV 2009 O=D-Trust GmbH +# Label: "D-TRUST Root Class 3 CA 2 EV 2009" +# Serial: 623604 +# MD5 Fingerprint: aa:c6:43:2c:5e:2d:cd:c4:34:c0:50:4f:11:02:4f:b6 +# SHA1 Fingerprint: 96:c9:1b:0b:95:b4:10:98:42:fa:d0:d8:22:79:fe:60:fa:b9:16:83 +# SHA256 Fingerprint: ee:c5:49:6b:98:8c:e9:86:25:b9:34:09:2e:ec:29:08:be:d0:b0:f3:16:c2:d4:73:0c:84:ea:f1:f3:d3:48:81 +-----BEGIN CERTIFICATE----- +MIIEQzCCAyugAwIBAgIDCYP0MA0GCSqGSIb3DQEBCwUAMFAxCzAJBgNVBAYTAkRF +MRUwEwYDVQQKDAxELVRydXN0IEdtYkgxKjAoBgNVBAMMIUQtVFJVU1QgUm9vdCBD +bGFzcyAzIENBIDIgRVYgMjAwOTAeFw0wOTExMDUwODUwNDZaFw0yOTExMDUwODUw +NDZaMFAxCzAJBgNVBAYTAkRFMRUwEwYDVQQKDAxELVRydXN0IEdtYkgxKjAoBgNV +BAMMIUQtVFJVU1QgUm9vdCBDbGFzcyAzIENBIDIgRVYgMjAwOTCCASIwDQYJKoZI +hvcNAQEBBQADggEPADCCAQoCggEBAJnxhDRwui+3MKCOvXwEz75ivJn9gpfSegpn +ljgJ9hBOlSJzmY3aFS3nBfwZcyK3jpgAvDw9rKFs+9Z5JUut8Mxk2og+KbgPCdM0 +3TP1YtHhzRnp7hhPTFiu4h7WDFsVWtg6uMQYZB7jM7K1iXdODL/ZlGsTl28So/6Z +qQTMFexgaDbtCHu39b+T7WYxg4zGcTSHThfqr4uRjRxWQa4iN1438h3Z0S0NL2lR +p75mpoo6Kr3HGrHhFPC+Oh25z1uxav60sUYgovseO3Dvk5h9jHOW8sXvhXCtKSb8 +HgQ+HKDYD8tSg2J87otTlZCpV6LqYQXY+U3EJ/pure3511H3a6UCAwEAAaOCASQw +ggEgMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFNOUikxiEyoZLsyvcop9Ntea +HNxnMA4GA1UdDwEB/wQEAwIBBjCB3QYDVR0fBIHVMIHSMIGHoIGEoIGBhn9sZGFw +Oi8vZGlyZWN0b3J5LmQtdHJ1c3QubmV0L0NOPUQtVFJVU1QlMjBSb290JTIwQ2xh +c3MlMjAzJTIwQ0ElMjAyJTIwRVYlMjAyMDA5LE89RC1UcnVzdCUyMEdtYkgsQz1E +RT9jZXJ0aWZpY2F0ZXJldm9jYXRpb25saXN0MEagRKBChkBodHRwOi8vd3d3LmQt +dHJ1c3QubmV0L2NybC9kLXRydXN0X3Jvb3RfY2xhc3NfM19jYV8yX2V2XzIwMDku +Y3JsMA0GCSqGSIb3DQEBCwUAA4IBAQA07XtaPKSUiO8aEXUHL7P+PPoeUSbrh/Yp +3uDx1MYkCenBz1UbtDDZzhr+BlGmFaQt77JLvyAoJUnRpjZ3NOhk31KxEcdzes05 +nsKtjHEh8lprr988TlWvsoRlFIm5d8sqMb7Po23Pb0iUMkZv53GMoKaEGTcH8gNF +CSuGdXzfX2lXANtu2KZyIktQ1HWYVt+3GP9DQ1CuekR78HlR10M9p9OB0/DJT7na +xpeG0ILD5EJt/rDiZE4OJudANCa1CInXCGNjOCd1HjPqbqjdn5lPdE2BiYBL3ZqX +KVwvvoFBuYz/6n1gBp7N1z3TLqMVvKjmJuVvw9y4AyHqnxbxLFS1 +-----END CERTIFICATE----- + +# Issuer: CN=CA Disig Root R2 O=Disig a.s. +# Subject: CN=CA Disig Root R2 O=Disig a.s. +# Label: "CA Disig Root R2" +# Serial: 10572350602393338211 +# MD5 Fingerprint: 26:01:fb:d8:27:a7:17:9a:45:54:38:1a:43:01:3b:03 +# SHA1 Fingerprint: b5:61:eb:ea:a4:de:e4:25:4b:69:1a:98:a5:57:47:c2:34:c7:d9:71 +# SHA256 Fingerprint: e2:3d:4a:03:6d:7b:70:e9:f5:95:b1:42:20:79:d2:b9:1e:df:bb:1f:b6:51:a0:63:3e:aa:8a:9d:c5:f8:07:03 +-----BEGIN CERTIFICATE----- +MIIFaTCCA1GgAwIBAgIJAJK4iNuwisFjMA0GCSqGSIb3DQEBCwUAMFIxCzAJBgNV +BAYTAlNLMRMwEQYDVQQHEwpCcmF0aXNsYXZhMRMwEQYDVQQKEwpEaXNpZyBhLnMu +MRkwFwYDVQQDExBDQSBEaXNpZyBSb290IFIyMB4XDTEyMDcxOTA5MTUzMFoXDTQy +MDcxOTA5MTUzMFowUjELMAkGA1UEBhMCU0sxEzARBgNVBAcTCkJyYXRpc2xhdmEx +EzARBgNVBAoTCkRpc2lnIGEucy4xGTAXBgNVBAMTEENBIERpc2lnIFJvb3QgUjIw +ggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQCio8QACdaFXS1tFPbCw3Oe +NcJxVX6B+6tGUODBfEl45qt5WDza/3wcn9iXAng+a0EE6UG9vgMsRfYvZNSrXaNH +PWSb6WiaxswbP7q+sos0Ai6YVRn8jG+qX9pMzk0DIaPY0jSTVpbLTAwAFjxfGs3I +x2ymrdMxp7zo5eFm1tL7A7RBZckQrg4FY8aAamkw/dLukO8NJ9+flXP04SXabBbe +QTg06ov80egEFGEtQX6sx3dOy1FU+16SGBsEWmjGycT6txOgmLcRK7fWV8x8nhfR +yyX+hk4kLlYMeE2eARKmK6cBZW58Yh2EhN/qwGu1pSqVg8NTEQxzHQuyRpDRQjrO +QG6Vrf/GlK1ul4SOfW+eioANSW1z4nuSHsPzwfPrLgVv2RvPN3YEyLRa5Beny912 +H9AZdugsBbPWnDTYltxhh5EF5EQIM8HauQhl1K6yNg3ruji6DOWbnuuNZt2Zz9aJ +QfYEkoopKW1rOhzndX0CcQ7zwOe9yxndnWCywmZgtrEE7snmhrmaZkCo5xHtgUUD +i/ZnWejBBhG93c+AAk9lQHhcR1DIm+YfgXvkRKhbhZri3lrVx/k6RGZL5DJUfORs +nLMOPReisjQS1n6yqEm70XooQL6iFh/f5DcfEXP7kAplQ6INfPgGAVUzfbANuPT1 +rqVCV3w2EYx7XsQDnYx5nQIDAQABo0IwQDAPBgNVHRMBAf8EBTADAQH/MA4GA1Ud +DwEB/wQEAwIBBjAdBgNVHQ4EFgQUtZn4r7CU9eMg1gqtzk5WpC5uQu0wDQYJKoZI +hvcNAQELBQADggIBACYGXnDnZTPIgm7ZnBc6G3pmsgH2eDtpXi/q/075KMOYKmFM +tCQSin1tERT3nLXK5ryeJ45MGcipvXrA1zYObYVybqjGom32+nNjf7xueQgcnYqf +GopTpti72TVVsRHFqQOzVju5hJMiXn7B9hJSi+osZ7z+Nkz1uM/Rs0mSO9MpDpkb +lvdhuDvEK7Z4bLQjb/D907JedR+Zlais9trhxTF7+9FGs9K8Z7RiVLoJ92Owk6Ka ++elSLotgEqv89WBW7xBci8QaQtyDW2QOy7W81k/BfDxujRNt+3vrMNDcTa/F1bal +TFtxyegxvug4BkihGuLq0t4SOVga/4AOgnXmt8kHbA7v/zjxmHHEt38OFdAlab0i +nSvtBfZGR6ztwPDUO+Ls7pZbkBNOHlY667DvlruWIxG68kOGdGSVyCh13x01utI3 +gzhTODY7z2zp+WsO0PsE6E9312UBeIYMej4hYvF/Y3EMyZ9E26gnonW+boE+18Dr +G5gPcFw0sorMwIUY6256s/daoQe/qUKS82Ail+QUoQebTnbAjn39pCXHR+3/H3Os +zMOl6W8KjptlwlCFtaOgUxLMVYdh84GuEEZhvUQhuMI9dM9+JDX6HAcOmz0iyu8x +L4ysEr3vQCj8KWefshNPZiTEUxnpHikV7+ZtsH8tZ/3zbBt1RqPlShfppNcL +-----END CERTIFICATE----- + +# Issuer: CN=ACCVRAIZ1 O=ACCV OU=PKIACCV +# Subject: CN=ACCVRAIZ1 O=ACCV OU=PKIACCV +# Label: "ACCVRAIZ1" +# Serial: 6828503384748696800 +# MD5 Fingerprint: d0:a0:5a:ee:05:b6:09:94:21:a1:7d:f1:b2:29:82:02 +# SHA1 Fingerprint: 93:05:7a:88:15:c6:4f:ce:88:2f:fa:91:16:52:28:78:bc:53:64:17 +# SHA256 Fingerprint: 9a:6e:c0:12:e1:a7:da:9d:be:34:19:4d:47:8a:d7:c0:db:18:22:fb:07:1d:f1:29:81:49:6e:d1:04:38:41:13 +-----BEGIN CERTIFICATE----- +MIIH0zCCBbugAwIBAgIIXsO3pkN/pOAwDQYJKoZIhvcNAQEFBQAwQjESMBAGA1UE +AwwJQUNDVlJBSVoxMRAwDgYDVQQLDAdQS0lBQ0NWMQ0wCwYDVQQKDARBQ0NWMQsw +CQYDVQQGEwJFUzAeFw0xMTA1MDUwOTM3MzdaFw0zMDEyMzEwOTM3MzdaMEIxEjAQ +BgNVBAMMCUFDQ1ZSQUlaMTEQMA4GA1UECwwHUEtJQUNDVjENMAsGA1UECgwEQUND +VjELMAkGA1UEBhMCRVMwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQCb +qau/YUqXry+XZpp0X9DZlv3P4uRm7x8fRzPCRKPfmt4ftVTdFXxpNRFvu8gMjmoY +HtiP2Ra8EEg2XPBjs5BaXCQ316PWywlxufEBcoSwfdtNgM3802/J+Nq2DoLSRYWo +G2ioPej0RGy9ocLLA76MPhMAhN9KSMDjIgro6TenGEyxCQ0jVn8ETdkXhBilyNpA +lHPrzg5XPAOBOp0KoVdDaaxXbXmQeOW1tDvYvEyNKKGno6e6Ak4l0Squ7a4DIrhr +IA8wKFSVf+DuzgpmndFALW4ir50awQUZ0m/A8p/4e7MCQvtQqR0tkw8jq8bBD5L/ +0KIV9VMJcRz/RROE5iZe+OCIHAr8Fraocwa48GOEAqDGWuzndN9wrqODJerWx5eH +k6fGioozl2A3ED6XPm4pFdahD9GILBKfb6qkxkLrQaLjlUPTAYVtjrs78yM2x/47 +4KElB0iryYl0/wiPgL/AlmXz7uxLaL2diMMxs0Dx6M/2OLuc5NF/1OVYm3z61PMO +m3WR5LpSLhl+0fXNWhn8ugb2+1KoS5kE3fj5tItQo05iifCHJPqDQsGH+tUtKSpa +cXpkatcnYGMN285J9Y0fkIkyF/hzQ7jSWpOGYdbhdQrqeWZ2iE9x6wQl1gpaepPl +uUsXQA+xtrn13k/c4LOsOxFwYIRKQ26ZIMApcQrAZQIDAQABo4ICyzCCAscwfQYI +KwYBBQUHAQEEcTBvMEwGCCsGAQUFBzAChkBodHRwOi8vd3d3LmFjY3YuZXMvZmls +ZWFkbWluL0FyY2hpdm9zL2NlcnRpZmljYWRvcy9yYWl6YWNjdjEuY3J0MB8GCCsG +AQUFBzABhhNodHRwOi8vb2NzcC5hY2N2LmVzMB0GA1UdDgQWBBTSh7Tj3zcnk1X2 +VuqB5TbMjB4/vTAPBgNVHRMBAf8EBTADAQH/MB8GA1UdIwQYMBaAFNKHtOPfNyeT +VfZW6oHlNsyMHj+9MIIBcwYDVR0gBIIBajCCAWYwggFiBgRVHSAAMIIBWDCCASIG +CCsGAQUFBwICMIIBFB6CARAAQQB1AHQAbwByAGkAZABhAGQAIABkAGUAIABDAGUA +cgB0AGkAZgBpAGMAYQBjAGkA8wBuACAAUgBhAO0AegAgAGQAZQAgAGwAYQAgAEEA +QwBDAFYAIAAoAEEAZwBlAG4AYwBpAGEAIABkAGUAIABUAGUAYwBuAG8AbABvAGcA +7QBhACAAeQAgAEMAZQByAHQAaQBmAGkAYwBhAGMAaQDzAG4AIABFAGwAZQBjAHQA +cgDzAG4AaQBjAGEALAAgAEMASQBGACAAUQA0ADYAMAAxADEANQA2AEUAKQAuACAA +QwBQAFMAIABlAG4AIABoAHQAdABwADoALwAvAHcAdwB3AC4AYQBjAGMAdgAuAGUA +czAwBggrBgEFBQcCARYkaHR0cDovL3d3dy5hY2N2LmVzL2xlZ2lzbGFjaW9uX2Mu +aHRtMFUGA1UdHwROMEwwSqBIoEaGRGh0dHA6Ly93d3cuYWNjdi5lcy9maWxlYWRt +aW4vQXJjaGl2b3MvY2VydGlmaWNhZG9zL3JhaXphY2N2MV9kZXIuY3JsMA4GA1Ud +DwEB/wQEAwIBBjAXBgNVHREEEDAOgQxhY2N2QGFjY3YuZXMwDQYJKoZIhvcNAQEF +BQADggIBAJcxAp/n/UNnSEQU5CmH7UwoZtCPNdpNYbdKl02125DgBS4OxnnQ8pdp +D70ER9m+27Up2pvZrqmZ1dM8MJP1jaGo/AaNRPTKFpV8M9xii6g3+CfYCS0b78gU +JyCpZET/LtZ1qmxNYEAZSUNUY9rizLpm5U9EelvZaoErQNV/+QEnWCzI7UiRfD+m +AM/EKXMRNt6GGT6d7hmKG9Ww7Y49nCrADdg9ZuM8Db3VlFzi4qc1GwQA9j9ajepD +vV+JHanBsMyZ4k0ACtrJJ1vnE5Bc5PUzolVt3OAJTS+xJlsndQAJxGJ3KQhfnlms +tn6tn1QwIgPBHnFk/vk4CpYY3QIUrCPLBhwepH2NDd4nQeit2hW3sCPdK6jT2iWH +7ehVRE2I9DZ+hJp4rPcOVkkO1jMl1oRQQmwgEh0q1b688nCBpHBgvgW1m54ERL5h +I6zppSSMEYCUWqKiuUnSwdzRp+0xESyeGabu4VXhwOrPDYTkF7eifKXeVSUG7szA +h1xA2syVP1XgNce4hL60Xc16gwFy7ofmXx2utYXGJt/mwZrpHgJHnyqobalbz+xF +d3+YJ5oyXSrjhO7FmGYvliAd3djDJ9ew+f7Zfc3Qn48LFFhRny+Lwzgt3uiP1o2H +pPVWQxaZLPSkVrQ0uGE3ycJYgBugl6H8WY3pEfbRD0tVNEYqi4Y7 +-----END CERTIFICATE----- + +# Issuer: CN=TWCA Global Root CA O=TAIWAN-CA OU=Root CA +# Subject: CN=TWCA Global Root CA O=TAIWAN-CA OU=Root CA +# Label: "TWCA Global Root CA" +# Serial: 3262 +# MD5 Fingerprint: f9:03:7e:cf:e6:9e:3c:73:7a:2a:90:07:69:ff:2b:96 +# SHA1 Fingerprint: 9c:bb:48:53:f6:a4:f6:d3:52:a4:e8:32:52:55:60:13:f5:ad:af:65 +# SHA256 Fingerprint: 59:76:90:07:f7:68:5d:0f:cd:50:87:2f:9f:95:d5:75:5a:5b:2b:45:7d:81:f3:69:2b:61:0a:98:67:2f:0e:1b +-----BEGIN CERTIFICATE----- +MIIFQTCCAymgAwIBAgICDL4wDQYJKoZIhvcNAQELBQAwUTELMAkGA1UEBhMCVFcx +EjAQBgNVBAoTCVRBSVdBTi1DQTEQMA4GA1UECxMHUm9vdCBDQTEcMBoGA1UEAxMT +VFdDQSBHbG9iYWwgUm9vdCBDQTAeFw0xMjA2MjcwNjI4MzNaFw0zMDEyMzExNTU5 +NTlaMFExCzAJBgNVBAYTAlRXMRIwEAYDVQQKEwlUQUlXQU4tQ0ExEDAOBgNVBAsT +B1Jvb3QgQ0ExHDAaBgNVBAMTE1RXQ0EgR2xvYmFsIFJvb3QgQ0EwggIiMA0GCSqG +SIb3DQEBAQUAA4ICDwAwggIKAoICAQCwBdvI64zEbooh745NnHEKH1Jw7W2CnJfF +10xORUnLQEK1EjRsGcJ0pDFfhQKX7EMzClPSnIyOt7h52yvVavKOZsTuKwEHktSz +0ALfUPZVr2YOy+BHYC8rMjk1Ujoog/h7FsYYuGLWRyWRzvAZEk2tY/XTP3VfKfCh +MBwqoJimFb3u/Rk28OKRQ4/6ytYQJ0lM793B8YVwm8rqqFpD/G2Gb3PpN0Wp8DbH +zIh1HrtsBv+baz4X7GGqcXzGHaL3SekVtTzWoWH1EfcFbx39Eb7QMAfCKbAJTibc +46KokWofwpFFiFzlmLhxpRUZyXx1EcxwdE8tmx2RRP1WKKD+u4ZqyPpcC1jcxkt2 +yKsi2XMPpfRaAok/T54igu6idFMqPVMnaR1sjjIsZAAmY2E2TqNGtz99sy2sbZCi +laLOz9qC5wc0GZbpuCGqKX6mOL6OKUohZnkfs8O1CWfe1tQHRvMq2uYiN2DLgbYP +oA/pyJV/v1WRBXrPPRXAb94JlAGD1zQbzECl8LibZ9WYkTunhHiVJqRaCPgrdLQA +BDzfuBSO6N+pjWxnkjMdwLfS7JLIvgm/LCkFbwJrnu+8vyq8W8BQj0FwcYeyTbcE +qYSjMq+u7msXi7Kx/mzhkIyIqJdIzshNy/MGz19qCkKxHh53L46g5pIOBvwFItIm +4TFRfTLcDwIDAQABoyMwITAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB +/zANBgkqhkiG9w0BAQsFAAOCAgEAXzSBdu+WHdXltdkCY4QWwa6gcFGn90xHNcgL +1yg9iXHZqjNB6hQbbCEAwGxCGX6faVsgQt+i0trEfJdLjbDorMjupWkEmQqSpqsn +LhpNgb+E1HAerUf+/UqdM+DyucRFCCEK2mlpc3INvjT+lIutwx4116KD7+U4x6WF +H6vPNOw/KP4M8VeGTslV9xzU2KV9Bnpv1d8Q34FOIWWxtuEXeZVFBs5fzNxGiWNo +RI2T9GRwoD2dKAXDOXC4Ynsg/eTb6QihuJ49CcdP+yz4k3ZB3lLg4VfSnQO8d57+ +nile98FRYB/e2guyLXW3Q0iT5/Z5xoRdgFlglPx4mI88k1HtQJAH32RjJMtOcQWh +15QaiDLxInQirqWm2BJpTGCjAu4r7NRjkgtevi92a6O2JryPA9gK8kxkRr05YuWW +6zRjESjMlfGt7+/cgFhI6Uu46mWs6fyAtbXIRfmswZ/ZuepiiI7E8UuDEq3mi4TW +nsLrgxifarsbJGAzcMzs9zLzXNl5fe+epP7JI8Mk7hWSsT2RTyaGvWZzJBPqpK5j +wa19hAM8EHiGG3njxPPyBJUgriOCxLM6AGK/5jYk4Ve6xx6QddVfP5VhK8E7zeWz +aGHQRiapIVJpLesux+t3zqY6tQMzT3bR51xUAV3LePTJDL/PEo4XLSNolOer/qmy +KwbQBM0= +-----END CERTIFICATE----- + +# Issuer: CN=TeliaSonera Root CA v1 O=TeliaSonera +# Subject: CN=TeliaSonera Root CA v1 O=TeliaSonera +# Label: "TeliaSonera Root CA v1" +# Serial: 199041966741090107964904287217786801558 +# MD5 Fingerprint: 37:41:49:1b:18:56:9a:26:f5:ad:c2:66:fb:40:a5:4c +# SHA1 Fingerprint: 43:13:bb:96:f1:d5:86:9b:c1:4e:6a:92:f6:cf:f6:34:69:87:82:37 +# SHA256 Fingerprint: dd:69:36:fe:21:f8:f0:77:c1:23:a1:a5:21:c1:22:24:f7:22:55:b7:3e:03:a7:26:06:93:e8:a2:4b:0f:a3:89 +-----BEGIN CERTIFICATE----- +MIIFODCCAyCgAwIBAgIRAJW+FqD3LkbxezmCcvqLzZYwDQYJKoZIhvcNAQEFBQAw +NzEUMBIGA1UECgwLVGVsaWFTb25lcmExHzAdBgNVBAMMFlRlbGlhU29uZXJhIFJv +b3QgQ0EgdjEwHhcNMDcxMDE4MTIwMDUwWhcNMzIxMDE4MTIwMDUwWjA3MRQwEgYD +VQQKDAtUZWxpYVNvbmVyYTEfMB0GA1UEAwwWVGVsaWFTb25lcmEgUm9vdCBDQSB2 +MTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAMK+6yfwIaPzaSZVfp3F +VRaRXP3vIb9TgHot0pGMYzHw7CTww6XScnwQbfQ3t+XmfHnqjLWCi65ItqwA3GV1 +7CpNX8GH9SBlK4GoRz6JI5UwFpB/6FcHSOcZrr9FZ7E3GwYq/t75rH2D+1665I+X +Z75Ljo1kB1c4VWk0Nj0TSO9P4tNmHqTPGrdeNjPUtAa9GAH9d4RQAEX1jF3oI7x+ +/jXh7VB7qTCNGdMJjmhnXb88lxhTuylixcpecsHHltTbLaC0H2kD7OriUPEMPPCs +81Mt8Bz17Ww5OXOAFshSsCPN4D7c3TxHoLs1iuKYaIu+5b9y7tL6pe0S7fyYGKkm +dtwoSxAgHNN/Fnct7W+A90m7UwW7XWjH1Mh1Fj+JWov3F0fUTPHSiXk+TT2YqGHe +Oh7S+F4D4MHJHIzTjU3TlTazN19jY5szFPAtJmtTfImMMsJu7D0hADnJoWjiUIMu +sDor8zagrC/kb2HCUQk5PotTubtn2txTuXZZNp1D5SDgPTJghSJRt8czu90VL6R4 +pgd7gUY2BIbdeTXHlSw7sKMXNeVzH7RcWe/a6hBle3rQf5+ztCo3O3CLm1u5K7fs +slESl1MpWtTwEhDcTwK7EpIvYtQ/aUN8Ddb8WHUBiJ1YFkveupD/RwGJBmr2X7KQ +arMCpgKIv7NHfirZ1fpoeDVNAgMBAAGjPzA9MA8GA1UdEwEB/wQFMAMBAf8wCwYD +VR0PBAQDAgEGMB0GA1UdDgQWBBTwj1k4ALP1j5qWDNXr+nuqF+gTEjANBgkqhkiG +9w0BAQUFAAOCAgEAvuRcYk4k9AwI//DTDGjkk0kiP0Qnb7tt3oNmzqjMDfz1mgbl +dxSR651Be5kqhOX//CHBXfDkH1e3damhXwIm/9fH907eT/j3HEbAek9ALCI18Bmx +0GtnLLCo4MBANzX2hFxc469CeP6nyQ1Q6g2EdvZR74NTxnr/DlZJLo961gzmJ1Tj +TQpgcmLNkQfWpb/ImWvtxBnmq0wROMVvMeJuScg/doAmAyYp4Db29iBT4xdwNBed +Y2gea+zDTYa4EzAvXUYNR0PVG6pZDrlcjQZIrXSHX8f8MVRBE+LHIQ6e4B4N4cB7 +Q4WQxYpYxmUKeFfyxiMPAdkgS94P+5KFdSpcc41teyWRyu5FrgZLAMzTsVlQ2jqI +OylDRl6XK1TOU2+NSueW+r9xDkKLfP0ooNBIytrEgUy7onOTJsjrDNYmiLbAJM+7 +vVvrdX3pCI6GMyx5dwlppYn8s3CQh3aP0yK7Qs69cwsgJirQmz1wHiRszYd2qReW +t88NkvuOGKmYSdGe/mBEciG5Ge3C9THxOUiIkCR1VBatzvT4aRRkOfujuLpwQMcn +HL/EVlP6Y2XQ8xwOFvVrhlhNGNTkDY6lnVuR3HYkUD/GKvvZt5y11ubQ2egZixVx +SK236thZiNSQvxaz2emsWWFUyBy6ysHK4bkgTI86k4mloMy/0/Z1pHWWbVY= +-----END CERTIFICATE----- + +# Issuer: CN=E-Tugra Certification Authority O=E-Tu\u011fra EBG Bili\u015fim Teknolojileri ve Hizmetleri A.\u015e. OU=E-Tugra Sertifikasyon Merkezi +# Subject: CN=E-Tugra Certification Authority O=E-Tu\u011fra EBG Bili\u015fim Teknolojileri ve Hizmetleri A.\u015e. OU=E-Tugra Sertifikasyon Merkezi +# Label: "E-Tugra Certification Authority" +# Serial: 7667447206703254355 +# MD5 Fingerprint: b8:a1:03:63:b0:bd:21:71:70:8a:6f:13:3a:bb:79:49 +# SHA1 Fingerprint: 51:c6:e7:08:49:06:6e:f3:92:d4:5c:a0:0d:6d:a3:62:8f:c3:52:39 +# SHA256 Fingerprint: b0:bf:d5:2b:b0:d7:d9:bd:92:bf:5d:4d:c1:3d:a2:55:c0:2c:54:2f:37:83:65:ea:89:39:11:f5:5e:55:f2:3c +-----BEGIN CERTIFICATE----- +MIIGSzCCBDOgAwIBAgIIamg+nFGby1MwDQYJKoZIhvcNAQELBQAwgbIxCzAJBgNV +BAYTAlRSMQ8wDQYDVQQHDAZBbmthcmExQDA+BgNVBAoMN0UtVHXEn3JhIEVCRyBC +aWxpxZ9pbSBUZWtub2xvamlsZXJpIHZlIEhpem1ldGxlcmkgQS7Fni4xJjAkBgNV +BAsMHUUtVHVncmEgU2VydGlmaWthc3lvbiBNZXJrZXppMSgwJgYDVQQDDB9FLVR1 +Z3JhIENlcnRpZmljYXRpb24gQXV0aG9yaXR5MB4XDTEzMDMwNTEyMDk0OFoXDTIz +MDMwMzEyMDk0OFowgbIxCzAJBgNVBAYTAlRSMQ8wDQYDVQQHDAZBbmthcmExQDA+ +BgNVBAoMN0UtVHXEn3JhIEVCRyBCaWxpxZ9pbSBUZWtub2xvamlsZXJpIHZlIEhp +em1ldGxlcmkgQS7Fni4xJjAkBgNVBAsMHUUtVHVncmEgU2VydGlmaWthc3lvbiBN +ZXJrZXppMSgwJgYDVQQDDB9FLVR1Z3JhIENlcnRpZmljYXRpb24gQXV0aG9yaXR5 +MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEA4vU/kwVRHoViVF56C/UY +B4Oufq9899SKa6VjQzm5S/fDxmSJPZQuVIBSOTkHS0vdhQd2h8y/L5VMzH2nPbxH +D5hw+IyFHnSOkm0bQNGZDbt1bsipa5rAhDGvykPL6ys06I+XawGb1Q5KCKpbknSF +Q9OArqGIW66z6l7LFpp3RMih9lRozt6Plyu6W0ACDGQXwLWTzeHxE2bODHnv0ZEo +q1+gElIwcxmOj+GMB6LDu0rw6h8VqO4lzKRG+Bsi77MOQ7osJLjFLFzUHPhdZL3D +k14opz8n8Y4e0ypQBaNV2cvnOVPAmJ6MVGKLJrD3fY185MaeZkJVgkfnsliNZvcH +fC425lAcP9tDJMW/hkd5s3kc91r0E+xs+D/iWR+V7kI+ua2oMoVJl0b+SzGPWsut +dEcf6ZG33ygEIqDUD13ieU/qbIWGvaimzuT6w+Gzrt48Ue7LE3wBf4QOXVGUnhMM +ti6lTPk5cDZvlsouDERVxcr6XQKj39ZkjFqzAQqptQpHF//vkUAqjqFGOjGY5RH8 +zLtJVor8udBhmm9lbObDyz51Sf6Pp+KJxWfXnUYTTjF2OySznhFlhqt/7x3U+Lzn +rFpct1pHXFXOVbQicVtbC/DP3KBhZOqp12gKY6fgDT+gr9Oq0n7vUaDmUStVkhUX +U8u3Zg5mTPj5dUyQ5xJwx0UCAwEAAaNjMGEwHQYDVR0OBBYEFC7j27JJ0JxUeVz6 +Jyr+zE7S6E5UMA8GA1UdEwEB/wQFMAMBAf8wHwYDVR0jBBgwFoAULuPbsknQnFR5 +XPonKv7MTtLoTlQwDgYDVR0PAQH/BAQDAgEGMA0GCSqGSIb3DQEBCwUAA4ICAQAF +Nzr0TbdF4kV1JI+2d1LoHNgQk2Xz8lkGpD4eKexd0dCrfOAKkEh47U6YA5n+KGCR +HTAduGN8qOY1tfrTYXbm1gdLymmasoR6d5NFFxWfJNCYExL/u6Au/U5Mh/jOXKqY +GwXgAEZKgoClM4so3O0409/lPun++1ndYYRP0lSWE2ETPo+Aab6TR7U1Q9Jauz1c +77NCR807VRMGsAnb/WP2OogKmW9+4c4bU2pEZiNRCHu8W1Ki/QY3OEBhj0qWuJA3 ++GbHeJAAFS6LrVE1Uweoa2iu+U48BybNCAVwzDk/dr2l02cmAYamU9JgO3xDf1WK +vJUawSg5TB9D0pH0clmKuVb8P7Sd2nCcdlqMQ1DujjByTd//SffGqWfZbawCEeI6 +FiWnWAjLb1NBnEg4R2gz0dfHj9R0IdTDBZB6/86WiLEVKV0jq9BgoRJP3vQXzTLl +yb/IQ639Lo7xr+L0mPoSHyDYwKcMhcWQ9DstliaxLL5Mq+ux0orJ23gTDx4JnW2P +AJ8C2sH6H3p6CcRK5ogql5+Ji/03X186zjhZhkuvcQu02PJwT58yE+Owp1fl2tpD +y4Q08ijE6m30Ku/Ba3ba+367hTzSU8JNvnHhRdH9I2cNE3X7z2VnIp2usAnRCf8d +NL/+I5c30jn6PQ0GC7TbO6Orb1wdtn7os4I07QZcJA== +-----END CERTIFICATE----- + +# Issuer: CN=T-TeleSec GlobalRoot Class 2 O=T-Systems Enterprise Services GmbH OU=T-Systems Trust Center +# Subject: CN=T-TeleSec GlobalRoot Class 2 O=T-Systems Enterprise Services GmbH OU=T-Systems Trust Center +# Label: "T-TeleSec GlobalRoot Class 2" +# Serial: 1 +# MD5 Fingerprint: 2b:9b:9e:e4:7b:6c:1f:00:72:1a:cc:c1:77:79:df:6a +# SHA1 Fingerprint: 59:0d:2d:7d:88:4f:40:2e:61:7e:a5:62:32:17:65:cf:17:d8:94:e9 +# SHA256 Fingerprint: 91:e2:f5:78:8d:58:10:eb:a7:ba:58:73:7d:e1:54:8a:8e:ca:cd:01:45:98:bc:0b:14:3e:04:1b:17:05:25:52 +-----BEGIN CERTIFICATE----- +MIIDwzCCAqugAwIBAgIBATANBgkqhkiG9w0BAQsFADCBgjELMAkGA1UEBhMCREUx +KzApBgNVBAoMIlQtU3lzdGVtcyBFbnRlcnByaXNlIFNlcnZpY2VzIEdtYkgxHzAd +BgNVBAsMFlQtU3lzdGVtcyBUcnVzdCBDZW50ZXIxJTAjBgNVBAMMHFQtVGVsZVNl +YyBHbG9iYWxSb290IENsYXNzIDIwHhcNMDgxMDAxMTA0MDE0WhcNMzMxMDAxMjM1 +OTU5WjCBgjELMAkGA1UEBhMCREUxKzApBgNVBAoMIlQtU3lzdGVtcyBFbnRlcnBy +aXNlIFNlcnZpY2VzIEdtYkgxHzAdBgNVBAsMFlQtU3lzdGVtcyBUcnVzdCBDZW50 +ZXIxJTAjBgNVBAMMHFQtVGVsZVNlYyBHbG9iYWxSb290IENsYXNzIDIwggEiMA0G +CSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCqX9obX+hzkeXaXPSi5kfl82hVYAUd +AqSzm1nzHoqvNK38DcLZSBnuaY/JIPwhqgcZ7bBcrGXHX+0CfHt8LRvWurmAwhiC +FoT6ZrAIxlQjgeTNuUk/9k9uN0goOA/FvudocP05l03Sx5iRUKrERLMjfTlH6VJi +1hKTXrcxlkIF+3anHqP1wvzpesVsqXFP6st4vGCvx9702cu+fjOlbpSD8DT6Iavq +jnKgP6TeMFvvhk1qlVtDRKgQFRzlAVfFmPHmBiiRqiDFt1MmUUOyCxGVWOHAD3bZ +wI18gfNycJ5v/hqO2V81xrJvNHy+SE/iWjnX2J14np+GPgNeGYtEotXHAgMBAAGj +QjBAMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgEGMB0GA1UdDgQWBBS/ +WSA2AHmgoCJrjNXyYdK4LMuCSjANBgkqhkiG9w0BAQsFAAOCAQEAMQOiYQsfdOhy +NsZt+U2e+iKo4YFWz827n+qrkRk4r6p8FU3ztqONpfSO9kSpp+ghla0+AGIWiPAC +uvxhI+YzmzB6azZie60EI4RYZeLbK4rnJVM3YlNfvNoBYimipidx5joifsFvHZVw +IEoHNN/q/xWA5brXethbdXwFeilHfkCoMRN3zUA7tFFHei4R40cR3p1m0IvVVGb6 +g1XqfMIpiRvpb7PO4gWEyS8+eIVibslfwXhjdFjASBgMmTnrpMwatXlajRWc2BQN +9noHV8cigwUtPJslJj0Ys6lDfMjIq2SPDqO/nBudMNva0Bkuqjzx+zOAduTNrRlP +BSeOE6Fuwg== +-----END CERTIFICATE----- + +# Issuer: CN=Atos TrustedRoot 2011 O=Atos +# Subject: CN=Atos TrustedRoot 2011 O=Atos +# Label: "Atos TrustedRoot 2011" +# Serial: 6643877497813316402 +# MD5 Fingerprint: ae:b9:c4:32:4b:ac:7f:5d:66:cc:77:94:bb:2a:77:56 +# SHA1 Fingerprint: 2b:b1:f5:3e:55:0c:1d:c5:f1:d4:e6:b7:6a:46:4b:55:06:02:ac:21 +# SHA256 Fingerprint: f3:56:be:a2:44:b7:a9:1e:b3:5d:53:ca:9a:d7:86:4a:ce:01:8e:2d:35:d5:f8:f9:6d:df:68:a6:f4:1a:a4:74 +-----BEGIN CERTIFICATE----- +MIIDdzCCAl+gAwIBAgIIXDPLYixfszIwDQYJKoZIhvcNAQELBQAwPDEeMBwGA1UE +AwwVQXRvcyBUcnVzdGVkUm9vdCAyMDExMQ0wCwYDVQQKDARBdG9zMQswCQYDVQQG +EwJERTAeFw0xMTA3MDcxNDU4MzBaFw0zMDEyMzEyMzU5NTlaMDwxHjAcBgNVBAMM +FUF0b3MgVHJ1c3RlZFJvb3QgMjAxMTENMAsGA1UECgwEQXRvczELMAkGA1UEBhMC +REUwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCVhTuXbyo7LjvPpvMp +Nb7PGKw+qtn4TaA+Gke5vJrf8v7MPkfoepbCJI419KkM/IL9bcFyYie96mvr54rM +VD6QUM+A1JX76LWC1BTFtqlVJVfbsVD2sGBkWXppzwO3bw2+yj5vdHLqqjAqc2K+ +SZFhyBH+DgMq92og3AIVDV4VavzjgsG1xZ1kCWyjWZgHJ8cblithdHFsQ/H3NYkQ +4J7sVaE3IqKHBAUsR320HLliKWYoyrfhk/WklAOZuXCFteZI6o1Q/NnezG8HDt0L +cp2AMBYHlT8oDv3FdU9T1nSatCQujgKRz3bFmx5VdJx4IbHwLfELn8LVlhgf8FQi +eowHAgMBAAGjfTB7MB0GA1UdDgQWBBSnpQaxLKYJYO7Rl+lwrrw7GWzbITAPBgNV +HRMBAf8EBTADAQH/MB8GA1UdIwQYMBaAFKelBrEspglg7tGX6XCuvDsZbNshMBgG +A1UdIAQRMA8wDQYLKwYBBAGwLQMEAQEwDgYDVR0PAQH/BAQDAgGGMA0GCSqGSIb3 +DQEBCwUAA4IBAQAmdzTblEiGKkGdLD4GkGDEjKwLVLgfuXvTBznk+j57sj1O7Z8j +vZfza1zv7v1Apt+hk6EKhqzvINB5Ab149xnYJDE0BAGmuhWawyfc2E8PzBhj/5kP +DpFrdRbhIfzYJsdHt6bPWHJxfrrhTZVHO8mvbaG0weyJ9rQPOLXiZNwlz6bb65pc +maHFCN795trV1lpFDMS3wrUU77QR/w4VtfX128a961qn8FYiqTxlVMYVqL2Gns2D +lmh6cYGJ4Qvh6hEbaAjMaZ7snkGeRDImeuKHCnE96+RapNLbxc3G3mB/ufNPRJLv +KrcYPqcZ2Qt9sTdBQrC6YB3y/gkRsPCHe6ed +-----END CERTIFICATE----- + +# Issuer: CN=QuoVadis Root CA 1 G3 O=QuoVadis Limited +# Subject: CN=QuoVadis Root CA 1 G3 O=QuoVadis Limited +# Label: "QuoVadis Root CA 1 G3" +# Serial: 687049649626669250736271037606554624078720034195 +# MD5 Fingerprint: a4:bc:5b:3f:fe:37:9a:fa:64:f0:e2:fa:05:3d:0b:ab +# SHA1 Fingerprint: 1b:8e:ea:57:96:29:1a:c9:39:ea:b8:0a:81:1a:73:73:c0:93:79:67 +# SHA256 Fingerprint: 8a:86:6f:d1:b2:76:b5:7e:57:8e:92:1c:65:82:8a:2b:ed:58:e9:f2:f2:88:05:41:34:b7:f1:f4:bf:c9:cc:74 +-----BEGIN CERTIFICATE----- +MIIFYDCCA0igAwIBAgIUeFhfLq0sGUvjNwc1NBMotZbUZZMwDQYJKoZIhvcNAQEL +BQAwSDELMAkGA1UEBhMCQk0xGTAXBgNVBAoTEFF1b1ZhZGlzIExpbWl0ZWQxHjAc +BgNVBAMTFVF1b1ZhZGlzIFJvb3QgQ0EgMSBHMzAeFw0xMjAxMTIxNzI3NDRaFw00 +MjAxMTIxNzI3NDRaMEgxCzAJBgNVBAYTAkJNMRkwFwYDVQQKExBRdW9WYWRpcyBM +aW1pdGVkMR4wHAYDVQQDExVRdW9WYWRpcyBSb290IENBIDEgRzMwggIiMA0GCSqG +SIb3DQEBAQUAA4ICDwAwggIKAoICAQCgvlAQjunybEC0BJyFuTHK3C3kEakEPBtV +wedYMB0ktMPvhd6MLOHBPd+C5k+tR4ds7FtJwUrVu4/sh6x/gpqG7D0DmVIB0jWe +rNrwU8lmPNSsAgHaJNM7qAJGr6Qc4/hzWHa39g6QDbXwz8z6+cZM5cOGMAqNF341 +68Xfuw6cwI2H44g4hWf6Pser4BOcBRiYz5P1sZK0/CPTz9XEJ0ngnjybCKOLXSoh +4Pw5qlPafX7PGglTvF0FBM+hSo+LdoINofjSxxR3W5A2B4GbPgb6Ul5jxaYA/qXp +UhtStZI5cgMJYr2wYBZupt0lwgNm3fME0UDiTouG9G/lg6AnhF4EwfWQvTA9xO+o +abw4m6SkltFi2mnAAZauy8RRNOoMqv8hjlmPSlzkYZqn0ukqeI1RPToV7qJZjqlc +3sX5kCLliEVx3ZGZbHqfPT2YfF72vhZooF6uCyP8Wg+qInYtyaEQHeTTRCOQiJ/G +KubX9ZqzWB4vMIkIG1SitZgj7Ah3HJVdYdHLiZxfokqRmu8hqkkWCKi9YSgxyXSt +hfbZxbGL0eUQMk1fiyA6PEkfM4VZDdvLCXVDaXP7a3F98N/ETH3Goy7IlXnLc6KO +Tk0k+17kBL5yG6YnLUlamXrXXAkgt3+UuU/xDRxeiEIbEbfnkduebPRq34wGmAOt +zCjvpUfzUwIDAQABo0IwQDAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIB +BjAdBgNVHQ4EFgQUo5fW816iEOGrRZ88F2Q87gFwnMwwDQYJKoZIhvcNAQELBQAD +ggIBABj6W3X8PnrHX3fHyt/PX8MSxEBd1DKquGrX1RUVRpgjpeaQWxiZTOOtQqOC +MTaIzen7xASWSIsBx40Bz1szBpZGZnQdT+3Btrm0DWHMY37XLneMlhwqI2hrhVd2 +cDMT/uFPpiN3GPoajOi9ZcnPP/TJF9zrx7zABC4tRi9pZsMbj/7sPtPKlL92CiUN +qXsCHKnQO18LwIE6PWThv6ctTr1NxNgpxiIY0MWscgKCP6o6ojoilzHdCGPDdRS5 +YCgtW2jgFqlmgiNR9etT2DGbe+m3nUvriBbP+V04ikkwj+3x6xn0dxoxGE1nVGwv +b2X52z3sIexe9PSLymBlVNFxZPT5pqOBMzYzcfCkeF9OrYMh3jRJjehZrJ3ydlo2 +8hP0r+AJx2EqbPfgna67hkooby7utHnNkDPDs3b69fBsnQGQ+p6Q9pxyz0fawx/k +NSBT8lTR32GDpgLiJTjehTItXnOQUl1CxM49S+H5GYQd1aJQzEH7QRTDvdbJWqNj +ZgKAvQU6O0ec7AAmTPWIUb+oI38YB7AL7YsmoWTTYUrrXJ/es69nA7Mf3W1daWhp +q1467HxpvMc7hU6eFbm0FU/DlXpY18ls6Wy58yljXrQs8C097Vpl4KlbQMJImYFt +nh8GKjwStIsPm6Ik8KaN1nrgS7ZklmOVhMJKzRwuJIczYOXD +-----END CERTIFICATE----- + +# Issuer: CN=QuoVadis Root CA 2 G3 O=QuoVadis Limited +# Subject: CN=QuoVadis Root CA 2 G3 O=QuoVadis Limited +# Label: "QuoVadis Root CA 2 G3" +# Serial: 390156079458959257446133169266079962026824725800 +# MD5 Fingerprint: af:0c:86:6e:bf:40:2d:7f:0b:3e:12:50:ba:12:3d:06 +# SHA1 Fingerprint: 09:3c:61:f3:8b:8b:dc:7d:55:df:75:38:02:05:00:e1:25:f5:c8:36 +# SHA256 Fingerprint: 8f:e4:fb:0a:f9:3a:4d:0d:67:db:0b:eb:b2:3e:37:c7:1b:f3:25:dc:bc:dd:24:0e:a0:4d:af:58:b4:7e:18:40 +-----BEGIN CERTIFICATE----- +MIIFYDCCA0igAwIBAgIURFc0JFuBiZs18s64KztbpybwdSgwDQYJKoZIhvcNAQEL +BQAwSDELMAkGA1UEBhMCQk0xGTAXBgNVBAoTEFF1b1ZhZGlzIExpbWl0ZWQxHjAc +BgNVBAMTFVF1b1ZhZGlzIFJvb3QgQ0EgMiBHMzAeFw0xMjAxMTIxODU5MzJaFw00 +MjAxMTIxODU5MzJaMEgxCzAJBgNVBAYTAkJNMRkwFwYDVQQKExBRdW9WYWRpcyBM +aW1pdGVkMR4wHAYDVQQDExVRdW9WYWRpcyBSb290IENBIDIgRzMwggIiMA0GCSqG +SIb3DQEBAQUAA4ICDwAwggIKAoICAQChriWyARjcV4g/Ruv5r+LrI3HimtFhZiFf +qq8nUeVuGxbULX1QsFN3vXg6YOJkApt8hpvWGo6t/x8Vf9WVHhLL5hSEBMHfNrMW +n4rjyduYNM7YMxcoRvynyfDStNVNCXJJ+fKH46nafaF9a7I6JaltUkSs+L5u+9ym +c5GQYaYDFCDy54ejiK2toIz/pgslUiXnFgHVy7g1gQyjO/Dh4fxaXc6AcW34Sas+ +O7q414AB+6XrW7PFXmAqMaCvN+ggOp+oMiwMzAkd056OXbxMmO7FGmh77FOm6RQ1 +o9/NgJ8MSPsc9PG/Srj61YxxSscfrf5BmrODXfKEVu+lV0POKa2Mq1W/xPtbAd0j +IaFYAI7D0GoT7RPjEiuA3GfmlbLNHiJuKvhB1PLKFAeNilUSxmn1uIZoL1NesNKq +IcGY5jDjZ1XHm26sGahVpkUG0CM62+tlXSoREfA7T8pt9DTEceT/AFr2XK4jYIVz +8eQQsSWu1ZK7E8EM4DnatDlXtas1qnIhO4M15zHfeiFuuDIIfR0ykRVKYnLP43eh +vNURG3YBZwjgQQvD6xVu+KQZ2aKrr+InUlYrAoosFCT5v0ICvybIxo/gbjh9Uy3l +7ZizlWNof/k19N+IxWA1ksB8aRxhlRbQ694Lrz4EEEVlWFA4r0jyWbYW8jwNkALG +cC4BrTwV1wIDAQABo0IwQDAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIB +BjAdBgNVHQ4EFgQU7edvdlq/YOxJW8ald7tyFnGbxD0wDQYJKoZIhvcNAQELBQAD +ggIBAJHfgD9DCX5xwvfrs4iP4VGyvD11+ShdyLyZm3tdquXK4Qr36LLTn91nMX66 +AarHakE7kNQIXLJgapDwyM4DYvmL7ftuKtwGTTwpD4kWilhMSA/ohGHqPHKmd+RC +roijQ1h5fq7KpVMNqT1wvSAZYaRsOPxDMuHBR//47PERIjKWnML2W2mWeyAMQ0Ga +W/ZZGYjeVYg3UQt4XAoeo0L9x52ID8DyeAIkVJOviYeIyUqAHerQbj5hLja7NQ4n +lv1mNDthcnPxFlxHBlRJAHpYErAK74X9sbgzdWqTHBLmYF5vHX/JHyPLhGGfHoJE ++V+tYlUkmlKY7VHnoX6XOuYvHxHaU4AshZ6rNRDbIl9qxV6XU/IyAgkwo1jwDQHV +csaxfGl7w/U2Rcxhbl5MlMVerugOXou/983g7aEOGzPuVBj+D77vfoRrQ+NwmNtd +dbINWQeFFSM51vHfqSYP1kjHs6Yi9TM3WpVHn3u6GBVv/9YUZINJ0gpnIdsPNWNg +KCLjsZWDzYWm3S8P52dSbrsvhXz1SnPnxT7AvSESBT/8twNJAlvIJebiVDj1eYeM +HVOyToV7BjjHLPj4sHKNJeV3UvQDHEimUF+IIDBu8oJDqz2XhOdT+yHBTw8imoa4 +WSr2Rz0ZiC3oheGe7IUIarFsNMkd7EgrO3jtZsSOeWmD3n+M +-----END CERTIFICATE----- + +# Issuer: CN=QuoVadis Root CA 3 G3 O=QuoVadis Limited +# Subject: CN=QuoVadis Root CA 3 G3 O=QuoVadis Limited +# Label: "QuoVadis Root CA 3 G3" +# Serial: 268090761170461462463995952157327242137089239581 +# MD5 Fingerprint: df:7d:b9:ad:54:6f:68:a1:df:89:57:03:97:43:b0:d7 +# SHA1 Fingerprint: 48:12:bd:92:3c:a8:c4:39:06:e7:30:6d:27:96:e6:a4:cf:22:2e:7d +# SHA256 Fingerprint: 88:ef:81:de:20:2e:b0:18:45:2e:43:f8:64:72:5c:ea:5f:bd:1f:c2:d9:d2:05:73:07:09:c5:d8:b8:69:0f:46 +-----BEGIN CERTIFICATE----- +MIIFYDCCA0igAwIBAgIULvWbAiin23r/1aOp7r0DoM8Sah0wDQYJKoZIhvcNAQEL +BQAwSDELMAkGA1UEBhMCQk0xGTAXBgNVBAoTEFF1b1ZhZGlzIExpbWl0ZWQxHjAc +BgNVBAMTFVF1b1ZhZGlzIFJvb3QgQ0EgMyBHMzAeFw0xMjAxMTIyMDI2MzJaFw00 +MjAxMTIyMDI2MzJaMEgxCzAJBgNVBAYTAkJNMRkwFwYDVQQKExBRdW9WYWRpcyBM +aW1pdGVkMR4wHAYDVQQDExVRdW9WYWRpcyBSb290IENBIDMgRzMwggIiMA0GCSqG +SIb3DQEBAQUAA4ICDwAwggIKAoICAQCzyw4QZ47qFJenMioKVjZ/aEzHs286IxSR +/xl/pcqs7rN2nXrpixurazHb+gtTTK/FpRp5PIpM/6zfJd5O2YIyC0TeytuMrKNu +FoM7pmRLMon7FhY4futD4tN0SsJiCnMK3UmzV9KwCoWdcTzeo8vAMvMBOSBDGzXR +U7Ox7sWTaYI+FrUoRqHe6okJ7UO4BUaKhvVZR74bbwEhELn9qdIoyhA5CcoTNs+c +ra1AdHkrAj80//ogaX3T7mH1urPnMNA3I4ZyYUUpSFlob3emLoG+B01vr87ERROR +FHAGjx+f+IdpsQ7vw4kZ6+ocYfx6bIrc1gMLnia6Et3UVDmrJqMz6nWB2i3ND0/k +A9HvFZcba5DFApCTZgIhsUfei5pKgLlVj7WiL8DWM2fafsSntARE60f75li59wzw +eyuxwHApw0BiLTtIadwjPEjrewl5qW3aqDCYz4ByA4imW0aucnl8CAMhZa634Ryl +sSqiMd5mBPfAdOhx3v89WcyWJhKLhZVXGqtrdQtEPREoPHtht+KPZ0/l7DxMYIBp +VzgeAVuNVejH38DMdyM0SXV89pgR6y3e7UEuFAUCf+D+IOs15xGsIs5XPd7JMG0Q +A4XN8f+MFrXBsj6IbGB/kE+V9/YtrQE5BwT6dYB9v0lQ7e/JxHwc64B+27bQ3RP+ +ydOc17KXqQIDAQABo0IwQDAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIB +BjAdBgNVHQ4EFgQUxhfQvKjqAkPyGwaZXSuQILnXnOQwDQYJKoZIhvcNAQELBQAD +ggIBADRh2Va1EodVTd2jNTFGu6QHcrxfYWLopfsLN7E8trP6KZ1/AvWkyaiTt3px +KGmPc+FSkNrVvjrlt3ZqVoAh313m6Tqe5T72omnHKgqwGEfcIHB9UqM+WXzBusnI +FUBhynLWcKzSt/Ac5IYp8M7vaGPQtSCKFWGafoaYtMnCdvvMujAWzKNhxnQT5Wvv +oxXqA/4Ti2Tk08HS6IT7SdEQTXlm66r99I0xHnAUrdzeZxNMgRVhvLfZkXdxGYFg +u/BYpbWcC/ePIlUnwEsBbTuZDdQdm2NnL9DuDcpmvJRPpq3t/O5jrFc/ZSXPsoaP +0Aj/uHYUbt7lJ+yreLVTubY/6CD50qi+YUbKh4yE8/nxoGibIh6BJpsQBJFxwAYf +3KDTuVan45gtf4Od34wrnDKOMpTwATwiKp9Dwi7DmDkHOHv8XgBCH/MyJnmDhPbl +8MFREsALHgQjDFSlTC9JxUrRtm5gDWv8a4uFJGS3iQ6rJUdbPM9+Sb3H6QrG2vd+ +DhcI00iX0HGS8A85PjRqHH3Y8iKuu2n0M7SmSFXRDw4m6Oy2Cy2nhTXN/VnIn9HN +PlopNLk9hM6xZdRZkZFWdSHBd575euFgndOtBBj0fOtek49TSiIp+EgrPk2GrFt/ +ywaZWWDYWGWVjUTR939+J399roD1B0y2PpxxVJkES/1Y+Zj0 +-----END CERTIFICATE----- + +# Issuer: CN=DigiCert Assured ID Root G2 O=DigiCert Inc OU=www.digicert.com +# Subject: CN=DigiCert Assured ID Root G2 O=DigiCert Inc OU=www.digicert.com +# Label: "DigiCert Assured ID Root G2" +# Serial: 15385348160840213938643033620894905419 +# MD5 Fingerprint: 92:38:b9:f8:63:24:82:65:2c:57:33:e6:fe:81:8f:9d +# SHA1 Fingerprint: a1:4b:48:d9:43:ee:0a:0e:40:90:4f:3c:e0:a4:c0:91:93:51:5d:3f +# SHA256 Fingerprint: 7d:05:eb:b6:82:33:9f:8c:94:51:ee:09:4e:eb:fe:fa:79:53:a1:14:ed:b2:f4:49:49:45:2f:ab:7d:2f:c1:85 +-----BEGIN CERTIFICATE----- +MIIDljCCAn6gAwIBAgIQC5McOtY5Z+pnI7/Dr5r0SzANBgkqhkiG9w0BAQsFADBl +MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3 +d3cuZGlnaWNlcnQuY29tMSQwIgYDVQQDExtEaWdpQ2VydCBBc3N1cmVkIElEIFJv +b3QgRzIwHhcNMTMwODAxMTIwMDAwWhcNMzgwMTE1MTIwMDAwWjBlMQswCQYDVQQG +EwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3d3cuZGlnaWNl +cnQuY29tMSQwIgYDVQQDExtEaWdpQ2VydCBBc3N1cmVkIElEIFJvb3QgRzIwggEi +MA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDZ5ygvUj82ckmIkzTz+GoeMVSA +n61UQbVH35ao1K+ALbkKz3X9iaV9JPrjIgwrvJUXCzO/GU1BBpAAvQxNEP4Htecc +biJVMWWXvdMX0h5i89vqbFCMP4QMls+3ywPgym2hFEwbid3tALBSfK+RbLE4E9Hp +EgjAALAcKxHad3A2m67OeYfcgnDmCXRwVWmvo2ifv922ebPynXApVfSr/5Vh88lA +bx3RvpO704gqu52/clpWcTs/1PPRCv4o76Pu2ZmvA9OPYLfykqGxvYmJHzDNw6Yu +YjOuFgJ3RFrngQo8p0Quebg/BLxcoIfhG69Rjs3sLPr4/m3wOnyqi+RnlTGNAgMB +AAGjQjBAMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgGGMB0GA1UdDgQW +BBTOw0q5mVXyuNtgv6l+vVa1lzan1jANBgkqhkiG9w0BAQsFAAOCAQEAyqVVjOPI +QW5pJ6d1Ee88hjZv0p3GeDgdaZaikmkuOGybfQTUiaWxMTeKySHMq2zNixya1r9I +0jJmwYrA8y8678Dj1JGG0VDjA9tzd29KOVPt3ibHtX2vK0LRdWLjSisCx1BL4Gni +lmwORGYQRI+tBev4eaymG+g3NJ1TyWGqolKvSnAWhsI6yLETcDbYz+70CjTVW0z9 +B5yiutkBclzzTcHdDrEcDcRjvq30FPuJ7KJBDkzMyFdA0G4Dqs0MjomZmWzwPDCv +ON9vvKO+KSAnq3T/EyJ43pdSVR6DtVQgA+6uwE9W3jfMw3+qBCe703e4YtsXfJwo +IhNzbM8m9Yop5w== +-----END CERTIFICATE----- + +# Issuer: CN=DigiCert Assured ID Root G3 O=DigiCert Inc OU=www.digicert.com +# Subject: CN=DigiCert Assured ID Root G3 O=DigiCert Inc OU=www.digicert.com +# Label: "DigiCert Assured ID Root G3" +# Serial: 15459312981008553731928384953135426796 +# MD5 Fingerprint: 7c:7f:65:31:0c:81:df:8d:ba:3e:99:e2:5c:ad:6e:fb +# SHA1 Fingerprint: f5:17:a2:4f:9a:48:c6:c9:f8:a2:00:26:9f:dc:0f:48:2c:ab:30:89 +# SHA256 Fingerprint: 7e:37:cb:8b:4c:47:09:0c:ab:36:55:1b:a6:f4:5d:b8:40:68:0f:ba:16:6a:95:2d:b1:00:71:7f:43:05:3f:c2 +-----BEGIN CERTIFICATE----- +MIICRjCCAc2gAwIBAgIQC6Fa+h3foLVJRK/NJKBs7DAKBggqhkjOPQQDAzBlMQsw +CQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3d3cu +ZGlnaWNlcnQuY29tMSQwIgYDVQQDExtEaWdpQ2VydCBBc3N1cmVkIElEIFJvb3Qg +RzMwHhcNMTMwODAxMTIwMDAwWhcNMzgwMTE1MTIwMDAwWjBlMQswCQYDVQQGEwJV +UzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3d3cuZGlnaWNlcnQu +Y29tMSQwIgYDVQQDExtEaWdpQ2VydCBBc3N1cmVkIElEIFJvb3QgRzMwdjAQBgcq +hkjOPQIBBgUrgQQAIgNiAAQZ57ysRGXtzbg/WPuNsVepRC0FFfLvC/8QdJ+1YlJf +Zn4f5dwbRXkLzMZTCp2NXQLZqVneAlr2lSoOjThKiknGvMYDOAdfVdp+CW7if17Q +RSAPWXYQ1qAk8C3eNvJsKTmjQjBAMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/ +BAQDAgGGMB0GA1UdDgQWBBTL0L2p4ZgFUaFNN6KDec6NHSrkhDAKBggqhkjOPQQD +AwNnADBkAjAlpIFFAmsSS3V0T8gj43DydXLefInwz5FyYZ5eEJJZVrmDxxDnOOlY +JjZ91eQ0hjkCMHw2U/Aw5WJjOpnitqM7mzT6HtoQknFekROn3aRukswy1vUhZscv +6pZjamVFkpUBtA== +-----END CERTIFICATE----- + +# Issuer: CN=DigiCert Global Root G2 O=DigiCert Inc OU=www.digicert.com +# Subject: CN=DigiCert Global Root G2 O=DigiCert Inc OU=www.digicert.com +# Label: "DigiCert Global Root G2" +# Serial: 4293743540046975378534879503202253541 +# MD5 Fingerprint: e4:a6:8a:c8:54:ac:52:42:46:0a:fd:72:48:1b:2a:44 +# SHA1 Fingerprint: df:3c:24:f9:bf:d6:66:76:1b:26:80:73:fe:06:d1:cc:8d:4f:82:a4 +# SHA256 Fingerprint: cb:3c:cb:b7:60:31:e5:e0:13:8f:8d:d3:9a:23:f9:de:47:ff:c3:5e:43:c1:14:4c:ea:27:d4:6a:5a:b1:cb:5f +-----BEGIN CERTIFICATE----- +MIIDjjCCAnagAwIBAgIQAzrx5qcRqaC7KGSxHQn65TANBgkqhkiG9w0BAQsFADBh +MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3 +d3cuZGlnaWNlcnQuY29tMSAwHgYDVQQDExdEaWdpQ2VydCBHbG9iYWwgUm9vdCBH +MjAeFw0xMzA4MDExMjAwMDBaFw0zODAxMTUxMjAwMDBaMGExCzAJBgNVBAYTAlVT +MRUwEwYDVQQKEwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5j +b20xIDAeBgNVBAMTF0RpZ2lDZXJ0IEdsb2JhbCBSb290IEcyMIIBIjANBgkqhkiG +9w0BAQEFAAOCAQ8AMIIBCgKCAQEAuzfNNNx7a8myaJCtSnX/RrohCgiN9RlUyfuI +2/Ou8jqJkTx65qsGGmvPrC3oXgkkRLpimn7Wo6h+4FR1IAWsULecYxpsMNzaHxmx +1x7e/dfgy5SDN67sH0NO3Xss0r0upS/kqbitOtSZpLYl6ZtrAGCSYP9PIUkY92eQ +q2EGnI/yuum06ZIya7XzV+hdG82MHauVBJVJ8zUtluNJbd134/tJS7SsVQepj5Wz +tCO7TG1F8PapspUwtP1MVYwnSlcUfIKdzXOS0xZKBgyMUNGPHgm+F6HmIcr9g+UQ +vIOlCsRnKPZzFBQ9RnbDhxSJITRNrw9FDKZJobq7nMWxM4MphQIDAQABo0IwQDAP +BgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIBhjAdBgNVHQ4EFgQUTiJUIBiV +5uNu5g/6+rkS7QYXjzkwDQYJKoZIhvcNAQELBQADggEBAGBnKJRvDkhj6zHd6mcY +1Yl9PMWLSn/pvtsrF9+wX3N3KjITOYFnQoQj8kVnNeyIv/iPsGEMNKSuIEyExtv4 +NeF22d+mQrvHRAiGfzZ0JFrabA0UWTW98kndth/Jsw1HKj2ZL7tcu7XUIOGZX1NG +Fdtom/DzMNU+MeKNhJ7jitralj41E6Vf8PlwUHBHQRFXGU7Aj64GxJUTFy8bJZ91 +8rGOmaFvE7FBcf6IKshPECBV1/MUReXgRPTqh5Uykw7+U0b6LJ3/iyK5S9kJRaTe +pLiaWN0bfVKfjllDiIGknibVb63dDcY3fe0Dkhvld1927jyNxF1WW6LZZm6zNTfl +MrY= +-----END CERTIFICATE----- + +# Issuer: CN=DigiCert Global Root G3 O=DigiCert Inc OU=www.digicert.com +# Subject: CN=DigiCert Global Root G3 O=DigiCert Inc OU=www.digicert.com +# Label: "DigiCert Global Root G3" +# Serial: 7089244469030293291760083333884364146 +# MD5 Fingerprint: f5:5d:a4:50:a5:fb:28:7e:1e:0f:0d:cc:96:57:56:ca +# SHA1 Fingerprint: 7e:04:de:89:6a:3e:66:6d:00:e6:87:d3:3f:fa:d9:3b:e8:3d:34:9e +# SHA256 Fingerprint: 31:ad:66:48:f8:10:41:38:c7:38:f3:9e:a4:32:01:33:39:3e:3a:18:cc:02:29:6e:f9:7c:2a:c9:ef:67:31:d0 +-----BEGIN CERTIFICATE----- +MIICPzCCAcWgAwIBAgIQBVVWvPJepDU1w6QP1atFcjAKBggqhkjOPQQDAzBhMQsw +CQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3d3cu +ZGlnaWNlcnQuY29tMSAwHgYDVQQDExdEaWdpQ2VydCBHbG9iYWwgUm9vdCBHMzAe +Fw0xMzA4MDExMjAwMDBaFw0zODAxMTUxMjAwMDBaMGExCzAJBgNVBAYTAlVTMRUw +EwYDVQQKEwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5jb20x +IDAeBgNVBAMTF0RpZ2lDZXJ0IEdsb2JhbCBSb290IEczMHYwEAYHKoZIzj0CAQYF +K4EEACIDYgAE3afZu4q4C/sLfyHS8L6+c/MzXRq8NOrexpu80JX28MzQC7phW1FG +fp4tn+6OYwwX7Adw9c+ELkCDnOg/QW07rdOkFFk2eJ0DQ+4QE2xy3q6Ip6FrtUPO +Z9wj/wMco+I+o0IwQDAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIBhjAd +BgNVHQ4EFgQUs9tIpPmhxdiuNkHMEWNpYim8S8YwCgYIKoZIzj0EAwMDaAAwZQIx +AK288mw/EkrRLTnDCgmXc/SINoyIJ7vmiI1Qhadj+Z4y3maTD/HMsQmP3Wyr+mt/ +oAIwOWZbwmSNuJ5Q3KjVSaLtx9zRSX8XAbjIho9OjIgrqJqpisXRAL34VOKa5Vt8 +sycX +-----END CERTIFICATE----- + +# Issuer: CN=DigiCert Trusted Root G4 O=DigiCert Inc OU=www.digicert.com +# Subject: CN=DigiCert Trusted Root G4 O=DigiCert Inc OU=www.digicert.com +# Label: "DigiCert Trusted Root G4" +# Serial: 7451500558977370777930084869016614236 +# MD5 Fingerprint: 78:f2:fc:aa:60:1f:2f:b4:eb:c9:37:ba:53:2e:75:49 +# SHA1 Fingerprint: dd:fb:16:cd:49:31:c9:73:a2:03:7d:3f:c8:3a:4d:7d:77:5d:05:e4 +# SHA256 Fingerprint: 55:2f:7b:dc:f1:a7:af:9e:6c:e6:72:01:7f:4f:12:ab:f7:72:40:c7:8e:76:1a:c2:03:d1:d9:d2:0a:c8:99:88 +-----BEGIN CERTIFICATE----- +MIIFkDCCA3igAwIBAgIQBZsbV56OITLiOQe9p3d1XDANBgkqhkiG9w0BAQwFADBi +MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3 +d3cuZGlnaWNlcnQuY29tMSEwHwYDVQQDExhEaWdpQ2VydCBUcnVzdGVkIFJvb3Qg +RzQwHhcNMTMwODAxMTIwMDAwWhcNMzgwMTE1MTIwMDAwWjBiMQswCQYDVQQGEwJV +UzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3d3cuZGlnaWNlcnQu +Y29tMSEwHwYDVQQDExhEaWdpQ2VydCBUcnVzdGVkIFJvb3QgRzQwggIiMA0GCSqG +SIb3DQEBAQUAA4ICDwAwggIKAoICAQC/5pBzaN675F1KPDAiMGkz7MKnJS7JIT3y +ithZwuEppz1Yq3aaza57G4QNxDAf8xukOBbrVsaXbR2rsnnyyhHS5F/WBTxSD1If +xp4VpX6+n6lXFllVcq9ok3DCsrp1mWpzMpTREEQQLt+C8weE5nQ7bXHiLQwb7iDV +ySAdYyktzuxeTsiT+CFhmzTrBcZe7FsavOvJz82sNEBfsXpm7nfISKhmV1efVFiO +DCu3T6cw2Vbuyntd463JT17lNecxy9qTXtyOj4DatpGYQJB5w3jHtrHEtWoYOAMQ +jdjUN6QuBX2I9YI+EJFwq1WCQTLX2wRzKm6RAXwhTNS8rhsDdV14Ztk6MUSaM0C/ +CNdaSaTC5qmgZ92kJ7yhTzm1EVgX9yRcRo9k98FpiHaYdj1ZXUJ2h4mXaXpI8OCi +EhtmmnTK3kse5w5jrubU75KSOp493ADkRSWJtppEGSt+wJS00mFt6zPZxd9LBADM +fRyVw4/3IbKyEbe7f/LVjHAsQWCqsWMYRJUadmJ+9oCw++hkpjPRiQfhvbfmQ6QY +uKZ3AeEPlAwhHbJUKSWJbOUOUlFHdL4mrLZBdd56rF+NP8m800ERElvlEFDrMcXK +chYiCd98THU/Y+whX8QgUWtvsauGi0/C1kVfnSD8oR7FwI+isX4KJpn15GkvmB0t +9dmpsh3lGwIDAQABo0IwQDAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIB +hjAdBgNVHQ4EFgQU7NfjgtJxXWRM3y5nP+e6mK4cD08wDQYJKoZIhvcNAQEMBQAD +ggIBALth2X2pbL4XxJEbw6GiAI3jZGgPVs93rnD5/ZpKmbnJeFwMDF/k5hQpVgs2 +SV1EY+CtnJYYZhsjDT156W1r1lT40jzBQ0CuHVD1UvyQO7uYmWlrx8GnqGikJ9yd ++SeuMIW59mdNOj6PWTkiU0TryF0Dyu1Qen1iIQqAyHNm0aAFYF/opbSnr6j3bTWc +fFqK1qI4mfN4i/RN0iAL3gTujJtHgXINwBQy7zBZLq7gcfJW5GqXb5JQbZaNaHqa +sjYUegbyJLkJEVDXCLG4iXqEI2FCKeWjzaIgQdfRnGTZ6iahixTXTBmyUEFxPT9N +cCOGDErcgdLMMpSEDQgJlxxPwO5rIHQw0uA5NBCFIRUBCOhVMt5xSdkoF1BN5r5N +0XWs0Mr7QbhDparTwwVETyw2m+L64kW4I1NsBm9nVX9GtUw/bihaeSbSpKhil9Ie +4u1Ki7wb/UdKDd9nZn6yW0HQO+T0O/QEY+nvwlQAUaCKKsnOeMzV6ocEGLPOr0mI +r/OSmbaz5mEP0oUA51Aa5BuVnRmhuZyxm7EAHu/QD09CbMkKvO5D+jpxpchNJqU1 +/YldvIViHTLSoCtU7ZpXwdv6EM8Zt4tKG48BtieVU+i2iW1bvGjUI+iLUaJW+fCm +gKDWHrO8Dw9TdSmq6hN35N6MgSGtBxBHEa2HPQfRdbzP82Z+ +-----END CERTIFICATE----- + +# Issuer: CN=COMODO RSA Certification Authority O=COMODO CA Limited +# Subject: CN=COMODO RSA Certification Authority O=COMODO CA Limited +# Label: "COMODO RSA Certification Authority" +# Serial: 101909084537582093308941363524873193117 +# MD5 Fingerprint: 1b:31:b0:71:40:36:cc:14:36:91:ad:c4:3e:fd:ec:18 +# SHA1 Fingerprint: af:e5:d2:44:a8:d1:19:42:30:ff:47:9f:e2:f8:97:bb:cd:7a:8c:b4 +# SHA256 Fingerprint: 52:f0:e1:c4:e5:8e:c6:29:29:1b:60:31:7f:07:46:71:b8:5d:7e:a8:0d:5b:07:27:34:63:53:4b:32:b4:02:34 +-----BEGIN CERTIFICATE----- +MIIF2DCCA8CgAwIBAgIQTKr5yttjb+Af907YWwOGnTANBgkqhkiG9w0BAQwFADCB +hTELMAkGA1UEBhMCR0IxGzAZBgNVBAgTEkdyZWF0ZXIgTWFuY2hlc3RlcjEQMA4G +A1UEBxMHU2FsZm9yZDEaMBgGA1UEChMRQ09NT0RPIENBIExpbWl0ZWQxKzApBgNV +BAMTIkNPTU9ETyBSU0EgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwHhcNMTAwMTE5 +MDAwMDAwWhcNMzgwMTE4MjM1OTU5WjCBhTELMAkGA1UEBhMCR0IxGzAZBgNVBAgT +EkdyZWF0ZXIgTWFuY2hlc3RlcjEQMA4GA1UEBxMHU2FsZm9yZDEaMBgGA1UEChMR +Q09NT0RPIENBIExpbWl0ZWQxKzApBgNVBAMTIkNPTU9ETyBSU0EgQ2VydGlmaWNh +dGlvbiBBdXRob3JpdHkwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQCR +6FSS0gpWsawNJN3Fz0RndJkrN6N9I3AAcbxT38T6KhKPS38QVr2fcHK3YX/JSw8X +pz3jsARh7v8Rl8f0hj4K+j5c+ZPmNHrZFGvnnLOFoIJ6dq9xkNfs/Q36nGz637CC +9BR++b7Epi9Pf5l/tfxnQ3K9DADWietrLNPtj5gcFKt+5eNu/Nio5JIk2kNrYrhV +/erBvGy2i/MOjZrkm2xpmfh4SDBF1a3hDTxFYPwyllEnvGfDyi62a+pGx8cgoLEf +Zd5ICLqkTqnyg0Y3hOvozIFIQ2dOciqbXL1MGyiKXCJ7tKuY2e7gUYPDCUZObT6Z ++pUX2nwzV0E8jVHtC7ZcryxjGt9XyD+86V3Em69FmeKjWiS0uqlWPc9vqv9JWL7w +qP/0uK3pN/u6uPQLOvnoQ0IeidiEyxPx2bvhiWC4jChWrBQdnArncevPDt09qZah +SL0896+1DSJMwBGB7FY79tOi4lu3sgQiUpWAk2nojkxl8ZEDLXB0AuqLZxUpaVIC +u9ffUGpVRr+goyhhf3DQw6KqLCGqR84onAZFdr+CGCe01a60y1Dma/RMhnEw6abf +Fobg2P9A3fvQQoh/ozM6LlweQRGBY84YcWsr7KaKtzFcOmpH4MN5WdYgGq/yapiq +crxXStJLnbsQ/LBMQeXtHT1eKJ2czL+zUdqnR+WEUwIDAQABo0IwQDAdBgNVHQ4E +FgQUu69+Aj36pvE8hI6t7jiY7NkyMtQwDgYDVR0PAQH/BAQDAgEGMA8GA1UdEwEB +/wQFMAMBAf8wDQYJKoZIhvcNAQEMBQADggIBAArx1UaEt65Ru2yyTUEUAJNMnMvl +wFTPoCWOAvn9sKIN9SCYPBMtrFaisNZ+EZLpLrqeLppysb0ZRGxhNaKatBYSaVqM +4dc+pBroLwP0rmEdEBsqpIt6xf4FpuHA1sj+nq6PK7o9mfjYcwlYRm6mnPTXJ9OV +2jeDchzTc+CiR5kDOF3VSXkAKRzH7JsgHAckaVd4sjn8OoSgtZx8jb8uk2Intzna +FxiuvTwJaP+EmzzV1gsD41eeFPfR60/IvYcjt7ZJQ3mFXLrrkguhxuhoqEwWsRqZ +CuhTLJK7oQkYdQxlqHvLI7cawiiFwxv/0Cti76R7CZGYZ4wUAc1oBmpjIXUDgIiK +boHGhfKppC3n9KUkEEeDys30jXlYsQab5xoq2Z0B15R97QNKyvDb6KkBPvVWmcke +jkk9u+UJueBPSZI9FoJAzMxZxuY67RIuaTxslbH9qh17f4a+Hg4yRvv7E491f0yL +S0Zj/gA0QHDBw7mh3aZw4gSzQbzpgJHqZJx64SIDqZxubw5lT2yHh17zbqD5daWb +QOhTsiedSrnAdyGN/4fy3ryM7xfft0kL0fJuMAsaDk527RH89elWsn2/x20Kk4yl +0MC2Hb46TpSi125sC8KKfPog88Tk5c0NqMuRkrF8hey1FGlmDoLnzc7ILaZRfyHB +NVOFBkpdn627G190 +-----END CERTIFICATE----- + +# Issuer: CN=USERTrust RSA Certification Authority O=The USERTRUST Network +# Subject: CN=USERTrust RSA Certification Authority O=The USERTRUST Network +# Label: "USERTrust RSA Certification Authority" +# Serial: 2645093764781058787591871645665788717 +# MD5 Fingerprint: 1b:fe:69:d1:91:b7:19:33:a3:72:a8:0f:e1:55:e5:b5 +# SHA1 Fingerprint: 2b:8f:1b:57:33:0d:bb:a2:d0:7a:6c:51:f7:0e:e9:0d:da:b9:ad:8e +# SHA256 Fingerprint: e7:93:c9:b0:2f:d8:aa:13:e2:1c:31:22:8a:cc:b0:81:19:64:3b:74:9c:89:89:64:b1:74:6d:46:c3:d4:cb:d2 +-----BEGIN CERTIFICATE----- +MIIF3jCCA8agAwIBAgIQAf1tMPyjylGoG7xkDjUDLTANBgkqhkiG9w0BAQwFADCB +iDELMAkGA1UEBhMCVVMxEzARBgNVBAgTCk5ldyBKZXJzZXkxFDASBgNVBAcTC0pl +cnNleSBDaXR5MR4wHAYDVQQKExVUaGUgVVNFUlRSVVNUIE5ldHdvcmsxLjAsBgNV +BAMTJVVTRVJUcnVzdCBSU0EgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwHhcNMTAw +MjAxMDAwMDAwWhcNMzgwMTE4MjM1OTU5WjCBiDELMAkGA1UEBhMCVVMxEzARBgNV +BAgTCk5ldyBKZXJzZXkxFDASBgNVBAcTC0plcnNleSBDaXR5MR4wHAYDVQQKExVU +aGUgVVNFUlRSVVNUIE5ldHdvcmsxLjAsBgNVBAMTJVVTRVJUcnVzdCBSU0EgQ2Vy +dGlmaWNhdGlvbiBBdXRob3JpdHkwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIK +AoICAQCAEmUXNg7D2wiz0KxXDXbtzSfTTK1Qg2HiqiBNCS1kCdzOiZ/MPans9s/B +3PHTsdZ7NygRK0faOca8Ohm0X6a9fZ2jY0K2dvKpOyuR+OJv0OwWIJAJPuLodMkY +tJHUYmTbf6MG8YgYapAiPLz+E/CHFHv25B+O1ORRxhFnRghRy4YUVD+8M/5+bJz/ +Fp0YvVGONaanZshyZ9shZrHUm3gDwFA66Mzw3LyeTP6vBZY1H1dat//O+T23LLb2 +VN3I5xI6Ta5MirdcmrS3ID3KfyI0rn47aGYBROcBTkZTmzNg95S+UzeQc0PzMsNT +79uq/nROacdrjGCT3sTHDN/hMq7MkztReJVni+49Vv4M0GkPGw/zJSZrM233bkf6 +c0Plfg6lZrEpfDKEY1WJxA3Bk1QwGROs0303p+tdOmw1XNtB1xLaqUkL39iAigmT +Yo61Zs8liM2EuLE/pDkP2QKe6xJMlXzzawWpXhaDzLhn4ugTncxbgtNMs+1b/97l +c6wjOy0AvzVVdAlJ2ElYGn+SNuZRkg7zJn0cTRe8yexDJtC/QV9AqURE9JnnV4ee +UB9XVKg+/XRjL7FQZQnmWEIuQxpMtPAlR1n6BB6T1CZGSlCBst6+eLf8ZxXhyVeE +Hg9j1uliutZfVS7qXMYoCAQlObgOK6nyTJccBz8NUvXt7y+CDwIDAQABo0IwQDAd +BgNVHQ4EFgQUU3m/WqorSs9UgOHYm8Cd8rIDZsswDgYDVR0PAQH/BAQDAgEGMA8G +A1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQEMBQADggIBAFzUfA3P9wF9QZllDHPF +Up/L+M+ZBn8b2kMVn54CVVeWFPFSPCeHlCjtHzoBN6J2/FNQwISbxmtOuowhT6KO +VWKR82kV2LyI48SqC/3vqOlLVSoGIG1VeCkZ7l8wXEskEVX/JJpuXior7gtNn3/3 +ATiUFJVDBwn7YKnuHKsSjKCaXqeYalltiz8I+8jRRa8YFWSQEg9zKC7F4iRO/Fjs +8PRF/iKz6y+O0tlFYQXBl2+odnKPi4w2r78NBc5xjeambx9spnFixdjQg3IM8WcR +iQycE0xyNN+81XHfqnHd4blsjDwSXWXavVcStkNr/+XeTWYRUc+ZruwXtuhxkYze +Sf7dNXGiFSeUHM9h4ya7b6NnJSFd5t0dCy5oGzuCr+yDZ4XUmFF0sbmZgIn/f3gZ +XHlKYC6SQK5MNyosycdiyA5d9zZbyuAlJQG03RoHnHcAP9Dc1ew91Pq7P8yF1m9/ +qS3fuQL39ZeatTXaw2ewh0qpKJ4jjv9cJ2vhsE/zB+4ALtRZh8tSQZXq9EfX7mRB +VXyNWQKV3WKdwrnuWih0hKWbt5DHDAff9Yk2dDLWKMGwsAvgnEzDHNb842m1R0aB +L6KCq9NjRHDEjf8tM7qtj3u1cIiuPhnPQCjY/MiQu12ZIvVS5ljFH4gxQ+6IHdfG +jjxDah2nGN59PRbxYvnKkKj9 +-----END CERTIFICATE----- + +# Issuer: CN=USERTrust ECC Certification Authority O=The USERTRUST Network +# Subject: CN=USERTrust ECC Certification Authority O=The USERTRUST Network +# Label: "USERTrust ECC Certification Authority" +# Serial: 123013823720199481456569720443997572134 +# MD5 Fingerprint: fa:68:bc:d9:b5:7f:ad:fd:c9:1d:06:83:28:cc:24:c1 +# SHA1 Fingerprint: d1:cb:ca:5d:b2:d5:2a:7f:69:3b:67:4d:e5:f0:5a:1d:0c:95:7d:f0 +# SHA256 Fingerprint: 4f:f4:60:d5:4b:9c:86:da:bf:bc:fc:57:12:e0:40:0d:2b:ed:3f:bc:4d:4f:bd:aa:86:e0:6a:dc:d2:a9:ad:7a +-----BEGIN CERTIFICATE----- +MIICjzCCAhWgAwIBAgIQXIuZxVqUxdJxVt7NiYDMJjAKBggqhkjOPQQDAzCBiDEL +MAkGA1UEBhMCVVMxEzARBgNVBAgTCk5ldyBKZXJzZXkxFDASBgNVBAcTC0plcnNl +eSBDaXR5MR4wHAYDVQQKExVUaGUgVVNFUlRSVVNUIE5ldHdvcmsxLjAsBgNVBAMT +JVVTRVJUcnVzdCBFQ0MgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwHhcNMTAwMjAx +MDAwMDAwWhcNMzgwMTE4MjM1OTU5WjCBiDELMAkGA1UEBhMCVVMxEzARBgNVBAgT +Ck5ldyBKZXJzZXkxFDASBgNVBAcTC0plcnNleSBDaXR5MR4wHAYDVQQKExVUaGUg +VVNFUlRSVVNUIE5ldHdvcmsxLjAsBgNVBAMTJVVTRVJUcnVzdCBFQ0MgQ2VydGlm +aWNhdGlvbiBBdXRob3JpdHkwdjAQBgcqhkjOPQIBBgUrgQQAIgNiAAQarFRaqflo +I+d61SRvU8Za2EurxtW20eZzca7dnNYMYf3boIkDuAUU7FfO7l0/4iGzzvfUinng +o4N+LZfQYcTxmdwlkWOrfzCjtHDix6EznPO/LlxTsV+zfTJ/ijTjeXmjQjBAMB0G +A1UdDgQWBBQ64QmG1M8ZwpZ2dEl23OA1xmNjmjAOBgNVHQ8BAf8EBAMCAQYwDwYD +VR0TAQH/BAUwAwEB/zAKBggqhkjOPQQDAwNoADBlAjA2Z6EWCNzklwBBHU6+4WMB +zzuqQhFkoJ2UOQIReVx7Hfpkue4WQrO/isIJxOzksU0CMQDpKmFHjFJKS04YcPbW +RNZu9YO6bVi9JNlWSOrvxKJGgYhqOkbRqZtNyWHa0V1Xahg= +-----END CERTIFICATE----- + +# Issuer: CN=GlobalSign O=GlobalSign OU=GlobalSign ECC Root CA - R4 +# Subject: CN=GlobalSign O=GlobalSign OU=GlobalSign ECC Root CA - R4 +# Label: "GlobalSign ECC Root CA - R4" +# Serial: 14367148294922964480859022125800977897474 +# MD5 Fingerprint: 20:f0:27:68:d1:7e:a0:9d:0e:e6:2a:ca:df:5c:89:8e +# SHA1 Fingerprint: 69:69:56:2e:40:80:f4:24:a1:e7:19:9f:14:ba:f3:ee:58:ab:6a:bb +# SHA256 Fingerprint: be:c9:49:11:c2:95:56:76:db:6c:0a:55:09:86:d7:6e:3b:a0:05:66:7c:44:2c:97:62:b4:fb:b7:73:de:22:8c +-----BEGIN CERTIFICATE----- +MIIB4TCCAYegAwIBAgIRKjikHJYKBN5CsiilC+g0mAIwCgYIKoZIzj0EAwIwUDEk +MCIGA1UECxMbR2xvYmFsU2lnbiBFQ0MgUm9vdCBDQSAtIFI0MRMwEQYDVQQKEwpH +bG9iYWxTaWduMRMwEQYDVQQDEwpHbG9iYWxTaWduMB4XDTEyMTExMzAwMDAwMFoX +DTM4MDExOTAzMTQwN1owUDEkMCIGA1UECxMbR2xvYmFsU2lnbiBFQ0MgUm9vdCBD +QSAtIFI0MRMwEQYDVQQKEwpHbG9iYWxTaWduMRMwEQYDVQQDEwpHbG9iYWxTaWdu +MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEuMZ5049sJQ6fLjkZHAOkrprlOQcJ +FspjsbmG+IpXwVfOQvpzofdlQv8ewQCybnMO/8ch5RikqtlxP6jUuc6MHaNCMEAw +DgYDVR0PAQH/BAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFFSwe61F +uOJAf/sKbvu+M8k8o4TVMAoGCCqGSM49BAMCA0gAMEUCIQDckqGgE6bPA7DmxCGX +kPoUVy0D7O48027KqGx2vKLeuwIgJ6iFJzWbVsaj8kfSt24bAgAXqmemFZHe+pTs +ewv4n4Q= +-----END CERTIFICATE----- + +# Issuer: CN=GlobalSign O=GlobalSign OU=GlobalSign ECC Root CA - R5 +# Subject: CN=GlobalSign O=GlobalSign OU=GlobalSign ECC Root CA - R5 +# Label: "GlobalSign ECC Root CA - R5" +# Serial: 32785792099990507226680698011560947931244 +# MD5 Fingerprint: 9f:ad:3b:1c:02:1e:8a:ba:17:74:38:81:0c:a2:bc:08 +# SHA1 Fingerprint: 1f:24:c6:30:cd:a4:18:ef:20:69:ff:ad:4f:dd:5f:46:3a:1b:69:aa +# SHA256 Fingerprint: 17:9f:bc:14:8a:3d:d0:0f:d2:4e:a1:34:58:cc:43:bf:a7:f5:9c:81:82:d7:83:a5:13:f6:eb:ec:10:0c:89:24 +-----BEGIN CERTIFICATE----- +MIICHjCCAaSgAwIBAgIRYFlJ4CYuu1X5CneKcflK2GwwCgYIKoZIzj0EAwMwUDEk +MCIGA1UECxMbR2xvYmFsU2lnbiBFQ0MgUm9vdCBDQSAtIFI1MRMwEQYDVQQKEwpH +bG9iYWxTaWduMRMwEQYDVQQDEwpHbG9iYWxTaWduMB4XDTEyMTExMzAwMDAwMFoX +DTM4MDExOTAzMTQwN1owUDEkMCIGA1UECxMbR2xvYmFsU2lnbiBFQ0MgUm9vdCBD +QSAtIFI1MRMwEQYDVQQKEwpHbG9iYWxTaWduMRMwEQYDVQQDEwpHbG9iYWxTaWdu +MHYwEAYHKoZIzj0CAQYFK4EEACIDYgAER0UOlvt9Xb/pOdEh+J8LttV7HpI6SFkc +8GIxLcB6KP4ap1yztsyX50XUWPrRd21DosCHZTQKH3rd6zwzocWdTaRvQZU4f8ke +hOvRnkmSh5SHDDqFSmafnVmTTZdhBoZKo0IwQDAOBgNVHQ8BAf8EBAMCAQYwDwYD +VR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUPeYpSJvqB8ohREom3m7e0oPQn1kwCgYI +KoZIzj0EAwMDaAAwZQIxAOVpEslu28YxuglB4Zf4+/2a4n0Sye18ZNPLBSWLVtmg +515dTguDnFt2KaAJJiFqYgIwcdK1j1zqO+F4CYWodZI7yFz9SO8NdCKoCOJuxUnO +xwy8p2Fp8fc74SrL+SvzZpA3 +-----END CERTIFICATE----- + +# Issuer: CN=Staat der Nederlanden Root CA - G3 O=Staat der Nederlanden +# Subject: CN=Staat der Nederlanden Root CA - G3 O=Staat der Nederlanden +# Label: "Staat der Nederlanden Root CA - G3" +# Serial: 10003001 +# MD5 Fingerprint: 0b:46:67:07:db:10:2f:19:8c:35:50:60:d1:0b:f4:37 +# SHA1 Fingerprint: d8:eb:6b:41:51:92:59:e0:f3:e7:85:00:c0:3d:b6:88:97:c9:ee:fc +# SHA256 Fingerprint: 3c:4f:b0:b9:5a:b8:b3:00:32:f4:32:b8:6f:53:5f:e1:72:c1:85:d0:fd:39:86:58:37:cf:36:18:7f:a6:f4:28 +-----BEGIN CERTIFICATE----- +MIIFdDCCA1ygAwIBAgIEAJiiOTANBgkqhkiG9w0BAQsFADBaMQswCQYDVQQGEwJO +TDEeMBwGA1UECgwVU3RhYXQgZGVyIE5lZGVybGFuZGVuMSswKQYDVQQDDCJTdGFh +dCBkZXIgTmVkZXJsYW5kZW4gUm9vdCBDQSAtIEczMB4XDTEzMTExNDExMjg0MloX +DTI4MTExMzIzMDAwMFowWjELMAkGA1UEBhMCTkwxHjAcBgNVBAoMFVN0YWF0IGRl +ciBOZWRlcmxhbmRlbjErMCkGA1UEAwwiU3RhYXQgZGVyIE5lZGVybGFuZGVuIFJv +b3QgQ0EgLSBHMzCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAL4yolQP +cPssXFnrbMSkUeiFKrPMSjTysF/zDsccPVMeiAho2G89rcKezIJnByeHaHE6n3WW +IkYFsO2tx1ueKt6c/DrGlaf1F2cY5y9JCAxcz+bMNO14+1Cx3Gsy8KL+tjzk7FqX +xz8ecAgwoNzFs21v0IJyEavSgWhZghe3eJJg+szeP4TrjTgzkApyI/o1zCZxMdFy +KJLZWyNtZrVtB0LrpjPOktvA9mxjeM3KTj215VKb8b475lRgsGYeCasH/lSJEULR +9yS6YHgamPfJEf0WwTUaVHXvQ9Plrk7O53vDxk5hUUurmkVLoR9BvUhTFXFkC4az +5S6+zqQbwSmEorXLCCN2QyIkHxcE1G6cxvx/K2Ya7Irl1s9N9WMJtxU51nus6+N8 +6U78dULI7ViVDAZCopz35HCz33JvWjdAidiFpNfxC95DGdRKWCyMijmev4SH8RY7 +Ngzp07TKbBlBUgmhHbBqv4LvcFEhMtwFdozL92TkA1CvjJFnq8Xy7ljY3r735zHP +bMk7ccHViLVlvMDoFxcHErVc0qsgk7TmgoNwNsXNo42ti+yjwUOH5kPiNL6VizXt +BznaqB16nzaeErAMZRKQFWDZJkBE41ZgpRDUajz9QdwOWke275dhdU/Z/seyHdTt +XUmzqWrLZoQT1Vyg3N9udwbRcXXIV2+vD3dbAgMBAAGjQjBAMA8GA1UdEwEB/wQF +MAMBAf8wDgYDVR0PAQH/BAQDAgEGMB0GA1UdDgQWBBRUrfrHkleuyjWcLhL75Lpd +INyUVzANBgkqhkiG9w0BAQsFAAOCAgEAMJmdBTLIXg47mAE6iqTnB/d6+Oea31BD +U5cqPco8R5gu4RV78ZLzYdqQJRZlwJ9UXQ4DO1t3ApyEtg2YXzTdO2PCwyiBwpwp +LiniyMMB8jPqKqrMCQj3ZWfGzd/TtiunvczRDnBfuCPRy5FOCvTIeuXZYzbB1N/8 +Ipf3YF3qKS9Ysr1YvY2WTxB1v0h7PVGHoTx0IsL8B3+A3MSs/mrBcDCw6Y5p4ixp +gZQJut3+TcCDjJRYwEYgr5wfAvg1VUkvRtTA8KCWAg8zxXHzniN9lLf9OtMJgwYh +/WA9rjLA0u6NpvDntIJ8CsxwyXmA+P5M9zWEGYox+wrZ13+b8KKaa8MFSu1BYBQw +0aoRQm7TIwIEC8Zl3d1Sd9qBa7Ko+gE4uZbqKmxnl4mUnrzhVNXkanjvSr0rmj1A +fsbAddJu+2gw7OyLnflJNZoaLNmzlTnVHpL3prllL+U9bTpITAjc5CgSKL59NVzq +4BZ+Extq1z7XnvwtdbLBFNUjA9tbbws+eC8N3jONFrdI54OagQ97wUNNVQQXOEpR +1VmiiXTTn74eS9fGbbeIJG9gkaSChVtWQbzQRKtqE77RLFi3EjNYsjdj3BP1lB0/ +QFH1T/U67cjF68IeHRaVesd+QnGTbksVtzDfqu1XhUisHWrdOWnk4Xl4vs4Fv6EM +94B7IWcnMFk= +-----END CERTIFICATE----- + +# Issuer: CN=Staat der Nederlanden EV Root CA O=Staat der Nederlanden +# Subject: CN=Staat der Nederlanden EV Root CA O=Staat der Nederlanden +# Label: "Staat der Nederlanden EV Root CA" +# Serial: 10000013 +# MD5 Fingerprint: fc:06:af:7b:e8:1a:f1:9a:b4:e8:d2:70:1f:c0:f5:ba +# SHA1 Fingerprint: 76:e2:7e:c1:4f:db:82:c1:c0:a6:75:b5:05:be:3d:29:b4:ed:db:bb +# SHA256 Fingerprint: 4d:24:91:41:4c:fe:95:67:46:ec:4c:ef:a6:cf:6f:72:e2:8a:13:29:43:2f:9d:8a:90:7a:c4:cb:5d:ad:c1:5a +-----BEGIN CERTIFICATE----- +MIIFcDCCA1igAwIBAgIEAJiWjTANBgkqhkiG9w0BAQsFADBYMQswCQYDVQQGEwJO +TDEeMBwGA1UECgwVU3RhYXQgZGVyIE5lZGVybGFuZGVuMSkwJwYDVQQDDCBTdGFh +dCBkZXIgTmVkZXJsYW5kZW4gRVYgUm9vdCBDQTAeFw0xMDEyMDgxMTE5MjlaFw0y +MjEyMDgxMTEwMjhaMFgxCzAJBgNVBAYTAk5MMR4wHAYDVQQKDBVTdGFhdCBkZXIg +TmVkZXJsYW5kZW4xKTAnBgNVBAMMIFN0YWF0IGRlciBOZWRlcmxhbmRlbiBFViBS +b290IENBMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEA48d+ifkkSzrS +M4M1LGns3Amk41GoJSt5uAg94JG6hIXGhaTK5skuU6TJJB79VWZxXSzFYGgEt9nC +UiY4iKTWO0Cmws0/zZiTs1QUWJZV1VD+hq2kY39ch/aO5ieSZxeSAgMs3NZmdO3d +Z//BYY1jTw+bbRcwJu+r0h8QoPnFfxZpgQNH7R5ojXKhTbImxrpsX23Wr9GxE46p +rfNeaXUmGD5BKyF/7otdBwadQ8QpCiv8Kj6GyzyDOvnJDdrFmeK8eEEzduG/L13l +pJhQDBXd4Pqcfzho0LKmeqfRMb1+ilgnQ7O6M5HTp5gVXJrm0w912fxBmJc+qiXb +j5IusHsMX/FjqTf5m3VpTCgmJdrV8hJwRVXj33NeN/UhbJCONVrJ0yPr08C+eKxC +KFhmpUZtcALXEPlLVPxdhkqHz3/KRawRWrUgUY0viEeXOcDPusBCAUCZSCELa6fS +/ZbV0b5GnUngC6agIk440ME8MLxwjyx1zNDFjFE7PZQIZCZhfbnDZY8UnCHQqv0X +cgOPvZuM5l5Tnrmd74K74bzickFbIZTTRTeU0d8JOV3nI6qaHcptqAqGhYqCvkIH +1vI4gnPah1vlPNOePqc7nvQDs/nxfRN0Av+7oeX6AHkcpmZBiFxgV6YuCcS6/ZrP +px9Aw7vMWgpVSzs4dlG4Y4uElBbmVvMCAwEAAaNCMEAwDwYDVR0TAQH/BAUwAwEB +/zAOBgNVHQ8BAf8EBAMCAQYwHQYDVR0OBBYEFP6rAJCYniT8qcwaivsnuL8wbqg7 +MA0GCSqGSIb3DQEBCwUAA4ICAQDPdyxuVr5Os7aEAJSrR8kN0nbHhp8dB9O2tLsI +eK9p0gtJ3jPFrK3CiAJ9Brc1AsFgyb/E6JTe1NOpEyVa/m6irn0F3H3zbPB+po3u +2dfOWBfoqSmuc0iH55vKbimhZF8ZE/euBhD/UcabTVUlT5OZEAFTdfETzsemQUHS +v4ilf0X8rLiltTMMgsT7B/Zq5SWEXwbKwYY5EdtYzXc7LMJMD16a4/CrPmEbUCTC +wPTxGfARKbalGAKb12NMcIxHowNDXLldRqANb/9Zjr7dn3LDWyvfjFvO5QxGbJKy +CqNMVEIYFRIYvdr8unRu/8G2oGTYqV9Vrp9canaW2HNnh/tNf1zuacpzEPuKqf2e +vTY4SUmH9A4U8OmHuD+nT3pajnnUk+S7aFKErGzp85hwVXIy+TSrK0m1zSBi5Dp6 +Z2Orltxtrpfs/J92VoguZs9btsmksNcFuuEnL5O7Jiqik7Ab846+HUCjuTaPPoIa +Gl6I6lD4WeKDRikL40Rc4ZW2aZCaFG+XroHPaO+Zmr615+F/+PoTRxZMzG0IQOeL +eG9QgkRQP2YGiqtDhFZKDyAthg710tvSeopLzaXoTvFeJiUBWSOgftL2fiFX1ye8 +FVdMpEbB4IMeDExNH08GGeL5qPQ6gqGyeUN51q1veieQA6TqJIc/2b3Z6fJfUEkc +7uzXLg== +-----END CERTIFICATE----- + +# Issuer: CN=IdenTrust Commercial Root CA 1 O=IdenTrust +# Subject: CN=IdenTrust Commercial Root CA 1 O=IdenTrust +# Label: "IdenTrust Commercial Root CA 1" +# Serial: 13298821034946342390520003877796839426 +# MD5 Fingerprint: b3:3e:77:73:75:ee:a0:d3:e3:7e:49:63:49:59:bb:c7 +# SHA1 Fingerprint: df:71:7e:aa:4a:d9:4e:c9:55:84:99:60:2d:48:de:5f:bc:f0:3a:25 +# SHA256 Fingerprint: 5d:56:49:9b:e4:d2:e0:8b:cf:ca:d0:8a:3e:38:72:3d:50:50:3b:de:70:69:48:e4:2f:55:60:30:19:e5:28:ae +-----BEGIN CERTIFICATE----- +MIIFYDCCA0igAwIBAgIQCgFCgAAAAUUjyES1AAAAAjANBgkqhkiG9w0BAQsFADBK +MQswCQYDVQQGEwJVUzESMBAGA1UEChMJSWRlblRydXN0MScwJQYDVQQDEx5JZGVu +VHJ1c3QgQ29tbWVyY2lhbCBSb290IENBIDEwHhcNMTQwMTE2MTgxMjIzWhcNMzQw +MTE2MTgxMjIzWjBKMQswCQYDVQQGEwJVUzESMBAGA1UEChMJSWRlblRydXN0MScw +JQYDVQQDEx5JZGVuVHJ1c3QgQ29tbWVyY2lhbCBSb290IENBIDEwggIiMA0GCSqG +SIb3DQEBAQUAA4ICDwAwggIKAoICAQCnUBneP5k91DNG8W9RYYKyqU+PZ4ldhNlT +3Qwo2dfw/66VQ3KZ+bVdfIrBQuExUHTRgQ18zZshq0PirK1ehm7zCYofWjK9ouuU ++ehcCuz/mNKvcbO0U59Oh++SvL3sTzIwiEsXXlfEU8L2ApeN2WIrvyQfYo3fw7gp +S0l4PJNgiCL8mdo2yMKi1CxUAGc1bnO/AljwpN3lsKImesrgNqUZFvX9t++uP0D1 +bVoE/c40yiTcdCMbXTMTEl3EASX2MN0CXZ/g1Ue9tOsbobtJSdifWwLziuQkkORi +T0/Br4sOdBeo0XKIanoBScy0RnnGF7HamB4HWfp1IYVl3ZBWzvurpWCdxJ35UrCL +vYf5jysjCiN2O/cz4ckA82n5S6LgTrx+kzmEB/dEcH7+B1rlsazRGMzyNeVJSQjK +Vsk9+w8YfYs7wRPCTY/JTw436R+hDmrfYi7LNQZReSzIJTj0+kuniVyc0uMNOYZK +dHzVWYfCP04MXFL0PfdSgvHqo6z9STQaKPNBiDoT7uje/5kdX7rL6B7yuVBgwDHT +c+XvvqDtMwt0viAgxGds8AgDelWAf0ZOlqf0Hj7h9tgJ4TNkK2PXMl6f+cB7D3hv +l7yTmvmcEpB4eoCHFddydJxVdHixuuFucAS6T6C6aMN7/zHwcz09lCqxC0EOoP5N +iGVreTO01wIDAQABo0IwQDAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB +/zAdBgNVHQ4EFgQU7UQZwNPwBovupHu+QucmVMiONnYwDQYJKoZIhvcNAQELBQAD +ggIBAA2ukDL2pkt8RHYZYR4nKM1eVO8lvOMIkPkp165oCOGUAFjvLi5+U1KMtlwH +6oi6mYtQlNeCgN9hCQCTrQ0U5s7B8jeUeLBfnLOic7iPBZM4zY0+sLj7wM+x8uwt +LRvM7Kqas6pgghstO8OEPVeKlh6cdbjTMM1gCIOQ045U8U1mwF10A0Cj7oV+wh93 +nAbowacYXVKV7cndJZ5t+qntozo00Fl72u1Q8zW/7esUTTHHYPTa8Yec4kjixsU3 ++wYQ+nVZZjFHKdp2mhzpgq7vmrlR94gjmmmVYjzlVYA211QC//G5Xc7UI2/YRYRK +W2XviQzdFKcgyxilJbQN+QHwotL0AMh0jqEqSI5l2xPE4iUXfeu+h1sXIFRRk0pT +AwvsXcoz7WL9RccvW9xYoIA55vrX/hMUpu09lEpCdNTDd1lzzY9GvlU47/rokTLq +l1gEIt44w8y8bckzOmoKaT+gyOpyj4xjhiO9bTyWnpXgSUyqorkqG5w2gXjtw+hG +4iZZRHUe2XWJUc0QhJ1hYMtd+ZciTY6Y5uN/9lu7rs3KSoFrXgvzUeF0K+l+J6fZ +mUlO+KWA2yUPHGNiiskzZ2s8EIPGrd6ozRaOjfAHN3Gf8qv8QfXBi+wAN10J5U6A +7/qxXDgGpRtK4dw4LTzcqx+QGtVKnO7RcGzM7vRX+Bi6hG6H +-----END CERTIFICATE----- + +# Issuer: CN=IdenTrust Public Sector Root CA 1 O=IdenTrust +# Subject: CN=IdenTrust Public Sector Root CA 1 O=IdenTrust +# Label: "IdenTrust Public Sector Root CA 1" +# Serial: 13298821034946342390521976156843933698 +# MD5 Fingerprint: 37:06:a5:b0:fc:89:9d:ba:f4:6b:8c:1a:64:cd:d5:ba +# SHA1 Fingerprint: ba:29:41:60:77:98:3f:f4:f3:ef:f2:31:05:3b:2e:ea:6d:4d:45:fd +# SHA256 Fingerprint: 30:d0:89:5a:9a:44:8a:26:20:91:63:55:22:d1:f5:20:10:b5:86:7a:ca:e1:2c:78:ef:95:8f:d4:f4:38:9f:2f +-----BEGIN CERTIFICATE----- +MIIFZjCCA06gAwIBAgIQCgFCgAAAAUUjz0Z8AAAAAjANBgkqhkiG9w0BAQsFADBN +MQswCQYDVQQGEwJVUzESMBAGA1UEChMJSWRlblRydXN0MSowKAYDVQQDEyFJZGVu +VHJ1c3QgUHVibGljIFNlY3RvciBSb290IENBIDEwHhcNMTQwMTE2MTc1MzMyWhcN +MzQwMTE2MTc1MzMyWjBNMQswCQYDVQQGEwJVUzESMBAGA1UEChMJSWRlblRydXN0 +MSowKAYDVQQDEyFJZGVuVHJ1c3QgUHVibGljIFNlY3RvciBSb290IENBIDEwggIi +MA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQC2IpT8pEiv6EdrCvsnduTyP4o7 +ekosMSqMjbCpwzFrqHd2hCa2rIFCDQjrVVi7evi8ZX3yoG2LqEfpYnYeEe4IFNGy +RBb06tD6Hi9e28tzQa68ALBKK0CyrOE7S8ItneShm+waOh7wCLPQ5CQ1B5+ctMlS +bdsHyo+1W/CD80/HLaXIrcuVIKQxKFdYWuSNG5qrng0M8gozOSI5Cpcu81N3uURF +/YTLNiCBWS2ab21ISGHKTN9T0a9SvESfqy9rg3LvdYDaBjMbXcjaY8ZNzaxmMc3R +3j6HEDbhuaR672BQssvKplbgN6+rNBM5Jeg5ZuSYeqoSmJxZZoY+rfGwyj4GD3vw +EUs3oERte8uojHH01bWRNszwFcYr3lEXsZdMUD2xlVl8BX0tIdUAvwFnol57plzy +9yLxkA2T26pEUWbMfXYD62qoKjgZl3YNa4ph+bz27nb9cCvdKTz4Ch5bQhyLVi9V +GxyhLrXHFub4qjySjmm2AcG1hp2JDws4lFTo6tyePSW8Uybt1as5qsVATFSrsrTZ +2fjXctscvG29ZV/viDUqZi/u9rNl8DONfJhBaUYPQxxp+pu10GFqzcpL2UyQRqsV +WaFHVCkugyhfHMKiq3IXAAaOReyL4jM9f9oZRORicsPfIsbyVtTdX5Vy7W1f90gD +W/3FKqD2cyOEEBsB5wIDAQABo0IwQDAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/ +BAUwAwEB/zAdBgNVHQ4EFgQU43HgntinQtnbcZFrlJPrw6PRFKMwDQYJKoZIhvcN +AQELBQADggIBAEf63QqwEZE4rU1d9+UOl1QZgkiHVIyqZJnYWv6IAcVYpZmxI1Qj +t2odIFflAWJBF9MJ23XLblSQdf4an4EKwt3X9wnQW3IV5B4Jaj0z8yGa5hV+rVHV +DRDtfULAj+7AmgjVQdZcDiFpboBhDhXAuM/FSRJSzL46zNQuOAXeNf0fb7iAaJg9 +TaDKQGXSc3z1i9kKlT/YPyNtGtEqJBnZhbMX73huqVjRI9PHE+1yJX9dsXNw0H8G +lwmEKYBhHfpe/3OsoOOJuBxxFcbeMX8S3OFtm6/n6J91eEyrRjuazr8FGF1NFTwW +mhlQBJqymm9li1JfPFgEKCXAZmExfrngdbkaqIHWchezxQMxNRF4eKLg6TCMf4Df +WN88uieW4oA0beOY02QnrEh+KHdcxiVhJfiFDGX6xDIvpZgF5PgLZxYWxoK4Mhn5 ++bl53B/N66+rDt0b20XkeucC4pVd/GnwU2lhlXV5C15V5jgclKlZM57IcXR5f1GJ +tshquDDIajjDbp7hNxbqBWJMWxJH7ae0s1hWx0nzfxJoCTFx8G34Tkf71oXuxVhA +GaQdp/lLQzfcaFpPz+vCZHTetBXZ9FRUGi8c15dxVJCO2SCdUyt/q4/i6jC8UDfv +8Ue1fXwsBOxonbRJRBD0ckscZOf85muQ3Wl9af0AVqW3rLatt8o+Ae+c +-----END CERTIFICATE----- + +# Issuer: CN=Entrust Root Certification Authority - G2 O=Entrust, Inc. OU=See www.entrust.net/legal-terms/(c) 2009 Entrust, Inc. - for authorized use only +# Subject: CN=Entrust Root Certification Authority - G2 O=Entrust, Inc. OU=See www.entrust.net/legal-terms/(c) 2009 Entrust, Inc. - for authorized use only +# Label: "Entrust Root Certification Authority - G2" +# Serial: 1246989352 +# MD5 Fingerprint: 4b:e2:c9:91:96:65:0c:f4:0e:5a:93:92:a0:0a:fe:b2 +# SHA1 Fingerprint: 8c:f4:27:fd:79:0c:3a:d1:66:06:8d:e8:1e:57:ef:bb:93:22:72:d4 +# SHA256 Fingerprint: 43:df:57:74:b0:3e:7f:ef:5f:e4:0d:93:1a:7b:ed:f1:bb:2e:6b:42:73:8c:4e:6d:38:41:10:3d:3a:a7:f3:39 +-----BEGIN CERTIFICATE----- +MIIEPjCCAyagAwIBAgIESlOMKDANBgkqhkiG9w0BAQsFADCBvjELMAkGA1UEBhMC +VVMxFjAUBgNVBAoTDUVudHJ1c3QsIEluYy4xKDAmBgNVBAsTH1NlZSB3d3cuZW50 +cnVzdC5uZXQvbGVnYWwtdGVybXMxOTA3BgNVBAsTMChjKSAyMDA5IEVudHJ1c3Qs +IEluYy4gLSBmb3IgYXV0aG9yaXplZCB1c2Ugb25seTEyMDAGA1UEAxMpRW50cnVz +dCBSb290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5IC0gRzIwHhcNMDkwNzA3MTcy +NTU0WhcNMzAxMjA3MTc1NTU0WjCBvjELMAkGA1UEBhMCVVMxFjAUBgNVBAoTDUVu +dHJ1c3QsIEluYy4xKDAmBgNVBAsTH1NlZSB3d3cuZW50cnVzdC5uZXQvbGVnYWwt +dGVybXMxOTA3BgNVBAsTMChjKSAyMDA5IEVudHJ1c3QsIEluYy4gLSBmb3IgYXV0 +aG9yaXplZCB1c2Ugb25seTEyMDAGA1UEAxMpRW50cnVzdCBSb290IENlcnRpZmlj +YXRpb24gQXV0aG9yaXR5IC0gRzIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEK +AoIBAQC6hLZy254Ma+KZ6TABp3bqMriVQRrJ2mFOWHLP/vaCeb9zYQYKpSfYs1/T +RU4cctZOMvJyig/3gxnQaoCAAEUesMfnmr8SVycco2gvCoe9amsOXmXzHHfV1IWN +cCG0szLni6LVhjkCsbjSR87kyUnEO6fe+1R9V77w6G7CebI6C1XiUJgWMhNcL3hW +wcKUs/Ja5CeanyTXxuzQmyWC48zCxEXFjJd6BmsqEZ+pCm5IO2/b1BEZQvePB7/1 +U1+cPvQXLOZprE4yTGJ36rfo5bs0vBmLrpxR57d+tVOxMyLlbc9wPBr64ptntoP0 +jaWvYkxN4FisZDQSA/i2jZRjJKRxAgMBAAGjQjBAMA4GA1UdDwEB/wQEAwIBBjAP +BgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBRqciZ60B7vfec7aVHUbI2fkBJmqzAN +BgkqhkiG9w0BAQsFAAOCAQEAeZ8dlsa2eT8ijYfThwMEYGprmi5ZiXMRrEPR9RP/ +jTkrwPK9T3CMqS/qF8QLVJ7UG5aYMzyorWKiAHarWWluBh1+xLlEjZivEtRh2woZ +Rkfz6/djwUAFQKXSt/S1mja/qYh2iARVBCuch38aNzx+LaUa2NSJXsq9rD1s2G2v +1fN2D807iDginWyTmsQ9v4IbZT+mD12q/OWyFcq1rca8PdCE6OoGcrBNOTJ4vz4R +nAuknZoh8/CbCzB428Hch0P+vGOaysXCHMnHjf87ElgI5rY97HosTvuDls4MPGmH +VHOkc8KT/1EQrBVUAdj8BbGJoX90g5pJ19xOe4pIb4tF9g== +-----END CERTIFICATE----- + +# Issuer: CN=Entrust Root Certification Authority - EC1 O=Entrust, Inc. OU=See www.entrust.net/legal-terms/(c) 2012 Entrust, Inc. - for authorized use only +# Subject: CN=Entrust Root Certification Authority - EC1 O=Entrust, Inc. OU=See www.entrust.net/legal-terms/(c) 2012 Entrust, Inc. - for authorized use only +# Label: "Entrust Root Certification Authority - EC1" +# Serial: 51543124481930649114116133369 +# MD5 Fingerprint: b6:7e:1d:f0:58:c5:49:6c:24:3b:3d:ed:98:18:ed:bc +# SHA1 Fingerprint: 20:d8:06:40:df:9b:25:f5:12:25:3a:11:ea:f7:59:8a:eb:14:b5:47 +# SHA256 Fingerprint: 02:ed:0e:b2:8c:14:da:45:16:5c:56:67:91:70:0d:64:51:d7:fb:56:f0:b2:ab:1d:3b:8e:b0:70:e5:6e:df:f5 +-----BEGIN CERTIFICATE----- +MIIC+TCCAoCgAwIBAgINAKaLeSkAAAAAUNCR+TAKBggqhkjOPQQDAzCBvzELMAkG +A1UEBhMCVVMxFjAUBgNVBAoTDUVudHJ1c3QsIEluYy4xKDAmBgNVBAsTH1NlZSB3 +d3cuZW50cnVzdC5uZXQvbGVnYWwtdGVybXMxOTA3BgNVBAsTMChjKSAyMDEyIEVu +dHJ1c3QsIEluYy4gLSBmb3IgYXV0aG9yaXplZCB1c2Ugb25seTEzMDEGA1UEAxMq +RW50cnVzdCBSb290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5IC0gRUMxMB4XDTEy +MTIxODE1MjUzNloXDTM3MTIxODE1NTUzNlowgb8xCzAJBgNVBAYTAlVTMRYwFAYD +VQQKEw1FbnRydXN0LCBJbmMuMSgwJgYDVQQLEx9TZWUgd3d3LmVudHJ1c3QubmV0 +L2xlZ2FsLXRlcm1zMTkwNwYDVQQLEzAoYykgMjAxMiBFbnRydXN0LCBJbmMuIC0g +Zm9yIGF1dGhvcml6ZWQgdXNlIG9ubHkxMzAxBgNVBAMTKkVudHJ1c3QgUm9vdCBD +ZXJ0aWZpY2F0aW9uIEF1dGhvcml0eSAtIEVDMTB2MBAGByqGSM49AgEGBSuBBAAi +A2IABIQTydC6bUF74mzQ61VfZgIaJPRbiWlH47jCffHyAsWfoPZb1YsGGYZPUxBt +ByQnoaD41UcZYUx9ypMn6nQM72+WCf5j7HBdNq1nd67JnXxVRDqiY1Ef9eNi1KlH +Bz7MIKNCMEAwDgYDVR0PAQH/BAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0O +BBYEFLdj5xrdjekIplWDpOBqUEFlEUJJMAoGCCqGSM49BAMDA2cAMGQCMGF52OVC +R98crlOZF7ZvHH3hvxGU0QOIdeSNiaSKd0bebWHvAvX7td/M/k7//qnmpwIwW5nX +hTcGtXsI/esni0qU+eH6p44mCOh8kmhtc9hvJqwhAriZtyZBWyVgrtBIGu4G +-----END CERTIFICATE----- + +# Issuer: CN=CFCA EV ROOT O=China Financial Certification Authority +# Subject: CN=CFCA EV ROOT O=China Financial Certification Authority +# Label: "CFCA EV ROOT" +# Serial: 407555286 +# MD5 Fingerprint: 74:e1:b6:ed:26:7a:7a:44:30:33:94:ab:7b:27:81:30 +# SHA1 Fingerprint: e2:b8:29:4b:55:84:ab:6b:58:c2:90:46:6c:ac:3f:b8:39:8f:84:83 +# SHA256 Fingerprint: 5c:c3:d7:8e:4e:1d:5e:45:54:7a:04:e6:87:3e:64:f9:0c:f9:53:6d:1c:cc:2e:f8:00:f3:55:c4:c5:fd:70:fd +-----BEGIN CERTIFICATE----- +MIIFjTCCA3WgAwIBAgIEGErM1jANBgkqhkiG9w0BAQsFADBWMQswCQYDVQQGEwJD +TjEwMC4GA1UECgwnQ2hpbmEgRmluYW5jaWFsIENlcnRpZmljYXRpb24gQXV0aG9y +aXR5MRUwEwYDVQQDDAxDRkNBIEVWIFJPT1QwHhcNMTIwODA4MDMwNzAxWhcNMjkx +MjMxMDMwNzAxWjBWMQswCQYDVQQGEwJDTjEwMC4GA1UECgwnQ2hpbmEgRmluYW5j +aWFsIENlcnRpZmljYXRpb24gQXV0aG9yaXR5MRUwEwYDVQQDDAxDRkNBIEVWIFJP +T1QwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDXXWvNED8fBVnVBU03 +sQ7smCuOFR36k0sXgiFxEFLXUWRwFsJVaU2OFW2fvwwbwuCjZ9YMrM8irq93VCpL +TIpTUnrD7i7es3ElweldPe6hL6P3KjzJIx1qqx2hp/Hz7KDVRM8Vz3IvHWOX6Jn5 +/ZOkVIBMUtRSqy5J35DNuF++P96hyk0g1CXohClTt7GIH//62pCfCqktQT+x8Rgp +7hZZLDRJGqgG16iI0gNyejLi6mhNbiyWZXvKWfry4t3uMCz7zEasxGPrb382KzRz +EpR/38wmnvFyXVBlWY9ps4deMm/DGIq1lY+wejfeWkU7xzbh72fROdOXW3NiGUgt +hxwG+3SYIElz8AXSG7Ggo7cbcNOIabla1jj0Ytwli3i/+Oh+uFzJlU9fpy25IGvP +a931DfSCt/SyZi4QKPaXWnuWFo8BGS1sbn85WAZkgwGDg8NNkt0yxoekN+kWzqot +aK8KgWU6cMGbrU1tVMoqLUuFG7OA5nBFDWteNfB/O7ic5ARwiRIlk9oKmSJgamNg +TnYGmE69g60dWIolhdLHZR4tjsbftsbhf4oEIRUpdPA+nJCdDC7xij5aqgwJHsfV +PKPtl8MeNPo4+QgO48BdK4PRVmrJtqhUUy54Mmc9gn900PvhtgVguXDbjgv5E1hv +cWAQUhC5wUEJ73IfZzF4/5YFjQIDAQABo2MwYTAfBgNVHSMEGDAWgBTj/i39KNAL +tbq2osS/BqoFjJP7LzAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIBBjAd +BgNVHQ4EFgQU4/4t/SjQC7W6tqLEvwaqBYyT+y8wDQYJKoZIhvcNAQELBQADggIB +ACXGumvrh8vegjmWPfBEp2uEcwPenStPuiB/vHiyz5ewG5zz13ku9Ui20vsXiObT +ej/tUxPQ4i9qecsAIyjmHjdXNYmEwnZPNDatZ8POQQaIxffu2Bq41gt/UP+TqhdL +jOztUmCypAbqTuv0axn96/Ua4CUqmtzHQTb3yHQFhDmVOdYLO6Qn+gjYXB74BGBS +ESgoA//vU2YApUo0FmZ8/Qmkrp5nGm9BC2sGE5uPhnEFtC+NiWYzKXZUmhH4J/qy +P5Hgzg0b8zAarb8iXRvTvyUFTeGSGn+ZnzxEk8rUQElsgIfXBDrDMlI1Dlb4pd19 +xIsNER9Tyx6yF7Zod1rg1MvIB671Oi6ON7fQAUtDKXeMOZePglr4UeWJoBjnaH9d +Ci77o0cOPaYjesYBx4/IXr9tgFa+iiS6M+qf4TIRnvHST4D2G0CvOJ4RUHlzEhLN +5mydLIhyPDCBBpEi6lmt2hkuIsKNuYyH4Ga8cyNfIWRjgEj1oDwYPZTISEEdQLpe +/v5WOaHIz16eGWRGENoXkbcFgKyLmZJ956LYBws2J+dIeWCKw9cTXPhyQN9Ky8+Z +AAoACxGV2lZFA4gKn2fQ1XmxqI1AbQ3CekD6819kR5LLU7m7Wc5P/dAVUwHY3+vZ +5nbv0CO7O6l5s9UCKc2Jo5YPSjXnTkLAdc0Hz+Ys63su +-----END CERTIFICATE----- + +# Issuer: CN=Certinomis - Root CA O=Certinomis OU=0002 433998903 +# Subject: CN=Certinomis - Root CA O=Certinomis OU=0002 433998903 +# Label: "Certinomis - Root CA" +# Serial: 1 +# MD5 Fingerprint: 14:0a:fd:8d:a8:28:b5:38:69:db:56:7e:61:22:03:3f +# SHA1 Fingerprint: 9d:70:bb:01:a5:a4:a0:18:11:2e:f7:1c:01:b9:32:c5:34:e7:88:a8 +# SHA256 Fingerprint: 2a:99:f5:bc:11:74:b7:3c:bb:1d:62:08:84:e0:1c:34:e5:1c:cb:39:78:da:12:5f:0e:33:26:88:83:bf:41:58 +-----BEGIN CERTIFICATE----- +MIIFkjCCA3qgAwIBAgIBATANBgkqhkiG9w0BAQsFADBaMQswCQYDVQQGEwJGUjET +MBEGA1UEChMKQ2VydGlub21pczEXMBUGA1UECxMOMDAwMiA0MzM5OTg5MDMxHTAb +BgNVBAMTFENlcnRpbm9taXMgLSBSb290IENBMB4XDTEzMTAyMTA5MTcxOFoXDTMz +MTAyMTA5MTcxOFowWjELMAkGA1UEBhMCRlIxEzARBgNVBAoTCkNlcnRpbm9taXMx +FzAVBgNVBAsTDjAwMDIgNDMzOTk4OTAzMR0wGwYDVQQDExRDZXJ0aW5vbWlzIC0g +Um9vdCBDQTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBANTMCQosP5L2 +fxSeC5yaah1AMGT9qt8OHgZbn1CF6s2Nq0Nn3rD6foCWnoR4kkjW4znuzuRZWJfl +LieY6pOod5tK8O90gC3rMB+12ceAnGInkYjwSond3IjmFPnVAy//ldu9n+ws+hQV +WZUKxkd8aRi5pwP5ynapz8dvtF4F/u7BUrJ1Mofs7SlmO/NKFoL21prbcpjp3vDF +TKWrteoB4owuZH9kb/2jJZOLyKIOSY008B/sWEUuNKqEUL3nskoTuLAPrjhdsKkb +5nPJWqHZZkCqqU2mNAKthH6yI8H7KsZn9DS2sJVqM09xRLWtwHkziOC/7aOgFLSc +CbAK42C++PhmiM1b8XcF4LVzbsF9Ri6OSyemzTUK/eVNfaoqoynHWmgE6OXWk6Ri +wsXm9E/G+Z8ajYJJGYrKWUM66A0ywfRMEwNvbqY/kXPLynNvEiCL7sCCeN5LLsJJ +wx3tFvYk9CcbXFcx3FXuqB5vbKziRcxXV4p1VxngtViZSTYxPDMBbRZKzbgqg4SG +m/lg0h9tkQPTYKbVPZrdd5A9NaSfD171UkRpucC63M9933zZxKyGIjK8e2uR73r4 +F2iw4lNVYC2vPsKD2NkJK/DAZNuHi5HMkesE/Xa0lZrmFAYb1TQdvtj/dBxThZng +WVJKYe2InmtJiUZ+IFrZ50rlau7SZRFDAgMBAAGjYzBhMA4GA1UdDwEB/wQEAwIB +BjAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBTvkUz1pcMw6C8I6tNxIqSSaHh0 +2TAfBgNVHSMEGDAWgBTvkUz1pcMw6C8I6tNxIqSSaHh02TANBgkqhkiG9w0BAQsF +AAOCAgEAfj1U2iJdGlg+O1QnurrMyOMaauo++RLrVl89UM7g6kgmJs95Vn6RHJk/ +0KGRHCwPT5iVWVO90CLYiF2cN/z7ZMF4jIuaYAnq1fohX9B0ZedQxb8uuQsLrbWw +F6YSjNRieOpWauwK0kDDPAUwPk2Ut59KA9N9J0u2/kTO+hkzGm2kQtHdzMjI1xZS +g081lLMSVX3l4kLr5JyTCcBMWwerx20RoFAXlCOotQqSD7J6wWAsOMwaplv/8gzj +qh8c3LigkyfeY+N/IZ865Z764BNqdeuWXGKRlI5nU7aJ+BIJy29SWwNyhlCVCNSN +h4YVH5Uk2KRvms6knZtt0rJ2BobGVgjF6wnaNsIbW0G+YSrjcOa4pvi2WsS9Iff/ +ql+hbHY5ZtbqTFXhADObE5hjyW/QASAJN1LnDE8+zbz1X5YnpyACleAu6AdBBR8V +btaw5BngDwKTACdyxYvRVB9dSsNAl35VpnzBMwQUAR1JIGkLGZOdblgi90AMRgwj +Y/M50n92Uaf0yKHxDHYiI0ZSKS3io0EHVmmY0gUJvGnHWmHNj4FgFU2A3ZDifcRQ +8ow7bkrHxuaAKzyBvBGAFhAn1/DNP3nMcyrDflOR1m749fPH0FFNjkulW+YZFzvW +gQncItzujrnEj1PhZ7szuIgVRs/taTX/dQ1G885x4cVrhkIGuUE= +-----END CERTIFICATE----- + +# Issuer: CN=OISTE WISeKey Global Root GB CA O=WISeKey OU=OISTE Foundation Endorsed +# Subject: CN=OISTE WISeKey Global Root GB CA O=WISeKey OU=OISTE Foundation Endorsed +# Label: "OISTE WISeKey Global Root GB CA" +# Serial: 157768595616588414422159278966750757568 +# MD5 Fingerprint: a4:eb:b9:61:28:2e:b7:2f:98:b0:35:26:90:99:51:1d +# SHA1 Fingerprint: 0f:f9:40:76:18:d3:d7:6a:4b:98:f0:a8:35:9e:0c:fd:27:ac:cc:ed +# SHA256 Fingerprint: 6b:9c:08:e8:6e:b0:f7:67:cf:ad:65:cd:98:b6:21:49:e5:49:4a:67:f5:84:5e:7b:d1:ed:01:9f:27:b8:6b:d6 +-----BEGIN CERTIFICATE----- +MIIDtTCCAp2gAwIBAgIQdrEgUnTwhYdGs/gjGvbCwDANBgkqhkiG9w0BAQsFADBt +MQswCQYDVQQGEwJDSDEQMA4GA1UEChMHV0lTZUtleTEiMCAGA1UECxMZT0lTVEUg +Rm91bmRhdGlvbiBFbmRvcnNlZDEoMCYGA1UEAxMfT0lTVEUgV0lTZUtleSBHbG9i +YWwgUm9vdCBHQiBDQTAeFw0xNDEyMDExNTAwMzJaFw0zOTEyMDExNTEwMzFaMG0x +CzAJBgNVBAYTAkNIMRAwDgYDVQQKEwdXSVNlS2V5MSIwIAYDVQQLExlPSVNURSBG +b3VuZGF0aW9uIEVuZG9yc2VkMSgwJgYDVQQDEx9PSVNURSBXSVNlS2V5IEdsb2Jh +bCBSb290IEdCIENBMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA2Be3 +HEokKtaXscriHvt9OO+Y9bI5mE4nuBFde9IllIiCFSZqGzG7qFshISvYD06fWvGx +WuR51jIjK+FTzJlFXHtPrby/h0oLS5daqPZI7H17Dc0hBt+eFf1Biki3IPShehtX +1F1Q/7pn2COZH8g/497/b1t3sWtuuMlk9+HKQUYOKXHQuSP8yYFfTvdv37+ErXNk +u7dCjmn21HYdfp2nuFeKUWdy19SouJVUQHMD9ur06/4oQnc/nSMbsrY9gBQHTC5P +99UKFg29ZkM3fiNDecNAhvVMKdqOmq0NpQSHiB6F4+lT1ZvIiwNjeOvgGUpuuy9r +M2RYk61pv48b74JIxwIDAQABo1EwTzALBgNVHQ8EBAMCAYYwDwYDVR0TAQH/BAUw +AwEB/zAdBgNVHQ4EFgQUNQ/INmNe4qPs+TtmFc5RUuORmj0wEAYJKwYBBAGCNxUB +BAMCAQAwDQYJKoZIhvcNAQELBQADggEBAEBM+4eymYGQfp3FsLAmzYh7KzKNbrgh +cViXfa43FK8+5/ea4n32cZiZBKpDdHij40lhPnOMTZTg+XHEthYOU3gf1qKHLwI5 +gSk8rxWYITD+KJAAjNHhy/peyP34EEY7onhCkRd0VQreUGdNZtGn//3ZwLWoo4rO +ZvUPQ82nK1d7Y0Zqqi5S2PTt4W2tKZB4SLrhI6qjiey1q5bAtEuiHZeeevJuQHHf +aPFlTc58Bd9TZaml8LGXBHAVRgOY1NK/VLSgWH1Sb9pWJmLU2NuJMW8c8CLC02Ic +Nc1MaRVUGpCY3useX8p3x8uOPUNpnJpY0CQ73xtAln41rYHHTnG6iBM= +-----END CERTIFICATE----- + +# Issuer: CN=SZAFIR ROOT CA2 O=Krajowa Izba Rozliczeniowa S.A. +# Subject: CN=SZAFIR ROOT CA2 O=Krajowa Izba Rozliczeniowa S.A. +# Label: "SZAFIR ROOT CA2" +# Serial: 357043034767186914217277344587386743377558296292 +# MD5 Fingerprint: 11:64:c1:89:b0:24:b1:8c:b1:07:7e:89:9e:51:9e:99 +# SHA1 Fingerprint: e2:52:fa:95:3f:ed:db:24:60:bd:6e:28:f3:9c:cc:cf:5e:b3:3f:de +# SHA256 Fingerprint: a1:33:9d:33:28:1a:0b:56:e5:57:d3:d3:2b:1c:e7:f9:36:7e:b0:94:bd:5f:a7:2a:7e:50:04:c8:de:d7:ca:fe +-----BEGIN CERTIFICATE----- +MIIDcjCCAlqgAwIBAgIUPopdB+xV0jLVt+O2XwHrLdzk1uQwDQYJKoZIhvcNAQEL +BQAwUTELMAkGA1UEBhMCUEwxKDAmBgNVBAoMH0tyYWpvd2EgSXpiYSBSb3psaWN6 +ZW5pb3dhIFMuQS4xGDAWBgNVBAMMD1NaQUZJUiBST09UIENBMjAeFw0xNTEwMTkw +NzQzMzBaFw0zNTEwMTkwNzQzMzBaMFExCzAJBgNVBAYTAlBMMSgwJgYDVQQKDB9L +cmFqb3dhIEl6YmEgUm96bGljemVuaW93YSBTLkEuMRgwFgYDVQQDDA9TWkFGSVIg +Uk9PVCBDQTIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC3vD5QqEvN +QLXOYeeWyrSh2gwisPq1e3YAd4wLz32ohswmUeQgPYUM1ljj5/QqGJ3a0a4m7utT +3PSQ1hNKDJA8w/Ta0o4NkjrcsbH/ON7Dui1fgLkCvUqdGw+0w8LBZwPd3BucPbOw +3gAeqDRHu5rr/gsUvTaE2g0gv/pby6kWIK05YO4vdbbnl5z5Pv1+TW9NL++IDWr6 +3fE9biCloBK0TXC5ztdyO4mTp4CEHCdJckm1/zuVnsHMyAHs6A6KCpbns6aH5db5 +BSsNl0BwPLqsdVqc1U2dAgrSS5tmS0YHF2Wtn2yIANwiieDhZNRnvDF5YTy7ykHN +XGoAyDw4jlivAgMBAAGjQjBAMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQD +AgEGMB0GA1UdDgQWBBQuFqlKGLXLzPVvUPMjX/hd56zwyDANBgkqhkiG9w0BAQsF +AAOCAQEAtXP4A9xZWx126aMqe5Aosk3AM0+qmrHUuOQn/6mWmc5G4G18TKI4pAZw +8PRBEew/R40/cof5O/2kbytTAOD/OblqBw7rHRz2onKQy4I9EYKL0rufKq8h5mOG +nXkZ7/e7DDWQw4rtTw/1zBLZpD67oPwglV9PJi8RI4NOdQcPv5vRtB3pEAT+ymCP +oky4rc/hkA/NrgrHXXu3UNLUYfrVFdvXn4dRVOul4+vJhaAlIDf7js4MNIThPIGy +d05DpYhfhmehPea0XGG2Ptv+tyjFogeutcrKjSoS75ftwjCkySp6+/NNIxuZMzSg +LvWpCz/UXeHPhJ/iGcJfitYgHuNztw== +-----END CERTIFICATE----- + +# Issuer: CN=Certum Trusted Network CA 2 O=Unizeto Technologies S.A. OU=Certum Certification Authority +# Subject: CN=Certum Trusted Network CA 2 O=Unizeto Technologies S.A. OU=Certum Certification Authority +# Label: "Certum Trusted Network CA 2" +# Serial: 44979900017204383099463764357512596969 +# MD5 Fingerprint: 6d:46:9e:d9:25:6d:08:23:5b:5e:74:7d:1e:27:db:f2 +# SHA1 Fingerprint: d3:dd:48:3e:2b:bf:4c:05:e8:af:10:f5:fa:76:26:cf:d3:dc:30:92 +# SHA256 Fingerprint: b6:76:f2:ed:da:e8:77:5c:d3:6c:b0:f6:3c:d1:d4:60:39:61:f4:9e:62:65:ba:01:3a:2f:03:07:b6:d0:b8:04 +-----BEGIN CERTIFICATE----- +MIIF0jCCA7qgAwIBAgIQIdbQSk8lD8kyN/yqXhKN6TANBgkqhkiG9w0BAQ0FADCB +gDELMAkGA1UEBhMCUEwxIjAgBgNVBAoTGVVuaXpldG8gVGVjaG5vbG9naWVzIFMu +QS4xJzAlBgNVBAsTHkNlcnR1bSBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTEkMCIG +A1UEAxMbQ2VydHVtIFRydXN0ZWQgTmV0d29yayBDQSAyMCIYDzIwMTExMDA2MDgz +OTU2WhgPMjA0NjEwMDYwODM5NTZaMIGAMQswCQYDVQQGEwJQTDEiMCAGA1UEChMZ +VW5pemV0byBUZWNobm9sb2dpZXMgUy5BLjEnMCUGA1UECxMeQ2VydHVtIENlcnRp +ZmljYXRpb24gQXV0aG9yaXR5MSQwIgYDVQQDExtDZXJ0dW0gVHJ1c3RlZCBOZXR3 +b3JrIENBIDIwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQC9+Xj45tWA +DGSdhhuWZGc/IjoedQF97/tcZ4zJzFxrqZHmuULlIEub2pt7uZld2ZuAS9eEQCsn +0+i6MLs+CRqnSZXvK0AkwpfHp+6bJe+oCgCXhVqqndwpyeI1B+twTUrWwbNWuKFB +OJvR+zF/j+Bf4bE/D44WSWDXBo0Y+aomEKsq09DRZ40bRr5HMNUuctHFY9rnY3lE +fktjJImGLjQ/KUxSiyqnwOKRKIm5wFv5HdnnJ63/mgKXwcZQkpsCLL2puTRZCr+E +Sv/f/rOf69me4Jgj7KZrdxYq28ytOxykh9xGc14ZYmhFV+SQgkK7QtbwYeDBoz1m +o130GO6IyY0XRSmZMnUCMe4pJshrAua1YkV/NxVaI2iJ1D7eTiew8EAMvE0Xy02i +sx7QBlrd9pPPV3WZ9fqGGmd4s7+W/jTcvedSVuWz5XV710GRBdxdaeOVDUO5/IOW +OZV7bIBaTxNyxtd9KXpEulKkKtVBRgkg/iKgtlswjbyJDNXXcPiHUv3a76xRLgez +Tv7QCdpw75j6VuZt27VXS9zlLCUVyJ4ueE742pyehizKV/Ma5ciSixqClnrDvFAS +adgOWkaLOusm+iPJtrCBvkIApPjW/jAux9JG9uWOdf3yzLnQh1vMBhBgu4M1t15n +3kfsmUjxpKEV/q2MYo45VU85FrmxY53/twIDAQABo0IwQDAPBgNVHRMBAf8EBTAD +AQH/MB0GA1UdDgQWBBS2oVQ5AsOgP46KvPrU+Bym0ToO/TAOBgNVHQ8BAf8EBAMC +AQYwDQYJKoZIhvcNAQENBQADggIBAHGlDs7k6b8/ONWJWsQCYftMxRQXLYtPU2sQ +F/xlhMcQSZDe28cmk4gmb3DWAl45oPePq5a1pRNcgRRtDoGCERuKTsZPpd1iHkTf +CVn0W3cLN+mLIMb4Ck4uWBzrM9DPhmDJ2vuAL55MYIR4PSFk1vtBHxgP58l1cb29 +XN40hz5BsA72udY/CROWFC/emh1auVbONTqwX3BNXuMp8SMoclm2q8KMZiYcdywm +djWLKKdpoPk79SPdhRB0yZADVpHnr7pH1BKXESLjokmUbOe3lEu6LaTaM4tMpkT/ +WjzGHWTYtTHkpjx6qFcL2+1hGsvxznN3Y6SHb0xRONbkX8eftoEq5IVIeVheO/jb +AoJnwTnbw3RLPTYe+SmTiGhbqEQZIfCn6IENLOiTNrQ3ssqwGyZ6miUfmpqAnksq +P/ujmv5zMnHCnsZy4YpoJ/HkD7TETKVhk/iXEAcqMCWpuchxuO9ozC1+9eB+D4Ko +b7a6bINDd82Kkhehnlt4Fj1F4jNy3eFmypnTycUm/Q1oBEauttmbjL4ZvrHG8hnj +XALKLNhvSgfZyTXaQHXyxKcZb55CEJh15pWLYLztxRLXis7VmFxWlgPF7ncGNf/P +5O4/E2Hu29othfDNrp2yGAlFw5Khchf8R7agCyzxxN5DaAhqXzvwdmP7zAYspsbi +DrW5viSP +-----END CERTIFICATE----- + +# Issuer: CN=Hellenic Academic and Research Institutions RootCA 2015 O=Hellenic Academic and Research Institutions Cert. Authority +# Subject: CN=Hellenic Academic and Research Institutions RootCA 2015 O=Hellenic Academic and Research Institutions Cert. Authority +# Label: "Hellenic Academic and Research Institutions RootCA 2015" +# Serial: 0 +# MD5 Fingerprint: ca:ff:e2:db:03:d9:cb:4b:e9:0f:ad:84:fd:7b:18:ce +# SHA1 Fingerprint: 01:0c:06:95:a6:98:19:14:ff:bf:5f:c6:b0:b6:95:ea:29:e9:12:a6 +# SHA256 Fingerprint: a0:40:92:9a:02:ce:53:b4:ac:f4:f2:ff:c6:98:1c:e4:49:6f:75:5e:6d:45:fe:0b:2a:69:2b:cd:52:52:3f:36 +-----BEGIN CERTIFICATE----- +MIIGCzCCA/OgAwIBAgIBADANBgkqhkiG9w0BAQsFADCBpjELMAkGA1UEBhMCR1Ix +DzANBgNVBAcTBkF0aGVuczFEMEIGA1UEChM7SGVsbGVuaWMgQWNhZGVtaWMgYW5k +IFJlc2VhcmNoIEluc3RpdHV0aW9ucyBDZXJ0LiBBdXRob3JpdHkxQDA+BgNVBAMT +N0hlbGxlbmljIEFjYWRlbWljIGFuZCBSZXNlYXJjaCBJbnN0aXR1dGlvbnMgUm9v +dENBIDIwMTUwHhcNMTUwNzA3MTAxMTIxWhcNNDAwNjMwMTAxMTIxWjCBpjELMAkG +A1UEBhMCR1IxDzANBgNVBAcTBkF0aGVuczFEMEIGA1UEChM7SGVsbGVuaWMgQWNh +ZGVtaWMgYW5kIFJlc2VhcmNoIEluc3RpdHV0aW9ucyBDZXJ0LiBBdXRob3JpdHkx +QDA+BgNVBAMTN0hlbGxlbmljIEFjYWRlbWljIGFuZCBSZXNlYXJjaCBJbnN0aXR1 +dGlvbnMgUm9vdENBIDIwMTUwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoIC +AQDC+Kk/G4n8PDwEXT2QNrCROnk8ZlrvbTkBSRq0t89/TSNTt5AA4xMqKKYx8ZEA +4yjsriFBzh/a/X0SWwGDD7mwX5nh8hKDgE0GPt+sr+ehiGsxr/CL0BgzuNtFajT0 +AoAkKAoCFZVedioNmToUW/bLy1O8E00BiDeUJRtCvCLYjqOWXjrZMts+6PAQZe10 +4S+nfK8nNLspfZu2zwnI5dMK/IhlZXQK3HMcXM1AsRzUtoSMTFDPaI6oWa7CJ06C +ojXdFPQf/7J31Ycvqm59JCfnxssm5uX+Zwdj2EUN3TpZZTlYepKZcj2chF6IIbjV +9Cz82XBST3i4vTwri5WY9bPRaM8gFH5MXF/ni+X1NYEZN9cRCLdmvtNKzoNXADrD +gfgXy5I2XdGj2HUb4Ysn6npIQf1FGQatJ5lOwXBH3bWfgVMS5bGMSF0xQxfjjMZ6 +Y5ZLKTBOhE5iGV48zpeQpX8B653g+IuJ3SWYPZK2fu/Z8VFRfS0myGlZYeCsargq +NhEEelC9MoS+L9xy1dcdFkfkR2YgP/SWxa+OAXqlD3pk9Q0Yh9muiNX6hME6wGko +LfINaFGq46V3xqSQDqE3izEjR8EJCOtu93ib14L8hCCZSRm2Ekax+0VVFqmjZayc +Bw/qa9wfLgZy7IaIEuQt218FL+TwA9MmM+eAws1CoRc0CwIDAQABo0IwQDAPBgNV +HRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIBBjAdBgNVHQ4EFgQUcRVnyMjJvXVd +ctA4GGqd83EkVAswDQYJKoZIhvcNAQELBQADggIBAHW7bVRLqhBYRjTyYtcWNl0I +XtVsyIe9tC5G8jH4fOpCtZMWVdyhDBKg2mF+D1hYc2Ryx+hFjtyp8iY/xnmMsVMI +M4GwVhO+5lFc2JsKT0ucVlMC6U/2DWDqTUJV6HwbISHTGzrMd/K4kPFox/la/vot +9L/J9UUbzjgQKjeKeaO04wlshYaT/4mWJ3iBj2fjRnRUjtkNaeJK9E10A/+yd+2V +Z5fkscWrv2oj6NSU4kQoYsRL4vDY4ilrGnB+JGGTe08DMiUNRSQrlrRGar9KC/ea +j8GsGsVn82800vpzY4zvFrCopEYq+OsS7HK07/grfoxSwIuEVPkvPuNVqNxmsdnh +X9izjFk0WaSrT2y7HxjbdavYy5LNlDhhDgcGH0tGEPEVvo2FXDtKK4F5D7Rpn0lQ +l033DlZdwJVqwjbDG2jJ9SrcR5q+ss7FJej6A7na+RZukYT1HCjI/CbM1xyQVqdf +bzoEvM14iQuODy+jqk+iGxI9FghAD/FGTNeqewjBCvVtJ94Cj8rDtSvK6evIIVM4 +pcw72Hc3MKJP2W/R8kCtQXoXxdZKNYm3QdV8hn9VTYNKpXMgwDqvkPGaJI7ZjnHK +e7iG2rKPmT4dEw0SEe7Uq/DpFXYC5ODfqiAeW2GFZECpkJcNrVPSWh2HagCXZWK0 +vm9qp/UsQu0yrbYhnr68 +-----END CERTIFICATE----- + +# Issuer: CN=Hellenic Academic and Research Institutions ECC RootCA 2015 O=Hellenic Academic and Research Institutions Cert. Authority +# Subject: CN=Hellenic Academic and Research Institutions ECC RootCA 2015 O=Hellenic Academic and Research Institutions Cert. Authority +# Label: "Hellenic Academic and Research Institutions ECC RootCA 2015" +# Serial: 0 +# MD5 Fingerprint: 81:e5:b4:17:eb:c2:f5:e1:4b:0d:41:7b:49:92:fe:ef +# SHA1 Fingerprint: 9f:f1:71:8d:92:d5:9a:f3:7d:74:97:b4:bc:6f:84:68:0b:ba:b6:66 +# SHA256 Fingerprint: 44:b5:45:aa:8a:25:e6:5a:73:ca:15:dc:27:fc:36:d2:4c:1c:b9:95:3a:06:65:39:b1:15:82:dc:48:7b:48:33 +-----BEGIN CERTIFICATE----- +MIICwzCCAkqgAwIBAgIBADAKBggqhkjOPQQDAjCBqjELMAkGA1UEBhMCR1IxDzAN +BgNVBAcTBkF0aGVuczFEMEIGA1UEChM7SGVsbGVuaWMgQWNhZGVtaWMgYW5kIFJl +c2VhcmNoIEluc3RpdHV0aW9ucyBDZXJ0LiBBdXRob3JpdHkxRDBCBgNVBAMTO0hl +bGxlbmljIEFjYWRlbWljIGFuZCBSZXNlYXJjaCBJbnN0aXR1dGlvbnMgRUNDIFJv +b3RDQSAyMDE1MB4XDTE1MDcwNzEwMzcxMloXDTQwMDYzMDEwMzcxMlowgaoxCzAJ +BgNVBAYTAkdSMQ8wDQYDVQQHEwZBdGhlbnMxRDBCBgNVBAoTO0hlbGxlbmljIEFj +YWRlbWljIGFuZCBSZXNlYXJjaCBJbnN0aXR1dGlvbnMgQ2VydC4gQXV0aG9yaXR5 +MUQwQgYDVQQDEztIZWxsZW5pYyBBY2FkZW1pYyBhbmQgUmVzZWFyY2ggSW5zdGl0 +dXRpb25zIEVDQyBSb290Q0EgMjAxNTB2MBAGByqGSM49AgEGBSuBBAAiA2IABJKg +QehLgoRc4vgxEZmGZE4JJS+dQS8KrjVPdJWyUWRrjWvmP3CV8AVER6ZyOFB2lQJa +jq4onvktTpnvLEhvTCUp6NFxW98dwXU3tNf6e3pCnGoKVlp8aQuqgAkkbH7BRqNC +MEAwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwHQYDVR0OBBYEFLQi +C4KZJAEOnLvkDv2/+5cgk5kqMAoGCCqGSM49BAMCA2cAMGQCMGfOFmI4oqxiRaep +lSTAGiecMjvAwNW6qef4BENThe5SId6d9SWDPp5YSy/XZxMOIQIwBeF1Ad5o7Sof +TUwJCA3sS61kFyjndc5FZXIhF8siQQ6ME5g4mlRtm8rifOoCWCKR +-----END CERTIFICATE----- + +# Issuer: CN=ISRG Root X1 O=Internet Security Research Group +# Subject: CN=ISRG Root X1 O=Internet Security Research Group +# Label: "ISRG Root X1" +# Serial: 172886928669790476064670243504169061120 +# MD5 Fingerprint: 0c:d2:f9:e0:da:17:73:e9:ed:86:4d:a5:e3:70:e7:4e +# SHA1 Fingerprint: ca:bd:2a:79:a1:07:6a:31:f2:1d:25:36:35:cb:03:9d:43:29:a5:e8 +# SHA256 Fingerprint: 96:bc:ec:06:26:49:76:f3:74:60:77:9a:cf:28:c5:a7:cf:e8:a3:c0:aa:e1:1a:8f:fc:ee:05:c0:bd:df:08:c6 +-----BEGIN CERTIFICATE----- +MIIFazCCA1OgAwIBAgIRAIIQz7DSQONZRGPgu2OCiwAwDQYJKoZIhvcNAQELBQAw +TzELMAkGA1UEBhMCVVMxKTAnBgNVBAoTIEludGVybmV0IFNlY3VyaXR5IFJlc2Vh +cmNoIEdyb3VwMRUwEwYDVQQDEwxJU1JHIFJvb3QgWDEwHhcNMTUwNjA0MTEwNDM4 +WhcNMzUwNjA0MTEwNDM4WjBPMQswCQYDVQQGEwJVUzEpMCcGA1UEChMgSW50ZXJu +ZXQgU2VjdXJpdHkgUmVzZWFyY2ggR3JvdXAxFTATBgNVBAMTDElTUkcgUm9vdCBY +MTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAK3oJHP0FDfzm54rVygc +h77ct984kIxuPOZXoHj3dcKi/vVqbvYATyjb3miGbESTtrFj/RQSa78f0uoxmyF+ +0TM8ukj13Xnfs7j/EvEhmkvBioZxaUpmZmyPfjxwv60pIgbz5MDmgK7iS4+3mX6U +A5/TR5d8mUgjU+g4rk8Kb4Mu0UlXjIB0ttov0DiNewNwIRt18jA8+o+u3dpjq+sW +T8KOEUt+zwvo/7V3LvSye0rgTBIlDHCNAymg4VMk7BPZ7hm/ELNKjD+Jo2FR3qyH +B5T0Y3HsLuJvW5iB4YlcNHlsdu87kGJ55tukmi8mxdAQ4Q7e2RCOFvu396j3x+UC +B5iPNgiV5+I3lg02dZ77DnKxHZu8A/lJBdiB3QW0KtZB6awBdpUKD9jf1b0SHzUv +KBds0pjBqAlkd25HN7rOrFleaJ1/ctaJxQZBKT5ZPt0m9STJEadao0xAH0ahmbWn +OlFuhjuefXKnEgV4We0+UXgVCwOPjdAvBbI+e0ocS3MFEvzG6uBQE3xDk3SzynTn +jh8BCNAw1FtxNrQHusEwMFxIt4I7mKZ9YIqioymCzLq9gwQbooMDQaHWBfEbwrbw +qHyGO0aoSCqI3Haadr8faqU9GY/rOPNk3sgrDQoo//fb4hVC1CLQJ13hef4Y53CI +rU7m2Ys6xt0nUW7/vGT1M0NPAgMBAAGjQjBAMA4GA1UdDwEB/wQEAwIBBjAPBgNV +HRMBAf8EBTADAQH/MB0GA1UdDgQWBBR5tFnme7bl5AFzgAiIyBpY9umbbjANBgkq +hkiG9w0BAQsFAAOCAgEAVR9YqbyyqFDQDLHYGmkgJykIrGF1XIpu+ILlaS/V9lZL +ubhzEFnTIZd+50xx+7LSYK05qAvqFyFWhfFQDlnrzuBZ6brJFe+GnY+EgPbk6ZGQ +3BebYhtF8GaV0nxvwuo77x/Py9auJ/GpsMiu/X1+mvoiBOv/2X/qkSsisRcOj/KK +NFtY2PwByVS5uCbMiogziUwthDyC3+6WVwW6LLv3xLfHTjuCvjHIInNzktHCgKQ5 +ORAzI4JMPJ+GslWYHb4phowim57iaztXOoJwTdwJx4nLCgdNbOhdjsnvzqvHu7Ur +TkXWStAmzOVyyghqpZXjFaH3pO3JLF+l+/+sKAIuvtd7u+Nxe5AW0wdeRlN8NwdC +jNPElpzVmbUq4JUagEiuTDkHzsxHpFKVK7q4+63SM1N95R1NbdWhscdCb+ZAJzVc +oyi3B43njTOQ5yOf+1CceWxG1bQVs5ZufpsMljq4Ui0/1lvh+wjChP4kqKOJ2qxq +4RgqsahDYVvTH9w7jXbyLeiNdd8XM2w9U/t7y0Ff/9yi0GE44Za4rF2LN9d11TPA +mRGunUHBcnWEvgJBQl9nJEiU0Zsnvgc/ubhPgXRR4Xq37Z0j4r7g1SgEEzwxA57d +emyPxgcYxn/eR44/KJ4EBs+lVDR3veyJm+kXQ99b21/+jh5Xos1AnX5iItreGCc= +-----END CERTIFICATE----- + +# Issuer: O=FNMT-RCM OU=AC RAIZ FNMT-RCM +# Subject: O=FNMT-RCM OU=AC RAIZ FNMT-RCM +# Label: "AC RAIZ FNMT-RCM" +# Serial: 485876308206448804701554682760554759 +# MD5 Fingerprint: e2:09:04:b4:d3:bd:d1:a0:14:fd:1a:d2:47:c4:57:1d +# SHA1 Fingerprint: ec:50:35:07:b2:15:c4:95:62:19:e2:a8:9a:5b:42:99:2c:4c:2c:20 +# SHA256 Fingerprint: eb:c5:57:0c:29:01:8c:4d:67:b1:aa:12:7b:af:12:f7:03:b4:61:1e:bc:17:b7:da:b5:57:38:94:17:9b:93:fa +-----BEGIN CERTIFICATE----- +MIIFgzCCA2ugAwIBAgIPXZONMGc2yAYdGsdUhGkHMA0GCSqGSIb3DQEBCwUAMDsx +CzAJBgNVBAYTAkVTMREwDwYDVQQKDAhGTk1ULVJDTTEZMBcGA1UECwwQQUMgUkFJ +WiBGTk1ULVJDTTAeFw0wODEwMjkxNTU5NTZaFw0zMDAxMDEwMDAwMDBaMDsxCzAJ +BgNVBAYTAkVTMREwDwYDVQQKDAhGTk1ULVJDTTEZMBcGA1UECwwQQUMgUkFJWiBG +Tk1ULVJDTTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBALpxgHpMhm5/ +yBNtwMZ9HACXjywMI7sQmkCpGreHiPibVmr75nuOi5KOpyVdWRHbNi63URcfqQgf +BBckWKo3Shjf5TnUV/3XwSyRAZHiItQDwFj8d0fsjz50Q7qsNI1NOHZnjrDIbzAz +WHFctPVrbtQBULgTfmxKo0nRIBnuvMApGGWn3v7v3QqQIecaZ5JCEJhfTzC8PhxF +tBDXaEAUwED653cXeuYLj2VbPNmaUtu1vZ5Gzz3rkQUCwJaydkxNEJY7kvqcfw+Z +374jNUUeAlz+taibmSXaXvMiwzn15Cou08YfxGyqxRxqAQVKL9LFwag0Jl1mpdIC +IfkYtwb1TplvqKtMUejPUBjFd8g5CSxJkjKZqLsXF3mwWsXmo8RZZUc1g16p6DUL +mbvkzSDGm0oGObVo/CK67lWMK07q87Hj/LaZmtVC+nFNCM+HHmpxffnTtOmlcYF7 +wk5HlqX2doWjKI/pgG6BU6VtX7hI+cL5NqYuSf+4lsKMB7ObiFj86xsc3i1w4peS +MKGJ47xVqCfWS+2QrYv6YyVZLag13cqXM7zlzced0ezvXg5KkAYmY6252TUtB7p2 +ZSysV4999AeU14ECll2jB0nVetBX+RvnU0Z1qrB5QstocQjpYL05ac70r8NWQMet +UqIJ5G+GR4of6ygnXYMgrwTJbFaai0b1AgMBAAGjgYMwgYAwDwYDVR0TAQH/BAUw +AwEB/zAOBgNVHQ8BAf8EBAMCAQYwHQYDVR0OBBYEFPd9xf3E6Jobd2Sn9R2gzL+H +YJptMD4GA1UdIAQ3MDUwMwYEVR0gADArMCkGCCsGAQUFBwIBFh1odHRwOi8vd3d3 +LmNlcnQuZm5tdC5lcy9kcGNzLzANBgkqhkiG9w0BAQsFAAOCAgEAB5BK3/MjTvDD +nFFlm5wioooMhfNzKWtN/gHiqQxjAb8EZ6WdmF/9ARP67Jpi6Yb+tmLSbkyU+8B1 +RXxlDPiyN8+sD8+Nb/kZ94/sHvJwnvDKuO+3/3Y3dlv2bojzr2IyIpMNOmqOFGYM +LVN0V2Ue1bLdI4E7pWYjJ2cJj+F3qkPNZVEI7VFY/uY5+ctHhKQV8Xa7pO6kO8Rf +77IzlhEYt8llvhjho6Tc+hj507wTmzl6NLrTQfv6MooqtyuGC2mDOL7Nii4LcK2N +JpLuHvUBKwrZ1pebbuCoGRw6IYsMHkCtA+fdZn71uSANA+iW+YJF1DngoABd15jm +fZ5nc8OaKveri6E6FO80vFIOiZiaBECEHX5FaZNXzuvO+FB8TxxuBEOb+dY7Ixjp +6o7RTUaN8Tvkasq6+yO3m/qZASlaWFot4/nUbQ4mrcFuNLwy+AwF+mWj2zs3gyLp +1txyM/1d8iC9djwj2ij3+RvrWWTV3F9yfiD8zYm1kGdNYno/Tq0dwzn+evQoFt9B +9kiABdcPUXmsEKvU7ANm5mqwujGSQkBqvjrTcuFqN1W8rB2Vt2lh8kORdOag0wok +RqEIr9baRRmW1FMdW4R58MD3R++Lj8UGrp1MYp3/RgT408m2ECVAdf4WqslKYIYv +uu8wd+RU4riEmViAqhOLUTpPSPaLtrM= +-----END CERTIFICATE----- + +# Issuer: CN=Amazon Root CA 1 O=Amazon +# Subject: CN=Amazon Root CA 1 O=Amazon +# Label: "Amazon Root CA 1" +# Serial: 143266978916655856878034712317230054538369994 +# MD5 Fingerprint: 43:c6:bf:ae:ec:fe:ad:2f:18:c6:88:68:30:fc:c8:e6 +# SHA1 Fingerprint: 8d:a7:f9:65:ec:5e:fc:37:91:0f:1c:6e:59:fd:c1:cc:6a:6e:de:16 +# SHA256 Fingerprint: 8e:cd:e6:88:4f:3d:87:b1:12:5b:a3:1a:c3:fc:b1:3d:70:16:de:7f:57:cc:90:4f:e1:cb:97:c6:ae:98:19:6e +-----BEGIN CERTIFICATE----- +MIIDQTCCAimgAwIBAgITBmyfz5m/jAo54vB4ikPmljZbyjANBgkqhkiG9w0BAQsF +ADA5MQswCQYDVQQGEwJVUzEPMA0GA1UEChMGQW1hem9uMRkwFwYDVQQDExBBbWF6 +b24gUm9vdCBDQSAxMB4XDTE1MDUyNjAwMDAwMFoXDTM4MDExNzAwMDAwMFowOTEL +MAkGA1UEBhMCVVMxDzANBgNVBAoTBkFtYXpvbjEZMBcGA1UEAxMQQW1hem9uIFJv +b3QgQ0EgMTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBALJ4gHHKeNXj +ca9HgFB0fW7Y14h29Jlo91ghYPl0hAEvrAIthtOgQ3pOsqTQNroBvo3bSMgHFzZM +9O6II8c+6zf1tRn4SWiw3te5djgdYZ6k/oI2peVKVuRF4fn9tBb6dNqcmzU5L/qw +IFAGbHrQgLKm+a/sRxmPUDgH3KKHOVj4utWp+UhnMJbulHheb4mjUcAwhmahRWa6 +VOujw5H5SNz/0egwLX0tdHA114gk957EWW67c4cX8jJGKLhD+rcdqsq08p8kDi1L +93FcXmn/6pUCyziKrlA4b9v7LWIbxcceVOF34GfID5yHI9Y/QCB/IIDEgEw+OyQm +jgSubJrIqg0CAwEAAaNCMEAwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMC +AYYwHQYDVR0OBBYEFIQYzIU07LwMlJQuCFmcx7IQTgoIMA0GCSqGSIb3DQEBCwUA +A4IBAQCY8jdaQZChGsV2USggNiMOruYou6r4lK5IpDB/G/wkjUu0yKGX9rbxenDI +U5PMCCjjmCXPI6T53iHTfIUJrU6adTrCC2qJeHZERxhlbI1Bjjt/msv0tadQ1wUs +N+gDS63pYaACbvXy8MWy7Vu33PqUXHeeE6V/Uq2V8viTO96LXFvKWlJbYK8U90vv +o/ufQJVtMVT8QtPHRh8jrdkPSHCa2XV4cdFyQzR1bldZwgJcJmApzyMZFo6IQ6XU +5MsI+yMRQ+hDKXJioaldXgjUkK642M4UwtBV8ob2xJNDd2ZhwLnoQdeXeGADbkpy +rqXRfboQnoZsG4q5WTP468SQvvG5 +-----END CERTIFICATE----- + +# Issuer: CN=Amazon Root CA 2 O=Amazon +# Subject: CN=Amazon Root CA 2 O=Amazon +# Label: "Amazon Root CA 2" +# Serial: 143266982885963551818349160658925006970653239 +# MD5 Fingerprint: c8:e5:8d:ce:a8:42:e2:7a:c0:2a:5c:7c:9e:26:bf:66 +# SHA1 Fingerprint: 5a:8c:ef:45:d7:a6:98:59:76:7a:8c:8b:44:96:b5:78:cf:47:4b:1a +# SHA256 Fingerprint: 1b:a5:b2:aa:8c:65:40:1a:82:96:01:18:f8:0b:ec:4f:62:30:4d:83:ce:c4:71:3a:19:c3:9c:01:1e:a4:6d:b4 +-----BEGIN CERTIFICATE----- +MIIFQTCCAymgAwIBAgITBmyf0pY1hp8KD+WGePhbJruKNzANBgkqhkiG9w0BAQwF +ADA5MQswCQYDVQQGEwJVUzEPMA0GA1UEChMGQW1hem9uMRkwFwYDVQQDExBBbWF6 +b24gUm9vdCBDQSAyMB4XDTE1MDUyNjAwMDAwMFoXDTQwMDUyNjAwMDAwMFowOTEL +MAkGA1UEBhMCVVMxDzANBgNVBAoTBkFtYXpvbjEZMBcGA1UEAxMQQW1hem9uIFJv +b3QgQ0EgMjCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAK2Wny2cSkxK +gXlRmeyKy2tgURO8TW0G/LAIjd0ZEGrHJgw12MBvIITplLGbhQPDW9tK6Mj4kHbZ +W0/jTOgGNk3Mmqw9DJArktQGGWCsN0R5hYGCrVo34A3MnaZMUnbqQ523BNFQ9lXg +1dKmSYXpN+nKfq5clU1Imj+uIFptiJXZNLhSGkOQsL9sBbm2eLfq0OQ6PBJTYv9K +8nu+NQWpEjTj82R0Yiw9AElaKP4yRLuH3WUnAnE72kr3H9rN9yFVkE8P7K6C4Z9r +2UXTu/Bfh+08LDmG2j/e7HJV63mjrdvdfLC6HM783k81ds8P+HgfajZRRidhW+me +z/CiVX18JYpvL7TFz4QuK/0NURBs+18bvBt+xa47mAExkv8LV/SasrlX6avvDXbR +8O70zoan4G7ptGmh32n2M8ZpLpcTnqWHsFcQgTfJU7O7f/aS0ZzQGPSSbtqDT6Zj +mUyl+17vIWR6IF9sZIUVyzfpYgwLKhbcAS4y2j5L9Z469hdAlO+ekQiG+r5jqFoz +7Mt0Q5X5bGlSNscpb/xVA1wf+5+9R+vnSUeVC06JIglJ4PVhHvG/LopyboBZ/1c6 ++XUyo05f7O0oYtlNc/LMgRdg7c3r3NunysV+Ar3yVAhU/bQtCSwXVEqY0VThUWcI +0u1ufm8/0i2BWSlmy5A5lREedCf+3euvAgMBAAGjQjBAMA8GA1UdEwEB/wQFMAMB +Af8wDgYDVR0PAQH/BAQDAgGGMB0GA1UdDgQWBBSwDPBMMPQFWAJI/TPlUq9LhONm +UjANBgkqhkiG9w0BAQwFAAOCAgEAqqiAjw54o+Ci1M3m9Zh6O+oAA7CXDpO8Wqj2 +LIxyh6mx/H9z/WNxeKWHWc8w4Q0QshNabYL1auaAn6AFC2jkR2vHat+2/XcycuUY ++gn0oJMsXdKMdYV2ZZAMA3m3MSNjrXiDCYZohMr/+c8mmpJ5581LxedhpxfL86kS +k5Nrp+gvU5LEYFiwzAJRGFuFjWJZY7attN6a+yb3ACfAXVU3dJnJUH/jWS5E4ywl +7uxMMne0nxrpS10gxdr9HIcWxkPo1LsmmkVwXqkLN1PiRnsn/eBG8om3zEK2yygm +btmlyTrIQRNg91CMFa6ybRoVGld45pIq2WWQgj9sAq+uEjonljYE1x2igGOpm/Hl +urR8FLBOybEfdF849lHqm/osohHUqS0nGkWxr7JOcQ3AWEbWaQbLU8uz/mtBzUF+ +fUwPfHJ5elnNXkoOrJupmHN5fLT0zLm4BwyydFy4x2+IoZCn9Kr5v2c69BoVYh63 +n749sSmvZ6ES8lgQGVMDMBu4Gon2nL2XA46jCfMdiyHxtN/kHNGfZQIG6lzWE7OE +76KlXIx3KadowGuuQNKotOrN8I1LOJwZmhsoVLiJkO/KdYE+HvJkJMcYr07/R54H +9jVlpNMKVv/1F2Rs76giJUmTtt8AF9pYfl3uxRuw0dFfIRDH+fO6AgonB8Xx1sfT +4PsJYGw= +-----END CERTIFICATE----- + +# Issuer: CN=Amazon Root CA 3 O=Amazon +# Subject: CN=Amazon Root CA 3 O=Amazon +# Label: "Amazon Root CA 3" +# Serial: 143266986699090766294700635381230934788665930 +# MD5 Fingerprint: a0:d4:ef:0b:f7:b5:d8:49:95:2a:ec:f5:c4:fc:81:87 +# SHA1 Fingerprint: 0d:44:dd:8c:3c:8c:1a:1a:58:75:64:81:e9:0f:2e:2a:ff:b3:d2:6e +# SHA256 Fingerprint: 18:ce:6c:fe:7b:f1:4e:60:b2:e3:47:b8:df:e8:68:cb:31:d0:2e:bb:3a:da:27:15:69:f5:03:43:b4:6d:b3:a4 +-----BEGIN CERTIFICATE----- +MIIBtjCCAVugAwIBAgITBmyf1XSXNmY/Owua2eiedgPySjAKBggqhkjOPQQDAjA5 +MQswCQYDVQQGEwJVUzEPMA0GA1UEChMGQW1hem9uMRkwFwYDVQQDExBBbWF6b24g +Um9vdCBDQSAzMB4XDTE1MDUyNjAwMDAwMFoXDTQwMDUyNjAwMDAwMFowOTELMAkG +A1UEBhMCVVMxDzANBgNVBAoTBkFtYXpvbjEZMBcGA1UEAxMQQW1hem9uIFJvb3Qg +Q0EgMzBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABCmXp8ZBf8ANm+gBG1bG8lKl +ui2yEujSLtf6ycXYqm0fc4E7O5hrOXwzpcVOho6AF2hiRVd9RFgdszflZwjrZt6j +QjBAMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgGGMB0GA1UdDgQWBBSr +ttvXBp43rDCGB5Fwx5zEGbF4wDAKBggqhkjOPQQDAgNJADBGAiEA4IWSoxe3jfkr +BqWTrBqYaGFy+uGh0PsceGCmQ5nFuMQCIQCcAu/xlJyzlvnrxir4tiz+OpAUFteM +YyRIHN8wfdVoOw== +-----END CERTIFICATE----- + +# Issuer: CN=Amazon Root CA 4 O=Amazon +# Subject: CN=Amazon Root CA 4 O=Amazon +# Label: "Amazon Root CA 4" +# Serial: 143266989758080763974105200630763877849284878 +# MD5 Fingerprint: 89:bc:27:d5:eb:17:8d:06:6a:69:d5:fd:89:47:b4:cd +# SHA1 Fingerprint: f6:10:84:07:d6:f8:bb:67:98:0c:c2:e2:44:c2:eb:ae:1c:ef:63:be +# SHA256 Fingerprint: e3:5d:28:41:9e:d0:20:25:cf:a6:90:38:cd:62:39:62:45:8d:a5:c6:95:fb:de:a3:c2:2b:0b:fb:25:89:70:92 +-----BEGIN CERTIFICATE----- +MIIB8jCCAXigAwIBAgITBmyf18G7EEwpQ+Vxe3ssyBrBDjAKBggqhkjOPQQDAzA5 +MQswCQYDVQQGEwJVUzEPMA0GA1UEChMGQW1hem9uMRkwFwYDVQQDExBBbWF6b24g +Um9vdCBDQSA0MB4XDTE1MDUyNjAwMDAwMFoXDTQwMDUyNjAwMDAwMFowOTELMAkG +A1UEBhMCVVMxDzANBgNVBAoTBkFtYXpvbjEZMBcGA1UEAxMQQW1hem9uIFJvb3Qg +Q0EgNDB2MBAGByqGSM49AgEGBSuBBAAiA2IABNKrijdPo1MN/sGKe0uoe0ZLY7Bi +9i0b2whxIdIA6GO9mif78DluXeo9pcmBqqNbIJhFXRbb/egQbeOc4OO9X4Ri83Bk +M6DLJC9wuoihKqB1+IGuYgbEgds5bimwHvouXKNCMEAwDwYDVR0TAQH/BAUwAwEB +/zAOBgNVHQ8BAf8EBAMCAYYwHQYDVR0OBBYEFNPsxzplbszh2naaVvuc84ZtV+WB +MAoGCCqGSM49BAMDA2gAMGUCMDqLIfG9fhGt0O9Yli/W651+kI0rz2ZVwyzjKKlw +CkcO8DdZEv8tmZQoTipPNU0zWgIxAOp1AE47xDqUEpHJWEadIRNyp4iciuRMStuW +1KyLa2tJElMzrdfkviT8tQp21KW8EA== +-----END CERTIFICATE----- + +# Issuer: CN=LuxTrust Global Root 2 O=LuxTrust S.A. +# Subject: CN=LuxTrust Global Root 2 O=LuxTrust S.A. +# Label: "LuxTrust Global Root 2" +# Serial: 59914338225734147123941058376788110305822489521 +# MD5 Fingerprint: b2:e1:09:00:61:af:f7:f1:91:6f:c4:ad:8d:5e:3b:7c +# SHA1 Fingerprint: 1e:0e:56:19:0a:d1:8b:25:98:b2:04:44:ff:66:8a:04:17:99:5f:3f +# SHA256 Fingerprint: 54:45:5f:71:29:c2:0b:14:47:c4:18:f9:97:16:8f:24:c5:8f:c5:02:3b:f5:da:5b:e2:eb:6e:1d:d8:90:2e:d5 +-----BEGIN CERTIFICATE----- +MIIFwzCCA6ugAwIBAgIUCn6m30tEntpqJIWe5rgV0xZ/u7EwDQYJKoZIhvcNAQEL +BQAwRjELMAkGA1UEBhMCTFUxFjAUBgNVBAoMDUx1eFRydXN0IFMuQS4xHzAdBgNV +BAMMFkx1eFRydXN0IEdsb2JhbCBSb290IDIwHhcNMTUwMzA1MTMyMTU3WhcNMzUw +MzA1MTMyMTU3WjBGMQswCQYDVQQGEwJMVTEWMBQGA1UECgwNTHV4VHJ1c3QgUy5B +LjEfMB0GA1UEAwwWTHV4VHJ1c3QgR2xvYmFsIFJvb3QgMjCCAiIwDQYJKoZIhvcN +AQEBBQADggIPADCCAgoCggIBANeFl78RmOnwYoNMPIf5U2o3C/IPPIfOb9wmKb3F +ibrJgz337spbxm1Jc7TJRqMbNBM/wYlFV/TZsfs2ZUv7COJIcRHIbjuend+JZTem +hfY7RBi2xjcwYkSSl2l9QjAk5A0MiWtj3sXh306pFGxT4GHO9hcvHTy95iJMHZP1 +EMShduxq3sVs35a0VkBCwGKSMKEtFZSg0iAGCW5qbeXrt77U8PEVfIvmTroTzEsn +Xpk8F12PgX8zPU/TPxvsXD/wPEx1bvKm1Z3aLQdjAsZy6ZS8TEmVT4hSyNvoaYL4 +zDRbIvCGp4m9SAptZoFtyMhk+wHh9OHe2Z7d21vUKpkmFRseTJIpgp7VkoGSQXAZ +96Tlk0u8d2cx3Rz9MXANF5kM+Qw5GSoXtTBxVdUPrljhPS80m8+f9niFwpN6cj5m +j5wWEWCPnolvZ77gR1o7DJpni89Gxq44o/KnvObWhWszJHAiS8sIm7vI+AIpHb4g +DEa/a4ebsypmQjVGbKq6rfmYe+lQVRQxv7HaLe2ArWgk+2mr2HETMOZns4dA/Yl+ +8kPREd8vZS9kzl8UubG/Mb2HeFpZZYiq/FkySIbWTLkpS5XTdvN3JW1CHDiDTf2j +X5t/Lax5Gw5CMZdjpPuKadUiDTSQMC6otOBttpSsvItO13D8xTiOZCXhTTmQzsmH +hFhxAgMBAAGjgagwgaUwDwYDVR0TAQH/BAUwAwEB/zBCBgNVHSAEOzA5MDcGByuB +KwEBAQowLDAqBggrBgEFBQcCARYeaHR0cHM6Ly9yZXBvc2l0b3J5Lmx1eHRydXN0 +Lmx1MA4GA1UdDwEB/wQEAwIBBjAfBgNVHSMEGDAWgBT/GCh2+UgFLKGu8SsbK7JT ++Et8szAdBgNVHQ4EFgQU/xgodvlIBSyhrvErGyuyU/hLfLMwDQYJKoZIhvcNAQEL +BQADggIBAGoZFO1uecEsh9QNcH7X9njJCwROxLHOk3D+sFTAMs2ZMGQXvw/l4jP9 +BzZAcg4atmpZ1gDlaCDdLnINH2pkMSCEfUmmWjfrRcmF9dTHF5kH5ptV5AzoqbTO +jFu1EVzPig4N1qx3gf4ynCSecs5U89BvolbW7MM3LGVYvlcAGvI1+ut7MV3CwRI9 +loGIlonBWVx65n9wNOeD4rHh4bhY79SV5GCc8JaXcozrhAIuZY+kt9J/Z93I055c +qqmkoCUUBpvsT34tC38ddfEz2O3OuHVtPlu5mB0xDVbYQw8wkbIEa91WvpWAVWe+ +2M2D2RjuLg+GLZKecBPs3lHJQ3gCpU3I+V/EkVhGFndadKpAvAefMLmx9xIX3eP/ +JEAdemrRTxgKqpAd60Ae36EeRJIQmvKN4dFLRp7oRUKX6kWZ8+xm1QL68qZKJKre +zrnK+T+Tb/mjuuqlPpmt/f97mfVl7vBZKGfXkJWkE4SphMHozs51k2MavDzq1WQf +LSoSOcbDWjLtR5EWDrw4wVDej8oqkDQc7kGUnF4ZLvhFSZl0kbAEb+MEWrGrKqv+ +x9CWttrhSmQGbmBNvUJO/3jaJMobtNeWOWyu8Q6qp31IiyBMz2TWuJdGsE7RKlY6 +oJO9r4Ak4Ap+58rVyuiFVdw2KuGUaJPHZnJED4AhMmwlxyOAgwrr +-----END CERTIFICATE----- + +# Issuer: CN=TUBITAK Kamu SM SSL Kok Sertifikasi - Surum 1 O=Turkiye Bilimsel ve Teknolojik Arastirma Kurumu - TUBITAK OU=Kamu Sertifikasyon Merkezi - Kamu SM +# Subject: CN=TUBITAK Kamu SM SSL Kok Sertifikasi - Surum 1 O=Turkiye Bilimsel ve Teknolojik Arastirma Kurumu - TUBITAK OU=Kamu Sertifikasyon Merkezi - Kamu SM +# Label: "TUBITAK Kamu SM SSL Kok Sertifikasi - Surum 1" +# Serial: 1 +# MD5 Fingerprint: dc:00:81:dc:69:2f:3e:2f:b0:3b:f6:3d:5a:91:8e:49 +# SHA1 Fingerprint: 31:43:64:9b:ec:ce:27:ec:ed:3a:3f:0b:8f:0d:e4:e8:91:dd:ee:ca +# SHA256 Fingerprint: 46:ed:c3:68:90:46:d5:3a:45:3f:b3:10:4a:b8:0d:ca:ec:65:8b:26:60:ea:16:29:dd:7e:86:79:90:64:87:16 +-----BEGIN CERTIFICATE----- +MIIEYzCCA0ugAwIBAgIBATANBgkqhkiG9w0BAQsFADCB0jELMAkGA1UEBhMCVFIx +GDAWBgNVBAcTD0dlYnplIC0gS29jYWVsaTFCMEAGA1UEChM5VHVya2l5ZSBCaWxp +bXNlbCB2ZSBUZWtub2xvamlrIEFyYXN0aXJtYSBLdXJ1bXUgLSBUVUJJVEFLMS0w +KwYDVQQLEyRLYW11IFNlcnRpZmlrYXN5b24gTWVya2V6aSAtIEthbXUgU00xNjA0 +BgNVBAMTLVRVQklUQUsgS2FtdSBTTSBTU0wgS29rIFNlcnRpZmlrYXNpIC0gU3Vy +dW0gMTAeFw0xMzExMjUwODI1NTVaFw00MzEwMjUwODI1NTVaMIHSMQswCQYDVQQG +EwJUUjEYMBYGA1UEBxMPR2ViemUgLSBLb2NhZWxpMUIwQAYDVQQKEzlUdXJraXll +IEJpbGltc2VsIHZlIFRla25vbG9qaWsgQXJhc3Rpcm1hIEt1cnVtdSAtIFRVQklU +QUsxLTArBgNVBAsTJEthbXUgU2VydGlmaWthc3lvbiBNZXJrZXppIC0gS2FtdSBT +TTE2MDQGA1UEAxMtVFVCSVRBSyBLYW11IFNNIFNTTCBLb2sgU2VydGlmaWthc2kg +LSBTdXJ1bSAxMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAr3UwM6q7 +a9OZLBI3hNmNe5eA027n/5tQlT6QlVZC1xl8JoSNkvoBHToP4mQ4t4y86Ij5iySr +LqP1N+RAjhgleYN1Hzv/bKjFxlb4tO2KRKOrbEz8HdDc72i9z+SqzvBV96I01INr +N3wcwv61A+xXzry0tcXtAA9TNypN9E8Mg/uGz8v+jE69h/mniyFXnHrfA2eJLJ2X +YacQuFWQfw4tJzh03+f92k4S400VIgLI4OD8D62K18lUUMw7D8oWgITQUVbDjlZ/ +iSIzL+aFCr2lqBs23tPcLG07xxO9WSMs5uWk99gL7eqQQESolbuT1dCANLZGeA4f +AJNG4e7p+exPFwIDAQABo0IwQDAdBgNVHQ4EFgQUZT/HiobGPN08VFw1+DrtUgxH +V8gwDgYDVR0PAQH/BAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQEL +BQADggEBACo/4fEyjq7hmFxLXs9rHmoJ0iKpEsdeV31zVmSAhHqT5Am5EM2fKifh +AHe+SMg1qIGf5LgsyX8OsNJLN13qudULXjS99HMpw+0mFZx+CFOKWI3QSyjfwbPf +IPP54+M638yclNhOT8NrF7f3cuitZjO1JVOr4PhMqZ398g26rrnZqsZr+ZO7rqu4 +lzwDGrpDxpa5RXI4s6ehlj2Re37AIVNMh+3yC1SVUZPVIqUNivGTDj5UDrDYyU7c +8jEyVupk+eq1nRZmQnLzf9OxMUP8pI4X8W0jq5Rm+K37DwhuJi1/FwcJsoz7UMCf +lo3Ptv0AnVoUmr8CRPXBwp8iXqIPoeM= +-----END CERTIFICATE----- + +# Issuer: CN=GDCA TrustAUTH R5 ROOT O=GUANG DONG CERTIFICATE AUTHORITY CO.,LTD. +# Subject: CN=GDCA TrustAUTH R5 ROOT O=GUANG DONG CERTIFICATE AUTHORITY CO.,LTD. +# Label: "GDCA TrustAUTH R5 ROOT" +# Serial: 9009899650740120186 +# MD5 Fingerprint: 63:cc:d9:3d:34:35:5c:6f:53:a3:e2:08:70:48:1f:b4 +# SHA1 Fingerprint: 0f:36:38:5b:81:1a:25:c3:9b:31:4e:83:ca:e9:34:66:70:cc:74:b4 +# SHA256 Fingerprint: bf:ff:8f:d0:44:33:48:7d:6a:8a:a6:0c:1a:29:76:7a:9f:c2:bb:b0:5e:42:0f:71:3a:13:b9:92:89:1d:38:93 +-----BEGIN CERTIFICATE----- +MIIFiDCCA3CgAwIBAgIIfQmX/vBH6nowDQYJKoZIhvcNAQELBQAwYjELMAkGA1UE +BhMCQ04xMjAwBgNVBAoMKUdVQU5HIERPTkcgQ0VSVElGSUNBVEUgQVVUSE9SSVRZ +IENPLixMVEQuMR8wHQYDVQQDDBZHRENBIFRydXN0QVVUSCBSNSBST09UMB4XDTE0 +MTEyNjA1MTMxNVoXDTQwMTIzMTE1NTk1OVowYjELMAkGA1UEBhMCQ04xMjAwBgNV +BAoMKUdVQU5HIERPTkcgQ0VSVElGSUNBVEUgQVVUSE9SSVRZIENPLixMVEQuMR8w +HQYDVQQDDBZHRENBIFRydXN0QVVUSCBSNSBST09UMIICIjANBgkqhkiG9w0BAQEF +AAOCAg8AMIICCgKCAgEA2aMW8Mh0dHeb7zMNOwZ+Vfy1YI92hhJCfVZmPoiC7XJj +Dp6L3TQsAlFRwxn9WVSEyfFrs0yw6ehGXTjGoqcuEVe6ghWinI9tsJlKCvLriXBj +TnnEt1u9ol2x8kECK62pOqPseQrsXzrj/e+APK00mxqriCZ7VqKChh/rNYmDf1+u +KU49tm7srsHwJ5uu4/Ts765/94Y9cnrrpftZTqfrlYwiOXnhLQiPzLyRuEH3FMEj +qcOtmkVEs7LXLM3GKeJQEK5cy4KOFxg2fZfmiJqwTTQJ9Cy5WmYqsBebnh52nUpm +MUHfP/vFBu8btn4aRjb3ZGM74zkYI+dndRTVdVeSN72+ahsmUPI2JgaQxXABZG12 +ZuGR224HwGGALrIuL4xwp9E7PLOR5G62xDtw8mySlwnNR30YwPO7ng/Wi64HtloP +zgsMR6flPri9fcebNaBhlzpBdRfMK5Z3KpIhHtmVdiBnaM8Nvd/WHwlqmuLMc3Gk +L30SgLdTMEZeS1SZD2fJpcjyIMGC7J0R38IC+xo70e0gmu9lZJIQDSri3nDxGGeC +jGHeuLzRL5z7D9Ar7Rt2ueQ5Vfj4oR24qoAATILnsn8JuLwwoC8N9VKejveSswoA +HQBUlwbgsQfZxw9cZX08bVlX5O2ljelAU58VS6Bx9hoh49pwBiFYFIeFd3mqgnkC +AwEAAaNCMEAwHQYDVR0OBBYEFOLJQJ9NzuiaoXzPDj9lxSmIahlRMA8GA1UdEwEB +/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgGGMA0GCSqGSIb3DQEBCwUAA4ICAQDRSVfg +p8xoWLoBDysZzY2wYUWsEe1jUGn4H3++Fo/9nesLqjJHdtJnJO29fDMylyrHBYZm +DRd9FBUb1Ov9H5r2XpdptxolpAqzkT9fNqyL7FeoPueBihhXOYV0GkLH6VsTX4/5 +COmSdI31R9KrO9b7eGZONn356ZLpBN79SWP8bfsUcZNnL0dKt7n/HipzcEYwv1ry +L3ml4Y0M2fmyYzeMN2WFcGpcWwlyua1jPLHd+PwyvzeG5LuOmCd+uh8W4XAR8gPf +JWIyJyYYMoSf/wA6E7qaTfRPuBRwIrHKK5DOKcFw9C+df/KQHtZa37dG/OaG+svg +IHZ6uqbL9XzeYqWxi+7egmaKTjowHz+Ay60nugxe19CxVsp3cbK1daFQqUBDF8Io +2c9Si1vIY9RCPqAzekYu9wogRlR+ak8x8YF+QnQ4ZXMn7sZ8uI7XpTrXmKGcjBBV +09tL7ECQ8s1uV9JiDnxXk7Gnbc2dg7sq5+W2O3FYrf3RRbxake5TFW/TRQl1brqQ +XR4EzzffHqhmsYzmIGrv/EhOdJhCrylvLmrH+33RZjEizIYAfmaDDEL0vTSSwxrq +T8p+ck0LcIymSLumoRT2+1hEmRSuqguTaaApJUqlyyvdimYHFngVV3Eb7PVHhPOe +MTd61X8kreS8/f3MboPoDKi3QWwH3b08hpcv0g== +-----END CERTIFICATE----- + +# Issuer: CN=TrustCor RootCert CA-1 O=TrustCor Systems S. de R.L. OU=TrustCor Certificate Authority +# Subject: CN=TrustCor RootCert CA-1 O=TrustCor Systems S. de R.L. OU=TrustCor Certificate Authority +# Label: "TrustCor RootCert CA-1" +# Serial: 15752444095811006489 +# MD5 Fingerprint: 6e:85:f1:dc:1a:00:d3:22:d5:b2:b2:ac:6b:37:05:45 +# SHA1 Fingerprint: ff:bd:cd:e7:82:c8:43:5e:3c:6f:26:86:5c:ca:a8:3a:45:5b:c3:0a +# SHA256 Fingerprint: d4:0e:9c:86:cd:8f:e4:68:c1:77:69:59:f4:9e:a7:74:fa:54:86:84:b6:c4:06:f3:90:92:61:f4:dc:e2:57:5c +-----BEGIN CERTIFICATE----- +MIIEMDCCAxigAwIBAgIJANqb7HHzA7AZMA0GCSqGSIb3DQEBCwUAMIGkMQswCQYD +VQQGEwJQQTEPMA0GA1UECAwGUGFuYW1hMRQwEgYDVQQHDAtQYW5hbWEgQ2l0eTEk +MCIGA1UECgwbVHJ1c3RDb3IgU3lzdGVtcyBTLiBkZSBSLkwuMScwJQYDVQQLDB5U +cnVzdENvciBDZXJ0aWZpY2F0ZSBBdXRob3JpdHkxHzAdBgNVBAMMFlRydXN0Q29y +IFJvb3RDZXJ0IENBLTEwHhcNMTYwMjA0MTIzMjE2WhcNMjkxMjMxMTcyMzE2WjCB +pDELMAkGA1UEBhMCUEExDzANBgNVBAgMBlBhbmFtYTEUMBIGA1UEBwwLUGFuYW1h +IENpdHkxJDAiBgNVBAoMG1RydXN0Q29yIFN5c3RlbXMgUy4gZGUgUi5MLjEnMCUG +A1UECwweVHJ1c3RDb3IgQ2VydGlmaWNhdGUgQXV0aG9yaXR5MR8wHQYDVQQDDBZU +cnVzdENvciBSb290Q2VydCBDQS0xMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIB +CgKCAQEAv463leLCJhJrMxnHQFgKq1mqjQCj/IDHUHuO1CAmujIS2CNUSSUQIpid +RtLByZ5OGy4sDjjzGiVoHKZaBeYei0i/mJZ0PmnK6bV4pQa81QBeCQryJ3pS/C3V +seq0iWEk8xoT26nPUu0MJLq5nux+AHT6k61sKZKuUbS701e/s/OojZz0JEsq1pme +9J7+wH5COucLlVPat2gOkEz7cD+PSiyU8ybdY2mplNgQTsVHCJCZGxdNuWxu72CV +EY4hgLW9oHPY0LJ3xEXqWib7ZnZ2+AYfYW0PVcWDtxBWcgYHpfOxGgMFZA6dWorW +hnAbJN7+KIor0Gqw/Hqi3LJ5DotlDwIDAQABo2MwYTAdBgNVHQ4EFgQU7mtJPHo/ +DeOxCbeKyKsZn3MzUOcwHwYDVR0jBBgwFoAU7mtJPHo/DeOxCbeKyKsZn3MzUOcw +DwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAYYwDQYJKoZIhvcNAQELBQAD +ggEBACUY1JGPE+6PHh0RU9otRCkZoB5rMZ5NDp6tPVxBb5UrJKF5mDo4Nvu7Zp5I +/5CQ7z3UuJu0h3U/IJvOcs+hVcFNZKIZBqEHMwwLKeXx6quj7LUKdJDHfXLy11yf +ke+Ri7fc7Waiz45mO7yfOgLgJ90WmMCV1Aqk5IGadZQ1nJBfiDcGrVmVCrDRZ9MZ +yonnMlo2HD6CqFqTvsbQZJG2z9m2GM/bftJlo6bEjhcxwft+dtvTheNYsnd6djts +L1Ac59v2Z3kf9YKVmgenFK+P3CghZwnS1k1aHBkcjndcw5QkPTJrS37UeJSDvjdN +zl/HHk484IkzlQsPpTLWPFp5LBk= +-----END CERTIFICATE----- + +# Issuer: CN=TrustCor RootCert CA-2 O=TrustCor Systems S. de R.L. OU=TrustCor Certificate Authority +# Subject: CN=TrustCor RootCert CA-2 O=TrustCor Systems S. de R.L. OU=TrustCor Certificate Authority +# Label: "TrustCor RootCert CA-2" +# Serial: 2711694510199101698 +# MD5 Fingerprint: a2:e1:f8:18:0b:ba:45:d5:c7:41:2a:bb:37:52:45:64 +# SHA1 Fingerprint: b8:be:6d:cb:56:f1:55:b9:63:d4:12:ca:4e:06:34:c7:94:b2:1c:c0 +# SHA256 Fingerprint: 07:53:e9:40:37:8c:1b:d5:e3:83:6e:39:5d:ae:a5:cb:83:9e:50:46:f1:bd:0e:ae:19:51:cf:10:fe:c7:c9:65 +-----BEGIN CERTIFICATE----- +MIIGLzCCBBegAwIBAgIIJaHfyjPLWQIwDQYJKoZIhvcNAQELBQAwgaQxCzAJBgNV +BAYTAlBBMQ8wDQYDVQQIDAZQYW5hbWExFDASBgNVBAcMC1BhbmFtYSBDaXR5MSQw +IgYDVQQKDBtUcnVzdENvciBTeXN0ZW1zIFMuIGRlIFIuTC4xJzAlBgNVBAsMHlRy +dXN0Q29yIENlcnRpZmljYXRlIEF1dGhvcml0eTEfMB0GA1UEAwwWVHJ1c3RDb3Ig +Um9vdENlcnQgQ0EtMjAeFw0xNjAyMDQxMjMyMjNaFw0zNDEyMzExNzI2MzlaMIGk +MQswCQYDVQQGEwJQQTEPMA0GA1UECAwGUGFuYW1hMRQwEgYDVQQHDAtQYW5hbWEg +Q2l0eTEkMCIGA1UECgwbVHJ1c3RDb3IgU3lzdGVtcyBTLiBkZSBSLkwuMScwJQYD +VQQLDB5UcnVzdENvciBDZXJ0aWZpY2F0ZSBBdXRob3JpdHkxHzAdBgNVBAMMFlRy +dXN0Q29yIFJvb3RDZXJ0IENBLTIwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIK +AoICAQCnIG7CKqJiJJWQdsg4foDSq8GbZQWU9MEKENUCrO2fk8eHyLAnK0IMPQo+ +QVqedd2NyuCb7GgypGmSaIwLgQ5WoD4a3SwlFIIvl9NkRvRUqdw6VC0xK5mC8tkq +1+9xALgxpL56JAfDQiDyitSSBBtlVkxs1Pu2YVpHI7TYabS3OtB0PAx1oYxOdqHp +2yqlO/rOsP9+aij9JxzIsekp8VduZLTQwRVtDr4uDkbIXvRR/u8OYzo7cbrPb1nK +DOObXUm4TOJXsZiKQlecdu/vvdFoqNL0Cbt3Nb4lggjEFixEIFapRBF37120Hape +az6LMvYHL1cEksr1/p3C6eizjkxLAjHZ5DxIgif3GIJ2SDpxsROhOdUuxTTCHWKF +3wP+TfSvPd9cW436cOGlfifHhi5qjxLGhF5DUVCcGZt45vz27Ud+ez1m7xMTiF88 +oWP7+ayHNZ/zgp6kPwqcMWmLmaSISo5uZk3vFsQPeSghYA2FFn3XVDjxklb9tTNM +g9zXEJ9L/cb4Qr26fHMC4P99zVvh1Kxhe1fVSntb1IVYJ12/+CtgrKAmrhQhJ8Z3 +mjOAPF5GP/fDsaOGM8boXg25NSyqRsGFAnWAoOsk+xWq5Gd/bnc/9ASKL3x74xdh +8N0JqSDIvgmk0H5Ew7IwSjiqqewYmgeCK9u4nBit2uBGF6zPXQIDAQABo2MwYTAd +BgNVHQ4EFgQU2f4hQG6UnrybPZx9mCAZ5YwwYrIwHwYDVR0jBBgwFoAU2f4hQG6U +nrybPZx9mCAZ5YwwYrIwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAYYw +DQYJKoZIhvcNAQELBQADggIBAJ5Fngw7tu/hOsh80QA9z+LqBrWyOrsGS2h60COX +dKcs8AjYeVrXWoSK2BKaG9l9XE1wxaX5q+WjiYndAfrs3fnpkpfbsEZC89NiqpX+ +MWcUaViQCqoL7jcjx1BRtPV+nuN79+TMQjItSQzL/0kMmx40/W5ulop5A7Zv2wnL +/V9lFDfhOPXzYRZY5LVtDQsEGz9QLX+zx3oaFoBg+Iof6Rsqxvm6ARppv9JYx1RX +CI/hOWB3S6xZhBqI8d3LT3jX5+EzLfzuQfogsL7L9ziUwOHQhQ+77Sxzq+3+knYa +ZH9bDTMJBzN7Bj8RpFxwPIXAz+OQqIN3+tvmxYxoZxBnpVIt8MSZj3+/0WvitUfW +2dCFmU2Umw9Lje4AWkcdEQOsQRivh7dvDDqPys/cA8GiCcjl/YBeyGBCARsaU1q7 +N6a3vLqE6R5sGtRk2tRD/pOLS/IseRYQ1JMLiI+h2IYURpFHmygk71dSTlxCnKr3 +Sewn6EAes6aJInKc9Q0ztFijMDvd1GpUk74aTfOTlPf8hAs/hCBcNANExdqtvArB +As8e5ZTZ845b2EzwnexhF7sUMlQMAimTHpKG9n/v55IFDlndmQguLvqcAFLTxWYp +5KeXRKQOKIETNcX2b2TmQcTVL8w0RSXPQQCWPUouwpaYT05KnJe32x+SMsj/D1Fu +1uwJ +-----END CERTIFICATE----- + +# Issuer: CN=TrustCor ECA-1 O=TrustCor Systems S. de R.L. OU=TrustCor Certificate Authority +# Subject: CN=TrustCor ECA-1 O=TrustCor Systems S. de R.L. OU=TrustCor Certificate Authority +# Label: "TrustCor ECA-1" +# Serial: 9548242946988625984 +# MD5 Fingerprint: 27:92:23:1d:0a:f5:40:7c:e9:e6:6b:9d:d8:f5:e7:6c +# SHA1 Fingerprint: 58:d1:df:95:95:67:6b:63:c0:f0:5b:1c:17:4d:8b:84:0b:c8:78:bd +# SHA256 Fingerprint: 5a:88:5d:b1:9c:01:d9:12:c5:75:93:88:93:8c:af:bb:df:03:1a:b2:d4:8e:91:ee:15:58:9b:42:97:1d:03:9c +-----BEGIN CERTIFICATE----- +MIIEIDCCAwigAwIBAgIJAISCLF8cYtBAMA0GCSqGSIb3DQEBCwUAMIGcMQswCQYD +VQQGEwJQQTEPMA0GA1UECAwGUGFuYW1hMRQwEgYDVQQHDAtQYW5hbWEgQ2l0eTEk +MCIGA1UECgwbVHJ1c3RDb3IgU3lzdGVtcyBTLiBkZSBSLkwuMScwJQYDVQQLDB5U +cnVzdENvciBDZXJ0aWZpY2F0ZSBBdXRob3JpdHkxFzAVBgNVBAMMDlRydXN0Q29y +IEVDQS0xMB4XDTE2MDIwNDEyMzIzM1oXDTI5MTIzMTE3MjgwN1owgZwxCzAJBgNV +BAYTAlBBMQ8wDQYDVQQIDAZQYW5hbWExFDASBgNVBAcMC1BhbmFtYSBDaXR5MSQw +IgYDVQQKDBtUcnVzdENvciBTeXN0ZW1zIFMuIGRlIFIuTC4xJzAlBgNVBAsMHlRy +dXN0Q29yIENlcnRpZmljYXRlIEF1dGhvcml0eTEXMBUGA1UEAwwOVHJ1c3RDb3Ig +RUNBLTEwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDPj+ARtZ+odnbb +3w9U73NjKYKtR8aja+3+XzP4Q1HpGjORMRegdMTUpwHmspI+ap3tDvl0mEDTPwOA +BoJA6LHip1GnHYMma6ve+heRK9jGrB6xnhkB1Zem6g23xFUfJ3zSCNV2HykVh0A5 +3ThFEXXQmqc04L/NyFIduUd+Dbi7xgz2c1cWWn5DkR9VOsZtRASqnKmcp0yJF4Ou +owReUoCLHhIlERnXDH19MURB6tuvsBzvgdAsxZohmz3tQjtQJvLsznFhBmIhVE5/ +wZ0+fyCMgMsq2JdiyIMzkX2woloPV+g7zPIlstR8L+xNxqE6FXrntl019fZISjZF +ZtS6mFjBAgMBAAGjYzBhMB0GA1UdDgQWBBREnkj1zG1I1KBLf/5ZJC+Dl5mahjAf +BgNVHSMEGDAWgBREnkj1zG1I1KBLf/5ZJC+Dl5mahjAPBgNVHRMBAf8EBTADAQH/ +MA4GA1UdDwEB/wQEAwIBhjANBgkqhkiG9w0BAQsFAAOCAQEABT41XBVwm8nHc2Fv +civUwo/yQ10CzsSUuZQRg2dd4mdsdXa/uwyqNsatR5Nj3B5+1t4u/ukZMjgDfxT2 +AHMsWbEhBuH7rBiVDKP/mZb3Kyeb1STMHd3BOuCYRLDE5D53sXOpZCz2HAF8P11F +hcCF5yWPldwX8zyfGm6wyuMdKulMY/okYWLW2n62HGz1Ah3UKt1VkOsqEUc8Ll50 +soIipX1TH0XsJ5F95yIW6MBoNtjG8U+ARDL54dHRHareqKucBK+tIA5kmE2la8BI +WJZpTdwHjFGTot+fDz2LYLSCjaoITmJF4PkL0uDgPFveXHEnJcLmA4GLEFPjx1Wi +tJ/X5g== +-----END CERTIFICATE----- + +# Issuer: CN=SSL.com Root Certification Authority RSA O=SSL Corporation +# Subject: CN=SSL.com Root Certification Authority RSA O=SSL Corporation +# Label: "SSL.com Root Certification Authority RSA" +# Serial: 8875640296558310041 +# MD5 Fingerprint: 86:69:12:c0:70:f1:ec:ac:ac:c2:d5:bc:a5:5b:a1:29 +# SHA1 Fingerprint: b7:ab:33:08:d1:ea:44:77:ba:14:80:12:5a:6f:bd:a9:36:49:0c:bb +# SHA256 Fingerprint: 85:66:6a:56:2e:e0:be:5c:e9:25:c1:d8:89:0a:6f:76:a8:7e:c1:6d:4d:7d:5f:29:ea:74:19:cf:20:12:3b:69 +-----BEGIN CERTIFICATE----- +MIIF3TCCA8WgAwIBAgIIeyyb0xaAMpkwDQYJKoZIhvcNAQELBQAwfDELMAkGA1UE +BhMCVVMxDjAMBgNVBAgMBVRleGFzMRAwDgYDVQQHDAdIb3VzdG9uMRgwFgYDVQQK +DA9TU0wgQ29ycG9yYXRpb24xMTAvBgNVBAMMKFNTTC5jb20gUm9vdCBDZXJ0aWZp +Y2F0aW9uIEF1dGhvcml0eSBSU0EwHhcNMTYwMjEyMTczOTM5WhcNNDEwMjEyMTcz +OTM5WjB8MQswCQYDVQQGEwJVUzEOMAwGA1UECAwFVGV4YXMxEDAOBgNVBAcMB0hv +dXN0b24xGDAWBgNVBAoMD1NTTCBDb3Jwb3JhdGlvbjExMC8GA1UEAwwoU1NMLmNv +bSBSb290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5IFJTQTCCAiIwDQYJKoZIhvcN +AQEBBQADggIPADCCAgoCggIBAPkP3aMrfcvQKv7sZ4Wm5y4bunfh4/WvpOz6Sl2R +xFdHaxh3a3by/ZPkPQ/CFp4LZsNWlJ4Xg4XOVu/yFv0AYvUiCVToZRdOQbngT0aX +qhvIuG5iXmmxX9sqAn78bMrzQdjt0Oj8P2FI7bADFB0QDksZ4LtO7IZl/zbzXmcC +C52GVWH9ejjt/uIZALdvoVBidXQ8oPrIJZK0bnoix/geoeOy3ZExqysdBP+lSgQ3 +6YWkMyv94tZVNHwZpEpox7Ko07fKoZOI68GXvIz5HdkihCR0xwQ9aqkpk8zruFvh +/l8lqjRYyMEjVJ0bmBHDOJx+PYZspQ9AhnwC9FwCTyjLrnGfDzrIM/4RJTXq/LrF +YD3ZfBjVsqnTdXgDciLKOsMf7yzlLqn6niy2UUb9rwPW6mBo6oUWNmuF6R7As93E +JNyAKoFBbZQ+yODJgUEAnl6/f8UImKIYLEJAs/lvOCdLToD0PYFH4Ih86hzOtXVc +US4cK38acijnALXRdMbX5J+tB5O2UzU1/Dfkw/ZdFr4hc96SCvigY2q8lpJqPvi8 +ZVWb3vUNiSYE/CUapiVpy8JtynziWV+XrOvvLsi81xtZPCvM8hnIk2snYxnP/Okm ++Mpxm3+T/jRnhE6Z6/yzeAkzcLpmpnbtG3PrGqUNxCITIJRWCk4sbE6x/c+cCbqi +M+2HAgMBAAGjYzBhMB0GA1UdDgQWBBTdBAkHovV6fVJTEpKV7jiAJQ2mWTAPBgNV +HRMBAf8EBTADAQH/MB8GA1UdIwQYMBaAFN0ECQei9Xp9UlMSkpXuOIAlDaZZMA4G +A1UdDwEB/wQEAwIBhjANBgkqhkiG9w0BAQsFAAOCAgEAIBgRlCn7Jp0cHh5wYfGV +cpNxJK1ok1iOMq8bs3AD/CUrdIWQPXhq9LmLpZc7tRiRux6n+UBbkflVma8eEdBc +Hadm47GUBwwyOabqG7B52B2ccETjit3E+ZUfijhDPwGFpUenPUayvOUiaPd7nNgs +PgohyC0zrL/FgZkxdMF1ccW+sfAjRfSda/wZY52jvATGGAslu1OJD7OAUN5F7kR/ +q5R4ZJjT9ijdh9hwZXT7DrkT66cPYakylszeu+1jTBi7qUD3oFRuIIhxdRjqerQ0 +cuAjJ3dctpDqhiVAq+8zD8ufgr6iIPv2tS0a5sKFsXQP+8hlAqRSAUfdSSLBv9jr +a6x+3uxjMxW3IwiPxg+NQVrdjsW5j+VFP3jbutIbQLH+cU0/4IGiul607BXgk90I +H37hVZkLId6Tngr75qNJvTYw/ud3sqB1l7UtgYgXZSD32pAAn8lSzDLKNXz1PQ/Y +K9f1JmzJBjSWFupwWRoyeXkLtoh/D1JIPb9s2KJELtFOt3JY04kTlf5Eq/jXixtu +nLwsoFvVagCvXzfh1foQC5ichucmj87w7G6KVwuA406ywKBjYZC6VWg3dGq2ktuf +oYYitmUnDuy2n0Jg5GfCtdpBC8TTi2EbvPofkSvXRAdeuims2cXp71NIWuuA8ShY +Ic2wBlX7Jz9TkHCpBB5XJ7k= +-----END CERTIFICATE----- + +# Issuer: CN=SSL.com Root Certification Authority ECC O=SSL Corporation +# Subject: CN=SSL.com Root Certification Authority ECC O=SSL Corporation +# Label: "SSL.com Root Certification Authority ECC" +# Serial: 8495723813297216424 +# MD5 Fingerprint: 2e:da:e4:39:7f:9c:8f:37:d1:70:9f:26:17:51:3a:8e +# SHA1 Fingerprint: c3:19:7c:39:24:e6:54:af:1b:c4:ab:20:95:7a:e2:c3:0e:13:02:6a +# SHA256 Fingerprint: 34:17:bb:06:cc:60:07:da:1b:96:1c:92:0b:8a:b4:ce:3f:ad:82:0e:4a:a3:0b:9a:cb:c4:a7:4e:bd:ce:bc:65 +-----BEGIN CERTIFICATE----- +MIICjTCCAhSgAwIBAgIIdebfy8FoW6gwCgYIKoZIzj0EAwIwfDELMAkGA1UEBhMC +VVMxDjAMBgNVBAgMBVRleGFzMRAwDgYDVQQHDAdIb3VzdG9uMRgwFgYDVQQKDA9T +U0wgQ29ycG9yYXRpb24xMTAvBgNVBAMMKFNTTC5jb20gUm9vdCBDZXJ0aWZpY2F0 +aW9uIEF1dGhvcml0eSBFQ0MwHhcNMTYwMjEyMTgxNDAzWhcNNDEwMjEyMTgxNDAz +WjB8MQswCQYDVQQGEwJVUzEOMAwGA1UECAwFVGV4YXMxEDAOBgNVBAcMB0hvdXN0 +b24xGDAWBgNVBAoMD1NTTCBDb3Jwb3JhdGlvbjExMC8GA1UEAwwoU1NMLmNvbSBS +b290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5IEVDQzB2MBAGByqGSM49AgEGBSuB +BAAiA2IABEVuqVDEpiM2nl8ojRfLliJkP9x6jh3MCLOicSS6jkm5BBtHllirLZXI +7Z4INcgn64mMU1jrYor+8FsPazFSY0E7ic3s7LaNGdM0B9y7xgZ/wkWV7Mt/qCPg +CemB+vNH06NjMGEwHQYDVR0OBBYEFILRhXMw5zUE044CkvvlpNHEIejNMA8GA1Ud +EwEB/wQFMAMBAf8wHwYDVR0jBBgwFoAUgtGFczDnNQTTjgKS++Wk0cQh6M0wDgYD +VR0PAQH/BAQDAgGGMAoGCCqGSM49BAMCA2cAMGQCMG/n61kRpGDPYbCWe+0F+S8T +kdzt5fxQaxFGRrMcIQBiu77D5+jNB5n5DQtdcj7EqgIwH7y6C+IwJPt8bYBVCpk+ +gA0z5Wajs6O7pdWLjwkspl1+4vAHCGht0nxpbl/f5Wpl +-----END CERTIFICATE----- + +# Issuer: CN=SSL.com EV Root Certification Authority RSA R2 O=SSL Corporation +# Subject: CN=SSL.com EV Root Certification Authority RSA R2 O=SSL Corporation +# Label: "SSL.com EV Root Certification Authority RSA R2" +# Serial: 6248227494352943350 +# MD5 Fingerprint: e1:1e:31:58:1a:ae:54:53:02:f6:17:6a:11:7b:4d:95 +# SHA1 Fingerprint: 74:3a:f0:52:9b:d0:32:a0:f4:4a:83:cd:d4:ba:a9:7b:7c:2e:c4:9a +# SHA256 Fingerprint: 2e:7b:f1:6c:c2:24:85:a7:bb:e2:aa:86:96:75:07:61:b0:ae:39:be:3b:2f:e9:d0:cc:6d:4e:f7:34:91:42:5c +-----BEGIN CERTIFICATE----- +MIIF6zCCA9OgAwIBAgIIVrYpzTS8ePYwDQYJKoZIhvcNAQELBQAwgYIxCzAJBgNV +BAYTAlVTMQ4wDAYDVQQIDAVUZXhhczEQMA4GA1UEBwwHSG91c3RvbjEYMBYGA1UE +CgwPU1NMIENvcnBvcmF0aW9uMTcwNQYDVQQDDC5TU0wuY29tIEVWIFJvb3QgQ2Vy +dGlmaWNhdGlvbiBBdXRob3JpdHkgUlNBIFIyMB4XDTE3MDUzMTE4MTQzN1oXDTQy +MDUzMDE4MTQzN1owgYIxCzAJBgNVBAYTAlVTMQ4wDAYDVQQIDAVUZXhhczEQMA4G +A1UEBwwHSG91c3RvbjEYMBYGA1UECgwPU1NMIENvcnBvcmF0aW9uMTcwNQYDVQQD +DC5TU0wuY29tIEVWIFJvb3QgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkgUlNBIFIy +MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAjzZlQOHWTcDXtOlG2mvq +M0fNTPl9fb69LT3w23jhhqXZuglXaO1XPqDQCEGD5yhBJB/jchXQARr7XnAjssuf +OePPxU7Gkm0mxnu7s9onnQqG6YE3Bf7wcXHswxzpY6IXFJ3vG2fThVUCAtZJycxa +4bH3bzKfydQ7iEGonL3Lq9ttewkfokxykNorCPzPPFTOZw+oz12WGQvE43LrrdF9 +HSfvkusQv1vrO6/PgN3B0pYEW3p+pKk8OHakYo6gOV7qd89dAFmPZiw+B6KjBSYR +aZfqhbcPlgtLyEDhULouisv3D5oi53+aNxPN8k0TayHRwMwi8qFG9kRpnMphNQcA +b9ZhCBHqurj26bNg5U257J8UZslXWNvNh2n4ioYSA0e/ZhN2rHd9NCSFg83XqpyQ +Gp8hLH94t2S42Oim9HizVcuE0jLEeK6jj2HdzghTreyI/BXkmg3mnxp3zkyPuBQV +PWKchjgGAGYS5Fl2WlPAApiiECtoRHuOec4zSnaqW4EWG7WK2NAAe15itAnWhmMO +pgWVSbooi4iTsjQc2KRVbrcc0N6ZVTsj9CLg+SlmJuwgUHfbSguPvuUCYHBBXtSu +UDkiFCbLsjtzdFVHB3mBOagwE0TlBIqulhMlQg+5U8Sb/M3kHN48+qvWBkofZ6aY +MBzdLNvcGJVXZsb/XItW9XcCAwEAAaNjMGEwDwYDVR0TAQH/BAUwAwEB/zAfBgNV +HSMEGDAWgBT5YLvU49U09rj1BoAlp3PbRmmonjAdBgNVHQ4EFgQU+WC71OPVNPa4 +9QaAJadz20ZpqJ4wDgYDVR0PAQH/BAQDAgGGMA0GCSqGSIb3DQEBCwUAA4ICAQBW +s47LCp1Jjr+kxJG7ZhcFUZh1++VQLHqe8RT6q9OKPv+RKY9ji9i0qVQBDb6Thi/5 +Sm3HXvVX+cpVHBK+Rw82xd9qt9t1wkclf7nxY/hoLVUE0fKNsKTPvDxeH3jnpaAg +cLAExbf3cqfeIg29MyVGjGSSJuM+LmOW2puMPfgYCdcDzH2GguDKBAdRUNf/ktUM +79qGn5nX67evaOI5JpS6aLe/g9Pqemc9YmeuJeVy6OLk7K4S9ksrPJ/psEDzOFSz +/bdoyNrGj1E8svuR3Bznm53htw1yj+KkxKl4+esUrMZDBcJlOSgYAsOCsp0FvmXt +ll9ldDz7CTUue5wT/RsPXcdtgTpWD8w74a8CLyKsRspGPKAcTNZEtF4uXBVmCeEm +Kf7GUmG6sXP/wwyc5WxqlD8UykAWlYTzWamsX0xhk23RO8yilQwipmdnRC652dKK +QbNmC1r7fSOl8hqw/96bg5Qu0T/fkreRrwU7ZcegbLHNYhLDkBvjJc40vG93drEQ +w/cFGsDWr3RiSBd3kmmQYRzelYB0VI8YHMPzA9C/pEN1hlMYegouCRw2n5H9gooi +S9EOUCXdywMMF8mDAAhONU2Ki+3wApRmLER/y5UnlhetCTCstnEXbosX9hwJ1C07 +mKVx01QT2WDz9UtmT/rx7iASjbSsV7FFY6GsdqnC+w== +-----END CERTIFICATE----- + +# Issuer: CN=SSL.com EV Root Certification Authority ECC O=SSL Corporation +# Subject: CN=SSL.com EV Root Certification Authority ECC O=SSL Corporation +# Label: "SSL.com EV Root Certification Authority ECC" +# Serial: 3182246526754555285 +# MD5 Fingerprint: 59:53:22:65:83:42:01:54:c0:ce:42:b9:5a:7c:f2:90 +# SHA1 Fingerprint: 4c:dd:51:a3:d1:f5:20:32:14:b0:c6:c5:32:23:03:91:c7:46:42:6d +# SHA256 Fingerprint: 22:a2:c1:f7:bd:ed:70:4c:c1:e7:01:b5:f4:08:c3:10:88:0f:e9:56:b5:de:2a:4a:44:f9:9c:87:3a:25:a7:c8 +-----BEGIN CERTIFICATE----- +MIIClDCCAhqgAwIBAgIILCmcWxbtBZUwCgYIKoZIzj0EAwIwfzELMAkGA1UEBhMC +VVMxDjAMBgNVBAgMBVRleGFzMRAwDgYDVQQHDAdIb3VzdG9uMRgwFgYDVQQKDA9T +U0wgQ29ycG9yYXRpb24xNDAyBgNVBAMMK1NTTC5jb20gRVYgUm9vdCBDZXJ0aWZp +Y2F0aW9uIEF1dGhvcml0eSBFQ0MwHhcNMTYwMjEyMTgxNTIzWhcNNDEwMjEyMTgx +NTIzWjB/MQswCQYDVQQGEwJVUzEOMAwGA1UECAwFVGV4YXMxEDAOBgNVBAcMB0hv +dXN0b24xGDAWBgNVBAoMD1NTTCBDb3Jwb3JhdGlvbjE0MDIGA1UEAwwrU1NMLmNv +bSBFViBSb290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5IEVDQzB2MBAGByqGSM49 +AgEGBSuBBAAiA2IABKoSR5CYG/vvw0AHgyBO8TCCogbR8pKGYfL2IWjKAMTH6kMA +VIbc/R/fALhBYlzccBYy3h+Z1MzFB8gIH2EWB1E9fVwHU+M1OIzfzZ/ZLg1Kthku +WnBaBu2+8KGwytAJKaNjMGEwHQYDVR0OBBYEFFvKXuXe0oGqzagtZFG22XKbl+ZP +MA8GA1UdEwEB/wQFMAMBAf8wHwYDVR0jBBgwFoAUW8pe5d7SgarNqC1kUbbZcpuX +5k8wDgYDVR0PAQH/BAQDAgGGMAoGCCqGSM49BAMCA2gAMGUCMQCK5kCJN+vp1RPZ +ytRrJPOwPYdGWBrssd9v+1a6cGvHOMzosYxPD/fxZ3YOg9AeUY8CMD32IygmTMZg +h5Mmm7I1HrrW9zzRHM76JTymGoEVW/MSD2zuZYrJh6j5B+BimoxcSg== +-----END CERTIFICATE----- + +# Issuer: CN=GlobalSign O=GlobalSign OU=GlobalSign Root CA - R6 +# Subject: CN=GlobalSign O=GlobalSign OU=GlobalSign Root CA - R6 +# Label: "GlobalSign Root CA - R6" +# Serial: 1417766617973444989252670301619537 +# MD5 Fingerprint: 4f:dd:07:e4:d4:22:64:39:1e:0c:37:42:ea:d1:c6:ae +# SHA1 Fingerprint: 80:94:64:0e:b5:a7:a1:ca:11:9c:1f:dd:d5:9f:81:02:63:a7:fb:d1 +# SHA256 Fingerprint: 2c:ab:ea:fe:37:d0:6c:a2:2a:ba:73:91:c0:03:3d:25:98:29:52:c4:53:64:73:49:76:3a:3a:b5:ad:6c:cf:69 +-----BEGIN CERTIFICATE----- +MIIFgzCCA2ugAwIBAgIORea7A4Mzw4VlSOb/RVEwDQYJKoZIhvcNAQEMBQAwTDEg +MB4GA1UECxMXR2xvYmFsU2lnbiBSb290IENBIC0gUjYxEzARBgNVBAoTCkdsb2Jh +bFNpZ24xEzARBgNVBAMTCkdsb2JhbFNpZ24wHhcNMTQxMjEwMDAwMDAwWhcNMzQx +MjEwMDAwMDAwWjBMMSAwHgYDVQQLExdHbG9iYWxTaWduIFJvb3QgQ0EgLSBSNjET +MBEGA1UEChMKR2xvYmFsU2lnbjETMBEGA1UEAxMKR2xvYmFsU2lnbjCCAiIwDQYJ +KoZIhvcNAQEBBQADggIPADCCAgoCggIBAJUH6HPKZvnsFMp7PPcNCPG0RQssgrRI +xutbPK6DuEGSMxSkb3/pKszGsIhrxbaJ0cay/xTOURQh7ErdG1rG1ofuTToVBu1k +ZguSgMpE3nOUTvOniX9PeGMIyBJQbUJmL025eShNUhqKGoC3GYEOfsSKvGRMIRxD +aNc9PIrFsmbVkJq3MQbFvuJtMgamHvm566qjuL++gmNQ0PAYid/kD3n16qIfKtJw +LnvnvJO7bVPiSHyMEAc4/2ayd2F+4OqMPKq0pPbzlUoSB239jLKJz9CgYXfIWHSw +1CM69106yqLbnQneXUQtkPGBzVeS+n68UARjNN9rkxi+azayOeSsJDa38O+2HBNX +k7besvjihbdzorg1qkXy4J02oW9UivFyVm4uiMVRQkQVlO6jxTiWm05OWgtH8wY2 +SXcwvHE35absIQh1/OZhFj931dmRl4QKbNQCTXTAFO39OfuD8l4UoQSwC+n+7o/h +bguyCLNhZglqsQY6ZZZZwPA1/cnaKI0aEYdwgQqomnUdnjqGBQCe24DWJfncBZ4n +WUx2OVvq+aWh2IMP0f/fMBH5hc8zSPXKbWQULHpYT9NLCEnFlWQaYw55PfWzjMpY +rZxCRXluDocZXFSxZba/jJvcE+kNb7gu3GduyYsRtYQUigAZcIN5kZeR1Bonvzce +MgfYFGM8KEyvAgMBAAGjYzBhMA4GA1UdDwEB/wQEAwIBBjAPBgNVHRMBAf8EBTAD +AQH/MB0GA1UdDgQWBBSubAWjkxPioufi1xzWx/B/yGdToDAfBgNVHSMEGDAWgBSu +bAWjkxPioufi1xzWx/B/yGdToDANBgkqhkiG9w0BAQwFAAOCAgEAgyXt6NH9lVLN +nsAEoJFp5lzQhN7craJP6Ed41mWYqVuoPId8AorRbrcWc+ZfwFSY1XS+wc3iEZGt +Ixg93eFyRJa0lV7Ae46ZeBZDE1ZXs6KzO7V33EByrKPrmzU+sQghoefEQzd5Mr61 +55wsTLxDKZmOMNOsIeDjHfrYBzN2VAAiKrlNIC5waNrlU/yDXNOd8v9EDERm8tLj +vUYAGm0CuiVdjaExUd1URhxN25mW7xocBFymFe944Hn+Xds+qkxV/ZoVqW/hpvvf +cDDpw+5CRu3CkwWJ+n1jez/QcYF8AOiYrg54NMMl+68KnyBr3TsTjxKM4kEaSHpz +oHdpx7Zcf4LIHv5YGygrqGytXm3ABdJ7t+uA/iU3/gKbaKxCXcPu9czc8FB10jZp +nOZ7BN9uBmm23goJSFmH63sUYHpkqmlD75HHTOwY3WzvUy2MmeFe8nI+z1TIvWfs +pA9MRf/TuTAjB0yPEL+GltmZWrSZVxykzLsViVO6LAUP5MSeGbEYNNVMnbrt9x+v +JJUEeKgDu+6B5dpffItKoZB0JaezPkvILFa9x8jvOOJckvB595yEunQtYQEgfn7R +8k8HWV+LLUNS60YMlOH1Zkd5d9VUWx+tJDfLRVpOoERIyNiwmcUVhAn21klJwGW4 +5hpxbqCo8YLoRT5s1gLXCmeDBVrJpBA= +-----END CERTIFICATE----- + +# Issuer: CN=OISTE WISeKey Global Root GC CA O=WISeKey OU=OISTE Foundation Endorsed +# Subject: CN=OISTE WISeKey Global Root GC CA O=WISeKey OU=OISTE Foundation Endorsed +# Label: "OISTE WISeKey Global Root GC CA" +# Serial: 44084345621038548146064804565436152554 +# MD5 Fingerprint: a9:d6:b9:2d:2f:93:64:f8:a5:69:ca:91:e9:68:07:23 +# SHA1 Fingerprint: e0:11:84:5e:34:de:be:88:81:b9:9c:f6:16:26:d1:96:1f:c3:b9:31 +# SHA256 Fingerprint: 85:60:f9:1c:36:24:da:ba:95:70:b5:fe:a0:db:e3:6f:f1:1a:83:23:be:94:86:85:4f:b3:f3:4a:55:71:19:8d +-----BEGIN CERTIFICATE----- +MIICaTCCAe+gAwIBAgIQISpWDK7aDKtARb8roi066jAKBggqhkjOPQQDAzBtMQsw +CQYDVQQGEwJDSDEQMA4GA1UEChMHV0lTZUtleTEiMCAGA1UECxMZT0lTVEUgRm91 +bmRhdGlvbiBFbmRvcnNlZDEoMCYGA1UEAxMfT0lTVEUgV0lTZUtleSBHbG9iYWwg +Um9vdCBHQyBDQTAeFw0xNzA1MDkwOTQ4MzRaFw00MjA1MDkwOTU4MzNaMG0xCzAJ +BgNVBAYTAkNIMRAwDgYDVQQKEwdXSVNlS2V5MSIwIAYDVQQLExlPSVNURSBGb3Vu +ZGF0aW9uIEVuZG9yc2VkMSgwJgYDVQQDEx9PSVNURSBXSVNlS2V5IEdsb2JhbCBS +b290IEdDIENBMHYwEAYHKoZIzj0CAQYFK4EEACIDYgAETOlQwMYPchi82PG6s4ni +eUqjFqdrVCTbUf/q9Akkwwsin8tqJ4KBDdLArzHkdIJuyiXZjHWd8dvQmqJLIX4W +p2OQ0jnUsYd4XxiWD1AbNTcPasbc2RNNpI6QN+a9WzGRo1QwUjAOBgNVHQ8BAf8E +BAMCAQYwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUSIcUrOPDnpBgOtfKie7T +rYy0UGYwEAYJKwYBBAGCNxUBBAMCAQAwCgYIKoZIzj0EAwMDaAAwZQIwJsdpW9zV +57LnyAyMjMPdeYwbY9XJUpROTYJKcx6ygISpJcBMWm1JKWB4E+J+SOtkAjEA2zQg +Mgj/mkkCtojeFK9dbJlxjRo/i9fgojaGHAeCOnZT/cKi7e97sIBPWA9LUzm9 +-----END CERTIFICATE----- + +# Issuer: CN=GTS Root R1 O=Google Trust Services LLC +# Subject: CN=GTS Root R1 O=Google Trust Services LLC +# Label: "GTS Root R1" +# Serial: 146587175971765017618439757810265552097 +# MD5 Fingerprint: 82:1a:ef:d4:d2:4a:f2:9f:e2:3d:97:06:14:70:72:85 +# SHA1 Fingerprint: e1:c9:50:e6:ef:22:f8:4c:56:45:72:8b:92:20:60:d7:d5:a7:a3:e8 +# SHA256 Fingerprint: 2a:57:54:71:e3:13:40:bc:21:58:1c:bd:2c:f1:3e:15:84:63:20:3e:ce:94:bc:f9:d3:cc:19:6b:f0:9a:54:72 +-----BEGIN CERTIFICATE----- +MIIFWjCCA0KgAwIBAgIQbkepxUtHDA3sM9CJuRz04TANBgkqhkiG9w0BAQwFADBH +MQswCQYDVQQGEwJVUzEiMCAGA1UEChMZR29vZ2xlIFRydXN0IFNlcnZpY2VzIExM +QzEUMBIGA1UEAxMLR1RTIFJvb3QgUjEwHhcNMTYwNjIyMDAwMDAwWhcNMzYwNjIy +MDAwMDAwWjBHMQswCQYDVQQGEwJVUzEiMCAGA1UEChMZR29vZ2xlIFRydXN0IFNl +cnZpY2VzIExMQzEUMBIGA1UEAxMLR1RTIFJvb3QgUjEwggIiMA0GCSqGSIb3DQEB +AQUAA4ICDwAwggIKAoICAQC2EQKLHuOhd5s73L+UPreVp0A8of2C+X0yBoJx9vaM +f/vo27xqLpeXo4xL+Sv2sfnOhB2x+cWX3u+58qPpvBKJXqeqUqv4IyfLpLGcY9vX +mX7wCl7raKb0xlpHDU0QM+NOsROjyBhsS+z8CZDfnWQpJSMHobTSPS5g4M/SCYe7 +zUjwTcLCeoiKu7rPWRnWr4+wB7CeMfGCwcDfLqZtbBkOtdh+JhpFAz2weaSUKK0P +fyblqAj+lug8aJRT7oM6iCsVlgmy4HqMLnXWnOunVmSPlk9orj2XwoSPwLxAwAtc +vfaHszVsrBhQf4TgTM2S0yDpM7xSma8ytSmzJSq0SPly4cpk9+aCEI3oncKKiPo4 +Zor8Y/kB+Xj9e1x3+naH+uzfsQ55lVe0vSbv1gHR6xYKu44LtcXFilWr06zqkUsp +zBmkMiVOKvFlRNACzqrOSbTqn3yDsEB750Orp2yjj32JgfpMpf/VjsPOS+C12LOO +Rc92wO1AK/1TD7Cn1TsNsYqiA94xrcx36m97PtbfkSIS5r762DL8EGMUUXLeXdYW +k70paDPvOmbsB4om3xPXV2V4J95eSRQAogB/mqghtqmxlbCluQ0WEdrHbEg8QOB+ +DVrNVjzRlwW5y0vtOUucxD/SVRNuJLDWcfr0wbrM7Rv1/oFB2ACYPTrIrnqYNxgF +lQIDAQABo0IwQDAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB/zAdBgNV +HQ4EFgQU5K8rJnEaK0gnhS9SZizv8IkTcT4wDQYJKoZIhvcNAQEMBQADggIBADiW +Cu49tJYeX++dnAsznyvgyv3SjgofQXSlfKqE1OXyHuY3UjKcC9FhHb8owbZEKTV1 +d5iyfNm9dKyKaOOpMQkpAWBz40d8U6iQSifvS9efk+eCNs6aaAyC58/UEBZvXw6Z +XPYfcX3v73svfuo21pdwCxXu11xWajOl40k4DLh9+42FpLFZXvRq4d2h9mREruZR +gyFmxhE+885H7pwoHyXa/6xmld01D1zvICxi/ZG6qcz8WpyTgYMpl0p8WnK0OdC3 +d8t5/Wk6kjftbjhlRn7pYL15iJdfOBL07q9bgsiG1eGZbYwE8na6SfZu6W0eX6Dv +J4J2QPim01hcDyxC2kLGe4g0x8HYRZvBPsVhHdljUEn2NIVq4BjFbkerQUIpm/Zg +DdIx02OYI5NaAIFItO/Nis3Jz5nu2Z6qNuFoS3FJFDYoOj0dzpqPJeaAcWErtXvM ++SUWgeExX6GjfhaknBZqlxi9dnKlC54dNuYvoS++cJEPqOba+MSSQGwlfnuzCdyy +F62ARPBopY+Udf90WuioAnwMCeKpSwughQtiue+hMZL77/ZRBIls6Kl0obsXs7X9 +SQ98POyDGCBDTtWTurQ0sR8WNh8M5mQ5Fkzc4P4dyKliPUDqysU0ArSuiYgzNdws +E3PYJ/HQcu51OyLemGhmW/HGY0dVHLqlCFF1pkgl +-----END CERTIFICATE----- + +# Issuer: CN=GTS Root R2 O=Google Trust Services LLC +# Subject: CN=GTS Root R2 O=Google Trust Services LLC +# Label: "GTS Root R2" +# Serial: 146587176055767053814479386953112547951 +# MD5 Fingerprint: 44:ed:9a:0e:a4:09:3b:00:f2:ae:4c:a3:c6:61:b0:8b +# SHA1 Fingerprint: d2:73:96:2a:2a:5e:39:9f:73:3f:e1:c7:1e:64:3f:03:38:34:fc:4d +# SHA256 Fingerprint: c4:5d:7b:b0:8e:6d:67:e6:2e:42:35:11:0b:56:4e:5f:78:fd:92:ef:05:8c:84:0a:ea:4e:64:55:d7:58:5c:60 +-----BEGIN CERTIFICATE----- +MIIFWjCCA0KgAwIBAgIQbkepxlqz5yDFMJo/aFLybzANBgkqhkiG9w0BAQwFADBH +MQswCQYDVQQGEwJVUzEiMCAGA1UEChMZR29vZ2xlIFRydXN0IFNlcnZpY2VzIExM +QzEUMBIGA1UEAxMLR1RTIFJvb3QgUjIwHhcNMTYwNjIyMDAwMDAwWhcNMzYwNjIy +MDAwMDAwWjBHMQswCQYDVQQGEwJVUzEiMCAGA1UEChMZR29vZ2xlIFRydXN0IFNl +cnZpY2VzIExMQzEUMBIGA1UEAxMLR1RTIFJvb3QgUjIwggIiMA0GCSqGSIb3DQEB +AQUAA4ICDwAwggIKAoICAQDO3v2m++zsFDQ8BwZabFn3GTXd98GdVarTzTukk3Lv +CvptnfbwhYBboUhSnznFt+4orO/LdmgUud+tAWyZH8QiHZ/+cnfgLFuv5AS/T3Kg +GjSY6Dlo7JUle3ah5mm5hRm9iYz+re026nO8/4Piy33B0s5Ks40FnotJk9/BW9Bu +XvAuMC6C/Pq8tBcKSOWIm8Wba96wyrQD8Nr0kLhlZPdcTK3ofmZemde4wj7I0BOd +re7kRXuJVfeKH2JShBKzwkCX44ofR5GmdFrS+LFjKBC4swm4VndAoiaYecb+3yXu +PuWgf9RhD1FLPD+M2uFwdNjCaKH5wQzpoeJ/u1U8dgbuak7MkogwTZq9TwtImoS1 +mKPV+3PBV2HdKFZ1E66HjucMUQkQdYhMvI35ezzUIkgfKtzra7tEscszcTJGr61K +8YzodDqs5xoic4DSMPclQsciOzsSrZYuxsN2B6ogtzVJV+mSSeh2FnIxZyuWfoqj +x5RWIr9qS34BIbIjMt/kmkRtWVtd9QCgHJvGeJeNkP+byKq0rxFROV7Z+2et1VsR +nTKaG73VululycslaVNVJ1zgyjbLiGH7HrfQy+4W+9OmTN6SpdTi3/UGVN4unUu0 +kzCqgc7dGtxRcw1PcOnlthYhGXmy5okLdWTK1au8CcEYof/UVKGFPP0UJAOyh9Ok +twIDAQABo0IwQDAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB/zAdBgNV +HQ4EFgQUu//KjiOfT5nK2+JopqUVJxce2Q4wDQYJKoZIhvcNAQEMBQADggIBALZp +8KZ3/p7uC4Gt4cCpx/k1HUCCq+YEtN/L9x0Pg/B+E02NjO7jMyLDOfxA325BS0JT +vhaI8dI4XsRomRyYUpOM52jtG2pzegVATX9lO9ZY8c6DR2Dj/5epnGB3GFW1fgiT +z9D2PGcDFWEJ+YF59exTpJ/JjwGLc8R3dtyDovUMSRqodt6Sm2T4syzFJ9MHwAiA +pJiS4wGWAqoC7o87xdFtCjMwc3i5T1QWvwsHoaRc5svJXISPD+AVdyx+Jn7axEvb +pxZ3B7DNdehyQtaVhJ2Gg/LkkM0JR9SLA3DaWsYDQvTtN6LwG1BUSw7YhN4ZKJmB +R64JGz9I0cNv4rBgF/XuIwKl2gBbbZCr7qLpGzvpx0QnRY5rn/WkhLx3+WuXrD5R +RaIRpsyF7gpo8j5QOHokYh4XIDdtak23CZvJ/KRY9bb7nE4Yu5UC56GtmwfuNmsk +0jmGwZODUNKBRqhfYlcsu2xkiAhu7xNUX90txGdj08+JN7+dIPT7eoOboB6BAFDC +5AwiWVIQ7UNWhwD4FFKnHYuTjKJNRn8nxnGbJN7k2oaLDX5rIMHAnuFl2GqjpuiF +izoHCBy69Y9Vmhh1fuXsgWbRIXOhNUQLgD1bnF5vKheW0YMjiGZt5obicDIvUiLn +yOd/xCxgXS/Dr55FBcOEArf9LAhST4Ldo/DUhgkC +-----END CERTIFICATE----- + +# Issuer: CN=GTS Root R3 O=Google Trust Services LLC +# Subject: CN=GTS Root R3 O=Google Trust Services LLC +# Label: "GTS Root R3" +# Serial: 146587176140553309517047991083707763997 +# MD5 Fingerprint: 1a:79:5b:6b:04:52:9c:5d:c7:74:33:1b:25:9a:f9:25 +# SHA1 Fingerprint: 30:d4:24:6f:07:ff:db:91:89:8a:0b:e9:49:66:11:eb:8c:5e:46:e5 +# SHA256 Fingerprint: 15:d5:b8:77:46:19:ea:7d:54:ce:1c:a6:d0:b0:c4:03:e0:37:a9:17:f1:31:e8:a0:4e:1e:6b:7a:71:ba:bc:e5 +-----BEGIN CERTIFICATE----- +MIICDDCCAZGgAwIBAgIQbkepx2ypcyRAiQ8DVd2NHTAKBggqhkjOPQQDAzBHMQsw +CQYDVQQGEwJVUzEiMCAGA1UEChMZR29vZ2xlIFRydXN0IFNlcnZpY2VzIExMQzEU +MBIGA1UEAxMLR1RTIFJvb3QgUjMwHhcNMTYwNjIyMDAwMDAwWhcNMzYwNjIyMDAw +MDAwWjBHMQswCQYDVQQGEwJVUzEiMCAGA1UEChMZR29vZ2xlIFRydXN0IFNlcnZp +Y2VzIExMQzEUMBIGA1UEAxMLR1RTIFJvb3QgUjMwdjAQBgcqhkjOPQIBBgUrgQQA +IgNiAAQfTzOHMymKoYTey8chWEGJ6ladK0uFxh1MJ7x/JlFyb+Kf1qPKzEUURout +736GjOyxfi//qXGdGIRFBEFVbivqJn+7kAHjSxm65FSWRQmx1WyRRK2EE46ajA2A +DDL24CejQjBAMA4GA1UdDwEB/wQEAwIBBjAPBgNVHRMBAf8EBTADAQH/MB0GA1Ud +DgQWBBTB8Sa6oC2uhYHP0/EqEr24Cmf9vDAKBggqhkjOPQQDAwNpADBmAjEAgFuk +fCPAlaUs3L6JbyO5o91lAFJekazInXJ0glMLfalAvWhgxeG4VDvBNhcl2MG9AjEA +njWSdIUlUfUk7GRSJFClH9voy8l27OyCbvWFGFPouOOaKaqW04MjyaR7YbPMAuhd +-----END CERTIFICATE----- + +# Issuer: CN=GTS Root R4 O=Google Trust Services LLC +# Subject: CN=GTS Root R4 O=Google Trust Services LLC +# Label: "GTS Root R4" +# Serial: 146587176229350439916519468929765261721 +# MD5 Fingerprint: 5d:b6:6a:c4:60:17:24:6a:1a:99:a8:4b:ee:5e:b4:26 +# SHA1 Fingerprint: 2a:1d:60:27:d9:4a:b1:0a:1c:4d:91:5c:cd:33:a0:cb:3e:2d:54:cb +# SHA256 Fingerprint: 71:cc:a5:39:1f:9e:79:4b:04:80:25:30:b3:63:e1:21:da:8a:30:43:bb:26:66:2f:ea:4d:ca:7f:c9:51:a4:bd +-----BEGIN CERTIFICATE----- +MIICCjCCAZGgAwIBAgIQbkepyIuUtui7OyrYorLBmTAKBggqhkjOPQQDAzBHMQsw +CQYDVQQGEwJVUzEiMCAGA1UEChMZR29vZ2xlIFRydXN0IFNlcnZpY2VzIExMQzEU +MBIGA1UEAxMLR1RTIFJvb3QgUjQwHhcNMTYwNjIyMDAwMDAwWhcNMzYwNjIyMDAw +MDAwWjBHMQswCQYDVQQGEwJVUzEiMCAGA1UEChMZR29vZ2xlIFRydXN0IFNlcnZp +Y2VzIExMQzEUMBIGA1UEAxMLR1RTIFJvb3QgUjQwdjAQBgcqhkjOPQIBBgUrgQQA +IgNiAATzdHOnaItgrkO4NcWBMHtLSZ37wWHO5t5GvWvVYRg1rkDdc/eJkTBa6zzu +hXyiQHY7qca4R9gq55KRanPpsXI5nymfopjTX15YhmUPoYRlBtHci8nHc8iMai/l +xKvRHYqjQjBAMA4GA1UdDwEB/wQEAwIBBjAPBgNVHRMBAf8EBTADAQH/MB0GA1Ud +DgQWBBSATNbrdP9JNqPV2Py1PsVq8JQdjDAKBggqhkjOPQQDAwNnADBkAjBqUFJ0 +CMRw3J5QdCHojXohw0+WbhXRIjVhLfoIN+4Zba3bssx9BzT1YBkstTTZbyACMANx +sbqjYAuG7ZoIapVon+Kz4ZNkfF6Tpt95LY2F45TPI11xzPKwTdb+mciUqXWi4w== +-----END CERTIFICATE----- + +# Issuer: CN=UCA Global G2 Root O=UniTrust +# Subject: CN=UCA Global G2 Root O=UniTrust +# Label: "UCA Global G2 Root" +# Serial: 124779693093741543919145257850076631279 +# MD5 Fingerprint: 80:fe:f0:c4:4a:f0:5c:62:32:9f:1c:ba:78:a9:50:f8 +# SHA1 Fingerprint: 28:f9:78:16:19:7a:ff:18:25:18:aa:44:fe:c1:a0:ce:5c:b6:4c:8a +# SHA256 Fingerprint: 9b:ea:11:c9:76:fe:01:47:64:c1:be:56:a6:f9:14:b5:a5:60:31:7a:bd:99:88:39:33:82:e5:16:1a:a0:49:3c +-----BEGIN CERTIFICATE----- +MIIFRjCCAy6gAwIBAgIQXd+x2lqj7V2+WmUgZQOQ7zANBgkqhkiG9w0BAQsFADA9 +MQswCQYDVQQGEwJDTjERMA8GA1UECgwIVW5pVHJ1c3QxGzAZBgNVBAMMElVDQSBH +bG9iYWwgRzIgUm9vdDAeFw0xNjAzMTEwMDAwMDBaFw00MDEyMzEwMDAwMDBaMD0x +CzAJBgNVBAYTAkNOMREwDwYDVQQKDAhVbmlUcnVzdDEbMBkGA1UEAwwSVUNBIEds +b2JhbCBHMiBSb290MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAxeYr +b3zvJgUno4Ek2m/LAfmZmqkywiKHYUGRO8vDaBsGxUypK8FnFyIdK+35KYmToni9 +kmugow2ifsqTs6bRjDXVdfkX9s9FxeV67HeToI8jrg4aA3++1NDtLnurRiNb/yzm +VHqUwCoV8MmNsHo7JOHXaOIxPAYzRrZUEaalLyJUKlgNAQLx+hVRZ2zA+te2G3/R +VogvGjqNO7uCEeBHANBSh6v7hn4PJGtAnTRnvI3HLYZveT6OqTwXS3+wmeOwcWDc +C/Vkw85DvG1xudLeJ1uK6NjGruFZfc8oLTW4lVYa8bJYS7cSN8h8s+1LgOGN+jIj +tm+3SJUIsUROhYw6AlQgL9+/V087OpAh18EmNVQg7Mc/R+zvWr9LesGtOxdQXGLY +D0tK3Cv6brxzks3sx1DoQZbXqX5t2Okdj4q1uViSukqSKwxW/YDrCPBeKW4bHAyv +j5OJrdu9o54hyokZ7N+1wxrrFv54NkzWbtA+FxyQF2smuvt6L78RHBgOLXMDj6Dl +NaBa4kx1HXHhOThTeEDMg5PXCp6dW4+K5OXgSORIskfNTip1KnvyIvbJvgmRlld6 +iIis7nCs+dwp4wwcOxJORNanTrAmyPPZGpeRaOrvjUYG0lZFWJo8DA+DuAUlwznP +O6Q0ibd5Ei9Hxeepl2n8pndntd978XplFeRhVmUCAwEAAaNCMEAwDgYDVR0PAQH/ +BAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFIHEjMz15DD/pQwIX4wV +ZyF0Ad/fMA0GCSqGSIb3DQEBCwUAA4ICAQATZSL1jiutROTL/7lo5sOASD0Ee/oj +L3rtNtqyzm325p7lX1iPyzcyochltq44PTUbPrw7tgTQvPlJ9Zv3hcU2tsu8+Mg5 +1eRfB70VVJd0ysrtT7q6ZHafgbiERUlMjW+i67HM0cOU2kTC5uLqGOiiHycFutfl +1qnN3e92mI0ADs0b+gO3joBYDic/UvuUospeZcnWhNq5NXHzJsBPd+aBJ9J3O5oU +b3n09tDh05S60FdRvScFDcH9yBIw7m+NESsIndTUv4BFFJqIRNow6rSn4+7vW4LV +PtateJLbXDzz2K36uGt/xDYotgIVilQsnLAXc47QN6MUPJiVAAwpBVueSUmxX8fj +y88nZY41F7dXyDDZQVu5FLbowg+UMaeUmMxq67XhJ/UQqAHojhJi6IjMtX9Gl8Cb +EGY4GjZGXyJoPd/JxhMnq1MGrKI8hgZlb7F+sSlEmqO6SWkoaY/X5V+tBIZkbxqg +DMUIYs6Ao9Dz7GjevjPHF1t/gMRMTLGmhIrDO7gJzRSBuhjjVFc2/tsvfEehOjPI ++Vg7RE+xygKJBJYoaMVLuCaJu9YzL1DV/pqJuhgyklTGW+Cd+V7lDSKb9triyCGy +YiGqhkCyLmTTX8jjfhFnRR8F/uOi77Oos/N9j/gMHyIfLXC0uAE0djAA5SN4p1bX +UB+K+wb1whnw0A== +-----END CERTIFICATE----- + +# Issuer: CN=UCA Extended Validation Root O=UniTrust +# Subject: CN=UCA Extended Validation Root O=UniTrust +# Label: "UCA Extended Validation Root" +# Serial: 106100277556486529736699587978573607008 +# MD5 Fingerprint: a1:f3:5f:43:c6:34:9b:da:bf:8c:7e:05:53:ad:96:e2 +# SHA1 Fingerprint: a3:a1:b0:6f:24:61:23:4a:e3:36:a5:c2:37:fc:a6:ff:dd:f0:d7:3a +# SHA256 Fingerprint: d4:3a:f9:b3:54:73:75:5c:96:84:fc:06:d7:d8:cb:70:ee:5c:28:e7:73:fb:29:4e:b4:1e:e7:17:22:92:4d:24 +-----BEGIN CERTIFICATE----- +MIIFWjCCA0KgAwIBAgIQT9Irj/VkyDOeTzRYZiNwYDANBgkqhkiG9w0BAQsFADBH +MQswCQYDVQQGEwJDTjERMA8GA1UECgwIVW5pVHJ1c3QxJTAjBgNVBAMMHFVDQSBF +eHRlbmRlZCBWYWxpZGF0aW9uIFJvb3QwHhcNMTUwMzEzMDAwMDAwWhcNMzgxMjMx +MDAwMDAwWjBHMQswCQYDVQQGEwJDTjERMA8GA1UECgwIVW5pVHJ1c3QxJTAjBgNV +BAMMHFVDQSBFeHRlbmRlZCBWYWxpZGF0aW9uIFJvb3QwggIiMA0GCSqGSIb3DQEB +AQUAA4ICDwAwggIKAoICAQCpCQcoEwKwmeBkqh5DFnpzsZGgdT6o+uM4AHrsiWog +D4vFsJszA1qGxliG1cGFu0/GnEBNyr7uaZa4rYEwmnySBesFK5pI0Lh2PpbIILvS +sPGP2KxFRv+qZ2C0d35qHzwaUnoEPQc8hQ2E0B92CvdqFN9y4zR8V05WAT558aop +O2z6+I9tTcg1367r3CTueUWnhbYFiN6IXSV8l2RnCdm/WhUFhvMJHuxYMjMR83dk +sHYf5BA1FxvyDrFspCqjc/wJHx4yGVMR59mzLC52LqGj3n5qiAno8geK+LLNEOfi +c0CTuwjRP+H8C5SzJe98ptfRr5//lpr1kXuYC3fUfugH0mK1lTnj8/FtDw5lhIpj +VMWAtuCeS31HJqcBCF3RiJ7XwzJE+oJKCmhUfzhTA8ykADNkUVkLo4KRel7sFsLz +KuZi2irbWWIQJUoqgQtHB0MGcIfS+pMRKXpITeuUx3BNr2fVUbGAIAEBtHoIppB/ +TuDvB0GHr2qlXov7z1CymlSvw4m6WC31MJixNnI5fkkE/SmnTHnkBVfblLkWU41G +sx2VYVdWf6/wFlthWG82UBEL2KwrlRYaDh8IzTY0ZRBiZtWAXxQgXy0MoHgKaNYs +1+lvK9JKBZP8nm9rZ/+I8U6laUpSNwXqxhaN0sSZ0YIrO7o1dfdRUVjzyAfd5LQD +fwIDAQABo0IwQDAdBgNVHQ4EFgQU2XQ65DA9DfcS3H5aBZ8eNJr34RQwDwYDVR0T +AQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAYYwDQYJKoZIhvcNAQELBQADggIBADaN +l8xCFWQpN5smLNb7rhVpLGsaGvdftvkHTFnq88nIua7Mui563MD1sC3AO6+fcAUR +ap8lTwEpcOPlDOHqWnzcSbvBHiqB9RZLcpHIojG5qtr8nR/zXUACE/xOHAbKsxSQ +VBcZEhrxH9cMaVr2cXj0lH2RC47skFSOvG+hTKv8dGT9cZr4QQehzZHkPJrgmzI5 +c6sq1WnIeJEmMX3ixzDx/BR4dxIOE/TdFpS/S2d7cFOFyrC78zhNLJA5wA3CXWvp +4uXViI3WLL+rG761KIcSF3Ru/H38j9CHJrAb+7lsq+KePRXBOy5nAliRn+/4Qh8s +t2j1da3Ptfb/EX3C8CSlrdP6oDyp+l3cpaDvRKS+1ujl5BOWF3sGPjLtx7dCvHaj +2GU4Kzg1USEODm8uNBNA4StnDG1KQTAYI1oyVZnJF+A83vbsea0rWBmirSwiGpWO +vpaQXUJXxPkUAzUrHC1RVwinOt4/5Mi0A3PCwSaAuwtCH60NryZy2sy+s6ODWA2C +xR9GUeOcGMyNm43sSet1UNWMKFnKdDTajAshqx7qG+XH/RU+wBeq+yNuJkbL+vmx +cmtpzyKEC2IPrNkZAJSidjzULZrtBJ4tBmIQN1IchXIbJ+XMxjHsN+xjWZsLHXbM +fjKaiJUINlK73nZfdklJrX+9ZSCyycErdhh2n1ax +-----END CERTIFICATE----- + +# Issuer: CN=Certigna Root CA O=Dhimyotis OU=0002 48146308100036 +# Subject: CN=Certigna Root CA O=Dhimyotis OU=0002 48146308100036 +# Label: "Certigna Root CA" +# Serial: 269714418870597844693661054334862075617 +# MD5 Fingerprint: 0e:5c:30:62:27:eb:5b:bc:d7:ae:62:ba:e9:d5:df:77 +# SHA1 Fingerprint: 2d:0d:52:14:ff:9e:ad:99:24:01:74:20:47:6e:6c:85:27:27:f5:43 +# SHA256 Fingerprint: d4:8d:3d:23:ee:db:50:a4:59:e5:51:97:60:1c:27:77:4b:9d:7b:18:c9:4d:5a:05:95:11:a1:02:50:b9:31:68 +-----BEGIN CERTIFICATE----- +MIIGWzCCBEOgAwIBAgIRAMrpG4nxVQMNo+ZBbcTjpuEwDQYJKoZIhvcNAQELBQAw +WjELMAkGA1UEBhMCRlIxEjAQBgNVBAoMCURoaW15b3RpczEcMBoGA1UECwwTMDAw +MiA0ODE0NjMwODEwMDAzNjEZMBcGA1UEAwwQQ2VydGlnbmEgUm9vdCBDQTAeFw0x +MzEwMDEwODMyMjdaFw0zMzEwMDEwODMyMjdaMFoxCzAJBgNVBAYTAkZSMRIwEAYD +VQQKDAlEaGlteW90aXMxHDAaBgNVBAsMEzAwMDIgNDgxNDYzMDgxMDAwMzYxGTAX +BgNVBAMMEENlcnRpZ25hIFJvb3QgQ0EwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAw +ggIKAoICAQDNGDllGlmx6mQWDoyUJJV8g9PFOSbcDO8WV43X2KyjQn+Cyu3NW9sO +ty3tRQgXstmzy9YXUnIo245Onoq2C/mehJpNdt4iKVzSs9IGPjA5qXSjklYcoW9M +CiBtnyN6tMbaLOQdLNyzKNAT8kxOAkmhVECe5uUFoC2EyP+YbNDrihqECB63aCPu +I9Vwzm1RaRDuoXrC0SIxwoKF0vJVdlB8JXrJhFwLrN1CTivngqIkicuQstDuI7pm +TLtipPlTWmR7fJj6o0ieD5Wupxj0auwuA0Wv8HT4Ks16XdG+RCYyKfHx9WzMfgIh +C59vpD++nVPiz32pLHxYGpfhPTc3GGYo0kDFUYqMwy3OU4gkWGQwFsWq4NYKpkDf +ePb1BHxpE4S80dGnBs8B92jAqFe7OmGtBIyT46388NtEbVncSVmurJqZNjBBe3Yz +IoejwpKGbvlw7q6Hh5UbxHq9MfPU0uWZ/75I7HX1eBYdpnDBfzwboZL7z8g81sWT +Co/1VTp2lc5ZmIoJlXcymoO6LAQ6l73UL77XbJuiyn1tJslV1c/DeVIICZkHJC1k +JWumIWmbat10TWuXekG9qxf5kBdIjzb5LdXF2+6qhUVB+s06RbFo5jZMm5BX7CO5 +hwjCxAnxl4YqKE3idMDaxIzb3+KhF1nOJFl0Mdp//TBt2dzhauH8XwIDAQABo4IB +GjCCARYwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwHQYDVR0OBBYE +FBiHVuBud+4kNTxOc5of1uHieX4rMB8GA1UdIwQYMBaAFBiHVuBud+4kNTxOc5of +1uHieX4rMEQGA1UdIAQ9MDswOQYEVR0gADAxMC8GCCsGAQUFBwIBFiNodHRwczov +L3d3d3cuY2VydGlnbmEuZnIvYXV0b3JpdGVzLzBtBgNVHR8EZjBkMC+gLaArhilo +dHRwOi8vY3JsLmNlcnRpZ25hLmZyL2NlcnRpZ25hcm9vdGNhLmNybDAxoC+gLYYr +aHR0cDovL2NybC5kaGlteW90aXMuY29tL2NlcnRpZ25hcm9vdGNhLmNybDANBgkq +hkiG9w0BAQsFAAOCAgEAlLieT/DjlQgi581oQfccVdV8AOItOoldaDgvUSILSo3L +6btdPrtcPbEo/uRTVRPPoZAbAh1fZkYJMyjhDSSXcNMQH+pkV5a7XdrnxIxPTGRG +HVyH41neQtGbqH6mid2PHMkwgu07nM3A6RngatgCdTer9zQoKJHyBApPNeNgJgH6 +0BGM+RFq7q89w1DTj18zeTyGqHNFkIwgtnJzFyO+B2XleJINugHA64wcZr+shncB +lA2c5uk5jR+mUYyZDDl34bSb+hxnV29qao6pK0xXeXpXIs/NX2NGjVxZOob4Mkdi +o2cNGJHc+6Zr9UhhcyNZjgKnvETq9Emd8VRY+WCv2hikLyhF3HqgiIZd8zvn/yk1 +gPxkQ5Tm4xxvvq0OKmOZK8l+hfZx6AYDlf7ej0gcWtSS6Cvu5zHbugRqh5jnxV/v +faci9wHYTfmJ0A6aBVmknpjZbyvKcL5kwlWj9Omvw5Ip3IgWJJk8jSaYtlu3zM63 +Nwf9JtmYhST/WSMDmu2dnajkXjjO11INb9I/bbEFa0nOipFGc/T2L/Coc3cOZayh +jWZSaX5LaAzHHjcng6WMxwLkFM1JAbBzs/3GkDpv0mztO+7skb6iQ12LAEpmJURw +3kAP+HwV96LOPNdeE4yBFxgX0b3xdxA61GU5wSesVywlVP+i2k+KYTlerj1KjL0= +-----END CERTIFICATE----- + +# Issuer: CN=emSign Root CA - G1 O=eMudhra Technologies Limited OU=emSign PKI +# Subject: CN=emSign Root CA - G1 O=eMudhra Technologies Limited OU=emSign PKI +# Label: "emSign Root CA - G1" +# Serial: 235931866688319308814040 +# MD5 Fingerprint: 9c:42:84:57:dd:cb:0b:a7:2e:95:ad:b6:f3:da:bc:ac +# SHA1 Fingerprint: 8a:c7:ad:8f:73:ac:4e:c1:b5:75:4d:a5:40:f4:fc:cf:7c:b5:8e:8c +# SHA256 Fingerprint: 40:f6:af:03:46:a9:9a:a1:cd:1d:55:5a:4e:9c:ce:62:c7:f9:63:46:03:ee:40:66:15:83:3d:c8:c8:d0:03:67 +-----BEGIN CERTIFICATE----- +MIIDlDCCAnygAwIBAgIKMfXkYgxsWO3W2DANBgkqhkiG9w0BAQsFADBnMQswCQYD +VQQGEwJJTjETMBEGA1UECxMKZW1TaWduIFBLSTElMCMGA1UEChMcZU11ZGhyYSBU +ZWNobm9sb2dpZXMgTGltaXRlZDEcMBoGA1UEAxMTZW1TaWduIFJvb3QgQ0EgLSBH +MTAeFw0xODAyMTgxODMwMDBaFw00MzAyMTgxODMwMDBaMGcxCzAJBgNVBAYTAklO +MRMwEQYDVQQLEwplbVNpZ24gUEtJMSUwIwYDVQQKExxlTXVkaHJhIFRlY2hub2xv +Z2llcyBMaW1pdGVkMRwwGgYDVQQDExNlbVNpZ24gUm9vdCBDQSAtIEcxMIIBIjAN +BgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAk0u76WaK7p1b1TST0Bsew+eeuGQz +f2N4aLTNLnF115sgxk0pvLZoYIr3IZpWNVrzdr3YzZr/k1ZLpVkGoZM0Kd0WNHVO +8oG0x5ZOrRkVUkr+PHB1cM2vK6sVmjM8qrOLqs1D/fXqcP/tzxE7lM5OMhbTI0Aq +d7OvPAEsbO2ZLIvZTmmYsvePQbAyeGHWDV/D+qJAkh1cF+ZwPjXnorfCYuKrpDhM +tTk1b+oDafo6VGiFbdbyL0NVHpENDtjVaqSW0RM8LHhQ6DqS0hdW5TUaQBw+jSzt +Od9C4INBdN+jzcKGYEho42kLVACL5HZpIQ15TjQIXhTCzLG3rdd8cIrHhQIDAQAB +o0IwQDAdBgNVHQ4EFgQU++8Nhp6w492pufEhF38+/PB3KxowDgYDVR0PAQH/BAQD +AgEGMA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQELBQADggEBAFn/8oz1h31x +PaOfG1vR2vjTnGs2vZupYeveFix0PZ7mddrXuqe8QhfnPZHr5X3dPpzxz5KsbEjM +wiI/aTvFthUvozXGaCocV685743QNcMYDHsAVhzNixl03r4PEuDQqqE/AjSxcM6d +GNYIAwlG7mDgfrbESQRRfXBgvKqy/3lyeqYdPV8q+Mri/Tm3R7nrft8EI6/6nAYH +6ftjk4BAtcZsCjEozgyfz7MjNYBBjWzEN3uBL4ChQEKF6dk4jeihU80Bv2noWgby +RQuQ+q7hv53yrlc8pa6yVvSLZUDp/TGBLPQ5Cdjua6e0ph0VpZj3AYHYhX3zUVxx +iN66zB+Afko= +-----END CERTIFICATE----- + +# Issuer: CN=emSign ECC Root CA - G3 O=eMudhra Technologies Limited OU=emSign PKI +# Subject: CN=emSign ECC Root CA - G3 O=eMudhra Technologies Limited OU=emSign PKI +# Label: "emSign ECC Root CA - G3" +# Serial: 287880440101571086945156 +# MD5 Fingerprint: ce:0b:72:d1:9f:88:8e:d0:50:03:e8:e3:b8:8b:67:40 +# SHA1 Fingerprint: 30:43:fa:4f:f2:57:dc:a0:c3:80:ee:2e:58:ea:78:b2:3f:e6:bb:c1 +# SHA256 Fingerprint: 86:a1:ec:ba:08:9c:4a:8d:3b:be:27:34:c6:12:ba:34:1d:81:3e:04:3c:f9:e8:a8:62:cd:5c:57:a3:6b:be:6b +-----BEGIN CERTIFICATE----- +MIICTjCCAdOgAwIBAgIKPPYHqWhwDtqLhDAKBggqhkjOPQQDAzBrMQswCQYDVQQG +EwJJTjETMBEGA1UECxMKZW1TaWduIFBLSTElMCMGA1UEChMcZU11ZGhyYSBUZWNo +bm9sb2dpZXMgTGltaXRlZDEgMB4GA1UEAxMXZW1TaWduIEVDQyBSb290IENBIC0g +RzMwHhcNMTgwMjE4MTgzMDAwWhcNNDMwMjE4MTgzMDAwWjBrMQswCQYDVQQGEwJJ +TjETMBEGA1UECxMKZW1TaWduIFBLSTElMCMGA1UEChMcZU11ZGhyYSBUZWNobm9s +b2dpZXMgTGltaXRlZDEgMB4GA1UEAxMXZW1TaWduIEVDQyBSb290IENBIC0gRzMw +djAQBgcqhkjOPQIBBgUrgQQAIgNiAAQjpQy4LRL1KPOxst3iAhKAnjlfSU2fySU0 +WXTsuwYc58Byr+iuL+FBVIcUqEqy6HyC5ltqtdyzdc6LBtCGI79G1Y4PPwT01xyS +fvalY8L1X44uT6EYGQIrMgqCZH0Wk9GjQjBAMB0GA1UdDgQWBBR8XQKEE9TMipuB +zhccLikenEhjQjAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB/zAKBggq +hkjOPQQDAwNpADBmAjEAvvNhzwIQHWSVB7gYboiFBS+DCBeQyh+KTOgNG3qxrdWB +CUfvO6wIBHxcmbHtRwfSAjEAnbpV/KlK6O3t5nYBQnvI+GDZjVGLVTv7jHvrZQnD ++JbNR6iC8hZVdyR+EhCVBCyj +-----END CERTIFICATE----- + +# Issuer: CN=emSign Root CA - C1 O=eMudhra Inc OU=emSign PKI +# Subject: CN=emSign Root CA - C1 O=eMudhra Inc OU=emSign PKI +# Label: "emSign Root CA - C1" +# Serial: 825510296613316004955058 +# MD5 Fingerprint: d8:e3:5d:01:21:fa:78:5a:b0:df:ba:d2:ee:2a:5f:68 +# SHA1 Fingerprint: e7:2e:f1:df:fc:b2:09:28:cf:5d:d4:d5:67:37:b1:51:cb:86:4f:01 +# SHA256 Fingerprint: 12:56:09:aa:30:1d:a0:a2:49:b9:7a:82:39:cb:6a:34:21:6f:44:dc:ac:9f:39:54:b1:42:92:f2:e8:c8:60:8f +-----BEGIN CERTIFICATE----- +MIIDczCCAlugAwIBAgILAK7PALrEzzL4Q7IwDQYJKoZIhvcNAQELBQAwVjELMAkG +A1UEBhMCVVMxEzARBgNVBAsTCmVtU2lnbiBQS0kxFDASBgNVBAoTC2VNdWRocmEg +SW5jMRwwGgYDVQQDExNlbVNpZ24gUm9vdCBDQSAtIEMxMB4XDTE4MDIxODE4MzAw +MFoXDTQzMDIxODE4MzAwMFowVjELMAkGA1UEBhMCVVMxEzARBgNVBAsTCmVtU2ln +biBQS0kxFDASBgNVBAoTC2VNdWRocmEgSW5jMRwwGgYDVQQDExNlbVNpZ24gUm9v +dCBDQSAtIEMxMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAz+upufGZ +BczYKCFK83M0UYRWEPWgTywS4/oTmifQz/l5GnRfHXk5/Fv4cI7gklL35CX5VIPZ +HdPIWoU/Xse2B+4+wM6ar6xWQio5JXDWv7V7Nq2s9nPczdcdioOl+yuQFTdrHCZH +3DspVpNqs8FqOp099cGXOFgFixwR4+S0uF2FHYP+eF8LRWgYSKVGczQ7/g/IdrvH +GPMF0Ybzhe3nudkyrVWIzqa2kbBPrH4VI5b2P/AgNBbeCsbEBEV5f6f9vtKppa+c +xSMq9zwhbL2vj07FOrLzNBL834AaSaTUqZX3noleoomslMuoaJuvimUnzYnu3Yy1 +aylwQ6BpC+S5DwIDAQABo0IwQDAdBgNVHQ4EFgQU/qHgcB4qAzlSWkK+XJGFehiq +TbUwDgYDVR0PAQH/BAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQEL +BQADggEBAMJKVvoVIXsoounlHfv4LcQ5lkFMOycsxGwYFYDGrK9HWS8mC+M2sO87 +/kOXSTKZEhVb3xEp/6tT+LvBeA+snFOvV71ojD1pM/CjoCNjO2RnIkSt1XHLVip4 +kqNPEjE2NuLe/gDEo2APJ62gsIq1NnpSob0n9CAnYuhNlCQT5AoE6TyrLshDCUrG +YQTlSTR+08TI9Q/Aqum6VF7zYytPT1DU/rl7mYw9wC68AivTxEDkigcxHpvOJpkT ++xHqmiIMERnHXhuBUDDIlhJu58tBf5E7oke3VIAb3ADMmpDqw8NQBmIMMMAVSKeo +WXzhriKi4gp6D/piq1JM4fHfyr6DDUI= +-----END CERTIFICATE----- + +# Issuer: CN=emSign ECC Root CA - C3 O=eMudhra Inc OU=emSign PKI +# Subject: CN=emSign ECC Root CA - C3 O=eMudhra Inc OU=emSign PKI +# Label: "emSign ECC Root CA - C3" +# Serial: 582948710642506000014504 +# MD5 Fingerprint: 3e:53:b3:a3:81:ee:d7:10:f8:d3:b0:1d:17:92:f5:d5 +# SHA1 Fingerprint: b6:af:43:c2:9b:81:53:7d:f6:ef:6b:c3:1f:1f:60:15:0c:ee:48:66 +# SHA256 Fingerprint: bc:4d:80:9b:15:18:9d:78:db:3e:1d:8c:f4:f9:72:6a:79:5d:a1:64:3c:a5:f1:35:8e:1d:db:0e:dc:0d:7e:b3 +-----BEGIN CERTIFICATE----- +MIICKzCCAbGgAwIBAgIKe3G2gla4EnycqDAKBggqhkjOPQQDAzBaMQswCQYDVQQG +EwJVUzETMBEGA1UECxMKZW1TaWduIFBLSTEUMBIGA1UEChMLZU11ZGhyYSBJbmMx +IDAeBgNVBAMTF2VtU2lnbiBFQ0MgUm9vdCBDQSAtIEMzMB4XDTE4MDIxODE4MzAw +MFoXDTQzMDIxODE4MzAwMFowWjELMAkGA1UEBhMCVVMxEzARBgNVBAsTCmVtU2ln +biBQS0kxFDASBgNVBAoTC2VNdWRocmEgSW5jMSAwHgYDVQQDExdlbVNpZ24gRUND +IFJvb3QgQ0EgLSBDMzB2MBAGByqGSM49AgEGBSuBBAAiA2IABP2lYa57JhAd6bci +MK4G9IGzsUJxlTm801Ljr6/58pc1kjZGDoeVjbk5Wum739D+yAdBPLtVb4Ojavti +sIGJAnB9SMVK4+kiVCJNk7tCDK93nCOmfddhEc5lx/h//vXyqaNCMEAwHQYDVR0O +BBYEFPtaSNCAIEDyqOkAB2kZd6fmw/TPMA4GA1UdDwEB/wQEAwIBBjAPBgNVHRMB +Af8EBTADAQH/MAoGCCqGSM49BAMDA2gAMGUCMQC02C8Cif22TGK6Q04ThHK1rt0c +3ta13FaPWEBaLd4gTCKDypOofu4SQMfWh0/434UCMBwUZOR8loMRnLDRWmFLpg9J +0wD8ofzkpf9/rdcw0Md3f76BB1UwUCAU9Vc4CqgxUQ== +-----END CERTIFICATE----- + +# Issuer: CN=Hongkong Post Root CA 3 O=Hongkong Post +# Subject: CN=Hongkong Post Root CA 3 O=Hongkong Post +# Label: "Hongkong Post Root CA 3" +# Serial: 46170865288971385588281144162979347873371282084 +# MD5 Fingerprint: 11:fc:9f:bd:73:30:02:8a:fd:3f:f3:58:b9:cb:20:f0 +# SHA1 Fingerprint: 58:a2:d0:ec:20:52:81:5b:c1:f3:f8:64:02:24:4e:c2:8e:02:4b:02 +# SHA256 Fingerprint: 5a:2f:c0:3f:0c:83:b0:90:bb:fa:40:60:4b:09:88:44:6c:76:36:18:3d:f9:84:6e:17:10:1a:44:7f:b8:ef:d6 +-----BEGIN CERTIFICATE----- +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 +mpv0 +-----END CERTIFICATE----- diff --git a/venv2/lib/python2.7/site-packages/pip/_vendor/certifi/core.py b/venv2/lib/python2.7/site-packages/pip/_vendor/certifi/core.py new file mode 100644 index 0000000..7271acf --- /dev/null +++ b/venv2/lib/python2.7/site-packages/pip/_vendor/certifi/core.py @@ -0,0 +1,15 @@ +# -*- coding: utf-8 -*- + +""" +certifi.py +~~~~~~~~~~ + +This module returns the installation location of cacert.pem. +""" +import os + + +def where(): + f = os.path.dirname(__file__) + + return os.path.join(f, 'cacert.pem') diff --git a/venv2/lib/python2.7/site-packages/pip/_vendor/chardet/__init__.py b/venv2/lib/python2.7/site-packages/pip/_vendor/chardet/__init__.py new file mode 100644 index 0000000..0f9f820 --- /dev/null +++ b/venv2/lib/python2.7/site-packages/pip/_vendor/chardet/__init__.py @@ -0,0 +1,39 @@ +######################## BEGIN LICENSE BLOCK ######################## +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA +# 02110-1301 USA +######################### END LICENSE BLOCK ######################### + + +from .compat import PY2, PY3 +from .universaldetector import UniversalDetector +from .version import __version__, VERSION + + +def detect(byte_str): + """ + Detect the encoding of the given byte string. + + :param byte_str: The byte sequence to examine. + :type byte_str: ``bytes`` or ``bytearray`` + """ + if not isinstance(byte_str, bytearray): + if not isinstance(byte_str, bytes): + raise TypeError('Expected object of type bytes or bytearray, got: ' + '{0}'.format(type(byte_str))) + else: + byte_str = bytearray(byte_str) + detector = UniversalDetector() + detector.feed(byte_str) + return detector.close() diff --git a/venv2/lib/python2.7/site-packages/pip/_vendor/chardet/big5freq.py b/venv2/lib/python2.7/site-packages/pip/_vendor/chardet/big5freq.py new file mode 100644 index 0000000..38f3251 --- /dev/null +++ b/venv2/lib/python2.7/site-packages/pip/_vendor/chardet/big5freq.py @@ -0,0 +1,386 @@ +######################## BEGIN LICENSE BLOCK ######################## +# The Original Code is Mozilla Communicator client code. +# +# The Initial Developer of the Original Code is +# Netscape Communications Corporation. +# Portions created by the Initial Developer are Copyright (C) 1998 +# the Initial Developer. All Rights Reserved. +# +# Contributor(s): +# Mark Pilgrim - port to Python +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA +# 02110-1301 USA +######################### END LICENSE BLOCK ######################### + +# Big5 frequency table +# by Taiwan's Mandarin Promotion Council +# <http://www.edu.tw:81/mandr/> +# +# 128 --> 0.42261 +# 256 --> 0.57851 +# 512 --> 0.74851 +# 1024 --> 0.89384 +# 2048 --> 0.97583 +# +# Ideal Distribution Ratio = 0.74851/(1-0.74851) =2.98 +# Random Distribution Ration = 512/(5401-512)=0.105 +# +# Typical Distribution Ratio about 25% of Ideal one, still much higher than RDR + +BIG5_TYPICAL_DISTRIBUTION_RATIO = 0.75 + +#Char to FreqOrder table +BIG5_TABLE_SIZE = 5376 + +BIG5_CHAR_TO_FREQ_ORDER = ( + 1,1801,1506, 255,1431, 198, 9, 82, 6,5008, 177, 202,3681,1256,2821, 110, # 16 +3814, 33,3274, 261, 76, 44,2114, 16,2946,2187,1176, 659,3971, 26,3451,2653, # 32 +1198,3972,3350,4202, 410,2215, 302, 590, 361,1964, 8, 204, 58,4510,5009,1932, # 48 + 63,5010,5011, 317,1614, 75, 222, 159,4203,2417,1480,5012,3555,3091, 224,2822, # 64 +3682, 3, 10,3973,1471, 29,2787,1135,2866,1940, 873, 130,3275,1123, 312,5013, # 80 +4511,2052, 507, 252, 682,5014, 142,1915, 124, 206,2947, 34,3556,3204, 64, 604, # 96 +5015,2501,1977,1978, 155,1991, 645, 641,1606,5016,3452, 337, 72, 406,5017, 80, # 112 + 630, 238,3205,1509, 263, 939,1092,2654, 756,1440,1094,3453, 449, 69,2987, 591, # 128 + 179,2096, 471, 115,2035,1844, 60, 50,2988, 134, 806,1869, 734,2036,3454, 180, # 144 + 995,1607, 156, 537,2907, 688,5018, 319,1305, 779,2145, 514,2379, 298,4512, 359, # 160 +2502, 90,2716,1338, 663, 11, 906,1099,2553, 20,2441, 182, 532,1716,5019, 732, # 176 +1376,4204,1311,1420,3206, 25,2317,1056, 113, 399, 382,1950, 242,3455,2474, 529, # 192 +3276, 475,1447,3683,5020, 117, 21, 656, 810,1297,2300,2334,3557,5021, 126,4205, # 208 + 706, 456, 150, 613,4513, 71,1118,2037,4206, 145,3092, 85, 835, 486,2115,1246, # 224 +1426, 428, 727,1285,1015, 800, 106, 623, 303,1281,5022,2128,2359, 347,3815, 221, # 240 +3558,3135,5023,1956,1153,4207, 83, 296,1199,3093, 192, 624, 93,5024, 822,1898, # 256 +2823,3136, 795,2065, 991,1554,1542,1592, 27, 43,2867, 859, 139,1456, 860,4514, # 272 + 437, 712,3974, 164,2397,3137, 695, 211,3037,2097, 195,3975,1608,3559,3560,3684, # 288 +3976, 234, 811,2989,2098,3977,2233,1441,3561,1615,2380, 668,2077,1638, 305, 228, # 304 +1664,4515, 467, 415,5025, 262,2099,1593, 239, 108, 300, 200,1033, 512,1247,2078, # 320 +5026,5027,2176,3207,3685,2682, 593, 845,1062,3277, 88,1723,2038,3978,1951, 212, # 336 + 266, 152, 149, 468,1899,4208,4516, 77, 187,5028,3038, 37, 5,2990,5029,3979, # 352 +5030,5031, 39,2524,4517,2908,3208,2079, 55, 148, 74,4518, 545, 483,1474,1029, # 368 +1665, 217,1870,1531,3138,1104,2655,4209, 24, 172,3562, 900,3980,3563,3564,4519, # 384 + 32,1408,2824,1312, 329, 487,2360,2251,2717, 784,2683, 4,3039,3351,1427,1789, # 400 + 188, 109, 499,5032,3686,1717,1790, 888,1217,3040,4520,5033,3565,5034,3352,1520, # 416 +3687,3981, 196,1034, 775,5035,5036, 929,1816, 249, 439, 38,5037,1063,5038, 794, # 432 +3982,1435,2301, 46, 178,3278,2066,5039,2381,5040, 214,1709,4521, 804, 35, 707, # 448 + 324,3688,1601,2554, 140, 459,4210,5041,5042,1365, 839, 272, 978,2262,2580,3456, # 464 +2129,1363,3689,1423, 697, 100,3094, 48, 70,1231, 495,3139,2196,5043,1294,5044, # 480 +2080, 462, 586,1042,3279, 853, 256, 988, 185,2382,3457,1698, 434,1084,5045,3458, # 496 + 314,2625,2788,4522,2335,2336, 569,2285, 637,1817,2525, 757,1162,1879,1616,3459, # 512 + 287,1577,2116, 768,4523,1671,2868,3566,2526,1321,3816, 909,2418,5046,4211, 933, # 528 +3817,4212,2053,2361,1222,4524, 765,2419,1322, 786,4525,5047,1920,1462,1677,2909, # 544 +1699,5048,4526,1424,2442,3140,3690,2600,3353,1775,1941,3460,3983,4213, 309,1369, # 560 +1130,2825, 364,2234,1653,1299,3984,3567,3985,3986,2656, 525,1085,3041, 902,2001, # 576 +1475, 964,4527, 421,1845,1415,1057,2286, 940,1364,3141, 376,4528,4529,1381, 7, # 592 +2527, 983,2383, 336,1710,2684,1846, 321,3461, 559,1131,3042,2752,1809,1132,1313, # 608 + 265,1481,1858,5049, 352,1203,2826,3280, 167,1089, 420,2827, 776, 792,1724,3568, # 624 +4214,2443,3281,5050,4215,5051, 446, 229, 333,2753, 901,3818,1200,1557,4530,2657, # 640 +1921, 395,2754,2685,3819,4216,1836, 125, 916,3209,2626,4531,5052,5053,3820,5054, # 656 +5055,5056,4532,3142,3691,1133,2555,1757,3462,1510,2318,1409,3569,5057,2146, 438, # 672 +2601,2910,2384,3354,1068, 958,3043, 461, 311,2869,2686,4217,1916,3210,4218,1979, # 688 + 383, 750,2755,2627,4219, 274, 539, 385,1278,1442,5058,1154,1965, 384, 561, 210, # 704 + 98,1295,2556,3570,5059,1711,2420,1482,3463,3987,2911,1257, 129,5060,3821, 642, # 720 + 523,2789,2790,2658,5061, 141,2235,1333, 68, 176, 441, 876, 907,4220, 603,2602, # 736 + 710, 171,3464, 404, 549, 18,3143,2398,1410,3692,1666,5062,3571,4533,2912,4534, # 752 +5063,2991, 368,5064, 146, 366, 99, 871,3693,1543, 748, 807,1586,1185, 22,2263, # 768 + 379,3822,3211,5065,3212, 505,1942,2628,1992,1382,2319,5066, 380,2362, 218, 702, # 784 +1818,1248,3465,3044,3572,3355,3282,5067,2992,3694, 930,3283,3823,5068, 59,5069, # 800 + 585, 601,4221, 497,3466,1112,1314,4535,1802,5070,1223,1472,2177,5071, 749,1837, # 816 + 690,1900,3824,1773,3988,1476, 429,1043,1791,2236,2117, 917,4222, 447,1086,1629, # 832 +5072, 556,5073,5074,2021,1654, 844,1090, 105, 550, 966,1758,2828,1008,1783, 686, # 848 +1095,5075,2287, 793,1602,5076,3573,2603,4536,4223,2948,2302,4537,3825, 980,2503, # 864 + 544, 353, 527,4538, 908,2687,2913,5077, 381,2629,1943,1348,5078,1341,1252, 560, # 880 +3095,5079,3467,2870,5080,2054, 973, 886,2081, 143,4539,5081,5082, 157,3989, 496, # 896 +4224, 57, 840, 540,2039,4540,4541,3468,2118,1445, 970,2264,1748,1966,2082,4225, # 912 +3144,1234,1776,3284,2829,3695, 773,1206,2130,1066,2040,1326,3990,1738,1725,4226, # 928 + 279,3145, 51,1544,2604, 423,1578,2131,2067, 173,4542,1880,5083,5084,1583, 264, # 944 + 610,3696,4543,2444, 280, 154,5085,5086,5087,1739, 338,1282,3096, 693,2871,1411, # 960 +1074,3826,2445,5088,4544,5089,5090,1240, 952,2399,5091,2914,1538,2688, 685,1483, # 976 +4227,2475,1436, 953,4228,2055,4545, 671,2400, 79,4229,2446,3285, 608, 567,2689, # 992 +3469,4230,4231,1691, 393,1261,1792,2401,5092,4546,5093,5094,5095,5096,1383,1672, # 1008 +3827,3213,1464, 522,1119, 661,1150, 216, 675,4547,3991,1432,3574, 609,4548,2690, # 1024 +2402,5097,5098,5099,4232,3045, 0,5100,2476, 315, 231,2447, 301,3356,4549,2385, # 1040 +5101, 233,4233,3697,1819,4550,4551,5102, 96,1777,1315,2083,5103, 257,5104,1810, # 1056 +3698,2718,1139,1820,4234,2022,1124,2164,2791,1778,2659,5105,3097, 363,1655,3214, # 1072 +5106,2993,5107,5108,5109,3992,1567,3993, 718, 103,3215, 849,1443, 341,3357,2949, # 1088 +1484,5110,1712, 127, 67, 339,4235,2403, 679,1412, 821,5111,5112, 834, 738, 351, # 1104 +2994,2147, 846, 235,1497,1881, 418,1993,3828,2719, 186,1100,2148,2756,3575,1545, # 1120 +1355,2950,2872,1377, 583,3994,4236,2581,2995,5113,1298,3699,1078,2557,3700,2363, # 1136 + 78,3829,3830, 267,1289,2100,2002,1594,4237, 348, 369,1274,2197,2178,1838,4552, # 1152 +1821,2830,3701,2757,2288,2003,4553,2951,2758, 144,3358, 882,4554,3995,2759,3470, # 1168 +4555,2915,5114,4238,1726, 320,5115,3996,3046, 788,2996,5116,2831,1774,1327,2873, # 1184 +3997,2832,5117,1306,4556,2004,1700,3831,3576,2364,2660, 787,2023, 506, 824,3702, # 1200 + 534, 323,4557,1044,3359,2024,1901, 946,3471,5118,1779,1500,1678,5119,1882,4558, # 1216 + 165, 243,4559,3703,2528, 123, 683,4239, 764,4560, 36,3998,1793, 589,2916, 816, # 1232 + 626,1667,3047,2237,1639,1555,1622,3832,3999,5120,4000,2874,1370,1228,1933, 891, # 1248 +2084,2917, 304,4240,5121, 292,2997,2720,3577, 691,2101,4241,1115,4561, 118, 662, # 1264 +5122, 611,1156, 854,2386,1316,2875, 2, 386, 515,2918,5123,5124,3286, 868,2238, # 1280 +1486, 855,2661, 785,2216,3048,5125,1040,3216,3578,5126,3146, 448,5127,1525,5128, # 1296 +2165,4562,5129,3833,5130,4242,2833,3579,3147, 503, 818,4001,3148,1568, 814, 676, # 1312 +1444, 306,1749,5131,3834,1416,1030, 197,1428, 805,2834,1501,4563,5132,5133,5134, # 1328 +1994,5135,4564,5136,5137,2198, 13,2792,3704,2998,3149,1229,1917,5138,3835,2132, # 1344 +5139,4243,4565,2404,3580,5140,2217,1511,1727,1120,5141,5142, 646,3836,2448, 307, # 1360 +5143,5144,1595,3217,5145,5146,5147,3705,1113,1356,4002,1465,2529,2530,5148, 519, # 1376 +5149, 128,2133, 92,2289,1980,5150,4003,1512, 342,3150,2199,5151,2793,2218,1981, # 1392 +3360,4244, 290,1656,1317, 789, 827,2365,5152,3837,4566, 562, 581,4004,5153, 401, # 1408 +4567,2252, 94,4568,5154,1399,2794,5155,1463,2025,4569,3218,1944,5156, 828,1105, # 1424 +4245,1262,1394,5157,4246, 605,4570,5158,1784,2876,5159,2835, 819,2102, 578,2200, # 1440 +2952,5160,1502, 436,3287,4247,3288,2836,4005,2919,3472,3473,5161,2721,2320,5162, # 1456 +5163,2337,2068, 23,4571, 193, 826,3838,2103, 699,1630,4248,3098, 390,1794,1064, # 1472 +3581,5164,1579,3099,3100,1400,5165,4249,1839,1640,2877,5166,4572,4573, 137,4250, # 1488 + 598,3101,1967, 780, 104, 974,2953,5167, 278, 899, 253, 402, 572, 504, 493,1339, # 1504 +5168,4006,1275,4574,2582,2558,5169,3706,3049,3102,2253, 565,1334,2722, 863, 41, # 1520 +5170,5171,4575,5172,1657,2338, 19, 463,2760,4251, 606,5173,2999,3289,1087,2085, # 1536 +1323,2662,3000,5174,1631,1623,1750,4252,2691,5175,2878, 791,2723,2663,2339, 232, # 1552 +2421,5176,3001,1498,5177,2664,2630, 755,1366,3707,3290,3151,2026,1609, 119,1918, # 1568 +3474, 862,1026,4253,5178,4007,3839,4576,4008,4577,2265,1952,2477,5179,1125, 817, # 1584 +4254,4255,4009,1513,1766,2041,1487,4256,3050,3291,2837,3840,3152,5180,5181,1507, # 1600 +5182,2692, 733, 40,1632,1106,2879, 345,4257, 841,2531, 230,4578,3002,1847,3292, # 1616 +3475,5183,1263, 986,3476,5184, 735, 879, 254,1137, 857, 622,1300,1180,1388,1562, # 1632 +4010,4011,2954, 967,2761,2665,1349, 592,2134,1692,3361,3003,1995,4258,1679,4012, # 1648 +1902,2188,5185, 739,3708,2724,1296,1290,5186,4259,2201,2202,1922,1563,2605,2559, # 1664 +1871,2762,3004,5187, 435,5188, 343,1108, 596, 17,1751,4579,2239,3477,3709,5189, # 1680 +4580, 294,3582,2955,1693, 477, 979, 281,2042,3583, 643,2043,3710,2631,2795,2266, # 1696 +1031,2340,2135,2303,3584,4581, 367,1249,2560,5190,3585,5191,4582,1283,3362,2005, # 1712 + 240,1762,3363,4583,4584, 836,1069,3153, 474,5192,2149,2532, 268,3586,5193,3219, # 1728 +1521,1284,5194,1658,1546,4260,5195,3587,3588,5196,4261,3364,2693,1685,4262, 961, # 1744 +1673,2632, 190,2006,2203,3841,4585,4586,5197, 570,2504,3711,1490,5198,4587,2633, # 1760 +3293,1957,4588, 584,1514, 396,1045,1945,5199,4589,1968,2449,5200,5201,4590,4013, # 1776 + 619,5202,3154,3294, 215,2007,2796,2561,3220,4591,3221,4592, 763,4263,3842,4593, # 1792 +5203,5204,1958,1767,2956,3365,3712,1174, 452,1477,4594,3366,3155,5205,2838,1253, # 1808 +2387,2189,1091,2290,4264, 492,5206, 638,1169,1825,2136,1752,4014, 648, 926,1021, # 1824 +1324,4595, 520,4596, 997, 847,1007, 892,4597,3843,2267,1872,3713,2405,1785,4598, # 1840 +1953,2957,3103,3222,1728,4265,2044,3714,4599,2008,1701,3156,1551, 30,2268,4266, # 1856 +5207,2027,4600,3589,5208, 501,5209,4267, 594,3478,2166,1822,3590,3479,3591,3223, # 1872 + 829,2839,4268,5210,1680,3157,1225,4269,5211,3295,4601,4270,3158,2341,5212,4602, # 1888 +4271,5213,4015,4016,5214,1848,2388,2606,3367,5215,4603, 374,4017, 652,4272,4273, # 1904 + 375,1140, 798,5216,5217,5218,2366,4604,2269, 546,1659, 138,3051,2450,4605,5219, # 1920 +2254, 612,1849, 910, 796,3844,1740,1371, 825,3845,3846,5220,2920,2562,5221, 692, # 1936 + 444,3052,2634, 801,4606,4274,5222,1491, 244,1053,3053,4275,4276, 340,5223,4018, # 1952 +1041,3005, 293,1168, 87,1357,5224,1539, 959,5225,2240, 721, 694,4277,3847, 219, # 1968 +1478, 644,1417,3368,2666,1413,1401,1335,1389,4019,5226,5227,3006,2367,3159,1826, # 1984 + 730,1515, 184,2840, 66,4607,5228,1660,2958, 246,3369, 378,1457, 226,3480, 975, # 2000 +4020,2959,1264,3592, 674, 696,5229, 163,5230,1141,2422,2167, 713,3593,3370,4608, # 2016 +4021,5231,5232,1186, 15,5233,1079,1070,5234,1522,3224,3594, 276,1050,2725, 758, # 2032 +1126, 653,2960,3296,5235,2342, 889,3595,4022,3104,3007, 903,1250,4609,4023,3481, # 2048 +3596,1342,1681,1718, 766,3297, 286, 89,2961,3715,5236,1713,5237,2607,3371,3008, # 2064 +5238,2962,2219,3225,2880,5239,4610,2505,2533, 181, 387,1075,4024, 731,2190,3372, # 2080 +5240,3298, 310, 313,3482,2304, 770,4278, 54,3054, 189,4611,3105,3848,4025,5241, # 2096 +1230,1617,1850, 355,3597,4279,4612,3373, 111,4280,3716,1350,3160,3483,3055,4281, # 2112 +2150,3299,3598,5242,2797,4026,4027,3009, 722,2009,5243,1071, 247,1207,2343,2478, # 2128 +1378,4613,2010, 864,1437,1214,4614, 373,3849,1142,2220, 667,4615, 442,2763,2563, # 2144 +3850,4028,1969,4282,3300,1840, 837, 170,1107, 934,1336,1883,5244,5245,2119,4283, # 2160 +2841, 743,1569,5246,4616,4284, 582,2389,1418,3484,5247,1803,5248, 357,1395,1729, # 2176 +3717,3301,2423,1564,2241,5249,3106,3851,1633,4617,1114,2086,4285,1532,5250, 482, # 2192 +2451,4618,5251,5252,1492, 833,1466,5253,2726,3599,1641,2842,5254,1526,1272,3718, # 2208 +4286,1686,1795, 416,2564,1903,1954,1804,5255,3852,2798,3853,1159,2321,5256,2881, # 2224 +4619,1610,1584,3056,2424,2764, 443,3302,1163,3161,5257,5258,4029,5259,4287,2506, # 2240 +3057,4620,4030,3162,2104,1647,3600,2011,1873,4288,5260,4289, 431,3485,5261, 250, # 2256 + 97, 81,4290,5262,1648,1851,1558, 160, 848,5263, 866, 740,1694,5264,2204,2843, # 2272 +3226,4291,4621,3719,1687, 950,2479, 426, 469,3227,3720,3721,4031,5265,5266,1188, # 2288 + 424,1996, 861,3601,4292,3854,2205,2694, 168,1235,3602,4293,5267,2087,1674,4622, # 2304 +3374,3303, 220,2565,1009,5268,3855, 670,3010, 332,1208, 717,5269,5270,3603,2452, # 2320 +4032,3375,5271, 513,5272,1209,2882,3376,3163,4623,1080,5273,5274,5275,5276,2534, # 2336 +3722,3604, 815,1587,4033,4034,5277,3605,3486,3856,1254,4624,1328,3058,1390,4035, # 2352 +1741,4036,3857,4037,5278, 236,3858,2453,3304,5279,5280,3723,3859,1273,3860,4625, # 2368 +5281, 308,5282,4626, 245,4627,1852,2480,1307,2583, 430, 715,2137,2454,5283, 270, # 2384 + 199,2883,4038,5284,3606,2727,1753, 761,1754, 725,1661,1841,4628,3487,3724,5285, # 2400 +5286, 587, 14,3305, 227,2608, 326, 480,2270, 943,2765,3607, 291, 650,1884,5287, # 2416 +1702,1226, 102,1547, 62,3488, 904,4629,3489,1164,4294,5288,5289,1224,1548,2766, # 2432 + 391, 498,1493,5290,1386,1419,5291,2056,1177,4630, 813, 880,1081,2368, 566,1145, # 2448 +4631,2291,1001,1035,2566,2609,2242, 394,1286,5292,5293,2069,5294, 86,1494,1730, # 2464 +4039, 491,1588, 745, 897,2963, 843,3377,4040,2767,2884,3306,1768, 998,2221,2070, # 2480 + 397,1827,1195,1970,3725,3011,3378, 284,5295,3861,2507,2138,2120,1904,5296,4041, # 2496 +2151,4042,4295,1036,3490,1905, 114,2567,4296, 209,1527,5297,5298,2964,2844,2635, # 2512 +2390,2728,3164, 812,2568,5299,3307,5300,1559, 737,1885,3726,1210, 885, 28,2695, # 2528 +3608,3862,5301,4297,1004,1780,4632,5302, 346,1982,2222,2696,4633,3863,1742, 797, # 2544 +1642,4043,1934,1072,1384,2152, 896,4044,3308,3727,3228,2885,3609,5303,2569,1959, # 2560 +4634,2455,1786,5304,5305,5306,4045,4298,1005,1308,3728,4299,2729,4635,4636,1528, # 2576 +2610, 161,1178,4300,1983, 987,4637,1101,4301, 631,4046,1157,3229,2425,1343,1241, # 2592 +1016,2243,2570, 372, 877,2344,2508,1160, 555,1935, 911,4047,5307, 466,1170, 169, # 2608 +1051,2921,2697,3729,2481,3012,1182,2012,2571,1251,2636,5308, 992,2345,3491,1540, # 2624 +2730,1201,2071,2406,1997,2482,5309,4638, 528,1923,2191,1503,1874,1570,2369,3379, # 2640 +3309,5310, 557,1073,5311,1828,3492,2088,2271,3165,3059,3107, 767,3108,2799,4639, # 2656 +1006,4302,4640,2346,1267,2179,3730,3230, 778,4048,3231,2731,1597,2667,5312,4641, # 2672 +5313,3493,5314,5315,5316,3310,2698,1433,3311, 131, 95,1504,4049, 723,4303,3166, # 2688 +1842,3610,2768,2192,4050,2028,2105,3731,5317,3013,4051,1218,5318,3380,3232,4052, # 2704 +4304,2584, 248,1634,3864, 912,5319,2845,3732,3060,3865, 654, 53,5320,3014,5321, # 2720 +1688,4642, 777,3494,1032,4053,1425,5322, 191, 820,2121,2846, 971,4643, 931,3233, # 2736 + 135, 664, 783,3866,1998, 772,2922,1936,4054,3867,4644,2923,3234, 282,2732, 640, # 2752 +1372,3495,1127, 922, 325,3381,5323,5324, 711,2045,5325,5326,4055,2223,2800,1937, # 2768 +4056,3382,2224,2255,3868,2305,5327,4645,3869,1258,3312,4057,3235,2139,2965,4058, # 2784 +4059,5328,2225, 258,3236,4646, 101,1227,5329,3313,1755,5330,1391,3314,5331,2924, # 2800 +2057, 893,5332,5333,5334,1402,4305,2347,5335,5336,3237,3611,5337,5338, 878,1325, # 2816 +1781,2801,4647, 259,1385,2585, 744,1183,2272,4648,5339,4060,2509,5340, 684,1024, # 2832 +4306,5341, 472,3612,3496,1165,3315,4061,4062, 322,2153, 881, 455,1695,1152,1340, # 2848 + 660, 554,2154,4649,1058,4650,4307, 830,1065,3383,4063,4651,1924,5342,1703,1919, # 2864 +5343, 932,2273, 122,5344,4652, 947, 677,5345,3870,2637, 297,1906,1925,2274,4653, # 2880 +2322,3316,5346,5347,4308,5348,4309, 84,4310, 112, 989,5349, 547,1059,4064, 701, # 2896 +3613,1019,5350,4311,5351,3497, 942, 639, 457,2306,2456, 993,2966, 407, 851, 494, # 2912 +4654,3384, 927,5352,1237,5353,2426,3385, 573,4312, 680, 921,2925,1279,1875, 285, # 2928 + 790,1448,1984, 719,2168,5354,5355,4655,4065,4066,1649,5356,1541, 563,5357,1077, # 2944 +5358,3386,3061,3498, 511,3015,4067,4068,3733,4069,1268,2572,3387,3238,4656,4657, # 2960 +5359, 535,1048,1276,1189,2926,2029,3167,1438,1373,2847,2967,1134,2013,5360,4313, # 2976 +1238,2586,3109,1259,5361, 700,5362,2968,3168,3734,4314,5363,4315,1146,1876,1907, # 2992 +4658,2611,4070, 781,2427, 132,1589, 203, 147, 273,2802,2407, 898,1787,2155,4071, # 3008 +4072,5364,3871,2803,5365,5366,4659,4660,5367,3239,5368,1635,3872, 965,5369,1805, # 3024 +2699,1516,3614,1121,1082,1329,3317,4073,1449,3873, 65,1128,2848,2927,2769,1590, # 3040 +3874,5370,5371, 12,2668, 45, 976,2587,3169,4661, 517,2535,1013,1037,3240,5372, # 3056 +3875,2849,5373,3876,5374,3499,5375,2612, 614,1999,2323,3877,3110,2733,2638,5376, # 3072 +2588,4316, 599,1269,5377,1811,3735,5378,2700,3111, 759,1060, 489,1806,3388,3318, # 3088 +1358,5379,5380,2391,1387,1215,2639,2256, 490,5381,5382,4317,1759,2392,2348,5383, # 3104 +4662,3878,1908,4074,2640,1807,3241,4663,3500,3319,2770,2349, 874,5384,5385,3501, # 3120 +3736,1859, 91,2928,3737,3062,3879,4664,5386,3170,4075,2669,5387,3502,1202,1403, # 3136 +3880,2969,2536,1517,2510,4665,3503,2511,5388,4666,5389,2701,1886,1495,1731,4076, # 3152 +2370,4667,5390,2030,5391,5392,4077,2702,1216, 237,2589,4318,2324,4078,3881,4668, # 3168 +4669,2703,3615,3504, 445,4670,5393,5394,5395,5396,2771, 61,4079,3738,1823,4080, # 3184 +5397, 687,2046, 935, 925, 405,2670, 703,1096,1860,2734,4671,4081,1877,1367,2704, # 3200 +3389, 918,2106,1782,2483, 334,3320,1611,1093,4672, 564,3171,3505,3739,3390, 945, # 3216 +2641,2058,4673,5398,1926, 872,4319,5399,3506,2705,3112, 349,4320,3740,4082,4674, # 3232 +3882,4321,3741,2156,4083,4675,4676,4322,4677,2408,2047, 782,4084, 400, 251,4323, # 3248 +1624,5400,5401, 277,3742, 299,1265, 476,1191,3883,2122,4324,4325,1109, 205,5402, # 3264 +2590,1000,2157,3616,1861,5403,5404,5405,4678,5406,4679,2573, 107,2484,2158,4085, # 3280 +3507,3172,5407,1533, 541,1301, 158, 753,4326,2886,3617,5408,1696, 370,1088,4327, # 3296 +4680,3618, 579, 327, 440, 162,2244, 269,1938,1374,3508, 968,3063, 56,1396,3113, # 3312 +2107,3321,3391,5409,1927,2159,4681,3016,5410,3619,5411,5412,3743,4682,2485,5413, # 3328 +2804,5414,1650,4683,5415,2613,5416,5417,4086,2671,3392,1149,3393,4087,3884,4088, # 3344 +5418,1076, 49,5419, 951,3242,3322,3323, 450,2850, 920,5420,1812,2805,2371,4328, # 3360 +1909,1138,2372,3885,3509,5421,3243,4684,1910,1147,1518,2428,4685,3886,5422,4686, # 3376 +2393,2614, 260,1796,3244,5423,5424,3887,3324, 708,5425,3620,1704,5426,3621,1351, # 3392 +1618,3394,3017,1887, 944,4329,3395,4330,3064,3396,4331,5427,3744, 422, 413,1714, # 3408 +3325, 500,2059,2350,4332,2486,5428,1344,1911, 954,5429,1668,5430,5431,4089,2409, # 3424 +4333,3622,3888,4334,5432,2307,1318,2512,3114, 133,3115,2887,4687, 629, 31,2851, # 3440 +2706,3889,4688, 850, 949,4689,4090,2970,1732,2089,4335,1496,1853,5433,4091, 620, # 3456 +3245, 981,1242,3745,3397,1619,3746,1643,3326,2140,2457,1971,1719,3510,2169,5434, # 3472 +3246,5435,5436,3398,1829,5437,1277,4690,1565,2048,5438,1636,3623,3116,5439, 869, # 3488 +2852, 655,3890,3891,3117,4092,3018,3892,1310,3624,4691,5440,5441,5442,1733, 558, # 3504 +4692,3747, 335,1549,3065,1756,4336,3748,1946,3511,1830,1291,1192, 470,2735,2108, # 3520 +2806, 913,1054,4093,5443,1027,5444,3066,4094,4693, 982,2672,3399,3173,3512,3247, # 3536 +3248,1947,2807,5445, 571,4694,5446,1831,5447,3625,2591,1523,2429,5448,2090, 984, # 3552 +4695,3749,1960,5449,3750, 852, 923,2808,3513,3751, 969,1519, 999,2049,2325,1705, # 3568 +5450,3118, 615,1662, 151, 597,4095,2410,2326,1049, 275,4696,3752,4337, 568,3753, # 3584 +3626,2487,4338,3754,5451,2430,2275, 409,3249,5452,1566,2888,3514,1002, 769,2853, # 3600 + 194,2091,3174,3755,2226,3327,4339, 628,1505,5453,5454,1763,2180,3019,4096, 521, # 3616 +1161,2592,1788,2206,2411,4697,4097,1625,4340,4341, 412, 42,3119, 464,5455,2642, # 3632 +4698,3400,1760,1571,2889,3515,2537,1219,2207,3893,2643,2141,2373,4699,4700,3328, # 3648 +1651,3401,3627,5456,5457,3628,2488,3516,5458,3756,5459,5460,2276,2092, 460,5461, # 3664 +4701,5462,3020, 962, 588,3629, 289,3250,2644,1116, 52,5463,3067,1797,5464,5465, # 3680 +5466,1467,5467,1598,1143,3757,4342,1985,1734,1067,4702,1280,3402, 465,4703,1572, # 3696 + 510,5468,1928,2245,1813,1644,3630,5469,4704,3758,5470,5471,2673,1573,1534,5472, # 3712 +5473, 536,1808,1761,3517,3894,3175,2645,5474,5475,5476,4705,3518,2929,1912,2809, # 3728 +5477,3329,1122, 377,3251,5478, 360,5479,5480,4343,1529, 551,5481,2060,3759,1769, # 3744 +2431,5482,2930,4344,3330,3120,2327,2109,2031,4706,1404, 136,1468,1479, 672,1171, # 3760 +3252,2308, 271,3176,5483,2772,5484,2050, 678,2736, 865,1948,4707,5485,2014,4098, # 3776 +2971,5486,2737,2227,1397,3068,3760,4708,4709,1735,2931,3403,3631,5487,3895, 509, # 3792 +2854,2458,2890,3896,5488,5489,3177,3178,4710,4345,2538,4711,2309,1166,1010, 552, # 3808 + 681,1888,5490,5491,2972,2973,4099,1287,1596,1862,3179, 358, 453, 736, 175, 478, # 3824 +1117, 905,1167,1097,5492,1854,1530,5493,1706,5494,2181,3519,2292,3761,3520,3632, # 3840 +4346,2093,4347,5495,3404,1193,2489,4348,1458,2193,2208,1863,1889,1421,3331,2932, # 3856 +3069,2182,3521, 595,2123,5496,4100,5497,5498,4349,1707,2646, 223,3762,1359, 751, # 3872 +3121, 183,3522,5499,2810,3021, 419,2374, 633, 704,3897,2394, 241,5500,5501,5502, # 3888 + 838,3022,3763,2277,2773,2459,3898,1939,2051,4101,1309,3122,2246,1181,5503,1136, # 3904 +2209,3899,2375,1446,4350,2310,4712,5504,5505,4351,1055,2615, 484,3764,5506,4102, # 3920 + 625,4352,2278,3405,1499,4353,4103,5507,4104,4354,3253,2279,2280,3523,5508,5509, # 3936 +2774, 808,2616,3765,3406,4105,4355,3123,2539, 526,3407,3900,4356, 955,5510,1620, # 3952 +4357,2647,2432,5511,1429,3766,1669,1832, 994, 928,5512,3633,1260,5513,5514,5515, # 3968 +1949,2293, 741,2933,1626,4358,2738,2460, 867,1184, 362,3408,1392,5516,5517,4106, # 3984 +4359,1770,1736,3254,2934,4713,4714,1929,2707,1459,1158,5518,3070,3409,2891,1292, # 4000 +1930,2513,2855,3767,1986,1187,2072,2015,2617,4360,5519,2574,2514,2170,3768,2490, # 4016 +3332,5520,3769,4715,5521,5522, 666,1003,3023,1022,3634,4361,5523,4716,1814,2257, # 4032 + 574,3901,1603, 295,1535, 705,3902,4362, 283, 858, 417,5524,5525,3255,4717,4718, # 4048 +3071,1220,1890,1046,2281,2461,4107,1393,1599, 689,2575, 388,4363,5526,2491, 802, # 4064 +5527,2811,3903,2061,1405,2258,5528,4719,3904,2110,1052,1345,3256,1585,5529, 809, # 4080 +5530,5531,5532, 575,2739,3524, 956,1552,1469,1144,2328,5533,2329,1560,2462,3635, # 4096 +3257,4108, 616,2210,4364,3180,2183,2294,5534,1833,5535,3525,4720,5536,1319,3770, # 4112 +3771,1211,3636,1023,3258,1293,2812,5537,5538,5539,3905, 607,2311,3906, 762,2892, # 4128 +1439,4365,1360,4721,1485,3072,5540,4722,1038,4366,1450,2062,2648,4367,1379,4723, # 4144 +2593,5541,5542,4368,1352,1414,2330,2935,1172,5543,5544,3907,3908,4724,1798,1451, # 4160 +5545,5546,5547,5548,2936,4109,4110,2492,2351, 411,4111,4112,3637,3333,3124,4725, # 4176 +1561,2674,1452,4113,1375,5549,5550, 47,2974, 316,5551,1406,1591,2937,3181,5552, # 4192 +1025,2142,3125,3182, 354,2740, 884,2228,4369,2412, 508,3772, 726,3638, 996,2433, # 4208 +3639, 729,5553, 392,2194,1453,4114,4726,3773,5554,5555,2463,3640,2618,1675,2813, # 4224 + 919,2352,2975,2353,1270,4727,4115, 73,5556,5557, 647,5558,3259,2856,2259,1550, # 4240 +1346,3024,5559,1332, 883,3526,5560,5561,5562,5563,3334,2775,5564,1212, 831,1347, # 4256 +4370,4728,2331,3909,1864,3073, 720,3910,4729,4730,3911,5565,4371,5566,5567,4731, # 4272 +5568,5569,1799,4732,3774,2619,4733,3641,1645,2376,4734,5570,2938, 669,2211,2675, # 4288 +2434,5571,2893,5572,5573,1028,3260,5574,4372,2413,5575,2260,1353,5576,5577,4735, # 4304 +3183, 518,5578,4116,5579,4373,1961,5580,2143,4374,5581,5582,3025,2354,2355,3912, # 4320 + 516,1834,1454,4117,2708,4375,4736,2229,2620,1972,1129,3642,5583,2776,5584,2976, # 4336 +1422, 577,1470,3026,1524,3410,5585,5586, 432,4376,3074,3527,5587,2594,1455,2515, # 4352 +2230,1973,1175,5588,1020,2741,4118,3528,4737,5589,2742,5590,1743,1361,3075,3529, # 4368 +2649,4119,4377,4738,2295, 895, 924,4378,2171, 331,2247,3076, 166,1627,3077,1098, # 4384 +5591,1232,2894,2231,3411,4739, 657, 403,1196,2377, 542,3775,3412,1600,4379,3530, # 4400 +5592,4740,2777,3261, 576, 530,1362,4741,4742,2540,2676,3776,4120,5593, 842,3913, # 4416 +5594,2814,2032,1014,4121, 213,2709,3413, 665, 621,4380,5595,3777,2939,2435,5596, # 4432 +2436,3335,3643,3414,4743,4381,2541,4382,4744,3644,1682,4383,3531,1380,5597, 724, # 4448 +2282, 600,1670,5598,1337,1233,4745,3126,2248,5599,1621,4746,5600, 651,4384,5601, # 4464 +1612,4385,2621,5602,2857,5603,2743,2312,3078,5604, 716,2464,3079, 174,1255,2710, # 4480 +4122,3645, 548,1320,1398, 728,4123,1574,5605,1891,1197,3080,4124,5606,3081,3082, # 4496 +3778,3646,3779, 747,5607, 635,4386,4747,5608,5609,5610,4387,5611,5612,4748,5613, # 4512 +3415,4749,2437, 451,5614,3780,2542,2073,4388,2744,4389,4125,5615,1764,4750,5616, # 4528 +4390, 350,4751,2283,2395,2493,5617,4391,4126,2249,1434,4127, 488,4752, 458,4392, # 4544 +4128,3781, 771,1330,2396,3914,2576,3184,2160,2414,1553,2677,3185,4393,5618,2494, # 4560 +2895,2622,1720,2711,4394,3416,4753,5619,2543,4395,5620,3262,4396,2778,5621,2016, # 4576 +2745,5622,1155,1017,3782,3915,5623,3336,2313, 201,1865,4397,1430,5624,4129,5625, # 4592 +5626,5627,5628,5629,4398,1604,5630, 414,1866, 371,2595,4754,4755,3532,2017,3127, # 4608 +4756,1708, 960,4399, 887, 389,2172,1536,1663,1721,5631,2232,4130,2356,2940,1580, # 4624 +5632,5633,1744,4757,2544,4758,4759,5634,4760,5635,2074,5636,4761,3647,3417,2896, # 4640 +4400,5637,4401,2650,3418,2815, 673,2712,2465, 709,3533,4131,3648,4402,5638,1148, # 4656 + 502, 634,5639,5640,1204,4762,3649,1575,4763,2623,3783,5641,3784,3128, 948,3263, # 4672 + 121,1745,3916,1110,5642,4403,3083,2516,3027,4132,3785,1151,1771,3917,1488,4133, # 4688 +1987,5643,2438,3534,5644,5645,2094,5646,4404,3918,1213,1407,2816, 531,2746,2545, # 4704 +3264,1011,1537,4764,2779,4405,3129,1061,5647,3786,3787,1867,2897,5648,2018, 120, # 4720 +4406,4407,2063,3650,3265,2314,3919,2678,3419,1955,4765,4134,5649,3535,1047,2713, # 4736 +1266,5650,1368,4766,2858, 649,3420,3920,2546,2747,1102,2859,2679,5651,5652,2000, # 4752 +5653,1111,3651,2977,5654,2495,3921,3652,2817,1855,3421,3788,5655,5656,3422,2415, # 4768 +2898,3337,3266,3653,5657,2577,5658,3654,2818,4135,1460, 856,5659,3655,5660,2899, # 4784 +2978,5661,2900,3922,5662,4408, 632,2517, 875,3923,1697,3924,2296,5663,5664,4767, # 4800 +3028,1239, 580,4768,4409,5665, 914, 936,2075,1190,4136,1039,2124,5666,5667,5668, # 4816 +5669,3423,1473,5670,1354,4410,3925,4769,2173,3084,4137, 915,3338,4411,4412,3339, # 4832 +1605,1835,5671,2748, 398,3656,4413,3926,4138, 328,1913,2860,4139,3927,1331,4414, # 4848 +3029, 937,4415,5672,3657,4140,4141,3424,2161,4770,3425, 524, 742, 538,3085,1012, # 4864 +5673,5674,3928,2466,5675, 658,1103, 225,3929,5676,5677,4771,5678,4772,5679,3267, # 4880 +1243,5680,4142, 963,2250,4773,5681,2714,3658,3186,5682,5683,2596,2332,5684,4774, # 4896 +5685,5686,5687,3536, 957,3426,2547,2033,1931,2941,2467, 870,2019,3659,1746,2780, # 4912 +2781,2439,2468,5688,3930,5689,3789,3130,3790,3537,3427,3791,5690,1179,3086,5691, # 4928 +3187,2378,4416,3792,2548,3188,3131,2749,4143,5692,3428,1556,2549,2297, 977,2901, # 4944 +2034,4144,1205,3429,5693,1765,3430,3189,2125,1271, 714,1689,4775,3538,5694,2333, # 4960 +3931, 533,4417,3660,2184, 617,5695,2469,3340,3539,2315,5696,5697,3190,5698,5699, # 4976 +3932,1988, 618, 427,2651,3540,3431,5700,5701,1244,1690,5702,2819,4418,4776,5703, # 4992 +3541,4777,5704,2284,1576, 473,3661,4419,3432, 972,5705,3662,5706,3087,5707,5708, # 5008 +4778,4779,5709,3793,4145,4146,5710, 153,4780, 356,5711,1892,2902,4420,2144, 408, # 5024 + 803,2357,5712,3933,5713,4421,1646,2578,2518,4781,4782,3934,5714,3935,4422,5715, # 5040 +2416,3433, 752,5716,5717,1962,3341,2979,5718, 746,3030,2470,4783,4423,3794, 698, # 5056 +4784,1893,4424,3663,2550,4785,3664,3936,5719,3191,3434,5720,1824,1302,4147,2715, # 5072 +3937,1974,4425,5721,4426,3192, 823,1303,1288,1236,2861,3542,4148,3435, 774,3938, # 5088 +5722,1581,4786,1304,2862,3939,4787,5723,2440,2162,1083,3268,4427,4149,4428, 344, # 5104 +1173, 288,2316, 454,1683,5724,5725,1461,4788,4150,2597,5726,5727,4789, 985, 894, # 5120 +5728,3436,3193,5729,1914,2942,3795,1989,5730,2111,1975,5731,4151,5732,2579,1194, # 5136 + 425,5733,4790,3194,1245,3796,4429,5734,5735,2863,5736, 636,4791,1856,3940, 760, # 5152 +1800,5737,4430,2212,1508,4792,4152,1894,1684,2298,5738,5739,4793,4431,4432,2213, # 5168 + 479,5740,5741, 832,5742,4153,2496,5743,2980,2497,3797, 990,3132, 627,1815,2652, # 5184 +4433,1582,4434,2126,2112,3543,4794,5744, 799,4435,3195,5745,4795,2113,1737,3031, # 5200 +1018, 543, 754,4436,3342,1676,4796,4797,4154,4798,1489,5746,3544,5747,2624,2903, # 5216 +4155,5748,5749,2981,5750,5751,5752,5753,3196,4799,4800,2185,1722,5754,3269,3270, # 5232 +1843,3665,1715, 481, 365,1976,1857,5755,5756,1963,2498,4801,5757,2127,3666,3271, # 5248 + 433,1895,2064,2076,5758, 602,2750,5759,5760,5761,5762,5763,3032,1628,3437,5764, # 5264 +3197,4802,4156,2904,4803,2519,5765,2551,2782,5766,5767,5768,3343,4804,2905,5769, # 5280 +4805,5770,2864,4806,4807,1221,2982,4157,2520,5771,5772,5773,1868,1990,5774,5775, # 5296 +5776,1896,5777,5778,4808,1897,4158, 318,5779,2095,4159,4437,5780,5781, 485,5782, # 5312 + 938,3941, 553,2680, 116,5783,3942,3667,5784,3545,2681,2783,3438,3344,2820,5785, # 5328 +3668,2943,4160,1747,2944,2983,5786,5787, 207,5788,4809,5789,4810,2521,5790,3033, # 5344 + 890,3669,3943,5791,1878,3798,3439,5792,2186,2358,3440,1652,5793,5794,5795, 941, # 5360 +2299, 208,3546,4161,2020, 330,4438,3944,2906,2499,3799,4439,4811,5796,5797,5798, # 5376 +) + diff --git a/venv2/lib/python2.7/site-packages/pip/_vendor/chardet/big5prober.py b/venv2/lib/python2.7/site-packages/pip/_vendor/chardet/big5prober.py new file mode 100644 index 0000000..98f9970 --- /dev/null +++ b/venv2/lib/python2.7/site-packages/pip/_vendor/chardet/big5prober.py @@ -0,0 +1,47 @@ +######################## BEGIN LICENSE BLOCK ######################## +# The Original Code is Mozilla Communicator client code. +# +# The Initial Developer of the Original Code is +# Netscape Communications Corporation. +# Portions created by the Initial Developer are Copyright (C) 1998 +# the Initial Developer. All Rights Reserved. +# +# Contributor(s): +# Mark Pilgrim - port to Python +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA +# 02110-1301 USA +######################### END LICENSE BLOCK ######################### + +from .mbcharsetprober import MultiByteCharSetProber +from .codingstatemachine import CodingStateMachine +from .chardistribution import Big5DistributionAnalysis +from .mbcssm import BIG5_SM_MODEL + + +class Big5Prober(MultiByteCharSetProber): + def __init__(self): + super(Big5Prober, self).__init__() + self.coding_sm = CodingStateMachine(BIG5_SM_MODEL) + self.distribution_analyzer = Big5DistributionAnalysis() + self.reset() + + @property + def charset_name(self): + return "Big5" + + @property + def language(self): + return "Chinese" diff --git a/venv2/lib/python2.7/site-packages/pip/_vendor/chardet/chardistribution.py b/venv2/lib/python2.7/site-packages/pip/_vendor/chardet/chardistribution.py new file mode 100644 index 0000000..c0395f4 --- /dev/null +++ b/venv2/lib/python2.7/site-packages/pip/_vendor/chardet/chardistribution.py @@ -0,0 +1,233 @@ +######################## BEGIN LICENSE BLOCK ######################## +# The Original Code is Mozilla Communicator client code. +# +# The Initial Developer of the Original Code is +# Netscape Communications Corporation. +# Portions created by the Initial Developer are Copyright (C) 1998 +# the Initial Developer. All Rights Reserved. +# +# Contributor(s): +# Mark Pilgrim - port to Python +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA +# 02110-1301 USA +######################### END LICENSE BLOCK ######################### + +from .euctwfreq import (EUCTW_CHAR_TO_FREQ_ORDER, EUCTW_TABLE_SIZE, + EUCTW_TYPICAL_DISTRIBUTION_RATIO) +from .euckrfreq import (EUCKR_CHAR_TO_FREQ_ORDER, EUCKR_TABLE_SIZE, + EUCKR_TYPICAL_DISTRIBUTION_RATIO) +from .gb2312freq import (GB2312_CHAR_TO_FREQ_ORDER, GB2312_TABLE_SIZE, + GB2312_TYPICAL_DISTRIBUTION_RATIO) +from .big5freq import (BIG5_CHAR_TO_FREQ_ORDER, BIG5_TABLE_SIZE, + BIG5_TYPICAL_DISTRIBUTION_RATIO) +from .jisfreq import (JIS_CHAR_TO_FREQ_ORDER, JIS_TABLE_SIZE, + JIS_TYPICAL_DISTRIBUTION_RATIO) + + +class CharDistributionAnalysis(object): + ENOUGH_DATA_THRESHOLD = 1024 + SURE_YES = 0.99 + SURE_NO = 0.01 + MINIMUM_DATA_THRESHOLD = 3 + + def __init__(self): + # Mapping table to get frequency order from char order (get from + # GetOrder()) + self._char_to_freq_order = None + self._table_size = None # Size of above table + # This is a constant value which varies from language to language, + # used in calculating confidence. See + # http://www.mozilla.org/projects/intl/UniversalCharsetDetection.html + # for further detail. + self.typical_distribution_ratio = None + self._done = None + self._total_chars = None + self._freq_chars = None + self.reset() + + def reset(self): + """reset analyser, clear any state""" + # If this flag is set to True, detection is done and conclusion has + # been made + self._done = False + self._total_chars = 0 # Total characters encountered + # The number of characters whose frequency order is less than 512 + self._freq_chars = 0 + + def feed(self, char, char_len): + """feed a character with known length""" + if char_len == 2: + # we only care about 2-bytes character in our distribution analysis + order = self.get_order(char) + else: + order = -1 + if order >= 0: + self._total_chars += 1 + # order is valid + if order < self._table_size: + if 512 > self._char_to_freq_order[order]: + self._freq_chars += 1 + + def get_confidence(self): + """return confidence based on existing data""" + # if we didn't receive any character in our consideration range, + # return negative answer + if self._total_chars <= 0 or self._freq_chars <= self.MINIMUM_DATA_THRESHOLD: + return self.SURE_NO + + if self._total_chars != self._freq_chars: + r = (self._freq_chars / ((self._total_chars - self._freq_chars) + * self.typical_distribution_ratio)) + if r < self.SURE_YES: + return r + + # normalize confidence (we don't want to be 100% sure) + return self.SURE_YES + + def got_enough_data(self): + # It is not necessary to receive all data to draw conclusion. + # For charset detection, certain amount of data is enough + return self._total_chars > self.ENOUGH_DATA_THRESHOLD + + def get_order(self, byte_str): + # We do not handle characters based on the original encoding string, + # but convert this encoding string to a number, here called order. + # This allows multiple encodings of a language to share one frequency + # table. + return -1 + + +class EUCTWDistributionAnalysis(CharDistributionAnalysis): + def __init__(self): + super(EUCTWDistributionAnalysis, self).__init__() + self._char_to_freq_order = EUCTW_CHAR_TO_FREQ_ORDER + self._table_size = EUCTW_TABLE_SIZE + self.typical_distribution_ratio = EUCTW_TYPICAL_DISTRIBUTION_RATIO + + def get_order(self, byte_str): + # for euc-TW encoding, we are interested + # first byte range: 0xc4 -- 0xfe + # second byte range: 0xa1 -- 0xfe + # no validation needed here. State machine has done that + first_char = byte_str[0] + if first_char >= 0xC4: + return 94 * (first_char - 0xC4) + byte_str[1] - 0xA1 + else: + return -1 + + +class EUCKRDistributionAnalysis(CharDistributionAnalysis): + def __init__(self): + super(EUCKRDistributionAnalysis, self).__init__() + self._char_to_freq_order = EUCKR_CHAR_TO_FREQ_ORDER + self._table_size = EUCKR_TABLE_SIZE + self.typical_distribution_ratio = EUCKR_TYPICAL_DISTRIBUTION_RATIO + + def get_order(self, byte_str): + # for euc-KR encoding, we are interested + # first byte range: 0xb0 -- 0xfe + # second byte range: 0xa1 -- 0xfe + # no validation needed here. State machine has done that + first_char = byte_str[0] + if first_char >= 0xB0: + return 94 * (first_char - 0xB0) + byte_str[1] - 0xA1 + else: + return -1 + + +class GB2312DistributionAnalysis(CharDistributionAnalysis): + def __init__(self): + super(GB2312DistributionAnalysis, self).__init__() + self._char_to_freq_order = GB2312_CHAR_TO_FREQ_ORDER + self._table_size = GB2312_TABLE_SIZE + self.typical_distribution_ratio = GB2312_TYPICAL_DISTRIBUTION_RATIO + + def get_order(self, byte_str): + # for GB2312 encoding, we are interested + # first byte range: 0xb0 -- 0xfe + # second byte range: 0xa1 -- 0xfe + # no validation needed here. State machine has done that + first_char, second_char = byte_str[0], byte_str[1] + if (first_char >= 0xB0) and (second_char >= 0xA1): + return 94 * (first_char - 0xB0) + second_char - 0xA1 + else: + return -1 + + +class Big5DistributionAnalysis(CharDistributionAnalysis): + def __init__(self): + super(Big5DistributionAnalysis, self).__init__() + self._char_to_freq_order = BIG5_CHAR_TO_FREQ_ORDER + self._table_size = BIG5_TABLE_SIZE + self.typical_distribution_ratio = BIG5_TYPICAL_DISTRIBUTION_RATIO + + def get_order(self, byte_str): + # for big5 encoding, we are interested + # first byte range: 0xa4 -- 0xfe + # second byte range: 0x40 -- 0x7e , 0xa1 -- 0xfe + # no validation needed here. State machine has done that + first_char, second_char = byte_str[0], byte_str[1] + if first_char >= 0xA4: + if second_char >= 0xA1: + return 157 * (first_char - 0xA4) + second_char - 0xA1 + 63 + else: + return 157 * (first_char - 0xA4) + second_char - 0x40 + else: + return -1 + + +class SJISDistributionAnalysis(CharDistributionAnalysis): + def __init__(self): + super(SJISDistributionAnalysis, self).__init__() + self._char_to_freq_order = JIS_CHAR_TO_FREQ_ORDER + self._table_size = JIS_TABLE_SIZE + self.typical_distribution_ratio = JIS_TYPICAL_DISTRIBUTION_RATIO + + def get_order(self, byte_str): + # for sjis encoding, we are interested + # first byte range: 0x81 -- 0x9f , 0xe0 -- 0xfe + # second byte range: 0x40 -- 0x7e, 0x81 -- oxfe + # no validation needed here. State machine has done that + first_char, second_char = byte_str[0], byte_str[1] + if (first_char >= 0x81) and (first_char <= 0x9F): + order = 188 * (first_char - 0x81) + elif (first_char >= 0xE0) and (first_char <= 0xEF): + order = 188 * (first_char - 0xE0 + 31) + else: + return -1 + order = order + second_char - 0x40 + if second_char > 0x7F: + order = -1 + return order + + +class EUCJPDistributionAnalysis(CharDistributionAnalysis): + def __init__(self): + super(EUCJPDistributionAnalysis, self).__init__() + self._char_to_freq_order = JIS_CHAR_TO_FREQ_ORDER + self._table_size = JIS_TABLE_SIZE + self.typical_distribution_ratio = JIS_TYPICAL_DISTRIBUTION_RATIO + + def get_order(self, byte_str): + # for euc-JP encoding, we are interested + # first byte range: 0xa0 -- 0xfe + # second byte range: 0xa1 -- 0xfe + # no validation needed here. State machine has done that + char = byte_str[0] + if char >= 0xA0: + return 94 * (char - 0xA1) + byte_str[1] - 0xa1 + else: + return -1 diff --git a/venv2/lib/python2.7/site-packages/pip/_vendor/chardet/charsetgroupprober.py b/venv2/lib/python2.7/site-packages/pip/_vendor/chardet/charsetgroupprober.py new file mode 100644 index 0000000..8b3738e --- /dev/null +++ b/venv2/lib/python2.7/site-packages/pip/_vendor/chardet/charsetgroupprober.py @@ -0,0 +1,106 @@ +######################## BEGIN LICENSE BLOCK ######################## +# The Original Code is Mozilla Communicator client code. +# +# The Initial Developer of the Original Code is +# Netscape Communications Corporation. +# Portions created by the Initial Developer are Copyright (C) 1998 +# the Initial Developer. All Rights Reserved. +# +# Contributor(s): +# Mark Pilgrim - port to Python +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA +# 02110-1301 USA +######################### END LICENSE BLOCK ######################### + +from .enums import ProbingState +from .charsetprober import CharSetProber + + +class CharSetGroupProber(CharSetProber): + def __init__(self, lang_filter=None): + super(CharSetGroupProber, self).__init__(lang_filter=lang_filter) + self._active_num = 0 + self.probers = [] + self._best_guess_prober = None + + def reset(self): + super(CharSetGroupProber, self).reset() + self._active_num = 0 + for prober in self.probers: + if prober: + prober.reset() + prober.active = True + self._active_num += 1 + self._best_guess_prober = None + + @property + def charset_name(self): + if not self._best_guess_prober: + self.get_confidence() + if not self._best_guess_prober: + return None + return self._best_guess_prober.charset_name + + @property + def language(self): + if not self._best_guess_prober: + self.get_confidence() + if not self._best_guess_prober: + return None + return self._best_guess_prober.language + + def feed(self, byte_str): + for prober in self.probers: + if not prober: + continue + if not prober.active: + continue + state = prober.feed(byte_str) + if not state: + continue + if state == ProbingState.FOUND_IT: + self._best_guess_prober = prober + return self.state + elif state == ProbingState.NOT_ME: + prober.active = False + self._active_num -= 1 + if self._active_num <= 0: + self._state = ProbingState.NOT_ME + return self.state + return self.state + + def get_confidence(self): + state = self.state + if state == ProbingState.FOUND_IT: + return 0.99 + elif state == ProbingState.NOT_ME: + return 0.01 + best_conf = 0.0 + self._best_guess_prober = None + for prober in self.probers: + if not prober: + continue + if not prober.active: + self.logger.debug('%s not active', prober.charset_name) + continue + conf = prober.get_confidence() + self.logger.debug('%s %s confidence = %s', prober.charset_name, prober.language, conf) + if best_conf < conf: + best_conf = conf + self._best_guess_prober = prober + if not self._best_guess_prober: + return 0.0 + return best_conf diff --git a/venv2/lib/python2.7/site-packages/pip/_vendor/chardet/charsetprober.py b/venv2/lib/python2.7/site-packages/pip/_vendor/chardet/charsetprober.py new file mode 100644 index 0000000..eac4e59 --- /dev/null +++ b/venv2/lib/python2.7/site-packages/pip/_vendor/chardet/charsetprober.py @@ -0,0 +1,145 @@ +######################## BEGIN LICENSE BLOCK ######################## +# The Original Code is Mozilla Universal charset detector code. +# +# The Initial Developer of the Original Code is +# Netscape Communications Corporation. +# Portions created by the Initial Developer are Copyright (C) 2001 +# the Initial Developer. All Rights Reserved. +# +# Contributor(s): +# Mark Pilgrim - port to Python +# Shy Shalom - original C code +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA +# 02110-1301 USA +######################### END LICENSE BLOCK ######################### + +import logging +import re + +from .enums import ProbingState + + +class CharSetProber(object): + + SHORTCUT_THRESHOLD = 0.95 + + def __init__(self, lang_filter=None): + self._state = None + self.lang_filter = lang_filter + self.logger = logging.getLogger(__name__) + + def reset(self): + self._state = ProbingState.DETECTING + + @property + def charset_name(self): + return None + + def feed(self, buf): + pass + + @property + def state(self): + return self._state + + def get_confidence(self): + return 0.0 + + @staticmethod + def filter_high_byte_only(buf): + buf = re.sub(b'([\x00-\x7F])+', b' ', buf) + return buf + + @staticmethod + def filter_international_words(buf): + """ + We define three types of bytes: + alphabet: english alphabets [a-zA-Z] + international: international characters [\x80-\xFF] + marker: everything else [^a-zA-Z\x80-\xFF] + + The input buffer can be thought to contain a series of words delimited + by markers. This function works to filter all words that contain at + least one international character. All contiguous sequences of markers + are replaced by a single space ascii character. + + This filter applies to all scripts which do not use English characters. + """ + filtered = bytearray() + + # This regex expression filters out only words that have at-least one + # international character. The word may include one marker character at + # the end. + words = re.findall(b'[a-zA-Z]*[\x80-\xFF]+[a-zA-Z]*[^a-zA-Z\x80-\xFF]?', + buf) + + for word in words: + filtered.extend(word[:-1]) + + # If the last character in the word is a marker, replace it with a + # space as markers shouldn't affect our analysis (they are used + # similarly across all languages and may thus have similar + # frequencies). + last_char = word[-1:] + if not last_char.isalpha() and last_char < b'\x80': + last_char = b' ' + filtered.extend(last_char) + + return filtered + + @staticmethod + def filter_with_english_letters(buf): + """ + Returns a copy of ``buf`` that retains only the sequences of English + alphabet and high byte characters that are not between <> characters. + Also retains English alphabet and high byte characters immediately + before occurrences of >. + + This filter can be applied to all scripts which contain both English + characters and extended ASCII characters, but is currently only used by + ``Latin1Prober``. + """ + filtered = bytearray() + in_tag = False + prev = 0 + + for curr in range(len(buf)): + # Slice here to get bytes instead of an int with Python 3 + buf_char = buf[curr:curr + 1] + # Check if we're coming out of or entering an HTML tag + if buf_char == b'>': + in_tag = False + elif buf_char == b'<': + in_tag = True + + # If current character is not extended-ASCII and not alphabetic... + if buf_char < b'\x80' and not buf_char.isalpha(): + # ...and we're not in a tag + if curr > prev and not in_tag: + # Keep everything after last non-extended-ASCII, + # non-alphabetic character + filtered.extend(buf[prev:curr]) + # Output a space to delimit stretch we kept + filtered.extend(b' ') + prev = curr + 1 + + # If we're not in a tag... + if not in_tag: + # Keep everything after last non-extended-ASCII, non-alphabetic + # character + filtered.extend(buf[prev:]) + + return filtered diff --git a/venv2/lib/python2.7/site-packages/pip/_vendor/chardet/cli/__init__.py b/venv2/lib/python2.7/site-packages/pip/_vendor/chardet/cli/__init__.py new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/venv2/lib/python2.7/site-packages/pip/_vendor/chardet/cli/__init__.py @@ -0,0 +1 @@ + diff --git a/venv2/lib/python2.7/site-packages/pip/_vendor/chardet/cli/chardetect.py b/venv2/lib/python2.7/site-packages/pip/_vendor/chardet/cli/chardetect.py new file mode 100644 index 0000000..c61136b --- /dev/null +++ b/venv2/lib/python2.7/site-packages/pip/_vendor/chardet/cli/chardetect.py @@ -0,0 +1,85 @@ +#!/usr/bin/env python +""" +Script which takes one or more file paths and reports on their detected +encodings + +Example:: + + % chardetect somefile someotherfile + somefile: windows-1252 with confidence 0.5 + someotherfile: ascii with confidence 1.0 + +If no paths are provided, it takes its input from stdin. + +""" + +from __future__ import absolute_import, print_function, unicode_literals + +import argparse +import sys + +from pip._vendor.chardet import __version__ +from pip._vendor.chardet.compat import PY2 +from pip._vendor.chardet.universaldetector import UniversalDetector + + +def description_of(lines, name='stdin'): + """ + Return a string describing the probable encoding of a file or + list of strings. + + :param lines: The lines to get the encoding of. + :type lines: Iterable of bytes + :param name: Name of file or collection of lines + :type name: str + """ + u = UniversalDetector() + for line in lines: + line = bytearray(line) + u.feed(line) + # shortcut out of the loop to save reading further - particularly useful if we read a BOM. + if u.done: + break + u.close() + result = u.result + if PY2: + name = name.decode(sys.getfilesystemencoding(), 'ignore') + if result['encoding']: + return '{0}: {1} with confidence {2}'.format(name, result['encoding'], + result['confidence']) + else: + return '{0}: no result'.format(name) + + +def main(argv=None): + """ + Handles command line arguments and gets things started. + + :param argv: List of arguments, as if specified on the command-line. + If None, ``sys.argv[1:]`` is used instead. + :type argv: list of str + """ + # Get command line arguments + parser = argparse.ArgumentParser( + description="Takes one or more file paths and reports their detected \ + encodings") + parser.add_argument('input', + help='File whose encoding we would like to determine. \ + (default: stdin)', + type=argparse.FileType('rb'), nargs='*', + default=[sys.stdin if PY2 else sys.stdin.buffer]) + parser.add_argument('--version', action='version', + version='%(prog)s {0}'.format(__version__)) + args = parser.parse_args(argv) + + for f in args.input: + if f.isatty(): + print("You are running chardetect interactively. Press " + + "CTRL-D twice at the start of a blank line to signal the " + + "end of your input. If you want help, run chardetect " + + "--help\n", file=sys.stderr) + print(description_of(f, f.name)) + + +if __name__ == '__main__': + main() diff --git a/venv2/lib/python2.7/site-packages/pip/_vendor/chardet/codingstatemachine.py b/venv2/lib/python2.7/site-packages/pip/_vendor/chardet/codingstatemachine.py new file mode 100644 index 0000000..68fba44 --- /dev/null +++ b/venv2/lib/python2.7/site-packages/pip/_vendor/chardet/codingstatemachine.py @@ -0,0 +1,88 @@ +######################## BEGIN LICENSE BLOCK ######################## +# The Original Code is mozilla.org code. +# +# The Initial Developer of the Original Code is +# Netscape Communications Corporation. +# Portions created by the Initial Developer are Copyright (C) 1998 +# the Initial Developer. All Rights Reserved. +# +# Contributor(s): +# Mark Pilgrim - port to Python +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA +# 02110-1301 USA +######################### END LICENSE BLOCK ######################### + +import logging + +from .enums import MachineState + + +class CodingStateMachine(object): + """ + A state machine to verify a byte sequence for a particular encoding. For + each byte the detector receives, it will feed that byte to every active + state machine available, one byte at a time. The state machine changes its + state based on its previous state and the byte it receives. There are 3 + states in a state machine that are of interest to an auto-detector: + + START state: This is the state to start with, or a legal byte sequence + (i.e. a valid code point) for character has been identified. + + ME state: This indicates that the state machine identified a byte sequence + that is specific to the charset it is designed for and that + there is no other possible encoding which can contain this byte + sequence. This will to lead to an immediate positive answer for + the detector. + + ERROR state: This indicates the state machine identified an illegal byte + sequence for that encoding. This will lead to an immediate + negative answer for this encoding. Detector will exclude this + encoding from consideration from here on. + """ + def __init__(self, sm): + self._model = sm + self._curr_byte_pos = 0 + self._curr_char_len = 0 + self._curr_state = None + self.logger = logging.getLogger(__name__) + self.reset() + + def reset(self): + self._curr_state = MachineState.START + + def next_state(self, c): + # for each byte we get its class + # if it is first byte, we also get byte length + byte_class = self._model['class_table'][c] + if self._curr_state == MachineState.START: + self._curr_byte_pos = 0 + self._curr_char_len = self._model['char_len_table'][byte_class] + # from byte's class and state_table, we get its next state + curr_state = (self._curr_state * self._model['class_factor'] + + byte_class) + self._curr_state = self._model['state_table'][curr_state] + self._curr_byte_pos += 1 + return self._curr_state + + def get_current_charlen(self): + return self._curr_char_len + + def get_coding_state_machine(self): + return self._model['name'] + + @property + def language(self): + return self._model['language'] diff --git a/venv2/lib/python2.7/site-packages/pip/_vendor/chardet/compat.py b/venv2/lib/python2.7/site-packages/pip/_vendor/chardet/compat.py new file mode 100644 index 0000000..ddd7468 --- /dev/null +++ b/venv2/lib/python2.7/site-packages/pip/_vendor/chardet/compat.py @@ -0,0 +1,34 @@ +######################## BEGIN LICENSE BLOCK ######################## +# Contributor(s): +# Dan Blanchard +# Ian Cordasco +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA +# 02110-1301 USA +######################### END LICENSE BLOCK ######################### + +import sys + + +if sys.version_info < (3, 0): + PY2 = True + PY3 = False + base_str = (str, unicode) + text_type = unicode +else: + PY2 = False + PY3 = True + base_str = (bytes, str) + text_type = str diff --git a/venv2/lib/python2.7/site-packages/pip/_vendor/chardet/cp949prober.py b/venv2/lib/python2.7/site-packages/pip/_vendor/chardet/cp949prober.py new file mode 100644 index 0000000..efd793a --- /dev/null +++ b/venv2/lib/python2.7/site-packages/pip/_vendor/chardet/cp949prober.py @@ -0,0 +1,49 @@ +######################## BEGIN LICENSE BLOCK ######################## +# The Original Code is mozilla.org code. +# +# The Initial Developer of the Original Code is +# Netscape Communications Corporation. +# Portions created by the Initial Developer are Copyright (C) 1998 +# the Initial Developer. All Rights Reserved. +# +# Contributor(s): +# Mark Pilgrim - port to Python +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA +# 02110-1301 USA +######################### END LICENSE BLOCK ######################### + +from .chardistribution import EUCKRDistributionAnalysis +from .codingstatemachine import CodingStateMachine +from .mbcharsetprober import MultiByteCharSetProber +from .mbcssm import CP949_SM_MODEL + + +class CP949Prober(MultiByteCharSetProber): + def __init__(self): + super(CP949Prober, self).__init__() + self.coding_sm = CodingStateMachine(CP949_SM_MODEL) + # NOTE: CP949 is a superset of EUC-KR, so the distribution should be + # not different. + self.distribution_analyzer = EUCKRDistributionAnalysis() + self.reset() + + @property + def charset_name(self): + return "CP949" + + @property + def language(self): + return "Korean" diff --git a/venv2/lib/python2.7/site-packages/pip/_vendor/chardet/enums.py b/venv2/lib/python2.7/site-packages/pip/_vendor/chardet/enums.py new file mode 100644 index 0000000..0451207 --- /dev/null +++ b/venv2/lib/python2.7/site-packages/pip/_vendor/chardet/enums.py @@ -0,0 +1,76 @@ +""" +All of the Enums that are used throughout the chardet package. + +:author: Dan Blanchard (dan.blanchard@gmail.com) +""" + + +class InputState(object): + """ + This enum represents the different states a universal detector can be in. + """ + PURE_ASCII = 0 + ESC_ASCII = 1 + HIGH_BYTE = 2 + + +class LanguageFilter(object): + """ + This enum represents the different language filters we can apply to a + ``UniversalDetector``. + """ + CHINESE_SIMPLIFIED = 0x01 + CHINESE_TRADITIONAL = 0x02 + JAPANESE = 0x04 + KOREAN = 0x08 + NON_CJK = 0x10 + ALL = 0x1F + CHINESE = CHINESE_SIMPLIFIED | CHINESE_TRADITIONAL + CJK = CHINESE | JAPANESE | KOREAN + + +class ProbingState(object): + """ + This enum represents the different states a prober can be in. + """ + DETECTING = 0 + FOUND_IT = 1 + NOT_ME = 2 + + +class MachineState(object): + """ + This enum represents the different states a state machine can be in. + """ + START = 0 + ERROR = 1 + ITS_ME = 2 + + +class SequenceLikelihood(object): + """ + This enum represents the likelihood of a character following the previous one. + """ + NEGATIVE = 0 + UNLIKELY = 1 + LIKELY = 2 + POSITIVE = 3 + + @classmethod + def get_num_categories(cls): + """:returns: The number of likelihood categories in the enum.""" + return 4 + + +class CharacterCategory(object): + """ + This enum represents the different categories language models for + ``SingleByteCharsetProber`` put characters into. + + Anything less than CONTROL is considered a letter. + """ + UNDEFINED = 255 + LINE_BREAK = 254 + SYMBOL = 253 + DIGIT = 252 + CONTROL = 251 diff --git a/venv2/lib/python2.7/site-packages/pip/_vendor/chardet/escprober.py b/venv2/lib/python2.7/site-packages/pip/_vendor/chardet/escprober.py new file mode 100644 index 0000000..c70493f --- /dev/null +++ b/venv2/lib/python2.7/site-packages/pip/_vendor/chardet/escprober.py @@ -0,0 +1,101 @@ +######################## BEGIN LICENSE BLOCK ######################## +# The Original Code is mozilla.org code. +# +# The Initial Developer of the Original Code is +# Netscape Communications Corporation. +# Portions created by the Initial Developer are Copyright (C) 1998 +# the Initial Developer. All Rights Reserved. +# +# Contributor(s): +# Mark Pilgrim - port to Python +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA +# 02110-1301 USA +######################### END LICENSE BLOCK ######################### + +from .charsetprober import CharSetProber +from .codingstatemachine import CodingStateMachine +from .enums import LanguageFilter, ProbingState, MachineState +from .escsm import (HZ_SM_MODEL, ISO2022CN_SM_MODEL, ISO2022JP_SM_MODEL, + ISO2022KR_SM_MODEL) + + +class EscCharSetProber(CharSetProber): + """ + This CharSetProber uses a "code scheme" approach for detecting encodings, + whereby easily recognizable escape or shift sequences are relied on to + identify these encodings. + """ + + def __init__(self, lang_filter=None): + super(EscCharSetProber, self).__init__(lang_filter=lang_filter) + self.coding_sm = [] + if self.lang_filter & LanguageFilter.CHINESE_SIMPLIFIED: + self.coding_sm.append(CodingStateMachine(HZ_SM_MODEL)) + self.coding_sm.append(CodingStateMachine(ISO2022CN_SM_MODEL)) + if self.lang_filter & LanguageFilter.JAPANESE: + self.coding_sm.append(CodingStateMachine(ISO2022JP_SM_MODEL)) + if self.lang_filter & LanguageFilter.KOREAN: + self.coding_sm.append(CodingStateMachine(ISO2022KR_SM_MODEL)) + self.active_sm_count = None + self._detected_charset = None + self._detected_language = None + self._state = None + self.reset() + + def reset(self): + super(EscCharSetProber, self).reset() + for coding_sm in self.coding_sm: + if not coding_sm: + continue + coding_sm.active = True + coding_sm.reset() + self.active_sm_count = len(self.coding_sm) + self._detected_charset = None + self._detected_language = None + + @property + def charset_name(self): + return self._detected_charset + + @property + def language(self): + return self._detected_language + + def get_confidence(self): + if self._detected_charset: + return 0.99 + else: + return 0.00 + + def feed(self, byte_str): + for c in byte_str: + for coding_sm in self.coding_sm: + if not coding_sm or not coding_sm.active: + continue + coding_state = coding_sm.next_state(c) + if coding_state == MachineState.ERROR: + coding_sm.active = False + self.active_sm_count -= 1 + if self.active_sm_count <= 0: + self._state = ProbingState.NOT_ME + return self.state + elif coding_state == MachineState.ITS_ME: + self._state = ProbingState.FOUND_IT + self._detected_charset = coding_sm.get_coding_state_machine() + self._detected_language = coding_sm.language + return self.state + + return self.state diff --git a/venv2/lib/python2.7/site-packages/pip/_vendor/chardet/escsm.py b/venv2/lib/python2.7/site-packages/pip/_vendor/chardet/escsm.py new file mode 100644 index 0000000..0069523 --- /dev/null +++ b/venv2/lib/python2.7/site-packages/pip/_vendor/chardet/escsm.py @@ -0,0 +1,246 @@ +######################## BEGIN LICENSE BLOCK ######################## +# The Original Code is mozilla.org code. +# +# The Initial Developer of the Original Code is +# Netscape Communications Corporation. +# Portions created by the Initial Developer are Copyright (C) 1998 +# the Initial Developer. All Rights Reserved. +# +# Contributor(s): +# Mark Pilgrim - port to Python +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA +# 02110-1301 USA +######################### END LICENSE BLOCK ######################### + +from .enums import MachineState + +HZ_CLS = ( +1,0,0,0,0,0,0,0, # 00 - 07 +0,0,0,0,0,0,0,0, # 08 - 0f +0,0,0,0,0,0,0,0, # 10 - 17 +0,0,0,1,0,0,0,0, # 18 - 1f +0,0,0,0,0,0,0,0, # 20 - 27 +0,0,0,0,0,0,0,0, # 28 - 2f +0,0,0,0,0,0,0,0, # 30 - 37 +0,0,0,0,0,0,0,0, # 38 - 3f +0,0,0,0,0,0,0,0, # 40 - 47 +0,0,0,0,0,0,0,0, # 48 - 4f +0,0,0,0,0,0,0,0, # 50 - 57 +0,0,0,0,0,0,0,0, # 58 - 5f +0,0,0,0,0,0,0,0, # 60 - 67 +0,0,0,0,0,0,0,0, # 68 - 6f +0,0,0,0,0,0,0,0, # 70 - 77 +0,0,0,4,0,5,2,0, # 78 - 7f +1,1,1,1,1,1,1,1, # 80 - 87 +1,1,1,1,1,1,1,1, # 88 - 8f +1,1,1,1,1,1,1,1, # 90 - 97 +1,1,1,1,1,1,1,1, # 98 - 9f +1,1,1,1,1,1,1,1, # a0 - a7 +1,1,1,1,1,1,1,1, # a8 - af +1,1,1,1,1,1,1,1, # b0 - b7 +1,1,1,1,1,1,1,1, # b8 - bf +1,1,1,1,1,1,1,1, # c0 - c7 +1,1,1,1,1,1,1,1, # c8 - cf +1,1,1,1,1,1,1,1, # d0 - d7 +1,1,1,1,1,1,1,1, # d8 - df +1,1,1,1,1,1,1,1, # e0 - e7 +1,1,1,1,1,1,1,1, # e8 - ef +1,1,1,1,1,1,1,1, # f0 - f7 +1,1,1,1,1,1,1,1, # f8 - ff +) + +HZ_ST = ( +MachineState.START,MachineState.ERROR, 3,MachineState.START,MachineState.START,MachineState.START,MachineState.ERROR,MachineState.ERROR,# 00-07 +MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ITS_ME,MachineState.ITS_ME,MachineState.ITS_ME,MachineState.ITS_ME,# 08-0f +MachineState.ITS_ME,MachineState.ITS_ME,MachineState.ERROR,MachineState.ERROR,MachineState.START,MachineState.START, 4,MachineState.ERROR,# 10-17 + 5,MachineState.ERROR, 6,MachineState.ERROR, 5, 5, 4,MachineState.ERROR,# 18-1f + 4,MachineState.ERROR, 4, 4, 4,MachineState.ERROR, 4,MachineState.ERROR,# 20-27 + 4,MachineState.ITS_ME,MachineState.START,MachineState.START,MachineState.START,MachineState.START,MachineState.START,MachineState.START,# 28-2f +) + +HZ_CHAR_LEN_TABLE = (0, 0, 0, 0, 0, 0) + +HZ_SM_MODEL = {'class_table': HZ_CLS, + 'class_factor': 6, + 'state_table': HZ_ST, + 'char_len_table': HZ_CHAR_LEN_TABLE, + 'name': "HZ-GB-2312", + 'language': 'Chinese'} + +ISO2022CN_CLS = ( +2,0,0,0,0,0,0,0, # 00 - 07 +0,0,0,0,0,0,0,0, # 08 - 0f +0,0,0,0,0,0,0,0, # 10 - 17 +0,0,0,1,0,0,0,0, # 18 - 1f +0,0,0,0,0,0,0,0, # 20 - 27 +0,3,0,0,0,0,0,0, # 28 - 2f +0,0,0,0,0,0,0,0, # 30 - 37 +0,0,0,0,0,0,0,0, # 38 - 3f +0,0,0,4,0,0,0,0, # 40 - 47 +0,0,0,0,0,0,0,0, # 48 - 4f +0,0,0,0,0,0,0,0, # 50 - 57 +0,0,0,0,0,0,0,0, # 58 - 5f +0,0,0,0,0,0,0,0, # 60 - 67 +0,0,0,0,0,0,0,0, # 68 - 6f +0,0,0,0,0,0,0,0, # 70 - 77 +0,0,0,0,0,0,0,0, # 78 - 7f +2,2,2,2,2,2,2,2, # 80 - 87 +2,2,2,2,2,2,2,2, # 88 - 8f +2,2,2,2,2,2,2,2, # 90 - 97 +2,2,2,2,2,2,2,2, # 98 - 9f +2,2,2,2,2,2,2,2, # a0 - a7 +2,2,2,2,2,2,2,2, # a8 - af +2,2,2,2,2,2,2,2, # b0 - b7 +2,2,2,2,2,2,2,2, # b8 - bf +2,2,2,2,2,2,2,2, # c0 - c7 +2,2,2,2,2,2,2,2, # c8 - cf +2,2,2,2,2,2,2,2, # d0 - d7 +2,2,2,2,2,2,2,2, # d8 - df +2,2,2,2,2,2,2,2, # e0 - e7 +2,2,2,2,2,2,2,2, # e8 - ef +2,2,2,2,2,2,2,2, # f0 - f7 +2,2,2,2,2,2,2,2, # f8 - ff +) + +ISO2022CN_ST = ( +MachineState.START, 3,MachineState.ERROR,MachineState.START,MachineState.START,MachineState.START,MachineState.START,MachineState.START,# 00-07 +MachineState.START,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,# 08-0f +MachineState.ERROR,MachineState.ERROR,MachineState.ITS_ME,MachineState.ITS_ME,MachineState.ITS_ME,MachineState.ITS_ME,MachineState.ITS_ME,MachineState.ITS_ME,# 10-17 +MachineState.ITS_ME,MachineState.ITS_ME,MachineState.ITS_ME,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR, 4,MachineState.ERROR,# 18-1f +MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ITS_ME,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,# 20-27 + 5, 6,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,# 28-2f +MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ITS_ME,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,# 30-37 +MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ITS_ME,MachineState.ERROR,MachineState.START,# 38-3f +) + +ISO2022CN_CHAR_LEN_TABLE = (0, 0, 0, 0, 0, 0, 0, 0, 0) + +ISO2022CN_SM_MODEL = {'class_table': ISO2022CN_CLS, + 'class_factor': 9, + 'state_table': ISO2022CN_ST, + 'char_len_table': ISO2022CN_CHAR_LEN_TABLE, + 'name': "ISO-2022-CN", + 'language': 'Chinese'} + +ISO2022JP_CLS = ( +2,0,0,0,0,0,0,0, # 00 - 07 +0,0,0,0,0,0,2,2, # 08 - 0f +0,0,0,0,0,0,0,0, # 10 - 17 +0,0,0,1,0,0,0,0, # 18 - 1f +0,0,0,0,7,0,0,0, # 20 - 27 +3,0,0,0,0,0,0,0, # 28 - 2f +0,0,0,0,0,0,0,0, # 30 - 37 +0,0,0,0,0,0,0,0, # 38 - 3f +6,0,4,0,8,0,0,0, # 40 - 47 +0,9,5,0,0,0,0,0, # 48 - 4f +0,0,0,0,0,0,0,0, # 50 - 57 +0,0,0,0,0,0,0,0, # 58 - 5f +0,0,0,0,0,0,0,0, # 60 - 67 +0,0,0,0,0,0,0,0, # 68 - 6f +0,0,0,0,0,0,0,0, # 70 - 77 +0,0,0,0,0,0,0,0, # 78 - 7f +2,2,2,2,2,2,2,2, # 80 - 87 +2,2,2,2,2,2,2,2, # 88 - 8f +2,2,2,2,2,2,2,2, # 90 - 97 +2,2,2,2,2,2,2,2, # 98 - 9f +2,2,2,2,2,2,2,2, # a0 - a7 +2,2,2,2,2,2,2,2, # a8 - af +2,2,2,2,2,2,2,2, # b0 - b7 +2,2,2,2,2,2,2,2, # b8 - bf +2,2,2,2,2,2,2,2, # c0 - c7 +2,2,2,2,2,2,2,2, # c8 - cf +2,2,2,2,2,2,2,2, # d0 - d7 +2,2,2,2,2,2,2,2, # d8 - df +2,2,2,2,2,2,2,2, # e0 - e7 +2,2,2,2,2,2,2,2, # e8 - ef +2,2,2,2,2,2,2,2, # f0 - f7 +2,2,2,2,2,2,2,2, # f8 - ff +) + +ISO2022JP_ST = ( +MachineState.START, 3,MachineState.ERROR,MachineState.START,MachineState.START,MachineState.START,MachineState.START,MachineState.START,# 00-07 +MachineState.START,MachineState.START,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,# 08-0f +MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ITS_ME,MachineState.ITS_ME,MachineState.ITS_ME,MachineState.ITS_ME,# 10-17 +MachineState.ITS_ME,MachineState.ITS_ME,MachineState.ITS_ME,MachineState.ITS_ME,MachineState.ITS_ME,MachineState.ITS_ME,MachineState.ERROR,MachineState.ERROR,# 18-1f +MachineState.ERROR, 5,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR, 4,MachineState.ERROR,MachineState.ERROR,# 20-27 +MachineState.ERROR,MachineState.ERROR,MachineState.ERROR, 6,MachineState.ITS_ME,MachineState.ERROR,MachineState.ITS_ME,MachineState.ERROR,# 28-2f +MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ITS_ME,MachineState.ITS_ME,# 30-37 +MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ITS_ME,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,# 38-3f +MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ITS_ME,MachineState.ERROR,MachineState.START,MachineState.START,# 40-47 +) + +ISO2022JP_CHAR_LEN_TABLE = (0, 0, 0, 0, 0, 0, 0, 0, 0, 0) + +ISO2022JP_SM_MODEL = {'class_table': ISO2022JP_CLS, + 'class_factor': 10, + 'state_table': ISO2022JP_ST, + 'char_len_table': ISO2022JP_CHAR_LEN_TABLE, + 'name': "ISO-2022-JP", + 'language': 'Japanese'} + +ISO2022KR_CLS = ( +2,0,0,0,0,0,0,0, # 00 - 07 +0,0,0,0,0,0,0,0, # 08 - 0f +0,0,0,0,0,0,0,0, # 10 - 17 +0,0,0,1,0,0,0,0, # 18 - 1f +0,0,0,0,3,0,0,0, # 20 - 27 +0,4,0,0,0,0,0,0, # 28 - 2f +0,0,0,0,0,0,0,0, # 30 - 37 +0,0,0,0,0,0,0,0, # 38 - 3f +0,0,0,5,0,0,0,0, # 40 - 47 +0,0,0,0,0,0,0,0, # 48 - 4f +0,0,0,0,0,0,0,0, # 50 - 57 +0,0,0,0,0,0,0,0, # 58 - 5f +0,0,0,0,0,0,0,0, # 60 - 67 +0,0,0,0,0,0,0,0, # 68 - 6f +0,0,0,0,0,0,0,0, # 70 - 77 +0,0,0,0,0,0,0,0, # 78 - 7f +2,2,2,2,2,2,2,2, # 80 - 87 +2,2,2,2,2,2,2,2, # 88 - 8f +2,2,2,2,2,2,2,2, # 90 - 97 +2,2,2,2,2,2,2,2, # 98 - 9f +2,2,2,2,2,2,2,2, # a0 - a7 +2,2,2,2,2,2,2,2, # a8 - af +2,2,2,2,2,2,2,2, # b0 - b7 +2,2,2,2,2,2,2,2, # b8 - bf +2,2,2,2,2,2,2,2, # c0 - c7 +2,2,2,2,2,2,2,2, # c8 - cf +2,2,2,2,2,2,2,2, # d0 - d7 +2,2,2,2,2,2,2,2, # d8 - df +2,2,2,2,2,2,2,2, # e0 - e7 +2,2,2,2,2,2,2,2, # e8 - ef +2,2,2,2,2,2,2,2, # f0 - f7 +2,2,2,2,2,2,2,2, # f8 - ff +) + +ISO2022KR_ST = ( +MachineState.START, 3,MachineState.ERROR,MachineState.START,MachineState.START,MachineState.START,MachineState.ERROR,MachineState.ERROR,# 00-07 +MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ITS_ME,MachineState.ITS_ME,MachineState.ITS_ME,MachineState.ITS_ME,# 08-0f +MachineState.ITS_ME,MachineState.ITS_ME,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR, 4,MachineState.ERROR,MachineState.ERROR,# 10-17 +MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR, 5,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,# 18-1f +MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ITS_ME,MachineState.START,MachineState.START,MachineState.START,MachineState.START,# 20-27 +) + +ISO2022KR_CHAR_LEN_TABLE = (0, 0, 0, 0, 0, 0) + +ISO2022KR_SM_MODEL = {'class_table': ISO2022KR_CLS, + 'class_factor': 6, + 'state_table': ISO2022KR_ST, + 'char_len_table': ISO2022KR_CHAR_LEN_TABLE, + 'name': "ISO-2022-KR", + 'language': 'Korean'} + + diff --git a/venv2/lib/python2.7/site-packages/pip/_vendor/chardet/eucjpprober.py b/venv2/lib/python2.7/site-packages/pip/_vendor/chardet/eucjpprober.py new file mode 100644 index 0000000..20ce8f7 --- /dev/null +++ b/venv2/lib/python2.7/site-packages/pip/_vendor/chardet/eucjpprober.py @@ -0,0 +1,92 @@ +######################## BEGIN LICENSE BLOCK ######################## +# The Original Code is mozilla.org code. +# +# The Initial Developer of the Original Code is +# Netscape Communications Corporation. +# Portions created by the Initial Developer are Copyright (C) 1998 +# the Initial Developer. All Rights Reserved. +# +# Contributor(s): +# Mark Pilgrim - port to Python +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA +# 02110-1301 USA +######################### END LICENSE BLOCK ######################### + +from .enums import ProbingState, MachineState +from .mbcharsetprober import MultiByteCharSetProber +from .codingstatemachine import CodingStateMachine +from .chardistribution import EUCJPDistributionAnalysis +from .jpcntx import EUCJPContextAnalysis +from .mbcssm import EUCJP_SM_MODEL + + +class EUCJPProber(MultiByteCharSetProber): + def __init__(self): + super(EUCJPProber, self).__init__() + self.coding_sm = CodingStateMachine(EUCJP_SM_MODEL) + self.distribution_analyzer = EUCJPDistributionAnalysis() + self.context_analyzer = EUCJPContextAnalysis() + self.reset() + + def reset(self): + super(EUCJPProber, self).reset() + self.context_analyzer.reset() + + @property + def charset_name(self): + return "EUC-JP" + + @property + def language(self): + return "Japanese" + + def feed(self, byte_str): + for i in range(len(byte_str)): + # PY3K: byte_str is a byte array, so byte_str[i] is an int, not a byte + coding_state = self.coding_sm.next_state(byte_str[i]) + if coding_state == MachineState.ERROR: + self.logger.debug('%s %s prober hit error at byte %s', + self.charset_name, self.language, i) + self._state = ProbingState.NOT_ME + break + elif coding_state == MachineState.ITS_ME: + self._state = ProbingState.FOUND_IT + break + elif coding_state == MachineState.START: + char_len = self.coding_sm.get_current_charlen() + if i == 0: + self._last_char[1] = byte_str[0] + self.context_analyzer.feed(self._last_char, char_len) + self.distribution_analyzer.feed(self._last_char, char_len) + else: + self.context_analyzer.feed(byte_str[i - 1:i + 1], + char_len) + self.distribution_analyzer.feed(byte_str[i - 1:i + 1], + char_len) + + self._last_char[0] = byte_str[-1] + + if self.state == ProbingState.DETECTING: + if (self.context_analyzer.got_enough_data() and + (self.get_confidence() > self.SHORTCUT_THRESHOLD)): + self._state = ProbingState.FOUND_IT + + return self.state + + def get_confidence(self): + context_conf = self.context_analyzer.get_confidence() + distrib_conf = self.distribution_analyzer.get_confidence() + return max(context_conf, distrib_conf) diff --git a/venv2/lib/python2.7/site-packages/pip/_vendor/chardet/euckrfreq.py b/venv2/lib/python2.7/site-packages/pip/_vendor/chardet/euckrfreq.py new file mode 100644 index 0000000..b68078c --- /dev/null +++ b/venv2/lib/python2.7/site-packages/pip/_vendor/chardet/euckrfreq.py @@ -0,0 +1,195 @@ +######################## BEGIN LICENSE BLOCK ######################## +# The Original Code is Mozilla Communicator client code. +# +# The Initial Developer of the Original Code is +# Netscape Communications Corporation. +# Portions created by the Initial Developer are Copyright (C) 1998 +# the Initial Developer. All Rights Reserved. +# +# Contributor(s): +# Mark Pilgrim - port to Python +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA +# 02110-1301 USA +######################### END LICENSE BLOCK ######################### + +# Sampling from about 20M text materials include literature and computer technology + +# 128 --> 0.79 +# 256 --> 0.92 +# 512 --> 0.986 +# 1024 --> 0.99944 +# 2048 --> 0.99999 +# +# Idea Distribution Ratio = 0.98653 / (1-0.98653) = 73.24 +# Random Distribution Ration = 512 / (2350-512) = 0.279. +# +# Typical Distribution Ratio + +EUCKR_TYPICAL_DISTRIBUTION_RATIO = 6.0 + +EUCKR_TABLE_SIZE = 2352 + +# Char to FreqOrder table , +EUCKR_CHAR_TO_FREQ_ORDER = ( + 13, 130, 120,1396, 481,1719,1720, 328, 609, 212,1721, 707, 400, 299,1722, 87, +1397,1723, 104, 536,1117,1203,1724,1267, 685,1268, 508,1725,1726,1727,1728,1398, +1399,1729,1730,1731, 141, 621, 326,1057, 368,1732, 267, 488, 20,1733,1269,1734, + 945,1400,1735, 47, 904,1270,1736,1737, 773, 248,1738, 409, 313, 786, 429,1739, + 116, 987, 813,1401, 683, 75,1204, 145,1740,1741,1742,1743, 16, 847, 667, 622, + 708,1744,1745,1746, 966, 787, 304, 129,1747, 60, 820, 123, 676,1748,1749,1750, +1751, 617,1752, 626,1753,1754,1755,1756, 653,1757,1758,1759,1760,1761,1762, 856, + 344,1763,1764,1765,1766, 89, 401, 418, 806, 905, 848,1767,1768,1769, 946,1205, + 709,1770,1118,1771, 241,1772,1773,1774,1271,1775, 569,1776, 999,1777,1778,1779, +1780, 337, 751,1058, 28, 628, 254,1781, 177, 906, 270, 349, 891,1079,1782, 19, +1783, 379,1784, 315,1785, 629, 754,1402, 559,1786, 636, 203,1206,1787, 710, 567, +1788, 935, 814,1789,1790,1207, 766, 528,1791,1792,1208,1793,1794,1795,1796,1797, +1403,1798,1799, 533,1059,1404,1405,1156,1406, 936, 884,1080,1800, 351,1801,1802, +1803,1804,1805, 801,1806,1807,1808,1119,1809,1157, 714, 474,1407,1810, 298, 899, + 885,1811,1120, 802,1158,1812, 892,1813,1814,1408, 659,1815,1816,1121,1817,1818, +1819,1820,1821,1822, 319,1823, 594, 545,1824, 815, 937,1209,1825,1826, 573,1409, +1022,1827,1210,1828,1829,1830,1831,1832,1833, 556, 722, 807,1122,1060,1834, 697, +1835, 900, 557, 715,1836,1410, 540,1411, 752,1159, 294, 597,1211, 976, 803, 770, +1412,1837,1838, 39, 794,1413, 358,1839, 371, 925,1840, 453, 661, 788, 531, 723, + 544,1023,1081, 869, 91,1841, 392, 430, 790, 602,1414, 677,1082, 457,1415,1416, +1842,1843, 475, 327,1024,1417, 795, 121,1844, 733, 403,1418,1845,1846,1847, 300, + 119, 711,1212, 627,1848,1272, 207,1849,1850, 796,1213, 382,1851, 519,1852,1083, + 893,1853,1854,1855, 367, 809, 487, 671,1856, 663,1857,1858, 956, 471, 306, 857, +1859,1860,1160,1084,1861,1862,1863,1864,1865,1061,1866,1867,1868,1869,1870,1871, + 282, 96, 574,1872, 502,1085,1873,1214,1874, 907,1875,1876, 827, 977,1419,1420, +1421, 268,1877,1422,1878,1879,1880, 308,1881, 2, 537,1882,1883,1215,1884,1885, + 127, 791,1886,1273,1423,1887, 34, 336, 404, 643,1888, 571, 654, 894, 840,1889, + 0, 886,1274, 122, 575, 260, 908, 938,1890,1275, 410, 316,1891,1892, 100,1893, +1894,1123, 48,1161,1124,1025,1895, 633, 901,1276,1896,1897, 115, 816,1898, 317, +1899, 694,1900, 909, 734,1424, 572, 866,1425, 691, 85, 524,1010, 543, 394, 841, +1901,1902,1903,1026,1904,1905,1906,1907,1908,1909, 30, 451, 651, 988, 310,1910, +1911,1426, 810,1216, 93,1912,1913,1277,1217,1914, 858, 759, 45, 58, 181, 610, + 269,1915,1916, 131,1062, 551, 443,1000, 821,1427, 957, 895,1086,1917,1918, 375, +1919, 359,1920, 687,1921, 822,1922, 293,1923,1924, 40, 662, 118, 692, 29, 939, + 887, 640, 482, 174,1925, 69,1162, 728,1428, 910,1926,1278,1218,1279, 386, 870, + 217, 854,1163, 823,1927,1928,1929,1930, 834,1931, 78,1932, 859,1933,1063,1934, +1935,1936,1937, 438,1164, 208, 595,1938,1939,1940,1941,1219,1125,1942, 280, 888, +1429,1430,1220,1431,1943,1944,1945,1946,1947,1280, 150, 510,1432,1948,1949,1950, +1951,1952,1953,1954,1011,1087,1955,1433,1043,1956, 881,1957, 614, 958,1064,1065, +1221,1958, 638,1001, 860, 967, 896,1434, 989, 492, 553,1281,1165,1959,1282,1002, +1283,1222,1960,1961,1962,1963, 36, 383, 228, 753, 247, 454,1964, 876, 678,1965, +1966,1284, 126, 464, 490, 835, 136, 672, 529, 940,1088,1435, 473,1967,1968, 467, + 50, 390, 227, 587, 279, 378, 598, 792, 968, 240, 151, 160, 849, 882,1126,1285, + 639,1044, 133, 140, 288, 360, 811, 563,1027, 561, 142, 523,1969,1970,1971, 7, + 103, 296, 439, 407, 506, 634, 990,1972,1973,1974,1975, 645,1976,1977,1978,1979, +1980,1981, 236,1982,1436,1983,1984,1089, 192, 828, 618, 518,1166, 333,1127,1985, + 818,1223,1986,1987,1988,1989,1990,1991,1992,1993, 342,1128,1286, 746, 842,1994, +1995, 560, 223,1287, 98, 8, 189, 650, 978,1288,1996,1437,1997, 17, 345, 250, + 423, 277, 234, 512, 226, 97, 289, 42, 167,1998, 201,1999,2000, 843, 836, 824, + 532, 338, 783,1090, 182, 576, 436,1438,1439, 527, 500,2001, 947, 889,2002,2003, +2004,2005, 262, 600, 314, 447,2006, 547,2007, 693, 738,1129,2008, 71,1440, 745, + 619, 688,2009, 829,2010,2011, 147,2012, 33, 948,2013,2014, 74, 224,2015, 61, + 191, 918, 399, 637,2016,1028,1130, 257, 902,2017,2018,2019,2020,2021,2022,2023, +2024,2025,2026, 837,2027,2028,2029,2030, 179, 874, 591, 52, 724, 246,2031,2032, +2033,2034,1167, 969,2035,1289, 630, 605, 911,1091,1168,2036,2037,2038,1441, 912, +2039, 623,2040,2041, 253,1169,1290,2042,1442, 146, 620, 611, 577, 433,2043,1224, + 719,1170, 959, 440, 437, 534, 84, 388, 480,1131, 159, 220, 198, 679,2044,1012, + 819,1066,1443, 113,1225, 194, 318,1003,1029,2045,2046,2047,2048,1067,2049,2050, +2051,2052,2053, 59, 913, 112,2054, 632,2055, 455, 144, 739,1291,2056, 273, 681, + 499,2057, 448,2058,2059, 760,2060,2061, 970, 384, 169, 245,1132,2062,2063, 414, +1444,2064,2065, 41, 235,2066, 157, 252, 877, 568, 919, 789, 580,2067, 725,2068, +2069,1292,2070,2071,1445,2072,1446,2073,2074, 55, 588, 66,1447, 271,1092,2075, +1226,2076, 960,1013, 372,2077,2078,2079,2080,2081,1293,2082,2083,2084,2085, 850, +2086,2087,2088,2089,2090, 186,2091,1068, 180,2092,2093,2094, 109,1227, 522, 606, +2095, 867,1448,1093, 991,1171, 926, 353,1133,2096, 581,2097,2098,2099,1294,1449, +1450,2100, 596,1172,1014,1228,2101,1451,1295,1173,1229,2102,2103,1296,1134,1452, + 949,1135,2104,2105,1094,1453,1454,1455,2106,1095,2107,2108,2109,2110,2111,2112, +2113,2114,2115,2116,2117, 804,2118,2119,1230,1231, 805,1456, 405,1136,2120,2121, +2122,2123,2124, 720, 701,1297, 992,1457, 927,1004,2125,2126,2127,2128,2129,2130, + 22, 417,2131, 303,2132, 385,2133, 971, 520, 513,2134,1174, 73,1096, 231, 274, + 962,1458, 673,2135,1459,2136, 152,1137,2137,2138,2139,2140,1005,1138,1460,1139, +2141,2142,2143,2144, 11, 374, 844,2145, 154,1232, 46,1461,2146, 838, 830, 721, +1233, 106,2147, 90, 428, 462, 578, 566,1175, 352,2148,2149, 538,1234, 124,1298, +2150,1462, 761, 565,2151, 686,2152, 649,2153, 72, 173,2154, 460, 415,2155,1463, +2156,1235, 305,2157,2158,2159,2160,2161,2162, 579,2163,2164,2165,2166,2167, 747, +2168,2169,2170,2171,1464, 669,2172,2173,2174,2175,2176,1465,2177, 23, 530, 285, +2178, 335, 729,2179, 397,2180,2181,2182,1030,2183,2184, 698,2185,2186, 325,2187, +2188, 369,2189, 799,1097,1015, 348,2190,1069, 680,2191, 851,1466,2192,2193, 10, +2194, 613, 424,2195, 979, 108, 449, 589, 27, 172, 81,1031, 80, 774, 281, 350, +1032, 525, 301, 582,1176,2196, 674,1045,2197,2198,1467, 730, 762,2199,2200,2201, +2202,1468,2203, 993,2204,2205, 266,1070, 963,1140,2206,2207,2208, 664,1098, 972, +2209,2210,2211,1177,1469,1470, 871,2212,2213,2214,2215,2216,1471,2217,2218,2219, +2220,2221,2222,2223,2224,2225,2226,2227,1472,1236,2228,2229,2230,2231,2232,2233, +2234,2235,1299,2236,2237, 200,2238, 477, 373,2239,2240, 731, 825, 777,2241,2242, +2243, 521, 486, 548,2244,2245,2246,1473,1300, 53, 549, 137, 875, 76, 158,2247, +1301,1474, 469, 396,1016, 278, 712,2248, 321, 442, 503, 767, 744, 941,1237,1178, +1475,2249, 82, 178,1141,1179, 973,2250,1302,2251, 297,2252,2253, 570,2254,2255, +2256, 18, 450, 206,2257, 290, 292,1142,2258, 511, 162, 99, 346, 164, 735,2259, +1476,1477, 4, 554, 343, 798,1099,2260,1100,2261, 43, 171,1303, 139, 215,2262, +2263, 717, 775,2264,1033, 322, 216,2265, 831,2266, 149,2267,1304,2268,2269, 702, +1238, 135, 845, 347, 309,2270, 484,2271, 878, 655, 238,1006,1478,2272, 67,2273, + 295,2274,2275, 461,2276, 478, 942, 412,2277,1034,2278,2279,2280, 265,2281, 541, +2282,2283,2284,2285,2286, 70, 852,1071,2287,2288,2289,2290, 21, 56, 509, 117, + 432,2291,2292, 331, 980, 552,1101, 148, 284, 105, 393,1180,1239, 755,2293, 187, +2294,1046,1479,2295, 340,2296, 63,1047, 230,2297,2298,1305, 763,1306, 101, 800, + 808, 494,2299,2300,2301, 903,2302, 37,1072, 14, 5,2303, 79, 675,2304, 312, +2305,2306,2307,2308,2309,1480, 6,1307,2310,2311,2312, 1, 470, 35, 24, 229, +2313, 695, 210, 86, 778, 15, 784, 592, 779, 32, 77, 855, 964,2314, 259,2315, + 501, 380,2316,2317, 83, 981, 153, 689,1308,1481,1482,1483,2318,2319, 716,1484, +2320,2321,2322,2323,2324,2325,1485,2326,2327, 128, 57, 68, 261,1048, 211, 170, +1240, 31,2328, 51, 435, 742,2329,2330,2331, 635,2332, 264, 456,2333,2334,2335, + 425,2336,1486, 143, 507, 263, 943,2337, 363, 920,1487, 256,1488,1102, 243, 601, +1489,2338,2339,2340,2341,2342,2343,2344, 861,2345,2346,2347,2348,2349,2350, 395, +2351,1490,1491, 62, 535, 166, 225,2352,2353, 668, 419,1241, 138, 604, 928,2354, +1181,2355,1492,1493,2356,2357,2358,1143,2359, 696,2360, 387, 307,1309, 682, 476, +2361,2362, 332, 12, 222, 156,2363, 232,2364, 641, 276, 656, 517,1494,1495,1035, + 416, 736,1496,2365,1017, 586,2366,2367,2368,1497,2369, 242,2370,2371,2372,1498, +2373, 965, 713,2374,2375,2376,2377, 740, 982,1499, 944,1500,1007,2378,2379,1310, +1501,2380,2381,2382, 785, 329,2383,2384,1502,2385,2386,2387, 932,2388,1503,2389, +2390,2391,2392,1242,2393,2394,2395,2396,2397, 994, 950,2398,2399,2400,2401,1504, +1311,2402,2403,2404,2405,1049, 749,2406,2407, 853, 718,1144,1312,2408,1182,1505, +2409,2410, 255, 516, 479, 564, 550, 214,1506,1507,1313, 413, 239, 444, 339,1145, +1036,1508,1509,1314,1037,1510,1315,2411,1511,2412,2413,2414, 176, 703, 497, 624, + 593, 921, 302,2415, 341, 165,1103,1512,2416,1513,2417,2418,2419, 376,2420, 700, +2421,2422,2423, 258, 768,1316,2424,1183,2425, 995, 608,2426,2427,2428,2429, 221, +2430,2431,2432,2433,2434,2435,2436,2437, 195, 323, 726, 188, 897, 983,1317, 377, + 644,1050, 879,2438, 452,2439,2440,2441,2442,2443,2444, 914,2445,2446,2447,2448, + 915, 489,2449,1514,1184,2450,2451, 515, 64, 427, 495,2452, 583,2453, 483, 485, +1038, 562, 213,1515, 748, 666,2454,2455,2456,2457, 334,2458, 780, 996,1008, 705, +1243,2459,2460,2461,2462,2463, 114,2464, 493,1146, 366, 163,1516, 961,1104,2465, + 291,2466,1318,1105,2467,1517, 365,2468, 355, 951,1244,2469,1319,2470, 631,2471, +2472, 218,1320, 364, 320, 756,1518,1519,1321,1520,1322,2473,2474,2475,2476, 997, +2477,2478,2479,2480, 665,1185,2481, 916,1521,2482,2483,2484, 584, 684,2485,2486, + 797,2487,1051,1186,2488,2489,2490,1522,2491,2492, 370,2493,1039,1187, 65,2494, + 434, 205, 463,1188,2495, 125, 812, 391, 402, 826, 699, 286, 398, 155, 781, 771, + 585,2496, 590, 505,1073,2497, 599, 244, 219, 917,1018, 952, 646,1523,2498,1323, +2499,2500, 49, 984, 354, 741,2501, 625,2502,1324,2503,1019, 190, 357, 757, 491, + 95, 782, 868,2504,2505,2506,2507,2508,2509, 134,1524,1074, 422,1525, 898,2510, + 161,2511,2512,2513,2514, 769,2515,1526,2516,2517, 411,1325,2518, 472,1527,2519, +2520,2521,2522,2523,2524, 985,2525,2526,2527,2528,2529,2530, 764,2531,1245,2532, +2533, 25, 204, 311,2534, 496,2535,1052,2536,2537,2538,2539,2540,2541,2542, 199, + 704, 504, 468, 758, 657,1528, 196, 44, 839,1246, 272, 750,2543, 765, 862,2544, +2545,1326,2546, 132, 615, 933,2547, 732,2548,2549,2550,1189,1529,2551, 283,1247, +1053, 607, 929,2552,2553,2554, 930, 183, 872, 616,1040,1147,2555,1148,1020, 441, + 249,1075,2556,2557,2558, 466, 743,2559,2560,2561, 92, 514, 426, 420, 526,2562, +2563,2564,2565,2566,2567,2568, 185,2569,2570,2571,2572, 776,1530, 658,2573, 362, +2574, 361, 922,1076, 793,2575,2576,2577,2578,2579,2580,1531, 251,2581,2582,2583, +2584,1532, 54, 612, 237,1327,2585,2586, 275, 408, 647, 111,2587,1533,1106, 465, + 3, 458, 9, 38,2588, 107, 110, 890, 209, 26, 737, 498,2589,1534,2590, 431, + 202, 88,1535, 356, 287,1107, 660,1149,2591, 381,1536, 986,1150, 445,1248,1151, + 974,2592,2593, 846,2594, 446, 953, 184,1249,1250, 727,2595, 923, 193, 883,2596, +2597,2598, 102, 324, 539, 817,2599, 421,1041,2600, 832,2601, 94, 175, 197, 406, +2602, 459,2603,2604,2605,2606,2607, 330, 555,2608,2609,2610, 706,1108, 389,2611, +2612,2613,2614, 233,2615, 833, 558, 931, 954,1251,2616,2617,1537, 546,2618,2619, +1009,2620,2621,2622,1538, 690,1328,2623, 955,2624,1539,2625,2626, 772,2627,2628, +2629,2630,2631, 924, 648, 863, 603,2632,2633, 934,1540, 864, 865,2634, 642,1042, + 670,1190,2635,2636,2637,2638, 168,2639, 652, 873, 542,1054,1541,2640,2641,2642, # 512, 256 +) + diff --git a/venv2/lib/python2.7/site-packages/pip/_vendor/chardet/euckrprober.py b/venv2/lib/python2.7/site-packages/pip/_vendor/chardet/euckrprober.py new file mode 100644 index 0000000..345a060 --- /dev/null +++ b/venv2/lib/python2.7/site-packages/pip/_vendor/chardet/euckrprober.py @@ -0,0 +1,47 @@ +######################## BEGIN LICENSE BLOCK ######################## +# The Original Code is mozilla.org code. +# +# The Initial Developer of the Original Code is +# Netscape Communications Corporation. +# Portions created by the Initial Developer are Copyright (C) 1998 +# the Initial Developer. All Rights Reserved. +# +# Contributor(s): +# Mark Pilgrim - port to Python +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA +# 02110-1301 USA +######################### END LICENSE BLOCK ######################### + +from .mbcharsetprober import MultiByteCharSetProber +from .codingstatemachine import CodingStateMachine +from .chardistribution import EUCKRDistributionAnalysis +from .mbcssm import EUCKR_SM_MODEL + + +class EUCKRProber(MultiByteCharSetProber): + def __init__(self): + super(EUCKRProber, self).__init__() + self.coding_sm = CodingStateMachine(EUCKR_SM_MODEL) + self.distribution_analyzer = EUCKRDistributionAnalysis() + self.reset() + + @property + def charset_name(self): + return "EUC-KR" + + @property + def language(self): + return "Korean" diff --git a/venv2/lib/python2.7/site-packages/pip/_vendor/chardet/euctwfreq.py b/venv2/lib/python2.7/site-packages/pip/_vendor/chardet/euctwfreq.py new file mode 100644 index 0000000..ed7a995 --- /dev/null +++ b/venv2/lib/python2.7/site-packages/pip/_vendor/chardet/euctwfreq.py @@ -0,0 +1,387 @@ +######################## BEGIN LICENSE BLOCK ######################## +# The Original Code is Mozilla Communicator client code. +# +# The Initial Developer of the Original Code is +# Netscape Communications Corporation. +# Portions created by the Initial Developer are Copyright (C) 1998 +# the Initial Developer. All Rights Reserved. +# +# Contributor(s): +# Mark Pilgrim - port to Python +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA +# 02110-1301 USA +######################### END LICENSE BLOCK ######################### + +# EUCTW frequency table +# Converted from big5 work +# by Taiwan's Mandarin Promotion Council +# <http:#www.edu.tw:81/mandr/> + +# 128 --> 0.42261 +# 256 --> 0.57851 +# 512 --> 0.74851 +# 1024 --> 0.89384 +# 2048 --> 0.97583 +# +# Idea Distribution Ratio = 0.74851/(1-0.74851) =2.98 +# Random Distribution Ration = 512/(5401-512)=0.105 +# +# Typical Distribution Ratio about 25% of Ideal one, still much higher than RDR + +EUCTW_TYPICAL_DISTRIBUTION_RATIO = 0.75 + +# Char to FreqOrder table , +EUCTW_TABLE_SIZE = 5376 + +EUCTW_CHAR_TO_FREQ_ORDER = ( + 1,1800,1506, 255,1431, 198, 9, 82, 6,7310, 177, 202,3615,1256,2808, 110, # 2742 +3735, 33,3241, 261, 76, 44,2113, 16,2931,2184,1176, 659,3868, 26,3404,2643, # 2758 +1198,3869,3313,4060, 410,2211, 302, 590, 361,1963, 8, 204, 58,4296,7311,1931, # 2774 + 63,7312,7313, 317,1614, 75, 222, 159,4061,2412,1480,7314,3500,3068, 224,2809, # 2790 +3616, 3, 10,3870,1471, 29,2774,1135,2852,1939, 873, 130,3242,1123, 312,7315, # 2806 +4297,2051, 507, 252, 682,7316, 142,1914, 124, 206,2932, 34,3501,3173, 64, 604, # 2822 +7317,2494,1976,1977, 155,1990, 645, 641,1606,7318,3405, 337, 72, 406,7319, 80, # 2838 + 630, 238,3174,1509, 263, 939,1092,2644, 756,1440,1094,3406, 449, 69,2969, 591, # 2854 + 179,2095, 471, 115,2034,1843, 60, 50,2970, 134, 806,1868, 734,2035,3407, 180, # 2870 + 995,1607, 156, 537,2893, 688,7320, 319,1305, 779,2144, 514,2374, 298,4298, 359, # 2886 +2495, 90,2707,1338, 663, 11, 906,1099,2545, 20,2436, 182, 532,1716,7321, 732, # 2902 +1376,4062,1311,1420,3175, 25,2312,1056, 113, 399, 382,1949, 242,3408,2467, 529, # 2918 +3243, 475,1447,3617,7322, 117, 21, 656, 810,1297,2295,2329,3502,7323, 126,4063, # 2934 + 706, 456, 150, 613,4299, 71,1118,2036,4064, 145,3069, 85, 835, 486,2114,1246, # 2950 +1426, 428, 727,1285,1015, 800, 106, 623, 303,1281,7324,2127,2354, 347,3736, 221, # 2966 +3503,3110,7325,1955,1153,4065, 83, 296,1199,3070, 192, 624, 93,7326, 822,1897, # 2982 +2810,3111, 795,2064, 991,1554,1542,1592, 27, 43,2853, 859, 139,1456, 860,4300, # 2998 + 437, 712,3871, 164,2392,3112, 695, 211,3017,2096, 195,3872,1608,3504,3505,3618, # 3014 +3873, 234, 811,2971,2097,3874,2229,1441,3506,1615,2375, 668,2076,1638, 305, 228, # 3030 +1664,4301, 467, 415,7327, 262,2098,1593, 239, 108, 300, 200,1033, 512,1247,2077, # 3046 +7328,7329,2173,3176,3619,2673, 593, 845,1062,3244, 88,1723,2037,3875,1950, 212, # 3062 + 266, 152, 149, 468,1898,4066,4302, 77, 187,7330,3018, 37, 5,2972,7331,3876, # 3078 +7332,7333, 39,2517,4303,2894,3177,2078, 55, 148, 74,4304, 545, 483,1474,1029, # 3094 +1665, 217,1869,1531,3113,1104,2645,4067, 24, 172,3507, 900,3877,3508,3509,4305, # 3110 + 32,1408,2811,1312, 329, 487,2355,2247,2708, 784,2674, 4,3019,3314,1427,1788, # 3126 + 188, 109, 499,7334,3620,1717,1789, 888,1217,3020,4306,7335,3510,7336,3315,1520, # 3142 +3621,3878, 196,1034, 775,7337,7338, 929,1815, 249, 439, 38,7339,1063,7340, 794, # 3158 +3879,1435,2296, 46, 178,3245,2065,7341,2376,7342, 214,1709,4307, 804, 35, 707, # 3174 + 324,3622,1601,2546, 140, 459,4068,7343,7344,1365, 839, 272, 978,2257,2572,3409, # 3190 +2128,1363,3623,1423, 697, 100,3071, 48, 70,1231, 495,3114,2193,7345,1294,7346, # 3206 +2079, 462, 586,1042,3246, 853, 256, 988, 185,2377,3410,1698, 434,1084,7347,3411, # 3222 + 314,2615,2775,4308,2330,2331, 569,2280, 637,1816,2518, 757,1162,1878,1616,3412, # 3238 + 287,1577,2115, 768,4309,1671,2854,3511,2519,1321,3737, 909,2413,7348,4069, 933, # 3254 +3738,7349,2052,2356,1222,4310, 765,2414,1322, 786,4311,7350,1919,1462,1677,2895, # 3270 +1699,7351,4312,1424,2437,3115,3624,2590,3316,1774,1940,3413,3880,4070, 309,1369, # 3286 +1130,2812, 364,2230,1653,1299,3881,3512,3882,3883,2646, 525,1085,3021, 902,2000, # 3302 +1475, 964,4313, 421,1844,1415,1057,2281, 940,1364,3116, 376,4314,4315,1381, 7, # 3318 +2520, 983,2378, 336,1710,2675,1845, 321,3414, 559,1131,3022,2742,1808,1132,1313, # 3334 + 265,1481,1857,7352, 352,1203,2813,3247, 167,1089, 420,2814, 776, 792,1724,3513, # 3350 +4071,2438,3248,7353,4072,7354, 446, 229, 333,2743, 901,3739,1200,1557,4316,2647, # 3366 +1920, 395,2744,2676,3740,4073,1835, 125, 916,3178,2616,4317,7355,7356,3741,7357, # 3382 +7358,7359,4318,3117,3625,1133,2547,1757,3415,1510,2313,1409,3514,7360,2145, 438, # 3398 +2591,2896,2379,3317,1068, 958,3023, 461, 311,2855,2677,4074,1915,3179,4075,1978, # 3414 + 383, 750,2745,2617,4076, 274, 539, 385,1278,1442,7361,1154,1964, 384, 561, 210, # 3430 + 98,1295,2548,3515,7362,1711,2415,1482,3416,3884,2897,1257, 129,7363,3742, 642, # 3446 + 523,2776,2777,2648,7364, 141,2231,1333, 68, 176, 441, 876, 907,4077, 603,2592, # 3462 + 710, 171,3417, 404, 549, 18,3118,2393,1410,3626,1666,7365,3516,4319,2898,4320, # 3478 +7366,2973, 368,7367, 146, 366, 99, 871,3627,1543, 748, 807,1586,1185, 22,2258, # 3494 + 379,3743,3180,7368,3181, 505,1941,2618,1991,1382,2314,7369, 380,2357, 218, 702, # 3510 +1817,1248,3418,3024,3517,3318,3249,7370,2974,3628, 930,3250,3744,7371, 59,7372, # 3526 + 585, 601,4078, 497,3419,1112,1314,4321,1801,7373,1223,1472,2174,7374, 749,1836, # 3542 + 690,1899,3745,1772,3885,1476, 429,1043,1790,2232,2116, 917,4079, 447,1086,1629, # 3558 +7375, 556,7376,7377,2020,1654, 844,1090, 105, 550, 966,1758,2815,1008,1782, 686, # 3574 +1095,7378,2282, 793,1602,7379,3518,2593,4322,4080,2933,2297,4323,3746, 980,2496, # 3590 + 544, 353, 527,4324, 908,2678,2899,7380, 381,2619,1942,1348,7381,1341,1252, 560, # 3606 +3072,7382,3420,2856,7383,2053, 973, 886,2080, 143,4325,7384,7385, 157,3886, 496, # 3622 +4081, 57, 840, 540,2038,4326,4327,3421,2117,1445, 970,2259,1748,1965,2081,4082, # 3638 +3119,1234,1775,3251,2816,3629, 773,1206,2129,1066,2039,1326,3887,1738,1725,4083, # 3654 + 279,3120, 51,1544,2594, 423,1578,2130,2066, 173,4328,1879,7386,7387,1583, 264, # 3670 + 610,3630,4329,2439, 280, 154,7388,7389,7390,1739, 338,1282,3073, 693,2857,1411, # 3686 +1074,3747,2440,7391,4330,7392,7393,1240, 952,2394,7394,2900,1538,2679, 685,1483, # 3702 +4084,2468,1436, 953,4085,2054,4331, 671,2395, 79,4086,2441,3252, 608, 567,2680, # 3718 +3422,4087,4088,1691, 393,1261,1791,2396,7395,4332,7396,7397,7398,7399,1383,1672, # 3734 +3748,3182,1464, 522,1119, 661,1150, 216, 675,4333,3888,1432,3519, 609,4334,2681, # 3750 +2397,7400,7401,7402,4089,3025, 0,7403,2469, 315, 231,2442, 301,3319,4335,2380, # 3766 +7404, 233,4090,3631,1818,4336,4337,7405, 96,1776,1315,2082,7406, 257,7407,1809, # 3782 +3632,2709,1139,1819,4091,2021,1124,2163,2778,1777,2649,7408,3074, 363,1655,3183, # 3798 +7409,2975,7410,7411,7412,3889,1567,3890, 718, 103,3184, 849,1443, 341,3320,2934, # 3814 +1484,7413,1712, 127, 67, 339,4092,2398, 679,1412, 821,7414,7415, 834, 738, 351, # 3830 +2976,2146, 846, 235,1497,1880, 418,1992,3749,2710, 186,1100,2147,2746,3520,1545, # 3846 +1355,2935,2858,1377, 583,3891,4093,2573,2977,7416,1298,3633,1078,2549,3634,2358, # 3862 + 78,3750,3751, 267,1289,2099,2001,1594,4094, 348, 369,1274,2194,2175,1837,4338, # 3878 +1820,2817,3635,2747,2283,2002,4339,2936,2748, 144,3321, 882,4340,3892,2749,3423, # 3894 +4341,2901,7417,4095,1726, 320,7418,3893,3026, 788,2978,7419,2818,1773,1327,2859, # 3910 +3894,2819,7420,1306,4342,2003,1700,3752,3521,2359,2650, 787,2022, 506, 824,3636, # 3926 + 534, 323,4343,1044,3322,2023,1900, 946,3424,7421,1778,1500,1678,7422,1881,4344, # 3942 + 165, 243,4345,3637,2521, 123, 683,4096, 764,4346, 36,3895,1792, 589,2902, 816, # 3958 + 626,1667,3027,2233,1639,1555,1622,3753,3896,7423,3897,2860,1370,1228,1932, 891, # 3974 +2083,2903, 304,4097,7424, 292,2979,2711,3522, 691,2100,4098,1115,4347, 118, 662, # 3990 +7425, 611,1156, 854,2381,1316,2861, 2, 386, 515,2904,7426,7427,3253, 868,2234, # 4006 +1486, 855,2651, 785,2212,3028,7428,1040,3185,3523,7429,3121, 448,7430,1525,7431, # 4022 +2164,4348,7432,3754,7433,4099,2820,3524,3122, 503, 818,3898,3123,1568, 814, 676, # 4038 +1444, 306,1749,7434,3755,1416,1030, 197,1428, 805,2821,1501,4349,7435,7436,7437, # 4054 +1993,7438,4350,7439,7440,2195, 13,2779,3638,2980,3124,1229,1916,7441,3756,2131, # 4070 +7442,4100,4351,2399,3525,7443,2213,1511,1727,1120,7444,7445, 646,3757,2443, 307, # 4086 +7446,7447,1595,3186,7448,7449,7450,3639,1113,1356,3899,1465,2522,2523,7451, 519, # 4102 +7452, 128,2132, 92,2284,1979,7453,3900,1512, 342,3125,2196,7454,2780,2214,1980, # 4118 +3323,7455, 290,1656,1317, 789, 827,2360,7456,3758,4352, 562, 581,3901,7457, 401, # 4134 +4353,2248, 94,4354,1399,2781,7458,1463,2024,4355,3187,1943,7459, 828,1105,4101, # 4150 +1262,1394,7460,4102, 605,4356,7461,1783,2862,7462,2822, 819,2101, 578,2197,2937, # 4166 +7463,1502, 436,3254,4103,3255,2823,3902,2905,3425,3426,7464,2712,2315,7465,7466, # 4182 +2332,2067, 23,4357, 193, 826,3759,2102, 699,1630,4104,3075, 390,1793,1064,3526, # 4198 +7467,1579,3076,3077,1400,7468,4105,1838,1640,2863,7469,4358,4359, 137,4106, 598, # 4214 +3078,1966, 780, 104, 974,2938,7470, 278, 899, 253, 402, 572, 504, 493,1339,7471, # 4230 +3903,1275,4360,2574,2550,7472,3640,3029,3079,2249, 565,1334,2713, 863, 41,7473, # 4246 +7474,4361,7475,1657,2333, 19, 463,2750,4107, 606,7476,2981,3256,1087,2084,1323, # 4262 +2652,2982,7477,1631,1623,1750,4108,2682,7478,2864, 791,2714,2653,2334, 232,2416, # 4278 +7479,2983,1498,7480,2654,2620, 755,1366,3641,3257,3126,2025,1609, 119,1917,3427, # 4294 + 862,1026,4109,7481,3904,3760,4362,3905,4363,2260,1951,2470,7482,1125, 817,4110, # 4310 +4111,3906,1513,1766,2040,1487,4112,3030,3258,2824,3761,3127,7483,7484,1507,7485, # 4326 +2683, 733, 40,1632,1106,2865, 345,4113, 841,2524, 230,4364,2984,1846,3259,3428, # 4342 +7486,1263, 986,3429,7487, 735, 879, 254,1137, 857, 622,1300,1180,1388,1562,3907, # 4358 +3908,2939, 967,2751,2655,1349, 592,2133,1692,3324,2985,1994,4114,1679,3909,1901, # 4374 +2185,7488, 739,3642,2715,1296,1290,7489,4115,2198,2199,1921,1563,2595,2551,1870, # 4390 +2752,2986,7490, 435,7491, 343,1108, 596, 17,1751,4365,2235,3430,3643,7492,4366, # 4406 + 294,3527,2940,1693, 477, 979, 281,2041,3528, 643,2042,3644,2621,2782,2261,1031, # 4422 +2335,2134,2298,3529,4367, 367,1249,2552,7493,3530,7494,4368,1283,3325,2004, 240, # 4438 +1762,3326,4369,4370, 836,1069,3128, 474,7495,2148,2525, 268,3531,7496,3188,1521, # 4454 +1284,7497,1658,1546,4116,7498,3532,3533,7499,4117,3327,2684,1685,4118, 961,1673, # 4470 +2622, 190,2005,2200,3762,4371,4372,7500, 570,2497,3645,1490,7501,4373,2623,3260, # 4486 +1956,4374, 584,1514, 396,1045,1944,7502,4375,1967,2444,7503,7504,4376,3910, 619, # 4502 +7505,3129,3261, 215,2006,2783,2553,3189,4377,3190,4378, 763,4119,3763,4379,7506, # 4518 +7507,1957,1767,2941,3328,3646,1174, 452,1477,4380,3329,3130,7508,2825,1253,2382, # 4534 +2186,1091,2285,4120, 492,7509, 638,1169,1824,2135,1752,3911, 648, 926,1021,1324, # 4550 +4381, 520,4382, 997, 847,1007, 892,4383,3764,2262,1871,3647,7510,2400,1784,4384, # 4566 +1952,2942,3080,3191,1728,4121,2043,3648,4385,2007,1701,3131,1551, 30,2263,4122, # 4582 +7511,2026,4386,3534,7512, 501,7513,4123, 594,3431,2165,1821,3535,3432,3536,3192, # 4598 + 829,2826,4124,7514,1680,3132,1225,4125,7515,3262,4387,4126,3133,2336,7516,4388, # 4614 +4127,7517,3912,3913,7518,1847,2383,2596,3330,7519,4389, 374,3914, 652,4128,4129, # 4630 + 375,1140, 798,7520,7521,7522,2361,4390,2264, 546,1659, 138,3031,2445,4391,7523, # 4646 +2250, 612,1848, 910, 796,3765,1740,1371, 825,3766,3767,7524,2906,2554,7525, 692, # 4662 + 444,3032,2624, 801,4392,4130,7526,1491, 244,1053,3033,4131,4132, 340,7527,3915, # 4678 +1041,2987, 293,1168, 87,1357,7528,1539, 959,7529,2236, 721, 694,4133,3768, 219, # 4694 +1478, 644,1417,3331,2656,1413,1401,1335,1389,3916,7530,7531,2988,2362,3134,1825, # 4710 + 730,1515, 184,2827, 66,4393,7532,1660,2943, 246,3332, 378,1457, 226,3433, 975, # 4726 +3917,2944,1264,3537, 674, 696,7533, 163,7534,1141,2417,2166, 713,3538,3333,4394, # 4742 +3918,7535,7536,1186, 15,7537,1079,1070,7538,1522,3193,3539, 276,1050,2716, 758, # 4758 +1126, 653,2945,3263,7539,2337, 889,3540,3919,3081,2989, 903,1250,4395,3920,3434, # 4774 +3541,1342,1681,1718, 766,3264, 286, 89,2946,3649,7540,1713,7541,2597,3334,2990, # 4790 +7542,2947,2215,3194,2866,7543,4396,2498,2526, 181, 387,1075,3921, 731,2187,3335, # 4806 +7544,3265, 310, 313,3435,2299, 770,4134, 54,3034, 189,4397,3082,3769,3922,7545, # 4822 +1230,1617,1849, 355,3542,4135,4398,3336, 111,4136,3650,1350,3135,3436,3035,4137, # 4838 +2149,3266,3543,7546,2784,3923,3924,2991, 722,2008,7547,1071, 247,1207,2338,2471, # 4854 +1378,4399,2009, 864,1437,1214,4400, 373,3770,1142,2216, 667,4401, 442,2753,2555, # 4870 +3771,3925,1968,4138,3267,1839, 837, 170,1107, 934,1336,1882,7548,7549,2118,4139, # 4886 +2828, 743,1569,7550,4402,4140, 582,2384,1418,3437,7551,1802,7552, 357,1395,1729, # 4902 +3651,3268,2418,1564,2237,7553,3083,3772,1633,4403,1114,2085,4141,1532,7554, 482, # 4918 +2446,4404,7555,7556,1492, 833,1466,7557,2717,3544,1641,2829,7558,1526,1272,3652, # 4934 +4142,1686,1794, 416,2556,1902,1953,1803,7559,3773,2785,3774,1159,2316,7560,2867, # 4950 +4405,1610,1584,3036,2419,2754, 443,3269,1163,3136,7561,7562,3926,7563,4143,2499, # 4966 +3037,4406,3927,3137,2103,1647,3545,2010,1872,4144,7564,4145, 431,3438,7565, 250, # 4982 + 97, 81,4146,7566,1648,1850,1558, 160, 848,7567, 866, 740,1694,7568,2201,2830, # 4998 +3195,4147,4407,3653,1687, 950,2472, 426, 469,3196,3654,3655,3928,7569,7570,1188, # 5014 + 424,1995, 861,3546,4148,3775,2202,2685, 168,1235,3547,4149,7571,2086,1674,4408, # 5030 +3337,3270, 220,2557,1009,7572,3776, 670,2992, 332,1208, 717,7573,7574,3548,2447, # 5046 +3929,3338,7575, 513,7576,1209,2868,3339,3138,4409,1080,7577,7578,7579,7580,2527, # 5062 +3656,3549, 815,1587,3930,3931,7581,3550,3439,3777,1254,4410,1328,3038,1390,3932, # 5078 +1741,3933,3778,3934,7582, 236,3779,2448,3271,7583,7584,3657,3780,1273,3781,4411, # 5094 +7585, 308,7586,4412, 245,4413,1851,2473,1307,2575, 430, 715,2136,2449,7587, 270, # 5110 + 199,2869,3935,7588,3551,2718,1753, 761,1754, 725,1661,1840,4414,3440,3658,7589, # 5126 +7590, 587, 14,3272, 227,2598, 326, 480,2265, 943,2755,3552, 291, 650,1883,7591, # 5142 +1702,1226, 102,1547, 62,3441, 904,4415,3442,1164,4150,7592,7593,1224,1548,2756, # 5158 + 391, 498,1493,7594,1386,1419,7595,2055,1177,4416, 813, 880,1081,2363, 566,1145, # 5174 +4417,2286,1001,1035,2558,2599,2238, 394,1286,7596,7597,2068,7598, 86,1494,1730, # 5190 +3936, 491,1588, 745, 897,2948, 843,3340,3937,2757,2870,3273,1768, 998,2217,2069, # 5206 + 397,1826,1195,1969,3659,2993,3341, 284,7599,3782,2500,2137,2119,1903,7600,3938, # 5222 +2150,3939,4151,1036,3443,1904, 114,2559,4152, 209,1527,7601,7602,2949,2831,2625, # 5238 +2385,2719,3139, 812,2560,7603,3274,7604,1559, 737,1884,3660,1210, 885, 28,2686, # 5254 +3553,3783,7605,4153,1004,1779,4418,7606, 346,1981,2218,2687,4419,3784,1742, 797, # 5270 +1642,3940,1933,1072,1384,2151, 896,3941,3275,3661,3197,2871,3554,7607,2561,1958, # 5286 +4420,2450,1785,7608,7609,7610,3942,4154,1005,1308,3662,4155,2720,4421,4422,1528, # 5302 +2600, 161,1178,4156,1982, 987,4423,1101,4157, 631,3943,1157,3198,2420,1343,1241, # 5318 +1016,2239,2562, 372, 877,2339,2501,1160, 555,1934, 911,3944,7611, 466,1170, 169, # 5334 +1051,2907,2688,3663,2474,2994,1182,2011,2563,1251,2626,7612, 992,2340,3444,1540, # 5350 +2721,1201,2070,2401,1996,2475,7613,4424, 528,1922,2188,1503,1873,1570,2364,3342, # 5366 +3276,7614, 557,1073,7615,1827,3445,2087,2266,3140,3039,3084, 767,3085,2786,4425, # 5382 +1006,4158,4426,2341,1267,2176,3664,3199, 778,3945,3200,2722,1597,2657,7616,4427, # 5398 +7617,3446,7618,7619,7620,3277,2689,1433,3278, 131, 95,1504,3946, 723,4159,3141, # 5414 +1841,3555,2758,2189,3947,2027,2104,3665,7621,2995,3948,1218,7622,3343,3201,3949, # 5430 +4160,2576, 248,1634,3785, 912,7623,2832,3666,3040,3786, 654, 53,7624,2996,7625, # 5446 +1688,4428, 777,3447,1032,3950,1425,7626, 191, 820,2120,2833, 971,4429, 931,3202, # 5462 + 135, 664, 783,3787,1997, 772,2908,1935,3951,3788,4430,2909,3203, 282,2723, 640, # 5478 +1372,3448,1127, 922, 325,3344,7627,7628, 711,2044,7629,7630,3952,2219,2787,1936, # 5494 +3953,3345,2220,2251,3789,2300,7631,4431,3790,1258,3279,3954,3204,2138,2950,3955, # 5510 +3956,7632,2221, 258,3205,4432, 101,1227,7633,3280,1755,7634,1391,3281,7635,2910, # 5526 +2056, 893,7636,7637,7638,1402,4161,2342,7639,7640,3206,3556,7641,7642, 878,1325, # 5542 +1780,2788,4433, 259,1385,2577, 744,1183,2267,4434,7643,3957,2502,7644, 684,1024, # 5558 +4162,7645, 472,3557,3449,1165,3282,3958,3959, 322,2152, 881, 455,1695,1152,1340, # 5574 + 660, 554,2153,4435,1058,4436,4163, 830,1065,3346,3960,4437,1923,7646,1703,1918, # 5590 +7647, 932,2268, 122,7648,4438, 947, 677,7649,3791,2627, 297,1905,1924,2269,4439, # 5606 +2317,3283,7650,7651,4164,7652,4165, 84,4166, 112, 989,7653, 547,1059,3961, 701, # 5622 +3558,1019,7654,4167,7655,3450, 942, 639, 457,2301,2451, 993,2951, 407, 851, 494, # 5638 +4440,3347, 927,7656,1237,7657,2421,3348, 573,4168, 680, 921,2911,1279,1874, 285, # 5654 + 790,1448,1983, 719,2167,7658,7659,4441,3962,3963,1649,7660,1541, 563,7661,1077, # 5670 +7662,3349,3041,3451, 511,2997,3964,3965,3667,3966,1268,2564,3350,3207,4442,4443, # 5686 +7663, 535,1048,1276,1189,2912,2028,3142,1438,1373,2834,2952,1134,2012,7664,4169, # 5702 +1238,2578,3086,1259,7665, 700,7666,2953,3143,3668,4170,7667,4171,1146,1875,1906, # 5718 +4444,2601,3967, 781,2422, 132,1589, 203, 147, 273,2789,2402, 898,1786,2154,3968, # 5734 +3969,7668,3792,2790,7669,7670,4445,4446,7671,3208,7672,1635,3793, 965,7673,1804, # 5750 +2690,1516,3559,1121,1082,1329,3284,3970,1449,3794, 65,1128,2835,2913,2759,1590, # 5766 +3795,7674,7675, 12,2658, 45, 976,2579,3144,4447, 517,2528,1013,1037,3209,7676, # 5782 +3796,2836,7677,3797,7678,3452,7679,2602, 614,1998,2318,3798,3087,2724,2628,7680, # 5798 +2580,4172, 599,1269,7681,1810,3669,7682,2691,3088, 759,1060, 489,1805,3351,3285, # 5814 +1358,7683,7684,2386,1387,1215,2629,2252, 490,7685,7686,4173,1759,2387,2343,7687, # 5830 +4448,3799,1907,3971,2630,1806,3210,4449,3453,3286,2760,2344, 874,7688,7689,3454, # 5846 +3670,1858, 91,2914,3671,3042,3800,4450,7690,3145,3972,2659,7691,3455,1202,1403, # 5862 +3801,2954,2529,1517,2503,4451,3456,2504,7692,4452,7693,2692,1885,1495,1731,3973, # 5878 +2365,4453,7694,2029,7695,7696,3974,2693,1216, 237,2581,4174,2319,3975,3802,4454, # 5894 +4455,2694,3560,3457, 445,4456,7697,7698,7699,7700,2761, 61,3976,3672,1822,3977, # 5910 +7701, 687,2045, 935, 925, 405,2660, 703,1096,1859,2725,4457,3978,1876,1367,2695, # 5926 +3352, 918,2105,1781,2476, 334,3287,1611,1093,4458, 564,3146,3458,3673,3353, 945, # 5942 +2631,2057,4459,7702,1925, 872,4175,7703,3459,2696,3089, 349,4176,3674,3979,4460, # 5958 +3803,4177,3675,2155,3980,4461,4462,4178,4463,2403,2046, 782,3981, 400, 251,4179, # 5974 +1624,7704,7705, 277,3676, 299,1265, 476,1191,3804,2121,4180,4181,1109, 205,7706, # 5990 +2582,1000,2156,3561,1860,7707,7708,7709,4464,7710,4465,2565, 107,2477,2157,3982, # 6006 +3460,3147,7711,1533, 541,1301, 158, 753,4182,2872,3562,7712,1696, 370,1088,4183, # 6022 +4466,3563, 579, 327, 440, 162,2240, 269,1937,1374,3461, 968,3043, 56,1396,3090, # 6038 +2106,3288,3354,7713,1926,2158,4467,2998,7714,3564,7715,7716,3677,4468,2478,7717, # 6054 +2791,7718,1650,4469,7719,2603,7720,7721,3983,2661,3355,1149,3356,3984,3805,3985, # 6070 +7722,1076, 49,7723, 951,3211,3289,3290, 450,2837, 920,7724,1811,2792,2366,4184, # 6086 +1908,1138,2367,3806,3462,7725,3212,4470,1909,1147,1518,2423,4471,3807,7726,4472, # 6102 +2388,2604, 260,1795,3213,7727,7728,3808,3291, 708,7729,3565,1704,7730,3566,1351, # 6118 +1618,3357,2999,1886, 944,4185,3358,4186,3044,3359,4187,7731,3678, 422, 413,1714, # 6134 +3292, 500,2058,2345,4188,2479,7732,1344,1910, 954,7733,1668,7734,7735,3986,2404, # 6150 +4189,3567,3809,4190,7736,2302,1318,2505,3091, 133,3092,2873,4473, 629, 31,2838, # 6166 +2697,3810,4474, 850, 949,4475,3987,2955,1732,2088,4191,1496,1852,7737,3988, 620, # 6182 +3214, 981,1242,3679,3360,1619,3680,1643,3293,2139,2452,1970,1719,3463,2168,7738, # 6198 +3215,7739,7740,3361,1828,7741,1277,4476,1565,2047,7742,1636,3568,3093,7743, 869, # 6214 +2839, 655,3811,3812,3094,3989,3000,3813,1310,3569,4477,7744,7745,7746,1733, 558, # 6230 +4478,3681, 335,1549,3045,1756,4192,3682,1945,3464,1829,1291,1192, 470,2726,2107, # 6246 +2793, 913,1054,3990,7747,1027,7748,3046,3991,4479, 982,2662,3362,3148,3465,3216, # 6262 +3217,1946,2794,7749, 571,4480,7750,1830,7751,3570,2583,1523,2424,7752,2089, 984, # 6278 +4481,3683,1959,7753,3684, 852, 923,2795,3466,3685, 969,1519, 999,2048,2320,1705, # 6294 +7754,3095, 615,1662, 151, 597,3992,2405,2321,1049, 275,4482,3686,4193, 568,3687, # 6310 +3571,2480,4194,3688,7755,2425,2270, 409,3218,7756,1566,2874,3467,1002, 769,2840, # 6326 + 194,2090,3149,3689,2222,3294,4195, 628,1505,7757,7758,1763,2177,3001,3993, 521, # 6342 +1161,2584,1787,2203,2406,4483,3994,1625,4196,4197, 412, 42,3096, 464,7759,2632, # 6358 +4484,3363,1760,1571,2875,3468,2530,1219,2204,3814,2633,2140,2368,4485,4486,3295, # 6374 +1651,3364,3572,7760,7761,3573,2481,3469,7762,3690,7763,7764,2271,2091, 460,7765, # 6390 +4487,7766,3002, 962, 588,3574, 289,3219,2634,1116, 52,7767,3047,1796,7768,7769, # 6406 +7770,1467,7771,1598,1143,3691,4198,1984,1734,1067,4488,1280,3365, 465,4489,1572, # 6422 + 510,7772,1927,2241,1812,1644,3575,7773,4490,3692,7774,7775,2663,1573,1534,7776, # 6438 +7777,4199, 536,1807,1761,3470,3815,3150,2635,7778,7779,7780,4491,3471,2915,1911, # 6454 +2796,7781,3296,1122, 377,3220,7782, 360,7783,7784,4200,1529, 551,7785,2059,3693, # 6470 +1769,2426,7786,2916,4201,3297,3097,2322,2108,2030,4492,1404, 136,1468,1479, 672, # 6486 +1171,3221,2303, 271,3151,7787,2762,7788,2049, 678,2727, 865,1947,4493,7789,2013, # 6502 +3995,2956,7790,2728,2223,1397,3048,3694,4494,4495,1735,2917,3366,3576,7791,3816, # 6518 + 509,2841,2453,2876,3817,7792,7793,3152,3153,4496,4202,2531,4497,2304,1166,1010, # 6534 + 552, 681,1887,7794,7795,2957,2958,3996,1287,1596,1861,3154, 358, 453, 736, 175, # 6550 + 478,1117, 905,1167,1097,7796,1853,1530,7797,1706,7798,2178,3472,2287,3695,3473, # 6566 +3577,4203,2092,4204,7799,3367,1193,2482,4205,1458,2190,2205,1862,1888,1421,3298, # 6582 +2918,3049,2179,3474, 595,2122,7800,3997,7801,7802,4206,1707,2636, 223,3696,1359, # 6598 + 751,3098, 183,3475,7803,2797,3003, 419,2369, 633, 704,3818,2389, 241,7804,7805, # 6614 +7806, 838,3004,3697,2272,2763,2454,3819,1938,2050,3998,1309,3099,2242,1181,7807, # 6630 +1136,2206,3820,2370,1446,4207,2305,4498,7808,7809,4208,1055,2605, 484,3698,7810, # 6646 +3999, 625,4209,2273,3368,1499,4210,4000,7811,4001,4211,3222,2274,2275,3476,7812, # 6662 +7813,2764, 808,2606,3699,3369,4002,4212,3100,2532, 526,3370,3821,4213, 955,7814, # 6678 +1620,4214,2637,2427,7815,1429,3700,1669,1831, 994, 928,7816,3578,1260,7817,7818, # 6694 +7819,1948,2288, 741,2919,1626,4215,2729,2455, 867,1184, 362,3371,1392,7820,7821, # 6710 +4003,4216,1770,1736,3223,2920,4499,4500,1928,2698,1459,1158,7822,3050,3372,2877, # 6726 +1292,1929,2506,2842,3701,1985,1187,2071,2014,2607,4217,7823,2566,2507,2169,3702, # 6742 +2483,3299,7824,3703,4501,7825,7826, 666,1003,3005,1022,3579,4218,7827,4502,1813, # 6758 +2253, 574,3822,1603, 295,1535, 705,3823,4219, 283, 858, 417,7828,7829,3224,4503, # 6774 +4504,3051,1220,1889,1046,2276,2456,4004,1393,1599, 689,2567, 388,4220,7830,2484, # 6790 + 802,7831,2798,3824,2060,1405,2254,7832,4505,3825,2109,1052,1345,3225,1585,7833, # 6806 + 809,7834,7835,7836, 575,2730,3477, 956,1552,1469,1144,2323,7837,2324,1560,2457, # 6822 +3580,3226,4005, 616,2207,3155,2180,2289,7838,1832,7839,3478,4506,7840,1319,3704, # 6838 +3705,1211,3581,1023,3227,1293,2799,7841,7842,7843,3826, 607,2306,3827, 762,2878, # 6854 +1439,4221,1360,7844,1485,3052,7845,4507,1038,4222,1450,2061,2638,4223,1379,4508, # 6870 +2585,7846,7847,4224,1352,1414,2325,2921,1172,7848,7849,3828,3829,7850,1797,1451, # 6886 +7851,7852,7853,7854,2922,4006,4007,2485,2346, 411,4008,4009,3582,3300,3101,4509, # 6902 +1561,2664,1452,4010,1375,7855,7856, 47,2959, 316,7857,1406,1591,2923,3156,7858, # 6918 +1025,2141,3102,3157, 354,2731, 884,2224,4225,2407, 508,3706, 726,3583, 996,2428, # 6934 +3584, 729,7859, 392,2191,1453,4011,4510,3707,7860,7861,2458,3585,2608,1675,2800, # 6950 + 919,2347,2960,2348,1270,4511,4012, 73,7862,7863, 647,7864,3228,2843,2255,1550, # 6966 +1346,3006,7865,1332, 883,3479,7866,7867,7868,7869,3301,2765,7870,1212, 831,1347, # 6982 +4226,4512,2326,3830,1863,3053, 720,3831,4513,4514,3832,7871,4227,7872,7873,4515, # 6998 +7874,7875,1798,4516,3708,2609,4517,3586,1645,2371,7876,7877,2924, 669,2208,2665, # 7014 +2429,7878,2879,7879,7880,1028,3229,7881,4228,2408,7882,2256,1353,7883,7884,4518, # 7030 +3158, 518,7885,4013,7886,4229,1960,7887,2142,4230,7888,7889,3007,2349,2350,3833, # 7046 + 516,1833,1454,4014,2699,4231,4519,2225,2610,1971,1129,3587,7890,2766,7891,2961, # 7062 +1422, 577,1470,3008,1524,3373,7892,7893, 432,4232,3054,3480,7894,2586,1455,2508, # 7078 +2226,1972,1175,7895,1020,2732,4015,3481,4520,7896,2733,7897,1743,1361,3055,3482, # 7094 +2639,4016,4233,4521,2290, 895, 924,4234,2170, 331,2243,3056, 166,1627,3057,1098, # 7110 +7898,1232,2880,2227,3374,4522, 657, 403,1196,2372, 542,3709,3375,1600,4235,3483, # 7126 +7899,4523,2767,3230, 576, 530,1362,7900,4524,2533,2666,3710,4017,7901, 842,3834, # 7142 +7902,2801,2031,1014,4018, 213,2700,3376, 665, 621,4236,7903,3711,2925,2430,7904, # 7158 +2431,3302,3588,3377,7905,4237,2534,4238,4525,3589,1682,4239,3484,1380,7906, 724, # 7174 +2277, 600,1670,7907,1337,1233,4526,3103,2244,7908,1621,4527,7909, 651,4240,7910, # 7190 +1612,4241,2611,7911,2844,7912,2734,2307,3058,7913, 716,2459,3059, 174,1255,2701, # 7206 +4019,3590, 548,1320,1398, 728,4020,1574,7914,1890,1197,3060,4021,7915,3061,3062, # 7222 +3712,3591,3713, 747,7916, 635,4242,4528,7917,7918,7919,4243,7920,7921,4529,7922, # 7238 +3378,4530,2432, 451,7923,3714,2535,2072,4244,2735,4245,4022,7924,1764,4531,7925, # 7254 +4246, 350,7926,2278,2390,2486,7927,4247,4023,2245,1434,4024, 488,4532, 458,4248, # 7270 +4025,3715, 771,1330,2391,3835,2568,3159,2159,2409,1553,2667,3160,4249,7928,2487, # 7286 +2881,2612,1720,2702,4250,3379,4533,7929,2536,4251,7930,3231,4252,2768,7931,2015, # 7302 +2736,7932,1155,1017,3716,3836,7933,3303,2308, 201,1864,4253,1430,7934,4026,7935, # 7318 +7936,7937,7938,7939,4254,1604,7940, 414,1865, 371,2587,4534,4535,3485,2016,3104, # 7334 +4536,1708, 960,4255, 887, 389,2171,1536,1663,1721,7941,2228,4027,2351,2926,1580, # 7350 +7942,7943,7944,1744,7945,2537,4537,4538,7946,4539,7947,2073,7948,7949,3592,3380, # 7366 +2882,4256,7950,4257,2640,3381,2802, 673,2703,2460, 709,3486,4028,3593,4258,7951, # 7382 +1148, 502, 634,7952,7953,1204,4540,3594,1575,4541,2613,3717,7954,3718,3105, 948, # 7398 +3232, 121,1745,3837,1110,7955,4259,3063,2509,3009,4029,3719,1151,1771,3838,1488, # 7414 +4030,1986,7956,2433,3487,7957,7958,2093,7959,4260,3839,1213,1407,2803, 531,2737, # 7430 +2538,3233,1011,1537,7960,2769,4261,3106,1061,7961,3720,3721,1866,2883,7962,2017, # 7446 + 120,4262,4263,2062,3595,3234,2309,3840,2668,3382,1954,4542,7963,7964,3488,1047, # 7462 +2704,1266,7965,1368,4543,2845, 649,3383,3841,2539,2738,1102,2846,2669,7966,7967, # 7478 +1999,7968,1111,3596,2962,7969,2488,3842,3597,2804,1854,3384,3722,7970,7971,3385, # 7494 +2410,2884,3304,3235,3598,7972,2569,7973,3599,2805,4031,1460, 856,7974,3600,7975, # 7510 +2885,2963,7976,2886,3843,7977,4264, 632,2510, 875,3844,1697,3845,2291,7978,7979, # 7526 +4544,3010,1239, 580,4545,4265,7980, 914, 936,2074,1190,4032,1039,2123,7981,7982, # 7542 +7983,3386,1473,7984,1354,4266,3846,7985,2172,3064,4033, 915,3305,4267,4268,3306, # 7558 +1605,1834,7986,2739, 398,3601,4269,3847,4034, 328,1912,2847,4035,3848,1331,4270, # 7574 +3011, 937,4271,7987,3602,4036,4037,3387,2160,4546,3388, 524, 742, 538,3065,1012, # 7590 +7988,7989,3849,2461,7990, 658,1103, 225,3850,7991,7992,4547,7993,4548,7994,3236, # 7606 +1243,7995,4038, 963,2246,4549,7996,2705,3603,3161,7997,7998,2588,2327,7999,4550, # 7622 +8000,8001,8002,3489,3307, 957,3389,2540,2032,1930,2927,2462, 870,2018,3604,1746, # 7638 +2770,2771,2434,2463,8003,3851,8004,3723,3107,3724,3490,3390,3725,8005,1179,3066, # 7654 +8006,3162,2373,4272,3726,2541,3163,3108,2740,4039,8007,3391,1556,2542,2292, 977, # 7670 +2887,2033,4040,1205,3392,8008,1765,3393,3164,2124,1271,1689, 714,4551,3491,8009, # 7686 +2328,3852, 533,4273,3605,2181, 617,8010,2464,3308,3492,2310,8011,8012,3165,8013, # 7702 +8014,3853,1987, 618, 427,2641,3493,3394,8015,8016,1244,1690,8017,2806,4274,4552, # 7718 +8018,3494,8019,8020,2279,1576, 473,3606,4275,3395, 972,8021,3607,8022,3067,8023, # 7734 +8024,4553,4554,8025,3727,4041,4042,8026, 153,4555, 356,8027,1891,2888,4276,2143, # 7750 + 408, 803,2352,8028,3854,8029,4277,1646,2570,2511,4556,4557,3855,8030,3856,4278, # 7766 +8031,2411,3396, 752,8032,8033,1961,2964,8034, 746,3012,2465,8035,4279,3728, 698, # 7782 +4558,1892,4280,3608,2543,4559,3609,3857,8036,3166,3397,8037,1823,1302,4043,2706, # 7798 +3858,1973,4281,8038,4282,3167, 823,1303,1288,1236,2848,3495,4044,3398, 774,3859, # 7814 +8039,1581,4560,1304,2849,3860,4561,8040,2435,2161,1083,3237,4283,4045,4284, 344, # 7830 +1173, 288,2311, 454,1683,8041,8042,1461,4562,4046,2589,8043,8044,4563, 985, 894, # 7846 +8045,3399,3168,8046,1913,2928,3729,1988,8047,2110,1974,8048,4047,8049,2571,1194, # 7862 + 425,8050,4564,3169,1245,3730,4285,8051,8052,2850,8053, 636,4565,1855,3861, 760, # 7878 +1799,8054,4286,2209,1508,4566,4048,1893,1684,2293,8055,8056,8057,4287,4288,2210, # 7894 + 479,8058,8059, 832,8060,4049,2489,8061,2965,2490,3731, 990,3109, 627,1814,2642, # 7910 +4289,1582,4290,2125,2111,3496,4567,8062, 799,4291,3170,8063,4568,2112,1737,3013, # 7926 +1018, 543, 754,4292,3309,1676,4569,4570,4050,8064,1489,8065,3497,8066,2614,2889, # 7942 +4051,8067,8068,2966,8069,8070,8071,8072,3171,4571,4572,2182,1722,8073,3238,3239, # 7958 +1842,3610,1715, 481, 365,1975,1856,8074,8075,1962,2491,4573,8076,2126,3611,3240, # 7974 + 433,1894,2063,2075,8077, 602,2741,8078,8079,8080,8081,8082,3014,1628,3400,8083, # 7990 +3172,4574,4052,2890,4575,2512,8084,2544,2772,8085,8086,8087,3310,4576,2891,8088, # 8006 +4577,8089,2851,4578,4579,1221,2967,4053,2513,8090,8091,8092,1867,1989,8093,8094, # 8022 +8095,1895,8096,8097,4580,1896,4054, 318,8098,2094,4055,4293,8099,8100, 485,8101, # 8038 + 938,3862, 553,2670, 116,8102,3863,3612,8103,3498,2671,2773,3401,3311,2807,8104, # 8054 +3613,2929,4056,1747,2930,2968,8105,8106, 207,8107,8108,2672,4581,2514,8109,3015, # 8070 + 890,3614,3864,8110,1877,3732,3402,8111,2183,2353,3403,1652,8112,8113,8114, 941, # 8086 +2294, 208,3499,4057,2019, 330,4294,3865,2892,2492,3733,4295,8115,8116,8117,8118, # 8102 +) + diff --git a/venv2/lib/python2.7/site-packages/pip/_vendor/chardet/euctwprober.py b/venv2/lib/python2.7/site-packages/pip/_vendor/chardet/euctwprober.py new file mode 100644 index 0000000..35669cc --- /dev/null +++ b/venv2/lib/python2.7/site-packages/pip/_vendor/chardet/euctwprober.py @@ -0,0 +1,46 @@ +######################## BEGIN LICENSE BLOCK ######################## +# The Original Code is mozilla.org code. +# +# The Initial Developer of the Original Code is +# Netscape Communications Corporation. +# Portions created by the Initial Developer are Copyright (C) 1998 +# the Initial Developer. All Rights Reserved. +# +# Contributor(s): +# Mark Pilgrim - port to Python +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA +# 02110-1301 USA +######################### END LICENSE BLOCK ######################### + +from .mbcharsetprober import MultiByteCharSetProber +from .codingstatemachine import CodingStateMachine +from .chardistribution import EUCTWDistributionAnalysis +from .mbcssm import EUCTW_SM_MODEL + +class EUCTWProber(MultiByteCharSetProber): + def __init__(self): + super(EUCTWProber, self).__init__() + self.coding_sm = CodingStateMachine(EUCTW_SM_MODEL) + self.distribution_analyzer = EUCTWDistributionAnalysis() + self.reset() + + @property + def charset_name(self): + return "EUC-TW" + + @property + def language(self): + return "Taiwan" diff --git a/venv2/lib/python2.7/site-packages/pip/_vendor/chardet/gb2312freq.py b/venv2/lib/python2.7/site-packages/pip/_vendor/chardet/gb2312freq.py new file mode 100644 index 0000000..697837b --- /dev/null +++ b/venv2/lib/python2.7/site-packages/pip/_vendor/chardet/gb2312freq.py @@ -0,0 +1,283 @@ +######################## BEGIN LICENSE BLOCK ######################## +# The Original Code is Mozilla Communicator client code. +# +# The Initial Developer of the Original Code is +# Netscape Communications Corporation. +# Portions created by the Initial Developer are Copyright (C) 1998 +# the Initial Developer. All Rights Reserved. +# +# Contributor(s): +# Mark Pilgrim - port to Python +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA +# 02110-1301 USA +######################### END LICENSE BLOCK ######################### + +# GB2312 most frequently used character table +# +# Char to FreqOrder table , from hz6763 + +# 512 --> 0.79 -- 0.79 +# 1024 --> 0.92 -- 0.13 +# 2048 --> 0.98 -- 0.06 +# 6768 --> 1.00 -- 0.02 +# +# Ideal Distribution Ratio = 0.79135/(1-0.79135) = 3.79 +# Random Distribution Ration = 512 / (3755 - 512) = 0.157 +# +# Typical Distribution Ratio about 25% of Ideal one, still much higher that RDR + +GB2312_TYPICAL_DISTRIBUTION_RATIO = 0.9 + +GB2312_TABLE_SIZE = 3760 + +GB2312_CHAR_TO_FREQ_ORDER = ( +1671, 749,1443,2364,3924,3807,2330,3921,1704,3463,2691,1511,1515, 572,3191,2205, +2361, 224,2558, 479,1711, 963,3162, 440,4060,1905,2966,2947,3580,2647,3961,3842, +2204, 869,4207, 970,2678,5626,2944,2956,1479,4048, 514,3595, 588,1346,2820,3409, + 249,4088,1746,1873,2047,1774, 581,1813, 358,1174,3590,1014,1561,4844,2245, 670, +1636,3112, 889,1286, 953, 556,2327,3060,1290,3141, 613, 185,3477,1367, 850,3820, +1715,2428,2642,2303,2732,3041,2562,2648,3566,3946,1349, 388,3098,2091,1360,3585, + 152,1687,1539, 738,1559, 59,1232,2925,2267,1388,1249,1741,1679,2960, 151,1566, +1125,1352,4271, 924,4296, 385,3166,4459, 310,1245,2850, 70,3285,2729,3534,3575, +2398,3298,3466,1960,2265, 217,3647, 864,1909,2084,4401,2773,1010,3269,5152, 853, +3051,3121,1244,4251,1895, 364,1499,1540,2313,1180,3655,2268, 562, 715,2417,3061, + 544, 336,3768,2380,1752,4075, 950, 280,2425,4382, 183,2759,3272, 333,4297,2155, +1688,2356,1444,1039,4540, 736,1177,3349,2443,2368,2144,2225, 565, 196,1482,3406, + 927,1335,4147, 692, 878,1311,1653,3911,3622,1378,4200,1840,2969,3149,2126,1816, +2534,1546,2393,2760, 737,2494, 13, 447, 245,2747, 38,2765,2129,2589,1079, 606, + 360, 471,3755,2890, 404, 848, 699,1785,1236, 370,2221,1023,3746,2074,2026,2023, +2388,1581,2119, 812,1141,3091,2536,1519, 804,2053, 406,1596,1090, 784, 548,4414, +1806,2264,2936,1100, 343,4114,5096, 622,3358, 743,3668,1510,1626,5020,3567,2513, +3195,4115,5627,2489,2991, 24,2065,2697,1087,2719, 48,1634, 315, 68, 985,2052, + 198,2239,1347,1107,1439, 597,2366,2172, 871,3307, 919,2487,2790,1867, 236,2570, +1413,3794, 906,3365,3381,1701,1982,1818,1524,2924,1205, 616,2586,2072,2004, 575, + 253,3099, 32,1365,1182, 197,1714,2454,1201, 554,3388,3224,2748, 756,2587, 250, +2567,1507,1517,3529,1922,2761,2337,3416,1961,1677,2452,2238,3153, 615, 911,1506, +1474,2495,1265,1906,2749,3756,3280,2161, 898,2714,1759,3450,2243,2444, 563, 26, +3286,2266,3769,3344,2707,3677, 611,1402, 531,1028,2871,4548,1375, 261,2948, 835, +1190,4134, 353, 840,2684,1900,3082,1435,2109,1207,1674, 329,1872,2781,4055,2686, +2104, 608,3318,2423,2957,2768,1108,3739,3512,3271,3985,2203,1771,3520,1418,2054, +1681,1153, 225,1627,2929, 162,2050,2511,3687,1954, 124,1859,2431,1684,3032,2894, + 585,4805,3969,2869,2704,2088,2032,2095,3656,2635,4362,2209, 256, 518,2042,2105, +3777,3657, 643,2298,1148,1779, 190, 989,3544, 414, 11,2135,2063,2979,1471, 403, +3678, 126, 770,1563, 671,2499,3216,2877, 600,1179, 307,2805,4937,1268,1297,2694, + 252,4032,1448,1494,1331,1394, 127,2256, 222,1647,1035,1481,3056,1915,1048, 873, +3651, 210, 33,1608,2516, 200,1520, 415, 102, 0,3389,1287, 817, 91,3299,2940, + 836,1814, 549,2197,1396,1669,2987,3582,2297,2848,4528,1070, 687, 20,1819, 121, +1552,1364,1461,1968,2617,3540,2824,2083, 177, 948,4938,2291, 110,4549,2066, 648, +3359,1755,2110,2114,4642,4845,1693,3937,3308,1257,1869,2123, 208,1804,3159,2992, +2531,2549,3361,2418,1350,2347,2800,2568,1291,2036,2680, 72, 842,1990, 212,1233, +1154,1586, 75,2027,3410,4900,1823,1337,2710,2676, 728,2810,1522,3026,4995, 157, + 755,1050,4022, 710, 785,1936,2194,2085,1406,2777,2400, 150,1250,4049,1206, 807, +1910, 534, 529,3309,1721,1660, 274, 39,2827, 661,2670,1578, 925,3248,3815,1094, +4278,4901,4252, 41,1150,3747,2572,2227,4501,3658,4902,3813,3357,3617,2884,2258, + 887, 538,4187,3199,1294,2439,3042,2329,2343,2497,1255, 107, 543,1527, 521,3478, +3568, 194,5062, 15, 961,3870,1241,1192,2664, 66,5215,3260,2111,1295,1127,2152, +3805,4135, 901,1164,1976, 398,1278, 530,1460, 748, 904,1054,1966,1426, 53,2909, + 509, 523,2279,1534, 536,1019, 239,1685, 460,2353, 673,1065,2401,3600,4298,2272, +1272,2363, 284,1753,3679,4064,1695, 81, 815,2677,2757,2731,1386, 859, 500,4221, +2190,2566, 757,1006,2519,2068,1166,1455, 337,2654,3203,1863,1682,1914,3025,1252, +1409,1366, 847, 714,2834,2038,3209, 964,2970,1901, 885,2553,1078,1756,3049, 301, +1572,3326, 688,2130,1996,2429,1805,1648,2930,3421,2750,3652,3088, 262,1158,1254, + 389,1641,1812, 526,1719, 923,2073,1073,1902, 468, 489,4625,1140, 857,2375,3070, +3319,2863, 380, 116,1328,2693,1161,2244, 273,1212,1884,2769,3011,1775,1142, 461, +3066,1200,2147,2212, 790, 702,2695,4222,1601,1058, 434,2338,5153,3640, 67,2360, +4099,2502, 618,3472,1329, 416,1132, 830,2782,1807,2653,3211,3510,1662, 192,2124, + 296,3979,1739,1611,3684, 23, 118, 324, 446,1239,1225, 293,2520,3814,3795,2535, +3116, 17,1074, 467,2692,2201, 387,2922, 45,1326,3055,1645,3659,2817, 958, 243, +1903,2320,1339,2825,1784,3289, 356, 576, 865,2315,2381,3377,3916,1088,3122,1713, +1655, 935, 628,4689,1034,1327, 441, 800, 720, 894,1979,2183,1528,5289,2702,1071, +4046,3572,2399,1571,3281, 79, 761,1103, 327, 134, 758,1899,1371,1615, 879, 442, + 215,2605,2579, 173,2048,2485,1057,2975,3317,1097,2253,3801,4263,1403,1650,2946, + 814,4968,3487,1548,2644,1567,1285, 2, 295,2636, 97, 946,3576, 832, 141,4257, +3273, 760,3821,3521,3156,2607, 949,1024,1733,1516,1803,1920,2125,2283,2665,3180, +1501,2064,3560,2171,1592, 803,3518,1416, 732,3897,4258,1363,1362,2458, 119,1427, + 602,1525,2608,1605,1639,3175, 694,3064, 10, 465, 76,2000,4846,4208, 444,3781, +1619,3353,2206,1273,3796, 740,2483, 320,1723,2377,3660,2619,1359,1137,1762,1724, +2345,2842,1850,1862, 912, 821,1866, 612,2625,1735,2573,3369,1093, 844, 89, 937, + 930,1424,3564,2413,2972,1004,3046,3019,2011, 711,3171,1452,4178, 428, 801,1943, + 432, 445,2811, 206,4136,1472, 730, 349, 73, 397,2802,2547, 998,1637,1167, 789, + 396,3217, 154,1218, 716,1120,1780,2819,4826,1931,3334,3762,2139,1215,2627, 552, +3664,3628,3232,1405,2383,3111,1356,2652,3577,3320,3101,1703, 640,1045,1370,1246, +4996, 371,1575,2436,1621,2210, 984,4033,1734,2638, 16,4529, 663,2755,3255,1451, +3917,2257,1253,1955,2234,1263,2951, 214,1229, 617, 485, 359,1831,1969, 473,2310, + 750,2058, 165, 80,2864,2419, 361,4344,2416,2479,1134, 796,3726,1266,2943, 860, +2715, 938, 390,2734,1313,1384, 248, 202, 877,1064,2854, 522,3907, 279,1602, 297, +2357, 395,3740, 137,2075, 944,4089,2584,1267,3802, 62,1533,2285, 178, 176, 780, +2440, 201,3707, 590, 478,1560,4354,2117,1075, 30, 74,4643,4004,1635,1441,2745, + 776,2596, 238,1077,1692,1912,2844, 605, 499,1742,3947, 241,3053, 980,1749, 936, +2640,4511,2582, 515,1543,2162,5322,2892,2993, 890,2148,1924, 665,1827,3581,1032, + 968,3163, 339,1044,1896, 270, 583,1791,1720,4367,1194,3488,3669, 43,2523,1657, + 163,2167, 290,1209,1622,3378, 550, 634,2508,2510, 695,2634,2384,2512,1476,1414, + 220,1469,2341,2138,2852,3183,2900,4939,2865,3502,1211,3680, 854,3227,1299,2976, +3172, 186,2998,1459, 443,1067,3251,1495, 321,1932,3054, 909, 753,1410,1828, 436, +2441,1119,1587,3164,2186,1258, 227, 231,1425,1890,3200,3942, 247, 959, 725,5254, +2741, 577,2158,2079, 929, 120, 174, 838,2813, 591,1115, 417,2024, 40,3240,1536, +1037, 291,4151,2354, 632,1298,2406,2500,3535,1825,1846,3451, 205,1171, 345,4238, + 18,1163, 811, 685,2208,1217, 425,1312,1508,1175,4308,2552,1033, 587,1381,3059, +2984,3482, 340,1316,4023,3972, 792,3176, 519, 777,4690, 918, 933,4130,2981,3741, + 90,3360,2911,2200,5184,4550, 609,3079,2030, 272,3379,2736, 363,3881,1130,1447, + 286, 779, 357,1169,3350,3137,1630,1220,2687,2391, 747,1277,3688,2618,2682,2601, +1156,3196,5290,4034,3102,1689,3596,3128, 874, 219,2783, 798, 508,1843,2461, 269, +1658,1776,1392,1913,2983,3287,2866,2159,2372, 829,4076, 46,4253,2873,1889,1894, + 915,1834,1631,2181,2318, 298, 664,2818,3555,2735, 954,3228,3117, 527,3511,2173, + 681,2712,3033,2247,2346,3467,1652, 155,2164,3382, 113,1994, 450, 899, 494, 994, +1237,2958,1875,2336,1926,3727, 545,1577,1550, 633,3473, 204,1305,3072,2410,1956, +2471, 707,2134, 841,2195,2196,2663,3843,1026,4940, 990,3252,4997, 368,1092, 437, +3212,3258,1933,1829, 675,2977,2893, 412, 943,3723,4644,3294,3283,2230,2373,5154, +2389,2241,2661,2323,1404,2524, 593, 787, 677,3008,1275,2059, 438,2709,2609,2240, +2269,2246,1446, 36,1568,1373,3892,1574,2301,1456,3962, 693,2276,5216,2035,1143, +2720,1919,1797,1811,2763,4137,2597,1830,1699,1488,1198,2090, 424,1694, 312,3634, +3390,4179,3335,2252,1214, 561,1059,3243,2295,2561, 975,5155,2321,2751,3772, 472, +1537,3282,3398,1047,2077,2348,2878,1323,3340,3076, 690,2906, 51, 369, 170,3541, +1060,2187,2688,3670,2541,1083,1683, 928,3918, 459, 109,4427, 599,3744,4286, 143, +2101,2730,2490, 82,1588,3036,2121, 281,1860, 477,4035,1238,2812,3020,2716,3312, +1530,2188,2055,1317, 843, 636,1808,1173,3495, 649, 181,1002, 147,3641,1159,2414, +3750,2289,2795, 813,3123,2610,1136,4368, 5,3391,4541,2174, 420, 429,1728, 754, +1228,2115,2219, 347,2223,2733, 735,1518,3003,2355,3134,1764,3948,3329,1888,2424, +1001,1234,1972,3321,3363,1672,1021,1450,1584, 226, 765, 655,2526,3404,3244,2302, +3665, 731, 594,2184, 319,1576, 621, 658,2656,4299,2099,3864,1279,2071,2598,2739, + 795,3086,3699,3908,1707,2352,2402,1382,3136,2475,1465,4847,3496,3865,1085,3004, +2591,1084, 213,2287,1963,3565,2250, 822, 793,4574,3187,1772,1789,3050, 595,1484, +1959,2770,1080,2650, 456, 422,2996, 940,3322,4328,4345,3092,2742, 965,2784, 739, +4124, 952,1358,2498,2949,2565, 332,2698,2378, 660,2260,2473,4194,3856,2919, 535, +1260,2651,1208,1428,1300,1949,1303,2942, 433,2455,2450,1251,1946, 614,1269, 641, +1306,1810,2737,3078,2912, 564,2365,1419,1415,1497,4460,2367,2185,1379,3005,1307, +3218,2175,1897,3063, 682,1157,4040,4005,1712,1160,1941,1399, 394, 402,2952,1573, +1151,2986,2404, 862, 299,2033,1489,3006, 346, 171,2886,3401,1726,2932, 168,2533, + 47,2507,1030,3735,1145,3370,1395,1318,1579,3609,4560,2857,4116,1457,2529,1965, + 504,1036,2690,2988,2405, 745,5871, 849,2397,2056,3081, 863,2359,3857,2096, 99, +1397,1769,2300,4428,1643,3455,1978,1757,3718,1440, 35,4879,3742,1296,4228,2280, + 160,5063,1599,2013, 166, 520,3479,1646,3345,3012, 490,1937,1545,1264,2182,2505, +1096,1188,1369,1436,2421,1667,2792,2460,1270,2122, 727,3167,2143, 806,1706,1012, +1800,3037, 960,2218,1882, 805, 139,2456,1139,1521, 851,1052,3093,3089, 342,2039, + 744,5097,1468,1502,1585,2087, 223, 939, 326,2140,2577, 892,2481,1623,4077, 982, +3708, 135,2131, 87,2503,3114,2326,1106, 876,1616, 547,2997,2831,2093,3441,4530, +4314, 9,3256,4229,4148, 659,1462,1986,1710,2046,2913,2231,4090,4880,5255,3392, +3274,1368,3689,4645,1477, 705,3384,3635,1068,1529,2941,1458,3782,1509, 100,1656, +2548, 718,2339, 408,1590,2780,3548,1838,4117,3719,1345,3530, 717,3442,2778,3220, +2898,1892,4590,3614,3371,2043,1998,1224,3483, 891, 635, 584,2559,3355, 733,1766, +1729,1172,3789,1891,2307, 781,2982,2271,1957,1580,5773,2633,2005,4195,3097,1535, +3213,1189,1934,5693,3262, 586,3118,1324,1598, 517,1564,2217,1868,1893,4445,3728, +2703,3139,1526,1787,1992,3882,2875,1549,1199,1056,2224,1904,2711,5098,4287, 338, +1993,3129,3489,2689,1809,2815,1997, 957,1855,3898,2550,3275,3057,1105,1319, 627, +1505,1911,1883,3526, 698,3629,3456,1833,1431, 746, 77,1261,2017,2296,1977,1885, + 125,1334,1600, 525,1798,1109,2222,1470,1945, 559,2236,1186,3443,2476,1929,1411, +2411,3135,1777,3372,2621,1841,1613,3229, 668,1430,1839,2643,2916, 195,1989,2671, +2358,1387, 629,3205,2293,5256,4439, 123,1310, 888,1879,4300,3021,3605,1003,1162, +3192,2910,2010, 140,2395,2859, 55,1082,2012,2901, 662, 419,2081,1438, 680,2774, +4654,3912,1620,1731,1625,5035,4065,2328, 512,1344, 802,5443,2163,2311,2537, 524, +3399, 98,1155,2103,1918,2606,3925,2816,1393,2465,1504,3773,2177,3963,1478,4346, + 180,1113,4655,3461,2028,1698, 833,2696,1235,1322,1594,4408,3623,3013,3225,2040, +3022, 541,2881, 607,3632,2029,1665,1219, 639,1385,1686,1099,2803,3231,1938,3188, +2858, 427, 676,2772,1168,2025, 454,3253,2486,3556, 230,1950, 580, 791,1991,1280, +1086,1974,2034, 630, 257,3338,2788,4903,1017, 86,4790, 966,2789,1995,1696,1131, + 259,3095,4188,1308, 179,1463,5257, 289,4107,1248, 42,3413,1725,2288, 896,1947, + 774,4474,4254, 604,3430,4264, 392,2514,2588, 452, 237,1408,3018, 988,4531,1970, +3034,3310, 540,2370,1562,1288,2990, 502,4765,1147, 4,1853,2708, 207, 294,2814, +4078,2902,2509, 684, 34,3105,3532,2551, 644, 709,2801,2344, 573,1727,3573,3557, +2021,1081,3100,4315,2100,3681, 199,2263,1837,2385, 146,3484,1195,2776,3949, 997, +1939,3973,1008,1091,1202,1962,1847,1149,4209,5444,1076, 493, 117,5400,2521, 972, +1490,2934,1796,4542,2374,1512,2933,2657, 413,2888,1135,2762,2314,2156,1355,2369, + 766,2007,2527,2170,3124,2491,2593,2632,4757,2437, 234,3125,3591,1898,1750,1376, +1942,3468,3138, 570,2127,2145,3276,4131, 962, 132,1445,4196, 19, 941,3624,3480, +3366,1973,1374,4461,3431,2629, 283,2415,2275, 808,2887,3620,2112,2563,1353,3610, + 955,1089,3103,1053, 96, 88,4097, 823,3808,1583, 399, 292,4091,3313, 421,1128, + 642,4006, 903,2539,1877,2082, 596, 29,4066,1790, 722,2157, 130, 995,1569, 769, +1485, 464, 513,2213, 288,1923,1101,2453,4316, 133, 486,2445, 50, 625, 487,2207, + 57, 423, 481,2962, 159,3729,1558, 491, 303, 482, 501, 240,2837, 112,3648,2392, +1783, 362, 8,3433,3422, 610,2793,3277,1390,1284,1654, 21,3823, 734, 367, 623, + 193, 287, 374,1009,1483, 816, 476, 313,2255,2340,1262,2150,2899,1146,2581, 782, +2116,1659,2018,1880, 255,3586,3314,1110,2867,2137,2564, 986,2767,5185,2006, 650, + 158, 926, 762, 881,3157,2717,2362,3587, 306,3690,3245,1542,3077,2427,1691,2478, +2118,2985,3490,2438, 539,2305, 983, 129,1754, 355,4201,2386, 827,2923, 104,1773, +2838,2771, 411,2905,3919, 376, 767, 122,1114, 828,2422,1817,3506, 266,3460,1007, +1609,4998, 945,2612,4429,2274, 726,1247,1964,2914,2199,2070,4002,4108, 657,3323, +1422, 579, 455,2764,4737,1222,2895,1670, 824,1223,1487,2525, 558, 861,3080, 598, +2659,2515,1967, 752,2583,2376,2214,4180, 977, 704,2464,4999,2622,4109,1210,2961, + 819,1541, 142,2284, 44, 418, 457,1126,3730,4347,4626,1644,1876,3671,1864, 302, +1063,5694, 624, 723,1984,3745,1314,1676,2488,1610,1449,3558,3569,2166,2098, 409, +1011,2325,3704,2306, 818,1732,1383,1824,1844,3757, 999,2705,3497,1216,1423,2683, +2426,2954,2501,2726,2229,1475,2554,5064,1971,1794,1666,2014,1343, 783, 724, 191, +2434,1354,2220,5065,1763,2752,2472,4152, 131, 175,2885,3434, 92,1466,4920,2616, +3871,3872,3866, 128,1551,1632, 669,1854,3682,4691,4125,1230, 188,2973,3290,1302, +1213, 560,3266, 917, 763,3909,3249,1760, 868,1958, 764,1782,2097, 145,2277,3774, +4462, 64,1491,3062, 971,2132,3606,2442, 221,1226,1617, 218, 323,1185,3207,3147, + 571, 619,1473,1005,1744,2281, 449,1887,2396,3685, 275, 375,3816,1743,3844,3731, + 845,1983,2350,4210,1377, 773, 967,3499,3052,3743,2725,4007,1697,1022,3943,1464, +3264,2855,2722,1952,1029,2839,2467, 84,4383,2215, 820,1391,2015,2448,3672, 377, +1948,2168, 797,2545,3536,2578,2645, 94,2874,1678, 405,1259,3071, 771, 546,1315, + 470,1243,3083, 895,2468, 981, 969,2037, 846,4181, 653,1276,2928, 14,2594, 557, +3007,2474, 156, 902,1338,1740,2574, 537,2518, 973,2282,2216,2433,1928, 138,2903, +1293,2631,1612, 646,3457, 839,2935, 111, 496,2191,2847, 589,3186, 149,3994,2060, +4031,2641,4067,3145,1870, 37,3597,2136,1025,2051,3009,3383,3549,1121,1016,3261, +1301, 251,2446,2599,2153, 872,3246, 637, 334,3705, 831, 884, 921,3065,3140,4092, +2198,1944, 246,2964, 108,2045,1152,1921,2308,1031, 203,3173,4170,1907,3890, 810, +1401,2003,1690, 506, 647,1242,2828,1761,1649,3208,2249,1589,3709,2931,5156,1708, + 498, 666,2613, 834,3817,1231, 184,2851,1124, 883,3197,2261,3710,1765,1553,2658, +1178,2639,2351, 93,1193, 942,2538,2141,4402, 235,1821, 870,1591,2192,1709,1871, +3341,1618,4126,2595,2334, 603, 651, 69, 701, 268,2662,3411,2555,1380,1606, 503, + 448, 254,2371,2646, 574,1187,2309,1770, 322,2235,1292,1801, 305, 566,1133, 229, +2067,2057, 706, 167, 483,2002,2672,3295,1820,3561,3067, 316, 378,2746,3452,1112, + 136,1981, 507,1651,2917,1117, 285,4591, 182,2580,3522,1304, 335,3303,1835,2504, +1795,1792,2248, 674,1018,2106,2449,1857,2292,2845, 976,3047,1781,2600,2727,1389, +1281, 52,3152, 153, 265,3950, 672,3485,3951,4463, 430,1183, 365, 278,2169, 27, +1407,1336,2304, 209,1340,1730,2202,1852,2403,2883, 979,1737,1062, 631,2829,2542, +3876,2592, 825,2086,2226,3048,3625, 352,1417,3724, 542, 991, 431,1351,3938,1861, +2294, 826,1361,2927,3142,3503,1738, 463,2462,2723, 582,1916,1595,2808, 400,3845, +3891,2868,3621,2254, 58,2492,1123, 910,2160,2614,1372,1603,1196,1072,3385,1700, +3267,1980, 696, 480,2430, 920, 799,1570,2920,1951,2041,4047,2540,1321,4223,2469, +3562,2228,1271,2602, 401,2833,3351,2575,5157, 907,2312,1256, 410, 263,3507,1582, + 996, 678,1849,2316,1480, 908,3545,2237, 703,2322, 667,1826,2849,1531,2604,2999, +2407,3146,2151,2630,1786,3711, 469,3542, 497,3899,2409, 858, 837,4446,3393,1274, + 786, 620,1845,2001,3311, 484, 308,3367,1204,1815,3691,2332,1532,2557,1842,2020, +2724,1927,2333,4440, 567, 22,1673,2728,4475,1987,1858,1144,1597, 101,1832,3601, + 12, 974,3783,4391, 951,1412, 1,3720, 453,4608,4041, 528,1041,1027,3230,2628, +1129, 875,1051,3291,1203,2262,1069,2860,2799,2149,2615,3278, 144,1758,3040, 31, + 475,1680, 366,2685,3184, 311,1642,4008,2466,5036,1593,1493,2809, 216,1420,1668, + 233, 304,2128,3284, 232,1429,1768,1040,2008,3407,2740,2967,2543, 242,2133, 778, +1565,2022,2620, 505,2189,2756,1098,2273, 372,1614, 708, 553,2846,2094,2278, 169, +3626,2835,4161, 228,2674,3165, 809,1454,1309, 466,1705,1095, 900,3423, 880,2667, +3751,5258,2317,3109,2571,4317,2766,1503,1342, 866,4447,1118, 63,2076, 314,1881, +1348,1061, 172, 978,3515,1747, 532, 511,3970, 6, 601, 905,2699,3300,1751, 276, +1467,3725,2668, 65,4239,2544,2779,2556,1604, 578,2451,1802, 992,2331,2624,1320, +3446, 713,1513,1013, 103,2786,2447,1661, 886,1702, 916, 654,3574,2031,1556, 751, +2178,2821,2179,1498,1538,2176, 271, 914,2251,2080,1325, 638,1953,2937,3877,2432, +2754, 95,3265,1716, 260,1227,4083, 775, 106,1357,3254, 426,1607, 555,2480, 772, +1985, 244,2546, 474, 495,1046,2611,1851,2061, 71,2089,1675,2590, 742,3758,2843, +3222,1433, 267,2180,2576,2826,2233,2092,3913,2435, 956,1745,3075, 856,2113,1116, + 451, 3,1988,2896,1398, 993,2463,1878,2049,1341,2718,2721,2870,2108, 712,2904, +4363,2753,2324, 277,2872,2349,2649, 384, 987, 435, 691,3000, 922, 164,3939, 652, +1500,1184,4153,2482,3373,2165,4848,2335,3775,3508,3154,2806,2830,1554,2102,1664, +2530,1434,2408, 893,1547,2623,3447,2832,2242,2532,3169,2856,3223,2078, 49,3770, +3469, 462, 318, 656,2259,3250,3069, 679,1629,2758, 344,1138,1104,3120,1836,1283, +3115,2154,1437,4448, 934, 759,1999, 794,2862,1038, 533,2560,1722,2342, 855,2626, +1197,1663,4476,3127, 85,4240,2528, 25,1111,1181,3673, 407,3470,4561,2679,2713, + 768,1925,2841,3986,1544,1165, 932, 373,1240,2146,1930,2673, 721,4766, 354,4333, + 391,2963, 187, 61,3364,1442,1102, 330,1940,1767, 341,3809,4118, 393,2496,2062, +2211, 105, 331, 300, 439, 913,1332, 626, 379,3304,1557, 328, 689,3952, 309,1555, + 931, 317,2517,3027, 325, 569, 686,2107,3084, 60,1042,1333,2794, 264,3177,4014, +1628, 258,3712, 7,4464,1176,1043,1778, 683, 114,1975, 78,1492, 383,1886, 510, + 386, 645,5291,2891,2069,3305,4138,3867,2939,2603,2493,1935,1066,1848,3588,1015, +1282,1289,4609, 697,1453,3044,2666,3611,1856,2412, 54, 719,1330, 568,3778,2459, +1748, 788, 492, 551,1191,1000, 488,3394,3763, 282,1799, 348,2016,1523,3155,2390, +1049, 382,2019,1788,1170, 729,2968,3523, 897,3926,2785,2938,3292, 350,2319,3238, +1718,1717,2655,3453,3143,4465, 161,2889,2980,2009,1421, 56,1908,1640,2387,2232, +1917,1874,2477,4921, 148, 83,3438, 592,4245,2882,1822,1055, 741, 115,1496,1624, + 381,1638,4592,1020, 516,3214, 458, 947,4575,1432, 211,1514,2926,1865,2142, 189, + 852,1221,1400,1486, 882,2299,4036, 351, 28,1122, 700,6479,6480,6481,6482,6483, #last 512 +) + diff --git a/venv2/lib/python2.7/site-packages/pip/_vendor/chardet/gb2312prober.py b/venv2/lib/python2.7/site-packages/pip/_vendor/chardet/gb2312prober.py new file mode 100644 index 0000000..8446d2d --- /dev/null +++ b/venv2/lib/python2.7/site-packages/pip/_vendor/chardet/gb2312prober.py @@ -0,0 +1,46 @@ +######################## BEGIN LICENSE BLOCK ######################## +# The Original Code is mozilla.org code. +# +# The Initial Developer of the Original Code is +# Netscape Communications Corporation. +# Portions created by the Initial Developer are Copyright (C) 1998 +# the Initial Developer. All Rights Reserved. +# +# Contributor(s): +# Mark Pilgrim - port to Python +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA +# 02110-1301 USA +######################### END LICENSE BLOCK ######################### + +from .mbcharsetprober import MultiByteCharSetProber +from .codingstatemachine import CodingStateMachine +from .chardistribution import GB2312DistributionAnalysis +from .mbcssm import GB2312_SM_MODEL + +class GB2312Prober(MultiByteCharSetProber): + def __init__(self): + super(GB2312Prober, self).__init__() + self.coding_sm = CodingStateMachine(GB2312_SM_MODEL) + self.distribution_analyzer = GB2312DistributionAnalysis() + self.reset() + + @property + def charset_name(self): + return "GB2312" + + @property + def language(self): + return "Chinese" diff --git a/venv2/lib/python2.7/site-packages/pip/_vendor/chardet/hebrewprober.py b/venv2/lib/python2.7/site-packages/pip/_vendor/chardet/hebrewprober.py new file mode 100644 index 0000000..b0e1bf4 --- /dev/null +++ b/venv2/lib/python2.7/site-packages/pip/_vendor/chardet/hebrewprober.py @@ -0,0 +1,292 @@ +######################## BEGIN LICENSE BLOCK ######################## +# The Original Code is Mozilla Universal charset detector code. +# +# The Initial Developer of the Original Code is +# Shy Shalom +# Portions created by the Initial Developer are Copyright (C) 2005 +# the Initial Developer. All Rights Reserved. +# +# Contributor(s): +# Mark Pilgrim - port to Python +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA +# 02110-1301 USA +######################### END LICENSE BLOCK ######################### + +from .charsetprober import CharSetProber +from .enums import ProbingState + +# This prober doesn't actually recognize a language or a charset. +# It is a helper prober for the use of the Hebrew model probers + +### General ideas of the Hebrew charset recognition ### +# +# Four main charsets exist in Hebrew: +# "ISO-8859-8" - Visual Hebrew +# "windows-1255" - Logical Hebrew +# "ISO-8859-8-I" - Logical Hebrew +# "x-mac-hebrew" - ?? Logical Hebrew ?? +# +# Both "ISO" charsets use a completely identical set of code points, whereas +# "windows-1255" and "x-mac-hebrew" are two different proper supersets of +# these code points. windows-1255 defines additional characters in the range +# 0x80-0x9F as some misc punctuation marks as well as some Hebrew-specific +# diacritics and additional 'Yiddish' ligature letters in the range 0xc0-0xd6. +# x-mac-hebrew defines similar additional code points but with a different +# mapping. +# +# As far as an average Hebrew text with no diacritics is concerned, all four +# charsets are identical with respect to code points. Meaning that for the +# main Hebrew alphabet, all four map the same values to all 27 Hebrew letters +# (including final letters). +# +# The dominant difference between these charsets is their directionality. +# "Visual" directionality means that the text is ordered as if the renderer is +# not aware of a BIDI rendering algorithm. The renderer sees the text and +# draws it from left to right. The text itself when ordered naturally is read +# backwards. A buffer of Visual Hebrew generally looks like so: +# "[last word of first line spelled backwards] [whole line ordered backwards +# and spelled backwards] [first word of first line spelled backwards] +# [end of line] [last word of second line] ... etc' " +# adding punctuation marks, numbers and English text to visual text is +# naturally also "visual" and from left to right. +# +# "Logical" directionality means the text is ordered "naturally" according to +# the order it is read. It is the responsibility of the renderer to display +# the text from right to left. A BIDI algorithm is used to place general +# punctuation marks, numbers and English text in the text. +# +# Texts in x-mac-hebrew are almost impossible to find on the Internet. From +# what little evidence I could find, it seems that its general directionality +# is Logical. +# +# To sum up all of the above, the Hebrew probing mechanism knows about two +# charsets: +# Visual Hebrew - "ISO-8859-8" - backwards text - Words and sentences are +# backwards while line order is natural. For charset recognition purposes +# the line order is unimportant (In fact, for this implementation, even +# word order is unimportant). +# Logical Hebrew - "windows-1255" - normal, naturally ordered text. +# +# "ISO-8859-8-I" is a subset of windows-1255 and doesn't need to be +# specifically identified. +# "x-mac-hebrew" is also identified as windows-1255. A text in x-mac-hebrew +# that contain special punctuation marks or diacritics is displayed with +# some unconverted characters showing as question marks. This problem might +# be corrected using another model prober for x-mac-hebrew. Due to the fact +# that x-mac-hebrew texts are so rare, writing another model prober isn't +# worth the effort and performance hit. +# +#### The Prober #### +# +# The prober is divided between two SBCharSetProbers and a HebrewProber, +# all of which are managed, created, fed data, inquired and deleted by the +# SBCSGroupProber. The two SBCharSetProbers identify that the text is in +# fact some kind of Hebrew, Logical or Visual. The final decision about which +# one is it is made by the HebrewProber by combining final-letter scores +# with the scores of the two SBCharSetProbers to produce a final answer. +# +# The SBCSGroupProber is responsible for stripping the original text of HTML +# tags, English characters, numbers, low-ASCII punctuation characters, spaces +# and new lines. It reduces any sequence of such characters to a single space. +# The buffer fed to each prober in the SBCS group prober is pure text in +# high-ASCII. +# The two SBCharSetProbers (model probers) share the same language model: +# Win1255Model. +# The first SBCharSetProber uses the model normally as any other +# SBCharSetProber does, to recognize windows-1255, upon which this model was +# built. The second SBCharSetProber is told to make the pair-of-letter +# lookup in the language model backwards. This in practice exactly simulates +# a visual Hebrew model using the windows-1255 logical Hebrew model. +# +# The HebrewProber is not using any language model. All it does is look for +# final-letter evidence suggesting the text is either logical Hebrew or visual +# Hebrew. Disjointed from the model probers, the results of the HebrewProber +# alone are meaningless. HebrewProber always returns 0.00 as confidence +# since it never identifies a charset by itself. Instead, the pointer to the +# HebrewProber is passed to the model probers as a helper "Name Prober". +# When the Group prober receives a positive identification from any prober, +# it asks for the name of the charset identified. If the prober queried is a +# Hebrew model prober, the model prober forwards the call to the +# HebrewProber to make the final decision. In the HebrewProber, the +# decision is made according to the final-letters scores maintained and Both +# model probers scores. The answer is returned in the form of the name of the +# charset identified, either "windows-1255" or "ISO-8859-8". + +class HebrewProber(CharSetProber): + # windows-1255 / ISO-8859-8 code points of interest + FINAL_KAF = 0xea + NORMAL_KAF = 0xeb + FINAL_MEM = 0xed + NORMAL_MEM = 0xee + FINAL_NUN = 0xef + NORMAL_NUN = 0xf0 + FINAL_PE = 0xf3 + NORMAL_PE = 0xf4 + FINAL_TSADI = 0xf5 + NORMAL_TSADI = 0xf6 + + # Minimum Visual vs Logical final letter score difference. + # If the difference is below this, don't rely solely on the final letter score + # distance. + MIN_FINAL_CHAR_DISTANCE = 5 + + # Minimum Visual vs Logical model score difference. + # If the difference is below this, don't rely at all on the model score + # distance. + MIN_MODEL_DISTANCE = 0.01 + + VISUAL_HEBREW_NAME = "ISO-8859-8" + LOGICAL_HEBREW_NAME = "windows-1255" + + def __init__(self): + super(HebrewProber, self).__init__() + self._final_char_logical_score = None + self._final_char_visual_score = None + self._prev = None + self._before_prev = None + self._logical_prober = None + self._visual_prober = None + self.reset() + + def reset(self): + self._final_char_logical_score = 0 + self._final_char_visual_score = 0 + # The two last characters seen in the previous buffer, + # mPrev and mBeforePrev are initialized to space in order to simulate + # a word delimiter at the beginning of the data + self._prev = ' ' + self._before_prev = ' ' + # These probers are owned by the group prober. + + def set_model_probers(self, logicalProber, visualProber): + self._logical_prober = logicalProber + self._visual_prober = visualProber + + def is_final(self, c): + return c in [self.FINAL_KAF, self.FINAL_MEM, self.FINAL_NUN, + self.FINAL_PE, self.FINAL_TSADI] + + def is_non_final(self, c): + # The normal Tsadi is not a good Non-Final letter due to words like + # 'lechotet' (to chat) containing an apostrophe after the tsadi. This + # apostrophe is converted to a space in FilterWithoutEnglishLetters + # causing the Non-Final tsadi to appear at an end of a word even + # though this is not the case in the original text. + # The letters Pe and Kaf rarely display a related behavior of not being + # a good Non-Final letter. Words like 'Pop', 'Winamp' and 'Mubarak' + # for example legally end with a Non-Final Pe or Kaf. However, the + # benefit of these letters as Non-Final letters outweighs the damage + # since these words are quite rare. + return c in [self.NORMAL_KAF, self.NORMAL_MEM, + self.NORMAL_NUN, self.NORMAL_PE] + + def feed(self, byte_str): + # Final letter analysis for logical-visual decision. + # Look for evidence that the received buffer is either logical Hebrew + # or visual Hebrew. + # The following cases are checked: + # 1) A word longer than 1 letter, ending with a final letter. This is + # an indication that the text is laid out "naturally" since the + # final letter really appears at the end. +1 for logical score. + # 2) A word longer than 1 letter, ending with a Non-Final letter. In + # normal Hebrew, words ending with Kaf, Mem, Nun, Pe or Tsadi, + # should not end with the Non-Final form of that letter. Exceptions + # to this rule are mentioned above in isNonFinal(). This is an + # indication that the text is laid out backwards. +1 for visual + # score + # 3) A word longer than 1 letter, starting with a final letter. Final + # letters should not appear at the beginning of a word. This is an + # indication that the text is laid out backwards. +1 for visual + # score. + # + # The visual score and logical score are accumulated throughout the + # text and are finally checked against each other in GetCharSetName(). + # No checking for final letters in the middle of words is done since + # that case is not an indication for either Logical or Visual text. + # + # We automatically filter out all 7-bit characters (replace them with + # spaces) so the word boundary detection works properly. [MAP] + + if self.state == ProbingState.NOT_ME: + # Both model probers say it's not them. No reason to continue. + return ProbingState.NOT_ME + + byte_str = self.filter_high_byte_only(byte_str) + + for cur in byte_str: + if cur == ' ': + # We stand on a space - a word just ended + if self._before_prev != ' ': + # next-to-last char was not a space so self._prev is not a + # 1 letter word + if self.is_final(self._prev): + # case (1) [-2:not space][-1:final letter][cur:space] + self._final_char_logical_score += 1 + elif self.is_non_final(self._prev): + # case (2) [-2:not space][-1:Non-Final letter][ + # cur:space] + self._final_char_visual_score += 1 + else: + # Not standing on a space + if ((self._before_prev == ' ') and + (self.is_final(self._prev)) and (cur != ' ')): + # case (3) [-2:space][-1:final letter][cur:not space] + self._final_char_visual_score += 1 + self._before_prev = self._prev + self._prev = cur + + # Forever detecting, till the end or until both model probers return + # ProbingState.NOT_ME (handled above) + return ProbingState.DETECTING + + @property + def charset_name(self): + # Make the decision: is it Logical or Visual? + # If the final letter score distance is dominant enough, rely on it. + finalsub = self._final_char_logical_score - self._final_char_visual_score + if finalsub >= self.MIN_FINAL_CHAR_DISTANCE: + return self.LOGICAL_HEBREW_NAME + if finalsub <= -self.MIN_FINAL_CHAR_DISTANCE: + return self.VISUAL_HEBREW_NAME + + # It's not dominant enough, try to rely on the model scores instead. + modelsub = (self._logical_prober.get_confidence() + - self._visual_prober.get_confidence()) + if modelsub > self.MIN_MODEL_DISTANCE: + return self.LOGICAL_HEBREW_NAME + if modelsub < -self.MIN_MODEL_DISTANCE: + return self.VISUAL_HEBREW_NAME + + # Still no good, back to final letter distance, maybe it'll save the + # day. + if finalsub < 0.0: + return self.VISUAL_HEBREW_NAME + + # (finalsub > 0 - Logical) or (don't know what to do) default to + # Logical. + return self.LOGICAL_HEBREW_NAME + + @property + def language(self): + return 'Hebrew' + + @property + def state(self): + # Remain active as long as any of the model probers are active. + if (self._logical_prober.state == ProbingState.NOT_ME) and \ + (self._visual_prober.state == ProbingState.NOT_ME): + return ProbingState.NOT_ME + return ProbingState.DETECTING diff --git a/venv2/lib/python2.7/site-packages/pip/_vendor/chardet/jisfreq.py b/venv2/lib/python2.7/site-packages/pip/_vendor/chardet/jisfreq.py new file mode 100644 index 0000000..83fc082 --- /dev/null +++ b/venv2/lib/python2.7/site-packages/pip/_vendor/chardet/jisfreq.py @@ -0,0 +1,325 @@ +######################## BEGIN LICENSE BLOCK ######################## +# The Original Code is Mozilla Communicator client code. +# +# The Initial Developer of the Original Code is +# Netscape Communications Corporation. +# Portions created by the Initial Developer are Copyright (C) 1998 +# the Initial Developer. All Rights Reserved. +# +# Contributor(s): +# Mark Pilgrim - port to Python +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA +# 02110-1301 USA +######################### END LICENSE BLOCK ######################### + +# Sampling from about 20M text materials include literature and computer technology +# +# Japanese frequency table, applied to both S-JIS and EUC-JP +# They are sorted in order. + +# 128 --> 0.77094 +# 256 --> 0.85710 +# 512 --> 0.92635 +# 1024 --> 0.97130 +# 2048 --> 0.99431 +# +# Ideal Distribution Ratio = 0.92635 / (1-0.92635) = 12.58 +# Random Distribution Ration = 512 / (2965+62+83+86-512) = 0.191 +# +# Typical Distribution Ratio, 25% of IDR + +JIS_TYPICAL_DISTRIBUTION_RATIO = 3.0 + +# Char to FreqOrder table , +JIS_TABLE_SIZE = 4368 + +JIS_CHAR_TO_FREQ_ORDER = ( + 40, 1, 6, 182, 152, 180, 295,2127, 285, 381,3295,4304,3068,4606,3165,3510, # 16 +3511,1822,2785,4607,1193,2226,5070,4608, 171,2996,1247, 18, 179,5071, 856,1661, # 32 +1262,5072, 619, 127,3431,3512,3230,1899,1700, 232, 228,1294,1298, 284, 283,2041, # 48 +2042,1061,1062, 48, 49, 44, 45, 433, 434,1040,1041, 996, 787,2997,1255,4305, # 64 +2108,4609,1684,1648,5073,5074,5075,5076,5077,5078,3687,5079,4610,5080,3927,3928, # 80 +5081,3296,3432, 290,2285,1471,2187,5082,2580,2825,1303,2140,1739,1445,2691,3375, # 96 +1691,3297,4306,4307,4611, 452,3376,1182,2713,3688,3069,4308,5083,5084,5085,5086, # 112 +5087,5088,5089,5090,5091,5092,5093,5094,5095,5096,5097,5098,5099,5100,5101,5102, # 128 +5103,5104,5105,5106,5107,5108,5109,5110,5111,5112,4097,5113,5114,5115,5116,5117, # 144 +5118,5119,5120,5121,5122,5123,5124,5125,5126,5127,5128,5129,5130,5131,5132,5133, # 160 +5134,5135,5136,5137,5138,5139,5140,5141,5142,5143,5144,5145,5146,5147,5148,5149, # 176 +5150,5151,5152,4612,5153,5154,5155,5156,5157,5158,5159,5160,5161,5162,5163,5164, # 192 +5165,5166,5167,5168,5169,5170,5171,5172,5173,5174,5175,1472, 598, 618, 820,1205, # 208 +1309,1412,1858,1307,1692,5176,5177,5178,5179,5180,5181,5182,1142,1452,1234,1172, # 224 +1875,2043,2149,1793,1382,2973, 925,2404,1067,1241, 960,1377,2935,1491, 919,1217, # 240 +1865,2030,1406,1499,2749,4098,5183,5184,5185,5186,5187,5188,2561,4099,3117,1804, # 256 +2049,3689,4309,3513,1663,5189,3166,3118,3298,1587,1561,3433,5190,3119,1625,2998, # 272 +3299,4613,1766,3690,2786,4614,5191,5192,5193,5194,2161, 26,3377, 2,3929, 20, # 288 +3691, 47,4100, 50, 17, 16, 35, 268, 27, 243, 42, 155, 24, 154, 29, 184, # 304 + 4, 91, 14, 92, 53, 396, 33, 289, 9, 37, 64, 620, 21, 39, 321, 5, # 320 + 12, 11, 52, 13, 3, 208, 138, 0, 7, 60, 526, 141, 151,1069, 181, 275, # 336 +1591, 83, 132,1475, 126, 331, 829, 15, 69, 160, 59, 22, 157, 55,1079, 312, # 352 + 109, 38, 23, 25, 10, 19, 79,5195, 61, 382,1124, 8, 30,5196,5197,5198, # 368 +5199,5200,5201,5202,5203,5204,5205,5206, 89, 62, 74, 34,2416, 112, 139, 196, # 384 + 271, 149, 84, 607, 131, 765, 46, 88, 153, 683, 76, 874, 101, 258, 57, 80, # 400 + 32, 364, 121,1508, 169,1547, 68, 235, 145,2999, 41, 360,3027, 70, 63, 31, # 416 + 43, 259, 262,1383, 99, 533, 194, 66, 93, 846, 217, 192, 56, 106, 58, 565, # 432 + 280, 272, 311, 256, 146, 82, 308, 71, 100, 128, 214, 655, 110, 261, 104,1140, # 448 + 54, 51, 36, 87, 67,3070, 185,2618,2936,2020, 28,1066,2390,2059,5207,5208, # 464 +5209,5210,5211,5212,5213,5214,5215,5216,4615,5217,5218,5219,5220,5221,5222,5223, # 480 +5224,5225,5226,5227,5228,5229,5230,5231,5232,5233,5234,5235,5236,3514,5237,5238, # 496 +5239,5240,5241,5242,5243,5244,2297,2031,4616,4310,3692,5245,3071,5246,3598,5247, # 512 +4617,3231,3515,5248,4101,4311,4618,3808,4312,4102,5249,4103,4104,3599,5250,5251, # 528 +5252,5253,5254,5255,5256,5257,5258,5259,5260,5261,5262,5263,5264,5265,5266,5267, # 544 +5268,5269,5270,5271,5272,5273,5274,5275,5276,5277,5278,5279,5280,5281,5282,5283, # 560 +5284,5285,5286,5287,5288,5289,5290,5291,5292,5293,5294,5295,5296,5297,5298,5299, # 576 +5300,5301,5302,5303,5304,5305,5306,5307,5308,5309,5310,5311,5312,5313,5314,5315, # 592 +5316,5317,5318,5319,5320,5321,5322,5323,5324,5325,5326,5327,5328,5329,5330,5331, # 608 +5332,5333,5334,5335,5336,5337,5338,5339,5340,5341,5342,5343,5344,5345,5346,5347, # 624 +5348,5349,5350,5351,5352,5353,5354,5355,5356,5357,5358,5359,5360,5361,5362,5363, # 640 +5364,5365,5366,5367,5368,5369,5370,5371,5372,5373,5374,5375,5376,5377,5378,5379, # 656 +5380,5381, 363, 642,2787,2878,2788,2789,2316,3232,2317,3434,2011, 165,1942,3930, # 672 +3931,3932,3933,5382,4619,5383,4620,5384,5385,5386,5387,5388,5389,5390,5391,5392, # 688 +5393,5394,5395,5396,5397,5398,5399,5400,5401,5402,5403,5404,5405,5406,5407,5408, # 704 +5409,5410,5411,5412,5413,5414,5415,5416,5417,5418,5419,5420,5421,5422,5423,5424, # 720 +5425,5426,5427,5428,5429,5430,5431,5432,5433,5434,5435,5436,5437,5438,5439,5440, # 736 +5441,5442,5443,5444,5445,5446,5447,5448,5449,5450,5451,5452,5453,5454,5455,5456, # 752 +5457,5458,5459,5460,5461,5462,5463,5464,5465,5466,5467,5468,5469,5470,5471,5472, # 768 +5473,5474,5475,5476,5477,5478,5479,5480,5481,5482,5483,5484,5485,5486,5487,5488, # 784 +5489,5490,5491,5492,5493,5494,5495,5496,5497,5498,5499,5500,5501,5502,5503,5504, # 800 +5505,5506,5507,5508,5509,5510,5511,5512,5513,5514,5515,5516,5517,5518,5519,5520, # 816 +5521,5522,5523,5524,5525,5526,5527,5528,5529,5530,5531,5532,5533,5534,5535,5536, # 832 +5537,5538,5539,5540,5541,5542,5543,5544,5545,5546,5547,5548,5549,5550,5551,5552, # 848 +5553,5554,5555,5556,5557,5558,5559,5560,5561,5562,5563,5564,5565,5566,5567,5568, # 864 +5569,5570,5571,5572,5573,5574,5575,5576,5577,5578,5579,5580,5581,5582,5583,5584, # 880 +5585,5586,5587,5588,5589,5590,5591,5592,5593,5594,5595,5596,5597,5598,5599,5600, # 896 +5601,5602,5603,5604,5605,5606,5607,5608,5609,5610,5611,5612,5613,5614,5615,5616, # 912 +5617,5618,5619,5620,5621,5622,5623,5624,5625,5626,5627,5628,5629,5630,5631,5632, # 928 +5633,5634,5635,5636,5637,5638,5639,5640,5641,5642,5643,5644,5645,5646,5647,5648, # 944 +5649,5650,5651,5652,5653,5654,5655,5656,5657,5658,5659,5660,5661,5662,5663,5664, # 960 +5665,5666,5667,5668,5669,5670,5671,5672,5673,5674,5675,5676,5677,5678,5679,5680, # 976 +5681,5682,5683,5684,5685,5686,5687,5688,5689,5690,5691,5692,5693,5694,5695,5696, # 992 +5697,5698,5699,5700,5701,5702,5703,5704,5705,5706,5707,5708,5709,5710,5711,5712, # 1008 +5713,5714,5715,5716,5717,5718,5719,5720,5721,5722,5723,5724,5725,5726,5727,5728, # 1024 +5729,5730,5731,5732,5733,5734,5735,5736,5737,5738,5739,5740,5741,5742,5743,5744, # 1040 +5745,5746,5747,5748,5749,5750,5751,5752,5753,5754,5755,5756,5757,5758,5759,5760, # 1056 +5761,5762,5763,5764,5765,5766,5767,5768,5769,5770,5771,5772,5773,5774,5775,5776, # 1072 +5777,5778,5779,5780,5781,5782,5783,5784,5785,5786,5787,5788,5789,5790,5791,5792, # 1088 +5793,5794,5795,5796,5797,5798,5799,5800,5801,5802,5803,5804,5805,5806,5807,5808, # 1104 +5809,5810,5811,5812,5813,5814,5815,5816,5817,5818,5819,5820,5821,5822,5823,5824, # 1120 +5825,5826,5827,5828,5829,5830,5831,5832,5833,5834,5835,5836,5837,5838,5839,5840, # 1136 +5841,5842,5843,5844,5845,5846,5847,5848,5849,5850,5851,5852,5853,5854,5855,5856, # 1152 +5857,5858,5859,5860,5861,5862,5863,5864,5865,5866,5867,5868,5869,5870,5871,5872, # 1168 +5873,5874,5875,5876,5877,5878,5879,5880,5881,5882,5883,5884,5885,5886,5887,5888, # 1184 +5889,5890,5891,5892,5893,5894,5895,5896,5897,5898,5899,5900,5901,5902,5903,5904, # 1200 +5905,5906,5907,5908,5909,5910,5911,5912,5913,5914,5915,5916,5917,5918,5919,5920, # 1216 +5921,5922,5923,5924,5925,5926,5927,5928,5929,5930,5931,5932,5933,5934,5935,5936, # 1232 +5937,5938,5939,5940,5941,5942,5943,5944,5945,5946,5947,5948,5949,5950,5951,5952, # 1248 +5953,5954,5955,5956,5957,5958,5959,5960,5961,5962,5963,5964,5965,5966,5967,5968, # 1264 +5969,5970,5971,5972,5973,5974,5975,5976,5977,5978,5979,5980,5981,5982,5983,5984, # 1280 +5985,5986,5987,5988,5989,5990,5991,5992,5993,5994,5995,5996,5997,5998,5999,6000, # 1296 +6001,6002,6003,6004,6005,6006,6007,6008,6009,6010,6011,6012,6013,6014,6015,6016, # 1312 +6017,6018,6019,6020,6021,6022,6023,6024,6025,6026,6027,6028,6029,6030,6031,6032, # 1328 +6033,6034,6035,6036,6037,6038,6039,6040,6041,6042,6043,6044,6045,6046,6047,6048, # 1344 +6049,6050,6051,6052,6053,6054,6055,6056,6057,6058,6059,6060,6061,6062,6063,6064, # 1360 +6065,6066,6067,6068,6069,6070,6071,6072,6073,6074,6075,6076,6077,6078,6079,6080, # 1376 +6081,6082,6083,6084,6085,6086,6087,6088,6089,6090,6091,6092,6093,6094,6095,6096, # 1392 +6097,6098,6099,6100,6101,6102,6103,6104,6105,6106,6107,6108,6109,6110,6111,6112, # 1408 +6113,6114,2044,2060,4621, 997,1235, 473,1186,4622, 920,3378,6115,6116, 379,1108, # 1424 +4313,2657,2735,3934,6117,3809, 636,3233, 573,1026,3693,3435,2974,3300,2298,4105, # 1440 + 854,2937,2463, 393,2581,2417, 539, 752,1280,2750,2480, 140,1161, 440, 708,1569, # 1456 + 665,2497,1746,1291,1523,3000, 164,1603, 847,1331, 537,1997, 486, 508,1693,2418, # 1472 +1970,2227, 878,1220, 299,1030, 969, 652,2751, 624,1137,3301,2619, 65,3302,2045, # 1488 +1761,1859,3120,1930,3694,3516, 663,1767, 852, 835,3695, 269, 767,2826,2339,1305, # 1504 + 896,1150, 770,1616,6118, 506,1502,2075,1012,2519, 775,2520,2975,2340,2938,4314, # 1520 +3028,2086,1224,1943,2286,6119,3072,4315,2240,1273,1987,3935,1557, 175, 597, 985, # 1536 +3517,2419,2521,1416,3029, 585, 938,1931,1007,1052,1932,1685,6120,3379,4316,4623, # 1552 + 804, 599,3121,1333,2128,2539,1159,1554,2032,3810, 687,2033,2904, 952, 675,1467, # 1568 +3436,6121,2241,1096,1786,2440,1543,1924, 980,1813,2228, 781,2692,1879, 728,1918, # 1584 +3696,4624, 548,1950,4625,1809,1088,1356,3303,2522,1944, 502, 972, 373, 513,2827, # 1600 + 586,2377,2391,1003,1976,1631,6122,2464,1084, 648,1776,4626,2141, 324, 962,2012, # 1616 +2177,2076,1384, 742,2178,1448,1173,1810, 222, 102, 301, 445, 125,2420, 662,2498, # 1632 + 277, 200,1476,1165,1068, 224,2562,1378,1446, 450,1880, 659, 791, 582,4627,2939, # 1648 +3936,1516,1274, 555,2099,3697,1020,1389,1526,3380,1762,1723,1787,2229, 412,2114, # 1664 +1900,2392,3518, 512,2597, 427,1925,2341,3122,1653,1686,2465,2499, 697, 330, 273, # 1680 + 380,2162, 951, 832, 780, 991,1301,3073, 965,2270,3519, 668,2523,2636,1286, 535, # 1696 +1407, 518, 671, 957,2658,2378, 267, 611,2197,3030,6123, 248,2299, 967,1799,2356, # 1712 + 850,1418,3437,1876,1256,1480,2828,1718,6124,6125,1755,1664,2405,6126,4628,2879, # 1728 +2829, 499,2179, 676,4629, 557,2329,2214,2090, 325,3234, 464, 811,3001, 992,2342, # 1744 +2481,1232,1469, 303,2242, 466,1070,2163, 603,1777,2091,4630,2752,4631,2714, 322, # 1760 +2659,1964,1768, 481,2188,1463,2330,2857,3600,2092,3031,2421,4632,2318,2070,1849, # 1776 +2598,4633,1302,2254,1668,1701,2422,3811,2905,3032,3123,2046,4106,1763,1694,4634, # 1792 +1604, 943,1724,1454, 917, 868,2215,1169,2940, 552,1145,1800,1228,1823,1955, 316, # 1808 +1080,2510, 361,1807,2830,4107,2660,3381,1346,1423,1134,4108,6127, 541,1263,1229, # 1824 +1148,2540, 545, 465,1833,2880,3438,1901,3074,2482, 816,3937, 713,1788,2500, 122, # 1840 +1575, 195,1451,2501,1111,6128, 859, 374,1225,2243,2483,4317, 390,1033,3439,3075, # 1856 +2524,1687, 266, 793,1440,2599, 946, 779, 802, 507, 897,1081, 528,2189,1292, 711, # 1872 +1866,1725,1167,1640, 753, 398,2661,1053, 246, 348,4318, 137,1024,3440,1600,2077, # 1888 +2129, 825,4319, 698, 238, 521, 187,2300,1157,2423,1641,1605,1464,1610,1097,2541, # 1904 +1260,1436, 759,2255,1814,2150, 705,3235, 409,2563,3304, 561,3033,2005,2564, 726, # 1920 +1956,2343,3698,4109, 949,3812,3813,3520,1669, 653,1379,2525, 881,2198, 632,2256, # 1936 +1027, 778,1074, 733,1957, 514,1481,2466, 554,2180, 702,3938,1606,1017,1398,6129, # 1952 +1380,3521, 921, 993,1313, 594, 449,1489,1617,1166, 768,1426,1360, 495,1794,3601, # 1968 +1177,3602,1170,4320,2344, 476, 425,3167,4635,3168,1424, 401,2662,1171,3382,1998, # 1984 +1089,4110, 477,3169, 474,6130,1909, 596,2831,1842, 494, 693,1051,1028,1207,3076, # 2000 + 606,2115, 727,2790,1473,1115, 743,3522, 630, 805,1532,4321,2021, 366,1057, 838, # 2016 + 684,1114,2142,4322,2050,1492,1892,1808,2271,3814,2424,1971,1447,1373,3305,1090, # 2032 +1536,3939,3523,3306,1455,2199, 336, 369,2331,1035, 584,2393, 902, 718,2600,6131, # 2048 +2753, 463,2151,1149,1611,2467, 715,1308,3124,1268, 343,1413,3236,1517,1347,2663, # 2064 +2093,3940,2022,1131,1553,2100,2941,1427,3441,2942,1323,2484,6132,1980, 872,2368, # 2080 +2441,2943, 320,2369,2116,1082, 679,1933,3941,2791,3815, 625,1143,2023, 422,2200, # 2096 +3816,6133, 730,1695, 356,2257,1626,2301,2858,2637,1627,1778, 937, 883,2906,2693, # 2112 +3002,1769,1086, 400,1063,1325,3307,2792,4111,3077, 456,2345,1046, 747,6134,1524, # 2128 + 884,1094,3383,1474,2164,1059, 974,1688,2181,2258,1047, 345,1665,1187, 358, 875, # 2144 +3170, 305, 660,3524,2190,1334,1135,3171,1540,1649,2542,1527, 927, 968,2793, 885, # 2160 +1972,1850, 482, 500,2638,1218,1109,1085,2543,1654,2034, 876, 78,2287,1482,1277, # 2176 + 861,1675,1083,1779, 724,2754, 454, 397,1132,1612,2332, 893, 672,1237, 257,2259, # 2192 +2370, 135,3384, 337,2244, 547, 352, 340, 709,2485,1400, 788,1138,2511, 540, 772, # 2208 +1682,2260,2272,2544,2013,1843,1902,4636,1999,1562,2288,4637,2201,1403,1533, 407, # 2224 + 576,3308,1254,2071, 978,3385, 170, 136,1201,3125,2664,3172,2394, 213, 912, 873, # 2240 +3603,1713,2202, 699,3604,3699, 813,3442, 493, 531,1054, 468,2907,1483, 304, 281, # 2256 +4112,1726,1252,2094, 339,2319,2130,2639, 756,1563,2944, 748, 571,2976,1588,2425, # 2272 +2715,1851,1460,2426,1528,1392,1973,3237, 288,3309, 685,3386, 296, 892,2716,2216, # 2288 +1570,2245, 722,1747,2217, 905,3238,1103,6135,1893,1441,1965, 251,1805,2371,3700, # 2304 +2601,1919,1078, 75,2182,1509,1592,1270,2640,4638,2152,6136,3310,3817, 524, 706, # 2320 +1075, 292,3818,1756,2602, 317, 98,3173,3605,3525,1844,2218,3819,2502, 814, 567, # 2336 + 385,2908,1534,6137, 534,1642,3239, 797,6138,1670,1529, 953,4323, 188,1071, 538, # 2352 + 178, 729,3240,2109,1226,1374,2000,2357,2977, 731,2468,1116,2014,2051,6139,1261, # 2368 +1593, 803,2859,2736,3443, 556, 682, 823,1541,6140,1369,2289,1706,2794, 845, 462, # 2384 +2603,2665,1361, 387, 162,2358,1740, 739,1770,1720,1304,1401,3241,1049, 627,1571, # 2400 +2427,3526,1877,3942,1852,1500, 431,1910,1503, 677, 297,2795, 286,1433,1038,1198, # 2416 +2290,1133,1596,4113,4639,2469,1510,1484,3943,6141,2442, 108, 712,4640,2372, 866, # 2432 +3701,2755,3242,1348, 834,1945,1408,3527,2395,3243,1811, 824, 994,1179,2110,1548, # 2448 +1453, 790,3003, 690,4324,4325,2832,2909,3820,1860,3821, 225,1748, 310, 346,1780, # 2464 +2470, 821,1993,2717,2796, 828, 877,3528,2860,2471,1702,2165,2910,2486,1789, 453, # 2480 + 359,2291,1676, 73,1164,1461,1127,3311, 421, 604, 314,1037, 589, 116,2487, 737, # 2496 + 837,1180, 111, 244, 735,6142,2261,1861,1362, 986, 523, 418, 581,2666,3822, 103, # 2512 + 855, 503,1414,1867,2488,1091, 657,1597, 979, 605,1316,4641,1021,2443,2078,2001, # 2528 +1209, 96, 587,2166,1032, 260,1072,2153, 173, 94, 226,3244, 819,2006,4642,4114, # 2544 +2203, 231,1744, 782, 97,2667, 786,3387, 887, 391, 442,2219,4326,1425,6143,2694, # 2560 + 633,1544,1202, 483,2015, 592,2052,1958,2472,1655, 419, 129,4327,3444,3312,1714, # 2576 +1257,3078,4328,1518,1098, 865,1310,1019,1885,1512,1734, 469,2444, 148, 773, 436, # 2592 +1815,1868,1128,1055,4329,1245,2756,3445,2154,1934,1039,4643, 579,1238, 932,2320, # 2608 + 353, 205, 801, 115,2428, 944,2321,1881, 399,2565,1211, 678, 766,3944, 335,2101, # 2624 +1459,1781,1402,3945,2737,2131,1010, 844, 981,1326,1013, 550,1816,1545,2620,1335, # 2640 +1008, 371,2881, 936,1419,1613,3529,1456,1395,2273,1834,2604,1317,2738,2503, 416, # 2656 +1643,4330, 806,1126, 229, 591,3946,1314,1981,1576,1837,1666, 347,1790, 977,3313, # 2672 + 764,2861,1853, 688,2429,1920,1462, 77, 595, 415,2002,3034, 798,1192,4115,6144, # 2688 +2978,4331,3035,2695,2582,2072,2566, 430,2430,1727, 842,1396,3947,3702, 613, 377, # 2704 + 278, 236,1417,3388,3314,3174, 757,1869, 107,3530,6145,1194, 623,2262, 207,1253, # 2720 +2167,3446,3948, 492,1117,1935, 536,1838,2757,1246,4332, 696,2095,2406,1393,1572, # 2736 +3175,1782, 583, 190, 253,1390,2230, 830,3126,3389, 934,3245,1703,1749,2979,1870, # 2752 +2545,1656,2204, 869,2346,4116,3176,1817, 496,1764,4644, 942,1504, 404,1903,1122, # 2768 +1580,3606,2945,1022, 515, 372,1735, 955,2431,3036,6146,2797,1110,2302,2798, 617, # 2784 +6147, 441, 762,1771,3447,3607,3608,1904, 840,3037, 86, 939,1385, 572,1370,2445, # 2800 +1336, 114,3703, 898, 294, 203,3315, 703,1583,2274, 429, 961,4333,1854,1951,3390, # 2816 +2373,3704,4334,1318,1381, 966,1911,2322,1006,1155, 309, 989, 458,2718,1795,1372, # 2832 +1203, 252,1689,1363,3177, 517,1936, 168,1490, 562, 193,3823,1042,4117,1835, 551, # 2848 + 470,4645, 395, 489,3448,1871,1465,2583,2641, 417,1493, 279,1295, 511,1236,1119, # 2864 + 72,1231,1982,1812,3004, 871,1564, 984,3449,1667,2696,2096,4646,2347,2833,1673, # 2880 +3609, 695,3246,2668, 807,1183,4647, 890, 388,2333,1801,1457,2911,1765,1477,1031, # 2896 +3316,3317,1278,3391,2799,2292,2526, 163,3450,4335,2669,1404,1802,6148,2323,2407, # 2912 +1584,1728,1494,1824,1269, 298, 909,3318,1034,1632, 375, 776,1683,2061, 291, 210, # 2928 +1123, 809,1249,1002,2642,3038, 206,1011,2132, 144, 975, 882,1565, 342, 667, 754, # 2944 +1442,2143,1299,2303,2062, 447, 626,2205,1221,2739,2912,1144,1214,2206,2584, 760, # 2960 +1715, 614, 950,1281,2670,2621, 810, 577,1287,2546,4648, 242,2168, 250,2643, 691, # 2976 + 123,2644, 647, 313,1029, 689,1357,2946,1650, 216, 771,1339,1306, 808,2063, 549, # 2992 + 913,1371,2913,2914,6149,1466,1092,1174,1196,1311,2605,2396,1783,1796,3079, 406, # 3008 +2671,2117,3949,4649, 487,1825,2220,6150,2915, 448,2348,1073,6151,2397,1707, 130, # 3024 + 900,1598, 329, 176,1959,2527,1620,6152,2275,4336,3319,1983,2191,3705,3610,2155, # 3040 +3706,1912,1513,1614,6153,1988, 646, 392,2304,1589,3320,3039,1826,1239,1352,1340, # 3056 +2916, 505,2567,1709,1437,2408,2547, 906,6154,2672, 384,1458,1594,1100,1329, 710, # 3072 + 423,3531,2064,2231,2622,1989,2673,1087,1882, 333, 841,3005,1296,2882,2379, 580, # 3088 +1937,1827,1293,2585, 601, 574, 249,1772,4118,2079,1120, 645, 901,1176,1690, 795, # 3104 +2207, 478,1434, 516,1190,1530, 761,2080, 930,1264, 355, 435,1552, 644,1791, 987, # 3120 + 220,1364,1163,1121,1538, 306,2169,1327,1222, 546,2645, 218, 241, 610,1704,3321, # 3136 +1984,1839,1966,2528, 451,6155,2586,3707,2568, 907,3178, 254,2947, 186,1845,4650, # 3152 + 745, 432,1757, 428,1633, 888,2246,2221,2489,3611,2118,1258,1265, 956,3127,1784, # 3168 +4337,2490, 319, 510, 119, 457,3612, 274,2035,2007,4651,1409,3128, 970,2758, 590, # 3184 +2800, 661,2247,4652,2008,3950,1420,1549,3080,3322,3951,1651,1375,2111, 485,2491, # 3200 +1429,1156,6156,2548,2183,1495, 831,1840,2529,2446, 501,1657, 307,1894,3247,1341, # 3216 + 666, 899,2156,1539,2549,1559, 886, 349,2208,3081,2305,1736,3824,2170,2759,1014, # 3232 +1913,1386, 542,1397,2948, 490, 368, 716, 362, 159, 282,2569,1129,1658,1288,1750, # 3248 +2674, 276, 649,2016, 751,1496, 658,1818,1284,1862,2209,2087,2512,3451, 622,2834, # 3264 + 376, 117,1060,2053,1208,1721,1101,1443, 247,1250,3179,1792,3952,2760,2398,3953, # 3280 +6157,2144,3708, 446,2432,1151,2570,3452,2447,2761,2835,1210,2448,3082, 424,2222, # 3296 +1251,2449,2119,2836, 504,1581,4338, 602, 817, 857,3825,2349,2306, 357,3826,1470, # 3312 +1883,2883, 255, 958, 929,2917,3248, 302,4653,1050,1271,1751,2307,1952,1430,2697, # 3328 +2719,2359, 354,3180, 777, 158,2036,4339,1659,4340,4654,2308,2949,2248,1146,2232, # 3344 +3532,2720,1696,2623,3827,6158,3129,1550,2698,1485,1297,1428, 637, 931,2721,2145, # 3360 + 914,2550,2587, 81,2450, 612, 827,2646,1242,4655,1118,2884, 472,1855,3181,3533, # 3376 +3534, 569,1353,2699,1244,1758,2588,4119,2009,2762,2171,3709,1312,1531,6159,1152, # 3392 +1938, 134,1830, 471,3710,2276,1112,1535,3323,3453,3535, 982,1337,2950, 488, 826, # 3408 + 674,1058,1628,4120,2017, 522,2399, 211, 568,1367,3454, 350, 293,1872,1139,3249, # 3424 +1399,1946,3006,1300,2360,3324, 588, 736,6160,2606, 744, 669,3536,3828,6161,1358, # 3440 + 199, 723, 848, 933, 851,1939,1505,1514,1338,1618,1831,4656,1634,3613, 443,2740, # 3456 +3829, 717,1947, 491,1914,6162,2551,1542,4121,1025,6163,1099,1223, 198,3040,2722, # 3472 + 370, 410,1905,2589, 998,1248,3182,2380, 519,1449,4122,1710, 947, 928,1153,4341, # 3488 +2277, 344,2624,1511, 615, 105, 161,1212,1076,1960,3130,2054,1926,1175,1906,2473, # 3504 + 414,1873,2801,6164,2309, 315,1319,3325, 318,2018,2146,2157, 963, 631, 223,4342, # 3520 +4343,2675, 479,3711,1197,2625,3712,2676,2361,6165,4344,4123,6166,2451,3183,1886, # 3536 +2184,1674,1330,1711,1635,1506, 799, 219,3250,3083,3954,1677,3713,3326,2081,3614, # 3552 +1652,2073,4657,1147,3041,1752, 643,1961, 147,1974,3955,6167,1716,2037, 918,3007, # 3568 +1994, 120,1537, 118, 609,3184,4345, 740,3455,1219, 332,1615,3830,6168,1621,2980, # 3584 +1582, 783, 212, 553,2350,3714,1349,2433,2082,4124, 889,6169,2310,1275,1410, 973, # 3600 + 166,1320,3456,1797,1215,3185,2885,1846,2590,2763,4658, 629, 822,3008, 763, 940, # 3616 +1990,2862, 439,2409,1566,1240,1622, 926,1282,1907,2764, 654,2210,1607, 327,1130, # 3632 +3956,1678,1623,6170,2434,2192, 686, 608,3831,3715, 903,3957,3042,6171,2741,1522, # 3648 +1915,1105,1555,2552,1359, 323,3251,4346,3457, 738,1354,2553,2311,2334,1828,2003, # 3664 +3832,1753,2351,1227,6172,1887,4125,1478,6173,2410,1874,1712,1847, 520,1204,2607, # 3680 + 264,4659, 836,2677,2102, 600,4660,3833,2278,3084,6174,4347,3615,1342, 640, 532, # 3696 + 543,2608,1888,2400,2591,1009,4348,1497, 341,1737,3616,2723,1394, 529,3252,1321, # 3712 + 983,4661,1515,2120, 971,2592, 924, 287,1662,3186,4349,2700,4350,1519, 908,1948, # 3728 +2452, 156, 796,1629,1486,2223,2055, 694,4126,1259,1036,3392,1213,2249,2742,1889, # 3744 +1230,3958,1015, 910, 408, 559,3617,4662, 746, 725, 935,4663,3959,3009,1289, 563, # 3760 + 867,4664,3960,1567,2981,2038,2626, 988,2263,2381,4351, 143,2374, 704,1895,6175, # 3776 +1188,3716,2088, 673,3085,2362,4352, 484,1608,1921,2765,2918, 215, 904,3618,3537, # 3792 + 894, 509, 976,3043,2701,3961,4353,2837,2982, 498,6176,6177,1102,3538,1332,3393, # 3808 +1487,1636,1637, 233, 245,3962, 383, 650, 995,3044, 460,1520,1206,2352, 749,3327, # 3824 + 530, 700, 389,1438,1560,1773,3963,2264, 719,2951,2724,3834, 870,1832,1644,1000, # 3840 + 839,2474,3717, 197,1630,3394, 365,2886,3964,1285,2133, 734, 922, 818,1106, 732, # 3856 + 480,2083,1774,3458, 923,2279,1350, 221,3086, 85,2233,2234,3835,1585,3010,2147, # 3872 +1387,1705,2382,1619,2475, 133, 239,2802,1991,1016,2084,2383, 411,2838,1113, 651, # 3888 +1985,1160,3328, 990,1863,3087,1048,1276,2647, 265,2627,1599,3253,2056, 150, 638, # 3904 +2019, 656, 853, 326,1479, 680,1439,4354,1001,1759, 413,3459,3395,2492,1431, 459, # 3920 +4355,1125,3329,2265,1953,1450,2065,2863, 849, 351,2678,3131,3254,3255,1104,1577, # 3936 + 227,1351,1645,2453,2193,1421,2887, 812,2121, 634, 95,2435, 201,2312,4665,1646, # 3952 +1671,2743,1601,2554,2702,2648,2280,1315,1366,2089,3132,1573,3718,3965,1729,1189, # 3968 + 328,2679,1077,1940,1136, 558,1283, 964,1195, 621,2074,1199,1743,3460,3619,1896, # 3984 +1916,1890,3836,2952,1154,2112,1064, 862, 378,3011,2066,2113,2803,1568,2839,6178, # 4000 +3088,2919,1941,1660,2004,1992,2194, 142, 707,1590,1708,1624,1922,1023,1836,1233, # 4016 +1004,2313, 789, 741,3620,6179,1609,2411,1200,4127,3719,3720,4666,2057,3721, 593, # 4032 +2840, 367,2920,1878,6180,3461,1521, 628,1168, 692,2211,2649, 300, 720,2067,2571, # 4048 +2953,3396, 959,2504,3966,3539,3462,1977, 701,6181, 954,1043, 800, 681, 183,3722, # 4064 +1803,1730,3540,4128,2103, 815,2314, 174, 467, 230,2454,1093,2134, 755,3541,3397, # 4080 +1141,1162,6182,1738,2039, 270,3256,2513,1005,1647,2185,3837, 858,1679,1897,1719, # 4096 +2954,2324,1806, 402, 670, 167,4129,1498,2158,2104, 750,6183, 915, 189,1680,1551, # 4112 + 455,4356,1501,2455, 405,1095,2955, 338,1586,1266,1819, 570, 641,1324, 237,1556, # 4128 +2650,1388,3723,6184,1368,2384,1343,1978,3089,2436, 879,3724, 792,1191, 758,3012, # 4144 +1411,2135,1322,4357, 240,4667,1848,3725,1574,6185, 420,3045,1546,1391, 714,4358, # 4160 +1967, 941,1864, 863, 664, 426, 560,1731,2680,1785,2864,1949,2363, 403,3330,1415, # 4176 +1279,2136,1697,2335, 204, 721,2097,3838, 90,6186,2085,2505, 191,3967, 124,2148, # 4192 +1376,1798,1178,1107,1898,1405, 860,4359,1243,1272,2375,2983,1558,2456,1638, 113, # 4208 +3621, 578,1923,2609, 880, 386,4130, 784,2186,2266,1422,2956,2172,1722, 497, 263, # 4224 +2514,1267,2412,2610, 177,2703,3542, 774,1927,1344, 616,1432,1595,1018, 172,4360, # 4240 +2325, 911,4361, 438,1468,3622, 794,3968,2024,2173,1681,1829,2957, 945, 895,3090, # 4256 + 575,2212,2476, 475,2401,2681, 785,2744,1745,2293,2555,1975,3133,2865, 394,4668, # 4272 +3839, 635,4131, 639, 202,1507,2195,2766,1345,1435,2572,3726,1908,1184,1181,2457, # 4288 +3727,3134,4362, 843,2611, 437, 916,4669, 234, 769,1884,3046,3047,3623, 833,6187, # 4304 +1639,2250,2402,1355,1185,2010,2047, 999, 525,1732,1290,1488,2612, 948,1578,3728, # 4320 +2413,2477,1216,2725,2159, 334,3840,1328,3624,2921,1525,4132, 564,1056, 891,4363, # 4336 +1444,1698,2385,2251,3729,1365,2281,2235,1717,6188, 864,3841,2515, 444, 527,2767, # 4352 +2922,3625, 544, 461,6189, 566, 209,2437,3398,2098,1065,2068,3331,3626,3257,2137, # 4368 #last 512 +) + + diff --git a/venv2/lib/python2.7/site-packages/pip/_vendor/chardet/jpcntx.py b/venv2/lib/python2.7/site-packages/pip/_vendor/chardet/jpcntx.py new file mode 100644 index 0000000..20044e4 --- /dev/null +++ b/venv2/lib/python2.7/site-packages/pip/_vendor/chardet/jpcntx.py @@ -0,0 +1,233 @@ +######################## BEGIN LICENSE BLOCK ######################## +# The Original Code is Mozilla Communicator client code. +# +# The Initial Developer of the Original Code is +# Netscape Communications Corporation. +# Portions created by the Initial Developer are Copyright (C) 1998 +# the Initial Developer. All Rights Reserved. +# +# Contributor(s): +# Mark Pilgrim - port to Python +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA +# 02110-1301 USA +######################### END LICENSE BLOCK ######################### + + +# This is hiragana 2-char sequence table, the number in each cell represents its frequency category +jp2CharContext = ( +(0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,1,0,0,0,0,0,0,0,0,0,0,1), +(2,4,0,4,0,3,0,4,0,3,4,4,4,2,4,3,3,4,3,2,3,3,4,2,3,3,3,2,4,1,4,3,3,1,5,4,3,4,3,4,3,5,3,0,3,5,4,2,0,3,1,0,3,3,0,3,3,0,1,1,0,4,3,0,3,3,0,4,0,2,0,3,5,5,5,5,4,0,4,1,0,3,4), +(0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2), +(0,4,0,5,0,5,0,4,0,4,5,4,4,3,5,3,5,1,5,3,4,3,4,4,3,4,3,3,4,3,5,4,4,3,5,5,3,5,5,5,3,5,5,3,4,5,5,3,1,3,2,0,3,4,0,4,2,0,4,2,1,5,3,2,3,5,0,4,0,2,0,5,4,4,5,4,5,0,4,0,0,4,4), +(0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0), +(0,3,0,4,0,3,0,3,0,4,5,4,3,3,3,3,4,3,5,4,4,3,5,4,4,3,4,3,4,4,4,4,5,3,4,4,3,4,5,5,4,5,5,1,4,5,4,3,0,3,3,1,3,3,0,4,4,0,3,3,1,5,3,3,3,5,0,4,0,3,0,4,4,3,4,3,3,0,4,1,1,3,4), +(0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0), +(0,4,0,3,0,3,0,4,0,3,4,4,3,2,2,1,2,1,3,1,3,3,3,3,3,4,3,1,3,3,5,3,3,0,4,3,0,5,4,3,3,5,4,4,3,4,4,5,0,1,2,0,1,2,0,2,2,0,1,0,0,5,2,2,1,4,0,3,0,1,0,4,4,3,5,4,3,0,2,1,0,4,3), +(0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0), +(0,3,0,5,0,4,0,2,1,4,4,2,4,1,4,2,4,2,4,3,3,3,4,3,3,3,3,1,4,2,3,3,3,1,4,4,1,1,1,4,3,3,2,0,2,4,3,2,0,3,3,0,3,1,1,0,0,0,3,3,0,4,2,2,3,4,0,4,0,3,0,4,4,5,3,4,4,0,3,0,0,1,4), +(1,4,0,4,0,4,0,4,0,3,5,4,4,3,4,3,5,4,3,3,4,3,5,4,4,4,4,3,4,2,4,3,3,1,5,4,3,2,4,5,4,5,5,4,4,5,4,4,0,3,2,2,3,3,0,4,3,1,3,2,1,4,3,3,4,5,0,3,0,2,0,4,5,5,4,5,4,0,4,0,0,5,4), +(0,5,0,5,0,4,0,3,0,4,4,3,4,3,3,3,4,0,4,4,4,3,4,3,4,3,3,1,4,2,4,3,4,0,5,4,1,4,5,4,4,5,3,2,4,3,4,3,2,4,1,3,3,3,2,3,2,0,4,3,3,4,3,3,3,4,0,4,0,3,0,4,5,4,4,4,3,0,4,1,0,1,3), +(0,3,1,4,0,3,0,2,0,3,4,4,3,1,4,2,3,3,4,3,4,3,4,3,4,4,3,2,3,1,5,4,4,1,4,4,3,5,4,4,3,5,5,4,3,4,4,3,1,2,3,1,2,2,0,3,2,0,3,1,0,5,3,3,3,4,3,3,3,3,4,4,4,4,5,4,2,0,3,3,2,4,3), +(0,2,0,3,0,1,0,1,0,0,3,2,0,0,2,0,1,0,2,1,3,3,3,1,2,3,1,0,1,0,4,2,1,1,3,3,0,4,3,3,1,4,3,3,0,3,3,2,0,0,0,0,1,0,0,2,0,0,0,0,0,4,1,0,2,3,2,2,2,1,3,3,3,4,4,3,2,0,3,1,0,3,3), +(0,4,0,4,0,3,0,3,0,4,4,4,3,3,3,3,3,3,4,3,4,2,4,3,4,3,3,2,4,3,4,5,4,1,4,5,3,5,4,5,3,5,4,0,3,5,5,3,1,3,3,2,2,3,0,3,4,1,3,3,2,4,3,3,3,4,0,4,0,3,0,4,5,4,4,5,3,0,4,1,0,3,4), +(0,2,0,3,0,3,0,0,0,2,2,2,1,0,1,0,0,0,3,0,3,0,3,0,1,3,1,0,3,1,3,3,3,1,3,3,3,0,1,3,1,3,4,0,0,3,1,1,0,3,2,0,0,0,0,1,3,0,1,0,0,3,3,2,0,3,0,0,0,0,0,3,4,3,4,3,3,0,3,0,0,2,3), +(2,3,0,3,0,2,0,1,0,3,3,4,3,1,3,1,1,1,3,1,4,3,4,3,3,3,0,0,3,1,5,4,3,1,4,3,2,5,5,4,4,4,4,3,3,4,4,4,0,2,1,1,3,2,0,1,2,0,0,1,0,4,1,3,3,3,0,3,0,1,0,4,4,4,5,5,3,0,2,0,0,4,4), +(0,2,0,1,0,3,1,3,0,2,3,3,3,0,3,1,0,0,3,0,3,2,3,1,3,2,1,1,0,0,4,2,1,0,2,3,1,4,3,2,0,4,4,3,1,3,1,3,0,1,0,0,1,0,0,0,1,0,0,0,0,4,1,1,1,2,0,3,0,0,0,3,4,2,4,3,2,0,1,0,0,3,3), +(0,1,0,4,0,5,0,4,0,2,4,4,2,3,3,2,3,3,5,3,3,3,4,3,4,2,3,0,4,3,3,3,4,1,4,3,2,1,5,5,3,4,5,1,3,5,4,2,0,3,3,0,1,3,0,4,2,0,1,3,1,4,3,3,3,3,0,3,0,1,0,3,4,4,4,5,5,0,3,0,1,4,5), +(0,2,0,3,0,3,0,0,0,2,3,1,3,0,4,0,1,1,3,0,3,4,3,2,3,1,0,3,3,2,3,1,3,0,2,3,0,2,1,4,1,2,2,0,0,3,3,0,0,2,0,0,0,1,0,0,0,0,2,2,0,3,2,1,3,3,0,2,0,2,0,0,3,3,1,2,4,0,3,0,2,2,3), +(2,4,0,5,0,4,0,4,0,2,4,4,4,3,4,3,3,3,1,2,4,3,4,3,4,4,5,0,3,3,3,3,2,0,4,3,1,4,3,4,1,4,4,3,3,4,4,3,1,2,3,0,4,2,0,4,1,0,3,3,0,4,3,3,3,4,0,4,0,2,0,3,5,3,4,5,2,0,3,0,0,4,5), +(0,3,0,4,0,1,0,1,0,1,3,2,2,1,3,0,3,0,2,0,2,0,3,0,2,0,0,0,1,0,1,1,0,0,3,1,0,0,0,4,0,3,1,0,2,1,3,0,0,0,0,0,0,3,0,0,0,0,0,0,0,4,2,2,3,1,0,3,0,0,0,1,4,4,4,3,0,0,4,0,0,1,4), +(1,4,1,5,0,3,0,3,0,4,5,4,4,3,5,3,3,4,4,3,4,1,3,3,3,3,2,1,4,1,5,4,3,1,4,4,3,5,4,4,3,5,4,3,3,4,4,4,0,3,3,1,2,3,0,3,1,0,3,3,0,5,4,4,4,4,4,4,3,3,5,4,4,3,3,5,4,0,3,2,0,4,4), +(0,2,0,3,0,1,0,0,0,1,3,3,3,2,4,1,3,0,3,1,3,0,2,2,1,1,0,0,2,0,4,3,1,0,4,3,0,4,4,4,1,4,3,1,1,3,3,1,0,2,0,0,1,3,0,0,0,0,2,0,0,4,3,2,4,3,5,4,3,3,3,4,3,3,4,3,3,0,2,1,0,3,3), +(0,2,0,4,0,3,0,2,0,2,5,5,3,4,4,4,4,1,4,3,3,0,4,3,4,3,1,3,3,2,4,3,0,3,4,3,0,3,4,4,2,4,4,0,4,5,3,3,2,2,1,1,1,2,0,1,5,0,3,3,2,4,3,3,3,4,0,3,0,2,0,4,4,3,5,5,0,0,3,0,2,3,3), +(0,3,0,4,0,3,0,1,0,3,4,3,3,1,3,3,3,0,3,1,3,0,4,3,3,1,1,0,3,0,3,3,0,0,4,4,0,1,5,4,3,3,5,0,3,3,4,3,0,2,0,1,1,1,0,1,3,0,1,2,1,3,3,2,3,3,0,3,0,1,0,1,3,3,4,4,1,0,1,2,2,1,3), +(0,1,0,4,0,4,0,3,0,1,3,3,3,2,3,1,1,0,3,0,3,3,4,3,2,4,2,0,1,0,4,3,2,0,4,3,0,5,3,3,2,4,4,4,3,3,3,4,0,1,3,0,0,1,0,0,1,0,0,0,0,4,2,3,3,3,0,3,0,0,0,4,4,4,5,3,2,0,3,3,0,3,5), +(0,2,0,3,0,0,0,3,0,1,3,0,2,0,0,0,1,0,3,1,1,3,3,0,0,3,0,0,3,0,2,3,1,0,3,1,0,3,3,2,0,4,2,2,0,2,0,0,0,4,0,0,0,0,0,0,0,0,0,0,0,2,1,2,0,1,0,1,0,0,0,1,3,1,2,0,0,0,1,0,0,1,4), +(0,3,0,3,0,5,0,1,0,2,4,3,1,3,3,2,1,1,5,2,1,0,5,1,2,0,0,0,3,3,2,2,3,2,4,3,0,0,3,3,1,3,3,0,2,5,3,4,0,3,3,0,1,2,0,2,2,0,3,2,0,2,2,3,3,3,0,2,0,1,0,3,4,4,2,5,4,0,3,0,0,3,5), +(0,3,0,3,0,3,0,1,0,3,3,3,3,0,3,0,2,0,2,1,1,0,2,0,1,0,0,0,2,1,0,0,1,0,3,2,0,0,3,3,1,2,3,1,0,3,3,0,0,1,0,0,0,0,0,2,0,0,0,0,0,2,3,1,2,3,0,3,0,1,0,3,2,1,0,4,3,0,1,1,0,3,3), +(0,4,0,5,0,3,0,3,0,4,5,5,4,3,5,3,4,3,5,3,3,2,5,3,4,4,4,3,4,3,4,5,5,3,4,4,3,4,4,5,4,4,4,3,4,5,5,4,2,3,4,2,3,4,0,3,3,1,4,3,2,4,3,3,5,5,0,3,0,3,0,5,5,5,5,4,4,0,4,0,1,4,4), +(0,4,0,4,0,3,0,3,0,3,5,4,4,2,3,2,5,1,3,2,5,1,4,2,3,2,3,3,4,3,3,3,3,2,5,4,1,3,3,5,3,4,4,0,4,4,3,1,1,3,1,0,2,3,0,2,3,0,3,0,0,4,3,1,3,4,0,3,0,2,0,4,4,4,3,4,5,0,4,0,0,3,4), +(0,3,0,3,0,3,1,2,0,3,4,4,3,3,3,0,2,2,4,3,3,1,3,3,3,1,1,0,3,1,4,3,2,3,4,4,2,4,4,4,3,4,4,3,2,4,4,3,1,3,3,1,3,3,0,4,1,0,2,2,1,4,3,2,3,3,5,4,3,3,5,4,4,3,3,0,4,0,3,2,2,4,4), +(0,2,0,1,0,0,0,0,0,1,2,1,3,0,0,0,0,0,2,0,1,2,1,0,0,1,0,0,0,0,3,0,0,1,0,1,1,3,1,0,0,0,1,1,0,1,1,0,0,0,0,0,2,0,0,0,0,0,0,0,0,1,1,2,2,0,3,4,0,0,0,1,1,0,0,1,0,0,0,0,0,1,1), +(0,1,0,0,0,1,0,0,0,0,4,0,4,1,4,0,3,0,4,0,3,0,4,0,3,0,3,0,4,1,5,1,4,0,0,3,0,5,0,5,2,0,1,0,0,0,2,1,4,0,1,3,0,0,3,0,0,3,1,1,4,1,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0), +(1,4,0,5,0,3,0,2,0,3,5,4,4,3,4,3,5,3,4,3,3,0,4,3,3,3,3,3,3,2,4,4,3,1,3,4,4,5,4,4,3,4,4,1,3,5,4,3,3,3,1,2,2,3,3,1,3,1,3,3,3,5,3,3,4,5,0,3,0,3,0,3,4,3,4,4,3,0,3,0,2,4,3), +(0,1,0,4,0,0,0,0,0,1,4,0,4,1,4,2,4,0,3,0,1,0,1,0,0,0,0,0,2,0,3,1,1,1,0,3,0,0,0,1,2,1,0,0,1,1,1,1,0,1,0,0,0,1,0,0,3,0,0,0,0,3,2,0,2,2,0,1,0,0,0,2,3,2,3,3,0,0,0,0,2,1,0), +(0,5,1,5,0,3,0,3,0,5,4,4,5,1,5,3,3,0,4,3,4,3,5,3,4,3,3,2,4,3,4,3,3,0,3,3,1,4,4,3,4,4,4,3,4,5,5,3,2,3,1,1,3,3,1,3,1,1,3,3,2,4,5,3,3,5,0,4,0,3,0,4,4,3,5,3,3,0,3,4,0,4,3), +(0,5,0,5,0,3,0,2,0,4,4,3,5,2,4,3,3,3,4,4,4,3,5,3,5,3,3,1,4,0,4,3,3,0,3,3,0,4,4,4,4,5,4,3,3,5,5,3,2,3,1,2,3,2,0,1,0,0,3,2,2,4,4,3,1,5,0,4,0,3,0,4,3,1,3,2,1,0,3,3,0,3,3), +(0,4,0,5,0,5,0,4,0,4,5,5,5,3,4,3,3,2,5,4,4,3,5,3,5,3,4,0,4,3,4,4,3,2,4,4,3,4,5,4,4,5,5,0,3,5,5,4,1,3,3,2,3,3,1,3,1,0,4,3,1,4,4,3,4,5,0,4,0,2,0,4,3,4,4,3,3,0,4,0,0,5,5), +(0,4,0,4,0,5,0,1,1,3,3,4,4,3,4,1,3,0,5,1,3,0,3,1,3,1,1,0,3,0,3,3,4,0,4,3,0,4,4,4,3,4,4,0,3,5,4,1,0,3,0,0,2,3,0,3,1,0,3,1,0,3,2,1,3,5,0,3,0,1,0,3,2,3,3,4,4,0,2,2,0,4,4), +(2,4,0,5,0,4,0,3,0,4,5,5,4,3,5,3,5,3,5,3,5,2,5,3,4,3,3,4,3,4,5,3,2,1,5,4,3,2,3,4,5,3,4,1,2,5,4,3,0,3,3,0,3,2,0,2,3,0,4,1,0,3,4,3,3,5,0,3,0,1,0,4,5,5,5,4,3,0,4,2,0,3,5), +(0,5,0,4,0,4,0,2,0,5,4,3,4,3,4,3,3,3,4,3,4,2,5,3,5,3,4,1,4,3,4,4,4,0,3,5,0,4,4,4,4,5,3,1,3,4,5,3,3,3,3,3,3,3,0,2,2,0,3,3,2,4,3,3,3,5,3,4,1,3,3,5,3,2,0,0,0,0,4,3,1,3,3), +(0,1,0,3,0,3,0,1,0,1,3,3,3,2,3,3,3,0,3,0,0,0,3,1,3,0,0,0,2,2,2,3,0,0,3,2,0,1,2,4,1,3,3,0,0,3,3,3,0,1,0,0,2,1,0,0,3,0,3,1,0,3,0,0,1,3,0,2,0,1,0,3,3,1,3,3,0,0,1,1,0,3,3), +(0,2,0,3,0,2,1,4,0,2,2,3,1,1,3,1,1,0,2,0,3,1,2,3,1,3,0,0,1,0,4,3,2,3,3,3,1,4,2,3,3,3,3,1,0,3,1,4,0,1,1,0,1,2,0,1,1,0,1,1,0,3,1,3,2,2,0,1,0,0,0,2,3,3,3,1,0,0,0,0,0,2,3), +(0,5,0,4,0,5,0,2,0,4,5,5,3,3,4,3,3,1,5,4,4,2,4,4,4,3,4,2,4,3,5,5,4,3,3,4,3,3,5,5,4,5,5,1,3,4,5,3,1,4,3,1,3,3,0,3,3,1,4,3,1,4,5,3,3,5,0,4,0,3,0,5,3,3,1,4,3,0,4,0,1,5,3), +(0,5,0,5,0,4,0,2,0,4,4,3,4,3,3,3,3,3,5,4,4,4,4,4,4,5,3,3,5,2,4,4,4,3,4,4,3,3,4,4,5,5,3,3,4,3,4,3,3,4,3,3,3,3,1,2,2,1,4,3,3,5,4,4,3,4,0,4,0,3,0,4,4,4,4,4,1,0,4,2,0,2,4), +(0,4,0,4,0,3,0,1,0,3,5,2,3,0,3,0,2,1,4,2,3,3,4,1,4,3,3,2,4,1,3,3,3,0,3,3,0,0,3,3,3,5,3,3,3,3,3,2,0,2,0,0,2,0,0,2,0,0,1,0,0,3,1,2,2,3,0,3,0,2,0,4,4,3,3,4,1,0,3,0,0,2,4), +(0,0,0,4,0,0,0,0,0,0,1,0,1,0,2,0,0,0,0,0,1,0,2,0,1,0,0,0,0,0,3,1,3,0,3,2,0,0,0,1,0,3,2,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,3,4,0,2,0,0,0,0,0,0,2), +(0,2,1,3,0,2,0,2,0,3,3,3,3,1,3,1,3,3,3,3,3,3,4,2,2,1,2,1,4,0,4,3,1,3,3,3,2,4,3,5,4,3,3,3,3,3,3,3,0,1,3,0,2,0,0,1,0,0,1,0,0,4,2,0,2,3,0,3,3,0,3,3,4,2,3,1,4,0,1,2,0,2,3), +(0,3,0,3,0,1,0,3,0,2,3,3,3,0,3,1,2,0,3,3,2,3,3,2,3,2,3,1,3,0,4,3,2,0,3,3,1,4,3,3,2,3,4,3,1,3,3,1,1,0,1,1,0,1,0,1,0,1,0,0,0,4,1,1,0,3,0,3,1,0,2,3,3,3,3,3,1,0,0,2,0,3,3), +(0,0,0,0,0,0,0,0,0,0,3,0,2,0,3,0,0,0,0,0,0,0,3,0,0,0,0,0,0,0,3,0,3,0,3,1,0,1,0,1,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,3,0,2,0,2,3,0,0,0,0,0,0,0,0,3), +(0,2,0,3,1,3,0,3,0,2,3,3,3,1,3,1,3,1,3,1,3,3,3,1,3,0,2,3,1,1,4,3,3,2,3,3,1,2,2,4,1,3,3,0,1,4,2,3,0,1,3,0,3,0,0,1,3,0,2,0,0,3,3,2,1,3,0,3,0,2,0,3,4,4,4,3,1,0,3,0,0,3,3), +(0,2,0,1,0,2,0,0,0,1,3,2,2,1,3,0,1,1,3,0,3,2,3,1,2,0,2,0,1,1,3,3,3,0,3,3,1,1,2,3,2,3,3,1,2,3,2,0,0,1,0,0,0,0,0,0,3,0,1,0,0,2,1,2,1,3,0,3,0,0,0,3,4,4,4,3,2,0,2,0,0,2,4), +(0,0,0,1,0,1,0,0,0,0,1,0,0,0,1,0,0,0,0,0,0,0,1,1,1,0,0,0,0,0,0,0,0,0,2,2,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,1,3,1,0,0,0,0,0,0,0,3), +(0,3,0,3,0,2,0,3,0,3,3,3,2,3,2,2,2,0,3,1,3,3,3,2,3,3,0,0,3,0,3,2,2,0,2,3,1,4,3,4,3,3,2,3,1,5,4,4,0,3,1,2,1,3,0,3,1,1,2,0,2,3,1,3,1,3,0,3,0,1,0,3,3,4,4,2,1,0,2,1,0,2,4), +(0,1,0,3,0,1,0,2,0,1,4,2,5,1,4,0,2,0,2,1,3,1,4,0,2,1,0,0,2,1,4,1,1,0,3,3,0,5,1,3,2,3,3,1,0,3,2,3,0,1,0,0,0,0,0,0,1,0,0,0,0,4,0,1,0,3,0,2,0,1,0,3,3,3,4,3,3,0,0,0,0,2,3), +(0,0,0,1,0,0,0,0,0,0,2,0,1,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,3,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,1,0,0,1,0,0,0,0,0,3), +(0,1,0,3,0,4,0,3,0,2,4,3,1,0,3,2,2,1,3,1,2,2,3,1,1,1,2,1,3,0,1,2,0,1,3,2,1,3,0,5,5,1,0,0,1,3,2,1,0,3,0,0,1,0,0,0,0,0,3,4,0,1,1,1,3,2,0,2,0,1,0,2,3,3,1,2,3,0,1,0,1,0,4), +(0,0,0,1,0,3,0,3,0,2,2,1,0,0,4,0,3,0,3,1,3,0,3,0,3,0,1,0,3,0,3,1,3,0,3,3,0,0,1,2,1,1,1,0,1,2,0,0,0,1,0,0,1,0,0,0,0,0,0,0,0,2,2,1,2,0,0,2,0,0,0,0,2,3,3,3,3,0,0,0,0,1,4), +(0,0,0,3,0,3,0,0,0,0,3,1,1,0,3,0,1,0,2,0,1,0,0,0,0,0,0,0,1,0,3,0,2,0,2,3,0,0,2,2,3,1,2,0,0,1,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,3,0,0,2,0,0,0,0,2,3), +(2,4,0,5,0,5,0,4,0,3,4,3,3,3,4,3,3,3,4,3,4,4,5,4,5,5,5,2,3,0,5,5,4,1,5,4,3,1,5,4,3,4,4,3,3,4,3,3,0,3,2,0,2,3,0,3,0,0,3,3,0,5,3,2,3,3,0,3,0,3,0,3,4,5,4,5,3,0,4,3,0,3,4), +(0,3,0,3,0,3,0,3,0,3,3,4,3,2,3,2,3,0,4,3,3,3,3,3,3,3,3,0,3,2,4,3,3,1,3,4,3,4,4,4,3,4,4,3,2,4,4,1,0,2,0,0,1,1,0,2,0,0,3,1,0,5,3,2,1,3,0,3,0,1,2,4,3,2,4,3,3,0,3,2,0,4,4), +(0,3,0,3,0,1,0,0,0,1,4,3,3,2,3,1,3,1,4,2,3,2,4,2,3,4,3,0,2,2,3,3,3,0,3,3,3,0,3,4,1,3,3,0,3,4,3,3,0,1,1,0,1,0,0,0,4,0,3,0,0,3,1,2,1,3,0,4,0,1,0,4,3,3,4,3,3,0,2,0,0,3,3), +(0,3,0,4,0,1,0,3,0,3,4,3,3,0,3,3,3,1,3,1,3,3,4,3,3,3,0,0,3,1,5,3,3,1,3,3,2,5,4,3,3,4,5,3,2,5,3,4,0,1,0,0,0,0,0,2,0,0,1,1,0,4,2,2,1,3,0,3,0,2,0,4,4,3,5,3,2,0,1,1,0,3,4), +(0,5,0,4,0,5,0,2,0,4,4,3,3,2,3,3,3,1,4,3,4,1,5,3,4,3,4,0,4,2,4,3,4,1,5,4,0,4,4,4,4,5,4,1,3,5,4,2,1,4,1,1,3,2,0,3,1,0,3,2,1,4,3,3,3,4,0,4,0,3,0,4,4,4,3,3,3,0,4,2,0,3,4), +(1,4,0,4,0,3,0,1,0,3,3,3,1,1,3,3,2,2,3,3,1,0,3,2,2,1,2,0,3,1,2,1,2,0,3,2,0,2,2,3,3,4,3,0,3,3,1,2,0,1,1,3,1,2,0,0,3,0,1,1,0,3,2,2,3,3,0,3,0,0,0,2,3,3,4,3,3,0,1,0,0,1,4), +(0,4,0,4,0,4,0,0,0,3,4,4,3,1,4,2,3,2,3,3,3,1,4,3,4,0,3,0,4,2,3,3,2,2,5,4,2,1,3,4,3,4,3,1,3,3,4,2,0,2,1,0,3,3,0,0,2,0,3,1,0,4,4,3,4,3,0,4,0,1,0,2,4,4,4,4,4,0,3,2,0,3,3), +(0,0,0,1,0,4,0,0,0,0,0,0,1,1,1,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,1,0,3,2,0,0,1,0,0,0,1,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,2), +(0,2,0,3,0,4,0,4,0,1,3,3,3,0,4,0,2,1,2,1,1,1,2,0,3,1,1,0,1,0,3,1,0,0,3,3,2,0,1,1,0,0,0,0,0,1,0,2,0,2,2,0,3,1,0,0,1,0,1,1,0,1,2,0,3,0,0,0,0,1,0,0,3,3,4,3,1,0,1,0,3,0,2), +(0,0,0,3,0,5,0,0,0,0,1,0,2,0,3,1,0,1,3,0,0,0,2,0,0,0,1,0,0,0,1,1,0,0,4,0,0,0,2,3,0,1,4,1,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,3,0,0,0,0,0,1,0,0,0,0,0,0,0,2,0,0,3,0,0,0,0,0,3), +(0,2,0,5,0,5,0,1,0,2,4,3,3,2,5,1,3,2,3,3,3,0,4,1,2,0,3,0,4,0,2,2,1,1,5,3,0,0,1,4,2,3,2,0,3,3,3,2,0,2,4,1,1,2,0,1,1,0,3,1,0,1,3,1,2,3,0,2,0,0,0,1,3,5,4,4,4,0,3,0,0,1,3), +(0,4,0,5,0,4,0,4,0,4,5,4,3,3,4,3,3,3,4,3,4,4,5,3,4,5,4,2,4,2,3,4,3,1,4,4,1,3,5,4,4,5,5,4,4,5,5,5,2,3,3,1,4,3,1,3,3,0,3,3,1,4,3,4,4,4,0,3,0,4,0,3,3,4,4,5,0,0,4,3,0,4,5), +(0,4,0,4,0,3,0,3,0,3,4,4,4,3,3,2,4,3,4,3,4,3,5,3,4,3,2,1,4,2,4,4,3,1,3,4,2,4,5,5,3,4,5,4,1,5,4,3,0,3,2,2,3,2,1,3,1,0,3,3,3,5,3,3,3,5,4,4,2,3,3,4,3,3,3,2,1,0,3,2,1,4,3), +(0,4,0,5,0,4,0,3,0,3,5,5,3,2,4,3,4,0,5,4,4,1,4,4,4,3,3,3,4,3,5,5,2,3,3,4,1,2,5,5,3,5,5,2,3,5,5,4,0,3,2,0,3,3,1,1,5,1,4,1,0,4,3,2,3,5,0,4,0,3,0,5,4,3,4,3,0,0,4,1,0,4,4), +(1,3,0,4,0,2,0,2,0,2,5,5,3,3,3,3,3,0,4,2,3,4,4,4,3,4,0,0,3,4,5,4,3,3,3,3,2,5,5,4,5,5,5,4,3,5,5,5,1,3,1,0,1,0,0,3,2,0,4,2,0,5,2,3,2,4,1,3,0,3,0,4,5,4,5,4,3,0,4,2,0,5,4), +(0,3,0,4,0,5,0,3,0,3,4,4,3,2,3,2,3,3,3,3,3,2,4,3,3,2,2,0,3,3,3,3,3,1,3,3,3,0,4,4,3,4,4,1,1,4,4,2,0,3,1,0,1,1,0,4,1,0,2,3,1,3,3,1,3,4,0,3,0,1,0,3,1,3,0,0,1,0,2,0,0,4,4), +(0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0), +(0,3,0,3,0,2,0,3,0,1,5,4,3,3,3,1,4,2,1,2,3,4,4,2,4,4,5,0,3,1,4,3,4,0,4,3,3,3,2,3,2,5,3,4,3,2,2,3,0,0,3,0,2,1,0,1,2,0,0,0,0,2,1,1,3,1,0,2,0,4,0,3,4,4,4,5,2,0,2,0,0,1,3), +(0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,1,1,1,0,0,1,1,0,0,0,4,2,1,1,0,1,0,3,2,0,0,3,1,1,1,2,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,3,0,1,0,0,0,2,0,0,0,1,4,0,4,2,1,0,0,0,0,0,1), +(0,0,0,0,0,0,0,0,0,1,0,1,0,0,0,0,1,0,0,0,0,0,0,1,0,1,0,0,0,0,3,1,0,0,0,2,0,2,1,0,0,1,2,1,0,1,1,0,0,3,0,0,0,0,0,0,0,0,0,0,0,1,3,1,0,0,0,0,0,1,0,0,2,1,0,0,0,0,0,0,0,0,2), +(0,4,0,4,0,4,0,3,0,4,4,3,4,2,4,3,2,0,4,4,4,3,5,3,5,3,3,2,4,2,4,3,4,3,1,4,0,2,3,4,4,4,3,3,3,4,4,4,3,4,1,3,4,3,2,1,2,1,3,3,3,4,4,3,3,5,0,4,0,3,0,4,3,3,3,2,1,0,3,0,0,3,3), +(0,4,0,3,0,3,0,3,0,3,5,5,3,3,3,3,4,3,4,3,3,3,4,4,4,3,3,3,3,4,3,5,3,3,1,3,2,4,5,5,5,5,4,3,4,5,5,3,2,2,3,3,3,3,2,3,3,1,2,3,2,4,3,3,3,4,0,4,0,2,0,4,3,2,2,1,2,0,3,0,0,4,1), +) + +class JapaneseContextAnalysis(object): + NUM_OF_CATEGORY = 6 + DONT_KNOW = -1 + ENOUGH_REL_THRESHOLD = 100 + MAX_REL_THRESHOLD = 1000 + MINIMUM_DATA_THRESHOLD = 4 + + def __init__(self): + self._total_rel = None + self._rel_sample = None + self._need_to_skip_char_num = None + self._last_char_order = None + self._done = None + self.reset() + + def reset(self): + self._total_rel = 0 # total sequence received + # category counters, each integer counts sequence in its category + self._rel_sample = [0] * self.NUM_OF_CATEGORY + # if last byte in current buffer is not the last byte of a character, + # we need to know how many bytes to skip in next buffer + self._need_to_skip_char_num = 0 + self._last_char_order = -1 # The order of previous char + # If this flag is set to True, detection is done and conclusion has + # been made + self._done = False + + def feed(self, byte_str, num_bytes): + if self._done: + return + + # The buffer we got is byte oriented, and a character may span in more than one + # buffers. In case the last one or two byte in last buffer is not + # complete, we record how many byte needed to complete that character + # and skip these bytes here. We can choose to record those bytes as + # well and analyse the character once it is complete, but since a + # character will not make much difference, by simply skipping + # this character will simply our logic and improve performance. + i = self._need_to_skip_char_num + while i < num_bytes: + order, char_len = self.get_order(byte_str[i:i + 2]) + i += char_len + if i > num_bytes: + self._need_to_skip_char_num = i - num_bytes + self._last_char_order = -1 + else: + if (order != -1) and (self._last_char_order != -1): + self._total_rel += 1 + if self._total_rel > self.MAX_REL_THRESHOLD: + self._done = True + break + self._rel_sample[jp2CharContext[self._last_char_order][order]] += 1 + self._last_char_order = order + + def got_enough_data(self): + return self._total_rel > self.ENOUGH_REL_THRESHOLD + + def get_confidence(self): + # This is just one way to calculate confidence. It works well for me. + if self._total_rel > self.MINIMUM_DATA_THRESHOLD: + return (self._total_rel - self._rel_sample[0]) / self._total_rel + else: + return self.DONT_KNOW + + def get_order(self, byte_str): + return -1, 1 + +class SJISContextAnalysis(JapaneseContextAnalysis): + def __init__(self): + super(SJISContextAnalysis, self).__init__() + self._charset_name = "SHIFT_JIS" + + @property + def charset_name(self): + return self._charset_name + + def get_order(self, byte_str): + if not byte_str: + return -1, 1 + # find out current char's byte length + first_char = byte_str[0] + if (0x81 <= first_char <= 0x9F) or (0xE0 <= first_char <= 0xFC): + char_len = 2 + if (first_char == 0x87) or (0xFA <= first_char <= 0xFC): + self._charset_name = "CP932" + else: + char_len = 1 + + # return its order if it is hiragana + if len(byte_str) > 1: + second_char = byte_str[1] + if (first_char == 202) and (0x9F <= second_char <= 0xF1): + return second_char - 0x9F, char_len + + return -1, char_len + +class EUCJPContextAnalysis(JapaneseContextAnalysis): + def get_order(self, byte_str): + if not byte_str: + return -1, 1 + # find out current char's byte length + first_char = byte_str[0] + if (first_char == 0x8E) or (0xA1 <= first_char <= 0xFE): + char_len = 2 + elif first_char == 0x8F: + char_len = 3 + else: + char_len = 1 + + # return its order if it is hiragana + if len(byte_str) > 1: + second_char = byte_str[1] + if (first_char == 0xA4) and (0xA1 <= second_char <= 0xF3): + return second_char - 0xA1, char_len + + return -1, char_len + + diff --git a/venv2/lib/python2.7/site-packages/pip/_vendor/chardet/langbulgarianmodel.py b/venv2/lib/python2.7/site-packages/pip/_vendor/chardet/langbulgarianmodel.py new file mode 100644 index 0000000..2aa4fb2 --- /dev/null +++ b/venv2/lib/python2.7/site-packages/pip/_vendor/chardet/langbulgarianmodel.py @@ -0,0 +1,228 @@ +######################## BEGIN LICENSE BLOCK ######################## +# The Original Code is Mozilla Communicator client code. +# +# The Initial Developer of the Original Code is +# Netscape Communications Corporation. +# Portions created by the Initial Developer are Copyright (C) 1998 +# the Initial Developer. All Rights Reserved. +# +# Contributor(s): +# Mark Pilgrim - port to Python +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA +# 02110-1301 USA +######################### END LICENSE BLOCK ######################### + +# 255: Control characters that usually does not exist in any text +# 254: Carriage/Return +# 253: symbol (punctuation) that does not belong to word +# 252: 0 - 9 + +# Character Mapping Table: +# this table is modified base on win1251BulgarianCharToOrderMap, so +# only number <64 is sure valid + +Latin5_BulgarianCharToOrderMap = ( +255,255,255,255,255,255,255,255,255,255,254,255,255,254,255,255, # 00 +255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, # 10 +253,253,253,253,253,253,253,253,253,253,253,253,253,253,253,253, # 20 +252,252,252,252,252,252,252,252,252,252,253,253,253,253,253,253, # 30 +253, 77, 90, 99,100, 72,109,107,101, 79,185, 81,102, 76, 94, 82, # 40 +110,186,108, 91, 74,119, 84, 96,111,187,115,253,253,253,253,253, # 50 +253, 65, 69, 70, 66, 63, 68,112,103, 92,194,104, 95, 86, 87, 71, # 60 +116,195, 85, 93, 97,113,196,197,198,199,200,253,253,253,253,253, # 70 +194,195,196,197,198,199,200,201,202,203,204,205,206,207,208,209, # 80 +210,211,212,213,214,215,216,217,218,219,220,221,222,223,224,225, # 90 + 81,226,227,228,229,230,105,231,232,233,234,235,236, 45,237,238, # a0 + 31, 32, 35, 43, 37, 44, 55, 47, 40, 59, 33, 46, 38, 36, 41, 30, # b0 + 39, 28, 34, 51, 48, 49, 53, 50, 54, 57, 61,239, 67,240, 60, 56, # c0 + 1, 18, 9, 20, 11, 3, 23, 15, 2, 26, 12, 10, 14, 6, 4, 13, # d0 + 7, 8, 5, 19, 29, 25, 22, 21, 27, 24, 17, 75, 52,241, 42, 16, # e0 + 62,242,243,244, 58,245, 98,246,247,248,249,250,251, 91,252,253, # f0 +) + +win1251BulgarianCharToOrderMap = ( +255,255,255,255,255,255,255,255,255,255,254,255,255,254,255,255, # 00 +255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, # 10 +253,253,253,253,253,253,253,253,253,253,253,253,253,253,253,253, # 20 +252,252,252,252,252,252,252,252,252,252,253,253,253,253,253,253, # 30 +253, 77, 90, 99,100, 72,109,107,101, 79,185, 81,102, 76, 94, 82, # 40 +110,186,108, 91, 74,119, 84, 96,111,187,115,253,253,253,253,253, # 50 +253, 65, 69, 70, 66, 63, 68,112,103, 92,194,104, 95, 86, 87, 71, # 60 +116,195, 85, 93, 97,113,196,197,198,199,200,253,253,253,253,253, # 70 +206,207,208,209,210,211,212,213,120,214,215,216,217,218,219,220, # 80 +221, 78, 64, 83,121, 98,117,105,222,223,224,225,226,227,228,229, # 90 + 88,230,231,232,233,122, 89,106,234,235,236,237,238, 45,239,240, # a0 + 73, 80,118,114,241,242,243,244,245, 62, 58,246,247,248,249,250, # b0 + 31, 32, 35, 43, 37, 44, 55, 47, 40, 59, 33, 46, 38, 36, 41, 30, # c0 + 39, 28, 34, 51, 48, 49, 53, 50, 54, 57, 61,251, 67,252, 60, 56, # d0 + 1, 18, 9, 20, 11, 3, 23, 15, 2, 26, 12, 10, 14, 6, 4, 13, # e0 + 7, 8, 5, 19, 29, 25, 22, 21, 27, 24, 17, 75, 52,253, 42, 16, # f0 +) + +# Model Table: +# total sequences: 100% +# first 512 sequences: 96.9392% +# first 1024 sequences:3.0618% +# rest sequences: 0.2992% +# negative sequences: 0.0020% +BulgarianLangModel = ( +0,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,2,3,3,3,3,3,3,3,3,2,3,3,3,3,3, +3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,0,3,3,3,2,2,3,2,2,1,2,2, +3,1,3,3,2,3,3,3,3,3,3,3,3,3,3,3,3,0,3,3,3,3,3,3,3,3,3,3,0,3,0,1, +0,0,0,0,0,0,0,0,0,0,1,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1, +3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,2,3,2,3,3,3,3,3,3,3,3,0,3,1,0, +0,1,0,0,0,0,0,0,0,0,1,1,0,1,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1, +3,2,2,2,3,3,3,3,3,3,3,3,3,3,3,3,3,1,3,2,3,3,3,3,3,3,3,3,0,3,0,0, +0,0,0,0,0,0,0,0,0,0,1,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +3,2,3,3,2,3,3,3,3,3,3,3,3,3,3,3,3,1,3,2,3,3,3,3,3,3,3,3,0,3,0,0, +0,0,0,0,0,0,0,0,0,0,1,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +3,3,3,3,3,3,3,3,3,3,3,2,3,2,2,1,3,3,3,3,2,2,2,1,1,2,0,1,0,1,0,0, +0,0,0,0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0,0,1, +3,3,3,3,3,3,3,2,3,2,2,3,3,1,1,2,3,3,2,3,3,3,3,2,1,2,0,2,0,3,0,0, +0,0,0,0,0,0,0,1,0,0,2,0,0,0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0,0,1, +3,3,3,3,3,3,3,1,3,3,3,3,3,2,3,2,3,3,3,3,3,2,3,3,1,3,0,3,0,2,0,0, +0,0,0,0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,1, +3,3,3,3,3,3,3,3,1,3,3,2,3,3,3,1,3,3,2,3,2,2,2,0,0,2,0,2,0,2,0,0, +0,0,0,0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0,0,1, +3,3,3,3,3,3,3,3,3,0,3,3,3,2,2,3,3,3,1,2,2,3,2,1,1,2,0,2,0,0,0,0, +1,0,0,0,0,0,0,0,0,0,2,0,0,1,0,0,1,0,0,0,1,0,0,0,0,0,0,0,0,0,0,1, +3,3,3,3,3,3,3,2,3,3,1,2,3,2,2,2,3,3,3,3,3,2,2,3,1,2,0,2,1,2,0,0, +0,0,0,0,0,0,0,0,0,0,3,0,0,1,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0,0,1, +3,3,3,3,3,1,3,3,3,3,3,2,3,3,3,2,3,3,2,3,2,2,2,3,1,2,0,1,0,1,0,0, +0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,1, +3,3,3,3,3,3,3,3,3,3,3,1,1,1,2,2,1,3,1,3,2,2,3,0,0,1,0,1,0,1,0,0, +0,0,0,1,0,0,0,0,1,0,2,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,1, +3,3,3,3,3,2,2,3,2,2,3,1,2,1,1,1,2,3,1,3,1,2,2,0,1,1,1,1,0,1,0,0, +0,0,0,0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,1, +3,3,3,3,3,1,3,2,2,3,3,1,2,3,1,1,3,3,3,3,1,2,2,1,1,1,0,2,0,2,0,1, +0,0,0,0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,1, +3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,1,2,2,3,3,3,2,2,1,1,2,0,2,0,1,0,0, +0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,1, +3,0,1,2,1,3,3,2,3,3,3,3,3,2,3,2,1,0,3,1,2,1,2,1,2,3,2,1,0,1,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +1,1,1,2,3,3,3,3,3,3,3,3,3,3,3,3,0,0,3,1,3,3,2,3,3,2,2,2,0,1,0,0, +0,0,0,0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +2,3,3,3,3,0,3,3,3,3,3,2,1,1,2,1,3,3,0,3,1,1,1,1,3,2,0,1,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,1, +3,3,2,2,2,3,3,3,3,3,3,3,3,3,3,3,1,1,3,1,3,3,2,3,2,2,2,3,0,2,0,0, +0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +3,3,3,3,3,2,3,3,2,2,3,2,1,1,1,1,1,3,1,3,1,1,0,0,0,1,0,0,0,1,0,0, +0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0, +3,3,3,3,3,2,3,2,0,3,2,0,3,0,2,0,0,2,1,3,1,0,0,1,0,0,0,1,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1, +3,3,3,3,2,1,1,1,1,2,1,1,2,1,1,1,2,2,1,2,1,1,1,0,1,1,0,1,0,1,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,1, +3,3,3,3,2,1,3,1,1,2,1,3,2,1,1,0,1,2,3,2,1,1,1,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +2,3,3,3,3,2,2,1,0,1,0,0,1,0,0,0,2,1,0,3,0,0,1,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1, +3,3,3,2,3,2,3,3,1,3,2,1,1,1,2,1,1,2,1,3,0,1,0,0,0,1,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +3,1,1,2,2,3,3,2,3,2,2,2,3,1,2,2,1,1,2,1,1,2,2,0,1,1,0,1,0,2,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +3,3,3,3,2,1,3,1,0,2,2,1,3,2,1,0,0,2,0,2,0,1,0,0,0,0,0,0,0,1,0,0, +0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,1, +3,3,3,3,3,3,1,2,0,2,3,1,2,3,2,0,1,3,1,2,1,1,1,0,0,1,0,0,2,2,2,3, +2,2,2,2,1,2,1,1,2,2,1,1,2,0,1,1,1,0,0,1,1,0,0,1,1,0,0,0,1,1,0,1, +3,3,3,3,3,2,1,2,2,1,2,0,2,0,1,0,1,2,1,2,1,1,0,0,0,1,0,1,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0,0,1, +3,3,2,3,3,1,1,3,1,0,3,2,1,0,0,0,1,2,0,2,0,1,0,0,0,1,0,1,2,1,2,2, +1,1,1,1,1,1,1,2,2,2,1,1,1,1,1,1,1,0,1,2,1,1,1,0,0,0,0,0,1,1,0,0, +3,1,0,1,0,2,3,2,2,2,3,2,2,2,2,2,1,0,2,1,2,1,1,1,0,1,2,1,2,2,2,1, +1,1,2,2,2,2,1,2,1,1,0,1,2,1,2,2,2,1,1,1,0,1,1,1,1,2,0,1,0,0,0,0, +2,3,2,3,3,0,0,2,1,0,2,1,0,0,0,0,2,3,0,2,0,0,0,0,0,1,0,0,2,0,1,2, +2,1,2,1,2,2,1,1,1,2,1,1,1,0,1,2,2,1,1,1,1,1,0,1,1,1,0,0,1,2,0,0, +3,3,2,2,3,0,2,3,1,1,2,0,0,0,1,0,0,2,0,2,0,0,0,1,0,1,0,1,2,0,2,2, +1,1,1,1,2,1,0,1,2,2,2,1,1,1,1,1,1,1,0,1,1,1,0,0,0,0,0,0,1,1,0,0, +2,3,2,3,3,0,0,3,0,1,1,0,1,0,0,0,2,2,1,2,0,0,0,0,0,0,0,0,2,0,1,2, +2,2,1,1,1,1,1,2,2,2,1,0,2,0,1,0,1,0,0,1,0,1,0,0,1,0,0,0,0,1,0,0, +3,3,3,3,2,2,2,2,2,0,2,1,1,1,1,2,1,2,1,1,0,2,0,1,0,1,0,0,2,0,1,2, +1,1,1,1,1,1,1,2,2,1,1,0,2,0,1,0,2,0,0,1,1,1,0,0,2,0,0,0,1,1,0,0, +2,3,3,3,3,1,0,0,0,0,0,0,0,0,0,0,2,0,0,1,1,0,0,0,0,0,0,1,2,0,1,2, +2,2,2,1,1,2,1,1,2,2,2,1,2,0,1,1,1,1,1,1,0,1,1,1,1,0,0,1,1,1,0,0, +2,3,3,3,3,0,2,2,0,2,1,0,0,0,1,1,1,2,0,2,0,0,0,3,0,0,0,0,2,0,2,2, +1,1,1,2,1,2,1,1,2,2,2,1,2,0,1,1,1,0,1,1,1,1,0,2,1,0,0,0,1,1,0,0, +2,3,3,3,3,0,2,1,0,0,2,0,0,0,0,0,1,2,0,2,0,0,0,0,0,0,0,0,2,0,1,2, +1,1,1,2,1,1,1,1,2,2,2,0,1,0,1,1,1,0,0,1,1,1,0,0,1,0,0,0,0,1,0,0, +3,3,2,2,3,0,1,0,1,0,0,0,0,0,0,0,1,1,0,3,0,0,0,0,0,0,0,0,1,0,2,2, +1,1,1,1,1,2,1,1,2,2,1,2,2,1,0,1,1,1,1,1,0,1,0,0,1,0,0,0,1,1,0,0, +3,1,0,1,0,2,2,2,2,3,2,1,1,1,2,3,0,0,1,0,2,1,1,0,1,1,1,1,2,1,1,1, +1,2,2,1,2,1,2,2,1,1,0,1,2,1,2,2,1,1,1,0,0,1,1,1,2,1,0,1,0,0,0,0, +2,1,0,1,0,3,1,2,2,2,2,1,2,2,1,1,1,0,2,1,2,2,1,1,2,1,1,0,2,1,1,1, +1,2,2,2,2,2,2,2,1,2,0,1,1,0,2,1,1,1,1,1,0,0,1,1,1,1,0,1,0,0,0,0, +2,1,1,1,1,2,2,2,2,1,2,2,2,1,2,2,1,1,2,1,2,3,2,2,1,1,1,1,0,1,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +2,2,2,3,2,0,1,2,0,1,2,1,1,0,1,0,1,2,1,2,0,0,0,1,1,0,0,0,1,0,0,2, +1,1,0,0,1,1,0,1,1,1,1,0,2,0,1,1,1,0,0,1,1,0,0,0,0,1,0,0,0,1,0,0, +2,0,0,0,0,1,2,2,2,2,2,2,2,1,2,1,1,1,1,1,1,1,0,1,1,1,1,1,2,1,1,1, +1,2,2,2,2,1,1,2,1,2,1,1,1,0,2,1,2,1,1,1,0,2,1,1,1,1,0,1,0,0,0,0, +3,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,1,0, +1,1,0,1,0,1,1,1,1,1,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +2,2,2,3,2,0,0,0,0,1,0,0,0,0,0,0,1,1,0,2,0,0,0,0,0,0,0,0,1,0,1,2, +1,1,1,1,1,1,0,0,2,2,2,2,2,0,1,1,0,1,1,1,1,1,0,0,1,0,0,0,1,1,0,1, +2,3,1,2,1,0,1,1,0,2,2,2,0,0,1,0,0,1,1,1,1,0,0,0,0,0,0,0,1,0,1,2, +1,1,1,1,2,1,1,1,1,1,1,1,1,0,1,1,0,1,0,1,0,1,0,0,1,0,0,0,0,1,0,0, +2,2,2,2,2,0,0,2,0,0,2,0,0,0,0,0,0,1,0,1,0,0,0,0,0,0,0,0,2,0,2,2, +1,1,1,1,1,0,0,1,2,1,1,0,1,0,1,0,0,0,0,1,1,0,0,0,0,0,0,0,0,0,0,0, +1,2,2,2,2,0,0,2,0,1,1,0,0,0,1,0,0,2,0,2,0,0,0,0,0,0,0,0,0,0,1,1, +0,0,0,1,1,1,1,1,1,1,1,1,1,0,1,0,0,1,0,0,1,0,0,0,0,0,0,0,0,0,0,0, +1,2,2,3,2,0,0,1,0,0,1,0,0,0,0,0,0,1,0,2,0,0,0,1,0,0,0,0,0,0,0,2, +1,1,0,0,1,0,0,0,1,1,0,0,1,0,1,1,0,0,0,1,1,0,0,0,0,0,0,0,0,0,0,0, +2,1,2,2,2,1,2,1,2,2,1,1,2,1,1,1,0,1,1,1,1,2,0,1,0,1,1,1,1,0,1,1, +1,1,2,1,1,1,1,1,1,0,0,1,2,1,1,1,1,1,1,0,0,1,1,1,0,0,0,0,0,0,0,0, +1,0,0,1,3,1,1,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +2,2,2,2,1,0,0,1,0,2,0,0,0,0,0,1,1,1,0,1,0,0,0,0,0,0,0,0,2,0,0,1, +0,2,0,1,0,0,1,1,2,0,1,0,1,0,1,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0, +1,2,2,2,2,0,1,1,0,2,1,0,1,1,1,0,0,1,0,2,0,1,0,0,0,0,0,0,0,0,0,1, +0,1,0,0,1,0,0,0,1,1,0,0,1,0,0,1,0,0,0,1,1,0,0,0,0,0,0,0,0,0,0,0, +2,2,2,2,2,0,0,1,0,0,0,1,0,1,0,0,0,1,0,1,0,0,0,0,0,0,0,0,0,0,0,1, +0,1,0,1,1,1,0,0,1,1,1,0,1,0,0,0,0,0,0,1,1,0,0,0,0,0,0,0,0,0,0,0, +2,0,1,0,0,1,2,1,1,1,1,1,1,2,2,1,0,0,1,0,1,0,0,0,0,1,1,1,1,0,0,0, +1,1,2,1,1,1,1,0,0,0,1,1,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +2,2,1,2,1,0,0,1,0,0,0,0,0,0,0,0,1,1,0,1,0,0,0,0,0,0,0,0,0,0,0,1, +0,0,0,0,0,0,0,0,1,1,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +3,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +1,0,0,1,2,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,1,0,0,0, +0,1,1,0,1,1,1,0,0,1,0,0,1,0,1,0,0,0,1,0,0,0,0,0,1,0,0,0,0,0,0,0, +1,0,1,0,0,1,1,1,1,1,1,1,1,1,1,1,0,0,1,0,2,0,0,2,0,1,0,0,1,0,0,1, +1,1,0,0,1,1,0,1,0,0,0,1,0,0,1,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,1,1,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,1,0, +1,1,1,1,1,1,1,2,0,0,0,0,0,0,2,1,0,1,1,0,0,1,1,1,0,1,0,0,0,0,0,0, +2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +1,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,1,0,1,1,0,1,1,1,1,1,0,1,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1, +) + +Latin5BulgarianModel = { + 'char_to_order_map': Latin5_BulgarianCharToOrderMap, + 'precedence_matrix': BulgarianLangModel, + 'typical_positive_ratio': 0.969392, + 'keep_english_letter': False, + 'charset_name': "ISO-8859-5", + 'language': 'Bulgairan', +} + +Win1251BulgarianModel = { + 'char_to_order_map': win1251BulgarianCharToOrderMap, + 'precedence_matrix': BulgarianLangModel, + 'typical_positive_ratio': 0.969392, + 'keep_english_letter': False, + 'charset_name': "windows-1251", + 'language': 'Bulgarian', +} diff --git a/venv2/lib/python2.7/site-packages/pip/_vendor/chardet/langcyrillicmodel.py b/venv2/lib/python2.7/site-packages/pip/_vendor/chardet/langcyrillicmodel.py new file mode 100644 index 0000000..e5f9a1f --- /dev/null +++ b/venv2/lib/python2.7/site-packages/pip/_vendor/chardet/langcyrillicmodel.py @@ -0,0 +1,333 @@ +######################## BEGIN LICENSE BLOCK ######################## +# The Original Code is Mozilla Communicator client code. +# +# The Initial Developer of the Original Code is +# Netscape Communications Corporation. +# Portions created by the Initial Developer are Copyright (C) 1998 +# the Initial Developer. All Rights Reserved. +# +# Contributor(s): +# Mark Pilgrim - port to Python +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA +# 02110-1301 USA +######################### END LICENSE BLOCK ######################### + +# KOI8-R language model +# Character Mapping Table: +KOI8R_char_to_order_map = ( +255,255,255,255,255,255,255,255,255,255,254,255,255,254,255,255, # 00 +255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, # 10 +253,253,253,253,253,253,253,253,253,253,253,253,253,253,253,253, # 20 +252,252,252,252,252,252,252,252,252,252,253,253,253,253,253,253, # 30 +253,142,143,144,145,146,147,148,149,150,151,152, 74,153, 75,154, # 40 +155,156,157,158,159,160,161,162,163,164,165,253,253,253,253,253, # 50 +253, 71,172, 66,173, 65,174, 76,175, 64,176,177, 77, 72,178, 69, # 60 + 67,179, 78, 73,180,181, 79,182,183,184,185,253,253,253,253,253, # 70 +191,192,193,194,195,196,197,198,199,200,201,202,203,204,205,206, # 80 +207,208,209,210,211,212,213,214,215,216,217,218,219,220,221,222, # 90 +223,224,225, 68,226,227,228,229,230,231,232,233,234,235,236,237, # a0 +238,239,240,241,242,243,244,245,246,247,248,249,250,251,252,253, # b0 + 27, 3, 21, 28, 13, 2, 39, 19, 26, 4, 23, 11, 8, 12, 5, 1, # c0 + 15, 16, 9, 7, 6, 14, 24, 10, 17, 18, 20, 25, 30, 29, 22, 54, # d0 + 59, 37, 44, 58, 41, 48, 53, 46, 55, 42, 60, 36, 49, 38, 31, 34, # e0 + 35, 43, 45, 32, 40, 52, 56, 33, 61, 62, 51, 57, 47, 63, 50, 70, # f0 +) + +win1251_char_to_order_map = ( +255,255,255,255,255,255,255,255,255,255,254,255,255,254,255,255, # 00 +255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, # 10 +253,253,253,253,253,253,253,253,253,253,253,253,253,253,253,253, # 20 +252,252,252,252,252,252,252,252,252,252,253,253,253,253,253,253, # 30 +253,142,143,144,145,146,147,148,149,150,151,152, 74,153, 75,154, # 40 +155,156,157,158,159,160,161,162,163,164,165,253,253,253,253,253, # 50 +253, 71,172, 66,173, 65,174, 76,175, 64,176,177, 77, 72,178, 69, # 60 + 67,179, 78, 73,180,181, 79,182,183,184,185,253,253,253,253,253, # 70 +191,192,193,194,195,196,197,198,199,200,201,202,203,204,205,206, +207,208,209,210,211,212,213,214,215,216,217,218,219,220,221,222, +223,224,225,226,227,228,229,230,231,232,233,234,235,236,237,238, +239,240,241,242,243,244,245,246, 68,247,248,249,250,251,252,253, + 37, 44, 33, 46, 41, 48, 56, 51, 42, 60, 36, 49, 38, 31, 34, 35, + 45, 32, 40, 52, 53, 55, 58, 50, 57, 63, 70, 62, 61, 47, 59, 43, + 3, 21, 10, 19, 13, 2, 24, 20, 4, 23, 11, 8, 12, 5, 1, 15, + 9, 7, 6, 14, 39, 26, 28, 22, 25, 29, 54, 18, 17, 30, 27, 16, +) + +latin5_char_to_order_map = ( +255,255,255,255,255,255,255,255,255,255,254,255,255,254,255,255, # 00 +255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, # 10 +253,253,253,253,253,253,253,253,253,253,253,253,253,253,253,253, # 20 +252,252,252,252,252,252,252,252,252,252,253,253,253,253,253,253, # 30 +253,142,143,144,145,146,147,148,149,150,151,152, 74,153, 75,154, # 40 +155,156,157,158,159,160,161,162,163,164,165,253,253,253,253,253, # 50 +253, 71,172, 66,173, 65,174, 76,175, 64,176,177, 77, 72,178, 69, # 60 + 67,179, 78, 73,180,181, 79,182,183,184,185,253,253,253,253,253, # 70 +191,192,193,194,195,196,197,198,199,200,201,202,203,204,205,206, +207,208,209,210,211,212,213,214,215,216,217,218,219,220,221,222, +223,224,225,226,227,228,229,230,231,232,233,234,235,236,237,238, + 37, 44, 33, 46, 41, 48, 56, 51, 42, 60, 36, 49, 38, 31, 34, 35, + 45, 32, 40, 52, 53, 55, 58, 50, 57, 63, 70, 62, 61, 47, 59, 43, + 3, 21, 10, 19, 13, 2, 24, 20, 4, 23, 11, 8, 12, 5, 1, 15, + 9, 7, 6, 14, 39, 26, 28, 22, 25, 29, 54, 18, 17, 30, 27, 16, +239, 68,240,241,242,243,244,245,246,247,248,249,250,251,252,255, +) + +macCyrillic_char_to_order_map = ( +255,255,255,255,255,255,255,255,255,255,254,255,255,254,255,255, # 00 +255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, # 10 +253,253,253,253,253,253,253,253,253,253,253,253,253,253,253,253, # 20 +252,252,252,252,252,252,252,252,252,252,253,253,253,253,253,253, # 30 +253,142,143,144,145,146,147,148,149,150,151,152, 74,153, 75,154, # 40 +155,156,157,158,159,160,161,162,163,164,165,253,253,253,253,253, # 50 +253, 71,172, 66,173, 65,174, 76,175, 64,176,177, 77, 72,178, 69, # 60 + 67,179, 78, 73,180,181, 79,182,183,184,185,253,253,253,253,253, # 70 + 37, 44, 33, 46, 41, 48, 56, 51, 42, 60, 36, 49, 38, 31, 34, 35, + 45, 32, 40, 52, 53, 55, 58, 50, 57, 63, 70, 62, 61, 47, 59, 43, +191,192,193,194,195,196,197,198,199,200,201,202,203,204,205,206, +207,208,209,210,211,212,213,214,215,216,217,218,219,220,221,222, +223,224,225,226,227,228,229,230,231,232,233,234,235,236,237,238, +239,240,241,242,243,244,245,246,247,248,249,250,251,252, 68, 16, + 3, 21, 10, 19, 13, 2, 24, 20, 4, 23, 11, 8, 12, 5, 1, 15, + 9, 7, 6, 14, 39, 26, 28, 22, 25, 29, 54, 18, 17, 30, 27,255, +) + +IBM855_char_to_order_map = ( +255,255,255,255,255,255,255,255,255,255,254,255,255,254,255,255, # 00 +255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, # 10 +253,253,253,253,253,253,253,253,253,253,253,253,253,253,253,253, # 20 +252,252,252,252,252,252,252,252,252,252,253,253,253,253,253,253, # 30 +253,142,143,144,145,146,147,148,149,150,151,152, 74,153, 75,154, # 40 +155,156,157,158,159,160,161,162,163,164,165,253,253,253,253,253, # 50 +253, 71,172, 66,173, 65,174, 76,175, 64,176,177, 77, 72,178, 69, # 60 + 67,179, 78, 73,180,181, 79,182,183,184,185,253,253,253,253,253, # 70 +191,192,193,194, 68,195,196,197,198,199,200,201,202,203,204,205, +206,207,208,209,210,211,212,213,214,215,216,217, 27, 59, 54, 70, + 3, 37, 21, 44, 28, 58, 13, 41, 2, 48, 39, 53, 19, 46,218,219, +220,221,222,223,224, 26, 55, 4, 42,225,226,227,228, 23, 60,229, +230,231,232,233,234,235, 11, 36,236,237,238,239,240,241,242,243, + 8, 49, 12, 38, 5, 31, 1, 34, 15,244,245,246,247, 35, 16,248, + 43, 9, 45, 7, 32, 6, 40, 14, 52, 24, 56, 10, 33, 17, 61,249, +250, 18, 62, 20, 51, 25, 57, 30, 47, 29, 63, 22, 50,251,252,255, +) + +IBM866_char_to_order_map = ( +255,255,255,255,255,255,255,255,255,255,254,255,255,254,255,255, # 00 +255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, # 10 +253,253,253,253,253,253,253,253,253,253,253,253,253,253,253,253, # 20 +252,252,252,252,252,252,252,252,252,252,253,253,253,253,253,253, # 30 +253,142,143,144,145,146,147,148,149,150,151,152, 74,153, 75,154, # 40 +155,156,157,158,159,160,161,162,163,164,165,253,253,253,253,253, # 50 +253, 71,172, 66,173, 65,174, 76,175, 64,176,177, 77, 72,178, 69, # 60 + 67,179, 78, 73,180,181, 79,182,183,184,185,253,253,253,253,253, # 70 + 37, 44, 33, 46, 41, 48, 56, 51, 42, 60, 36, 49, 38, 31, 34, 35, + 45, 32, 40, 52, 53, 55, 58, 50, 57, 63, 70, 62, 61, 47, 59, 43, + 3, 21, 10, 19, 13, 2, 24, 20, 4, 23, 11, 8, 12, 5, 1, 15, +191,192,193,194,195,196,197,198,199,200,201,202,203,204,205,206, +207,208,209,210,211,212,213,214,215,216,217,218,219,220,221,222, +223,224,225,226,227,228,229,230,231,232,233,234,235,236,237,238, + 9, 7, 6, 14, 39, 26, 28, 22, 25, 29, 54, 18, 17, 30, 27, 16, +239, 68,240,241,242,243,244,245,246,247,248,249,250,251,252,255, +) + +# Model Table: +# total sequences: 100% +# first 512 sequences: 97.6601% +# first 1024 sequences: 2.3389% +# rest sequences: 0.1237% +# negative sequences: 0.0009% +RussianLangModel = ( +0,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,1,1,3,3,3,3,1,3,3,3,2,3,2,3,3, +3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,0,3,2,2,2,2,2,0,0,2, +3,3,3,2,3,3,3,3,3,3,3,3,3,3,2,3,3,0,0,3,3,3,3,3,3,3,3,3,2,3,2,0, +0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +3,3,3,2,2,3,3,3,3,3,3,3,3,3,2,3,3,0,0,3,3,3,3,3,3,3,3,2,3,3,1,0, +0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +3,2,3,2,3,3,3,3,3,3,3,3,3,3,3,3,3,0,0,3,3,3,3,3,3,3,3,3,3,3,2,1, +0,0,0,0,0,0,0,2,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +3,3,3,3,3,3,3,3,3,3,3,3,3,3,2,3,3,0,0,3,3,3,3,3,3,3,3,3,3,3,2,1, +0,0,0,0,0,1,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +3,3,3,3,3,3,3,3,2,2,2,3,1,3,3,1,3,3,3,3,2,2,3,0,2,2,2,3,3,2,1,0, +0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0, +3,3,3,3,3,3,2,3,3,3,3,3,2,2,3,2,3,3,3,2,1,2,2,0,1,2,2,2,2,2,2,0, +0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0, +3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,2,2,2,3,0,2,2,3,3,2,1,2,0, +0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,1,0,0,2,0,0,0,0,0,0,0,0,0, +3,3,3,3,3,3,2,3,3,1,2,3,2,2,3,2,3,3,3,3,2,2,3,0,3,2,2,3,1,1,1,0, +0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +3,3,3,3,3,3,3,3,2,2,3,3,3,3,3,2,3,3,3,3,2,2,2,0,3,3,3,2,2,2,2,0, +0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +3,3,3,3,3,3,3,3,3,3,2,3,2,3,3,3,3,3,3,2,3,2,2,0,1,3,2,1,2,2,1,0, +0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0, +3,3,3,3,3,3,3,3,3,3,3,2,1,1,3,0,1,1,1,1,2,1,1,0,2,2,2,1,2,0,1,0, +0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +3,3,3,3,3,3,2,3,3,2,2,2,2,1,3,2,3,2,3,2,1,2,2,0,1,1,2,1,2,1,2,0, +0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +3,3,3,3,3,3,3,3,3,3,3,3,2,2,3,2,3,3,3,2,2,2,2,0,2,2,2,2,3,1,1,0, +0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0, +3,2,3,2,2,3,3,3,3,3,3,3,3,3,1,3,2,0,0,3,3,3,3,2,3,3,3,3,2,3,2,0, +0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +2,3,3,3,3,3,2,2,3,3,0,2,1,0,3,2,3,2,3,0,0,1,2,0,0,1,0,1,2,1,1,0, +0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +3,0,3,0,2,3,3,3,3,2,3,3,3,3,1,2,2,0,0,2,3,2,2,2,3,2,3,2,2,3,0,0, +0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +3,2,3,0,2,3,2,3,0,1,2,3,3,2,0,2,3,0,0,2,3,2,2,0,1,3,1,3,2,2,1,0, +0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +3,1,3,0,2,3,3,3,3,3,3,3,3,2,1,3,2,0,0,2,2,3,3,3,2,3,3,0,2,2,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +3,3,3,3,3,3,2,2,3,3,2,2,2,3,3,0,0,1,1,1,1,1,2,0,0,1,1,1,1,0,1,0, +0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +3,3,3,3,3,3,2,2,3,3,3,3,3,3,3,0,3,2,3,3,2,3,2,0,2,1,0,1,1,0,1,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0, +3,3,3,3,3,3,2,3,3,3,2,2,2,2,3,1,3,2,3,1,1,2,1,0,2,2,2,2,1,3,1,0, +0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0, +2,2,3,3,3,3,3,1,2,2,1,3,1,0,3,0,0,3,0,0,0,1,1,0,1,2,1,0,0,0,0,0, +0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +3,2,2,1,1,3,3,3,2,2,1,2,2,3,1,1,2,0,0,2,2,1,3,0,0,2,1,1,2,1,1,0, +0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +3,2,3,3,3,3,1,2,2,2,1,2,1,3,3,1,1,2,1,2,1,2,2,0,2,0,0,1,1,0,1,0, +0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +2,3,3,3,3,3,2,1,3,2,2,3,2,0,3,2,0,3,0,1,0,1,1,0,0,1,1,1,1,0,1,0, +0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +3,3,2,3,3,3,2,2,2,3,3,1,2,1,2,1,0,1,0,1,1,0,1,0,0,2,1,1,1,0,1,0, +0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0, +3,1,1,2,1,2,3,3,2,2,1,2,2,3,0,2,1,0,0,2,2,3,2,1,2,2,2,2,2,3,1,0, +0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +3,3,3,3,3,1,1,0,1,1,2,2,1,1,3,0,0,1,3,1,1,1,0,0,0,1,0,1,1,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +2,1,3,3,3,2,0,0,0,2,1,0,1,0,2,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +2,0,1,0,0,2,3,2,2,2,1,2,2,2,1,2,1,0,0,1,1,1,0,2,0,1,1,1,0,0,1,1, +1,0,0,0,0,0,1,2,0,0,0,0,0,1,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0, +2,3,3,3,3,0,0,0,0,1,0,0,0,0,3,0,1,2,1,0,0,0,0,0,0,0,1,1,0,0,1,1, +1,0,1,0,1,2,0,0,1,1,2,1,0,1,1,1,1,0,1,1,1,1,0,1,0,0,1,0,0,1,1,0, +2,2,3,2,2,2,3,1,2,2,2,2,2,2,2,2,1,1,1,1,1,1,1,0,1,0,1,1,1,0,2,1, +1,1,1,1,1,1,1,1,2,1,1,1,1,1,1,1,1,1,1,0,1,0,1,1,0,1,1,1,0,1,1,0, +3,3,3,2,2,2,2,3,2,2,1,1,2,2,2,2,1,1,3,1,2,1,2,0,0,1,1,0,1,0,2,1, +1,1,1,1,1,2,1,0,1,1,1,1,0,1,0,0,1,1,0,0,1,0,1,0,0,1,0,0,0,1,1,0, +2,0,0,1,0,3,2,2,2,2,1,2,1,2,1,2,0,0,0,2,1,2,2,1,1,2,2,0,1,1,0,2, +1,1,1,1,1,0,1,1,1,2,1,1,1,2,1,0,1,2,1,1,1,1,0,1,1,1,0,0,1,0,0,1, +1,3,2,2,2,1,1,1,2,3,0,0,0,0,2,0,2,2,1,0,0,0,0,0,0,1,0,0,0,0,1,1, +1,0,1,1,0,1,0,1,1,0,1,1,0,2,0,0,1,1,0,0,1,0,0,0,0,0,0,0,0,1,1,0, +2,3,2,3,2,1,2,2,2,2,1,0,0,0,2,0,0,1,1,0,0,0,0,0,0,0,1,1,0,0,2,1, +1,1,2,1,0,2,0,0,1,0,1,0,0,1,0,0,1,1,0,1,1,0,0,0,0,0,1,0,0,0,0,0, +3,0,0,1,0,2,2,2,3,2,2,2,2,2,2,2,0,0,0,2,1,2,1,1,1,2,2,0,0,0,1,2, +1,1,1,1,1,0,1,2,1,1,1,1,1,1,1,0,1,1,1,1,1,1,0,1,1,1,1,1,1,0,0,1, +2,3,2,3,3,2,0,1,1,1,0,0,1,0,2,0,1,1,3,1,0,0,0,0,0,0,0,1,0,0,2,1, +1,1,1,1,1,1,1,0,1,0,1,1,1,1,0,1,1,1,0,0,1,1,0,1,0,0,0,0,0,0,1,0, +2,3,3,3,3,1,2,2,2,2,0,1,1,0,2,1,1,1,2,1,0,1,1,0,0,1,0,1,0,0,2,0, +0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +2,3,3,3,2,0,0,1,1,2,2,1,0,0,2,0,1,1,3,0,0,1,0,0,0,0,0,1,0,1,2,1, +1,1,2,0,1,1,1,0,1,0,1,1,0,1,0,1,1,1,1,0,1,0,0,0,0,0,0,1,0,1,1,0, +1,3,2,3,2,1,0,0,2,2,2,0,1,0,2,0,1,1,1,0,1,0,0,0,3,0,1,1,0,0,2,1, +1,1,1,0,1,1,0,0,0,0,1,1,0,1,0,0,2,1,1,0,1,0,0,0,1,0,1,0,0,1,1,0, +3,1,2,1,1,2,2,2,2,2,2,1,2,2,1,1,0,0,0,2,2,2,0,0,0,1,2,1,0,1,0,1, +2,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,2,1,1,1,0,1,0,1,1,0,1,1,1,0,0,1, +3,0,0,0,0,2,0,1,1,1,1,1,1,1,0,1,0,0,0,1,1,1,0,1,0,1,1,0,0,1,0,1, +1,1,0,0,1,0,0,0,1,0,1,1,0,0,1,0,1,0,1,0,0,0,0,1,0,0,0,1,0,0,0,1, +1,3,3,2,2,0,0,0,2,2,0,0,0,1,2,0,1,1,2,0,0,0,0,0,0,0,0,1,0,0,2,1, +0,1,1,0,0,1,1,0,0,0,1,1,0,1,1,0,1,1,0,0,1,0,0,0,0,0,0,0,0,0,1,0, +2,3,2,3,2,0,0,0,0,1,1,0,0,0,2,0,2,0,2,0,0,0,0,0,1,0,0,1,0,0,1,1, +1,1,2,0,1,2,1,0,1,1,2,1,1,1,1,1,2,1,1,0,1,0,0,1,1,1,1,1,0,1,1,0, +1,3,2,2,2,1,0,0,2,2,1,0,1,2,2,0,0,1,0,0,0,0,0,0,0,0,0,1,0,0,1,1, +0,0,1,1,0,1,1,0,0,1,1,0,1,1,0,0,1,1,0,0,1,0,0,0,0,0,0,0,0,0,0,0, +1,0,0,1,0,2,3,1,2,2,2,2,2,2,1,1,0,0,0,1,0,1,0,2,1,1,1,0,0,0,0,1, +1,1,0,1,1,0,1,1,1,1,0,0,0,1,0,0,0,1,0,0,0,0,0,0,0,0,0,0,1,0,0,0, +2,0,2,0,0,1,0,3,2,1,2,1,2,2,0,1,0,0,0,2,1,0,0,2,1,1,1,1,0,2,0,2, +2,1,1,1,1,1,1,1,1,1,1,1,1,2,1,0,1,1,1,1,0,0,0,1,1,1,1,0,1,0,0,1, +1,2,2,2,2,1,0,0,1,0,0,0,0,0,2,0,1,1,1,1,0,0,0,0,1,0,1,2,0,0,2,0, +1,0,1,1,1,2,1,0,1,0,1,1,0,0,1,0,1,1,1,0,1,0,0,0,1,0,0,1,0,1,1,0, +2,1,2,2,2,0,3,0,1,1,0,0,0,0,2,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,1, +0,0,0,1,1,1,0,0,1,0,1,0,0,0,0,0,1,0,0,0,1,0,0,0,0,0,0,0,0,1,0,0, +1,2,2,3,2,2,0,0,1,1,2,0,1,2,1,0,1,0,1,0,0,1,0,0,0,0,0,0,0,0,0,1, +0,1,1,0,0,1,1,0,0,1,1,0,0,1,1,0,1,1,0,0,1,0,0,0,0,0,0,0,0,1,1,0, +2,2,1,1,2,1,2,2,2,2,2,1,2,2,0,1,0,0,0,1,2,2,2,1,2,1,1,1,1,1,2,1, +1,1,1,1,1,1,1,1,1,1,0,0,1,1,1,0,1,1,1,0,0,0,0,1,1,1,0,1,1,0,0,1, +1,2,2,2,2,0,1,0,2,2,0,0,0,0,2,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,2,0, +0,0,1,0,0,1,0,0,0,0,1,0,1,1,0,0,1,1,0,0,1,0,0,0,0,0,0,0,0,0,0,0, +0,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +1,2,2,2,2,0,0,0,2,2,2,0,1,0,1,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,1,1, +0,1,1,0,0,1,1,0,0,0,1,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +1,2,2,2,2,0,0,0,0,1,0,0,1,1,2,0,0,0,0,1,0,1,0,0,1,0,0,2,0,0,0,1, +0,0,1,0,0,1,0,0,0,1,1,0,0,0,0,0,1,0,0,1,1,0,0,0,0,0,0,0,0,0,0,0, +1,2,2,2,1,1,2,0,2,1,1,1,1,0,2,2,0,0,0,0,0,0,0,0,0,1,1,0,0,0,1,1, +0,0,1,0,1,1,0,0,0,0,1,0,0,0,0,0,1,1,0,0,1,0,0,0,0,0,0,0,0,0,0,0, +1,0,2,1,2,0,0,0,0,0,1,0,0,0,1,0,0,0,1,0,0,0,0,0,0,0,0,1,0,0,0,0, +0,0,1,0,1,1,0,0,0,0,1,0,0,0,0,0,1,0,0,0,1,0,0,0,0,0,0,0,0,0,1,0, +1,0,0,0,0,2,0,1,2,1,0,1,1,1,0,1,0,0,0,1,0,1,0,0,1,0,1,0,0,0,0,1, +0,0,0,0,0,1,0,0,1,1,0,0,1,1,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,1, +2,2,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1, +1,0,0,0,1,0,0,0,1,1,0,0,0,0,0,0,0,1,0,0,0,0,0,1,0,0,1,0,0,0,0,0, +2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1, +1,1,1,0,1,0,1,0,0,1,1,1,1,0,0,0,1,0,0,0,0,1,0,0,0,1,0,1,0,0,0,0, +1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1, +1,1,0,1,1,0,1,0,1,0,0,0,0,1,1,0,1,1,0,0,0,0,0,1,0,1,1,0,1,0,0,0, +0,1,1,1,1,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,1,0,0,0,0,1,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0, +) + +Koi8rModel = { + 'char_to_order_map': KOI8R_char_to_order_map, + 'precedence_matrix': RussianLangModel, + 'typical_positive_ratio': 0.976601, + 'keep_english_letter': False, + 'charset_name': "KOI8-R", + 'language': 'Russian', +} + +Win1251CyrillicModel = { + 'char_to_order_map': win1251_char_to_order_map, + 'precedence_matrix': RussianLangModel, + 'typical_positive_ratio': 0.976601, + 'keep_english_letter': False, + 'charset_name': "windows-1251", + 'language': 'Russian', +} + +Latin5CyrillicModel = { + 'char_to_order_map': latin5_char_to_order_map, + 'precedence_matrix': RussianLangModel, + 'typical_positive_ratio': 0.976601, + 'keep_english_letter': False, + 'charset_name': "ISO-8859-5", + 'language': 'Russian', +} + +MacCyrillicModel = { + 'char_to_order_map': macCyrillic_char_to_order_map, + 'precedence_matrix': RussianLangModel, + 'typical_positive_ratio': 0.976601, + 'keep_english_letter': False, + 'charset_name': "MacCyrillic", + 'language': 'Russian', +} + +Ibm866Model = { + 'char_to_order_map': IBM866_char_to_order_map, + 'precedence_matrix': RussianLangModel, + 'typical_positive_ratio': 0.976601, + 'keep_english_letter': False, + 'charset_name': "IBM866", + 'language': 'Russian', +} + +Ibm855Model = { + 'char_to_order_map': IBM855_char_to_order_map, + 'precedence_matrix': RussianLangModel, + 'typical_positive_ratio': 0.976601, + 'keep_english_letter': False, + 'charset_name': "IBM855", + 'language': 'Russian', +} diff --git a/venv2/lib/python2.7/site-packages/pip/_vendor/chardet/langgreekmodel.py b/venv2/lib/python2.7/site-packages/pip/_vendor/chardet/langgreekmodel.py new file mode 100644 index 0000000..5332221 --- /dev/null +++ b/venv2/lib/python2.7/site-packages/pip/_vendor/chardet/langgreekmodel.py @@ -0,0 +1,225 @@ +######################## BEGIN LICENSE BLOCK ######################## +# The Original Code is Mozilla Communicator client code. +# +# The Initial Developer of the Original Code is +# Netscape Communications Corporation. +# Portions created by the Initial Developer are Copyright (C) 1998 +# the Initial Developer. All Rights Reserved. +# +# Contributor(s): +# Mark Pilgrim - port to Python +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA +# 02110-1301 USA +######################### END LICENSE BLOCK ######################### + +# 255: Control characters that usually does not exist in any text +# 254: Carriage/Return +# 253: symbol (punctuation) that does not belong to word +# 252: 0 - 9 + +# Character Mapping Table: +Latin7_char_to_order_map = ( +255,255,255,255,255,255,255,255,255,255,254,255,255,254,255,255, # 00 +255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, # 10 +253,253,253,253,253,253,253,253,253,253,253,253,253,253,253,253, # 20 +252,252,252,252,252,252,252,252,252,252,253,253,253,253,253,253, # 30 +253, 82,100,104, 94, 98,101,116,102,111,187,117, 92, 88,113, 85, # 40 + 79,118,105, 83, 67,114,119, 95, 99,109,188,253,253,253,253,253, # 50 +253, 72, 70, 80, 81, 60, 96, 93, 89, 68,120, 97, 77, 86, 69, 55, # 60 + 78,115, 65, 66, 58, 76,106,103, 87,107,112,253,253,253,253,253, # 70 +255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, # 80 +255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, # 90 +253,233, 90,253,253,253,253,253,253,253,253,253,253, 74,253,253, # a0 +253,253,253,253,247,248, 61, 36, 46, 71, 73,253, 54,253,108,123, # b0 +110, 31, 51, 43, 41, 34, 91, 40, 52, 47, 44, 53, 38, 49, 59, 39, # c0 + 35, 48,250, 37, 33, 45, 56, 50, 84, 57,120,121, 17, 18, 22, 15, # d0 +124, 1, 29, 20, 21, 3, 32, 13, 25, 5, 11, 16, 10, 6, 30, 4, # e0 + 9, 8, 14, 7, 2, 12, 28, 23, 42, 24, 64, 75, 19, 26, 27,253, # f0 +) + +win1253_char_to_order_map = ( +255,255,255,255,255,255,255,255,255,255,254,255,255,254,255,255, # 00 +255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, # 10 +253,253,253,253,253,253,253,253,253,253,253,253,253,253,253,253, # 20 +252,252,252,252,252,252,252,252,252,252,253,253,253,253,253,253, # 30 +253, 82,100,104, 94, 98,101,116,102,111,187,117, 92, 88,113, 85, # 40 + 79,118,105, 83, 67,114,119, 95, 99,109,188,253,253,253,253,253, # 50 +253, 72, 70, 80, 81, 60, 96, 93, 89, 68,120, 97, 77, 86, 69, 55, # 60 + 78,115, 65, 66, 58, 76,106,103, 87,107,112,253,253,253,253,253, # 70 +255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, # 80 +255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, # 90 +253,233, 61,253,253,253,253,253,253,253,253,253,253, 74,253,253, # a0 +253,253,253,253,247,253,253, 36, 46, 71, 73,253, 54,253,108,123, # b0 +110, 31, 51, 43, 41, 34, 91, 40, 52, 47, 44, 53, 38, 49, 59, 39, # c0 + 35, 48,250, 37, 33, 45, 56, 50, 84, 57,120,121, 17, 18, 22, 15, # d0 +124, 1, 29, 20, 21, 3, 32, 13, 25, 5, 11, 16, 10, 6, 30, 4, # e0 + 9, 8, 14, 7, 2, 12, 28, 23, 42, 24, 64, 75, 19, 26, 27,253, # f0 +) + +# Model Table: +# total sequences: 100% +# first 512 sequences: 98.2851% +# first 1024 sequences:1.7001% +# rest sequences: 0.0359% +# negative sequences: 0.0148% +GreekLangModel = ( +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,3,2,2,3,3,3,3,3,3,3,3,1,3,3,3,0,2,2,3,3,0,3,0,3,2,0,3,3,3,0, +3,0,0,0,2,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,3,3,3,3,3,0,3,3,0,3,2,3,3,0,3,2,3,3,3,0,0,3,0,3,0,3,3,2,0,0,0, +2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0, +0,2,3,2,2,3,3,3,3,3,3,3,3,0,3,3,3,3,0,2,3,3,0,3,3,3,3,2,3,3,3,0, +2,0,0,0,2,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,2,3,3,2,3,3,3,3,3,3,3,3,3,3,3,3,0,2,1,3,3,3,3,2,3,3,2,3,3,2,0, +0,0,0,0,2,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,3,3,3,3,0,3,3,3,3,3,3,0,3,3,0,3,3,3,3,3,3,3,3,3,3,0,3,2,3,3,0, +2,0,1,0,2,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0, +0,3,3,3,3,3,2,3,0,0,0,0,3,3,0,3,1,3,3,3,0,3,3,0,3,3,3,3,0,0,0,0, +2,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,3,3,3,3,3,0,3,0,3,3,3,3,3,0,3,2,2,2,3,0,2,3,3,3,3,3,2,3,3,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,3,3,3,3,3,3,2,2,2,3,3,3,3,0,3,1,3,3,3,3,2,3,3,3,3,3,3,3,2,2,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,3,3,3,3,3,2,0,3,0,0,0,3,3,2,3,3,3,3,3,0,0,3,2,3,0,2,3,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,3,0,3,3,3,3,0,0,3,3,0,2,3,0,3,0,3,3,3,0,0,3,0,3,0,2,2,3,3,0,0, +0,0,1,0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,3,3,3,3,3,2,0,3,2,3,3,3,3,0,3,3,3,3,3,0,3,3,2,3,2,3,3,2,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,3,3,2,3,2,3,3,3,3,3,3,0,2,3,2,3,2,2,2,3,2,3,3,2,3,0,2,2,2,3,0, +2,0,0,0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,3,0,0,0,3,3,3,2,3,3,0,0,3,0,3,0,0,0,3,2,0,3,0,3,0,0,2,0,2,0, +0,0,0,0,2,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,3,3,3,3,0,3,3,3,3,3,3,0,3,3,0,3,0,0,0,3,3,0,3,3,3,0,0,1,2,3,0, +3,0,0,0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,3,3,3,3,3,2,0,0,3,2,2,3,3,0,3,3,3,3,3,2,1,3,0,3,2,3,3,2,1,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,3,3,0,2,3,3,3,3,3,3,0,0,3,0,3,0,0,0,3,3,0,3,2,3,0,0,3,3,3,0, +3,0,0,0,2,0,0,0,0,0,3,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,3,3,3,3,0,3,3,3,3,3,3,0,0,3,0,3,0,0,0,3,2,0,3,2,3,0,0,3,2,3,0, +2,0,0,0,0,0,0,0,0,0,3,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,3,1,2,2,3,3,3,3,3,3,0,2,3,0,3,0,0,0,3,3,0,3,0,2,0,0,2,3,1,0, +2,0,0,0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,3,0,3,3,3,3,0,3,0,3,3,2,3,0,3,3,3,3,3,3,0,3,3,3,0,2,3,0,0,3,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,3,0,3,3,3,0,0,3,0,0,0,3,3,0,3,0,2,3,3,0,0,3,0,3,0,3,3,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,3,0,0,0,3,3,3,3,3,3,0,0,3,0,2,0,0,0,3,3,0,3,0,3,0,0,2,0,2,0, +0,0,0,0,1,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,3,3,3,3,3,3,0,3,0,2,0,3,2,0,3,2,3,2,3,0,0,3,2,3,2,3,3,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,3,0,0,2,3,3,3,3,3,0,0,0,3,0,2,1,0,0,3,2,2,2,0,3,0,0,2,2,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,3,0,3,3,3,2,0,3,0,3,0,3,3,0,2,1,2,3,3,0,0,3,0,3,0,3,3,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,2,3,3,3,0,3,3,3,3,3,3,0,2,3,0,3,0,0,0,2,1,0,2,2,3,0,0,2,2,2,0, +0,0,0,0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,3,0,0,2,3,3,3,2,3,0,0,1,3,0,2,0,0,0,0,3,0,1,0,2,0,0,1,1,1,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,3,3,3,3,3,1,0,3,0,0,0,3,2,0,3,2,3,3,3,0,0,3,0,3,2,2,2,1,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,3,0,3,3,3,0,0,3,0,0,0,0,2,0,2,3,3,2,2,2,2,3,0,2,0,2,2,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,3,3,3,3,2,0,0,0,0,0,0,2,3,0,2,0,2,3,2,0,0,3,0,3,0,3,1,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,3,2,3,3,2,2,3,0,2,0,3,0,0,0,2,0,0,0,0,1,2,0,2,0,2,0, +0,2,0,2,0,2,2,0,0,1,0,2,2,2,0,2,2,2,0,2,2,2,0,0,2,0,0,1,0,0,0,0, +0,2,0,3,3,2,0,0,0,0,0,0,1,3,0,2,0,2,2,2,0,0,2,0,3,0,0,2,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,3,0,2,3,2,0,2,2,0,2,0,2,2,0,2,0,2,2,2,0,0,0,0,0,0,2,3,0,0,0,2, +0,1,2,0,0,0,0,2,2,0,0,0,2,1,0,2,2,0,0,0,0,0,0,1,0,2,0,0,0,0,0,0, +0,0,2,1,0,2,3,2,2,3,2,3,2,0,0,3,3,3,0,0,3,2,0,0,0,1,1,0,2,0,2,2, +0,2,0,2,0,2,2,0,0,2,0,2,2,2,0,2,2,2,2,0,0,2,0,0,0,2,0,1,0,0,0,0, +0,3,0,3,3,2,2,0,3,0,0,0,2,2,0,2,2,2,1,2,0,0,1,2,2,0,0,3,0,0,0,2, +0,1,2,0,0,0,1,2,0,0,0,0,0,0,0,2,2,0,1,0,0,2,0,0,0,2,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,2,3,3,2,2,0,0,0,2,0,2,3,3,0,2,0,0,0,0,0,0,2,2,2,0,2,2,0,2,0,2, +0,2,2,0,0,2,2,2,2,1,0,0,2,2,0,2,0,0,2,0,0,0,0,0,0,2,0,0,0,0,0,0, +0,2,0,3,2,3,0,0,0,3,0,0,2,2,0,2,0,2,2,2,0,0,2,0,0,0,0,0,0,0,0,2, +0,0,2,2,0,0,2,2,2,0,0,0,0,0,0,2,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,2,0,0,3,2,0,2,2,2,2,2,0,0,0,2,0,0,0,0,2,0,1,0,0,2,0,1,0,0,0, +0,2,2,2,0,2,2,0,1,2,0,2,2,2,0,2,2,2,2,1,2,2,0,0,2,0,0,0,0,0,0,0, +0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,2,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0, +0,2,0,2,0,2,2,0,0,0,0,1,2,1,0,0,2,2,0,0,2,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,3,2,3,0,0,2,0,0,0,2,2,0,2,0,0,0,1,0,0,2,0,2,0,2,2,0,0,0,0, +0,0,2,0,0,0,0,2,2,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0,2,0,0,0,0,0,0, +0,2,2,3,2,2,0,0,0,0,0,0,1,3,0,2,0,2,2,0,0,0,1,0,2,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,2,0,2,0,3,2,0,2,0,0,0,0,0,0,2,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1, +0,0,2,0,0,0,0,1,1,0,0,2,1,2,0,2,2,0,1,0,0,1,0,0,0,2,0,0,0,0,0,0, +0,3,0,2,2,2,0,0,2,0,0,0,2,0,0,0,2,3,0,2,0,0,0,0,0,0,2,2,0,0,0,2, +0,1,2,0,0,0,1,2,2,1,0,0,0,2,0,0,2,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,3,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,2,1,2,0,2,2,0,2,0,0,2,0,0,0,0,1,2,1,0,2,1,0,0,0,0,0,0,0,0,0,0, +0,0,2,0,0,0,3,1,2,2,0,2,0,0,0,0,2,0,0,0,2,0,0,3,0,0,0,0,2,2,2,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,2,1,0,2,0,1,2,0,0,0,0,0,0,0,0,0,0,0,0,0,2,0,0,1,0,0,0,0,0,0,2, +0,2,2,0,0,2,2,2,2,2,0,1,2,0,0,0,2,2,0,1,0,2,0,0,2,2,0,0,0,0,0,0, +0,0,0,0,1,0,0,0,0,0,0,0,3,0,0,2,0,0,0,0,0,0,0,0,2,0,2,0,0,0,0,2, +0,1,2,0,0,0,0,2,2,1,0,1,0,1,0,2,2,2,1,0,0,0,0,0,0,1,0,0,0,0,0,0, +0,2,0,1,2,0,0,0,0,0,0,0,0,0,0,2,0,0,2,2,0,0,0,0,1,0,0,0,0,0,0,2, +0,2,2,0,0,0,0,2,2,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0,2,0,0,2,0,0,0, +0,2,2,2,2,0,0,0,3,0,0,0,0,0,0,0,0,2,0,0,0,0,0,0,2,0,0,0,0,0,0,1, +0,0,2,0,0,0,0,1,2,0,0,0,0,0,0,2,2,1,1,0,0,0,0,0,0,1,0,0,0,0,0,0, +0,2,0,2,2,2,0,0,2,0,0,0,0,0,0,0,2,2,2,0,0,0,2,0,0,0,0,0,0,0,0,2, +0,0,1,0,0,0,0,2,1,0,0,0,0,0,0,1,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0, +0,3,0,2,0,0,0,0,0,0,0,0,2,0,0,0,0,0,2,0,0,0,0,0,0,0,2,0,0,0,0,2, +0,0,2,0,0,0,0,2,2,0,0,0,0,1,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,2,0,2,2,1,0,0,0,0,0,0,2,0,0,2,0,2,2,2,0,0,0,0,0,0,2,0,0,0,0,2, +0,0,2,0,0,2,0,2,2,0,0,0,0,2,0,2,0,0,0,0,0,2,0,0,0,2,0,0,0,0,0,0, +0,0,3,0,0,0,2,2,0,2,2,0,0,0,0,0,2,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,1,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,0,0,2,0,0,0,0,0, +0,2,2,2,2,2,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,0,0,0,1, +0,0,0,0,0,0,0,2,1,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,2,2,0,0,0,0,0,2,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0, +0,2,0,0,0,2,0,0,0,0,0,1,0,0,0,0,2,2,0,0,0,1,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,1,0,2,0,0,0, +0,2,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,1,0,0,0,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,1,0,0,2,0,2,0,0,0, +0,0,0,0,0,0,0,0,2,1,0,0,0,0,0,0,2,0,0,0,1,2,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +) + +Latin7GreekModel = { + 'char_to_order_map': Latin7_char_to_order_map, + 'precedence_matrix': GreekLangModel, + 'typical_positive_ratio': 0.982851, + 'keep_english_letter': False, + 'charset_name': "ISO-8859-7", + 'language': 'Greek', +} + +Win1253GreekModel = { + 'char_to_order_map': win1253_char_to_order_map, + 'precedence_matrix': GreekLangModel, + 'typical_positive_ratio': 0.982851, + 'keep_english_letter': False, + 'charset_name': "windows-1253", + 'language': 'Greek', +} diff --git a/venv2/lib/python2.7/site-packages/pip/_vendor/chardet/langhebrewmodel.py b/venv2/lib/python2.7/site-packages/pip/_vendor/chardet/langhebrewmodel.py new file mode 100644 index 0000000..58f4c87 --- /dev/null +++ b/venv2/lib/python2.7/site-packages/pip/_vendor/chardet/langhebrewmodel.py @@ -0,0 +1,200 @@ +######################## BEGIN LICENSE BLOCK ######################## +# The Original Code is Mozilla Universal charset detector code. +# +# The Initial Developer of the Original Code is +# Simon Montagu +# Portions created by the Initial Developer are Copyright (C) 2005 +# the Initial Developer. All Rights Reserved. +# +# Contributor(s): +# Mark Pilgrim - port to Python +# Shy Shalom - original C code +# Shoshannah Forbes - original C code (?) +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA +# 02110-1301 USA +######################### END LICENSE BLOCK ######################### + +# 255: Control characters that usually does not exist in any text +# 254: Carriage/Return +# 253: symbol (punctuation) that does not belong to word +# 252: 0 - 9 + +# Windows-1255 language model +# Character Mapping Table: +WIN1255_CHAR_TO_ORDER_MAP = ( +255,255,255,255,255,255,255,255,255,255,254,255,255,254,255,255, # 00 +255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, # 10 +253,253,253,253,253,253,253,253,253,253,253,253,253,253,253,253, # 20 +252,252,252,252,252,252,252,252,252,252,253,253,253,253,253,253, # 30 +253, 69, 91, 79, 80, 92, 89, 97, 90, 68,111,112, 82, 73, 95, 85, # 40 + 78,121, 86, 71, 67,102,107, 84,114,103,115,253,253,253,253,253, # 50 +253, 50, 74, 60, 61, 42, 76, 70, 64, 53,105, 93, 56, 65, 54, 49, # 60 + 66,110, 51, 43, 44, 63, 81, 77, 98, 75,108,253,253,253,253,253, # 70 +124,202,203,204,205, 40, 58,206,207,208,209,210,211,212,213,214, +215, 83, 52, 47, 46, 72, 32, 94,216,113,217,109,218,219,220,221, + 34,116,222,118,100,223,224,117,119,104,125,225,226, 87, 99,227, +106,122,123,228, 55,229,230,101,231,232,120,233, 48, 39, 57,234, + 30, 59, 41, 88, 33, 37, 36, 31, 29, 35,235, 62, 28,236,126,237, +238, 38, 45,239,240,241,242,243,127,244,245,246,247,248,249,250, + 9, 8, 20, 16, 3, 2, 24, 14, 22, 1, 25, 15, 4, 11, 6, 23, + 12, 19, 13, 26, 18, 27, 21, 17, 7, 10, 5,251,252,128, 96,253, +) + +# Model Table: +# total sequences: 100% +# first 512 sequences: 98.4004% +# first 1024 sequences: 1.5981% +# rest sequences: 0.087% +# negative sequences: 0.0015% +HEBREW_LANG_MODEL = ( +0,3,3,3,3,3,3,3,3,3,3,2,3,3,3,3,3,3,3,3,3,3,3,2,3,2,1,2,0,1,0,0, +3,0,3,1,0,0,1,3,2,0,1,1,2,0,2,2,2,1,1,1,1,2,1,1,1,2,0,0,2,2,0,1, +3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,2,2,2,2, +1,2,1,2,1,2,0,0,2,0,0,0,0,0,1,0,1,0,0,0,0,0,0,1,0,0,0,0,0,0,1,0, +3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,2,2,2, +1,2,1,3,1,1,0,0,2,0,0,0,1,0,1,0,1,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0, +3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,1,0,1,2,2,1,3, +1,2,1,1,2,2,0,0,2,2,0,0,0,0,1,0,1,0,0,0,1,0,0,0,0,0,0,1,0,1,1,0, +3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,2,3,3,2,2,2,2,3,2, +1,2,1,2,2,2,0,0,1,0,0,0,0,0,1,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,1,0, +3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,2,3,3,2,3,2,2,3,2,2,2,1,2,2,2,2, +1,2,1,1,2,2,0,1,2,0,0,0,0,0,0,0,1,0,0,0,1,0,0,0,0,0,0,0,0,0,1,0, +3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,2,0,2,2,2,2,2, +0,2,0,2,2,2,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,1,0, +3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,2,3,0,2,2,2, +0,2,1,2,2,2,0,0,2,1,0,0,0,0,1,0,1,0,0,0,0,0,0,2,0,0,0,0,0,0,1,0, +3,3,3,3,3,3,3,3,3,3,3,2,3,3,3,3,3,3,3,3,3,3,3,3,3,2,1,2,3,2,2,2, +1,2,1,2,2,2,0,0,1,0,0,0,0,0,1,0,0,0,0,0,0,0,0,1,0,0,0,0,0,1,1,0, +3,3,3,3,3,3,3,3,3,2,3,3,3,2,3,3,3,3,3,3,3,3,3,3,3,3,3,1,0,2,0,2, +0,2,1,2,2,2,0,0,1,2,0,0,0,0,1,0,1,0,0,0,0,0,0,1,0,0,0,2,0,0,1,0, +3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,2,3,2,3,2,2,3,2,1,2,1,1,1, +0,1,1,1,1,1,3,0,1,0,0,0,0,2,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0, +3,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,1,1,0,1,1,0,0,1,0,0,1,0,0,0,0, +0,0,1,0,0,0,0,0,2,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,2,2,2,2,2,2,2, +0,2,0,1,2,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0, +3,3,3,3,3,3,3,3,3,2,3,3,3,2,1,2,3,3,2,3,3,3,3,2,3,2,1,2,0,2,1,2, +0,2,0,2,2,2,0,0,1,2,0,0,0,0,1,0,1,0,0,0,0,0,0,0,0,0,0,1,0,0,1,0, +3,3,3,3,3,3,3,3,3,2,3,3,3,1,2,2,3,3,2,3,2,3,2,2,3,1,2,2,0,2,2,2, +0,2,1,2,2,2,0,0,1,2,0,0,0,0,1,0,0,0,0,0,1,0,0,1,0,0,0,1,0,0,1,0, +3,3,3,3,3,3,3,3,3,3,3,3,3,2,3,3,3,2,3,3,2,2,2,3,3,3,3,1,3,2,2,2, +0,2,0,1,2,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0, +3,3,3,3,3,3,3,3,3,3,3,3,3,3,2,2,3,3,3,2,3,2,2,2,1,2,2,0,2,2,2,2, +0,2,0,2,2,2,0,0,1,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0, +3,3,3,3,3,3,3,3,3,3,3,2,3,3,3,1,3,2,3,3,2,3,3,2,2,1,2,2,2,2,2,2, +0,2,1,2,1,2,0,0,1,0,0,0,0,0,1,0,0,0,0,0,1,0,0,1,0,0,0,0,0,0,1,0, +3,3,3,3,3,3,2,3,2,3,3,2,3,3,3,3,2,3,2,3,3,3,3,3,2,2,2,2,2,2,2,1, +0,2,0,1,2,1,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,1,0, +3,3,3,3,3,3,3,3,3,2,1,2,3,3,3,3,3,3,3,2,3,2,3,2,1,2,3,0,2,1,2,2, +0,2,1,1,2,1,0,0,1,0,0,0,0,0,1,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,2,0, +3,3,3,3,3,3,3,3,3,2,3,3,3,3,2,1,3,1,2,2,2,1,2,3,3,1,2,1,2,2,2,2, +0,1,1,1,1,1,0,0,0,0,0,0,0,0,1,0,0,0,0,0,1,0,0,2,0,0,0,0,0,0,0,0, +3,3,3,3,3,3,3,3,3,3,0,2,3,3,3,1,3,3,3,1,2,2,2,2,1,1,2,2,2,2,2,2, +0,2,0,1,1,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,1,0, +3,3,3,3,3,3,2,3,3,3,2,2,3,3,3,2,1,2,3,2,3,2,2,2,2,1,2,1,1,1,2,2, +0,2,1,1,1,1,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0, +3,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,1,0,0,0,1,0,0,0,0,0, +1,0,1,0,0,0,0,0,2,0,0,0,0,0,1,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +3,3,3,3,3,2,3,3,2,3,1,2,2,2,2,3,2,3,1,1,2,2,1,2,2,1,1,0,2,2,2,2, +0,1,0,1,2,2,0,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,1,0, +3,0,0,1,1,0,1,0,0,1,1,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,1,2,2,0, +0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +3,0,1,0,1,0,1,1,0,1,1,0,0,0,1,1,0,1,1,1,0,0,0,0,0,0,1,0,0,0,0,0, +0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +3,0,0,0,1,1,0,1,0,1,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0, +3,2,2,1,2,2,2,2,2,2,2,1,2,2,1,2,2,1,1,1,1,1,1,1,1,2,1,1,0,3,3,3, +0,3,0,2,2,2,2,0,0,1,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0, +2,2,2,3,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,1,2,2,1,2,2,2,1,1,1,2,0,1, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +2,2,2,2,2,2,2,2,2,2,2,1,2,2,2,2,2,2,2,2,2,2,2,0,2,2,0,0,0,0,0,0, +0,0,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +2,3,1,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,1,2,1,0,2,1,0, +0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +3,1,1,1,1,1,1,1,1,1,1,0,0,1,1,1,1,0,1,1,1,1,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0, +0,3,1,1,2,2,2,2,2,1,2,2,2,1,1,2,2,2,2,2,2,2,1,2,2,1,0,1,1,1,1,0, +0,1,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +3,2,1,1,1,1,2,1,1,2,1,0,1,1,1,1,1,1,1,1,1,1,1,0,1,0,0,0,0,0,0,0, +0,0,2,0,0,0,0,0,0,0,0,1,1,0,0,0,0,1,1,0,0,1,1,0,0,0,0,0,0,1,0,0, +2,1,1,2,2,2,2,2,2,2,2,2,2,2,1,2,2,2,2,2,1,2,1,2,1,1,1,1,0,0,0,0, +0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +1,2,1,2,2,2,2,2,2,2,2,2,2,1,2,1,2,1,1,2,1,1,1,2,1,2,1,2,0,1,0,1, +0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,3,1,2,2,2,1,2,2,2,2,2,2,2,2,1,2,1,1,1,1,1,1,2,1,2,1,1,0,1,0,1, +0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +2,1,2,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,2,2, +0,2,0,1,2,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0, +3,0,0,0,1,0,0,0,0,0,0,0,0,0,0,1,0,1,0,0,0,1,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +2,1,1,1,1,1,1,1,0,1,1,0,1,0,0,1,0,0,1,0,0,0,0,0,1,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,2,0,1,1,1,0,1,0,0,0,1,1,0,1,1,0,0,0,0,0,1,1,0,0, +0,1,1,1,2,1,2,2,2,0,2,0,2,0,1,1,2,1,1,1,1,2,1,0,1,1,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0, +1,0,1,0,0,0,0,0,1,0,1,2,2,0,1,0,0,1,1,2,2,1,2,0,2,0,0,0,1,2,0,1, +2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,2,0,2,1,2,0,2,0,0,1,1,1,1,1,1,0,1,0,0,0,1,0,0,1, +2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,1,0,0,0,0,0,1,0,2,1,1,0,1,0,0,1,1,1,2,2,0,0,1,0,0,0,1,0,0,1, +1,1,2,1,0,1,1,1,0,1,0,1,1,1,1,0,0,0,1,0,1,0,0,0,0,0,0,0,0,2,2,1, +0,2,0,1,2,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +2,1,0,0,1,0,1,1,1,1,0,0,0,0,0,1,0,0,0,0,1,1,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +1,1,1,1,1,1,1,1,1,2,1,0,1,1,1,1,1,1,1,1,1,1,1,0,1,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,1,1,1,0,0,0,0,1,1,1,0,1,1,0,1,0,0,0,1,1,0,1, +2,0,1,0,1,0,1,0,0,1,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,1,0,1,1,1,0,1,0,0,1,1,2,1,1,2,0,1,0,0,0,1,1,0,1, +1,0,0,1,0,0,1,0,0,0,1,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,1,0,1,1,2,0,1,0,0,0,0,2,1,1,2,0,2,0,0,0,1,1,0,1, +1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,1,0,2,1,1,0,1,0,0,2,2,1,2,1,1,0,1,0,0,0,1,1,0,1, +2,0,1,0,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,1,2,2,0,0,0,0,0,1,1,0,1,0,0,1,0,0,0,0,1,0,1, +1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,1,2,2,0,0,0,0,2,1,1,1,0,2,1,1,0,0,0,2,1,0,1, +1,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,1,0,1,1,2,0,1,0,0,1,1,0,2,1,1,0,1,0,0,0,1,1,0,1, +2,2,1,1,1,0,1,1,0,1,1,0,1,0,0,0,0,0,0,1,0,0,0,1,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,1,0,2,1,1,0,1,0,0,1,1,0,1,2,1,0,2,0,0,0,1,1,0,1, +2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,0,0,0,0,0, +0,1,0,0,2,0,2,1,1,0,1,0,1,0,0,1,0,0,0,0,1,0,0,0,1,0,0,0,0,0,1,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +1,0,0,1,0,0,1,0,0,1,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,1,0,1,1,2,0,1,0,0,1,1,1,0,1,0,0,1,0,0,0,1,0,0,1, +1,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +1,0,0,0,0,0,0,0,1,0,1,1,0,0,1,0,0,2,1,1,1,1,1,0,1,0,0,0,0,1,0,1, +0,1,1,1,2,1,1,1,1,0,1,1,1,1,1,1,1,1,1,1,1,1,0,1,1,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,1,2,1,0,0,0,0,0,1,1,1,1,1,0,1,0,0,0,1,1,0,0, +) + +Win1255HebrewModel = { + 'char_to_order_map': WIN1255_CHAR_TO_ORDER_MAP, + 'precedence_matrix': HEBREW_LANG_MODEL, + 'typical_positive_ratio': 0.984004, + 'keep_english_letter': False, + 'charset_name': "windows-1255", + 'language': 'Hebrew', +} diff --git a/venv2/lib/python2.7/site-packages/pip/_vendor/chardet/langhungarianmodel.py b/venv2/lib/python2.7/site-packages/pip/_vendor/chardet/langhungarianmodel.py new file mode 100644 index 0000000..bb7c095 --- /dev/null +++ b/venv2/lib/python2.7/site-packages/pip/_vendor/chardet/langhungarianmodel.py @@ -0,0 +1,225 @@ +######################## BEGIN LICENSE BLOCK ######################## +# The Original Code is Mozilla Communicator client code. +# +# The Initial Developer of the Original Code is +# Netscape Communications Corporation. +# Portions created by the Initial Developer are Copyright (C) 1998 +# the Initial Developer. All Rights Reserved. +# +# Contributor(s): +# Mark Pilgrim - port to Python +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA +# 02110-1301 USA +######################### END LICENSE BLOCK ######################### + +# 255: Control characters that usually does not exist in any text +# 254: Carriage/Return +# 253: symbol (punctuation) that does not belong to word +# 252: 0 - 9 + +# Character Mapping Table: +Latin2_HungarianCharToOrderMap = ( +255,255,255,255,255,255,255,255,255,255,254,255,255,254,255,255, # 00 +255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, # 10 +253,253,253,253,253,253,253,253,253,253,253,253,253,253,253,253, # 20 +252,252,252,252,252,252,252,252,252,252,253,253,253,253,253,253, # 30 +253, 28, 40, 54, 45, 32, 50, 49, 38, 39, 53, 36, 41, 34, 35, 47, + 46, 71, 43, 33, 37, 57, 48, 64, 68, 55, 52,253,253,253,253,253, +253, 2, 18, 26, 17, 1, 27, 12, 20, 9, 22, 7, 6, 13, 4, 8, + 23, 67, 10, 5, 3, 21, 19, 65, 62, 16, 11,253,253,253,253,253, +159,160,161,162,163,164,165,166,167,168,169,170,171,172,173,174, +175,176,177,178,179,180,181,182,183,184,185,186,187,188,189,190, +191,192,193,194,195,196,197, 75,198,199,200,201,202,203,204,205, + 79,206,207,208,209,210,211,212,213,214,215,216,217,218,219,220, +221, 51, 81,222, 78,223,224,225,226, 44,227,228,229, 61,230,231, +232,233,234, 58,235, 66, 59,236,237,238, 60, 69, 63,239,240,241, + 82, 14, 74,242, 70, 80,243, 72,244, 15, 83, 77, 84, 30, 76, 85, +245,246,247, 25, 73, 42, 24,248,249,250, 31, 56, 29,251,252,253, +) + +win1250HungarianCharToOrderMap = ( +255,255,255,255,255,255,255,255,255,255,254,255,255,254,255,255, # 00 +255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, # 10 +253,253,253,253,253,253,253,253,253,253,253,253,253,253,253,253, # 20 +252,252,252,252,252,252,252,252,252,252,253,253,253,253,253,253, # 30 +253, 28, 40, 54, 45, 32, 50, 49, 38, 39, 53, 36, 41, 34, 35, 47, + 46, 72, 43, 33, 37, 57, 48, 64, 68, 55, 52,253,253,253,253,253, +253, 2, 18, 26, 17, 1, 27, 12, 20, 9, 22, 7, 6, 13, 4, 8, + 23, 67, 10, 5, 3, 21, 19, 65, 62, 16, 11,253,253,253,253,253, +161,162,163,164,165,166,167,168,169,170,171,172,173,174,175,176, +177,178,179,180, 78,181, 69,182,183,184,185,186,187,188,189,190, +191,192,193,194,195,196,197, 76,198,199,200,201,202,203,204,205, + 81,206,207,208,209,210,211,212,213,214,215,216,217,218,219,220, +221, 51, 83,222, 80,223,224,225,226, 44,227,228,229, 61,230,231, +232,233,234, 58,235, 66, 59,236,237,238, 60, 70, 63,239,240,241, + 84, 14, 75,242, 71, 82,243, 73,244, 15, 85, 79, 86, 30, 77, 87, +245,246,247, 25, 74, 42, 24,248,249,250, 31, 56, 29,251,252,253, +) + +# Model Table: +# total sequences: 100% +# first 512 sequences: 94.7368% +# first 1024 sequences:5.2623% +# rest sequences: 0.8894% +# negative sequences: 0.0009% +HungarianLangModel = ( +0,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,1,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3, +3,3,3,3,3,3,3,3,3,3,2,3,3,3,3,3,3,3,3,2,2,3,3,1,1,2,2,2,2,2,1,2, +3,2,2,3,3,3,3,3,2,3,3,3,3,3,3,1,2,3,3,3,3,2,3,3,1,1,3,3,0,1,1,1, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,0, +3,2,1,3,3,3,3,3,2,3,3,3,3,3,1,1,2,3,3,3,3,3,3,3,1,1,3,2,0,1,1,1, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0, +3,3,3,3,3,3,3,3,3,3,3,1,1,2,3,3,3,1,3,3,3,3,3,1,3,3,2,2,0,3,2,3, +0,0,0,0,0,0,0,0,0,0,3,0,0,0,0,0,0,0,0,0,0,0,0,0,2,0,0,0,0,0,0,0, +3,3,3,3,3,3,2,3,3,3,2,3,3,2,3,3,3,3,3,2,3,3,2,2,3,2,3,2,0,3,2,2, +0,0,0,0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,1,0, +3,3,3,3,3,3,2,3,3,3,3,3,2,3,3,3,1,2,3,2,2,3,1,2,3,3,2,2,0,3,3,3, +0,0,0,0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0, +3,3,3,3,3,3,3,3,3,3,2,2,3,3,3,3,3,3,2,3,3,3,3,2,3,3,3,3,0,2,3,2, +0,0,0,1,1,0,0,0,0,0,3,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0, +3,3,3,3,3,3,3,3,3,3,3,1,1,1,3,3,2,1,3,2,2,3,2,1,3,2,2,1,0,3,3,1, +0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0, +3,2,2,3,3,3,3,3,1,2,3,3,3,3,1,2,1,3,3,3,3,2,2,3,1,1,3,2,0,1,1,1, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0, +3,3,3,3,3,3,3,3,2,2,3,3,3,3,3,2,1,3,3,3,3,3,2,2,1,3,3,3,0,1,1,2, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,1,0, +3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,2,3,3,3,2,3,3,2,3,3,3,2,0,3,2,3, +0,0,0,0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,2,0,0,0,0,0,1,0, +3,3,3,3,3,3,2,3,3,3,2,3,2,3,3,3,1,3,2,2,2,3,1,1,3,3,1,1,0,3,3,2, +0,0,0,0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0, +3,3,3,3,3,3,3,2,3,3,3,2,3,2,3,3,3,2,3,3,3,3,3,1,2,3,2,2,0,2,2,2, +0,0,0,0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0, +3,3,3,2,2,2,3,1,3,3,2,2,1,3,3,3,1,1,3,1,2,3,2,3,2,2,2,1,0,2,2,2, +0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,2,0,0,0,0,0,0,0, +3,1,1,3,3,3,3,3,1,2,3,3,3,3,1,2,1,3,3,3,2,2,3,2,1,0,3,2,0,1,1,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +3,1,1,3,3,3,3,3,1,2,3,3,3,3,1,1,0,3,3,3,3,0,2,3,0,0,2,1,0,1,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +3,3,3,3,3,3,2,2,3,3,2,2,2,2,3,3,0,1,2,3,2,3,2,2,3,2,1,2,0,2,2,2, +0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,2,0,0,0,0,0,0,0, +3,3,3,3,3,3,1,2,3,3,3,2,1,2,3,3,2,2,2,3,2,3,3,1,3,3,1,1,0,2,3,2, +0,0,0,0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0, +3,3,3,1,2,2,2,2,3,3,3,1,1,1,3,3,1,1,3,1,1,3,2,1,2,3,1,1,0,2,2,2, +0,0,0,0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0, +3,3,3,2,1,2,1,1,3,3,1,1,1,1,3,3,1,1,2,2,1,2,1,1,2,2,1,1,0,2,2,1, +0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0, +3,3,3,1,1,2,1,1,3,3,1,0,1,1,3,3,2,0,1,1,2,3,1,0,2,2,1,0,0,1,3,2, +0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0, +3,2,1,3,3,3,3,3,1,2,3,2,3,3,2,1,1,3,2,3,2,1,2,2,0,1,2,1,0,0,1,1, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0, +3,3,3,3,2,2,2,2,3,1,2,2,1,1,3,3,0,3,2,1,2,3,2,1,3,3,1,1,0,2,1,3, +0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0, +3,3,3,2,2,2,3,2,3,3,3,2,1,1,3,3,1,1,1,2,2,3,2,3,2,2,2,1,0,2,2,1, +0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0, +1,0,0,3,3,3,3,3,0,0,3,3,2,3,0,0,0,2,3,3,1,0,1,2,0,0,1,1,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +3,1,2,3,3,3,3,3,1,2,3,3,2,2,1,1,0,3,3,2,2,1,2,2,1,0,2,2,0,1,1,1, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +3,3,2,2,1,3,1,2,3,3,2,2,1,1,2,2,1,1,1,1,3,2,1,1,1,1,2,1,0,1,2,1, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,1,0,0,0,0,0,0,0,0,0, +2,3,3,1,1,1,1,1,3,3,3,0,1,1,3,3,1,1,1,1,1,2,2,0,3,1,1,2,0,2,1,1, +0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0, +3,1,0,1,2,1,2,2,0,1,2,3,1,2,0,0,0,2,1,1,1,1,1,2,0,0,1,1,0,0,0,0, +1,2,1,2,2,2,1,2,1,2,0,2,0,2,2,1,1,2,1,1,2,1,1,1,0,1,0,0,0,1,1,0, +1,1,1,2,3,2,3,3,0,1,2,2,3,1,0,1,0,2,1,2,2,0,1,1,0,0,1,1,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +1,0,0,3,3,2,2,1,0,0,3,2,3,2,0,0,0,1,1,3,0,0,1,1,0,0,2,1,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +3,1,1,2,2,3,3,1,0,1,3,2,3,1,1,1,0,1,1,1,1,1,3,1,0,0,2,2,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +3,1,1,1,2,2,2,1,0,1,2,3,3,2,0,0,0,2,1,1,1,2,1,1,1,0,1,1,1,0,0,0, +1,2,2,2,2,2,1,1,1,2,0,2,1,1,1,1,1,2,1,1,1,1,1,1,0,1,1,1,0,0,1,1, +3,2,2,1,0,0,1,1,2,2,0,3,0,1,2,1,1,0,0,1,1,1,0,1,1,1,1,0,2,1,1,1, +2,2,1,1,1,2,1,2,1,1,1,1,1,1,1,2,1,1,1,2,3,1,1,1,1,1,1,1,1,1,0,1, +2,3,3,0,1,0,0,0,3,3,1,0,0,1,2,2,1,0,0,0,0,2,0,0,1,1,1,0,2,1,1,1, +2,1,1,1,1,1,1,2,1,1,0,1,1,0,1,1,1,0,1,2,1,1,0,1,1,1,1,1,1,1,0,1, +2,3,3,0,1,0,0,0,2,2,0,0,0,0,1,2,2,0,0,0,0,1,0,0,1,1,0,0,2,0,1,0, +2,1,1,1,1,2,1,1,1,1,1,1,1,2,1,1,1,1,1,1,1,1,1,2,0,1,1,1,1,1,0,1, +3,2,2,0,1,0,1,0,2,3,2,0,0,1,2,2,1,0,0,1,1,1,0,0,2,1,0,1,2,2,1,1, +2,1,1,1,1,1,1,2,1,1,1,1,1,1,0,2,1,0,1,1,0,1,1,1,0,1,1,2,1,1,0,1, +2,2,2,0,0,1,0,0,2,2,1,1,0,0,2,1,1,0,0,0,1,2,0,0,2,1,0,0,2,1,1,1, +2,1,1,1,1,2,1,2,1,1,1,2,2,1,1,2,1,1,1,2,1,1,1,1,1,1,1,1,1,1,0,1, +1,2,3,0,0,0,1,0,3,2,1,0,0,1,2,1,1,0,0,0,0,2,1,0,1,1,0,0,2,1,2,1, +1,1,0,0,0,1,0,1,1,1,1,1,2,0,0,1,0,0,0,2,0,0,1,1,1,1,1,1,1,1,0,1, +3,0,0,2,1,2,2,1,0,0,2,1,2,2,0,0,0,2,1,1,1,0,1,1,0,0,1,1,2,0,0,0, +1,2,1,2,2,1,1,2,1,2,0,1,1,1,1,1,1,1,1,1,2,1,1,0,0,1,1,1,1,0,0,1, +1,3,2,0,0,0,1,0,2,2,2,0,0,0,2,2,1,0,0,0,0,3,1,1,1,1,0,0,2,1,1,1, +2,1,0,1,1,1,0,1,1,1,1,1,1,1,0,2,1,0,0,1,0,1,1,0,1,1,1,1,1,1,0,1, +2,3,2,0,0,0,1,0,2,2,0,0,0,0,2,1,1,0,0,0,0,2,1,0,1,1,0,0,2,1,1,0, +2,1,1,1,1,2,1,2,1,2,0,1,1,1,0,2,1,1,1,2,1,1,1,1,0,1,1,1,1,1,0,1, +3,1,1,2,2,2,3,2,1,1,2,2,1,1,0,1,0,2,2,1,1,1,1,1,0,0,1,1,0,1,1,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +2,2,2,0,0,0,0,0,2,2,0,0,0,0,2,2,1,0,0,0,1,1,0,0,1,2,0,0,2,1,1,1, +2,2,1,1,1,2,1,2,1,1,0,1,1,1,1,2,1,1,1,2,1,1,1,1,0,1,2,1,1,1,0,1, +1,0,0,1,2,3,2,1,0,0,2,0,1,1,0,0,0,1,1,1,1,0,1,1,0,0,1,0,0,0,0,0, +1,2,1,2,1,2,1,1,1,2,0,2,1,1,1,0,1,2,0,0,1,1,1,0,0,0,0,0,0,0,0,0, +2,3,2,0,0,0,0,0,1,1,2,1,0,0,1,1,1,0,0,0,0,2,0,0,1,1,0,0,2,1,1,1, +2,1,1,1,1,1,1,2,1,0,1,1,1,1,0,2,1,1,1,1,1,1,0,1,0,1,1,1,1,1,0,1, +1,2,2,0,1,1,1,0,2,2,2,0,0,0,3,2,1,0,0,0,1,1,0,0,1,1,0,1,1,1,0,0, +1,1,0,1,1,1,1,1,1,1,1,2,1,1,1,1,1,1,1,2,1,1,1,0,0,1,1,1,0,1,0,1, +2,1,0,2,1,1,2,2,1,1,2,1,1,1,0,0,0,1,1,0,1,1,1,1,0,0,1,1,1,0,0,0, +1,2,2,2,2,2,1,1,1,2,0,2,1,1,1,1,1,1,1,1,1,1,1,1,0,1,1,0,0,0,1,0, +1,2,3,0,0,0,1,0,2,2,0,0,0,0,2,2,0,0,0,0,0,1,0,0,1,0,0,0,2,0,1,0, +2,1,1,1,1,1,0,2,0,0,0,1,2,1,1,1,1,0,1,2,0,1,0,1,0,1,1,1,0,1,0,1, +2,2,2,0,0,0,1,0,2,1,2,0,0,0,1,1,2,0,0,0,0,1,0,0,1,1,0,0,2,1,0,1, +2,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,2,0,1,1,1,1,1,0,1, +1,2,2,0,0,0,1,0,2,2,2,0,0,0,1,1,0,0,0,0,0,1,1,0,2,0,0,1,1,1,0,1, +1,0,1,1,1,1,1,1,0,1,1,1,1,0,0,1,0,0,1,1,0,1,0,1,1,1,1,1,0,0,0,1, +1,0,0,1,0,1,2,1,0,0,1,1,1,2,0,0,0,1,1,0,1,0,1,1,0,0,1,0,0,0,0,0, +0,2,1,2,1,1,1,1,1,2,0,2,0,1,1,0,1,2,1,0,1,1,1,0,0,0,0,0,0,1,0,0, +2,1,1,0,1,2,0,0,1,1,1,0,0,0,1,1,0,0,0,0,0,1,0,0,1,0,0,0,2,1,0,1, +2,2,1,1,1,1,1,2,1,1,0,1,1,1,1,2,1,1,1,2,1,1,0,1,0,1,1,1,1,1,0,1, +1,2,2,0,0,0,0,0,1,1,0,0,0,0,2,1,0,0,0,0,0,2,0,0,2,2,0,0,2,0,0,1, +2,1,1,1,1,1,1,1,0,1,1,0,1,1,0,1,0,0,0,1,1,1,1,0,0,1,1,1,1,0,0,1, +1,1,2,0,0,3,1,0,2,1,1,1,0,0,1,1,1,0,0,0,1,1,0,0,0,1,0,0,1,0,1,0, +1,2,1,0,1,1,1,2,1,1,0,1,1,1,1,1,0,0,0,1,1,1,1,1,0,1,0,0,0,1,0,0, +2,1,1,0,0,0,0,0,1,0,0,0,0,0,0,0,0,1,0,1,0,0,0,1,0,0,0,0,2,0,0,0, +2,1,1,1,1,1,1,1,1,1,0,1,1,1,1,1,1,1,1,1,2,1,1,0,0,1,1,1,1,1,0,1, +2,1,1,1,2,1,1,1,0,1,1,2,1,0,0,0,0,1,1,1,1,0,1,0,0,0,0,1,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +1,1,0,1,1,1,1,1,0,0,1,1,2,1,0,0,0,1,1,0,0,0,1,1,0,0,1,0,1,0,0,0, +1,2,1,1,1,1,1,1,1,1,0,1,0,1,1,1,1,1,1,0,1,1,1,0,0,0,0,0,0,1,0,0, +2,0,0,0,1,1,1,1,0,0,1,1,0,0,0,0,0,1,1,1,2,0,0,1,0,0,1,0,1,0,0,0, +0,1,1,1,1,1,1,1,1,2,0,1,1,1,1,0,1,1,1,0,1,1,1,0,0,0,0,0,0,0,0,0, +1,0,0,1,1,1,1,1,0,0,2,1,0,1,0,0,0,1,0,1,0,0,0,0,0,0,1,0,0,0,0,0, +0,1,1,1,1,1,1,0,1,1,0,1,0,1,1,0,1,1,0,0,1,1,1,0,0,0,0,0,0,0,0,0, +1,0,0,1,1,1,0,0,0,0,1,0,2,0,0,0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0, +0,1,1,1,1,1,0,0,1,1,0,1,0,1,0,0,1,1,1,0,1,1,1,0,0,0,0,0,0,0,0,0, +0,0,0,1,0,0,0,0,0,0,1,1,2,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,1,1,1,0,1,0,0,1,1,0,1,0,1,1,0,1,1,1,0,1,1,1,0,0,0,0,0,0,0,0,0, +2,1,1,1,1,1,1,1,1,1,1,0,0,1,1,1,0,0,1,0,0,1,0,1,0,1,1,1,0,0,1,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +1,0,0,1,1,1,1,0,0,0,1,1,1,0,0,0,0,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0, +0,1,1,1,1,1,1,0,1,1,0,1,0,1,0,0,1,1,0,0,1,1,0,0,0,0,0,0,0,0,0,0, +) + +Latin2HungarianModel = { + 'char_to_order_map': Latin2_HungarianCharToOrderMap, + 'precedence_matrix': HungarianLangModel, + 'typical_positive_ratio': 0.947368, + 'keep_english_letter': True, + 'charset_name': "ISO-8859-2", + 'language': 'Hungarian', +} + +Win1250HungarianModel = { + 'char_to_order_map': win1250HungarianCharToOrderMap, + 'precedence_matrix': HungarianLangModel, + 'typical_positive_ratio': 0.947368, + 'keep_english_letter': True, + 'charset_name': "windows-1250", + 'language': 'Hungarian', +} diff --git a/venv2/lib/python2.7/site-packages/pip/_vendor/chardet/langthaimodel.py b/venv2/lib/python2.7/site-packages/pip/_vendor/chardet/langthaimodel.py new file mode 100644 index 0000000..15f94c2 --- /dev/null +++ b/venv2/lib/python2.7/site-packages/pip/_vendor/chardet/langthaimodel.py @@ -0,0 +1,199 @@ +######################## BEGIN LICENSE BLOCK ######################## +# The Original Code is Mozilla Communicator client code. +# +# The Initial Developer of the Original Code is +# Netscape Communications Corporation. +# Portions created by the Initial Developer are Copyright (C) 1998 +# the Initial Developer. All Rights Reserved. +# +# Contributor(s): +# Mark Pilgrim - port to Python +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA +# 02110-1301 USA +######################### END LICENSE BLOCK ######################### + +# 255: Control characters that usually does not exist in any text +# 254: Carriage/Return +# 253: symbol (punctuation) that does not belong to word +# 252: 0 - 9 + +# The following result for thai was collected from a limited sample (1M). + +# Character Mapping Table: +TIS620CharToOrderMap = ( +255,255,255,255,255,255,255,255,255,255,254,255,255,254,255,255, # 00 +255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, # 10 +253,253,253,253,253,253,253,253,253,253,253,253,253,253,253,253, # 20 +252,252,252,252,252,252,252,252,252,252,253,253,253,253,253,253, # 30 +253,182,106,107,100,183,184,185,101, 94,186,187,108,109,110,111, # 40 +188,189,190, 89, 95,112,113,191,192,193,194,253,253,253,253,253, # 50 +253, 64, 72, 73,114, 74,115,116,102, 81,201,117, 90,103, 78, 82, # 60 + 96,202, 91, 79, 84,104,105, 97, 98, 92,203,253,253,253,253,253, # 70 +209,210,211,212,213, 88,214,215,216,217,218,219,220,118,221,222, +223,224, 99, 85, 83,225,226,227,228,229,230,231,232,233,234,235, +236, 5, 30,237, 24,238, 75, 8, 26, 52, 34, 51,119, 47, 58, 57, + 49, 53, 55, 43, 20, 19, 44, 14, 48, 3, 17, 25, 39, 62, 31, 54, + 45, 9, 16, 2, 61, 15,239, 12, 42, 46, 18, 21, 76, 4, 66, 63, + 22, 10, 1, 36, 23, 13, 40, 27, 32, 35, 86,240,241,242,243,244, + 11, 28, 41, 29, 33,245, 50, 37, 6, 7, 67, 77, 38, 93,246,247, + 68, 56, 59, 65, 69, 60, 70, 80, 71, 87,248,249,250,251,252,253, +) + +# Model Table: +# total sequences: 100% +# first 512 sequences: 92.6386% +# first 1024 sequences:7.3177% +# rest sequences: 1.0230% +# negative sequences: 0.0436% +ThaiLangModel = ( +0,1,3,3,3,3,0,0,3,3,0,3,3,0,3,3,3,3,3,3,3,3,0,0,3,3,3,0,3,3,3,3, +0,3,3,0,0,0,1,3,0,3,3,2,3,3,0,1,2,3,3,3,3,0,2,0,2,0,0,3,2,1,2,2, +3,0,3,3,2,3,0,0,3,3,0,3,3,0,3,3,3,3,3,3,3,3,3,0,3,2,3,0,2,2,2,3, +0,2,3,0,0,0,0,1,0,1,2,3,1,1,3,2,2,0,1,1,0,0,1,0,0,0,0,0,0,0,1,1, +3,3,3,2,3,3,3,3,3,3,3,3,3,3,3,2,2,2,2,2,2,2,3,3,2,3,2,3,3,2,2,2, +3,1,2,3,0,3,3,2,2,1,2,3,3,1,2,0,1,3,0,1,0,0,1,0,0,0,0,0,0,0,1,1, +3,3,2,2,3,3,3,3,1,2,3,3,3,3,3,2,2,2,2,3,3,2,2,3,3,2,2,3,2,3,2,2, +3,3,1,2,3,1,2,2,3,3,1,0,2,1,0,0,3,1,2,1,0,0,1,0,0,0,0,0,0,1,0,1, +3,3,3,3,3,3,2,2,3,3,3,3,2,3,2,2,3,3,2,2,3,2,2,2,2,1,1,3,1,2,1,1, +3,2,1,0,2,1,0,1,0,1,1,0,1,1,0,0,1,0,1,0,0,0,1,0,0,0,0,0,0,0,0,0, +3,3,3,2,3,2,3,3,2,2,3,2,3,3,2,3,1,1,2,3,2,2,2,3,2,2,2,2,2,1,2,1, +2,2,1,1,3,3,2,1,0,1,2,2,0,1,3,0,0,0,1,1,0,0,0,0,0,2,3,0,0,2,1,1, +3,3,2,3,3,2,0,0,3,3,0,3,3,0,2,2,3,1,2,2,1,1,1,0,2,2,2,0,2,2,1,1, +0,2,1,0,2,0,0,2,0,1,0,0,1,0,0,0,1,1,1,1,0,0,0,0,0,0,0,0,0,0,1,0, +3,3,2,3,3,2,0,0,3,3,0,2,3,0,2,1,2,2,2,2,1,2,0,0,2,2,2,0,2,2,1,1, +0,2,1,0,2,0,0,2,0,1,1,0,1,0,0,0,0,0,0,1,0,0,1,0,0,0,0,0,0,0,0,0, +3,3,2,3,2,3,2,0,2,2,1,3,2,1,3,2,1,2,3,2,2,3,0,2,3,2,2,1,2,2,2,2, +1,2,2,0,0,0,0,2,0,1,2,0,1,1,1,0,1,0,3,1,1,0,0,0,0,0,0,0,0,0,1,0, +3,3,2,3,3,2,3,2,2,2,3,2,2,3,2,2,1,2,3,2,2,3,1,3,2,2,2,3,2,2,2,3, +3,2,1,3,0,1,1,1,0,2,1,1,1,1,1,0,1,0,1,1,0,0,0,0,0,0,0,0,0,2,0,0, +1,0,0,3,0,3,3,3,3,3,0,0,3,0,2,2,3,3,3,3,3,0,0,0,1,1,3,0,0,0,0,2, +0,0,1,0,0,0,0,0,0,0,2,3,0,0,0,3,0,2,0,0,0,0,0,3,0,0,0,0,0,0,0,0, +2,0,3,3,3,3,0,0,2,3,0,0,3,0,3,3,2,3,3,3,3,3,0,0,3,3,3,0,0,0,3,3, +0,0,3,0,0,0,0,2,0,0,2,1,1,3,0,0,1,0,0,2,3,0,1,0,0,0,0,0,0,0,1,0, +3,3,3,3,2,3,3,3,3,3,3,3,1,2,1,3,3,2,2,1,2,2,2,3,1,1,2,0,2,1,2,1, +2,2,1,0,0,0,1,1,0,1,0,1,1,0,0,0,0,0,1,1,0,0,1,0,0,0,0,0,0,0,0,0, +3,0,2,1,2,3,3,3,0,2,0,2,2,0,2,1,3,2,2,1,2,1,0,0,2,2,1,0,2,1,2,2, +0,1,1,0,0,0,0,1,0,1,1,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0, +3,3,3,3,2,1,3,3,1,1,3,0,2,3,1,1,3,2,1,1,2,0,2,2,3,2,1,1,1,1,1,2, +3,0,0,1,3,1,2,1,2,0,3,0,0,0,1,0,3,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0, +3,3,1,1,3,2,3,3,3,1,3,2,1,3,2,1,3,2,2,2,2,1,3,3,1,2,1,3,1,2,3,0, +2,1,1,3,2,2,2,1,2,1,0,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2, +3,3,2,3,2,3,3,2,3,2,3,2,3,3,2,1,0,3,2,2,2,1,2,2,2,1,2,2,1,2,1,1, +2,2,2,3,0,1,3,1,1,1,1,0,1,1,0,2,1,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0, +3,3,3,3,2,3,2,2,1,1,3,2,3,2,3,2,0,3,2,2,1,2,0,2,2,2,1,2,2,2,2,1, +3,2,1,2,2,1,0,2,0,1,0,0,1,1,0,0,0,0,0,1,1,0,1,0,0,0,0,0,0,0,0,1, +3,3,3,3,3,2,3,1,2,3,3,2,2,3,0,1,1,2,0,3,3,2,2,3,0,1,1,3,0,0,0,0, +3,1,0,3,3,0,2,0,2,1,0,0,3,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +3,3,3,2,3,2,3,3,0,1,3,1,1,2,1,2,1,1,3,1,1,0,2,3,1,1,1,1,1,1,1,1, +3,1,1,2,2,2,2,1,1,1,0,0,2,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1, +3,2,2,1,1,2,1,3,3,2,3,2,2,3,2,2,3,1,2,2,1,2,0,3,2,1,2,2,2,2,2,1, +3,2,1,2,2,2,1,1,1,1,0,0,1,1,0,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0, +3,3,3,3,3,3,3,3,1,3,3,0,2,1,0,3,2,0,0,3,1,0,1,1,0,1,0,0,0,0,0,1, +1,0,0,1,0,3,2,0,0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +3,0,2,2,2,3,0,0,1,3,0,3,2,0,3,2,2,3,3,3,3,3,1,0,2,2,2,0,2,2,1,2, +0,2,3,0,0,0,0,1,0,1,0,0,1,1,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1, +3,0,2,3,1,3,3,2,3,3,0,3,3,0,3,2,2,3,2,3,3,3,0,0,2,2,3,0,1,1,1,3, +0,0,3,0,0,0,2,2,0,1,3,0,1,2,2,2,3,0,0,0,0,0,1,0,0,0,0,0,0,0,0,1, +3,2,3,3,2,0,3,3,2,2,3,1,3,2,1,3,2,0,1,2,2,0,2,3,2,1,0,3,0,0,0,0, +3,0,0,2,3,1,3,0,0,3,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +3,1,3,2,2,2,1,2,0,1,3,1,1,3,1,3,0,0,2,1,1,1,1,2,1,1,1,0,2,1,0,1, +1,2,0,0,0,3,1,1,0,0,0,0,1,0,1,0,0,1,0,1,0,0,0,0,0,3,1,0,0,0,1,0, +3,3,3,3,2,2,2,2,2,1,3,1,1,1,2,0,1,1,2,1,2,1,3,2,0,0,3,1,1,1,1,1, +3,1,0,2,3,0,0,0,3,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,2,3,0,3,3,0,2,0,0,0,0,0,0,0,3,0,0,1,0,0,0,0,0,0,0,0,0,0,0, +0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,2,3,1,3,0,0,1,2,0,0,2,0,3,3,2,3,3,3,2,3,0,0,2,2,2,0,0,0,2,2, +0,0,1,0,0,0,0,3,0,0,0,0,2,0,0,0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0, +0,0,0,3,0,2,0,0,0,0,0,0,0,0,0,0,1,2,3,1,3,3,0,0,1,0,3,0,0,0,0,0, +0,0,3,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +3,3,1,2,3,1,2,3,1,0,3,0,2,2,1,0,2,1,1,2,0,1,0,0,1,1,1,1,0,1,0,0, +1,0,0,0,0,1,1,0,3,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +3,3,3,3,2,1,0,1,1,1,3,1,2,2,2,2,2,2,1,1,1,1,0,3,1,0,1,3,1,1,1,1, +1,1,0,2,0,1,3,1,1,0,0,1,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,2,0,1, +3,0,2,2,1,3,3,2,3,3,0,1,1,0,2,2,1,2,1,3,3,1,0,0,3,2,0,0,0,0,2,1, +0,1,0,0,0,0,1,2,0,1,1,3,1,1,2,2,1,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0, +0,0,3,0,0,1,0,0,0,3,0,0,3,0,3,1,0,1,1,1,3,2,0,0,0,3,0,0,0,0,2,0, +0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,2,0,0,0,0,0,0,0,0,0, +3,3,1,3,2,1,3,3,1,2,2,0,1,2,1,0,1,2,0,0,0,0,0,3,0,0,0,3,0,0,0,0, +3,0,0,1,1,1,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +3,0,1,2,0,3,3,3,2,2,0,1,1,0,1,3,0,0,0,2,2,0,0,0,0,3,1,0,1,0,0,0, +0,0,0,0,0,0,0,0,0,1,0,1,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +3,0,2,3,1,2,0,0,2,1,0,3,1,0,1,2,0,1,1,1,1,3,0,0,3,1,1,0,2,2,1,1, +0,2,0,0,0,0,0,1,0,1,0,0,1,1,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +3,0,0,3,1,2,0,0,2,2,0,1,2,0,1,0,1,3,1,2,1,0,0,0,2,0,3,0,0,0,1,0, +0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +3,0,1,1,2,2,0,0,0,2,0,2,1,0,1,1,0,1,1,1,2,1,0,0,1,1,1,0,2,1,1,1, +0,1,1,0,0,0,0,0,0,1,0,0,1,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,1,0,1, +0,0,0,2,0,1,3,1,1,1,1,0,0,0,0,3,2,0,1,0,0,0,1,2,0,0,0,1,0,0,0,0, +0,0,0,3,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,3,3,3,3,1,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +1,0,2,3,2,2,0,0,0,1,0,0,0,0,2,3,2,1,2,2,3,0,0,0,2,3,1,0,0,0,1,1, +0,0,1,0,0,0,0,0,0,0,1,0,0,1,0,0,0,0,0,1,1,0,1,0,0,0,0,0,0,0,0,0, +3,3,2,2,0,1,0,0,0,0,2,0,2,0,1,0,0,0,1,1,0,0,0,2,1,0,1,0,1,1,0,0, +0,1,0,2,0,0,1,0,3,0,1,0,0,0,2,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +3,3,1,0,0,1,0,0,0,0,0,1,1,2,0,0,0,0,1,0,0,1,3,1,0,0,0,0,1,1,0,0, +0,1,0,0,0,0,3,0,0,0,0,0,0,3,0,0,0,0,0,0,0,3,0,0,0,0,0,0,0,0,0,0, +3,3,1,1,1,1,2,3,0,0,2,1,1,1,1,1,0,2,1,1,0,0,0,2,1,0,1,2,1,1,0,1, +2,1,0,3,0,0,0,0,3,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +1,3,1,0,0,0,0,0,0,0,3,0,0,0,3,0,0,0,0,0,0,0,0,1,1,0,0,0,0,0,0,1, +0,0,0,2,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +3,3,2,0,0,0,0,0,0,1,2,1,0,1,1,0,2,0,0,1,0,0,2,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,2,0,0,0,1,3,0,1,0,0,0,2,0,0,0,0,0,0,0,1,2,0,0,0,0,0, +3,3,0,0,1,1,2,0,0,1,2,1,0,1,1,1,0,1,1,0,0,2,1,1,0,1,0,0,1,1,1,0, +0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,3,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0, +2,2,2,1,0,0,0,0,1,0,0,0,0,3,0,0,0,0,0,0,0,0,0,3,0,0,0,0,0,0,0,0, +2,0,0,0,0,0,3,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +2,3,0,0,1,1,0,0,0,2,0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,1,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +3,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +1,1,0,1,2,0,1,2,0,0,1,1,0,2,0,1,0,0,1,0,0,0,0,1,0,0,0,2,0,0,0,0, +1,0,0,1,0,1,1,0,3,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,1,0,0,0,0,0,0,0,1,1,0,1,1,0,2,1,3,0,0,0,0,1,1,0,0,0,0,0,0,0,3, +1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,3,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +2,0,1,0,1,0,0,2,0,0,2,0,0,1,1,2,0,0,1,1,0,0,0,1,0,0,0,1,1,0,0,0, +1,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0, +1,0,0,3,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,1, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +3,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,0,0,1,1,0,0,0, +2,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,3,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +2,0,0,0,0,2,0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,1,0,1,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,1,3,0,0,0, +2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,0,0,1,0,0,0,0, +1,0,0,0,0,0,0,0,0,1,0,0,0,0,2,0,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,1,1,0,0,2,1,0,0,1,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +) + +TIS620ThaiModel = { + 'char_to_order_map': TIS620CharToOrderMap, + 'precedence_matrix': ThaiLangModel, + 'typical_positive_ratio': 0.926386, + 'keep_english_letter': False, + 'charset_name': "TIS-620", + 'language': 'Thai', +} diff --git a/venv2/lib/python2.7/site-packages/pip/_vendor/chardet/langturkishmodel.py b/venv2/lib/python2.7/site-packages/pip/_vendor/chardet/langturkishmodel.py new file mode 100644 index 0000000..a427a45 --- /dev/null +++ b/venv2/lib/python2.7/site-packages/pip/_vendor/chardet/langturkishmodel.py @@ -0,0 +1,193 @@ +# -*- coding: utf-8 -*- +######################## BEGIN LICENSE BLOCK ######################## +# The Original Code is Mozilla Communicator client code. +# +# The Initial Developer of the Original Code is +# Netscape Communications Corporation. +# Portions created by the Initial Developer are Copyright (C) 1998 +# the Initial Developer. All Rights Reserved. +# +# Contributor(s): +# Mark Pilgrim - port to Python +# Özgür Baskın - Turkish Language Model +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA +# 02110-1301 USA +######################### END LICENSE BLOCK ######################### + +# 255: Control characters that usually does not exist in any text +# 254: Carriage/Return +# 253: symbol (punctuation) that does not belong to word +# 252: 0 - 9 + +# Character Mapping Table: +Latin5_TurkishCharToOrderMap = ( +255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, +255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, +255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, +255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, +255, 23, 37, 47, 39, 29, 52, 36, 45, 53, 60, 16, 49, 20, 46, 42, + 48, 69, 44, 35, 31, 51, 38, 62, 65, 43, 56,255,255,255,255,255, +255, 1, 21, 28, 12, 2, 18, 27, 25, 3, 24, 10, 5, 13, 4, 15, + 26, 64, 7, 8, 9, 14, 32, 57, 58, 11, 22,255,255,255,255,255, +180,179,178,177,176,175,174,173,172,171,170,169,168,167,166,165, +164,163,162,161,160,159,101,158,157,156,155,154,153,152,151,106, +150,149,148,147,146,145,144,100,143,142,141,140,139,138,137,136, + 94, 80, 93,135,105,134,133, 63,132,131,130,129,128,127,126,125, +124,104, 73, 99, 79, 85,123, 54,122, 98, 92,121,120, 91,103,119, + 68,118,117, 97,116,115, 50, 90,114,113,112,111, 55, 41, 40, 86, + 89, 70, 59, 78, 71, 82, 88, 33, 77, 66, 84, 83,110, 75, 61, 96, + 30, 67,109, 74, 87,102, 34, 95, 81,108, 76, 72, 17, 6, 19,107, +) + +TurkishLangModel = ( +3,2,3,3,3,1,3,3,3,3,3,3,3,3,2,1,1,3,3,1,3,3,0,3,3,3,3,3,0,3,1,3, +3,2,1,0,0,1,1,0,0,0,1,0,0,1,1,1,1,0,0,0,0,0,0,0,2,2,0,0,1,0,0,1, +3,2,2,3,3,0,3,3,3,3,3,3,3,2,3,1,0,3,3,1,3,3,0,3,3,3,3,3,0,3,0,3, +3,1,1,0,1,0,1,0,0,0,0,0,0,1,1,1,1,0,0,0,0,0,0,0,2,2,0,0,0,1,0,1, +3,3,2,3,3,0,3,3,3,3,3,3,3,2,3,1,1,3,3,0,3,3,1,2,3,3,3,3,0,3,0,3, +3,1,1,0,0,0,1,0,0,0,0,1,1,0,1,2,1,0,0,0,1,0,0,0,0,2,0,0,0,0,0,1, +3,3,3,3,3,3,2,3,3,3,3,3,3,3,3,1,3,3,2,0,3,2,1,2,2,1,3,3,0,0,0,2, +2,2,0,1,0,0,1,0,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0,1,0,1,1,0,1,0,0,1, +3,3,3,2,3,3,1,2,3,3,3,3,3,3,3,1,3,2,1,0,3,2,0,1,2,3,3,2,1,0,0,2, +2,1,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,2,0,2,0,0,0, +1,0,1,3,3,1,3,3,3,3,3,3,3,1,2,0,0,2,3,0,2,3,0,0,2,2,2,3,0,3,0,1, +2,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,0,3,3,3,0,3,2,0,2,3,2,3,3,1,0,0,2, +3,2,0,0,1,0,0,0,0,0,0,2,0,0,1,0,0,0,0,0,0,0,0,0,1,1,1,0,2,0,0,1, +3,3,3,2,3,3,2,3,3,3,3,2,3,3,3,0,3,3,0,0,2,1,0,0,2,3,2,2,0,0,0,2, +2,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,0,0,0,1,0,1,0,2,0,0,1, +3,3,3,2,3,3,3,3,3,3,3,2,3,3,3,0,3,2,0,1,3,2,1,1,3,2,3,2,1,0,0,2, +2,2,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,1,0,0,0,0,0, +3,3,3,2,3,3,3,3,3,3,3,2,3,3,3,0,3,2,2,0,2,3,0,0,2,2,2,2,0,0,0,2, +3,3,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,2,0,1,0,0,0, +3,3,3,3,3,3,3,2,2,2,2,3,2,3,3,0,3,3,1,1,2,2,0,0,2,2,3,2,0,0,1,3, +0,3,1,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,1,0,0,0,0,1, +3,3,3,2,3,3,3,2,1,2,2,3,2,3,3,0,3,2,0,0,1,1,0,1,1,2,1,2,0,0,0,1, +0,3,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,1,0,1,0,1,0,0,0, +3,3,3,2,3,3,2,3,2,2,2,3,3,3,3,1,3,1,1,0,3,2,1,1,3,3,2,3,1,0,0,1, +1,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,2,0,0,1, +3,2,2,3,3,0,3,3,3,3,3,3,3,2,2,1,0,3,3,1,3,3,0,1,3,3,2,3,0,3,0,3, +2,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0, +2,2,2,3,3,0,3,3,3,3,3,3,3,3,3,0,0,3,2,0,3,3,0,3,2,3,3,3,0,3,1,3, +2,0,0,0,0,0,0,0,0,0,0,1,0,1,2,0,1,0,0,0,0,0,0,0,2,2,0,0,1,0,0,1, +3,3,3,1,2,3,3,1,0,0,1,0,0,3,3,2,3,0,0,2,0,0,2,0,2,0,0,0,2,0,2,0, +0,3,1,0,1,0,0,0,2,2,1,0,1,1,2,1,2,2,2,0,2,1,1,0,0,0,2,0,0,0,0,0, +1,2,1,3,3,0,3,3,3,3,3,2,3,0,0,0,0,2,3,0,2,3,1,0,2,3,1,3,0,3,0,2, +3,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +3,3,3,1,3,3,2,2,3,2,2,0,1,2,3,0,1,2,1,0,1,0,0,0,1,0,2,2,0,0,0,1, +1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,1,1,0,0,1,0,0,0, +3,3,3,1,3,3,1,1,3,3,1,1,3,3,1,0,2,1,2,0,2,1,0,0,1,1,2,1,0,0,0,2, +2,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +3,3,3,1,0,2,1,3,0,0,2,0,0,3,3,0,3,0,0,1,0,1,2,0,0,1,1,2,2,0,1,0, +0,1,2,1,1,0,1,0,1,1,1,1,1,0,1,1,1,2,2,1,2,0,1,0,0,0,0,0,0,1,0,0, +3,3,3,2,3,2,3,3,0,2,2,2,3,3,3,0,3,0,0,0,2,2,0,1,2,1,1,1,0,0,0,1, +0,3,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0, +3,3,3,3,3,3,2,1,2,2,3,3,3,3,2,0,2,0,0,0,2,2,0,0,2,1,3,3,0,0,1,1, +1,1,0,0,1,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,1,0,0,0, +1,1,2,3,3,0,3,3,3,3,3,3,2,2,0,2,0,2,3,2,3,2,2,2,2,2,2,2,1,3,2,3, +2,0,2,1,2,2,2,2,1,1,2,2,1,2,2,1,2,0,0,2,1,1,0,2,1,0,0,1,0,0,0,1, +2,3,3,1,1,1,0,1,1,1,2,3,2,1,1,0,0,0,0,0,0,0,0,0,0,1,0,1,0,0,0,0, +0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +3,3,3,2,2,2,3,2,3,2,2,1,3,3,3,0,2,1,2,0,2,1,0,0,1,1,1,1,1,0,0,1, +2,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,1,0,2,0,1,0,0,0, +3,3,3,2,3,3,3,3,3,2,3,1,2,3,3,1,2,0,0,0,0,0,0,0,3,2,1,1,0,0,0,0, +2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0, +3,3,3,2,2,3,3,2,1,1,1,1,1,3,3,0,3,1,0,0,1,1,0,0,3,1,2,1,0,0,0,0, +0,3,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0, +3,3,3,2,2,3,2,2,2,3,2,1,1,3,3,0,3,0,0,0,0,1,0,0,3,1,1,2,0,0,0,1, +1,0,0,1,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1, +1,1,1,3,3,0,3,3,3,3,3,2,2,2,1,2,0,2,1,2,2,1,1,0,1,2,2,2,2,2,2,2, +0,0,2,1,2,1,2,1,0,1,1,3,1,2,1,1,2,0,0,2,0,1,0,1,0,1,0,0,0,1,0,1, +3,3,3,1,3,3,3,0,1,1,0,2,2,3,1,0,3,0,0,0,1,0,0,0,1,0,0,1,0,1,0,0, +1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +3,3,2,0,0,2,2,1,0,0,1,0,0,3,3,1,3,0,0,1,1,0,2,0,3,0,0,0,2,0,1,1, +0,1,2,0,1,2,2,0,2,2,2,2,1,0,2,1,1,0,2,0,2,1,2,0,0,0,0,0,0,0,0,0, +3,3,3,1,3,2,3,2,0,2,2,2,1,3,2,0,2,1,2,0,1,2,0,0,1,0,2,2,0,0,0,2, +1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,0,1,0,0,0, +3,3,3,0,3,3,1,1,2,3,1,0,3,2,3,0,3,0,0,0,1,0,0,0,1,0,1,0,0,0,0,0, +1,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,3,3,0,3,3,2,3,3,2,2,0,0,0,0,1,2,0,1,3,0,0,0,3,1,1,0,3,0,2, +2,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +3,3,3,1,2,2,1,0,3,1,1,1,1,3,3,2,3,0,0,1,0,1,2,0,2,2,0,2,2,0,2,1, +0,2,2,1,1,1,1,0,2,1,1,0,1,1,1,1,2,1,2,1,2,0,1,0,1,0,0,0,0,0,0,0, +3,3,3,0,1,1,3,0,0,1,1,0,0,2,2,0,3,0,0,1,1,0,1,0,0,0,0,0,2,0,0,0, +0,3,1,0,1,0,1,0,2,0,0,1,0,1,0,1,1,1,2,1,1,0,2,0,0,0,0,0,0,0,0,0, +3,3,3,0,2,0,2,0,1,1,1,0,0,3,3,0,2,0,0,1,0,0,2,1,1,0,1,0,1,0,1,0, +0,2,0,1,2,0,2,0,2,1,1,0,1,0,2,1,1,0,2,1,1,0,1,0,0,0,1,1,0,0,0,0, +3,2,3,0,1,0,0,0,0,0,0,0,0,1,2,0,1,0,0,1,0,0,1,0,0,0,0,0,2,0,0,0, +0,0,1,1,0,0,1,0,1,0,0,1,0,0,0,2,1,0,1,0,2,0,0,0,0,0,0,0,0,0,0,0, +3,3,3,0,0,2,3,0,0,1,0,1,0,2,3,2,3,0,0,1,3,0,2,1,0,0,0,0,2,0,1,0, +0,2,1,0,0,1,1,0,2,1,0,0,1,0,0,1,1,0,1,1,2,0,1,0,0,0,0,1,0,0,0,0, +3,2,2,0,0,1,1,0,0,0,0,0,0,3,1,1,1,0,0,0,0,0,1,0,0,0,0,0,2,0,1,0, +0,1,0,0,0,0,0,0,1,0,0,0,0,0,0,0,1,0,0,0,1,0,1,0,0,0,0,0,0,0,0,0, +0,0,0,3,3,0,2,3,2,2,1,2,2,1,1,2,0,1,3,2,2,2,0,0,2,2,0,0,0,1,2,1, +3,0,2,1,1,0,1,1,1,0,1,2,2,2,1,1,2,0,0,0,0,1,0,1,1,0,0,0,0,0,0,0, +0,1,1,2,3,0,3,3,3,2,2,2,2,1,0,1,0,1,0,1,2,2,0,0,2,2,1,3,1,1,2,1, +0,0,1,1,2,0,1,1,0,0,1,2,0,2,1,1,2,0,0,1,0,0,0,1,0,1,0,1,0,0,0,0, +3,3,2,0,0,3,1,0,0,0,0,0,0,3,2,1,2,0,0,1,0,0,2,0,0,0,0,0,2,0,1,0, +0,2,1,1,0,0,1,0,1,2,0,0,1,1,0,0,2,1,1,1,1,0,2,0,0,0,0,0,0,0,0,0, +3,3,2,0,0,1,0,0,0,0,1,0,0,3,3,2,2,0,0,1,0,0,2,0,1,0,0,0,2,0,1,0, +0,0,1,1,0,0,2,0,2,1,0,0,1,1,2,1,2,0,2,1,2,1,1,1,0,0,1,1,0,0,0,0, +3,3,2,0,0,2,2,0,0,0,1,1,0,2,2,1,3,1,0,1,0,1,2,0,0,0,0,0,1,0,1,0, +0,1,1,0,0,0,0,0,1,0,0,1,0,0,0,1,1,0,1,0,1,0,0,0,0,0,0,0,0,0,0,0, +3,3,3,2,0,0,0,1,0,0,1,0,0,2,3,1,2,0,0,1,0,0,2,0,0,0,1,0,2,0,2,0, +0,1,1,2,2,1,2,0,2,1,1,0,0,1,1,0,1,1,1,1,2,1,1,0,0,0,0,0,0,0,0,0, +3,3,3,0,2,1,2,1,0,0,1,1,0,3,3,1,2,0,0,1,0,0,2,0,2,0,1,1,2,0,0,0, +0,0,1,1,1,1,2,0,1,1,0,1,1,1,1,0,0,0,1,1,1,0,1,0,0,0,1,0,0,0,0,0, +3,3,3,0,2,2,3,2,0,0,1,0,0,2,3,1,0,0,0,0,0,0,2,0,2,0,0,0,2,0,0,0, +0,1,1,0,0,0,1,0,0,1,0,1,1,0,1,0,1,1,1,0,1,0,0,0,0,0,0,0,0,0,0,0, +3,2,3,0,0,0,0,0,0,0,1,0,0,2,2,2,2,0,0,1,0,0,2,0,0,0,0,0,2,0,1,0, +0,0,2,1,1,0,1,0,2,1,1,0,0,1,1,2,1,0,2,0,2,0,1,0,0,0,2,0,0,0,0,0, +0,0,0,2,2,0,2,1,1,1,1,2,2,0,0,1,0,1,0,0,1,3,0,0,0,0,1,0,0,2,1,0, +0,0,1,0,1,0,0,0,0,0,2,1,0,1,0,0,0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0, +2,0,0,2,3,0,2,3,1,2,2,0,2,0,0,2,0,2,1,1,1,2,1,0,0,1,2,1,1,2,1,0, +1,0,2,0,1,0,1,1,0,0,2,2,1,2,1,1,2,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0, +3,3,3,0,2,1,2,0,0,0,1,0,0,3,2,0,1,0,0,1,0,0,2,0,0,0,1,2,1,0,1,0, +0,0,0,0,1,0,1,0,0,1,0,0,0,0,1,0,1,0,1,1,1,0,1,0,0,0,0,0,0,0,0,0, +0,0,0,2,2,0,2,2,1,1,0,1,1,1,1,1,0,0,1,2,1,1,1,0,1,0,0,0,1,1,1,1, +0,0,2,1,0,1,1,1,0,1,1,2,1,2,1,1,2,0,1,1,2,1,0,2,0,0,0,0,0,0,0,0, +3,2,2,0,0,2,0,0,0,0,0,0,0,2,2,0,2,0,0,1,0,0,2,0,0,0,0,0,2,0,0,0, +0,2,1,0,0,0,0,0,1,0,0,0,0,0,0,0,1,0,0,0,1,0,1,0,0,0,0,0,0,0,0,0, +0,0,0,3,2,0,2,2,0,1,1,0,1,0,0,1,0,0,0,1,0,1,0,0,0,0,0,1,0,0,0,0, +2,0,1,0,1,0,1,1,0,0,1,2,0,1,0,1,1,0,0,1,0,1,0,2,0,0,0,0,0,0,0,0, +2,2,2,0,1,1,0,0,0,1,0,0,0,1,2,0,1,0,0,1,0,0,1,0,0,0,0,1,2,0,1,0, +0,0,1,0,0,0,1,0,0,1,0,0,0,0,0,0,1,0,1,0,2,0,0,0,0,0,0,0,0,0,0,0, +2,2,2,2,1,0,1,1,1,0,0,0,0,1,2,0,0,1,0,0,0,1,0,0,1,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,0,0,0,0,0,0,0, +1,1,2,0,1,0,0,0,1,0,1,0,0,0,1,0,0,1,0,0,0,0,0,0,0,1,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,2,0,0,0,0,0,1, +0,0,1,2,2,0,2,1,2,1,1,2,2,0,0,0,0,1,0,0,1,1,0,0,2,0,0,0,0,1,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0, +2,2,2,0,0,0,1,0,0,0,0,0,0,2,2,1,1,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0, +0,0,0,0,1,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,1,1,0,0,0,1,0,0,0,1,0,0,0,0,0,0,0,1,1,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +2,2,2,0,1,0,1,0,0,0,0,0,0,1,1,0,0,0,0,0,0,0,1,0,1,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,1,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,1,0,0,0,0,0,0,0,0,0,0,2,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +) + +Latin5TurkishModel = { + 'char_to_order_map': Latin5_TurkishCharToOrderMap, + 'precedence_matrix': TurkishLangModel, + 'typical_positive_ratio': 0.970290, + 'keep_english_letter': True, + 'charset_name': "ISO-8859-9", + 'language': 'Turkish', +} diff --git a/venv2/lib/python2.7/site-packages/pip/_vendor/chardet/latin1prober.py b/venv2/lib/python2.7/site-packages/pip/_vendor/chardet/latin1prober.py new file mode 100644 index 0000000..7d1e8c2 --- /dev/null +++ b/venv2/lib/python2.7/site-packages/pip/_vendor/chardet/latin1prober.py @@ -0,0 +1,145 @@ +######################## BEGIN LICENSE BLOCK ######################## +# The Original Code is Mozilla Universal charset detector code. +# +# The Initial Developer of the Original Code is +# Netscape Communications Corporation. +# Portions created by the Initial Developer are Copyright (C) 2001 +# the Initial Developer. All Rights Reserved. +# +# Contributor(s): +# Mark Pilgrim - port to Python +# Shy Shalom - original C code +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA +# 02110-1301 USA +######################### END LICENSE BLOCK ######################### + +from .charsetprober import CharSetProber +from .enums import ProbingState + +FREQ_CAT_NUM = 4 + +UDF = 0 # undefined +OTH = 1 # other +ASC = 2 # ascii capital letter +ASS = 3 # ascii small letter +ACV = 4 # accent capital vowel +ACO = 5 # accent capital other +ASV = 6 # accent small vowel +ASO = 7 # accent small other +CLASS_NUM = 8 # total classes + +Latin1_CharToClass = ( + OTH, OTH, OTH, OTH, OTH, OTH, OTH, OTH, # 00 - 07 + OTH, OTH, OTH, OTH, OTH, OTH, OTH, OTH, # 08 - 0F + OTH, OTH, OTH, OTH, OTH, OTH, OTH, OTH, # 10 - 17 + OTH, OTH, OTH, OTH, OTH, OTH, OTH, OTH, # 18 - 1F + OTH, OTH, OTH, OTH, OTH, OTH, OTH, OTH, # 20 - 27 + OTH, OTH, OTH, OTH, OTH, OTH, OTH, OTH, # 28 - 2F + OTH, OTH, OTH, OTH, OTH, OTH, OTH, OTH, # 30 - 37 + OTH, OTH, OTH, OTH, OTH, OTH, OTH, OTH, # 38 - 3F + OTH, ASC, ASC, ASC, ASC, ASC, ASC, ASC, # 40 - 47 + ASC, ASC, ASC, ASC, ASC, ASC, ASC, ASC, # 48 - 4F + ASC, ASC, ASC, ASC, ASC, ASC, ASC, ASC, # 50 - 57 + ASC, ASC, ASC, OTH, OTH, OTH, OTH, OTH, # 58 - 5F + OTH, ASS, ASS, ASS, ASS, ASS, ASS, ASS, # 60 - 67 + ASS, ASS, ASS, ASS, ASS, ASS, ASS, ASS, # 68 - 6F + ASS, ASS, ASS, ASS, ASS, ASS, ASS, ASS, # 70 - 77 + ASS, ASS, ASS, OTH, OTH, OTH, OTH, OTH, # 78 - 7F + OTH, UDF, OTH, ASO, OTH, OTH, OTH, OTH, # 80 - 87 + OTH, OTH, ACO, OTH, ACO, UDF, ACO, UDF, # 88 - 8F + UDF, OTH, OTH, OTH, OTH, OTH, OTH, OTH, # 90 - 97 + OTH, OTH, ASO, OTH, ASO, UDF, ASO, ACO, # 98 - 9F + OTH, OTH, OTH, OTH, OTH, OTH, OTH, OTH, # A0 - A7 + OTH, OTH, OTH, OTH, OTH, OTH, OTH, OTH, # A8 - AF + OTH, OTH, OTH, OTH, OTH, OTH, OTH, OTH, # B0 - B7 + OTH, OTH, OTH, OTH, OTH, OTH, OTH, OTH, # B8 - BF + ACV, ACV, ACV, ACV, ACV, ACV, ACO, ACO, # C0 - C7 + ACV, ACV, ACV, ACV, ACV, ACV, ACV, ACV, # C8 - CF + ACO, ACO, ACV, ACV, ACV, ACV, ACV, OTH, # D0 - D7 + ACV, ACV, ACV, ACV, ACV, ACO, ACO, ACO, # D8 - DF + ASV, ASV, ASV, ASV, ASV, ASV, ASO, ASO, # E0 - E7 + ASV, ASV, ASV, ASV, ASV, ASV, ASV, ASV, # E8 - EF + ASO, ASO, ASV, ASV, ASV, ASV, ASV, OTH, # F0 - F7 + ASV, ASV, ASV, ASV, ASV, ASO, ASO, ASO, # F8 - FF +) + +# 0 : illegal +# 1 : very unlikely +# 2 : normal +# 3 : very likely +Latin1ClassModel = ( +# UDF OTH ASC ASS ACV ACO ASV ASO + 0, 0, 0, 0, 0, 0, 0, 0, # UDF + 0, 3, 3, 3, 3, 3, 3, 3, # OTH + 0, 3, 3, 3, 3, 3, 3, 3, # ASC + 0, 3, 3, 3, 1, 1, 3, 3, # ASS + 0, 3, 3, 3, 1, 2, 1, 2, # ACV + 0, 3, 3, 3, 3, 3, 3, 3, # ACO + 0, 3, 1, 3, 1, 1, 1, 3, # ASV + 0, 3, 1, 3, 1, 1, 3, 3, # ASO +) + + +class Latin1Prober(CharSetProber): + def __init__(self): + super(Latin1Prober, self).__init__() + self._last_char_class = None + self._freq_counter = None + self.reset() + + def reset(self): + self._last_char_class = OTH + self._freq_counter = [0] * FREQ_CAT_NUM + CharSetProber.reset(self) + + @property + def charset_name(self): + return "ISO-8859-1" + + @property + def language(self): + return "" + + def feed(self, byte_str): + byte_str = self.filter_with_english_letters(byte_str) + for c in byte_str: + char_class = Latin1_CharToClass[c] + freq = Latin1ClassModel[(self._last_char_class * CLASS_NUM) + + char_class] + if freq == 0: + self._state = ProbingState.NOT_ME + break + self._freq_counter[freq] += 1 + self._last_char_class = char_class + + return self.state + + def get_confidence(self): + if self.state == ProbingState.NOT_ME: + return 0.01 + + total = sum(self._freq_counter) + if total < 0.01: + confidence = 0.0 + else: + confidence = ((self._freq_counter[3] - self._freq_counter[1] * 20.0) + / total) + if confidence < 0.0: + confidence = 0.0 + # lower the confidence of latin1 so that other more accurate + # detector can take priority. + confidence = confidence * 0.73 + return confidence diff --git a/venv2/lib/python2.7/site-packages/pip/_vendor/chardet/mbcharsetprober.py b/venv2/lib/python2.7/site-packages/pip/_vendor/chardet/mbcharsetprober.py new file mode 100644 index 0000000..6256ecf --- /dev/null +++ b/venv2/lib/python2.7/site-packages/pip/_vendor/chardet/mbcharsetprober.py @@ -0,0 +1,91 @@ +######################## BEGIN LICENSE BLOCK ######################## +# The Original Code is Mozilla Universal charset detector code. +# +# The Initial Developer of the Original Code is +# Netscape Communications Corporation. +# Portions created by the Initial Developer are Copyright (C) 2001 +# the Initial Developer. All Rights Reserved. +# +# Contributor(s): +# Mark Pilgrim - port to Python +# Shy Shalom - original C code +# Proofpoint, Inc. +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA +# 02110-1301 USA +######################### END LICENSE BLOCK ######################### + +from .charsetprober import CharSetProber +from .enums import ProbingState, MachineState + + +class MultiByteCharSetProber(CharSetProber): + """ + MultiByteCharSetProber + """ + + def __init__(self, lang_filter=None): + super(MultiByteCharSetProber, self).__init__(lang_filter=lang_filter) + self.distribution_analyzer = None + self.coding_sm = None + self._last_char = [0, 0] + + def reset(self): + super(MultiByteCharSetProber, self).reset() + if self.coding_sm: + self.coding_sm.reset() + if self.distribution_analyzer: + self.distribution_analyzer.reset() + self._last_char = [0, 0] + + @property + def charset_name(self): + raise NotImplementedError + + @property + def language(self): + raise NotImplementedError + + def feed(self, byte_str): + for i in range(len(byte_str)): + coding_state = self.coding_sm.next_state(byte_str[i]) + if coding_state == MachineState.ERROR: + self.logger.debug('%s %s prober hit error at byte %s', + self.charset_name, self.language, i) + self._state = ProbingState.NOT_ME + break + elif coding_state == MachineState.ITS_ME: + self._state = ProbingState.FOUND_IT + break + elif coding_state == MachineState.START: + char_len = self.coding_sm.get_current_charlen() + if i == 0: + self._last_char[1] = byte_str[0] + self.distribution_analyzer.feed(self._last_char, char_len) + else: + self.distribution_analyzer.feed(byte_str[i - 1:i + 1], + char_len) + + self._last_char[0] = byte_str[-1] + + if self.state == ProbingState.DETECTING: + if (self.distribution_analyzer.got_enough_data() and + (self.get_confidence() > self.SHORTCUT_THRESHOLD)): + self._state = ProbingState.FOUND_IT + + return self.state + + def get_confidence(self): + return self.distribution_analyzer.get_confidence() diff --git a/venv2/lib/python2.7/site-packages/pip/_vendor/chardet/mbcsgroupprober.py b/venv2/lib/python2.7/site-packages/pip/_vendor/chardet/mbcsgroupprober.py new file mode 100644 index 0000000..530abe7 --- /dev/null +++ b/venv2/lib/python2.7/site-packages/pip/_vendor/chardet/mbcsgroupprober.py @@ -0,0 +1,54 @@ +######################## BEGIN LICENSE BLOCK ######################## +# The Original Code is Mozilla Universal charset detector code. +# +# The Initial Developer of the Original Code is +# Netscape Communications Corporation. +# Portions created by the Initial Developer are Copyright (C) 2001 +# the Initial Developer. All Rights Reserved. +# +# Contributor(s): +# Mark Pilgrim - port to Python +# Shy Shalom - original C code +# Proofpoint, Inc. +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA +# 02110-1301 USA +######################### END LICENSE BLOCK ######################### + +from .charsetgroupprober import CharSetGroupProber +from .utf8prober import UTF8Prober +from .sjisprober import SJISProber +from .eucjpprober import EUCJPProber +from .gb2312prober import GB2312Prober +from .euckrprober import EUCKRProber +from .cp949prober import CP949Prober +from .big5prober import Big5Prober +from .euctwprober import EUCTWProber + + +class MBCSGroupProber(CharSetGroupProber): + def __init__(self, lang_filter=None): + super(MBCSGroupProber, self).__init__(lang_filter=lang_filter) + self.probers = [ + UTF8Prober(), + SJISProber(), + EUCJPProber(), + GB2312Prober(), + EUCKRProber(), + CP949Prober(), + Big5Prober(), + EUCTWProber() + ] + self.reset() diff --git a/venv2/lib/python2.7/site-packages/pip/_vendor/chardet/mbcssm.py b/venv2/lib/python2.7/site-packages/pip/_vendor/chardet/mbcssm.py new file mode 100644 index 0000000..8360d0f --- /dev/null +++ b/venv2/lib/python2.7/site-packages/pip/_vendor/chardet/mbcssm.py @@ -0,0 +1,572 @@ +######################## BEGIN LICENSE BLOCK ######################## +# The Original Code is mozilla.org code. +# +# The Initial Developer of the Original Code is +# Netscape Communications Corporation. +# Portions created by the Initial Developer are Copyright (C) 1998 +# the Initial Developer. All Rights Reserved. +# +# Contributor(s): +# Mark Pilgrim - port to Python +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA +# 02110-1301 USA +######################### END LICENSE BLOCK ######################### + +from .enums import MachineState + +# BIG5 + +BIG5_CLS = ( + 1,1,1,1,1,1,1,1, # 00 - 07 #allow 0x00 as legal value + 1,1,1,1,1,1,0,0, # 08 - 0f + 1,1,1,1,1,1,1,1, # 10 - 17 + 1,1,1,0,1,1,1,1, # 18 - 1f + 1,1,1,1,1,1,1,1, # 20 - 27 + 1,1,1,1,1,1,1,1, # 28 - 2f + 1,1,1,1,1,1,1,1, # 30 - 37 + 1,1,1,1,1,1,1,1, # 38 - 3f + 2,2,2,2,2,2,2,2, # 40 - 47 + 2,2,2,2,2,2,2,2, # 48 - 4f + 2,2,2,2,2,2,2,2, # 50 - 57 + 2,2,2,2,2,2,2,2, # 58 - 5f + 2,2,2,2,2,2,2,2, # 60 - 67 + 2,2,2,2,2,2,2,2, # 68 - 6f + 2,2,2,2,2,2,2,2, # 70 - 77 + 2,2,2,2,2,2,2,1, # 78 - 7f + 4,4,4,4,4,4,4,4, # 80 - 87 + 4,4,4,4,4,4,4,4, # 88 - 8f + 4,4,4,4,4,4,4,4, # 90 - 97 + 4,4,4,4,4,4,4,4, # 98 - 9f + 4,3,3,3,3,3,3,3, # a0 - a7 + 3,3,3,3,3,3,3,3, # a8 - af + 3,3,3,3,3,3,3,3, # b0 - b7 + 3,3,3,3,3,3,3,3, # b8 - bf + 3,3,3,3,3,3,3,3, # c0 - c7 + 3,3,3,3,3,3,3,3, # c8 - cf + 3,3,3,3,3,3,3,3, # d0 - d7 + 3,3,3,3,3,3,3,3, # d8 - df + 3,3,3,3,3,3,3,3, # e0 - e7 + 3,3,3,3,3,3,3,3, # e8 - ef + 3,3,3,3,3,3,3,3, # f0 - f7 + 3,3,3,3,3,3,3,0 # f8 - ff +) + +BIG5_ST = ( + MachineState.ERROR,MachineState.START,MachineState.START, 3,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,#00-07 + MachineState.ERROR,MachineState.ERROR,MachineState.ITS_ME,MachineState.ITS_ME,MachineState.ITS_ME,MachineState.ITS_ME,MachineState.ITS_ME,MachineState.ERROR,#08-0f + MachineState.ERROR,MachineState.START,MachineState.START,MachineState.START,MachineState.START,MachineState.START,MachineState.START,MachineState.START#10-17 +) + +BIG5_CHAR_LEN_TABLE = (0, 1, 1, 2, 0) + +BIG5_SM_MODEL = {'class_table': BIG5_CLS, + 'class_factor': 5, + 'state_table': BIG5_ST, + 'char_len_table': BIG5_CHAR_LEN_TABLE, + 'name': 'Big5'} + +# CP949 + +CP949_CLS = ( + 1,1,1,1,1,1,1,1, 1,1,1,1,1,1,0,0, # 00 - 0f + 1,1,1,1,1,1,1,1, 1,1,1,0,1,1,1,1, # 10 - 1f + 1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1, # 20 - 2f + 1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1, # 30 - 3f + 1,4,4,4,4,4,4,4, 4,4,4,4,4,4,4,4, # 40 - 4f + 4,4,5,5,5,5,5,5, 5,5,5,1,1,1,1,1, # 50 - 5f + 1,5,5,5,5,5,5,5, 5,5,5,5,5,5,5,5, # 60 - 6f + 5,5,5,5,5,5,5,5, 5,5,5,1,1,1,1,1, # 70 - 7f + 0,6,6,6,6,6,6,6, 6,6,6,6,6,6,6,6, # 80 - 8f + 6,6,6,6,6,6,6,6, 6,6,6,6,6,6,6,6, # 90 - 9f + 6,7,7,7,7,7,7,7, 7,7,7,7,7,8,8,8, # a0 - af + 7,7,7,7,7,7,7,7, 7,7,7,7,7,7,7,7, # b0 - bf + 7,7,7,7,7,7,9,2, 2,3,2,2,2,2,2,2, # c0 - cf + 2,2,2,2,2,2,2,2, 2,2,2,2,2,2,2,2, # d0 - df + 2,2,2,2,2,2,2,2, 2,2,2,2,2,2,2,2, # e0 - ef + 2,2,2,2,2,2,2,2, 2,2,2,2,2,2,2,0, # f0 - ff +) + +CP949_ST = ( +#cls= 0 1 2 3 4 5 6 7 8 9 # previous state = + MachineState.ERROR,MachineState.START, 3,MachineState.ERROR,MachineState.START,MachineState.START, 4, 5,MachineState.ERROR, 6, # MachineState.START + MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR, # MachineState.ERROR + MachineState.ITS_ME,MachineState.ITS_ME,MachineState.ITS_ME,MachineState.ITS_ME,MachineState.ITS_ME,MachineState.ITS_ME,MachineState.ITS_ME,MachineState.ITS_ME,MachineState.ITS_ME,MachineState.ITS_ME, # MachineState.ITS_ME + MachineState.ERROR,MachineState.ERROR,MachineState.START,MachineState.START,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.START,MachineState.START,MachineState.START, # 3 + MachineState.ERROR,MachineState.ERROR,MachineState.START,MachineState.START,MachineState.START,MachineState.START,MachineState.START,MachineState.START,MachineState.START,MachineState.START, # 4 + MachineState.ERROR,MachineState.START,MachineState.START,MachineState.START,MachineState.START,MachineState.START,MachineState.START,MachineState.START,MachineState.START,MachineState.START, # 5 + MachineState.ERROR,MachineState.START,MachineState.START,MachineState.START,MachineState.START,MachineState.ERROR,MachineState.ERROR,MachineState.START,MachineState.START,MachineState.START, # 6 +) + +CP949_CHAR_LEN_TABLE = (0, 1, 2, 0, 1, 1, 2, 2, 0, 2) + +CP949_SM_MODEL = {'class_table': CP949_CLS, + 'class_factor': 10, + 'state_table': CP949_ST, + 'char_len_table': CP949_CHAR_LEN_TABLE, + 'name': 'CP949'} + +# EUC-JP + +EUCJP_CLS = ( + 4,4,4,4,4,4,4,4, # 00 - 07 + 4,4,4,4,4,4,5,5, # 08 - 0f + 4,4,4,4,4,4,4,4, # 10 - 17 + 4,4,4,5,4,4,4,4, # 18 - 1f + 4,4,4,4,4,4,4,4, # 20 - 27 + 4,4,4,4,4,4,4,4, # 28 - 2f + 4,4,4,4,4,4,4,4, # 30 - 37 + 4,4,4,4,4,4,4,4, # 38 - 3f + 4,4,4,4,4,4,4,4, # 40 - 47 + 4,4,4,4,4,4,4,4, # 48 - 4f + 4,4,4,4,4,4,4,4, # 50 - 57 + 4,4,4,4,4,4,4,4, # 58 - 5f + 4,4,4,4,4,4,4,4, # 60 - 67 + 4,4,4,4,4,4,4,4, # 68 - 6f + 4,4,4,4,4,4,4,4, # 70 - 77 + 4,4,4,4,4,4,4,4, # 78 - 7f + 5,5,5,5,5,5,5,5, # 80 - 87 + 5,5,5,5,5,5,1,3, # 88 - 8f + 5,5,5,5,5,5,5,5, # 90 - 97 + 5,5,5,5,5,5,5,5, # 98 - 9f + 5,2,2,2,2,2,2,2, # a0 - a7 + 2,2,2,2,2,2,2,2, # a8 - af + 2,2,2,2,2,2,2,2, # b0 - b7 + 2,2,2,2,2,2,2,2, # b8 - bf + 2,2,2,2,2,2,2,2, # c0 - c7 + 2,2,2,2,2,2,2,2, # c8 - cf + 2,2,2,2,2,2,2,2, # d0 - d7 + 2,2,2,2,2,2,2,2, # d8 - df + 0,0,0,0,0,0,0,0, # e0 - e7 + 0,0,0,0,0,0,0,0, # e8 - ef + 0,0,0,0,0,0,0,0, # f0 - f7 + 0,0,0,0,0,0,0,5 # f8 - ff +) + +EUCJP_ST = ( + 3, 4, 3, 5,MachineState.START,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,#00-07 + MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ITS_ME,MachineState.ITS_ME,MachineState.ITS_ME,MachineState.ITS_ME,#08-0f + MachineState.ITS_ME,MachineState.ITS_ME,MachineState.START,MachineState.ERROR,MachineState.START,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,#10-17 + MachineState.ERROR,MachineState.ERROR,MachineState.START,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR, 3,MachineState.ERROR,#18-1f + 3,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.START,MachineState.START,MachineState.START,MachineState.START#20-27 +) + +EUCJP_CHAR_LEN_TABLE = (2, 2, 2, 3, 1, 0) + +EUCJP_SM_MODEL = {'class_table': EUCJP_CLS, + 'class_factor': 6, + 'state_table': EUCJP_ST, + 'char_len_table': EUCJP_CHAR_LEN_TABLE, + 'name': 'EUC-JP'} + +# EUC-KR + +EUCKR_CLS = ( + 1,1,1,1,1,1,1,1, # 00 - 07 + 1,1,1,1,1,1,0,0, # 08 - 0f + 1,1,1,1,1,1,1,1, # 10 - 17 + 1,1,1,0,1,1,1,1, # 18 - 1f + 1,1,1,1,1,1,1,1, # 20 - 27 + 1,1,1,1,1,1,1,1, # 28 - 2f + 1,1,1,1,1,1,1,1, # 30 - 37 + 1,1,1,1,1,1,1,1, # 38 - 3f + 1,1,1,1,1,1,1,1, # 40 - 47 + 1,1,1,1,1,1,1,1, # 48 - 4f + 1,1,1,1,1,1,1,1, # 50 - 57 + 1,1,1,1,1,1,1,1, # 58 - 5f + 1,1,1,1,1,1,1,1, # 60 - 67 + 1,1,1,1,1,1,1,1, # 68 - 6f + 1,1,1,1,1,1,1,1, # 70 - 77 + 1,1,1,1,1,1,1,1, # 78 - 7f + 0,0,0,0,0,0,0,0, # 80 - 87 + 0,0,0,0,0,0,0,0, # 88 - 8f + 0,0,0,0,0,0,0,0, # 90 - 97 + 0,0,0,0,0,0,0,0, # 98 - 9f + 0,2,2,2,2,2,2,2, # a0 - a7 + 2,2,2,2,2,3,3,3, # a8 - af + 2,2,2,2,2,2,2,2, # b0 - b7 + 2,2,2,2,2,2,2,2, # b8 - bf + 2,2,2,2,2,2,2,2, # c0 - c7 + 2,3,2,2,2,2,2,2, # c8 - cf + 2,2,2,2,2,2,2,2, # d0 - d7 + 2,2,2,2,2,2,2,2, # d8 - df + 2,2,2,2,2,2,2,2, # e0 - e7 + 2,2,2,2,2,2,2,2, # e8 - ef + 2,2,2,2,2,2,2,2, # f0 - f7 + 2,2,2,2,2,2,2,0 # f8 - ff +) + +EUCKR_ST = ( + MachineState.ERROR,MachineState.START, 3,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,#00-07 + MachineState.ITS_ME,MachineState.ITS_ME,MachineState.ITS_ME,MachineState.ITS_ME,MachineState.ERROR,MachineState.ERROR,MachineState.START,MachineState.START #08-0f +) + +EUCKR_CHAR_LEN_TABLE = (0, 1, 2, 0) + +EUCKR_SM_MODEL = {'class_table': EUCKR_CLS, + 'class_factor': 4, + 'state_table': EUCKR_ST, + 'char_len_table': EUCKR_CHAR_LEN_TABLE, + 'name': 'EUC-KR'} + +# EUC-TW + +EUCTW_CLS = ( + 2,2,2,2,2,2,2,2, # 00 - 07 + 2,2,2,2,2,2,0,0, # 08 - 0f + 2,2,2,2,2,2,2,2, # 10 - 17 + 2,2,2,0,2,2,2,2, # 18 - 1f + 2,2,2,2,2,2,2,2, # 20 - 27 + 2,2,2,2,2,2,2,2, # 28 - 2f + 2,2,2,2,2,2,2,2, # 30 - 37 + 2,2,2,2,2,2,2,2, # 38 - 3f + 2,2,2,2,2,2,2,2, # 40 - 47 + 2,2,2,2,2,2,2,2, # 48 - 4f + 2,2,2,2,2,2,2,2, # 50 - 57 + 2,2,2,2,2,2,2,2, # 58 - 5f + 2,2,2,2,2,2,2,2, # 60 - 67 + 2,2,2,2,2,2,2,2, # 68 - 6f + 2,2,2,2,2,2,2,2, # 70 - 77 + 2,2,2,2,2,2,2,2, # 78 - 7f + 0,0,0,0,0,0,0,0, # 80 - 87 + 0,0,0,0,0,0,6,0, # 88 - 8f + 0,0,0,0,0,0,0,0, # 90 - 97 + 0,0,0,0,0,0,0,0, # 98 - 9f + 0,3,4,4,4,4,4,4, # a0 - a7 + 5,5,1,1,1,1,1,1, # a8 - af + 1,1,1,1,1,1,1,1, # b0 - b7 + 1,1,1,1,1,1,1,1, # b8 - bf + 1,1,3,1,3,3,3,3, # c0 - c7 + 3,3,3,3,3,3,3,3, # c8 - cf + 3,3,3,3,3,3,3,3, # d0 - d7 + 3,3,3,3,3,3,3,3, # d8 - df + 3,3,3,3,3,3,3,3, # e0 - e7 + 3,3,3,3,3,3,3,3, # e8 - ef + 3,3,3,3,3,3,3,3, # f0 - f7 + 3,3,3,3,3,3,3,0 # f8 - ff +) + +EUCTW_ST = ( + MachineState.ERROR,MachineState.ERROR,MachineState.START, 3, 3, 3, 4,MachineState.ERROR,#00-07 + MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ITS_ME,MachineState.ITS_ME,#08-0f + MachineState.ITS_ME,MachineState.ITS_ME,MachineState.ITS_ME,MachineState.ITS_ME,MachineState.ITS_ME,MachineState.ERROR,MachineState.START,MachineState.ERROR,#10-17 + MachineState.START,MachineState.START,MachineState.START,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,#18-1f + 5,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.START,MachineState.ERROR,MachineState.START,MachineState.START,#20-27 + MachineState.START,MachineState.ERROR,MachineState.START,MachineState.START,MachineState.START,MachineState.START,MachineState.START,MachineState.START #28-2f +) + +EUCTW_CHAR_LEN_TABLE = (0, 0, 1, 2, 2, 2, 3) + +EUCTW_SM_MODEL = {'class_table': EUCTW_CLS, + 'class_factor': 7, + 'state_table': EUCTW_ST, + 'char_len_table': EUCTW_CHAR_LEN_TABLE, + 'name': 'x-euc-tw'} + +# GB2312 + +GB2312_CLS = ( + 1,1,1,1,1,1,1,1, # 00 - 07 + 1,1,1,1,1,1,0,0, # 08 - 0f + 1,1,1,1,1,1,1,1, # 10 - 17 + 1,1,1,0,1,1,1,1, # 18 - 1f + 1,1,1,1,1,1,1,1, # 20 - 27 + 1,1,1,1,1,1,1,1, # 28 - 2f + 3,3,3,3,3,3,3,3, # 30 - 37 + 3,3,1,1,1,1,1,1, # 38 - 3f + 2,2,2,2,2,2,2,2, # 40 - 47 + 2,2,2,2,2,2,2,2, # 48 - 4f + 2,2,2,2,2,2,2,2, # 50 - 57 + 2,2,2,2,2,2,2,2, # 58 - 5f + 2,2,2,2,2,2,2,2, # 60 - 67 + 2,2,2,2,2,2,2,2, # 68 - 6f + 2,2,2,2,2,2,2,2, # 70 - 77 + 2,2,2,2,2,2,2,4, # 78 - 7f + 5,6,6,6,6,6,6,6, # 80 - 87 + 6,6,6,6,6,6,6,6, # 88 - 8f + 6,6,6,6,6,6,6,6, # 90 - 97 + 6,6,6,6,6,6,6,6, # 98 - 9f + 6,6,6,6,6,6,6,6, # a0 - a7 + 6,6,6,6,6,6,6,6, # a8 - af + 6,6,6,6,6,6,6,6, # b0 - b7 + 6,6,6,6,6,6,6,6, # b8 - bf + 6,6,6,6,6,6,6,6, # c0 - c7 + 6,6,6,6,6,6,6,6, # c8 - cf + 6,6,6,6,6,6,6,6, # d0 - d7 + 6,6,6,6,6,6,6,6, # d8 - df + 6,6,6,6,6,6,6,6, # e0 - e7 + 6,6,6,6,6,6,6,6, # e8 - ef + 6,6,6,6,6,6,6,6, # f0 - f7 + 6,6,6,6,6,6,6,0 # f8 - ff +) + +GB2312_ST = ( + MachineState.ERROR,MachineState.START,MachineState.START,MachineState.START,MachineState.START,MachineState.START, 3,MachineState.ERROR,#00-07 + MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ITS_ME,MachineState.ITS_ME,#08-0f + MachineState.ITS_ME,MachineState.ITS_ME,MachineState.ITS_ME,MachineState.ITS_ME,MachineState.ITS_ME,MachineState.ERROR,MachineState.ERROR,MachineState.START,#10-17 + 4,MachineState.ERROR,MachineState.START,MachineState.START,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,#18-1f + MachineState.ERROR,MachineState.ERROR, 5,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ITS_ME,MachineState.ERROR,#20-27 + MachineState.ERROR,MachineState.ERROR,MachineState.START,MachineState.START,MachineState.START,MachineState.START,MachineState.START,MachineState.START #28-2f +) + +# To be accurate, the length of class 6 can be either 2 or 4. +# But it is not necessary to discriminate between the two since +# it is used for frequency analysis only, and we are validating +# each code range there as well. So it is safe to set it to be +# 2 here. +GB2312_CHAR_LEN_TABLE = (0, 1, 1, 1, 1, 1, 2) + +GB2312_SM_MODEL = {'class_table': GB2312_CLS, + 'class_factor': 7, + 'state_table': GB2312_ST, + 'char_len_table': GB2312_CHAR_LEN_TABLE, + 'name': 'GB2312'} + +# Shift_JIS + +SJIS_CLS = ( + 1,1,1,1,1,1,1,1, # 00 - 07 + 1,1,1,1,1,1,0,0, # 08 - 0f + 1,1,1,1,1,1,1,1, # 10 - 17 + 1,1,1,0,1,1,1,1, # 18 - 1f + 1,1,1,1,1,1,1,1, # 20 - 27 + 1,1,1,1,1,1,1,1, # 28 - 2f + 1,1,1,1,1,1,1,1, # 30 - 37 + 1,1,1,1,1,1,1,1, # 38 - 3f + 2,2,2,2,2,2,2,2, # 40 - 47 + 2,2,2,2,2,2,2,2, # 48 - 4f + 2,2,2,2,2,2,2,2, # 50 - 57 + 2,2,2,2,2,2,2,2, # 58 - 5f + 2,2,2,2,2,2,2,2, # 60 - 67 + 2,2,2,2,2,2,2,2, # 68 - 6f + 2,2,2,2,2,2,2,2, # 70 - 77 + 2,2,2,2,2,2,2,1, # 78 - 7f + 3,3,3,3,3,2,2,3, # 80 - 87 + 3,3,3,3,3,3,3,3, # 88 - 8f + 3,3,3,3,3,3,3,3, # 90 - 97 + 3,3,3,3,3,3,3,3, # 98 - 9f + #0xa0 is illegal in sjis encoding, but some pages does + #contain such byte. We need to be more error forgiven. + 2,2,2,2,2,2,2,2, # a0 - a7 + 2,2,2,2,2,2,2,2, # a8 - af + 2,2,2,2,2,2,2,2, # b0 - b7 + 2,2,2,2,2,2,2,2, # b8 - bf + 2,2,2,2,2,2,2,2, # c0 - c7 + 2,2,2,2,2,2,2,2, # c8 - cf + 2,2,2,2,2,2,2,2, # d0 - d7 + 2,2,2,2,2,2,2,2, # d8 - df + 3,3,3,3,3,3,3,3, # e0 - e7 + 3,3,3,3,3,4,4,4, # e8 - ef + 3,3,3,3,3,3,3,3, # f0 - f7 + 3,3,3,3,3,0,0,0) # f8 - ff + + +SJIS_ST = ( + MachineState.ERROR,MachineState.START,MachineState.START, 3,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,#00-07 + MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ITS_ME,MachineState.ITS_ME,MachineState.ITS_ME,MachineState.ITS_ME,#08-0f + MachineState.ITS_ME,MachineState.ITS_ME,MachineState.ERROR,MachineState.ERROR,MachineState.START,MachineState.START,MachineState.START,MachineState.START #10-17 +) + +SJIS_CHAR_LEN_TABLE = (0, 1, 1, 2, 0, 0) + +SJIS_SM_MODEL = {'class_table': SJIS_CLS, + 'class_factor': 6, + 'state_table': SJIS_ST, + 'char_len_table': SJIS_CHAR_LEN_TABLE, + 'name': 'Shift_JIS'} + +# UCS2-BE + +UCS2BE_CLS = ( + 0,0,0,0,0,0,0,0, # 00 - 07 + 0,0,1,0,0,2,0,0, # 08 - 0f + 0,0,0,0,0,0,0,0, # 10 - 17 + 0,0,0,3,0,0,0,0, # 18 - 1f + 0,0,0,0,0,0,0,0, # 20 - 27 + 0,3,3,3,3,3,0,0, # 28 - 2f + 0,0,0,0,0,0,0,0, # 30 - 37 + 0,0,0,0,0,0,0,0, # 38 - 3f + 0,0,0,0,0,0,0,0, # 40 - 47 + 0,0,0,0,0,0,0,0, # 48 - 4f + 0,0,0,0,0,0,0,0, # 50 - 57 + 0,0,0,0,0,0,0,0, # 58 - 5f + 0,0,0,0,0,0,0,0, # 60 - 67 + 0,0,0,0,0,0,0,0, # 68 - 6f + 0,0,0,0,0,0,0,0, # 70 - 77 + 0,0,0,0,0,0,0,0, # 78 - 7f + 0,0,0,0,0,0,0,0, # 80 - 87 + 0,0,0,0,0,0,0,0, # 88 - 8f + 0,0,0,0,0,0,0,0, # 90 - 97 + 0,0,0,0,0,0,0,0, # 98 - 9f + 0,0,0,0,0,0,0,0, # a0 - a7 + 0,0,0,0,0,0,0,0, # a8 - af + 0,0,0,0,0,0,0,0, # b0 - b7 + 0,0,0,0,0,0,0,0, # b8 - bf + 0,0,0,0,0,0,0,0, # c0 - c7 + 0,0,0,0,0,0,0,0, # c8 - cf + 0,0,0,0,0,0,0,0, # d0 - d7 + 0,0,0,0,0,0,0,0, # d8 - df + 0,0,0,0,0,0,0,0, # e0 - e7 + 0,0,0,0,0,0,0,0, # e8 - ef + 0,0,0,0,0,0,0,0, # f0 - f7 + 0,0,0,0,0,0,4,5 # f8 - ff +) + +UCS2BE_ST = ( + 5, 7, 7,MachineState.ERROR, 4, 3,MachineState.ERROR,MachineState.ERROR,#00-07 + MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ITS_ME,MachineState.ITS_ME,MachineState.ITS_ME,MachineState.ITS_ME,#08-0f + MachineState.ITS_ME,MachineState.ITS_ME, 6, 6, 6, 6,MachineState.ERROR,MachineState.ERROR,#10-17 + 6, 6, 6, 6, 6,MachineState.ITS_ME, 6, 6,#18-1f + 6, 6, 6, 6, 5, 7, 7,MachineState.ERROR,#20-27 + 5, 8, 6, 6,MachineState.ERROR, 6, 6, 6,#28-2f + 6, 6, 6, 6,MachineState.ERROR,MachineState.ERROR,MachineState.START,MachineState.START #30-37 +) + +UCS2BE_CHAR_LEN_TABLE = (2, 2, 2, 0, 2, 2) + +UCS2BE_SM_MODEL = {'class_table': UCS2BE_CLS, + 'class_factor': 6, + 'state_table': UCS2BE_ST, + 'char_len_table': UCS2BE_CHAR_LEN_TABLE, + 'name': 'UTF-16BE'} + +# UCS2-LE + +UCS2LE_CLS = ( + 0,0,0,0,0,0,0,0, # 00 - 07 + 0,0,1,0,0,2,0,0, # 08 - 0f + 0,0,0,0,0,0,0,0, # 10 - 17 + 0,0,0,3,0,0,0,0, # 18 - 1f + 0,0,0,0,0,0,0,0, # 20 - 27 + 0,3,3,3,3,3,0,0, # 28 - 2f + 0,0,0,0,0,0,0,0, # 30 - 37 + 0,0,0,0,0,0,0,0, # 38 - 3f + 0,0,0,0,0,0,0,0, # 40 - 47 + 0,0,0,0,0,0,0,0, # 48 - 4f + 0,0,0,0,0,0,0,0, # 50 - 57 + 0,0,0,0,0,0,0,0, # 58 - 5f + 0,0,0,0,0,0,0,0, # 60 - 67 + 0,0,0,0,0,0,0,0, # 68 - 6f + 0,0,0,0,0,0,0,0, # 70 - 77 + 0,0,0,0,0,0,0,0, # 78 - 7f + 0,0,0,0,0,0,0,0, # 80 - 87 + 0,0,0,0,0,0,0,0, # 88 - 8f + 0,0,0,0,0,0,0,0, # 90 - 97 + 0,0,0,0,0,0,0,0, # 98 - 9f + 0,0,0,0,0,0,0,0, # a0 - a7 + 0,0,0,0,0,0,0,0, # a8 - af + 0,0,0,0,0,0,0,0, # b0 - b7 + 0,0,0,0,0,0,0,0, # b8 - bf + 0,0,0,0,0,0,0,0, # c0 - c7 + 0,0,0,0,0,0,0,0, # c8 - cf + 0,0,0,0,0,0,0,0, # d0 - d7 + 0,0,0,0,0,0,0,0, # d8 - df + 0,0,0,0,0,0,0,0, # e0 - e7 + 0,0,0,0,0,0,0,0, # e8 - ef + 0,0,0,0,0,0,0,0, # f0 - f7 + 0,0,0,0,0,0,4,5 # f8 - ff +) + +UCS2LE_ST = ( + 6, 6, 7, 6, 4, 3,MachineState.ERROR,MachineState.ERROR,#00-07 + MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ITS_ME,MachineState.ITS_ME,MachineState.ITS_ME,MachineState.ITS_ME,#08-0f + MachineState.ITS_ME,MachineState.ITS_ME, 5, 5, 5,MachineState.ERROR,MachineState.ITS_ME,MachineState.ERROR,#10-17 + 5, 5, 5,MachineState.ERROR, 5,MachineState.ERROR, 6, 6,#18-1f + 7, 6, 8, 8, 5, 5, 5,MachineState.ERROR,#20-27 + 5, 5, 5,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR, 5, 5,#28-2f + 5, 5, 5,MachineState.ERROR, 5,MachineState.ERROR,MachineState.START,MachineState.START #30-37 +) + +UCS2LE_CHAR_LEN_TABLE = (2, 2, 2, 2, 2, 2) + +UCS2LE_SM_MODEL = {'class_table': UCS2LE_CLS, + 'class_factor': 6, + 'state_table': UCS2LE_ST, + 'char_len_table': UCS2LE_CHAR_LEN_TABLE, + 'name': 'UTF-16LE'} + +# UTF-8 + +UTF8_CLS = ( + 1,1,1,1,1,1,1,1, # 00 - 07 #allow 0x00 as a legal value + 1,1,1,1,1,1,0,0, # 08 - 0f + 1,1,1,1,1,1,1,1, # 10 - 17 + 1,1,1,0,1,1,1,1, # 18 - 1f + 1,1,1,1,1,1,1,1, # 20 - 27 + 1,1,1,1,1,1,1,1, # 28 - 2f + 1,1,1,1,1,1,1,1, # 30 - 37 + 1,1,1,1,1,1,1,1, # 38 - 3f + 1,1,1,1,1,1,1,1, # 40 - 47 + 1,1,1,1,1,1,1,1, # 48 - 4f + 1,1,1,1,1,1,1,1, # 50 - 57 + 1,1,1,1,1,1,1,1, # 58 - 5f + 1,1,1,1,1,1,1,1, # 60 - 67 + 1,1,1,1,1,1,1,1, # 68 - 6f + 1,1,1,1,1,1,1,1, # 70 - 77 + 1,1,1,1,1,1,1,1, # 78 - 7f + 2,2,2,2,3,3,3,3, # 80 - 87 + 4,4,4,4,4,4,4,4, # 88 - 8f + 4,4,4,4,4,4,4,4, # 90 - 97 + 4,4,4,4,4,4,4,4, # 98 - 9f + 5,5,5,5,5,5,5,5, # a0 - a7 + 5,5,5,5,5,5,5,5, # a8 - af + 5,5,5,5,5,5,5,5, # b0 - b7 + 5,5,5,5,5,5,5,5, # b8 - bf + 0,0,6,6,6,6,6,6, # c0 - c7 + 6,6,6,6,6,6,6,6, # c8 - cf + 6,6,6,6,6,6,6,6, # d0 - d7 + 6,6,6,6,6,6,6,6, # d8 - df + 7,8,8,8,8,8,8,8, # e0 - e7 + 8,8,8,8,8,9,8,8, # e8 - ef + 10,11,11,11,11,11,11,11, # f0 - f7 + 12,13,13,13,14,15,0,0 # f8 - ff +) + +UTF8_ST = ( + MachineState.ERROR,MachineState.START,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR, 12, 10,#00-07 + 9, 11, 8, 7, 6, 5, 4, 3,#08-0f + MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,#10-17 + MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,#18-1f + MachineState.ITS_ME,MachineState.ITS_ME,MachineState.ITS_ME,MachineState.ITS_ME,MachineState.ITS_ME,MachineState.ITS_ME,MachineState.ITS_ME,MachineState.ITS_ME,#20-27 + MachineState.ITS_ME,MachineState.ITS_ME,MachineState.ITS_ME,MachineState.ITS_ME,MachineState.ITS_ME,MachineState.ITS_ME,MachineState.ITS_ME,MachineState.ITS_ME,#28-2f + MachineState.ERROR,MachineState.ERROR, 5, 5, 5, 5,MachineState.ERROR,MachineState.ERROR,#30-37 + MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,#38-3f + MachineState.ERROR,MachineState.ERROR,MachineState.ERROR, 5, 5, 5,MachineState.ERROR,MachineState.ERROR,#40-47 + MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,#48-4f + MachineState.ERROR,MachineState.ERROR, 7, 7, 7, 7,MachineState.ERROR,MachineState.ERROR,#50-57 + MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,#58-5f + MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR, 7, 7,MachineState.ERROR,MachineState.ERROR,#60-67 + MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,#68-6f + MachineState.ERROR,MachineState.ERROR, 9, 9, 9, 9,MachineState.ERROR,MachineState.ERROR,#70-77 + MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,#78-7f + MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR, 9,MachineState.ERROR,MachineState.ERROR,#80-87 + MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,#88-8f + MachineState.ERROR,MachineState.ERROR, 12, 12, 12, 12,MachineState.ERROR,MachineState.ERROR,#90-97 + MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,#98-9f + MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR, 12,MachineState.ERROR,MachineState.ERROR,#a0-a7 + MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,#a8-af + MachineState.ERROR,MachineState.ERROR, 12, 12, 12,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,#b0-b7 + MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,#b8-bf + MachineState.ERROR,MachineState.ERROR,MachineState.START,MachineState.START,MachineState.START,MachineState.START,MachineState.ERROR,MachineState.ERROR,#c0-c7 + MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR #c8-cf +) + +UTF8_CHAR_LEN_TABLE = (0, 1, 0, 0, 0, 0, 2, 3, 3, 3, 4, 4, 5, 5, 6, 6) + +UTF8_SM_MODEL = {'class_table': UTF8_CLS, + 'class_factor': 16, + 'state_table': UTF8_ST, + 'char_len_table': UTF8_CHAR_LEN_TABLE, + 'name': 'UTF-8'} diff --git a/venv2/lib/python2.7/site-packages/pip/_vendor/chardet/sbcharsetprober.py b/venv2/lib/python2.7/site-packages/pip/_vendor/chardet/sbcharsetprober.py new file mode 100644 index 0000000..0adb51d --- /dev/null +++ b/venv2/lib/python2.7/site-packages/pip/_vendor/chardet/sbcharsetprober.py @@ -0,0 +1,132 @@ +######################## BEGIN LICENSE BLOCK ######################## +# The Original Code is Mozilla Universal charset detector code. +# +# The Initial Developer of the Original Code is +# Netscape Communications Corporation. +# Portions created by the Initial Developer are Copyright (C) 2001 +# the Initial Developer. All Rights Reserved. +# +# Contributor(s): +# Mark Pilgrim - port to Python +# Shy Shalom - original C code +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA +# 02110-1301 USA +######################### END LICENSE BLOCK ######################### + +from .charsetprober import CharSetProber +from .enums import CharacterCategory, ProbingState, SequenceLikelihood + + +class SingleByteCharSetProber(CharSetProber): + SAMPLE_SIZE = 64 + SB_ENOUGH_REL_THRESHOLD = 1024 # 0.25 * SAMPLE_SIZE^2 + POSITIVE_SHORTCUT_THRESHOLD = 0.95 + NEGATIVE_SHORTCUT_THRESHOLD = 0.05 + + def __init__(self, model, reversed=False, name_prober=None): + super(SingleByteCharSetProber, self).__init__() + self._model = model + # TRUE if we need to reverse every pair in the model lookup + self._reversed = reversed + # Optional auxiliary prober for name decision + self._name_prober = name_prober + self._last_order = None + self._seq_counters = None + self._total_seqs = None + self._total_char = None + self._freq_char = None + self.reset() + + def reset(self): + super(SingleByteCharSetProber, self).reset() + # char order of last character + self._last_order = 255 + self._seq_counters = [0] * SequenceLikelihood.get_num_categories() + self._total_seqs = 0 + self._total_char = 0 + # characters that fall in our sampling range + self._freq_char = 0 + + @property + def charset_name(self): + if self._name_prober: + return self._name_prober.charset_name + else: + return self._model['charset_name'] + + @property + def language(self): + if self._name_prober: + return self._name_prober.language + else: + return self._model.get('language') + + def feed(self, byte_str): + if not self._model['keep_english_letter']: + byte_str = self.filter_international_words(byte_str) + if not byte_str: + return self.state + char_to_order_map = self._model['char_to_order_map'] + for i, c in enumerate(byte_str): + # XXX: Order is in range 1-64, so one would think we want 0-63 here, + # but that leads to 27 more test failures than before. + order = char_to_order_map[c] + # XXX: This was SYMBOL_CAT_ORDER before, with a value of 250, but + # CharacterCategory.SYMBOL is actually 253, so we use CONTROL + # to make it closer to the original intent. The only difference + # is whether or not we count digits and control characters for + # _total_char purposes. + if order < CharacterCategory.CONTROL: + self._total_char += 1 + if order < self.SAMPLE_SIZE: + self._freq_char += 1 + if self._last_order < self.SAMPLE_SIZE: + self._total_seqs += 1 + if not self._reversed: + i = (self._last_order * self.SAMPLE_SIZE) + order + model = self._model['precedence_matrix'][i] + else: # reverse the order of the letters in the lookup + i = (order * self.SAMPLE_SIZE) + self._last_order + model = self._model['precedence_matrix'][i] + self._seq_counters[model] += 1 + self._last_order = order + + charset_name = self._model['charset_name'] + if self.state == ProbingState.DETECTING: + if self._total_seqs > self.SB_ENOUGH_REL_THRESHOLD: + confidence = self.get_confidence() + if confidence > self.POSITIVE_SHORTCUT_THRESHOLD: + self.logger.debug('%s confidence = %s, we have a winner', + charset_name, confidence) + self._state = ProbingState.FOUND_IT + elif confidence < self.NEGATIVE_SHORTCUT_THRESHOLD: + self.logger.debug('%s confidence = %s, below negative ' + 'shortcut threshhold %s', charset_name, + confidence, + self.NEGATIVE_SHORTCUT_THRESHOLD) + self._state = ProbingState.NOT_ME + + return self.state + + def get_confidence(self): + r = 0.01 + if self._total_seqs > 0: + r = ((1.0 * self._seq_counters[SequenceLikelihood.POSITIVE]) / + self._total_seqs / self._model['typical_positive_ratio']) + r = r * self._freq_char / self._total_char + if r >= 1.0: + r = 0.99 + return r diff --git a/venv2/lib/python2.7/site-packages/pip/_vendor/chardet/sbcsgroupprober.py b/venv2/lib/python2.7/site-packages/pip/_vendor/chardet/sbcsgroupprober.py new file mode 100644 index 0000000..98e95dc --- /dev/null +++ b/venv2/lib/python2.7/site-packages/pip/_vendor/chardet/sbcsgroupprober.py @@ -0,0 +1,73 @@ +######################## BEGIN LICENSE BLOCK ######################## +# The Original Code is Mozilla Universal charset detector code. +# +# The Initial Developer of the Original Code is +# Netscape Communications Corporation. +# Portions created by the Initial Developer are Copyright (C) 2001 +# the Initial Developer. All Rights Reserved. +# +# Contributor(s): +# Mark Pilgrim - port to Python +# Shy Shalom - original C code +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA +# 02110-1301 USA +######################### END LICENSE BLOCK ######################### + +from .charsetgroupprober import CharSetGroupProber +from .sbcharsetprober import SingleByteCharSetProber +from .langcyrillicmodel import (Win1251CyrillicModel, Koi8rModel, + Latin5CyrillicModel, MacCyrillicModel, + Ibm866Model, Ibm855Model) +from .langgreekmodel import Latin7GreekModel, Win1253GreekModel +from .langbulgarianmodel import Latin5BulgarianModel, Win1251BulgarianModel +# from .langhungarianmodel import Latin2HungarianModel, Win1250HungarianModel +from .langthaimodel import TIS620ThaiModel +from .langhebrewmodel import Win1255HebrewModel +from .hebrewprober import HebrewProber +from .langturkishmodel import Latin5TurkishModel + + +class SBCSGroupProber(CharSetGroupProber): + def __init__(self): + super(SBCSGroupProber, self).__init__() + self.probers = [ + SingleByteCharSetProber(Win1251CyrillicModel), + SingleByteCharSetProber(Koi8rModel), + SingleByteCharSetProber(Latin5CyrillicModel), + SingleByteCharSetProber(MacCyrillicModel), + SingleByteCharSetProber(Ibm866Model), + SingleByteCharSetProber(Ibm855Model), + SingleByteCharSetProber(Latin7GreekModel), + SingleByteCharSetProber(Win1253GreekModel), + SingleByteCharSetProber(Latin5BulgarianModel), + SingleByteCharSetProber(Win1251BulgarianModel), + # TODO: Restore Hungarian encodings (iso-8859-2 and windows-1250) + # after we retrain model. + # SingleByteCharSetProber(Latin2HungarianModel), + # SingleByteCharSetProber(Win1250HungarianModel), + SingleByteCharSetProber(TIS620ThaiModel), + SingleByteCharSetProber(Latin5TurkishModel), + ] + hebrew_prober = HebrewProber() + logical_hebrew_prober = SingleByteCharSetProber(Win1255HebrewModel, + False, hebrew_prober) + visual_hebrew_prober = SingleByteCharSetProber(Win1255HebrewModel, True, + hebrew_prober) + hebrew_prober.set_model_probers(logical_hebrew_prober, visual_hebrew_prober) + self.probers.extend([hebrew_prober, logical_hebrew_prober, + visual_hebrew_prober]) + + self.reset() diff --git a/venv2/lib/python2.7/site-packages/pip/_vendor/chardet/sjisprober.py b/venv2/lib/python2.7/site-packages/pip/_vendor/chardet/sjisprober.py new file mode 100644 index 0000000..9e29623 --- /dev/null +++ b/venv2/lib/python2.7/site-packages/pip/_vendor/chardet/sjisprober.py @@ -0,0 +1,92 @@ +######################## BEGIN LICENSE BLOCK ######################## +# The Original Code is mozilla.org code. +# +# The Initial Developer of the Original Code is +# Netscape Communications Corporation. +# Portions created by the Initial Developer are Copyright (C) 1998 +# the Initial Developer. All Rights Reserved. +# +# Contributor(s): +# Mark Pilgrim - port to Python +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA +# 02110-1301 USA +######################### END LICENSE BLOCK ######################### + +from .mbcharsetprober import MultiByteCharSetProber +from .codingstatemachine import CodingStateMachine +from .chardistribution import SJISDistributionAnalysis +from .jpcntx import SJISContextAnalysis +from .mbcssm import SJIS_SM_MODEL +from .enums import ProbingState, MachineState + + +class SJISProber(MultiByteCharSetProber): + def __init__(self): + super(SJISProber, self).__init__() + self.coding_sm = CodingStateMachine(SJIS_SM_MODEL) + self.distribution_analyzer = SJISDistributionAnalysis() + self.context_analyzer = SJISContextAnalysis() + self.reset() + + def reset(self): + super(SJISProber, self).reset() + self.context_analyzer.reset() + + @property + def charset_name(self): + return self.context_analyzer.charset_name + + @property + def language(self): + return "Japanese" + + def feed(self, byte_str): + for i in range(len(byte_str)): + coding_state = self.coding_sm.next_state(byte_str[i]) + if coding_state == MachineState.ERROR: + self.logger.debug('%s %s prober hit error at byte %s', + self.charset_name, self.language, i) + self._state = ProbingState.NOT_ME + break + elif coding_state == MachineState.ITS_ME: + self._state = ProbingState.FOUND_IT + break + elif coding_state == MachineState.START: + char_len = self.coding_sm.get_current_charlen() + if i == 0: + self._last_char[1] = byte_str[0] + self.context_analyzer.feed(self._last_char[2 - char_len:], + char_len) + self.distribution_analyzer.feed(self._last_char, char_len) + else: + self.context_analyzer.feed(byte_str[i + 1 - char_len:i + 3 + - char_len], char_len) + self.distribution_analyzer.feed(byte_str[i - 1:i + 1], + char_len) + + self._last_char[0] = byte_str[-1] + + if self.state == ProbingState.DETECTING: + if (self.context_analyzer.got_enough_data() and + (self.get_confidence() > self.SHORTCUT_THRESHOLD)): + self._state = ProbingState.FOUND_IT + + return self.state + + def get_confidence(self): + context_conf = self.context_analyzer.get_confidence() + distrib_conf = self.distribution_analyzer.get_confidence() + return max(context_conf, distrib_conf) diff --git a/venv2/lib/python2.7/site-packages/pip/_vendor/chardet/universaldetector.py b/venv2/lib/python2.7/site-packages/pip/_vendor/chardet/universaldetector.py new file mode 100644 index 0000000..7b4e92d --- /dev/null +++ b/venv2/lib/python2.7/site-packages/pip/_vendor/chardet/universaldetector.py @@ -0,0 +1,286 @@ +######################## BEGIN LICENSE BLOCK ######################## +# The Original Code is Mozilla Universal charset detector code. +# +# The Initial Developer of the Original Code is +# Netscape Communications Corporation. +# Portions created by the Initial Developer are Copyright (C) 2001 +# the Initial Developer. All Rights Reserved. +# +# Contributor(s): +# Mark Pilgrim - port to Python +# Shy Shalom - original C code +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA +# 02110-1301 USA +######################### END LICENSE BLOCK ######################### +""" +Module containing the UniversalDetector detector class, which is the primary +class a user of ``chardet`` should use. + +:author: Mark Pilgrim (initial port to Python) +:author: Shy Shalom (original C code) +:author: Dan Blanchard (major refactoring for 3.0) +:author: Ian Cordasco +""" + + +import codecs +import logging +import re + +from .charsetgroupprober import CharSetGroupProber +from .enums import InputState, LanguageFilter, ProbingState +from .escprober import EscCharSetProber +from .latin1prober import Latin1Prober +from .mbcsgroupprober import MBCSGroupProber +from .sbcsgroupprober import SBCSGroupProber + + +class UniversalDetector(object): + """ + The ``UniversalDetector`` class underlies the ``chardet.detect`` function + and coordinates all of the different charset probers. + + To get a ``dict`` containing an encoding and its confidence, you can simply + run: + + .. code:: + + u = UniversalDetector() + u.feed(some_bytes) + u.close() + detected = u.result + + """ + + MINIMUM_THRESHOLD = 0.20 + HIGH_BYTE_DETECTOR = re.compile(b'[\x80-\xFF]') + ESC_DETECTOR = re.compile(b'(\033|~{)') + WIN_BYTE_DETECTOR = re.compile(b'[\x80-\x9F]') + ISO_WIN_MAP = {'iso-8859-1': 'Windows-1252', + 'iso-8859-2': 'Windows-1250', + 'iso-8859-5': 'Windows-1251', + 'iso-8859-6': 'Windows-1256', + 'iso-8859-7': 'Windows-1253', + 'iso-8859-8': 'Windows-1255', + 'iso-8859-9': 'Windows-1254', + 'iso-8859-13': 'Windows-1257'} + + def __init__(self, lang_filter=LanguageFilter.ALL): + self._esc_charset_prober = None + self._charset_probers = [] + self.result = None + self.done = None + self._got_data = None + self._input_state = None + self._last_char = None + self.lang_filter = lang_filter + self.logger = logging.getLogger(__name__) + self._has_win_bytes = None + self.reset() + + def reset(self): + """ + Reset the UniversalDetector and all of its probers back to their + initial states. This is called by ``__init__``, so you only need to + call this directly in between analyses of different documents. + """ + self.result = {'encoding': None, 'confidence': 0.0, 'language': None} + self.done = False + self._got_data = False + self._has_win_bytes = False + self._input_state = InputState.PURE_ASCII + self._last_char = b'' + if self._esc_charset_prober: + self._esc_charset_prober.reset() + for prober in self._charset_probers: + prober.reset() + + def feed(self, byte_str): + """ + Takes a chunk of a document and feeds it through all of the relevant + charset probers. + + After calling ``feed``, you can check the value of the ``done`` + attribute to see if you need to continue feeding the + ``UniversalDetector`` more data, or if it has made a prediction + (in the ``result`` attribute). + + .. note:: + You should always call ``close`` when you're done feeding in your + document if ``done`` is not already ``True``. + """ + if self.done: + return + + if not len(byte_str): + return + + if not isinstance(byte_str, bytearray): + byte_str = bytearray(byte_str) + + # First check for known BOMs, since these are guaranteed to be correct + if not self._got_data: + # If the data starts with BOM, we know it is UTF + if byte_str.startswith(codecs.BOM_UTF8): + # EF BB BF UTF-8 with BOM + self.result = {'encoding': "UTF-8-SIG", + 'confidence': 1.0, + 'language': ''} + elif byte_str.startswith((codecs.BOM_UTF32_LE, + codecs.BOM_UTF32_BE)): + # FF FE 00 00 UTF-32, little-endian BOM + # 00 00 FE FF UTF-32, big-endian BOM + self.result = {'encoding': "UTF-32", + 'confidence': 1.0, + 'language': ''} + elif byte_str.startswith(b'\xFE\xFF\x00\x00'): + # FE FF 00 00 UCS-4, unusual octet order BOM (3412) + self.result = {'encoding': "X-ISO-10646-UCS-4-3412", + 'confidence': 1.0, + 'language': ''} + elif byte_str.startswith(b'\x00\x00\xFF\xFE'): + # 00 00 FF FE UCS-4, unusual octet order BOM (2143) + self.result = {'encoding': "X-ISO-10646-UCS-4-2143", + 'confidence': 1.0, + 'language': ''} + elif byte_str.startswith((codecs.BOM_LE, codecs.BOM_BE)): + # FF FE UTF-16, little endian BOM + # FE FF UTF-16, big endian BOM + self.result = {'encoding': "UTF-16", + 'confidence': 1.0, + 'language': ''} + + self._got_data = True + if self.result['encoding'] is not None: + self.done = True + return + + # If none of those matched and we've only see ASCII so far, check + # for high bytes and escape sequences + if self._input_state == InputState.PURE_ASCII: + if self.HIGH_BYTE_DETECTOR.search(byte_str): + self._input_state = InputState.HIGH_BYTE + elif self._input_state == InputState.PURE_ASCII and \ + self.ESC_DETECTOR.search(self._last_char + byte_str): + self._input_state = InputState.ESC_ASCII + + self._last_char = byte_str[-1:] + + # If we've seen escape sequences, use the EscCharSetProber, which + # uses a simple state machine to check for known escape sequences in + # HZ and ISO-2022 encodings, since those are the only encodings that + # use such sequences. + if self._input_state == InputState.ESC_ASCII: + if not self._esc_charset_prober: + self._esc_charset_prober = EscCharSetProber(self.lang_filter) + if self._esc_charset_prober.feed(byte_str) == ProbingState.FOUND_IT: + self.result = {'encoding': + self._esc_charset_prober.charset_name, + 'confidence': + self._esc_charset_prober.get_confidence(), + 'language': + self._esc_charset_prober.language} + self.done = True + # If we've seen high bytes (i.e., those with values greater than 127), + # we need to do more complicated checks using all our multi-byte and + # single-byte probers that are left. The single-byte probers + # use character bigram distributions to determine the encoding, whereas + # the multi-byte probers use a combination of character unigram and + # bigram distributions. + elif self._input_state == InputState.HIGH_BYTE: + if not self._charset_probers: + self._charset_probers = [MBCSGroupProber(self.lang_filter)] + # If we're checking non-CJK encodings, use single-byte prober + if self.lang_filter & LanguageFilter.NON_CJK: + self._charset_probers.append(SBCSGroupProber()) + self._charset_probers.append(Latin1Prober()) + for prober in self._charset_probers: + if prober.feed(byte_str) == ProbingState.FOUND_IT: + self.result = {'encoding': prober.charset_name, + 'confidence': prober.get_confidence(), + 'language': prober.language} + self.done = True + break + if self.WIN_BYTE_DETECTOR.search(byte_str): + self._has_win_bytes = True + + def close(self): + """ + Stop analyzing the current document and come up with a final + prediction. + + :returns: The ``result`` attribute, a ``dict`` with the keys + `encoding`, `confidence`, and `language`. + """ + # Don't bother with checks if we're already done + if self.done: + return self.result + self.done = True + + if not self._got_data: + self.logger.debug('no data received!') + + # Default to ASCII if it is all we've seen so far + elif self._input_state == InputState.PURE_ASCII: + self.result = {'encoding': 'ascii', + 'confidence': 1.0, + 'language': ''} + + # If we have seen non-ASCII, return the best that met MINIMUM_THRESHOLD + elif self._input_state == InputState.HIGH_BYTE: + prober_confidence = None + max_prober_confidence = 0.0 + max_prober = None + for prober in self._charset_probers: + if not prober: + continue + prober_confidence = prober.get_confidence() + if prober_confidence > max_prober_confidence: + max_prober_confidence = prober_confidence + max_prober = prober + if max_prober and (max_prober_confidence > self.MINIMUM_THRESHOLD): + charset_name = max_prober.charset_name + lower_charset_name = max_prober.charset_name.lower() + confidence = max_prober.get_confidence() + # Use Windows encoding name instead of ISO-8859 if we saw any + # extra Windows-specific bytes + if lower_charset_name.startswith('iso-8859'): + if self._has_win_bytes: + charset_name = self.ISO_WIN_MAP.get(lower_charset_name, + charset_name) + self.result = {'encoding': charset_name, + 'confidence': confidence, + 'language': max_prober.language} + + # Log all prober confidences if none met MINIMUM_THRESHOLD + if self.logger.getEffectiveLevel() == logging.DEBUG: + if self.result['encoding'] is None: + self.logger.debug('no probers hit minimum threshold') + for group_prober in self._charset_probers: + if not group_prober: + continue + if isinstance(group_prober, CharSetGroupProber): + for prober in group_prober.probers: + self.logger.debug('%s %s confidence = %s', + prober.charset_name, + prober.language, + prober.get_confidence()) + else: + self.logger.debug('%s %s confidence = %s', + prober.charset_name, + prober.language, + prober.get_confidence()) + return self.result diff --git a/venv2/lib/python2.7/site-packages/pip/_vendor/chardet/utf8prober.py b/venv2/lib/python2.7/site-packages/pip/_vendor/chardet/utf8prober.py new file mode 100644 index 0000000..6c3196c --- /dev/null +++ b/venv2/lib/python2.7/site-packages/pip/_vendor/chardet/utf8prober.py @@ -0,0 +1,82 @@ +######################## BEGIN LICENSE BLOCK ######################## +# The Original Code is mozilla.org code. +# +# The Initial Developer of the Original Code is +# Netscape Communications Corporation. +# Portions created by the Initial Developer are Copyright (C) 1998 +# the Initial Developer. All Rights Reserved. +# +# Contributor(s): +# Mark Pilgrim - port to Python +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA +# 02110-1301 USA +######################### END LICENSE BLOCK ######################### + +from .charsetprober import CharSetProber +from .enums import ProbingState, MachineState +from .codingstatemachine import CodingStateMachine +from .mbcssm import UTF8_SM_MODEL + + + +class UTF8Prober(CharSetProber): + ONE_CHAR_PROB = 0.5 + + def __init__(self): + super(UTF8Prober, self).__init__() + self.coding_sm = CodingStateMachine(UTF8_SM_MODEL) + self._num_mb_chars = None + self.reset() + + def reset(self): + super(UTF8Prober, self).reset() + self.coding_sm.reset() + self._num_mb_chars = 0 + + @property + def charset_name(self): + return "utf-8" + + @property + def language(self): + return "" + + def feed(self, byte_str): + for c in byte_str: + coding_state = self.coding_sm.next_state(c) + if coding_state == MachineState.ERROR: + self._state = ProbingState.NOT_ME + break + elif coding_state == MachineState.ITS_ME: + self._state = ProbingState.FOUND_IT + break + elif coding_state == MachineState.START: + if self.coding_sm.get_current_charlen() >= 2: + self._num_mb_chars += 1 + + if self.state == ProbingState.DETECTING: + if self.get_confidence() > self.SHORTCUT_THRESHOLD: + self._state = ProbingState.FOUND_IT + + return self.state + + def get_confidence(self): + unlike = 0.99 + if self._num_mb_chars < 6: + unlike *= self.ONE_CHAR_PROB ** self._num_mb_chars + return 1.0 - unlike + else: + return unlike diff --git a/venv2/lib/python2.7/site-packages/pip/_vendor/chardet/version.py b/venv2/lib/python2.7/site-packages/pip/_vendor/chardet/version.py new file mode 100644 index 0000000..bb2a34a --- /dev/null +++ b/venv2/lib/python2.7/site-packages/pip/_vendor/chardet/version.py @@ -0,0 +1,9 @@ +""" +This module exists only to simplify retrieving the version number of chardet +from within setup.py and from chardet subpackages. + +:author: Dan Blanchard (dan.blanchard@gmail.com) +""" + +__version__ = "3.0.4" +VERSION = __version__.split('.') diff --git a/venv2/lib/python2.7/site-packages/pip/_vendor/colorama/__init__.py b/venv2/lib/python2.7/site-packages/pip/_vendor/colorama/__init__.py new file mode 100644 index 0000000..2a3bf47 --- /dev/null +++ b/venv2/lib/python2.7/site-packages/pip/_vendor/colorama/__init__.py @@ -0,0 +1,6 @@ +# Copyright Jonathan Hartley 2013. BSD 3-Clause license, see LICENSE file. +from .initialise import init, deinit, reinit, colorama_text +from .ansi import Fore, Back, Style, Cursor +from .ansitowin32 import AnsiToWin32 + +__version__ = '0.4.1' diff --git a/venv2/lib/python2.7/site-packages/pip/_vendor/colorama/ansi.py b/venv2/lib/python2.7/site-packages/pip/_vendor/colorama/ansi.py new file mode 100644 index 0000000..7877658 --- /dev/null +++ b/venv2/lib/python2.7/site-packages/pip/_vendor/colorama/ansi.py @@ -0,0 +1,102 @@ +# Copyright Jonathan Hartley 2013. BSD 3-Clause license, see LICENSE file. +''' +This module generates ANSI character codes to printing colors to terminals. +See: http://en.wikipedia.org/wiki/ANSI_escape_code +''' + +CSI = '\033[' +OSC = '\033]' +BEL = '\007' + + +def code_to_chars(code): + return CSI + str(code) + 'm' + +def set_title(title): + return OSC + '2;' + title + BEL + +def clear_screen(mode=2): + return CSI + str(mode) + 'J' + +def clear_line(mode=2): + return CSI + str(mode) + 'K' + + +class AnsiCodes(object): + def __init__(self): + # the subclasses declare class attributes which are numbers. + # Upon instantiation we define instance attributes, which are the same + # as the class attributes but wrapped with the ANSI escape sequence + for name in dir(self): + if not name.startswith('_'): + value = getattr(self, name) + setattr(self, name, code_to_chars(value)) + + +class AnsiCursor(object): + def UP(self, n=1): + return CSI + str(n) + 'A' + def DOWN(self, n=1): + return CSI + str(n) + 'B' + def FORWARD(self, n=1): + return CSI + str(n) + 'C' + def BACK(self, n=1): + return CSI + str(n) + 'D' + def POS(self, x=1, y=1): + return CSI + str(y) + ';' + str(x) + 'H' + + +class AnsiFore(AnsiCodes): + BLACK = 30 + RED = 31 + GREEN = 32 + YELLOW = 33 + BLUE = 34 + MAGENTA = 35 + CYAN = 36 + WHITE = 37 + RESET = 39 + + # These are fairly well supported, but not part of the standard. + LIGHTBLACK_EX = 90 + LIGHTRED_EX = 91 + LIGHTGREEN_EX = 92 + LIGHTYELLOW_EX = 93 + LIGHTBLUE_EX = 94 + LIGHTMAGENTA_EX = 95 + LIGHTCYAN_EX = 96 + LIGHTWHITE_EX = 97 + + +class AnsiBack(AnsiCodes): + BLACK = 40 + RED = 41 + GREEN = 42 + YELLOW = 43 + BLUE = 44 + MAGENTA = 45 + CYAN = 46 + WHITE = 47 + RESET = 49 + + # These are fairly well supported, but not part of the standard. + LIGHTBLACK_EX = 100 + LIGHTRED_EX = 101 + LIGHTGREEN_EX = 102 + LIGHTYELLOW_EX = 103 + LIGHTBLUE_EX = 104 + LIGHTMAGENTA_EX = 105 + LIGHTCYAN_EX = 106 + LIGHTWHITE_EX = 107 + + +class AnsiStyle(AnsiCodes): + BRIGHT = 1 + DIM = 2 + NORMAL = 22 + RESET_ALL = 0 + +Fore = AnsiFore() +Back = AnsiBack() +Style = AnsiStyle() +Cursor = AnsiCursor() diff --git a/venv2/lib/python2.7/site-packages/pip/_vendor/colorama/ansitowin32.py b/venv2/lib/python2.7/site-packages/pip/_vendor/colorama/ansitowin32.py new file mode 100644 index 0000000..359c92b --- /dev/null +++ b/venv2/lib/python2.7/site-packages/pip/_vendor/colorama/ansitowin32.py @@ -0,0 +1,257 @@ +# Copyright Jonathan Hartley 2013. BSD 3-Clause license, see LICENSE file. +import re +import sys +import os + +from .ansi import AnsiFore, AnsiBack, AnsiStyle, Style +from .winterm import WinTerm, WinColor, WinStyle +from .win32 import windll, winapi_test + + +winterm = None +if windll is not None: + winterm = WinTerm() + + +class StreamWrapper(object): + ''' + Wraps a stream (such as stdout), acting as a transparent proxy for all + attribute access apart from method 'write()', which is delegated to our + Converter instance. + ''' + def __init__(self, wrapped, converter): + # double-underscore everything to prevent clashes with names of + # attributes on the wrapped stream object. + self.__wrapped = wrapped + self.__convertor = converter + + def __getattr__(self, name): + return getattr(self.__wrapped, name) + + def __enter__(self, *args, **kwargs): + # special method lookup bypasses __getattr__/__getattribute__, see + # https://stackoverflow.com/questions/12632894/why-doesnt-getattr-work-with-exit + # thus, contextlib magic methods are not proxied via __getattr__ + return self.__wrapped.__enter__(*args, **kwargs) + + def __exit__(self, *args, **kwargs): + return self.__wrapped.__exit__(*args, **kwargs) + + def write(self, text): + self.__convertor.write(text) + + def isatty(self): + stream = self.__wrapped + if 'PYCHARM_HOSTED' in os.environ: + if stream is not None and (stream is sys.__stdout__ or stream is sys.__stderr__): + return True + try: + stream_isatty = stream.isatty + except AttributeError: + return False + else: + return stream_isatty() + + @property + def closed(self): + stream = self.__wrapped + try: + return stream.closed + except AttributeError: + return True + + +class AnsiToWin32(object): + ''' + Implements a 'write()' method which, on Windows, will strip ANSI character + sequences from the text, and if outputting to a tty, will convert them into + win32 function calls. + ''' + ANSI_CSI_RE = re.compile('\001?\033\\[((?:\\d|;)*)([a-zA-Z])\002?') # Control Sequence Introducer + ANSI_OSC_RE = re.compile('\001?\033\\]((?:.|;)*?)(\x07)\002?') # Operating System Command + + def __init__(self, wrapped, convert=None, strip=None, autoreset=False): + # The wrapped stream (normally sys.stdout or sys.stderr) + self.wrapped = wrapped + + # should we reset colors to defaults after every .write() + self.autoreset = autoreset + + # create the proxy wrapping our output stream + self.stream = StreamWrapper(wrapped, self) + + on_windows = os.name == 'nt' + # We test if the WinAPI works, because even if we are on Windows + # we may be using a terminal that doesn't support the WinAPI + # (e.g. Cygwin Terminal). In this case it's up to the terminal + # to support the ANSI codes. + conversion_supported = on_windows and winapi_test() + + # should we strip ANSI sequences from our output? + if strip is None: + strip = conversion_supported or (not self.stream.closed and not self.stream.isatty()) + self.strip = strip + + # should we should convert ANSI sequences into win32 calls? + if convert is None: + convert = conversion_supported and not self.stream.closed and self.stream.isatty() + self.convert = convert + + # dict of ansi codes to win32 functions and parameters + self.win32_calls = self.get_win32_calls() + + # are we wrapping stderr? + self.on_stderr = self.wrapped is sys.stderr + + def should_wrap(self): + ''' + True if this class is actually needed. If false, then the output + stream will not be affected, nor will win32 calls be issued, so + wrapping stdout is not actually required. This will generally be + False on non-Windows platforms, unless optional functionality like + autoreset has been requested using kwargs to init() + ''' + return self.convert or self.strip or self.autoreset + + def get_win32_calls(self): + if self.convert and winterm: + return { + AnsiStyle.RESET_ALL: (winterm.reset_all, ), + AnsiStyle.BRIGHT: (winterm.style, WinStyle.BRIGHT), + AnsiStyle.DIM: (winterm.style, WinStyle.NORMAL), + AnsiStyle.NORMAL: (winterm.style, WinStyle.NORMAL), + AnsiFore.BLACK: (winterm.fore, WinColor.BLACK), + AnsiFore.RED: (winterm.fore, WinColor.RED), + AnsiFore.GREEN: (winterm.fore, WinColor.GREEN), + AnsiFore.YELLOW: (winterm.fore, WinColor.YELLOW), + AnsiFore.BLUE: (winterm.fore, WinColor.BLUE), + AnsiFore.MAGENTA: (winterm.fore, WinColor.MAGENTA), + AnsiFore.CYAN: (winterm.fore, WinColor.CYAN), + AnsiFore.WHITE: (winterm.fore, WinColor.GREY), + AnsiFore.RESET: (winterm.fore, ), + AnsiFore.LIGHTBLACK_EX: (winterm.fore, WinColor.BLACK, True), + AnsiFore.LIGHTRED_EX: (winterm.fore, WinColor.RED, True), + AnsiFore.LIGHTGREEN_EX: (winterm.fore, WinColor.GREEN, True), + AnsiFore.LIGHTYELLOW_EX: (winterm.fore, WinColor.YELLOW, True), + AnsiFore.LIGHTBLUE_EX: (winterm.fore, WinColor.BLUE, True), + AnsiFore.LIGHTMAGENTA_EX: (winterm.fore, WinColor.MAGENTA, True), + AnsiFore.LIGHTCYAN_EX: (winterm.fore, WinColor.CYAN, True), + AnsiFore.LIGHTWHITE_EX: (winterm.fore, WinColor.GREY, True), + AnsiBack.BLACK: (winterm.back, WinColor.BLACK), + AnsiBack.RED: (winterm.back, WinColor.RED), + AnsiBack.GREEN: (winterm.back, WinColor.GREEN), + AnsiBack.YELLOW: (winterm.back, WinColor.YELLOW), + AnsiBack.BLUE: (winterm.back, WinColor.BLUE), + AnsiBack.MAGENTA: (winterm.back, WinColor.MAGENTA), + AnsiBack.CYAN: (winterm.back, WinColor.CYAN), + AnsiBack.WHITE: (winterm.back, WinColor.GREY), + AnsiBack.RESET: (winterm.back, ), + AnsiBack.LIGHTBLACK_EX: (winterm.back, WinColor.BLACK, True), + AnsiBack.LIGHTRED_EX: (winterm.back, WinColor.RED, True), + AnsiBack.LIGHTGREEN_EX: (winterm.back, WinColor.GREEN, True), + AnsiBack.LIGHTYELLOW_EX: (winterm.back, WinColor.YELLOW, True), + AnsiBack.LIGHTBLUE_EX: (winterm.back, WinColor.BLUE, True), + AnsiBack.LIGHTMAGENTA_EX: (winterm.back, WinColor.MAGENTA, True), + AnsiBack.LIGHTCYAN_EX: (winterm.back, WinColor.CYAN, True), + AnsiBack.LIGHTWHITE_EX: (winterm.back, WinColor.GREY, True), + } + return dict() + + def write(self, text): + if self.strip or self.convert: + self.write_and_convert(text) + else: + self.wrapped.write(text) + self.wrapped.flush() + if self.autoreset: + self.reset_all() + + + def reset_all(self): + if self.convert: + self.call_win32('m', (0,)) + elif not self.strip and not self.stream.closed: + self.wrapped.write(Style.RESET_ALL) + + + def write_and_convert(self, text): + ''' + Write the given text to our wrapped stream, stripping any ANSI + sequences from the text, and optionally converting them into win32 + calls. + ''' + cursor = 0 + text = self.convert_osc(text) + for match in self.ANSI_CSI_RE.finditer(text): + start, end = match.span() + self.write_plain_text(text, cursor, start) + self.convert_ansi(*match.groups()) + cursor = end + self.write_plain_text(text, cursor, len(text)) + + + def write_plain_text(self, text, start, end): + if start < end: + self.wrapped.write(text[start:end]) + self.wrapped.flush() + + + def convert_ansi(self, paramstring, command): + if self.convert: + params = self.extract_params(command, paramstring) + self.call_win32(command, params) + + + def extract_params(self, command, paramstring): + if command in 'Hf': + params = tuple(int(p) if len(p) != 0 else 1 for p in paramstring.split(';')) + while len(params) < 2: + # defaults: + params = params + (1,) + else: + params = tuple(int(p) for p in paramstring.split(';') if len(p) != 0) + if len(params) == 0: + # defaults: + if command in 'JKm': + params = (0,) + elif command in 'ABCD': + params = (1,) + + return params + + + def call_win32(self, command, params): + if command == 'm': + for param in params: + if param in self.win32_calls: + func_args = self.win32_calls[param] + func = func_args[0] + args = func_args[1:] + kwargs = dict(on_stderr=self.on_stderr) + func(*args, **kwargs) + elif command in 'J': + winterm.erase_screen(params[0], on_stderr=self.on_stderr) + elif command in 'K': + winterm.erase_line(params[0], on_stderr=self.on_stderr) + elif command in 'Hf': # cursor position - absolute + winterm.set_cursor_position(params, on_stderr=self.on_stderr) + elif command in 'ABCD': # cursor position - relative + n = params[0] + # A - up, B - down, C - forward, D - back + x, y = {'A': (0, -n), 'B': (0, n), 'C': (n, 0), 'D': (-n, 0)}[command] + winterm.cursor_adjust(x, y, on_stderr=self.on_stderr) + + + def convert_osc(self, text): + for match in self.ANSI_OSC_RE.finditer(text): + start, end = match.span() + text = text[:start] + text[end:] + paramstring, command = match.groups() + if command in '\x07': # \x07 = BEL + params = paramstring.split(";") + # 0 - change title and icon (we will only change title) + # 1 - change icon (we don't support this) + # 2 - change title + if params[0] in '02': + winterm.set_title(params[1]) + return text diff --git a/venv2/lib/python2.7/site-packages/pip/_vendor/colorama/initialise.py b/venv2/lib/python2.7/site-packages/pip/_vendor/colorama/initialise.py new file mode 100644 index 0000000..430d066 --- /dev/null +++ b/venv2/lib/python2.7/site-packages/pip/_vendor/colorama/initialise.py @@ -0,0 +1,80 @@ +# Copyright Jonathan Hartley 2013. BSD 3-Clause license, see LICENSE file. +import atexit +import contextlib +import sys + +from .ansitowin32 import AnsiToWin32 + + +orig_stdout = None +orig_stderr = None + +wrapped_stdout = None +wrapped_stderr = None + +atexit_done = False + + +def reset_all(): + if AnsiToWin32 is not None: # Issue #74: objects might become None at exit + AnsiToWin32(orig_stdout).reset_all() + + +def init(autoreset=False, convert=None, strip=None, wrap=True): + + if not wrap and any([autoreset, convert, strip]): + raise ValueError('wrap=False conflicts with any other arg=True') + + global wrapped_stdout, wrapped_stderr + global orig_stdout, orig_stderr + + orig_stdout = sys.stdout + orig_stderr = sys.stderr + + if sys.stdout is None: + wrapped_stdout = None + else: + sys.stdout = wrapped_stdout = \ + wrap_stream(orig_stdout, convert, strip, autoreset, wrap) + if sys.stderr is None: + wrapped_stderr = None + else: + sys.stderr = wrapped_stderr = \ + wrap_stream(orig_stderr, convert, strip, autoreset, wrap) + + global atexit_done + if not atexit_done: + atexit.register(reset_all) + atexit_done = True + + +def deinit(): + if orig_stdout is not None: + sys.stdout = orig_stdout + if orig_stderr is not None: + sys.stderr = orig_stderr + + +@contextlib.contextmanager +def colorama_text(*args, **kwargs): + init(*args, **kwargs) + try: + yield + finally: + deinit() + + +def reinit(): + if wrapped_stdout is not None: + sys.stdout = wrapped_stdout + if wrapped_stderr is not None: + sys.stderr = wrapped_stderr + + +def wrap_stream(stream, convert, strip, autoreset, wrap): + if wrap: + wrapper = AnsiToWin32(stream, + convert=convert, strip=strip, autoreset=autoreset) + if wrapper.should_wrap(): + stream = wrapper.stream + return stream diff --git a/venv2/lib/python2.7/site-packages/pip/_vendor/colorama/win32.py b/venv2/lib/python2.7/site-packages/pip/_vendor/colorama/win32.py new file mode 100644 index 0000000..c2d8360 --- /dev/null +++ b/venv2/lib/python2.7/site-packages/pip/_vendor/colorama/win32.py @@ -0,0 +1,152 @@ +# Copyright Jonathan Hartley 2013. BSD 3-Clause license, see LICENSE file. + +# from winbase.h +STDOUT = -11 +STDERR = -12 + +try: + import ctypes + from ctypes import LibraryLoader + windll = LibraryLoader(ctypes.WinDLL) + from ctypes import wintypes +except (AttributeError, ImportError): + windll = None + SetConsoleTextAttribute = lambda *_: None + winapi_test = lambda *_: None +else: + from ctypes import byref, Structure, c_char, POINTER + + COORD = wintypes._COORD + + class CONSOLE_SCREEN_BUFFER_INFO(Structure): + """struct in wincon.h.""" + _fields_ = [ + ("dwSize", COORD), + ("dwCursorPosition", COORD), + ("wAttributes", wintypes.WORD), + ("srWindow", wintypes.SMALL_RECT), + ("dwMaximumWindowSize", COORD), + ] + def __str__(self): + return '(%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d)' % ( + self.dwSize.Y, self.dwSize.X + , self.dwCursorPosition.Y, self.dwCursorPosition.X + , self.wAttributes + , self.srWindow.Top, self.srWindow.Left, self.srWindow.Bottom, self.srWindow.Right + , self.dwMaximumWindowSize.Y, self.dwMaximumWindowSize.X + ) + + _GetStdHandle = windll.kernel32.GetStdHandle + _GetStdHandle.argtypes = [ + wintypes.DWORD, + ] + _GetStdHandle.restype = wintypes.HANDLE + + _GetConsoleScreenBufferInfo = windll.kernel32.GetConsoleScreenBufferInfo + _GetConsoleScreenBufferInfo.argtypes = [ + wintypes.HANDLE, + POINTER(CONSOLE_SCREEN_BUFFER_INFO), + ] + _GetConsoleScreenBufferInfo.restype = wintypes.BOOL + + _SetConsoleTextAttribute = windll.kernel32.SetConsoleTextAttribute + _SetConsoleTextAttribute.argtypes = [ + wintypes.HANDLE, + wintypes.WORD, + ] + _SetConsoleTextAttribute.restype = wintypes.BOOL + + _SetConsoleCursorPosition = windll.kernel32.SetConsoleCursorPosition + _SetConsoleCursorPosition.argtypes = [ + wintypes.HANDLE, + COORD, + ] + _SetConsoleCursorPosition.restype = wintypes.BOOL + + _FillConsoleOutputCharacterA = windll.kernel32.FillConsoleOutputCharacterA + _FillConsoleOutputCharacterA.argtypes = [ + wintypes.HANDLE, + c_char, + wintypes.DWORD, + COORD, + POINTER(wintypes.DWORD), + ] + _FillConsoleOutputCharacterA.restype = wintypes.BOOL + + _FillConsoleOutputAttribute = windll.kernel32.FillConsoleOutputAttribute + _FillConsoleOutputAttribute.argtypes = [ + wintypes.HANDLE, + wintypes.WORD, + wintypes.DWORD, + COORD, + POINTER(wintypes.DWORD), + ] + _FillConsoleOutputAttribute.restype = wintypes.BOOL + + _SetConsoleTitleW = windll.kernel32.SetConsoleTitleW + _SetConsoleTitleW.argtypes = [ + wintypes.LPCWSTR + ] + _SetConsoleTitleW.restype = wintypes.BOOL + + def _winapi_test(handle): + csbi = CONSOLE_SCREEN_BUFFER_INFO() + success = _GetConsoleScreenBufferInfo( + handle, byref(csbi)) + return bool(success) + + def winapi_test(): + return any(_winapi_test(h) for h in + (_GetStdHandle(STDOUT), _GetStdHandle(STDERR))) + + def GetConsoleScreenBufferInfo(stream_id=STDOUT): + handle = _GetStdHandle(stream_id) + csbi = CONSOLE_SCREEN_BUFFER_INFO() + success = _GetConsoleScreenBufferInfo( + handle, byref(csbi)) + return csbi + + def SetConsoleTextAttribute(stream_id, attrs): + handle = _GetStdHandle(stream_id) + return _SetConsoleTextAttribute(handle, attrs) + + def SetConsoleCursorPosition(stream_id, position, adjust=True): + position = COORD(*position) + # If the position is out of range, do nothing. + if position.Y <= 0 or position.X <= 0: + return + # Adjust for Windows' SetConsoleCursorPosition: + # 1. being 0-based, while ANSI is 1-based. + # 2. expecting (x,y), while ANSI uses (y,x). + adjusted_position = COORD(position.Y - 1, position.X - 1) + if adjust: + # Adjust for viewport's scroll position + sr = GetConsoleScreenBufferInfo(STDOUT).srWindow + adjusted_position.Y += sr.Top + adjusted_position.X += sr.Left + # Resume normal processing + handle = _GetStdHandle(stream_id) + return _SetConsoleCursorPosition(handle, adjusted_position) + + def FillConsoleOutputCharacter(stream_id, char, length, start): + handle = _GetStdHandle(stream_id) + char = c_char(char.encode()) + length = wintypes.DWORD(length) + num_written = wintypes.DWORD(0) + # Note that this is hard-coded for ANSI (vs wide) bytes. + success = _FillConsoleOutputCharacterA( + handle, char, length, start, byref(num_written)) + return num_written.value + + def FillConsoleOutputAttribute(stream_id, attr, length, start): + ''' FillConsoleOutputAttribute( hConsole, csbi.wAttributes, dwConSize, coordScreen, &cCharsWritten )''' + handle = _GetStdHandle(stream_id) + attribute = wintypes.WORD(attr) + length = wintypes.DWORD(length) + num_written = wintypes.DWORD(0) + # Note that this is hard-coded for ANSI (vs wide) bytes. + return _FillConsoleOutputAttribute( + handle, attribute, length, start, byref(num_written)) + + def SetConsoleTitle(title): + return _SetConsoleTitleW(title) diff --git a/venv2/lib/python2.7/site-packages/pip/_vendor/colorama/winterm.py b/venv2/lib/python2.7/site-packages/pip/_vendor/colorama/winterm.py new file mode 100644 index 0000000..0fdb4ec --- /dev/null +++ b/venv2/lib/python2.7/site-packages/pip/_vendor/colorama/winterm.py @@ -0,0 +1,169 @@ +# Copyright Jonathan Hartley 2013. BSD 3-Clause license, see LICENSE file. +from . import win32 + + +# from wincon.h +class WinColor(object): + BLACK = 0 + BLUE = 1 + GREEN = 2 + CYAN = 3 + RED = 4 + MAGENTA = 5 + YELLOW = 6 + GREY = 7 + +# from wincon.h +class WinStyle(object): + NORMAL = 0x00 # dim text, dim background + BRIGHT = 0x08 # bright text, dim background + BRIGHT_BACKGROUND = 0x80 # dim text, bright background + +class WinTerm(object): + + def __init__(self): + self._default = win32.GetConsoleScreenBufferInfo(win32.STDOUT).wAttributes + self.set_attrs(self._default) + self._default_fore = self._fore + self._default_back = self._back + self._default_style = self._style + # In order to emulate LIGHT_EX in windows, we borrow the BRIGHT style. + # So that LIGHT_EX colors and BRIGHT style do not clobber each other, + # we track them separately, since LIGHT_EX is overwritten by Fore/Back + # and BRIGHT is overwritten by Style codes. + self._light = 0 + + def get_attrs(self): + return self._fore + self._back * 16 + (self._style | self._light) + + def set_attrs(self, value): + self._fore = value & 7 + self._back = (value >> 4) & 7 + self._style = value & (WinStyle.BRIGHT | WinStyle.BRIGHT_BACKGROUND) + + def reset_all(self, on_stderr=None): + self.set_attrs(self._default) + self.set_console(attrs=self._default) + self._light = 0 + + def fore(self, fore=None, light=False, on_stderr=False): + if fore is None: + fore = self._default_fore + self._fore = fore + # Emulate LIGHT_EX with BRIGHT Style + if light: + self._light |= WinStyle.BRIGHT + else: + self._light &= ~WinStyle.BRIGHT + self.set_console(on_stderr=on_stderr) + + def back(self, back=None, light=False, on_stderr=False): + if back is None: + back = self._default_back + self._back = back + # Emulate LIGHT_EX with BRIGHT_BACKGROUND Style + if light: + self._light |= WinStyle.BRIGHT_BACKGROUND + else: + self._light &= ~WinStyle.BRIGHT_BACKGROUND + self.set_console(on_stderr=on_stderr) + + def style(self, style=None, on_stderr=False): + if style is None: + style = self._default_style + self._style = style + self.set_console(on_stderr=on_stderr) + + def set_console(self, attrs=None, on_stderr=False): + if attrs is None: + attrs = self.get_attrs() + handle = win32.STDOUT + if on_stderr: + handle = win32.STDERR + win32.SetConsoleTextAttribute(handle, attrs) + + def get_position(self, handle): + position = win32.GetConsoleScreenBufferInfo(handle).dwCursorPosition + # Because Windows coordinates are 0-based, + # and win32.SetConsoleCursorPosition expects 1-based. + position.X += 1 + position.Y += 1 + return position + + def set_cursor_position(self, position=None, on_stderr=False): + if position is None: + # I'm not currently tracking the position, so there is no default. + # position = self.get_position() + return + handle = win32.STDOUT + if on_stderr: + handle = win32.STDERR + win32.SetConsoleCursorPosition(handle, position) + + def cursor_adjust(self, x, y, on_stderr=False): + handle = win32.STDOUT + if on_stderr: + handle = win32.STDERR + position = self.get_position(handle) + adjusted_position = (position.Y + y, position.X + x) + win32.SetConsoleCursorPosition(handle, adjusted_position, adjust=False) + + def erase_screen(self, mode=0, on_stderr=False): + # 0 should clear from the cursor to the end of the screen. + # 1 should clear from the cursor to the beginning of the screen. + # 2 should clear the entire screen, and move cursor to (1,1) + handle = win32.STDOUT + if on_stderr: + handle = win32.STDERR + csbi = win32.GetConsoleScreenBufferInfo(handle) + # get the number of character cells in the current buffer + cells_in_screen = csbi.dwSize.X * csbi.dwSize.Y + # get number of character cells before current cursor position + cells_before_cursor = csbi.dwSize.X * csbi.dwCursorPosition.Y + csbi.dwCursorPosition.X + if mode == 0: + from_coord = csbi.dwCursorPosition + cells_to_erase = cells_in_screen - cells_before_cursor + elif mode == 1: + from_coord = win32.COORD(0, 0) + cells_to_erase = cells_before_cursor + elif mode == 2: + from_coord = win32.COORD(0, 0) + cells_to_erase = cells_in_screen + else: + # invalid mode + return + # fill the entire screen with blanks + win32.FillConsoleOutputCharacter(handle, ' ', cells_to_erase, from_coord) + # now set the buffer's attributes accordingly + win32.FillConsoleOutputAttribute(handle, self.get_attrs(), cells_to_erase, from_coord) + if mode == 2: + # put the cursor where needed + win32.SetConsoleCursorPosition(handle, (1, 1)) + + def erase_line(self, mode=0, on_stderr=False): + # 0 should clear from the cursor to the end of the line. + # 1 should clear from the cursor to the beginning of the line. + # 2 should clear the entire line. + handle = win32.STDOUT + if on_stderr: + handle = win32.STDERR + csbi = win32.GetConsoleScreenBufferInfo(handle) + if mode == 0: + from_coord = csbi.dwCursorPosition + cells_to_erase = csbi.dwSize.X - csbi.dwCursorPosition.X + elif mode == 1: + from_coord = win32.COORD(0, csbi.dwCursorPosition.Y) + cells_to_erase = csbi.dwCursorPosition.X + elif mode == 2: + from_coord = win32.COORD(0, csbi.dwCursorPosition.Y) + cells_to_erase = csbi.dwSize.X + else: + # invalid mode + return + # fill the entire screen with blanks + win32.FillConsoleOutputCharacter(handle, ' ', cells_to_erase, from_coord) + # now set the buffer's attributes accordingly + win32.FillConsoleOutputAttribute(handle, self.get_attrs(), cells_to_erase, from_coord) + + def set_title(self, title): + win32.SetConsoleTitle(title) diff --git a/venv2/lib/python2.7/site-packages/pip/_vendor/distlib/__init__.py b/venv2/lib/python2.7/site-packages/pip/_vendor/distlib/__init__.py new file mode 100644 index 0000000..a786b4d --- /dev/null +++ b/venv2/lib/python2.7/site-packages/pip/_vendor/distlib/__init__.py @@ -0,0 +1,23 @@ +# -*- coding: utf-8 -*- +# +# Copyright (C) 2012-2017 Vinay Sajip. +# Licensed to the Python Software Foundation under a contributor agreement. +# See LICENSE.txt and CONTRIBUTORS.txt. +# +import logging + +__version__ = '0.2.8' + +class DistlibException(Exception): + pass + +try: + from logging import NullHandler +except ImportError: # pragma: no cover + class NullHandler(logging.Handler): + def handle(self, record): pass + def emit(self, record): pass + def createLock(self): self.lock = None + +logger = logging.getLogger(__name__) +logger.addHandler(NullHandler()) diff --git a/venv2/lib/python2.7/site-packages/pip/_vendor/distlib/_backport/__init__.py b/venv2/lib/python2.7/site-packages/pip/_vendor/distlib/_backport/__init__.py new file mode 100644 index 0000000..f7dbf4c --- /dev/null +++ b/venv2/lib/python2.7/site-packages/pip/_vendor/distlib/_backport/__init__.py @@ -0,0 +1,6 @@ +"""Modules copied from Python 3 standard libraries, for internal use only. + +Individual classes and functions are found in d2._backport.misc. Intended +usage is to always import things missing from 3.1 from that module: the +built-in/stdlib objects will be used if found. +""" diff --git a/venv2/lib/python2.7/site-packages/pip/_vendor/distlib/_backport/misc.py b/venv2/lib/python2.7/site-packages/pip/_vendor/distlib/_backport/misc.py new file mode 100644 index 0000000..cfb318d --- /dev/null +++ b/venv2/lib/python2.7/site-packages/pip/_vendor/distlib/_backport/misc.py @@ -0,0 +1,41 @@ +# -*- coding: utf-8 -*- +# +# Copyright (C) 2012 The Python Software Foundation. +# See LICENSE.txt and CONTRIBUTORS.txt. +# +"""Backports for individual classes and functions.""" + +import os +import sys + +__all__ = ['cache_from_source', 'callable', 'fsencode'] + + +try: + from imp import cache_from_source +except ImportError: + def cache_from_source(py_file, debug=__debug__): + ext = debug and 'c' or 'o' + return py_file + ext + + +try: + callable = callable +except NameError: + from collections import Callable + + def callable(obj): + return isinstance(obj, Callable) + + +try: + fsencode = os.fsencode +except AttributeError: + def fsencode(filename): + if isinstance(filename, bytes): + return filename + elif isinstance(filename, str): + return filename.encode(sys.getfilesystemencoding()) + else: + raise TypeError("expect bytes or str, not %s" % + type(filename).__name__) diff --git a/venv2/lib/python2.7/site-packages/pip/_vendor/distlib/_backport/shutil.py b/venv2/lib/python2.7/site-packages/pip/_vendor/distlib/_backport/shutil.py new file mode 100644 index 0000000..159e49e --- /dev/null +++ b/venv2/lib/python2.7/site-packages/pip/_vendor/distlib/_backport/shutil.py @@ -0,0 +1,761 @@ +# -*- coding: utf-8 -*- +# +# Copyright (C) 2012 The Python Software Foundation. +# See LICENSE.txt and CONTRIBUTORS.txt. +# +"""Utility functions for copying and archiving files and directory trees. + +XXX The functions here don't copy the resource fork or other metadata on Mac. + +""" + +import os +import sys +import stat +from os.path import abspath +import fnmatch +import collections +import errno +from . import tarfile + +try: + import bz2 + _BZ2_SUPPORTED = True +except ImportError: + _BZ2_SUPPORTED = False + +try: + from pwd import getpwnam +except ImportError: + getpwnam = None + +try: + from grp import getgrnam +except ImportError: + getgrnam = None + +__all__ = ["copyfileobj", "copyfile", "copymode", "copystat", "copy", "copy2", + "copytree", "move", "rmtree", "Error", "SpecialFileError", + "ExecError", "make_archive", "get_archive_formats", + "register_archive_format", "unregister_archive_format", + "get_unpack_formats", "register_unpack_format", + "unregister_unpack_format", "unpack_archive", "ignore_patterns"] + +class Error(EnvironmentError): + pass + +class SpecialFileError(EnvironmentError): + """Raised when trying to do a kind of operation (e.g. copying) which is + not supported on a special file (e.g. a named pipe)""" + +class ExecError(EnvironmentError): + """Raised when a command could not be executed""" + +class ReadError(EnvironmentError): + """Raised when an archive cannot be read""" + +class RegistryError(Exception): + """Raised when a registry operation with the archiving + and unpacking registries fails""" + + +try: + WindowsError +except NameError: + WindowsError = None + +def copyfileobj(fsrc, fdst, length=16*1024): + """copy data from file-like object fsrc to file-like object fdst""" + while 1: + buf = fsrc.read(length) + if not buf: + break + fdst.write(buf) + +def _samefile(src, dst): + # Macintosh, Unix. + if hasattr(os.path, 'samefile'): + try: + return os.path.samefile(src, dst) + except OSError: + return False + + # All other platforms: check for same pathname. + return (os.path.normcase(os.path.abspath(src)) == + os.path.normcase(os.path.abspath(dst))) + +def copyfile(src, dst): + """Copy data from src to dst""" + if _samefile(src, dst): + raise Error("`%s` and `%s` are the same file" % (src, dst)) + + for fn in [src, dst]: + try: + st = os.stat(fn) + except OSError: + # File most likely does not exist + pass + else: + # XXX What about other special files? (sockets, devices...) + if stat.S_ISFIFO(st.st_mode): + raise SpecialFileError("`%s` is a named pipe" % fn) + + with open(src, 'rb') as fsrc: + with open(dst, 'wb') as fdst: + copyfileobj(fsrc, fdst) + +def copymode(src, dst): + """Copy mode bits from src to dst""" + if hasattr(os, 'chmod'): + st = os.stat(src) + mode = stat.S_IMODE(st.st_mode) + os.chmod(dst, mode) + +def copystat(src, dst): + """Copy all stat info (mode bits, atime, mtime, flags) from src to dst""" + st = os.stat(src) + mode = stat.S_IMODE(st.st_mode) + if hasattr(os, 'utime'): + os.utime(dst, (st.st_atime, st.st_mtime)) + if hasattr(os, 'chmod'): + os.chmod(dst, mode) + if hasattr(os, 'chflags') and hasattr(st, 'st_flags'): + try: + os.chflags(dst, st.st_flags) + except OSError as why: + if (not hasattr(errno, 'EOPNOTSUPP') or + why.errno != errno.EOPNOTSUPP): + raise + +def copy(src, dst): + """Copy data and mode bits ("cp src dst"). + + The destination may be a directory. + + """ + if os.path.isdir(dst): + dst = os.path.join(dst, os.path.basename(src)) + copyfile(src, dst) + copymode(src, dst) + +def copy2(src, dst): + """Copy data and all stat info ("cp -p src dst"). + + The destination may be a directory. + + """ + if os.path.isdir(dst): + dst = os.path.join(dst, os.path.basename(src)) + copyfile(src, dst) + copystat(src, dst) + +def ignore_patterns(*patterns): + """Function that can be used as copytree() ignore parameter. + + Patterns is a sequence of glob-style patterns + that are used to exclude files""" + def _ignore_patterns(path, names): + ignored_names = [] + for pattern in patterns: + ignored_names.extend(fnmatch.filter(names, pattern)) + return set(ignored_names) + return _ignore_patterns + +def copytree(src, dst, symlinks=False, ignore=None, copy_function=copy2, + ignore_dangling_symlinks=False): + """Recursively copy a directory tree. + + The destination directory must not already exist. + If exception(s) occur, an Error is raised with a list of reasons. + + If the optional symlinks flag is true, symbolic links in the + source tree result in symbolic links in the destination tree; if + it is false, the contents of the files pointed to by symbolic + links are copied. If the file pointed by the symlink doesn't + exist, an exception will be added in the list of errors raised in + an Error exception at the end of the copy process. + + You can set the optional ignore_dangling_symlinks flag to true if you + want to silence this exception. Notice that this has no effect on + platforms that don't support os.symlink. + + The optional ignore argument is a callable. If given, it + is called with the `src` parameter, which is the directory + being visited by copytree(), and `names` which is the list of + `src` contents, as returned by os.listdir(): + + callable(src, names) -> ignored_names + + Since copytree() is called recursively, the callable will be + called once for each directory that is copied. It returns a + list of names relative to the `src` directory that should + not be copied. + + The optional copy_function argument is a callable that will be used + to copy each file. It will be called with the source path and the + destination path as arguments. By default, copy2() is used, but any + function that supports the same signature (like copy()) can be used. + + """ + names = os.listdir(src) + if ignore is not None: + ignored_names = ignore(src, names) + else: + ignored_names = set() + + os.makedirs(dst) + errors = [] + for name in names: + if name in ignored_names: + continue + srcname = os.path.join(src, name) + dstname = os.path.join(dst, name) + try: + if os.path.islink(srcname): + linkto = os.readlink(srcname) + if symlinks: + os.symlink(linkto, dstname) + else: + # ignore dangling symlink if the flag is on + if not os.path.exists(linkto) and ignore_dangling_symlinks: + continue + # otherwise let the copy occurs. copy2 will raise an error + copy_function(srcname, dstname) + elif os.path.isdir(srcname): + copytree(srcname, dstname, symlinks, ignore, copy_function) + else: + # Will raise a SpecialFileError for unsupported file types + copy_function(srcname, dstname) + # catch the Error from the recursive copytree so that we can + # continue with other files + except Error as err: + errors.extend(err.args[0]) + except EnvironmentError as why: + errors.append((srcname, dstname, str(why))) + try: + copystat(src, dst) + except OSError as why: + if WindowsError is not None and isinstance(why, WindowsError): + # Copying file access times may fail on Windows + pass + else: + errors.extend((src, dst, str(why))) + if errors: + raise Error(errors) + +def rmtree(path, ignore_errors=False, onerror=None): + """Recursively delete a directory tree. + + If ignore_errors is set, errors are ignored; otherwise, if onerror + is set, it is called to handle the error with arguments (func, + path, exc_info) where func is os.listdir, os.remove, or os.rmdir; + path is the argument to that function that caused it to fail; and + exc_info is a tuple returned by sys.exc_info(). If ignore_errors + is false and onerror is None, an exception is raised. + + """ + if ignore_errors: + def onerror(*args): + pass + elif onerror is None: + def onerror(*args): + raise + try: + if os.path.islink(path): + # symlinks to directories are forbidden, see bug #1669 + raise OSError("Cannot call rmtree on a symbolic link") + except OSError: + onerror(os.path.islink, path, sys.exc_info()) + # can't continue even if onerror hook returns + return + 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 _basename(path): + # A basename() variant which first strips the trailing slash, if present. + # Thus we always get the last component of the path, even for directories. + return os.path.basename(path.rstrip(os.path.sep)) + +def move(src, dst): + """Recursively move a file or directory to another location. This is + similar to the Unix "mv" command. + + If the destination is a directory or a symlink to a directory, the source + is moved inside the directory. The destination path must not already + exist. + + If the destination already exists but is not a directory, it may be + overwritten depending on os.rename() semantics. + + If the destination is on our current filesystem, then rename() is used. + Otherwise, src is copied to the destination and then removed. + A lot more could be done here... A look at a mv.c shows a lot of + the issues this implementation glosses over. + + """ + real_dst = dst + if os.path.isdir(dst): + if _samefile(src, dst): + # We might be on a case insensitive filesystem, + # perform the rename anyway. + os.rename(src, dst) + return + + real_dst = os.path.join(dst, _basename(src)) + if os.path.exists(real_dst): + raise Error("Destination path '%s' already exists" % real_dst) + try: + os.rename(src, real_dst) + except OSError: + if os.path.isdir(src): + if _destinsrc(src, dst): + raise Error("Cannot move a directory '%s' into itself '%s'." % (src, dst)) + copytree(src, real_dst, symlinks=True) + rmtree(src) + else: + copy2(src, real_dst) + os.unlink(src) + +def _destinsrc(src, dst): + src = abspath(src) + dst = abspath(dst) + if not src.endswith(os.path.sep): + src += os.path.sep + if not dst.endswith(os.path.sep): + dst += os.path.sep + return dst.startswith(src) + +def _get_gid(name): + """Returns a gid, given a group name.""" + if getgrnam is None or name is None: + return None + try: + result = getgrnam(name) + except KeyError: + result = None + if result is not None: + return result[2] + return None + +def _get_uid(name): + """Returns an uid, given a user name.""" + if getpwnam is None or name is None: + return None + try: + result = getpwnam(name) + except KeyError: + result = None + if result is not None: + return result[2] + return None + +def _make_tarball(base_name, base_dir, compress="gzip", verbose=0, dry_run=0, + owner=None, group=None, logger=None): + """Create a (possibly compressed) tar file from all the files under + 'base_dir'. + + 'compress' must be "gzip" (the default), "bzip2", or None. + + 'owner' and 'group' can be used to define an owner and a group for the + archive that is being built. If not provided, the current owner and group + will be used. + + The output tar file will be named 'base_name' + ".tar", possibly plus + the appropriate compression extension (".gz", or ".bz2"). + + Returns the output filename. + """ + tar_compression = {'gzip': 'gz', None: ''} + compress_ext = {'gzip': '.gz'} + + if _BZ2_SUPPORTED: + tar_compression['bzip2'] = 'bz2' + compress_ext['bzip2'] = '.bz2' + + # flags for compression program, each element of list will be an argument + if compress is not None and compress not in compress_ext: + raise ValueError("bad value for 'compress', or compression format not " + "supported : {0}".format(compress)) + + archive_name = base_name + '.tar' + compress_ext.get(compress, '') + archive_dir = os.path.dirname(archive_name) + + if not os.path.exists(archive_dir): + if logger is not None: + logger.info("creating %s", archive_dir) + if not dry_run: + os.makedirs(archive_dir) + + # creating the tarball + if logger is not None: + logger.info('Creating tar archive') + + uid = _get_uid(owner) + gid = _get_gid(group) + + def _set_uid_gid(tarinfo): + if gid is not None: + tarinfo.gid = gid + tarinfo.gname = group + if uid is not None: + tarinfo.uid = uid + tarinfo.uname = owner + return tarinfo + + if not dry_run: + tar = tarfile.open(archive_name, 'w|%s' % tar_compression[compress]) + try: + tar.add(base_dir, filter=_set_uid_gid) + finally: + tar.close() + + return archive_name + +def _call_external_zip(base_dir, zip_filename, verbose=False, dry_run=False): + # XXX see if we want to keep an external call here + if verbose: + zipoptions = "-r" + else: + zipoptions = "-rq" + from distutils.errors import DistutilsExecError + from distutils.spawn import spawn + try: + spawn(["zip", zipoptions, zip_filename, base_dir], dry_run=dry_run) + except DistutilsExecError: + # XXX really should distinguish between "couldn't find + # external 'zip' command" and "zip failed". + raise ExecError("unable to create zip file '%s': " + "could neither import the 'zipfile' module nor " + "find a standalone zip utility") % zip_filename + +def _make_zipfile(base_name, base_dir, verbose=0, dry_run=0, logger=None): + """Create a zip file from all the files under 'base_dir'. + + The output zip file will be named 'base_name' + ".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 ExecError. Returns the name of the output zip + file. + """ + zip_filename = base_name + ".zip" + archive_dir = os.path.dirname(base_name) + + if not os.path.exists(archive_dir): + if logger is not None: + logger.info("creating %s", archive_dir) + if not dry_run: + os.makedirs(archive_dir) + + # If zipfile module is not available, try spawning an external 'zip' + # command. + try: + import zipfile + except ImportError: + zipfile = None + + if zipfile is None: + _call_external_zip(base_dir, zip_filename, verbose, dry_run) + else: + if logger is not None: + logger.info("creating '%s' and adding '%s' to it", + zip_filename, base_dir) + + if not dry_run: + zip = zipfile.ZipFile(zip_filename, "w", + compression=zipfile.ZIP_DEFLATED) + + for dirpath, dirnames, filenames in os.walk(base_dir): + for name in filenames: + path = os.path.normpath(os.path.join(dirpath, name)) + if os.path.isfile(path): + zip.write(path, path) + if logger is not None: + logger.info("adding '%s'", path) + zip.close() + + return zip_filename + +_ARCHIVE_FORMATS = { + 'gztar': (_make_tarball, [('compress', 'gzip')], "gzip'ed tar-file"), + 'bztar': (_make_tarball, [('compress', 'bzip2')], "bzip2'ed tar-file"), + 'tar': (_make_tarball, [('compress', None)], "uncompressed tar file"), + 'zip': (_make_zipfile, [], "ZIP file"), + } + +if _BZ2_SUPPORTED: + _ARCHIVE_FORMATS['bztar'] = (_make_tarball, [('compress', 'bzip2')], + "bzip2'ed tar-file") + +def get_archive_formats(): + """Returns a list of supported formats for archiving and unarchiving. + + Each element of the returned sequence is a tuple (name, description) + """ + formats = [(name, registry[2]) for name, registry in + _ARCHIVE_FORMATS.items()] + formats.sort() + return formats + +def register_archive_format(name, function, extra_args=None, description=''): + """Registers an archive format. + + name is the name of the format. function is the callable that will be + used to create archives. If provided, extra_args is a sequence of + (name, value) tuples that will be passed as arguments to the callable. + description can be provided to describe the format, and will be returned + by the get_archive_formats() function. + """ + if extra_args is None: + extra_args = [] + if not isinstance(function, collections.Callable): + raise TypeError('The %s object is not callable' % function) + if not isinstance(extra_args, (tuple, list)): + raise TypeError('extra_args needs to be a sequence') + for element in extra_args: + if not isinstance(element, (tuple, list)) or len(element) !=2: + raise TypeError('extra_args elements are : (arg_name, value)') + + _ARCHIVE_FORMATS[name] = (function, extra_args, description) + +def unregister_archive_format(name): + del _ARCHIVE_FORMATS[name] + +def make_archive(base_name, format, root_dir=None, base_dir=None, verbose=0, + dry_run=0, owner=None, group=None, logger=None): + """Create an archive file (eg. zip or tar). + + 'base_name' is the name of the file to create, minus any format-specific + extension; 'format' is the archive format: one of "zip", "tar", "bztar" + or "gztar". + + 'root_dir' is a directory that will be the root directory of the + archive; ie. we typically chdir into 'root_dir' before creating the + archive. 'base_dir' is the directory where we start archiving from; + ie. 'base_dir' will be the common prefix of all files and + directories in the archive. 'root_dir' and 'base_dir' both default + to the current directory. Returns the name of the archive file. + + 'owner' and 'group' are used when creating a tar archive. By default, + uses the current owner and group. + """ + save_cwd = os.getcwd() + if root_dir is not None: + if logger is not None: + logger.debug("changing into '%s'", root_dir) + base_name = os.path.abspath(base_name) + if not dry_run: + os.chdir(root_dir) + + if base_dir is None: + base_dir = os.curdir + + kwargs = {'dry_run': dry_run, 'logger': logger} + + try: + format_info = _ARCHIVE_FORMATS[format] + except KeyError: + raise ValueError("unknown archive format '%s'" % format) + + func = format_info[0] + for arg, val in format_info[1]: + kwargs[arg] = val + + if format != 'zip': + kwargs['owner'] = owner + kwargs['group'] = group + + try: + filename = func(base_name, base_dir, **kwargs) + finally: + if root_dir is not None: + if logger is not None: + logger.debug("changing back to '%s'", save_cwd) + os.chdir(save_cwd) + + return filename + + +def get_unpack_formats(): + """Returns a list of supported formats for unpacking. + + Each element of the returned sequence is a tuple + (name, extensions, description) + """ + formats = [(name, info[0], info[3]) for name, info in + _UNPACK_FORMATS.items()] + formats.sort() + return formats + +def _check_unpack_options(extensions, function, extra_args): + """Checks what gets registered as an unpacker.""" + # first make sure no other unpacker is registered for this extension + existing_extensions = {} + for name, info in _UNPACK_FORMATS.items(): + for ext in info[0]: + existing_extensions[ext] = name + + for extension in extensions: + if extension in existing_extensions: + msg = '%s is already registered for "%s"' + raise RegistryError(msg % (extension, + existing_extensions[extension])) + + if not isinstance(function, collections.Callable): + raise TypeError('The registered function must be a callable') + + +def register_unpack_format(name, extensions, function, extra_args=None, + description=''): + """Registers an unpack format. + + `name` is the name of the format. `extensions` is a list of extensions + corresponding to the format. + + `function` is the callable that will be + used to unpack archives. The callable will receive archives to unpack. + If it's unable to handle an archive, it needs to raise a ReadError + exception. + + If provided, `extra_args` is a sequence of + (name, value) tuples that will be passed as arguments to the callable. + description can be provided to describe the format, and will be returned + by the get_unpack_formats() function. + """ + if extra_args is None: + extra_args = [] + _check_unpack_options(extensions, function, extra_args) + _UNPACK_FORMATS[name] = extensions, function, extra_args, description + +def unregister_unpack_format(name): + """Removes the pack format from the registry.""" + del _UNPACK_FORMATS[name] + +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 _unpack_zipfile(filename, extract_dir): + """Unpack zip `filename` to `extract_dir` + """ + try: + import zipfile + except ImportError: + raise ReadError('zlib not supported, cannot unpack this archive.') + + if not zipfile.is_zipfile(filename): + raise ReadError("%s is not a zip file" % filename) + + zip = zipfile.ZipFile(filename) + try: + for info in zip.infolist(): + name = info.filename + + # don't extract absolute paths or ones with .. in them + if name.startswith('/') or '..' in name: + continue + + target = os.path.join(extract_dir, *name.split('/')) + if not target: + continue + + _ensure_directory(target) + if not name.endswith('/'): + # file + data = zip.read(info.filename) + f = open(target, 'wb') + try: + f.write(data) + finally: + f.close() + del data + finally: + zip.close() + +def _unpack_tarfile(filename, extract_dir): + """Unpack tar/tar.gz/tar.bz2 `filename` to `extract_dir` + """ + try: + tarobj = tarfile.open(filename) + except tarfile.TarError: + raise ReadError( + "%s is not a compressed or uncompressed tar file" % filename) + try: + tarobj.extractall(extract_dir) + finally: + tarobj.close() + +_UNPACK_FORMATS = { + 'gztar': (['.tar.gz', '.tgz'], _unpack_tarfile, [], "gzip'ed tar-file"), + 'tar': (['.tar'], _unpack_tarfile, [], "uncompressed tar file"), + 'zip': (['.zip'], _unpack_zipfile, [], "ZIP file") + } + +if _BZ2_SUPPORTED: + _UNPACK_FORMATS['bztar'] = (['.bz2'], _unpack_tarfile, [], + "bzip2'ed tar-file") + +def _find_unpack_format(filename): + for name, info in _UNPACK_FORMATS.items(): + for extension in info[0]: + if filename.endswith(extension): + return name + return None + +def unpack_archive(filename, extract_dir=None, format=None): + """Unpack an archive. + + `filename` is the name of the archive. + + `extract_dir` is the name of the target directory, where the archive + is unpacked. If not provided, the current working directory is used. + + `format` is the archive format: one of "zip", "tar", or "gztar". Or any + other registered format. If not provided, unpack_archive will use the + filename extension and see if an unpacker was registered for that + extension. + + In case none is found, a ValueError is raised. + """ + if extract_dir is None: + extract_dir = os.getcwd() + + if format is not None: + try: + format_info = _UNPACK_FORMATS[format] + except KeyError: + raise ValueError("Unknown unpack format '{0}'".format(format)) + + func = format_info[1] + func(filename, extract_dir, **dict(format_info[2])) + else: + # we need to look at the registered unpackers supported extensions + format = _find_unpack_format(filename) + if format is None: + raise ReadError("Unknown archive format '{0}'".format(filename)) + + func = _UNPACK_FORMATS[format][1] + kwargs = dict(_UNPACK_FORMATS[format][2]) + func(filename, extract_dir, **kwargs) diff --git a/venv2/lib/python2.7/site-packages/pip/_vendor/distlib/_backport/sysconfig.cfg b/venv2/lib/python2.7/site-packages/pip/_vendor/distlib/_backport/sysconfig.cfg new file mode 100644 index 0000000..1746bd0 --- /dev/null +++ b/venv2/lib/python2.7/site-packages/pip/_vendor/distlib/_backport/sysconfig.cfg @@ -0,0 +1,84 @@ +[posix_prefix] +# Configuration directories. Some of these come straight out of the +# configure script. They are for implementing the other variables, not to +# be used directly in [resource_locations]. +confdir = /etc +datadir = /usr/share +libdir = /usr/lib +statedir = /var +# User resource directory +local = ~/.local/{distribution.name} + +stdlib = {base}/lib/python{py_version_short} +platstdlib = {platbase}/lib/python{py_version_short} +purelib = {base}/lib/python{py_version_short}/site-packages +platlib = {platbase}/lib/python{py_version_short}/site-packages +include = {base}/include/python{py_version_short}{abiflags} +platinclude = {platbase}/include/python{py_version_short}{abiflags} +data = {base} + +[posix_home] +stdlib = {base}/lib/python +platstdlib = {base}/lib/python +purelib = {base}/lib/python +platlib = {base}/lib/python +include = {base}/include/python +platinclude = {base}/include/python +scripts = {base}/bin +data = {base} + +[nt] +stdlib = {base}/Lib +platstdlib = {base}/Lib +purelib = {base}/Lib/site-packages +platlib = {base}/Lib/site-packages +include = {base}/Include +platinclude = {base}/Include +scripts = {base}/Scripts +data = {base} + +[os2] +stdlib = {base}/Lib +platstdlib = {base}/Lib +purelib = {base}/Lib/site-packages +platlib = {base}/Lib/site-packages +include = {base}/Include +platinclude = {base}/Include +scripts = {base}/Scripts +data = {base} + +[os2_home] +stdlib = {userbase}/lib/python{py_version_short} +platstdlib = {userbase}/lib/python{py_version_short} +purelib = {userbase}/lib/python{py_version_short}/site-packages +platlib = {userbase}/lib/python{py_version_short}/site-packages +include = {userbase}/include/python{py_version_short} +scripts = {userbase}/bin +data = {userbase} + +[nt_user] +stdlib = {userbase}/Python{py_version_nodot} +platstdlib = {userbase}/Python{py_version_nodot} +purelib = {userbase}/Python{py_version_nodot}/site-packages +platlib = {userbase}/Python{py_version_nodot}/site-packages +include = {userbase}/Python{py_version_nodot}/Include +scripts = {userbase}/Scripts +data = {userbase} + +[posix_user] +stdlib = {userbase}/lib/python{py_version_short} +platstdlib = {userbase}/lib/python{py_version_short} +purelib = {userbase}/lib/python{py_version_short}/site-packages +platlib = {userbase}/lib/python{py_version_short}/site-packages +include = {userbase}/include/python{py_version_short} +scripts = {userbase}/bin +data = {userbase} + +[osx_framework_user] +stdlib = {userbase}/lib/python +platstdlib = {userbase}/lib/python +purelib = {userbase}/lib/python/site-packages +platlib = {userbase}/lib/python/site-packages +include = {userbase}/include +scripts = {userbase}/bin +data = {userbase} diff --git a/venv2/lib/python2.7/site-packages/pip/_vendor/distlib/_backport/sysconfig.py b/venv2/lib/python2.7/site-packages/pip/_vendor/distlib/_backport/sysconfig.py new file mode 100644 index 0000000..1df3aba --- /dev/null +++ b/venv2/lib/python2.7/site-packages/pip/_vendor/distlib/_backport/sysconfig.py @@ -0,0 +1,788 @@ +# -*- coding: utf-8 -*- +# +# Copyright (C) 2012 The Python Software Foundation. +# See LICENSE.txt and CONTRIBUTORS.txt. +# +"""Access to Python's configuration information.""" + +import codecs +import os +import re +import sys +from os.path import pardir, realpath +try: + import configparser +except ImportError: + import ConfigParser as configparser + + +__all__ = [ + 'get_config_h_filename', + 'get_config_var', + 'get_config_vars', + 'get_makefile_filename', + 'get_path', + 'get_path_names', + 'get_paths', + 'get_platform', + 'get_python_version', + 'get_scheme_names', + 'parse_config_h', +] + + +def _safe_realpath(path): + try: + return realpath(path) + except OSError: + return path + + +if sys.executable: + _PROJECT_BASE = os.path.dirname(_safe_realpath(sys.executable)) +else: + # sys.executable can be empty if argv[0] has been changed and Python is + # unable to retrieve the real program name + _PROJECT_BASE = _safe_realpath(os.getcwd()) + +if os.name == "nt" and "pcbuild" in _PROJECT_BASE[-8:].lower(): + _PROJECT_BASE = _safe_realpath(os.path.join(_PROJECT_BASE, pardir)) +# PC/VS7.1 +if os.name == "nt" and "\\pc\\v" in _PROJECT_BASE[-10:].lower(): + _PROJECT_BASE = _safe_realpath(os.path.join(_PROJECT_BASE, pardir, pardir)) +# PC/AMD64 +if os.name == "nt" and "\\pcbuild\\amd64" in _PROJECT_BASE[-14:].lower(): + _PROJECT_BASE = _safe_realpath(os.path.join(_PROJECT_BASE, pardir, pardir)) + + +def is_python_build(): + for fn in ("Setup.dist", "Setup.local"): + if os.path.isfile(os.path.join(_PROJECT_BASE, "Modules", fn)): + return True + return False + +_PYTHON_BUILD = is_python_build() + +_cfg_read = False + +def _ensure_cfg_read(): + global _cfg_read + if not _cfg_read: + from ..resources import finder + backport_package = __name__.rsplit('.', 1)[0] + _finder = finder(backport_package) + _cfgfile = _finder.find('sysconfig.cfg') + assert _cfgfile, 'sysconfig.cfg exists' + with _cfgfile.as_stream() as s: + _SCHEMES.readfp(s) + if _PYTHON_BUILD: + for scheme in ('posix_prefix', 'posix_home'): + _SCHEMES.set(scheme, 'include', '{srcdir}/Include') + _SCHEMES.set(scheme, 'platinclude', '{projectbase}/.') + + _cfg_read = True + + +_SCHEMES = configparser.RawConfigParser() +_VAR_REPL = re.compile(r'\{([^{]*?)\}') + +def _expand_globals(config): + _ensure_cfg_read() + if config.has_section('globals'): + globals = config.items('globals') + else: + globals = tuple() + + sections = config.sections() + for section in sections: + if section == 'globals': + continue + for option, value in globals: + if config.has_option(section, option): + continue + config.set(section, option, value) + config.remove_section('globals') + + # now expanding local variables defined in the cfg file + # + for section in config.sections(): + variables = dict(config.items(section)) + + def _replacer(matchobj): + name = matchobj.group(1) + if name in variables: + return variables[name] + return matchobj.group(0) + + for option, value in config.items(section): + config.set(section, option, _VAR_REPL.sub(_replacer, value)) + +#_expand_globals(_SCHEMES) + + # FIXME don't rely on sys.version here, its format is an implementation detail + # of CPython, use sys.version_info or sys.hexversion +_PY_VERSION = sys.version.split()[0] +_PY_VERSION_SHORT = sys.version[:3] +_PY_VERSION_SHORT_NO_DOT = _PY_VERSION[0] + _PY_VERSION[2] +_PREFIX = os.path.normpath(sys.prefix) +_EXEC_PREFIX = os.path.normpath(sys.exec_prefix) +_CONFIG_VARS = None +_USER_BASE = None + + +def _subst_vars(path, local_vars): + """In the string `path`, replace tokens like {some.thing} with the + corresponding value from the map `local_vars`. + + If there is no corresponding value, leave the token unchanged. + """ + def _replacer(matchobj): + name = matchobj.group(1) + if name in local_vars: + return local_vars[name] + elif name in os.environ: + return os.environ[name] + return matchobj.group(0) + return _VAR_REPL.sub(_replacer, path) + + +def _extend_dict(target_dict, other_dict): + target_keys = target_dict.keys() + for key, value in other_dict.items(): + if key in target_keys: + continue + target_dict[key] = value + + +def _expand_vars(scheme, vars): + res = {} + if vars is None: + vars = {} + _extend_dict(vars, get_config_vars()) + + for key, value in _SCHEMES.items(scheme): + if os.name in ('posix', 'nt'): + value = os.path.expanduser(value) + res[key] = os.path.normpath(_subst_vars(value, vars)) + return res + + +def format_value(value, vars): + def _replacer(matchobj): + name = matchobj.group(1) + if name in vars: + return vars[name] + return matchobj.group(0) + return _VAR_REPL.sub(_replacer, value) + + +def _get_default_scheme(): + if os.name == 'posix': + # the default scheme for posix is posix_prefix + return 'posix_prefix' + return os.name + + +def _getuserbase(): + env_base = os.environ.get("PYTHONUSERBASE", None) + + def joinuser(*args): + return os.path.expanduser(os.path.join(*args)) + + # what about 'os2emx', 'riscos' ? + if os.name == "nt": + base = os.environ.get("APPDATA") or "~" + if env_base: + return env_base + else: + return joinuser(base, "Python") + + if sys.platform == "darwin": + framework = get_config_var("PYTHONFRAMEWORK") + if framework: + if env_base: + return env_base + else: + return joinuser("~", "Library", framework, "%d.%d" % + sys.version_info[:2]) + + if env_base: + return env_base + else: + return joinuser("~", ".local") + + +def _parse_makefile(filename, vars=None): + """Parse a Makefile-style file. + + A dictionary containing name/value pairs is returned. If an + optional dictionary is passed in as the second argument, it is + used instead of a new dictionary. + """ + # Regexes needed for parsing Makefile (and similar syntaxes, + # like old-style Setup files). + _variable_rx = re.compile(r"([a-zA-Z][a-zA-Z0-9_]+)\s*=\s*(.*)") + _findvar1_rx = re.compile(r"\$\(([A-Za-z][A-Za-z0-9_]*)\)") + _findvar2_rx = re.compile(r"\${([A-Za-z][A-Za-z0-9_]*)}") + + if vars is None: + vars = {} + done = {} + notdone = {} + + with codecs.open(filename, encoding='utf-8', errors="surrogateescape") as f: + lines = f.readlines() + + for line in lines: + if line.startswith('#') or line.strip() == '': + continue + m = _variable_rx.match(line) + if m: + n, v = m.group(1, 2) + v = v.strip() + # `$$' is a literal `$' in make + tmpv = v.replace('$$', '') + + if "$" in tmpv: + notdone[n] = v + else: + try: + v = int(v) + except ValueError: + # insert literal `$' + done[n] = v.replace('$$', '$') + else: + done[n] = v + + # do variable interpolation here + variables = list(notdone.keys()) + + # Variables with a 'PY_' prefix in the makefile. These need to + # be made available without that prefix through sysconfig. + # Special care is needed to ensure that variable expansion works, even + # if the expansion uses the name without a prefix. + renamed_variables = ('CFLAGS', 'LDFLAGS', 'CPPFLAGS') + + while len(variables) > 0: + for name in tuple(variables): + value = notdone[name] + m = _findvar1_rx.search(value) or _findvar2_rx.search(value) + if m is not None: + n = m.group(1) + found = True + if n in done: + item = str(done[n]) + elif n in notdone: + # get it on a subsequent round + found = False + elif n in os.environ: + # do it like make: fall back to environment + item = os.environ[n] + + elif n in renamed_variables: + if (name.startswith('PY_') and + name[3:] in renamed_variables): + item = "" + + elif 'PY_' + n in notdone: + found = False + + else: + item = str(done['PY_' + n]) + + else: + done[n] = item = "" + + if found: + after = value[m.end():] + value = value[:m.start()] + item + after + if "$" in after: + notdone[name] = value + else: + try: + value = int(value) + except ValueError: + done[name] = value.strip() + else: + done[name] = value + variables.remove(name) + + if (name.startswith('PY_') and + name[3:] in renamed_variables): + + name = name[3:] + if name not in done: + done[name] = value + + else: + # bogus variable reference (e.g. "prefix=$/opt/python"); + # just drop it since we can't deal + done[name] = value + variables.remove(name) + + # strip spurious spaces + for k, v in done.items(): + if isinstance(v, str): + done[k] = v.strip() + + # save the results in the global dictionary + vars.update(done) + return vars + + +def get_makefile_filename(): + """Return the path of the Makefile.""" + if _PYTHON_BUILD: + return os.path.join(_PROJECT_BASE, "Makefile") + if hasattr(sys, 'abiflags'): + config_dir_name = 'config-%s%s' % (_PY_VERSION_SHORT, sys.abiflags) + else: + config_dir_name = 'config' + return os.path.join(get_path('stdlib'), config_dir_name, 'Makefile') + + +def _init_posix(vars): + """Initialize the module as appropriate for POSIX systems.""" + # load the installed Makefile: + makefile = get_makefile_filename() + try: + _parse_makefile(makefile, vars) + except IOError as e: + msg = "invalid Python installation: unable to open %s" % makefile + if hasattr(e, "strerror"): + msg = msg + " (%s)" % e.strerror + raise IOError(msg) + # load the installed pyconfig.h: + config_h = get_config_h_filename() + try: + with open(config_h) as f: + parse_config_h(f, vars) + except IOError as e: + msg = "invalid Python installation: unable to open %s" % config_h + if hasattr(e, "strerror"): + msg = msg + " (%s)" % e.strerror + raise IOError(msg) + # On AIX, there are wrong paths to the linker scripts in the Makefile + # -- these paths are relative to the Python source, but when installed + # the scripts are in another directory. + if _PYTHON_BUILD: + vars['LDSHARED'] = vars['BLDSHARED'] + + +def _init_non_posix(vars): + """Initialize the module as appropriate for NT""" + # set basic install directories + vars['LIBDEST'] = get_path('stdlib') + vars['BINLIBDEST'] = get_path('platstdlib') + vars['INCLUDEPY'] = get_path('include') + vars['SO'] = '.pyd' + vars['EXE'] = '.exe' + vars['VERSION'] = _PY_VERSION_SHORT_NO_DOT + vars['BINDIR'] = os.path.dirname(_safe_realpath(sys.executable)) + +# +# public APIs +# + + +def parse_config_h(fp, vars=None): + """Parse a config.h-style file. + + A dictionary containing name/value pairs is returned. If an + optional dictionary is passed in as the second argument, it is + used instead of a new dictionary. + """ + if vars is None: + vars = {} + define_rx = re.compile("#define ([A-Z][A-Za-z0-9_]+) (.*)\n") + undef_rx = re.compile("/[*] #undef ([A-Z][A-Za-z0-9_]+) [*]/\n") + + while True: + line = fp.readline() + if not line: + break + m = define_rx.match(line) + if m: + n, v = m.group(1, 2) + try: + v = int(v) + except ValueError: + pass + vars[n] = v + else: + m = undef_rx.match(line) + if m: + vars[m.group(1)] = 0 + return vars + + +def get_config_h_filename(): + """Return the path of pyconfig.h.""" + if _PYTHON_BUILD: + if os.name == "nt": + inc_dir = os.path.join(_PROJECT_BASE, "PC") + else: + inc_dir = _PROJECT_BASE + else: + inc_dir = get_path('platinclude') + return os.path.join(inc_dir, 'pyconfig.h') + + +def get_scheme_names(): + """Return a tuple containing the schemes names.""" + return tuple(sorted(_SCHEMES.sections())) + + +def get_path_names(): + """Return a tuple containing the paths names.""" + # xxx see if we want a static list + return _SCHEMES.options('posix_prefix') + + +def get_paths(scheme=_get_default_scheme(), vars=None, expand=True): + """Return a mapping containing an install scheme. + + ``scheme`` is the install scheme name. If not provided, it will + return the default scheme for the current platform. + """ + _ensure_cfg_read() + if expand: + return _expand_vars(scheme, vars) + else: + return dict(_SCHEMES.items(scheme)) + + +def get_path(name, scheme=_get_default_scheme(), vars=None, expand=True): + """Return a path corresponding to the scheme. + + ``scheme`` is the install scheme name. + """ + return get_paths(scheme, vars, expand)[name] + + +def get_config_vars(*args): + """With no arguments, return a dictionary of all configuration + variables relevant for the current platform. + + On Unix, this means every variable defined in Python's installed Makefile; + On Windows and Mac OS it's a much smaller set. + + With arguments, return a list of values that result from looking up + each argument in the configuration variable dictionary. + """ + global _CONFIG_VARS + if _CONFIG_VARS is None: + _CONFIG_VARS = {} + # Normalized versions of prefix and exec_prefix are handy to have; + # in fact, these are the standard versions used most places in the + # distutils2 module. + _CONFIG_VARS['prefix'] = _PREFIX + _CONFIG_VARS['exec_prefix'] = _EXEC_PREFIX + _CONFIG_VARS['py_version'] = _PY_VERSION + _CONFIG_VARS['py_version_short'] = _PY_VERSION_SHORT + _CONFIG_VARS['py_version_nodot'] = _PY_VERSION[0] + _PY_VERSION[2] + _CONFIG_VARS['base'] = _PREFIX + _CONFIG_VARS['platbase'] = _EXEC_PREFIX + _CONFIG_VARS['projectbase'] = _PROJECT_BASE + try: + _CONFIG_VARS['abiflags'] = sys.abiflags + except AttributeError: + # sys.abiflags may not be defined on all platforms. + _CONFIG_VARS['abiflags'] = '' + + if os.name in ('nt', 'os2'): + _init_non_posix(_CONFIG_VARS) + if os.name == 'posix': + _init_posix(_CONFIG_VARS) + # Setting 'userbase' is done below the call to the + # init function to enable using 'get_config_var' in + # the init-function. + if sys.version >= '2.6': + _CONFIG_VARS['userbase'] = _getuserbase() + + if 'srcdir' not in _CONFIG_VARS: + _CONFIG_VARS['srcdir'] = _PROJECT_BASE + else: + _CONFIG_VARS['srcdir'] = _safe_realpath(_CONFIG_VARS['srcdir']) + + # Convert srcdir into an absolute path if it appears necessary. + # Normally it is relative to the build directory. However, during + # testing, for example, we might be running a non-installed python + # from a different directory. + if _PYTHON_BUILD and os.name == "posix": + base = _PROJECT_BASE + try: + cwd = os.getcwd() + except OSError: + cwd = None + if (not os.path.isabs(_CONFIG_VARS['srcdir']) and + base != cwd): + # srcdir is relative and we are not in the same directory + # as the executable. Assume executable is in the build + # directory and make srcdir absolute. + srcdir = os.path.join(base, _CONFIG_VARS['srcdir']) + _CONFIG_VARS['srcdir'] = os.path.normpath(srcdir) + + if sys.platform == 'darwin': + kernel_version = os.uname()[2] # Kernel version (8.4.3) + major_version = int(kernel_version.split('.')[0]) + + if major_version < 8: + # On Mac OS X before 10.4, check if -arch and -isysroot + # are in CFLAGS or LDFLAGS and remove them if they are. + # This is needed when building extensions on a 10.3 system + # using a universal build of python. + for key in ('LDFLAGS', 'BASECFLAGS', + # a number of derived variables. These need to be + # patched up as well. + 'CFLAGS', 'PY_CFLAGS', 'BLDSHARED'): + flags = _CONFIG_VARS[key] + flags = re.sub(r'-arch\s+\w+\s', ' ', flags) + flags = re.sub('-isysroot [^ \t]*', ' ', flags) + _CONFIG_VARS[key] = flags + else: + # Allow the user to override the architecture flags using + # an environment variable. + # NOTE: This name was introduced by Apple in OSX 10.5 and + # is used by several scripting languages distributed with + # that OS release. + if 'ARCHFLAGS' in os.environ: + arch = os.environ['ARCHFLAGS'] + for key in ('LDFLAGS', 'BASECFLAGS', + # a number of derived variables. These need to be + # patched up as well. + 'CFLAGS', 'PY_CFLAGS', 'BLDSHARED'): + + flags = _CONFIG_VARS[key] + flags = re.sub(r'-arch\s+\w+\s', ' ', flags) + flags = flags + ' ' + arch + _CONFIG_VARS[key] = flags + + # If we're on OSX 10.5 or later and the user tries to + # compiles an extension using an SDK that is not present + # on the current machine it is better to not use an SDK + # than to fail. + # + # The major usecase for this is users using a Python.org + # binary installer on OSX 10.6: that installer uses + # the 10.4u SDK, but that SDK is not installed by default + # when you install Xcode. + # + CFLAGS = _CONFIG_VARS.get('CFLAGS', '') + m = re.search(r'-isysroot\s+(\S+)', CFLAGS) + if m is not None: + sdk = m.group(1) + if not os.path.exists(sdk): + for key in ('LDFLAGS', 'BASECFLAGS', + # a number of derived variables. These need to be + # patched up as well. + 'CFLAGS', 'PY_CFLAGS', 'BLDSHARED'): + + flags = _CONFIG_VARS[key] + flags = re.sub(r'-isysroot\s+\S+(\s|$)', ' ', flags) + _CONFIG_VARS[key] = flags + + if args: + vals = [] + for name in args: + vals.append(_CONFIG_VARS.get(name)) + return vals + else: + return _CONFIG_VARS + + +def get_config_var(name): + """Return the value of a single variable using the dictionary returned by + 'get_config_vars()'. + + Equivalent to get_config_vars().get(name) + """ + return get_config_vars().get(name) + + +def get_platform(): + """Return a string that identifies the current platform. + + This is used mainly to distinguish platform-specific build directories and + platform-specific built distributions. Typically includes the OS name + and version and the architecture (as supplied by 'os.uname()'), + although the exact information included depends on the OS; eg. for IRIX + the architecture isn't particularly important (IRIX only runs on SGI + hardware), but for Linux the kernel version isn't particularly + important. + + Examples of returned values: + linux-i586 + linux-alpha (?) + solaris-2.6-sun4u + irix-5.3 + irix64-6.2 + + Windows will return one of: + win-amd64 (64bit Windows on AMD64 (aka x86_64, Intel64, EM64T, etc) + win-ia64 (64bit Windows on Itanium) + win32 (all others - specifically, sys.platform is returned) + + For other non-POSIX platforms, currently just returns 'sys.platform'. + """ + if os.name == 'nt': + # sniff sys.version for architecture. + prefix = " bit (" + i = sys.version.find(prefix) + if i == -1: + return sys.platform + j = sys.version.find(")", i) + look = sys.version[i+len(prefix):j].lower() + if look == 'amd64': + return 'win-amd64' + if look == 'itanium': + return 'win-ia64' + return sys.platform + + if os.name != "posix" or not hasattr(os, 'uname'): + # XXX what about the architecture? NT is Intel or Alpha, + # Mac OS is M68k or PPC, etc. + return sys.platform + + # Try to distinguish various flavours of Unix + osname, host, release, version, machine = os.uname() + + # Convert the OS name to lowercase, remove '/' characters + # (to accommodate BSD/OS), and translate spaces (for "Power Macintosh") + osname = osname.lower().replace('/', '') + machine = machine.replace(' ', '_') + machine = machine.replace('/', '-') + + if osname[:5] == "linux": + # At least on Linux/Intel, 'machine' is the processor -- + # i386, etc. + # XXX what about Alpha, SPARC, etc? + return "%s-%s" % (osname, machine) + elif osname[:5] == "sunos": + if release[0] >= "5": # SunOS 5 == Solaris 2 + osname = "solaris" + release = "%d.%s" % (int(release[0]) - 3, release[2:]) + # fall through to standard osname-release-machine representation + elif osname[:4] == "irix": # could be "irix64"! + return "%s-%s" % (osname, release) + elif osname[:3] == "aix": + return "%s-%s.%s" % (osname, version, release) + elif osname[:6] == "cygwin": + osname = "cygwin" + rel_re = re.compile(r'[\d.]+') + m = rel_re.match(release) + if m: + release = m.group() + elif osname[:6] == "darwin": + # + # For our purposes, we'll assume that the system version from + # distutils' perspective is what MACOSX_DEPLOYMENT_TARGET is set + # to. This makes the compatibility story a bit more sane because the + # machine is going to compile and link as if it were + # MACOSX_DEPLOYMENT_TARGET. + cfgvars = get_config_vars() + macver = cfgvars.get('MACOSX_DEPLOYMENT_TARGET') + + if True: + # Always calculate the release of the running machine, + # needed to determine if we can build fat binaries or not. + + macrelease = macver + # Get the system version. Reading this plist is a documented + # way to get the system version (see the documentation for + # the Gestalt Manager) + try: + f = open('/System/Library/CoreServices/SystemVersion.plist') + except IOError: + # We're on a plain darwin box, fall back to the default + # behaviour. + pass + else: + try: + m = re.search(r'<key>ProductUserVisibleVersion</key>\s*' + r'<string>(.*?)</string>', f.read()) + finally: + f.close() + if m is not None: + macrelease = '.'.join(m.group(1).split('.')[:2]) + # else: fall back to the default behaviour + + if not macver: + macver = macrelease + + if macver: + release = macver + osname = "macosx" + + if ((macrelease + '.') >= '10.4.' and + '-arch' in get_config_vars().get('CFLAGS', '').strip()): + # The universal build will build fat binaries, but not on + # systems before 10.4 + # + # Try to detect 4-way universal builds, those have machine-type + # 'universal' instead of 'fat'. + + machine = 'fat' + cflags = get_config_vars().get('CFLAGS') + + archs = re.findall(r'-arch\s+(\S+)', cflags) + archs = tuple(sorted(set(archs))) + + if len(archs) == 1: + machine = archs[0] + elif archs == ('i386', 'ppc'): + machine = 'fat' + elif archs == ('i386', 'x86_64'): + machine = 'intel' + elif archs == ('i386', 'ppc', 'x86_64'): + machine = 'fat3' + elif archs == ('ppc64', 'x86_64'): + machine = 'fat64' + elif archs == ('i386', 'ppc', 'ppc64', 'x86_64'): + machine = 'universal' + else: + raise ValueError( + "Don't know machine value for archs=%r" % (archs,)) + + elif machine == 'i386': + # On OSX the machine type returned by uname is always the + # 32-bit variant, even if the executable architecture is + # the 64-bit variant + if sys.maxsize >= 2**32: + machine = 'x86_64' + + elif machine in ('PowerPC', 'Power_Macintosh'): + # Pick a sane name for the PPC architecture. + # See 'i386' case + if sys.maxsize >= 2**32: + machine = 'ppc64' + else: + machine = 'ppc' + + return "%s-%s-%s" % (osname, release, machine) + + +def get_python_version(): + return _PY_VERSION_SHORT + + +def _print_dict(title, data): + for index, (key, value) in enumerate(sorted(data.items())): + if index == 0: + print('%s: ' % (title)) + print('\t%s = "%s"' % (key, value)) + + +def _main(): + """Display all information sysconfig detains.""" + print('Platform: "%s"' % get_platform()) + print('Python version: "%s"' % get_python_version()) + print('Current installation scheme: "%s"' % _get_default_scheme()) + print() + _print_dict('Paths', get_paths()) + print() + _print_dict('Variables', get_config_vars()) + + +if __name__ == '__main__': + _main() diff --git a/venv2/lib/python2.7/site-packages/pip/_vendor/distlib/_backport/tarfile.py b/venv2/lib/python2.7/site-packages/pip/_vendor/distlib/_backport/tarfile.py new file mode 100644 index 0000000..d66d856 --- /dev/null +++ b/venv2/lib/python2.7/site-packages/pip/_vendor/distlib/_backport/tarfile.py @@ -0,0 +1,2607 @@ +#------------------------------------------------------------------- +# tarfile.py +#------------------------------------------------------------------- +# Copyright (C) 2002 Lars Gustaebel <lars@gustaebel.de> +# All rights reserved. +# +# 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 print_function + +"""Read from and write to tar format archives. +""" + +__version__ = "$Revision$" + +version = "0.9.0" +__author__ = "Lars Gust\u00e4bel (lars@gustaebel.de)" +__date__ = "$Date: 2011-02-25 17:42:01 +0200 (Fri, 25 Feb 2011) $" +__cvsid__ = "$Id: tarfile.py 88586 2011-02-25 15:42:01Z marc-andre.lemburg $" +__credits__ = "Gustavo Niemeyer, Niels Gust\u00e4bel, Richard Townsend." + +#--------- +# Imports +#--------- +import sys +import os +import stat +import errno +import time +import struct +import copy +import re + +try: + import grp, pwd +except ImportError: + grp = pwd = None + +# os.symlink on Windows prior to 6.0 raises NotImplementedError +symlink_exception = (AttributeError, NotImplementedError) +try: + # WindowsError (1314) will be raised if the caller does not hold the + # SeCreateSymbolicLinkPrivilege privilege + symlink_exception += (WindowsError,) +except NameError: + pass + +# from tarfile import * +__all__ = ["TarFile", "TarInfo", "is_tarfile", "TarError"] + +if sys.version_info[0] < 3: + import __builtin__ as builtins +else: + import builtins + +_open = builtins.open # Since 'open' is TarFile.open + +#--------------------------------------------------------- +# tar constants +#--------------------------------------------------------- +NUL = b"\0" # the null character +BLOCKSIZE = 512 # length of processing blocks +RECORDSIZE = BLOCKSIZE * 20 # length of records +GNU_MAGIC = b"ustar \0" # magic gnu tar string +POSIX_MAGIC = b"ustar\x0000" # magic posix tar string + +LENGTH_NAME = 100 # maximum length of a filename +LENGTH_LINK = 100 # maximum length of a linkname +LENGTH_PREFIX = 155 # maximum length of the prefix field + +REGTYPE = b"0" # regular file +AREGTYPE = b"\0" # regular file +LNKTYPE = b"1" # link (inside tarfile) +SYMTYPE = b"2" # symbolic link +CHRTYPE = b"3" # character special device +BLKTYPE = b"4" # block special device +DIRTYPE = b"5" # directory +FIFOTYPE = b"6" # fifo special device +CONTTYPE = b"7" # contiguous file + +GNUTYPE_LONGNAME = b"L" # GNU tar longname +GNUTYPE_LONGLINK = b"K" # GNU tar longlink +GNUTYPE_SPARSE = b"S" # GNU tar sparse file + +XHDTYPE = b"x" # POSIX.1-2001 extended header +XGLTYPE = b"g" # POSIX.1-2001 global header +SOLARIS_XHDTYPE = b"X" # Solaris extended header + +USTAR_FORMAT = 0 # POSIX.1-1988 (ustar) format +GNU_FORMAT = 1 # GNU tar format +PAX_FORMAT = 2 # POSIX.1-2001 (pax) format +DEFAULT_FORMAT = GNU_FORMAT + +#--------------------------------------------------------- +# tarfile constants +#--------------------------------------------------------- +# File types that tarfile supports: +SUPPORTED_TYPES = (REGTYPE, AREGTYPE, LNKTYPE, + SYMTYPE, DIRTYPE, FIFOTYPE, + CONTTYPE, CHRTYPE, BLKTYPE, + GNUTYPE_LONGNAME, GNUTYPE_LONGLINK, + GNUTYPE_SPARSE) + +# File types that will be treated as a regular file. +REGULAR_TYPES = (REGTYPE, AREGTYPE, + CONTTYPE, GNUTYPE_SPARSE) + +# File types that are part of the GNU tar format. +GNU_TYPES = (GNUTYPE_LONGNAME, GNUTYPE_LONGLINK, + GNUTYPE_SPARSE) + +# Fields from a pax header that override a TarInfo attribute. +PAX_FIELDS = ("path", "linkpath", "size", "mtime", + "uid", "gid", "uname", "gname") + +# Fields from a pax header that are affected by hdrcharset. +PAX_NAME_FIELDS = set(("path", "linkpath", "uname", "gname")) + +# Fields in a pax header that are numbers, all other fields +# are treated as strings. +PAX_NUMBER_FIELDS = { + "atime": float, + "ctime": float, + "mtime": float, + "uid": int, + "gid": int, + "size": int +} + +#--------------------------------------------------------- +# Bits used in the mode field, values in octal. +#--------------------------------------------------------- +S_IFLNK = 0o120000 # symbolic link +S_IFREG = 0o100000 # regular file +S_IFBLK = 0o060000 # block device +S_IFDIR = 0o040000 # directory +S_IFCHR = 0o020000 # character device +S_IFIFO = 0o010000 # fifo + +TSUID = 0o4000 # set UID on execution +TSGID = 0o2000 # set GID on execution +TSVTX = 0o1000 # reserved + +TUREAD = 0o400 # read by owner +TUWRITE = 0o200 # write by owner +TUEXEC = 0o100 # execute/search by owner +TGREAD = 0o040 # read by group +TGWRITE = 0o020 # write by group +TGEXEC = 0o010 # execute/search by group +TOREAD = 0o004 # read by other +TOWRITE = 0o002 # write by other +TOEXEC = 0o001 # execute/search by other + +#--------------------------------------------------------- +# initialization +#--------------------------------------------------------- +if os.name in ("nt", "ce"): + ENCODING = "utf-8" +else: + ENCODING = sys.getfilesystemencoding() + +#--------------------------------------------------------- +# Some useful functions +#--------------------------------------------------------- + +def stn(s, length, encoding, errors): + """Convert a string to a null-terminated bytes object. + """ + s = s.encode(encoding, errors) + return s[:length] + (length - len(s)) * NUL + +def nts(s, encoding, errors): + """Convert a null-terminated bytes object to a string. + """ + p = s.find(b"\0") + if p != -1: + s = s[:p] + return s.decode(encoding, errors) + +def nti(s): + """Convert a number field to a python number. + """ + # There are two possible encodings for a number field, see + # itn() below. + if s[0] != chr(0o200): + try: + n = int(nts(s, "ascii", "strict") or "0", 8) + except ValueError: + raise InvalidHeaderError("invalid header") + else: + n = 0 + for i in range(len(s) - 1): + n <<= 8 + n += ord(s[i + 1]) + return n + +def itn(n, digits=8, format=DEFAULT_FORMAT): + """Convert a python number to a number field. + """ + # POSIX 1003.1-1988 requires numbers to be encoded as a string of + # octal digits followed by a null-byte, this allows values up to + # (8**(digits-1))-1. GNU tar allows storing numbers greater than + # that if necessary. A leading 0o200 byte indicates this particular + # encoding, the following digits-1 bytes are a big-endian + # representation. This allows values up to (256**(digits-1))-1. + if 0 <= n < 8 ** (digits - 1): + s = ("%0*o" % (digits - 1, n)).encode("ascii") + NUL + else: + if format != GNU_FORMAT or n >= 256 ** (digits - 1): + raise ValueError("overflow in number field") + + if n < 0: + # XXX We mimic GNU tar's behaviour with negative numbers, + # this could raise OverflowError. + n = struct.unpack("L", struct.pack("l", n))[0] + + s = bytearray() + for i in range(digits - 1): + s.insert(0, n & 0o377) + n >>= 8 + s.insert(0, 0o200) + return s + +def calc_chksums(buf): + """Calculate the checksum for a member's header by summing up all + characters except for the chksum field which is treated as if + it was filled with spaces. According to the GNU tar sources, + some tars (Sun and NeXT) calculate chksum with signed char, + which will be different if there are chars in the buffer with + the high bit set. So we calculate two checksums, unsigned and + signed. + """ + unsigned_chksum = 256 + sum(struct.unpack("148B", buf[:148]) + struct.unpack("356B", buf[156:512])) + signed_chksum = 256 + sum(struct.unpack("148b", buf[:148]) + struct.unpack("356b", buf[156:512])) + return unsigned_chksum, signed_chksum + +def copyfileobj(src, dst, length=None): + """Copy length bytes from fileobj src to fileobj dst. + If length is None, copy the entire content. + """ + if length == 0: + return + if length is None: + while True: + buf = src.read(16*1024) + if not buf: + break + dst.write(buf) + return + + BUFSIZE = 16 * 1024 + blocks, remainder = divmod(length, BUFSIZE) + for b in range(blocks): + buf = src.read(BUFSIZE) + if len(buf) < BUFSIZE: + raise IOError("end of file reached") + dst.write(buf) + + if remainder != 0: + buf = src.read(remainder) + if len(buf) < remainder: + raise IOError("end of file reached") + dst.write(buf) + return + +filemode_table = ( + ((S_IFLNK, "l"), + (S_IFREG, "-"), + (S_IFBLK, "b"), + (S_IFDIR, "d"), + (S_IFCHR, "c"), + (S_IFIFO, "p")), + + ((TUREAD, "r"),), + ((TUWRITE, "w"),), + ((TUEXEC|TSUID, "s"), + (TSUID, "S"), + (TUEXEC, "x")), + + ((TGREAD, "r"),), + ((TGWRITE, "w"),), + ((TGEXEC|TSGID, "s"), + (TSGID, "S"), + (TGEXEC, "x")), + + ((TOREAD, "r"),), + ((TOWRITE, "w"),), + ((TOEXEC|TSVTX, "t"), + (TSVTX, "T"), + (TOEXEC, "x")) +) + +def filemode(mode): + """Convert a file's mode to a string of the form + -rwxrwxrwx. + Used by TarFile.list() + """ + perm = [] + for table in filemode_table: + for bit, char in table: + if mode & bit == bit: + perm.append(char) + break + else: + perm.append("-") + return "".join(perm) + +class TarError(Exception): + """Base exception.""" + pass +class ExtractError(TarError): + """General exception for extract errors.""" + pass +class ReadError(TarError): + """Exception for unreadable tar archives.""" + pass +class CompressionError(TarError): + """Exception for unavailable compression methods.""" + pass +class StreamError(TarError): + """Exception for unsupported operations on stream-like TarFiles.""" + pass +class HeaderError(TarError): + """Base exception for header errors.""" + pass +class EmptyHeaderError(HeaderError): + """Exception for empty headers.""" + pass +class TruncatedHeaderError(HeaderError): + """Exception for truncated headers.""" + pass +class EOFHeaderError(HeaderError): + """Exception for end of file headers.""" + pass +class InvalidHeaderError(HeaderError): + """Exception for invalid headers.""" + pass +class SubsequentHeaderError(HeaderError): + """Exception for missing and invalid extended headers.""" + pass + +#--------------------------- +# internal stream interface +#--------------------------- +class _LowLevelFile(object): + """Low-level file object. Supports reading and writing. + It is used instead of a regular file object for streaming + access. + """ + + def __init__(self, name, mode): + mode = { + "r": os.O_RDONLY, + "w": os.O_WRONLY | os.O_CREAT | os.O_TRUNC, + }[mode] + if hasattr(os, "O_BINARY"): + mode |= os.O_BINARY + self.fd = os.open(name, mode, 0o666) + + def close(self): + os.close(self.fd) + + def read(self, size): + return os.read(self.fd, size) + + def write(self, s): + os.write(self.fd, s) + +class _Stream(object): + """Class that serves as an adapter between TarFile and + a stream-like object. The stream-like object only + needs to have a read() or write() method and is accessed + blockwise. Use of gzip or bzip2 compression is possible. + A stream-like object could be for example: sys.stdin, + sys.stdout, a socket, a tape device etc. + + _Stream is intended to be used only internally. + """ + + def __init__(self, name, mode, comptype, fileobj, bufsize): + """Construct a _Stream object. + """ + self._extfileobj = True + if fileobj is None: + fileobj = _LowLevelFile(name, mode) + self._extfileobj = False + + if comptype == '*': + # Enable transparent compression detection for the + # stream interface + fileobj = _StreamProxy(fileobj) + comptype = fileobj.getcomptype() + + self.name = name or "" + self.mode = mode + self.comptype = comptype + self.fileobj = fileobj + self.bufsize = bufsize + self.buf = b"" + self.pos = 0 + self.closed = False + + try: + if comptype == "gz": + try: + import zlib + except ImportError: + raise CompressionError("zlib module is not available") + self.zlib = zlib + self.crc = zlib.crc32(b"") + if mode == "r": + self._init_read_gz() + else: + self._init_write_gz() + + if comptype == "bz2": + try: + import bz2 + except ImportError: + raise CompressionError("bz2 module is not available") + if mode == "r": + self.dbuf = b"" + self.cmp = bz2.BZ2Decompressor() + else: + self.cmp = bz2.BZ2Compressor() + except: + if not self._extfileobj: + self.fileobj.close() + self.closed = True + raise + + def __del__(self): + if hasattr(self, "closed") and not self.closed: + self.close() + + def _init_write_gz(self): + """Initialize for writing with gzip compression. + """ + self.cmp = self.zlib.compressobj(9, self.zlib.DEFLATED, + -self.zlib.MAX_WBITS, + self.zlib.DEF_MEM_LEVEL, + 0) + timestamp = struct.pack("<L", int(time.time())) + self.__write(b"\037\213\010\010" + timestamp + b"\002\377") + if self.name.endswith(".gz"): + self.name = self.name[:-3] + # RFC1952 says we must use ISO-8859-1 for the FNAME field. + self.__write(self.name.encode("iso-8859-1", "replace") + NUL) + + def write(self, s): + """Write string s to the stream. + """ + if self.comptype == "gz": + self.crc = self.zlib.crc32(s, self.crc) + self.pos += len(s) + if self.comptype != "tar": + s = self.cmp.compress(s) + self.__write(s) + + def __write(self, s): + """Write string s to the stream if a whole new block + is ready to be written. + """ + self.buf += s + while len(self.buf) > self.bufsize: + self.fileobj.write(self.buf[:self.bufsize]) + self.buf = self.buf[self.bufsize:] + + def close(self): + """Close the _Stream object. No operation should be + done on it afterwards. + """ + if self.closed: + return + + if self.mode == "w" and self.comptype != "tar": + self.buf += self.cmp.flush() + + if self.mode == "w" and self.buf: + self.fileobj.write(self.buf) + self.buf = b"" + if self.comptype == "gz": + # The native zlib crc is an unsigned 32-bit integer, but + # the Python wrapper implicitly casts that to a signed C + # long. So, on a 32-bit box self.crc may "look negative", + # while the same crc on a 64-bit box may "look positive". + # To avoid irksome warnings from the `struct` module, force + # it to look positive on all boxes. + self.fileobj.write(struct.pack("<L", self.crc & 0xffffffff)) + self.fileobj.write(struct.pack("<L", self.pos & 0xffffFFFF)) + + if not self._extfileobj: + self.fileobj.close() + + self.closed = True + + def _init_read_gz(self): + """Initialize for reading a gzip compressed fileobj. + """ + self.cmp = self.zlib.decompressobj(-self.zlib.MAX_WBITS) + self.dbuf = b"" + + # taken from gzip.GzipFile with some alterations + if self.__read(2) != b"\037\213": + raise ReadError("not a gzip file") + if self.__read(1) != b"\010": + raise CompressionError("unsupported compression method") + + flag = ord(self.__read(1)) + self.__read(6) + + if flag & 4: + xlen = ord(self.__read(1)) + 256 * ord(self.__read(1)) + self.read(xlen) + if flag & 8: + while True: + s = self.__read(1) + if not s or s == NUL: + break + if flag & 16: + while True: + s = self.__read(1) + if not s or s == NUL: + break + if flag & 2: + self.__read(2) + + def tell(self): + """Return the stream's file pointer position. + """ + return self.pos + + def seek(self, pos=0): + """Set the stream's file pointer to pos. Negative seeking + is forbidden. + """ + if pos - self.pos >= 0: + blocks, remainder = divmod(pos - self.pos, self.bufsize) + for i in range(blocks): + self.read(self.bufsize) + self.read(remainder) + else: + raise StreamError("seeking backwards is not allowed") + return self.pos + + def read(self, size=None): + """Return the next size number of bytes from the stream. + If size is not defined, return all bytes of the stream + up to EOF. + """ + if size is None: + t = [] + while True: + buf = self._read(self.bufsize) + if not buf: + break + t.append(buf) + buf = "".join(t) + else: + buf = self._read(size) + self.pos += len(buf) + return buf + + def _read(self, size): + """Return size bytes from the stream. + """ + if self.comptype == "tar": + return self.__read(size) + + c = len(self.dbuf) + while c < size: + buf = self.__read(self.bufsize) + if not buf: + break + try: + buf = self.cmp.decompress(buf) + except IOError: + raise ReadError("invalid compressed data") + self.dbuf += buf + c += len(buf) + buf = self.dbuf[:size] + self.dbuf = self.dbuf[size:] + return buf + + def __read(self, size): + """Return size bytes from stream. If internal buffer is empty, + read another block from the stream. + """ + c = len(self.buf) + while c < size: + buf = self.fileobj.read(self.bufsize) + if not buf: + break + self.buf += buf + c += len(buf) + buf = self.buf[:size] + self.buf = self.buf[size:] + return buf +# class _Stream + +class _StreamProxy(object): + """Small proxy class that enables transparent compression + detection for the Stream interface (mode 'r|*'). + """ + + def __init__(self, fileobj): + self.fileobj = fileobj + self.buf = self.fileobj.read(BLOCKSIZE) + + def read(self, size): + self.read = self.fileobj.read + return self.buf + + def getcomptype(self): + if self.buf.startswith(b"\037\213\010"): + return "gz" + if self.buf.startswith(b"BZh91"): + return "bz2" + return "tar" + + def close(self): + self.fileobj.close() +# class StreamProxy + +class _BZ2Proxy(object): + """Small proxy class that enables external file object + support for "r:bz2" and "w:bz2" modes. This is actually + a workaround for a limitation in bz2 module's BZ2File + class which (unlike gzip.GzipFile) has no support for + a file object argument. + """ + + blocksize = 16 * 1024 + + def __init__(self, fileobj, mode): + self.fileobj = fileobj + self.mode = mode + self.name = getattr(self.fileobj, "name", None) + self.init() + + def init(self): + import bz2 + self.pos = 0 + if self.mode == "r": + self.bz2obj = bz2.BZ2Decompressor() + self.fileobj.seek(0) + self.buf = b"" + else: + self.bz2obj = bz2.BZ2Compressor() + + def read(self, size): + x = len(self.buf) + while x < size: + raw = self.fileobj.read(self.blocksize) + if not raw: + break + data = self.bz2obj.decompress(raw) + self.buf += data + x += len(data) + + buf = self.buf[:size] + self.buf = self.buf[size:] + self.pos += len(buf) + return buf + + def seek(self, pos): + if pos < self.pos: + self.init() + self.read(pos - self.pos) + + def tell(self): + return self.pos + + def write(self, data): + self.pos += len(data) + raw = self.bz2obj.compress(data) + self.fileobj.write(raw) + + def close(self): + if self.mode == "w": + raw = self.bz2obj.flush() + self.fileobj.write(raw) +# class _BZ2Proxy + +#------------------------ +# Extraction file object +#------------------------ +class _FileInFile(object): + """A thin wrapper around an existing file object that + provides a part of its data as an individual file + object. + """ + + def __init__(self, fileobj, offset, size, blockinfo=None): + self.fileobj = fileobj + self.offset = offset + self.size = size + self.position = 0 + + if blockinfo is None: + blockinfo = [(0, size)] + + # Construct a map with data and zero blocks. + self.map_index = 0 + self.map = [] + lastpos = 0 + realpos = self.offset + for offset, size in blockinfo: + if offset > lastpos: + self.map.append((False, lastpos, offset, None)) + self.map.append((True, offset, offset + size, realpos)) + realpos += size + lastpos = offset + size + if lastpos < self.size: + self.map.append((False, lastpos, self.size, None)) + + def seekable(self): + if not hasattr(self.fileobj, "seekable"): + # XXX gzip.GzipFile and bz2.BZ2File + return True + return self.fileobj.seekable() + + def tell(self): + """Return the current file position. + """ + return self.position + + def seek(self, position): + """Seek to a position in the file. + """ + self.position = position + + def read(self, size=None): + """Read data from the file. + """ + if size is None: + size = self.size - self.position + else: + size = min(size, self.size - self.position) + + buf = b"" + while size > 0: + while True: + data, start, stop, offset = self.map[self.map_index] + if start <= self.position < stop: + break + else: + self.map_index += 1 + if self.map_index == len(self.map): + self.map_index = 0 + length = min(size, stop - self.position) + if data: + self.fileobj.seek(offset + (self.position - start)) + buf += self.fileobj.read(length) + else: + buf += NUL * length + size -= length + self.position += length + return buf +#class _FileInFile + + +class ExFileObject(object): + """File-like object for reading an archive member. + Is returned by TarFile.extractfile(). + """ + blocksize = 1024 + + def __init__(self, tarfile, tarinfo): + self.fileobj = _FileInFile(tarfile.fileobj, + tarinfo.offset_data, + tarinfo.size, + tarinfo.sparse) + self.name = tarinfo.name + self.mode = "r" + self.closed = False + self.size = tarinfo.size + + self.position = 0 + self.buffer = b"" + + def readable(self): + return True + + def writable(self): + return False + + def seekable(self): + return self.fileobj.seekable() + + def read(self, size=None): + """Read at most size bytes from the file. If size is not + present or None, read all data until EOF is reached. + """ + if self.closed: + raise ValueError("I/O operation on closed file") + + buf = b"" + if self.buffer: + if size is None: + buf = self.buffer + self.buffer = b"" + else: + buf = self.buffer[:size] + self.buffer = self.buffer[size:] + + if size is None: + buf += self.fileobj.read() + else: + buf += self.fileobj.read(size - len(buf)) + + self.position += len(buf) + return buf + + # XXX TextIOWrapper uses the read1() method. + read1 = read + + def readline(self, size=-1): + """Read one entire line from the file. If size is present + and non-negative, return a string with at most that + size, which may be an incomplete line. + """ + if self.closed: + raise ValueError("I/O operation on closed file") + + pos = self.buffer.find(b"\n") + 1 + if pos == 0: + # no newline found. + while True: + buf = self.fileobj.read(self.blocksize) + self.buffer += buf + if not buf or b"\n" in buf: + pos = self.buffer.find(b"\n") + 1 + if pos == 0: + # no newline found. + pos = len(self.buffer) + break + + if size != -1: + pos = min(size, pos) + + buf = self.buffer[:pos] + self.buffer = self.buffer[pos:] + self.position += len(buf) + return buf + + def readlines(self): + """Return a list with all remaining lines. + """ + result = [] + while True: + line = self.readline() + if not line: break + result.append(line) + return result + + def tell(self): + """Return the current file position. + """ + if self.closed: + raise ValueError("I/O operation on closed file") + + return self.position + + def seek(self, pos, whence=os.SEEK_SET): + """Seek to a position in the file. + """ + if self.closed: + raise ValueError("I/O operation on closed file") + + if whence == os.SEEK_SET: + self.position = min(max(pos, 0), self.size) + elif whence == os.SEEK_CUR: + if pos < 0: + self.position = max(self.position + pos, 0) + else: + self.position = min(self.position + pos, self.size) + elif whence == os.SEEK_END: + self.position = max(min(self.size + pos, self.size), 0) + else: + raise ValueError("Invalid argument") + + self.buffer = b"" + self.fileobj.seek(self.position) + + def close(self): + """Close the file object. + """ + self.closed = True + + def __iter__(self): + """Get an iterator over the file's lines. + """ + while True: + line = self.readline() + if not line: + break + yield line +#class ExFileObject + +#------------------ +# Exported Classes +#------------------ +class TarInfo(object): + """Informational class which holds the details about an + archive member given by a tar header block. + TarInfo objects are returned by TarFile.getmember(), + TarFile.getmembers() and TarFile.gettarinfo() and are + usually created internally. + """ + + __slots__ = ("name", "mode", "uid", "gid", "size", "mtime", + "chksum", "type", "linkname", "uname", "gname", + "devmajor", "devminor", + "offset", "offset_data", "pax_headers", "sparse", + "tarfile", "_sparse_structs", "_link_target") + + def __init__(self, name=""): + """Construct a TarInfo object. name is the optional name + of the member. + """ + self.name = name # member name + self.mode = 0o644 # file permissions + self.uid = 0 # user id + self.gid = 0 # group id + self.size = 0 # file size + self.mtime = 0 # modification time + self.chksum = 0 # header checksum + self.type = REGTYPE # member type + self.linkname = "" # link name + self.uname = "" # user name + self.gname = "" # group name + self.devmajor = 0 # device major number + self.devminor = 0 # device minor number + + self.offset = 0 # the tar header starts here + self.offset_data = 0 # the file's data starts here + + self.sparse = None # sparse member information + self.pax_headers = {} # pax header information + + # In pax headers the "name" and "linkname" field are called + # "path" and "linkpath". + def _getpath(self): + return self.name + def _setpath(self, name): + self.name = name + path = property(_getpath, _setpath) + + def _getlinkpath(self): + return self.linkname + def _setlinkpath(self, linkname): + self.linkname = linkname + linkpath = property(_getlinkpath, _setlinkpath) + + def __repr__(self): + return "<%s %r at %#x>" % (self.__class__.__name__,self.name,id(self)) + + def get_info(self): + """Return the TarInfo's attributes as a dictionary. + """ + info = { + "name": self.name, + "mode": self.mode & 0o7777, + "uid": self.uid, + "gid": self.gid, + "size": self.size, + "mtime": self.mtime, + "chksum": self.chksum, + "type": self.type, + "linkname": self.linkname, + "uname": self.uname, + "gname": self.gname, + "devmajor": self.devmajor, + "devminor": self.devminor + } + + if info["type"] == DIRTYPE and not info["name"].endswith("/"): + info["name"] += "/" + + return info + + def tobuf(self, format=DEFAULT_FORMAT, encoding=ENCODING, errors="surrogateescape"): + """Return a tar header as a string of 512 byte blocks. + """ + info = self.get_info() + + if format == USTAR_FORMAT: + return self.create_ustar_header(info, encoding, errors) + elif format == GNU_FORMAT: + return self.create_gnu_header(info, encoding, errors) + elif format == PAX_FORMAT: + return self.create_pax_header(info, encoding) + else: + raise ValueError("invalid format") + + def create_ustar_header(self, info, encoding, errors): + """Return the object as a ustar header block. + """ + info["magic"] = POSIX_MAGIC + + if len(info["linkname"]) > LENGTH_LINK: + raise ValueError("linkname is too long") + + if len(info["name"]) > LENGTH_NAME: + info["prefix"], info["name"] = self._posix_split_name(info["name"]) + + return self._create_header(info, USTAR_FORMAT, encoding, errors) + + def create_gnu_header(self, info, encoding, errors): + """Return the object as a GNU header block sequence. + """ + info["magic"] = GNU_MAGIC + + buf = b"" + if len(info["linkname"]) > LENGTH_LINK: + buf += self._create_gnu_long_header(info["linkname"], GNUTYPE_LONGLINK, encoding, errors) + + if len(info["name"]) > LENGTH_NAME: + buf += self._create_gnu_long_header(info["name"], GNUTYPE_LONGNAME, encoding, errors) + + return buf + self._create_header(info, GNU_FORMAT, encoding, errors) + + def create_pax_header(self, info, encoding): + """Return the object as a ustar header block. If it cannot be + represented this way, prepend a pax extended header sequence + with supplement information. + """ + info["magic"] = POSIX_MAGIC + pax_headers = self.pax_headers.copy() + + # Test string fields for values that exceed the field length or cannot + # be represented in ASCII encoding. + for name, hname, length in ( + ("name", "path", LENGTH_NAME), ("linkname", "linkpath", LENGTH_LINK), + ("uname", "uname", 32), ("gname", "gname", 32)): + + if hname in pax_headers: + # The pax header has priority. + continue + + # Try to encode the string as ASCII. + try: + info[name].encode("ascii", "strict") + except UnicodeEncodeError: + pax_headers[hname] = info[name] + continue + + if len(info[name]) > length: + pax_headers[hname] = info[name] + + # Test number fields for values that exceed the field limit or values + # that like to be stored as float. + for name, digits in (("uid", 8), ("gid", 8), ("size", 12), ("mtime", 12)): + if name in pax_headers: + # The pax header has priority. Avoid overflow. + info[name] = 0 + continue + + val = info[name] + if not 0 <= val < 8 ** (digits - 1) or isinstance(val, float): + pax_headers[name] = str(val) + info[name] = 0 + + # Create a pax extended header if necessary. + if pax_headers: + buf = self._create_pax_generic_header(pax_headers, XHDTYPE, encoding) + else: + buf = b"" + + return buf + self._create_header(info, USTAR_FORMAT, "ascii", "replace") + + @classmethod + def create_pax_global_header(cls, pax_headers): + """Return the object as a pax global header block sequence. + """ + return cls._create_pax_generic_header(pax_headers, XGLTYPE, "utf8") + + def _posix_split_name(self, name): + """Split a name longer than 100 chars into a prefix + and a name part. + """ + prefix = name[:LENGTH_PREFIX + 1] + while prefix and prefix[-1] != "/": + prefix = prefix[:-1] + + name = name[len(prefix):] + prefix = prefix[:-1] + + if not prefix or len(name) > LENGTH_NAME: + raise ValueError("name is too long") + return prefix, name + + @staticmethod + def _create_header(info, format, encoding, errors): + """Return a header block. info is a dictionary with file + information, format must be one of the *_FORMAT constants. + """ + parts = [ + stn(info.get("name", ""), 100, encoding, errors), + itn(info.get("mode", 0) & 0o7777, 8, format), + itn(info.get("uid", 0), 8, format), + itn(info.get("gid", 0), 8, format), + itn(info.get("size", 0), 12, format), + itn(info.get("mtime", 0), 12, format), + b" ", # checksum field + info.get("type", REGTYPE), + stn(info.get("linkname", ""), 100, encoding, errors), + info.get("magic", POSIX_MAGIC), + stn(info.get("uname", ""), 32, encoding, errors), + stn(info.get("gname", ""), 32, encoding, errors), + itn(info.get("devmajor", 0), 8, format), + itn(info.get("devminor", 0), 8, format), + stn(info.get("prefix", ""), 155, encoding, errors) + ] + + buf = struct.pack("%ds" % BLOCKSIZE, b"".join(parts)) + chksum = calc_chksums(buf[-BLOCKSIZE:])[0] + buf = buf[:-364] + ("%06o\0" % chksum).encode("ascii") + buf[-357:] + return buf + + @staticmethod + def _create_payload(payload): + """Return the string payload filled with zero bytes + up to the next 512 byte border. + """ + blocks, remainder = divmod(len(payload), BLOCKSIZE) + if remainder > 0: + payload += (BLOCKSIZE - remainder) * NUL + return payload + + @classmethod + def _create_gnu_long_header(cls, name, type, encoding, errors): + """Return a GNUTYPE_LONGNAME or GNUTYPE_LONGLINK sequence + for name. + """ + name = name.encode(encoding, errors) + NUL + + info = {} + info["name"] = "././@LongLink" + info["type"] = type + info["size"] = len(name) + info["magic"] = GNU_MAGIC + + # create extended header + name blocks. + return cls._create_header(info, USTAR_FORMAT, encoding, errors) + \ + cls._create_payload(name) + + @classmethod + def _create_pax_generic_header(cls, pax_headers, type, encoding): + """Return a POSIX.1-2008 extended or global header sequence + that contains a list of keyword, value pairs. The values + must be strings. + """ + # Check if one of the fields contains surrogate characters and thereby + # forces hdrcharset=BINARY, see _proc_pax() for more information. + binary = False + for keyword, value in pax_headers.items(): + try: + value.encode("utf8", "strict") + except UnicodeEncodeError: + binary = True + break + + records = b"" + if binary: + # Put the hdrcharset field at the beginning of the header. + records += b"21 hdrcharset=BINARY\n" + + for keyword, value in pax_headers.items(): + keyword = keyword.encode("utf8") + if binary: + # Try to restore the original byte representation of `value'. + # Needless to say, that the encoding must match the string. + value = value.encode(encoding, "surrogateescape") + else: + value = value.encode("utf8") + + l = len(keyword) + len(value) + 3 # ' ' + '=' + '\n' + n = p = 0 + while True: + n = l + len(str(p)) + if n == p: + break + p = n + records += bytes(str(p), "ascii") + b" " + keyword + b"=" + value + b"\n" + + # We use a hardcoded "././@PaxHeader" name like star does + # instead of the one that POSIX recommends. + info = {} + info["name"] = "././@PaxHeader" + info["type"] = type + info["size"] = len(records) + info["magic"] = POSIX_MAGIC + + # Create pax header + record blocks. + return cls._create_header(info, USTAR_FORMAT, "ascii", "replace") + \ + cls._create_payload(records) + + @classmethod + def frombuf(cls, buf, encoding, errors): + """Construct a TarInfo object from a 512 byte bytes object. + """ + if len(buf) == 0: + raise EmptyHeaderError("empty header") + if len(buf) != BLOCKSIZE: + raise TruncatedHeaderError("truncated header") + if buf.count(NUL) == BLOCKSIZE: + raise EOFHeaderError("end of file header") + + chksum = nti(buf[148:156]) + if chksum not in calc_chksums(buf): + raise InvalidHeaderError("bad checksum") + + obj = cls() + obj.name = nts(buf[0:100], encoding, errors) + obj.mode = nti(buf[100:108]) + obj.uid = nti(buf[108:116]) + obj.gid = nti(buf[116:124]) + obj.size = nti(buf[124:136]) + obj.mtime = nti(buf[136:148]) + obj.chksum = chksum + obj.type = buf[156:157] + obj.linkname = nts(buf[157:257], encoding, errors) + obj.uname = nts(buf[265:297], encoding, errors) + obj.gname = nts(buf[297:329], encoding, errors) + obj.devmajor = nti(buf[329:337]) + obj.devminor = nti(buf[337:345]) + prefix = nts(buf[345:500], encoding, errors) + + # Old V7 tar format represents a directory as a regular + # file with a trailing slash. + if obj.type == AREGTYPE and obj.name.endswith("/"): + obj.type = DIRTYPE + + # The old GNU sparse format occupies some of the unused + # space in the buffer for up to 4 sparse structures. + # Save the them for later processing in _proc_sparse(). + if obj.type == GNUTYPE_SPARSE: + pos = 386 + structs = [] + for i in range(4): + try: + offset = nti(buf[pos:pos + 12]) + numbytes = nti(buf[pos + 12:pos + 24]) + except ValueError: + break + structs.append((offset, numbytes)) + pos += 24 + isextended = bool(buf[482]) + origsize = nti(buf[483:495]) + obj._sparse_structs = (structs, isextended, origsize) + + # Remove redundant slashes from directories. + if obj.isdir(): + obj.name = obj.name.rstrip("/") + + # Reconstruct a ustar longname. + if prefix and obj.type not in GNU_TYPES: + obj.name = prefix + "/" + obj.name + return obj + + @classmethod + def fromtarfile(cls, tarfile): + """Return the next TarInfo object from TarFile object + tarfile. + """ + buf = tarfile.fileobj.read(BLOCKSIZE) + obj = cls.frombuf(buf, tarfile.encoding, tarfile.errors) + obj.offset = tarfile.fileobj.tell() - BLOCKSIZE + return obj._proc_member(tarfile) + + #-------------------------------------------------------------------------- + # The following are methods that are called depending on the type of a + # member. The entry point is _proc_member() which can be overridden in a + # subclass to add custom _proc_*() methods. A _proc_*() method MUST + # implement the following + # operations: + # 1. Set self.offset_data to the position where the data blocks begin, + # if there is data that follows. + # 2. Set tarfile.offset to the position where the next member's header will + # begin. + # 3. Return self or another valid TarInfo object. + def _proc_member(self, tarfile): + """Choose the right processing method depending on + the type and call it. + """ + if self.type in (GNUTYPE_LONGNAME, GNUTYPE_LONGLINK): + return self._proc_gnulong(tarfile) + elif self.type == GNUTYPE_SPARSE: + return self._proc_sparse(tarfile) + elif self.type in (XHDTYPE, XGLTYPE, SOLARIS_XHDTYPE): + return self._proc_pax(tarfile) + else: + return self._proc_builtin(tarfile) + + def _proc_builtin(self, tarfile): + """Process a builtin type or an unknown type which + will be treated as a regular file. + """ + self.offset_data = tarfile.fileobj.tell() + offset = self.offset_data + if self.isreg() or self.type not in SUPPORTED_TYPES: + # Skip the following data blocks. + offset += self._block(self.size) + tarfile.offset = offset + + # Patch the TarInfo object with saved global + # header information. + self._apply_pax_info(tarfile.pax_headers, tarfile.encoding, tarfile.errors) + + return self + + def _proc_gnulong(self, tarfile): + """Process the blocks that hold a GNU longname + or longlink member. + """ + buf = tarfile.fileobj.read(self._block(self.size)) + + # Fetch the next header and process it. + try: + next = self.fromtarfile(tarfile) + except HeaderError: + raise SubsequentHeaderError("missing or bad subsequent header") + + # Patch the TarInfo object from the next header with + # the longname information. + next.offset = self.offset + if self.type == GNUTYPE_LONGNAME: + next.name = nts(buf, tarfile.encoding, tarfile.errors) + elif self.type == GNUTYPE_LONGLINK: + next.linkname = nts(buf, tarfile.encoding, tarfile.errors) + + return next + + def _proc_sparse(self, tarfile): + """Process a GNU sparse header plus extra headers. + """ + # We already collected some sparse structures in frombuf(). + structs, isextended, origsize = self._sparse_structs + del self._sparse_structs + + # Collect sparse structures from extended header blocks. + while isextended: + buf = tarfile.fileobj.read(BLOCKSIZE) + pos = 0 + for i in range(21): + try: + offset = nti(buf[pos:pos + 12]) + numbytes = nti(buf[pos + 12:pos + 24]) + except ValueError: + break + if offset and numbytes: + structs.append((offset, numbytes)) + pos += 24 + isextended = bool(buf[504]) + self.sparse = structs + + self.offset_data = tarfile.fileobj.tell() + tarfile.offset = self.offset_data + self._block(self.size) + self.size = origsize + return self + + def _proc_pax(self, tarfile): + """Process an extended or global header as described in + POSIX.1-2008. + """ + # Read the header information. + buf = tarfile.fileobj.read(self._block(self.size)) + + # A pax header stores supplemental information for either + # the following file (extended) or all following files + # (global). + if self.type == XGLTYPE: + pax_headers = tarfile.pax_headers + else: + pax_headers = tarfile.pax_headers.copy() + + # Check if the pax header contains a hdrcharset field. This tells us + # the encoding of the path, linkpath, uname and gname fields. Normally, + # these fields are UTF-8 encoded but since POSIX.1-2008 tar + # implementations are allowed to store them as raw binary strings if + # the translation to UTF-8 fails. + match = re.search(br"\d+ hdrcharset=([^\n]+)\n", buf) + if match is not None: + pax_headers["hdrcharset"] = match.group(1).decode("utf8") + + # For the time being, we don't care about anything other than "BINARY". + # The only other value that is currently allowed by the standard is + # "ISO-IR 10646 2000 UTF-8" in other words UTF-8. + hdrcharset = pax_headers.get("hdrcharset") + if hdrcharset == "BINARY": + encoding = tarfile.encoding + else: + encoding = "utf8" + + # Parse pax header information. A record looks like that: + # "%d %s=%s\n" % (length, keyword, value). length is the size + # of the complete record including the length field itself and + # the newline. keyword and value are both UTF-8 encoded strings. + regex = re.compile(br"(\d+) ([^=]+)=") + pos = 0 + while True: + match = regex.match(buf, pos) + if not match: + break + + length, keyword = match.groups() + length = int(length) + value = buf[match.end(2) + 1:match.start(1) + length - 1] + + # Normally, we could just use "utf8" as the encoding and "strict" + # as the error handler, but we better not take the risk. For + # example, GNU tar <= 1.23 is known to store filenames it cannot + # translate to UTF-8 as raw strings (unfortunately without a + # hdrcharset=BINARY header). + # We first try the strict standard encoding, and if that fails we + # fall back on the user's encoding and error handler. + keyword = self._decode_pax_field(keyword, "utf8", "utf8", + tarfile.errors) + if keyword in PAX_NAME_FIELDS: + value = self._decode_pax_field(value, encoding, tarfile.encoding, + tarfile.errors) + else: + value = self._decode_pax_field(value, "utf8", "utf8", + tarfile.errors) + + pax_headers[keyword] = value + pos += length + + # Fetch the next header. + try: + next = self.fromtarfile(tarfile) + except HeaderError: + raise SubsequentHeaderError("missing or bad subsequent header") + + # Process GNU sparse information. + if "GNU.sparse.map" in pax_headers: + # GNU extended sparse format version 0.1. + self._proc_gnusparse_01(next, pax_headers) + + elif "GNU.sparse.size" in pax_headers: + # GNU extended sparse format version 0.0. + self._proc_gnusparse_00(next, pax_headers, buf) + + elif pax_headers.get("GNU.sparse.major") == "1" and pax_headers.get("GNU.sparse.minor") == "0": + # GNU extended sparse format version 1.0. + self._proc_gnusparse_10(next, pax_headers, tarfile) + + if self.type in (XHDTYPE, SOLARIS_XHDTYPE): + # Patch the TarInfo object with the extended header info. + next._apply_pax_info(pax_headers, tarfile.encoding, tarfile.errors) + next.offset = self.offset + + if "size" in pax_headers: + # If the extended header replaces the size field, + # we need to recalculate the offset where the next + # header starts. + offset = next.offset_data + if next.isreg() or next.type not in SUPPORTED_TYPES: + offset += next._block(next.size) + tarfile.offset = offset + + return next + + def _proc_gnusparse_00(self, next, pax_headers, buf): + """Process a GNU tar extended sparse header, version 0.0. + """ + offsets = [] + for match in re.finditer(br"\d+ GNU.sparse.offset=(\d+)\n", buf): + offsets.append(int(match.group(1))) + numbytes = [] + for match in re.finditer(br"\d+ GNU.sparse.numbytes=(\d+)\n", buf): + numbytes.append(int(match.group(1))) + next.sparse = list(zip(offsets, numbytes)) + + def _proc_gnusparse_01(self, next, pax_headers): + """Process a GNU tar extended sparse header, version 0.1. + """ + sparse = [int(x) for x in pax_headers["GNU.sparse.map"].split(",")] + next.sparse = list(zip(sparse[::2], sparse[1::2])) + + def _proc_gnusparse_10(self, next, pax_headers, tarfile): + """Process a GNU tar extended sparse header, version 1.0. + """ + fields = None + sparse = [] + buf = tarfile.fileobj.read(BLOCKSIZE) + fields, buf = buf.split(b"\n", 1) + fields = int(fields) + while len(sparse) < fields * 2: + if b"\n" not in buf: + buf += tarfile.fileobj.read(BLOCKSIZE) + number, buf = buf.split(b"\n", 1) + sparse.append(int(number)) + next.offset_data = tarfile.fileobj.tell() + next.sparse = list(zip(sparse[::2], sparse[1::2])) + + def _apply_pax_info(self, pax_headers, encoding, errors): + """Replace fields with supplemental information from a previous + pax extended or global header. + """ + for keyword, value in pax_headers.items(): + if keyword == "GNU.sparse.name": + setattr(self, "path", value) + elif keyword == "GNU.sparse.size": + setattr(self, "size", int(value)) + elif keyword == "GNU.sparse.realsize": + setattr(self, "size", int(value)) + elif keyword in PAX_FIELDS: + if keyword in PAX_NUMBER_FIELDS: + try: + value = PAX_NUMBER_FIELDS[keyword](value) + except ValueError: + value = 0 + if keyword == "path": + value = value.rstrip("/") + setattr(self, keyword, value) + + self.pax_headers = pax_headers.copy() + + def _decode_pax_field(self, value, encoding, fallback_encoding, fallback_errors): + """Decode a single field from a pax record. + """ + try: + return value.decode(encoding, "strict") + except UnicodeDecodeError: + return value.decode(fallback_encoding, fallback_errors) + + def _block(self, count): + """Round up a byte count by BLOCKSIZE and return it, + e.g. _block(834) => 1024. + """ + blocks, remainder = divmod(count, BLOCKSIZE) + if remainder: + blocks += 1 + return blocks * BLOCKSIZE + + def isreg(self): + return self.type in REGULAR_TYPES + def isfile(self): + return self.isreg() + def isdir(self): + return self.type == DIRTYPE + def issym(self): + return self.type == SYMTYPE + def islnk(self): + return self.type == LNKTYPE + def ischr(self): + return self.type == CHRTYPE + def isblk(self): + return self.type == BLKTYPE + def isfifo(self): + return self.type == FIFOTYPE + def issparse(self): + return self.sparse is not None + def isdev(self): + return self.type in (CHRTYPE, BLKTYPE, FIFOTYPE) +# class TarInfo + +class TarFile(object): + """The TarFile Class provides an interface to tar archives. + """ + + debug = 0 # May be set from 0 (no msgs) to 3 (all msgs) + + dereference = False # If true, add content of linked file to the + # tar file, else the link. + + ignore_zeros = False # If true, skips empty or invalid blocks and + # continues processing. + + errorlevel = 1 # If 0, fatal errors only appear in debug + # messages (if debug >= 0). If > 0, errors + # are passed to the caller as exceptions. + + format = DEFAULT_FORMAT # The format to use when creating an archive. + + encoding = ENCODING # Encoding for 8-bit character strings. + + errors = None # Error handler for unicode conversion. + + tarinfo = TarInfo # The default TarInfo class to use. + + fileobject = ExFileObject # The default ExFileObject class to use. + + def __init__(self, name=None, mode="r", fileobj=None, format=None, + tarinfo=None, dereference=None, ignore_zeros=None, encoding=None, + errors="surrogateescape", pax_headers=None, debug=None, errorlevel=None): + """Open an (uncompressed) tar archive `name'. `mode' is either 'r' to + read from an existing archive, 'a' to append data to an existing + file or 'w' to create a new file overwriting an existing one. `mode' + defaults to 'r'. + If `fileobj' is given, it is used for reading or writing data. If it + can be determined, `mode' is overridden by `fileobj's mode. + `fileobj' is not closed, when TarFile is closed. + """ + if len(mode) > 1 or mode not in "raw": + raise ValueError("mode must be 'r', 'a' or 'w'") + self.mode = mode + self._mode = {"r": "rb", "a": "r+b", "w": "wb"}[mode] + + if not fileobj: + if self.mode == "a" and not os.path.exists(name): + # Create nonexistent files in append mode. + self.mode = "w" + self._mode = "wb" + fileobj = bltn_open(name, self._mode) + self._extfileobj = False + else: + if name is None and hasattr(fileobj, "name"): + name = fileobj.name + if hasattr(fileobj, "mode"): + self._mode = fileobj.mode + self._extfileobj = True + self.name = os.path.abspath(name) if name else None + self.fileobj = fileobj + + # Init attributes. + if format is not None: + self.format = format + if tarinfo is not None: + self.tarinfo = tarinfo + if dereference is not None: + self.dereference = dereference + if ignore_zeros is not None: + self.ignore_zeros = ignore_zeros + if encoding is not None: + self.encoding = encoding + self.errors = errors + + if pax_headers is not None and self.format == PAX_FORMAT: + self.pax_headers = pax_headers + else: + self.pax_headers = {} + + if debug is not None: + self.debug = debug + if errorlevel is not None: + self.errorlevel = errorlevel + + # Init datastructures. + self.closed = False + self.members = [] # list of members as TarInfo objects + self._loaded = False # flag if all members have been read + self.offset = self.fileobj.tell() + # current position in the archive file + self.inodes = {} # dictionary caching the inodes of + # archive members already added + + try: + if self.mode == "r": + self.firstmember = None + self.firstmember = self.next() + + if self.mode == "a": + # Move to the end of the archive, + # before the first empty block. + while True: + self.fileobj.seek(self.offset) + try: + tarinfo = self.tarinfo.fromtarfile(self) + self.members.append(tarinfo) + except EOFHeaderError: + self.fileobj.seek(self.offset) + break + except HeaderError as e: + raise ReadError(str(e)) + + if self.mode in "aw": + self._loaded = True + + if self.pax_headers: + buf = self.tarinfo.create_pax_global_header(self.pax_headers.copy()) + self.fileobj.write(buf) + self.offset += len(buf) + except: + if not self._extfileobj: + self.fileobj.close() + self.closed = True + raise + + #-------------------------------------------------------------------------- + # Below are the classmethods which act as alternate constructors to the + # TarFile class. The open() method is the only one that is needed for + # public use; it is the "super"-constructor and is able to select an + # adequate "sub"-constructor for a particular compression using the mapping + # from OPEN_METH. + # + # This concept allows one to subclass TarFile without losing the comfort of + # the super-constructor. A sub-constructor is registered and made available + # by adding it to the mapping in OPEN_METH. + + @classmethod + def open(cls, name=None, mode="r", fileobj=None, bufsize=RECORDSIZE, **kwargs): + """Open a tar archive for reading, writing or appending. Return + an appropriate TarFile class. + + mode: + 'r' or 'r:*' open for reading with transparent compression + 'r:' open for reading exclusively uncompressed + 'r:gz' open for reading with gzip compression + 'r:bz2' open for reading with bzip2 compression + 'a' or 'a:' open for appending, creating the file if necessary + 'w' or 'w:' open for writing without compression + 'w:gz' open for writing with gzip compression + 'w:bz2' open for writing with bzip2 compression + + 'r|*' open a stream of tar blocks with transparent compression + 'r|' open an uncompressed stream of tar blocks for reading + 'r|gz' open a gzip compressed stream of tar blocks + 'r|bz2' open a bzip2 compressed stream of tar blocks + 'w|' open an uncompressed stream for writing + 'w|gz' open a gzip compressed stream for writing + 'w|bz2' open a bzip2 compressed stream for writing + """ + + if not name and not fileobj: + raise ValueError("nothing to open") + + if mode in ("r", "r:*"): + # Find out which *open() is appropriate for opening the file. + for comptype in cls.OPEN_METH: + func = getattr(cls, cls.OPEN_METH[comptype]) + if fileobj is not None: + saved_pos = fileobj.tell() + try: + return func(name, "r", fileobj, **kwargs) + except (ReadError, CompressionError) as e: + if fileobj is not None: + fileobj.seek(saved_pos) + continue + raise ReadError("file could not be opened successfully") + + elif ":" in mode: + filemode, comptype = mode.split(":", 1) + filemode = filemode or "r" + comptype = comptype or "tar" + + # Select the *open() function according to + # given compression. + if comptype in cls.OPEN_METH: + func = getattr(cls, cls.OPEN_METH[comptype]) + else: + raise CompressionError("unknown compression type %r" % comptype) + return func(name, filemode, fileobj, **kwargs) + + elif "|" in mode: + filemode, comptype = mode.split("|", 1) + filemode = filemode or "r" + comptype = comptype or "tar" + + if filemode not in "rw": + raise ValueError("mode must be 'r' or 'w'") + + stream = _Stream(name, filemode, comptype, fileobj, bufsize) + try: + t = cls(name, filemode, stream, **kwargs) + except: + stream.close() + raise + t._extfileobj = False + return t + + elif mode in "aw": + return cls.taropen(name, mode, fileobj, **kwargs) + + raise ValueError("undiscernible mode") + + @classmethod + def taropen(cls, name, mode="r", fileobj=None, **kwargs): + """Open uncompressed tar archive name for reading or writing. + """ + if len(mode) > 1 or mode not in "raw": + raise ValueError("mode must be 'r', 'a' or 'w'") + return cls(name, mode, fileobj, **kwargs) + + @classmethod + def gzopen(cls, name, mode="r", fileobj=None, compresslevel=9, **kwargs): + """Open gzip compressed tar archive name for reading or writing. + Appending is not allowed. + """ + if len(mode) > 1 or mode not in "rw": + raise ValueError("mode must be 'r' or 'w'") + + try: + import gzip + gzip.GzipFile + except (ImportError, AttributeError): + raise CompressionError("gzip module is not available") + + extfileobj = fileobj is not None + try: + fileobj = gzip.GzipFile(name, mode + "b", compresslevel, fileobj) + t = cls.taropen(name, mode, fileobj, **kwargs) + except IOError: + if not extfileobj and fileobj is not None: + fileobj.close() + if fileobj is None: + raise + raise ReadError("not a gzip file") + except: + if not extfileobj and fileobj is not None: + fileobj.close() + raise + t._extfileobj = extfileobj + return t + + @classmethod + def bz2open(cls, name, mode="r", fileobj=None, compresslevel=9, **kwargs): + """Open bzip2 compressed tar archive name for reading or writing. + Appending is not allowed. + """ + if len(mode) > 1 or mode not in "rw": + raise ValueError("mode must be 'r' or 'w'.") + + try: + import bz2 + except ImportError: + raise CompressionError("bz2 module is not available") + + if fileobj is not None: + fileobj = _BZ2Proxy(fileobj, mode) + else: + fileobj = bz2.BZ2File(name, mode, compresslevel=compresslevel) + + try: + t = cls.taropen(name, mode, fileobj, **kwargs) + except (IOError, EOFError): + fileobj.close() + raise ReadError("not a bzip2 file") + t._extfileobj = False + return t + + # All *open() methods are registered here. + OPEN_METH = { + "tar": "taropen", # uncompressed tar + "gz": "gzopen", # gzip compressed tar + "bz2": "bz2open" # bzip2 compressed tar + } + + #-------------------------------------------------------------------------- + # The public methods which TarFile provides: + + def close(self): + """Close the TarFile. In write-mode, two finishing zero blocks are + appended to the archive. + """ + if self.closed: + return + + if self.mode in "aw": + self.fileobj.write(NUL * (BLOCKSIZE * 2)) + self.offset += (BLOCKSIZE * 2) + # fill up the end with zero-blocks + # (like option -b20 for tar does) + blocks, remainder = divmod(self.offset, RECORDSIZE) + if remainder > 0: + self.fileobj.write(NUL * (RECORDSIZE - remainder)) + + if not self._extfileobj: + self.fileobj.close() + self.closed = True + + def getmember(self, name): + """Return a TarInfo object for member `name'. If `name' can not be + found in the archive, KeyError is raised. If a member occurs more + than once in the archive, its last occurrence is assumed to be the + most up-to-date version. + """ + tarinfo = self._getmember(name) + if tarinfo is None: + raise KeyError("filename %r not found" % name) + return tarinfo + + def getmembers(self): + """Return the members of the archive as a list of TarInfo objects. The + list has the same order as the members in the archive. + """ + self._check() + if not self._loaded: # if we want to obtain a list of + self._load() # all members, we first have to + # scan the whole archive. + return self.members + + def getnames(self): + """Return the members of the archive as a list of their names. It has + the same order as the list returned by getmembers(). + """ + return [tarinfo.name for tarinfo in self.getmembers()] + + def gettarinfo(self, name=None, arcname=None, fileobj=None): + """Create a TarInfo object for either the file `name' or the file + object `fileobj' (using os.fstat on its file descriptor). You can + modify some of the TarInfo's attributes before you add it using + addfile(). If given, `arcname' specifies an alternative name for the + file in the archive. + """ + self._check("aw") + + # When fileobj is given, replace name by + # fileobj's real name. + if fileobj is not None: + name = fileobj.name + + # Building the name of the member in the archive. + # Backward slashes are converted to forward slashes, + # Absolute paths are turned to relative paths. + if arcname is None: + arcname = name + drv, arcname = os.path.splitdrive(arcname) + arcname = arcname.replace(os.sep, "/") + arcname = arcname.lstrip("/") + + # Now, fill the TarInfo object with + # information specific for the file. + tarinfo = self.tarinfo() + tarinfo.tarfile = self + + # Use os.stat or os.lstat, depending on platform + # and if symlinks shall be resolved. + if fileobj is None: + if hasattr(os, "lstat") and not self.dereference: + statres = os.lstat(name) + else: + statres = os.stat(name) + else: + statres = os.fstat(fileobj.fileno()) + linkname = "" + + stmd = statres.st_mode + if stat.S_ISREG(stmd): + inode = (statres.st_ino, statres.st_dev) + if not self.dereference and statres.st_nlink > 1 and \ + inode in self.inodes and arcname != self.inodes[inode]: + # Is it a hardlink to an already + # archived file? + type = LNKTYPE + linkname = self.inodes[inode] + else: + # The inode is added only if its valid. + # For win32 it is always 0. + type = REGTYPE + if inode[0]: + self.inodes[inode] = arcname + elif stat.S_ISDIR(stmd): + type = DIRTYPE + elif stat.S_ISFIFO(stmd): + type = FIFOTYPE + elif stat.S_ISLNK(stmd): + type = SYMTYPE + linkname = os.readlink(name) + elif stat.S_ISCHR(stmd): + type = CHRTYPE + elif stat.S_ISBLK(stmd): + type = BLKTYPE + else: + return None + + # Fill the TarInfo object with all + # information we can get. + tarinfo.name = arcname + tarinfo.mode = stmd + tarinfo.uid = statres.st_uid + tarinfo.gid = statres.st_gid + if type == REGTYPE: + tarinfo.size = statres.st_size + else: + tarinfo.size = 0 + tarinfo.mtime = statres.st_mtime + tarinfo.type = type + tarinfo.linkname = linkname + if pwd: + try: + tarinfo.uname = pwd.getpwuid(tarinfo.uid)[0] + except KeyError: + pass + if grp: + try: + tarinfo.gname = grp.getgrgid(tarinfo.gid)[0] + except KeyError: + pass + + if type in (CHRTYPE, BLKTYPE): + if hasattr(os, "major") and hasattr(os, "minor"): + tarinfo.devmajor = os.major(statres.st_rdev) + tarinfo.devminor = os.minor(statres.st_rdev) + return tarinfo + + def list(self, verbose=True): + """Print a table of contents to sys.stdout. If `verbose' is False, only + the names of the members are printed. If it is True, an `ls -l'-like + output is produced. + """ + self._check() + + for tarinfo in self: + if verbose: + print(filemode(tarinfo.mode), end=' ') + print("%s/%s" % (tarinfo.uname or tarinfo.uid, + tarinfo.gname or tarinfo.gid), end=' ') + if tarinfo.ischr() or tarinfo.isblk(): + print("%10s" % ("%d,%d" \ + % (tarinfo.devmajor, tarinfo.devminor)), end=' ') + else: + print("%10d" % tarinfo.size, end=' ') + print("%d-%02d-%02d %02d:%02d:%02d" \ + % time.localtime(tarinfo.mtime)[:6], end=' ') + + print(tarinfo.name + ("/" if tarinfo.isdir() else ""), end=' ') + + if verbose: + if tarinfo.issym(): + print("->", tarinfo.linkname, end=' ') + if tarinfo.islnk(): + print("link to", tarinfo.linkname, end=' ') + print() + + def add(self, name, arcname=None, recursive=True, exclude=None, filter=None): + """Add the file `name' to the archive. `name' may be any type of file + (directory, fifo, symbolic link, etc.). If given, `arcname' + specifies an alternative name for the file in the archive. + Directories are added recursively by default. This can be avoided by + setting `recursive' to False. `exclude' is a function that should + return True for each filename to be excluded. `filter' is a function + that expects a TarInfo object argument and returns the changed + TarInfo object, if it returns None the TarInfo object will be + excluded from the archive. + """ + self._check("aw") + + if arcname is None: + arcname = name + + # Exclude pathnames. + if exclude is not None: + import warnings + warnings.warn("use the filter argument instead", + DeprecationWarning, 2) + if exclude(name): + self._dbg(2, "tarfile: Excluded %r" % name) + return + + # Skip if somebody tries to archive the archive... + if self.name is not None and os.path.abspath(name) == self.name: + self._dbg(2, "tarfile: Skipped %r" % name) + return + + self._dbg(1, name) + + # Create a TarInfo object from the file. + tarinfo = self.gettarinfo(name, arcname) + + if tarinfo is None: + self._dbg(1, "tarfile: Unsupported type %r" % name) + return + + # Change or exclude the TarInfo object. + if filter is not None: + tarinfo = filter(tarinfo) + if tarinfo is None: + self._dbg(2, "tarfile: Excluded %r" % name) + return + + # Append the tar header and data to the archive. + if tarinfo.isreg(): + f = bltn_open(name, "rb") + self.addfile(tarinfo, f) + f.close() + + elif tarinfo.isdir(): + self.addfile(tarinfo) + if recursive: + for f in os.listdir(name): + self.add(os.path.join(name, f), os.path.join(arcname, f), + recursive, exclude, filter=filter) + + else: + self.addfile(tarinfo) + + def addfile(self, tarinfo, fileobj=None): + """Add the TarInfo object `tarinfo' to the archive. If `fileobj' is + given, tarinfo.size bytes are read from it and added to the archive. + You can create TarInfo objects using gettarinfo(). + On Windows platforms, `fileobj' should always be opened with mode + 'rb' to avoid irritation about the file size. + """ + self._check("aw") + + tarinfo = copy.copy(tarinfo) + + buf = tarinfo.tobuf(self.format, self.encoding, self.errors) + self.fileobj.write(buf) + self.offset += len(buf) + + # If there's data to follow, append it. + if fileobj is not None: + copyfileobj(fileobj, self.fileobj, tarinfo.size) + blocks, remainder = divmod(tarinfo.size, BLOCKSIZE) + if remainder > 0: + self.fileobj.write(NUL * (BLOCKSIZE - remainder)) + blocks += 1 + self.offset += blocks * BLOCKSIZE + + self.members.append(tarinfo) + + def extractall(self, path=".", members=None): + """Extract all members from the archive to the current working + directory and set owner, modification time and permissions on + directories afterwards. `path' specifies a different directory + to extract to. `members' is optional and must be a subset of the + list returned by getmembers(). + """ + directories = [] + + if members is None: + members = self + + for tarinfo in members: + if tarinfo.isdir(): + # Extract directories with a safe mode. + directories.append(tarinfo) + tarinfo = copy.copy(tarinfo) + tarinfo.mode = 0o700 + # Do not set_attrs directories, as we will do that further down + self.extract(tarinfo, path, set_attrs=not tarinfo.isdir()) + + # Reverse sort directories. + directories.sort(key=lambda a: a.name) + directories.reverse() + + # Set correct owner, mtime and filemode on directories. + for tarinfo in directories: + dirpath = os.path.join(path, tarinfo.name) + try: + self.chown(tarinfo, dirpath) + self.utime(tarinfo, dirpath) + self.chmod(tarinfo, dirpath) + except ExtractError as e: + if self.errorlevel > 1: + raise + else: + self._dbg(1, "tarfile: %s" % e) + + def extract(self, member, path="", set_attrs=True): + """Extract a member from the archive to the current working directory, + using its full name. Its file information is extracted as accurately + as possible. `member' may be a filename or a TarInfo object. You can + specify a different directory using `path'. File attributes (owner, + mtime, mode) are set unless `set_attrs' is False. + """ + self._check("r") + + if isinstance(member, str): + tarinfo = self.getmember(member) + else: + tarinfo = member + + # Prepare the link target for makelink(). + if tarinfo.islnk(): + tarinfo._link_target = os.path.join(path, tarinfo.linkname) + + try: + self._extract_member(tarinfo, os.path.join(path, tarinfo.name), + set_attrs=set_attrs) + except EnvironmentError as e: + if self.errorlevel > 0: + raise + else: + if e.filename is None: + self._dbg(1, "tarfile: %s" % e.strerror) + else: + self._dbg(1, "tarfile: %s %r" % (e.strerror, e.filename)) + except ExtractError as e: + if self.errorlevel > 1: + raise + else: + self._dbg(1, "tarfile: %s" % e) + + def extractfile(self, member): + """Extract a member from the archive as a file object. `member' may be + a filename or a TarInfo object. If `member' is a regular file, a + file-like object is returned. If `member' is a link, a file-like + object is constructed from the link's target. If `member' is none of + the above, None is returned. + The file-like object is read-only and provides the following + methods: read(), readline(), readlines(), seek() and tell() + """ + self._check("r") + + if isinstance(member, str): + tarinfo = self.getmember(member) + else: + tarinfo = member + + if tarinfo.isreg(): + return self.fileobject(self, tarinfo) + + elif tarinfo.type not in SUPPORTED_TYPES: + # If a member's type is unknown, it is treated as a + # regular file. + return self.fileobject(self, tarinfo) + + elif tarinfo.islnk() or tarinfo.issym(): + if isinstance(self.fileobj, _Stream): + # A small but ugly workaround for the case that someone tries + # to extract a (sym)link as a file-object from a non-seekable + # stream of tar blocks. + raise StreamError("cannot extract (sym)link as file object") + else: + # A (sym)link's file object is its target's file object. + return self.extractfile(self._find_link_target(tarinfo)) + else: + # If there's no data associated with the member (directory, chrdev, + # blkdev, etc.), return None instead of a file object. + return None + + def _extract_member(self, tarinfo, targetpath, set_attrs=True): + """Extract the TarInfo object tarinfo to a physical + file called targetpath. + """ + # Fetch the TarInfo object for the given name + # and build the destination pathname, replacing + # forward slashes to platform specific separators. + targetpath = targetpath.rstrip("/") + targetpath = targetpath.replace("/", os.sep) + + # Create all upper directories. + upperdirs = os.path.dirname(targetpath) + if upperdirs and not os.path.exists(upperdirs): + # Create directories that are not part of the archive with + # default permissions. + os.makedirs(upperdirs) + + if tarinfo.islnk() or tarinfo.issym(): + self._dbg(1, "%s -> %s" % (tarinfo.name, tarinfo.linkname)) + else: + self._dbg(1, tarinfo.name) + + if tarinfo.isreg(): + self.makefile(tarinfo, targetpath) + elif tarinfo.isdir(): + self.makedir(tarinfo, targetpath) + elif tarinfo.isfifo(): + self.makefifo(tarinfo, targetpath) + elif tarinfo.ischr() or tarinfo.isblk(): + self.makedev(tarinfo, targetpath) + elif tarinfo.islnk() or tarinfo.issym(): + self.makelink(tarinfo, targetpath) + elif tarinfo.type not in SUPPORTED_TYPES: + self.makeunknown(tarinfo, targetpath) + else: + self.makefile(tarinfo, targetpath) + + if set_attrs: + self.chown(tarinfo, targetpath) + if not tarinfo.issym(): + self.chmod(tarinfo, targetpath) + self.utime(tarinfo, targetpath) + + #-------------------------------------------------------------------------- + # Below are the different file methods. They are called via + # _extract_member() when extract() is called. They can be replaced in a + # subclass to implement other functionality. + + def makedir(self, tarinfo, targetpath): + """Make a directory called targetpath. + """ + try: + # Use a safe mode for the directory, the real mode is set + # later in _extract_member(). + os.mkdir(targetpath, 0o700) + except EnvironmentError as e: + if e.errno != errno.EEXIST: + raise + + def makefile(self, tarinfo, targetpath): + """Make a file called targetpath. + """ + source = self.fileobj + source.seek(tarinfo.offset_data) + target = bltn_open(targetpath, "wb") + if tarinfo.sparse is not None: + for offset, size in tarinfo.sparse: + target.seek(offset) + copyfileobj(source, target, size) + else: + copyfileobj(source, target, tarinfo.size) + target.seek(tarinfo.size) + target.truncate() + target.close() + + def makeunknown(self, tarinfo, targetpath): + """Make a file from a TarInfo object with an unknown type + at targetpath. + """ + self.makefile(tarinfo, targetpath) + self._dbg(1, "tarfile: Unknown file type %r, " \ + "extracted as regular file." % tarinfo.type) + + def makefifo(self, tarinfo, targetpath): + """Make a fifo called targetpath. + """ + if hasattr(os, "mkfifo"): + os.mkfifo(targetpath) + else: + raise ExtractError("fifo not supported by system") + + def makedev(self, tarinfo, targetpath): + """Make a character or block device called targetpath. + """ + if not hasattr(os, "mknod") or not hasattr(os, "makedev"): + raise ExtractError("special devices not supported by system") + + mode = tarinfo.mode + if tarinfo.isblk(): + mode |= stat.S_IFBLK + else: + mode |= stat.S_IFCHR + + os.mknod(targetpath, mode, + os.makedev(tarinfo.devmajor, tarinfo.devminor)) + + def makelink(self, tarinfo, targetpath): + """Make a (symbolic) link called targetpath. If it cannot be created + (platform limitation), we try to make a copy of the referenced file + instead of a link. + """ + try: + # For systems that support symbolic and hard links. + if tarinfo.issym(): + os.symlink(tarinfo.linkname, targetpath) + else: + # See extract(). + if os.path.exists(tarinfo._link_target): + os.link(tarinfo._link_target, targetpath) + else: + self._extract_member(self._find_link_target(tarinfo), + targetpath) + except symlink_exception: + if tarinfo.issym(): + linkpath = os.path.join(os.path.dirname(tarinfo.name), + tarinfo.linkname) + else: + linkpath = tarinfo.linkname + else: + try: + self._extract_member(self._find_link_target(tarinfo), + targetpath) + except KeyError: + raise ExtractError("unable to resolve link inside archive") + + def chown(self, tarinfo, targetpath): + """Set owner of targetpath according to tarinfo. + """ + if pwd and hasattr(os, "geteuid") and os.geteuid() == 0: + # We have to be root to do so. + try: + g = grp.getgrnam(tarinfo.gname)[2] + except KeyError: + g = tarinfo.gid + try: + u = pwd.getpwnam(tarinfo.uname)[2] + except KeyError: + u = tarinfo.uid + try: + if tarinfo.issym() and hasattr(os, "lchown"): + os.lchown(targetpath, u, g) + else: + if sys.platform != "os2emx": + os.chown(targetpath, u, g) + except EnvironmentError as e: + raise ExtractError("could not change owner") + + def chmod(self, tarinfo, targetpath): + """Set file permissions of targetpath according to tarinfo. + """ + if hasattr(os, 'chmod'): + try: + os.chmod(targetpath, tarinfo.mode) + except EnvironmentError as e: + raise ExtractError("could not change mode") + + def utime(self, tarinfo, targetpath): + """Set modification time of targetpath according to tarinfo. + """ + if not hasattr(os, 'utime'): + return + try: + os.utime(targetpath, (tarinfo.mtime, tarinfo.mtime)) + except EnvironmentError as e: + raise ExtractError("could not change modification time") + + #-------------------------------------------------------------------------- + def next(self): + """Return the next member of the archive as a TarInfo object, when + TarFile is opened for reading. Return None if there is no more + available. + """ + self._check("ra") + if self.firstmember is not None: + m = self.firstmember + self.firstmember = None + return m + + # Read the next block. + self.fileobj.seek(self.offset) + tarinfo = None + while True: + try: + tarinfo = self.tarinfo.fromtarfile(self) + except EOFHeaderError as e: + if self.ignore_zeros: + self._dbg(2, "0x%X: %s" % (self.offset, e)) + self.offset += BLOCKSIZE + continue + except InvalidHeaderError as e: + if self.ignore_zeros: + self._dbg(2, "0x%X: %s" % (self.offset, e)) + self.offset += BLOCKSIZE + continue + elif self.offset == 0: + raise ReadError(str(e)) + except EmptyHeaderError: + if self.offset == 0: + raise ReadError("empty file") + except TruncatedHeaderError as e: + if self.offset == 0: + raise ReadError(str(e)) + except SubsequentHeaderError as e: + raise ReadError(str(e)) + break + + if tarinfo is not None: + self.members.append(tarinfo) + else: + self._loaded = True + + return tarinfo + + #-------------------------------------------------------------------------- + # Little helper methods: + + def _getmember(self, name, tarinfo=None, normalize=False): + """Find an archive member by name from bottom to top. + If tarinfo is given, it is used as the starting point. + """ + # Ensure that all members have been loaded. + members = self.getmembers() + + # Limit the member search list up to tarinfo. + if tarinfo is not None: + members = members[:members.index(tarinfo)] + + if normalize: + name = os.path.normpath(name) + + for member in reversed(members): + if normalize: + member_name = os.path.normpath(member.name) + else: + member_name = member.name + + if name == member_name: + return member + + def _load(self): + """Read through the entire archive file and look for readable + members. + """ + while True: + tarinfo = self.next() + if tarinfo is None: + break + self._loaded = True + + def _check(self, mode=None): + """Check if TarFile is still open, and if the operation's mode + corresponds to TarFile's mode. + """ + if self.closed: + raise IOError("%s is closed" % self.__class__.__name__) + if mode is not None and self.mode not in mode: + raise IOError("bad operation for mode %r" % self.mode) + + def _find_link_target(self, tarinfo): + """Find the target member of a symlink or hardlink member in the + archive. + """ + if tarinfo.issym(): + # Always search the entire archive. + linkname = os.path.dirname(tarinfo.name) + "/" + tarinfo.linkname + limit = None + else: + # Search the archive before the link, because a hard link is + # just a reference to an already archived file. + linkname = tarinfo.linkname + limit = tarinfo + + member = self._getmember(linkname, tarinfo=limit, normalize=True) + if member is None: + raise KeyError("linkname %r not found" % linkname) + return member + + def __iter__(self): + """Provide an iterator object. + """ + if self._loaded: + return iter(self.members) + else: + return TarIter(self) + + def _dbg(self, level, msg): + """Write debugging output to sys.stderr. + """ + if level <= self.debug: + print(msg, file=sys.stderr) + + def __enter__(self): + self._check() + return self + + def __exit__(self, type, value, traceback): + if type is None: + self.close() + else: + # An exception occurred. We must not call close() because + # it would try to write end-of-archive blocks and padding. + if not self._extfileobj: + self.fileobj.close() + self.closed = True +# class TarFile + +class TarIter(object): + """Iterator Class. + + for tarinfo in TarFile(...): + suite... + """ + + def __init__(self, tarfile): + """Construct a TarIter object. + """ + self.tarfile = tarfile + self.index = 0 + def __iter__(self): + """Return iterator object. + """ + return self + + def __next__(self): + """Return the next item using TarFile's next() method. + When all members have been read, set TarFile as _loaded. + """ + # Fix for SF #1100429: Under rare circumstances it can + # happen that getmembers() is called during iteration, + # which will cause TarIter to stop prematurely. + if not self.tarfile._loaded: + tarinfo = self.tarfile.next() + if not tarinfo: + self.tarfile._loaded = True + raise StopIteration + else: + try: + tarinfo = self.tarfile.members[self.index] + except IndexError: + raise StopIteration + self.index += 1 + return tarinfo + + next = __next__ # for Python 2.x + +#-------------------- +# exported functions +#-------------------- +def is_tarfile(name): + """Return True if name points to a tar archive that we + are able to handle, else return False. + """ + try: + t = open(name) + t.close() + return True + except TarError: + return False + +bltn_open = open +open = TarFile.open diff --git a/venv2/lib/python2.7/site-packages/pip/_vendor/distlib/compat.py b/venv2/lib/python2.7/site-packages/pip/_vendor/distlib/compat.py new file mode 100644 index 0000000..ff328c8 --- /dev/null +++ b/venv2/lib/python2.7/site-packages/pip/_vendor/distlib/compat.py @@ -0,0 +1,1120 @@ +# -*- coding: utf-8 -*- +# +# Copyright (C) 2013-2017 Vinay Sajip. +# Licensed to the Python Software Foundation under a contributor agreement. +# See LICENSE.txt and CONTRIBUTORS.txt. +# +from __future__ import absolute_import + +import os +import re +import sys + +try: + import ssl +except ImportError: # pragma: no cover + ssl = None + +if sys.version_info[0] < 3: # pragma: no cover + from StringIO import StringIO + string_types = basestring, + text_type = unicode + from types import FileType as file_type + import __builtin__ as builtins + import ConfigParser as configparser + from ._backport import shutil + from urlparse import urlparse, urlunparse, urljoin, urlsplit, urlunsplit + from urllib import (urlretrieve, quote as _quote, unquote, url2pathname, + pathname2url, ContentTooShortError, splittype) + + def quote(s): + if isinstance(s, unicode): + s = s.encode('utf-8') + return _quote(s) + + import urllib2 + from urllib2 import (Request, urlopen, URLError, HTTPError, + HTTPBasicAuthHandler, HTTPPasswordMgr, + HTTPHandler, HTTPRedirectHandler, + build_opener) + if ssl: + from urllib2 import HTTPSHandler + import httplib + import xmlrpclib + import Queue as queue + from HTMLParser import HTMLParser + import htmlentitydefs + raw_input = raw_input + from itertools import ifilter as filter + from itertools import ifilterfalse as filterfalse + + _userprog = None + def splituser(host): + """splituser('user[:passwd]@host[:port]') --> 'user[:passwd]', 'host[:port]'.""" + global _userprog + if _userprog is None: + import re + _userprog = re.compile('^(.*)@(.*)$') + + match = _userprog.match(host) + if match: return match.group(1, 2) + return None, host + +else: # pragma: no cover + from io import StringIO + string_types = str, + text_type = str + from io import TextIOWrapper as file_type + import builtins + import configparser + import shutil + from urllib.parse import (urlparse, urlunparse, urljoin, splituser, quote, + unquote, urlsplit, urlunsplit, splittype) + from urllib.request import (urlopen, urlretrieve, Request, url2pathname, + pathname2url, + HTTPBasicAuthHandler, HTTPPasswordMgr, + HTTPHandler, HTTPRedirectHandler, + build_opener) + if ssl: + from urllib.request import HTTPSHandler + from urllib.error import HTTPError, URLError, ContentTooShortError + import http.client as httplib + import urllib.request as urllib2 + import xmlrpc.client as xmlrpclib + import queue + from html.parser import HTMLParser + import html.entities as htmlentitydefs + raw_input = input + from itertools import filterfalse + filter = filter + +try: + from ssl import match_hostname, CertificateError +except ImportError: # pragma: no cover + class CertificateError(ValueError): + pass + + + 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 + + parts = dn.split('.') + leftmost, remainder = parts[0], 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, match_hostname needs a " + "SSL socket or SSL context with either " + "CERT_OPTIONAL or CERT_REQUIRED") + 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") + + +try: + from types import SimpleNamespace as Container +except ImportError: # pragma: no cover + class Container(object): + """ + A generic container for when multiple values need to be returned + """ + def __init__(self, **kwargs): + self.__dict__.update(kwargs) + + +try: + from shutil import which +except ImportError: # pragma: no cover + # Implementation from Python 3.3 + def which(cmd, mode=os.F_OK | os.X_OK, path=None): + """Given a command, mode, and a PATH string, return the path which + conforms to the given mode on the PATH, or None if there is no such + file. + + `mode` defaults to os.F_OK | os.X_OK. `path` defaults to the result + of os.environ.get("PATH"), or can be overridden with a custom search + path. + + """ + # Check that a given file can be accessed with the correct mode. + # Additionally check that `file` is not a directory, as on Windows + # directories pass the os.access check. + def _access_check(fn, mode): + return (os.path.exists(fn) and os.access(fn, mode) + and not os.path.isdir(fn)) + + # If we're given a path with a directory part, look it up directly rather + # than referring to PATH directories. This includes checking relative to the + # current directory, e.g. ./script + if os.path.dirname(cmd): + if _access_check(cmd, mode): + return cmd + return None + + if path is None: + path = os.environ.get("PATH", os.defpath) + if not path: + return None + path = path.split(os.pathsep) + + if sys.platform == "win32": + # The current directory takes precedence on Windows. + if not os.curdir in path: + path.insert(0, os.curdir) + + # PATHEXT is necessary to check on Windows. + pathext = os.environ.get("PATHEXT", "").split(os.pathsep) + # See if the given file matches any of the expected path extensions. + # This will allow us to short circuit when given "python.exe". + # If it does match, only test that one, otherwise we have to try + # others. + if any(cmd.lower().endswith(ext.lower()) for ext in pathext): + files = [cmd] + else: + files = [cmd + ext for ext in pathext] + else: + # On other platforms you don't have things like PATHEXT to tell you + # what file suffixes are executable, so just pass on cmd as-is. + files = [cmd] + + seen = set() + for dir in path: + normdir = os.path.normcase(dir) + if not normdir in seen: + seen.add(normdir) + for thefile in files: + name = os.path.join(dir, thefile) + if _access_check(name, mode): + return name + return None + + +# ZipFile is a context manager in 2.7, but not in 2.6 + +from zipfile import ZipFile as BaseZipFile + +if hasattr(BaseZipFile, '__enter__'): # pragma: no cover + ZipFile = BaseZipFile +else: # pragma: no cover + from zipfile import ZipExtFile as BaseZipExtFile + + class ZipExtFile(BaseZipExtFile): + def __init__(self, base): + self.__dict__.update(base.__dict__) + + def __enter__(self): + return self + + def __exit__(self, *exc_info): + self.close() + # return None, so if an exception occurred, it will propagate + + class ZipFile(BaseZipFile): + def __enter__(self): + return self + + def __exit__(self, *exc_info): + self.close() + # return None, so if an exception occurred, it will propagate + + def open(self, *args, **kwargs): + base = BaseZipFile.open(self, *args, **kwargs) + return ZipExtFile(base) + +try: + from platform import python_implementation +except ImportError: # pragma: no cover + def python_implementation(): + """Return a string identifying the Python implementation.""" + if 'PyPy' in sys.version: + return 'PyPy' + if os.name == 'java': + return 'Jython' + if sys.version.startswith('IronPython'): + return 'IronPython' + return 'CPython' + +try: + import sysconfig +except ImportError: # pragma: no cover + from ._backport import sysconfig + +try: + callable = callable +except NameError: # pragma: no cover + from collections import Callable + + def callable(obj): + return isinstance(obj, Callable) + + +try: + fsencode = os.fsencode + fsdecode = os.fsdecode +except AttributeError: # pragma: no cover + # Issue #99: on some systems (e.g. containerised), + # sys.getfilesystemencoding() returns None, and we need a real value, + # so fall back to utf-8. From the CPython 2.7 docs relating to Unix and + # sys.getfilesystemencoding(): the return value is "the user’s preference + # according to the result of nl_langinfo(CODESET), or None if the + # nl_langinfo(CODESET) failed." + _fsencoding = sys.getfilesystemencoding() or 'utf-8' + if _fsencoding == 'mbcs': + _fserrors = 'strict' + else: + _fserrors = 'surrogateescape' + + def fsencode(filename): + if isinstance(filename, bytes): + return filename + elif isinstance(filename, text_type): + return filename.encode(_fsencoding, _fserrors) + else: + raise TypeError("expect bytes or str, not %s" % + type(filename).__name__) + + def fsdecode(filename): + if isinstance(filename, text_type): + return filename + elif isinstance(filename, bytes): + return filename.decode(_fsencoding, _fserrors) + else: + raise TypeError("expect bytes or str, not %s" % + type(filename).__name__) + +try: + from tokenize import detect_encoding +except ImportError: # pragma: no cover + from codecs import BOM_UTF8, lookup + import re + + cookie_re = re.compile(r"coding[:=]\s*([-\w.]+)") + + def _get_normal_name(orig_enc): + """Imitates get_normal_name in tokenizer.c.""" + # Only care about the first 12 characters. + enc = orig_enc[:12].lower().replace("_", "-") + if enc == "utf-8" or enc.startswith("utf-8-"): + return "utf-8" + if enc in ("latin-1", "iso-8859-1", "iso-latin-1") or \ + enc.startswith(("latin-1-", "iso-8859-1-", "iso-latin-1-")): + return "iso-8859-1" + return orig_enc + + def detect_encoding(readline): + """ + The detect_encoding() function is used to detect the encoding that should + be used to decode a Python source file. It requires one argument, readline, + in the same way as the tokenize() generator. + + It will call readline a maximum of twice, and return the encoding used + (as a string) and a list of any lines (left as bytes) it has read in. + + It detects the encoding from the presence of a utf-8 bom or an encoding + cookie as specified in pep-0263. If both a bom and a cookie are present, + but disagree, a SyntaxError will be raised. If the encoding cookie is an + invalid charset, raise a SyntaxError. Note that if a utf-8 bom is found, + 'utf-8-sig' is returned. + + If no encoding is specified, then the default of 'utf-8' will be returned. + """ + try: + filename = readline.__self__.name + except AttributeError: + filename = None + bom_found = False + encoding = None + default = 'utf-8' + def read_or_stop(): + try: + return readline() + except StopIteration: + return b'' + + def find_cookie(line): + try: + # Decode as UTF-8. Either the line is an encoding declaration, + # in which case it should be pure ASCII, or it must be UTF-8 + # per default encoding. + line_string = line.decode('utf-8') + except UnicodeDecodeError: + msg = "invalid or missing encoding declaration" + if filename is not None: + msg = '{} for {!r}'.format(msg, filename) + raise SyntaxError(msg) + + matches = cookie_re.findall(line_string) + if not matches: + return None + encoding = _get_normal_name(matches[0]) + try: + codec = lookup(encoding) + except LookupError: + # This behaviour mimics the Python interpreter + if filename is None: + msg = "unknown encoding: " + encoding + else: + msg = "unknown encoding for {!r}: {}".format(filename, + encoding) + raise SyntaxError(msg) + + if bom_found: + if codec.name != 'utf-8': + # This behaviour mimics the Python interpreter + if filename is None: + msg = 'encoding problem: utf-8' + else: + msg = 'encoding problem for {!r}: utf-8'.format(filename) + raise SyntaxError(msg) + encoding += '-sig' + return encoding + + first = read_or_stop() + if first.startswith(BOM_UTF8): + bom_found = True + first = first[3:] + default = 'utf-8-sig' + if not first: + return default, [] + + encoding = find_cookie(first) + if encoding: + return encoding, [first] + + second = read_or_stop() + if not second: + return default, [first] + + encoding = find_cookie(second) + if encoding: + return encoding, [first, second] + + return default, [first, second] + +# For converting & <-> & etc. +try: + from html import escape +except ImportError: + from cgi import escape +if sys.version_info[:2] < (3, 4): + unescape = HTMLParser().unescape +else: + from html import unescape + +try: + from collections import ChainMap +except ImportError: # pragma: no cover + from collections import MutableMapping + + try: + from reprlib import recursive_repr as _recursive_repr + except ImportError: + def _recursive_repr(fillvalue='...'): + ''' + Decorator to make a repr function return fillvalue for a recursive + call + ''' + + def decorating_function(user_function): + repr_running = set() + + def wrapper(self): + key = id(self), get_ident() + if key in repr_running: + return fillvalue + repr_running.add(key) + try: + result = user_function(self) + finally: + repr_running.discard(key) + return result + + # Can't use functools.wraps() here because of bootstrap issues + wrapper.__module__ = getattr(user_function, '__module__') + wrapper.__doc__ = getattr(user_function, '__doc__') + wrapper.__name__ = getattr(user_function, '__name__') + wrapper.__annotations__ = getattr(user_function, '__annotations__', {}) + return wrapper + + return decorating_function + + class ChainMap(MutableMapping): + ''' A ChainMap groups multiple dicts (or other mappings) together + to create a single, updateable view. + + The underlying mappings are stored in a list. That list is public and can + accessed or updated using the *maps* attribute. There is no other state. + + Lookups search the underlying mappings successively until a key is found. + In contrast, writes, updates, and deletions only operate on the first + mapping. + + ''' + + def __init__(self, *maps): + '''Initialize a ChainMap by setting *maps* to the given mappings. + If no mappings are provided, a single empty dictionary is used. + + ''' + self.maps = list(maps) or [{}] # always at least one map + + def __missing__(self, key): + raise KeyError(key) + + def __getitem__(self, key): + for mapping in self.maps: + try: + return mapping[key] # can't use 'key in mapping' with defaultdict + except KeyError: + pass + return self.__missing__(key) # support subclasses that define __missing__ + + def get(self, key, default=None): + return self[key] if key in self else default + + def __len__(self): + return len(set().union(*self.maps)) # reuses stored hash values if possible + + def __iter__(self): + return iter(set().union(*self.maps)) + + def __contains__(self, key): + return any(key in m for m in self.maps) + + def __bool__(self): + return any(self.maps) + + @_recursive_repr() + def __repr__(self): + return '{0.__class__.__name__}({1})'.format( + self, ', '.join(map(repr, self.maps))) + + @classmethod + def fromkeys(cls, iterable, *args): + 'Create a ChainMap with a single dict created from the iterable.' + return cls(dict.fromkeys(iterable, *args)) + + def copy(self): + 'New ChainMap or subclass with a new copy of maps[0] and refs to maps[1:]' + return self.__class__(self.maps[0].copy(), *self.maps[1:]) + + __copy__ = copy + + def new_child(self): # like Django's Context.push() + 'New ChainMap with a new dict followed by all previous maps.' + return self.__class__({}, *self.maps) + + @property + def parents(self): # like Django's Context.pop() + 'New ChainMap from maps[1:].' + return self.__class__(*self.maps[1:]) + + def __setitem__(self, key, value): + self.maps[0][key] = value + + def __delitem__(self, key): + try: + del self.maps[0][key] + except KeyError: + raise KeyError('Key not found in the first mapping: {!r}'.format(key)) + + def popitem(self): + 'Remove and return an item pair from maps[0]. Raise KeyError is maps[0] is empty.' + try: + return self.maps[0].popitem() + except KeyError: + raise KeyError('No keys found in the first mapping.') + + def pop(self, key, *args): + 'Remove *key* from maps[0] and return its value. Raise KeyError if *key* not in maps[0].' + try: + return self.maps[0].pop(key, *args) + except KeyError: + raise KeyError('Key not found in the first mapping: {!r}'.format(key)) + + def clear(self): + 'Clear maps[0], leaving maps[1:] intact.' + self.maps[0].clear() + +try: + from importlib.util import cache_from_source # Python >= 3.4 +except ImportError: # pragma: no cover + try: + from imp import cache_from_source + except ImportError: # pragma: no cover + def cache_from_source(path, debug_override=None): + assert path.endswith('.py') + if debug_override is None: + debug_override = __debug__ + if debug_override: + suffix = 'c' + else: + suffix = 'o' + return path + suffix + +try: + from collections import OrderedDict +except ImportError: # pragma: no cover +## {{{ http://code.activestate.com/recipes/576693/ (r9) +# Backport of OrderedDict() class that runs on Python 2.4, 2.5, 2.6, 2.7 and pypy. +# Passes Python2.7's test suite and incorporates all the latest updates. + try: + from thread import get_ident as _get_ident + except ImportError: + from dummy_thread import get_ident as _get_ident + + try: + from _abcoll import KeysView, ValuesView, ItemsView + except ImportError: + pass + + + class OrderedDict(dict): + 'Dictionary that remembers insertion order' + # An inherited dict maps keys to values. + # The inherited dict provides __getitem__, __len__, __contains__, and get. + # The remaining methods are order-aware. + # Big-O running times for all methods are the same as for regular dictionaries. + + # The internal self.__map dictionary maps keys to links in a doubly linked list. + # The circular doubly linked list starts and ends with a sentinel element. + # The sentinel element never gets deleted (this simplifies the algorithm). + # Each link is stored as a list of length three: [PREV, NEXT, KEY]. + + def __init__(self, *args, **kwds): + '''Initialize an ordered dictionary. Signature is the same as for + regular dictionaries, but keyword arguments are not recommended + because their insertion order is arbitrary. + + ''' + if len(args) > 1: + raise TypeError('expected at most 1 arguments, got %d' % len(args)) + try: + self.__root + except AttributeError: + self.__root = root = [] # sentinel node + root[:] = [root, root, None] + self.__map = {} + self.__update(*args, **kwds) + + def __setitem__(self, key, value, dict_setitem=dict.__setitem__): + 'od.__setitem__(i, y) <==> od[i]=y' + # Setting a new item creates a new link which goes at the end of the linked + # list, and the inherited dictionary is updated with the new key/value pair. + if key not in self: + root = self.__root + last = root[0] + last[1] = root[0] = self.__map[key] = [last, root, key] + dict_setitem(self, key, value) + + def __delitem__(self, key, dict_delitem=dict.__delitem__): + 'od.__delitem__(y) <==> del od[y]' + # Deleting an existing item uses self.__map to find the link which is + # then removed by updating the links in the predecessor and successor nodes. + dict_delitem(self, key) + link_prev, link_next, key = self.__map.pop(key) + link_prev[1] = link_next + link_next[0] = link_prev + + def __iter__(self): + 'od.__iter__() <==> iter(od)' + root = self.__root + curr = root[1] + while curr is not root: + yield curr[2] + curr = curr[1] + + def __reversed__(self): + 'od.__reversed__() <==> reversed(od)' + root = self.__root + curr = root[0] + while curr is not root: + yield curr[2] + curr = curr[0] + + def clear(self): + 'od.clear() -> None. Remove all items from od.' + try: + for node in self.__map.itervalues(): + del node[:] + root = self.__root + root[:] = [root, root, None] + self.__map.clear() + except AttributeError: + pass + dict.clear(self) + + def popitem(self, last=True): + '''od.popitem() -> (k, v), return and remove a (key, value) pair. + Pairs are returned in LIFO order if last is true or FIFO order if false. + + ''' + if not self: + raise KeyError('dictionary is empty') + root = self.__root + if last: + link = root[0] + link_prev = link[0] + link_prev[1] = root + root[0] = link_prev + else: + link = root[1] + link_next = link[1] + root[1] = link_next + link_next[0] = root + key = link[2] + del self.__map[key] + value = dict.pop(self, key) + return key, value + + # -- the following methods do not depend on the internal structure -- + + def keys(self): + 'od.keys() -> list of keys in od' + return list(self) + + def values(self): + 'od.values() -> list of values in od' + return [self[key] for key in self] + + def items(self): + 'od.items() -> list of (key, value) pairs in od' + return [(key, self[key]) for key in self] + + def iterkeys(self): + 'od.iterkeys() -> an iterator over the keys in od' + return iter(self) + + def itervalues(self): + 'od.itervalues -> an iterator over the values in od' + for k in self: + yield self[k] + + def iteritems(self): + 'od.iteritems -> an iterator over the (key, value) items in od' + for k in self: + yield (k, self[k]) + + def update(*args, **kwds): + '''od.update(E, **F) -> None. Update od from dict/iterable E and F. + + If E is a dict instance, does: for k in E: od[k] = E[k] + If E has a .keys() method, does: for k in E.keys(): od[k] = E[k] + Or if E is an iterable of items, does: for k, v in E: od[k] = v + In either case, this is followed by: for k, v in F.items(): od[k] = v + + ''' + if len(args) > 2: + raise TypeError('update() takes at most 2 positional ' + 'arguments (%d given)' % (len(args),)) + elif not args: + raise TypeError('update() takes at least 1 argument (0 given)') + self = args[0] + # Make progressively weaker assumptions about "other" + other = () + if len(args) == 2: + other = args[1] + if isinstance(other, dict): + for key in other: + self[key] = other[key] + elif hasattr(other, 'keys'): + for key in other.keys(): + self[key] = other[key] + else: + for key, value in other: + self[key] = value + for key, value in kwds.items(): + self[key] = value + + __update = update # let subclasses override update without breaking __init__ + + __marker = object() + + def pop(self, key, default=__marker): + '''od.pop(k[,d]) -> v, remove specified key and return the corresponding value. + If key is not found, d is returned if given, otherwise KeyError is raised. + + ''' + if key in self: + result = self[key] + del self[key] + return result + if default is self.__marker: + raise KeyError(key) + return default + + def setdefault(self, key, default=None): + 'od.setdefault(k[,d]) -> od.get(k,d), also set od[k]=d if k not in od' + if key in self: + return self[key] + self[key] = default + return default + + def __repr__(self, _repr_running=None): + 'od.__repr__() <==> repr(od)' + if not _repr_running: _repr_running = {} + call_key = id(self), _get_ident() + if call_key in _repr_running: + return '...' + _repr_running[call_key] = 1 + try: + if not self: + return '%s()' % (self.__class__.__name__,) + return '%s(%r)' % (self.__class__.__name__, self.items()) + finally: + del _repr_running[call_key] + + def __reduce__(self): + 'Return state information for pickling' + items = [[k, self[k]] for k in self] + inst_dict = vars(self).copy() + for k in vars(OrderedDict()): + inst_dict.pop(k, None) + if inst_dict: + return (self.__class__, (items,), inst_dict) + return self.__class__, (items,) + + def copy(self): + 'od.copy() -> a shallow copy of od' + return self.__class__(self) + + @classmethod + def fromkeys(cls, iterable, value=None): + '''OD.fromkeys(S[, v]) -> New ordered dictionary with keys from S + and values equal to v (which defaults to None). + + ''' + d = cls() + for key in iterable: + d[key] = value + return d + + def __eq__(self, other): + '''od.__eq__(y) <==> od==y. Comparison to another OD is order-sensitive + while comparison to a regular mapping is order-insensitive. + + ''' + if isinstance(other, OrderedDict): + return len(self)==len(other) and self.items() == other.items() + return dict.__eq__(self, other) + + def __ne__(self, other): + return not self == other + + # -- the following methods are only used in Python 2.7 -- + + def viewkeys(self): + "od.viewkeys() -> a set-like object providing a view on od's keys" + return KeysView(self) + + def viewvalues(self): + "od.viewvalues() -> an object providing a view on od's values" + return ValuesView(self) + + def viewitems(self): + "od.viewitems() -> a set-like object providing a view on od's items" + return ItemsView(self) + +try: + from logging.config import BaseConfigurator, valid_ident +except ImportError: # pragma: no cover + 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 + + + # 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<prefix>[a-z]+)://(?P<suffix>.*)$') + + 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 = staticmethod(__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, string_types): + 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 callable(c): + 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 diff --git a/venv2/lib/python2.7/site-packages/pip/_vendor/distlib/database.py b/venv2/lib/python2.7/site-packages/pip/_vendor/distlib/database.py new file mode 100644 index 0000000..b13cdac --- /dev/null +++ b/venv2/lib/python2.7/site-packages/pip/_vendor/distlib/database.py @@ -0,0 +1,1339 @@ +# -*- coding: utf-8 -*- +# +# Copyright (C) 2012-2017 The Python Software Foundation. +# See LICENSE.txt and CONTRIBUTORS.txt. +# +"""PEP 376 implementation.""" + +from __future__ import unicode_literals + +import base64 +import codecs +import contextlib +import hashlib +import logging +import os +import posixpath +import sys +import zipimport + +from . import DistlibException, resources +from .compat import StringIO +from .version import get_scheme, UnsupportedVersionError +from .metadata import (Metadata, METADATA_FILENAME, WHEEL_METADATA_FILENAME, + LEGACY_METADATA_FILENAME) +from .util import (parse_requirement, cached_property, parse_name_and_version, + read_exports, write_exports, CSVReader, CSVWriter) + + +__all__ = ['Distribution', 'BaseInstalledDistribution', + 'InstalledDistribution', 'EggInfoDistribution', + 'DistributionPath'] + + +logger = logging.getLogger(__name__) + +EXPORTS_FILENAME = 'pydist-exports.json' +COMMANDS_FILENAME = 'pydist-commands.json' + +DIST_FILES = ('INSTALLER', METADATA_FILENAME, 'RECORD', 'REQUESTED', + 'RESOURCES', EXPORTS_FILENAME, 'SHARED') + +DISTINFO_EXT = '.dist-info' + + +class _Cache(object): + """ + A simple cache mapping names and .dist-info paths to distributions + """ + def __init__(self): + """ + Initialise an instance. There is normally one for each DistributionPath. + """ + self.name = {} + self.path = {} + self.generated = False + + def clear(self): + """ + Clear the cache, setting it to its initial state. + """ + self.name.clear() + self.path.clear() + self.generated = False + + def add(self, dist): + """ + Add a distribution to the cache. + :param dist: The distribution to add. + """ + if dist.path not in self.path: + self.path[dist.path] = dist + self.name.setdefault(dist.key, []).append(dist) + + +class DistributionPath(object): + """ + Represents a set of distributions installed on a path (typically sys.path). + """ + def __init__(self, path=None, include_egg=False): + """ + Create an instance from a path, optionally including legacy (distutils/ + setuptools/distribute) distributions. + :param path: The path to use, as a list of directories. If not specified, + sys.path is used. + :param include_egg: If True, this instance will look for and return legacy + distributions as well as those based on PEP 376. + """ + if path is None: + path = sys.path + self.path = path + self._include_dist = True + self._include_egg = include_egg + + self._cache = _Cache() + self._cache_egg = _Cache() + self._cache_enabled = True + self._scheme = get_scheme('default') + + def _get_cache_enabled(self): + return self._cache_enabled + + def _set_cache_enabled(self, value): + self._cache_enabled = value + + cache_enabled = property(_get_cache_enabled, _set_cache_enabled) + + def clear_cache(self): + """ + Clears the internal cache. + """ + self._cache.clear() + self._cache_egg.clear() + + + def _yield_distributions(self): + """ + Yield .dist-info and/or .egg(-info) distributions. + """ + # We need to check if we've seen some resources already, because on + # some Linux systems (e.g. some Debian/Ubuntu variants) there are + # symlinks which alias other files in the environment. + seen = set() + for path in self.path: + finder = resources.finder_for_path(path) + if finder is None: + continue + r = finder.find('') + if not r or not r.is_container: + continue + rset = sorted(r.resources) + for entry in rset: + r = finder.find(entry) + if not r or r.path in seen: + continue + if self._include_dist and entry.endswith(DISTINFO_EXT): + possible_filenames = [METADATA_FILENAME, + WHEEL_METADATA_FILENAME, + LEGACY_METADATA_FILENAME] + for metadata_filename in possible_filenames: + metadata_path = posixpath.join(entry, metadata_filename) + pydist = finder.find(metadata_path) + if pydist: + break + else: + continue + + with contextlib.closing(pydist.as_stream()) as stream: + metadata = Metadata(fileobj=stream, scheme='legacy') + logger.debug('Found %s', r.path) + seen.add(r.path) + yield new_dist_class(r.path, metadata=metadata, + env=self) + elif self._include_egg and entry.endswith(('.egg-info', + '.egg')): + logger.debug('Found %s', r.path) + seen.add(r.path) + yield old_dist_class(r.path, self) + + def _generate_cache(self): + """ + Scan the path for distributions and populate the cache with + those that are found. + """ + gen_dist = not self._cache.generated + gen_egg = self._include_egg and not self._cache_egg.generated + if gen_dist or gen_egg: + for dist in self._yield_distributions(): + if isinstance(dist, InstalledDistribution): + self._cache.add(dist) + else: + self._cache_egg.add(dist) + + if gen_dist: + self._cache.generated = True + if gen_egg: + self._cache_egg.generated = True + + @classmethod + def distinfo_dirname(cls, name, version): + """ + The *name* and *version* parameters are converted into their + filename-escaped form, i.e. any ``'-'`` characters are replaced + with ``'_'`` other than the one in ``'dist-info'`` and the one + separating the name from the version number. + + :parameter name: is converted to a standard distribution name by replacing + any runs of non- alphanumeric characters with a single + ``'-'``. + :type name: string + :parameter version: is converted to a standard version string. Spaces + become dots, and all other non-alphanumeric characters + (except dots) become dashes, with runs of multiple + dashes condensed to a single dash. + :type version: string + :returns: directory name + :rtype: string""" + name = name.replace('-', '_') + return '-'.join([name, version]) + DISTINFO_EXT + + def get_distributions(self): + """ + Provides an iterator that looks for distributions and returns + :class:`InstalledDistribution` or + :class:`EggInfoDistribution` instances for each one of them. + + :rtype: iterator of :class:`InstalledDistribution` and + :class:`EggInfoDistribution` instances + """ + if not self._cache_enabled: + for dist in self._yield_distributions(): + yield dist + else: + self._generate_cache() + + for dist in self._cache.path.values(): + yield dist + + if self._include_egg: + for dist in self._cache_egg.path.values(): + yield dist + + def get_distribution(self, name): + """ + Looks for a named distribution on the path. + + This function only returns the first result found, as no more than one + value is expected. If nothing is found, ``None`` is returned. + + :rtype: :class:`InstalledDistribution`, :class:`EggInfoDistribution` + or ``None`` + """ + result = None + name = name.lower() + if not self._cache_enabled: + for dist in self._yield_distributions(): + if dist.key == name: + result = dist + break + else: + self._generate_cache() + + if name in self._cache.name: + result = self._cache.name[name][0] + elif self._include_egg and name in self._cache_egg.name: + result = self._cache_egg.name[name][0] + return result + + def provides_distribution(self, name, version=None): + """ + Iterates over all distributions to find which distributions provide *name*. + If a *version* is provided, it will be used to filter the results. + + This function only returns the first result found, since no more than + one values are expected. If the directory is not found, returns ``None``. + + :parameter version: a version specifier that indicates the version + required, conforming to the format in ``PEP-345`` + + :type name: string + :type version: string + """ + matcher = None + if version is not None: + try: + matcher = self._scheme.matcher('%s (%s)' % (name, version)) + except ValueError: + raise DistlibException('invalid name or version: %r, %r' % + (name, version)) + + for dist in self.get_distributions(): + # We hit a problem on Travis where enum34 was installed and doesn't + # have a provides attribute ... + if not hasattr(dist, 'provides'): + logger.debug('No "provides": %s', dist) + else: + provided = dist.provides + + for p in provided: + p_name, p_ver = parse_name_and_version(p) + if matcher is None: + if p_name == name: + yield dist + break + else: + if p_name == name and matcher.match(p_ver): + yield dist + break + + def get_file_path(self, name, relative_path): + """ + Return the path to a resource file. + """ + dist = self.get_distribution(name) + if dist is None: + raise LookupError('no distribution named %r found' % name) + return dist.get_resource_path(relative_path) + + def get_exported_entries(self, category, name=None): + """ + Return all of the exported entries in a particular category. + + :param category: The category to search for entries. + :param name: If specified, only entries with that name are returned. + """ + for dist in self.get_distributions(): + r = dist.exports + if category in r: + d = r[category] + if name is not None: + if name in d: + yield d[name] + else: + for v in d.values(): + yield v + + +class Distribution(object): + """ + A base class for distributions, whether installed or from indexes. + Either way, it must have some metadata, so that's all that's needed + for construction. + """ + + build_time_dependency = False + """ + Set to True if it's known to be only a build-time dependency (i.e. + not needed after installation). + """ + + requested = False + """A boolean that indicates whether the ``REQUESTED`` metadata file is + present (in other words, whether the package was installed by user + request or it was installed as a dependency).""" + + def __init__(self, metadata): + """ + Initialise an instance. + :param metadata: The instance of :class:`Metadata` describing this + distribution. + """ + self.metadata = metadata + self.name = metadata.name + self.key = self.name.lower() # for case-insensitive comparisons + self.version = metadata.version + self.locator = None + self.digest = None + self.extras = None # additional features requested + self.context = None # environment marker overrides + self.download_urls = set() + self.digests = {} + + @property + def source_url(self): + """ + The source archive download URL for this distribution. + """ + return self.metadata.source_url + + download_url = source_url # Backward compatibility + + @property + def name_and_version(self): + """ + A utility property which displays the name and version in parentheses. + """ + return '%s (%s)' % (self.name, self.version) + + @property + def provides(self): + """ + A set of distribution names and versions provided by this distribution. + :return: A set of "name (version)" strings. + """ + plist = self.metadata.provides + s = '%s (%s)' % (self.name, self.version) + if s not in plist: + plist.append(s) + return plist + + def _get_requirements(self, req_attr): + md = self.metadata + logger.debug('Getting requirements from metadata %r', md.todict()) + reqts = getattr(md, req_attr) + return set(md.get_requirements(reqts, extras=self.extras, + env=self.context)) + + @property + def run_requires(self): + return self._get_requirements('run_requires') + + @property + def meta_requires(self): + return self._get_requirements('meta_requires') + + @property + def build_requires(self): + return self._get_requirements('build_requires') + + @property + def test_requires(self): + return self._get_requirements('test_requires') + + @property + def dev_requires(self): + return self._get_requirements('dev_requires') + + def matches_requirement(self, req): + """ + Say if this instance matches (fulfills) a requirement. + :param req: The requirement to match. + :rtype req: str + :return: True if it matches, else False. + """ + # Requirement may contain extras - parse to lose those + # from what's passed to the matcher + r = parse_requirement(req) + scheme = get_scheme(self.metadata.scheme) + try: + matcher = scheme.matcher(r.requirement) + except UnsupportedVersionError: + # XXX compat-mode if cannot read the version + logger.warning('could not read version %r - using name only', + req) + name = req.split()[0] + matcher = scheme.matcher(name) + + name = matcher.key # case-insensitive + + result = False + for p in self.provides: + p_name, p_ver = parse_name_and_version(p) + if p_name != name: + continue + try: + result = matcher.match(p_ver) + break + except UnsupportedVersionError: + pass + return result + + def __repr__(self): + """ + Return a textual representation of this instance, + """ + if self.source_url: + suffix = ' [%s]' % self.source_url + else: + suffix = '' + return '<Distribution %s (%s)%s>' % (self.name, self.version, suffix) + + def __eq__(self, other): + """ + See if this distribution is the same as another. + :param other: The distribution to compare with. To be equal to one + another. distributions must have the same type, name, + version and source_url. + :return: True if it is the same, else False. + """ + if type(other) is not type(self): + result = False + else: + result = (self.name == other.name and + self.version == other.version and + self.source_url == other.source_url) + return result + + def __hash__(self): + """ + Compute hash in a way which matches the equality test. + """ + return hash(self.name) + hash(self.version) + hash(self.source_url) + + +class BaseInstalledDistribution(Distribution): + """ + This is the base class for installed distributions (whether PEP 376 or + legacy). + """ + + hasher = None + + def __init__(self, metadata, path, env=None): + """ + Initialise an instance. + :param metadata: An instance of :class:`Metadata` which describes the + distribution. This will normally have been initialised + from a metadata file in the ``path``. + :param path: The path of the ``.dist-info`` or ``.egg-info`` + directory for the distribution. + :param env: This is normally the :class:`DistributionPath` + instance where this distribution was found. + """ + super(BaseInstalledDistribution, self).__init__(metadata) + self.path = path + self.dist_path = env + + def get_hash(self, data, hasher=None): + """ + Get the hash of some data, using a particular hash algorithm, if + specified. + + :param data: The data to be hashed. + :type data: bytes + :param hasher: The name of a hash implementation, supported by hashlib, + or ``None``. Examples of valid values are ``'sha1'``, + ``'sha224'``, ``'sha384'``, '``sha256'``, ``'md5'`` and + ``'sha512'``. If no hasher is specified, the ``hasher`` + attribute of the :class:`InstalledDistribution` instance + is used. If the hasher is determined to be ``None``, MD5 + is used as the hashing algorithm. + :returns: The hash of the data. If a hasher was explicitly specified, + the returned hash will be prefixed with the specified hasher + followed by '='. + :rtype: str + """ + if hasher is None: + hasher = self.hasher + if hasher is None: + hasher = hashlib.md5 + prefix = '' + else: + hasher = getattr(hashlib, hasher) + prefix = '%s=' % self.hasher + digest = hasher(data).digest() + digest = base64.urlsafe_b64encode(digest).rstrip(b'=').decode('ascii') + return '%s%s' % (prefix, digest) + + +class InstalledDistribution(BaseInstalledDistribution): + """ + Created with the *path* of the ``.dist-info`` directory provided to the + constructor. It reads the metadata contained in ``pydist.json`` when it is + instantiated., or uses a passed in Metadata instance (useful for when + dry-run mode is being used). + """ + + hasher = 'sha256' + + def __init__(self, path, metadata=None, env=None): + self.modules = [] + self.finder = finder = resources.finder_for_path(path) + if finder is None: + raise ValueError('finder unavailable for %s' % path) + if env and env._cache_enabled and path in env._cache.path: + metadata = env._cache.path[path].metadata + elif metadata is None: + r = finder.find(METADATA_FILENAME) + # Temporary - for Wheel 0.23 support + if r is None: + r = finder.find(WHEEL_METADATA_FILENAME) + # Temporary - for legacy support + if r is None: + r = finder.find('METADATA') + if r is None: + raise ValueError('no %s found in %s' % (METADATA_FILENAME, + path)) + with contextlib.closing(r.as_stream()) as stream: + metadata = Metadata(fileobj=stream, scheme='legacy') + + super(InstalledDistribution, self).__init__(metadata, path, env) + + if env and env._cache_enabled: + env._cache.add(self) + + r = finder.find('REQUESTED') + self.requested = r is not None + p = os.path.join(path, 'top_level.txt') + if os.path.exists(p): + with open(p, 'rb') as f: + data = f.read() + self.modules = data.splitlines() + + def __repr__(self): + return '<InstalledDistribution %r %s at %r>' % ( + self.name, self.version, self.path) + + def __str__(self): + return "%s %s" % (self.name, self.version) + + def _get_records(self): + """ + Get the list of installed files for the distribution + :return: A list of tuples of path, hash and size. Note that hash and + size might be ``None`` for some entries. The path is exactly + as stored in the file (which is as in PEP 376). + """ + results = [] + r = self.get_distinfo_resource('RECORD') + with contextlib.closing(r.as_stream()) as stream: + with CSVReader(stream=stream) as record_reader: + # Base location is parent dir of .dist-info dir + #base_location = os.path.dirname(self.path) + #base_location = os.path.abspath(base_location) + for row in record_reader: + missing = [None for i in range(len(row), 3)] + path, checksum, size = row + missing + #if not os.path.isabs(path): + # path = path.replace('/', os.sep) + # path = os.path.join(base_location, path) + results.append((path, checksum, size)) + return results + + @cached_property + def exports(self): + """ + Return the information exported by this distribution. + :return: A dictionary of exports, mapping an export category to a dict + of :class:`ExportEntry` instances describing the individual + export entries, and keyed by name. + """ + result = {} + r = self.get_distinfo_resource(EXPORTS_FILENAME) + if r: + result = self.read_exports() + return result + + def read_exports(self): + """ + Read exports data from a file in .ini format. + + :return: A dictionary of exports, mapping an export category to a list + of :class:`ExportEntry` instances describing the individual + export entries. + """ + result = {} + r = self.get_distinfo_resource(EXPORTS_FILENAME) + if r: + with contextlib.closing(r.as_stream()) as stream: + result = read_exports(stream) + return result + + def write_exports(self, exports): + """ + Write a dictionary of exports to a file in .ini format. + :param exports: A dictionary of exports, mapping an export category to + a list of :class:`ExportEntry` instances describing the + individual export entries. + """ + rf = self.get_distinfo_file(EXPORTS_FILENAME) + with open(rf, 'w') as f: + write_exports(exports, f) + + def get_resource_path(self, relative_path): + """ + NOTE: This API may change in the future. + + Return the absolute path to a resource file with the given relative + path. + + :param relative_path: The path, relative to .dist-info, of the resource + of interest. + :return: The absolute path where the resource is to be found. + """ + r = self.get_distinfo_resource('RESOURCES') + with contextlib.closing(r.as_stream()) as stream: + with CSVReader(stream=stream) as resources_reader: + for relative, destination in resources_reader: + if relative == relative_path: + return destination + raise KeyError('no resource file with relative path %r ' + 'is installed' % relative_path) + + def list_installed_files(self): + """ + Iterates over the ``RECORD`` entries and returns a tuple + ``(path, hash, size)`` for each line. + + :returns: iterator of (path, hash, size) + """ + for result in self._get_records(): + yield result + + def write_installed_files(self, paths, prefix, dry_run=False): + """ + Writes the ``RECORD`` file, using the ``paths`` iterable passed in. Any + existing ``RECORD`` file is silently overwritten. + + prefix is used to determine when to write absolute paths. + """ + prefix = os.path.join(prefix, '') + base = os.path.dirname(self.path) + base_under_prefix = base.startswith(prefix) + base = os.path.join(base, '') + record_path = self.get_distinfo_file('RECORD') + logger.info('creating %s', record_path) + if dry_run: + return None + with CSVWriter(record_path) as writer: + for path in paths: + if os.path.isdir(path) or path.endswith(('.pyc', '.pyo')): + # do not put size and hash, as in PEP-376 + hash_value = size = '' + else: + size = '%d' % os.path.getsize(path) + with open(path, 'rb') as fp: + hash_value = self.get_hash(fp.read()) + if path.startswith(base) or (base_under_prefix and + path.startswith(prefix)): + path = os.path.relpath(path, base) + writer.writerow((path, hash_value, size)) + + # add the RECORD file itself + if record_path.startswith(base): + record_path = os.path.relpath(record_path, base) + writer.writerow((record_path, '', '')) + return record_path + + def check_installed_files(self): + """ + Checks that the hashes and sizes of the files in ``RECORD`` are + matched by the files themselves. Returns a (possibly empty) list of + mismatches. Each entry in the mismatch list will be a tuple consisting + of the path, 'exists', 'size' or 'hash' according to what didn't match + (existence is checked first, then size, then hash), the expected + value and the actual value. + """ + mismatches = [] + base = os.path.dirname(self.path) + record_path = self.get_distinfo_file('RECORD') + for path, hash_value, size in self.list_installed_files(): + if not os.path.isabs(path): + path = os.path.join(base, path) + if path == record_path: + continue + if not os.path.exists(path): + mismatches.append((path, 'exists', True, False)) + elif os.path.isfile(path): + actual_size = str(os.path.getsize(path)) + if size and actual_size != size: + mismatches.append((path, 'size', size, actual_size)) + elif hash_value: + if '=' in hash_value: + hasher = hash_value.split('=', 1)[0] + else: + hasher = None + + with open(path, 'rb') as f: + actual_hash = self.get_hash(f.read(), hasher) + if actual_hash != hash_value: + mismatches.append((path, 'hash', hash_value, actual_hash)) + return mismatches + + @cached_property + def shared_locations(self): + """ + A dictionary of shared locations whose keys are in the set 'prefix', + 'purelib', 'platlib', 'scripts', 'headers', 'data' and 'namespace'. + The corresponding value is the absolute path of that category for + this distribution, and takes into account any paths selected by the + user at installation time (e.g. via command-line arguments). In the + case of the 'namespace' key, this would be a list of absolute paths + for the roots of namespace packages in this distribution. + + The first time this property is accessed, the relevant information is + read from the SHARED file in the .dist-info directory. + """ + result = {} + shared_path = os.path.join(self.path, 'SHARED') + if os.path.isfile(shared_path): + with codecs.open(shared_path, 'r', encoding='utf-8') as f: + lines = f.read().splitlines() + for line in lines: + key, value = line.split('=', 1) + if key == 'namespace': + result.setdefault(key, []).append(value) + else: + result[key] = value + return result + + def write_shared_locations(self, paths, dry_run=False): + """ + Write shared location information to the SHARED file in .dist-info. + :param paths: A dictionary as described in the documentation for + :meth:`shared_locations`. + :param dry_run: If True, the action is logged but no file is actually + written. + :return: The path of the file written to. + """ + shared_path = os.path.join(self.path, 'SHARED') + logger.info('creating %s', shared_path) + if dry_run: + return None + lines = [] + for key in ('prefix', 'lib', 'headers', 'scripts', 'data'): + path = paths[key] + if os.path.isdir(paths[key]): + lines.append('%s=%s' % (key, path)) + for ns in paths.get('namespace', ()): + lines.append('namespace=%s' % ns) + + with codecs.open(shared_path, 'w', encoding='utf-8') as f: + f.write('\n'.join(lines)) + return shared_path + + def get_distinfo_resource(self, path): + if path not in DIST_FILES: + raise DistlibException('invalid path for a dist-info file: ' + '%r at %r' % (path, self.path)) + finder = resources.finder_for_path(self.path) + if finder is None: + raise DistlibException('Unable to get a finder for %s' % self.path) + return finder.find(path) + + def get_distinfo_file(self, path): + """ + Returns a path located under the ``.dist-info`` directory. Returns a + string representing the path. + + :parameter path: a ``'/'``-separated path relative to the + ``.dist-info`` directory or an absolute path; + If *path* is an absolute path and doesn't start + with the ``.dist-info`` directory path, + a :class:`DistlibException` is raised + :type path: str + :rtype: str + """ + # Check if it is an absolute path # XXX use relpath, add tests + if path.find(os.sep) >= 0: + # it's an absolute path? + distinfo_dirname, path = path.split(os.sep)[-2:] + if distinfo_dirname != self.path.split(os.sep)[-1]: + raise DistlibException( + 'dist-info file %r does not belong to the %r %s ' + 'distribution' % (path, self.name, self.version)) + + # The file must be relative + if path not in DIST_FILES: + raise DistlibException('invalid path for a dist-info file: ' + '%r at %r' % (path, self.path)) + + return os.path.join(self.path, path) + + def list_distinfo_files(self): + """ + Iterates over the ``RECORD`` entries and returns paths for each line if + the path is pointing to a file located in the ``.dist-info`` directory + or one of its subdirectories. + + :returns: iterator of paths + """ + base = os.path.dirname(self.path) + for path, checksum, size in self._get_records(): + # XXX add separator or use real relpath algo + if not os.path.isabs(path): + path = os.path.join(base, path) + if path.startswith(self.path): + yield path + + def __eq__(self, other): + return (isinstance(other, InstalledDistribution) and + self.path == other.path) + + # See http://docs.python.org/reference/datamodel#object.__hash__ + __hash__ = object.__hash__ + + +class EggInfoDistribution(BaseInstalledDistribution): + """Created with the *path* of the ``.egg-info`` directory or file provided + to the constructor. It reads the metadata contained in the file itself, or + if the given path happens to be a directory, the metadata is read from the + file ``PKG-INFO`` under that directory.""" + + requested = True # as we have no way of knowing, assume it was + shared_locations = {} + + def __init__(self, path, env=None): + def set_name_and_version(s, n, v): + s.name = n + s.key = n.lower() # for case-insensitive comparisons + s.version = v + + self.path = path + self.dist_path = env + if env and env._cache_enabled and path in env._cache_egg.path: + metadata = env._cache_egg.path[path].metadata + set_name_and_version(self, metadata.name, metadata.version) + else: + metadata = self._get_metadata(path) + + # Need to be set before caching + set_name_and_version(self, metadata.name, metadata.version) + + if env and env._cache_enabled: + env._cache_egg.add(self) + super(EggInfoDistribution, self).__init__(metadata, path, env) + + def _get_metadata(self, path): + requires = None + + def parse_requires_data(data): + """Create a list of dependencies from a requires.txt file. + + *data*: the contents of a setuptools-produced requires.txt file. + """ + reqs = [] + lines = data.splitlines() + for line in lines: + line = line.strip() + if line.startswith('['): + logger.warning('Unexpected line: quitting requirement scan: %r', + line) + break + r = parse_requirement(line) + if not r: + logger.warning('Not recognised as a requirement: %r', line) + continue + if r.extras: + logger.warning('extra requirements in requires.txt are ' + 'not supported') + if not r.constraints: + reqs.append(r.name) + else: + cons = ', '.join('%s%s' % c for c in r.constraints) + reqs.append('%s (%s)' % (r.name, cons)) + return reqs + + def parse_requires_path(req_path): + """Create a list of dependencies from a requires.txt file. + + *req_path*: the path to a setuptools-produced requires.txt file. + """ + + reqs = [] + try: + with codecs.open(req_path, 'r', 'utf-8') as fp: + reqs = parse_requires_data(fp.read()) + except IOError: + pass + return reqs + + tl_path = tl_data = None + if path.endswith('.egg'): + if os.path.isdir(path): + p = os.path.join(path, 'EGG-INFO') + meta_path = os.path.join(p, 'PKG-INFO') + metadata = Metadata(path=meta_path, scheme='legacy') + req_path = os.path.join(p, 'requires.txt') + tl_path = os.path.join(p, 'top_level.txt') + requires = parse_requires_path(req_path) + else: + # FIXME handle the case where zipfile is not available + zipf = zipimport.zipimporter(path) + fileobj = StringIO( + zipf.get_data('EGG-INFO/PKG-INFO').decode('utf8')) + metadata = Metadata(fileobj=fileobj, scheme='legacy') + try: + data = zipf.get_data('EGG-INFO/requires.txt') + tl_data = zipf.get_data('EGG-INFO/top_level.txt').decode('utf-8') + requires = parse_requires_data(data.decode('utf-8')) + except IOError: + requires = None + elif path.endswith('.egg-info'): + if os.path.isdir(path): + req_path = os.path.join(path, 'requires.txt') + requires = parse_requires_path(req_path) + path = os.path.join(path, 'PKG-INFO') + tl_path = os.path.join(path, 'top_level.txt') + metadata = Metadata(path=path, scheme='legacy') + else: + raise DistlibException('path must end with .egg-info or .egg, ' + 'got %r' % path) + + if requires: + metadata.add_requirements(requires) + # look for top-level modules in top_level.txt, if present + if tl_data is None: + if tl_path is not None and os.path.exists(tl_path): + with open(tl_path, 'rb') as f: + tl_data = f.read().decode('utf-8') + if not tl_data: + tl_data = [] + else: + tl_data = tl_data.splitlines() + self.modules = tl_data + return metadata + + def __repr__(self): + return '<EggInfoDistribution %r %s at %r>' % ( + self.name, self.version, self.path) + + def __str__(self): + return "%s %s" % (self.name, self.version) + + def check_installed_files(self): + """ + Checks that the hashes and sizes of the files in ``RECORD`` are + matched by the files themselves. Returns a (possibly empty) list of + mismatches. Each entry in the mismatch list will be a tuple consisting + of the path, 'exists', 'size' or 'hash' according to what didn't match + (existence is checked first, then size, then hash), the expected + value and the actual value. + """ + mismatches = [] + record_path = os.path.join(self.path, 'installed-files.txt') + if os.path.exists(record_path): + for path, _, _ in self.list_installed_files(): + if path == record_path: + continue + if not os.path.exists(path): + mismatches.append((path, 'exists', True, False)) + return mismatches + + def list_installed_files(self): + """ + Iterates over the ``installed-files.txt`` entries and returns a tuple + ``(path, hash, size)`` for each line. + + :returns: a list of (path, hash, size) + """ + + def _md5(path): + f = open(path, 'rb') + try: + content = f.read() + finally: + f.close() + return hashlib.md5(content).hexdigest() + + def _size(path): + return os.stat(path).st_size + + record_path = os.path.join(self.path, 'installed-files.txt') + result = [] + if os.path.exists(record_path): + with codecs.open(record_path, 'r', encoding='utf-8') as f: + for line in f: + line = line.strip() + p = os.path.normpath(os.path.join(self.path, line)) + # "./" is present as a marker between installed files + # and installation metadata files + if not os.path.exists(p): + logger.warning('Non-existent file: %s', p) + if p.endswith(('.pyc', '.pyo')): + continue + #otherwise fall through and fail + if not os.path.isdir(p): + result.append((p, _md5(p), _size(p))) + result.append((record_path, None, None)) + return result + + def list_distinfo_files(self, absolute=False): + """ + Iterates over the ``installed-files.txt`` entries and returns paths for + each line if the path is pointing to a file located in the + ``.egg-info`` directory or one of its subdirectories. + + :parameter absolute: If *absolute* is ``True``, each returned path is + transformed into a local absolute path. Otherwise the + raw value from ``installed-files.txt`` is returned. + :type absolute: boolean + :returns: iterator of paths + """ + record_path = os.path.join(self.path, 'installed-files.txt') + if os.path.exists(record_path): + skip = True + with codecs.open(record_path, 'r', encoding='utf-8') as f: + for line in f: + line = line.strip() + if line == './': + skip = False + continue + if not skip: + p = os.path.normpath(os.path.join(self.path, line)) + if p.startswith(self.path): + if absolute: + yield p + else: + yield line + + def __eq__(self, other): + return (isinstance(other, EggInfoDistribution) and + self.path == other.path) + + # See http://docs.python.org/reference/datamodel#object.__hash__ + __hash__ = object.__hash__ + +new_dist_class = InstalledDistribution +old_dist_class = EggInfoDistribution + + +class DependencyGraph(object): + """ + Represents a dependency graph between distributions. + + The dependency relationships are stored in an ``adjacency_list`` that maps + distributions to a list of ``(other, label)`` tuples where ``other`` + is a distribution and the edge is labeled with ``label`` (i.e. the version + specifier, if such was provided). Also, for more efficient traversal, for + every distribution ``x``, a list of predecessors is kept in + ``reverse_list[x]``. An edge from distribution ``a`` to + distribution ``b`` means that ``a`` depends on ``b``. If any missing + dependencies are found, they are stored in ``missing``, which is a + dictionary that maps distributions to a list of requirements that were not + provided by any other distributions. + """ + + def __init__(self): + self.adjacency_list = {} + self.reverse_list = {} + self.missing = {} + + def add_distribution(self, distribution): + """Add the *distribution* to the graph. + + :type distribution: :class:`distutils2.database.InstalledDistribution` + or :class:`distutils2.database.EggInfoDistribution` + """ + self.adjacency_list[distribution] = [] + self.reverse_list[distribution] = [] + #self.missing[distribution] = [] + + def add_edge(self, x, y, label=None): + """Add an edge from distribution *x* to distribution *y* with the given + *label*. + + :type x: :class:`distutils2.database.InstalledDistribution` or + :class:`distutils2.database.EggInfoDistribution` + :type y: :class:`distutils2.database.InstalledDistribution` or + :class:`distutils2.database.EggInfoDistribution` + :type label: ``str`` or ``None`` + """ + self.adjacency_list[x].append((y, label)) + # multiple edges are allowed, so be careful + if x not in self.reverse_list[y]: + self.reverse_list[y].append(x) + + def add_missing(self, distribution, requirement): + """ + Add a missing *requirement* for the given *distribution*. + + :type distribution: :class:`distutils2.database.InstalledDistribution` + or :class:`distutils2.database.EggInfoDistribution` + :type requirement: ``str`` + """ + logger.debug('%s missing %r', distribution, requirement) + self.missing.setdefault(distribution, []).append(requirement) + + def _repr_dist(self, dist): + return '%s %s' % (dist.name, dist.version) + + def repr_node(self, dist, level=1): + """Prints only a subgraph""" + output = [self._repr_dist(dist)] + for other, label in self.adjacency_list[dist]: + dist = self._repr_dist(other) + if label is not None: + dist = '%s [%s]' % (dist, label) + output.append(' ' * level + str(dist)) + suboutput = self.repr_node(other, level + 1) + subs = suboutput.split('\n') + output.extend(subs[1:]) + return '\n'.join(output) + + def to_dot(self, f, skip_disconnected=True): + """Writes a DOT output for the graph to the provided file *f*. + + If *skip_disconnected* is set to ``True``, then all distributions + that are not dependent on any other distribution are skipped. + + :type f: has to support ``file``-like operations + :type skip_disconnected: ``bool`` + """ + disconnected = [] + + f.write("digraph dependencies {\n") + for dist, adjs in self.adjacency_list.items(): + if len(adjs) == 0 and not skip_disconnected: + disconnected.append(dist) + for other, label in adjs: + if not label is None: + f.write('"%s" -> "%s" [label="%s"]\n' % + (dist.name, other.name, label)) + else: + f.write('"%s" -> "%s"\n' % (dist.name, other.name)) + if not skip_disconnected and len(disconnected) > 0: + f.write('subgraph disconnected {\n') + f.write('label = "Disconnected"\n') + f.write('bgcolor = red\n') + + for dist in disconnected: + f.write('"%s"' % dist.name) + f.write('\n') + f.write('}\n') + f.write('}\n') + + def topological_sort(self): + """ + Perform a topological sort of the graph. + :return: A tuple, the first element of which is a topologically sorted + list of distributions, and the second element of which is a + list of distributions that cannot be sorted because they have + circular dependencies and so form a cycle. + """ + result = [] + # Make a shallow copy of the adjacency list + alist = {} + for k, v in self.adjacency_list.items(): + alist[k] = v[:] + while True: + # See what we can remove in this run + to_remove = [] + for k, v in list(alist.items())[:]: + if not v: + to_remove.append(k) + del alist[k] + if not to_remove: + # What's left in alist (if anything) is a cycle. + break + # Remove from the adjacency list of others + for k, v in alist.items(): + alist[k] = [(d, r) for d, r in v if d not in to_remove] + logger.debug('Moving to result: %s', + ['%s (%s)' % (d.name, d.version) for d in to_remove]) + result.extend(to_remove) + return result, list(alist.keys()) + + def __repr__(self): + """Representation of the graph""" + output = [] + for dist, adjs in self.adjacency_list.items(): + output.append(self.repr_node(dist)) + return '\n'.join(output) + + +def make_graph(dists, scheme='default'): + """Makes a dependency graph from the given distributions. + + :parameter dists: a list of distributions + :type dists: list of :class:`distutils2.database.InstalledDistribution` and + :class:`distutils2.database.EggInfoDistribution` instances + :rtype: a :class:`DependencyGraph` instance + """ + scheme = get_scheme(scheme) + graph = DependencyGraph() + provided = {} # maps names to lists of (version, dist) tuples + + # first, build the graph and find out what's provided + for dist in dists: + graph.add_distribution(dist) + + for p in dist.provides: + name, version = parse_name_and_version(p) + logger.debug('Add to provided: %s, %s, %s', name, version, dist) + provided.setdefault(name, []).append((version, dist)) + + # now make the edges + for dist in dists: + requires = (dist.run_requires | dist.meta_requires | + dist.build_requires | dist.dev_requires) + for req in requires: + try: + matcher = scheme.matcher(req) + except UnsupportedVersionError: + # XXX compat-mode if cannot read the version + logger.warning('could not read version %r - using name only', + req) + name = req.split()[0] + matcher = scheme.matcher(name) + + name = matcher.key # case-insensitive + + matched = False + if name in provided: + for version, provider in provided[name]: + try: + match = matcher.match(version) + except UnsupportedVersionError: + match = False + + if match: + graph.add_edge(dist, provider, req) + matched = True + break + if not matched: + graph.add_missing(dist, req) + return graph + + +def get_dependent_dists(dists, dist): + """Recursively generate a list of distributions from *dists* that are + dependent on *dist*. + + :param dists: a list of distributions + :param dist: a distribution, member of *dists* for which we are interested + """ + if dist not in dists: + raise DistlibException('given distribution %r is not a member ' + 'of the list' % dist.name) + graph = make_graph(dists) + + dep = [dist] # dependent distributions + todo = graph.reverse_list[dist] # list of nodes we should inspect + + while todo: + d = todo.pop() + dep.append(d) + for succ in graph.reverse_list[d]: + if succ not in dep: + todo.append(succ) + + dep.pop(0) # remove dist from dep, was there to prevent infinite loops + return dep + + +def get_required_dists(dists, dist): + """Recursively generate a list of distributions from *dists* that are + required by *dist*. + + :param dists: a list of distributions + :param dist: a distribution, member of *dists* for which we are interested + """ + if dist not in dists: + raise DistlibException('given distribution %r is not a member ' + 'of the list' % dist.name) + graph = make_graph(dists) + + req = [] # required distributions + todo = graph.adjacency_list[dist] # list of nodes we should inspect + + while todo: + d = todo.pop()[0] + req.append(d) + for pred in graph.adjacency_list[d]: + if pred not in req: + todo.append(pred) + + return req + + +def make_dist(name, version, **kwargs): + """ + A convenience method for making a dist given just a name and version. + """ + summary = kwargs.pop('summary', 'Placeholder for summary') + md = Metadata(**kwargs) + md.name = name + md.version = version + md.summary = summary or 'Placeholder for summary' + return Distribution(md) diff --git a/venv2/lib/python2.7/site-packages/pip/_vendor/distlib/index.py b/venv2/lib/python2.7/site-packages/pip/_vendor/distlib/index.py new file mode 100644 index 0000000..2406be2 --- /dev/null +++ b/venv2/lib/python2.7/site-packages/pip/_vendor/distlib/index.py @@ -0,0 +1,516 @@ +# -*- coding: utf-8 -*- +# +# Copyright (C) 2013 Vinay Sajip. +# Licensed to the Python Software Foundation under a contributor agreement. +# See LICENSE.txt and CONTRIBUTORS.txt. +# +import hashlib +import logging +import os +import shutil +import subprocess +import tempfile +try: + from threading import Thread +except ImportError: + from dummy_threading import Thread + +from . import DistlibException +from .compat import (HTTPBasicAuthHandler, Request, HTTPPasswordMgr, + urlparse, build_opener, string_types) +from .util import cached_property, zip_dir, ServerProxy + +logger = logging.getLogger(__name__) + +DEFAULT_INDEX = 'https://pypi.python.org/pypi' +DEFAULT_REALM = 'pypi' + +class PackageIndex(object): + """ + This class represents a package index compatible with PyPI, the Python + Package Index. + """ + + boundary = b'----------ThIs_Is_tHe_distlib_index_bouNdaRY_$' + + def __init__(self, url=None): + """ + Initialise an instance. + + :param url: The URL of the index. If not specified, the URL for PyPI is + used. + """ + self.url = url or DEFAULT_INDEX + self.read_configuration() + scheme, netloc, path, params, query, frag = urlparse(self.url) + if params or query or frag or scheme not in ('http', 'https'): + raise DistlibException('invalid repository: %s' % self.url) + self.password_handler = None + self.ssl_verifier = None + self.gpg = None + self.gpg_home = None + with open(os.devnull, 'w') as sink: + # Use gpg by default rather than gpg2, as gpg2 insists on + # prompting for passwords + for s in ('gpg', 'gpg2'): + try: + rc = subprocess.check_call([s, '--version'], stdout=sink, + stderr=sink) + if rc == 0: + self.gpg = s + break + except OSError: + pass + + def _get_pypirc_command(self): + """ + Get the distutils command for interacting with PyPI configurations. + :return: the command. + """ + from distutils.core import Distribution + from distutils.config import PyPIRCCommand + d = Distribution() + return PyPIRCCommand(d) + + def read_configuration(self): + """ + Read the PyPI access configuration as supported by distutils, getting + PyPI to do the actual work. This populates ``username``, ``password``, + ``realm`` and ``url`` attributes from the configuration. + """ + # get distutils to do the work + c = self._get_pypirc_command() + c.repository = self.url + cfg = c._read_pypirc() + self.username = cfg.get('username') + self.password = cfg.get('password') + self.realm = cfg.get('realm', 'pypi') + self.url = cfg.get('repository', self.url) + + def save_configuration(self): + """ + Save the PyPI access configuration. You must have set ``username`` and + ``password`` attributes before calling this method. + + Again, distutils is used to do the actual work. + """ + self.check_credentials() + # get distutils to do the work + c = self._get_pypirc_command() + c._store_pypirc(self.username, self.password) + + def check_credentials(self): + """ + Check that ``username`` and ``password`` have been set, and raise an + exception if not. + """ + if self.username is None or self.password is None: + raise DistlibException('username and password must be set') + pm = HTTPPasswordMgr() + _, netloc, _, _, _, _ = urlparse(self.url) + pm.add_password(self.realm, netloc, self.username, self.password) + self.password_handler = HTTPBasicAuthHandler(pm) + + def register(self, metadata): + """ + Register a distribution on PyPI, using the provided metadata. + + :param metadata: A :class:`Metadata` instance defining at least a name + and version number for the distribution to be + registered. + :return: The HTTP response received from PyPI upon submission of the + request. + """ + self.check_credentials() + metadata.validate() + d = metadata.todict() + d[':action'] = 'verify' + request = self.encode_request(d.items(), []) + response = self.send_request(request) + d[':action'] = 'submit' + request = self.encode_request(d.items(), []) + return self.send_request(request) + + def _reader(self, name, stream, outbuf): + """ + Thread runner for reading lines of from a subprocess into a buffer. + + :param name: The logical name of the stream (used for logging only). + :param stream: The stream to read from. This will typically a pipe + connected to the output stream of a subprocess. + :param outbuf: The list to append the read lines to. + """ + while True: + s = stream.readline() + if not s: + break + s = s.decode('utf-8').rstrip() + outbuf.append(s) + logger.debug('%s: %s' % (name, s)) + stream.close() + + def get_sign_command(self, filename, signer, sign_password, + keystore=None): + """ + Return a suitable command for signing a file. + + :param filename: The pathname to the file to be signed. + :param signer: The identifier of the signer of the file. + :param sign_password: The passphrase for the signer's + private key used for signing. + :param keystore: The path to a directory which contains the keys + used in verification. If not specified, the + instance's ``gpg_home`` attribute is used instead. + :return: The signing command as a list suitable to be + passed to :class:`subprocess.Popen`. + """ + cmd = [self.gpg, '--status-fd', '2', '--no-tty'] + if keystore is None: + keystore = self.gpg_home + if keystore: + cmd.extend(['--homedir', keystore]) + if sign_password is not None: + cmd.extend(['--batch', '--passphrase-fd', '0']) + td = tempfile.mkdtemp() + sf = os.path.join(td, os.path.basename(filename) + '.asc') + cmd.extend(['--detach-sign', '--armor', '--local-user', + signer, '--output', sf, filename]) + logger.debug('invoking: %s', ' '.join(cmd)) + return cmd, sf + + def run_command(self, cmd, input_data=None): + """ + Run a command in a child process , passing it any input data specified. + + :param cmd: The command to run. + :param input_data: If specified, this must be a byte string containing + data to be sent to the child process. + :return: A tuple consisting of the subprocess' exit code, a list of + lines read from the subprocess' ``stdout``, and a list of + lines read from the subprocess' ``stderr``. + """ + kwargs = { + 'stdout': subprocess.PIPE, + 'stderr': subprocess.PIPE, + } + if input_data is not None: + kwargs['stdin'] = subprocess.PIPE + stdout = [] + stderr = [] + p = subprocess.Popen(cmd, **kwargs) + # We don't use communicate() here because we may need to + # get clever with interacting with the command + t1 = Thread(target=self._reader, args=('stdout', p.stdout, stdout)) + t1.start() + t2 = Thread(target=self._reader, args=('stderr', p.stderr, stderr)) + t2.start() + if input_data is not None: + p.stdin.write(input_data) + p.stdin.close() + + p.wait() + t1.join() + t2.join() + return p.returncode, stdout, stderr + + def sign_file(self, filename, signer, sign_password, keystore=None): + """ + Sign a file. + + :param filename: The pathname to the file to be signed. + :param signer: The identifier of the signer of the file. + :param sign_password: The passphrase for the signer's + private key used for signing. + :param keystore: The path to a directory which contains the keys + used in signing. If not specified, the instance's + ``gpg_home`` attribute is used instead. + :return: The absolute pathname of the file where the signature is + stored. + """ + cmd, sig_file = self.get_sign_command(filename, signer, sign_password, + keystore) + rc, stdout, stderr = self.run_command(cmd, + sign_password.encode('utf-8')) + if rc != 0: + raise DistlibException('sign command failed with error ' + 'code %s' % rc) + return sig_file + + def upload_file(self, metadata, filename, signer=None, sign_password=None, + filetype='sdist', pyversion='source', keystore=None): + """ + Upload a release file to the index. + + :param metadata: A :class:`Metadata` instance defining at least a name + and version number for the file to be uploaded. + :param filename: The pathname of the file to be uploaded. + :param signer: The identifier of the signer of the file. + :param sign_password: The passphrase for the signer's + private key used for signing. + :param filetype: The type of the file being uploaded. This is the + distutils command which produced that file, e.g. + ``sdist`` or ``bdist_wheel``. + :param pyversion: The version of Python which the release relates + to. For code compatible with any Python, this would + be ``source``, otherwise it would be e.g. ``3.2``. + :param keystore: The path to a directory which contains the keys + used in signing. If not specified, the instance's + ``gpg_home`` attribute is used instead. + :return: The HTTP response received from PyPI upon submission of the + request. + """ + self.check_credentials() + if not os.path.exists(filename): + raise DistlibException('not found: %s' % filename) + metadata.validate() + d = metadata.todict() + sig_file = None + if signer: + if not self.gpg: + logger.warning('no signing program available - not signed') + else: + sig_file = self.sign_file(filename, signer, sign_password, + keystore) + with open(filename, 'rb') as f: + file_data = f.read() + md5_digest = hashlib.md5(file_data).hexdigest() + sha256_digest = hashlib.sha256(file_data).hexdigest() + d.update({ + ':action': 'file_upload', + 'protocol_version': '1', + 'filetype': filetype, + 'pyversion': pyversion, + 'md5_digest': md5_digest, + 'sha256_digest': sha256_digest, + }) + files = [('content', os.path.basename(filename), file_data)] + if sig_file: + with open(sig_file, 'rb') as f: + sig_data = f.read() + files.append(('gpg_signature', os.path.basename(sig_file), + sig_data)) + shutil.rmtree(os.path.dirname(sig_file)) + request = self.encode_request(d.items(), files) + return self.send_request(request) + + def upload_documentation(self, metadata, doc_dir): + """ + Upload documentation to the index. + + :param metadata: A :class:`Metadata` instance defining at least a name + and version number for the documentation to be + uploaded. + :param doc_dir: The pathname of the directory which contains the + documentation. This should be the directory that + contains the ``index.html`` for the documentation. + :return: The HTTP response received from PyPI upon submission of the + request. + """ + self.check_credentials() + if not os.path.isdir(doc_dir): + raise DistlibException('not a directory: %r' % doc_dir) + fn = os.path.join(doc_dir, 'index.html') + if not os.path.exists(fn): + raise DistlibException('not found: %r' % fn) + metadata.validate() + name, version = metadata.name, metadata.version + zip_data = zip_dir(doc_dir).getvalue() + fields = [(':action', 'doc_upload'), + ('name', name), ('version', version)] + files = [('content', name, zip_data)] + request = self.encode_request(fields, files) + return self.send_request(request) + + def get_verify_command(self, signature_filename, data_filename, + keystore=None): + """ + Return a suitable command for verifying a file. + + :param signature_filename: The pathname to the file containing the + signature. + :param data_filename: The pathname to the file containing the + signed data. + :param keystore: The path to a directory which contains the keys + used in verification. If not specified, the + instance's ``gpg_home`` attribute is used instead. + :return: The verifying command as a list suitable to be + passed to :class:`subprocess.Popen`. + """ + cmd = [self.gpg, '--status-fd', '2', '--no-tty'] + if keystore is None: + keystore = self.gpg_home + if keystore: + cmd.extend(['--homedir', keystore]) + cmd.extend(['--verify', signature_filename, data_filename]) + logger.debug('invoking: %s', ' '.join(cmd)) + return cmd + + def verify_signature(self, signature_filename, data_filename, + keystore=None): + """ + Verify a signature for a file. + + :param signature_filename: The pathname to the file containing the + signature. + :param data_filename: The pathname to the file containing the + signed data. + :param keystore: The path to a directory which contains the keys + used in verification. If not specified, the + instance's ``gpg_home`` attribute is used instead. + :return: True if the signature was verified, else False. + """ + if not self.gpg: + raise DistlibException('verification unavailable because gpg ' + 'unavailable') + cmd = self.get_verify_command(signature_filename, data_filename, + keystore) + rc, stdout, stderr = self.run_command(cmd) + if rc not in (0, 1): + raise DistlibException('verify command failed with error ' + 'code %s' % rc) + return rc == 0 + + def download_file(self, url, destfile, digest=None, reporthook=None): + """ + This is a convenience method for downloading a file from an URL. + Normally, this will be a file from the index, though currently + no check is made for this (i.e. a file can be downloaded from + anywhere). + + The method is just like the :func:`urlretrieve` function in the + standard library, except that it allows digest computation to be + done during download and checking that the downloaded data + matched any expected value. + + :param url: The URL of the file to be downloaded (assumed to be + available via an HTTP GET request). + :param destfile: The pathname where the downloaded file is to be + saved. + :param digest: If specified, this must be a (hasher, value) + tuple, where hasher is the algorithm used (e.g. + ``'md5'``) and ``value`` is the expected value. + :param reporthook: The same as for :func:`urlretrieve` in the + standard library. + """ + if digest is None: + digester = None + logger.debug('No digest specified') + else: + if isinstance(digest, (list, tuple)): + hasher, digest = digest + else: + hasher = 'md5' + digester = getattr(hashlib, hasher)() + logger.debug('Digest specified: %s' % digest) + # The following code is equivalent to urlretrieve. + # We need to do it this way so that we can compute the + # digest of the file as we go. + with open(destfile, 'wb') as dfp: + # addinfourl is not a context manager on 2.x + # so we have to use try/finally + sfp = self.send_request(Request(url)) + try: + headers = sfp.info() + blocksize = 8192 + size = -1 + read = 0 + blocknum = 0 + if "content-length" in headers: + size = int(headers["Content-Length"]) + if reporthook: + reporthook(blocknum, blocksize, size) + while True: + block = sfp.read(blocksize) + if not block: + break + read += len(block) + dfp.write(block) + if digester: + digester.update(block) + blocknum += 1 + if reporthook: + reporthook(blocknum, blocksize, size) + finally: + sfp.close() + + # check that we got the whole file, if we can + if size >= 0 and read < size: + raise DistlibException( + 'retrieval incomplete: got only %d out of %d bytes' + % (read, size)) + # if we have a digest, it must match. + if digester: + actual = digester.hexdigest() + if digest != actual: + raise DistlibException('%s digest mismatch for %s: expected ' + '%s, got %s' % (hasher, destfile, + digest, actual)) + logger.debug('Digest verified: %s', digest) + + def send_request(self, req): + """ + Send a standard library :class:`Request` to PyPI and return its + response. + + :param req: The request to send. + :return: The HTTP response from PyPI (a standard library HTTPResponse). + """ + handlers = [] + if self.password_handler: + handlers.append(self.password_handler) + if self.ssl_verifier: + handlers.append(self.ssl_verifier) + opener = build_opener(*handlers) + return opener.open(req) + + def encode_request(self, fields, files): + """ + Encode fields and files for posting to an HTTP server. + + :param fields: The fields to send as a list of (fieldname, value) + tuples. + :param files: The files to send as a list of (fieldname, filename, + file_bytes) tuple. + """ + # Adapted from packaging, which in turn was adapted from + # http://code.activestate.com/recipes/146306 + + parts = [] + boundary = self.boundary + for k, values in fields: + if not isinstance(values, (list, tuple)): + values = [values] + + for v in values: + parts.extend(( + b'--' + boundary, + ('Content-Disposition: form-data; name="%s"' % + k).encode('utf-8'), + b'', + v.encode('utf-8'))) + for key, filename, value in files: + parts.extend(( + b'--' + boundary, + ('Content-Disposition: form-data; name="%s"; filename="%s"' % + (key, filename)).encode('utf-8'), + b'', + value)) + + parts.extend((b'--' + boundary + b'--', b'')) + + body = b'\r\n'.join(parts) + ct = b'multipart/form-data; boundary=' + boundary + headers = { + 'Content-type': ct, + 'Content-length': str(len(body)) + } + return Request(self.url, body, headers) + + def search(self, terms, operator=None): + if isinstance(terms, string_types): + terms = {'name': terms} + rpc_proxy = ServerProxy(self.url, timeout=3.0) + try: + return rpc_proxy.search(terms, operator or 'and') + finally: + rpc_proxy('close')() diff --git a/venv2/lib/python2.7/site-packages/pip/_vendor/distlib/locators.py b/venv2/lib/python2.7/site-packages/pip/_vendor/distlib/locators.py new file mode 100644 index 0000000..5c655c3 --- /dev/null +++ b/venv2/lib/python2.7/site-packages/pip/_vendor/distlib/locators.py @@ -0,0 +1,1295 @@ +# -*- coding: utf-8 -*- +# +# Copyright (C) 2012-2015 Vinay Sajip. +# Licensed to the Python Software Foundation under a contributor agreement. +# See LICENSE.txt and CONTRIBUTORS.txt. +# + +import gzip +from io import BytesIO +import json +import logging +import os +import posixpath +import re +try: + import threading +except ImportError: # pragma: no cover + import dummy_threading as threading +import zlib + +from . import DistlibException +from .compat import (urljoin, urlparse, urlunparse, url2pathname, pathname2url, + queue, quote, unescape, string_types, build_opener, + HTTPRedirectHandler as BaseRedirectHandler, text_type, + Request, HTTPError, URLError) +from .database import Distribution, DistributionPath, make_dist +from .metadata import Metadata, MetadataInvalidError +from .util import (cached_property, parse_credentials, ensure_slash, + split_filename, get_project_data, parse_requirement, + parse_name_and_version, ServerProxy, normalize_name) +from .version import get_scheme, UnsupportedVersionError +from .wheel import Wheel, is_compatible + +logger = logging.getLogger(__name__) + +HASHER_HASH = re.compile(r'^(\w+)=([a-f0-9]+)') +CHARSET = re.compile(r';\s*charset\s*=\s*(.*)\s*$', re.I) +HTML_CONTENT_TYPE = re.compile('text/html|application/x(ht)?ml') +DEFAULT_INDEX = 'https://pypi.python.org/pypi' + +def get_all_distribution_names(url=None): + """ + Return all distribution names known by an index. + :param url: The URL of the index. + :return: A list of all known distribution names. + """ + if url is None: + url = DEFAULT_INDEX + client = ServerProxy(url, timeout=3.0) + try: + return client.list_packages() + finally: + client('close')() + +class RedirectHandler(BaseRedirectHandler): + """ + A class to work around a bug in some Python 3.2.x releases. + """ + # There's a bug in the base version for some 3.2.x + # (e.g. 3.2.2 on Ubuntu Oneiric). If a Location header + # returns e.g. /abc, it bails because it says the scheme '' + # is bogus, when actually it should use the request's + # URL for the scheme. See Python issue #13696. + def http_error_302(self, req, fp, code, msg, headers): + # Some servers (incorrectly) return multiple Location headers + # (so probably same goes for URI). Use first header. + newurl = None + for key in ('location', 'uri'): + if key in headers: + newurl = headers[key] + break + if newurl is None: # pragma: no cover + return + urlparts = urlparse(newurl) + if urlparts.scheme == '': + newurl = urljoin(req.get_full_url(), newurl) + if hasattr(headers, 'replace_header'): + headers.replace_header(key, newurl) + else: + headers[key] = newurl + return BaseRedirectHandler.http_error_302(self, req, fp, code, msg, + headers) + + http_error_301 = http_error_303 = http_error_307 = http_error_302 + +class Locator(object): + """ + A base class for locators - things that locate distributions. + """ + source_extensions = ('.tar.gz', '.tar.bz2', '.tar', '.zip', '.tgz', '.tbz') + binary_extensions = ('.egg', '.exe', '.whl') + excluded_extensions = ('.pdf',) + + # A list of tags indicating which wheels you want to match. The default + # value of None matches against the tags compatible with the running + # Python. If you want to match other values, set wheel_tags on a locator + # instance to a list of tuples (pyver, abi, arch) which you want to match. + wheel_tags = None + + downloadable_extensions = source_extensions + ('.whl',) + + def __init__(self, scheme='default'): + """ + Initialise an instance. + :param scheme: Because locators look for most recent versions, they + need to know the version scheme to use. This specifies + the current PEP-recommended scheme - use ``'legacy'`` + if you need to support existing distributions on PyPI. + """ + self._cache = {} + self.scheme = scheme + # Because of bugs in some of the handlers on some of the platforms, + # we use our own opener rather than just using urlopen. + self.opener = build_opener(RedirectHandler()) + # If get_project() is called from locate(), the matcher instance + # is set from the requirement passed to locate(). See issue #18 for + # why this can be useful to know. + self.matcher = None + self.errors = queue.Queue() + + def get_errors(self): + """ + Return any errors which have occurred. + """ + result = [] + while not self.errors.empty(): # pragma: no cover + try: + e = self.errors.get(False) + result.append(e) + except self.errors.Empty: + continue + self.errors.task_done() + return result + + def clear_errors(self): + """ + Clear any errors which may have been logged. + """ + # Just get the errors and throw them away + self.get_errors() + + def clear_cache(self): + self._cache.clear() + + def _get_scheme(self): + return self._scheme + + def _set_scheme(self, value): + self._scheme = value + + scheme = property(_get_scheme, _set_scheme) + + def _get_project(self, name): + """ + For a given project, get a dictionary mapping available versions to Distribution + instances. + + This should be implemented in subclasses. + + If called from a locate() request, self.matcher will be set to a + matcher for the requirement to satisfy, otherwise it will be None. + """ + raise NotImplementedError('Please implement in the subclass') + + def get_distribution_names(self): + """ + Return all the distribution names known to this locator. + """ + raise NotImplementedError('Please implement in the subclass') + + def get_project(self, name): + """ + For a given project, get a dictionary mapping available versions to Distribution + instances. + + This calls _get_project to do all the work, and just implements a caching layer on top. + """ + if self._cache is None: # pragma: no cover + result = self._get_project(name) + elif name in self._cache: + result = self._cache[name] + else: + self.clear_errors() + result = self._get_project(name) + self._cache[name] = result + return result + + def score_url(self, url): + """ + Give an url a score which can be used to choose preferred URLs + for a given project release. + """ + t = urlparse(url) + basename = posixpath.basename(t.path) + compatible = True + is_wheel = basename.endswith('.whl') + is_downloadable = basename.endswith(self.downloadable_extensions) + if is_wheel: + compatible = is_compatible(Wheel(basename), self.wheel_tags) + return (t.scheme == 'https', 'pypi.python.org' in t.netloc, + is_downloadable, is_wheel, compatible, basename) + + def prefer_url(self, url1, url2): + """ + Choose one of two URLs where both are candidates for distribution + archives for the same version of a distribution (for example, + .tar.gz vs. zip). + + The current implementation favours https:// URLs over http://, archives + from PyPI over those from other locations, wheel compatibility (if a + wheel) and then the archive name. + """ + result = url2 + if url1: + s1 = self.score_url(url1) + s2 = self.score_url(url2) + if s1 > s2: + result = url1 + if result != url2: + logger.debug('Not replacing %r with %r', url1, url2) + else: + logger.debug('Replacing %r with %r', url1, url2) + return result + + def split_filename(self, filename, project_name): + """ + Attempt to split a filename in project name, version and Python version. + """ + return split_filename(filename, project_name) + + def convert_url_to_download_info(self, url, project_name): + """ + See if a URL is a candidate for a download URL for a project (the URL + has typically been scraped from an HTML page). + + If it is, a dictionary is returned with keys "name", "version", + "filename" and "url"; otherwise, None is returned. + """ + def same_project(name1, name2): + return normalize_name(name1) == normalize_name(name2) + + result = None + scheme, netloc, path, params, query, frag = urlparse(url) + if frag.lower().startswith('egg='): # pragma: no cover + logger.debug('%s: version hint in fragment: %r', + project_name, frag) + m = HASHER_HASH.match(frag) + if m: + algo, digest = m.groups() + else: + algo, digest = None, None + origpath = path + if path and path[-1] == '/': # pragma: no cover + path = path[:-1] + if path.endswith('.whl'): + try: + wheel = Wheel(path) + if not is_compatible(wheel, self.wheel_tags): + logger.debug('Wheel not compatible: %s', path) + else: + if project_name is None: + include = True + else: + include = same_project(wheel.name, project_name) + if include: + result = { + 'name': wheel.name, + 'version': wheel.version, + 'filename': wheel.filename, + 'url': urlunparse((scheme, netloc, origpath, + params, query, '')), + 'python-version': ', '.join( + ['.'.join(list(v[2:])) for v in wheel.pyver]), + } + except Exception as e: # pragma: no cover + logger.warning('invalid path for wheel: %s', path) + elif not path.endswith(self.downloadable_extensions): # pragma: no cover + logger.debug('Not downloadable: %s', path) + else: # downloadable extension + path = filename = posixpath.basename(path) + for ext in self.downloadable_extensions: + if path.endswith(ext): + path = path[:-len(ext)] + t = self.split_filename(path, project_name) + if not t: # pragma: no cover + logger.debug('No match for project/version: %s', path) + else: + name, version, pyver = t + if not project_name or same_project(project_name, name): + result = { + 'name': name, + 'version': version, + 'filename': filename, + 'url': urlunparse((scheme, netloc, origpath, + params, query, '')), + #'packagetype': 'sdist', + } + if pyver: # pragma: no cover + result['python-version'] = pyver + break + if result and algo: + result['%s_digest' % algo] = digest + return result + + def _get_digest(self, info): + """ + Get a digest from a dictionary by looking at keys of the form + 'algo_digest'. + + Returns a 2-tuple (algo, digest) if found, else None. Currently + looks only for SHA256, then MD5. + """ + result = None + for algo in ('sha256', 'md5'): + key = '%s_digest' % algo + if key in info: + result = (algo, info[key]) + break + return result + + def _update_version_data(self, result, info): + """ + Update a result dictionary (the final result from _get_project) with a + dictionary for a specific version, which typically holds information + gleaned from a filename or URL for an archive for the distribution. + """ + name = info.pop('name') + version = info.pop('version') + if version in result: + dist = result[version] + md = dist.metadata + else: + dist = make_dist(name, version, scheme=self.scheme) + md = dist.metadata + dist.digest = digest = self._get_digest(info) + url = info['url'] + result['digests'][url] = digest + if md.source_url != info['url']: + md.source_url = self.prefer_url(md.source_url, url) + result['urls'].setdefault(version, set()).add(url) + dist.locator = self + result[version] = dist + + def locate(self, requirement, prereleases=False): + """ + Find the most recent distribution which matches the given + requirement. + + :param requirement: A requirement of the form 'foo (1.0)' or perhaps + 'foo (>= 1.0, < 2.0, != 1.3)' + :param prereleases: If ``True``, allow pre-release versions + to be located. Otherwise, pre-release versions + are not returned. + :return: A :class:`Distribution` instance, or ``None`` if no such + distribution could be located. + """ + result = None + r = parse_requirement(requirement) + if r is None: # pragma: no cover + raise DistlibException('Not a valid requirement: %r' % requirement) + scheme = get_scheme(self.scheme) + self.matcher = matcher = scheme.matcher(r.requirement) + logger.debug('matcher: %s (%s)', matcher, type(matcher).__name__) + versions = self.get_project(r.name) + if len(versions) > 2: # urls and digests keys are present + # sometimes, versions are invalid + slist = [] + vcls = matcher.version_class + for k in versions: + if k in ('urls', 'digests'): + continue + try: + if not matcher.match(k): + logger.debug('%s did not match %r', matcher, k) + else: + if prereleases or not vcls(k).is_prerelease: + slist.append(k) + else: + logger.debug('skipping pre-release ' + 'version %s of %s', k, matcher.name) + except Exception: # pragma: no cover + logger.warning('error matching %s with %r', matcher, k) + pass # slist.append(k) + if len(slist) > 1: + slist = sorted(slist, key=scheme.key) + if slist: + logger.debug('sorted list: %s', slist) + version = slist[-1] + result = versions[version] + if result: + if r.extras: + result.extras = r.extras + result.download_urls = versions.get('urls', {}).get(version, set()) + d = {} + sd = versions.get('digests', {}) + for url in result.download_urls: + if url in sd: # pragma: no cover + d[url] = sd[url] + result.digests = d + self.matcher = None + return result + + +class PyPIRPCLocator(Locator): + """ + This locator uses XML-RPC to locate distributions. It therefore + cannot be used with simple mirrors (that only mirror file content). + """ + def __init__(self, url, **kwargs): + """ + Initialise an instance. + + :param url: The URL to use for XML-RPC. + :param kwargs: Passed to the superclass constructor. + """ + super(PyPIRPCLocator, self).__init__(**kwargs) + self.base_url = url + self.client = ServerProxy(url, timeout=3.0) + + def get_distribution_names(self): + """ + Return all the distribution names known to this locator. + """ + return set(self.client.list_packages()) + + def _get_project(self, name): + result = {'urls': {}, 'digests': {}} + versions = self.client.package_releases(name, True) + for v in versions: + urls = self.client.release_urls(name, v) + data = self.client.release_data(name, v) + metadata = Metadata(scheme=self.scheme) + metadata.name = data['name'] + metadata.version = data['version'] + metadata.license = data.get('license') + metadata.keywords = data.get('keywords', []) + metadata.summary = data.get('summary') + dist = Distribution(metadata) + if urls: + info = urls[0] + metadata.source_url = info['url'] + dist.digest = self._get_digest(info) + dist.locator = self + result[v] = dist + for info in urls: + url = info['url'] + digest = self._get_digest(info) + result['urls'].setdefault(v, set()).add(url) + result['digests'][url] = digest + return result + +class PyPIJSONLocator(Locator): + """ + This locator uses PyPI's JSON interface. It's very limited in functionality + and probably not worth using. + """ + def __init__(self, url, **kwargs): + super(PyPIJSONLocator, self).__init__(**kwargs) + self.base_url = ensure_slash(url) + + def get_distribution_names(self): + """ + Return all the distribution names known to this locator. + """ + raise NotImplementedError('Not available from this locator') + + def _get_project(self, name): + result = {'urls': {}, 'digests': {}} + url = urljoin(self.base_url, '%s/json' % quote(name)) + try: + resp = self.opener.open(url) + data = resp.read().decode() # for now + d = json.loads(data) + md = Metadata(scheme=self.scheme) + data = d['info'] + md.name = data['name'] + md.version = data['version'] + md.license = data.get('license') + md.keywords = data.get('keywords', []) + md.summary = data.get('summary') + dist = Distribution(md) + dist.locator = self + urls = d['urls'] + result[md.version] = dist + for info in d['urls']: + url = info['url'] + dist.download_urls.add(url) + dist.digests[url] = self._get_digest(info) + result['urls'].setdefault(md.version, set()).add(url) + result['digests'][url] = self._get_digest(info) + # Now get other releases + for version, infos in d['releases'].items(): + if version == md.version: + continue # already done + omd = Metadata(scheme=self.scheme) + omd.name = md.name + omd.version = version + odist = Distribution(omd) + odist.locator = self + result[version] = odist + for info in infos: + url = info['url'] + odist.download_urls.add(url) + odist.digests[url] = self._get_digest(info) + result['urls'].setdefault(version, set()).add(url) + result['digests'][url] = self._get_digest(info) +# for info in urls: +# md.source_url = info['url'] +# dist.digest = self._get_digest(info) +# dist.locator = self +# for info in urls: +# url = info['url'] +# result['urls'].setdefault(md.version, set()).add(url) +# result['digests'][url] = self._get_digest(info) + except Exception as e: + self.errors.put(text_type(e)) + logger.exception('JSON fetch failed: %s', e) + return result + + +class Page(object): + """ + This class represents a scraped HTML page. + """ + # The following slightly hairy-looking regex just looks for the contents of + # an anchor link, which has an attribute "href" either immediately preceded + # or immediately followed by a "rel" attribute. The attribute values can be + # declared with double quotes, single quotes or no quotes - which leads to + # the length of the expression. + _href = re.compile(""" +(rel\\s*=\\s*(?:"(?P<rel1>[^"]*)"|'(?P<rel2>[^']*)'|(?P<rel3>[^>\\s\n]*))\\s+)? +href\\s*=\\s*(?:"(?P<url1>[^"]*)"|'(?P<url2>[^']*)'|(?P<url3>[^>\\s\n]*)) +(\\s+rel\\s*=\\s*(?:"(?P<rel4>[^"]*)"|'(?P<rel5>[^']*)'|(?P<rel6>[^>\\s\n]*)))? +""", re.I | re.S | re.X) + _base = re.compile(r"""<base\s+href\s*=\s*['"]?([^'">]+)""", re.I | re.S) + + def __init__(self, data, url): + """ + Initialise an instance with the Unicode page contents and the URL they + came from. + """ + self.data = data + self.base_url = self.url = url + m = self._base.search(self.data) + if m: + self.base_url = m.group(1) + + _clean_re = re.compile(r'[^a-z0-9$&+,/:;=?@.#%_\\|-]', re.I) + + @cached_property + def links(self): + """ + Return the URLs of all the links on a page together with information + about their "rel" attribute, for determining which ones to treat as + downloads and which ones to queue for further scraping. + """ + def clean(url): + "Tidy up an URL." + scheme, netloc, path, params, query, frag = urlparse(url) + return urlunparse((scheme, netloc, quote(path), + params, query, frag)) + + result = set() + for match in self._href.finditer(self.data): + d = match.groupdict('') + rel = (d['rel1'] or d['rel2'] or d['rel3'] or + d['rel4'] or d['rel5'] or d['rel6']) + url = d['url1'] or d['url2'] or d['url3'] + url = urljoin(self.base_url, url) + url = unescape(url) + url = self._clean_re.sub(lambda m: '%%%2x' % ord(m.group(0)), url) + result.add((url, rel)) + # We sort the result, hoping to bring the most recent versions + # to the front + result = sorted(result, key=lambda t: t[0], reverse=True) + return result + + +class SimpleScrapingLocator(Locator): + """ + A locator which scrapes HTML pages to locate downloads for a distribution. + This runs multiple threads to do the I/O; performance is at least as good + as pip's PackageFinder, which works in an analogous fashion. + """ + + # These are used to deal with various Content-Encoding schemes. + decoders = { + 'deflate': zlib.decompress, + 'gzip': lambda b: gzip.GzipFile(fileobj=BytesIO(d)).read(), + 'none': lambda b: b, + } + + def __init__(self, url, timeout=None, num_workers=10, **kwargs): + """ + Initialise an instance. + :param url: The root URL to use for scraping. + :param timeout: The timeout, in seconds, to be applied to requests. + This defaults to ``None`` (no timeout specified). + :param num_workers: The number of worker threads you want to do I/O, + This defaults to 10. + :param kwargs: Passed to the superclass. + """ + super(SimpleScrapingLocator, self).__init__(**kwargs) + self.base_url = ensure_slash(url) + self.timeout = timeout + self._page_cache = {} + self._seen = set() + self._to_fetch = queue.Queue() + self._bad_hosts = set() + self.skip_externals = False + self.num_workers = num_workers + self._lock = threading.RLock() + # See issue #45: we need to be resilient when the locator is used + # in a thread, e.g. with concurrent.futures. We can't use self._lock + # as it is for coordinating our internal threads - the ones created + # in _prepare_threads. + self._gplock = threading.RLock() + self.platform_check = False # See issue #112 + + def _prepare_threads(self): + """ + Threads are created only when get_project is called, and terminate + before it returns. They are there primarily to parallelise I/O (i.e. + fetching web pages). + """ + self._threads = [] + for i in range(self.num_workers): + t = threading.Thread(target=self._fetch) + t.setDaemon(True) + t.start() + self._threads.append(t) + + def _wait_threads(self): + """ + Tell all the threads to terminate (by sending a sentinel value) and + wait for them to do so. + """ + # Note that you need two loops, since you can't say which + # thread will get each sentinel + for t in self._threads: + self._to_fetch.put(None) # sentinel + for t in self._threads: + t.join() + self._threads = [] + + def _get_project(self, name): + result = {'urls': {}, 'digests': {}} + with self._gplock: + self.result = result + self.project_name = name + url = urljoin(self.base_url, '%s/' % quote(name)) + self._seen.clear() + self._page_cache.clear() + self._prepare_threads() + try: + logger.debug('Queueing %s', url) + self._to_fetch.put(url) + self._to_fetch.join() + finally: + self._wait_threads() + del self.result + return result + + platform_dependent = re.compile(r'\b(linux_(i\d86|x86_64|arm\w+)|' + r'win(32|_amd64)|macosx_?\d+)\b', re.I) + + def _is_platform_dependent(self, url): + """ + Does an URL refer to a platform-specific download? + """ + return self.platform_dependent.search(url) + + def _process_download(self, url): + """ + See if an URL is a suitable download for a project. + + If it is, register information in the result dictionary (for + _get_project) about the specific version it's for. + + Note that the return value isn't actually used other than as a boolean + value. + """ + if self.platform_check and self._is_platform_dependent(url): + info = None + else: + info = self.convert_url_to_download_info(url, self.project_name) + logger.debug('process_download: %s -> %s', url, info) + if info: + with self._lock: # needed because self.result is shared + self._update_version_data(self.result, info) + return info + + def _should_queue(self, link, referrer, rel): + """ + Determine whether a link URL from a referring page and with a + particular "rel" attribute should be queued for scraping. + """ + scheme, netloc, path, _, _, _ = urlparse(link) + if path.endswith(self.source_extensions + self.binary_extensions + + self.excluded_extensions): + result = False + elif self.skip_externals and not link.startswith(self.base_url): + result = False + elif not referrer.startswith(self.base_url): + result = False + elif rel not in ('homepage', 'download'): + result = False + elif scheme not in ('http', 'https', 'ftp'): + result = False + elif self._is_platform_dependent(link): + result = False + else: + host = netloc.split(':', 1)[0] + if host.lower() == 'localhost': + result = False + else: + result = True + logger.debug('should_queue: %s (%s) from %s -> %s', link, rel, + referrer, result) + return result + + def _fetch(self): + """ + Get a URL to fetch from the work queue, get the HTML page, examine its + links for download candidates and candidates for further scraping. + + This is a handy method to run in a thread. + """ + while True: + url = self._to_fetch.get() + try: + if url: + page = self.get_page(url) + if page is None: # e.g. after an error + continue + for link, rel in page.links: + if link not in self._seen: + try: + self._seen.add(link) + if (not self._process_download(link) and + self._should_queue(link, url, rel)): + logger.debug('Queueing %s from %s', link, url) + self._to_fetch.put(link) + except MetadataInvalidError: # e.g. invalid versions + pass + except Exception as e: # pragma: no cover + self.errors.put(text_type(e)) + finally: + # always do this, to avoid hangs :-) + self._to_fetch.task_done() + if not url: + #logger.debug('Sentinel seen, quitting.') + break + + def get_page(self, url): + """ + Get the HTML for an URL, possibly from an in-memory cache. + + XXX TODO Note: this cache is never actually cleared. It's assumed that + the data won't get stale over the lifetime of a locator instance (not + necessarily true for the default_locator). + """ + # http://peak.telecommunity.com/DevCenter/EasyInstall#package-index-api + scheme, netloc, path, _, _, _ = urlparse(url) + if scheme == 'file' and os.path.isdir(url2pathname(path)): + url = urljoin(ensure_slash(url), 'index.html') + + if url in self._page_cache: + result = self._page_cache[url] + logger.debug('Returning %s from cache: %s', url, result) + else: + host = netloc.split(':', 1)[0] + result = None + if host in self._bad_hosts: + logger.debug('Skipping %s due to bad host %s', url, host) + else: + req = Request(url, headers={'Accept-encoding': 'identity'}) + try: + logger.debug('Fetching %s', url) + resp = self.opener.open(req, timeout=self.timeout) + logger.debug('Fetched %s', url) + headers = resp.info() + content_type = headers.get('Content-Type', '') + if HTML_CONTENT_TYPE.match(content_type): + final_url = resp.geturl() + data = resp.read() + encoding = headers.get('Content-Encoding') + if encoding: + decoder = self.decoders[encoding] # fail if not found + data = decoder(data) + encoding = 'utf-8' + m = CHARSET.search(content_type) + if m: + encoding = m.group(1) + try: + data = data.decode(encoding) + except UnicodeError: # pragma: no cover + data = data.decode('latin-1') # fallback + result = Page(data, final_url) + self._page_cache[final_url] = result + except HTTPError as e: + if e.code != 404: + logger.exception('Fetch failed: %s: %s', url, e) + except URLError as e: # pragma: no cover + logger.exception('Fetch failed: %s: %s', url, e) + with self._lock: + self._bad_hosts.add(host) + except Exception as e: # pragma: no cover + logger.exception('Fetch failed: %s: %s', url, e) + finally: + self._page_cache[url] = result # even if None (failure) + return result + + _distname_re = re.compile('<a href=[^>]*>([^<]+)<') + + def get_distribution_names(self): + """ + Return all the distribution names known to this locator. + """ + result = set() + page = self.get_page(self.base_url) + if not page: + raise DistlibException('Unable to get %s' % self.base_url) + for match in self._distname_re.finditer(page.data): + result.add(match.group(1)) + return result + +class DirectoryLocator(Locator): + """ + This class locates distributions in a directory tree. + """ + + def __init__(self, path, **kwargs): + """ + Initialise an instance. + :param path: The root of the directory tree to search. + :param kwargs: Passed to the superclass constructor, + except for: + * recursive - if True (the default), subdirectories are + recursed into. If False, only the top-level directory + is searched, + """ + self.recursive = kwargs.pop('recursive', True) + super(DirectoryLocator, self).__init__(**kwargs) + path = os.path.abspath(path) + if not os.path.isdir(path): # pragma: no cover + raise DistlibException('Not a directory: %r' % path) + self.base_dir = path + + def should_include(self, filename, parent): + """ + Should a filename be considered as a candidate for a distribution + archive? As well as the filename, the directory which contains it + is provided, though not used by the current implementation. + """ + return filename.endswith(self.downloadable_extensions) + + def _get_project(self, name): + result = {'urls': {}, 'digests': {}} + for root, dirs, files in os.walk(self.base_dir): + for fn in files: + if self.should_include(fn, root): + fn = os.path.join(root, fn) + url = urlunparse(('file', '', + pathname2url(os.path.abspath(fn)), + '', '', '')) + info = self.convert_url_to_download_info(url, name) + if info: + self._update_version_data(result, info) + if not self.recursive: + break + return result + + def get_distribution_names(self): + """ + Return all the distribution names known to this locator. + """ + result = set() + for root, dirs, files in os.walk(self.base_dir): + for fn in files: + if self.should_include(fn, root): + fn = os.path.join(root, fn) + url = urlunparse(('file', '', + pathname2url(os.path.abspath(fn)), + '', '', '')) + info = self.convert_url_to_download_info(url, None) + if info: + result.add(info['name']) + if not self.recursive: + break + return result + +class JSONLocator(Locator): + """ + This locator uses special extended metadata (not available on PyPI) and is + the basis of performant dependency resolution in distlib. Other locators + require archive downloads before dependencies can be determined! As you + might imagine, that can be slow. + """ + def get_distribution_names(self): + """ + Return all the distribution names known to this locator. + """ + raise NotImplementedError('Not available from this locator') + + def _get_project(self, name): + result = {'urls': {}, 'digests': {}} + data = get_project_data(name) + if data: + for info in data.get('files', []): + if info['ptype'] != 'sdist' or info['pyversion'] != 'source': + continue + # We don't store summary in project metadata as it makes + # the data bigger for no benefit during dependency + # resolution + dist = make_dist(data['name'], info['version'], + summary=data.get('summary', + 'Placeholder for summary'), + scheme=self.scheme) + md = dist.metadata + md.source_url = info['url'] + # TODO SHA256 digest + if 'digest' in info and info['digest']: + dist.digest = ('md5', info['digest']) + md.dependencies = info.get('requirements', {}) + dist.exports = info.get('exports', {}) + result[dist.version] = dist + result['urls'].setdefault(dist.version, set()).add(info['url']) + return result + +class DistPathLocator(Locator): + """ + This locator finds installed distributions in a path. It can be useful for + adding to an :class:`AggregatingLocator`. + """ + def __init__(self, distpath, **kwargs): + """ + Initialise an instance. + + :param distpath: A :class:`DistributionPath` instance to search. + """ + super(DistPathLocator, self).__init__(**kwargs) + assert isinstance(distpath, DistributionPath) + self.distpath = distpath + + def _get_project(self, name): + dist = self.distpath.get_distribution(name) + if dist is None: + result = {'urls': {}, 'digests': {}} + else: + result = { + dist.version: dist, + 'urls': {dist.version: set([dist.source_url])}, + 'digests': {dist.version: set([None])} + } + return result + + +class AggregatingLocator(Locator): + """ + This class allows you to chain and/or merge a list of locators. + """ + def __init__(self, *locators, **kwargs): + """ + Initialise an instance. + + :param locators: The list of locators to search. + :param kwargs: Passed to the superclass constructor, + except for: + * merge - if False (the default), the first successful + search from any of the locators is returned. If True, + the results from all locators are merged (this can be + slow). + """ + self.merge = kwargs.pop('merge', False) + self.locators = locators + super(AggregatingLocator, self).__init__(**kwargs) + + def clear_cache(self): + super(AggregatingLocator, self).clear_cache() + for locator in self.locators: + locator.clear_cache() + + def _set_scheme(self, value): + self._scheme = value + for locator in self.locators: + locator.scheme = value + + scheme = property(Locator.scheme.fget, _set_scheme) + + def _get_project(self, name): + result = {} + for locator in self.locators: + d = locator.get_project(name) + if d: + if self.merge: + files = result.get('urls', {}) + digests = result.get('digests', {}) + # next line could overwrite result['urls'], result['digests'] + result.update(d) + df = result.get('urls') + if files and df: + for k, v in files.items(): + if k in df: + df[k] |= v + else: + df[k] = v + dd = result.get('digests') + if digests and dd: + dd.update(digests) + else: + # See issue #18. If any dists are found and we're looking + # for specific constraints, we only return something if + # a match is found. For example, if a DirectoryLocator + # returns just foo (1.0) while we're looking for + # foo (>= 2.0), we'll pretend there was nothing there so + # that subsequent locators can be queried. Otherwise we + # would just return foo (1.0) which would then lead to a + # failure to find foo (>= 2.0), because other locators + # weren't searched. Note that this only matters when + # merge=False. + if self.matcher is None: + found = True + else: + found = False + for k in d: + if self.matcher.match(k): + found = True + break + if found: + result = d + break + return result + + def get_distribution_names(self): + """ + Return all the distribution names known to this locator. + """ + result = set() + for locator in self.locators: + try: + result |= locator.get_distribution_names() + except NotImplementedError: + pass + return result + + +# We use a legacy scheme simply because most of the dists on PyPI use legacy +# versions which don't conform to PEP 426 / PEP 440. +default_locator = AggregatingLocator( + JSONLocator(), + SimpleScrapingLocator('https://pypi.python.org/simple/', + timeout=3.0), + scheme='legacy') + +locate = default_locator.locate + +NAME_VERSION_RE = re.compile(r'(?P<name>[\w-]+)\s*' + r'\(\s*(==\s*)?(?P<ver>[^)]+)\)$') + +class DependencyFinder(object): + """ + Locate dependencies for distributions. + """ + + def __init__(self, locator=None): + """ + Initialise an instance, using the specified locator + to locate distributions. + """ + self.locator = locator or default_locator + self.scheme = get_scheme(self.locator.scheme) + + def add_distribution(self, dist): + """ + Add a distribution to the finder. This will update internal information + about who provides what. + :param dist: The distribution to add. + """ + logger.debug('adding distribution %s', dist) + name = dist.key + self.dists_by_name[name] = dist + self.dists[(name, dist.version)] = dist + for p in dist.provides: + name, version = parse_name_and_version(p) + logger.debug('Add to provided: %s, %s, %s', name, version, dist) + self.provided.setdefault(name, set()).add((version, dist)) + + def remove_distribution(self, dist): + """ + Remove a distribution from the finder. This will update internal + information about who provides what. + :param dist: The distribution to remove. + """ + logger.debug('removing distribution %s', dist) + name = dist.key + del self.dists_by_name[name] + del self.dists[(name, dist.version)] + for p in dist.provides: + name, version = parse_name_and_version(p) + logger.debug('Remove from provided: %s, %s, %s', name, version, dist) + s = self.provided[name] + s.remove((version, dist)) + if not s: + del self.provided[name] + + def get_matcher(self, reqt): + """ + Get a version matcher for a requirement. + :param reqt: The requirement + :type reqt: str + :return: A version matcher (an instance of + :class:`distlib.version.Matcher`). + """ + try: + matcher = self.scheme.matcher(reqt) + except UnsupportedVersionError: # pragma: no cover + # XXX compat-mode if cannot read the version + name = reqt.split()[0] + matcher = self.scheme.matcher(name) + return matcher + + def find_providers(self, reqt): + """ + Find the distributions which can fulfill a requirement. + + :param reqt: The requirement. + :type reqt: str + :return: A set of distribution which can fulfill the requirement. + """ + matcher = self.get_matcher(reqt) + name = matcher.key # case-insensitive + result = set() + provided = self.provided + if name in provided: + for version, provider in provided[name]: + try: + match = matcher.match(version) + except UnsupportedVersionError: + match = False + + if match: + result.add(provider) + break + return result + + def try_to_replace(self, provider, other, problems): + """ + Attempt to replace one provider with another. This is typically used + when resolving dependencies from multiple sources, e.g. A requires + (B >= 1.0) while C requires (B >= 1.1). + + For successful replacement, ``provider`` must meet all the requirements + which ``other`` fulfills. + + :param provider: The provider we are trying to replace with. + :param other: The provider we're trying to replace. + :param problems: If False is returned, this will contain what + problems prevented replacement. This is currently + a tuple of the literal string 'cantreplace', + ``provider``, ``other`` and the set of requirements + that ``provider`` couldn't fulfill. + :return: True if we can replace ``other`` with ``provider``, else + False. + """ + rlist = self.reqts[other] + unmatched = set() + for s in rlist: + matcher = self.get_matcher(s) + if not matcher.match(provider.version): + unmatched.add(s) + if unmatched: + # can't replace other with provider + problems.add(('cantreplace', provider, other, + frozenset(unmatched))) + result = False + else: + # can replace other with provider + self.remove_distribution(other) + del self.reqts[other] + for s in rlist: + self.reqts.setdefault(provider, set()).add(s) + self.add_distribution(provider) + result = True + return result + + def find(self, requirement, meta_extras=None, prereleases=False): + """ + Find a distribution and all distributions it depends on. + + :param requirement: The requirement specifying the distribution to + find, or a Distribution instance. + :param meta_extras: A list of meta extras such as :test:, :build: and + so on. + :param prereleases: If ``True``, allow pre-release versions to be + returned - otherwise, don't return prereleases + unless they're all that's available. + + Return a set of :class:`Distribution` instances and a set of + problems. + + The distributions returned should be such that they have the + :attr:`required` attribute set to ``True`` if they were + from the ``requirement`` passed to ``find()``, and they have the + :attr:`build_time_dependency` attribute set to ``True`` unless they + are post-installation dependencies of the ``requirement``. + + The problems should be a tuple consisting of the string + ``'unsatisfied'`` and the requirement which couldn't be satisfied + by any distribution known to the locator. + """ + + self.provided = {} + self.dists = {} + self.dists_by_name = {} + self.reqts = {} + + meta_extras = set(meta_extras or []) + if ':*:' in meta_extras: + meta_extras.remove(':*:') + # :meta: and :run: are implicitly included + meta_extras |= set([':test:', ':build:', ':dev:']) + + if isinstance(requirement, Distribution): + dist = odist = requirement + logger.debug('passed %s as requirement', odist) + else: + dist = odist = self.locator.locate(requirement, + prereleases=prereleases) + if dist is None: + raise DistlibException('Unable to locate %r' % requirement) + logger.debug('located %s', odist) + dist.requested = True + problems = set() + todo = set([dist]) + install_dists = set([odist]) + while todo: + dist = todo.pop() + name = dist.key # case-insensitive + if name not in self.dists_by_name: + self.add_distribution(dist) + else: + #import pdb; pdb.set_trace() + other = self.dists_by_name[name] + if other != dist: + self.try_to_replace(dist, other, problems) + + ireqts = dist.run_requires | dist.meta_requires + sreqts = dist.build_requires + ereqts = set() + if meta_extras and dist in install_dists: + for key in ('test', 'build', 'dev'): + e = ':%s:' % key + if e in meta_extras: + ereqts |= getattr(dist, '%s_requires' % key) + all_reqts = ireqts | sreqts | ereqts + for r in all_reqts: + providers = self.find_providers(r) + if not providers: + logger.debug('No providers found for %r', r) + provider = self.locator.locate(r, prereleases=prereleases) + # If no provider is found and we didn't consider + # prereleases, consider them now. + if provider is None and not prereleases: + provider = self.locator.locate(r, prereleases=True) + if provider is None: + logger.debug('Cannot satisfy %r', r) + problems.add(('unsatisfied', r)) + else: + n, v = provider.key, provider.version + if (n, v) not in self.dists: + todo.add(provider) + providers.add(provider) + if r in ireqts and dist in install_dists: + install_dists.add(provider) + logger.debug('Adding %s to install_dists', + provider.name_and_version) + for p in providers: + name = p.key + if name not in self.dists_by_name: + self.reqts.setdefault(p, set()).add(r) + else: + other = self.dists_by_name[name] + if other != p: + # see if other can be replaced by p + self.try_to_replace(p, other, problems) + + dists = set(self.dists.values()) + for dist in dists: + dist.build_time_dependency = dist not in install_dists + if dist.build_time_dependency: + logger.debug('%s is a build-time dependency only.', + dist.name_and_version) + logger.debug('find done for %s', odist) + return dists, problems diff --git a/venv2/lib/python2.7/site-packages/pip/_vendor/distlib/manifest.py b/venv2/lib/python2.7/site-packages/pip/_vendor/distlib/manifest.py new file mode 100644 index 0000000..ca0fe44 --- /dev/null +++ b/venv2/lib/python2.7/site-packages/pip/_vendor/distlib/manifest.py @@ -0,0 +1,393 @@ +# -*- coding: utf-8 -*- +# +# Copyright (C) 2012-2013 Python Software Foundation. +# See LICENSE.txt and CONTRIBUTORS.txt. +# +""" +Class representing the list of files in a distribution. + +Equivalent to distutils.filelist, but fixes some problems. +""" +import fnmatch +import logging +import os +import re +import sys + +from . import DistlibException +from .compat import fsdecode +from .util import convert_path + + +__all__ = ['Manifest'] + +logger = logging.getLogger(__name__) + +# a \ followed by some spaces + EOL +_COLLAPSE_PATTERN = re.compile('\\\\w*\n', re.M) +_COMMENTED_LINE = re.compile('#.*?(?=\n)|\n(?=$)', re.M | re.S) + +# +# Due to the different results returned by fnmatch.translate, we need +# to do slightly different processing for Python 2.7 and 3.2 ... this needed +# to be brought in for Python 3.6 onwards. +# +_PYTHON_VERSION = sys.version_info[:2] + +class Manifest(object): + """A list of files built by on exploring the filesystem and filtered by + applying various patterns to what we find there. + """ + + def __init__(self, base=None): + """ + Initialise an instance. + + :param base: The base directory to explore under. + """ + self.base = os.path.abspath(os.path.normpath(base or os.getcwd())) + self.prefix = self.base + os.sep + self.allfiles = None + self.files = set() + + # + # Public API + # + + def findall(self): + """Find all files under the base and set ``allfiles`` to the absolute + pathnames of files found. + """ + from stat import S_ISREG, S_ISDIR, S_ISLNK + + self.allfiles = allfiles = [] + root = self.base + stack = [root] + pop = stack.pop + push = stack.append + + while stack: + root = pop() + names = os.listdir(root) + + for name in names: + fullname = os.path.join(root, name) + + # Avoid excess stat calls -- just one will do, thank you! + stat = os.stat(fullname) + mode = stat.st_mode + if S_ISREG(mode): + allfiles.append(fsdecode(fullname)) + elif S_ISDIR(mode) and not S_ISLNK(mode): + push(fullname) + + def add(self, item): + """ + Add a file to the manifest. + + :param item: The pathname to add. This can be relative to the base. + """ + if not item.startswith(self.prefix): + item = os.path.join(self.base, item) + self.files.add(os.path.normpath(item)) + + def add_many(self, items): + """ + Add a list of files to the manifest. + + :param items: The pathnames to add. These can be relative to the base. + """ + for item in items: + self.add(item) + + def sorted(self, wantdirs=False): + """ + Return sorted files in directory order + """ + + def add_dir(dirs, d): + dirs.add(d) + logger.debug('add_dir added %s', d) + if d != self.base: + parent, _ = os.path.split(d) + assert parent not in ('', '/') + add_dir(dirs, parent) + + result = set(self.files) # make a copy! + if wantdirs: + dirs = set() + for f in result: + add_dir(dirs, os.path.dirname(f)) + result |= dirs + return [os.path.join(*path_tuple) for path_tuple in + sorted(os.path.split(path) for path in result)] + + def clear(self): + """Clear all collected files.""" + self.files = set() + self.allfiles = [] + + def process_directive(self, directive): + """ + Process a directive which either adds some files from ``allfiles`` to + ``files``, or removes some files from ``files``. + + :param directive: The directive to process. This should be in a format + compatible with distutils ``MANIFEST.in`` files: + + http://docs.python.org/distutils/sourcedist.html#commands + """ + # Parse the line: split it up, make sure the right number of words + # is there, and return the relevant words. 'action' is always + # defined: it's the first word of the line. Which of the other + # three are defined depends on the action; it'll be either + # patterns, (dir and patterns), or (dirpattern). + action, patterns, thedir, dirpattern = self._parse_directive(directive) + + # OK, now we know that the action is valid and we have the + # right number of words on the line for that action -- so we + # can proceed with minimal error-checking. + if action == 'include': + for pattern in patterns: + if not self._include_pattern(pattern, anchor=True): + logger.warning('no files found matching %r', pattern) + + elif action == 'exclude': + for pattern in patterns: + found = self._exclude_pattern(pattern, anchor=True) + #if not found: + # logger.warning('no previously-included files ' + # 'found matching %r', pattern) + + elif action == 'global-include': + for pattern in patterns: + if not self._include_pattern(pattern, anchor=False): + logger.warning('no files found matching %r ' + 'anywhere in distribution', pattern) + + elif action == 'global-exclude': + for pattern in patterns: + found = self._exclude_pattern(pattern, anchor=False) + #if not found: + # logger.warning('no previously-included files ' + # 'matching %r found anywhere in ' + # 'distribution', pattern) + + elif action == 'recursive-include': + for pattern in patterns: + if not self._include_pattern(pattern, prefix=thedir): + logger.warning('no files found matching %r ' + 'under directory %r', pattern, thedir) + + elif action == 'recursive-exclude': + for pattern in patterns: + found = self._exclude_pattern(pattern, prefix=thedir) + #if not found: + # logger.warning('no previously-included files ' + # 'matching %r found under directory %r', + # pattern, thedir) + + elif action == 'graft': + if not self._include_pattern(None, prefix=dirpattern): + logger.warning('no directories found matching %r', + dirpattern) + + elif action == 'prune': + if not self._exclude_pattern(None, prefix=dirpattern): + logger.warning('no previously-included directories found ' + 'matching %r', dirpattern) + else: # pragma: no cover + # This should never happen, as it should be caught in + # _parse_template_line + raise DistlibException( + 'invalid action %r' % action) + + # + # Private API + # + + def _parse_directive(self, directive): + """ + Validate a directive. + :param directive: The directive to validate. + :return: A tuple of action, patterns, thedir, dir_patterns + """ + words = directive.split() + if len(words) == 1 and words[0] not in ('include', 'exclude', + 'global-include', + 'global-exclude', + 'recursive-include', + 'recursive-exclude', + 'graft', 'prune'): + # no action given, let's use the default 'include' + words.insert(0, 'include') + + action = words[0] + patterns = thedir = dir_pattern = None + + if action in ('include', 'exclude', + 'global-include', 'global-exclude'): + if len(words) < 2: + raise DistlibException( + '%r expects <pattern1> <pattern2> ...' % action) + + patterns = [convert_path(word) for word in words[1:]] + + elif action in ('recursive-include', 'recursive-exclude'): + if len(words) < 3: + raise DistlibException( + '%r expects <dir> <pattern1> <pattern2> ...' % action) + + thedir = convert_path(words[1]) + patterns = [convert_path(word) for word in words[2:]] + + elif action in ('graft', 'prune'): + if len(words) != 2: + raise DistlibException( + '%r expects a single <dir_pattern>' % action) + + dir_pattern = convert_path(words[1]) + + else: + raise DistlibException('unknown action %r' % action) + + return action, patterns, thedir, dir_pattern + + def _include_pattern(self, pattern, anchor=True, prefix=None, + is_regex=False): + """Select strings (presumably filenames) from 'self.files' that + match 'pattern', a Unix-style wildcard (glob) pattern. + + Patterns are not quite the same as implemented by the 'fnmatch' + module: '*' and '?' match non-special characters, where "special" + is platform-dependent: slash on Unix; colon, slash, and backslash on + DOS/Windows; and colon on Mac OS. + + If 'anchor' is true (the default), then the pattern match is more + stringent: "*.py" will match "foo.py" but not "foo/bar.py". If + 'anchor' is false, both of these will match. + + If 'prefix' is supplied, then only filenames starting with 'prefix' + (itself a pattern) and ending with 'pattern', with anything in between + them, will match. 'anchor' is ignored in this case. + + If 'is_regex' is true, 'anchor' and 'prefix' are ignored, and + 'pattern' is assumed to be either a string containing a regex or a + regex object -- no translation is done, the regex is just compiled + and used as-is. + + Selected strings will be added to self.files. + + Return True if files are found. + """ + # XXX docstring lying about what the special chars are? + found = False + pattern_re = self._translate_pattern(pattern, anchor, prefix, is_regex) + + # delayed loading of allfiles list + if self.allfiles is None: + self.findall() + + for name in self.allfiles: + if pattern_re.search(name): + self.files.add(name) + found = True + return found + + def _exclude_pattern(self, pattern, anchor=True, prefix=None, + is_regex=False): + """Remove strings (presumably filenames) from 'files' that match + 'pattern'. + + Other parameters are the same as for 'include_pattern()', above. + The list 'self.files' is modified in place. Return True if files are + found. + + This API is public to allow e.g. exclusion of SCM subdirs, e.g. when + packaging source distributions + """ + found = False + pattern_re = self._translate_pattern(pattern, anchor, prefix, is_regex) + for f in list(self.files): + if pattern_re.search(f): + self.files.remove(f) + found = True + return found + + def _translate_pattern(self, pattern, anchor=True, prefix=None, + is_regex=False): + """Translate a shell-like wildcard pattern to a compiled regular + expression. + + Return the compiled regex. If 'is_regex' true, + then 'pattern' is directly compiled to a regex (if it's a string) + or just returned as-is (assumes it's a regex object). + """ + if is_regex: + if isinstance(pattern, str): + return re.compile(pattern) + else: + return pattern + + if _PYTHON_VERSION > (3, 2): + # ditch start and end characters + start, _, end = self._glob_to_re('_').partition('_') + + if pattern: + pattern_re = self._glob_to_re(pattern) + if _PYTHON_VERSION > (3, 2): + assert pattern_re.startswith(start) and pattern_re.endswith(end) + else: + pattern_re = '' + + base = re.escape(os.path.join(self.base, '')) + if prefix is not None: + # ditch end of pattern character + if _PYTHON_VERSION <= (3, 2): + empty_pattern = self._glob_to_re('') + prefix_re = self._glob_to_re(prefix)[:-len(empty_pattern)] + else: + prefix_re = self._glob_to_re(prefix) + assert prefix_re.startswith(start) and prefix_re.endswith(end) + prefix_re = prefix_re[len(start): len(prefix_re) - len(end)] + sep = os.sep + if os.sep == '\\': + sep = r'\\' + if _PYTHON_VERSION <= (3, 2): + pattern_re = '^' + base + sep.join((prefix_re, + '.*' + pattern_re)) + else: + pattern_re = pattern_re[len(start): len(pattern_re) - len(end)] + pattern_re = r'%s%s%s%s.*%s%s' % (start, base, prefix_re, sep, + pattern_re, end) + else: # no prefix -- respect anchor flag + if anchor: + if _PYTHON_VERSION <= (3, 2): + pattern_re = '^' + base + pattern_re + else: + pattern_re = r'%s%s%s' % (start, base, pattern_re[len(start):]) + + return re.compile(pattern_re) + + def _glob_to_re(self, pattern): + """Translate a shell-like glob pattern to a regular expression. + + Return a string containing the regex. Differs from + 'fnmatch.translate()' in that '*' does not match "special characters" + (which are platform-specific). + """ + pattern_re = fnmatch.translate(pattern) + + # '?' and '*' in the glob pattern become '.' and '.*' in the RE, which + # IMHO is wrong -- '?' and '*' aren't supposed to match slash in Unix, + # and by extension they shouldn't match such "special characters" under + # any OS. So change all non-escaped dots in the RE to match any + # character except the special characters (currently: just os.sep). + sep = os.sep + if os.sep == '\\': + # we're using a regex to manipulate a regex, so we need + # to escape the backslash twice + sep = r'\\\\' + escaped = r'\1[^%s]' % sep + pattern_re = re.sub(r'((?<!\\)(\\\\)*)\.', escaped, pattern_re) + return pattern_re diff --git a/venv2/lib/python2.7/site-packages/pip/_vendor/distlib/markers.py b/venv2/lib/python2.7/site-packages/pip/_vendor/distlib/markers.py new file mode 100644 index 0000000..ee1f3e2 --- /dev/null +++ b/venv2/lib/python2.7/site-packages/pip/_vendor/distlib/markers.py @@ -0,0 +1,131 @@ +# -*- coding: utf-8 -*- +# +# Copyright (C) 2012-2017 Vinay Sajip. +# Licensed to the Python Software Foundation under a contributor agreement. +# See LICENSE.txt and CONTRIBUTORS.txt. +# +""" +Parser for the environment markers micro-language defined in PEP 508. +""" + +# Note: In PEP 345, the micro-language was Python compatible, so the ast +# module could be used to parse it. However, PEP 508 introduced operators such +# as ~= and === which aren't in Python, necessitating a different approach. + +import os +import sys +import platform +import re + +from .compat import python_implementation, urlparse, string_types +from .util import in_venv, parse_marker + +__all__ = ['interpret'] + +def _is_literal(o): + if not isinstance(o, string_types) or not o: + return False + return o[0] in '\'"' + +class Evaluator(object): + """ + This class is used to evaluate marker expessions. + """ + + operations = { + '==': lambda x, y: x == y, + '===': lambda x, y: x == y, + '~=': lambda x, y: x == y or x > y, + '!=': lambda x, y: x != y, + '<': lambda x, y: x < y, + '<=': lambda x, y: x == y or x < y, + '>': lambda x, y: x > y, + '>=': lambda x, y: x == y or x > y, + 'and': lambda x, y: x and y, + 'or': lambda x, y: x or y, + 'in': lambda x, y: x in y, + 'not in': lambda x, y: x not in y, + } + + def evaluate(self, expr, context): + """ + Evaluate a marker expression returned by the :func:`parse_requirement` + function in the specified context. + """ + if isinstance(expr, string_types): + if expr[0] in '\'"': + result = expr[1:-1] + else: + if expr not in context: + raise SyntaxError('unknown variable: %s' % expr) + result = context[expr] + else: + assert isinstance(expr, dict) + op = expr['op'] + if op not in self.operations: + raise NotImplementedError('op not implemented: %s' % op) + elhs = expr['lhs'] + erhs = expr['rhs'] + if _is_literal(expr['lhs']) and _is_literal(expr['rhs']): + raise SyntaxError('invalid comparison: %s %s %s' % (elhs, op, erhs)) + + lhs = self.evaluate(elhs, context) + rhs = self.evaluate(erhs, context) + result = self.operations[op](lhs, rhs) + return result + +def default_context(): + def format_full_version(info): + version = '%s.%s.%s' % (info.major, info.minor, info.micro) + kind = info.releaselevel + if kind != 'final': + version += kind[0] + str(info.serial) + return version + + if hasattr(sys, 'implementation'): + implementation_version = format_full_version(sys.implementation.version) + implementation_name = sys.implementation.name + else: + implementation_version = '0' + implementation_name = '' + + result = { + 'implementation_name': implementation_name, + 'implementation_version': implementation_version, + 'os_name': os.name, + 'platform_machine': platform.machine(), + 'platform_python_implementation': platform.python_implementation(), + 'platform_release': platform.release(), + 'platform_system': platform.system(), + 'platform_version': platform.version(), + 'platform_in_venv': str(in_venv()), + 'python_full_version': platform.python_version(), + 'python_version': platform.python_version()[:3], + 'sys_platform': sys.platform, + } + return result + +DEFAULT_CONTEXT = default_context() +del default_context + +evaluator = Evaluator() + +def interpret(marker, execution_context=None): + """ + Interpret a marker and return a result depending on environment. + + :param marker: The marker to interpret. + :type marker: str + :param execution_context: The context used for name lookup. + :type execution_context: mapping + """ + try: + expr, rest = parse_marker(marker) + except Exception as e: + raise SyntaxError('Unable to interpret marker syntax: %s: %s' % (marker, e)) + if rest and rest[0] != '#': + raise SyntaxError('unexpected trailing data in marker: %s: %s' % (marker, rest)) + context = dict(DEFAULT_CONTEXT) + if execution_context: + context.update(execution_context) + return evaluator.evaluate(expr, context) diff --git a/venv2/lib/python2.7/site-packages/pip/_vendor/distlib/metadata.py b/venv2/lib/python2.7/site-packages/pip/_vendor/distlib/metadata.py new file mode 100644 index 0000000..77eed7f --- /dev/null +++ b/venv2/lib/python2.7/site-packages/pip/_vendor/distlib/metadata.py @@ -0,0 +1,1094 @@ +# -*- coding: utf-8 -*- +# +# Copyright (C) 2012 The Python Software Foundation. +# See LICENSE.txt and CONTRIBUTORS.txt. +# +"""Implementation of the Metadata for Python packages PEPs. + +Supports all metadata formats (1.0, 1.1, 1.2, and 2.0 experimental). +""" +from __future__ import unicode_literals + +import codecs +from email import message_from_file +import json +import logging +import re + + +from . import DistlibException, __version__ +from .compat import StringIO, string_types, text_type +from .markers import interpret +from .util import extract_by_key, get_extras +from .version import get_scheme, PEP440_VERSION_RE + +logger = logging.getLogger(__name__) + + +class MetadataMissingError(DistlibException): + """A required metadata is missing""" + + +class MetadataConflictError(DistlibException): + """Attempt to read or write metadata fields that are conflictual.""" + + +class MetadataUnrecognizedVersionError(DistlibException): + """Unknown metadata version number.""" + + +class MetadataInvalidError(DistlibException): + """A metadata value is invalid""" + +# public API of this module +__all__ = ['Metadata', 'PKG_INFO_ENCODING', 'PKG_INFO_PREFERRED_VERSION'] + +# Encoding used for the PKG-INFO files +PKG_INFO_ENCODING = 'utf-8' + +# preferred version. Hopefully will be changed +# to 1.2 once PEP 345 is supported everywhere +PKG_INFO_PREFERRED_VERSION = '1.1' + +_LINE_PREFIX_1_2 = re.compile('\n \\|') +_LINE_PREFIX_PRE_1_2 = re.compile('\n ') +_241_FIELDS = ('Metadata-Version', 'Name', 'Version', 'Platform', + 'Summary', 'Description', + 'Keywords', 'Home-page', 'Author', 'Author-email', + 'License') + +_314_FIELDS = ('Metadata-Version', 'Name', 'Version', 'Platform', + 'Supported-Platform', 'Summary', 'Description', + 'Keywords', 'Home-page', 'Author', 'Author-email', + 'License', 'Classifier', 'Download-URL', 'Obsoletes', + 'Provides', 'Requires') + +_314_MARKERS = ('Obsoletes', 'Provides', 'Requires', 'Classifier', + 'Download-URL') + +_345_FIELDS = ('Metadata-Version', 'Name', 'Version', 'Platform', + 'Supported-Platform', 'Summary', 'Description', + 'Keywords', 'Home-page', 'Author', 'Author-email', + 'Maintainer', 'Maintainer-email', 'License', + 'Classifier', 'Download-URL', 'Obsoletes-Dist', + 'Project-URL', 'Provides-Dist', 'Requires-Dist', + 'Requires-Python', 'Requires-External') + +_345_MARKERS = ('Provides-Dist', 'Requires-Dist', 'Requires-Python', + 'Obsoletes-Dist', 'Requires-External', 'Maintainer', + 'Maintainer-email', 'Project-URL') + +_426_FIELDS = ('Metadata-Version', 'Name', 'Version', 'Platform', + 'Supported-Platform', 'Summary', 'Description', + 'Keywords', 'Home-page', 'Author', 'Author-email', + 'Maintainer', 'Maintainer-email', 'License', + 'Classifier', 'Download-URL', 'Obsoletes-Dist', + 'Project-URL', 'Provides-Dist', 'Requires-Dist', + 'Requires-Python', 'Requires-External', 'Private-Version', + 'Obsoleted-By', 'Setup-Requires-Dist', 'Extension', + 'Provides-Extra') + +_426_MARKERS = ('Private-Version', 'Provides-Extra', 'Obsoleted-By', + 'Setup-Requires-Dist', 'Extension') + +# See issue #106: Sometimes 'Requires' occurs wrongly in the metadata. Include +# it in the tuple literal below to allow it (for now) +_566_FIELDS = _426_FIELDS + ('Description-Content-Type', 'Requires') + +_566_MARKERS = ('Description-Content-Type',) + +_ALL_FIELDS = set() +_ALL_FIELDS.update(_241_FIELDS) +_ALL_FIELDS.update(_314_FIELDS) +_ALL_FIELDS.update(_345_FIELDS) +_ALL_FIELDS.update(_426_FIELDS) +_ALL_FIELDS.update(_566_FIELDS) + +EXTRA_RE = re.compile(r'''extra\s*==\s*("([^"]+)"|'([^']+)')''') + + +def _version2fieldlist(version): + if version == '1.0': + return _241_FIELDS + elif version == '1.1': + return _314_FIELDS + elif version == '1.2': + return _345_FIELDS + elif version in ('1.3', '2.1'): + return _345_FIELDS + _566_FIELDS + elif version == '2.0': + return _426_FIELDS + raise MetadataUnrecognizedVersionError(version) + + +def _best_version(fields): + """Detect the best version depending on the fields used.""" + def _has_marker(keys, markers): + for marker in markers: + if marker in keys: + return True + return False + + keys = [] + for key, value in fields.items(): + if value in ([], 'UNKNOWN', None): + continue + keys.append(key) + + possible_versions = ['1.0', '1.1', '1.2', '1.3', '2.0', '2.1'] + + # first let's try to see if a field is not part of one of the version + for key in keys: + if key not in _241_FIELDS and '1.0' in possible_versions: + possible_versions.remove('1.0') + logger.debug('Removed 1.0 due to %s', key) + if key not in _314_FIELDS and '1.1' in possible_versions: + possible_versions.remove('1.1') + logger.debug('Removed 1.1 due to %s', key) + if key not in _345_FIELDS and '1.2' in possible_versions: + possible_versions.remove('1.2') + logger.debug('Removed 1.2 due to %s', key) + if key not in _566_FIELDS and '1.3' in possible_versions: + possible_versions.remove('1.3') + logger.debug('Removed 1.3 due to %s', key) + if key not in _566_FIELDS and '2.1' in possible_versions: + if key != 'Description': # In 2.1, description allowed after headers + possible_versions.remove('2.1') + logger.debug('Removed 2.1 due to %s', key) + if key not in _426_FIELDS and '2.0' in possible_versions: + possible_versions.remove('2.0') + logger.debug('Removed 2.0 due to %s', key) + + # possible_version contains qualified versions + if len(possible_versions) == 1: + return possible_versions[0] # found ! + elif len(possible_versions) == 0: + logger.debug('Out of options - unknown metadata set: %s', fields) + raise MetadataConflictError('Unknown metadata set') + + # let's see if one unique marker is found + is_1_1 = '1.1' in possible_versions and _has_marker(keys, _314_MARKERS) + is_1_2 = '1.2' in possible_versions and _has_marker(keys, _345_MARKERS) + is_2_1 = '2.1' in possible_versions and _has_marker(keys, _566_MARKERS) + is_2_0 = '2.0' in possible_versions and _has_marker(keys, _426_MARKERS) + if int(is_1_1) + int(is_1_2) + int(is_2_1) + int(is_2_0) > 1: + raise MetadataConflictError('You used incompatible 1.1/1.2/2.0/2.1 fields') + + # we have the choice, 1.0, or 1.2, or 2.0 + # - 1.0 has a broken Summary field but works with all tools + # - 1.1 is to avoid + # - 1.2 fixes Summary but has little adoption + # - 2.0 adds more features and is very new + if not is_1_1 and not is_1_2 and not is_2_1 and not is_2_0: + # we couldn't find any specific marker + if PKG_INFO_PREFERRED_VERSION in possible_versions: + return PKG_INFO_PREFERRED_VERSION + if is_1_1: + return '1.1' + if is_1_2: + return '1.2' + if is_2_1: + return '2.1' + + return '2.0' + +_ATTR2FIELD = { + 'metadata_version': 'Metadata-Version', + 'name': 'Name', + 'version': 'Version', + 'platform': 'Platform', + 'supported_platform': 'Supported-Platform', + 'summary': 'Summary', + 'description': 'Description', + 'keywords': 'Keywords', + 'home_page': 'Home-page', + 'author': 'Author', + 'author_email': 'Author-email', + 'maintainer': 'Maintainer', + 'maintainer_email': 'Maintainer-email', + 'license': 'License', + 'classifier': 'Classifier', + 'download_url': 'Download-URL', + 'obsoletes_dist': 'Obsoletes-Dist', + 'provides_dist': 'Provides-Dist', + 'requires_dist': 'Requires-Dist', + 'setup_requires_dist': 'Setup-Requires-Dist', + 'requires_python': 'Requires-Python', + 'requires_external': 'Requires-External', + 'requires': 'Requires', + 'provides': 'Provides', + 'obsoletes': 'Obsoletes', + 'project_url': 'Project-URL', + 'private_version': 'Private-Version', + 'obsoleted_by': 'Obsoleted-By', + 'extension': 'Extension', + 'provides_extra': 'Provides-Extra', +} + +_PREDICATE_FIELDS = ('Requires-Dist', 'Obsoletes-Dist', 'Provides-Dist') +_VERSIONS_FIELDS = ('Requires-Python',) +_VERSION_FIELDS = ('Version',) +_LISTFIELDS = ('Platform', 'Classifier', 'Obsoletes', + 'Requires', 'Provides', 'Obsoletes-Dist', + 'Provides-Dist', 'Requires-Dist', 'Requires-External', + 'Project-URL', 'Supported-Platform', 'Setup-Requires-Dist', + 'Provides-Extra', 'Extension') +_LISTTUPLEFIELDS = ('Project-URL',) + +_ELEMENTSFIELD = ('Keywords',) + +_UNICODEFIELDS = ('Author', 'Maintainer', 'Summary', 'Description') + +_MISSING = object() + +_FILESAFE = re.compile('[^A-Za-z0-9.]+') + + +def _get_name_and_version(name, version, for_filename=False): + """Return the distribution name with version. + + If for_filename is true, return a filename-escaped form.""" + if for_filename: + # For both name and version any runs of non-alphanumeric or '.' + # characters are replaced with a single '-'. Additionally any + # spaces in the version string become '.' + name = _FILESAFE.sub('-', name) + version = _FILESAFE.sub('-', version.replace(' ', '.')) + return '%s-%s' % (name, version) + + +class LegacyMetadata(object): + """The legacy metadata of a release. + + Supports versions 1.0, 1.1 and 1.2 (auto-detected). You can + instantiate the class with one of these arguments (or none): + - *path*, the path to a metadata file + - *fileobj* give a file-like object with metadata as content + - *mapping* is a dict-like object + - *scheme* is a version scheme name + """ + # TODO document the mapping API and UNKNOWN default key + + def __init__(self, path=None, fileobj=None, mapping=None, + scheme='default'): + if [path, fileobj, mapping].count(None) < 2: + raise TypeError('path, fileobj and mapping are exclusive') + self._fields = {} + self.requires_files = [] + self._dependencies = None + self.scheme = scheme + if path is not None: + self.read(path) + elif fileobj is not None: + self.read_file(fileobj) + elif mapping is not None: + self.update(mapping) + self.set_metadata_version() + + def set_metadata_version(self): + self._fields['Metadata-Version'] = _best_version(self._fields) + + def _write_field(self, fileobj, name, value): + fileobj.write('%s: %s\n' % (name, value)) + + def __getitem__(self, name): + return self.get(name) + + def __setitem__(self, name, value): + return self.set(name, value) + + def __delitem__(self, name): + field_name = self._convert_name(name) + try: + del self._fields[field_name] + except KeyError: + raise KeyError(name) + + def __contains__(self, name): + return (name in self._fields or + self._convert_name(name) in self._fields) + + def _convert_name(self, name): + if name in _ALL_FIELDS: + return name + name = name.replace('-', '_').lower() + return _ATTR2FIELD.get(name, name) + + def _default_value(self, name): + if name in _LISTFIELDS or name in _ELEMENTSFIELD: + return [] + return 'UNKNOWN' + + def _remove_line_prefix(self, value): + if self.metadata_version in ('1.0', '1.1'): + return _LINE_PREFIX_PRE_1_2.sub('\n', value) + else: + return _LINE_PREFIX_1_2.sub('\n', value) + + def __getattr__(self, name): + if name in _ATTR2FIELD: + return self[name] + raise AttributeError(name) + + # + # Public API + # + +# dependencies = property(_get_dependencies, _set_dependencies) + + def get_fullname(self, filesafe=False): + """Return the distribution name with version. + + If filesafe is true, return a filename-escaped form.""" + return _get_name_and_version(self['Name'], self['Version'], filesafe) + + def is_field(self, name): + """return True if name is a valid metadata key""" + name = self._convert_name(name) + return name in _ALL_FIELDS + + def is_multi_field(self, name): + name = self._convert_name(name) + return name in _LISTFIELDS + + def read(self, filepath): + """Read the metadata values from a file path.""" + fp = codecs.open(filepath, 'r', encoding='utf-8') + try: + self.read_file(fp) + finally: + fp.close() + + def read_file(self, fileob): + """Read the metadata values from a file object.""" + msg = message_from_file(fileob) + self._fields['Metadata-Version'] = msg['metadata-version'] + + # When reading, get all the fields we can + for field in _ALL_FIELDS: + if field not in msg: + continue + if field in _LISTFIELDS: + # we can have multiple lines + values = msg.get_all(field) + if field in _LISTTUPLEFIELDS and values is not None: + values = [tuple(value.split(',')) for value in values] + self.set(field, values) + else: + # single line + value = msg[field] + if value is not None and value != 'UNKNOWN': + self.set(field, value) + # logger.debug('Attempting to set metadata for %s', self) + # self.set_metadata_version() + + def write(self, filepath, skip_unknown=False): + """Write the metadata fields to filepath.""" + fp = codecs.open(filepath, 'w', encoding='utf-8') + try: + self.write_file(fp, skip_unknown) + finally: + fp.close() + + def write_file(self, fileobject, skip_unknown=False): + """Write the PKG-INFO format data to a file object.""" + self.set_metadata_version() + + for field in _version2fieldlist(self['Metadata-Version']): + values = self.get(field) + if skip_unknown and values in ('UNKNOWN', [], ['UNKNOWN']): + continue + if field in _ELEMENTSFIELD: + self._write_field(fileobject, field, ','.join(values)) + continue + if field not in _LISTFIELDS: + if field == 'Description': + if self.metadata_version in ('1.0', '1.1'): + values = values.replace('\n', '\n ') + else: + values = values.replace('\n', '\n |') + values = [values] + + if field in _LISTTUPLEFIELDS: + values = [','.join(value) for value in values] + + for value in values: + self._write_field(fileobject, field, value) + + def update(self, other=None, **kwargs): + """Set metadata values from the given iterable `other` and kwargs. + + Behavior is like `dict.update`: If `other` has a ``keys`` method, + they are looped over and ``self[key]`` is assigned ``other[key]``. + Else, ``other`` is an iterable of ``(key, value)`` iterables. + + Keys that don't match a metadata field or that have an empty value are + dropped. + """ + def _set(key, value): + if key in _ATTR2FIELD and value: + self.set(self._convert_name(key), value) + + if not other: + # other is None or empty container + pass + elif hasattr(other, 'keys'): + for k in other.keys(): + _set(k, other[k]) + else: + for k, v in other: + _set(k, v) + + if kwargs: + for k, v in kwargs.items(): + _set(k, v) + + def set(self, name, value): + """Control then set a metadata field.""" + name = self._convert_name(name) + + if ((name in _ELEMENTSFIELD or name == 'Platform') and + not isinstance(value, (list, tuple))): + if isinstance(value, string_types): + value = [v.strip() for v in value.split(',')] + else: + value = [] + elif (name in _LISTFIELDS and + not isinstance(value, (list, tuple))): + if isinstance(value, string_types): + value = [value] + else: + value = [] + + if logger.isEnabledFor(logging.WARNING): + project_name = self['Name'] + + scheme = get_scheme(self.scheme) + if name in _PREDICATE_FIELDS and value is not None: + for v in value: + # check that the values are valid + if not scheme.is_valid_matcher(v.split(';')[0]): + logger.warning( + "'%s': '%s' is not valid (field '%s')", + project_name, v, name) + # FIXME this rejects UNKNOWN, is that right? + elif name in _VERSIONS_FIELDS and value is not None: + if not scheme.is_valid_constraint_list(value): + logger.warning("'%s': '%s' is not a valid version (field '%s')", + project_name, value, name) + elif name in _VERSION_FIELDS and value is not None: + if not scheme.is_valid_version(value): + logger.warning("'%s': '%s' is not a valid version (field '%s')", + project_name, value, name) + + if name in _UNICODEFIELDS: + if name == 'Description': + value = self._remove_line_prefix(value) + + self._fields[name] = value + + def get(self, name, default=_MISSING): + """Get a metadata field.""" + name = self._convert_name(name) + if name not in self._fields: + if default is _MISSING: + default = self._default_value(name) + return default + if name in _UNICODEFIELDS: + value = self._fields[name] + return value + elif name in _LISTFIELDS: + value = self._fields[name] + if value is None: + return [] + res = [] + for val in value: + if name not in _LISTTUPLEFIELDS: + res.append(val) + else: + # That's for Project-URL + res.append((val[0], val[1])) + return res + + elif name in _ELEMENTSFIELD: + value = self._fields[name] + if isinstance(value, string_types): + return value.split(',') + return self._fields[name] + + def check(self, strict=False): + """Check if the metadata is compliant. If strict is True then raise if + no Name or Version are provided""" + self.set_metadata_version() + + # XXX should check the versions (if the file was loaded) + missing, warnings = [], [] + + for attr in ('Name', 'Version'): # required by PEP 345 + if attr not in self: + missing.append(attr) + + if strict and missing != []: + msg = 'missing required metadata: %s' % ', '.join(missing) + raise MetadataMissingError(msg) + + for attr in ('Home-page', 'Author'): + if attr not in self: + missing.append(attr) + + # checking metadata 1.2 (XXX needs to check 1.1, 1.0) + if self['Metadata-Version'] != '1.2': + return missing, warnings + + scheme = get_scheme(self.scheme) + + def are_valid_constraints(value): + for v in value: + if not scheme.is_valid_matcher(v.split(';')[0]): + return False + return True + + for fields, controller in ((_PREDICATE_FIELDS, are_valid_constraints), + (_VERSIONS_FIELDS, + scheme.is_valid_constraint_list), + (_VERSION_FIELDS, + scheme.is_valid_version)): + for field in fields: + value = self.get(field, None) + if value is not None and not controller(value): + warnings.append("Wrong value for '%s': %s" % (field, value)) + + return missing, warnings + + def todict(self, skip_missing=False): + """Return fields as a dict. + + Field names will be converted to use the underscore-lowercase style + instead of hyphen-mixed case (i.e. home_page instead of Home-page). + """ + self.set_metadata_version() + + mapping_1_0 = ( + ('metadata_version', 'Metadata-Version'), + ('name', 'Name'), + ('version', 'Version'), + ('summary', 'Summary'), + ('home_page', 'Home-page'), + ('author', 'Author'), + ('author_email', 'Author-email'), + ('license', 'License'), + ('description', 'Description'), + ('keywords', 'Keywords'), + ('platform', 'Platform'), + ('classifiers', 'Classifier'), + ('download_url', 'Download-URL'), + ) + + data = {} + for key, field_name in mapping_1_0: + if not skip_missing or field_name in self._fields: + data[key] = self[field_name] + + if self['Metadata-Version'] == '1.2': + mapping_1_2 = ( + ('requires_dist', 'Requires-Dist'), + ('requires_python', 'Requires-Python'), + ('requires_external', 'Requires-External'), + ('provides_dist', 'Provides-Dist'), + ('obsoletes_dist', 'Obsoletes-Dist'), + ('project_url', 'Project-URL'), + ('maintainer', 'Maintainer'), + ('maintainer_email', 'Maintainer-email'), + ) + for key, field_name in mapping_1_2: + if not skip_missing or field_name in self._fields: + if key != 'project_url': + data[key] = self[field_name] + else: + data[key] = [','.join(u) for u in self[field_name]] + + elif self['Metadata-Version'] == '1.1': + mapping_1_1 = ( + ('provides', 'Provides'), + ('requires', 'Requires'), + ('obsoletes', 'Obsoletes'), + ) + for key, field_name in mapping_1_1: + if not skip_missing or field_name in self._fields: + data[key] = self[field_name] + + return data + + def add_requirements(self, requirements): + if self['Metadata-Version'] == '1.1': + # we can't have 1.1 metadata *and* Setuptools requires + for field in ('Obsoletes', 'Requires', 'Provides'): + if field in self: + del self[field] + self['Requires-Dist'] += requirements + + # Mapping API + # TODO could add iter* variants + + def keys(self): + return list(_version2fieldlist(self['Metadata-Version'])) + + def __iter__(self): + for key in self.keys(): + yield key + + def values(self): + return [self[key] for key in self.keys()] + + def items(self): + return [(key, self[key]) for key in self.keys()] + + def __repr__(self): + return '<%s %s %s>' % (self.__class__.__name__, self.name, + self.version) + + +METADATA_FILENAME = 'pydist.json' +WHEEL_METADATA_FILENAME = 'metadata.json' +LEGACY_METADATA_FILENAME = 'METADATA' + + +class Metadata(object): + """ + The metadata of a release. This implementation uses 2.0 (JSON) + metadata where possible. If not possible, it wraps a LegacyMetadata + instance which handles the key-value metadata format. + """ + + METADATA_VERSION_MATCHER = re.compile(r'^\d+(\.\d+)*$') + + NAME_MATCHER = re.compile('^[0-9A-Z]([0-9A-Z_.-]*[0-9A-Z])?$', re.I) + + VERSION_MATCHER = PEP440_VERSION_RE + + SUMMARY_MATCHER = re.compile('.{1,2047}') + + METADATA_VERSION = '2.0' + + GENERATOR = 'distlib (%s)' % __version__ + + MANDATORY_KEYS = { + 'name': (), + 'version': (), + 'summary': ('legacy',), + } + + INDEX_KEYS = ('name version license summary description author ' + 'author_email keywords platform home_page classifiers ' + 'download_url') + + DEPENDENCY_KEYS = ('extras run_requires test_requires build_requires ' + 'dev_requires provides meta_requires obsoleted_by ' + 'supports_environments') + + SYNTAX_VALIDATORS = { + 'metadata_version': (METADATA_VERSION_MATCHER, ()), + 'name': (NAME_MATCHER, ('legacy',)), + 'version': (VERSION_MATCHER, ('legacy',)), + 'summary': (SUMMARY_MATCHER, ('legacy',)), + } + + __slots__ = ('_legacy', '_data', 'scheme') + + def __init__(self, path=None, fileobj=None, mapping=None, + scheme='default'): + if [path, fileobj, mapping].count(None) < 2: + raise TypeError('path, fileobj and mapping are exclusive') + self._legacy = None + self._data = None + self.scheme = scheme + #import pdb; pdb.set_trace() + if mapping is not None: + try: + self._validate_mapping(mapping, scheme) + self._data = mapping + except MetadataUnrecognizedVersionError: + self._legacy = LegacyMetadata(mapping=mapping, scheme=scheme) + self.validate() + else: + data = None + if path: + with open(path, 'rb') as f: + data = f.read() + elif fileobj: + data = fileobj.read() + if data is None: + # Initialised with no args - to be added + self._data = { + 'metadata_version': self.METADATA_VERSION, + 'generator': self.GENERATOR, + } + else: + if not isinstance(data, text_type): + data = data.decode('utf-8') + try: + self._data = json.loads(data) + self._validate_mapping(self._data, scheme) + except ValueError: + # Note: MetadataUnrecognizedVersionError does not + # inherit from ValueError (it's a DistlibException, + # which should not inherit from ValueError). + # The ValueError comes from the json.load - if that + # succeeds and we get a validation error, we want + # that to propagate + self._legacy = LegacyMetadata(fileobj=StringIO(data), + scheme=scheme) + self.validate() + + common_keys = set(('name', 'version', 'license', 'keywords', 'summary')) + + none_list = (None, list) + none_dict = (None, dict) + + mapped_keys = { + 'run_requires': ('Requires-Dist', list), + 'build_requires': ('Setup-Requires-Dist', list), + 'dev_requires': none_list, + 'test_requires': none_list, + 'meta_requires': none_list, + 'extras': ('Provides-Extra', list), + 'modules': none_list, + 'namespaces': none_list, + 'exports': none_dict, + 'commands': none_dict, + 'classifiers': ('Classifier', list), + 'source_url': ('Download-URL', None), + 'metadata_version': ('Metadata-Version', None), + } + + del none_list, none_dict + + def __getattribute__(self, key): + common = object.__getattribute__(self, 'common_keys') + mapped = object.__getattribute__(self, 'mapped_keys') + if key in mapped: + lk, maker = mapped[key] + if self._legacy: + if lk is None: + result = None if maker is None else maker() + else: + result = self._legacy.get(lk) + else: + value = None if maker is None else maker() + if key not in ('commands', 'exports', 'modules', 'namespaces', + 'classifiers'): + result = self._data.get(key, value) + else: + # special cases for PEP 459 + sentinel = object() + result = sentinel + d = self._data.get('extensions') + if d: + if key == 'commands': + result = d.get('python.commands', value) + elif key == 'classifiers': + d = d.get('python.details') + if d: + result = d.get(key, value) + else: + d = d.get('python.exports') + if not d: + d = self._data.get('python.exports') + if d: + result = d.get(key, value) + if result is sentinel: + result = value + elif key not in common: + result = object.__getattribute__(self, key) + elif self._legacy: + result = self._legacy.get(key) + else: + result = self._data.get(key) + return result + + def _validate_value(self, key, value, scheme=None): + if key in self.SYNTAX_VALIDATORS: + pattern, exclusions = self.SYNTAX_VALIDATORS[key] + if (scheme or self.scheme) not in exclusions: + m = pattern.match(value) + if not m: + raise MetadataInvalidError("'%s' is an invalid value for " + "the '%s' property" % (value, + key)) + + def __setattr__(self, key, value): + self._validate_value(key, value) + common = object.__getattribute__(self, 'common_keys') + mapped = object.__getattribute__(self, 'mapped_keys') + if key in mapped: + lk, _ = mapped[key] + if self._legacy: + if lk is None: + raise NotImplementedError + self._legacy[lk] = value + elif key not in ('commands', 'exports', 'modules', 'namespaces', + 'classifiers'): + self._data[key] = value + else: + # special cases for PEP 459 + d = self._data.setdefault('extensions', {}) + if key == 'commands': + d['python.commands'] = value + elif key == 'classifiers': + d = d.setdefault('python.details', {}) + d[key] = value + else: + d = d.setdefault('python.exports', {}) + d[key] = value + elif key not in common: + object.__setattr__(self, key, value) + else: + if key == 'keywords': + if isinstance(value, string_types): + value = value.strip() + if value: + value = value.split() + else: + value = [] + if self._legacy: + self._legacy[key] = value + else: + self._data[key] = value + + @property + def name_and_version(self): + return _get_name_and_version(self.name, self.version, True) + + @property + def provides(self): + if self._legacy: + result = self._legacy['Provides-Dist'] + else: + result = self._data.setdefault('provides', []) + s = '%s (%s)' % (self.name, self.version) + if s not in result: + result.append(s) + return result + + @provides.setter + def provides(self, value): + if self._legacy: + self._legacy['Provides-Dist'] = value + else: + self._data['provides'] = value + + def get_requirements(self, reqts, extras=None, env=None): + """ + Base method to get dependencies, given a set of extras + to satisfy and an optional environment context. + :param reqts: A list of sometimes-wanted dependencies, + perhaps dependent on extras and environment. + :param extras: A list of optional components being requested. + :param env: An optional environment for marker evaluation. + """ + if self._legacy: + result = reqts + else: + result = [] + extras = get_extras(extras or [], self.extras) + for d in reqts: + if 'extra' not in d and 'environment' not in d: + # unconditional + include = True + else: + if 'extra' not in d: + # Not extra-dependent - only environment-dependent + include = True + else: + include = d.get('extra') in extras + if include: + # Not excluded because of extras, check environment + marker = d.get('environment') + if marker: + include = interpret(marker, env) + if include: + result.extend(d['requires']) + for key in ('build', 'dev', 'test'): + e = ':%s:' % key + if e in extras: + extras.remove(e) + # A recursive call, but it should terminate since 'test' + # has been removed from the extras + reqts = self._data.get('%s_requires' % key, []) + result.extend(self.get_requirements(reqts, extras=extras, + env=env)) + return result + + @property + def dictionary(self): + if self._legacy: + return self._from_legacy() + return self._data + + @property + def dependencies(self): + if self._legacy: + raise NotImplementedError + else: + return extract_by_key(self._data, self.DEPENDENCY_KEYS) + + @dependencies.setter + def dependencies(self, value): + if self._legacy: + raise NotImplementedError + else: + self._data.update(value) + + def _validate_mapping(self, mapping, scheme): + if mapping.get('metadata_version') != self.METADATA_VERSION: + raise MetadataUnrecognizedVersionError() + missing = [] + for key, exclusions in self.MANDATORY_KEYS.items(): + if key not in mapping: + if scheme not in exclusions: + missing.append(key) + if missing: + msg = 'Missing metadata items: %s' % ', '.join(missing) + raise MetadataMissingError(msg) + for k, v in mapping.items(): + self._validate_value(k, v, scheme) + + def validate(self): + if self._legacy: + missing, warnings = self._legacy.check(True) + if missing or warnings: + logger.warning('Metadata: missing: %s, warnings: %s', + missing, warnings) + else: + self._validate_mapping(self._data, self.scheme) + + def todict(self): + if self._legacy: + return self._legacy.todict(True) + else: + result = extract_by_key(self._data, self.INDEX_KEYS) + return result + + def _from_legacy(self): + assert self._legacy and not self._data + result = { + 'metadata_version': self.METADATA_VERSION, + 'generator': self.GENERATOR, + } + lmd = self._legacy.todict(True) # skip missing ones + for k in ('name', 'version', 'license', 'summary', 'description', + 'classifier'): + if k in lmd: + if k == 'classifier': + nk = 'classifiers' + else: + nk = k + result[nk] = lmd[k] + kw = lmd.get('Keywords', []) + if kw == ['']: + kw = [] + result['keywords'] = kw + keys = (('requires_dist', 'run_requires'), + ('setup_requires_dist', 'build_requires')) + for ok, nk in keys: + if ok in lmd and lmd[ok]: + result[nk] = [{'requires': lmd[ok]}] + result['provides'] = self.provides + author = {} + maintainer = {} + return result + + LEGACY_MAPPING = { + 'name': 'Name', + 'version': 'Version', + 'license': 'License', + 'summary': 'Summary', + 'description': 'Description', + 'classifiers': 'Classifier', + } + + def _to_legacy(self): + def process_entries(entries): + reqts = set() + for e in entries: + extra = e.get('extra') + env = e.get('environment') + rlist = e['requires'] + for r in rlist: + if not env and not extra: + reqts.add(r) + else: + marker = '' + if extra: + marker = 'extra == "%s"' % extra + if env: + if marker: + marker = '(%s) and %s' % (env, marker) + else: + marker = env + reqts.add(';'.join((r, marker))) + return reqts + + assert self._data and not self._legacy + result = LegacyMetadata() + nmd = self._data + for nk, ok in self.LEGACY_MAPPING.items(): + if nk in nmd: + result[ok] = nmd[nk] + r1 = process_entries(self.run_requires + self.meta_requires) + r2 = process_entries(self.build_requires + self.dev_requires) + if self.extras: + result['Provides-Extra'] = sorted(self.extras) + result['Requires-Dist'] = sorted(r1) + result['Setup-Requires-Dist'] = sorted(r2) + # TODO: other fields such as contacts + return result + + def write(self, path=None, fileobj=None, legacy=False, skip_unknown=True): + if [path, fileobj].count(None) != 1: + raise ValueError('Exactly one of path and fileobj is needed') + self.validate() + if legacy: + if self._legacy: + legacy_md = self._legacy + else: + legacy_md = self._to_legacy() + if path: + legacy_md.write(path, skip_unknown=skip_unknown) + else: + legacy_md.write_file(fileobj, skip_unknown=skip_unknown) + else: + if self._legacy: + d = self._from_legacy() + else: + d = self._data + if fileobj: + json.dump(d, fileobj, ensure_ascii=True, indent=2, + sort_keys=True) + else: + with codecs.open(path, 'w', 'utf-8') as f: + json.dump(d, f, ensure_ascii=True, indent=2, + sort_keys=True) + + def add_requirements(self, requirements): + if self._legacy: + self._legacy.add_requirements(requirements) + else: + run_requires = self._data.setdefault('run_requires', []) + always = None + for entry in run_requires: + if 'environment' not in entry and 'extra' not in entry: + always = entry + break + if always is None: + always = { 'requires': requirements } + run_requires.insert(0, always) + else: + rset = set(always['requires']) | set(requirements) + always['requires'] = sorted(rset) + + def __repr__(self): + name = self.name or '(no name)' + version = self.version or 'no version' + return '<%s %s %s (%s)>' % (self.__class__.__name__, + self.metadata_version, name, version) diff --git a/venv2/lib/python2.7/site-packages/pip/_vendor/distlib/resources.py b/venv2/lib/python2.7/site-packages/pip/_vendor/distlib/resources.py new file mode 100644 index 0000000..1884016 --- /dev/null +++ b/venv2/lib/python2.7/site-packages/pip/_vendor/distlib/resources.py @@ -0,0 +1,355 @@ +# -*- coding: utf-8 -*- +# +# Copyright (C) 2013-2017 Vinay Sajip. +# Licensed to the Python Software Foundation under a contributor agreement. +# See LICENSE.txt and CONTRIBUTORS.txt. +# +from __future__ import unicode_literals + +import bisect +import io +import logging +import os +import pkgutil +import shutil +import sys +import types +import zipimport + +from . import DistlibException +from .util import cached_property, get_cache_base, path_to_cache_dir, Cache + +logger = logging.getLogger(__name__) + + +cache = None # created when needed + + +class ResourceCache(Cache): + def __init__(self, base=None): + if base is None: + # Use native string to avoid issues on 2.x: see Python #20140. + base = os.path.join(get_cache_base(), str('resource-cache')) + super(ResourceCache, self).__init__(base) + + def is_stale(self, resource, path): + """ + Is the cache stale for the given resource? + + :param resource: The :class:`Resource` being cached. + :param path: The path of the resource in the cache. + :return: True if the cache is stale. + """ + # Cache invalidation is a hard problem :-) + return True + + def get(self, resource): + """ + Get a resource into the cache, + + :param resource: A :class:`Resource` instance. + :return: The pathname of the resource in the cache. + """ + prefix, path = resource.finder.get_cache_info(resource) + if prefix is None: + result = path + else: + result = os.path.join(self.base, self.prefix_to_dir(prefix), path) + dirname = os.path.dirname(result) + if not os.path.isdir(dirname): + os.makedirs(dirname) + if not os.path.exists(result): + stale = True + else: + stale = self.is_stale(resource, path) + if stale: + # write the bytes of the resource to the cache location + with open(result, 'wb') as f: + f.write(resource.bytes) + return result + + +class ResourceBase(object): + def __init__(self, finder, name): + self.finder = finder + self.name = name + + +class Resource(ResourceBase): + """ + A class representing an in-package resource, such as a data file. This is + not normally instantiated by user code, but rather by a + :class:`ResourceFinder` which manages the resource. + """ + is_container = False # Backwards compatibility + + def as_stream(self): + """ + Get the resource as a stream. + + This is not a property to make it obvious that it returns a new stream + each time. + """ + return self.finder.get_stream(self) + + @cached_property + def file_path(self): + global cache + if cache is None: + cache = ResourceCache() + return cache.get(self) + + @cached_property + def bytes(self): + return self.finder.get_bytes(self) + + @cached_property + def size(self): + return self.finder.get_size(self) + + +class ResourceContainer(ResourceBase): + is_container = True # Backwards compatibility + + @cached_property + def resources(self): + return self.finder.get_resources(self) + + +class ResourceFinder(object): + """ + Resource finder for file system resources. + """ + + if sys.platform.startswith('java'): + skipped_extensions = ('.pyc', '.pyo', '.class') + else: + skipped_extensions = ('.pyc', '.pyo') + + def __init__(self, module): + self.module = module + self.loader = getattr(module, '__loader__', None) + self.base = os.path.dirname(getattr(module, '__file__', '')) + + def _adjust_path(self, path): + return os.path.realpath(path) + + def _make_path(self, resource_name): + # Issue #50: need to preserve type of path on Python 2.x + # like os.path._get_sep + if isinstance(resource_name, bytes): # should only happen on 2.x + sep = b'/' + else: + sep = '/' + parts = resource_name.split(sep) + parts.insert(0, self.base) + result = os.path.join(*parts) + return self._adjust_path(result) + + def _find(self, path): + return os.path.exists(path) + + def get_cache_info(self, resource): + return None, resource.path + + def find(self, resource_name): + path = self._make_path(resource_name) + if not self._find(path): + result = None + else: + if self._is_directory(path): + result = ResourceContainer(self, resource_name) + else: + result = Resource(self, resource_name) + result.path = path + return result + + def get_stream(self, resource): + return open(resource.path, 'rb') + + def get_bytes(self, resource): + with open(resource.path, 'rb') as f: + return f.read() + + def get_size(self, resource): + return os.path.getsize(resource.path) + + def get_resources(self, resource): + def allowed(f): + return (f != '__pycache__' and not + f.endswith(self.skipped_extensions)) + return set([f for f in os.listdir(resource.path) if allowed(f)]) + + def is_container(self, resource): + return self._is_directory(resource.path) + + _is_directory = staticmethod(os.path.isdir) + + def iterator(self, resource_name): + resource = self.find(resource_name) + if resource is not None: + todo = [resource] + while todo: + resource = todo.pop(0) + yield resource + if resource.is_container: + rname = resource.name + for name in resource.resources: + if not rname: + new_name = name + else: + new_name = '/'.join([rname, name]) + child = self.find(new_name) + if child.is_container: + todo.append(child) + else: + yield child + + +class ZipResourceFinder(ResourceFinder): + """ + Resource finder for resources in .zip files. + """ + def __init__(self, module): + super(ZipResourceFinder, self).__init__(module) + archive = self.loader.archive + self.prefix_len = 1 + len(archive) + # PyPy doesn't have a _files attr on zipimporter, and you can't set one + if hasattr(self.loader, '_files'): + self._files = self.loader._files + else: + self._files = zipimport._zip_directory_cache[archive] + self.index = sorted(self._files) + + def _adjust_path(self, path): + return path + + def _find(self, path): + path = path[self.prefix_len:] + if path in self._files: + result = True + else: + if path and path[-1] != os.sep: + path = path + os.sep + i = bisect.bisect(self.index, path) + try: + result = self.index[i].startswith(path) + except IndexError: + result = False + if not result: + logger.debug('_find failed: %r %r', path, self.loader.prefix) + else: + logger.debug('_find worked: %r %r', path, self.loader.prefix) + return result + + def get_cache_info(self, resource): + prefix = self.loader.archive + path = resource.path[1 + len(prefix):] + return prefix, path + + def get_bytes(self, resource): + return self.loader.get_data(resource.path) + + def get_stream(self, resource): + return io.BytesIO(self.get_bytes(resource)) + + def get_size(self, resource): + path = resource.path[self.prefix_len:] + return self._files[path][3] + + def get_resources(self, resource): + path = resource.path[self.prefix_len:] + if path and path[-1] != os.sep: + path += os.sep + plen = len(path) + result = set() + i = bisect.bisect(self.index, path) + while i < len(self.index): + if not self.index[i].startswith(path): + break + s = self.index[i][plen:] + result.add(s.split(os.sep, 1)[0]) # only immediate children + i += 1 + return result + + def _is_directory(self, path): + path = path[self.prefix_len:] + if path and path[-1] != os.sep: + path += os.sep + i = bisect.bisect(self.index, path) + try: + result = self.index[i].startswith(path) + except IndexError: + result = False + return result + +_finder_registry = { + type(None): ResourceFinder, + zipimport.zipimporter: ZipResourceFinder +} + +try: + # In Python 3.6, _frozen_importlib -> _frozen_importlib_external + try: + import _frozen_importlib_external as _fi + except ImportError: + import _frozen_importlib as _fi + _finder_registry[_fi.SourceFileLoader] = ResourceFinder + _finder_registry[_fi.FileFinder] = ResourceFinder + del _fi +except (ImportError, AttributeError): + pass + + +def register_finder(loader, finder_maker): + _finder_registry[type(loader)] = finder_maker + +_finder_cache = {} + + +def finder(package): + """ + Return a resource finder for a package. + :param package: The name of the package. + :return: A :class:`ResourceFinder` instance for the package. + """ + if package in _finder_cache: + result = _finder_cache[package] + else: + if package not in sys.modules: + __import__(package) + module = sys.modules[package] + path = getattr(module, '__path__', None) + if path is None: + raise DistlibException('You cannot get a finder for a module, ' + 'only for a package') + loader = getattr(module, '__loader__', None) + finder_maker = _finder_registry.get(type(loader)) + if finder_maker is None: + raise DistlibException('Unable to locate finder for %r' % package) + result = finder_maker(module) + _finder_cache[package] = result + return result + + +_dummy_module = types.ModuleType(str('__dummy__')) + + +def finder_for_path(path): + """ + Return a resource finder for a path, which should represent a container. + + :param path: The path. + :return: A :class:`ResourceFinder` instance for the path. + """ + result = None + # calls any path hooks, gets importer into cache + pkgutil.get_importer(path) + loader = sys.path_importer_cache.get(path) + finder = _finder_registry.get(type(loader)) + if finder: + module = _dummy_module + module.__file__ = os.path.join(path, '') + module.__loader__ = loader + result = finder(module) + return result diff --git a/venv2/lib/python2.7/site-packages/pip/_vendor/distlib/scripts.py b/venv2/lib/python2.7/site-packages/pip/_vendor/distlib/scripts.py new file mode 100644 index 0000000..8e22cb9 --- /dev/null +++ b/venv2/lib/python2.7/site-packages/pip/_vendor/distlib/scripts.py @@ -0,0 +1,417 @@ +# -*- coding: utf-8 -*- +# +# Copyright (C) 2013-2015 Vinay Sajip. +# Licensed to the Python Software Foundation under a contributor agreement. +# See LICENSE.txt and CONTRIBUTORS.txt. +# +from io import BytesIO +import logging +import os +import re +import struct +import sys + +from .compat import sysconfig, detect_encoding, ZipFile +from .resources import finder +from .util import (FileOperator, get_export_entry, convert_path, + get_executable, in_venv) + +logger = logging.getLogger(__name__) + +_DEFAULT_MANIFEST = ''' +<?xml version="1.0" encoding="UTF-8" standalone="yes"?> +<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0"> + <assemblyIdentity version="1.0.0.0" + processorArchitecture="X86" + name="%s" + type="win32"/> + + <!-- Identify the application security requirements. --> + <trustInfo xmlns="urn:schemas-microsoft-com:asm.v3"> + <security> + <requestedPrivileges> + <requestedExecutionLevel level="asInvoker" uiAccess="false"/> + </requestedPrivileges> + </security> + </trustInfo> +</assembly>'''.strip() + +# check if Python is called on the first line with this expression +FIRST_LINE_RE = re.compile(b'^#!.*pythonw?[0-9.]*([ \t].*)?$') +SCRIPT_TEMPLATE = r'''# -*- coding: utf-8 -*- +if __name__ == '__main__': + import sys, re + + def _resolve(module, func): + __import__(module) + mod = sys.modules[module] + parts = func.split('.') + result = getattr(mod, parts.pop(0)) + for p in parts: + result = getattr(result, p) + return result + + try: + sys.argv[0] = re.sub(r'(-script\.pyw?|\.exe)?$', '', sys.argv[0]) + + func = _resolve('%(module)s', '%(func)s') + rc = func() # None interpreted as 0 + except Exception as e: # only supporting Python >= 2.6 + sys.stderr.write('%%s\n' %% e) + rc = 1 + sys.exit(rc) +''' + + +def _enquote_executable(executable): + if ' ' in executable: + # make sure we quote only the executable in case of env + # for example /usr/bin/env "/dir with spaces/bin/jython" + # instead of "/usr/bin/env /dir with spaces/bin/jython" + # otherwise whole + if executable.startswith('/usr/bin/env '): + env, _executable = executable.split(' ', 1) + if ' ' in _executable and not _executable.startswith('"'): + executable = '%s "%s"' % (env, _executable) + else: + if not executable.startswith('"'): + executable = '"%s"' % executable + return executable + + +class ScriptMaker(object): + """ + A class to copy or create scripts from source scripts or callable + specifications. + """ + script_template = SCRIPT_TEMPLATE + + executable = None # for shebangs + + def __init__(self, source_dir, target_dir, add_launchers=True, + dry_run=False, fileop=None): + self.source_dir = source_dir + self.target_dir = target_dir + self.add_launchers = add_launchers + self.force = False + self.clobber = False + # It only makes sense to set mode bits on POSIX. + self.set_mode = (os.name == 'posix') or (os.name == 'java' and + os._name == 'posix') + self.variants = set(('', 'X.Y')) + self._fileop = fileop or FileOperator(dry_run) + + self._is_nt = os.name == 'nt' or ( + os.name == 'java' and os._name == 'nt') + + def _get_alternate_executable(self, executable, options): + if options.get('gui', False) and self._is_nt: # pragma: no cover + dn, fn = os.path.split(executable) + fn = fn.replace('python', 'pythonw') + executable = os.path.join(dn, fn) + return executable + + if sys.platform.startswith('java'): # pragma: no cover + def _is_shell(self, executable): + """ + Determine if the specified executable is a script + (contains a #! line) + """ + try: + with open(executable) as fp: + return fp.read(2) == '#!' + except (OSError, IOError): + logger.warning('Failed to open %s', executable) + return False + + def _fix_jython_executable(self, executable): + if self._is_shell(executable): + # Workaround for Jython is not needed on Linux systems. + import java + + if java.lang.System.getProperty('os.name') == 'Linux': + return executable + elif executable.lower().endswith('jython.exe'): + # Use wrapper exe for Jython on Windows + return executable + return '/usr/bin/env %s' % executable + + def _build_shebang(self, executable, post_interp): + """ + Build a shebang line. In the simple case (on Windows, or a shebang line + which is not too long or contains spaces) use a simple formulation for + the shebang. Otherwise, use /bin/sh as the executable, with a contrived + shebang which allows the script to run either under Python or sh, using + suitable quoting. Thanks to Harald Nordgren for his input. + + See also: http://www.in-ulm.de/~mascheck/various/shebang/#length + https://hg.mozilla.org/mozilla-central/file/tip/mach + """ + if os.name != 'posix': + simple_shebang = True + else: + # Add 3 for '#!' prefix and newline suffix. + shebang_length = len(executable) + len(post_interp) + 3 + if sys.platform == 'darwin': + max_shebang_length = 512 + else: + max_shebang_length = 127 + simple_shebang = ((b' ' not in executable) and + (shebang_length <= max_shebang_length)) + + if simple_shebang: + result = b'#!' + executable + post_interp + b'\n' + else: + result = b'#!/bin/sh\n' + result += b"'''exec' " + executable + post_interp + b' "$0" "$@"\n' + result += b"' '''" + return result + + def _get_shebang(self, encoding, post_interp=b'', options=None): + enquote = True + if self.executable: + executable = self.executable + enquote = False # assume this will be taken care of + elif not sysconfig.is_python_build(): + executable = get_executable() + elif in_venv(): # pragma: no cover + executable = os.path.join(sysconfig.get_path('scripts'), + 'python%s' % sysconfig.get_config_var('EXE')) + else: # pragma: no cover + executable = os.path.join( + sysconfig.get_config_var('BINDIR'), + 'python%s%s' % (sysconfig.get_config_var('VERSION'), + sysconfig.get_config_var('EXE'))) + if options: + executable = self._get_alternate_executable(executable, options) + + if sys.platform.startswith('java'): # pragma: no cover + executable = self._fix_jython_executable(executable) + # Normalise case for Windows + executable = os.path.normcase(executable) + # If the user didn't specify an executable, it may be necessary to + # cater for executable paths with spaces (not uncommon on Windows) + if enquote: + executable = _enquote_executable(executable) + # Issue #51: don't use fsencode, since we later try to + # check that the shebang is decodable using utf-8. + executable = executable.encode('utf-8') + # in case of IronPython, play safe and enable frames support + if (sys.platform == 'cli' and '-X:Frames' not in post_interp + and '-X:FullFrames' not in post_interp): # pragma: no cover + post_interp += b' -X:Frames' + shebang = self._build_shebang(executable, post_interp) + # Python parser starts to read a script using UTF-8 until + # it gets a #coding:xxx cookie. The shebang has to be the + # first line of a file, the #coding:xxx cookie cannot be + # written before. So the shebang has to be decodable from + # UTF-8. + try: + shebang.decode('utf-8') + except UnicodeDecodeError: # pragma: no cover + raise ValueError( + 'The shebang (%r) is not decodable from utf-8' % shebang) + # If the script is encoded to a custom encoding (use a + # #coding:xxx cookie), the shebang has to be decodable from + # the script encoding too. + if encoding != 'utf-8': + try: + shebang.decode(encoding) + except UnicodeDecodeError: # pragma: no cover + raise ValueError( + 'The shebang (%r) is not decodable ' + 'from the script encoding (%r)' % (shebang, encoding)) + return shebang + + def _get_script_text(self, entry): + return self.script_template % dict(module=entry.prefix, + func=entry.suffix) + + manifest = _DEFAULT_MANIFEST + + def get_manifest(self, exename): + base = os.path.basename(exename) + return self.manifest % base + + def _write_script(self, names, shebang, script_bytes, filenames, ext): + use_launcher = self.add_launchers and self._is_nt + linesep = os.linesep.encode('utf-8') + if not shebang.endswith(linesep): + shebang += linesep + if not use_launcher: + script_bytes = shebang + script_bytes + else: # pragma: no cover + if ext == 'py': + launcher = self._get_launcher('t') + else: + launcher = self._get_launcher('w') + stream = BytesIO() + with ZipFile(stream, 'w') as zf: + zf.writestr('__main__.py', script_bytes) + zip_data = stream.getvalue() + script_bytes = launcher + shebang + zip_data + for name in names: + outname = os.path.join(self.target_dir, name) + if use_launcher: # pragma: no cover + n, e = os.path.splitext(outname) + if e.startswith('.py'): + outname = n + outname = '%s.exe' % outname + try: + self._fileop.write_binary_file(outname, script_bytes) + except Exception: + # Failed writing an executable - it might be in use. + logger.warning('Failed to write executable - trying to ' + 'use .deleteme logic') + dfname = '%s.deleteme' % outname + if os.path.exists(dfname): + os.remove(dfname) # Not allowed to fail here + os.rename(outname, dfname) # nor here + self._fileop.write_binary_file(outname, script_bytes) + logger.debug('Able to replace executable using ' + '.deleteme logic') + try: + os.remove(dfname) + except Exception: + pass # still in use - ignore error + else: + if self._is_nt and not outname.endswith('.' + ext): # pragma: no cover + outname = '%s.%s' % (outname, ext) + if os.path.exists(outname) and not self.clobber: + logger.warning('Skipping existing file %s', outname) + continue + self._fileop.write_binary_file(outname, script_bytes) + if self.set_mode: + self._fileop.set_executable_mode([outname]) + filenames.append(outname) + + def _make_script(self, entry, filenames, options=None): + post_interp = b'' + if options: + args = options.get('interpreter_args', []) + if args: + args = ' %s' % ' '.join(args) + post_interp = args.encode('utf-8') + shebang = self._get_shebang('utf-8', post_interp, options=options) + script = self._get_script_text(entry).encode('utf-8') + name = entry.name + scriptnames = set() + if '' in self.variants: + scriptnames.add(name) + if 'X' in self.variants: + scriptnames.add('%s%s' % (name, sys.version[0])) + if 'X.Y' in self.variants: + scriptnames.add('%s-%s' % (name, sys.version[:3])) + if options and options.get('gui', False): + ext = 'pyw' + else: + ext = 'py' + self._write_script(scriptnames, shebang, script, filenames, ext) + + def _copy_script(self, script, filenames): + adjust = False + script = os.path.join(self.source_dir, convert_path(script)) + outname = os.path.join(self.target_dir, os.path.basename(script)) + if not self.force and not self._fileop.newer(script, outname): + logger.debug('not copying %s (up-to-date)', script) + return + + # Always open the file, but ignore failures in dry-run mode -- + # that way, we'll get accurate feedback if we can read the + # script. + try: + f = open(script, 'rb') + except IOError: # pragma: no cover + if not self.dry_run: + raise + f = None + else: + first_line = f.readline() + if not first_line: # pragma: no cover + logger.warning('%s: %s is an empty file (skipping)', + self.get_command_name(), script) + return + + match = FIRST_LINE_RE.match(first_line.replace(b'\r\n', b'\n')) + if match: + adjust = True + post_interp = match.group(1) or b'' + + if not adjust: + if f: + f.close() + self._fileop.copy_file(script, outname) + if self.set_mode: + self._fileop.set_executable_mode([outname]) + filenames.append(outname) + else: + logger.info('copying and adjusting %s -> %s', script, + self.target_dir) + if not self._fileop.dry_run: + encoding, lines = detect_encoding(f.readline) + f.seek(0) + shebang = self._get_shebang(encoding, post_interp) + if b'pythonw' in first_line: # pragma: no cover + ext = 'pyw' + else: + ext = 'py' + n = os.path.basename(outname) + self._write_script([n], shebang, f.read(), filenames, ext) + if f: + f.close() + + @property + def dry_run(self): + return self._fileop.dry_run + + @dry_run.setter + def dry_run(self, value): + self._fileop.dry_run = value + + if os.name == 'nt' or (os.name == 'java' and os._name == 'nt'): # pragma: no cover + # Executable launcher support. + # Launchers are from https://bitbucket.org/vinay.sajip/simple_launcher/ + + def _get_launcher(self, kind): + if struct.calcsize('P') == 8: # 64-bit + bits = '64' + else: + bits = '32' + name = '%s%s.exe' % (kind, bits) + # Issue 31: don't hardcode an absolute package name, but + # determine it relative to the current package + distlib_package = __name__.rsplit('.', 1)[0] + result = finder(distlib_package).find(name).bytes + return result + + # Public API follows + + def make(self, specification, options=None): + """ + Make a script. + + :param specification: The specification, which is either a valid export + entry specification (to make a script from a + callable) or a filename (to make a script by + copying from a source location). + :param options: A dictionary of options controlling script generation. + :return: A list of all absolute pathnames written to. + """ + filenames = [] + entry = get_export_entry(specification) + if entry is None: + self._copy_script(specification, filenames) + else: + self._make_script(entry, filenames, options=options) + return filenames + + def make_multiple(self, specifications, options=None): + """ + Take a list of specifications and make scripts from them, + :param specifications: A list of specifications. + :return: A list of all absolute pathnames written to, + """ + filenames = [] + for specification in specifications: + filenames.extend(self.make(specification, options)) + return filenames diff --git a/venv2/lib/python2.7/site-packages/pip/_vendor/distlib/t32.exe b/venv2/lib/python2.7/site-packages/pip/_vendor/distlib/t32.exe new file mode 100644 index 0000000000000000000000000000000000000000..a09d926872d84ae22a617dfe9ebb560d420b37de GIT binary patch literal 92672 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 literal 0 HcmV?d00001 diff --git a/venv2/lib/python2.7/site-packages/pip/_vendor/distlib/t64.exe b/venv2/lib/python2.7/site-packages/pip/_vendor/distlib/t64.exe new file mode 100644 index 0000000000000000000000000000000000000000..9da9b40de922fb203df6b9a1d0ad4139af536850 GIT binary patch literal 102400 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�vlnW`{LoXpTySn$@;x8yoz96jn?+Kk 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�?dYNUd4WaURX_A__M4W}D0!0n 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 literal 0 HcmV?d00001 diff --git a/venv2/lib/python2.7/site-packages/pip/_vendor/distlib/util.py b/venv2/lib/python2.7/site-packages/pip/_vendor/distlib/util.py new file mode 100644 index 0000000..9d4bfd3 --- /dev/null +++ b/venv2/lib/python2.7/site-packages/pip/_vendor/distlib/util.py @@ -0,0 +1,1756 @@ +# +# Copyright (C) 2012-2017 The Python Software Foundation. +# See LICENSE.txt and CONTRIBUTORS.txt. +# +import codecs +from collections import deque +import contextlib +import csv +from glob import iglob as std_iglob +import io +import json +import logging +import os +import py_compile +import re +import socket +try: + import ssl +except ImportError: # pragma: no cover + ssl = None +import subprocess +import sys +import tarfile +import tempfile +import textwrap + +try: + import threading +except ImportError: # pragma: no cover + import dummy_threading as threading +import time + +from . import DistlibException +from .compat import (string_types, text_type, shutil, raw_input, StringIO, + cache_from_source, urlopen, urljoin, httplib, xmlrpclib, + splittype, HTTPHandler, BaseConfigurator, valid_ident, + Container, configparser, URLError, ZipFile, fsdecode, + unquote, urlparse) + +logger = logging.getLogger(__name__) + +# +# Requirement parsing code as per PEP 508 +# + +IDENTIFIER = re.compile(r'^([\w\.-]+)\s*') +VERSION_IDENTIFIER = re.compile(r'^([\w\.*+-]+)\s*') +COMPARE_OP = re.compile(r'^(<=?|>=?|={2,3}|[~!]=)\s*') +MARKER_OP = re.compile(r'^((<=?)|(>=?)|={2,3}|[~!]=|in|not\s+in)\s*') +OR = re.compile(r'^or\b\s*') +AND = re.compile(r'^and\b\s*') +NON_SPACE = re.compile(r'(\S+)\s*') +STRING_CHUNK = re.compile(r'([\s\w\.{}()*+#:;,/?!~`@$%^&=|<>\[\]-]+)') + + +def parse_marker(marker_string): + """ + Parse a marker string and return a dictionary containing a marker expression. + + The dictionary will contain keys "op", "lhs" and "rhs" for non-terminals in + the expression grammar, or strings. A string contained in quotes is to be + interpreted as a literal string, and a string not contained in quotes is a + variable (such as os_name). + """ + def marker_var(remaining): + # either identifier, or literal string + m = IDENTIFIER.match(remaining) + if m: + result = m.groups()[0] + remaining = remaining[m.end():] + elif not remaining: + raise SyntaxError('unexpected end of input') + else: + q = remaining[0] + if q not in '\'"': + raise SyntaxError('invalid expression: %s' % remaining) + oq = '\'"'.replace(q, '') + remaining = remaining[1:] + parts = [q] + while remaining: + # either a string chunk, or oq, or q to terminate + if remaining[0] == q: + break + elif remaining[0] == oq: + parts.append(oq) + remaining = remaining[1:] + else: + m = STRING_CHUNK.match(remaining) + if not m: + raise SyntaxError('error in string literal: %s' % remaining) + parts.append(m.groups()[0]) + remaining = remaining[m.end():] + else: + s = ''.join(parts) + raise SyntaxError('unterminated string: %s' % s) + parts.append(q) + result = ''.join(parts) + remaining = remaining[1:].lstrip() # skip past closing quote + return result, remaining + + def marker_expr(remaining): + if remaining and remaining[0] == '(': + result, remaining = marker(remaining[1:].lstrip()) + if remaining[0] != ')': + raise SyntaxError('unterminated parenthesis: %s' % remaining) + remaining = remaining[1:].lstrip() + else: + lhs, remaining = marker_var(remaining) + while remaining: + m = MARKER_OP.match(remaining) + if not m: + break + op = m.groups()[0] + remaining = remaining[m.end():] + rhs, remaining = marker_var(remaining) + lhs = {'op': op, 'lhs': lhs, 'rhs': rhs} + result = lhs + return result, remaining + + def marker_and(remaining): + lhs, remaining = marker_expr(remaining) + while remaining: + m = AND.match(remaining) + if not m: + break + remaining = remaining[m.end():] + rhs, remaining = marker_expr(remaining) + lhs = {'op': 'and', 'lhs': lhs, 'rhs': rhs} + return lhs, remaining + + def marker(remaining): + lhs, remaining = marker_and(remaining) + while remaining: + m = OR.match(remaining) + if not m: + break + remaining = remaining[m.end():] + rhs, remaining = marker_and(remaining) + lhs = {'op': 'or', 'lhs': lhs, 'rhs': rhs} + return lhs, remaining + + return marker(marker_string) + + +def parse_requirement(req): + """ + Parse a requirement passed in as a string. Return a Container + whose attributes contain the various parts of the requirement. + """ + remaining = req.strip() + if not remaining or remaining.startswith('#'): + return None + m = IDENTIFIER.match(remaining) + if not m: + raise SyntaxError('name expected: %s' % remaining) + distname = m.groups()[0] + remaining = remaining[m.end():] + extras = mark_expr = versions = uri = None + if remaining and remaining[0] == '[': + i = remaining.find(']', 1) + if i < 0: + raise SyntaxError('unterminated extra: %s' % remaining) + s = remaining[1:i] + remaining = remaining[i + 1:].lstrip() + extras = [] + while s: + m = IDENTIFIER.match(s) + if not m: + raise SyntaxError('malformed extra: %s' % s) + extras.append(m.groups()[0]) + s = s[m.end():] + if not s: + break + if s[0] != ',': + raise SyntaxError('comma expected in extras: %s' % s) + s = s[1:].lstrip() + if not extras: + extras = None + if remaining: + if remaining[0] == '@': + # it's a URI + remaining = remaining[1:].lstrip() + m = NON_SPACE.match(remaining) + if not m: + raise SyntaxError('invalid URI: %s' % remaining) + uri = m.groups()[0] + t = urlparse(uri) + # there are issues with Python and URL parsing, so this test + # is a bit crude. See bpo-20271, bpo-23505. Python doesn't + # always parse invalid URLs correctly - it should raise + # exceptions for malformed URLs + if not (t.scheme and t.netloc): + raise SyntaxError('Invalid URL: %s' % uri) + remaining = remaining[m.end():].lstrip() + else: + + def get_versions(ver_remaining): + """ + Return a list of operator, version tuples if any are + specified, else None. + """ + m = COMPARE_OP.match(ver_remaining) + versions = None + if m: + versions = [] + while True: + op = m.groups()[0] + ver_remaining = ver_remaining[m.end():] + m = VERSION_IDENTIFIER.match(ver_remaining) + if not m: + raise SyntaxError('invalid version: %s' % ver_remaining) + v = m.groups()[0] + versions.append((op, v)) + ver_remaining = ver_remaining[m.end():] + if not ver_remaining or ver_remaining[0] != ',': + break + ver_remaining = ver_remaining[1:].lstrip() + m = COMPARE_OP.match(ver_remaining) + if not m: + raise SyntaxError('invalid constraint: %s' % ver_remaining) + if not versions: + versions = None + return versions, ver_remaining + + if remaining[0] != '(': + versions, remaining = get_versions(remaining) + else: + i = remaining.find(')', 1) + if i < 0: + raise SyntaxError('unterminated parenthesis: %s' % remaining) + s = remaining[1:i] + remaining = remaining[i + 1:].lstrip() + # As a special diversion from PEP 508, allow a version number + # a.b.c in parentheses as a synonym for ~= a.b.c (because this + # is allowed in earlier PEPs) + if COMPARE_OP.match(s): + versions, _ = get_versions(s) + else: + m = VERSION_IDENTIFIER.match(s) + if not m: + raise SyntaxError('invalid constraint: %s' % s) + v = m.groups()[0] + s = s[m.end():].lstrip() + if s: + raise SyntaxError('invalid constraint: %s' % s) + versions = [('~=', v)] + + if remaining: + if remaining[0] != ';': + raise SyntaxError('invalid requirement: %s' % remaining) + remaining = remaining[1:].lstrip() + + mark_expr, remaining = parse_marker(remaining) + + if remaining and remaining[0] != '#': + raise SyntaxError('unexpected trailing data: %s' % remaining) + + if not versions: + rs = distname + else: + rs = '%s %s' % (distname, ', '.join(['%s %s' % con for con in versions])) + return Container(name=distname, extras=extras, constraints=versions, + marker=mark_expr, url=uri, requirement=rs) + + +def get_resources_dests(resources_root, rules): + """Find destinations for resources files""" + + def get_rel_path(root, path): + # normalizes and returns a lstripped-/-separated path + root = root.replace(os.path.sep, '/') + path = path.replace(os.path.sep, '/') + assert path.startswith(root) + return path[len(root):].lstrip('/') + + destinations = {} + for base, suffix, dest in rules: + prefix = os.path.join(resources_root, base) + for abs_base in iglob(prefix): + abs_glob = os.path.join(abs_base, suffix) + for abs_path in iglob(abs_glob): + resource_file = get_rel_path(resources_root, abs_path) + if dest is None: # remove the entry if it was here + destinations.pop(resource_file, None) + else: + rel_path = get_rel_path(abs_base, abs_path) + rel_dest = dest.replace(os.path.sep, '/').rstrip('/') + destinations[resource_file] = rel_dest + '/' + rel_path + return destinations + + +def in_venv(): + if hasattr(sys, 'real_prefix'): + # virtualenv venvs + result = True + else: + # PEP 405 venvs + result = sys.prefix != getattr(sys, 'base_prefix', sys.prefix) + return result + + +def get_executable(): +# The __PYVENV_LAUNCHER__ dance is apparently no longer needed, as +# changes to the stub launcher mean that sys.executable always points +# to the stub on OS X +# if sys.platform == 'darwin' and ('__PYVENV_LAUNCHER__' +# in os.environ): +# result = os.environ['__PYVENV_LAUNCHER__'] +# else: +# result = sys.executable +# return result + result = os.path.normcase(sys.executable) + if not isinstance(result, text_type): + result = fsdecode(result) + return result + + +def proceed(prompt, allowed_chars, error_prompt=None, default=None): + p = prompt + while True: + s = raw_input(p) + p = prompt + if not s and default: + s = default + if s: + c = s[0].lower() + if c in allowed_chars: + break + if error_prompt: + p = '%c: %s\n%s' % (c, error_prompt, prompt) + return c + + +def extract_by_key(d, keys): + if isinstance(keys, string_types): + keys = keys.split() + result = {} + for key in keys: + if key in d: + result[key] = d[key] + return result + +def read_exports(stream): + if sys.version_info[0] >= 3: + # needs to be a text stream + stream = codecs.getreader('utf-8')(stream) + # Try to load as JSON, falling back on legacy format + data = stream.read() + stream = StringIO(data) + try: + jdata = json.load(stream) + result = jdata['extensions']['python.exports']['exports'] + for group, entries in result.items(): + for k, v in entries.items(): + s = '%s = %s' % (k, v) + entry = get_export_entry(s) + assert entry is not None + entries[k] = entry + return result + except Exception: + stream.seek(0, 0) + + def read_stream(cp, stream): + if hasattr(cp, 'read_file'): + cp.read_file(stream) + else: + cp.readfp(stream) + + cp = configparser.ConfigParser() + try: + read_stream(cp, stream) + except configparser.MissingSectionHeaderError: + stream.close() + data = textwrap.dedent(data) + stream = StringIO(data) + read_stream(cp, stream) + + result = {} + for key in cp.sections(): + result[key] = entries = {} + for name, value in cp.items(key): + s = '%s = %s' % (name, value) + entry = get_export_entry(s) + assert entry is not None + #entry.dist = self + entries[name] = entry + return result + + +def write_exports(exports, stream): + if sys.version_info[0] >= 3: + # needs to be a text stream + stream = codecs.getwriter('utf-8')(stream) + cp = configparser.ConfigParser() + for k, v in exports.items(): + # TODO check k, v for valid values + cp.add_section(k) + for entry in v.values(): + if entry.suffix is None: + s = entry.prefix + else: + s = '%s:%s' % (entry.prefix, entry.suffix) + if entry.flags: + s = '%s [%s]' % (s, ', '.join(entry.flags)) + cp.set(k, entry.name, s) + cp.write(stream) + + +@contextlib.contextmanager +def tempdir(): + td = tempfile.mkdtemp() + try: + yield td + finally: + shutil.rmtree(td) + +@contextlib.contextmanager +def chdir(d): + cwd = os.getcwd() + try: + os.chdir(d) + yield + finally: + os.chdir(cwd) + + +@contextlib.contextmanager +def socket_timeout(seconds=15): + cto = socket.getdefaulttimeout() + try: + socket.setdefaulttimeout(seconds) + yield + finally: + socket.setdefaulttimeout(cto) + + +class cached_property(object): + def __init__(self, func): + self.func = func + #for attr in ('__name__', '__module__', '__doc__'): + # setattr(self, attr, getattr(func, attr, None)) + + def __get__(self, obj, cls=None): + if obj is None: + return self + value = self.func(obj) + object.__setattr__(obj, self.func.__name__, value) + #obj.__dict__[self.func.__name__] = value = self.func(obj) + return value + +def convert_path(pathname): + """Return 'pathname' as a name that will work on the native filesystem. + + The path is split on '/' and put back together again using the current + directory separator. Needed because filenames in the setup script are + always supplied in Unix style, and have to be converted to the local + convention before we can actually use them in the filesystem. Raises + ValueError on non-Unix-ish systems if 'pathname' either starts or + ends with a slash. + """ + if os.sep == '/': + return pathname + if not pathname: + return pathname + if pathname[0] == '/': + raise ValueError("path '%s' cannot be absolute" % pathname) + if pathname[-1] == '/': + raise ValueError("path '%s' cannot end with '/'" % pathname) + + paths = pathname.split('/') + while os.curdir in paths: + paths.remove(os.curdir) + if not paths: + return os.curdir + return os.path.join(*paths) + + +class FileOperator(object): + def __init__(self, dry_run=False): + self.dry_run = dry_run + self.ensured = set() + self._init_record() + + def _init_record(self): + self.record = False + self.files_written = set() + self.dirs_created = set() + + def record_as_written(self, path): + if self.record: + self.files_written.add(path) + + def newer(self, source, target): + """Tell if the target is newer than the source. + + Returns true if 'source' exists and is more recently modified than + 'target', or if 'source' exists and 'target' doesn't. + + Returns false if both exist and 'target' is the same age or younger + than 'source'. Raise PackagingFileError if 'source' does not exist. + + Note that this test is not very accurate: files created in the same + second will have the same "age". + """ + if not os.path.exists(source): + raise DistlibException("file '%r' does not exist" % + os.path.abspath(source)) + if not os.path.exists(target): + return True + + return os.stat(source).st_mtime > os.stat(target).st_mtime + + def copy_file(self, infile, outfile, check=True): + """Copy a file respecting dry-run and force flags. + """ + self.ensure_dir(os.path.dirname(outfile)) + logger.info('Copying %s to %s', infile, outfile) + if not self.dry_run: + msg = None + if check: + if os.path.islink(outfile): + msg = '%s is a symlink' % outfile + elif os.path.exists(outfile) and not os.path.isfile(outfile): + msg = '%s is a non-regular file' % outfile + if msg: + raise ValueError(msg + ' which would be overwritten') + shutil.copyfile(infile, outfile) + self.record_as_written(outfile) + + def copy_stream(self, instream, outfile, encoding=None): + assert not os.path.isdir(outfile) + self.ensure_dir(os.path.dirname(outfile)) + logger.info('Copying stream %s to %s', instream, outfile) + if not self.dry_run: + if encoding is None: + outstream = open(outfile, 'wb') + else: + outstream = codecs.open(outfile, 'w', encoding=encoding) + try: + shutil.copyfileobj(instream, outstream) + finally: + outstream.close() + self.record_as_written(outfile) + + def write_binary_file(self, path, data): + self.ensure_dir(os.path.dirname(path)) + if not self.dry_run: + if os.path.exists(path): + os.remove(path) + with open(path, 'wb') as f: + f.write(data) + self.record_as_written(path) + + def write_text_file(self, path, data, encoding): + self.write_binary_file(path, data.encode(encoding)) + + def set_mode(self, bits, mask, files): + if os.name == 'posix' or (os.name == 'java' and os._name == 'posix'): + # Set the executable bits (owner, group, and world) on + # all the files specified. + for f in files: + if self.dry_run: + logger.info("changing mode of %s", f) + else: + mode = (os.stat(f).st_mode | bits) & mask + logger.info("changing mode of %s to %o", f, mode) + os.chmod(f, mode) + + set_executable_mode = lambda s, f: s.set_mode(0o555, 0o7777, f) + + def ensure_dir(self, path): + path = os.path.abspath(path) + if path not in self.ensured and not os.path.exists(path): + self.ensured.add(path) + d, f = os.path.split(path) + self.ensure_dir(d) + logger.info('Creating %s' % path) + if not self.dry_run: + os.mkdir(path) + if self.record: + self.dirs_created.add(path) + + def byte_compile(self, path, optimize=False, force=False, prefix=None, hashed_invalidation=False): + dpath = cache_from_source(path, not optimize) + logger.info('Byte-compiling %s to %s', path, dpath) + if not self.dry_run: + if force or self.newer(path, dpath): + if not prefix: + diagpath = None + else: + assert path.startswith(prefix) + diagpath = path[len(prefix):] + compile_kwargs = {} + if hashed_invalidation and hasattr(py_compile, 'PycInvalidationMode'): + compile_kwargs['invalidation_mode'] = py_compile.PycInvalidationMode.CHECKED_HASH + py_compile.compile(path, dpath, diagpath, True, **compile_kwargs) # raise error + self.record_as_written(dpath) + return dpath + + def ensure_removed(self, path): + if os.path.exists(path): + if os.path.isdir(path) and not os.path.islink(path): + logger.debug('Removing directory tree at %s', path) + if not self.dry_run: + shutil.rmtree(path) + if self.record: + if path in self.dirs_created: + self.dirs_created.remove(path) + else: + if os.path.islink(path): + s = 'link' + else: + s = 'file' + logger.debug('Removing %s %s', s, path) + if not self.dry_run: + os.remove(path) + if self.record: + if path in self.files_written: + self.files_written.remove(path) + + def is_writable(self, path): + result = False + while not result: + if os.path.exists(path): + result = os.access(path, os.W_OK) + break + parent = os.path.dirname(path) + if parent == path: + break + path = parent + return result + + def commit(self): + """ + Commit recorded changes, turn off recording, return + changes. + """ + assert self.record + result = self.files_written, self.dirs_created + self._init_record() + return result + + def rollback(self): + if not self.dry_run: + for f in list(self.files_written): + if os.path.exists(f): + os.remove(f) + # dirs should all be empty now, except perhaps for + # __pycache__ subdirs + # reverse so that subdirs appear before their parents + dirs = sorted(self.dirs_created, reverse=True) + for d in dirs: + flist = os.listdir(d) + if flist: + assert flist == ['__pycache__'] + sd = os.path.join(d, flist[0]) + os.rmdir(sd) + os.rmdir(d) # should fail if non-empty + self._init_record() + +def resolve(module_name, dotted_path): + if module_name in sys.modules: + mod = sys.modules[module_name] + else: + mod = __import__(module_name) + if dotted_path is None: + result = mod + else: + parts = dotted_path.split('.') + result = getattr(mod, parts.pop(0)) + for p in parts: + result = getattr(result, p) + return result + + +class ExportEntry(object): + def __init__(self, name, prefix, suffix, flags): + self.name = name + self.prefix = prefix + self.suffix = suffix + self.flags = flags + + @cached_property + def value(self): + return resolve(self.prefix, self.suffix) + + def __repr__(self): # pragma: no cover + return '<ExportEntry %s = %s:%s %s>' % (self.name, self.prefix, + self.suffix, self.flags) + + def __eq__(self, other): + if not isinstance(other, ExportEntry): + result = False + else: + result = (self.name == other.name and + self.prefix == other.prefix and + self.suffix == other.suffix and + self.flags == other.flags) + return result + + __hash__ = object.__hash__ + + +ENTRY_RE = re.compile(r'''(?P<name>(\w|[-.+])+) + \s*=\s*(?P<callable>(\w+)([:\.]\w+)*) + \s*(\[\s*(?P<flags>\w+(=\w+)?(,\s*\w+(=\w+)?)*)\s*\])? + ''', re.VERBOSE) + +def get_export_entry(specification): + m = ENTRY_RE.search(specification) + if not m: + result = None + if '[' in specification or ']' in specification: + raise DistlibException("Invalid specification " + "'%s'" % specification) + else: + d = m.groupdict() + name = d['name'] + path = d['callable'] + colons = path.count(':') + if colons == 0: + prefix, suffix = path, None + else: + if colons != 1: + raise DistlibException("Invalid specification " + "'%s'" % specification) + prefix, suffix = path.split(':') + flags = d['flags'] + if flags is None: + if '[' in specification or ']' in specification: + raise DistlibException("Invalid specification " + "'%s'" % specification) + flags = [] + else: + flags = [f.strip() for f in flags.split(',')] + result = ExportEntry(name, prefix, suffix, flags) + return result + + +def get_cache_base(suffix=None): + """ + Return the default base location for distlib caches. If the directory does + not exist, it is created. Use the suffix provided for the base directory, + and default to '.distlib' if it isn't provided. + + On Windows, if LOCALAPPDATA is defined in the environment, then it is + assumed to be a directory, and will be the parent directory of the result. + On POSIX, and on Windows if LOCALAPPDATA is not defined, the user's home + directory - using os.expanduser('~') - will be the parent directory of + the result. + + The result is just the directory '.distlib' in the parent directory as + determined above, or with the name specified with ``suffix``. + """ + if suffix is None: + suffix = '.distlib' + if os.name == 'nt' and 'LOCALAPPDATA' in os.environ: + result = os.path.expandvars('$localappdata') + else: + # Assume posix, or old Windows + result = os.path.expanduser('~') + # we use 'isdir' instead of 'exists', because we want to + # fail if there's a file with that name + if os.path.isdir(result): + usable = os.access(result, os.W_OK) + if not usable: + logger.warning('Directory exists but is not writable: %s', result) + else: + try: + os.makedirs(result) + usable = True + except OSError: + logger.warning('Unable to create %s', result, exc_info=True) + usable = False + if not usable: + result = tempfile.mkdtemp() + logger.warning('Default location unusable, using %s', result) + return os.path.join(result, suffix) + + +def path_to_cache_dir(path): + """ + Convert an absolute path to a directory name for use in a cache. + + The algorithm used is: + + #. On Windows, any ``':'`` in the drive is replaced with ``'---'``. + #. Any occurrence of ``os.sep`` is replaced with ``'--'``. + #. ``'.cache'`` is appended. + """ + d, p = os.path.splitdrive(os.path.abspath(path)) + if d: + d = d.replace(':', '---') + p = p.replace(os.sep, '--') + return d + p + '.cache' + + +def ensure_slash(s): + if not s.endswith('/'): + return s + '/' + return s + + +def parse_credentials(netloc): + username = password = None + if '@' in netloc: + prefix, netloc = netloc.split('@', 1) + if ':' not in prefix: + username = prefix + else: + username, password = prefix.split(':', 1) + return username, password, netloc + + +def get_process_umask(): + result = os.umask(0o22) + os.umask(result) + return result + +def is_string_sequence(seq): + result = True + i = None + for i, s in enumerate(seq): + if not isinstance(s, string_types): + result = False + break + assert i is not None + return result + +PROJECT_NAME_AND_VERSION = re.compile('([a-z0-9_]+([.-][a-z_][a-z0-9_]*)*)-' + '([a-z0-9_.+-]+)', re.I) +PYTHON_VERSION = re.compile(r'-py(\d\.?\d?)') + + +def split_filename(filename, project_name=None): + """ + Extract name, version, python version from a filename (no extension) + + Return name, version, pyver or None + """ + result = None + pyver = None + filename = unquote(filename).replace(' ', '-') + m = PYTHON_VERSION.search(filename) + if m: + pyver = m.group(1) + filename = filename[:m.start()] + if project_name and len(filename) > len(project_name) + 1: + m = re.match(re.escape(project_name) + r'\b', filename) + if m: + n = m.end() + result = filename[:n], filename[n + 1:], pyver + if result is None: + m = PROJECT_NAME_AND_VERSION.match(filename) + if m: + result = m.group(1), m.group(3), pyver + return result + +# Allow spaces in name because of legacy dists like "Twisted Core" +NAME_VERSION_RE = re.compile(r'(?P<name>[\w .-]+)\s*' + r'\(\s*(?P<ver>[^\s)]+)\)$') + +def parse_name_and_version(p): + """ + A utility method used to get name and version from a string. + + From e.g. a Provides-Dist value. + + :param p: A value in a form 'foo (1.0)' + :return: The name and version as a tuple. + """ + m = NAME_VERSION_RE.match(p) + if not m: + raise DistlibException('Ill-formed name/version string: \'%s\'' % p) + d = m.groupdict() + return d['name'].strip().lower(), d['ver'] + +def get_extras(requested, available): + result = set() + requested = set(requested or []) + available = set(available or []) + if '*' in requested: + requested.remove('*') + result |= available + for r in requested: + if r == '-': + result.add(r) + elif r.startswith('-'): + unwanted = r[1:] + if unwanted not in available: + logger.warning('undeclared extra: %s' % unwanted) + if unwanted in result: + result.remove(unwanted) + else: + if r not in available: + logger.warning('undeclared extra: %s' % r) + result.add(r) + return result +# +# Extended metadata functionality +# + +def _get_external_data(url): + result = {} + try: + # urlopen might fail if it runs into redirections, + # because of Python issue #13696. Fixed in locators + # using a custom redirect handler. + resp = urlopen(url) + headers = resp.info() + ct = headers.get('Content-Type') + if not ct.startswith('application/json'): + logger.debug('Unexpected response for JSON request: %s', ct) + else: + reader = codecs.getreader('utf-8')(resp) + #data = reader.read().decode('utf-8') + #result = json.loads(data) + result = json.load(reader) + except Exception as e: + logger.exception('Failed to get external data for %s: %s', url, e) + return result + +_external_data_base_url = 'https://www.red-dove.com/pypi/projects/' + +def get_project_data(name): + url = '%s/%s/project.json' % (name[0].upper(), name) + url = urljoin(_external_data_base_url, url) + result = _get_external_data(url) + return result + +def get_package_data(name, version): + url = '%s/%s/package-%s.json' % (name[0].upper(), name, version) + url = urljoin(_external_data_base_url, url) + return _get_external_data(url) + + +class Cache(object): + """ + A class implementing a cache for resources that need to live in the file system + e.g. shared libraries. This class was moved from resources to here because it + could be used by other modules, e.g. the wheel module. + """ + + def __init__(self, base): + """ + Initialise an instance. + + :param base: The base directory where the cache should be located. + """ + # we use 'isdir' instead of 'exists', because we want to + # fail if there's a file with that name + if not os.path.isdir(base): # pragma: no cover + os.makedirs(base) + if (os.stat(base).st_mode & 0o77) != 0: + logger.warning('Directory \'%s\' is not private', base) + self.base = os.path.abspath(os.path.normpath(base)) + + def prefix_to_dir(self, prefix): + """ + Converts a resource prefix to a directory name in the cache. + """ + return path_to_cache_dir(prefix) + + def clear(self): + """ + Clear the cache. + """ + not_removed = [] + for fn in os.listdir(self.base): + fn = os.path.join(self.base, fn) + try: + if os.path.islink(fn) or os.path.isfile(fn): + os.remove(fn) + elif os.path.isdir(fn): + shutil.rmtree(fn) + except Exception: + not_removed.append(fn) + return not_removed + + +class EventMixin(object): + """ + A very simple publish/subscribe system. + """ + def __init__(self): + self._subscribers = {} + + def add(self, event, subscriber, append=True): + """ + Add a subscriber for an event. + + :param event: The name of an event. + :param subscriber: The subscriber to be added (and called when the + event is published). + :param append: Whether to append or prepend the subscriber to an + existing subscriber list for the event. + """ + subs = self._subscribers + if event not in subs: + subs[event] = deque([subscriber]) + else: + sq = subs[event] + if append: + sq.append(subscriber) + else: + sq.appendleft(subscriber) + + def remove(self, event, subscriber): + """ + Remove a subscriber for an event. + + :param event: The name of an event. + :param subscriber: The subscriber to be removed. + """ + subs = self._subscribers + if event not in subs: + raise ValueError('No subscribers: %r' % event) + subs[event].remove(subscriber) + + def get_subscribers(self, event): + """ + Return an iterator for the subscribers for an event. + :param event: The event to return subscribers for. + """ + return iter(self._subscribers.get(event, ())) + + def publish(self, event, *args, **kwargs): + """ + Publish a event and return a list of values returned by its + subscribers. + + :param event: The event to publish. + :param args: The positional arguments to pass to the event's + subscribers. + :param kwargs: The keyword arguments to pass to the event's + subscribers. + """ + result = [] + for subscriber in self.get_subscribers(event): + try: + value = subscriber(event, *args, **kwargs) + except Exception: + logger.exception('Exception during event publication') + value = None + result.append(value) + logger.debug('publish %s: args = %s, kwargs = %s, result = %s', + event, args, kwargs, result) + return result + +# +# Simple sequencing +# +class Sequencer(object): + def __init__(self): + self._preds = {} + self._succs = {} + self._nodes = set() # nodes with no preds/succs + + def add_node(self, node): + self._nodes.add(node) + + def remove_node(self, node, edges=False): + if node in self._nodes: + self._nodes.remove(node) + if edges: + for p in set(self._preds.get(node, ())): + self.remove(p, node) + for s in set(self._succs.get(node, ())): + self.remove(node, s) + # Remove empties + for k, v in list(self._preds.items()): + if not v: + del self._preds[k] + for k, v in list(self._succs.items()): + if not v: + del self._succs[k] + + def add(self, pred, succ): + assert pred != succ + self._preds.setdefault(succ, set()).add(pred) + self._succs.setdefault(pred, set()).add(succ) + + def remove(self, pred, succ): + assert pred != succ + try: + preds = self._preds[succ] + succs = self._succs[pred] + except KeyError: # pragma: no cover + raise ValueError('%r not a successor of anything' % succ) + try: + preds.remove(pred) + succs.remove(succ) + except KeyError: # pragma: no cover + raise ValueError('%r not a successor of %r' % (succ, pred)) + + def is_step(self, step): + return (step in self._preds or step in self._succs or + step in self._nodes) + + def get_steps(self, final): + if not self.is_step(final): + raise ValueError('Unknown: %r' % final) + result = [] + todo = [] + seen = set() + todo.append(final) + while todo: + step = todo.pop(0) + if step in seen: + # if a step was already seen, + # move it to the end (so it will appear earlier + # when reversed on return) ... but not for the + # final step, as that would be confusing for + # users + if step != final: + result.remove(step) + result.append(step) + else: + seen.add(step) + result.append(step) + preds = self._preds.get(step, ()) + todo.extend(preds) + return reversed(result) + + @property + def strong_connections(self): + #http://en.wikipedia.org/wiki/Tarjan%27s_strongly_connected_components_algorithm + index_counter = [0] + stack = [] + lowlinks = {} + index = {} + result = [] + + graph = self._succs + + def strongconnect(node): + # set the depth index for this node to the smallest unused index + index[node] = index_counter[0] + lowlinks[node] = index_counter[0] + index_counter[0] += 1 + stack.append(node) + + # Consider successors + try: + successors = graph[node] + except Exception: + successors = [] + for successor in successors: + if successor not in lowlinks: + # Successor has not yet been visited + strongconnect(successor) + lowlinks[node] = min(lowlinks[node],lowlinks[successor]) + elif successor in stack: + # the successor is in the stack and hence in the current + # strongly connected component (SCC) + lowlinks[node] = min(lowlinks[node],index[successor]) + + # If `node` is a root node, pop the stack and generate an SCC + if lowlinks[node] == index[node]: + connected_component = [] + + while True: + successor = stack.pop() + connected_component.append(successor) + if successor == node: break + component = tuple(connected_component) + # storing the result + result.append(component) + + for node in graph: + if node not in lowlinks: + strongconnect(node) + + return result + + @property + def dot(self): + result = ['digraph G {'] + for succ in self._preds: + preds = self._preds[succ] + for pred in preds: + result.append(' %s -> %s;' % (pred, succ)) + for node in self._nodes: + result.append(' %s;' % node) + result.append('}') + return '\n'.join(result) + +# +# Unarchiving functionality for zip, tar, tgz, tbz, whl +# + +ARCHIVE_EXTENSIONS = ('.tar.gz', '.tar.bz2', '.tar', '.zip', + '.tgz', '.tbz', '.whl') + +def unarchive(archive_filename, dest_dir, format=None, check=True): + + def check_path(path): + if not isinstance(path, text_type): + path = path.decode('utf-8') + p = os.path.abspath(os.path.join(dest_dir, path)) + if not p.startswith(dest_dir) or p[plen] != os.sep: + raise ValueError('path outside destination: %r' % p) + + dest_dir = os.path.abspath(dest_dir) + plen = len(dest_dir) + archive = None + if format is None: + if archive_filename.endswith(('.zip', '.whl')): + format = 'zip' + elif archive_filename.endswith(('.tar.gz', '.tgz')): + format = 'tgz' + mode = 'r:gz' + elif archive_filename.endswith(('.tar.bz2', '.tbz')): + format = 'tbz' + mode = 'r:bz2' + elif archive_filename.endswith('.tar'): + format = 'tar' + mode = 'r' + else: # pragma: no cover + raise ValueError('Unknown format for %r' % archive_filename) + try: + if format == 'zip': + archive = ZipFile(archive_filename, 'r') + if check: + names = archive.namelist() + for name in names: + check_path(name) + else: + archive = tarfile.open(archive_filename, mode) + if check: + names = archive.getnames() + for name in names: + check_path(name) + if format != 'zip' and sys.version_info[0] < 3: + # See Python issue 17153. If the dest path contains Unicode, + # tarfile extraction fails on Python 2.x if a member path name + # contains non-ASCII characters - it leads to an implicit + # bytes -> unicode conversion using ASCII to decode. + for tarinfo in archive.getmembers(): + if not isinstance(tarinfo.name, text_type): + tarinfo.name = tarinfo.name.decode('utf-8') + archive.extractall(dest_dir) + + finally: + if archive: + archive.close() + + +def zip_dir(directory): + """zip a directory tree into a BytesIO object""" + result = io.BytesIO() + dlen = len(directory) + with ZipFile(result, "w") as zf: + for root, dirs, files in os.walk(directory): + for name in files: + full = os.path.join(root, name) + rel = root[dlen:] + dest = os.path.join(rel, name) + zf.write(full, dest) + return result + +# +# Simple progress bar +# + +UNITS = ('', 'K', 'M', 'G','T','P') + + +class Progress(object): + unknown = 'UNKNOWN' + + def __init__(self, minval=0, maxval=100): + assert maxval is None or maxval >= minval + self.min = self.cur = minval + self.max = maxval + self.started = None + self.elapsed = 0 + self.done = False + + def update(self, curval): + assert self.min <= curval + assert self.max is None or curval <= self.max + self.cur = curval + now = time.time() + if self.started is None: + self.started = now + else: + self.elapsed = now - self.started + + def increment(self, incr): + assert incr >= 0 + self.update(self.cur + incr) + + def start(self): + self.update(self.min) + return self + + def stop(self): + if self.max is not None: + self.update(self.max) + self.done = True + + @property + def maximum(self): + return self.unknown if self.max is None else self.max + + @property + def percentage(self): + if self.done: + result = '100 %' + elif self.max is None: + result = ' ?? %' + else: + v = 100.0 * (self.cur - self.min) / (self.max - self.min) + result = '%3d %%' % v + return result + + def format_duration(self, duration): + if (duration <= 0) and self.max is None or self.cur == self.min: + result = '??:??:??' + #elif duration < 1: + # result = '--:--:--' + else: + result = time.strftime('%H:%M:%S', time.gmtime(duration)) + return result + + @property + def ETA(self): + if self.done: + prefix = 'Done' + t = self.elapsed + #import pdb; pdb.set_trace() + else: + prefix = 'ETA ' + if self.max is None: + t = -1 + elif self.elapsed == 0 or (self.cur == self.min): + t = 0 + else: + #import pdb; pdb.set_trace() + t = float(self.max - self.min) + t /= self.cur - self.min + t = (t - 1) * self.elapsed + return '%s: %s' % (prefix, self.format_duration(t)) + + @property + def speed(self): + if self.elapsed == 0: + result = 0.0 + else: + result = (self.cur - self.min) / self.elapsed + for unit in UNITS: + if result < 1000: + break + result /= 1000.0 + return '%d %sB/s' % (result, unit) + +# +# Glob functionality +# + +RICH_GLOB = re.compile(r'\{([^}]*)\}') +_CHECK_RECURSIVE_GLOB = re.compile(r'[^/\\,{]\*\*|\*\*[^/\\,}]') +_CHECK_MISMATCH_SET = re.compile(r'^[^{]*\}|\{[^}]*$') + + +def iglob(path_glob): + """Extended globbing function that supports ** and {opt1,opt2,opt3}.""" + if _CHECK_RECURSIVE_GLOB.search(path_glob): + msg = """invalid glob %r: recursive glob "**" must be used alone""" + raise ValueError(msg % path_glob) + if _CHECK_MISMATCH_SET.search(path_glob): + msg = """invalid glob %r: mismatching set marker '{' or '}'""" + raise ValueError(msg % path_glob) + return _iglob(path_glob) + + +def _iglob(path_glob): + rich_path_glob = RICH_GLOB.split(path_glob, 1) + if len(rich_path_glob) > 1: + assert len(rich_path_glob) == 3, rich_path_glob + prefix, set, suffix = rich_path_glob + for item in set.split(','): + for path in _iglob(''.join((prefix, item, suffix))): + yield path + else: + if '**' not in path_glob: + for item in std_iglob(path_glob): + yield item + else: + prefix, radical = path_glob.split('**', 1) + if prefix == '': + prefix = '.' + if radical == '': + radical = '*' + else: + # we support both + radical = radical.lstrip('/') + radical = radical.lstrip('\\') + for path, dir, files in os.walk(prefix): + path = os.path.normpath(path) + for fn in _iglob(os.path.join(path, radical)): + yield fn + +if ssl: + from .compat import (HTTPSHandler as BaseHTTPSHandler, match_hostname, + CertificateError) + + +# +# HTTPSConnection which verifies certificates/matches domains +# + + class HTTPSConnection(httplib.HTTPSConnection): + ca_certs = None # set this to the path to the certs file (.pem) + check_domain = True # only used if ca_certs is not None + + # noinspection PyPropertyAccess + def connect(self): + sock = socket.create_connection((self.host, self.port), self.timeout) + if getattr(self, '_tunnel_host', False): + self.sock = sock + self._tunnel() + + if not hasattr(ssl, 'SSLContext'): + # For 2.x + if self.ca_certs: + cert_reqs = ssl.CERT_REQUIRED + else: + cert_reqs = ssl.CERT_NONE + self.sock = ssl.wrap_socket(sock, self.key_file, self.cert_file, + cert_reqs=cert_reqs, + ssl_version=ssl.PROTOCOL_SSLv23, + ca_certs=self.ca_certs) + else: # pragma: no cover + context = ssl.SSLContext(ssl.PROTOCOL_SSLv23) + context.options |= ssl.OP_NO_SSLv2 + if self.cert_file: + context.load_cert_chain(self.cert_file, self.key_file) + kwargs = {} + if self.ca_certs: + context.verify_mode = ssl.CERT_REQUIRED + context.load_verify_locations(cafile=self.ca_certs) + if getattr(ssl, 'HAS_SNI', False): + kwargs['server_hostname'] = self.host + self.sock = context.wrap_socket(sock, **kwargs) + if self.ca_certs and self.check_domain: + try: + match_hostname(self.sock.getpeercert(), self.host) + logger.debug('Host verified: %s', self.host) + except CertificateError: # pragma: no cover + self.sock.shutdown(socket.SHUT_RDWR) + self.sock.close() + raise + + class HTTPSHandler(BaseHTTPSHandler): + def __init__(self, ca_certs, check_domain=True): + BaseHTTPSHandler.__init__(self) + self.ca_certs = ca_certs + self.check_domain = check_domain + + def _conn_maker(self, *args, **kwargs): + """ + This is called to create a connection instance. Normally you'd + pass a connection class to do_open, but it doesn't actually check for + a class, and just expects a callable. As long as we behave just as a + constructor would have, we should be OK. If it ever changes so that + we *must* pass a class, we'll create an UnsafeHTTPSConnection class + which just sets check_domain to False in the class definition, and + choose which one to pass to do_open. + """ + result = HTTPSConnection(*args, **kwargs) + if self.ca_certs: + result.ca_certs = self.ca_certs + result.check_domain = self.check_domain + return result + + def https_open(self, req): + try: + return self.do_open(self._conn_maker, req) + except URLError as e: + if 'certificate verify failed' in str(e.reason): + raise CertificateError('Unable to verify server certificate ' + 'for %s' % req.host) + else: + raise + + # + # To prevent against mixing HTTP traffic with HTTPS (examples: A Man-In-The- + # Middle proxy using HTTP listens on port 443, or an index mistakenly serves + # HTML containing a http://xyz link when it should be https://xyz), + # you can use the following handler class, which does not allow HTTP traffic. + # + # It works by inheriting from HTTPHandler - so build_opener won't add a + # handler for HTTP itself. + # + class HTTPSOnlyHandler(HTTPSHandler, HTTPHandler): + def http_open(self, req): + raise URLError('Unexpected HTTP request on what should be a secure ' + 'connection: %s' % req) + +# +# XML-RPC with timeouts +# + +_ver_info = sys.version_info[:2] + +if _ver_info == (2, 6): + class HTTP(httplib.HTTP): + def __init__(self, host='', port=None, **kwargs): + if port == 0: # 0 means use port 0, not the default port + port = None + self._setup(self._connection_class(host, port, **kwargs)) + + + if ssl: + class HTTPS(httplib.HTTPS): + def __init__(self, host='', port=None, **kwargs): + if port == 0: # 0 means use port 0, not the default port + port = None + self._setup(self._connection_class(host, port, **kwargs)) + + +class Transport(xmlrpclib.Transport): + def __init__(self, timeout, use_datetime=0): + self.timeout = timeout + xmlrpclib.Transport.__init__(self, use_datetime) + + def make_connection(self, host): + h, eh, x509 = self.get_host_info(host) + if _ver_info == (2, 6): + result = HTTP(h, timeout=self.timeout) + else: + if not self._connection or host != self._connection[0]: + self._extra_headers = eh + self._connection = host, httplib.HTTPConnection(h) + result = self._connection[1] + return result + +if ssl: + class SafeTransport(xmlrpclib.SafeTransport): + def __init__(self, timeout, use_datetime=0): + self.timeout = timeout + xmlrpclib.SafeTransport.__init__(self, use_datetime) + + def make_connection(self, host): + h, eh, kwargs = self.get_host_info(host) + if not kwargs: + kwargs = {} + kwargs['timeout'] = self.timeout + if _ver_info == (2, 6): + result = HTTPS(host, None, **kwargs) + else: + if not self._connection or host != self._connection[0]: + self._extra_headers = eh + self._connection = host, httplib.HTTPSConnection(h, None, + **kwargs) + result = self._connection[1] + return result + + +class ServerProxy(xmlrpclib.ServerProxy): + def __init__(self, uri, **kwargs): + self.timeout = timeout = kwargs.pop('timeout', None) + # The above classes only come into play if a timeout + # is specified + if timeout is not None: + scheme, _ = splittype(uri) + use_datetime = kwargs.get('use_datetime', 0) + if scheme == 'https': + tcls = SafeTransport + else: + tcls = Transport + kwargs['transport'] = t = tcls(timeout, use_datetime=use_datetime) + self.transport = t + xmlrpclib.ServerProxy.__init__(self, uri, **kwargs) + +# +# CSV functionality. This is provided because on 2.x, the csv module can't +# handle Unicode. However, we need to deal with Unicode in e.g. RECORD files. +# + +def _csv_open(fn, mode, **kwargs): + if sys.version_info[0] < 3: + mode += 'b' + else: + kwargs['newline'] = '' + # Python 3 determines encoding from locale. Force 'utf-8' + # file encoding to match other forced utf-8 encoding + kwargs['encoding'] = 'utf-8' + return open(fn, mode, **kwargs) + + +class CSVBase(object): + defaults = { + 'delimiter': str(','), # The strs are used because we need native + 'quotechar': str('"'), # str in the csv API (2.x won't take + 'lineterminator': str('\n') # Unicode) + } + + def __enter__(self): + return self + + def __exit__(self, *exc_info): + self.stream.close() + + +class CSVReader(CSVBase): + def __init__(self, **kwargs): + if 'stream' in kwargs: + stream = kwargs['stream'] + if sys.version_info[0] >= 3: + # needs to be a text stream + stream = codecs.getreader('utf-8')(stream) + self.stream = stream + else: + self.stream = _csv_open(kwargs['path'], 'r') + self.reader = csv.reader(self.stream, **self.defaults) + + def __iter__(self): + return self + + def next(self): + result = next(self.reader) + if sys.version_info[0] < 3: + for i, item in enumerate(result): + if not isinstance(item, text_type): + result[i] = item.decode('utf-8') + return result + + __next__ = next + +class CSVWriter(CSVBase): + def __init__(self, fn, **kwargs): + self.stream = _csv_open(fn, 'w') + self.writer = csv.writer(self.stream, **self.defaults) + + def writerow(self, row): + if sys.version_info[0] < 3: + r = [] + for item in row: + if isinstance(item, text_type): + item = item.encode('utf-8') + r.append(item) + row = r + self.writer.writerow(row) + +# +# Configurator functionality +# + +class Configurator(BaseConfigurator): + + value_converters = dict(BaseConfigurator.value_converters) + value_converters['inc'] = 'inc_convert' + + def __init__(self, config, base=None): + super(Configurator, self).__init__(config) + self.base = base or os.getcwd() + + def configure_custom(self, config): + def convert(o): + if isinstance(o, (list, tuple)): + result = type(o)([convert(i) for i in o]) + elif isinstance(o, dict): + if '()' in o: + result = self.configure_custom(o) + else: + result = {} + for k in o: + result[k] = convert(o[k]) + else: + result = self.convert(o) + return result + + c = config.pop('()') + if not callable(c): + c = self.resolve(c) + props = config.pop('.', None) + # Check for valid identifiers + args = config.pop('[]', ()) + if args: + args = tuple([convert(o) for o in args]) + items = [(k, convert(config[k])) for k in config if valid_ident(k)] + kwargs = dict(items) + result = c(*args, **kwargs) + if props: + for n, v in props.items(): + setattr(result, n, convert(v)) + return result + + def __getitem__(self, key): + result = self.config[key] + if isinstance(result, dict) and '()' in result: + self.config[key] = result = self.configure_custom(result) + return result + + def inc_convert(self, value): + """Default converter for the inc:// protocol.""" + if not os.path.isabs(value): + value = os.path.join(self.base, value) + with codecs.open(value, 'r', encoding='utf-8') as f: + result = json.load(f) + return result + + +class SubprocessMixin(object): + """ + Mixin for running subprocesses and capturing their output + """ + def __init__(self, verbose=False, progress=None): + self.verbose = verbose + self.progress = progress + + def reader(self, stream, context): + """ + Read lines from a subprocess' output stream and either pass to a progress + callable (if specified) or write progress information to sys.stderr. + """ + progress = self.progress + verbose = self.verbose + while True: + s = stream.readline() + if not s: + break + if progress is not None: + progress(s, context) + else: + if not verbose: + sys.stderr.write('.') + else: + sys.stderr.write(s.decode('utf-8')) + sys.stderr.flush() + stream.close() + + def run_command(self, cmd, **kwargs): + p = subprocess.Popen(cmd, stdout=subprocess.PIPE, + stderr=subprocess.PIPE, **kwargs) + t1 = threading.Thread(target=self.reader, args=(p.stdout, 'stdout')) + t1.start() + t2 = threading.Thread(target=self.reader, args=(p.stderr, 'stderr')) + t2.start() + p.wait() + t1.join() + t2.join() + if self.progress is not None: + self.progress('done.', 'main') + elif self.verbose: + sys.stderr.write('done.\n') + return p + + +def normalize_name(name): + """Normalize a python package name a la PEP 503""" + # https://www.python.org/dev/peps/pep-0503/#normalized-names + return re.sub('[-_.]+', '-', name).lower() diff --git a/venv2/lib/python2.7/site-packages/pip/_vendor/distlib/version.py b/venv2/lib/python2.7/site-packages/pip/_vendor/distlib/version.py new file mode 100644 index 0000000..3eebe18 --- /dev/null +++ b/venv2/lib/python2.7/site-packages/pip/_vendor/distlib/version.py @@ -0,0 +1,736 @@ +# -*- coding: utf-8 -*- +# +# Copyright (C) 2012-2017 The Python Software Foundation. +# See LICENSE.txt and CONTRIBUTORS.txt. +# +""" +Implementation of a flexible versioning scheme providing support for PEP-440, +setuptools-compatible and semantic versioning. +""" + +import logging +import re + +from .compat import string_types +from .util import parse_requirement + +__all__ = ['NormalizedVersion', 'NormalizedMatcher', + 'LegacyVersion', 'LegacyMatcher', + 'SemanticVersion', 'SemanticMatcher', + 'UnsupportedVersionError', 'get_scheme'] + +logger = logging.getLogger(__name__) + + +class UnsupportedVersionError(ValueError): + """This is an unsupported version.""" + pass + + +class Version(object): + def __init__(self, s): + self._string = s = s.strip() + self._parts = parts = self.parse(s) + assert isinstance(parts, tuple) + assert len(parts) > 0 + + def parse(self, s): + raise NotImplementedError('please implement in a subclass') + + def _check_compatible(self, other): + if type(self) != type(other): + raise TypeError('cannot compare %r and %r' % (self, other)) + + def __eq__(self, other): + self._check_compatible(other) + return self._parts == other._parts + + def __ne__(self, other): + return not self.__eq__(other) + + def __lt__(self, other): + self._check_compatible(other) + return self._parts < other._parts + + def __gt__(self, other): + return not (self.__lt__(other) or self.__eq__(other)) + + def __le__(self, other): + return self.__lt__(other) or self.__eq__(other) + + def __ge__(self, other): + return self.__gt__(other) or self.__eq__(other) + + # See http://docs.python.org/reference/datamodel#object.__hash__ + def __hash__(self): + return hash(self._parts) + + def __repr__(self): + return "%s('%s')" % (self.__class__.__name__, self._string) + + def __str__(self): + return self._string + + @property + def is_prerelease(self): + raise NotImplementedError('Please implement in subclasses.') + + +class Matcher(object): + version_class = None + + # value is either a callable or the name of a method + _operators = { + '<': lambda v, c, p: v < c, + '>': lambda v, c, p: v > c, + '<=': lambda v, c, p: v == c or v < c, + '>=': lambda v, c, p: v == c or v > c, + '==': lambda v, c, p: v == c, + '===': lambda v, c, p: v == c, + # by default, compatible => >=. + '~=': lambda v, c, p: v == c or v > c, + '!=': lambda v, c, p: v != c, + } + + # this is a method only to support alternative implementations + # via overriding + def parse_requirement(self, s): + return parse_requirement(s) + + def __init__(self, s): + if self.version_class is None: + raise ValueError('Please specify a version class') + self._string = s = s.strip() + r = self.parse_requirement(s) + if not r: + raise ValueError('Not valid: %r' % s) + self.name = r.name + self.key = self.name.lower() # for case-insensitive comparisons + clist = [] + if r.constraints: + # import pdb; pdb.set_trace() + for op, s in r.constraints: + if s.endswith('.*'): + if op not in ('==', '!='): + raise ValueError('\'.*\' not allowed for ' + '%r constraints' % op) + # Could be a partial version (e.g. for '2.*') which + # won't parse as a version, so keep it as a string + vn, prefix = s[:-2], True + # Just to check that vn is a valid version + self.version_class(vn) + else: + # Should parse as a version, so we can create an + # instance for the comparison + vn, prefix = self.version_class(s), False + clist.append((op, vn, prefix)) + self._parts = tuple(clist) + + def match(self, version): + """ + Check if the provided version matches the constraints. + + :param version: The version to match against this instance. + :type version: String or :class:`Version` instance. + """ + if isinstance(version, string_types): + version = self.version_class(version) + for operator, constraint, prefix in self._parts: + f = self._operators.get(operator) + if isinstance(f, string_types): + f = getattr(self, f) + if not f: + msg = ('%r not implemented ' + 'for %s' % (operator, self.__class__.__name__)) + raise NotImplementedError(msg) + if not f(version, constraint, prefix): + return False + return True + + @property + def exact_version(self): + result = None + if len(self._parts) == 1 and self._parts[0][0] in ('==', '==='): + result = self._parts[0][1] + return result + + def _check_compatible(self, other): + if type(self) != type(other) or self.name != other.name: + raise TypeError('cannot compare %s and %s' % (self, other)) + + def __eq__(self, other): + self._check_compatible(other) + return self.key == other.key and self._parts == other._parts + + def __ne__(self, other): + return not self.__eq__(other) + + # See http://docs.python.org/reference/datamodel#object.__hash__ + def __hash__(self): + return hash(self.key) + hash(self._parts) + + def __repr__(self): + return "%s(%r)" % (self.__class__.__name__, self._string) + + def __str__(self): + return self._string + + +PEP440_VERSION_RE = re.compile(r'^v?(\d+!)?(\d+(\.\d+)*)((a|b|c|rc)(\d+))?' + r'(\.(post)(\d+))?(\.(dev)(\d+))?' + r'(\+([a-zA-Z\d]+(\.[a-zA-Z\d]+)?))?$') + + +def _pep_440_key(s): + s = s.strip() + m = PEP440_VERSION_RE.match(s) + if not m: + raise UnsupportedVersionError('Not a valid version: %s' % s) + groups = m.groups() + nums = tuple(int(v) for v in groups[1].split('.')) + while len(nums) > 1 and nums[-1] == 0: + nums = nums[:-1] + + if not groups[0]: + epoch = 0 + else: + epoch = int(groups[0]) + pre = groups[4:6] + post = groups[7:9] + dev = groups[10:12] + local = groups[13] + if pre == (None, None): + pre = () + else: + pre = pre[0], int(pre[1]) + if post == (None, None): + post = () + else: + post = post[0], int(post[1]) + if dev == (None, None): + dev = () + else: + dev = dev[0], int(dev[1]) + if local is None: + local = () + else: + parts = [] + for part in local.split('.'): + # to ensure that numeric compares as > lexicographic, avoid + # comparing them directly, but encode a tuple which ensures + # correct sorting + if part.isdigit(): + part = (1, int(part)) + else: + part = (0, part) + parts.append(part) + local = tuple(parts) + if not pre: + # either before pre-release, or final release and after + if not post and dev: + # before pre-release + pre = ('a', -1) # to sort before a0 + else: + pre = ('z',) # to sort after all pre-releases + # now look at the state of post and dev. + if not post: + post = ('_',) # sort before 'a' + if not dev: + dev = ('final',) + + #print('%s -> %s' % (s, m.groups())) + return epoch, nums, pre, post, dev, local + + +_normalized_key = _pep_440_key + + +class NormalizedVersion(Version): + """A rational version. + + Good: + 1.2 # equivalent to "1.2.0" + 1.2.0 + 1.2a1 + 1.2.3a2 + 1.2.3b1 + 1.2.3c1 + 1.2.3.4 + TODO: fill this out + + Bad: + 1 # minimum two numbers + 1.2a # release level must have a release serial + 1.2.3b + """ + def parse(self, s): + result = _normalized_key(s) + # _normalized_key loses trailing zeroes in the release + # clause, since that's needed to ensure that X.Y == X.Y.0 == X.Y.0.0 + # However, PEP 440 prefix matching needs it: for example, + # (~= 1.4.5.0) matches differently to (~= 1.4.5.0.0). + m = PEP440_VERSION_RE.match(s) # must succeed + groups = m.groups() + self._release_clause = tuple(int(v) for v in groups[1].split('.')) + return result + + PREREL_TAGS = set(['a', 'b', 'c', 'rc', 'dev']) + + @property + def is_prerelease(self): + return any(t[0] in self.PREREL_TAGS for t in self._parts if t) + + +def _match_prefix(x, y): + x = str(x) + y = str(y) + if x == y: + return True + if not x.startswith(y): + return False + n = len(y) + return x[n] == '.' + + +class NormalizedMatcher(Matcher): + version_class = NormalizedVersion + + # value is either a callable or the name of a method + _operators = { + '~=': '_match_compatible', + '<': '_match_lt', + '>': '_match_gt', + '<=': '_match_le', + '>=': '_match_ge', + '==': '_match_eq', + '===': '_match_arbitrary', + '!=': '_match_ne', + } + + def _adjust_local(self, version, constraint, prefix): + if prefix: + strip_local = '+' not in constraint and version._parts[-1] + else: + # both constraint and version are + # NormalizedVersion instances. + # If constraint does not have a local component, + # ensure the version doesn't, either. + strip_local = not constraint._parts[-1] and version._parts[-1] + if strip_local: + s = version._string.split('+', 1)[0] + version = self.version_class(s) + return version, constraint + + def _match_lt(self, version, constraint, prefix): + version, constraint = self._adjust_local(version, constraint, prefix) + if version >= constraint: + return False + release_clause = constraint._release_clause + pfx = '.'.join([str(i) for i in release_clause]) + return not _match_prefix(version, pfx) + + def _match_gt(self, version, constraint, prefix): + version, constraint = self._adjust_local(version, constraint, prefix) + if version <= constraint: + return False + release_clause = constraint._release_clause + pfx = '.'.join([str(i) for i in release_clause]) + return not _match_prefix(version, pfx) + + def _match_le(self, version, constraint, prefix): + version, constraint = self._adjust_local(version, constraint, prefix) + return version <= constraint + + def _match_ge(self, version, constraint, prefix): + version, constraint = self._adjust_local(version, constraint, prefix) + return version >= constraint + + def _match_eq(self, version, constraint, prefix): + version, constraint = self._adjust_local(version, constraint, prefix) + if not prefix: + result = (version == constraint) + else: + result = _match_prefix(version, constraint) + return result + + def _match_arbitrary(self, version, constraint, prefix): + return str(version) == str(constraint) + + def _match_ne(self, version, constraint, prefix): + version, constraint = self._adjust_local(version, constraint, prefix) + if not prefix: + result = (version != constraint) + else: + result = not _match_prefix(version, constraint) + return result + + def _match_compatible(self, version, constraint, prefix): + version, constraint = self._adjust_local(version, constraint, prefix) + if version == constraint: + return True + if version < constraint: + return False +# if not prefix: +# return True + release_clause = constraint._release_clause + if len(release_clause) > 1: + release_clause = release_clause[:-1] + pfx = '.'.join([str(i) for i in release_clause]) + return _match_prefix(version, pfx) + +_REPLACEMENTS = ( + (re.compile('[.+-]$'), ''), # remove trailing puncts + (re.compile(r'^[.](\d)'), r'0.\1'), # .N -> 0.N at start + (re.compile('^[.-]'), ''), # remove leading puncts + (re.compile(r'^\((.*)\)$'), r'\1'), # remove parentheses + (re.compile(r'^v(ersion)?\s*(\d+)'), r'\2'), # remove leading v(ersion) + (re.compile(r'^r(ev)?\s*(\d+)'), r'\2'), # remove leading v(ersion) + (re.compile('[.]{2,}'), '.'), # multiple runs of '.' + (re.compile(r'\b(alfa|apha)\b'), 'alpha'), # misspelt alpha + (re.compile(r'\b(pre-alpha|prealpha)\b'), + 'pre.alpha'), # standardise + (re.compile(r'\(beta\)$'), 'beta'), # remove parentheses +) + +_SUFFIX_REPLACEMENTS = ( + (re.compile('^[:~._+-]+'), ''), # remove leading puncts + (re.compile('[,*")([\\]]'), ''), # remove unwanted chars + (re.compile('[~:+_ -]'), '.'), # replace illegal chars + (re.compile('[.]{2,}'), '.'), # multiple runs of '.' + (re.compile(r'\.$'), ''), # trailing '.' +) + +_NUMERIC_PREFIX = re.compile(r'(\d+(\.\d+)*)') + + +def _suggest_semantic_version(s): + """ + Try to suggest a semantic form for a version for which + _suggest_normalized_version couldn't come up with anything. + """ + result = s.strip().lower() + for pat, repl in _REPLACEMENTS: + result = pat.sub(repl, result) + if not result: + result = '0.0.0' + + # Now look for numeric prefix, and separate it out from + # the rest. + #import pdb; pdb.set_trace() + m = _NUMERIC_PREFIX.match(result) + if not m: + prefix = '0.0.0' + suffix = result + else: + prefix = m.groups()[0].split('.') + prefix = [int(i) for i in prefix] + while len(prefix) < 3: + prefix.append(0) + if len(prefix) == 3: + suffix = result[m.end():] + else: + suffix = '.'.join([str(i) for i in prefix[3:]]) + result[m.end():] + prefix = prefix[:3] + prefix = '.'.join([str(i) for i in prefix]) + suffix = suffix.strip() + if suffix: + #import pdb; pdb.set_trace() + # massage the suffix. + for pat, repl in _SUFFIX_REPLACEMENTS: + suffix = pat.sub(repl, suffix) + + if not suffix: + result = prefix + else: + sep = '-' if 'dev' in suffix else '+' + result = prefix + sep + suffix + if not is_semver(result): + result = None + return result + + +def _suggest_normalized_version(s): + """Suggest a normalized version close to the given version string. + + If you have a version string that isn't rational (i.e. NormalizedVersion + doesn't like it) then you might be able to get an equivalent (or close) + rational version from this function. + + This does a number of simple normalizations to the given string, based + on observation of versions currently in use on PyPI. Given a dump of + those version during PyCon 2009, 4287 of them: + - 2312 (53.93%) match NormalizedVersion without change + with the automatic suggestion + - 3474 (81.04%) match when using this suggestion method + + @param s {str} An irrational version string. + @returns A rational version string, or None, if couldn't determine one. + """ + try: + _normalized_key(s) + return s # already rational + except UnsupportedVersionError: + pass + + rs = s.lower() + + # part of this could use maketrans + for orig, repl in (('-alpha', 'a'), ('-beta', 'b'), ('alpha', 'a'), + ('beta', 'b'), ('rc', 'c'), ('-final', ''), + ('-pre', 'c'), + ('-release', ''), ('.release', ''), ('-stable', ''), + ('+', '.'), ('_', '.'), (' ', ''), ('.final', ''), + ('final', '')): + rs = rs.replace(orig, repl) + + # if something ends with dev or pre, we add a 0 + rs = re.sub(r"pre$", r"pre0", rs) + rs = re.sub(r"dev$", r"dev0", rs) + + # if we have something like "b-2" or "a.2" at the end of the + # version, that is probably beta, alpha, etc + # let's remove the dash or dot + rs = re.sub(r"([abc]|rc)[\-\.](\d+)$", r"\1\2", rs) + + # 1.0-dev-r371 -> 1.0.dev371 + # 0.1-dev-r79 -> 0.1.dev79 + rs = re.sub(r"[\-\.](dev)[\-\.]?r?(\d+)$", r".\1\2", rs) + + # Clean: 2.0.a.3, 2.0.b1, 0.9.0~c1 + rs = re.sub(r"[.~]?([abc])\.?", r"\1", rs) + + # Clean: v0.3, v1.0 + if rs.startswith('v'): + rs = rs[1:] + + # Clean leading '0's on numbers. + #TODO: unintended side-effect on, e.g., "2003.05.09" + # PyPI stats: 77 (~2%) better + rs = re.sub(r"\b0+(\d+)(?!\d)", r"\1", rs) + + # Clean a/b/c with no version. E.g. "1.0a" -> "1.0a0". Setuptools infers + # zero. + # PyPI stats: 245 (7.56%) better + rs = re.sub(r"(\d+[abc])$", r"\g<1>0", rs) + + # the 'dev-rNNN' tag is a dev tag + rs = re.sub(r"\.?(dev-r|dev\.r)\.?(\d+)$", r".dev\2", rs) + + # clean the - when used as a pre delimiter + rs = re.sub(r"-(a|b|c)(\d+)$", r"\1\2", rs) + + # a terminal "dev" or "devel" can be changed into ".dev0" + rs = re.sub(r"[\.\-](dev|devel)$", r".dev0", rs) + + # a terminal "dev" can be changed into ".dev0" + rs = re.sub(r"(?![\.\-])dev$", r".dev0", rs) + + # a terminal "final" or "stable" can be removed + rs = re.sub(r"(final|stable)$", "", rs) + + # The 'r' and the '-' tags are post release tags + # 0.4a1.r10 -> 0.4a1.post10 + # 0.9.33-17222 -> 0.9.33.post17222 + # 0.9.33-r17222 -> 0.9.33.post17222 + rs = re.sub(r"\.?(r|-|-r)\.?(\d+)$", r".post\2", rs) + + # Clean 'r' instead of 'dev' usage: + # 0.9.33+r17222 -> 0.9.33.dev17222 + # 1.0dev123 -> 1.0.dev123 + # 1.0.git123 -> 1.0.dev123 + # 1.0.bzr123 -> 1.0.dev123 + # 0.1a0dev.123 -> 0.1a0.dev123 + # PyPI stats: ~150 (~4%) better + rs = re.sub(r"\.?(dev|git|bzr)\.?(\d+)$", r".dev\2", rs) + + # Clean '.pre' (normalized from '-pre' above) instead of 'c' usage: + # 0.2.pre1 -> 0.2c1 + # 0.2-c1 -> 0.2c1 + # 1.0preview123 -> 1.0c123 + # PyPI stats: ~21 (0.62%) better + rs = re.sub(r"\.?(pre|preview|-c)(\d+)$", r"c\g<2>", rs) + + # Tcl/Tk uses "px" for their post release markers + rs = re.sub(r"p(\d+)$", r".post\1", rs) + + try: + _normalized_key(rs) + except UnsupportedVersionError: + rs = None + return rs + +# +# Legacy version processing (distribute-compatible) +# + +_VERSION_PART = re.compile(r'([a-z]+|\d+|[\.-])', re.I) +_VERSION_REPLACE = { + 'pre': 'c', + 'preview': 'c', + '-': 'final-', + 'rc': 'c', + 'dev': '@', + '': None, + '.': None, +} + + +def _legacy_key(s): + def get_parts(s): + result = [] + for p in _VERSION_PART.split(s.lower()): + p = _VERSION_REPLACE.get(p, p) + if p: + if '0' <= p[:1] <= '9': + p = p.zfill(8) + else: + p = '*' + p + result.append(p) + result.append('*final') + return result + + result = [] + for p in get_parts(s): + if p.startswith('*'): + if p < '*final': + while result and result[-1] == '*final-': + result.pop() + while result and result[-1] == '00000000': + result.pop() + result.append(p) + return tuple(result) + + +class LegacyVersion(Version): + def parse(self, s): + return _legacy_key(s) + + @property + def is_prerelease(self): + result = False + for x in self._parts: + if (isinstance(x, string_types) and x.startswith('*') and + x < '*final'): + result = True + break + return result + + +class LegacyMatcher(Matcher): + version_class = LegacyVersion + + _operators = dict(Matcher._operators) + _operators['~='] = '_match_compatible' + + numeric_re = re.compile(r'^(\d+(\.\d+)*)') + + def _match_compatible(self, version, constraint, prefix): + if version < constraint: + return False + m = self.numeric_re.match(str(constraint)) + if not m: + logger.warning('Cannot compute compatible match for version %s ' + ' and constraint %s', version, constraint) + return True + s = m.groups()[0] + if '.' in s: + s = s.rsplit('.', 1)[0] + return _match_prefix(version, s) + +# +# Semantic versioning +# + +_SEMVER_RE = re.compile(r'^(\d+)\.(\d+)\.(\d+)' + r'(-[a-z0-9]+(\.[a-z0-9-]+)*)?' + r'(\+[a-z0-9]+(\.[a-z0-9-]+)*)?$', re.I) + + +def is_semver(s): + return _SEMVER_RE.match(s) + + +def _semantic_key(s): + def make_tuple(s, absent): + if s is None: + result = (absent,) + else: + parts = s[1:].split('.') + # We can't compare ints and strings on Python 3, so fudge it + # by zero-filling numeric values so simulate a numeric comparison + result = tuple([p.zfill(8) if p.isdigit() else p for p in parts]) + return result + + m = is_semver(s) + if not m: + raise UnsupportedVersionError(s) + groups = m.groups() + major, minor, patch = [int(i) for i in groups[:3]] + # choose the '|' and '*' so that versions sort correctly + pre, build = make_tuple(groups[3], '|'), make_tuple(groups[5], '*') + return (major, minor, patch), pre, build + + +class SemanticVersion(Version): + def parse(self, s): + return _semantic_key(s) + + @property + def is_prerelease(self): + return self._parts[1][0] != '|' + + +class SemanticMatcher(Matcher): + version_class = SemanticVersion + + +class VersionScheme(object): + def __init__(self, key, matcher, suggester=None): + self.key = key + self.matcher = matcher + self.suggester = suggester + + def is_valid_version(self, s): + try: + self.matcher.version_class(s) + result = True + except UnsupportedVersionError: + result = False + return result + + def is_valid_matcher(self, s): + try: + self.matcher(s) + result = True + except UnsupportedVersionError: + result = False + return result + + def is_valid_constraint_list(self, s): + """ + Used for processing some metadata fields + """ + return self.is_valid_matcher('dummy_name (%s)' % s) + + def suggest(self, s): + if self.suggester is None: + result = None + else: + result = self.suggester(s) + return result + +_SCHEMES = { + 'normalized': VersionScheme(_normalized_key, NormalizedMatcher, + _suggest_normalized_version), + 'legacy': VersionScheme(_legacy_key, LegacyMatcher, lambda self, s: s), + 'semantic': VersionScheme(_semantic_key, SemanticMatcher, + _suggest_semantic_version), +} + +_SCHEMES['default'] = _SCHEMES['normalized'] + + +def get_scheme(name): + if name not in _SCHEMES: + raise ValueError('unknown scheme name: %r' % name) + return _SCHEMES[name] diff --git a/venv2/lib/python2.7/site-packages/pip/_vendor/distlib/w32.exe b/venv2/lib/python2.7/site-packages/pip/_vendor/distlib/w32.exe new file mode 100644 index 0000000000000000000000000000000000000000..732215a9d34ccb7b417d637a7646d9b843ecafa8 GIT binary patch literal 89088 zcmeFae|S?>wm*FGqitH!CO`{C3REl(SahnPNDYM`O{q#T7)Xdvz$<hz5gjb&0FD$| zPs=nt#$jgk-tk`NUb*PJIy$3wMg@OiORy=5PEkZ0P^;6cyPm2=Y8#~Xyq~pC(iU~@ z_dd`2ywCg3TS)e=wSTX@_S$Q$y;FJf9>F9Cf*Bu86NCeB>CeT#|L3nblC!40kR?2m z{>H@z3`^g*ct!B1Tk<#8{Ol(+x7?n8>n(TO@iQ_1pEl;#NO$D_^p5<p8@>6r|7^p? zD>5@P3KB)%InlPUc<U{5lb_$uJ2!U@!dIxT&CTYnHuoCtzCL#`-1%8w%`JjkymkHD zpYr&>Cg9H}+(GW%^wV2|ROUbGfyXTfOART)i?<!WISJD#7!6|#8G`TvV*Xu^z32+K zc6>T0?9%;4K}Zn{6fx`yPa}*$9C+R!7zI~72c&$InY+UdMGBkF3c`HyxD3K09`bzW z?_q;rO&5ec#{?noJ4vI19bbHBt~vx^h2FH$V8i|^#EsiUg#JboP3@w-(&Uf&%NK<8 zSJZ5{MZ852W)~s>WeT(LIf&1wKNqULLI)GN_(-E-D)X~ZK=1;t<%*guHMhdg`-(mb zHzDv1KBN9zR9?--O+N$ReOXAr81V9z!X5SJ5`=3<1^<8V|AP@&sr2}Qvp;hQT24iW zOHg|EiZd1ojV;oo#(r^ba2`^8T22{~_UQ@YMZp7O1R*2@?SerFE~TuJB_wDaYC0h8 zfONF1t%{=H`W`bdYp+>YBsg9Ty9ec3iy+O3xa}TIvPK#Q&udyx1MvwG0(#iaGC|N| zJ#3?<Y4YLRkU`54s9BYRjyISA>){9$tW;Y34lPnX=&>D4X~|k7c$TwEfOzs@Yh#Nz z`FV;`(w!E`sKg@`2E}bDY>ku^4XS@tV(WO*<eu67;0Clk;vRHp!Qf<+5w0B!*Y>g? zYH=KK#%%Yu1~&m>IlB^#2^syGG|2AB1(}4aOcaC%!)}%`I7AIC2(Ro3yW`GSttng^ z_xb=ECor!L{-PNO>_ulJ3%fm=O0X!s%)$GZ?~I94l-^KEAX0n$?4wGpr7&i4#~)OB zQD%2NrWUKtZuYT1Vnu}AeF`cSgx>Rkk@}Lg{WltgT76VeA2aic`cTnpXrt2WXmJkM z9%u<Rp-*9{$HQ)>Xm?McyDyZ28Ux7mpxy?mnmvzMMr-85vkRrJLaDRx>|I7je+cM+ zj{RJ(3Vrgke;W@#D!y%U%fQLtlPKTAzWtVuOQdXpwsy6eRjt^cZ%0D4bF7$F;f!th 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ð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 literal 0 HcmV?d00001 diff --git a/venv2/lib/python2.7/site-packages/pip/_vendor/distlib/w64.exe b/venv2/lib/python2.7/site-packages/pip/_vendor/distlib/w64.exe new file mode 100644 index 0000000000000000000000000000000000000000..c41bd0a011fd760ce20ba795d9e535e0d2c39876 GIT binary patch literal 99328 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 B=L-M; literal 0 HcmV?d00001 diff --git a/venv2/lib/python2.7/site-packages/pip/_vendor/distlib/wheel.py b/venv2/lib/python2.7/site-packages/pip/_vendor/distlib/wheel.py new file mode 100644 index 0000000..b04bfae --- /dev/null +++ b/venv2/lib/python2.7/site-packages/pip/_vendor/distlib/wheel.py @@ -0,0 +1,988 @@ +# -*- coding: utf-8 -*- +# +# Copyright (C) 2013-2017 Vinay Sajip. +# Licensed to the Python Software Foundation under a contributor agreement. +# See LICENSE.txt and CONTRIBUTORS.txt. +# +from __future__ import unicode_literals + +import base64 +import codecs +import datetime +import distutils.util +from email import message_from_file +import hashlib +import imp +import json +import logging +import os +import posixpath +import re +import shutil +import sys +import tempfile +import zipfile + +from . import __version__, DistlibException +from .compat import sysconfig, ZipFile, fsdecode, text_type, filter +from .database import InstalledDistribution +from .metadata import Metadata, METADATA_FILENAME, WHEEL_METADATA_FILENAME +from .util import (FileOperator, convert_path, CSVReader, CSVWriter, Cache, + cached_property, get_cache_base, read_exports, tempdir) +from .version import NormalizedVersion, UnsupportedVersionError + +logger = logging.getLogger(__name__) + +cache = None # created when needed + +if hasattr(sys, 'pypy_version_info'): # pragma: no cover + IMP_PREFIX = 'pp' +elif sys.platform.startswith('java'): # pragma: no cover + IMP_PREFIX = 'jy' +elif sys.platform == 'cli': # pragma: no cover + IMP_PREFIX = 'ip' +else: + IMP_PREFIX = 'cp' + +VER_SUFFIX = sysconfig.get_config_var('py_version_nodot') +if not VER_SUFFIX: # pragma: no cover + VER_SUFFIX = '%s%s' % sys.version_info[:2] +PYVER = 'py' + VER_SUFFIX +IMPVER = IMP_PREFIX + VER_SUFFIX + +ARCH = distutils.util.get_platform().replace('-', '_').replace('.', '_') + +ABI = sysconfig.get_config_var('SOABI') +if ABI and ABI.startswith('cpython-'): + ABI = ABI.replace('cpython-', 'cp') +else: + def _derive_abi(): + parts = ['cp', VER_SUFFIX] + if sysconfig.get_config_var('Py_DEBUG'): + parts.append('d') + if sysconfig.get_config_var('WITH_PYMALLOC'): + parts.append('m') + if sysconfig.get_config_var('Py_UNICODE_SIZE') == 4: + parts.append('u') + return ''.join(parts) + ABI = _derive_abi() + del _derive_abi + +FILENAME_RE = re.compile(r''' +(?P<nm>[^-]+) +-(?P<vn>\d+[^-]*) +(-(?P<bn>\d+[^-]*))? +-(?P<py>\w+\d+(\.\w+\d+)*) +-(?P<bi>\w+) +-(?P<ar>\w+(\.\w+)*) +\.whl$ +''', re.IGNORECASE | re.VERBOSE) + +NAME_VERSION_RE = re.compile(r''' +(?P<nm>[^-]+) +-(?P<vn>\d+[^-]*) +(-(?P<bn>\d+[^-]*))?$ +''', re.IGNORECASE | re.VERBOSE) + +SHEBANG_RE = re.compile(br'\s*#![^\r\n]*') +SHEBANG_DETAIL_RE = re.compile(br'^(\s*#!("[^"]+"|\S+))\s+(.*)$') +SHEBANG_PYTHON = b'#!python' +SHEBANG_PYTHONW = b'#!pythonw' + +if os.sep == '/': + to_posix = lambda o: o +else: + to_posix = lambda o: o.replace(os.sep, '/') + + +class Mounter(object): + def __init__(self): + self.impure_wheels = {} + self.libs = {} + + def add(self, pathname, extensions): + self.impure_wheels[pathname] = extensions + self.libs.update(extensions) + + def remove(self, pathname): + extensions = self.impure_wheels.pop(pathname) + for k, v in extensions: + if k in self.libs: + del self.libs[k] + + def find_module(self, fullname, path=None): + if fullname in self.libs: + result = self + else: + result = None + return result + + def load_module(self, fullname): + if fullname in sys.modules: + result = sys.modules[fullname] + else: + if fullname not in self.libs: + raise ImportError('unable to find extension for %s' % fullname) + result = imp.load_dynamic(fullname, self.libs[fullname]) + result.__loader__ = self + parts = fullname.rsplit('.', 1) + if len(parts) > 1: + result.__package__ = parts[0] + return result + +_hook = Mounter() + + +class Wheel(object): + """ + Class to build and install from Wheel files (PEP 427). + """ + + wheel_version = (1, 1) + hash_kind = 'sha256' + + def __init__(self, filename=None, sign=False, verify=False): + """ + Initialise an instance using a (valid) filename. + """ + self.sign = sign + self.should_verify = verify + self.buildver = '' + self.pyver = [PYVER] + self.abi = ['none'] + self.arch = ['any'] + self.dirname = os.getcwd() + if filename is None: + self.name = 'dummy' + self.version = '0.1' + self._filename = self.filename + else: + m = NAME_VERSION_RE.match(filename) + if m: + info = m.groupdict('') + self.name = info['nm'] + # Reinstate the local version separator + self.version = info['vn'].replace('_', '-') + self.buildver = info['bn'] + self._filename = self.filename + else: + dirname, filename = os.path.split(filename) + m = FILENAME_RE.match(filename) + if not m: + raise DistlibException('Invalid name or ' + 'filename: %r' % filename) + if dirname: + self.dirname = os.path.abspath(dirname) + self._filename = filename + info = m.groupdict('') + self.name = info['nm'] + self.version = info['vn'] + self.buildver = info['bn'] + self.pyver = info['py'].split('.') + self.abi = info['bi'].split('.') + self.arch = info['ar'].split('.') + + @property + def filename(self): + """ + Build and return a filename from the various components. + """ + if self.buildver: + buildver = '-' + self.buildver + else: + buildver = '' + pyver = '.'.join(self.pyver) + abi = '.'.join(self.abi) + arch = '.'.join(self.arch) + # replace - with _ as a local version separator + version = self.version.replace('-', '_') + return '%s-%s%s-%s-%s-%s.whl' % (self.name, version, buildver, + pyver, abi, arch) + + @property + def exists(self): + path = os.path.join(self.dirname, self.filename) + return os.path.isfile(path) + + @property + def tags(self): + for pyver in self.pyver: + for abi in self.abi: + for arch in self.arch: + yield pyver, abi, arch + + @cached_property + def metadata(self): + pathname = os.path.join(self.dirname, self.filename) + name_ver = '%s-%s' % (self.name, self.version) + info_dir = '%s.dist-info' % name_ver + wrapper = codecs.getreader('utf-8') + with ZipFile(pathname, 'r') as zf: + wheel_metadata = self.get_wheel_metadata(zf) + wv = wheel_metadata['Wheel-Version'].split('.', 1) + file_version = tuple([int(i) for i in wv]) + if file_version < (1, 1): + fns = [WHEEL_METADATA_FILENAME, METADATA_FILENAME, 'METADATA'] + else: + fns = [WHEEL_METADATA_FILENAME, METADATA_FILENAME] + result = None + for fn in fns: + try: + metadata_filename = posixpath.join(info_dir, fn) + with zf.open(metadata_filename) as bf: + wf = wrapper(bf) + result = Metadata(fileobj=wf) + if result: + break + except KeyError: + pass + if not result: + raise ValueError('Invalid wheel, because metadata is ' + 'missing: looked in %s' % ', '.join(fns)) + return result + + def get_wheel_metadata(self, zf): + name_ver = '%s-%s' % (self.name, self.version) + info_dir = '%s.dist-info' % name_ver + metadata_filename = posixpath.join(info_dir, 'WHEEL') + with zf.open(metadata_filename) as bf: + wf = codecs.getreader('utf-8')(bf) + message = message_from_file(wf) + return dict(message) + + @cached_property + def info(self): + pathname = os.path.join(self.dirname, self.filename) + with ZipFile(pathname, 'r') as zf: + result = self.get_wheel_metadata(zf) + return result + + def process_shebang(self, data): + m = SHEBANG_RE.match(data) + if m: + end = m.end() + shebang, data_after_shebang = data[:end], data[end:] + # Preserve any arguments after the interpreter + if b'pythonw' in shebang.lower(): + shebang_python = SHEBANG_PYTHONW + else: + shebang_python = SHEBANG_PYTHON + m = SHEBANG_DETAIL_RE.match(shebang) + if m: + args = b' ' + m.groups()[-1] + else: + args = b'' + shebang = shebang_python + args + data = shebang + data_after_shebang + else: + cr = data.find(b'\r') + lf = data.find(b'\n') + if cr < 0 or cr > lf: + term = b'\n' + else: + if data[cr:cr + 2] == b'\r\n': + term = b'\r\n' + else: + term = b'\r' + data = SHEBANG_PYTHON + term + data + return data + + def get_hash(self, data, hash_kind=None): + if hash_kind is None: + hash_kind = self.hash_kind + try: + hasher = getattr(hashlib, hash_kind) + except AttributeError: + raise DistlibException('Unsupported hash algorithm: %r' % hash_kind) + result = hasher(data).digest() + result = base64.urlsafe_b64encode(result).rstrip(b'=').decode('ascii') + return hash_kind, result + + def write_record(self, records, record_path, base): + records = list(records) # make a copy for sorting + p = to_posix(os.path.relpath(record_path, base)) + records.append((p, '', '')) + records.sort() + with CSVWriter(record_path) as writer: + for row in records: + writer.writerow(row) + + def write_records(self, info, libdir, archive_paths): + records = [] + distinfo, info_dir = info + hasher = getattr(hashlib, self.hash_kind) + for ap, p in archive_paths: + with open(p, 'rb') as f: + data = f.read() + digest = '%s=%s' % self.get_hash(data) + size = os.path.getsize(p) + records.append((ap, digest, size)) + + p = os.path.join(distinfo, 'RECORD') + self.write_record(records, p, libdir) + ap = to_posix(os.path.join(info_dir, 'RECORD')) + archive_paths.append((ap, p)) + + def build_zip(self, pathname, archive_paths): + with ZipFile(pathname, 'w', zipfile.ZIP_DEFLATED) as zf: + for ap, p in archive_paths: + logger.debug('Wrote %s to %s in wheel', p, ap) + zf.write(p, ap) + + def build(self, paths, tags=None, wheel_version=None): + """ + Build a wheel from files in specified paths, and use any specified tags + when determining the name of the wheel. + """ + if tags is None: + tags = {} + + libkey = list(filter(lambda o: o in paths, ('purelib', 'platlib')))[0] + if libkey == 'platlib': + is_pure = 'false' + default_pyver = [IMPVER] + default_abi = [ABI] + default_arch = [ARCH] + else: + is_pure = 'true' + default_pyver = [PYVER] + default_abi = ['none'] + default_arch = ['any'] + + self.pyver = tags.get('pyver', default_pyver) + self.abi = tags.get('abi', default_abi) + self.arch = tags.get('arch', default_arch) + + libdir = paths[libkey] + + name_ver = '%s-%s' % (self.name, self.version) + data_dir = '%s.data' % name_ver + info_dir = '%s.dist-info' % name_ver + + archive_paths = [] + + # First, stuff which is not in site-packages + for key in ('data', 'headers', 'scripts'): + if key not in paths: + continue + path = paths[key] + if os.path.isdir(path): + for root, dirs, files in os.walk(path): + for fn in files: + p = fsdecode(os.path.join(root, fn)) + rp = os.path.relpath(p, path) + ap = to_posix(os.path.join(data_dir, key, rp)) + archive_paths.append((ap, p)) + if key == 'scripts' and not p.endswith('.exe'): + with open(p, 'rb') as f: + data = f.read() + data = self.process_shebang(data) + with open(p, 'wb') as f: + f.write(data) + + # Now, stuff which is in site-packages, other than the + # distinfo stuff. + path = libdir + distinfo = None + for root, dirs, files in os.walk(path): + if root == path: + # At the top level only, save distinfo for later + # and skip it for now + for i, dn in enumerate(dirs): + dn = fsdecode(dn) + if dn.endswith('.dist-info'): + distinfo = os.path.join(root, dn) + del dirs[i] + break + assert distinfo, '.dist-info directory expected, not found' + + for fn in files: + # comment out next suite to leave .pyc files in + if fsdecode(fn).endswith(('.pyc', '.pyo')): + continue + p = os.path.join(root, fn) + rp = to_posix(os.path.relpath(p, path)) + archive_paths.append((rp, p)) + + # Now distinfo. Assumed to be flat, i.e. os.listdir is enough. + files = os.listdir(distinfo) + for fn in files: + if fn not in ('RECORD', 'INSTALLER', 'SHARED', 'WHEEL'): + p = fsdecode(os.path.join(distinfo, fn)) + ap = to_posix(os.path.join(info_dir, fn)) + archive_paths.append((ap, p)) + + wheel_metadata = [ + 'Wheel-Version: %d.%d' % (wheel_version or self.wheel_version), + 'Generator: distlib %s' % __version__, + 'Root-Is-Purelib: %s' % is_pure, + ] + for pyver, abi, arch in self.tags: + wheel_metadata.append('Tag: %s-%s-%s' % (pyver, abi, arch)) + p = os.path.join(distinfo, 'WHEEL') + with open(p, 'w') as f: + f.write('\n'.join(wheel_metadata)) + ap = to_posix(os.path.join(info_dir, 'WHEEL')) + archive_paths.append((ap, p)) + + # Now, at last, RECORD. + # Paths in here are archive paths - nothing else makes sense. + self.write_records((distinfo, info_dir), libdir, archive_paths) + # Now, ready to build the zip file + pathname = os.path.join(self.dirname, self.filename) + self.build_zip(pathname, archive_paths) + return pathname + + def install(self, paths, maker, **kwargs): + """ + Install a wheel to the specified paths. If kwarg ``warner`` is + specified, it should be a callable, which will be called with two + tuples indicating the wheel version of this software and the wheel + version in the file, if there is a discrepancy in the versions. + This can be used to issue any warnings to raise any exceptions. + If kwarg ``lib_only`` is True, only the purelib/platlib files are + installed, and the headers, scripts, data and dist-info metadata are + not written. If kwarg ``bytecode_hashed_invalidation`` is True, written + bytecode will try to use file-hash based invalidation (PEP-552) on + supported interpreter versions (CPython 2.7+). + + The return value is a :class:`InstalledDistribution` instance unless + ``options.lib_only`` is True, in which case the return value is ``None``. + """ + + dry_run = maker.dry_run + warner = kwargs.get('warner') + lib_only = kwargs.get('lib_only', False) + bc_hashed_invalidation = kwargs.get('bytecode_hashed_invalidation', False) + + pathname = os.path.join(self.dirname, self.filename) + name_ver = '%s-%s' % (self.name, self.version) + data_dir = '%s.data' % name_ver + info_dir = '%s.dist-info' % name_ver + + metadata_name = posixpath.join(info_dir, METADATA_FILENAME) + wheel_metadata_name = posixpath.join(info_dir, 'WHEEL') + record_name = posixpath.join(info_dir, 'RECORD') + + wrapper = codecs.getreader('utf-8') + + with ZipFile(pathname, 'r') as zf: + with zf.open(wheel_metadata_name) as bwf: + wf = wrapper(bwf) + message = message_from_file(wf) + wv = message['Wheel-Version'].split('.', 1) + file_version = tuple([int(i) for i in wv]) + if (file_version != self.wheel_version) and warner: + warner(self.wheel_version, file_version) + + if message['Root-Is-Purelib'] == 'true': + libdir = paths['purelib'] + else: + libdir = paths['platlib'] + + records = {} + with zf.open(record_name) as bf: + with CSVReader(stream=bf) as reader: + for row in reader: + p = row[0] + records[p] = row + + data_pfx = posixpath.join(data_dir, '') + info_pfx = posixpath.join(info_dir, '') + script_pfx = posixpath.join(data_dir, 'scripts', '') + + # make a new instance rather than a copy of maker's, + # as we mutate it + fileop = FileOperator(dry_run=dry_run) + fileop.record = True # so we can rollback if needed + + bc = not sys.dont_write_bytecode # Double negatives. Lovely! + + outfiles = [] # for RECORD writing + + # for script copying/shebang processing + workdir = tempfile.mkdtemp() + # set target dir later + # we default add_launchers to False, as the + # Python Launcher should be used instead + maker.source_dir = workdir + maker.target_dir = None + try: + for zinfo in zf.infolist(): + arcname = zinfo.filename + if isinstance(arcname, text_type): + u_arcname = arcname + else: + u_arcname = arcname.decode('utf-8') + # The signature file won't be in RECORD, + # and we don't currently don't do anything with it + if u_arcname.endswith('/RECORD.jws'): + continue + row = records[u_arcname] + if row[2] and str(zinfo.file_size) != row[2]: + raise DistlibException('size mismatch for ' + '%s' % u_arcname) + if row[1]: + kind, value = row[1].split('=', 1) + with zf.open(arcname) as bf: + data = bf.read() + _, digest = self.get_hash(data, kind) + if digest != value: + raise DistlibException('digest mismatch for ' + '%s' % arcname) + + if lib_only and u_arcname.startswith((info_pfx, data_pfx)): + logger.debug('lib_only: skipping %s', u_arcname) + continue + is_script = (u_arcname.startswith(script_pfx) + and not u_arcname.endswith('.exe')) + + if u_arcname.startswith(data_pfx): + _, where, rp = u_arcname.split('/', 2) + outfile = os.path.join(paths[where], convert_path(rp)) + else: + # meant for site-packages. + if u_arcname in (wheel_metadata_name, record_name): + continue + outfile = os.path.join(libdir, convert_path(u_arcname)) + if not is_script: + with zf.open(arcname) as bf: + fileop.copy_stream(bf, outfile) + outfiles.append(outfile) + # Double check the digest of the written file + if not dry_run and row[1]: + with open(outfile, 'rb') as bf: + data = bf.read() + _, newdigest = self.get_hash(data, kind) + if newdigest != digest: + raise DistlibException('digest mismatch ' + 'on write for ' + '%s' % outfile) + if bc and outfile.endswith('.py'): + try: + pyc = fileop.byte_compile(outfile, + hashed_invalidation=bc_hashed_invalidation) + outfiles.append(pyc) + except Exception: + # Don't give up if byte-compilation fails, + # but log it and perhaps warn the user + logger.warning('Byte-compilation failed', + exc_info=True) + else: + fn = os.path.basename(convert_path(arcname)) + workname = os.path.join(workdir, fn) + with zf.open(arcname) as bf: + fileop.copy_stream(bf, workname) + + dn, fn = os.path.split(outfile) + maker.target_dir = dn + filenames = maker.make(fn) + fileop.set_executable_mode(filenames) + outfiles.extend(filenames) + + if lib_only: + logger.debug('lib_only: returning None') + dist = None + else: + # Generate scripts + + # Try to get pydist.json so we can see if there are + # any commands to generate. If this fails (e.g. because + # of a legacy wheel), log a warning but don't give up. + commands = None + file_version = self.info['Wheel-Version'] + if file_version == '1.0': + # Use legacy info + ep = posixpath.join(info_dir, 'entry_points.txt') + try: + with zf.open(ep) as bwf: + epdata = read_exports(bwf) + commands = {} + for key in ('console', 'gui'): + k = '%s_scripts' % key + if k in epdata: + commands['wrap_%s' % key] = d = {} + for v in epdata[k].values(): + s = '%s:%s' % (v.prefix, v.suffix) + if v.flags: + s += ' %s' % v.flags + d[v.name] = s + except Exception: + logger.warning('Unable to read legacy script ' + 'metadata, so cannot generate ' + 'scripts') + else: + try: + with zf.open(metadata_name) as bwf: + wf = wrapper(bwf) + commands = json.load(wf).get('extensions') + if commands: + commands = commands.get('python.commands') + except Exception: + logger.warning('Unable to read JSON metadata, so ' + 'cannot generate scripts') + if commands: + console_scripts = commands.get('wrap_console', {}) + gui_scripts = commands.get('wrap_gui', {}) + if console_scripts or gui_scripts: + script_dir = paths.get('scripts', '') + if not os.path.isdir(script_dir): + raise ValueError('Valid script path not ' + 'specified') + maker.target_dir = script_dir + for k, v in console_scripts.items(): + script = '%s = %s' % (k, v) + filenames = maker.make(script) + fileop.set_executable_mode(filenames) + + if gui_scripts: + options = {'gui': True } + for k, v in gui_scripts.items(): + script = '%s = %s' % (k, v) + filenames = maker.make(script, options) + fileop.set_executable_mode(filenames) + + p = os.path.join(libdir, info_dir) + dist = InstalledDistribution(p) + + # Write SHARED + paths = dict(paths) # don't change passed in dict + del paths['purelib'] + del paths['platlib'] + paths['lib'] = libdir + p = dist.write_shared_locations(paths, dry_run) + if p: + outfiles.append(p) + + # Write RECORD + dist.write_installed_files(outfiles, paths['prefix'], + dry_run) + return dist + except Exception: # pragma: no cover + logger.exception('installation failed.') + fileop.rollback() + raise + finally: + shutil.rmtree(workdir) + + def _get_dylib_cache(self): + global cache + if cache is None: + # Use native string to avoid issues on 2.x: see Python #20140. + base = os.path.join(get_cache_base(), str('dylib-cache'), + sys.version[:3]) + cache = Cache(base) + return cache + + def _get_extensions(self): + pathname = os.path.join(self.dirname, self.filename) + name_ver = '%s-%s' % (self.name, self.version) + info_dir = '%s.dist-info' % name_ver + arcname = posixpath.join(info_dir, 'EXTENSIONS') + wrapper = codecs.getreader('utf-8') + result = [] + with ZipFile(pathname, 'r') as zf: + try: + with zf.open(arcname) as bf: + wf = wrapper(bf) + extensions = json.load(wf) + cache = self._get_dylib_cache() + prefix = cache.prefix_to_dir(pathname) + cache_base = os.path.join(cache.base, prefix) + if not os.path.isdir(cache_base): + os.makedirs(cache_base) + for name, relpath in extensions.items(): + dest = os.path.join(cache_base, convert_path(relpath)) + if not os.path.exists(dest): + extract = True + else: + file_time = os.stat(dest).st_mtime + file_time = datetime.datetime.fromtimestamp(file_time) + info = zf.getinfo(relpath) + wheel_time = datetime.datetime(*info.date_time) + extract = wheel_time > file_time + if extract: + zf.extract(relpath, cache_base) + result.append((name, dest)) + except KeyError: + pass + return result + + def is_compatible(self): + """ + Determine if a wheel is compatible with the running system. + """ + return is_compatible(self) + + def is_mountable(self): + """ + Determine if a wheel is asserted as mountable by its metadata. + """ + return True # for now - metadata details TBD + + def mount(self, append=False): + pathname = os.path.abspath(os.path.join(self.dirname, self.filename)) + if not self.is_compatible(): + msg = 'Wheel %s not compatible with this Python.' % pathname + raise DistlibException(msg) + if not self.is_mountable(): + msg = 'Wheel %s is marked as not mountable.' % pathname + raise DistlibException(msg) + if pathname in sys.path: + logger.debug('%s already in path', pathname) + else: + if append: + sys.path.append(pathname) + else: + sys.path.insert(0, pathname) + extensions = self._get_extensions() + if extensions: + if _hook not in sys.meta_path: + sys.meta_path.append(_hook) + _hook.add(pathname, extensions) + + def unmount(self): + pathname = os.path.abspath(os.path.join(self.dirname, self.filename)) + if pathname not in sys.path: + logger.debug('%s not in path', pathname) + else: + sys.path.remove(pathname) + if pathname in _hook.impure_wheels: + _hook.remove(pathname) + if not _hook.impure_wheels: + if _hook in sys.meta_path: + sys.meta_path.remove(_hook) + + def verify(self): + pathname = os.path.join(self.dirname, self.filename) + name_ver = '%s-%s' % (self.name, self.version) + data_dir = '%s.data' % name_ver + info_dir = '%s.dist-info' % name_ver + + metadata_name = posixpath.join(info_dir, METADATA_FILENAME) + wheel_metadata_name = posixpath.join(info_dir, 'WHEEL') + record_name = posixpath.join(info_dir, 'RECORD') + + wrapper = codecs.getreader('utf-8') + + with ZipFile(pathname, 'r') as zf: + with zf.open(wheel_metadata_name) as bwf: + wf = wrapper(bwf) + message = message_from_file(wf) + wv = message['Wheel-Version'].split('.', 1) + file_version = tuple([int(i) for i in wv]) + # TODO version verification + + records = {} + with zf.open(record_name) as bf: + with CSVReader(stream=bf) as reader: + for row in reader: + p = row[0] + records[p] = row + + for zinfo in zf.infolist(): + arcname = zinfo.filename + if isinstance(arcname, text_type): + u_arcname = arcname + else: + u_arcname = arcname.decode('utf-8') + if '..' in u_arcname: + raise DistlibException('invalid entry in ' + 'wheel: %r' % u_arcname) + + # The signature file won't be in RECORD, + # and we don't currently don't do anything with it + if u_arcname.endswith('/RECORD.jws'): + continue + row = records[u_arcname] + if row[2] and str(zinfo.file_size) != row[2]: + raise DistlibException('size mismatch for ' + '%s' % u_arcname) + if row[1]: + kind, value = row[1].split('=', 1) + with zf.open(arcname) as bf: + data = bf.read() + _, digest = self.get_hash(data, kind) + if digest != value: + raise DistlibException('digest mismatch for ' + '%s' % arcname) + + def update(self, modifier, dest_dir=None, **kwargs): + """ + Update the contents of a wheel in a generic way. The modifier should + be a callable which expects a dictionary argument: its keys are + archive-entry paths, and its values are absolute filesystem paths + where the contents the corresponding archive entries can be found. The + modifier is free to change the contents of the files pointed to, add + new entries and remove entries, before returning. This method will + extract the entire contents of the wheel to a temporary location, call + the modifier, and then use the passed (and possibly updated) + dictionary to write a new wheel. If ``dest_dir`` is specified, the new + wheel is written there -- otherwise, the original wheel is overwritten. + + The modifier should return True if it updated the wheel, else False. + This method returns the same value the modifier returns. + """ + + def get_version(path_map, info_dir): + version = path = None + key = '%s/%s' % (info_dir, METADATA_FILENAME) + if key not in path_map: + key = '%s/PKG-INFO' % info_dir + if key in path_map: + path = path_map[key] + version = Metadata(path=path).version + return version, path + + def update_version(version, path): + updated = None + try: + v = NormalizedVersion(version) + i = version.find('-') + if i < 0: + updated = '%s+1' % version + else: + parts = [int(s) for s in version[i + 1:].split('.')] + parts[-1] += 1 + updated = '%s+%s' % (version[:i], + '.'.join(str(i) for i in parts)) + except UnsupportedVersionError: + logger.debug('Cannot update non-compliant (PEP-440) ' + 'version %r', version) + if updated: + md = Metadata(path=path) + md.version = updated + legacy = not path.endswith(METADATA_FILENAME) + md.write(path=path, legacy=legacy) + logger.debug('Version updated from %r to %r', version, + updated) + + pathname = os.path.join(self.dirname, self.filename) + name_ver = '%s-%s' % (self.name, self.version) + info_dir = '%s.dist-info' % name_ver + record_name = posixpath.join(info_dir, 'RECORD') + with tempdir() as workdir: + with ZipFile(pathname, 'r') as zf: + path_map = {} + for zinfo in zf.infolist(): + arcname = zinfo.filename + if isinstance(arcname, text_type): + u_arcname = arcname + else: + u_arcname = arcname.decode('utf-8') + if u_arcname == record_name: + continue + if '..' in u_arcname: + raise DistlibException('invalid entry in ' + 'wheel: %r' % u_arcname) + zf.extract(zinfo, workdir) + path = os.path.join(workdir, convert_path(u_arcname)) + path_map[u_arcname] = path + + # Remember the version. + original_version, _ = get_version(path_map, info_dir) + # Files extracted. Call the modifier. + modified = modifier(path_map, **kwargs) + if modified: + # Something changed - need to build a new wheel. + current_version, path = get_version(path_map, info_dir) + if current_version and (current_version == original_version): + # Add or update local version to signify changes. + update_version(current_version, path) + # Decide where the new wheel goes. + if dest_dir is None: + fd, newpath = tempfile.mkstemp(suffix='.whl', + prefix='wheel-update-', + dir=workdir) + os.close(fd) + else: + if not os.path.isdir(dest_dir): + raise DistlibException('Not a directory: %r' % dest_dir) + newpath = os.path.join(dest_dir, self.filename) + archive_paths = list(path_map.items()) + distinfo = os.path.join(workdir, info_dir) + info = distinfo, info_dir + self.write_records(info, workdir, archive_paths) + self.build_zip(newpath, archive_paths) + if dest_dir is None: + shutil.copyfile(newpath, pathname) + return modified + +def compatible_tags(): + """ + Return (pyver, abi, arch) tuples compatible with this Python. + """ + versions = [VER_SUFFIX] + major = VER_SUFFIX[0] + for minor in range(sys.version_info[1] - 1, - 1, -1): + versions.append(''.join([major, str(minor)])) + + abis = [] + for suffix, _, _ in imp.get_suffixes(): + if suffix.startswith('.abi'): + abis.append(suffix.split('.', 2)[1]) + abis.sort() + if ABI != 'none': + abis.insert(0, ABI) + abis.append('none') + result = [] + + arches = [ARCH] + if sys.platform == 'darwin': + m = re.match(r'(\w+)_(\d+)_(\d+)_(\w+)$', ARCH) + if m: + name, major, minor, arch = m.groups() + minor = int(minor) + matches = [arch] + if arch in ('i386', 'ppc'): + matches.append('fat') + if arch in ('i386', 'ppc', 'x86_64'): + matches.append('fat3') + if arch in ('ppc64', 'x86_64'): + matches.append('fat64') + if arch in ('i386', 'x86_64'): + matches.append('intel') + if arch in ('i386', 'x86_64', 'intel', 'ppc', 'ppc64'): + matches.append('universal') + while minor >= 0: + for match in matches: + s = '%s_%s_%s_%s' % (name, major, minor, match) + if s != ARCH: # already there + arches.append(s) + minor -= 1 + + # Most specific - our Python version, ABI and arch + for abi in abis: + for arch in arches: + result.append((''.join((IMP_PREFIX, versions[0])), abi, arch)) + + # where no ABI / arch dependency, but IMP_PREFIX dependency + for i, version in enumerate(versions): + result.append((''.join((IMP_PREFIX, version)), 'none', 'any')) + if i == 0: + result.append((''.join((IMP_PREFIX, version[0])), 'none', 'any')) + + # no IMP_PREFIX, ABI or arch dependency + for i, version in enumerate(versions): + result.append((''.join(('py', version)), 'none', 'any')) + if i == 0: + result.append((''.join(('py', version[0])), 'none', 'any')) + return set(result) + + +COMPATIBLE_TAGS = compatible_tags() + +del compatible_tags + + +def is_compatible(wheel, tags=None): + if not isinstance(wheel, Wheel): + wheel = Wheel(wheel) # assume it's a filename + result = False + if tags is None: + tags = COMPATIBLE_TAGS + for ver, abi, arch in tags: + if ver in wheel.pyver and abi in wheel.abi and arch in wheel.arch: + result = True + break + return result diff --git a/venv2/lib/python2.7/site-packages/pip/_vendor/distro.py b/venv2/lib/python2.7/site-packages/pip/_vendor/distro.py new file mode 100644 index 0000000..3306163 --- /dev/null +++ b/venv2/lib/python2.7/site-packages/pip/_vendor/distro.py @@ -0,0 +1,1216 @@ +# Copyright 2015,2016,2017 Nir Cohen +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +""" +The ``distro`` package (``distro`` stands for Linux Distribution) provides +information about the Linux distribution it runs on, such as a reliable +machine-readable distro ID, or version information. + +It is the recommended replacement for Python's original +:py:func:`platform.linux_distribution` function, but it provides much more +functionality. An alternative implementation became necessary because Python +3.5 deprecated this function, and Python 3.8 will remove it altogether. +Its predecessor function :py:func:`platform.dist` was already +deprecated since Python 2.6 and will also be removed in Python 3.8. +Still, there are many cases in which access to OS distribution information +is needed. See `Python issue 1322 <https://bugs.python.org/issue1322>`_ for +more information. +""" + +import os +import re +import sys +import json +import shlex +import logging +import argparse +import subprocess + + +_UNIXCONFDIR = os.environ.get('UNIXCONFDIR', '/etc') +_OS_RELEASE_BASENAME = 'os-release' + +#: Translation table for normalizing the "ID" attribute defined in os-release +#: files, for use by the :func:`distro.id` method. +#: +#: * Key: Value as defined in the os-release file, translated to lower case, +#: with blanks translated to underscores. +#: +#: * Value: Normalized value. +NORMALIZED_OS_ID = { + 'ol': 'oracle', # Oracle Enterprise Linux +} + +#: Translation table for normalizing the "Distributor ID" attribute returned by +#: the lsb_release command, for use by the :func:`distro.id` method. +#: +#: * Key: Value as returned by the lsb_release command, translated to lower +#: case, with blanks translated to underscores. +#: +#: * Value: Normalized value. +NORMALIZED_LSB_ID = { + 'enterpriseenterprise': 'oracle', # Oracle Enterprise Linux + 'redhatenterpriseworkstation': 'rhel', # RHEL 6, 7 Workstation + 'redhatenterpriseserver': 'rhel', # RHEL 6, 7 Server +} + +#: Translation table for normalizing the distro ID derived from the file name +#: of distro release files, for use by the :func:`distro.id` method. +#: +#: * Key: Value as derived from the file name of a distro release file, +#: translated to lower case, with blanks translated to underscores. +#: +#: * Value: Normalized value. +NORMALIZED_DISTRO_ID = { + 'redhat': 'rhel', # RHEL 6.x, 7.x +} + +# Pattern for content of distro release file (reversed) +_DISTRO_RELEASE_CONTENT_REVERSED_PATTERN = re.compile( + r'(?:[^)]*\)(.*)\()? *(?:STL )?([\d.+\-a-z]*\d) *(?:esaeler *)?(.+)') + +# Pattern for base file name of distro release file +_DISTRO_RELEASE_BASENAME_PATTERN = re.compile( + r'(\w+)[-_](release|version)$') + +# Base file names to be ignored when searching for distro release file +_DISTRO_RELEASE_IGNORE_BASENAMES = ( + 'debian_version', + 'lsb-release', + 'oem-release', + _OS_RELEASE_BASENAME, + 'system-release' +) + + +def linux_distribution(full_distribution_name=True): + """ + Return information about the current OS distribution as a tuple + ``(id_name, version, codename)`` with items as follows: + + * ``id_name``: If *full_distribution_name* is false, the result of + :func:`distro.id`. Otherwise, the result of :func:`distro.name`. + + * ``version``: The result of :func:`distro.version`. + + * ``codename``: The result of :func:`distro.codename`. + + The interface of this function is compatible with the original + :py:func:`platform.linux_distribution` function, supporting a subset of + its parameters. + + The data it returns may not exactly be the same, because it uses more data + sources than the original function, and that may lead to different data if + the OS distribution is not consistent across multiple data sources it + provides (there are indeed such distributions ...). + + Another reason for differences is the fact that the :func:`distro.id` + method normalizes the distro ID string to a reliable machine-readable value + for a number of popular OS distributions. + """ + return _distro.linux_distribution(full_distribution_name) + + +def id(): + """ + Return the distro ID of the current distribution, as a + machine-readable string. + + For a number of OS distributions, the returned distro ID value is + *reliable*, in the sense that it is documented and that it does not change + across releases of the distribution. + + This package maintains the following reliable distro ID values: + + ============== ========================================= + Distro ID Distribution + ============== ========================================= + "ubuntu" Ubuntu + "debian" Debian + "rhel" RedHat Enterprise Linux + "centos" CentOS + "fedora" Fedora + "sles" SUSE Linux Enterprise Server + "opensuse" openSUSE + "amazon" Amazon Linux + "arch" Arch Linux + "cloudlinux" CloudLinux OS + "exherbo" Exherbo Linux + "gentoo" GenToo Linux + "ibm_powerkvm" IBM PowerKVM + "kvmibm" KVM for IBM z Systems + "linuxmint" Linux Mint + "mageia" Mageia + "mandriva" Mandriva Linux + "parallels" Parallels + "pidora" Pidora + "raspbian" Raspbian + "oracle" Oracle Linux (and Oracle Enterprise Linux) + "scientific" Scientific Linux + "slackware" Slackware + "xenserver" XenServer + "openbsd" OpenBSD + "netbsd" NetBSD + "freebsd" FreeBSD + ============== ========================================= + + If you have a need to get distros for reliable IDs added into this set, + or if you find that the :func:`distro.id` function returns a different + distro ID for one of the listed distros, please create an issue in the + `distro issue tracker`_. + + **Lookup hierarchy and transformations:** + + First, the ID is obtained from the following sources, in the specified + order. The first available and non-empty value is used: + + * the value of the "ID" attribute of the os-release file, + + * the value of the "Distributor ID" attribute returned by the lsb_release + command, + + * the first part of the file name of the distro release file, + + The so determined ID value then passes the following transformations, + before it is returned by this method: + + * it is translated to lower case, + + * blanks (which should not be there anyway) are translated to underscores, + + * a normalization of the ID is performed, based upon + `normalization tables`_. The purpose of this normalization is to ensure + that the ID is as reliable as possible, even across incompatible changes + in the OS distributions. A common reason for an incompatible change is + the addition of an os-release file, or the addition of the lsb_release + command, with ID values that differ from what was previously determined + from the distro release file name. + """ + return _distro.id() + + +def name(pretty=False): + """ + Return the name of the current OS distribution, as a human-readable + string. + + If *pretty* is false, the name is returned without version or codename. + (e.g. "CentOS Linux") + + If *pretty* is true, the version and codename are appended. + (e.g. "CentOS Linux 7.1.1503 (Core)") + + **Lookup hierarchy:** + + The name is obtained from the following sources, in the specified order. + The first available and non-empty value is used: + + * If *pretty* is false: + + - the value of the "NAME" attribute of the os-release file, + + - the value of the "Distributor ID" attribute returned by the lsb_release + command, + + - the value of the "<name>" field of the distro release file. + + * If *pretty* is true: + + - the value of the "PRETTY_NAME" attribute of the os-release file, + + - the value of the "Description" attribute returned by the lsb_release + command, + + - the value of the "<name>" field of the distro release file, appended + with the value of the pretty version ("<version_id>" and "<codename>" + fields) of the distro release file, if available. + """ + return _distro.name(pretty) + + +def version(pretty=False, best=False): + """ + Return the version of the current OS distribution, as a human-readable + string. + + If *pretty* is false, the version is returned without codename (e.g. + "7.0"). + + If *pretty* is true, the codename in parenthesis is appended, if the + codename is non-empty (e.g. "7.0 (Maipo)"). + + Some distributions provide version numbers with different precisions in + the different sources of distribution information. Examining the different + sources in a fixed priority order does not always yield the most precise + version (e.g. for Debian 8.2, or CentOS 7.1). + + The *best* parameter can be used to control the approach for the returned + version: + + If *best* is false, the first non-empty version number in priority order of + the examined sources is returned. + + If *best* is true, the most precise version number out of all examined + sources is returned. + + **Lookup hierarchy:** + + In all cases, the version number is obtained from the following sources. + If *best* is false, this order represents the priority order: + + * the value of the "VERSION_ID" attribute of the os-release file, + * the value of the "Release" attribute returned by the lsb_release + command, + * the version number parsed from the "<version_id>" field of the first line + of the distro release file, + * the version number parsed from the "PRETTY_NAME" attribute of the + os-release file, if it follows the format of the distro release files. + * the version number parsed from the "Description" attribute returned by + the lsb_release command, if it follows the format of the distro release + files. + """ + return _distro.version(pretty, best) + + +def version_parts(best=False): + """ + Return the version of the current OS distribution as a tuple + ``(major, minor, build_number)`` with items as follows: + + * ``major``: The result of :func:`distro.major_version`. + + * ``minor``: The result of :func:`distro.minor_version`. + + * ``build_number``: The result of :func:`distro.build_number`. + + For a description of the *best* parameter, see the :func:`distro.version` + method. + """ + return _distro.version_parts(best) + + +def major_version(best=False): + """ + Return the major version of the current OS distribution, as a string, + if provided. + Otherwise, the empty string is returned. The major version is the first + part of the dot-separated version string. + + For a description of the *best* parameter, see the :func:`distro.version` + method. + """ + return _distro.major_version(best) + + +def minor_version(best=False): + """ + Return the minor version of the current OS distribution, as a string, + if provided. + Otherwise, the empty string is returned. The minor version is the second + part of the dot-separated version string. + + For a description of the *best* parameter, see the :func:`distro.version` + method. + """ + return _distro.minor_version(best) + + +def build_number(best=False): + """ + Return the build number of the current OS distribution, as a string, + if provided. + Otherwise, the empty string is returned. The build number is the third part + of the dot-separated version string. + + For a description of the *best* parameter, see the :func:`distro.version` + method. + """ + return _distro.build_number(best) + + +def like(): + """ + Return a space-separated list of distro IDs of distributions that are + closely related to the current OS distribution in regards to packaging + and programming interfaces, for example distributions the current + distribution is a derivative from. + + **Lookup hierarchy:** + + This information item is only provided by the os-release file. + For details, see the description of the "ID_LIKE" attribute in the + `os-release man page + <http://www.freedesktop.org/software/systemd/man/os-release.html>`_. + """ + return _distro.like() + + +def codename(): + """ + Return the codename for the release of the current OS distribution, + as a string. + + If the distribution does not have a codename, an empty string is returned. + + Note that the returned codename is not always really a codename. For + example, openSUSE returns "x86_64". This function does not handle such + cases in any special way and just returns the string it finds, if any. + + **Lookup hierarchy:** + + * the codename within the "VERSION" attribute of the os-release file, if + provided, + + * the value of the "Codename" attribute returned by the lsb_release + command, + + * the value of the "<codename>" field of the distro release file. + """ + return _distro.codename() + + +def info(pretty=False, best=False): + """ + Return certain machine-readable information items about the current OS + distribution in a dictionary, as shown in the following example: + + .. sourcecode:: python + + { + 'id': 'rhel', + 'version': '7.0', + 'version_parts': { + 'major': '7', + 'minor': '0', + 'build_number': '' + }, + 'like': 'fedora', + 'codename': 'Maipo' + } + + The dictionary structure and keys are always the same, regardless of which + information items are available in the underlying data sources. The values + for the various keys are as follows: + + * ``id``: The result of :func:`distro.id`. + + * ``version``: The result of :func:`distro.version`. + + * ``version_parts -> major``: The result of :func:`distro.major_version`. + + * ``version_parts -> minor``: The result of :func:`distro.minor_version`. + + * ``version_parts -> build_number``: The result of + :func:`distro.build_number`. + + * ``like``: The result of :func:`distro.like`. + + * ``codename``: The result of :func:`distro.codename`. + + For a description of the *pretty* and *best* parameters, see the + :func:`distro.version` method. + """ + return _distro.info(pretty, best) + + +def os_release_info(): + """ + Return a dictionary containing key-value pairs for the information items + from the os-release file data source of the current OS distribution. + + See `os-release file`_ for details about these information items. + """ + return _distro.os_release_info() + + +def lsb_release_info(): + """ + Return a dictionary containing key-value pairs for the information items + from the lsb_release command data source of the current OS distribution. + + See `lsb_release command output`_ for details about these information + items. + """ + return _distro.lsb_release_info() + + +def distro_release_info(): + """ + Return a dictionary containing key-value pairs for the information items + from the distro release file data source of the current OS distribution. + + See `distro release file`_ for details about these information items. + """ + return _distro.distro_release_info() + + +def uname_info(): + """ + Return a dictionary containing key-value pairs for the information items + from the distro release file data source of the current OS distribution. + """ + return _distro.uname_info() + + +def os_release_attr(attribute): + """ + Return a single named information item from the os-release file data source + of the current OS distribution. + + Parameters: + + * ``attribute`` (string): Key of the information item. + + Returns: + + * (string): Value of the information item, if the item exists. + The empty string, if the item does not exist. + + See `os-release file`_ for details about these information items. + """ + return _distro.os_release_attr(attribute) + + +def lsb_release_attr(attribute): + """ + Return a single named information item from the lsb_release command output + data source of the current OS distribution. + + Parameters: + + * ``attribute`` (string): Key of the information item. + + Returns: + + * (string): Value of the information item, if the item exists. + The empty string, if the item does not exist. + + See `lsb_release command output`_ for details about these information + items. + """ + return _distro.lsb_release_attr(attribute) + + +def distro_release_attr(attribute): + """ + Return a single named information item from the distro release file + data source of the current OS distribution. + + Parameters: + + * ``attribute`` (string): Key of the information item. + + Returns: + + * (string): Value of the information item, if the item exists. + The empty string, if the item does not exist. + + See `distro release file`_ for details about these information items. + """ + return _distro.distro_release_attr(attribute) + + +def uname_attr(attribute): + """ + Return a single named information item from the distro release file + data source of the current OS distribution. + + Parameters: + + * ``attribute`` (string): Key of the information item. + + Returns: + + * (string): Value of the information item, if the item exists. + The empty string, if the item does not exist. + """ + return _distro.uname_attr(attribute) + + +class cached_property(object): + """A version of @property which caches the value. On access, it calls the + underlying function and sets the value in `__dict__` so future accesses + will not re-call the property. + """ + def __init__(self, f): + self._fname = f.__name__ + self._f = f + + def __get__(self, obj, owner): + assert obj is not None, 'call {} on an instance'.format(self._fname) + ret = obj.__dict__[self._fname] = self._f(obj) + return ret + + +class LinuxDistribution(object): + """ + Provides information about a OS distribution. + + This package creates a private module-global instance of this class with + default initialization arguments, that is used by the + `consolidated accessor functions`_ and `single source accessor functions`_. + By using default initialization arguments, that module-global instance + returns data about the current OS distribution (i.e. the distro this + package runs on). + + Normally, it is not necessary to create additional instances of this class. + However, in situations where control is needed over the exact data sources + that are used, instances of this class can be created with a specific + distro release file, or a specific os-release file, or without invoking the + lsb_release command. + """ + + def __init__(self, + include_lsb=True, + os_release_file='', + distro_release_file='', + include_uname=True): + """ + The initialization method of this class gathers information from the + available data sources, and stores that in private instance attributes. + Subsequent access to the information items uses these private instance + attributes, so that the data sources are read only once. + + Parameters: + + * ``include_lsb`` (bool): Controls whether the + `lsb_release command output`_ is included as a data source. + + If the lsb_release command is not available in the program execution + path, the data source for the lsb_release command will be empty. + + * ``os_release_file`` (string): The path name of the + `os-release file`_ that is to be used as a data source. + + An empty string (the default) will cause the default path name to + be used (see `os-release file`_ for details). + + If the specified or defaulted os-release file does not exist, the + data source for the os-release file will be empty. + + * ``distro_release_file`` (string): The path name of the + `distro release file`_ that is to be used as a data source. + + An empty string (the default) will cause a default search algorithm + to be used (see `distro release file`_ for details). + + If the specified distro release file does not exist, or if no default + distro release file can be found, the data source for the distro + release file will be empty. + + * ``include_name`` (bool): Controls whether uname command output is + included as a data source. If the uname command is not available in + the program execution path the data source for the uname command will + be empty. + + Public instance attributes: + + * ``os_release_file`` (string): The path name of the + `os-release file`_ that is actually used as a data source. The + empty string if no distro release file is used as a data source. + + * ``distro_release_file`` (string): The path name of the + `distro release file`_ that is actually used as a data source. The + empty string if no distro release file is used as a data source. + + * ``include_lsb`` (bool): The result of the ``include_lsb`` parameter. + This controls whether the lsb information will be loaded. + + * ``include_uname`` (bool): The result of the ``include_uname`` + parameter. This controls whether the uname information will + be loaded. + + Raises: + + * :py:exc:`IOError`: Some I/O issue with an os-release file or distro + release file. + + * :py:exc:`subprocess.CalledProcessError`: The lsb_release command had + some issue (other than not being available in the program execution + path). + + * :py:exc:`UnicodeError`: A data source has unexpected characters or + uses an unexpected encoding. + """ + self.os_release_file = os_release_file or \ + os.path.join(_UNIXCONFDIR, _OS_RELEASE_BASENAME) + self.distro_release_file = distro_release_file or '' # updated later + self.include_lsb = include_lsb + self.include_uname = include_uname + + def __repr__(self): + """Return repr of all info + """ + return \ + "LinuxDistribution(" \ + "os_release_file={self.os_release_file!r}, " \ + "distro_release_file={self.distro_release_file!r}, " \ + "include_lsb={self.include_lsb!r}, " \ + "include_uname={self.include_uname!r}, " \ + "_os_release_info={self._os_release_info!r}, " \ + "_lsb_release_info={self._lsb_release_info!r}, " \ + "_distro_release_info={self._distro_release_info!r}, " \ + "_uname_info={self._uname_info!r})".format( + self=self) + + def linux_distribution(self, full_distribution_name=True): + """ + Return information about the OS distribution that is compatible + with Python's :func:`platform.linux_distribution`, supporting a subset + of its parameters. + + For details, see :func:`distro.linux_distribution`. + """ + return ( + self.name() if full_distribution_name else self.id(), + self.version(), + self.codename() + ) + + def id(self): + """Return the distro ID of the OS distribution, as a string. + + For details, see :func:`distro.id`. + """ + def normalize(distro_id, table): + distro_id = distro_id.lower().replace(' ', '_') + return table.get(distro_id, distro_id) + + distro_id = self.os_release_attr('id') + if distro_id: + return normalize(distro_id, NORMALIZED_OS_ID) + + distro_id = self.lsb_release_attr('distributor_id') + if distro_id: + return normalize(distro_id, NORMALIZED_LSB_ID) + + distro_id = self.distro_release_attr('id') + if distro_id: + return normalize(distro_id, NORMALIZED_DISTRO_ID) + + distro_id = self.uname_attr('id') + if distro_id: + return normalize(distro_id, NORMALIZED_DISTRO_ID) + + return '' + + def name(self, pretty=False): + """ + Return the name of the OS distribution, as a string. + + For details, see :func:`distro.name`. + """ + name = self.os_release_attr('name') \ + or self.lsb_release_attr('distributor_id') \ + or self.distro_release_attr('name') \ + or self.uname_attr('name') + if pretty: + name = self.os_release_attr('pretty_name') \ + or self.lsb_release_attr('description') + if not name: + name = self.distro_release_attr('name') \ + or self.uname_attr('name') + version = self.version(pretty=True) + if version: + name = name + ' ' + version + return name or '' + + def version(self, pretty=False, best=False): + """ + Return the version of the OS distribution, as a string. + + For details, see :func:`distro.version`. + """ + versions = [ + self.os_release_attr('version_id'), + self.lsb_release_attr('release'), + self.distro_release_attr('version_id'), + self._parse_distro_release_content( + self.os_release_attr('pretty_name')).get('version_id', ''), + self._parse_distro_release_content( + self.lsb_release_attr('description')).get('version_id', ''), + self.uname_attr('release') + ] + version = '' + if best: + # This algorithm uses the last version in priority order that has + # the best precision. If the versions are not in conflict, that + # does not matter; otherwise, using the last one instead of the + # first one might be considered a surprise. + for v in versions: + if v.count(".") > version.count(".") or version == '': + version = v + else: + for v in versions: + if v != '': + version = v + break + if pretty and version and self.codename(): + version = u'{0} ({1})'.format(version, self.codename()) + return version + + def version_parts(self, best=False): + """ + Return the version of the OS distribution, as a tuple of version + numbers. + + For details, see :func:`distro.version_parts`. + """ + version_str = self.version(best=best) + if version_str: + version_regex = re.compile(r'(\d+)\.?(\d+)?\.?(\d+)?') + matches = version_regex.match(version_str) + if matches: + major, minor, build_number = matches.groups() + return major, minor or '', build_number or '' + return '', '', '' + + def major_version(self, best=False): + """ + Return the major version number of the current distribution. + + For details, see :func:`distro.major_version`. + """ + return self.version_parts(best)[0] + + def minor_version(self, best=False): + """ + Return the minor version number of the current distribution. + + For details, see :func:`distro.minor_version`. + """ + return self.version_parts(best)[1] + + def build_number(self, best=False): + """ + Return the build number of the current distribution. + + For details, see :func:`distro.build_number`. + """ + return self.version_parts(best)[2] + + def like(self): + """ + Return the IDs of distributions that are like the OS distribution. + + For details, see :func:`distro.like`. + """ + return self.os_release_attr('id_like') or '' + + def codename(self): + """ + Return the codename of the OS distribution. + + For details, see :func:`distro.codename`. + """ + try: + # Handle os_release specially since distros might purposefully set + # this to empty string to have no codename + return self._os_release_info['codename'] + except KeyError: + return self.lsb_release_attr('codename') \ + or self.distro_release_attr('codename') \ + or '' + + def info(self, pretty=False, best=False): + """ + Return certain machine-readable information about the OS + distribution. + + For details, see :func:`distro.info`. + """ + return dict( + id=self.id(), + version=self.version(pretty, best), + version_parts=dict( + major=self.major_version(best), + minor=self.minor_version(best), + build_number=self.build_number(best) + ), + like=self.like(), + codename=self.codename(), + ) + + def os_release_info(self): + """ + Return a dictionary containing key-value pairs for the information + items from the os-release file data source of the OS distribution. + + For details, see :func:`distro.os_release_info`. + """ + return self._os_release_info + + def lsb_release_info(self): + """ + Return a dictionary containing key-value pairs for the information + items from the lsb_release command data source of the OS + distribution. + + For details, see :func:`distro.lsb_release_info`. + """ + return self._lsb_release_info + + def distro_release_info(self): + """ + Return a dictionary containing key-value pairs for the information + items from the distro release file data source of the OS + distribution. + + For details, see :func:`distro.distro_release_info`. + """ + return self._distro_release_info + + def uname_info(self): + """ + Return a dictionary containing key-value pairs for the information + items from the uname command data source of the OS distribution. + + For details, see :func:`distro.uname_info`. + """ + return self._uname_info + + def os_release_attr(self, attribute): + """ + Return a single named information item from the os-release file data + source of the OS distribution. + + For details, see :func:`distro.os_release_attr`. + """ + return self._os_release_info.get(attribute, '') + + def lsb_release_attr(self, attribute): + """ + Return a single named information item from the lsb_release command + output data source of the OS distribution. + + For details, see :func:`distro.lsb_release_attr`. + """ + return self._lsb_release_info.get(attribute, '') + + def distro_release_attr(self, attribute): + """ + Return a single named information item from the distro release file + data source of the OS distribution. + + For details, see :func:`distro.distro_release_attr`. + """ + return self._distro_release_info.get(attribute, '') + + def uname_attr(self, attribute): + """ + Return a single named information item from the uname command + output data source of the OS distribution. + + For details, see :func:`distro.uname_release_attr`. + """ + return self._uname_info.get(attribute, '') + + @cached_property + def _os_release_info(self): + """ + Get the information items from the specified os-release file. + + Returns: + A dictionary containing all information items. + """ + if os.path.isfile(self.os_release_file): + with open(self.os_release_file) as release_file: + return self._parse_os_release_content(release_file) + return {} + + @staticmethod + def _parse_os_release_content(lines): + """ + Parse the lines of an os-release file. + + Parameters: + + * lines: Iterable through the lines in the os-release file. + Each line must be a unicode string or a UTF-8 encoded byte + string. + + Returns: + A dictionary containing all information items. + """ + props = {} + lexer = shlex.shlex(lines, posix=True) + lexer.whitespace_split = True + + # The shlex module defines its `wordchars` variable using literals, + # making it dependent on the encoding of the Python source file. + # In Python 2.6 and 2.7, the shlex source file is encoded in + # 'iso-8859-1', and the `wordchars` variable is defined as a byte + # string. This causes a UnicodeDecodeError to be raised when the + # parsed content is a unicode object. The following fix resolves that + # (... but it should be fixed in shlex...): + if sys.version_info[0] == 2 and isinstance(lexer.wordchars, bytes): + lexer.wordchars = lexer.wordchars.decode('iso-8859-1') + + tokens = list(lexer) + for token in tokens: + # At this point, all shell-like parsing has been done (i.e. + # comments processed, quotes and backslash escape sequences + # processed, multi-line values assembled, trailing newlines + # stripped, etc.), so the tokens are now either: + # * variable assignments: var=value + # * commands or their arguments (not allowed in os-release) + if '=' in token: + k, v = token.split('=', 1) + if isinstance(v, bytes): + v = v.decode('utf-8') + props[k.lower()] = v + else: + # Ignore any tokens that are not variable assignments + pass + + if 'version_codename' in props: + # os-release added a version_codename field. Use that in + # preference to anything else Note that some distros purposefully + # do not have code names. They should be setting + # version_codename="" + props['codename'] = props['version_codename'] + elif 'ubuntu_codename' in props: + # Same as above but a non-standard field name used on older Ubuntus + props['codename'] = props['ubuntu_codename'] + elif 'version' in props: + # If there is no version_codename, parse it from the version + codename = re.search(r'(\(\D+\))|,(\s+)?\D+', props['version']) + if codename: + codename = codename.group() + codename = codename.strip('()') + codename = codename.strip(',') + codename = codename.strip() + # codename appears within paranthese. + props['codename'] = codename + + return props + + @cached_property + def _lsb_release_info(self): + """ + Get the information items from the lsb_release command output. + + Returns: + A dictionary containing all information items. + """ + if not self.include_lsb: + return {} + with open(os.devnull, 'w') as devnull: + try: + cmd = ('lsb_release', '-a') + stdout = subprocess.check_output(cmd, stderr=devnull) + except OSError: # Command not found + return {} + content = stdout.decode(sys.getfilesystemencoding()).splitlines() + return self._parse_lsb_release_content(content) + + @staticmethod + def _parse_lsb_release_content(lines): + """ + Parse the output of the lsb_release command. + + Parameters: + + * lines: Iterable through the lines of the lsb_release output. + Each line must be a unicode string or a UTF-8 encoded byte + string. + + Returns: + A dictionary containing all information items. + """ + props = {} + for line in lines: + kv = line.strip('\n').split(':', 1) + if len(kv) != 2: + # Ignore lines without colon. + continue + k, v = kv + props.update({k.replace(' ', '_').lower(): v.strip()}) + return props + + @cached_property + def _uname_info(self): + with open(os.devnull, 'w') as devnull: + try: + cmd = ('uname', '-rs') + stdout = subprocess.check_output(cmd, stderr=devnull) + except OSError: + return {} + content = stdout.decode(sys.getfilesystemencoding()).splitlines() + return self._parse_uname_content(content) + + @staticmethod + def _parse_uname_content(lines): + props = {} + match = re.search(r'^([^\s]+)\s+([\d\.]+)', lines[0].strip()) + if match: + name, version = match.groups() + + # This is to prevent the Linux kernel version from + # appearing as the 'best' version on otherwise + # identifiable distributions. + if name == 'Linux': + return {} + props['id'] = name.lower() + props['name'] = name + props['release'] = version + return props + + @cached_property + def _distro_release_info(self): + """ + Get the information items from the specified distro release file. + + Returns: + A dictionary containing all information items. + """ + if self.distro_release_file: + # If it was specified, we use it and parse what we can, even if + # its file name or content does not match the expected pattern. + distro_info = self._parse_distro_release_file( + self.distro_release_file) + basename = os.path.basename(self.distro_release_file) + # The file name pattern for user-specified distro release files + # is somewhat more tolerant (compared to when searching for the + # file), because we want to use what was specified as best as + # possible. + match = _DISTRO_RELEASE_BASENAME_PATTERN.match(basename) + if 'name' in distro_info \ + and 'cloudlinux' in distro_info['name'].lower(): + distro_info['id'] = 'cloudlinux' + elif match: + distro_info['id'] = match.group(1) + return distro_info + else: + try: + basenames = os.listdir(_UNIXCONFDIR) + # We sort for repeatability in cases where there are multiple + # distro specific files; e.g. CentOS, Oracle, Enterprise all + # containing `redhat-release` on top of their own. + basenames.sort() + except OSError: + # This may occur when /etc is not readable but we can't be + # sure about the *-release files. Check common entries of + # /etc for information. If they turn out to not be there the + # error is handled in `_parse_distro_release_file()`. + basenames = ['SuSE-release', + 'arch-release', + 'base-release', + 'centos-release', + 'fedora-release', + 'gentoo-release', + 'mageia-release', + 'mandrake-release', + 'mandriva-release', + 'mandrivalinux-release', + 'manjaro-release', + 'oracle-release', + 'redhat-release', + 'sl-release', + 'slackware-version'] + for basename in basenames: + if basename in _DISTRO_RELEASE_IGNORE_BASENAMES: + continue + match = _DISTRO_RELEASE_BASENAME_PATTERN.match(basename) + if match: + filepath = os.path.join(_UNIXCONFDIR, basename) + distro_info = self._parse_distro_release_file(filepath) + if 'name' in distro_info: + # The name is always present if the pattern matches + self.distro_release_file = filepath + distro_info['id'] = match.group(1) + if 'cloudlinux' in distro_info['name'].lower(): + distro_info['id'] = 'cloudlinux' + return distro_info + return {} + + def _parse_distro_release_file(self, filepath): + """ + Parse a distro release file. + + Parameters: + + * filepath: Path name of the distro release file. + + Returns: + A dictionary containing all information items. + """ + try: + with open(filepath) as fp: + # Only parse the first line. For instance, on SLES there + # are multiple lines. We don't want them... + return self._parse_distro_release_content(fp.readline()) + except (OSError, IOError): + # Ignore not being able to read a specific, seemingly version + # related file. + # See https://github.com/nir0s/distro/issues/162 + return {} + + @staticmethod + def _parse_distro_release_content(line): + """ + Parse a line from a distro release file. + + Parameters: + * line: Line from the distro release file. Must be a unicode string + or a UTF-8 encoded byte string. + + Returns: + A dictionary containing all information items. + """ + if isinstance(line, bytes): + line = line.decode('utf-8') + matches = _DISTRO_RELEASE_CONTENT_REVERSED_PATTERN.match( + line.strip()[::-1]) + distro_info = {} + if matches: + # regexp ensures non-None + distro_info['name'] = matches.group(3)[::-1] + if matches.group(2): + distro_info['version_id'] = matches.group(2)[::-1] + if matches.group(1): + distro_info['codename'] = matches.group(1)[::-1] + elif line: + distro_info['name'] = line.strip() + return distro_info + + +_distro = LinuxDistribution() + + +def main(): + logger = logging.getLogger(__name__) + logger.setLevel(logging.DEBUG) + logger.addHandler(logging.StreamHandler(sys.stdout)) + + parser = argparse.ArgumentParser(description="OS distro info tool") + parser.add_argument( + '--json', + '-j', + help="Output in machine readable format", + action="store_true") + args = parser.parse_args() + + if args.json: + logger.info(json.dumps(info(), indent=4, sort_keys=True)) + else: + logger.info('Name: %s', name(pretty=True)) + distribution_version = version(pretty=True) + logger.info('Version: %s', distribution_version) + distribution_codename = codename() + logger.info('Codename: %s', distribution_codename) + + +if __name__ == '__main__': + main() diff --git a/venv2/lib/python2.7/site-packages/pip/_vendor/html5lib/__init__.py b/venv2/lib/python2.7/site-packages/pip/_vendor/html5lib/__init__.py new file mode 100644 index 0000000..0491234 --- /dev/null +++ b/venv2/lib/python2.7/site-packages/pip/_vendor/html5lib/__init__.py @@ -0,0 +1,35 @@ +""" +HTML parsing library based on the `WHATWG HTML specification +<https://whatwg.org/html>`_. The parser is designed to be compatible with +existing HTML found in the wild and implements well-defined error recovery that +is largely compatible with modern desktop web browsers. + +Example usage:: + + from pip._vendor import html5lib + with open("my_document.html", "rb") as f: + tree = html5lib.parse(f) + +For convenience, this module re-exports the following names: + +* :func:`~.html5parser.parse` +* :func:`~.html5parser.parseFragment` +* :class:`~.html5parser.HTMLParser` +* :func:`~.treebuilders.getTreeBuilder` +* :func:`~.treewalkers.getTreeWalker` +* :func:`~.serializer.serialize` +""" + +from __future__ import absolute_import, division, unicode_literals + +from .html5parser import HTMLParser, parse, parseFragment +from .treebuilders import getTreeBuilder +from .treewalkers import getTreeWalker +from .serializer import serialize + +__all__ = ["HTMLParser", "parse", "parseFragment", "getTreeBuilder", + "getTreeWalker", "serialize"] + +# this has to be at the top level, see how setup.py parses this +#: Distribution version number. +__version__ = "1.0.1" diff --git a/venv2/lib/python2.7/site-packages/pip/_vendor/html5lib/_ihatexml.py b/venv2/lib/python2.7/site-packages/pip/_vendor/html5lib/_ihatexml.py new file mode 100644 index 0000000..4c77717 --- /dev/null +++ b/venv2/lib/python2.7/site-packages/pip/_vendor/html5lib/_ihatexml.py @@ -0,0 +1,288 @@ +from __future__ import absolute_import, division, unicode_literals + +import re +import warnings + +from .constants import DataLossWarning + +baseChar = """ +[#x0041-#x005A] | [#x0061-#x007A] | [#x00C0-#x00D6] | [#x00D8-#x00F6] | +[#x00F8-#x00FF] | [#x0100-#x0131] | [#x0134-#x013E] | [#x0141-#x0148] | +[#x014A-#x017E] | [#x0180-#x01C3] | [#x01CD-#x01F0] | [#x01F4-#x01F5] | +[#x01FA-#x0217] | [#x0250-#x02A8] | [#x02BB-#x02C1] | #x0386 | +[#x0388-#x038A] | #x038C | [#x038E-#x03A1] | [#x03A3-#x03CE] | +[#x03D0-#x03D6] | #x03DA | #x03DC | #x03DE | #x03E0 | [#x03E2-#x03F3] | +[#x0401-#x040C] | [#x040E-#x044F] | [#x0451-#x045C] | [#x045E-#x0481] | +[#x0490-#x04C4] | [#x04C7-#x04C8] | [#x04CB-#x04CC] | [#x04D0-#x04EB] | +[#x04EE-#x04F5] | [#x04F8-#x04F9] | [#x0531-#x0556] | #x0559 | +[#x0561-#x0586] | [#x05D0-#x05EA] | [#x05F0-#x05F2] | [#x0621-#x063A] | +[#x0641-#x064A] | [#x0671-#x06B7] | [#x06BA-#x06BE] | [#x06C0-#x06CE] | +[#x06D0-#x06D3] | #x06D5 | [#x06E5-#x06E6] | [#x0905-#x0939] | #x093D | +[#x0958-#x0961] | [#x0985-#x098C] | [#x098F-#x0990] | [#x0993-#x09A8] | +[#x09AA-#x09B0] | #x09B2 | [#x09B6-#x09B9] | [#x09DC-#x09DD] | +[#x09DF-#x09E1] | [#x09F0-#x09F1] | [#x0A05-#x0A0A] | [#x0A0F-#x0A10] | +[#x0A13-#x0A28] | [#x0A2A-#x0A30] | [#x0A32-#x0A33] | [#x0A35-#x0A36] | +[#x0A38-#x0A39] | [#x0A59-#x0A5C] | #x0A5E | [#x0A72-#x0A74] | +[#x0A85-#x0A8B] | #x0A8D | [#x0A8F-#x0A91] | [#x0A93-#x0AA8] | +[#x0AAA-#x0AB0] | [#x0AB2-#x0AB3] | [#x0AB5-#x0AB9] | #x0ABD | #x0AE0 | +[#x0B05-#x0B0C] | [#x0B0F-#x0B10] | [#x0B13-#x0B28] | [#x0B2A-#x0B30] | +[#x0B32-#x0B33] | [#x0B36-#x0B39] | #x0B3D | [#x0B5C-#x0B5D] | +[#x0B5F-#x0B61] | [#x0B85-#x0B8A] | [#x0B8E-#x0B90] | [#x0B92-#x0B95] | +[#x0B99-#x0B9A] | #x0B9C | [#x0B9E-#x0B9F] | [#x0BA3-#x0BA4] | +[#x0BA8-#x0BAA] | [#x0BAE-#x0BB5] | [#x0BB7-#x0BB9] | [#x0C05-#x0C0C] | +[#x0C0E-#x0C10] | [#x0C12-#x0C28] | [#x0C2A-#x0C33] | [#x0C35-#x0C39] | +[#x0C60-#x0C61] | [#x0C85-#x0C8C] | [#x0C8E-#x0C90] | [#x0C92-#x0CA8] | +[#x0CAA-#x0CB3] | [#x0CB5-#x0CB9] | #x0CDE | [#x0CE0-#x0CE1] | +[#x0D05-#x0D0C] | [#x0D0E-#x0D10] | [#x0D12-#x0D28] | [#x0D2A-#x0D39] | +[#x0D60-#x0D61] | [#x0E01-#x0E2E] | #x0E30 | [#x0E32-#x0E33] | +[#x0E40-#x0E45] | [#x0E81-#x0E82] | #x0E84 | [#x0E87-#x0E88] | #x0E8A | +#x0E8D | [#x0E94-#x0E97] | [#x0E99-#x0E9F] | [#x0EA1-#x0EA3] | #x0EA5 | +#x0EA7 | [#x0EAA-#x0EAB] | [#x0EAD-#x0EAE] | #x0EB0 | [#x0EB2-#x0EB3] | +#x0EBD | [#x0EC0-#x0EC4] | [#x0F40-#x0F47] | [#x0F49-#x0F69] | +[#x10A0-#x10C5] | [#x10D0-#x10F6] | #x1100 | [#x1102-#x1103] | +[#x1105-#x1107] | #x1109 | [#x110B-#x110C] | [#x110E-#x1112] | #x113C | +#x113E | #x1140 | #x114C | #x114E | #x1150 | [#x1154-#x1155] | #x1159 | +[#x115F-#x1161] | #x1163 | #x1165 | #x1167 | #x1169 | [#x116D-#x116E] | +[#x1172-#x1173] | #x1175 | #x119E | #x11A8 | #x11AB | [#x11AE-#x11AF] | +[#x11B7-#x11B8] | #x11BA | [#x11BC-#x11C2] | #x11EB | #x11F0 | #x11F9 | +[#x1E00-#x1E9B] | [#x1EA0-#x1EF9] | [#x1F00-#x1F15] | [#x1F18-#x1F1D] | +[#x1F20-#x1F45] | [#x1F48-#x1F4D] | [#x1F50-#x1F57] | #x1F59 | #x1F5B | +#x1F5D | [#x1F5F-#x1F7D] | [#x1F80-#x1FB4] | [#x1FB6-#x1FBC] | #x1FBE | +[#x1FC2-#x1FC4] | [#x1FC6-#x1FCC] | [#x1FD0-#x1FD3] | [#x1FD6-#x1FDB] | +[#x1FE0-#x1FEC] | [#x1FF2-#x1FF4] | [#x1FF6-#x1FFC] | #x2126 | +[#x212A-#x212B] | #x212E | [#x2180-#x2182] | [#x3041-#x3094] | +[#x30A1-#x30FA] | [#x3105-#x312C] | [#xAC00-#xD7A3]""" + +ideographic = """[#x4E00-#x9FA5] | #x3007 | [#x3021-#x3029]""" + +combiningCharacter = """ +[#x0300-#x0345] | [#x0360-#x0361] | [#x0483-#x0486] | [#x0591-#x05A1] | +[#x05A3-#x05B9] | [#x05BB-#x05BD] | #x05BF | [#x05C1-#x05C2] | #x05C4 | +[#x064B-#x0652] | #x0670 | [#x06D6-#x06DC] | [#x06DD-#x06DF] | +[#x06E0-#x06E4] | [#x06E7-#x06E8] | [#x06EA-#x06ED] | [#x0901-#x0903] | +#x093C | [#x093E-#x094C] | #x094D | [#x0951-#x0954] | [#x0962-#x0963] | +[#x0981-#x0983] | #x09BC | #x09BE | #x09BF | [#x09C0-#x09C4] | +[#x09C7-#x09C8] | [#x09CB-#x09CD] | #x09D7 | [#x09E2-#x09E3] | #x0A02 | +#x0A3C | #x0A3E | #x0A3F | [#x0A40-#x0A42] | [#x0A47-#x0A48] | +[#x0A4B-#x0A4D] | [#x0A70-#x0A71] | [#x0A81-#x0A83] | #x0ABC | +[#x0ABE-#x0AC5] | [#x0AC7-#x0AC9] | [#x0ACB-#x0ACD] | [#x0B01-#x0B03] | +#x0B3C | [#x0B3E-#x0B43] | [#x0B47-#x0B48] | [#x0B4B-#x0B4D] | +[#x0B56-#x0B57] | [#x0B82-#x0B83] | [#x0BBE-#x0BC2] | [#x0BC6-#x0BC8] | +[#x0BCA-#x0BCD] | #x0BD7 | [#x0C01-#x0C03] | [#x0C3E-#x0C44] | +[#x0C46-#x0C48] | [#x0C4A-#x0C4D] | [#x0C55-#x0C56] | [#x0C82-#x0C83] | +[#x0CBE-#x0CC4] | [#x0CC6-#x0CC8] | [#x0CCA-#x0CCD] | [#x0CD5-#x0CD6] | +[#x0D02-#x0D03] | [#x0D3E-#x0D43] | [#x0D46-#x0D48] | [#x0D4A-#x0D4D] | +#x0D57 | #x0E31 | [#x0E34-#x0E3A] | [#x0E47-#x0E4E] | #x0EB1 | +[#x0EB4-#x0EB9] | [#x0EBB-#x0EBC] | [#x0EC8-#x0ECD] | [#x0F18-#x0F19] | +#x0F35 | #x0F37 | #x0F39 | #x0F3E | #x0F3F | [#x0F71-#x0F84] | +[#x0F86-#x0F8B] | [#x0F90-#x0F95] | #x0F97 | [#x0F99-#x0FAD] | +[#x0FB1-#x0FB7] | #x0FB9 | [#x20D0-#x20DC] | #x20E1 | [#x302A-#x302F] | +#x3099 | #x309A""" + +digit = """ +[#x0030-#x0039] | [#x0660-#x0669] | [#x06F0-#x06F9] | [#x0966-#x096F] | +[#x09E6-#x09EF] | [#x0A66-#x0A6F] | [#x0AE6-#x0AEF] | [#x0B66-#x0B6F] | +[#x0BE7-#x0BEF] | [#x0C66-#x0C6F] | [#x0CE6-#x0CEF] | [#x0D66-#x0D6F] | +[#x0E50-#x0E59] | [#x0ED0-#x0ED9] | [#x0F20-#x0F29]""" + +extender = """ +#x00B7 | #x02D0 | #x02D1 | #x0387 | #x0640 | #x0E46 | #x0EC6 | #x3005 | +#[#x3031-#x3035] | [#x309D-#x309E] | [#x30FC-#x30FE]""" + +letter = " | ".join([baseChar, ideographic]) + +# Without the +name = " | ".join([letter, digit, ".", "-", "_", combiningCharacter, + extender]) +nameFirst = " | ".join([letter, "_"]) + +reChar = re.compile(r"#x([\d|A-F]{4,4})") +reCharRange = re.compile(r"\[#x([\d|A-F]{4,4})-#x([\d|A-F]{4,4})\]") + + +def charStringToList(chars): + charRanges = [item.strip() for item in chars.split(" | ")] + rv = [] + for item in charRanges: + foundMatch = False + for regexp in (reChar, reCharRange): + match = regexp.match(item) + if match is not None: + rv.append([hexToInt(item) for item in match.groups()]) + if len(rv[-1]) == 1: + rv[-1] = rv[-1] * 2 + foundMatch = True + break + if not foundMatch: + assert len(item) == 1 + + rv.append([ord(item)] * 2) + rv = normaliseCharList(rv) + return rv + + +def normaliseCharList(charList): + charList = sorted(charList) + for item in charList: + assert item[1] >= item[0] + rv = [] + i = 0 + while i < len(charList): + j = 1 + rv.append(charList[i]) + while i + j < len(charList) and charList[i + j][0] <= rv[-1][1] + 1: + rv[-1][1] = charList[i + j][1] + j += 1 + i += j + return rv + +# We don't really support characters above the BMP :( +max_unicode = int("FFFF", 16) + + +def missingRanges(charList): + rv = [] + if charList[0] != 0: + rv.append([0, charList[0][0] - 1]) + for i, item in enumerate(charList[:-1]): + rv.append([item[1] + 1, charList[i + 1][0] - 1]) + if charList[-1][1] != max_unicode: + rv.append([charList[-1][1] + 1, max_unicode]) + return rv + + +def listToRegexpStr(charList): + rv = [] + for item in charList: + if item[0] == item[1]: + rv.append(escapeRegexp(chr(item[0]))) + else: + rv.append(escapeRegexp(chr(item[0])) + "-" + + escapeRegexp(chr(item[1]))) + return "[%s]" % "".join(rv) + + +def hexToInt(hex_str): + return int(hex_str, 16) + + +def escapeRegexp(string): + specialCharacters = (".", "^", "$", "*", "+", "?", "{", "}", + "[", "]", "|", "(", ")", "-") + for char in specialCharacters: + string = string.replace(char, "\\" + char) + + return string + +# output from the above +nonXmlNameBMPRegexp = re.compile('[\x00-,/:-@\\[-\\^`\\{-\xb6\xb8-\xbf\xd7\xf7\u0132-\u0133\u013f-\u0140\u0149\u017f\u01c4-\u01cc\u01f1-\u01f3\u01f6-\u01f9\u0218-\u024f\u02a9-\u02ba\u02c2-\u02cf\u02d2-\u02ff\u0346-\u035f\u0362-\u0385\u038b\u038d\u03a2\u03cf\u03d7-\u03d9\u03db\u03dd\u03df\u03e1\u03f4-\u0400\u040d\u0450\u045d\u0482\u0487-\u048f\u04c5-\u04c6\u04c9-\u04ca\u04cd-\u04cf\u04ec-\u04ed\u04f6-\u04f7\u04fa-\u0530\u0557-\u0558\u055a-\u0560\u0587-\u0590\u05a2\u05ba\u05be\u05c0\u05c3\u05c5-\u05cf\u05eb-\u05ef\u05f3-\u0620\u063b-\u063f\u0653-\u065f\u066a-\u066f\u06b8-\u06b9\u06bf\u06cf\u06d4\u06e9\u06ee-\u06ef\u06fa-\u0900\u0904\u093a-\u093b\u094e-\u0950\u0955-\u0957\u0964-\u0965\u0970-\u0980\u0984\u098d-\u098e\u0991-\u0992\u09a9\u09b1\u09b3-\u09b5\u09ba-\u09bb\u09bd\u09c5-\u09c6\u09c9-\u09ca\u09ce-\u09d6\u09d8-\u09db\u09de\u09e4-\u09e5\u09f2-\u0a01\u0a03-\u0a04\u0a0b-\u0a0e\u0a11-\u0a12\u0a29\u0a31\u0a34\u0a37\u0a3a-\u0a3b\u0a3d\u0a43-\u0a46\u0a49-\u0a4a\u0a4e-\u0a58\u0a5d\u0a5f-\u0a65\u0a75-\u0a80\u0a84\u0a8c\u0a8e\u0a92\u0aa9\u0ab1\u0ab4\u0aba-\u0abb\u0ac6\u0aca\u0ace-\u0adf\u0ae1-\u0ae5\u0af0-\u0b00\u0b04\u0b0d-\u0b0e\u0b11-\u0b12\u0b29\u0b31\u0b34-\u0b35\u0b3a-\u0b3b\u0b44-\u0b46\u0b49-\u0b4a\u0b4e-\u0b55\u0b58-\u0b5b\u0b5e\u0b62-\u0b65\u0b70-\u0b81\u0b84\u0b8b-\u0b8d\u0b91\u0b96-\u0b98\u0b9b\u0b9d\u0ba0-\u0ba2\u0ba5-\u0ba7\u0bab-\u0bad\u0bb6\u0bba-\u0bbd\u0bc3-\u0bc5\u0bc9\u0bce-\u0bd6\u0bd8-\u0be6\u0bf0-\u0c00\u0c04\u0c0d\u0c11\u0c29\u0c34\u0c3a-\u0c3d\u0c45\u0c49\u0c4e-\u0c54\u0c57-\u0c5f\u0c62-\u0c65\u0c70-\u0c81\u0c84\u0c8d\u0c91\u0ca9\u0cb4\u0cba-\u0cbd\u0cc5\u0cc9\u0cce-\u0cd4\u0cd7-\u0cdd\u0cdf\u0ce2-\u0ce5\u0cf0-\u0d01\u0d04\u0d0d\u0d11\u0d29\u0d3a-\u0d3d\u0d44-\u0d45\u0d49\u0d4e-\u0d56\u0d58-\u0d5f\u0d62-\u0d65\u0d70-\u0e00\u0e2f\u0e3b-\u0e3f\u0e4f\u0e5a-\u0e80\u0e83\u0e85-\u0e86\u0e89\u0e8b-\u0e8c\u0e8e-\u0e93\u0e98\u0ea0\u0ea4\u0ea6\u0ea8-\u0ea9\u0eac\u0eaf\u0eba\u0ebe-\u0ebf\u0ec5\u0ec7\u0ece-\u0ecf\u0eda-\u0f17\u0f1a-\u0f1f\u0f2a-\u0f34\u0f36\u0f38\u0f3a-\u0f3d\u0f48\u0f6a-\u0f70\u0f85\u0f8c-\u0f8f\u0f96\u0f98\u0fae-\u0fb0\u0fb8\u0fba-\u109f\u10c6-\u10cf\u10f7-\u10ff\u1101\u1104\u1108\u110a\u110d\u1113-\u113b\u113d\u113f\u1141-\u114b\u114d\u114f\u1151-\u1153\u1156-\u1158\u115a-\u115e\u1162\u1164\u1166\u1168\u116a-\u116c\u116f-\u1171\u1174\u1176-\u119d\u119f-\u11a7\u11a9-\u11aa\u11ac-\u11ad\u11b0-\u11b6\u11b9\u11bb\u11c3-\u11ea\u11ec-\u11ef\u11f1-\u11f8\u11fa-\u1dff\u1e9c-\u1e9f\u1efa-\u1eff\u1f16-\u1f17\u1f1e-\u1f1f\u1f46-\u1f47\u1f4e-\u1f4f\u1f58\u1f5a\u1f5c\u1f5e\u1f7e-\u1f7f\u1fb5\u1fbd\u1fbf-\u1fc1\u1fc5\u1fcd-\u1fcf\u1fd4-\u1fd5\u1fdc-\u1fdf\u1fed-\u1ff1\u1ff5\u1ffd-\u20cf\u20dd-\u20e0\u20e2-\u2125\u2127-\u2129\u212c-\u212d\u212f-\u217f\u2183-\u3004\u3006\u3008-\u3020\u3030\u3036-\u3040\u3095-\u3098\u309b-\u309c\u309f-\u30a0\u30fb\u30ff-\u3104\u312d-\u4dff\u9fa6-\uabff\ud7a4-\uffff]') # noqa + +nonXmlNameFirstBMPRegexp = re.compile('[\x00-@\\[-\\^`\\{-\xbf\xd7\xf7\u0132-\u0133\u013f-\u0140\u0149\u017f\u01c4-\u01cc\u01f1-\u01f3\u01f6-\u01f9\u0218-\u024f\u02a9-\u02ba\u02c2-\u0385\u0387\u038b\u038d\u03a2\u03cf\u03d7-\u03d9\u03db\u03dd\u03df\u03e1\u03f4-\u0400\u040d\u0450\u045d\u0482-\u048f\u04c5-\u04c6\u04c9-\u04ca\u04cd-\u04cf\u04ec-\u04ed\u04f6-\u04f7\u04fa-\u0530\u0557-\u0558\u055a-\u0560\u0587-\u05cf\u05eb-\u05ef\u05f3-\u0620\u063b-\u0640\u064b-\u0670\u06b8-\u06b9\u06bf\u06cf\u06d4\u06d6-\u06e4\u06e7-\u0904\u093a-\u093c\u093e-\u0957\u0962-\u0984\u098d-\u098e\u0991-\u0992\u09a9\u09b1\u09b3-\u09b5\u09ba-\u09db\u09de\u09e2-\u09ef\u09f2-\u0a04\u0a0b-\u0a0e\u0a11-\u0a12\u0a29\u0a31\u0a34\u0a37\u0a3a-\u0a58\u0a5d\u0a5f-\u0a71\u0a75-\u0a84\u0a8c\u0a8e\u0a92\u0aa9\u0ab1\u0ab4\u0aba-\u0abc\u0abe-\u0adf\u0ae1-\u0b04\u0b0d-\u0b0e\u0b11-\u0b12\u0b29\u0b31\u0b34-\u0b35\u0b3a-\u0b3c\u0b3e-\u0b5b\u0b5e\u0b62-\u0b84\u0b8b-\u0b8d\u0b91\u0b96-\u0b98\u0b9b\u0b9d\u0ba0-\u0ba2\u0ba5-\u0ba7\u0bab-\u0bad\u0bb6\u0bba-\u0c04\u0c0d\u0c11\u0c29\u0c34\u0c3a-\u0c5f\u0c62-\u0c84\u0c8d\u0c91\u0ca9\u0cb4\u0cba-\u0cdd\u0cdf\u0ce2-\u0d04\u0d0d\u0d11\u0d29\u0d3a-\u0d5f\u0d62-\u0e00\u0e2f\u0e31\u0e34-\u0e3f\u0e46-\u0e80\u0e83\u0e85-\u0e86\u0e89\u0e8b-\u0e8c\u0e8e-\u0e93\u0e98\u0ea0\u0ea4\u0ea6\u0ea8-\u0ea9\u0eac\u0eaf\u0eb1\u0eb4-\u0ebc\u0ebe-\u0ebf\u0ec5-\u0f3f\u0f48\u0f6a-\u109f\u10c6-\u10cf\u10f7-\u10ff\u1101\u1104\u1108\u110a\u110d\u1113-\u113b\u113d\u113f\u1141-\u114b\u114d\u114f\u1151-\u1153\u1156-\u1158\u115a-\u115e\u1162\u1164\u1166\u1168\u116a-\u116c\u116f-\u1171\u1174\u1176-\u119d\u119f-\u11a7\u11a9-\u11aa\u11ac-\u11ad\u11b0-\u11b6\u11b9\u11bb\u11c3-\u11ea\u11ec-\u11ef\u11f1-\u11f8\u11fa-\u1dff\u1e9c-\u1e9f\u1efa-\u1eff\u1f16-\u1f17\u1f1e-\u1f1f\u1f46-\u1f47\u1f4e-\u1f4f\u1f58\u1f5a\u1f5c\u1f5e\u1f7e-\u1f7f\u1fb5\u1fbd\u1fbf-\u1fc1\u1fc5\u1fcd-\u1fcf\u1fd4-\u1fd5\u1fdc-\u1fdf\u1fed-\u1ff1\u1ff5\u1ffd-\u2125\u2127-\u2129\u212c-\u212d\u212f-\u217f\u2183-\u3006\u3008-\u3020\u302a-\u3040\u3095-\u30a0\u30fb-\u3104\u312d-\u4dff\u9fa6-\uabff\ud7a4-\uffff]') # noqa + +# Simpler things +nonPubidCharRegexp = re.compile("[^\x20\x0D\x0Aa-zA-Z0-9\\-'()+,./:=?;!*#@$_%]") + + +class InfosetFilter(object): + replacementRegexp = re.compile(r"U[\dA-F]{5,5}") + + def __init__(self, + dropXmlnsLocalName=False, + dropXmlnsAttrNs=False, + preventDoubleDashComments=False, + preventDashAtCommentEnd=False, + replaceFormFeedCharacters=True, + preventSingleQuotePubid=False): + + self.dropXmlnsLocalName = dropXmlnsLocalName + self.dropXmlnsAttrNs = dropXmlnsAttrNs + + self.preventDoubleDashComments = preventDoubleDashComments + self.preventDashAtCommentEnd = preventDashAtCommentEnd + + self.replaceFormFeedCharacters = replaceFormFeedCharacters + + self.preventSingleQuotePubid = preventSingleQuotePubid + + self.replaceCache = {} + + def coerceAttribute(self, name, namespace=None): + if self.dropXmlnsLocalName and name.startswith("xmlns:"): + warnings.warn("Attributes cannot begin with xmlns", DataLossWarning) + return None + elif (self.dropXmlnsAttrNs and + namespace == "http://www.w3.org/2000/xmlns/"): + warnings.warn("Attributes cannot be in the xml namespace", DataLossWarning) + return None + else: + return self.toXmlName(name) + + def coerceElement(self, name): + return self.toXmlName(name) + + def coerceComment(self, data): + if self.preventDoubleDashComments: + while "--" in data: + warnings.warn("Comments cannot contain adjacent dashes", DataLossWarning) + data = data.replace("--", "- -") + if data.endswith("-"): + warnings.warn("Comments cannot end in a dash", DataLossWarning) + data += " " + return data + + def coerceCharacters(self, data): + if self.replaceFormFeedCharacters: + for _ in range(data.count("\x0C")): + warnings.warn("Text cannot contain U+000C", DataLossWarning) + data = data.replace("\x0C", " ") + # Other non-xml characters + return data + + def coercePubid(self, data): + dataOutput = data + for char in nonPubidCharRegexp.findall(data): + warnings.warn("Coercing non-XML pubid", DataLossWarning) + replacement = self.getReplacementCharacter(char) + dataOutput = dataOutput.replace(char, replacement) + if self.preventSingleQuotePubid and dataOutput.find("'") >= 0: + warnings.warn("Pubid cannot contain single quote", DataLossWarning) + dataOutput = dataOutput.replace("'", self.getReplacementCharacter("'")) + return dataOutput + + def toXmlName(self, name): + nameFirst = name[0] + nameRest = name[1:] + m = nonXmlNameFirstBMPRegexp.match(nameFirst) + if m: + warnings.warn("Coercing non-XML name", DataLossWarning) + nameFirstOutput = self.getReplacementCharacter(nameFirst) + else: + nameFirstOutput = nameFirst + + nameRestOutput = nameRest + replaceChars = set(nonXmlNameBMPRegexp.findall(nameRest)) + for char in replaceChars: + warnings.warn("Coercing non-XML name", DataLossWarning) + replacement = self.getReplacementCharacter(char) + nameRestOutput = nameRestOutput.replace(char, replacement) + return nameFirstOutput + nameRestOutput + + def getReplacementCharacter(self, char): + if char in self.replaceCache: + replacement = self.replaceCache[char] + else: + replacement = self.escapeChar(char) + return replacement + + def fromXmlName(self, name): + for item in set(self.replacementRegexp.findall(name)): + name = name.replace(item, self.unescapeChar(item)) + return name + + def escapeChar(self, char): + replacement = "U%05X" % ord(char) + self.replaceCache[char] = replacement + return replacement + + def unescapeChar(self, charcode): + return chr(int(charcode[1:], 16)) diff --git a/venv2/lib/python2.7/site-packages/pip/_vendor/html5lib/_inputstream.py b/venv2/lib/python2.7/site-packages/pip/_vendor/html5lib/_inputstream.py new file mode 100644 index 0000000..a65e55f --- /dev/null +++ b/venv2/lib/python2.7/site-packages/pip/_vendor/html5lib/_inputstream.py @@ -0,0 +1,923 @@ +from __future__ import absolute_import, division, unicode_literals + +from pip._vendor.six import text_type, binary_type +from pip._vendor.six.moves import http_client, urllib + +import codecs +import re + +from pip._vendor import webencodings + +from .constants import EOF, spaceCharacters, asciiLetters, asciiUppercase +from .constants import _ReparseException +from . import _utils + +from io import StringIO + +try: + from io import BytesIO +except ImportError: + BytesIO = StringIO + +# Non-unicode versions of constants for use in the pre-parser +spaceCharactersBytes = frozenset([item.encode("ascii") for item in spaceCharacters]) +asciiLettersBytes = frozenset([item.encode("ascii") for item in asciiLetters]) +asciiUppercaseBytes = frozenset([item.encode("ascii") for item in asciiUppercase]) +spacesAngleBrackets = spaceCharactersBytes | frozenset([b">", b"<"]) + + +invalid_unicode_no_surrogate = "[\u0001-\u0008\u000B\u000E-\u001F\u007F-\u009F\uFDD0-\uFDEF\uFFFE\uFFFF\U0001FFFE\U0001FFFF\U0002FFFE\U0002FFFF\U0003FFFE\U0003FFFF\U0004FFFE\U0004FFFF\U0005FFFE\U0005FFFF\U0006FFFE\U0006FFFF\U0007FFFE\U0007FFFF\U0008FFFE\U0008FFFF\U0009FFFE\U0009FFFF\U000AFFFE\U000AFFFF\U000BFFFE\U000BFFFF\U000CFFFE\U000CFFFF\U000DFFFE\U000DFFFF\U000EFFFE\U000EFFFF\U000FFFFE\U000FFFFF\U0010FFFE\U0010FFFF]" # noqa + +if _utils.supports_lone_surrogates: + # Use one extra step of indirection and create surrogates with + # eval. Not using this indirection would introduce an illegal + # unicode literal on platforms not supporting such lone + # surrogates. + assert invalid_unicode_no_surrogate[-1] == "]" and invalid_unicode_no_surrogate.count("]") == 1 + invalid_unicode_re = re.compile(invalid_unicode_no_surrogate[:-1] + + eval('"\\uD800-\\uDFFF"') + # pylint:disable=eval-used + "]") +else: + invalid_unicode_re = re.compile(invalid_unicode_no_surrogate) + +non_bmp_invalid_codepoints = set([0x1FFFE, 0x1FFFF, 0x2FFFE, 0x2FFFF, 0x3FFFE, + 0x3FFFF, 0x4FFFE, 0x4FFFF, 0x5FFFE, 0x5FFFF, + 0x6FFFE, 0x6FFFF, 0x7FFFE, 0x7FFFF, 0x8FFFE, + 0x8FFFF, 0x9FFFE, 0x9FFFF, 0xAFFFE, 0xAFFFF, + 0xBFFFE, 0xBFFFF, 0xCFFFE, 0xCFFFF, 0xDFFFE, + 0xDFFFF, 0xEFFFE, 0xEFFFF, 0xFFFFE, 0xFFFFF, + 0x10FFFE, 0x10FFFF]) + +ascii_punctuation_re = re.compile("[\u0009-\u000D\u0020-\u002F\u003A-\u0040\u005C\u005B-\u0060\u007B-\u007E]") + +# Cache for charsUntil() +charsUntilRegEx = {} + + +class BufferedStream(object): + """Buffering for streams that do not have buffering of their own + + The buffer is implemented as a list of chunks on the assumption that + joining many strings will be slow since it is O(n**2) + """ + + def __init__(self, stream): + self.stream = stream + self.buffer = [] + self.position = [-1, 0] # chunk number, offset + + def tell(self): + pos = 0 + for chunk in self.buffer[:self.position[0]]: + pos += len(chunk) + pos += self.position[1] + return pos + + def seek(self, pos): + assert pos <= self._bufferedBytes() + offset = pos + i = 0 + while len(self.buffer[i]) < offset: + offset -= len(self.buffer[i]) + i += 1 + self.position = [i, offset] + + def read(self, bytes): + if not self.buffer: + return self._readStream(bytes) + elif (self.position[0] == len(self.buffer) and + self.position[1] == len(self.buffer[-1])): + return self._readStream(bytes) + else: + return self._readFromBuffer(bytes) + + def _bufferedBytes(self): + return sum([len(item) for item in self.buffer]) + + def _readStream(self, bytes): + data = self.stream.read(bytes) + self.buffer.append(data) + self.position[0] += 1 + self.position[1] = len(data) + return data + + def _readFromBuffer(self, bytes): + remainingBytes = bytes + rv = [] + bufferIndex = self.position[0] + bufferOffset = self.position[1] + while bufferIndex < len(self.buffer) and remainingBytes != 0: + assert remainingBytes > 0 + bufferedData = self.buffer[bufferIndex] + + if remainingBytes <= len(bufferedData) - bufferOffset: + bytesToRead = remainingBytes + self.position = [bufferIndex, bufferOffset + bytesToRead] + else: + bytesToRead = len(bufferedData) - bufferOffset + self.position = [bufferIndex, len(bufferedData)] + bufferIndex += 1 + rv.append(bufferedData[bufferOffset:bufferOffset + bytesToRead]) + remainingBytes -= bytesToRead + + bufferOffset = 0 + + if remainingBytes: + rv.append(self._readStream(remainingBytes)) + + return b"".join(rv) + + +def HTMLInputStream(source, **kwargs): + # Work around Python bug #20007: read(0) closes the connection. + # http://bugs.python.org/issue20007 + if (isinstance(source, http_client.HTTPResponse) or + # Also check for addinfourl wrapping HTTPResponse + (isinstance(source, urllib.response.addbase) and + isinstance(source.fp, http_client.HTTPResponse))): + isUnicode = False + elif hasattr(source, "read"): + isUnicode = isinstance(source.read(0), text_type) + else: + isUnicode = isinstance(source, text_type) + + if isUnicode: + encodings = [x for x in kwargs if x.endswith("_encoding")] + if encodings: + raise TypeError("Cannot set an encoding with a unicode input, set %r" % encodings) + + return HTMLUnicodeInputStream(source, **kwargs) + else: + return HTMLBinaryInputStream(source, **kwargs) + + +class HTMLUnicodeInputStream(object): + """Provides a unicode stream of characters to the HTMLTokenizer. + + This class takes care of character encoding and removing or replacing + incorrect byte-sequences and also provides column and line tracking. + + """ + + _defaultChunkSize = 10240 + + def __init__(self, source): + """Initialises the HTMLInputStream. + + HTMLInputStream(source, [encoding]) -> Normalized stream from source + for use by html5lib. + + source can be either a file-object, local filename or a string. + + The optional encoding parameter must be a string that indicates + the encoding. If specified, that encoding will be used, + regardless of any BOM or later declaration (such as in a meta + element) + + """ + + if not _utils.supports_lone_surrogates: + # Such platforms will have already checked for such + # surrogate errors, so no need to do this checking. + self.reportCharacterErrors = None + elif len("\U0010FFFF") == 1: + self.reportCharacterErrors = self.characterErrorsUCS4 + else: + self.reportCharacterErrors = self.characterErrorsUCS2 + + # List of where new lines occur + self.newLines = [0] + + self.charEncoding = (lookupEncoding("utf-8"), "certain") + self.dataStream = self.openStream(source) + + self.reset() + + def reset(self): + self.chunk = "" + self.chunkSize = 0 + self.chunkOffset = 0 + self.errors = [] + + # number of (complete) lines in previous chunks + self.prevNumLines = 0 + # number of columns in the last line of the previous chunk + self.prevNumCols = 0 + + # Deal with CR LF and surrogates split over chunk boundaries + self._bufferedCharacter = None + + def openStream(self, source): + """Produces a file object from source. + + source can be either a file object, local filename or a string. + + """ + # Already a file object + if hasattr(source, 'read'): + stream = source + else: + stream = StringIO(source) + + return stream + + def _position(self, offset): + chunk = self.chunk + nLines = chunk.count('\n', 0, offset) + positionLine = self.prevNumLines + nLines + lastLinePos = chunk.rfind('\n', 0, offset) + if lastLinePos == -1: + positionColumn = self.prevNumCols + offset + else: + positionColumn = offset - (lastLinePos + 1) + return (positionLine, positionColumn) + + def position(self): + """Returns (line, col) of the current position in the stream.""" + line, col = self._position(self.chunkOffset) + return (line + 1, col) + + def char(self): + """ Read one character from the stream or queue if available. Return + EOF when EOF is reached. + """ + # Read a new chunk from the input stream if necessary + if self.chunkOffset >= self.chunkSize: + if not self.readChunk(): + return EOF + + chunkOffset = self.chunkOffset + char = self.chunk[chunkOffset] + self.chunkOffset = chunkOffset + 1 + + return char + + def readChunk(self, chunkSize=None): + if chunkSize is None: + chunkSize = self._defaultChunkSize + + self.prevNumLines, self.prevNumCols = self._position(self.chunkSize) + + self.chunk = "" + self.chunkSize = 0 + self.chunkOffset = 0 + + data = self.dataStream.read(chunkSize) + + # Deal with CR LF and surrogates broken across chunks + if self._bufferedCharacter: + data = self._bufferedCharacter + data + self._bufferedCharacter = None + elif not data: + # We have no more data, bye-bye stream + return False + + if len(data) > 1: + lastv = ord(data[-1]) + if lastv == 0x0D or 0xD800 <= lastv <= 0xDBFF: + self._bufferedCharacter = data[-1] + data = data[:-1] + + if self.reportCharacterErrors: + self.reportCharacterErrors(data) + + # Replace invalid characters + data = data.replace("\r\n", "\n") + data = data.replace("\r", "\n") + + self.chunk = data + self.chunkSize = len(data) + + return True + + def characterErrorsUCS4(self, data): + for _ in range(len(invalid_unicode_re.findall(data))): + self.errors.append("invalid-codepoint") + + def characterErrorsUCS2(self, data): + # Someone picked the wrong compile option + # You lose + skip = False + for match in invalid_unicode_re.finditer(data): + if skip: + continue + codepoint = ord(match.group()) + pos = match.start() + # Pretty sure there should be endianness issues here + if _utils.isSurrogatePair(data[pos:pos + 2]): + # We have a surrogate pair! + char_val = _utils.surrogatePairToCodepoint(data[pos:pos + 2]) + if char_val in non_bmp_invalid_codepoints: + self.errors.append("invalid-codepoint") + skip = True + elif (codepoint >= 0xD800 and codepoint <= 0xDFFF and + pos == len(data) - 1): + self.errors.append("invalid-codepoint") + else: + skip = False + self.errors.append("invalid-codepoint") + + def charsUntil(self, characters, opposite=False): + """ Returns a string of characters from the stream up to but not + including any character in 'characters' or EOF. 'characters' must be + a container that supports the 'in' method and iteration over its + characters. + """ + + # Use a cache of regexps to find the required characters + try: + chars = charsUntilRegEx[(characters, opposite)] + except KeyError: + if __debug__: + for c in characters: + assert(ord(c) < 128) + regex = "".join(["\\x%02x" % ord(c) for c in characters]) + if not opposite: + regex = "^%s" % regex + chars = charsUntilRegEx[(characters, opposite)] = re.compile("[%s]+" % regex) + + rv = [] + + while True: + # Find the longest matching prefix + m = chars.match(self.chunk, self.chunkOffset) + if m is None: + # If nothing matched, and it wasn't because we ran out of chunk, + # then stop + if self.chunkOffset != self.chunkSize: + break + else: + end = m.end() + # If not the whole chunk matched, return everything + # up to the part that didn't match + if end != self.chunkSize: + rv.append(self.chunk[self.chunkOffset:end]) + self.chunkOffset = end + break + # If the whole remainder of the chunk matched, + # use it all and read the next chunk + rv.append(self.chunk[self.chunkOffset:]) + if not self.readChunk(): + # Reached EOF + break + + r = "".join(rv) + return r + + def unget(self, char): + # Only one character is allowed to be ungotten at once - it must + # be consumed again before any further call to unget + if char is not None: + if self.chunkOffset == 0: + # unget is called quite rarely, so it's a good idea to do + # more work here if it saves a bit of work in the frequently + # called char and charsUntil. + # So, just prepend the ungotten character onto the current + # chunk: + self.chunk = char + self.chunk + self.chunkSize += 1 + else: + self.chunkOffset -= 1 + assert self.chunk[self.chunkOffset] == char + + +class HTMLBinaryInputStream(HTMLUnicodeInputStream): + """Provides a unicode stream of characters to the HTMLTokenizer. + + This class takes care of character encoding and removing or replacing + incorrect byte-sequences and also provides column and line tracking. + + """ + + def __init__(self, source, override_encoding=None, transport_encoding=None, + same_origin_parent_encoding=None, likely_encoding=None, + default_encoding="windows-1252", useChardet=True): + """Initialises the HTMLInputStream. + + HTMLInputStream(source, [encoding]) -> Normalized stream from source + for use by html5lib. + + source can be either a file-object, local filename or a string. + + The optional encoding parameter must be a string that indicates + the encoding. If specified, that encoding will be used, + regardless of any BOM or later declaration (such as in a meta + element) + + """ + # Raw Stream - for unicode objects this will encode to utf-8 and set + # self.charEncoding as appropriate + self.rawStream = self.openStream(source) + + HTMLUnicodeInputStream.__init__(self, self.rawStream) + + # Encoding Information + # Number of bytes to use when looking for a meta element with + # encoding information + self.numBytesMeta = 1024 + # Number of bytes to use when using detecting encoding using chardet + self.numBytesChardet = 100 + # Things from args + self.override_encoding = override_encoding + self.transport_encoding = transport_encoding + self.same_origin_parent_encoding = same_origin_parent_encoding + self.likely_encoding = likely_encoding + self.default_encoding = default_encoding + + # Determine encoding + self.charEncoding = self.determineEncoding(useChardet) + assert self.charEncoding[0] is not None + + # Call superclass + self.reset() + + def reset(self): + self.dataStream = self.charEncoding[0].codec_info.streamreader(self.rawStream, 'replace') + HTMLUnicodeInputStream.reset(self) + + def openStream(self, source): + """Produces a file object from source. + + source can be either a file object, local filename or a string. + + """ + # Already a file object + if hasattr(source, 'read'): + stream = source + else: + stream = BytesIO(source) + + try: + stream.seek(stream.tell()) + except: # pylint:disable=bare-except + stream = BufferedStream(stream) + + return stream + + def determineEncoding(self, chardet=True): + # BOMs take precedence over everything + # This will also read past the BOM if present + charEncoding = self.detectBOM(), "certain" + if charEncoding[0] is not None: + return charEncoding + + # If we've been overriden, we've been overriden + charEncoding = lookupEncoding(self.override_encoding), "certain" + if charEncoding[0] is not None: + return charEncoding + + # Now check the transport layer + charEncoding = lookupEncoding(self.transport_encoding), "certain" + if charEncoding[0] is not None: + return charEncoding + + # Look for meta elements with encoding information + charEncoding = self.detectEncodingMeta(), "tentative" + if charEncoding[0] is not None: + return charEncoding + + # Parent document encoding + charEncoding = lookupEncoding(self.same_origin_parent_encoding), "tentative" + if charEncoding[0] is not None and not charEncoding[0].name.startswith("utf-16"): + return charEncoding + + # "likely" encoding + charEncoding = lookupEncoding(self.likely_encoding), "tentative" + if charEncoding[0] is not None: + return charEncoding + + # Guess with chardet, if available + if chardet: + try: + from pip._vendor.chardet.universaldetector import UniversalDetector + except ImportError: + pass + else: + buffers = [] + detector = UniversalDetector() + while not detector.done: + buffer = self.rawStream.read(self.numBytesChardet) + assert isinstance(buffer, bytes) + if not buffer: + break + buffers.append(buffer) + detector.feed(buffer) + detector.close() + encoding = lookupEncoding(detector.result['encoding']) + self.rawStream.seek(0) + if encoding is not None: + return encoding, "tentative" + + # Try the default encoding + charEncoding = lookupEncoding(self.default_encoding), "tentative" + if charEncoding[0] is not None: + return charEncoding + + # Fallback to html5lib's default if even that hasn't worked + return lookupEncoding("windows-1252"), "tentative" + + def changeEncoding(self, newEncoding): + assert self.charEncoding[1] != "certain" + newEncoding = lookupEncoding(newEncoding) + if newEncoding is None: + return + if newEncoding.name in ("utf-16be", "utf-16le"): + newEncoding = lookupEncoding("utf-8") + assert newEncoding is not None + elif newEncoding == self.charEncoding[0]: + self.charEncoding = (self.charEncoding[0], "certain") + else: + self.rawStream.seek(0) + self.charEncoding = (newEncoding, "certain") + self.reset() + raise _ReparseException("Encoding changed from %s to %s" % (self.charEncoding[0], newEncoding)) + + def detectBOM(self): + """Attempts to detect at BOM at the start of the stream. If + an encoding can be determined from the BOM return the name of the + encoding otherwise return None""" + bomDict = { + codecs.BOM_UTF8: 'utf-8', + codecs.BOM_UTF16_LE: 'utf-16le', codecs.BOM_UTF16_BE: 'utf-16be', + codecs.BOM_UTF32_LE: 'utf-32le', codecs.BOM_UTF32_BE: 'utf-32be' + } + + # Go to beginning of file and read in 4 bytes + string = self.rawStream.read(4) + assert isinstance(string, bytes) + + # Try detecting the BOM using bytes from the string + encoding = bomDict.get(string[:3]) # UTF-8 + seek = 3 + if not encoding: + # Need to detect UTF-32 before UTF-16 + encoding = bomDict.get(string) # UTF-32 + seek = 4 + if not encoding: + encoding = bomDict.get(string[:2]) # UTF-16 + seek = 2 + + # Set the read position past the BOM if one was found, otherwise + # set it to the start of the stream + if encoding: + self.rawStream.seek(seek) + return lookupEncoding(encoding) + else: + self.rawStream.seek(0) + return None + + def detectEncodingMeta(self): + """Report the encoding declared by the meta element + """ + buffer = self.rawStream.read(self.numBytesMeta) + assert isinstance(buffer, bytes) + parser = EncodingParser(buffer) + self.rawStream.seek(0) + encoding = parser.getEncoding() + + if encoding is not None and encoding.name in ("utf-16be", "utf-16le"): + encoding = lookupEncoding("utf-8") + + return encoding + + +class EncodingBytes(bytes): + """String-like object with an associated position and various extra methods + If the position is ever greater than the string length then an exception is + raised""" + def __new__(self, value): + assert isinstance(value, bytes) + return bytes.__new__(self, value.lower()) + + def __init__(self, value): + # pylint:disable=unused-argument + self._position = -1 + + def __iter__(self): + return self + + def __next__(self): + p = self._position = self._position + 1 + if p >= len(self): + raise StopIteration + elif p < 0: + raise TypeError + return self[p:p + 1] + + def next(self): + # Py2 compat + return self.__next__() + + def previous(self): + p = self._position + if p >= len(self): + raise StopIteration + elif p < 0: + raise TypeError + self._position = p = p - 1 + return self[p:p + 1] + + def setPosition(self, position): + if self._position >= len(self): + raise StopIteration + self._position = position + + def getPosition(self): + if self._position >= len(self): + raise StopIteration + if self._position >= 0: + return self._position + else: + return None + + position = property(getPosition, setPosition) + + def getCurrentByte(self): + return self[self.position:self.position + 1] + + currentByte = property(getCurrentByte) + + def skip(self, chars=spaceCharactersBytes): + """Skip past a list of characters""" + p = self.position # use property for the error-checking + while p < len(self): + c = self[p:p + 1] + if c not in chars: + self._position = p + return c + p += 1 + self._position = p + return None + + def skipUntil(self, chars): + p = self.position + while p < len(self): + c = self[p:p + 1] + if c in chars: + self._position = p + return c + p += 1 + self._position = p + return None + + def matchBytes(self, bytes): + """Look for a sequence of bytes at the start of a string. If the bytes + are found return True and advance the position to the byte after the + match. Otherwise return False and leave the position alone""" + p = self.position + data = self[p:p + len(bytes)] + rv = data.startswith(bytes) + if rv: + self.position += len(bytes) + return rv + + def jumpTo(self, bytes): + """Look for the next sequence of bytes matching a given sequence. If + a match is found advance the position to the last byte of the match""" + newPosition = self[self.position:].find(bytes) + if newPosition > -1: + # XXX: This is ugly, but I can't see a nicer way to fix this. + if self._position == -1: + self._position = 0 + self._position += (newPosition + len(bytes) - 1) + return True + else: + raise StopIteration + + +class EncodingParser(object): + """Mini parser for detecting character encoding from meta elements""" + + def __init__(self, data): + """string - the data to work on for encoding detection""" + self.data = EncodingBytes(data) + self.encoding = None + + def getEncoding(self): + methodDispatch = ( + (b"<!--", self.handleComment), + (b"<meta", self.handleMeta), + (b"</", self.handlePossibleEndTag), + (b"<!", self.handleOther), + (b"<?", self.handleOther), + (b"<", self.handlePossibleStartTag)) + for _ in self.data: + keepParsing = True + for key, method in methodDispatch: + if self.data.matchBytes(key): + try: + keepParsing = method() + break + except StopIteration: + keepParsing = False + break + if not keepParsing: + break + + return self.encoding + + def handleComment(self): + """Skip over comments""" + return self.data.jumpTo(b"-->") + + def handleMeta(self): + if self.data.currentByte not in spaceCharactersBytes: + # if we have <meta not followed by a space so just keep going + return True + # We have a valid meta element we want to search for attributes + hasPragma = False + pendingEncoding = None + while True: + # Try to find the next attribute after the current position + attr = self.getAttribute() + if attr is None: + return True + else: + if attr[0] == b"http-equiv": + hasPragma = attr[1] == b"content-type" + if hasPragma and pendingEncoding is not None: + self.encoding = pendingEncoding + return False + elif attr[0] == b"charset": + tentativeEncoding = attr[1] + codec = lookupEncoding(tentativeEncoding) + if codec is not None: + self.encoding = codec + return False + elif attr[0] == b"content": + contentParser = ContentAttrParser(EncodingBytes(attr[1])) + tentativeEncoding = contentParser.parse() + if tentativeEncoding is not None: + codec = lookupEncoding(tentativeEncoding) + if codec is not None: + if hasPragma: + self.encoding = codec + return False + else: + pendingEncoding = codec + + def handlePossibleStartTag(self): + return self.handlePossibleTag(False) + + def handlePossibleEndTag(self): + next(self.data) + return self.handlePossibleTag(True) + + def handlePossibleTag(self, endTag): + data = self.data + if data.currentByte not in asciiLettersBytes: + # If the next byte is not an ascii letter either ignore this + # fragment (possible start tag case) or treat it according to + # handleOther + if endTag: + data.previous() + self.handleOther() + return True + + c = data.skipUntil(spacesAngleBrackets) + if c == b"<": + # return to the first step in the overall "two step" algorithm + # reprocessing the < byte + data.previous() + else: + # Read all attributes + attr = self.getAttribute() + while attr is not None: + attr = self.getAttribute() + return True + + def handleOther(self): + return self.data.jumpTo(b">") + + def getAttribute(self): + """Return a name,value pair for the next attribute in the stream, + if one is found, or None""" + data = self.data + # Step 1 (skip chars) + c = data.skip(spaceCharactersBytes | frozenset([b"/"])) + assert c is None or len(c) == 1 + # Step 2 + if c in (b">", None): + return None + # Step 3 + attrName = [] + attrValue = [] + # Step 4 attribute name + while True: + if c == b"=" and attrName: + break + elif c in spaceCharactersBytes: + # Step 6! + c = data.skip() + break + elif c in (b"/", b">"): + return b"".join(attrName), b"" + elif c in asciiUppercaseBytes: + attrName.append(c.lower()) + elif c is None: + return None + else: + attrName.append(c) + # Step 5 + c = next(data) + # Step 7 + if c != b"=": + data.previous() + return b"".join(attrName), b"" + # Step 8 + next(data) + # Step 9 + c = data.skip() + # Step 10 + if c in (b"'", b'"'): + # 10.1 + quoteChar = c + while True: + # 10.2 + c = next(data) + # 10.3 + if c == quoteChar: + next(data) + return b"".join(attrName), b"".join(attrValue) + # 10.4 + elif c in asciiUppercaseBytes: + attrValue.append(c.lower()) + # 10.5 + else: + attrValue.append(c) + elif c == b">": + return b"".join(attrName), b"" + elif c in asciiUppercaseBytes: + attrValue.append(c.lower()) + elif c is None: + return None + else: + attrValue.append(c) + # Step 11 + while True: + c = next(data) + if c in spacesAngleBrackets: + return b"".join(attrName), b"".join(attrValue) + elif c in asciiUppercaseBytes: + attrValue.append(c.lower()) + elif c is None: + return None + else: + attrValue.append(c) + + +class ContentAttrParser(object): + def __init__(self, data): + assert isinstance(data, bytes) + self.data = data + + def parse(self): + try: + # Check if the attr name is charset + # otherwise return + self.data.jumpTo(b"charset") + self.data.position += 1 + self.data.skip() + if not self.data.currentByte == b"=": + # If there is no = sign keep looking for attrs + return None + self.data.position += 1 + self.data.skip() + # Look for an encoding between matching quote marks + if self.data.currentByte in (b'"', b"'"): + quoteMark = self.data.currentByte + self.data.position += 1 + oldPosition = self.data.position + if self.data.jumpTo(quoteMark): + return self.data[oldPosition:self.data.position] + else: + return None + else: + # Unquoted value + oldPosition = self.data.position + try: + self.data.skipUntil(spaceCharactersBytes) + return self.data[oldPosition:self.data.position] + except StopIteration: + # Return the whole remaining value + return self.data[oldPosition:] + except StopIteration: + return None + + +def lookupEncoding(encoding): + """Return the python codec name corresponding to an encoding or None if the + string doesn't correspond to a valid encoding.""" + if isinstance(encoding, binary_type): + try: + encoding = encoding.decode("ascii") + except UnicodeDecodeError: + return None + + if encoding is not None: + try: + return webencodings.lookup(encoding) + except AttributeError: + return None + else: + return None diff --git a/venv2/lib/python2.7/site-packages/pip/_vendor/html5lib/_tokenizer.py b/venv2/lib/python2.7/site-packages/pip/_vendor/html5lib/_tokenizer.py new file mode 100644 index 0000000..178f6e7 --- /dev/null +++ b/venv2/lib/python2.7/site-packages/pip/_vendor/html5lib/_tokenizer.py @@ -0,0 +1,1721 @@ +from __future__ import absolute_import, division, unicode_literals + +from pip._vendor.six import unichr as chr + +from collections import deque + +from .constants import spaceCharacters +from .constants import entities +from .constants import asciiLetters, asciiUpper2Lower +from .constants import digits, hexDigits, EOF +from .constants import tokenTypes, tagTokenTypes +from .constants import replacementCharacters + +from ._inputstream import HTMLInputStream + +from ._trie import Trie + +entitiesTrie = Trie(entities) + + +class HTMLTokenizer(object): + """ This class takes care of tokenizing HTML. + + * self.currentToken + Holds the token that is currently being processed. + + * self.state + Holds a reference to the method to be invoked... XXX + + * self.stream + Points to HTMLInputStream object. + """ + + def __init__(self, stream, parser=None, **kwargs): + + self.stream = HTMLInputStream(stream, **kwargs) + self.parser = parser + + # Setup the initial tokenizer state + self.escapeFlag = False + self.lastFourChars = [] + self.state = self.dataState + self.escape = False + + # The current token being created + self.currentToken = None + super(HTMLTokenizer, self).__init__() + + def __iter__(self): + """ This is where the magic happens. + + We do our usually processing through the states and when we have a token + to return we yield the token which pauses processing until the next token + is requested. + """ + self.tokenQueue = deque([]) + # Start processing. When EOF is reached self.state will return False + # instead of True and the loop will terminate. + while self.state(): + while self.stream.errors: + yield {"type": tokenTypes["ParseError"], "data": self.stream.errors.pop(0)} + while self.tokenQueue: + yield self.tokenQueue.popleft() + + def consumeNumberEntity(self, isHex): + """This function returns either U+FFFD or the character based on the + decimal or hexadecimal representation. It also discards ";" if present. + If not present self.tokenQueue.append({"type": tokenTypes["ParseError"]}) is invoked. + """ + + allowed = digits + radix = 10 + if isHex: + allowed = hexDigits + radix = 16 + + charStack = [] + + # Consume all the characters that are in range while making sure we + # don't hit an EOF. + c = self.stream.char() + while c in allowed and c is not EOF: + charStack.append(c) + c = self.stream.char() + + # Convert the set of characters consumed to an int. + charAsInt = int("".join(charStack), radix) + + # Certain characters get replaced with others + if charAsInt in replacementCharacters: + char = replacementCharacters[charAsInt] + self.tokenQueue.append({"type": tokenTypes["ParseError"], "data": + "illegal-codepoint-for-numeric-entity", + "datavars": {"charAsInt": charAsInt}}) + elif ((0xD800 <= charAsInt <= 0xDFFF) or + (charAsInt > 0x10FFFF)): + char = "\uFFFD" + self.tokenQueue.append({"type": tokenTypes["ParseError"], "data": + "illegal-codepoint-for-numeric-entity", + "datavars": {"charAsInt": charAsInt}}) + else: + # Should speed up this check somehow (e.g. move the set to a constant) + if ((0x0001 <= charAsInt <= 0x0008) or + (0x000E <= charAsInt <= 0x001F) or + (0x007F <= charAsInt <= 0x009F) or + (0xFDD0 <= charAsInt <= 0xFDEF) or + charAsInt in frozenset([0x000B, 0xFFFE, 0xFFFF, 0x1FFFE, + 0x1FFFF, 0x2FFFE, 0x2FFFF, 0x3FFFE, + 0x3FFFF, 0x4FFFE, 0x4FFFF, 0x5FFFE, + 0x5FFFF, 0x6FFFE, 0x6FFFF, 0x7FFFE, + 0x7FFFF, 0x8FFFE, 0x8FFFF, 0x9FFFE, + 0x9FFFF, 0xAFFFE, 0xAFFFF, 0xBFFFE, + 0xBFFFF, 0xCFFFE, 0xCFFFF, 0xDFFFE, + 0xDFFFF, 0xEFFFE, 0xEFFFF, 0xFFFFE, + 0xFFFFF, 0x10FFFE, 0x10FFFF])): + self.tokenQueue.append({"type": tokenTypes["ParseError"], + "data": + "illegal-codepoint-for-numeric-entity", + "datavars": {"charAsInt": charAsInt}}) + try: + # Try/except needed as UCS-2 Python builds' unichar only works + # within the BMP. + char = chr(charAsInt) + except ValueError: + v = charAsInt - 0x10000 + char = chr(0xD800 | (v >> 10)) + chr(0xDC00 | (v & 0x3FF)) + + # Discard the ; if present. Otherwise, put it back on the queue and + # invoke parseError on parser. + if c != ";": + self.tokenQueue.append({"type": tokenTypes["ParseError"], "data": + "numeric-entity-without-semicolon"}) + self.stream.unget(c) + + return char + + def consumeEntity(self, allowedChar=None, fromAttribute=False): + # Initialise to the default output for when no entity is matched + output = "&" + + charStack = [self.stream.char()] + if (charStack[0] in spaceCharacters or charStack[0] in (EOF, "<", "&") or + (allowedChar is not None and allowedChar == charStack[0])): + self.stream.unget(charStack[0]) + + elif charStack[0] == "#": + # Read the next character to see if it's hex or decimal + hex = False + charStack.append(self.stream.char()) + if charStack[-1] in ("x", "X"): + hex = True + charStack.append(self.stream.char()) + + # charStack[-1] should be the first digit + if (hex and charStack[-1] in hexDigits) \ + or (not hex and charStack[-1] in digits): + # At least one digit found, so consume the whole number + self.stream.unget(charStack[-1]) + output = self.consumeNumberEntity(hex) + else: + # No digits found + self.tokenQueue.append({"type": tokenTypes["ParseError"], + "data": "expected-numeric-entity"}) + self.stream.unget(charStack.pop()) + output = "&" + "".join(charStack) + + else: + # At this point in the process might have named entity. Entities + # are stored in the global variable "entities". + # + # Consume characters and compare to these to a substring of the + # entity names in the list until the substring no longer matches. + while (charStack[-1] is not EOF): + if not entitiesTrie.has_keys_with_prefix("".join(charStack)): + break + charStack.append(self.stream.char()) + + # At this point we have a string that starts with some characters + # that may match an entity + # Try to find the longest entity the string will match to take care + # of ¬i for instance. + try: + entityName = entitiesTrie.longest_prefix("".join(charStack[:-1])) + entityLength = len(entityName) + except KeyError: + entityName = None + + if entityName is not None: + if entityName[-1] != ";": + self.tokenQueue.append({"type": tokenTypes["ParseError"], "data": + "named-entity-without-semicolon"}) + if (entityName[-1] != ";" and fromAttribute and + (charStack[entityLength] in asciiLetters or + charStack[entityLength] in digits or + charStack[entityLength] == "=")): + self.stream.unget(charStack.pop()) + output = "&" + "".join(charStack) + else: + output = entities[entityName] + self.stream.unget(charStack.pop()) + output += "".join(charStack[entityLength:]) + else: + self.tokenQueue.append({"type": tokenTypes["ParseError"], "data": + "expected-named-entity"}) + self.stream.unget(charStack.pop()) + output = "&" + "".join(charStack) + + if fromAttribute: + self.currentToken["data"][-1][1] += output + else: + if output in spaceCharacters: + tokenType = "SpaceCharacters" + else: + tokenType = "Characters" + self.tokenQueue.append({"type": tokenTypes[tokenType], "data": output}) + + def processEntityInAttribute(self, allowedChar): + """This method replaces the need for "entityInAttributeValueState". + """ + self.consumeEntity(allowedChar=allowedChar, fromAttribute=True) + + def emitCurrentToken(self): + """This method is a generic handler for emitting the tags. It also sets + the state to "data" because that's what's needed after a token has been + emitted. + """ + token = self.currentToken + # Add token to the queue to be yielded + if (token["type"] in tagTokenTypes): + token["name"] = token["name"].translate(asciiUpper2Lower) + if token["type"] == tokenTypes["EndTag"]: + if token["data"]: + self.tokenQueue.append({"type": tokenTypes["ParseError"], + "data": "attributes-in-end-tag"}) + if token["selfClosing"]: + self.tokenQueue.append({"type": tokenTypes["ParseError"], + "data": "self-closing-flag-on-end-tag"}) + self.tokenQueue.append(token) + self.state = self.dataState + + # Below are the various tokenizer states worked out. + def dataState(self): + data = self.stream.char() + if data == "&": + self.state = self.entityDataState + elif data == "<": + self.state = self.tagOpenState + elif data == "\u0000": + self.tokenQueue.append({"type": tokenTypes["ParseError"], + "data": "invalid-codepoint"}) + self.tokenQueue.append({"type": tokenTypes["Characters"], + "data": "\u0000"}) + elif data is EOF: + # Tokenization ends. + return False + elif data in spaceCharacters: + # Directly after emitting a token you switch back to the "data + # state". At that point spaceCharacters are important so they are + # emitted separately. + self.tokenQueue.append({"type": tokenTypes["SpaceCharacters"], "data": + data + self.stream.charsUntil(spaceCharacters, True)}) + # No need to update lastFourChars here, since the first space will + # have already been appended to lastFourChars and will have broken + # any <!-- or --> sequences + else: + chars = self.stream.charsUntil(("&", "<", "\u0000")) + self.tokenQueue.append({"type": tokenTypes["Characters"], "data": + data + chars}) + return True + + def entityDataState(self): + self.consumeEntity() + self.state = self.dataState + return True + + def rcdataState(self): + data = self.stream.char() + if data == "&": + self.state = self.characterReferenceInRcdata + elif data == "<": + self.state = self.rcdataLessThanSignState + elif data == EOF: + # Tokenization ends. + return False + elif data == "\u0000": + self.tokenQueue.append({"type": tokenTypes["ParseError"], + "data": "invalid-codepoint"}) + self.tokenQueue.append({"type": tokenTypes["Characters"], + "data": "\uFFFD"}) + elif data in spaceCharacters: + # Directly after emitting a token you switch back to the "data + # state". At that point spaceCharacters are important so they are + # emitted separately. + self.tokenQueue.append({"type": tokenTypes["SpaceCharacters"], "data": + data + self.stream.charsUntil(spaceCharacters, True)}) + # No need to update lastFourChars here, since the first space will + # have already been appended to lastFourChars and will have broken + # any <!-- or --> sequences + else: + chars = self.stream.charsUntil(("&", "<", "\u0000")) + self.tokenQueue.append({"type": tokenTypes["Characters"], "data": + data + chars}) + return True + + def characterReferenceInRcdata(self): + self.consumeEntity() + self.state = self.rcdataState + return True + + def rawtextState(self): + data = self.stream.char() + if data == "<": + self.state = self.rawtextLessThanSignState + elif data == "\u0000": + self.tokenQueue.append({"type": tokenTypes["ParseError"], + "data": "invalid-codepoint"}) + self.tokenQueue.append({"type": tokenTypes["Characters"], + "data": "\uFFFD"}) + elif data == EOF: + # Tokenization ends. + return False + else: + chars = self.stream.charsUntil(("<", "\u0000")) + self.tokenQueue.append({"type": tokenTypes["Characters"], "data": + data + chars}) + return True + + def scriptDataState(self): + data = self.stream.char() + if data == "<": + self.state = self.scriptDataLessThanSignState + elif data == "\u0000": + self.tokenQueue.append({"type": tokenTypes["ParseError"], + "data": "invalid-codepoint"}) + self.tokenQueue.append({"type": tokenTypes["Characters"], + "data": "\uFFFD"}) + elif data == EOF: + # Tokenization ends. + return False + else: + chars = self.stream.charsUntil(("<", "\u0000")) + self.tokenQueue.append({"type": tokenTypes["Characters"], "data": + data + chars}) + return True + + def plaintextState(self): + data = self.stream.char() + if data == EOF: + # Tokenization ends. + return False + elif data == "\u0000": + self.tokenQueue.append({"type": tokenTypes["ParseError"], + "data": "invalid-codepoint"}) + self.tokenQueue.append({"type": tokenTypes["Characters"], + "data": "\uFFFD"}) + else: + self.tokenQueue.append({"type": tokenTypes["Characters"], "data": + data + self.stream.charsUntil("\u0000")}) + return True + + def tagOpenState(self): + data = self.stream.char() + if data == "!": + self.state = self.markupDeclarationOpenState + elif data == "/": + self.state = self.closeTagOpenState + elif data in asciiLetters: + self.currentToken = {"type": tokenTypes["StartTag"], + "name": data, "data": [], + "selfClosing": False, + "selfClosingAcknowledged": False} + self.state = self.tagNameState + elif data == ">": + # XXX In theory it could be something besides a tag name. But + # do we really care? + self.tokenQueue.append({"type": tokenTypes["ParseError"], "data": + "expected-tag-name-but-got-right-bracket"}) + self.tokenQueue.append({"type": tokenTypes["Characters"], "data": "<>"}) + self.state = self.dataState + elif data == "?": + # XXX In theory it could be something besides a tag name. But + # do we really care? + self.tokenQueue.append({"type": tokenTypes["ParseError"], "data": + "expected-tag-name-but-got-question-mark"}) + self.stream.unget(data) + self.state = self.bogusCommentState + else: + # XXX + self.tokenQueue.append({"type": tokenTypes["ParseError"], "data": + "expected-tag-name"}) + self.tokenQueue.append({"type": tokenTypes["Characters"], "data": "<"}) + self.stream.unget(data) + self.state = self.dataState + return True + + def closeTagOpenState(self): + data = self.stream.char() + if data in asciiLetters: + self.currentToken = {"type": tokenTypes["EndTag"], "name": data, + "data": [], "selfClosing": False} + self.state = self.tagNameState + elif data == ">": + self.tokenQueue.append({"type": tokenTypes["ParseError"], "data": + "expected-closing-tag-but-got-right-bracket"}) + self.state = self.dataState + elif data is EOF: + self.tokenQueue.append({"type": tokenTypes["ParseError"], "data": + "expected-closing-tag-but-got-eof"}) + self.tokenQueue.append({"type": tokenTypes["Characters"], "data": "</"}) + self.state = self.dataState + else: + # XXX data can be _'_... + self.tokenQueue.append({"type": tokenTypes["ParseError"], "data": + "expected-closing-tag-but-got-char", + "datavars": {"data": data}}) + self.stream.unget(data) + self.state = self.bogusCommentState + return True + + def tagNameState(self): + data = self.stream.char() + if data in spaceCharacters: + self.state = self.beforeAttributeNameState + elif data == ">": + self.emitCurrentToken() + elif data is EOF: + self.tokenQueue.append({"type": tokenTypes["ParseError"], "data": + "eof-in-tag-name"}) + self.state = self.dataState + elif data == "/": + self.state = self.selfClosingStartTagState + elif data == "\u0000": + self.tokenQueue.append({"type": tokenTypes["ParseError"], + "data": "invalid-codepoint"}) + self.currentToken["name"] += "\uFFFD" + else: + self.currentToken["name"] += data + # (Don't use charsUntil here, because tag names are + # very short and it's faster to not do anything fancy) + return True + + def rcdataLessThanSignState(self): + data = self.stream.char() + if data == "/": + self.temporaryBuffer = "" + self.state = self.rcdataEndTagOpenState + else: + self.tokenQueue.append({"type": tokenTypes["Characters"], "data": "<"}) + self.stream.unget(data) + self.state = self.rcdataState + return True + + def rcdataEndTagOpenState(self): + data = self.stream.char() + if data in asciiLetters: + self.temporaryBuffer += data + self.state = self.rcdataEndTagNameState + else: + self.tokenQueue.append({"type": tokenTypes["Characters"], "data": "</"}) + self.stream.unget(data) + self.state = self.rcdataState + return True + + def rcdataEndTagNameState(self): + appropriate = self.currentToken and self.currentToken["name"].lower() == self.temporaryBuffer.lower() + data = self.stream.char() + if data in spaceCharacters and appropriate: + self.currentToken = {"type": tokenTypes["EndTag"], + "name": self.temporaryBuffer, + "data": [], "selfClosing": False} + self.state = self.beforeAttributeNameState + elif data == "/" and appropriate: + self.currentToken = {"type": tokenTypes["EndTag"], + "name": self.temporaryBuffer, + "data": [], "selfClosing": False} + self.state = self.selfClosingStartTagState + elif data == ">" and appropriate: + self.currentToken = {"type": tokenTypes["EndTag"], + "name": self.temporaryBuffer, + "data": [], "selfClosing": False} + self.emitCurrentToken() + self.state = self.dataState + elif data in asciiLetters: + self.temporaryBuffer += data + else: + self.tokenQueue.append({"type": tokenTypes["Characters"], + "data": "</" + self.temporaryBuffer}) + self.stream.unget(data) + self.state = self.rcdataState + return True + + def rawtextLessThanSignState(self): + data = self.stream.char() + if data == "/": + self.temporaryBuffer = "" + self.state = self.rawtextEndTagOpenState + else: + self.tokenQueue.append({"type": tokenTypes["Characters"], "data": "<"}) + self.stream.unget(data) + self.state = self.rawtextState + return True + + def rawtextEndTagOpenState(self): + data = self.stream.char() + if data in asciiLetters: + self.temporaryBuffer += data + self.state = self.rawtextEndTagNameState + else: + self.tokenQueue.append({"type": tokenTypes["Characters"], "data": "</"}) + self.stream.unget(data) + self.state = self.rawtextState + return True + + def rawtextEndTagNameState(self): + appropriate = self.currentToken and self.currentToken["name"].lower() == self.temporaryBuffer.lower() + data = self.stream.char() + if data in spaceCharacters and appropriate: + self.currentToken = {"type": tokenTypes["EndTag"], + "name": self.temporaryBuffer, + "data": [], "selfClosing": False} + self.state = self.beforeAttributeNameState + elif data == "/" and appropriate: + self.currentToken = {"type": tokenTypes["EndTag"], + "name": self.temporaryBuffer, + "data": [], "selfClosing": False} + self.state = self.selfClosingStartTagState + elif data == ">" and appropriate: + self.currentToken = {"type": tokenTypes["EndTag"], + "name": self.temporaryBuffer, + "data": [], "selfClosing": False} + self.emitCurrentToken() + self.state = self.dataState + elif data in asciiLetters: + self.temporaryBuffer += data + else: + self.tokenQueue.append({"type": tokenTypes["Characters"], + "data": "</" + self.temporaryBuffer}) + self.stream.unget(data) + self.state = self.rawtextState + return True + + def scriptDataLessThanSignState(self): + data = self.stream.char() + if data == "/": + self.temporaryBuffer = "" + self.state = self.scriptDataEndTagOpenState + elif data == "!": + self.tokenQueue.append({"type": tokenTypes["Characters"], "data": "<!"}) + self.state = self.scriptDataEscapeStartState + else: + self.tokenQueue.append({"type": tokenTypes["Characters"], "data": "<"}) + self.stream.unget(data) + self.state = self.scriptDataState + return True + + def scriptDataEndTagOpenState(self): + data = self.stream.char() + if data in asciiLetters: + self.temporaryBuffer += data + self.state = self.scriptDataEndTagNameState + else: + self.tokenQueue.append({"type": tokenTypes["Characters"], "data": "</"}) + self.stream.unget(data) + self.state = self.scriptDataState + return True + + def scriptDataEndTagNameState(self): + appropriate = self.currentToken and self.currentToken["name"].lower() == self.temporaryBuffer.lower() + data = self.stream.char() + if data in spaceCharacters and appropriate: + self.currentToken = {"type": tokenTypes["EndTag"], + "name": self.temporaryBuffer, + "data": [], "selfClosing": False} + self.state = self.beforeAttributeNameState + elif data == "/" and appropriate: + self.currentToken = {"type": tokenTypes["EndTag"], + "name": self.temporaryBuffer, + "data": [], "selfClosing": False} + self.state = self.selfClosingStartTagState + elif data == ">" and appropriate: + self.currentToken = {"type": tokenTypes["EndTag"], + "name": self.temporaryBuffer, + "data": [], "selfClosing": False} + self.emitCurrentToken() + self.state = self.dataState + elif data in asciiLetters: + self.temporaryBuffer += data + else: + self.tokenQueue.append({"type": tokenTypes["Characters"], + "data": "</" + self.temporaryBuffer}) + self.stream.unget(data) + self.state = self.scriptDataState + return True + + def scriptDataEscapeStartState(self): + data = self.stream.char() + if data == "-": + self.tokenQueue.append({"type": tokenTypes["Characters"], "data": "-"}) + self.state = self.scriptDataEscapeStartDashState + else: + self.stream.unget(data) + self.state = self.scriptDataState + return True + + def scriptDataEscapeStartDashState(self): + data = self.stream.char() + if data == "-": + self.tokenQueue.append({"type": tokenTypes["Characters"], "data": "-"}) + self.state = self.scriptDataEscapedDashDashState + else: + self.stream.unget(data) + self.state = self.scriptDataState + return True + + def scriptDataEscapedState(self): + data = self.stream.char() + if data == "-": + self.tokenQueue.append({"type": tokenTypes["Characters"], "data": "-"}) + self.state = self.scriptDataEscapedDashState + elif data == "<": + self.state = self.scriptDataEscapedLessThanSignState + elif data == "\u0000": + self.tokenQueue.append({"type": tokenTypes["ParseError"], + "data": "invalid-codepoint"}) + self.tokenQueue.append({"type": tokenTypes["Characters"], + "data": "\uFFFD"}) + elif data == EOF: + self.state = self.dataState + else: + chars = self.stream.charsUntil(("<", "-", "\u0000")) + self.tokenQueue.append({"type": tokenTypes["Characters"], "data": + data + chars}) + return True + + def scriptDataEscapedDashState(self): + data = self.stream.char() + if data == "-": + self.tokenQueue.append({"type": tokenTypes["Characters"], "data": "-"}) + self.state = self.scriptDataEscapedDashDashState + elif data == "<": + self.state = self.scriptDataEscapedLessThanSignState + elif data == "\u0000": + self.tokenQueue.append({"type": tokenTypes["ParseError"], + "data": "invalid-codepoint"}) + self.tokenQueue.append({"type": tokenTypes["Characters"], + "data": "\uFFFD"}) + self.state = self.scriptDataEscapedState + elif data == EOF: + self.state = self.dataState + else: + self.tokenQueue.append({"type": tokenTypes["Characters"], "data": data}) + self.state = self.scriptDataEscapedState + return True + + def scriptDataEscapedDashDashState(self): + data = self.stream.char() + if data == "-": + self.tokenQueue.append({"type": tokenTypes["Characters"], "data": "-"}) + elif data == "<": + self.state = self.scriptDataEscapedLessThanSignState + elif data == ">": + self.tokenQueue.append({"type": tokenTypes["Characters"], "data": ">"}) + self.state = self.scriptDataState + elif data == "\u0000": + self.tokenQueue.append({"type": tokenTypes["ParseError"], + "data": "invalid-codepoint"}) + self.tokenQueue.append({"type": tokenTypes["Characters"], + "data": "\uFFFD"}) + self.state = self.scriptDataEscapedState + elif data == EOF: + self.state = self.dataState + else: + self.tokenQueue.append({"type": tokenTypes["Characters"], "data": data}) + self.state = self.scriptDataEscapedState + return True + + def scriptDataEscapedLessThanSignState(self): + data = self.stream.char() + if data == "/": + self.temporaryBuffer = "" + self.state = self.scriptDataEscapedEndTagOpenState + elif data in asciiLetters: + self.tokenQueue.append({"type": tokenTypes["Characters"], "data": "<" + data}) + self.temporaryBuffer = data + self.state = self.scriptDataDoubleEscapeStartState + else: + self.tokenQueue.append({"type": tokenTypes["Characters"], "data": "<"}) + self.stream.unget(data) + self.state = self.scriptDataEscapedState + return True + + def scriptDataEscapedEndTagOpenState(self): + data = self.stream.char() + if data in asciiLetters: + self.temporaryBuffer = data + self.state = self.scriptDataEscapedEndTagNameState + else: + self.tokenQueue.append({"type": tokenTypes["Characters"], "data": "</"}) + self.stream.unget(data) + self.state = self.scriptDataEscapedState + return True + + def scriptDataEscapedEndTagNameState(self): + appropriate = self.currentToken and self.currentToken["name"].lower() == self.temporaryBuffer.lower() + data = self.stream.char() + if data in spaceCharacters and appropriate: + self.currentToken = {"type": tokenTypes["EndTag"], + "name": self.temporaryBuffer, + "data": [], "selfClosing": False} + self.state = self.beforeAttributeNameState + elif data == "/" and appropriate: + self.currentToken = {"type": tokenTypes["EndTag"], + "name": self.temporaryBuffer, + "data": [], "selfClosing": False} + self.state = self.selfClosingStartTagState + elif data == ">" and appropriate: + self.currentToken = {"type": tokenTypes["EndTag"], + "name": self.temporaryBuffer, + "data": [], "selfClosing": False} + self.emitCurrentToken() + self.state = self.dataState + elif data in asciiLetters: + self.temporaryBuffer += data + else: + self.tokenQueue.append({"type": tokenTypes["Characters"], + "data": "</" + self.temporaryBuffer}) + self.stream.unget(data) + self.state = self.scriptDataEscapedState + return True + + def scriptDataDoubleEscapeStartState(self): + data = self.stream.char() + if data in (spaceCharacters | frozenset(("/", ">"))): + self.tokenQueue.append({"type": tokenTypes["Characters"], "data": data}) + if self.temporaryBuffer.lower() == "script": + self.state = self.scriptDataDoubleEscapedState + else: + self.state = self.scriptDataEscapedState + elif data in asciiLetters: + self.tokenQueue.append({"type": tokenTypes["Characters"], "data": data}) + self.temporaryBuffer += data + else: + self.stream.unget(data) + self.state = self.scriptDataEscapedState + return True + + def scriptDataDoubleEscapedState(self): + data = self.stream.char() + if data == "-": + self.tokenQueue.append({"type": tokenTypes["Characters"], "data": "-"}) + self.state = self.scriptDataDoubleEscapedDashState + elif data == "<": + self.tokenQueue.append({"type": tokenTypes["Characters"], "data": "<"}) + self.state = self.scriptDataDoubleEscapedLessThanSignState + elif data == "\u0000": + self.tokenQueue.append({"type": tokenTypes["ParseError"], + "data": "invalid-codepoint"}) + self.tokenQueue.append({"type": tokenTypes["Characters"], + "data": "\uFFFD"}) + elif data == EOF: + self.tokenQueue.append({"type": tokenTypes["ParseError"], "data": + "eof-in-script-in-script"}) + self.state = self.dataState + else: + self.tokenQueue.append({"type": tokenTypes["Characters"], "data": data}) + return True + + def scriptDataDoubleEscapedDashState(self): + data = self.stream.char() + if data == "-": + self.tokenQueue.append({"type": tokenTypes["Characters"], "data": "-"}) + self.state = self.scriptDataDoubleEscapedDashDashState + elif data == "<": + self.tokenQueue.append({"type": tokenTypes["Characters"], "data": "<"}) + self.state = self.scriptDataDoubleEscapedLessThanSignState + elif data == "\u0000": + self.tokenQueue.append({"type": tokenTypes["ParseError"], + "data": "invalid-codepoint"}) + self.tokenQueue.append({"type": tokenTypes["Characters"], + "data": "\uFFFD"}) + self.state = self.scriptDataDoubleEscapedState + elif data == EOF: + self.tokenQueue.append({"type": tokenTypes["ParseError"], "data": + "eof-in-script-in-script"}) + self.state = self.dataState + else: + self.tokenQueue.append({"type": tokenTypes["Characters"], "data": data}) + self.state = self.scriptDataDoubleEscapedState + return True + + def scriptDataDoubleEscapedDashDashState(self): + data = self.stream.char() + if data == "-": + self.tokenQueue.append({"type": tokenTypes["Characters"], "data": "-"}) + elif data == "<": + self.tokenQueue.append({"type": tokenTypes["Characters"], "data": "<"}) + self.state = self.scriptDataDoubleEscapedLessThanSignState + elif data == ">": + self.tokenQueue.append({"type": tokenTypes["Characters"], "data": ">"}) + self.state = self.scriptDataState + elif data == "\u0000": + self.tokenQueue.append({"type": tokenTypes["ParseError"], + "data": "invalid-codepoint"}) + self.tokenQueue.append({"type": tokenTypes["Characters"], + "data": "\uFFFD"}) + self.state = self.scriptDataDoubleEscapedState + elif data == EOF: + self.tokenQueue.append({"type": tokenTypes["ParseError"], "data": + "eof-in-script-in-script"}) + self.state = self.dataState + else: + self.tokenQueue.append({"type": tokenTypes["Characters"], "data": data}) + self.state = self.scriptDataDoubleEscapedState + return True + + def scriptDataDoubleEscapedLessThanSignState(self): + data = self.stream.char() + if data == "/": + self.tokenQueue.append({"type": tokenTypes["Characters"], "data": "/"}) + self.temporaryBuffer = "" + self.state = self.scriptDataDoubleEscapeEndState + else: + self.stream.unget(data) + self.state = self.scriptDataDoubleEscapedState + return True + + def scriptDataDoubleEscapeEndState(self): + data = self.stream.char() + if data in (spaceCharacters | frozenset(("/", ">"))): + self.tokenQueue.append({"type": tokenTypes["Characters"], "data": data}) + if self.temporaryBuffer.lower() == "script": + self.state = self.scriptDataEscapedState + else: + self.state = self.scriptDataDoubleEscapedState + elif data in asciiLetters: + self.tokenQueue.append({"type": tokenTypes["Characters"], "data": data}) + self.temporaryBuffer += data + else: + self.stream.unget(data) + self.state = self.scriptDataDoubleEscapedState + return True + + def beforeAttributeNameState(self): + data = self.stream.char() + if data in spaceCharacters: + self.stream.charsUntil(spaceCharacters, True) + elif data in asciiLetters: + self.currentToken["data"].append([data, ""]) + self.state = self.attributeNameState + elif data == ">": + self.emitCurrentToken() + elif data == "/": + self.state = self.selfClosingStartTagState + elif data in ("'", '"', "=", "<"): + self.tokenQueue.append({"type": tokenTypes["ParseError"], "data": + "invalid-character-in-attribute-name"}) + self.currentToken["data"].append([data, ""]) + self.state = self.attributeNameState + elif data == "\u0000": + self.tokenQueue.append({"type": tokenTypes["ParseError"], + "data": "invalid-codepoint"}) + self.currentToken["data"].append(["\uFFFD", ""]) + self.state = self.attributeNameState + elif data is EOF: + self.tokenQueue.append({"type": tokenTypes["ParseError"], "data": + "expected-attribute-name-but-got-eof"}) + self.state = self.dataState + else: + self.currentToken["data"].append([data, ""]) + self.state = self.attributeNameState + return True + + def attributeNameState(self): + data = self.stream.char() + leavingThisState = True + emitToken = False + if data == "=": + self.state = self.beforeAttributeValueState + elif data in asciiLetters: + self.currentToken["data"][-1][0] += data +\ + self.stream.charsUntil(asciiLetters, True) + leavingThisState = False + elif data == ">": + # XXX If we emit here the attributes are converted to a dict + # without being checked and when the code below runs we error + # because data is a dict not a list + emitToken = True + elif data in spaceCharacters: + self.state = self.afterAttributeNameState + elif data == "/": + self.state = self.selfClosingStartTagState + elif data == "\u0000": + self.tokenQueue.append({"type": tokenTypes["ParseError"], + "data": "invalid-codepoint"}) + self.currentToken["data"][-1][0] += "\uFFFD" + leavingThisState = False + elif data in ("'", '"', "<"): + self.tokenQueue.append({"type": tokenTypes["ParseError"], + "data": + "invalid-character-in-attribute-name"}) + self.currentToken["data"][-1][0] += data + leavingThisState = False + elif data is EOF: + self.tokenQueue.append({"type": tokenTypes["ParseError"], + "data": "eof-in-attribute-name"}) + self.state = self.dataState + else: + self.currentToken["data"][-1][0] += data + leavingThisState = False + + if leavingThisState: + # Attributes are not dropped at this stage. That happens when the + # start tag token is emitted so values can still be safely appended + # to attributes, but we do want to report the parse error in time. + self.currentToken["data"][-1][0] = ( + self.currentToken["data"][-1][0].translate(asciiUpper2Lower)) + for name, _ in self.currentToken["data"][:-1]: + if self.currentToken["data"][-1][0] == name: + self.tokenQueue.append({"type": tokenTypes["ParseError"], "data": + "duplicate-attribute"}) + break + # XXX Fix for above XXX + if emitToken: + self.emitCurrentToken() + return True + + def afterAttributeNameState(self): + data = self.stream.char() + if data in spaceCharacters: + self.stream.charsUntil(spaceCharacters, True) + elif data == "=": + self.state = self.beforeAttributeValueState + elif data == ">": + self.emitCurrentToken() + elif data in asciiLetters: + self.currentToken["data"].append([data, ""]) + self.state = self.attributeNameState + elif data == "/": + self.state = self.selfClosingStartTagState + elif data == "\u0000": + self.tokenQueue.append({"type": tokenTypes["ParseError"], + "data": "invalid-codepoint"}) + self.currentToken["data"].append(["\uFFFD", ""]) + self.state = self.attributeNameState + elif data in ("'", '"', "<"): + self.tokenQueue.append({"type": tokenTypes["ParseError"], "data": + "invalid-character-after-attribute-name"}) + self.currentToken["data"].append([data, ""]) + self.state = self.attributeNameState + elif data is EOF: + self.tokenQueue.append({"type": tokenTypes["ParseError"], "data": + "expected-end-of-tag-but-got-eof"}) + self.state = self.dataState + else: + self.currentToken["data"].append([data, ""]) + self.state = self.attributeNameState + return True + + def beforeAttributeValueState(self): + data = self.stream.char() + if data in spaceCharacters: + self.stream.charsUntil(spaceCharacters, True) + elif data == "\"": + self.state = self.attributeValueDoubleQuotedState + elif data == "&": + self.state = self.attributeValueUnQuotedState + self.stream.unget(data) + elif data == "'": + self.state = self.attributeValueSingleQuotedState + elif data == ">": + self.tokenQueue.append({"type": tokenTypes["ParseError"], "data": + "expected-attribute-value-but-got-right-bracket"}) + self.emitCurrentToken() + elif data == "\u0000": + self.tokenQueue.append({"type": tokenTypes["ParseError"], + "data": "invalid-codepoint"}) + self.currentToken["data"][-1][1] += "\uFFFD" + self.state = self.attributeValueUnQuotedState + elif data in ("=", "<", "`"): + self.tokenQueue.append({"type": tokenTypes["ParseError"], "data": + "equals-in-unquoted-attribute-value"}) + self.currentToken["data"][-1][1] += data + self.state = self.attributeValueUnQuotedState + elif data is EOF: + self.tokenQueue.append({"type": tokenTypes["ParseError"], "data": + "expected-attribute-value-but-got-eof"}) + self.state = self.dataState + else: + self.currentToken["data"][-1][1] += data + self.state = self.attributeValueUnQuotedState + return True + + def attributeValueDoubleQuotedState(self): + data = self.stream.char() + if data == "\"": + self.state = self.afterAttributeValueState + elif data == "&": + self.processEntityInAttribute('"') + elif data == "\u0000": + self.tokenQueue.append({"type": tokenTypes["ParseError"], + "data": "invalid-codepoint"}) + self.currentToken["data"][-1][1] += "\uFFFD" + elif data is EOF: + self.tokenQueue.append({"type": tokenTypes["ParseError"], "data": + "eof-in-attribute-value-double-quote"}) + self.state = self.dataState + else: + self.currentToken["data"][-1][1] += data +\ + self.stream.charsUntil(("\"", "&", "\u0000")) + return True + + def attributeValueSingleQuotedState(self): + data = self.stream.char() + if data == "'": + self.state = self.afterAttributeValueState + elif data == "&": + self.processEntityInAttribute("'") + elif data == "\u0000": + self.tokenQueue.append({"type": tokenTypes["ParseError"], + "data": "invalid-codepoint"}) + self.currentToken["data"][-1][1] += "\uFFFD" + elif data is EOF: + self.tokenQueue.append({"type": tokenTypes["ParseError"], "data": + "eof-in-attribute-value-single-quote"}) + self.state = self.dataState + else: + self.currentToken["data"][-1][1] += data +\ + self.stream.charsUntil(("'", "&", "\u0000")) + return True + + def attributeValueUnQuotedState(self): + data = self.stream.char() + if data in spaceCharacters: + self.state = self.beforeAttributeNameState + elif data == "&": + self.processEntityInAttribute(">") + elif data == ">": + self.emitCurrentToken() + elif data in ('"', "'", "=", "<", "`"): + self.tokenQueue.append({"type": tokenTypes["ParseError"], "data": + "unexpected-character-in-unquoted-attribute-value"}) + self.currentToken["data"][-1][1] += data + elif data == "\u0000": + self.tokenQueue.append({"type": tokenTypes["ParseError"], + "data": "invalid-codepoint"}) + self.currentToken["data"][-1][1] += "\uFFFD" + elif data is EOF: + self.tokenQueue.append({"type": tokenTypes["ParseError"], "data": + "eof-in-attribute-value-no-quotes"}) + self.state = self.dataState + else: + self.currentToken["data"][-1][1] += data + self.stream.charsUntil( + frozenset(("&", ">", '"', "'", "=", "<", "`", "\u0000")) | spaceCharacters) + return True + + def afterAttributeValueState(self): + data = self.stream.char() + if data in spaceCharacters: + self.state = self.beforeAttributeNameState + elif data == ">": + self.emitCurrentToken() + elif data == "/": + self.state = self.selfClosingStartTagState + elif data is EOF: + self.tokenQueue.append({"type": tokenTypes["ParseError"], "data": + "unexpected-EOF-after-attribute-value"}) + self.stream.unget(data) + self.state = self.dataState + else: + self.tokenQueue.append({"type": tokenTypes["ParseError"], "data": + "unexpected-character-after-attribute-value"}) + self.stream.unget(data) + self.state = self.beforeAttributeNameState + return True + + def selfClosingStartTagState(self): + data = self.stream.char() + if data == ">": + self.currentToken["selfClosing"] = True + self.emitCurrentToken() + elif data is EOF: + self.tokenQueue.append({"type": tokenTypes["ParseError"], + "data": + "unexpected-EOF-after-solidus-in-tag"}) + self.stream.unget(data) + self.state = self.dataState + else: + self.tokenQueue.append({"type": tokenTypes["ParseError"], "data": + "unexpected-character-after-solidus-in-tag"}) + self.stream.unget(data) + self.state = self.beforeAttributeNameState + return True + + def bogusCommentState(self): + # Make a new comment token and give it as value all the characters + # until the first > or EOF (charsUntil checks for EOF automatically) + # and emit it. + data = self.stream.charsUntil(">") + data = data.replace("\u0000", "\uFFFD") + self.tokenQueue.append( + {"type": tokenTypes["Comment"], "data": data}) + + # Eat the character directly after the bogus comment which is either a + # ">" or an EOF. + self.stream.char() + self.state = self.dataState + return True + + def markupDeclarationOpenState(self): + charStack = [self.stream.char()] + if charStack[-1] == "-": + charStack.append(self.stream.char()) + if charStack[-1] == "-": + self.currentToken = {"type": tokenTypes["Comment"], "data": ""} + self.state = self.commentStartState + return True + elif charStack[-1] in ('d', 'D'): + matched = True + for expected in (('o', 'O'), ('c', 'C'), ('t', 'T'), + ('y', 'Y'), ('p', 'P'), ('e', 'E')): + charStack.append(self.stream.char()) + if charStack[-1] not in expected: + matched = False + break + if matched: + self.currentToken = {"type": tokenTypes["Doctype"], + "name": "", + "publicId": None, "systemId": None, + "correct": True} + self.state = self.doctypeState + return True + elif (charStack[-1] == "[" and + self.parser is not None and + self.parser.tree.openElements and + self.parser.tree.openElements[-1].namespace != self.parser.tree.defaultNamespace): + matched = True + for expected in ["C", "D", "A", "T", "A", "["]: + charStack.append(self.stream.char()) + if charStack[-1] != expected: + matched = False + break + if matched: + self.state = self.cdataSectionState + return True + + self.tokenQueue.append({"type": tokenTypes["ParseError"], "data": + "expected-dashes-or-doctype"}) + + while charStack: + self.stream.unget(charStack.pop()) + self.state = self.bogusCommentState + return True + + def commentStartState(self): + data = self.stream.char() + if data == "-": + self.state = self.commentStartDashState + elif data == "\u0000": + self.tokenQueue.append({"type": tokenTypes["ParseError"], + "data": "invalid-codepoint"}) + self.currentToken["data"] += "\uFFFD" + elif data == ">": + self.tokenQueue.append({"type": tokenTypes["ParseError"], "data": + "incorrect-comment"}) + self.tokenQueue.append(self.currentToken) + self.state = self.dataState + elif data is EOF: + self.tokenQueue.append({"type": tokenTypes["ParseError"], "data": + "eof-in-comment"}) + self.tokenQueue.append(self.currentToken) + self.state = self.dataState + else: + self.currentToken["data"] += data + self.state = self.commentState + return True + + def commentStartDashState(self): + data = self.stream.char() + if data == "-": + self.state = self.commentEndState + elif data == "\u0000": + self.tokenQueue.append({"type": tokenTypes["ParseError"], + "data": "invalid-codepoint"}) + self.currentToken["data"] += "-\uFFFD" + elif data == ">": + self.tokenQueue.append({"type": tokenTypes["ParseError"], "data": + "incorrect-comment"}) + self.tokenQueue.append(self.currentToken) + self.state = self.dataState + elif data is EOF: + self.tokenQueue.append({"type": tokenTypes["ParseError"], "data": + "eof-in-comment"}) + self.tokenQueue.append(self.currentToken) + self.state = self.dataState + else: + self.currentToken["data"] += "-" + data + self.state = self.commentState + return True + + def commentState(self): + data = self.stream.char() + if data == "-": + self.state = self.commentEndDashState + elif data == "\u0000": + self.tokenQueue.append({"type": tokenTypes["ParseError"], + "data": "invalid-codepoint"}) + self.currentToken["data"] += "\uFFFD" + elif data is EOF: + self.tokenQueue.append({"type": tokenTypes["ParseError"], + "data": "eof-in-comment"}) + self.tokenQueue.append(self.currentToken) + self.state = self.dataState + else: + self.currentToken["data"] += data + \ + self.stream.charsUntil(("-", "\u0000")) + return True + + def commentEndDashState(self): + data = self.stream.char() + if data == "-": + self.state = self.commentEndState + elif data == "\u0000": + self.tokenQueue.append({"type": tokenTypes["ParseError"], + "data": "invalid-codepoint"}) + self.currentToken["data"] += "-\uFFFD" + self.state = self.commentState + elif data is EOF: + self.tokenQueue.append({"type": tokenTypes["ParseError"], "data": + "eof-in-comment-end-dash"}) + self.tokenQueue.append(self.currentToken) + self.state = self.dataState + else: + self.currentToken["data"] += "-" + data + self.state = self.commentState + return True + + def commentEndState(self): + data = self.stream.char() + if data == ">": + self.tokenQueue.append(self.currentToken) + self.state = self.dataState + elif data == "\u0000": + self.tokenQueue.append({"type": tokenTypes["ParseError"], + "data": "invalid-codepoint"}) + self.currentToken["data"] += "--\uFFFD" + self.state = self.commentState + elif data == "!": + self.tokenQueue.append({"type": tokenTypes["ParseError"], "data": + "unexpected-bang-after-double-dash-in-comment"}) + self.state = self.commentEndBangState + elif data == "-": + self.tokenQueue.append({"type": tokenTypes["ParseError"], "data": + "unexpected-dash-after-double-dash-in-comment"}) + self.currentToken["data"] += data + elif data is EOF: + self.tokenQueue.append({"type": tokenTypes["ParseError"], "data": + "eof-in-comment-double-dash"}) + self.tokenQueue.append(self.currentToken) + self.state = self.dataState + else: + # XXX + self.tokenQueue.append({"type": tokenTypes["ParseError"], "data": + "unexpected-char-in-comment"}) + self.currentToken["data"] += "--" + data + self.state = self.commentState + return True + + def commentEndBangState(self): + data = self.stream.char() + if data == ">": + self.tokenQueue.append(self.currentToken) + self.state = self.dataState + elif data == "-": + self.currentToken["data"] += "--!" + self.state = self.commentEndDashState + elif data == "\u0000": + self.tokenQueue.append({"type": tokenTypes["ParseError"], + "data": "invalid-codepoint"}) + self.currentToken["data"] += "--!\uFFFD" + self.state = self.commentState + elif data is EOF: + self.tokenQueue.append({"type": tokenTypes["ParseError"], "data": + "eof-in-comment-end-bang-state"}) + self.tokenQueue.append(self.currentToken) + self.state = self.dataState + else: + self.currentToken["data"] += "--!" + data + self.state = self.commentState + return True + + def doctypeState(self): + data = self.stream.char() + if data in spaceCharacters: + self.state = self.beforeDoctypeNameState + elif data is EOF: + self.tokenQueue.append({"type": tokenTypes["ParseError"], "data": + "expected-doctype-name-but-got-eof"}) + self.currentToken["correct"] = False + self.tokenQueue.append(self.currentToken) + self.state = self.dataState + else: + self.tokenQueue.append({"type": tokenTypes["ParseError"], "data": + "need-space-after-doctype"}) + self.stream.unget(data) + self.state = self.beforeDoctypeNameState + return True + + def beforeDoctypeNameState(self): + data = self.stream.char() + if data in spaceCharacters: + pass + elif data == ">": + self.tokenQueue.append({"type": tokenTypes["ParseError"], "data": + "expected-doctype-name-but-got-right-bracket"}) + self.currentToken["correct"] = False + self.tokenQueue.append(self.currentToken) + self.state = self.dataState + elif data == "\u0000": + self.tokenQueue.append({"type": tokenTypes["ParseError"], + "data": "invalid-codepoint"}) + self.currentToken["name"] = "\uFFFD" + self.state = self.doctypeNameState + elif data is EOF: + self.tokenQueue.append({"type": tokenTypes["ParseError"], "data": + "expected-doctype-name-but-got-eof"}) + self.currentToken["correct"] = False + self.tokenQueue.append(self.currentToken) + self.state = self.dataState + else: + self.currentToken["name"] = data + self.state = self.doctypeNameState + return True + + def doctypeNameState(self): + data = self.stream.char() + if data in spaceCharacters: + self.currentToken["name"] = self.currentToken["name"].translate(asciiUpper2Lower) + self.state = self.afterDoctypeNameState + elif data == ">": + self.currentToken["name"] = self.currentToken["name"].translate(asciiUpper2Lower) + self.tokenQueue.append(self.currentToken) + self.state = self.dataState + elif data == "\u0000": + self.tokenQueue.append({"type": tokenTypes["ParseError"], + "data": "invalid-codepoint"}) + self.currentToken["name"] += "\uFFFD" + self.state = self.doctypeNameState + elif data is EOF: + self.tokenQueue.append({"type": tokenTypes["ParseError"], "data": + "eof-in-doctype-name"}) + self.currentToken["correct"] = False + self.currentToken["name"] = self.currentToken["name"].translate(asciiUpper2Lower) + self.tokenQueue.append(self.currentToken) + self.state = self.dataState + else: + self.currentToken["name"] += data + return True + + def afterDoctypeNameState(self): + data = self.stream.char() + if data in spaceCharacters: + pass + elif data == ">": + self.tokenQueue.append(self.currentToken) + self.state = self.dataState + elif data is EOF: + self.currentToken["correct"] = False + self.stream.unget(data) + self.tokenQueue.append({"type": tokenTypes["ParseError"], "data": + "eof-in-doctype"}) + self.tokenQueue.append(self.currentToken) + self.state = self.dataState + else: + if data in ("p", "P"): + matched = True + for expected in (("u", "U"), ("b", "B"), ("l", "L"), + ("i", "I"), ("c", "C")): + data = self.stream.char() + if data not in expected: + matched = False + break + if matched: + self.state = self.afterDoctypePublicKeywordState + return True + elif data in ("s", "S"): + matched = True + for expected in (("y", "Y"), ("s", "S"), ("t", "T"), + ("e", "E"), ("m", "M")): + data = self.stream.char() + if data not in expected: + matched = False + break + if matched: + self.state = self.afterDoctypeSystemKeywordState + return True + + # All the characters read before the current 'data' will be + # [a-zA-Z], so they're garbage in the bogus doctype and can be + # discarded; only the latest character might be '>' or EOF + # and needs to be ungetted + self.stream.unget(data) + self.tokenQueue.append({"type": tokenTypes["ParseError"], "data": + "expected-space-or-right-bracket-in-doctype", "datavars": + {"data": data}}) + self.currentToken["correct"] = False + self.state = self.bogusDoctypeState + + return True + + def afterDoctypePublicKeywordState(self): + data = self.stream.char() + if data in spaceCharacters: + self.state = self.beforeDoctypePublicIdentifierState + elif data in ("'", '"'): + self.tokenQueue.append({"type": tokenTypes["ParseError"], "data": + "unexpected-char-in-doctype"}) + self.stream.unget(data) + self.state = self.beforeDoctypePublicIdentifierState + elif data is EOF: + self.tokenQueue.append({"type": tokenTypes["ParseError"], "data": + "eof-in-doctype"}) + self.currentToken["correct"] = False + self.tokenQueue.append(self.currentToken) + self.state = self.dataState + else: + self.stream.unget(data) + self.state = self.beforeDoctypePublicIdentifierState + return True + + def beforeDoctypePublicIdentifierState(self): + data = self.stream.char() + if data in spaceCharacters: + pass + elif data == "\"": + self.currentToken["publicId"] = "" + self.state = self.doctypePublicIdentifierDoubleQuotedState + elif data == "'": + self.currentToken["publicId"] = "" + self.state = self.doctypePublicIdentifierSingleQuotedState + elif data == ">": + self.tokenQueue.append({"type": tokenTypes["ParseError"], "data": + "unexpected-end-of-doctype"}) + self.currentToken["correct"] = False + self.tokenQueue.append(self.currentToken) + self.state = self.dataState + elif data is EOF: + self.tokenQueue.append({"type": tokenTypes["ParseError"], "data": + "eof-in-doctype"}) + self.currentToken["correct"] = False + self.tokenQueue.append(self.currentToken) + self.state = self.dataState + else: + self.tokenQueue.append({"type": tokenTypes["ParseError"], "data": + "unexpected-char-in-doctype"}) + self.currentToken["correct"] = False + self.state = self.bogusDoctypeState + return True + + def doctypePublicIdentifierDoubleQuotedState(self): + data = self.stream.char() + if data == "\"": + self.state = self.afterDoctypePublicIdentifierState + elif data == "\u0000": + self.tokenQueue.append({"type": tokenTypes["ParseError"], + "data": "invalid-codepoint"}) + self.currentToken["publicId"] += "\uFFFD" + elif data == ">": + self.tokenQueue.append({"type": tokenTypes["ParseError"], "data": + "unexpected-end-of-doctype"}) + self.currentToken["correct"] = False + self.tokenQueue.append(self.currentToken) + self.state = self.dataState + elif data is EOF: + self.tokenQueue.append({"type": tokenTypes["ParseError"], "data": + "eof-in-doctype"}) + self.currentToken["correct"] = False + self.tokenQueue.append(self.currentToken) + self.state = self.dataState + else: + self.currentToken["publicId"] += data + return True + + def doctypePublicIdentifierSingleQuotedState(self): + data = self.stream.char() + if data == "'": + self.state = self.afterDoctypePublicIdentifierState + elif data == "\u0000": + self.tokenQueue.append({"type": tokenTypes["ParseError"], + "data": "invalid-codepoint"}) + self.currentToken["publicId"] += "\uFFFD" + elif data == ">": + self.tokenQueue.append({"type": tokenTypes["ParseError"], "data": + "unexpected-end-of-doctype"}) + self.currentToken["correct"] = False + self.tokenQueue.append(self.currentToken) + self.state = self.dataState + elif data is EOF: + self.tokenQueue.append({"type": tokenTypes["ParseError"], "data": + "eof-in-doctype"}) + self.currentToken["correct"] = False + self.tokenQueue.append(self.currentToken) + self.state = self.dataState + else: + self.currentToken["publicId"] += data + return True + + def afterDoctypePublicIdentifierState(self): + data = self.stream.char() + if data in spaceCharacters: + self.state = self.betweenDoctypePublicAndSystemIdentifiersState + elif data == ">": + self.tokenQueue.append(self.currentToken) + self.state = self.dataState + elif data == '"': + self.tokenQueue.append({"type": tokenTypes["ParseError"], "data": + "unexpected-char-in-doctype"}) + self.currentToken["systemId"] = "" + self.state = self.doctypeSystemIdentifierDoubleQuotedState + elif data == "'": + self.tokenQueue.append({"type": tokenTypes["ParseError"], "data": + "unexpected-char-in-doctype"}) + self.currentToken["systemId"] = "" + self.state = self.doctypeSystemIdentifierSingleQuotedState + elif data is EOF: + self.tokenQueue.append({"type": tokenTypes["ParseError"], "data": + "eof-in-doctype"}) + self.currentToken["correct"] = False + self.tokenQueue.append(self.currentToken) + self.state = self.dataState + else: + self.tokenQueue.append({"type": tokenTypes["ParseError"], "data": + "unexpected-char-in-doctype"}) + self.currentToken["correct"] = False + self.state = self.bogusDoctypeState + return True + + def betweenDoctypePublicAndSystemIdentifiersState(self): + data = self.stream.char() + if data in spaceCharacters: + pass + elif data == ">": + self.tokenQueue.append(self.currentToken) + self.state = self.dataState + elif data == '"': + self.currentToken["systemId"] = "" + self.state = self.doctypeSystemIdentifierDoubleQuotedState + elif data == "'": + self.currentToken["systemId"] = "" + self.state = self.doctypeSystemIdentifierSingleQuotedState + elif data == EOF: + self.tokenQueue.append({"type": tokenTypes["ParseError"], "data": + "eof-in-doctype"}) + self.currentToken["correct"] = False + self.tokenQueue.append(self.currentToken) + self.state = self.dataState + else: + self.tokenQueue.append({"type": tokenTypes["ParseError"], "data": + "unexpected-char-in-doctype"}) + self.currentToken["correct"] = False + self.state = self.bogusDoctypeState + return True + + def afterDoctypeSystemKeywordState(self): + data = self.stream.char() + if data in spaceCharacters: + self.state = self.beforeDoctypeSystemIdentifierState + elif data in ("'", '"'): + self.tokenQueue.append({"type": tokenTypes["ParseError"], "data": + "unexpected-char-in-doctype"}) + self.stream.unget(data) + self.state = self.beforeDoctypeSystemIdentifierState + elif data is EOF: + self.tokenQueue.append({"type": tokenTypes["ParseError"], "data": + "eof-in-doctype"}) + self.currentToken["correct"] = False + self.tokenQueue.append(self.currentToken) + self.state = self.dataState + else: + self.stream.unget(data) + self.state = self.beforeDoctypeSystemIdentifierState + return True + + def beforeDoctypeSystemIdentifierState(self): + data = self.stream.char() + if data in spaceCharacters: + pass + elif data == "\"": + self.currentToken["systemId"] = "" + self.state = self.doctypeSystemIdentifierDoubleQuotedState + elif data == "'": + self.currentToken["systemId"] = "" + self.state = self.doctypeSystemIdentifierSingleQuotedState + elif data == ">": + self.tokenQueue.append({"type": tokenTypes["ParseError"], "data": + "unexpected-char-in-doctype"}) + self.currentToken["correct"] = False + self.tokenQueue.append(self.currentToken) + self.state = self.dataState + elif data is EOF: + self.tokenQueue.append({"type": tokenTypes["ParseError"], "data": + "eof-in-doctype"}) + self.currentToken["correct"] = False + self.tokenQueue.append(self.currentToken) + self.state = self.dataState + else: + self.tokenQueue.append({"type": tokenTypes["ParseError"], "data": + "unexpected-char-in-doctype"}) + self.currentToken["correct"] = False + self.state = self.bogusDoctypeState + return True + + def doctypeSystemIdentifierDoubleQuotedState(self): + data = self.stream.char() + if data == "\"": + self.state = self.afterDoctypeSystemIdentifierState + elif data == "\u0000": + self.tokenQueue.append({"type": tokenTypes["ParseError"], + "data": "invalid-codepoint"}) + self.currentToken["systemId"] += "\uFFFD" + elif data == ">": + self.tokenQueue.append({"type": tokenTypes["ParseError"], "data": + "unexpected-end-of-doctype"}) + self.currentToken["correct"] = False + self.tokenQueue.append(self.currentToken) + self.state = self.dataState + elif data is EOF: + self.tokenQueue.append({"type": tokenTypes["ParseError"], "data": + "eof-in-doctype"}) + self.currentToken["correct"] = False + self.tokenQueue.append(self.currentToken) + self.state = self.dataState + else: + self.currentToken["systemId"] += data + return True + + def doctypeSystemIdentifierSingleQuotedState(self): + data = self.stream.char() + if data == "'": + self.state = self.afterDoctypeSystemIdentifierState + elif data == "\u0000": + self.tokenQueue.append({"type": tokenTypes["ParseError"], + "data": "invalid-codepoint"}) + self.currentToken["systemId"] += "\uFFFD" + elif data == ">": + self.tokenQueue.append({"type": tokenTypes["ParseError"], "data": + "unexpected-end-of-doctype"}) + self.currentToken["correct"] = False + self.tokenQueue.append(self.currentToken) + self.state = self.dataState + elif data is EOF: + self.tokenQueue.append({"type": tokenTypes["ParseError"], "data": + "eof-in-doctype"}) + self.currentToken["correct"] = False + self.tokenQueue.append(self.currentToken) + self.state = self.dataState + else: + self.currentToken["systemId"] += data + return True + + def afterDoctypeSystemIdentifierState(self): + data = self.stream.char() + if data in spaceCharacters: + pass + elif data == ">": + self.tokenQueue.append(self.currentToken) + self.state = self.dataState + elif data is EOF: + self.tokenQueue.append({"type": tokenTypes["ParseError"], "data": + "eof-in-doctype"}) + self.currentToken["correct"] = False + self.tokenQueue.append(self.currentToken) + self.state = self.dataState + else: + self.tokenQueue.append({"type": tokenTypes["ParseError"], "data": + "unexpected-char-in-doctype"}) + self.state = self.bogusDoctypeState + return True + + def bogusDoctypeState(self): + data = self.stream.char() + if data == ">": + self.tokenQueue.append(self.currentToken) + self.state = self.dataState + elif data is EOF: + # XXX EMIT + self.stream.unget(data) + self.tokenQueue.append(self.currentToken) + self.state = self.dataState + else: + pass + return True + + def cdataSectionState(self): + data = [] + while True: + data.append(self.stream.charsUntil("]")) + data.append(self.stream.charsUntil(">")) + char = self.stream.char() + if char == EOF: + break + else: + assert char == ">" + if data[-1][-2:] == "]]": + data[-1] = data[-1][:-2] + break + else: + data.append(char) + + data = "".join(data) # pylint:disable=redefined-variable-type + # Deal with null here rather than in the parser + nullCount = data.count("\u0000") + if nullCount > 0: + for _ in range(nullCount): + self.tokenQueue.append({"type": tokenTypes["ParseError"], + "data": "invalid-codepoint"}) + data = data.replace("\u0000", "\uFFFD") + if data: + self.tokenQueue.append({"type": tokenTypes["Characters"], + "data": data}) + self.state = self.dataState + return True diff --git a/venv2/lib/python2.7/site-packages/pip/_vendor/html5lib/_trie/__init__.py b/venv2/lib/python2.7/site-packages/pip/_vendor/html5lib/_trie/__init__.py new file mode 100644 index 0000000..a5ba4bf --- /dev/null +++ b/venv2/lib/python2.7/site-packages/pip/_vendor/html5lib/_trie/__init__.py @@ -0,0 +1,14 @@ +from __future__ import absolute_import, division, unicode_literals + +from .py import Trie as PyTrie + +Trie = PyTrie + +# pylint:disable=wrong-import-position +try: + from .datrie import Trie as DATrie +except ImportError: + pass +else: + Trie = DATrie +# pylint:enable=wrong-import-position diff --git a/venv2/lib/python2.7/site-packages/pip/_vendor/html5lib/_trie/_base.py b/venv2/lib/python2.7/site-packages/pip/_vendor/html5lib/_trie/_base.py new file mode 100644 index 0000000..a1158bb --- /dev/null +++ b/venv2/lib/python2.7/site-packages/pip/_vendor/html5lib/_trie/_base.py @@ -0,0 +1,37 @@ +from __future__ import absolute_import, division, unicode_literals + +from collections import Mapping + + +class Trie(Mapping): + """Abstract base class for tries""" + + def keys(self, prefix=None): + # pylint:disable=arguments-differ + keys = super(Trie, self).keys() + + if prefix is None: + return set(keys) + + return {x for x in keys if x.startswith(prefix)} + + def has_keys_with_prefix(self, prefix): + for key in self.keys(): + if key.startswith(prefix): + return True + + return False + + def longest_prefix(self, prefix): + if prefix in self: + return prefix + + for i in range(1, len(prefix) + 1): + if prefix[:-i] in self: + return prefix[:-i] + + raise KeyError(prefix) + + def longest_prefix_item(self, prefix): + lprefix = self.longest_prefix(prefix) + return (lprefix, self[lprefix]) diff --git a/venv2/lib/python2.7/site-packages/pip/_vendor/html5lib/_trie/datrie.py b/venv2/lib/python2.7/site-packages/pip/_vendor/html5lib/_trie/datrie.py new file mode 100644 index 0000000..e2e5f86 --- /dev/null +++ b/venv2/lib/python2.7/site-packages/pip/_vendor/html5lib/_trie/datrie.py @@ -0,0 +1,44 @@ +from __future__ import absolute_import, division, unicode_literals + +from datrie import Trie as DATrie +from pip._vendor.six import text_type + +from ._base import Trie as ABCTrie + + +class Trie(ABCTrie): + def __init__(self, data): + chars = set() + for key in data.keys(): + if not isinstance(key, text_type): + raise TypeError("All keys must be strings") + for char in key: + chars.add(char) + + self._data = DATrie("".join(chars)) + for key, value in data.items(): + self._data[key] = value + + def __contains__(self, key): + return key in self._data + + def __len__(self): + return len(self._data) + + def __iter__(self): + raise NotImplementedError() + + def __getitem__(self, key): + return self._data[key] + + def keys(self, prefix=None): + return self._data.keys(prefix) + + def has_keys_with_prefix(self, prefix): + return self._data.has_keys_with_prefix(prefix) + + def longest_prefix(self, prefix): + return self._data.longest_prefix(prefix) + + def longest_prefix_item(self, prefix): + return self._data.longest_prefix_item(prefix) diff --git a/venv2/lib/python2.7/site-packages/pip/_vendor/html5lib/_trie/py.py b/venv2/lib/python2.7/site-packages/pip/_vendor/html5lib/_trie/py.py new file mode 100644 index 0000000..c178b21 --- /dev/null +++ b/venv2/lib/python2.7/site-packages/pip/_vendor/html5lib/_trie/py.py @@ -0,0 +1,67 @@ +from __future__ import absolute_import, division, unicode_literals +from pip._vendor.six import text_type + +from bisect import bisect_left + +from ._base import Trie as ABCTrie + + +class Trie(ABCTrie): + def __init__(self, data): + if not all(isinstance(x, text_type) for x in data.keys()): + raise TypeError("All keys must be strings") + + self._data = data + self._keys = sorted(data.keys()) + self._cachestr = "" + self._cachepoints = (0, len(data)) + + def __contains__(self, key): + return key in self._data + + def __len__(self): + return len(self._data) + + def __iter__(self): + return iter(self._data) + + def __getitem__(self, key): + return self._data[key] + + def keys(self, prefix=None): + if prefix is None or prefix == "" or not self._keys: + return set(self._keys) + + if prefix.startswith(self._cachestr): + lo, hi = self._cachepoints + start = i = bisect_left(self._keys, prefix, lo, hi) + else: + start = i = bisect_left(self._keys, prefix) + + keys = set() + if start == len(self._keys): + return keys + + while self._keys[i].startswith(prefix): + keys.add(self._keys[i]) + i += 1 + + self._cachestr = prefix + self._cachepoints = (start, i) + + return keys + + def has_keys_with_prefix(self, prefix): + if prefix in self._data: + return True + + if prefix.startswith(self._cachestr): + lo, hi = self._cachepoints + i = bisect_left(self._keys, prefix, lo, hi) + else: + i = bisect_left(self._keys, prefix) + + if i == len(self._keys): + return False + + return self._keys[i].startswith(prefix) diff --git a/venv2/lib/python2.7/site-packages/pip/_vendor/html5lib/_utils.py b/venv2/lib/python2.7/site-packages/pip/_vendor/html5lib/_utils.py new file mode 100644 index 0000000..0703afb --- /dev/null +++ b/venv2/lib/python2.7/site-packages/pip/_vendor/html5lib/_utils.py @@ -0,0 +1,124 @@ +from __future__ import absolute_import, division, unicode_literals + +from types import ModuleType + +from pip._vendor.six import text_type + +try: + import xml.etree.cElementTree as default_etree +except ImportError: + import xml.etree.ElementTree as default_etree + + +__all__ = ["default_etree", "MethodDispatcher", "isSurrogatePair", + "surrogatePairToCodepoint", "moduleFactoryFactory", + "supports_lone_surrogates"] + + +# Platforms not supporting lone surrogates (\uD800-\uDFFF) should be +# caught by the below test. In general this would be any platform +# using UTF-16 as its encoding of unicode strings, such as +# Jython. This is because UTF-16 itself is based on the use of such +# surrogates, and there is no mechanism to further escape such +# escapes. +try: + _x = eval('"\\uD800"') # pylint:disable=eval-used + if not isinstance(_x, text_type): + # We need this with u"" because of http://bugs.jython.org/issue2039 + _x = eval('u"\\uD800"') # pylint:disable=eval-used + assert isinstance(_x, text_type) +except: # pylint:disable=bare-except + supports_lone_surrogates = False +else: + supports_lone_surrogates = True + + +class MethodDispatcher(dict): + """Dict with 2 special properties: + + On initiation, keys that are lists, sets or tuples are converted to + multiple keys so accessing any one of the items in the original + list-like object returns the matching value + + md = MethodDispatcher({("foo", "bar"):"baz"}) + md["foo"] == "baz" + + A default value which can be set through the default attribute. + """ + + def __init__(self, items=()): + # Using _dictEntries instead of directly assigning to self is about + # twice as fast. Please do careful performance testing before changing + # anything here. + _dictEntries = [] + for name, value in items: + if isinstance(name, (list, tuple, frozenset, set)): + for item in name: + _dictEntries.append((item, value)) + else: + _dictEntries.append((name, value)) + dict.__init__(self, _dictEntries) + assert len(self) == len(_dictEntries) + self.default = None + + def __getitem__(self, key): + return dict.get(self, key, self.default) + + +# Some utility functions to deal with weirdness around UCS2 vs UCS4 +# python builds + +def isSurrogatePair(data): + return (len(data) == 2 and + ord(data[0]) >= 0xD800 and ord(data[0]) <= 0xDBFF and + ord(data[1]) >= 0xDC00 and ord(data[1]) <= 0xDFFF) + + +def surrogatePairToCodepoint(data): + char_val = (0x10000 + (ord(data[0]) - 0xD800) * 0x400 + + (ord(data[1]) - 0xDC00)) + return char_val + +# Module Factory Factory (no, this isn't Java, I know) +# Here to stop this being duplicated all over the place. + + +def moduleFactoryFactory(factory): + moduleCache = {} + + def moduleFactory(baseModule, *args, **kwargs): + if isinstance(ModuleType.__name__, type("")): + name = "_%s_factory" % baseModule.__name__ + else: + name = b"_%s_factory" % baseModule.__name__ + + kwargs_tuple = tuple(kwargs.items()) + + try: + return moduleCache[name][args][kwargs_tuple] + except KeyError: + mod = ModuleType(name) + objs = factory(baseModule, *args, **kwargs) + mod.__dict__.update(objs) + if "name" not in moduleCache: + moduleCache[name] = {} + if "args" not in moduleCache[name]: + moduleCache[name][args] = {} + if "kwargs" not in moduleCache[name][args]: + moduleCache[name][args][kwargs_tuple] = {} + moduleCache[name][args][kwargs_tuple] = mod + return mod + + return moduleFactory + + +def memoize(func): + cache = {} + + def wrapped(*args, **kwargs): + key = (tuple(args), tuple(kwargs.items())) + if key not in cache: + cache[key] = func(*args, **kwargs) + return cache[key] + + return wrapped diff --git a/venv2/lib/python2.7/site-packages/pip/_vendor/html5lib/constants.py b/venv2/lib/python2.7/site-packages/pip/_vendor/html5lib/constants.py new file mode 100644 index 0000000..1ff8041 --- /dev/null +++ b/venv2/lib/python2.7/site-packages/pip/_vendor/html5lib/constants.py @@ -0,0 +1,2947 @@ +from __future__ import absolute_import, division, unicode_literals + +import string + +EOF = None + +E = { + "null-character": + "Null character in input stream, replaced with U+FFFD.", + "invalid-codepoint": + "Invalid codepoint in stream.", + "incorrectly-placed-solidus": + "Solidus (/) incorrectly placed in tag.", + "incorrect-cr-newline-entity": + "Incorrect CR newline entity, replaced with LF.", + "illegal-windows-1252-entity": + "Entity used with illegal number (windows-1252 reference).", + "cant-convert-numeric-entity": + "Numeric entity couldn't be converted to character " + "(codepoint U+%(charAsInt)08x).", + "illegal-codepoint-for-numeric-entity": + "Numeric entity represents an illegal codepoint: " + "U+%(charAsInt)08x.", + "numeric-entity-without-semicolon": + "Numeric entity didn't end with ';'.", + "expected-numeric-entity-but-got-eof": + "Numeric entity expected. Got end of file instead.", + "expected-numeric-entity": + "Numeric entity expected but none found.", + "named-entity-without-semicolon": + "Named entity didn't end with ';'.", + "expected-named-entity": + "Named entity expected. Got none.", + "attributes-in-end-tag": + "End tag contains unexpected attributes.", + 'self-closing-flag-on-end-tag': + "End tag contains unexpected self-closing flag.", + "expected-tag-name-but-got-right-bracket": + "Expected tag name. Got '>' instead.", + "expected-tag-name-but-got-question-mark": + "Expected tag name. Got '?' instead. (HTML doesn't " + "support processing instructions.)", + "expected-tag-name": + "Expected tag name. Got something else instead", + "expected-closing-tag-but-got-right-bracket": + "Expected closing tag. Got '>' instead. Ignoring '</>'.", + "expected-closing-tag-but-got-eof": + "Expected closing tag. Unexpected end of file.", + "expected-closing-tag-but-got-char": + "Expected closing tag. Unexpected character '%(data)s' found.", + "eof-in-tag-name": + "Unexpected end of file in the tag name.", + "expected-attribute-name-but-got-eof": + "Unexpected end of file. Expected attribute name instead.", + "eof-in-attribute-name": + "Unexpected end of file in attribute name.", + "invalid-character-in-attribute-name": + "Invalid character in attribute name", + "duplicate-attribute": + "Dropped duplicate attribute on tag.", + "expected-end-of-tag-name-but-got-eof": + "Unexpected end of file. Expected = or end of tag.", + "expected-attribute-value-but-got-eof": + "Unexpected end of file. Expected attribute value.", + "expected-attribute-value-but-got-right-bracket": + "Expected attribute value. Got '>' instead.", + 'equals-in-unquoted-attribute-value': + "Unexpected = in unquoted attribute", + 'unexpected-character-in-unquoted-attribute-value': + "Unexpected character in unquoted attribute", + "invalid-character-after-attribute-name": + "Unexpected character after attribute name.", + "unexpected-character-after-attribute-value": + "Unexpected character after attribute value.", + "eof-in-attribute-value-double-quote": + "Unexpected end of file in attribute value (\").", + "eof-in-attribute-value-single-quote": + "Unexpected end of file in attribute value (').", + "eof-in-attribute-value-no-quotes": + "Unexpected end of file in attribute value.", + "unexpected-EOF-after-solidus-in-tag": + "Unexpected end of file in tag. Expected >", + "unexpected-character-after-solidus-in-tag": + "Unexpected character after / in tag. Expected >", + "expected-dashes-or-doctype": + "Expected '--' or 'DOCTYPE'. Not found.", + "unexpected-bang-after-double-dash-in-comment": + "Unexpected ! after -- in comment", + "unexpected-space-after-double-dash-in-comment": + "Unexpected space after -- in comment", + "incorrect-comment": + "Incorrect comment.", + "eof-in-comment": + "Unexpected end of file in comment.", + "eof-in-comment-end-dash": + "Unexpected end of file in comment (-)", + "unexpected-dash-after-double-dash-in-comment": + "Unexpected '-' after '--' found in comment.", + "eof-in-comment-double-dash": + "Unexpected end of file in comment (--).", + "eof-in-comment-end-space-state": + "Unexpected end of file in comment.", + "eof-in-comment-end-bang-state": + "Unexpected end of file in comment.", + "unexpected-char-in-comment": + "Unexpected character in comment found.", + "need-space-after-doctype": + "No space after literal string 'DOCTYPE'.", + "expected-doctype-name-but-got-right-bracket": + "Unexpected > character. Expected DOCTYPE name.", + "expected-doctype-name-but-got-eof": + "Unexpected end of file. Expected DOCTYPE name.", + "eof-in-doctype-name": + "Unexpected end of file in DOCTYPE name.", + "eof-in-doctype": + "Unexpected end of file in DOCTYPE.", + "expected-space-or-right-bracket-in-doctype": + "Expected space or '>'. Got '%(data)s'", + "unexpected-end-of-doctype": + "Unexpected end of DOCTYPE.", + "unexpected-char-in-doctype": + "Unexpected character in DOCTYPE.", + "eof-in-innerhtml": + "XXX innerHTML EOF", + "unexpected-doctype": + "Unexpected DOCTYPE. Ignored.", + "non-html-root": + "html needs to be the first start tag.", + "expected-doctype-but-got-eof": + "Unexpected End of file. Expected DOCTYPE.", + "unknown-doctype": + "Erroneous DOCTYPE.", + "expected-doctype-but-got-chars": + "Unexpected non-space characters. Expected DOCTYPE.", + "expected-doctype-but-got-start-tag": + "Unexpected start tag (%(name)s). Expected DOCTYPE.", + "expected-doctype-but-got-end-tag": + "Unexpected end tag (%(name)s). Expected DOCTYPE.", + "end-tag-after-implied-root": + "Unexpected end tag (%(name)s) after the (implied) root element.", + "expected-named-closing-tag-but-got-eof": + "Unexpected end of file. Expected end tag (%(name)s).", + "two-heads-are-not-better-than-one": + "Unexpected start tag head in existing head. Ignored.", + "unexpected-end-tag": + "Unexpected end tag (%(name)s). Ignored.", + "unexpected-start-tag-out-of-my-head": + "Unexpected start tag (%(name)s) that can be in head. Moved.", + "unexpected-start-tag": + "Unexpected start tag (%(name)s).", + "missing-end-tag": + "Missing end tag (%(name)s).", + "missing-end-tags": + "Missing end tags (%(name)s).", + "unexpected-start-tag-implies-end-tag": + "Unexpected start tag (%(startName)s) " + "implies end tag (%(endName)s).", + "unexpected-start-tag-treated-as": + "Unexpected start tag (%(originalName)s). Treated as %(newName)s.", + "deprecated-tag": + "Unexpected start tag %(name)s. Don't use it!", + "unexpected-start-tag-ignored": + "Unexpected start tag %(name)s. Ignored.", + "expected-one-end-tag-but-got-another": + "Unexpected end tag (%(gotName)s). " + "Missing end tag (%(expectedName)s).", + "end-tag-too-early": + "End tag (%(name)s) seen too early. Expected other end tag.", + "end-tag-too-early-named": + "Unexpected end tag (%(gotName)s). Expected end tag (%(expectedName)s).", + "end-tag-too-early-ignored": + "End tag (%(name)s) seen too early. Ignored.", + "adoption-agency-1.1": + "End tag (%(name)s) violates step 1, " + "paragraph 1 of the adoption agency algorithm.", + "adoption-agency-1.2": + "End tag (%(name)s) violates step 1, " + "paragraph 2 of the adoption agency algorithm.", + "adoption-agency-1.3": + "End tag (%(name)s) violates step 1, " + "paragraph 3 of the adoption agency algorithm.", + "adoption-agency-4.4": + "End tag (%(name)s) violates step 4, " + "paragraph 4 of the adoption agency algorithm.", + "unexpected-end-tag-treated-as": + "Unexpected end tag (%(originalName)s). Treated as %(newName)s.", + "no-end-tag": + "This element (%(name)s) has no end tag.", + "unexpected-implied-end-tag-in-table": + "Unexpected implied end tag (%(name)s) in the table phase.", + "unexpected-implied-end-tag-in-table-body": + "Unexpected implied end tag (%(name)s) in the table body phase.", + "unexpected-char-implies-table-voodoo": + "Unexpected non-space characters in " + "table context caused voodoo mode.", + "unexpected-hidden-input-in-table": + "Unexpected input with type hidden in table context.", + "unexpected-form-in-table": + "Unexpected form in table context.", + "unexpected-start-tag-implies-table-voodoo": + "Unexpected start tag (%(name)s) in " + "table context caused voodoo mode.", + "unexpected-end-tag-implies-table-voodoo": + "Unexpected end tag (%(name)s) in " + "table context caused voodoo mode.", + "unexpected-cell-in-table-body": + "Unexpected table cell start tag (%(name)s) " + "in the table body phase.", + "unexpected-cell-end-tag": + "Got table cell end tag (%(name)s) " + "while required end tags are missing.", + "unexpected-end-tag-in-table-body": + "Unexpected end tag (%(name)s) in the table body phase. Ignored.", + "unexpected-implied-end-tag-in-table-row": + "Unexpected implied end tag (%(name)s) in the table row phase.", + "unexpected-end-tag-in-table-row": + "Unexpected end tag (%(name)s) in the table row phase. Ignored.", + "unexpected-select-in-select": + "Unexpected select start tag in the select phase " + "treated as select end tag.", + "unexpected-input-in-select": + "Unexpected input start tag in the select phase.", + "unexpected-start-tag-in-select": + "Unexpected start tag token (%(name)s in the select phase. " + "Ignored.", + "unexpected-end-tag-in-select": + "Unexpected end tag (%(name)s) in the select phase. Ignored.", + "unexpected-table-element-start-tag-in-select-in-table": + "Unexpected table element start tag (%(name)s) in the select in table phase.", + "unexpected-table-element-end-tag-in-select-in-table": + "Unexpected table element end tag (%(name)s) in the select in table phase.", + "unexpected-char-after-body": + "Unexpected non-space characters in the after body phase.", + "unexpected-start-tag-after-body": + "Unexpected start tag token (%(name)s)" + " in the after body phase.", + "unexpected-end-tag-after-body": + "Unexpected end tag token (%(name)s)" + " in the after body phase.", + "unexpected-char-in-frameset": + "Unexpected characters in the frameset phase. Characters ignored.", + "unexpected-start-tag-in-frameset": + "Unexpected start tag token (%(name)s)" + " in the frameset phase. Ignored.", + "unexpected-frameset-in-frameset-innerhtml": + "Unexpected end tag token (frameset) " + "in the frameset phase (innerHTML).", + "unexpected-end-tag-in-frameset": + "Unexpected end tag token (%(name)s)" + " in the frameset phase. Ignored.", + "unexpected-char-after-frameset": + "Unexpected non-space characters in the " + "after frameset phase. Ignored.", + "unexpected-start-tag-after-frameset": + "Unexpected start tag (%(name)s)" + " in the after frameset phase. Ignored.", + "unexpected-end-tag-after-frameset": + "Unexpected end tag (%(name)s)" + " in the after frameset phase. Ignored.", + "unexpected-end-tag-after-body-innerhtml": + "Unexpected end tag after body(innerHtml)", + "expected-eof-but-got-char": + "Unexpected non-space characters. Expected end of file.", + "expected-eof-but-got-start-tag": + "Unexpected start tag (%(name)s)" + ". Expected end of file.", + "expected-eof-but-got-end-tag": + "Unexpected end tag (%(name)s)" + ". Expected end of file.", + "eof-in-table": + "Unexpected end of file. Expected table content.", + "eof-in-select": + "Unexpected end of file. Expected select content.", + "eof-in-frameset": + "Unexpected end of file. Expected frameset content.", + "eof-in-script-in-script": + "Unexpected end of file. Expected script content.", + "eof-in-foreign-lands": + "Unexpected end of file. Expected foreign content", + "non-void-element-with-trailing-solidus": + "Trailing solidus not allowed on element %(name)s", + "unexpected-html-element-in-foreign-content": + "Element %(name)s not allowed in a non-html context", + "unexpected-end-tag-before-html": + "Unexpected end tag (%(name)s) before html.", + "unexpected-inhead-noscript-tag": + "Element %(name)s not allowed in a inhead-noscript context", + "eof-in-head-noscript": + "Unexpected end of file. Expected inhead-noscript content", + "char-in-head-noscript": + "Unexpected non-space character. Expected inhead-noscript content", + "XXX-undefined-error": + "Undefined error (this sucks and should be fixed)", +} + +namespaces = { + "html": "http://www.w3.org/1999/xhtml", + "mathml": "http://www.w3.org/1998/Math/MathML", + "svg": "http://www.w3.org/2000/svg", + "xlink": "http://www.w3.org/1999/xlink", + "xml": "http://www.w3.org/XML/1998/namespace", + "xmlns": "http://www.w3.org/2000/xmlns/" +} + +scopingElements = frozenset([ + (namespaces["html"], "applet"), + (namespaces["html"], "caption"), + (namespaces["html"], "html"), + (namespaces["html"], "marquee"), + (namespaces["html"], "object"), + (namespaces["html"], "table"), + (namespaces["html"], "td"), + (namespaces["html"], "th"), + (namespaces["mathml"], "mi"), + (namespaces["mathml"], "mo"), + (namespaces["mathml"], "mn"), + (namespaces["mathml"], "ms"), + (namespaces["mathml"], "mtext"), + (namespaces["mathml"], "annotation-xml"), + (namespaces["svg"], "foreignObject"), + (namespaces["svg"], "desc"), + (namespaces["svg"], "title"), +]) + +formattingElements = frozenset([ + (namespaces["html"], "a"), + (namespaces["html"], "b"), + (namespaces["html"], "big"), + (namespaces["html"], "code"), + (namespaces["html"], "em"), + (namespaces["html"], "font"), + (namespaces["html"], "i"), + (namespaces["html"], "nobr"), + (namespaces["html"], "s"), + (namespaces["html"], "small"), + (namespaces["html"], "strike"), + (namespaces["html"], "strong"), + (namespaces["html"], "tt"), + (namespaces["html"], "u") +]) + +specialElements = frozenset([ + (namespaces["html"], "address"), + (namespaces["html"], "applet"), + (namespaces["html"], "area"), + (namespaces["html"], "article"), + (namespaces["html"], "aside"), + (namespaces["html"], "base"), + (namespaces["html"], "basefont"), + (namespaces["html"], "bgsound"), + (namespaces["html"], "blockquote"), + (namespaces["html"], "body"), + (namespaces["html"], "br"), + (namespaces["html"], "button"), + (namespaces["html"], "caption"), + (namespaces["html"], "center"), + (namespaces["html"], "col"), + (namespaces["html"], "colgroup"), + (namespaces["html"], "command"), + (namespaces["html"], "dd"), + (namespaces["html"], "details"), + (namespaces["html"], "dir"), + (namespaces["html"], "div"), + (namespaces["html"], "dl"), + (namespaces["html"], "dt"), + (namespaces["html"], "embed"), + (namespaces["html"], "fieldset"), + (namespaces["html"], "figure"), + (namespaces["html"], "footer"), + (namespaces["html"], "form"), + (namespaces["html"], "frame"), + (namespaces["html"], "frameset"), + (namespaces["html"], "h1"), + (namespaces["html"], "h2"), + (namespaces["html"], "h3"), + (namespaces["html"], "h4"), + (namespaces["html"], "h5"), + (namespaces["html"], "h6"), + (namespaces["html"], "head"), + (namespaces["html"], "header"), + (namespaces["html"], "hr"), + (namespaces["html"], "html"), + (namespaces["html"], "iframe"), + # Note that image is commented out in the spec as "this isn't an + # element that can end up on the stack, so it doesn't matter," + (namespaces["html"], "image"), + (namespaces["html"], "img"), + (namespaces["html"], "input"), + (namespaces["html"], "isindex"), + (namespaces["html"], "li"), + (namespaces["html"], "link"), + (namespaces["html"], "listing"), + (namespaces["html"], "marquee"), + (namespaces["html"], "menu"), + (namespaces["html"], "meta"), + (namespaces["html"], "nav"), + (namespaces["html"], "noembed"), + (namespaces["html"], "noframes"), + (namespaces["html"], "noscript"), + (namespaces["html"], "object"), + (namespaces["html"], "ol"), + (namespaces["html"], "p"), + (namespaces["html"], "param"), + (namespaces["html"], "plaintext"), + (namespaces["html"], "pre"), + (namespaces["html"], "script"), + (namespaces["html"], "section"), + (namespaces["html"], "select"), + (namespaces["html"], "style"), + (namespaces["html"], "table"), + (namespaces["html"], "tbody"), + (namespaces["html"], "td"), + (namespaces["html"], "textarea"), + (namespaces["html"], "tfoot"), + (namespaces["html"], "th"), + (namespaces["html"], "thead"), + (namespaces["html"], "title"), + (namespaces["html"], "tr"), + (namespaces["html"], "ul"), + (namespaces["html"], "wbr"), + (namespaces["html"], "xmp"), + (namespaces["svg"], "foreignObject") +]) + +htmlIntegrationPointElements = frozenset([ + (namespaces["mathml"], "annotation-xml"), + (namespaces["svg"], "foreignObject"), + (namespaces["svg"], "desc"), + (namespaces["svg"], "title") +]) + +mathmlTextIntegrationPointElements = frozenset([ + (namespaces["mathml"], "mi"), + (namespaces["mathml"], "mo"), + (namespaces["mathml"], "mn"), + (namespaces["mathml"], "ms"), + (namespaces["mathml"], "mtext") +]) + +adjustSVGAttributes = { + "attributename": "attributeName", + "attributetype": "attributeType", + "basefrequency": "baseFrequency", + "baseprofile": "baseProfile", + "calcmode": "calcMode", + "clippathunits": "clipPathUnits", + "contentscripttype": "contentScriptType", + "contentstyletype": "contentStyleType", + "diffuseconstant": "diffuseConstant", + "edgemode": "edgeMode", + "externalresourcesrequired": "externalResourcesRequired", + "filterres": "filterRes", + "filterunits": "filterUnits", + "glyphref": "glyphRef", + "gradienttransform": "gradientTransform", + "gradientunits": "gradientUnits", + "kernelmatrix": "kernelMatrix", + "kernelunitlength": "kernelUnitLength", + "keypoints": "keyPoints", + "keysplines": "keySplines", + "keytimes": "keyTimes", + "lengthadjust": "lengthAdjust", + "limitingconeangle": "limitingConeAngle", + "markerheight": "markerHeight", + "markerunits": "markerUnits", + "markerwidth": "markerWidth", + "maskcontentunits": "maskContentUnits", + "maskunits": "maskUnits", + "numoctaves": "numOctaves", + "pathlength": "pathLength", + "patterncontentunits": "patternContentUnits", + "patterntransform": "patternTransform", + "patternunits": "patternUnits", + "pointsatx": "pointsAtX", + "pointsaty": "pointsAtY", + "pointsatz": "pointsAtZ", + "preservealpha": "preserveAlpha", + "preserveaspectratio": "preserveAspectRatio", + "primitiveunits": "primitiveUnits", + "refx": "refX", + "refy": "refY", + "repeatcount": "repeatCount", + "repeatdur": "repeatDur", + "requiredextensions": "requiredExtensions", + "requiredfeatures": "requiredFeatures", + "specularconstant": "specularConstant", + "specularexponent": "specularExponent", + "spreadmethod": "spreadMethod", + "startoffset": "startOffset", + "stddeviation": "stdDeviation", + "stitchtiles": "stitchTiles", + "surfacescale": "surfaceScale", + "systemlanguage": "systemLanguage", + "tablevalues": "tableValues", + "targetx": "targetX", + "targety": "targetY", + "textlength": "textLength", + "viewbox": "viewBox", + "viewtarget": "viewTarget", + "xchannelselector": "xChannelSelector", + "ychannelselector": "yChannelSelector", + "zoomandpan": "zoomAndPan" +} + +adjustMathMLAttributes = {"definitionurl": "definitionURL"} + +adjustForeignAttributes = { + "xlink:actuate": ("xlink", "actuate", namespaces["xlink"]), + "xlink:arcrole": ("xlink", "arcrole", namespaces["xlink"]), + "xlink:href": ("xlink", "href", namespaces["xlink"]), + "xlink:role": ("xlink", "role", namespaces["xlink"]), + "xlink:show": ("xlink", "show", namespaces["xlink"]), + "xlink:title": ("xlink", "title", namespaces["xlink"]), + "xlink:type": ("xlink", "type", namespaces["xlink"]), + "xml:base": ("xml", "base", namespaces["xml"]), + "xml:lang": ("xml", "lang", namespaces["xml"]), + "xml:space": ("xml", "space", namespaces["xml"]), + "xmlns": (None, "xmlns", namespaces["xmlns"]), + "xmlns:xlink": ("xmlns", "xlink", namespaces["xmlns"]) +} + +unadjustForeignAttributes = dict([((ns, local), qname) for qname, (prefix, local, ns) in + adjustForeignAttributes.items()]) + +spaceCharacters = frozenset([ + "\t", + "\n", + "\u000C", + " ", + "\r" +]) + +tableInsertModeElements = frozenset([ + "table", + "tbody", + "tfoot", + "thead", + "tr" +]) + +asciiLowercase = frozenset(string.ascii_lowercase) +asciiUppercase = frozenset(string.ascii_uppercase) +asciiLetters = frozenset(string.ascii_letters) +digits = frozenset(string.digits) +hexDigits = frozenset(string.hexdigits) + +asciiUpper2Lower = dict([(ord(c), ord(c.lower())) + for c in string.ascii_uppercase]) + +# Heading elements need to be ordered +headingElements = ( + "h1", + "h2", + "h3", + "h4", + "h5", + "h6" +) + +voidElements = frozenset([ + "base", + "command", + "event-source", + "link", + "meta", + "hr", + "br", + "img", + "embed", + "param", + "area", + "col", + "input", + "source", + "track" +]) + +cdataElements = frozenset(['title', 'textarea']) + +rcdataElements = frozenset([ + 'style', + 'script', + 'xmp', + 'iframe', + 'noembed', + 'noframes', + 'noscript' +]) + +booleanAttributes = { + "": frozenset(["irrelevant", "itemscope"]), + "style": frozenset(["scoped"]), + "img": frozenset(["ismap"]), + "audio": frozenset(["autoplay", "controls"]), + "video": frozenset(["autoplay", "controls"]), + "script": frozenset(["defer", "async"]), + "details": frozenset(["open"]), + "datagrid": frozenset(["multiple", "disabled"]), + "command": frozenset(["hidden", "disabled", "checked", "default"]), + "hr": frozenset(["noshade"]), + "menu": frozenset(["autosubmit"]), + "fieldset": frozenset(["disabled", "readonly"]), + "option": frozenset(["disabled", "readonly", "selected"]), + "optgroup": frozenset(["disabled", "readonly"]), + "button": frozenset(["disabled", "autofocus"]), + "input": frozenset(["disabled", "readonly", "required", "autofocus", "checked", "ismap"]), + "select": frozenset(["disabled", "readonly", "autofocus", "multiple"]), + "output": frozenset(["disabled", "readonly"]), + "iframe": frozenset(["seamless"]), +} + +# entitiesWindows1252 has to be _ordered_ and needs to have an index. It +# therefore can't be a frozenset. +entitiesWindows1252 = ( + 8364, # 0x80 0x20AC EURO SIGN + 65533, # 0x81 UNDEFINED + 8218, # 0x82 0x201A SINGLE LOW-9 QUOTATION MARK + 402, # 0x83 0x0192 LATIN SMALL LETTER F WITH HOOK + 8222, # 0x84 0x201E DOUBLE LOW-9 QUOTATION MARK + 8230, # 0x85 0x2026 HORIZONTAL ELLIPSIS + 8224, # 0x86 0x2020 DAGGER + 8225, # 0x87 0x2021 DOUBLE DAGGER + 710, # 0x88 0x02C6 MODIFIER LETTER CIRCUMFLEX ACCENT + 8240, # 0x89 0x2030 PER MILLE SIGN + 352, # 0x8A 0x0160 LATIN CAPITAL LETTER S WITH CARON + 8249, # 0x8B 0x2039 SINGLE LEFT-POINTING ANGLE QUOTATION MARK + 338, # 0x8C 0x0152 LATIN CAPITAL LIGATURE OE + 65533, # 0x8D UNDEFINED + 381, # 0x8E 0x017D LATIN CAPITAL LETTER Z WITH CARON + 65533, # 0x8F UNDEFINED + 65533, # 0x90 UNDEFINED + 8216, # 0x91 0x2018 LEFT SINGLE QUOTATION MARK + 8217, # 0x92 0x2019 RIGHT SINGLE QUOTATION MARK + 8220, # 0x93 0x201C LEFT DOUBLE QUOTATION MARK + 8221, # 0x94 0x201D RIGHT DOUBLE QUOTATION MARK + 8226, # 0x95 0x2022 BULLET + 8211, # 0x96 0x2013 EN DASH + 8212, # 0x97 0x2014 EM DASH + 732, # 0x98 0x02DC SMALL TILDE + 8482, # 0x99 0x2122 TRADE MARK SIGN + 353, # 0x9A 0x0161 LATIN SMALL LETTER S WITH CARON + 8250, # 0x9B 0x203A SINGLE RIGHT-POINTING ANGLE QUOTATION MARK + 339, # 0x9C 0x0153 LATIN SMALL LIGATURE OE + 65533, # 0x9D UNDEFINED + 382, # 0x9E 0x017E LATIN SMALL LETTER Z WITH CARON + 376 # 0x9F 0x0178 LATIN CAPITAL LETTER Y WITH DIAERESIS +) + +xmlEntities = frozenset(['lt;', 'gt;', 'amp;', 'apos;', 'quot;']) + +entities = { + "AElig": "\xc6", + "AElig;": "\xc6", + "AMP": "&", + "AMP;": "&", + "Aacute": "\xc1", + "Aacute;": "\xc1", + "Abreve;": "\u0102", + "Acirc": "\xc2", + "Acirc;": "\xc2", + "Acy;": "\u0410", + "Afr;": "\U0001d504", + "Agrave": "\xc0", + "Agrave;": "\xc0", + "Alpha;": "\u0391", + "Amacr;": "\u0100", + "And;": "\u2a53", + "Aogon;": "\u0104", + "Aopf;": "\U0001d538", + "ApplyFunction;": "\u2061", + "Aring": "\xc5", + "Aring;": "\xc5", + "Ascr;": "\U0001d49c", + "Assign;": "\u2254", + "Atilde": "\xc3", + "Atilde;": "\xc3", + "Auml": "\xc4", + "Auml;": "\xc4", + "Backslash;": "\u2216", + "Barv;": "\u2ae7", + "Barwed;": "\u2306", + "Bcy;": "\u0411", + "Because;": "\u2235", + "Bernoullis;": "\u212c", + "Beta;": "\u0392", + "Bfr;": "\U0001d505", + "Bopf;": "\U0001d539", + "Breve;": "\u02d8", + "Bscr;": "\u212c", + "Bumpeq;": "\u224e", + "CHcy;": "\u0427", + "COPY": "\xa9", + "COPY;": "\xa9", + "Cacute;": "\u0106", + "Cap;": "\u22d2", + "CapitalDifferentialD;": "\u2145", + "Cayleys;": "\u212d", + "Ccaron;": "\u010c", + "Ccedil": "\xc7", + "Ccedil;": "\xc7", + "Ccirc;": "\u0108", + "Cconint;": "\u2230", + "Cdot;": "\u010a", + "Cedilla;": "\xb8", + "CenterDot;": "\xb7", + "Cfr;": "\u212d", + "Chi;": "\u03a7", + "CircleDot;": "\u2299", + "CircleMinus;": "\u2296", + "CirclePlus;": "\u2295", + "CircleTimes;": "\u2297", + "ClockwiseContourIntegral;": "\u2232", + "CloseCurlyDoubleQuote;": "\u201d", + "CloseCurlyQuote;": "\u2019", + "Colon;": "\u2237", + "Colone;": "\u2a74", + "Congruent;": "\u2261", + "Conint;": "\u222f", + "ContourIntegral;": "\u222e", + "Copf;": "\u2102", + "Coproduct;": "\u2210", + "CounterClockwiseContourIntegral;": "\u2233", + "Cross;": "\u2a2f", + "Cscr;": "\U0001d49e", + "Cup;": "\u22d3", + "CupCap;": "\u224d", + "DD;": "\u2145", + "DDotrahd;": "\u2911", + "DJcy;": "\u0402", + "DScy;": "\u0405", + "DZcy;": "\u040f", + "Dagger;": "\u2021", + "Darr;": "\u21a1", + "Dashv;": "\u2ae4", + "Dcaron;": "\u010e", + "Dcy;": "\u0414", + "Del;": "\u2207", + "Delta;": "\u0394", + "Dfr;": "\U0001d507", + "DiacriticalAcute;": "\xb4", + "DiacriticalDot;": "\u02d9", + "DiacriticalDoubleAcute;": "\u02dd", + "DiacriticalGrave;": "`", + "DiacriticalTilde;": "\u02dc", + "Diamond;": "\u22c4", + "DifferentialD;": "\u2146", + "Dopf;": "\U0001d53b", + "Dot;": "\xa8", + "DotDot;": "\u20dc", + "DotEqual;": "\u2250", + "DoubleContourIntegral;": "\u222f", + "DoubleDot;": "\xa8", + "DoubleDownArrow;": "\u21d3", + "DoubleLeftArrow;": "\u21d0", + "DoubleLeftRightArrow;": "\u21d4", + "DoubleLeftTee;": "\u2ae4", + "DoubleLongLeftArrow;": "\u27f8", + "DoubleLongLeftRightArrow;": "\u27fa", + "DoubleLongRightArrow;": "\u27f9", + "DoubleRightArrow;": "\u21d2", + "DoubleRightTee;": "\u22a8", + "DoubleUpArrow;": "\u21d1", + "DoubleUpDownArrow;": "\u21d5", + "DoubleVerticalBar;": "\u2225", + "DownArrow;": "\u2193", + "DownArrowBar;": "\u2913", + "DownArrowUpArrow;": "\u21f5", + "DownBreve;": "\u0311", + "DownLeftRightVector;": "\u2950", + "DownLeftTeeVector;": "\u295e", + "DownLeftVector;": "\u21bd", + "DownLeftVectorBar;": "\u2956", + "DownRightTeeVector;": "\u295f", + "DownRightVector;": "\u21c1", + "DownRightVectorBar;": "\u2957", + "DownTee;": "\u22a4", + "DownTeeArrow;": "\u21a7", + "Downarrow;": "\u21d3", + "Dscr;": "\U0001d49f", + "Dstrok;": "\u0110", + "ENG;": "\u014a", + "ETH": "\xd0", + "ETH;": "\xd0", + "Eacute": "\xc9", + "Eacute;": "\xc9", + "Ecaron;": "\u011a", + "Ecirc": "\xca", + "Ecirc;": "\xca", + "Ecy;": "\u042d", + "Edot;": "\u0116", + "Efr;": "\U0001d508", + "Egrave": "\xc8", + "Egrave;": "\xc8", + "Element;": "\u2208", + "Emacr;": "\u0112", + "EmptySmallSquare;": "\u25fb", + "EmptyVerySmallSquare;": "\u25ab", + "Eogon;": "\u0118", + "Eopf;": "\U0001d53c", + "Epsilon;": "\u0395", + "Equal;": "\u2a75", + "EqualTilde;": "\u2242", + "Equilibrium;": "\u21cc", + "Escr;": "\u2130", + "Esim;": "\u2a73", + "Eta;": "\u0397", + "Euml": "\xcb", + "Euml;": "\xcb", + "Exists;": "\u2203", + "ExponentialE;": "\u2147", + "Fcy;": "\u0424", + "Ffr;": "\U0001d509", + "FilledSmallSquare;": "\u25fc", + "FilledVerySmallSquare;": "\u25aa", + "Fopf;": "\U0001d53d", + "ForAll;": "\u2200", + "Fouriertrf;": "\u2131", + "Fscr;": "\u2131", + "GJcy;": "\u0403", + "GT": ">", + "GT;": ">", + "Gamma;": "\u0393", + "Gammad;": "\u03dc", + "Gbreve;": "\u011e", + "Gcedil;": "\u0122", + "Gcirc;": "\u011c", + "Gcy;": "\u0413", + "Gdot;": "\u0120", + "Gfr;": "\U0001d50a", + "Gg;": "\u22d9", + "Gopf;": "\U0001d53e", + "GreaterEqual;": "\u2265", + "GreaterEqualLess;": "\u22db", + "GreaterFullEqual;": "\u2267", + "GreaterGreater;": "\u2aa2", + "GreaterLess;": "\u2277", + "GreaterSlantEqual;": "\u2a7e", + "GreaterTilde;": "\u2273", + "Gscr;": "\U0001d4a2", + "Gt;": "\u226b", + "HARDcy;": "\u042a", + "Hacek;": "\u02c7", + "Hat;": "^", + "Hcirc;": "\u0124", + "Hfr;": "\u210c", + "HilbertSpace;": "\u210b", + "Hopf;": "\u210d", + "HorizontalLine;": "\u2500", + "Hscr;": "\u210b", + "Hstrok;": "\u0126", + "HumpDownHump;": "\u224e", + "HumpEqual;": "\u224f", + "IEcy;": "\u0415", + "IJlig;": "\u0132", + "IOcy;": "\u0401", + "Iacute": "\xcd", + "Iacute;": "\xcd", + "Icirc": "\xce", + "Icirc;": "\xce", + "Icy;": "\u0418", + "Idot;": "\u0130", + "Ifr;": "\u2111", + "Igrave": "\xcc", + "Igrave;": "\xcc", + "Im;": "\u2111", + "Imacr;": "\u012a", + "ImaginaryI;": "\u2148", + "Implies;": "\u21d2", + "Int;": "\u222c", + "Integral;": "\u222b", + "Intersection;": "\u22c2", + "InvisibleComma;": "\u2063", + "InvisibleTimes;": "\u2062", + "Iogon;": "\u012e", + "Iopf;": "\U0001d540", + "Iota;": "\u0399", + "Iscr;": "\u2110", + "Itilde;": "\u0128", + "Iukcy;": "\u0406", + "Iuml": "\xcf", + "Iuml;": "\xcf", + "Jcirc;": "\u0134", + "Jcy;": "\u0419", + "Jfr;": "\U0001d50d", + "Jopf;": "\U0001d541", + "Jscr;": "\U0001d4a5", + "Jsercy;": "\u0408", + "Jukcy;": "\u0404", + "KHcy;": "\u0425", + "KJcy;": "\u040c", + "Kappa;": "\u039a", + "Kcedil;": "\u0136", + "Kcy;": "\u041a", + "Kfr;": "\U0001d50e", + "Kopf;": "\U0001d542", + "Kscr;": "\U0001d4a6", + "LJcy;": "\u0409", + "LT": "<", + "LT;": "<", + "Lacute;": "\u0139", + "Lambda;": "\u039b", + "Lang;": "\u27ea", + "Laplacetrf;": "\u2112", + "Larr;": "\u219e", + "Lcaron;": "\u013d", + "Lcedil;": "\u013b", + "Lcy;": "\u041b", + "LeftAngleBracket;": "\u27e8", + "LeftArrow;": "\u2190", + "LeftArrowBar;": "\u21e4", + "LeftArrowRightArrow;": "\u21c6", + "LeftCeiling;": "\u2308", + "LeftDoubleBracket;": "\u27e6", + "LeftDownTeeVector;": "\u2961", + "LeftDownVector;": "\u21c3", + "LeftDownVectorBar;": "\u2959", + "LeftFloor;": "\u230a", + "LeftRightArrow;": "\u2194", + "LeftRightVector;": "\u294e", + "LeftTee;": "\u22a3", + "LeftTeeArrow;": "\u21a4", + "LeftTeeVector;": "\u295a", + "LeftTriangle;": "\u22b2", + "LeftTriangleBar;": "\u29cf", + "LeftTriangleEqual;": "\u22b4", + "LeftUpDownVector;": "\u2951", + "LeftUpTeeVector;": "\u2960", + "LeftUpVector;": "\u21bf", + "LeftUpVectorBar;": "\u2958", + "LeftVector;": "\u21bc", + "LeftVectorBar;": "\u2952", + "Leftarrow;": "\u21d0", + "Leftrightarrow;": "\u21d4", + "LessEqualGreater;": "\u22da", + "LessFullEqual;": "\u2266", + "LessGreater;": "\u2276", + "LessLess;": "\u2aa1", + "LessSlantEqual;": "\u2a7d", + "LessTilde;": "\u2272", + "Lfr;": "\U0001d50f", + "Ll;": "\u22d8", + "Lleftarrow;": "\u21da", + "Lmidot;": "\u013f", + "LongLeftArrow;": "\u27f5", + "LongLeftRightArrow;": "\u27f7", + "LongRightArrow;": "\u27f6", + "Longleftarrow;": "\u27f8", + "Longleftrightarrow;": "\u27fa", + "Longrightarrow;": "\u27f9", + "Lopf;": "\U0001d543", + "LowerLeftArrow;": "\u2199", + "LowerRightArrow;": "\u2198", + "Lscr;": "\u2112", + "Lsh;": "\u21b0", + "Lstrok;": "\u0141", + "Lt;": "\u226a", + "Map;": "\u2905", + "Mcy;": "\u041c", + "MediumSpace;": "\u205f", + "Mellintrf;": "\u2133", + "Mfr;": "\U0001d510", + "MinusPlus;": "\u2213", + "Mopf;": "\U0001d544", + "Mscr;": "\u2133", + "Mu;": "\u039c", + "NJcy;": "\u040a", + "Nacute;": "\u0143", + "Ncaron;": "\u0147", + "Ncedil;": "\u0145", + "Ncy;": "\u041d", + "NegativeMediumSpace;": "\u200b", + "NegativeThickSpace;": "\u200b", + "NegativeThinSpace;": "\u200b", + "NegativeVeryThinSpace;": "\u200b", + "NestedGreaterGreater;": "\u226b", + "NestedLessLess;": "\u226a", + "NewLine;": "\n", + "Nfr;": "\U0001d511", + "NoBreak;": "\u2060", + "NonBreakingSpace;": "\xa0", + "Nopf;": "\u2115", + "Not;": "\u2aec", + "NotCongruent;": "\u2262", + "NotCupCap;": "\u226d", + "NotDoubleVerticalBar;": "\u2226", + "NotElement;": "\u2209", + "NotEqual;": "\u2260", + "NotEqualTilde;": "\u2242\u0338", + "NotExists;": "\u2204", + "NotGreater;": "\u226f", + "NotGreaterEqual;": "\u2271", + "NotGreaterFullEqual;": "\u2267\u0338", + "NotGreaterGreater;": "\u226b\u0338", + "NotGreaterLess;": "\u2279", + "NotGreaterSlantEqual;": "\u2a7e\u0338", + "NotGreaterTilde;": "\u2275", + "NotHumpDownHump;": "\u224e\u0338", + "NotHumpEqual;": "\u224f\u0338", + "NotLeftTriangle;": "\u22ea", + "NotLeftTriangleBar;": "\u29cf\u0338", + "NotLeftTriangleEqual;": "\u22ec", + "NotLess;": "\u226e", + "NotLessEqual;": "\u2270", + "NotLessGreater;": "\u2278", + "NotLessLess;": "\u226a\u0338", + "NotLessSlantEqual;": "\u2a7d\u0338", + "NotLessTilde;": "\u2274", + "NotNestedGreaterGreater;": "\u2aa2\u0338", + "NotNestedLessLess;": "\u2aa1\u0338", + "NotPrecedes;": "\u2280", + "NotPrecedesEqual;": "\u2aaf\u0338", + "NotPrecedesSlantEqual;": "\u22e0", + "NotReverseElement;": "\u220c", + "NotRightTriangle;": "\u22eb", + "NotRightTriangleBar;": "\u29d0\u0338", + "NotRightTriangleEqual;": "\u22ed", + "NotSquareSubset;": "\u228f\u0338", + "NotSquareSubsetEqual;": "\u22e2", + "NotSquareSuperset;": "\u2290\u0338", + "NotSquareSupersetEqual;": "\u22e3", + "NotSubset;": "\u2282\u20d2", + "NotSubsetEqual;": "\u2288", + "NotSucceeds;": "\u2281", + "NotSucceedsEqual;": "\u2ab0\u0338", + "NotSucceedsSlantEqual;": "\u22e1", + "NotSucceedsTilde;": "\u227f\u0338", + "NotSuperset;": "\u2283\u20d2", + "NotSupersetEqual;": "\u2289", + "NotTilde;": "\u2241", + "NotTildeEqual;": "\u2244", + "NotTildeFullEqual;": "\u2247", + "NotTildeTilde;": "\u2249", + "NotVerticalBar;": "\u2224", + "Nscr;": "\U0001d4a9", + "Ntilde": "\xd1", + "Ntilde;": "\xd1", + "Nu;": "\u039d", + "OElig;": "\u0152", + "Oacute": "\xd3", + "Oacute;": "\xd3", + "Ocirc": "\xd4", + "Ocirc;": "\xd4", + "Ocy;": "\u041e", + "Odblac;": "\u0150", + "Ofr;": "\U0001d512", + "Ograve": "\xd2", + "Ograve;": "\xd2", + "Omacr;": "\u014c", + "Omega;": "\u03a9", + "Omicron;": "\u039f", + "Oopf;": "\U0001d546", + "OpenCurlyDoubleQuote;": "\u201c", + "OpenCurlyQuote;": "\u2018", + "Or;": "\u2a54", + "Oscr;": "\U0001d4aa", + "Oslash": "\xd8", + "Oslash;": "\xd8", + "Otilde": "\xd5", + "Otilde;": "\xd5", + "Otimes;": "\u2a37", + "Ouml": "\xd6", + "Ouml;": "\xd6", + "OverBar;": "\u203e", + "OverBrace;": "\u23de", + "OverBracket;": "\u23b4", + "OverParenthesis;": "\u23dc", + "PartialD;": "\u2202", + "Pcy;": "\u041f", + "Pfr;": "\U0001d513", + "Phi;": "\u03a6", + "Pi;": "\u03a0", + "PlusMinus;": "\xb1", + "Poincareplane;": "\u210c", + "Popf;": "\u2119", + "Pr;": "\u2abb", + "Precedes;": "\u227a", + "PrecedesEqual;": "\u2aaf", + "PrecedesSlantEqual;": "\u227c", + "PrecedesTilde;": "\u227e", + "Prime;": "\u2033", + "Product;": "\u220f", + "Proportion;": "\u2237", + "Proportional;": "\u221d", + "Pscr;": "\U0001d4ab", + "Psi;": "\u03a8", + "QUOT": "\"", + "QUOT;": "\"", + "Qfr;": "\U0001d514", + "Qopf;": "\u211a", + "Qscr;": "\U0001d4ac", + "RBarr;": "\u2910", + "REG": "\xae", + "REG;": "\xae", + "Racute;": "\u0154", + "Rang;": "\u27eb", + "Rarr;": "\u21a0", + "Rarrtl;": "\u2916", + "Rcaron;": "\u0158", + "Rcedil;": "\u0156", + "Rcy;": "\u0420", + "Re;": "\u211c", + "ReverseElement;": "\u220b", + "ReverseEquilibrium;": "\u21cb", + "ReverseUpEquilibrium;": "\u296f", + "Rfr;": "\u211c", + "Rho;": "\u03a1", + "RightAngleBracket;": "\u27e9", + "RightArrow;": "\u2192", + "RightArrowBar;": "\u21e5", + "RightArrowLeftArrow;": "\u21c4", + "RightCeiling;": "\u2309", + "RightDoubleBracket;": "\u27e7", + "RightDownTeeVector;": "\u295d", + "RightDownVector;": "\u21c2", + "RightDownVectorBar;": "\u2955", + "RightFloor;": "\u230b", + "RightTee;": "\u22a2", + "RightTeeArrow;": "\u21a6", + "RightTeeVector;": "\u295b", + "RightTriangle;": "\u22b3", + "RightTriangleBar;": "\u29d0", + "RightTriangleEqual;": "\u22b5", + "RightUpDownVector;": "\u294f", + "RightUpTeeVector;": "\u295c", + "RightUpVector;": "\u21be", + "RightUpVectorBar;": "\u2954", + "RightVector;": "\u21c0", + "RightVectorBar;": "\u2953", + "Rightarrow;": "\u21d2", + "Ropf;": "\u211d", + "RoundImplies;": "\u2970", + "Rrightarrow;": "\u21db", + "Rscr;": "\u211b", + "Rsh;": "\u21b1", + "RuleDelayed;": "\u29f4", + "SHCHcy;": "\u0429", + "SHcy;": "\u0428", + "SOFTcy;": "\u042c", + "Sacute;": "\u015a", + "Sc;": "\u2abc", + "Scaron;": "\u0160", + "Scedil;": "\u015e", + "Scirc;": "\u015c", + "Scy;": "\u0421", + "Sfr;": "\U0001d516", + "ShortDownArrow;": "\u2193", + "ShortLeftArrow;": "\u2190", + "ShortRightArrow;": "\u2192", + "ShortUpArrow;": "\u2191", + "Sigma;": "\u03a3", + "SmallCircle;": "\u2218", + "Sopf;": "\U0001d54a", + "Sqrt;": "\u221a", + "Square;": "\u25a1", + "SquareIntersection;": "\u2293", + "SquareSubset;": "\u228f", + "SquareSubsetEqual;": "\u2291", + "SquareSuperset;": "\u2290", + "SquareSupersetEqual;": "\u2292", + "SquareUnion;": "\u2294", + "Sscr;": "\U0001d4ae", + "Star;": "\u22c6", + "Sub;": "\u22d0", + "Subset;": "\u22d0", + "SubsetEqual;": "\u2286", + "Succeeds;": "\u227b", + "SucceedsEqual;": "\u2ab0", + "SucceedsSlantEqual;": "\u227d", + "SucceedsTilde;": "\u227f", + "SuchThat;": "\u220b", + "Sum;": "\u2211", + "Sup;": "\u22d1", + "Superset;": "\u2283", + "SupersetEqual;": "\u2287", + "Supset;": "\u22d1", + "THORN": "\xde", + "THORN;": "\xde", + "TRADE;": "\u2122", + "TSHcy;": "\u040b", + "TScy;": "\u0426", + "Tab;": "\t", + "Tau;": "\u03a4", + "Tcaron;": "\u0164", + "Tcedil;": "\u0162", + "Tcy;": "\u0422", + "Tfr;": "\U0001d517", + "Therefore;": "\u2234", + "Theta;": "\u0398", + "ThickSpace;": "\u205f\u200a", + "ThinSpace;": "\u2009", + "Tilde;": "\u223c", + "TildeEqual;": "\u2243", + "TildeFullEqual;": "\u2245", + "TildeTilde;": "\u2248", + "Topf;": "\U0001d54b", + "TripleDot;": "\u20db", + "Tscr;": "\U0001d4af", + "Tstrok;": "\u0166", + "Uacute": "\xda", + "Uacute;": "\xda", + "Uarr;": "\u219f", + "Uarrocir;": "\u2949", + "Ubrcy;": "\u040e", + "Ubreve;": "\u016c", + "Ucirc": "\xdb", + "Ucirc;": "\xdb", + "Ucy;": "\u0423", + "Udblac;": "\u0170", + "Ufr;": "\U0001d518", + "Ugrave": "\xd9", + "Ugrave;": "\xd9", + "Umacr;": "\u016a", + "UnderBar;": "_", + "UnderBrace;": "\u23df", + "UnderBracket;": "\u23b5", + "UnderParenthesis;": "\u23dd", + "Union;": "\u22c3", + "UnionPlus;": "\u228e", + "Uogon;": "\u0172", + "Uopf;": "\U0001d54c", + "UpArrow;": "\u2191", + "UpArrowBar;": "\u2912", + "UpArrowDownArrow;": "\u21c5", + "UpDownArrow;": "\u2195", + "UpEquilibrium;": "\u296e", + "UpTee;": "\u22a5", + "UpTeeArrow;": "\u21a5", + "Uparrow;": "\u21d1", + "Updownarrow;": "\u21d5", + "UpperLeftArrow;": "\u2196", + "UpperRightArrow;": "\u2197", + "Upsi;": "\u03d2", + "Upsilon;": "\u03a5", + "Uring;": "\u016e", + "Uscr;": "\U0001d4b0", + "Utilde;": "\u0168", + "Uuml": "\xdc", + "Uuml;": "\xdc", + "VDash;": "\u22ab", + "Vbar;": "\u2aeb", + "Vcy;": "\u0412", + "Vdash;": "\u22a9", + "Vdashl;": "\u2ae6", + "Vee;": "\u22c1", + "Verbar;": "\u2016", + "Vert;": "\u2016", + "VerticalBar;": "\u2223", + "VerticalLine;": "|", + "VerticalSeparator;": "\u2758", + "VerticalTilde;": "\u2240", + "VeryThinSpace;": "\u200a", + "Vfr;": "\U0001d519", + "Vopf;": "\U0001d54d", + "Vscr;": "\U0001d4b1", + "Vvdash;": "\u22aa", + "Wcirc;": "\u0174", + "Wedge;": "\u22c0", + "Wfr;": "\U0001d51a", + "Wopf;": "\U0001d54e", + "Wscr;": "\U0001d4b2", + "Xfr;": "\U0001d51b", + "Xi;": "\u039e", + "Xopf;": "\U0001d54f", + "Xscr;": "\U0001d4b3", + "YAcy;": "\u042f", + "YIcy;": "\u0407", + "YUcy;": "\u042e", + "Yacute": "\xdd", + "Yacute;": "\xdd", + "Ycirc;": "\u0176", + "Ycy;": "\u042b", + "Yfr;": "\U0001d51c", + "Yopf;": "\U0001d550", + "Yscr;": "\U0001d4b4", + "Yuml;": "\u0178", + "ZHcy;": "\u0416", + "Zacute;": "\u0179", + "Zcaron;": "\u017d", + "Zcy;": "\u0417", + "Zdot;": "\u017b", + "ZeroWidthSpace;": "\u200b", + "Zeta;": "\u0396", + "Zfr;": "\u2128", + "Zopf;": "\u2124", + "Zscr;": "\U0001d4b5", + "aacute": "\xe1", + "aacute;": "\xe1", + "abreve;": "\u0103", + "ac;": "\u223e", + "acE;": "\u223e\u0333", + "acd;": "\u223f", + "acirc": "\xe2", + "acirc;": "\xe2", + "acute": "\xb4", + "acute;": "\xb4", + "acy;": "\u0430", + "aelig": "\xe6", + "aelig;": "\xe6", + "af;": "\u2061", + "afr;": "\U0001d51e", + "agrave": "\xe0", + "agrave;": "\xe0", + "alefsym;": "\u2135", + "aleph;": "\u2135", + "alpha;": "\u03b1", + "amacr;": "\u0101", + "amalg;": "\u2a3f", + "amp": "&", + "amp;": "&", + "and;": "\u2227", + "andand;": "\u2a55", + "andd;": "\u2a5c", + "andslope;": "\u2a58", + "andv;": "\u2a5a", + "ang;": "\u2220", + "ange;": "\u29a4", + "angle;": "\u2220", + "angmsd;": "\u2221", + "angmsdaa;": "\u29a8", + "angmsdab;": "\u29a9", + "angmsdac;": "\u29aa", + "angmsdad;": "\u29ab", + "angmsdae;": "\u29ac", + "angmsdaf;": "\u29ad", + "angmsdag;": "\u29ae", + "angmsdah;": "\u29af", + "angrt;": "\u221f", + "angrtvb;": "\u22be", + "angrtvbd;": "\u299d", + "angsph;": "\u2222", + "angst;": "\xc5", + "angzarr;": "\u237c", + "aogon;": "\u0105", + "aopf;": "\U0001d552", + "ap;": "\u2248", + "apE;": "\u2a70", + "apacir;": "\u2a6f", + "ape;": "\u224a", + "apid;": "\u224b", + "apos;": "'", + "approx;": "\u2248", + "approxeq;": "\u224a", + "aring": "\xe5", + "aring;": "\xe5", + "ascr;": "\U0001d4b6", + "ast;": "*", + "asymp;": "\u2248", + "asympeq;": "\u224d", + "atilde": "\xe3", + "atilde;": "\xe3", + "auml": "\xe4", + "auml;": "\xe4", + "awconint;": "\u2233", + "awint;": "\u2a11", + "bNot;": "\u2aed", + "backcong;": "\u224c", + "backepsilon;": "\u03f6", + "backprime;": "\u2035", + "backsim;": "\u223d", + "backsimeq;": "\u22cd", + "barvee;": "\u22bd", + "barwed;": "\u2305", + "barwedge;": "\u2305", + "bbrk;": "\u23b5", + "bbrktbrk;": "\u23b6", + "bcong;": "\u224c", + "bcy;": "\u0431", + "bdquo;": "\u201e", + "becaus;": "\u2235", + "because;": "\u2235", + "bemptyv;": "\u29b0", + "bepsi;": "\u03f6", + "bernou;": "\u212c", + "beta;": "\u03b2", + "beth;": "\u2136", + "between;": "\u226c", + "bfr;": "\U0001d51f", + "bigcap;": "\u22c2", + "bigcirc;": "\u25ef", + "bigcup;": "\u22c3", + "bigodot;": "\u2a00", + "bigoplus;": "\u2a01", + "bigotimes;": "\u2a02", + "bigsqcup;": "\u2a06", + "bigstar;": "\u2605", + "bigtriangledown;": "\u25bd", + "bigtriangleup;": "\u25b3", + "biguplus;": "\u2a04", + "bigvee;": "\u22c1", + "bigwedge;": "\u22c0", + "bkarow;": "\u290d", + "blacklozenge;": "\u29eb", + "blacksquare;": "\u25aa", + "blacktriangle;": "\u25b4", + "blacktriangledown;": "\u25be", + "blacktriangleleft;": "\u25c2", + "blacktriangleright;": "\u25b8", + "blank;": "\u2423", + "blk12;": "\u2592", + "blk14;": "\u2591", + "blk34;": "\u2593", + "block;": "\u2588", + "bne;": "=\u20e5", + "bnequiv;": "\u2261\u20e5", + "bnot;": "\u2310", + "bopf;": "\U0001d553", + "bot;": "\u22a5", + "bottom;": "\u22a5", + "bowtie;": "\u22c8", + "boxDL;": "\u2557", + "boxDR;": "\u2554", + "boxDl;": "\u2556", + "boxDr;": "\u2553", + "boxH;": "\u2550", + "boxHD;": "\u2566", + "boxHU;": "\u2569", + "boxHd;": "\u2564", + "boxHu;": "\u2567", + "boxUL;": "\u255d", + "boxUR;": "\u255a", + "boxUl;": "\u255c", + "boxUr;": "\u2559", + "boxV;": "\u2551", + "boxVH;": "\u256c", + "boxVL;": "\u2563", + "boxVR;": "\u2560", + "boxVh;": "\u256b", + "boxVl;": "\u2562", + "boxVr;": "\u255f", + "boxbox;": "\u29c9", + "boxdL;": "\u2555", + "boxdR;": "\u2552", + "boxdl;": "\u2510", + "boxdr;": "\u250c", + "boxh;": "\u2500", + "boxhD;": "\u2565", + "boxhU;": "\u2568", + "boxhd;": "\u252c", + "boxhu;": "\u2534", + "boxminus;": "\u229f", + "boxplus;": "\u229e", + "boxtimes;": "\u22a0", + "boxuL;": "\u255b", + "boxuR;": "\u2558", + "boxul;": "\u2518", + "boxur;": "\u2514", + "boxv;": "\u2502", + "boxvH;": "\u256a", + "boxvL;": "\u2561", + "boxvR;": "\u255e", + "boxvh;": "\u253c", + "boxvl;": "\u2524", + "boxvr;": "\u251c", + "bprime;": "\u2035", + "breve;": "\u02d8", + "brvbar": "\xa6", + "brvbar;": "\xa6", + "bscr;": "\U0001d4b7", + "bsemi;": "\u204f", + "bsim;": "\u223d", + "bsime;": "\u22cd", + "bsol;": "\\", + "bsolb;": "\u29c5", + "bsolhsub;": "\u27c8", + "bull;": "\u2022", + "bullet;": "\u2022", + "bump;": "\u224e", + "bumpE;": "\u2aae", + "bumpe;": "\u224f", + "bumpeq;": "\u224f", + "cacute;": "\u0107", + "cap;": "\u2229", + "capand;": "\u2a44", + "capbrcup;": "\u2a49", + "capcap;": "\u2a4b", + "capcup;": "\u2a47", + "capdot;": "\u2a40", + "caps;": "\u2229\ufe00", + "caret;": "\u2041", + "caron;": "\u02c7", + "ccaps;": "\u2a4d", + "ccaron;": "\u010d", + "ccedil": "\xe7", + "ccedil;": "\xe7", + "ccirc;": "\u0109", + "ccups;": "\u2a4c", + "ccupssm;": "\u2a50", + "cdot;": "\u010b", + "cedil": "\xb8", + "cedil;": "\xb8", + "cemptyv;": "\u29b2", + "cent": "\xa2", + "cent;": "\xa2", + "centerdot;": "\xb7", + "cfr;": "\U0001d520", + "chcy;": "\u0447", + "check;": "\u2713", + "checkmark;": "\u2713", + "chi;": "\u03c7", + "cir;": "\u25cb", + "cirE;": "\u29c3", + "circ;": "\u02c6", + "circeq;": "\u2257", + "circlearrowleft;": "\u21ba", + "circlearrowright;": "\u21bb", + "circledR;": "\xae", + "circledS;": "\u24c8", + "circledast;": "\u229b", + "circledcirc;": "\u229a", + "circleddash;": "\u229d", + "cire;": "\u2257", + "cirfnint;": "\u2a10", + "cirmid;": "\u2aef", + "cirscir;": "\u29c2", + "clubs;": "\u2663", + "clubsuit;": "\u2663", + "colon;": ":", + "colone;": "\u2254", + "coloneq;": "\u2254", + "comma;": ",", + "commat;": "@", + "comp;": "\u2201", + "compfn;": "\u2218", + "complement;": "\u2201", + "complexes;": "\u2102", + "cong;": "\u2245", + "congdot;": "\u2a6d", + "conint;": "\u222e", + "copf;": "\U0001d554", + "coprod;": "\u2210", + "copy": "\xa9", + "copy;": "\xa9", + "copysr;": "\u2117", + "crarr;": "\u21b5", + "cross;": "\u2717", + "cscr;": "\U0001d4b8", + "csub;": "\u2acf", + "csube;": "\u2ad1", + "csup;": "\u2ad0", + "csupe;": "\u2ad2", + "ctdot;": "\u22ef", + "cudarrl;": "\u2938", + "cudarrr;": "\u2935", + "cuepr;": "\u22de", + "cuesc;": "\u22df", + "cularr;": "\u21b6", + "cularrp;": "\u293d", + "cup;": "\u222a", + "cupbrcap;": "\u2a48", + "cupcap;": "\u2a46", + "cupcup;": "\u2a4a", + "cupdot;": "\u228d", + "cupor;": "\u2a45", + "cups;": "\u222a\ufe00", + "curarr;": "\u21b7", + "curarrm;": "\u293c", + "curlyeqprec;": "\u22de", + "curlyeqsucc;": "\u22df", + "curlyvee;": "\u22ce", + "curlywedge;": "\u22cf", + "curren": "\xa4", + "curren;": "\xa4", + "curvearrowleft;": "\u21b6", + "curvearrowright;": "\u21b7", + "cuvee;": "\u22ce", + "cuwed;": "\u22cf", + "cwconint;": "\u2232", + "cwint;": "\u2231", + "cylcty;": "\u232d", + "dArr;": "\u21d3", + "dHar;": "\u2965", + "dagger;": "\u2020", + "daleth;": "\u2138", + "darr;": "\u2193", + "dash;": "\u2010", + "dashv;": "\u22a3", + "dbkarow;": "\u290f", + "dblac;": "\u02dd", + "dcaron;": "\u010f", + "dcy;": "\u0434", + "dd;": "\u2146", + "ddagger;": "\u2021", + "ddarr;": "\u21ca", + "ddotseq;": "\u2a77", + "deg": "\xb0", + "deg;": "\xb0", + "delta;": "\u03b4", + "demptyv;": "\u29b1", + "dfisht;": "\u297f", + "dfr;": "\U0001d521", + "dharl;": "\u21c3", + "dharr;": "\u21c2", + "diam;": "\u22c4", + "diamond;": "\u22c4", + "diamondsuit;": "\u2666", + "diams;": "\u2666", + "die;": "\xa8", + "digamma;": "\u03dd", + "disin;": "\u22f2", + "div;": "\xf7", + "divide": "\xf7", + "divide;": "\xf7", + "divideontimes;": "\u22c7", + "divonx;": "\u22c7", + "djcy;": "\u0452", + "dlcorn;": "\u231e", + "dlcrop;": "\u230d", + "dollar;": "$", + "dopf;": "\U0001d555", + "dot;": "\u02d9", + "doteq;": "\u2250", + "doteqdot;": "\u2251", + "dotminus;": "\u2238", + "dotplus;": "\u2214", + "dotsquare;": "\u22a1", + "doublebarwedge;": "\u2306", + "downarrow;": "\u2193", + "downdownarrows;": "\u21ca", + "downharpoonleft;": "\u21c3", + "downharpoonright;": "\u21c2", + "drbkarow;": "\u2910", + "drcorn;": "\u231f", + "drcrop;": "\u230c", + "dscr;": "\U0001d4b9", + "dscy;": "\u0455", + "dsol;": "\u29f6", + "dstrok;": "\u0111", + "dtdot;": "\u22f1", + "dtri;": "\u25bf", + "dtrif;": "\u25be", + "duarr;": "\u21f5", + "duhar;": "\u296f", + "dwangle;": "\u29a6", + "dzcy;": "\u045f", + "dzigrarr;": "\u27ff", + "eDDot;": "\u2a77", + "eDot;": "\u2251", + "eacute": "\xe9", + "eacute;": "\xe9", + "easter;": "\u2a6e", + "ecaron;": "\u011b", + "ecir;": "\u2256", + "ecirc": "\xea", + "ecirc;": "\xea", + "ecolon;": "\u2255", + "ecy;": "\u044d", + "edot;": "\u0117", + "ee;": "\u2147", + "efDot;": "\u2252", + "efr;": "\U0001d522", + "eg;": "\u2a9a", + "egrave": "\xe8", + "egrave;": "\xe8", + "egs;": "\u2a96", + "egsdot;": "\u2a98", + "el;": "\u2a99", + "elinters;": "\u23e7", + "ell;": "\u2113", + "els;": "\u2a95", + "elsdot;": "\u2a97", + "emacr;": "\u0113", + "empty;": "\u2205", + "emptyset;": "\u2205", + "emptyv;": "\u2205", + "emsp13;": "\u2004", + "emsp14;": "\u2005", + "emsp;": "\u2003", + "eng;": "\u014b", + "ensp;": "\u2002", + "eogon;": "\u0119", + "eopf;": "\U0001d556", + "epar;": "\u22d5", + "eparsl;": "\u29e3", + "eplus;": "\u2a71", + "epsi;": "\u03b5", + "epsilon;": "\u03b5", + "epsiv;": "\u03f5", + "eqcirc;": "\u2256", + "eqcolon;": "\u2255", + "eqsim;": "\u2242", + "eqslantgtr;": "\u2a96", + "eqslantless;": "\u2a95", + "equals;": "=", + "equest;": "\u225f", + "equiv;": "\u2261", + "equivDD;": "\u2a78", + "eqvparsl;": "\u29e5", + "erDot;": "\u2253", + "erarr;": "\u2971", + "escr;": "\u212f", + "esdot;": "\u2250", + "esim;": "\u2242", + "eta;": "\u03b7", + "eth": "\xf0", + "eth;": "\xf0", + "euml": "\xeb", + "euml;": "\xeb", + "euro;": "\u20ac", + "excl;": "!", + "exist;": "\u2203", + "expectation;": "\u2130", + "exponentiale;": "\u2147", + "fallingdotseq;": "\u2252", + "fcy;": "\u0444", + "female;": "\u2640", + "ffilig;": "\ufb03", + "fflig;": "\ufb00", + "ffllig;": "\ufb04", + "ffr;": "\U0001d523", + "filig;": "\ufb01", + "fjlig;": "fj", + "flat;": "\u266d", + "fllig;": "\ufb02", + "fltns;": "\u25b1", + "fnof;": "\u0192", + "fopf;": "\U0001d557", + "forall;": "\u2200", + "fork;": "\u22d4", + "forkv;": "\u2ad9", + "fpartint;": "\u2a0d", + "frac12": "\xbd", + "frac12;": "\xbd", + "frac13;": "\u2153", + "frac14": "\xbc", + "frac14;": "\xbc", + "frac15;": "\u2155", + "frac16;": "\u2159", + "frac18;": "\u215b", + "frac23;": "\u2154", + "frac25;": "\u2156", + "frac34": "\xbe", + "frac34;": "\xbe", + "frac35;": "\u2157", + "frac38;": "\u215c", + "frac45;": "\u2158", + "frac56;": "\u215a", + "frac58;": "\u215d", + "frac78;": "\u215e", + "frasl;": "\u2044", + "frown;": "\u2322", + "fscr;": "\U0001d4bb", + "gE;": "\u2267", + "gEl;": "\u2a8c", + "gacute;": "\u01f5", + "gamma;": "\u03b3", + "gammad;": "\u03dd", + "gap;": "\u2a86", + "gbreve;": "\u011f", + "gcirc;": "\u011d", + "gcy;": "\u0433", + "gdot;": "\u0121", + "ge;": "\u2265", + "gel;": "\u22db", + "geq;": "\u2265", + "geqq;": "\u2267", + "geqslant;": "\u2a7e", + "ges;": "\u2a7e", + "gescc;": "\u2aa9", + "gesdot;": "\u2a80", + "gesdoto;": "\u2a82", + "gesdotol;": "\u2a84", + "gesl;": "\u22db\ufe00", + "gesles;": "\u2a94", + "gfr;": "\U0001d524", + "gg;": "\u226b", + "ggg;": "\u22d9", + "gimel;": "\u2137", + "gjcy;": "\u0453", + "gl;": "\u2277", + "glE;": "\u2a92", + "gla;": "\u2aa5", + "glj;": "\u2aa4", + "gnE;": "\u2269", + "gnap;": "\u2a8a", + "gnapprox;": "\u2a8a", + "gne;": "\u2a88", + "gneq;": "\u2a88", + "gneqq;": "\u2269", + "gnsim;": "\u22e7", + "gopf;": "\U0001d558", + "grave;": "`", + "gscr;": "\u210a", + "gsim;": "\u2273", + "gsime;": "\u2a8e", + "gsiml;": "\u2a90", + "gt": ">", + "gt;": ">", + "gtcc;": "\u2aa7", + "gtcir;": "\u2a7a", + "gtdot;": "\u22d7", + "gtlPar;": "\u2995", + "gtquest;": "\u2a7c", + "gtrapprox;": "\u2a86", + "gtrarr;": "\u2978", + "gtrdot;": "\u22d7", + "gtreqless;": "\u22db", + "gtreqqless;": "\u2a8c", + "gtrless;": "\u2277", + "gtrsim;": "\u2273", + "gvertneqq;": "\u2269\ufe00", + "gvnE;": "\u2269\ufe00", + "hArr;": "\u21d4", + "hairsp;": "\u200a", + "half;": "\xbd", + "hamilt;": "\u210b", + "hardcy;": "\u044a", + "harr;": "\u2194", + "harrcir;": "\u2948", + "harrw;": "\u21ad", + "hbar;": "\u210f", + "hcirc;": "\u0125", + "hearts;": "\u2665", + "heartsuit;": "\u2665", + "hellip;": "\u2026", + "hercon;": "\u22b9", + "hfr;": "\U0001d525", + "hksearow;": "\u2925", + "hkswarow;": "\u2926", + "hoarr;": "\u21ff", + "homtht;": "\u223b", + "hookleftarrow;": "\u21a9", + "hookrightarrow;": "\u21aa", + "hopf;": "\U0001d559", + "horbar;": "\u2015", + "hscr;": "\U0001d4bd", + "hslash;": "\u210f", + "hstrok;": "\u0127", + "hybull;": "\u2043", + "hyphen;": "\u2010", + "iacute": "\xed", + "iacute;": "\xed", + "ic;": "\u2063", + "icirc": "\xee", + "icirc;": "\xee", + "icy;": "\u0438", + "iecy;": "\u0435", + "iexcl": "\xa1", + "iexcl;": "\xa1", + "iff;": "\u21d4", + "ifr;": "\U0001d526", + "igrave": "\xec", + "igrave;": "\xec", + "ii;": "\u2148", + "iiiint;": "\u2a0c", + "iiint;": "\u222d", + "iinfin;": "\u29dc", + "iiota;": "\u2129", + "ijlig;": "\u0133", + "imacr;": "\u012b", + "image;": "\u2111", + "imagline;": "\u2110", + "imagpart;": "\u2111", + "imath;": "\u0131", + "imof;": "\u22b7", + "imped;": "\u01b5", + "in;": "\u2208", + "incare;": "\u2105", + "infin;": "\u221e", + "infintie;": "\u29dd", + "inodot;": "\u0131", + "int;": "\u222b", + "intcal;": "\u22ba", + "integers;": "\u2124", + "intercal;": "\u22ba", + "intlarhk;": "\u2a17", + "intprod;": "\u2a3c", + "iocy;": "\u0451", + "iogon;": "\u012f", + "iopf;": "\U0001d55a", + "iota;": "\u03b9", + "iprod;": "\u2a3c", + "iquest": "\xbf", + "iquest;": "\xbf", + "iscr;": "\U0001d4be", + "isin;": "\u2208", + "isinE;": "\u22f9", + "isindot;": "\u22f5", + "isins;": "\u22f4", + "isinsv;": "\u22f3", + "isinv;": "\u2208", + "it;": "\u2062", + "itilde;": "\u0129", + "iukcy;": "\u0456", + "iuml": "\xef", + "iuml;": "\xef", + "jcirc;": "\u0135", + "jcy;": "\u0439", + "jfr;": "\U0001d527", + "jmath;": "\u0237", + "jopf;": "\U0001d55b", + "jscr;": "\U0001d4bf", + "jsercy;": "\u0458", + "jukcy;": "\u0454", + "kappa;": "\u03ba", + "kappav;": "\u03f0", + "kcedil;": "\u0137", + "kcy;": "\u043a", + "kfr;": "\U0001d528", + "kgreen;": "\u0138", + "khcy;": "\u0445", + "kjcy;": "\u045c", + "kopf;": "\U0001d55c", + "kscr;": "\U0001d4c0", + "lAarr;": "\u21da", + "lArr;": "\u21d0", + "lAtail;": "\u291b", + "lBarr;": "\u290e", + "lE;": "\u2266", + "lEg;": "\u2a8b", + "lHar;": "\u2962", + "lacute;": "\u013a", + "laemptyv;": "\u29b4", + "lagran;": "\u2112", + "lambda;": "\u03bb", + "lang;": "\u27e8", + "langd;": "\u2991", + "langle;": "\u27e8", + "lap;": "\u2a85", + "laquo": "\xab", + "laquo;": "\xab", + "larr;": "\u2190", + "larrb;": "\u21e4", + "larrbfs;": "\u291f", + "larrfs;": "\u291d", + "larrhk;": "\u21a9", + "larrlp;": "\u21ab", + "larrpl;": "\u2939", + "larrsim;": "\u2973", + "larrtl;": "\u21a2", + "lat;": "\u2aab", + "latail;": "\u2919", + "late;": "\u2aad", + "lates;": "\u2aad\ufe00", + "lbarr;": "\u290c", + "lbbrk;": "\u2772", + "lbrace;": "{", + "lbrack;": "[", + "lbrke;": "\u298b", + "lbrksld;": "\u298f", + "lbrkslu;": "\u298d", + "lcaron;": "\u013e", + "lcedil;": "\u013c", + "lceil;": "\u2308", + "lcub;": "{", + "lcy;": "\u043b", + "ldca;": "\u2936", + "ldquo;": "\u201c", + "ldquor;": "\u201e", + "ldrdhar;": "\u2967", + "ldrushar;": "\u294b", + "ldsh;": "\u21b2", + "le;": "\u2264", + "leftarrow;": "\u2190", + "leftarrowtail;": "\u21a2", + "leftharpoondown;": "\u21bd", + "leftharpoonup;": "\u21bc", + "leftleftarrows;": "\u21c7", + "leftrightarrow;": "\u2194", + "leftrightarrows;": "\u21c6", + "leftrightharpoons;": "\u21cb", + "leftrightsquigarrow;": "\u21ad", + "leftthreetimes;": "\u22cb", + "leg;": "\u22da", + "leq;": "\u2264", + "leqq;": "\u2266", + "leqslant;": "\u2a7d", + "les;": "\u2a7d", + "lescc;": "\u2aa8", + "lesdot;": "\u2a7f", + "lesdoto;": "\u2a81", + "lesdotor;": "\u2a83", + "lesg;": "\u22da\ufe00", + "lesges;": "\u2a93", + "lessapprox;": "\u2a85", + "lessdot;": "\u22d6", + "lesseqgtr;": "\u22da", + "lesseqqgtr;": "\u2a8b", + "lessgtr;": "\u2276", + "lesssim;": "\u2272", + "lfisht;": "\u297c", + "lfloor;": "\u230a", + "lfr;": "\U0001d529", + "lg;": "\u2276", + "lgE;": "\u2a91", + "lhard;": "\u21bd", + "lharu;": "\u21bc", + "lharul;": "\u296a", + "lhblk;": "\u2584", + "ljcy;": "\u0459", + "ll;": "\u226a", + "llarr;": "\u21c7", + "llcorner;": "\u231e", + "llhard;": "\u296b", + "lltri;": "\u25fa", + "lmidot;": "\u0140", + "lmoust;": "\u23b0", + "lmoustache;": "\u23b0", + "lnE;": "\u2268", + "lnap;": "\u2a89", + "lnapprox;": "\u2a89", + "lne;": "\u2a87", + "lneq;": "\u2a87", + "lneqq;": "\u2268", + "lnsim;": "\u22e6", + "loang;": "\u27ec", + "loarr;": "\u21fd", + "lobrk;": "\u27e6", + "longleftarrow;": "\u27f5", + "longleftrightarrow;": "\u27f7", + "longmapsto;": "\u27fc", + "longrightarrow;": "\u27f6", + "looparrowleft;": "\u21ab", + "looparrowright;": "\u21ac", + "lopar;": "\u2985", + "lopf;": "\U0001d55d", + "loplus;": "\u2a2d", + "lotimes;": "\u2a34", + "lowast;": "\u2217", + "lowbar;": "_", + "loz;": "\u25ca", + "lozenge;": "\u25ca", + "lozf;": "\u29eb", + "lpar;": "(", + "lparlt;": "\u2993", + "lrarr;": "\u21c6", + "lrcorner;": "\u231f", + "lrhar;": "\u21cb", + "lrhard;": "\u296d", + "lrm;": "\u200e", + "lrtri;": "\u22bf", + "lsaquo;": "\u2039", + "lscr;": "\U0001d4c1", + "lsh;": "\u21b0", + "lsim;": "\u2272", + "lsime;": "\u2a8d", + "lsimg;": "\u2a8f", + "lsqb;": "[", + "lsquo;": "\u2018", + "lsquor;": "\u201a", + "lstrok;": "\u0142", + "lt": "<", + "lt;": "<", + "ltcc;": "\u2aa6", + "ltcir;": "\u2a79", + "ltdot;": "\u22d6", + "lthree;": "\u22cb", + "ltimes;": "\u22c9", + "ltlarr;": "\u2976", + "ltquest;": "\u2a7b", + "ltrPar;": "\u2996", + "ltri;": "\u25c3", + "ltrie;": "\u22b4", + "ltrif;": "\u25c2", + "lurdshar;": "\u294a", + "luruhar;": "\u2966", + "lvertneqq;": "\u2268\ufe00", + "lvnE;": "\u2268\ufe00", + "mDDot;": "\u223a", + "macr": "\xaf", + "macr;": "\xaf", + "male;": "\u2642", + "malt;": "\u2720", + "maltese;": "\u2720", + "map;": "\u21a6", + "mapsto;": "\u21a6", + "mapstodown;": "\u21a7", + "mapstoleft;": "\u21a4", + "mapstoup;": "\u21a5", + "marker;": "\u25ae", + "mcomma;": "\u2a29", + "mcy;": "\u043c", + "mdash;": "\u2014", + "measuredangle;": "\u2221", + "mfr;": "\U0001d52a", + "mho;": "\u2127", + "micro": "\xb5", + "micro;": "\xb5", + "mid;": "\u2223", + "midast;": "*", + "midcir;": "\u2af0", + "middot": "\xb7", + "middot;": "\xb7", + "minus;": "\u2212", + "minusb;": "\u229f", + "minusd;": "\u2238", + "minusdu;": "\u2a2a", + "mlcp;": "\u2adb", + "mldr;": "\u2026", + "mnplus;": "\u2213", + "models;": "\u22a7", + "mopf;": "\U0001d55e", + "mp;": "\u2213", + "mscr;": "\U0001d4c2", + "mstpos;": "\u223e", + "mu;": "\u03bc", + "multimap;": "\u22b8", + "mumap;": "\u22b8", + "nGg;": "\u22d9\u0338", + "nGt;": "\u226b\u20d2", + "nGtv;": "\u226b\u0338", + "nLeftarrow;": "\u21cd", + "nLeftrightarrow;": "\u21ce", + "nLl;": "\u22d8\u0338", + "nLt;": "\u226a\u20d2", + "nLtv;": "\u226a\u0338", + "nRightarrow;": "\u21cf", + "nVDash;": "\u22af", + "nVdash;": "\u22ae", + "nabla;": "\u2207", + "nacute;": "\u0144", + "nang;": "\u2220\u20d2", + "nap;": "\u2249", + "napE;": "\u2a70\u0338", + "napid;": "\u224b\u0338", + "napos;": "\u0149", + "napprox;": "\u2249", + "natur;": "\u266e", + "natural;": "\u266e", + "naturals;": "\u2115", + "nbsp": "\xa0", + "nbsp;": "\xa0", + "nbump;": "\u224e\u0338", + "nbumpe;": "\u224f\u0338", + "ncap;": "\u2a43", + "ncaron;": "\u0148", + "ncedil;": "\u0146", + "ncong;": "\u2247", + "ncongdot;": "\u2a6d\u0338", + "ncup;": "\u2a42", + "ncy;": "\u043d", + "ndash;": "\u2013", + "ne;": "\u2260", + "neArr;": "\u21d7", + "nearhk;": "\u2924", + "nearr;": "\u2197", + "nearrow;": "\u2197", + "nedot;": "\u2250\u0338", + "nequiv;": "\u2262", + "nesear;": "\u2928", + "nesim;": "\u2242\u0338", + "nexist;": "\u2204", + "nexists;": "\u2204", + "nfr;": "\U0001d52b", + "ngE;": "\u2267\u0338", + "nge;": "\u2271", + "ngeq;": "\u2271", + "ngeqq;": "\u2267\u0338", + "ngeqslant;": "\u2a7e\u0338", + "nges;": "\u2a7e\u0338", + "ngsim;": "\u2275", + "ngt;": "\u226f", + "ngtr;": "\u226f", + "nhArr;": "\u21ce", + "nharr;": "\u21ae", + "nhpar;": "\u2af2", + "ni;": "\u220b", + "nis;": "\u22fc", + "nisd;": "\u22fa", + "niv;": "\u220b", + "njcy;": "\u045a", + "nlArr;": "\u21cd", + "nlE;": "\u2266\u0338", + "nlarr;": "\u219a", + "nldr;": "\u2025", + "nle;": "\u2270", + "nleftarrow;": "\u219a", + "nleftrightarrow;": "\u21ae", + "nleq;": "\u2270", + "nleqq;": "\u2266\u0338", + "nleqslant;": "\u2a7d\u0338", + "nles;": "\u2a7d\u0338", + "nless;": "\u226e", + "nlsim;": "\u2274", + "nlt;": "\u226e", + "nltri;": "\u22ea", + "nltrie;": "\u22ec", + "nmid;": "\u2224", + "nopf;": "\U0001d55f", + "not": "\xac", + "not;": "\xac", + "notin;": "\u2209", + "notinE;": "\u22f9\u0338", + "notindot;": "\u22f5\u0338", + "notinva;": "\u2209", + "notinvb;": "\u22f7", + "notinvc;": "\u22f6", + "notni;": "\u220c", + "notniva;": "\u220c", + "notnivb;": "\u22fe", + "notnivc;": "\u22fd", + "npar;": "\u2226", + "nparallel;": "\u2226", + "nparsl;": "\u2afd\u20e5", + "npart;": "\u2202\u0338", + "npolint;": "\u2a14", + "npr;": "\u2280", + "nprcue;": "\u22e0", + "npre;": "\u2aaf\u0338", + "nprec;": "\u2280", + "npreceq;": "\u2aaf\u0338", + "nrArr;": "\u21cf", + "nrarr;": "\u219b", + "nrarrc;": "\u2933\u0338", + "nrarrw;": "\u219d\u0338", + "nrightarrow;": "\u219b", + "nrtri;": "\u22eb", + "nrtrie;": "\u22ed", + "nsc;": "\u2281", + "nsccue;": "\u22e1", + "nsce;": "\u2ab0\u0338", + "nscr;": "\U0001d4c3", + "nshortmid;": "\u2224", + "nshortparallel;": "\u2226", + "nsim;": "\u2241", + "nsime;": "\u2244", + "nsimeq;": "\u2244", + "nsmid;": "\u2224", + "nspar;": "\u2226", + "nsqsube;": "\u22e2", + "nsqsupe;": "\u22e3", + "nsub;": "\u2284", + "nsubE;": "\u2ac5\u0338", + "nsube;": "\u2288", + "nsubset;": "\u2282\u20d2", + "nsubseteq;": "\u2288", + "nsubseteqq;": "\u2ac5\u0338", + "nsucc;": "\u2281", + "nsucceq;": "\u2ab0\u0338", + "nsup;": "\u2285", + "nsupE;": "\u2ac6\u0338", + "nsupe;": "\u2289", + "nsupset;": "\u2283\u20d2", + "nsupseteq;": "\u2289", + "nsupseteqq;": "\u2ac6\u0338", + "ntgl;": "\u2279", + "ntilde": "\xf1", + "ntilde;": "\xf1", + "ntlg;": "\u2278", + "ntriangleleft;": "\u22ea", + "ntrianglelefteq;": "\u22ec", + "ntriangleright;": "\u22eb", + "ntrianglerighteq;": "\u22ed", + "nu;": "\u03bd", + "num;": "#", + "numero;": "\u2116", + "numsp;": "\u2007", + "nvDash;": "\u22ad", + "nvHarr;": "\u2904", + "nvap;": "\u224d\u20d2", + "nvdash;": "\u22ac", + "nvge;": "\u2265\u20d2", + "nvgt;": ">\u20d2", + "nvinfin;": "\u29de", + "nvlArr;": "\u2902", + "nvle;": "\u2264\u20d2", + "nvlt;": "<\u20d2", + "nvltrie;": "\u22b4\u20d2", + "nvrArr;": "\u2903", + "nvrtrie;": "\u22b5\u20d2", + "nvsim;": "\u223c\u20d2", + "nwArr;": "\u21d6", + "nwarhk;": "\u2923", + "nwarr;": "\u2196", + "nwarrow;": "\u2196", + "nwnear;": "\u2927", + "oS;": "\u24c8", + "oacute": "\xf3", + "oacute;": "\xf3", + "oast;": "\u229b", + "ocir;": "\u229a", + "ocirc": "\xf4", + "ocirc;": "\xf4", + "ocy;": "\u043e", + "odash;": "\u229d", + "odblac;": "\u0151", + "odiv;": "\u2a38", + "odot;": "\u2299", + "odsold;": "\u29bc", + "oelig;": "\u0153", + "ofcir;": "\u29bf", + "ofr;": "\U0001d52c", + "ogon;": "\u02db", + "ograve": "\xf2", + "ograve;": "\xf2", + "ogt;": "\u29c1", + "ohbar;": "\u29b5", + "ohm;": "\u03a9", + "oint;": "\u222e", + "olarr;": "\u21ba", + "olcir;": "\u29be", + "olcross;": "\u29bb", + "oline;": "\u203e", + "olt;": "\u29c0", + "omacr;": "\u014d", + "omega;": "\u03c9", + "omicron;": "\u03bf", + "omid;": "\u29b6", + "ominus;": "\u2296", + "oopf;": "\U0001d560", + "opar;": "\u29b7", + "operp;": "\u29b9", + "oplus;": "\u2295", + "or;": "\u2228", + "orarr;": "\u21bb", + "ord;": "\u2a5d", + "order;": "\u2134", + "orderof;": "\u2134", + "ordf": "\xaa", + "ordf;": "\xaa", + "ordm": "\xba", + "ordm;": "\xba", + "origof;": "\u22b6", + "oror;": "\u2a56", + "orslope;": "\u2a57", + "orv;": "\u2a5b", + "oscr;": "\u2134", + "oslash": "\xf8", + "oslash;": "\xf8", + "osol;": "\u2298", + "otilde": "\xf5", + "otilde;": "\xf5", + "otimes;": "\u2297", + "otimesas;": "\u2a36", + "ouml": "\xf6", + "ouml;": "\xf6", + "ovbar;": "\u233d", + "par;": "\u2225", + "para": "\xb6", + "para;": "\xb6", + "parallel;": "\u2225", + "parsim;": "\u2af3", + "parsl;": "\u2afd", + "part;": "\u2202", + "pcy;": "\u043f", + "percnt;": "%", + "period;": ".", + "permil;": "\u2030", + "perp;": "\u22a5", + "pertenk;": "\u2031", + "pfr;": "\U0001d52d", + "phi;": "\u03c6", + "phiv;": "\u03d5", + "phmmat;": "\u2133", + "phone;": "\u260e", + "pi;": "\u03c0", + "pitchfork;": "\u22d4", + "piv;": "\u03d6", + "planck;": "\u210f", + "planckh;": "\u210e", + "plankv;": "\u210f", + "plus;": "+", + "plusacir;": "\u2a23", + "plusb;": "\u229e", + "pluscir;": "\u2a22", + "plusdo;": "\u2214", + "plusdu;": "\u2a25", + "pluse;": "\u2a72", + "plusmn": "\xb1", + "plusmn;": "\xb1", + "plussim;": "\u2a26", + "plustwo;": "\u2a27", + "pm;": "\xb1", + "pointint;": "\u2a15", + "popf;": "\U0001d561", + "pound": "\xa3", + "pound;": "\xa3", + "pr;": "\u227a", + "prE;": "\u2ab3", + "prap;": "\u2ab7", + "prcue;": "\u227c", + "pre;": "\u2aaf", + "prec;": "\u227a", + "precapprox;": "\u2ab7", + "preccurlyeq;": "\u227c", + "preceq;": "\u2aaf", + "precnapprox;": "\u2ab9", + "precneqq;": "\u2ab5", + "precnsim;": "\u22e8", + "precsim;": "\u227e", + "prime;": "\u2032", + "primes;": "\u2119", + "prnE;": "\u2ab5", + "prnap;": "\u2ab9", + "prnsim;": "\u22e8", + "prod;": "\u220f", + "profalar;": "\u232e", + "profline;": "\u2312", + "profsurf;": "\u2313", + "prop;": "\u221d", + "propto;": "\u221d", + "prsim;": "\u227e", + "prurel;": "\u22b0", + "pscr;": "\U0001d4c5", + "psi;": "\u03c8", + "puncsp;": "\u2008", + "qfr;": "\U0001d52e", + "qint;": "\u2a0c", + "qopf;": "\U0001d562", + "qprime;": "\u2057", + "qscr;": "\U0001d4c6", + "quaternions;": "\u210d", + "quatint;": "\u2a16", + "quest;": "?", + "questeq;": "\u225f", + "quot": "\"", + "quot;": "\"", + "rAarr;": "\u21db", + "rArr;": "\u21d2", + "rAtail;": "\u291c", + "rBarr;": "\u290f", + "rHar;": "\u2964", + "race;": "\u223d\u0331", + "racute;": "\u0155", + "radic;": "\u221a", + "raemptyv;": "\u29b3", + "rang;": "\u27e9", + "rangd;": "\u2992", + "range;": "\u29a5", + "rangle;": "\u27e9", + "raquo": "\xbb", + "raquo;": "\xbb", + "rarr;": "\u2192", + "rarrap;": "\u2975", + "rarrb;": "\u21e5", + "rarrbfs;": "\u2920", + "rarrc;": "\u2933", + "rarrfs;": "\u291e", + "rarrhk;": "\u21aa", + "rarrlp;": "\u21ac", + "rarrpl;": "\u2945", + "rarrsim;": "\u2974", + "rarrtl;": "\u21a3", + "rarrw;": "\u219d", + "ratail;": "\u291a", + "ratio;": "\u2236", + "rationals;": "\u211a", + "rbarr;": "\u290d", + "rbbrk;": "\u2773", + "rbrace;": "}", + "rbrack;": "]", + "rbrke;": "\u298c", + "rbrksld;": "\u298e", + "rbrkslu;": "\u2990", + "rcaron;": "\u0159", + "rcedil;": "\u0157", + "rceil;": "\u2309", + "rcub;": "}", + "rcy;": "\u0440", + "rdca;": "\u2937", + "rdldhar;": "\u2969", + "rdquo;": "\u201d", + "rdquor;": "\u201d", + "rdsh;": "\u21b3", + "real;": "\u211c", + "realine;": "\u211b", + "realpart;": "\u211c", + "reals;": "\u211d", + "rect;": "\u25ad", + "reg": "\xae", + "reg;": "\xae", + "rfisht;": "\u297d", + "rfloor;": "\u230b", + "rfr;": "\U0001d52f", + "rhard;": "\u21c1", + "rharu;": "\u21c0", + "rharul;": "\u296c", + "rho;": "\u03c1", + "rhov;": "\u03f1", + "rightarrow;": "\u2192", + "rightarrowtail;": "\u21a3", + "rightharpoondown;": "\u21c1", + "rightharpoonup;": "\u21c0", + "rightleftarrows;": "\u21c4", + "rightleftharpoons;": "\u21cc", + "rightrightarrows;": "\u21c9", + "rightsquigarrow;": "\u219d", + "rightthreetimes;": "\u22cc", + "ring;": "\u02da", + "risingdotseq;": "\u2253", + "rlarr;": "\u21c4", + "rlhar;": "\u21cc", + "rlm;": "\u200f", + "rmoust;": "\u23b1", + "rmoustache;": "\u23b1", + "rnmid;": "\u2aee", + "roang;": "\u27ed", + "roarr;": "\u21fe", + "robrk;": "\u27e7", + "ropar;": "\u2986", + "ropf;": "\U0001d563", + "roplus;": "\u2a2e", + "rotimes;": "\u2a35", + "rpar;": ")", + "rpargt;": "\u2994", + "rppolint;": "\u2a12", + "rrarr;": "\u21c9", + "rsaquo;": "\u203a", + "rscr;": "\U0001d4c7", + "rsh;": "\u21b1", + "rsqb;": "]", + "rsquo;": "\u2019", + "rsquor;": "\u2019", + "rthree;": "\u22cc", + "rtimes;": "\u22ca", + "rtri;": "\u25b9", + "rtrie;": "\u22b5", + "rtrif;": "\u25b8", + "rtriltri;": "\u29ce", + "ruluhar;": "\u2968", + "rx;": "\u211e", + "sacute;": "\u015b", + "sbquo;": "\u201a", + "sc;": "\u227b", + "scE;": "\u2ab4", + "scap;": "\u2ab8", + "scaron;": "\u0161", + "sccue;": "\u227d", + "sce;": "\u2ab0", + "scedil;": "\u015f", + "scirc;": "\u015d", + "scnE;": "\u2ab6", + "scnap;": "\u2aba", + "scnsim;": "\u22e9", + "scpolint;": "\u2a13", + "scsim;": "\u227f", + "scy;": "\u0441", + "sdot;": "\u22c5", + "sdotb;": "\u22a1", + "sdote;": "\u2a66", + "seArr;": "\u21d8", + "searhk;": "\u2925", + "searr;": "\u2198", + "searrow;": "\u2198", + "sect": "\xa7", + "sect;": "\xa7", + "semi;": ";", + "seswar;": "\u2929", + "setminus;": "\u2216", + "setmn;": "\u2216", + "sext;": "\u2736", + "sfr;": "\U0001d530", + "sfrown;": "\u2322", + "sharp;": "\u266f", + "shchcy;": "\u0449", + "shcy;": "\u0448", + "shortmid;": "\u2223", + "shortparallel;": "\u2225", + "shy": "\xad", + "shy;": "\xad", + "sigma;": "\u03c3", + "sigmaf;": "\u03c2", + "sigmav;": "\u03c2", + "sim;": "\u223c", + "simdot;": "\u2a6a", + "sime;": "\u2243", + "simeq;": "\u2243", + "simg;": "\u2a9e", + "simgE;": "\u2aa0", + "siml;": "\u2a9d", + "simlE;": "\u2a9f", + "simne;": "\u2246", + "simplus;": "\u2a24", + "simrarr;": "\u2972", + "slarr;": "\u2190", + "smallsetminus;": "\u2216", + "smashp;": "\u2a33", + "smeparsl;": "\u29e4", + "smid;": "\u2223", + "smile;": "\u2323", + "smt;": "\u2aaa", + "smte;": "\u2aac", + "smtes;": "\u2aac\ufe00", + "softcy;": "\u044c", + "sol;": "/", + "solb;": "\u29c4", + "solbar;": "\u233f", + "sopf;": "\U0001d564", + "spades;": "\u2660", + "spadesuit;": "\u2660", + "spar;": "\u2225", + "sqcap;": "\u2293", + "sqcaps;": "\u2293\ufe00", + "sqcup;": "\u2294", + "sqcups;": "\u2294\ufe00", + "sqsub;": "\u228f", + "sqsube;": "\u2291", + "sqsubset;": "\u228f", + "sqsubseteq;": "\u2291", + "sqsup;": "\u2290", + "sqsupe;": "\u2292", + "sqsupset;": "\u2290", + "sqsupseteq;": "\u2292", + "squ;": "\u25a1", + "square;": "\u25a1", + "squarf;": "\u25aa", + "squf;": "\u25aa", + "srarr;": "\u2192", + "sscr;": "\U0001d4c8", + "ssetmn;": "\u2216", + "ssmile;": "\u2323", + "sstarf;": "\u22c6", + "star;": "\u2606", + "starf;": "\u2605", + "straightepsilon;": "\u03f5", + "straightphi;": "\u03d5", + "strns;": "\xaf", + "sub;": "\u2282", + "subE;": "\u2ac5", + "subdot;": "\u2abd", + "sube;": "\u2286", + "subedot;": "\u2ac3", + "submult;": "\u2ac1", + "subnE;": "\u2acb", + "subne;": "\u228a", + "subplus;": "\u2abf", + "subrarr;": "\u2979", + "subset;": "\u2282", + "subseteq;": "\u2286", + "subseteqq;": "\u2ac5", + "subsetneq;": "\u228a", + "subsetneqq;": "\u2acb", + "subsim;": "\u2ac7", + "subsub;": "\u2ad5", + "subsup;": "\u2ad3", + "succ;": "\u227b", + "succapprox;": "\u2ab8", + "succcurlyeq;": "\u227d", + "succeq;": "\u2ab0", + "succnapprox;": "\u2aba", + "succneqq;": "\u2ab6", + "succnsim;": "\u22e9", + "succsim;": "\u227f", + "sum;": "\u2211", + "sung;": "\u266a", + "sup1": "\xb9", + "sup1;": "\xb9", + "sup2": "\xb2", + "sup2;": "\xb2", + "sup3": "\xb3", + "sup3;": "\xb3", + "sup;": "\u2283", + "supE;": "\u2ac6", + "supdot;": "\u2abe", + "supdsub;": "\u2ad8", + "supe;": "\u2287", + "supedot;": "\u2ac4", + "suphsol;": "\u27c9", + "suphsub;": "\u2ad7", + "suplarr;": "\u297b", + "supmult;": "\u2ac2", + "supnE;": "\u2acc", + "supne;": "\u228b", + "supplus;": "\u2ac0", + "supset;": "\u2283", + "supseteq;": "\u2287", + "supseteqq;": "\u2ac6", + "supsetneq;": "\u228b", + "supsetneqq;": "\u2acc", + "supsim;": "\u2ac8", + "supsub;": "\u2ad4", + "supsup;": "\u2ad6", + "swArr;": "\u21d9", + "swarhk;": "\u2926", + "swarr;": "\u2199", + "swarrow;": "\u2199", + "swnwar;": "\u292a", + "szlig": "\xdf", + "szlig;": "\xdf", + "target;": "\u2316", + "tau;": "\u03c4", + "tbrk;": "\u23b4", + "tcaron;": "\u0165", + "tcedil;": "\u0163", + "tcy;": "\u0442", + "tdot;": "\u20db", + "telrec;": "\u2315", + "tfr;": "\U0001d531", + "there4;": "\u2234", + "therefore;": "\u2234", + "theta;": "\u03b8", + "thetasym;": "\u03d1", + "thetav;": "\u03d1", + "thickapprox;": "\u2248", + "thicksim;": "\u223c", + "thinsp;": "\u2009", + "thkap;": "\u2248", + "thksim;": "\u223c", + "thorn": "\xfe", + "thorn;": "\xfe", + "tilde;": "\u02dc", + "times": "\xd7", + "times;": "\xd7", + "timesb;": "\u22a0", + "timesbar;": "\u2a31", + "timesd;": "\u2a30", + "tint;": "\u222d", + "toea;": "\u2928", + "top;": "\u22a4", + "topbot;": "\u2336", + "topcir;": "\u2af1", + "topf;": "\U0001d565", + "topfork;": "\u2ada", + "tosa;": "\u2929", + "tprime;": "\u2034", + "trade;": "\u2122", + "triangle;": "\u25b5", + "triangledown;": "\u25bf", + "triangleleft;": "\u25c3", + "trianglelefteq;": "\u22b4", + "triangleq;": "\u225c", + "triangleright;": "\u25b9", + "trianglerighteq;": "\u22b5", + "tridot;": "\u25ec", + "trie;": "\u225c", + "triminus;": "\u2a3a", + "triplus;": "\u2a39", + "trisb;": "\u29cd", + "tritime;": "\u2a3b", + "trpezium;": "\u23e2", + "tscr;": "\U0001d4c9", + "tscy;": "\u0446", + "tshcy;": "\u045b", + "tstrok;": "\u0167", + "twixt;": "\u226c", + "twoheadleftarrow;": "\u219e", + "twoheadrightarrow;": "\u21a0", + "uArr;": "\u21d1", + "uHar;": "\u2963", + "uacute": "\xfa", + "uacute;": "\xfa", + "uarr;": "\u2191", + "ubrcy;": "\u045e", + "ubreve;": "\u016d", + "ucirc": "\xfb", + "ucirc;": "\xfb", + "ucy;": "\u0443", + "udarr;": "\u21c5", + "udblac;": "\u0171", + "udhar;": "\u296e", + "ufisht;": "\u297e", + "ufr;": "\U0001d532", + "ugrave": "\xf9", + "ugrave;": "\xf9", + "uharl;": "\u21bf", + "uharr;": "\u21be", + "uhblk;": "\u2580", + "ulcorn;": "\u231c", + "ulcorner;": "\u231c", + "ulcrop;": "\u230f", + "ultri;": "\u25f8", + "umacr;": "\u016b", + "uml": "\xa8", + "uml;": "\xa8", + "uogon;": "\u0173", + "uopf;": "\U0001d566", + "uparrow;": "\u2191", + "updownarrow;": "\u2195", + "upharpoonleft;": "\u21bf", + "upharpoonright;": "\u21be", + "uplus;": "\u228e", + "upsi;": "\u03c5", + "upsih;": "\u03d2", + "upsilon;": "\u03c5", + "upuparrows;": "\u21c8", + "urcorn;": "\u231d", + "urcorner;": "\u231d", + "urcrop;": "\u230e", + "uring;": "\u016f", + "urtri;": "\u25f9", + "uscr;": "\U0001d4ca", + "utdot;": "\u22f0", + "utilde;": "\u0169", + "utri;": "\u25b5", + "utrif;": "\u25b4", + "uuarr;": "\u21c8", + "uuml": "\xfc", + "uuml;": "\xfc", + "uwangle;": "\u29a7", + "vArr;": "\u21d5", + "vBar;": "\u2ae8", + "vBarv;": "\u2ae9", + "vDash;": "\u22a8", + "vangrt;": "\u299c", + "varepsilon;": "\u03f5", + "varkappa;": "\u03f0", + "varnothing;": "\u2205", + "varphi;": "\u03d5", + "varpi;": "\u03d6", + "varpropto;": "\u221d", + "varr;": "\u2195", + "varrho;": "\u03f1", + "varsigma;": "\u03c2", + "varsubsetneq;": "\u228a\ufe00", + "varsubsetneqq;": "\u2acb\ufe00", + "varsupsetneq;": "\u228b\ufe00", + "varsupsetneqq;": "\u2acc\ufe00", + "vartheta;": "\u03d1", + "vartriangleleft;": "\u22b2", + "vartriangleright;": "\u22b3", + "vcy;": "\u0432", + "vdash;": "\u22a2", + "vee;": "\u2228", + "veebar;": "\u22bb", + "veeeq;": "\u225a", + "vellip;": "\u22ee", + "verbar;": "|", + "vert;": "|", + "vfr;": "\U0001d533", + "vltri;": "\u22b2", + "vnsub;": "\u2282\u20d2", + "vnsup;": "\u2283\u20d2", + "vopf;": "\U0001d567", + "vprop;": "\u221d", + "vrtri;": "\u22b3", + "vscr;": "\U0001d4cb", + "vsubnE;": "\u2acb\ufe00", + "vsubne;": "\u228a\ufe00", + "vsupnE;": "\u2acc\ufe00", + "vsupne;": "\u228b\ufe00", + "vzigzag;": "\u299a", + "wcirc;": "\u0175", + "wedbar;": "\u2a5f", + "wedge;": "\u2227", + "wedgeq;": "\u2259", + "weierp;": "\u2118", + "wfr;": "\U0001d534", + "wopf;": "\U0001d568", + "wp;": "\u2118", + "wr;": "\u2240", + "wreath;": "\u2240", + "wscr;": "\U0001d4cc", + "xcap;": "\u22c2", + "xcirc;": "\u25ef", + "xcup;": "\u22c3", + "xdtri;": "\u25bd", + "xfr;": "\U0001d535", + "xhArr;": "\u27fa", + "xharr;": "\u27f7", + "xi;": "\u03be", + "xlArr;": "\u27f8", + "xlarr;": "\u27f5", + "xmap;": "\u27fc", + "xnis;": "\u22fb", + "xodot;": "\u2a00", + "xopf;": "\U0001d569", + "xoplus;": "\u2a01", + "xotime;": "\u2a02", + "xrArr;": "\u27f9", + "xrarr;": "\u27f6", + "xscr;": "\U0001d4cd", + "xsqcup;": "\u2a06", + "xuplus;": "\u2a04", + "xutri;": "\u25b3", + "xvee;": "\u22c1", + "xwedge;": "\u22c0", + "yacute": "\xfd", + "yacute;": "\xfd", + "yacy;": "\u044f", + "ycirc;": "\u0177", + "ycy;": "\u044b", + "yen": "\xa5", + "yen;": "\xa5", + "yfr;": "\U0001d536", + "yicy;": "\u0457", + "yopf;": "\U0001d56a", + "yscr;": "\U0001d4ce", + "yucy;": "\u044e", + "yuml": "\xff", + "yuml;": "\xff", + "zacute;": "\u017a", + "zcaron;": "\u017e", + "zcy;": "\u0437", + "zdot;": "\u017c", + "zeetrf;": "\u2128", + "zeta;": "\u03b6", + "zfr;": "\U0001d537", + "zhcy;": "\u0436", + "zigrarr;": "\u21dd", + "zopf;": "\U0001d56b", + "zscr;": "\U0001d4cf", + "zwj;": "\u200d", + "zwnj;": "\u200c", +} + +replacementCharacters = { + 0x0: "\uFFFD", + 0x0d: "\u000D", + 0x80: "\u20AC", + 0x81: "\u0081", + 0x82: "\u201A", + 0x83: "\u0192", + 0x84: "\u201E", + 0x85: "\u2026", + 0x86: "\u2020", + 0x87: "\u2021", + 0x88: "\u02C6", + 0x89: "\u2030", + 0x8A: "\u0160", + 0x8B: "\u2039", + 0x8C: "\u0152", + 0x8D: "\u008D", + 0x8E: "\u017D", + 0x8F: "\u008F", + 0x90: "\u0090", + 0x91: "\u2018", + 0x92: "\u2019", + 0x93: "\u201C", + 0x94: "\u201D", + 0x95: "\u2022", + 0x96: "\u2013", + 0x97: "\u2014", + 0x98: "\u02DC", + 0x99: "\u2122", + 0x9A: "\u0161", + 0x9B: "\u203A", + 0x9C: "\u0153", + 0x9D: "\u009D", + 0x9E: "\u017E", + 0x9F: "\u0178", +} + +tokenTypes = { + "Doctype": 0, + "Characters": 1, + "SpaceCharacters": 2, + "StartTag": 3, + "EndTag": 4, + "EmptyTag": 5, + "Comment": 6, + "ParseError": 7 +} + +tagTokenTypes = frozenset([tokenTypes["StartTag"], tokenTypes["EndTag"], + tokenTypes["EmptyTag"]]) + + +prefixes = dict([(v, k) for k, v in namespaces.items()]) +prefixes["http://www.w3.org/1998/Math/MathML"] = "math" + + +class DataLossWarning(UserWarning): + """Raised when the current tree is unable to represent the input data""" + pass + + +class _ReparseException(Exception): + pass diff --git a/venv2/lib/python2.7/site-packages/pip/_vendor/html5lib/filters/__init__.py b/venv2/lib/python2.7/site-packages/pip/_vendor/html5lib/filters/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/venv2/lib/python2.7/site-packages/pip/_vendor/html5lib/filters/alphabeticalattributes.py b/venv2/lib/python2.7/site-packages/pip/_vendor/html5lib/filters/alphabeticalattributes.py new file mode 100644 index 0000000..5ba926e --- /dev/null +++ b/venv2/lib/python2.7/site-packages/pip/_vendor/html5lib/filters/alphabeticalattributes.py @@ -0,0 +1,29 @@ +from __future__ import absolute_import, division, unicode_literals + +from . import base + +from collections import OrderedDict + + +def _attr_key(attr): + """Return an appropriate key for an attribute for sorting + + Attributes have a namespace that can be either ``None`` or a string. We + can't compare the two because they're different types, so we convert + ``None`` to an empty string first. + + """ + return (attr[0][0] or ''), attr[0][1] + + +class Filter(base.Filter): + """Alphabetizes attributes for elements""" + def __iter__(self): + for token in base.Filter.__iter__(self): + if token["type"] in ("StartTag", "EmptyTag"): + attrs = OrderedDict() + for name, value in sorted(token["data"].items(), + key=_attr_key): + attrs[name] = value + token["data"] = attrs + yield token diff --git a/venv2/lib/python2.7/site-packages/pip/_vendor/html5lib/filters/base.py b/venv2/lib/python2.7/site-packages/pip/_vendor/html5lib/filters/base.py new file mode 100644 index 0000000..c7dbaed --- /dev/null +++ b/venv2/lib/python2.7/site-packages/pip/_vendor/html5lib/filters/base.py @@ -0,0 +1,12 @@ +from __future__ import absolute_import, division, unicode_literals + + +class Filter(object): + def __init__(self, source): + self.source = source + + def __iter__(self): + return iter(self.source) + + def __getattr__(self, name): + return getattr(self.source, name) diff --git a/venv2/lib/python2.7/site-packages/pip/_vendor/html5lib/filters/inject_meta_charset.py b/venv2/lib/python2.7/site-packages/pip/_vendor/html5lib/filters/inject_meta_charset.py new file mode 100644 index 0000000..aefb5c8 --- /dev/null +++ b/venv2/lib/python2.7/site-packages/pip/_vendor/html5lib/filters/inject_meta_charset.py @@ -0,0 +1,73 @@ +from __future__ import absolute_import, division, unicode_literals + +from . import base + + +class Filter(base.Filter): + """Injects ``<meta charset=ENCODING>`` tag into head of document""" + def __init__(self, source, encoding): + """Creates a Filter + + :arg source: the source token stream + + :arg encoding: the encoding to set + + """ + base.Filter.__init__(self, source) + self.encoding = encoding + + def __iter__(self): + state = "pre_head" + meta_found = (self.encoding is None) + pending = [] + + for token in base.Filter.__iter__(self): + type = token["type"] + if type == "StartTag": + if token["name"].lower() == "head": + state = "in_head" + + elif type == "EmptyTag": + if token["name"].lower() == "meta": + # replace charset with actual encoding + has_http_equiv_content_type = False + for (namespace, name), value in token["data"].items(): + if namespace is not None: + continue + elif name.lower() == 'charset': + token["data"][(namespace, name)] = self.encoding + meta_found = True + break + elif name == 'http-equiv' and value.lower() == 'content-type': + has_http_equiv_content_type = True + else: + if has_http_equiv_content_type and (None, "content") in token["data"]: + token["data"][(None, "content")] = 'text/html; charset=%s' % self.encoding + meta_found = True + + elif token["name"].lower() == "head" and not meta_found: + # insert meta into empty head + yield {"type": "StartTag", "name": "head", + "data": token["data"]} + yield {"type": "EmptyTag", "name": "meta", + "data": {(None, "charset"): self.encoding}} + yield {"type": "EndTag", "name": "head"} + meta_found = True + continue + + elif type == "EndTag": + if token["name"].lower() == "head" and pending: + # insert meta into head (if necessary) and flush pending queue + yield pending.pop(0) + if not meta_found: + yield {"type": "EmptyTag", "name": "meta", + "data": {(None, "charset"): self.encoding}} + while pending: + yield pending.pop(0) + meta_found = True + state = "post_head" + + if state == "in_head": + pending.append(token) + else: + yield token diff --git a/venv2/lib/python2.7/site-packages/pip/_vendor/html5lib/filters/lint.py b/venv2/lib/python2.7/site-packages/pip/_vendor/html5lib/filters/lint.py new file mode 100644 index 0000000..fcc07ee --- /dev/null +++ b/venv2/lib/python2.7/site-packages/pip/_vendor/html5lib/filters/lint.py @@ -0,0 +1,93 @@ +from __future__ import absolute_import, division, unicode_literals + +from pip._vendor.six import text_type + +from . import base +from ..constants import namespaces, voidElements + +from ..constants import spaceCharacters +spaceCharacters = "".join(spaceCharacters) + + +class Filter(base.Filter): + """Lints the token stream for errors + + If it finds any errors, it'll raise an ``AssertionError``. + + """ + def __init__(self, source, require_matching_tags=True): + """Creates a Filter + + :arg source: the source token stream + + :arg require_matching_tags: whether or not to require matching tags + + """ + super(Filter, self).__init__(source) + self.require_matching_tags = require_matching_tags + + def __iter__(self): + open_elements = [] + for token in base.Filter.__iter__(self): + type = token["type"] + if type in ("StartTag", "EmptyTag"): + namespace = token["namespace"] + name = token["name"] + assert namespace is None or isinstance(namespace, text_type) + assert namespace != "" + assert isinstance(name, text_type) + assert name != "" + assert isinstance(token["data"], dict) + if (not namespace or namespace == namespaces["html"]) and name in voidElements: + assert type == "EmptyTag" + else: + assert type == "StartTag" + if type == "StartTag" and self.require_matching_tags: + open_elements.append((namespace, name)) + for (namespace, name), value in token["data"].items(): + assert namespace is None or isinstance(namespace, text_type) + assert namespace != "" + assert isinstance(name, text_type) + assert name != "" + assert isinstance(value, text_type) + + elif type == "EndTag": + namespace = token["namespace"] + name = token["name"] + assert namespace is None or isinstance(namespace, text_type) + assert namespace != "" + assert isinstance(name, text_type) + assert name != "" + if (not namespace or namespace == namespaces["html"]) and name in voidElements: + assert False, "Void element reported as EndTag token: %(tag)s" % {"tag": name} + elif self.require_matching_tags: + start = open_elements.pop() + assert start == (namespace, name) + + elif type == "Comment": + data = token["data"] + assert isinstance(data, text_type) + + elif type in ("Characters", "SpaceCharacters"): + data = token["data"] + assert isinstance(data, text_type) + assert data != "" + if type == "SpaceCharacters": + assert data.strip(spaceCharacters) == "" + + elif type == "Doctype": + name = token["name"] + assert name is None or isinstance(name, text_type) + assert token["publicId"] is None or isinstance(name, text_type) + assert token["systemId"] is None or isinstance(name, text_type) + + elif type == "Entity": + assert isinstance(token["name"], text_type) + + elif type == "SerializerError": + assert isinstance(token["data"], text_type) + + else: + assert False, "Unknown token type: %(type)s" % {"type": type} + + yield token diff --git a/venv2/lib/python2.7/site-packages/pip/_vendor/html5lib/filters/optionaltags.py b/venv2/lib/python2.7/site-packages/pip/_vendor/html5lib/filters/optionaltags.py new file mode 100644 index 0000000..4a86501 --- /dev/null +++ b/venv2/lib/python2.7/site-packages/pip/_vendor/html5lib/filters/optionaltags.py @@ -0,0 +1,207 @@ +from __future__ import absolute_import, division, unicode_literals + +from . import base + + +class Filter(base.Filter): + """Removes optional tags from the token stream""" + def slider(self): + previous1 = previous2 = None + for token in self.source: + if previous1 is not None: + yield previous2, previous1, token + previous2 = previous1 + previous1 = token + if previous1 is not None: + yield previous2, previous1, None + + def __iter__(self): + for previous, token, next in self.slider(): + type = token["type"] + if type == "StartTag": + if (token["data"] or + not self.is_optional_start(token["name"], previous, next)): + yield token + elif type == "EndTag": + if not self.is_optional_end(token["name"], next): + yield token + else: + yield token + + def is_optional_start(self, tagname, previous, next): + type = next and next["type"] or None + if tagname in 'html': + # An html element's start tag may be omitted if the first thing + # inside the html element is not a space character or a comment. + return type not in ("Comment", "SpaceCharacters") + elif tagname == 'head': + # A head element's start tag may be omitted if the first thing + # inside the head element is an element. + # XXX: we also omit the start tag if the head element is empty + if type in ("StartTag", "EmptyTag"): + return True + elif type == "EndTag": + return next["name"] == "head" + elif tagname == 'body': + # A body element's start tag may be omitted if the first thing + # inside the body element is not a space character or a comment, + # except if the first thing inside the body element is a script + # or style element and the node immediately preceding the body + # element is a head element whose end tag has been omitted. + if type in ("Comment", "SpaceCharacters"): + return False + elif type == "StartTag": + # XXX: we do not look at the preceding event, so we never omit + # the body element's start tag if it's followed by a script or + # a style element. + return next["name"] not in ('script', 'style') + else: + return True + elif tagname == 'colgroup': + # A colgroup element's start tag may be omitted if the first thing + # inside the colgroup element is a col element, and if the element + # is not immediately preceded by another colgroup element whose + # end tag has been omitted. + if type in ("StartTag", "EmptyTag"): + # XXX: we do not look at the preceding event, so instead we never + # omit the colgroup element's end tag when it is immediately + # followed by another colgroup element. See is_optional_end. + return next["name"] == "col" + else: + return False + elif tagname == 'tbody': + # A tbody element's start tag may be omitted if the first thing + # inside the tbody element is a tr element, and if the element is + # not immediately preceded by a tbody, thead, or tfoot element + # whose end tag has been omitted. + if type == "StartTag": + # omit the thead and tfoot elements' end tag when they are + # immediately followed by a tbody element. See is_optional_end. + if previous and previous['type'] == 'EndTag' and \ + previous['name'] in ('tbody', 'thead', 'tfoot'): + return False + return next["name"] == 'tr' + else: + return False + return False + + def is_optional_end(self, tagname, next): + type = next and next["type"] or None + if tagname in ('html', 'head', 'body'): + # An html element's end tag may be omitted if the html element + # is not immediately followed by a space character or a comment. + return type not in ("Comment", "SpaceCharacters") + elif tagname in ('li', 'optgroup', 'tr'): + # A li element's end tag may be omitted if the li element is + # immediately followed by another li element or if there is + # no more content in the parent element. + # An optgroup element's end tag may be omitted if the optgroup + # element is immediately followed by another optgroup element, + # or if there is no more content in the parent element. + # A tr element's end tag may be omitted if the tr element is + # immediately followed by another tr element, or if there is + # no more content in the parent element. + if type == "StartTag": + return next["name"] == tagname + else: + return type == "EndTag" or type is None + elif tagname in ('dt', 'dd'): + # A dt element's end tag may be omitted if the dt element is + # immediately followed by another dt element or a dd element. + # A dd element's end tag may be omitted if the dd element is + # immediately followed by another dd element or a dt element, + # or if there is no more content in the parent element. + if type == "StartTag": + return next["name"] in ('dt', 'dd') + elif tagname == 'dd': + return type == "EndTag" or type is None + else: + return False + elif tagname == 'p': + # A p element's end tag may be omitted if the p element is + # immediately followed by an address, article, aside, + # blockquote, datagrid, dialog, dir, div, dl, fieldset, + # footer, form, h1, h2, h3, h4, h5, h6, header, hr, menu, + # nav, ol, p, pre, section, table, or ul, element, or if + # there is no more content in the parent element. + if type in ("StartTag", "EmptyTag"): + return next["name"] in ('address', 'article', 'aside', + 'blockquote', 'datagrid', 'dialog', + 'dir', 'div', 'dl', 'fieldset', 'footer', + 'form', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', + 'header', 'hr', 'menu', 'nav', 'ol', + 'p', 'pre', 'section', 'table', 'ul') + else: + return type == "EndTag" or type is None + elif tagname == 'option': + # An option element's end tag may be omitted if the option + # element is immediately followed by another option element, + # or if it is immediately followed by an <code>optgroup</code> + # element, or if there is no more content in the parent + # element. + if type == "StartTag": + return next["name"] in ('option', 'optgroup') + else: + return type == "EndTag" or type is None + elif tagname in ('rt', 'rp'): + # An rt element's end tag may be omitted if the rt element is + # immediately followed by an rt or rp element, or if there is + # no more content in the parent element. + # An rp element's end tag may be omitted if the rp element is + # immediately followed by an rt or rp element, or if there is + # no more content in the parent element. + if type == "StartTag": + return next["name"] in ('rt', 'rp') + else: + return type == "EndTag" or type is None + elif tagname == 'colgroup': + # A colgroup element's end tag may be omitted if the colgroup + # element is not immediately followed by a space character or + # a comment. + if type in ("Comment", "SpaceCharacters"): + return False + elif type == "StartTag": + # XXX: we also look for an immediately following colgroup + # element. See is_optional_start. + return next["name"] != 'colgroup' + else: + return True + elif tagname in ('thead', 'tbody'): + # A thead element's end tag may be omitted if the thead element + # is immediately followed by a tbody or tfoot element. + # A tbody element's end tag may be omitted if the tbody element + # is immediately followed by a tbody or tfoot element, or if + # there is no more content in the parent element. + # A tfoot element's end tag may be omitted if the tfoot element + # is immediately followed by a tbody element, or if there is no + # more content in the parent element. + # XXX: we never omit the end tag when the following element is + # a tbody. See is_optional_start. + if type == "StartTag": + return next["name"] in ['tbody', 'tfoot'] + elif tagname == 'tbody': + return type == "EndTag" or type is None + else: + return False + elif tagname == 'tfoot': + # A tfoot element's end tag may be omitted if the tfoot element + # is immediately followed by a tbody element, or if there is no + # more content in the parent element. + # XXX: we never omit the end tag when the following element is + # a tbody. See is_optional_start. + if type == "StartTag": + return next["name"] == 'tbody' + else: + return type == "EndTag" or type is None + elif tagname in ('td', 'th'): + # A td element's end tag may be omitted if the td element is + # immediately followed by a td or th element, or if there is + # no more content in the parent element. + # A th element's end tag may be omitted if the th element is + # immediately followed by a td or th element, or if there is + # no more content in the parent element. + if type == "StartTag": + return next["name"] in ('td', 'th') + else: + return type == "EndTag" or type is None + return False diff --git a/venv2/lib/python2.7/site-packages/pip/_vendor/html5lib/filters/sanitizer.py b/venv2/lib/python2.7/site-packages/pip/_vendor/html5lib/filters/sanitizer.py new file mode 100644 index 0000000..af8e77b --- /dev/null +++ b/venv2/lib/python2.7/site-packages/pip/_vendor/html5lib/filters/sanitizer.py @@ -0,0 +1,896 @@ +from __future__ import absolute_import, division, unicode_literals + +import re +from xml.sax.saxutils import escape, unescape + +from pip._vendor.six.moves import urllib_parse as urlparse + +from . import base +from ..constants import namespaces, prefixes + +__all__ = ["Filter"] + + +allowed_elements = frozenset(( + (namespaces['html'], 'a'), + (namespaces['html'], 'abbr'), + (namespaces['html'], 'acronym'), + (namespaces['html'], 'address'), + (namespaces['html'], 'area'), + (namespaces['html'], 'article'), + (namespaces['html'], 'aside'), + (namespaces['html'], 'audio'), + (namespaces['html'], 'b'), + (namespaces['html'], 'big'), + (namespaces['html'], 'blockquote'), + (namespaces['html'], 'br'), + (namespaces['html'], 'button'), + (namespaces['html'], 'canvas'), + (namespaces['html'], 'caption'), + (namespaces['html'], 'center'), + (namespaces['html'], 'cite'), + (namespaces['html'], 'code'), + (namespaces['html'], 'col'), + (namespaces['html'], 'colgroup'), + (namespaces['html'], 'command'), + (namespaces['html'], 'datagrid'), + (namespaces['html'], 'datalist'), + (namespaces['html'], 'dd'), + (namespaces['html'], 'del'), + (namespaces['html'], 'details'), + (namespaces['html'], 'dfn'), + (namespaces['html'], 'dialog'), + (namespaces['html'], 'dir'), + (namespaces['html'], 'div'), + (namespaces['html'], 'dl'), + (namespaces['html'], 'dt'), + (namespaces['html'], 'em'), + (namespaces['html'], 'event-source'), + (namespaces['html'], 'fieldset'), + (namespaces['html'], 'figcaption'), + (namespaces['html'], 'figure'), + (namespaces['html'], 'footer'), + (namespaces['html'], 'font'), + (namespaces['html'], 'form'), + (namespaces['html'], 'header'), + (namespaces['html'], 'h1'), + (namespaces['html'], 'h2'), + (namespaces['html'], 'h3'), + (namespaces['html'], 'h4'), + (namespaces['html'], 'h5'), + (namespaces['html'], 'h6'), + (namespaces['html'], 'hr'), + (namespaces['html'], 'i'), + (namespaces['html'], 'img'), + (namespaces['html'], 'input'), + (namespaces['html'], 'ins'), + (namespaces['html'], 'keygen'), + (namespaces['html'], 'kbd'), + (namespaces['html'], 'label'), + (namespaces['html'], 'legend'), + (namespaces['html'], 'li'), + (namespaces['html'], 'm'), + (namespaces['html'], 'map'), + (namespaces['html'], 'menu'), + (namespaces['html'], 'meter'), + (namespaces['html'], 'multicol'), + (namespaces['html'], 'nav'), + (namespaces['html'], 'nextid'), + (namespaces['html'], 'ol'), + (namespaces['html'], 'output'), + (namespaces['html'], 'optgroup'), + (namespaces['html'], 'option'), + (namespaces['html'], 'p'), + (namespaces['html'], 'pre'), + (namespaces['html'], 'progress'), + (namespaces['html'], 'q'), + (namespaces['html'], 's'), + (namespaces['html'], 'samp'), + (namespaces['html'], 'section'), + (namespaces['html'], 'select'), + (namespaces['html'], 'small'), + (namespaces['html'], 'sound'), + (namespaces['html'], 'source'), + (namespaces['html'], 'spacer'), + (namespaces['html'], 'span'), + (namespaces['html'], 'strike'), + (namespaces['html'], 'strong'), + (namespaces['html'], 'sub'), + (namespaces['html'], 'sup'), + (namespaces['html'], 'table'), + (namespaces['html'], 'tbody'), + (namespaces['html'], 'td'), + (namespaces['html'], 'textarea'), + (namespaces['html'], 'time'), + (namespaces['html'], 'tfoot'), + (namespaces['html'], 'th'), + (namespaces['html'], 'thead'), + (namespaces['html'], 'tr'), + (namespaces['html'], 'tt'), + (namespaces['html'], 'u'), + (namespaces['html'], 'ul'), + (namespaces['html'], 'var'), + (namespaces['html'], 'video'), + (namespaces['mathml'], 'maction'), + (namespaces['mathml'], 'math'), + (namespaces['mathml'], 'merror'), + (namespaces['mathml'], 'mfrac'), + (namespaces['mathml'], 'mi'), + (namespaces['mathml'], 'mmultiscripts'), + (namespaces['mathml'], 'mn'), + (namespaces['mathml'], 'mo'), + (namespaces['mathml'], 'mover'), + (namespaces['mathml'], 'mpadded'), + (namespaces['mathml'], 'mphantom'), + (namespaces['mathml'], 'mprescripts'), + (namespaces['mathml'], 'mroot'), + (namespaces['mathml'], 'mrow'), + (namespaces['mathml'], 'mspace'), + (namespaces['mathml'], 'msqrt'), + (namespaces['mathml'], 'mstyle'), + (namespaces['mathml'], 'msub'), + (namespaces['mathml'], 'msubsup'), + (namespaces['mathml'], 'msup'), + (namespaces['mathml'], 'mtable'), + (namespaces['mathml'], 'mtd'), + (namespaces['mathml'], 'mtext'), + (namespaces['mathml'], 'mtr'), + (namespaces['mathml'], 'munder'), + (namespaces['mathml'], 'munderover'), + (namespaces['mathml'], 'none'), + (namespaces['svg'], 'a'), + (namespaces['svg'], 'animate'), + (namespaces['svg'], 'animateColor'), + (namespaces['svg'], 'animateMotion'), + (namespaces['svg'], 'animateTransform'), + (namespaces['svg'], 'clipPath'), + (namespaces['svg'], 'circle'), + (namespaces['svg'], 'defs'), + (namespaces['svg'], 'desc'), + (namespaces['svg'], 'ellipse'), + (namespaces['svg'], 'font-face'), + (namespaces['svg'], 'font-face-name'), + (namespaces['svg'], 'font-face-src'), + (namespaces['svg'], 'g'), + (namespaces['svg'], 'glyph'), + (namespaces['svg'], 'hkern'), + (namespaces['svg'], 'linearGradient'), + (namespaces['svg'], 'line'), + (namespaces['svg'], 'marker'), + (namespaces['svg'], 'metadata'), + (namespaces['svg'], 'missing-glyph'), + (namespaces['svg'], 'mpath'), + (namespaces['svg'], 'path'), + (namespaces['svg'], 'polygon'), + (namespaces['svg'], 'polyline'), + (namespaces['svg'], 'radialGradient'), + (namespaces['svg'], 'rect'), + (namespaces['svg'], 'set'), + (namespaces['svg'], 'stop'), + (namespaces['svg'], 'svg'), + (namespaces['svg'], 'switch'), + (namespaces['svg'], 'text'), + (namespaces['svg'], 'title'), + (namespaces['svg'], 'tspan'), + (namespaces['svg'], 'use'), +)) + +allowed_attributes = frozenset(( + # HTML attributes + (None, 'abbr'), + (None, 'accept'), + (None, 'accept-charset'), + (None, 'accesskey'), + (None, 'action'), + (None, 'align'), + (None, 'alt'), + (None, 'autocomplete'), + (None, 'autofocus'), + (None, 'axis'), + (None, 'background'), + (None, 'balance'), + (None, 'bgcolor'), + (None, 'bgproperties'), + (None, 'border'), + (None, 'bordercolor'), + (None, 'bordercolordark'), + (None, 'bordercolorlight'), + (None, 'bottompadding'), + (None, 'cellpadding'), + (None, 'cellspacing'), + (None, 'ch'), + (None, 'challenge'), + (None, 'char'), + (None, 'charoff'), + (None, 'choff'), + (None, 'charset'), + (None, 'checked'), + (None, 'cite'), + (None, 'class'), + (None, 'clear'), + (None, 'color'), + (None, 'cols'), + (None, 'colspan'), + (None, 'compact'), + (None, 'contenteditable'), + (None, 'controls'), + (None, 'coords'), + (None, 'data'), + (None, 'datafld'), + (None, 'datapagesize'), + (None, 'datasrc'), + (None, 'datetime'), + (None, 'default'), + (None, 'delay'), + (None, 'dir'), + (None, 'disabled'), + (None, 'draggable'), + (None, 'dynsrc'), + (None, 'enctype'), + (None, 'end'), + (None, 'face'), + (None, 'for'), + (None, 'form'), + (None, 'frame'), + (None, 'galleryimg'), + (None, 'gutter'), + (None, 'headers'), + (None, 'height'), + (None, 'hidefocus'), + (None, 'hidden'), + (None, 'high'), + (None, 'href'), + (None, 'hreflang'), + (None, 'hspace'), + (None, 'icon'), + (None, 'id'), + (None, 'inputmode'), + (None, 'ismap'), + (None, 'keytype'), + (None, 'label'), + (None, 'leftspacing'), + (None, 'lang'), + (None, 'list'), + (None, 'longdesc'), + (None, 'loop'), + (None, 'loopcount'), + (None, 'loopend'), + (None, 'loopstart'), + (None, 'low'), + (None, 'lowsrc'), + (None, 'max'), + (None, 'maxlength'), + (None, 'media'), + (None, 'method'), + (None, 'min'), + (None, 'multiple'), + (None, 'name'), + (None, 'nohref'), + (None, 'noshade'), + (None, 'nowrap'), + (None, 'open'), + (None, 'optimum'), + (None, 'pattern'), + (None, 'ping'), + (None, 'point-size'), + (None, 'poster'), + (None, 'pqg'), + (None, 'preload'), + (None, 'prompt'), + (None, 'radiogroup'), + (None, 'readonly'), + (None, 'rel'), + (None, 'repeat-max'), + (None, 'repeat-min'), + (None, 'replace'), + (None, 'required'), + (None, 'rev'), + (None, 'rightspacing'), + (None, 'rows'), + (None, 'rowspan'), + (None, 'rules'), + (None, 'scope'), + (None, 'selected'), + (None, 'shape'), + (None, 'size'), + (None, 'span'), + (None, 'src'), + (None, 'start'), + (None, 'step'), + (None, 'style'), + (None, 'summary'), + (None, 'suppress'), + (None, 'tabindex'), + (None, 'target'), + (None, 'template'), + (None, 'title'), + (None, 'toppadding'), + (None, 'type'), + (None, 'unselectable'), + (None, 'usemap'), + (None, 'urn'), + (None, 'valign'), + (None, 'value'), + (None, 'variable'), + (None, 'volume'), + (None, 'vspace'), + (None, 'vrml'), + (None, 'width'), + (None, 'wrap'), + (namespaces['xml'], 'lang'), + # MathML attributes + (None, 'actiontype'), + (None, 'align'), + (None, 'columnalign'), + (None, 'columnalign'), + (None, 'columnalign'), + (None, 'columnlines'), + (None, 'columnspacing'), + (None, 'columnspan'), + (None, 'depth'), + (None, 'display'), + (None, 'displaystyle'), + (None, 'equalcolumns'), + (None, 'equalrows'), + (None, 'fence'), + (None, 'fontstyle'), + (None, 'fontweight'), + (None, 'frame'), + (None, 'height'), + (None, 'linethickness'), + (None, 'lspace'), + (None, 'mathbackground'), + (None, 'mathcolor'), + (None, 'mathvariant'), + (None, 'mathvariant'), + (None, 'maxsize'), + (None, 'minsize'), + (None, 'other'), + (None, 'rowalign'), + (None, 'rowalign'), + (None, 'rowalign'), + (None, 'rowlines'), + (None, 'rowspacing'), + (None, 'rowspan'), + (None, 'rspace'), + (None, 'scriptlevel'), + (None, 'selection'), + (None, 'separator'), + (None, 'stretchy'), + (None, 'width'), + (None, 'width'), + (namespaces['xlink'], 'href'), + (namespaces['xlink'], 'show'), + (namespaces['xlink'], 'type'), + # SVG attributes + (None, 'accent-height'), + (None, 'accumulate'), + (None, 'additive'), + (None, 'alphabetic'), + (None, 'arabic-form'), + (None, 'ascent'), + (None, 'attributeName'), + (None, 'attributeType'), + (None, 'baseProfile'), + (None, 'bbox'), + (None, 'begin'), + (None, 'by'), + (None, 'calcMode'), + (None, 'cap-height'), + (None, 'class'), + (None, 'clip-path'), + (None, 'color'), + (None, 'color-rendering'), + (None, 'content'), + (None, 'cx'), + (None, 'cy'), + (None, 'd'), + (None, 'dx'), + (None, 'dy'), + (None, 'descent'), + (None, 'display'), + (None, 'dur'), + (None, 'end'), + (None, 'fill'), + (None, 'fill-opacity'), + (None, 'fill-rule'), + (None, 'font-family'), + (None, 'font-size'), + (None, 'font-stretch'), + (None, 'font-style'), + (None, 'font-variant'), + (None, 'font-weight'), + (None, 'from'), + (None, 'fx'), + (None, 'fy'), + (None, 'g1'), + (None, 'g2'), + (None, 'glyph-name'), + (None, 'gradientUnits'), + (None, 'hanging'), + (None, 'height'), + (None, 'horiz-adv-x'), + (None, 'horiz-origin-x'), + (None, 'id'), + (None, 'ideographic'), + (None, 'k'), + (None, 'keyPoints'), + (None, 'keySplines'), + (None, 'keyTimes'), + (None, 'lang'), + (None, 'marker-end'), + (None, 'marker-mid'), + (None, 'marker-start'), + (None, 'markerHeight'), + (None, 'markerUnits'), + (None, 'markerWidth'), + (None, 'mathematical'), + (None, 'max'), + (None, 'min'), + (None, 'name'), + (None, 'offset'), + (None, 'opacity'), + (None, 'orient'), + (None, 'origin'), + (None, 'overline-position'), + (None, 'overline-thickness'), + (None, 'panose-1'), + (None, 'path'), + (None, 'pathLength'), + (None, 'points'), + (None, 'preserveAspectRatio'), + (None, 'r'), + (None, 'refX'), + (None, 'refY'), + (None, 'repeatCount'), + (None, 'repeatDur'), + (None, 'requiredExtensions'), + (None, 'requiredFeatures'), + (None, 'restart'), + (None, 'rotate'), + (None, 'rx'), + (None, 'ry'), + (None, 'slope'), + (None, 'stemh'), + (None, 'stemv'), + (None, 'stop-color'), + (None, 'stop-opacity'), + (None, 'strikethrough-position'), + (None, 'strikethrough-thickness'), + (None, 'stroke'), + (None, 'stroke-dasharray'), + (None, 'stroke-dashoffset'), + (None, 'stroke-linecap'), + (None, 'stroke-linejoin'), + (None, 'stroke-miterlimit'), + (None, 'stroke-opacity'), + (None, 'stroke-width'), + (None, 'systemLanguage'), + (None, 'target'), + (None, 'text-anchor'), + (None, 'to'), + (None, 'transform'), + (None, 'type'), + (None, 'u1'), + (None, 'u2'), + (None, 'underline-position'), + (None, 'underline-thickness'), + (None, 'unicode'), + (None, 'unicode-range'), + (None, 'units-per-em'), + (None, 'values'), + (None, 'version'), + (None, 'viewBox'), + (None, 'visibility'), + (None, 'width'), + (None, 'widths'), + (None, 'x'), + (None, 'x-height'), + (None, 'x1'), + (None, 'x2'), + (namespaces['xlink'], 'actuate'), + (namespaces['xlink'], 'arcrole'), + (namespaces['xlink'], 'href'), + (namespaces['xlink'], 'role'), + (namespaces['xlink'], 'show'), + (namespaces['xlink'], 'title'), + (namespaces['xlink'], 'type'), + (namespaces['xml'], 'base'), + (namespaces['xml'], 'lang'), + (namespaces['xml'], 'space'), + (None, 'y'), + (None, 'y1'), + (None, 'y2'), + (None, 'zoomAndPan'), +)) + +attr_val_is_uri = frozenset(( + (None, 'href'), + (None, 'src'), + (None, 'cite'), + (None, 'action'), + (None, 'longdesc'), + (None, 'poster'), + (None, 'background'), + (None, 'datasrc'), + (None, 'dynsrc'), + (None, 'lowsrc'), + (None, 'ping'), + (namespaces['xlink'], 'href'), + (namespaces['xml'], 'base'), +)) + +svg_attr_val_allows_ref = frozenset(( + (None, 'clip-path'), + (None, 'color-profile'), + (None, 'cursor'), + (None, 'fill'), + (None, 'filter'), + (None, 'marker'), + (None, 'marker-start'), + (None, 'marker-mid'), + (None, 'marker-end'), + (None, 'mask'), + (None, 'stroke'), +)) + +svg_allow_local_href = frozenset(( + (None, 'altGlyph'), + (None, 'animate'), + (None, 'animateColor'), + (None, 'animateMotion'), + (None, 'animateTransform'), + (None, 'cursor'), + (None, 'feImage'), + (None, 'filter'), + (None, 'linearGradient'), + (None, 'pattern'), + (None, 'radialGradient'), + (None, 'textpath'), + (None, 'tref'), + (None, 'set'), + (None, 'use') +)) + +allowed_css_properties = frozenset(( + 'azimuth', + 'background-color', + 'border-bottom-color', + 'border-collapse', + 'border-color', + 'border-left-color', + 'border-right-color', + 'border-top-color', + 'clear', + 'color', + 'cursor', + 'direction', + 'display', + 'elevation', + 'float', + 'font', + 'font-family', + 'font-size', + 'font-style', + 'font-variant', + 'font-weight', + 'height', + 'letter-spacing', + 'line-height', + 'overflow', + 'pause', + 'pause-after', + 'pause-before', + 'pitch', + 'pitch-range', + 'richness', + 'speak', + 'speak-header', + 'speak-numeral', + 'speak-punctuation', + 'speech-rate', + 'stress', + 'text-align', + 'text-decoration', + 'text-indent', + 'unicode-bidi', + 'vertical-align', + 'voice-family', + 'volume', + 'white-space', + 'width', +)) + +allowed_css_keywords = frozenset(( + 'auto', + 'aqua', + 'black', + 'block', + 'blue', + 'bold', + 'both', + 'bottom', + 'brown', + 'center', + 'collapse', + 'dashed', + 'dotted', + 'fuchsia', + 'gray', + 'green', + '!important', + 'italic', + 'left', + 'lime', + 'maroon', + 'medium', + 'none', + 'navy', + 'normal', + 'nowrap', + 'olive', + 'pointer', + 'purple', + 'red', + 'right', + 'solid', + 'silver', + 'teal', + 'top', + 'transparent', + 'underline', + 'white', + 'yellow', +)) + +allowed_svg_properties = frozenset(( + 'fill', + 'fill-opacity', + 'fill-rule', + 'stroke', + 'stroke-width', + 'stroke-linecap', + 'stroke-linejoin', + 'stroke-opacity', +)) + +allowed_protocols = frozenset(( + 'ed2k', + 'ftp', + 'http', + 'https', + 'irc', + 'mailto', + 'news', + 'gopher', + 'nntp', + 'telnet', + 'webcal', + 'xmpp', + 'callto', + 'feed', + 'urn', + 'aim', + 'rsync', + 'tag', + 'ssh', + 'sftp', + 'rtsp', + 'afs', + 'data', +)) + +allowed_content_types = frozenset(( + 'image/png', + 'image/jpeg', + 'image/gif', + 'image/webp', + 'image/bmp', + 'text/plain', +)) + + +data_content_type = re.compile(r''' + ^ + # Match a content type <application>/<type> + (?P<content_type>[-a-zA-Z0-9.]+/[-a-zA-Z0-9.]+) + # Match any character set and encoding + (?:(?:;charset=(?:[-a-zA-Z0-9]+)(?:;(?:base64))?) + |(?:;(?:base64))?(?:;charset=(?:[-a-zA-Z0-9]+))?) + # Assume the rest is data + ,.* + $ + ''', + re.VERBOSE) + + +class Filter(base.Filter): + """Sanitizes token stream of XHTML+MathML+SVG and of inline style attributes""" + def __init__(self, + source, + allowed_elements=allowed_elements, + allowed_attributes=allowed_attributes, + allowed_css_properties=allowed_css_properties, + allowed_css_keywords=allowed_css_keywords, + allowed_svg_properties=allowed_svg_properties, + allowed_protocols=allowed_protocols, + allowed_content_types=allowed_content_types, + attr_val_is_uri=attr_val_is_uri, + svg_attr_val_allows_ref=svg_attr_val_allows_ref, + svg_allow_local_href=svg_allow_local_href): + """Creates a Filter + + :arg allowed_elements: set of elements to allow--everything else will + be escaped + + :arg allowed_attributes: set of attributes to allow in + elements--everything else will be stripped + + :arg allowed_css_properties: set of CSS properties to allow--everything + else will be stripped + + :arg allowed_css_keywords: set of CSS keywords to allow--everything + else will be stripped + + :arg allowed_svg_properties: set of SVG properties to allow--everything + else will be removed + + :arg allowed_protocols: set of allowed protocols for URIs + + :arg allowed_content_types: set of allowed content types for ``data`` URIs. + + :arg attr_val_is_uri: set of attributes that have URI values--values + that have a scheme not listed in ``allowed_protocols`` are removed + + :arg svg_attr_val_allows_ref: set of SVG attributes that can have + references + + :arg svg_allow_local_href: set of SVG elements that can have local + hrefs--these are removed + + """ + super(Filter, self).__init__(source) + self.allowed_elements = allowed_elements + self.allowed_attributes = allowed_attributes + self.allowed_css_properties = allowed_css_properties + self.allowed_css_keywords = allowed_css_keywords + self.allowed_svg_properties = allowed_svg_properties + self.allowed_protocols = allowed_protocols + self.allowed_content_types = allowed_content_types + self.attr_val_is_uri = attr_val_is_uri + self.svg_attr_val_allows_ref = svg_attr_val_allows_ref + self.svg_allow_local_href = svg_allow_local_href + + def __iter__(self): + for token in base.Filter.__iter__(self): + token = self.sanitize_token(token) + if token: + yield token + + # Sanitize the +html+, escaping all elements not in ALLOWED_ELEMENTS, and + # stripping out all attributes not in ALLOWED_ATTRIBUTES. Style attributes + # are parsed, and a restricted set, specified by ALLOWED_CSS_PROPERTIES and + # ALLOWED_CSS_KEYWORDS, are allowed through. attributes in ATTR_VAL_IS_URI + # are scanned, and only URI schemes specified in ALLOWED_PROTOCOLS are + # allowed. + # + # sanitize_html('<script> do_nasty_stuff() </script>') + # => <script> do_nasty_stuff() </script> + # sanitize_html('<a href="javascript: sucker();">Click here for $100</a>') + # => <a>Click here for $100</a> + def sanitize_token(self, token): + + # accommodate filters which use token_type differently + token_type = token["type"] + if token_type in ("StartTag", "EndTag", "EmptyTag"): + name = token["name"] + namespace = token["namespace"] + if ((namespace, name) in self.allowed_elements or + (namespace is None and + (namespaces["html"], name) in self.allowed_elements)): + return self.allowed_token(token) + else: + return self.disallowed_token(token) + elif token_type == "Comment": + pass + else: + return token + + def allowed_token(self, token): + if "data" in token: + attrs = token["data"] + attr_names = set(attrs.keys()) + + # Remove forbidden attributes + for to_remove in (attr_names - self.allowed_attributes): + del token["data"][to_remove] + attr_names.remove(to_remove) + + # Remove attributes with disallowed URL values + for attr in (attr_names & self.attr_val_is_uri): + assert attr in attrs + # I don't have a clue where this regexp comes from or why it matches those + # characters, nor why we call unescape. I just know it's always been here. + # Should you be worried by this comment in a sanitizer? Yes. On the other hand, all + # this will do is remove *more* than it otherwise would. + val_unescaped = re.sub("[`\x00-\x20\x7f-\xa0\\s]+", '', + unescape(attrs[attr])).lower() + # remove replacement characters from unescaped characters + val_unescaped = val_unescaped.replace("\ufffd", "") + try: + uri = urlparse.urlparse(val_unescaped) + except ValueError: + uri = None + del attrs[attr] + if uri and uri.scheme: + if uri.scheme not in self.allowed_protocols: + del attrs[attr] + if uri.scheme == 'data': + m = data_content_type.match(uri.path) + if not m: + del attrs[attr] + elif m.group('content_type') not in self.allowed_content_types: + del attrs[attr] + + for attr in self.svg_attr_val_allows_ref: + if attr in attrs: + attrs[attr] = re.sub(r'url\s*\(\s*[^#\s][^)]+?\)', + ' ', + unescape(attrs[attr])) + if (token["name"] in self.svg_allow_local_href and + (namespaces['xlink'], 'href') in attrs and re.search(r'^\s*[^#\s].*', + attrs[(namespaces['xlink'], 'href')])): + del attrs[(namespaces['xlink'], 'href')] + if (None, 'style') in attrs: + attrs[(None, 'style')] = self.sanitize_css(attrs[(None, 'style')]) + token["data"] = attrs + return token + + def disallowed_token(self, token): + token_type = token["type"] + if token_type == "EndTag": + token["data"] = "</%s>" % token["name"] + elif token["data"]: + assert token_type in ("StartTag", "EmptyTag") + attrs = [] + for (ns, name), v in token["data"].items(): + attrs.append(' %s="%s"' % (name if ns is None else "%s:%s" % (prefixes[ns], name), escape(v))) + token["data"] = "<%s%s>" % (token["name"], ''.join(attrs)) + else: + token["data"] = "<%s>" % token["name"] + if token.get("selfClosing"): + token["data"] = token["data"][:-1] + "/>" + + token["type"] = "Characters" + + del token["name"] + return token + + def sanitize_css(self, style): + # disallow urls + style = re.compile(r'url\s*\(\s*[^\s)]+?\s*\)\s*').sub(' ', style) + + # gauntlet + if not re.match(r"""^([:,;#%.\sa-zA-Z0-9!]|\w-\w|'[\s\w]+'|"[\s\w]+"|\([\d,\s]+\))*$""", style): + return '' + if not re.match(r"^\s*([-\w]+\s*:[^:;]*(;\s*|$))*$", style): + return '' + + clean = [] + for prop, value in re.findall(r"([-\w]+)\s*:\s*([^:;]*)", style): + if not value: + continue + if prop.lower() in self.allowed_css_properties: + clean.append(prop + ': ' + value + ';') + elif prop.split('-')[0].lower() in ['background', 'border', 'margin', + 'padding']: + for keyword in value.split(): + if keyword not in self.allowed_css_keywords and \ + not re.match(r"^(#[0-9a-fA-F]+|rgb\(\d+%?,\d*%?,?\d*%?\)?|\d{0,2}\.?\d{0,2}(cm|em|ex|in|mm|pc|pt|px|%|,|\))?)$", keyword): # noqa + break + else: + clean.append(prop + ': ' + value + ';') + elif prop.lower() in self.allowed_svg_properties: + clean.append(prop + ': ' + value + ';') + + return ' '.join(clean) diff --git a/venv2/lib/python2.7/site-packages/pip/_vendor/html5lib/filters/whitespace.py b/venv2/lib/python2.7/site-packages/pip/_vendor/html5lib/filters/whitespace.py new file mode 100644 index 0000000..0d12584 --- /dev/null +++ b/venv2/lib/python2.7/site-packages/pip/_vendor/html5lib/filters/whitespace.py @@ -0,0 +1,38 @@ +from __future__ import absolute_import, division, unicode_literals + +import re + +from . import base +from ..constants import rcdataElements, spaceCharacters +spaceCharacters = "".join(spaceCharacters) + +SPACES_REGEX = re.compile("[%s]+" % spaceCharacters) + + +class Filter(base.Filter): + """Collapses whitespace except in pre, textarea, and script elements""" + spacePreserveElements = frozenset(["pre", "textarea"] + list(rcdataElements)) + + def __iter__(self): + preserve = 0 + for token in base.Filter.__iter__(self): + type = token["type"] + if type == "StartTag" \ + and (preserve or token["name"] in self.spacePreserveElements): + preserve += 1 + + elif type == "EndTag" and preserve: + preserve -= 1 + + elif not preserve and type == "SpaceCharacters" and token["data"]: + # Test on token["data"] above to not introduce spaces where there were not + token["data"] = " " + + elif not preserve and type == "Characters": + token["data"] = collapse_spaces(token["data"]) + + yield token + + +def collapse_spaces(text): + return SPACES_REGEX.sub(' ', text) diff --git a/venv2/lib/python2.7/site-packages/pip/_vendor/html5lib/html5parser.py b/venv2/lib/python2.7/site-packages/pip/_vendor/html5lib/html5parser.py new file mode 100644 index 0000000..ae41a13 --- /dev/null +++ b/venv2/lib/python2.7/site-packages/pip/_vendor/html5lib/html5parser.py @@ -0,0 +1,2791 @@ +from __future__ import absolute_import, division, unicode_literals +from pip._vendor.six import with_metaclass, viewkeys + +import types +from collections import OrderedDict + +from . import _inputstream +from . import _tokenizer + +from . import treebuilders +from .treebuilders.base import Marker + +from . import _utils +from .constants import ( + spaceCharacters, asciiUpper2Lower, + specialElements, headingElements, cdataElements, rcdataElements, + tokenTypes, tagTokenTypes, + namespaces, + htmlIntegrationPointElements, mathmlTextIntegrationPointElements, + adjustForeignAttributes as adjustForeignAttributesMap, + adjustMathMLAttributes, adjustSVGAttributes, + E, + _ReparseException +) + + +def parse(doc, treebuilder="etree", namespaceHTMLElements=True, **kwargs): + """Parse an HTML document as a string or file-like object into a tree + + :arg doc: the document to parse as a string or file-like object + + :arg treebuilder: the treebuilder to use when parsing + + :arg namespaceHTMLElements: whether or not to namespace HTML elements + + :returns: parsed tree + + Example: + + >>> from html5lib.html5parser import parse + >>> parse('<html><body><p>This is a doc</p></body></html>') + <Element u'{http://www.w3.org/1999/xhtml}html' at 0x7feac4909db0> + + """ + tb = treebuilders.getTreeBuilder(treebuilder) + p = HTMLParser(tb, namespaceHTMLElements=namespaceHTMLElements) + return p.parse(doc, **kwargs) + + +def parseFragment(doc, container="div", treebuilder="etree", namespaceHTMLElements=True, **kwargs): + """Parse an HTML fragment as a string or file-like object into a tree + + :arg doc: the fragment to parse as a string or file-like object + + :arg container: the container context to parse the fragment in + + :arg treebuilder: the treebuilder to use when parsing + + :arg namespaceHTMLElements: whether or not to namespace HTML elements + + :returns: parsed tree + + Example: + + >>> from html5lib.html5libparser import parseFragment + >>> parseFragment('<b>this is a fragment</b>') + <Element u'DOCUMENT_FRAGMENT' at 0x7feac484b090> + + """ + tb = treebuilders.getTreeBuilder(treebuilder) + p = HTMLParser(tb, namespaceHTMLElements=namespaceHTMLElements) + return p.parseFragment(doc, container=container, **kwargs) + + +def method_decorator_metaclass(function): + class Decorated(type): + def __new__(meta, classname, bases, classDict): + for attributeName, attribute in classDict.items(): + if isinstance(attribute, types.FunctionType): + attribute = function(attribute) + + classDict[attributeName] = attribute + return type.__new__(meta, classname, bases, classDict) + return Decorated + + +class HTMLParser(object): + """HTML parser + + Generates a tree structure from a stream of (possibly malformed) HTML. + + """ + + def __init__(self, tree=None, strict=False, namespaceHTMLElements=True, debug=False): + """ + :arg tree: a treebuilder class controlling the type of tree that will be + returned. Built in treebuilders can be accessed through + html5lib.treebuilders.getTreeBuilder(treeType) + + :arg strict: raise an exception when a parse error is encountered + + :arg namespaceHTMLElements: whether or not to namespace HTML elements + + :arg debug: whether or not to enable debug mode which logs things + + Example: + + >>> from html5lib.html5parser import HTMLParser + >>> parser = HTMLParser() # generates parser with etree builder + >>> parser = HTMLParser('lxml', strict=True) # generates parser with lxml builder which is strict + + """ + + # Raise an exception on the first error encountered + self.strict = strict + + if tree is None: + tree = treebuilders.getTreeBuilder("etree") + self.tree = tree(namespaceHTMLElements) + self.errors = [] + + self.phases = dict([(name, cls(self, self.tree)) for name, cls in + getPhases(debug).items()]) + + def _parse(self, stream, innerHTML=False, container="div", scripting=False, **kwargs): + + self.innerHTMLMode = innerHTML + self.container = container + self.scripting = scripting + self.tokenizer = _tokenizer.HTMLTokenizer(stream, parser=self, **kwargs) + self.reset() + + try: + self.mainLoop() + except _ReparseException: + self.reset() + self.mainLoop() + + def reset(self): + self.tree.reset() + self.firstStartTag = False + self.errors = [] + self.log = [] # only used with debug mode + # "quirks" / "limited quirks" / "no quirks" + self.compatMode = "no quirks" + + if self.innerHTMLMode: + self.innerHTML = self.container.lower() + + if self.innerHTML in cdataElements: + self.tokenizer.state = self.tokenizer.rcdataState + elif self.innerHTML in rcdataElements: + self.tokenizer.state = self.tokenizer.rawtextState + elif self.innerHTML == 'plaintext': + self.tokenizer.state = self.tokenizer.plaintextState + else: + # state already is data state + # self.tokenizer.state = self.tokenizer.dataState + pass + self.phase = self.phases["beforeHtml"] + self.phase.insertHtmlElement() + self.resetInsertionMode() + else: + self.innerHTML = False # pylint:disable=redefined-variable-type + self.phase = self.phases["initial"] + + self.lastPhase = None + + self.beforeRCDataPhase = None + + self.framesetOK = True + + @property + def documentEncoding(self): + """Name of the character encoding that was used to decode the input stream, or + :obj:`None` if that is not determined yet + + """ + if not hasattr(self, 'tokenizer'): + return None + return self.tokenizer.stream.charEncoding[0].name + + def isHTMLIntegrationPoint(self, element): + if (element.name == "annotation-xml" and + element.namespace == namespaces["mathml"]): + return ("encoding" in element.attributes and + element.attributes["encoding"].translate( + asciiUpper2Lower) in + ("text/html", "application/xhtml+xml")) + else: + return (element.namespace, element.name) in htmlIntegrationPointElements + + def isMathMLTextIntegrationPoint(self, element): + return (element.namespace, element.name) in mathmlTextIntegrationPointElements + + def mainLoop(self): + CharactersToken = tokenTypes["Characters"] + SpaceCharactersToken = tokenTypes["SpaceCharacters"] + StartTagToken = tokenTypes["StartTag"] + EndTagToken = tokenTypes["EndTag"] + CommentToken = tokenTypes["Comment"] + DoctypeToken = tokenTypes["Doctype"] + ParseErrorToken = tokenTypes["ParseError"] + + for token in self.normalizedTokens(): + prev_token = None + new_token = token + while new_token is not None: + prev_token = new_token + currentNode = self.tree.openElements[-1] if self.tree.openElements else None + currentNodeNamespace = currentNode.namespace if currentNode else None + currentNodeName = currentNode.name if currentNode else None + + type = new_token["type"] + + if type == ParseErrorToken: + self.parseError(new_token["data"], new_token.get("datavars", {})) + new_token = None + else: + if (len(self.tree.openElements) == 0 or + currentNodeNamespace == self.tree.defaultNamespace or + (self.isMathMLTextIntegrationPoint(currentNode) and + ((type == StartTagToken and + token["name"] not in frozenset(["mglyph", "malignmark"])) or + type in (CharactersToken, SpaceCharactersToken))) or + (currentNodeNamespace == namespaces["mathml"] and + currentNodeName == "annotation-xml" and + type == StartTagToken and + token["name"] == "svg") or + (self.isHTMLIntegrationPoint(currentNode) and + type in (StartTagToken, CharactersToken, SpaceCharactersToken))): + phase = self.phase + else: + phase = self.phases["inForeignContent"] + + if type == CharactersToken: + new_token = phase.processCharacters(new_token) + elif type == SpaceCharactersToken: + new_token = phase.processSpaceCharacters(new_token) + elif type == StartTagToken: + new_token = phase.processStartTag(new_token) + elif type == EndTagToken: + new_token = phase.processEndTag(new_token) + elif type == CommentToken: + new_token = phase.processComment(new_token) + elif type == DoctypeToken: + new_token = phase.processDoctype(new_token) + + if (type == StartTagToken and prev_token["selfClosing"] and + not prev_token["selfClosingAcknowledged"]): + self.parseError("non-void-element-with-trailing-solidus", + {"name": prev_token["name"]}) + + # When the loop finishes it's EOF + reprocess = True + phases = [] + while reprocess: + phases.append(self.phase) + reprocess = self.phase.processEOF() + if reprocess: + assert self.phase not in phases + + def normalizedTokens(self): + for token in self.tokenizer: + yield self.normalizeToken(token) + + def parse(self, stream, *args, **kwargs): + """Parse a HTML document into a well-formed tree + + :arg stream: a file-like object or string containing the HTML to be parsed + + The optional encoding parameter must be a string that indicates + the encoding. If specified, that encoding will be used, + regardless of any BOM or later declaration (such as in a meta + element). + + :arg scripting: treat noscript elements as if JavaScript was turned on + + :returns: parsed tree + + Example: + + >>> from html5lib.html5parser import HTMLParser + >>> parser = HTMLParser() + >>> parser.parse('<html><body><p>This is a doc</p></body></html>') + <Element u'{http://www.w3.org/1999/xhtml}html' at 0x7feac4909db0> + + """ + self._parse(stream, False, None, *args, **kwargs) + return self.tree.getDocument() + + def parseFragment(self, stream, *args, **kwargs): + """Parse a HTML fragment into a well-formed tree fragment + + :arg container: name of the element we're setting the innerHTML + property if set to None, default to 'div' + + :arg stream: a file-like object or string containing the HTML to be parsed + + The optional encoding parameter must be a string that indicates + the encoding. If specified, that encoding will be used, + regardless of any BOM or later declaration (such as in a meta + element) + + :arg scripting: treat noscript elements as if JavaScript was turned on + + :returns: parsed tree + + Example: + + >>> from html5lib.html5libparser import HTMLParser + >>> parser = HTMLParser() + >>> parser.parseFragment('<b>this is a fragment</b>') + <Element u'DOCUMENT_FRAGMENT' at 0x7feac484b090> + + """ + self._parse(stream, True, *args, **kwargs) + return self.tree.getFragment() + + def parseError(self, errorcode="XXX-undefined-error", datavars=None): + # XXX The idea is to make errorcode mandatory. + if datavars is None: + datavars = {} + self.errors.append((self.tokenizer.stream.position(), errorcode, datavars)) + if self.strict: + raise ParseError(E[errorcode] % datavars) + + def normalizeToken(self, token): + # HTML5 specific normalizations to the token stream + if token["type"] == tokenTypes["StartTag"]: + raw = token["data"] + token["data"] = OrderedDict(raw) + if len(raw) > len(token["data"]): + # we had some duplicated attribute, fix so first wins + token["data"].update(raw[::-1]) + + return token + + def adjustMathMLAttributes(self, token): + adjust_attributes(token, adjustMathMLAttributes) + + def adjustSVGAttributes(self, token): + adjust_attributes(token, adjustSVGAttributes) + + def adjustForeignAttributes(self, token): + adjust_attributes(token, adjustForeignAttributesMap) + + def reparseTokenNormal(self, token): + # pylint:disable=unused-argument + self.parser.phase() + + def resetInsertionMode(self): + # The name of this method is mostly historical. (It's also used in the + # specification.) + last = False + newModes = { + "select": "inSelect", + "td": "inCell", + "th": "inCell", + "tr": "inRow", + "tbody": "inTableBody", + "thead": "inTableBody", + "tfoot": "inTableBody", + "caption": "inCaption", + "colgroup": "inColumnGroup", + "table": "inTable", + "head": "inBody", + "body": "inBody", + "frameset": "inFrameset", + "html": "beforeHead" + } + for node in self.tree.openElements[::-1]: + nodeName = node.name + new_phase = None + if node == self.tree.openElements[0]: + assert self.innerHTML + last = True + nodeName = self.innerHTML + # Check for conditions that should only happen in the innerHTML + # case + if nodeName in ("select", "colgroup", "head", "html"): + assert self.innerHTML + + if not last and node.namespace != self.tree.defaultNamespace: + continue + + if nodeName in newModes: + new_phase = self.phases[newModes[nodeName]] + break + elif last: + new_phase = self.phases["inBody"] + break + + self.phase = new_phase + + def parseRCDataRawtext(self, token, contentType): + # Generic RCDATA/RAWTEXT Parsing algorithm + assert contentType in ("RAWTEXT", "RCDATA") + + self.tree.insertElement(token) + + if contentType == "RAWTEXT": + self.tokenizer.state = self.tokenizer.rawtextState + else: + self.tokenizer.state = self.tokenizer.rcdataState + + self.originalPhase = self.phase + + self.phase = self.phases["text"] + + +@_utils.memoize +def getPhases(debug): + def log(function): + """Logger that records which phase processes each token""" + type_names = dict((value, key) for key, value in + tokenTypes.items()) + + def wrapped(self, *args, **kwargs): + if function.__name__.startswith("process") and len(args) > 0: + token = args[0] + try: + info = {"type": type_names[token['type']]} + except: + raise + if token['type'] in tagTokenTypes: + info["name"] = token['name'] + + self.parser.log.append((self.parser.tokenizer.state.__name__, + self.parser.phase.__class__.__name__, + self.__class__.__name__, + function.__name__, + info)) + return function(self, *args, **kwargs) + else: + return function(self, *args, **kwargs) + return wrapped + + def getMetaclass(use_metaclass, metaclass_func): + if use_metaclass: + return method_decorator_metaclass(metaclass_func) + else: + return type + + # pylint:disable=unused-argument + class Phase(with_metaclass(getMetaclass(debug, log))): + """Base class for helper object that implements each phase of processing + """ + + def __init__(self, parser, tree): + self.parser = parser + self.tree = tree + + def processEOF(self): + raise NotImplementedError + + def processComment(self, token): + # For most phases the following is correct. Where it's not it will be + # overridden. + self.tree.insertComment(token, self.tree.openElements[-1]) + + def processDoctype(self, token): + self.parser.parseError("unexpected-doctype") + + def processCharacters(self, token): + self.tree.insertText(token["data"]) + + def processSpaceCharacters(self, token): + self.tree.insertText(token["data"]) + + def processStartTag(self, token): + return self.startTagHandler[token["name"]](token) + + def startTagHtml(self, token): + if not self.parser.firstStartTag and token["name"] == "html": + self.parser.parseError("non-html-root") + # XXX Need a check here to see if the first start tag token emitted is + # this token... If it's not, invoke self.parser.parseError(). + for attr, value in token["data"].items(): + if attr not in self.tree.openElements[0].attributes: + self.tree.openElements[0].attributes[attr] = value + self.parser.firstStartTag = False + + def processEndTag(self, token): + return self.endTagHandler[token["name"]](token) + + class InitialPhase(Phase): + def processSpaceCharacters(self, token): + pass + + def processComment(self, token): + self.tree.insertComment(token, self.tree.document) + + def processDoctype(self, token): + name = token["name"] + publicId = token["publicId"] + systemId = token["systemId"] + correct = token["correct"] + + if (name != "html" or publicId is not None or + systemId is not None and systemId != "about:legacy-compat"): + self.parser.parseError("unknown-doctype") + + if publicId is None: + publicId = "" + + self.tree.insertDoctype(token) + + if publicId != "": + publicId = publicId.translate(asciiUpper2Lower) + + if (not correct or token["name"] != "html" or + publicId.startswith( + ("+//silmaril//dtd html pro v0r11 19970101//", + "-//advasoft ltd//dtd html 3.0 aswedit + extensions//", + "-//as//dtd html 3.0 aswedit + extensions//", + "-//ietf//dtd html 2.0 level 1//", + "-//ietf//dtd html 2.0 level 2//", + "-//ietf//dtd html 2.0 strict level 1//", + "-//ietf//dtd html 2.0 strict level 2//", + "-//ietf//dtd html 2.0 strict//", + "-//ietf//dtd html 2.0//", + "-//ietf//dtd html 2.1e//", + "-//ietf//dtd html 3.0//", + "-//ietf//dtd html 3.2 final//", + "-//ietf//dtd html 3.2//", + "-//ietf//dtd html 3//", + "-//ietf//dtd html level 0//", + "-//ietf//dtd html level 1//", + "-//ietf//dtd html level 2//", + "-//ietf//dtd html level 3//", + "-//ietf//dtd html strict level 0//", + "-//ietf//dtd html strict level 1//", + "-//ietf//dtd html strict level 2//", + "-//ietf//dtd html strict level 3//", + "-//ietf//dtd html strict//", + "-//ietf//dtd html//", + "-//metrius//dtd metrius presentational//", + "-//microsoft//dtd internet explorer 2.0 html strict//", + "-//microsoft//dtd internet explorer 2.0 html//", + "-//microsoft//dtd internet explorer 2.0 tables//", + "-//microsoft//dtd internet explorer 3.0 html strict//", + "-//microsoft//dtd internet explorer 3.0 html//", + "-//microsoft//dtd internet explorer 3.0 tables//", + "-//netscape comm. corp.//dtd html//", + "-//netscape comm. corp.//dtd strict html//", + "-//o'reilly and associates//dtd html 2.0//", + "-//o'reilly and associates//dtd html extended 1.0//", + "-//o'reilly and associates//dtd html extended relaxed 1.0//", + "-//softquad software//dtd hotmetal pro 6.0::19990601::extensions to html 4.0//", + "-//softquad//dtd hotmetal pro 4.0::19971010::extensions to html 4.0//", + "-//spyglass//dtd html 2.0 extended//", + "-//sq//dtd html 2.0 hotmetal + extensions//", + "-//sun microsystems corp.//dtd hotjava html//", + "-//sun microsystems corp.//dtd hotjava strict html//", + "-//w3c//dtd html 3 1995-03-24//", + "-//w3c//dtd html 3.2 draft//", + "-//w3c//dtd html 3.2 final//", + "-//w3c//dtd html 3.2//", + "-//w3c//dtd html 3.2s draft//", + "-//w3c//dtd html 4.0 frameset//", + "-//w3c//dtd html 4.0 transitional//", + "-//w3c//dtd html experimental 19960712//", + "-//w3c//dtd html experimental 970421//", + "-//w3c//dtd w3 html//", + "-//w3o//dtd w3 html 3.0//", + "-//webtechs//dtd mozilla html 2.0//", + "-//webtechs//dtd mozilla html//")) or + publicId in ("-//w3o//dtd w3 html strict 3.0//en//", + "-/w3c/dtd html 4.0 transitional/en", + "html") or + publicId.startswith( + ("-//w3c//dtd html 4.01 frameset//", + "-//w3c//dtd html 4.01 transitional//")) and + systemId is None or + systemId and systemId.lower() == "http://www.ibm.com/data/dtd/v11/ibmxhtml1-transitional.dtd"): + self.parser.compatMode = "quirks" + elif (publicId.startswith( + ("-//w3c//dtd xhtml 1.0 frameset//", + "-//w3c//dtd xhtml 1.0 transitional//")) or + publicId.startswith( + ("-//w3c//dtd html 4.01 frameset//", + "-//w3c//dtd html 4.01 transitional//")) and + systemId is not None): + self.parser.compatMode = "limited quirks" + + self.parser.phase = self.parser.phases["beforeHtml"] + + def anythingElse(self): + self.parser.compatMode = "quirks" + self.parser.phase = self.parser.phases["beforeHtml"] + + def processCharacters(self, token): + self.parser.parseError("expected-doctype-but-got-chars") + self.anythingElse() + return token + + def processStartTag(self, token): + self.parser.parseError("expected-doctype-but-got-start-tag", + {"name": token["name"]}) + self.anythingElse() + return token + + def processEndTag(self, token): + self.parser.parseError("expected-doctype-but-got-end-tag", + {"name": token["name"]}) + self.anythingElse() + return token + + def processEOF(self): + self.parser.parseError("expected-doctype-but-got-eof") + self.anythingElse() + return True + + class BeforeHtmlPhase(Phase): + # helper methods + def insertHtmlElement(self): + self.tree.insertRoot(impliedTagToken("html", "StartTag")) + self.parser.phase = self.parser.phases["beforeHead"] + + # other + def processEOF(self): + self.insertHtmlElement() + return True + + def processComment(self, token): + self.tree.insertComment(token, self.tree.document) + + def processSpaceCharacters(self, token): + pass + + def processCharacters(self, token): + self.insertHtmlElement() + return token + + def processStartTag(self, token): + if token["name"] == "html": + self.parser.firstStartTag = True + self.insertHtmlElement() + return token + + def processEndTag(self, token): + if token["name"] not in ("head", "body", "html", "br"): + self.parser.parseError("unexpected-end-tag-before-html", + {"name": token["name"]}) + else: + self.insertHtmlElement() + return token + + class BeforeHeadPhase(Phase): + def __init__(self, parser, tree): + Phase.__init__(self, parser, tree) + + self.startTagHandler = _utils.MethodDispatcher([ + ("html", self.startTagHtml), + ("head", self.startTagHead) + ]) + self.startTagHandler.default = self.startTagOther + + self.endTagHandler = _utils.MethodDispatcher([ + (("head", "body", "html", "br"), self.endTagImplyHead) + ]) + self.endTagHandler.default = self.endTagOther + + def processEOF(self): + self.startTagHead(impliedTagToken("head", "StartTag")) + return True + + def processSpaceCharacters(self, token): + pass + + def processCharacters(self, token): + self.startTagHead(impliedTagToken("head", "StartTag")) + return token + + def startTagHtml(self, token): + return self.parser.phases["inBody"].processStartTag(token) + + def startTagHead(self, token): + self.tree.insertElement(token) + self.tree.headPointer = self.tree.openElements[-1] + self.parser.phase = self.parser.phases["inHead"] + + def startTagOther(self, token): + self.startTagHead(impliedTagToken("head", "StartTag")) + return token + + def endTagImplyHead(self, token): + self.startTagHead(impliedTagToken("head", "StartTag")) + return token + + def endTagOther(self, token): + self.parser.parseError("end-tag-after-implied-root", + {"name": token["name"]}) + + class InHeadPhase(Phase): + def __init__(self, parser, tree): + Phase.__init__(self, parser, tree) + + self.startTagHandler = _utils.MethodDispatcher([ + ("html", self.startTagHtml), + ("title", self.startTagTitle), + (("noframes", "style"), self.startTagNoFramesStyle), + ("noscript", self.startTagNoscript), + ("script", self.startTagScript), + (("base", "basefont", "bgsound", "command", "link"), + self.startTagBaseLinkCommand), + ("meta", self.startTagMeta), + ("head", self.startTagHead) + ]) + self.startTagHandler.default = self.startTagOther + + self.endTagHandler = _utils.MethodDispatcher([ + ("head", self.endTagHead), + (("br", "html", "body"), self.endTagHtmlBodyBr) + ]) + self.endTagHandler.default = self.endTagOther + + # the real thing + def processEOF(self): + self.anythingElse() + return True + + def processCharacters(self, token): + self.anythingElse() + return token + + def startTagHtml(self, token): + return self.parser.phases["inBody"].processStartTag(token) + + def startTagHead(self, token): + self.parser.parseError("two-heads-are-not-better-than-one") + + def startTagBaseLinkCommand(self, token): + self.tree.insertElement(token) + self.tree.openElements.pop() + token["selfClosingAcknowledged"] = True + + def startTagMeta(self, token): + self.tree.insertElement(token) + self.tree.openElements.pop() + token["selfClosingAcknowledged"] = True + + attributes = token["data"] + if self.parser.tokenizer.stream.charEncoding[1] == "tentative": + if "charset" in attributes: + self.parser.tokenizer.stream.changeEncoding(attributes["charset"]) + elif ("content" in attributes and + "http-equiv" in attributes and + attributes["http-equiv"].lower() == "content-type"): + # Encoding it as UTF-8 here is a hack, as really we should pass + # the abstract Unicode string, and just use the + # ContentAttrParser on that, but using UTF-8 allows all chars + # to be encoded and as a ASCII-superset works. + data = _inputstream.EncodingBytes(attributes["content"].encode("utf-8")) + parser = _inputstream.ContentAttrParser(data) + codec = parser.parse() + self.parser.tokenizer.stream.changeEncoding(codec) + + def startTagTitle(self, token): + self.parser.parseRCDataRawtext(token, "RCDATA") + + def startTagNoFramesStyle(self, token): + # Need to decide whether to implement the scripting-disabled case + self.parser.parseRCDataRawtext(token, "RAWTEXT") + + def startTagNoscript(self, token): + if self.parser.scripting: + self.parser.parseRCDataRawtext(token, "RAWTEXT") + else: + self.tree.insertElement(token) + self.parser.phase = self.parser.phases["inHeadNoscript"] + + def startTagScript(self, token): + self.tree.insertElement(token) + self.parser.tokenizer.state = self.parser.tokenizer.scriptDataState + self.parser.originalPhase = self.parser.phase + self.parser.phase = self.parser.phases["text"] + + def startTagOther(self, token): + self.anythingElse() + return token + + def endTagHead(self, token): + node = self.parser.tree.openElements.pop() + assert node.name == "head", "Expected head got %s" % node.name + self.parser.phase = self.parser.phases["afterHead"] + + def endTagHtmlBodyBr(self, token): + self.anythingElse() + return token + + def endTagOther(self, token): + self.parser.parseError("unexpected-end-tag", {"name": token["name"]}) + + def anythingElse(self): + self.endTagHead(impliedTagToken("head")) + + class InHeadNoscriptPhase(Phase): + def __init__(self, parser, tree): + Phase.__init__(self, parser, tree) + + self.startTagHandler = _utils.MethodDispatcher([ + ("html", self.startTagHtml), + (("basefont", "bgsound", "link", "meta", "noframes", "style"), self.startTagBaseLinkCommand), + (("head", "noscript"), self.startTagHeadNoscript), + ]) + self.startTagHandler.default = self.startTagOther + + self.endTagHandler = _utils.MethodDispatcher([ + ("noscript", self.endTagNoscript), + ("br", self.endTagBr), + ]) + self.endTagHandler.default = self.endTagOther + + def processEOF(self): + self.parser.parseError("eof-in-head-noscript") + self.anythingElse() + return True + + def processComment(self, token): + return self.parser.phases["inHead"].processComment(token) + + def processCharacters(self, token): + self.parser.parseError("char-in-head-noscript") + self.anythingElse() + return token + + def processSpaceCharacters(self, token): + return self.parser.phases["inHead"].processSpaceCharacters(token) + + def startTagHtml(self, token): + return self.parser.phases["inBody"].processStartTag(token) + + def startTagBaseLinkCommand(self, token): + return self.parser.phases["inHead"].processStartTag(token) + + def startTagHeadNoscript(self, token): + self.parser.parseError("unexpected-start-tag", {"name": token["name"]}) + + def startTagOther(self, token): + self.parser.parseError("unexpected-inhead-noscript-tag", {"name": token["name"]}) + self.anythingElse() + return token + + def endTagNoscript(self, token): + node = self.parser.tree.openElements.pop() + assert node.name == "noscript", "Expected noscript got %s" % node.name + self.parser.phase = self.parser.phases["inHead"] + + def endTagBr(self, token): + self.parser.parseError("unexpected-inhead-noscript-tag", {"name": token["name"]}) + self.anythingElse() + return token + + def endTagOther(self, token): + self.parser.parseError("unexpected-end-tag", {"name": token["name"]}) + + def anythingElse(self): + # Caller must raise parse error first! + self.endTagNoscript(impliedTagToken("noscript")) + + class AfterHeadPhase(Phase): + def __init__(self, parser, tree): + Phase.__init__(self, parser, tree) + + self.startTagHandler = _utils.MethodDispatcher([ + ("html", self.startTagHtml), + ("body", self.startTagBody), + ("frameset", self.startTagFrameset), + (("base", "basefont", "bgsound", "link", "meta", "noframes", "script", + "style", "title"), + self.startTagFromHead), + ("head", self.startTagHead) + ]) + self.startTagHandler.default = self.startTagOther + self.endTagHandler = _utils.MethodDispatcher([(("body", "html", "br"), + self.endTagHtmlBodyBr)]) + self.endTagHandler.default = self.endTagOther + + def processEOF(self): + self.anythingElse() + return True + + def processCharacters(self, token): + self.anythingElse() + return token + + def startTagHtml(self, token): + return self.parser.phases["inBody"].processStartTag(token) + + def startTagBody(self, token): + self.parser.framesetOK = False + self.tree.insertElement(token) + self.parser.phase = self.parser.phases["inBody"] + + def startTagFrameset(self, token): + self.tree.insertElement(token) + self.parser.phase = self.parser.phases["inFrameset"] + + def startTagFromHead(self, token): + self.parser.parseError("unexpected-start-tag-out-of-my-head", + {"name": token["name"]}) + self.tree.openElements.append(self.tree.headPointer) + self.parser.phases["inHead"].processStartTag(token) + for node in self.tree.openElements[::-1]: + if node.name == "head": + self.tree.openElements.remove(node) + break + + def startTagHead(self, token): + self.parser.parseError("unexpected-start-tag", {"name": token["name"]}) + + def startTagOther(self, token): + self.anythingElse() + return token + + def endTagHtmlBodyBr(self, token): + self.anythingElse() + return token + + def endTagOther(self, token): + self.parser.parseError("unexpected-end-tag", {"name": token["name"]}) + + def anythingElse(self): + self.tree.insertElement(impliedTagToken("body", "StartTag")) + self.parser.phase = self.parser.phases["inBody"] + self.parser.framesetOK = True + + class InBodyPhase(Phase): + # http://www.whatwg.org/specs/web-apps/current-work/#parsing-main-inbody + # the really-really-really-very crazy mode + def __init__(self, parser, tree): + Phase.__init__(self, parser, tree) + + # Set this to the default handler + self.processSpaceCharacters = self.processSpaceCharactersNonPre + + self.startTagHandler = _utils.MethodDispatcher([ + ("html", self.startTagHtml), + (("base", "basefont", "bgsound", "command", "link", "meta", + "script", "style", "title"), + self.startTagProcessInHead), + ("body", self.startTagBody), + ("frameset", self.startTagFrameset), + (("address", "article", "aside", "blockquote", "center", "details", + "dir", "div", "dl", "fieldset", "figcaption", "figure", + "footer", "header", "hgroup", "main", "menu", "nav", "ol", "p", + "section", "summary", "ul"), + self.startTagCloseP), + (headingElements, self.startTagHeading), + (("pre", "listing"), self.startTagPreListing), + ("form", self.startTagForm), + (("li", "dd", "dt"), self.startTagListItem), + ("plaintext", self.startTagPlaintext), + ("a", self.startTagA), + (("b", "big", "code", "em", "font", "i", "s", "small", "strike", + "strong", "tt", "u"), self.startTagFormatting), + ("nobr", self.startTagNobr), + ("button", self.startTagButton), + (("applet", "marquee", "object"), self.startTagAppletMarqueeObject), + ("xmp", self.startTagXmp), + ("table", self.startTagTable), + (("area", "br", "embed", "img", "keygen", "wbr"), + self.startTagVoidFormatting), + (("param", "source", "track"), self.startTagParamSource), + ("input", self.startTagInput), + ("hr", self.startTagHr), + ("image", self.startTagImage), + ("isindex", self.startTagIsIndex), + ("textarea", self.startTagTextarea), + ("iframe", self.startTagIFrame), + ("noscript", self.startTagNoscript), + (("noembed", "noframes"), self.startTagRawtext), + ("select", self.startTagSelect), + (("rp", "rt"), self.startTagRpRt), + (("option", "optgroup"), self.startTagOpt), + (("math"), self.startTagMath), + (("svg"), self.startTagSvg), + (("caption", "col", "colgroup", "frame", "head", + "tbody", "td", "tfoot", "th", "thead", + "tr"), self.startTagMisplaced) + ]) + self.startTagHandler.default = self.startTagOther + + self.endTagHandler = _utils.MethodDispatcher([ + ("body", self.endTagBody), + ("html", self.endTagHtml), + (("address", "article", "aside", "blockquote", "button", "center", + "details", "dialog", "dir", "div", "dl", "fieldset", "figcaption", "figure", + "footer", "header", "hgroup", "listing", "main", "menu", "nav", "ol", "pre", + "section", "summary", "ul"), self.endTagBlock), + ("form", self.endTagForm), + ("p", self.endTagP), + (("dd", "dt", "li"), self.endTagListItem), + (headingElements, self.endTagHeading), + (("a", "b", "big", "code", "em", "font", "i", "nobr", "s", "small", + "strike", "strong", "tt", "u"), self.endTagFormatting), + (("applet", "marquee", "object"), self.endTagAppletMarqueeObject), + ("br", self.endTagBr), + ]) + self.endTagHandler.default = self.endTagOther + + def isMatchingFormattingElement(self, node1, node2): + return (node1.name == node2.name and + node1.namespace == node2.namespace and + node1.attributes == node2.attributes) + + # helper + def addFormattingElement(self, token): + self.tree.insertElement(token) + element = self.tree.openElements[-1] + + matchingElements = [] + for node in self.tree.activeFormattingElements[::-1]: + if node is Marker: + break + elif self.isMatchingFormattingElement(node, element): + matchingElements.append(node) + + assert len(matchingElements) <= 3 + if len(matchingElements) == 3: + self.tree.activeFormattingElements.remove(matchingElements[-1]) + self.tree.activeFormattingElements.append(element) + + # the real deal + def processEOF(self): + allowed_elements = frozenset(("dd", "dt", "li", "p", "tbody", "td", + "tfoot", "th", "thead", "tr", "body", + "html")) + for node in self.tree.openElements[::-1]: + if node.name not in allowed_elements: + self.parser.parseError("expected-closing-tag-but-got-eof") + break + # Stop parsing + + def processSpaceCharactersDropNewline(self, token): + # Sometimes (start of <pre>, <listing>, and <textarea> blocks) we + # want to drop leading newlines + data = token["data"] + self.processSpaceCharacters = self.processSpaceCharactersNonPre + if (data.startswith("\n") and + self.tree.openElements[-1].name in ("pre", "listing", "textarea") and + not self.tree.openElements[-1].hasContent()): + data = data[1:] + if data: + self.tree.reconstructActiveFormattingElements() + self.tree.insertText(data) + + def processCharacters(self, token): + if token["data"] == "\u0000": + # The tokenizer should always emit null on its own + return + self.tree.reconstructActiveFormattingElements() + self.tree.insertText(token["data"]) + # This must be bad for performance + if (self.parser.framesetOK and + any([char not in spaceCharacters + for char in token["data"]])): + self.parser.framesetOK = False + + def processSpaceCharactersNonPre(self, token): + self.tree.reconstructActiveFormattingElements() + self.tree.insertText(token["data"]) + + def startTagProcessInHead(self, token): + return self.parser.phases["inHead"].processStartTag(token) + + def startTagBody(self, token): + self.parser.parseError("unexpected-start-tag", {"name": "body"}) + if (len(self.tree.openElements) == 1 or + self.tree.openElements[1].name != "body"): + assert self.parser.innerHTML + else: + self.parser.framesetOK = False + for attr, value in token["data"].items(): + if attr not in self.tree.openElements[1].attributes: + self.tree.openElements[1].attributes[attr] = value + + def startTagFrameset(self, token): + self.parser.parseError("unexpected-start-tag", {"name": "frameset"}) + if (len(self.tree.openElements) == 1 or self.tree.openElements[1].name != "body"): + assert self.parser.innerHTML + elif not self.parser.framesetOK: + pass + else: + if self.tree.openElements[1].parent: + self.tree.openElements[1].parent.removeChild(self.tree.openElements[1]) + while self.tree.openElements[-1].name != "html": + self.tree.openElements.pop() + self.tree.insertElement(token) + self.parser.phase = self.parser.phases["inFrameset"] + + def startTagCloseP(self, token): + if self.tree.elementInScope("p", variant="button"): + self.endTagP(impliedTagToken("p")) + self.tree.insertElement(token) + + def startTagPreListing(self, token): + if self.tree.elementInScope("p", variant="button"): + self.endTagP(impliedTagToken("p")) + self.tree.insertElement(token) + self.parser.framesetOK = False + self.processSpaceCharacters = self.processSpaceCharactersDropNewline + + def startTagForm(self, token): + if self.tree.formPointer: + self.parser.parseError("unexpected-start-tag", {"name": "form"}) + else: + if self.tree.elementInScope("p", variant="button"): + self.endTagP(impliedTagToken("p")) + self.tree.insertElement(token) + self.tree.formPointer = self.tree.openElements[-1] + + def startTagListItem(self, token): + self.parser.framesetOK = False + + stopNamesMap = {"li": ["li"], + "dt": ["dt", "dd"], + "dd": ["dt", "dd"]} + stopNames = stopNamesMap[token["name"]] + for node in reversed(self.tree.openElements): + if node.name in stopNames: + self.parser.phase.processEndTag( + impliedTagToken(node.name, "EndTag")) + break + if (node.nameTuple in specialElements and + node.name not in ("address", "div", "p")): + break + + if self.tree.elementInScope("p", variant="button"): + self.parser.phase.processEndTag( + impliedTagToken("p", "EndTag")) + + self.tree.insertElement(token) + + def startTagPlaintext(self, token): + if self.tree.elementInScope("p", variant="button"): + self.endTagP(impliedTagToken("p")) + self.tree.insertElement(token) + self.parser.tokenizer.state = self.parser.tokenizer.plaintextState + + def startTagHeading(self, token): + if self.tree.elementInScope("p", variant="button"): + self.endTagP(impliedTagToken("p")) + if self.tree.openElements[-1].name in headingElements: + self.parser.parseError("unexpected-start-tag", {"name": token["name"]}) + self.tree.openElements.pop() + self.tree.insertElement(token) + + def startTagA(self, token): + afeAElement = self.tree.elementInActiveFormattingElements("a") + if afeAElement: + self.parser.parseError("unexpected-start-tag-implies-end-tag", + {"startName": "a", "endName": "a"}) + self.endTagFormatting(impliedTagToken("a")) + if afeAElement in self.tree.openElements: + self.tree.openElements.remove(afeAElement) + if afeAElement in self.tree.activeFormattingElements: + self.tree.activeFormattingElements.remove(afeAElement) + self.tree.reconstructActiveFormattingElements() + self.addFormattingElement(token) + + def startTagFormatting(self, token): + self.tree.reconstructActiveFormattingElements() + self.addFormattingElement(token) + + def startTagNobr(self, token): + self.tree.reconstructActiveFormattingElements() + if self.tree.elementInScope("nobr"): + self.parser.parseError("unexpected-start-tag-implies-end-tag", + {"startName": "nobr", "endName": "nobr"}) + self.processEndTag(impliedTagToken("nobr")) + # XXX Need tests that trigger the following + self.tree.reconstructActiveFormattingElements() + self.addFormattingElement(token) + + def startTagButton(self, token): + if self.tree.elementInScope("button"): + self.parser.parseError("unexpected-start-tag-implies-end-tag", + {"startName": "button", "endName": "button"}) + self.processEndTag(impliedTagToken("button")) + return token + else: + self.tree.reconstructActiveFormattingElements() + self.tree.insertElement(token) + self.parser.framesetOK = False + + def startTagAppletMarqueeObject(self, token): + self.tree.reconstructActiveFormattingElements() + self.tree.insertElement(token) + self.tree.activeFormattingElements.append(Marker) + self.parser.framesetOK = False + + def startTagXmp(self, token): + if self.tree.elementInScope("p", variant="button"): + self.endTagP(impliedTagToken("p")) + self.tree.reconstructActiveFormattingElements() + self.parser.framesetOK = False + self.parser.parseRCDataRawtext(token, "RAWTEXT") + + def startTagTable(self, token): + if self.parser.compatMode != "quirks": + if self.tree.elementInScope("p", variant="button"): + self.processEndTag(impliedTagToken("p")) + self.tree.insertElement(token) + self.parser.framesetOK = False + self.parser.phase = self.parser.phases["inTable"] + + def startTagVoidFormatting(self, token): + self.tree.reconstructActiveFormattingElements() + self.tree.insertElement(token) + self.tree.openElements.pop() + token["selfClosingAcknowledged"] = True + self.parser.framesetOK = False + + def startTagInput(self, token): + framesetOK = self.parser.framesetOK + self.startTagVoidFormatting(token) + if ("type" in token["data"] and + token["data"]["type"].translate(asciiUpper2Lower) == "hidden"): + # input type=hidden doesn't change framesetOK + self.parser.framesetOK = framesetOK + + def startTagParamSource(self, token): + self.tree.insertElement(token) + self.tree.openElements.pop() + token["selfClosingAcknowledged"] = True + + def startTagHr(self, token): + if self.tree.elementInScope("p", variant="button"): + self.endTagP(impliedTagToken("p")) + self.tree.insertElement(token) + self.tree.openElements.pop() + token["selfClosingAcknowledged"] = True + self.parser.framesetOK = False + + def startTagImage(self, token): + # No really... + self.parser.parseError("unexpected-start-tag-treated-as", + {"originalName": "image", "newName": "img"}) + self.processStartTag(impliedTagToken("img", "StartTag", + attributes=token["data"], + selfClosing=token["selfClosing"])) + + def startTagIsIndex(self, token): + self.parser.parseError("deprecated-tag", {"name": "isindex"}) + if self.tree.formPointer: + return + form_attrs = {} + if "action" in token["data"]: + form_attrs["action"] = token["data"]["action"] + self.processStartTag(impliedTagToken("form", "StartTag", + attributes=form_attrs)) + self.processStartTag(impliedTagToken("hr", "StartTag")) + self.processStartTag(impliedTagToken("label", "StartTag")) + # XXX Localization ... + if "prompt" in token["data"]: + prompt = token["data"]["prompt"] + else: + prompt = "This is a searchable index. Enter search keywords: " + self.processCharacters( + {"type": tokenTypes["Characters"], "data": prompt}) + attributes = token["data"].copy() + if "action" in attributes: + del attributes["action"] + if "prompt" in attributes: + del attributes["prompt"] + attributes["name"] = "isindex" + self.processStartTag(impliedTagToken("input", "StartTag", + attributes=attributes, + selfClosing=token["selfClosing"])) + self.processEndTag(impliedTagToken("label")) + self.processStartTag(impliedTagToken("hr", "StartTag")) + self.processEndTag(impliedTagToken("form")) + + def startTagTextarea(self, token): + self.tree.insertElement(token) + self.parser.tokenizer.state = self.parser.tokenizer.rcdataState + self.processSpaceCharacters = self.processSpaceCharactersDropNewline + self.parser.framesetOK = False + + def startTagIFrame(self, token): + self.parser.framesetOK = False + self.startTagRawtext(token) + + def startTagNoscript(self, token): + if self.parser.scripting: + self.startTagRawtext(token) + else: + self.startTagOther(token) + + def startTagRawtext(self, token): + """iframe, noembed noframes, noscript(if scripting enabled)""" + self.parser.parseRCDataRawtext(token, "RAWTEXT") + + def startTagOpt(self, token): + if self.tree.openElements[-1].name == "option": + self.parser.phase.processEndTag(impliedTagToken("option")) + self.tree.reconstructActiveFormattingElements() + self.parser.tree.insertElement(token) + + def startTagSelect(self, token): + self.tree.reconstructActiveFormattingElements() + self.tree.insertElement(token) + self.parser.framesetOK = False + if self.parser.phase in (self.parser.phases["inTable"], + self.parser.phases["inCaption"], + self.parser.phases["inColumnGroup"], + self.parser.phases["inTableBody"], + self.parser.phases["inRow"], + self.parser.phases["inCell"]): + self.parser.phase = self.parser.phases["inSelectInTable"] + else: + self.parser.phase = self.parser.phases["inSelect"] + + def startTagRpRt(self, token): + if self.tree.elementInScope("ruby"): + self.tree.generateImpliedEndTags() + if self.tree.openElements[-1].name != "ruby": + self.parser.parseError() + self.tree.insertElement(token) + + def startTagMath(self, token): + self.tree.reconstructActiveFormattingElements() + self.parser.adjustMathMLAttributes(token) + self.parser.adjustForeignAttributes(token) + token["namespace"] = namespaces["mathml"] + self.tree.insertElement(token) + # Need to get the parse error right for the case where the token + # has a namespace not equal to the xmlns attribute + if token["selfClosing"]: + self.tree.openElements.pop() + token["selfClosingAcknowledged"] = True + + def startTagSvg(self, token): + self.tree.reconstructActiveFormattingElements() + self.parser.adjustSVGAttributes(token) + self.parser.adjustForeignAttributes(token) + token["namespace"] = namespaces["svg"] + self.tree.insertElement(token) + # Need to get the parse error right for the case where the token + # has a namespace not equal to the xmlns attribute + if token["selfClosing"]: + self.tree.openElements.pop() + token["selfClosingAcknowledged"] = True + + def startTagMisplaced(self, token): + """ Elements that should be children of other elements that have a + different insertion mode; here they are ignored + "caption", "col", "colgroup", "frame", "frameset", "head", + "option", "optgroup", "tbody", "td", "tfoot", "th", "thead", + "tr", "noscript" + """ + self.parser.parseError("unexpected-start-tag-ignored", {"name": token["name"]}) + + def startTagOther(self, token): + self.tree.reconstructActiveFormattingElements() + self.tree.insertElement(token) + + def endTagP(self, token): + if not self.tree.elementInScope("p", variant="button"): + self.startTagCloseP(impliedTagToken("p", "StartTag")) + self.parser.parseError("unexpected-end-tag", {"name": "p"}) + self.endTagP(impliedTagToken("p", "EndTag")) + else: + self.tree.generateImpliedEndTags("p") + if self.tree.openElements[-1].name != "p": + self.parser.parseError("unexpected-end-tag", {"name": "p"}) + node = self.tree.openElements.pop() + while node.name != "p": + node = self.tree.openElements.pop() + + def endTagBody(self, token): + if not self.tree.elementInScope("body"): + self.parser.parseError() + return + elif self.tree.openElements[-1].name != "body": + for node in self.tree.openElements[2:]: + if node.name not in frozenset(("dd", "dt", "li", "optgroup", + "option", "p", "rp", "rt", + "tbody", "td", "tfoot", + "th", "thead", "tr", "body", + "html")): + # Not sure this is the correct name for the parse error + self.parser.parseError( + "expected-one-end-tag-but-got-another", + {"gotName": "body", "expectedName": node.name}) + break + self.parser.phase = self.parser.phases["afterBody"] + + def endTagHtml(self, token): + # We repeat the test for the body end tag token being ignored here + if self.tree.elementInScope("body"): + self.endTagBody(impliedTagToken("body")) + return token + + def endTagBlock(self, token): + # Put us back in the right whitespace handling mode + if token["name"] == "pre": + self.processSpaceCharacters = self.processSpaceCharactersNonPre + inScope = self.tree.elementInScope(token["name"]) + if inScope: + self.tree.generateImpliedEndTags() + if self.tree.openElements[-1].name != token["name"]: + self.parser.parseError("end-tag-too-early", {"name": token["name"]}) + if inScope: + node = self.tree.openElements.pop() + while node.name != token["name"]: + node = self.tree.openElements.pop() + + def endTagForm(self, token): + node = self.tree.formPointer + self.tree.formPointer = None + if node is None or not self.tree.elementInScope(node): + self.parser.parseError("unexpected-end-tag", + {"name": "form"}) + else: + self.tree.generateImpliedEndTags() + if self.tree.openElements[-1] != node: + self.parser.parseError("end-tag-too-early-ignored", + {"name": "form"}) + self.tree.openElements.remove(node) + + def endTagListItem(self, token): + if token["name"] == "li": + variant = "list" + else: + variant = None + if not self.tree.elementInScope(token["name"], variant=variant): + self.parser.parseError("unexpected-end-tag", {"name": token["name"]}) + else: + self.tree.generateImpliedEndTags(exclude=token["name"]) + if self.tree.openElements[-1].name != token["name"]: + self.parser.parseError( + "end-tag-too-early", + {"name": token["name"]}) + node = self.tree.openElements.pop() + while node.name != token["name"]: + node = self.tree.openElements.pop() + + def endTagHeading(self, token): + for item in headingElements: + if self.tree.elementInScope(item): + self.tree.generateImpliedEndTags() + break + if self.tree.openElements[-1].name != token["name"]: + self.parser.parseError("end-tag-too-early", {"name": token["name"]}) + + for item in headingElements: + if self.tree.elementInScope(item): + item = self.tree.openElements.pop() + while item.name not in headingElements: + item = self.tree.openElements.pop() + break + + def endTagFormatting(self, token): + """The much-feared adoption agency algorithm""" + # http://svn.whatwg.org/webapps/complete.html#adoptionAgency revision 7867 + # XXX Better parseError messages appreciated. + + # Step 1 + outerLoopCounter = 0 + + # Step 2 + while outerLoopCounter < 8: + + # Step 3 + outerLoopCounter += 1 + + # Step 4: + + # Let the formatting element be the last element in + # the list of active formatting elements that: + # - is between the end of the list and the last scope + # marker in the list, if any, or the start of the list + # otherwise, and + # - has the same tag name as the token. + formattingElement = self.tree.elementInActiveFormattingElements( + token["name"]) + if (not formattingElement or + (formattingElement in self.tree.openElements and + not self.tree.elementInScope(formattingElement.name))): + # If there is no such node, then abort these steps + # and instead act as described in the "any other + # end tag" entry below. + self.endTagOther(token) + return + + # Otherwise, if there is such a node, but that node is + # not in the stack of open elements, then this is a + # parse error; remove the element from the list, and + # abort these steps. + elif formattingElement not in self.tree.openElements: + self.parser.parseError("adoption-agency-1.2", {"name": token["name"]}) + self.tree.activeFormattingElements.remove(formattingElement) + return + + # Otherwise, if there is such a node, and that node is + # also in the stack of open elements, but the element + # is not in scope, then this is a parse error; ignore + # the token, and abort these steps. + elif not self.tree.elementInScope(formattingElement.name): + self.parser.parseError("adoption-agency-4.4", {"name": token["name"]}) + return + + # Otherwise, there is a formatting element and that + # element is in the stack and is in scope. If the + # element is not the current node, this is a parse + # error. In any case, proceed with the algorithm as + # written in the following steps. + else: + if formattingElement != self.tree.openElements[-1]: + self.parser.parseError("adoption-agency-1.3", {"name": token["name"]}) + + # Step 5: + + # Let the furthest block be the topmost node in the + # stack of open elements that is lower in the stack + # than the formatting element, and is an element in + # the special category. There might not be one. + afeIndex = self.tree.openElements.index(formattingElement) + furthestBlock = None + for element in self.tree.openElements[afeIndex:]: + if element.nameTuple in specialElements: + furthestBlock = element + break + + # Step 6: + + # If there is no furthest block, then the UA must + # first pop all the nodes from the bottom of the stack + # of open elements, from the current node up to and + # including the formatting element, then remove the + # formatting element from the list of active + # formatting elements, and finally abort these steps. + if furthestBlock is None: + element = self.tree.openElements.pop() + while element != formattingElement: + element = self.tree.openElements.pop() + self.tree.activeFormattingElements.remove(element) + return + + # Step 7 + commonAncestor = self.tree.openElements[afeIndex - 1] + + # Step 8: + # The bookmark is supposed to help us identify where to reinsert + # nodes in step 15. We have to ensure that we reinsert nodes after + # the node before the active formatting element. Note the bookmark + # can move in step 9.7 + bookmark = self.tree.activeFormattingElements.index(formattingElement) + + # Step 9 + lastNode = node = furthestBlock + innerLoopCounter = 0 + + index = self.tree.openElements.index(node) + while innerLoopCounter < 3: + innerLoopCounter += 1 + # Node is element before node in open elements + index -= 1 + node = self.tree.openElements[index] + if node not in self.tree.activeFormattingElements: + self.tree.openElements.remove(node) + continue + # Step 9.6 + if node == formattingElement: + break + # Step 9.7 + if lastNode == furthestBlock: + bookmark = self.tree.activeFormattingElements.index(node) + 1 + # Step 9.8 + clone = node.cloneNode() + # Replace node with clone + self.tree.activeFormattingElements[ + self.tree.activeFormattingElements.index(node)] = clone + self.tree.openElements[ + self.tree.openElements.index(node)] = clone + node = clone + # Step 9.9 + # Remove lastNode from its parents, if any + if lastNode.parent: + lastNode.parent.removeChild(lastNode) + node.appendChild(lastNode) + # Step 9.10 + lastNode = node + + # Step 10 + # Foster parent lastNode if commonAncestor is a + # table, tbody, tfoot, thead, or tr we need to foster + # parent the lastNode + if lastNode.parent: + lastNode.parent.removeChild(lastNode) + + if commonAncestor.name in frozenset(("table", "tbody", "tfoot", "thead", "tr")): + parent, insertBefore = self.tree.getTableMisnestedNodePosition() + parent.insertBefore(lastNode, insertBefore) + else: + commonAncestor.appendChild(lastNode) + + # Step 11 + clone = formattingElement.cloneNode() + + # Step 12 + furthestBlock.reparentChildren(clone) + + # Step 13 + furthestBlock.appendChild(clone) + + # Step 14 + self.tree.activeFormattingElements.remove(formattingElement) + self.tree.activeFormattingElements.insert(bookmark, clone) + + # Step 15 + self.tree.openElements.remove(formattingElement) + self.tree.openElements.insert( + self.tree.openElements.index(furthestBlock) + 1, clone) + + def endTagAppletMarqueeObject(self, token): + if self.tree.elementInScope(token["name"]): + self.tree.generateImpliedEndTags() + if self.tree.openElements[-1].name != token["name"]: + self.parser.parseError("end-tag-too-early", {"name": token["name"]}) + + if self.tree.elementInScope(token["name"]): + element = self.tree.openElements.pop() + while element.name != token["name"]: + element = self.tree.openElements.pop() + self.tree.clearActiveFormattingElements() + + def endTagBr(self, token): + self.parser.parseError("unexpected-end-tag-treated-as", + {"originalName": "br", "newName": "br element"}) + self.tree.reconstructActiveFormattingElements() + self.tree.insertElement(impliedTagToken("br", "StartTag")) + self.tree.openElements.pop() + + def endTagOther(self, token): + for node in self.tree.openElements[::-1]: + if node.name == token["name"]: + self.tree.generateImpliedEndTags(exclude=token["name"]) + if self.tree.openElements[-1].name != token["name"]: + self.parser.parseError("unexpected-end-tag", {"name": token["name"]}) + while self.tree.openElements.pop() != node: + pass + break + else: + if node.nameTuple in specialElements: + self.parser.parseError("unexpected-end-tag", {"name": token["name"]}) + break + + class TextPhase(Phase): + def __init__(self, parser, tree): + Phase.__init__(self, parser, tree) + self.startTagHandler = _utils.MethodDispatcher([]) + self.startTagHandler.default = self.startTagOther + self.endTagHandler = _utils.MethodDispatcher([ + ("script", self.endTagScript)]) + self.endTagHandler.default = self.endTagOther + + def processCharacters(self, token): + self.tree.insertText(token["data"]) + + def processEOF(self): + self.parser.parseError("expected-named-closing-tag-but-got-eof", + {"name": self.tree.openElements[-1].name}) + self.tree.openElements.pop() + self.parser.phase = self.parser.originalPhase + return True + + def startTagOther(self, token): + assert False, "Tried to process start tag %s in RCDATA/RAWTEXT mode" % token['name'] + + def endTagScript(self, token): + node = self.tree.openElements.pop() + assert node.name == "script" + self.parser.phase = self.parser.originalPhase + # The rest of this method is all stuff that only happens if + # document.write works + + def endTagOther(self, token): + self.tree.openElements.pop() + self.parser.phase = self.parser.originalPhase + + class InTablePhase(Phase): + # http://www.whatwg.org/specs/web-apps/current-work/#in-table + def __init__(self, parser, tree): + Phase.__init__(self, parser, tree) + self.startTagHandler = _utils.MethodDispatcher([ + ("html", self.startTagHtml), + ("caption", self.startTagCaption), + ("colgroup", self.startTagColgroup), + ("col", self.startTagCol), + (("tbody", "tfoot", "thead"), self.startTagRowGroup), + (("td", "th", "tr"), self.startTagImplyTbody), + ("table", self.startTagTable), + (("style", "script"), self.startTagStyleScript), + ("input", self.startTagInput), + ("form", self.startTagForm) + ]) + self.startTagHandler.default = self.startTagOther + + self.endTagHandler = _utils.MethodDispatcher([ + ("table", self.endTagTable), + (("body", "caption", "col", "colgroup", "html", "tbody", "td", + "tfoot", "th", "thead", "tr"), self.endTagIgnore) + ]) + self.endTagHandler.default = self.endTagOther + + # helper methods + def clearStackToTableContext(self): + # "clear the stack back to a table context" + while self.tree.openElements[-1].name not in ("table", "html"): + # self.parser.parseError("unexpected-implied-end-tag-in-table", + # {"name": self.tree.openElements[-1].name}) + self.tree.openElements.pop() + # When the current node is <html> it's an innerHTML case + + # processing methods + def processEOF(self): + if self.tree.openElements[-1].name != "html": + self.parser.parseError("eof-in-table") + else: + assert self.parser.innerHTML + # Stop parsing + + def processSpaceCharacters(self, token): + originalPhase = self.parser.phase + self.parser.phase = self.parser.phases["inTableText"] + self.parser.phase.originalPhase = originalPhase + self.parser.phase.processSpaceCharacters(token) + + def processCharacters(self, token): + originalPhase = self.parser.phase + self.parser.phase = self.parser.phases["inTableText"] + self.parser.phase.originalPhase = originalPhase + self.parser.phase.processCharacters(token) + + def insertText(self, token): + # If we get here there must be at least one non-whitespace character + # Do the table magic! + self.tree.insertFromTable = True + self.parser.phases["inBody"].processCharacters(token) + self.tree.insertFromTable = False + + def startTagCaption(self, token): + self.clearStackToTableContext() + self.tree.activeFormattingElements.append(Marker) + self.tree.insertElement(token) + self.parser.phase = self.parser.phases["inCaption"] + + def startTagColgroup(self, token): + self.clearStackToTableContext() + self.tree.insertElement(token) + self.parser.phase = self.parser.phases["inColumnGroup"] + + def startTagCol(self, token): + self.startTagColgroup(impliedTagToken("colgroup", "StartTag")) + return token + + def startTagRowGroup(self, token): + self.clearStackToTableContext() + self.tree.insertElement(token) + self.parser.phase = self.parser.phases["inTableBody"] + + def startTagImplyTbody(self, token): + self.startTagRowGroup(impliedTagToken("tbody", "StartTag")) + return token + + def startTagTable(self, token): + self.parser.parseError("unexpected-start-tag-implies-end-tag", + {"startName": "table", "endName": "table"}) + self.parser.phase.processEndTag(impliedTagToken("table")) + if not self.parser.innerHTML: + return token + + def startTagStyleScript(self, token): + return self.parser.phases["inHead"].processStartTag(token) + + def startTagInput(self, token): + if ("type" in token["data"] and + token["data"]["type"].translate(asciiUpper2Lower) == "hidden"): + self.parser.parseError("unexpected-hidden-input-in-table") + self.tree.insertElement(token) + # XXX associate with form + self.tree.openElements.pop() + else: + self.startTagOther(token) + + def startTagForm(self, token): + self.parser.parseError("unexpected-form-in-table") + if self.tree.formPointer is None: + self.tree.insertElement(token) + self.tree.formPointer = self.tree.openElements[-1] + self.tree.openElements.pop() + + def startTagOther(self, token): + self.parser.parseError("unexpected-start-tag-implies-table-voodoo", {"name": token["name"]}) + # Do the table magic! + self.tree.insertFromTable = True + self.parser.phases["inBody"].processStartTag(token) + self.tree.insertFromTable = False + + def endTagTable(self, token): + if self.tree.elementInScope("table", variant="table"): + self.tree.generateImpliedEndTags() + if self.tree.openElements[-1].name != "table": + self.parser.parseError("end-tag-too-early-named", + {"gotName": "table", + "expectedName": self.tree.openElements[-1].name}) + while self.tree.openElements[-1].name != "table": + self.tree.openElements.pop() + self.tree.openElements.pop() + self.parser.resetInsertionMode() + else: + # innerHTML case + assert self.parser.innerHTML + self.parser.parseError() + + def endTagIgnore(self, token): + self.parser.parseError("unexpected-end-tag", {"name": token["name"]}) + + def endTagOther(self, token): + self.parser.parseError("unexpected-end-tag-implies-table-voodoo", {"name": token["name"]}) + # Do the table magic! + self.tree.insertFromTable = True + self.parser.phases["inBody"].processEndTag(token) + self.tree.insertFromTable = False + + class InTableTextPhase(Phase): + def __init__(self, parser, tree): + Phase.__init__(self, parser, tree) + self.originalPhase = None + self.characterTokens = [] + + def flushCharacters(self): + data = "".join([item["data"] for item in self.characterTokens]) + if any([item not in spaceCharacters for item in data]): + token = {"type": tokenTypes["Characters"], "data": data} + self.parser.phases["inTable"].insertText(token) + elif data: + self.tree.insertText(data) + self.characterTokens = [] + + def processComment(self, token): + self.flushCharacters() + self.parser.phase = self.originalPhase + return token + + def processEOF(self): + self.flushCharacters() + self.parser.phase = self.originalPhase + return True + + def processCharacters(self, token): + if token["data"] == "\u0000": + return + self.characterTokens.append(token) + + def processSpaceCharacters(self, token): + # pretty sure we should never reach here + self.characterTokens.append(token) + # assert False + + def processStartTag(self, token): + self.flushCharacters() + self.parser.phase = self.originalPhase + return token + + def processEndTag(self, token): + self.flushCharacters() + self.parser.phase = self.originalPhase + return token + + class InCaptionPhase(Phase): + # http://www.whatwg.org/specs/web-apps/current-work/#in-caption + def __init__(self, parser, tree): + Phase.__init__(self, parser, tree) + + self.startTagHandler = _utils.MethodDispatcher([ + ("html", self.startTagHtml), + (("caption", "col", "colgroup", "tbody", "td", "tfoot", "th", + "thead", "tr"), self.startTagTableElement) + ]) + self.startTagHandler.default = self.startTagOther + + self.endTagHandler = _utils.MethodDispatcher([ + ("caption", self.endTagCaption), + ("table", self.endTagTable), + (("body", "col", "colgroup", "html", "tbody", "td", "tfoot", "th", + "thead", "tr"), self.endTagIgnore) + ]) + self.endTagHandler.default = self.endTagOther + + def ignoreEndTagCaption(self): + return not self.tree.elementInScope("caption", variant="table") + + def processEOF(self): + self.parser.phases["inBody"].processEOF() + + def processCharacters(self, token): + return self.parser.phases["inBody"].processCharacters(token) + + def startTagTableElement(self, token): + self.parser.parseError() + # XXX Have to duplicate logic here to find out if the tag is ignored + ignoreEndTag = self.ignoreEndTagCaption() + self.parser.phase.processEndTag(impliedTagToken("caption")) + if not ignoreEndTag: + return token + + def startTagOther(self, token): + return self.parser.phases["inBody"].processStartTag(token) + + def endTagCaption(self, token): + if not self.ignoreEndTagCaption(): + # AT this code is quite similar to endTagTable in "InTable" + self.tree.generateImpliedEndTags() + if self.tree.openElements[-1].name != "caption": + self.parser.parseError("expected-one-end-tag-but-got-another", + {"gotName": "caption", + "expectedName": self.tree.openElements[-1].name}) + while self.tree.openElements[-1].name != "caption": + self.tree.openElements.pop() + self.tree.openElements.pop() + self.tree.clearActiveFormattingElements() + self.parser.phase = self.parser.phases["inTable"] + else: + # innerHTML case + assert self.parser.innerHTML + self.parser.parseError() + + def endTagTable(self, token): + self.parser.parseError() + ignoreEndTag = self.ignoreEndTagCaption() + self.parser.phase.processEndTag(impliedTagToken("caption")) + if not ignoreEndTag: + return token + + def endTagIgnore(self, token): + self.parser.parseError("unexpected-end-tag", {"name": token["name"]}) + + def endTagOther(self, token): + return self.parser.phases["inBody"].processEndTag(token) + + class InColumnGroupPhase(Phase): + # http://www.whatwg.org/specs/web-apps/current-work/#in-column + + def __init__(self, parser, tree): + Phase.__init__(self, parser, tree) + + self.startTagHandler = _utils.MethodDispatcher([ + ("html", self.startTagHtml), + ("col", self.startTagCol) + ]) + self.startTagHandler.default = self.startTagOther + + self.endTagHandler = _utils.MethodDispatcher([ + ("colgroup", self.endTagColgroup), + ("col", self.endTagCol) + ]) + self.endTagHandler.default = self.endTagOther + + def ignoreEndTagColgroup(self): + return self.tree.openElements[-1].name == "html" + + def processEOF(self): + if self.tree.openElements[-1].name == "html": + assert self.parser.innerHTML + return + else: + ignoreEndTag = self.ignoreEndTagColgroup() + self.endTagColgroup(impliedTagToken("colgroup")) + if not ignoreEndTag: + return True + + def processCharacters(self, token): + ignoreEndTag = self.ignoreEndTagColgroup() + self.endTagColgroup(impliedTagToken("colgroup")) + if not ignoreEndTag: + return token + + def startTagCol(self, token): + self.tree.insertElement(token) + self.tree.openElements.pop() + token["selfClosingAcknowledged"] = True + + def startTagOther(self, token): + ignoreEndTag = self.ignoreEndTagColgroup() + self.endTagColgroup(impliedTagToken("colgroup")) + if not ignoreEndTag: + return token + + def endTagColgroup(self, token): + if self.ignoreEndTagColgroup(): + # innerHTML case + assert self.parser.innerHTML + self.parser.parseError() + else: + self.tree.openElements.pop() + self.parser.phase = self.parser.phases["inTable"] + + def endTagCol(self, token): + self.parser.parseError("no-end-tag", {"name": "col"}) + + def endTagOther(self, token): + ignoreEndTag = self.ignoreEndTagColgroup() + self.endTagColgroup(impliedTagToken("colgroup")) + if not ignoreEndTag: + return token + + class InTableBodyPhase(Phase): + # http://www.whatwg.org/specs/web-apps/current-work/#in-table0 + def __init__(self, parser, tree): + Phase.__init__(self, parser, tree) + self.startTagHandler = _utils.MethodDispatcher([ + ("html", self.startTagHtml), + ("tr", self.startTagTr), + (("td", "th"), self.startTagTableCell), + (("caption", "col", "colgroup", "tbody", "tfoot", "thead"), + self.startTagTableOther) + ]) + self.startTagHandler.default = self.startTagOther + + self.endTagHandler = _utils.MethodDispatcher([ + (("tbody", "tfoot", "thead"), self.endTagTableRowGroup), + ("table", self.endTagTable), + (("body", "caption", "col", "colgroup", "html", "td", "th", + "tr"), self.endTagIgnore) + ]) + self.endTagHandler.default = self.endTagOther + + # helper methods + def clearStackToTableBodyContext(self): + while self.tree.openElements[-1].name not in ("tbody", "tfoot", + "thead", "html"): + # self.parser.parseError("unexpected-implied-end-tag-in-table", + # {"name": self.tree.openElements[-1].name}) + self.tree.openElements.pop() + if self.tree.openElements[-1].name == "html": + assert self.parser.innerHTML + + # the rest + def processEOF(self): + self.parser.phases["inTable"].processEOF() + + def processSpaceCharacters(self, token): + return self.parser.phases["inTable"].processSpaceCharacters(token) + + def processCharacters(self, token): + return self.parser.phases["inTable"].processCharacters(token) + + def startTagTr(self, token): + self.clearStackToTableBodyContext() + self.tree.insertElement(token) + self.parser.phase = self.parser.phases["inRow"] + + def startTagTableCell(self, token): + self.parser.parseError("unexpected-cell-in-table-body", + {"name": token["name"]}) + self.startTagTr(impliedTagToken("tr", "StartTag")) + return token + + def startTagTableOther(self, token): + # XXX AT Any ideas on how to share this with endTagTable? + if (self.tree.elementInScope("tbody", variant="table") or + self.tree.elementInScope("thead", variant="table") or + self.tree.elementInScope("tfoot", variant="table")): + self.clearStackToTableBodyContext() + self.endTagTableRowGroup( + impliedTagToken(self.tree.openElements[-1].name)) + return token + else: + # innerHTML case + assert self.parser.innerHTML + self.parser.parseError() + + def startTagOther(self, token): + return self.parser.phases["inTable"].processStartTag(token) + + def endTagTableRowGroup(self, token): + if self.tree.elementInScope(token["name"], variant="table"): + self.clearStackToTableBodyContext() + self.tree.openElements.pop() + self.parser.phase = self.parser.phases["inTable"] + else: + self.parser.parseError("unexpected-end-tag-in-table-body", + {"name": token["name"]}) + + def endTagTable(self, token): + if (self.tree.elementInScope("tbody", variant="table") or + self.tree.elementInScope("thead", variant="table") or + self.tree.elementInScope("tfoot", variant="table")): + self.clearStackToTableBodyContext() + self.endTagTableRowGroup( + impliedTagToken(self.tree.openElements[-1].name)) + return token + else: + # innerHTML case + assert self.parser.innerHTML + self.parser.parseError() + + def endTagIgnore(self, token): + self.parser.parseError("unexpected-end-tag-in-table-body", + {"name": token["name"]}) + + def endTagOther(self, token): + return self.parser.phases["inTable"].processEndTag(token) + + class InRowPhase(Phase): + # http://www.whatwg.org/specs/web-apps/current-work/#in-row + def __init__(self, parser, tree): + Phase.__init__(self, parser, tree) + self.startTagHandler = _utils.MethodDispatcher([ + ("html", self.startTagHtml), + (("td", "th"), self.startTagTableCell), + (("caption", "col", "colgroup", "tbody", "tfoot", "thead", + "tr"), self.startTagTableOther) + ]) + self.startTagHandler.default = self.startTagOther + + self.endTagHandler = _utils.MethodDispatcher([ + ("tr", self.endTagTr), + ("table", self.endTagTable), + (("tbody", "tfoot", "thead"), self.endTagTableRowGroup), + (("body", "caption", "col", "colgroup", "html", "td", "th"), + self.endTagIgnore) + ]) + self.endTagHandler.default = self.endTagOther + + # helper methods (XXX unify this with other table helper methods) + def clearStackToTableRowContext(self): + while self.tree.openElements[-1].name not in ("tr", "html"): + self.parser.parseError("unexpected-implied-end-tag-in-table-row", + {"name": self.tree.openElements[-1].name}) + self.tree.openElements.pop() + + def ignoreEndTagTr(self): + return not self.tree.elementInScope("tr", variant="table") + + # the rest + def processEOF(self): + self.parser.phases["inTable"].processEOF() + + def processSpaceCharacters(self, token): + return self.parser.phases["inTable"].processSpaceCharacters(token) + + def processCharacters(self, token): + return self.parser.phases["inTable"].processCharacters(token) + + def startTagTableCell(self, token): + self.clearStackToTableRowContext() + self.tree.insertElement(token) + self.parser.phase = self.parser.phases["inCell"] + self.tree.activeFormattingElements.append(Marker) + + def startTagTableOther(self, token): + ignoreEndTag = self.ignoreEndTagTr() + self.endTagTr(impliedTagToken("tr")) + # XXX how are we sure it's always ignored in the innerHTML case? + if not ignoreEndTag: + return token + + def startTagOther(self, token): + return self.parser.phases["inTable"].processStartTag(token) + + def endTagTr(self, token): + if not self.ignoreEndTagTr(): + self.clearStackToTableRowContext() + self.tree.openElements.pop() + self.parser.phase = self.parser.phases["inTableBody"] + else: + # innerHTML case + assert self.parser.innerHTML + self.parser.parseError() + + def endTagTable(self, token): + ignoreEndTag = self.ignoreEndTagTr() + self.endTagTr(impliedTagToken("tr")) + # Reprocess the current tag if the tr end tag was not ignored + # XXX how are we sure it's always ignored in the innerHTML case? + if not ignoreEndTag: + return token + + def endTagTableRowGroup(self, token): + if self.tree.elementInScope(token["name"], variant="table"): + self.endTagTr(impliedTagToken("tr")) + return token + else: + self.parser.parseError() + + def endTagIgnore(self, token): + self.parser.parseError("unexpected-end-tag-in-table-row", + {"name": token["name"]}) + + def endTagOther(self, token): + return self.parser.phases["inTable"].processEndTag(token) + + class InCellPhase(Phase): + # http://www.whatwg.org/specs/web-apps/current-work/#in-cell + def __init__(self, parser, tree): + Phase.__init__(self, parser, tree) + self.startTagHandler = _utils.MethodDispatcher([ + ("html", self.startTagHtml), + (("caption", "col", "colgroup", "tbody", "td", "tfoot", "th", + "thead", "tr"), self.startTagTableOther) + ]) + self.startTagHandler.default = self.startTagOther + + self.endTagHandler = _utils.MethodDispatcher([ + (("td", "th"), self.endTagTableCell), + (("body", "caption", "col", "colgroup", "html"), self.endTagIgnore), + (("table", "tbody", "tfoot", "thead", "tr"), self.endTagImply) + ]) + self.endTagHandler.default = self.endTagOther + + # helper + def closeCell(self): + if self.tree.elementInScope("td", variant="table"): + self.endTagTableCell(impliedTagToken("td")) + elif self.tree.elementInScope("th", variant="table"): + self.endTagTableCell(impliedTagToken("th")) + + # the rest + def processEOF(self): + self.parser.phases["inBody"].processEOF() + + def processCharacters(self, token): + return self.parser.phases["inBody"].processCharacters(token) + + def startTagTableOther(self, token): + if (self.tree.elementInScope("td", variant="table") or + self.tree.elementInScope("th", variant="table")): + self.closeCell() + return token + else: + # innerHTML case + assert self.parser.innerHTML + self.parser.parseError() + + def startTagOther(self, token): + return self.parser.phases["inBody"].processStartTag(token) + + def endTagTableCell(self, token): + if self.tree.elementInScope(token["name"], variant="table"): + self.tree.generateImpliedEndTags(token["name"]) + if self.tree.openElements[-1].name != token["name"]: + self.parser.parseError("unexpected-cell-end-tag", + {"name": token["name"]}) + while True: + node = self.tree.openElements.pop() + if node.name == token["name"]: + break + else: + self.tree.openElements.pop() + self.tree.clearActiveFormattingElements() + self.parser.phase = self.parser.phases["inRow"] + else: + self.parser.parseError("unexpected-end-tag", {"name": token["name"]}) + + def endTagIgnore(self, token): + self.parser.parseError("unexpected-end-tag", {"name": token["name"]}) + + def endTagImply(self, token): + if self.tree.elementInScope(token["name"], variant="table"): + self.closeCell() + return token + else: + # sometimes innerHTML case + self.parser.parseError() + + def endTagOther(self, token): + return self.parser.phases["inBody"].processEndTag(token) + + class InSelectPhase(Phase): + def __init__(self, parser, tree): + Phase.__init__(self, parser, tree) + + self.startTagHandler = _utils.MethodDispatcher([ + ("html", self.startTagHtml), + ("option", self.startTagOption), + ("optgroup", self.startTagOptgroup), + ("select", self.startTagSelect), + (("input", "keygen", "textarea"), self.startTagInput), + ("script", self.startTagScript) + ]) + self.startTagHandler.default = self.startTagOther + + self.endTagHandler = _utils.MethodDispatcher([ + ("option", self.endTagOption), + ("optgroup", self.endTagOptgroup), + ("select", self.endTagSelect) + ]) + self.endTagHandler.default = self.endTagOther + + # http://www.whatwg.org/specs/web-apps/current-work/#in-select + def processEOF(self): + if self.tree.openElements[-1].name != "html": + self.parser.parseError("eof-in-select") + else: + assert self.parser.innerHTML + + def processCharacters(self, token): + if token["data"] == "\u0000": + return + self.tree.insertText(token["data"]) + + def startTagOption(self, token): + # We need to imply </option> if <option> is the current node. + if self.tree.openElements[-1].name == "option": + self.tree.openElements.pop() + self.tree.insertElement(token) + + def startTagOptgroup(self, token): + if self.tree.openElements[-1].name == "option": + self.tree.openElements.pop() + if self.tree.openElements[-1].name == "optgroup": + self.tree.openElements.pop() + self.tree.insertElement(token) + + def startTagSelect(self, token): + self.parser.parseError("unexpected-select-in-select") + self.endTagSelect(impliedTagToken("select")) + + def startTagInput(self, token): + self.parser.parseError("unexpected-input-in-select") + if self.tree.elementInScope("select", variant="select"): + self.endTagSelect(impliedTagToken("select")) + return token + else: + assert self.parser.innerHTML + + def startTagScript(self, token): + return self.parser.phases["inHead"].processStartTag(token) + + def startTagOther(self, token): + self.parser.parseError("unexpected-start-tag-in-select", + {"name": token["name"]}) + + def endTagOption(self, token): + if self.tree.openElements[-1].name == "option": + self.tree.openElements.pop() + else: + self.parser.parseError("unexpected-end-tag-in-select", + {"name": "option"}) + + def endTagOptgroup(self, token): + # </optgroup> implicitly closes <option> + if (self.tree.openElements[-1].name == "option" and + self.tree.openElements[-2].name == "optgroup"): + self.tree.openElements.pop() + # It also closes </optgroup> + if self.tree.openElements[-1].name == "optgroup": + self.tree.openElements.pop() + # But nothing else + else: + self.parser.parseError("unexpected-end-tag-in-select", + {"name": "optgroup"}) + + def endTagSelect(self, token): + if self.tree.elementInScope("select", variant="select"): + node = self.tree.openElements.pop() + while node.name != "select": + node = self.tree.openElements.pop() + self.parser.resetInsertionMode() + else: + # innerHTML case + assert self.parser.innerHTML + self.parser.parseError() + + def endTagOther(self, token): + self.parser.parseError("unexpected-end-tag-in-select", + {"name": token["name"]}) + + class InSelectInTablePhase(Phase): + def __init__(self, parser, tree): + Phase.__init__(self, parser, tree) + + self.startTagHandler = _utils.MethodDispatcher([ + (("caption", "table", "tbody", "tfoot", "thead", "tr", "td", "th"), + self.startTagTable) + ]) + self.startTagHandler.default = self.startTagOther + + self.endTagHandler = _utils.MethodDispatcher([ + (("caption", "table", "tbody", "tfoot", "thead", "tr", "td", "th"), + self.endTagTable) + ]) + self.endTagHandler.default = self.endTagOther + + def processEOF(self): + self.parser.phases["inSelect"].processEOF() + + def processCharacters(self, token): + return self.parser.phases["inSelect"].processCharacters(token) + + def startTagTable(self, token): + self.parser.parseError("unexpected-table-element-start-tag-in-select-in-table", {"name": token["name"]}) + self.endTagOther(impliedTagToken("select")) + return token + + def startTagOther(self, token): + return self.parser.phases["inSelect"].processStartTag(token) + + def endTagTable(self, token): + self.parser.parseError("unexpected-table-element-end-tag-in-select-in-table", {"name": token["name"]}) + if self.tree.elementInScope(token["name"], variant="table"): + self.endTagOther(impliedTagToken("select")) + return token + + def endTagOther(self, token): + return self.parser.phases["inSelect"].processEndTag(token) + + class InForeignContentPhase(Phase): + breakoutElements = frozenset(["b", "big", "blockquote", "body", "br", + "center", "code", "dd", "div", "dl", "dt", + "em", "embed", "h1", "h2", "h3", + "h4", "h5", "h6", "head", "hr", "i", "img", + "li", "listing", "menu", "meta", "nobr", + "ol", "p", "pre", "ruby", "s", "small", + "span", "strong", "strike", "sub", "sup", + "table", "tt", "u", "ul", "var"]) + + def __init__(self, parser, tree): + Phase.__init__(self, parser, tree) + + def adjustSVGTagNames(self, token): + replacements = {"altglyph": "altGlyph", + "altglyphdef": "altGlyphDef", + "altglyphitem": "altGlyphItem", + "animatecolor": "animateColor", + "animatemotion": "animateMotion", + "animatetransform": "animateTransform", + "clippath": "clipPath", + "feblend": "feBlend", + "fecolormatrix": "feColorMatrix", + "fecomponenttransfer": "feComponentTransfer", + "fecomposite": "feComposite", + "feconvolvematrix": "feConvolveMatrix", + "fediffuselighting": "feDiffuseLighting", + "fedisplacementmap": "feDisplacementMap", + "fedistantlight": "feDistantLight", + "feflood": "feFlood", + "fefunca": "feFuncA", + "fefuncb": "feFuncB", + "fefuncg": "feFuncG", + "fefuncr": "feFuncR", + "fegaussianblur": "feGaussianBlur", + "feimage": "feImage", + "femerge": "feMerge", + "femergenode": "feMergeNode", + "femorphology": "feMorphology", + "feoffset": "feOffset", + "fepointlight": "fePointLight", + "fespecularlighting": "feSpecularLighting", + "fespotlight": "feSpotLight", + "fetile": "feTile", + "feturbulence": "feTurbulence", + "foreignobject": "foreignObject", + "glyphref": "glyphRef", + "lineargradient": "linearGradient", + "radialgradient": "radialGradient", + "textpath": "textPath"} + + if token["name"] in replacements: + token["name"] = replacements[token["name"]] + + def processCharacters(self, token): + if token["data"] == "\u0000": + token["data"] = "\uFFFD" + elif (self.parser.framesetOK and + any(char not in spaceCharacters for char in token["data"])): + self.parser.framesetOK = False + Phase.processCharacters(self, token) + + def processStartTag(self, token): + currentNode = self.tree.openElements[-1] + if (token["name"] in self.breakoutElements or + (token["name"] == "font" and + set(token["data"].keys()) & set(["color", "face", "size"]))): + self.parser.parseError("unexpected-html-element-in-foreign-content", + {"name": token["name"]}) + while (self.tree.openElements[-1].namespace != + self.tree.defaultNamespace and + not self.parser.isHTMLIntegrationPoint(self.tree.openElements[-1]) and + not self.parser.isMathMLTextIntegrationPoint(self.tree.openElements[-1])): + self.tree.openElements.pop() + return token + + else: + if currentNode.namespace == namespaces["mathml"]: + self.parser.adjustMathMLAttributes(token) + elif currentNode.namespace == namespaces["svg"]: + self.adjustSVGTagNames(token) + self.parser.adjustSVGAttributes(token) + self.parser.adjustForeignAttributes(token) + token["namespace"] = currentNode.namespace + self.tree.insertElement(token) + if token["selfClosing"]: + self.tree.openElements.pop() + token["selfClosingAcknowledged"] = True + + def processEndTag(self, token): + nodeIndex = len(self.tree.openElements) - 1 + node = self.tree.openElements[-1] + if node.name.translate(asciiUpper2Lower) != token["name"]: + self.parser.parseError("unexpected-end-tag", {"name": token["name"]}) + + while True: + if node.name.translate(asciiUpper2Lower) == token["name"]: + # XXX this isn't in the spec but it seems necessary + if self.parser.phase == self.parser.phases["inTableText"]: + self.parser.phase.flushCharacters() + self.parser.phase = self.parser.phase.originalPhase + while self.tree.openElements.pop() != node: + assert self.tree.openElements + new_token = None + break + nodeIndex -= 1 + + node = self.tree.openElements[nodeIndex] + if node.namespace != self.tree.defaultNamespace: + continue + else: + new_token = self.parser.phase.processEndTag(token) + break + return new_token + + class AfterBodyPhase(Phase): + def __init__(self, parser, tree): + Phase.__init__(self, parser, tree) + + self.startTagHandler = _utils.MethodDispatcher([ + ("html", self.startTagHtml) + ]) + self.startTagHandler.default = self.startTagOther + + self.endTagHandler = _utils.MethodDispatcher([("html", self.endTagHtml)]) + self.endTagHandler.default = self.endTagOther + + def processEOF(self): + # Stop parsing + pass + + def processComment(self, token): + # This is needed because data is to be appended to the <html> element + # here and not to whatever is currently open. + self.tree.insertComment(token, self.tree.openElements[0]) + + def processCharacters(self, token): + self.parser.parseError("unexpected-char-after-body") + self.parser.phase = self.parser.phases["inBody"] + return token + + def startTagHtml(self, token): + return self.parser.phases["inBody"].processStartTag(token) + + def startTagOther(self, token): + self.parser.parseError("unexpected-start-tag-after-body", + {"name": token["name"]}) + self.parser.phase = self.parser.phases["inBody"] + return token + + def endTagHtml(self, name): + if self.parser.innerHTML: + self.parser.parseError("unexpected-end-tag-after-body-innerhtml") + else: + self.parser.phase = self.parser.phases["afterAfterBody"] + + def endTagOther(self, token): + self.parser.parseError("unexpected-end-tag-after-body", + {"name": token["name"]}) + self.parser.phase = self.parser.phases["inBody"] + return token + + class InFramesetPhase(Phase): + # http://www.whatwg.org/specs/web-apps/current-work/#in-frameset + def __init__(self, parser, tree): + Phase.__init__(self, parser, tree) + + self.startTagHandler = _utils.MethodDispatcher([ + ("html", self.startTagHtml), + ("frameset", self.startTagFrameset), + ("frame", self.startTagFrame), + ("noframes", self.startTagNoframes) + ]) + self.startTagHandler.default = self.startTagOther + + self.endTagHandler = _utils.MethodDispatcher([ + ("frameset", self.endTagFrameset) + ]) + self.endTagHandler.default = self.endTagOther + + def processEOF(self): + if self.tree.openElements[-1].name != "html": + self.parser.parseError("eof-in-frameset") + else: + assert self.parser.innerHTML + + def processCharacters(self, token): + self.parser.parseError("unexpected-char-in-frameset") + + def startTagFrameset(self, token): + self.tree.insertElement(token) + + def startTagFrame(self, token): + self.tree.insertElement(token) + self.tree.openElements.pop() + + def startTagNoframes(self, token): + return self.parser.phases["inBody"].processStartTag(token) + + def startTagOther(self, token): + self.parser.parseError("unexpected-start-tag-in-frameset", + {"name": token["name"]}) + + def endTagFrameset(self, token): + if self.tree.openElements[-1].name == "html": + # innerHTML case + self.parser.parseError("unexpected-frameset-in-frameset-innerhtml") + else: + self.tree.openElements.pop() + if (not self.parser.innerHTML and + self.tree.openElements[-1].name != "frameset"): + # If we're not in innerHTML mode and the current node is not a + # "frameset" element (anymore) then switch. + self.parser.phase = self.parser.phases["afterFrameset"] + + def endTagOther(self, token): + self.parser.parseError("unexpected-end-tag-in-frameset", + {"name": token["name"]}) + + class AfterFramesetPhase(Phase): + # http://www.whatwg.org/specs/web-apps/current-work/#after3 + def __init__(self, parser, tree): + Phase.__init__(self, parser, tree) + + self.startTagHandler = _utils.MethodDispatcher([ + ("html", self.startTagHtml), + ("noframes", self.startTagNoframes) + ]) + self.startTagHandler.default = self.startTagOther + + self.endTagHandler = _utils.MethodDispatcher([ + ("html", self.endTagHtml) + ]) + self.endTagHandler.default = self.endTagOther + + def processEOF(self): + # Stop parsing + pass + + def processCharacters(self, token): + self.parser.parseError("unexpected-char-after-frameset") + + def startTagNoframes(self, token): + return self.parser.phases["inHead"].processStartTag(token) + + def startTagOther(self, token): + self.parser.parseError("unexpected-start-tag-after-frameset", + {"name": token["name"]}) + + def endTagHtml(self, token): + self.parser.phase = self.parser.phases["afterAfterFrameset"] + + def endTagOther(self, token): + self.parser.parseError("unexpected-end-tag-after-frameset", + {"name": token["name"]}) + + class AfterAfterBodyPhase(Phase): + def __init__(self, parser, tree): + Phase.__init__(self, parser, tree) + + self.startTagHandler = _utils.MethodDispatcher([ + ("html", self.startTagHtml) + ]) + self.startTagHandler.default = self.startTagOther + + def processEOF(self): + pass + + def processComment(self, token): + self.tree.insertComment(token, self.tree.document) + + def processSpaceCharacters(self, token): + return self.parser.phases["inBody"].processSpaceCharacters(token) + + def processCharacters(self, token): + self.parser.parseError("expected-eof-but-got-char") + self.parser.phase = self.parser.phases["inBody"] + return token + + def startTagHtml(self, token): + return self.parser.phases["inBody"].processStartTag(token) + + def startTagOther(self, token): + self.parser.parseError("expected-eof-but-got-start-tag", + {"name": token["name"]}) + self.parser.phase = self.parser.phases["inBody"] + return token + + def processEndTag(self, token): + self.parser.parseError("expected-eof-but-got-end-tag", + {"name": token["name"]}) + self.parser.phase = self.parser.phases["inBody"] + return token + + class AfterAfterFramesetPhase(Phase): + def __init__(self, parser, tree): + Phase.__init__(self, parser, tree) + + self.startTagHandler = _utils.MethodDispatcher([ + ("html", self.startTagHtml), + ("noframes", self.startTagNoFrames) + ]) + self.startTagHandler.default = self.startTagOther + + def processEOF(self): + pass + + def processComment(self, token): + self.tree.insertComment(token, self.tree.document) + + def processSpaceCharacters(self, token): + return self.parser.phases["inBody"].processSpaceCharacters(token) + + def processCharacters(self, token): + self.parser.parseError("expected-eof-but-got-char") + + def startTagHtml(self, token): + return self.parser.phases["inBody"].processStartTag(token) + + def startTagNoFrames(self, token): + return self.parser.phases["inHead"].processStartTag(token) + + def startTagOther(self, token): + self.parser.parseError("expected-eof-but-got-start-tag", + {"name": token["name"]}) + + def processEndTag(self, token): + self.parser.parseError("expected-eof-but-got-end-tag", + {"name": token["name"]}) + # pylint:enable=unused-argument + + return { + "initial": InitialPhase, + "beforeHtml": BeforeHtmlPhase, + "beforeHead": BeforeHeadPhase, + "inHead": InHeadPhase, + "inHeadNoscript": InHeadNoscriptPhase, + "afterHead": AfterHeadPhase, + "inBody": InBodyPhase, + "text": TextPhase, + "inTable": InTablePhase, + "inTableText": InTableTextPhase, + "inCaption": InCaptionPhase, + "inColumnGroup": InColumnGroupPhase, + "inTableBody": InTableBodyPhase, + "inRow": InRowPhase, + "inCell": InCellPhase, + "inSelect": InSelectPhase, + "inSelectInTable": InSelectInTablePhase, + "inForeignContent": InForeignContentPhase, + "afterBody": AfterBodyPhase, + "inFrameset": InFramesetPhase, + "afterFrameset": AfterFramesetPhase, + "afterAfterBody": AfterAfterBodyPhase, + "afterAfterFrameset": AfterAfterFramesetPhase, + # XXX after after frameset + } + + +def adjust_attributes(token, replacements): + needs_adjustment = viewkeys(token['data']) & viewkeys(replacements) + if needs_adjustment: + token['data'] = OrderedDict((replacements.get(k, k), v) + for k, v in token['data'].items()) + + +def impliedTagToken(name, type="EndTag", attributes=None, + selfClosing=False): + if attributes is None: + attributes = {} + return {"type": tokenTypes[type], "name": name, "data": attributes, + "selfClosing": selfClosing} + + +class ParseError(Exception): + """Error in parsed document""" + pass diff --git a/venv2/lib/python2.7/site-packages/pip/_vendor/html5lib/serializer.py b/venv2/lib/python2.7/site-packages/pip/_vendor/html5lib/serializer.py new file mode 100644 index 0000000..53f4d44 --- /dev/null +++ b/venv2/lib/python2.7/site-packages/pip/_vendor/html5lib/serializer.py @@ -0,0 +1,409 @@ +from __future__ import absolute_import, division, unicode_literals +from pip._vendor.six import text_type + +import re + +from codecs import register_error, xmlcharrefreplace_errors + +from .constants import voidElements, booleanAttributes, spaceCharacters +from .constants import rcdataElements, entities, xmlEntities +from . import treewalkers, _utils +from xml.sax.saxutils import escape + +_quoteAttributeSpecChars = "".join(spaceCharacters) + "\"'=<>`" +_quoteAttributeSpec = re.compile("[" + _quoteAttributeSpecChars + "]") +_quoteAttributeLegacy = re.compile("[" + _quoteAttributeSpecChars + + "\x00\x01\x02\x03\x04\x05\x06\x07\x08\t\n" + "\x0b\x0c\r\x0e\x0f\x10\x11\x12\x13\x14\x15" + "\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f" + "\x20\x2f\x60\xa0\u1680\u180e\u180f\u2000" + "\u2001\u2002\u2003\u2004\u2005\u2006\u2007" + "\u2008\u2009\u200a\u2028\u2029\u202f\u205f" + "\u3000]") + + +_encode_entity_map = {} +_is_ucs4 = len("\U0010FFFF") == 1 +for k, v in list(entities.items()): + # skip multi-character entities + if ((_is_ucs4 and len(v) > 1) or + (not _is_ucs4 and len(v) > 2)): + continue + if v != "&": + if len(v) == 2: + v = _utils.surrogatePairToCodepoint(v) + else: + v = ord(v) + if v not in _encode_entity_map or k.islower(): + # prefer < over < and similarly for &, >, etc. + _encode_entity_map[v] = k + + +def htmlentityreplace_errors(exc): + if isinstance(exc, (UnicodeEncodeError, UnicodeTranslateError)): + res = [] + codepoints = [] + skip = False + for i, c in enumerate(exc.object[exc.start:exc.end]): + if skip: + skip = False + continue + index = i + exc.start + if _utils.isSurrogatePair(exc.object[index:min([exc.end, index + 2])]): + codepoint = _utils.surrogatePairToCodepoint(exc.object[index:index + 2]) + skip = True + else: + codepoint = ord(c) + codepoints.append(codepoint) + for cp in codepoints: + e = _encode_entity_map.get(cp) + if e: + res.append("&") + res.append(e) + if not e.endswith(";"): + res.append(";") + else: + res.append("&#x%s;" % (hex(cp)[2:])) + return ("".join(res), exc.end) + else: + return xmlcharrefreplace_errors(exc) + + +register_error("htmlentityreplace", htmlentityreplace_errors) + + +def serialize(input, tree="etree", encoding=None, **serializer_opts): + """Serializes the input token stream using the specified treewalker + + :arg input: the token stream to serialize + + :arg tree: the treewalker to use + + :arg encoding: the encoding to use + + :arg serializer_opts: any options to pass to the + :py:class:`html5lib.serializer.HTMLSerializer` that gets created + + :returns: the tree serialized as a string + + Example: + + >>> from html5lib.html5parser import parse + >>> from html5lib.serializer import serialize + >>> token_stream = parse('<html><body><p>Hi!</p></body></html>') + >>> serialize(token_stream, omit_optional_tags=False) + '<html><head></head><body><p>Hi!</p></body></html>' + + """ + # XXX: Should we cache this? + walker = treewalkers.getTreeWalker(tree) + s = HTMLSerializer(**serializer_opts) + return s.render(walker(input), encoding) + + +class HTMLSerializer(object): + + # attribute quoting options + quote_attr_values = "legacy" # be secure by default + quote_char = '"' + use_best_quote_char = True + + # tag syntax options + omit_optional_tags = True + minimize_boolean_attributes = True + use_trailing_solidus = False + space_before_trailing_solidus = True + + # escaping options + escape_lt_in_attrs = False + escape_rcdata = False + resolve_entities = True + + # miscellaneous options + alphabetical_attributes = False + inject_meta_charset = True + strip_whitespace = False + sanitize = False + + options = ("quote_attr_values", "quote_char", "use_best_quote_char", + "omit_optional_tags", "minimize_boolean_attributes", + "use_trailing_solidus", "space_before_trailing_solidus", + "escape_lt_in_attrs", "escape_rcdata", "resolve_entities", + "alphabetical_attributes", "inject_meta_charset", + "strip_whitespace", "sanitize") + + def __init__(self, **kwargs): + """Initialize HTMLSerializer + + :arg inject_meta_charset: Whether or not to inject the meta charset. + + Defaults to ``True``. + + :arg quote_attr_values: Whether to quote attribute values that don't + require quoting per legacy browser behavior (``"legacy"``), when + required by the standard (``"spec"``), or always (``"always"``). + + Defaults to ``"legacy"``. + + :arg quote_char: Use given quote character for attribute quoting. + + Defaults to ``"`` which will use double quotes unless attribute + value contains a double quote, in which case single quotes are + used. + + :arg escape_lt_in_attrs: Whether or not to escape ``<`` in attribute + values. + + Defaults to ``False``. + + :arg escape_rcdata: Whether to escape characters that need to be + escaped within normal elements within rcdata elements such as + style. + + Defaults to ``False``. + + :arg resolve_entities: Whether to resolve named character entities that + appear in the source tree. The XML predefined entities < > + & " ' are unaffected by this setting. + + Defaults to ``True``. + + :arg strip_whitespace: Whether to remove semantically meaningless + whitespace. (This compresses all whitespace to a single space + except within ``pre``.) + + Defaults to ``False``. + + :arg minimize_boolean_attributes: Shortens boolean attributes to give + just the attribute value, for example:: + + <input disabled="disabled"> + + becomes:: + + <input disabled> + + Defaults to ``True``. + + :arg use_trailing_solidus: Includes a close-tag slash at the end of the + start tag of void elements (empty elements whose end tag is + forbidden). E.g. ``<hr/>``. + + Defaults to ``False``. + + :arg space_before_trailing_solidus: Places a space immediately before + the closing slash in a tag using a trailing solidus. E.g. + ``<hr />``. Requires ``use_trailing_solidus=True``. + + Defaults to ``True``. + + :arg sanitize: Strip all unsafe or unknown constructs from output. + See :py:class:`html5lib.filters.sanitizer.Filter`. + + Defaults to ``False``. + + :arg omit_optional_tags: Omit start/end tags that are optional. + + Defaults to ``True``. + + :arg alphabetical_attributes: Reorder attributes to be in alphabetical order. + + Defaults to ``False``. + + """ + unexpected_args = frozenset(kwargs) - frozenset(self.options) + if len(unexpected_args) > 0: + raise TypeError("__init__() got an unexpected keyword argument '%s'" % next(iter(unexpected_args))) + if 'quote_char' in kwargs: + self.use_best_quote_char = False + for attr in self.options: + setattr(self, attr, kwargs.get(attr, getattr(self, attr))) + self.errors = [] + self.strict = False + + def encode(self, string): + assert(isinstance(string, text_type)) + if self.encoding: + return string.encode(self.encoding, "htmlentityreplace") + else: + return string + + def encodeStrict(self, string): + assert(isinstance(string, text_type)) + if self.encoding: + return string.encode(self.encoding, "strict") + else: + return string + + def serialize(self, treewalker, encoding=None): + # pylint:disable=too-many-nested-blocks + self.encoding = encoding + in_cdata = False + self.errors = [] + + if encoding and self.inject_meta_charset: + from .filters.inject_meta_charset import Filter + treewalker = Filter(treewalker, encoding) + # Alphabetical attributes is here under the assumption that none of + # the later filters add or change order of attributes; it needs to be + # before the sanitizer so escaped elements come out correctly + if self.alphabetical_attributes: + from .filters.alphabeticalattributes import Filter + treewalker = Filter(treewalker) + # WhitespaceFilter should be used before OptionalTagFilter + # for maximum efficiently of this latter filter + if self.strip_whitespace: + from .filters.whitespace import Filter + treewalker = Filter(treewalker) + if self.sanitize: + from .filters.sanitizer import Filter + treewalker = Filter(treewalker) + if self.omit_optional_tags: + from .filters.optionaltags import Filter + treewalker = Filter(treewalker) + + for token in treewalker: + type = token["type"] + if type == "Doctype": + doctype = "<!DOCTYPE %s" % token["name"] + + if token["publicId"]: + doctype += ' PUBLIC "%s"' % token["publicId"] + elif token["systemId"]: + doctype += " SYSTEM" + if token["systemId"]: + if token["systemId"].find('"') >= 0: + if token["systemId"].find("'") >= 0: + self.serializeError("System identifer contains both single and double quote characters") + quote_char = "'" + else: + quote_char = '"' + doctype += " %s%s%s" % (quote_char, token["systemId"], quote_char) + + doctype += ">" + yield self.encodeStrict(doctype) + + elif type in ("Characters", "SpaceCharacters"): + if type == "SpaceCharacters" or in_cdata: + if in_cdata and token["data"].find("</") >= 0: + self.serializeError("Unexpected </ in CDATA") + yield self.encode(token["data"]) + else: + yield self.encode(escape(token["data"])) + + elif type in ("StartTag", "EmptyTag"): + name = token["name"] + yield self.encodeStrict("<%s" % name) + if name in rcdataElements and not self.escape_rcdata: + in_cdata = True + elif in_cdata: + self.serializeError("Unexpected child element of a CDATA element") + for (_, attr_name), attr_value in token["data"].items(): + # TODO: Add namespace support here + k = attr_name + v = attr_value + yield self.encodeStrict(' ') + + yield self.encodeStrict(k) + if not self.minimize_boolean_attributes or \ + (k not in booleanAttributes.get(name, tuple()) and + k not in booleanAttributes.get("", tuple())): + yield self.encodeStrict("=") + if self.quote_attr_values == "always" or len(v) == 0: + quote_attr = True + elif self.quote_attr_values == "spec": + quote_attr = _quoteAttributeSpec.search(v) is not None + elif self.quote_attr_values == "legacy": + quote_attr = _quoteAttributeLegacy.search(v) is not None + else: + raise ValueError("quote_attr_values must be one of: " + "'always', 'spec', or 'legacy'") + v = v.replace("&", "&") + if self.escape_lt_in_attrs: + v = v.replace("<", "<") + if quote_attr: + quote_char = self.quote_char + if self.use_best_quote_char: + if "'" in v and '"' not in v: + quote_char = '"' + elif '"' in v and "'" not in v: + quote_char = "'" + if quote_char == "'": + v = v.replace("'", "'") + else: + v = v.replace('"', """) + yield self.encodeStrict(quote_char) + yield self.encode(v) + yield self.encodeStrict(quote_char) + else: + yield self.encode(v) + if name in voidElements and self.use_trailing_solidus: + if self.space_before_trailing_solidus: + yield self.encodeStrict(" /") + else: + yield self.encodeStrict("/") + yield self.encode(">") + + elif type == "EndTag": + name = token["name"] + if name in rcdataElements: + in_cdata = False + elif in_cdata: + self.serializeError("Unexpected child element of a CDATA element") + yield self.encodeStrict("</%s>" % name) + + elif type == "Comment": + data = token["data"] + if data.find("--") >= 0: + self.serializeError("Comment contains --") + yield self.encodeStrict("<!--%s-->" % token["data"]) + + elif type == "Entity": + name = token["name"] + key = name + ";" + if key not in entities: + self.serializeError("Entity %s not recognized" % name) + if self.resolve_entities and key not in xmlEntities: + data = entities[key] + else: + data = "&%s;" % name + yield self.encodeStrict(data) + + else: + self.serializeError(token["data"]) + + def render(self, treewalker, encoding=None): + """Serializes the stream from the treewalker into a string + + :arg treewalker: the treewalker to serialize + + :arg encoding: the string encoding to use + + :returns: the serialized tree + + Example: + + >>> from html5lib import parse, getTreeWalker + >>> from html5lib.serializer import HTMLSerializer + >>> token_stream = parse('<html><body>Hi!</body></html>') + >>> walker = getTreeWalker('etree') + >>> serializer = HTMLSerializer(omit_optional_tags=False) + >>> serializer.render(walker(token_stream)) + '<html><head></head><body>Hi!</body></html>' + + """ + if encoding: + return b"".join(list(self.serialize(treewalker, encoding))) + else: + return "".join(list(self.serialize(treewalker))) + + def serializeError(self, data="XXX ERROR MESSAGE NEEDED"): + # XXX The idea is to make data mandatory. + self.errors.append(data) + if self.strict: + raise SerializeError + + +class SerializeError(Exception): + """Error in serialized tree""" + pass diff --git a/venv2/lib/python2.7/site-packages/pip/_vendor/html5lib/treeadapters/__init__.py b/venv2/lib/python2.7/site-packages/pip/_vendor/html5lib/treeadapters/__init__.py new file mode 100644 index 0000000..7ef5959 --- /dev/null +++ b/venv2/lib/python2.7/site-packages/pip/_vendor/html5lib/treeadapters/__init__.py @@ -0,0 +1,30 @@ +"""Tree adapters let you convert from one tree structure to another + +Example: + +.. code-block:: python + + from pip._vendor import html5lib + from pip._vendor.html5lib.treeadapters import genshi + + doc = '<html><body>Hi!</body></html>' + treebuilder = html5lib.getTreeBuilder('etree') + parser = html5lib.HTMLParser(tree=treebuilder) + tree = parser.parse(doc) + TreeWalker = html5lib.getTreeWalker('etree') + + genshi_tree = genshi.to_genshi(TreeWalker(tree)) + +""" +from __future__ import absolute_import, division, unicode_literals + +from . import sax + +__all__ = ["sax"] + +try: + from . import genshi # noqa +except ImportError: + pass +else: + __all__.append("genshi") diff --git a/venv2/lib/python2.7/site-packages/pip/_vendor/html5lib/treeadapters/genshi.py b/venv2/lib/python2.7/site-packages/pip/_vendor/html5lib/treeadapters/genshi.py new file mode 100644 index 0000000..61d5fb6 --- /dev/null +++ b/venv2/lib/python2.7/site-packages/pip/_vendor/html5lib/treeadapters/genshi.py @@ -0,0 +1,54 @@ +from __future__ import absolute_import, division, unicode_literals + +from genshi.core import QName, Attrs +from genshi.core import START, END, TEXT, COMMENT, DOCTYPE + + +def to_genshi(walker): + """Convert a tree to a genshi tree + + :arg walker: the treewalker to use to walk the tree to convert it + + :returns: generator of genshi nodes + + """ + text = [] + for token in walker: + type = token["type"] + if type in ("Characters", "SpaceCharacters"): + text.append(token["data"]) + elif text: + yield TEXT, "".join(text), (None, -1, -1) + text = [] + + if type in ("StartTag", "EmptyTag"): + if token["namespace"]: + name = "{%s}%s" % (token["namespace"], token["name"]) + else: + name = token["name"] + attrs = Attrs([(QName("{%s}%s" % attr if attr[0] is not None else attr[1]), value) + for attr, value in token["data"].items()]) + yield (START, (QName(name), attrs), (None, -1, -1)) + if type == "EmptyTag": + type = "EndTag" + + if type == "EndTag": + if token["namespace"]: + name = "{%s}%s" % (token["namespace"], token["name"]) + else: + name = token["name"] + + yield END, QName(name), (None, -1, -1) + + elif type == "Comment": + yield COMMENT, token["data"], (None, -1, -1) + + elif type == "Doctype": + yield DOCTYPE, (token["name"], token["publicId"], + token["systemId"]), (None, -1, -1) + + else: + pass # FIXME: What to do? + + if text: + yield TEXT, "".join(text), (None, -1, -1) diff --git a/venv2/lib/python2.7/site-packages/pip/_vendor/html5lib/treeadapters/sax.py b/venv2/lib/python2.7/site-packages/pip/_vendor/html5lib/treeadapters/sax.py new file mode 100644 index 0000000..f4ccea5 --- /dev/null +++ b/venv2/lib/python2.7/site-packages/pip/_vendor/html5lib/treeadapters/sax.py @@ -0,0 +1,50 @@ +from __future__ import absolute_import, division, unicode_literals + +from xml.sax.xmlreader import AttributesNSImpl + +from ..constants import adjustForeignAttributes, unadjustForeignAttributes + +prefix_mapping = {} +for prefix, localName, namespace in adjustForeignAttributes.values(): + if prefix is not None: + prefix_mapping[prefix] = namespace + + +def to_sax(walker, handler): + """Call SAX-like content handler based on treewalker walker + + :arg walker: the treewalker to use to walk the tree to convert it + + :arg handler: SAX handler to use + + """ + handler.startDocument() + for prefix, namespace in prefix_mapping.items(): + handler.startPrefixMapping(prefix, namespace) + + for token in walker: + type = token["type"] + if type == "Doctype": + continue + elif type in ("StartTag", "EmptyTag"): + attrs = AttributesNSImpl(token["data"], + unadjustForeignAttributes) + handler.startElementNS((token["namespace"], token["name"]), + token["name"], + attrs) + if type == "EmptyTag": + handler.endElementNS((token["namespace"], token["name"]), + token["name"]) + elif type == "EndTag": + handler.endElementNS((token["namespace"], token["name"]), + token["name"]) + elif type in ("Characters", "SpaceCharacters"): + handler.characters(token["data"]) + elif type == "Comment": + pass + else: + assert False, "Unknown token type" + + for prefix, namespace in prefix_mapping.items(): + handler.endPrefixMapping(prefix) + handler.endDocument() diff --git a/venv2/lib/python2.7/site-packages/pip/_vendor/html5lib/treebuilders/__init__.py b/venv2/lib/python2.7/site-packages/pip/_vendor/html5lib/treebuilders/__init__.py new file mode 100644 index 0000000..d44447e --- /dev/null +++ b/venv2/lib/python2.7/site-packages/pip/_vendor/html5lib/treebuilders/__init__.py @@ -0,0 +1,88 @@ +"""A collection of modules for building different kinds of trees from HTML +documents. + +To create a treebuilder for a new type of tree, you need to do +implement several things: + +1. A set of classes for various types of elements: Document, Doctype, Comment, + Element. These must implement the interface of ``base.treebuilders.Node`` + (although comment nodes have a different signature for their constructor, + see ``treebuilders.etree.Comment``) Textual content may also be implemented + as another node type, or not, as your tree implementation requires. + +2. A treebuilder object (called ``TreeBuilder`` by convention) that inherits + from ``treebuilders.base.TreeBuilder``. This has 4 required attributes: + + * ``documentClass`` - the class to use for the bottommost node of a document + * ``elementClass`` - the class to use for HTML Elements + * ``commentClass`` - the class to use for comments + * ``doctypeClass`` - the class to use for doctypes + + It also has one required method: + + * ``getDocument`` - Returns the root node of the complete document tree + +3. If you wish to run the unit tests, you must also create a ``testSerializer`` + method on your treebuilder which accepts a node and returns a string + containing Node and its children serialized according to the format used in + the unittests + +""" + +from __future__ import absolute_import, division, unicode_literals + +from .._utils import default_etree + +treeBuilderCache = {} + + +def getTreeBuilder(treeType, implementation=None, **kwargs): + """Get a TreeBuilder class for various types of trees with built-in support + + :arg treeType: the name of the tree type required (case-insensitive). Supported + values are: + + * "dom" - A generic builder for DOM implementations, defaulting to a + xml.dom.minidom based implementation. + * "etree" - A generic builder for tree implementations exposing an + ElementTree-like interface, defaulting to xml.etree.cElementTree if + available and xml.etree.ElementTree if not. + * "lxml" - A etree-based builder for lxml.etree, handling limitations + of lxml's implementation. + + :arg implementation: (Currently applies to the "etree" and "dom" tree + types). A module implementing the tree type e.g. xml.etree.ElementTree + or xml.etree.cElementTree. + + :arg kwargs: Any additional options to pass to the TreeBuilder when + creating it. + + Example: + + >>> from html5lib.treebuilders import getTreeBuilder + >>> builder = getTreeBuilder('etree') + + """ + + treeType = treeType.lower() + if treeType not in treeBuilderCache: + if treeType == "dom": + from . import dom + # Come up with a sane default (pref. from the stdlib) + if implementation is None: + from xml.dom import minidom + implementation = minidom + # NEVER cache here, caching is done in the dom submodule + return dom.getDomModule(implementation, **kwargs).TreeBuilder + elif treeType == "lxml": + from . import etree_lxml + treeBuilderCache[treeType] = etree_lxml.TreeBuilder + elif treeType == "etree": + from . import etree + if implementation is None: + implementation = default_etree + # NEVER cache here, caching is done in the etree submodule + return etree.getETreeModule(implementation, **kwargs).TreeBuilder + else: + raise ValueError("""Unrecognised treebuilder "%s" """ % treeType) + return treeBuilderCache.get(treeType) diff --git a/venv2/lib/python2.7/site-packages/pip/_vendor/html5lib/treebuilders/base.py b/venv2/lib/python2.7/site-packages/pip/_vendor/html5lib/treebuilders/base.py new file mode 100644 index 0000000..73973db --- /dev/null +++ b/venv2/lib/python2.7/site-packages/pip/_vendor/html5lib/treebuilders/base.py @@ -0,0 +1,417 @@ +from __future__ import absolute_import, division, unicode_literals +from pip._vendor.six import text_type + +from ..constants import scopingElements, tableInsertModeElements, namespaces + +# The scope markers are inserted when entering object elements, +# marquees, table cells, and table captions, and are used to prevent formatting +# from "leaking" into tables, object elements, and marquees. +Marker = None + +listElementsMap = { + None: (frozenset(scopingElements), False), + "button": (frozenset(scopingElements | set([(namespaces["html"], "button")])), False), + "list": (frozenset(scopingElements | set([(namespaces["html"], "ol"), + (namespaces["html"], "ul")])), False), + "table": (frozenset([(namespaces["html"], "html"), + (namespaces["html"], "table")]), False), + "select": (frozenset([(namespaces["html"], "optgroup"), + (namespaces["html"], "option")]), True) +} + + +class Node(object): + """Represents an item in the tree""" + def __init__(self, name): + """Creates a Node + + :arg name: The tag name associated with the node + + """ + # The tag name assocaited with the node + self.name = name + # The parent of the current node (or None for the document node) + self.parent = None + # The value of the current node (applies to text nodes and comments) + self.value = None + # A dict holding name -> value pairs for attributes of the node + self.attributes = {} + # A list of child nodes of the current node. This must include all + # elements but not necessarily other node types. + self.childNodes = [] + # A list of miscellaneous flags that can be set on the node. + self._flags = [] + + def __str__(self): + attributesStr = " ".join(["%s=\"%s\"" % (name, value) + for name, value in + self.attributes.items()]) + if attributesStr: + return "<%s %s>" % (self.name, attributesStr) + else: + return "<%s>" % (self.name) + + def __repr__(self): + return "<%s>" % (self.name) + + def appendChild(self, node): + """Insert node as a child of the current node + + :arg node: the node to insert + + """ + raise NotImplementedError + + def insertText(self, data, insertBefore=None): + """Insert data as text in the current node, positioned before the + start of node insertBefore or to the end of the node's text. + + :arg data: the data to insert + + :arg insertBefore: True if you want to insert the text before the node + and False if you want to insert it after the node + + """ + raise NotImplementedError + + def insertBefore(self, node, refNode): + """Insert node as a child of the current node, before refNode in the + list of child nodes. Raises ValueError if refNode is not a child of + the current node + + :arg node: the node to insert + + :arg refNode: the child node to insert the node before + + """ + raise NotImplementedError + + def removeChild(self, node): + """Remove node from the children of the current node + + :arg node: the child node to remove + + """ + raise NotImplementedError + + def reparentChildren(self, newParent): + """Move all the children of the current node to newParent. + This is needed so that trees that don't store text as nodes move the + text in the correct way + + :arg newParent: the node to move all this node's children to + + """ + # XXX - should this method be made more general? + for child in self.childNodes: + newParent.appendChild(child) + self.childNodes = [] + + def cloneNode(self): + """Return a shallow copy of the current node i.e. a node with the same + name and attributes but with no parent or child nodes + """ + raise NotImplementedError + + def hasContent(self): + """Return true if the node has children or text, false otherwise + """ + raise NotImplementedError + + +class ActiveFormattingElements(list): + def append(self, node): + equalCount = 0 + if node != Marker: + for element in self[::-1]: + if element == Marker: + break + if self.nodesEqual(element, node): + equalCount += 1 + if equalCount == 3: + self.remove(element) + break + list.append(self, node) + + def nodesEqual(self, node1, node2): + if not node1.nameTuple == node2.nameTuple: + return False + + if not node1.attributes == node2.attributes: + return False + + return True + + +class TreeBuilder(object): + """Base treebuilder implementation + + * documentClass - the class to use for the bottommost node of a document + * elementClass - the class to use for HTML Elements + * commentClass - the class to use for comments + * doctypeClass - the class to use for doctypes + + """ + # pylint:disable=not-callable + + # Document class + documentClass = None + + # The class to use for creating a node + elementClass = None + + # The class to use for creating comments + commentClass = None + + # The class to use for creating doctypes + doctypeClass = None + + # Fragment class + fragmentClass = None + + def __init__(self, namespaceHTMLElements): + """Create a TreeBuilder + + :arg namespaceHTMLElements: whether or not to namespace HTML elements + + """ + if namespaceHTMLElements: + self.defaultNamespace = "http://www.w3.org/1999/xhtml" + else: + self.defaultNamespace = None + self.reset() + + def reset(self): + self.openElements = [] + self.activeFormattingElements = ActiveFormattingElements() + + # XXX - rename these to headElement, formElement + self.headPointer = None + self.formPointer = None + + self.insertFromTable = False + + self.document = self.documentClass() + + def elementInScope(self, target, variant=None): + + # If we pass a node in we match that. if we pass a string + # match any node with that name + exactNode = hasattr(target, "nameTuple") + if not exactNode: + if isinstance(target, text_type): + target = (namespaces["html"], target) + assert isinstance(target, tuple) + + listElements, invert = listElementsMap[variant] + + for node in reversed(self.openElements): + if exactNode and node == target: + return True + elif not exactNode and node.nameTuple == target: + return True + elif (invert ^ (node.nameTuple in listElements)): + return False + + assert False # We should never reach this point + + def reconstructActiveFormattingElements(self): + # Within this algorithm the order of steps described in the + # specification is not quite the same as the order of steps in the + # code. It should still do the same though. + + # Step 1: stop the algorithm when there's nothing to do. + if not self.activeFormattingElements: + return + + # Step 2 and step 3: we start with the last element. So i is -1. + i = len(self.activeFormattingElements) - 1 + entry = self.activeFormattingElements[i] + if entry == Marker or entry in self.openElements: + return + + # Step 6 + while entry != Marker and entry not in self.openElements: + if i == 0: + # This will be reset to 0 below + i = -1 + break + i -= 1 + # Step 5: let entry be one earlier in the list. + entry = self.activeFormattingElements[i] + + while True: + # Step 7 + i += 1 + + # Step 8 + entry = self.activeFormattingElements[i] + clone = entry.cloneNode() # Mainly to get a new copy of the attributes + + # Step 9 + element = self.insertElement({"type": "StartTag", + "name": clone.name, + "namespace": clone.namespace, + "data": clone.attributes}) + + # Step 10 + self.activeFormattingElements[i] = element + + # Step 11 + if element == self.activeFormattingElements[-1]: + break + + def clearActiveFormattingElements(self): + entry = self.activeFormattingElements.pop() + while self.activeFormattingElements and entry != Marker: + entry = self.activeFormattingElements.pop() + + def elementInActiveFormattingElements(self, name): + """Check if an element exists between the end of the active + formatting elements and the last marker. If it does, return it, else + return false""" + + for item in self.activeFormattingElements[::-1]: + # Check for Marker first because if it's a Marker it doesn't have a + # name attribute. + if item == Marker: + break + elif item.name == name: + return item + return False + + def insertRoot(self, token): + element = self.createElement(token) + self.openElements.append(element) + self.document.appendChild(element) + + def insertDoctype(self, token): + name = token["name"] + publicId = token["publicId"] + systemId = token["systemId"] + + doctype = self.doctypeClass(name, publicId, systemId) + self.document.appendChild(doctype) + + def insertComment(self, token, parent=None): + if parent is None: + parent = self.openElements[-1] + parent.appendChild(self.commentClass(token["data"])) + + def createElement(self, token): + """Create an element but don't insert it anywhere""" + name = token["name"] + namespace = token.get("namespace", self.defaultNamespace) + element = self.elementClass(name, namespace) + element.attributes = token["data"] + return element + + def _getInsertFromTable(self): + return self._insertFromTable + + def _setInsertFromTable(self, value): + """Switch the function used to insert an element from the + normal one to the misnested table one and back again""" + self._insertFromTable = value + if value: + self.insertElement = self.insertElementTable + else: + self.insertElement = self.insertElementNormal + + insertFromTable = property(_getInsertFromTable, _setInsertFromTable) + + def insertElementNormal(self, token): + name = token["name"] + assert isinstance(name, text_type), "Element %s not unicode" % name + namespace = token.get("namespace", self.defaultNamespace) + element = self.elementClass(name, namespace) + element.attributes = token["data"] + self.openElements[-1].appendChild(element) + self.openElements.append(element) + return element + + def insertElementTable(self, token): + """Create an element and insert it into the tree""" + element = self.createElement(token) + if self.openElements[-1].name not in tableInsertModeElements: + return self.insertElementNormal(token) + else: + # We should be in the InTable mode. This means we want to do + # special magic element rearranging + parent, insertBefore = self.getTableMisnestedNodePosition() + if insertBefore is None: + parent.appendChild(element) + else: + parent.insertBefore(element, insertBefore) + self.openElements.append(element) + return element + + def insertText(self, data, parent=None): + """Insert text data.""" + if parent is None: + parent = self.openElements[-1] + + if (not self.insertFromTable or (self.insertFromTable and + self.openElements[-1].name + not in tableInsertModeElements)): + parent.insertText(data) + else: + # We should be in the InTable mode. This means we want to do + # special magic element rearranging + parent, insertBefore = self.getTableMisnestedNodePosition() + parent.insertText(data, insertBefore) + + def getTableMisnestedNodePosition(self): + """Get the foster parent element, and sibling to insert before + (or None) when inserting a misnested table node""" + # The foster parent element is the one which comes before the most + # recently opened table element + # XXX - this is really inelegant + lastTable = None + fosterParent = None + insertBefore = None + for elm in self.openElements[::-1]: + if elm.name == "table": + lastTable = elm + break + if lastTable: + # XXX - we should really check that this parent is actually a + # node here + if lastTable.parent: + fosterParent = lastTable.parent + insertBefore = lastTable + else: + fosterParent = self.openElements[ + self.openElements.index(lastTable) - 1] + else: + fosterParent = self.openElements[0] + return fosterParent, insertBefore + + def generateImpliedEndTags(self, exclude=None): + name = self.openElements[-1].name + # XXX td, th and tr are not actually needed + if (name in frozenset(("dd", "dt", "li", "option", "optgroup", "p", "rp", "rt")) and + name != exclude): + self.openElements.pop() + # XXX This is not entirely what the specification says. We should + # investigate it more closely. + self.generateImpliedEndTags(exclude) + + def getDocument(self): + """Return the final tree""" + return self.document + + def getFragment(self): + """Return the final fragment""" + # assert self.innerHTML + fragment = self.fragmentClass() + self.openElements[0].reparentChildren(fragment) + return fragment + + def testSerializer(self, node): + """Serialize the subtree of node in the format required by unit tests + + :arg node: the node from which to start serializing + + """ + raise NotImplementedError diff --git a/venv2/lib/python2.7/site-packages/pip/_vendor/html5lib/treebuilders/dom.py b/venv2/lib/python2.7/site-packages/pip/_vendor/html5lib/treebuilders/dom.py new file mode 100644 index 0000000..dcfac22 --- /dev/null +++ b/venv2/lib/python2.7/site-packages/pip/_vendor/html5lib/treebuilders/dom.py @@ -0,0 +1,236 @@ +from __future__ import absolute_import, division, unicode_literals + + +from collections import MutableMapping +from xml.dom import minidom, Node +import weakref + +from . import base +from .. import constants +from ..constants import namespaces +from .._utils import moduleFactoryFactory + + +def getDomBuilder(DomImplementation): + Dom = DomImplementation + + class AttrList(MutableMapping): + def __init__(self, element): + self.element = element + + def __iter__(self): + return iter(self.element.attributes.keys()) + + def __setitem__(self, name, value): + if isinstance(name, tuple): + raise NotImplementedError + else: + attr = self.element.ownerDocument.createAttribute(name) + attr.value = value + self.element.attributes[name] = attr + + def __len__(self): + return len(self.element.attributes) + + def items(self): + return list(self.element.attributes.items()) + + def values(self): + return list(self.element.attributes.values()) + + def __getitem__(self, name): + if isinstance(name, tuple): + raise NotImplementedError + else: + return self.element.attributes[name].value + + def __delitem__(self, name): + if isinstance(name, tuple): + raise NotImplementedError + else: + del self.element.attributes[name] + + class NodeBuilder(base.Node): + def __init__(self, element): + base.Node.__init__(self, element.nodeName) + self.element = element + + namespace = property(lambda self: hasattr(self.element, "namespaceURI") and + self.element.namespaceURI or None) + + def appendChild(self, node): + node.parent = self + self.element.appendChild(node.element) + + def insertText(self, data, insertBefore=None): + text = self.element.ownerDocument.createTextNode(data) + if insertBefore: + self.element.insertBefore(text, insertBefore.element) + else: + self.element.appendChild(text) + + def insertBefore(self, node, refNode): + self.element.insertBefore(node.element, refNode.element) + node.parent = self + + def removeChild(self, node): + if node.element.parentNode == self.element: + self.element.removeChild(node.element) + node.parent = None + + def reparentChildren(self, newParent): + while self.element.hasChildNodes(): + child = self.element.firstChild + self.element.removeChild(child) + newParent.element.appendChild(child) + self.childNodes = [] + + def getAttributes(self): + return AttrList(self.element) + + def setAttributes(self, attributes): + if attributes: + for name, value in list(attributes.items()): + if isinstance(name, tuple): + if name[0] is not None: + qualifiedName = (name[0] + ":" + name[1]) + else: + qualifiedName = name[1] + self.element.setAttributeNS(name[2], qualifiedName, + value) + else: + self.element.setAttribute( + name, value) + attributes = property(getAttributes, setAttributes) + + def cloneNode(self): + return NodeBuilder(self.element.cloneNode(False)) + + def hasContent(self): + return self.element.hasChildNodes() + + def getNameTuple(self): + if self.namespace is None: + return namespaces["html"], self.name + else: + return self.namespace, self.name + + nameTuple = property(getNameTuple) + + class TreeBuilder(base.TreeBuilder): # pylint:disable=unused-variable + def documentClass(self): + self.dom = Dom.getDOMImplementation().createDocument(None, None, None) + return weakref.proxy(self) + + def insertDoctype(self, token): + name = token["name"] + publicId = token["publicId"] + systemId = token["systemId"] + + domimpl = Dom.getDOMImplementation() + doctype = domimpl.createDocumentType(name, publicId, systemId) + self.document.appendChild(NodeBuilder(doctype)) + if Dom == minidom: + doctype.ownerDocument = self.dom + + def elementClass(self, name, namespace=None): + if namespace is None and self.defaultNamespace is None: + node = self.dom.createElement(name) + else: + node = self.dom.createElementNS(namespace, name) + + return NodeBuilder(node) + + def commentClass(self, data): + return NodeBuilder(self.dom.createComment(data)) + + def fragmentClass(self): + return NodeBuilder(self.dom.createDocumentFragment()) + + def appendChild(self, node): + self.dom.appendChild(node.element) + + def testSerializer(self, element): + return testSerializer(element) + + def getDocument(self): + return self.dom + + def getFragment(self): + return base.TreeBuilder.getFragment(self).element + + def insertText(self, data, parent=None): + data = data + if parent != self: + base.TreeBuilder.insertText(self, data, parent) + else: + # HACK: allow text nodes as children of the document node + if hasattr(self.dom, '_child_node_types'): + # pylint:disable=protected-access + if Node.TEXT_NODE not in self.dom._child_node_types: + self.dom._child_node_types = list(self.dom._child_node_types) + self.dom._child_node_types.append(Node.TEXT_NODE) + self.dom.appendChild(self.dom.createTextNode(data)) + + implementation = DomImplementation + name = None + + def testSerializer(element): + element.normalize() + rv = [] + + def serializeElement(element, indent=0): + if element.nodeType == Node.DOCUMENT_TYPE_NODE: + if element.name: + if element.publicId or element.systemId: + publicId = element.publicId or "" + systemId = element.systemId or "" + rv.append("""|%s<!DOCTYPE %s "%s" "%s">""" % + (' ' * indent, element.name, publicId, systemId)) + else: + rv.append("|%s<!DOCTYPE %s>" % (' ' * indent, element.name)) + else: + rv.append("|%s<!DOCTYPE >" % (' ' * indent,)) + elif element.nodeType == Node.DOCUMENT_NODE: + rv.append("#document") + elif element.nodeType == Node.DOCUMENT_FRAGMENT_NODE: + rv.append("#document-fragment") + elif element.nodeType == Node.COMMENT_NODE: + rv.append("|%s<!-- %s -->" % (' ' * indent, element.nodeValue)) + elif element.nodeType == Node.TEXT_NODE: + rv.append("|%s\"%s\"" % (' ' * indent, element.nodeValue)) + else: + if (hasattr(element, "namespaceURI") and + element.namespaceURI is not None): + name = "%s %s" % (constants.prefixes[element.namespaceURI], + element.nodeName) + else: + name = element.nodeName + rv.append("|%s<%s>" % (' ' * indent, name)) + if element.hasAttributes(): + attributes = [] + for i in range(len(element.attributes)): + attr = element.attributes.item(i) + name = attr.nodeName + value = attr.value + ns = attr.namespaceURI + if ns: + name = "%s %s" % (constants.prefixes[ns], attr.localName) + else: + name = attr.nodeName + attributes.append((name, value)) + + for name, value in sorted(attributes): + rv.append('|%s%s="%s"' % (' ' * (indent + 2), name, value)) + indent += 2 + for child in element.childNodes: + serializeElement(child, indent) + serializeElement(element, 0) + + return "\n".join(rv) + + return locals() + + +# The actual means to get a module! +getDomModule = moduleFactoryFactory(getDomBuilder) diff --git a/venv2/lib/python2.7/site-packages/pip/_vendor/html5lib/treebuilders/etree.py b/venv2/lib/python2.7/site-packages/pip/_vendor/html5lib/treebuilders/etree.py new file mode 100644 index 0000000..0dedf44 --- /dev/null +++ b/venv2/lib/python2.7/site-packages/pip/_vendor/html5lib/treebuilders/etree.py @@ -0,0 +1,340 @@ +from __future__ import absolute_import, division, unicode_literals +# pylint:disable=protected-access + +from pip._vendor.six import text_type + +import re + +from . import base +from .. import _ihatexml +from .. import constants +from ..constants import namespaces +from .._utils import moduleFactoryFactory + +tag_regexp = re.compile("{([^}]*)}(.*)") + + +def getETreeBuilder(ElementTreeImplementation, fullTree=False): + ElementTree = ElementTreeImplementation + ElementTreeCommentType = ElementTree.Comment("asd").tag + + class Element(base.Node): + def __init__(self, name, namespace=None): + self._name = name + self._namespace = namespace + self._element = ElementTree.Element(self._getETreeTag(name, + namespace)) + if namespace is None: + self.nameTuple = namespaces["html"], self._name + else: + self.nameTuple = self._namespace, self._name + self.parent = None + self._childNodes = [] + self._flags = [] + + def _getETreeTag(self, name, namespace): + if namespace is None: + etree_tag = name + else: + etree_tag = "{%s}%s" % (namespace, name) + return etree_tag + + def _setName(self, name): + self._name = name + self._element.tag = self._getETreeTag(self._name, self._namespace) + + def _getName(self): + return self._name + + name = property(_getName, _setName) + + def _setNamespace(self, namespace): + self._namespace = namespace + self._element.tag = self._getETreeTag(self._name, self._namespace) + + def _getNamespace(self): + return self._namespace + + namespace = property(_getNamespace, _setNamespace) + + def _getAttributes(self): + return self._element.attrib + + def _setAttributes(self, attributes): + # Delete existing attributes first + # XXX - there may be a better way to do this... + for key in list(self._element.attrib.keys()): + del self._element.attrib[key] + for key, value in attributes.items(): + if isinstance(key, tuple): + name = "{%s}%s" % (key[2], key[1]) + else: + name = key + self._element.set(name, value) + + attributes = property(_getAttributes, _setAttributes) + + def _getChildNodes(self): + return self._childNodes + + def _setChildNodes(self, value): + del self._element[:] + self._childNodes = [] + for element in value: + self.insertChild(element) + + childNodes = property(_getChildNodes, _setChildNodes) + + def hasContent(self): + """Return true if the node has children or text""" + return bool(self._element.text or len(self._element)) + + def appendChild(self, node): + self._childNodes.append(node) + self._element.append(node._element) + node.parent = self + + def insertBefore(self, node, refNode): + index = list(self._element).index(refNode._element) + self._element.insert(index, node._element) + node.parent = self + + def removeChild(self, node): + self._childNodes.remove(node) + self._element.remove(node._element) + node.parent = None + + def insertText(self, data, insertBefore=None): + if not(len(self._element)): + if not self._element.text: + self._element.text = "" + self._element.text += data + elif insertBefore is None: + # Insert the text as the tail of the last child element + if not self._element[-1].tail: + self._element[-1].tail = "" + self._element[-1].tail += data + else: + # Insert the text before the specified node + children = list(self._element) + index = children.index(insertBefore._element) + if index > 0: + if not self._element[index - 1].tail: + self._element[index - 1].tail = "" + self._element[index - 1].tail += data + else: + if not self._element.text: + self._element.text = "" + self._element.text += data + + def cloneNode(self): + element = type(self)(self.name, self.namespace) + for name, value in self.attributes.items(): + element.attributes[name] = value + return element + + def reparentChildren(self, newParent): + if newParent.childNodes: + newParent.childNodes[-1]._element.tail += self._element.text + else: + if not newParent._element.text: + newParent._element.text = "" + if self._element.text is not None: + newParent._element.text += self._element.text + self._element.text = "" + base.Node.reparentChildren(self, newParent) + + class Comment(Element): + def __init__(self, data): + # Use the superclass constructor to set all properties on the + # wrapper element + self._element = ElementTree.Comment(data) + self.parent = None + self._childNodes = [] + self._flags = [] + + def _getData(self): + return self._element.text + + def _setData(self, value): + self._element.text = value + + data = property(_getData, _setData) + + class DocumentType(Element): + def __init__(self, name, publicId, systemId): + Element.__init__(self, "<!DOCTYPE>") + self._element.text = name + self.publicId = publicId + self.systemId = systemId + + def _getPublicId(self): + return self._element.get("publicId", "") + + def _setPublicId(self, value): + if value is not None: + self._element.set("publicId", value) + + publicId = property(_getPublicId, _setPublicId) + + def _getSystemId(self): + return self._element.get("systemId", "") + + def _setSystemId(self, value): + if value is not None: + self._element.set("systemId", value) + + systemId = property(_getSystemId, _setSystemId) + + class Document(Element): + def __init__(self): + Element.__init__(self, "DOCUMENT_ROOT") + + class DocumentFragment(Element): + def __init__(self): + Element.__init__(self, "DOCUMENT_FRAGMENT") + + def testSerializer(element): + rv = [] + + def serializeElement(element, indent=0): + if not(hasattr(element, "tag")): + element = element.getroot() + if element.tag == "<!DOCTYPE>": + if element.get("publicId") or element.get("systemId"): + publicId = element.get("publicId") or "" + systemId = element.get("systemId") or "" + rv.append("""<!DOCTYPE %s "%s" "%s">""" % + (element.text, publicId, systemId)) + else: + rv.append("<!DOCTYPE %s>" % (element.text,)) + elif element.tag == "DOCUMENT_ROOT": + rv.append("#document") + if element.text is not None: + rv.append("|%s\"%s\"" % (' ' * (indent + 2), element.text)) + if element.tail is not None: + raise TypeError("Document node cannot have tail") + if hasattr(element, "attrib") and len(element.attrib): + raise TypeError("Document node cannot have attributes") + elif element.tag == ElementTreeCommentType: + rv.append("|%s<!-- %s -->" % (' ' * indent, element.text)) + else: + assert isinstance(element.tag, text_type), \ + "Expected unicode, got %s, %s" % (type(element.tag), element.tag) + nsmatch = tag_regexp.match(element.tag) + + if nsmatch is None: + name = element.tag + else: + ns, name = nsmatch.groups() + prefix = constants.prefixes[ns] + name = "%s %s" % (prefix, name) + rv.append("|%s<%s>" % (' ' * indent, name)) + + if hasattr(element, "attrib"): + attributes = [] + for name, value in element.attrib.items(): + nsmatch = tag_regexp.match(name) + if nsmatch is not None: + ns, name = nsmatch.groups() + prefix = constants.prefixes[ns] + attr_string = "%s %s" % (prefix, name) + else: + attr_string = name + attributes.append((attr_string, value)) + + for name, value in sorted(attributes): + rv.append('|%s%s="%s"' % (' ' * (indent + 2), name, value)) + if element.text: + rv.append("|%s\"%s\"" % (' ' * (indent + 2), element.text)) + indent += 2 + for child in element: + serializeElement(child, indent) + if element.tail: + rv.append("|%s\"%s\"" % (' ' * (indent - 2), element.tail)) + serializeElement(element, 0) + + return "\n".join(rv) + + def tostring(element): # pylint:disable=unused-variable + """Serialize an element and its child nodes to a string""" + rv = [] + filter = _ihatexml.InfosetFilter() + + def serializeElement(element): + if isinstance(element, ElementTree.ElementTree): + element = element.getroot() + + if element.tag == "<!DOCTYPE>": + if element.get("publicId") or element.get("systemId"): + publicId = element.get("publicId") or "" + systemId = element.get("systemId") or "" + rv.append("""<!DOCTYPE %s PUBLIC "%s" "%s">""" % + (element.text, publicId, systemId)) + else: + rv.append("<!DOCTYPE %s>" % (element.text,)) + elif element.tag == "DOCUMENT_ROOT": + if element.text is not None: + rv.append(element.text) + if element.tail is not None: + raise TypeError("Document node cannot have tail") + if hasattr(element, "attrib") and len(element.attrib): + raise TypeError("Document node cannot have attributes") + + for child in element: + serializeElement(child) + + elif element.tag == ElementTreeCommentType: + rv.append("<!--%s-->" % (element.text,)) + else: + # This is assumed to be an ordinary element + if not element.attrib: + rv.append("<%s>" % (filter.fromXmlName(element.tag),)) + else: + attr = " ".join(["%s=\"%s\"" % ( + filter.fromXmlName(name), value) + for name, value in element.attrib.items()]) + rv.append("<%s %s>" % (element.tag, attr)) + if element.text: + rv.append(element.text) + + for child in element: + serializeElement(child) + + rv.append("</%s>" % (element.tag,)) + + if element.tail: + rv.append(element.tail) + + serializeElement(element) + + return "".join(rv) + + class TreeBuilder(base.TreeBuilder): # pylint:disable=unused-variable + documentClass = Document + doctypeClass = DocumentType + elementClass = Element + commentClass = Comment + fragmentClass = DocumentFragment + implementation = ElementTreeImplementation + + def testSerializer(self, element): + return testSerializer(element) + + def getDocument(self): + if fullTree: + return self.document._element + else: + if self.defaultNamespace is not None: + return self.document._element.find( + "{%s}html" % self.defaultNamespace) + else: + return self.document._element.find("html") + + def getFragment(self): + return base.TreeBuilder.getFragment(self)._element + + return locals() + + +getETreeModule = moduleFactoryFactory(getETreeBuilder) diff --git a/venv2/lib/python2.7/site-packages/pip/_vendor/html5lib/treebuilders/etree_lxml.py b/venv2/lib/python2.7/site-packages/pip/_vendor/html5lib/treebuilders/etree_lxml.py new file mode 100644 index 0000000..ca12a99 --- /dev/null +++ b/venv2/lib/python2.7/site-packages/pip/_vendor/html5lib/treebuilders/etree_lxml.py @@ -0,0 +1,366 @@ +"""Module for supporting the lxml.etree library. The idea here is to use as much +of the native library as possible, without using fragile hacks like custom element +names that break between releases. The downside of this is that we cannot represent +all possible trees; specifically the following are known to cause problems: + +Text or comments as siblings of the root element +Docypes with no name + +When any of these things occur, we emit a DataLossWarning +""" + +from __future__ import absolute_import, division, unicode_literals +# pylint:disable=protected-access + +import warnings +import re +import sys + +from . import base +from ..constants import DataLossWarning +from .. import constants +from . import etree as etree_builders +from .. import _ihatexml + +import lxml.etree as etree + + +fullTree = True +tag_regexp = re.compile("{([^}]*)}(.*)") + +comment_type = etree.Comment("asd").tag + + +class DocumentType(object): + def __init__(self, name, publicId, systemId): + self.name = name + self.publicId = publicId + self.systemId = systemId + + +class Document(object): + def __init__(self): + self._elementTree = None + self._childNodes = [] + + def appendChild(self, element): + self._elementTree.getroot().addnext(element._element) + + def _getChildNodes(self): + return self._childNodes + + childNodes = property(_getChildNodes) + + +def testSerializer(element): + rv = [] + infosetFilter = _ihatexml.InfosetFilter(preventDoubleDashComments=True) + + def serializeElement(element, indent=0): + if not hasattr(element, "tag"): + if hasattr(element, "getroot"): + # Full tree case + rv.append("#document") + if element.docinfo.internalDTD: + if not (element.docinfo.public_id or + element.docinfo.system_url): + dtd_str = "<!DOCTYPE %s>" % element.docinfo.root_name + else: + dtd_str = """<!DOCTYPE %s "%s" "%s">""" % ( + element.docinfo.root_name, + element.docinfo.public_id, + element.docinfo.system_url) + rv.append("|%s%s" % (' ' * (indent + 2), dtd_str)) + next_element = element.getroot() + while next_element.getprevious() is not None: + next_element = next_element.getprevious() + while next_element is not None: + serializeElement(next_element, indent + 2) + next_element = next_element.getnext() + elif isinstance(element, str) or isinstance(element, bytes): + # Text in a fragment + assert isinstance(element, str) or sys.version_info[0] == 2 + rv.append("|%s\"%s\"" % (' ' * indent, element)) + else: + # Fragment case + rv.append("#document-fragment") + for next_element in element: + serializeElement(next_element, indent + 2) + elif element.tag == comment_type: + rv.append("|%s<!-- %s -->" % (' ' * indent, element.text)) + if hasattr(element, "tail") and element.tail: + rv.append("|%s\"%s\"" % (' ' * indent, element.tail)) + else: + assert isinstance(element, etree._Element) + nsmatch = etree_builders.tag_regexp.match(element.tag) + if nsmatch is not None: + ns = nsmatch.group(1) + tag = nsmatch.group(2) + prefix = constants.prefixes[ns] + rv.append("|%s<%s %s>" % (' ' * indent, prefix, + infosetFilter.fromXmlName(tag))) + else: + rv.append("|%s<%s>" % (' ' * indent, + infosetFilter.fromXmlName(element.tag))) + + if hasattr(element, "attrib"): + attributes = [] + for name, value in element.attrib.items(): + nsmatch = tag_regexp.match(name) + if nsmatch is not None: + ns, name = nsmatch.groups() + name = infosetFilter.fromXmlName(name) + prefix = constants.prefixes[ns] + attr_string = "%s %s" % (prefix, name) + else: + attr_string = infosetFilter.fromXmlName(name) + attributes.append((attr_string, value)) + + for name, value in sorted(attributes): + rv.append('|%s%s="%s"' % (' ' * (indent + 2), name, value)) + + if element.text: + rv.append("|%s\"%s\"" % (' ' * (indent + 2), element.text)) + indent += 2 + for child in element: + serializeElement(child, indent) + if hasattr(element, "tail") and element.tail: + rv.append("|%s\"%s\"" % (' ' * (indent - 2), element.tail)) + serializeElement(element, 0) + + return "\n".join(rv) + + +def tostring(element): + """Serialize an element and its child nodes to a string""" + rv = [] + + def serializeElement(element): + if not hasattr(element, "tag"): + if element.docinfo.internalDTD: + if element.docinfo.doctype: + dtd_str = element.docinfo.doctype + else: + dtd_str = "<!DOCTYPE %s>" % element.docinfo.root_name + rv.append(dtd_str) + serializeElement(element.getroot()) + + elif element.tag == comment_type: + rv.append("<!--%s-->" % (element.text,)) + + else: + # This is assumed to be an ordinary element + if not element.attrib: + rv.append("<%s>" % (element.tag,)) + else: + attr = " ".join(["%s=\"%s\"" % (name, value) + for name, value in element.attrib.items()]) + rv.append("<%s %s>" % (element.tag, attr)) + if element.text: + rv.append(element.text) + + for child in element: + serializeElement(child) + + rv.append("</%s>" % (element.tag,)) + + if hasattr(element, "tail") and element.tail: + rv.append(element.tail) + + serializeElement(element) + + return "".join(rv) + + +class TreeBuilder(base.TreeBuilder): + documentClass = Document + doctypeClass = DocumentType + elementClass = None + commentClass = None + fragmentClass = Document + implementation = etree + + def __init__(self, namespaceHTMLElements, fullTree=False): + builder = etree_builders.getETreeModule(etree, fullTree=fullTree) + infosetFilter = self.infosetFilter = _ihatexml.InfosetFilter(preventDoubleDashComments=True) + self.namespaceHTMLElements = namespaceHTMLElements + + class Attributes(dict): + def __init__(self, element, value=None): + if value is None: + value = {} + self._element = element + dict.__init__(self, value) # pylint:disable=non-parent-init-called + for key, value in self.items(): + if isinstance(key, tuple): + name = "{%s}%s" % (key[2], infosetFilter.coerceAttribute(key[1])) + else: + name = infosetFilter.coerceAttribute(key) + self._element._element.attrib[name] = value + + def __setitem__(self, key, value): + dict.__setitem__(self, key, value) + if isinstance(key, tuple): + name = "{%s}%s" % (key[2], infosetFilter.coerceAttribute(key[1])) + else: + name = infosetFilter.coerceAttribute(key) + self._element._element.attrib[name] = value + + class Element(builder.Element): + def __init__(self, name, namespace): + name = infosetFilter.coerceElement(name) + builder.Element.__init__(self, name, namespace=namespace) + self._attributes = Attributes(self) + + def _setName(self, name): + self._name = infosetFilter.coerceElement(name) + self._element.tag = self._getETreeTag( + self._name, self._namespace) + + def _getName(self): + return infosetFilter.fromXmlName(self._name) + + name = property(_getName, _setName) + + def _getAttributes(self): + return self._attributes + + def _setAttributes(self, attributes): + self._attributes = Attributes(self, attributes) + + attributes = property(_getAttributes, _setAttributes) + + def insertText(self, data, insertBefore=None): + data = infosetFilter.coerceCharacters(data) + builder.Element.insertText(self, data, insertBefore) + + def appendChild(self, child): + builder.Element.appendChild(self, child) + + class Comment(builder.Comment): + def __init__(self, data): + data = infosetFilter.coerceComment(data) + builder.Comment.__init__(self, data) + + def _setData(self, data): + data = infosetFilter.coerceComment(data) + self._element.text = data + + def _getData(self): + return self._element.text + + data = property(_getData, _setData) + + self.elementClass = Element + self.commentClass = Comment + # self.fragmentClass = builder.DocumentFragment + base.TreeBuilder.__init__(self, namespaceHTMLElements) + + def reset(self): + base.TreeBuilder.reset(self) + self.insertComment = self.insertCommentInitial + self.initial_comments = [] + self.doctype = None + + def testSerializer(self, element): + return testSerializer(element) + + def getDocument(self): + if fullTree: + return self.document._elementTree + else: + return self.document._elementTree.getroot() + + def getFragment(self): + fragment = [] + element = self.openElements[0]._element + if element.text: + fragment.append(element.text) + fragment.extend(list(element)) + if element.tail: + fragment.append(element.tail) + return fragment + + def insertDoctype(self, token): + name = token["name"] + publicId = token["publicId"] + systemId = token["systemId"] + + if not name: + warnings.warn("lxml cannot represent empty doctype", DataLossWarning) + self.doctype = None + else: + coercedName = self.infosetFilter.coerceElement(name) + if coercedName != name: + warnings.warn("lxml cannot represent non-xml doctype", DataLossWarning) + + doctype = self.doctypeClass(coercedName, publicId, systemId) + self.doctype = doctype + + def insertCommentInitial(self, data, parent=None): + assert parent is None or parent is self.document + assert self.document._elementTree is None + self.initial_comments.append(data) + + def insertCommentMain(self, data, parent=None): + if (parent == self.document and + self.document._elementTree.getroot()[-1].tag == comment_type): + warnings.warn("lxml cannot represent adjacent comments beyond the root elements", DataLossWarning) + super(TreeBuilder, self).insertComment(data, parent) + + def insertRoot(self, token): + # Because of the way libxml2 works, it doesn't seem to be possible to + # alter information like the doctype after the tree has been parsed. + # Therefore we need to use the built-in parser to create our initial + # tree, after which we can add elements like normal + docStr = "" + if self.doctype: + assert self.doctype.name + docStr += "<!DOCTYPE %s" % self.doctype.name + if (self.doctype.publicId is not None or + self.doctype.systemId is not None): + docStr += (' PUBLIC "%s" ' % + (self.infosetFilter.coercePubid(self.doctype.publicId or ""))) + if self.doctype.systemId: + sysid = self.doctype.systemId + if sysid.find("'") >= 0 and sysid.find('"') >= 0: + warnings.warn("DOCTYPE system cannot contain single and double quotes", DataLossWarning) + sysid = sysid.replace("'", 'U00027') + if sysid.find("'") >= 0: + docStr += '"%s"' % sysid + else: + docStr += "'%s'" % sysid + else: + docStr += "''" + docStr += ">" + if self.doctype.name != token["name"]: + warnings.warn("lxml cannot represent doctype with a different name to the root element", DataLossWarning) + docStr += "<THIS_SHOULD_NEVER_APPEAR_PUBLICLY/>" + root = etree.fromstring(docStr) + + # Append the initial comments: + for comment_token in self.initial_comments: + comment = self.commentClass(comment_token["data"]) + root.addprevious(comment._element) + + # Create the root document and add the ElementTree to it + self.document = self.documentClass() + self.document._elementTree = root.getroottree() + + # Give the root element the right name + name = token["name"] + namespace = token.get("namespace", self.defaultNamespace) + if namespace is None: + etree_tag = name + else: + etree_tag = "{%s}%s" % (namespace, name) + root.tag = etree_tag + + # Add the root element to the internal child/open data structures + root_element = self.elementClass(name, namespace) + root_element._element = root + self.document._childNodes.append(root_element) + self.openElements.append(root_element) + + # Reset to the default insert comment function + self.insertComment = self.insertCommentMain diff --git a/venv2/lib/python2.7/site-packages/pip/_vendor/html5lib/treewalkers/__init__.py b/venv2/lib/python2.7/site-packages/pip/_vendor/html5lib/treewalkers/__init__.py new file mode 100644 index 0000000..9bec207 --- /dev/null +++ b/venv2/lib/python2.7/site-packages/pip/_vendor/html5lib/treewalkers/__init__.py @@ -0,0 +1,154 @@ +"""A collection of modules for iterating through different kinds of +tree, generating tokens identical to those produced by the tokenizer +module. + +To create a tree walker for a new type of tree, you need to do +implement a tree walker object (called TreeWalker by convention) that +implements a 'serialize' method taking a tree as sole argument and +returning an iterator generating tokens. +""" + +from __future__ import absolute_import, division, unicode_literals + +from .. import constants +from .._utils import default_etree + +__all__ = ["getTreeWalker", "pprint"] + +treeWalkerCache = {} + + +def getTreeWalker(treeType, implementation=None, **kwargs): + """Get a TreeWalker class for various types of tree with built-in support + + :arg str treeType: the name of the tree type required (case-insensitive). + Supported values are: + + * "dom": The xml.dom.minidom DOM implementation + * "etree": A generic walker for tree implementations exposing an + elementtree-like interface (known to work with ElementTree, + cElementTree and lxml.etree). + * "lxml": Optimized walker for lxml.etree + * "genshi": a Genshi stream + + :arg implementation: A module implementing the tree type e.g. + xml.etree.ElementTree or cElementTree (Currently applies to the "etree" + tree type only). + + :arg kwargs: keyword arguments passed to the etree walker--for other + walkers, this has no effect + + :returns: a TreeWalker class + + """ + + treeType = treeType.lower() + if treeType not in treeWalkerCache: + if treeType == "dom": + from . import dom + treeWalkerCache[treeType] = dom.TreeWalker + elif treeType == "genshi": + from . import genshi + treeWalkerCache[treeType] = genshi.TreeWalker + elif treeType == "lxml": + from . import etree_lxml + treeWalkerCache[treeType] = etree_lxml.TreeWalker + elif treeType == "etree": + from . import etree + if implementation is None: + implementation = default_etree + # XXX: NEVER cache here, caching is done in the etree submodule + return etree.getETreeModule(implementation, **kwargs).TreeWalker + return treeWalkerCache.get(treeType) + + +def concatenateCharacterTokens(tokens): + pendingCharacters = [] + for token in tokens: + type = token["type"] + if type in ("Characters", "SpaceCharacters"): + pendingCharacters.append(token["data"]) + else: + if pendingCharacters: + yield {"type": "Characters", "data": "".join(pendingCharacters)} + pendingCharacters = [] + yield token + if pendingCharacters: + yield {"type": "Characters", "data": "".join(pendingCharacters)} + + +def pprint(walker): + """Pretty printer for tree walkers + + Takes a TreeWalker instance and pretty prints the output of walking the tree. + + :arg walker: a TreeWalker instance + + """ + output = [] + indent = 0 + for token in concatenateCharacterTokens(walker): + type = token["type"] + if type in ("StartTag", "EmptyTag"): + # tag name + if token["namespace"] and token["namespace"] != constants.namespaces["html"]: + if token["namespace"] in constants.prefixes: + ns = constants.prefixes[token["namespace"]] + else: + ns = token["namespace"] + name = "%s %s" % (ns, token["name"]) + else: + name = token["name"] + output.append("%s<%s>" % (" " * indent, name)) + indent += 2 + # attributes (sorted for consistent ordering) + attrs = token["data"] + for (namespace, localname), value in sorted(attrs.items()): + if namespace: + if namespace in constants.prefixes: + ns = constants.prefixes[namespace] + else: + ns = namespace + name = "%s %s" % (ns, localname) + else: + name = localname + output.append("%s%s=\"%s\"" % (" " * indent, name, value)) + # self-closing + if type == "EmptyTag": + indent -= 2 + + elif type == "EndTag": + indent -= 2 + + elif type == "Comment": + output.append("%s<!-- %s -->" % (" " * indent, token["data"])) + + elif type == "Doctype": + if token["name"]: + if token["publicId"]: + output.append("""%s<!DOCTYPE %s "%s" "%s">""" % + (" " * indent, + token["name"], + token["publicId"], + token["systemId"] if token["systemId"] else "")) + elif token["systemId"]: + output.append("""%s<!DOCTYPE %s "" "%s">""" % + (" " * indent, + token["name"], + token["systemId"])) + else: + output.append("%s<!DOCTYPE %s>" % (" " * indent, + token["name"])) + else: + output.append("%s<!DOCTYPE >" % (" " * indent,)) + + elif type == "Characters": + output.append("%s\"%s\"" % (" " * indent, token["data"])) + + elif type == "SpaceCharacters": + assert False, "concatenateCharacterTokens should have got rid of all Space tokens" + + else: + raise ValueError("Unknown token type, %s" % type) + + return "\n".join(output) diff --git a/venv2/lib/python2.7/site-packages/pip/_vendor/html5lib/treewalkers/base.py b/venv2/lib/python2.7/site-packages/pip/_vendor/html5lib/treewalkers/base.py new file mode 100644 index 0000000..80c474c --- /dev/null +++ b/venv2/lib/python2.7/site-packages/pip/_vendor/html5lib/treewalkers/base.py @@ -0,0 +1,252 @@ +from __future__ import absolute_import, division, unicode_literals + +from xml.dom import Node +from ..constants import namespaces, voidElements, spaceCharacters + +__all__ = ["DOCUMENT", "DOCTYPE", "TEXT", "ELEMENT", "COMMENT", "ENTITY", "UNKNOWN", + "TreeWalker", "NonRecursiveTreeWalker"] + +DOCUMENT = Node.DOCUMENT_NODE +DOCTYPE = Node.DOCUMENT_TYPE_NODE +TEXT = Node.TEXT_NODE +ELEMENT = Node.ELEMENT_NODE +COMMENT = Node.COMMENT_NODE +ENTITY = Node.ENTITY_NODE +UNKNOWN = "<#UNKNOWN#>" + +spaceCharacters = "".join(spaceCharacters) + + +class TreeWalker(object): + """Walks a tree yielding tokens + + Tokens are dicts that all have a ``type`` field specifying the type of the + token. + + """ + def __init__(self, tree): + """Creates a TreeWalker + + :arg tree: the tree to walk + + """ + self.tree = tree + + def __iter__(self): + raise NotImplementedError + + def error(self, msg): + """Generates an error token with the given message + + :arg msg: the error message + + :returns: SerializeError token + + """ + return {"type": "SerializeError", "data": msg} + + def emptyTag(self, namespace, name, attrs, hasChildren=False): + """Generates an EmptyTag token + + :arg namespace: the namespace of the token--can be ``None`` + + :arg name: the name of the element + + :arg attrs: the attributes of the element as a dict + + :arg hasChildren: whether or not to yield a SerializationError because + this tag shouldn't have children + + :returns: EmptyTag token + + """ + yield {"type": "EmptyTag", "name": name, + "namespace": namespace, + "data": attrs} + if hasChildren: + yield self.error("Void element has children") + + def startTag(self, namespace, name, attrs): + """Generates a StartTag token + + :arg namespace: the namespace of the token--can be ``None`` + + :arg name: the name of the element + + :arg attrs: the attributes of the element as a dict + + :returns: StartTag token + + """ + return {"type": "StartTag", + "name": name, + "namespace": namespace, + "data": attrs} + + def endTag(self, namespace, name): + """Generates an EndTag token + + :arg namespace: the namespace of the token--can be ``None`` + + :arg name: the name of the element + + :returns: EndTag token + + """ + return {"type": "EndTag", + "name": name, + "namespace": namespace} + + def text(self, data): + """Generates SpaceCharacters and Characters tokens + + Depending on what's in the data, this generates one or more + ``SpaceCharacters`` and ``Characters`` tokens. + + For example: + + >>> from html5lib.treewalkers.base import TreeWalker + >>> # Give it an empty tree just so it instantiates + >>> walker = TreeWalker([]) + >>> list(walker.text('')) + [] + >>> list(walker.text(' ')) + [{u'data': ' ', u'type': u'SpaceCharacters'}] + >>> list(walker.text(' abc ')) # doctest: +NORMALIZE_WHITESPACE + [{u'data': ' ', u'type': u'SpaceCharacters'}, + {u'data': u'abc', u'type': u'Characters'}, + {u'data': u' ', u'type': u'SpaceCharacters'}] + + :arg data: the text data + + :returns: one or more ``SpaceCharacters`` and ``Characters`` tokens + + """ + data = data + middle = data.lstrip(spaceCharacters) + left = data[:len(data) - len(middle)] + if left: + yield {"type": "SpaceCharacters", "data": left} + data = middle + middle = data.rstrip(spaceCharacters) + right = data[len(middle):] + if middle: + yield {"type": "Characters", "data": middle} + if right: + yield {"type": "SpaceCharacters", "data": right} + + def comment(self, data): + """Generates a Comment token + + :arg data: the comment + + :returns: Comment token + + """ + return {"type": "Comment", "data": data} + + def doctype(self, name, publicId=None, systemId=None): + """Generates a Doctype token + + :arg name: + + :arg publicId: + + :arg systemId: + + :returns: the Doctype token + + """ + return {"type": "Doctype", + "name": name, + "publicId": publicId, + "systemId": systemId} + + def entity(self, name): + """Generates an Entity token + + :arg name: the entity name + + :returns: an Entity token + + """ + return {"type": "Entity", "name": name} + + def unknown(self, nodeType): + """Handles unknown node types""" + return self.error("Unknown node type: " + nodeType) + + +class NonRecursiveTreeWalker(TreeWalker): + def getNodeDetails(self, node): + raise NotImplementedError + + def getFirstChild(self, node): + raise NotImplementedError + + def getNextSibling(self, node): + raise NotImplementedError + + def getParentNode(self, node): + raise NotImplementedError + + def __iter__(self): + currentNode = self.tree + while currentNode is not None: + details = self.getNodeDetails(currentNode) + type, details = details[0], details[1:] + hasChildren = False + + if type == DOCTYPE: + yield self.doctype(*details) + + elif type == TEXT: + for token in self.text(*details): + yield token + + elif type == ELEMENT: + namespace, name, attributes, hasChildren = details + if (not namespace or namespace == namespaces["html"]) and name in voidElements: + for token in self.emptyTag(namespace, name, attributes, + hasChildren): + yield token + hasChildren = False + else: + yield self.startTag(namespace, name, attributes) + + elif type == COMMENT: + yield self.comment(details[0]) + + elif type == ENTITY: + yield self.entity(details[0]) + + elif type == DOCUMENT: + hasChildren = True + + else: + yield self.unknown(details[0]) + + if hasChildren: + firstChild = self.getFirstChild(currentNode) + else: + firstChild = None + + if firstChild is not None: + currentNode = firstChild + else: + while currentNode is not None: + details = self.getNodeDetails(currentNode) + type, details = details[0], details[1:] + if type == ELEMENT: + namespace, name, attributes, hasChildren = details + if (namespace and namespace != namespaces["html"]) or name not in voidElements: + yield self.endTag(namespace, name) + if self.tree is currentNode: + currentNode = None + break + nextSibling = self.getNextSibling(currentNode) + if nextSibling is not None: + currentNode = nextSibling + break + else: + currentNode = self.getParentNode(currentNode) diff --git a/venv2/lib/python2.7/site-packages/pip/_vendor/html5lib/treewalkers/dom.py b/venv2/lib/python2.7/site-packages/pip/_vendor/html5lib/treewalkers/dom.py new file mode 100644 index 0000000..b0c89b0 --- /dev/null +++ b/venv2/lib/python2.7/site-packages/pip/_vendor/html5lib/treewalkers/dom.py @@ -0,0 +1,43 @@ +from __future__ import absolute_import, division, unicode_literals + +from xml.dom import Node + +from . import base + + +class TreeWalker(base.NonRecursiveTreeWalker): + def getNodeDetails(self, node): + if node.nodeType == Node.DOCUMENT_TYPE_NODE: + return base.DOCTYPE, node.name, node.publicId, node.systemId + + elif node.nodeType in (Node.TEXT_NODE, Node.CDATA_SECTION_NODE): + return base.TEXT, node.nodeValue + + elif node.nodeType == Node.ELEMENT_NODE: + attrs = {} + for attr in list(node.attributes.keys()): + attr = node.getAttributeNode(attr) + if attr.namespaceURI: + attrs[(attr.namespaceURI, attr.localName)] = attr.value + else: + attrs[(None, attr.name)] = attr.value + return (base.ELEMENT, node.namespaceURI, node.nodeName, + attrs, node.hasChildNodes()) + + elif node.nodeType == Node.COMMENT_NODE: + return base.COMMENT, node.nodeValue + + elif node.nodeType in (Node.DOCUMENT_NODE, Node.DOCUMENT_FRAGMENT_NODE): + return (base.DOCUMENT,) + + else: + return base.UNKNOWN, node.nodeType + + def getFirstChild(self, node): + return node.firstChild + + def getNextSibling(self, node): + return node.nextSibling + + def getParentNode(self, node): + return node.parentNode diff --git a/venv2/lib/python2.7/site-packages/pip/_vendor/html5lib/treewalkers/etree.py b/venv2/lib/python2.7/site-packages/pip/_vendor/html5lib/treewalkers/etree.py new file mode 100644 index 0000000..95fc0c1 --- /dev/null +++ b/venv2/lib/python2.7/site-packages/pip/_vendor/html5lib/treewalkers/etree.py @@ -0,0 +1,130 @@ +from __future__ import absolute_import, division, unicode_literals + +from collections import OrderedDict +import re + +from pip._vendor.six import string_types + +from . import base +from .._utils import moduleFactoryFactory + +tag_regexp = re.compile("{([^}]*)}(.*)") + + +def getETreeBuilder(ElementTreeImplementation): + ElementTree = ElementTreeImplementation + ElementTreeCommentType = ElementTree.Comment("asd").tag + + class TreeWalker(base.NonRecursiveTreeWalker): # pylint:disable=unused-variable + """Given the particular ElementTree representation, this implementation, + to avoid using recursion, returns "nodes" as tuples with the following + content: + + 1. The current element + + 2. The index of the element relative to its parent + + 3. A stack of ancestor elements + + 4. A flag "text", "tail" or None to indicate if the current node is a + text node; either the text or tail of the current element (1) + """ + def getNodeDetails(self, node): + if isinstance(node, tuple): # It might be the root Element + elt, _, _, flag = node + if flag in ("text", "tail"): + return base.TEXT, getattr(elt, flag) + else: + node = elt + + if not(hasattr(node, "tag")): + node = node.getroot() + + if node.tag in ("DOCUMENT_ROOT", "DOCUMENT_FRAGMENT"): + return (base.DOCUMENT,) + + elif node.tag == "<!DOCTYPE>": + return (base.DOCTYPE, node.text, + node.get("publicId"), node.get("systemId")) + + elif node.tag == ElementTreeCommentType: + return base.COMMENT, node.text + + else: + assert isinstance(node.tag, string_types), type(node.tag) + # This is assumed to be an ordinary element + match = tag_regexp.match(node.tag) + if match: + namespace, tag = match.groups() + else: + namespace = None + tag = node.tag + attrs = OrderedDict() + for name, value in list(node.attrib.items()): + match = tag_regexp.match(name) + if match: + attrs[(match.group(1), match.group(2))] = value + else: + attrs[(None, name)] = value + return (base.ELEMENT, namespace, tag, + attrs, len(node) or node.text) + + def getFirstChild(self, node): + if isinstance(node, tuple): + element, key, parents, flag = node + else: + element, key, parents, flag = node, None, [], None + + if flag in ("text", "tail"): + return None + else: + if element.text: + return element, key, parents, "text" + elif len(element): + parents.append(element) + return element[0], 0, parents, None + else: + return None + + def getNextSibling(self, node): + if isinstance(node, tuple): + element, key, parents, flag = node + else: + return None + + if flag == "text": + if len(element): + parents.append(element) + return element[0], 0, parents, None + else: + return None + else: + if element.tail and flag != "tail": + return element, key, parents, "tail" + elif key < len(parents[-1]) - 1: + return parents[-1][key + 1], key + 1, parents, None + else: + return None + + def getParentNode(self, node): + if isinstance(node, tuple): + element, key, parents, flag = node + else: + return None + + if flag == "text": + if not parents: + return element + else: + return element, key, parents, None + else: + parent = parents.pop() + if not parents: + return parent + else: + assert list(parents[-1]).count(parent) == 1 + return parent, list(parents[-1]).index(parent), parents, None + + return locals() + +getETreeModule = moduleFactoryFactory(getETreeBuilder) diff --git a/venv2/lib/python2.7/site-packages/pip/_vendor/html5lib/treewalkers/etree_lxml.py b/venv2/lib/python2.7/site-packages/pip/_vendor/html5lib/treewalkers/etree_lxml.py new file mode 100644 index 0000000..e81ddf3 --- /dev/null +++ b/venv2/lib/python2.7/site-packages/pip/_vendor/html5lib/treewalkers/etree_lxml.py @@ -0,0 +1,213 @@ +from __future__ import absolute_import, division, unicode_literals +from pip._vendor.six import text_type + +from lxml import etree +from ..treebuilders.etree import tag_regexp + +from . import base + +from .. import _ihatexml + + +def ensure_str(s): + if s is None: + return None + elif isinstance(s, text_type): + return s + else: + return s.decode("ascii", "strict") + + +class Root(object): + def __init__(self, et): + self.elementtree = et + self.children = [] + + try: + if et.docinfo.internalDTD: + self.children.append(Doctype(self, + ensure_str(et.docinfo.root_name), + ensure_str(et.docinfo.public_id), + ensure_str(et.docinfo.system_url))) + except AttributeError: + pass + + try: + node = et.getroot() + except AttributeError: + node = et + + while node.getprevious() is not None: + node = node.getprevious() + while node is not None: + self.children.append(node) + node = node.getnext() + + self.text = None + self.tail = None + + def __getitem__(self, key): + return self.children[key] + + def getnext(self): + return None + + def __len__(self): + return 1 + + +class Doctype(object): + def __init__(self, root_node, name, public_id, system_id): + self.root_node = root_node + self.name = name + self.public_id = public_id + self.system_id = system_id + + self.text = None + self.tail = None + + def getnext(self): + return self.root_node.children[1] + + +class FragmentRoot(Root): + def __init__(self, children): + self.children = [FragmentWrapper(self, child) for child in children] + self.text = self.tail = None + + def getnext(self): + return None + + +class FragmentWrapper(object): + def __init__(self, fragment_root, obj): + self.root_node = fragment_root + self.obj = obj + if hasattr(self.obj, 'text'): + self.text = ensure_str(self.obj.text) + else: + self.text = None + if hasattr(self.obj, 'tail'): + self.tail = ensure_str(self.obj.tail) + else: + self.tail = None + + def __getattr__(self, name): + return getattr(self.obj, name) + + def getnext(self): + siblings = self.root_node.children + idx = siblings.index(self) + if idx < len(siblings) - 1: + return siblings[idx + 1] + else: + return None + + def __getitem__(self, key): + return self.obj[key] + + def __bool__(self): + return bool(self.obj) + + def getparent(self): + return None + + def __str__(self): + return str(self.obj) + + def __unicode__(self): + return str(self.obj) + + def __len__(self): + return len(self.obj) + + +class TreeWalker(base.NonRecursiveTreeWalker): + def __init__(self, tree): + # pylint:disable=redefined-variable-type + if isinstance(tree, list): + self.fragmentChildren = set(tree) + tree = FragmentRoot(tree) + else: + self.fragmentChildren = set() + tree = Root(tree) + base.NonRecursiveTreeWalker.__init__(self, tree) + self.filter = _ihatexml.InfosetFilter() + + def getNodeDetails(self, node): + if isinstance(node, tuple): # Text node + node, key = node + assert key in ("text", "tail"), "Text nodes are text or tail, found %s" % key + return base.TEXT, ensure_str(getattr(node, key)) + + elif isinstance(node, Root): + return (base.DOCUMENT,) + + elif isinstance(node, Doctype): + return base.DOCTYPE, node.name, node.public_id, node.system_id + + elif isinstance(node, FragmentWrapper) and not hasattr(node, "tag"): + return base.TEXT, ensure_str(node.obj) + + elif node.tag == etree.Comment: + return base.COMMENT, ensure_str(node.text) + + elif node.tag == etree.Entity: + return base.ENTITY, ensure_str(node.text)[1:-1] # strip &; + + else: + # This is assumed to be an ordinary element + match = tag_regexp.match(ensure_str(node.tag)) + if match: + namespace, tag = match.groups() + else: + namespace = None + tag = ensure_str(node.tag) + attrs = {} + for name, value in list(node.attrib.items()): + name = ensure_str(name) + value = ensure_str(value) + match = tag_regexp.match(name) + if match: + attrs[(match.group(1), match.group(2))] = value + else: + attrs[(None, name)] = value + return (base.ELEMENT, namespace, self.filter.fromXmlName(tag), + attrs, len(node) > 0 or node.text) + + def getFirstChild(self, node): + assert not isinstance(node, tuple), "Text nodes have no children" + + assert len(node) or node.text, "Node has no children" + if node.text: + return (node, "text") + else: + return node[0] + + def getNextSibling(self, node): + if isinstance(node, tuple): # Text node + node, key = node + assert key in ("text", "tail"), "Text nodes are text or tail, found %s" % key + if key == "text": + # XXX: we cannot use a "bool(node) and node[0] or None" construct here + # because node[0] might evaluate to False if it has no child element + if len(node): + return node[0] + else: + return None + else: # tail + return node.getnext() + + return (node, "tail") if node.tail else node.getnext() + + def getParentNode(self, node): + if isinstance(node, tuple): # Text node + node, key = node + assert key in ("text", "tail"), "Text nodes are text or tail, found %s" % key + if key == "text": + return node + # else: fallback to "normal" processing + elif node in self.fragmentChildren: + return None + + return node.getparent() diff --git a/venv2/lib/python2.7/site-packages/pip/_vendor/html5lib/treewalkers/genshi.py b/venv2/lib/python2.7/site-packages/pip/_vendor/html5lib/treewalkers/genshi.py new file mode 100644 index 0000000..7483be2 --- /dev/null +++ b/venv2/lib/python2.7/site-packages/pip/_vendor/html5lib/treewalkers/genshi.py @@ -0,0 +1,69 @@ +from __future__ import absolute_import, division, unicode_literals + +from genshi.core import QName +from genshi.core import START, END, XML_NAMESPACE, DOCTYPE, TEXT +from genshi.core import START_NS, END_NS, START_CDATA, END_CDATA, PI, COMMENT + +from . import base + +from ..constants import voidElements, namespaces + + +class TreeWalker(base.TreeWalker): + def __iter__(self): + # Buffer the events so we can pass in the following one + previous = None + for event in self.tree: + if previous is not None: + for token in self.tokens(previous, event): + yield token + previous = event + + # Don't forget the final event! + if previous is not None: + for token in self.tokens(previous, None): + yield token + + def tokens(self, event, next): + kind, data, _ = event + if kind == START: + tag, attribs = data + name = tag.localname + namespace = tag.namespace + converted_attribs = {} + for k, v in attribs: + if isinstance(k, QName): + converted_attribs[(k.namespace, k.localname)] = v + else: + converted_attribs[(None, k)] = v + + if namespace == namespaces["html"] and name in voidElements: + for token in self.emptyTag(namespace, name, converted_attribs, + not next or next[0] != END or + next[1] != tag): + yield token + else: + yield self.startTag(namespace, name, converted_attribs) + + elif kind == END: + name = data.localname + namespace = data.namespace + if namespace != namespaces["html"] or name not in voidElements: + yield self.endTag(namespace, name) + + elif kind == COMMENT: + yield self.comment(data) + + elif kind == TEXT: + for token in self.text(data): + yield token + + elif kind == DOCTYPE: + yield self.doctype(*data) + + elif kind in (XML_NAMESPACE, DOCTYPE, START_NS, END_NS, + START_CDATA, END_CDATA, PI): + pass + + else: + yield self.unknown(kind) diff --git a/venv2/lib/python2.7/site-packages/pip/_vendor/idna/__init__.py b/venv2/lib/python2.7/site-packages/pip/_vendor/idna/__init__.py new file mode 100644 index 0000000..847bf93 --- /dev/null +++ b/venv2/lib/python2.7/site-packages/pip/_vendor/idna/__init__.py @@ -0,0 +1,2 @@ +from .package_data import __version__ +from .core import * diff --git a/venv2/lib/python2.7/site-packages/pip/_vendor/idna/codec.py b/venv2/lib/python2.7/site-packages/pip/_vendor/idna/codec.py new file mode 100644 index 0000000..98c65ea --- /dev/null +++ b/venv2/lib/python2.7/site-packages/pip/_vendor/idna/codec.py @@ -0,0 +1,118 @@ +from .core import encode, decode, alabel, ulabel, IDNAError +import codecs +import re + +_unicode_dots_re = re.compile(u'[\u002e\u3002\uff0e\uff61]') + +class Codec(codecs.Codec): + + def encode(self, data, errors='strict'): + + if errors != 'strict': + raise IDNAError("Unsupported error handling \"{0}\"".format(errors)) + + if not data: + return "", 0 + + return encode(data), len(data) + + def decode(self, data, errors='strict'): + + if errors != 'strict': + raise IDNAError("Unsupported error handling \"{0}\"".format(errors)) + + if not data: + return u"", 0 + + return decode(data), len(data) + +class IncrementalEncoder(codecs.BufferedIncrementalEncoder): + def _buffer_encode(self, data, errors, final): + if errors != 'strict': + raise IDNAError("Unsupported error handling \"{0}\"".format(errors)) + + if not data: + return ("", 0) + + labels = _unicode_dots_re.split(data) + trailing_dot = u'' + if labels: + if not labels[-1]: + trailing_dot = '.' + del labels[-1] + elif not final: + # Keep potentially unfinished label until the next call + del labels[-1] + if labels: + trailing_dot = '.' + + result = [] + size = 0 + for label in labels: + result.append(alabel(label)) + if size: + size += 1 + size += len(label) + + # Join with U+002E + result = ".".join(result) + trailing_dot + size += len(trailing_dot) + return (result, size) + +class IncrementalDecoder(codecs.BufferedIncrementalDecoder): + def _buffer_decode(self, data, errors, final): + if errors != 'strict': + raise IDNAError("Unsupported error handling \"{0}\"".format(errors)) + + if not data: + return (u"", 0) + + # IDNA allows decoding to operate on Unicode strings, too. + if isinstance(data, unicode): + labels = _unicode_dots_re.split(data) + else: + # Must be ASCII string + data = str(data) + unicode(data, "ascii") + labels = data.split(".") + + trailing_dot = u'' + if labels: + if not labels[-1]: + trailing_dot = u'.' + del labels[-1] + elif not final: + # Keep potentially unfinished label until the next call + del labels[-1] + if labels: + trailing_dot = u'.' + + result = [] + size = 0 + for label in labels: + result.append(ulabel(label)) + if size: + size += 1 + size += len(label) + + result = u".".join(result) + trailing_dot + size += len(trailing_dot) + return (result, size) + + +class StreamWriter(Codec, codecs.StreamWriter): + pass + +class StreamReader(Codec, codecs.StreamReader): + pass + +def getregentry(): + return codecs.CodecInfo( + name='idna', + encode=Codec().encode, + decode=Codec().decode, + incrementalencoder=IncrementalEncoder, + incrementaldecoder=IncrementalDecoder, + streamwriter=StreamWriter, + streamreader=StreamReader, + ) diff --git a/venv2/lib/python2.7/site-packages/pip/_vendor/idna/compat.py b/venv2/lib/python2.7/site-packages/pip/_vendor/idna/compat.py new file mode 100644 index 0000000..4d47f33 --- /dev/null +++ b/venv2/lib/python2.7/site-packages/pip/_vendor/idna/compat.py @@ -0,0 +1,12 @@ +from .core import * +from .codec import * + +def ToASCII(label): + return encode(label) + +def ToUnicode(label): + return decode(label) + +def nameprep(s): + raise NotImplementedError("IDNA 2008 does not utilise nameprep protocol") + diff --git a/venv2/lib/python2.7/site-packages/pip/_vendor/idna/core.py b/venv2/lib/python2.7/site-packages/pip/_vendor/idna/core.py new file mode 100644 index 0000000..104624a --- /dev/null +++ b/venv2/lib/python2.7/site-packages/pip/_vendor/idna/core.py @@ -0,0 +1,396 @@ +from . import idnadata +import bisect +import unicodedata +import re +import sys +from .intranges import intranges_contain + +_virama_combining_class = 9 +_alabel_prefix = b'xn--' +_unicode_dots_re = re.compile(u'[\u002e\u3002\uff0e\uff61]') + +if sys.version_info[0] == 3: + unicode = str + unichr = chr + +class IDNAError(UnicodeError): + """ Base exception for all IDNA-encoding related problems """ + pass + + +class IDNABidiError(IDNAError): + """ Exception when bidirectional requirements are not satisfied """ + pass + + +class InvalidCodepoint(IDNAError): + """ Exception when a disallowed or unallocated codepoint is used """ + pass + + +class InvalidCodepointContext(IDNAError): + """ Exception when the codepoint is not valid in the context it is used """ + pass + + +def _combining_class(cp): + v = unicodedata.combining(unichr(cp)) + if v == 0: + if not unicodedata.name(unichr(cp)): + raise ValueError("Unknown character in unicodedata") + return v + +def _is_script(cp, script): + return intranges_contain(ord(cp), idnadata.scripts[script]) + +def _punycode(s): + return s.encode('punycode') + +def _unot(s): + return 'U+{0:04X}'.format(s) + + +def valid_label_length(label): + + if len(label) > 63: + return False + return True + + +def valid_string_length(label, trailing_dot): + + if len(label) > (254 if trailing_dot else 253): + return False + return True + + +def check_bidi(label, check_ltr=False): + + # Bidi rules should only be applied if string contains RTL characters + bidi_label = False + for (idx, cp) in enumerate(label, 1): + direction = unicodedata.bidirectional(cp) + if direction == '': + # String likely comes from a newer version of Unicode + raise IDNABidiError('Unknown directionality in label {0} at position {1}'.format(repr(label), idx)) + if direction in ['R', 'AL', 'AN']: + bidi_label = True + if not bidi_label and not check_ltr: + return True + + # Bidi rule 1 + direction = unicodedata.bidirectional(label[0]) + if direction in ['R', 'AL']: + rtl = True + elif direction == 'L': + rtl = False + else: + raise IDNABidiError('First codepoint in label {0} must be directionality L, R or AL'.format(repr(label))) + + valid_ending = False + number_type = False + for (idx, cp) in enumerate(label, 1): + direction = unicodedata.bidirectional(cp) + + if rtl: + # Bidi rule 2 + if not direction in ['R', 'AL', 'AN', 'EN', 'ES', 'CS', 'ET', 'ON', 'BN', 'NSM']: + raise IDNABidiError('Invalid direction for codepoint at position {0} in a right-to-left label'.format(idx)) + # Bidi rule 3 + if direction in ['R', 'AL', 'EN', 'AN']: + valid_ending = True + elif direction != 'NSM': + valid_ending = False + # Bidi rule 4 + if direction in ['AN', 'EN']: + if not number_type: + number_type = direction + else: + if number_type != direction: + raise IDNABidiError('Can not mix numeral types in a right-to-left label') + else: + # Bidi rule 5 + if not direction in ['L', 'EN', 'ES', 'CS', 'ET', 'ON', 'BN', 'NSM']: + raise IDNABidiError('Invalid direction for codepoint at position {0} in a left-to-right label'.format(idx)) + # Bidi rule 6 + if direction in ['L', 'EN']: + valid_ending = True + elif direction != 'NSM': + valid_ending = False + + if not valid_ending: + raise IDNABidiError('Label ends with illegal codepoint directionality') + + return True + + +def check_initial_combiner(label): + + if unicodedata.category(label[0])[0] == 'M': + raise IDNAError('Label begins with an illegal combining character') + return True + + +def check_hyphen_ok(label): + + if label[2:4] == '--': + raise IDNAError('Label has disallowed hyphens in 3rd and 4th position') + if label[0] == '-' or label[-1] == '-': + raise IDNAError('Label must not start or end with a hyphen') + return True + + +def check_nfc(label): + + if unicodedata.normalize('NFC', label) != label: + raise IDNAError('Label must be in Normalization Form C') + + +def valid_contextj(label, pos): + + cp_value = ord(label[pos]) + + if cp_value == 0x200c: + + if pos > 0: + if _combining_class(ord(label[pos - 1])) == _virama_combining_class: + return True + + ok = False + for i in range(pos-1, -1, -1): + joining_type = idnadata.joining_types.get(ord(label[i])) + if joining_type == ord('T'): + continue + if joining_type in [ord('L'), ord('D')]: + ok = True + break + + if not ok: + return False + + ok = False + for i in range(pos+1, len(label)): + joining_type = idnadata.joining_types.get(ord(label[i])) + if joining_type == ord('T'): + continue + if joining_type in [ord('R'), ord('D')]: + ok = True + break + return ok + + if cp_value == 0x200d: + + if pos > 0: + if _combining_class(ord(label[pos - 1])) == _virama_combining_class: + return True + return False + + else: + + return False + + +def valid_contexto(label, pos, exception=False): + + cp_value = ord(label[pos]) + + if cp_value == 0x00b7: + if 0 < pos < len(label)-1: + if ord(label[pos - 1]) == 0x006c and ord(label[pos + 1]) == 0x006c: + return True + return False + + elif cp_value == 0x0375: + if pos < len(label)-1 and len(label) > 1: + return _is_script(label[pos + 1], 'Greek') + return False + + elif cp_value == 0x05f3 or cp_value == 0x05f4: + if pos > 0: + return _is_script(label[pos - 1], 'Hebrew') + return False + + elif cp_value == 0x30fb: + for cp in label: + if cp == u'\u30fb': + continue + if _is_script(cp, 'Hiragana') or _is_script(cp, 'Katakana') or _is_script(cp, 'Han'): + return True + return False + + elif 0x660 <= cp_value <= 0x669: + for cp in label: + if 0x6f0 <= ord(cp) <= 0x06f9: + return False + return True + + elif 0x6f0 <= cp_value <= 0x6f9: + for cp in label: + if 0x660 <= ord(cp) <= 0x0669: + return False + return True + + +def check_label(label): + + if isinstance(label, (bytes, bytearray)): + label = label.decode('utf-8') + if len(label) == 0: + raise IDNAError('Empty Label') + + check_nfc(label) + check_hyphen_ok(label) + check_initial_combiner(label) + + for (pos, cp) in enumerate(label): + cp_value = ord(cp) + if intranges_contain(cp_value, idnadata.codepoint_classes['PVALID']): + continue + elif intranges_contain(cp_value, idnadata.codepoint_classes['CONTEXTJ']): + try: + if not valid_contextj(label, pos): + raise InvalidCodepointContext('Joiner {0} not allowed at position {1} in {2}'.format( + _unot(cp_value), pos+1, repr(label))) + except ValueError: + raise IDNAError('Unknown codepoint adjacent to joiner {0} at position {1} in {2}'.format( + _unot(cp_value), pos+1, repr(label))) + elif intranges_contain(cp_value, idnadata.codepoint_classes['CONTEXTO']): + if not valid_contexto(label, pos): + raise InvalidCodepointContext('Codepoint {0} not allowed at position {1} in {2}'.format(_unot(cp_value), pos+1, repr(label))) + else: + raise InvalidCodepoint('Codepoint {0} at position {1} of {2} not allowed'.format(_unot(cp_value), pos+1, repr(label))) + + check_bidi(label) + + +def alabel(label): + + try: + label = label.encode('ascii') + ulabel(label) + if not valid_label_length(label): + raise IDNAError('Label too long') + return label + except UnicodeEncodeError: + pass + + if not label: + raise IDNAError('No Input') + + label = unicode(label) + check_label(label) + label = _punycode(label) + label = _alabel_prefix + label + + if not valid_label_length(label): + raise IDNAError('Label too long') + + return label + + +def ulabel(label): + + if not isinstance(label, (bytes, bytearray)): + try: + label = label.encode('ascii') + except UnicodeEncodeError: + check_label(label) + return label + + label = label.lower() + if label.startswith(_alabel_prefix): + label = label[len(_alabel_prefix):] + else: + check_label(label) + return label.decode('ascii') + + label = label.decode('punycode') + check_label(label) + return label + + +def uts46_remap(domain, std3_rules=True, transitional=False): + """Re-map the characters in the string according to UTS46 processing.""" + from .uts46data import uts46data + output = u"" + try: + for pos, char in enumerate(domain): + code_point = ord(char) + uts46row = uts46data[code_point if code_point < 256 else + bisect.bisect_left(uts46data, (code_point, "Z")) - 1] + status = uts46row[1] + replacement = uts46row[2] if len(uts46row) == 3 else None + if (status == "V" or + (status == "D" and not transitional) or + (status == "3" and not std3_rules and replacement is None)): + output += char + elif replacement is not None and (status == "M" or + (status == "3" and not std3_rules) or + (status == "D" and transitional)): + output += replacement + elif status != "I": + raise IndexError() + return unicodedata.normalize("NFC", output) + except IndexError: + raise InvalidCodepoint( + "Codepoint {0} not allowed at position {1} in {2}".format( + _unot(code_point), pos + 1, repr(domain))) + + +def encode(s, strict=False, uts46=False, std3_rules=False, transitional=False): + + if isinstance(s, (bytes, bytearray)): + s = s.decode("ascii") + if uts46: + s = uts46_remap(s, std3_rules, transitional) + trailing_dot = False + result = [] + if strict: + labels = s.split('.') + else: + labels = _unicode_dots_re.split(s) + if not labels or labels == ['']: + raise IDNAError('Empty domain') + if labels[-1] == '': + del labels[-1] + trailing_dot = True + for label in labels: + s = alabel(label) + if s: + result.append(s) + else: + raise IDNAError('Empty label') + if trailing_dot: + result.append(b'') + s = b'.'.join(result) + if not valid_string_length(s, trailing_dot): + raise IDNAError('Domain too long') + return s + + +def decode(s, strict=False, uts46=False, std3_rules=False): + + if isinstance(s, (bytes, bytearray)): + s = s.decode("ascii") + if uts46: + s = uts46_remap(s, std3_rules, False) + trailing_dot = False + result = [] + if not strict: + labels = _unicode_dots_re.split(s) + else: + labels = s.split(u'.') + if not labels or labels == ['']: + raise IDNAError('Empty domain') + if not labels[-1]: + del labels[-1] + trailing_dot = True + for label in labels: + s = ulabel(label) + if s: + result.append(s) + else: + raise IDNAError('Empty label') + if trailing_dot: + result.append(u'') + return u'.'.join(result) diff --git a/venv2/lib/python2.7/site-packages/pip/_vendor/idna/idnadata.py b/venv2/lib/python2.7/site-packages/pip/_vendor/idna/idnadata.py new file mode 100644 index 0000000..a80c959 --- /dev/null +++ b/venv2/lib/python2.7/site-packages/pip/_vendor/idna/idnadata.py @@ -0,0 +1,1979 @@ +# This file is automatically generated by tools/idna-data + +__version__ = "11.0.0" +scripts = { + 'Greek': ( + 0x37000000374, + 0x37500000378, + 0x37a0000037e, + 0x37f00000380, + 0x38400000385, + 0x38600000387, + 0x3880000038b, + 0x38c0000038d, + 0x38e000003a2, + 0x3a3000003e2, + 0x3f000000400, + 0x1d2600001d2b, + 0x1d5d00001d62, + 0x1d6600001d6b, + 0x1dbf00001dc0, + 0x1f0000001f16, + 0x1f1800001f1e, + 0x1f2000001f46, + 0x1f4800001f4e, + 0x1f5000001f58, + 0x1f5900001f5a, + 0x1f5b00001f5c, + 0x1f5d00001f5e, + 0x1f5f00001f7e, + 0x1f8000001fb5, + 0x1fb600001fc5, + 0x1fc600001fd4, + 0x1fd600001fdc, + 0x1fdd00001ff0, + 0x1ff200001ff5, + 0x1ff600001fff, + 0x212600002127, + 0xab650000ab66, + 0x101400001018f, + 0x101a0000101a1, + 0x1d2000001d246, + ), + 'Han': ( + 0x2e8000002e9a, + 0x2e9b00002ef4, + 0x2f0000002fd6, + 0x300500003006, + 0x300700003008, + 0x30210000302a, + 0x30380000303c, + 0x340000004db6, + 0x4e0000009ff0, + 0xf9000000fa6e, + 0xfa700000fada, + 0x200000002a6d7, + 0x2a7000002b735, + 0x2b7400002b81e, + 0x2b8200002cea2, + 0x2ceb00002ebe1, + 0x2f8000002fa1e, + ), + 'Hebrew': ( + 0x591000005c8, + 0x5d0000005eb, + 0x5ef000005f5, + 0xfb1d0000fb37, + 0xfb380000fb3d, + 0xfb3e0000fb3f, + 0xfb400000fb42, + 0xfb430000fb45, + 0xfb460000fb50, + ), + 'Hiragana': ( + 0x304100003097, + 0x309d000030a0, + 0x1b0010001b11f, + 0x1f2000001f201, + ), + 'Katakana': ( + 0x30a1000030fb, + 0x30fd00003100, + 0x31f000003200, + 0x32d0000032ff, + 0x330000003358, + 0xff660000ff70, + 0xff710000ff9e, + 0x1b0000001b001, + ), +} +joining_types = { + 0x600: 85, + 0x601: 85, + 0x602: 85, + 0x603: 85, + 0x604: 85, + 0x605: 85, + 0x608: 85, + 0x60b: 85, + 0x620: 68, + 0x621: 85, + 0x622: 82, + 0x623: 82, + 0x624: 82, + 0x625: 82, + 0x626: 68, + 0x627: 82, + 0x628: 68, + 0x629: 82, + 0x62a: 68, + 0x62b: 68, + 0x62c: 68, + 0x62d: 68, + 0x62e: 68, + 0x62f: 82, + 0x630: 82, + 0x631: 82, + 0x632: 82, + 0x633: 68, + 0x634: 68, + 0x635: 68, + 0x636: 68, + 0x637: 68, + 0x638: 68, + 0x639: 68, + 0x63a: 68, + 0x63b: 68, + 0x63c: 68, + 0x63d: 68, + 0x63e: 68, + 0x63f: 68, + 0x640: 67, + 0x641: 68, + 0x642: 68, + 0x643: 68, + 0x644: 68, + 0x645: 68, + 0x646: 68, + 0x647: 68, + 0x648: 82, + 0x649: 68, + 0x64a: 68, + 0x66e: 68, + 0x66f: 68, + 0x671: 82, + 0x672: 82, + 0x673: 82, + 0x674: 85, + 0x675: 82, + 0x676: 82, + 0x677: 82, + 0x678: 68, + 0x679: 68, + 0x67a: 68, + 0x67b: 68, + 0x67c: 68, + 0x67d: 68, + 0x67e: 68, + 0x67f: 68, + 0x680: 68, + 0x681: 68, + 0x682: 68, + 0x683: 68, + 0x684: 68, + 0x685: 68, + 0x686: 68, + 0x687: 68, + 0x688: 82, + 0x689: 82, + 0x68a: 82, + 0x68b: 82, + 0x68c: 82, + 0x68d: 82, + 0x68e: 82, + 0x68f: 82, + 0x690: 82, + 0x691: 82, + 0x692: 82, + 0x693: 82, + 0x694: 82, + 0x695: 82, + 0x696: 82, + 0x697: 82, + 0x698: 82, + 0x699: 82, + 0x69a: 68, + 0x69b: 68, + 0x69c: 68, + 0x69d: 68, + 0x69e: 68, + 0x69f: 68, + 0x6a0: 68, + 0x6a1: 68, + 0x6a2: 68, + 0x6a3: 68, + 0x6a4: 68, + 0x6a5: 68, + 0x6a6: 68, + 0x6a7: 68, + 0x6a8: 68, + 0x6a9: 68, + 0x6aa: 68, + 0x6ab: 68, + 0x6ac: 68, + 0x6ad: 68, + 0x6ae: 68, + 0x6af: 68, + 0x6b0: 68, + 0x6b1: 68, + 0x6b2: 68, + 0x6b3: 68, + 0x6b4: 68, + 0x6b5: 68, + 0x6b6: 68, + 0x6b7: 68, + 0x6b8: 68, + 0x6b9: 68, + 0x6ba: 68, + 0x6bb: 68, + 0x6bc: 68, + 0x6bd: 68, + 0x6be: 68, + 0x6bf: 68, + 0x6c0: 82, + 0x6c1: 68, + 0x6c2: 68, + 0x6c3: 82, + 0x6c4: 82, + 0x6c5: 82, + 0x6c6: 82, + 0x6c7: 82, + 0x6c8: 82, + 0x6c9: 82, + 0x6ca: 82, + 0x6cb: 82, + 0x6cc: 68, + 0x6cd: 82, + 0x6ce: 68, + 0x6cf: 82, + 0x6d0: 68, + 0x6d1: 68, + 0x6d2: 82, + 0x6d3: 82, + 0x6d5: 82, + 0x6dd: 85, + 0x6ee: 82, + 0x6ef: 82, + 0x6fa: 68, + 0x6fb: 68, + 0x6fc: 68, + 0x6ff: 68, + 0x70f: 84, + 0x710: 82, + 0x712: 68, + 0x713: 68, + 0x714: 68, + 0x715: 82, + 0x716: 82, + 0x717: 82, + 0x718: 82, + 0x719: 82, + 0x71a: 68, + 0x71b: 68, + 0x71c: 68, + 0x71d: 68, + 0x71e: 82, + 0x71f: 68, + 0x720: 68, + 0x721: 68, + 0x722: 68, + 0x723: 68, + 0x724: 68, + 0x725: 68, + 0x726: 68, + 0x727: 68, + 0x728: 82, + 0x729: 68, + 0x72a: 82, + 0x72b: 68, + 0x72c: 82, + 0x72d: 68, + 0x72e: 68, + 0x72f: 82, + 0x74d: 82, + 0x74e: 68, + 0x74f: 68, + 0x750: 68, + 0x751: 68, + 0x752: 68, + 0x753: 68, + 0x754: 68, + 0x755: 68, + 0x756: 68, + 0x757: 68, + 0x758: 68, + 0x759: 82, + 0x75a: 82, + 0x75b: 82, + 0x75c: 68, + 0x75d: 68, + 0x75e: 68, + 0x75f: 68, + 0x760: 68, + 0x761: 68, + 0x762: 68, + 0x763: 68, + 0x764: 68, + 0x765: 68, + 0x766: 68, + 0x767: 68, + 0x768: 68, + 0x769: 68, + 0x76a: 68, + 0x76b: 82, + 0x76c: 82, + 0x76d: 68, + 0x76e: 68, + 0x76f: 68, + 0x770: 68, + 0x771: 82, + 0x772: 68, + 0x773: 82, + 0x774: 82, + 0x775: 68, + 0x776: 68, + 0x777: 68, + 0x778: 82, + 0x779: 82, + 0x77a: 68, + 0x77b: 68, + 0x77c: 68, + 0x77d: 68, + 0x77e: 68, + 0x77f: 68, + 0x7ca: 68, + 0x7cb: 68, + 0x7cc: 68, + 0x7cd: 68, + 0x7ce: 68, + 0x7cf: 68, + 0x7d0: 68, + 0x7d1: 68, + 0x7d2: 68, + 0x7d3: 68, + 0x7d4: 68, + 0x7d5: 68, + 0x7d6: 68, + 0x7d7: 68, + 0x7d8: 68, + 0x7d9: 68, + 0x7da: 68, + 0x7db: 68, + 0x7dc: 68, + 0x7dd: 68, + 0x7de: 68, + 0x7df: 68, + 0x7e0: 68, + 0x7e1: 68, + 0x7e2: 68, + 0x7e3: 68, + 0x7e4: 68, + 0x7e5: 68, + 0x7e6: 68, + 0x7e7: 68, + 0x7e8: 68, + 0x7e9: 68, + 0x7ea: 68, + 0x7fa: 67, + 0x840: 82, + 0x841: 68, + 0x842: 68, + 0x843: 68, + 0x844: 68, + 0x845: 68, + 0x846: 82, + 0x847: 82, + 0x848: 68, + 0x849: 82, + 0x84a: 68, + 0x84b: 68, + 0x84c: 68, + 0x84d: 68, + 0x84e: 68, + 0x84f: 68, + 0x850: 68, + 0x851: 68, + 0x852: 68, + 0x853: 68, + 0x854: 82, + 0x855: 68, + 0x856: 85, + 0x857: 85, + 0x858: 85, + 0x860: 68, + 0x861: 85, + 0x862: 68, + 0x863: 68, + 0x864: 68, + 0x865: 68, + 0x866: 85, + 0x867: 82, + 0x868: 68, + 0x869: 82, + 0x86a: 82, + 0x8a0: 68, + 0x8a1: 68, + 0x8a2: 68, + 0x8a3: 68, + 0x8a4: 68, + 0x8a5: 68, + 0x8a6: 68, + 0x8a7: 68, + 0x8a8: 68, + 0x8a9: 68, + 0x8aa: 82, + 0x8ab: 82, + 0x8ac: 82, + 0x8ad: 85, + 0x8ae: 82, + 0x8af: 68, + 0x8b0: 68, + 0x8b1: 82, + 0x8b2: 82, + 0x8b3: 68, + 0x8b4: 68, + 0x8b6: 68, + 0x8b7: 68, + 0x8b8: 68, + 0x8b9: 82, + 0x8ba: 68, + 0x8bb: 68, + 0x8bc: 68, + 0x8bd: 68, + 0x8e2: 85, + 0x1806: 85, + 0x1807: 68, + 0x180a: 67, + 0x180e: 85, + 0x1820: 68, + 0x1821: 68, + 0x1822: 68, + 0x1823: 68, + 0x1824: 68, + 0x1825: 68, + 0x1826: 68, + 0x1827: 68, + 0x1828: 68, + 0x1829: 68, + 0x182a: 68, + 0x182b: 68, + 0x182c: 68, + 0x182d: 68, + 0x182e: 68, + 0x182f: 68, + 0x1830: 68, + 0x1831: 68, + 0x1832: 68, + 0x1833: 68, + 0x1834: 68, + 0x1835: 68, + 0x1836: 68, + 0x1837: 68, + 0x1838: 68, + 0x1839: 68, + 0x183a: 68, + 0x183b: 68, + 0x183c: 68, + 0x183d: 68, + 0x183e: 68, + 0x183f: 68, + 0x1840: 68, + 0x1841: 68, + 0x1842: 68, + 0x1843: 68, + 0x1844: 68, + 0x1845: 68, + 0x1846: 68, + 0x1847: 68, + 0x1848: 68, + 0x1849: 68, + 0x184a: 68, + 0x184b: 68, + 0x184c: 68, + 0x184d: 68, + 0x184e: 68, + 0x184f: 68, + 0x1850: 68, + 0x1851: 68, + 0x1852: 68, + 0x1853: 68, + 0x1854: 68, + 0x1855: 68, + 0x1856: 68, + 0x1857: 68, + 0x1858: 68, + 0x1859: 68, + 0x185a: 68, + 0x185b: 68, + 0x185c: 68, + 0x185d: 68, + 0x185e: 68, + 0x185f: 68, + 0x1860: 68, + 0x1861: 68, + 0x1862: 68, + 0x1863: 68, + 0x1864: 68, + 0x1865: 68, + 0x1866: 68, + 0x1867: 68, + 0x1868: 68, + 0x1869: 68, + 0x186a: 68, + 0x186b: 68, + 0x186c: 68, + 0x186d: 68, + 0x186e: 68, + 0x186f: 68, + 0x1870: 68, + 0x1871: 68, + 0x1872: 68, + 0x1873: 68, + 0x1874: 68, + 0x1875: 68, + 0x1876: 68, + 0x1877: 68, + 0x1878: 68, + 0x1880: 85, + 0x1881: 85, + 0x1882: 85, + 0x1883: 85, + 0x1884: 85, + 0x1885: 84, + 0x1886: 84, + 0x1887: 68, + 0x1888: 68, + 0x1889: 68, + 0x188a: 68, + 0x188b: 68, + 0x188c: 68, + 0x188d: 68, + 0x188e: 68, + 0x188f: 68, + 0x1890: 68, + 0x1891: 68, + 0x1892: 68, + 0x1893: 68, + 0x1894: 68, + 0x1895: 68, + 0x1896: 68, + 0x1897: 68, + 0x1898: 68, + 0x1899: 68, + 0x189a: 68, + 0x189b: 68, + 0x189c: 68, + 0x189d: 68, + 0x189e: 68, + 0x189f: 68, + 0x18a0: 68, + 0x18a1: 68, + 0x18a2: 68, + 0x18a3: 68, + 0x18a4: 68, + 0x18a5: 68, + 0x18a6: 68, + 0x18a7: 68, + 0x18a8: 68, + 0x18aa: 68, + 0x200c: 85, + 0x200d: 67, + 0x202f: 85, + 0x2066: 85, + 0x2067: 85, + 0x2068: 85, + 0x2069: 85, + 0xa840: 68, + 0xa841: 68, + 0xa842: 68, + 0xa843: 68, + 0xa844: 68, + 0xa845: 68, + 0xa846: 68, + 0xa847: 68, + 0xa848: 68, + 0xa849: 68, + 0xa84a: 68, + 0xa84b: 68, + 0xa84c: 68, + 0xa84d: 68, + 0xa84e: 68, + 0xa84f: 68, + 0xa850: 68, + 0xa851: 68, + 0xa852: 68, + 0xa853: 68, + 0xa854: 68, + 0xa855: 68, + 0xa856: 68, + 0xa857: 68, + 0xa858: 68, + 0xa859: 68, + 0xa85a: 68, + 0xa85b: 68, + 0xa85c: 68, + 0xa85d: 68, + 0xa85e: 68, + 0xa85f: 68, + 0xa860: 68, + 0xa861: 68, + 0xa862: 68, + 0xa863: 68, + 0xa864: 68, + 0xa865: 68, + 0xa866: 68, + 0xa867: 68, + 0xa868: 68, + 0xa869: 68, + 0xa86a: 68, + 0xa86b: 68, + 0xa86c: 68, + 0xa86d: 68, + 0xa86e: 68, + 0xa86f: 68, + 0xa870: 68, + 0xa871: 68, + 0xa872: 76, + 0xa873: 85, + 0x10ac0: 68, + 0x10ac1: 68, + 0x10ac2: 68, + 0x10ac3: 68, + 0x10ac4: 68, + 0x10ac5: 82, + 0x10ac6: 85, + 0x10ac7: 82, + 0x10ac8: 85, + 0x10ac9: 82, + 0x10aca: 82, + 0x10acb: 85, + 0x10acc: 85, + 0x10acd: 76, + 0x10ace: 82, + 0x10acf: 82, + 0x10ad0: 82, + 0x10ad1: 82, + 0x10ad2: 82, + 0x10ad3: 68, + 0x10ad4: 68, + 0x10ad5: 68, + 0x10ad6: 68, + 0x10ad7: 76, + 0x10ad8: 68, + 0x10ad9: 68, + 0x10ada: 68, + 0x10adb: 68, + 0x10adc: 68, + 0x10add: 82, + 0x10ade: 68, + 0x10adf: 68, + 0x10ae0: 68, + 0x10ae1: 82, + 0x10ae2: 85, + 0x10ae3: 85, + 0x10ae4: 82, + 0x10aeb: 68, + 0x10aec: 68, + 0x10aed: 68, + 0x10aee: 68, + 0x10aef: 82, + 0x10b80: 68, + 0x10b81: 82, + 0x10b82: 68, + 0x10b83: 82, + 0x10b84: 82, + 0x10b85: 82, + 0x10b86: 68, + 0x10b87: 68, + 0x10b88: 68, + 0x10b89: 82, + 0x10b8a: 68, + 0x10b8b: 68, + 0x10b8c: 82, + 0x10b8d: 68, + 0x10b8e: 82, + 0x10b8f: 82, + 0x10b90: 68, + 0x10b91: 82, + 0x10ba9: 82, + 0x10baa: 82, + 0x10bab: 82, + 0x10bac: 82, + 0x10bad: 68, + 0x10bae: 68, + 0x10baf: 85, + 0x10d00: 76, + 0x10d01: 68, + 0x10d02: 68, + 0x10d03: 68, + 0x10d04: 68, + 0x10d05: 68, + 0x10d06: 68, + 0x10d07: 68, + 0x10d08: 68, + 0x10d09: 68, + 0x10d0a: 68, + 0x10d0b: 68, + 0x10d0c: 68, + 0x10d0d: 68, + 0x10d0e: 68, + 0x10d0f: 68, + 0x10d10: 68, + 0x10d11: 68, + 0x10d12: 68, + 0x10d13: 68, + 0x10d14: 68, + 0x10d15: 68, + 0x10d16: 68, + 0x10d17: 68, + 0x10d18: 68, + 0x10d19: 68, + 0x10d1a: 68, + 0x10d1b: 68, + 0x10d1c: 68, + 0x10d1d: 68, + 0x10d1e: 68, + 0x10d1f: 68, + 0x10d20: 68, + 0x10d21: 68, + 0x10d22: 82, + 0x10d23: 68, + 0x10f30: 68, + 0x10f31: 68, + 0x10f32: 68, + 0x10f33: 82, + 0x10f34: 68, + 0x10f35: 68, + 0x10f36: 68, + 0x10f37: 68, + 0x10f38: 68, + 0x10f39: 68, + 0x10f3a: 68, + 0x10f3b: 68, + 0x10f3c: 68, + 0x10f3d: 68, + 0x10f3e: 68, + 0x10f3f: 68, + 0x10f40: 68, + 0x10f41: 68, + 0x10f42: 68, + 0x10f43: 68, + 0x10f44: 68, + 0x10f45: 85, + 0x10f51: 68, + 0x10f52: 68, + 0x10f53: 68, + 0x10f54: 82, + 0x110bd: 85, + 0x110cd: 85, + 0x1e900: 68, + 0x1e901: 68, + 0x1e902: 68, + 0x1e903: 68, + 0x1e904: 68, + 0x1e905: 68, + 0x1e906: 68, + 0x1e907: 68, + 0x1e908: 68, + 0x1e909: 68, + 0x1e90a: 68, + 0x1e90b: 68, + 0x1e90c: 68, + 0x1e90d: 68, + 0x1e90e: 68, + 0x1e90f: 68, + 0x1e910: 68, + 0x1e911: 68, + 0x1e912: 68, + 0x1e913: 68, + 0x1e914: 68, + 0x1e915: 68, + 0x1e916: 68, + 0x1e917: 68, + 0x1e918: 68, + 0x1e919: 68, + 0x1e91a: 68, + 0x1e91b: 68, + 0x1e91c: 68, + 0x1e91d: 68, + 0x1e91e: 68, + 0x1e91f: 68, + 0x1e920: 68, + 0x1e921: 68, + 0x1e922: 68, + 0x1e923: 68, + 0x1e924: 68, + 0x1e925: 68, + 0x1e926: 68, + 0x1e927: 68, + 0x1e928: 68, + 0x1e929: 68, + 0x1e92a: 68, + 0x1e92b: 68, + 0x1e92c: 68, + 0x1e92d: 68, + 0x1e92e: 68, + 0x1e92f: 68, + 0x1e930: 68, + 0x1e931: 68, + 0x1e932: 68, + 0x1e933: 68, + 0x1e934: 68, + 0x1e935: 68, + 0x1e936: 68, + 0x1e937: 68, + 0x1e938: 68, + 0x1e939: 68, + 0x1e93a: 68, + 0x1e93b: 68, + 0x1e93c: 68, + 0x1e93d: 68, + 0x1e93e: 68, + 0x1e93f: 68, + 0x1e940: 68, + 0x1e941: 68, + 0x1e942: 68, + 0x1e943: 68, +} +codepoint_classes = { + 'PVALID': ( + 0x2d0000002e, + 0x300000003a, + 0x610000007b, + 0xdf000000f7, + 0xf800000100, + 0x10100000102, + 0x10300000104, + 0x10500000106, + 0x10700000108, + 0x1090000010a, + 0x10b0000010c, + 0x10d0000010e, + 0x10f00000110, + 0x11100000112, + 0x11300000114, + 0x11500000116, + 0x11700000118, + 0x1190000011a, + 0x11b0000011c, + 0x11d0000011e, + 0x11f00000120, + 0x12100000122, + 0x12300000124, + 0x12500000126, + 0x12700000128, + 0x1290000012a, + 0x12b0000012c, + 0x12d0000012e, + 0x12f00000130, + 0x13100000132, + 0x13500000136, + 0x13700000139, + 0x13a0000013b, + 0x13c0000013d, + 0x13e0000013f, + 0x14200000143, + 0x14400000145, + 0x14600000147, + 0x14800000149, + 0x14b0000014c, + 0x14d0000014e, + 0x14f00000150, + 0x15100000152, + 0x15300000154, + 0x15500000156, + 0x15700000158, + 0x1590000015a, + 0x15b0000015c, + 0x15d0000015e, + 0x15f00000160, + 0x16100000162, + 0x16300000164, + 0x16500000166, + 0x16700000168, + 0x1690000016a, + 0x16b0000016c, + 0x16d0000016e, + 0x16f00000170, + 0x17100000172, + 0x17300000174, + 0x17500000176, + 0x17700000178, + 0x17a0000017b, + 0x17c0000017d, + 0x17e0000017f, + 0x18000000181, + 0x18300000184, + 0x18500000186, + 0x18800000189, + 0x18c0000018e, + 0x19200000193, + 0x19500000196, + 0x1990000019c, + 0x19e0000019f, + 0x1a1000001a2, + 0x1a3000001a4, + 0x1a5000001a6, + 0x1a8000001a9, + 0x1aa000001ac, + 0x1ad000001ae, + 0x1b0000001b1, + 0x1b4000001b5, + 0x1b6000001b7, + 0x1b9000001bc, + 0x1bd000001c4, + 0x1ce000001cf, + 0x1d0000001d1, + 0x1d2000001d3, + 0x1d4000001d5, + 0x1d6000001d7, + 0x1d8000001d9, + 0x1da000001db, + 0x1dc000001de, + 0x1df000001e0, + 0x1e1000001e2, + 0x1e3000001e4, + 0x1e5000001e6, + 0x1e7000001e8, + 0x1e9000001ea, + 0x1eb000001ec, + 0x1ed000001ee, + 0x1ef000001f1, + 0x1f5000001f6, + 0x1f9000001fa, + 0x1fb000001fc, + 0x1fd000001fe, + 0x1ff00000200, + 0x20100000202, + 0x20300000204, + 0x20500000206, + 0x20700000208, + 0x2090000020a, + 0x20b0000020c, + 0x20d0000020e, + 0x20f00000210, + 0x21100000212, + 0x21300000214, + 0x21500000216, + 0x21700000218, + 0x2190000021a, + 0x21b0000021c, + 0x21d0000021e, + 0x21f00000220, + 0x22100000222, + 0x22300000224, + 0x22500000226, + 0x22700000228, + 0x2290000022a, + 0x22b0000022c, + 0x22d0000022e, + 0x22f00000230, + 0x23100000232, + 0x2330000023a, + 0x23c0000023d, + 0x23f00000241, + 0x24200000243, + 0x24700000248, + 0x2490000024a, + 0x24b0000024c, + 0x24d0000024e, + 0x24f000002b0, + 0x2b9000002c2, + 0x2c6000002d2, + 0x2ec000002ed, + 0x2ee000002ef, + 0x30000000340, + 0x34200000343, + 0x3460000034f, + 0x35000000370, + 0x37100000372, + 0x37300000374, + 0x37700000378, + 0x37b0000037e, + 0x39000000391, + 0x3ac000003cf, + 0x3d7000003d8, + 0x3d9000003da, + 0x3db000003dc, + 0x3dd000003de, + 0x3df000003e0, + 0x3e1000003e2, + 0x3e3000003e4, + 0x3e5000003e6, + 0x3e7000003e8, + 0x3e9000003ea, + 0x3eb000003ec, + 0x3ed000003ee, + 0x3ef000003f0, + 0x3f3000003f4, + 0x3f8000003f9, + 0x3fb000003fd, + 0x43000000460, + 0x46100000462, + 0x46300000464, + 0x46500000466, + 0x46700000468, + 0x4690000046a, + 0x46b0000046c, + 0x46d0000046e, + 0x46f00000470, + 0x47100000472, + 0x47300000474, + 0x47500000476, + 0x47700000478, + 0x4790000047a, + 0x47b0000047c, + 0x47d0000047e, + 0x47f00000480, + 0x48100000482, + 0x48300000488, + 0x48b0000048c, + 0x48d0000048e, + 0x48f00000490, + 0x49100000492, + 0x49300000494, + 0x49500000496, + 0x49700000498, + 0x4990000049a, + 0x49b0000049c, + 0x49d0000049e, + 0x49f000004a0, + 0x4a1000004a2, + 0x4a3000004a4, + 0x4a5000004a6, + 0x4a7000004a8, + 0x4a9000004aa, + 0x4ab000004ac, + 0x4ad000004ae, + 0x4af000004b0, + 0x4b1000004b2, + 0x4b3000004b4, + 0x4b5000004b6, + 0x4b7000004b8, + 0x4b9000004ba, + 0x4bb000004bc, + 0x4bd000004be, + 0x4bf000004c0, + 0x4c2000004c3, + 0x4c4000004c5, + 0x4c6000004c7, + 0x4c8000004c9, + 0x4ca000004cb, + 0x4cc000004cd, + 0x4ce000004d0, + 0x4d1000004d2, + 0x4d3000004d4, + 0x4d5000004d6, + 0x4d7000004d8, + 0x4d9000004da, + 0x4db000004dc, + 0x4dd000004de, + 0x4df000004e0, + 0x4e1000004e2, + 0x4e3000004e4, + 0x4e5000004e6, + 0x4e7000004e8, + 0x4e9000004ea, + 0x4eb000004ec, + 0x4ed000004ee, + 0x4ef000004f0, + 0x4f1000004f2, + 0x4f3000004f4, + 0x4f5000004f6, + 0x4f7000004f8, + 0x4f9000004fa, + 0x4fb000004fc, + 0x4fd000004fe, + 0x4ff00000500, + 0x50100000502, + 0x50300000504, + 0x50500000506, + 0x50700000508, + 0x5090000050a, + 0x50b0000050c, + 0x50d0000050e, + 0x50f00000510, + 0x51100000512, + 0x51300000514, + 0x51500000516, + 0x51700000518, + 0x5190000051a, + 0x51b0000051c, + 0x51d0000051e, + 0x51f00000520, + 0x52100000522, + 0x52300000524, + 0x52500000526, + 0x52700000528, + 0x5290000052a, + 0x52b0000052c, + 0x52d0000052e, + 0x52f00000530, + 0x5590000055a, + 0x56000000587, + 0x58800000589, + 0x591000005be, + 0x5bf000005c0, + 0x5c1000005c3, + 0x5c4000005c6, + 0x5c7000005c8, + 0x5d0000005eb, + 0x5ef000005f3, + 0x6100000061b, + 0x62000000640, + 0x64100000660, + 0x66e00000675, + 0x679000006d4, + 0x6d5000006dd, + 0x6df000006e9, + 0x6ea000006f0, + 0x6fa00000700, + 0x7100000074b, + 0x74d000007b2, + 0x7c0000007f6, + 0x7fd000007fe, + 0x8000000082e, + 0x8400000085c, + 0x8600000086b, + 0x8a0000008b5, + 0x8b6000008be, + 0x8d3000008e2, + 0x8e300000958, + 0x96000000964, + 0x96600000970, + 0x97100000984, + 0x9850000098d, + 0x98f00000991, + 0x993000009a9, + 0x9aa000009b1, + 0x9b2000009b3, + 0x9b6000009ba, + 0x9bc000009c5, + 0x9c7000009c9, + 0x9cb000009cf, + 0x9d7000009d8, + 0x9e0000009e4, + 0x9e6000009f2, + 0x9fc000009fd, + 0x9fe000009ff, + 0xa0100000a04, + 0xa0500000a0b, + 0xa0f00000a11, + 0xa1300000a29, + 0xa2a00000a31, + 0xa3200000a33, + 0xa3500000a36, + 0xa3800000a3a, + 0xa3c00000a3d, + 0xa3e00000a43, + 0xa4700000a49, + 0xa4b00000a4e, + 0xa5100000a52, + 0xa5c00000a5d, + 0xa6600000a76, + 0xa8100000a84, + 0xa8500000a8e, + 0xa8f00000a92, + 0xa9300000aa9, + 0xaaa00000ab1, + 0xab200000ab4, + 0xab500000aba, + 0xabc00000ac6, + 0xac700000aca, + 0xacb00000ace, + 0xad000000ad1, + 0xae000000ae4, + 0xae600000af0, + 0xaf900000b00, + 0xb0100000b04, + 0xb0500000b0d, + 0xb0f00000b11, + 0xb1300000b29, + 0xb2a00000b31, + 0xb3200000b34, + 0xb3500000b3a, + 0xb3c00000b45, + 0xb4700000b49, + 0xb4b00000b4e, + 0xb5600000b58, + 0xb5f00000b64, + 0xb6600000b70, + 0xb7100000b72, + 0xb8200000b84, + 0xb8500000b8b, + 0xb8e00000b91, + 0xb9200000b96, + 0xb9900000b9b, + 0xb9c00000b9d, + 0xb9e00000ba0, + 0xba300000ba5, + 0xba800000bab, + 0xbae00000bba, + 0xbbe00000bc3, + 0xbc600000bc9, + 0xbca00000bce, + 0xbd000000bd1, + 0xbd700000bd8, + 0xbe600000bf0, + 0xc0000000c0d, + 0xc0e00000c11, + 0xc1200000c29, + 0xc2a00000c3a, + 0xc3d00000c45, + 0xc4600000c49, + 0xc4a00000c4e, + 0xc5500000c57, + 0xc5800000c5b, + 0xc6000000c64, + 0xc6600000c70, + 0xc8000000c84, + 0xc8500000c8d, + 0xc8e00000c91, + 0xc9200000ca9, + 0xcaa00000cb4, + 0xcb500000cba, + 0xcbc00000cc5, + 0xcc600000cc9, + 0xcca00000cce, + 0xcd500000cd7, + 0xcde00000cdf, + 0xce000000ce4, + 0xce600000cf0, + 0xcf100000cf3, + 0xd0000000d04, + 0xd0500000d0d, + 0xd0e00000d11, + 0xd1200000d45, + 0xd4600000d49, + 0xd4a00000d4f, + 0xd5400000d58, + 0xd5f00000d64, + 0xd6600000d70, + 0xd7a00000d80, + 0xd8200000d84, + 0xd8500000d97, + 0xd9a00000db2, + 0xdb300000dbc, + 0xdbd00000dbe, + 0xdc000000dc7, + 0xdca00000dcb, + 0xdcf00000dd5, + 0xdd600000dd7, + 0xdd800000de0, + 0xde600000df0, + 0xdf200000df4, + 0xe0100000e33, + 0xe3400000e3b, + 0xe4000000e4f, + 0xe5000000e5a, + 0xe8100000e83, + 0xe8400000e85, + 0xe8700000e89, + 0xe8a00000e8b, + 0xe8d00000e8e, + 0xe9400000e98, + 0xe9900000ea0, + 0xea100000ea4, + 0xea500000ea6, + 0xea700000ea8, + 0xeaa00000eac, + 0xead00000eb3, + 0xeb400000eba, + 0xebb00000ebe, + 0xec000000ec5, + 0xec600000ec7, + 0xec800000ece, + 0xed000000eda, + 0xede00000ee0, + 0xf0000000f01, + 0xf0b00000f0c, + 0xf1800000f1a, + 0xf2000000f2a, + 0xf3500000f36, + 0xf3700000f38, + 0xf3900000f3a, + 0xf3e00000f43, + 0xf4400000f48, + 0xf4900000f4d, + 0xf4e00000f52, + 0xf5300000f57, + 0xf5800000f5c, + 0xf5d00000f69, + 0xf6a00000f6d, + 0xf7100000f73, + 0xf7400000f75, + 0xf7a00000f81, + 0xf8200000f85, + 0xf8600000f93, + 0xf9400000f98, + 0xf9900000f9d, + 0xf9e00000fa2, + 0xfa300000fa7, + 0xfa800000fac, + 0xfad00000fb9, + 0xfba00000fbd, + 0xfc600000fc7, + 0x10000000104a, + 0x10500000109e, + 0x10d0000010fb, + 0x10fd00001100, + 0x120000001249, + 0x124a0000124e, + 0x125000001257, + 0x125800001259, + 0x125a0000125e, + 0x126000001289, + 0x128a0000128e, + 0x1290000012b1, + 0x12b2000012b6, + 0x12b8000012bf, + 0x12c0000012c1, + 0x12c2000012c6, + 0x12c8000012d7, + 0x12d800001311, + 0x131200001316, + 0x13180000135b, + 0x135d00001360, + 0x138000001390, + 0x13a0000013f6, + 0x14010000166d, + 0x166f00001680, + 0x16810000169b, + 0x16a0000016eb, + 0x16f1000016f9, + 0x17000000170d, + 0x170e00001715, + 0x172000001735, + 0x174000001754, + 0x17600000176d, + 0x176e00001771, + 0x177200001774, + 0x1780000017b4, + 0x17b6000017d4, + 0x17d7000017d8, + 0x17dc000017de, + 0x17e0000017ea, + 0x18100000181a, + 0x182000001879, + 0x1880000018ab, + 0x18b0000018f6, + 0x19000000191f, + 0x19200000192c, + 0x19300000193c, + 0x19460000196e, + 0x197000001975, + 0x1980000019ac, + 0x19b0000019ca, + 0x19d0000019da, + 0x1a0000001a1c, + 0x1a2000001a5f, + 0x1a6000001a7d, + 0x1a7f00001a8a, + 0x1a9000001a9a, + 0x1aa700001aa8, + 0x1ab000001abe, + 0x1b0000001b4c, + 0x1b5000001b5a, + 0x1b6b00001b74, + 0x1b8000001bf4, + 0x1c0000001c38, + 0x1c4000001c4a, + 0x1c4d00001c7e, + 0x1cd000001cd3, + 0x1cd400001cfa, + 0x1d0000001d2c, + 0x1d2f00001d30, + 0x1d3b00001d3c, + 0x1d4e00001d4f, + 0x1d6b00001d78, + 0x1d7900001d9b, + 0x1dc000001dfa, + 0x1dfb00001e00, + 0x1e0100001e02, + 0x1e0300001e04, + 0x1e0500001e06, + 0x1e0700001e08, + 0x1e0900001e0a, + 0x1e0b00001e0c, + 0x1e0d00001e0e, + 0x1e0f00001e10, + 0x1e1100001e12, + 0x1e1300001e14, + 0x1e1500001e16, + 0x1e1700001e18, + 0x1e1900001e1a, + 0x1e1b00001e1c, + 0x1e1d00001e1e, + 0x1e1f00001e20, + 0x1e2100001e22, + 0x1e2300001e24, + 0x1e2500001e26, + 0x1e2700001e28, + 0x1e2900001e2a, + 0x1e2b00001e2c, + 0x1e2d00001e2e, + 0x1e2f00001e30, + 0x1e3100001e32, + 0x1e3300001e34, + 0x1e3500001e36, + 0x1e3700001e38, + 0x1e3900001e3a, + 0x1e3b00001e3c, + 0x1e3d00001e3e, + 0x1e3f00001e40, + 0x1e4100001e42, + 0x1e4300001e44, + 0x1e4500001e46, + 0x1e4700001e48, + 0x1e4900001e4a, + 0x1e4b00001e4c, + 0x1e4d00001e4e, + 0x1e4f00001e50, + 0x1e5100001e52, + 0x1e5300001e54, + 0x1e5500001e56, + 0x1e5700001e58, + 0x1e5900001e5a, + 0x1e5b00001e5c, + 0x1e5d00001e5e, + 0x1e5f00001e60, + 0x1e6100001e62, + 0x1e6300001e64, + 0x1e6500001e66, + 0x1e6700001e68, + 0x1e6900001e6a, + 0x1e6b00001e6c, + 0x1e6d00001e6e, + 0x1e6f00001e70, + 0x1e7100001e72, + 0x1e7300001e74, + 0x1e7500001e76, + 0x1e7700001e78, + 0x1e7900001e7a, + 0x1e7b00001e7c, + 0x1e7d00001e7e, + 0x1e7f00001e80, + 0x1e8100001e82, + 0x1e8300001e84, + 0x1e8500001e86, + 0x1e8700001e88, + 0x1e8900001e8a, + 0x1e8b00001e8c, + 0x1e8d00001e8e, + 0x1e8f00001e90, + 0x1e9100001e92, + 0x1e9300001e94, + 0x1e9500001e9a, + 0x1e9c00001e9e, + 0x1e9f00001ea0, + 0x1ea100001ea2, + 0x1ea300001ea4, + 0x1ea500001ea6, + 0x1ea700001ea8, + 0x1ea900001eaa, + 0x1eab00001eac, + 0x1ead00001eae, + 0x1eaf00001eb0, + 0x1eb100001eb2, + 0x1eb300001eb4, + 0x1eb500001eb6, + 0x1eb700001eb8, + 0x1eb900001eba, + 0x1ebb00001ebc, + 0x1ebd00001ebe, + 0x1ebf00001ec0, + 0x1ec100001ec2, + 0x1ec300001ec4, + 0x1ec500001ec6, + 0x1ec700001ec8, + 0x1ec900001eca, + 0x1ecb00001ecc, + 0x1ecd00001ece, + 0x1ecf00001ed0, + 0x1ed100001ed2, + 0x1ed300001ed4, + 0x1ed500001ed6, + 0x1ed700001ed8, + 0x1ed900001eda, + 0x1edb00001edc, + 0x1edd00001ede, + 0x1edf00001ee0, + 0x1ee100001ee2, + 0x1ee300001ee4, + 0x1ee500001ee6, + 0x1ee700001ee8, + 0x1ee900001eea, + 0x1eeb00001eec, + 0x1eed00001eee, + 0x1eef00001ef0, + 0x1ef100001ef2, + 0x1ef300001ef4, + 0x1ef500001ef6, + 0x1ef700001ef8, + 0x1ef900001efa, + 0x1efb00001efc, + 0x1efd00001efe, + 0x1eff00001f08, + 0x1f1000001f16, + 0x1f2000001f28, + 0x1f3000001f38, + 0x1f4000001f46, + 0x1f5000001f58, + 0x1f6000001f68, + 0x1f7000001f71, + 0x1f7200001f73, + 0x1f7400001f75, + 0x1f7600001f77, + 0x1f7800001f79, + 0x1f7a00001f7b, + 0x1f7c00001f7d, + 0x1fb000001fb2, + 0x1fb600001fb7, + 0x1fc600001fc7, + 0x1fd000001fd3, + 0x1fd600001fd8, + 0x1fe000001fe3, + 0x1fe400001fe8, + 0x1ff600001ff7, + 0x214e0000214f, + 0x218400002185, + 0x2c3000002c5f, + 0x2c6100002c62, + 0x2c6500002c67, + 0x2c6800002c69, + 0x2c6a00002c6b, + 0x2c6c00002c6d, + 0x2c7100002c72, + 0x2c7300002c75, + 0x2c7600002c7c, + 0x2c8100002c82, + 0x2c8300002c84, + 0x2c8500002c86, + 0x2c8700002c88, + 0x2c8900002c8a, + 0x2c8b00002c8c, + 0x2c8d00002c8e, + 0x2c8f00002c90, + 0x2c9100002c92, + 0x2c9300002c94, + 0x2c9500002c96, + 0x2c9700002c98, + 0x2c9900002c9a, + 0x2c9b00002c9c, + 0x2c9d00002c9e, + 0x2c9f00002ca0, + 0x2ca100002ca2, + 0x2ca300002ca4, + 0x2ca500002ca6, + 0x2ca700002ca8, + 0x2ca900002caa, + 0x2cab00002cac, + 0x2cad00002cae, + 0x2caf00002cb0, + 0x2cb100002cb2, + 0x2cb300002cb4, + 0x2cb500002cb6, + 0x2cb700002cb8, + 0x2cb900002cba, + 0x2cbb00002cbc, + 0x2cbd00002cbe, + 0x2cbf00002cc0, + 0x2cc100002cc2, + 0x2cc300002cc4, + 0x2cc500002cc6, + 0x2cc700002cc8, + 0x2cc900002cca, + 0x2ccb00002ccc, + 0x2ccd00002cce, + 0x2ccf00002cd0, + 0x2cd100002cd2, + 0x2cd300002cd4, + 0x2cd500002cd6, + 0x2cd700002cd8, + 0x2cd900002cda, + 0x2cdb00002cdc, + 0x2cdd00002cde, + 0x2cdf00002ce0, + 0x2ce100002ce2, + 0x2ce300002ce5, + 0x2cec00002ced, + 0x2cee00002cf2, + 0x2cf300002cf4, + 0x2d0000002d26, + 0x2d2700002d28, + 0x2d2d00002d2e, + 0x2d3000002d68, + 0x2d7f00002d97, + 0x2da000002da7, + 0x2da800002daf, + 0x2db000002db7, + 0x2db800002dbf, + 0x2dc000002dc7, + 0x2dc800002dcf, + 0x2dd000002dd7, + 0x2dd800002ddf, + 0x2de000002e00, + 0x2e2f00002e30, + 0x300500003008, + 0x302a0000302e, + 0x303c0000303d, + 0x304100003097, + 0x30990000309b, + 0x309d0000309f, + 0x30a1000030fb, + 0x30fc000030ff, + 0x310500003130, + 0x31a0000031bb, + 0x31f000003200, + 0x340000004db6, + 0x4e0000009ff0, + 0xa0000000a48d, + 0xa4d00000a4fe, + 0xa5000000a60d, + 0xa6100000a62c, + 0xa6410000a642, + 0xa6430000a644, + 0xa6450000a646, + 0xa6470000a648, + 0xa6490000a64a, + 0xa64b0000a64c, + 0xa64d0000a64e, + 0xa64f0000a650, + 0xa6510000a652, + 0xa6530000a654, + 0xa6550000a656, + 0xa6570000a658, + 0xa6590000a65a, + 0xa65b0000a65c, + 0xa65d0000a65e, + 0xa65f0000a660, + 0xa6610000a662, + 0xa6630000a664, + 0xa6650000a666, + 0xa6670000a668, + 0xa6690000a66a, + 0xa66b0000a66c, + 0xa66d0000a670, + 0xa6740000a67e, + 0xa67f0000a680, + 0xa6810000a682, + 0xa6830000a684, + 0xa6850000a686, + 0xa6870000a688, + 0xa6890000a68a, + 0xa68b0000a68c, + 0xa68d0000a68e, + 0xa68f0000a690, + 0xa6910000a692, + 0xa6930000a694, + 0xa6950000a696, + 0xa6970000a698, + 0xa6990000a69a, + 0xa69b0000a69c, + 0xa69e0000a6e6, + 0xa6f00000a6f2, + 0xa7170000a720, + 0xa7230000a724, + 0xa7250000a726, + 0xa7270000a728, + 0xa7290000a72a, + 0xa72b0000a72c, + 0xa72d0000a72e, + 0xa72f0000a732, + 0xa7330000a734, + 0xa7350000a736, + 0xa7370000a738, + 0xa7390000a73a, + 0xa73b0000a73c, + 0xa73d0000a73e, + 0xa73f0000a740, + 0xa7410000a742, + 0xa7430000a744, + 0xa7450000a746, + 0xa7470000a748, + 0xa7490000a74a, + 0xa74b0000a74c, + 0xa74d0000a74e, + 0xa74f0000a750, + 0xa7510000a752, + 0xa7530000a754, + 0xa7550000a756, + 0xa7570000a758, + 0xa7590000a75a, + 0xa75b0000a75c, + 0xa75d0000a75e, + 0xa75f0000a760, + 0xa7610000a762, + 0xa7630000a764, + 0xa7650000a766, + 0xa7670000a768, + 0xa7690000a76a, + 0xa76b0000a76c, + 0xa76d0000a76e, + 0xa76f0000a770, + 0xa7710000a779, + 0xa77a0000a77b, + 0xa77c0000a77d, + 0xa77f0000a780, + 0xa7810000a782, + 0xa7830000a784, + 0xa7850000a786, + 0xa7870000a789, + 0xa78c0000a78d, + 0xa78e0000a790, + 0xa7910000a792, + 0xa7930000a796, + 0xa7970000a798, + 0xa7990000a79a, + 0xa79b0000a79c, + 0xa79d0000a79e, + 0xa79f0000a7a0, + 0xa7a10000a7a2, + 0xa7a30000a7a4, + 0xa7a50000a7a6, + 0xa7a70000a7a8, + 0xa7a90000a7aa, + 0xa7af0000a7b0, + 0xa7b50000a7b6, + 0xa7b70000a7b8, + 0xa7b90000a7ba, + 0xa7f70000a7f8, + 0xa7fa0000a828, + 0xa8400000a874, + 0xa8800000a8c6, + 0xa8d00000a8da, + 0xa8e00000a8f8, + 0xa8fb0000a8fc, + 0xa8fd0000a92e, + 0xa9300000a954, + 0xa9800000a9c1, + 0xa9cf0000a9da, + 0xa9e00000a9ff, + 0xaa000000aa37, + 0xaa400000aa4e, + 0xaa500000aa5a, + 0xaa600000aa77, + 0xaa7a0000aac3, + 0xaadb0000aade, + 0xaae00000aaf0, + 0xaaf20000aaf7, + 0xab010000ab07, + 0xab090000ab0f, + 0xab110000ab17, + 0xab200000ab27, + 0xab280000ab2f, + 0xab300000ab5b, + 0xab600000ab66, + 0xabc00000abeb, + 0xabec0000abee, + 0xabf00000abfa, + 0xac000000d7a4, + 0xfa0e0000fa10, + 0xfa110000fa12, + 0xfa130000fa15, + 0xfa1f0000fa20, + 0xfa210000fa22, + 0xfa230000fa25, + 0xfa270000fa2a, + 0xfb1e0000fb1f, + 0xfe200000fe30, + 0xfe730000fe74, + 0x100000001000c, + 0x1000d00010027, + 0x100280001003b, + 0x1003c0001003e, + 0x1003f0001004e, + 0x100500001005e, + 0x10080000100fb, + 0x101fd000101fe, + 0x102800001029d, + 0x102a0000102d1, + 0x102e0000102e1, + 0x1030000010320, + 0x1032d00010341, + 0x103420001034a, + 0x103500001037b, + 0x103800001039e, + 0x103a0000103c4, + 0x103c8000103d0, + 0x104280001049e, + 0x104a0000104aa, + 0x104d8000104fc, + 0x1050000010528, + 0x1053000010564, + 0x1060000010737, + 0x1074000010756, + 0x1076000010768, + 0x1080000010806, + 0x1080800010809, + 0x1080a00010836, + 0x1083700010839, + 0x1083c0001083d, + 0x1083f00010856, + 0x1086000010877, + 0x108800001089f, + 0x108e0000108f3, + 0x108f4000108f6, + 0x1090000010916, + 0x109200001093a, + 0x10980000109b8, + 0x109be000109c0, + 0x10a0000010a04, + 0x10a0500010a07, + 0x10a0c00010a14, + 0x10a1500010a18, + 0x10a1900010a36, + 0x10a3800010a3b, + 0x10a3f00010a40, + 0x10a6000010a7d, + 0x10a8000010a9d, + 0x10ac000010ac8, + 0x10ac900010ae7, + 0x10b0000010b36, + 0x10b4000010b56, + 0x10b6000010b73, + 0x10b8000010b92, + 0x10c0000010c49, + 0x10cc000010cf3, + 0x10d0000010d28, + 0x10d3000010d3a, + 0x10f0000010f1d, + 0x10f2700010f28, + 0x10f3000010f51, + 0x1100000011047, + 0x1106600011070, + 0x1107f000110bb, + 0x110d0000110e9, + 0x110f0000110fa, + 0x1110000011135, + 0x1113600011140, + 0x1114400011147, + 0x1115000011174, + 0x1117600011177, + 0x11180000111c5, + 0x111c9000111cd, + 0x111d0000111db, + 0x111dc000111dd, + 0x1120000011212, + 0x1121300011238, + 0x1123e0001123f, + 0x1128000011287, + 0x1128800011289, + 0x1128a0001128e, + 0x1128f0001129e, + 0x1129f000112a9, + 0x112b0000112eb, + 0x112f0000112fa, + 0x1130000011304, + 0x113050001130d, + 0x1130f00011311, + 0x1131300011329, + 0x1132a00011331, + 0x1133200011334, + 0x113350001133a, + 0x1133b00011345, + 0x1134700011349, + 0x1134b0001134e, + 0x1135000011351, + 0x1135700011358, + 0x1135d00011364, + 0x113660001136d, + 0x1137000011375, + 0x114000001144b, + 0x114500001145a, + 0x1145e0001145f, + 0x11480000114c6, + 0x114c7000114c8, + 0x114d0000114da, + 0x11580000115b6, + 0x115b8000115c1, + 0x115d8000115de, + 0x1160000011641, + 0x1164400011645, + 0x116500001165a, + 0x11680000116b8, + 0x116c0000116ca, + 0x117000001171b, + 0x1171d0001172c, + 0x117300001173a, + 0x118000001183b, + 0x118c0000118ea, + 0x118ff00011900, + 0x11a0000011a3f, + 0x11a4700011a48, + 0x11a5000011a84, + 0x11a8600011a9a, + 0x11a9d00011a9e, + 0x11ac000011af9, + 0x11c0000011c09, + 0x11c0a00011c37, + 0x11c3800011c41, + 0x11c5000011c5a, + 0x11c7200011c90, + 0x11c9200011ca8, + 0x11ca900011cb7, + 0x11d0000011d07, + 0x11d0800011d0a, + 0x11d0b00011d37, + 0x11d3a00011d3b, + 0x11d3c00011d3e, + 0x11d3f00011d48, + 0x11d5000011d5a, + 0x11d6000011d66, + 0x11d6700011d69, + 0x11d6a00011d8f, + 0x11d9000011d92, + 0x11d9300011d99, + 0x11da000011daa, + 0x11ee000011ef7, + 0x120000001239a, + 0x1248000012544, + 0x130000001342f, + 0x1440000014647, + 0x1680000016a39, + 0x16a4000016a5f, + 0x16a6000016a6a, + 0x16ad000016aee, + 0x16af000016af5, + 0x16b0000016b37, + 0x16b4000016b44, + 0x16b5000016b5a, + 0x16b6300016b78, + 0x16b7d00016b90, + 0x16e6000016e80, + 0x16f0000016f45, + 0x16f5000016f7f, + 0x16f8f00016fa0, + 0x16fe000016fe2, + 0x17000000187f2, + 0x1880000018af3, + 0x1b0000001b11f, + 0x1b1700001b2fc, + 0x1bc000001bc6b, + 0x1bc700001bc7d, + 0x1bc800001bc89, + 0x1bc900001bc9a, + 0x1bc9d0001bc9f, + 0x1da000001da37, + 0x1da3b0001da6d, + 0x1da750001da76, + 0x1da840001da85, + 0x1da9b0001daa0, + 0x1daa10001dab0, + 0x1e0000001e007, + 0x1e0080001e019, + 0x1e01b0001e022, + 0x1e0230001e025, + 0x1e0260001e02b, + 0x1e8000001e8c5, + 0x1e8d00001e8d7, + 0x1e9220001e94b, + 0x1e9500001e95a, + 0x200000002a6d7, + 0x2a7000002b735, + 0x2b7400002b81e, + 0x2b8200002cea2, + 0x2ceb00002ebe1, + ), + 'CONTEXTJ': ( + 0x200c0000200e, + ), + 'CONTEXTO': ( + 0xb7000000b8, + 0x37500000376, + 0x5f3000005f5, + 0x6600000066a, + 0x6f0000006fa, + 0x30fb000030fc, + ), +} diff --git a/venv2/lib/python2.7/site-packages/pip/_vendor/idna/intranges.py b/venv2/lib/python2.7/site-packages/pip/_vendor/idna/intranges.py new file mode 100644 index 0000000..fa8a735 --- /dev/null +++ b/venv2/lib/python2.7/site-packages/pip/_vendor/idna/intranges.py @@ -0,0 +1,53 @@ +""" +Given a list of integers, made up of (hopefully) a small number of long runs +of consecutive integers, compute a representation of the form +((start1, end1), (start2, end2) ...). Then answer the question "was x present +in the original list?" in time O(log(# runs)). +""" + +import bisect + +def intranges_from_list(list_): + """Represent a list of integers as a sequence of ranges: + ((start_0, end_0), (start_1, end_1), ...), such that the original + integers are exactly those x such that start_i <= x < end_i for some i. + + Ranges are encoded as single integers (start << 32 | end), not as tuples. + """ + + sorted_list = sorted(list_) + ranges = [] + last_write = -1 + for i in range(len(sorted_list)): + if i+1 < len(sorted_list): + if sorted_list[i] == sorted_list[i+1]-1: + continue + current_range = sorted_list[last_write+1:i+1] + ranges.append(_encode_range(current_range[0], current_range[-1] + 1)) + last_write = i + + return tuple(ranges) + +def _encode_range(start, end): + return (start << 32) | end + +def _decode_range(r): + return (r >> 32), (r & ((1 << 32) - 1)) + + +def intranges_contain(int_, ranges): + """Determine if `int_` falls into one of the ranges in `ranges`.""" + tuple_ = _encode_range(int_, 0) + pos = bisect.bisect_left(ranges, tuple_) + # we could be immediately ahead of a tuple (start, end) + # with start < int_ <= end + if pos > 0: + left, right = _decode_range(ranges[pos-1]) + if left <= int_ < right: + return True + # or we could be immediately behind a tuple (int_, end) + if pos < len(ranges): + left, _ = _decode_range(ranges[pos]) + if left == int_: + return True + return False diff --git a/venv2/lib/python2.7/site-packages/pip/_vendor/idna/package_data.py b/venv2/lib/python2.7/site-packages/pip/_vendor/idna/package_data.py new file mode 100644 index 0000000..257e898 --- /dev/null +++ b/venv2/lib/python2.7/site-packages/pip/_vendor/idna/package_data.py @@ -0,0 +1,2 @@ +__version__ = '2.8' + diff --git a/venv2/lib/python2.7/site-packages/pip/_vendor/idna/uts46data.py b/venv2/lib/python2.7/site-packages/pip/_vendor/idna/uts46data.py new file mode 100644 index 0000000..a68ed4c --- /dev/null +++ b/venv2/lib/python2.7/site-packages/pip/_vendor/idna/uts46data.py @@ -0,0 +1,8205 @@ +# This file is automatically generated by tools/idna-data +# vim: set fileencoding=utf-8 : + +"""IDNA Mapping Table from UTS46.""" + + +__version__ = "11.0.0" +def _seg_0(): + return [ + (0x0, '3'), + (0x1, '3'), + (0x2, '3'), + (0x3, '3'), + (0x4, '3'), + (0x5, '3'), + (0x6, '3'), + (0x7, '3'), + (0x8, '3'), + (0x9, '3'), + (0xA, '3'), + (0xB, '3'), + (0xC, '3'), + (0xD, '3'), + (0xE, '3'), + (0xF, '3'), + (0x10, '3'), + (0x11, '3'), + (0x12, '3'), + (0x13, '3'), + (0x14, '3'), + (0x15, '3'), + (0x16, '3'), + (0x17, '3'), + (0x18, '3'), + (0x19, '3'), + (0x1A, '3'), + (0x1B, '3'), + (0x1C, '3'), + (0x1D, '3'), + (0x1E, '3'), + (0x1F, '3'), + (0x20, '3'), + (0x21, '3'), + (0x22, '3'), + (0x23, '3'), + (0x24, '3'), + (0x25, '3'), + (0x26, '3'), + (0x27, '3'), + (0x28, '3'), + (0x29, '3'), + (0x2A, '3'), + (0x2B, '3'), + (0x2C, '3'), + (0x2D, 'V'), + (0x2E, 'V'), + (0x2F, '3'), + (0x30, 'V'), + (0x31, 'V'), + (0x32, 'V'), + (0x33, 'V'), + (0x34, 'V'), + (0x35, 'V'), + (0x36, 'V'), + (0x37, 'V'), + (0x38, 'V'), + (0x39, 'V'), + (0x3A, '3'), + (0x3B, '3'), + (0x3C, '3'), + (0x3D, '3'), + (0x3E, '3'), + (0x3F, '3'), + (0x40, '3'), + (0x41, 'M', u'a'), + (0x42, 'M', u'b'), + (0x43, 'M', u'c'), + (0x44, 'M', u'd'), + (0x45, 'M', u'e'), + (0x46, 'M', u'f'), + (0x47, 'M', u'g'), + (0x48, 'M', u'h'), + (0x49, 'M', u'i'), + (0x4A, 'M', u'j'), + (0x4B, 'M', u'k'), + (0x4C, 'M', u'l'), + (0x4D, 'M', u'm'), + (0x4E, 'M', u'n'), + (0x4F, 'M', u'o'), + (0x50, 'M', u'p'), + (0x51, 'M', u'q'), + (0x52, 'M', u'r'), + (0x53, 'M', u's'), + (0x54, 'M', u't'), + (0x55, 'M', u'u'), + (0x56, 'M', u'v'), + (0x57, 'M', u'w'), + (0x58, 'M', u'x'), + (0x59, 'M', u'y'), + (0x5A, 'M', u'z'), + (0x5B, '3'), + (0x5C, '3'), + (0x5D, '3'), + (0x5E, '3'), + (0x5F, '3'), + (0x60, '3'), + (0x61, 'V'), + (0x62, 'V'), + (0x63, 'V'), + ] + +def _seg_1(): + return [ + (0x64, 'V'), + (0x65, 'V'), + (0x66, 'V'), + (0x67, 'V'), + (0x68, 'V'), + (0x69, 'V'), + (0x6A, 'V'), + (0x6B, 'V'), + (0x6C, 'V'), + (0x6D, 'V'), + (0x6E, 'V'), + (0x6F, 'V'), + (0x70, 'V'), + (0x71, 'V'), + (0x72, 'V'), + (0x73, 'V'), + (0x74, 'V'), + (0x75, 'V'), + (0x76, 'V'), + (0x77, 'V'), + (0x78, 'V'), + (0x79, 'V'), + (0x7A, 'V'), + (0x7B, '3'), + (0x7C, '3'), + (0x7D, '3'), + (0x7E, '3'), + (0x7F, '3'), + (0x80, 'X'), + (0x81, 'X'), + (0x82, 'X'), + (0x83, 'X'), + (0x84, 'X'), + (0x85, 'X'), + (0x86, 'X'), + (0x87, 'X'), + (0x88, 'X'), + (0x89, 'X'), + (0x8A, 'X'), + (0x8B, 'X'), + (0x8C, 'X'), + (0x8D, 'X'), + (0x8E, 'X'), + (0x8F, 'X'), + (0x90, 'X'), + (0x91, 'X'), + (0x92, 'X'), + (0x93, 'X'), + (0x94, 'X'), + (0x95, 'X'), + (0x96, 'X'), + (0x97, 'X'), + (0x98, 'X'), + (0x99, 'X'), + (0x9A, 'X'), + (0x9B, 'X'), + (0x9C, 'X'), + (0x9D, 'X'), + (0x9E, 'X'), + (0x9F, 'X'), + (0xA0, '3', u' '), + (0xA1, 'V'), + (0xA2, 'V'), + (0xA3, 'V'), + (0xA4, 'V'), + (0xA5, 'V'), + (0xA6, 'V'), + (0xA7, 'V'), + (0xA8, '3', u' ̈'), + (0xA9, 'V'), + (0xAA, 'M', u'a'), + (0xAB, 'V'), + (0xAC, 'V'), + (0xAD, 'I'), + (0xAE, 'V'), + (0xAF, '3', u' ̄'), + (0xB0, 'V'), + (0xB1, 'V'), + (0xB2, 'M', u'2'), + (0xB3, 'M', u'3'), + (0xB4, '3', u' ́'), + (0xB5, 'M', u'μ'), + (0xB6, 'V'), + (0xB7, 'V'), + (0xB8, '3', u' ̧'), + (0xB9, 'M', u'1'), + (0xBA, 'M', u'o'), + (0xBB, 'V'), + (0xBC, 'M', u'1⁄4'), + (0xBD, 'M', u'1⁄2'), + (0xBE, 'M', u'3⁄4'), + (0xBF, 'V'), + (0xC0, 'M', u'à'), + (0xC1, 'M', u'á'), + (0xC2, 'M', u'â'), + (0xC3, 'M', u'ã'), + (0xC4, 'M', u'ä'), + (0xC5, 'M', u'å'), + (0xC6, 'M', u'æ'), + (0xC7, 'M', u'ç'), + ] + +def _seg_2(): + return [ + (0xC8, 'M', u'è'), + (0xC9, 'M', u'é'), + (0xCA, 'M', u'ê'), + (0xCB, 'M', u'ë'), + (0xCC, 'M', u'ì'), + (0xCD, 'M', u'í'), + (0xCE, 'M', u'î'), + (0xCF, 'M', u'ï'), + (0xD0, 'M', u'ð'), + (0xD1, 'M', u'ñ'), + (0xD2, 'M', u'ò'), + (0xD3, 'M', u'ó'), + (0xD4, 'M', u'ô'), + (0xD5, 'M', u'õ'), + (0xD6, 'M', u'ö'), + (0xD7, 'V'), + (0xD8, 'M', u'ø'), + (0xD9, 'M', u'ù'), + (0xDA, 'M', u'ú'), + (0xDB, 'M', u'û'), + (0xDC, 'M', u'ü'), + (0xDD, 'M', u'ý'), + (0xDE, 'M', u'þ'), + (0xDF, 'D', u'ss'), + (0xE0, 'V'), + (0xE1, 'V'), + (0xE2, 'V'), + (0xE3, 'V'), + (0xE4, 'V'), + (0xE5, 'V'), + (0xE6, 'V'), + (0xE7, 'V'), + (0xE8, 'V'), + (0xE9, 'V'), + (0xEA, 'V'), + (0xEB, 'V'), + (0xEC, 'V'), + (0xED, 'V'), + (0xEE, 'V'), + (0xEF, 'V'), + (0xF0, 'V'), + (0xF1, 'V'), + (0xF2, 'V'), + (0xF3, 'V'), + (0xF4, 'V'), + (0xF5, 'V'), + (0xF6, 'V'), + (0xF7, 'V'), + (0xF8, 'V'), + (0xF9, 'V'), + (0xFA, 'V'), + (0xFB, 'V'), + (0xFC, 'V'), + (0xFD, 'V'), + (0xFE, 'V'), + (0xFF, 'V'), + (0x100, 'M', u'ā'), + (0x101, 'V'), + (0x102, 'M', u'ă'), + (0x103, 'V'), + (0x104, 'M', u'ą'), + (0x105, 'V'), + (0x106, 'M', u'ć'), + (0x107, 'V'), + (0x108, 'M', u'ĉ'), + (0x109, 'V'), + (0x10A, 'M', u'ċ'), + (0x10B, 'V'), + (0x10C, 'M', u'č'), + (0x10D, 'V'), + (0x10E, 'M', u'ď'), + (0x10F, 'V'), + (0x110, 'M', u'đ'), + (0x111, 'V'), + (0x112, 'M', u'ē'), + (0x113, 'V'), + (0x114, 'M', u'ĕ'), + (0x115, 'V'), + (0x116, 'M', u'ė'), + (0x117, 'V'), + (0x118, 'M', u'ę'), + (0x119, 'V'), + (0x11A, 'M', u'ě'), + (0x11B, 'V'), + (0x11C, 'M', u'ĝ'), + (0x11D, 'V'), + (0x11E, 'M', u'ğ'), + (0x11F, 'V'), + (0x120, 'M', u'ġ'), + (0x121, 'V'), + (0x122, 'M', u'ģ'), + (0x123, 'V'), + (0x124, 'M', u'ĥ'), + (0x125, 'V'), + (0x126, 'M', u'ħ'), + (0x127, 'V'), + (0x128, 'M', u'ĩ'), + (0x129, 'V'), + (0x12A, 'M', u'ī'), + (0x12B, 'V'), + ] + +def _seg_3(): + return [ + (0x12C, 'M', u'ĭ'), + (0x12D, 'V'), + (0x12E, 'M', u'į'), + (0x12F, 'V'), + (0x130, 'M', u'i̇'), + (0x131, 'V'), + (0x132, 'M', u'ij'), + (0x134, 'M', u'ĵ'), + (0x135, 'V'), + (0x136, 'M', u'ķ'), + (0x137, 'V'), + (0x139, 'M', u'ĺ'), + (0x13A, 'V'), + (0x13B, 'M', u'ļ'), + (0x13C, 'V'), + (0x13D, 'M', u'ľ'), + (0x13E, 'V'), + (0x13F, 'M', u'l·'), + (0x141, 'M', u'ł'), + (0x142, 'V'), + (0x143, 'M', u'ń'), + (0x144, 'V'), + (0x145, 'M', u'ņ'), + (0x146, 'V'), + (0x147, 'M', u'ň'), + (0x148, 'V'), + (0x149, 'M', u'ʼn'), + (0x14A, 'M', u'ŋ'), + (0x14B, 'V'), + (0x14C, 'M', u'ō'), + (0x14D, 'V'), + (0x14E, 'M', u'ŏ'), + (0x14F, 'V'), + (0x150, 'M', u'ő'), + (0x151, 'V'), + (0x152, 'M', u'œ'), + (0x153, 'V'), + (0x154, 'M', u'ŕ'), + (0x155, 'V'), + (0x156, 'M', u'ŗ'), + (0x157, 'V'), + (0x158, 'M', u'ř'), + (0x159, 'V'), + (0x15A, 'M', u'ś'), + (0x15B, 'V'), + (0x15C, 'M', u'ŝ'), + (0x15D, 'V'), + (0x15E, 'M', u'ş'), + (0x15F, 'V'), + (0x160, 'M', u'š'), + (0x161, 'V'), + (0x162, 'M', u'ţ'), + (0x163, 'V'), + (0x164, 'M', u'ť'), + (0x165, 'V'), + (0x166, 'M', u'ŧ'), + (0x167, 'V'), + (0x168, 'M', u'ũ'), + (0x169, 'V'), + (0x16A, 'M', u'ū'), + (0x16B, 'V'), + (0x16C, 'M', u'ŭ'), + (0x16D, 'V'), + (0x16E, 'M', u'ů'), + (0x16F, 'V'), + (0x170, 'M', u'ű'), + (0x171, 'V'), + (0x172, 'M', u'ų'), + (0x173, 'V'), + (0x174, 'M', u'ŵ'), + (0x175, 'V'), + (0x176, 'M', u'ŷ'), + (0x177, 'V'), + (0x178, 'M', u'ÿ'), + (0x179, 'M', u'ź'), + (0x17A, 'V'), + (0x17B, 'M', u'ż'), + (0x17C, 'V'), + (0x17D, 'M', u'ž'), + (0x17E, 'V'), + (0x17F, 'M', u's'), + (0x180, 'V'), + (0x181, 'M', u'ɓ'), + (0x182, 'M', u'ƃ'), + (0x183, 'V'), + (0x184, 'M', u'ƅ'), + (0x185, 'V'), + (0x186, 'M', u'ɔ'), + (0x187, 'M', u'ƈ'), + (0x188, 'V'), + (0x189, 'M', u'ɖ'), + (0x18A, 'M', u'ɗ'), + (0x18B, 'M', u'ƌ'), + (0x18C, 'V'), + (0x18E, 'M', u'ǝ'), + (0x18F, 'M', u'ə'), + (0x190, 'M', u'ɛ'), + (0x191, 'M', u'ƒ'), + (0x192, 'V'), + (0x193, 'M', u'ɠ'), + ] + +def _seg_4(): + return [ + (0x194, 'M', u'ɣ'), + (0x195, 'V'), + (0x196, 'M', u'ɩ'), + (0x197, 'M', u'ɨ'), + (0x198, 'M', u'ƙ'), + (0x199, 'V'), + (0x19C, 'M', u'ɯ'), + (0x19D, 'M', u'ɲ'), + (0x19E, 'V'), + (0x19F, 'M', u'ɵ'), + (0x1A0, 'M', u'ơ'), + (0x1A1, 'V'), + (0x1A2, 'M', u'ƣ'), + (0x1A3, 'V'), + (0x1A4, 'M', u'ƥ'), + (0x1A5, 'V'), + (0x1A6, 'M', u'ʀ'), + (0x1A7, 'M', u'ƨ'), + (0x1A8, 'V'), + (0x1A9, 'M', u'ʃ'), + (0x1AA, 'V'), + (0x1AC, 'M', u'ƭ'), + (0x1AD, 'V'), + (0x1AE, 'M', u'ʈ'), + (0x1AF, 'M', u'ư'), + (0x1B0, 'V'), + (0x1B1, 'M', u'ʊ'), + (0x1B2, 'M', u'ʋ'), + (0x1B3, 'M', u'ƴ'), + (0x1B4, 'V'), + (0x1B5, 'M', u'ƶ'), + (0x1B6, 'V'), + (0x1B7, 'M', u'ʒ'), + (0x1B8, 'M', u'ƹ'), + (0x1B9, 'V'), + (0x1BC, 'M', u'ƽ'), + (0x1BD, 'V'), + (0x1C4, 'M', u'dž'), + (0x1C7, 'M', u'lj'), + (0x1CA, 'M', u'nj'), + (0x1CD, 'M', u'ǎ'), + (0x1CE, 'V'), + (0x1CF, 'M', u'ǐ'), + (0x1D0, 'V'), + (0x1D1, 'M', u'ǒ'), + (0x1D2, 'V'), + (0x1D3, 'M', u'ǔ'), + (0x1D4, 'V'), + (0x1D5, 'M', u'ǖ'), + (0x1D6, 'V'), + (0x1D7, 'M', u'ǘ'), + (0x1D8, 'V'), + (0x1D9, 'M', u'ǚ'), + (0x1DA, 'V'), + (0x1DB, 'M', u'ǜ'), + (0x1DC, 'V'), + (0x1DE, 'M', u'ǟ'), + (0x1DF, 'V'), + (0x1E0, 'M', u'ǡ'), + (0x1E1, 'V'), + (0x1E2, 'M', u'ǣ'), + (0x1E3, 'V'), + (0x1E4, 'M', u'ǥ'), + (0x1E5, 'V'), + (0x1E6, 'M', u'ǧ'), + (0x1E7, 'V'), + (0x1E8, 'M', u'ǩ'), + (0x1E9, 'V'), + (0x1EA, 'M', u'ǫ'), + (0x1EB, 'V'), + (0x1EC, 'M', u'ǭ'), + (0x1ED, 'V'), + (0x1EE, 'M', u'ǯ'), + (0x1EF, 'V'), + (0x1F1, 'M', u'dz'), + (0x1F4, 'M', u'ǵ'), + (0x1F5, 'V'), + (0x1F6, 'M', u'ƕ'), + (0x1F7, 'M', u'ƿ'), + (0x1F8, 'M', u'ǹ'), + (0x1F9, 'V'), + (0x1FA, 'M', u'ǻ'), + (0x1FB, 'V'), + (0x1FC, 'M', u'ǽ'), + (0x1FD, 'V'), + (0x1FE, 'M', u'ǿ'), + (0x1FF, 'V'), + (0x200, 'M', u'ȁ'), + (0x201, 'V'), + (0x202, 'M', u'ȃ'), + (0x203, 'V'), + (0x204, 'M', u'ȅ'), + (0x205, 'V'), + (0x206, 'M', u'ȇ'), + (0x207, 'V'), + (0x208, 'M', u'ȉ'), + (0x209, 'V'), + (0x20A, 'M', u'ȋ'), + (0x20B, 'V'), + (0x20C, 'M', u'ȍ'), + ] + +def _seg_5(): + return [ + (0x20D, 'V'), + (0x20E, 'M', u'ȏ'), + (0x20F, 'V'), + (0x210, 'M', u'ȑ'), + (0x211, 'V'), + (0x212, 'M', u'ȓ'), + (0x213, 'V'), + (0x214, 'M', u'ȕ'), + (0x215, 'V'), + (0x216, 'M', u'ȗ'), + (0x217, 'V'), + (0x218, 'M', u'ș'), + (0x219, 'V'), + (0x21A, 'M', u'ț'), + (0x21B, 'V'), + (0x21C, 'M', u'ȝ'), + (0x21D, 'V'), + (0x21E, 'M', u'ȟ'), + (0x21F, 'V'), + (0x220, 'M', u'ƞ'), + (0x221, 'V'), + (0x222, 'M', u'ȣ'), + (0x223, 'V'), + (0x224, 'M', u'ȥ'), + (0x225, 'V'), + (0x226, 'M', u'ȧ'), + (0x227, 'V'), + (0x228, 'M', u'ȩ'), + (0x229, 'V'), + (0x22A, 'M', u'ȫ'), + (0x22B, 'V'), + (0x22C, 'M', u'ȭ'), + (0x22D, 'V'), + (0x22E, 'M', u'ȯ'), + (0x22F, 'V'), + (0x230, 'M', u'ȱ'), + (0x231, 'V'), + (0x232, 'M', u'ȳ'), + (0x233, 'V'), + (0x23A, 'M', u'ⱥ'), + (0x23B, 'M', u'ȼ'), + (0x23C, 'V'), + (0x23D, 'M', u'ƚ'), + (0x23E, 'M', u'ⱦ'), + (0x23F, 'V'), + (0x241, 'M', u'ɂ'), + (0x242, 'V'), + (0x243, 'M', u'ƀ'), + (0x244, 'M', u'ʉ'), + (0x245, 'M', u'ʌ'), + (0x246, 'M', u'ɇ'), + (0x247, 'V'), + (0x248, 'M', u'ɉ'), + (0x249, 'V'), + (0x24A, 'M', u'ɋ'), + (0x24B, 'V'), + (0x24C, 'M', u'ɍ'), + (0x24D, 'V'), + (0x24E, 'M', u'ɏ'), + (0x24F, 'V'), + (0x2B0, 'M', u'h'), + (0x2B1, 'M', u'ɦ'), + (0x2B2, 'M', u'j'), + (0x2B3, 'M', u'r'), + (0x2B4, 'M', u'ɹ'), + (0x2B5, 'M', u'ɻ'), + (0x2B6, 'M', u'ʁ'), + (0x2B7, 'M', u'w'), + (0x2B8, 'M', u'y'), + (0x2B9, 'V'), + (0x2D8, '3', u' ̆'), + (0x2D9, '3', u' ̇'), + (0x2DA, '3', u' ̊'), + (0x2DB, '3', u' ̨'), + (0x2DC, '3', u' ̃'), + (0x2DD, '3', u' ̋'), + (0x2DE, 'V'), + (0x2E0, 'M', u'ɣ'), + (0x2E1, 'M', u'l'), + (0x2E2, 'M', u's'), + (0x2E3, 'M', u'x'), + (0x2E4, 'M', u'ʕ'), + (0x2E5, 'V'), + (0x340, 'M', u'̀'), + (0x341, 'M', u'́'), + (0x342, 'V'), + (0x343, 'M', u'̓'), + (0x344, 'M', u'̈́'), + (0x345, 'M', u'ι'), + (0x346, 'V'), + (0x34F, 'I'), + (0x350, 'V'), + (0x370, 'M', u'ͱ'), + (0x371, 'V'), + (0x372, 'M', u'ͳ'), + (0x373, 'V'), + (0x374, 'M', u'ʹ'), + (0x375, 'V'), + (0x376, 'M', u'ͷ'), + (0x377, 'V'), + ] + +def _seg_6(): + return [ + (0x378, 'X'), + (0x37A, '3', u' ι'), + (0x37B, 'V'), + (0x37E, '3', u';'), + (0x37F, 'M', u'ϳ'), + (0x380, 'X'), + (0x384, '3', u' ́'), + (0x385, '3', u' ̈́'), + (0x386, 'M', u'ά'), + (0x387, 'M', u'·'), + (0x388, 'M', u'έ'), + (0x389, 'M', u'ή'), + (0x38A, 'M', u'ί'), + (0x38B, 'X'), + (0x38C, 'M', u'ό'), + (0x38D, 'X'), + (0x38E, 'M', u'ύ'), + (0x38F, 'M', u'ώ'), + (0x390, 'V'), + (0x391, 'M', u'α'), + (0x392, 'M', u'β'), + (0x393, 'M', u'γ'), + (0x394, 'M', u'δ'), + (0x395, 'M', u'ε'), + (0x396, 'M', u'ζ'), + (0x397, 'M', u'η'), + (0x398, 'M', u'θ'), + (0x399, 'M', u'ι'), + (0x39A, 'M', u'κ'), + (0x39B, 'M', u'λ'), + (0x39C, 'M', u'μ'), + (0x39D, 'M', u'ν'), + (0x39E, 'M', u'ξ'), + (0x39F, 'M', u'ο'), + (0x3A0, 'M', u'π'), + (0x3A1, 'M', u'ρ'), + (0x3A2, 'X'), + (0x3A3, 'M', u'σ'), + (0x3A4, 'M', u'τ'), + (0x3A5, 'M', u'υ'), + (0x3A6, 'M', u'φ'), + (0x3A7, 'M', u'χ'), + (0x3A8, 'M', u'ψ'), + (0x3A9, 'M', u'ω'), + (0x3AA, 'M', u'ϊ'), + (0x3AB, 'M', u'ϋ'), + (0x3AC, 'V'), + (0x3C2, 'D', u'σ'), + (0x3C3, 'V'), + (0x3CF, 'M', u'ϗ'), + (0x3D0, 'M', u'β'), + (0x3D1, 'M', u'θ'), + (0x3D2, 'M', u'υ'), + (0x3D3, 'M', u'ύ'), + (0x3D4, 'M', u'ϋ'), + (0x3D5, 'M', u'φ'), + (0x3D6, 'M', u'π'), + (0x3D7, 'V'), + (0x3D8, 'M', u'ϙ'), + (0x3D9, 'V'), + (0x3DA, 'M', u'ϛ'), + (0x3DB, 'V'), + (0x3DC, 'M', u'ϝ'), + (0x3DD, 'V'), + (0x3DE, 'M', u'ϟ'), + (0x3DF, 'V'), + (0x3E0, 'M', u'ϡ'), + (0x3E1, 'V'), + (0x3E2, 'M', u'ϣ'), + (0x3E3, 'V'), + (0x3E4, 'M', u'ϥ'), + (0x3E5, 'V'), + (0x3E6, 'M', u'ϧ'), + (0x3E7, 'V'), + (0x3E8, 'M', u'ϩ'), + (0x3E9, 'V'), + (0x3EA, 'M', u'ϫ'), + (0x3EB, 'V'), + (0x3EC, 'M', u'ϭ'), + (0x3ED, 'V'), + (0x3EE, 'M', u'ϯ'), + (0x3EF, 'V'), + (0x3F0, 'M', u'κ'), + (0x3F1, 'M', u'ρ'), + (0x3F2, 'M', u'σ'), + (0x3F3, 'V'), + (0x3F4, 'M', u'θ'), + (0x3F5, 'M', u'ε'), + (0x3F6, 'V'), + (0x3F7, 'M', u'ϸ'), + (0x3F8, 'V'), + (0x3F9, 'M', u'σ'), + (0x3FA, 'M', u'ϻ'), + (0x3FB, 'V'), + (0x3FD, 'M', u'ͻ'), + (0x3FE, 'M', u'ͼ'), + (0x3FF, 'M', u'ͽ'), + (0x400, 'M', u'ѐ'), + (0x401, 'M', u'ё'), + (0x402, 'M', u'ђ'), + ] + +def _seg_7(): + return [ + (0x403, 'M', u'ѓ'), + (0x404, 'M', u'є'), + (0x405, 'M', u'ѕ'), + (0x406, 'M', u'і'), + (0x407, 'M', u'ї'), + (0x408, 'M', u'ј'), + (0x409, 'M', u'љ'), + (0x40A, 'M', u'њ'), + (0x40B, 'M', u'ћ'), + (0x40C, 'M', u'ќ'), + (0x40D, 'M', u'ѝ'), + (0x40E, 'M', u'ў'), + (0x40F, 'M', u'џ'), + (0x410, 'M', u'а'), + (0x411, 'M', u'б'), + (0x412, 'M', u'в'), + (0x413, 'M', u'г'), + (0x414, 'M', u'д'), + (0x415, 'M', u'е'), + (0x416, 'M', u'ж'), + (0x417, 'M', u'з'), + (0x418, 'M', u'и'), + (0x419, 'M', u'й'), + (0x41A, 'M', u'к'), + (0x41B, 'M', u'л'), + (0x41C, 'M', u'м'), + (0x41D, 'M', u'н'), + (0x41E, 'M', u'о'), + (0x41F, 'M', u'п'), + (0x420, 'M', u'р'), + (0x421, 'M', u'с'), + (0x422, 'M', u'т'), + (0x423, 'M', u'у'), + (0x424, 'M', u'ф'), + (0x425, 'M', u'х'), + (0x426, 'M', u'ц'), + (0x427, 'M', u'ч'), + (0x428, 'M', u'ш'), + (0x429, 'M', u'щ'), + (0x42A, 'M', u'ъ'), + (0x42B, 'M', u'ы'), + (0x42C, 'M', u'ь'), + (0x42D, 'M', u'э'), + (0x42E, 'M', u'ю'), + (0x42F, 'M', u'я'), + (0x430, 'V'), + (0x460, 'M', u'ѡ'), + (0x461, 'V'), + (0x462, 'M', u'ѣ'), + (0x463, 'V'), + (0x464, 'M', u'ѥ'), + (0x465, 'V'), + (0x466, 'M', u'ѧ'), + (0x467, 'V'), + (0x468, 'M', u'ѩ'), + (0x469, 'V'), + (0x46A, 'M', u'ѫ'), + (0x46B, 'V'), + (0x46C, 'M', u'ѭ'), + (0x46D, 'V'), + (0x46E, 'M', u'ѯ'), + (0x46F, 'V'), + (0x470, 'M', u'ѱ'), + (0x471, 'V'), + (0x472, 'M', u'ѳ'), + (0x473, 'V'), + (0x474, 'M', u'ѵ'), + (0x475, 'V'), + (0x476, 'M', u'ѷ'), + (0x477, 'V'), + (0x478, 'M', u'ѹ'), + (0x479, 'V'), + (0x47A, 'M', u'ѻ'), + (0x47B, 'V'), + (0x47C, 'M', u'ѽ'), + (0x47D, 'V'), + (0x47E, 'M', u'ѿ'), + (0x47F, 'V'), + (0x480, 'M', u'ҁ'), + (0x481, 'V'), + (0x48A, 'M', u'ҋ'), + (0x48B, 'V'), + (0x48C, 'M', u'ҍ'), + (0x48D, 'V'), + (0x48E, 'M', u'ҏ'), + (0x48F, 'V'), + (0x490, 'M', u'ґ'), + (0x491, 'V'), + (0x492, 'M', u'ғ'), + (0x493, 'V'), + (0x494, 'M', u'ҕ'), + (0x495, 'V'), + (0x496, 'M', u'җ'), + (0x497, 'V'), + (0x498, 'M', u'ҙ'), + (0x499, 'V'), + (0x49A, 'M', u'қ'), + (0x49B, 'V'), + (0x49C, 'M', u'ҝ'), + (0x49D, 'V'), + ] + +def _seg_8(): + return [ + (0x49E, 'M', u'ҟ'), + (0x49F, 'V'), + (0x4A0, 'M', u'ҡ'), + (0x4A1, 'V'), + (0x4A2, 'M', u'ң'), + (0x4A3, 'V'), + (0x4A4, 'M', u'ҥ'), + (0x4A5, 'V'), + (0x4A6, 'M', u'ҧ'), + (0x4A7, 'V'), + (0x4A8, 'M', u'ҩ'), + (0x4A9, 'V'), + (0x4AA, 'M', u'ҫ'), + (0x4AB, 'V'), + (0x4AC, 'M', u'ҭ'), + (0x4AD, 'V'), + (0x4AE, 'M', u'ү'), + (0x4AF, 'V'), + (0x4B0, 'M', u'ұ'), + (0x4B1, 'V'), + (0x4B2, 'M', u'ҳ'), + (0x4B3, 'V'), + (0x4B4, 'M', u'ҵ'), + (0x4B5, 'V'), + (0x4B6, 'M', u'ҷ'), + (0x4B7, 'V'), + (0x4B8, 'M', u'ҹ'), + (0x4B9, 'V'), + (0x4BA, 'M', u'һ'), + (0x4BB, 'V'), + (0x4BC, 'M', u'ҽ'), + (0x4BD, 'V'), + (0x4BE, 'M', u'ҿ'), + (0x4BF, 'V'), + (0x4C0, 'X'), + (0x4C1, 'M', u'ӂ'), + (0x4C2, 'V'), + (0x4C3, 'M', u'ӄ'), + (0x4C4, 'V'), + (0x4C5, 'M', u'ӆ'), + (0x4C6, 'V'), + (0x4C7, 'M', u'ӈ'), + (0x4C8, 'V'), + (0x4C9, 'M', u'ӊ'), + (0x4CA, 'V'), + (0x4CB, 'M', u'ӌ'), + (0x4CC, 'V'), + (0x4CD, 'M', u'ӎ'), + (0x4CE, 'V'), + (0x4D0, 'M', u'ӑ'), + (0x4D1, 'V'), + (0x4D2, 'M', u'ӓ'), + (0x4D3, 'V'), + (0x4D4, 'M', u'ӕ'), + (0x4D5, 'V'), + (0x4D6, 'M', u'ӗ'), + (0x4D7, 'V'), + (0x4D8, 'M', u'ә'), + (0x4D9, 'V'), + (0x4DA, 'M', u'ӛ'), + (0x4DB, 'V'), + (0x4DC, 'M', u'ӝ'), + (0x4DD, 'V'), + (0x4DE, 'M', u'ӟ'), + (0x4DF, 'V'), + (0x4E0, 'M', u'ӡ'), + (0x4E1, 'V'), + (0x4E2, 'M', u'ӣ'), + (0x4E3, 'V'), + (0x4E4, 'M', u'ӥ'), + (0x4E5, 'V'), + (0x4E6, 'M', u'ӧ'), + (0x4E7, 'V'), + (0x4E8, 'M', u'ө'), + (0x4E9, 'V'), + (0x4EA, 'M', u'ӫ'), + (0x4EB, 'V'), + (0x4EC, 'M', u'ӭ'), + (0x4ED, 'V'), + (0x4EE, 'M', u'ӯ'), + (0x4EF, 'V'), + (0x4F0, 'M', u'ӱ'), + (0x4F1, 'V'), + (0x4F2, 'M', u'ӳ'), + (0x4F3, 'V'), + (0x4F4, 'M', u'ӵ'), + (0x4F5, 'V'), + (0x4F6, 'M', u'ӷ'), + (0x4F7, 'V'), + (0x4F8, 'M', u'ӹ'), + (0x4F9, 'V'), + (0x4FA, 'M', u'ӻ'), + (0x4FB, 'V'), + (0x4FC, 'M', u'ӽ'), + (0x4FD, 'V'), + (0x4FE, 'M', u'ӿ'), + (0x4FF, 'V'), + (0x500, 'M', u'ԁ'), + (0x501, 'V'), + (0x502, 'M', u'ԃ'), + ] + +def _seg_9(): + return [ + (0x503, 'V'), + (0x504, 'M', u'ԅ'), + (0x505, 'V'), + (0x506, 'M', u'ԇ'), + (0x507, 'V'), + (0x508, 'M', u'ԉ'), + (0x509, 'V'), + (0x50A, 'M', u'ԋ'), + (0x50B, 'V'), + (0x50C, 'M', u'ԍ'), + (0x50D, 'V'), + (0x50E, 'M', u'ԏ'), + (0x50F, 'V'), + (0x510, 'M', u'ԑ'), + (0x511, 'V'), + (0x512, 'M', u'ԓ'), + (0x513, 'V'), + (0x514, 'M', u'ԕ'), + (0x515, 'V'), + (0x516, 'M', u'ԗ'), + (0x517, 'V'), + (0x518, 'M', u'ԙ'), + (0x519, 'V'), + (0x51A, 'M', u'ԛ'), + (0x51B, 'V'), + (0x51C, 'M', u'ԝ'), + (0x51D, 'V'), + (0x51E, 'M', u'ԟ'), + (0x51F, 'V'), + (0x520, 'M', u'ԡ'), + (0x521, 'V'), + (0x522, 'M', u'ԣ'), + (0x523, 'V'), + (0x524, 'M', u'ԥ'), + (0x525, 'V'), + (0x526, 'M', u'ԧ'), + (0x527, 'V'), + (0x528, 'M', u'ԩ'), + (0x529, 'V'), + (0x52A, 'M', u'ԫ'), + (0x52B, 'V'), + (0x52C, 'M', u'ԭ'), + (0x52D, 'V'), + (0x52E, 'M', u'ԯ'), + (0x52F, 'V'), + (0x530, 'X'), + (0x531, 'M', u'ա'), + (0x532, 'M', u'բ'), + (0x533, 'M', u'գ'), + (0x534, 'M', u'դ'), + (0x535, 'M', u'ե'), + (0x536, 'M', u'զ'), + (0x537, 'M', u'է'), + (0x538, 'M', u'ը'), + (0x539, 'M', u'թ'), + (0x53A, 'M', u'ժ'), + (0x53B, 'M', u'ի'), + (0x53C, 'M', u'լ'), + (0x53D, 'M', u'խ'), + (0x53E, 'M', u'ծ'), + (0x53F, 'M', u'կ'), + (0x540, 'M', u'հ'), + (0x541, 'M', u'ձ'), + (0x542, 'M', u'ղ'), + (0x543, 'M', u'ճ'), + (0x544, 'M', u'մ'), + (0x545, 'M', u'յ'), + (0x546, 'M', u'ն'), + (0x547, 'M', u'շ'), + (0x548, 'M', u'ո'), + (0x549, 'M', u'չ'), + (0x54A, 'M', u'պ'), + (0x54B, 'M', u'ջ'), + (0x54C, 'M', u'ռ'), + (0x54D, 'M', u'ս'), + (0x54E, 'M', u'վ'), + (0x54F, 'M', u'տ'), + (0x550, 'M', u'ր'), + (0x551, 'M', u'ց'), + (0x552, 'M', u'ւ'), + (0x553, 'M', u'փ'), + (0x554, 'M', u'ք'), + (0x555, 'M', u'օ'), + (0x556, 'M', u'ֆ'), + (0x557, 'X'), + (0x559, 'V'), + (0x587, 'M', u'եւ'), + (0x588, 'V'), + (0x58B, 'X'), + (0x58D, 'V'), + (0x590, 'X'), + (0x591, 'V'), + (0x5C8, 'X'), + (0x5D0, 'V'), + (0x5EB, 'X'), + (0x5EF, 'V'), + (0x5F5, 'X'), + (0x606, 'V'), + (0x61C, 'X'), + (0x61E, 'V'), + ] + +def _seg_10(): + return [ + (0x675, 'M', u'اٴ'), + (0x676, 'M', u'وٴ'), + (0x677, 'M', u'ۇٴ'), + (0x678, 'M', u'يٴ'), + (0x679, 'V'), + (0x6DD, 'X'), + (0x6DE, 'V'), + (0x70E, 'X'), + (0x710, 'V'), + (0x74B, 'X'), + (0x74D, 'V'), + (0x7B2, 'X'), + (0x7C0, 'V'), + (0x7FB, 'X'), + (0x7FD, 'V'), + (0x82E, 'X'), + (0x830, 'V'), + (0x83F, 'X'), + (0x840, 'V'), + (0x85C, 'X'), + (0x85E, 'V'), + (0x85F, 'X'), + (0x860, 'V'), + (0x86B, 'X'), + (0x8A0, 'V'), + (0x8B5, 'X'), + (0x8B6, 'V'), + (0x8BE, 'X'), + (0x8D3, 'V'), + (0x8E2, 'X'), + (0x8E3, 'V'), + (0x958, 'M', u'क़'), + (0x959, 'M', u'ख़'), + (0x95A, 'M', u'ग़'), + (0x95B, 'M', u'ज़'), + (0x95C, 'M', u'ड़'), + (0x95D, 'M', u'ढ़'), + (0x95E, 'M', u'फ़'), + (0x95F, 'M', u'य़'), + (0x960, 'V'), + (0x984, 'X'), + (0x985, 'V'), + (0x98D, 'X'), + (0x98F, 'V'), + (0x991, 'X'), + (0x993, 'V'), + (0x9A9, 'X'), + (0x9AA, 'V'), + (0x9B1, 'X'), + (0x9B2, 'V'), + (0x9B3, 'X'), + (0x9B6, 'V'), + (0x9BA, 'X'), + (0x9BC, 'V'), + (0x9C5, 'X'), + (0x9C7, 'V'), + (0x9C9, 'X'), + (0x9CB, 'V'), + (0x9CF, 'X'), + (0x9D7, 'V'), + (0x9D8, 'X'), + (0x9DC, 'M', u'ড়'), + (0x9DD, 'M', u'ঢ়'), + (0x9DE, 'X'), + (0x9DF, 'M', u'য়'), + (0x9E0, 'V'), + (0x9E4, 'X'), + (0x9E6, 'V'), + (0x9FF, 'X'), + (0xA01, 'V'), + (0xA04, 'X'), + (0xA05, 'V'), + (0xA0B, 'X'), + (0xA0F, 'V'), + (0xA11, 'X'), + (0xA13, 'V'), + (0xA29, 'X'), + (0xA2A, 'V'), + (0xA31, 'X'), + (0xA32, 'V'), + (0xA33, 'M', u'ਲ਼'), + (0xA34, 'X'), + (0xA35, 'V'), + (0xA36, 'M', u'ਸ਼'), + (0xA37, 'X'), + (0xA38, 'V'), + (0xA3A, 'X'), + (0xA3C, 'V'), + (0xA3D, 'X'), + (0xA3E, 'V'), + (0xA43, 'X'), + (0xA47, 'V'), + (0xA49, 'X'), + (0xA4B, 'V'), + (0xA4E, 'X'), + (0xA51, 'V'), + (0xA52, 'X'), + (0xA59, 'M', u'ਖ਼'), + (0xA5A, 'M', u'ਗ਼'), + (0xA5B, 'M', u'ਜ਼'), + ] + +def _seg_11(): + return [ + (0xA5C, 'V'), + (0xA5D, 'X'), + (0xA5E, 'M', u'ਫ਼'), + (0xA5F, 'X'), + (0xA66, 'V'), + (0xA77, 'X'), + (0xA81, 'V'), + (0xA84, 'X'), + (0xA85, 'V'), + (0xA8E, 'X'), + (0xA8F, 'V'), + (0xA92, 'X'), + (0xA93, 'V'), + (0xAA9, 'X'), + (0xAAA, 'V'), + (0xAB1, 'X'), + (0xAB2, 'V'), + (0xAB4, 'X'), + (0xAB5, 'V'), + (0xABA, 'X'), + (0xABC, 'V'), + (0xAC6, 'X'), + (0xAC7, 'V'), + (0xACA, 'X'), + (0xACB, 'V'), + (0xACE, 'X'), + (0xAD0, 'V'), + (0xAD1, 'X'), + (0xAE0, 'V'), + (0xAE4, 'X'), + (0xAE6, 'V'), + (0xAF2, 'X'), + (0xAF9, 'V'), + (0xB00, 'X'), + (0xB01, 'V'), + (0xB04, 'X'), + (0xB05, 'V'), + (0xB0D, 'X'), + (0xB0F, 'V'), + (0xB11, 'X'), + (0xB13, 'V'), + (0xB29, 'X'), + (0xB2A, 'V'), + (0xB31, 'X'), + (0xB32, 'V'), + (0xB34, 'X'), + (0xB35, 'V'), + (0xB3A, 'X'), + (0xB3C, 'V'), + (0xB45, 'X'), + (0xB47, 'V'), + (0xB49, 'X'), + (0xB4B, 'V'), + (0xB4E, 'X'), + (0xB56, 'V'), + (0xB58, 'X'), + (0xB5C, 'M', u'ଡ଼'), + (0xB5D, 'M', u'ଢ଼'), + (0xB5E, 'X'), + (0xB5F, 'V'), + (0xB64, 'X'), + (0xB66, 'V'), + (0xB78, 'X'), + (0xB82, 'V'), + (0xB84, 'X'), + (0xB85, 'V'), + (0xB8B, 'X'), + (0xB8E, 'V'), + (0xB91, 'X'), + (0xB92, 'V'), + (0xB96, 'X'), + (0xB99, 'V'), + (0xB9B, 'X'), + (0xB9C, 'V'), + (0xB9D, 'X'), + (0xB9E, 'V'), + (0xBA0, 'X'), + (0xBA3, 'V'), + (0xBA5, 'X'), + (0xBA8, 'V'), + (0xBAB, 'X'), + (0xBAE, 'V'), + (0xBBA, 'X'), + (0xBBE, 'V'), + (0xBC3, 'X'), + (0xBC6, 'V'), + (0xBC9, 'X'), + (0xBCA, 'V'), + (0xBCE, 'X'), + (0xBD0, 'V'), + (0xBD1, 'X'), + (0xBD7, 'V'), + (0xBD8, 'X'), + (0xBE6, 'V'), + (0xBFB, 'X'), + (0xC00, 'V'), + (0xC0D, 'X'), + (0xC0E, 'V'), + (0xC11, 'X'), + (0xC12, 'V'), + ] + +def _seg_12(): + return [ + (0xC29, 'X'), + (0xC2A, 'V'), + (0xC3A, 'X'), + (0xC3D, 'V'), + (0xC45, 'X'), + (0xC46, 'V'), + (0xC49, 'X'), + (0xC4A, 'V'), + (0xC4E, 'X'), + (0xC55, 'V'), + (0xC57, 'X'), + (0xC58, 'V'), + (0xC5B, 'X'), + (0xC60, 'V'), + (0xC64, 'X'), + (0xC66, 'V'), + (0xC70, 'X'), + (0xC78, 'V'), + (0xC8D, 'X'), + (0xC8E, 'V'), + (0xC91, 'X'), + (0xC92, 'V'), + (0xCA9, 'X'), + (0xCAA, 'V'), + (0xCB4, 'X'), + (0xCB5, 'V'), + (0xCBA, 'X'), + (0xCBC, 'V'), + (0xCC5, 'X'), + (0xCC6, 'V'), + (0xCC9, 'X'), + (0xCCA, 'V'), + (0xCCE, 'X'), + (0xCD5, 'V'), + (0xCD7, 'X'), + (0xCDE, 'V'), + (0xCDF, 'X'), + (0xCE0, 'V'), + (0xCE4, 'X'), + (0xCE6, 'V'), + (0xCF0, 'X'), + (0xCF1, 'V'), + (0xCF3, 'X'), + (0xD00, 'V'), + (0xD04, 'X'), + (0xD05, 'V'), + (0xD0D, 'X'), + (0xD0E, 'V'), + (0xD11, 'X'), + (0xD12, 'V'), + (0xD45, 'X'), + (0xD46, 'V'), + (0xD49, 'X'), + (0xD4A, 'V'), + (0xD50, 'X'), + (0xD54, 'V'), + (0xD64, 'X'), + (0xD66, 'V'), + (0xD80, 'X'), + (0xD82, 'V'), + (0xD84, 'X'), + (0xD85, 'V'), + (0xD97, 'X'), + (0xD9A, 'V'), + (0xDB2, 'X'), + (0xDB3, 'V'), + (0xDBC, 'X'), + (0xDBD, 'V'), + (0xDBE, 'X'), + (0xDC0, 'V'), + (0xDC7, 'X'), + (0xDCA, 'V'), + (0xDCB, 'X'), + (0xDCF, 'V'), + (0xDD5, 'X'), + (0xDD6, 'V'), + (0xDD7, 'X'), + (0xDD8, 'V'), + (0xDE0, 'X'), + (0xDE6, 'V'), + (0xDF0, 'X'), + (0xDF2, 'V'), + (0xDF5, 'X'), + (0xE01, 'V'), + (0xE33, 'M', u'ํา'), + (0xE34, 'V'), + (0xE3B, 'X'), + (0xE3F, 'V'), + (0xE5C, 'X'), + (0xE81, 'V'), + (0xE83, 'X'), + (0xE84, 'V'), + (0xE85, 'X'), + (0xE87, 'V'), + (0xE89, 'X'), + (0xE8A, 'V'), + (0xE8B, 'X'), + (0xE8D, 'V'), + (0xE8E, 'X'), + (0xE94, 'V'), + ] + +def _seg_13(): + return [ + (0xE98, 'X'), + (0xE99, 'V'), + (0xEA0, 'X'), + (0xEA1, 'V'), + (0xEA4, 'X'), + (0xEA5, 'V'), + (0xEA6, 'X'), + (0xEA7, 'V'), + (0xEA8, 'X'), + (0xEAA, 'V'), + (0xEAC, 'X'), + (0xEAD, 'V'), + (0xEB3, 'M', u'ໍາ'), + (0xEB4, 'V'), + (0xEBA, 'X'), + (0xEBB, 'V'), + (0xEBE, 'X'), + (0xEC0, 'V'), + (0xEC5, 'X'), + (0xEC6, 'V'), + (0xEC7, 'X'), + (0xEC8, 'V'), + (0xECE, 'X'), + (0xED0, 'V'), + (0xEDA, 'X'), + (0xEDC, 'M', u'ຫນ'), + (0xEDD, 'M', u'ຫມ'), + (0xEDE, 'V'), + (0xEE0, 'X'), + (0xF00, 'V'), + (0xF0C, 'M', u'་'), + (0xF0D, 'V'), + (0xF43, 'M', u'གྷ'), + (0xF44, 'V'), + (0xF48, 'X'), + (0xF49, 'V'), + (0xF4D, 'M', u'ཌྷ'), + (0xF4E, 'V'), + (0xF52, 'M', u'དྷ'), + (0xF53, 'V'), + (0xF57, 'M', u'བྷ'), + (0xF58, 'V'), + (0xF5C, 'M', u'ཛྷ'), + (0xF5D, 'V'), + (0xF69, 'M', u'ཀྵ'), + (0xF6A, 'V'), + (0xF6D, 'X'), + (0xF71, 'V'), + (0xF73, 'M', u'ཱི'), + (0xF74, 'V'), + (0xF75, 'M', u'ཱུ'), + (0xF76, 'M', u'ྲྀ'), + (0xF77, 'M', u'ྲཱྀ'), + (0xF78, 'M', u'ླྀ'), + (0xF79, 'M', u'ླཱྀ'), + (0xF7A, 'V'), + (0xF81, 'M', u'ཱྀ'), + (0xF82, 'V'), + (0xF93, 'M', u'ྒྷ'), + (0xF94, 'V'), + (0xF98, 'X'), + (0xF99, 'V'), + (0xF9D, 'M', u'ྜྷ'), + (0xF9E, 'V'), + (0xFA2, 'M', u'ྡྷ'), + (0xFA3, 'V'), + (0xFA7, 'M', u'ྦྷ'), + (0xFA8, 'V'), + (0xFAC, 'M', u'ྫྷ'), + (0xFAD, 'V'), + (0xFB9, 'M', u'ྐྵ'), + (0xFBA, 'V'), + (0xFBD, 'X'), + (0xFBE, 'V'), + (0xFCD, 'X'), + (0xFCE, 'V'), + (0xFDB, 'X'), + (0x1000, 'V'), + (0x10A0, 'X'), + (0x10C7, 'M', u'ⴧ'), + (0x10C8, 'X'), + (0x10CD, 'M', u'ⴭ'), + (0x10CE, 'X'), + (0x10D0, 'V'), + (0x10FC, 'M', u'ნ'), + (0x10FD, 'V'), + (0x115F, 'X'), + (0x1161, 'V'), + (0x1249, 'X'), + (0x124A, 'V'), + (0x124E, 'X'), + (0x1250, 'V'), + (0x1257, 'X'), + (0x1258, 'V'), + (0x1259, 'X'), + (0x125A, 'V'), + (0x125E, 'X'), + (0x1260, 'V'), + (0x1289, 'X'), + (0x128A, 'V'), + ] + +def _seg_14(): + return [ + (0x128E, 'X'), + (0x1290, 'V'), + (0x12B1, 'X'), + (0x12B2, 'V'), + (0x12B6, 'X'), + (0x12B8, 'V'), + (0x12BF, 'X'), + (0x12C0, 'V'), + (0x12C1, 'X'), + (0x12C2, 'V'), + (0x12C6, 'X'), + (0x12C8, 'V'), + (0x12D7, 'X'), + (0x12D8, 'V'), + (0x1311, 'X'), + (0x1312, 'V'), + (0x1316, 'X'), + (0x1318, 'V'), + (0x135B, 'X'), + (0x135D, 'V'), + (0x137D, 'X'), + (0x1380, 'V'), + (0x139A, 'X'), + (0x13A0, 'V'), + (0x13F6, 'X'), + (0x13F8, 'M', u'Ᏸ'), + (0x13F9, 'M', u'Ᏹ'), + (0x13FA, 'M', u'Ᏺ'), + (0x13FB, 'M', u'Ᏻ'), + (0x13FC, 'M', u'Ᏼ'), + (0x13FD, 'M', u'Ᏽ'), + (0x13FE, 'X'), + (0x1400, 'V'), + (0x1680, 'X'), + (0x1681, 'V'), + (0x169D, 'X'), + (0x16A0, 'V'), + (0x16F9, 'X'), + (0x1700, 'V'), + (0x170D, 'X'), + (0x170E, 'V'), + (0x1715, 'X'), + (0x1720, 'V'), + (0x1737, 'X'), + (0x1740, 'V'), + (0x1754, 'X'), + (0x1760, 'V'), + (0x176D, 'X'), + (0x176E, 'V'), + (0x1771, 'X'), + (0x1772, 'V'), + (0x1774, 'X'), + (0x1780, 'V'), + (0x17B4, 'X'), + (0x17B6, 'V'), + (0x17DE, 'X'), + (0x17E0, 'V'), + (0x17EA, 'X'), + (0x17F0, 'V'), + (0x17FA, 'X'), + (0x1800, 'V'), + (0x1806, 'X'), + (0x1807, 'V'), + (0x180B, 'I'), + (0x180E, 'X'), + (0x1810, 'V'), + (0x181A, 'X'), + (0x1820, 'V'), + (0x1879, 'X'), + (0x1880, 'V'), + (0x18AB, 'X'), + (0x18B0, 'V'), + (0x18F6, 'X'), + (0x1900, 'V'), + (0x191F, 'X'), + (0x1920, 'V'), + (0x192C, 'X'), + (0x1930, 'V'), + (0x193C, 'X'), + (0x1940, 'V'), + (0x1941, 'X'), + (0x1944, 'V'), + (0x196E, 'X'), + (0x1970, 'V'), + (0x1975, 'X'), + (0x1980, 'V'), + (0x19AC, 'X'), + (0x19B0, 'V'), + (0x19CA, 'X'), + (0x19D0, 'V'), + (0x19DB, 'X'), + (0x19DE, 'V'), + (0x1A1C, 'X'), + (0x1A1E, 'V'), + (0x1A5F, 'X'), + (0x1A60, 'V'), + (0x1A7D, 'X'), + (0x1A7F, 'V'), + (0x1A8A, 'X'), + (0x1A90, 'V'), + ] + +def _seg_15(): + return [ + (0x1A9A, 'X'), + (0x1AA0, 'V'), + (0x1AAE, 'X'), + (0x1AB0, 'V'), + (0x1ABF, 'X'), + (0x1B00, 'V'), + (0x1B4C, 'X'), + (0x1B50, 'V'), + (0x1B7D, 'X'), + (0x1B80, 'V'), + (0x1BF4, 'X'), + (0x1BFC, 'V'), + (0x1C38, 'X'), + (0x1C3B, 'V'), + (0x1C4A, 'X'), + (0x1C4D, 'V'), + (0x1C80, 'M', u'в'), + (0x1C81, 'M', u'д'), + (0x1C82, 'M', u'о'), + (0x1C83, 'M', u'с'), + (0x1C84, 'M', u'т'), + (0x1C86, 'M', u'ъ'), + (0x1C87, 'M', u'ѣ'), + (0x1C88, 'M', u'ꙋ'), + (0x1C89, 'X'), + (0x1CC0, 'V'), + (0x1CC8, 'X'), + (0x1CD0, 'V'), + (0x1CFA, 'X'), + (0x1D00, 'V'), + (0x1D2C, 'M', u'a'), + (0x1D2D, 'M', u'æ'), + (0x1D2E, 'M', u'b'), + (0x1D2F, 'V'), + (0x1D30, 'M', u'd'), + (0x1D31, 'M', u'e'), + (0x1D32, 'M', u'ǝ'), + (0x1D33, 'M', u'g'), + (0x1D34, 'M', u'h'), + (0x1D35, 'M', u'i'), + (0x1D36, 'M', u'j'), + (0x1D37, 'M', u'k'), + (0x1D38, 'M', u'l'), + (0x1D39, 'M', u'm'), + (0x1D3A, 'M', u'n'), + (0x1D3B, 'V'), + (0x1D3C, 'M', u'o'), + (0x1D3D, 'M', u'ȣ'), + (0x1D3E, 'M', u'p'), + (0x1D3F, 'M', u'r'), + (0x1D40, 'M', u't'), + (0x1D41, 'M', u'u'), + (0x1D42, 'M', u'w'), + (0x1D43, 'M', u'a'), + (0x1D44, 'M', u'ɐ'), + (0x1D45, 'M', u'ɑ'), + (0x1D46, 'M', u'ᴂ'), + (0x1D47, 'M', u'b'), + (0x1D48, 'M', u'd'), + (0x1D49, 'M', u'e'), + (0x1D4A, 'M', u'ə'), + (0x1D4B, 'M', u'ɛ'), + (0x1D4C, 'M', u'ɜ'), + (0x1D4D, 'M', u'g'), + (0x1D4E, 'V'), + (0x1D4F, 'M', u'k'), + (0x1D50, 'M', u'm'), + (0x1D51, 'M', u'ŋ'), + (0x1D52, 'M', u'o'), + (0x1D53, 'M', u'ɔ'), + (0x1D54, 'M', u'ᴖ'), + (0x1D55, 'M', u'ᴗ'), + (0x1D56, 'M', u'p'), + (0x1D57, 'M', u't'), + (0x1D58, 'M', u'u'), + (0x1D59, 'M', u'ᴝ'), + (0x1D5A, 'M', u'ɯ'), + (0x1D5B, 'M', u'v'), + (0x1D5C, 'M', u'ᴥ'), + (0x1D5D, 'M', u'β'), + (0x1D5E, 'M', u'γ'), + (0x1D5F, 'M', u'δ'), + (0x1D60, 'M', u'φ'), + (0x1D61, 'M', u'χ'), + (0x1D62, 'M', u'i'), + (0x1D63, 'M', u'r'), + (0x1D64, 'M', u'u'), + (0x1D65, 'M', u'v'), + (0x1D66, 'M', u'β'), + (0x1D67, 'M', u'γ'), + (0x1D68, 'M', u'ρ'), + (0x1D69, 'M', u'φ'), + (0x1D6A, 'M', u'χ'), + (0x1D6B, 'V'), + (0x1D78, 'M', u'н'), + (0x1D79, 'V'), + (0x1D9B, 'M', u'ɒ'), + (0x1D9C, 'M', u'c'), + (0x1D9D, 'M', u'ɕ'), + (0x1D9E, 'M', u'ð'), + ] + +def _seg_16(): + return [ + (0x1D9F, 'M', u'ɜ'), + (0x1DA0, 'M', u'f'), + (0x1DA1, 'M', u'ɟ'), + (0x1DA2, 'M', u'ɡ'), + (0x1DA3, 'M', u'ɥ'), + (0x1DA4, 'M', u'ɨ'), + (0x1DA5, 'M', u'ɩ'), + (0x1DA6, 'M', u'ɪ'), + (0x1DA7, 'M', u'ᵻ'), + (0x1DA8, 'M', u'ʝ'), + (0x1DA9, 'M', u'ɭ'), + (0x1DAA, 'M', u'ᶅ'), + (0x1DAB, 'M', u'ʟ'), + (0x1DAC, 'M', u'ɱ'), + (0x1DAD, 'M', u'ɰ'), + (0x1DAE, 'M', u'ɲ'), + (0x1DAF, 'M', u'ɳ'), + (0x1DB0, 'M', u'ɴ'), + (0x1DB1, 'M', u'ɵ'), + (0x1DB2, 'M', u'ɸ'), + (0x1DB3, 'M', u'ʂ'), + (0x1DB4, 'M', u'ʃ'), + (0x1DB5, 'M', u'ƫ'), + (0x1DB6, 'M', u'ʉ'), + (0x1DB7, 'M', u'ʊ'), + (0x1DB8, 'M', u'ᴜ'), + (0x1DB9, 'M', u'ʋ'), + (0x1DBA, 'M', u'ʌ'), + (0x1DBB, 'M', u'z'), + (0x1DBC, 'M', u'ʐ'), + (0x1DBD, 'M', u'ʑ'), + (0x1DBE, 'M', u'ʒ'), + (0x1DBF, 'M', u'θ'), + (0x1DC0, 'V'), + (0x1DFA, 'X'), + (0x1DFB, 'V'), + (0x1E00, 'M', u'ḁ'), + (0x1E01, 'V'), + (0x1E02, 'M', u'ḃ'), + (0x1E03, 'V'), + (0x1E04, 'M', u'ḅ'), + (0x1E05, 'V'), + (0x1E06, 'M', u'ḇ'), + (0x1E07, 'V'), + (0x1E08, 'M', u'ḉ'), + (0x1E09, 'V'), + (0x1E0A, 'M', u'ḋ'), + (0x1E0B, 'V'), + (0x1E0C, 'M', u'ḍ'), + (0x1E0D, 'V'), + (0x1E0E, 'M', u'ḏ'), + (0x1E0F, 'V'), + (0x1E10, 'M', u'ḑ'), + (0x1E11, 'V'), + (0x1E12, 'M', u'ḓ'), + (0x1E13, 'V'), + (0x1E14, 'M', u'ḕ'), + (0x1E15, 'V'), + (0x1E16, 'M', u'ḗ'), + (0x1E17, 'V'), + (0x1E18, 'M', u'ḙ'), + (0x1E19, 'V'), + (0x1E1A, 'M', u'ḛ'), + (0x1E1B, 'V'), + (0x1E1C, 'M', u'ḝ'), + (0x1E1D, 'V'), + (0x1E1E, 'M', u'ḟ'), + (0x1E1F, 'V'), + (0x1E20, 'M', u'ḡ'), + (0x1E21, 'V'), + (0x1E22, 'M', u'ḣ'), + (0x1E23, 'V'), + (0x1E24, 'M', u'ḥ'), + (0x1E25, 'V'), + (0x1E26, 'M', u'ḧ'), + (0x1E27, 'V'), + (0x1E28, 'M', u'ḩ'), + (0x1E29, 'V'), + (0x1E2A, 'M', u'ḫ'), + (0x1E2B, 'V'), + (0x1E2C, 'M', u'ḭ'), + (0x1E2D, 'V'), + (0x1E2E, 'M', u'ḯ'), + (0x1E2F, 'V'), + (0x1E30, 'M', u'ḱ'), + (0x1E31, 'V'), + (0x1E32, 'M', u'ḳ'), + (0x1E33, 'V'), + (0x1E34, 'M', u'ḵ'), + (0x1E35, 'V'), + (0x1E36, 'M', u'ḷ'), + (0x1E37, 'V'), + (0x1E38, 'M', u'ḹ'), + (0x1E39, 'V'), + (0x1E3A, 'M', u'ḻ'), + (0x1E3B, 'V'), + (0x1E3C, 'M', u'ḽ'), + (0x1E3D, 'V'), + (0x1E3E, 'M', u'ḿ'), + (0x1E3F, 'V'), + ] + +def _seg_17(): + return [ + (0x1E40, 'M', u'ṁ'), + (0x1E41, 'V'), + (0x1E42, 'M', u'ṃ'), + (0x1E43, 'V'), + (0x1E44, 'M', u'ṅ'), + (0x1E45, 'V'), + (0x1E46, 'M', u'ṇ'), + (0x1E47, 'V'), + (0x1E48, 'M', u'ṉ'), + (0x1E49, 'V'), + (0x1E4A, 'M', u'ṋ'), + (0x1E4B, 'V'), + (0x1E4C, 'M', u'ṍ'), + (0x1E4D, 'V'), + (0x1E4E, 'M', u'ṏ'), + (0x1E4F, 'V'), + (0x1E50, 'M', u'ṑ'), + (0x1E51, 'V'), + (0x1E52, 'M', u'ṓ'), + (0x1E53, 'V'), + (0x1E54, 'M', u'ṕ'), + (0x1E55, 'V'), + (0x1E56, 'M', u'ṗ'), + (0x1E57, 'V'), + (0x1E58, 'M', u'ṙ'), + (0x1E59, 'V'), + (0x1E5A, 'M', u'ṛ'), + (0x1E5B, 'V'), + (0x1E5C, 'M', u'ṝ'), + (0x1E5D, 'V'), + (0x1E5E, 'M', u'ṟ'), + (0x1E5F, 'V'), + (0x1E60, 'M', u'ṡ'), + (0x1E61, 'V'), + (0x1E62, 'M', u'ṣ'), + (0x1E63, 'V'), + (0x1E64, 'M', u'ṥ'), + (0x1E65, 'V'), + (0x1E66, 'M', u'ṧ'), + (0x1E67, 'V'), + (0x1E68, 'M', u'ṩ'), + (0x1E69, 'V'), + (0x1E6A, 'M', u'ṫ'), + (0x1E6B, 'V'), + (0x1E6C, 'M', u'ṭ'), + (0x1E6D, 'V'), + (0x1E6E, 'M', u'ṯ'), + (0x1E6F, 'V'), + (0x1E70, 'M', u'ṱ'), + (0x1E71, 'V'), + (0x1E72, 'M', u'ṳ'), + (0x1E73, 'V'), + (0x1E74, 'M', u'ṵ'), + (0x1E75, 'V'), + (0x1E76, 'M', u'ṷ'), + (0x1E77, 'V'), + (0x1E78, 'M', u'ṹ'), + (0x1E79, 'V'), + (0x1E7A, 'M', u'ṻ'), + (0x1E7B, 'V'), + (0x1E7C, 'M', u'ṽ'), + (0x1E7D, 'V'), + (0x1E7E, 'M', u'ṿ'), + (0x1E7F, 'V'), + (0x1E80, 'M', u'ẁ'), + (0x1E81, 'V'), + (0x1E82, 'M', u'ẃ'), + (0x1E83, 'V'), + (0x1E84, 'M', u'ẅ'), + (0x1E85, 'V'), + (0x1E86, 'M', u'ẇ'), + (0x1E87, 'V'), + (0x1E88, 'M', u'ẉ'), + (0x1E89, 'V'), + (0x1E8A, 'M', u'ẋ'), + (0x1E8B, 'V'), + (0x1E8C, 'M', u'ẍ'), + (0x1E8D, 'V'), + (0x1E8E, 'M', u'ẏ'), + (0x1E8F, 'V'), + (0x1E90, 'M', u'ẑ'), + (0x1E91, 'V'), + (0x1E92, 'M', u'ẓ'), + (0x1E93, 'V'), + (0x1E94, 'M', u'ẕ'), + (0x1E95, 'V'), + (0x1E9A, 'M', u'aʾ'), + (0x1E9B, 'M', u'ṡ'), + (0x1E9C, 'V'), + (0x1E9E, 'M', u'ss'), + (0x1E9F, 'V'), + (0x1EA0, 'M', u'ạ'), + (0x1EA1, 'V'), + (0x1EA2, 'M', u'ả'), + (0x1EA3, 'V'), + (0x1EA4, 'M', u'ấ'), + (0x1EA5, 'V'), + (0x1EA6, 'M', u'ầ'), + (0x1EA7, 'V'), + (0x1EA8, 'M', u'ẩ'), + ] + +def _seg_18(): + return [ + (0x1EA9, 'V'), + (0x1EAA, 'M', u'ẫ'), + (0x1EAB, 'V'), + (0x1EAC, 'M', u'ậ'), + (0x1EAD, 'V'), + (0x1EAE, 'M', u'ắ'), + (0x1EAF, 'V'), + (0x1EB0, 'M', u'ằ'), + (0x1EB1, 'V'), + (0x1EB2, 'M', u'ẳ'), + (0x1EB3, 'V'), + (0x1EB4, 'M', u'ẵ'), + (0x1EB5, 'V'), + (0x1EB6, 'M', u'ặ'), + (0x1EB7, 'V'), + (0x1EB8, 'M', u'ẹ'), + (0x1EB9, 'V'), + (0x1EBA, 'M', u'ẻ'), + (0x1EBB, 'V'), + (0x1EBC, 'M', u'ẽ'), + (0x1EBD, 'V'), + (0x1EBE, 'M', u'ế'), + (0x1EBF, 'V'), + (0x1EC0, 'M', u'ề'), + (0x1EC1, 'V'), + (0x1EC2, 'M', u'ể'), + (0x1EC3, 'V'), + (0x1EC4, 'M', u'ễ'), + (0x1EC5, 'V'), + (0x1EC6, 'M', u'ệ'), + (0x1EC7, 'V'), + (0x1EC8, 'M', u'ỉ'), + (0x1EC9, 'V'), + (0x1ECA, 'M', u'ị'), + (0x1ECB, 'V'), + (0x1ECC, 'M', u'ọ'), + (0x1ECD, 'V'), + (0x1ECE, 'M', u'ỏ'), + (0x1ECF, 'V'), + (0x1ED0, 'M', u'ố'), + (0x1ED1, 'V'), + (0x1ED2, 'M', u'ồ'), + (0x1ED3, 'V'), + (0x1ED4, 'M', u'ổ'), + (0x1ED5, 'V'), + (0x1ED6, 'M', u'ỗ'), + (0x1ED7, 'V'), + (0x1ED8, 'M', u'ộ'), + (0x1ED9, 'V'), + (0x1EDA, 'M', u'ớ'), + (0x1EDB, 'V'), + (0x1EDC, 'M', u'ờ'), + (0x1EDD, 'V'), + (0x1EDE, 'M', u'ở'), + (0x1EDF, 'V'), + (0x1EE0, 'M', u'ỡ'), + (0x1EE1, 'V'), + (0x1EE2, 'M', u'ợ'), + (0x1EE3, 'V'), + (0x1EE4, 'M', u'ụ'), + (0x1EE5, 'V'), + (0x1EE6, 'M', u'ủ'), + (0x1EE7, 'V'), + (0x1EE8, 'M', u'ứ'), + (0x1EE9, 'V'), + (0x1EEA, 'M', u'ừ'), + (0x1EEB, 'V'), + (0x1EEC, 'M', u'ử'), + (0x1EED, 'V'), + (0x1EEE, 'M', u'ữ'), + (0x1EEF, 'V'), + (0x1EF0, 'M', u'ự'), + (0x1EF1, 'V'), + (0x1EF2, 'M', u'ỳ'), + (0x1EF3, 'V'), + (0x1EF4, 'M', u'ỵ'), + (0x1EF5, 'V'), + (0x1EF6, 'M', u'ỷ'), + (0x1EF7, 'V'), + (0x1EF8, 'M', u'ỹ'), + (0x1EF9, 'V'), + (0x1EFA, 'M', u'ỻ'), + (0x1EFB, 'V'), + (0x1EFC, 'M', u'ỽ'), + (0x1EFD, 'V'), + (0x1EFE, 'M', u'ỿ'), + (0x1EFF, 'V'), + (0x1F08, 'M', u'ἀ'), + (0x1F09, 'M', u'ἁ'), + (0x1F0A, 'M', u'ἂ'), + (0x1F0B, 'M', u'ἃ'), + (0x1F0C, 'M', u'ἄ'), + (0x1F0D, 'M', u'ἅ'), + (0x1F0E, 'M', u'ἆ'), + (0x1F0F, 'M', u'ἇ'), + (0x1F10, 'V'), + (0x1F16, 'X'), + (0x1F18, 'M', u'ἐ'), + (0x1F19, 'M', u'ἑ'), + (0x1F1A, 'M', u'ἒ'), + ] + +def _seg_19(): + return [ + (0x1F1B, 'M', u'ἓ'), + (0x1F1C, 'M', u'ἔ'), + (0x1F1D, 'M', u'ἕ'), + (0x1F1E, 'X'), + (0x1F20, 'V'), + (0x1F28, 'M', u'ἠ'), + (0x1F29, 'M', u'ἡ'), + (0x1F2A, 'M', u'ἢ'), + (0x1F2B, 'M', u'ἣ'), + (0x1F2C, 'M', u'ἤ'), + (0x1F2D, 'M', u'ἥ'), + (0x1F2E, 'M', u'ἦ'), + (0x1F2F, 'M', u'ἧ'), + (0x1F30, 'V'), + (0x1F38, 'M', u'ἰ'), + (0x1F39, 'M', u'ἱ'), + (0x1F3A, 'M', u'ἲ'), + (0x1F3B, 'M', u'ἳ'), + (0x1F3C, 'M', u'ἴ'), + (0x1F3D, 'M', u'ἵ'), + (0x1F3E, 'M', u'ἶ'), + (0x1F3F, 'M', u'ἷ'), + (0x1F40, 'V'), + (0x1F46, 'X'), + (0x1F48, 'M', u'ὀ'), + (0x1F49, 'M', u'ὁ'), + (0x1F4A, 'M', u'ὂ'), + (0x1F4B, 'M', u'ὃ'), + (0x1F4C, 'M', u'ὄ'), + (0x1F4D, 'M', u'ὅ'), + (0x1F4E, 'X'), + (0x1F50, 'V'), + (0x1F58, 'X'), + (0x1F59, 'M', u'ὑ'), + (0x1F5A, 'X'), + (0x1F5B, 'M', u'ὓ'), + (0x1F5C, 'X'), + (0x1F5D, 'M', u'ὕ'), + (0x1F5E, 'X'), + (0x1F5F, 'M', u'ὗ'), + (0x1F60, 'V'), + (0x1F68, 'M', u'ὠ'), + (0x1F69, 'M', u'ὡ'), + (0x1F6A, 'M', u'ὢ'), + (0x1F6B, 'M', u'ὣ'), + (0x1F6C, 'M', u'ὤ'), + (0x1F6D, 'M', u'ὥ'), + (0x1F6E, 'M', u'ὦ'), + (0x1F6F, 'M', u'ὧ'), + (0x1F70, 'V'), + (0x1F71, 'M', u'ά'), + (0x1F72, 'V'), + (0x1F73, 'M', u'έ'), + (0x1F74, 'V'), + (0x1F75, 'M', u'ή'), + (0x1F76, 'V'), + (0x1F77, 'M', u'ί'), + (0x1F78, 'V'), + (0x1F79, 'M', u'ό'), + (0x1F7A, 'V'), + (0x1F7B, 'M', u'ύ'), + (0x1F7C, 'V'), + (0x1F7D, 'M', u'ώ'), + (0x1F7E, 'X'), + (0x1F80, 'M', u'ἀι'), + (0x1F81, 'M', u'ἁι'), + (0x1F82, 'M', u'ἂι'), + (0x1F83, 'M', u'ἃι'), + (0x1F84, 'M', u'ἄι'), + (0x1F85, 'M', u'ἅι'), + (0x1F86, 'M', u'ἆι'), + (0x1F87, 'M', u'ἇι'), + (0x1F88, 'M', u'ἀι'), + (0x1F89, 'M', u'ἁι'), + (0x1F8A, 'M', u'ἂι'), + (0x1F8B, 'M', u'ἃι'), + (0x1F8C, 'M', u'ἄι'), + (0x1F8D, 'M', u'ἅι'), + (0x1F8E, 'M', u'ἆι'), + (0x1F8F, 'M', u'ἇι'), + (0x1F90, 'M', u'ἠι'), + (0x1F91, 'M', u'ἡι'), + (0x1F92, 'M', u'ἢι'), + (0x1F93, 'M', u'ἣι'), + (0x1F94, 'M', u'ἤι'), + (0x1F95, 'M', u'ἥι'), + (0x1F96, 'M', u'ἦι'), + (0x1F97, 'M', u'ἧι'), + (0x1F98, 'M', u'ἠι'), + (0x1F99, 'M', u'ἡι'), + (0x1F9A, 'M', u'ἢι'), + (0x1F9B, 'M', u'ἣι'), + (0x1F9C, 'M', u'ἤι'), + (0x1F9D, 'M', u'ἥι'), + (0x1F9E, 'M', u'ἦι'), + (0x1F9F, 'M', u'ἧι'), + (0x1FA0, 'M', u'ὠι'), + (0x1FA1, 'M', u'ὡι'), + (0x1FA2, 'M', u'ὢι'), + (0x1FA3, 'M', u'ὣι'), + ] + +def _seg_20(): + return [ + (0x1FA4, 'M', u'ὤι'), + (0x1FA5, 'M', u'ὥι'), + (0x1FA6, 'M', u'ὦι'), + (0x1FA7, 'M', u'ὧι'), + (0x1FA8, 'M', u'ὠι'), + (0x1FA9, 'M', u'ὡι'), + (0x1FAA, 'M', u'ὢι'), + (0x1FAB, 'M', u'ὣι'), + (0x1FAC, 'M', u'ὤι'), + (0x1FAD, 'M', u'ὥι'), + (0x1FAE, 'M', u'ὦι'), + (0x1FAF, 'M', u'ὧι'), + (0x1FB0, 'V'), + (0x1FB2, 'M', u'ὰι'), + (0x1FB3, 'M', u'αι'), + (0x1FB4, 'M', u'άι'), + (0x1FB5, 'X'), + (0x1FB6, 'V'), + (0x1FB7, 'M', u'ᾶι'), + (0x1FB8, 'M', u'ᾰ'), + (0x1FB9, 'M', u'ᾱ'), + (0x1FBA, 'M', u'ὰ'), + (0x1FBB, 'M', u'ά'), + (0x1FBC, 'M', u'αι'), + (0x1FBD, '3', u' ̓'), + (0x1FBE, 'M', u'ι'), + (0x1FBF, '3', u' ̓'), + (0x1FC0, '3', u' ͂'), + (0x1FC1, '3', u' ̈͂'), + (0x1FC2, 'M', u'ὴι'), + (0x1FC3, 'M', u'ηι'), + (0x1FC4, 'M', u'ήι'), + (0x1FC5, 'X'), + (0x1FC6, 'V'), + (0x1FC7, 'M', u'ῆι'), + (0x1FC8, 'M', u'ὲ'), + (0x1FC9, 'M', u'έ'), + (0x1FCA, 'M', u'ὴ'), + (0x1FCB, 'M', u'ή'), + (0x1FCC, 'M', u'ηι'), + (0x1FCD, '3', u' ̓̀'), + (0x1FCE, '3', u' ̓́'), + (0x1FCF, '3', u' ̓͂'), + (0x1FD0, 'V'), + (0x1FD3, 'M', u'ΐ'), + (0x1FD4, 'X'), + (0x1FD6, 'V'), + (0x1FD8, 'M', u'ῐ'), + (0x1FD9, 'M', u'ῑ'), + (0x1FDA, 'M', u'ὶ'), + (0x1FDB, 'M', u'ί'), + (0x1FDC, 'X'), + (0x1FDD, '3', u' ̔̀'), + (0x1FDE, '3', u' ̔́'), + (0x1FDF, '3', u' ̔͂'), + (0x1FE0, 'V'), + (0x1FE3, 'M', u'ΰ'), + (0x1FE4, 'V'), + (0x1FE8, 'M', u'ῠ'), + (0x1FE9, 'M', u'ῡ'), + (0x1FEA, 'M', u'ὺ'), + (0x1FEB, 'M', u'ύ'), + (0x1FEC, 'M', u'ῥ'), + (0x1FED, '3', u' ̈̀'), + (0x1FEE, '3', u' ̈́'), + (0x1FEF, '3', u'`'), + (0x1FF0, 'X'), + (0x1FF2, 'M', u'ὼι'), + (0x1FF3, 'M', u'ωι'), + (0x1FF4, 'M', u'ώι'), + (0x1FF5, 'X'), + (0x1FF6, 'V'), + (0x1FF7, 'M', u'ῶι'), + (0x1FF8, 'M', u'ὸ'), + (0x1FF9, 'M', u'ό'), + (0x1FFA, 'M', u'ὼ'), + (0x1FFB, 'M', u'ώ'), + (0x1FFC, 'M', u'ωι'), + (0x1FFD, '3', u' ́'), + (0x1FFE, '3', u' ̔'), + (0x1FFF, 'X'), + (0x2000, '3', u' '), + (0x200B, 'I'), + (0x200C, 'D', u''), + (0x200E, 'X'), + (0x2010, 'V'), + (0x2011, 'M', u'‐'), + (0x2012, 'V'), + (0x2017, '3', u' ̳'), + (0x2018, 'V'), + (0x2024, 'X'), + (0x2027, 'V'), + (0x2028, 'X'), + (0x202F, '3', u' '), + (0x2030, 'V'), + (0x2033, 'M', u'′′'), + (0x2034, 'M', u'′′′'), + (0x2035, 'V'), + (0x2036, 'M', u'‵‵'), + (0x2037, 'M', u'‵‵‵'), + ] + +def _seg_21(): + return [ + (0x2038, 'V'), + (0x203C, '3', u'!!'), + (0x203D, 'V'), + (0x203E, '3', u' ̅'), + (0x203F, 'V'), + (0x2047, '3', u'??'), + (0x2048, '3', u'?!'), + (0x2049, '3', u'!?'), + (0x204A, 'V'), + (0x2057, 'M', u'′′′′'), + (0x2058, 'V'), + (0x205F, '3', u' '), + (0x2060, 'I'), + (0x2061, 'X'), + (0x2064, 'I'), + (0x2065, 'X'), + (0x2070, 'M', u'0'), + (0x2071, 'M', u'i'), + (0x2072, 'X'), + (0x2074, 'M', u'4'), + (0x2075, 'M', u'5'), + (0x2076, 'M', u'6'), + (0x2077, 'M', u'7'), + (0x2078, 'M', u'8'), + (0x2079, 'M', u'9'), + (0x207A, '3', u'+'), + (0x207B, 'M', u'−'), + (0x207C, '3', u'='), + (0x207D, '3', u'('), + (0x207E, '3', u')'), + (0x207F, 'M', u'n'), + (0x2080, 'M', u'0'), + (0x2081, 'M', u'1'), + (0x2082, 'M', u'2'), + (0x2083, 'M', u'3'), + (0x2084, 'M', u'4'), + (0x2085, 'M', u'5'), + (0x2086, 'M', u'6'), + (0x2087, 'M', u'7'), + (0x2088, 'M', u'8'), + (0x2089, 'M', u'9'), + (0x208A, '3', u'+'), + (0x208B, 'M', u'−'), + (0x208C, '3', u'='), + (0x208D, '3', u'('), + (0x208E, '3', u')'), + (0x208F, 'X'), + (0x2090, 'M', u'a'), + (0x2091, 'M', u'e'), + (0x2092, 'M', u'o'), + (0x2093, 'M', u'x'), + (0x2094, 'M', u'ə'), + (0x2095, 'M', u'h'), + (0x2096, 'M', u'k'), + (0x2097, 'M', u'l'), + (0x2098, 'M', u'm'), + (0x2099, 'M', u'n'), + (0x209A, 'M', u'p'), + (0x209B, 'M', u's'), + (0x209C, 'M', u't'), + (0x209D, 'X'), + (0x20A0, 'V'), + (0x20A8, 'M', u'rs'), + (0x20A9, 'V'), + (0x20C0, 'X'), + (0x20D0, 'V'), + (0x20F1, 'X'), + (0x2100, '3', u'a/c'), + (0x2101, '3', u'a/s'), + (0x2102, 'M', u'c'), + (0x2103, 'M', u'°c'), + (0x2104, 'V'), + (0x2105, '3', u'c/o'), + (0x2106, '3', u'c/u'), + (0x2107, 'M', u'ɛ'), + (0x2108, 'V'), + (0x2109, 'M', u'°f'), + (0x210A, 'M', u'g'), + (0x210B, 'M', u'h'), + (0x210F, 'M', u'ħ'), + (0x2110, 'M', u'i'), + (0x2112, 'M', u'l'), + (0x2114, 'V'), + (0x2115, 'M', u'n'), + (0x2116, 'M', u'no'), + (0x2117, 'V'), + (0x2119, 'M', u'p'), + (0x211A, 'M', u'q'), + (0x211B, 'M', u'r'), + (0x211E, 'V'), + (0x2120, 'M', u'sm'), + (0x2121, 'M', u'tel'), + (0x2122, 'M', u'tm'), + (0x2123, 'V'), + (0x2124, 'M', u'z'), + (0x2125, 'V'), + (0x2126, 'M', u'ω'), + (0x2127, 'V'), + (0x2128, 'M', u'z'), + (0x2129, 'V'), + ] + +def _seg_22(): + return [ + (0x212A, 'M', u'k'), + (0x212B, 'M', u'å'), + (0x212C, 'M', u'b'), + (0x212D, 'M', u'c'), + (0x212E, 'V'), + (0x212F, 'M', u'e'), + (0x2131, 'M', u'f'), + (0x2132, 'X'), + (0x2133, 'M', u'm'), + (0x2134, 'M', u'o'), + (0x2135, 'M', u'א'), + (0x2136, 'M', u'ב'), + (0x2137, 'M', u'ג'), + (0x2138, 'M', u'ד'), + (0x2139, 'M', u'i'), + (0x213A, 'V'), + (0x213B, 'M', u'fax'), + (0x213C, 'M', u'π'), + (0x213D, 'M', u'γ'), + (0x213F, 'M', u'π'), + (0x2140, 'M', u'∑'), + (0x2141, 'V'), + (0x2145, 'M', u'd'), + (0x2147, 'M', u'e'), + (0x2148, 'M', u'i'), + (0x2149, 'M', u'j'), + (0x214A, 'V'), + (0x2150, 'M', u'1⁄7'), + (0x2151, 'M', u'1⁄9'), + (0x2152, 'M', u'1⁄10'), + (0x2153, 'M', u'1⁄3'), + (0x2154, 'M', u'2⁄3'), + (0x2155, 'M', u'1⁄5'), + (0x2156, 'M', u'2⁄5'), + (0x2157, 'M', u'3⁄5'), + (0x2158, 'M', u'4⁄5'), + (0x2159, 'M', u'1⁄6'), + (0x215A, 'M', u'5⁄6'), + (0x215B, 'M', u'1⁄8'), + (0x215C, 'M', u'3⁄8'), + (0x215D, 'M', u'5⁄8'), + (0x215E, 'M', u'7⁄8'), + (0x215F, 'M', u'1⁄'), + (0x2160, 'M', u'i'), + (0x2161, 'M', u'ii'), + (0x2162, 'M', u'iii'), + (0x2163, 'M', u'iv'), + (0x2164, 'M', u'v'), + (0x2165, 'M', u'vi'), + (0x2166, 'M', u'vii'), + (0x2167, 'M', u'viii'), + (0x2168, 'M', u'ix'), + (0x2169, 'M', u'x'), + (0x216A, 'M', u'xi'), + (0x216B, 'M', u'xii'), + (0x216C, 'M', u'l'), + (0x216D, 'M', u'c'), + (0x216E, 'M', u'd'), + (0x216F, 'M', u'm'), + (0x2170, 'M', u'i'), + (0x2171, 'M', u'ii'), + (0x2172, 'M', u'iii'), + (0x2173, 'M', u'iv'), + (0x2174, 'M', u'v'), + (0x2175, 'M', u'vi'), + (0x2176, 'M', u'vii'), + (0x2177, 'M', u'viii'), + (0x2178, 'M', u'ix'), + (0x2179, 'M', u'x'), + (0x217A, 'M', u'xi'), + (0x217B, 'M', u'xii'), + (0x217C, 'M', u'l'), + (0x217D, 'M', u'c'), + (0x217E, 'M', u'd'), + (0x217F, 'M', u'm'), + (0x2180, 'V'), + (0x2183, 'X'), + (0x2184, 'V'), + (0x2189, 'M', u'0⁄3'), + (0x218A, 'V'), + (0x218C, 'X'), + (0x2190, 'V'), + (0x222C, 'M', u'∫∫'), + (0x222D, 'M', u'∫∫∫'), + (0x222E, 'V'), + (0x222F, 'M', u'∮∮'), + (0x2230, 'M', u'∮∮∮'), + (0x2231, 'V'), + (0x2260, '3'), + (0x2261, 'V'), + (0x226E, '3'), + (0x2270, 'V'), + (0x2329, 'M', u'〈'), + (0x232A, 'M', u'〉'), + (0x232B, 'V'), + (0x2427, 'X'), + (0x2440, 'V'), + (0x244B, 'X'), + (0x2460, 'M', u'1'), + (0x2461, 'M', u'2'), + ] + +def _seg_23(): + return [ + (0x2462, 'M', u'3'), + (0x2463, 'M', u'4'), + (0x2464, 'M', u'5'), + (0x2465, 'M', u'6'), + (0x2466, 'M', u'7'), + (0x2467, 'M', u'8'), + (0x2468, 'M', u'9'), + (0x2469, 'M', u'10'), + (0x246A, 'M', u'11'), + (0x246B, 'M', u'12'), + (0x246C, 'M', u'13'), + (0x246D, 'M', u'14'), + (0x246E, 'M', u'15'), + (0x246F, 'M', u'16'), + (0x2470, 'M', u'17'), + (0x2471, 'M', u'18'), + (0x2472, 'M', u'19'), + (0x2473, 'M', u'20'), + (0x2474, '3', u'(1)'), + (0x2475, '3', u'(2)'), + (0x2476, '3', u'(3)'), + (0x2477, '3', u'(4)'), + (0x2478, '3', u'(5)'), + (0x2479, '3', u'(6)'), + (0x247A, '3', u'(7)'), + (0x247B, '3', u'(8)'), + (0x247C, '3', u'(9)'), + (0x247D, '3', u'(10)'), + (0x247E, '3', u'(11)'), + (0x247F, '3', u'(12)'), + (0x2480, '3', u'(13)'), + (0x2481, '3', u'(14)'), + (0x2482, '3', u'(15)'), + (0x2483, '3', u'(16)'), + (0x2484, '3', u'(17)'), + (0x2485, '3', u'(18)'), + (0x2486, '3', u'(19)'), + (0x2487, '3', u'(20)'), + (0x2488, 'X'), + (0x249C, '3', u'(a)'), + (0x249D, '3', u'(b)'), + (0x249E, '3', u'(c)'), + (0x249F, '3', u'(d)'), + (0x24A0, '3', u'(e)'), + (0x24A1, '3', u'(f)'), + (0x24A2, '3', u'(g)'), + (0x24A3, '3', u'(h)'), + (0x24A4, '3', u'(i)'), + (0x24A5, '3', u'(j)'), + (0x24A6, '3', u'(k)'), + (0x24A7, '3', u'(l)'), + (0x24A8, '3', u'(m)'), + (0x24A9, '3', u'(n)'), + (0x24AA, '3', u'(o)'), + (0x24AB, '3', u'(p)'), + (0x24AC, '3', u'(q)'), + (0x24AD, '3', u'(r)'), + (0x24AE, '3', u'(s)'), + (0x24AF, '3', u'(t)'), + (0x24B0, '3', u'(u)'), + (0x24B1, '3', u'(v)'), + (0x24B2, '3', u'(w)'), + (0x24B3, '3', u'(x)'), + (0x24B4, '3', u'(y)'), + (0x24B5, '3', u'(z)'), + (0x24B6, 'M', u'a'), + (0x24B7, 'M', u'b'), + (0x24B8, 'M', u'c'), + (0x24B9, 'M', u'd'), + (0x24BA, 'M', u'e'), + (0x24BB, 'M', u'f'), + (0x24BC, 'M', u'g'), + (0x24BD, 'M', u'h'), + (0x24BE, 'M', u'i'), + (0x24BF, 'M', u'j'), + (0x24C0, 'M', u'k'), + (0x24C1, 'M', u'l'), + (0x24C2, 'M', u'm'), + (0x24C3, 'M', u'n'), + (0x24C4, 'M', u'o'), + (0x24C5, 'M', u'p'), + (0x24C6, 'M', u'q'), + (0x24C7, 'M', u'r'), + (0x24C8, 'M', u's'), + (0x24C9, 'M', u't'), + (0x24CA, 'M', u'u'), + (0x24CB, 'M', u'v'), + (0x24CC, 'M', u'w'), + (0x24CD, 'M', u'x'), + (0x24CE, 'M', u'y'), + (0x24CF, 'M', u'z'), + (0x24D0, 'M', u'a'), + (0x24D1, 'M', u'b'), + (0x24D2, 'M', u'c'), + (0x24D3, 'M', u'd'), + (0x24D4, 'M', u'e'), + (0x24D5, 'M', u'f'), + (0x24D6, 'M', u'g'), + (0x24D7, 'M', u'h'), + (0x24D8, 'M', u'i'), + ] + +def _seg_24(): + return [ + (0x24D9, 'M', u'j'), + (0x24DA, 'M', u'k'), + (0x24DB, 'M', u'l'), + (0x24DC, 'M', u'm'), + (0x24DD, 'M', u'n'), + (0x24DE, 'M', u'o'), + (0x24DF, 'M', u'p'), + (0x24E0, 'M', u'q'), + (0x24E1, 'M', u'r'), + (0x24E2, 'M', u's'), + (0x24E3, 'M', u't'), + (0x24E4, 'M', u'u'), + (0x24E5, 'M', u'v'), + (0x24E6, 'M', u'w'), + (0x24E7, 'M', u'x'), + (0x24E8, 'M', u'y'), + (0x24E9, 'M', u'z'), + (0x24EA, 'M', u'0'), + (0x24EB, 'V'), + (0x2A0C, 'M', u'∫∫∫∫'), + (0x2A0D, 'V'), + (0x2A74, '3', u'::='), + (0x2A75, '3', u'=='), + (0x2A76, '3', u'==='), + (0x2A77, 'V'), + (0x2ADC, 'M', u'⫝̸'), + (0x2ADD, 'V'), + (0x2B74, 'X'), + (0x2B76, 'V'), + (0x2B96, 'X'), + (0x2B98, 'V'), + (0x2BC9, 'X'), + (0x2BCA, 'V'), + (0x2BFF, 'X'), + (0x2C00, 'M', u'ⰰ'), + (0x2C01, 'M', u'ⰱ'), + (0x2C02, 'M', u'ⰲ'), + (0x2C03, 'M', u'ⰳ'), + (0x2C04, 'M', u'ⰴ'), + (0x2C05, 'M', u'ⰵ'), + (0x2C06, 'M', u'ⰶ'), + (0x2C07, 'M', u'ⰷ'), + (0x2C08, 'M', u'ⰸ'), + (0x2C09, 'M', u'ⰹ'), + (0x2C0A, 'M', u'ⰺ'), + (0x2C0B, 'M', u'ⰻ'), + (0x2C0C, 'M', u'ⰼ'), + (0x2C0D, 'M', u'ⰽ'), + (0x2C0E, 'M', u'ⰾ'), + (0x2C0F, 'M', u'ⰿ'), + (0x2C10, 'M', u'ⱀ'), + (0x2C11, 'M', u'ⱁ'), + (0x2C12, 'M', u'ⱂ'), + (0x2C13, 'M', u'ⱃ'), + (0x2C14, 'M', u'ⱄ'), + (0x2C15, 'M', u'ⱅ'), + (0x2C16, 'M', u'ⱆ'), + (0x2C17, 'M', u'ⱇ'), + (0x2C18, 'M', u'ⱈ'), + (0x2C19, 'M', u'ⱉ'), + (0x2C1A, 'M', u'ⱊ'), + (0x2C1B, 'M', u'ⱋ'), + (0x2C1C, 'M', u'ⱌ'), + (0x2C1D, 'M', u'ⱍ'), + (0x2C1E, 'M', u'ⱎ'), + (0x2C1F, 'M', u'ⱏ'), + (0x2C20, 'M', u'ⱐ'), + (0x2C21, 'M', u'ⱑ'), + (0x2C22, 'M', u'ⱒ'), + (0x2C23, 'M', u'ⱓ'), + (0x2C24, 'M', u'ⱔ'), + (0x2C25, 'M', u'ⱕ'), + (0x2C26, 'M', u'ⱖ'), + (0x2C27, 'M', u'ⱗ'), + (0x2C28, 'M', u'ⱘ'), + (0x2C29, 'M', u'ⱙ'), + (0x2C2A, 'M', u'ⱚ'), + (0x2C2B, 'M', u'ⱛ'), + (0x2C2C, 'M', u'ⱜ'), + (0x2C2D, 'M', u'ⱝ'), + (0x2C2E, 'M', u'ⱞ'), + (0x2C2F, 'X'), + (0x2C30, 'V'), + (0x2C5F, 'X'), + (0x2C60, 'M', u'ⱡ'), + (0x2C61, 'V'), + (0x2C62, 'M', u'ɫ'), + (0x2C63, 'M', u'ᵽ'), + (0x2C64, 'M', u'ɽ'), + (0x2C65, 'V'), + (0x2C67, 'M', u'ⱨ'), + (0x2C68, 'V'), + (0x2C69, 'M', u'ⱪ'), + (0x2C6A, 'V'), + (0x2C6B, 'M', u'ⱬ'), + (0x2C6C, 'V'), + (0x2C6D, 'M', u'ɑ'), + (0x2C6E, 'M', u'ɱ'), + (0x2C6F, 'M', u'ɐ'), + (0x2C70, 'M', u'ɒ'), + ] + +def _seg_25(): + return [ + (0x2C71, 'V'), + (0x2C72, 'M', u'ⱳ'), + (0x2C73, 'V'), + (0x2C75, 'M', u'ⱶ'), + (0x2C76, 'V'), + (0x2C7C, 'M', u'j'), + (0x2C7D, 'M', u'v'), + (0x2C7E, 'M', u'ȿ'), + (0x2C7F, 'M', u'ɀ'), + (0x2C80, 'M', u'ⲁ'), + (0x2C81, 'V'), + (0x2C82, 'M', u'ⲃ'), + (0x2C83, 'V'), + (0x2C84, 'M', u'ⲅ'), + (0x2C85, 'V'), + (0x2C86, 'M', u'ⲇ'), + (0x2C87, 'V'), + (0x2C88, 'M', u'ⲉ'), + (0x2C89, 'V'), + (0x2C8A, 'M', u'ⲋ'), + (0x2C8B, 'V'), + (0x2C8C, 'M', u'ⲍ'), + (0x2C8D, 'V'), + (0x2C8E, 'M', u'ⲏ'), + (0x2C8F, 'V'), + (0x2C90, 'M', u'ⲑ'), + (0x2C91, 'V'), + (0x2C92, 'M', u'ⲓ'), + (0x2C93, 'V'), + (0x2C94, 'M', u'ⲕ'), + (0x2C95, 'V'), + (0x2C96, 'M', u'ⲗ'), + (0x2C97, 'V'), + (0x2C98, 'M', u'ⲙ'), + (0x2C99, 'V'), + (0x2C9A, 'M', u'ⲛ'), + (0x2C9B, 'V'), + (0x2C9C, 'M', u'ⲝ'), + (0x2C9D, 'V'), + (0x2C9E, 'M', u'ⲟ'), + (0x2C9F, 'V'), + (0x2CA0, 'M', u'ⲡ'), + (0x2CA1, 'V'), + (0x2CA2, 'M', u'ⲣ'), + (0x2CA3, 'V'), + (0x2CA4, 'M', u'ⲥ'), + (0x2CA5, 'V'), + (0x2CA6, 'M', u'ⲧ'), + (0x2CA7, 'V'), + (0x2CA8, 'M', u'ⲩ'), + (0x2CA9, 'V'), + (0x2CAA, 'M', u'ⲫ'), + (0x2CAB, 'V'), + (0x2CAC, 'M', u'ⲭ'), + (0x2CAD, 'V'), + (0x2CAE, 'M', u'ⲯ'), + (0x2CAF, 'V'), + (0x2CB0, 'M', u'ⲱ'), + (0x2CB1, 'V'), + (0x2CB2, 'M', u'ⲳ'), + (0x2CB3, 'V'), + (0x2CB4, 'M', u'ⲵ'), + (0x2CB5, 'V'), + (0x2CB6, 'M', u'ⲷ'), + (0x2CB7, 'V'), + (0x2CB8, 'M', u'ⲹ'), + (0x2CB9, 'V'), + (0x2CBA, 'M', u'ⲻ'), + (0x2CBB, 'V'), + (0x2CBC, 'M', u'ⲽ'), + (0x2CBD, 'V'), + (0x2CBE, 'M', u'ⲿ'), + (0x2CBF, 'V'), + (0x2CC0, 'M', u'ⳁ'), + (0x2CC1, 'V'), + (0x2CC2, 'M', u'ⳃ'), + (0x2CC3, 'V'), + (0x2CC4, 'M', u'ⳅ'), + (0x2CC5, 'V'), + (0x2CC6, 'M', u'ⳇ'), + (0x2CC7, 'V'), + (0x2CC8, 'M', u'ⳉ'), + (0x2CC9, 'V'), + (0x2CCA, 'M', u'ⳋ'), + (0x2CCB, 'V'), + (0x2CCC, 'M', u'ⳍ'), + (0x2CCD, 'V'), + (0x2CCE, 'M', u'ⳏ'), + (0x2CCF, 'V'), + (0x2CD0, 'M', u'ⳑ'), + (0x2CD1, 'V'), + (0x2CD2, 'M', u'ⳓ'), + (0x2CD3, 'V'), + (0x2CD4, 'M', u'ⳕ'), + (0x2CD5, 'V'), + (0x2CD6, 'M', u'ⳗ'), + (0x2CD7, 'V'), + (0x2CD8, 'M', u'ⳙ'), + (0x2CD9, 'V'), + (0x2CDA, 'M', u'ⳛ'), + ] + +def _seg_26(): + return [ + (0x2CDB, 'V'), + (0x2CDC, 'M', u'ⳝ'), + (0x2CDD, 'V'), + (0x2CDE, 'M', u'ⳟ'), + (0x2CDF, 'V'), + (0x2CE0, 'M', u'ⳡ'), + (0x2CE1, 'V'), + (0x2CE2, 'M', u'ⳣ'), + (0x2CE3, 'V'), + (0x2CEB, 'M', u'ⳬ'), + (0x2CEC, 'V'), + (0x2CED, 'M', u'ⳮ'), + (0x2CEE, 'V'), + (0x2CF2, 'M', u'ⳳ'), + (0x2CF3, 'V'), + (0x2CF4, 'X'), + (0x2CF9, 'V'), + (0x2D26, 'X'), + (0x2D27, 'V'), + (0x2D28, 'X'), + (0x2D2D, 'V'), + (0x2D2E, 'X'), + (0x2D30, 'V'), + (0x2D68, 'X'), + (0x2D6F, 'M', u'ⵡ'), + (0x2D70, 'V'), + (0x2D71, 'X'), + (0x2D7F, 'V'), + (0x2D97, 'X'), + (0x2DA0, 'V'), + (0x2DA7, 'X'), + (0x2DA8, 'V'), + (0x2DAF, 'X'), + (0x2DB0, 'V'), + (0x2DB7, 'X'), + (0x2DB8, 'V'), + (0x2DBF, 'X'), + (0x2DC0, 'V'), + (0x2DC7, 'X'), + (0x2DC8, 'V'), + (0x2DCF, 'X'), + (0x2DD0, 'V'), + (0x2DD7, 'X'), + (0x2DD8, 'V'), + (0x2DDF, 'X'), + (0x2DE0, 'V'), + (0x2E4F, 'X'), + (0x2E80, 'V'), + (0x2E9A, 'X'), + (0x2E9B, 'V'), + (0x2E9F, 'M', u'母'), + (0x2EA0, 'V'), + (0x2EF3, 'M', u'龟'), + (0x2EF4, 'X'), + (0x2F00, 'M', u'一'), + (0x2F01, 'M', u'丨'), + (0x2F02, 'M', u'丶'), + (0x2F03, 'M', u'丿'), + (0x2F04, 'M', u'乙'), + (0x2F05, 'M', u'亅'), + (0x2F06, 'M', u'二'), + (0x2F07, 'M', u'亠'), + (0x2F08, 'M', u'人'), + (0x2F09, 'M', u'儿'), + (0x2F0A, 'M', u'入'), + (0x2F0B, 'M', u'八'), + (0x2F0C, 'M', u'冂'), + (0x2F0D, 'M', u'冖'), + (0x2F0E, 'M', u'冫'), + (0x2F0F, 'M', u'几'), + (0x2F10, 'M', u'凵'), + (0x2F11, 'M', u'刀'), + (0x2F12, 'M', u'力'), + (0x2F13, 'M', u'勹'), + (0x2F14, 'M', u'匕'), + (0x2F15, 'M', u'匚'), + (0x2F16, 'M', u'匸'), + (0x2F17, 'M', u'十'), + (0x2F18, 'M', u'卜'), + (0x2F19, 'M', u'卩'), + (0x2F1A, 'M', u'厂'), + (0x2F1B, 'M', u'厶'), + (0x2F1C, 'M', u'又'), + (0x2F1D, 'M', u'口'), + (0x2F1E, 'M', u'囗'), + (0x2F1F, 'M', u'土'), + (0x2F20, 'M', u'士'), + (0x2F21, 'M', u'夂'), + (0x2F22, 'M', u'夊'), + (0x2F23, 'M', u'夕'), + (0x2F24, 'M', u'大'), + (0x2F25, 'M', u'女'), + (0x2F26, 'M', u'子'), + (0x2F27, 'M', u'宀'), + (0x2F28, 'M', u'寸'), + (0x2F29, 'M', u'小'), + (0x2F2A, 'M', u'尢'), + (0x2F2B, 'M', u'尸'), + (0x2F2C, 'M', u'屮'), + (0x2F2D, 'M', u'山'), + ] + +def _seg_27(): + return [ + (0x2F2E, 'M', u'巛'), + (0x2F2F, 'M', u'工'), + (0x2F30, 'M', u'己'), + (0x2F31, 'M', u'巾'), + (0x2F32, 'M', u'干'), + (0x2F33, 'M', u'幺'), + (0x2F34, 'M', u'广'), + (0x2F35, 'M', u'廴'), + (0x2F36, 'M', u'廾'), + (0x2F37, 'M', u'弋'), + (0x2F38, 'M', u'弓'), + (0x2F39, 'M', u'彐'), + (0x2F3A, 'M', u'彡'), + (0x2F3B, 'M', u'彳'), + (0x2F3C, 'M', u'心'), + (0x2F3D, 'M', u'戈'), + (0x2F3E, 'M', u'戶'), + (0x2F3F, 'M', u'手'), + (0x2F40, 'M', u'支'), + (0x2F41, 'M', u'攴'), + (0x2F42, 'M', u'文'), + (0x2F43, 'M', u'斗'), + (0x2F44, 'M', u'斤'), + (0x2F45, 'M', u'方'), + (0x2F46, 'M', u'无'), + (0x2F47, 'M', u'日'), + (0x2F48, 'M', u'曰'), + (0x2F49, 'M', u'月'), + (0x2F4A, 'M', u'木'), + (0x2F4B, 'M', u'欠'), + (0x2F4C, 'M', u'止'), + (0x2F4D, 'M', u'歹'), + (0x2F4E, 'M', u'殳'), + (0x2F4F, 'M', u'毋'), + (0x2F50, 'M', u'比'), + (0x2F51, 'M', u'毛'), + (0x2F52, 'M', u'氏'), + (0x2F53, 'M', u'气'), + (0x2F54, 'M', u'水'), + (0x2F55, 'M', u'火'), + (0x2F56, 'M', u'爪'), + (0x2F57, 'M', u'父'), + (0x2F58, 'M', u'爻'), + (0x2F59, 'M', u'爿'), + (0x2F5A, 'M', u'片'), + (0x2F5B, 'M', u'牙'), + (0x2F5C, 'M', u'牛'), + (0x2F5D, 'M', u'犬'), + (0x2F5E, 'M', u'玄'), + (0x2F5F, 'M', u'玉'), + (0x2F60, 'M', u'瓜'), + (0x2F61, 'M', u'瓦'), + (0x2F62, 'M', u'甘'), + (0x2F63, 'M', u'生'), + (0x2F64, 'M', u'用'), + (0x2F65, 'M', u'田'), + (0x2F66, 'M', u'疋'), + (0x2F67, 'M', u'疒'), + (0x2F68, 'M', u'癶'), + (0x2F69, 'M', u'白'), + (0x2F6A, 'M', u'皮'), + (0x2F6B, 'M', u'皿'), + (0x2F6C, 'M', u'目'), + (0x2F6D, 'M', u'矛'), + (0x2F6E, 'M', u'矢'), + (0x2F6F, 'M', u'石'), + (0x2F70, 'M', u'示'), + (0x2F71, 'M', u'禸'), + (0x2F72, 'M', u'禾'), + (0x2F73, 'M', u'穴'), + (0x2F74, 'M', u'立'), + (0x2F75, 'M', u'竹'), + (0x2F76, 'M', u'米'), + (0x2F77, 'M', u'糸'), + (0x2F78, 'M', u'缶'), + (0x2F79, 'M', u'网'), + (0x2F7A, 'M', u'羊'), + (0x2F7B, 'M', u'羽'), + (0x2F7C, 'M', u'老'), + (0x2F7D, 'M', u'而'), + (0x2F7E, 'M', u'耒'), + (0x2F7F, 'M', u'耳'), + (0x2F80, 'M', u'聿'), + (0x2F81, 'M', u'肉'), + (0x2F82, 'M', u'臣'), + (0x2F83, 'M', u'自'), + (0x2F84, 'M', u'至'), + (0x2F85, 'M', u'臼'), + (0x2F86, 'M', u'舌'), + (0x2F87, 'M', u'舛'), + (0x2F88, 'M', u'舟'), + (0x2F89, 'M', u'艮'), + (0x2F8A, 'M', u'色'), + (0x2F8B, 'M', u'艸'), + (0x2F8C, 'M', u'虍'), + (0x2F8D, 'M', u'虫'), + (0x2F8E, 'M', u'血'), + (0x2F8F, 'M', u'行'), + (0x2F90, 'M', u'衣'), + (0x2F91, 'M', u'襾'), + ] + +def _seg_28(): + return [ + (0x2F92, 'M', u'見'), + (0x2F93, 'M', u'角'), + (0x2F94, 'M', u'言'), + (0x2F95, 'M', u'谷'), + (0x2F96, 'M', u'豆'), + (0x2F97, 'M', u'豕'), + (0x2F98, 'M', u'豸'), + (0x2F99, 'M', u'貝'), + (0x2F9A, 'M', u'赤'), + (0x2F9B, 'M', u'走'), + (0x2F9C, 'M', u'足'), + (0x2F9D, 'M', u'身'), + (0x2F9E, 'M', u'車'), + (0x2F9F, 'M', u'辛'), + (0x2FA0, 'M', u'辰'), + (0x2FA1, 'M', u'辵'), + (0x2FA2, 'M', u'邑'), + (0x2FA3, 'M', u'酉'), + (0x2FA4, 'M', u'釆'), + (0x2FA5, 'M', u'里'), + (0x2FA6, 'M', u'金'), + (0x2FA7, 'M', u'長'), + (0x2FA8, 'M', u'門'), + (0x2FA9, 'M', u'阜'), + (0x2FAA, 'M', u'隶'), + (0x2FAB, 'M', u'隹'), + (0x2FAC, 'M', u'雨'), + (0x2FAD, 'M', u'靑'), + (0x2FAE, 'M', u'非'), + (0x2FAF, 'M', u'面'), + (0x2FB0, 'M', u'革'), + (0x2FB1, 'M', u'韋'), + (0x2FB2, 'M', u'韭'), + (0x2FB3, 'M', u'音'), + (0x2FB4, 'M', u'頁'), + (0x2FB5, 'M', u'風'), + (0x2FB6, 'M', u'飛'), + (0x2FB7, 'M', u'食'), + (0x2FB8, 'M', u'首'), + (0x2FB9, 'M', u'香'), + (0x2FBA, 'M', u'馬'), + (0x2FBB, 'M', u'骨'), + (0x2FBC, 'M', u'高'), + (0x2FBD, 'M', u'髟'), + (0x2FBE, 'M', u'鬥'), + (0x2FBF, 'M', u'鬯'), + (0x2FC0, 'M', u'鬲'), + (0x2FC1, 'M', u'鬼'), + (0x2FC2, 'M', u'魚'), + (0x2FC3, 'M', u'鳥'), + (0x2FC4, 'M', u'鹵'), + (0x2FC5, 'M', u'鹿'), + (0x2FC6, 'M', u'麥'), + (0x2FC7, 'M', u'麻'), + (0x2FC8, 'M', u'黃'), + (0x2FC9, 'M', u'黍'), + (0x2FCA, 'M', u'黑'), + (0x2FCB, 'M', u'黹'), + (0x2FCC, 'M', u'黽'), + (0x2FCD, 'M', u'鼎'), + (0x2FCE, 'M', u'鼓'), + (0x2FCF, 'M', u'鼠'), + (0x2FD0, 'M', u'鼻'), + (0x2FD1, 'M', u'齊'), + (0x2FD2, 'M', u'齒'), + (0x2FD3, 'M', u'龍'), + (0x2FD4, 'M', u'龜'), + (0x2FD5, 'M', u'龠'), + (0x2FD6, 'X'), + (0x3000, '3', u' '), + (0x3001, 'V'), + (0x3002, 'M', u'.'), + (0x3003, 'V'), + (0x3036, 'M', u'〒'), + (0x3037, 'V'), + (0x3038, 'M', u'十'), + (0x3039, 'M', u'卄'), + (0x303A, 'M', u'卅'), + (0x303B, 'V'), + (0x3040, 'X'), + (0x3041, 'V'), + (0x3097, 'X'), + (0x3099, 'V'), + (0x309B, '3', u' ゙'), + (0x309C, '3', u' ゚'), + (0x309D, 'V'), + (0x309F, 'M', u'より'), + (0x30A0, 'V'), + (0x30FF, 'M', u'コト'), + (0x3100, 'X'), + (0x3105, 'V'), + (0x3130, 'X'), + (0x3131, 'M', u'ᄀ'), + (0x3132, 'M', u'ᄁ'), + (0x3133, 'M', u'ᆪ'), + (0x3134, 'M', u'ᄂ'), + (0x3135, 'M', u'ᆬ'), + (0x3136, 'M', u'ᆭ'), + (0x3137, 'M', u'ᄃ'), + (0x3138, 'M', u'ᄄ'), + ] + +def _seg_29(): + return [ + (0x3139, 'M', u'ᄅ'), + (0x313A, 'M', u'ᆰ'), + (0x313B, 'M', u'ᆱ'), + (0x313C, 'M', u'ᆲ'), + (0x313D, 'M', u'ᆳ'), + (0x313E, 'M', u'ᆴ'), + (0x313F, 'M', u'ᆵ'), + (0x3140, 'M', u'ᄚ'), + (0x3141, 'M', u'ᄆ'), + (0x3142, 'M', u'ᄇ'), + (0x3143, 'M', u'ᄈ'), + (0x3144, 'M', u'ᄡ'), + (0x3145, 'M', u'ᄉ'), + (0x3146, 'M', u'ᄊ'), + (0x3147, 'M', u'ᄋ'), + (0x3148, 'M', u'ᄌ'), + (0x3149, 'M', u'ᄍ'), + (0x314A, 'M', u'ᄎ'), + (0x314B, 'M', u'ᄏ'), + (0x314C, 'M', u'ᄐ'), + (0x314D, 'M', u'ᄑ'), + (0x314E, 'M', u'ᄒ'), + (0x314F, 'M', u'ᅡ'), + (0x3150, 'M', u'ᅢ'), + (0x3151, 'M', u'ᅣ'), + (0x3152, 'M', u'ᅤ'), + (0x3153, 'M', u'ᅥ'), + (0x3154, 'M', u'ᅦ'), + (0x3155, 'M', u'ᅧ'), + (0x3156, 'M', u'ᅨ'), + (0x3157, 'M', u'ᅩ'), + (0x3158, 'M', u'ᅪ'), + (0x3159, 'M', u'ᅫ'), + (0x315A, 'M', u'ᅬ'), + (0x315B, 'M', u'ᅭ'), + (0x315C, 'M', u'ᅮ'), + (0x315D, 'M', u'ᅯ'), + (0x315E, 'M', u'ᅰ'), + (0x315F, 'M', u'ᅱ'), + (0x3160, 'M', u'ᅲ'), + (0x3161, 'M', u'ᅳ'), + (0x3162, 'M', u'ᅴ'), + (0x3163, 'M', u'ᅵ'), + (0x3164, 'X'), + (0x3165, 'M', u'ᄔ'), + (0x3166, 'M', u'ᄕ'), + (0x3167, 'M', u'ᇇ'), + (0x3168, 'M', u'ᇈ'), + (0x3169, 'M', u'ᇌ'), + (0x316A, 'M', u'ᇎ'), + (0x316B, 'M', u'ᇓ'), + (0x316C, 'M', u'ᇗ'), + (0x316D, 'M', u'ᇙ'), + (0x316E, 'M', u'ᄜ'), + (0x316F, 'M', u'ᇝ'), + (0x3170, 'M', u'ᇟ'), + (0x3171, 'M', u'ᄝ'), + (0x3172, 'M', u'ᄞ'), + (0x3173, 'M', u'ᄠ'), + (0x3174, 'M', u'ᄢ'), + (0x3175, 'M', u'ᄣ'), + (0x3176, 'M', u'ᄧ'), + (0x3177, 'M', u'ᄩ'), + (0x3178, 'M', u'ᄫ'), + (0x3179, 'M', u'ᄬ'), + (0x317A, 'M', u'ᄭ'), + (0x317B, 'M', u'ᄮ'), + (0x317C, 'M', u'ᄯ'), + (0x317D, 'M', u'ᄲ'), + (0x317E, 'M', u'ᄶ'), + (0x317F, 'M', u'ᅀ'), + (0x3180, 'M', u'ᅇ'), + (0x3181, 'M', u'ᅌ'), + (0x3182, 'M', u'ᇱ'), + (0x3183, 'M', u'ᇲ'), + (0x3184, 'M', u'ᅗ'), + (0x3185, 'M', u'ᅘ'), + (0x3186, 'M', u'ᅙ'), + (0x3187, 'M', u'ᆄ'), + (0x3188, 'M', u'ᆅ'), + (0x3189, 'M', u'ᆈ'), + (0x318A, 'M', u'ᆑ'), + (0x318B, 'M', u'ᆒ'), + (0x318C, 'M', u'ᆔ'), + (0x318D, 'M', u'ᆞ'), + (0x318E, 'M', u'ᆡ'), + (0x318F, 'X'), + (0x3190, 'V'), + (0x3192, 'M', u'一'), + (0x3193, 'M', u'二'), + (0x3194, 'M', u'三'), + (0x3195, 'M', u'四'), + (0x3196, 'M', u'上'), + (0x3197, 'M', u'中'), + (0x3198, 'M', u'下'), + (0x3199, 'M', u'甲'), + (0x319A, 'M', u'乙'), + (0x319B, 'M', u'丙'), + (0x319C, 'M', u'丁'), + (0x319D, 'M', u'天'), + ] + +def _seg_30(): + return [ + (0x319E, 'M', u'地'), + (0x319F, 'M', u'人'), + (0x31A0, 'V'), + (0x31BB, 'X'), + (0x31C0, 'V'), + (0x31E4, 'X'), + (0x31F0, 'V'), + (0x3200, '3', u'(ᄀ)'), + (0x3201, '3', u'(ᄂ)'), + (0x3202, '3', u'(ᄃ)'), + (0x3203, '3', u'(ᄅ)'), + (0x3204, '3', u'(ᄆ)'), + (0x3205, '3', u'(ᄇ)'), + (0x3206, '3', u'(ᄉ)'), + (0x3207, '3', u'(ᄋ)'), + (0x3208, '3', u'(ᄌ)'), + (0x3209, '3', u'(ᄎ)'), + (0x320A, '3', u'(ᄏ)'), + (0x320B, '3', u'(ᄐ)'), + (0x320C, '3', u'(ᄑ)'), + (0x320D, '3', u'(ᄒ)'), + (0x320E, '3', u'(가)'), + (0x320F, '3', u'(나)'), + (0x3210, '3', u'(다)'), + (0x3211, '3', u'(라)'), + (0x3212, '3', u'(마)'), + (0x3213, '3', u'(바)'), + (0x3214, '3', u'(사)'), + (0x3215, '3', u'(아)'), + (0x3216, '3', u'(자)'), + (0x3217, '3', u'(차)'), + (0x3218, '3', u'(카)'), + (0x3219, '3', u'(타)'), + (0x321A, '3', u'(파)'), + (0x321B, '3', u'(하)'), + (0x321C, '3', u'(주)'), + (0x321D, '3', u'(오전)'), + (0x321E, '3', u'(오후)'), + (0x321F, 'X'), + (0x3220, '3', u'(一)'), + (0x3221, '3', u'(二)'), + (0x3222, '3', u'(三)'), + (0x3223, '3', u'(四)'), + (0x3224, '3', u'(五)'), + (0x3225, '3', u'(六)'), + (0x3226, '3', u'(七)'), + (0x3227, '3', u'(八)'), + (0x3228, '3', u'(九)'), + (0x3229, '3', u'(十)'), + (0x322A, '3', u'(月)'), + (0x322B, '3', u'(火)'), + (0x322C, '3', u'(水)'), + (0x322D, '3', u'(木)'), + (0x322E, '3', u'(金)'), + (0x322F, '3', u'(土)'), + (0x3230, '3', u'(日)'), + (0x3231, '3', u'(株)'), + (0x3232, '3', u'(有)'), + (0x3233, '3', u'(社)'), + (0x3234, '3', u'(名)'), + (0x3235, '3', u'(特)'), + (0x3236, '3', u'(財)'), + (0x3237, '3', u'(祝)'), + (0x3238, '3', u'(労)'), + (0x3239, '3', u'(代)'), + (0x323A, '3', u'(呼)'), + (0x323B, '3', u'(学)'), + (0x323C, '3', u'(監)'), + (0x323D, '3', u'(企)'), + (0x323E, '3', u'(資)'), + (0x323F, '3', u'(協)'), + (0x3240, '3', u'(祭)'), + (0x3241, '3', u'(休)'), + (0x3242, '3', u'(自)'), + (0x3243, '3', u'(至)'), + (0x3244, 'M', u'問'), + (0x3245, 'M', u'幼'), + (0x3246, 'M', u'文'), + (0x3247, 'M', u'箏'), + (0x3248, 'V'), + (0x3250, 'M', u'pte'), + (0x3251, 'M', u'21'), + (0x3252, 'M', u'22'), + (0x3253, 'M', u'23'), + (0x3254, 'M', u'24'), + (0x3255, 'M', u'25'), + (0x3256, 'M', u'26'), + (0x3257, 'M', u'27'), + (0x3258, 'M', u'28'), + (0x3259, 'M', u'29'), + (0x325A, 'M', u'30'), + (0x325B, 'M', u'31'), + (0x325C, 'M', u'32'), + (0x325D, 'M', u'33'), + (0x325E, 'M', u'34'), + (0x325F, 'M', u'35'), + (0x3260, 'M', u'ᄀ'), + (0x3261, 'M', u'ᄂ'), + (0x3262, 'M', u'ᄃ'), + (0x3263, 'M', u'ᄅ'), + ] + +def _seg_31(): + return [ + (0x3264, 'M', u'ᄆ'), + (0x3265, 'M', u'ᄇ'), + (0x3266, 'M', u'ᄉ'), + (0x3267, 'M', u'ᄋ'), + (0x3268, 'M', u'ᄌ'), + (0x3269, 'M', u'ᄎ'), + (0x326A, 'M', u'ᄏ'), + (0x326B, 'M', u'ᄐ'), + (0x326C, 'M', u'ᄑ'), + (0x326D, 'M', u'ᄒ'), + (0x326E, 'M', u'가'), + (0x326F, 'M', u'나'), + (0x3270, 'M', u'다'), + (0x3271, 'M', u'라'), + (0x3272, 'M', u'마'), + (0x3273, 'M', u'바'), + (0x3274, 'M', u'사'), + (0x3275, 'M', u'아'), + (0x3276, 'M', u'자'), + (0x3277, 'M', u'차'), + (0x3278, 'M', u'카'), + (0x3279, 'M', u'타'), + (0x327A, 'M', u'파'), + (0x327B, 'M', u'하'), + (0x327C, 'M', u'참고'), + (0x327D, 'M', u'주의'), + (0x327E, 'M', u'우'), + (0x327F, 'V'), + (0x3280, 'M', u'一'), + (0x3281, 'M', u'二'), + (0x3282, 'M', u'三'), + (0x3283, 'M', u'四'), + (0x3284, 'M', u'五'), + (0x3285, 'M', u'六'), + (0x3286, 'M', u'七'), + (0x3287, 'M', u'八'), + (0x3288, 'M', u'九'), + (0x3289, 'M', u'十'), + (0x328A, 'M', u'月'), + (0x328B, 'M', u'火'), + (0x328C, 'M', u'水'), + (0x328D, 'M', u'木'), + (0x328E, 'M', u'金'), + (0x328F, 'M', u'土'), + (0x3290, 'M', u'日'), + (0x3291, 'M', u'株'), + (0x3292, 'M', u'有'), + (0x3293, 'M', u'社'), + (0x3294, 'M', u'名'), + (0x3295, 'M', u'特'), + (0x3296, 'M', u'財'), + (0x3297, 'M', u'祝'), + (0x3298, 'M', u'労'), + (0x3299, 'M', u'秘'), + (0x329A, 'M', u'男'), + (0x329B, 'M', u'女'), + (0x329C, 'M', u'適'), + (0x329D, 'M', u'優'), + (0x329E, 'M', u'印'), + (0x329F, 'M', u'注'), + (0x32A0, 'M', u'項'), + (0x32A1, 'M', u'休'), + (0x32A2, 'M', u'写'), + (0x32A3, 'M', u'正'), + (0x32A4, 'M', u'上'), + (0x32A5, 'M', u'中'), + (0x32A6, 'M', u'下'), + (0x32A7, 'M', u'左'), + (0x32A8, 'M', u'右'), + (0x32A9, 'M', u'医'), + (0x32AA, 'M', u'宗'), + (0x32AB, 'M', u'学'), + (0x32AC, 'M', u'監'), + (0x32AD, 'M', u'企'), + (0x32AE, 'M', u'資'), + (0x32AF, 'M', u'協'), + (0x32B0, 'M', u'夜'), + (0x32B1, 'M', u'36'), + (0x32B2, 'M', u'37'), + (0x32B3, 'M', u'38'), + (0x32B4, 'M', u'39'), + (0x32B5, 'M', u'40'), + (0x32B6, 'M', u'41'), + (0x32B7, 'M', u'42'), + (0x32B8, 'M', u'43'), + (0x32B9, 'M', u'44'), + (0x32BA, 'M', u'45'), + (0x32BB, 'M', u'46'), + (0x32BC, 'M', u'47'), + (0x32BD, 'M', u'48'), + (0x32BE, 'M', u'49'), + (0x32BF, 'M', u'50'), + (0x32C0, 'M', u'1月'), + (0x32C1, 'M', u'2月'), + (0x32C2, 'M', u'3月'), + (0x32C3, 'M', u'4月'), + (0x32C4, 'M', u'5月'), + (0x32C5, 'M', u'6月'), + (0x32C6, 'M', u'7月'), + (0x32C7, 'M', u'8月'), + ] + +def _seg_32(): + return [ + (0x32C8, 'M', u'9月'), + (0x32C9, 'M', u'10月'), + (0x32CA, 'M', u'11月'), + (0x32CB, 'M', u'12月'), + (0x32CC, 'M', u'hg'), + (0x32CD, 'M', u'erg'), + (0x32CE, 'M', u'ev'), + (0x32CF, 'M', u'ltd'), + (0x32D0, 'M', u'ア'), + (0x32D1, 'M', u'イ'), + (0x32D2, 'M', u'ウ'), + (0x32D3, 'M', u'エ'), + (0x32D4, 'M', u'オ'), + (0x32D5, 'M', u'カ'), + (0x32D6, 'M', u'キ'), + (0x32D7, 'M', u'ク'), + (0x32D8, 'M', u'ケ'), + (0x32D9, 'M', u'コ'), + (0x32DA, 'M', u'サ'), + (0x32DB, 'M', u'シ'), + (0x32DC, 'M', u'ス'), + (0x32DD, 'M', u'セ'), + (0x32DE, 'M', u'ソ'), + (0x32DF, 'M', u'タ'), + (0x32E0, 'M', u'チ'), + (0x32E1, 'M', u'ツ'), + (0x32E2, 'M', u'テ'), + (0x32E3, 'M', u'ト'), + (0x32E4, 'M', u'ナ'), + (0x32E5, 'M', u'ニ'), + (0x32E6, 'M', u'ヌ'), + (0x32E7, 'M', u'ネ'), + (0x32E8, 'M', u'ノ'), + (0x32E9, 'M', u'ハ'), + (0x32EA, 'M', u'ヒ'), + (0x32EB, 'M', u'フ'), + (0x32EC, 'M', u'ヘ'), + (0x32ED, 'M', u'ホ'), + (0x32EE, 'M', u'マ'), + (0x32EF, 'M', u'ミ'), + (0x32F0, 'M', u'ム'), + (0x32F1, 'M', u'メ'), + (0x32F2, 'M', u'モ'), + (0x32F3, 'M', u'ヤ'), + (0x32F4, 'M', u'ユ'), + (0x32F5, 'M', u'ヨ'), + (0x32F6, 'M', u'ラ'), + (0x32F7, 'M', u'リ'), + (0x32F8, 'M', u'ル'), + (0x32F9, 'M', u'レ'), + (0x32FA, 'M', u'ロ'), + (0x32FB, 'M', u'ワ'), + (0x32FC, 'M', u'ヰ'), + (0x32FD, 'M', u'ヱ'), + (0x32FE, 'M', u'ヲ'), + (0x32FF, 'X'), + (0x3300, 'M', u'アパート'), + (0x3301, 'M', u'アルファ'), + (0x3302, 'M', u'アンペア'), + (0x3303, 'M', u'アール'), + (0x3304, 'M', u'イニング'), + (0x3305, 'M', u'インチ'), + (0x3306, 'M', u'ウォン'), + (0x3307, 'M', u'エスクード'), + (0x3308, 'M', u'エーカー'), + (0x3309, 'M', u'オンス'), + (0x330A, 'M', u'オーム'), + (0x330B, 'M', u'カイリ'), + (0x330C, 'M', u'カラット'), + (0x330D, 'M', u'カロリー'), + (0x330E, 'M', u'ガロン'), + (0x330F, 'M', u'ガンマ'), + (0x3310, 'M', u'ギガ'), + (0x3311, 'M', u'ギニー'), + (0x3312, 'M', u'キュリー'), + (0x3313, 'M', u'ギルダー'), + (0x3314, 'M', u'キロ'), + (0x3315, 'M', u'キログラム'), + (0x3316, 'M', u'キロメートル'), + (0x3317, 'M', u'キロワット'), + (0x3318, 'M', u'グラム'), + (0x3319, 'M', u'グラムトン'), + (0x331A, 'M', u'クルゼイロ'), + (0x331B, 'M', u'クローネ'), + (0x331C, 'M', u'ケース'), + (0x331D, 'M', u'コルナ'), + (0x331E, 'M', u'コーポ'), + (0x331F, 'M', u'サイクル'), + (0x3320, 'M', u'サンチーム'), + (0x3321, 'M', u'シリング'), + (0x3322, 'M', u'センチ'), + (0x3323, 'M', u'セント'), + (0x3324, 'M', u'ダース'), + (0x3325, 'M', u'デシ'), + (0x3326, 'M', u'ドル'), + (0x3327, 'M', u'トン'), + (0x3328, 'M', u'ナノ'), + (0x3329, 'M', u'ノット'), + (0x332A, 'M', u'ハイツ'), + (0x332B, 'M', u'パーセント'), + ] + +def _seg_33(): + return [ + (0x332C, 'M', u'パーツ'), + (0x332D, 'M', u'バーレル'), + (0x332E, 'M', u'ピアストル'), + (0x332F, 'M', u'ピクル'), + (0x3330, 'M', u'ピコ'), + (0x3331, 'M', u'ビル'), + (0x3332, 'M', u'ファラッド'), + (0x3333, 'M', u'フィート'), + (0x3334, 'M', u'ブッシェル'), + (0x3335, 'M', u'フラン'), + (0x3336, 'M', u'ヘクタール'), + (0x3337, 'M', u'ペソ'), + (0x3338, 'M', u'ペニヒ'), + (0x3339, 'M', u'ヘルツ'), + (0x333A, 'M', u'ペンス'), + (0x333B, 'M', u'ページ'), + (0x333C, 'M', u'ベータ'), + (0x333D, 'M', u'ポイント'), + (0x333E, 'M', u'ボルト'), + (0x333F, 'M', u'ホン'), + (0x3340, 'M', u'ポンド'), + (0x3341, 'M', u'ホール'), + (0x3342, 'M', u'ホーン'), + (0x3343, 'M', u'マイクロ'), + (0x3344, 'M', u'マイル'), + (0x3345, 'M', u'マッハ'), + (0x3346, 'M', u'マルク'), + (0x3347, 'M', u'マンション'), + (0x3348, 'M', u'ミクロン'), + (0x3349, 'M', u'ミリ'), + (0x334A, 'M', u'ミリバール'), + (0x334B, 'M', u'メガ'), + (0x334C, 'M', u'メガトン'), + (0x334D, 'M', u'メートル'), + (0x334E, 'M', u'ヤード'), + (0x334F, 'M', u'ヤール'), + (0x3350, 'M', u'ユアン'), + (0x3351, 'M', u'リットル'), + (0x3352, 'M', u'リラ'), + (0x3353, 'M', u'ルピー'), + (0x3354, 'M', u'ルーブル'), + (0x3355, 'M', u'レム'), + (0x3356, 'M', u'レントゲン'), + (0x3357, 'M', u'ワット'), + (0x3358, 'M', u'0点'), + (0x3359, 'M', u'1点'), + (0x335A, 'M', u'2点'), + (0x335B, 'M', u'3点'), + (0x335C, 'M', u'4点'), + (0x335D, 'M', u'5点'), + (0x335E, 'M', u'6点'), + (0x335F, 'M', u'7点'), + (0x3360, 'M', u'8点'), + (0x3361, 'M', u'9点'), + (0x3362, 'M', u'10点'), + (0x3363, 'M', u'11点'), + (0x3364, 'M', u'12点'), + (0x3365, 'M', u'13点'), + (0x3366, 'M', u'14点'), + (0x3367, 'M', u'15点'), + (0x3368, 'M', u'16点'), + (0x3369, 'M', u'17点'), + (0x336A, 'M', u'18点'), + (0x336B, 'M', u'19点'), + (0x336C, 'M', u'20点'), + (0x336D, 'M', u'21点'), + (0x336E, 'M', u'22点'), + (0x336F, 'M', u'23点'), + (0x3370, 'M', u'24点'), + (0x3371, 'M', u'hpa'), + (0x3372, 'M', u'da'), + (0x3373, 'M', u'au'), + (0x3374, 'M', u'bar'), + (0x3375, 'M', u'ov'), + (0x3376, 'M', u'pc'), + (0x3377, 'M', u'dm'), + (0x3378, 'M', u'dm2'), + (0x3379, 'M', u'dm3'), + (0x337A, 'M', u'iu'), + (0x337B, 'M', u'平成'), + (0x337C, 'M', u'昭和'), + (0x337D, 'M', u'大正'), + (0x337E, 'M', u'明治'), + (0x337F, 'M', u'株式会社'), + (0x3380, 'M', u'pa'), + (0x3381, 'M', u'na'), + (0x3382, 'M', u'μa'), + (0x3383, 'M', u'ma'), + (0x3384, 'M', u'ka'), + (0x3385, 'M', u'kb'), + (0x3386, 'M', u'mb'), + (0x3387, 'M', u'gb'), + (0x3388, 'M', u'cal'), + (0x3389, 'M', u'kcal'), + (0x338A, 'M', u'pf'), + (0x338B, 'M', u'nf'), + (0x338C, 'M', u'μf'), + (0x338D, 'M', u'μg'), + (0x338E, 'M', u'mg'), + (0x338F, 'M', u'kg'), + ] + +def _seg_34(): + return [ + (0x3390, 'M', u'hz'), + (0x3391, 'M', u'khz'), + (0x3392, 'M', u'mhz'), + (0x3393, 'M', u'ghz'), + (0x3394, 'M', u'thz'), + (0x3395, 'M', u'μl'), + (0x3396, 'M', u'ml'), + (0x3397, 'M', u'dl'), + (0x3398, 'M', u'kl'), + (0x3399, 'M', u'fm'), + (0x339A, 'M', u'nm'), + (0x339B, 'M', u'μm'), + (0x339C, 'M', u'mm'), + (0x339D, 'M', u'cm'), + (0x339E, 'M', u'km'), + (0x339F, 'M', u'mm2'), + (0x33A0, 'M', u'cm2'), + (0x33A1, 'M', u'm2'), + (0x33A2, 'M', u'km2'), + (0x33A3, 'M', u'mm3'), + (0x33A4, 'M', u'cm3'), + (0x33A5, 'M', u'm3'), + (0x33A6, 'M', u'km3'), + (0x33A7, 'M', u'm∕s'), + (0x33A8, 'M', u'm∕s2'), + (0x33A9, 'M', u'pa'), + (0x33AA, 'M', u'kpa'), + (0x33AB, 'M', u'mpa'), + (0x33AC, 'M', u'gpa'), + (0x33AD, 'M', u'rad'), + (0x33AE, 'M', u'rad∕s'), + (0x33AF, 'M', u'rad∕s2'), + (0x33B0, 'M', u'ps'), + (0x33B1, 'M', u'ns'), + (0x33B2, 'M', u'μs'), + (0x33B3, 'M', u'ms'), + (0x33B4, 'M', u'pv'), + (0x33B5, 'M', u'nv'), + (0x33B6, 'M', u'μv'), + (0x33B7, 'M', u'mv'), + (0x33B8, 'M', u'kv'), + (0x33B9, 'M', u'mv'), + (0x33BA, 'M', u'pw'), + (0x33BB, 'M', u'nw'), + (0x33BC, 'M', u'μw'), + (0x33BD, 'M', u'mw'), + (0x33BE, 'M', u'kw'), + (0x33BF, 'M', u'mw'), + (0x33C0, 'M', u'kω'), + (0x33C1, 'M', u'mω'), + (0x33C2, 'X'), + (0x33C3, 'M', u'bq'), + (0x33C4, 'M', u'cc'), + (0x33C5, 'M', u'cd'), + (0x33C6, 'M', u'c∕kg'), + (0x33C7, 'X'), + (0x33C8, 'M', u'db'), + (0x33C9, 'M', u'gy'), + (0x33CA, 'M', u'ha'), + (0x33CB, 'M', u'hp'), + (0x33CC, 'M', u'in'), + (0x33CD, 'M', u'kk'), + (0x33CE, 'M', u'km'), + (0x33CF, 'M', u'kt'), + (0x33D0, 'M', u'lm'), + (0x33D1, 'M', u'ln'), + (0x33D2, 'M', u'log'), + (0x33D3, 'M', u'lx'), + (0x33D4, 'M', u'mb'), + (0x33D5, 'M', u'mil'), + (0x33D6, 'M', u'mol'), + (0x33D7, 'M', u'ph'), + (0x33D8, 'X'), + (0x33D9, 'M', u'ppm'), + (0x33DA, 'M', u'pr'), + (0x33DB, 'M', u'sr'), + (0x33DC, 'M', u'sv'), + (0x33DD, 'M', u'wb'), + (0x33DE, 'M', u'v∕m'), + (0x33DF, 'M', u'a∕m'), + (0x33E0, 'M', u'1日'), + (0x33E1, 'M', u'2日'), + (0x33E2, 'M', u'3日'), + (0x33E3, 'M', u'4日'), + (0x33E4, 'M', u'5日'), + (0x33E5, 'M', u'6日'), + (0x33E6, 'M', u'7日'), + (0x33E7, 'M', u'8日'), + (0x33E8, 'M', u'9日'), + (0x33E9, 'M', u'10日'), + (0x33EA, 'M', u'11日'), + (0x33EB, 'M', u'12日'), + (0x33EC, 'M', u'13日'), + (0x33ED, 'M', u'14日'), + (0x33EE, 'M', u'15日'), + (0x33EF, 'M', u'16日'), + (0x33F0, 'M', u'17日'), + (0x33F1, 'M', u'18日'), + (0x33F2, 'M', u'19日'), + (0x33F3, 'M', u'20日'), + ] + +def _seg_35(): + return [ + (0x33F4, 'M', u'21日'), + (0x33F5, 'M', u'22日'), + (0x33F6, 'M', u'23日'), + (0x33F7, 'M', u'24日'), + (0x33F8, 'M', u'25日'), + (0x33F9, 'M', u'26日'), + (0x33FA, 'M', u'27日'), + (0x33FB, 'M', u'28日'), + (0x33FC, 'M', u'29日'), + (0x33FD, 'M', u'30日'), + (0x33FE, 'M', u'31日'), + (0x33FF, 'M', u'gal'), + (0x3400, 'V'), + (0x4DB6, 'X'), + (0x4DC0, 'V'), + (0x9FF0, 'X'), + (0xA000, 'V'), + (0xA48D, 'X'), + (0xA490, 'V'), + (0xA4C7, 'X'), + (0xA4D0, 'V'), + (0xA62C, 'X'), + (0xA640, 'M', u'ꙁ'), + (0xA641, 'V'), + (0xA642, 'M', u'ꙃ'), + (0xA643, 'V'), + (0xA644, 'M', u'ꙅ'), + (0xA645, 'V'), + (0xA646, 'M', u'ꙇ'), + (0xA647, 'V'), + (0xA648, 'M', u'ꙉ'), + (0xA649, 'V'), + (0xA64A, 'M', u'ꙋ'), + (0xA64B, 'V'), + (0xA64C, 'M', u'ꙍ'), + (0xA64D, 'V'), + (0xA64E, 'M', u'ꙏ'), + (0xA64F, 'V'), + (0xA650, 'M', u'ꙑ'), + (0xA651, 'V'), + (0xA652, 'M', u'ꙓ'), + (0xA653, 'V'), + (0xA654, 'M', u'ꙕ'), + (0xA655, 'V'), + (0xA656, 'M', u'ꙗ'), + (0xA657, 'V'), + (0xA658, 'M', u'ꙙ'), + (0xA659, 'V'), + (0xA65A, 'M', u'ꙛ'), + (0xA65B, 'V'), + (0xA65C, 'M', u'ꙝ'), + (0xA65D, 'V'), + (0xA65E, 'M', u'ꙟ'), + (0xA65F, 'V'), + (0xA660, 'M', u'ꙡ'), + (0xA661, 'V'), + (0xA662, 'M', u'ꙣ'), + (0xA663, 'V'), + (0xA664, 'M', u'ꙥ'), + (0xA665, 'V'), + (0xA666, 'M', u'ꙧ'), + (0xA667, 'V'), + (0xA668, 'M', u'ꙩ'), + (0xA669, 'V'), + (0xA66A, 'M', u'ꙫ'), + (0xA66B, 'V'), + (0xA66C, 'M', u'ꙭ'), + (0xA66D, 'V'), + (0xA680, 'M', u'ꚁ'), + (0xA681, 'V'), + (0xA682, 'M', u'ꚃ'), + (0xA683, 'V'), + (0xA684, 'M', u'ꚅ'), + (0xA685, 'V'), + (0xA686, 'M', u'ꚇ'), + (0xA687, 'V'), + (0xA688, 'M', u'ꚉ'), + (0xA689, 'V'), + (0xA68A, 'M', u'ꚋ'), + (0xA68B, 'V'), + (0xA68C, 'M', u'ꚍ'), + (0xA68D, 'V'), + (0xA68E, 'M', u'ꚏ'), + (0xA68F, 'V'), + (0xA690, 'M', u'ꚑ'), + (0xA691, 'V'), + (0xA692, 'M', u'ꚓ'), + (0xA693, 'V'), + (0xA694, 'M', u'ꚕ'), + (0xA695, 'V'), + (0xA696, 'M', u'ꚗ'), + (0xA697, 'V'), + (0xA698, 'M', u'ꚙ'), + (0xA699, 'V'), + (0xA69A, 'M', u'ꚛ'), + (0xA69B, 'V'), + (0xA69C, 'M', u'ъ'), + (0xA69D, 'M', u'ь'), + (0xA69E, 'V'), + (0xA6F8, 'X'), + ] + +def _seg_36(): + return [ + (0xA700, 'V'), + (0xA722, 'M', u'ꜣ'), + (0xA723, 'V'), + (0xA724, 'M', u'ꜥ'), + (0xA725, 'V'), + (0xA726, 'M', u'ꜧ'), + (0xA727, 'V'), + (0xA728, 'M', u'ꜩ'), + (0xA729, 'V'), + (0xA72A, 'M', u'ꜫ'), + (0xA72B, 'V'), + (0xA72C, 'M', u'ꜭ'), + (0xA72D, 'V'), + (0xA72E, 'M', u'ꜯ'), + (0xA72F, 'V'), + (0xA732, 'M', u'ꜳ'), + (0xA733, 'V'), + (0xA734, 'M', u'ꜵ'), + (0xA735, 'V'), + (0xA736, 'M', u'ꜷ'), + (0xA737, 'V'), + (0xA738, 'M', u'ꜹ'), + (0xA739, 'V'), + (0xA73A, 'M', u'ꜻ'), + (0xA73B, 'V'), + (0xA73C, 'M', u'ꜽ'), + (0xA73D, 'V'), + (0xA73E, 'M', u'ꜿ'), + (0xA73F, 'V'), + (0xA740, 'M', u'ꝁ'), + (0xA741, 'V'), + (0xA742, 'M', u'ꝃ'), + (0xA743, 'V'), + (0xA744, 'M', u'ꝅ'), + (0xA745, 'V'), + (0xA746, 'M', u'ꝇ'), + (0xA747, 'V'), + (0xA748, 'M', u'ꝉ'), + (0xA749, 'V'), + (0xA74A, 'M', u'ꝋ'), + (0xA74B, 'V'), + (0xA74C, 'M', u'ꝍ'), + (0xA74D, 'V'), + (0xA74E, 'M', u'ꝏ'), + (0xA74F, 'V'), + (0xA750, 'M', u'ꝑ'), + (0xA751, 'V'), + (0xA752, 'M', u'ꝓ'), + (0xA753, 'V'), + (0xA754, 'M', u'ꝕ'), + (0xA755, 'V'), + (0xA756, 'M', u'ꝗ'), + (0xA757, 'V'), + (0xA758, 'M', u'ꝙ'), + (0xA759, 'V'), + (0xA75A, 'M', u'ꝛ'), + (0xA75B, 'V'), + (0xA75C, 'M', u'ꝝ'), + (0xA75D, 'V'), + (0xA75E, 'M', u'ꝟ'), + (0xA75F, 'V'), + (0xA760, 'M', u'ꝡ'), + (0xA761, 'V'), + (0xA762, 'M', u'ꝣ'), + (0xA763, 'V'), + (0xA764, 'M', u'ꝥ'), + (0xA765, 'V'), + (0xA766, 'M', u'ꝧ'), + (0xA767, 'V'), + (0xA768, 'M', u'ꝩ'), + (0xA769, 'V'), + (0xA76A, 'M', u'ꝫ'), + (0xA76B, 'V'), + (0xA76C, 'M', u'ꝭ'), + (0xA76D, 'V'), + (0xA76E, 'M', u'ꝯ'), + (0xA76F, 'V'), + (0xA770, 'M', u'ꝯ'), + (0xA771, 'V'), + (0xA779, 'M', u'ꝺ'), + (0xA77A, 'V'), + (0xA77B, 'M', u'ꝼ'), + (0xA77C, 'V'), + (0xA77D, 'M', u'ᵹ'), + (0xA77E, 'M', u'ꝿ'), + (0xA77F, 'V'), + (0xA780, 'M', u'ꞁ'), + (0xA781, 'V'), + (0xA782, 'M', u'ꞃ'), + (0xA783, 'V'), + (0xA784, 'M', u'ꞅ'), + (0xA785, 'V'), + (0xA786, 'M', u'ꞇ'), + (0xA787, 'V'), + (0xA78B, 'M', u'ꞌ'), + (0xA78C, 'V'), + (0xA78D, 'M', u'ɥ'), + (0xA78E, 'V'), + (0xA790, 'M', u'ꞑ'), + (0xA791, 'V'), + ] + +def _seg_37(): + return [ + (0xA792, 'M', u'ꞓ'), + (0xA793, 'V'), + (0xA796, 'M', u'ꞗ'), + (0xA797, 'V'), + (0xA798, 'M', u'ꞙ'), + (0xA799, 'V'), + (0xA79A, 'M', u'ꞛ'), + (0xA79B, 'V'), + (0xA79C, 'M', u'ꞝ'), + (0xA79D, 'V'), + (0xA79E, 'M', u'ꞟ'), + (0xA79F, 'V'), + (0xA7A0, 'M', u'ꞡ'), + (0xA7A1, 'V'), + (0xA7A2, 'M', u'ꞣ'), + (0xA7A3, 'V'), + (0xA7A4, 'M', u'ꞥ'), + (0xA7A5, 'V'), + (0xA7A6, 'M', u'ꞧ'), + (0xA7A7, 'V'), + (0xA7A8, 'M', u'ꞩ'), + (0xA7A9, 'V'), + (0xA7AA, 'M', u'ɦ'), + (0xA7AB, 'M', u'ɜ'), + (0xA7AC, 'M', u'ɡ'), + (0xA7AD, 'M', u'ɬ'), + (0xA7AE, 'M', u'ɪ'), + (0xA7AF, 'V'), + (0xA7B0, 'M', u'ʞ'), + (0xA7B1, 'M', u'ʇ'), + (0xA7B2, 'M', u'ʝ'), + (0xA7B3, 'M', u'ꭓ'), + (0xA7B4, 'M', u'ꞵ'), + (0xA7B5, 'V'), + (0xA7B6, 'M', u'ꞷ'), + (0xA7B7, 'V'), + (0xA7B8, 'X'), + (0xA7B9, 'V'), + (0xA7BA, 'X'), + (0xA7F7, 'V'), + (0xA7F8, 'M', u'ħ'), + (0xA7F9, 'M', u'œ'), + (0xA7FA, 'V'), + (0xA82C, 'X'), + (0xA830, 'V'), + (0xA83A, 'X'), + (0xA840, 'V'), + (0xA878, 'X'), + (0xA880, 'V'), + (0xA8C6, 'X'), + (0xA8CE, 'V'), + (0xA8DA, 'X'), + (0xA8E0, 'V'), + (0xA954, 'X'), + (0xA95F, 'V'), + (0xA97D, 'X'), + (0xA980, 'V'), + (0xA9CE, 'X'), + (0xA9CF, 'V'), + (0xA9DA, 'X'), + (0xA9DE, 'V'), + (0xA9FF, 'X'), + (0xAA00, 'V'), + (0xAA37, 'X'), + (0xAA40, 'V'), + (0xAA4E, 'X'), + (0xAA50, 'V'), + (0xAA5A, 'X'), + (0xAA5C, 'V'), + (0xAAC3, 'X'), + (0xAADB, 'V'), + (0xAAF7, 'X'), + (0xAB01, 'V'), + (0xAB07, 'X'), + (0xAB09, 'V'), + (0xAB0F, 'X'), + (0xAB11, 'V'), + (0xAB17, 'X'), + (0xAB20, 'V'), + (0xAB27, 'X'), + (0xAB28, 'V'), + (0xAB2F, 'X'), + (0xAB30, 'V'), + (0xAB5C, 'M', u'ꜧ'), + (0xAB5D, 'M', u'ꬷ'), + (0xAB5E, 'M', u'ɫ'), + (0xAB5F, 'M', u'ꭒ'), + (0xAB60, 'V'), + (0xAB66, 'X'), + (0xAB70, 'M', u'Ꭰ'), + (0xAB71, 'M', u'Ꭱ'), + (0xAB72, 'M', u'Ꭲ'), + (0xAB73, 'M', u'Ꭳ'), + (0xAB74, 'M', u'Ꭴ'), + (0xAB75, 'M', u'Ꭵ'), + (0xAB76, 'M', u'Ꭶ'), + (0xAB77, 'M', u'Ꭷ'), + (0xAB78, 'M', u'Ꭸ'), + (0xAB79, 'M', u'Ꭹ'), + (0xAB7A, 'M', u'Ꭺ'), + ] + +def _seg_38(): + return [ + (0xAB7B, 'M', u'Ꭻ'), + (0xAB7C, 'M', u'Ꭼ'), + (0xAB7D, 'M', u'Ꭽ'), + (0xAB7E, 'M', u'Ꭾ'), + (0xAB7F, 'M', u'Ꭿ'), + (0xAB80, 'M', u'Ꮀ'), + (0xAB81, 'M', u'Ꮁ'), + (0xAB82, 'M', u'Ꮂ'), + (0xAB83, 'M', u'Ꮃ'), + (0xAB84, 'M', u'Ꮄ'), + (0xAB85, 'M', u'Ꮅ'), + (0xAB86, 'M', u'Ꮆ'), + (0xAB87, 'M', u'Ꮇ'), + (0xAB88, 'M', u'Ꮈ'), + (0xAB89, 'M', u'Ꮉ'), + (0xAB8A, 'M', u'Ꮊ'), + (0xAB8B, 'M', u'Ꮋ'), + (0xAB8C, 'M', u'Ꮌ'), + (0xAB8D, 'M', u'Ꮍ'), + (0xAB8E, 'M', u'Ꮎ'), + (0xAB8F, 'M', u'Ꮏ'), + (0xAB90, 'M', u'Ꮐ'), + (0xAB91, 'M', u'Ꮑ'), + (0xAB92, 'M', u'Ꮒ'), + (0xAB93, 'M', u'Ꮓ'), + (0xAB94, 'M', u'Ꮔ'), + (0xAB95, 'M', u'Ꮕ'), + (0xAB96, 'M', u'Ꮖ'), + (0xAB97, 'M', u'Ꮗ'), + (0xAB98, 'M', u'Ꮘ'), + (0xAB99, 'M', u'Ꮙ'), + (0xAB9A, 'M', u'Ꮚ'), + (0xAB9B, 'M', u'Ꮛ'), + (0xAB9C, 'M', u'Ꮜ'), + (0xAB9D, 'M', u'Ꮝ'), + (0xAB9E, 'M', u'Ꮞ'), + (0xAB9F, 'M', u'Ꮟ'), + (0xABA0, 'M', u'Ꮠ'), + (0xABA1, 'M', u'Ꮡ'), + (0xABA2, 'M', u'Ꮢ'), + (0xABA3, 'M', u'Ꮣ'), + (0xABA4, 'M', u'Ꮤ'), + (0xABA5, 'M', u'Ꮥ'), + (0xABA6, 'M', u'Ꮦ'), + (0xABA7, 'M', u'Ꮧ'), + (0xABA8, 'M', u'Ꮨ'), + (0xABA9, 'M', u'Ꮩ'), + (0xABAA, 'M', u'Ꮪ'), + (0xABAB, 'M', u'Ꮫ'), + (0xABAC, 'M', u'Ꮬ'), + (0xABAD, 'M', u'Ꮭ'), + (0xABAE, 'M', u'Ꮮ'), + (0xABAF, 'M', u'Ꮯ'), + (0xABB0, 'M', u'Ꮰ'), + (0xABB1, 'M', u'Ꮱ'), + (0xABB2, 'M', u'Ꮲ'), + (0xABB3, 'M', u'Ꮳ'), + (0xABB4, 'M', u'Ꮴ'), + (0xABB5, 'M', u'Ꮵ'), + (0xABB6, 'M', u'Ꮶ'), + (0xABB7, 'M', u'Ꮷ'), + (0xABB8, 'M', u'Ꮸ'), + (0xABB9, 'M', u'Ꮹ'), + (0xABBA, 'M', u'Ꮺ'), + (0xABBB, 'M', u'Ꮻ'), + (0xABBC, 'M', u'Ꮼ'), + (0xABBD, 'M', u'Ꮽ'), + (0xABBE, 'M', u'Ꮾ'), + (0xABBF, 'M', u'Ꮿ'), + (0xABC0, 'V'), + (0xABEE, 'X'), + (0xABF0, 'V'), + (0xABFA, 'X'), + (0xAC00, 'V'), + (0xD7A4, 'X'), + (0xD7B0, 'V'), + (0xD7C7, 'X'), + (0xD7CB, 'V'), + (0xD7FC, 'X'), + (0xF900, 'M', u'豈'), + (0xF901, 'M', u'更'), + (0xF902, 'M', u'車'), + (0xF903, 'M', u'賈'), + (0xF904, 'M', u'滑'), + (0xF905, 'M', u'串'), + (0xF906, 'M', u'句'), + (0xF907, 'M', u'龜'), + (0xF909, 'M', u'契'), + (0xF90A, 'M', u'金'), + (0xF90B, 'M', u'喇'), + (0xF90C, 'M', u'奈'), + (0xF90D, 'M', u'懶'), + (0xF90E, 'M', u'癩'), + (0xF90F, 'M', u'羅'), + (0xF910, 'M', u'蘿'), + (0xF911, 'M', u'螺'), + (0xF912, 'M', u'裸'), + (0xF913, 'M', u'邏'), + (0xF914, 'M', u'樂'), + (0xF915, 'M', u'洛'), + ] + +def _seg_39(): + return [ + (0xF916, 'M', u'烙'), + (0xF917, 'M', u'珞'), + (0xF918, 'M', u'落'), + (0xF919, 'M', u'酪'), + (0xF91A, 'M', u'駱'), + (0xF91B, 'M', u'亂'), + (0xF91C, 'M', u'卵'), + (0xF91D, 'M', u'欄'), + (0xF91E, 'M', u'爛'), + (0xF91F, 'M', u'蘭'), + (0xF920, 'M', u'鸞'), + (0xF921, 'M', u'嵐'), + (0xF922, 'M', u'濫'), + (0xF923, 'M', u'藍'), + (0xF924, 'M', u'襤'), + (0xF925, 'M', u'拉'), + (0xF926, 'M', u'臘'), + (0xF927, 'M', u'蠟'), + (0xF928, 'M', u'廊'), + (0xF929, 'M', u'朗'), + (0xF92A, 'M', u'浪'), + (0xF92B, 'M', u'狼'), + (0xF92C, 'M', u'郎'), + (0xF92D, 'M', u'來'), + (0xF92E, 'M', u'冷'), + (0xF92F, 'M', u'勞'), + (0xF930, 'M', u'擄'), + (0xF931, 'M', u'櫓'), + (0xF932, 'M', u'爐'), + (0xF933, 'M', u'盧'), + (0xF934, 'M', u'老'), + (0xF935, 'M', u'蘆'), + (0xF936, 'M', u'虜'), + (0xF937, 'M', u'路'), + (0xF938, 'M', u'露'), + (0xF939, 'M', u'魯'), + (0xF93A, 'M', u'鷺'), + (0xF93B, 'M', u'碌'), + (0xF93C, 'M', u'祿'), + (0xF93D, 'M', u'綠'), + (0xF93E, 'M', u'菉'), + (0xF93F, 'M', u'錄'), + (0xF940, 'M', u'鹿'), + (0xF941, 'M', u'論'), + (0xF942, 'M', u'壟'), + (0xF943, 'M', u'弄'), + (0xF944, 'M', u'籠'), + (0xF945, 'M', u'聾'), + (0xF946, 'M', u'牢'), + (0xF947, 'M', u'磊'), + (0xF948, 'M', u'賂'), + (0xF949, 'M', u'雷'), + (0xF94A, 'M', u'壘'), + (0xF94B, 'M', u'屢'), + (0xF94C, 'M', u'樓'), + (0xF94D, 'M', u'淚'), + (0xF94E, 'M', u'漏'), + (0xF94F, 'M', u'累'), + (0xF950, 'M', u'縷'), + (0xF951, 'M', u'陋'), + (0xF952, 'M', u'勒'), + (0xF953, 'M', u'肋'), + (0xF954, 'M', u'凜'), + (0xF955, 'M', u'凌'), + (0xF956, 'M', u'稜'), + (0xF957, 'M', u'綾'), + (0xF958, 'M', u'菱'), + (0xF959, 'M', u'陵'), + (0xF95A, 'M', u'讀'), + (0xF95B, 'M', u'拏'), + (0xF95C, 'M', u'樂'), + (0xF95D, 'M', u'諾'), + (0xF95E, 'M', u'丹'), + (0xF95F, 'M', u'寧'), + (0xF960, 'M', u'怒'), + (0xF961, 'M', u'率'), + (0xF962, 'M', u'異'), + (0xF963, 'M', u'北'), + (0xF964, 'M', u'磻'), + (0xF965, 'M', u'便'), + (0xF966, 'M', u'復'), + (0xF967, 'M', u'不'), + (0xF968, 'M', u'泌'), + (0xF969, 'M', u'數'), + (0xF96A, 'M', u'索'), + (0xF96B, 'M', u'參'), + (0xF96C, 'M', u'塞'), + (0xF96D, 'M', u'省'), + (0xF96E, 'M', u'葉'), + (0xF96F, 'M', u'說'), + (0xF970, 'M', u'殺'), + (0xF971, 'M', u'辰'), + (0xF972, 'M', u'沈'), + (0xF973, 'M', u'拾'), + (0xF974, 'M', u'若'), + (0xF975, 'M', u'掠'), + (0xF976, 'M', u'略'), + (0xF977, 'M', u'亮'), + (0xF978, 'M', u'兩'), + (0xF979, 'M', u'凉'), + ] + +def _seg_40(): + return [ + (0xF97A, 'M', u'梁'), + (0xF97B, 'M', u'糧'), + (0xF97C, 'M', u'良'), + (0xF97D, 'M', u'諒'), + (0xF97E, 'M', u'量'), + (0xF97F, 'M', u'勵'), + (0xF980, 'M', u'呂'), + (0xF981, 'M', u'女'), + (0xF982, 'M', u'廬'), + (0xF983, 'M', u'旅'), + (0xF984, 'M', u'濾'), + (0xF985, 'M', u'礪'), + (0xF986, 'M', u'閭'), + (0xF987, 'M', u'驪'), + (0xF988, 'M', u'麗'), + (0xF989, 'M', u'黎'), + (0xF98A, 'M', u'力'), + (0xF98B, 'M', u'曆'), + (0xF98C, 'M', u'歷'), + (0xF98D, 'M', u'轢'), + (0xF98E, 'M', u'年'), + (0xF98F, 'M', u'憐'), + (0xF990, 'M', u'戀'), + (0xF991, 'M', u'撚'), + (0xF992, 'M', u'漣'), + (0xF993, 'M', u'煉'), + (0xF994, 'M', u'璉'), + (0xF995, 'M', u'秊'), + (0xF996, 'M', u'練'), + (0xF997, 'M', u'聯'), + (0xF998, 'M', u'輦'), + (0xF999, 'M', u'蓮'), + (0xF99A, 'M', u'連'), + (0xF99B, 'M', u'鍊'), + (0xF99C, 'M', u'列'), + (0xF99D, 'M', u'劣'), + (0xF99E, 'M', u'咽'), + (0xF99F, 'M', u'烈'), + (0xF9A0, 'M', u'裂'), + (0xF9A1, 'M', u'說'), + (0xF9A2, 'M', u'廉'), + (0xF9A3, 'M', u'念'), + (0xF9A4, 'M', u'捻'), + (0xF9A5, 'M', u'殮'), + (0xF9A6, 'M', u'簾'), + (0xF9A7, 'M', u'獵'), + (0xF9A8, 'M', u'令'), + (0xF9A9, 'M', u'囹'), + (0xF9AA, 'M', u'寧'), + (0xF9AB, 'M', u'嶺'), + (0xF9AC, 'M', u'怜'), + (0xF9AD, 'M', u'玲'), + (0xF9AE, 'M', u'瑩'), + (0xF9AF, 'M', u'羚'), + (0xF9B0, 'M', u'聆'), + (0xF9B1, 'M', u'鈴'), + (0xF9B2, 'M', u'零'), + (0xF9B3, 'M', u'靈'), + (0xF9B4, 'M', u'領'), + (0xF9B5, 'M', u'例'), + (0xF9B6, 'M', u'禮'), + (0xF9B7, 'M', u'醴'), + (0xF9B8, 'M', u'隸'), + (0xF9B9, 'M', u'惡'), + (0xF9BA, 'M', u'了'), + (0xF9BB, 'M', u'僚'), + (0xF9BC, 'M', u'寮'), + (0xF9BD, 'M', u'尿'), + (0xF9BE, 'M', u'料'), + (0xF9BF, 'M', u'樂'), + (0xF9C0, 'M', u'燎'), + (0xF9C1, 'M', u'療'), + (0xF9C2, 'M', u'蓼'), + (0xF9C3, 'M', u'遼'), + (0xF9C4, 'M', u'龍'), + (0xF9C5, 'M', u'暈'), + (0xF9C6, 'M', u'阮'), + (0xF9C7, 'M', u'劉'), + (0xF9C8, 'M', u'杻'), + (0xF9C9, 'M', u'柳'), + (0xF9CA, 'M', u'流'), + (0xF9CB, 'M', u'溜'), + (0xF9CC, 'M', u'琉'), + (0xF9CD, 'M', u'留'), + (0xF9CE, 'M', u'硫'), + (0xF9CF, 'M', u'紐'), + (0xF9D0, 'M', u'類'), + (0xF9D1, 'M', u'六'), + (0xF9D2, 'M', u'戮'), + (0xF9D3, 'M', u'陸'), + (0xF9D4, 'M', u'倫'), + (0xF9D5, 'M', u'崙'), + (0xF9D6, 'M', u'淪'), + (0xF9D7, 'M', u'輪'), + (0xF9D8, 'M', u'律'), + (0xF9D9, 'M', u'慄'), + (0xF9DA, 'M', u'栗'), + (0xF9DB, 'M', u'率'), + (0xF9DC, 'M', u'隆'), + (0xF9DD, 'M', u'利'), + ] + +def _seg_41(): + return [ + (0xF9DE, 'M', u'吏'), + (0xF9DF, 'M', u'履'), + (0xF9E0, 'M', u'易'), + (0xF9E1, 'M', u'李'), + (0xF9E2, 'M', u'梨'), + (0xF9E3, 'M', u'泥'), + (0xF9E4, 'M', u'理'), + (0xF9E5, 'M', u'痢'), + (0xF9E6, 'M', u'罹'), + (0xF9E7, 'M', u'裏'), + (0xF9E8, 'M', u'裡'), + (0xF9E9, 'M', u'里'), + (0xF9EA, 'M', u'離'), + (0xF9EB, 'M', u'匿'), + (0xF9EC, 'M', u'溺'), + (0xF9ED, 'M', u'吝'), + (0xF9EE, 'M', u'燐'), + (0xF9EF, 'M', u'璘'), + (0xF9F0, 'M', u'藺'), + (0xF9F1, 'M', u'隣'), + (0xF9F2, 'M', u'鱗'), + (0xF9F3, 'M', u'麟'), + (0xF9F4, 'M', u'林'), + (0xF9F5, 'M', u'淋'), + (0xF9F6, 'M', u'臨'), + (0xF9F7, 'M', u'立'), + (0xF9F8, 'M', u'笠'), + (0xF9F9, 'M', u'粒'), + (0xF9FA, 'M', u'狀'), + (0xF9FB, 'M', u'炙'), + (0xF9FC, 'M', u'識'), + (0xF9FD, 'M', u'什'), + (0xF9FE, 'M', u'茶'), + (0xF9FF, 'M', u'刺'), + (0xFA00, 'M', u'切'), + (0xFA01, 'M', u'度'), + (0xFA02, 'M', u'拓'), + (0xFA03, 'M', u'糖'), + (0xFA04, 'M', u'宅'), + (0xFA05, 'M', u'洞'), + (0xFA06, 'M', u'暴'), + (0xFA07, 'M', u'輻'), + (0xFA08, 'M', u'行'), + (0xFA09, 'M', u'降'), + (0xFA0A, 'M', u'見'), + (0xFA0B, 'M', u'廓'), + (0xFA0C, 'M', u'兀'), + (0xFA0D, 'M', u'嗀'), + (0xFA0E, 'V'), + (0xFA10, 'M', u'塚'), + (0xFA11, 'V'), + (0xFA12, 'M', u'晴'), + (0xFA13, 'V'), + (0xFA15, 'M', u'凞'), + (0xFA16, 'M', u'猪'), + (0xFA17, 'M', u'益'), + (0xFA18, 'M', u'礼'), + (0xFA19, 'M', u'神'), + (0xFA1A, 'M', u'祥'), + (0xFA1B, 'M', u'福'), + (0xFA1C, 'M', u'靖'), + (0xFA1D, 'M', u'精'), + (0xFA1E, 'M', u'羽'), + (0xFA1F, 'V'), + (0xFA20, 'M', u'蘒'), + (0xFA21, 'V'), + (0xFA22, 'M', u'諸'), + (0xFA23, 'V'), + (0xFA25, 'M', u'逸'), + (0xFA26, 'M', u'都'), + (0xFA27, 'V'), + (0xFA2A, 'M', u'飯'), + (0xFA2B, 'M', u'飼'), + (0xFA2C, 'M', u'館'), + (0xFA2D, 'M', u'鶴'), + (0xFA2E, 'M', u'郞'), + (0xFA2F, 'M', u'隷'), + (0xFA30, 'M', u'侮'), + (0xFA31, 'M', u'僧'), + (0xFA32, 'M', u'免'), + (0xFA33, 'M', u'勉'), + (0xFA34, 'M', u'勤'), + (0xFA35, 'M', u'卑'), + (0xFA36, 'M', u'喝'), + (0xFA37, 'M', u'嘆'), + (0xFA38, 'M', u'器'), + (0xFA39, 'M', u'塀'), + (0xFA3A, 'M', u'墨'), + (0xFA3B, 'M', u'層'), + (0xFA3C, 'M', u'屮'), + (0xFA3D, 'M', u'悔'), + (0xFA3E, 'M', u'慨'), + (0xFA3F, 'M', u'憎'), + (0xFA40, 'M', u'懲'), + (0xFA41, 'M', u'敏'), + (0xFA42, 'M', u'既'), + (0xFA43, 'M', u'暑'), + (0xFA44, 'M', u'梅'), + (0xFA45, 'M', u'海'), + (0xFA46, 'M', u'渚'), + ] + +def _seg_42(): + return [ + (0xFA47, 'M', u'漢'), + (0xFA48, 'M', u'煮'), + (0xFA49, 'M', u'爫'), + (0xFA4A, 'M', u'琢'), + (0xFA4B, 'M', u'碑'), + (0xFA4C, 'M', u'社'), + (0xFA4D, 'M', u'祉'), + (0xFA4E, 'M', u'祈'), + (0xFA4F, 'M', u'祐'), + (0xFA50, 'M', u'祖'), + (0xFA51, 'M', u'祝'), + (0xFA52, 'M', u'禍'), + (0xFA53, 'M', u'禎'), + (0xFA54, 'M', u'穀'), + (0xFA55, 'M', u'突'), + (0xFA56, 'M', u'節'), + (0xFA57, 'M', u'練'), + (0xFA58, 'M', u'縉'), + (0xFA59, 'M', u'繁'), + (0xFA5A, 'M', u'署'), + (0xFA5B, 'M', u'者'), + (0xFA5C, 'M', u'臭'), + (0xFA5D, 'M', u'艹'), + (0xFA5F, 'M', u'著'), + (0xFA60, 'M', u'褐'), + (0xFA61, 'M', u'視'), + (0xFA62, 'M', u'謁'), + (0xFA63, 'M', u'謹'), + (0xFA64, 'M', u'賓'), + (0xFA65, 'M', u'贈'), + (0xFA66, 'M', u'辶'), + (0xFA67, 'M', u'逸'), + (0xFA68, 'M', u'難'), + (0xFA69, 'M', u'響'), + (0xFA6A, 'M', u'頻'), + (0xFA6B, 'M', u'恵'), + (0xFA6C, 'M', u'𤋮'), + (0xFA6D, 'M', u'舘'), + (0xFA6E, 'X'), + (0xFA70, 'M', u'並'), + (0xFA71, 'M', u'况'), + (0xFA72, 'M', u'全'), + (0xFA73, 'M', u'侀'), + (0xFA74, 'M', u'充'), + (0xFA75, 'M', u'冀'), + (0xFA76, 'M', u'勇'), + (0xFA77, 'M', u'勺'), + (0xFA78, 'M', u'喝'), + (0xFA79, 'M', u'啕'), + (0xFA7A, 'M', u'喙'), + (0xFA7B, 'M', u'嗢'), + (0xFA7C, 'M', u'塚'), + (0xFA7D, 'M', u'墳'), + (0xFA7E, 'M', u'奄'), + (0xFA7F, 'M', u'奔'), + (0xFA80, 'M', u'婢'), + (0xFA81, 'M', u'嬨'), + (0xFA82, 'M', u'廒'), + (0xFA83, 'M', u'廙'), + (0xFA84, 'M', u'彩'), + (0xFA85, 'M', u'徭'), + (0xFA86, 'M', u'惘'), + (0xFA87, 'M', u'慎'), + (0xFA88, 'M', u'愈'), + (0xFA89, 'M', u'憎'), + (0xFA8A, 'M', u'慠'), + (0xFA8B, 'M', u'懲'), + (0xFA8C, 'M', u'戴'), + (0xFA8D, 'M', u'揄'), + (0xFA8E, 'M', u'搜'), + (0xFA8F, 'M', u'摒'), + (0xFA90, 'M', u'敖'), + (0xFA91, 'M', u'晴'), + (0xFA92, 'M', u'朗'), + (0xFA93, 'M', u'望'), + (0xFA94, 'M', u'杖'), + (0xFA95, 'M', u'歹'), + (0xFA96, 'M', u'殺'), + (0xFA97, 'M', u'流'), + (0xFA98, 'M', u'滛'), + (0xFA99, 'M', u'滋'), + (0xFA9A, 'M', u'漢'), + (0xFA9B, 'M', u'瀞'), + (0xFA9C, 'M', u'煮'), + (0xFA9D, 'M', u'瞧'), + (0xFA9E, 'M', u'爵'), + (0xFA9F, 'M', u'犯'), + (0xFAA0, 'M', u'猪'), + (0xFAA1, 'M', u'瑱'), + (0xFAA2, 'M', u'甆'), + (0xFAA3, 'M', u'画'), + (0xFAA4, 'M', u'瘝'), + (0xFAA5, 'M', u'瘟'), + (0xFAA6, 'M', u'益'), + (0xFAA7, 'M', u'盛'), + (0xFAA8, 'M', u'直'), + (0xFAA9, 'M', u'睊'), + (0xFAAA, 'M', u'着'), + (0xFAAB, 'M', u'磌'), + (0xFAAC, 'M', u'窱'), + ] + +def _seg_43(): + return [ + (0xFAAD, 'M', u'節'), + (0xFAAE, 'M', u'类'), + (0xFAAF, 'M', u'絛'), + (0xFAB0, 'M', u'練'), + (0xFAB1, 'M', u'缾'), + (0xFAB2, 'M', u'者'), + (0xFAB3, 'M', u'荒'), + (0xFAB4, 'M', u'華'), + (0xFAB5, 'M', u'蝹'), + (0xFAB6, 'M', u'襁'), + (0xFAB7, 'M', u'覆'), + (0xFAB8, 'M', u'視'), + (0xFAB9, 'M', u'調'), + (0xFABA, 'M', u'諸'), + (0xFABB, 'M', u'請'), + (0xFABC, 'M', u'謁'), + (0xFABD, 'M', u'諾'), + (0xFABE, 'M', u'諭'), + (0xFABF, 'M', u'謹'), + (0xFAC0, 'M', u'變'), + (0xFAC1, 'M', u'贈'), + (0xFAC2, 'M', u'輸'), + (0xFAC3, 'M', u'遲'), + (0xFAC4, 'M', u'醙'), + (0xFAC5, 'M', u'鉶'), + (0xFAC6, 'M', u'陼'), + (0xFAC7, 'M', u'難'), + (0xFAC8, 'M', u'靖'), + (0xFAC9, 'M', u'韛'), + (0xFACA, 'M', u'響'), + (0xFACB, 'M', u'頋'), + (0xFACC, 'M', u'頻'), + (0xFACD, 'M', u'鬒'), + (0xFACE, 'M', u'龜'), + (0xFACF, 'M', u'𢡊'), + (0xFAD0, 'M', u'𢡄'), + (0xFAD1, 'M', u'𣏕'), + (0xFAD2, 'M', u'㮝'), + (0xFAD3, 'M', u'䀘'), + (0xFAD4, 'M', u'䀹'), + (0xFAD5, 'M', u'𥉉'), + (0xFAD6, 'M', u'𥳐'), + (0xFAD7, 'M', u'𧻓'), + (0xFAD8, 'M', u'齃'), + (0xFAD9, 'M', u'龎'), + (0xFADA, 'X'), + (0xFB00, 'M', u'ff'), + (0xFB01, 'M', u'fi'), + (0xFB02, 'M', u'fl'), + (0xFB03, 'M', u'ffi'), + (0xFB04, 'M', u'ffl'), + (0xFB05, 'M', u'st'), + (0xFB07, 'X'), + (0xFB13, 'M', u'մն'), + (0xFB14, 'M', u'մե'), + (0xFB15, 'M', u'մի'), + (0xFB16, 'M', u'վն'), + (0xFB17, 'M', u'մխ'), + (0xFB18, 'X'), + (0xFB1D, 'M', u'יִ'), + (0xFB1E, 'V'), + (0xFB1F, 'M', u'ײַ'), + (0xFB20, 'M', u'ע'), + (0xFB21, 'M', u'א'), + (0xFB22, 'M', u'ד'), + (0xFB23, 'M', u'ה'), + (0xFB24, 'M', u'כ'), + (0xFB25, 'M', u'ל'), + (0xFB26, 'M', u'ם'), + (0xFB27, 'M', u'ר'), + (0xFB28, 'M', u'ת'), + (0xFB29, '3', u'+'), + (0xFB2A, 'M', u'שׁ'), + (0xFB2B, 'M', u'שׂ'), + (0xFB2C, 'M', u'שּׁ'), + (0xFB2D, 'M', u'שּׂ'), + (0xFB2E, 'M', u'אַ'), + (0xFB2F, 'M', u'אָ'), + (0xFB30, 'M', u'אּ'), + (0xFB31, 'M', u'בּ'), + (0xFB32, 'M', u'גּ'), + (0xFB33, 'M', u'דּ'), + (0xFB34, 'M', u'הּ'), + (0xFB35, 'M', u'וּ'), + (0xFB36, 'M', u'זּ'), + (0xFB37, 'X'), + (0xFB38, 'M', u'טּ'), + (0xFB39, 'M', u'יּ'), + (0xFB3A, 'M', u'ךּ'), + (0xFB3B, 'M', u'כּ'), + (0xFB3C, 'M', u'לּ'), + (0xFB3D, 'X'), + (0xFB3E, 'M', u'מּ'), + (0xFB3F, 'X'), + (0xFB40, 'M', u'נּ'), + (0xFB41, 'M', u'סּ'), + (0xFB42, 'X'), + (0xFB43, 'M', u'ףּ'), + (0xFB44, 'M', u'פּ'), + (0xFB45, 'X'), + ] + +def _seg_44(): + return [ + (0xFB46, 'M', u'צּ'), + (0xFB47, 'M', u'קּ'), + (0xFB48, 'M', u'רּ'), + (0xFB49, 'M', u'שּ'), + (0xFB4A, 'M', u'תּ'), + (0xFB4B, 'M', u'וֹ'), + (0xFB4C, 'M', u'בֿ'), + (0xFB4D, 'M', u'כֿ'), + (0xFB4E, 'M', u'פֿ'), + (0xFB4F, 'M', u'אל'), + (0xFB50, 'M', u'ٱ'), + (0xFB52, 'M', u'ٻ'), + (0xFB56, 'M', u'پ'), + (0xFB5A, 'M', u'ڀ'), + (0xFB5E, 'M', u'ٺ'), + (0xFB62, 'M', u'ٿ'), + (0xFB66, 'M', u'ٹ'), + (0xFB6A, 'M', u'ڤ'), + (0xFB6E, 'M', u'ڦ'), + (0xFB72, 'M', u'ڄ'), + (0xFB76, 'M', u'ڃ'), + (0xFB7A, 'M', u'چ'), + (0xFB7E, 'M', u'ڇ'), + (0xFB82, 'M', u'ڍ'), + (0xFB84, 'M', u'ڌ'), + (0xFB86, 'M', u'ڎ'), + (0xFB88, 'M', u'ڈ'), + (0xFB8A, 'M', u'ژ'), + (0xFB8C, 'M', u'ڑ'), + (0xFB8E, 'M', u'ک'), + (0xFB92, 'M', u'گ'), + (0xFB96, 'M', u'ڳ'), + (0xFB9A, 'M', u'ڱ'), + (0xFB9E, 'M', u'ں'), + (0xFBA0, 'M', u'ڻ'), + (0xFBA4, 'M', u'ۀ'), + (0xFBA6, 'M', u'ہ'), + (0xFBAA, 'M', u'ھ'), + (0xFBAE, 'M', u'ے'), + (0xFBB0, 'M', u'ۓ'), + (0xFBB2, 'V'), + (0xFBC2, 'X'), + (0xFBD3, 'M', u'ڭ'), + (0xFBD7, 'M', u'ۇ'), + (0xFBD9, 'M', u'ۆ'), + (0xFBDB, 'M', u'ۈ'), + (0xFBDD, 'M', u'ۇٴ'), + (0xFBDE, 'M', u'ۋ'), + (0xFBE0, 'M', u'ۅ'), + (0xFBE2, 'M', u'ۉ'), + (0xFBE4, 'M', u'ې'), + (0xFBE8, 'M', u'ى'), + (0xFBEA, 'M', u'ئا'), + (0xFBEC, 'M', u'ئە'), + (0xFBEE, 'M', u'ئو'), + (0xFBF0, 'M', u'ئۇ'), + (0xFBF2, 'M', u'ئۆ'), + (0xFBF4, 'M', u'ئۈ'), + (0xFBF6, 'M', u'ئې'), + (0xFBF9, 'M', u'ئى'), + (0xFBFC, 'M', u'ی'), + (0xFC00, 'M', u'ئج'), + (0xFC01, 'M', u'ئح'), + (0xFC02, 'M', u'ئم'), + (0xFC03, 'M', u'ئى'), + (0xFC04, 'M', u'ئي'), + (0xFC05, 'M', u'بج'), + (0xFC06, 'M', u'بح'), + (0xFC07, 'M', u'بخ'), + (0xFC08, 'M', u'بم'), + (0xFC09, 'M', u'بى'), + (0xFC0A, 'M', u'بي'), + (0xFC0B, 'M', u'تج'), + (0xFC0C, 'M', u'تح'), + (0xFC0D, 'M', u'تخ'), + (0xFC0E, 'M', u'تم'), + (0xFC0F, 'M', u'تى'), + (0xFC10, 'M', u'تي'), + (0xFC11, 'M', u'ثج'), + (0xFC12, 'M', u'ثم'), + (0xFC13, 'M', u'ثى'), + (0xFC14, 'M', u'ثي'), + (0xFC15, 'M', u'جح'), + (0xFC16, 'M', u'جم'), + (0xFC17, 'M', u'حج'), + (0xFC18, 'M', u'حم'), + (0xFC19, 'M', u'خج'), + (0xFC1A, 'M', u'خح'), + (0xFC1B, 'M', u'خم'), + (0xFC1C, 'M', u'سج'), + (0xFC1D, 'M', u'سح'), + (0xFC1E, 'M', u'سخ'), + (0xFC1F, 'M', u'سم'), + (0xFC20, 'M', u'صح'), + (0xFC21, 'M', u'صم'), + (0xFC22, 'M', u'ضج'), + (0xFC23, 'M', u'ضح'), + (0xFC24, 'M', u'ضخ'), + (0xFC25, 'M', u'ضم'), + (0xFC26, 'M', u'طح'), + ] + +def _seg_45(): + return [ + (0xFC27, 'M', u'طم'), + (0xFC28, 'M', u'ظم'), + (0xFC29, 'M', u'عج'), + (0xFC2A, 'M', u'عم'), + (0xFC2B, 'M', u'غج'), + (0xFC2C, 'M', u'غم'), + (0xFC2D, 'M', u'فج'), + (0xFC2E, 'M', u'فح'), + (0xFC2F, 'M', u'فخ'), + (0xFC30, 'M', u'فم'), + (0xFC31, 'M', u'فى'), + (0xFC32, 'M', u'في'), + (0xFC33, 'M', u'قح'), + (0xFC34, 'M', u'قم'), + (0xFC35, 'M', u'قى'), + (0xFC36, 'M', u'قي'), + (0xFC37, 'M', u'كا'), + (0xFC38, 'M', u'كج'), + (0xFC39, 'M', u'كح'), + (0xFC3A, 'M', u'كخ'), + (0xFC3B, 'M', u'كل'), + (0xFC3C, 'M', u'كم'), + (0xFC3D, 'M', u'كى'), + (0xFC3E, 'M', u'كي'), + (0xFC3F, 'M', u'لج'), + (0xFC40, 'M', u'لح'), + (0xFC41, 'M', u'لخ'), + (0xFC42, 'M', u'لم'), + (0xFC43, 'M', u'لى'), + (0xFC44, 'M', u'لي'), + (0xFC45, 'M', u'مج'), + (0xFC46, 'M', u'مح'), + (0xFC47, 'M', u'مخ'), + (0xFC48, 'M', u'مم'), + (0xFC49, 'M', u'مى'), + (0xFC4A, 'M', u'مي'), + (0xFC4B, 'M', u'نج'), + (0xFC4C, 'M', u'نح'), + (0xFC4D, 'M', u'نخ'), + (0xFC4E, 'M', u'نم'), + (0xFC4F, 'M', u'نى'), + (0xFC50, 'M', u'ني'), + (0xFC51, 'M', u'هج'), + (0xFC52, 'M', u'هم'), + (0xFC53, 'M', u'هى'), + (0xFC54, 'M', u'هي'), + (0xFC55, 'M', u'يج'), + (0xFC56, 'M', u'يح'), + (0xFC57, 'M', u'يخ'), + (0xFC58, 'M', u'يم'), + (0xFC59, 'M', u'يى'), + (0xFC5A, 'M', u'يي'), + (0xFC5B, 'M', u'ذٰ'), + (0xFC5C, 'M', u'رٰ'), + (0xFC5D, 'M', u'ىٰ'), + (0xFC5E, '3', u' ٌّ'), + (0xFC5F, '3', u' ٍّ'), + (0xFC60, '3', u' َّ'), + (0xFC61, '3', u' ُّ'), + (0xFC62, '3', u' ِّ'), + (0xFC63, '3', u' ّٰ'), + (0xFC64, 'M', u'ئر'), + (0xFC65, 'M', u'ئز'), + (0xFC66, 'M', u'ئم'), + (0xFC67, 'M', u'ئن'), + (0xFC68, 'M', u'ئى'), + (0xFC69, 'M', u'ئي'), + (0xFC6A, 'M', u'بر'), + (0xFC6B, 'M', u'بز'), + (0xFC6C, 'M', u'بم'), + (0xFC6D, 'M', u'بن'), + (0xFC6E, 'M', u'بى'), + (0xFC6F, 'M', u'بي'), + (0xFC70, 'M', u'تر'), + (0xFC71, 'M', u'تز'), + (0xFC72, 'M', u'تم'), + (0xFC73, 'M', u'تن'), + (0xFC74, 'M', u'تى'), + (0xFC75, 'M', u'تي'), + (0xFC76, 'M', u'ثر'), + (0xFC77, 'M', u'ثز'), + (0xFC78, 'M', u'ثم'), + (0xFC79, 'M', u'ثن'), + (0xFC7A, 'M', u'ثى'), + (0xFC7B, 'M', u'ثي'), + (0xFC7C, 'M', u'فى'), + (0xFC7D, 'M', u'في'), + (0xFC7E, 'M', u'قى'), + (0xFC7F, 'M', u'قي'), + (0xFC80, 'M', u'كا'), + (0xFC81, 'M', u'كل'), + (0xFC82, 'M', u'كم'), + (0xFC83, 'M', u'كى'), + (0xFC84, 'M', u'كي'), + (0xFC85, 'M', u'لم'), + (0xFC86, 'M', u'لى'), + (0xFC87, 'M', u'لي'), + (0xFC88, 'M', u'ما'), + (0xFC89, 'M', u'مم'), + (0xFC8A, 'M', u'نر'), + ] + +def _seg_46(): + return [ + (0xFC8B, 'M', u'نز'), + (0xFC8C, 'M', u'نم'), + (0xFC8D, 'M', u'نن'), + (0xFC8E, 'M', u'نى'), + (0xFC8F, 'M', u'ني'), + (0xFC90, 'M', u'ىٰ'), + (0xFC91, 'M', u'ير'), + (0xFC92, 'M', u'يز'), + (0xFC93, 'M', u'يم'), + (0xFC94, 'M', u'ين'), + (0xFC95, 'M', u'يى'), + (0xFC96, 'M', u'يي'), + (0xFC97, 'M', u'ئج'), + (0xFC98, 'M', u'ئح'), + (0xFC99, 'M', u'ئخ'), + (0xFC9A, 'M', u'ئم'), + (0xFC9B, 'M', u'ئه'), + (0xFC9C, 'M', u'بج'), + (0xFC9D, 'M', u'بح'), + (0xFC9E, 'M', u'بخ'), + (0xFC9F, 'M', u'بم'), + (0xFCA0, 'M', u'به'), + (0xFCA1, 'M', u'تج'), + (0xFCA2, 'M', u'تح'), + (0xFCA3, 'M', u'تخ'), + (0xFCA4, 'M', u'تم'), + (0xFCA5, 'M', u'ته'), + (0xFCA6, 'M', u'ثم'), + (0xFCA7, 'M', u'جح'), + (0xFCA8, 'M', u'جم'), + (0xFCA9, 'M', u'حج'), + (0xFCAA, 'M', u'حم'), + (0xFCAB, 'M', u'خج'), + (0xFCAC, 'M', u'خم'), + (0xFCAD, 'M', u'سج'), + (0xFCAE, 'M', u'سح'), + (0xFCAF, 'M', u'سخ'), + (0xFCB0, 'M', u'سم'), + (0xFCB1, 'M', u'صح'), + (0xFCB2, 'M', u'صخ'), + (0xFCB3, 'M', u'صم'), + (0xFCB4, 'M', u'ضج'), + (0xFCB5, 'M', u'ضح'), + (0xFCB6, 'M', u'ضخ'), + (0xFCB7, 'M', u'ضم'), + (0xFCB8, 'M', u'طح'), + (0xFCB9, 'M', u'ظم'), + (0xFCBA, 'M', u'عج'), + (0xFCBB, 'M', u'عم'), + (0xFCBC, 'M', u'غج'), + (0xFCBD, 'M', u'غم'), + (0xFCBE, 'M', u'فج'), + (0xFCBF, 'M', u'فح'), + (0xFCC0, 'M', u'فخ'), + (0xFCC1, 'M', u'فم'), + (0xFCC2, 'M', u'قح'), + (0xFCC3, 'M', u'قم'), + (0xFCC4, 'M', u'كج'), + (0xFCC5, 'M', u'كح'), + (0xFCC6, 'M', u'كخ'), + (0xFCC7, 'M', u'كل'), + (0xFCC8, 'M', u'كم'), + (0xFCC9, 'M', u'لج'), + (0xFCCA, 'M', u'لح'), + (0xFCCB, 'M', u'لخ'), + (0xFCCC, 'M', u'لم'), + (0xFCCD, 'M', u'له'), + (0xFCCE, 'M', u'مج'), + (0xFCCF, 'M', u'مح'), + (0xFCD0, 'M', u'مخ'), + (0xFCD1, 'M', u'مم'), + (0xFCD2, 'M', u'نج'), + (0xFCD3, 'M', u'نح'), + (0xFCD4, 'M', u'نخ'), + (0xFCD5, 'M', u'نم'), + (0xFCD6, 'M', u'نه'), + (0xFCD7, 'M', u'هج'), + (0xFCD8, 'M', u'هم'), + (0xFCD9, 'M', u'هٰ'), + (0xFCDA, 'M', u'يج'), + (0xFCDB, 'M', u'يح'), + (0xFCDC, 'M', u'يخ'), + (0xFCDD, 'M', u'يم'), + (0xFCDE, 'M', u'يه'), + (0xFCDF, 'M', u'ئم'), + (0xFCE0, 'M', u'ئه'), + (0xFCE1, 'M', u'بم'), + (0xFCE2, 'M', u'به'), + (0xFCE3, 'M', u'تم'), + (0xFCE4, 'M', u'ته'), + (0xFCE5, 'M', u'ثم'), + (0xFCE6, 'M', u'ثه'), + (0xFCE7, 'M', u'سم'), + (0xFCE8, 'M', u'سه'), + (0xFCE9, 'M', u'شم'), + (0xFCEA, 'M', u'شه'), + (0xFCEB, 'M', u'كل'), + (0xFCEC, 'M', u'كم'), + (0xFCED, 'M', u'لم'), + (0xFCEE, 'M', u'نم'), + ] + +def _seg_47(): + return [ + (0xFCEF, 'M', u'نه'), + (0xFCF0, 'M', u'يم'), + (0xFCF1, 'M', u'يه'), + (0xFCF2, 'M', u'ـَّ'), + (0xFCF3, 'M', u'ـُّ'), + (0xFCF4, 'M', u'ـِّ'), + (0xFCF5, 'M', u'طى'), + (0xFCF6, 'M', u'طي'), + (0xFCF7, 'M', u'عى'), + (0xFCF8, 'M', u'عي'), + (0xFCF9, 'M', u'غى'), + (0xFCFA, 'M', u'غي'), + (0xFCFB, 'M', u'سى'), + (0xFCFC, 'M', u'سي'), + (0xFCFD, 'M', u'شى'), + (0xFCFE, 'M', u'شي'), + (0xFCFF, 'M', u'حى'), + (0xFD00, 'M', u'حي'), + (0xFD01, 'M', u'جى'), + (0xFD02, 'M', u'جي'), + (0xFD03, 'M', u'خى'), + (0xFD04, 'M', u'خي'), + (0xFD05, 'M', u'صى'), + (0xFD06, 'M', u'صي'), + (0xFD07, 'M', u'ضى'), + (0xFD08, 'M', u'ضي'), + (0xFD09, 'M', u'شج'), + (0xFD0A, 'M', u'شح'), + (0xFD0B, 'M', u'شخ'), + (0xFD0C, 'M', u'شم'), + (0xFD0D, 'M', u'شر'), + (0xFD0E, 'M', u'سر'), + (0xFD0F, 'M', u'صر'), + (0xFD10, 'M', u'ضر'), + (0xFD11, 'M', u'طى'), + (0xFD12, 'M', u'طي'), + (0xFD13, 'M', u'عى'), + (0xFD14, 'M', u'عي'), + (0xFD15, 'M', u'غى'), + (0xFD16, 'M', u'غي'), + (0xFD17, 'M', u'سى'), + (0xFD18, 'M', u'سي'), + (0xFD19, 'M', u'شى'), + (0xFD1A, 'M', u'شي'), + (0xFD1B, 'M', u'حى'), + (0xFD1C, 'M', u'حي'), + (0xFD1D, 'M', u'جى'), + (0xFD1E, 'M', u'جي'), + (0xFD1F, 'M', u'خى'), + (0xFD20, 'M', u'خي'), + (0xFD21, 'M', u'صى'), + (0xFD22, 'M', u'صي'), + (0xFD23, 'M', u'ضى'), + (0xFD24, 'M', u'ضي'), + (0xFD25, 'M', u'شج'), + (0xFD26, 'M', u'شح'), + (0xFD27, 'M', u'شخ'), + (0xFD28, 'M', u'شم'), + (0xFD29, 'M', u'شر'), + (0xFD2A, 'M', u'سر'), + (0xFD2B, 'M', u'صر'), + (0xFD2C, 'M', u'ضر'), + (0xFD2D, 'M', u'شج'), + (0xFD2E, 'M', u'شح'), + (0xFD2F, 'M', u'شخ'), + (0xFD30, 'M', u'شم'), + (0xFD31, 'M', u'سه'), + (0xFD32, 'M', u'شه'), + (0xFD33, 'M', u'طم'), + (0xFD34, 'M', u'سج'), + (0xFD35, 'M', u'سح'), + (0xFD36, 'M', u'سخ'), + (0xFD37, 'M', u'شج'), + (0xFD38, 'M', u'شح'), + (0xFD39, 'M', u'شخ'), + (0xFD3A, 'M', u'طم'), + (0xFD3B, 'M', u'ظم'), + (0xFD3C, 'M', u'اً'), + (0xFD3E, 'V'), + (0xFD40, 'X'), + (0xFD50, 'M', u'تجم'), + (0xFD51, 'M', u'تحج'), + (0xFD53, 'M', u'تحم'), + (0xFD54, 'M', u'تخم'), + (0xFD55, 'M', u'تمج'), + (0xFD56, 'M', u'تمح'), + (0xFD57, 'M', u'تمخ'), + (0xFD58, 'M', u'جمح'), + (0xFD5A, 'M', u'حمي'), + (0xFD5B, 'M', u'حمى'), + (0xFD5C, 'M', u'سحج'), + (0xFD5D, 'M', u'سجح'), + (0xFD5E, 'M', u'سجى'), + (0xFD5F, 'M', u'سمح'), + (0xFD61, 'M', u'سمج'), + (0xFD62, 'M', u'سمم'), + (0xFD64, 'M', u'صحح'), + (0xFD66, 'M', u'صمم'), + (0xFD67, 'M', u'شحم'), + (0xFD69, 'M', u'شجي'), + ] + +def _seg_48(): + return [ + (0xFD6A, 'M', u'شمخ'), + (0xFD6C, 'M', u'شمم'), + (0xFD6E, 'M', u'ضحى'), + (0xFD6F, 'M', u'ضخم'), + (0xFD71, 'M', u'طمح'), + (0xFD73, 'M', u'طمم'), + (0xFD74, 'M', u'طمي'), + (0xFD75, 'M', u'عجم'), + (0xFD76, 'M', u'عمم'), + (0xFD78, 'M', u'عمى'), + (0xFD79, 'M', u'غمم'), + (0xFD7A, 'M', u'غمي'), + (0xFD7B, 'M', u'غمى'), + (0xFD7C, 'M', u'فخم'), + (0xFD7E, 'M', u'قمح'), + (0xFD7F, 'M', u'قمم'), + (0xFD80, 'M', u'لحم'), + (0xFD81, 'M', u'لحي'), + (0xFD82, 'M', u'لحى'), + (0xFD83, 'M', u'لجج'), + (0xFD85, 'M', u'لخم'), + (0xFD87, 'M', u'لمح'), + (0xFD89, 'M', u'محج'), + (0xFD8A, 'M', u'محم'), + (0xFD8B, 'M', u'محي'), + (0xFD8C, 'M', u'مجح'), + (0xFD8D, 'M', u'مجم'), + (0xFD8E, 'M', u'مخج'), + (0xFD8F, 'M', u'مخم'), + (0xFD90, 'X'), + (0xFD92, 'M', u'مجخ'), + (0xFD93, 'M', u'همج'), + (0xFD94, 'M', u'همم'), + (0xFD95, 'M', u'نحم'), + (0xFD96, 'M', u'نحى'), + (0xFD97, 'M', u'نجم'), + (0xFD99, 'M', u'نجى'), + (0xFD9A, 'M', u'نمي'), + (0xFD9B, 'M', u'نمى'), + (0xFD9C, 'M', u'يمم'), + (0xFD9E, 'M', u'بخي'), + (0xFD9F, 'M', u'تجي'), + (0xFDA0, 'M', u'تجى'), + (0xFDA1, 'M', u'تخي'), + (0xFDA2, 'M', u'تخى'), + (0xFDA3, 'M', u'تمي'), + (0xFDA4, 'M', u'تمى'), + (0xFDA5, 'M', u'جمي'), + (0xFDA6, 'M', u'جحى'), + (0xFDA7, 'M', u'جمى'), + (0xFDA8, 'M', u'سخى'), + (0xFDA9, 'M', u'صحي'), + (0xFDAA, 'M', u'شحي'), + (0xFDAB, 'M', u'ضحي'), + (0xFDAC, 'M', u'لجي'), + (0xFDAD, 'M', u'لمي'), + (0xFDAE, 'M', u'يحي'), + (0xFDAF, 'M', u'يجي'), + (0xFDB0, 'M', u'يمي'), + (0xFDB1, 'M', u'ممي'), + (0xFDB2, 'M', u'قمي'), + (0xFDB3, 'M', u'نحي'), + (0xFDB4, 'M', u'قمح'), + (0xFDB5, 'M', u'لحم'), + (0xFDB6, 'M', u'عمي'), + (0xFDB7, 'M', u'كمي'), + (0xFDB8, 'M', u'نجح'), + (0xFDB9, 'M', u'مخي'), + (0xFDBA, 'M', u'لجم'), + (0xFDBB, 'M', u'كمم'), + (0xFDBC, 'M', u'لجم'), + (0xFDBD, 'M', u'نجح'), + (0xFDBE, 'M', u'جحي'), + (0xFDBF, 'M', u'حجي'), + (0xFDC0, 'M', u'مجي'), + (0xFDC1, 'M', u'فمي'), + (0xFDC2, 'M', u'بحي'), + (0xFDC3, 'M', u'كمم'), + (0xFDC4, 'M', u'عجم'), + (0xFDC5, 'M', u'صمم'), + (0xFDC6, 'M', u'سخي'), + (0xFDC7, 'M', u'نجي'), + (0xFDC8, 'X'), + (0xFDF0, 'M', u'صلے'), + (0xFDF1, 'M', u'قلے'), + (0xFDF2, 'M', u'الله'), + (0xFDF3, 'M', u'اكبر'), + (0xFDF4, 'M', u'محمد'), + (0xFDF5, 'M', u'صلعم'), + (0xFDF6, 'M', u'رسول'), + (0xFDF7, 'M', u'عليه'), + (0xFDF8, 'M', u'وسلم'), + (0xFDF9, 'M', u'صلى'), + (0xFDFA, '3', u'صلى الله عليه وسلم'), + (0xFDFB, '3', u'جل جلاله'), + (0xFDFC, 'M', u'ریال'), + (0xFDFD, 'V'), + (0xFDFE, 'X'), + (0xFE00, 'I'), + (0xFE10, '3', u','), + ] + +def _seg_49(): + return [ + (0xFE11, 'M', u'、'), + (0xFE12, 'X'), + (0xFE13, '3', u':'), + (0xFE14, '3', u';'), + (0xFE15, '3', u'!'), + (0xFE16, '3', u'?'), + (0xFE17, 'M', u'〖'), + (0xFE18, 'M', u'〗'), + (0xFE19, 'X'), + (0xFE20, 'V'), + (0xFE30, 'X'), + (0xFE31, 'M', u'—'), + (0xFE32, 'M', u'–'), + (0xFE33, '3', u'_'), + (0xFE35, '3', u'('), + (0xFE36, '3', u')'), + (0xFE37, '3', u'{'), + (0xFE38, '3', u'}'), + (0xFE39, 'M', u'〔'), + (0xFE3A, 'M', u'〕'), + (0xFE3B, 'M', u'【'), + (0xFE3C, 'M', u'】'), + (0xFE3D, 'M', u'《'), + (0xFE3E, 'M', u'》'), + (0xFE3F, 'M', u'〈'), + (0xFE40, 'M', u'〉'), + (0xFE41, 'M', u'「'), + (0xFE42, 'M', u'」'), + (0xFE43, 'M', u'『'), + (0xFE44, 'M', u'』'), + (0xFE45, 'V'), + (0xFE47, '3', u'['), + (0xFE48, '3', u']'), + (0xFE49, '3', u' ̅'), + (0xFE4D, '3', u'_'), + (0xFE50, '3', u','), + (0xFE51, 'M', u'、'), + (0xFE52, 'X'), + (0xFE54, '3', u';'), + (0xFE55, '3', u':'), + (0xFE56, '3', u'?'), + (0xFE57, '3', u'!'), + (0xFE58, 'M', u'—'), + (0xFE59, '3', u'('), + (0xFE5A, '3', u')'), + (0xFE5B, '3', u'{'), + (0xFE5C, '3', u'}'), + (0xFE5D, 'M', u'〔'), + (0xFE5E, 'M', u'〕'), + (0xFE5F, '3', u'#'), + (0xFE60, '3', u'&'), + (0xFE61, '3', u'*'), + (0xFE62, '3', u'+'), + (0xFE63, 'M', u'-'), + (0xFE64, '3', u'<'), + (0xFE65, '3', u'>'), + (0xFE66, '3', u'='), + (0xFE67, 'X'), + (0xFE68, '3', u'\\'), + (0xFE69, '3', u'$'), + (0xFE6A, '3', u'%'), + (0xFE6B, '3', u'@'), + (0xFE6C, 'X'), + (0xFE70, '3', u' ً'), + (0xFE71, 'M', u'ـً'), + (0xFE72, '3', u' ٌ'), + (0xFE73, 'V'), + (0xFE74, '3', u' ٍ'), + (0xFE75, 'X'), + (0xFE76, '3', u' َ'), + (0xFE77, 'M', u'ـَ'), + (0xFE78, '3', u' ُ'), + (0xFE79, 'M', u'ـُ'), + (0xFE7A, '3', u' ِ'), + (0xFE7B, 'M', u'ـِ'), + (0xFE7C, '3', u' ّ'), + (0xFE7D, 'M', u'ـّ'), + (0xFE7E, '3', u' ْ'), + (0xFE7F, 'M', u'ـْ'), + (0xFE80, 'M', u'ء'), + (0xFE81, 'M', u'آ'), + (0xFE83, 'M', u'أ'), + (0xFE85, 'M', u'ؤ'), + (0xFE87, 'M', u'إ'), + (0xFE89, 'M', u'ئ'), + (0xFE8D, 'M', u'ا'), + (0xFE8F, 'M', u'ب'), + (0xFE93, 'M', u'ة'), + (0xFE95, 'M', u'ت'), + (0xFE99, 'M', u'ث'), + (0xFE9D, 'M', u'ج'), + (0xFEA1, 'M', u'ح'), + (0xFEA5, 'M', u'خ'), + (0xFEA9, 'M', u'د'), + (0xFEAB, 'M', u'ذ'), + (0xFEAD, 'M', u'ر'), + (0xFEAF, 'M', u'ز'), + (0xFEB1, 'M', u'س'), + (0xFEB5, 'M', u'ش'), + (0xFEB9, 'M', u'ص'), + ] + +def _seg_50(): + return [ + (0xFEBD, 'M', u'ض'), + (0xFEC1, 'M', u'ط'), + (0xFEC5, 'M', u'ظ'), + (0xFEC9, 'M', u'ع'), + (0xFECD, 'M', u'غ'), + (0xFED1, 'M', u'ف'), + (0xFED5, 'M', u'ق'), + (0xFED9, 'M', u'ك'), + (0xFEDD, 'M', u'ل'), + (0xFEE1, 'M', u'م'), + (0xFEE5, 'M', u'ن'), + (0xFEE9, 'M', u'ه'), + (0xFEED, 'M', u'و'), + (0xFEEF, 'M', u'ى'), + (0xFEF1, 'M', u'ي'), + (0xFEF5, 'M', u'لآ'), + (0xFEF7, 'M', u'لأ'), + (0xFEF9, 'M', u'لإ'), + (0xFEFB, 'M', u'لا'), + (0xFEFD, 'X'), + (0xFEFF, 'I'), + (0xFF00, 'X'), + (0xFF01, '3', u'!'), + (0xFF02, '3', u'"'), + (0xFF03, '3', u'#'), + (0xFF04, '3', u'$'), + (0xFF05, '3', u'%'), + (0xFF06, '3', u'&'), + (0xFF07, '3', u'\''), + (0xFF08, '3', u'('), + (0xFF09, '3', u')'), + (0xFF0A, '3', u'*'), + (0xFF0B, '3', u'+'), + (0xFF0C, '3', u','), + (0xFF0D, 'M', u'-'), + (0xFF0E, 'M', u'.'), + (0xFF0F, '3', u'/'), + (0xFF10, 'M', u'0'), + (0xFF11, 'M', u'1'), + (0xFF12, 'M', u'2'), + (0xFF13, 'M', u'3'), + (0xFF14, 'M', u'4'), + (0xFF15, 'M', u'5'), + (0xFF16, 'M', u'6'), + (0xFF17, 'M', u'7'), + (0xFF18, 'M', u'8'), + (0xFF19, 'M', u'9'), + (0xFF1A, '3', u':'), + (0xFF1B, '3', u';'), + (0xFF1C, '3', u'<'), + (0xFF1D, '3', u'='), + (0xFF1E, '3', u'>'), + (0xFF1F, '3', u'?'), + (0xFF20, '3', u'@'), + (0xFF21, 'M', u'a'), + (0xFF22, 'M', u'b'), + (0xFF23, 'M', u'c'), + (0xFF24, 'M', u'd'), + (0xFF25, 'M', u'e'), + (0xFF26, 'M', u'f'), + (0xFF27, 'M', u'g'), + (0xFF28, 'M', u'h'), + (0xFF29, 'M', u'i'), + (0xFF2A, 'M', u'j'), + (0xFF2B, 'M', u'k'), + (0xFF2C, 'M', u'l'), + (0xFF2D, 'M', u'm'), + (0xFF2E, 'M', u'n'), + (0xFF2F, 'M', u'o'), + (0xFF30, 'M', u'p'), + (0xFF31, 'M', u'q'), + (0xFF32, 'M', u'r'), + (0xFF33, 'M', u's'), + (0xFF34, 'M', u't'), + (0xFF35, 'M', u'u'), + (0xFF36, 'M', u'v'), + (0xFF37, 'M', u'w'), + (0xFF38, 'M', u'x'), + (0xFF39, 'M', u'y'), + (0xFF3A, 'M', u'z'), + (0xFF3B, '3', u'['), + (0xFF3C, '3', u'\\'), + (0xFF3D, '3', u']'), + (0xFF3E, '3', u'^'), + (0xFF3F, '3', u'_'), + (0xFF40, '3', u'`'), + (0xFF41, 'M', u'a'), + (0xFF42, 'M', u'b'), + (0xFF43, 'M', u'c'), + (0xFF44, 'M', u'd'), + (0xFF45, 'M', u'e'), + (0xFF46, 'M', u'f'), + (0xFF47, 'M', u'g'), + (0xFF48, 'M', u'h'), + (0xFF49, 'M', u'i'), + (0xFF4A, 'M', u'j'), + (0xFF4B, 'M', u'k'), + (0xFF4C, 'M', u'l'), + (0xFF4D, 'M', u'm'), + (0xFF4E, 'M', u'n'), + ] + +def _seg_51(): + return [ + (0xFF4F, 'M', u'o'), + (0xFF50, 'M', u'p'), + (0xFF51, 'M', u'q'), + (0xFF52, 'M', u'r'), + (0xFF53, 'M', u's'), + (0xFF54, 'M', u't'), + (0xFF55, 'M', u'u'), + (0xFF56, 'M', u'v'), + (0xFF57, 'M', u'w'), + (0xFF58, 'M', u'x'), + (0xFF59, 'M', u'y'), + (0xFF5A, 'M', u'z'), + (0xFF5B, '3', u'{'), + (0xFF5C, '3', u'|'), + (0xFF5D, '3', u'}'), + (0xFF5E, '3', u'~'), + (0xFF5F, 'M', u'⦅'), + (0xFF60, 'M', u'⦆'), + (0xFF61, 'M', u'.'), + (0xFF62, 'M', u'「'), + (0xFF63, 'M', u'」'), + (0xFF64, 'M', u'、'), + (0xFF65, 'M', u'・'), + (0xFF66, 'M', u'ヲ'), + (0xFF67, 'M', u'ァ'), + (0xFF68, 'M', u'ィ'), + (0xFF69, 'M', u'ゥ'), + (0xFF6A, 'M', u'ェ'), + (0xFF6B, 'M', u'ォ'), + (0xFF6C, 'M', u'ャ'), + (0xFF6D, 'M', u'ュ'), + (0xFF6E, 'M', u'ョ'), + (0xFF6F, 'M', u'ッ'), + (0xFF70, 'M', u'ー'), + (0xFF71, 'M', u'ア'), + (0xFF72, 'M', u'イ'), + (0xFF73, 'M', u'ウ'), + (0xFF74, 'M', u'エ'), + (0xFF75, 'M', u'オ'), + (0xFF76, 'M', u'カ'), + (0xFF77, 'M', u'キ'), + (0xFF78, 'M', u'ク'), + (0xFF79, 'M', u'ケ'), + (0xFF7A, 'M', u'コ'), + (0xFF7B, 'M', u'サ'), + (0xFF7C, 'M', u'シ'), + (0xFF7D, 'M', u'ス'), + (0xFF7E, 'M', u'セ'), + (0xFF7F, 'M', u'ソ'), + (0xFF80, 'M', u'タ'), + (0xFF81, 'M', u'チ'), + (0xFF82, 'M', u'ツ'), + (0xFF83, 'M', u'テ'), + (0xFF84, 'M', u'ト'), + (0xFF85, 'M', u'ナ'), + (0xFF86, 'M', u'ニ'), + (0xFF87, 'M', u'ヌ'), + (0xFF88, 'M', u'ネ'), + (0xFF89, 'M', u'ノ'), + (0xFF8A, 'M', u'ハ'), + (0xFF8B, 'M', u'ヒ'), + (0xFF8C, 'M', u'フ'), + (0xFF8D, 'M', u'ヘ'), + (0xFF8E, 'M', u'ホ'), + (0xFF8F, 'M', u'マ'), + (0xFF90, 'M', u'ミ'), + (0xFF91, 'M', u'ム'), + (0xFF92, 'M', u'メ'), + (0xFF93, 'M', u'モ'), + (0xFF94, 'M', u'ヤ'), + (0xFF95, 'M', u'ユ'), + (0xFF96, 'M', u'ヨ'), + (0xFF97, 'M', u'ラ'), + (0xFF98, 'M', u'リ'), + (0xFF99, 'M', u'ル'), + (0xFF9A, 'M', u'レ'), + (0xFF9B, 'M', u'ロ'), + (0xFF9C, 'M', u'ワ'), + (0xFF9D, 'M', u'ン'), + (0xFF9E, 'M', u'゙'), + (0xFF9F, 'M', u'゚'), + (0xFFA0, 'X'), + (0xFFA1, 'M', u'ᄀ'), + (0xFFA2, 'M', u'ᄁ'), + (0xFFA3, 'M', u'ᆪ'), + (0xFFA4, 'M', u'ᄂ'), + (0xFFA5, 'M', u'ᆬ'), + (0xFFA6, 'M', u'ᆭ'), + (0xFFA7, 'M', u'ᄃ'), + (0xFFA8, 'M', u'ᄄ'), + (0xFFA9, 'M', u'ᄅ'), + (0xFFAA, 'M', u'ᆰ'), + (0xFFAB, 'M', u'ᆱ'), + (0xFFAC, 'M', u'ᆲ'), + (0xFFAD, 'M', u'ᆳ'), + (0xFFAE, 'M', u'ᆴ'), + (0xFFAF, 'M', u'ᆵ'), + (0xFFB0, 'M', u'ᄚ'), + (0xFFB1, 'M', u'ᄆ'), + (0xFFB2, 'M', u'ᄇ'), + ] + +def _seg_52(): + return [ + (0xFFB3, 'M', u'ᄈ'), + (0xFFB4, 'M', u'ᄡ'), + (0xFFB5, 'M', u'ᄉ'), + (0xFFB6, 'M', u'ᄊ'), + (0xFFB7, 'M', u'ᄋ'), + (0xFFB8, 'M', u'ᄌ'), + (0xFFB9, 'M', u'ᄍ'), + (0xFFBA, 'M', u'ᄎ'), + (0xFFBB, 'M', u'ᄏ'), + (0xFFBC, 'M', u'ᄐ'), + (0xFFBD, 'M', u'ᄑ'), + (0xFFBE, 'M', u'ᄒ'), + (0xFFBF, 'X'), + (0xFFC2, 'M', u'ᅡ'), + (0xFFC3, 'M', u'ᅢ'), + (0xFFC4, 'M', u'ᅣ'), + (0xFFC5, 'M', u'ᅤ'), + (0xFFC6, 'M', u'ᅥ'), + (0xFFC7, 'M', u'ᅦ'), + (0xFFC8, 'X'), + (0xFFCA, 'M', u'ᅧ'), + (0xFFCB, 'M', u'ᅨ'), + (0xFFCC, 'M', u'ᅩ'), + (0xFFCD, 'M', u'ᅪ'), + (0xFFCE, 'M', u'ᅫ'), + (0xFFCF, 'M', u'ᅬ'), + (0xFFD0, 'X'), + (0xFFD2, 'M', u'ᅭ'), + (0xFFD3, 'M', u'ᅮ'), + (0xFFD4, 'M', u'ᅯ'), + (0xFFD5, 'M', u'ᅰ'), + (0xFFD6, 'M', u'ᅱ'), + (0xFFD7, 'M', u'ᅲ'), + (0xFFD8, 'X'), + (0xFFDA, 'M', u'ᅳ'), + (0xFFDB, 'M', u'ᅴ'), + (0xFFDC, 'M', u'ᅵ'), + (0xFFDD, 'X'), + (0xFFE0, 'M', u'¢'), + (0xFFE1, 'M', u'£'), + (0xFFE2, 'M', u'¬'), + (0xFFE3, '3', u' ̄'), + (0xFFE4, 'M', u'¦'), + (0xFFE5, 'M', u'¥'), + (0xFFE6, 'M', u'₩'), + (0xFFE7, 'X'), + (0xFFE8, 'M', u'│'), + (0xFFE9, 'M', u'←'), + (0xFFEA, 'M', u'↑'), + (0xFFEB, 'M', u'→'), + (0xFFEC, 'M', u'↓'), + (0xFFED, 'M', u'■'), + (0xFFEE, 'M', u'○'), + (0xFFEF, 'X'), + (0x10000, 'V'), + (0x1000C, 'X'), + (0x1000D, 'V'), + (0x10027, 'X'), + (0x10028, 'V'), + (0x1003B, 'X'), + (0x1003C, 'V'), + (0x1003E, 'X'), + (0x1003F, 'V'), + (0x1004E, 'X'), + (0x10050, 'V'), + (0x1005E, 'X'), + (0x10080, 'V'), + (0x100FB, 'X'), + (0x10100, 'V'), + (0x10103, 'X'), + (0x10107, 'V'), + (0x10134, 'X'), + (0x10137, 'V'), + (0x1018F, 'X'), + (0x10190, 'V'), + (0x1019C, 'X'), + (0x101A0, 'V'), + (0x101A1, 'X'), + (0x101D0, 'V'), + (0x101FE, 'X'), + (0x10280, 'V'), + (0x1029D, 'X'), + (0x102A0, 'V'), + (0x102D1, 'X'), + (0x102E0, 'V'), + (0x102FC, 'X'), + (0x10300, 'V'), + (0x10324, 'X'), + (0x1032D, 'V'), + (0x1034B, 'X'), + (0x10350, 'V'), + (0x1037B, 'X'), + (0x10380, 'V'), + (0x1039E, 'X'), + (0x1039F, 'V'), + (0x103C4, 'X'), + (0x103C8, 'V'), + (0x103D6, 'X'), + (0x10400, 'M', u'𐐨'), + (0x10401, 'M', u'𐐩'), + ] + +def _seg_53(): + return [ + (0x10402, 'M', u'𐐪'), + (0x10403, 'M', u'𐐫'), + (0x10404, 'M', u'𐐬'), + (0x10405, 'M', u'𐐭'), + (0x10406, 'M', u'𐐮'), + (0x10407, 'M', u'𐐯'), + (0x10408, 'M', u'𐐰'), + (0x10409, 'M', u'𐐱'), + (0x1040A, 'M', u'𐐲'), + (0x1040B, 'M', u'𐐳'), + (0x1040C, 'M', u'𐐴'), + (0x1040D, 'M', u'𐐵'), + (0x1040E, 'M', u'𐐶'), + (0x1040F, 'M', u'𐐷'), + (0x10410, 'M', u'𐐸'), + (0x10411, 'M', u'𐐹'), + (0x10412, 'M', u'𐐺'), + (0x10413, 'M', u'𐐻'), + (0x10414, 'M', u'𐐼'), + (0x10415, 'M', u'𐐽'), + (0x10416, 'M', u'𐐾'), + (0x10417, 'M', u'𐐿'), + (0x10418, 'M', u'𐑀'), + (0x10419, 'M', u'𐑁'), + (0x1041A, 'M', u'𐑂'), + (0x1041B, 'M', u'𐑃'), + (0x1041C, 'M', u'𐑄'), + (0x1041D, 'M', u'𐑅'), + (0x1041E, 'M', u'𐑆'), + (0x1041F, 'M', u'𐑇'), + (0x10420, 'M', u'𐑈'), + (0x10421, 'M', u'𐑉'), + (0x10422, 'M', u'𐑊'), + (0x10423, 'M', u'𐑋'), + (0x10424, 'M', u'𐑌'), + (0x10425, 'M', u'𐑍'), + (0x10426, 'M', u'𐑎'), + (0x10427, 'M', u'𐑏'), + (0x10428, 'V'), + (0x1049E, 'X'), + (0x104A0, 'V'), + (0x104AA, 'X'), + (0x104B0, 'M', u'𐓘'), + (0x104B1, 'M', u'𐓙'), + (0x104B2, 'M', u'𐓚'), + (0x104B3, 'M', u'𐓛'), + (0x104B4, 'M', u'𐓜'), + (0x104B5, 'M', u'𐓝'), + (0x104B6, 'M', u'𐓞'), + (0x104B7, 'M', u'𐓟'), + (0x104B8, 'M', u'𐓠'), + (0x104B9, 'M', u'𐓡'), + (0x104BA, 'M', u'𐓢'), + (0x104BB, 'M', u'𐓣'), + (0x104BC, 'M', u'𐓤'), + (0x104BD, 'M', u'𐓥'), + (0x104BE, 'M', u'𐓦'), + (0x104BF, 'M', u'𐓧'), + (0x104C0, 'M', u'𐓨'), + (0x104C1, 'M', u'𐓩'), + (0x104C2, 'M', u'𐓪'), + (0x104C3, 'M', u'𐓫'), + (0x104C4, 'M', u'𐓬'), + (0x104C5, 'M', u'𐓭'), + (0x104C6, 'M', u'𐓮'), + (0x104C7, 'M', u'𐓯'), + (0x104C8, 'M', u'𐓰'), + (0x104C9, 'M', u'𐓱'), + (0x104CA, 'M', u'𐓲'), + (0x104CB, 'M', u'𐓳'), + (0x104CC, 'M', u'𐓴'), + (0x104CD, 'M', u'𐓵'), + (0x104CE, 'M', u'𐓶'), + (0x104CF, 'M', u'𐓷'), + (0x104D0, 'M', u'𐓸'), + (0x104D1, 'M', u'𐓹'), + (0x104D2, 'M', u'𐓺'), + (0x104D3, 'M', u'𐓻'), + (0x104D4, 'X'), + (0x104D8, 'V'), + (0x104FC, 'X'), + (0x10500, 'V'), + (0x10528, 'X'), + (0x10530, 'V'), + (0x10564, 'X'), + (0x1056F, 'V'), + (0x10570, 'X'), + (0x10600, 'V'), + (0x10737, 'X'), + (0x10740, 'V'), + (0x10756, 'X'), + (0x10760, 'V'), + (0x10768, 'X'), + (0x10800, 'V'), + (0x10806, 'X'), + (0x10808, 'V'), + (0x10809, 'X'), + (0x1080A, 'V'), + (0x10836, 'X'), + (0x10837, 'V'), + ] + +def _seg_54(): + return [ + (0x10839, 'X'), + (0x1083C, 'V'), + (0x1083D, 'X'), + (0x1083F, 'V'), + (0x10856, 'X'), + (0x10857, 'V'), + (0x1089F, 'X'), + (0x108A7, 'V'), + (0x108B0, 'X'), + (0x108E0, 'V'), + (0x108F3, 'X'), + (0x108F4, 'V'), + (0x108F6, 'X'), + (0x108FB, 'V'), + (0x1091C, 'X'), + (0x1091F, 'V'), + (0x1093A, 'X'), + (0x1093F, 'V'), + (0x10940, 'X'), + (0x10980, 'V'), + (0x109B8, 'X'), + (0x109BC, 'V'), + (0x109D0, 'X'), + (0x109D2, 'V'), + (0x10A04, 'X'), + (0x10A05, 'V'), + (0x10A07, 'X'), + (0x10A0C, 'V'), + (0x10A14, 'X'), + (0x10A15, 'V'), + (0x10A18, 'X'), + (0x10A19, 'V'), + (0x10A36, 'X'), + (0x10A38, 'V'), + (0x10A3B, 'X'), + (0x10A3F, 'V'), + (0x10A49, 'X'), + (0x10A50, 'V'), + (0x10A59, 'X'), + (0x10A60, 'V'), + (0x10AA0, 'X'), + (0x10AC0, 'V'), + (0x10AE7, 'X'), + (0x10AEB, 'V'), + (0x10AF7, 'X'), + (0x10B00, 'V'), + (0x10B36, 'X'), + (0x10B39, 'V'), + (0x10B56, 'X'), + (0x10B58, 'V'), + (0x10B73, 'X'), + (0x10B78, 'V'), + (0x10B92, 'X'), + (0x10B99, 'V'), + (0x10B9D, 'X'), + (0x10BA9, 'V'), + (0x10BB0, 'X'), + (0x10C00, 'V'), + (0x10C49, 'X'), + (0x10C80, 'M', u'𐳀'), + (0x10C81, 'M', u'𐳁'), + (0x10C82, 'M', u'𐳂'), + (0x10C83, 'M', u'𐳃'), + (0x10C84, 'M', u'𐳄'), + (0x10C85, 'M', u'𐳅'), + (0x10C86, 'M', u'𐳆'), + (0x10C87, 'M', u'𐳇'), + (0x10C88, 'M', u'𐳈'), + (0x10C89, 'M', u'𐳉'), + (0x10C8A, 'M', u'𐳊'), + (0x10C8B, 'M', u'𐳋'), + (0x10C8C, 'M', u'𐳌'), + (0x10C8D, 'M', u'𐳍'), + (0x10C8E, 'M', u'𐳎'), + (0x10C8F, 'M', u'𐳏'), + (0x10C90, 'M', u'𐳐'), + (0x10C91, 'M', u'𐳑'), + (0x10C92, 'M', u'𐳒'), + (0x10C93, 'M', u'𐳓'), + (0x10C94, 'M', u'𐳔'), + (0x10C95, 'M', u'𐳕'), + (0x10C96, 'M', u'𐳖'), + (0x10C97, 'M', u'𐳗'), + (0x10C98, 'M', u'𐳘'), + (0x10C99, 'M', u'𐳙'), + (0x10C9A, 'M', u'𐳚'), + (0x10C9B, 'M', u'𐳛'), + (0x10C9C, 'M', u'𐳜'), + (0x10C9D, 'M', u'𐳝'), + (0x10C9E, 'M', u'𐳞'), + (0x10C9F, 'M', u'𐳟'), + (0x10CA0, 'M', u'𐳠'), + (0x10CA1, 'M', u'𐳡'), + (0x10CA2, 'M', u'𐳢'), + (0x10CA3, 'M', u'𐳣'), + (0x10CA4, 'M', u'𐳤'), + (0x10CA5, 'M', u'𐳥'), + (0x10CA6, 'M', u'𐳦'), + (0x10CA7, 'M', u'𐳧'), + (0x10CA8, 'M', u'𐳨'), + ] + +def _seg_55(): + return [ + (0x10CA9, 'M', u'𐳩'), + (0x10CAA, 'M', u'𐳪'), + (0x10CAB, 'M', u'𐳫'), + (0x10CAC, 'M', u'𐳬'), + (0x10CAD, 'M', u'𐳭'), + (0x10CAE, 'M', u'𐳮'), + (0x10CAF, 'M', u'𐳯'), + (0x10CB0, 'M', u'𐳰'), + (0x10CB1, 'M', u'𐳱'), + (0x10CB2, 'M', u'𐳲'), + (0x10CB3, 'X'), + (0x10CC0, 'V'), + (0x10CF3, 'X'), + (0x10CFA, 'V'), + (0x10D28, 'X'), + (0x10D30, 'V'), + (0x10D3A, 'X'), + (0x10E60, 'V'), + (0x10E7F, 'X'), + (0x10F00, 'V'), + (0x10F28, 'X'), + (0x10F30, 'V'), + (0x10F5A, 'X'), + (0x11000, 'V'), + (0x1104E, 'X'), + (0x11052, 'V'), + (0x11070, 'X'), + (0x1107F, 'V'), + (0x110BD, 'X'), + (0x110BE, 'V'), + (0x110C2, 'X'), + (0x110D0, 'V'), + (0x110E9, 'X'), + (0x110F0, 'V'), + (0x110FA, 'X'), + (0x11100, 'V'), + (0x11135, 'X'), + (0x11136, 'V'), + (0x11147, 'X'), + (0x11150, 'V'), + (0x11177, 'X'), + (0x11180, 'V'), + (0x111CE, 'X'), + (0x111D0, 'V'), + (0x111E0, 'X'), + (0x111E1, 'V'), + (0x111F5, 'X'), + (0x11200, 'V'), + (0x11212, 'X'), + (0x11213, 'V'), + (0x1123F, 'X'), + (0x11280, 'V'), + (0x11287, 'X'), + (0x11288, 'V'), + (0x11289, 'X'), + (0x1128A, 'V'), + (0x1128E, 'X'), + (0x1128F, 'V'), + (0x1129E, 'X'), + (0x1129F, 'V'), + (0x112AA, 'X'), + (0x112B0, 'V'), + (0x112EB, 'X'), + (0x112F0, 'V'), + (0x112FA, 'X'), + (0x11300, 'V'), + (0x11304, 'X'), + (0x11305, 'V'), + (0x1130D, 'X'), + (0x1130F, 'V'), + (0x11311, 'X'), + (0x11313, 'V'), + (0x11329, 'X'), + (0x1132A, 'V'), + (0x11331, 'X'), + (0x11332, 'V'), + (0x11334, 'X'), + (0x11335, 'V'), + (0x1133A, 'X'), + (0x1133B, 'V'), + (0x11345, 'X'), + (0x11347, 'V'), + (0x11349, 'X'), + (0x1134B, 'V'), + (0x1134E, 'X'), + (0x11350, 'V'), + (0x11351, 'X'), + (0x11357, 'V'), + (0x11358, 'X'), + (0x1135D, 'V'), + (0x11364, 'X'), + (0x11366, 'V'), + (0x1136D, 'X'), + (0x11370, 'V'), + (0x11375, 'X'), + (0x11400, 'V'), + (0x1145A, 'X'), + (0x1145B, 'V'), + (0x1145C, 'X'), + (0x1145D, 'V'), + ] + +def _seg_56(): + return [ + (0x1145F, 'X'), + (0x11480, 'V'), + (0x114C8, 'X'), + (0x114D0, 'V'), + (0x114DA, 'X'), + (0x11580, 'V'), + (0x115B6, 'X'), + (0x115B8, 'V'), + (0x115DE, 'X'), + (0x11600, 'V'), + (0x11645, 'X'), + (0x11650, 'V'), + (0x1165A, 'X'), + (0x11660, 'V'), + (0x1166D, 'X'), + (0x11680, 'V'), + (0x116B8, 'X'), + (0x116C0, 'V'), + (0x116CA, 'X'), + (0x11700, 'V'), + (0x1171B, 'X'), + (0x1171D, 'V'), + (0x1172C, 'X'), + (0x11730, 'V'), + (0x11740, 'X'), + (0x11800, 'V'), + (0x1183C, 'X'), + (0x118A0, 'M', u'𑣀'), + (0x118A1, 'M', u'𑣁'), + (0x118A2, 'M', u'𑣂'), + (0x118A3, 'M', u'𑣃'), + (0x118A4, 'M', u'𑣄'), + (0x118A5, 'M', u'𑣅'), + (0x118A6, 'M', u'𑣆'), + (0x118A7, 'M', u'𑣇'), + (0x118A8, 'M', u'𑣈'), + (0x118A9, 'M', u'𑣉'), + (0x118AA, 'M', u'𑣊'), + (0x118AB, 'M', u'𑣋'), + (0x118AC, 'M', u'𑣌'), + (0x118AD, 'M', u'𑣍'), + (0x118AE, 'M', u'𑣎'), + (0x118AF, 'M', u'𑣏'), + (0x118B0, 'M', u'𑣐'), + (0x118B1, 'M', u'𑣑'), + (0x118B2, 'M', u'𑣒'), + (0x118B3, 'M', u'𑣓'), + (0x118B4, 'M', u'𑣔'), + (0x118B5, 'M', u'𑣕'), + (0x118B6, 'M', u'𑣖'), + (0x118B7, 'M', u'𑣗'), + (0x118B8, 'M', u'𑣘'), + (0x118B9, 'M', u'𑣙'), + (0x118BA, 'M', u'𑣚'), + (0x118BB, 'M', u'𑣛'), + (0x118BC, 'M', u'𑣜'), + (0x118BD, 'M', u'𑣝'), + (0x118BE, 'M', u'𑣞'), + (0x118BF, 'M', u'𑣟'), + (0x118C0, 'V'), + (0x118F3, 'X'), + (0x118FF, 'V'), + (0x11900, 'X'), + (0x11A00, 'V'), + (0x11A48, 'X'), + (0x11A50, 'V'), + (0x11A84, 'X'), + (0x11A86, 'V'), + (0x11AA3, 'X'), + (0x11AC0, 'V'), + (0x11AF9, 'X'), + (0x11C00, 'V'), + (0x11C09, 'X'), + (0x11C0A, 'V'), + (0x11C37, 'X'), + (0x11C38, 'V'), + (0x11C46, 'X'), + (0x11C50, 'V'), + (0x11C6D, 'X'), + (0x11C70, 'V'), + (0x11C90, 'X'), + (0x11C92, 'V'), + (0x11CA8, 'X'), + (0x11CA9, 'V'), + (0x11CB7, 'X'), + (0x11D00, 'V'), + (0x11D07, 'X'), + (0x11D08, 'V'), + (0x11D0A, 'X'), + (0x11D0B, 'V'), + (0x11D37, 'X'), + (0x11D3A, 'V'), + (0x11D3B, 'X'), + (0x11D3C, 'V'), + (0x11D3E, 'X'), + (0x11D3F, 'V'), + (0x11D48, 'X'), + (0x11D50, 'V'), + (0x11D5A, 'X'), + (0x11D60, 'V'), + ] + +def _seg_57(): + return [ + (0x11D66, 'X'), + (0x11D67, 'V'), + (0x11D69, 'X'), + (0x11D6A, 'V'), + (0x11D8F, 'X'), + (0x11D90, 'V'), + (0x11D92, 'X'), + (0x11D93, 'V'), + (0x11D99, 'X'), + (0x11DA0, 'V'), + (0x11DAA, 'X'), + (0x11EE0, 'V'), + (0x11EF9, 'X'), + (0x12000, 'V'), + (0x1239A, 'X'), + (0x12400, 'V'), + (0x1246F, 'X'), + (0x12470, 'V'), + (0x12475, 'X'), + (0x12480, 'V'), + (0x12544, 'X'), + (0x13000, 'V'), + (0x1342F, 'X'), + (0x14400, 'V'), + (0x14647, 'X'), + (0x16800, 'V'), + (0x16A39, 'X'), + (0x16A40, 'V'), + (0x16A5F, 'X'), + (0x16A60, 'V'), + (0x16A6A, 'X'), + (0x16A6E, 'V'), + (0x16A70, 'X'), + (0x16AD0, 'V'), + (0x16AEE, 'X'), + (0x16AF0, 'V'), + (0x16AF6, 'X'), + (0x16B00, 'V'), + (0x16B46, 'X'), + (0x16B50, 'V'), + (0x16B5A, 'X'), + (0x16B5B, 'V'), + (0x16B62, 'X'), + (0x16B63, 'V'), + (0x16B78, 'X'), + (0x16B7D, 'V'), + (0x16B90, 'X'), + (0x16E60, 'V'), + (0x16E9B, 'X'), + (0x16F00, 'V'), + (0x16F45, 'X'), + (0x16F50, 'V'), + (0x16F7F, 'X'), + (0x16F8F, 'V'), + (0x16FA0, 'X'), + (0x16FE0, 'V'), + (0x16FE2, 'X'), + (0x17000, 'V'), + (0x187F2, 'X'), + (0x18800, 'V'), + (0x18AF3, 'X'), + (0x1B000, 'V'), + (0x1B11F, 'X'), + (0x1B170, 'V'), + (0x1B2FC, 'X'), + (0x1BC00, 'V'), + (0x1BC6B, 'X'), + (0x1BC70, 'V'), + (0x1BC7D, 'X'), + (0x1BC80, 'V'), + (0x1BC89, 'X'), + (0x1BC90, 'V'), + (0x1BC9A, 'X'), + (0x1BC9C, 'V'), + (0x1BCA0, 'I'), + (0x1BCA4, 'X'), + (0x1D000, 'V'), + (0x1D0F6, 'X'), + (0x1D100, 'V'), + (0x1D127, 'X'), + (0x1D129, 'V'), + (0x1D15E, 'M', u'𝅗𝅥'), + (0x1D15F, 'M', u'𝅘𝅥'), + (0x1D160, 'M', u'𝅘𝅥𝅮'), + (0x1D161, 'M', u'𝅘𝅥𝅯'), + (0x1D162, 'M', u'𝅘𝅥𝅰'), + (0x1D163, 'M', u'𝅘𝅥𝅱'), + (0x1D164, 'M', u'𝅘𝅥𝅲'), + (0x1D165, 'V'), + (0x1D173, 'X'), + (0x1D17B, 'V'), + (0x1D1BB, 'M', u'𝆹𝅥'), + (0x1D1BC, 'M', u'𝆺𝅥'), + (0x1D1BD, 'M', u'𝆹𝅥𝅮'), + (0x1D1BE, 'M', u'𝆺𝅥𝅮'), + (0x1D1BF, 'M', u'𝆹𝅥𝅯'), + (0x1D1C0, 'M', u'𝆺𝅥𝅯'), + (0x1D1C1, 'V'), + (0x1D1E9, 'X'), + (0x1D200, 'V'), + ] + +def _seg_58(): + return [ + (0x1D246, 'X'), + (0x1D2E0, 'V'), + (0x1D2F4, 'X'), + (0x1D300, 'V'), + (0x1D357, 'X'), + (0x1D360, 'V'), + (0x1D379, 'X'), + (0x1D400, 'M', u'a'), + (0x1D401, 'M', u'b'), + (0x1D402, 'M', u'c'), + (0x1D403, 'M', u'd'), + (0x1D404, 'M', u'e'), + (0x1D405, 'M', u'f'), + (0x1D406, 'M', u'g'), + (0x1D407, 'M', u'h'), + (0x1D408, 'M', u'i'), + (0x1D409, 'M', u'j'), + (0x1D40A, 'M', u'k'), + (0x1D40B, 'M', u'l'), + (0x1D40C, 'M', u'm'), + (0x1D40D, 'M', u'n'), + (0x1D40E, 'M', u'o'), + (0x1D40F, 'M', u'p'), + (0x1D410, 'M', u'q'), + (0x1D411, 'M', u'r'), + (0x1D412, 'M', u's'), + (0x1D413, 'M', u't'), + (0x1D414, 'M', u'u'), + (0x1D415, 'M', u'v'), + (0x1D416, 'M', u'w'), + (0x1D417, 'M', u'x'), + (0x1D418, 'M', u'y'), + (0x1D419, 'M', u'z'), + (0x1D41A, 'M', u'a'), + (0x1D41B, 'M', u'b'), + (0x1D41C, 'M', u'c'), + (0x1D41D, 'M', u'd'), + (0x1D41E, 'M', u'e'), + (0x1D41F, 'M', u'f'), + (0x1D420, 'M', u'g'), + (0x1D421, 'M', u'h'), + (0x1D422, 'M', u'i'), + (0x1D423, 'M', u'j'), + (0x1D424, 'M', u'k'), + (0x1D425, 'M', u'l'), + (0x1D426, 'M', u'm'), + (0x1D427, 'M', u'n'), + (0x1D428, 'M', u'o'), + (0x1D429, 'M', u'p'), + (0x1D42A, 'M', u'q'), + (0x1D42B, 'M', u'r'), + (0x1D42C, 'M', u's'), + (0x1D42D, 'M', u't'), + (0x1D42E, 'M', u'u'), + (0x1D42F, 'M', u'v'), + (0x1D430, 'M', u'w'), + (0x1D431, 'M', u'x'), + (0x1D432, 'M', u'y'), + (0x1D433, 'M', u'z'), + (0x1D434, 'M', u'a'), + (0x1D435, 'M', u'b'), + (0x1D436, 'M', u'c'), + (0x1D437, 'M', u'd'), + (0x1D438, 'M', u'e'), + (0x1D439, 'M', u'f'), + (0x1D43A, 'M', u'g'), + (0x1D43B, 'M', u'h'), + (0x1D43C, 'M', u'i'), + (0x1D43D, 'M', u'j'), + (0x1D43E, 'M', u'k'), + (0x1D43F, 'M', u'l'), + (0x1D440, 'M', u'm'), + (0x1D441, 'M', u'n'), + (0x1D442, 'M', u'o'), + (0x1D443, 'M', u'p'), + (0x1D444, 'M', u'q'), + (0x1D445, 'M', u'r'), + (0x1D446, 'M', u's'), + (0x1D447, 'M', u't'), + (0x1D448, 'M', u'u'), + (0x1D449, 'M', u'v'), + (0x1D44A, 'M', u'w'), + (0x1D44B, 'M', u'x'), + (0x1D44C, 'M', u'y'), + (0x1D44D, 'M', u'z'), + (0x1D44E, 'M', u'a'), + (0x1D44F, 'M', u'b'), + (0x1D450, 'M', u'c'), + (0x1D451, 'M', u'd'), + (0x1D452, 'M', u'e'), + (0x1D453, 'M', u'f'), + (0x1D454, 'M', u'g'), + (0x1D455, 'X'), + (0x1D456, 'M', u'i'), + (0x1D457, 'M', u'j'), + (0x1D458, 'M', u'k'), + (0x1D459, 'M', u'l'), + (0x1D45A, 'M', u'm'), + (0x1D45B, 'M', u'n'), + (0x1D45C, 'M', u'o'), + ] + +def _seg_59(): + return [ + (0x1D45D, 'M', u'p'), + (0x1D45E, 'M', u'q'), + (0x1D45F, 'M', u'r'), + (0x1D460, 'M', u's'), + (0x1D461, 'M', u't'), + (0x1D462, 'M', u'u'), + (0x1D463, 'M', u'v'), + (0x1D464, 'M', u'w'), + (0x1D465, 'M', u'x'), + (0x1D466, 'M', u'y'), + (0x1D467, 'M', u'z'), + (0x1D468, 'M', u'a'), + (0x1D469, 'M', u'b'), + (0x1D46A, 'M', u'c'), + (0x1D46B, 'M', u'd'), + (0x1D46C, 'M', u'e'), + (0x1D46D, 'M', u'f'), + (0x1D46E, 'M', u'g'), + (0x1D46F, 'M', u'h'), + (0x1D470, 'M', u'i'), + (0x1D471, 'M', u'j'), + (0x1D472, 'M', u'k'), + (0x1D473, 'M', u'l'), + (0x1D474, 'M', u'm'), + (0x1D475, 'M', u'n'), + (0x1D476, 'M', u'o'), + (0x1D477, 'M', u'p'), + (0x1D478, 'M', u'q'), + (0x1D479, 'M', u'r'), + (0x1D47A, 'M', u's'), + (0x1D47B, 'M', u't'), + (0x1D47C, 'M', u'u'), + (0x1D47D, 'M', u'v'), + (0x1D47E, 'M', u'w'), + (0x1D47F, 'M', u'x'), + (0x1D480, 'M', u'y'), + (0x1D481, 'M', u'z'), + (0x1D482, 'M', u'a'), + (0x1D483, 'M', u'b'), + (0x1D484, 'M', u'c'), + (0x1D485, 'M', u'd'), + (0x1D486, 'M', u'e'), + (0x1D487, 'M', u'f'), + (0x1D488, 'M', u'g'), + (0x1D489, 'M', u'h'), + (0x1D48A, 'M', u'i'), + (0x1D48B, 'M', u'j'), + (0x1D48C, 'M', u'k'), + (0x1D48D, 'M', u'l'), + (0x1D48E, 'M', u'm'), + (0x1D48F, 'M', u'n'), + (0x1D490, 'M', u'o'), + (0x1D491, 'M', u'p'), + (0x1D492, 'M', u'q'), + (0x1D493, 'M', u'r'), + (0x1D494, 'M', u's'), + (0x1D495, 'M', u't'), + (0x1D496, 'M', u'u'), + (0x1D497, 'M', u'v'), + (0x1D498, 'M', u'w'), + (0x1D499, 'M', u'x'), + (0x1D49A, 'M', u'y'), + (0x1D49B, 'M', u'z'), + (0x1D49C, 'M', u'a'), + (0x1D49D, 'X'), + (0x1D49E, 'M', u'c'), + (0x1D49F, 'M', u'd'), + (0x1D4A0, 'X'), + (0x1D4A2, 'M', u'g'), + (0x1D4A3, 'X'), + (0x1D4A5, 'M', u'j'), + (0x1D4A6, 'M', u'k'), + (0x1D4A7, 'X'), + (0x1D4A9, 'M', u'n'), + (0x1D4AA, 'M', u'o'), + (0x1D4AB, 'M', u'p'), + (0x1D4AC, 'M', u'q'), + (0x1D4AD, 'X'), + (0x1D4AE, 'M', u's'), + (0x1D4AF, 'M', u't'), + (0x1D4B0, 'M', u'u'), + (0x1D4B1, 'M', u'v'), + (0x1D4B2, 'M', u'w'), + (0x1D4B3, 'M', u'x'), + (0x1D4B4, 'M', u'y'), + (0x1D4B5, 'M', u'z'), + (0x1D4B6, 'M', u'a'), + (0x1D4B7, 'M', u'b'), + (0x1D4B8, 'M', u'c'), + (0x1D4B9, 'M', u'd'), + (0x1D4BA, 'X'), + (0x1D4BB, 'M', u'f'), + (0x1D4BC, 'X'), + (0x1D4BD, 'M', u'h'), + (0x1D4BE, 'M', u'i'), + (0x1D4BF, 'M', u'j'), + (0x1D4C0, 'M', u'k'), + (0x1D4C1, 'M', u'l'), + (0x1D4C2, 'M', u'm'), + (0x1D4C3, 'M', u'n'), + ] + +def _seg_60(): + return [ + (0x1D4C4, 'X'), + (0x1D4C5, 'M', u'p'), + (0x1D4C6, 'M', u'q'), + (0x1D4C7, 'M', u'r'), + (0x1D4C8, 'M', u's'), + (0x1D4C9, 'M', u't'), + (0x1D4CA, 'M', u'u'), + (0x1D4CB, 'M', u'v'), + (0x1D4CC, 'M', u'w'), + (0x1D4CD, 'M', u'x'), + (0x1D4CE, 'M', u'y'), + (0x1D4CF, 'M', u'z'), + (0x1D4D0, 'M', u'a'), + (0x1D4D1, 'M', u'b'), + (0x1D4D2, 'M', u'c'), + (0x1D4D3, 'M', u'd'), + (0x1D4D4, 'M', u'e'), + (0x1D4D5, 'M', u'f'), + (0x1D4D6, 'M', u'g'), + (0x1D4D7, 'M', u'h'), + (0x1D4D8, 'M', u'i'), + (0x1D4D9, 'M', u'j'), + (0x1D4DA, 'M', u'k'), + (0x1D4DB, 'M', u'l'), + (0x1D4DC, 'M', u'm'), + (0x1D4DD, 'M', u'n'), + (0x1D4DE, 'M', u'o'), + (0x1D4DF, 'M', u'p'), + (0x1D4E0, 'M', u'q'), + (0x1D4E1, 'M', u'r'), + (0x1D4E2, 'M', u's'), + (0x1D4E3, 'M', u't'), + (0x1D4E4, 'M', u'u'), + (0x1D4E5, 'M', u'v'), + (0x1D4E6, 'M', u'w'), + (0x1D4E7, 'M', u'x'), + (0x1D4E8, 'M', u'y'), + (0x1D4E9, 'M', u'z'), + (0x1D4EA, 'M', u'a'), + (0x1D4EB, 'M', u'b'), + (0x1D4EC, 'M', u'c'), + (0x1D4ED, 'M', u'd'), + (0x1D4EE, 'M', u'e'), + (0x1D4EF, 'M', u'f'), + (0x1D4F0, 'M', u'g'), + (0x1D4F1, 'M', u'h'), + (0x1D4F2, 'M', u'i'), + (0x1D4F3, 'M', u'j'), + (0x1D4F4, 'M', u'k'), + (0x1D4F5, 'M', u'l'), + (0x1D4F6, 'M', u'm'), + (0x1D4F7, 'M', u'n'), + (0x1D4F8, 'M', u'o'), + (0x1D4F9, 'M', u'p'), + (0x1D4FA, 'M', u'q'), + (0x1D4FB, 'M', u'r'), + (0x1D4FC, 'M', u's'), + (0x1D4FD, 'M', u't'), + (0x1D4FE, 'M', u'u'), + (0x1D4FF, 'M', u'v'), + (0x1D500, 'M', u'w'), + (0x1D501, 'M', u'x'), + (0x1D502, 'M', u'y'), + (0x1D503, 'M', u'z'), + (0x1D504, 'M', u'a'), + (0x1D505, 'M', u'b'), + (0x1D506, 'X'), + (0x1D507, 'M', u'd'), + (0x1D508, 'M', u'e'), + (0x1D509, 'M', u'f'), + (0x1D50A, 'M', u'g'), + (0x1D50B, 'X'), + (0x1D50D, 'M', u'j'), + (0x1D50E, 'M', u'k'), + (0x1D50F, 'M', u'l'), + (0x1D510, 'M', u'm'), + (0x1D511, 'M', u'n'), + (0x1D512, 'M', u'o'), + (0x1D513, 'M', u'p'), + (0x1D514, 'M', u'q'), + (0x1D515, 'X'), + (0x1D516, 'M', u's'), + (0x1D517, 'M', u't'), + (0x1D518, 'M', u'u'), + (0x1D519, 'M', u'v'), + (0x1D51A, 'M', u'w'), + (0x1D51B, 'M', u'x'), + (0x1D51C, 'M', u'y'), + (0x1D51D, 'X'), + (0x1D51E, 'M', u'a'), + (0x1D51F, 'M', u'b'), + (0x1D520, 'M', u'c'), + (0x1D521, 'M', u'd'), + (0x1D522, 'M', u'e'), + (0x1D523, 'M', u'f'), + (0x1D524, 'M', u'g'), + (0x1D525, 'M', u'h'), + (0x1D526, 'M', u'i'), + (0x1D527, 'M', u'j'), + (0x1D528, 'M', u'k'), + ] + +def _seg_61(): + return [ + (0x1D529, 'M', u'l'), + (0x1D52A, 'M', u'm'), + (0x1D52B, 'M', u'n'), + (0x1D52C, 'M', u'o'), + (0x1D52D, 'M', u'p'), + (0x1D52E, 'M', u'q'), + (0x1D52F, 'M', u'r'), + (0x1D530, 'M', u's'), + (0x1D531, 'M', u't'), + (0x1D532, 'M', u'u'), + (0x1D533, 'M', u'v'), + (0x1D534, 'M', u'w'), + (0x1D535, 'M', u'x'), + (0x1D536, 'M', u'y'), + (0x1D537, 'M', u'z'), + (0x1D538, 'M', u'a'), + (0x1D539, 'M', u'b'), + (0x1D53A, 'X'), + (0x1D53B, 'M', u'd'), + (0x1D53C, 'M', u'e'), + (0x1D53D, 'M', u'f'), + (0x1D53E, 'M', u'g'), + (0x1D53F, 'X'), + (0x1D540, 'M', u'i'), + (0x1D541, 'M', u'j'), + (0x1D542, 'M', u'k'), + (0x1D543, 'M', u'l'), + (0x1D544, 'M', u'm'), + (0x1D545, 'X'), + (0x1D546, 'M', u'o'), + (0x1D547, 'X'), + (0x1D54A, 'M', u's'), + (0x1D54B, 'M', u't'), + (0x1D54C, 'M', u'u'), + (0x1D54D, 'M', u'v'), + (0x1D54E, 'M', u'w'), + (0x1D54F, 'M', u'x'), + (0x1D550, 'M', u'y'), + (0x1D551, 'X'), + (0x1D552, 'M', u'a'), + (0x1D553, 'M', u'b'), + (0x1D554, 'M', u'c'), + (0x1D555, 'M', u'd'), + (0x1D556, 'M', u'e'), + (0x1D557, 'M', u'f'), + (0x1D558, 'M', u'g'), + (0x1D559, 'M', u'h'), + (0x1D55A, 'M', u'i'), + (0x1D55B, 'M', u'j'), + (0x1D55C, 'M', u'k'), + (0x1D55D, 'M', u'l'), + (0x1D55E, 'M', u'm'), + (0x1D55F, 'M', u'n'), + (0x1D560, 'M', u'o'), + (0x1D561, 'M', u'p'), + (0x1D562, 'M', u'q'), + (0x1D563, 'M', u'r'), + (0x1D564, 'M', u's'), + (0x1D565, 'M', u't'), + (0x1D566, 'M', u'u'), + (0x1D567, 'M', u'v'), + (0x1D568, 'M', u'w'), + (0x1D569, 'M', u'x'), + (0x1D56A, 'M', u'y'), + (0x1D56B, 'M', u'z'), + (0x1D56C, 'M', u'a'), + (0x1D56D, 'M', u'b'), + (0x1D56E, 'M', u'c'), + (0x1D56F, 'M', u'd'), + (0x1D570, 'M', u'e'), + (0x1D571, 'M', u'f'), + (0x1D572, 'M', u'g'), + (0x1D573, 'M', u'h'), + (0x1D574, 'M', u'i'), + (0x1D575, 'M', u'j'), + (0x1D576, 'M', u'k'), + (0x1D577, 'M', u'l'), + (0x1D578, 'M', u'm'), + (0x1D579, 'M', u'n'), + (0x1D57A, 'M', u'o'), + (0x1D57B, 'M', u'p'), + (0x1D57C, 'M', u'q'), + (0x1D57D, 'M', u'r'), + (0x1D57E, 'M', u's'), + (0x1D57F, 'M', u't'), + (0x1D580, 'M', u'u'), + (0x1D581, 'M', u'v'), + (0x1D582, 'M', u'w'), + (0x1D583, 'M', u'x'), + (0x1D584, 'M', u'y'), + (0x1D585, 'M', u'z'), + (0x1D586, 'M', u'a'), + (0x1D587, 'M', u'b'), + (0x1D588, 'M', u'c'), + (0x1D589, 'M', u'd'), + (0x1D58A, 'M', u'e'), + (0x1D58B, 'M', u'f'), + (0x1D58C, 'M', u'g'), + (0x1D58D, 'M', u'h'), + (0x1D58E, 'M', u'i'), + ] + +def _seg_62(): + return [ + (0x1D58F, 'M', u'j'), + (0x1D590, 'M', u'k'), + (0x1D591, 'M', u'l'), + (0x1D592, 'M', u'm'), + (0x1D593, 'M', u'n'), + (0x1D594, 'M', u'o'), + (0x1D595, 'M', u'p'), + (0x1D596, 'M', u'q'), + (0x1D597, 'M', u'r'), + (0x1D598, 'M', u's'), + (0x1D599, 'M', u't'), + (0x1D59A, 'M', u'u'), + (0x1D59B, 'M', u'v'), + (0x1D59C, 'M', u'w'), + (0x1D59D, 'M', u'x'), + (0x1D59E, 'M', u'y'), + (0x1D59F, 'M', u'z'), + (0x1D5A0, 'M', u'a'), + (0x1D5A1, 'M', u'b'), + (0x1D5A2, 'M', u'c'), + (0x1D5A3, 'M', u'd'), + (0x1D5A4, 'M', u'e'), + (0x1D5A5, 'M', u'f'), + (0x1D5A6, 'M', u'g'), + (0x1D5A7, 'M', u'h'), + (0x1D5A8, 'M', u'i'), + (0x1D5A9, 'M', u'j'), + (0x1D5AA, 'M', u'k'), + (0x1D5AB, 'M', u'l'), + (0x1D5AC, 'M', u'm'), + (0x1D5AD, 'M', u'n'), + (0x1D5AE, 'M', u'o'), + (0x1D5AF, 'M', u'p'), + (0x1D5B0, 'M', u'q'), + (0x1D5B1, 'M', u'r'), + (0x1D5B2, 'M', u's'), + (0x1D5B3, 'M', u't'), + (0x1D5B4, 'M', u'u'), + (0x1D5B5, 'M', u'v'), + (0x1D5B6, 'M', u'w'), + (0x1D5B7, 'M', u'x'), + (0x1D5B8, 'M', u'y'), + (0x1D5B9, 'M', u'z'), + (0x1D5BA, 'M', u'a'), + (0x1D5BB, 'M', u'b'), + (0x1D5BC, 'M', u'c'), + (0x1D5BD, 'M', u'd'), + (0x1D5BE, 'M', u'e'), + (0x1D5BF, 'M', u'f'), + (0x1D5C0, 'M', u'g'), + (0x1D5C1, 'M', u'h'), + (0x1D5C2, 'M', u'i'), + (0x1D5C3, 'M', u'j'), + (0x1D5C4, 'M', u'k'), + (0x1D5C5, 'M', u'l'), + (0x1D5C6, 'M', u'm'), + (0x1D5C7, 'M', u'n'), + (0x1D5C8, 'M', u'o'), + (0x1D5C9, 'M', u'p'), + (0x1D5CA, 'M', u'q'), + (0x1D5CB, 'M', u'r'), + (0x1D5CC, 'M', u's'), + (0x1D5CD, 'M', u't'), + (0x1D5CE, 'M', u'u'), + (0x1D5CF, 'M', u'v'), + (0x1D5D0, 'M', u'w'), + (0x1D5D1, 'M', u'x'), + (0x1D5D2, 'M', u'y'), + (0x1D5D3, 'M', u'z'), + (0x1D5D4, 'M', u'a'), + (0x1D5D5, 'M', u'b'), + (0x1D5D6, 'M', u'c'), + (0x1D5D7, 'M', u'd'), + (0x1D5D8, 'M', u'e'), + (0x1D5D9, 'M', u'f'), + (0x1D5DA, 'M', u'g'), + (0x1D5DB, 'M', u'h'), + (0x1D5DC, 'M', u'i'), + (0x1D5DD, 'M', u'j'), + (0x1D5DE, 'M', u'k'), + (0x1D5DF, 'M', u'l'), + (0x1D5E0, 'M', u'm'), + (0x1D5E1, 'M', u'n'), + (0x1D5E2, 'M', u'o'), + (0x1D5E3, 'M', u'p'), + (0x1D5E4, 'M', u'q'), + (0x1D5E5, 'M', u'r'), + (0x1D5E6, 'M', u's'), + (0x1D5E7, 'M', u't'), + (0x1D5E8, 'M', u'u'), + (0x1D5E9, 'M', u'v'), + (0x1D5EA, 'M', u'w'), + (0x1D5EB, 'M', u'x'), + (0x1D5EC, 'M', u'y'), + (0x1D5ED, 'M', u'z'), + (0x1D5EE, 'M', u'a'), + (0x1D5EF, 'M', u'b'), + (0x1D5F0, 'M', u'c'), + (0x1D5F1, 'M', u'd'), + (0x1D5F2, 'M', u'e'), + ] + +def _seg_63(): + return [ + (0x1D5F3, 'M', u'f'), + (0x1D5F4, 'M', u'g'), + (0x1D5F5, 'M', u'h'), + (0x1D5F6, 'M', u'i'), + (0x1D5F7, 'M', u'j'), + (0x1D5F8, 'M', u'k'), + (0x1D5F9, 'M', u'l'), + (0x1D5FA, 'M', u'm'), + (0x1D5FB, 'M', u'n'), + (0x1D5FC, 'M', u'o'), + (0x1D5FD, 'M', u'p'), + (0x1D5FE, 'M', u'q'), + (0x1D5FF, 'M', u'r'), + (0x1D600, 'M', u's'), + (0x1D601, 'M', u't'), + (0x1D602, 'M', u'u'), + (0x1D603, 'M', u'v'), + (0x1D604, 'M', u'w'), + (0x1D605, 'M', u'x'), + (0x1D606, 'M', u'y'), + (0x1D607, 'M', u'z'), + (0x1D608, 'M', u'a'), + (0x1D609, 'M', u'b'), + (0x1D60A, 'M', u'c'), + (0x1D60B, 'M', u'd'), + (0x1D60C, 'M', u'e'), + (0x1D60D, 'M', u'f'), + (0x1D60E, 'M', u'g'), + (0x1D60F, 'M', u'h'), + (0x1D610, 'M', u'i'), + (0x1D611, 'M', u'j'), + (0x1D612, 'M', u'k'), + (0x1D613, 'M', u'l'), + (0x1D614, 'M', u'm'), + (0x1D615, 'M', u'n'), + (0x1D616, 'M', u'o'), + (0x1D617, 'M', u'p'), + (0x1D618, 'M', u'q'), + (0x1D619, 'M', u'r'), + (0x1D61A, 'M', u's'), + (0x1D61B, 'M', u't'), + (0x1D61C, 'M', u'u'), + (0x1D61D, 'M', u'v'), + (0x1D61E, 'M', u'w'), + (0x1D61F, 'M', u'x'), + (0x1D620, 'M', u'y'), + (0x1D621, 'M', u'z'), + (0x1D622, 'M', u'a'), + (0x1D623, 'M', u'b'), + (0x1D624, 'M', u'c'), + (0x1D625, 'M', u'd'), + (0x1D626, 'M', u'e'), + (0x1D627, 'M', u'f'), + (0x1D628, 'M', u'g'), + (0x1D629, 'M', u'h'), + (0x1D62A, 'M', u'i'), + (0x1D62B, 'M', u'j'), + (0x1D62C, 'M', u'k'), + (0x1D62D, 'M', u'l'), + (0x1D62E, 'M', u'm'), + (0x1D62F, 'M', u'n'), + (0x1D630, 'M', u'o'), + (0x1D631, 'M', u'p'), + (0x1D632, 'M', u'q'), + (0x1D633, 'M', u'r'), + (0x1D634, 'M', u's'), + (0x1D635, 'M', u't'), + (0x1D636, 'M', u'u'), + (0x1D637, 'M', u'v'), + (0x1D638, 'M', u'w'), + (0x1D639, 'M', u'x'), + (0x1D63A, 'M', u'y'), + (0x1D63B, 'M', u'z'), + (0x1D63C, 'M', u'a'), + (0x1D63D, 'M', u'b'), + (0x1D63E, 'M', u'c'), + (0x1D63F, 'M', u'd'), + (0x1D640, 'M', u'e'), + (0x1D641, 'M', u'f'), + (0x1D642, 'M', u'g'), + (0x1D643, 'M', u'h'), + (0x1D644, 'M', u'i'), + (0x1D645, 'M', u'j'), + (0x1D646, 'M', u'k'), + (0x1D647, 'M', u'l'), + (0x1D648, 'M', u'm'), + (0x1D649, 'M', u'n'), + (0x1D64A, 'M', u'o'), + (0x1D64B, 'M', u'p'), + (0x1D64C, 'M', u'q'), + (0x1D64D, 'M', u'r'), + (0x1D64E, 'M', u's'), + (0x1D64F, 'M', u't'), + (0x1D650, 'M', u'u'), + (0x1D651, 'M', u'v'), + (0x1D652, 'M', u'w'), + (0x1D653, 'M', u'x'), + (0x1D654, 'M', u'y'), + (0x1D655, 'M', u'z'), + (0x1D656, 'M', u'a'), + ] + +def _seg_64(): + return [ + (0x1D657, 'M', u'b'), + (0x1D658, 'M', u'c'), + (0x1D659, 'M', u'd'), + (0x1D65A, 'M', u'e'), + (0x1D65B, 'M', u'f'), + (0x1D65C, 'M', u'g'), + (0x1D65D, 'M', u'h'), + (0x1D65E, 'M', u'i'), + (0x1D65F, 'M', u'j'), + (0x1D660, 'M', u'k'), + (0x1D661, 'M', u'l'), + (0x1D662, 'M', u'm'), + (0x1D663, 'M', u'n'), + (0x1D664, 'M', u'o'), + (0x1D665, 'M', u'p'), + (0x1D666, 'M', u'q'), + (0x1D667, 'M', u'r'), + (0x1D668, 'M', u's'), + (0x1D669, 'M', u't'), + (0x1D66A, 'M', u'u'), + (0x1D66B, 'M', u'v'), + (0x1D66C, 'M', u'w'), + (0x1D66D, 'M', u'x'), + (0x1D66E, 'M', u'y'), + (0x1D66F, 'M', u'z'), + (0x1D670, 'M', u'a'), + (0x1D671, 'M', u'b'), + (0x1D672, 'M', u'c'), + (0x1D673, 'M', u'd'), + (0x1D674, 'M', u'e'), + (0x1D675, 'M', u'f'), + (0x1D676, 'M', u'g'), + (0x1D677, 'M', u'h'), + (0x1D678, 'M', u'i'), + (0x1D679, 'M', u'j'), + (0x1D67A, 'M', u'k'), + (0x1D67B, 'M', u'l'), + (0x1D67C, 'M', u'm'), + (0x1D67D, 'M', u'n'), + (0x1D67E, 'M', u'o'), + (0x1D67F, 'M', u'p'), + (0x1D680, 'M', u'q'), + (0x1D681, 'M', u'r'), + (0x1D682, 'M', u's'), + (0x1D683, 'M', u't'), + (0x1D684, 'M', u'u'), + (0x1D685, 'M', u'v'), + (0x1D686, 'M', u'w'), + (0x1D687, 'M', u'x'), + (0x1D688, 'M', u'y'), + (0x1D689, 'M', u'z'), + (0x1D68A, 'M', u'a'), + (0x1D68B, 'M', u'b'), + (0x1D68C, 'M', u'c'), + (0x1D68D, 'M', u'd'), + (0x1D68E, 'M', u'e'), + (0x1D68F, 'M', u'f'), + (0x1D690, 'M', u'g'), + (0x1D691, 'M', u'h'), + (0x1D692, 'M', u'i'), + (0x1D693, 'M', u'j'), + (0x1D694, 'M', u'k'), + (0x1D695, 'M', u'l'), + (0x1D696, 'M', u'm'), + (0x1D697, 'M', u'n'), + (0x1D698, 'M', u'o'), + (0x1D699, 'M', u'p'), + (0x1D69A, 'M', u'q'), + (0x1D69B, 'M', u'r'), + (0x1D69C, 'M', u's'), + (0x1D69D, 'M', u't'), + (0x1D69E, 'M', u'u'), + (0x1D69F, 'M', u'v'), + (0x1D6A0, 'M', u'w'), + (0x1D6A1, 'M', u'x'), + (0x1D6A2, 'M', u'y'), + (0x1D6A3, 'M', u'z'), + (0x1D6A4, 'M', u'ı'), + (0x1D6A5, 'M', u'ȷ'), + (0x1D6A6, 'X'), + (0x1D6A8, 'M', u'α'), + (0x1D6A9, 'M', u'β'), + (0x1D6AA, 'M', u'γ'), + (0x1D6AB, 'M', u'δ'), + (0x1D6AC, 'M', u'ε'), + (0x1D6AD, 'M', u'ζ'), + (0x1D6AE, 'M', u'η'), + (0x1D6AF, 'M', u'θ'), + (0x1D6B0, 'M', u'ι'), + (0x1D6B1, 'M', u'κ'), + (0x1D6B2, 'M', u'λ'), + (0x1D6B3, 'M', u'μ'), + (0x1D6B4, 'M', u'ν'), + (0x1D6B5, 'M', u'ξ'), + (0x1D6B6, 'M', u'ο'), + (0x1D6B7, 'M', u'π'), + (0x1D6B8, 'M', u'ρ'), + (0x1D6B9, 'M', u'θ'), + (0x1D6BA, 'M', u'σ'), + (0x1D6BB, 'M', u'τ'), + ] + +def _seg_65(): + return [ + (0x1D6BC, 'M', u'υ'), + (0x1D6BD, 'M', u'φ'), + (0x1D6BE, 'M', u'χ'), + (0x1D6BF, 'M', u'ψ'), + (0x1D6C0, 'M', u'ω'), + (0x1D6C1, 'M', u'∇'), + (0x1D6C2, 'M', u'α'), + (0x1D6C3, 'M', u'β'), + (0x1D6C4, 'M', u'γ'), + (0x1D6C5, 'M', u'δ'), + (0x1D6C6, 'M', u'ε'), + (0x1D6C7, 'M', u'ζ'), + (0x1D6C8, 'M', u'η'), + (0x1D6C9, 'M', u'θ'), + (0x1D6CA, 'M', u'ι'), + (0x1D6CB, 'M', u'κ'), + (0x1D6CC, 'M', u'λ'), + (0x1D6CD, 'M', u'μ'), + (0x1D6CE, 'M', u'ν'), + (0x1D6CF, 'M', u'ξ'), + (0x1D6D0, 'M', u'ο'), + (0x1D6D1, 'M', u'π'), + (0x1D6D2, 'M', u'ρ'), + (0x1D6D3, 'M', u'σ'), + (0x1D6D5, 'M', u'τ'), + (0x1D6D6, 'M', u'υ'), + (0x1D6D7, 'M', u'φ'), + (0x1D6D8, 'M', u'χ'), + (0x1D6D9, 'M', u'ψ'), + (0x1D6DA, 'M', u'ω'), + (0x1D6DB, 'M', u'∂'), + (0x1D6DC, 'M', u'ε'), + (0x1D6DD, 'M', u'θ'), + (0x1D6DE, 'M', u'κ'), + (0x1D6DF, 'M', u'φ'), + (0x1D6E0, 'M', u'ρ'), + (0x1D6E1, 'M', u'π'), + (0x1D6E2, 'M', u'α'), + (0x1D6E3, 'M', u'β'), + (0x1D6E4, 'M', u'γ'), + (0x1D6E5, 'M', u'δ'), + (0x1D6E6, 'M', u'ε'), + (0x1D6E7, 'M', u'ζ'), + (0x1D6E8, 'M', u'η'), + (0x1D6E9, 'M', u'θ'), + (0x1D6EA, 'M', u'ι'), + (0x1D6EB, 'M', u'κ'), + (0x1D6EC, 'M', u'λ'), + (0x1D6ED, 'M', u'μ'), + (0x1D6EE, 'M', u'ν'), + (0x1D6EF, 'M', u'ξ'), + (0x1D6F0, 'M', u'ο'), + (0x1D6F1, 'M', u'π'), + (0x1D6F2, 'M', u'ρ'), + (0x1D6F3, 'M', u'θ'), + (0x1D6F4, 'M', u'σ'), + (0x1D6F5, 'M', u'τ'), + (0x1D6F6, 'M', u'υ'), + (0x1D6F7, 'M', u'φ'), + (0x1D6F8, 'M', u'χ'), + (0x1D6F9, 'M', u'ψ'), + (0x1D6FA, 'M', u'ω'), + (0x1D6FB, 'M', u'∇'), + (0x1D6FC, 'M', u'α'), + (0x1D6FD, 'M', u'β'), + (0x1D6FE, 'M', u'γ'), + (0x1D6FF, 'M', u'δ'), + (0x1D700, 'M', u'ε'), + (0x1D701, 'M', u'ζ'), + (0x1D702, 'M', u'η'), + (0x1D703, 'M', u'θ'), + (0x1D704, 'M', u'ι'), + (0x1D705, 'M', u'κ'), + (0x1D706, 'M', u'λ'), + (0x1D707, 'M', u'μ'), + (0x1D708, 'M', u'ν'), + (0x1D709, 'M', u'ξ'), + (0x1D70A, 'M', u'ο'), + (0x1D70B, 'M', u'π'), + (0x1D70C, 'M', u'ρ'), + (0x1D70D, 'M', u'σ'), + (0x1D70F, 'M', u'τ'), + (0x1D710, 'M', u'υ'), + (0x1D711, 'M', u'φ'), + (0x1D712, 'M', u'χ'), + (0x1D713, 'M', u'ψ'), + (0x1D714, 'M', u'ω'), + (0x1D715, 'M', u'∂'), + (0x1D716, 'M', u'ε'), + (0x1D717, 'M', u'θ'), + (0x1D718, 'M', u'κ'), + (0x1D719, 'M', u'φ'), + (0x1D71A, 'M', u'ρ'), + (0x1D71B, 'M', u'π'), + (0x1D71C, 'M', u'α'), + (0x1D71D, 'M', u'β'), + (0x1D71E, 'M', u'γ'), + (0x1D71F, 'M', u'δ'), + (0x1D720, 'M', u'ε'), + (0x1D721, 'M', u'ζ'), + ] + +def _seg_66(): + return [ + (0x1D722, 'M', u'η'), + (0x1D723, 'M', u'θ'), + (0x1D724, 'M', u'ι'), + (0x1D725, 'M', u'κ'), + (0x1D726, 'M', u'λ'), + (0x1D727, 'M', u'μ'), + (0x1D728, 'M', u'ν'), + (0x1D729, 'M', u'ξ'), + (0x1D72A, 'M', u'ο'), + (0x1D72B, 'M', u'π'), + (0x1D72C, 'M', u'ρ'), + (0x1D72D, 'M', u'θ'), + (0x1D72E, 'M', u'σ'), + (0x1D72F, 'M', u'τ'), + (0x1D730, 'M', u'υ'), + (0x1D731, 'M', u'φ'), + (0x1D732, 'M', u'χ'), + (0x1D733, 'M', u'ψ'), + (0x1D734, 'M', u'ω'), + (0x1D735, 'M', u'∇'), + (0x1D736, 'M', u'α'), + (0x1D737, 'M', u'β'), + (0x1D738, 'M', u'γ'), + (0x1D739, 'M', u'δ'), + (0x1D73A, 'M', u'ε'), + (0x1D73B, 'M', u'ζ'), + (0x1D73C, 'M', u'η'), + (0x1D73D, 'M', u'θ'), + (0x1D73E, 'M', u'ι'), + (0x1D73F, 'M', u'κ'), + (0x1D740, 'M', u'λ'), + (0x1D741, 'M', u'μ'), + (0x1D742, 'M', u'ν'), + (0x1D743, 'M', u'ξ'), + (0x1D744, 'M', u'ο'), + (0x1D745, 'M', u'π'), + (0x1D746, 'M', u'ρ'), + (0x1D747, 'M', u'σ'), + (0x1D749, 'M', u'τ'), + (0x1D74A, 'M', u'υ'), + (0x1D74B, 'M', u'φ'), + (0x1D74C, 'M', u'χ'), + (0x1D74D, 'M', u'ψ'), + (0x1D74E, 'M', u'ω'), + (0x1D74F, 'M', u'∂'), + (0x1D750, 'M', u'ε'), + (0x1D751, 'M', u'θ'), + (0x1D752, 'M', u'κ'), + (0x1D753, 'M', u'φ'), + (0x1D754, 'M', u'ρ'), + (0x1D755, 'M', u'π'), + (0x1D756, 'M', u'α'), + (0x1D757, 'M', u'β'), + (0x1D758, 'M', u'γ'), + (0x1D759, 'M', u'δ'), + (0x1D75A, 'M', u'ε'), + (0x1D75B, 'M', u'ζ'), + (0x1D75C, 'M', u'η'), + (0x1D75D, 'M', u'θ'), + (0x1D75E, 'M', u'ι'), + (0x1D75F, 'M', u'κ'), + (0x1D760, 'M', u'λ'), + (0x1D761, 'M', u'μ'), + (0x1D762, 'M', u'ν'), + (0x1D763, 'M', u'ξ'), + (0x1D764, 'M', u'ο'), + (0x1D765, 'M', u'π'), + (0x1D766, 'M', u'ρ'), + (0x1D767, 'M', u'θ'), + (0x1D768, 'M', u'σ'), + (0x1D769, 'M', u'τ'), + (0x1D76A, 'M', u'υ'), + (0x1D76B, 'M', u'φ'), + (0x1D76C, 'M', u'χ'), + (0x1D76D, 'M', u'ψ'), + (0x1D76E, 'M', u'ω'), + (0x1D76F, 'M', u'∇'), + (0x1D770, 'M', u'α'), + (0x1D771, 'M', u'β'), + (0x1D772, 'M', u'γ'), + (0x1D773, 'M', u'δ'), + (0x1D774, 'M', u'ε'), + (0x1D775, 'M', u'ζ'), + (0x1D776, 'M', u'η'), + (0x1D777, 'M', u'θ'), + (0x1D778, 'M', u'ι'), + (0x1D779, 'M', u'κ'), + (0x1D77A, 'M', u'λ'), + (0x1D77B, 'M', u'μ'), + (0x1D77C, 'M', u'ν'), + (0x1D77D, 'M', u'ξ'), + (0x1D77E, 'M', u'ο'), + (0x1D77F, 'M', u'π'), + (0x1D780, 'M', u'ρ'), + (0x1D781, 'M', u'σ'), + (0x1D783, 'M', u'τ'), + (0x1D784, 'M', u'υ'), + (0x1D785, 'M', u'φ'), + (0x1D786, 'M', u'χ'), + (0x1D787, 'M', u'ψ'), + ] + +def _seg_67(): + return [ + (0x1D788, 'M', u'ω'), + (0x1D789, 'M', u'∂'), + (0x1D78A, 'M', u'ε'), + (0x1D78B, 'M', u'θ'), + (0x1D78C, 'M', u'κ'), + (0x1D78D, 'M', u'φ'), + (0x1D78E, 'M', u'ρ'), + (0x1D78F, 'M', u'π'), + (0x1D790, 'M', u'α'), + (0x1D791, 'M', u'β'), + (0x1D792, 'M', u'γ'), + (0x1D793, 'M', u'δ'), + (0x1D794, 'M', u'ε'), + (0x1D795, 'M', u'ζ'), + (0x1D796, 'M', u'η'), + (0x1D797, 'M', u'θ'), + (0x1D798, 'M', u'ι'), + (0x1D799, 'M', u'κ'), + (0x1D79A, 'M', u'λ'), + (0x1D79B, 'M', u'μ'), + (0x1D79C, 'M', u'ν'), + (0x1D79D, 'M', u'ξ'), + (0x1D79E, 'M', u'ο'), + (0x1D79F, 'M', u'π'), + (0x1D7A0, 'M', u'ρ'), + (0x1D7A1, 'M', u'θ'), + (0x1D7A2, 'M', u'σ'), + (0x1D7A3, 'M', u'τ'), + (0x1D7A4, 'M', u'υ'), + (0x1D7A5, 'M', u'φ'), + (0x1D7A6, 'M', u'χ'), + (0x1D7A7, 'M', u'ψ'), + (0x1D7A8, 'M', u'ω'), + (0x1D7A9, 'M', u'∇'), + (0x1D7AA, 'M', u'α'), + (0x1D7AB, 'M', u'β'), + (0x1D7AC, 'M', u'γ'), + (0x1D7AD, 'M', u'δ'), + (0x1D7AE, 'M', u'ε'), + (0x1D7AF, 'M', u'ζ'), + (0x1D7B0, 'M', u'η'), + (0x1D7B1, 'M', u'θ'), + (0x1D7B2, 'M', u'ι'), + (0x1D7B3, 'M', u'κ'), + (0x1D7B4, 'M', u'λ'), + (0x1D7B5, 'M', u'μ'), + (0x1D7B6, 'M', u'ν'), + (0x1D7B7, 'M', u'ξ'), + (0x1D7B8, 'M', u'ο'), + (0x1D7B9, 'M', u'π'), + (0x1D7BA, 'M', u'ρ'), + (0x1D7BB, 'M', u'σ'), + (0x1D7BD, 'M', u'τ'), + (0x1D7BE, 'M', u'υ'), + (0x1D7BF, 'M', u'φ'), + (0x1D7C0, 'M', u'χ'), + (0x1D7C1, 'M', u'ψ'), + (0x1D7C2, 'M', u'ω'), + (0x1D7C3, 'M', u'∂'), + (0x1D7C4, 'M', u'ε'), + (0x1D7C5, 'M', u'θ'), + (0x1D7C6, 'M', u'κ'), + (0x1D7C7, 'M', u'φ'), + (0x1D7C8, 'M', u'ρ'), + (0x1D7C9, 'M', u'π'), + (0x1D7CA, 'M', u'ϝ'), + (0x1D7CC, 'X'), + (0x1D7CE, 'M', u'0'), + (0x1D7CF, 'M', u'1'), + (0x1D7D0, 'M', u'2'), + (0x1D7D1, 'M', u'3'), + (0x1D7D2, 'M', u'4'), + (0x1D7D3, 'M', u'5'), + (0x1D7D4, 'M', u'6'), + (0x1D7D5, 'M', u'7'), + (0x1D7D6, 'M', u'8'), + (0x1D7D7, 'M', u'9'), + (0x1D7D8, 'M', u'0'), + (0x1D7D9, 'M', u'1'), + (0x1D7DA, 'M', u'2'), + (0x1D7DB, 'M', u'3'), + (0x1D7DC, 'M', u'4'), + (0x1D7DD, 'M', u'5'), + (0x1D7DE, 'M', u'6'), + (0x1D7DF, 'M', u'7'), + (0x1D7E0, 'M', u'8'), + (0x1D7E1, 'M', u'9'), + (0x1D7E2, 'M', u'0'), + (0x1D7E3, 'M', u'1'), + (0x1D7E4, 'M', u'2'), + (0x1D7E5, 'M', u'3'), + (0x1D7E6, 'M', u'4'), + (0x1D7E7, 'M', u'5'), + (0x1D7E8, 'M', u'6'), + (0x1D7E9, 'M', u'7'), + (0x1D7EA, 'M', u'8'), + (0x1D7EB, 'M', u'9'), + (0x1D7EC, 'M', u'0'), + (0x1D7ED, 'M', u'1'), + (0x1D7EE, 'M', u'2'), + ] + +def _seg_68(): + return [ + (0x1D7EF, 'M', u'3'), + (0x1D7F0, 'M', u'4'), + (0x1D7F1, 'M', u'5'), + (0x1D7F2, 'M', u'6'), + (0x1D7F3, 'M', u'7'), + (0x1D7F4, 'M', u'8'), + (0x1D7F5, 'M', u'9'), + (0x1D7F6, 'M', u'0'), + (0x1D7F7, 'M', u'1'), + (0x1D7F8, 'M', u'2'), + (0x1D7F9, 'M', u'3'), + (0x1D7FA, 'M', u'4'), + (0x1D7FB, 'M', u'5'), + (0x1D7FC, 'M', u'6'), + (0x1D7FD, 'M', u'7'), + (0x1D7FE, 'M', u'8'), + (0x1D7FF, 'M', u'9'), + (0x1D800, 'V'), + (0x1DA8C, 'X'), + (0x1DA9B, 'V'), + (0x1DAA0, 'X'), + (0x1DAA1, 'V'), + (0x1DAB0, 'X'), + (0x1E000, 'V'), + (0x1E007, 'X'), + (0x1E008, 'V'), + (0x1E019, 'X'), + (0x1E01B, 'V'), + (0x1E022, 'X'), + (0x1E023, 'V'), + (0x1E025, 'X'), + (0x1E026, 'V'), + (0x1E02B, 'X'), + (0x1E800, 'V'), + (0x1E8C5, 'X'), + (0x1E8C7, 'V'), + (0x1E8D7, 'X'), + (0x1E900, 'M', u'𞤢'), + (0x1E901, 'M', u'𞤣'), + (0x1E902, 'M', u'𞤤'), + (0x1E903, 'M', u'𞤥'), + (0x1E904, 'M', u'𞤦'), + (0x1E905, 'M', u'𞤧'), + (0x1E906, 'M', u'𞤨'), + (0x1E907, 'M', u'𞤩'), + (0x1E908, 'M', u'𞤪'), + (0x1E909, 'M', u'𞤫'), + (0x1E90A, 'M', u'𞤬'), + (0x1E90B, 'M', u'𞤭'), + (0x1E90C, 'M', u'𞤮'), + (0x1E90D, 'M', u'𞤯'), + (0x1E90E, 'M', u'𞤰'), + (0x1E90F, 'M', u'𞤱'), + (0x1E910, 'M', u'𞤲'), + (0x1E911, 'M', u'𞤳'), + (0x1E912, 'M', u'𞤴'), + (0x1E913, 'M', u'𞤵'), + (0x1E914, 'M', u'𞤶'), + (0x1E915, 'M', u'𞤷'), + (0x1E916, 'M', u'𞤸'), + (0x1E917, 'M', u'𞤹'), + (0x1E918, 'M', u'𞤺'), + (0x1E919, 'M', u'𞤻'), + (0x1E91A, 'M', u'𞤼'), + (0x1E91B, 'M', u'𞤽'), + (0x1E91C, 'M', u'𞤾'), + (0x1E91D, 'M', u'𞤿'), + (0x1E91E, 'M', u'𞥀'), + (0x1E91F, 'M', u'𞥁'), + (0x1E920, 'M', u'𞥂'), + (0x1E921, 'M', u'𞥃'), + (0x1E922, 'V'), + (0x1E94B, 'X'), + (0x1E950, 'V'), + (0x1E95A, 'X'), + (0x1E95E, 'V'), + (0x1E960, 'X'), + (0x1EC71, 'V'), + (0x1ECB5, 'X'), + (0x1EE00, 'M', u'ا'), + (0x1EE01, 'M', u'ب'), + (0x1EE02, 'M', u'ج'), + (0x1EE03, 'M', u'د'), + (0x1EE04, 'X'), + (0x1EE05, 'M', u'و'), + (0x1EE06, 'M', u'ز'), + (0x1EE07, 'M', u'ح'), + (0x1EE08, 'M', u'ط'), + (0x1EE09, 'M', u'ي'), + (0x1EE0A, 'M', u'ك'), + (0x1EE0B, 'M', u'ل'), + (0x1EE0C, 'M', u'م'), + (0x1EE0D, 'M', u'ن'), + (0x1EE0E, 'M', u'س'), + (0x1EE0F, 'M', u'ع'), + (0x1EE10, 'M', u'ف'), + (0x1EE11, 'M', u'ص'), + (0x1EE12, 'M', u'ق'), + (0x1EE13, 'M', u'ر'), + (0x1EE14, 'M', u'ش'), + ] + +def _seg_69(): + return [ + (0x1EE15, 'M', u'ت'), + (0x1EE16, 'M', u'ث'), + (0x1EE17, 'M', u'خ'), + (0x1EE18, 'M', u'ذ'), + (0x1EE19, 'M', u'ض'), + (0x1EE1A, 'M', u'ظ'), + (0x1EE1B, 'M', u'غ'), + (0x1EE1C, 'M', u'ٮ'), + (0x1EE1D, 'M', u'ں'), + (0x1EE1E, 'M', u'ڡ'), + (0x1EE1F, 'M', u'ٯ'), + (0x1EE20, 'X'), + (0x1EE21, 'M', u'ب'), + (0x1EE22, 'M', u'ج'), + (0x1EE23, 'X'), + (0x1EE24, 'M', u'ه'), + (0x1EE25, 'X'), + (0x1EE27, 'M', u'ح'), + (0x1EE28, 'X'), + (0x1EE29, 'M', u'ي'), + (0x1EE2A, 'M', u'ك'), + (0x1EE2B, 'M', u'ل'), + (0x1EE2C, 'M', u'م'), + (0x1EE2D, 'M', u'ن'), + (0x1EE2E, 'M', u'س'), + (0x1EE2F, 'M', u'ع'), + (0x1EE30, 'M', u'ف'), + (0x1EE31, 'M', u'ص'), + (0x1EE32, 'M', u'ق'), + (0x1EE33, 'X'), + (0x1EE34, 'M', u'ش'), + (0x1EE35, 'M', u'ت'), + (0x1EE36, 'M', u'ث'), + (0x1EE37, 'M', u'خ'), + (0x1EE38, 'X'), + (0x1EE39, 'M', u'ض'), + (0x1EE3A, 'X'), + (0x1EE3B, 'M', u'غ'), + (0x1EE3C, 'X'), + (0x1EE42, 'M', u'ج'), + (0x1EE43, 'X'), + (0x1EE47, 'M', u'ح'), + (0x1EE48, 'X'), + (0x1EE49, 'M', u'ي'), + (0x1EE4A, 'X'), + (0x1EE4B, 'M', u'ل'), + (0x1EE4C, 'X'), + (0x1EE4D, 'M', u'ن'), + (0x1EE4E, 'M', u'س'), + (0x1EE4F, 'M', u'ع'), + (0x1EE50, 'X'), + (0x1EE51, 'M', u'ص'), + (0x1EE52, 'M', u'ق'), + (0x1EE53, 'X'), + (0x1EE54, 'M', u'ش'), + (0x1EE55, 'X'), + (0x1EE57, 'M', u'خ'), + (0x1EE58, 'X'), + (0x1EE59, 'M', u'ض'), + (0x1EE5A, 'X'), + (0x1EE5B, 'M', u'غ'), + (0x1EE5C, 'X'), + (0x1EE5D, 'M', u'ں'), + (0x1EE5E, 'X'), + (0x1EE5F, 'M', u'ٯ'), + (0x1EE60, 'X'), + (0x1EE61, 'M', u'ب'), + (0x1EE62, 'M', u'ج'), + (0x1EE63, 'X'), + (0x1EE64, 'M', u'ه'), + (0x1EE65, 'X'), + (0x1EE67, 'M', u'ح'), + (0x1EE68, 'M', u'ط'), + (0x1EE69, 'M', u'ي'), + (0x1EE6A, 'M', u'ك'), + (0x1EE6B, 'X'), + (0x1EE6C, 'M', u'م'), + (0x1EE6D, 'M', u'ن'), + (0x1EE6E, 'M', u'س'), + (0x1EE6F, 'M', u'ع'), + (0x1EE70, 'M', u'ف'), + (0x1EE71, 'M', u'ص'), + (0x1EE72, 'M', u'ق'), + (0x1EE73, 'X'), + (0x1EE74, 'M', u'ش'), + (0x1EE75, 'M', u'ت'), + (0x1EE76, 'M', u'ث'), + (0x1EE77, 'M', u'خ'), + (0x1EE78, 'X'), + (0x1EE79, 'M', u'ض'), + (0x1EE7A, 'M', u'ظ'), + (0x1EE7B, 'M', u'غ'), + (0x1EE7C, 'M', u'ٮ'), + (0x1EE7D, 'X'), + (0x1EE7E, 'M', u'ڡ'), + (0x1EE7F, 'X'), + (0x1EE80, 'M', u'ا'), + (0x1EE81, 'M', u'ب'), + (0x1EE82, 'M', u'ج'), + (0x1EE83, 'M', u'د'), + ] + +def _seg_70(): + return [ + (0x1EE84, 'M', u'ه'), + (0x1EE85, 'M', u'و'), + (0x1EE86, 'M', u'ز'), + (0x1EE87, 'M', u'ح'), + (0x1EE88, 'M', u'ط'), + (0x1EE89, 'M', u'ي'), + (0x1EE8A, 'X'), + (0x1EE8B, 'M', u'ل'), + (0x1EE8C, 'M', u'م'), + (0x1EE8D, 'M', u'ن'), + (0x1EE8E, 'M', u'س'), + (0x1EE8F, 'M', u'ع'), + (0x1EE90, 'M', u'ف'), + (0x1EE91, 'M', u'ص'), + (0x1EE92, 'M', u'ق'), + (0x1EE93, 'M', u'ر'), + (0x1EE94, 'M', u'ش'), + (0x1EE95, 'M', u'ت'), + (0x1EE96, 'M', u'ث'), + (0x1EE97, 'M', u'خ'), + (0x1EE98, 'M', u'ذ'), + (0x1EE99, 'M', u'ض'), + (0x1EE9A, 'M', u'ظ'), + (0x1EE9B, 'M', u'غ'), + (0x1EE9C, 'X'), + (0x1EEA1, 'M', u'ب'), + (0x1EEA2, 'M', u'ج'), + (0x1EEA3, 'M', u'د'), + (0x1EEA4, 'X'), + (0x1EEA5, 'M', u'و'), + (0x1EEA6, 'M', u'ز'), + (0x1EEA7, 'M', u'ح'), + (0x1EEA8, 'M', u'ط'), + (0x1EEA9, 'M', u'ي'), + (0x1EEAA, 'X'), + (0x1EEAB, 'M', u'ل'), + (0x1EEAC, 'M', u'م'), + (0x1EEAD, 'M', u'ن'), + (0x1EEAE, 'M', u'س'), + (0x1EEAF, 'M', u'ع'), + (0x1EEB0, 'M', u'ف'), + (0x1EEB1, 'M', u'ص'), + (0x1EEB2, 'M', u'ق'), + (0x1EEB3, 'M', u'ر'), + (0x1EEB4, 'M', u'ش'), + (0x1EEB5, 'M', u'ت'), + (0x1EEB6, 'M', u'ث'), + (0x1EEB7, 'M', u'خ'), + (0x1EEB8, 'M', u'ذ'), + (0x1EEB9, 'M', u'ض'), + (0x1EEBA, 'M', u'ظ'), + (0x1EEBB, 'M', u'غ'), + (0x1EEBC, 'X'), + (0x1EEF0, 'V'), + (0x1EEF2, 'X'), + (0x1F000, 'V'), + (0x1F02C, 'X'), + (0x1F030, 'V'), + (0x1F094, 'X'), + (0x1F0A0, 'V'), + (0x1F0AF, 'X'), + (0x1F0B1, 'V'), + (0x1F0C0, 'X'), + (0x1F0C1, 'V'), + (0x1F0D0, 'X'), + (0x1F0D1, 'V'), + (0x1F0F6, 'X'), + (0x1F101, '3', u'0,'), + (0x1F102, '3', u'1,'), + (0x1F103, '3', u'2,'), + (0x1F104, '3', u'3,'), + (0x1F105, '3', u'4,'), + (0x1F106, '3', u'5,'), + (0x1F107, '3', u'6,'), + (0x1F108, '3', u'7,'), + (0x1F109, '3', u'8,'), + (0x1F10A, '3', u'9,'), + (0x1F10B, 'V'), + (0x1F10D, 'X'), + (0x1F110, '3', u'(a)'), + (0x1F111, '3', u'(b)'), + (0x1F112, '3', u'(c)'), + (0x1F113, '3', u'(d)'), + (0x1F114, '3', u'(e)'), + (0x1F115, '3', u'(f)'), + (0x1F116, '3', u'(g)'), + (0x1F117, '3', u'(h)'), + (0x1F118, '3', u'(i)'), + (0x1F119, '3', u'(j)'), + (0x1F11A, '3', u'(k)'), + (0x1F11B, '3', u'(l)'), + (0x1F11C, '3', u'(m)'), + (0x1F11D, '3', u'(n)'), + (0x1F11E, '3', u'(o)'), + (0x1F11F, '3', u'(p)'), + (0x1F120, '3', u'(q)'), + (0x1F121, '3', u'(r)'), + (0x1F122, '3', u'(s)'), + (0x1F123, '3', u'(t)'), + (0x1F124, '3', u'(u)'), + ] + +def _seg_71(): + return [ + (0x1F125, '3', u'(v)'), + (0x1F126, '3', u'(w)'), + (0x1F127, '3', u'(x)'), + (0x1F128, '3', u'(y)'), + (0x1F129, '3', u'(z)'), + (0x1F12A, 'M', u'〔s〕'), + (0x1F12B, 'M', u'c'), + (0x1F12C, 'M', u'r'), + (0x1F12D, 'M', u'cd'), + (0x1F12E, 'M', u'wz'), + (0x1F12F, 'V'), + (0x1F130, 'M', u'a'), + (0x1F131, 'M', u'b'), + (0x1F132, 'M', u'c'), + (0x1F133, 'M', u'd'), + (0x1F134, 'M', u'e'), + (0x1F135, 'M', u'f'), + (0x1F136, 'M', u'g'), + (0x1F137, 'M', u'h'), + (0x1F138, 'M', u'i'), + (0x1F139, 'M', u'j'), + (0x1F13A, 'M', u'k'), + (0x1F13B, 'M', u'l'), + (0x1F13C, 'M', u'm'), + (0x1F13D, 'M', u'n'), + (0x1F13E, 'M', u'o'), + (0x1F13F, 'M', u'p'), + (0x1F140, 'M', u'q'), + (0x1F141, 'M', u'r'), + (0x1F142, 'M', u's'), + (0x1F143, 'M', u't'), + (0x1F144, 'M', u'u'), + (0x1F145, 'M', u'v'), + (0x1F146, 'M', u'w'), + (0x1F147, 'M', u'x'), + (0x1F148, 'M', u'y'), + (0x1F149, 'M', u'z'), + (0x1F14A, 'M', u'hv'), + (0x1F14B, 'M', u'mv'), + (0x1F14C, 'M', u'sd'), + (0x1F14D, 'M', u'ss'), + (0x1F14E, 'M', u'ppv'), + (0x1F14F, 'M', u'wc'), + (0x1F150, 'V'), + (0x1F16A, 'M', u'mc'), + (0x1F16B, 'M', u'md'), + (0x1F16C, 'X'), + (0x1F170, 'V'), + (0x1F190, 'M', u'dj'), + (0x1F191, 'V'), + (0x1F1AD, 'X'), + (0x1F1E6, 'V'), + (0x1F200, 'M', u'ほか'), + (0x1F201, 'M', u'ココ'), + (0x1F202, 'M', u'サ'), + (0x1F203, 'X'), + (0x1F210, 'M', u'手'), + (0x1F211, 'M', u'字'), + (0x1F212, 'M', u'双'), + (0x1F213, 'M', u'デ'), + (0x1F214, 'M', u'二'), + (0x1F215, 'M', u'多'), + (0x1F216, 'M', u'解'), + (0x1F217, 'M', u'天'), + (0x1F218, 'M', u'交'), + (0x1F219, 'M', u'映'), + (0x1F21A, 'M', u'無'), + (0x1F21B, 'M', u'料'), + (0x1F21C, 'M', u'前'), + (0x1F21D, 'M', u'後'), + (0x1F21E, 'M', u'再'), + (0x1F21F, 'M', u'新'), + (0x1F220, 'M', u'初'), + (0x1F221, 'M', u'終'), + (0x1F222, 'M', u'生'), + (0x1F223, 'M', u'販'), + (0x1F224, 'M', u'声'), + (0x1F225, 'M', u'吹'), + (0x1F226, 'M', u'演'), + (0x1F227, 'M', u'投'), + (0x1F228, 'M', u'捕'), + (0x1F229, 'M', u'一'), + (0x1F22A, 'M', u'三'), + (0x1F22B, 'M', u'遊'), + (0x1F22C, 'M', u'左'), + (0x1F22D, 'M', u'中'), + (0x1F22E, 'M', u'右'), + (0x1F22F, 'M', u'指'), + (0x1F230, 'M', u'走'), + (0x1F231, 'M', u'打'), + (0x1F232, 'M', u'禁'), + (0x1F233, 'M', u'空'), + (0x1F234, 'M', u'合'), + (0x1F235, 'M', u'満'), + (0x1F236, 'M', u'有'), + (0x1F237, 'M', u'月'), + (0x1F238, 'M', u'申'), + (0x1F239, 'M', u'割'), + (0x1F23A, 'M', u'営'), + (0x1F23B, 'M', u'配'), + ] + +def _seg_72(): + return [ + (0x1F23C, 'X'), + (0x1F240, 'M', u'〔本〕'), + (0x1F241, 'M', u'〔三〕'), + (0x1F242, 'M', u'〔二〕'), + (0x1F243, 'M', u'〔安〕'), + (0x1F244, 'M', u'〔点〕'), + (0x1F245, 'M', u'〔打〕'), + (0x1F246, 'M', u'〔盗〕'), + (0x1F247, 'M', u'〔勝〕'), + (0x1F248, 'M', u'〔敗〕'), + (0x1F249, 'X'), + (0x1F250, 'M', u'得'), + (0x1F251, 'M', u'可'), + (0x1F252, 'X'), + (0x1F260, 'V'), + (0x1F266, 'X'), + (0x1F300, 'V'), + (0x1F6D5, 'X'), + (0x1F6E0, 'V'), + (0x1F6ED, 'X'), + (0x1F6F0, 'V'), + (0x1F6FA, 'X'), + (0x1F700, 'V'), + (0x1F774, 'X'), + (0x1F780, 'V'), + (0x1F7D9, 'X'), + (0x1F800, 'V'), + (0x1F80C, 'X'), + (0x1F810, 'V'), + (0x1F848, 'X'), + (0x1F850, 'V'), + (0x1F85A, 'X'), + (0x1F860, 'V'), + (0x1F888, 'X'), + (0x1F890, 'V'), + (0x1F8AE, 'X'), + (0x1F900, 'V'), + (0x1F90C, 'X'), + (0x1F910, 'V'), + (0x1F93F, 'X'), + (0x1F940, 'V'), + (0x1F971, 'X'), + (0x1F973, 'V'), + (0x1F977, 'X'), + (0x1F97A, 'V'), + (0x1F97B, 'X'), + (0x1F97C, 'V'), + (0x1F9A3, 'X'), + (0x1F9B0, 'V'), + (0x1F9BA, 'X'), + (0x1F9C0, 'V'), + (0x1F9C3, 'X'), + (0x1F9D0, 'V'), + (0x1FA00, 'X'), + (0x1FA60, 'V'), + (0x1FA6E, 'X'), + (0x20000, 'V'), + (0x2A6D7, 'X'), + (0x2A700, 'V'), + (0x2B735, 'X'), + (0x2B740, 'V'), + (0x2B81E, 'X'), + (0x2B820, 'V'), + (0x2CEA2, 'X'), + (0x2CEB0, 'V'), + (0x2EBE1, 'X'), + (0x2F800, 'M', u'丽'), + (0x2F801, 'M', u'丸'), + (0x2F802, 'M', u'乁'), + (0x2F803, 'M', u'𠄢'), + (0x2F804, 'M', u'你'), + (0x2F805, 'M', u'侮'), + (0x2F806, 'M', u'侻'), + (0x2F807, 'M', u'倂'), + (0x2F808, 'M', u'偺'), + (0x2F809, 'M', u'備'), + (0x2F80A, 'M', u'僧'), + (0x2F80B, 'M', u'像'), + (0x2F80C, 'M', u'㒞'), + (0x2F80D, 'M', u'𠘺'), + (0x2F80E, 'M', u'免'), + (0x2F80F, 'M', u'兔'), + (0x2F810, 'M', u'兤'), + (0x2F811, 'M', u'具'), + (0x2F812, 'M', u'𠔜'), + (0x2F813, 'M', u'㒹'), + (0x2F814, 'M', u'內'), + (0x2F815, 'M', u'再'), + (0x2F816, 'M', u'𠕋'), + (0x2F817, 'M', u'冗'), + (0x2F818, 'M', u'冤'), + (0x2F819, 'M', u'仌'), + (0x2F81A, 'M', u'冬'), + (0x2F81B, 'M', u'况'), + (0x2F81C, 'M', u'𩇟'), + (0x2F81D, 'M', u'凵'), + (0x2F81E, 'M', u'刃'), + (0x2F81F, 'M', u'㓟'), + (0x2F820, 'M', u'刻'), + (0x2F821, 'M', u'剆'), + ] + +def _seg_73(): + return [ + (0x2F822, 'M', u'割'), + (0x2F823, 'M', u'剷'), + (0x2F824, 'M', u'㔕'), + (0x2F825, 'M', u'勇'), + (0x2F826, 'M', u'勉'), + (0x2F827, 'M', u'勤'), + (0x2F828, 'M', u'勺'), + (0x2F829, 'M', u'包'), + (0x2F82A, 'M', u'匆'), + (0x2F82B, 'M', u'北'), + (0x2F82C, 'M', u'卉'), + (0x2F82D, 'M', u'卑'), + (0x2F82E, 'M', u'博'), + (0x2F82F, 'M', u'即'), + (0x2F830, 'M', u'卽'), + (0x2F831, 'M', u'卿'), + (0x2F834, 'M', u'𠨬'), + (0x2F835, 'M', u'灰'), + (0x2F836, 'M', u'及'), + (0x2F837, 'M', u'叟'), + (0x2F838, 'M', u'𠭣'), + (0x2F839, 'M', u'叫'), + (0x2F83A, 'M', u'叱'), + (0x2F83B, 'M', u'吆'), + (0x2F83C, 'M', u'咞'), + (0x2F83D, 'M', u'吸'), + (0x2F83E, 'M', u'呈'), + (0x2F83F, 'M', u'周'), + (0x2F840, 'M', u'咢'), + (0x2F841, 'M', u'哶'), + (0x2F842, 'M', u'唐'), + (0x2F843, 'M', u'啓'), + (0x2F844, 'M', u'啣'), + (0x2F845, 'M', u'善'), + (0x2F847, 'M', u'喙'), + (0x2F848, 'M', u'喫'), + (0x2F849, 'M', u'喳'), + (0x2F84A, 'M', u'嗂'), + (0x2F84B, 'M', u'圖'), + (0x2F84C, 'M', u'嘆'), + (0x2F84D, 'M', u'圗'), + (0x2F84E, 'M', u'噑'), + (0x2F84F, 'M', u'噴'), + (0x2F850, 'M', u'切'), + (0x2F851, 'M', u'壮'), + (0x2F852, 'M', u'城'), + (0x2F853, 'M', u'埴'), + (0x2F854, 'M', u'堍'), + (0x2F855, 'M', u'型'), + (0x2F856, 'M', u'堲'), + (0x2F857, 'M', u'報'), + (0x2F858, 'M', u'墬'), + (0x2F859, 'M', u'𡓤'), + (0x2F85A, 'M', u'売'), + (0x2F85B, 'M', u'壷'), + (0x2F85C, 'M', u'夆'), + (0x2F85D, 'M', u'多'), + (0x2F85E, 'M', u'夢'), + (0x2F85F, 'M', u'奢'), + (0x2F860, 'M', u'𡚨'), + (0x2F861, 'M', u'𡛪'), + (0x2F862, 'M', u'姬'), + (0x2F863, 'M', u'娛'), + (0x2F864, 'M', u'娧'), + (0x2F865, 'M', u'姘'), + (0x2F866, 'M', u'婦'), + (0x2F867, 'M', u'㛮'), + (0x2F868, 'X'), + (0x2F869, 'M', u'嬈'), + (0x2F86A, 'M', u'嬾'), + (0x2F86C, 'M', u'𡧈'), + (0x2F86D, 'M', u'寃'), + (0x2F86E, 'M', u'寘'), + (0x2F86F, 'M', u'寧'), + (0x2F870, 'M', u'寳'), + (0x2F871, 'M', u'𡬘'), + (0x2F872, 'M', u'寿'), + (0x2F873, 'M', u'将'), + (0x2F874, 'X'), + (0x2F875, 'M', u'尢'), + (0x2F876, 'M', u'㞁'), + (0x2F877, 'M', u'屠'), + (0x2F878, 'M', u'屮'), + (0x2F879, 'M', u'峀'), + (0x2F87A, 'M', u'岍'), + (0x2F87B, 'M', u'𡷤'), + (0x2F87C, 'M', u'嵃'), + (0x2F87D, 'M', u'𡷦'), + (0x2F87E, 'M', u'嵮'), + (0x2F87F, 'M', u'嵫'), + (0x2F880, 'M', u'嵼'), + (0x2F881, 'M', u'巡'), + (0x2F882, 'M', u'巢'), + (0x2F883, 'M', u'㠯'), + (0x2F884, 'M', u'巽'), + (0x2F885, 'M', u'帨'), + (0x2F886, 'M', u'帽'), + (0x2F887, 'M', u'幩'), + (0x2F888, 'M', u'㡢'), + (0x2F889, 'M', u'𢆃'), + ] + +def _seg_74(): + return [ + (0x2F88A, 'M', u'㡼'), + (0x2F88B, 'M', u'庰'), + (0x2F88C, 'M', u'庳'), + (0x2F88D, 'M', u'庶'), + (0x2F88E, 'M', u'廊'), + (0x2F88F, 'M', u'𪎒'), + (0x2F890, 'M', u'廾'), + (0x2F891, 'M', u'𢌱'), + (0x2F893, 'M', u'舁'), + (0x2F894, 'M', u'弢'), + (0x2F896, 'M', u'㣇'), + (0x2F897, 'M', u'𣊸'), + (0x2F898, 'M', u'𦇚'), + (0x2F899, 'M', u'形'), + (0x2F89A, 'M', u'彫'), + (0x2F89B, 'M', u'㣣'), + (0x2F89C, 'M', u'徚'), + (0x2F89D, 'M', u'忍'), + (0x2F89E, 'M', u'志'), + (0x2F89F, 'M', u'忹'), + (0x2F8A0, 'M', u'悁'), + (0x2F8A1, 'M', u'㤺'), + (0x2F8A2, 'M', u'㤜'), + (0x2F8A3, 'M', u'悔'), + (0x2F8A4, 'M', u'𢛔'), + (0x2F8A5, 'M', u'惇'), + (0x2F8A6, 'M', u'慈'), + (0x2F8A7, 'M', u'慌'), + (0x2F8A8, 'M', u'慎'), + (0x2F8A9, 'M', u'慌'), + (0x2F8AA, 'M', u'慺'), + (0x2F8AB, 'M', u'憎'), + (0x2F8AC, 'M', u'憲'), + (0x2F8AD, 'M', u'憤'), + (0x2F8AE, 'M', u'憯'), + (0x2F8AF, 'M', u'懞'), + (0x2F8B0, 'M', u'懲'), + (0x2F8B1, 'M', u'懶'), + (0x2F8B2, 'M', u'成'), + (0x2F8B3, 'M', u'戛'), + (0x2F8B4, 'M', u'扝'), + (0x2F8B5, 'M', u'抱'), + (0x2F8B6, 'M', u'拔'), + (0x2F8B7, 'M', u'捐'), + (0x2F8B8, 'M', u'𢬌'), + (0x2F8B9, 'M', u'挽'), + (0x2F8BA, 'M', u'拼'), + (0x2F8BB, 'M', u'捨'), + (0x2F8BC, 'M', u'掃'), + (0x2F8BD, 'M', u'揤'), + (0x2F8BE, 'M', u'𢯱'), + (0x2F8BF, 'M', u'搢'), + (0x2F8C0, 'M', u'揅'), + (0x2F8C1, 'M', u'掩'), + (0x2F8C2, 'M', u'㨮'), + (0x2F8C3, 'M', u'摩'), + (0x2F8C4, 'M', u'摾'), + (0x2F8C5, 'M', u'撝'), + (0x2F8C6, 'M', u'摷'), + (0x2F8C7, 'M', u'㩬'), + (0x2F8C8, 'M', u'敏'), + (0x2F8C9, 'M', u'敬'), + (0x2F8CA, 'M', u'𣀊'), + (0x2F8CB, 'M', u'旣'), + (0x2F8CC, 'M', u'書'), + (0x2F8CD, 'M', u'晉'), + (0x2F8CE, 'M', u'㬙'), + (0x2F8CF, 'M', u'暑'), + (0x2F8D0, 'M', u'㬈'), + (0x2F8D1, 'M', u'㫤'), + (0x2F8D2, 'M', u'冒'), + (0x2F8D3, 'M', u'冕'), + (0x2F8D4, 'M', u'最'), + (0x2F8D5, 'M', u'暜'), + (0x2F8D6, 'M', u'肭'), + (0x2F8D7, 'M', u'䏙'), + (0x2F8D8, 'M', u'朗'), + (0x2F8D9, 'M', u'望'), + (0x2F8DA, 'M', u'朡'), + (0x2F8DB, 'M', u'杞'), + (0x2F8DC, 'M', u'杓'), + (0x2F8DD, 'M', u'𣏃'), + (0x2F8DE, 'M', u'㭉'), + (0x2F8DF, 'M', u'柺'), + (0x2F8E0, 'M', u'枅'), + (0x2F8E1, 'M', u'桒'), + (0x2F8E2, 'M', u'梅'), + (0x2F8E3, 'M', u'𣑭'), + (0x2F8E4, 'M', u'梎'), + (0x2F8E5, 'M', u'栟'), + (0x2F8E6, 'M', u'椔'), + (0x2F8E7, 'M', u'㮝'), + (0x2F8E8, 'M', u'楂'), + (0x2F8E9, 'M', u'榣'), + (0x2F8EA, 'M', u'槪'), + (0x2F8EB, 'M', u'檨'), + (0x2F8EC, 'M', u'𣚣'), + (0x2F8ED, 'M', u'櫛'), + (0x2F8EE, 'M', u'㰘'), + (0x2F8EF, 'M', u'次'), + ] + +def _seg_75(): + return [ + (0x2F8F0, 'M', u'𣢧'), + (0x2F8F1, 'M', u'歔'), + (0x2F8F2, 'M', u'㱎'), + (0x2F8F3, 'M', u'歲'), + (0x2F8F4, 'M', u'殟'), + (0x2F8F5, 'M', u'殺'), + (0x2F8F6, 'M', u'殻'), + (0x2F8F7, 'M', u'𣪍'), + (0x2F8F8, 'M', u'𡴋'), + (0x2F8F9, 'M', u'𣫺'), + (0x2F8FA, 'M', u'汎'), + (0x2F8FB, 'M', u'𣲼'), + (0x2F8FC, 'M', u'沿'), + (0x2F8FD, 'M', u'泍'), + (0x2F8FE, 'M', u'汧'), + (0x2F8FF, 'M', u'洖'), + (0x2F900, 'M', u'派'), + (0x2F901, 'M', u'海'), + (0x2F902, 'M', u'流'), + (0x2F903, 'M', u'浩'), + (0x2F904, 'M', u'浸'), + (0x2F905, 'M', u'涅'), + (0x2F906, 'M', u'𣴞'), + (0x2F907, 'M', u'洴'), + (0x2F908, 'M', u'港'), + (0x2F909, 'M', u'湮'), + (0x2F90A, 'M', u'㴳'), + (0x2F90B, 'M', u'滋'), + (0x2F90C, 'M', u'滇'), + (0x2F90D, 'M', u'𣻑'), + (0x2F90E, 'M', u'淹'), + (0x2F90F, 'M', u'潮'), + (0x2F910, 'M', u'𣽞'), + (0x2F911, 'M', u'𣾎'), + (0x2F912, 'M', u'濆'), + (0x2F913, 'M', u'瀹'), + (0x2F914, 'M', u'瀞'), + (0x2F915, 'M', u'瀛'), + (0x2F916, 'M', u'㶖'), + (0x2F917, 'M', u'灊'), + (0x2F918, 'M', u'災'), + (0x2F919, 'M', u'灷'), + (0x2F91A, 'M', u'炭'), + (0x2F91B, 'M', u'𠔥'), + (0x2F91C, 'M', u'煅'), + (0x2F91D, 'M', u'𤉣'), + (0x2F91E, 'M', u'熜'), + (0x2F91F, 'X'), + (0x2F920, 'M', u'爨'), + (0x2F921, 'M', u'爵'), + (0x2F922, 'M', u'牐'), + (0x2F923, 'M', u'𤘈'), + (0x2F924, 'M', u'犀'), + (0x2F925, 'M', u'犕'), + (0x2F926, 'M', u'𤜵'), + (0x2F927, 'M', u'𤠔'), + (0x2F928, 'M', u'獺'), + (0x2F929, 'M', u'王'), + (0x2F92A, 'M', u'㺬'), + (0x2F92B, 'M', u'玥'), + (0x2F92C, 'M', u'㺸'), + (0x2F92E, 'M', u'瑇'), + (0x2F92F, 'M', u'瑜'), + (0x2F930, 'M', u'瑱'), + (0x2F931, 'M', u'璅'), + (0x2F932, 'M', u'瓊'), + (0x2F933, 'M', u'㼛'), + (0x2F934, 'M', u'甤'), + (0x2F935, 'M', u'𤰶'), + (0x2F936, 'M', u'甾'), + (0x2F937, 'M', u'𤲒'), + (0x2F938, 'M', u'異'), + (0x2F939, 'M', u'𢆟'), + (0x2F93A, 'M', u'瘐'), + (0x2F93B, 'M', u'𤾡'), + (0x2F93C, 'M', u'𤾸'), + (0x2F93D, 'M', u'𥁄'), + (0x2F93E, 'M', u'㿼'), + (0x2F93F, 'M', u'䀈'), + (0x2F940, 'M', u'直'), + (0x2F941, 'M', u'𥃳'), + (0x2F942, 'M', u'𥃲'), + (0x2F943, 'M', u'𥄙'), + (0x2F944, 'M', u'𥄳'), + (0x2F945, 'M', u'眞'), + (0x2F946, 'M', u'真'), + (0x2F948, 'M', u'睊'), + (0x2F949, 'M', u'䀹'), + (0x2F94A, 'M', u'瞋'), + (0x2F94B, 'M', u'䁆'), + (0x2F94C, 'M', u'䂖'), + (0x2F94D, 'M', u'𥐝'), + (0x2F94E, 'M', u'硎'), + (0x2F94F, 'M', u'碌'), + (0x2F950, 'M', u'磌'), + (0x2F951, 'M', u'䃣'), + (0x2F952, 'M', u'𥘦'), + (0x2F953, 'M', u'祖'), + (0x2F954, 'M', u'𥚚'), + (0x2F955, 'M', u'𥛅'), + ] + +def _seg_76(): + return [ + (0x2F956, 'M', u'福'), + (0x2F957, 'M', u'秫'), + (0x2F958, 'M', u'䄯'), + (0x2F959, 'M', u'穀'), + (0x2F95A, 'M', u'穊'), + (0x2F95B, 'M', u'穏'), + (0x2F95C, 'M', u'𥥼'), + (0x2F95D, 'M', u'𥪧'), + (0x2F95F, 'X'), + (0x2F960, 'M', u'䈂'), + (0x2F961, 'M', u'𥮫'), + (0x2F962, 'M', u'篆'), + (0x2F963, 'M', u'築'), + (0x2F964, 'M', u'䈧'), + (0x2F965, 'M', u'𥲀'), + (0x2F966, 'M', u'糒'), + (0x2F967, 'M', u'䊠'), + (0x2F968, 'M', u'糨'), + (0x2F969, 'M', u'糣'), + (0x2F96A, 'M', u'紀'), + (0x2F96B, 'M', u'𥾆'), + (0x2F96C, 'M', u'絣'), + (0x2F96D, 'M', u'䌁'), + (0x2F96E, 'M', u'緇'), + (0x2F96F, 'M', u'縂'), + (0x2F970, 'M', u'繅'), + (0x2F971, 'M', u'䌴'), + (0x2F972, 'M', u'𦈨'), + (0x2F973, 'M', u'𦉇'), + (0x2F974, 'M', u'䍙'), + (0x2F975, 'M', u'𦋙'), + (0x2F976, 'M', u'罺'), + (0x2F977, 'M', u'𦌾'), + (0x2F978, 'M', u'羕'), + (0x2F979, 'M', u'翺'), + (0x2F97A, 'M', u'者'), + (0x2F97B, 'M', u'𦓚'), + (0x2F97C, 'M', u'𦔣'), + (0x2F97D, 'M', u'聠'), + (0x2F97E, 'M', u'𦖨'), + (0x2F97F, 'M', u'聰'), + (0x2F980, 'M', u'𣍟'), + (0x2F981, 'M', u'䏕'), + (0x2F982, 'M', u'育'), + (0x2F983, 'M', u'脃'), + (0x2F984, 'M', u'䐋'), + (0x2F985, 'M', u'脾'), + (0x2F986, 'M', u'媵'), + (0x2F987, 'M', u'𦞧'), + (0x2F988, 'M', u'𦞵'), + (0x2F989, 'M', u'𣎓'), + (0x2F98A, 'M', u'𣎜'), + (0x2F98B, 'M', u'舁'), + (0x2F98C, 'M', u'舄'), + (0x2F98D, 'M', u'辞'), + (0x2F98E, 'M', u'䑫'), + (0x2F98F, 'M', u'芑'), + (0x2F990, 'M', u'芋'), + (0x2F991, 'M', u'芝'), + (0x2F992, 'M', u'劳'), + (0x2F993, 'M', u'花'), + (0x2F994, 'M', u'芳'), + (0x2F995, 'M', u'芽'), + (0x2F996, 'M', u'苦'), + (0x2F997, 'M', u'𦬼'), + (0x2F998, 'M', u'若'), + (0x2F999, 'M', u'茝'), + (0x2F99A, 'M', u'荣'), + (0x2F99B, 'M', u'莭'), + (0x2F99C, 'M', u'茣'), + (0x2F99D, 'M', u'莽'), + (0x2F99E, 'M', u'菧'), + (0x2F99F, 'M', u'著'), + (0x2F9A0, 'M', u'荓'), + (0x2F9A1, 'M', u'菊'), + (0x2F9A2, 'M', u'菌'), + (0x2F9A3, 'M', u'菜'), + (0x2F9A4, 'M', u'𦰶'), + (0x2F9A5, 'M', u'𦵫'), + (0x2F9A6, 'M', u'𦳕'), + (0x2F9A7, 'M', u'䔫'), + (0x2F9A8, 'M', u'蓱'), + (0x2F9A9, 'M', u'蓳'), + (0x2F9AA, 'M', u'蔖'), + (0x2F9AB, 'M', u'𧏊'), + (0x2F9AC, 'M', u'蕤'), + (0x2F9AD, 'M', u'𦼬'), + (0x2F9AE, 'M', u'䕝'), + (0x2F9AF, 'M', u'䕡'), + (0x2F9B0, 'M', u'𦾱'), + (0x2F9B1, 'M', u'𧃒'), + (0x2F9B2, 'M', u'䕫'), + (0x2F9B3, 'M', u'虐'), + (0x2F9B4, 'M', u'虜'), + (0x2F9B5, 'M', u'虧'), + (0x2F9B6, 'M', u'虩'), + (0x2F9B7, 'M', u'蚩'), + (0x2F9B8, 'M', u'蚈'), + (0x2F9B9, 'M', u'蜎'), + (0x2F9BA, 'M', u'蛢'), + ] + +def _seg_77(): + return [ + (0x2F9BB, 'M', u'蝹'), + (0x2F9BC, 'M', u'蜨'), + (0x2F9BD, 'M', u'蝫'), + (0x2F9BE, 'M', u'螆'), + (0x2F9BF, 'X'), + (0x2F9C0, 'M', u'蟡'), + (0x2F9C1, 'M', u'蠁'), + (0x2F9C2, 'M', u'䗹'), + (0x2F9C3, 'M', u'衠'), + (0x2F9C4, 'M', u'衣'), + (0x2F9C5, 'M', u'𧙧'), + (0x2F9C6, 'M', u'裗'), + (0x2F9C7, 'M', u'裞'), + (0x2F9C8, 'M', u'䘵'), + (0x2F9C9, 'M', u'裺'), + (0x2F9CA, 'M', u'㒻'), + (0x2F9CB, 'M', u'𧢮'), + (0x2F9CC, 'M', u'𧥦'), + (0x2F9CD, 'M', u'䚾'), + (0x2F9CE, 'M', u'䛇'), + (0x2F9CF, 'M', u'誠'), + (0x2F9D0, 'M', u'諭'), + (0x2F9D1, 'M', u'變'), + (0x2F9D2, 'M', u'豕'), + (0x2F9D3, 'M', u'𧲨'), + (0x2F9D4, 'M', u'貫'), + (0x2F9D5, 'M', u'賁'), + (0x2F9D6, 'M', u'贛'), + (0x2F9D7, 'M', u'起'), + (0x2F9D8, 'M', u'𧼯'), + (0x2F9D9, 'M', u'𠠄'), + (0x2F9DA, 'M', u'跋'), + (0x2F9DB, 'M', u'趼'), + (0x2F9DC, 'M', u'跰'), + (0x2F9DD, 'M', u'𠣞'), + (0x2F9DE, 'M', u'軔'), + (0x2F9DF, 'M', u'輸'), + (0x2F9E0, 'M', u'𨗒'), + (0x2F9E1, 'M', u'𨗭'), + (0x2F9E2, 'M', u'邔'), + (0x2F9E3, 'M', u'郱'), + (0x2F9E4, 'M', u'鄑'), + (0x2F9E5, 'M', u'𨜮'), + (0x2F9E6, 'M', u'鄛'), + (0x2F9E7, 'M', u'鈸'), + (0x2F9E8, 'M', u'鋗'), + (0x2F9E9, 'M', u'鋘'), + (0x2F9EA, 'M', u'鉼'), + (0x2F9EB, 'M', u'鏹'), + (0x2F9EC, 'M', u'鐕'), + (0x2F9ED, 'M', u'𨯺'), + (0x2F9EE, 'M', u'開'), + (0x2F9EF, 'M', u'䦕'), + (0x2F9F0, 'M', u'閷'), + (0x2F9F1, 'M', u'𨵷'), + (0x2F9F2, 'M', u'䧦'), + (0x2F9F3, 'M', u'雃'), + (0x2F9F4, 'M', u'嶲'), + (0x2F9F5, 'M', u'霣'), + (0x2F9F6, 'M', u'𩅅'), + (0x2F9F7, 'M', u'𩈚'), + (0x2F9F8, 'M', u'䩮'), + (0x2F9F9, 'M', u'䩶'), + (0x2F9FA, 'M', u'韠'), + (0x2F9FB, 'M', u'𩐊'), + (0x2F9FC, 'M', u'䪲'), + (0x2F9FD, 'M', u'𩒖'), + (0x2F9FE, 'M', u'頋'), + (0x2FA00, 'M', u'頩'), + (0x2FA01, 'M', u'𩖶'), + (0x2FA02, 'M', u'飢'), + (0x2FA03, 'M', u'䬳'), + (0x2FA04, 'M', u'餩'), + (0x2FA05, 'M', u'馧'), + (0x2FA06, 'M', u'駂'), + (0x2FA07, 'M', u'駾'), + (0x2FA08, 'M', u'䯎'), + (0x2FA09, 'M', u'𩬰'), + (0x2FA0A, 'M', u'鬒'), + (0x2FA0B, 'M', u'鱀'), + (0x2FA0C, 'M', u'鳽'), + (0x2FA0D, 'M', u'䳎'), + (0x2FA0E, 'M', u'䳭'), + (0x2FA0F, 'M', u'鵧'), + (0x2FA10, 'M', u'𪃎'), + (0x2FA11, 'M', u'䳸'), + (0x2FA12, 'M', u'𪄅'), + (0x2FA13, 'M', u'𪈎'), + (0x2FA14, 'M', u'𪊑'), + (0x2FA15, 'M', u'麻'), + (0x2FA16, 'M', u'䵖'), + (0x2FA17, 'M', u'黹'), + (0x2FA18, 'M', u'黾'), + (0x2FA19, 'M', u'鼅'), + (0x2FA1A, 'M', u'鼏'), + (0x2FA1B, 'M', u'鼖'), + (0x2FA1C, 'M', u'鼻'), + (0x2FA1D, 'M', u'𪘀'), + (0x2FA1E, 'X'), + (0xE0100, 'I'), + ] + +def _seg_78(): + return [ + (0xE01F0, 'X'), + ] + +uts46data = tuple( + _seg_0() + + _seg_1() + + _seg_2() + + _seg_3() + + _seg_4() + + _seg_5() + + _seg_6() + + _seg_7() + + _seg_8() + + _seg_9() + + _seg_10() + + _seg_11() + + _seg_12() + + _seg_13() + + _seg_14() + + _seg_15() + + _seg_16() + + _seg_17() + + _seg_18() + + _seg_19() + + _seg_20() + + _seg_21() + + _seg_22() + + _seg_23() + + _seg_24() + + _seg_25() + + _seg_26() + + _seg_27() + + _seg_28() + + _seg_29() + + _seg_30() + + _seg_31() + + _seg_32() + + _seg_33() + + _seg_34() + + _seg_35() + + _seg_36() + + _seg_37() + + _seg_38() + + _seg_39() + + _seg_40() + + _seg_41() + + _seg_42() + + _seg_43() + + _seg_44() + + _seg_45() + + _seg_46() + + _seg_47() + + _seg_48() + + _seg_49() + + _seg_50() + + _seg_51() + + _seg_52() + + _seg_53() + + _seg_54() + + _seg_55() + + _seg_56() + + _seg_57() + + _seg_58() + + _seg_59() + + _seg_60() + + _seg_61() + + _seg_62() + + _seg_63() + + _seg_64() + + _seg_65() + + _seg_66() + + _seg_67() + + _seg_68() + + _seg_69() + + _seg_70() + + _seg_71() + + _seg_72() + + _seg_73() + + _seg_74() + + _seg_75() + + _seg_76() + + _seg_77() + + _seg_78() +) diff --git a/venv2/lib/python2.7/site-packages/pip/_vendor/ipaddress.py b/venv2/lib/python2.7/site-packages/pip/_vendor/ipaddress.py new file mode 100644 index 0000000..f2d0766 --- /dev/null +++ b/venv2/lib/python2.7/site-packages/pip/_vendor/ipaddress.py @@ -0,0 +1,2419 @@ +# Copyright 2007 Google Inc. +# Licensed to PSF under a Contributor Agreement. + +"""A fast, lightweight IPv4/IPv6 manipulation library in Python. + +This library is used to create/poke/manipulate IPv4 and IPv6 addresses +and networks. + +""" + +from __future__ import unicode_literals + + +import itertools +import struct + +__version__ = '1.0.22' + +# Compatibility functions +_compat_int_types = (int,) +try: + _compat_int_types = (int, long) +except NameError: + pass +try: + _compat_str = unicode +except NameError: + _compat_str = str + assert bytes != str +if b'\0'[0] == 0: # Python 3 semantics + def _compat_bytes_to_byte_vals(byt): + return byt +else: + def _compat_bytes_to_byte_vals(byt): + return [struct.unpack(b'!B', b)[0] for b in byt] +try: + _compat_int_from_byte_vals = int.from_bytes +except AttributeError: + def _compat_int_from_byte_vals(bytvals, endianess): + assert endianess == 'big' + res = 0 + for bv in bytvals: + assert isinstance(bv, _compat_int_types) + res = (res << 8) + bv + return res + + +def _compat_to_bytes(intval, length, endianess): + assert isinstance(intval, _compat_int_types) + assert endianess == 'big' + if length == 4: + if intval < 0 or intval >= 2 ** 32: + raise struct.error("integer out of range for 'I' format code") + return struct.pack(b'!I', intval) + elif length == 16: + if intval < 0 or intval >= 2 ** 128: + raise struct.error("integer out of range for 'QQ' format code") + return struct.pack(b'!QQ', intval >> 64, intval & 0xffffffffffffffff) + else: + raise NotImplementedError() + + +if hasattr(int, 'bit_length'): + # Not int.bit_length , since that won't work in 2.7 where long exists + def _compat_bit_length(i): + return i.bit_length() +else: + def _compat_bit_length(i): + for res in itertools.count(): + if i >> res == 0: + return res + + +def _compat_range(start, end, step=1): + assert step > 0 + i = start + while i < end: + yield i + i += step + + +class _TotalOrderingMixin(object): + __slots__ = () + + # Helper that derives the other comparison operations from + # __lt__ and __eq__ + # We avoid functools.total_ordering because it doesn't handle + # NotImplemented correctly yet (http://bugs.python.org/issue10042) + def __eq__(self, other): + raise NotImplementedError + + def __ne__(self, other): + equal = self.__eq__(other) + if equal is NotImplemented: + return NotImplemented + return not equal + + def __lt__(self, other): + raise NotImplementedError + + def __le__(self, other): + less = self.__lt__(other) + if less is NotImplemented or not less: + return self.__eq__(other) + return less + + def __gt__(self, other): + less = self.__lt__(other) + if less is NotImplemented: + return NotImplemented + equal = self.__eq__(other) + if equal is NotImplemented: + return NotImplemented + return not (less or equal) + + def __ge__(self, other): + less = self.__lt__(other) + if less is NotImplemented: + return NotImplemented + return not less + + +IPV4LENGTH = 32 +IPV6LENGTH = 128 + + +class AddressValueError(ValueError): + """A Value Error related to the address.""" + + +class NetmaskValueError(ValueError): + """A Value Error related to the netmask.""" + + +def ip_address(address): + """Take an IP string/int and return an object of the correct type. + + Args: + address: A string or integer, the IP address. Either IPv4 or + IPv6 addresses may be supplied; integers less than 2**32 will + be considered to be IPv4 by default. + + Returns: + An IPv4Address or IPv6Address object. + + Raises: + ValueError: if the *address* passed isn't either a v4 or a v6 + address + + """ + try: + return IPv4Address(address) + except (AddressValueError, NetmaskValueError): + pass + + try: + return IPv6Address(address) + except (AddressValueError, NetmaskValueError): + pass + + if isinstance(address, bytes): + raise AddressValueError( + '%r does not appear to be an IPv4 or IPv6 address. ' + 'Did you pass in a bytes (str in Python 2) instead of' + ' a unicode object?' % address) + + raise ValueError('%r does not appear to be an IPv4 or IPv6 address' % + address) + + +def ip_network(address, strict=True): + """Take an IP string/int and return an object of the correct type. + + Args: + address: A string or integer, the IP network. Either IPv4 or + IPv6 networks may be supplied; integers less than 2**32 will + be considered to be IPv4 by default. + + Returns: + An IPv4Network or IPv6Network object. + + Raises: + ValueError: if the string passed isn't either a v4 or a v6 + address. Or if the network has host bits set. + + """ + try: + return IPv4Network(address, strict) + except (AddressValueError, NetmaskValueError): + pass + + try: + return IPv6Network(address, strict) + except (AddressValueError, NetmaskValueError): + pass + + if isinstance(address, bytes): + raise AddressValueError( + '%r does not appear to be an IPv4 or IPv6 network. ' + 'Did you pass in a bytes (str in Python 2) instead of' + ' a unicode object?' % address) + + raise ValueError('%r does not appear to be an IPv4 or IPv6 network' % + address) + + +def ip_interface(address): + """Take an IP string/int and return an object of the correct type. + + Args: + address: A string or integer, the IP address. Either IPv4 or + IPv6 addresses may be supplied; integers less than 2**32 will + be considered to be IPv4 by default. + + Returns: + An IPv4Interface or IPv6Interface object. + + Raises: + ValueError: if the string passed isn't either a v4 or a v6 + address. + + Notes: + The IPv?Interface classes describe an Address on a particular + Network, so they're basically a combination of both the Address + and Network classes. + + """ + try: + return IPv4Interface(address) + except (AddressValueError, NetmaskValueError): + pass + + try: + return IPv6Interface(address) + except (AddressValueError, NetmaskValueError): + pass + + raise ValueError('%r does not appear to be an IPv4 or IPv6 interface' % + address) + + +def v4_int_to_packed(address): + """Represent an address as 4 packed bytes in network (big-endian) order. + + Args: + address: An integer representation of an IPv4 IP address. + + Returns: + The integer address packed as 4 bytes in network (big-endian) order. + + Raises: + ValueError: If the integer is negative or too large to be an + IPv4 IP address. + + """ + try: + return _compat_to_bytes(address, 4, 'big') + except (struct.error, OverflowError): + raise ValueError("Address negative or too large for IPv4") + + +def v6_int_to_packed(address): + """Represent an address as 16 packed bytes in network (big-endian) order. + + Args: + address: An integer representation of an IPv6 IP address. + + Returns: + The integer address packed as 16 bytes in network (big-endian) order. + + """ + try: + return _compat_to_bytes(address, 16, 'big') + except (struct.error, OverflowError): + raise ValueError("Address negative or too large for IPv6") + + +def _split_optional_netmask(address): + """Helper to split the netmask and raise AddressValueError if needed""" + addr = _compat_str(address).split('/') + if len(addr) > 2: + raise AddressValueError("Only one '/' permitted in %r" % address) + return addr + + +def _find_address_range(addresses): + """Find a sequence of sorted deduplicated IPv#Address. + + Args: + addresses: a list of IPv#Address objects. + + Yields: + A tuple containing the first and last IP addresses in the sequence. + + """ + it = iter(addresses) + first = last = next(it) + for ip in it: + if ip._ip != last._ip + 1: + yield first, last + first = ip + last = ip + yield first, last + + +def _count_righthand_zero_bits(number, bits): + """Count the number of zero bits on the right hand side. + + Args: + number: an integer. + bits: maximum number of bits to count. + + Returns: + The number of zero bits on the right hand side of the number. + + """ + if number == 0: + return bits + return min(bits, _compat_bit_length(~number & (number - 1))) + + +def summarize_address_range(first, last): + """Summarize a network range given the first and last IP addresses. + + Example: + >>> list(summarize_address_range(IPv4Address('192.0.2.0'), + ... IPv4Address('192.0.2.130'))) + ... #doctest: +NORMALIZE_WHITESPACE + [IPv4Network('192.0.2.0/25'), IPv4Network('192.0.2.128/31'), + IPv4Network('192.0.2.130/32')] + + Args: + first: the first IPv4Address or IPv6Address in the range. + last: the last IPv4Address or IPv6Address in the range. + + Returns: + An iterator of the summarized IPv(4|6) network objects. + + Raise: + TypeError: + If the first and last objects are not IP addresses. + If the first and last objects are not the same version. + ValueError: + If the last object is not greater than the first. + If the version of the first address is not 4 or 6. + + """ + if (not (isinstance(first, _BaseAddress) and + isinstance(last, _BaseAddress))): + raise TypeError('first and last must be IP addresses, not networks') + if first.version != last.version: + raise TypeError("%s and %s are not of the same version" % ( + first, last)) + if first > last: + raise ValueError('last IP address must be greater than first') + + if first.version == 4: + ip = IPv4Network + elif first.version == 6: + ip = IPv6Network + else: + raise ValueError('unknown IP version') + + ip_bits = first._max_prefixlen + first_int = first._ip + last_int = last._ip + while first_int <= last_int: + nbits = min(_count_righthand_zero_bits(first_int, ip_bits), + _compat_bit_length(last_int - first_int + 1) - 1) + net = ip((first_int, ip_bits - nbits)) + yield net + first_int += 1 << nbits + if first_int - 1 == ip._ALL_ONES: + break + + +def _collapse_addresses_internal(addresses): + """Loops through the addresses, collapsing concurrent netblocks. + + Example: + + ip1 = IPv4Network('192.0.2.0/26') + ip2 = IPv4Network('192.0.2.64/26') + ip3 = IPv4Network('192.0.2.128/26') + ip4 = IPv4Network('192.0.2.192/26') + + _collapse_addresses_internal([ip1, ip2, ip3, ip4]) -> + [IPv4Network('192.0.2.0/24')] + + This shouldn't be called directly; it is called via + collapse_addresses([]). + + Args: + addresses: A list of IPv4Network's or IPv6Network's + + Returns: + A list of IPv4Network's or IPv6Network's depending on what we were + passed. + + """ + # First merge + to_merge = list(addresses) + subnets = {} + while to_merge: + net = to_merge.pop() + supernet = net.supernet() + existing = subnets.get(supernet) + if existing is None: + subnets[supernet] = net + elif existing != net: + # Merge consecutive subnets + del subnets[supernet] + to_merge.append(supernet) + # Then iterate over resulting networks, skipping subsumed subnets + last = None + for net in sorted(subnets.values()): + if last is not None: + # Since they are sorted, + # last.network_address <= net.network_address is a given. + if last.broadcast_address >= net.broadcast_address: + continue + yield net + last = net + + +def collapse_addresses(addresses): + """Collapse a list of IP objects. + + Example: + collapse_addresses([IPv4Network('192.0.2.0/25'), + IPv4Network('192.0.2.128/25')]) -> + [IPv4Network('192.0.2.0/24')] + + Args: + addresses: An iterator of IPv4Network or IPv6Network objects. + + Returns: + An iterator of the collapsed IPv(4|6)Network objects. + + Raises: + TypeError: If passed a list of mixed version objects. + + """ + addrs = [] + ips = [] + nets = [] + + # split IP addresses and networks + for ip in addresses: + if isinstance(ip, _BaseAddress): + if ips and ips[-1]._version != ip._version: + raise TypeError("%s and %s are not of the same version" % ( + ip, ips[-1])) + ips.append(ip) + elif ip._prefixlen == ip._max_prefixlen: + if ips and ips[-1]._version != ip._version: + raise TypeError("%s and %s are not of the same version" % ( + ip, ips[-1])) + try: + ips.append(ip.ip) + except AttributeError: + ips.append(ip.network_address) + else: + if nets and nets[-1]._version != ip._version: + raise TypeError("%s and %s are not of the same version" % ( + ip, nets[-1])) + nets.append(ip) + + # sort and dedup + ips = sorted(set(ips)) + + # find consecutive address ranges in the sorted sequence and summarize them + if ips: + for first, last in _find_address_range(ips): + addrs.extend(summarize_address_range(first, last)) + + return _collapse_addresses_internal(addrs + nets) + + +def get_mixed_type_key(obj): + """Return a key suitable for sorting between networks and addresses. + + Address and Network objects are not sortable by default; they're + fundamentally different so the expression + + IPv4Address('192.0.2.0') <= IPv4Network('192.0.2.0/24') + + doesn't make any sense. There are some times however, where you may wish + to have ipaddress sort these for you anyway. If you need to do this, you + can use this function as the key= argument to sorted(). + + Args: + obj: either a Network or Address object. + Returns: + appropriate key. + + """ + if isinstance(obj, _BaseNetwork): + return obj._get_networks_key() + elif isinstance(obj, _BaseAddress): + return obj._get_address_key() + return NotImplemented + + +class _IPAddressBase(_TotalOrderingMixin): + + """The mother class.""" + + __slots__ = () + + @property + def exploded(self): + """Return the longhand version of the IP address as a string.""" + return self._explode_shorthand_ip_string() + + @property + def compressed(self): + """Return the shorthand version of the IP address as a string.""" + return _compat_str(self) + + @property + def reverse_pointer(self): + """The name of the reverse DNS pointer for the IP address, e.g.: + >>> ipaddress.ip_address("127.0.0.1").reverse_pointer + '1.0.0.127.in-addr.arpa' + >>> ipaddress.ip_address("2001:db8::1").reverse_pointer + '1.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.8.b.d.0.1.0.0.2.ip6.arpa' + + """ + return self._reverse_pointer() + + @property + def version(self): + msg = '%200s has no version specified' % (type(self),) + raise NotImplementedError(msg) + + def _check_int_address(self, address): + if address < 0: + msg = "%d (< 0) is not permitted as an IPv%d address" + raise AddressValueError(msg % (address, self._version)) + if address > self._ALL_ONES: + msg = "%d (>= 2**%d) is not permitted as an IPv%d address" + raise AddressValueError(msg % (address, self._max_prefixlen, + self._version)) + + def _check_packed_address(self, address, expected_len): + address_len = len(address) + if address_len != expected_len: + msg = ( + '%r (len %d != %d) is not permitted as an IPv%d address. ' + 'Did you pass in a bytes (str in Python 2) instead of' + ' a unicode object?') + raise AddressValueError(msg % (address, address_len, + expected_len, self._version)) + + @classmethod + def _ip_int_from_prefix(cls, prefixlen): + """Turn the prefix length into a bitwise netmask + + Args: + prefixlen: An integer, the prefix length. + + Returns: + An integer. + + """ + return cls._ALL_ONES ^ (cls._ALL_ONES >> prefixlen) + + @classmethod + def _prefix_from_ip_int(cls, ip_int): + """Return prefix length from the bitwise netmask. + + Args: + ip_int: An integer, the netmask in expanded bitwise format + + Returns: + An integer, the prefix length. + + Raises: + ValueError: If the input intermingles zeroes & ones + """ + trailing_zeroes = _count_righthand_zero_bits(ip_int, + cls._max_prefixlen) + prefixlen = cls._max_prefixlen - trailing_zeroes + leading_ones = ip_int >> trailing_zeroes + all_ones = (1 << prefixlen) - 1 + if leading_ones != all_ones: + byteslen = cls._max_prefixlen // 8 + details = _compat_to_bytes(ip_int, byteslen, 'big') + msg = 'Netmask pattern %r mixes zeroes & ones' + raise ValueError(msg % details) + return prefixlen + + @classmethod + def _report_invalid_netmask(cls, netmask_str): + msg = '%r is not a valid netmask' % netmask_str + raise NetmaskValueError(msg) + + @classmethod + def _prefix_from_prefix_string(cls, prefixlen_str): + """Return prefix length from a numeric string + + Args: + prefixlen_str: The string to be converted + + Returns: + An integer, the prefix length. + + Raises: + NetmaskValueError: If the input is not a valid netmask + """ + # int allows a leading +/- as well as surrounding whitespace, + # so we ensure that isn't the case + if not _BaseV4._DECIMAL_DIGITS.issuperset(prefixlen_str): + cls._report_invalid_netmask(prefixlen_str) + try: + prefixlen = int(prefixlen_str) + except ValueError: + cls._report_invalid_netmask(prefixlen_str) + if not (0 <= prefixlen <= cls._max_prefixlen): + cls._report_invalid_netmask(prefixlen_str) + return prefixlen + + @classmethod + def _prefix_from_ip_string(cls, ip_str): + """Turn a netmask/hostmask string into a prefix length + + Args: + ip_str: The netmask/hostmask to be converted + + Returns: + An integer, the prefix length. + + Raises: + NetmaskValueError: If the input is not a valid netmask/hostmask + """ + # Parse the netmask/hostmask like an IP address. + try: + ip_int = cls._ip_int_from_string(ip_str) + except AddressValueError: + cls._report_invalid_netmask(ip_str) + + # Try matching a netmask (this would be /1*0*/ as a bitwise regexp). + # Note that the two ambiguous cases (all-ones and all-zeroes) are + # treated as netmasks. + try: + return cls._prefix_from_ip_int(ip_int) + except ValueError: + pass + + # Invert the bits, and try matching a /0+1+/ hostmask instead. + ip_int ^= cls._ALL_ONES + try: + return cls._prefix_from_ip_int(ip_int) + except ValueError: + cls._report_invalid_netmask(ip_str) + + def __reduce__(self): + return self.__class__, (_compat_str(self),) + + +class _BaseAddress(_IPAddressBase): + + """A generic IP object. + + This IP class contains the version independent methods which are + used by single IP addresses. + """ + + __slots__ = () + + def __int__(self): + return self._ip + + def __eq__(self, other): + try: + return (self._ip == other._ip and + self._version == other._version) + except AttributeError: + return NotImplemented + + def __lt__(self, other): + if not isinstance(other, _IPAddressBase): + return NotImplemented + if not isinstance(other, _BaseAddress): + raise TypeError('%s and %s are not of the same type' % ( + self, other)) + if self._version != other._version: + raise TypeError('%s and %s are not of the same version' % ( + self, other)) + if self._ip != other._ip: + return self._ip < other._ip + return False + + # Shorthand for Integer addition and subtraction. This is not + # meant to ever support addition/subtraction of addresses. + def __add__(self, other): + if not isinstance(other, _compat_int_types): + return NotImplemented + return self.__class__(int(self) + other) + + def __sub__(self, other): + if not isinstance(other, _compat_int_types): + return NotImplemented + return self.__class__(int(self) - other) + + def __repr__(self): + return '%s(%r)' % (self.__class__.__name__, _compat_str(self)) + + def __str__(self): + return _compat_str(self._string_from_ip_int(self._ip)) + + def __hash__(self): + return hash(hex(int(self._ip))) + + def _get_address_key(self): + return (self._version, self) + + def __reduce__(self): + return self.__class__, (self._ip,) + + +class _BaseNetwork(_IPAddressBase): + + """A generic IP network object. + + This IP class contains the version independent methods which are + used by networks. + + """ + def __init__(self, address): + self._cache = {} + + def __repr__(self): + return '%s(%r)' % (self.__class__.__name__, _compat_str(self)) + + def __str__(self): + return '%s/%d' % (self.network_address, self.prefixlen) + + def hosts(self): + """Generate Iterator over usable hosts in a network. + + This is like __iter__ except it doesn't return the network + or broadcast addresses. + + """ + network = int(self.network_address) + broadcast = int(self.broadcast_address) + for x in _compat_range(network + 1, broadcast): + yield self._address_class(x) + + def __iter__(self): + network = int(self.network_address) + broadcast = int(self.broadcast_address) + for x in _compat_range(network, broadcast + 1): + yield self._address_class(x) + + def __getitem__(self, n): + network = int(self.network_address) + broadcast = int(self.broadcast_address) + if n >= 0: + if network + n > broadcast: + raise IndexError('address out of range') + return self._address_class(network + n) + else: + n += 1 + if broadcast + n < network: + raise IndexError('address out of range') + return self._address_class(broadcast + n) + + def __lt__(self, other): + if not isinstance(other, _IPAddressBase): + return NotImplemented + if not isinstance(other, _BaseNetwork): + raise TypeError('%s and %s are not of the same type' % ( + self, other)) + if self._version != other._version: + raise TypeError('%s and %s are not of the same version' % ( + self, other)) + if self.network_address != other.network_address: + return self.network_address < other.network_address + if self.netmask != other.netmask: + return self.netmask < other.netmask + return False + + def __eq__(self, other): + try: + return (self._version == other._version and + self.network_address == other.network_address and + int(self.netmask) == int(other.netmask)) + except AttributeError: + return NotImplemented + + def __hash__(self): + return hash(int(self.network_address) ^ int(self.netmask)) + + def __contains__(self, other): + # always false if one is v4 and the other is v6. + if self._version != other._version: + return False + # dealing with another network. + if isinstance(other, _BaseNetwork): + return False + # dealing with another address + else: + # address + return (int(self.network_address) <= int(other._ip) <= + int(self.broadcast_address)) + + def overlaps(self, other): + """Tell if self is partly contained in other.""" + return self.network_address in other or ( + self.broadcast_address in other or ( + other.network_address in self or ( + other.broadcast_address in self))) + + @property + def broadcast_address(self): + x = self._cache.get('broadcast_address') + if x is None: + x = self._address_class(int(self.network_address) | + int(self.hostmask)) + self._cache['broadcast_address'] = x + return x + + @property + def hostmask(self): + x = self._cache.get('hostmask') + if x is None: + x = self._address_class(int(self.netmask) ^ self._ALL_ONES) + self._cache['hostmask'] = x + return x + + @property + def with_prefixlen(self): + return '%s/%d' % (self.network_address, self._prefixlen) + + @property + def with_netmask(self): + return '%s/%s' % (self.network_address, self.netmask) + + @property + def with_hostmask(self): + return '%s/%s' % (self.network_address, self.hostmask) + + @property + def num_addresses(self): + """Number of hosts in the current subnet.""" + return int(self.broadcast_address) - int(self.network_address) + 1 + + @property + def _address_class(self): + # Returning bare address objects (rather than interfaces) allows for + # more consistent behaviour across the network address, broadcast + # address and individual host addresses. + msg = '%200s has no associated address class' % (type(self),) + raise NotImplementedError(msg) + + @property + def prefixlen(self): + return self._prefixlen + + def address_exclude(self, other): + """Remove an address from a larger block. + + For example: + + addr1 = ip_network('192.0.2.0/28') + addr2 = ip_network('192.0.2.1/32') + list(addr1.address_exclude(addr2)) = + [IPv4Network('192.0.2.0/32'), IPv4Network('192.0.2.2/31'), + IPv4Network('192.0.2.4/30'), IPv4Network('192.0.2.8/29')] + + or IPv6: + + addr1 = ip_network('2001:db8::1/32') + addr2 = ip_network('2001:db8::1/128') + list(addr1.address_exclude(addr2)) = + [ip_network('2001:db8::1/128'), + ip_network('2001:db8::2/127'), + ip_network('2001:db8::4/126'), + ip_network('2001:db8::8/125'), + ... + ip_network('2001:db8:8000::/33')] + + Args: + other: An IPv4Network or IPv6Network object of the same type. + + Returns: + An iterator of the IPv(4|6)Network objects which is self + minus other. + + Raises: + TypeError: If self and other are of differing address + versions, or if other is not a network object. + ValueError: If other is not completely contained by self. + + """ + if not self._version == other._version: + raise TypeError("%s and %s are not of the same version" % ( + self, other)) + + if not isinstance(other, _BaseNetwork): + raise TypeError("%s is not a network object" % other) + + if not other.subnet_of(self): + raise ValueError('%s not contained in %s' % (other, self)) + if other == self: + return + + # Make sure we're comparing the network of other. + other = other.__class__('%s/%s' % (other.network_address, + other.prefixlen)) + + s1, s2 = self.subnets() + while s1 != other and s2 != other: + if other.subnet_of(s1): + yield s2 + s1, s2 = s1.subnets() + elif other.subnet_of(s2): + yield s1 + s1, s2 = s2.subnets() + else: + # If we got here, there's a bug somewhere. + raise AssertionError('Error performing exclusion: ' + 's1: %s s2: %s other: %s' % + (s1, s2, other)) + if s1 == other: + yield s2 + elif s2 == other: + yield s1 + else: + # If we got here, there's a bug somewhere. + raise AssertionError('Error performing exclusion: ' + 's1: %s s2: %s other: %s' % + (s1, s2, other)) + + def compare_networks(self, other): + """Compare two IP objects. + + This is only concerned about the comparison of the integer + representation of the network addresses. This means that the + host bits aren't considered at all in this method. If you want + to compare host bits, you can easily enough do a + 'HostA._ip < HostB._ip' + + Args: + other: An IP object. + + Returns: + If the IP versions of self and other are the same, returns: + + -1 if self < other: + eg: IPv4Network('192.0.2.0/25') < IPv4Network('192.0.2.128/25') + IPv6Network('2001:db8::1000/124') < + IPv6Network('2001:db8::2000/124') + 0 if self == other + eg: IPv4Network('192.0.2.0/24') == IPv4Network('192.0.2.0/24') + IPv6Network('2001:db8::1000/124') == + IPv6Network('2001:db8::1000/124') + 1 if self > other + eg: IPv4Network('192.0.2.128/25') > IPv4Network('192.0.2.0/25') + IPv6Network('2001:db8::2000/124') > + IPv6Network('2001:db8::1000/124') + + Raises: + TypeError if the IP versions are different. + + """ + # does this need to raise a ValueError? + if self._version != other._version: + raise TypeError('%s and %s are not of the same type' % ( + self, other)) + # self._version == other._version below here: + if self.network_address < other.network_address: + return -1 + if self.network_address > other.network_address: + return 1 + # self.network_address == other.network_address below here: + if self.netmask < other.netmask: + return -1 + if self.netmask > other.netmask: + return 1 + return 0 + + def _get_networks_key(self): + """Network-only key function. + + Returns an object that identifies this address' network and + netmask. This function is a suitable "key" argument for sorted() + and list.sort(). + + """ + return (self._version, self.network_address, self.netmask) + + def subnets(self, prefixlen_diff=1, new_prefix=None): + """The subnets which join to make the current subnet. + + In the case that self contains only one IP + (self._prefixlen == 32 for IPv4 or self._prefixlen == 128 + for IPv6), yield an iterator with just ourself. + + Args: + prefixlen_diff: An integer, the amount the prefix length + should be increased by. This should not be set if + new_prefix is also set. + new_prefix: The desired new prefix length. This must be a + larger number (smaller prefix) than the existing prefix. + This should not be set if prefixlen_diff is also set. + + Returns: + An iterator of IPv(4|6) objects. + + Raises: + ValueError: The prefixlen_diff is too small or too large. + OR + prefixlen_diff and new_prefix are both set or new_prefix + is a smaller number than the current prefix (smaller + number means a larger network) + + """ + if self._prefixlen == self._max_prefixlen: + yield self + return + + if new_prefix is not None: + if new_prefix < self._prefixlen: + raise ValueError('new prefix must be longer') + if prefixlen_diff != 1: + raise ValueError('cannot set prefixlen_diff and new_prefix') + prefixlen_diff = new_prefix - self._prefixlen + + if prefixlen_diff < 0: + raise ValueError('prefix length diff must be > 0') + new_prefixlen = self._prefixlen + prefixlen_diff + + if new_prefixlen > self._max_prefixlen: + raise ValueError( + 'prefix length diff %d is invalid for netblock %s' % ( + new_prefixlen, self)) + + start = int(self.network_address) + end = int(self.broadcast_address) + 1 + step = (int(self.hostmask) + 1) >> prefixlen_diff + for new_addr in _compat_range(start, end, step): + current = self.__class__((new_addr, new_prefixlen)) + yield current + + def supernet(self, prefixlen_diff=1, new_prefix=None): + """The supernet containing the current network. + + Args: + prefixlen_diff: An integer, the amount the prefix length of + the network should be decreased by. For example, given a + /24 network and a prefixlen_diff of 3, a supernet with a + /21 netmask is returned. + + Returns: + An IPv4 network object. + + Raises: + ValueError: If self.prefixlen - prefixlen_diff < 0. I.e., you have + a negative prefix length. + OR + If prefixlen_diff and new_prefix are both set or new_prefix is a + larger number than the current prefix (larger number means a + smaller network) + + """ + if self._prefixlen == 0: + return self + + if new_prefix is not None: + if new_prefix > self._prefixlen: + raise ValueError('new prefix must be shorter') + if prefixlen_diff != 1: + raise ValueError('cannot set prefixlen_diff and new_prefix') + prefixlen_diff = self._prefixlen - new_prefix + + new_prefixlen = self.prefixlen - prefixlen_diff + if new_prefixlen < 0: + raise ValueError( + 'current prefixlen is %d, cannot have a prefixlen_diff of %d' % + (self.prefixlen, prefixlen_diff)) + return self.__class__(( + int(self.network_address) & (int(self.netmask) << prefixlen_diff), + new_prefixlen)) + + @property + def is_multicast(self): + """Test if the address is reserved for multicast use. + + Returns: + A boolean, True if the address is a multicast address. + See RFC 2373 2.7 for details. + + """ + return (self.network_address.is_multicast and + self.broadcast_address.is_multicast) + + @staticmethod + def _is_subnet_of(a, b): + try: + # Always false if one is v4 and the other is v6. + if a._version != b._version: + raise TypeError("%s and %s are not of the same version" (a, b)) + return (b.network_address <= a.network_address and + b.broadcast_address >= a.broadcast_address) + except AttributeError: + raise TypeError("Unable to test subnet containment " + "between %s and %s" % (a, b)) + + def subnet_of(self, other): + """Return True if this network is a subnet of other.""" + return self._is_subnet_of(self, other) + + def supernet_of(self, other): + """Return True if this network is a supernet of other.""" + return self._is_subnet_of(other, self) + + @property + def is_reserved(self): + """Test if the address is otherwise IETF reserved. + + Returns: + A boolean, True if the address is within one of the + reserved IPv6 Network ranges. + + """ + return (self.network_address.is_reserved and + self.broadcast_address.is_reserved) + + @property + def is_link_local(self): + """Test if the address is reserved for link-local. + + Returns: + A boolean, True if the address is reserved per RFC 4291. + + """ + return (self.network_address.is_link_local and + self.broadcast_address.is_link_local) + + @property + def is_private(self): + """Test if this address is allocated for private networks. + + Returns: + A boolean, True if the address is reserved per + iana-ipv4-special-registry or iana-ipv6-special-registry. + + """ + return (self.network_address.is_private and + self.broadcast_address.is_private) + + @property + def is_global(self): + """Test if this address is allocated for public networks. + + Returns: + A boolean, True if the address is not reserved per + iana-ipv4-special-registry or iana-ipv6-special-registry. + + """ + return not self.is_private + + @property + def is_unspecified(self): + """Test if the address is unspecified. + + Returns: + A boolean, True if this is the unspecified address as defined in + RFC 2373 2.5.2. + + """ + return (self.network_address.is_unspecified and + self.broadcast_address.is_unspecified) + + @property + def is_loopback(self): + """Test if the address is a loopback address. + + Returns: + A boolean, True if the address is a loopback address as defined in + RFC 2373 2.5.3. + + """ + return (self.network_address.is_loopback and + self.broadcast_address.is_loopback) + + +class _BaseV4(object): + + """Base IPv4 object. + + The following methods are used by IPv4 objects in both single IP + addresses and networks. + + """ + + __slots__ = () + _version = 4 + # Equivalent to 255.255.255.255 or 32 bits of 1's. + _ALL_ONES = (2 ** IPV4LENGTH) - 1 + _DECIMAL_DIGITS = frozenset('0123456789') + + # the valid octets for host and netmasks. only useful for IPv4. + _valid_mask_octets = frozenset([255, 254, 252, 248, 240, 224, 192, 128, 0]) + + _max_prefixlen = IPV4LENGTH + # There are only a handful of valid v4 netmasks, so we cache them all + # when constructed (see _make_netmask()). + _netmask_cache = {} + + def _explode_shorthand_ip_string(self): + return _compat_str(self) + + @classmethod + def _make_netmask(cls, arg): + """Make a (netmask, prefix_len) tuple from the given argument. + + Argument can be: + - an integer (the prefix length) + - a string representing the prefix length (e.g. "24") + - a string representing the prefix netmask (e.g. "255.255.255.0") + """ + if arg not in cls._netmask_cache: + if isinstance(arg, _compat_int_types): + prefixlen = arg + else: + try: + # Check for a netmask in prefix length form + prefixlen = cls._prefix_from_prefix_string(arg) + except NetmaskValueError: + # Check for a netmask or hostmask in dotted-quad form. + # This may raise NetmaskValueError. + prefixlen = cls._prefix_from_ip_string(arg) + netmask = IPv4Address(cls._ip_int_from_prefix(prefixlen)) + cls._netmask_cache[arg] = netmask, prefixlen + return cls._netmask_cache[arg] + + @classmethod + def _ip_int_from_string(cls, ip_str): + """Turn the given IP string into an integer for comparison. + + Args: + ip_str: A string, the IP ip_str. + + Returns: + The IP ip_str as an integer. + + Raises: + AddressValueError: if ip_str isn't a valid IPv4 Address. + + """ + if not ip_str: + raise AddressValueError('Address cannot be empty') + + octets = ip_str.split('.') + if len(octets) != 4: + raise AddressValueError("Expected 4 octets in %r" % ip_str) + + try: + return _compat_int_from_byte_vals( + map(cls._parse_octet, octets), 'big') + except ValueError as exc: + raise AddressValueError("%s in %r" % (exc, ip_str)) + + @classmethod + def _parse_octet(cls, octet_str): + """Convert a decimal octet into an integer. + + Args: + octet_str: A string, the number to parse. + + Returns: + The octet as an integer. + + Raises: + ValueError: if the octet isn't strictly a decimal from [0..255]. + + """ + if not octet_str: + raise ValueError("Empty octet not permitted") + # Whitelist the characters, since int() allows a lot of bizarre stuff. + if not cls._DECIMAL_DIGITS.issuperset(octet_str): + msg = "Only decimal digits permitted in %r" + raise ValueError(msg % octet_str) + # We do the length check second, since the invalid character error + # is likely to be more informative for the user + if len(octet_str) > 3: + msg = "At most 3 characters permitted in %r" + raise ValueError(msg % octet_str) + # Convert to integer (we know digits are legal) + octet_int = int(octet_str, 10) + # Any octets that look like they *might* be written in octal, + # and which don't look exactly the same in both octal and + # decimal are rejected as ambiguous + if octet_int > 7 and octet_str[0] == '0': + msg = "Ambiguous (octal/decimal) value in %r not permitted" + raise ValueError(msg % octet_str) + if octet_int > 255: + raise ValueError("Octet %d (> 255) not permitted" % octet_int) + return octet_int + + @classmethod + def _string_from_ip_int(cls, ip_int): + """Turns a 32-bit integer into dotted decimal notation. + + Args: + ip_int: An integer, the IP address. + + Returns: + The IP address as a string in dotted decimal notation. + + """ + return '.'.join(_compat_str(struct.unpack(b'!B', b)[0] + if isinstance(b, bytes) + else b) + for b in _compat_to_bytes(ip_int, 4, 'big')) + + def _is_hostmask(self, ip_str): + """Test if the IP string is a hostmask (rather than a netmask). + + Args: + ip_str: A string, the potential hostmask. + + Returns: + A boolean, True if the IP string is a hostmask. + + """ + bits = ip_str.split('.') + try: + parts = [x for x in map(int, bits) if x in self._valid_mask_octets] + except ValueError: + return False + if len(parts) != len(bits): + return False + if parts[0] < parts[-1]: + return True + return False + + def _reverse_pointer(self): + """Return the reverse DNS pointer name for the IPv4 address. + + This implements the method described in RFC1035 3.5. + + """ + reverse_octets = _compat_str(self).split('.')[::-1] + return '.'.join(reverse_octets) + '.in-addr.arpa' + + @property + def max_prefixlen(self): + return self._max_prefixlen + + @property + def version(self): + return self._version + + +class IPv4Address(_BaseV4, _BaseAddress): + + """Represent and manipulate single IPv4 Addresses.""" + + __slots__ = ('_ip', '__weakref__') + + def __init__(self, address): + + """ + Args: + address: A string or integer representing the IP + + Additionally, an integer can be passed, so + IPv4Address('192.0.2.1') == IPv4Address(3221225985). + or, more generally + IPv4Address(int(IPv4Address('192.0.2.1'))) == + IPv4Address('192.0.2.1') + + Raises: + AddressValueError: If ipaddress isn't a valid IPv4 address. + + """ + # Efficient constructor from integer. + if isinstance(address, _compat_int_types): + self._check_int_address(address) + self._ip = address + return + + # Constructing from a packed address + if isinstance(address, bytes): + self._check_packed_address(address, 4) + bvs = _compat_bytes_to_byte_vals(address) + self._ip = _compat_int_from_byte_vals(bvs, 'big') + return + + # Assume input argument to be string or any object representation + # which converts into a formatted IP string. + addr_str = _compat_str(address) + if '/' in addr_str: + raise AddressValueError("Unexpected '/' in %r" % address) + self._ip = self._ip_int_from_string(addr_str) + + @property + def packed(self): + """The binary representation of this address.""" + return v4_int_to_packed(self._ip) + + @property + def is_reserved(self): + """Test if the address is otherwise IETF reserved. + + Returns: + A boolean, True if the address is within the + reserved IPv4 Network range. + + """ + return self in self._constants._reserved_network + + @property + def is_private(self): + """Test if this address is allocated for private networks. + + Returns: + A boolean, True if the address is reserved per + iana-ipv4-special-registry. + + """ + return any(self in net for net in self._constants._private_networks) + + @property + def is_global(self): + return ( + self not in self._constants._public_network and + not self.is_private) + + @property + def is_multicast(self): + """Test if the address is reserved for multicast use. + + Returns: + A boolean, True if the address is multicast. + See RFC 3171 for details. + + """ + return self in self._constants._multicast_network + + @property + def is_unspecified(self): + """Test if the address is unspecified. + + Returns: + A boolean, True if this is the unspecified address as defined in + RFC 5735 3. + + """ + return self == self._constants._unspecified_address + + @property + def is_loopback(self): + """Test if the address is a loopback address. + + Returns: + A boolean, True if the address is a loopback per RFC 3330. + + """ + return self in self._constants._loopback_network + + @property + def is_link_local(self): + """Test if the address is reserved for link-local. + + Returns: + A boolean, True if the address is link-local per RFC 3927. + + """ + return self in self._constants._linklocal_network + + +class IPv4Interface(IPv4Address): + + def __init__(self, address): + if isinstance(address, (bytes, _compat_int_types)): + IPv4Address.__init__(self, address) + self.network = IPv4Network(self._ip) + self._prefixlen = self._max_prefixlen + return + + if isinstance(address, tuple): + IPv4Address.__init__(self, address[0]) + if len(address) > 1: + self._prefixlen = int(address[1]) + else: + self._prefixlen = self._max_prefixlen + + self.network = IPv4Network(address, strict=False) + self.netmask = self.network.netmask + self.hostmask = self.network.hostmask + return + + addr = _split_optional_netmask(address) + IPv4Address.__init__(self, addr[0]) + + self.network = IPv4Network(address, strict=False) + self._prefixlen = self.network._prefixlen + + self.netmask = self.network.netmask + self.hostmask = self.network.hostmask + + def __str__(self): + return '%s/%d' % (self._string_from_ip_int(self._ip), + self.network.prefixlen) + + def __eq__(self, other): + address_equal = IPv4Address.__eq__(self, other) + if not address_equal or address_equal is NotImplemented: + return address_equal + try: + return self.network == other.network + except AttributeError: + # An interface with an associated network is NOT the + # same as an unassociated address. That's why the hash + # takes the extra info into account. + return False + + def __lt__(self, other): + address_less = IPv4Address.__lt__(self, other) + if address_less is NotImplemented: + return NotImplemented + try: + return (self.network < other.network or + self.network == other.network and address_less) + except AttributeError: + # We *do* allow addresses and interfaces to be sorted. The + # unassociated address is considered less than all interfaces. + return False + + def __hash__(self): + return self._ip ^ self._prefixlen ^ int(self.network.network_address) + + __reduce__ = _IPAddressBase.__reduce__ + + @property + def ip(self): + return IPv4Address(self._ip) + + @property + def with_prefixlen(self): + return '%s/%s' % (self._string_from_ip_int(self._ip), + self._prefixlen) + + @property + def with_netmask(self): + return '%s/%s' % (self._string_from_ip_int(self._ip), + self.netmask) + + @property + def with_hostmask(self): + return '%s/%s' % (self._string_from_ip_int(self._ip), + self.hostmask) + + +class IPv4Network(_BaseV4, _BaseNetwork): + + """This class represents and manipulates 32-bit IPv4 network + addresses.. + + Attributes: [examples for IPv4Network('192.0.2.0/27')] + .network_address: IPv4Address('192.0.2.0') + .hostmask: IPv4Address('0.0.0.31') + .broadcast_address: IPv4Address('192.0.2.32') + .netmask: IPv4Address('255.255.255.224') + .prefixlen: 27 + + """ + # Class to use when creating address objects + _address_class = IPv4Address + + def __init__(self, address, strict=True): + + """Instantiate a new IPv4 network object. + + Args: + address: A string or integer representing the IP [& network]. + '192.0.2.0/24' + '192.0.2.0/255.255.255.0' + '192.0.0.2/0.0.0.255' + are all functionally the same in IPv4. Similarly, + '192.0.2.1' + '192.0.2.1/255.255.255.255' + '192.0.2.1/32' + are also functionally equivalent. That is to say, failing to + provide a subnetmask will create an object with a mask of /32. + + If the mask (portion after the / in the argument) is given in + dotted quad form, it is treated as a netmask if it starts with a + non-zero field (e.g. /255.0.0.0 == /8) and as a hostmask if it + starts with a zero field (e.g. 0.255.255.255 == /8), with the + single exception of an all-zero mask which is treated as a + netmask == /0. If no mask is given, a default of /32 is used. + + Additionally, an integer can be passed, so + IPv4Network('192.0.2.1') == IPv4Network(3221225985) + or, more generally + IPv4Interface(int(IPv4Interface('192.0.2.1'))) == + IPv4Interface('192.0.2.1') + + Raises: + AddressValueError: If ipaddress isn't a valid IPv4 address. + NetmaskValueError: If the netmask isn't valid for + an IPv4 address. + ValueError: If strict is True and a network address is not + supplied. + + """ + _BaseNetwork.__init__(self, address) + + # Constructing from a packed address or integer + if isinstance(address, (_compat_int_types, bytes)): + self.network_address = IPv4Address(address) + self.netmask, self._prefixlen = self._make_netmask( + self._max_prefixlen) + # fixme: address/network test here. + return + + if isinstance(address, tuple): + if len(address) > 1: + arg = address[1] + else: + # We weren't given an address[1] + arg = self._max_prefixlen + self.network_address = IPv4Address(address[0]) + self.netmask, self._prefixlen = self._make_netmask(arg) + packed = int(self.network_address) + if packed & int(self.netmask) != packed: + if strict: + raise ValueError('%s has host bits set' % self) + else: + self.network_address = IPv4Address(packed & + int(self.netmask)) + return + + # Assume input argument to be string or any object representation + # which converts into a formatted IP prefix string. + addr = _split_optional_netmask(address) + self.network_address = IPv4Address(self._ip_int_from_string(addr[0])) + + if len(addr) == 2: + arg = addr[1] + else: + arg = self._max_prefixlen + self.netmask, self._prefixlen = self._make_netmask(arg) + + if strict: + if (IPv4Address(int(self.network_address) & int(self.netmask)) != + self.network_address): + raise ValueError('%s has host bits set' % self) + self.network_address = IPv4Address(int(self.network_address) & + int(self.netmask)) + + if self._prefixlen == (self._max_prefixlen - 1): + self.hosts = self.__iter__ + + @property + def is_global(self): + """Test if this address is allocated for public networks. + + Returns: + A boolean, True if the address is not reserved per + iana-ipv4-special-registry. + + """ + return (not (self.network_address in IPv4Network('100.64.0.0/10') and + self.broadcast_address in IPv4Network('100.64.0.0/10')) and + not self.is_private) + + +class _IPv4Constants(object): + + _linklocal_network = IPv4Network('169.254.0.0/16') + + _loopback_network = IPv4Network('127.0.0.0/8') + + _multicast_network = IPv4Network('224.0.0.0/4') + + _public_network = IPv4Network('100.64.0.0/10') + + _private_networks = [ + IPv4Network('0.0.0.0/8'), + IPv4Network('10.0.0.0/8'), + IPv4Network('127.0.0.0/8'), + IPv4Network('169.254.0.0/16'), + IPv4Network('172.16.0.0/12'), + IPv4Network('192.0.0.0/29'), + IPv4Network('192.0.0.170/31'), + IPv4Network('192.0.2.0/24'), + IPv4Network('192.168.0.0/16'), + IPv4Network('198.18.0.0/15'), + IPv4Network('198.51.100.0/24'), + IPv4Network('203.0.113.0/24'), + IPv4Network('240.0.0.0/4'), + IPv4Network('255.255.255.255/32'), + ] + + _reserved_network = IPv4Network('240.0.0.0/4') + + _unspecified_address = IPv4Address('0.0.0.0') + + +IPv4Address._constants = _IPv4Constants + + +class _BaseV6(object): + + """Base IPv6 object. + + The following methods are used by IPv6 objects in both single IP + addresses and networks. + + """ + + __slots__ = () + _version = 6 + _ALL_ONES = (2 ** IPV6LENGTH) - 1 + _HEXTET_COUNT = 8 + _HEX_DIGITS = frozenset('0123456789ABCDEFabcdef') + _max_prefixlen = IPV6LENGTH + + # There are only a bunch of valid v6 netmasks, so we cache them all + # when constructed (see _make_netmask()). + _netmask_cache = {} + + @classmethod + def _make_netmask(cls, arg): + """Make a (netmask, prefix_len) tuple from the given argument. + + Argument can be: + - an integer (the prefix length) + - a string representing the prefix length (e.g. "24") + - a string representing the prefix netmask (e.g. "255.255.255.0") + """ + if arg not in cls._netmask_cache: + if isinstance(arg, _compat_int_types): + prefixlen = arg + else: + prefixlen = cls._prefix_from_prefix_string(arg) + netmask = IPv6Address(cls._ip_int_from_prefix(prefixlen)) + cls._netmask_cache[arg] = netmask, prefixlen + return cls._netmask_cache[arg] + + @classmethod + def _ip_int_from_string(cls, ip_str): + """Turn an IPv6 ip_str into an integer. + + Args: + ip_str: A string, the IPv6 ip_str. + + Returns: + An int, the IPv6 address + + Raises: + AddressValueError: if ip_str isn't a valid IPv6 Address. + + """ + if not ip_str: + raise AddressValueError('Address cannot be empty') + + parts = ip_str.split(':') + + # An IPv6 address needs at least 2 colons (3 parts). + _min_parts = 3 + if len(parts) < _min_parts: + msg = "At least %d parts expected in %r" % (_min_parts, ip_str) + raise AddressValueError(msg) + + # If the address has an IPv4-style suffix, convert it to hexadecimal. + if '.' in parts[-1]: + try: + ipv4_int = IPv4Address(parts.pop())._ip + except AddressValueError as exc: + raise AddressValueError("%s in %r" % (exc, ip_str)) + parts.append('%x' % ((ipv4_int >> 16) & 0xFFFF)) + parts.append('%x' % (ipv4_int & 0xFFFF)) + + # An IPv6 address can't have more than 8 colons (9 parts). + # The extra colon comes from using the "::" notation for a single + # leading or trailing zero part. + _max_parts = cls._HEXTET_COUNT + 1 + if len(parts) > _max_parts: + msg = "At most %d colons permitted in %r" % ( + _max_parts - 1, ip_str) + raise AddressValueError(msg) + + # Disregarding the endpoints, find '::' with nothing in between. + # This indicates that a run of zeroes has been skipped. + skip_index = None + for i in _compat_range(1, len(parts) - 1): + if not parts[i]: + if skip_index is not None: + # Can't have more than one '::' + msg = "At most one '::' permitted in %r" % ip_str + raise AddressValueError(msg) + skip_index = i + + # parts_hi is the number of parts to copy from above/before the '::' + # parts_lo is the number of parts to copy from below/after the '::' + if skip_index is not None: + # If we found a '::', then check if it also covers the endpoints. + parts_hi = skip_index + parts_lo = len(parts) - skip_index - 1 + if not parts[0]: + parts_hi -= 1 + if parts_hi: + msg = "Leading ':' only permitted as part of '::' in %r" + raise AddressValueError(msg % ip_str) # ^: requires ^:: + if not parts[-1]: + parts_lo -= 1 + if parts_lo: + msg = "Trailing ':' only permitted as part of '::' in %r" + raise AddressValueError(msg % ip_str) # :$ requires ::$ + parts_skipped = cls._HEXTET_COUNT - (parts_hi + parts_lo) + if parts_skipped < 1: + msg = "Expected at most %d other parts with '::' in %r" + raise AddressValueError(msg % (cls._HEXTET_COUNT - 1, ip_str)) + else: + # Otherwise, allocate the entire address to parts_hi. The + # endpoints could still be empty, but _parse_hextet() will check + # for that. + if len(parts) != cls._HEXTET_COUNT: + msg = "Exactly %d parts expected without '::' in %r" + raise AddressValueError(msg % (cls._HEXTET_COUNT, ip_str)) + if not parts[0]: + msg = "Leading ':' only permitted as part of '::' in %r" + raise AddressValueError(msg % ip_str) # ^: requires ^:: + if not parts[-1]: + msg = "Trailing ':' only permitted as part of '::' in %r" + raise AddressValueError(msg % ip_str) # :$ requires ::$ + parts_hi = len(parts) + parts_lo = 0 + parts_skipped = 0 + + try: + # Now, parse the hextets into a 128-bit integer. + ip_int = 0 + for i in range(parts_hi): + ip_int <<= 16 + ip_int |= cls._parse_hextet(parts[i]) + ip_int <<= 16 * parts_skipped + for i in range(-parts_lo, 0): + ip_int <<= 16 + ip_int |= cls._parse_hextet(parts[i]) + return ip_int + except ValueError as exc: + raise AddressValueError("%s in %r" % (exc, ip_str)) + + @classmethod + def _parse_hextet(cls, hextet_str): + """Convert an IPv6 hextet string into an integer. + + Args: + hextet_str: A string, the number to parse. + + Returns: + The hextet as an integer. + + Raises: + ValueError: if the input isn't strictly a hex number from + [0..FFFF]. + + """ + # Whitelist the characters, since int() allows a lot of bizarre stuff. + if not cls._HEX_DIGITS.issuperset(hextet_str): + raise ValueError("Only hex digits permitted in %r" % hextet_str) + # We do the length check second, since the invalid character error + # is likely to be more informative for the user + if len(hextet_str) > 4: + msg = "At most 4 characters permitted in %r" + raise ValueError(msg % hextet_str) + # Length check means we can skip checking the integer value + return int(hextet_str, 16) + + @classmethod + def _compress_hextets(cls, hextets): + """Compresses a list of hextets. + + Compresses a list of strings, replacing the longest continuous + sequence of "0" in the list with "" and adding empty strings at + the beginning or at the end of the string such that subsequently + calling ":".join(hextets) will produce the compressed version of + the IPv6 address. + + Args: + hextets: A list of strings, the hextets to compress. + + Returns: + A list of strings. + + """ + best_doublecolon_start = -1 + best_doublecolon_len = 0 + doublecolon_start = -1 + doublecolon_len = 0 + for index, hextet in enumerate(hextets): + if hextet == '0': + doublecolon_len += 1 + if doublecolon_start == -1: + # Start of a sequence of zeros. + doublecolon_start = index + if doublecolon_len > best_doublecolon_len: + # This is the longest sequence of zeros so far. + best_doublecolon_len = doublecolon_len + best_doublecolon_start = doublecolon_start + else: + doublecolon_len = 0 + doublecolon_start = -1 + + if best_doublecolon_len > 1: + best_doublecolon_end = (best_doublecolon_start + + best_doublecolon_len) + # For zeros at the end of the address. + if best_doublecolon_end == len(hextets): + hextets += [''] + hextets[best_doublecolon_start:best_doublecolon_end] = [''] + # For zeros at the beginning of the address. + if best_doublecolon_start == 0: + hextets = [''] + hextets + + return hextets + + @classmethod + def _string_from_ip_int(cls, ip_int=None): + """Turns a 128-bit integer into hexadecimal notation. + + Args: + ip_int: An integer, the IP address. + + Returns: + A string, the hexadecimal representation of the address. + + Raises: + ValueError: The address is bigger than 128 bits of all ones. + + """ + if ip_int is None: + ip_int = int(cls._ip) + + if ip_int > cls._ALL_ONES: + raise ValueError('IPv6 address is too large') + + hex_str = '%032x' % ip_int + hextets = ['%x' % int(hex_str[x:x + 4], 16) for x in range(0, 32, 4)] + + hextets = cls._compress_hextets(hextets) + return ':'.join(hextets) + + def _explode_shorthand_ip_string(self): + """Expand a shortened IPv6 address. + + Args: + ip_str: A string, the IPv6 address. + + Returns: + A string, the expanded IPv6 address. + + """ + if isinstance(self, IPv6Network): + ip_str = _compat_str(self.network_address) + elif isinstance(self, IPv6Interface): + ip_str = _compat_str(self.ip) + else: + ip_str = _compat_str(self) + + ip_int = self._ip_int_from_string(ip_str) + hex_str = '%032x' % ip_int + parts = [hex_str[x:x + 4] for x in range(0, 32, 4)] + if isinstance(self, (_BaseNetwork, IPv6Interface)): + return '%s/%d' % (':'.join(parts), self._prefixlen) + return ':'.join(parts) + + def _reverse_pointer(self): + """Return the reverse DNS pointer name for the IPv6 address. + + This implements the method described in RFC3596 2.5. + + """ + reverse_chars = self.exploded[::-1].replace(':', '') + return '.'.join(reverse_chars) + '.ip6.arpa' + + @property + def max_prefixlen(self): + return self._max_prefixlen + + @property + def version(self): + return self._version + + +class IPv6Address(_BaseV6, _BaseAddress): + + """Represent and manipulate single IPv6 Addresses.""" + + __slots__ = ('_ip', '__weakref__') + + def __init__(self, address): + """Instantiate a new IPv6 address object. + + Args: + address: A string or integer representing the IP + + Additionally, an integer can be passed, so + IPv6Address('2001:db8::') == + IPv6Address(42540766411282592856903984951653826560) + or, more generally + IPv6Address(int(IPv6Address('2001:db8::'))) == + IPv6Address('2001:db8::') + + Raises: + AddressValueError: If address isn't a valid IPv6 address. + + """ + # Efficient constructor from integer. + if isinstance(address, _compat_int_types): + self._check_int_address(address) + self._ip = address + return + + # Constructing from a packed address + if isinstance(address, bytes): + self._check_packed_address(address, 16) + bvs = _compat_bytes_to_byte_vals(address) + self._ip = _compat_int_from_byte_vals(bvs, 'big') + return + + # Assume input argument to be string or any object representation + # which converts into a formatted IP string. + addr_str = _compat_str(address) + if '/' in addr_str: + raise AddressValueError("Unexpected '/' in %r" % address) + self._ip = self._ip_int_from_string(addr_str) + + @property + def packed(self): + """The binary representation of this address.""" + return v6_int_to_packed(self._ip) + + @property + def is_multicast(self): + """Test if the address is reserved for multicast use. + + Returns: + A boolean, True if the address is a multicast address. + See RFC 2373 2.7 for details. + + """ + return self in self._constants._multicast_network + + @property + def is_reserved(self): + """Test if the address is otherwise IETF reserved. + + Returns: + A boolean, True if the address is within one of the + reserved IPv6 Network ranges. + + """ + return any(self in x for x in self._constants._reserved_networks) + + @property + def is_link_local(self): + """Test if the address is reserved for link-local. + + Returns: + A boolean, True if the address is reserved per RFC 4291. + + """ + return self in self._constants._linklocal_network + + @property + def is_site_local(self): + """Test if the address is reserved for site-local. + + Note that the site-local address space has been deprecated by RFC 3879. + Use is_private to test if this address is in the space of unique local + addresses as defined by RFC 4193. + + Returns: + A boolean, True if the address is reserved per RFC 3513 2.5.6. + + """ + return self in self._constants._sitelocal_network + + @property + def is_private(self): + """Test if this address is allocated for private networks. + + Returns: + A boolean, True if the address is reserved per + iana-ipv6-special-registry. + + """ + return any(self in net for net in self._constants._private_networks) + + @property + def is_global(self): + """Test if this address is allocated for public networks. + + Returns: + A boolean, true if the address is not reserved per + iana-ipv6-special-registry. + + """ + return not self.is_private + + @property + def is_unspecified(self): + """Test if the address is unspecified. + + Returns: + A boolean, True if this is the unspecified address as defined in + RFC 2373 2.5.2. + + """ + return self._ip == 0 + + @property + def is_loopback(self): + """Test if the address is a loopback address. + + Returns: + A boolean, True if the address is a loopback address as defined in + RFC 2373 2.5.3. + + """ + return self._ip == 1 + + @property + def ipv4_mapped(self): + """Return the IPv4 mapped address. + + Returns: + If the IPv6 address is a v4 mapped address, return the + IPv4 mapped address. Return None otherwise. + + """ + if (self._ip >> 32) != 0xFFFF: + return None + return IPv4Address(self._ip & 0xFFFFFFFF) + + @property + def teredo(self): + """Tuple of embedded teredo IPs. + + Returns: + Tuple of the (server, client) IPs or None if the address + doesn't appear to be a teredo address (doesn't start with + 2001::/32) + + """ + if (self._ip >> 96) != 0x20010000: + return None + return (IPv4Address((self._ip >> 64) & 0xFFFFFFFF), + IPv4Address(~self._ip & 0xFFFFFFFF)) + + @property + def sixtofour(self): + """Return the IPv4 6to4 embedded address. + + Returns: + The IPv4 6to4-embedded address if present or None if the + address doesn't appear to contain a 6to4 embedded address. + + """ + if (self._ip >> 112) != 0x2002: + return None + return IPv4Address((self._ip >> 80) & 0xFFFFFFFF) + + +class IPv6Interface(IPv6Address): + + def __init__(self, address): + if isinstance(address, (bytes, _compat_int_types)): + IPv6Address.__init__(self, address) + self.network = IPv6Network(self._ip) + self._prefixlen = self._max_prefixlen + return + if isinstance(address, tuple): + IPv6Address.__init__(self, address[0]) + if len(address) > 1: + self._prefixlen = int(address[1]) + else: + self._prefixlen = self._max_prefixlen + self.network = IPv6Network(address, strict=False) + self.netmask = self.network.netmask + self.hostmask = self.network.hostmask + return + + addr = _split_optional_netmask(address) + IPv6Address.__init__(self, addr[0]) + self.network = IPv6Network(address, strict=False) + self.netmask = self.network.netmask + self._prefixlen = self.network._prefixlen + self.hostmask = self.network.hostmask + + def __str__(self): + return '%s/%d' % (self._string_from_ip_int(self._ip), + self.network.prefixlen) + + def __eq__(self, other): + address_equal = IPv6Address.__eq__(self, other) + if not address_equal or address_equal is NotImplemented: + return address_equal + try: + return self.network == other.network + except AttributeError: + # An interface with an associated network is NOT the + # same as an unassociated address. That's why the hash + # takes the extra info into account. + return False + + def __lt__(self, other): + address_less = IPv6Address.__lt__(self, other) + if address_less is NotImplemented: + return NotImplemented + try: + return (self.network < other.network or + self.network == other.network and address_less) + except AttributeError: + # We *do* allow addresses and interfaces to be sorted. The + # unassociated address is considered less than all interfaces. + return False + + def __hash__(self): + return self._ip ^ self._prefixlen ^ int(self.network.network_address) + + __reduce__ = _IPAddressBase.__reduce__ + + @property + def ip(self): + return IPv6Address(self._ip) + + @property + def with_prefixlen(self): + return '%s/%s' % (self._string_from_ip_int(self._ip), + self._prefixlen) + + @property + def with_netmask(self): + return '%s/%s' % (self._string_from_ip_int(self._ip), + self.netmask) + + @property + def with_hostmask(self): + return '%s/%s' % (self._string_from_ip_int(self._ip), + self.hostmask) + + @property + def is_unspecified(self): + return self._ip == 0 and self.network.is_unspecified + + @property + def is_loopback(self): + return self._ip == 1 and self.network.is_loopback + + +class IPv6Network(_BaseV6, _BaseNetwork): + + """This class represents and manipulates 128-bit IPv6 networks. + + Attributes: [examples for IPv6('2001:db8::1000/124')] + .network_address: IPv6Address('2001:db8::1000') + .hostmask: IPv6Address('::f') + .broadcast_address: IPv6Address('2001:db8::100f') + .netmask: IPv6Address('ffff:ffff:ffff:ffff:ffff:ffff:ffff:fff0') + .prefixlen: 124 + + """ + + # Class to use when creating address objects + _address_class = IPv6Address + + def __init__(self, address, strict=True): + """Instantiate a new IPv6 Network object. + + Args: + address: A string or integer representing the IPv6 network or the + IP and prefix/netmask. + '2001:db8::/128' + '2001:db8:0000:0000:0000:0000:0000:0000/128' + '2001:db8::' + are all functionally the same in IPv6. That is to say, + failing to provide a subnetmask will create an object with + a mask of /128. + + Additionally, an integer can be passed, so + IPv6Network('2001:db8::') == + IPv6Network(42540766411282592856903984951653826560) + or, more generally + IPv6Network(int(IPv6Network('2001:db8::'))) == + IPv6Network('2001:db8::') + + strict: A boolean. If true, ensure that we have been passed + A true network address, eg, 2001:db8::1000/124 and not an + IP address on a network, eg, 2001:db8::1/124. + + Raises: + AddressValueError: If address isn't a valid IPv6 address. + NetmaskValueError: If the netmask isn't valid for + an IPv6 address. + ValueError: If strict was True and a network address was not + supplied. + + """ + _BaseNetwork.__init__(self, address) + + # Efficient constructor from integer or packed address + if isinstance(address, (bytes, _compat_int_types)): + self.network_address = IPv6Address(address) + self.netmask, self._prefixlen = self._make_netmask( + self._max_prefixlen) + return + + if isinstance(address, tuple): + if len(address) > 1: + arg = address[1] + else: + arg = self._max_prefixlen + self.netmask, self._prefixlen = self._make_netmask(arg) + self.network_address = IPv6Address(address[0]) + packed = int(self.network_address) + if packed & int(self.netmask) != packed: + if strict: + raise ValueError('%s has host bits set' % self) + else: + self.network_address = IPv6Address(packed & + int(self.netmask)) + return + + # Assume input argument to be string or any object representation + # which converts into a formatted IP prefix string. + addr = _split_optional_netmask(address) + + self.network_address = IPv6Address(self._ip_int_from_string(addr[0])) + + if len(addr) == 2: + arg = addr[1] + else: + arg = self._max_prefixlen + self.netmask, self._prefixlen = self._make_netmask(arg) + + if strict: + if (IPv6Address(int(self.network_address) & int(self.netmask)) != + self.network_address): + raise ValueError('%s has host bits set' % self) + self.network_address = IPv6Address(int(self.network_address) & + int(self.netmask)) + + if self._prefixlen == (self._max_prefixlen - 1): + self.hosts = self.__iter__ + + def hosts(self): + """Generate Iterator over usable hosts in a network. + + This is like __iter__ except it doesn't return the + Subnet-Router anycast address. + + """ + network = int(self.network_address) + broadcast = int(self.broadcast_address) + for x in _compat_range(network + 1, broadcast + 1): + yield self._address_class(x) + + @property + def is_site_local(self): + """Test if the address is reserved for site-local. + + Note that the site-local address space has been deprecated by RFC 3879. + Use is_private to test if this address is in the space of unique local + addresses as defined by RFC 4193. + + Returns: + A boolean, True if the address is reserved per RFC 3513 2.5.6. + + """ + return (self.network_address.is_site_local and + self.broadcast_address.is_site_local) + + +class _IPv6Constants(object): + + _linklocal_network = IPv6Network('fe80::/10') + + _multicast_network = IPv6Network('ff00::/8') + + _private_networks = [ + IPv6Network('::1/128'), + IPv6Network('::/128'), + IPv6Network('::ffff:0:0/96'), + IPv6Network('100::/64'), + IPv6Network('2001::/23'), + IPv6Network('2001:2::/48'), + IPv6Network('2001:db8::/32'), + IPv6Network('2001:10::/28'), + IPv6Network('fc00::/7'), + IPv6Network('fe80::/10'), + ] + + _reserved_networks = [ + IPv6Network('::/8'), IPv6Network('100::/8'), + IPv6Network('200::/7'), IPv6Network('400::/6'), + IPv6Network('800::/5'), IPv6Network('1000::/4'), + IPv6Network('4000::/3'), IPv6Network('6000::/3'), + IPv6Network('8000::/3'), IPv6Network('A000::/3'), + IPv6Network('C000::/3'), IPv6Network('E000::/4'), + IPv6Network('F000::/5'), IPv6Network('F800::/6'), + IPv6Network('FE00::/9'), + ] + + _sitelocal_network = IPv6Network('fec0::/10') + + +IPv6Address._constants = _IPv6Constants diff --git a/venv2/lib/python2.7/site-packages/pip/_vendor/lockfile/__init__.py b/venv2/lib/python2.7/site-packages/pip/_vendor/lockfile/__init__.py new file mode 100644 index 0000000..a6f44a5 --- /dev/null +++ b/venv2/lib/python2.7/site-packages/pip/_vendor/lockfile/__init__.py @@ -0,0 +1,347 @@ +# -*- coding: utf-8 -*- + +""" +lockfile.py - Platform-independent advisory file locks. + +Requires Python 2.5 unless you apply 2.4.diff +Locking is done on a per-thread basis instead of a per-process basis. + +Usage: + +>>> lock = LockFile('somefile') +>>> try: +... lock.acquire() +... except AlreadyLocked: +... print 'somefile', 'is locked already.' +... except LockFailed: +... print 'somefile', 'can\\'t be locked.' +... else: +... print 'got lock' +got lock +>>> print lock.is_locked() +True +>>> lock.release() + +>>> lock = LockFile('somefile') +>>> print lock.is_locked() +False +>>> with lock: +... print lock.is_locked() +True +>>> print lock.is_locked() +False + +>>> lock = LockFile('somefile') +>>> # It is okay to lock twice from the same thread... +>>> with lock: +... lock.acquire() +... +>>> # Though no counter is kept, so you can't unlock multiple times... +>>> print lock.is_locked() +False + +Exceptions: + + Error - base class for other exceptions + LockError - base class for all locking exceptions + AlreadyLocked - Another thread or process already holds the lock + LockFailed - Lock failed for some other reason + UnlockError - base class for all unlocking exceptions + AlreadyUnlocked - File was not locked. + NotMyLock - File was locked but not by the current thread/process +""" + +from __future__ import absolute_import + +import functools +import os +import socket +import threading +import warnings + +# Work with PEP8 and non-PEP8 versions of threading module. +if not hasattr(threading, "current_thread"): + threading.current_thread = threading.currentThread +if not hasattr(threading.Thread, "get_name"): + threading.Thread.get_name = threading.Thread.getName + +__all__ = ['Error', 'LockError', 'LockTimeout', 'AlreadyLocked', + 'LockFailed', 'UnlockError', 'NotLocked', 'NotMyLock', + 'LinkFileLock', 'MkdirFileLock', 'SQLiteFileLock', + 'LockBase', 'locked'] + + +class Error(Exception): + """ + Base class for other exceptions. + + >>> try: + ... raise Error + ... except Exception: + ... pass + """ + pass + + +class LockError(Error): + """ + Base class for error arising from attempts to acquire the lock. + + >>> try: + ... raise LockError + ... except Error: + ... pass + """ + pass + + +class LockTimeout(LockError): + """Raised when lock creation fails within a user-defined period of time. + + >>> try: + ... raise LockTimeout + ... except LockError: + ... pass + """ + pass + + +class AlreadyLocked(LockError): + """Some other thread/process is locking the file. + + >>> try: + ... raise AlreadyLocked + ... except LockError: + ... pass + """ + pass + + +class LockFailed(LockError): + """Lock file creation failed for some other reason. + + >>> try: + ... raise LockFailed + ... except LockError: + ... pass + """ + pass + + +class UnlockError(Error): + """ + Base class for errors arising from attempts to release the lock. + + >>> try: + ... raise UnlockError + ... except Error: + ... pass + """ + pass + + +class NotLocked(UnlockError): + """Raised when an attempt is made to unlock an unlocked file. + + >>> try: + ... raise NotLocked + ... except UnlockError: + ... pass + """ + pass + + +class NotMyLock(UnlockError): + """Raised when an attempt is made to unlock a file someone else locked. + + >>> try: + ... raise NotMyLock + ... except UnlockError: + ... pass + """ + pass + + +class _SharedBase(object): + def __init__(self, path): + self.path = path + + def acquire(self, timeout=None): + """ + Acquire the lock. + + * If timeout is omitted (or None), wait forever trying to lock the + file. + + * If timeout > 0, try to acquire the lock for that many seconds. If + the lock period expires and the file is still locked, raise + LockTimeout. + + * If timeout <= 0, raise AlreadyLocked immediately if the file is + already locked. + """ + raise NotImplemented("implement in subclass") + + def release(self): + """ + Release the lock. + + If the file is not locked, raise NotLocked. + """ + raise NotImplemented("implement in subclass") + + def __enter__(self): + """ + Context manager support. + """ + self.acquire() + return self + + def __exit__(self, *_exc): + """ + Context manager support. + """ + self.release() + + def __repr__(self): + return "<%s: %r>" % (self.__class__.__name__, self.path) + + +class LockBase(_SharedBase): + """Base class for platform-specific lock classes.""" + def __init__(self, path, threaded=True, timeout=None): + """ + >>> lock = LockBase('somefile') + >>> lock = LockBase('somefile', threaded=False) + """ + super(LockBase, self).__init__(path) + self.lock_file = os.path.abspath(path) + ".lock" + self.hostname = socket.gethostname() + self.pid = os.getpid() + if threaded: + t = threading.current_thread() + # Thread objects in Python 2.4 and earlier do not have ident + # attrs. Worm around that. + ident = getattr(t, "ident", hash(t)) + self.tname = "-%x" % (ident & 0xffffffff) + else: + self.tname = "" + dirname = os.path.dirname(self.lock_file) + + # unique name is mostly about the current process, but must + # also contain the path -- otherwise, two adjacent locked + # files conflict (one file gets locked, creating lock-file and + # unique file, the other one gets locked, creating lock-file + # and overwriting the already existing lock-file, then one + # gets unlocked, deleting both lock-file and unique file, + # finally the last lock errors out upon releasing. + self.unique_name = os.path.join(dirname, + "%s%s.%s%s" % (self.hostname, + self.tname, + self.pid, + hash(self.path))) + self.timeout = timeout + + def is_locked(self): + """ + Tell whether or not the file is locked. + """ + raise NotImplemented("implement in subclass") + + def i_am_locking(self): + """ + Return True if this object is locking the file. + """ + raise NotImplemented("implement in subclass") + + def break_lock(self): + """ + Remove a lock. Useful if a locking thread failed to unlock. + """ + raise NotImplemented("implement in subclass") + + def __repr__(self): + return "<%s: %r -- %r>" % (self.__class__.__name__, self.unique_name, + self.path) + + +def _fl_helper(cls, mod, *args, **kwds): + warnings.warn("Import from %s module instead of lockfile package" % mod, + DeprecationWarning, stacklevel=2) + # This is a bit funky, but it's only for awhile. The way the unit tests + # are constructed this function winds up as an unbound method, so it + # actually takes three args, not two. We want to toss out self. + if not isinstance(args[0], str): + # We are testing, avoid the first arg + args = args[1:] + if len(args) == 1 and not kwds: + kwds["threaded"] = True + return cls(*args, **kwds) + + +def LinkFileLock(*args, **kwds): + """Factory function provided for backwards compatibility. + + Do not use in new code. Instead, import LinkLockFile from the + lockfile.linklockfile module. + """ + from . import linklockfile + return _fl_helper(linklockfile.LinkLockFile, "lockfile.linklockfile", + *args, **kwds) + + +def MkdirFileLock(*args, **kwds): + """Factory function provided for backwards compatibility. + + Do not use in new code. Instead, import MkdirLockFile from the + lockfile.mkdirlockfile module. + """ + from . import mkdirlockfile + return _fl_helper(mkdirlockfile.MkdirLockFile, "lockfile.mkdirlockfile", + *args, **kwds) + + +def SQLiteFileLock(*args, **kwds): + """Factory function provided for backwards compatibility. + + Do not use in new code. Instead, import SQLiteLockFile from the + lockfile.mkdirlockfile module. + """ + from . import sqlitelockfile + return _fl_helper(sqlitelockfile.SQLiteLockFile, "lockfile.sqlitelockfile", + *args, **kwds) + + +def locked(path, timeout=None): + """Decorator which enables locks for decorated function. + + Arguments: + - path: path for lockfile. + - timeout (optional): Timeout for acquiring lock. + + Usage: + @locked('/var/run/myname', timeout=0) + def myname(...): + ... + """ + def decor(func): + @functools.wraps(func) + def wrapper(*args, **kwargs): + lock = FileLock(path, timeout=timeout) + lock.acquire() + try: + return func(*args, **kwargs) + finally: + lock.release() + return wrapper + return decor + + +if hasattr(os, "link"): + from . import linklockfile as _llf + LockFile = _llf.LinkLockFile +else: + from . import mkdirlockfile as _mlf + LockFile = _mlf.MkdirLockFile + +FileLock = LockFile diff --git a/venv2/lib/python2.7/site-packages/pip/_vendor/lockfile/linklockfile.py b/venv2/lib/python2.7/site-packages/pip/_vendor/lockfile/linklockfile.py new file mode 100644 index 0000000..2ca9be0 --- /dev/null +++ b/venv2/lib/python2.7/site-packages/pip/_vendor/lockfile/linklockfile.py @@ -0,0 +1,73 @@ +from __future__ import absolute_import + +import time +import os + +from . import (LockBase, LockFailed, NotLocked, NotMyLock, LockTimeout, + AlreadyLocked) + + +class LinkLockFile(LockBase): + """Lock access to a file using atomic property of link(2). + + >>> lock = LinkLockFile('somefile') + >>> lock = LinkLockFile('somefile', threaded=False) + """ + + def acquire(self, timeout=None): + try: + open(self.unique_name, "wb").close() + except IOError: + raise LockFailed("failed to create %s" % self.unique_name) + + timeout = timeout if timeout is not None else self.timeout + end_time = time.time() + if timeout is not None and timeout > 0: + end_time += timeout + + while True: + # Try and create a hard link to it. + try: + os.link(self.unique_name, self.lock_file) + except OSError: + # Link creation failed. Maybe we've double-locked? + nlinks = os.stat(self.unique_name).st_nlink + if nlinks == 2: + # The original link plus the one I created == 2. We're + # good to go. + return + else: + # Otherwise the lock creation failed. + if timeout is not None and time.time() > end_time: + os.unlink(self.unique_name) + if timeout > 0: + raise LockTimeout("Timeout waiting to acquire" + " lock for %s" % + self.path) + else: + raise AlreadyLocked("%s is already locked" % + self.path) + time.sleep(timeout is not None and timeout / 10 or 0.1) + else: + # Link creation succeeded. We're good to go. + return + + def release(self): + if not self.is_locked(): + raise NotLocked("%s is not locked" % self.path) + elif not os.path.exists(self.unique_name): + raise NotMyLock("%s is locked, but not by me" % self.path) + os.unlink(self.unique_name) + os.unlink(self.lock_file) + + def is_locked(self): + return os.path.exists(self.lock_file) + + def i_am_locking(self): + return (self.is_locked() and + os.path.exists(self.unique_name) and + os.stat(self.unique_name).st_nlink == 2) + + def break_lock(self): + if os.path.exists(self.lock_file): + os.unlink(self.lock_file) diff --git a/venv2/lib/python2.7/site-packages/pip/_vendor/lockfile/mkdirlockfile.py b/venv2/lib/python2.7/site-packages/pip/_vendor/lockfile/mkdirlockfile.py new file mode 100644 index 0000000..05a8c96 --- /dev/null +++ b/venv2/lib/python2.7/site-packages/pip/_vendor/lockfile/mkdirlockfile.py @@ -0,0 +1,84 @@ +from __future__ import absolute_import, division + +import time +import os +import sys +import errno + +from . import (LockBase, LockFailed, NotLocked, NotMyLock, LockTimeout, + AlreadyLocked) + + +class MkdirLockFile(LockBase): + """Lock file by creating a directory.""" + def __init__(self, path, threaded=True, timeout=None): + """ + >>> lock = MkdirLockFile('somefile') + >>> lock = MkdirLockFile('somefile', threaded=False) + """ + LockBase.__init__(self, path, threaded, timeout) + # Lock file itself is a directory. Place the unique file name into + # it. + self.unique_name = os.path.join(self.lock_file, + "%s.%s%s" % (self.hostname, + self.tname, + self.pid)) + + def acquire(self, timeout=None): + timeout = timeout if timeout is not None else self.timeout + end_time = time.time() + if timeout is not None and timeout > 0: + end_time += timeout + + if timeout is None: + wait = 0.1 + else: + wait = max(0, timeout / 10) + + while True: + try: + os.mkdir(self.lock_file) + except OSError: + err = sys.exc_info()[1] + if err.errno == errno.EEXIST: + # Already locked. + if os.path.exists(self.unique_name): + # Already locked by me. + return + if timeout is not None and time.time() > end_time: + if timeout > 0: + raise LockTimeout("Timeout waiting to acquire" + " lock for %s" % + self.path) + else: + # Someone else has the lock. + raise AlreadyLocked("%s is already locked" % + self.path) + time.sleep(wait) + else: + # Couldn't create the lock for some other reason + raise LockFailed("failed to create %s" % self.lock_file) + else: + open(self.unique_name, "wb").close() + return + + def release(self): + if not self.is_locked(): + raise NotLocked("%s is not locked" % self.path) + elif not os.path.exists(self.unique_name): + raise NotMyLock("%s is locked, but not by me" % self.path) + os.unlink(self.unique_name) + os.rmdir(self.lock_file) + + def is_locked(self): + return os.path.exists(self.lock_file) + + def i_am_locking(self): + return (self.is_locked() and + os.path.exists(self.unique_name)) + + def break_lock(self): + if os.path.exists(self.lock_file): + for name in os.listdir(self.lock_file): + os.unlink(os.path.join(self.lock_file, name)) + os.rmdir(self.lock_file) diff --git a/venv2/lib/python2.7/site-packages/pip/_vendor/lockfile/pidlockfile.py b/venv2/lib/python2.7/site-packages/pip/_vendor/lockfile/pidlockfile.py new file mode 100644 index 0000000..069e85b --- /dev/null +++ b/venv2/lib/python2.7/site-packages/pip/_vendor/lockfile/pidlockfile.py @@ -0,0 +1,190 @@ +# -*- coding: utf-8 -*- + +# pidlockfile.py +# +# Copyright © 2008–2009 Ben Finney <ben+python@benfinney.id.au> +# +# This is free software: you may copy, modify, and/or distribute this work +# under the terms of the Python Software Foundation License, version 2 or +# later as published by the Python Software Foundation. +# No warranty expressed or implied. See the file LICENSE.PSF-2 for details. + +""" Lockfile behaviour implemented via Unix PID files. + """ + +from __future__ import absolute_import + +import errno +import os +import time + +from . import (LockBase, AlreadyLocked, LockFailed, NotLocked, NotMyLock, + LockTimeout) + + +class PIDLockFile(LockBase): + """ Lockfile implemented as a Unix PID file. + + The lock file is a normal file named by the attribute `path`. + A lock's PID file contains a single line of text, containing + the process ID (PID) of the process that acquired the lock. + + >>> lock = PIDLockFile('somefile') + >>> lock = PIDLockFile('somefile') + """ + + def __init__(self, path, threaded=False, timeout=None): + # pid lockfiles don't support threaded operation, so always force + # False as the threaded arg. + LockBase.__init__(self, path, False, timeout) + self.unique_name = self.path + + def read_pid(self): + """ Get the PID from the lock file. + """ + return read_pid_from_pidfile(self.path) + + def is_locked(self): + """ Test if the lock is currently held. + + The lock is held if the PID file for this lock exists. + + """ + return os.path.exists(self.path) + + def i_am_locking(self): + """ Test if the lock is held by the current process. + + Returns ``True`` if the current process ID matches the + number stored in the PID file. + """ + return self.is_locked() and os.getpid() == self.read_pid() + + def acquire(self, timeout=None): + """ Acquire the lock. + + Creates the PID file for this lock, or raises an error if + the lock could not be acquired. + """ + + timeout = timeout if timeout is not None else self.timeout + end_time = time.time() + if timeout is not None and timeout > 0: + end_time += timeout + + while True: + try: + write_pid_to_pidfile(self.path) + except OSError as exc: + if exc.errno == errno.EEXIST: + # The lock creation failed. Maybe sleep a bit. + if time.time() > end_time: + if timeout is not None and timeout > 0: + raise LockTimeout("Timeout waiting to acquire" + " lock for %s" % + self.path) + else: + raise AlreadyLocked("%s is already locked" % + self.path) + time.sleep(timeout is not None and timeout / 10 or 0.1) + else: + raise LockFailed("failed to create %s" % self.path) + else: + return + + def release(self): + """ Release the lock. + + Removes the PID file to release the lock, or raises an + error if the current process does not hold the lock. + + """ + if not self.is_locked(): + raise NotLocked("%s is not locked" % self.path) + if not self.i_am_locking(): + raise NotMyLock("%s is locked, but not by me" % self.path) + remove_existing_pidfile(self.path) + + def break_lock(self): + """ Break an existing lock. + + Removes the PID file if it already exists, otherwise does + nothing. + + """ + remove_existing_pidfile(self.path) + + +def read_pid_from_pidfile(pidfile_path): + """ Read the PID recorded in the named PID file. + + Read and return the numeric PID recorded as text in the named + PID file. If the PID file cannot be read, or if the content is + not a valid PID, return ``None``. + + """ + pid = None + try: + pidfile = open(pidfile_path, 'r') + except IOError: + pass + else: + # According to the FHS 2.3 section on PID files in /var/run: + # + # The file must consist of the process identifier in + # ASCII-encoded decimal, followed by a newline character. + # + # Programs that read PID files should be somewhat flexible + # in what they accept; i.e., they should ignore extra + # whitespace, leading zeroes, absence of the trailing + # newline, or additional lines in the PID file. + + line = pidfile.readline().strip() + try: + pid = int(line) + except ValueError: + pass + pidfile.close() + + return pid + + +def write_pid_to_pidfile(pidfile_path): + """ Write the PID in the named PID file. + + Get the numeric process ID (“PID”) of the current process + and write it to the named file as a line of text. + + """ + open_flags = (os.O_CREAT | os.O_EXCL | os.O_WRONLY) + open_mode = 0o644 + pidfile_fd = os.open(pidfile_path, open_flags, open_mode) + pidfile = os.fdopen(pidfile_fd, 'w') + + # According to the FHS 2.3 section on PID files in /var/run: + # + # The file must consist of the process identifier in + # ASCII-encoded decimal, followed by a newline character. For + # example, if crond was process number 25, /var/run/crond.pid + # would contain three characters: two, five, and newline. + + pid = os.getpid() + pidfile.write("%s\n" % pid) + pidfile.close() + + +def remove_existing_pidfile(pidfile_path): + """ Remove the named PID file if it exists. + + Removing a PID file that doesn't already exist puts us in the + desired state, so we ignore the condition if the file does not + exist. + + """ + try: + os.remove(pidfile_path) + except OSError as exc: + if exc.errno == errno.ENOENT: + pass + else: + raise diff --git a/venv2/lib/python2.7/site-packages/pip/_vendor/lockfile/sqlitelockfile.py b/venv2/lib/python2.7/site-packages/pip/_vendor/lockfile/sqlitelockfile.py new file mode 100644 index 0000000..f997e24 --- /dev/null +++ b/venv2/lib/python2.7/site-packages/pip/_vendor/lockfile/sqlitelockfile.py @@ -0,0 +1,156 @@ +from __future__ import absolute_import, division + +import time +import os + +try: + unicode +except NameError: + unicode = str + +from . import LockBase, NotLocked, NotMyLock, LockTimeout, AlreadyLocked + + +class SQLiteLockFile(LockBase): + "Demonstrate SQL-based locking." + + testdb = None + + def __init__(self, path, threaded=True, timeout=None): + """ + >>> lock = SQLiteLockFile('somefile') + >>> lock = SQLiteLockFile('somefile', threaded=False) + """ + LockBase.__init__(self, path, threaded, timeout) + self.lock_file = unicode(self.lock_file) + self.unique_name = unicode(self.unique_name) + + if SQLiteLockFile.testdb is None: + import tempfile + _fd, testdb = tempfile.mkstemp() + os.close(_fd) + os.unlink(testdb) + del _fd, tempfile + SQLiteLockFile.testdb = testdb + + import sqlite3 + self.connection = sqlite3.connect(SQLiteLockFile.testdb) + + c = self.connection.cursor() + try: + c.execute("create table locks" + "(" + " lock_file varchar(32)," + " unique_name varchar(32)" + ")") + except sqlite3.OperationalError: + pass + else: + self.connection.commit() + import atexit + atexit.register(os.unlink, SQLiteLockFile.testdb) + + def acquire(self, timeout=None): + timeout = timeout if timeout is not None else self.timeout + end_time = time.time() + if timeout is not None and timeout > 0: + end_time += timeout + + if timeout is None: + wait = 0.1 + elif timeout <= 0: + wait = 0 + else: + wait = timeout / 10 + + cursor = self.connection.cursor() + + while True: + if not self.is_locked(): + # Not locked. Try to lock it. + cursor.execute("insert into locks" + " (lock_file, unique_name)" + " values" + " (?, ?)", + (self.lock_file, self.unique_name)) + self.connection.commit() + + # Check to see if we are the only lock holder. + cursor.execute("select * from locks" + " where unique_name = ?", + (self.unique_name,)) + rows = cursor.fetchall() + if len(rows) > 1: + # Nope. Someone else got there. Remove our lock. + cursor.execute("delete from locks" + " where unique_name = ?", + (self.unique_name,)) + self.connection.commit() + else: + # Yup. We're done, so go home. + return + else: + # Check to see if we are the only lock holder. + cursor.execute("select * from locks" + " where unique_name = ?", + (self.unique_name,)) + rows = cursor.fetchall() + if len(rows) == 1: + # We're the locker, so go home. + return + + # Maybe we should wait a bit longer. + if timeout is not None and time.time() > end_time: + if timeout > 0: + # No more waiting. + raise LockTimeout("Timeout waiting to acquire" + " lock for %s" % + self.path) + else: + # Someone else has the lock and we are impatient.. + raise AlreadyLocked("%s is already locked" % self.path) + + # Well, okay. We'll give it a bit longer. + time.sleep(wait) + + def release(self): + if not self.is_locked(): + raise NotLocked("%s is not locked" % self.path) + if not self.i_am_locking(): + raise NotMyLock("%s is locked, but not by me (by %s)" % + (self.unique_name, self._who_is_locking())) + cursor = self.connection.cursor() + cursor.execute("delete from locks" + " where unique_name = ?", + (self.unique_name,)) + self.connection.commit() + + def _who_is_locking(self): + cursor = self.connection.cursor() + cursor.execute("select unique_name from locks" + " where lock_file = ?", + (self.lock_file,)) + return cursor.fetchone()[0] + + def is_locked(self): + cursor = self.connection.cursor() + cursor.execute("select * from locks" + " where lock_file = ?", + (self.lock_file,)) + rows = cursor.fetchall() + return not not rows + + def i_am_locking(self): + cursor = self.connection.cursor() + cursor.execute("select * from locks" + " where lock_file = ?" + " and unique_name = ?", + (self.lock_file, self.unique_name)) + return not not cursor.fetchall() + + def break_lock(self): + cursor = self.connection.cursor() + cursor.execute("delete from locks" + " where lock_file = ?", + (self.lock_file,)) + self.connection.commit() diff --git a/venv2/lib/python2.7/site-packages/pip/_vendor/lockfile/symlinklockfile.py b/venv2/lib/python2.7/site-packages/pip/_vendor/lockfile/symlinklockfile.py new file mode 100644 index 0000000..23b41f5 --- /dev/null +++ b/venv2/lib/python2.7/site-packages/pip/_vendor/lockfile/symlinklockfile.py @@ -0,0 +1,70 @@ +from __future__ import absolute_import + +import os +import time + +from . import (LockBase, NotLocked, NotMyLock, LockTimeout, + AlreadyLocked) + + +class SymlinkLockFile(LockBase): + """Lock access to a file using symlink(2).""" + + def __init__(self, path, threaded=True, timeout=None): + # super(SymlinkLockFile).__init(...) + LockBase.__init__(self, path, threaded, timeout) + # split it back! + self.unique_name = os.path.split(self.unique_name)[1] + + def acquire(self, timeout=None): + # Hopefully unnecessary for symlink. + # try: + # open(self.unique_name, "wb").close() + # except IOError: + # raise LockFailed("failed to create %s" % self.unique_name) + timeout = timeout if timeout is not None else self.timeout + end_time = time.time() + if timeout is not None and timeout > 0: + end_time += timeout + + while True: + # Try and create a symbolic link to it. + try: + os.symlink(self.unique_name, self.lock_file) + except OSError: + # Link creation failed. Maybe we've double-locked? + if self.i_am_locking(): + # Linked to out unique name. Proceed. + return + else: + # Otherwise the lock creation failed. + if timeout is not None and time.time() > end_time: + if timeout > 0: + raise LockTimeout("Timeout waiting to acquire" + " lock for %s" % + self.path) + else: + raise AlreadyLocked("%s is already locked" % + self.path) + time.sleep(timeout / 10 if timeout is not None else 0.1) + else: + # Link creation succeeded. We're good to go. + return + + def release(self): + if not self.is_locked(): + raise NotLocked("%s is not locked" % self.path) + elif not self.i_am_locking(): + raise NotMyLock("%s is locked, but not by me" % self.path) + os.unlink(self.lock_file) + + def is_locked(self): + return os.path.islink(self.lock_file) + + def i_am_locking(self): + return (os.path.islink(self.lock_file) + and os.readlink(self.lock_file) == self.unique_name) + + def break_lock(self): + if os.path.islink(self.lock_file): # exists && link + os.unlink(self.lock_file) diff --git a/venv2/lib/python2.7/site-packages/pip/_vendor/msgpack/__init__.py b/venv2/lib/python2.7/site-packages/pip/_vendor/msgpack/__init__.py new file mode 100644 index 0000000..2afca5a --- /dev/null +++ b/venv2/lib/python2.7/site-packages/pip/_vendor/msgpack/__init__.py @@ -0,0 +1,66 @@ +# coding: utf-8 +from pip._vendor.msgpack._version import version +from pip._vendor.msgpack.exceptions import * + +from collections import namedtuple + + +class ExtType(namedtuple('ExtType', 'code data')): + """ExtType represents ext type in msgpack.""" + def __new__(cls, code, data): + if not isinstance(code, int): + raise TypeError("code must be int") + if not isinstance(data, bytes): + raise TypeError("data must be bytes") + if not 0 <= code <= 127: + raise ValueError("code must be 0~127") + return super(ExtType, cls).__new__(cls, code, data) + + +import os +if os.environ.get('MSGPACK_PUREPYTHON'): + from pip._vendor.msgpack.fallback import Packer, unpackb, Unpacker +else: + try: + from pip._vendor.msgpack._packer import Packer + from pip._vendor.msgpack._unpacker import unpackb, Unpacker + except ImportError: + from pip._vendor.msgpack.fallback import Packer, unpackb, Unpacker + + +def pack(o, stream, **kwargs): + """ + Pack object `o` and write it to `stream` + + See :class:`Packer` for options. + """ + packer = Packer(**kwargs) + stream.write(packer.pack(o)) + + +def packb(o, **kwargs): + """ + Pack object `o` and return packed bytes + + See :class:`Packer` for options. + """ + return Packer(**kwargs).pack(o) + + +def unpack(stream, **kwargs): + """ + Unpack an object from `stream`. + + Raises `ExtraData` when `stream` contains extra bytes. + See :class:`Unpacker` for options. + """ + data = stream.read() + return unpackb(data, **kwargs) + + +# alias for compatibility to simplejson/marshal/pickle. +load = unpack +loads = unpackb + +dump = pack +dumps = packb diff --git a/venv2/lib/python2.7/site-packages/pip/_vendor/msgpack/_version.py b/venv2/lib/python2.7/site-packages/pip/_vendor/msgpack/_version.py new file mode 100644 index 0000000..d28f0de --- /dev/null +++ b/venv2/lib/python2.7/site-packages/pip/_vendor/msgpack/_version.py @@ -0,0 +1 @@ +version = (0, 5, 6) diff --git a/venv2/lib/python2.7/site-packages/pip/_vendor/msgpack/exceptions.py b/venv2/lib/python2.7/site-packages/pip/_vendor/msgpack/exceptions.py new file mode 100644 index 0000000..9766881 --- /dev/null +++ b/venv2/lib/python2.7/site-packages/pip/_vendor/msgpack/exceptions.py @@ -0,0 +1,41 @@ +class UnpackException(Exception): + """Deprecated. Use Exception instead to catch all exception during unpacking.""" + + +class BufferFull(UnpackException): + pass + + +class OutOfData(UnpackException): + pass + + +class UnpackValueError(UnpackException, ValueError): + """Deprecated. Use ValueError instead.""" + + +class ExtraData(UnpackValueError): + def __init__(self, unpacked, extra): + self.unpacked = unpacked + self.extra = extra + + def __str__(self): + return "unpack(b) received extra data." + + +class PackException(Exception): + """Deprecated. Use Exception instead to catch all exception during packing.""" + + +class PackValueError(PackException, ValueError): + """PackValueError is raised when type of input data is supported but it's value is unsupported. + + Deprecated. Use ValueError instead. + """ + + +class PackOverflowError(PackValueError, OverflowError): + """PackOverflowError is raised when integer value is out of range of msgpack support [-2**31, 2**32). + + Deprecated. Use ValueError instead. + """ diff --git a/venv2/lib/python2.7/site-packages/pip/_vendor/msgpack/fallback.py b/venv2/lib/python2.7/site-packages/pip/_vendor/msgpack/fallback.py new file mode 100644 index 0000000..9418421 --- /dev/null +++ b/venv2/lib/python2.7/site-packages/pip/_vendor/msgpack/fallback.py @@ -0,0 +1,977 @@ +"""Fallback pure Python implementation of msgpack""" + +import sys +import struct +import warnings + +if sys.version_info[0] == 3: + PY3 = True + int_types = int + Unicode = str + xrange = range + def dict_iteritems(d): + return d.items() +else: + PY3 = False + int_types = (int, long) + Unicode = unicode + def dict_iteritems(d): + return d.iteritems() + + +if hasattr(sys, 'pypy_version_info'): + # cStringIO is slow on PyPy, StringIO is faster. However: PyPy's own + # StringBuilder is fastest. + from __pypy__ import newlist_hint + try: + from __pypy__.builders import BytesBuilder as StringBuilder + except ImportError: + from __pypy__.builders import StringBuilder + USING_STRINGBUILDER = True + class StringIO(object): + def __init__(self, s=b''): + if s: + self.builder = StringBuilder(len(s)) + self.builder.append(s) + else: + self.builder = StringBuilder() + def write(self, s): + if isinstance(s, memoryview): + s = s.tobytes() + elif isinstance(s, bytearray): + s = bytes(s) + self.builder.append(s) + def getvalue(self): + return self.builder.build() +else: + USING_STRINGBUILDER = False + from io import BytesIO as StringIO + newlist_hint = lambda size: [] + + +from pip._vendor.msgpack.exceptions import ( + BufferFull, + OutOfData, + UnpackValueError, + PackValueError, + PackOverflowError, + ExtraData) + +from pip._vendor.msgpack import ExtType + + +EX_SKIP = 0 +EX_CONSTRUCT = 1 +EX_READ_ARRAY_HEADER = 2 +EX_READ_MAP_HEADER = 3 + +TYPE_IMMEDIATE = 0 +TYPE_ARRAY = 1 +TYPE_MAP = 2 +TYPE_RAW = 3 +TYPE_BIN = 4 +TYPE_EXT = 5 + +DEFAULT_RECURSE_LIMIT = 511 + + +def _check_type_strict(obj, t, type=type, tuple=tuple): + if type(t) is tuple: + return type(obj) in t + else: + return type(obj) is t + + +def _get_data_from_buffer(obj): + try: + view = memoryview(obj) + except TypeError: + # try to use legacy buffer protocol if 2.7, otherwise re-raise + if not PY3: + view = memoryview(buffer(obj)) + warnings.warn("using old buffer interface to unpack %s; " + "this leads to unpacking errors if slicing is used and " + "will be removed in a future version" % type(obj), + RuntimeWarning) + else: + raise + if view.itemsize != 1: + raise ValueError("cannot unpack from multi-byte object") + return view + + +def unpack(stream, **kwargs): + warnings.warn( + "Direct calling implementation's unpack() is deprecated, Use msgpack.unpack() or unpackb() instead.", + PendingDeprecationWarning) + data = stream.read() + return unpackb(data, **kwargs) + + +def unpackb(packed, **kwargs): + """ + Unpack an object from `packed`. + + Raises `ExtraData` when `packed` contains extra bytes. + See :class:`Unpacker` for options. + """ + unpacker = Unpacker(None, **kwargs) + unpacker.feed(packed) + try: + ret = unpacker._unpack() + except OutOfData: + raise UnpackValueError("Data is not enough.") + if unpacker._got_extradata(): + raise ExtraData(ret, unpacker._get_extradata()) + return ret + + +class Unpacker(object): + """Streaming unpacker. + + arguments: + + :param file_like: + File-like object having `.read(n)` method. + If specified, unpacker reads serialized data from it and :meth:`feed()` is not usable. + + :param int read_size: + Used as `file_like.read(read_size)`. (default: `min(16*1024, max_buffer_size)`) + + :param bool use_list: + If true, unpack msgpack array to Python list. + Otherwise, unpack to Python tuple. (default: True) + + :param bool raw: + If true, unpack msgpack raw to Python bytes (default). + Otherwise, unpack to Python str (or unicode on Python 2) by decoding + with UTF-8 encoding (recommended). + Currently, the default is true, but it will be changed to false in + near future. So you must specify it explicitly for keeping backward + compatibility. + + *encoding* option which is deprecated overrides this option. + + :param callable object_hook: + When specified, it should be callable. + Unpacker calls it with a dict argument after unpacking msgpack map. + (See also simplejson) + + :param callable object_pairs_hook: + When specified, it should be callable. + Unpacker calls it with a list of key-value pairs after unpacking msgpack map. + (See also simplejson) + + :param str encoding: + Encoding used for decoding msgpack raw. + If it is None (default), msgpack raw is deserialized to Python bytes. + + :param str unicode_errors: + (deprecated) Used for decoding msgpack raw with *encoding*. + (default: `'strict'`) + + :param int max_buffer_size: + Limits size of data waiting unpacked. 0 means system's INT_MAX (default). + Raises `BufferFull` exception when it is insufficient. + You should set this parameter when unpacking data from untrusted source. + + :param int max_str_len: + Limits max length of str. (default: 2**31-1) + + :param int max_bin_len: + Limits max length of bin. (default: 2**31-1) + + :param int max_array_len: + Limits max length of array. (default: 2**31-1) + + :param int max_map_len: + Limits max length of map. (default: 2**31-1) + + + example of streaming deserialize from file-like object:: + + unpacker = Unpacker(file_like, raw=False) + for o in unpacker: + process(o) + + example of streaming deserialize from socket:: + + unpacker = Unpacker(raw=False) + while True: + buf = sock.recv(1024**2) + if not buf: + break + unpacker.feed(buf) + for o in unpacker: + process(o) + """ + + def __init__(self, file_like=None, read_size=0, use_list=True, raw=True, + object_hook=None, object_pairs_hook=None, list_hook=None, + encoding=None, unicode_errors=None, max_buffer_size=0, + ext_hook=ExtType, + max_str_len=2147483647, # 2**32-1 + max_bin_len=2147483647, + max_array_len=2147483647, + max_map_len=2147483647, + max_ext_len=2147483647): + + if encoding is not None: + warnings.warn( + "encoding is deprecated, Use raw=False instead.", + PendingDeprecationWarning) + + if unicode_errors is None: + unicode_errors = 'strict' + + if file_like is None: + self._feeding = True + else: + if not callable(file_like.read): + raise TypeError("`file_like.read` must be callable") + self.file_like = file_like + self._feeding = False + + #: array of bytes fed. + self._buffer = bytearray() + # Some very old pythons don't support `struct.unpack_from()` with a + # `bytearray`. So we wrap it in a `buffer()` there. + if sys.version_info < (2, 7, 6): + self._buffer_view = buffer(self._buffer) + else: + self._buffer_view = self._buffer + #: Which position we currently reads + self._buff_i = 0 + + # When Unpacker is used as an iterable, between the calls to next(), + # the buffer is not "consumed" completely, for efficiency sake. + # Instead, it is done sloppily. To make sure we raise BufferFull at + # the correct moments, we have to keep track of how sloppy we were. + # Furthermore, when the buffer is incomplete (that is: in the case + # we raise an OutOfData) we need to rollback the buffer to the correct + # state, which _buf_checkpoint records. + self._buf_checkpoint = 0 + + self._max_buffer_size = max_buffer_size or 2**31-1 + if read_size > self._max_buffer_size: + raise ValueError("read_size must be smaller than max_buffer_size") + self._read_size = read_size or min(self._max_buffer_size, 16*1024) + self._raw = bool(raw) + self._encoding = encoding + self._unicode_errors = unicode_errors + self._use_list = use_list + self._list_hook = list_hook + self._object_hook = object_hook + self._object_pairs_hook = object_pairs_hook + self._ext_hook = ext_hook + self._max_str_len = max_str_len + self._max_bin_len = max_bin_len + self._max_array_len = max_array_len + self._max_map_len = max_map_len + self._max_ext_len = max_ext_len + self._stream_offset = 0 + + if list_hook is not None and not callable(list_hook): + raise TypeError('`list_hook` is not callable') + if object_hook is not None and not callable(object_hook): + raise TypeError('`object_hook` is not callable') + if object_pairs_hook is not None and not callable(object_pairs_hook): + raise TypeError('`object_pairs_hook` is not callable') + if object_hook is not None and object_pairs_hook is not None: + raise TypeError("object_pairs_hook and object_hook are mutually " + "exclusive") + if not callable(ext_hook): + raise TypeError("`ext_hook` is not callable") + + def feed(self, next_bytes): + assert self._feeding + view = _get_data_from_buffer(next_bytes) + if (len(self._buffer) - self._buff_i + len(view) > self._max_buffer_size): + raise BufferFull + + # Strip buffer before checkpoint before reading file. + if self._buf_checkpoint > 0: + del self._buffer[:self._buf_checkpoint] + self._buff_i -= self._buf_checkpoint + self._buf_checkpoint = 0 + + self._buffer += view + + def _consume(self): + """ Gets rid of the used parts of the buffer. """ + self._stream_offset += self._buff_i - self._buf_checkpoint + self._buf_checkpoint = self._buff_i + + def _got_extradata(self): + return self._buff_i < len(self._buffer) + + def _get_extradata(self): + return self._buffer[self._buff_i:] + + def read_bytes(self, n): + return self._read(n) + + def _read(self, n): + # (int) -> bytearray + self._reserve(n) + i = self._buff_i + self._buff_i = i+n + return self._buffer[i:i+n] + + def _reserve(self, n): + remain_bytes = len(self._buffer) - self._buff_i - n + + # Fast path: buffer has n bytes already + if remain_bytes >= 0: + return + + if self._feeding: + self._buff_i = self._buf_checkpoint + raise OutOfData + + # Strip buffer before checkpoint before reading file. + if self._buf_checkpoint > 0: + del self._buffer[:self._buf_checkpoint] + self._buff_i -= self._buf_checkpoint + self._buf_checkpoint = 0 + + # Read from file + remain_bytes = -remain_bytes + while remain_bytes > 0: + to_read_bytes = max(self._read_size, remain_bytes) + read_data = self.file_like.read(to_read_bytes) + if not read_data: + break + assert isinstance(read_data, bytes) + self._buffer += read_data + remain_bytes -= len(read_data) + + if len(self._buffer) < n + self._buff_i: + self._buff_i = 0 # rollback + raise OutOfData + + def _read_header(self, execute=EX_CONSTRUCT): + typ = TYPE_IMMEDIATE + n = 0 + obj = None + self._reserve(1) + b = self._buffer[self._buff_i] + self._buff_i += 1 + if b & 0b10000000 == 0: + obj = b + elif b & 0b11100000 == 0b11100000: + obj = -1 - (b ^ 0xff) + elif b & 0b11100000 == 0b10100000: + n = b & 0b00011111 + typ = TYPE_RAW + if n > self._max_str_len: + raise UnpackValueError("%s exceeds max_str_len(%s)", n, self._max_str_len) + obj = self._read(n) + elif b & 0b11110000 == 0b10010000: + n = b & 0b00001111 + typ = TYPE_ARRAY + if n > self._max_array_len: + raise UnpackValueError("%s exceeds max_array_len(%s)", n, self._max_array_len) + elif b & 0b11110000 == 0b10000000: + n = b & 0b00001111 + typ = TYPE_MAP + if n > self._max_map_len: + raise UnpackValueError("%s exceeds max_map_len(%s)", n, self._max_map_len) + elif b == 0xc0: + obj = None + elif b == 0xc2: + obj = False + elif b == 0xc3: + obj = True + elif b == 0xc4: + typ = TYPE_BIN + self._reserve(1) + n = self._buffer[self._buff_i] + self._buff_i += 1 + if n > self._max_bin_len: + raise UnpackValueError("%s exceeds max_bin_len(%s)" % (n, self._max_bin_len)) + obj = self._read(n) + elif b == 0xc5: + typ = TYPE_BIN + self._reserve(2) + n = struct.unpack_from(">H", self._buffer_view, self._buff_i)[0] + self._buff_i += 2 + if n > self._max_bin_len: + raise UnpackValueError("%s exceeds max_bin_len(%s)" % (n, self._max_bin_len)) + obj = self._read(n) + elif b == 0xc6: + typ = TYPE_BIN + self._reserve(4) + n = struct.unpack_from(">I", self._buffer_view, self._buff_i)[0] + self._buff_i += 4 + if n > self._max_bin_len: + raise UnpackValueError("%s exceeds max_bin_len(%s)" % (n, self._max_bin_len)) + obj = self._read(n) + elif b == 0xc7: # ext 8 + typ = TYPE_EXT + self._reserve(2) + L, n = struct.unpack_from('Bb', self._buffer_view, self._buff_i) + self._buff_i += 2 + if L > self._max_ext_len: + raise UnpackValueError("%s exceeds max_ext_len(%s)" % (L, self._max_ext_len)) + obj = self._read(L) + elif b == 0xc8: # ext 16 + typ = TYPE_EXT + self._reserve(3) + L, n = struct.unpack_from('>Hb', self._buffer_view, self._buff_i) + self._buff_i += 3 + if L > self._max_ext_len: + raise UnpackValueError("%s exceeds max_ext_len(%s)" % (L, self._max_ext_len)) + obj = self._read(L) + elif b == 0xc9: # ext 32 + typ = TYPE_EXT + self._reserve(5) + L, n = struct.unpack_from('>Ib', self._buffer_view, self._buff_i) + self._buff_i += 5 + if L > self._max_ext_len: + raise UnpackValueError("%s exceeds max_ext_len(%s)" % (L, self._max_ext_len)) + obj = self._read(L) + elif b == 0xca: + self._reserve(4) + obj = struct.unpack_from(">f", self._buffer_view, self._buff_i)[0] + self._buff_i += 4 + elif b == 0xcb: + self._reserve(8) + obj = struct.unpack_from(">d", self._buffer_view, self._buff_i)[0] + self._buff_i += 8 + elif b == 0xcc: + self._reserve(1) + obj = self._buffer[self._buff_i] + self._buff_i += 1 + elif b == 0xcd: + self._reserve(2) + obj = struct.unpack_from(">H", self._buffer_view, self._buff_i)[0] + self._buff_i += 2 + elif b == 0xce: + self._reserve(4) + obj = struct.unpack_from(">I", self._buffer_view, self._buff_i)[0] + self._buff_i += 4 + elif b == 0xcf: + self._reserve(8) + obj = struct.unpack_from(">Q", self._buffer_view, self._buff_i)[0] + self._buff_i += 8 + elif b == 0xd0: + self._reserve(1) + obj = struct.unpack_from("b", self._buffer_view, self._buff_i)[0] + self._buff_i += 1 + elif b == 0xd1: + self._reserve(2) + obj = struct.unpack_from(">h", self._buffer_view, self._buff_i)[0] + self._buff_i += 2 + elif b == 0xd2: + self._reserve(4) + obj = struct.unpack_from(">i", self._buffer_view, self._buff_i)[0] + self._buff_i += 4 + elif b == 0xd3: + self._reserve(8) + obj = struct.unpack_from(">q", self._buffer_view, self._buff_i)[0] + self._buff_i += 8 + elif b == 0xd4: # fixext 1 + typ = TYPE_EXT + if self._max_ext_len < 1: + raise UnpackValueError("%s exceeds max_ext_len(%s)" % (1, self._max_ext_len)) + self._reserve(2) + n, obj = struct.unpack_from("b1s", self._buffer_view, self._buff_i) + self._buff_i += 2 + elif b == 0xd5: # fixext 2 + typ = TYPE_EXT + if self._max_ext_len < 2: + raise UnpackValueError("%s exceeds max_ext_len(%s)" % (2, self._max_ext_len)) + self._reserve(3) + n, obj = struct.unpack_from("b2s", self._buffer_view, self._buff_i) + self._buff_i += 3 + elif b == 0xd6: # fixext 4 + typ = TYPE_EXT + if self._max_ext_len < 4: + raise UnpackValueError("%s exceeds max_ext_len(%s)" % (4, self._max_ext_len)) + self._reserve(5) + n, obj = struct.unpack_from("b4s", self._buffer_view, self._buff_i) + self._buff_i += 5 + elif b == 0xd7: # fixext 8 + typ = TYPE_EXT + if self._max_ext_len < 8: + raise UnpackValueError("%s exceeds max_ext_len(%s)" % (8, self._max_ext_len)) + self._reserve(9) + n, obj = struct.unpack_from("b8s", self._buffer_view, self._buff_i) + self._buff_i += 9 + elif b == 0xd8: # fixext 16 + typ = TYPE_EXT + if self._max_ext_len < 16: + raise UnpackValueError("%s exceeds max_ext_len(%s)" % (16, self._max_ext_len)) + self._reserve(17) + n, obj = struct.unpack_from("b16s", self._buffer_view, self._buff_i) + self._buff_i += 17 + elif b == 0xd9: + typ = TYPE_RAW + self._reserve(1) + n = self._buffer[self._buff_i] + self._buff_i += 1 + if n > self._max_str_len: + raise UnpackValueError("%s exceeds max_str_len(%s)", n, self._max_str_len) + obj = self._read(n) + elif b == 0xda: + typ = TYPE_RAW + self._reserve(2) + n, = struct.unpack_from(">H", self._buffer_view, self._buff_i) + self._buff_i += 2 + if n > self._max_str_len: + raise UnpackValueError("%s exceeds max_str_len(%s)", n, self._max_str_len) + obj = self._read(n) + elif b == 0xdb: + typ = TYPE_RAW + self._reserve(4) + n, = struct.unpack_from(">I", self._buffer_view, self._buff_i) + self._buff_i += 4 + if n > self._max_str_len: + raise UnpackValueError("%s exceeds max_str_len(%s)", n, self._max_str_len) + obj = self._read(n) + elif b == 0xdc: + typ = TYPE_ARRAY + self._reserve(2) + n, = struct.unpack_from(">H", self._buffer_view, self._buff_i) + self._buff_i += 2 + if n > self._max_array_len: + raise UnpackValueError("%s exceeds max_array_len(%s)", n, self._max_array_len) + elif b == 0xdd: + typ = TYPE_ARRAY + self._reserve(4) + n, = struct.unpack_from(">I", self._buffer_view, self._buff_i) + self._buff_i += 4 + if n > self._max_array_len: + raise UnpackValueError("%s exceeds max_array_len(%s)", n, self._max_array_len) + elif b == 0xde: + self._reserve(2) + n, = struct.unpack_from(">H", self._buffer_view, self._buff_i) + self._buff_i += 2 + if n > self._max_map_len: + raise UnpackValueError("%s exceeds max_map_len(%s)", n, self._max_map_len) + typ = TYPE_MAP + elif b == 0xdf: + self._reserve(4) + n, = struct.unpack_from(">I", self._buffer_view, self._buff_i) + self._buff_i += 4 + if n > self._max_map_len: + raise UnpackValueError("%s exceeds max_map_len(%s)", n, self._max_map_len) + typ = TYPE_MAP + else: + raise UnpackValueError("Unknown header: 0x%x" % b) + return typ, n, obj + + def _unpack(self, execute=EX_CONSTRUCT): + typ, n, obj = self._read_header(execute) + + if execute == EX_READ_ARRAY_HEADER: + if typ != TYPE_ARRAY: + raise UnpackValueError("Expected array") + return n + if execute == EX_READ_MAP_HEADER: + if typ != TYPE_MAP: + raise UnpackValueError("Expected map") + return n + # TODO should we eliminate the recursion? + if typ == TYPE_ARRAY: + if execute == EX_SKIP: + for i in xrange(n): + # TODO check whether we need to call `list_hook` + self._unpack(EX_SKIP) + return + ret = newlist_hint(n) + for i in xrange(n): + ret.append(self._unpack(EX_CONSTRUCT)) + if self._list_hook is not None: + ret = self._list_hook(ret) + # TODO is the interaction between `list_hook` and `use_list` ok? + return ret if self._use_list else tuple(ret) + if typ == TYPE_MAP: + if execute == EX_SKIP: + for i in xrange(n): + # TODO check whether we need to call hooks + self._unpack(EX_SKIP) + self._unpack(EX_SKIP) + return + if self._object_pairs_hook is not None: + ret = self._object_pairs_hook( + (self._unpack(EX_CONSTRUCT), + self._unpack(EX_CONSTRUCT)) + for _ in xrange(n)) + else: + ret = {} + for _ in xrange(n): + key = self._unpack(EX_CONSTRUCT) + ret[key] = self._unpack(EX_CONSTRUCT) + if self._object_hook is not None: + ret = self._object_hook(ret) + return ret + if execute == EX_SKIP: + return + if typ == TYPE_RAW: + if self._encoding is not None: + obj = obj.decode(self._encoding, self._unicode_errors) + elif self._raw: + obj = bytes(obj) + else: + obj = obj.decode('utf_8') + return obj + if typ == TYPE_EXT: + return self._ext_hook(n, bytes(obj)) + if typ == TYPE_BIN: + return bytes(obj) + assert typ == TYPE_IMMEDIATE + return obj + + def __iter__(self): + return self + + def __next__(self): + try: + ret = self._unpack(EX_CONSTRUCT) + self._consume() + return ret + except OutOfData: + self._consume() + raise StopIteration + + next = __next__ + + def skip(self, write_bytes=None): + self._unpack(EX_SKIP) + if write_bytes is not None: + warnings.warn("`write_bytes` option is deprecated. Use `.tell()` instead.", DeprecationWarning) + write_bytes(self._buffer[self._buf_checkpoint:self._buff_i]) + self._consume() + + def unpack(self, write_bytes=None): + ret = self._unpack(EX_CONSTRUCT) + if write_bytes is not None: + warnings.warn("`write_bytes` option is deprecated. Use `.tell()` instead.", DeprecationWarning) + write_bytes(self._buffer[self._buf_checkpoint:self._buff_i]) + self._consume() + return ret + + def read_array_header(self, write_bytes=None): + ret = self._unpack(EX_READ_ARRAY_HEADER) + if write_bytes is not None: + warnings.warn("`write_bytes` option is deprecated. Use `.tell()` instead.", DeprecationWarning) + write_bytes(self._buffer[self._buf_checkpoint:self._buff_i]) + self._consume() + return ret + + def read_map_header(self, write_bytes=None): + ret = self._unpack(EX_READ_MAP_HEADER) + if write_bytes is not None: + warnings.warn("`write_bytes` option is deprecated. Use `.tell()` instead.", DeprecationWarning) + write_bytes(self._buffer[self._buf_checkpoint:self._buff_i]) + self._consume() + return ret + + def tell(self): + return self._stream_offset + + +class Packer(object): + """ + MessagePack Packer + + usage: + + packer = Packer() + astream.write(packer.pack(a)) + astream.write(packer.pack(b)) + + Packer's constructor has some keyword arguments: + + :param callable default: + Convert user type to builtin type that Packer supports. + See also simplejson's document. + + :param bool use_single_float: + Use single precision float type for float. (default: False) + + :param bool autoreset: + Reset buffer after each pack and return its content as `bytes`. (default: True). + If set this to false, use `bytes()` to get content and `.reset()` to clear buffer. + + :param bool use_bin_type: + Use bin type introduced in msgpack spec 2.0 for bytes. + It also enables str8 type for unicode. + + :param bool strict_types: + If set to true, types will be checked to be exact. Derived classes + from serializeable types will not be serialized and will be + treated as unsupported type and forwarded to default. + Additionally tuples will not be serialized as lists. + This is useful when trying to implement accurate serialization + for python types. + + :param str encoding: + (deprecated) Convert unicode to bytes with this encoding. (default: 'utf-8') + + :param str unicode_errors: + Error handler for encoding unicode. (default: 'strict') + """ + def __init__(self, default=None, encoding=None, unicode_errors=None, + use_single_float=False, autoreset=True, use_bin_type=False, + strict_types=False): + if encoding is None: + encoding = 'utf_8' + else: + warnings.warn( + "encoding is deprecated, Use raw=False instead.", + PendingDeprecationWarning) + + if unicode_errors is None: + unicode_errors = 'strict' + + self._strict_types = strict_types + self._use_float = use_single_float + self._autoreset = autoreset + self._use_bin_type = use_bin_type + self._encoding = encoding + self._unicode_errors = unicode_errors + self._buffer = StringIO() + if default is not None: + if not callable(default): + raise TypeError("default must be callable") + self._default = default + + def _pack(self, obj, nest_limit=DEFAULT_RECURSE_LIMIT, + check=isinstance, check_type_strict=_check_type_strict): + default_used = False + if self._strict_types: + check = check_type_strict + list_types = list + else: + list_types = (list, tuple) + while True: + if nest_limit < 0: + raise PackValueError("recursion limit exceeded") + if obj is None: + return self._buffer.write(b"\xc0") + if check(obj, bool): + if obj: + return self._buffer.write(b"\xc3") + return self._buffer.write(b"\xc2") + if check(obj, int_types): + if 0 <= obj < 0x80: + return self._buffer.write(struct.pack("B", obj)) + if -0x20 <= obj < 0: + return self._buffer.write(struct.pack("b", obj)) + if 0x80 <= obj <= 0xff: + return self._buffer.write(struct.pack("BB", 0xcc, obj)) + if -0x80 <= obj < 0: + return self._buffer.write(struct.pack(">Bb", 0xd0, obj)) + if 0xff < obj <= 0xffff: + return self._buffer.write(struct.pack(">BH", 0xcd, obj)) + if -0x8000 <= obj < -0x80: + return self._buffer.write(struct.pack(">Bh", 0xd1, obj)) + if 0xffff < obj <= 0xffffffff: + return self._buffer.write(struct.pack(">BI", 0xce, obj)) + if -0x80000000 <= obj < -0x8000: + return self._buffer.write(struct.pack(">Bi", 0xd2, obj)) + if 0xffffffff < obj <= 0xffffffffffffffff: + return self._buffer.write(struct.pack(">BQ", 0xcf, obj)) + if -0x8000000000000000 <= obj < -0x80000000: + return self._buffer.write(struct.pack(">Bq", 0xd3, obj)) + if not default_used and self._default is not None: + obj = self._default(obj) + default_used = True + continue + raise PackOverflowError("Integer value out of range") + if check(obj, (bytes, bytearray)): + n = len(obj) + if n >= 2**32: + raise PackValueError("%s is too large" % type(obj).__name__) + self._pack_bin_header(n) + return self._buffer.write(obj) + if check(obj, Unicode): + if self._encoding is None: + raise TypeError( + "Can't encode unicode string: " + "no encoding is specified") + obj = obj.encode(self._encoding, self._unicode_errors) + n = len(obj) + if n >= 2**32: + raise PackValueError("String is too large") + self._pack_raw_header(n) + return self._buffer.write(obj) + if check(obj, memoryview): + n = len(obj) * obj.itemsize + if n >= 2**32: + raise PackValueError("Memoryview is too large") + self._pack_bin_header(n) + return self._buffer.write(obj) + if check(obj, float): + if self._use_float: + return self._buffer.write(struct.pack(">Bf", 0xca, obj)) + return self._buffer.write(struct.pack(">Bd", 0xcb, obj)) + if check(obj, ExtType): + code = obj.code + data = obj.data + assert isinstance(code, int) + assert isinstance(data, bytes) + L = len(data) + if L == 1: + self._buffer.write(b'\xd4') + elif L == 2: + self._buffer.write(b'\xd5') + elif L == 4: + self._buffer.write(b'\xd6') + elif L == 8: + self._buffer.write(b'\xd7') + elif L == 16: + self._buffer.write(b'\xd8') + elif L <= 0xff: + self._buffer.write(struct.pack(">BB", 0xc7, L)) + elif L <= 0xffff: + self._buffer.write(struct.pack(">BH", 0xc8, L)) + else: + self._buffer.write(struct.pack(">BI", 0xc9, L)) + self._buffer.write(struct.pack("b", code)) + self._buffer.write(data) + return + if check(obj, list_types): + n = len(obj) + self._pack_array_header(n) + for i in xrange(n): + self._pack(obj[i], nest_limit - 1) + return + if check(obj, dict): + return self._pack_map_pairs(len(obj), dict_iteritems(obj), + nest_limit - 1) + if not default_used and self._default is not None: + obj = self._default(obj) + default_used = 1 + continue + raise TypeError("Cannot serialize %r" % (obj, )) + + def pack(self, obj): + try: + self._pack(obj) + except: + self._buffer = StringIO() # force reset + raise + ret = self._buffer.getvalue() + if self._autoreset: + self._buffer = StringIO() + elif USING_STRINGBUILDER: + self._buffer = StringIO(ret) + return ret + + def pack_map_pairs(self, pairs): + self._pack_map_pairs(len(pairs), pairs) + ret = self._buffer.getvalue() + if self._autoreset: + self._buffer = StringIO() + elif USING_STRINGBUILDER: + self._buffer = StringIO(ret) + return ret + + def pack_array_header(self, n): + if n >= 2**32: + raise PackValueError + self._pack_array_header(n) + ret = self._buffer.getvalue() + if self._autoreset: + self._buffer = StringIO() + elif USING_STRINGBUILDER: + self._buffer = StringIO(ret) + return ret + + def pack_map_header(self, n): + if n >= 2**32: + raise PackValueError + self._pack_map_header(n) + ret = self._buffer.getvalue() + if self._autoreset: + self._buffer = StringIO() + elif USING_STRINGBUILDER: + self._buffer = StringIO(ret) + return ret + + def pack_ext_type(self, typecode, data): + if not isinstance(typecode, int): + raise TypeError("typecode must have int type.") + if not 0 <= typecode <= 127: + raise ValueError("typecode should be 0-127") + if not isinstance(data, bytes): + raise TypeError("data must have bytes type") + L = len(data) + if L > 0xffffffff: + raise PackValueError("Too large data") + if L == 1: + self._buffer.write(b'\xd4') + elif L == 2: + self._buffer.write(b'\xd5') + elif L == 4: + self._buffer.write(b'\xd6') + elif L == 8: + self._buffer.write(b'\xd7') + elif L == 16: + self._buffer.write(b'\xd8') + elif L <= 0xff: + self._buffer.write(b'\xc7' + struct.pack('B', L)) + elif L <= 0xffff: + self._buffer.write(b'\xc8' + struct.pack('>H', L)) + else: + self._buffer.write(b'\xc9' + struct.pack('>I', L)) + self._buffer.write(struct.pack('B', typecode)) + self._buffer.write(data) + + def _pack_array_header(self, n): + if n <= 0x0f: + return self._buffer.write(struct.pack('B', 0x90 + n)) + if n <= 0xffff: + return self._buffer.write(struct.pack(">BH", 0xdc, n)) + if n <= 0xffffffff: + return self._buffer.write(struct.pack(">BI", 0xdd, n)) + raise PackValueError("Array is too large") + + def _pack_map_header(self, n): + if n <= 0x0f: + return self._buffer.write(struct.pack('B', 0x80 + n)) + if n <= 0xffff: + return self._buffer.write(struct.pack(">BH", 0xde, n)) + if n <= 0xffffffff: + return self._buffer.write(struct.pack(">BI", 0xdf, n)) + raise PackValueError("Dict is too large") + + def _pack_map_pairs(self, n, pairs, nest_limit=DEFAULT_RECURSE_LIMIT): + self._pack_map_header(n) + for (k, v) in pairs: + self._pack(k, nest_limit - 1) + self._pack(v, nest_limit - 1) + + def _pack_raw_header(self, n): + if n <= 0x1f: + self._buffer.write(struct.pack('B', 0xa0 + n)) + elif self._use_bin_type and n <= 0xff: + self._buffer.write(struct.pack('>BB', 0xd9, n)) + elif n <= 0xffff: + self._buffer.write(struct.pack(">BH", 0xda, n)) + elif n <= 0xffffffff: + self._buffer.write(struct.pack(">BI", 0xdb, n)) + else: + raise PackValueError('Raw is too large') + + def _pack_bin_header(self, n): + if not self._use_bin_type: + return self._pack_raw_header(n) + elif n <= 0xff: + return self._buffer.write(struct.pack('>BB', 0xc4, n)) + elif n <= 0xffff: + return self._buffer.write(struct.pack(">BH", 0xc5, n)) + elif n <= 0xffffffff: + return self._buffer.write(struct.pack(">BI", 0xc6, n)) + else: + raise PackValueError('Bin is too large') + + def bytes(self): + return self._buffer.getvalue() + + def reset(self): + self._buffer = StringIO() diff --git a/venv2/lib/python2.7/site-packages/pip/_vendor/packaging/__about__.py b/venv2/lib/python2.7/site-packages/pip/_vendor/packaging/__about__.py new file mode 100644 index 0000000..7481c9e --- /dev/null +++ b/venv2/lib/python2.7/site-packages/pip/_vendor/packaging/__about__.py @@ -0,0 +1,27 @@ +# 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__ = "19.0" + +__author__ = "Donald Stufft and individual contributors" +__email__ = "donald@stufft.io" + +__license__ = "BSD or Apache License, Version 2.0" +__copyright__ = "Copyright 2014-2019 %s" % __author__ diff --git a/venv2/lib/python2.7/site-packages/pip/_vendor/packaging/__init__.py b/venv2/lib/python2.7/site-packages/pip/_vendor/packaging/__init__.py new file mode 100644 index 0000000..a0cf67d --- /dev/null +++ b/venv2/lib/python2.7/site-packages/pip/_vendor/packaging/__init__.py @@ -0,0 +1,26 @@ +# 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/venv2/lib/python2.7/site-packages/pip/_vendor/packaging/_compat.py b/venv2/lib/python2.7/site-packages/pip/_vendor/packaging/_compat.py new file mode 100644 index 0000000..25da473 --- /dev/null +++ b/venv2/lib/python2.7/site-packages/pip/_vendor/packaging/_compat.py @@ -0,0 +1,31 @@ +# 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/venv2/lib/python2.7/site-packages/pip/_vendor/packaging/_structures.py b/venv2/lib/python2.7/site-packages/pip/_vendor/packaging/_structures.py new file mode 100644 index 0000000..68dcca6 --- /dev/null +++ b/venv2/lib/python2.7/site-packages/pip/_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/venv2/lib/python2.7/site-packages/pip/_vendor/packaging/markers.py b/venv2/lib/python2.7/site-packages/pip/_vendor/packaging/markers.py new file mode 100644 index 0000000..5482476 --- /dev/null +++ b/venv2/lib/python2.7/site-packages/pip/_vendor/packaging/markers.py @@ -0,0 +1,296 @@ +# 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 pip._vendor.pyparsing import ParseException, ParseResults, stringStart, stringEnd +from pip._vendor.pyparsing import ZeroOrMore, Group, Forward, QuotedString +from pip._vendor.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)) + + def serialize(self): + raise NotImplementedError + + +class Variable(Node): + def serialize(self): + return str(self) + + +class Value(Node): + def serialize(self): + return '"{0}"'.format(self) + + +class Op(Node): + def serialize(self): + return str(self) + + +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") + | L("sys.platform") # PEP-345 + | L("platform.version") # PEP-345 + | L("platform.machine") # PEP-345 + | L("platform.python_implementation") # PEP-345 + | L("python_implementation") # PEP-345 + | L("extra") # undocumented setuptools legacy +) +ALIASES = { + "os.name": "os_name", + "sys.platform": "sys_platform", + "platform.version": "platform_version", + "platform.machine": "platform_machine", + "platform.python_implementation": "platform_python_implementation", + "python_implementation": "platform_python_implementation", +} +VARIABLE.setParseAction(lambda s, l, t: Variable(ALIASES.get(t[0], t[0]))) + +VERSION_CMP = ( + L("===") | L("==") | L(">=") | L("<=") | L("!=") | L("~=") | L(">") | L("<") +) + +MARKER_OP = VERSION_CMP | L("not in") | L("in") +MARKER_OP.setParseAction(lambda s, l, t: Op(t[0])) + +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 " ".join([m.serialize() for m in 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.serialize(), rhs])) + except InvalidSpecifier: + pass + else: + return spec.contains(lhs) + + oper = _operators.get(op.serialize()) + 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/venv2/lib/python2.7/site-packages/pip/_vendor/packaging/requirements.py b/venv2/lib/python2.7/site-packages/pip/_vendor/packaging/requirements.py new file mode 100644 index 0000000..dbc5f11 --- /dev/null +++ b/venv2/lib/python2.7/site-packages/pip/_vendor/packaging/requirements.py @@ -0,0 +1,138 @@ +# 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 pip._vendor.pyparsing import stringStart, stringEnd, originalTextFor, ParseException +from pip._vendor.pyparsing import ZeroOrMore, Word, Optional, Regex, Combine +from pip._vendor.pyparsing import Literal as L # noqa +from pip._vendor.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_SEPARATOR = SEMICOLON +MARKER = MARKER_SEPARATOR + 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 +# pyparsing isn't thread safe during initialization, so we do it eagerly, see +# issue #104 +REQUIREMENT.parseString("x[]") + + +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( + 'Parse error at "{0!r}": {1}'.format( + requirement_string[e.loc : e.loc + 8], e.msg + ) + ) + + self.name = req.name + if req.url: + parsed_url = urlparse.urlparse(req.url) + if parsed_url.scheme == "file": + if urlparse.urlunparse(parsed_url) != req.url: + raise InvalidRequirement("Invalid URL given") + elif not (parsed_url.scheme and parsed_url.netloc) or ( + not parsed_url.scheme and not parsed_url.netloc + ): + raise InvalidRequirement("Invalid URL: {0}".format(req.url)) + 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(" ") + + 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/venv2/lib/python2.7/site-packages/pip/_vendor/packaging/specifiers.py b/venv2/lib/python2.7/site-packages/pip/_vendor/packaging/specifiers.py new file mode 100644 index 0000000..743576a --- /dev/null +++ b/venv2/lib/python2.7/site-packages/pip/_vendor/packaging/specifiers.py @@ -0,0 +1,749 @@ +# 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 beginning. + 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 technically 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/venv2/lib/python2.7/site-packages/pip/_vendor/packaging/utils.py b/venv2/lib/python2.7/site-packages/pip/_vendor/packaging/utils.py new file mode 100644 index 0000000..8841878 --- /dev/null +++ b/venv2/lib/python2.7/site-packages/pip/_vendor/packaging/utils.py @@ -0,0 +1,57 @@ +# 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 + +from .version import InvalidVersion, Version + + +_canonicalize_regex = re.compile(r"[-_.]+") + + +def canonicalize_name(name): + # This is taken from PEP 503. + return _canonicalize_regex.sub("-", name).lower() + + +def canonicalize_version(version): + """ + This is very similar to Version.__str__, but has one subtle differences + with the way it handles the release segment. + """ + + try: + version = Version(version) + except InvalidVersion: + # Legacy versions cannot be normalized + return version + + parts = [] + + # Epoch + if version.epoch != 0: + parts.append("{0}!".format(version.epoch)) + + # Release segment + # NB: This strips trailing '.0's to normalize + parts.append(re.sub(r"(\.0)+$", "", ".".join(str(x) for x in version.release))) + + # Pre-release + if version.pre is not None: + parts.append("".join(str(x) for x in version.pre)) + + # Post-release + if version.post is not None: + parts.append(".post{0}".format(version.post)) + + # Development release + if version.dev is not None: + parts.append(".dev{0}".format(version.dev)) + + # Local version segment + if version.local is not None: + parts.append("+{0}".format(version.local)) + + return "".join(parts) diff --git a/venv2/lib/python2.7/site-packages/pip/_vendor/packaging/version.py b/venv2/lib/python2.7/site-packages/pip/_vendor/packaging/version.py new file mode 100644 index 0000000..95157a1 --- /dev/null +++ b/venv2/lib/python2.7/site-packages/pip/_vendor/packaging/version.py @@ -0,0 +1,420 @@ +# 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 epoch(self): + return -1 + + @property + def release(self): + return None + + @property + def pre(self): + return None + + @property + def post(self): + return None + + @property + def dev(self): + return None + + @property + def local(self): + return None + + @property + def is_prerelease(self): + return False + + @property + def is_postrelease(self): + return False + + @property + def is_devrelease(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.epoch != 0: + parts.append("{0}!".format(self.epoch)) + + # Release segment + parts.append(".".join(str(x) for x in self.release)) + + # Pre-release + if self.pre is not None: + parts.append("".join(str(x) for x in self.pre)) + + # Post-release + if self.post is not None: + parts.append(".post{0}".format(self.post)) + + # Development release + if self.dev is not None: + parts.append(".dev{0}".format(self.dev)) + + # Local version segment + if self.local is not None: + parts.append("+{0}".format(self.local)) + + return "".join(parts) + + @property + def epoch(self): + return self._version.epoch + + @property + def release(self): + return self._version.release + + @property + def pre(self): + return self._version.pre + + @property + def post(self): + return self._version.post[1] if self._version.post else None + + @property + def dev(self): + return self._version.dev[1] if self._version.dev else None + + @property + def local(self): + if self._version.local: + return ".".join(str(x) for x in self._version.local) + else: + return None + + @property + def public(self): + return str(self).split("+", 1)[0] + + @property + def base_version(self): + parts = [] + + # Epoch + if self.epoch != 0: + parts.append("{0}!".format(self.epoch)) + + # Release segment + parts.append(".".join(str(x) for x in self.release)) + + return "".join(parts) + + @property + def is_prerelease(self): + return self.dev is not None or self.pre is not None + + @property + def is_postrelease(self): + return self.post is not None + + @property + def is_devrelease(self): + return self.dev is not None + + +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_separators = 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_separators.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/venv2/lib/python2.7/site-packages/pip/_vendor/pep517/__init__.py b/venv2/lib/python2.7/site-packages/pip/_vendor/pep517/__init__.py new file mode 100644 index 0000000..9c1a098 --- /dev/null +++ b/venv2/lib/python2.7/site-packages/pip/_vendor/pep517/__init__.py @@ -0,0 +1,4 @@ +"""Wrappers to build Python packages using PEP 517 hooks +""" + +__version__ = '0.5.0' diff --git a/venv2/lib/python2.7/site-packages/pip/_vendor/pep517/_in_process.py b/venv2/lib/python2.7/site-packages/pip/_vendor/pep517/_in_process.py new file mode 100644 index 0000000..d6524b6 --- /dev/null +++ b/venv2/lib/python2.7/site-packages/pip/_vendor/pep517/_in_process.py @@ -0,0 +1,207 @@ +"""This is invoked in a subprocess to call the build backend hooks. + +It expects: +- Command line args: hook_name, control_dir +- Environment variable: PEP517_BUILD_BACKEND=entry.point:spec +- control_dir/input.json: + - {"kwargs": {...}} + +Results: +- control_dir/output.json + - {"return_val": ...} +""" +from glob import glob +from importlib import import_module +import os +from os.path import join as pjoin +import re +import shutil +import sys + +# This is run as a script, not a module, so it can't do a relative import +import compat + + +class BackendUnavailable(Exception): + """Raised if we cannot import the backend""" + + +def _build_backend(): + """Find and load the build backend""" + ep = os.environ['PEP517_BUILD_BACKEND'] + mod_path, _, obj_path = ep.partition(':') + try: + obj = import_module(mod_path) + except ImportError: + raise BackendUnavailable + if obj_path: + for path_part in obj_path.split('.'): + obj = getattr(obj, path_part) + return obj + + +def get_requires_for_build_wheel(config_settings): + """Invoke the optional get_requires_for_build_wheel hook + + Returns [] if the hook is not defined. + """ + backend = _build_backend() + try: + hook = backend.get_requires_for_build_wheel + except AttributeError: + return [] + else: + return hook(config_settings) + + +def prepare_metadata_for_build_wheel(metadata_directory, config_settings): + """Invoke optional prepare_metadata_for_build_wheel + + Implements a fallback by building a wheel if the hook isn't defined. + """ + backend = _build_backend() + try: + hook = backend.prepare_metadata_for_build_wheel + except AttributeError: + return _get_wheel_metadata_from_wheel(backend, metadata_directory, + config_settings) + else: + return hook(metadata_directory, config_settings) + + +WHEEL_BUILT_MARKER = 'PEP517_ALREADY_BUILT_WHEEL' + + +def _dist_info_files(whl_zip): + """Identify the .dist-info folder inside a wheel ZipFile.""" + res = [] + for path in whl_zip.namelist(): + m = re.match(r'[^/\\]+-[^/\\]+\.dist-info/', path) + if m: + res.append(path) + if res: + return res + raise Exception("No .dist-info folder found in wheel") + + +def _get_wheel_metadata_from_wheel( + backend, metadata_directory, config_settings): + """Build a wheel and extract the metadata from it. + + Fallback for when the build backend does not + define the 'get_wheel_metadata' hook. + """ + from zipfile import ZipFile + whl_basename = backend.build_wheel(metadata_directory, config_settings) + with open(os.path.join(metadata_directory, WHEEL_BUILT_MARKER), 'wb'): + pass # Touch marker file + + whl_file = os.path.join(metadata_directory, whl_basename) + with ZipFile(whl_file) as zipf: + dist_info = _dist_info_files(zipf) + zipf.extractall(path=metadata_directory, members=dist_info) + return dist_info[0].split('/')[0] + + +def _find_already_built_wheel(metadata_directory): + """Check for a wheel already built during the get_wheel_metadata hook. + """ + if not metadata_directory: + return None + metadata_parent = os.path.dirname(metadata_directory) + if not os.path.isfile(pjoin(metadata_parent, WHEEL_BUILT_MARKER)): + return None + + whl_files = glob(os.path.join(metadata_parent, '*.whl')) + if not whl_files: + print('Found wheel built marker, but no .whl files') + return None + if len(whl_files) > 1: + print('Found multiple .whl files; unspecified behaviour. ' + 'Will call build_wheel.') + return None + + # Exactly one .whl file + return whl_files[0] + + +def build_wheel(wheel_directory, config_settings, metadata_directory=None): + """Invoke the mandatory build_wheel hook. + + If a wheel was already built in the + prepare_metadata_for_build_wheel fallback, this + will copy it rather than rebuilding the wheel. + """ + prebuilt_whl = _find_already_built_wheel(metadata_directory) + if prebuilt_whl: + shutil.copy2(prebuilt_whl, wheel_directory) + return os.path.basename(prebuilt_whl) + + return _build_backend().build_wheel(wheel_directory, config_settings, + metadata_directory) + + +def get_requires_for_build_sdist(config_settings): + """Invoke the optional get_requires_for_build_wheel hook + + Returns [] if the hook is not defined. + """ + backend = _build_backend() + try: + hook = backend.get_requires_for_build_sdist + except AttributeError: + return [] + else: + return hook(config_settings) + + +class _DummyException(Exception): + """Nothing should ever raise this exception""" + + +class GotUnsupportedOperation(Exception): + """For internal use when backend raises UnsupportedOperation""" + + +def build_sdist(sdist_directory, config_settings): + """Invoke the mandatory build_sdist hook.""" + backend = _build_backend() + try: + return backend.build_sdist(sdist_directory, config_settings) + except getattr(backend, 'UnsupportedOperation', _DummyException): + raise GotUnsupportedOperation + + +HOOK_NAMES = { + 'get_requires_for_build_wheel', + 'prepare_metadata_for_build_wheel', + 'build_wheel', + 'get_requires_for_build_sdist', + 'build_sdist', +} + + +def main(): + if len(sys.argv) < 3: + sys.exit("Needs args: hook_name, control_dir") + hook_name = sys.argv[1] + control_dir = sys.argv[2] + if hook_name not in HOOK_NAMES: + sys.exit("Unknown hook: %s" % hook_name) + hook = globals()[hook_name] + + hook_input = compat.read_json(pjoin(control_dir, 'input.json')) + + json_out = {'unsupported': False, 'return_val': None} + try: + json_out['return_val'] = hook(**hook_input['kwargs']) + except BackendUnavailable: + json_out['no_backend'] = True + except GotUnsupportedOperation: + json_out['unsupported'] = True + + compat.write_json(json_out, pjoin(control_dir, 'output.json'), indent=2) + + +if __name__ == '__main__': + main() diff --git a/venv2/lib/python2.7/site-packages/pip/_vendor/pep517/build.py b/venv2/lib/python2.7/site-packages/pip/_vendor/pep517/build.py new file mode 100644 index 0000000..ac6c949 --- /dev/null +++ b/venv2/lib/python2.7/site-packages/pip/_vendor/pep517/build.py @@ -0,0 +1,108 @@ +"""Build a project using PEP 517 hooks. +""" +import argparse +import logging +import os +import contextlib +from pip._vendor import pytoml +import shutil +import errno +import tempfile + +from .envbuild import BuildEnvironment +from .wrappers import Pep517HookCaller + +log = logging.getLogger(__name__) + + +@contextlib.contextmanager +def tempdir(): + td = tempfile.mkdtemp() + try: + yield td + finally: + shutil.rmtree(td) + + +def _do_build(hooks, env, dist, dest): + get_requires_name = 'get_requires_for_build_{dist}'.format(**locals()) + get_requires = getattr(hooks, get_requires_name) + reqs = get_requires({}) + log.info('Got build requires: %s', reqs) + + env.pip_install(reqs) + log.info('Installed dynamic build dependencies') + + with tempdir() as td: + log.info('Trying to build %s in %s', dist, td) + build_name = 'build_{dist}'.format(**locals()) + build = getattr(hooks, build_name) + filename = build(td, {}) + source = os.path.join(td, filename) + shutil.move(source, os.path.join(dest, os.path.basename(filename))) + + +def mkdir_p(*args, **kwargs): + """Like `mkdir`, but does not raise an exception if the + directory already exists. + """ + try: + return os.mkdir(*args, **kwargs) + except OSError as exc: + if exc.errno != errno.EEXIST: + raise + + +def build(source_dir, dist, dest=None): + pyproject = os.path.join(source_dir, 'pyproject.toml') + dest = os.path.join(source_dir, dest or 'dist') + mkdir_p(dest) + + with open(pyproject) as f: + pyproject_data = pytoml.load(f) + # Ensure the mandatory data can be loaded + buildsys = pyproject_data['build-system'] + requires = buildsys['requires'] + backend = buildsys['build-backend'] + + hooks = Pep517HookCaller(source_dir, backend) + + with BuildEnvironment() as env: + env.pip_install(requires) + _do_build(hooks, env, dist, dest) + + +parser = argparse.ArgumentParser() +parser.add_argument( + 'source_dir', + help="A directory containing pyproject.toml", +) +parser.add_argument( + '--binary', '-b', + action='store_true', + default=False, +) +parser.add_argument( + '--source', '-s', + action='store_true', + default=False, +) +parser.add_argument( + '--out-dir', '-o', + help="Destination in which to save the builds relative to source dir", +) + + +def main(args): + # determine which dists to build + dists = list(filter(None, ( + 'sdist' if args.source or not args.binary else None, + 'wheel' if args.binary or not args.source else None, + ))) + + for dist in dists: + build(args.source_dir, dist, args.out_dir) + + +if __name__ == '__main__': + main(parser.parse_args()) diff --git a/venv2/lib/python2.7/site-packages/pip/_vendor/pep517/check.py b/venv2/lib/python2.7/site-packages/pip/_vendor/pep517/check.py new file mode 100644 index 0000000..f4cdc6b --- /dev/null +++ b/venv2/lib/python2.7/site-packages/pip/_vendor/pep517/check.py @@ -0,0 +1,202 @@ +"""Check a project and backend by attempting to build using PEP 517 hooks. +""" +import argparse +import logging +import os +from os.path import isfile, join as pjoin +from pip._vendor.pytoml import TomlError, load as toml_load +import shutil +from subprocess import CalledProcessError +import sys +import tarfile +from tempfile import mkdtemp +import zipfile + +from .colorlog import enable_colourful_output +from .envbuild import BuildEnvironment +from .wrappers import Pep517HookCaller + +log = logging.getLogger(__name__) + + +def check_build_sdist(hooks, build_sys_requires): + with BuildEnvironment() as env: + try: + env.pip_install(build_sys_requires) + log.info('Installed static build dependencies') + except CalledProcessError: + log.error('Failed to install static build dependencies') + return False + + try: + reqs = hooks.get_requires_for_build_sdist({}) + log.info('Got build requires: %s', reqs) + except Exception: + log.error('Failure in get_requires_for_build_sdist', exc_info=True) + return False + + try: + env.pip_install(reqs) + log.info('Installed dynamic build dependencies') + except CalledProcessError: + log.error('Failed to install dynamic build dependencies') + return False + + td = mkdtemp() + log.info('Trying to build sdist in %s', td) + try: + try: + filename = hooks.build_sdist(td, {}) + log.info('build_sdist returned %r', filename) + except Exception: + log.info('Failure in build_sdist', exc_info=True) + return False + + if not filename.endswith('.tar.gz'): + log.error( + "Filename %s doesn't have .tar.gz extension", filename) + return False + + path = pjoin(td, filename) + if isfile(path): + log.info("Output file %s exists", path) + else: + log.error("Output file %s does not exist", path) + return False + + if tarfile.is_tarfile(path): + log.info("Output file is a tar file") + else: + log.error("Output file is not a tar file") + return False + + finally: + shutil.rmtree(td) + + return True + + +def check_build_wheel(hooks, build_sys_requires): + with BuildEnvironment() as env: + try: + env.pip_install(build_sys_requires) + log.info('Installed static build dependencies') + except CalledProcessError: + log.error('Failed to install static build dependencies') + return False + + try: + reqs = hooks.get_requires_for_build_wheel({}) + log.info('Got build requires: %s', reqs) + except Exception: + log.error('Failure in get_requires_for_build_sdist', exc_info=True) + return False + + try: + env.pip_install(reqs) + log.info('Installed dynamic build dependencies') + except CalledProcessError: + log.error('Failed to install dynamic build dependencies') + return False + + td = mkdtemp() + log.info('Trying to build wheel in %s', td) + try: + try: + filename = hooks.build_wheel(td, {}) + log.info('build_wheel returned %r', filename) + except Exception: + log.info('Failure in build_wheel', exc_info=True) + return False + + if not filename.endswith('.whl'): + log.error("Filename %s doesn't have .whl extension", filename) + return False + + path = pjoin(td, filename) + if isfile(path): + log.info("Output file %s exists", path) + else: + log.error("Output file %s does not exist", path) + return False + + if zipfile.is_zipfile(path): + log.info("Output file is a zip file") + else: + log.error("Output file is not a zip file") + return False + + finally: + shutil.rmtree(td) + + return True + + +def check(source_dir): + pyproject = pjoin(source_dir, 'pyproject.toml') + if isfile(pyproject): + log.info('Found pyproject.toml') + else: + log.error('Missing pyproject.toml') + return False + + try: + with open(pyproject) as f: + pyproject_data = toml_load(f) + # Ensure the mandatory data can be loaded + buildsys = pyproject_data['build-system'] + requires = buildsys['requires'] + backend = buildsys['build-backend'] + log.info('Loaded pyproject.toml') + except (TomlError, KeyError): + log.error("Invalid pyproject.toml", exc_info=True) + return False + + hooks = Pep517HookCaller(source_dir, backend) + + sdist_ok = check_build_sdist(hooks, requires) + wheel_ok = check_build_wheel(hooks, requires) + + if not sdist_ok: + log.warning('Sdist checks failed; scroll up to see') + if not wheel_ok: + log.warning('Wheel checks failed') + + return sdist_ok + + +def main(argv=None): + ap = argparse.ArgumentParser() + ap.add_argument( + 'source_dir', + help="A directory containing pyproject.toml") + args = ap.parse_args(argv) + + enable_colourful_output() + + ok = check(args.source_dir) + + if ok: + print(ansi('Checks passed', 'green')) + else: + print(ansi('Checks failed', 'red')) + sys.exit(1) + + +ansi_codes = { + 'reset': '\x1b[0m', + 'bold': '\x1b[1m', + 'red': '\x1b[31m', + 'green': '\x1b[32m', +} + + +def ansi(s, attr): + if os.name != 'nt' and sys.stdout.isatty(): + return ansi_codes[attr] + str(s) + ansi_codes['reset'] + else: + return str(s) + + +if __name__ == '__main__': + main() diff --git a/venv2/lib/python2.7/site-packages/pip/_vendor/pep517/colorlog.py b/venv2/lib/python2.7/site-packages/pip/_vendor/pep517/colorlog.py new file mode 100644 index 0000000..69c8a59 --- /dev/null +++ b/venv2/lib/python2.7/site-packages/pip/_vendor/pep517/colorlog.py @@ -0,0 +1,115 @@ +"""Nicer log formatting with colours. + +Code copied from Tornado, Apache licensed. +""" +# Copyright 2012 Facebook +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +import logging +import sys + +try: + import curses +except ImportError: + curses = None + + +def _stderr_supports_color(): + color = False + if curses and hasattr(sys.stderr, 'isatty') and sys.stderr.isatty(): + try: + curses.setupterm() + if curses.tigetnum("colors") > 0: + color = True + except Exception: + pass + return color + + +class LogFormatter(logging.Formatter): + """Log formatter with colour support + """ + DEFAULT_COLORS = { + logging.INFO: 2, # Green + logging.WARNING: 3, # Yellow + logging.ERROR: 1, # Red + logging.CRITICAL: 1, + } + + def __init__(self, color=True, datefmt=None): + r""" + :arg bool color: Enables color support. + :arg string fmt: Log message format. + It will be applied to the attributes dict of log records. The + text between ``%(color)s`` and ``%(end_color)s`` will be colored + depending on the level if color support is on. + :arg dict colors: color mappings from logging level to terminal color + code + :arg string datefmt: Datetime format. + Used for formatting ``(asctime)`` placeholder in ``prefix_fmt``. + .. versionchanged:: 3.2 + Added ``fmt`` and ``datefmt`` arguments. + """ + logging.Formatter.__init__(self, datefmt=datefmt) + self._colors = {} + if color and _stderr_supports_color(): + # The curses module has some str/bytes confusion in + # python3. Until version 3.2.3, most methods return + # bytes, but only accept strings. In addition, we want to + # output these strings with the logging module, which + # works with unicode strings. The explicit calls to + # unicode() below are harmless in python2 but will do the + # right conversion in python 3. + fg_color = (curses.tigetstr("setaf") or + curses.tigetstr("setf") or "") + if (3, 0) < sys.version_info < (3, 2, 3): + fg_color = str(fg_color, "ascii") + + for levelno, code in self.DEFAULT_COLORS.items(): + self._colors[levelno] = str( + curses.tparm(fg_color, code), "ascii") + self._normal = str(curses.tigetstr("sgr0"), "ascii") + + scr = curses.initscr() + self.termwidth = scr.getmaxyx()[1] + curses.endwin() + else: + self._normal = '' + # Default width is usually 80, but too wide is + # worse than too narrow + self.termwidth = 70 + + def formatMessage(self, record): + mlen = len(record.message) + right_text = '{initial}-{name}'.format(initial=record.levelname[0], + name=record.name) + if mlen + len(right_text) < self.termwidth: + space = ' ' * (self.termwidth - (mlen + len(right_text))) + else: + space = ' ' + + if record.levelno in self._colors: + start_color = self._colors[record.levelno] + end_color = self._normal + else: + start_color = end_color = '' + + return record.message + space + start_color + right_text + end_color + + +def enable_colourful_output(level=logging.INFO): + handler = logging.StreamHandler() + handler.setFormatter(LogFormatter()) + logging.root.addHandler(handler) + logging.root.setLevel(level) diff --git a/venv2/lib/python2.7/site-packages/pip/_vendor/pep517/compat.py b/venv2/lib/python2.7/site-packages/pip/_vendor/pep517/compat.py new file mode 100644 index 0000000..01c66fc --- /dev/null +++ b/venv2/lib/python2.7/site-packages/pip/_vendor/pep517/compat.py @@ -0,0 +1,23 @@ +"""Handle reading and writing JSON in UTF-8, on Python 3 and 2.""" +import json +import sys + +if sys.version_info[0] >= 3: + # Python 3 + def write_json(obj, path, **kwargs): + with open(path, 'w', encoding='utf-8') as f: + json.dump(obj, f, **kwargs) + + def read_json(path): + with open(path, 'r', encoding='utf-8') as f: + return json.load(f) + +else: + # Python 2 + def write_json(obj, path, **kwargs): + with open(path, 'wb') as f: + json.dump(obj, f, encoding='utf-8', **kwargs) + + def read_json(path): + with open(path, 'rb') as f: + return json.load(f) diff --git a/venv2/lib/python2.7/site-packages/pip/_vendor/pep517/envbuild.py b/venv2/lib/python2.7/site-packages/pip/_vendor/pep517/envbuild.py new file mode 100644 index 0000000..f7ac5f4 --- /dev/null +++ b/venv2/lib/python2.7/site-packages/pip/_vendor/pep517/envbuild.py @@ -0,0 +1,158 @@ +"""Build wheels/sdists by installing build deps to a temporary environment. +""" + +import os +import logging +from pip._vendor import pytoml +import shutil +from subprocess import check_call +import sys +from sysconfig import get_paths +from tempfile import mkdtemp + +from .wrappers import Pep517HookCaller + +log = logging.getLogger(__name__) + + +def _load_pyproject(source_dir): + with open(os.path.join(source_dir, 'pyproject.toml')) as f: + pyproject_data = pytoml.load(f) + buildsys = pyproject_data['build-system'] + return buildsys['requires'], buildsys['build-backend'] + + +class BuildEnvironment(object): + """Context manager to install build deps in a simple temporary environment + + Based on code I wrote for pip, which is MIT licensed. + """ + # Copyright (c) 2008-2016 The pip developers (see AUTHORS.txt file) + # + # 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. + + path = None + + def __init__(self, cleanup=True): + self._cleanup = cleanup + + def __enter__(self): + self.path = mkdtemp(prefix='pep517-build-env-') + log.info('Temporary build environment: %s', self.path) + + self.save_path = os.environ.get('PATH', None) + self.save_pythonpath = os.environ.get('PYTHONPATH', None) + + install_scheme = 'nt' if (os.name == 'nt') else 'posix_prefix' + install_dirs = get_paths(install_scheme, vars={ + 'base': self.path, + 'platbase': self.path, + }) + + scripts = install_dirs['scripts'] + if self.save_path: + os.environ['PATH'] = scripts + os.pathsep + self.save_path + else: + os.environ['PATH'] = scripts + os.pathsep + os.defpath + + if install_dirs['purelib'] == install_dirs['platlib']: + lib_dirs = install_dirs['purelib'] + else: + lib_dirs = install_dirs['purelib'] + os.pathsep + \ + install_dirs['platlib'] + if self.save_pythonpath: + os.environ['PYTHONPATH'] = lib_dirs + os.pathsep + \ + self.save_pythonpath + else: + os.environ['PYTHONPATH'] = lib_dirs + + return self + + def pip_install(self, reqs): + """Install dependencies into this env by calling pip in a subprocess""" + if not reqs: + return + log.info('Calling pip to install %s', reqs) + check_call([ + sys.executable, '-m', 'pip', 'install', '--ignore-installed', + '--prefix', self.path] + list(reqs)) + + def __exit__(self, exc_type, exc_val, exc_tb): + needs_cleanup = ( + self._cleanup and + self.path is not None and + os.path.isdir(self.path) + ) + if needs_cleanup: + shutil.rmtree(self.path) + + if self.save_path is None: + os.environ.pop('PATH', None) + else: + os.environ['PATH'] = self.save_path + + if self.save_pythonpath is None: + os.environ.pop('PYTHONPATH', None) + else: + os.environ['PYTHONPATH'] = self.save_pythonpath + + +def build_wheel(source_dir, wheel_dir, config_settings=None): + """Build a wheel from a source directory using PEP 517 hooks. + + :param str source_dir: Source directory containing pyproject.toml + :param str wheel_dir: Target directory to create wheel in + :param dict config_settings: Options to pass to build backend + + This is a blocking function which will run pip in a subprocess to install + build requirements. + """ + if config_settings is None: + config_settings = {} + requires, backend = _load_pyproject(source_dir) + hooks = Pep517HookCaller(source_dir, backend) + + with BuildEnvironment() as env: + env.pip_install(requires) + reqs = hooks.get_requires_for_build_wheel(config_settings) + env.pip_install(reqs) + return hooks.build_wheel(wheel_dir, config_settings) + + +def build_sdist(source_dir, sdist_dir, config_settings=None): + """Build an sdist from a source directory using PEP 517 hooks. + + :param str source_dir: Source directory containing pyproject.toml + :param str sdist_dir: Target directory to place sdist in + :param dict config_settings: Options to pass to build backend + + This is a blocking function which will run pip in a subprocess to install + build requirements. + """ + if config_settings is None: + config_settings = {} + requires, backend = _load_pyproject(source_dir) + hooks = Pep517HookCaller(source_dir, backend) + + with BuildEnvironment() as env: + env.pip_install(requires) + reqs = hooks.get_requires_for_build_sdist(config_settings) + env.pip_install(reqs) + return hooks.build_sdist(sdist_dir, config_settings) diff --git a/venv2/lib/python2.7/site-packages/pip/_vendor/pep517/wrappers.py b/venv2/lib/python2.7/site-packages/pip/_vendor/pep517/wrappers.py new file mode 100644 index 0000000..b14b899 --- /dev/null +++ b/venv2/lib/python2.7/site-packages/pip/_vendor/pep517/wrappers.py @@ -0,0 +1,163 @@ +from contextlib import contextmanager +import os +from os.path import dirname, abspath, join as pjoin +import shutil +from subprocess import check_call +import sys +from tempfile import mkdtemp + +from . import compat + +_in_proc_script = pjoin(dirname(abspath(__file__)), '_in_process.py') + + +@contextmanager +def tempdir(): + td = mkdtemp() + try: + yield td + finally: + shutil.rmtree(td) + + +class BackendUnavailable(Exception): + """Will be raised if the backend cannot be imported in the hook process.""" + + +class UnsupportedOperation(Exception): + """May be raised by build_sdist if the backend indicates that it can't.""" + + +def default_subprocess_runner(cmd, cwd=None, extra_environ=None): + """The default method of calling the wrapper subprocess.""" + env = os.environ.copy() + if extra_environ: + env.update(extra_environ) + + check_call(cmd, cwd=cwd, env=env) + + +class Pep517HookCaller(object): + """A wrapper around a source directory to be built with a PEP 517 backend. + + source_dir : The path to the source directory, containing pyproject.toml. + backend : The build backend spec, as per PEP 517, from pyproject.toml. + """ + def __init__(self, source_dir, build_backend): + self.source_dir = abspath(source_dir) + self.build_backend = build_backend + self._subprocess_runner = default_subprocess_runner + + # TODO: Is this over-engineered? Maybe frontends only need to + # set this when creating the wrapper, not on every call. + @contextmanager + def subprocess_runner(self, runner): + prev = self._subprocess_runner + self._subprocess_runner = runner + yield + self._subprocess_runner = prev + + def get_requires_for_build_wheel(self, config_settings=None): + """Identify packages required for building a wheel + + Returns a list of dependency specifications, e.g.: + ["wheel >= 0.25", "setuptools"] + + This does not include requirements specified in pyproject.toml. + It returns the result of calling the equivalently named hook in a + subprocess. + """ + return self._call_hook('get_requires_for_build_wheel', { + 'config_settings': config_settings + }) + + def prepare_metadata_for_build_wheel( + self, metadata_directory, config_settings=None): + """Prepare a *.dist-info folder with metadata for this project. + + Returns the name of the newly created folder. + + If the build backend defines a hook with this name, it will be called + in a subprocess. If not, the backend will be asked to build a wheel, + and the dist-info extracted from that. + """ + return self._call_hook('prepare_metadata_for_build_wheel', { + 'metadata_directory': abspath(metadata_directory), + 'config_settings': config_settings, + }) + + def build_wheel( + self, wheel_directory, config_settings=None, + metadata_directory=None): + """Build a wheel from this project. + + Returns the name of the newly created file. + + In general, this will call the 'build_wheel' hook in the backend. + However, if that was previously called by + 'prepare_metadata_for_build_wheel', and the same metadata_directory is + used, the previously built wheel will be copied to wheel_directory. + """ + if metadata_directory is not None: + metadata_directory = abspath(metadata_directory) + return self._call_hook('build_wheel', { + 'wheel_directory': abspath(wheel_directory), + 'config_settings': config_settings, + 'metadata_directory': metadata_directory, + }) + + def get_requires_for_build_sdist(self, config_settings=None): + """Identify packages required for building a wheel + + Returns a list of dependency specifications, e.g.: + ["setuptools >= 26"] + + This does not include requirements specified in pyproject.toml. + It returns the result of calling the equivalently named hook in a + subprocess. + """ + return self._call_hook('get_requires_for_build_sdist', { + 'config_settings': config_settings + }) + + def build_sdist(self, sdist_directory, config_settings=None): + """Build an sdist from this project. + + Returns the name of the newly created file. + + This calls the 'build_sdist' backend hook in a subprocess. + """ + return self._call_hook('build_sdist', { + 'sdist_directory': abspath(sdist_directory), + 'config_settings': config_settings, + }) + + def _call_hook(self, hook_name, kwargs): + # On Python 2, pytoml returns Unicode values (which is correct) but the + # environment passed to check_call needs to contain string values. We + # convert here by encoding using ASCII (the backend can only contain + # letters, digits and _, . and : characters, and will be used as a + # Python identifier, so non-ASCII content is wrong on Python 2 in + # any case). + if sys.version_info[0] == 2: + build_backend = self.build_backend.encode('ASCII') + else: + build_backend = self.build_backend + + with tempdir() as td: + compat.write_json({'kwargs': kwargs}, pjoin(td, 'input.json'), + indent=2) + + # Run the hook in a subprocess + self._subprocess_runner( + [sys.executable, _in_proc_script, hook_name, td], + cwd=self.source_dir, + extra_environ={'PEP517_BUILD_BACKEND': build_backend} + ) + + data = compat.read_json(pjoin(td, 'output.json')) + if data.get('unsupported'): + raise UnsupportedOperation + if data.get('no_backend'): + raise BackendUnavailable + return data['return_val'] diff --git a/venv2/lib/python2.7/site-packages/pip/_vendor/pkg_resources/__init__.py b/venv2/lib/python2.7/site-packages/pip/_vendor/pkg_resources/__init__.py new file mode 100644 index 0000000..fdd40de --- /dev/null +++ b/venv2/lib/python2.7/site-packages/pip/_vendor/pkg_resources/__init__.py @@ -0,0 +1,3286 @@ +# coding: utf-8 +""" +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 errno +import tempfile +import textwrap +import itertools +import inspect +import ntpath +import posixpath +from pkgutil import get_importer + +try: + import _imp +except ImportError: + # Python 3.2 compatibility + import imp as _imp + +try: + FileExistsError +except NameError: + FileExistsError = OSError + +from pip._vendor import six +from pip._vendor.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 . import py31compat +from pip._vendor import appdirs +from pip._vendor import packaging +__import__('pip._vendor.packaging.version') +__import__('pip._vendor.packaging.specifiers') +__import__('pip._vendor.packaging.requirements') +__import__('pip._vendor.packaging.markers') + + +__metaclass__ = type + + +if (3, 0) < sys.version_info < (3, 4): + raise RuntimeError("Python 3.4 or later is required") + +if six.PY2: + # Those builtin exceptions are only defined in Python 3 + PermissionError = None + NotADirectoryError = None + +# declare some globals that will be defined later to +# satisfy the linters. +require = None +working_set = None +add_activation_listener = None +resources_stream = None +cleanup_resources = None +resource_dir = None +resource_stream = None +set_extraction_path = None +resource_isdir = None +resource_string = None +iter_entry_points = None +resource_listdir = None +resource_filename = None +resource_exists = None +_distribution_finders = None +_namespace_handlers = None +_namespace_packages = None + + +class PEP440Warning(RuntimeWarning): + """ + Used when there is an issue with a version or specifier not complying with + PEP 440. + """ + + +def parse_version(v): + try: + return packaging.version.Version(v) + except packaging.version.InvalidVersion: + return packaging.version.LegacyVersion(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', + + # Warnings + 'PkgResourcesDeprecationWarning', + + # 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. + """ + from sysconfig 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: + """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). + """ + return ( + entry + for dist in self + for entry in dist.get_entry_map(group).values() + if name is None or name == entry.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, extras=None): + """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. + + `extras` is a list of the extras to be used with these requirements. + This is important because extra requirements may look like `my_req; + extra = "my_extra"`, which would otherwise be interpreted as a purely + optional requirement. Instead, we want to be able to assert that these + requirements are truly required. + """ + + # 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, extras): + 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, + replace_conflicting=replace_conflicting + ) + 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, existing=True): + """Invoke `callback` for all distributions + + If `existing=True` (default), + call on all existing ones, as well. + """ + if callback in self.callbacks: + return + self.callbacks.append(callback) + if not existing: + return + 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, extras=None): + """ + 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, ()) + (extras or (None,)) + ) + return not req.marker or any(extra_evals) + + +class Environment: + """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.6'``); + 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. + """ + py_compat = ( + self.python is None + or dist.py_version is None + or dist.py_version == self.python + ) + return py_compat 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, replace_conflicting=False): + """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. + """ + try: + dist = working_set.find(req) + except VersionConflict: + if not replace_conflicting: + raise + dist = None + 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 Exception: + 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(): + """ + Return the ``PYTHON_EGG_CACHE`` environment variable + or a platform-relevant user cache dir for an app + named "Python-Eggs". + """ + return ( + os.environ.get('PYTHON_EGG_CACHE') + or appdirs.user_cache_dir(appname='Python-Eggs') + ) + + +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 _get_metadata_path(self, name): + return self._fn(self.egg_info, name) + + def has_metadata(self, name): + if not self.egg_info: + return self.egg_info + + path = self._get_metadata_path(name) + return self._has(path) + + def get_metadata(self, name): + if not self.egg_info: + return "" + value = self._get(self._fn(self.egg_info, name)) + return value.decode('utf-8') if six.PY3 else value + + 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( + "Script {script!r} not found in metadata at {self.egg_info!r}" + .format(**locals()), + ) + 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): + self._validate_resource_path(resource_name) + if resource_name: + return os.path.join(base, *resource_name.split('/')) + return base + + @staticmethod + def _validate_resource_path(path): + """ + Validate the resource paths according to the docs. + https://setuptools.readthedocs.io/en/latest/pkg_resources.html#basic-resource-access + + >>> warned = getfixture('recwarn') + >>> warnings.simplefilter('always') + >>> vrp = NullProvider._validate_resource_path + >>> vrp('foo/bar.txt') + >>> bool(warned) + False + >>> vrp('../foo/bar.txt') + >>> bool(warned) + True + >>> warned.clear() + >>> vrp('/foo/bar.txt') + >>> bool(warned) + True + >>> vrp('foo/../../bar.txt') + >>> bool(warned) + True + >>> warned.clear() + >>> vrp('foo/f../bar.txt') + >>> bool(warned) + False + + Windows path separators are straight-up disallowed. + >>> vrp(r'\\foo/bar.txt') + Traceback (most recent call last): + ... + ValueError: Use of .. or absolute path in a resource path \ +is not allowed. + + >>> vrp(r'C:\\foo/bar.txt') + Traceback (most recent call last): + ... + ValueError: Use of .. or absolute path in a resource path \ +is not allowed. + + Blank values are allowed + + >>> vrp('') + >>> bool(warned) + False + + Non-string values are not. + + >>> vrp(None) + Traceback (most recent call last): + ... + AttributeError: ... + """ + invalid = ( + os.path.pardir in path.split(posixpath.sep) or + posixpath.isabs(path) or + ntpath.isabs(path) + ) + if not invalid: + return + + msg = "Use of .. or absolute path in a resource path is not allowed." + + # Aggressively disallow Windows absolute paths + if ntpath.isabs(path) and not posixpath.isabs(path): + raise ValueError(msg) + + # for compatibility, warn; in future + # raise ValueError(msg) + warnings.warn( + msg[:-1] + " and will raise exceptions in a future release.", + DeprecationWarning, + stacklevel=4, + ) + + 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_egg_path(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_names = 'SourceFileLoader', 'SourcelessFileLoader', + for name in loader_names: + loader_cls = getattr(importlib_machinery, name, type(None)) + register_loader_type(loader_cls, cls) + + +DefaultProvider._register() + + +class EmptyProvider(NullProvider): + """Provider that returns nothing for all requests""" + + module_path = None + + _isdir = _has = lambda self, path: False + + def _get(self, path): + return '' + + def _listdir(self, path): + return [] + + 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 zipfile.ZipFile(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 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 + fspath = fspath.rstrip(os.sep) + if fspath == self.loader.archive: + return '' + 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 _get_metadata_path(self, name): + return self.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': + raise KeyError("No metadata except PKG-INFO is available") + + with io.open(self.path, encoding='utf-8', errors="replace") as f: + metadata = f.read() + self._warn_on_replacement(metadata) + return metadata + + def _warn_on_replacement(self, metadata): + # Python 2.7 compat for: replacement_char = '�' + replacement_char = b'\xef\xbf\xbd'.decode('utf-8') + if replacement_char in metadata: + tmpl = "{self.path} could not be properly decoded in UTF-8" + msg = tmpl.format(**locals()) + warnings.warn(msg) + + 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_egg_path(subitem): + subpath = os.path.join(path_item, subitem) + dists = find_eggs_in_zip(zipimport.zipimporter(subpath), subpath) + for dist in dists: + yield dist + elif subitem.lower().endswith('.dist-info'): + subpath = os.path.join(path_item, subitem) + submeta = EggMetadata(zipimport.zipimporter(subpath)) + submeta.egg_info = subpath + yield Distribution.from_location(path_item, subitem, submeta) + + +register_finder(zipimport.zipimporter, find_eggs_in_zip) + + +def find_nothing(importer, path_item, only=False): + return () + + +register_finder(object, find_nothing) + + +def _by_version_descending(names): + """ + Given a list of filenames, return them in descending order + by version number. + + >>> names = 'bar', 'foo', 'Python-2.7.10.egg', 'Python-2.7.2.egg' + >>> _by_version_descending(names) + ['Python-2.7.10.egg', 'Python-2.7.2.egg', 'foo', 'bar'] + >>> names = 'Setuptools-1.2.3b1.egg', 'Setuptools-1.2.3.egg' + >>> _by_version_descending(names) + ['Setuptools-1.2.3.egg', 'Setuptools-1.2.3b1.egg'] + >>> names = 'Setuptools-1.2.3b1.egg', 'Setuptools-1.2.3.post1.egg' + >>> _by_version_descending(names) + ['Setuptools-1.2.3.post1.egg', 'Setuptools-1.2.3b1.egg'] + """ + def _by_version(name): + """ + Parse each component of the filename + """ + name, ext = os.path.splitext(name) + parts = itertools.chain(name.split('-'), [ext]) + return [packaging.version.parse(part) for part in parts] + + return sorted(names, key=_by_version, reverse=True) + + +def find_on_path(importer, path_item, only=False): + """Yield distributions accessible on a sys.path directory""" + path_item = _normalize_cached(path_item) + + if _is_unpacked_egg(path_item): + yield Distribution.from_filename( + path_item, metadata=PathMetadata( + path_item, os.path.join(path_item, 'EGG-INFO') + ) + ) + return + + entries = safe_listdir(path_item) + + # for performance, before sorting by version, + # screen entries for only those that will yield + # distributions + filtered = ( + entry + for entry in entries + if dist_factory(path_item, entry, only) + ) + + # scan for .egg and .egg-info in directory + path_item_entries = _by_version_descending(filtered) + for entry in path_item_entries: + fullpath = os.path.join(path_item, entry) + factory = dist_factory(path_item, entry, only) + for dist in factory(fullpath): + yield dist + + +def dist_factory(path_item, entry, only): + """ + Return a dist_factory for a path_item and entry + """ + lower = entry.lower() + is_meta = any(map(lower.endswith, ('.egg-info', '.dist-info'))) + return ( + distributions_from_metadata + if is_meta else + find_distributions + if not only and _is_egg_path(entry) else + resolve_egg_link + if not only and lower.endswith('.egg-link') else + NoDists() + ) + + +class NoDists: + """ + >>> bool(NoDists()) + False + + >>> list(NoDists()('anything')) + [] + """ + def __bool__(self): + return False + if six.PY2: + __nonzero__ = __bool__ + + def __call__(self, fullpath): + return iter(()) + + +def safe_listdir(path): + """ + Attempt to list contents of path, but suppress some exceptions. + """ + try: + return os.listdir(path) + except (PermissionError, NotADirectoryError): + pass + except OSError as e: + # Ignore the directory if does not exist, not a directory or + # permission denied + ignorable = ( + e.errno in (errno.ENOTDIR, errno.EACCES, errno.ENOENT) + # Python 2 on Windows needs to be handled this way :( + or getattr(e, "winerror", None) == 267 + ) + if not ignorable: + raise + return () + + +def distributions_from_metadata(path): + root = os.path.dirname(path) + if os.path.isdir(path): + if len(os.listdir(path)) == 0: + # empty metadata dir; skip + return + metadata = PathMetadata(root, path) + else: + metadata = FileMetadata(path) + entry = os.path.basename(path) + yield Distribution.from_location( + root, entry, metadata, precedence=DEVELOP_DIST, + ) + + +def non_empty_lines(path): + """ + Yield non-empty lines from file at path + """ + with open(path) as f: + for line in f: + line = line.strip() + if line: + yield line + + +def resolve_egg_link(path): + """ + Given a path to an .egg-link, resolve distributions + present in the referenced path. + """ + referenced_paths = non_empty_lines(path) + resolved_paths = ( + os.path.join(os.path.dirname(path), ref) + for ref in referenced_paths + ) + dist_groups = map(find_distributions, resolved_paths) + return next(dist_groups, ()) + + +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 + + # capture warnings due to #1111 + with warnings.catch_warnings(): + warnings.simplefilter("ignore") + 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 safe_sys_path_index(entry): + """ + Workaround for #520 and #513. + """ + try: + return sys_path.index(entry) + except ValueError: + return float('inf') + + 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 safe_sys_path_index(_normalize_cached(os.sep.join(parts))) + + new_path = sorted(orig_path, key=position_in_sys_path) + new_path = [_normalize_cached(p) for p in new_path] + + if isinstance(module.__path__, list): + module.__path__[:] = new_path + else: + module.__path__ = new_path + + +def declare_namespace(packageName): + """Declare that package 'packageName' is a namespace package""" + + _imp.acquire_lock() + try: + if packageName in _namespace_packages: + return + + path = sys.path + parent, _, _ = packageName.rpartition('.') + + if parent: + 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 or None, []).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(os.path.normpath(_cygwin_patch(filename)))) + + +def _cygwin_patch(filename): # pragma: nocover + """ + Contrary to POSIX 2008, on Cygwin, getcwd (3) contains + symlink components. Using + os.path.abspath() works around this limitation. A fix in os.getcwd() + would probably better, in Cygwin even more so, except + that this seems to be by design... + """ + return os.path.abspath(filename) if sys.platform == 'cygwin' else filename + + +def _normalize_cached(filename, _cache={}): + try: + return _cache[filename] + except KeyError: + _cache[filename] = result = normalize_path(filename) + return result + + +def _is_egg_path(path): + """ + Determine if given path appears to be an egg. + """ + return path.lower().endswith('.egg') + + +def _is_unpacked_egg(path): + """ + Determine if given path appears to be an unpacked egg. + """ + return ( + _is_egg_path(path) and + os.path.isfile(os.path.join(path, 'EGG-INFO', 'PKG-INFO')) + ) + + +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 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 = tuple(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.", + PkgResourcesDeprecationWarning, + 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) + + # Get the requirements for this entry point with all its extras and + # then resolve them. We have to pass `extras` along when resolving so + # that the working set knows what extras we want. Otherwise, for + # dist-info distributions, the working set will assume that the + # requirements for that extra are purely optional and skip over them. + reqs = self.dist.requires(self.extras) + items = working_set.resolve(reqs, env, installer, extras=self.extras) + 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. + """ + def is_version_line(line): + return 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: + """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 = self._get_version() + if version is None: + path = self._get_metadata_path_for_display(self.PKG_INFO) + msg = ( + "Missing 'Version:' header and/or {} file at path: {}" + ).format(self.PKG_INFO, path) + raise ValueError(msg, self) + + return version + + @property + def _dep_map(self): + """ + A map of extra to its list of (direct) requirements + for this distribution, including the null extra. + """ + try: + return self.__dep_map + except AttributeError: + self.__dep_map = self._filter_extras(self._build_dep_map()) + return self.__dep_map + + @staticmethod + def _filter_extras(dm): + """ + Given a mapping of extras to dependencies, strip off + environment markers and filter out any dependencies + not matching the markers. + """ + for extra in list(filter(None, dm)): + new_extra = extra + reqs = dm.pop(extra) + new_extra, _, marker = extra.partition(':') + fails_marker = marker and ( + invalid_marker(marker) + or not evaluate_marker(marker) + ) + if fails_marker: + reqs = [] + new_extra = safe_extra(new_extra) or None + + dm.setdefault(new_extra, []).extend(reqs) + return dm + + def _build_dep_map(self): + dm = {} + for name in 'requires.txt', 'depends.txt': + for extra, reqs in split_sections(self._get_metadata(name)): + 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_path_for_display(self, name): + """ + Return the path to the given metadata file, if available. + """ + try: + # We need to access _get_metadata_path() on the provider object + # directly rather than through this class's __getattr__() + # since _get_metadata_path() is marked private. + path = self._provider._get_metadata_path(name) + + # Handle exceptions e.g. in case the distribution's metadata + # provider doesn't support _get_metadata_path(). + except Exception: + return '[could not detect]' + + return path + + def _get_metadata(self, name): + if self.has_metadata(name): + for line in self.get_metadata_lines(name): + yield line + + def _get_version(self): + lines = self._get_metadata(self.PKG_INFO) + version = _version_from_file(lines) + + return version + + def activate(self, path=None, replace=False): + """Ensure distribution is importable on `path` (default=sys.path)""" + if path is None: + path = sys.path + self.insert_on(path, replace=replace) + 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) + + def __dir__(self): + return list( + set(super(Distribution, self).__dir__()) + | set( + attr for attr in self._provider.__dir__() + if not attr.startswith('_') + ) + ) + + if not hasattr(object, '__dir__'): + # python 2.7 not supported + del __dir__ + + @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): + """Ensure self.location is on path + + If replace=False (default): + - If location is already in path anywhere, do nothing. + - Else: + - If it's an egg and its parent directory is on path, + insert just ahead of the parent. + - Else: add to the end of path. + If replace=True: + - If location is already on path anywhere (not eggs) + or higher priority than its parent (eggs) + do nothing. + - Else: + - If it's an egg and its parent directory is on path, + insert just ahead of the parent, + removing any lower-priority entries. + - Else: add it to the front of path. + """ + + 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: + if replace: + break + else: + # don't modify path (even removing duplicates) if + # found and not replace + return + elif item == bdir and self.precedence == EGG_DIST: + # if it's an .egg, give it precedence over its directory + # UNLESS it's already been added to sys.path and replace=False + if (not replace) and nloc in npath[p:]: + return + 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 = self._get_version() + 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 []: + s_extra = safe_extra(extra.strip()) + dm[s_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() + try: + line += next(lines) + except StopIteration: + return + 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 _always_object(classes): + """ + Ensure object appears in the mro even + for old-style classes. + """ + if object not in classes: + return classes + (object,) + return classes + + +def _find_adapter(registry, ob): + """Return an adapter factory for `ob` from `registry`""" + types = _always_object(inspect.getmro(getattr(ob, '__class__', type(ob)))) + for t in types: + if t in registry: + return registry[t] + + +def ensure_directory(path): + """Ensure that the parent directory of `path` exists""" + dirname = os.path.dirname(path) + py31compat.makedirs(dirname, exist_ok=True) + + +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) + try: + mkdir(dirname, 0o755) + except FileExistsError: + pass + + +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 + g.update( + (name, getattr(manager, name)) + for name in dir(manager) + if not name.startswith('_') + ) + + +@_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 with replace=False and + # ensure that all distributions added to the working set in the future + # (e.g. by calling ``require()``) will get activated as well, + # with higher priority (replace=True). + tuple( + dist.activate(replace=False) + for dist in working_set + ) + add_activation_listener( + lambda dist: dist.activate(replace=True), + existing=False, + ) + working_set.entries = [] + # match order + list(map(working_set.add_entry, sys.path)) + globals().update(locals()) + +class PkgResourcesDeprecationWarning(Warning): + """ + Base class for warning about deprecations in ``pkg_resources`` + + This class is not derived from ``DeprecationWarning``, and as such is + visible by default. + """ diff --git a/venv2/lib/python2.7/site-packages/pip/_vendor/pkg_resources/py31compat.py b/venv2/lib/python2.7/site-packages/pip/_vendor/pkg_resources/py31compat.py new file mode 100644 index 0000000..a2d3007 --- /dev/null +++ b/venv2/lib/python2.7/site-packages/pip/_vendor/pkg_resources/py31compat.py @@ -0,0 +1,23 @@ +import os +import errno +import sys + +from pip._vendor import six + + +def _makedirs_31(path, exist_ok=False): + try: + os.makedirs(path) + except OSError as exc: + if not exist_ok or exc.errno != errno.EEXIST: + raise + + +# rely on compatibility behavior until mode considerations +# and exists_ok considerations are disentangled. +# See https://github.com/pypa/setuptools/pull/1083#issuecomment-315168663 +needs_makedirs = ( + six.PY2 or + (3, 4) <= sys.version_info < (3, 4, 1) +) +makedirs = _makedirs_31 if needs_makedirs else os.makedirs diff --git a/venv2/lib/python2.7/site-packages/pip/_vendor/progress/__init__.py b/venv2/lib/python2.7/site-packages/pip/_vendor/progress/__init__.py new file mode 100644 index 0000000..e434c25 --- /dev/null +++ b/venv2/lib/python2.7/site-packages/pip/_vendor/progress/__init__.py @@ -0,0 +1,177 @@ +# Copyright (c) 2012 Giorgos Verigakis <verigak@gmail.com> +# +# Permission to use, copy, modify, and distribute this software for any +# purpose with or without fee is hereby granted, provided that the above +# copyright notice and this permission notice appear in all copies. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +# ANY SPECIAL, DIRECT, 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 division, print_function + +from collections import deque +from datetime import timedelta +from math import ceil +from sys import stderr +try: + from time import monotonic +except ImportError: + from time import time as monotonic + + +__version__ = '1.5' + +HIDE_CURSOR = '\x1b[?25l' +SHOW_CURSOR = '\x1b[?25h' + + +class Infinite(object): + file = stderr + sma_window = 10 # Simple Moving Average window + check_tty = True + hide_cursor = True + + def __init__(self, message='', **kwargs): + self.index = 0 + self.start_ts = monotonic() + self.avg = 0 + self._avg_update_ts = self.start_ts + self._ts = self.start_ts + self._xput = deque(maxlen=self.sma_window) + for key, val in kwargs.items(): + setattr(self, key, val) + + self._width = 0 + self.message = message + + if self.file and self.is_tty(): + if self.hide_cursor: + print(HIDE_CURSOR, end='', file=self.file) + print(self.message, end='', file=self.file) + self.file.flush() + + def __getitem__(self, key): + if key.startswith('_'): + return None + return getattr(self, key, None) + + @property + def elapsed(self): + return int(monotonic() - self.start_ts) + + @property + def elapsed_td(self): + return timedelta(seconds=self.elapsed) + + def update_avg(self, n, dt): + if n > 0: + xput_len = len(self._xput) + self._xput.append(dt / n) + now = monotonic() + # update when we're still filling _xput, then after every second + if (xput_len < self.sma_window or + now - self._avg_update_ts > 1): + self.avg = sum(self._xput) / len(self._xput) + self._avg_update_ts = now + + def update(self): + pass + + def start(self): + pass + + def clearln(self): + if self.file and self.is_tty(): + print('\r\x1b[K', end='', file=self.file) + + def write(self, s): + if self.file and self.is_tty(): + line = self.message + s.ljust(self._width) + print('\r' + line, end='', file=self.file) + self._width = max(self._width, len(s)) + self.file.flush() + + def writeln(self, line): + if self.file and self.is_tty(): + self.clearln() + print(line, end='', file=self.file) + self.file.flush() + + def finish(self): + if self.file and self.is_tty(): + print(file=self.file) + if self.hide_cursor: + print(SHOW_CURSOR, end='', file=self.file) + + def is_tty(self): + return self.file.isatty() if self.check_tty else True + + def next(self, n=1): + now = monotonic() + dt = now - self._ts + self.update_avg(n, dt) + self._ts = now + self.index = self.index + n + self.update() + + def iter(self, it): + with self: + for x in it: + yield x + self.next() + + def __enter__(self): + self.start() + return self + + def __exit__(self, exc_type, exc_val, exc_tb): + self.finish() + + +class Progress(Infinite): + def __init__(self, *args, **kwargs): + super(Progress, self).__init__(*args, **kwargs) + self.max = kwargs.get('max', 100) + + @property + def eta(self): + return int(ceil(self.avg * self.remaining)) + + @property + def eta_td(self): + return timedelta(seconds=self.eta) + + @property + def percent(self): + return self.progress * 100 + + @property + def progress(self): + return min(1, self.index / self.max) + + @property + def remaining(self): + return max(self.max - self.index, 0) + + def start(self): + self.update() + + def goto(self, index): + incr = index - self.index + self.next(incr) + + def iter(self, it): + try: + self.max = len(it) + except TypeError: + pass + + with self: + for x in it: + yield x + self.next() diff --git a/venv2/lib/python2.7/site-packages/pip/_vendor/progress/bar.py b/venv2/lib/python2.7/site-packages/pip/_vendor/progress/bar.py new file mode 100644 index 0000000..8819efd --- /dev/null +++ b/venv2/lib/python2.7/site-packages/pip/_vendor/progress/bar.py @@ -0,0 +1,91 @@ +# -*- coding: utf-8 -*- + +# Copyright (c) 2012 Giorgos Verigakis <verigak@gmail.com> +# +# Permission to use, copy, modify, and distribute this software for any +# purpose with or without fee is hereby granted, provided that the above +# copyright notice and this permission notice appear in all copies. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +# ANY SPECIAL, DIRECT, 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 unicode_literals + +import sys + +from . import Progress + + +class Bar(Progress): + width = 32 + suffix = '%(index)d/%(max)d' + bar_prefix = ' |' + bar_suffix = '| ' + empty_fill = ' ' + fill = '#' + + def update(self): + filled_length = int(self.width * self.progress) + empty_length = self.width - filled_length + + message = self.message % self + bar = self.fill * filled_length + empty = self.empty_fill * empty_length + suffix = self.suffix % self + line = ''.join([message, self.bar_prefix, bar, empty, self.bar_suffix, + suffix]) + self.writeln(line) + + +class ChargingBar(Bar): + suffix = '%(percent)d%%' + bar_prefix = ' ' + bar_suffix = ' ' + empty_fill = '∙' + fill = '█' + + +class FillingSquaresBar(ChargingBar): + empty_fill = '▢' + fill = '▣' + + +class FillingCirclesBar(ChargingBar): + empty_fill = '◯' + fill = '◉' + + +class IncrementalBar(Bar): + if sys.platform.startswith('win'): + phases = (u' ', u'▌', u'█') + else: + phases = (' ', '▏', '▎', '▍', '▌', '▋', '▊', '▉', '█') + + def update(self): + nphases = len(self.phases) + filled_len = self.width * self.progress + nfull = int(filled_len) # Number of full chars + phase = int((filled_len - nfull) * nphases) # Phase of last char + nempty = self.width - nfull # Number of empty chars + + message = self.message % self + bar = self.phases[-1] * nfull + current = self.phases[phase] if phase > 0 else '' + empty = self.empty_fill * max(0, nempty - len(current)) + suffix = self.suffix % self + line = ''.join([message, self.bar_prefix, bar, current, empty, + self.bar_suffix, suffix]) + self.writeln(line) + + +class PixelBar(IncrementalBar): + phases = ('⡀', '⡄', '⡆', '⡇', '⣇', '⣧', '⣷', '⣿') + + +class ShadyBar(IncrementalBar): + phases = (' ', '░', '▒', '▓', '█') diff --git a/venv2/lib/python2.7/site-packages/pip/_vendor/progress/counter.py b/venv2/lib/python2.7/site-packages/pip/_vendor/progress/counter.py new file mode 100644 index 0000000..d955ca4 --- /dev/null +++ b/venv2/lib/python2.7/site-packages/pip/_vendor/progress/counter.py @@ -0,0 +1,41 @@ +# -*- coding: utf-8 -*- + +# Copyright (c) 2012 Giorgos Verigakis <verigak@gmail.com> +# +# Permission to use, copy, modify, and distribute this software for any +# purpose with or without fee is hereby granted, provided that the above +# copyright notice and this permission notice appear in all copies. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +# ANY SPECIAL, DIRECT, 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 unicode_literals +from . import Infinite, Progress + + +class Counter(Infinite): + def update(self): + self.write(str(self.index)) + + +class Countdown(Progress): + def update(self): + self.write(str(self.remaining)) + + +class Stack(Progress): + phases = (' ', '▁', '▂', '▃', '▄', '▅', '▆', '▇', '█') + + def update(self): + nphases = len(self.phases) + i = min(nphases - 1, int(self.progress * nphases)) + self.write(self.phases[i]) + + +class Pie(Stack): + phases = ('○', '◔', '◑', '◕', '●') diff --git a/venv2/lib/python2.7/site-packages/pip/_vendor/progress/spinner.py b/venv2/lib/python2.7/site-packages/pip/_vendor/progress/spinner.py new file mode 100644 index 0000000..4e100ca --- /dev/null +++ b/venv2/lib/python2.7/site-packages/pip/_vendor/progress/spinner.py @@ -0,0 +1,43 @@ +# -*- coding: utf-8 -*- + +# Copyright (c) 2012 Giorgos Verigakis <verigak@gmail.com> +# +# Permission to use, copy, modify, and distribute this software for any +# purpose with or without fee is hereby granted, provided that the above +# copyright notice and this permission notice appear in all copies. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +# ANY SPECIAL, DIRECT, 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 unicode_literals +from . import Infinite + + +class Spinner(Infinite): + phases = ('-', '\\', '|', '/') + hide_cursor = True + + def update(self): + i = self.index % len(self.phases) + self.write(self.phases[i]) + + +class PieSpinner(Spinner): + phases = ['◷', '◶', '◵', '◴'] + + +class MoonSpinner(Spinner): + phases = ['◑', '◒', '◐', '◓'] + + +class LineSpinner(Spinner): + phases = ['⎺', '⎻', '⎼', '⎽', '⎼', '⎻'] + + +class PixelSpinner(Spinner): + phases = ['⣾', '⣷', '⣯', '⣟', '⡿', '⢿', '⣻', '⣽'] diff --git a/venv2/lib/python2.7/site-packages/pip/_vendor/pyparsing.py b/venv2/lib/python2.7/site-packages/pip/_vendor/pyparsing.py new file mode 100644 index 0000000..9d6a01d --- /dev/null +++ b/venv2/lib/python2.7/site-packages/pip/_vendor/pyparsing.py @@ -0,0 +1,6493 @@ +#-*- coding: utf-8 -*- +# module pyparsing.py +# +# Copyright (c) 2003-2019 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 +``"<salutation>, <addressee>!"``), built up using :class:`Word`, +:class:`Literal`, and :class:`And` elements +(the :class:`'+'<ParserElement.__add__>` operators create :class:`And` expressions, +and the strings are auto-converted to :class:`Literal` expressions):: + + from pip._vendor.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 :class:`ParseResults` object returned from +:class:`ParserElement.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 + + +Getting Started - +----------------- +Visit the classes :class:`ParserElement` and :class:`ParseResults` to +see the base classes that most other pyparsing +classes inherit from. Use the docstrings for examples of how to: + + - construct literal match expressions from :class:`Literal` and + :class:`CaselessLiteral` classes + - construct character word-group expressions using the :class:`Word` + class + - see how to create repetitive expressions using :class:`ZeroOrMore` + and :class:`OneOrMore` classes + - use :class:`'+'<And>`, :class:`'|'<MatchFirst>`, :class:`'^'<Or>`, + and :class:`'&'<Each>` operators to combine simple expressions into + more complex ones + - associate names with your parsed results using + :class:`ParserElement.setResultsName` + - find some helpful expression short-cuts like :class:`delimitedList` + and :class:`oneOf` + - find more useful common expressions in the :class:`pyparsing_common` + namespace class +""" + +__version__ = "2.4.0" +__versionTime__ = "07 Apr 2019 18:28 UTC" +__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 traceback +import types +from datetime import datetime + +try: + # Python 3 + from itertools import filterfalse +except ImportError: + from itertools import ifilterfalse as filterfalse + +try: + from _thread import RLock +except ImportError: + from threading import RLock + +try: + # Python 3 + from collections.abc import Iterable + from collections.abc import MutableMapping +except ImportError: + # Python 2.7 + from collections import Iterable + from collections import MutableMapping + +try: + from collections import OrderedDict as _OrderedDict +except ImportError: + try: + from ordereddict import OrderedDict as _OrderedDict + except ImportError: + _OrderedDict = None + +try: + from types import SimpleNamespace +except ImportError: + class SimpleNamespace: pass + +# version compatibility configuration +__compat__ = SimpleNamespace() +__compat__.__doc__ = """ + A cross-version compatibility configuration for pyparsing features that will be + released in a future version. By setting values in this configuration to True, + those features can be enabled in prior versions for compatibility development + and testing. + + - collect_all_And_tokens - flag to enable fix for Issue #63 that fixes erroneous grouping + of results names when an And expression is nested within an Or or MatchFirst; set to + True to enable bugfix to be released in pyparsing 2.4 +""" +__compat__.collect_all_And_tokens = True + + +#~ sys.stderr.write( "testing pyparsing module, version %s, %s\n" % (__version__,__versionTime__ ) ) + +__all__ = [ '__version__', '__versionTime__', '__author__', '__compat__', +'And', 'CaselessKeyword', 'CaselessLiteral', 'CharsNotIn', 'Combine', 'Dict', 'Each', 'Empty', +'FollowedBy', 'Forward', 'GoToColumn', 'Group', 'Keyword', 'LineEnd', 'LineStart', 'Literal', +'PrecededBy', 'MatchFirst', 'NoMatch', 'NotAny', 'OneOrMore', 'OnlyOnce', 'Optional', 'Or', +'ParseBaseException', 'ParseElementEnhance', 'ParseException', 'ParseExpression', 'ParseFatalException', +'ParseResults', 'ParseSyntaxException', 'ParserElement', 'QuotedString', 'RecursiveGrammarException', +'Regex', 'SkipTo', 'StringEnd', 'StringStart', 'Suppress', 'Token', 'TokenConverter', +'White', 'Word', 'WordEnd', 'WordStart', 'ZeroOrMore', 'Char', +'alphanums', 'alphas', 'alphas8bit', 'anyCloseTag', 'anyOpenTag', 'cStyleComment', 'col', +'commaSeparatedList', 'commonHTMLEntity', 'countedArray', 'cppStyleComment', 'dblQuotedString', +'dblSlashComment', 'delimitedList', 'dictOf', 'downcaseTokens', 'empty', 'hexnums', +'htmlComment', 'javaStyleComment', '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', +'CloseMatch', 'tokenMap', 'pyparsing_common', 'pyparsing_unicode', 'unicode_set', +] + +system_version = tuple(sys.version_info)[:3] +PY_3 = system_version[0] == 3 +if PY_3: + _MAX_INT = sys.maxsize + basestring = str + unichr = chr + unicode = str + _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: + # Else encode it + ret = unicode(obj).encode(sys.getdefaultencoding(), 'xmlcharrefreplace') + xmlcharref = Regex(r'&#\d+;') + xmlcharref.setParseAction(lambda t: '\\u' + hex(int(t[0][2:-1]))[2:]) + return xmlcharref.transformString(ret) + + # 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 + +alphas = string.ascii_uppercase + string.ascii_lowercase +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 + self.args = (pstr, loc, msg) + + @classmethod + def _from_exception(cls, pe): + """ + internal factory method to simplify creating one type of ParseException + from another - avoids having __init__ signature conflicts among subclasses + """ + return cls(pe.pstr, pe.loc, pe.msg, pe.parserElement) + + 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 "lineno col line".split() + dir(type(self)) + +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 + + Example:: + + try: + Word(nums).setName("integer").parseString("ABC") + except ParseException as pe: + print(pe) + print("column: {}".format(pe.col)) + + prints:: + + Expected integer (at char 0), (line:1, col:1) + column: 1 + + """ + + @staticmethod + def explain(exc, depth=16): + """ + Method to take an exception and translate the Python internal traceback into a list + of the pyparsing expressions that caused the exception to be raised. + + Parameters: + + - exc - exception raised during parsing (need not be a ParseException, in support + of Python exceptions that might be raised in a parse action) + - depth (default=16) - number of levels back in the stack trace to list expression + and function names; if None, the full stack trace names will be listed; if 0, only + the failing input line, marker, and exception string will be shown + + Returns a multi-line string listing the ParserElements and/or function names in the + exception's stack trace. + + Note: the diagnostic output will include string representations of the expressions + that failed to parse. These representations will be more helpful if you use `setName` to + give identifiable names to your expressions. Otherwise they will use the default string + forms, which may be cryptic to read. + + explain() is only supported under Python 3. + """ + import inspect + + if depth is None: + depth = sys.getrecursionlimit() + ret = [] + if isinstance(exc, ParseBaseException): + ret.append(exc.line) + ret.append(' ' * (exc.col - 1) + '^') + ret.append("{0}: {1}".format(type(exc).__name__, exc)) + + if depth > 0: + callers = inspect.getinnerframes(exc.__traceback__, context=depth) + seen = set() + for i, ff in enumerate(callers[-depth:]): + frm = ff[0] + + f_self = frm.f_locals.get('self', None) + if isinstance(f_self, ParserElement): + if frm.f_code.co_name not in ('parseImpl', '_parseNoCache'): + continue + if f_self in seen: + continue + seen.add(f_self) + + self_type = type(f_self) + ret.append("{0}.{1} - {2}".format(self_type.__module__, + self_type.__name__, + f_self)) + elif f_self is not None: + self_type = type(f_self) + ret.append("{0}.{1}".format(self_type.__module__, + self_type.__name__)) + else: + code = frm.f_code + if code.co_name in ('wrapper', '<module>'): + continue + + ret.append("{0}".format(code.co_name)) + + depth -= 1 + if not depth: + break + + return '\n'.join(ret) + + +class ParseFatalException(ParseBaseException): + """user-throwable exception thrown when inconsistent parse content + is found; stops all parsing immediately""" + pass + +class ParseSyntaxException(ParseFatalException): + """just like :class:`ParseFatalException`, but thrown internally + when an :class:`ErrorStop<And._ErrorStop>` ('-' operator) indicates + that parsing is to stop immediately because an unbacktrackable + syntax error has been found. + """ + pass + +#~ 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 :class:`ParserElement.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[0]) + 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 (``len(results)``) + - by list index (``results[0], results[1]``, etc.) + - by attribute (``results.<resultsName>`` - see :class:`ParserElement.setResultsName`) + + Example:: + + integer = Word(nums) + date_str = (integer.setResultsName("year") + '/' + + integer.setResultsName("month") + '/' + + integer.setResultsName("day")) + # equivalent form: + # date_str = integer("year") + '/' + integer("month") + '/' + integer("day") + + # parseString returns a ParseResults object + result = date_str.parseString("1999/12/31") + + def test(s, fn=repr): + print("%s -> %s" % (s, fn(eval(s)))) + test("list(result)") + test("result[0]") + test("result['month']") + test("result.day") + test("'month' in result") + test("'minutes' in result") + test("result.dump()", str) + + prints:: + + list(result) -> ['1999', '/', '12', '/', '31'] + result[0] -> '1999' + result['month'] -> '12' + result.day -> '31' + 'month' in result -> True + 'minutes' in result -> False + result.dump() -> ['1999', '/', '12', '/', '31'] + - day: 31 + - month: 12 + - year: 1999 + """ + def __new__(cls, toklist=None, 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=None, name=None, asList=True, modal=True, isinstance=isinstance ): + if self.__doinit: + self.__doinit = False + self.__name = None + self.__parent = None + self.__accumNames = {} + self.__asList = asList + self.__modal = modal + if toklist is None: + toklist = [] + 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(ParseResults(toklist.__toklist), 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,slice)): + 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,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 ( not not self.__toklist ) + __nonzero__ = __bool__ + def __iter__( self ): return iter( self.__toklist ) + def __reversed__( self ): return iter( self.__toklist[::-1] ) + def _iterkeys( self ): + if hasattr(self.__tokdict, "iterkeys"): + return self.__tokdict.iterkeys() + else: + return iter(self.__tokdict) + + def _itervalues( self ): + 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 + """Returns an iterator of all named result keys.""" + + values = _itervalues + """Returns an iterator of all named result values.""" + + items = _iteritems + """Returns an iterator of all named result key-value tuples.""" + + else: + iterkeys = _iterkeys + """Returns an iterator of all named result keys (Python 2.x only).""" + + itervalues = _itervalues + """Returns an iterator of all named result values (Python 2.x only).""" + + iteritems = _iteritems + """Returns an iterator of all named result key-value tuples (Python 2.x only).""" + + def keys( self ): + """Returns all named result keys (as a list in Python 2.x, as an iterator in Python 3.x).""" + return list(self.iterkeys()) + + def values( self ): + """Returns all named result values (as a list in Python 2.x, as an iterator in Python 3.x).""" + return list(self.itervalues()) + + def items( self ): + """Returns all named result key-values (as a list of tuples in Python 2.x, as an iterator in Python 3.x).""" + 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()``. + + Example:: + + def remove_first(tokens): + tokens.pop(0) + print(OneOrMore(Word(nums)).parseString("0 123 321")) # -> ['0', '123', '321'] + print(OneOrMore(Word(nums)).addParseAction(remove_first).parseString("0 123 321")) # -> ['123', '321'] + + label = Word(alphas) + patt = label("LABEL") + OneOrMore(Word(nums)) + print(patt.parseString("AAB 123 321").dump()) + + # Use pop() in a parse action to remove named result (note that corresponding value is not + # removed from list form of results) + def remove_LABEL(tokens): + tokens.pop("LABEL") + return tokens + patt.addParseAction(remove_LABEL) + print(patt.parseString("AAB 123 321").dump()) + + prints:: + + ['AAB', '123', '321'] + - LABEL: AAB + + ['AAB', '123', '321'] + """ + 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 ``defaultValue`` or ``None`` if no + ``defaultValue`` is specified. + + Similar to ``dict.get()``. + + Example:: + + integer = Word(nums) + date_str = integer("year") + '/' + integer("month") + '/' + integer("day") + + result = date_str.parseString("1999/12/31") + print(result.get("year")) # -> '1999' + print(result.get("hour", "not specified")) # -> 'not specified' + print(result.get("hour")) # -> None + """ + 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. + + Similar to ``list.insert()``. + + Example:: + + print(OneOrMore(Word(nums)).parseString("0 123 321")) # -> ['0', '123', '321'] + + # use a parse action to insert the parse location in the front of the parsed results + def insert_locn(locn, tokens): + tokens.insert(0, locn) + print(OneOrMore(Word(nums)).addParseAction(insert_locn).parseString("0 123 321")) # -> [0, '0', '123', '321'] + """ + self.__toklist.insert(index, insStr) + # fixup indices in token dictionary + 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. + + Example:: + + print(OneOrMore(Word(nums)).parseString("0 123 321")) # -> ['0', '123', '321'] + + # use a parse action to compute the sum of the parsed integers, and add it to the end + def append_sum(tokens): + tokens.append(sum(map(int, tokens))) + print(OneOrMore(Word(nums)).addParseAction(append_sum).parseString("0 123 321")) # -> ['0', '123', '321', 444] + """ + self.__toklist.append(item) + + def extend( self, itemseq ): + """ + Add sequence of elements to end of ParseResults list of elements. + + Example:: + + patt = OneOrMore(Word(alphas)) + + # use a parse action to append the reverse of the matched strings, to make a palindrome + def make_palindrome(tokens): + tokens.extend(reversed([t[::-1] for t in tokens])) + return ''.join(tokens) + print(patt.addParseAction(make_palindrome).parseString("lskdj sdlkjf lksd")) # -> 'lskdjsdlkjflksddsklfjkldsjdksl' + """ + if isinstance(itemseq, ParseResults): + self.__iadd__(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: + # useful for merging many ParseResults using sum() builtin + return self.copy() + else: + # this may raise a TypeError - so be it + return other + self + + 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. + + Example:: + + patt = OneOrMore(Word(alphas)) + result = patt.parseString("sldkj lsdkj sldkj") + # even though the result prints in string-like form, it is actually a pyparsing ParseResults + print(type(result), result) # -> <class 'pyparsing.ParseResults'> ['sldkj', 'lsdkj', 'sldkj'] + + # Use asList() to create an actual list + result_list = result.asList() + print(type(result_list), result_list) # -> <class 'list'> ['sldkj', 'lsdkj', 'sldkj'] + """ + return [res.asList() if isinstance(res,ParseResults) else res for res in self.__toklist] + + def asDict( self ): + """ + Returns the named parse results as a nested dictionary. + + Example:: + + integer = Word(nums) + date_str = integer("year") + '/' + integer("month") + '/' + integer("day") + + result = date_str.parseString('12/31/1999') + print(type(result), repr(result)) # -> <class 'pyparsing.ParseResults'> (['12', '/', '31', '/', '1999'], {'day': [('1999', 4)], 'year': [('12', 0)], 'month': [('31', 2)]}) + + result_dict = result.asDict() + print(type(result_dict), repr(result_dict)) # -> <class 'dict'> {'day': '1999', 'year': '12', 'month': '31'} + + # even though a ParseResults supports dict-like access, sometime you just need to have a dict + import json + print(json.dumps(result)) # -> Exception: TypeError: ... is not JSON serializable + print(json.dumps(result.asDict())) # -> {"month": "31", "day": "1999", "year": "12"} + """ + if PY_3: + item_fn = self.items + else: + item_fn = self.iteritems + + def toItem(obj): + if isinstance(obj, ParseResults): + if obj.haskeys(): + return obj.asDict() + else: + return [toItem(v) for v in obj] + else: + return obj + + return dict((k,toItem(v)) for k,v in item_fn()) + + def copy( self ): + """ + Returns a new copy of a :class:`ParseResults` object. + """ + ret = ParseResults( self.__toklist ) + ret.__tokdict = dict(self.__tokdict.items()) + 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 ): + """ + (Deprecated) 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): + r""" + Returns the results name for this token expression. Useful when several + different expressions might match at a particular location. + + Example:: + + integer = Word(nums) + ssn_expr = Regex(r"\d\d\d-\d\d-\d\d\d\d") + house_number_expr = Suppress('#') + Word(nums, alphanums) + user_data = (Group(house_number_expr)("house_number") + | Group(ssn_expr)("ssn") + | Group(integer)("age")) + user_info = OneOrMore(user_data) + + result = user_info.parseString("22 111-22-3333 #221B") + for item in result: + print(item.getName(), ':', item[0]) + + prints:: + + age : 22 + ssn : 111-22-3333 + house_number : 221B + """ + 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 + next(iter(self.__tokdict.values()))[0][1] in (0,-1)): + return next(iter(self.__tokdict.keys())) + else: + return None + + def dump(self, indent='', depth=0, full=True): + """ + Diagnostic method for listing out the contents of + a :class:`ParseResults`. Accepts an optional ``indent`` argument so + that this string can be embedded in a nested display of other data. + + Example:: + + integer = Word(nums) + date_str = integer("year") + '/' + integer("month") + '/' + integer("day") + + result = date_str.parseString('12/31/1999') + print(result.dump()) + + prints:: + + ['12', '/', '31', '/', '1999'] + - day: 1999 + - month: 31 + - year: 12 + """ + out = [] + NL = '\n' + out.append( indent+_ustr(self.asList()) ) + if full: + if self.haskeys(): + items = sorted((str(k), v) for k,v in 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(repr(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 + `pprint <https://docs.python.org/3/library/pprint.html>`_ module. + Accepts additional positional or keyword args as defined for + `pprint.pprint <https://docs.python.org/3/library/pprint.html#pprint.pprint>`_ . + + Example:: + + ident = Word(alphas, alphanums) + num = Word(nums) + func = Forward() + term = ident | num | Group('(' + func + ')') + func <<= ident + Group(Optional(delimitedList(term))) + result = func.parseString("fna a,b,(fnb c,d,200),100") + result.pprint(width=40) + + prints:: + + ['fna', + ['a', + 'b', + ['(', 'fnb', ['c', 'd', '200'], ')'], + '100']] + """ + 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 __getnewargs__(self): + return self.__toklist, self.__name, self.__asList, self.__modal + + def __dir__(self): + return (dir(type(self)) + list(self.keys())) + +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 + :class:`ParserElement.parseString` for more + information on parsing strings containing ``<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 0<loc<len(s) and s[loc-1] == '\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 :class:`ParserElement.parseString` + for more information on parsing strings containing ``<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] + + # traceback return data structure changed in Py3.5 - normalize back to plain tuples + if system_version[:2] >= (3,5): + def extract_stack(limit=0): + # special handling for Python 3.5.0 - extra deep call stack by 1 + offset = -3 if system_version == (3,5,0) else -2 + frame_summary = traceback.extract_stack(limit=-offset+limit-1)[offset] + return [frame_summary[:2]] + def extract_tb(tb, limit=0): + frames = traceback.extract_tb(tb, limit=limit) + frame_summary = frames[-1] + return [frame_summary[:2]] + else: + extract_stack = traceback.extract_stack + extract_tb = traceback.extract_tb + + # synthesize what would be returned by traceback.extract_stack at the call to + # user's parse action 'func', so that we don't incur call penalty at parse time + + LINE_DIFF = 6 + # IF ANY CODE CHANGES, EVEN JUST COMMENTS OR BLANK LINES, BETWEEN THE NEXT LINE AND + # THE CALL TO FUNC INSIDE WRAPPER, LINE_DIFF MUST BE MODIFIED!!!! + this_line = extract_stack(limit=2)[-1] + pa_call_line_synth = (this_line[0], this_line[1]+LINE_DIFF) + + def wrapper(*args): + while 1: + try: + ret = func(*args[limit[0]:]) + foundArity[0] = True + return ret + except TypeError: + # re-raise TypeErrors if they did not come from our arity testing + if foundArity[0]: + raise + else: + try: + tb = sys.exc_info()[-1] + if not extract_tb(tb, limit=2)[-1][:2] == pa_call_line_synth: + raise + finally: + del tb + + if limit[0] <= maxargs: + limit[0] += 1 + continue + raise + + # copy func name to wrapper for sensible debug output + func_name = "<parse action>" + try: + func_name = getattr(func, '__name__', + getattr(func, '__class__').__name__) + except Exception: + func_name = str(func) + wrapper.__name__ = func_name + + return wrapper + +class ParserElement(object): + """Abstract base level parser element class.""" + DEFAULT_WHITE_CHARS = " \n\t\r" + verbose_stacktrace = False + + @staticmethod + def setDefaultWhitespaceChars( chars ): + r""" + Overrides the default whitespace chars + + Example:: + + # default whitespace chars are space, <TAB> and newline + OneOrMore(Word(alphas)).parseString("abc def\nghi jkl") # -> ['abc', 'def', 'ghi', 'jkl'] + + # change to just treat newline as significant + ParserElement.setDefaultWhitespaceChars(" \t") + OneOrMore(Word(alphas)).parseString("abc def\nghi jkl") # -> ['abc', 'def'] + """ + ParserElement.DEFAULT_WHITE_CHARS = chars + + @staticmethod + def inlineLiteralsUsing(cls): + """ + Set class to be used for inclusion of string literals into a parser. + + Example:: + + # default literal class used is Literal + integer = Word(nums) + date_str = integer("year") + '/' + integer("month") + '/' + integer("day") + + date_str.parseString("1999/12/31") # -> ['1999', '/', '12', '/', '31'] + + + # change to Suppress + ParserElement.inlineLiteralsUsing(Suppress) + date_str = integer("year") + '/' + integer("month") + '/' + integer("day") + + date_str.parseString("1999/12/31") # -> ['1999', '12', '31'] + """ + 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 = set(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 :class:`ParserElement`. Useful for defining + different parse actions for the same parsing pattern, using copies of + the original parse element. + + Example:: + + integer = Word(nums).setParseAction(lambda toks: int(toks[0])) + integerK = integer.copy().addParseAction(lambda toks: toks[0]*1024) + Suppress("K") + integerM = integer.copy().addParseAction(lambda toks: toks[0]*1024*1024) + Suppress("M") + + print(OneOrMore(integerK | integerM | integer).parseString("5K 100 640K 256M")) + + prints:: + + [5120, 100, 655360, 268435456] + + Equivalent form of ``expr.copy()`` is just ``expr()``:: + + integerM = integer().addParseAction(lambda toks: toks[0]*1024*1024) + Suppress("M") + """ + 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, makes debugging and exception messages clearer. + + Example:: + + Word(nums).parseString("ABC") # -> Exception: Expected W:(0123...) (at char 0), (line:1, col:1) + Word(nums).setName("integer").parseString("ABC") # -> Exception: Expected integer (at char 0), (line:1, col:1) + """ + 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 :class:`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, + ``expr("name")`` in place of ``expr.setResultsName("name")`` + - see :class:`__call__`. + + Example:: + + date_str = (integer.setResultsName("year") + '/' + + integer.setResultsName("month") + '/' + + integer.setResultsName("day")) + + # equivalent form: + date_str = integer("year") + '/' + integer("month") + '/' + integer("day") + """ + 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 ``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 one or more actions to perform when successfully matching parse element definition. + Parse action fn is a callable method with 0-3 arguments, called as ``fn(s,loc,toks)`` , + ``fn(loc,toks)`` , ``fn(toks)`` , or just ``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 :class:`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. + + Optional keyword arguments: + - callDuringTry = (default= ``False`` ) indicate if parse action should be run during lookaheads and alternate testing + + Note: the default parsing behavior is to expand tabs in the input string + before starting the parsing process. See :class:`parseString for more + information on parsing strings containing ``<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. + + Example:: + + integer = Word(nums) + date_str = integer + '/' + integer + '/' + integer + + date_str.parseString("1999/12/31") # -> ['1999', '/', '12', '/', '31'] + + # use parse action to convert to ints at parse time + integer = Word(nums).setParseAction(lambda toks: int(toks[0])) + date_str = integer + '/' + integer + '/' + integer + + # note that integer fields are now ints, not strings + date_str.parseString("1999/12/31") # -> [1999, '/', 12, '/', 31] + """ + self.parseAction = list(map(_trim_arity, list(fns))) + self.callDuringTry = kwargs.get("callDuringTry", False) + return self + + def addParseAction( self, *fns, **kwargs ): + """ + Add one or more parse actions to expression's list of parse actions. See :class:`setParseAction`. + + See examples in :class:`copy`. + """ + 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 + :class:`setParseAction` for function call signatures. Unlike ``setParseAction``, + functions passed to ``addCondition`` need to return boolean success/fail of the condition. + + Optional keyword arguments: + - message = define a custom message to be used in the raised exception + - fatal = if True, will raise ParseFatalException to stop parsing immediately; otherwise will raise ParseException + + Example:: + + integer = Word(nums).setParseAction(lambda toks: int(toks[0])) + year_int = integer.copy() + year_int.addCondition(lambda toks: toks[0] >= 2000, message="Only support years 2000 and later") + date_str = year_int + '/' + integer + '/' + integer + + result = date_str.parseString("1999/12/31") # -> Exception: Only support years 2000 and later (at char 0), (line:1, col:1) + """ + msg = kwargs.get("message", "failed user-defined condition") + exc_type = ParseFatalException if kwargs.get("fatal", False) else ParseException + for fn in fns: + fn = _trim_arity(fn) + def pa(s,l,t): + if not bool(fn(s,l,t)): + raise exc_type(s,l,msg) + 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 + ``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 :class:`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 preloc >= 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: + try: + tokens = fn( instring, tokensStart, retTokens ) + except IndexError as parse_action_exc: + exc = ParseException("exception raised in parse action") + exc.__cause__ = parse_action_exc + raise exc + + if tokens is not None and tokens is not retTokens: + 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: + try: + tokens = fn( instring, tokensStart, retTokens ) + except IndexError as parse_action_exc: + exc = ParseException("exception raised in parse action") + exc.__cause__ = parse_action_exc + raise exc + + if tokens is not None and tokens is not retTokens: + 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) + + def canParseNext(self, instring, loc): + try: + self.tryParse(instring, loc) + except (ParseException, IndexError): + return False + else: + return True + + class _UnboundedCache(object): + def __init__(self): + cache = {} + self.not_in_cache = not_in_cache = object() + + def get(self, key): + return cache.get(key, not_in_cache) + + def set(self, key, value): + cache[key] = value + + def clear(self): + cache.clear() + + def cache_len(self): + return len(cache) + + self.get = types.MethodType(get, self) + self.set = types.MethodType(set, self) + self.clear = types.MethodType(clear, self) + self.__len__ = types.MethodType(cache_len, self) + + if _OrderedDict is not None: + class _FifoCache(object): + def __init__(self, size): + self.not_in_cache = not_in_cache = object() + + cache = _OrderedDict() + + def get(self, key): + return cache.get(key, not_in_cache) + + def set(self, key, value): + cache[key] = value + while len(cache) > size: + try: + cache.popitem(False) + except KeyError: + pass + + def clear(self): + cache.clear() + + def cache_len(self): + return len(cache) + + self.get = types.MethodType(get, self) + self.set = types.MethodType(set, self) + self.clear = types.MethodType(clear, self) + self.__len__ = types.MethodType(cache_len, self) + + else: + class _FifoCache(object): + def __init__(self, size): + self.not_in_cache = not_in_cache = object() + + cache = {} + key_fifo = collections.deque([], size) + + def get(self, key): + return cache.get(key, not_in_cache) + + def set(self, key, value): + cache[key] = value + while len(key_fifo) > size: + cache.pop(key_fifo.popleft(), None) + key_fifo.append(key) + + def clear(self): + cache.clear() + key_fifo.clear() + + def cache_len(self): + return len(cache) + + self.get = types.MethodType(get, self) + self.set = types.MethodType(set, self) + self.clear = types.MethodType(clear, self) + self.__len__ = types.MethodType(cache_len, self) + + # argument cache for optimizing repeated calls when backtracking through recursive expressions + packrat_cache = {} # this is set later by enabledPackrat(); this is here so that resetCache() doesn't fail + packrat_cache_lock = RLock() + packrat_cache_stats = [0, 0] + + # 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 ): + HIT, MISS = 0, 1 + lookup = (self, instring, loc, callPreParse, doActions) + with ParserElement.packrat_cache_lock: + cache = ParserElement.packrat_cache + value = cache.get(lookup) + if value is cache.not_in_cache: + ParserElement.packrat_cache_stats[MISS] += 1 + try: + value = self._parseNoCache(instring, loc, doActions, callPreParse) + except ParseBaseException as pe: + # cache a copy of the exception, without the traceback + cache.set(lookup, pe.__class__(*pe.args)) + raise + else: + cache.set(lookup, (value[0], value[1].copy())) + return value + else: + ParserElement.packrat_cache_stats[HIT] += 1 + if isinstance(value, Exception): + raise value + return (value[0], value[1].copy()) + + _parse = _parseNoCache + + @staticmethod + def resetCache(): + ParserElement.packrat_cache.clear() + ParserElement.packrat_cache_stats[:] = [0] * len(ParserElement.packrat_cache_stats) + + _packratEnabled = False + @staticmethod + def enablePackrat(cache_size_limit=128): + """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. + + Parameters: + + - cache_size_limit - (default= ``128``) - if an integer value is provided + will limit the size of the packrat cache; if None is passed, then + the cache size will be unbounded; if 0 is passed, the cache will + be effectively disabled. + + 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 :class:`ParserElement.enablePackrat`. + For best results, call ``enablePackrat()`` immediately after + importing pyparsing. + + Example:: + + from pip._vendor import pyparsing + pyparsing.ParserElement.enablePackrat() + """ + if not ParserElement._packratEnabled: + ParserElement._packratEnabled = True + if cache_size_limit is None: + ParserElement.packrat_cache = ParserElement._UnboundedCache() + else: + ParserElement.packrat_cache = ParserElement._FifoCache(cache_size_limit) + 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 ``parseAll`` to True (equivalent to ending + the grammar with ``StringEnd()``). + + Note: ``parseString`` implicitly calls ``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 ``loc`` argument to index into the + string being parsed, you can ensure you have a consistent view of the input + string by: + + - calling ``parseWithTabs`` on your grammar before calling ``parseString`` + (see :class:`parseWithTabs`) + - define your parse action using the full ``(s,loc,toks)`` signature, and + reference the input string using the parse action's ``s`` argument + - explictly expand the tabs in your input string before calling + ``parseString`` + + Example:: + + Word('a').parseString('aaaaabaaa') # -> ['aaaaa'] + Word('a').parseString('aaaaabaaa', parseAll=True) # -> Exception: Expected end of text + """ + 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 + ``maxMatches`` argument, to clip scanning after 'n' matches are found. If + ``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 :class:`parseString` for more information on parsing + strings with embedded tabs. + + Example:: + + source = "sldjf123lsdjjkf345sldkjf879lkjsfd987" + print(source) + for tokens,start,end in Word(alphas).scanString(source): + print(' '*start + '^'*(end-start)) + print(' '*start + tokens[0]) + + prints:: + + sldjf123lsdjjkf345sldkjf879lkjsfd987 + ^^^^^ + sldjf + ^^^^^^^ + lsdjjkf + ^^^^^^ + sldkjf + ^^^^^^ + lkjsfd + """ + 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 :class:`scanString`, to modify matching text with modified tokens that may + be returned from a parse action. To use ``transformString``, define a grammar and + attach a parse action to it that modifies the returned token list. + Invoking ``transformString()`` on a target string will then scan for matches, + and replace the matched text patterns according to the logic in the parse + action. ``transformString()`` returns the resulting transformed string. + + Example:: + + wd = Word(alphas) + wd.setParseAction(lambda toks: toks[0].title()) + + print(wd.transformString("now is the winter of our discontent made glorious summer by this sun of york.")) + + prints:: + + Now Is The Winter Of Our Discontent Made Glorious Summer By This Sun Of York. + """ + 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 :class:`scanString`, simplifying the access to the tokens found + to match the given parse expression. May be called with optional + ``maxMatches`` argument, to clip searching after 'n' matches are found. + + Example:: + + # a capitalized word starts with an uppercase letter, followed by zero or more lowercase letters + cap_word = Word(alphas.upper(), alphas.lower()) + + print(cap_word.searchString("More than Iron, more than Lead, more than Gold I need Electricity")) + + # the sum() builtin can be used to merge results into a single ParseResults object + print(sum(cap_word.searchString("More than Iron, more than Lead, more than Gold I need Electricity"))) + + prints:: + + [['More'], ['Iron'], ['Lead'], ['Gold'], ['I'], ['Electricity']] + ['More', 'Iron', 'Lead', 'Gold', 'I', 'Electricity'] + """ + 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 split(self, instring, maxsplit=_MAX_INT, includeSeparators=False): + """ + Generator method to split a string using the given expression as a separator. + May be called with optional ``maxsplit`` argument, to limit the number of splits; + and the optional ``includeSeparators`` argument (default= ``False``), if the separating + matching text should be included in the split results. + + Example:: + + punc = oneOf(list(".,;:/-!?")) + print(list(punc.split("This, this?, this sentence, is badly punctuated!"))) + + prints:: + + ['This', ' this', '', ' this sentence', ' is badly punctuated', ''] + """ + splits = 0 + last = 0 + for t,s,e in self.scanString(instring, maxMatches=maxsplit): + yield instring[last:s] + if includeSeparators: + yield t[0] + last = e + yield instring[last:] + + def __add__(self, other ): + """ + Implementation of + operator - returns :class:`And`. Adding strings to a ParserElement + converts them to :class:`Literal`s by default. + + Example:: + + greet = Word(alphas) + "," + Word(alphas) + "!" + hello = "Hello, World!" + print (hello, "->", greet.parseString(hello)) + + prints:: + + Hello, World! -> ['Hello', ',', 'World', '!'] + """ + 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 :class:`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 :class:`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 self + And._ErrorStop() + other + + def __rsub__(self, other ): + """ + Implementation of - operator when left operand is not a :class:`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 ``expr * 3`` in place of + ``expr + expr + expr``. Expressions may also me multiplied by a 2-integer + tuple, similar to ``{min,max}`` multipliers in regular expressions. Tuples + may also include ``None`` as in: + - ``expr*(n,None)`` or ``expr*(n,)`` is equivalent + to ``expr*n + ZeroOrMore(expr)`` + (read as "at least n instances of ``expr``") + - ``expr*(None,n)`` is equivalent to ``expr*(0,n)`` + (read as "0 to n instances of ``expr``") + - ``expr*(None,None)`` is equivalent to ``ZeroOrMore(expr)`` + - ``expr*(1,None)`` is equivalent to ``OneOrMore(expr)`` + + Note that ``expr*(None,n)`` does not raise an exception if + more than n exprs exist in the input stream; that is, + ``expr*(None,n)`` does not enforce a maximum number of expr + occurrences. If this behavior is desired, then write + ``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 :class:`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 :class:`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 :class:`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 :class:`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 :class:`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 :class:`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 :class:`NotAny` + """ + return NotAny( self ) + + def __call__(self, name=None): + """ + Shortcut for :class:`setResultsName`, with ``listAllMatches=False``. + + If ``name`` is given with a trailing ``'*'`` character, then ``listAllMatches`` will be + passed as ``True``. + + If ``name` is omitted, same as calling :class:`copy`. + + Example:: + + # these are equivalent + userdata = Word(alphas).setResultsName("name") + Word(nums+"-").setResultsName("socsecno") + userdata = Word(alphas)("name") + Word(nums+"-")("socsecno") + """ + if name is not None: + return self.setResultsName(name) + else: + return self.copy() + + def suppress( self ): + """ + Suppresses the output of this :class:`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 + :class:`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 ``<TAB>``s to spaces before parsing the input string. + Must be called before ``parseString`` when the input grammar contains elements that + match ``<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. + + Example:: + + patt = OneOrMore(Word(alphas)) + patt.parseString('ablaj /* comment */ lskjd') # -> ['ablaj'] + + patt.ignore(cStyleComment) + patt.parseString('ablaj /* comment */ lskjd') # -> ['ablaj', 'lskjd'] + """ + if isinstance(other, basestring): + other = Suppress(other) + + if isinstance( other, Suppress ): + if other not in self.ignoreExprs: + self.ignoreExprs.append(other) + 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 ``flag`` to True to enable, False to disable. + + Example:: + + wd = Word(alphas).setName("alphaword") + integer = Word(nums).setName("numword") + term = wd | integer + + # turn on debugging for wd + wd.setDebug() + + OneOrMore(term).parseString("abc 123 xyz 890") + + prints:: + + Match alphaword at loc 0(1,1) + Matched alphaword -> ['abc'] + Match alphaword at loc 3(1,4) + Exception raised:Expected alphaword (at char 4), (line:1, col:5) + Match alphaword at loc 7(1,8) + Matched alphaword -> ['xyz'] + Match alphaword at loc 11(1,12) + Exception raised:Expected alphaword (at char 12), (line:1, col:13) + Match alphaword at loc 15(1,16) + Exception raised:Expected alphaword (at char 15), (line:1, col:16) + + The output shown is that produced by the default debug actions - custom debug actions can be + specified using :class:`setDebugActions`. Prior to attempting + to match the ``wd`` expression, the debugging message ``"Match <exprname> at loc <n>(<line>,<col>)"`` + is shown. Then if the parse succeeds, a ``"Matched"`` message is shown, or an ``"Exception raised"`` + message is shown. Also note the use of :class:`setName` to assign a human-readable name to the expression, + which makes debugging and exception messages easier to understand - for instance, the default + name created for the :class:`Word` expression without calling ``setName`` is ``"W:(ABCD...)"``. + """ + 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: + with open(file_or_filename, "r") as f: + file_contents = f.read() + 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 vars(self) == vars(other) + elif isinstance(other, basestring): + return self.matches(other) + 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 matches(self, testString, parseAll=True): + """ + Method for quick testing of a parser against a test string. Good for simple + inline microtests of sub expressions while building up larger parser. + + Parameters: + - testString - to test against this expression for a match + - parseAll - (default= ``True``) - flag to pass to :class:`parseString` when running tests + + Example:: + + expr = Word(nums) + assert expr.matches("100") + """ + try: + self.parseString(_ustr(testString), parseAll=parseAll) + return True + except ParseBaseException: + return False + + def runTests(self, tests, parseAll=True, comment='#', + fullDump=True, printResults=True, failureTests=False, postParse=None): + """ + 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= ``True``) - flag to pass to :class:`parseString` when running tests + - comment - (default= ``'#'``) - expression for indicating embedded comments in the test + string; pass None to disable comment filtering + - fullDump - (default= ``True``) - dump results as list followed by results names in nested outline; + if False, only dump nested list + - printResults - (default= ``True``) prints test output to stdout + - failureTests - (default= ``False``) indicates if these tests are expected to fail parsing + - postParse - (default= ``None``) optional callback for successful parse results; called as + `fn(test_string, parse_results)` and returns a string to be added to the test output + + Returns: a (success, results) tuple, where success indicates that all tests succeeded + (or failed if ``failureTests`` is True), and the results contain a list of lines of each + test's output + + Example:: + + number_expr = pyparsing_common.number.copy() + + result = number_expr.runTests(''' + # unsigned integer + 100 + # negative integer + -100 + # float with scientific notation + 6.02e23 + # integer with scientific notation + 1e-12 + ''') + print("Success" if result[0] else "Failed!") + + result = number_expr.runTests(''' + # stray character + 100Z + # missing leading digit before '.' + -.100 + # too many '.' + 3.14.159 + ''', failureTests=True) + print("Success" if result[0] else "Failed!") + + prints:: + + # unsigned integer + 100 + [100] + + # negative integer + -100 + [-100] + + # float with scientific notation + 6.02e23 + [6.02e+23] + + # integer with scientific notation + 1e-12 + [1e-12] + + Success + + # stray character + 100Z + ^ + FAIL: Expected end of text (at char 3), (line:1, col:4) + + # missing leading digit before '.' + -.100 + ^ + FAIL: Expected {real number with scientific notation | real number | signed integer} (at char 0), (line:1, col:1) + + # too many '.' + 3.14.159 + ^ + FAIL: Expected end of text (at char 4), (line:1, col:5) + + Success + + Each test string must be on a single line. If you want to test a string that spans multiple + lines, create a test like this:: + + expr.runTest(r"this is a test\\n of strings that spans \\n 3 lines") + + (Note that this is a raw string literal, you must include the leading 'r'.) + """ + if isinstance(tests, basestring): + tests = list(map(str.strip, tests.rstrip().splitlines())) + if isinstance(comment, basestring): + comment = Literal(comment) + allResults = [] + comments = [] + success = True + for t in tests: + if comment is not None and comment.matches(t, False) or comments and not t: + comments.append(t) + continue + if not t: + continue + out = ['\n'.join(comments), t] + comments = [] + try: + # convert newline marks to actual newlines, and strip leading BOM if present + NL = Literal(r'\n').addParseAction(replaceWith('\n')).ignore(quotedString) + BOM = '\ufeff' + t = NL.transformString(t.lstrip(BOM)) + result = self.parseString(t, parseAll=parseAll) + out.append(result.dump(full=fullDump)) + success = success and not failureTests + if postParse is not None: + try: + pp_value = postParse(t, result) + if pp_value is not None: + out.append(str(pp_value)) + except Exception as e: + out.append("{0} failed: {1}: {2}".format(postParse.__name__, type(e).__name__, e)) + except ParseBaseException as pe: + fatal = "(FATAL)" if isinstance(pe, ParseFatalException) else "" + if '\n' in t: + out.append(line(pe.loc, t)) + out.append(' '*(col(pe.loc,t)-1) + '^' + fatal) + else: + out.append(' '*pe.loc + '^' + fatal) + out.append("FAIL: " + str(pe)) + success = success and failureTests + result = pe + except Exception as exc: + out.append("FAIL-EXCEPTION: " + str(exc)) + success = success and failureTests + result = exc + + if printResults: + if fullDump: + out.append('') + print('\n'.join(out)) + + allResults.append((t, result)) + + return success, allResults + + +class Token(ParserElement): + """Abstract :class:`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. + + Example:: + + Literal('blah').parseString('blah') # -> ['blah'] + Literal('blah').parseString('blahfooblah') # -> ['blah'] + Literal('blah').parseString('bla') # -> Exception: Expected "blah" + + For case-insensitive matching, use :class:`CaselessLiteral`. + + For keyword matching (force word break before and after the matched string), + use :class:`Keyword` or :class:`CaselessKeyword`. + """ + 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 :class:`Literal`: + + - ``Literal("if")`` will match the leading ``'if'`` in + ``'ifAndOnlyIf'``. + - ``Keyword("if")`` will not; it will only match the leading + ``'if'`` in ``'if x=1'``, or ``'if(y==2)'`` + + Accepts two optional constructor arguments in addition to the + keyword string: + + - ``identChars`` is a string of characters that would be valid + identifier characters, defaulting to all alphanumerics + "_" and + "$" + - ``caseless`` allows case-insensitive matching, default is ``False``. + + Example:: + + Keyword("start").parseString("start") # -> ['start'] + Keyword("start").parseString("starting") # -> Exception + + For case-insensitive matching, use :class:`CaselessKeyword`. + """ + DEFAULT_KEYWORD_CHARS = alphanums+"_$" + + def __init__( self, matchString, identChars=None, caseless=False ): + super(Keyword,self).__init__() + if identChars is None: + identChars = Keyword.DEFAULT_KEYWORD_CHARS + 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. + + Example:: + + OneOrMore(CaselessLiteral("CMD")).parseString("cmd CMD Cmd10") # -> ['CMD', 'CMD', 'CMD'] + + (Contrast with example for :class:`CaselessKeyword`.) + """ + 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): + """ + Caseless version of :class:`Keyword`. + + Example:: + + OneOrMore(CaselessKeyword("CMD")).parseString("cmd CMD Cmd10") # -> ['CMD', 'CMD'] + + (Contrast with example for :class:`CaselessLiteral`.) + """ + def __init__( self, matchString, identChars=None ): + super(CaselessKeyword,self).__init__( matchString, identChars, caseless=True ) + +class CloseMatch(Token): + """A variation on :class:`Literal` which matches "close" matches, + that is, strings with at most 'n' mismatching characters. + :class:`CloseMatch` takes parameters: + + - ``match_string`` - string to be matched + - ``maxMismatches`` - (``default=1``) maximum number of + mismatches allowed to count as a match + + The results from a successful parse will contain the matched text + from the input string and the following named results: + + - ``mismatches`` - a list of the positions within the + match_string where mismatches were found + - ``original`` - the original match_string used to compare + against the input string + + If ``mismatches`` is an empty list, then the match was an exact + match. + + Example:: + + patt = CloseMatch("ATCATCGAATGGA") + patt.parseString("ATCATCGAAXGGA") # -> (['ATCATCGAAXGGA'], {'mismatches': [[9]], 'original': ['ATCATCGAATGGA']}) + patt.parseString("ATCAXCGAAXGGA") # -> Exception: Expected 'ATCATCGAATGGA' (with up to 1 mismatches) (at char 0), (line:1, col:1) + + # exact match + patt.parseString("ATCATCGAATGGA") # -> (['ATCATCGAATGGA'], {'mismatches': [[]], 'original': ['ATCATCGAATGGA']}) + + # close match allowing up to 2 mismatches + patt = CloseMatch("ATCATCGAATGGA", maxMismatches=2) + patt.parseString("ATCAXCGAAXGGA") # -> (['ATCAXCGAAXGGA'], {'mismatches': [[4, 9]], 'original': ['ATCATCGAATGGA']}) + """ + def __init__(self, match_string, maxMismatches=1): + super(CloseMatch,self).__init__() + self.name = match_string + self.match_string = match_string + self.maxMismatches = maxMismatches + self.errmsg = "Expected %r (with up to %d mismatches)" % (self.match_string, self.maxMismatches) + self.mayIndexError = False + self.mayReturnEmpty = False + + def parseImpl( self, instring, loc, doActions=True ): + start = loc + instrlen = len(instring) + maxloc = start + len(self.match_string) + + if maxloc <= instrlen: + match_string = self.match_string + match_stringloc = 0 + mismatches = [] + maxMismatches = self.maxMismatches + + for match_stringloc,s_m in enumerate(zip(instring[loc:maxloc], self.match_string)): + src,mat = s_m + if src != mat: + mismatches.append(match_stringloc) + if len(mismatches) > maxMismatches: + break + else: + loc = match_stringloc + 1 + results = ParseResults([instring[start:loc]]) + results['original'] = self.match_string + results['mismatches'] = mismatches + return loc, results + + 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 ``min`` is + 1 (a minimum value < 1 is not valid); the default values for + ``max`` and ``exact`` are 0, meaning no maximum or exact + length restriction. An optional ``excludeChars`` parameter can + list characters that might be found in the input ``bodyChars`` + string; useful to define a word of all printables except for one or + two characters, for instance. + + :class:`srange` is useful for defining custom character set strings + for defining ``Word`` expressions, using range notation from + regular expression character sets. + + A common mistake is to use :class:`Word` to match a specific literal + string, as in ``Word("Address")``. Remember that :class:`Word` + uses the string argument to define *sets* of matchable characters. + This expression would match "Add", "AAA", "dAred", or any other word + made up of the characters 'A', 'd', 'r', 'e', and 's'. To match an + exact literal string, use :class:`Literal` or :class:`Keyword`. + + pyparsing includes helper strings for building Words: + + - :class:`alphas` + - :class:`nums` + - :class:`alphanums` + - :class:`hexnums` + - :class:`alphas8bit` (alphabetic characters in ASCII range 128-255 + - accented, tilded, umlauted, etc.) + - :class:`punc8bit` (non-alphabetic characters in ASCII range + 128-255 - currency, symbols, superscripts, diacriticals, etc.) + - :class:`printables` (any non-whitespace character) + + Example:: + + # a word composed of digits + integer = Word(nums) # equivalent to Word("0123456789") or Word(srange("0-9")) + + # a word with a leading capital, and zero or more lowercase + capital_word = Word(alphas.upper(), alphas.lower()) + + # hostnames are alphanumeric, with leading alpha, and '-' + hostname = Word(alphas, alphanums+'-') + + # roman numeral (not a strict parser, accepts invalid mix of characters) + roman = Word("IVXLCDM") + + # any string of non-whitespace characters, except for ',' + csv_value = Word(printables, excludeChars=",") + """ + def __init__( self, initChars, bodyChars=None, min=1, max=0, exact=0, asKeyword=False, excludeChars=None ): + super(Word,self).__init__() + if excludeChars: + excludeChars = set(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 Exception: + 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 instring[loc] not 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 + elif self.maxSpecified and loc < instrlen and instring[loc] in bodychars: + throwException = True + elif 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 Exception: + 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 Char(Word): + """A short-cut class for defining ``Word(characters, exact=1)``, + when defining a match of any single character in a string of + characters. + """ + def __init__(self, charset, asKeyword=False, excludeChars=None): + super(Char, self).__init__(charset, exact=1, asKeyword=asKeyword, excludeChars=excludeChars) + self.reString = "[%s]" % _escapeRegexRangeChars(self.initCharsOrig) + self.re = re.compile( self.reString ) + + +class Regex(Token): + r"""Token for matching strings that match a given regular + expression. Defined with string specifying the regular expression in + a form recognized by the stdlib Python `re module <https://docs.python.org/3/library/re.html>`_. + If the given regex contains named groups (defined using ``(?P<name>...)``), + these will be preserved as named parse results. + + Example:: + + realnum = Regex(r"[+-]?\d+\.\d*") + date = Regex(r'(?P<year>\d{4})-(?P<month>\d\d?)-(?P<day>\d\d?)') + # ref: https://stackoverflow.com/questions/267399/how-do-you-match-only-valid-roman-numerals-with-a-regular-expression + roman = Regex(r"M{0,4}(CM|CD|D?{0,3})(XC|XL|L?X{0,3})(IX|IV|V?I{0,3})") + """ + compiledREtype = type(re.compile("[A-Z]")) + def __init__( self, pattern, flags=0, asGroupList=False, asMatch=False): + """The parameters ``pattern`` and ``flags`` are passed + to the ``re.compile()`` function as-is. See the Python + `re module <https://docs.python.org/3/library/re.html>`_ module for an + explanation of the acceptable patterns and flags. + """ + super(Regex,self).__init__() + + if isinstance(pattern, basestring): + if not pattern: + 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 + self.asGroupList = asGroupList + self.asMatch = asMatch + if self.asGroupList: + self.parseImpl = self.parseImplAsGroupList + if self.asMatch: + self.parseImpl = self.parseImplAsMatch + + 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() + ret = ParseResults(result.group()) + d = result.groupdict() + if d: + for k, v in d.items(): + ret[k] = v + return loc, ret + + def parseImplAsGroupList(self, instring, loc, doActions=True): + result = self.re.match(instring,loc) + if not result: + raise ParseException(instring, loc, self.errmsg, self) + + loc = result.end() + ret = result.groups() + return loc, ret + + def parseImplAsMatch(self, instring, loc, doActions=True): + result = self.re.match(instring,loc) + if not result: + raise ParseException(instring, loc, self.errmsg, self) + + loc = result.end() + ret = result + return loc, ret + + def __str__( self ): + try: + return super(Regex,self).__str__() + except Exception: + pass + + if self.strRepr is None: + self.strRepr = "Re:(%s)" % repr(self.pattern) + + return self.strRepr + + def sub(self, repl): + r""" + Return Regex with an attached parse action to transform the parsed + result as if called using `re.sub(expr, repl, string) <https://docs.python.org/3/library/re.html#re.sub>`_. + + Example:: + + make_html = Regex(r"(\w+):(.*?):").sub(r"<\1>\2</\1>") + print(make_html.transformString("h1:main title:")) + # prints "<h1>main title</h1>" + """ + if self.asGroupList: + warnings.warn("cannot use sub() with Regex(asGroupList=True)", + SyntaxWarning, stacklevel=2) + raise SyntaxError() + + if self.asMatch and callable(repl): + warnings.warn("cannot use sub() with a callable with Regex(asMatch=True)", + SyntaxWarning, stacklevel=2) + raise SyntaxError() + + if self.asMatch: + def pa(tokens): + return tokens[0].expand(repl) + else: + def pa(tokens): + return self.re.sub(repl, tokens[0]) + return self.addParseAction(pa) + +class QuotedString(Token): + r""" + Token for matching strings that are delimited by quoting characters. + + 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= ``False`` ) + - unquoteResults - boolean indicating whether the matched text + should be unquoted (default= ``True`` ) + - endQuoteChar - string of one or more characters defining the + end of the quote delimited string (default= ``None`` => same as + quoteChar) + - convertWhitespaceEscapes - convert escaped whitespace + (``'\t'``, ``'\n'``, etc.) to actual whitespace + (default= ``True`` ) + + Example:: + + qs = QuotedString('"') + print(qs.searchString('lsjdf "This is the quote" sldjf')) + complex_qs = QuotedString('{{', endQuoteChar='}}') + print(complex_qs.searchString('lsjdf {{This is the "quote"}} sldjf')) + sql_qs = QuotedString('"', escQuote='""') + print(sql_qs.searchString('lsjdf "This is the quote with ""embedded"" quotes" sldjf')) + + prints:: + + [['This is the quote']] + [['This is the "quote"']] + [['This is the quote with "embedded" quotes']] + """ + def __init__( self, quoteChar, escChar=None, escQuote=None, multiline=False, unquoteResults=True, endQuoteChar=None, convertWhitespaceEscapes=True): + super(QuotedString,self).__init__() + + # remove white space from quote chars - wont work anyway + quoteChar = quoteChar.strip() + if not quoteChar: + warnings.warn("quoteChar cannot be the empty string",SyntaxWarning,stacklevel=2) + raise SyntaxError() + + if endQuoteChar is None: + endQuoteChar = quoteChar + else: + endQuoteChar = endQuoteChar.strip() + if not endQuoteChar: + 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 + self.convertWhitespaceEscapes = convertWhitespaceEscapes + + 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 whitespace + if '\\' in ret and self.convertWhitespaceEscapes: + ws_map = { + r'\t' : '\t', + r'\n' : '\n', + r'\f' : '\f', + r'\r' : '\r', + } + for wslit,wschar in ws_map.items(): + ret = ret.replace(wslit, wschar) + + # replace escaped characters + if self.escChar: + ret = re.sub(self.escCharReplacePattern, r"\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 Exception: + 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 (will include whitespace in matched characters if not listed in + the provided exclusion set - see example). Defined with string + containing all disallowed characters, and an optional minimum, + maximum, and/or exact length. The default value for ``min`` is + 1 (a minimum value < 1 is not valid); the default values for + ``max`` and ``exact`` are 0, meaning no maximum or exact + length restriction. + + Example:: + + # define a comma-separated-value as anything that is not a ',' + csv_value = CharsNotIn(',') + print(delimitedList(csv_value).parseString("dkls,lsdkjf,s12 34,@!#,213")) + + prints:: + + ['dkls', 'lsdkjf', 's12 34', '@!#', '213'] + """ + 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 Exception: + 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 ``" \\t\\r\\n"``. Also takes optional ``min``, + ``max``, and ``exact`` arguments, as defined for the + :class:`Word` class. + """ + whiteStrs = { + ' ' : '<SP>', + '\t': '<TAB>', + '\n': '<LF>', + '\r': '<CR>', + '\f': '<FF>', + 'u\00A0': '<NBSP>', + 'u\1680': '<OGHAM_SPACE_MARK>', + 'u\180E': '<MONGOLIAN_VOWEL_SEPARATOR>', + 'u\2000': '<EN_QUAD>', + 'u\2001': '<EM_QUAD>', + 'u\2002': '<EN_SPACE>', + 'u\2003': '<EM_SPACE>', + 'u\2004': '<THREE-PER-EM_SPACE>', + 'u\2005': '<FOUR-PER-EM_SPACE>', + 'u\2006': '<SIX-PER-EM_SPACE>', + 'u\2007': '<FIGURE_SPACE>', + 'u\2008': '<PUNCTUATION_SPACE>', + 'u\2009': '<THIN_SPACE>', + 'u\200A': '<HAIR_SPACE>', + 'u\200B': '<ZERO_WIDTH_SPACE>', + 'u\202F': '<NNBSP>', + 'u\205F': '<MMSP>', + 'u\3000': '<IDEOGRAPHIC_SPACE>', + } + 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 instring[loc] not 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): + r"""Matches if current position is at the beginning of a line within + the parse string + + Example:: + + test = '''\ + AAA this line + AAA and this line + AAA but not this one + B AAA and definitely not this one + ''' + + for t in (LineStart() + 'AAA' + restOfLine).searchString(test): + print(t) + + prints:: + + ['AAA', ' this line'] + ['AAA', ' and this line'] + + """ + def __init__( self ): + super(LineStart,self).__init__() + self.errmsg = "Expected start of line" + + def parseImpl( self, instring, loc, doActions=True ): + if col(loc, instring) == 1: + return loc, [] + raise ParseException(instring, loc, self.errmsg, self) + +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 + ``wordChars`` (default= ``printables``). To emulate the + ``\b`` behavior of regular expressions, use + ``WordStart(alphanums)``. ``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 ``wordChars`` + (default= ``printables``). To emulate the ``\b`` behavior of + regular expressions, use ``WordEnd(alphanums)``. ``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 = [ ParserElement._literalStringClass( exprs ) ] + elif isinstance( exprs, Iterable ): + exprs = list(exprs) + # if sequence of strings provided, wrap with Literal + if all(isinstance(expr, basestring) for expr in exprs): + exprs = map(ParserElement._literalStringClass, 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 ``leaveWhitespace`` defined in base class, and also invokes ``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 Exception: + 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 " + _ustr(self) + + return self + + 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 :class:`ParseExpression` s to be found in the given order. + Expressions may be separated by whitespace. + May be constructed using the ``'+'`` operator. + May also be constructed using the ``'-'`` operator, which will + suppress backtracking. + + Example:: + + integer = Word(nums) + name_expr = OneOrMore(Word(alphas)) + + expr = And([integer("id"),name_expr("name"),integer("age")]) + # more easily written as: + expr = integer("id") + name_expr("name") + integer("age") + """ + + 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 streamline(self): + super(And, self).streamline() + self.mayReturnEmpty = all(e.mayReturnEmpty for e in self.exprs) + return self + + 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._from_exception(pe) + except IndexError: + raise ParseSyntaxException(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 = ParserElement._literalStringClass( 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 :class:`ParseExpression` is found. If + two expressions match, the expression that matches the longest + string will be used. May be constructed using the ``'^'`` + operator. + + Example:: + + # construct Or using '^' operator + + number = Word(nums) ^ Combine(Word(nums) + '.' + Word(nums)) + print(number.searchString("123 3.1416 789")) + + prints:: + + [['123'], ['3.1416'], ['789']] + """ + 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 streamline(self): + super(Or, self).streamline() + if __compat__.collect_all_And_tokens: + self.saveAsList = any(e.saveAsList for e in self.exprs) + return self + + 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 :class:`ParseExpression` is found. If + two expressions match, the first one listed is the one that will + match. May be constructed using the ``'|'`` operator. + + Example:: + + # construct MatchFirst using '|' operator + + # watch the order of expressions to match + number = Word(nums) | Combine(Word(nums) + '.' + Word(nums)) + print(number.searchString("123 3.1416 789")) # Fail! -> [['123'], ['3'], ['1416'], ['789']] + + # put more selective expression first + number = Combine(Word(nums) + '.' + Word(nums)) | Word(nums) + print(number.searchString("123 3.1416 789")) # Better -> [['123'], ['3.1416'], ['789']] + """ + 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 streamline(self): + super(MatchFirst, self).streamline() + if __compat__.collect_all_And_tokens: + self.saveAsList = any(e.saveAsList for e in self.exprs) + return self + + 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 :class:`ParseExpression` s to be found, but in + any order. Expressions may be separated by whitespace. + + May be constructed using the ``'&'`` operator. + + Example:: + + color = oneOf("RED ORANGE YELLOW GREEN BLUE PURPLE BLACK WHITE BROWN") + shape_type = oneOf("SQUARE CIRCLE TRIANGLE STAR HEXAGON OCTAGON") + integer = Word(nums) + shape_attr = "shape:" + shape_type("shape") + posn_attr = "posn:" + Group(integer("x") + ',' + integer("y"))("posn") + color_attr = "color:" + color("color") + size_attr = "size:" + integer("size") + + # use Each (using operator '&') to accept attributes in any order + # (shape and posn are required, color and size are optional) + shape_spec = shape_attr & posn_attr & Optional(color_attr) & Optional(size_attr) + + shape_spec.runTests(''' + shape: SQUARE color: BLACK posn: 100, 120 + shape: CIRCLE size: 50 color: BLUE posn: 50,80 + color:GREEN size:20 shape:TRIANGLE posn:20,40 + ''' + ) + + prints:: + + shape: SQUARE color: BLACK posn: 100, 120 + ['shape:', 'SQUARE', 'color:', 'BLACK', 'posn:', ['100', ',', '120']] + - color: BLACK + - posn: ['100', ',', '120'] + - x: 100 + - y: 120 + - shape: SQUARE + + + shape: CIRCLE size: 50 color: BLUE posn: 50,80 + ['shape:', 'CIRCLE', 'size:', '50', 'color:', 'BLUE', 'posn:', ['50', ',', '80']] + - color: BLUE + - posn: ['50', ',', '80'] + - x: 50 + - y: 80 + - shape: CIRCLE + - size: 50 + + + color: GREEN size: 20 shape: TRIANGLE posn: 20,40 + ['color:', 'GREEN', 'size:', '20', 'shape:', 'TRIANGLE', 'posn:', ['20', ',', '40']] + - color: GREEN + - posn: ['20', ',', '40'] + - x: 20 + - y: 40 + - shape: TRIANGLE + - size: 20 + """ + 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 + self.saveAsList = True + + def streamline(self): + super(Each, self).streamline() + self.mayReturnEmpty = all(e.mayReturnEmpty for e in self.exprs) + return self + + 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 = sum(resultlist, ParseResults([])) + 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 :class:`ParserElement`, for combining and + post-processing parsed tokens. + """ + def __init__( self, expr, savelist=False ): + super(ParseElementEnhance,self).__init__(savelist) + if isinstance( expr, basestring ): + if issubclass(ParserElement._literalStringClass, Token): + expr = ParserElement._literalStringClass(expr) + else: + expr = ParserElement._literalStringClass(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 Exception: + 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. + ``FollowedBy`` does *not* advance the parsing position within + the input string, it only verifies that the specified parse + expression matches at the current position. ``FollowedBy`` + always returns a null token list. If any results names are defined + in the lookahead expression, those *will* be returned for access by + name. + + Example:: + + # use FollowedBy to match a label only if it is followed by a ':' + data_word = Word(alphas) + label = data_word + FollowedBy(':') + attr_expr = Group(label + Suppress(':') + OneOrMore(data_word, stopOn=label).setParseAction(' '.join)) + + OneOrMore(attr_expr).parseString("shape: SQUARE color: BLACK posn: upper left").pprint() + + prints:: + + [['shape', 'SQUARE'], ['color', 'BLACK'], ['posn', 'upper left']] + """ + def __init__( self, expr ): + super(FollowedBy,self).__init__(expr) + self.mayReturnEmpty = True + + def parseImpl( self, instring, loc, doActions=True ): + _, ret = self.expr._parse(instring, loc, doActions=doActions) + del ret[:] + return loc, ret + + +class PrecededBy(ParseElementEnhance): + """Lookbehind matching of the given parse expression. + ``PrecededBy`` does not advance the parsing position within the + input string, it only verifies that the specified parse expression + matches prior to the current position. ``PrecededBy`` always + returns a null token list, but if a results name is defined on the + given expression, it is returned. + + Parameters: + + - expr - expression that must match prior to the current parse + location + - retreat - (default= ``None``) - (int) maximum number of characters + to lookbehind prior to the current parse location + + If the lookbehind expression is a string, Literal, Keyword, or + a Word or CharsNotIn with a specified exact or maximum length, then + the retreat parameter is not required. Otherwise, retreat must be + specified to give a maximum number of characters to look back from + the current parse position for a lookbehind match. + + Example:: + + # VB-style variable names with type prefixes + int_var = PrecededBy("#") + pyparsing_common.identifier + str_var = PrecededBy("$") + pyparsing_common.identifier + + """ + def __init__(self, expr, retreat=None): + super(PrecededBy, self).__init__(expr) + self.expr = self.expr().leaveWhitespace() + self.mayReturnEmpty = True + self.mayIndexError = False + self.exact = False + if isinstance(expr, str): + retreat = len(expr) + self.exact = True + elif isinstance(expr, (Literal, Keyword)): + retreat = expr.matchLen + self.exact = True + elif isinstance(expr, (Word, CharsNotIn)) and expr.maxLen != _MAX_INT: + retreat = expr.maxLen + self.exact = True + elif isinstance(expr, _PositionToken): + retreat = 0 + self.exact = True + self.retreat = retreat + self.errmsg = "not preceded by " + str(expr) + self.skipWhitespace = False + + def parseImpl(self, instring, loc=0, doActions=True): + if self.exact: + if loc < self.retreat: + raise ParseException(instring, loc, self.errmsg) + start = loc - self.retreat + _, ret = self.expr._parse(instring, start) + else: + # retreat specified a maximum lookbehind window, iterate + test_expr = self.expr + StringEnd() + instring_slice = instring[:loc] + last_expr = ParseException(instring, loc, self.errmsg) + for offset in range(1, min(loc, self.retreat+1)): + try: + _, ret = test_expr._parse(instring_slice, loc-offset) + except ParseBaseException as pbe: + last_expr = pbe + else: + break + else: + raise last_expr + # return empty list of tokens, but preserve any defined results names + del ret[:] + return loc, ret + + +class NotAny(ParseElementEnhance): + """Lookahead to disallow matching with the given parse expression. + ``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, ``NotAny`` does + *not* skip over leading whitespace. ``NotAny`` always returns + a null token list. May be constructed using the '~' operator. + + Example:: + + AND, OR, NOT = map(CaselessKeyword, "AND OR NOT".split()) + + # take care not to mistake keywords for identifiers + ident = ~(AND | OR | NOT) + Word(alphas) + boolean_term = Optional(NOT) + ident + + # very crude boolean expression - to support parenthesis groups and + # operation hierarchy, use infixNotation + boolean_expr = boolean_term + ZeroOrMore((AND | OR) + boolean_term) + + # integers that are followed by "." are actually floats + integer = Word(nums) + ~Char(".") + """ + 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 ): + if self.expr.canParseNext(instring, loc): + 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 _MultipleMatch(ParseElementEnhance): + def __init__( self, expr, stopOn=None): + super(_MultipleMatch, self).__init__(expr) + self.saveAsList = True + ender = stopOn + if isinstance(ender, basestring): + ender = ParserElement._literalStringClass(ender) + self.not_ender = ~ender if ender is not None else None + + def parseImpl( self, instring, loc, doActions=True ): + self_expr_parse = self.expr._parse + self_skip_ignorables = self._skipIgnorables + check_ender = self.not_ender is not None + if check_ender: + try_not_ender = self.not_ender.tryParse + + # must be at least one (but first see if we are the stopOn sentinel; + # if so, fail) + if check_ender: + try_not_ender(instring, loc) + loc, tokens = self_expr_parse( instring, loc, doActions, callPreParse=False ) + try: + hasIgnoreExprs = (not not self.ignoreExprs) + while 1: + if check_ender: + try_not_ender(instring, loc) + if hasIgnoreExprs: + preloc = self_skip_ignorables( 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 + +class OneOrMore(_MultipleMatch): + """Repetition of one or more of the given expression. + + Parameters: + - expr - expression that must match one or more times + - stopOn - (default= ``None``) - expression for a terminating sentinel + (only required if the sentinel would ordinarily match the repetition + expression) + + Example:: + + data_word = Word(alphas) + label = data_word + FollowedBy(':') + attr_expr = Group(label + Suppress(':') + OneOrMore(data_word).setParseAction(' '.join)) + + text = "shape: SQUARE posn: upper left color: BLACK" + OneOrMore(attr_expr).parseString(text).pprint() # Fail! read 'color' as data instead of next label -> [['shape', 'SQUARE color']] + + # use stopOn attribute for OneOrMore to avoid reading label string as part of the data + attr_expr = Group(label + Suppress(':') + OneOrMore(data_word, stopOn=label).setParseAction(' '.join)) + OneOrMore(attr_expr).parseString(text).pprint() # Better -> [['shape', 'SQUARE'], ['posn', 'upper left'], ['color', 'BLACK']] + + # could also be written as + (attr_expr * (1,)).parseString(text).pprint() + """ + + 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(_MultipleMatch): + """Optional repetition of zero or more of the given expression. + + Parameters: + - expr - expression that must match zero or more times + - stopOn - (default= ``None``) - expression for a terminating sentinel + (only required if the sentinel would ordinarily match the repetition + expression) + + Example: similar to :class:`OneOrMore` + """ + def __init__( self, expr, stopOn=None): + super(ZeroOrMore,self).__init__(expr, stopOn=stopOn) + self.mayReturnEmpty = True + + def parseImpl( self, instring, loc, doActions=True ): + try: + return super(ZeroOrMore, self).parseImpl(instring, loc, doActions) + except (ParseException,IndexError): + 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 _NullToken(object): + def __bool__(self): + return False + __nonzero__ = __bool__ + def __str__(self): + return "" + +_optionalNotMatched = _NullToken() +class Optional(ParseElementEnhance): + """Optional matching of the given expression. + + Parameters: + - expr - expression that must match zero or more times + - default (optional) - value to be returned if the optional expression is not found. + + Example:: + + # US postal code can be a 5-digit zip, plus optional 4-digit qualifier + zip = Combine(Word(nums, exact=5) + Optional('-' + Word(nums, exact=4))) + zip.runTests(''' + # traditional ZIP code + 12345 + + # ZIP+4 form + 12101-0001 + + # invalid ZIP + 98765- + ''') + + prints:: + + # traditional ZIP code + 12345 + ['12345'] + + # ZIP+4 form + 12101-0001 + ['12101-0001'] + + # invalid ZIP + 98765- + ^ + FAIL: Expected end of text (at char 5), (line:1, col:6) + """ + def __init__( self, expr, default=_optionalNotMatched ): + super(Optional,self).__init__( expr, savelist=False ) + self.saveAsList = self.expr.saveAsList + 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. + + Parameters: + - expr - target expression marking the end of the data to be skipped + - include - (default= ``False``) if True, the target expression is also parsed + (the skipped text and target expression are returned as a 2-element list). + - ignore - (default= ``None``) used to define grammars (typically quoted strings and + comments) that might contain false matches to the target expression + - failOn - (default= ``None``) define expressions that are not allowed to be + included in the skipped test; if found before the target expression is found, + the SkipTo is not a match + + Example:: + + report = ''' + Outstanding Issues Report - 1 Jan 2000 + + # | Severity | Description | Days Open + -----+----------+-------------------------------------------+----------- + 101 | Critical | Intermittent system crash | 6 + 94 | Cosmetic | Spelling error on Login ('log|n') | 14 + 79 | Minor | System slow when running too many reports | 47 + ''' + integer = Word(nums) + SEP = Suppress('|') + # use SkipTo to simply match everything up until the next SEP + # - ignore quoted strings, so that a '|' character inside a quoted string does not match + # - parse action will call token.strip() for each matched token, i.e., the description body + string_data = SkipTo(SEP, ignore=quotedString) + string_data.setParseAction(tokenMap(str.strip)) + ticket_expr = (integer("issue_num") + SEP + + string_data("sev") + SEP + + string_data("desc") + SEP + + integer("days_open")) + + for tkt in ticket_expr.searchString(report): + print tkt.dump() + + prints:: + + ['101', 'Critical', 'Intermittent system crash', '6'] + - days_open: 6 + - desc: Intermittent system crash + - issue_num: 101 + - sev: Critical + ['94', 'Cosmetic', "Spelling error on Login ('log|n')", '14'] + - days_open: 14 + - desc: Spelling error on Login ('log|n') + - issue_num: 94 + - sev: Cosmetic + ['79', 'Minor', 'System slow when running too many reports', '47'] + - days_open: 47 + - desc: System slow when running too many reports + - issue_num: 79 + - sev: Minor + """ + 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.saveAsList = False + if isinstance(failOn, basestring): + self.failOn = ParserElement._literalStringClass(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 + expr_parse = self.expr._parse + self_failOn_canParseNext = self.failOn.canParseNext if self.failOn is not None else None + self_ignoreExpr_tryParse = self.ignoreExpr.tryParse if self.ignoreExpr is not None else None + + tmploc = loc + while tmploc <= instrlen: + if self_failOn_canParseNext is not None: + # break if failOn expression matches + if self_failOn_canParseNext(instring, tmploc): + break + + if self_ignoreExpr_tryParse is not None: + # advance past ignore expressions + while 1: + try: + tmploc = self_ignoreExpr_tryParse(instring, tmploc) + except ParseBaseException: + break + + try: + expr_parse(instring, tmploc, doActions=False, callPreParse=False) + except (ParseException, IndexError): + # no match, advance loc in string + tmploc += 1 + else: + # matched skipto expr, done + break + + else: + # ran off the end of the input string without matching skipto expr, fail + raise ParseException(instring, loc, self.errmsg, self) + + # build up return values + loc = tmploc + skiptext = instring[startloc:loc] + skipresult = ParseResults(skiptext) + + if self.includeMatch: + loc, mat = expr_parse(instring,loc,doActions,callPreParse=False) + skipresult += mat + + return loc, skipresult + +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 ``Forward`` + variable using the '<<' operator. + + Note: take care when assigning to ``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 ``Forward``:: + + fwdExpr << (a | b | c) + + Converting to use the '<<=' operator instead will avoid this problem. + + See :class:`ParseResults.pprint` for an example of a recursive + parser created using ``Forward``. + """ + 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.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 + + # Avoid infinite recursion by setting a temporary name + self.name = self.__class__.__name__ + ": ..." + + # Use the string representation of main expression. + try: + if self.expr is not None: + retString = _ustr(self.expr) + else: + retString = "None" + finally: + del self.name + 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 TokenConverter(ParseElementEnhance): + """ + Abstract subclass of :class:`ParseExpression`, for converting parsed results. + """ + def __init__( self, expr, savelist=False ): + super(TokenConverter,self).__init__( expr )#, savelist ) + self.saveAsList = False + +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 + ``'adjacent=False'`` in the constructor. + + Example:: + + real = Word(nums) + '.' + Word(nums) + print(real.parseString('3.1416')) # -> ['3', '.', '1416'] + # will also erroneously match the following + print(real.parseString('3. 1416')) # -> ['3', '.', '1416'] + + real = Combine(Word(nums) + '.' + Word(nums)) + print(real.parseString('3.1416')) # -> ['3.1416'] + # no match when there are internal spaces + print(real.parseString('3. 1416')) # -> Exception: Expected W:(0123...) + """ + 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 :class:`ZeroOrMore` and :class:`OneOrMore` expressions. + + Example:: + + ident = Word(alphas) + num = Word(nums) + term = ident | num + func = ident + Optional(delimitedList(term)) + print(func.parseString("fn a,b,100")) # -> ['fn', 'a', 'b', '100'] + + func = ident + Group(Optional(delimitedList(term))) + print(func.parseString("fn a,b,100")) # -> ['fn', ['a', 'b', '100']] + """ + 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. + + Example:: + + data_word = Word(alphas) + label = data_word + FollowedBy(':') + attr_expr = Group(label + Suppress(':') + OneOrMore(data_word).setParseAction(' '.join)) + + text = "shape: SQUARE posn: upper left color: light blue texture: burlap" + attr_expr = (label + Suppress(':') + OneOrMore(data_word, stopOn=label).setParseAction(' '.join)) + + # print attributes as plain groups + print(OneOrMore(attr_expr).parseString(text).dump()) + + # instead of OneOrMore(expr), parse using Dict(OneOrMore(Group(expr))) - Dict will auto-assign names + result = Dict(OneOrMore(Group(attr_expr))).parseString(text) + print(result.dump()) + + # access named fields as dict entries, or output as dict + print(result['shape']) + print(result.asDict()) + + prints:: + + ['shape', 'SQUARE', 'posn', 'upper left', 'color', 'light blue', 'texture', 'burlap'] + [['shape', 'SQUARE'], ['posn', 'upper left'], ['color', 'light blue'], ['texture', 'burlap']] + - color: light blue + - posn: upper left + - shape: SQUARE + - texture: burlap + SQUARE + {'color': 'light blue', 'posn': 'upper left', 'texture': 'burlap', 'shape': 'SQUARE'} + + See more examples at :class:`ParseResults` of accessing fields by results name. + """ + 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. + + Example:: + + source = "a, b, c,d" + wd = Word(alphas) + wd_list1 = wd + ZeroOrMore(',' + wd) + print(wd_list1.parseString(source)) + + # often, delimiters that are useful during parsing are just in the + # way afterward - use Suppress to keep them out of the parsed output + wd_list2 = wd + ZeroOrMore(Suppress(',') + wd) + print(wd_list2.parseString(source)) + + prints:: + + ['a', ',', 'b', ',', 'c', ',', 'd'] + ['a', 'b', 'c', 'd'] + + (See also :class:`delimitedList`.) + """ + 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. + + When the parse action is called, this decorator will print + ``">> entering method-name(line:<current_source_line>, <parse_location>, <matched_tokens>)"``. + When the parse action completes, the decorator will print + ``"<<"`` followed by the returned value, or any exception that the parse action raised. + + Example:: + + wd = Word(alphas) + + @traceParseAction + def remove_duplicate_chars(tokens): + return ''.join(sorted(set(''.join(tokens)))) + + wds = OneOrMore(wd).setParseAction(remove_duplicate_chars) + print(wds.parseString("slkdjs sld sldd sdlf sdljf")) + + prints:: + + >>entering remove_duplicate_chars(line: 'slkdjs sld sldd sdlf sdljf', 0, (['slkdjs', 'sld', 'sldd', 'sdlf', 'sdljf'], {})) + <<leaving remove_duplicate_chars (ret: 'dfjkls') + ['dfjkls'] + """ + f = _trim_arity(f) + def z(*paArgs): + thisFunc = f.__name__ + s,l,t = paArgs[-3:] + if len(paArgs)>3: + thisFunc = paArgs[0].__class__.__name__ + '.' + thisFunc + sys.stderr.write( ">>entering %s(line: '%s', %d, %r)\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: %r)\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 ``combine=True`` in the constructor. If + ``combine`` is set to ``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. + + Example:: + + delimitedList(Word(alphas)).parseString("aa,bb,cc") # -> ['aa', 'bb', 'cc'] + delimitedList(Word(hexnums), delim=':', combine=True).parseString("AA:BB:CC:DD:EE") # -> ['AA:BB:CC:DD:EE'] + """ + 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. + + If ``intExpr`` is specified, it should be a pyparsing expression + that produces an integer value. + + Example:: + + countedArray(Word(alphas)).parseString('2 ab cd ef') # -> ['ab', 'cd'] + + # in this parser, the leading integer value is given in binary, + # '10' indicating that 2 values are in the array + binaryConstant = Word('01').setParseAction(lambda t: int(t[0], 2)) + countedArray(Word(alphas), intExpr=binaryConstant).parseString('10 ab cd ef') # -> ['ab', 'cd'] + """ + 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 ).setName('(len) ' + _ustr(expr) + '...') + +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 ``"1:1"``, but not ``"1:2"``. Because this + matches a previous literal, will also match the leading + ``"1:1"`` in ``"1:10"``. If this is not desired, use + :class:`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) + rep.setName('(prev) ' + _ustr(expr)) + 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 ``"1:1"``, but not ``"1:2"``. Because this + matches by expressions, will *not* match the leading ``"1:1"`` + in ``"1:10"``; the expressions are evaluated first, and then + compared, so ``"1"`` is compared with ``"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) + rep.setName('(prev) ' + _ustr(expr)) + 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 :class:`MatchFirst` for best performance. + + Parameters: + + - strs - a string of space-delimited literals, or a collection 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 :class:`MatchFirst` object (if ``caseless=True``, or if + creating a :class:`Regex` raises an exception) + + Example:: + + comp_oper = oneOf("< = > <= >= !=") + var = Word(alphas) + number = Word(nums) + term = var | number + comparison_expr = term + comp_oper + term + print(comparison_expr.searchString("B = 12 AA=23 B<=AA AA>12")) + + prints:: + + [['B', '=', '12'], ['AA', '=', '23'], ['B', '<=', 'AA'], ['AA', '>', '12']] + """ + 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, Iterable): + symbols = list(strs) + else: + warnings.warn("Invalid argument to oneOf, expected string or iterable", + 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) ).setName(' | '.join(symbols)) + else: + return Regex( "|".join(re.escape(sym) for sym in symbols) ).setName(' | '.join(symbols)) + except Exception: + 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).setName(' | '.join(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 :class:`Dict`, :class:`ZeroOrMore`, and + :class:`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 :class:`Dict` results + can include named token fields. + + Example:: + + text = "shape: SQUARE posn: upper left color: light blue texture: burlap" + attr_expr = (label + Suppress(':') + OneOrMore(data_word, stopOn=label).setParseAction(' '.join)) + print(OneOrMore(attr_expr).parseString(text).dump()) + + attr_label = label + attr_value = Suppress(':') + OneOrMore(data_word, stopOn=label).setParseAction(' '.join) + + # similar to Dict, but simpler call format + result = dictOf(attr_label, attr_value).parseString(text) + print(result.dump()) + print(result['shape']) + print(result.shape) # object attribute access works too + print(result.asDict()) + + prints:: + + [['shape', 'SQUARE'], ['posn', 'upper left'], ['color', 'light blue'], ['texture', 'burlap']] + - color: light blue + - posn: upper left + - shape: SQUARE + - texture: burlap + SQUARE + SQUARE + {'color': 'light blue', 'shape': 'SQUARE', 'posn': 'upper left', 'texture': 'burlap'} + """ + return Dict(OneOrMore(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. By + default, returns astring containing the original parsed text. + + If the optional ``asString`` argument is passed as + ``False``, then the return value is + a :class:`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 + :class:`originalTextFor` contains expressions with defined + results names, you must set ``asString`` to ``False`` if you + want to preserve those results name values. + + Example:: + + src = "this is test <b> bold <i>text</i> </b> normal text " + for tag in ("b","i"): + opener,closer = makeHTMLTags(tag) + patt = originalTextFor(opener + SkipTo(closer) + closer) + print(patt.searchString(src)[0]) + + prints:: + + ['<b> bold <i>text</i> </b>'] + ['<i>text</i>'] + """ + 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): + t[:] = [s[t.pop('_original_start'):t.pop('_original_end')]] + matchExpr.setParseAction(extractText) + matchExpr.ignoreExprs = expr.ignoreExprs + 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).addParseAction(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 ``<TAB>`` characters, you + may want to call :class:`ParserElement.parseWithTabs` + + Example:: + + wd = Word(alphas) + for match in locatedExpr(wd).searchString("ljsdf123lksdjjf123lkkjj1222"): + print(match) + + prints:: + + [[0, 'ljsdf', 5]] + [[8, 'lksdjjf', 15]] + [[18, 'lkkjj', 23]] + """ + 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 | CharsNotIn(r'\]', exact=1) +_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 Exception: + 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 + :class:`transformString<ParserElement.transformString>` (). + + Example:: + + num = Word(nums).setParseAction(lambda toks: int(toks[0])) + na = oneOf("N/A NA").setParseAction(replaceWith(math.nan)) + term = na | num + + OneOrMore(term).parseString("324 234 N/A 234") # -> [324, 234, nan, 234] + """ + return lambda s,l,t: [replStr] + +def removeQuotes(s,l,t): + """Helper parse action for removing quotation marks from parsed + quoted strings. + + Example:: + + # by default, quotation marks are included in parsed results + quotedString.parseString("'Now is the Winter of our Discontent'") # -> ["'Now is the Winter of our Discontent'"] + + # use removeQuotes to strip quotation marks from parsed results + quotedString.setParseAction(removeQuotes) + quotedString.parseString("'Now is the Winter of our Discontent'") # -> ["Now is the Winter of our Discontent"] + """ + return t[0][1:-1] + +def tokenMap(func, *args): + """Helper to define a parse action by mapping a function to all + elements of a ParseResults list. If any additional args are passed, + they are forwarded to the given function as additional arguments + after the token, as in + ``hex_integer = Word(hexnums).setParseAction(tokenMap(int, 16))``, + which will convert the parsed data to an integer using base 16. + + Example (compare the last to example in :class:`ParserElement.transformString`:: + + hex_ints = OneOrMore(Word(hexnums)).setParseAction(tokenMap(int, 16)) + hex_ints.runTests(''' + 00 11 22 aa FF 0a 0d 1a + ''') + + upperword = Word(alphas).setParseAction(tokenMap(str.upper)) + OneOrMore(upperword).runTests(''' + my kingdom for a horse + ''') + + wd = Word(alphas).setParseAction(tokenMap(str.title)) + OneOrMore(wd).setParseAction(' '.join).runTests(''' + now is the winter of our discontent made glorious summer by this sun of york + ''') + + prints:: + + 00 11 22 aa FF 0a 0d 1a + [0, 17, 34, 170, 255, 10, 13, 26] + + my kingdom for a horse + ['MY', 'KINGDOM', 'FOR', 'A', 'HORSE'] + + now is the winter of our discontent made glorious summer by this sun of york + ['Now Is The Winter Of Our Discontent Made Glorious Summer By This Sun Of York'] + """ + def pa(s,l,t): + return [func(tokn, *args) for tokn in t] + + try: + func_name = getattr(func, '__name__', + getattr(func, '__class__').__name__) + except Exception: + func_name = str(func) + pa.__name__ = func_name + + return pa + +upcaseTokens = tokenMap(lambda t: _ustr(t).upper()) +"""(Deprecated) Helper parse action to convert tokens to upper case. +Deprecated in favor of :class:`pyparsing_common.upcaseTokens`""" + +downcaseTokens = tokenMap(lambda t: _ustr(t).lower()) +"""(Deprecated) Helper parse action to convert tokens to lower case. +Deprecated in favor of :class:`pyparsing_common.downcaseTokens`""" + +def _makeTags(tagStr, xml, + suppress_LT=Suppress("<"), + suppress_GT=Suppress(">")): + """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_LT + + tagStr("tag") + + Dict(ZeroOrMore(Group(tagAttrName + Suppress("=") + tagAttrValue ))) + + Optional("/", default=[False])("empty").setParseAction(lambda s,l,t:t[0]=='/') + + suppress_GT) + else: + tagAttrValue = quotedString.copy().setParseAction( removeQuotes ) | Word(printables, excludeChars=">") + openTag = (suppress_LT + + tagStr("tag") + + Dict(ZeroOrMore(Group(tagAttrName.setParseAction(downcaseTokens) + + Optional(Suppress("=") + tagAttrValue)))) + + Optional("/",default=[False])("empty").setParseAction(lambda s,l,t:t[0]=='/') + + suppress_GT) + closeTag = Combine(_L("</") + tagStr + ">", adjacent=False) + + openTag.setName("<%s>" % resname) + # add start<tagname> results name in parse action now that ungrouped names are not reported at two levels + openTag.addParseAction(lambda t: t.__setitem__("start"+"".join(resname.replace(":"," ").title().split()), t.copy())) + closeTag = closeTag("end"+"".join(resname.replace(":"," ").title().split())).setName("</%s>" % resname) + openTag.tag = resname + closeTag.tag = resname + openTag.tag_body = SkipTo(closeTag()) + return openTag, closeTag + +def makeHTMLTags(tagStr): + """Helper to construct opening and closing tag expressions for HTML, + given a tag name. Matches tags in either upper or lower case, + attributes with namespaces and with quoted or unquoted values. + + Example:: + + text = '<td>More info at the <a href="https://github.com/pyparsing/pyparsing/wiki">pyparsing</a> wiki page</td>' + # makeHTMLTags returns pyparsing expressions for the opening and + # closing tags as a 2-tuple + a,a_end = makeHTMLTags("A") + link_expr = a + SkipTo(a_end)("link_text") + a_end + + for link in link_expr.searchString(text): + # attributes in the <A> tag (like "href" shown here) are + # also accessible as named results + print(link.link_text, '->', link.href) + + prints:: + + pyparsing -> https://github.com/pyparsing/pyparsing/wiki + """ + return _makeTags( tagStr, False ) + +def makeXMLTags(tagStr): + """Helper to construct opening and closing tag expressions for XML, + given a tag name. Matches tags only in the given upper/lower case. + + Example: similar to :class:`makeHTMLTags` + """ + return _makeTags( tagStr, True ) + +def withAttribute(*args,**attrDict): + """Helper to create a validating parse action to be used with start + tags created with :class:`makeXMLTags` or + :class:`makeHTMLTags`. Use ``withAttribute`` to qualify + a starting tag with a required attribute value, to avoid false + matches on common tags such as ``<TD>`` or ``<DIV>``. + + Call ``withAttribute`` with a series of attribute names and + values. Specify the list of filter attributes names and values as: + + - keyword arguments, as in ``(align="right")``, or + - as an explicit dict with ``**`` operator, when an attribute + name is also a Python reserved word, as in ``**{"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 ``class`` (with or without a namespace), use + :class:`withClass`. + + To verify that the attribute exists, but without specifying a value, + pass ``withAttribute.ANY_VALUE`` as the value. + + Example:: + + html = ''' + <div> + Some text + <div type="grid">1 4 0 1 0</div> + <div type="graph">1,3 2,3 1,1</div> + <div>this has no type</div> + </div> + + ''' + div,div_end = makeHTMLTags("div") + + # only match div tag having a type attribute with value "grid" + div_grid = div().setParseAction(withAttribute(type="grid")) + grid_expr = div_grid + SkipTo(div | div_end)("body") + for grid_header in grid_expr.searchString(html): + print(grid_header.body) + + # construct a match with any div tag having a type attribute, regardless of the value + div_any_type = div().setParseAction(withAttribute(type=withAttribute.ANY_VALUE)) + div_expr = div_any_type + SkipTo(div | div_end)("body") + for div_header in div_expr.searchString(html): + print(div_header.body) + + prints:: + + 1 4 0 1 0 + + 1 4 0 1 0 + 1,3 2,3 1,1 + """ + 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 :class:`withAttribute` when + matching on a div class - made difficult because ``class`` is + a reserved word in Python. + + Example:: + + html = ''' + <div> + Some text + <div class="grid">1 4 0 1 0</div> + <div class="graph">1,3 2,3 1,1</div> + <div>this <div> has no class</div> + </div> + + ''' + div,div_end = makeHTMLTags("div") + div_grid = div().setParseAction(withClass("grid")) + + grid_expr = div_grid + SkipTo(div | div_end)("body") + for grid_header in grid_expr.searchString(html): + print(grid_header.body) + + div_any_type = div().setParseAction(withClass(withAttribute.ANY_VALUE)) + div_expr = div_any_type + SkipTo(div | div_end)("body") + for div_header in div_expr.searchString(html): + print(div_header.body) + + prints:: + + 1 4 0 1 0 + + 1 4 0 1 0 + 1,3 2,3 1,1 + """ + classattr = "%s:class" % namespace if namespace else "class" + return withAttribute(**{classattr : classname}) + +opAssoc = SimpleNamespace() +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. The generated parser will also + recognize the use of parentheses to override operator precedences + (see example below). + + Note: if you define a deep operator list, you may see performance + issues when using infixNotation. See + :class:`ParserElement.enablePackrat` for a mechanism to potentially + improve your parser performance. + + 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 + ``opAssoc.RIGHT`` and ``opAssoc.LEFT``. + - parseAction is the parse action to be associated with + expressions matching this operator expression (the parse action + tuple member may be omitted); if the parse action is passed + a tuple or list of functions, this is equivalent to calling + ``setParseAction(*fn)`` + (:class:`ParserElement.setParseAction`) + - lpar - expression for matching left-parentheses + (default= ``Suppress('(')``) + - rpar - expression for matching right-parentheses + (default= ``Suppress(')')``) + + Example:: + + # simple example of four-function arithmetic with ints and + # variable names + integer = pyparsing_common.signed_integer + varname = pyparsing_common.identifier + + arith_expr = infixNotation(integer | varname, + [ + ('-', 1, opAssoc.RIGHT), + (oneOf('* /'), 2, opAssoc.LEFT), + (oneOf('+ -'), 2, opAssoc.LEFT), + ]) + + arith_expr.runTests(''' + 5+3*6 + (5+3)*6 + -2--11 + ''', fullDump=False) + + prints:: + + 5+3*6 + [[5, '+', [3, '*', 6]]] + + (5+3)*6 + [[[5, '+', 3], '*', 6]] + + -2--11 + [[['-', 2], '-', ['-', 11]]] + """ + # captive version of FollowedBy that does not do parse actions or capture results names + class _FB(FollowedBy): + def parseImpl(self, instring, loc, doActions=True): + self.expr.tryParse(instring, loc) + return loc, [] + + ret = Forward() + lastExpr = baseExpr | ( lpar + ret + rpar ) + for i,operDef in enumerate(opList): + opExpr,arity,rightLeftAssoc,pa = (operDef + (None,))[:4] + termName = "%s term" % opExpr if arity < 3 else "%s%s term" % opExpr + 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(termName) + if rightLeftAssoc == opAssoc.LEFT: + if arity == 1: + matchExpr = _FB(lastExpr + opExpr) + Group( lastExpr + OneOrMore( opExpr ) ) + elif arity == 2: + if opExpr is not None: + matchExpr = _FB(lastExpr + opExpr + lastExpr) + Group( lastExpr + OneOrMore( opExpr + lastExpr ) ) + else: + matchExpr = _FB(lastExpr+lastExpr) + Group( lastExpr + OneOrMore(lastExpr) ) + elif arity == 3: + matchExpr = _FB(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 = _FB(opExpr.expr + thisExpr) + Group( opExpr + thisExpr ) + elif arity == 2: + if opExpr is not None: + matchExpr = _FB(lastExpr + opExpr + thisExpr) + Group( lastExpr + OneOrMore( opExpr + thisExpr ) ) + else: + matchExpr = _FB(lastExpr + thisExpr) + Group( lastExpr + OneOrMore( thisExpr ) ) + elif arity == 3: + matchExpr = _FB(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: + if isinstance(pa, (tuple, list)): + matchExpr.setParseAction(*pa) + else: + matchExpr.setParseAction(pa) + thisExpr <<= ( matchExpr.setName(termName) | lastExpr ) + lastExpr = thisExpr + ret <<= lastExpr + return ret + +operatorPrecedence = infixNotation +"""(Deprecated) Former name of :class:`infixNotation`, will be +dropped in a future release.""" + +dblQuotedString = Combine(Regex(r'"(?:[^"\n\r\\]|(?:"")|(?:\\(?:[^x]|x[0-9a-fA-F]+)))*')+'"').setName("string enclosed in double quotes") +sglQuotedString = Combine(Regex(r"'(?:[^'\n\r\\]|(?:'')|(?:\\(?:[^x]|x[0-9a-fA-F]+)))*")+"'").setName("string enclosed in single quotes") +quotedString = Combine(Regex(r'"(?:[^"\n\r\\]|(?:"")|(?:\\(?:[^x]|x[0-9a-fA-F]+)))*')+'"'| + Regex(r"'(?:[^'\n\r\\]|(?:'')|(?:\\(?:[^x]|x[0-9a-fA-F]+)))*")+"'").setName("quotedString using single or double quotes") +unicodeString = Combine(_L('u') + quotedString.copy()).setName("unicode string literal") + +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= :class:`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 ``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 + :class:`Or` or :class:`MatchFirst`. The default is + :class:`quotedString`, but if no expressions are to be ignored, then + pass ``None`` for this argument. + + Example:: + + data_type = oneOf("void int short long char float double") + decl_data_type = Combine(data_type + Optional(Word('*'))) + ident = Word(alphas+'_', alphanums+'_') + number = pyparsing_common.number + arg = Group(decl_data_type + ident) + LPAR,RPAR = map(Suppress, "()") + + code_body = nestedExpr('{', '}', ignoreExpr=(quotedString | cStyleComment)) + + c_function = (decl_data_type("type") + + ident("name") + + LPAR + Optional(delimitedList(arg), [])("args") + RPAR + + code_body("body")) + c_function.ignore(cStyleComment) + + source_code = ''' + int is_odd(int x) { + return (x%2); + } + + int dec_to_hex(char hchar) { + if (hchar >= '0' && hchar <= '9') { + return (ord(hchar)-ord('0')); + } else { + return (10+ord(hchar)-ord('A')); + } + } + ''' + for func in c_function.searchString(source_code): + print("%(name)s (%(type)s) args: %(args)s" % func) + + + prints:: + + is_odd (int) args: [['int', 'x']] + dec_to_hex (int) args: [['char', 'hchar']] + """ + 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) ) + ret.setName('nested %s%s expression' % (opener,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 ``blockStatement``. + + Example:: + + data = ''' + def A(z): + A1 + B = 100 + G = A2 + A2 + A3 + B + def BB(a,b,c): + BB1 + def BBA(): + bba1 + bba2 + bba3 + C + D + def spam(x,y): + def eggs(z): + pass + ''' + + + indentStack = [1] + stmt = Forward() + + identifier = Word(alphas, alphanums) + funcDecl = ("def" + identifier + Group( "(" + Optional( delimitedList(identifier) ) + ")" ) + ":") + func_body = indentedBlock(stmt, indentStack) + funcDef = Group( funcDecl + func_body ) + + rvalue = Forward() + funcCall = Group(identifier + "(" + Optional(delimitedList(rvalue)) + ")") + rvalue << (funcCall | identifier | Word(nums)) + assignment = Group(identifier + "=" + rvalue) + stmt << ( funcDef | assignment | identifier ) + + module_body = OneOrMore(stmt) + + parseTree = module_body.parseString(data) + parseTree.pprint() + + prints:: + + [['def', + 'A', + ['(', 'z', ')'], + ':', + [['A1'], [['B', '=', '100']], [['G', '=', 'A2']], ['A2'], ['A3']]], + 'B', + ['def', + 'BB', + ['(', 'a', 'b', 'c', ')'], + ':', + [['BB1'], [['def', 'BBA', ['(', ')'], ':', [['bba1'], ['bba2'], ['bba3']]]]]], + 'C', + 'D', + ['def', + 'spam', + ['(', 'x', 'y', ')'], + ':', + [[['def', 'eggs', ['(', 'z', ')'], ':', [['pass']]]]]]] + """ + backup_stack = indentStack[:] + + def reset_stack(): + indentStack[:] = backup_stack + + def checkPeerIndent(s,l,t): + if l >= len(s): return + curCol = col(l,s) + if curCol != indentStack[-1]: + if curCol > indentStack[-1]: + raise ParseException(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)).setName('INDENT') + PEER = Empty().setParseAction(checkPeerIndent).setName('') + UNDENT = Empty().setParseAction(checkUnindent).setName('UNINDENT') + 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) )) ) + smExpr.setFailAction(lambda a, b, c, d: reset_stack()) + blockStatementExpr.ignore(_bslash + LineEnd()) + return smExpr.setName('indented block') + +alphas8bit = srange(r"[\0xc0-\0xd6\0xd8-\0xf6\0xf8-\0xff]") +punc8bit = srange(r"[\0xa1-\0xbf\0xd7\0xf7]") + +anyOpenTag,anyCloseTag = makeHTMLTags(Word(alphas,alphanums+"_:").setName('any tag')) +_htmlEntityMap = dict(zip("gt lt amp nbsp quot apos".split(),'><& "\'')) +commonHTMLEntity = Regex('&(?P<entity>' + '|'.join(_htmlEntityMap.keys()) +");").setName("common HTML entity") +def replaceHTMLEntity(t): + """Helper parser action to replace common HTML entities with their special characters""" + return _htmlEntityMap.get(t.entity) + +# it's easy to get these comment structures wrong - they're very common, so may as well make them available +cStyleComment = Combine(Regex(r"/\*(?:[^*]|\*(?!/))*") + '*/').setName("C style comment") +"Comment of the form ``/* ... */``" + +htmlComment = Regex(r"<!--[\s\S]*?-->").setName("HTML comment") +"Comment of the form ``<!-- ... -->``" + +restOfLine = Regex(r".*").leaveWhitespace().setName("rest of line") +dblSlashComment = Regex(r"//(?:\\\n|[^\n])*").setName("// comment") +"Comment of the form ``// ... (to end of line)``" + +cppStyleComment = Combine(Regex(r"/\*(?:[^*]|\*(?!/))*") + '*/'| dblSlashComment).setName("C++ style comment") +"Comment of either form :class:`cStyleComment` or :class:`dblSlashComment`" + +javaStyleComment = cppStyleComment +"Same as :class:`cppStyleComment`" + +pythonStyleComment = Regex(r"#.*").setName("Python style comment") +"Comment of the form ``# ... (to end of line)``" + +_commasepitem = Combine(OneOrMore(Word(printables, excludeChars=',') + + Optional( Word(" \t") + + ~Literal(",") + ~LineEnd() ) ) ).streamline().setName("commaItem") +commaSeparatedList = delimitedList( Optional( quotedString.copy() | _commasepitem, default="") ).setName("commaSeparatedList") +"""(Deprecated) Predefined expression of 1 or more printable words or +quoted strings, separated by commas. + +This expression is deprecated in favor of :class:`pyparsing_common.comma_separated_list`. +""" + +# some other useful expressions - using lower-case class name since we are really using this as a namespace +class pyparsing_common: + """Here are some common low-level expressions that may be useful in + jump-starting parser development: + + - numeric forms (:class:`integers<integer>`, :class:`reals<real>`, + :class:`scientific notation<sci_real>`) + - common :class:`programming identifiers<identifier>` + - network addresses (:class:`MAC<mac_address>`, + :class:`IPv4<ipv4_address>`, :class:`IPv6<ipv6_address>`) + - ISO8601 :class:`dates<iso8601_date>` and + :class:`datetime<iso8601_datetime>` + - :class:`UUID<uuid>` + - :class:`comma-separated list<comma_separated_list>` + + Parse actions: + + - :class:`convertToInteger` + - :class:`convertToFloat` + - :class:`convertToDate` + - :class:`convertToDatetime` + - :class:`stripHTMLTags` + - :class:`upcaseTokens` + - :class:`downcaseTokens` + + Example:: + + pyparsing_common.number.runTests(''' + # any int or real number, returned as the appropriate type + 100 + -100 + +100 + 3.14159 + 6.02e23 + 1e-12 + ''') + + pyparsing_common.fnumber.runTests(''' + # any int or real number, returned as float + 100 + -100 + +100 + 3.14159 + 6.02e23 + 1e-12 + ''') + + pyparsing_common.hex_integer.runTests(''' + # hex numbers + 100 + FF + ''') + + pyparsing_common.fraction.runTests(''' + # fractions + 1/2 + -3/4 + ''') + + pyparsing_common.mixed_integer.runTests(''' + # mixed fractions + 1 + 1/2 + -3/4 + 1-3/4 + ''') + + import uuid + pyparsing_common.uuid.setParseAction(tokenMap(uuid.UUID)) + pyparsing_common.uuid.runTests(''' + # uuid + 12345678-1234-5678-1234-567812345678 + ''') + + prints:: + + # any int or real number, returned as the appropriate type + 100 + [100] + + -100 + [-100] + + +100 + [100] + + 3.14159 + [3.14159] + + 6.02e23 + [6.02e+23] + + 1e-12 + [1e-12] + + # any int or real number, returned as float + 100 + [100.0] + + -100 + [-100.0] + + +100 + [100.0] + + 3.14159 + [3.14159] + + 6.02e23 + [6.02e+23] + + 1e-12 + [1e-12] + + # hex numbers + 100 + [256] + + FF + [255] + + # fractions + 1/2 + [0.5] + + -3/4 + [-0.75] + + # mixed fractions + 1 + [1] + + 1/2 + [0.5] + + -3/4 + [-0.75] + + 1-3/4 + [1.75] + + # uuid + 12345678-1234-5678-1234-567812345678 + [UUID('12345678-1234-5678-1234-567812345678')] + """ + + convertToInteger = tokenMap(int) + """ + Parse action for converting parsed integers to Python int + """ + + convertToFloat = tokenMap(float) + """ + Parse action for converting parsed numbers to Python float + """ + + integer = Word(nums).setName("integer").setParseAction(convertToInteger) + """expression that parses an unsigned integer, returns an int""" + + hex_integer = Word(hexnums).setName("hex integer").setParseAction(tokenMap(int,16)) + """expression that parses a hexadecimal integer, returns an int""" + + signed_integer = Regex(r'[+-]?\d+').setName("signed integer").setParseAction(convertToInteger) + """expression that parses an integer with optional leading sign, returns an int""" + + fraction = (signed_integer().setParseAction(convertToFloat) + '/' + signed_integer().setParseAction(convertToFloat)).setName("fraction") + """fractional expression of an integer divided by an integer, returns a float""" + fraction.addParseAction(lambda t: t[0]/t[-1]) + + mixed_integer = (fraction | signed_integer + Optional(Optional('-').suppress() + fraction)).setName("fraction or mixed integer-fraction") + """mixed integer of the form 'integer - fraction', with optional leading integer, returns float""" + mixed_integer.addParseAction(sum) + + real = Regex(r'[+-]?\d+\.\d*').setName("real number").setParseAction(convertToFloat) + """expression that parses a floating point number and returns a float""" + + sci_real = Regex(r'[+-]?\d+([eE][+-]?\d+|\.\d*([eE][+-]?\d+)?)').setName("real number with scientific notation").setParseAction(convertToFloat) + """expression that parses a floating point number with optional + scientific notation and returns a float""" + + # streamlining this expression makes the docs nicer-looking + number = (sci_real | real | signed_integer).streamline() + """any numeric expression, returns the corresponding Python type""" + + fnumber = Regex(r'[+-]?\d+\.?\d*([eE][+-]?\d+)?').setName("fnumber").setParseAction(convertToFloat) + """any int or real number, returned as float""" + + identifier = Word(alphas+'_', alphanums+'_').setName("identifier") + """typical code identifier (leading alpha or '_', followed by 0 or more alphas, nums, or '_')""" + + ipv4_address = Regex(r'(25[0-5]|2[0-4][0-9]|1?[0-9]{1,2})(\.(25[0-5]|2[0-4][0-9]|1?[0-9]{1,2})){3}').setName("IPv4 address") + "IPv4 address (``0.0.0.0 - 255.255.255.255``)" + + _ipv6_part = Regex(r'[0-9a-fA-F]{1,4}').setName("hex_integer") + _full_ipv6_address = (_ipv6_part + (':' + _ipv6_part)*7).setName("full IPv6 address") + _short_ipv6_address = (Optional(_ipv6_part + (':' + _ipv6_part)*(0,6)) + "::" + Optional(_ipv6_part + (':' + _ipv6_part)*(0,6))).setName("short IPv6 address") + _short_ipv6_address.addCondition(lambda t: sum(1 for tt in t if pyparsing_common._ipv6_part.matches(tt)) < 8) + _mixed_ipv6_address = ("::ffff:" + ipv4_address).setName("mixed IPv6 address") + ipv6_address = Combine((_full_ipv6_address | _mixed_ipv6_address | _short_ipv6_address).setName("IPv6 address")).setName("IPv6 address") + "IPv6 address (long, short, or mixed form)" + + mac_address = Regex(r'[0-9a-fA-F]{2}([:.-])[0-9a-fA-F]{2}(?:\1[0-9a-fA-F]{2}){4}').setName("MAC address") + "MAC address xx:xx:xx:xx:xx (may also have '-' or '.' delimiters)" + + @staticmethod + def convertToDate(fmt="%Y-%m-%d"): + """ + Helper to create a parse action for converting parsed date string to Python datetime.date + + Params - + - fmt - format to be passed to datetime.strptime (default= ``"%Y-%m-%d"``) + + Example:: + + date_expr = pyparsing_common.iso8601_date.copy() + date_expr.setParseAction(pyparsing_common.convertToDate()) + print(date_expr.parseString("1999-12-31")) + + prints:: + + [datetime.date(1999, 12, 31)] + """ + def cvt_fn(s,l,t): + try: + return datetime.strptime(t[0], fmt).date() + except ValueError as ve: + raise ParseException(s, l, str(ve)) + return cvt_fn + + @staticmethod + def convertToDatetime(fmt="%Y-%m-%dT%H:%M:%S.%f"): + """Helper to create a parse action for converting parsed + datetime string to Python datetime.datetime + + Params - + - fmt - format to be passed to datetime.strptime (default= ``"%Y-%m-%dT%H:%M:%S.%f"``) + + Example:: + + dt_expr = pyparsing_common.iso8601_datetime.copy() + dt_expr.setParseAction(pyparsing_common.convertToDatetime()) + print(dt_expr.parseString("1999-12-31T23:59:59.999")) + + prints:: + + [datetime.datetime(1999, 12, 31, 23, 59, 59, 999000)] + """ + def cvt_fn(s,l,t): + try: + return datetime.strptime(t[0], fmt) + except ValueError as ve: + raise ParseException(s, l, str(ve)) + return cvt_fn + + iso8601_date = Regex(r'(?P<year>\d{4})(?:-(?P<month>\d\d)(?:-(?P<day>\d\d))?)?').setName("ISO8601 date") + "ISO8601 date (``yyyy-mm-dd``)" + + iso8601_datetime = Regex(r'(?P<year>\d{4})-(?P<month>\d\d)-(?P<day>\d\d)[T ](?P<hour>\d\d):(?P<minute>\d\d)(:(?P<second>\d\d(\.\d*)?)?)?(?P<tz>Z|[+-]\d\d:?\d\d)?').setName("ISO8601 datetime") + "ISO8601 datetime (``yyyy-mm-ddThh:mm:ss.s(Z|+-00:00)``) - trailing seconds, milliseconds, and timezone optional; accepts separating ``'T'`` or ``' '``" + + uuid = Regex(r'[0-9a-fA-F]{8}(-[0-9a-fA-F]{4}){3}-[0-9a-fA-F]{12}').setName("UUID") + "UUID (``xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx``)" + + _html_stripper = anyOpenTag.suppress() | anyCloseTag.suppress() + @staticmethod + def stripHTMLTags(s, l, tokens): + """Parse action to remove HTML tags from web page HTML source + + Example:: + + # strip HTML links from normal text + text = '<td>More info at the <a href="https://github.com/pyparsing/pyparsing/wiki">pyparsing</a> wiki page</td>' + td,td_end = makeHTMLTags("TD") + table_text = td + SkipTo(td_end).setParseAction(pyparsing_common.stripHTMLTags)("body") + td_end + print(table_text.parseString(text).body) + + Prints:: + + More info at the pyparsing wiki page + """ + return pyparsing_common._html_stripper.transformString(tokens[0]) + + _commasepitem = Combine(OneOrMore(~Literal(",") + ~LineEnd() + Word(printables, excludeChars=',') + + Optional( White(" \t") ) ) ).streamline().setName("commaItem") + comma_separated_list = delimitedList( Optional( quotedString.copy() | _commasepitem, default="") ).setName("comma separated list") + """Predefined expression of 1 or more printable words or quoted strings, separated by commas.""" + + upcaseTokens = staticmethod(tokenMap(lambda t: _ustr(t).upper())) + """Parse action to convert tokens to upper case.""" + + downcaseTokens = staticmethod(tokenMap(lambda t: _ustr(t).lower())) + """Parse action to convert tokens to lower case.""" + + +class _lazyclassproperty(object): + def __init__(self, fn): + self.fn = fn + self.__doc__ = fn.__doc__ + self.__name__ = fn.__name__ + + def __get__(self, obj, cls): + if cls is None: + cls = type(obj) + if not hasattr(cls, '_intern') or any(cls._intern is getattr(superclass, '_intern', []) for superclass in cls.__mro__[1:]): + cls._intern = {} + attrname = self.fn.__name__ + if attrname not in cls._intern: + cls._intern[attrname] = self.fn(cls) + return cls._intern[attrname] + + +class unicode_set(object): + """ + A set of Unicode characters, for language-specific strings for + ``alphas``, ``nums``, ``alphanums``, and ``printables``. + A unicode_set is defined by a list of ranges in the Unicode character + set, in a class attribute ``_ranges``, such as:: + + _ranges = [(0x0020, 0x007e), (0x00a0, 0x00ff),] + + A unicode set can also be defined using multiple inheritance of other unicode sets:: + + class CJK(Chinese, Japanese, Korean): + pass + """ + _ranges = [] + + @classmethod + def _get_chars_for_ranges(cls): + ret = [] + for cc in cls.__mro__: + if cc is unicode_set: + break + for rr in cc._ranges: + ret.extend(range(rr[0], rr[-1]+1)) + return [unichr(c) for c in sorted(set(ret))] + + @_lazyclassproperty + def printables(cls): + "all non-whitespace characters in this range" + return u''.join(filterfalse(unicode.isspace, cls._get_chars_for_ranges())) + + @_lazyclassproperty + def alphas(cls): + "all alphabetic characters in this range" + return u''.join(filter(unicode.isalpha, cls._get_chars_for_ranges())) + + @_lazyclassproperty + def nums(cls): + "all numeric digit characters in this range" + return u''.join(filter(unicode.isdigit, cls._get_chars_for_ranges())) + + @_lazyclassproperty + def alphanums(cls): + "all alphanumeric characters in this range" + return cls.alphas + cls.nums + + +class pyparsing_unicode(unicode_set): + """ + A namespace class for defining common language unicode_sets. + """ + _ranges = [(32, sys.maxunicode)] + + class Latin1(unicode_set): + "Unicode set for Latin-1 Unicode Character Range" + _ranges = [(0x0020, 0x007e), (0x00a0, 0x00ff),] + + class LatinA(unicode_set): + "Unicode set for Latin-A Unicode Character Range" + _ranges = [(0x0100, 0x017f),] + + class LatinB(unicode_set): + "Unicode set for Latin-B Unicode Character Range" + _ranges = [(0x0180, 0x024f),] + + class Greek(unicode_set): + "Unicode set for Greek Unicode Character Ranges" + _ranges = [ + (0x0370, 0x03ff), (0x1f00, 0x1f15), (0x1f18, 0x1f1d), (0x1f20, 0x1f45), (0x1f48, 0x1f4d), + (0x1f50, 0x1f57), (0x1f59,), (0x1f5b,), (0x1f5d,), (0x1f5f, 0x1f7d), (0x1f80, 0x1fb4), (0x1fb6, 0x1fc4), + (0x1fc6, 0x1fd3), (0x1fd6, 0x1fdb), (0x1fdd, 0x1fef), (0x1ff2, 0x1ff4), (0x1ff6, 0x1ffe), + ] + + class Cyrillic(unicode_set): + "Unicode set for Cyrillic Unicode Character Range" + _ranges = [(0x0400, 0x04ff)] + + class Chinese(unicode_set): + "Unicode set for Chinese Unicode Character Range" + _ranges = [(0x4e00, 0x9fff), (0x3000, 0x303f), ] + + class Japanese(unicode_set): + "Unicode set for Japanese Unicode Character Range, combining Kanji, Hiragana, and Katakana ranges" + _ranges = [ ] + + class Kanji(unicode_set): + "Unicode set for Kanji Unicode Character Range" + _ranges = [(0x4E00, 0x9Fbf), (0x3000, 0x303f), ] + + class Hiragana(unicode_set): + "Unicode set for Hiragana Unicode Character Range" + _ranges = [(0x3040, 0x309f), ] + + class Katakana(unicode_set): + "Unicode set for Katakana Unicode Character Range" + _ranges = [(0x30a0, 0x30ff), ] + + class Korean(unicode_set): + "Unicode set for Korean Unicode Character Range" + _ranges = [(0xac00, 0xd7af), (0x1100, 0x11ff), (0x3130, 0x318f), (0xa960, 0xa97f), (0xd7b0, 0xd7ff), (0x3000, 0x303f), ] + + class CJK(Chinese, Japanese, Korean): + "Unicode set for combined Chinese, Japanese, and Korean (CJK) Unicode Character Range" + pass + + class Thai(unicode_set): + "Unicode set for Thai Unicode Character Range" + _ranges = [(0x0e01, 0x0e3a), (0x0e3f, 0x0e5b), ] + + class Arabic(unicode_set): + "Unicode set for Arabic Unicode Character Range" + _ranges = [(0x0600, 0x061b), (0x061e, 0x06ff), (0x0700, 0x077f), ] + + class Hebrew(unicode_set): + "Unicode set for Hebrew Unicode Character Range" + _ranges = [(0x0590, 0x05ff), ] + + class Devanagari(unicode_set): + "Unicode set for Devanagari Unicode Character Range" + _ranges = [(0x0900, 0x097f), (0xa8e0, 0xa8ff)] + +pyparsing_unicode.Japanese._ranges = (pyparsing_unicode.Japanese.Kanji._ranges + + pyparsing_unicode.Japanese.Hiragana._ranges + + pyparsing_unicode.Japanese.Katakana._ranges) + +# define ranges in language character sets +if PY_3: + setattr(pyparsing_unicode, "العربية", pyparsing_unicode.Arabic) + setattr(pyparsing_unicode, "中文", pyparsing_unicode.Chinese) + setattr(pyparsing_unicode, "кириллица", pyparsing_unicode.Cyrillic) + setattr(pyparsing_unicode, "Ελληνικά", pyparsing_unicode.Greek) + setattr(pyparsing_unicode, "עִברִית", pyparsing_unicode.Hebrew) + setattr(pyparsing_unicode, "日本語", pyparsing_unicode.Japanese) + setattr(pyparsing_unicode.Japanese, "漢字", pyparsing_unicode.Japanese.Kanji) + setattr(pyparsing_unicode.Japanese, "カタカナ", pyparsing_unicode.Japanese.Katakana) + setattr(pyparsing_unicode.Japanese, "ひらがな", pyparsing_unicode.Japanese.Hiragana) + setattr(pyparsing_unicode, "한국어", pyparsing_unicode.Korean) + setattr(pyparsing_unicode, "ไทย", pyparsing_unicode.Thai) + setattr(pyparsing_unicode, "देवनागरी", pyparsing_unicode.Devanagari) + + +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") + columnSpec = ('*' | columnNameList) + + tableName = delimitedList(ident, ".", combine=True).setParseAction(upcaseTokens) + tableNameList = Group(delimitedList(tableName)).setName("tables") + + simpleSQL = selectToken("command") + columnSpec("columns") + fromToken + tableNameList("tables") + + # demo runTests method, including embedded comments in test string + simpleSQL.runTests(""" + # '*' as column list and dotted table name + select * from SYS.XYZZY + + # caseless match on "SELECT", and casts back to "select" + SELECT * from XYZZY, ABC + + # list of column names, and mixed case SELECT keyword + Select AA,BB,CC from Sys.dual + + # multiple tables + Select A, B, C from Sys.dual, Table2 + + # invalid SELECT keyword - should fail + Xelect A, B, C from Sys.dual + + # incomplete command - should fail + Select + + # invalid column name - should fail + Select ^^^ frox Sys.dual + + """) + + pyparsing_common.number.runTests(""" + 100 + -100 + +100 + 3.14159 + 6.02e23 + 1e-12 + """) + + # any int or real number, returned as float + pyparsing_common.fnumber.runTests(""" + 100 + -100 + +100 + 3.14159 + 6.02e23 + 1e-12 + """) + + pyparsing_common.hex_integer.runTests(""" + 100 + FF + """) + + import uuid + pyparsing_common.uuid.setParseAction(tokenMap(uuid.UUID)) + pyparsing_common.uuid.runTests(""" + 12345678-1234-5678-1234-567812345678 + """) diff --git a/venv2/lib/python2.7/site-packages/pip/_vendor/pytoml/__init__.py b/venv2/lib/python2.7/site-packages/pip/_vendor/pytoml/__init__.py new file mode 100644 index 0000000..8ed060f --- /dev/null +++ b/venv2/lib/python2.7/site-packages/pip/_vendor/pytoml/__init__.py @@ -0,0 +1,4 @@ +from .core import TomlError +from .parser import load, loads +from .test import translate_to_test +from .writer import dump, dumps \ No newline at end of file diff --git a/venv2/lib/python2.7/site-packages/pip/_vendor/pytoml/core.py b/venv2/lib/python2.7/site-packages/pip/_vendor/pytoml/core.py new file mode 100644 index 0000000..c182734 --- /dev/null +++ b/venv2/lib/python2.7/site-packages/pip/_vendor/pytoml/core.py @@ -0,0 +1,13 @@ +class TomlError(RuntimeError): + def __init__(self, message, line, col, filename): + RuntimeError.__init__(self, message, line, col, filename) + self.message = message + self.line = line + self.col = col + self.filename = filename + + def __str__(self): + return '{}({}, {}): {}'.format(self.filename, self.line, self.col, self.message) + + def __repr__(self): + return 'TomlError({!r}, {!r}, {!r}, {!r})'.format(self.message, self.line, self.col, self.filename) diff --git a/venv2/lib/python2.7/site-packages/pip/_vendor/pytoml/parser.py b/venv2/lib/python2.7/site-packages/pip/_vendor/pytoml/parser.py new file mode 100644 index 0000000..3493aa6 --- /dev/null +++ b/venv2/lib/python2.7/site-packages/pip/_vendor/pytoml/parser.py @@ -0,0 +1,341 @@ +import string, re, sys, datetime +from .core import TomlError +from .utils import rfc3339_re, parse_rfc3339_re + +if sys.version_info[0] == 2: + _chr = unichr +else: + _chr = chr + +def load(fin, translate=lambda t, x, v: v, object_pairs_hook=dict): + return loads(fin.read(), translate=translate, object_pairs_hook=object_pairs_hook, filename=getattr(fin, 'name', repr(fin))) + +def loads(s, filename='<string>', translate=lambda t, x, v: v, object_pairs_hook=dict): + if isinstance(s, bytes): + s = s.decode('utf-8') + + s = s.replace('\r\n', '\n') + + root = object_pairs_hook() + tables = object_pairs_hook() + scope = root + + src = _Source(s, filename=filename) + ast = _p_toml(src, object_pairs_hook=object_pairs_hook) + + def error(msg): + raise TomlError(msg, pos[0], pos[1], filename) + + def process_value(v, object_pairs_hook): + kind, text, value, pos = v + if kind == 'str' and value.startswith('\n'): + value = value[1:] + if kind == 'array': + if value and any(k != value[0][0] for k, t, v, p in value[1:]): + error('array-type-mismatch') + value = [process_value(item, object_pairs_hook=object_pairs_hook) for item in value] + elif kind == 'table': + value = object_pairs_hook([(k, process_value(value[k], object_pairs_hook=object_pairs_hook)) for k in value]) + return translate(kind, text, value) + + for kind, value, pos in ast: + if kind == 'kv': + k, v = value + if k in scope: + error('duplicate_keys. Key "{0}" was used more than once.'.format(k)) + scope[k] = process_value(v, object_pairs_hook=object_pairs_hook) + else: + is_table_array = (kind == 'table_array') + cur = tables + for name in value[:-1]: + if isinstance(cur.get(name), list): + d, cur = cur[name][-1] + else: + d, cur = cur.setdefault(name, (None, object_pairs_hook())) + + scope = object_pairs_hook() + name = value[-1] + if name not in cur: + if is_table_array: + cur[name] = [(scope, object_pairs_hook())] + else: + cur[name] = (scope, object_pairs_hook()) + elif isinstance(cur[name], list): + if not is_table_array: + error('table_type_mismatch') + cur[name].append((scope, object_pairs_hook())) + else: + if is_table_array: + error('table_type_mismatch') + old_scope, next_table = cur[name] + if old_scope is not None: + error('duplicate_tables') + cur[name] = (scope, next_table) + + def merge_tables(scope, tables): + if scope is None: + scope = object_pairs_hook() + for k in tables: + if k in scope: + error('key_table_conflict') + v = tables[k] + if isinstance(v, list): + scope[k] = [merge_tables(sc, tbl) for sc, tbl in v] + else: + scope[k] = merge_tables(v[0], v[1]) + return scope + + return merge_tables(root, tables) + +class _Source: + def __init__(self, s, filename=None): + self.s = s + self._pos = (1, 1) + self._last = None + self._filename = filename + self.backtrack_stack = [] + + def last(self): + return self._last + + def pos(self): + return self._pos + + def fail(self): + return self._expect(None) + + def consume_dot(self): + if self.s: + self._last = self.s[0] + self.s = self[1:] + self._advance(self._last) + return self._last + return None + + def expect_dot(self): + return self._expect(self.consume_dot()) + + def consume_eof(self): + if not self.s: + self._last = '' + return True + return False + + def expect_eof(self): + return self._expect(self.consume_eof()) + + def consume(self, s): + if self.s.startswith(s): + self.s = self.s[len(s):] + self._last = s + self._advance(s) + return True + return False + + def expect(self, s): + return self._expect(self.consume(s)) + + def consume_re(self, re): + m = re.match(self.s) + if m: + self.s = self.s[len(m.group(0)):] + self._last = m + self._advance(m.group(0)) + return m + return None + + def expect_re(self, re): + return self._expect(self.consume_re(re)) + + def __enter__(self): + self.backtrack_stack.append((self.s, self._pos)) + + def __exit__(self, type, value, traceback): + if type is None: + self.backtrack_stack.pop() + else: + self.s, self._pos = self.backtrack_stack.pop() + return type == TomlError + + def commit(self): + self.backtrack_stack[-1] = (self.s, self._pos) + + def _expect(self, r): + if not r: + raise TomlError('msg', self._pos[0], self._pos[1], self._filename) + return r + + def _advance(self, s): + suffix_pos = s.rfind('\n') + if suffix_pos == -1: + self._pos = (self._pos[0], self._pos[1] + len(s)) + else: + self._pos = (self._pos[0] + s.count('\n'), len(s) - suffix_pos) + +_ews_re = re.compile(r'(?:[ \t]|#[^\n]*\n|#[^\n]*\Z|\n)*') +def _p_ews(s): + s.expect_re(_ews_re) + +_ws_re = re.compile(r'[ \t]*') +def _p_ws(s): + s.expect_re(_ws_re) + +_escapes = { 'b': '\b', 'n': '\n', 'r': '\r', 't': '\t', '"': '"', + '\\': '\\', 'f': '\f' } + +_basicstr_re = re.compile(r'[^"\\\000-\037]*') +_short_uni_re = re.compile(r'u([0-9a-fA-F]{4})') +_long_uni_re = re.compile(r'U([0-9a-fA-F]{8})') +_escapes_re = re.compile(r'[btnfr\"\\]') +_newline_esc_re = re.compile('\n[ \t\n]*') +def _p_basicstr_content(s, content=_basicstr_re): + res = [] + while True: + res.append(s.expect_re(content).group(0)) + if not s.consume('\\'): + break + if s.consume_re(_newline_esc_re): + pass + elif s.consume_re(_short_uni_re) or s.consume_re(_long_uni_re): + v = int(s.last().group(1), 16) + if 0xd800 <= v < 0xe000: + s.fail() + res.append(_chr(v)) + else: + s.expect_re(_escapes_re) + res.append(_escapes[s.last().group(0)]) + return ''.join(res) + +_key_re = re.compile(r'[0-9a-zA-Z-_]+') +def _p_key(s): + with s: + s.expect('"') + r = _p_basicstr_content(s, _basicstr_re) + s.expect('"') + return r + if s.consume('\''): + if s.consume('\'\''): + r = s.expect_re(_litstr_ml_re).group(0) + s.expect('\'\'\'') + else: + r = s.expect_re(_litstr_re).group(0) + s.expect('\'') + return r + return s.expect_re(_key_re).group(0) + +_float_re = re.compile(r'[+-]?(?:0|[1-9](?:_?\d)*)(?:\.\d(?:_?\d)*)?(?:[eE][+-]?(?:\d(?:_?\d)*))?') + +_basicstr_ml_re = re.compile(r'(?:""?(?!")|[^"\\\000-\011\013-\037])*') +_litstr_re = re.compile(r"[^'\000\010\012-\037]*") +_litstr_ml_re = re.compile(r"(?:(?:|'|'')(?:[^'\000-\010\013-\037]))*") +def _p_value(s, object_pairs_hook): + pos = s.pos() + + if s.consume('true'): + return 'bool', s.last(), True, pos + if s.consume('false'): + return 'bool', s.last(), False, pos + + if s.consume('"'): + if s.consume('""'): + r = _p_basicstr_content(s, _basicstr_ml_re) + s.expect('"""') + else: + r = _p_basicstr_content(s, _basicstr_re) + s.expect('"') + return 'str', r, r, pos + + if s.consume('\''): + if s.consume('\'\''): + r = s.expect_re(_litstr_ml_re).group(0) + s.expect('\'\'\'') + else: + r = s.expect_re(_litstr_re).group(0) + s.expect('\'') + return 'str', r, r, pos + + if s.consume_re(rfc3339_re): + m = s.last() + return 'datetime', m.group(0), parse_rfc3339_re(m), pos + + if s.consume_re(_float_re): + m = s.last().group(0) + r = m.replace('_','') + if '.' in m or 'e' in m or 'E' in m: + return 'float', m, float(r), pos + else: + return 'int', m, int(r, 10), pos + + if s.consume('['): + items = [] + with s: + while True: + _p_ews(s) + items.append(_p_value(s, object_pairs_hook=object_pairs_hook)) + s.commit() + _p_ews(s) + s.expect(',') + s.commit() + _p_ews(s) + s.expect(']') + return 'array', None, items, pos + + if s.consume('{'): + _p_ws(s) + items = object_pairs_hook() + if not s.consume('}'): + k = _p_key(s) + _p_ws(s) + s.expect('=') + _p_ws(s) + items[k] = _p_value(s, object_pairs_hook=object_pairs_hook) + _p_ws(s) + while s.consume(','): + _p_ws(s) + k = _p_key(s) + _p_ws(s) + s.expect('=') + _p_ws(s) + items[k] = _p_value(s, object_pairs_hook=object_pairs_hook) + _p_ws(s) + s.expect('}') + return 'table', None, items, pos + + s.fail() + +def _p_stmt(s, object_pairs_hook): + pos = s.pos() + if s.consume( '['): + is_array = s.consume('[') + _p_ws(s) + keys = [_p_key(s)] + _p_ws(s) + while s.consume('.'): + _p_ws(s) + keys.append(_p_key(s)) + _p_ws(s) + s.expect(']') + if is_array: + s.expect(']') + return 'table_array' if is_array else 'table', keys, pos + + key = _p_key(s) + _p_ws(s) + s.expect('=') + _p_ws(s) + value = _p_value(s, object_pairs_hook=object_pairs_hook) + return 'kv', (key, value), pos + +_stmtsep_re = re.compile(r'(?:[ \t]*(?:#[^\n]*)?\n)+[ \t]*') +def _p_toml(s, object_pairs_hook): + stmts = [] + _p_ews(s) + with s: + stmts.append(_p_stmt(s, object_pairs_hook=object_pairs_hook)) + while True: + s.commit() + s.expect_re(_stmtsep_re) + stmts.append(_p_stmt(s, object_pairs_hook=object_pairs_hook)) + _p_ews(s) + s.expect_eof() + return stmts diff --git a/venv2/lib/python2.7/site-packages/pip/_vendor/pytoml/test.py b/venv2/lib/python2.7/site-packages/pip/_vendor/pytoml/test.py new file mode 100644 index 0000000..ec8abfc --- /dev/null +++ b/venv2/lib/python2.7/site-packages/pip/_vendor/pytoml/test.py @@ -0,0 +1,30 @@ +import datetime +from .utils import format_rfc3339 + +try: + _string_types = (str, unicode) + _int_types = (int, long) +except NameError: + _string_types = str + _int_types = int + +def translate_to_test(v): + if isinstance(v, dict): + return { k: translate_to_test(v) for k, v in v.items() } + if isinstance(v, list): + a = [translate_to_test(x) for x in v] + if v and isinstance(v[0], dict): + return a + else: + return {'type': 'array', 'value': a} + if isinstance(v, datetime.datetime): + return {'type': 'datetime', 'value': format_rfc3339(v)} + if isinstance(v, bool): + return {'type': 'bool', 'value': 'true' if v else 'false'} + if isinstance(v, _int_types): + return {'type': 'integer', 'value': str(v)} + if isinstance(v, float): + return {'type': 'float', 'value': '{:.17}'.format(v)} + if isinstance(v, _string_types): + return {'type': 'string', 'value': v} + raise RuntimeError('unexpected value: {!r}'.format(v)) diff --git a/venv2/lib/python2.7/site-packages/pip/_vendor/pytoml/utils.py b/venv2/lib/python2.7/site-packages/pip/_vendor/pytoml/utils.py new file mode 100644 index 0000000..636a680 --- /dev/null +++ b/venv2/lib/python2.7/site-packages/pip/_vendor/pytoml/utils.py @@ -0,0 +1,67 @@ +import datetime +import re + +rfc3339_re = re.compile(r'(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2})(\.\d+)?(?:Z|([+-]\d{2}):(\d{2}))') + +def parse_rfc3339(v): + m = rfc3339_re.match(v) + if not m or m.group(0) != v: + return None + return parse_rfc3339_re(m) + +def parse_rfc3339_re(m): + r = map(int, m.groups()[:6]) + if m.group(7): + micro = float(m.group(7)) + else: + micro = 0 + + if m.group(8): + g = int(m.group(8), 10) * 60 + int(m.group(9), 10) + tz = _TimeZone(datetime.timedelta(0, g * 60)) + else: + tz = _TimeZone(datetime.timedelta(0, 0)) + + y, m, d, H, M, S = r + return datetime.datetime(y, m, d, H, M, S, int(micro * 1000000), tz) + + +def format_rfc3339(v): + offs = v.utcoffset() + offs = int(offs.total_seconds()) // 60 if offs is not None else 0 + + if offs == 0: + suffix = 'Z' + else: + if offs > 0: + suffix = '+' + else: + suffix = '-' + offs = -offs + suffix = '{0}{1:02}:{2:02}'.format(suffix, offs // 60, offs % 60) + + if v.microsecond: + return v.strftime('%Y-%m-%dT%H:%M:%S.%f') + suffix + else: + return v.strftime('%Y-%m-%dT%H:%M:%S') + suffix + +class _TimeZone(datetime.tzinfo): + def __init__(self, offset): + self._offset = offset + + def utcoffset(self, dt): + return self._offset + + def dst(self, dt): + return None + + def tzname(self, dt): + m = self._offset.total_seconds() // 60 + if m < 0: + res = '-' + m = -m + else: + res = '+' + h = m // 60 + m = m - h * 60 + return '{}{:.02}{:.02}'.format(res, h, m) diff --git a/venv2/lib/python2.7/site-packages/pip/_vendor/pytoml/writer.py b/venv2/lib/python2.7/site-packages/pip/_vendor/pytoml/writer.py new file mode 100644 index 0000000..73b5089 --- /dev/null +++ b/venv2/lib/python2.7/site-packages/pip/_vendor/pytoml/writer.py @@ -0,0 +1,106 @@ +from __future__ import unicode_literals +import io, datetime, math, string, sys + +from .utils import format_rfc3339 + +if sys.version_info[0] == 3: + long = int + unicode = str + + +def dumps(obj, sort_keys=False): + fout = io.StringIO() + dump(obj, fout, sort_keys=sort_keys) + return fout.getvalue() + + +_escapes = {'\n': 'n', '\r': 'r', '\\': '\\', '\t': 't', '\b': 'b', '\f': 'f', '"': '"'} + + +def _escape_string(s): + res = [] + start = 0 + + def flush(): + if start != i: + res.append(s[start:i]) + return i + 1 + + i = 0 + while i < len(s): + c = s[i] + if c in '"\\\n\r\t\b\f': + start = flush() + res.append('\\' + _escapes[c]) + elif ord(c) < 0x20: + start = flush() + res.append('\\u%04x' % ord(c)) + i += 1 + + flush() + return '"' + ''.join(res) + '"' + + +_key_chars = string.digits + string.ascii_letters + '-_' +def _escape_id(s): + if any(c not in _key_chars for c in s): + return _escape_string(s) + return s + + +def _format_value(v): + if isinstance(v, bool): + return 'true' if v else 'false' + if isinstance(v, int) or isinstance(v, long): + return unicode(v) + if isinstance(v, float): + if math.isnan(v) or math.isinf(v): + raise ValueError("{0} is not a valid TOML value".format(v)) + else: + return repr(v) + elif isinstance(v, unicode) or isinstance(v, bytes): + return _escape_string(v) + elif isinstance(v, datetime.datetime): + return format_rfc3339(v) + elif isinstance(v, list): + return '[{0}]'.format(', '.join(_format_value(obj) for obj in v)) + elif isinstance(v, dict): + return '{{{0}}}'.format(', '.join('{} = {}'.format(_escape_id(k), _format_value(obj)) for k, obj in v.items())) + else: + raise RuntimeError(v) + + +def dump(obj, fout, sort_keys=False): + tables = [((), obj, False)] + + while tables: + name, table, is_array = tables.pop() + if name: + section_name = '.'.join(_escape_id(c) for c in name) + if is_array: + fout.write('[[{0}]]\n'.format(section_name)) + else: + fout.write('[{0}]\n'.format(section_name)) + + table_keys = sorted(table.keys()) if sort_keys else table.keys() + new_tables = [] + has_kv = False + for k in table_keys: + v = table[k] + if isinstance(v, dict): + new_tables.append((name + (k,), v, False)) + elif isinstance(v, list) and v and all(isinstance(o, dict) for o in v): + new_tables.extend((name + (k,), d, True) for d in v) + elif v is None: + # based on mojombo's comment: https://github.com/toml-lang/toml/issues/146#issuecomment-25019344 + fout.write( + '#{} = null # To use: uncomment and replace null with value\n'.format(_escape_id(k))) + has_kv = True + else: + fout.write('{0} = {1}\n'.format(_escape_id(k), _format_value(v))) + has_kv = True + + tables.extend(reversed(new_tables)) + + if (name or has_kv) and tables: + fout.write('\n') diff --git a/venv2/lib/python2.7/site-packages/pip/_vendor/requests/__init__.py b/venv2/lib/python2.7/site-packages/pip/_vendor/requests/__init__.py new file mode 100644 index 0000000..80c4ce1 --- /dev/null +++ b/venv2/lib/python2.7/site-packages/pip/_vendor/requests/__init__.py @@ -0,0 +1,133 @@ +# -*- coding: utf-8 -*- + +# __ +# /__) _ _ _ _ _/ _ +# / ( (- (/ (/ (- _) / _) +# / + +""" +Requests HTTP Library +~~~~~~~~~~~~~~~~~~~~~ + +Requests is an HTTP library, written in Python, for human beings. Basic GET +usage: + + >>> import requests + >>> r = requests.get('https://www.python.org') + >>> r.status_code + 200 + >>> 'Python is a programming language' in r.content + True + +... or POST: + + >>> payload = dict(key1='value1', key2='value2') + >>> r = requests.post('https://httpbin.org/post', data=payload) + >>> print(r.text) + { + ... + "form": { + "key2": "value2", + "key1": "value1" + }, + ... + } + +The other HTTP methods are supported - see `requests.api`. Full documentation +is at <http://python-requests.org>. + +:copyright: (c) 2017 by Kenneth Reitz. +:license: Apache 2.0, see LICENSE for more details. +""" + +from pip._vendor import urllib3 +from pip._vendor import chardet +import warnings +from .exceptions import RequestsDependencyWarning + + +def check_compatibility(urllib3_version, chardet_version): + urllib3_version = urllib3_version.split('.') + assert urllib3_version != ['dev'] # Verify urllib3 isn't installed from git. + + # Sometimes, urllib3 only reports its version as 16.1. + if len(urllib3_version) == 2: + urllib3_version.append('0') + + # Check urllib3 for compatibility. + major, minor, patch = urllib3_version # noqa: F811 + major, minor, patch = int(major), int(minor), int(patch) + # urllib3 >= 1.21.1, <= 1.24 + assert major == 1 + assert minor >= 21 + assert minor <= 24 + + # Check chardet for compatibility. + major, minor, patch = chardet_version.split('.')[:3] + major, minor, patch = int(major), int(minor), int(patch) + # chardet >= 3.0.2, < 3.1.0 + assert major == 3 + assert minor < 1 + assert patch >= 2 + + +def _check_cryptography(cryptography_version): + # cryptography < 1.3.4 + try: + cryptography_version = list(map(int, cryptography_version.split('.'))) + except ValueError: + return + + if cryptography_version < [1, 3, 4]: + warning = 'Old version of cryptography ({}) may cause slowdown.'.format(cryptography_version) + warnings.warn(warning, RequestsDependencyWarning) + +# Check imported dependencies for compatibility. +try: + check_compatibility(urllib3.__version__, chardet.__version__) +except (AssertionError, ValueError): + warnings.warn("urllib3 ({}) or chardet ({}) doesn't match a supported " + "version!".format(urllib3.__version__, chardet.__version__), + RequestsDependencyWarning) + +# Attempt to enable urllib3's SNI support, if possible +from pip._internal.utils.compat import WINDOWS +if not WINDOWS: + try: + from pip._vendor.urllib3.contrib import pyopenssl + pyopenssl.inject_into_urllib3() + + # Check cryptography version + from cryptography import __version__ as cryptography_version + _check_cryptography(cryptography_version) + except ImportError: + pass + +# urllib3's DependencyWarnings should be silenced. +from pip._vendor.urllib3.exceptions import DependencyWarning +warnings.simplefilter('ignore', DependencyWarning) + +from .__version__ import __title__, __description__, __url__, __version__ +from .__version__ import __build__, __author__, __author_email__, __license__ +from .__version__ import __copyright__, __cake__ + +from . import utils +from . import packages +from .models import Request, Response, PreparedRequest +from .api import request, get, head, post, patch, put, delete, options +from .sessions import session, Session +from .status_codes import codes +from .exceptions import ( + RequestException, Timeout, URLRequired, + TooManyRedirects, HTTPError, ConnectionError, + FileModeWarning, ConnectTimeout, ReadTimeout +) + +# Set default logging handler to avoid "No handler found" warnings. +import logging +from logging import NullHandler + +logging.getLogger(__name__).addHandler(NullHandler()) + +# FileModeWarnings go off per the default. +warnings.simplefilter('default', FileModeWarning, append=True) diff --git a/venv2/lib/python2.7/site-packages/pip/_vendor/requests/__version__.py b/venv2/lib/python2.7/site-packages/pip/_vendor/requests/__version__.py new file mode 100644 index 0000000..f5b5d03 --- /dev/null +++ b/venv2/lib/python2.7/site-packages/pip/_vendor/requests/__version__.py @@ -0,0 +1,14 @@ +# .-. .-. .-. . . .-. .-. .-. .-. +# |( |- |.| | | |- `-. | `-. +# ' ' `-' `-`.`-' `-' `-' ' `-' + +__title__ = 'requests' +__description__ = 'Python HTTP for Humans.' +__url__ = 'http://python-requests.org' +__version__ = '2.21.0' +__build__ = 0x022100 +__author__ = 'Kenneth Reitz' +__author_email__ = 'me@kennethreitz.org' +__license__ = 'Apache 2.0' +__copyright__ = 'Copyright 2018 Kenneth Reitz' +__cake__ = u'\u2728 \U0001f370 \u2728' diff --git a/venv2/lib/python2.7/site-packages/pip/_vendor/requests/_internal_utils.py b/venv2/lib/python2.7/site-packages/pip/_vendor/requests/_internal_utils.py new file mode 100644 index 0000000..759d9a5 --- /dev/null +++ b/venv2/lib/python2.7/site-packages/pip/_vendor/requests/_internal_utils.py @@ -0,0 +1,42 @@ +# -*- coding: utf-8 -*- + +""" +requests._internal_utils +~~~~~~~~~~~~~~ + +Provides utility functions that are consumed internally by Requests +which depend on extremely few external helpers (such as compat) +""" + +from .compat import is_py2, builtin_str, str + + +def to_native_string(string, encoding='ascii'): + """Given a string object, regardless of type, returns a representation of + that string in the native string type, encoding and decoding where + necessary. This assumes ASCII unless told otherwise. + """ + if isinstance(string, builtin_str): + out = string + else: + if is_py2: + out = string.encode(encoding) + else: + out = string.decode(encoding) + + return out + + +def unicode_is_ascii(u_string): + """Determine if unicode string only contains ASCII characters. + + :param str u_string: unicode string to check. Must be unicode + and not Python 2 `str`. + :rtype: bool + """ + assert isinstance(u_string, str) + try: + u_string.encode('ascii') + return True + except UnicodeEncodeError: + return False diff --git a/venv2/lib/python2.7/site-packages/pip/_vendor/requests/adapters.py b/venv2/lib/python2.7/site-packages/pip/_vendor/requests/adapters.py new file mode 100644 index 0000000..c30e7c9 --- /dev/null +++ b/venv2/lib/python2.7/site-packages/pip/_vendor/requests/adapters.py @@ -0,0 +1,533 @@ +# -*- coding: utf-8 -*- + +""" +requests.adapters +~~~~~~~~~~~~~~~~~ + +This module contains the transport adapters that Requests uses to define +and maintain connections. +""" + +import os.path +import socket + +from pip._vendor.urllib3.poolmanager import PoolManager, proxy_from_url +from pip._vendor.urllib3.response import HTTPResponse +from pip._vendor.urllib3.util import parse_url +from pip._vendor.urllib3.util import Timeout as TimeoutSauce +from pip._vendor.urllib3.util.retry import Retry +from pip._vendor.urllib3.exceptions import ClosedPoolError +from pip._vendor.urllib3.exceptions import ConnectTimeoutError +from pip._vendor.urllib3.exceptions import HTTPError as _HTTPError +from pip._vendor.urllib3.exceptions import MaxRetryError +from pip._vendor.urllib3.exceptions import NewConnectionError +from pip._vendor.urllib3.exceptions import ProxyError as _ProxyError +from pip._vendor.urllib3.exceptions import ProtocolError +from pip._vendor.urllib3.exceptions import ReadTimeoutError +from pip._vendor.urllib3.exceptions import SSLError as _SSLError +from pip._vendor.urllib3.exceptions import ResponseError +from pip._vendor.urllib3.exceptions import LocationValueError + +from .models import Response +from .compat import urlparse, basestring +from .utils import (DEFAULT_CA_BUNDLE_PATH, extract_zipped_paths, + get_encoding_from_headers, prepend_scheme_if_needed, + get_auth_from_url, urldefragauth, select_proxy) +from .structures import CaseInsensitiveDict +from .cookies import extract_cookies_to_jar +from .exceptions import (ConnectionError, ConnectTimeout, ReadTimeout, SSLError, + ProxyError, RetryError, InvalidSchema, InvalidProxyURL, + InvalidURL) +from .auth import _basic_auth_str + +try: + from pip._vendor.urllib3.contrib.socks import SOCKSProxyManager +except ImportError: + def SOCKSProxyManager(*args, **kwargs): + raise InvalidSchema("Missing dependencies for SOCKS support.") + +DEFAULT_POOLBLOCK = False +DEFAULT_POOLSIZE = 10 +DEFAULT_RETRIES = 0 +DEFAULT_POOL_TIMEOUT = None + + +class BaseAdapter(object): + """The Base Transport Adapter""" + + def __init__(self): + super(BaseAdapter, self).__init__() + + def send(self, request, stream=False, timeout=None, verify=True, + cert=None, proxies=None): + """Sends PreparedRequest object. Returns Response object. + + :param request: The :class:`PreparedRequest <PreparedRequest>` being sent. + :param stream: (optional) Whether to stream the request content. + :param timeout: (optional) How long to wait for the server to send + data before giving up, as a float, or a :ref:`(connect timeout, + read timeout) <timeouts>` tuple. + :type timeout: float or tuple + :param verify: (optional) Either a boolean, in which case it controls whether we verify + the server's TLS certificate, or a string, in which case it must be a path + to a CA bundle to use + :param cert: (optional) Any user-provided SSL certificate to be trusted. + :param proxies: (optional) The proxies dictionary to apply to the request. + """ + raise NotImplementedError + + def close(self): + """Cleans up adapter specific items.""" + raise NotImplementedError + + +class HTTPAdapter(BaseAdapter): + """The built-in HTTP Adapter for urllib3. + + Provides a general-case interface for Requests sessions to contact HTTP and + HTTPS urls by implementing the Transport Adapter interface. This class will + usually be created by the :class:`Session <Session>` class under the + covers. + + :param pool_connections: The number of urllib3 connection pools to cache. + :param pool_maxsize: The maximum number of connections to save in the pool. + :param max_retries: The maximum number of retries each connection + should attempt. Note, this applies only to failed DNS lookups, socket + connections and connection timeouts, never to requests where data has + made it to the server. By default, Requests does not retry failed + connections. If you need granular control over the conditions under + which we retry a request, import urllib3's ``Retry`` class and pass + that instead. + :param pool_block: Whether the connection pool should block for connections. + + Usage:: + + >>> import requests + >>> s = requests.Session() + >>> a = requests.adapters.HTTPAdapter(max_retries=3) + >>> s.mount('http://', a) + """ + __attrs__ = ['max_retries', 'config', '_pool_connections', '_pool_maxsize', + '_pool_block'] + + def __init__(self, pool_connections=DEFAULT_POOLSIZE, + pool_maxsize=DEFAULT_POOLSIZE, max_retries=DEFAULT_RETRIES, + pool_block=DEFAULT_POOLBLOCK): + if max_retries == DEFAULT_RETRIES: + self.max_retries = Retry(0, read=False) + else: + self.max_retries = Retry.from_int(max_retries) + self.config = {} + self.proxy_manager = {} + + super(HTTPAdapter, self).__init__() + + self._pool_connections = pool_connections + self._pool_maxsize = pool_maxsize + self._pool_block = pool_block + + self.init_poolmanager(pool_connections, pool_maxsize, block=pool_block) + + def __getstate__(self): + return {attr: getattr(self, attr, None) for attr in self.__attrs__} + + def __setstate__(self, state): + # Can't handle by adding 'proxy_manager' to self.__attrs__ because + # self.poolmanager uses a lambda function, which isn't pickleable. + self.proxy_manager = {} + self.config = {} + + for attr, value in state.items(): + setattr(self, attr, value) + + self.init_poolmanager(self._pool_connections, self._pool_maxsize, + block=self._pool_block) + + def init_poolmanager(self, connections, maxsize, block=DEFAULT_POOLBLOCK, **pool_kwargs): + """Initializes a urllib3 PoolManager. + + This method should not be called from user code, and is only + exposed for use when subclassing the + :class:`HTTPAdapter <requests.adapters.HTTPAdapter>`. + + :param connections: The number of urllib3 connection pools to cache. + :param maxsize: The maximum number of connections to save in the pool. + :param block: Block when no free connections are available. + :param pool_kwargs: Extra keyword arguments used to initialize the Pool Manager. + """ + # save these values for pickling + self._pool_connections = connections + self._pool_maxsize = maxsize + self._pool_block = block + + self.poolmanager = PoolManager(num_pools=connections, maxsize=maxsize, + block=block, strict=True, **pool_kwargs) + + def proxy_manager_for(self, proxy, **proxy_kwargs): + """Return urllib3 ProxyManager for the given proxy. + + This method should not be called from user code, and is only + exposed for use when subclassing the + :class:`HTTPAdapter <requests.adapters.HTTPAdapter>`. + + :param proxy: The proxy to return a urllib3 ProxyManager for. + :param proxy_kwargs: Extra keyword arguments used to configure the Proxy Manager. + :returns: ProxyManager + :rtype: urllib3.ProxyManager + """ + if proxy in self.proxy_manager: + manager = self.proxy_manager[proxy] + elif proxy.lower().startswith('socks'): + username, password = get_auth_from_url(proxy) + manager = self.proxy_manager[proxy] = SOCKSProxyManager( + proxy, + username=username, + password=password, + num_pools=self._pool_connections, + maxsize=self._pool_maxsize, + block=self._pool_block, + **proxy_kwargs + ) + else: + proxy_headers = self.proxy_headers(proxy) + manager = self.proxy_manager[proxy] = proxy_from_url( + proxy, + proxy_headers=proxy_headers, + num_pools=self._pool_connections, + maxsize=self._pool_maxsize, + block=self._pool_block, + **proxy_kwargs) + + return manager + + def cert_verify(self, conn, url, verify, cert): + """Verify a SSL certificate. This method should not be called from user + code, and is only exposed for use when subclassing the + :class:`HTTPAdapter <requests.adapters.HTTPAdapter>`. + + :param conn: The urllib3 connection object associated with the cert. + :param url: The requested URL. + :param verify: Either a boolean, in which case it controls whether we verify + the server's TLS certificate, or a string, in which case it must be a path + to a CA bundle to use + :param cert: The SSL certificate to verify. + """ + if url.lower().startswith('https') and verify: + + cert_loc = None + + # Allow self-specified cert location. + if verify is not True: + cert_loc = verify + + if not cert_loc: + cert_loc = extract_zipped_paths(DEFAULT_CA_BUNDLE_PATH) + + if not cert_loc or not os.path.exists(cert_loc): + raise IOError("Could not find a suitable TLS CA certificate bundle, " + "invalid path: {}".format(cert_loc)) + + conn.cert_reqs = 'CERT_REQUIRED' + + if not os.path.isdir(cert_loc): + conn.ca_certs = cert_loc + else: + conn.ca_cert_dir = cert_loc + else: + conn.cert_reqs = 'CERT_NONE' + conn.ca_certs = None + conn.ca_cert_dir = None + + if cert: + if not isinstance(cert, basestring): + conn.cert_file = cert[0] + conn.key_file = cert[1] + else: + conn.cert_file = cert + conn.key_file = None + if conn.cert_file and not os.path.exists(conn.cert_file): + raise IOError("Could not find the TLS certificate file, " + "invalid path: {}".format(conn.cert_file)) + if conn.key_file and not os.path.exists(conn.key_file): + raise IOError("Could not find the TLS key file, " + "invalid path: {}".format(conn.key_file)) + + def build_response(self, req, resp): + """Builds a :class:`Response <requests.Response>` object from a urllib3 + response. This should not be called from user code, and is only exposed + for use when subclassing the + :class:`HTTPAdapter <requests.adapters.HTTPAdapter>` + + :param req: The :class:`PreparedRequest <PreparedRequest>` used to generate the response. + :param resp: The urllib3 response object. + :rtype: requests.Response + """ + response = Response() + + # Fallback to None if there's no status_code, for whatever reason. + response.status_code = getattr(resp, 'status', None) + + # Make headers case-insensitive. + response.headers = CaseInsensitiveDict(getattr(resp, 'headers', {})) + + # Set encoding. + response.encoding = get_encoding_from_headers(response.headers) + response.raw = resp + response.reason = response.raw.reason + + if isinstance(req.url, bytes): + response.url = req.url.decode('utf-8') + else: + response.url = req.url + + # Add new cookies from the server. + extract_cookies_to_jar(response.cookies, req, resp) + + # Give the Response some context. + response.request = req + response.connection = self + + return response + + def get_connection(self, url, proxies=None): + """Returns a urllib3 connection for the given URL. This should not be + called from user code, and is only exposed for use when subclassing the + :class:`HTTPAdapter <requests.adapters.HTTPAdapter>`. + + :param url: The URL to connect to. + :param proxies: (optional) A Requests-style dictionary of proxies used on this request. + :rtype: urllib3.ConnectionPool + """ + proxy = select_proxy(url, proxies) + + if proxy: + proxy = prepend_scheme_if_needed(proxy, 'http') + proxy_url = parse_url(proxy) + if not proxy_url.host: + raise InvalidProxyURL("Please check proxy URL. It is malformed" + " and could be missing the host.") + proxy_manager = self.proxy_manager_for(proxy) + conn = proxy_manager.connection_from_url(url) + else: + # Only scheme should be lower case + parsed = urlparse(url) + url = parsed.geturl() + conn = self.poolmanager.connection_from_url(url) + + return conn + + def close(self): + """Disposes of any internal state. + + Currently, this closes the PoolManager and any active ProxyManager, + which closes any pooled connections. + """ + self.poolmanager.clear() + for proxy in self.proxy_manager.values(): + proxy.clear() + + def request_url(self, request, proxies): + """Obtain the url to use when making the final request. + + If the message is being sent through a HTTP proxy, the full URL has to + be used. Otherwise, we should only use the path portion of the URL. + + This should not be called from user code, and is only exposed for use + when subclassing the + :class:`HTTPAdapter <requests.adapters.HTTPAdapter>`. + + :param request: The :class:`PreparedRequest <PreparedRequest>` being sent. + :param proxies: A dictionary of schemes or schemes and hosts to proxy URLs. + :rtype: str + """ + proxy = select_proxy(request.url, proxies) + scheme = urlparse(request.url).scheme + + is_proxied_http_request = (proxy and scheme != 'https') + using_socks_proxy = False + if proxy: + proxy_scheme = urlparse(proxy).scheme.lower() + using_socks_proxy = proxy_scheme.startswith('socks') + + url = request.path_url + if is_proxied_http_request and not using_socks_proxy: + url = urldefragauth(request.url) + + return url + + def add_headers(self, request, **kwargs): + """Add any headers needed by the connection. As of v2.0 this does + nothing by default, but is left for overriding by users that subclass + the :class:`HTTPAdapter <requests.adapters.HTTPAdapter>`. + + This should not be called from user code, and is only exposed for use + when subclassing the + :class:`HTTPAdapter <requests.adapters.HTTPAdapter>`. + + :param request: The :class:`PreparedRequest <PreparedRequest>` to add headers to. + :param kwargs: The keyword arguments from the call to send(). + """ + pass + + def proxy_headers(self, proxy): + """Returns a dictionary of the headers to add to any request sent + through a proxy. This works with urllib3 magic to ensure that they are + correctly sent to the proxy, rather than in a tunnelled request if + CONNECT is being used. + + This should not be called from user code, and is only exposed for use + when subclassing the + :class:`HTTPAdapter <requests.adapters.HTTPAdapter>`. + + :param proxy: The url of the proxy being used for this request. + :rtype: dict + """ + headers = {} + username, password = get_auth_from_url(proxy) + + if username: + headers['Proxy-Authorization'] = _basic_auth_str(username, + password) + + return headers + + def send(self, request, stream=False, timeout=None, verify=True, cert=None, proxies=None): + """Sends PreparedRequest object. Returns Response object. + + :param request: The :class:`PreparedRequest <PreparedRequest>` being sent. + :param stream: (optional) Whether to stream the request content. + :param timeout: (optional) How long to wait for the server to send + data before giving up, as a float, or a :ref:`(connect timeout, + read timeout) <timeouts>` tuple. + :type timeout: float or tuple or urllib3 Timeout object + :param verify: (optional) Either a boolean, in which case it controls whether + we verify the server's TLS certificate, or a string, in which case it + must be a path to a CA bundle to use + :param cert: (optional) Any user-provided SSL certificate to be trusted. + :param proxies: (optional) The proxies dictionary to apply to the request. + :rtype: requests.Response + """ + + try: + conn = self.get_connection(request.url, proxies) + except LocationValueError as e: + raise InvalidURL(e, request=request) + + self.cert_verify(conn, request.url, verify, cert) + url = self.request_url(request, proxies) + self.add_headers(request, stream=stream, timeout=timeout, verify=verify, cert=cert, proxies=proxies) + + chunked = not (request.body is None or 'Content-Length' in request.headers) + + if isinstance(timeout, tuple): + try: + connect, read = timeout + timeout = TimeoutSauce(connect=connect, read=read) + except ValueError as e: + # this may raise a string formatting error. + err = ("Invalid timeout {}. Pass a (connect, read) " + "timeout tuple, or a single float to set " + "both timeouts to the same value".format(timeout)) + raise ValueError(err) + elif isinstance(timeout, TimeoutSauce): + pass + else: + timeout = TimeoutSauce(connect=timeout, read=timeout) + + try: + if not chunked: + resp = conn.urlopen( + method=request.method, + url=url, + body=request.body, + headers=request.headers, + redirect=False, + assert_same_host=False, + preload_content=False, + decode_content=False, + retries=self.max_retries, + timeout=timeout + ) + + # Send the request. + else: + if hasattr(conn, 'proxy_pool'): + conn = conn.proxy_pool + + low_conn = conn._get_conn(timeout=DEFAULT_POOL_TIMEOUT) + + try: + low_conn.putrequest(request.method, + url, + skip_accept_encoding=True) + + for header, value in request.headers.items(): + low_conn.putheader(header, value) + + low_conn.endheaders() + + for i in request.body: + low_conn.send(hex(len(i))[2:].encode('utf-8')) + low_conn.send(b'\r\n') + low_conn.send(i) + low_conn.send(b'\r\n') + low_conn.send(b'0\r\n\r\n') + + # Receive the response from the server + try: + # For Python 2.7, use buffering of HTTP responses + r = low_conn.getresponse(buffering=True) + except TypeError: + # For compatibility with Python 3.3+ + r = low_conn.getresponse() + + resp = HTTPResponse.from_httplib( + r, + pool=conn, + connection=low_conn, + preload_content=False, + decode_content=False + ) + except: + # If we hit any problems here, clean up the connection. + # Then, reraise so that we can handle the actual exception. + low_conn.close() + raise + + except (ProtocolError, socket.error) as err: + raise ConnectionError(err, request=request) + + except MaxRetryError as e: + if isinstance(e.reason, ConnectTimeoutError): + # TODO: Remove this in 3.0.0: see #2811 + if not isinstance(e.reason, NewConnectionError): + raise ConnectTimeout(e, request=request) + + if isinstance(e.reason, ResponseError): + raise RetryError(e, request=request) + + if isinstance(e.reason, _ProxyError): + raise ProxyError(e, request=request) + + if isinstance(e.reason, _SSLError): + # This branch is for urllib3 v1.22 and later. + raise SSLError(e, request=request) + + raise ConnectionError(e, request=request) + + except ClosedPoolError as e: + raise ConnectionError(e, request=request) + + except _ProxyError as e: + raise ProxyError(e) + + except (_SSLError, _HTTPError) as e: + if isinstance(e, _SSLError): + # This branch is for urllib3 versions earlier than v1.22 + raise SSLError(e, request=request) + elif isinstance(e, ReadTimeoutError): + raise ReadTimeout(e, request=request) + else: + raise + + return self.build_response(request, resp) diff --git a/venv2/lib/python2.7/site-packages/pip/_vendor/requests/api.py b/venv2/lib/python2.7/site-packages/pip/_vendor/requests/api.py new file mode 100644 index 0000000..abada96 --- /dev/null +++ b/venv2/lib/python2.7/site-packages/pip/_vendor/requests/api.py @@ -0,0 +1,158 @@ +# -*- coding: utf-8 -*- + +""" +requests.api +~~~~~~~~~~~~ + +This module implements the Requests API. + +:copyright: (c) 2012 by Kenneth Reitz. +:license: Apache2, see LICENSE for more details. +""" + +from . import sessions + + +def request(method, url, **kwargs): + """Constructs and sends a :class:`Request <Request>`. + + :param method: method for the new :class:`Request` object. + :param url: URL for the new :class:`Request` object. + :param params: (optional) Dictionary, list of tuples or bytes to send + in the body of the :class:`Request`. + :param data: (optional) Dictionary, list of tuples, bytes, or file-like + object to send in the body of the :class:`Request`. + :param json: (optional) A JSON serializable Python object to send in the body of the :class:`Request`. + :param headers: (optional) Dictionary of HTTP Headers to send with the :class:`Request`. + :param cookies: (optional) Dict or CookieJar object to send with the :class:`Request`. + :param files: (optional) Dictionary of ``'name': file-like-objects`` (or ``{'name': file-tuple}``) for multipart encoding upload. + ``file-tuple`` can be a 2-tuple ``('filename', fileobj)``, 3-tuple ``('filename', fileobj, 'content_type')`` + or a 4-tuple ``('filename', fileobj, 'content_type', custom_headers)``, where ``'content-type'`` is a string + defining the content type of the given file and ``custom_headers`` a dict-like object containing additional headers + to add for the file. + :param auth: (optional) Auth tuple to enable Basic/Digest/Custom HTTP Auth. + :param timeout: (optional) How many seconds to wait for the server to send data + before giving up, as a float, or a :ref:`(connect timeout, read + timeout) <timeouts>` tuple. + :type timeout: float or tuple + :param allow_redirects: (optional) Boolean. Enable/disable GET/OPTIONS/POST/PUT/PATCH/DELETE/HEAD redirection. Defaults to ``True``. + :type allow_redirects: bool + :param proxies: (optional) Dictionary mapping protocol to the URL of the proxy. + :param verify: (optional) Either a boolean, in which case it controls whether we verify + the server's TLS certificate, or a string, in which case it must be a path + to a CA bundle to use. Defaults to ``True``. + :param stream: (optional) if ``False``, the response content will be immediately downloaded. + :param cert: (optional) if String, path to ssl client cert file (.pem). If Tuple, ('cert', 'key') pair. + :return: :class:`Response <Response>` object + :rtype: requests.Response + + Usage:: + + >>> import requests + >>> req = requests.request('GET', 'https://httpbin.org/get') + <Response [200]> + """ + + # By using the 'with' statement we are sure the session is closed, thus we + # avoid leaving sockets open which can trigger a ResourceWarning in some + # cases, and look like a memory leak in others. + with sessions.Session() as session: + return session.request(method=method, url=url, **kwargs) + + +def get(url, params=None, **kwargs): + r"""Sends a GET request. + + :param url: URL for the new :class:`Request` object. + :param params: (optional) Dictionary, list of tuples or bytes to send + in the body of the :class:`Request`. + :param \*\*kwargs: Optional arguments that ``request`` takes. + :return: :class:`Response <Response>` object + :rtype: requests.Response + """ + + kwargs.setdefault('allow_redirects', True) + return request('get', url, params=params, **kwargs) + + +def options(url, **kwargs): + r"""Sends an OPTIONS request. + + :param url: URL for the new :class:`Request` object. + :param \*\*kwargs: Optional arguments that ``request`` takes. + :return: :class:`Response <Response>` object + :rtype: requests.Response + """ + + kwargs.setdefault('allow_redirects', True) + return request('options', url, **kwargs) + + +def head(url, **kwargs): + r"""Sends a HEAD request. + + :param url: URL for the new :class:`Request` object. + :param \*\*kwargs: Optional arguments that ``request`` takes. + :return: :class:`Response <Response>` object + :rtype: requests.Response + """ + + kwargs.setdefault('allow_redirects', False) + return request('head', url, **kwargs) + + +def post(url, data=None, json=None, **kwargs): + r"""Sends a POST request. + + :param url: URL for the new :class:`Request` object. + :param data: (optional) Dictionary, list of tuples, bytes, or file-like + object to send in the body of the :class:`Request`. + :param json: (optional) json data to send in the body of the :class:`Request`. + :param \*\*kwargs: Optional arguments that ``request`` takes. + :return: :class:`Response <Response>` object + :rtype: requests.Response + """ + + return request('post', url, data=data, json=json, **kwargs) + + +def put(url, data=None, **kwargs): + r"""Sends a PUT request. + + :param url: URL for the new :class:`Request` object. + :param data: (optional) Dictionary, list of tuples, bytes, or file-like + object to send in the body of the :class:`Request`. + :param json: (optional) json data to send in the body of the :class:`Request`. + :param \*\*kwargs: Optional arguments that ``request`` takes. + :return: :class:`Response <Response>` object + :rtype: requests.Response + """ + + return request('put', url, data=data, **kwargs) + + +def patch(url, data=None, **kwargs): + r"""Sends a PATCH request. + + :param url: URL for the new :class:`Request` object. + :param data: (optional) Dictionary, list of tuples, bytes, or file-like + object to send in the body of the :class:`Request`. + :param json: (optional) json data to send in the body of the :class:`Request`. + :param \*\*kwargs: Optional arguments that ``request`` takes. + :return: :class:`Response <Response>` object + :rtype: requests.Response + """ + + return request('patch', url, data=data, **kwargs) + + +def delete(url, **kwargs): + r"""Sends a DELETE request. + + :param url: URL for the new :class:`Request` object. + :param \*\*kwargs: Optional arguments that ``request`` takes. + :return: :class:`Response <Response>` object + :rtype: requests.Response + """ + + return request('delete', url, **kwargs) diff --git a/venv2/lib/python2.7/site-packages/pip/_vendor/requests/auth.py b/venv2/lib/python2.7/site-packages/pip/_vendor/requests/auth.py new file mode 100644 index 0000000..bdde51c --- /dev/null +++ b/venv2/lib/python2.7/site-packages/pip/_vendor/requests/auth.py @@ -0,0 +1,305 @@ +# -*- coding: utf-8 -*- + +""" +requests.auth +~~~~~~~~~~~~~ + +This module contains the authentication handlers for Requests. +""" + +import os +import re +import time +import hashlib +import threading +import warnings + +from base64 import b64encode + +from .compat import urlparse, str, basestring +from .cookies import extract_cookies_to_jar +from ._internal_utils import to_native_string +from .utils import parse_dict_header + +CONTENT_TYPE_FORM_URLENCODED = 'application/x-www-form-urlencoded' +CONTENT_TYPE_MULTI_PART = 'multipart/form-data' + + +def _basic_auth_str(username, password): + """Returns a Basic Auth string.""" + + # "I want us to put a big-ol' comment on top of it that + # says that this behaviour is dumb but we need to preserve + # it because people are relying on it." + # - Lukasa + # + # These are here solely to maintain backwards compatibility + # for things like ints. This will be removed in 3.0.0. + if not isinstance(username, basestring): + warnings.warn( + "Non-string usernames will no longer be supported in Requests " + "3.0.0. Please convert the object you've passed in ({!r}) to " + "a string or bytes object in the near future to avoid " + "problems.".format(username), + category=DeprecationWarning, + ) + username = str(username) + + if not isinstance(password, basestring): + warnings.warn( + "Non-string passwords will no longer be supported in Requests " + "3.0.0. Please convert the object you've passed in ({!r}) to " + "a string or bytes object in the near future to avoid " + "problems.".format(password), + category=DeprecationWarning, + ) + password = str(password) + # -- End Removal -- + + if isinstance(username, str): + username = username.encode('latin1') + + if isinstance(password, str): + password = password.encode('latin1') + + authstr = 'Basic ' + to_native_string( + b64encode(b':'.join((username, password))).strip() + ) + + return authstr + + +class AuthBase(object): + """Base class that all auth implementations derive from""" + + def __call__(self, r): + raise NotImplementedError('Auth hooks must be callable.') + + +class HTTPBasicAuth(AuthBase): + """Attaches HTTP Basic Authentication to the given Request object.""" + + def __init__(self, username, password): + self.username = username + self.password = password + + def __eq__(self, other): + return all([ + self.username == getattr(other, 'username', None), + self.password == getattr(other, 'password', None) + ]) + + def __ne__(self, other): + return not self == other + + def __call__(self, r): + r.headers['Authorization'] = _basic_auth_str(self.username, self.password) + return r + + +class HTTPProxyAuth(HTTPBasicAuth): + """Attaches HTTP Proxy Authentication to a given Request object.""" + + def __call__(self, r): + r.headers['Proxy-Authorization'] = _basic_auth_str(self.username, self.password) + return r + + +class HTTPDigestAuth(AuthBase): + """Attaches HTTP Digest Authentication to the given Request object.""" + + def __init__(self, username, password): + self.username = username + self.password = password + # Keep state in per-thread local storage + self._thread_local = threading.local() + + def init_per_thread_state(self): + # Ensure state is initialized just once per-thread + if not hasattr(self._thread_local, 'init'): + self._thread_local.init = True + self._thread_local.last_nonce = '' + self._thread_local.nonce_count = 0 + self._thread_local.chal = {} + self._thread_local.pos = None + self._thread_local.num_401_calls = None + + def build_digest_header(self, method, url): + """ + :rtype: str + """ + + realm = self._thread_local.chal['realm'] + nonce = self._thread_local.chal['nonce'] + qop = self._thread_local.chal.get('qop') + algorithm = self._thread_local.chal.get('algorithm') + opaque = self._thread_local.chal.get('opaque') + hash_utf8 = None + + if algorithm is None: + _algorithm = 'MD5' + else: + _algorithm = algorithm.upper() + # lambdas assume digest modules are imported at the top level + if _algorithm == 'MD5' or _algorithm == 'MD5-SESS': + def md5_utf8(x): + if isinstance(x, str): + x = x.encode('utf-8') + return hashlib.md5(x).hexdigest() + hash_utf8 = md5_utf8 + elif _algorithm == 'SHA': + def sha_utf8(x): + if isinstance(x, str): + x = x.encode('utf-8') + return hashlib.sha1(x).hexdigest() + hash_utf8 = sha_utf8 + elif _algorithm == 'SHA-256': + def sha256_utf8(x): + if isinstance(x, str): + x = x.encode('utf-8') + return hashlib.sha256(x).hexdigest() + hash_utf8 = sha256_utf8 + elif _algorithm == 'SHA-512': + def sha512_utf8(x): + if isinstance(x, str): + x = x.encode('utf-8') + return hashlib.sha512(x).hexdigest() + hash_utf8 = sha512_utf8 + + KD = lambda s, d: hash_utf8("%s:%s" % (s, d)) + + if hash_utf8 is None: + return None + + # XXX not implemented yet + entdig = None + p_parsed = urlparse(url) + #: path is request-uri defined in RFC 2616 which should not be empty + path = p_parsed.path or "/" + if p_parsed.query: + path += '?' + p_parsed.query + + A1 = '%s:%s:%s' % (self.username, realm, self.password) + A2 = '%s:%s' % (method, path) + + HA1 = hash_utf8(A1) + HA2 = hash_utf8(A2) + + if nonce == self._thread_local.last_nonce: + self._thread_local.nonce_count += 1 + else: + self._thread_local.nonce_count = 1 + ncvalue = '%08x' % self._thread_local.nonce_count + s = str(self._thread_local.nonce_count).encode('utf-8') + s += nonce.encode('utf-8') + s += time.ctime().encode('utf-8') + s += os.urandom(8) + + cnonce = (hashlib.sha1(s).hexdigest()[:16]) + if _algorithm == 'MD5-SESS': + HA1 = hash_utf8('%s:%s:%s' % (HA1, nonce, cnonce)) + + if not qop: + respdig = KD(HA1, "%s:%s" % (nonce, HA2)) + elif qop == 'auth' or 'auth' in qop.split(','): + noncebit = "%s:%s:%s:%s:%s" % ( + nonce, ncvalue, cnonce, 'auth', HA2 + ) + respdig = KD(HA1, noncebit) + else: + # XXX handle auth-int. + return None + + self._thread_local.last_nonce = nonce + + # XXX should the partial digests be encoded too? + base = 'username="%s", realm="%s", nonce="%s", uri="%s", ' \ + 'response="%s"' % (self.username, realm, nonce, path, respdig) + if opaque: + base += ', opaque="%s"' % opaque + if algorithm: + base += ', algorithm="%s"' % algorithm + if entdig: + base += ', digest="%s"' % entdig + if qop: + base += ', qop="auth", nc=%s, cnonce="%s"' % (ncvalue, cnonce) + + return 'Digest %s' % (base) + + def handle_redirect(self, r, **kwargs): + """Reset num_401_calls counter on redirects.""" + if r.is_redirect: + self._thread_local.num_401_calls = 1 + + def handle_401(self, r, **kwargs): + """ + Takes the given response and tries digest-auth, if needed. + + :rtype: requests.Response + """ + + # If response is not 4xx, do not auth + # See https://github.com/requests/requests/issues/3772 + if not 400 <= r.status_code < 500: + self._thread_local.num_401_calls = 1 + return r + + if self._thread_local.pos is not None: + # Rewind the file position indicator of the body to where + # it was to resend the request. + r.request.body.seek(self._thread_local.pos) + s_auth = r.headers.get('www-authenticate', '') + + if 'digest' in s_auth.lower() and self._thread_local.num_401_calls < 2: + + self._thread_local.num_401_calls += 1 + pat = re.compile(r'digest ', flags=re.IGNORECASE) + self._thread_local.chal = parse_dict_header(pat.sub('', s_auth, count=1)) + + # Consume content and release the original connection + # to allow our new request to reuse the same one. + r.content + r.close() + prep = r.request.copy() + extract_cookies_to_jar(prep._cookies, r.request, r.raw) + prep.prepare_cookies(prep._cookies) + + prep.headers['Authorization'] = self.build_digest_header( + prep.method, prep.url) + _r = r.connection.send(prep, **kwargs) + _r.history.append(r) + _r.request = prep + + return _r + + self._thread_local.num_401_calls = 1 + return r + + def __call__(self, r): + # Initialize per-thread state, if needed + self.init_per_thread_state() + # If we have a saved nonce, skip the 401 + if self._thread_local.last_nonce: + r.headers['Authorization'] = self.build_digest_header(r.method, r.url) + try: + self._thread_local.pos = r.body.tell() + except AttributeError: + # In the case of HTTPDigestAuth being reused and the body of + # the previous request was a file-like object, pos has the + # file position of the previous body. Ensure it's set to + # None. + self._thread_local.pos = None + r.register_hook('response', self.handle_401) + r.register_hook('response', self.handle_redirect) + self._thread_local.num_401_calls = 1 + + return r + + def __eq__(self, other): + return all([ + self.username == getattr(other, 'username', None), + self.password == getattr(other, 'password', None) + ]) + + def __ne__(self, other): + return not self == other diff --git a/venv2/lib/python2.7/site-packages/pip/_vendor/requests/certs.py b/venv2/lib/python2.7/site-packages/pip/_vendor/requests/certs.py new file mode 100644 index 0000000..06a594e --- /dev/null +++ b/venv2/lib/python2.7/site-packages/pip/_vendor/requests/certs.py @@ -0,0 +1,18 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +""" +requests.certs +~~~~~~~~~~~~~~ + +This module returns the preferred default CA certificate bundle. There is +only one — the one from the certifi package. + +If you are packaging Requests, e.g., for a Linux distribution or a managed +environment, you can change the definition of where() to return a separately +packaged CA bundle. +""" +from pip._vendor.certifi import where + +if __name__ == '__main__': + print(where()) diff --git a/venv2/lib/python2.7/site-packages/pip/_vendor/requests/compat.py b/venv2/lib/python2.7/site-packages/pip/_vendor/requests/compat.py new file mode 100644 index 0000000..6a86893 --- /dev/null +++ b/venv2/lib/python2.7/site-packages/pip/_vendor/requests/compat.py @@ -0,0 +1,74 @@ +# -*- coding: utf-8 -*- + +""" +requests.compat +~~~~~~~~~~~~~~~ + +This module handles import compatibility issues between Python 2 and +Python 3. +""" + +from pip._vendor import chardet + +import sys + +# ------- +# Pythons +# ------- + +# Syntax sugar. +_ver = sys.version_info + +#: Python 2.x? +is_py2 = (_ver[0] == 2) + +#: Python 3.x? +is_py3 = (_ver[0] == 3) + +# Note: We've patched out simplejson support in pip because it prevents +# upgrading simplejson on Windows. +# try: +# import simplejson as json +# except (ImportError, SyntaxError): +# # simplejson does not support Python 3.2, it throws a SyntaxError +# # because of u'...' Unicode literals. +import json + +# --------- +# Specifics +# --------- + +if is_py2: + from urllib import ( + quote, unquote, quote_plus, unquote_plus, urlencode, getproxies, + proxy_bypass, proxy_bypass_environment, getproxies_environment) + from urlparse import urlparse, urlunparse, urljoin, urlsplit, urldefrag + from urllib2 import parse_http_list + import cookielib + from Cookie import Morsel + from StringIO import StringIO + from collections import Callable, Mapping, MutableMapping, OrderedDict + + + builtin_str = str + bytes = str + str = unicode + basestring = basestring + numeric_types = (int, long, float) + integer_types = (int, long) + +elif is_py3: + from urllib.parse import urlparse, urlunparse, urljoin, urlsplit, urlencode, quote, unquote, quote_plus, unquote_plus, urldefrag + from urllib.request import parse_http_list, getproxies, proxy_bypass, proxy_bypass_environment, getproxies_environment + from http import cookiejar as cookielib + from http.cookies import Morsel + from io import StringIO + from collections import OrderedDict + from collections.abc import Callable, Mapping, MutableMapping + + builtin_str = str + str = str + bytes = bytes + basestring = (str, bytes) + numeric_types = (int, float) + integer_types = (int,) diff --git a/venv2/lib/python2.7/site-packages/pip/_vendor/requests/cookies.py b/venv2/lib/python2.7/site-packages/pip/_vendor/requests/cookies.py new file mode 100644 index 0000000..56fccd9 --- /dev/null +++ b/venv2/lib/python2.7/site-packages/pip/_vendor/requests/cookies.py @@ -0,0 +1,549 @@ +# -*- coding: utf-8 -*- + +""" +requests.cookies +~~~~~~~~~~~~~~~~ + +Compatibility code to be able to use `cookielib.CookieJar` with requests. + +requests.utils imports from here, so be careful with imports. +""" + +import copy +import time +import calendar + +from ._internal_utils import to_native_string +from .compat import cookielib, urlparse, urlunparse, Morsel, MutableMapping + +try: + import threading +except ImportError: + import dummy_threading as threading + + +class MockRequest(object): + """Wraps a `requests.Request` to mimic a `urllib2.Request`. + + The code in `cookielib.CookieJar` expects this interface in order to correctly + manage cookie policies, i.e., determine whether a cookie can be set, given the + domains of the request and the cookie. + + The original request object is read-only. The client is responsible for collecting + the new headers via `get_new_headers()` and interpreting them appropriately. You + probably want `get_cookie_header`, defined below. + """ + + def __init__(self, request): + self._r = request + self._new_headers = {} + self.type = urlparse(self._r.url).scheme + + def get_type(self): + return self.type + + def get_host(self): + return urlparse(self._r.url).netloc + + def get_origin_req_host(self): + return self.get_host() + + def get_full_url(self): + # Only return the response's URL if the user hadn't set the Host + # header + if not self._r.headers.get('Host'): + return self._r.url + # If they did set it, retrieve it and reconstruct the expected domain + host = to_native_string(self._r.headers['Host'], encoding='utf-8') + parsed = urlparse(self._r.url) + # Reconstruct the URL as we expect it + return urlunparse([ + parsed.scheme, host, parsed.path, parsed.params, parsed.query, + parsed.fragment + ]) + + def is_unverifiable(self): + return True + + def has_header(self, name): + return name in self._r.headers or name in self._new_headers + + def get_header(self, name, default=None): + return self._r.headers.get(name, self._new_headers.get(name, default)) + + def add_header(self, key, val): + """cookielib has no legitimate use for this method; add it back if you find one.""" + raise NotImplementedError("Cookie headers should be added with add_unredirected_header()") + + def add_unredirected_header(self, name, value): + self._new_headers[name] = value + + def get_new_headers(self): + return self._new_headers + + @property + def unverifiable(self): + return self.is_unverifiable() + + @property + def origin_req_host(self): + return self.get_origin_req_host() + + @property + def host(self): + return self.get_host() + + +class MockResponse(object): + """Wraps a `httplib.HTTPMessage` to mimic a `urllib.addinfourl`. + + ...what? Basically, expose the parsed HTTP headers from the server response + the way `cookielib` expects to see them. + """ + + def __init__(self, headers): + """Make a MockResponse for `cookielib` to read. + + :param headers: a httplib.HTTPMessage or analogous carrying the headers + """ + self._headers = headers + + def info(self): + return self._headers + + def getheaders(self, name): + self._headers.getheaders(name) + + +def extract_cookies_to_jar(jar, request, response): + """Extract the cookies from the response into a CookieJar. + + :param jar: cookielib.CookieJar (not necessarily a RequestsCookieJar) + :param request: our own requests.Request object + :param response: urllib3.HTTPResponse object + """ + if not (hasattr(response, '_original_response') and + response._original_response): + return + # the _original_response field is the wrapped httplib.HTTPResponse object, + req = MockRequest(request) + # pull out the HTTPMessage with the headers and put it in the mock: + res = MockResponse(response._original_response.msg) + jar.extract_cookies(res, req) + + +def get_cookie_header(jar, request): + """ + Produce an appropriate Cookie header string to be sent with `request`, or None. + + :rtype: str + """ + r = MockRequest(request) + jar.add_cookie_header(r) + return r.get_new_headers().get('Cookie') + + +def remove_cookie_by_name(cookiejar, name, domain=None, path=None): + """Unsets a cookie by name, by default over all domains and paths. + + Wraps CookieJar.clear(), is O(n). + """ + clearables = [] + for cookie in cookiejar: + if cookie.name != name: + continue + if domain is not None and domain != cookie.domain: + continue + if path is not None and path != cookie.path: + continue + clearables.append((cookie.domain, cookie.path, cookie.name)) + + for domain, path, name in clearables: + cookiejar.clear(domain, path, name) + + +class CookieConflictError(RuntimeError): + """There are two cookies that meet the criteria specified in the cookie jar. + Use .get and .set and include domain and path args in order to be more specific. + """ + + +class RequestsCookieJar(cookielib.CookieJar, MutableMapping): + """Compatibility class; is a cookielib.CookieJar, but exposes a dict + interface. + + This is the CookieJar we create by default for requests and sessions that + don't specify one, since some clients may expect response.cookies and + session.cookies to support dict operations. + + Requests does not use the dict interface internally; it's just for + compatibility with external client code. All requests code should work + out of the box with externally provided instances of ``CookieJar``, e.g. + ``LWPCookieJar`` and ``FileCookieJar``. + + Unlike a regular CookieJar, this class is pickleable. + + .. warning:: dictionary operations that are normally O(1) may be O(n). + """ + + def get(self, name, default=None, domain=None, path=None): + """Dict-like get() that also supports optional domain and path args in + order to resolve naming collisions from using one cookie jar over + multiple domains. + + .. warning:: operation is O(n), not O(1). + """ + try: + return self._find_no_duplicates(name, domain, path) + except KeyError: + return default + + def set(self, name, value, **kwargs): + """Dict-like set() that also supports optional domain and path args in + order to resolve naming collisions from using one cookie jar over + multiple domains. + """ + # support client code that unsets cookies by assignment of a None value: + if value is None: + remove_cookie_by_name(self, name, domain=kwargs.get('domain'), path=kwargs.get('path')) + return + + if isinstance(value, Morsel): + c = morsel_to_cookie(value) + else: + c = create_cookie(name, value, **kwargs) + self.set_cookie(c) + return c + + def iterkeys(self): + """Dict-like iterkeys() that returns an iterator of names of cookies + from the jar. + + .. seealso:: itervalues() and iteritems(). + """ + for cookie in iter(self): + yield cookie.name + + def keys(self): + """Dict-like keys() that returns a list of names of cookies from the + jar. + + .. seealso:: values() and items(). + """ + return list(self.iterkeys()) + + def itervalues(self): + """Dict-like itervalues() that returns an iterator of values of cookies + from the jar. + + .. seealso:: iterkeys() and iteritems(). + """ + for cookie in iter(self): + yield cookie.value + + def values(self): + """Dict-like values() that returns a list of values of cookies from the + jar. + + .. seealso:: keys() and items(). + """ + return list(self.itervalues()) + + def iteritems(self): + """Dict-like iteritems() that returns an iterator of name-value tuples + from the jar. + + .. seealso:: iterkeys() and itervalues(). + """ + for cookie in iter(self): + yield cookie.name, cookie.value + + def items(self): + """Dict-like items() that returns a list of name-value tuples from the + jar. Allows client-code to call ``dict(RequestsCookieJar)`` and get a + vanilla python dict of key value pairs. + + .. seealso:: keys() and values(). + """ + return list(self.iteritems()) + + def list_domains(self): + """Utility method to list all the domains in the jar.""" + domains = [] + for cookie in iter(self): + if cookie.domain not in domains: + domains.append(cookie.domain) + return domains + + def list_paths(self): + """Utility method to list all the paths in the jar.""" + paths = [] + for cookie in iter(self): + if cookie.path not in paths: + paths.append(cookie.path) + return paths + + def multiple_domains(self): + """Returns True if there are multiple domains in the jar. + Returns False otherwise. + + :rtype: bool + """ + domains = [] + for cookie in iter(self): + if cookie.domain is not None and cookie.domain in domains: + return True + domains.append(cookie.domain) + return False # there is only one domain in jar + + def get_dict(self, domain=None, path=None): + """Takes as an argument an optional domain and path and returns a plain + old Python dict of name-value pairs of cookies that meet the + requirements. + + :rtype: dict + """ + dictionary = {} + for cookie in iter(self): + if ( + (domain is None or cookie.domain == domain) and + (path is None or cookie.path == path) + ): + dictionary[cookie.name] = cookie.value + return dictionary + + def __contains__(self, name): + try: + return super(RequestsCookieJar, self).__contains__(name) + except CookieConflictError: + return True + + def __getitem__(self, name): + """Dict-like __getitem__() for compatibility with client code. Throws + exception if there are more than one cookie with name. In that case, + use the more explicit get() method instead. + + .. warning:: operation is O(n), not O(1). + """ + return self._find_no_duplicates(name) + + def __setitem__(self, name, value): + """Dict-like __setitem__ for compatibility with client code. Throws + exception if there is already a cookie of that name in the jar. In that + case, use the more explicit set() method instead. + """ + self.set(name, value) + + def __delitem__(self, name): + """Deletes a cookie given a name. Wraps ``cookielib.CookieJar``'s + ``remove_cookie_by_name()``. + """ + remove_cookie_by_name(self, name) + + def set_cookie(self, cookie, *args, **kwargs): + if hasattr(cookie.value, 'startswith') and cookie.value.startswith('"') and cookie.value.endswith('"'): + cookie.value = cookie.value.replace('\\"', '') + return super(RequestsCookieJar, self).set_cookie(cookie, *args, **kwargs) + + def update(self, other): + """Updates this jar with cookies from another CookieJar or dict-like""" + if isinstance(other, cookielib.CookieJar): + for cookie in other: + self.set_cookie(copy.copy(cookie)) + else: + super(RequestsCookieJar, self).update(other) + + def _find(self, name, domain=None, path=None): + """Requests uses this method internally to get cookie values. + + If there are conflicting cookies, _find arbitrarily chooses one. + See _find_no_duplicates if you want an exception thrown if there are + conflicting cookies. + + :param name: a string containing name of cookie + :param domain: (optional) string containing domain of cookie + :param path: (optional) string containing path of cookie + :return: cookie.value + """ + for cookie in iter(self): + if cookie.name == name: + if domain is None or cookie.domain == domain: + if path is None or cookie.path == path: + return cookie.value + + raise KeyError('name=%r, domain=%r, path=%r' % (name, domain, path)) + + def _find_no_duplicates(self, name, domain=None, path=None): + """Both ``__get_item__`` and ``get`` call this function: it's never + used elsewhere in Requests. + + :param name: a string containing name of cookie + :param domain: (optional) string containing domain of cookie + :param path: (optional) string containing path of cookie + :raises KeyError: if cookie is not found + :raises CookieConflictError: if there are multiple cookies + that match name and optionally domain and path + :return: cookie.value + """ + toReturn = None + for cookie in iter(self): + if cookie.name == name: + if domain is None or cookie.domain == domain: + if path is None or cookie.path == path: + if toReturn is not None: # if there are multiple cookies that meet passed in criteria + raise CookieConflictError('There are multiple cookies with name, %r' % (name)) + toReturn = cookie.value # we will eventually return this as long as no cookie conflict + + if toReturn: + return toReturn + raise KeyError('name=%r, domain=%r, path=%r' % (name, domain, path)) + + def __getstate__(self): + """Unlike a normal CookieJar, this class is pickleable.""" + state = self.__dict__.copy() + # remove the unpickleable RLock object + state.pop('_cookies_lock') + return state + + def __setstate__(self, state): + """Unlike a normal CookieJar, this class is pickleable.""" + self.__dict__.update(state) + if '_cookies_lock' not in self.__dict__: + self._cookies_lock = threading.RLock() + + def copy(self): + """Return a copy of this RequestsCookieJar.""" + new_cj = RequestsCookieJar() + new_cj.set_policy(self.get_policy()) + new_cj.update(self) + return new_cj + + def get_policy(self): + """Return the CookiePolicy instance used.""" + return self._policy + + +def _copy_cookie_jar(jar): + if jar is None: + return None + + if hasattr(jar, 'copy'): + # We're dealing with an instance of RequestsCookieJar + return jar.copy() + # We're dealing with a generic CookieJar instance + new_jar = copy.copy(jar) + new_jar.clear() + for cookie in jar: + new_jar.set_cookie(copy.copy(cookie)) + return new_jar + + +def create_cookie(name, value, **kwargs): + """Make a cookie from underspecified parameters. + + By default, the pair of `name` and `value` will be set for the domain '' + and sent on every request (this is sometimes called a "supercookie"). + """ + result = { + 'version': 0, + 'name': name, + 'value': value, + 'port': None, + 'domain': '', + 'path': '/', + 'secure': False, + 'expires': None, + 'discard': True, + 'comment': None, + 'comment_url': None, + 'rest': {'HttpOnly': None}, + 'rfc2109': False, + } + + badargs = set(kwargs) - set(result) + if badargs: + err = 'create_cookie() got unexpected keyword arguments: %s' + raise TypeError(err % list(badargs)) + + result.update(kwargs) + result['port_specified'] = bool(result['port']) + result['domain_specified'] = bool(result['domain']) + result['domain_initial_dot'] = result['domain'].startswith('.') + result['path_specified'] = bool(result['path']) + + return cookielib.Cookie(**result) + + +def morsel_to_cookie(morsel): + """Convert a Morsel object into a Cookie containing the one k/v pair.""" + + expires = None + if morsel['max-age']: + try: + expires = int(time.time() + int(morsel['max-age'])) + except ValueError: + raise TypeError('max-age: %s must be integer' % morsel['max-age']) + elif morsel['expires']: + time_template = '%a, %d-%b-%Y %H:%M:%S GMT' + expires = calendar.timegm( + time.strptime(morsel['expires'], time_template) + ) + return create_cookie( + comment=morsel['comment'], + comment_url=bool(morsel['comment']), + discard=False, + domain=morsel['domain'], + expires=expires, + name=morsel.key, + path=morsel['path'], + port=None, + rest={'HttpOnly': morsel['httponly']}, + rfc2109=False, + secure=bool(morsel['secure']), + value=morsel.value, + version=morsel['version'] or 0, + ) + + +def cookiejar_from_dict(cookie_dict, cookiejar=None, overwrite=True): + """Returns a CookieJar from a key/value dictionary. + + :param cookie_dict: Dict of key/values to insert into CookieJar. + :param cookiejar: (optional) A cookiejar to add the cookies to. + :param overwrite: (optional) If False, will not replace cookies + already in the jar with new ones. + :rtype: CookieJar + """ + if cookiejar is None: + cookiejar = RequestsCookieJar() + + if cookie_dict is not None: + names_from_jar = [cookie.name for cookie in cookiejar] + for name in cookie_dict: + if overwrite or (name not in names_from_jar): + cookiejar.set_cookie(create_cookie(name, cookie_dict[name])) + + return cookiejar + + +def merge_cookies(cookiejar, cookies): + """Add cookies to cookiejar and returns a merged CookieJar. + + :param cookiejar: CookieJar object to add the cookies to. + :param cookies: Dictionary or CookieJar object to be added. + :rtype: CookieJar + """ + if not isinstance(cookiejar, cookielib.CookieJar): + raise ValueError('You can only merge into CookieJar') + + if isinstance(cookies, dict): + cookiejar = cookiejar_from_dict( + cookies, cookiejar=cookiejar, overwrite=False) + elif isinstance(cookies, cookielib.CookieJar): + try: + cookiejar.update(cookies) + except AttributeError: + for cookie_in_jar in cookies: + cookiejar.set_cookie(cookie_in_jar) + + return cookiejar diff --git a/venv2/lib/python2.7/site-packages/pip/_vendor/requests/exceptions.py b/venv2/lib/python2.7/site-packages/pip/_vendor/requests/exceptions.py new file mode 100644 index 0000000..a91e1fd --- /dev/null +++ b/venv2/lib/python2.7/site-packages/pip/_vendor/requests/exceptions.py @@ -0,0 +1,126 @@ +# -*- coding: utf-8 -*- + +""" +requests.exceptions +~~~~~~~~~~~~~~~~~~~ + +This module contains the set of Requests' exceptions. +""" +from pip._vendor.urllib3.exceptions import HTTPError as BaseHTTPError + + +class RequestException(IOError): + """There was an ambiguous exception that occurred while handling your + request. + """ + + def __init__(self, *args, **kwargs): + """Initialize RequestException with `request` and `response` objects.""" + response = kwargs.pop('response', None) + self.response = response + self.request = kwargs.pop('request', None) + if (response is not None and not self.request and + hasattr(response, 'request')): + self.request = self.response.request + super(RequestException, self).__init__(*args, **kwargs) + + +class HTTPError(RequestException): + """An HTTP error occurred.""" + + +class ConnectionError(RequestException): + """A Connection error occurred.""" + + +class ProxyError(ConnectionError): + """A proxy error occurred.""" + + +class SSLError(ConnectionError): + """An SSL error occurred.""" + + +class Timeout(RequestException): + """The request timed out. + + Catching this error will catch both + :exc:`~requests.exceptions.ConnectTimeout` and + :exc:`~requests.exceptions.ReadTimeout` errors. + """ + + +class ConnectTimeout(ConnectionError, Timeout): + """The request timed out while trying to connect to the remote server. + + Requests that produced this error are safe to retry. + """ + + +class ReadTimeout(Timeout): + """The server did not send any data in the allotted amount of time.""" + + +class URLRequired(RequestException): + """A valid URL is required to make a request.""" + + +class TooManyRedirects(RequestException): + """Too many redirects.""" + + +class MissingSchema(RequestException, ValueError): + """The URL schema (e.g. http or https) is missing.""" + + +class InvalidSchema(RequestException, ValueError): + """See defaults.py for valid schemas.""" + + +class InvalidURL(RequestException, ValueError): + """The URL provided was somehow invalid.""" + + +class InvalidHeader(RequestException, ValueError): + """The header value provided was somehow invalid.""" + + +class InvalidProxyURL(InvalidURL): + """The proxy URL provided is invalid.""" + + +class ChunkedEncodingError(RequestException): + """The server declared chunked encoding but sent an invalid chunk.""" + + +class ContentDecodingError(RequestException, BaseHTTPError): + """Failed to decode response content""" + + +class StreamConsumedError(RequestException, TypeError): + """The content for this response was already consumed""" + + +class RetryError(RequestException): + """Custom retries logic failed""" + + +class UnrewindableBodyError(RequestException): + """Requests encountered an error when trying to rewind a body""" + +# Warnings + + +class RequestsWarning(Warning): + """Base warning for Requests.""" + pass + + +class FileModeWarning(RequestsWarning, DeprecationWarning): + """A file was opened in text mode, but Requests determined its binary length.""" + pass + + +class RequestsDependencyWarning(RequestsWarning): + """An imported dependency doesn't match the expected version range.""" + pass diff --git a/venv2/lib/python2.7/site-packages/pip/_vendor/requests/help.py b/venv2/lib/python2.7/site-packages/pip/_vendor/requests/help.py new file mode 100644 index 0000000..3c3072b --- /dev/null +++ b/venv2/lib/python2.7/site-packages/pip/_vendor/requests/help.py @@ -0,0 +1,119 @@ +"""Module containing bug report helper(s).""" +from __future__ import print_function + +import json +import platform +import sys +import ssl + +from pip._vendor import idna +from pip._vendor import urllib3 +from pip._vendor import chardet + +from . import __version__ as requests_version + +try: + from pip._vendor.urllib3.contrib import pyopenssl +except ImportError: + pyopenssl = None + OpenSSL = None + cryptography = None +else: + import OpenSSL + import cryptography + + +def _implementation(): + """Return a dict with the Python implementation and version. + + Provide both the name and the version of the Python implementation + currently running. For example, on CPython 2.7.5 it will return + {'name': 'CPython', 'version': '2.7.5'}. + + This function works best on CPython and PyPy: in particular, it probably + doesn't work for Jython or IronPython. Future investigation should be done + to work out the correct shape of the code for those platforms. + """ + implementation = platform.python_implementation() + + if implementation == 'CPython': + implementation_version = platform.python_version() + elif implementation == 'PyPy': + implementation_version = '%s.%s.%s' % (sys.pypy_version_info.major, + sys.pypy_version_info.minor, + sys.pypy_version_info.micro) + if sys.pypy_version_info.releaselevel != 'final': + implementation_version = ''.join([ + implementation_version, sys.pypy_version_info.releaselevel + ]) + elif implementation == 'Jython': + implementation_version = platform.python_version() # Complete Guess + elif implementation == 'IronPython': + implementation_version = platform.python_version() # Complete Guess + else: + implementation_version = 'Unknown' + + return {'name': implementation, 'version': implementation_version} + + +def info(): + """Generate information for a bug report.""" + try: + platform_info = { + 'system': platform.system(), + 'release': platform.release(), + } + except IOError: + platform_info = { + 'system': 'Unknown', + 'release': 'Unknown', + } + + implementation_info = _implementation() + urllib3_info = {'version': urllib3.__version__} + chardet_info = {'version': chardet.__version__} + + pyopenssl_info = { + 'version': None, + 'openssl_version': '', + } + if OpenSSL: + pyopenssl_info = { + 'version': OpenSSL.__version__, + 'openssl_version': '%x' % OpenSSL.SSL.OPENSSL_VERSION_NUMBER, + } + cryptography_info = { + 'version': getattr(cryptography, '__version__', ''), + } + idna_info = { + 'version': getattr(idna, '__version__', ''), + } + + system_ssl = ssl.OPENSSL_VERSION_NUMBER + system_ssl_info = { + 'version': '%x' % system_ssl if system_ssl is not None else '' + } + + return { + 'platform': platform_info, + 'implementation': implementation_info, + 'system_ssl': system_ssl_info, + 'using_pyopenssl': pyopenssl is not None, + 'pyOpenSSL': pyopenssl_info, + 'urllib3': urllib3_info, + 'chardet': chardet_info, + 'cryptography': cryptography_info, + 'idna': idna_info, + 'requests': { + 'version': requests_version, + }, + } + + +def main(): + """Pretty-print the bug information as JSON.""" + print(json.dumps(info(), sort_keys=True, indent=2)) + + +if __name__ == '__main__': + main() diff --git a/venv2/lib/python2.7/site-packages/pip/_vendor/requests/hooks.py b/venv2/lib/python2.7/site-packages/pip/_vendor/requests/hooks.py new file mode 100644 index 0000000..7a51f21 --- /dev/null +++ b/venv2/lib/python2.7/site-packages/pip/_vendor/requests/hooks.py @@ -0,0 +1,34 @@ +# -*- coding: utf-8 -*- + +""" +requests.hooks +~~~~~~~~~~~~~~ + +This module provides the capabilities for the Requests hooks system. + +Available hooks: + +``response``: + The response generated from a Request. +""" +HOOKS = ['response'] + + +def default_hooks(): + return {event: [] for event in HOOKS} + +# TODO: response is the only one + + +def dispatch_hook(key, hooks, hook_data, **kwargs): + """Dispatches a hook dictionary on a given piece of data.""" + hooks = hooks or {} + hooks = hooks.get(key) + if hooks: + if hasattr(hooks, '__call__'): + hooks = [hooks] + for hook in hooks: + _hook_data = hook(hook_data, **kwargs) + if _hook_data is not None: + hook_data = _hook_data + return hook_data diff --git a/venv2/lib/python2.7/site-packages/pip/_vendor/requests/models.py b/venv2/lib/python2.7/site-packages/pip/_vendor/requests/models.py new file mode 100644 index 0000000..0839957 --- /dev/null +++ b/venv2/lib/python2.7/site-packages/pip/_vendor/requests/models.py @@ -0,0 +1,953 @@ +# -*- coding: utf-8 -*- + +""" +requests.models +~~~~~~~~~~~~~~~ + +This module contains the primary objects that power Requests. +""" + +import datetime +import sys + +# Import encoding now, to avoid implicit import later. +# Implicit import within threads may cause LookupError when standard library is in a ZIP, +# such as in Embedded Python. See https://github.com/requests/requests/issues/3578. +import encodings.idna + +from pip._vendor.urllib3.fields import RequestField +from pip._vendor.urllib3.filepost import encode_multipart_formdata +from pip._vendor.urllib3.util import parse_url +from pip._vendor.urllib3.exceptions import ( + DecodeError, ReadTimeoutError, ProtocolError, LocationParseError) + +from io import UnsupportedOperation +from .hooks import default_hooks +from .structures import CaseInsensitiveDict + +from .auth import HTTPBasicAuth +from .cookies import cookiejar_from_dict, get_cookie_header, _copy_cookie_jar +from .exceptions import ( + HTTPError, MissingSchema, InvalidURL, ChunkedEncodingError, + ContentDecodingError, ConnectionError, StreamConsumedError) +from ._internal_utils import to_native_string, unicode_is_ascii +from .utils import ( + guess_filename, get_auth_from_url, requote_uri, + stream_decode_response_unicode, to_key_val_list, parse_header_links, + iter_slices, guess_json_utf, super_len, check_header_validity) +from .compat import ( + Callable, Mapping, + cookielib, urlunparse, urlsplit, urlencode, str, bytes, + is_py2, chardet, builtin_str, basestring) +from .compat import json as complexjson +from .status_codes import codes + +#: The set of HTTP status codes that indicate an automatically +#: processable redirect. +REDIRECT_STATI = ( + codes.moved, # 301 + codes.found, # 302 + codes.other, # 303 + codes.temporary_redirect, # 307 + codes.permanent_redirect, # 308 +) + +DEFAULT_REDIRECT_LIMIT = 30 +CONTENT_CHUNK_SIZE = 10 * 1024 +ITER_CHUNK_SIZE = 512 + + +class RequestEncodingMixin(object): + @property + def path_url(self): + """Build the path URL to use.""" + + url = [] + + p = urlsplit(self.url) + + path = p.path + if not path: + path = '/' + + url.append(path) + + query = p.query + if query: + url.append('?') + url.append(query) + + return ''.join(url) + + @staticmethod + def _encode_params(data): + """Encode parameters in a piece of data. + + Will successfully encode parameters when passed as a dict or a list of + 2-tuples. Order is retained if data is a list of 2-tuples but arbitrary + if parameters are supplied as a dict. + """ + + if isinstance(data, (str, bytes)): + return data + elif hasattr(data, 'read'): + return data + elif hasattr(data, '__iter__'): + result = [] + for k, vs in to_key_val_list(data): + if isinstance(vs, basestring) or not hasattr(vs, '__iter__'): + vs = [vs] + for v in vs: + if v is not None: + result.append( + (k.encode('utf-8') if isinstance(k, str) else k, + v.encode('utf-8') if isinstance(v, str) else v)) + return urlencode(result, doseq=True) + else: + return data + + @staticmethod + def _encode_files(files, data): + """Build the body for a multipart/form-data request. + + Will successfully encode files when passed as a dict or a list of + tuples. Order is retained if data is a list of tuples but arbitrary + if parameters are supplied as a dict. + The tuples may be 2-tuples (filename, fileobj), 3-tuples (filename, fileobj, contentype) + or 4-tuples (filename, fileobj, contentype, custom_headers). + """ + if (not files): + raise ValueError("Files must be provided.") + elif isinstance(data, basestring): + raise ValueError("Data must not be a string.") + + new_fields = [] + fields = to_key_val_list(data or {}) + files = to_key_val_list(files or {}) + + for field, val in fields: + if isinstance(val, basestring) or not hasattr(val, '__iter__'): + val = [val] + for v in val: + if v is not None: + # Don't call str() on bytestrings: in Py3 it all goes wrong. + if not isinstance(v, bytes): + v = str(v) + + new_fields.append( + (field.decode('utf-8') if isinstance(field, bytes) else field, + v.encode('utf-8') if isinstance(v, str) else v)) + + for (k, v) in files: + # support for explicit filename + ft = None + fh = None + if isinstance(v, (tuple, list)): + if len(v) == 2: + fn, fp = v + elif len(v) == 3: + fn, fp, ft = v + else: + fn, fp, ft, fh = v + else: + fn = guess_filename(v) or k + fp = v + + if isinstance(fp, (str, bytes, bytearray)): + fdata = fp + elif hasattr(fp, 'read'): + fdata = fp.read() + elif fp is None: + continue + else: + fdata = fp + + rf = RequestField(name=k, data=fdata, filename=fn, headers=fh) + rf.make_multipart(content_type=ft) + new_fields.append(rf) + + body, content_type = encode_multipart_formdata(new_fields) + + return body, content_type + + +class RequestHooksMixin(object): + def register_hook(self, event, hook): + """Properly register a hook.""" + + if event not in self.hooks: + raise ValueError('Unsupported event specified, with event name "%s"' % (event)) + + if isinstance(hook, Callable): + self.hooks[event].append(hook) + elif hasattr(hook, '__iter__'): + self.hooks[event].extend(h for h in hook if isinstance(h, Callable)) + + def deregister_hook(self, event, hook): + """Deregister a previously registered hook. + Returns True if the hook existed, False if not. + """ + + try: + self.hooks[event].remove(hook) + return True + except ValueError: + return False + + +class Request(RequestHooksMixin): + """A user-created :class:`Request <Request>` object. + + Used to prepare a :class:`PreparedRequest <PreparedRequest>`, which is sent to the server. + + :param method: HTTP method to use. + :param url: URL to send. + :param headers: dictionary of headers to send. + :param files: dictionary of {filename: fileobject} files to multipart upload. + :param data: the body to attach to the request. If a dictionary or + list of tuples ``[(key, value)]`` is provided, form-encoding will + take place. + :param json: json for the body to attach to the request (if files or data is not specified). + :param params: URL parameters to append to the URL. If a dictionary or + list of tuples ``[(key, value)]`` is provided, form-encoding will + take place. + :param auth: Auth handler or (user, pass) tuple. + :param cookies: dictionary or CookieJar of cookies to attach to this request. + :param hooks: dictionary of callback hooks, for internal usage. + + Usage:: + + >>> import requests + >>> req = requests.Request('GET', 'https://httpbin.org/get') + >>> req.prepare() + <PreparedRequest [GET]> + """ + + def __init__(self, + method=None, url=None, headers=None, files=None, data=None, + params=None, auth=None, cookies=None, hooks=None, json=None): + + # Default empty dicts for dict params. + data = [] if data is None else data + files = [] if files is None else files + headers = {} if headers is None else headers + params = {} if params is None else params + hooks = {} if hooks is None else hooks + + self.hooks = default_hooks() + for (k, v) in list(hooks.items()): + self.register_hook(event=k, hook=v) + + self.method = method + self.url = url + self.headers = headers + self.files = files + self.data = data + self.json = json + self.params = params + self.auth = auth + self.cookies = cookies + + def __repr__(self): + return '<Request [%s]>' % (self.method) + + def prepare(self): + """Constructs a :class:`PreparedRequest <PreparedRequest>` for transmission and returns it.""" + p = PreparedRequest() + p.prepare( + method=self.method, + url=self.url, + headers=self.headers, + files=self.files, + data=self.data, + json=self.json, + params=self.params, + auth=self.auth, + cookies=self.cookies, + hooks=self.hooks, + ) + return p + + +class PreparedRequest(RequestEncodingMixin, RequestHooksMixin): + """The fully mutable :class:`PreparedRequest <PreparedRequest>` object, + containing the exact bytes that will be sent to the server. + + Generated from either a :class:`Request <Request>` object or manually. + + Usage:: + + >>> import requests + >>> req = requests.Request('GET', 'https://httpbin.org/get') + >>> r = req.prepare() + <PreparedRequest [GET]> + + >>> s = requests.Session() + >>> s.send(r) + <Response [200]> + """ + + def __init__(self): + #: HTTP verb to send to the server. + self.method = None + #: HTTP URL to send the request to. + self.url = None + #: dictionary of HTTP headers. + self.headers = None + # The `CookieJar` used to create the Cookie header will be stored here + # after prepare_cookies is called + self._cookies = None + #: request body to send to the server. + self.body = None + #: dictionary of callback hooks, for internal usage. + self.hooks = default_hooks() + #: integer denoting starting position of a readable file-like body. + self._body_position = None + + def prepare(self, + method=None, url=None, headers=None, files=None, data=None, + params=None, auth=None, cookies=None, hooks=None, json=None): + """Prepares the entire request with the given parameters.""" + + self.prepare_method(method) + self.prepare_url(url, params) + self.prepare_headers(headers) + self.prepare_cookies(cookies) + self.prepare_body(data, files, json) + self.prepare_auth(auth, url) + + # Note that prepare_auth must be last to enable authentication schemes + # such as OAuth to work on a fully prepared request. + + # This MUST go after prepare_auth. Authenticators could add a hook + self.prepare_hooks(hooks) + + def __repr__(self): + return '<PreparedRequest [%s]>' % (self.method) + + def copy(self): + p = PreparedRequest() + p.method = self.method + p.url = self.url + p.headers = self.headers.copy() if self.headers is not None else None + p._cookies = _copy_cookie_jar(self._cookies) + p.body = self.body + p.hooks = self.hooks + p._body_position = self._body_position + return p + + def prepare_method(self, method): + """Prepares the given HTTP method.""" + self.method = method + if self.method is not None: + self.method = to_native_string(self.method.upper()) + + @staticmethod + def _get_idna_encoded_host(host): + from pip._vendor import idna + + try: + host = idna.encode(host, uts46=True).decode('utf-8') + except idna.IDNAError: + raise UnicodeError + return host + + def prepare_url(self, url, params): + """Prepares the given HTTP URL.""" + #: Accept objects that have string representations. + #: We're unable to blindly call unicode/str functions + #: as this will include the bytestring indicator (b'') + #: on python 3.x. + #: https://github.com/requests/requests/pull/2238 + if isinstance(url, bytes): + url = url.decode('utf8') + else: + url = unicode(url) if is_py2 else str(url) + + # Remove leading whitespaces from url + url = url.lstrip() + + # Don't do any URL preparation for non-HTTP schemes like `mailto`, + # `data` etc to work around exceptions from `url_parse`, which + # handles RFC 3986 only. + if ':' in url and not url.lower().startswith('http'): + self.url = url + return + + # Support for unicode domain names and paths. + try: + scheme, auth, host, port, path, query, fragment = parse_url(url) + except LocationParseError as e: + raise InvalidURL(*e.args) + + if not scheme: + error = ("Invalid URL {0!r}: No schema supplied. Perhaps you meant http://{0}?") + error = error.format(to_native_string(url, 'utf8')) + + raise MissingSchema(error) + + if not host: + raise InvalidURL("Invalid URL %r: No host supplied" % url) + + # In general, we want to try IDNA encoding the hostname if the string contains + # non-ASCII characters. This allows users to automatically get the correct IDNA + # behaviour. For strings containing only ASCII characters, we need to also verify + # it doesn't start with a wildcard (*), before allowing the unencoded hostname. + if not unicode_is_ascii(host): + try: + host = self._get_idna_encoded_host(host) + except UnicodeError: + raise InvalidURL('URL has an invalid label.') + elif host.startswith(u'*'): + raise InvalidURL('URL has an invalid label.') + + # Carefully reconstruct the network location + netloc = auth or '' + if netloc: + netloc += '@' + netloc += host + if port: + netloc += ':' + str(port) + + # Bare domains aren't valid URLs. + if not path: + path = '/' + + if is_py2: + if isinstance(scheme, str): + scheme = scheme.encode('utf-8') + if isinstance(netloc, str): + netloc = netloc.encode('utf-8') + if isinstance(path, str): + path = path.encode('utf-8') + if isinstance(query, str): + query = query.encode('utf-8') + if isinstance(fragment, str): + fragment = fragment.encode('utf-8') + + if isinstance(params, (str, bytes)): + params = to_native_string(params) + + enc_params = self._encode_params(params) + if enc_params: + if query: + query = '%s&%s' % (query, enc_params) + else: + query = enc_params + + url = requote_uri(urlunparse([scheme, netloc, path, None, query, fragment])) + self.url = url + + def prepare_headers(self, headers): + """Prepares the given HTTP headers.""" + + self.headers = CaseInsensitiveDict() + if headers: + for header in headers.items(): + # Raise exception on invalid header value. + check_header_validity(header) + name, value = header + self.headers[to_native_string(name)] = value + + def prepare_body(self, data, files, json=None): + """Prepares the given HTTP body data.""" + + # Check if file, fo, generator, iterator. + # If not, run through normal process. + + # Nottin' on you. + body = None + content_type = None + + if not data and json is not None: + # urllib3 requires a bytes-like body. Python 2's json.dumps + # provides this natively, but Python 3 gives a Unicode string. + content_type = 'application/json' + body = complexjson.dumps(json) + if not isinstance(body, bytes): + body = body.encode('utf-8') + + is_stream = all([ + hasattr(data, '__iter__'), + not isinstance(data, (basestring, list, tuple, Mapping)) + ]) + + try: + length = super_len(data) + except (TypeError, AttributeError, UnsupportedOperation): + length = None + + if is_stream: + body = data + + if getattr(body, 'tell', None) is not None: + # Record the current file position before reading. + # This will allow us to rewind a file in the event + # of a redirect. + try: + self._body_position = body.tell() + except (IOError, OSError): + # This differentiates from None, allowing us to catch + # a failed `tell()` later when trying to rewind the body + self._body_position = object() + + if files: + raise NotImplementedError('Streamed bodies and files are mutually exclusive.') + + if length: + self.headers['Content-Length'] = builtin_str(length) + else: + self.headers['Transfer-Encoding'] = 'chunked' + else: + # Multi-part file uploads. + if files: + (body, content_type) = self._encode_files(files, data) + else: + if data: + body = self._encode_params(data) + if isinstance(data, basestring) or hasattr(data, 'read'): + content_type = None + else: + content_type = 'application/x-www-form-urlencoded' + + self.prepare_content_length(body) + + # Add content-type if it wasn't explicitly provided. + if content_type and ('content-type' not in self.headers): + self.headers['Content-Type'] = content_type + + self.body = body + + def prepare_content_length(self, body): + """Prepare Content-Length header based on request method and body""" + if body is not None: + length = super_len(body) + if length: + # If length exists, set it. Otherwise, we fallback + # to Transfer-Encoding: chunked. + self.headers['Content-Length'] = builtin_str(length) + elif self.method not in ('GET', 'HEAD') and self.headers.get('Content-Length') is None: + # Set Content-Length to 0 for methods that can have a body + # but don't provide one. (i.e. not GET or HEAD) + self.headers['Content-Length'] = '0' + + def prepare_auth(self, auth, url=''): + """Prepares the given HTTP auth data.""" + + # If no Auth is explicitly provided, extract it from the URL first. + if auth is None: + url_auth = get_auth_from_url(self.url) + auth = url_auth if any(url_auth) else None + + if auth: + if isinstance(auth, tuple) and len(auth) == 2: + # special-case basic HTTP auth + auth = HTTPBasicAuth(*auth) + + # Allow auth to make its changes. + r = auth(self) + + # Update self to reflect the auth changes. + self.__dict__.update(r.__dict__) + + # Recompute Content-Length + self.prepare_content_length(self.body) + + def prepare_cookies(self, cookies): + """Prepares the given HTTP cookie data. + + This function eventually generates a ``Cookie`` header from the + given cookies using cookielib. Due to cookielib's design, the header + will not be regenerated if it already exists, meaning this function + can only be called once for the life of the + :class:`PreparedRequest <PreparedRequest>` object. Any subsequent calls + to ``prepare_cookies`` will have no actual effect, unless the "Cookie" + header is removed beforehand. + """ + if isinstance(cookies, cookielib.CookieJar): + self._cookies = cookies + else: + self._cookies = cookiejar_from_dict(cookies) + + cookie_header = get_cookie_header(self._cookies, self) + if cookie_header is not None: + self.headers['Cookie'] = cookie_header + + def prepare_hooks(self, hooks): + """Prepares the given hooks.""" + # hooks can be passed as None to the prepare method and to this + # method. To prevent iterating over None, simply use an empty list + # if hooks is False-y + hooks = hooks or [] + for event in hooks: + self.register_hook(event, hooks[event]) + + +class Response(object): + """The :class:`Response <Response>` object, which contains a + server's response to an HTTP request. + """ + + __attrs__ = [ + '_content', 'status_code', 'headers', 'url', 'history', + 'encoding', 'reason', 'cookies', 'elapsed', 'request' + ] + + def __init__(self): + self._content = False + self._content_consumed = False + self._next = None + + #: Integer Code of responded HTTP Status, e.g. 404 or 200. + self.status_code = None + + #: Case-insensitive Dictionary of Response Headers. + #: For example, ``headers['content-encoding']`` will return the + #: value of a ``'Content-Encoding'`` response header. + self.headers = CaseInsensitiveDict() + + #: File-like object representation of response (for advanced usage). + #: Use of ``raw`` requires that ``stream=True`` be set on the request. + # This requirement does not apply for use internally to Requests. + self.raw = None + + #: Final URL location of Response. + self.url = None + + #: Encoding to decode with when accessing r.text. + self.encoding = None + + #: A list of :class:`Response <Response>` objects from + #: the history of the Request. Any redirect responses will end + #: up here. The list is sorted from the oldest to the most recent request. + self.history = [] + + #: Textual reason of responded HTTP Status, e.g. "Not Found" or "OK". + self.reason = None + + #: A CookieJar of Cookies the server sent back. + self.cookies = cookiejar_from_dict({}) + + #: The amount of time elapsed between sending the request + #: and the arrival of the response (as a timedelta). + #: This property specifically measures the time taken between sending + #: the first byte of the request and finishing parsing the headers. It + #: is therefore unaffected by consuming the response content or the + #: value of the ``stream`` keyword argument. + self.elapsed = datetime.timedelta(0) + + #: The :class:`PreparedRequest <PreparedRequest>` object to which this + #: is a response. + self.request = None + + def __enter__(self): + return self + + def __exit__(self, *args): + self.close() + + def __getstate__(self): + # Consume everything; accessing the content attribute makes + # sure the content has been fully read. + if not self._content_consumed: + self.content + + return {attr: getattr(self, attr, None) for attr in self.__attrs__} + + def __setstate__(self, state): + for name, value in state.items(): + setattr(self, name, value) + + # pickled objects do not have .raw + setattr(self, '_content_consumed', True) + setattr(self, 'raw', None) + + def __repr__(self): + return '<Response [%s]>' % (self.status_code) + + def __bool__(self): + """Returns True if :attr:`status_code` is less than 400. + + This attribute checks if the status code of the response is between + 400 and 600 to see if there was a client error or a server error. If + the status code, is between 200 and 400, this will return True. This + is **not** a check to see if the response code is ``200 OK``. + """ + return self.ok + + def __nonzero__(self): + """Returns True if :attr:`status_code` is less than 400. + + This attribute checks if the status code of the response is between + 400 and 600 to see if there was a client error or a server error. If + the status code, is between 200 and 400, this will return True. This + is **not** a check to see if the response code is ``200 OK``. + """ + return self.ok + + def __iter__(self): + """Allows you to use a response as an iterator.""" + return self.iter_content(128) + + @property + def ok(self): + """Returns True if :attr:`status_code` is less than 400, False if not. + + This attribute checks if the status code of the response is between + 400 and 600 to see if there was a client error or a server error. If + the status code is between 200 and 400, this will return True. This + is **not** a check to see if the response code is ``200 OK``. + """ + try: + self.raise_for_status() + except HTTPError: + return False + return True + + @property + def is_redirect(self): + """True if this Response is a well-formed HTTP redirect that could have + been processed automatically (by :meth:`Session.resolve_redirects`). + """ + return ('location' in self.headers and self.status_code in REDIRECT_STATI) + + @property + def is_permanent_redirect(self): + """True if this Response one of the permanent versions of redirect.""" + return ('location' in self.headers and self.status_code in (codes.moved_permanently, codes.permanent_redirect)) + + @property + def next(self): + """Returns a PreparedRequest for the next request in a redirect chain, if there is one.""" + return self._next + + @property + def apparent_encoding(self): + """The apparent encoding, provided by the chardet library.""" + return chardet.detect(self.content)['encoding'] + + def iter_content(self, chunk_size=1, decode_unicode=False): + """Iterates over the response data. When stream=True is set on the + request, this avoids reading the content at once into memory for + large responses. The chunk size is the number of bytes it should + read into memory. This is not necessarily the length of each item + returned as decoding can take place. + + chunk_size must be of type int or None. A value of None will + function differently depending on the value of `stream`. + stream=True will read data as it arrives in whatever size the + chunks are received. If stream=False, data is returned as + a single chunk. + + If decode_unicode is True, content will be decoded using the best + available encoding based on the response. + """ + + def generate(): + # Special case for urllib3. + if hasattr(self.raw, 'stream'): + try: + for chunk in self.raw.stream(chunk_size, decode_content=True): + yield chunk + except ProtocolError as e: + raise ChunkedEncodingError(e) + except DecodeError as e: + raise ContentDecodingError(e) + except ReadTimeoutError as e: + raise ConnectionError(e) + else: + # Standard file-like object. + while True: + chunk = self.raw.read(chunk_size) + if not chunk: + break + yield chunk + + self._content_consumed = True + + if self._content_consumed and isinstance(self._content, bool): + raise StreamConsumedError() + elif chunk_size is not None and not isinstance(chunk_size, int): + raise TypeError("chunk_size must be an int, it is instead a %s." % type(chunk_size)) + # simulate reading small chunks of the content + reused_chunks = iter_slices(self._content, chunk_size) + + stream_chunks = generate() + + chunks = reused_chunks if self._content_consumed else stream_chunks + + if decode_unicode: + chunks = stream_decode_response_unicode(chunks, self) + + return chunks + + def iter_lines(self, chunk_size=ITER_CHUNK_SIZE, decode_unicode=False, delimiter=None): + """Iterates over the response data, one line at a time. When + stream=True is set on the request, this avoids reading the + content at once into memory for large responses. + + .. note:: This method is not reentrant safe. + """ + + pending = None + + for chunk in self.iter_content(chunk_size=chunk_size, decode_unicode=decode_unicode): + + if pending is not None: + chunk = pending + chunk + + if delimiter: + lines = chunk.split(delimiter) + else: + lines = chunk.splitlines() + + if lines and lines[-1] and chunk and lines[-1][-1] == chunk[-1]: + pending = lines.pop() + else: + pending = None + + for line in lines: + yield line + + if pending is not None: + yield pending + + @property + def content(self): + """Content of the response, in bytes.""" + + if self._content is False: + # Read the contents. + if self._content_consumed: + raise RuntimeError( + 'The content for this response was already consumed') + + if self.status_code == 0 or self.raw is None: + self._content = None + else: + self._content = b''.join(self.iter_content(CONTENT_CHUNK_SIZE)) or b'' + + self._content_consumed = True + # don't need to release the connection; that's been handled by urllib3 + # since we exhausted the data. + return self._content + + @property + def text(self): + """Content of the response, in unicode. + + If Response.encoding is None, encoding will be guessed using + ``chardet``. + + The encoding of the response content is determined based solely on HTTP + headers, following RFC 2616 to the letter. If you can take advantage of + non-HTTP knowledge to make a better guess at the encoding, you should + set ``r.encoding`` appropriately before accessing this property. + """ + + # Try charset from content-type + content = None + encoding = self.encoding + + if not self.content: + return str('') + + # Fallback to auto-detected encoding. + if self.encoding is None: + encoding = self.apparent_encoding + + # Decode unicode from given encoding. + try: + content = str(self.content, encoding, errors='replace') + except (LookupError, TypeError): + # A LookupError is raised if the encoding was not found which could + # indicate a misspelling or similar mistake. + # + # A TypeError can be raised if encoding is None + # + # So we try blindly encoding. + content = str(self.content, errors='replace') + + return content + + def json(self, **kwargs): + r"""Returns the json-encoded content of a response, if any. + + :param \*\*kwargs: Optional arguments that ``json.loads`` takes. + :raises ValueError: If the response body does not contain valid json. + """ + + if not self.encoding and self.content and len(self.content) > 3: + # No encoding set. JSON RFC 4627 section 3 states we should expect + # UTF-8, -16 or -32. Detect which one to use; If the detection or + # decoding fails, fall back to `self.text` (using chardet to make + # a best guess). + encoding = guess_json_utf(self.content) + if encoding is not None: + try: + return complexjson.loads( + self.content.decode(encoding), **kwargs + ) + except UnicodeDecodeError: + # Wrong UTF codec detected; usually because it's not UTF-8 + # but some other 8-bit codec. This is an RFC violation, + # and the server didn't bother to tell us what codec *was* + # used. + pass + return complexjson.loads(self.text, **kwargs) + + @property + def links(self): + """Returns the parsed header links of the response, if any.""" + + header = self.headers.get('link') + + # l = MultiDict() + l = {} + + if header: + links = parse_header_links(header) + + for link in links: + key = link.get('rel') or link.get('url') + l[key] = link + + return l + + def raise_for_status(self): + """Raises stored :class:`HTTPError`, if one occurred.""" + + http_error_msg = '' + if isinstance(self.reason, bytes): + # We attempt to decode utf-8 first because some servers + # choose to localize their reason strings. If the string + # isn't utf-8, we fall back to iso-8859-1 for all other + # encodings. (See PR #3538) + try: + reason = self.reason.decode('utf-8') + except UnicodeDecodeError: + reason = self.reason.decode('iso-8859-1') + else: + reason = self.reason + + if 400 <= self.status_code < 500: + http_error_msg = u'%s Client Error: %s for url: %s' % (self.status_code, reason, self.url) + + elif 500 <= self.status_code < 600: + http_error_msg = u'%s Server Error: %s for url: %s' % (self.status_code, reason, self.url) + + if http_error_msg: + raise HTTPError(http_error_msg, response=self) + + def close(self): + """Releases the connection back to the pool. Once this method has been + called the underlying ``raw`` object must not be accessed again. + + *Note: Should not normally need to be called explicitly.* + """ + if not self._content_consumed: + self.raw.close() + + release_conn = getattr(self.raw, 'release_conn', None) + if release_conn is not None: + release_conn() diff --git a/venv2/lib/python2.7/site-packages/pip/_vendor/requests/packages.py b/venv2/lib/python2.7/site-packages/pip/_vendor/requests/packages.py new file mode 100644 index 0000000..9582fa7 --- /dev/null +++ b/venv2/lib/python2.7/site-packages/pip/_vendor/requests/packages.py @@ -0,0 +1,16 @@ +import sys + +# This code exists for backwards compatibility reasons. +# I don't like it either. Just look the other way. :) + +for package in ('urllib3', 'idna', 'chardet'): + vendored_package = "pip._vendor." + package + locals()[package] = __import__(vendored_package) + # This traversal is apparently necessary such that the identities are + # preserved (requests.packages.urllib3.* is urllib3.*) + for mod in list(sys.modules): + if mod == vendored_package or mod.startswith(vendored_package + '.'): + unprefixed_mod = mod[len("pip._vendor."):] + sys.modules['pip._vendor.requests.packages.' + unprefixed_mod] = sys.modules[mod] + +# Kinda cool, though, right? diff --git a/venv2/lib/python2.7/site-packages/pip/_vendor/requests/sessions.py b/venv2/lib/python2.7/site-packages/pip/_vendor/requests/sessions.py new file mode 100644 index 0000000..d73d700 --- /dev/null +++ b/venv2/lib/python2.7/site-packages/pip/_vendor/requests/sessions.py @@ -0,0 +1,770 @@ +# -*- coding: utf-8 -*- + +""" +requests.session +~~~~~~~~~~~~~~~~ + +This module provides a Session object to manage and persist settings across +requests (cookies, auth, proxies). +""" +import os +import sys +import time +from datetime import timedelta + +from .auth import _basic_auth_str +from .compat import cookielib, is_py3, OrderedDict, urljoin, urlparse, Mapping +from .cookies import ( + cookiejar_from_dict, extract_cookies_to_jar, RequestsCookieJar, merge_cookies) +from .models import Request, PreparedRequest, DEFAULT_REDIRECT_LIMIT +from .hooks import default_hooks, dispatch_hook +from ._internal_utils import to_native_string +from .utils import to_key_val_list, default_headers, DEFAULT_PORTS +from .exceptions import ( + TooManyRedirects, InvalidSchema, ChunkedEncodingError, ContentDecodingError) + +from .structures import CaseInsensitiveDict +from .adapters import HTTPAdapter + +from .utils import ( + requote_uri, get_environ_proxies, get_netrc_auth, should_bypass_proxies, + get_auth_from_url, rewind_body +) + +from .status_codes import codes + +# formerly defined here, reexposed here for backward compatibility +from .models import REDIRECT_STATI + +# Preferred clock, based on which one is more accurate on a given system. +if sys.platform == 'win32': + try: # Python 3.4+ + preferred_clock = time.perf_counter + except AttributeError: # Earlier than Python 3. + preferred_clock = time.clock +else: + preferred_clock = time.time + + +def merge_setting(request_setting, session_setting, dict_class=OrderedDict): + """Determines appropriate setting for a given request, taking into account + the explicit setting on that request, and the setting in the session. If a + setting is a dictionary, they will be merged together using `dict_class` + """ + + if session_setting is None: + return request_setting + + if request_setting is None: + return session_setting + + # Bypass if not a dictionary (e.g. verify) + if not ( + isinstance(session_setting, Mapping) and + isinstance(request_setting, Mapping) + ): + return request_setting + + merged_setting = dict_class(to_key_val_list(session_setting)) + merged_setting.update(to_key_val_list(request_setting)) + + # Remove keys that are set to None. Extract keys first to avoid altering + # the dictionary during iteration. + none_keys = [k for (k, v) in merged_setting.items() if v is None] + for key in none_keys: + del merged_setting[key] + + return merged_setting + + +def merge_hooks(request_hooks, session_hooks, dict_class=OrderedDict): + """Properly merges both requests and session hooks. + + This is necessary because when request_hooks == {'response': []}, the + merge breaks Session hooks entirely. + """ + if session_hooks is None or session_hooks.get('response') == []: + return request_hooks + + if request_hooks is None or request_hooks.get('response') == []: + return session_hooks + + return merge_setting(request_hooks, session_hooks, dict_class) + + +class SessionRedirectMixin(object): + + def get_redirect_target(self, resp): + """Receives a Response. Returns a redirect URI or ``None``""" + # Due to the nature of how requests processes redirects this method will + # be called at least once upon the original response and at least twice + # on each subsequent redirect response (if any). + # If a custom mixin is used to handle this logic, it may be advantageous + # to cache the redirect location onto the response object as a private + # attribute. + if resp.is_redirect: + location = resp.headers['location'] + # Currently the underlying http module on py3 decode headers + # in latin1, but empirical evidence suggests that latin1 is very + # rarely used with non-ASCII characters in HTTP headers. + # It is more likely to get UTF8 header rather than latin1. + # This causes incorrect handling of UTF8 encoded location headers. + # To solve this, we re-encode the location in latin1. + if is_py3: + location = location.encode('latin1') + return to_native_string(location, 'utf8') + return None + + def should_strip_auth(self, old_url, new_url): + """Decide whether Authorization header should be removed when redirecting""" + old_parsed = urlparse(old_url) + new_parsed = urlparse(new_url) + if old_parsed.hostname != new_parsed.hostname: + return True + # Special case: allow http -> https redirect when using the standard + # ports. This isn't specified by RFC 7235, but is kept to avoid + # breaking backwards compatibility with older versions of requests + # that allowed any redirects on the same host. + if (old_parsed.scheme == 'http' and old_parsed.port in (80, None) + and new_parsed.scheme == 'https' and new_parsed.port in (443, None)): + return False + + # Handle default port usage corresponding to scheme. + changed_port = old_parsed.port != new_parsed.port + changed_scheme = old_parsed.scheme != new_parsed.scheme + default_port = (DEFAULT_PORTS.get(old_parsed.scheme, None), None) + if (not changed_scheme and old_parsed.port in default_port + and new_parsed.port in default_port): + return False + + # Standard case: root URI must match + return changed_port or changed_scheme + + def resolve_redirects(self, resp, req, stream=False, timeout=None, + verify=True, cert=None, proxies=None, yield_requests=False, **adapter_kwargs): + """Receives a Response. Returns a generator of Responses or Requests.""" + + hist = [] # keep track of history + + url = self.get_redirect_target(resp) + previous_fragment = urlparse(req.url).fragment + while url: + prepared_request = req.copy() + + # Update history and keep track of redirects. + # resp.history must ignore the original request in this loop + hist.append(resp) + resp.history = hist[1:] + + try: + resp.content # Consume socket so it can be released + except (ChunkedEncodingError, ContentDecodingError, RuntimeError): + resp.raw.read(decode_content=False) + + if len(resp.history) >= self.max_redirects: + raise TooManyRedirects('Exceeded %s redirects.' % self.max_redirects, response=resp) + + # Release the connection back into the pool. + resp.close() + + # Handle redirection without scheme (see: RFC 1808 Section 4) + if url.startswith('//'): + parsed_rurl = urlparse(resp.url) + url = '%s:%s' % (to_native_string(parsed_rurl.scheme), url) + + # Normalize url case and attach previous fragment if needed (RFC 7231 7.1.2) + parsed = urlparse(url) + if parsed.fragment == '' and previous_fragment: + parsed = parsed._replace(fragment=previous_fragment) + elif parsed.fragment: + previous_fragment = parsed.fragment + url = parsed.geturl() + + # Facilitate relative 'location' headers, as allowed by RFC 7231. + # (e.g. '/path/to/resource' instead of 'http://domain.tld/path/to/resource') + # Compliant with RFC3986, we percent encode the url. + if not parsed.netloc: + url = urljoin(resp.url, requote_uri(url)) + else: + url = requote_uri(url) + + prepared_request.url = to_native_string(url) + + self.rebuild_method(prepared_request, resp) + + # https://github.com/requests/requests/issues/1084 + if resp.status_code not in (codes.temporary_redirect, codes.permanent_redirect): + # https://github.com/requests/requests/issues/3490 + purged_headers = ('Content-Length', 'Content-Type', 'Transfer-Encoding') + for header in purged_headers: + prepared_request.headers.pop(header, None) + prepared_request.body = None + + headers = prepared_request.headers + try: + del headers['Cookie'] + except KeyError: + pass + + # Extract any cookies sent on the response to the cookiejar + # in the new request. Because we've mutated our copied prepared + # request, use the old one that we haven't yet touched. + extract_cookies_to_jar(prepared_request._cookies, req, resp.raw) + merge_cookies(prepared_request._cookies, self.cookies) + prepared_request.prepare_cookies(prepared_request._cookies) + + # Rebuild auth and proxy information. + proxies = self.rebuild_proxies(prepared_request, proxies) + self.rebuild_auth(prepared_request, resp) + + # A failed tell() sets `_body_position` to `object()`. This non-None + # value ensures `rewindable` will be True, allowing us to raise an + # UnrewindableBodyError, instead of hanging the connection. + rewindable = ( + prepared_request._body_position is not None and + ('Content-Length' in headers or 'Transfer-Encoding' in headers) + ) + + # Attempt to rewind consumed file-like object. + if rewindable: + rewind_body(prepared_request) + + # Override the original request. + req = prepared_request + + if yield_requests: + yield req + else: + + resp = self.send( + req, + stream=stream, + timeout=timeout, + verify=verify, + cert=cert, + proxies=proxies, + allow_redirects=False, + **adapter_kwargs + ) + + extract_cookies_to_jar(self.cookies, prepared_request, resp.raw) + + # extract redirect url, if any, for the next loop + url = self.get_redirect_target(resp) + yield resp + + def rebuild_auth(self, prepared_request, response): + """When being redirected we may want to strip authentication from the + request to avoid leaking credentials. This method intelligently removes + and reapplies authentication where possible to avoid credential loss. + """ + headers = prepared_request.headers + url = prepared_request.url + + if 'Authorization' in headers and self.should_strip_auth(response.request.url, url): + # If we get redirected to a new host, we should strip out any + # authentication headers. + del headers['Authorization'] + + # .netrc might have more auth for us on our new host. + new_auth = get_netrc_auth(url) if self.trust_env else None + if new_auth is not None: + prepared_request.prepare_auth(new_auth) + + return + + def rebuild_proxies(self, prepared_request, proxies): + """This method re-evaluates the proxy configuration by considering the + environment variables. If we are redirected to a URL covered by + NO_PROXY, we strip the proxy configuration. Otherwise, we set missing + proxy keys for this URL (in case they were stripped by a previous + redirect). + + This method also replaces the Proxy-Authorization header where + necessary. + + :rtype: dict + """ + proxies = proxies if proxies is not None else {} + headers = prepared_request.headers + url = prepared_request.url + scheme = urlparse(url).scheme + new_proxies = proxies.copy() + no_proxy = proxies.get('no_proxy') + + bypass_proxy = should_bypass_proxies(url, no_proxy=no_proxy) + if self.trust_env and not bypass_proxy: + environ_proxies = get_environ_proxies(url, no_proxy=no_proxy) + + proxy = environ_proxies.get(scheme, environ_proxies.get('all')) + + if proxy: + new_proxies.setdefault(scheme, proxy) + + if 'Proxy-Authorization' in headers: + del headers['Proxy-Authorization'] + + try: + username, password = get_auth_from_url(new_proxies[scheme]) + except KeyError: + username, password = None, None + + if username and password: + headers['Proxy-Authorization'] = _basic_auth_str(username, password) + + return new_proxies + + def rebuild_method(self, prepared_request, response): + """When being redirected we may want to change the method of the request + based on certain specs or browser behavior. + """ + method = prepared_request.method + + # https://tools.ietf.org/html/rfc7231#section-6.4.4 + if response.status_code == codes.see_other and method != 'HEAD': + method = 'GET' + + # Do what the browsers do, despite standards... + # First, turn 302s into GETs. + if response.status_code == codes.found and method != 'HEAD': + method = 'GET' + + # Second, if a POST is responded to with a 301, turn it into a GET. + # This bizarre behaviour is explained in Issue 1704. + if response.status_code == codes.moved and method == 'POST': + method = 'GET' + + prepared_request.method = method + + +class Session(SessionRedirectMixin): + """A Requests session. + + Provides cookie persistence, connection-pooling, and configuration. + + Basic Usage:: + + >>> import requests + >>> s = requests.Session() + >>> s.get('https://httpbin.org/get') + <Response [200]> + + Or as a context manager:: + + >>> with requests.Session() as s: + >>> s.get('https://httpbin.org/get') + <Response [200]> + """ + + __attrs__ = [ + 'headers', 'cookies', 'auth', 'proxies', 'hooks', 'params', 'verify', + 'cert', 'prefetch', 'adapters', 'stream', 'trust_env', + 'max_redirects', + ] + + def __init__(self): + + #: A case-insensitive dictionary of headers to be sent on each + #: :class:`Request <Request>` sent from this + #: :class:`Session <Session>`. + self.headers = default_headers() + + #: Default Authentication tuple or object to attach to + #: :class:`Request <Request>`. + self.auth = None + + #: Dictionary mapping protocol or protocol and host to the URL of the proxy + #: (e.g. {'http': 'foo.bar:3128', 'http://host.name': 'foo.bar:4012'}) to + #: be used on each :class:`Request <Request>`. + self.proxies = {} + + #: Event-handling hooks. + self.hooks = default_hooks() + + #: Dictionary of querystring data to attach to each + #: :class:`Request <Request>`. The dictionary values may be lists for + #: representing multivalued query parameters. + self.params = {} + + #: Stream response content default. + self.stream = False + + #: SSL Verification default. + self.verify = True + + #: SSL client certificate default, if String, path to ssl client + #: cert file (.pem). If Tuple, ('cert', 'key') pair. + self.cert = None + + #: Maximum number of redirects allowed. If the request exceeds this + #: limit, a :class:`TooManyRedirects` exception is raised. + #: This defaults to requests.models.DEFAULT_REDIRECT_LIMIT, which is + #: 30. + self.max_redirects = DEFAULT_REDIRECT_LIMIT + + #: Trust environment settings for proxy configuration, default + #: authentication and similar. + self.trust_env = True + + #: A CookieJar containing all currently outstanding cookies set on this + #: session. By default it is a + #: :class:`RequestsCookieJar <requests.cookies.RequestsCookieJar>`, but + #: may be any other ``cookielib.CookieJar`` compatible object. + self.cookies = cookiejar_from_dict({}) + + # Default connection adapters. + self.adapters = OrderedDict() + self.mount('https://', HTTPAdapter()) + self.mount('http://', HTTPAdapter()) + + def __enter__(self): + return self + + def __exit__(self, *args): + self.close() + + def prepare_request(self, request): + """Constructs a :class:`PreparedRequest <PreparedRequest>` for + transmission and returns it. The :class:`PreparedRequest` has settings + merged from the :class:`Request <Request>` instance and those of the + :class:`Session`. + + :param request: :class:`Request` instance to prepare with this + session's settings. + :rtype: requests.PreparedRequest + """ + cookies = request.cookies or {} + + # Bootstrap CookieJar. + if not isinstance(cookies, cookielib.CookieJar): + cookies = cookiejar_from_dict(cookies) + + # Merge with session cookies + merged_cookies = merge_cookies( + merge_cookies(RequestsCookieJar(), self.cookies), cookies) + + # Set environment's basic authentication if not explicitly set. + auth = request.auth + if self.trust_env and not auth and not self.auth: + auth = get_netrc_auth(request.url) + + p = PreparedRequest() + p.prepare( + method=request.method.upper(), + url=request.url, + files=request.files, + data=request.data, + json=request.json, + headers=merge_setting(request.headers, self.headers, dict_class=CaseInsensitiveDict), + params=merge_setting(request.params, self.params), + auth=merge_setting(auth, self.auth), + cookies=merged_cookies, + hooks=merge_hooks(request.hooks, self.hooks), + ) + return p + + def request(self, method, url, + params=None, data=None, headers=None, cookies=None, files=None, + auth=None, timeout=None, allow_redirects=True, proxies=None, + hooks=None, stream=None, verify=None, cert=None, json=None): + """Constructs a :class:`Request <Request>`, prepares it and sends it. + Returns :class:`Response <Response>` object. + + :param method: method for the new :class:`Request` object. + :param url: URL for the new :class:`Request` object. + :param params: (optional) Dictionary or bytes to be sent in the query + string for the :class:`Request`. + :param data: (optional) Dictionary, list of tuples, bytes, or file-like + object to send in the body of the :class:`Request`. + :param json: (optional) json to send in the body of the + :class:`Request`. + :param headers: (optional) Dictionary of HTTP Headers to send with the + :class:`Request`. + :param cookies: (optional) Dict or CookieJar object to send with the + :class:`Request`. + :param files: (optional) Dictionary of ``'filename': file-like-objects`` + for multipart encoding upload. + :param auth: (optional) Auth tuple or callable to enable + Basic/Digest/Custom HTTP Auth. + :param timeout: (optional) How long to wait for the server to send + data before giving up, as a float, or a :ref:`(connect timeout, + read timeout) <timeouts>` tuple. + :type timeout: float or tuple + :param allow_redirects: (optional) Set to True by default. + :type allow_redirects: bool + :param proxies: (optional) Dictionary mapping protocol or protocol and + hostname to the URL of the proxy. + :param stream: (optional) whether to immediately download the response + content. Defaults to ``False``. + :param verify: (optional) Either a boolean, in which case it controls whether we verify + the server's TLS certificate, or a string, in which case it must be a path + to a CA bundle to use. Defaults to ``True``. + :param cert: (optional) if String, path to ssl client cert file (.pem). + If Tuple, ('cert', 'key') pair. + :rtype: requests.Response + """ + # Create the Request. + req = Request( + method=method.upper(), + url=url, + headers=headers, + files=files, + data=data or {}, + json=json, + params=params or {}, + auth=auth, + cookies=cookies, + hooks=hooks, + ) + prep = self.prepare_request(req) + + proxies = proxies or {} + + settings = self.merge_environment_settings( + prep.url, proxies, stream, verify, cert + ) + + # Send the request. + send_kwargs = { + 'timeout': timeout, + 'allow_redirects': allow_redirects, + } + send_kwargs.update(settings) + resp = self.send(prep, **send_kwargs) + + return resp + + def get(self, url, **kwargs): + r"""Sends a GET request. Returns :class:`Response` object. + + :param url: URL for the new :class:`Request` object. + :param \*\*kwargs: Optional arguments that ``request`` takes. + :rtype: requests.Response + """ + + kwargs.setdefault('allow_redirects', True) + return self.request('GET', url, **kwargs) + + def options(self, url, **kwargs): + r"""Sends a OPTIONS request. Returns :class:`Response` object. + + :param url: URL for the new :class:`Request` object. + :param \*\*kwargs: Optional arguments that ``request`` takes. + :rtype: requests.Response + """ + + kwargs.setdefault('allow_redirects', True) + return self.request('OPTIONS', url, **kwargs) + + def head(self, url, **kwargs): + r"""Sends a HEAD request. Returns :class:`Response` object. + + :param url: URL for the new :class:`Request` object. + :param \*\*kwargs: Optional arguments that ``request`` takes. + :rtype: requests.Response + """ + + kwargs.setdefault('allow_redirects', False) + return self.request('HEAD', url, **kwargs) + + def post(self, url, data=None, json=None, **kwargs): + r"""Sends a POST request. Returns :class:`Response` object. + + :param url: URL for the new :class:`Request` object. + :param data: (optional) Dictionary, list of tuples, bytes, or file-like + object to send in the body of the :class:`Request`. + :param json: (optional) json to send in the body of the :class:`Request`. + :param \*\*kwargs: Optional arguments that ``request`` takes. + :rtype: requests.Response + """ + + return self.request('POST', url, data=data, json=json, **kwargs) + + def put(self, url, data=None, **kwargs): + r"""Sends a PUT request. Returns :class:`Response` object. + + :param url: URL for the new :class:`Request` object. + :param data: (optional) Dictionary, list of tuples, bytes, or file-like + object to send in the body of the :class:`Request`. + :param \*\*kwargs: Optional arguments that ``request`` takes. + :rtype: requests.Response + """ + + return self.request('PUT', url, data=data, **kwargs) + + def patch(self, url, data=None, **kwargs): + r"""Sends a PATCH request. Returns :class:`Response` object. + + :param url: URL for the new :class:`Request` object. + :param data: (optional) Dictionary, list of tuples, bytes, or file-like + object to send in the body of the :class:`Request`. + :param \*\*kwargs: Optional arguments that ``request`` takes. + :rtype: requests.Response + """ + + return self.request('PATCH', url, data=data, **kwargs) + + def delete(self, url, **kwargs): + r"""Sends a DELETE request. Returns :class:`Response` object. + + :param url: URL for the new :class:`Request` object. + :param \*\*kwargs: Optional arguments that ``request`` takes. + :rtype: requests.Response + """ + + return self.request('DELETE', url, **kwargs) + + def send(self, request, **kwargs): + """Send a given PreparedRequest. + + :rtype: requests.Response + """ + # Set defaults that the hooks can utilize to ensure they always have + # the correct parameters to reproduce the previous request. + kwargs.setdefault('stream', self.stream) + kwargs.setdefault('verify', self.verify) + kwargs.setdefault('cert', self.cert) + kwargs.setdefault('proxies', self.proxies) + + # It's possible that users might accidentally send a Request object. + # Guard against that specific failure case. + if isinstance(request, Request): + raise ValueError('You can only send PreparedRequests.') + + # Set up variables needed for resolve_redirects and dispatching of hooks + allow_redirects = kwargs.pop('allow_redirects', True) + stream = kwargs.get('stream') + hooks = request.hooks + + # Get the appropriate adapter to use + adapter = self.get_adapter(url=request.url) + + # Start time (approximately) of the request + start = preferred_clock() + + # Send the request + r = adapter.send(request, **kwargs) + + # Total elapsed time of the request (approximately) + elapsed = preferred_clock() - start + r.elapsed = timedelta(seconds=elapsed) + + # Response manipulation hooks + r = dispatch_hook('response', hooks, r, **kwargs) + + # Persist cookies + if r.history: + + # If the hooks create history then we want those cookies too + for resp in r.history: + extract_cookies_to_jar(self.cookies, resp.request, resp.raw) + + extract_cookies_to_jar(self.cookies, request, r.raw) + + # Redirect resolving generator. + gen = self.resolve_redirects(r, request, **kwargs) + + # Resolve redirects if allowed. + history = [resp for resp in gen] if allow_redirects else [] + + # Shuffle things around if there's history. + if history: + # Insert the first (original) request at the start + history.insert(0, r) + # Get the last request made + r = history.pop() + r.history = history + + # If redirects aren't being followed, store the response on the Request for Response.next(). + if not allow_redirects: + try: + r._next = next(self.resolve_redirects(r, request, yield_requests=True, **kwargs)) + except StopIteration: + pass + + if not stream: + r.content + + return r + + def merge_environment_settings(self, url, proxies, stream, verify, cert): + """ + Check the environment and merge it with some settings. + + :rtype: dict + """ + # Gather clues from the surrounding environment. + if self.trust_env: + # Set environment's proxies. + no_proxy = proxies.get('no_proxy') if proxies is not None else None + env_proxies = get_environ_proxies(url, no_proxy=no_proxy) + for (k, v) in env_proxies.items(): + proxies.setdefault(k, v) + + # Look for requests environment configuration and be compatible + # with cURL. + if verify is True or verify is None: + verify = (os.environ.get('REQUESTS_CA_BUNDLE') or + os.environ.get('CURL_CA_BUNDLE')) + + # Merge all the kwargs. + proxies = merge_setting(proxies, self.proxies) + stream = merge_setting(stream, self.stream) + verify = merge_setting(verify, self.verify) + cert = merge_setting(cert, self.cert) + + return {'verify': verify, 'proxies': proxies, 'stream': stream, + 'cert': cert} + + def get_adapter(self, url): + """ + Returns the appropriate connection adapter for the given URL. + + :rtype: requests.adapters.BaseAdapter + """ + for (prefix, adapter) in self.adapters.items(): + + if url.lower().startswith(prefix.lower()): + return adapter + + # Nothing matches :-/ + raise InvalidSchema("No connection adapters were found for '%s'" % url) + + def close(self): + """Closes all adapters and as such the session""" + for v in self.adapters.values(): + v.close() + + def mount(self, prefix, adapter): + """Registers a connection adapter to a prefix. + + Adapters are sorted in descending order by prefix length. + """ + self.adapters[prefix] = adapter + keys_to_move = [k for k in self.adapters if len(k) < len(prefix)] + + for key in keys_to_move: + self.adapters[key] = self.adapters.pop(key) + + def __getstate__(self): + state = {attr: getattr(self, attr, None) for attr in self.__attrs__} + return state + + def __setstate__(self, state): + for attr, value in state.items(): + setattr(self, attr, value) + + +def session(): + """ + Returns a :class:`Session` for context-management. + + .. deprecated:: 1.0.0 + + This method has been deprecated since version 1.0.0 and is only kept for + backwards compatibility. New code should use :class:`~requests.sessions.Session` + to create a session. This may be removed at a future date. + + :rtype: Session + """ + return Session() diff --git a/venv2/lib/python2.7/site-packages/pip/_vendor/requests/status_codes.py b/venv2/lib/python2.7/site-packages/pip/_vendor/requests/status_codes.py new file mode 100644 index 0000000..813e8c4 --- /dev/null +++ b/venv2/lib/python2.7/site-packages/pip/_vendor/requests/status_codes.py @@ -0,0 +1,120 @@ +# -*- coding: utf-8 -*- + +r""" +The ``codes`` object defines a mapping from common names for HTTP statuses +to their numerical codes, accessible either as attributes or as dictionary +items. + +>>> requests.codes['temporary_redirect'] +307 +>>> requests.codes.teapot +418 +>>> requests.codes['\o/'] +200 + +Some codes have multiple names, and both upper- and lower-case versions of +the names are allowed. For example, ``codes.ok``, ``codes.OK``, and +``codes.okay`` all correspond to the HTTP status code 200. +""" + +from .structures import LookupDict + +_codes = { + + # Informational. + 100: ('continue',), + 101: ('switching_protocols',), + 102: ('processing',), + 103: ('checkpoint',), + 122: ('uri_too_long', 'request_uri_too_long'), + 200: ('ok', 'okay', 'all_ok', 'all_okay', 'all_good', '\\o/', '✓'), + 201: ('created',), + 202: ('accepted',), + 203: ('non_authoritative_info', 'non_authoritative_information'), + 204: ('no_content',), + 205: ('reset_content', 'reset'), + 206: ('partial_content', 'partial'), + 207: ('multi_status', 'multiple_status', 'multi_stati', 'multiple_stati'), + 208: ('already_reported',), + 226: ('im_used',), + + # Redirection. + 300: ('multiple_choices',), + 301: ('moved_permanently', 'moved', '\\o-'), + 302: ('found',), + 303: ('see_other', 'other'), + 304: ('not_modified',), + 305: ('use_proxy',), + 306: ('switch_proxy',), + 307: ('temporary_redirect', 'temporary_moved', 'temporary'), + 308: ('permanent_redirect', + 'resume_incomplete', 'resume',), # These 2 to be removed in 3.0 + + # Client Error. + 400: ('bad_request', 'bad'), + 401: ('unauthorized',), + 402: ('payment_required', 'payment'), + 403: ('forbidden',), + 404: ('not_found', '-o-'), + 405: ('method_not_allowed', 'not_allowed'), + 406: ('not_acceptable',), + 407: ('proxy_authentication_required', 'proxy_auth', 'proxy_authentication'), + 408: ('request_timeout', 'timeout'), + 409: ('conflict',), + 410: ('gone',), + 411: ('length_required',), + 412: ('precondition_failed', 'precondition'), + 413: ('request_entity_too_large',), + 414: ('request_uri_too_large',), + 415: ('unsupported_media_type', 'unsupported_media', 'media_type'), + 416: ('requested_range_not_satisfiable', 'requested_range', 'range_not_satisfiable'), + 417: ('expectation_failed',), + 418: ('im_a_teapot', 'teapot', 'i_am_a_teapot'), + 421: ('misdirected_request',), + 422: ('unprocessable_entity', 'unprocessable'), + 423: ('locked',), + 424: ('failed_dependency', 'dependency'), + 425: ('unordered_collection', 'unordered'), + 426: ('upgrade_required', 'upgrade'), + 428: ('precondition_required', 'precondition'), + 429: ('too_many_requests', 'too_many'), + 431: ('header_fields_too_large', 'fields_too_large'), + 444: ('no_response', 'none'), + 449: ('retry_with', 'retry'), + 450: ('blocked_by_windows_parental_controls', 'parental_controls'), + 451: ('unavailable_for_legal_reasons', 'legal_reasons'), + 499: ('client_closed_request',), + + # Server Error. + 500: ('internal_server_error', 'server_error', '/o\\', '✗'), + 501: ('not_implemented',), + 502: ('bad_gateway',), + 503: ('service_unavailable', 'unavailable'), + 504: ('gateway_timeout',), + 505: ('http_version_not_supported', 'http_version'), + 506: ('variant_also_negotiates',), + 507: ('insufficient_storage',), + 509: ('bandwidth_limit_exceeded', 'bandwidth'), + 510: ('not_extended',), + 511: ('network_authentication_required', 'network_auth', 'network_authentication'), +} + +codes = LookupDict(name='status_codes') + +def _init(): + for code, titles in _codes.items(): + for title in titles: + setattr(codes, title, code) + if not title.startswith(('\\', '/')): + setattr(codes, title.upper(), code) + + def doc(code): + names = ', '.join('``%s``' % n for n in _codes[code]) + return '* %d: %s' % (code, names) + + global __doc__ + __doc__ = (__doc__ + '\n' + + '\n'.join(doc(code) for code in sorted(_codes)) + if __doc__ is not None else None) + +_init() diff --git a/venv2/lib/python2.7/site-packages/pip/_vendor/requests/structures.py b/venv2/lib/python2.7/site-packages/pip/_vendor/requests/structures.py new file mode 100644 index 0000000..da930e2 --- /dev/null +++ b/venv2/lib/python2.7/site-packages/pip/_vendor/requests/structures.py @@ -0,0 +1,103 @@ +# -*- coding: utf-8 -*- + +""" +requests.structures +~~~~~~~~~~~~~~~~~~~ + +Data structures that power Requests. +""" + +from .compat import OrderedDict, Mapping, MutableMapping + + +class CaseInsensitiveDict(MutableMapping): + """A case-insensitive ``dict``-like object. + + Implements all methods and operations of + ``MutableMapping`` as well as dict's ``copy``. Also + provides ``lower_items``. + + All keys are expected to be strings. The structure remembers the + case of the last key to be set, and ``iter(instance)``, + ``keys()``, ``items()``, ``iterkeys()``, and ``iteritems()`` + will contain case-sensitive keys. However, querying and contains + testing is case insensitive:: + + cid = CaseInsensitiveDict() + cid['Accept'] = 'application/json' + cid['aCCEPT'] == 'application/json' # True + list(cid) == ['Accept'] # True + + For example, ``headers['content-encoding']`` will return the + value of a ``'Content-Encoding'`` response header, regardless + of how the header name was originally stored. + + If the constructor, ``.update``, or equality comparison + operations are given keys that have equal ``.lower()``s, the + behavior is undefined. + """ + + def __init__(self, data=None, **kwargs): + self._store = OrderedDict() + if data is None: + data = {} + self.update(data, **kwargs) + + def __setitem__(self, key, value): + # Use the lowercased key for lookups, but store the actual + # key alongside the value. + self._store[key.lower()] = (key, value) + + def __getitem__(self, key): + return self._store[key.lower()][1] + + def __delitem__(self, key): + del self._store[key.lower()] + + def __iter__(self): + return (casedkey for casedkey, mappedvalue in self._store.values()) + + def __len__(self): + return len(self._store) + + def lower_items(self): + """Like iteritems(), but with all lowercase keys.""" + return ( + (lowerkey, keyval[1]) + for (lowerkey, keyval) + in self._store.items() + ) + + def __eq__(self, other): + if isinstance(other, Mapping): + other = CaseInsensitiveDict(other) + else: + return NotImplemented + # Compare insensitively + return dict(self.lower_items()) == dict(other.lower_items()) + + # Copy is required + def copy(self): + return CaseInsensitiveDict(self._store.values()) + + def __repr__(self): + return str(dict(self.items())) + + +class LookupDict(dict): + """Dictionary lookup object.""" + + def __init__(self, name=None): + self.name = name + super(LookupDict, self).__init__() + + def __repr__(self): + return '<lookup \'%s\'>' % (self.name) + + def __getitem__(self, key): + # We allow fall-through here, so values default to None + + return self.__dict__.get(key, None) + + def get(self, key, default=None): + return self.__dict__.get(key, default) diff --git a/venv2/lib/python2.7/site-packages/pip/_vendor/requests/utils.py b/venv2/lib/python2.7/site-packages/pip/_vendor/requests/utils.py new file mode 100644 index 0000000..8170a8d --- /dev/null +++ b/venv2/lib/python2.7/site-packages/pip/_vendor/requests/utils.py @@ -0,0 +1,977 @@ +# -*- coding: utf-8 -*- + +""" +requests.utils +~~~~~~~~~~~~~~ + +This module provides utility functions that are used within Requests +that are also useful for external consumption. +""" + +import codecs +import contextlib +import io +import os +import re +import socket +import struct +import sys +import tempfile +import warnings +import zipfile + +from .__version__ import __version__ +from . import certs +# to_native_string is unused here, but imported here for backwards compatibility +from ._internal_utils import to_native_string +from .compat import parse_http_list as _parse_list_header +from .compat import ( + quote, urlparse, bytes, str, OrderedDict, unquote, getproxies, + proxy_bypass, urlunparse, basestring, integer_types, is_py3, + proxy_bypass_environment, getproxies_environment, Mapping) +from .cookies import cookiejar_from_dict +from .structures import CaseInsensitiveDict +from .exceptions import ( + InvalidURL, InvalidHeader, FileModeWarning, UnrewindableBodyError) + +NETRC_FILES = ('.netrc', '_netrc') + +DEFAULT_CA_BUNDLE_PATH = certs.where() + +DEFAULT_PORTS = {'http': 80, 'https': 443} + + +if sys.platform == 'win32': + # provide a proxy_bypass version on Windows without DNS lookups + + def proxy_bypass_registry(host): + try: + if is_py3: + import winreg + else: + import _winreg as winreg + except ImportError: + return False + + try: + internetSettings = winreg.OpenKey(winreg.HKEY_CURRENT_USER, + r'Software\Microsoft\Windows\CurrentVersion\Internet Settings') + # ProxyEnable could be REG_SZ or REG_DWORD, normalizing it + proxyEnable = int(winreg.QueryValueEx(internetSettings, + 'ProxyEnable')[0]) + # ProxyOverride is almost always a string + proxyOverride = winreg.QueryValueEx(internetSettings, + 'ProxyOverride')[0] + except OSError: + return False + if not proxyEnable or not proxyOverride: + return False + + # make a check value list from the registry entry: replace the + # '<local>' string by the localhost entry and the corresponding + # canonical entry. + proxyOverride = proxyOverride.split(';') + # now check if we match one of the registry values. + for test in proxyOverride: + if test == '<local>': + if '.' not in host: + return True + test = test.replace(".", r"\.") # mask dots + test = test.replace("*", r".*") # change glob sequence + test = test.replace("?", r".") # change glob char + if re.match(test, host, re.I): + return True + return False + + def proxy_bypass(host): # noqa + """Return True, if the host should be bypassed. + + Checks proxy settings gathered from the environment, if specified, + or the registry. + """ + if getproxies_environment(): + return proxy_bypass_environment(host) + else: + return proxy_bypass_registry(host) + + +def dict_to_sequence(d): + """Returns an internal sequence dictionary update.""" + + if hasattr(d, 'items'): + d = d.items() + + return d + + +def super_len(o): + total_length = None + current_position = 0 + + if hasattr(o, '__len__'): + total_length = len(o) + + elif hasattr(o, 'len'): + total_length = o.len + + elif hasattr(o, 'fileno'): + try: + fileno = o.fileno() + except io.UnsupportedOperation: + pass + else: + total_length = os.fstat(fileno).st_size + + # Having used fstat to determine the file length, we need to + # confirm that this file was opened up in binary mode. + if 'b' not in o.mode: + warnings.warn(( + "Requests has determined the content-length for this " + "request using the binary size of the file: however, the " + "file has been opened in text mode (i.e. without the 'b' " + "flag in the mode). This may lead to an incorrect " + "content-length. In Requests 3.0, support will be removed " + "for files in text mode."), + FileModeWarning + ) + + if hasattr(o, 'tell'): + try: + current_position = o.tell() + except (OSError, IOError): + # This can happen in some weird situations, such as when the file + # is actually a special file descriptor like stdin. In this + # instance, we don't know what the length is, so set it to zero and + # let requests chunk it instead. + if total_length is not None: + current_position = total_length + else: + if hasattr(o, 'seek') and total_length is None: + # StringIO and BytesIO have seek but no useable fileno + try: + # seek to end of file + o.seek(0, 2) + total_length = o.tell() + + # seek back to current position to support + # partially read file-like objects + o.seek(current_position or 0) + except (OSError, IOError): + total_length = 0 + + if total_length is None: + total_length = 0 + + return max(0, total_length - current_position) + + +def get_netrc_auth(url, raise_errors=False): + """Returns the Requests tuple auth for a given url from netrc.""" + + try: + from netrc import netrc, NetrcParseError + + netrc_path = None + + for f in NETRC_FILES: + try: + loc = os.path.expanduser('~/{}'.format(f)) + except KeyError: + # os.path.expanduser can fail when $HOME is undefined and + # getpwuid fails. See https://bugs.python.org/issue20164 & + # https://github.com/requests/requests/issues/1846 + return + + if os.path.exists(loc): + netrc_path = loc + break + + # Abort early if there isn't one. + if netrc_path is None: + return + + ri = urlparse(url) + + # Strip port numbers from netloc. This weird `if...encode`` dance is + # used for Python 3.2, which doesn't support unicode literals. + splitstr = b':' + if isinstance(url, str): + splitstr = splitstr.decode('ascii') + host = ri.netloc.split(splitstr)[0] + + try: + _netrc = netrc(netrc_path).authenticators(host) + if _netrc: + # Return with login / password + login_i = (0 if _netrc[0] else 1) + return (_netrc[login_i], _netrc[2]) + except (NetrcParseError, IOError): + # If there was a parsing error or a permissions issue reading the file, + # we'll just skip netrc auth unless explicitly asked to raise errors. + if raise_errors: + raise + + # AppEngine hackiness. + except (ImportError, AttributeError): + pass + + +def guess_filename(obj): + """Tries to guess the filename of the given object.""" + name = getattr(obj, 'name', None) + if (name and isinstance(name, basestring) and name[0] != '<' and + name[-1] != '>'): + return os.path.basename(name) + + +def extract_zipped_paths(path): + """Replace nonexistent paths that look like they refer to a member of a zip + archive with the location of an extracted copy of the target, or else + just return the provided path unchanged. + """ + if os.path.exists(path): + # this is already a valid path, no need to do anything further + return path + + # find the first valid part of the provided path and treat that as a zip archive + # assume the rest of the path is the name of a member in the archive + archive, member = os.path.split(path) + while archive and not os.path.exists(archive): + archive, prefix = os.path.split(archive) + member = '/'.join([prefix, member]) + + if not zipfile.is_zipfile(archive): + return path + + zip_file = zipfile.ZipFile(archive) + if member not in zip_file.namelist(): + return path + + # we have a valid zip archive and a valid member of that archive + tmp = tempfile.gettempdir() + extracted_path = os.path.join(tmp, *member.split('/')) + if not os.path.exists(extracted_path): + extracted_path = zip_file.extract(member, path=tmp) + + return extracted_path + + +def from_key_val_list(value): + """Take an object and test to see if it can be represented as a + dictionary. Unless it can not be represented as such, return an + OrderedDict, e.g., + + :: + + >>> from_key_val_list([('key', 'val')]) + OrderedDict([('key', 'val')]) + >>> from_key_val_list('string') + ValueError: cannot encode objects that are not 2-tuples + >>> from_key_val_list({'key': 'val'}) + OrderedDict([('key', 'val')]) + + :rtype: OrderedDict + """ + if value is None: + return None + + if isinstance(value, (str, bytes, bool, int)): + raise ValueError('cannot encode objects that are not 2-tuples') + + return OrderedDict(value) + + +def to_key_val_list(value): + """Take an object and test to see if it can be represented as a + dictionary. If it can be, return a list of tuples, e.g., + + :: + + >>> to_key_val_list([('key', 'val')]) + [('key', 'val')] + >>> to_key_val_list({'key': 'val'}) + [('key', 'val')] + >>> to_key_val_list('string') + ValueError: cannot encode objects that are not 2-tuples. + + :rtype: list + """ + if value is None: + return None + + if isinstance(value, (str, bytes, bool, int)): + raise ValueError('cannot encode objects that are not 2-tuples') + + if isinstance(value, Mapping): + value = value.items() + + return list(value) + + +# From mitsuhiko/werkzeug (used with permission). +def parse_list_header(value): + """Parse lists as described by RFC 2068 Section 2. + + In particular, parse comma-separated lists where the elements of + the list may include quoted-strings. A quoted-string could + contain a comma. A non-quoted string could have quotes in the + middle. Quotes are removed automatically after parsing. + + It basically works like :func:`parse_set_header` just that items + may appear multiple times and case sensitivity is preserved. + + The return value is a standard :class:`list`: + + >>> parse_list_header('token, "quoted value"') + ['token', 'quoted value'] + + To create a header from the :class:`list` again, use the + :func:`dump_header` function. + + :param value: a string with a list header. + :return: :class:`list` + :rtype: list + """ + result = [] + for item in _parse_list_header(value): + if item[:1] == item[-1:] == '"': + item = unquote_header_value(item[1:-1]) + result.append(item) + return result + + +# From mitsuhiko/werkzeug (used with permission). +def parse_dict_header(value): + """Parse lists of key, value pairs as described by RFC 2068 Section 2 and + convert them into a python dict: + + >>> d = parse_dict_header('foo="is a fish", bar="as well"') + >>> type(d) is dict + True + >>> sorted(d.items()) + [('bar', 'as well'), ('foo', 'is a fish')] + + If there is no value for a key it will be `None`: + + >>> parse_dict_header('key_without_value') + {'key_without_value': None} + + To create a header from the :class:`dict` again, use the + :func:`dump_header` function. + + :param value: a string with a dict header. + :return: :class:`dict` + :rtype: dict + """ + result = {} + for item in _parse_list_header(value): + if '=' not in item: + result[item] = None + continue + name, value = item.split('=', 1) + if value[:1] == value[-1:] == '"': + value = unquote_header_value(value[1:-1]) + result[name] = value + return result + + +# From mitsuhiko/werkzeug (used with permission). +def unquote_header_value(value, is_filename=False): + r"""Unquotes a header value. (Reversal of :func:`quote_header_value`). + This does not use the real unquoting but what browsers are actually + using for quoting. + + :param value: the header value to unquote. + :rtype: str + """ + if value and value[0] == value[-1] == '"': + # this is not the real unquoting, but fixing this so that the + # RFC is met will result in bugs with internet explorer and + # probably some other browsers as well. IE for example is + # uploading files with "C:\foo\bar.txt" as filename + value = value[1:-1] + + # if this is a filename and the starting characters look like + # a UNC path, then just return the value without quotes. Using the + # replace sequence below on a UNC path has the effect of turning + # the leading double slash into a single slash and then + # _fix_ie_filename() doesn't work correctly. See #458. + if not is_filename or value[:2] != '\\\\': + return value.replace('\\\\', '\\').replace('\\"', '"') + return value + + +def dict_from_cookiejar(cj): + """Returns a key/value dictionary from a CookieJar. + + :param cj: CookieJar object to extract cookies from. + :rtype: dict + """ + + cookie_dict = {} + + for cookie in cj: + cookie_dict[cookie.name] = cookie.value + + return cookie_dict + + +def add_dict_to_cookiejar(cj, cookie_dict): + """Returns a CookieJar from a key/value dictionary. + + :param cj: CookieJar to insert cookies into. + :param cookie_dict: Dict of key/values to insert into CookieJar. + :rtype: CookieJar + """ + + return cookiejar_from_dict(cookie_dict, cj) + + +def get_encodings_from_content(content): + """Returns encodings from given content string. + + :param content: bytestring to extract encodings from. + """ + warnings.warn(( + 'In requests 3.0, get_encodings_from_content will be removed. For ' + 'more information, please see the discussion on issue #2266. (This' + ' warning should only appear once.)'), + DeprecationWarning) + + charset_re = re.compile(r'<meta.*?charset=["\']*(.+?)["\'>]', flags=re.I) + pragma_re = re.compile(r'<meta.*?content=["\']*;?charset=(.+?)["\'>]', flags=re.I) + xml_re = re.compile(r'^<\?xml.*?encoding=["\']*(.+?)["\'>]') + + return (charset_re.findall(content) + + pragma_re.findall(content) + + xml_re.findall(content)) + + +def _parse_content_type_header(header): + """Returns content type and parameters from given header + + :param header: string + :return: tuple containing content type and dictionary of + parameters + """ + + tokens = header.split(';') + content_type, params = tokens[0].strip(), tokens[1:] + params_dict = {} + items_to_strip = "\"' " + + for param in params: + param = param.strip() + if param: + key, value = param, True + index_of_equals = param.find("=") + if index_of_equals != -1: + key = param[:index_of_equals].strip(items_to_strip) + value = param[index_of_equals + 1:].strip(items_to_strip) + params_dict[key.lower()] = value + return content_type, params_dict + + +def get_encoding_from_headers(headers): + """Returns encodings from given HTTP Header Dict. + + :param headers: dictionary to extract encoding from. + :rtype: str + """ + + content_type = headers.get('content-type') + + if not content_type: + return None + + content_type, params = _parse_content_type_header(content_type) + + if 'charset' in params: + return params['charset'].strip("'\"") + + if 'text' in content_type: + return 'ISO-8859-1' + + +def stream_decode_response_unicode(iterator, r): + """Stream decodes a iterator.""" + + if r.encoding is None: + for item in iterator: + yield item + return + + decoder = codecs.getincrementaldecoder(r.encoding)(errors='replace') + for chunk in iterator: + rv = decoder.decode(chunk) + if rv: + yield rv + rv = decoder.decode(b'', final=True) + if rv: + yield rv + + +def iter_slices(string, slice_length): + """Iterate over slices of a string.""" + pos = 0 + if slice_length is None or slice_length <= 0: + slice_length = len(string) + while pos < len(string): + yield string[pos:pos + slice_length] + pos += slice_length + + +def get_unicode_from_response(r): + """Returns the requested content back in unicode. + + :param r: Response object to get unicode content from. + + Tried: + + 1. charset from content-type + 2. fall back and replace all unicode characters + + :rtype: str + """ + warnings.warn(( + 'In requests 3.0, get_unicode_from_response will be removed. For ' + 'more information, please see the discussion on issue #2266. (This' + ' warning should only appear once.)'), + DeprecationWarning) + + tried_encodings = [] + + # Try charset from content-type + encoding = get_encoding_from_headers(r.headers) + + if encoding: + try: + return str(r.content, encoding) + except UnicodeError: + tried_encodings.append(encoding) + + # Fall back: + try: + return str(r.content, encoding, errors='replace') + except TypeError: + return r.content + + +# The unreserved URI characters (RFC 3986) +UNRESERVED_SET = frozenset( + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz" + "0123456789-._~") + + +def unquote_unreserved(uri): + """Un-escape any percent-escape sequences in a URI that are unreserved + characters. This leaves all reserved, illegal and non-ASCII bytes encoded. + + :rtype: str + """ + parts = uri.split('%') + for i in range(1, len(parts)): + h = parts[i][0:2] + if len(h) == 2 and h.isalnum(): + try: + c = chr(int(h, 16)) + except ValueError: + raise InvalidURL("Invalid percent-escape sequence: '%s'" % h) + + if c in UNRESERVED_SET: + parts[i] = c + parts[i][2:] + else: + parts[i] = '%' + parts[i] + else: + parts[i] = '%' + parts[i] + return ''.join(parts) + + +def requote_uri(uri): + """Re-quote the given URI. + + This function passes the given URI through an unquote/quote cycle to + ensure that it is fully and consistently quoted. + + :rtype: str + """ + safe_with_percent = "!#$%&'()*+,/:;=?@[]~" + safe_without_percent = "!#$&'()*+,/:;=?@[]~" + try: + # Unquote only the unreserved characters + # Then quote only illegal characters (do not quote reserved, + # unreserved, or '%') + return quote(unquote_unreserved(uri), safe=safe_with_percent) + except InvalidURL: + # We couldn't unquote the given URI, so let's try quoting it, but + # there may be unquoted '%'s in the URI. We need to make sure they're + # properly quoted so they do not cause issues elsewhere. + return quote(uri, safe=safe_without_percent) + + +def address_in_network(ip, net): + """This function allows you to check if an IP belongs to a network subnet + + Example: returns True if ip = 192.168.1.1 and net = 192.168.1.0/24 + returns False if ip = 192.168.1.1 and net = 192.168.100.0/24 + + :rtype: bool + """ + ipaddr = struct.unpack('=L', socket.inet_aton(ip))[0] + netaddr, bits = net.split('/') + netmask = struct.unpack('=L', socket.inet_aton(dotted_netmask(int(bits))))[0] + network = struct.unpack('=L', socket.inet_aton(netaddr))[0] & netmask + return (ipaddr & netmask) == (network & netmask) + + +def dotted_netmask(mask): + """Converts mask from /xx format to xxx.xxx.xxx.xxx + + Example: if mask is 24 function returns 255.255.255.0 + + :rtype: str + """ + bits = 0xffffffff ^ (1 << 32 - mask) - 1 + return socket.inet_ntoa(struct.pack('>I', bits)) + + +def is_ipv4_address(string_ip): + """ + :rtype: bool + """ + try: + socket.inet_aton(string_ip) + except socket.error: + return False + return True + + +def is_valid_cidr(string_network): + """ + Very simple check of the cidr format in no_proxy variable. + + :rtype: bool + """ + if string_network.count('/') == 1: + try: + mask = int(string_network.split('/')[1]) + except ValueError: + return False + + if mask < 1 or mask > 32: + return False + + try: + socket.inet_aton(string_network.split('/')[0]) + except socket.error: + return False + else: + return False + return True + + +@contextlib.contextmanager +def set_environ(env_name, value): + """Set the environment variable 'env_name' to 'value' + + Save previous value, yield, and then restore the previous value stored in + the environment variable 'env_name'. + + If 'value' is None, do nothing""" + value_changed = value is not None + if value_changed: + old_value = os.environ.get(env_name) + os.environ[env_name] = value + try: + yield + finally: + if value_changed: + if old_value is None: + del os.environ[env_name] + else: + os.environ[env_name] = old_value + + +def should_bypass_proxies(url, no_proxy): + """ + Returns whether we should bypass proxies or not. + + :rtype: bool + """ + # Prioritize lowercase environment variables over uppercase + # to keep a consistent behaviour with other http projects (curl, wget). + get_proxy = lambda k: os.environ.get(k) or os.environ.get(k.upper()) + + # First check whether no_proxy is defined. If it is, check that the URL + # we're getting isn't in the no_proxy list. + no_proxy_arg = no_proxy + if no_proxy is None: + no_proxy = get_proxy('no_proxy') + parsed = urlparse(url) + + if parsed.hostname is None: + # URLs don't always have hostnames, e.g. file:/// urls. + return True + + if no_proxy: + # We need to check whether we match here. We need to see if we match + # the end of the hostname, both with and without the port. + no_proxy = ( + host for host in no_proxy.replace(' ', '').split(',') if host + ) + + if is_ipv4_address(parsed.hostname): + for proxy_ip in no_proxy: + if is_valid_cidr(proxy_ip): + if address_in_network(parsed.hostname, proxy_ip): + return True + elif parsed.hostname == proxy_ip: + # If no_proxy ip was defined in plain IP notation instead of cidr notation & + # matches the IP of the index + return True + else: + host_with_port = parsed.hostname + if parsed.port: + host_with_port += ':{}'.format(parsed.port) + + for host in no_proxy: + if parsed.hostname.endswith(host) or host_with_port.endswith(host): + # The URL does match something in no_proxy, so we don't want + # to apply the proxies on this URL. + return True + + with set_environ('no_proxy', no_proxy_arg): + # parsed.hostname can be `None` in cases such as a file URI. + try: + bypass = proxy_bypass(parsed.hostname) + except (TypeError, socket.gaierror): + bypass = False + + if bypass: + return True + + return False + + +def get_environ_proxies(url, no_proxy=None): + """ + Return a dict of environment proxies. + + :rtype: dict + """ + if should_bypass_proxies(url, no_proxy=no_proxy): + return {} + else: + return getproxies() + + +def select_proxy(url, proxies): + """Select a proxy for the url, if applicable. + + :param url: The url being for the request + :param proxies: A dictionary of schemes or schemes and hosts to proxy URLs + """ + proxies = proxies or {} + urlparts = urlparse(url) + if urlparts.hostname is None: + return proxies.get(urlparts.scheme, proxies.get('all')) + + proxy_keys = [ + urlparts.scheme + '://' + urlparts.hostname, + urlparts.scheme, + 'all://' + urlparts.hostname, + 'all', + ] + proxy = None + for proxy_key in proxy_keys: + if proxy_key in proxies: + proxy = proxies[proxy_key] + break + + return proxy + + +def default_user_agent(name="python-requests"): + """ + Return a string representing the default user agent. + + :rtype: str + """ + return '%s/%s' % (name, __version__) + + +def default_headers(): + """ + :rtype: requests.structures.CaseInsensitiveDict + """ + return CaseInsensitiveDict({ + 'User-Agent': default_user_agent(), + 'Accept-Encoding': ', '.join(('gzip', 'deflate')), + 'Accept': '*/*', + 'Connection': 'keep-alive', + }) + + +def parse_header_links(value): + """Return a list of parsed link headers proxies. + + i.e. Link: <http:/.../front.jpeg>; rel=front; type="image/jpeg",<http://.../back.jpeg>; rel=back;type="image/jpeg" + + :rtype: list + """ + + links = [] + + replace_chars = ' \'"' + + value = value.strip(replace_chars) + if not value: + return links + + for val in re.split(', *<', value): + try: + url, params = val.split(';', 1) + except ValueError: + url, params = val, '' + + link = {'url': url.strip('<> \'"')} + + for param in params.split(';'): + try: + key, value = param.split('=') + except ValueError: + break + + link[key.strip(replace_chars)] = value.strip(replace_chars) + + links.append(link) + + return links + + +# Null bytes; no need to recreate these on each call to guess_json_utf +_null = '\x00'.encode('ascii') # encoding to ASCII for Python 3 +_null2 = _null * 2 +_null3 = _null * 3 + + +def guess_json_utf(data): + """ + :rtype: str + """ + # JSON always starts with two ASCII characters, so detection is as + # easy as counting the nulls and from their location and count + # determine the encoding. Also detect a BOM, if present. + sample = data[:4] + if sample in (codecs.BOM_UTF32_LE, codecs.BOM_UTF32_BE): + return 'utf-32' # BOM included + if sample[:3] == codecs.BOM_UTF8: + return 'utf-8-sig' # BOM included, MS style (discouraged) + if sample[:2] in (codecs.BOM_UTF16_LE, codecs.BOM_UTF16_BE): + return 'utf-16' # BOM included + nullcount = sample.count(_null) + if nullcount == 0: + return 'utf-8' + if nullcount == 2: + if sample[::2] == _null2: # 1st and 3rd are null + return 'utf-16-be' + if sample[1::2] == _null2: # 2nd and 4th are null + return 'utf-16-le' + # Did not detect 2 valid UTF-16 ascii-range characters + if nullcount == 3: + if sample[:3] == _null3: + return 'utf-32-be' + if sample[1:] == _null3: + return 'utf-32-le' + # Did not detect a valid UTF-32 ascii-range character + return None + + +def prepend_scheme_if_needed(url, new_scheme): + """Given a URL that may or may not have a scheme, prepend the given scheme. + Does not replace a present scheme with the one provided as an argument. + + :rtype: str + """ + scheme, netloc, path, params, query, fragment = urlparse(url, new_scheme) + + # urlparse is a finicky beast, and sometimes decides that there isn't a + # netloc present. Assume that it's being over-cautious, and switch netloc + # and path if urlparse decided there was no netloc. + if not netloc: + netloc, path = path, netloc + + return urlunparse((scheme, netloc, path, params, query, fragment)) + + +def get_auth_from_url(url): + """Given a url with authentication components, extract them into a tuple of + username,password. + + :rtype: (str,str) + """ + parsed = urlparse(url) + + try: + auth = (unquote(parsed.username), unquote(parsed.password)) + except (AttributeError, TypeError): + auth = ('', '') + + return auth + + +# Moved outside of function to avoid recompile every call +_CLEAN_HEADER_REGEX_BYTE = re.compile(b'^\\S[^\\r\\n]*$|^$') +_CLEAN_HEADER_REGEX_STR = re.compile(r'^\S[^\r\n]*$|^$') + + +def check_header_validity(header): + """Verifies that header value is a string which doesn't contain + leading whitespace or return characters. This prevents unintended + header injection. + + :param header: tuple, in the format (name, value). + """ + name, value = header + + if isinstance(value, bytes): + pat = _CLEAN_HEADER_REGEX_BYTE + else: + pat = _CLEAN_HEADER_REGEX_STR + try: + if not pat.match(value): + raise InvalidHeader("Invalid return character or leading space in header: %s" % name) + except TypeError: + raise InvalidHeader("Value for header {%s: %s} must be of type str or " + "bytes, not %s" % (name, value, type(value))) + + +def urldefragauth(url): + """ + Given a url remove the fragment and the authentication part. + + :rtype: str + """ + scheme, netloc, path, params, query, fragment = urlparse(url) + + # see func:`prepend_scheme_if_needed` + if not netloc: + netloc, path = path, netloc + + netloc = netloc.rsplit('@', 1)[-1] + + return urlunparse((scheme, netloc, path, params, query, '')) + + +def rewind_body(prepared_request): + """Move file pointer back to its recorded starting position + so it can be read again on redirect. + """ + body_seek = getattr(prepared_request.body, 'seek', None) + if body_seek is not None and isinstance(prepared_request._body_position, integer_types): + try: + body_seek(prepared_request._body_position) + except (IOError, OSError): + raise UnrewindableBodyError("An error occurred when rewinding request " + "body for redirect.") + else: + raise UnrewindableBodyError("Unable to rewind request body for redirect.") diff --git a/venv2/lib/python2.7/site-packages/pip/_vendor/retrying.py b/venv2/lib/python2.7/site-packages/pip/_vendor/retrying.py new file mode 100644 index 0000000..6d1e627 --- /dev/null +++ b/venv2/lib/python2.7/site-packages/pip/_vendor/retrying.py @@ -0,0 +1,267 @@ +## Copyright 2013-2014 Ray Holder +## +## Licensed under the Apache License, Version 2.0 (the "License"); +## you may not use this file except in compliance with the License. +## You may obtain a copy of the License at +## +## http://www.apache.org/licenses/LICENSE-2.0 +## +## Unless required by applicable law or agreed to in writing, software +## distributed under the License is distributed on an "AS IS" BASIS, +## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +## See the License for the specific language governing permissions and +## limitations under the License. + +import random +from pip._vendor import six +import sys +import time +import traceback + + +# sys.maxint / 2, since Python 3.2 doesn't have a sys.maxint... +MAX_WAIT = 1073741823 + + +def retry(*dargs, **dkw): + """ + Decorator function that instantiates the Retrying object + @param *dargs: positional arguments passed to Retrying object + @param **dkw: keyword arguments passed to the Retrying object + """ + # support both @retry and @retry() as valid syntax + if len(dargs) == 1 and callable(dargs[0]): + def wrap_simple(f): + + @six.wraps(f) + def wrapped_f(*args, **kw): + return Retrying().call(f, *args, **kw) + + return wrapped_f + + return wrap_simple(dargs[0]) + + else: + def wrap(f): + + @six.wraps(f) + def wrapped_f(*args, **kw): + return Retrying(*dargs, **dkw).call(f, *args, **kw) + + return wrapped_f + + return wrap + + +class Retrying(object): + + def __init__(self, + stop=None, wait=None, + stop_max_attempt_number=None, + stop_max_delay=None, + wait_fixed=None, + wait_random_min=None, wait_random_max=None, + wait_incrementing_start=None, wait_incrementing_increment=None, + wait_exponential_multiplier=None, wait_exponential_max=None, + retry_on_exception=None, + retry_on_result=None, + wrap_exception=False, + stop_func=None, + wait_func=None, + wait_jitter_max=None): + + self._stop_max_attempt_number = 5 if stop_max_attempt_number is None else stop_max_attempt_number + self._stop_max_delay = 100 if stop_max_delay is None else stop_max_delay + self._wait_fixed = 1000 if wait_fixed is None else wait_fixed + self._wait_random_min = 0 if wait_random_min is None else wait_random_min + self._wait_random_max = 1000 if wait_random_max is None else wait_random_max + self._wait_incrementing_start = 0 if wait_incrementing_start is None else wait_incrementing_start + self._wait_incrementing_increment = 100 if wait_incrementing_increment is None else wait_incrementing_increment + self._wait_exponential_multiplier = 1 if wait_exponential_multiplier is None else wait_exponential_multiplier + self._wait_exponential_max = MAX_WAIT if wait_exponential_max is None else wait_exponential_max + self._wait_jitter_max = 0 if wait_jitter_max is None else wait_jitter_max + + # TODO add chaining of stop behaviors + # stop behavior + stop_funcs = [] + if stop_max_attempt_number is not None: + stop_funcs.append(self.stop_after_attempt) + + if stop_max_delay is not None: + stop_funcs.append(self.stop_after_delay) + + if stop_func is not None: + self.stop = stop_func + + elif stop is None: + self.stop = lambda attempts, delay: any(f(attempts, delay) for f in stop_funcs) + + else: + self.stop = getattr(self, stop) + + # TODO add chaining of wait behaviors + # wait behavior + wait_funcs = [lambda *args, **kwargs: 0] + if wait_fixed is not None: + wait_funcs.append(self.fixed_sleep) + + if wait_random_min is not None or wait_random_max is not None: + wait_funcs.append(self.random_sleep) + + if wait_incrementing_start is not None or wait_incrementing_increment is not None: + wait_funcs.append(self.incrementing_sleep) + + if wait_exponential_multiplier is not None or wait_exponential_max is not None: + wait_funcs.append(self.exponential_sleep) + + if wait_func is not None: + self.wait = wait_func + + elif wait is None: + self.wait = lambda attempts, delay: max(f(attempts, delay) for f in wait_funcs) + + else: + self.wait = getattr(self, wait) + + # retry on exception filter + if retry_on_exception is None: + self._retry_on_exception = self.always_reject + else: + self._retry_on_exception = retry_on_exception + + # TODO simplify retrying by Exception types + # retry on result filter + if retry_on_result is None: + self._retry_on_result = self.never_reject + else: + self._retry_on_result = retry_on_result + + self._wrap_exception = wrap_exception + + def stop_after_attempt(self, previous_attempt_number, delay_since_first_attempt_ms): + """Stop after the previous attempt >= stop_max_attempt_number.""" + return previous_attempt_number >= self._stop_max_attempt_number + + def stop_after_delay(self, previous_attempt_number, delay_since_first_attempt_ms): + """Stop after the time from the first attempt >= stop_max_delay.""" + return delay_since_first_attempt_ms >= self._stop_max_delay + + def no_sleep(self, previous_attempt_number, delay_since_first_attempt_ms): + """Don't sleep at all before retrying.""" + return 0 + + def fixed_sleep(self, previous_attempt_number, delay_since_first_attempt_ms): + """Sleep a fixed amount of time between each retry.""" + return self._wait_fixed + + def random_sleep(self, previous_attempt_number, delay_since_first_attempt_ms): + """Sleep a random amount of time between wait_random_min and wait_random_max""" + return random.randint(self._wait_random_min, self._wait_random_max) + + def incrementing_sleep(self, previous_attempt_number, delay_since_first_attempt_ms): + """ + Sleep an incremental amount of time after each attempt, starting at + wait_incrementing_start and incrementing by wait_incrementing_increment + """ + result = self._wait_incrementing_start + (self._wait_incrementing_increment * (previous_attempt_number - 1)) + if result < 0: + result = 0 + return result + + def exponential_sleep(self, previous_attempt_number, delay_since_first_attempt_ms): + exp = 2 ** previous_attempt_number + result = self._wait_exponential_multiplier * exp + if result > self._wait_exponential_max: + result = self._wait_exponential_max + if result < 0: + result = 0 + return result + + def never_reject(self, result): + return False + + def always_reject(self, result): + return True + + def should_reject(self, attempt): + reject = False + if attempt.has_exception: + reject |= self._retry_on_exception(attempt.value[1]) + else: + reject |= self._retry_on_result(attempt.value) + + return reject + + def call(self, fn, *args, **kwargs): + start_time = int(round(time.time() * 1000)) + attempt_number = 1 + while True: + try: + attempt = Attempt(fn(*args, **kwargs), attempt_number, False) + except: + tb = sys.exc_info() + attempt = Attempt(tb, attempt_number, True) + + if not self.should_reject(attempt): + return attempt.get(self._wrap_exception) + + delay_since_first_attempt_ms = int(round(time.time() * 1000)) - start_time + if self.stop(attempt_number, delay_since_first_attempt_ms): + if not self._wrap_exception and attempt.has_exception: + # get() on an attempt with an exception should cause it to be raised, but raise just in case + raise attempt.get() + else: + raise RetryError(attempt) + else: + sleep = self.wait(attempt_number, delay_since_first_attempt_ms) + if self._wait_jitter_max: + jitter = random.random() * self._wait_jitter_max + sleep = sleep + max(0, jitter) + time.sleep(sleep / 1000.0) + + attempt_number += 1 + + +class Attempt(object): + """ + An Attempt encapsulates a call to a target function that may end as a + normal return value from the function or an Exception depending on what + occurred during the execution. + """ + + def __init__(self, value, attempt_number, has_exception): + self.value = value + self.attempt_number = attempt_number + self.has_exception = has_exception + + def get(self, wrap_exception=False): + """ + Return the return value of this Attempt instance or raise an Exception. + If wrap_exception is true, this Attempt is wrapped inside of a + RetryError before being raised. + """ + if self.has_exception: + if wrap_exception: + raise RetryError(self) + else: + six.reraise(self.value[0], self.value[1], self.value[2]) + else: + return self.value + + def __repr__(self): + if self.has_exception: + return "Attempts: {0}, Error:\n{1}".format(self.attempt_number, "".join(traceback.format_tb(self.value[2]))) + else: + return "Attempts: {0}, Value: {1}".format(self.attempt_number, self.value) + + +class RetryError(Exception): + """ + A RetryError encapsulates the last Attempt instance right before giving up. + """ + + def __init__(self, last_attempt): + self.last_attempt = last_attempt + + def __str__(self): + return "RetryError[{0}]".format(self.last_attempt) diff --git a/venv2/lib/python2.7/site-packages/pip/_vendor/six.py b/venv2/lib/python2.7/site-packages/pip/_vendor/six.py new file mode 100644 index 0000000..89b2188 --- /dev/null +++ b/venv2/lib/python2.7/site-packages/pip/_vendor/six.py @@ -0,0 +1,952 @@ +# Copyright (c) 2010-2018 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. + +"""Utilities for writing code that runs on Python 2 and 3""" + +from __future__ import absolute_import + +import functools +import itertools +import operator +import sys +import types + +__author__ = "Benjamin Peterson <benjamin@python.org>" +__version__ = "1.12.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("getoutput", "commands", "subprocess"), + 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_base", "email.MIMEBase", "email.mime.base"), + MovedModule("email_mime_image", "email.MIMEImage", "email.mime.image"), + 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("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("unquote_to_bytes", "urllib", "urllib.parse", "unquote", "unquote_to_bytes"), + MovedAttribute("urlencode", "urllib", "urllib.parse"), + MovedAttribute("splitquery", "urllib", "urllib.parse"), + MovedAttribute("splittag", "urllib", "urllib.parse"), + MovedAttribute("splituser", "urllib", "urllib.parse"), + MovedAttribute("splitvalue", "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"), + MovedAttribute("parse_http_list", "urllib2", "urllib.request"), + MovedAttribute("parse_keqv_list", "urllib2", "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): + try: + if value is None: + value = tp() + if value.__traceback__ is not tb: + raise value.with_traceback(tb) + raise value + finally: + value = None + tb = None + +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): + try: + raise tp, value, tb + finally: + tb = None +""") + + +if sys.version_info[:2] == (3, 2): + exec_("""def raise_from(value, from_value): + try: + if from_value is None: + raise value + raise value from from_value + finally: + value = None +""") +elif sys.version_info[:2] > (3, 2): + exec_("""def raise_from(value, from_value): + try: + raise value from from_value + finally: + value = None +""") +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(type): + + def __new__(cls, name, this_bases, d): + return meta(name, bases, d) + + @classmethod + def __prepare__(cls, name, this_bases): + return meta.__prepare__(name, bases) + 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) + if hasattr(cls, '__qualname__'): + orig_vars['__qualname__'] = cls.__qualname__ + return metaclass(cls.__name__, cls.__bases__, orig_vars) + return wrapper + + +def ensure_binary(s, encoding='utf-8', errors='strict'): + """Coerce **s** to six.binary_type. + + For Python 2: + - `unicode` -> encoded to `str` + - `str` -> `str` + + For Python 3: + - `str` -> encoded to `bytes` + - `bytes` -> `bytes` + """ + if isinstance(s, text_type): + return s.encode(encoding, errors) + elif isinstance(s, binary_type): + return s + else: + raise TypeError("not expecting type '%s'" % type(s)) + + +def ensure_str(s, encoding='utf-8', errors='strict'): + """Coerce *s* to `str`. + + For Python 2: + - `unicode` -> encoded to `str` + - `str` -> `str` + + For Python 3: + - `str` -> `str` + - `bytes` -> decoded to `str` + """ + if not isinstance(s, (text_type, binary_type)): + raise TypeError("not expecting type '%s'" % type(s)) + if PY2 and isinstance(s, text_type): + s = s.encode(encoding, errors) + elif PY3 and isinstance(s, binary_type): + s = s.decode(encoding, errors) + return s + + +def ensure_text(s, encoding='utf-8', errors='strict'): + """Coerce *s* to six.text_type. + + For Python 2: + - `unicode` -> `unicode` + - `str` -> `unicode` + + For Python 3: + - `str` -> `str` + - `bytes` -> decoded to `str` + """ + if isinstance(s, binary_type): + return s.decode(encoding, errors) + elif isinstance(s, text_type): + return s + else: + raise TypeError("not expecting type '%s'" % type(s)) + + + +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/venv2/lib/python2.7/site-packages/pip/_vendor/urllib3/__init__.py b/venv2/lib/python2.7/site-packages/pip/_vendor/urllib3/__init__.py new file mode 100644 index 0000000..148a9c3 --- /dev/null +++ b/venv2/lib/python2.7/site-packages/pip/_vendor/urllib3/__init__.py @@ -0,0 +1,92 @@ +""" +urllib3 - Thread-safe connection pooling and re-using. +""" + +from __future__ import absolute_import +import warnings + +from .connectionpool import ( + HTTPConnectionPool, + HTTPSConnectionPool, + connection_from_url +) + +from . import exceptions +from .filepost import encode_multipart_formdata +from .poolmanager import PoolManager, ProxyManager, proxy_from_url +from .response import HTTPResponse +from .util.request import make_headers +from .util.url import get_host +from .util.timeout import Timeout +from .util.retry import Retry + + +# Set default logging handler to avoid "No handler found" warnings. +import logging +from logging import NullHandler + +__author__ = 'Andrey Petrov (andrey.petrov@shazow.net)' +__license__ = 'MIT' +__version__ = '1.24.1' + +__all__ = ( + 'HTTPConnectionPool', + 'HTTPSConnectionPool', + 'PoolManager', + 'ProxyManager', + 'HTTPResponse', + 'Retry', + 'Timeout', + 'add_stderr_logger', + 'connection_from_url', + 'disable_warnings', + 'encode_multipart_formdata', + 'get_host', + 'make_headers', + 'proxy_from_url', +) + +logging.getLogger(__name__).addHandler(NullHandler()) + + +def add_stderr_logger(level=logging.DEBUG): + """ + Helper for quickly adding a StreamHandler to the logger. Useful for + debugging. + + Returns the handler after adding it. + """ + # This method needs to be in this __init__.py to get the __name__ correct + # even if urllib3 is vendored within another package. + logger = logging.getLogger(__name__) + handler = logging.StreamHandler() + handler.setFormatter(logging.Formatter('%(asctime)s %(levelname)s %(message)s')) + logger.addHandler(handler) + logger.setLevel(level) + logger.debug('Added a stderr logging handler to logger: %s', __name__) + return handler + + +# ... Clean up. +del NullHandler + + +# All warning filters *must* be appended unless you're really certain that they +# shouldn't be: otherwise, it's very hard for users to use most Python +# mechanisms to silence them. +# SecurityWarning's always go off by default. +warnings.simplefilter('always', exceptions.SecurityWarning, append=True) +# SubjectAltNameWarning's should go off once per host +warnings.simplefilter('default', exceptions.SubjectAltNameWarning, append=True) +# InsecurePlatformWarning's don't vary between requests, so we keep it default. +warnings.simplefilter('default', exceptions.InsecurePlatformWarning, + append=True) +# SNIMissingWarnings should go off only once. +warnings.simplefilter('default', exceptions.SNIMissingWarning, append=True) + + +def disable_warnings(category=exceptions.HTTPWarning): + """ + Helper for quickly disabling all urllib3 warnings. + """ + warnings.simplefilter('ignore', category) diff --git a/venv2/lib/python2.7/site-packages/pip/_vendor/urllib3/_collections.py b/venv2/lib/python2.7/site-packages/pip/_vendor/urllib3/_collections.py new file mode 100644 index 0000000..34f2381 --- /dev/null +++ b/venv2/lib/python2.7/site-packages/pip/_vendor/urllib3/_collections.py @@ -0,0 +1,329 @@ +from __future__ import absolute_import +try: + from collections.abc import Mapping, MutableMapping +except ImportError: + from collections import Mapping, MutableMapping +try: + from threading import RLock +except ImportError: # Platform-specific: No threads available + class RLock: + def __enter__(self): + pass + + def __exit__(self, exc_type, exc_value, traceback): + pass + + +from collections import OrderedDict +from .exceptions import InvalidHeader +from .packages.six import iterkeys, itervalues, PY3 + + +__all__ = ['RecentlyUsedContainer', 'HTTPHeaderDict'] + + +_Null = object() + + +class RecentlyUsedContainer(MutableMapping): + """ + Provides a thread-safe dict-like container which maintains up to + ``maxsize`` keys while throwing away the least-recently-used keys beyond + ``maxsize``. + + :param maxsize: + Maximum number of recent elements to retain. + + :param dispose_func: + Every time an item is evicted from the container, + ``dispose_func(value)`` is called. Callback which will get called + """ + + ContainerCls = OrderedDict + + def __init__(self, maxsize=10, dispose_func=None): + self._maxsize = maxsize + self.dispose_func = dispose_func + + self._container = self.ContainerCls() + self.lock = RLock() + + def __getitem__(self, key): + # Re-insert the item, moving it to the end of the eviction line. + with self.lock: + item = self._container.pop(key) + self._container[key] = item + return item + + def __setitem__(self, key, value): + evicted_value = _Null + with self.lock: + # Possibly evict the existing value of 'key' + evicted_value = self._container.get(key, _Null) + self._container[key] = value + + # If we didn't evict an existing value, we might have to evict the + # least recently used item from the beginning of the container. + if len(self._container) > self._maxsize: + _key, evicted_value = self._container.popitem(last=False) + + if self.dispose_func and evicted_value is not _Null: + self.dispose_func(evicted_value) + + def __delitem__(self, key): + with self.lock: + value = self._container.pop(key) + + if self.dispose_func: + self.dispose_func(value) + + def __len__(self): + with self.lock: + return len(self._container) + + def __iter__(self): + raise NotImplementedError('Iteration over this class is unlikely to be threadsafe.') + + def clear(self): + with self.lock: + # Copy pointers to all values, then wipe the mapping + values = list(itervalues(self._container)) + self._container.clear() + + if self.dispose_func: + for value in values: + self.dispose_func(value) + + def keys(self): + with self.lock: + return list(iterkeys(self._container)) + + +class HTTPHeaderDict(MutableMapping): + """ + :param headers: + An iterable of field-value pairs. Must not contain multiple field names + when compared case-insensitively. + + :param kwargs: + Additional field-value pairs to pass in to ``dict.update``. + + A ``dict`` like container for storing HTTP Headers. + + Field names are stored and compared case-insensitively in compliance with + RFC 7230. Iteration provides the first case-sensitive key seen for each + case-insensitive pair. + + Using ``__setitem__`` syntax overwrites fields that compare equal + case-insensitively in order to maintain ``dict``'s api. For fields that + compare equal, instead create a new ``HTTPHeaderDict`` and use ``.add`` + in a loop. + + If multiple fields that are equal case-insensitively are passed to the + constructor or ``.update``, the behavior is undefined and some will be + lost. + + >>> headers = HTTPHeaderDict() + >>> headers.add('Set-Cookie', 'foo=bar') + >>> headers.add('set-cookie', 'baz=quxx') + >>> headers['content-length'] = '7' + >>> headers['SET-cookie'] + 'foo=bar, baz=quxx' + >>> headers['Content-Length'] + '7' + """ + + def __init__(self, headers=None, **kwargs): + super(HTTPHeaderDict, self).__init__() + self._container = OrderedDict() + if headers is not None: + if isinstance(headers, HTTPHeaderDict): + self._copy_from(headers) + else: + self.extend(headers) + if kwargs: + self.extend(kwargs) + + def __setitem__(self, key, val): + self._container[key.lower()] = [key, val] + return self._container[key.lower()] + + def __getitem__(self, key): + val = self._container[key.lower()] + return ', '.join(val[1:]) + + def __delitem__(self, key): + del self._container[key.lower()] + + def __contains__(self, key): + return key.lower() in self._container + + def __eq__(self, other): + if not isinstance(other, Mapping) and not hasattr(other, 'keys'): + return False + if not isinstance(other, type(self)): + other = type(self)(other) + return (dict((k.lower(), v) for k, v in self.itermerged()) == + dict((k.lower(), v) for k, v in other.itermerged())) + + def __ne__(self, other): + return not self.__eq__(other) + + if not PY3: # Python 2 + iterkeys = MutableMapping.iterkeys + itervalues = MutableMapping.itervalues + + __marker = object() + + def __len__(self): + return len(self._container) + + def __iter__(self): + # Only provide the originally cased names + for vals in self._container.values(): + yield vals[0] + + def pop(self, key, default=__marker): + '''D.pop(k[,d]) -> v, remove specified key and return the corresponding value. + If key is not found, d is returned if given, otherwise KeyError is raised. + ''' + # Using the MutableMapping function directly fails due to the private marker. + # Using ordinary dict.pop would expose the internal structures. + # So let's reinvent the wheel. + try: + value = self[key] + except KeyError: + if default is self.__marker: + raise + return default + else: + del self[key] + return value + + def discard(self, key): + try: + del self[key] + except KeyError: + pass + + def add(self, key, val): + """Adds a (name, value) pair, doesn't overwrite the value if it already + exists. + + >>> headers = HTTPHeaderDict(foo='bar') + >>> headers.add('Foo', 'baz') + >>> headers['foo'] + 'bar, baz' + """ + key_lower = key.lower() + new_vals = [key, val] + # Keep the common case aka no item present as fast as possible + vals = self._container.setdefault(key_lower, new_vals) + if new_vals is not vals: + vals.append(val) + + def extend(self, *args, **kwargs): + """Generic import function for any type of header-like object. + Adapted version of MutableMapping.update in order to insert items + with self.add instead of self.__setitem__ + """ + if len(args) > 1: + raise TypeError("extend() takes at most 1 positional " + "arguments ({0} given)".format(len(args))) + other = args[0] if len(args) >= 1 else () + + if isinstance(other, HTTPHeaderDict): + for key, val in other.iteritems(): + self.add(key, val) + elif isinstance(other, Mapping): + for key in other: + self.add(key, other[key]) + elif hasattr(other, "keys"): + for key in other.keys(): + self.add(key, other[key]) + else: + for key, value in other: + self.add(key, value) + + for key, value in kwargs.items(): + self.add(key, value) + + def getlist(self, key, default=__marker): + """Returns a list of all the values for the named field. Returns an + empty list if the key doesn't exist.""" + try: + vals = self._container[key.lower()] + except KeyError: + if default is self.__marker: + return [] + return default + else: + return vals[1:] + + # Backwards compatibility for httplib + getheaders = getlist + getallmatchingheaders = getlist + iget = getlist + + # Backwards compatibility for http.cookiejar + get_all = getlist + + def __repr__(self): + return "%s(%s)" % (type(self).__name__, dict(self.itermerged())) + + def _copy_from(self, other): + for key in other: + val = other.getlist(key) + if isinstance(val, list): + # Don't need to convert tuples + val = list(val) + self._container[key.lower()] = [key] + val + + def copy(self): + clone = type(self)() + clone._copy_from(self) + return clone + + def iteritems(self): + """Iterate over all header lines, including duplicate ones.""" + for key in self: + vals = self._container[key.lower()] + for val in vals[1:]: + yield vals[0], val + + def itermerged(self): + """Iterate over all headers, merging duplicate ones together.""" + for key in self: + val = self._container[key.lower()] + yield val[0], ', '.join(val[1:]) + + def items(self): + return list(self.iteritems()) + + @classmethod + def from_httplib(cls, message): # Python 2 + """Read headers from a Python 2 httplib message object.""" + # python2.7 does not expose a proper API for exporting multiheaders + # efficiently. This function re-reads raw lines from the message + # object and extracts the multiheaders properly. + obs_fold_continued_leaders = (' ', '\t') + headers = [] + + for line in message.headers: + if line.startswith(obs_fold_continued_leaders): + if not headers: + # We received a header line that starts with OWS as described + # in RFC-7230 S3.2.4. This indicates a multiline header, but + # there exists no previous header to which we can attach it. + raise InvalidHeader( + 'Header continuation with no previous header: %s' % line + ) + else: + key, value = headers[-1] + headers[-1] = (key, value + ' ' + line.strip()) + continue + + key, value = line.split(':', 1) + headers.append((key, value.strip())) + + return cls(headers) diff --git a/venv2/lib/python2.7/site-packages/pip/_vendor/urllib3/connection.py b/venv2/lib/python2.7/site-packages/pip/_vendor/urllib3/connection.py new file mode 100644 index 0000000..02b3665 --- /dev/null +++ b/venv2/lib/python2.7/site-packages/pip/_vendor/urllib3/connection.py @@ -0,0 +1,391 @@ +from __future__ import absolute_import +import datetime +import logging +import os +import socket +from socket import error as SocketError, timeout as SocketTimeout +import warnings +from .packages import six +from .packages.six.moves.http_client import HTTPConnection as _HTTPConnection +from .packages.six.moves.http_client import HTTPException # noqa: F401 + +try: # Compiled with SSL? + import ssl + BaseSSLError = ssl.SSLError +except (ImportError, AttributeError): # Platform-specific: No SSL. + ssl = None + + class BaseSSLError(BaseException): + pass + + +try: # Python 3: + # Not a no-op, we're adding this to the namespace so it can be imported. + ConnectionError = ConnectionError +except NameError: # Python 2: + class ConnectionError(Exception): + pass + + +from .exceptions import ( + NewConnectionError, + ConnectTimeoutError, + SubjectAltNameWarning, + SystemTimeWarning, +) +from .packages.ssl_match_hostname import match_hostname, CertificateError + +from .util.ssl_ import ( + resolve_cert_reqs, + resolve_ssl_version, + assert_fingerprint, + create_urllib3_context, + ssl_wrap_socket +) + + +from .util import connection + +from ._collections import HTTPHeaderDict + +log = logging.getLogger(__name__) + +port_by_scheme = { + 'http': 80, + 'https': 443, +} + +# When updating RECENT_DATE, move it to within two years of the current date, +# and not less than 6 months ago. +# Example: if Today is 2018-01-01, then RECENT_DATE should be any date on or +# after 2016-01-01 (today - 2 years) AND before 2017-07-01 (today - 6 months) +RECENT_DATE = datetime.date(2017, 6, 30) + + +class DummyConnection(object): + """Used to detect a failed ConnectionCls import.""" + pass + + +class HTTPConnection(_HTTPConnection, object): + """ + Based on httplib.HTTPConnection but provides an extra constructor + backwards-compatibility layer between older and newer Pythons. + + Additional keyword parameters are used to configure attributes of the connection. + Accepted parameters include: + + - ``strict``: See the documentation on :class:`urllib3.connectionpool.HTTPConnectionPool` + - ``source_address``: Set the source address for the current connection. + - ``socket_options``: Set specific options on the underlying socket. If not specified, then + defaults are loaded from ``HTTPConnection.default_socket_options`` which includes disabling + Nagle's algorithm (sets TCP_NODELAY to 1) unless the connection is behind a proxy. + + For example, if you wish to enable TCP Keep Alive in addition to the defaults, + you might pass:: + + HTTPConnection.default_socket_options + [ + (socket.SOL_SOCKET, socket.SO_KEEPALIVE, 1), + ] + + Or you may want to disable the defaults by passing an empty list (e.g., ``[]``). + """ + + default_port = port_by_scheme['http'] + + #: Disable Nagle's algorithm by default. + #: ``[(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1)]`` + default_socket_options = [(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1)] + + #: Whether this connection verifies the host's certificate. + is_verified = False + + def __init__(self, *args, **kw): + if six.PY3: # Python 3 + kw.pop('strict', None) + + # Pre-set source_address. + self.source_address = kw.get('source_address') + + #: The socket options provided by the user. If no options are + #: provided, we use the default options. + self.socket_options = kw.pop('socket_options', self.default_socket_options) + + _HTTPConnection.__init__(self, *args, **kw) + + @property + def host(self): + """ + Getter method to remove any trailing dots that indicate the hostname is an FQDN. + + In general, SSL certificates don't include the trailing dot indicating a + fully-qualified domain name, and thus, they don't validate properly when + checked against a domain name that includes the dot. In addition, some + servers may not expect to receive the trailing dot when provided. + + However, the hostname with trailing dot is critical to DNS resolution; doing a + lookup with the trailing dot will properly only resolve the appropriate FQDN, + whereas a lookup without a trailing dot will search the system's search domain + list. Thus, it's important to keep the original host around for use only in + those cases where it's appropriate (i.e., when doing DNS lookup to establish the + actual TCP connection across which we're going to send HTTP requests). + """ + return self._dns_host.rstrip('.') + + @host.setter + def host(self, value): + """ + Setter for the `host` property. + + We assume that only urllib3 uses the _dns_host attribute; httplib itself + only uses `host`, and it seems reasonable that other libraries follow suit. + """ + self._dns_host = value + + def _new_conn(self): + """ Establish a socket connection and set nodelay settings on it. + + :return: New socket connection. + """ + extra_kw = {} + if self.source_address: + extra_kw['source_address'] = self.source_address + + if self.socket_options: + extra_kw['socket_options'] = self.socket_options + + try: + conn = connection.create_connection( + (self._dns_host, self.port), self.timeout, **extra_kw) + + except SocketTimeout as e: + raise ConnectTimeoutError( + self, "Connection to %s timed out. (connect timeout=%s)" % + (self.host, self.timeout)) + + except SocketError as e: + raise NewConnectionError( + self, "Failed to establish a new connection: %s" % e) + + return conn + + def _prepare_conn(self, conn): + self.sock = conn + if self._tunnel_host: + # TODO: Fix tunnel so it doesn't depend on self.sock state. + self._tunnel() + # Mark this connection as not reusable + self.auto_open = 0 + + def connect(self): + conn = self._new_conn() + self._prepare_conn(conn) + + def request_chunked(self, method, url, body=None, headers=None): + """ + Alternative to the common request method, which sends the + body with chunked encoding and not as one block + """ + headers = HTTPHeaderDict(headers if headers is not None else {}) + skip_accept_encoding = 'accept-encoding' in headers + skip_host = 'host' in headers + self.putrequest( + method, + url, + skip_accept_encoding=skip_accept_encoding, + skip_host=skip_host + ) + for header, value in headers.items(): + self.putheader(header, value) + if 'transfer-encoding' not in headers: + self.putheader('Transfer-Encoding', 'chunked') + self.endheaders() + + if body is not None: + stringish_types = six.string_types + (bytes,) + if isinstance(body, stringish_types): + body = (body,) + for chunk in body: + if not chunk: + continue + if not isinstance(chunk, bytes): + chunk = chunk.encode('utf8') + len_str = hex(len(chunk))[2:] + self.send(len_str.encode('utf-8')) + self.send(b'\r\n') + self.send(chunk) + self.send(b'\r\n') + + # After the if clause, to always have a closed body + self.send(b'0\r\n\r\n') + + +class HTTPSConnection(HTTPConnection): + default_port = port_by_scheme['https'] + + ssl_version = None + + def __init__(self, host, port=None, key_file=None, cert_file=None, + strict=None, timeout=socket._GLOBAL_DEFAULT_TIMEOUT, + ssl_context=None, server_hostname=None, **kw): + + HTTPConnection.__init__(self, host, port, strict=strict, + timeout=timeout, **kw) + + self.key_file = key_file + self.cert_file = cert_file + self.ssl_context = ssl_context + self.server_hostname = server_hostname + + # Required property for Google AppEngine 1.9.0 which otherwise causes + # HTTPS requests to go out as HTTP. (See Issue #356) + self._protocol = 'https' + + def connect(self): + conn = self._new_conn() + self._prepare_conn(conn) + + if self.ssl_context is None: + self.ssl_context = create_urllib3_context( + ssl_version=resolve_ssl_version(None), + cert_reqs=resolve_cert_reqs(None), + ) + + self.sock = ssl_wrap_socket( + sock=conn, + keyfile=self.key_file, + certfile=self.cert_file, + ssl_context=self.ssl_context, + server_hostname=self.server_hostname + ) + + +class VerifiedHTTPSConnection(HTTPSConnection): + """ + Based on httplib.HTTPSConnection but wraps the socket with + SSL certification. + """ + cert_reqs = None + ca_certs = None + ca_cert_dir = None + ssl_version = None + assert_fingerprint = None + + def set_cert(self, key_file=None, cert_file=None, + cert_reqs=None, ca_certs=None, + assert_hostname=None, assert_fingerprint=None, + ca_cert_dir=None): + """ + This method should only be called once, before the connection is used. + """ + # If cert_reqs is not provided, we can try to guess. If the user gave + # us a cert database, we assume they want to use it: otherwise, if + # they gave us an SSL Context object we should use whatever is set for + # it. + if cert_reqs is None: + if ca_certs or ca_cert_dir: + cert_reqs = 'CERT_REQUIRED' + elif self.ssl_context is not None: + cert_reqs = self.ssl_context.verify_mode + + self.key_file = key_file + self.cert_file = cert_file + self.cert_reqs = cert_reqs + self.assert_hostname = assert_hostname + self.assert_fingerprint = assert_fingerprint + self.ca_certs = ca_certs and os.path.expanduser(ca_certs) + self.ca_cert_dir = ca_cert_dir and os.path.expanduser(ca_cert_dir) + + def connect(self): + # Add certificate verification + conn = self._new_conn() + hostname = self.host + + if self._tunnel_host: + self.sock = conn + # Calls self._set_hostport(), so self.host is + # self._tunnel_host below. + self._tunnel() + # Mark this connection as not reusable + self.auto_open = 0 + + # Override the host with the one we're requesting data from. + hostname = self._tunnel_host + + server_hostname = hostname + if self.server_hostname is not None: + server_hostname = self.server_hostname + + is_time_off = datetime.date.today() < RECENT_DATE + if is_time_off: + warnings.warn(( + 'System time is way off (before {0}). This will probably ' + 'lead to SSL verification errors').format(RECENT_DATE), + SystemTimeWarning + ) + + # Wrap socket using verification with the root certs in + # trusted_root_certs + if self.ssl_context is None: + self.ssl_context = create_urllib3_context( + ssl_version=resolve_ssl_version(self.ssl_version), + cert_reqs=resolve_cert_reqs(self.cert_reqs), + ) + + context = self.ssl_context + context.verify_mode = resolve_cert_reqs(self.cert_reqs) + self.sock = ssl_wrap_socket( + sock=conn, + keyfile=self.key_file, + certfile=self.cert_file, + ca_certs=self.ca_certs, + ca_cert_dir=self.ca_cert_dir, + server_hostname=server_hostname, + ssl_context=context) + + if self.assert_fingerprint: + assert_fingerprint(self.sock.getpeercert(binary_form=True), + self.assert_fingerprint) + elif context.verify_mode != ssl.CERT_NONE \ + and not getattr(context, 'check_hostname', False) \ + and self.assert_hostname is not False: + # While urllib3 attempts to always turn off hostname matching from + # the TLS library, this cannot always be done. So we check whether + # the TLS Library still thinks it's matching hostnames. + cert = self.sock.getpeercert() + if not cert.get('subjectAltName', ()): + warnings.warn(( + 'Certificate for {0} has no `subjectAltName`, falling back to check for a ' + '`commonName` for now. This feature is being removed by major browsers and ' + 'deprecated by RFC 2818. (See https://github.com/shazow/urllib3/issues/497 ' + 'for details.)'.format(hostname)), + SubjectAltNameWarning + ) + _match_hostname(cert, self.assert_hostname or server_hostname) + + self.is_verified = ( + context.verify_mode == ssl.CERT_REQUIRED or + self.assert_fingerprint is not None + ) + + +def _match_hostname(cert, asserted_hostname): + try: + match_hostname(cert, asserted_hostname) + except CertificateError as e: + log.error( + 'Certificate did not match expected hostname: %s. ' + 'Certificate: %s', asserted_hostname, cert + ) + # Add cert to exception and reraise so client code can inspect + # the cert when catching the exception, if they want to + e._peer_cert = cert + raise + + +if ssl: + # Make a copy for testing. + UnverifiedHTTPSConnection = HTTPSConnection + HTTPSConnection = VerifiedHTTPSConnection +else: + HTTPSConnection = DummyConnection diff --git a/venv2/lib/python2.7/site-packages/pip/_vendor/urllib3/connectionpool.py b/venv2/lib/python2.7/site-packages/pip/_vendor/urllib3/connectionpool.py new file mode 100644 index 0000000..f7a8f19 --- /dev/null +++ b/venv2/lib/python2.7/site-packages/pip/_vendor/urllib3/connectionpool.py @@ -0,0 +1,896 @@ +from __future__ import absolute_import +import errno +import logging +import sys +import warnings + +from socket import error as SocketError, timeout as SocketTimeout +import socket + + +from .exceptions import ( + ClosedPoolError, + ProtocolError, + EmptyPoolError, + HeaderParsingError, + HostChangedError, + LocationValueError, + MaxRetryError, + ProxyError, + ReadTimeoutError, + SSLError, + TimeoutError, + InsecureRequestWarning, + NewConnectionError, +) +from .packages.ssl_match_hostname import CertificateError +from .packages import six +from .packages.six.moves import queue +from .connection import ( + port_by_scheme, + DummyConnection, + HTTPConnection, HTTPSConnection, VerifiedHTTPSConnection, + HTTPException, BaseSSLError, +) +from .request import RequestMethods +from .response import HTTPResponse + +from .util.connection import is_connection_dropped +from .util.request import set_file_position +from .util.response import assert_header_parsing +from .util.retry import Retry +from .util.timeout import Timeout +from .util.url import get_host, Url, NORMALIZABLE_SCHEMES +from .util.queue import LifoQueue + + +xrange = six.moves.xrange + +log = logging.getLogger(__name__) + +_Default = object() + + +# Pool objects +class ConnectionPool(object): + """ + Base class for all connection pools, such as + :class:`.HTTPConnectionPool` and :class:`.HTTPSConnectionPool`. + """ + + scheme = None + QueueCls = LifoQueue + + def __init__(self, host, port=None): + if not host: + raise LocationValueError("No host specified.") + + self.host = _ipv6_host(host, self.scheme) + self._proxy_host = host.lower() + self.port = port + + def __str__(self): + return '%s(host=%r, port=%r)' % (type(self).__name__, + self.host, self.port) + + def __enter__(self): + return self + + def __exit__(self, exc_type, exc_val, exc_tb): + self.close() + # Return False to re-raise any potential exceptions + return False + + def close(self): + """ + Close all pooled connections and disable the pool. + """ + pass + + +# This is taken from http://hg.python.org/cpython/file/7aaba721ebc0/Lib/socket.py#l252 +_blocking_errnos = {errno.EAGAIN, errno.EWOULDBLOCK} + + +class HTTPConnectionPool(ConnectionPool, RequestMethods): + """ + Thread-safe connection pool for one host. + + :param host: + Host used for this HTTP Connection (e.g. "localhost"), passed into + :class:`httplib.HTTPConnection`. + + :param port: + Port used for this HTTP Connection (None is equivalent to 80), passed + into :class:`httplib.HTTPConnection`. + + :param strict: + Causes BadStatusLine to be raised if the status line can't be parsed + as a valid HTTP/1.0 or 1.1 status line, passed into + :class:`httplib.HTTPConnection`. + + .. note:: + Only works in Python 2. This parameter is ignored in Python 3. + + :param timeout: + Socket timeout in seconds for each individual connection. This can + be a float or integer, which sets the timeout for the HTTP request, + or an instance of :class:`urllib3.util.Timeout` which gives you more + fine-grained control over request timeouts. After the constructor has + been parsed, this is always a `urllib3.util.Timeout` object. + + :param maxsize: + Number of connections to save that can be reused. More than 1 is useful + in multithreaded situations. If ``block`` is set to False, more + connections will be created but they will not be saved once they've + been used. + + :param block: + If set to True, no more than ``maxsize`` connections will be used at + a time. When no free connections are available, the call will block + until a connection has been released. This is a useful side effect for + particular multithreaded situations where one does not want to use more + than maxsize connections per host to prevent flooding. + + :param headers: + Headers to include with all requests, unless other headers are given + explicitly. + + :param retries: + Retry configuration to use by default with requests in this pool. + + :param _proxy: + Parsed proxy URL, should not be used directly, instead, see + :class:`urllib3.connectionpool.ProxyManager`" + + :param _proxy_headers: + A dictionary with proxy headers, should not be used directly, + instead, see :class:`urllib3.connectionpool.ProxyManager`" + + :param \\**conn_kw: + Additional parameters are used to create fresh :class:`urllib3.connection.HTTPConnection`, + :class:`urllib3.connection.HTTPSConnection` instances. + """ + + scheme = 'http' + ConnectionCls = HTTPConnection + ResponseCls = HTTPResponse + + def __init__(self, host, port=None, strict=False, + timeout=Timeout.DEFAULT_TIMEOUT, maxsize=1, block=False, + headers=None, retries=None, + _proxy=None, _proxy_headers=None, + **conn_kw): + ConnectionPool.__init__(self, host, port) + RequestMethods.__init__(self, headers) + + self.strict = strict + + if not isinstance(timeout, Timeout): + timeout = Timeout.from_float(timeout) + + if retries is None: + retries = Retry.DEFAULT + + self.timeout = timeout + self.retries = retries + + self.pool = self.QueueCls(maxsize) + self.block = block + + self.proxy = _proxy + self.proxy_headers = _proxy_headers or {} + + # Fill the queue up so that doing get() on it will block properly + for _ in xrange(maxsize): + self.pool.put(None) + + # These are mostly for testing and debugging purposes. + self.num_connections = 0 + self.num_requests = 0 + self.conn_kw = conn_kw + + if self.proxy: + # Enable Nagle's algorithm for proxies, to avoid packet fragmentation. + # We cannot know if the user has added default socket options, so we cannot replace the + # list. + self.conn_kw.setdefault('socket_options', []) + + def _new_conn(self): + """ + Return a fresh :class:`HTTPConnection`. + """ + self.num_connections += 1 + log.debug("Starting new HTTP connection (%d): %s:%s", + self.num_connections, self.host, self.port or "80") + + conn = self.ConnectionCls(host=self.host, port=self.port, + timeout=self.timeout.connect_timeout, + strict=self.strict, **self.conn_kw) + return conn + + def _get_conn(self, timeout=None): + """ + Get a connection. Will return a pooled connection if one is available. + + If no connections are available and :prop:`.block` is ``False``, then a + fresh connection is returned. + + :param timeout: + Seconds to wait before giving up and raising + :class:`urllib3.exceptions.EmptyPoolError` if the pool is empty and + :prop:`.block` is ``True``. + """ + conn = None + try: + conn = self.pool.get(block=self.block, timeout=timeout) + + except AttributeError: # self.pool is None + raise ClosedPoolError(self, "Pool is closed.") + + except queue.Empty: + if self.block: + raise EmptyPoolError(self, + "Pool reached maximum size and no more " + "connections are allowed.") + pass # Oh well, we'll create a new connection then + + # If this is a persistent connection, check if it got disconnected + if conn and is_connection_dropped(conn): + log.debug("Resetting dropped connection: %s", self.host) + conn.close() + if getattr(conn, 'auto_open', 1) == 0: + # This is a proxied connection that has been mutated by + # httplib._tunnel() and cannot be reused (since it would + # attempt to bypass the proxy) + conn = None + + return conn or self._new_conn() + + def _put_conn(self, conn): + """ + Put a connection back into the pool. + + :param conn: + Connection object for the current host and port as returned by + :meth:`._new_conn` or :meth:`._get_conn`. + + If the pool is already full, the connection is closed and discarded + because we exceeded maxsize. If connections are discarded frequently, + then maxsize should be increased. + + If the pool is closed, then the connection will be closed and discarded. + """ + try: + self.pool.put(conn, block=False) + return # Everything is dandy, done. + except AttributeError: + # self.pool is None. + pass + except queue.Full: + # This should never happen if self.block == True + log.warning( + "Connection pool is full, discarding connection: %s", + self.host) + + # Connection never got put back into the pool, close it. + if conn: + conn.close() + + def _validate_conn(self, conn): + """ + Called right before a request is made, after the socket is created. + """ + pass + + def _prepare_proxy(self, conn): + # Nothing to do for HTTP connections. + pass + + def _get_timeout(self, timeout): + """ Helper that always returns a :class:`urllib3.util.Timeout` """ + if timeout is _Default: + return self.timeout.clone() + + if isinstance(timeout, Timeout): + return timeout.clone() + else: + # User passed us an int/float. This is for backwards compatibility, + # can be removed later + return Timeout.from_float(timeout) + + def _raise_timeout(self, err, url, timeout_value): + """Is the error actually a timeout? Will raise a ReadTimeout or pass""" + + if isinstance(err, SocketTimeout): + raise ReadTimeoutError(self, url, "Read timed out. (read timeout=%s)" % timeout_value) + + # See the above comment about EAGAIN in Python 3. In Python 2 we have + # to specifically catch it and throw the timeout error + if hasattr(err, 'errno') and err.errno in _blocking_errnos: + raise ReadTimeoutError(self, url, "Read timed out. (read timeout=%s)" % timeout_value) + + # Catch possible read timeouts thrown as SSL errors. If not the + # case, rethrow the original. We need to do this because of: + # http://bugs.python.org/issue10272 + if 'timed out' in str(err) or 'did not complete (read)' in str(err): # Python < 2.7.4 + raise ReadTimeoutError(self, url, "Read timed out. (read timeout=%s)" % timeout_value) + + def _make_request(self, conn, method, url, timeout=_Default, chunked=False, + **httplib_request_kw): + """ + Perform a request on a given urllib connection object taken from our + pool. + + :param conn: + a connection from one of our connection pools + + :param timeout: + Socket timeout in seconds for the request. This can be a + float or integer, which will set the same timeout value for + the socket connect and the socket read, or an instance of + :class:`urllib3.util.Timeout`, which gives you more fine-grained + control over your timeouts. + """ + self.num_requests += 1 + + timeout_obj = self._get_timeout(timeout) + timeout_obj.start_connect() + conn.timeout = timeout_obj.connect_timeout + + # Trigger any extra validation we need to do. + try: + self._validate_conn(conn) + except (SocketTimeout, BaseSSLError) as e: + # Py2 raises this as a BaseSSLError, Py3 raises it as socket timeout. + self._raise_timeout(err=e, url=url, timeout_value=conn.timeout) + raise + + # conn.request() calls httplib.*.request, not the method in + # urllib3.request. It also calls makefile (recv) on the socket. + if chunked: + conn.request_chunked(method, url, **httplib_request_kw) + else: + conn.request(method, url, **httplib_request_kw) + + # Reset the timeout for the recv() on the socket + read_timeout = timeout_obj.read_timeout + + # App Engine doesn't have a sock attr + if getattr(conn, 'sock', None): + # In Python 3 socket.py will catch EAGAIN and return None when you + # try and read into the file pointer created by http.client, which + # instead raises a BadStatusLine exception. Instead of catching + # the exception and assuming all BadStatusLine exceptions are read + # timeouts, check for a zero timeout before making the request. + if read_timeout == 0: + raise ReadTimeoutError( + self, url, "Read timed out. (read timeout=%s)" % read_timeout) + if read_timeout is Timeout.DEFAULT_TIMEOUT: + conn.sock.settimeout(socket.getdefaulttimeout()) + else: # None or a value + conn.sock.settimeout(read_timeout) + + # Receive the response from the server + try: + try: # Python 2.7, use buffering of HTTP responses + httplib_response = conn.getresponse(buffering=True) + except TypeError: # Python 3 + try: + httplib_response = conn.getresponse() + except Exception as e: + # Remove the TypeError from the exception chain in Python 3; + # otherwise it looks like a programming error was the cause. + six.raise_from(e, None) + except (SocketTimeout, BaseSSLError, SocketError) as e: + self._raise_timeout(err=e, url=url, timeout_value=read_timeout) + raise + + # AppEngine doesn't have a version attr. + http_version = getattr(conn, '_http_vsn_str', 'HTTP/?') + log.debug("%s://%s:%s \"%s %s %s\" %s %s", self.scheme, self.host, self.port, + method, url, http_version, httplib_response.status, + httplib_response.length) + + try: + assert_header_parsing(httplib_response.msg) + except (HeaderParsingError, TypeError) as hpe: # Platform-specific: Python 3 + log.warning( + 'Failed to parse headers (url=%s): %s', + self._absolute_url(url), hpe, exc_info=True) + + return httplib_response + + def _absolute_url(self, path): + return Url(scheme=self.scheme, host=self.host, port=self.port, path=path).url + + def close(self): + """ + Close all pooled connections and disable the pool. + """ + if self.pool is None: + return + # Disable access to the pool + old_pool, self.pool = self.pool, None + + try: + while True: + conn = old_pool.get(block=False) + if conn: + conn.close() + + except queue.Empty: + pass # Done. + + def is_same_host(self, url): + """ + Check if the given ``url`` is a member of the same host as this + connection pool. + """ + if url.startswith('/'): + return True + + # TODO: Add optional support for socket.gethostbyname checking. + scheme, host, port = get_host(url) + + host = _ipv6_host(host, self.scheme) + + # Use explicit default port for comparison when none is given + if self.port and not port: + port = port_by_scheme.get(scheme) + elif not self.port and port == port_by_scheme.get(scheme): + port = None + + return (scheme, host, port) == (self.scheme, self.host, self.port) + + def urlopen(self, method, url, body=None, headers=None, retries=None, + redirect=True, assert_same_host=True, timeout=_Default, + pool_timeout=None, release_conn=None, chunked=False, + body_pos=None, **response_kw): + """ + Get a connection from the pool and perform an HTTP request. This is the + lowest level call for making a request, so you'll need to specify all + the raw details. + + .. note:: + + More commonly, it's appropriate to use a convenience method provided + by :class:`.RequestMethods`, such as :meth:`request`. + + .. note:: + + `release_conn` will only behave as expected if + `preload_content=False` because we want to make + `preload_content=False` the default behaviour someday soon without + breaking backwards compatibility. + + :param method: + HTTP request method (such as GET, POST, PUT, etc.) + + :param body: + Data to send in the request body (useful for creating + POST requests, see HTTPConnectionPool.post_url for + more convenience). + + :param headers: + Dictionary of custom headers to send, such as User-Agent, + If-None-Match, etc. If None, pool headers are used. If provided, + these headers completely replace any pool-specific headers. + + :param retries: + Configure the number of retries to allow before raising a + :class:`~urllib3.exceptions.MaxRetryError` exception. + + Pass ``None`` to retry until you receive a response. Pass a + :class:`~urllib3.util.retry.Retry` object for fine-grained control + over different types of retries. + Pass an integer number to retry connection errors that many times, + but no other types of errors. Pass zero to never retry. + + If ``False``, then retries are disabled and any exception is raised + immediately. Also, instead of raising a MaxRetryError on redirects, + the redirect response will be returned. + + :type retries: :class:`~urllib3.util.retry.Retry`, False, or an int. + + :param redirect: + If True, automatically handle redirects (status codes 301, 302, + 303, 307, 308). Each redirect counts as a retry. Disabling retries + will disable redirect, too. + + :param assert_same_host: + If ``True``, will make sure that the host of the pool requests is + consistent else will raise HostChangedError. When False, you can + use the pool on an HTTP proxy and request foreign hosts. + + :param timeout: + If specified, overrides the default timeout for this one + request. It may be a float (in seconds) or an instance of + :class:`urllib3.util.Timeout`. + + :param pool_timeout: + If set and the pool is set to block=True, then this method will + block for ``pool_timeout`` seconds and raise EmptyPoolError if no + connection is available within the time period. + + :param release_conn: + If False, then the urlopen call will not release the connection + back into the pool once a response is received (but will release if + you read the entire contents of the response such as when + `preload_content=True`). This is useful if you're not preloading + the response's content immediately. You will need to call + ``r.release_conn()`` on the response ``r`` to return the connection + back into the pool. If None, it takes the value of + ``response_kw.get('preload_content', True)``. + + :param chunked: + If True, urllib3 will send the body using chunked transfer + encoding. Otherwise, urllib3 will send the body using the standard + content-length form. Defaults to False. + + :param int body_pos: + Position to seek to in file-like body in the event of a retry or + redirect. Typically this won't need to be set because urllib3 will + auto-populate the value when needed. + + :param \\**response_kw: + Additional parameters are passed to + :meth:`urllib3.response.HTTPResponse.from_httplib` + """ + if headers is None: + headers = self.headers + + if not isinstance(retries, Retry): + retries = Retry.from_int(retries, redirect=redirect, default=self.retries) + + if release_conn is None: + release_conn = response_kw.get('preload_content', True) + + # Check host + if assert_same_host and not self.is_same_host(url): + raise HostChangedError(self, url, retries) + + conn = None + + # Track whether `conn` needs to be released before + # returning/raising/recursing. Update this variable if necessary, and + # leave `release_conn` constant throughout the function. That way, if + # the function recurses, the original value of `release_conn` will be + # passed down into the recursive call, and its value will be respected. + # + # See issue #651 [1] for details. + # + # [1] <https://github.com/shazow/urllib3/issues/651> + release_this_conn = release_conn + + # Merge the proxy headers. Only do this in HTTP. We have to copy the + # headers dict so we can safely change it without those changes being + # reflected in anyone else's copy. + if self.scheme == 'http': + headers = headers.copy() + headers.update(self.proxy_headers) + + # Must keep the exception bound to a separate variable or else Python 3 + # complains about UnboundLocalError. + err = None + + # Keep track of whether we cleanly exited the except block. This + # ensures we do proper cleanup in finally. + clean_exit = False + + # Rewind body position, if needed. Record current position + # for future rewinds in the event of a redirect/retry. + body_pos = set_file_position(body, body_pos) + + try: + # Request a connection from the queue. + timeout_obj = self._get_timeout(timeout) + conn = self._get_conn(timeout=pool_timeout) + + conn.timeout = timeout_obj.connect_timeout + + is_new_proxy_conn = self.proxy is not None and not getattr(conn, 'sock', None) + if is_new_proxy_conn: + self._prepare_proxy(conn) + + # Make the request on the httplib connection object. + httplib_response = self._make_request(conn, method, url, + timeout=timeout_obj, + body=body, headers=headers, + chunked=chunked) + + # If we're going to release the connection in ``finally:``, then + # the response doesn't need to know about the connection. Otherwise + # it will also try to release it and we'll have a double-release + # mess. + response_conn = conn if not release_conn else None + + # Pass method to Response for length checking + response_kw['request_method'] = method + + # Import httplib's response into our own wrapper object + response = self.ResponseCls.from_httplib(httplib_response, + pool=self, + connection=response_conn, + retries=retries, + **response_kw) + + # Everything went great! + clean_exit = True + + except queue.Empty: + # Timed out by queue. + raise EmptyPoolError(self, "No pool connections are available.") + + except (TimeoutError, HTTPException, SocketError, ProtocolError, + BaseSSLError, SSLError, CertificateError) as e: + # Discard the connection for these exceptions. It will be + # replaced during the next _get_conn() call. + clean_exit = False + if isinstance(e, (BaseSSLError, CertificateError)): + e = SSLError(e) + elif isinstance(e, (SocketError, NewConnectionError)) and self.proxy: + e = ProxyError('Cannot connect to proxy.', e) + elif isinstance(e, (SocketError, HTTPException)): + e = ProtocolError('Connection aborted.', e) + + retries = retries.increment(method, url, error=e, _pool=self, + _stacktrace=sys.exc_info()[2]) + retries.sleep() + + # Keep track of the error for the retry warning. + err = e + + finally: + if not clean_exit: + # We hit some kind of exception, handled or otherwise. We need + # to throw the connection away unless explicitly told not to. + # Close the connection, set the variable to None, and make sure + # we put the None back in the pool to avoid leaking it. + conn = conn and conn.close() + release_this_conn = True + + if release_this_conn: + # Put the connection back to be reused. If the connection is + # expired then it will be None, which will get replaced with a + # fresh connection during _get_conn. + self._put_conn(conn) + + if not conn: + # Try again + log.warning("Retrying (%r) after connection " + "broken by '%r': %s", retries, err, url) + return self.urlopen(method, url, body, headers, retries, + redirect, assert_same_host, + timeout=timeout, pool_timeout=pool_timeout, + release_conn=release_conn, body_pos=body_pos, + **response_kw) + + def drain_and_release_conn(response): + try: + # discard any remaining response body, the connection will be + # released back to the pool once the entire response is read + response.read() + except (TimeoutError, HTTPException, SocketError, ProtocolError, + BaseSSLError, SSLError) as e: + pass + + # Handle redirect? + redirect_location = redirect and response.get_redirect_location() + if redirect_location: + if response.status == 303: + method = 'GET' + + try: + retries = retries.increment(method, url, response=response, _pool=self) + except MaxRetryError: + if retries.raise_on_redirect: + # Drain and release the connection for this response, since + # we're not returning it to be released manually. + drain_and_release_conn(response) + raise + return response + + # drain and return the connection to the pool before recursing + drain_and_release_conn(response) + + retries.sleep_for_retry(response) + log.debug("Redirecting %s -> %s", url, redirect_location) + return self.urlopen( + method, redirect_location, body, headers, + retries=retries, redirect=redirect, + assert_same_host=assert_same_host, + timeout=timeout, pool_timeout=pool_timeout, + release_conn=release_conn, body_pos=body_pos, + **response_kw) + + # Check if we should retry the HTTP response. + has_retry_after = bool(response.getheader('Retry-After')) + if retries.is_retry(method, response.status, has_retry_after): + try: + retries = retries.increment(method, url, response=response, _pool=self) + except MaxRetryError: + if retries.raise_on_status: + # Drain and release the connection for this response, since + # we're not returning it to be released manually. + drain_and_release_conn(response) + raise + return response + + # drain and return the connection to the pool before recursing + drain_and_release_conn(response) + + retries.sleep(response) + log.debug("Retry: %s", url) + return self.urlopen( + method, url, body, headers, + retries=retries, redirect=redirect, + assert_same_host=assert_same_host, + timeout=timeout, pool_timeout=pool_timeout, + release_conn=release_conn, + body_pos=body_pos, **response_kw) + + return response + + +class HTTPSConnectionPool(HTTPConnectionPool): + """ + Same as :class:`.HTTPConnectionPool`, but HTTPS. + + When Python is compiled with the :mod:`ssl` module, then + :class:`.VerifiedHTTPSConnection` is used, which *can* verify certificates, + instead of :class:`.HTTPSConnection`. + + :class:`.VerifiedHTTPSConnection` uses one of ``assert_fingerprint``, + ``assert_hostname`` and ``host`` in this order to verify connections. + If ``assert_hostname`` is False, no verification is done. + + The ``key_file``, ``cert_file``, ``cert_reqs``, ``ca_certs``, + ``ca_cert_dir``, and ``ssl_version`` are only used if :mod:`ssl` is + available and are fed into :meth:`urllib3.util.ssl_wrap_socket` to upgrade + the connection socket into an SSL socket. + """ + + scheme = 'https' + ConnectionCls = HTTPSConnection + + def __init__(self, host, port=None, + strict=False, timeout=Timeout.DEFAULT_TIMEOUT, maxsize=1, + block=False, headers=None, retries=None, + _proxy=None, _proxy_headers=None, + key_file=None, cert_file=None, cert_reqs=None, + ca_certs=None, ssl_version=None, + assert_hostname=None, assert_fingerprint=None, + ca_cert_dir=None, **conn_kw): + + HTTPConnectionPool.__init__(self, host, port, strict, timeout, maxsize, + block, headers, retries, _proxy, _proxy_headers, + **conn_kw) + + if ca_certs and cert_reqs is None: + cert_reqs = 'CERT_REQUIRED' + + self.key_file = key_file + self.cert_file = cert_file + self.cert_reqs = cert_reqs + self.ca_certs = ca_certs + self.ca_cert_dir = ca_cert_dir + self.ssl_version = ssl_version + self.assert_hostname = assert_hostname + self.assert_fingerprint = assert_fingerprint + + def _prepare_conn(self, conn): + """ + Prepare the ``connection`` for :meth:`urllib3.util.ssl_wrap_socket` + and establish the tunnel if proxy is used. + """ + + if isinstance(conn, VerifiedHTTPSConnection): + conn.set_cert(key_file=self.key_file, + cert_file=self.cert_file, + cert_reqs=self.cert_reqs, + ca_certs=self.ca_certs, + ca_cert_dir=self.ca_cert_dir, + assert_hostname=self.assert_hostname, + assert_fingerprint=self.assert_fingerprint) + conn.ssl_version = self.ssl_version + return conn + + def _prepare_proxy(self, conn): + """ + Establish tunnel connection early, because otherwise httplib + would improperly set Host: header to proxy's IP:port. + """ + conn.set_tunnel(self._proxy_host, self.port, self.proxy_headers) + conn.connect() + + def _new_conn(self): + """ + Return a fresh :class:`httplib.HTTPSConnection`. + """ + self.num_connections += 1 + log.debug("Starting new HTTPS connection (%d): %s:%s", + self.num_connections, self.host, self.port or "443") + + if not self.ConnectionCls or self.ConnectionCls is DummyConnection: + raise SSLError("Can't connect to HTTPS URL because the SSL " + "module is not available.") + + actual_host = self.host + actual_port = self.port + if self.proxy is not None: + actual_host = self.proxy.host + actual_port = self.proxy.port + + conn = self.ConnectionCls(host=actual_host, port=actual_port, + timeout=self.timeout.connect_timeout, + strict=self.strict, **self.conn_kw) + + return self._prepare_conn(conn) + + def _validate_conn(self, conn): + """ + Called right before a request is made, after the socket is created. + """ + super(HTTPSConnectionPool, self)._validate_conn(conn) + + # Force connect early to allow us to validate the connection. + if not getattr(conn, 'sock', None): # AppEngine might not have `.sock` + conn.connect() + + if not conn.is_verified: + warnings.warn(( + 'Unverified HTTPS request is being made. ' + 'Adding certificate verification is strongly advised. See: ' + 'https://urllib3.readthedocs.io/en/latest/advanced-usage.html' + '#ssl-warnings'), + InsecureRequestWarning) + + +def connection_from_url(url, **kw): + """ + Given a url, return an :class:`.ConnectionPool` instance of its host. + + This is a shortcut for not having to parse out the scheme, host, and port + of the url before creating an :class:`.ConnectionPool` instance. + + :param url: + Absolute URL string that must include the scheme. Port is optional. + + :param \\**kw: + Passes additional parameters to the constructor of the appropriate + :class:`.ConnectionPool`. Useful for specifying things like + timeout, maxsize, headers, etc. + + Example:: + + >>> conn = connection_from_url('http://google.com/') + >>> r = conn.request('GET', '/') + """ + scheme, host, port = get_host(url) + port = port or port_by_scheme.get(scheme, 80) + if scheme == 'https': + return HTTPSConnectionPool(host, port=port, **kw) + else: + return HTTPConnectionPool(host, port=port, **kw) + + +def _ipv6_host(host, scheme): + """ + Process IPv6 address literals + """ + + # httplib doesn't like it when we include brackets in IPv6 addresses + # Specifically, if we include brackets but also pass the port then + # httplib crazily doubles up the square brackets on the Host header. + # Instead, we need to make sure we never pass ``None`` as the port. + # However, for backward compatibility reasons we can't actually + # *assert* that. See http://bugs.python.org/issue28539 + # + # Also if an IPv6 address literal has a zone identifier, the + # percent sign might be URIencoded, convert it back into ASCII + if host.startswith('[') and host.endswith(']'): + host = host.replace('%25', '%').strip('[]') + if scheme in NORMALIZABLE_SCHEMES: + host = host.lower() + return host diff --git a/venv2/lib/python2.7/site-packages/pip/_vendor/urllib3/contrib/__init__.py b/venv2/lib/python2.7/site-packages/pip/_vendor/urllib3/contrib/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/venv2/lib/python2.7/site-packages/pip/_vendor/urllib3/contrib/_appengine_environ.py b/venv2/lib/python2.7/site-packages/pip/_vendor/urllib3/contrib/_appengine_environ.py new file mode 100644 index 0000000..f3e0094 --- /dev/null +++ b/venv2/lib/python2.7/site-packages/pip/_vendor/urllib3/contrib/_appengine_environ.py @@ -0,0 +1,30 @@ +""" +This module provides means to detect the App Engine environment. +""" + +import os + + +def is_appengine(): + return (is_local_appengine() or + is_prod_appengine() or + is_prod_appengine_mvms()) + + +def is_appengine_sandbox(): + return is_appengine() and not is_prod_appengine_mvms() + + +def is_local_appengine(): + return ('APPENGINE_RUNTIME' in os.environ and + 'Development/' in os.environ['SERVER_SOFTWARE']) + + +def is_prod_appengine(): + return ('APPENGINE_RUNTIME' in os.environ and + 'Google App Engine/' in os.environ['SERVER_SOFTWARE'] and + not is_prod_appengine_mvms()) + + +def is_prod_appengine_mvms(): + return os.environ.get('GAE_VM', False) == 'true' diff --git a/venv2/lib/python2.7/site-packages/pip/_vendor/urllib3/contrib/_securetransport/__init__.py b/venv2/lib/python2.7/site-packages/pip/_vendor/urllib3/contrib/_securetransport/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/venv2/lib/python2.7/site-packages/pip/_vendor/urllib3/contrib/_securetransport/bindings.py b/venv2/lib/python2.7/site-packages/pip/_vendor/urllib3/contrib/_securetransport/bindings.py new file mode 100644 index 0000000..bcf41c0 --- /dev/null +++ b/venv2/lib/python2.7/site-packages/pip/_vendor/urllib3/contrib/_securetransport/bindings.py @@ -0,0 +1,593 @@ +""" +This module uses ctypes to bind a whole bunch of functions and constants from +SecureTransport. The goal here is to provide the low-level API to +SecureTransport. These are essentially the C-level functions and constants, and +they're pretty gross to work with. + +This code is a bastardised version of the code found in Will Bond's oscrypto +library. An enormous debt is owed to him for blazing this trail for us. For +that reason, this code should be considered to be covered both by urllib3's +license and by oscrypto's: + + Copyright (c) 2015-2016 Will Bond <will@wbond.net> + + 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 platform +from ctypes.util import find_library +from ctypes import ( + c_void_p, c_int32, c_char_p, c_size_t, c_byte, c_uint32, c_ulong, c_long, + c_bool +) +from ctypes import CDLL, POINTER, CFUNCTYPE + + +security_path = find_library('Security') +if not security_path: + raise ImportError('The library Security could not be found') + + +core_foundation_path = find_library('CoreFoundation') +if not core_foundation_path: + raise ImportError('The library CoreFoundation could not be found') + + +version = platform.mac_ver()[0] +version_info = tuple(map(int, version.split('.'))) +if version_info < (10, 8): + raise OSError( + 'Only OS X 10.8 and newer are supported, not %s.%s' % ( + version_info[0], version_info[1] + ) + ) + +Security = CDLL(security_path, use_errno=True) +CoreFoundation = CDLL(core_foundation_path, use_errno=True) + +Boolean = c_bool +CFIndex = c_long +CFStringEncoding = c_uint32 +CFData = c_void_p +CFString = c_void_p +CFArray = c_void_p +CFMutableArray = c_void_p +CFDictionary = c_void_p +CFError = c_void_p +CFType = c_void_p +CFTypeID = c_ulong + +CFTypeRef = POINTER(CFType) +CFAllocatorRef = c_void_p + +OSStatus = c_int32 + +CFDataRef = POINTER(CFData) +CFStringRef = POINTER(CFString) +CFArrayRef = POINTER(CFArray) +CFMutableArrayRef = POINTER(CFMutableArray) +CFDictionaryRef = POINTER(CFDictionary) +CFArrayCallBacks = c_void_p +CFDictionaryKeyCallBacks = c_void_p +CFDictionaryValueCallBacks = c_void_p + +SecCertificateRef = POINTER(c_void_p) +SecExternalFormat = c_uint32 +SecExternalItemType = c_uint32 +SecIdentityRef = POINTER(c_void_p) +SecItemImportExportFlags = c_uint32 +SecItemImportExportKeyParameters = c_void_p +SecKeychainRef = POINTER(c_void_p) +SSLProtocol = c_uint32 +SSLCipherSuite = c_uint32 +SSLContextRef = POINTER(c_void_p) +SecTrustRef = POINTER(c_void_p) +SSLConnectionRef = c_uint32 +SecTrustResultType = c_uint32 +SecTrustOptionFlags = c_uint32 +SSLProtocolSide = c_uint32 +SSLConnectionType = c_uint32 +SSLSessionOption = c_uint32 + + +try: + Security.SecItemImport.argtypes = [ + CFDataRef, + CFStringRef, + POINTER(SecExternalFormat), + POINTER(SecExternalItemType), + SecItemImportExportFlags, + POINTER(SecItemImportExportKeyParameters), + SecKeychainRef, + POINTER(CFArrayRef), + ] + Security.SecItemImport.restype = OSStatus + + Security.SecCertificateGetTypeID.argtypes = [] + Security.SecCertificateGetTypeID.restype = CFTypeID + + Security.SecIdentityGetTypeID.argtypes = [] + Security.SecIdentityGetTypeID.restype = CFTypeID + + Security.SecKeyGetTypeID.argtypes = [] + Security.SecKeyGetTypeID.restype = CFTypeID + + Security.SecCertificateCreateWithData.argtypes = [ + CFAllocatorRef, + CFDataRef + ] + Security.SecCertificateCreateWithData.restype = SecCertificateRef + + Security.SecCertificateCopyData.argtypes = [ + SecCertificateRef + ] + Security.SecCertificateCopyData.restype = CFDataRef + + Security.SecCopyErrorMessageString.argtypes = [ + OSStatus, + c_void_p + ] + Security.SecCopyErrorMessageString.restype = CFStringRef + + Security.SecIdentityCreateWithCertificate.argtypes = [ + CFTypeRef, + SecCertificateRef, + POINTER(SecIdentityRef) + ] + Security.SecIdentityCreateWithCertificate.restype = OSStatus + + Security.SecKeychainCreate.argtypes = [ + c_char_p, + c_uint32, + c_void_p, + Boolean, + c_void_p, + POINTER(SecKeychainRef) + ] + Security.SecKeychainCreate.restype = OSStatus + + Security.SecKeychainDelete.argtypes = [ + SecKeychainRef + ] + Security.SecKeychainDelete.restype = OSStatus + + Security.SecPKCS12Import.argtypes = [ + CFDataRef, + CFDictionaryRef, + POINTER(CFArrayRef) + ] + Security.SecPKCS12Import.restype = OSStatus + + SSLReadFunc = CFUNCTYPE(OSStatus, SSLConnectionRef, c_void_p, POINTER(c_size_t)) + SSLWriteFunc = CFUNCTYPE(OSStatus, SSLConnectionRef, POINTER(c_byte), POINTER(c_size_t)) + + Security.SSLSetIOFuncs.argtypes = [ + SSLContextRef, + SSLReadFunc, + SSLWriteFunc + ] + Security.SSLSetIOFuncs.restype = OSStatus + + Security.SSLSetPeerID.argtypes = [ + SSLContextRef, + c_char_p, + c_size_t + ] + Security.SSLSetPeerID.restype = OSStatus + + Security.SSLSetCertificate.argtypes = [ + SSLContextRef, + CFArrayRef + ] + Security.SSLSetCertificate.restype = OSStatus + + Security.SSLSetCertificateAuthorities.argtypes = [ + SSLContextRef, + CFTypeRef, + Boolean + ] + Security.SSLSetCertificateAuthorities.restype = OSStatus + + Security.SSLSetConnection.argtypes = [ + SSLContextRef, + SSLConnectionRef + ] + Security.SSLSetConnection.restype = OSStatus + + Security.SSLSetPeerDomainName.argtypes = [ + SSLContextRef, + c_char_p, + c_size_t + ] + Security.SSLSetPeerDomainName.restype = OSStatus + + Security.SSLHandshake.argtypes = [ + SSLContextRef + ] + Security.SSLHandshake.restype = OSStatus + + Security.SSLRead.argtypes = [ + SSLContextRef, + c_char_p, + c_size_t, + POINTER(c_size_t) + ] + Security.SSLRead.restype = OSStatus + + Security.SSLWrite.argtypes = [ + SSLContextRef, + c_char_p, + c_size_t, + POINTER(c_size_t) + ] + Security.SSLWrite.restype = OSStatus + + Security.SSLClose.argtypes = [ + SSLContextRef + ] + Security.SSLClose.restype = OSStatus + + Security.SSLGetNumberSupportedCiphers.argtypes = [ + SSLContextRef, + POINTER(c_size_t) + ] + Security.SSLGetNumberSupportedCiphers.restype = OSStatus + + Security.SSLGetSupportedCiphers.argtypes = [ + SSLContextRef, + POINTER(SSLCipherSuite), + POINTER(c_size_t) + ] + Security.SSLGetSupportedCiphers.restype = OSStatus + + Security.SSLSetEnabledCiphers.argtypes = [ + SSLContextRef, + POINTER(SSLCipherSuite), + c_size_t + ] + Security.SSLSetEnabledCiphers.restype = OSStatus + + Security.SSLGetNumberEnabledCiphers.argtype = [ + SSLContextRef, + POINTER(c_size_t) + ] + Security.SSLGetNumberEnabledCiphers.restype = OSStatus + + Security.SSLGetEnabledCiphers.argtypes = [ + SSLContextRef, + POINTER(SSLCipherSuite), + POINTER(c_size_t) + ] + Security.SSLGetEnabledCiphers.restype = OSStatus + + Security.SSLGetNegotiatedCipher.argtypes = [ + SSLContextRef, + POINTER(SSLCipherSuite) + ] + Security.SSLGetNegotiatedCipher.restype = OSStatus + + Security.SSLGetNegotiatedProtocolVersion.argtypes = [ + SSLContextRef, + POINTER(SSLProtocol) + ] + Security.SSLGetNegotiatedProtocolVersion.restype = OSStatus + + Security.SSLCopyPeerTrust.argtypes = [ + SSLContextRef, + POINTER(SecTrustRef) + ] + Security.SSLCopyPeerTrust.restype = OSStatus + + Security.SecTrustSetAnchorCertificates.argtypes = [ + SecTrustRef, + CFArrayRef + ] + Security.SecTrustSetAnchorCertificates.restype = OSStatus + + Security.SecTrustSetAnchorCertificatesOnly.argstypes = [ + SecTrustRef, + Boolean + ] + Security.SecTrustSetAnchorCertificatesOnly.restype = OSStatus + + Security.SecTrustEvaluate.argtypes = [ + SecTrustRef, + POINTER(SecTrustResultType) + ] + Security.SecTrustEvaluate.restype = OSStatus + + Security.SecTrustGetCertificateCount.argtypes = [ + SecTrustRef + ] + Security.SecTrustGetCertificateCount.restype = CFIndex + + Security.SecTrustGetCertificateAtIndex.argtypes = [ + SecTrustRef, + CFIndex + ] + Security.SecTrustGetCertificateAtIndex.restype = SecCertificateRef + + Security.SSLCreateContext.argtypes = [ + CFAllocatorRef, + SSLProtocolSide, + SSLConnectionType + ] + Security.SSLCreateContext.restype = SSLContextRef + + Security.SSLSetSessionOption.argtypes = [ + SSLContextRef, + SSLSessionOption, + Boolean + ] + Security.SSLSetSessionOption.restype = OSStatus + + Security.SSLSetProtocolVersionMin.argtypes = [ + SSLContextRef, + SSLProtocol + ] + Security.SSLSetProtocolVersionMin.restype = OSStatus + + Security.SSLSetProtocolVersionMax.argtypes = [ + SSLContextRef, + SSLProtocol + ] + Security.SSLSetProtocolVersionMax.restype = OSStatus + + Security.SecCopyErrorMessageString.argtypes = [ + OSStatus, + c_void_p + ] + Security.SecCopyErrorMessageString.restype = CFStringRef + + Security.SSLReadFunc = SSLReadFunc + Security.SSLWriteFunc = SSLWriteFunc + Security.SSLContextRef = SSLContextRef + Security.SSLProtocol = SSLProtocol + Security.SSLCipherSuite = SSLCipherSuite + Security.SecIdentityRef = SecIdentityRef + Security.SecKeychainRef = SecKeychainRef + Security.SecTrustRef = SecTrustRef + Security.SecTrustResultType = SecTrustResultType + Security.SecExternalFormat = SecExternalFormat + Security.OSStatus = OSStatus + + Security.kSecImportExportPassphrase = CFStringRef.in_dll( + Security, 'kSecImportExportPassphrase' + ) + Security.kSecImportItemIdentity = CFStringRef.in_dll( + Security, 'kSecImportItemIdentity' + ) + + # CoreFoundation time! + CoreFoundation.CFRetain.argtypes = [ + CFTypeRef + ] + CoreFoundation.CFRetain.restype = CFTypeRef + + CoreFoundation.CFRelease.argtypes = [ + CFTypeRef + ] + CoreFoundation.CFRelease.restype = None + + CoreFoundation.CFGetTypeID.argtypes = [ + CFTypeRef + ] + CoreFoundation.CFGetTypeID.restype = CFTypeID + + CoreFoundation.CFStringCreateWithCString.argtypes = [ + CFAllocatorRef, + c_char_p, + CFStringEncoding + ] + CoreFoundation.CFStringCreateWithCString.restype = CFStringRef + + CoreFoundation.CFStringGetCStringPtr.argtypes = [ + CFStringRef, + CFStringEncoding + ] + CoreFoundation.CFStringGetCStringPtr.restype = c_char_p + + CoreFoundation.CFStringGetCString.argtypes = [ + CFStringRef, + c_char_p, + CFIndex, + CFStringEncoding + ] + CoreFoundation.CFStringGetCString.restype = c_bool + + CoreFoundation.CFDataCreate.argtypes = [ + CFAllocatorRef, + c_char_p, + CFIndex + ] + CoreFoundation.CFDataCreate.restype = CFDataRef + + CoreFoundation.CFDataGetLength.argtypes = [ + CFDataRef + ] + CoreFoundation.CFDataGetLength.restype = CFIndex + + CoreFoundation.CFDataGetBytePtr.argtypes = [ + CFDataRef + ] + CoreFoundation.CFDataGetBytePtr.restype = c_void_p + + CoreFoundation.CFDictionaryCreate.argtypes = [ + CFAllocatorRef, + POINTER(CFTypeRef), + POINTER(CFTypeRef), + CFIndex, + CFDictionaryKeyCallBacks, + CFDictionaryValueCallBacks + ] + CoreFoundation.CFDictionaryCreate.restype = CFDictionaryRef + + CoreFoundation.CFDictionaryGetValue.argtypes = [ + CFDictionaryRef, + CFTypeRef + ] + CoreFoundation.CFDictionaryGetValue.restype = CFTypeRef + + CoreFoundation.CFArrayCreate.argtypes = [ + CFAllocatorRef, + POINTER(CFTypeRef), + CFIndex, + CFArrayCallBacks, + ] + CoreFoundation.CFArrayCreate.restype = CFArrayRef + + CoreFoundation.CFArrayCreateMutable.argtypes = [ + CFAllocatorRef, + CFIndex, + CFArrayCallBacks + ] + CoreFoundation.CFArrayCreateMutable.restype = CFMutableArrayRef + + CoreFoundation.CFArrayAppendValue.argtypes = [ + CFMutableArrayRef, + c_void_p + ] + CoreFoundation.CFArrayAppendValue.restype = None + + CoreFoundation.CFArrayGetCount.argtypes = [ + CFArrayRef + ] + CoreFoundation.CFArrayGetCount.restype = CFIndex + + CoreFoundation.CFArrayGetValueAtIndex.argtypes = [ + CFArrayRef, + CFIndex + ] + CoreFoundation.CFArrayGetValueAtIndex.restype = c_void_p + + CoreFoundation.kCFAllocatorDefault = CFAllocatorRef.in_dll( + CoreFoundation, 'kCFAllocatorDefault' + ) + CoreFoundation.kCFTypeArrayCallBacks = c_void_p.in_dll(CoreFoundation, 'kCFTypeArrayCallBacks') + CoreFoundation.kCFTypeDictionaryKeyCallBacks = c_void_p.in_dll( + CoreFoundation, 'kCFTypeDictionaryKeyCallBacks' + ) + CoreFoundation.kCFTypeDictionaryValueCallBacks = c_void_p.in_dll( + CoreFoundation, 'kCFTypeDictionaryValueCallBacks' + ) + + CoreFoundation.CFTypeRef = CFTypeRef + CoreFoundation.CFArrayRef = CFArrayRef + CoreFoundation.CFStringRef = CFStringRef + CoreFoundation.CFDictionaryRef = CFDictionaryRef + +except (AttributeError): + raise ImportError('Error initializing ctypes') + + +class CFConst(object): + """ + A class object that acts as essentially a namespace for CoreFoundation + constants. + """ + kCFStringEncodingUTF8 = CFStringEncoding(0x08000100) + + +class SecurityConst(object): + """ + A class object that acts as essentially a namespace for Security constants. + """ + kSSLSessionOptionBreakOnServerAuth = 0 + + kSSLProtocol2 = 1 + kSSLProtocol3 = 2 + kTLSProtocol1 = 4 + kTLSProtocol11 = 7 + kTLSProtocol12 = 8 + + kSSLClientSide = 1 + kSSLStreamType = 0 + + kSecFormatPEMSequence = 10 + + kSecTrustResultInvalid = 0 + kSecTrustResultProceed = 1 + # This gap is present on purpose: this was kSecTrustResultConfirm, which + # is deprecated. + kSecTrustResultDeny = 3 + kSecTrustResultUnspecified = 4 + kSecTrustResultRecoverableTrustFailure = 5 + kSecTrustResultFatalTrustFailure = 6 + kSecTrustResultOtherError = 7 + + errSSLProtocol = -9800 + errSSLWouldBlock = -9803 + errSSLClosedGraceful = -9805 + errSSLClosedNoNotify = -9816 + errSSLClosedAbort = -9806 + + errSSLXCertChainInvalid = -9807 + errSSLCrypto = -9809 + errSSLInternal = -9810 + errSSLCertExpired = -9814 + errSSLCertNotYetValid = -9815 + errSSLUnknownRootCert = -9812 + errSSLNoRootCert = -9813 + errSSLHostNameMismatch = -9843 + errSSLPeerHandshakeFail = -9824 + errSSLPeerUserCancelled = -9839 + errSSLWeakPeerEphemeralDHKey = -9850 + errSSLServerAuthCompleted = -9841 + errSSLRecordOverflow = -9847 + + errSecVerifyFailed = -67808 + errSecNoTrustSettings = -25263 + errSecItemNotFound = -25300 + errSecInvalidTrustSettings = -25262 + + # Cipher suites. We only pick the ones our default cipher string allows. + TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384 = 0xC02C + TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 = 0xC030 + TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256 = 0xC02B + TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 = 0xC02F + TLS_DHE_DSS_WITH_AES_256_GCM_SHA384 = 0x00A3 + TLS_DHE_RSA_WITH_AES_256_GCM_SHA384 = 0x009F + TLS_DHE_DSS_WITH_AES_128_GCM_SHA256 = 0x00A2 + TLS_DHE_RSA_WITH_AES_128_GCM_SHA256 = 0x009E + TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384 = 0xC024 + TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384 = 0xC028 + TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA = 0xC00A + TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA = 0xC014 + TLS_DHE_RSA_WITH_AES_256_CBC_SHA256 = 0x006B + TLS_DHE_DSS_WITH_AES_256_CBC_SHA256 = 0x006A + TLS_DHE_RSA_WITH_AES_256_CBC_SHA = 0x0039 + TLS_DHE_DSS_WITH_AES_256_CBC_SHA = 0x0038 + TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256 = 0xC023 + TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256 = 0xC027 + TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA = 0xC009 + TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA = 0xC013 + TLS_DHE_RSA_WITH_AES_128_CBC_SHA256 = 0x0067 + TLS_DHE_DSS_WITH_AES_128_CBC_SHA256 = 0x0040 + TLS_DHE_RSA_WITH_AES_128_CBC_SHA = 0x0033 + TLS_DHE_DSS_WITH_AES_128_CBC_SHA = 0x0032 + TLS_RSA_WITH_AES_256_GCM_SHA384 = 0x009D + TLS_RSA_WITH_AES_128_GCM_SHA256 = 0x009C + TLS_RSA_WITH_AES_256_CBC_SHA256 = 0x003D + TLS_RSA_WITH_AES_128_CBC_SHA256 = 0x003C + TLS_RSA_WITH_AES_256_CBC_SHA = 0x0035 + TLS_RSA_WITH_AES_128_CBC_SHA = 0x002F + TLS_AES_128_GCM_SHA256 = 0x1301 + TLS_AES_256_GCM_SHA384 = 0x1302 + TLS_CHACHA20_POLY1305_SHA256 = 0x1303 diff --git a/venv2/lib/python2.7/site-packages/pip/_vendor/urllib3/contrib/_securetransport/low_level.py b/venv2/lib/python2.7/site-packages/pip/_vendor/urllib3/contrib/_securetransport/low_level.py new file mode 100644 index 0000000..b13cd9e --- /dev/null +++ b/venv2/lib/python2.7/site-packages/pip/_vendor/urllib3/contrib/_securetransport/low_level.py @@ -0,0 +1,346 @@ +""" +Low-level helpers for the SecureTransport bindings. + +These are Python functions that are not directly related to the high-level APIs +but are necessary to get them to work. They include a whole bunch of low-level +CoreFoundation messing about and memory management. The concerns in this module +are almost entirely about trying to avoid memory leaks and providing +appropriate and useful assistance to the higher-level code. +""" +import base64 +import ctypes +import itertools +import re +import os +import ssl +import tempfile + +from .bindings import Security, CoreFoundation, CFConst + + +# This regular expression is used to grab PEM data out of a PEM bundle. +_PEM_CERTS_RE = re.compile( + b"-----BEGIN CERTIFICATE-----\n(.*?)\n-----END CERTIFICATE-----", re.DOTALL +) + + +def _cf_data_from_bytes(bytestring): + """ + Given a bytestring, create a CFData object from it. This CFData object must + be CFReleased by the caller. + """ + return CoreFoundation.CFDataCreate( + CoreFoundation.kCFAllocatorDefault, bytestring, len(bytestring) + ) + + +def _cf_dictionary_from_tuples(tuples): + """ + Given a list of Python tuples, create an associated CFDictionary. + """ + dictionary_size = len(tuples) + + # We need to get the dictionary keys and values out in the same order. + keys = (t[0] for t in tuples) + values = (t[1] for t in tuples) + cf_keys = (CoreFoundation.CFTypeRef * dictionary_size)(*keys) + cf_values = (CoreFoundation.CFTypeRef * dictionary_size)(*values) + + return CoreFoundation.CFDictionaryCreate( + CoreFoundation.kCFAllocatorDefault, + cf_keys, + cf_values, + dictionary_size, + CoreFoundation.kCFTypeDictionaryKeyCallBacks, + CoreFoundation.kCFTypeDictionaryValueCallBacks, + ) + + +def _cf_string_to_unicode(value): + """ + Creates a Unicode string from a CFString object. Used entirely for error + reporting. + + Yes, it annoys me quite a lot that this function is this complex. + """ + value_as_void_p = ctypes.cast(value, ctypes.POINTER(ctypes.c_void_p)) + + string = CoreFoundation.CFStringGetCStringPtr( + value_as_void_p, + CFConst.kCFStringEncodingUTF8 + ) + if string is None: + buffer = ctypes.create_string_buffer(1024) + result = CoreFoundation.CFStringGetCString( + value_as_void_p, + buffer, + 1024, + CFConst.kCFStringEncodingUTF8 + ) + if not result: + raise OSError('Error copying C string from CFStringRef') + string = buffer.value + if string is not None: + string = string.decode('utf-8') + return string + + +def _assert_no_error(error, exception_class=None): + """ + Checks the return code and throws an exception if there is an error to + report + """ + if error == 0: + return + + cf_error_string = Security.SecCopyErrorMessageString(error, None) + output = _cf_string_to_unicode(cf_error_string) + CoreFoundation.CFRelease(cf_error_string) + + if output is None or output == u'': + output = u'OSStatus %s' % error + + if exception_class is None: + exception_class = ssl.SSLError + + raise exception_class(output) + + +def _cert_array_from_pem(pem_bundle): + """ + Given a bundle of certs in PEM format, turns them into a CFArray of certs + that can be used to validate a cert chain. + """ + # Normalize the PEM bundle's line endings. + pem_bundle = pem_bundle.replace(b"\r\n", b"\n") + + der_certs = [ + base64.b64decode(match.group(1)) + for match in _PEM_CERTS_RE.finditer(pem_bundle) + ] + if not der_certs: + raise ssl.SSLError("No root certificates specified") + + cert_array = CoreFoundation.CFArrayCreateMutable( + CoreFoundation.kCFAllocatorDefault, + 0, + ctypes.byref(CoreFoundation.kCFTypeArrayCallBacks) + ) + if not cert_array: + raise ssl.SSLError("Unable to allocate memory!") + + try: + for der_bytes in der_certs: + certdata = _cf_data_from_bytes(der_bytes) + if not certdata: + raise ssl.SSLError("Unable to allocate memory!") + cert = Security.SecCertificateCreateWithData( + CoreFoundation.kCFAllocatorDefault, certdata + ) + CoreFoundation.CFRelease(certdata) + if not cert: + raise ssl.SSLError("Unable to build cert object!") + + CoreFoundation.CFArrayAppendValue(cert_array, cert) + CoreFoundation.CFRelease(cert) + except Exception: + # We need to free the array before the exception bubbles further. + # We only want to do that if an error occurs: otherwise, the caller + # should free. + CoreFoundation.CFRelease(cert_array) + + return cert_array + + +def _is_cert(item): + """ + Returns True if a given CFTypeRef is a certificate. + """ + expected = Security.SecCertificateGetTypeID() + return CoreFoundation.CFGetTypeID(item) == expected + + +def _is_identity(item): + """ + Returns True if a given CFTypeRef is an identity. + """ + expected = Security.SecIdentityGetTypeID() + return CoreFoundation.CFGetTypeID(item) == expected + + +def _temporary_keychain(): + """ + This function creates a temporary Mac keychain that we can use to work with + credentials. This keychain uses a one-time password and a temporary file to + store the data. We expect to have one keychain per socket. The returned + SecKeychainRef must be freed by the caller, including calling + SecKeychainDelete. + + Returns a tuple of the SecKeychainRef and the path to the temporary + directory that contains it. + """ + # Unfortunately, SecKeychainCreate requires a path to a keychain. This + # means we cannot use mkstemp to use a generic temporary file. Instead, + # we're going to create a temporary directory and a filename to use there. + # This filename will be 8 random bytes expanded into base64. We also need + # some random bytes to password-protect the keychain we're creating, so we + # ask for 40 random bytes. + random_bytes = os.urandom(40) + filename = base64.b16encode(random_bytes[:8]).decode('utf-8') + password = base64.b16encode(random_bytes[8:]) # Must be valid UTF-8 + tempdirectory = tempfile.mkdtemp() + + keychain_path = os.path.join(tempdirectory, filename).encode('utf-8') + + # We now want to create the keychain itself. + keychain = Security.SecKeychainRef() + status = Security.SecKeychainCreate( + keychain_path, + len(password), + password, + False, + None, + ctypes.byref(keychain) + ) + _assert_no_error(status) + + # Having created the keychain, we want to pass it off to the caller. + return keychain, tempdirectory + + +def _load_items_from_file(keychain, path): + """ + Given a single file, loads all the trust objects from it into arrays and + the keychain. + Returns a tuple of lists: the first list is a list of identities, the + second a list of certs. + """ + certificates = [] + identities = [] + result_array = None + + with open(path, 'rb') as f: + raw_filedata = f.read() + + try: + filedata = CoreFoundation.CFDataCreate( + CoreFoundation.kCFAllocatorDefault, + raw_filedata, + len(raw_filedata) + ) + result_array = CoreFoundation.CFArrayRef() + result = Security.SecItemImport( + filedata, # cert data + None, # Filename, leaving it out for now + None, # What the type of the file is, we don't care + None, # what's in the file, we don't care + 0, # import flags + None, # key params, can include passphrase in the future + keychain, # The keychain to insert into + ctypes.byref(result_array) # Results + ) + _assert_no_error(result) + + # A CFArray is not very useful to us as an intermediary + # representation, so we are going to extract the objects we want + # and then free the array. We don't need to keep hold of keys: the + # keychain already has them! + result_count = CoreFoundation.CFArrayGetCount(result_array) + for index in range(result_count): + item = CoreFoundation.CFArrayGetValueAtIndex( + result_array, index + ) + item = ctypes.cast(item, CoreFoundation.CFTypeRef) + + if _is_cert(item): + CoreFoundation.CFRetain(item) + certificates.append(item) + elif _is_identity(item): + CoreFoundation.CFRetain(item) + identities.append(item) + finally: + if result_array: + CoreFoundation.CFRelease(result_array) + + CoreFoundation.CFRelease(filedata) + + return (identities, certificates) + + +def _load_client_cert_chain(keychain, *paths): + """ + Load certificates and maybe keys from a number of files. Has the end goal + of returning a CFArray containing one SecIdentityRef, and then zero or more + SecCertificateRef objects, suitable for use as a client certificate trust + chain. + """ + # Ok, the strategy. + # + # This relies on knowing that macOS will not give you a SecIdentityRef + # unless you have imported a key into a keychain. This is a somewhat + # artificial limitation of macOS (for example, it doesn't necessarily + # affect iOS), but there is nothing inside Security.framework that lets you + # get a SecIdentityRef without having a key in a keychain. + # + # So the policy here is we take all the files and iterate them in order. + # Each one will use SecItemImport to have one or more objects loaded from + # it. We will also point at a keychain that macOS can use to work with the + # private key. + # + # Once we have all the objects, we'll check what we actually have. If we + # already have a SecIdentityRef in hand, fab: we'll use that. Otherwise, + # we'll take the first certificate (which we assume to be our leaf) and + # ask the keychain to give us a SecIdentityRef with that cert's associated + # key. + # + # We'll then return a CFArray containing the trust chain: one + # SecIdentityRef and then zero-or-more SecCertificateRef objects. The + # responsibility for freeing this CFArray will be with the caller. This + # CFArray must remain alive for the entire connection, so in practice it + # will be stored with a single SSLSocket, along with the reference to the + # keychain. + certificates = [] + identities = [] + + # Filter out bad paths. + paths = (path for path in paths if path) + + try: + for file_path in paths: + new_identities, new_certs = _load_items_from_file( + keychain, file_path + ) + identities.extend(new_identities) + certificates.extend(new_certs) + + # Ok, we have everything. The question is: do we have an identity? If + # not, we want to grab one from the first cert we have. + if not identities: + new_identity = Security.SecIdentityRef() + status = Security.SecIdentityCreateWithCertificate( + keychain, + certificates[0], + ctypes.byref(new_identity) + ) + _assert_no_error(status) + identities.append(new_identity) + + # We now want to release the original certificate, as we no longer + # need it. + CoreFoundation.CFRelease(certificates.pop(0)) + + # We now need to build a new CFArray that holds the trust chain. + trust_chain = CoreFoundation.CFArrayCreateMutable( + CoreFoundation.kCFAllocatorDefault, + 0, + ctypes.byref(CoreFoundation.kCFTypeArrayCallBacks), + ) + for item in itertools.chain(identities, certificates): + # ArrayAppendValue does a CFRetain on the item. That's fine, + # because the finally block will release our other refs to them. + CoreFoundation.CFArrayAppendValue(trust_chain, item) + + return trust_chain + finally: + for obj in itertools.chain(identities, certificates): + CoreFoundation.CFRelease(obj) diff --git a/venv2/lib/python2.7/site-packages/pip/_vendor/urllib3/contrib/appengine.py b/venv2/lib/python2.7/site-packages/pip/_vendor/urllib3/contrib/appengine.py new file mode 100644 index 0000000..9b42952 --- /dev/null +++ b/venv2/lib/python2.7/site-packages/pip/_vendor/urllib3/contrib/appengine.py @@ -0,0 +1,289 @@ +""" +This module provides a pool manager that uses Google App Engine's +`URLFetch Service <https://cloud.google.com/appengine/docs/python/urlfetch>`_. + +Example usage:: + + from pip._vendor.urllib3 import PoolManager + from pip._vendor.urllib3.contrib.appengine import AppEngineManager, is_appengine_sandbox + + if is_appengine_sandbox(): + # AppEngineManager uses AppEngine's URLFetch API behind the scenes + http = AppEngineManager() + else: + # PoolManager uses a socket-level API behind the scenes + http = PoolManager() + + r = http.request('GET', 'https://google.com/') + +There are `limitations <https://cloud.google.com/appengine/docs/python/\ +urlfetch/#Python_Quotas_and_limits>`_ to the URLFetch service and it may not be +the best choice for your application. There are three options for using +urllib3 on Google App Engine: + +1. You can use :class:`AppEngineManager` with URLFetch. URLFetch is + cost-effective in many circumstances as long as your usage is within the + limitations. +2. You can use a normal :class:`~urllib3.PoolManager` by enabling sockets. + Sockets also have `limitations and restrictions + <https://cloud.google.com/appengine/docs/python/sockets/\ + #limitations-and-restrictions>`_ and have a lower free quota than URLFetch. + To use sockets, be sure to specify the following in your ``app.yaml``:: + + env_variables: + GAE_USE_SOCKETS_HTTPLIB : 'true' + +3. If you are using `App Engine Flexible +<https://cloud.google.com/appengine/docs/flexible/>`_, you can use the standard +:class:`PoolManager` without any configuration or special environment variables. +""" + +from __future__ import absolute_import +import io +import logging +import warnings +from ..packages.six.moves.urllib.parse import urljoin + +from ..exceptions import ( + HTTPError, + HTTPWarning, + MaxRetryError, + ProtocolError, + TimeoutError, + SSLError +) + +from ..request import RequestMethods +from ..response import HTTPResponse +from ..util.timeout import Timeout +from ..util.retry import Retry +from . import _appengine_environ + +try: + from google.appengine.api import urlfetch +except ImportError: + urlfetch = None + + +log = logging.getLogger(__name__) + + +class AppEnginePlatformWarning(HTTPWarning): + pass + + +class AppEnginePlatformError(HTTPError): + pass + + +class AppEngineManager(RequestMethods): + """ + Connection manager for Google App Engine sandbox applications. + + This manager uses the URLFetch service directly instead of using the + emulated httplib, and is subject to URLFetch limitations as described in + the App Engine documentation `here + <https://cloud.google.com/appengine/docs/python/urlfetch>`_. + + Notably it will raise an :class:`AppEnginePlatformError` if: + * URLFetch is not available. + * If you attempt to use this on App Engine Flexible, as full socket + support is available. + * If a request size is more than 10 megabytes. + * If a response size is more than 32 megabtyes. + * If you use an unsupported request method such as OPTIONS. + + Beyond those cases, it will raise normal urllib3 errors. + """ + + def __init__(self, headers=None, retries=None, validate_certificate=True, + urlfetch_retries=True): + if not urlfetch: + raise AppEnginePlatformError( + "URLFetch is not available in this environment.") + + if is_prod_appengine_mvms(): + raise AppEnginePlatformError( + "Use normal urllib3.PoolManager instead of AppEngineManager" + "on Managed VMs, as using URLFetch is not necessary in " + "this environment.") + + warnings.warn( + "urllib3 is using URLFetch on Google App Engine sandbox instead " + "of sockets. To use sockets directly instead of URLFetch see " + "https://urllib3.readthedocs.io/en/latest/reference/urllib3.contrib.html.", + AppEnginePlatformWarning) + + RequestMethods.__init__(self, headers) + self.validate_certificate = validate_certificate + self.urlfetch_retries = urlfetch_retries + + self.retries = retries or Retry.DEFAULT + + def __enter__(self): + return self + + def __exit__(self, exc_type, exc_val, exc_tb): + # Return False to re-raise any potential exceptions + return False + + def urlopen(self, method, url, body=None, headers=None, + retries=None, redirect=True, timeout=Timeout.DEFAULT_TIMEOUT, + **response_kw): + + retries = self._get_retries(retries, redirect) + + try: + follow_redirects = ( + redirect and + retries.redirect != 0 and + retries.total) + response = urlfetch.fetch( + url, + payload=body, + method=method, + headers=headers or {}, + allow_truncated=False, + follow_redirects=self.urlfetch_retries and follow_redirects, + deadline=self._get_absolute_timeout(timeout), + validate_certificate=self.validate_certificate, + ) + except urlfetch.DeadlineExceededError as e: + raise TimeoutError(self, e) + + except urlfetch.InvalidURLError as e: + if 'too large' in str(e): + raise AppEnginePlatformError( + "URLFetch request too large, URLFetch only " + "supports requests up to 10mb in size.", e) + raise ProtocolError(e) + + except urlfetch.DownloadError as e: + if 'Too many redirects' in str(e): + raise MaxRetryError(self, url, reason=e) + raise ProtocolError(e) + + except urlfetch.ResponseTooLargeError as e: + raise AppEnginePlatformError( + "URLFetch response too large, URLFetch only supports" + "responses up to 32mb in size.", e) + + except urlfetch.SSLCertificateError as e: + raise SSLError(e) + + except urlfetch.InvalidMethodError as e: + raise AppEnginePlatformError( + "URLFetch does not support method: %s" % method, e) + + http_response = self._urlfetch_response_to_http_response( + response, retries=retries, **response_kw) + + # Handle redirect? + redirect_location = redirect and http_response.get_redirect_location() + if redirect_location: + # Check for redirect response + if (self.urlfetch_retries and retries.raise_on_redirect): + raise MaxRetryError(self, url, "too many redirects") + else: + if http_response.status == 303: + method = 'GET' + + try: + retries = retries.increment(method, url, response=http_response, _pool=self) + except MaxRetryError: + if retries.raise_on_redirect: + raise MaxRetryError(self, url, "too many redirects") + return http_response + + retries.sleep_for_retry(http_response) + log.debug("Redirecting %s -> %s", url, redirect_location) + redirect_url = urljoin(url, redirect_location) + return self.urlopen( + method, redirect_url, body, headers, + retries=retries, redirect=redirect, + timeout=timeout, **response_kw) + + # Check if we should retry the HTTP response. + has_retry_after = bool(http_response.getheader('Retry-After')) + if retries.is_retry(method, http_response.status, has_retry_after): + retries = retries.increment( + method, url, response=http_response, _pool=self) + log.debug("Retry: %s", url) + retries.sleep(http_response) + return self.urlopen( + method, url, + body=body, headers=headers, + retries=retries, redirect=redirect, + timeout=timeout, **response_kw) + + return http_response + + def _urlfetch_response_to_http_response(self, urlfetch_resp, **response_kw): + + if is_prod_appengine(): + # Production GAE handles deflate encoding automatically, but does + # not remove the encoding header. + content_encoding = urlfetch_resp.headers.get('content-encoding') + + if content_encoding == 'deflate': + del urlfetch_resp.headers['content-encoding'] + + transfer_encoding = urlfetch_resp.headers.get('transfer-encoding') + # We have a full response's content, + # so let's make sure we don't report ourselves as chunked data. + if transfer_encoding == 'chunked': + encodings = transfer_encoding.split(",") + encodings.remove('chunked') + urlfetch_resp.headers['transfer-encoding'] = ','.join(encodings) + + original_response = HTTPResponse( + # In order for decoding to work, we must present the content as + # a file-like object. + body=io.BytesIO(urlfetch_resp.content), + msg=urlfetch_resp.header_msg, + headers=urlfetch_resp.headers, + status=urlfetch_resp.status_code, + **response_kw + ) + + return HTTPResponse( + body=io.BytesIO(urlfetch_resp.content), + headers=urlfetch_resp.headers, + status=urlfetch_resp.status_code, + original_response=original_response, + **response_kw + ) + + def _get_absolute_timeout(self, timeout): + if timeout is Timeout.DEFAULT_TIMEOUT: + return None # Defer to URLFetch's default. + if isinstance(timeout, Timeout): + if timeout._read is not None or timeout._connect is not None: + warnings.warn( + "URLFetch does not support granular timeout settings, " + "reverting to total or default URLFetch timeout.", + AppEnginePlatformWarning) + return timeout.total + return timeout + + def _get_retries(self, retries, redirect): + if not isinstance(retries, Retry): + retries = Retry.from_int( + retries, redirect=redirect, default=self.retries) + + if retries.connect or retries.read or retries.redirect: + warnings.warn( + "URLFetch only supports total retries and does not " + "recognize connect, read, or redirect retry parameters.", + AppEnginePlatformWarning) + + return retries + + +# Alias methods from _appengine_environ to maintain public API interface. + +is_appengine = _appengine_environ.is_appengine +is_appengine_sandbox = _appengine_environ.is_appengine_sandbox +is_local_appengine = _appengine_environ.is_local_appengine +is_prod_appengine = _appengine_environ.is_prod_appengine +is_prod_appengine_mvms = _appengine_environ.is_prod_appengine_mvms diff --git a/venv2/lib/python2.7/site-packages/pip/_vendor/urllib3/contrib/ntlmpool.py b/venv2/lib/python2.7/site-packages/pip/_vendor/urllib3/contrib/ntlmpool.py new file mode 100644 index 0000000..8ea127c --- /dev/null +++ b/venv2/lib/python2.7/site-packages/pip/_vendor/urllib3/contrib/ntlmpool.py @@ -0,0 +1,111 @@ +""" +NTLM authenticating pool, contributed by erikcederstran + +Issue #10, see: http://code.google.com/p/urllib3/issues/detail?id=10 +""" +from __future__ import absolute_import + +from logging import getLogger +from ntlm import ntlm + +from .. import HTTPSConnectionPool +from ..packages.six.moves.http_client import HTTPSConnection + + +log = getLogger(__name__) + + +class NTLMConnectionPool(HTTPSConnectionPool): + """ + Implements an NTLM authentication version of an urllib3 connection pool + """ + + scheme = 'https' + + def __init__(self, user, pw, authurl, *args, **kwargs): + """ + authurl is a random URL on the server that is protected by NTLM. + user is the Windows user, probably in the DOMAIN\\username format. + pw is the password for the user. + """ + super(NTLMConnectionPool, self).__init__(*args, **kwargs) + self.authurl = authurl + self.rawuser = user + user_parts = user.split('\\', 1) + self.domain = user_parts[0].upper() + self.user = user_parts[1] + self.pw = pw + + def _new_conn(self): + # Performs the NTLM handshake that secures the connection. The socket + # must be kept open while requests are performed. + self.num_connections += 1 + log.debug('Starting NTLM HTTPS connection no. %d: https://%s%s', + self.num_connections, self.host, self.authurl) + + headers = {'Connection': 'Keep-Alive'} + req_header = 'Authorization' + resp_header = 'www-authenticate' + + conn = HTTPSConnection(host=self.host, port=self.port) + + # Send negotiation message + headers[req_header] = ( + 'NTLM %s' % ntlm.create_NTLM_NEGOTIATE_MESSAGE(self.rawuser)) + log.debug('Request headers: %s', headers) + conn.request('GET', self.authurl, None, headers) + res = conn.getresponse() + reshdr = dict(res.getheaders()) + log.debug('Response status: %s %s', res.status, res.reason) + log.debug('Response headers: %s', reshdr) + log.debug('Response data: %s [...]', res.read(100)) + + # Remove the reference to the socket, so that it can not be closed by + # the response object (we want to keep the socket open) + res.fp = None + + # Server should respond with a challenge message + auth_header_values = reshdr[resp_header].split(', ') + auth_header_value = None + for s in auth_header_values: + if s[:5] == 'NTLM ': + auth_header_value = s[5:] + if auth_header_value is None: + raise Exception('Unexpected %s response header: %s' % + (resp_header, reshdr[resp_header])) + + # Send authentication message + ServerChallenge, NegotiateFlags = \ + ntlm.parse_NTLM_CHALLENGE_MESSAGE(auth_header_value) + auth_msg = ntlm.create_NTLM_AUTHENTICATE_MESSAGE(ServerChallenge, + self.user, + self.domain, + self.pw, + NegotiateFlags) + headers[req_header] = 'NTLM %s' % auth_msg + log.debug('Request headers: %s', headers) + conn.request('GET', self.authurl, None, headers) + res = conn.getresponse() + log.debug('Response status: %s %s', res.status, res.reason) + log.debug('Response headers: %s', dict(res.getheaders())) + log.debug('Response data: %s [...]', res.read()[:100]) + if res.status != 200: + if res.status == 401: + raise Exception('Server rejected request: wrong ' + 'username or password') + raise Exception('Wrong server response: %s %s' % + (res.status, res.reason)) + + res.fp = None + log.debug('Connection established') + return conn + + def urlopen(self, method, url, body=None, headers=None, retries=3, + redirect=True, assert_same_host=True): + if headers is None: + headers = {} + headers['Connection'] = 'Keep-Alive' + return super(NTLMConnectionPool, self).urlopen(method, url, body, + headers, retries, + redirect, + assert_same_host) diff --git a/venv2/lib/python2.7/site-packages/pip/_vendor/urllib3/contrib/pyopenssl.py b/venv2/lib/python2.7/site-packages/pip/_vendor/urllib3/contrib/pyopenssl.py new file mode 100644 index 0000000..363667c --- /dev/null +++ b/venv2/lib/python2.7/site-packages/pip/_vendor/urllib3/contrib/pyopenssl.py @@ -0,0 +1,466 @@ +""" +SSL with SNI_-support for Python 2. Follow these instructions if you would +like to verify SSL certificates in Python 2. Note, the default libraries do +*not* do certificate checking; you need to do additional work to validate +certificates yourself. + +This needs the following packages installed: + +* pyOpenSSL (tested with 16.0.0) +* cryptography (minimum 1.3.4, from pyopenssl) +* idna (minimum 2.0, from cryptography) + +However, pyopenssl depends on cryptography, which depends on idna, so while we +use all three directly here we end up having relatively few packages required. + +You can install them with the following command: + + pip install pyopenssl cryptography idna + +To activate certificate checking, call +:func:`~urllib3.contrib.pyopenssl.inject_into_urllib3` from your Python code +before you begin making HTTP requests. This can be done in a ``sitecustomize`` +module, or at any other time before your application begins using ``urllib3``, +like this:: + + try: + import urllib3.contrib.pyopenssl + urllib3.contrib.pyopenssl.inject_into_urllib3() + except ImportError: + pass + +Now you can use :mod:`urllib3` as you normally would, and it will support SNI +when the required modules are installed. + +Activating this module also has the positive side effect of disabling SSL/TLS +compression in Python 2 (see `CRIME attack`_). + +If you want to configure the default list of supported cipher suites, you can +set the ``urllib3.contrib.pyopenssl.DEFAULT_SSL_CIPHER_LIST`` variable. + +.. _sni: https://en.wikipedia.org/wiki/Server_Name_Indication +.. _crime attack: https://en.wikipedia.org/wiki/CRIME_(security_exploit) +""" +from __future__ import absolute_import + +import OpenSSL.SSL +from cryptography import x509 +from cryptography.hazmat.backends.openssl import backend as openssl_backend +from cryptography.hazmat.backends.openssl.x509 import _Certificate +try: + from cryptography.x509 import UnsupportedExtension +except ImportError: + # UnsupportedExtension is gone in cryptography >= 2.1.0 + class UnsupportedExtension(Exception): + pass + +from socket import timeout, error as SocketError +from io import BytesIO + +try: # Platform-specific: Python 2 + from socket import _fileobject +except ImportError: # Platform-specific: Python 3 + _fileobject = None + from ..packages.backports.makefile import backport_makefile + +import logging +import ssl +from ..packages import six +import sys + +from .. import util + +__all__ = ['inject_into_urllib3', 'extract_from_urllib3'] + +# SNI always works. +HAS_SNI = True + +# Map from urllib3 to PyOpenSSL compatible parameter-values. +_openssl_versions = { + ssl.PROTOCOL_SSLv23: OpenSSL.SSL.SSLv23_METHOD, + ssl.PROTOCOL_TLSv1: OpenSSL.SSL.TLSv1_METHOD, +} + +if hasattr(ssl, 'PROTOCOL_TLSv1_1') and hasattr(OpenSSL.SSL, 'TLSv1_1_METHOD'): + _openssl_versions[ssl.PROTOCOL_TLSv1_1] = OpenSSL.SSL.TLSv1_1_METHOD + +if hasattr(ssl, 'PROTOCOL_TLSv1_2') and hasattr(OpenSSL.SSL, 'TLSv1_2_METHOD'): + _openssl_versions[ssl.PROTOCOL_TLSv1_2] = OpenSSL.SSL.TLSv1_2_METHOD + +try: + _openssl_versions.update({ssl.PROTOCOL_SSLv3: OpenSSL.SSL.SSLv3_METHOD}) +except AttributeError: + pass + +_stdlib_to_openssl_verify = { + ssl.CERT_NONE: OpenSSL.SSL.VERIFY_NONE, + ssl.CERT_OPTIONAL: OpenSSL.SSL.VERIFY_PEER, + ssl.CERT_REQUIRED: + OpenSSL.SSL.VERIFY_PEER + OpenSSL.SSL.VERIFY_FAIL_IF_NO_PEER_CERT, +} +_openssl_to_stdlib_verify = dict( + (v, k) for k, v in _stdlib_to_openssl_verify.items() +) + +# OpenSSL will only write 16K at a time +SSL_WRITE_BLOCKSIZE = 16384 + +orig_util_HAS_SNI = util.HAS_SNI +orig_util_SSLContext = util.ssl_.SSLContext + + +log = logging.getLogger(__name__) + + +def inject_into_urllib3(): + 'Monkey-patch urllib3 with PyOpenSSL-backed SSL-support.' + + _validate_dependencies_met() + + util.ssl_.SSLContext = PyOpenSSLContext + util.HAS_SNI = HAS_SNI + util.ssl_.HAS_SNI = HAS_SNI + util.IS_PYOPENSSL = True + util.ssl_.IS_PYOPENSSL = True + + +def extract_from_urllib3(): + 'Undo monkey-patching by :func:`inject_into_urllib3`.' + + util.ssl_.SSLContext = orig_util_SSLContext + util.HAS_SNI = orig_util_HAS_SNI + util.ssl_.HAS_SNI = orig_util_HAS_SNI + util.IS_PYOPENSSL = False + util.ssl_.IS_PYOPENSSL = False + + +def _validate_dependencies_met(): + """ + Verifies that PyOpenSSL's package-level dependencies have been met. + Throws `ImportError` if they are not met. + """ + # Method added in `cryptography==1.1`; not available in older versions + from cryptography.x509.extensions import Extensions + if getattr(Extensions, "get_extension_for_class", None) is None: + raise ImportError("'cryptography' module missing required functionality. " + "Try upgrading to v1.3.4 or newer.") + + # pyOpenSSL 0.14 and above use cryptography for OpenSSL bindings. The _x509 + # attribute is only present on those versions. + from OpenSSL.crypto import X509 + x509 = X509() + if getattr(x509, "_x509", None) is None: + raise ImportError("'pyOpenSSL' module missing required functionality. " + "Try upgrading to v0.14 or newer.") + + +def _dnsname_to_stdlib(name): + """ + Converts a dNSName SubjectAlternativeName field to the form used by the + standard library on the given Python version. + + Cryptography produces a dNSName as a unicode string that was idna-decoded + from ASCII bytes. We need to idna-encode that string to get it back, and + then on Python 3 we also need to convert to unicode via UTF-8 (the stdlib + uses PyUnicode_FromStringAndSize on it, which decodes via UTF-8). + + If the name cannot be idna-encoded then we return None signalling that + the name given should be skipped. + """ + def idna_encode(name): + """ + Borrowed wholesale from the Python Cryptography Project. It turns out + that we can't just safely call `idna.encode`: it can explode for + wildcard names. This avoids that problem. + """ + from pip._vendor import idna + + try: + for prefix in [u'*.', u'.']: + if name.startswith(prefix): + name = name[len(prefix):] + return prefix.encode('ascii') + idna.encode(name) + return idna.encode(name) + except idna.core.IDNAError: + return None + + name = idna_encode(name) + if name is None: + return None + elif sys.version_info >= (3, 0): + name = name.decode('utf-8') + return name + + +def get_subj_alt_name(peer_cert): + """ + Given an PyOpenSSL certificate, provides all the subject alternative names. + """ + # Pass the cert to cryptography, which has much better APIs for this. + if hasattr(peer_cert, "to_cryptography"): + cert = peer_cert.to_cryptography() + else: + # This is technically using private APIs, but should work across all + # relevant versions before PyOpenSSL got a proper API for this. + cert = _Certificate(openssl_backend, peer_cert._x509) + + # We want to find the SAN extension. Ask Cryptography to locate it (it's + # faster than looping in Python) + try: + ext = cert.extensions.get_extension_for_class( + x509.SubjectAlternativeName + ).value + except x509.ExtensionNotFound: + # No such extension, return the empty list. + return [] + except (x509.DuplicateExtension, UnsupportedExtension, + x509.UnsupportedGeneralNameType, UnicodeError) as e: + # A problem has been found with the quality of the certificate. Assume + # no SAN field is present. + log.warning( + "A problem was encountered with the certificate that prevented " + "urllib3 from finding the SubjectAlternativeName field. This can " + "affect certificate validation. The error was %s", + e, + ) + return [] + + # We want to return dNSName and iPAddress fields. We need to cast the IPs + # back to strings because the match_hostname function wants them as + # strings. + # Sadly the DNS names need to be idna encoded and then, on Python 3, UTF-8 + # decoded. This is pretty frustrating, but that's what the standard library + # does with certificates, and so we need to attempt to do the same. + # We also want to skip over names which cannot be idna encoded. + names = [ + ('DNS', name) for name in map(_dnsname_to_stdlib, ext.get_values_for_type(x509.DNSName)) + if name is not None + ] + names.extend( + ('IP Address', str(name)) + for name in ext.get_values_for_type(x509.IPAddress) + ) + + return names + + +class WrappedSocket(object): + '''API-compatibility wrapper for Python OpenSSL's Connection-class. + + Note: _makefile_refs, _drop() and _reuse() are needed for the garbage + collector of pypy. + ''' + + def __init__(self, connection, socket, suppress_ragged_eofs=True): + self.connection = connection + self.socket = socket + self.suppress_ragged_eofs = suppress_ragged_eofs + self._makefile_refs = 0 + self._closed = False + + def fileno(self): + return self.socket.fileno() + + # Copy-pasted from Python 3.5 source code + def _decref_socketios(self): + if self._makefile_refs > 0: + self._makefile_refs -= 1 + if self._closed: + self.close() + + def recv(self, *args, **kwargs): + try: + data = self.connection.recv(*args, **kwargs) + except OpenSSL.SSL.SysCallError as e: + if self.suppress_ragged_eofs and e.args == (-1, 'Unexpected EOF'): + return b'' + else: + raise SocketError(str(e)) + except OpenSSL.SSL.ZeroReturnError as e: + if self.connection.get_shutdown() == OpenSSL.SSL.RECEIVED_SHUTDOWN: + return b'' + else: + raise + except OpenSSL.SSL.WantReadError: + if not util.wait_for_read(self.socket, self.socket.gettimeout()): + raise timeout('The read operation timed out') + else: + return self.recv(*args, **kwargs) + else: + return data + + def recv_into(self, *args, **kwargs): + try: + return self.connection.recv_into(*args, **kwargs) + except OpenSSL.SSL.SysCallError as e: + if self.suppress_ragged_eofs and e.args == (-1, 'Unexpected EOF'): + return 0 + else: + raise SocketError(str(e)) + except OpenSSL.SSL.ZeroReturnError as e: + if self.connection.get_shutdown() == OpenSSL.SSL.RECEIVED_SHUTDOWN: + return 0 + else: + raise + except OpenSSL.SSL.WantReadError: + if not util.wait_for_read(self.socket, self.socket.gettimeout()): + raise timeout('The read operation timed out') + else: + return self.recv_into(*args, **kwargs) + + def settimeout(self, timeout): + return self.socket.settimeout(timeout) + + def _send_until_done(self, data): + while True: + try: + return self.connection.send(data) + except OpenSSL.SSL.WantWriteError: + if not util.wait_for_write(self.socket, self.socket.gettimeout()): + raise timeout() + continue + except OpenSSL.SSL.SysCallError as e: + raise SocketError(str(e)) + + def sendall(self, data): + total_sent = 0 + while total_sent < len(data): + sent = self._send_until_done(data[total_sent:total_sent + SSL_WRITE_BLOCKSIZE]) + total_sent += sent + + def shutdown(self): + # FIXME rethrow compatible exceptions should we ever use this + self.connection.shutdown() + + def close(self): + if self._makefile_refs < 1: + try: + self._closed = True + return self.connection.close() + except OpenSSL.SSL.Error: + return + else: + self._makefile_refs -= 1 + + def getpeercert(self, binary_form=False): + x509 = self.connection.get_peer_certificate() + + if not x509: + return x509 + + if binary_form: + return OpenSSL.crypto.dump_certificate( + OpenSSL.crypto.FILETYPE_ASN1, + x509) + + return { + 'subject': ( + (('commonName', x509.get_subject().CN),), + ), + 'subjectAltName': get_subj_alt_name(x509) + } + + def _reuse(self): + self._makefile_refs += 1 + + def _drop(self): + if self._makefile_refs < 1: + self.close() + else: + self._makefile_refs -= 1 + + +if _fileobject: # Platform-specific: Python 2 + def makefile(self, mode, bufsize=-1): + self._makefile_refs += 1 + return _fileobject(self, mode, bufsize, close=True) +else: # Platform-specific: Python 3 + makefile = backport_makefile + +WrappedSocket.makefile = makefile + + +class PyOpenSSLContext(object): + """ + I am a wrapper class for the PyOpenSSL ``Context`` object. I am responsible + for translating the interface of the standard library ``SSLContext`` object + to calls into PyOpenSSL. + """ + def __init__(self, protocol): + self.protocol = _openssl_versions[protocol] + self._ctx = OpenSSL.SSL.Context(self.protocol) + self._options = 0 + self.check_hostname = False + + @property + def options(self): + return self._options + + @options.setter + def options(self, value): + self._options = value + self._ctx.set_options(value) + + @property + def verify_mode(self): + return _openssl_to_stdlib_verify[self._ctx.get_verify_mode()] + + @verify_mode.setter + def verify_mode(self, value): + self._ctx.set_verify( + _stdlib_to_openssl_verify[value], + _verify_callback + ) + + def set_default_verify_paths(self): + self._ctx.set_default_verify_paths() + + def set_ciphers(self, ciphers): + if isinstance(ciphers, six.text_type): + ciphers = ciphers.encode('utf-8') + self._ctx.set_cipher_list(ciphers) + + def load_verify_locations(self, cafile=None, capath=None, cadata=None): + if cafile is not None: + cafile = cafile.encode('utf-8') + if capath is not None: + capath = capath.encode('utf-8') + self._ctx.load_verify_locations(cafile, capath) + if cadata is not None: + self._ctx.load_verify_locations(BytesIO(cadata)) + + def load_cert_chain(self, certfile, keyfile=None, password=None): + self._ctx.use_certificate_chain_file(certfile) + if password is not None: + self._ctx.set_passwd_cb(lambda max_length, prompt_twice, userdata: password) + self._ctx.use_privatekey_file(keyfile or certfile) + + def wrap_socket(self, sock, server_side=False, + do_handshake_on_connect=True, suppress_ragged_eofs=True, + server_hostname=None): + cnx = OpenSSL.SSL.Connection(self._ctx, sock) + + if isinstance(server_hostname, six.text_type): # Platform-specific: Python 3 + server_hostname = server_hostname.encode('utf-8') + + if server_hostname is not None: + cnx.set_tlsext_host_name(server_hostname) + + cnx.set_connect_state() + + while True: + try: + cnx.do_handshake() + except OpenSSL.SSL.WantReadError: + if not util.wait_for_read(sock, sock.gettimeout()): + raise timeout('select timed out') + continue + except OpenSSL.SSL.Error as e: + raise ssl.SSLError('bad handshake: %r' % e) + break + + return WrappedSocket(cnx, sock) + + +def _verify_callback(cnx, x509, err_no, err_depth, return_code): + return err_no == 0 diff --git a/venv2/lib/python2.7/site-packages/pip/_vendor/urllib3/contrib/securetransport.py b/venv2/lib/python2.7/site-packages/pip/_vendor/urllib3/contrib/securetransport.py new file mode 100644 index 0000000..77cb59e --- /dev/null +++ b/venv2/lib/python2.7/site-packages/pip/_vendor/urllib3/contrib/securetransport.py @@ -0,0 +1,804 @@ +""" +SecureTranport support for urllib3 via ctypes. + +This makes platform-native TLS available to urllib3 users on macOS without the +use of a compiler. This is an important feature because the Python Package +Index is moving to become a TLSv1.2-or-higher server, and the default OpenSSL +that ships with macOS is not capable of doing TLSv1.2. The only way to resolve +this is to give macOS users an alternative solution to the problem, and that +solution is to use SecureTransport. + +We use ctypes here because this solution must not require a compiler. That's +because pip is not allowed to require a compiler either. + +This is not intended to be a seriously long-term solution to this problem. +The hope is that PEP 543 will eventually solve this issue for us, at which +point we can retire this contrib module. But in the short term, we need to +solve the impending tire fire that is Python on Mac without this kind of +contrib module. So...here we are. + +To use this module, simply import and inject it:: + + import urllib3.contrib.securetransport + urllib3.contrib.securetransport.inject_into_urllib3() + +Happy TLSing! +""" +from __future__ import absolute_import + +import contextlib +import ctypes +import errno +import os.path +import shutil +import socket +import ssl +import threading +import weakref + +from .. import util +from ._securetransport.bindings import ( + Security, SecurityConst, CoreFoundation +) +from ._securetransport.low_level import ( + _assert_no_error, _cert_array_from_pem, _temporary_keychain, + _load_client_cert_chain +) + +try: # Platform-specific: Python 2 + from socket import _fileobject +except ImportError: # Platform-specific: Python 3 + _fileobject = None + from ..packages.backports.makefile import backport_makefile + +__all__ = ['inject_into_urllib3', 'extract_from_urllib3'] + +# SNI always works +HAS_SNI = True + +orig_util_HAS_SNI = util.HAS_SNI +orig_util_SSLContext = util.ssl_.SSLContext + +# This dictionary is used by the read callback to obtain a handle to the +# calling wrapped socket. This is a pretty silly approach, but for now it'll +# do. I feel like I should be able to smuggle a handle to the wrapped socket +# directly in the SSLConnectionRef, but for now this approach will work I +# guess. +# +# We need to lock around this structure for inserts, but we don't do it for +# reads/writes in the callbacks. The reasoning here goes as follows: +# +# 1. It is not possible to call into the callbacks before the dictionary is +# populated, so once in the callback the id must be in the dictionary. +# 2. The callbacks don't mutate the dictionary, they only read from it, and +# so cannot conflict with any of the insertions. +# +# This is good: if we had to lock in the callbacks we'd drastically slow down +# the performance of this code. +_connection_refs = weakref.WeakValueDictionary() +_connection_ref_lock = threading.Lock() + +# Limit writes to 16kB. This is OpenSSL's limit, but we'll cargo-cult it over +# for no better reason than we need *a* limit, and this one is right there. +SSL_WRITE_BLOCKSIZE = 16384 + +# This is our equivalent of util.ssl_.DEFAULT_CIPHERS, but expanded out to +# individual cipher suites. We need to do this because this is how +# SecureTransport wants them. +CIPHER_SUITES = [ + SecurityConst.TLS_AES_256_GCM_SHA384, + SecurityConst.TLS_CHACHA20_POLY1305_SHA256, + SecurityConst.TLS_AES_128_GCM_SHA256, + SecurityConst.TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384, + SecurityConst.TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384, + SecurityConst.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, + SecurityConst.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256, + SecurityConst.TLS_DHE_DSS_WITH_AES_256_GCM_SHA384, + SecurityConst.TLS_DHE_RSA_WITH_AES_256_GCM_SHA384, + SecurityConst.TLS_DHE_DSS_WITH_AES_128_GCM_SHA256, + SecurityConst.TLS_DHE_RSA_WITH_AES_128_GCM_SHA256, + SecurityConst.TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384, + SecurityConst.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384, + SecurityConst.TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA, + SecurityConst.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA, + SecurityConst.TLS_DHE_RSA_WITH_AES_256_CBC_SHA256, + SecurityConst.TLS_DHE_DSS_WITH_AES_256_CBC_SHA256, + SecurityConst.TLS_DHE_RSA_WITH_AES_256_CBC_SHA, + SecurityConst.TLS_DHE_DSS_WITH_AES_256_CBC_SHA, + SecurityConst.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256, + SecurityConst.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256, + SecurityConst.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA, + SecurityConst.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA, + SecurityConst.TLS_DHE_RSA_WITH_AES_128_CBC_SHA256, + SecurityConst.TLS_DHE_DSS_WITH_AES_128_CBC_SHA256, + SecurityConst.TLS_DHE_RSA_WITH_AES_128_CBC_SHA, + SecurityConst.TLS_DHE_DSS_WITH_AES_128_CBC_SHA, + SecurityConst.TLS_RSA_WITH_AES_256_GCM_SHA384, + SecurityConst.TLS_RSA_WITH_AES_128_GCM_SHA256, + SecurityConst.TLS_RSA_WITH_AES_256_CBC_SHA256, + SecurityConst.TLS_RSA_WITH_AES_128_CBC_SHA256, + SecurityConst.TLS_RSA_WITH_AES_256_CBC_SHA, + SecurityConst.TLS_RSA_WITH_AES_128_CBC_SHA, +] + +# Basically this is simple: for PROTOCOL_SSLv23 we turn it into a low of +# TLSv1 and a high of TLSv1.2. For everything else, we pin to that version. +_protocol_to_min_max = { + ssl.PROTOCOL_SSLv23: (SecurityConst.kTLSProtocol1, SecurityConst.kTLSProtocol12), +} + +if hasattr(ssl, "PROTOCOL_SSLv2"): + _protocol_to_min_max[ssl.PROTOCOL_SSLv2] = ( + SecurityConst.kSSLProtocol2, SecurityConst.kSSLProtocol2 + ) +if hasattr(ssl, "PROTOCOL_SSLv3"): + _protocol_to_min_max[ssl.PROTOCOL_SSLv3] = ( + SecurityConst.kSSLProtocol3, SecurityConst.kSSLProtocol3 + ) +if hasattr(ssl, "PROTOCOL_TLSv1"): + _protocol_to_min_max[ssl.PROTOCOL_TLSv1] = ( + SecurityConst.kTLSProtocol1, SecurityConst.kTLSProtocol1 + ) +if hasattr(ssl, "PROTOCOL_TLSv1_1"): + _protocol_to_min_max[ssl.PROTOCOL_TLSv1_1] = ( + SecurityConst.kTLSProtocol11, SecurityConst.kTLSProtocol11 + ) +if hasattr(ssl, "PROTOCOL_TLSv1_2"): + _protocol_to_min_max[ssl.PROTOCOL_TLSv1_2] = ( + SecurityConst.kTLSProtocol12, SecurityConst.kTLSProtocol12 + ) +if hasattr(ssl, "PROTOCOL_TLS"): + _protocol_to_min_max[ssl.PROTOCOL_TLS] = _protocol_to_min_max[ssl.PROTOCOL_SSLv23] + + +def inject_into_urllib3(): + """ + Monkey-patch urllib3 with SecureTransport-backed SSL-support. + """ + util.ssl_.SSLContext = SecureTransportContext + util.HAS_SNI = HAS_SNI + util.ssl_.HAS_SNI = HAS_SNI + util.IS_SECURETRANSPORT = True + util.ssl_.IS_SECURETRANSPORT = True + + +def extract_from_urllib3(): + """ + Undo monkey-patching by :func:`inject_into_urllib3`. + """ + util.ssl_.SSLContext = orig_util_SSLContext + util.HAS_SNI = orig_util_HAS_SNI + util.ssl_.HAS_SNI = orig_util_HAS_SNI + util.IS_SECURETRANSPORT = False + util.ssl_.IS_SECURETRANSPORT = False + + +def _read_callback(connection_id, data_buffer, data_length_pointer): + """ + SecureTransport read callback. This is called by ST to request that data + be returned from the socket. + """ + wrapped_socket = None + try: + wrapped_socket = _connection_refs.get(connection_id) + if wrapped_socket is None: + return SecurityConst.errSSLInternal + base_socket = wrapped_socket.socket + + requested_length = data_length_pointer[0] + + timeout = wrapped_socket.gettimeout() + error = None + read_count = 0 + + try: + while read_count < requested_length: + if timeout is None or timeout >= 0: + if not util.wait_for_read(base_socket, timeout): + raise socket.error(errno.EAGAIN, 'timed out') + + remaining = requested_length - read_count + buffer = (ctypes.c_char * remaining).from_address( + data_buffer + read_count + ) + chunk_size = base_socket.recv_into(buffer, remaining) + read_count += chunk_size + if not chunk_size: + if not read_count: + return SecurityConst.errSSLClosedGraceful + break + except (socket.error) as e: + error = e.errno + + if error is not None and error != errno.EAGAIN: + data_length_pointer[0] = read_count + if error == errno.ECONNRESET or error == errno.EPIPE: + return SecurityConst.errSSLClosedAbort + raise + + data_length_pointer[0] = read_count + + if read_count != requested_length: + return SecurityConst.errSSLWouldBlock + + return 0 + except Exception as e: + if wrapped_socket is not None: + wrapped_socket._exception = e + return SecurityConst.errSSLInternal + + +def _write_callback(connection_id, data_buffer, data_length_pointer): + """ + SecureTransport write callback. This is called by ST to request that data + actually be sent on the network. + """ + wrapped_socket = None + try: + wrapped_socket = _connection_refs.get(connection_id) + if wrapped_socket is None: + return SecurityConst.errSSLInternal + base_socket = wrapped_socket.socket + + bytes_to_write = data_length_pointer[0] + data = ctypes.string_at(data_buffer, bytes_to_write) + + timeout = wrapped_socket.gettimeout() + error = None + sent = 0 + + try: + while sent < bytes_to_write: + if timeout is None or timeout >= 0: + if not util.wait_for_write(base_socket, timeout): + raise socket.error(errno.EAGAIN, 'timed out') + chunk_sent = base_socket.send(data) + sent += chunk_sent + + # This has some needless copying here, but I'm not sure there's + # much value in optimising this data path. + data = data[chunk_sent:] + except (socket.error) as e: + error = e.errno + + if error is not None and error != errno.EAGAIN: + data_length_pointer[0] = sent + if error == errno.ECONNRESET or error == errno.EPIPE: + return SecurityConst.errSSLClosedAbort + raise + + data_length_pointer[0] = sent + + if sent != bytes_to_write: + return SecurityConst.errSSLWouldBlock + + return 0 + except Exception as e: + if wrapped_socket is not None: + wrapped_socket._exception = e + return SecurityConst.errSSLInternal + + +# We need to keep these two objects references alive: if they get GC'd while +# in use then SecureTransport could attempt to call a function that is in freed +# memory. That would be...uh...bad. Yeah, that's the word. Bad. +_read_callback_pointer = Security.SSLReadFunc(_read_callback) +_write_callback_pointer = Security.SSLWriteFunc(_write_callback) + + +class WrappedSocket(object): + """ + API-compatibility wrapper for Python's OpenSSL wrapped socket object. + + Note: _makefile_refs, _drop(), and _reuse() are needed for the garbage + collector of PyPy. + """ + def __init__(self, socket): + self.socket = socket + self.context = None + self._makefile_refs = 0 + self._closed = False + self._exception = None + self._keychain = None + self._keychain_dir = None + self._client_cert_chain = None + + # We save off the previously-configured timeout and then set it to + # zero. This is done because we use select and friends to handle the + # timeouts, but if we leave the timeout set on the lower socket then + # Python will "kindly" call select on that socket again for us. Avoid + # that by forcing the timeout to zero. + self._timeout = self.socket.gettimeout() + self.socket.settimeout(0) + + @contextlib.contextmanager + def _raise_on_error(self): + """ + A context manager that can be used to wrap calls that do I/O from + SecureTransport. If any of the I/O callbacks hit an exception, this + context manager will correctly propagate the exception after the fact. + This avoids silently swallowing those exceptions. + + It also correctly forces the socket closed. + """ + self._exception = None + + # We explicitly don't catch around this yield because in the unlikely + # event that an exception was hit in the block we don't want to swallow + # it. + yield + if self._exception is not None: + exception, self._exception = self._exception, None + self.close() + raise exception + + def _set_ciphers(self): + """ + Sets up the allowed ciphers. By default this matches the set in + util.ssl_.DEFAULT_CIPHERS, at least as supported by macOS. This is done + custom and doesn't allow changing at this time, mostly because parsing + OpenSSL cipher strings is going to be a freaking nightmare. + """ + ciphers = (Security.SSLCipherSuite * len(CIPHER_SUITES))(*CIPHER_SUITES) + result = Security.SSLSetEnabledCiphers( + self.context, ciphers, len(CIPHER_SUITES) + ) + _assert_no_error(result) + + def _custom_validate(self, verify, trust_bundle): + """ + Called when we have set custom validation. We do this in two cases: + first, when cert validation is entirely disabled; and second, when + using a custom trust DB. + """ + # If we disabled cert validation, just say: cool. + if not verify: + return + + # We want data in memory, so load it up. + if os.path.isfile(trust_bundle): + with open(trust_bundle, 'rb') as f: + trust_bundle = f.read() + + cert_array = None + trust = Security.SecTrustRef() + + try: + # Get a CFArray that contains the certs we want. + cert_array = _cert_array_from_pem(trust_bundle) + + # Ok, now the hard part. We want to get the SecTrustRef that ST has + # created for this connection, shove our CAs into it, tell ST to + # ignore everything else it knows, and then ask if it can build a + # chain. This is a buuuunch of code. + result = Security.SSLCopyPeerTrust( + self.context, ctypes.byref(trust) + ) + _assert_no_error(result) + if not trust: + raise ssl.SSLError("Failed to copy trust reference") + + result = Security.SecTrustSetAnchorCertificates(trust, cert_array) + _assert_no_error(result) + + result = Security.SecTrustSetAnchorCertificatesOnly(trust, True) + _assert_no_error(result) + + trust_result = Security.SecTrustResultType() + result = Security.SecTrustEvaluate( + trust, ctypes.byref(trust_result) + ) + _assert_no_error(result) + finally: + if trust: + CoreFoundation.CFRelease(trust) + + if cert_array is not None: + CoreFoundation.CFRelease(cert_array) + + # Ok, now we can look at what the result was. + successes = ( + SecurityConst.kSecTrustResultUnspecified, + SecurityConst.kSecTrustResultProceed + ) + if trust_result.value not in successes: + raise ssl.SSLError( + "certificate verify failed, error code: %d" % + trust_result.value + ) + + def handshake(self, + server_hostname, + verify, + trust_bundle, + min_version, + max_version, + client_cert, + client_key, + client_key_passphrase): + """ + Actually performs the TLS handshake. This is run automatically by + wrapped socket, and shouldn't be needed in user code. + """ + # First, we do the initial bits of connection setup. We need to create + # a context, set its I/O funcs, and set the connection reference. + self.context = Security.SSLCreateContext( + None, SecurityConst.kSSLClientSide, SecurityConst.kSSLStreamType + ) + result = Security.SSLSetIOFuncs( + self.context, _read_callback_pointer, _write_callback_pointer + ) + _assert_no_error(result) + + # Here we need to compute the handle to use. We do this by taking the + # id of self modulo 2**31 - 1. If this is already in the dictionary, we + # just keep incrementing by one until we find a free space. + with _connection_ref_lock: + handle = id(self) % 2147483647 + while handle in _connection_refs: + handle = (handle + 1) % 2147483647 + _connection_refs[handle] = self + + result = Security.SSLSetConnection(self.context, handle) + _assert_no_error(result) + + # If we have a server hostname, we should set that too. + if server_hostname: + if not isinstance(server_hostname, bytes): + server_hostname = server_hostname.encode('utf-8') + + result = Security.SSLSetPeerDomainName( + self.context, server_hostname, len(server_hostname) + ) + _assert_no_error(result) + + # Setup the ciphers. + self._set_ciphers() + + # Set the minimum and maximum TLS versions. + result = Security.SSLSetProtocolVersionMin(self.context, min_version) + _assert_no_error(result) + result = Security.SSLSetProtocolVersionMax(self.context, max_version) + _assert_no_error(result) + + # If there's a trust DB, we need to use it. We do that by telling + # SecureTransport to break on server auth. We also do that if we don't + # want to validate the certs at all: we just won't actually do any + # authing in that case. + if not verify or trust_bundle is not None: + result = Security.SSLSetSessionOption( + self.context, + SecurityConst.kSSLSessionOptionBreakOnServerAuth, + True + ) + _assert_no_error(result) + + # If there's a client cert, we need to use it. + if client_cert: + self._keychain, self._keychain_dir = _temporary_keychain() + self._client_cert_chain = _load_client_cert_chain( + self._keychain, client_cert, client_key + ) + result = Security.SSLSetCertificate( + self.context, self._client_cert_chain + ) + _assert_no_error(result) + + while True: + with self._raise_on_error(): + result = Security.SSLHandshake(self.context) + + if result == SecurityConst.errSSLWouldBlock: + raise socket.timeout("handshake timed out") + elif result == SecurityConst.errSSLServerAuthCompleted: + self._custom_validate(verify, trust_bundle) + continue + else: + _assert_no_error(result) + break + + def fileno(self): + return self.socket.fileno() + + # Copy-pasted from Python 3.5 source code + def _decref_socketios(self): + if self._makefile_refs > 0: + self._makefile_refs -= 1 + if self._closed: + self.close() + + def recv(self, bufsiz): + buffer = ctypes.create_string_buffer(bufsiz) + bytes_read = self.recv_into(buffer, bufsiz) + data = buffer[:bytes_read] + return data + + def recv_into(self, buffer, nbytes=None): + # Read short on EOF. + if self._closed: + return 0 + + if nbytes is None: + nbytes = len(buffer) + + buffer = (ctypes.c_char * nbytes).from_buffer(buffer) + processed_bytes = ctypes.c_size_t(0) + + with self._raise_on_error(): + result = Security.SSLRead( + self.context, buffer, nbytes, ctypes.byref(processed_bytes) + ) + + # There are some result codes that we want to treat as "not always + # errors". Specifically, those are errSSLWouldBlock, + # errSSLClosedGraceful, and errSSLClosedNoNotify. + if (result == SecurityConst.errSSLWouldBlock): + # If we didn't process any bytes, then this was just a time out. + # However, we can get errSSLWouldBlock in situations when we *did* + # read some data, and in those cases we should just read "short" + # and return. + if processed_bytes.value == 0: + # Timed out, no data read. + raise socket.timeout("recv timed out") + elif result in (SecurityConst.errSSLClosedGraceful, SecurityConst.errSSLClosedNoNotify): + # The remote peer has closed this connection. We should do so as + # well. Note that we don't actually return here because in + # principle this could actually be fired along with return data. + # It's unlikely though. + self.close() + else: + _assert_no_error(result) + + # Ok, we read and probably succeeded. We should return whatever data + # was actually read. + return processed_bytes.value + + def settimeout(self, timeout): + self._timeout = timeout + + def gettimeout(self): + return self._timeout + + def send(self, data): + processed_bytes = ctypes.c_size_t(0) + + with self._raise_on_error(): + result = Security.SSLWrite( + self.context, data, len(data), ctypes.byref(processed_bytes) + ) + + if result == SecurityConst.errSSLWouldBlock and processed_bytes.value == 0: + # Timed out + raise socket.timeout("send timed out") + else: + _assert_no_error(result) + + # We sent, and probably succeeded. Tell them how much we sent. + return processed_bytes.value + + def sendall(self, data): + total_sent = 0 + while total_sent < len(data): + sent = self.send(data[total_sent:total_sent + SSL_WRITE_BLOCKSIZE]) + total_sent += sent + + def shutdown(self): + with self._raise_on_error(): + Security.SSLClose(self.context) + + def close(self): + # TODO: should I do clean shutdown here? Do I have to? + if self._makefile_refs < 1: + self._closed = True + if self.context: + CoreFoundation.CFRelease(self.context) + self.context = None + if self._client_cert_chain: + CoreFoundation.CFRelease(self._client_cert_chain) + self._client_cert_chain = None + if self._keychain: + Security.SecKeychainDelete(self._keychain) + CoreFoundation.CFRelease(self._keychain) + shutil.rmtree(self._keychain_dir) + self._keychain = self._keychain_dir = None + return self.socket.close() + else: + self._makefile_refs -= 1 + + def getpeercert(self, binary_form=False): + # Urgh, annoying. + # + # Here's how we do this: + # + # 1. Call SSLCopyPeerTrust to get hold of the trust object for this + # connection. + # 2. Call SecTrustGetCertificateAtIndex for index 0 to get the leaf. + # 3. To get the CN, call SecCertificateCopyCommonName and process that + # string so that it's of the appropriate type. + # 4. To get the SAN, we need to do something a bit more complex: + # a. Call SecCertificateCopyValues to get the data, requesting + # kSecOIDSubjectAltName. + # b. Mess about with this dictionary to try to get the SANs out. + # + # This is gross. Really gross. It's going to be a few hundred LoC extra + # just to repeat something that SecureTransport can *already do*. So my + # operating assumption at this time is that what we want to do is + # instead to just flag to urllib3 that it shouldn't do its own hostname + # validation when using SecureTransport. + if not binary_form: + raise ValueError( + "SecureTransport only supports dumping binary certs" + ) + trust = Security.SecTrustRef() + certdata = None + der_bytes = None + + try: + # Grab the trust store. + result = Security.SSLCopyPeerTrust( + self.context, ctypes.byref(trust) + ) + _assert_no_error(result) + if not trust: + # Probably we haven't done the handshake yet. No biggie. + return None + + cert_count = Security.SecTrustGetCertificateCount(trust) + if not cert_count: + # Also a case that might happen if we haven't handshaked. + # Handshook? Handshaken? + return None + + leaf = Security.SecTrustGetCertificateAtIndex(trust, 0) + assert leaf + + # Ok, now we want the DER bytes. + certdata = Security.SecCertificateCopyData(leaf) + assert certdata + + data_length = CoreFoundation.CFDataGetLength(certdata) + data_buffer = CoreFoundation.CFDataGetBytePtr(certdata) + der_bytes = ctypes.string_at(data_buffer, data_length) + finally: + if certdata: + CoreFoundation.CFRelease(certdata) + if trust: + CoreFoundation.CFRelease(trust) + + return der_bytes + + def _reuse(self): + self._makefile_refs += 1 + + def _drop(self): + if self._makefile_refs < 1: + self.close() + else: + self._makefile_refs -= 1 + + +if _fileobject: # Platform-specific: Python 2 + def makefile(self, mode, bufsize=-1): + self._makefile_refs += 1 + return _fileobject(self, mode, bufsize, close=True) +else: # Platform-specific: Python 3 + def makefile(self, mode="r", buffering=None, *args, **kwargs): + # We disable buffering with SecureTransport because it conflicts with + # the buffering that ST does internally (see issue #1153 for more). + buffering = 0 + return backport_makefile(self, mode, buffering, *args, **kwargs) + +WrappedSocket.makefile = makefile + + +class SecureTransportContext(object): + """ + I am a wrapper class for the SecureTransport library, to translate the + interface of the standard library ``SSLContext`` object to calls into + SecureTransport. + """ + def __init__(self, protocol): + self._min_version, self._max_version = _protocol_to_min_max[protocol] + self._options = 0 + self._verify = False + self._trust_bundle = None + self._client_cert = None + self._client_key = None + self._client_key_passphrase = None + + @property + def check_hostname(self): + """ + SecureTransport cannot have its hostname checking disabled. For more, + see the comment on getpeercert() in this file. + """ + return True + + @check_hostname.setter + def check_hostname(self, value): + """ + SecureTransport cannot have its hostname checking disabled. For more, + see the comment on getpeercert() in this file. + """ + pass + + @property + def options(self): + # TODO: Well, crap. + # + # So this is the bit of the code that is the most likely to cause us + # trouble. Essentially we need to enumerate all of the SSL options that + # users might want to use and try to see if we can sensibly translate + # them, or whether we should just ignore them. + return self._options + + @options.setter + def options(self, value): + # TODO: Update in line with above. + self._options = value + + @property + def verify_mode(self): + return ssl.CERT_REQUIRED if self._verify else ssl.CERT_NONE + + @verify_mode.setter + def verify_mode(self, value): + self._verify = True if value == ssl.CERT_REQUIRED else False + + def set_default_verify_paths(self): + # So, this has to do something a bit weird. Specifically, what it does + # is nothing. + # + # This means that, if we had previously had load_verify_locations + # called, this does not undo that. We need to do that because it turns + # out that the rest of the urllib3 code will attempt to load the + # default verify paths if it hasn't been told about any paths, even if + # the context itself was sometime earlier. We resolve that by just + # ignoring it. + pass + + def load_default_certs(self): + return self.set_default_verify_paths() + + def set_ciphers(self, ciphers): + # For now, we just require the default cipher string. + if ciphers != util.ssl_.DEFAULT_CIPHERS: + raise ValueError( + "SecureTransport doesn't support custom cipher strings" + ) + + def load_verify_locations(self, cafile=None, capath=None, cadata=None): + # OK, we only really support cadata and cafile. + if capath is not None: + raise ValueError( + "SecureTransport does not support cert directories" + ) + + self._trust_bundle = cafile or cadata + + def load_cert_chain(self, certfile, keyfile=None, password=None): + self._client_cert = certfile + self._client_key = keyfile + self._client_cert_passphrase = password + + def wrap_socket(self, sock, server_side=False, + do_handshake_on_connect=True, suppress_ragged_eofs=True, + server_hostname=None): + # So, what do we do here? Firstly, we assert some properties. This is a + # stripped down shim, so there is some functionality we don't support. + # See PEP 543 for the real deal. + assert not server_side + assert do_handshake_on_connect + assert suppress_ragged_eofs + + # Ok, we're good to go. Now we want to create the wrapped socket object + # and store it in the appropriate place. + wrapped_socket = WrappedSocket(sock) + + # Now we can handshake + wrapped_socket.handshake( + server_hostname, self._verify, self._trust_bundle, + self._min_version, self._max_version, self._client_cert, + self._client_key, self._client_key_passphrase + ) + return wrapped_socket diff --git a/venv2/lib/python2.7/site-packages/pip/_vendor/urllib3/contrib/socks.py b/venv2/lib/python2.7/site-packages/pip/_vendor/urllib3/contrib/socks.py new file mode 100644 index 0000000..811e312 --- /dev/null +++ b/venv2/lib/python2.7/site-packages/pip/_vendor/urllib3/contrib/socks.py @@ -0,0 +1,192 @@ +# -*- coding: utf-8 -*- +""" +This module contains provisional support for SOCKS proxies from within +urllib3. This module supports SOCKS4 (specifically the SOCKS4A variant) and +SOCKS5. To enable its functionality, either install PySocks or install this +module with the ``socks`` extra. + +The SOCKS implementation supports the full range of urllib3 features. It also +supports the following SOCKS features: + +- SOCKS4 +- SOCKS4a +- SOCKS5 +- Usernames and passwords for the SOCKS proxy + +Known Limitations: + +- Currently PySocks does not support contacting remote websites via literal + IPv6 addresses. Any such connection attempt will fail. You must use a domain + name. +- Currently PySocks does not support IPv6 connections to the SOCKS proxy. Any + such connection attempt will fail. +""" +from __future__ import absolute_import + +try: + import socks +except ImportError: + import warnings + from ..exceptions import DependencyWarning + + warnings.warn(( + 'SOCKS support in urllib3 requires the installation of optional ' + 'dependencies: specifically, PySocks. For more information, see ' + 'https://urllib3.readthedocs.io/en/latest/contrib.html#socks-proxies' + ), + DependencyWarning + ) + raise + +from socket import error as SocketError, timeout as SocketTimeout + +from ..connection import ( + HTTPConnection, HTTPSConnection +) +from ..connectionpool import ( + HTTPConnectionPool, HTTPSConnectionPool +) +from ..exceptions import ConnectTimeoutError, NewConnectionError +from ..poolmanager import PoolManager +from ..util.url import parse_url + +try: + import ssl +except ImportError: + ssl = None + + +class SOCKSConnection(HTTPConnection): + """ + A plain-text HTTP connection that connects via a SOCKS proxy. + """ + def __init__(self, *args, **kwargs): + self._socks_options = kwargs.pop('_socks_options') + super(SOCKSConnection, self).__init__(*args, **kwargs) + + def _new_conn(self): + """ + Establish a new connection via the SOCKS proxy. + """ + extra_kw = {} + if self.source_address: + extra_kw['source_address'] = self.source_address + + if self.socket_options: + extra_kw['socket_options'] = self.socket_options + + try: + conn = socks.create_connection( + (self.host, self.port), + proxy_type=self._socks_options['socks_version'], + proxy_addr=self._socks_options['proxy_host'], + proxy_port=self._socks_options['proxy_port'], + proxy_username=self._socks_options['username'], + proxy_password=self._socks_options['password'], + proxy_rdns=self._socks_options['rdns'], + timeout=self.timeout, + **extra_kw + ) + + except SocketTimeout as e: + raise ConnectTimeoutError( + self, "Connection to %s timed out. (connect timeout=%s)" % + (self.host, self.timeout)) + + except socks.ProxyError as e: + # This is fragile as hell, but it seems to be the only way to raise + # useful errors here. + if e.socket_err: + error = e.socket_err + if isinstance(error, SocketTimeout): + raise ConnectTimeoutError( + self, + "Connection to %s timed out. (connect timeout=%s)" % + (self.host, self.timeout) + ) + else: + raise NewConnectionError( + self, + "Failed to establish a new connection: %s" % error + ) + else: + raise NewConnectionError( + self, + "Failed to establish a new connection: %s" % e + ) + + except SocketError as e: # Defensive: PySocks should catch all these. + raise NewConnectionError( + self, "Failed to establish a new connection: %s" % e) + + return conn + + +# We don't need to duplicate the Verified/Unverified distinction from +# urllib3/connection.py here because the HTTPSConnection will already have been +# correctly set to either the Verified or Unverified form by that module. This +# means the SOCKSHTTPSConnection will automatically be the correct type. +class SOCKSHTTPSConnection(SOCKSConnection, HTTPSConnection): + pass + + +class SOCKSHTTPConnectionPool(HTTPConnectionPool): + ConnectionCls = SOCKSConnection + + +class SOCKSHTTPSConnectionPool(HTTPSConnectionPool): + ConnectionCls = SOCKSHTTPSConnection + + +class SOCKSProxyManager(PoolManager): + """ + A version of the urllib3 ProxyManager that routes connections via the + defined SOCKS proxy. + """ + pool_classes_by_scheme = { + 'http': SOCKSHTTPConnectionPool, + 'https': SOCKSHTTPSConnectionPool, + } + + def __init__(self, proxy_url, username=None, password=None, + num_pools=10, headers=None, **connection_pool_kw): + parsed = parse_url(proxy_url) + + if username is None and password is None and parsed.auth is not None: + split = parsed.auth.split(':') + if len(split) == 2: + username, password = split + if parsed.scheme == 'socks5': + socks_version = socks.PROXY_TYPE_SOCKS5 + rdns = False + elif parsed.scheme == 'socks5h': + socks_version = socks.PROXY_TYPE_SOCKS5 + rdns = True + elif parsed.scheme == 'socks4': + socks_version = socks.PROXY_TYPE_SOCKS4 + rdns = False + elif parsed.scheme == 'socks4a': + socks_version = socks.PROXY_TYPE_SOCKS4 + rdns = True + else: + raise ValueError( + "Unable to determine SOCKS version from %s" % proxy_url + ) + + self.proxy_url = proxy_url + + socks_options = { + 'socks_version': socks_version, + 'proxy_host': parsed.host, + 'proxy_port': parsed.port, + 'username': username, + 'password': password, + 'rdns': rdns + } + connection_pool_kw['_socks_options'] = socks_options + + super(SOCKSProxyManager, self).__init__( + num_pools, headers, **connection_pool_kw + ) + + self.pool_classes_by_scheme = SOCKSProxyManager.pool_classes_by_scheme diff --git a/venv2/lib/python2.7/site-packages/pip/_vendor/urllib3/exceptions.py b/venv2/lib/python2.7/site-packages/pip/_vendor/urllib3/exceptions.py new file mode 100644 index 0000000..7bbaa98 --- /dev/null +++ b/venv2/lib/python2.7/site-packages/pip/_vendor/urllib3/exceptions.py @@ -0,0 +1,246 @@ +from __future__ import absolute_import +from .packages.six.moves.http_client import ( + IncompleteRead as httplib_IncompleteRead +) +# Base Exceptions + + +class HTTPError(Exception): + "Base exception used by this module." + pass + + +class HTTPWarning(Warning): + "Base warning used by this module." + pass + + +class PoolError(HTTPError): + "Base exception for errors caused within a pool." + def __init__(self, pool, message): + self.pool = pool + HTTPError.__init__(self, "%s: %s" % (pool, message)) + + def __reduce__(self): + # For pickling purposes. + return self.__class__, (None, None) + + +class RequestError(PoolError): + "Base exception for PoolErrors that have associated URLs." + def __init__(self, pool, url, message): + self.url = url + PoolError.__init__(self, pool, message) + + def __reduce__(self): + # For pickling purposes. + return self.__class__, (None, self.url, None) + + +class SSLError(HTTPError): + "Raised when SSL certificate fails in an HTTPS connection." + pass + + +class ProxyError(HTTPError): + "Raised when the connection to a proxy fails." + pass + + +class DecodeError(HTTPError): + "Raised when automatic decoding based on Content-Type fails." + pass + + +class ProtocolError(HTTPError): + "Raised when something unexpected happens mid-request/response." + pass + + +#: Renamed to ProtocolError but aliased for backwards compatibility. +ConnectionError = ProtocolError + + +# Leaf Exceptions + +class MaxRetryError(RequestError): + """Raised when the maximum number of retries is exceeded. + + :param pool: The connection pool + :type pool: :class:`~urllib3.connectionpool.HTTPConnectionPool` + :param string url: The requested Url + :param exceptions.Exception reason: The underlying error + + """ + + def __init__(self, pool, url, reason=None): + self.reason = reason + + message = "Max retries exceeded with url: %s (Caused by %r)" % ( + url, reason) + + RequestError.__init__(self, pool, url, message) + + +class HostChangedError(RequestError): + "Raised when an existing pool gets a request for a foreign host." + + def __init__(self, pool, url, retries=3): + message = "Tried to open a foreign host with url: %s" % url + RequestError.__init__(self, pool, url, message) + self.retries = retries + + +class TimeoutStateError(HTTPError): + """ Raised when passing an invalid state to a timeout """ + pass + + +class TimeoutError(HTTPError): + """ Raised when a socket timeout error occurs. + + Catching this error will catch both :exc:`ReadTimeoutErrors + <ReadTimeoutError>` and :exc:`ConnectTimeoutErrors <ConnectTimeoutError>`. + """ + pass + + +class ReadTimeoutError(TimeoutError, RequestError): + "Raised when a socket timeout occurs while receiving data from a server" + pass + + +# This timeout error does not have a URL attached and needs to inherit from the +# base HTTPError +class ConnectTimeoutError(TimeoutError): + "Raised when a socket timeout occurs while connecting to a server" + pass + + +class NewConnectionError(ConnectTimeoutError, PoolError): + "Raised when we fail to establish a new connection. Usually ECONNREFUSED." + pass + + +class EmptyPoolError(PoolError): + "Raised when a pool runs out of connections and no more are allowed." + pass + + +class ClosedPoolError(PoolError): + "Raised when a request enters a pool after the pool has been closed." + pass + + +class LocationValueError(ValueError, HTTPError): + "Raised when there is something wrong with a given URL input." + pass + + +class LocationParseError(LocationValueError): + "Raised when get_host or similar fails to parse the URL input." + + def __init__(self, location): + message = "Failed to parse: %s" % location + HTTPError.__init__(self, message) + + self.location = location + + +class ResponseError(HTTPError): + "Used as a container for an error reason supplied in a MaxRetryError." + GENERIC_ERROR = 'too many error responses' + SPECIFIC_ERROR = 'too many {status_code} error responses' + + +class SecurityWarning(HTTPWarning): + "Warned when performing security reducing actions" + pass + + +class SubjectAltNameWarning(SecurityWarning): + "Warned when connecting to a host with a certificate missing a SAN." + pass + + +class InsecureRequestWarning(SecurityWarning): + "Warned when making an unverified HTTPS request." + pass + + +class SystemTimeWarning(SecurityWarning): + "Warned when system time is suspected to be wrong" + pass + + +class InsecurePlatformWarning(SecurityWarning): + "Warned when certain SSL configuration is not available on a platform." + pass + + +class SNIMissingWarning(HTTPWarning): + "Warned when making a HTTPS request without SNI available." + pass + + +class DependencyWarning(HTTPWarning): + """ + Warned when an attempt is made to import a module with missing optional + dependencies. + """ + pass + + +class ResponseNotChunked(ProtocolError, ValueError): + "Response needs to be chunked in order to read it as chunks." + pass + + +class BodyNotHttplibCompatible(HTTPError): + """ + Body should be httplib.HTTPResponse like (have an fp attribute which + returns raw chunks) for read_chunked(). + """ + pass + + +class IncompleteRead(HTTPError, httplib_IncompleteRead): + """ + Response length doesn't match expected Content-Length + + Subclass of http_client.IncompleteRead to allow int value + for `partial` to avoid creating large objects on streamed + reads. + """ + def __init__(self, partial, expected): + super(IncompleteRead, self).__init__(partial, expected) + + def __repr__(self): + return ('IncompleteRead(%i bytes read, ' + '%i more expected)' % (self.partial, self.expected)) + + +class InvalidHeader(HTTPError): + "The header provided was somehow invalid." + pass + + +class ProxySchemeUnknown(AssertionError, ValueError): + "ProxyManager does not support the supplied scheme" + # TODO(t-8ch): Stop inheriting from AssertionError in v2.0. + + def __init__(self, scheme): + message = "Not supported proxy scheme %s" % scheme + super(ProxySchemeUnknown, self).__init__(message) + + +class HeaderParsingError(HTTPError): + "Raised by assert_header_parsing, but we convert it to a log.warning statement." + def __init__(self, defects, unparsed_data): + message = '%s, unparsed data: %r' % (defects or 'Unknown', unparsed_data) + super(HeaderParsingError, self).__init__(message) + + +class UnrewindableBodyError(HTTPError): + "urllib3 encountered an error when trying to rewind a body" + pass diff --git a/venv2/lib/python2.7/site-packages/pip/_vendor/urllib3/fields.py b/venv2/lib/python2.7/site-packages/pip/_vendor/urllib3/fields.py new file mode 100644 index 0000000..37fe64a --- /dev/null +++ b/venv2/lib/python2.7/site-packages/pip/_vendor/urllib3/fields.py @@ -0,0 +1,178 @@ +from __future__ import absolute_import +import email.utils +import mimetypes + +from .packages import six + + +def guess_content_type(filename, default='application/octet-stream'): + """ + Guess the "Content-Type" of a file. + + :param filename: + The filename to guess the "Content-Type" of using :mod:`mimetypes`. + :param default: + If no "Content-Type" can be guessed, default to `default`. + """ + if filename: + return mimetypes.guess_type(filename)[0] or default + return default + + +def format_header_param(name, value): + """ + Helper function to format and quote a single header parameter. + + Particularly useful for header parameters which might contain + non-ASCII values, like file names. This follows RFC 2231, as + suggested by RFC 2388 Section 4.4. + + :param name: + The name of the parameter, a string expected to be ASCII only. + :param value: + The value of the parameter, provided as a unicode string. + """ + if not any(ch in value for ch in '"\\\r\n'): + result = '%s="%s"' % (name, value) + try: + result.encode('ascii') + except (UnicodeEncodeError, UnicodeDecodeError): + pass + else: + return result + if not six.PY3 and isinstance(value, six.text_type): # Python 2: + value = value.encode('utf-8') + value = email.utils.encode_rfc2231(value, 'utf-8') + value = '%s*=%s' % (name, value) + return value + + +class RequestField(object): + """ + A data container for request body parameters. + + :param name: + The name of this request field. + :param data: + The data/value body. + :param filename: + An optional filename of the request field. + :param headers: + An optional dict-like object of headers to initially use for the field. + """ + def __init__(self, name, data, filename=None, headers=None): + self._name = name + self._filename = filename + self.data = data + self.headers = {} + if headers: + self.headers = dict(headers) + + @classmethod + def from_tuples(cls, fieldname, value): + """ + A :class:`~urllib3.fields.RequestField` factory from old-style tuple parameters. + + Supports constructing :class:`~urllib3.fields.RequestField` from + parameter of key/value strings AND key/filetuple. A filetuple is a + (filename, data, MIME type) tuple where the MIME type is optional. + For example:: + + 'foo': 'bar', + 'fakefile': ('foofile.txt', 'contents of foofile'), + 'realfile': ('barfile.txt', open('realfile').read()), + 'typedfile': ('bazfile.bin', open('bazfile').read(), 'image/jpeg'), + 'nonamefile': 'contents of nonamefile field', + + Field names and filenames must be unicode. + """ + if isinstance(value, tuple): + if len(value) == 3: + filename, data, content_type = value + else: + filename, data = value + content_type = guess_content_type(filename) + else: + filename = None + content_type = None + data = value + + request_param = cls(fieldname, data, filename=filename) + request_param.make_multipart(content_type=content_type) + + return request_param + + def _render_part(self, name, value): + """ + Overridable helper function to format a single header parameter. + + :param name: + The name of the parameter, a string expected to be ASCII only. + :param value: + The value of the parameter, provided as a unicode string. + """ + return format_header_param(name, value) + + def _render_parts(self, header_parts): + """ + Helper function to format and quote a single header. + + Useful for single headers that are composed of multiple items. E.g., + 'Content-Disposition' fields. + + :param header_parts: + A sequence of (k, v) tuples or a :class:`dict` of (k, v) to format + as `k1="v1"; k2="v2"; ...`. + """ + parts = [] + iterable = header_parts + if isinstance(header_parts, dict): + iterable = header_parts.items() + + for name, value in iterable: + if value is not None: + parts.append(self._render_part(name, value)) + + return '; '.join(parts) + + def render_headers(self): + """ + Renders the headers for this request field. + """ + lines = [] + + sort_keys = ['Content-Disposition', 'Content-Type', 'Content-Location'] + for sort_key in sort_keys: + if self.headers.get(sort_key, False): + lines.append('%s: %s' % (sort_key, self.headers[sort_key])) + + for header_name, header_value in self.headers.items(): + if header_name not in sort_keys: + if header_value: + lines.append('%s: %s' % (header_name, header_value)) + + lines.append('\r\n') + return '\r\n'.join(lines) + + def make_multipart(self, content_disposition=None, content_type=None, + content_location=None): + """ + Makes this request field into a multipart request field. + + This method overrides "Content-Disposition", "Content-Type" and + "Content-Location" headers to the request parameter. + + :param content_type: + The 'Content-Type' of the request body. + :param content_location: + The 'Content-Location' of the request body. + + """ + self.headers['Content-Disposition'] = content_disposition or 'form-data' + self.headers['Content-Disposition'] += '; '.join([ + '', self._render_parts( + (('name', self._name), ('filename', self._filename)) + ) + ]) + self.headers['Content-Type'] = content_type + self.headers['Content-Location'] = content_location diff --git a/venv2/lib/python2.7/site-packages/pip/_vendor/urllib3/filepost.py b/venv2/lib/python2.7/site-packages/pip/_vendor/urllib3/filepost.py new file mode 100644 index 0000000..78f1e19 --- /dev/null +++ b/venv2/lib/python2.7/site-packages/pip/_vendor/urllib3/filepost.py @@ -0,0 +1,98 @@ +from __future__ import absolute_import +import binascii +import codecs +import os + +from io import BytesIO + +from .packages import six +from .packages.six import b +from .fields import RequestField + +writer = codecs.lookup('utf-8')[3] + + +def choose_boundary(): + """ + Our embarrassingly-simple replacement for mimetools.choose_boundary. + """ + boundary = binascii.hexlify(os.urandom(16)) + if six.PY3: + boundary = boundary.decode('ascii') + return boundary + + +def iter_field_objects(fields): + """ + Iterate over fields. + + Supports list of (k, v) tuples and dicts, and lists of + :class:`~urllib3.fields.RequestField`. + + """ + if isinstance(fields, dict): + i = six.iteritems(fields) + else: + i = iter(fields) + + for field in i: + if isinstance(field, RequestField): + yield field + else: + yield RequestField.from_tuples(*field) + + +def iter_fields(fields): + """ + .. deprecated:: 1.6 + + Iterate over fields. + + The addition of :class:`~urllib3.fields.RequestField` makes this function + obsolete. Instead, use :func:`iter_field_objects`, which returns + :class:`~urllib3.fields.RequestField` objects. + + Supports list of (k, v) tuples and dicts. + """ + if isinstance(fields, dict): + return ((k, v) for k, v in six.iteritems(fields)) + + return ((k, v) for k, v in fields) + + +def encode_multipart_formdata(fields, boundary=None): + """ + Encode a dictionary of ``fields`` using the multipart/form-data MIME format. + + :param fields: + Dictionary of fields or list of (key, :class:`~urllib3.fields.RequestField`). + + :param boundary: + If not specified, then a random boundary will be generated using + :func:`urllib3.filepost.choose_boundary`. + """ + body = BytesIO() + if boundary is None: + boundary = choose_boundary() + + for field in iter_field_objects(fields): + body.write(b('--%s\r\n' % (boundary))) + + writer(body).write(field.render_headers()) + data = field.data + + if isinstance(data, int): + data = str(data) # Backwards compatibility + + if isinstance(data, six.text_type): + writer(body).write(data) + else: + body.write(data) + + body.write(b'\r\n') + + body.write(b('--%s--\r\n' % (boundary))) + + content_type = str('multipart/form-data; boundary=%s' % boundary) + + return body.getvalue(), content_type diff --git a/venv2/lib/python2.7/site-packages/pip/_vendor/urllib3/packages/__init__.py b/venv2/lib/python2.7/site-packages/pip/_vendor/urllib3/packages/__init__.py new file mode 100644 index 0000000..170e974 --- /dev/null +++ b/venv2/lib/python2.7/site-packages/pip/_vendor/urllib3/packages/__init__.py @@ -0,0 +1,5 @@ +from __future__ import absolute_import + +from . import ssl_match_hostname + +__all__ = ('ssl_match_hostname', ) diff --git a/venv2/lib/python2.7/site-packages/pip/_vendor/urllib3/packages/backports/__init__.py b/venv2/lib/python2.7/site-packages/pip/_vendor/urllib3/packages/backports/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/venv2/lib/python2.7/site-packages/pip/_vendor/urllib3/packages/backports/makefile.py b/venv2/lib/python2.7/site-packages/pip/_vendor/urllib3/packages/backports/makefile.py new file mode 100644 index 0000000..740db37 --- /dev/null +++ b/venv2/lib/python2.7/site-packages/pip/_vendor/urllib3/packages/backports/makefile.py @@ -0,0 +1,53 @@ +# -*- coding: utf-8 -*- +""" +backports.makefile +~~~~~~~~~~~~~~~~~~ + +Backports the Python 3 ``socket.makefile`` method for use with anything that +wants to create a "fake" socket object. +""" +import io + +from socket import SocketIO + + +def backport_makefile(self, mode="r", buffering=None, encoding=None, + errors=None, newline=None): + """ + Backport of ``socket.makefile`` from Python 3.5. + """ + if not set(mode) <= {"r", "w", "b"}: + raise ValueError( + "invalid mode %r (only r, w, b allowed)" % (mode,) + ) + writing = "w" in mode + reading = "r" in mode or not writing + assert reading or writing + binary = "b" in mode + rawmode = "" + if reading: + rawmode += "r" + if writing: + rawmode += "w" + raw = SocketIO(self, rawmode) + self._makefile_refs += 1 + if buffering is None: + buffering = -1 + if buffering < 0: + buffering = io.DEFAULT_BUFFER_SIZE + if buffering == 0: + if not binary: + raise ValueError("unbuffered streams must be binary") + return raw + if reading and writing: + buffer = io.BufferedRWPair(raw, raw, buffering) + elif reading: + buffer = io.BufferedReader(raw, buffering) + else: + assert writing + buffer = io.BufferedWriter(raw, buffering) + if binary: + return buffer + text = io.TextIOWrapper(buffer, encoding, errors, newline) + text.mode = mode + return text diff --git a/venv2/lib/python2.7/site-packages/pip/_vendor/urllib3/packages/six.py b/venv2/lib/python2.7/site-packages/pip/_vendor/urllib3/packages/six.py new file mode 100644 index 0000000..190c023 --- /dev/null +++ b/venv2/lib/python2.7/site-packages/pip/_vendor/urllib3/packages/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/venv2/lib/python2.7/site-packages/pip/_vendor/urllib3/packages/ssl_match_hostname/__init__.py b/venv2/lib/python2.7/site-packages/pip/_vendor/urllib3/packages/ssl_match_hostname/__init__.py new file mode 100644 index 0000000..d6594eb --- /dev/null +++ b/venv2/lib/python2.7/site-packages/pip/_vendor/urllib3/packages/ssl_match_hostname/__init__.py @@ -0,0 +1,19 @@ +import sys + +try: + # Our match_hostname function is the same as 3.5's, so we only want to + # import the match_hostname function if it's at least that good. + if sys.version_info < (3, 5): + raise ImportError("Fallback to vendored code") + + from ssl import CertificateError, match_hostname +except ImportError: + try: + # Backport of the function from a pypi module + from backports.ssl_match_hostname import CertificateError, match_hostname + except ImportError: + # Our vendored copy + from ._implementation import CertificateError, match_hostname + +# Not needed, but documenting what we provide. +__all__ = ('CertificateError', 'match_hostname') diff --git a/venv2/lib/python2.7/site-packages/pip/_vendor/urllib3/packages/ssl_match_hostname/_implementation.py b/venv2/lib/python2.7/site-packages/pip/_vendor/urllib3/packages/ssl_match_hostname/_implementation.py new file mode 100644 index 0000000..970cf65 --- /dev/null +++ b/venv2/lib/python2.7/site-packages/pip/_vendor/urllib3/packages/ssl_match_hostname/_implementation.py @@ -0,0 +1,156 @@ +"""The match_hostname() function from Python 3.3.3, essential when using SSL.""" + +# Note: This file is under the PSF license as the code comes from the python +# stdlib. http://docs.python.org/3/license.html + +import re +import sys + +# ipaddress has been backported to 2.6+ in pypi. If it is installed on the +# system, use it to handle IPAddress ServerAltnames (this was added in +# python-3.5) otherwise only do DNS matching. This allows +# backports.ssl_match_hostname to continue to be used in Python 2.7. +try: + from pip._vendor import ipaddress +except ImportError: + ipaddress = None + +__version__ = '3.5.0.1' + + +class CertificateError(ValueError): + pass + + +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 _to_unicode(obj): + if isinstance(obj, str) and sys.version_info < (3,): + obj = unicode(obj, encoding='ascii', errors='strict') + return obj + +def _ipaddress_match(ipname, host_ip): + """Exact matching of IP addresses. + + RFC 6125 explicitly doesn't define an algorithm for this + (section 1.7.2 - "Out of Scope"). + """ + # OpenSSL may add a trailing newline to a subjectAltName's IP address + # Divergence from upstream: ipaddress can't handle byte str + ip = ipaddress.ip_address(_to_unicode(ipname).rstrip()) + return ip == host_ip + + +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, match_hostname needs a " + "SSL socket or SSL context with either " + "CERT_OPTIONAL or CERT_REQUIRED") + try: + # Divergence from upstream: ipaddress can't handle byte str + host_ip = ipaddress.ip_address(_to_unicode(hostname)) + except ValueError: + # Not an IP address (common case) + host_ip = None + except UnicodeError: + # Divergence from upstream: Have to deal with ipaddress not taking + # byte strings. addresses should be all ascii, so we consider it not + # an ipaddress in this case + host_ip = None + except AttributeError: + # Divergence from upstream: Make ipaddress library optional + if ipaddress is None: + host_ip = None + else: + raise + dnsnames = [] + san = cert.get('subjectAltName', ()) + for key, value in san: + if key == 'DNS': + if host_ip is None and _dnsname_match(value, hostname): + return + dnsnames.append(value) + elif key == 'IP Address': + if host_ip is not None and _ipaddress_match(value, host_ip): + 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") diff --git a/venv2/lib/python2.7/site-packages/pip/_vendor/urllib3/poolmanager.py b/venv2/lib/python2.7/site-packages/pip/_vendor/urllib3/poolmanager.py new file mode 100644 index 0000000..fe5491c --- /dev/null +++ b/venv2/lib/python2.7/site-packages/pip/_vendor/urllib3/poolmanager.py @@ -0,0 +1,450 @@ +from __future__ import absolute_import +import collections +import functools +import logging + +from ._collections import RecentlyUsedContainer +from .connectionpool import HTTPConnectionPool, HTTPSConnectionPool +from .connectionpool import port_by_scheme +from .exceptions import LocationValueError, MaxRetryError, ProxySchemeUnknown +from .packages.six.moves.urllib.parse import urljoin +from .request import RequestMethods +from .util.url import parse_url +from .util.retry import Retry + + +__all__ = ['PoolManager', 'ProxyManager', 'proxy_from_url'] + + +log = logging.getLogger(__name__) + +SSL_KEYWORDS = ('key_file', 'cert_file', 'cert_reqs', 'ca_certs', + 'ssl_version', 'ca_cert_dir', 'ssl_context') + +# All known keyword arguments that could be provided to the pool manager, its +# pools, or the underlying connections. This is used to construct a pool key. +_key_fields = ( + 'key_scheme', # str + 'key_host', # str + 'key_port', # int + 'key_timeout', # int or float or Timeout + 'key_retries', # int or Retry + 'key_strict', # bool + 'key_block', # bool + 'key_source_address', # str + 'key_key_file', # str + 'key_cert_file', # str + 'key_cert_reqs', # str + 'key_ca_certs', # str + 'key_ssl_version', # str + 'key_ca_cert_dir', # str + 'key_ssl_context', # instance of ssl.SSLContext or urllib3.util.ssl_.SSLContext + 'key_maxsize', # int + 'key_headers', # dict + 'key__proxy', # parsed proxy url + 'key__proxy_headers', # dict + 'key_socket_options', # list of (level (int), optname (int), value (int or str)) tuples + 'key__socks_options', # dict + 'key_assert_hostname', # bool or string + 'key_assert_fingerprint', # str + 'key_server_hostname', #str +) + +#: The namedtuple class used to construct keys for the connection pool. +#: All custom key schemes should include the fields in this key at a minimum. +PoolKey = collections.namedtuple('PoolKey', _key_fields) + + +def _default_key_normalizer(key_class, request_context): + """ + Create a pool key out of a request context dictionary. + + According to RFC 3986, both the scheme and host are case-insensitive. + Therefore, this function normalizes both before constructing the pool + key for an HTTPS request. If you wish to change this behaviour, provide + alternate callables to ``key_fn_by_scheme``. + + :param key_class: + The class to use when constructing the key. This should be a namedtuple + with the ``scheme`` and ``host`` keys at a minimum. + :type key_class: namedtuple + :param request_context: + A dictionary-like object that contain the context for a request. + :type request_context: dict + + :return: A namedtuple that can be used as a connection pool key. + :rtype: PoolKey + """ + # Since we mutate the dictionary, make a copy first + context = request_context.copy() + context['scheme'] = context['scheme'].lower() + context['host'] = context['host'].lower() + + # These are both dictionaries and need to be transformed into frozensets + for key in ('headers', '_proxy_headers', '_socks_options'): + if key in context and context[key] is not None: + context[key] = frozenset(context[key].items()) + + # The socket_options key may be a list and needs to be transformed into a + # tuple. + socket_opts = context.get('socket_options') + if socket_opts is not None: + context['socket_options'] = tuple(socket_opts) + + # Map the kwargs to the names in the namedtuple - this is necessary since + # namedtuples can't have fields starting with '_'. + for key in list(context.keys()): + context['key_' + key] = context.pop(key) + + # Default to ``None`` for keys missing from the context + for field in key_class._fields: + if field not in context: + context[field] = None + + return key_class(**context) + + +#: A dictionary that maps a scheme to a callable that creates a pool key. +#: This can be used to alter the way pool keys are constructed, if desired. +#: Each PoolManager makes a copy of this dictionary so they can be configured +#: globally here, or individually on the instance. +key_fn_by_scheme = { + 'http': functools.partial(_default_key_normalizer, PoolKey), + 'https': functools.partial(_default_key_normalizer, PoolKey), +} + +pool_classes_by_scheme = { + 'http': HTTPConnectionPool, + 'https': HTTPSConnectionPool, +} + + +class PoolManager(RequestMethods): + """ + Allows for arbitrary requests while transparently keeping track of + necessary connection pools for you. + + :param num_pools: + Number of connection pools to cache before discarding the least + recently used pool. + + :param headers: + Headers to include with all requests, unless other headers are given + explicitly. + + :param \\**connection_pool_kw: + Additional parameters are used to create fresh + :class:`urllib3.connectionpool.ConnectionPool` instances. + + Example:: + + >>> manager = PoolManager(num_pools=2) + >>> r = manager.request('GET', 'http://google.com/') + >>> r = manager.request('GET', 'http://google.com/mail') + >>> r = manager.request('GET', 'http://yahoo.com/') + >>> len(manager.pools) + 2 + + """ + + proxy = None + + def __init__(self, num_pools=10, headers=None, **connection_pool_kw): + RequestMethods.__init__(self, headers) + self.connection_pool_kw = connection_pool_kw + self.pools = RecentlyUsedContainer(num_pools, + dispose_func=lambda p: p.close()) + + # Locally set the pool classes and keys so other PoolManagers can + # override them. + self.pool_classes_by_scheme = pool_classes_by_scheme + self.key_fn_by_scheme = key_fn_by_scheme.copy() + + def __enter__(self): + return self + + def __exit__(self, exc_type, exc_val, exc_tb): + self.clear() + # Return False to re-raise any potential exceptions + return False + + def _new_pool(self, scheme, host, port, request_context=None): + """ + Create a new :class:`ConnectionPool` based on host, port, scheme, and + any additional pool keyword arguments. + + If ``request_context`` is provided, it is provided as keyword arguments + to the pool class used. This method is used to actually create the + connection pools handed out by :meth:`connection_from_url` and + companion methods. It is intended to be overridden for customization. + """ + pool_cls = self.pool_classes_by_scheme[scheme] + if request_context is None: + request_context = self.connection_pool_kw.copy() + + # Although the context has everything necessary to create the pool, + # this function has historically only used the scheme, host, and port + # in the positional args. When an API change is acceptable these can + # be removed. + for key in ('scheme', 'host', 'port'): + request_context.pop(key, None) + + if scheme == 'http': + for kw in SSL_KEYWORDS: + request_context.pop(kw, None) + + return pool_cls(host, port, **request_context) + + def clear(self): + """ + Empty our store of pools and direct them all to close. + + This will not affect in-flight connections, but they will not be + re-used after completion. + """ + self.pools.clear() + + def connection_from_host(self, host, port=None, scheme='http', pool_kwargs=None): + """ + Get a :class:`ConnectionPool` based on the host, port, and scheme. + + If ``port`` isn't given, it will be derived from the ``scheme`` using + ``urllib3.connectionpool.port_by_scheme``. If ``pool_kwargs`` is + provided, it is merged with the instance's ``connection_pool_kw`` + variable and used to create the new connection pool, if one is + needed. + """ + + if not host: + raise LocationValueError("No host specified.") + + request_context = self._merge_pool_kwargs(pool_kwargs) + request_context['scheme'] = scheme or 'http' + if not port: + port = port_by_scheme.get(request_context['scheme'].lower(), 80) + request_context['port'] = port + request_context['host'] = host + + return self.connection_from_context(request_context) + + def connection_from_context(self, request_context): + """ + Get a :class:`ConnectionPool` based on the request context. + + ``request_context`` must at least contain the ``scheme`` key and its + value must be a key in ``key_fn_by_scheme`` instance variable. + """ + scheme = request_context['scheme'].lower() + pool_key_constructor = self.key_fn_by_scheme[scheme] + pool_key = pool_key_constructor(request_context) + + return self.connection_from_pool_key(pool_key, request_context=request_context) + + def connection_from_pool_key(self, pool_key, request_context=None): + """ + Get a :class:`ConnectionPool` based on the provided pool key. + + ``pool_key`` should be a namedtuple that only contains immutable + objects. At a minimum it must have the ``scheme``, ``host``, and + ``port`` fields. + """ + with self.pools.lock: + # If the scheme, host, or port doesn't match existing open + # connections, open a new ConnectionPool. + pool = self.pools.get(pool_key) + if pool: + return pool + + # Make a fresh ConnectionPool of the desired type + scheme = request_context['scheme'] + host = request_context['host'] + port = request_context['port'] + pool = self._new_pool(scheme, host, port, request_context=request_context) + self.pools[pool_key] = pool + + return pool + + def connection_from_url(self, url, pool_kwargs=None): + """ + Similar to :func:`urllib3.connectionpool.connection_from_url`. + + If ``pool_kwargs`` is not provided and a new pool needs to be + constructed, ``self.connection_pool_kw`` is used to initialize + the :class:`urllib3.connectionpool.ConnectionPool`. If ``pool_kwargs`` + is provided, it is used instead. Note that if a new pool does not + need to be created for the request, the provided ``pool_kwargs`` are + not used. + """ + u = parse_url(url) + return self.connection_from_host(u.host, port=u.port, scheme=u.scheme, + pool_kwargs=pool_kwargs) + + def _merge_pool_kwargs(self, override): + """ + Merge a dictionary of override values for self.connection_pool_kw. + + This does not modify self.connection_pool_kw and returns a new dict. + Any keys in the override dictionary with a value of ``None`` are + removed from the merged dictionary. + """ + base_pool_kwargs = self.connection_pool_kw.copy() + if override: + for key, value in override.items(): + if value is None: + try: + del base_pool_kwargs[key] + except KeyError: + pass + else: + base_pool_kwargs[key] = value + return base_pool_kwargs + + def urlopen(self, method, url, redirect=True, **kw): + """ + Same as :meth:`urllib3.connectionpool.HTTPConnectionPool.urlopen` + with custom cross-host redirect logic and only sends the request-uri + portion of the ``url``. + + The given ``url`` parameter must be absolute, such that an appropriate + :class:`urllib3.connectionpool.ConnectionPool` can be chosen for it. + """ + u = parse_url(url) + conn = self.connection_from_host(u.host, port=u.port, scheme=u.scheme) + + kw['assert_same_host'] = False + kw['redirect'] = False + + if 'headers' not in kw: + kw['headers'] = self.headers.copy() + + if self.proxy is not None and u.scheme == "http": + response = conn.urlopen(method, url, **kw) + else: + response = conn.urlopen(method, u.request_uri, **kw) + + redirect_location = redirect and response.get_redirect_location() + if not redirect_location: + return response + + # Support relative URLs for redirecting. + redirect_location = urljoin(url, redirect_location) + + # RFC 7231, Section 6.4.4 + if response.status == 303: + method = 'GET' + + retries = kw.get('retries') + if not isinstance(retries, Retry): + retries = Retry.from_int(retries, redirect=redirect) + + # Strip headers marked as unsafe to forward to the redirected location. + # Check remove_headers_on_redirect to avoid a potential network call within + # conn.is_same_host() which may use socket.gethostbyname() in the future. + if (retries.remove_headers_on_redirect + and not conn.is_same_host(redirect_location)): + for header in retries.remove_headers_on_redirect: + kw['headers'].pop(header, None) + + try: + retries = retries.increment(method, url, response=response, _pool=conn) + except MaxRetryError: + if retries.raise_on_redirect: + raise + return response + + kw['retries'] = retries + kw['redirect'] = redirect + + log.info("Redirecting %s -> %s", url, redirect_location) + return self.urlopen(method, redirect_location, **kw) + + +class ProxyManager(PoolManager): + """ + Behaves just like :class:`PoolManager`, but sends all requests through + the defined proxy, using the CONNECT method for HTTPS URLs. + + :param proxy_url: + The URL of the proxy to be used. + + :param proxy_headers: + A dictionary containing headers that will be sent to the proxy. In case + of HTTP they are being sent with each request, while in the + HTTPS/CONNECT case they are sent only once. Could be used for proxy + authentication. + + Example: + >>> proxy = urllib3.ProxyManager('http://localhost:3128/') + >>> r1 = proxy.request('GET', 'http://google.com/') + >>> r2 = proxy.request('GET', 'http://httpbin.org/') + >>> len(proxy.pools) + 1 + >>> r3 = proxy.request('GET', 'https://httpbin.org/') + >>> r4 = proxy.request('GET', 'https://twitter.com/') + >>> len(proxy.pools) + 3 + + """ + + def __init__(self, proxy_url, num_pools=10, headers=None, + proxy_headers=None, **connection_pool_kw): + + if isinstance(proxy_url, HTTPConnectionPool): + proxy_url = '%s://%s:%i' % (proxy_url.scheme, proxy_url.host, + proxy_url.port) + proxy = parse_url(proxy_url) + if not proxy.port: + port = port_by_scheme.get(proxy.scheme, 80) + proxy = proxy._replace(port=port) + + if proxy.scheme not in ("http", "https"): + raise ProxySchemeUnknown(proxy.scheme) + + self.proxy = proxy + self.proxy_headers = proxy_headers or {} + + connection_pool_kw['_proxy'] = self.proxy + connection_pool_kw['_proxy_headers'] = self.proxy_headers + + super(ProxyManager, self).__init__( + num_pools, headers, **connection_pool_kw) + + def connection_from_host(self, host, port=None, scheme='http', pool_kwargs=None): + if scheme == "https": + return super(ProxyManager, self).connection_from_host( + host, port, scheme, pool_kwargs=pool_kwargs) + + return super(ProxyManager, self).connection_from_host( + self.proxy.host, self.proxy.port, self.proxy.scheme, pool_kwargs=pool_kwargs) + + def _set_proxy_headers(self, url, headers=None): + """ + Sets headers needed by proxies: specifically, the Accept and Host + headers. Only sets headers not provided by the user. + """ + headers_ = {'Accept': '*/*'} + + netloc = parse_url(url).netloc + if netloc: + headers_['Host'] = netloc + + if headers: + headers_.update(headers) + return headers_ + + def urlopen(self, method, url, redirect=True, **kw): + "Same as HTTP(S)ConnectionPool.urlopen, ``url`` must be absolute." + u = parse_url(url) + + if u.scheme == "http": + # For proxied HTTPS requests, httplib sets the necessary headers + # on the CONNECT to the proxy. For HTTP, we'll definitely + # need to set 'Host' at the very least. + headers = kw.get('headers', self.headers) + kw['headers'] = self._set_proxy_headers(url, headers) + + return super(ProxyManager, self).urlopen(method, url, redirect=redirect, **kw) + + +def proxy_from_url(url, **kw): + return ProxyManager(proxy_url=url, **kw) diff --git a/venv2/lib/python2.7/site-packages/pip/_vendor/urllib3/request.py b/venv2/lib/python2.7/site-packages/pip/_vendor/urllib3/request.py new file mode 100644 index 0000000..8f2f44b --- /dev/null +++ b/venv2/lib/python2.7/site-packages/pip/_vendor/urllib3/request.py @@ -0,0 +1,150 @@ +from __future__ import absolute_import + +from .filepost import encode_multipart_formdata +from .packages.six.moves.urllib.parse import urlencode + + +__all__ = ['RequestMethods'] + + +class RequestMethods(object): + """ + Convenience mixin for classes who implement a :meth:`urlopen` method, such + as :class:`~urllib3.connectionpool.HTTPConnectionPool` and + :class:`~urllib3.poolmanager.PoolManager`. + + Provides behavior for making common types of HTTP request methods and + decides which type of request field encoding to use. + + Specifically, + + :meth:`.request_encode_url` is for sending requests whose fields are + encoded in the URL (such as GET, HEAD, DELETE). + + :meth:`.request_encode_body` is for sending requests whose fields are + encoded in the *body* of the request using multipart or www-form-urlencoded + (such as for POST, PUT, PATCH). + + :meth:`.request` is for making any kind of request, it will look up the + appropriate encoding format and use one of the above two methods to make + the request. + + Initializer parameters: + + :param headers: + Headers to include with all requests, unless other headers are given + explicitly. + """ + + _encode_url_methods = {'DELETE', 'GET', 'HEAD', 'OPTIONS'} + + def __init__(self, headers=None): + self.headers = headers or {} + + def urlopen(self, method, url, body=None, headers=None, + encode_multipart=True, multipart_boundary=None, + **kw): # Abstract + raise NotImplementedError("Classes extending RequestMethods must implement " + "their own ``urlopen`` method.") + + def request(self, method, url, fields=None, headers=None, **urlopen_kw): + """ + Make a request using :meth:`urlopen` with the appropriate encoding of + ``fields`` based on the ``method`` used. + + This is a convenience method that requires the least amount of manual + effort. It can be used in most situations, while still having the + option to drop down to more specific methods when necessary, such as + :meth:`request_encode_url`, :meth:`request_encode_body`, + or even the lowest level :meth:`urlopen`. + """ + method = method.upper() + + urlopen_kw['request_url'] = url + + if method in self._encode_url_methods: + return self.request_encode_url(method, url, fields=fields, + headers=headers, + **urlopen_kw) + else: + return self.request_encode_body(method, url, fields=fields, + headers=headers, + **urlopen_kw) + + def request_encode_url(self, method, url, fields=None, headers=None, + **urlopen_kw): + """ + Make a request using :meth:`urlopen` with the ``fields`` encoded in + the url. This is useful for request methods like GET, HEAD, DELETE, etc. + """ + if headers is None: + headers = self.headers + + extra_kw = {'headers': headers} + extra_kw.update(urlopen_kw) + + if fields: + url += '?' + urlencode(fields) + + return self.urlopen(method, url, **extra_kw) + + def request_encode_body(self, method, url, fields=None, headers=None, + encode_multipart=True, multipart_boundary=None, + **urlopen_kw): + """ + Make a request using :meth:`urlopen` with the ``fields`` encoded in + the body. This is useful for request methods like POST, PUT, PATCH, etc. + + When ``encode_multipart=True`` (default), then + :meth:`urllib3.filepost.encode_multipart_formdata` is used to encode + the payload with the appropriate content type. Otherwise + :meth:`urllib.urlencode` is used with the + 'application/x-www-form-urlencoded' content type. + + Multipart encoding must be used when posting files, and it's reasonably + safe to use it in other times too. However, it may break request + signing, such as with OAuth. + + Supports an optional ``fields`` parameter of key/value strings AND + key/filetuple. A filetuple is a (filename, data, MIME type) tuple where + the MIME type is optional. For example:: + + fields = { + 'foo': 'bar', + 'fakefile': ('foofile.txt', 'contents of foofile'), + 'realfile': ('barfile.txt', open('realfile').read()), + 'typedfile': ('bazfile.bin', open('bazfile').read(), + 'image/jpeg'), + 'nonamefile': 'contents of nonamefile field', + } + + When uploading a file, providing a filename (the first parameter of the + tuple) is optional but recommended to best mimic behavior of browsers. + + Note that if ``headers`` are supplied, the 'Content-Type' header will + be overwritten because it depends on the dynamic random boundary string + which is used to compose the body of the request. The random boundary + string can be explicitly set with the ``multipart_boundary`` parameter. + """ + if headers is None: + headers = self.headers + + extra_kw = {'headers': {}} + + if fields: + if 'body' in urlopen_kw: + raise TypeError( + "request got values for both 'fields' and 'body', can only specify one.") + + if encode_multipart: + body, content_type = encode_multipart_formdata(fields, boundary=multipart_boundary) + else: + body, content_type = urlencode(fields), 'application/x-www-form-urlencoded' + + extra_kw['body'] = body + extra_kw['headers'] = {'Content-Type': content_type} + + extra_kw['headers'].update(headers) + extra_kw.update(urlopen_kw) + + return self.urlopen(method, url, **extra_kw) diff --git a/venv2/lib/python2.7/site-packages/pip/_vendor/urllib3/response.py b/venv2/lib/python2.7/site-packages/pip/_vendor/urllib3/response.py new file mode 100644 index 0000000..c112690 --- /dev/null +++ b/venv2/lib/python2.7/site-packages/pip/_vendor/urllib3/response.py @@ -0,0 +1,705 @@ +from __future__ import absolute_import +from contextlib import contextmanager +import zlib +import io +import logging +from socket import timeout as SocketTimeout +from socket import error as SocketError + +from ._collections import HTTPHeaderDict +from .exceptions import ( + BodyNotHttplibCompatible, ProtocolError, DecodeError, ReadTimeoutError, + ResponseNotChunked, IncompleteRead, InvalidHeader +) +from .packages.six import string_types as basestring, PY3 +from .packages.six.moves import http_client as httplib +from .connection import HTTPException, BaseSSLError +from .util.response import is_fp_closed, is_response_to_head + +log = logging.getLogger(__name__) + + +class DeflateDecoder(object): + + def __init__(self): + self._first_try = True + self._data = b'' + self._obj = zlib.decompressobj() + + def __getattr__(self, name): + return getattr(self._obj, name) + + def decompress(self, data): + if not data: + return data + + if not self._first_try: + return self._obj.decompress(data) + + self._data += data + try: + decompressed = self._obj.decompress(data) + if decompressed: + self._first_try = False + self._data = None + return decompressed + except zlib.error: + self._first_try = False + self._obj = zlib.decompressobj(-zlib.MAX_WBITS) + try: + return self.decompress(self._data) + finally: + self._data = None + + +class GzipDecoderState(object): + + FIRST_MEMBER = 0 + OTHER_MEMBERS = 1 + SWALLOW_DATA = 2 + + +class GzipDecoder(object): + + def __init__(self): + self._obj = zlib.decompressobj(16 + zlib.MAX_WBITS) + self._state = GzipDecoderState.FIRST_MEMBER + + def __getattr__(self, name): + return getattr(self._obj, name) + + def decompress(self, data): + ret = bytearray() + if self._state == GzipDecoderState.SWALLOW_DATA or not data: + return bytes(ret) + while True: + try: + ret += self._obj.decompress(data) + except zlib.error: + previous_state = self._state + # Ignore data after the first error + self._state = GzipDecoderState.SWALLOW_DATA + if previous_state == GzipDecoderState.OTHER_MEMBERS: + # Allow trailing garbage acceptable in other gzip clients + return bytes(ret) + raise + data = self._obj.unused_data + if not data: + return bytes(ret) + self._state = GzipDecoderState.OTHER_MEMBERS + self._obj = zlib.decompressobj(16 + zlib.MAX_WBITS) + + +class MultiDecoder(object): + """ + From RFC7231: + If one or more encodings have been applied to a representation, the + sender that applied the encodings MUST generate a Content-Encoding + header field that lists the content codings in the order in which + they were applied. + """ + + def __init__(self, modes): + self._decoders = [_get_decoder(m.strip()) for m in modes.split(',')] + + def flush(self): + return self._decoders[0].flush() + + def decompress(self, data): + for d in reversed(self._decoders): + data = d.decompress(data) + return data + + +def _get_decoder(mode): + if ',' in mode: + return MultiDecoder(mode) + + if mode == 'gzip': + return GzipDecoder() + + return DeflateDecoder() + + +class HTTPResponse(io.IOBase): + """ + HTTP Response container. + + Backwards-compatible to httplib's HTTPResponse but the response ``body`` is + loaded and decoded on-demand when the ``data`` property is accessed. This + class is also compatible with the Python standard library's :mod:`io` + module, and can hence be treated as a readable object in the context of that + framework. + + Extra parameters for behaviour not present in httplib.HTTPResponse: + + :param preload_content: + If True, the response's body will be preloaded during construction. + + :param decode_content: + If True, will attempt to decode the body based on the + 'content-encoding' header. + + :param original_response: + When this HTTPResponse wrapper is generated from an httplib.HTTPResponse + object, it's convenient to include the original for debug purposes. It's + otherwise unused. + + :param retries: + The retries contains the last :class:`~urllib3.util.retry.Retry` that + was used during the request. + + :param enforce_content_length: + Enforce content length checking. Body returned by server must match + value of Content-Length header, if present. Otherwise, raise error. + """ + + CONTENT_DECODERS = ['gzip', 'deflate'] + REDIRECT_STATUSES = [301, 302, 303, 307, 308] + + def __init__(self, body='', headers=None, status=0, version=0, reason=None, + strict=0, preload_content=True, decode_content=True, + original_response=None, pool=None, connection=None, msg=None, + retries=None, enforce_content_length=False, + request_method=None, request_url=None): + + if isinstance(headers, HTTPHeaderDict): + self.headers = headers + else: + self.headers = HTTPHeaderDict(headers) + self.status = status + self.version = version + self.reason = reason + self.strict = strict + self.decode_content = decode_content + self.retries = retries + self.enforce_content_length = enforce_content_length + + self._decoder = None + self._body = None + self._fp = None + self._original_response = original_response + self._fp_bytes_read = 0 + self.msg = msg + self._request_url = request_url + + if body and isinstance(body, (basestring, bytes)): + self._body = body + + self._pool = pool + self._connection = connection + + if hasattr(body, 'read'): + self._fp = body + + # Are we using the chunked-style of transfer encoding? + self.chunked = False + self.chunk_left = None + tr_enc = self.headers.get('transfer-encoding', '').lower() + # Don't incur the penalty of creating a list and then discarding it + encodings = (enc.strip() for enc in tr_enc.split(",")) + if "chunked" in encodings: + self.chunked = True + + # Determine length of response + self.length_remaining = self._init_length(request_method) + + # If requested, preload the body. + if preload_content and not self._body: + self._body = self.read(decode_content=decode_content) + + def get_redirect_location(self): + """ + Should we redirect and where to? + + :returns: Truthy redirect location string if we got a redirect status + code and valid location. ``None`` if redirect status and no + location. ``False`` if not a redirect status code. + """ + if self.status in self.REDIRECT_STATUSES: + return self.headers.get('location') + + return False + + def release_conn(self): + if not self._pool or not self._connection: + return + + self._pool._put_conn(self._connection) + self._connection = None + + @property + def data(self): + # For backwords-compat with earlier urllib3 0.4 and earlier. + if self._body: + return self._body + + if self._fp: + return self.read(cache_content=True) + + @property + def connection(self): + return self._connection + + def isclosed(self): + return is_fp_closed(self._fp) + + def tell(self): + """ + Obtain the number of bytes pulled over the wire so far. May differ from + the amount of content returned by :meth:``HTTPResponse.read`` if bytes + are encoded on the wire (e.g, compressed). + """ + return self._fp_bytes_read + + def _init_length(self, request_method): + """ + Set initial length value for Response content if available. + """ + length = self.headers.get('content-length') + + if length is not None: + if self.chunked: + # This Response will fail with an IncompleteRead if it can't be + # received as chunked. This method falls back to attempt reading + # the response before raising an exception. + log.warning("Received response with both Content-Length and " + "Transfer-Encoding set. This is expressly forbidden " + "by RFC 7230 sec 3.3.2. Ignoring Content-Length and " + "attempting to process response as Transfer-Encoding: " + "chunked.") + return None + + try: + # RFC 7230 section 3.3.2 specifies multiple content lengths can + # be sent in a single Content-Length header + # (e.g. Content-Length: 42, 42). This line ensures the values + # are all valid ints and that as long as the `set` length is 1, + # all values are the same. Otherwise, the header is invalid. + lengths = set([int(val) for val in length.split(',')]) + if len(lengths) > 1: + raise InvalidHeader("Content-Length contained multiple " + "unmatching values (%s)" % length) + length = lengths.pop() + except ValueError: + length = None + else: + if length < 0: + length = None + + # Convert status to int for comparison + # In some cases, httplib returns a status of "_UNKNOWN" + try: + status = int(self.status) + except ValueError: + status = 0 + + # Check for responses that shouldn't include a body + if status in (204, 304) or 100 <= status < 200 or request_method == 'HEAD': + length = 0 + + return length + + def _init_decoder(self): + """ + Set-up the _decoder attribute if necessary. + """ + # Note: content-encoding value should be case-insensitive, per RFC 7230 + # Section 3.2 + content_encoding = self.headers.get('content-encoding', '').lower() + if self._decoder is None: + if content_encoding in self.CONTENT_DECODERS: + self._decoder = _get_decoder(content_encoding) + elif ',' in content_encoding: + encodings = [e.strip() for e in content_encoding.split(',') if e.strip() in self.CONTENT_DECODERS] + if len(encodings): + self._decoder = _get_decoder(content_encoding) + + def _decode(self, data, decode_content, flush_decoder): + """ + Decode the data passed in and potentially flush the decoder. + """ + try: + if decode_content and self._decoder: + data = self._decoder.decompress(data) + except (IOError, zlib.error) as e: + content_encoding = self.headers.get('content-encoding', '').lower() + raise DecodeError( + "Received response with content-encoding: %s, but " + "failed to decode it." % content_encoding, e) + + if flush_decoder and decode_content: + data += self._flush_decoder() + + return data + + def _flush_decoder(self): + """ + Flushes the decoder. Should only be called if the decoder is actually + being used. + """ + if self._decoder: + buf = self._decoder.decompress(b'') + return buf + self._decoder.flush() + + return b'' + + @contextmanager + def _error_catcher(self): + """ + Catch low-level python exceptions, instead re-raising urllib3 + variants, so that low-level exceptions are not leaked in the + high-level api. + + On exit, release the connection back to the pool. + """ + clean_exit = False + + try: + try: + yield + + except SocketTimeout: + # FIXME: Ideally we'd like to include the url in the ReadTimeoutError but + # there is yet no clean way to get at it from this context. + raise ReadTimeoutError(self._pool, None, 'Read timed out.') + + except BaseSSLError as e: + # FIXME: Is there a better way to differentiate between SSLErrors? + if 'read operation timed out' not in str(e): # Defensive: + # This shouldn't happen but just in case we're missing an edge + # case, let's avoid swallowing SSL errors. + raise + + raise ReadTimeoutError(self._pool, None, 'Read timed out.') + + except (HTTPException, SocketError) as e: + # This includes IncompleteRead. + raise ProtocolError('Connection broken: %r' % e, e) + + # If no exception is thrown, we should avoid cleaning up + # unnecessarily. + clean_exit = True + finally: + # If we didn't terminate cleanly, we need to throw away our + # connection. + if not clean_exit: + # The response may not be closed but we're not going to use it + # anymore so close it now to ensure that the connection is + # released back to the pool. + if self._original_response: + self._original_response.close() + + # Closing the response may not actually be sufficient to close + # everything, so if we have a hold of the connection close that + # too. + if self._connection: + self._connection.close() + + # If we hold the original response but it's closed now, we should + # return the connection back to the pool. + if self._original_response and self._original_response.isclosed(): + self.release_conn() + + def read(self, amt=None, decode_content=None, cache_content=False): + """ + Similar to :meth:`httplib.HTTPResponse.read`, but with two additional + parameters: ``decode_content`` and ``cache_content``. + + :param amt: + How much of the content to read. If specified, caching is skipped + because it doesn't make sense to cache partial content as the full + response. + + :param decode_content: + If True, will attempt to decode the body based on the + 'content-encoding' header. + + :param cache_content: + If True, will save the returned data such that the same result is + returned despite of the state of the underlying file object. This + is useful if you want the ``.data`` property to continue working + after having ``.read()`` the file object. (Overridden if ``amt`` is + set.) + """ + self._init_decoder() + if decode_content is None: + decode_content = self.decode_content + + if self._fp is None: + return + + flush_decoder = False + data = None + + with self._error_catcher(): + if amt is None: + # cStringIO doesn't like amt=None + data = self._fp.read() + flush_decoder = True + else: + cache_content = False + data = self._fp.read(amt) + if amt != 0 and not data: # Platform-specific: Buggy versions of Python. + # Close the connection when no data is returned + # + # This is redundant to what httplib/http.client _should_ + # already do. However, versions of python released before + # December 15, 2012 (http://bugs.python.org/issue16298) do + # not properly close the connection in all cases. There is + # no harm in redundantly calling close. + self._fp.close() + flush_decoder = True + if self.enforce_content_length and self.length_remaining not in (0, None): + # This is an edge case that httplib failed to cover due + # to concerns of backward compatibility. We're + # addressing it here to make sure IncompleteRead is + # raised during streaming, so all calls with incorrect + # Content-Length are caught. + raise IncompleteRead(self._fp_bytes_read, self.length_remaining) + + if data: + self._fp_bytes_read += len(data) + if self.length_remaining is not None: + self.length_remaining -= len(data) + + data = self._decode(data, decode_content, flush_decoder) + + if cache_content: + self._body = data + + return data + + def stream(self, amt=2**16, decode_content=None): + """ + A generator wrapper for the read() method. A call will block until + ``amt`` bytes have been read from the connection or until the + connection is closed. + + :param amt: + How much of the content to read. The generator will return up to + much data per iteration, but may return less. This is particularly + likely when using compressed data. However, the empty string will + never be returned. + + :param decode_content: + If True, will attempt to decode the body based on the + 'content-encoding' header. + """ + if self.chunked and self.supports_chunked_reads(): + for line in self.read_chunked(amt, decode_content=decode_content): + yield line + else: + while not is_fp_closed(self._fp): + data = self.read(amt=amt, decode_content=decode_content) + + if data: + yield data + + @classmethod + def from_httplib(ResponseCls, r, **response_kw): + """ + Given an :class:`httplib.HTTPResponse` instance ``r``, return a + corresponding :class:`urllib3.response.HTTPResponse` object. + + Remaining parameters are passed to the HTTPResponse constructor, along + with ``original_response=r``. + """ + headers = r.msg + + if not isinstance(headers, HTTPHeaderDict): + if PY3: # Python 3 + headers = HTTPHeaderDict(headers.items()) + else: # Python 2 + headers = HTTPHeaderDict.from_httplib(headers) + + # HTTPResponse objects in Python 3 don't have a .strict attribute + strict = getattr(r, 'strict', 0) + resp = ResponseCls(body=r, + headers=headers, + status=r.status, + version=r.version, + reason=r.reason, + strict=strict, + original_response=r, + **response_kw) + return resp + + # Backwards-compatibility methods for httplib.HTTPResponse + def getheaders(self): + return self.headers + + def getheader(self, name, default=None): + return self.headers.get(name, default) + + # Backwards compatibility for http.cookiejar + def info(self): + return self.headers + + # Overrides from io.IOBase + def close(self): + if not self.closed: + self._fp.close() + + if self._connection: + self._connection.close() + + @property + def closed(self): + if self._fp is None: + return True + elif hasattr(self._fp, 'isclosed'): + return self._fp.isclosed() + elif hasattr(self._fp, 'closed'): + return self._fp.closed + else: + return True + + def fileno(self): + if self._fp is None: + raise IOError("HTTPResponse has no file to get a fileno from") + elif hasattr(self._fp, "fileno"): + return self._fp.fileno() + else: + raise IOError("The file-like object this HTTPResponse is wrapped " + "around has no file descriptor") + + def flush(self): + if self._fp is not None and hasattr(self._fp, 'flush'): + return self._fp.flush() + + def readable(self): + # This method is required for `io` module compatibility. + return True + + def readinto(self, b): + # This method is required for `io` module compatibility. + temp = self.read(len(b)) + if len(temp) == 0: + return 0 + else: + b[:len(temp)] = temp + return len(temp) + + def supports_chunked_reads(self): + """ + Checks if the underlying file-like object looks like a + httplib.HTTPResponse object. We do this by testing for the fp + attribute. If it is present we assume it returns raw chunks as + processed by read_chunked(). + """ + return hasattr(self._fp, 'fp') + + def _update_chunk_length(self): + # First, we'll figure out length of a chunk and then + # we'll try to read it from socket. + if self.chunk_left is not None: + return + line = self._fp.fp.readline() + line = line.split(b';', 1)[0] + try: + self.chunk_left = int(line, 16) + except ValueError: + # Invalid chunked protocol response, abort. + self.close() + raise httplib.IncompleteRead(line) + + def _handle_chunk(self, amt): + returned_chunk = None + if amt is None: + chunk = self._fp._safe_read(self.chunk_left) + returned_chunk = chunk + self._fp._safe_read(2) # Toss the CRLF at the end of the chunk. + self.chunk_left = None + elif amt < self.chunk_left: + value = self._fp._safe_read(amt) + self.chunk_left = self.chunk_left - amt + returned_chunk = value + elif amt == self.chunk_left: + value = self._fp._safe_read(amt) + self._fp._safe_read(2) # Toss the CRLF at the end of the chunk. + self.chunk_left = None + returned_chunk = value + else: # amt > self.chunk_left + returned_chunk = self._fp._safe_read(self.chunk_left) + self._fp._safe_read(2) # Toss the CRLF at the end of the chunk. + self.chunk_left = None + return returned_chunk + + def read_chunked(self, amt=None, decode_content=None): + """ + Similar to :meth:`HTTPResponse.read`, but with an additional + parameter: ``decode_content``. + + :param amt: + How much of the content to read. If specified, caching is skipped + because it doesn't make sense to cache partial content as the full + response. + + :param decode_content: + If True, will attempt to decode the body based on the + 'content-encoding' header. + """ + self._init_decoder() + # FIXME: Rewrite this method and make it a class with a better structured logic. + if not self.chunked: + raise ResponseNotChunked( + "Response is not chunked. " + "Header 'transfer-encoding: chunked' is missing.") + if not self.supports_chunked_reads(): + raise BodyNotHttplibCompatible( + "Body should be httplib.HTTPResponse like. " + "It should have have an fp attribute which returns raw chunks.") + + with self._error_catcher(): + # Don't bother reading the body of a HEAD request. + if self._original_response and is_response_to_head(self._original_response): + self._original_response.close() + return + + # If a response is already read and closed + # then return immediately. + if self._fp.fp is None: + return + + while True: + self._update_chunk_length() + if self.chunk_left == 0: + break + chunk = self._handle_chunk(amt) + decoded = self._decode(chunk, decode_content=decode_content, + flush_decoder=False) + if decoded: + yield decoded + + if decode_content: + # On CPython and PyPy, we should never need to flush the + # decoder. However, on Jython we *might* need to, so + # lets defensively do it anyway. + decoded = self._flush_decoder() + if decoded: # Platform-specific: Jython. + yield decoded + + # Chunk content ends with \r\n: discard it. + while True: + line = self._fp.fp.readline() + if not line: + # Some sites may not end with '\r\n'. + break + if line == b'\r\n': + break + + # We read everything; close the "file". + if self._original_response: + self._original_response.close() + + def geturl(self): + """ + Returns the URL that was the source of this response. + If the request that generated this response redirected, this method + will return the final redirect location. + """ + if self.retries is not None and len(self.retries.history): + return self.retries.history[-1].redirect_location + else: + return self._request_url diff --git a/venv2/lib/python2.7/site-packages/pip/_vendor/urllib3/util/__init__.py b/venv2/lib/python2.7/site-packages/pip/_vendor/urllib3/util/__init__.py new file mode 100644 index 0000000..2f2770b --- /dev/null +++ b/venv2/lib/python2.7/site-packages/pip/_vendor/urllib3/util/__init__.py @@ -0,0 +1,54 @@ +from __future__ import absolute_import +# For backwards compatibility, provide imports that used to be here. +from .connection import is_connection_dropped +from .request import make_headers +from .response import is_fp_closed +from .ssl_ import ( + SSLContext, + HAS_SNI, + IS_PYOPENSSL, + IS_SECURETRANSPORT, + assert_fingerprint, + resolve_cert_reqs, + resolve_ssl_version, + ssl_wrap_socket, +) +from .timeout import ( + current_time, + Timeout, +) + +from .retry import Retry +from .url import ( + get_host, + parse_url, + split_first, + Url, +) +from .wait import ( + wait_for_read, + wait_for_write +) + +__all__ = ( + 'HAS_SNI', + 'IS_PYOPENSSL', + 'IS_SECURETRANSPORT', + 'SSLContext', + 'Retry', + 'Timeout', + 'Url', + 'assert_fingerprint', + 'current_time', + 'is_connection_dropped', + 'is_fp_closed', + 'get_host', + 'parse_url', + 'make_headers', + 'resolve_cert_reqs', + 'resolve_ssl_version', + 'split_first', + 'ssl_wrap_socket', + 'wait_for_read', + 'wait_for_write' +) diff --git a/venv2/lib/python2.7/site-packages/pip/_vendor/urllib3/util/connection.py b/venv2/lib/python2.7/site-packages/pip/_vendor/urllib3/util/connection.py new file mode 100644 index 0000000..5ad70b2 --- /dev/null +++ b/venv2/lib/python2.7/site-packages/pip/_vendor/urllib3/util/connection.py @@ -0,0 +1,134 @@ +from __future__ import absolute_import +import socket +from .wait import NoWayToWaitForSocketError, wait_for_read +from ..contrib import _appengine_environ + + +def is_connection_dropped(conn): # Platform-specific + """ + Returns True if the connection is dropped and should be closed. + + :param conn: + :class:`httplib.HTTPConnection` object. + + Note: For platforms like AppEngine, this will always return ``False`` to + let the platform handle connection recycling transparently for us. + """ + sock = getattr(conn, 'sock', False) + if sock is False: # Platform-specific: AppEngine + return False + if sock is None: # Connection already closed (such as by httplib). + return True + try: + # Returns True if readable, which here means it's been dropped + return wait_for_read(sock, timeout=0.0) + except NoWayToWaitForSocketError: # Platform-specific: AppEngine + return False + + +# This function is copied from socket.py in the Python 2.7 standard +# library test suite. Added to its signature is only `socket_options`. +# One additional modification is that we avoid binding to IPv6 servers +# discovered in DNS if the system doesn't have IPv6 functionality. +def create_connection(address, timeout=socket._GLOBAL_DEFAULT_TIMEOUT, + source_address=None, socket_options=None): + """Connect to *address* and return the socket object. + + Convenience function. Connect to *address* (a 2-tuple ``(host, + port)``) and return the socket object. Passing the optional + *timeout* parameter will set the timeout on the socket instance + before attempting to connect. If no *timeout* is supplied, the + global default timeout setting returned by :func:`getdefaulttimeout` + is used. If *source_address* is set it must be a tuple of (host, port) + for the socket to bind as a source address before making the connection. + An host of '' or port 0 tells the OS to use the default. + """ + + host, port = address + if host.startswith('['): + host = host.strip('[]') + err = None + + # Using the value from allowed_gai_family() in the context of getaddrinfo lets + # us select whether to work with IPv4 DNS records, IPv6 records, or both. + # The original create_connection function always returns all records. + family = allowed_gai_family() + + for res in socket.getaddrinfo(host, port, family, socket.SOCK_STREAM): + af, socktype, proto, canonname, sa = res + sock = None + try: + sock = socket.socket(af, socktype, proto) + + # If provided, set socket level options before connecting. + _set_socket_options(sock, socket_options) + + if timeout is not socket._GLOBAL_DEFAULT_TIMEOUT: + sock.settimeout(timeout) + if source_address: + sock.bind(source_address) + sock.connect(sa) + return sock + + except socket.error as e: + err = e + if sock is not None: + sock.close() + sock = None + + if err is not None: + raise err + + raise socket.error("getaddrinfo returns an empty list") + + +def _set_socket_options(sock, options): + if options is None: + return + + for opt in options: + sock.setsockopt(*opt) + + +def allowed_gai_family(): + """This function is designed to work in the context of + getaddrinfo, where family=socket.AF_UNSPEC is the default and + will perform a DNS search for both IPv6 and IPv4 records.""" + + family = socket.AF_INET + if HAS_IPV6: + family = socket.AF_UNSPEC + return family + + +def _has_ipv6(host): + """ Returns True if the system can bind an IPv6 address. """ + sock = None + has_ipv6 = False + + # App Engine doesn't support IPV6 sockets and actually has a quota on the + # number of sockets that can be used, so just early out here instead of + # creating a socket needlessly. + # See https://github.com/urllib3/urllib3/issues/1446 + if _appengine_environ.is_appengine_sandbox(): + return False + + if socket.has_ipv6: + # has_ipv6 returns true if cPython was compiled with IPv6 support. + # It does not tell us if the system has IPv6 support enabled. To + # determine that we must bind to an IPv6 address. + # https://github.com/shazow/urllib3/pull/611 + # https://bugs.python.org/issue658327 + try: + sock = socket.socket(socket.AF_INET6) + sock.bind((host, 0)) + has_ipv6 = True + except Exception: + pass + + if sock: + sock.close() + return has_ipv6 + + +HAS_IPV6 = _has_ipv6('::1') diff --git a/venv2/lib/python2.7/site-packages/pip/_vendor/urllib3/util/queue.py b/venv2/lib/python2.7/site-packages/pip/_vendor/urllib3/util/queue.py new file mode 100644 index 0000000..d3d379a --- /dev/null +++ b/venv2/lib/python2.7/site-packages/pip/_vendor/urllib3/util/queue.py @@ -0,0 +1,21 @@ +import collections +from ..packages import six +from ..packages.six.moves import queue + +if six.PY2: + # Queue is imported for side effects on MS Windows. See issue #229. + import Queue as _unused_module_Queue # noqa: F401 + + +class LifoQueue(queue.Queue): + def _init(self, _): + self.queue = collections.deque() + + def _qsize(self, len=len): + return len(self.queue) + + def _put(self, item): + self.queue.append(item) + + def _get(self): + return self.queue.pop() diff --git a/venv2/lib/python2.7/site-packages/pip/_vendor/urllib3/util/request.py b/venv2/lib/python2.7/site-packages/pip/_vendor/urllib3/util/request.py new file mode 100644 index 0000000..3ddfcd5 --- /dev/null +++ b/venv2/lib/python2.7/site-packages/pip/_vendor/urllib3/util/request.py @@ -0,0 +1,118 @@ +from __future__ import absolute_import +from base64 import b64encode + +from ..packages.six import b, integer_types +from ..exceptions import UnrewindableBodyError + +ACCEPT_ENCODING = 'gzip,deflate' +_FAILEDTELL = object() + + +def make_headers(keep_alive=None, accept_encoding=None, user_agent=None, + basic_auth=None, proxy_basic_auth=None, disable_cache=None): + """ + Shortcuts for generating request headers. + + :param keep_alive: + If ``True``, adds 'connection: keep-alive' header. + + :param accept_encoding: + Can be a boolean, list, or string. + ``True`` translates to 'gzip,deflate'. + List will get joined by comma. + String will be used as provided. + + :param user_agent: + String representing the user-agent you want, such as + "python-urllib3/0.6" + + :param basic_auth: + Colon-separated username:password string for 'authorization: basic ...' + auth header. + + :param proxy_basic_auth: + Colon-separated username:password string for 'proxy-authorization: basic ...' + auth header. + + :param disable_cache: + If ``True``, adds 'cache-control: no-cache' header. + + Example:: + + >>> make_headers(keep_alive=True, user_agent="Batman/1.0") + {'connection': 'keep-alive', 'user-agent': 'Batman/1.0'} + >>> make_headers(accept_encoding=True) + {'accept-encoding': 'gzip,deflate'} + """ + headers = {} + if accept_encoding: + if isinstance(accept_encoding, str): + pass + elif isinstance(accept_encoding, list): + accept_encoding = ','.join(accept_encoding) + else: + accept_encoding = ACCEPT_ENCODING + headers['accept-encoding'] = accept_encoding + + if user_agent: + headers['user-agent'] = user_agent + + if keep_alive: + headers['connection'] = 'keep-alive' + + if basic_auth: + headers['authorization'] = 'Basic ' + \ + b64encode(b(basic_auth)).decode('utf-8') + + if proxy_basic_auth: + headers['proxy-authorization'] = 'Basic ' + \ + b64encode(b(proxy_basic_auth)).decode('utf-8') + + if disable_cache: + headers['cache-control'] = 'no-cache' + + return headers + + +def set_file_position(body, pos): + """ + If a position is provided, move file to that point. + Otherwise, we'll attempt to record a position for future use. + """ + if pos is not None: + rewind_body(body, pos) + elif getattr(body, 'tell', None) is not None: + try: + pos = body.tell() + except (IOError, OSError): + # This differentiates from None, allowing us to catch + # a failed `tell()` later when trying to rewind the body. + pos = _FAILEDTELL + + return pos + + +def rewind_body(body, body_pos): + """ + Attempt to rewind body to a certain position. + Primarily used for request redirects and retries. + + :param body: + File-like object that supports seek. + + :param int pos: + Position to seek to in file. + """ + body_seek = getattr(body, 'seek', None) + if body_seek is not None and isinstance(body_pos, integer_types): + try: + body_seek(body_pos) + except (IOError, OSError): + raise UnrewindableBodyError("An error occurred when rewinding request " + "body for redirect/retry.") + elif body_pos is _FAILEDTELL: + raise UnrewindableBodyError("Unable to record file position for rewinding " + "request body during a redirect/retry.") + else: + raise ValueError("body_pos must be of type integer, " + "instead it was %s." % type(body_pos)) diff --git a/venv2/lib/python2.7/site-packages/pip/_vendor/urllib3/util/response.py b/venv2/lib/python2.7/site-packages/pip/_vendor/urllib3/util/response.py new file mode 100644 index 0000000..3d54864 --- /dev/null +++ b/venv2/lib/python2.7/site-packages/pip/_vendor/urllib3/util/response.py @@ -0,0 +1,87 @@ +from __future__ import absolute_import +from ..packages.six.moves import http_client as httplib + +from ..exceptions import HeaderParsingError + + +def is_fp_closed(obj): + """ + Checks whether a given file-like object is closed. + + :param obj: + The file-like object to check. + """ + + try: + # Check `isclosed()` first, in case Python3 doesn't set `closed`. + # GH Issue #928 + return obj.isclosed() + except AttributeError: + pass + + try: + # Check via the official file-like-object way. + return obj.closed + except AttributeError: + pass + + try: + # Check if the object is a container for another file-like object that + # gets released on exhaustion (e.g. HTTPResponse). + return obj.fp is None + except AttributeError: + pass + + raise ValueError("Unable to determine whether fp is closed.") + + +def assert_header_parsing(headers): + """ + Asserts whether all headers have been successfully parsed. + Extracts encountered errors from the result of parsing headers. + + Only works on Python 3. + + :param headers: Headers to verify. + :type headers: `httplib.HTTPMessage`. + + :raises urllib3.exceptions.HeaderParsingError: + If parsing errors are found. + """ + + # This will fail silently if we pass in the wrong kind of parameter. + # To make debugging easier add an explicit check. + if not isinstance(headers, httplib.HTTPMessage): + raise TypeError('expected httplib.Message, got {0}.'.format( + type(headers))) + + defects = getattr(headers, 'defects', None) + get_payload = getattr(headers, 'get_payload', None) + + unparsed_data = None + if get_payload: + # get_payload is actually email.message.Message.get_payload; + # we're only interested in the result if it's not a multipart message + if not headers.is_multipart(): + payload = get_payload() + + if isinstance(payload, (bytes, str)): + unparsed_data = payload + + if defects or unparsed_data: + raise HeaderParsingError(defects=defects, unparsed_data=unparsed_data) + + +def is_response_to_head(response): + """ + Checks whether the request of a response has been a HEAD-request. + Handles the quirks of AppEngine. + + :param conn: + :type conn: :class:`httplib.HTTPResponse` + """ + # FIXME: Can we do this somehow without accessing private httplib _method? + method = response._method + if isinstance(method, int): # Platform-specific: Appengine + return method == 3 + return method.upper() == 'HEAD' diff --git a/venv2/lib/python2.7/site-packages/pip/_vendor/urllib3/util/retry.py b/venv2/lib/python2.7/site-packages/pip/_vendor/urllib3/util/retry.py new file mode 100644 index 0000000..e7d0abd --- /dev/null +++ b/venv2/lib/python2.7/site-packages/pip/_vendor/urllib3/util/retry.py @@ -0,0 +1,411 @@ +from __future__ import absolute_import +import time +import logging +from collections import namedtuple +from itertools import takewhile +import email +import re + +from ..exceptions import ( + ConnectTimeoutError, + MaxRetryError, + ProtocolError, + ReadTimeoutError, + ResponseError, + InvalidHeader, +) +from ..packages import six + + +log = logging.getLogger(__name__) + + +# Data structure for representing the metadata of requests that result in a retry. +RequestHistory = namedtuple('RequestHistory', ["method", "url", "error", + "status", "redirect_location"]) + + +class Retry(object): + """ Retry configuration. + + Each retry attempt will create a new Retry object with updated values, so + they can be safely reused. + + Retries can be defined as a default for a pool:: + + retries = Retry(connect=5, read=2, redirect=5) + http = PoolManager(retries=retries) + response = http.request('GET', 'http://example.com/') + + Or per-request (which overrides the default for the pool):: + + response = http.request('GET', 'http://example.com/', retries=Retry(10)) + + Retries can be disabled by passing ``False``:: + + response = http.request('GET', 'http://example.com/', retries=False) + + Errors will be wrapped in :class:`~urllib3.exceptions.MaxRetryError` unless + retries are disabled, in which case the causing exception will be raised. + + :param int total: + Total number of retries to allow. Takes precedence over other counts. + + Set to ``None`` to remove this constraint and fall back on other + counts. It's a good idea to set this to some sensibly-high value to + account for unexpected edge cases and avoid infinite retry loops. + + Set to ``0`` to fail on the first retry. + + Set to ``False`` to disable and imply ``raise_on_redirect=False``. + + :param int connect: + How many connection-related errors to retry on. + + These are errors raised before the request is sent to the remote server, + which we assume has not triggered the server to process the request. + + Set to ``0`` to fail on the first retry of this type. + + :param int read: + How many times to retry on read errors. + + These errors are raised after the request was sent to the server, so the + request may have side-effects. + + Set to ``0`` to fail on the first retry of this type. + + :param int redirect: + How many redirects to perform. Limit this to avoid infinite redirect + loops. + + A redirect is a HTTP response with a status code 301, 302, 303, 307 or + 308. + + Set to ``0`` to fail on the first retry of this type. + + Set to ``False`` to disable and imply ``raise_on_redirect=False``. + + :param int status: + How many times to retry on bad status codes. + + These are retries made on responses, where status code matches + ``status_forcelist``. + + Set to ``0`` to fail on the first retry of this type. + + :param iterable method_whitelist: + Set of uppercased HTTP method verbs that we should retry on. + + By default, we only retry on methods which are considered to be + idempotent (multiple requests with the same parameters end with the + same state). See :attr:`Retry.DEFAULT_METHOD_WHITELIST`. + + Set to a ``False`` value to retry on any verb. + + :param iterable status_forcelist: + A set of integer HTTP status codes that we should force a retry on. + A retry is initiated if the request method is in ``method_whitelist`` + and the response status code is in ``status_forcelist``. + + By default, this is disabled with ``None``. + + :param float backoff_factor: + A backoff factor to apply between attempts after the second try + (most errors are resolved immediately by a second try without a + delay). urllib3 will sleep for:: + + {backoff factor} * (2 ** ({number of total retries} - 1)) + + seconds. If the backoff_factor is 0.1, then :func:`.sleep` will sleep + for [0.0s, 0.2s, 0.4s, ...] between retries. It will never be longer + than :attr:`Retry.BACKOFF_MAX`. + + By default, backoff is disabled (set to 0). + + :param bool raise_on_redirect: Whether, if the number of redirects is + exhausted, to raise a MaxRetryError, or to return a response with a + response code in the 3xx range. + + :param bool raise_on_status: Similar meaning to ``raise_on_redirect``: + whether we should raise an exception, or return a response, + if status falls in ``status_forcelist`` range and retries have + been exhausted. + + :param tuple history: The history of the request encountered during + each call to :meth:`~Retry.increment`. The list is in the order + the requests occurred. Each list item is of class :class:`RequestHistory`. + + :param bool respect_retry_after_header: + Whether to respect Retry-After header on status codes defined as + :attr:`Retry.RETRY_AFTER_STATUS_CODES` or not. + + :param iterable remove_headers_on_redirect: + Sequence of headers to remove from the request when a response + indicating a redirect is returned before firing off the redirected + request. + """ + + DEFAULT_METHOD_WHITELIST = frozenset([ + 'HEAD', 'GET', 'PUT', 'DELETE', 'OPTIONS', 'TRACE']) + + RETRY_AFTER_STATUS_CODES = frozenset([413, 429, 503]) + + DEFAULT_REDIRECT_HEADERS_BLACKLIST = frozenset(['Authorization']) + + #: Maximum backoff time. + BACKOFF_MAX = 120 + + def __init__(self, total=10, connect=None, read=None, redirect=None, status=None, + method_whitelist=DEFAULT_METHOD_WHITELIST, status_forcelist=None, + backoff_factor=0, raise_on_redirect=True, raise_on_status=True, + history=None, respect_retry_after_header=True, + remove_headers_on_redirect=DEFAULT_REDIRECT_HEADERS_BLACKLIST): + + self.total = total + self.connect = connect + self.read = read + self.status = status + + if redirect is False or total is False: + redirect = 0 + raise_on_redirect = False + + self.redirect = redirect + self.status_forcelist = status_forcelist or set() + self.method_whitelist = method_whitelist + self.backoff_factor = backoff_factor + self.raise_on_redirect = raise_on_redirect + self.raise_on_status = raise_on_status + self.history = history or tuple() + self.respect_retry_after_header = respect_retry_after_header + self.remove_headers_on_redirect = remove_headers_on_redirect + + def new(self, **kw): + params = dict( + total=self.total, + connect=self.connect, read=self.read, redirect=self.redirect, status=self.status, + method_whitelist=self.method_whitelist, + status_forcelist=self.status_forcelist, + backoff_factor=self.backoff_factor, + raise_on_redirect=self.raise_on_redirect, + raise_on_status=self.raise_on_status, + history=self.history, + remove_headers_on_redirect=self.remove_headers_on_redirect + ) + params.update(kw) + return type(self)(**params) + + @classmethod + def from_int(cls, retries, redirect=True, default=None): + """ Backwards-compatibility for the old retries format.""" + if retries is None: + retries = default if default is not None else cls.DEFAULT + + if isinstance(retries, Retry): + return retries + + redirect = bool(redirect) and None + new_retries = cls(retries, redirect=redirect) + log.debug("Converted retries value: %r -> %r", retries, new_retries) + return new_retries + + def get_backoff_time(self): + """ Formula for computing the current backoff + + :rtype: float + """ + # We want to consider only the last consecutive errors sequence (Ignore redirects). + consecutive_errors_len = len(list(takewhile(lambda x: x.redirect_location is None, + reversed(self.history)))) + if consecutive_errors_len <= 1: + return 0 + + backoff_value = self.backoff_factor * (2 ** (consecutive_errors_len - 1)) + return min(self.BACKOFF_MAX, backoff_value) + + def parse_retry_after(self, retry_after): + # Whitespace: https://tools.ietf.org/html/rfc7230#section-3.2.4 + if re.match(r"^\s*[0-9]+\s*$", retry_after): + seconds = int(retry_after) + else: + retry_date_tuple = email.utils.parsedate(retry_after) + if retry_date_tuple is None: + raise InvalidHeader("Invalid Retry-After header: %s" % retry_after) + retry_date = time.mktime(retry_date_tuple) + seconds = retry_date - time.time() + + if seconds < 0: + seconds = 0 + + return seconds + + def get_retry_after(self, response): + """ Get the value of Retry-After in seconds. """ + + retry_after = response.getheader("Retry-After") + + if retry_after is None: + return None + + return self.parse_retry_after(retry_after) + + def sleep_for_retry(self, response=None): + retry_after = self.get_retry_after(response) + if retry_after: + time.sleep(retry_after) + return True + + return False + + def _sleep_backoff(self): + backoff = self.get_backoff_time() + if backoff <= 0: + return + time.sleep(backoff) + + def sleep(self, response=None): + """ Sleep between retry attempts. + + This method will respect a server's ``Retry-After`` response header + and sleep the duration of the time requested. If that is not present, it + will use an exponential backoff. By default, the backoff factor is 0 and + this method will return immediately. + """ + + if response: + slept = self.sleep_for_retry(response) + if slept: + return + + self._sleep_backoff() + + def _is_connection_error(self, err): + """ Errors when we're fairly sure that the server did not receive the + request, so it should be safe to retry. + """ + return isinstance(err, ConnectTimeoutError) + + def _is_read_error(self, err): + """ Errors that occur after the request has been started, so we should + assume that the server began processing it. + """ + return isinstance(err, (ReadTimeoutError, ProtocolError)) + + def _is_method_retryable(self, method): + """ Checks if a given HTTP method should be retried upon, depending if + it is included on the method whitelist. + """ + if self.method_whitelist and method.upper() not in self.method_whitelist: + return False + + return True + + def is_retry(self, method, status_code, has_retry_after=False): + """ Is this method/status code retryable? (Based on whitelists and control + variables such as the number of total retries to allow, whether to + respect the Retry-After header, whether this header is present, and + whether the returned status code is on the list of status codes to + be retried upon on the presence of the aforementioned header) + """ + if not self._is_method_retryable(method): + return False + + if self.status_forcelist and status_code in self.status_forcelist: + return True + + return (self.total and self.respect_retry_after_header and + has_retry_after and (status_code in self.RETRY_AFTER_STATUS_CODES)) + + def is_exhausted(self): + """ Are we out of retries? """ + retry_counts = (self.total, self.connect, self.read, self.redirect, self.status) + retry_counts = list(filter(None, retry_counts)) + if not retry_counts: + return False + + return min(retry_counts) < 0 + + def increment(self, method=None, url=None, response=None, error=None, + _pool=None, _stacktrace=None): + """ Return a new Retry object with incremented retry counters. + + :param response: A response object, or None, if the server did not + return a response. + :type response: :class:`~urllib3.response.HTTPResponse` + :param Exception error: An error encountered during the request, or + None if the response was received successfully. + + :return: A new ``Retry`` object. + """ + if self.total is False and error: + # Disabled, indicate to re-raise the error. + raise six.reraise(type(error), error, _stacktrace) + + total = self.total + if total is not None: + total -= 1 + + connect = self.connect + read = self.read + redirect = self.redirect + status_count = self.status + cause = 'unknown' + status = None + redirect_location = None + + if error and self._is_connection_error(error): + # Connect retry? + if connect is False: + raise six.reraise(type(error), error, _stacktrace) + elif connect is not None: + connect -= 1 + + elif error and self._is_read_error(error): + # Read retry? + if read is False or not self._is_method_retryable(method): + raise six.reraise(type(error), error, _stacktrace) + elif read is not None: + read -= 1 + + elif response and response.get_redirect_location(): + # Redirect retry? + if redirect is not None: + redirect -= 1 + cause = 'too many redirects' + redirect_location = response.get_redirect_location() + status = response.status + + else: + # Incrementing because of a server error like a 500 in + # status_forcelist and a the given method is in the whitelist + cause = ResponseError.GENERIC_ERROR + if response and response.status: + if status_count is not None: + status_count -= 1 + cause = ResponseError.SPECIFIC_ERROR.format( + status_code=response.status) + status = response.status + + history = self.history + (RequestHistory(method, url, error, status, redirect_location),) + + new_retry = self.new( + total=total, + connect=connect, read=read, redirect=redirect, status=status_count, + history=history) + + if new_retry.is_exhausted(): + raise MaxRetryError(_pool, url, error or ResponseError(cause)) + + log.debug("Incremented Retry for (url='%s'): %r", url, new_retry) + + return new_retry + + def __repr__(self): + return ('{cls.__name__}(total={self.total}, connect={self.connect}, ' + 'read={self.read}, redirect={self.redirect}, status={self.status})').format( + cls=type(self), self=self) + + +# For backwards compatibility (equivalent to pre-v1.9): +Retry.DEFAULT = Retry(3) diff --git a/venv2/lib/python2.7/site-packages/pip/_vendor/urllib3/util/ssl_.py b/venv2/lib/python2.7/site-packages/pip/_vendor/urllib3/util/ssl_.py new file mode 100644 index 0000000..dfc553f --- /dev/null +++ b/venv2/lib/python2.7/site-packages/pip/_vendor/urllib3/util/ssl_.py @@ -0,0 +1,381 @@ +from __future__ import absolute_import +import errno +import warnings +import hmac +import socket + +from binascii import hexlify, unhexlify +from hashlib import md5, sha1, sha256 + +from ..exceptions import SSLError, InsecurePlatformWarning, SNIMissingWarning +from ..packages import six + + +SSLContext = None +HAS_SNI = False +IS_PYOPENSSL = False +IS_SECURETRANSPORT = False + +# Maps the length of a digest to a possible hash function producing this digest +HASHFUNC_MAP = { + 32: md5, + 40: sha1, + 64: sha256, +} + + +def _const_compare_digest_backport(a, b): + """ + Compare two digests of equal length in constant time. + + The digests must be of type str/bytes. + Returns True if the digests match, and False otherwise. + """ + result = abs(len(a) - len(b)) + for l, r in zip(bytearray(a), bytearray(b)): + result |= l ^ r + return result == 0 + + +_const_compare_digest = getattr(hmac, 'compare_digest', + _const_compare_digest_backport) + + +try: # Test for SSL features + import ssl + from ssl import wrap_socket, CERT_NONE, PROTOCOL_SSLv23 + from ssl import HAS_SNI # Has SNI? +except ImportError: + pass + + +try: + from ssl import OP_NO_SSLv2, OP_NO_SSLv3, OP_NO_COMPRESSION +except ImportError: + OP_NO_SSLv2, OP_NO_SSLv3 = 0x1000000, 0x2000000 + OP_NO_COMPRESSION = 0x20000 + + +# Python 2.7 doesn't have inet_pton on non-Linux so we fallback on inet_aton in +# those cases. This means that we can only detect IPv4 addresses in this case. +if hasattr(socket, 'inet_pton'): + inet_pton = socket.inet_pton +else: + # Maybe we can use ipaddress if the user has urllib3[secure]? + try: + from pip._vendor import ipaddress + + def inet_pton(_, host): + if isinstance(host, bytes): + host = host.decode('ascii') + return ipaddress.ip_address(host) + + except ImportError: # Platform-specific: Non-Linux + def inet_pton(_, host): + return socket.inet_aton(host) + + +# A secure default. +# Sources for more information on TLS ciphers: +# +# - https://wiki.mozilla.org/Security/Server_Side_TLS +# - https://www.ssllabs.com/projects/best-practices/index.html +# - https://hynek.me/articles/hardening-your-web-servers-ssl-ciphers/ +# +# The general intent is: +# - Prefer TLS 1.3 cipher suites +# - prefer cipher suites that offer perfect forward secrecy (DHE/ECDHE), +# - prefer ECDHE over DHE for better performance, +# - prefer any AES-GCM and ChaCha20 over any AES-CBC for better performance and +# security, +# - prefer AES-GCM over ChaCha20 because hardware-accelerated AES is common, +# - disable NULL authentication, MD5 MACs and DSS for security reasons. +DEFAULT_CIPHERS = ':'.join([ + 'TLS13-AES-256-GCM-SHA384', + 'TLS13-CHACHA20-POLY1305-SHA256', + 'TLS13-AES-128-GCM-SHA256', + 'ECDH+AESGCM', + 'ECDH+CHACHA20', + 'DH+AESGCM', + 'DH+CHACHA20', + 'ECDH+AES256', + 'DH+AES256', + 'ECDH+AES128', + 'DH+AES', + 'RSA+AESGCM', + 'RSA+AES', + '!aNULL', + '!eNULL', + '!MD5', +]) + +try: + from ssl import SSLContext # Modern SSL? +except ImportError: + import sys + + class SSLContext(object): # Platform-specific: Python 2 + def __init__(self, protocol_version): + self.protocol = protocol_version + # Use default values from a real SSLContext + self.check_hostname = False + self.verify_mode = ssl.CERT_NONE + self.ca_certs = None + self.options = 0 + self.certfile = None + self.keyfile = None + self.ciphers = None + + def load_cert_chain(self, certfile, keyfile): + self.certfile = certfile + self.keyfile = keyfile + + def load_verify_locations(self, cafile=None, capath=None): + self.ca_certs = cafile + + if capath is not None: + raise SSLError("CA directories not supported in older Pythons") + + def set_ciphers(self, cipher_suite): + self.ciphers = cipher_suite + + def wrap_socket(self, socket, server_hostname=None, server_side=False): + warnings.warn( + 'A true SSLContext object is not available. This prevents ' + 'urllib3 from configuring SSL appropriately and may cause ' + 'certain SSL connections to fail. You can upgrade to a newer ' + 'version of Python to solve this. For more information, see ' + 'https://urllib3.readthedocs.io/en/latest/advanced-usage.html' + '#ssl-warnings', + InsecurePlatformWarning + ) + kwargs = { + 'keyfile': self.keyfile, + 'certfile': self.certfile, + 'ca_certs': self.ca_certs, + 'cert_reqs': self.verify_mode, + 'ssl_version': self.protocol, + 'server_side': server_side, + } + return wrap_socket(socket, ciphers=self.ciphers, **kwargs) + + +def assert_fingerprint(cert, fingerprint): + """ + Checks if given fingerprint matches the supplied certificate. + + :param cert: + Certificate as bytes object. + :param fingerprint: + Fingerprint as string of hexdigits, can be interspersed by colons. + """ + + fingerprint = fingerprint.replace(':', '').lower() + digest_length = len(fingerprint) + hashfunc = HASHFUNC_MAP.get(digest_length) + if not hashfunc: + raise SSLError( + 'Fingerprint of invalid length: {0}'.format(fingerprint)) + + # We need encode() here for py32; works on py2 and p33. + fingerprint_bytes = unhexlify(fingerprint.encode()) + + cert_digest = hashfunc(cert).digest() + + if not _const_compare_digest(cert_digest, fingerprint_bytes): + raise SSLError('Fingerprints did not match. Expected "{0}", got "{1}".' + .format(fingerprint, hexlify(cert_digest))) + + +def resolve_cert_reqs(candidate): + """ + Resolves the argument to a numeric constant, which can be passed to + the wrap_socket function/method from the ssl module. + Defaults to :data:`ssl.CERT_NONE`. + If given a string it is assumed to be the name of the constant in the + :mod:`ssl` module or its abbreviation. + (So you can specify `REQUIRED` instead of `CERT_REQUIRED`. + If it's neither `None` nor a string we assume it is already the numeric + constant which can directly be passed to wrap_socket. + """ + if candidate is None: + return CERT_NONE + + if isinstance(candidate, str): + res = getattr(ssl, candidate, None) + if res is None: + res = getattr(ssl, 'CERT_' + candidate) + return res + + return candidate + + +def resolve_ssl_version(candidate): + """ + like resolve_cert_reqs + """ + if candidate is None: + return PROTOCOL_SSLv23 + + if isinstance(candidate, str): + res = getattr(ssl, candidate, None) + if res is None: + res = getattr(ssl, 'PROTOCOL_' + candidate) + return res + + return candidate + + +def create_urllib3_context(ssl_version=None, cert_reqs=None, + options=None, ciphers=None): + """All arguments have the same meaning as ``ssl_wrap_socket``. + + By default, this function does a lot of the same work that + ``ssl.create_default_context`` does on Python 3.4+. It: + + - Disables SSLv2, SSLv3, and compression + - Sets a restricted set of server ciphers + + If you wish to enable SSLv3, you can do:: + + from pip._vendor.urllib3.util import ssl_ + context = ssl_.create_urllib3_context() + context.options &= ~ssl_.OP_NO_SSLv3 + + You can do the same to enable compression (substituting ``COMPRESSION`` + for ``SSLv3`` in the last line above). + + :param ssl_version: + The desired protocol version to use. This will default to + PROTOCOL_SSLv23 which will negotiate the highest protocol that both + the server and your installation of OpenSSL support. + :param cert_reqs: + Whether to require the certificate verification. This defaults to + ``ssl.CERT_REQUIRED``. + :param options: + Specific OpenSSL options. These default to ``ssl.OP_NO_SSLv2``, + ``ssl.OP_NO_SSLv3``, ``ssl.OP_NO_COMPRESSION``. + :param ciphers: + Which cipher suites to allow the server to select. + :returns: + Constructed SSLContext object with specified options + :rtype: SSLContext + """ + context = SSLContext(ssl_version or ssl.PROTOCOL_SSLv23) + + context.set_ciphers(ciphers or DEFAULT_CIPHERS) + + # Setting the default here, as we may have no ssl module on import + cert_reqs = ssl.CERT_REQUIRED if cert_reqs is None else cert_reqs + + if options is None: + options = 0 + # SSLv2 is easily broken and is considered harmful and dangerous + options |= OP_NO_SSLv2 + # SSLv3 has several problems and is now dangerous + options |= OP_NO_SSLv3 + # Disable compression to prevent CRIME attacks for OpenSSL 1.0+ + # (issue #309) + options |= OP_NO_COMPRESSION + + context.options |= options + + context.verify_mode = cert_reqs + if getattr(context, 'check_hostname', None) is not None: # Platform-specific: Python 3.2 + # We do our own verification, including fingerprints and alternative + # hostnames. So disable it here + context.check_hostname = False + return context + + +def ssl_wrap_socket(sock, keyfile=None, certfile=None, cert_reqs=None, + ca_certs=None, server_hostname=None, + ssl_version=None, ciphers=None, ssl_context=None, + ca_cert_dir=None): + """ + All arguments except for server_hostname, ssl_context, and ca_cert_dir have + the same meaning as they do when using :func:`ssl.wrap_socket`. + + :param server_hostname: + When SNI is supported, the expected hostname of the certificate + :param ssl_context: + A pre-made :class:`SSLContext` object. If none is provided, one will + be created using :func:`create_urllib3_context`. + :param ciphers: + A string of ciphers we wish the client to support. + :param ca_cert_dir: + A directory containing CA certificates in multiple separate files, as + supported by OpenSSL's -CApath flag or the capath argument to + SSLContext.load_verify_locations(). + """ + context = ssl_context + if context is None: + # Note: This branch of code and all the variables in it are no longer + # used by urllib3 itself. We should consider deprecating and removing + # this code. + context = create_urllib3_context(ssl_version, cert_reqs, + ciphers=ciphers) + + if ca_certs or ca_cert_dir: + try: + context.load_verify_locations(ca_certs, ca_cert_dir) + except IOError as e: # Platform-specific: Python 2.7 + raise SSLError(e) + # Py33 raises FileNotFoundError which subclasses OSError + # These are not equivalent unless we check the errno attribute + except OSError as e: # Platform-specific: Python 3.3 and beyond + if e.errno == errno.ENOENT: + raise SSLError(e) + raise + elif getattr(context, 'load_default_certs', None) is not None: + # try to load OS default certs; works well on Windows (require Python3.4+) + context.load_default_certs() + + if certfile: + context.load_cert_chain(certfile, keyfile) + + # If we detect server_hostname is an IP address then the SNI + # extension should not be used according to RFC3546 Section 3.1 + # We shouldn't warn the user if SNI isn't available but we would + # not be using SNI anyways due to IP address for server_hostname. + if ((server_hostname is not None and not is_ipaddress(server_hostname)) + or IS_SECURETRANSPORT): + if HAS_SNI and server_hostname is not None: + return context.wrap_socket(sock, server_hostname=server_hostname) + + warnings.warn( + 'An HTTPS request has been made, but the SNI (Server Name ' + 'Indication) extension to TLS is not available on this platform. ' + 'This may cause the server to present an incorrect TLS ' + 'certificate, which can cause validation failures. You can upgrade to ' + 'a newer version of Python to solve this. For more information, see ' + 'https://urllib3.readthedocs.io/en/latest/advanced-usage.html' + '#ssl-warnings', + SNIMissingWarning + ) + + return context.wrap_socket(sock) + + +def is_ipaddress(hostname): + """Detects whether the hostname given is an IP address. + + :param str hostname: Hostname to examine. + :return: True if the hostname is an IP address, False otherwise. + """ + if six.PY3 and isinstance(hostname, bytes): + # IDN A-label bytes are ASCII compatible. + hostname = hostname.decode('ascii') + + families = [socket.AF_INET] + if hasattr(socket, 'AF_INET6'): + families.append(socket.AF_INET6) + + for af in families: + try: + inet_pton(af, hostname) + except (socket.error, ValueError, OSError): + pass + else: + return True + return False diff --git a/venv2/lib/python2.7/site-packages/pip/_vendor/urllib3/util/timeout.py b/venv2/lib/python2.7/site-packages/pip/_vendor/urllib3/util/timeout.py new file mode 100644 index 0000000..cec817e --- /dev/null +++ b/venv2/lib/python2.7/site-packages/pip/_vendor/urllib3/util/timeout.py @@ -0,0 +1,242 @@ +from __future__ import absolute_import +# The default socket timeout, used by httplib to indicate that no timeout was +# specified by the user +from socket import _GLOBAL_DEFAULT_TIMEOUT +import time + +from ..exceptions import TimeoutStateError + +# A sentinel value to indicate that no timeout was specified by the user in +# urllib3 +_Default = object() + + +# Use time.monotonic if available. +current_time = getattr(time, "monotonic", time.time) + + +class Timeout(object): + """ Timeout configuration. + + Timeouts can be defined as a default for a pool:: + + timeout = Timeout(connect=2.0, read=7.0) + http = PoolManager(timeout=timeout) + response = http.request('GET', 'http://example.com/') + + Or per-request (which overrides the default for the pool):: + + response = http.request('GET', 'http://example.com/', timeout=Timeout(10)) + + Timeouts can be disabled by setting all the parameters to ``None``:: + + no_timeout = Timeout(connect=None, read=None) + response = http.request('GET', 'http://example.com/, timeout=no_timeout) + + + :param total: + This combines the connect and read timeouts into one; the read timeout + will be set to the time leftover from the connect attempt. In the + event that both a connect timeout and a total are specified, or a read + timeout and a total are specified, the shorter timeout will be applied. + + Defaults to None. + + :type total: integer, float, or None + + :param connect: + The maximum amount of time to wait for a connection attempt to a server + to succeed. Omitting the parameter will default the connect timeout to + the system default, probably `the global default timeout in socket.py + <http://hg.python.org/cpython/file/603b4d593758/Lib/socket.py#l535>`_. + None will set an infinite timeout for connection attempts. + + :type connect: integer, float, or None + + :param read: + The maximum amount of time to wait between consecutive + read operations for a response from the server. Omitting + the parameter will default the read timeout to the system + default, probably `the global default timeout in socket.py + <http://hg.python.org/cpython/file/603b4d593758/Lib/socket.py#l535>`_. + None will set an infinite timeout. + + :type read: integer, float, or None + + .. note:: + + Many factors can affect the total amount of time for urllib3 to return + an HTTP response. + + For example, Python's DNS resolver does not obey the timeout specified + on the socket. Other factors that can affect total request time include + high CPU load, high swap, the program running at a low priority level, + or other behaviors. + + In addition, the read and total timeouts only measure the time between + read operations on the socket connecting the client and the server, + not the total amount of time for the request to return a complete + response. For most requests, the timeout is raised because the server + has not sent the first byte in the specified time. This is not always + the case; if a server streams one byte every fifteen seconds, a timeout + of 20 seconds will not trigger, even though the request will take + several minutes to complete. + + If your goal is to cut off any request after a set amount of wall clock + time, consider having a second "watcher" thread to cut off a slow + request. + """ + + #: A sentinel object representing the default timeout value + DEFAULT_TIMEOUT = _GLOBAL_DEFAULT_TIMEOUT + + def __init__(self, total=None, connect=_Default, read=_Default): + self._connect = self._validate_timeout(connect, 'connect') + self._read = self._validate_timeout(read, 'read') + self.total = self._validate_timeout(total, 'total') + self._start_connect = None + + def __str__(self): + return '%s(connect=%r, read=%r, total=%r)' % ( + type(self).__name__, self._connect, self._read, self.total) + + @classmethod + def _validate_timeout(cls, value, name): + """ Check that a timeout attribute is valid. + + :param value: The timeout value to validate + :param name: The name of the timeout attribute to validate. This is + used to specify in error messages. + :return: The validated and casted version of the given value. + :raises ValueError: If it is a numeric value less than or equal to + zero, or the type is not an integer, float, or None. + """ + if value is _Default: + return cls.DEFAULT_TIMEOUT + + if value is None or value is cls.DEFAULT_TIMEOUT: + return value + + if isinstance(value, bool): + raise ValueError("Timeout cannot be a boolean value. It must " + "be an int, float or None.") + try: + float(value) + except (TypeError, ValueError): + raise ValueError("Timeout value %s was %s, but it must be an " + "int, float or None." % (name, value)) + + try: + if value <= 0: + raise ValueError("Attempted to set %s timeout to %s, but the " + "timeout cannot be set to a value less " + "than or equal to 0." % (name, value)) + except TypeError: # Python 3 + raise ValueError("Timeout value %s was %s, but it must be an " + "int, float or None." % (name, value)) + + return value + + @classmethod + def from_float(cls, timeout): + """ Create a new Timeout from a legacy timeout value. + + The timeout value used by httplib.py sets the same timeout on the + connect(), and recv() socket requests. This creates a :class:`Timeout` + object that sets the individual timeouts to the ``timeout`` value + passed to this function. + + :param timeout: The legacy timeout value. + :type timeout: integer, float, sentinel default object, or None + :return: Timeout object + :rtype: :class:`Timeout` + """ + return Timeout(read=timeout, connect=timeout) + + def clone(self): + """ Create a copy of the timeout object + + Timeout properties are stored per-pool but each request needs a fresh + Timeout object to ensure each one has its own start/stop configured. + + :return: a copy of the timeout object + :rtype: :class:`Timeout` + """ + # We can't use copy.deepcopy because that will also create a new object + # for _GLOBAL_DEFAULT_TIMEOUT, which socket.py uses as a sentinel to + # detect the user default. + return Timeout(connect=self._connect, read=self._read, + total=self.total) + + def start_connect(self): + """ Start the timeout clock, used during a connect() attempt + + :raises urllib3.exceptions.TimeoutStateError: if you attempt + to start a timer that has been started already. + """ + if self._start_connect is not None: + raise TimeoutStateError("Timeout timer has already been started.") + self._start_connect = current_time() + return self._start_connect + + def get_connect_duration(self): + """ Gets the time elapsed since the call to :meth:`start_connect`. + + :return: Elapsed time. + :rtype: float + :raises urllib3.exceptions.TimeoutStateError: if you attempt + to get duration for a timer that hasn't been started. + """ + if self._start_connect is None: + raise TimeoutStateError("Can't get connect duration for timer " + "that has not started.") + return current_time() - self._start_connect + + @property + def connect_timeout(self): + """ Get the value to use when setting a connection timeout. + + This will be a positive float or integer, the value None + (never timeout), or the default system timeout. + + :return: Connect timeout. + :rtype: int, float, :attr:`Timeout.DEFAULT_TIMEOUT` or None + """ + if self.total is None: + return self._connect + + if self._connect is None or self._connect is self.DEFAULT_TIMEOUT: + return self.total + + return min(self._connect, self.total) + + @property + def read_timeout(self): + """ Get the value for the read timeout. + + This assumes some time has elapsed in the connection timeout and + computes the read timeout appropriately. + + If self.total is set, the read timeout is dependent on the amount of + time taken by the connect timeout. If the connection time has not been + established, a :exc:`~urllib3.exceptions.TimeoutStateError` will be + raised. + + :return: Value to use for the read timeout. + :rtype: int, float, :attr:`Timeout.DEFAULT_TIMEOUT` or None + :raises urllib3.exceptions.TimeoutStateError: If :meth:`start_connect` + has not yet been called on this object. + """ + if (self.total is not None and + self.total is not self.DEFAULT_TIMEOUT and + self._read is not None and + self._read is not self.DEFAULT_TIMEOUT): + # In case the connect timeout has not yet been established. + if self._start_connect is None: + return self._read + return max(0, min(self.total - self.get_connect_duration(), + self._read)) + elif self.total is not None and self.total is not self.DEFAULT_TIMEOUT: + return max(0, self.total - self.get_connect_duration()) + else: + return self._read diff --git a/venv2/lib/python2.7/site-packages/pip/_vendor/urllib3/util/url.py b/venv2/lib/python2.7/site-packages/pip/_vendor/urllib3/util/url.py new file mode 100644 index 0000000..6b6f996 --- /dev/null +++ b/venv2/lib/python2.7/site-packages/pip/_vendor/urllib3/util/url.py @@ -0,0 +1,230 @@ +from __future__ import absolute_import +from collections import namedtuple + +from ..exceptions import LocationParseError + + +url_attrs = ['scheme', 'auth', 'host', 'port', 'path', 'query', 'fragment'] + +# We only want to normalize urls with an HTTP(S) scheme. +# urllib3 infers URLs without a scheme (None) to be http. +NORMALIZABLE_SCHEMES = ('http', 'https', None) + + +class Url(namedtuple('Url', url_attrs)): + """ + Datastructure for representing an HTTP URL. Used as a return value for + :func:`parse_url`. Both the scheme and host are normalized as they are + both case-insensitive according to RFC 3986. + """ + __slots__ = () + + def __new__(cls, scheme=None, auth=None, host=None, port=None, path=None, + query=None, fragment=None): + if path and not path.startswith('/'): + path = '/' + path + if scheme: + scheme = scheme.lower() + if host and scheme in NORMALIZABLE_SCHEMES: + host = host.lower() + return super(Url, cls).__new__(cls, scheme, auth, host, port, path, + query, fragment) + + @property + def hostname(self): + """For backwards-compatibility with urlparse. We're nice like that.""" + return self.host + + @property + def request_uri(self): + """Absolute path including the query string.""" + uri = self.path or '/' + + if self.query is not None: + uri += '?' + self.query + + return uri + + @property + def netloc(self): + """Network location including host and port""" + if self.port: + return '%s:%d' % (self.host, self.port) + return self.host + + @property + def url(self): + """ + Convert self into a url + + This function should more or less round-trip with :func:`.parse_url`. The + returned url may not be exactly the same as the url inputted to + :func:`.parse_url`, but it should be equivalent by the RFC (e.g., urls + with a blank port will have : removed). + + Example: :: + + >>> U = parse_url('http://google.com/mail/') + >>> U.url + 'http://google.com/mail/' + >>> Url('http', 'username:password', 'host.com', 80, + ... '/path', 'query', 'fragment').url + 'http://username:password@host.com:80/path?query#fragment' + """ + scheme, auth, host, port, path, query, fragment = self + url = '' + + # We use "is not None" we want things to happen with empty strings (or 0 port) + if scheme is not None: + url += scheme + '://' + if auth is not None: + url += auth + '@' + if host is not None: + url += host + if port is not None: + url += ':' + str(port) + if path is not None: + url += path + if query is not None: + url += '?' + query + if fragment is not None: + url += '#' + fragment + + return url + + def __str__(self): + return self.url + + +def split_first(s, delims): + """ + Given a string and an iterable of delimiters, split on the first found + delimiter. Return two split parts and the matched delimiter. + + If not found, then the first part is the full input string. + + Example:: + + >>> split_first('foo/bar?baz', '?/=') + ('foo', 'bar?baz', '/') + >>> split_first('foo/bar?baz', '123') + ('foo/bar?baz', '', None) + + Scales linearly with number of delims. Not ideal for large number of delims. + """ + min_idx = None + min_delim = None + for d in delims: + idx = s.find(d) + if idx < 0: + continue + + if min_idx is None or idx < min_idx: + min_idx = idx + min_delim = d + + if min_idx is None or min_idx < 0: + return s, '', None + + return s[:min_idx], s[min_idx + 1:], min_delim + + +def parse_url(url): + """ + Given a url, return a parsed :class:`.Url` namedtuple. Best-effort is + performed to parse incomplete urls. Fields not provided will be None. + + Partly backwards-compatible with :mod:`urlparse`. + + Example:: + + >>> parse_url('http://google.com/mail/') + Url(scheme='http', host='google.com', port=None, path='/mail/', ...) + >>> parse_url('google.com:80') + Url(scheme=None, host='google.com', port=80, path=None, ...) + >>> parse_url('/foo?bar') + Url(scheme=None, host=None, port=None, path='/foo', query='bar', ...) + """ + + # While this code has overlap with stdlib's urlparse, it is much + # simplified for our needs and less annoying. + # Additionally, this implementations does silly things to be optimal + # on CPython. + + if not url: + # Empty + return Url() + + scheme = None + auth = None + host = None + port = None + path = None + fragment = None + query = None + + # Scheme + if '://' in url: + scheme, url = url.split('://', 1) + + # Find the earliest Authority Terminator + # (http://tools.ietf.org/html/rfc3986#section-3.2) + url, path_, delim = split_first(url, ['/', '?', '#']) + + if delim: + # Reassemble the path + path = delim + path_ + + # Auth + if '@' in url: + # Last '@' denotes end of auth part + auth, url = url.rsplit('@', 1) + + # IPv6 + if url and url[0] == '[': + host, url = url.split(']', 1) + host += ']' + + # Port + if ':' in url: + _host, port = url.split(':', 1) + + if not host: + host = _host + + if port: + # If given, ports must be integers. No whitespace, no plus or + # minus prefixes, no non-integer digits such as ^2 (superscript). + if not port.isdigit(): + raise LocationParseError(url) + try: + port = int(port) + except ValueError: + raise LocationParseError(url) + else: + # Blank ports are cool, too. (rfc3986#section-3.2.3) + port = None + + elif not host and url: + host = url + + if not path: + return Url(scheme, auth, host, port, path, query, fragment) + + # Fragment + if '#' in path: + path, fragment = path.split('#', 1) + + # Query + if '?' in path: + path, query = path.split('?', 1) + + return Url(scheme, auth, host, port, path, query, fragment) + + +def get_host(url): + """ + Deprecated. Use :func:`parse_url` instead. + """ + p = parse_url(url) + return p.scheme or 'http', p.hostname, p.port diff --git a/venv2/lib/python2.7/site-packages/pip/_vendor/urllib3/util/wait.py b/venv2/lib/python2.7/site-packages/pip/_vendor/urllib3/util/wait.py new file mode 100644 index 0000000..4db71ba --- /dev/null +++ b/venv2/lib/python2.7/site-packages/pip/_vendor/urllib3/util/wait.py @@ -0,0 +1,150 @@ +import errno +from functools import partial +import select +import sys +try: + from time import monotonic +except ImportError: + from time import time as monotonic + +__all__ = ["NoWayToWaitForSocketError", "wait_for_read", "wait_for_write"] + + +class NoWayToWaitForSocketError(Exception): + pass + + +# How should we wait on sockets? +# +# There are two types of APIs you can use for waiting on sockets: the fancy +# modern stateful APIs like epoll/kqueue, and the older stateless APIs like +# select/poll. The stateful APIs are more efficient when you have a lots of +# sockets to keep track of, because you can set them up once and then use them +# lots of times. But we only ever want to wait on a single socket at a time +# and don't want to keep track of state, so the stateless APIs are actually +# more efficient. So we want to use select() or poll(). +# +# Now, how do we choose between select() and poll()? On traditional Unixes, +# select() has a strange calling convention that makes it slow, or fail +# altogether, for high-numbered file descriptors. The point of poll() is to fix +# that, so on Unixes, we prefer poll(). +# +# On Windows, there is no poll() (or at least Python doesn't provide a wrapper +# for it), but that's OK, because on Windows, select() doesn't have this +# strange calling convention; plain select() works fine. +# +# So: on Windows we use select(), and everywhere else we use poll(). We also +# fall back to select() in case poll() is somehow broken or missing. + +if sys.version_info >= (3, 5): + # Modern Python, that retries syscalls by default + def _retry_on_intr(fn, timeout): + return fn(timeout) +else: + # Old and broken Pythons. + def _retry_on_intr(fn, timeout): + if timeout is None: + deadline = float("inf") + else: + deadline = monotonic() + timeout + + while True: + try: + return fn(timeout) + # OSError for 3 <= pyver < 3.5, select.error for pyver <= 2.7 + except (OSError, select.error) as e: + # 'e.args[0]' incantation works for both OSError and select.error + if e.args[0] != errno.EINTR: + raise + else: + timeout = deadline - monotonic() + if timeout < 0: + timeout = 0 + if timeout == float("inf"): + timeout = None + continue + + +def select_wait_for_socket(sock, read=False, write=False, timeout=None): + if not read and not write: + raise RuntimeError("must specify at least one of read=True, write=True") + rcheck = [] + wcheck = [] + if read: + rcheck.append(sock) + if write: + wcheck.append(sock) + # When doing a non-blocking connect, most systems signal success by + # marking the socket writable. Windows, though, signals success by marked + # it as "exceptional". We paper over the difference by checking the write + # sockets for both conditions. (The stdlib selectors module does the same + # thing.) + fn = partial(select.select, rcheck, wcheck, wcheck) + rready, wready, xready = _retry_on_intr(fn, timeout) + return bool(rready or wready or xready) + + +def poll_wait_for_socket(sock, read=False, write=False, timeout=None): + if not read and not write: + raise RuntimeError("must specify at least one of read=True, write=True") + mask = 0 + if read: + mask |= select.POLLIN + if write: + mask |= select.POLLOUT + poll_obj = select.poll() + poll_obj.register(sock, mask) + + # For some reason, poll() takes timeout in milliseconds + def do_poll(t): + if t is not None: + t *= 1000 + return poll_obj.poll(t) + + return bool(_retry_on_intr(do_poll, timeout)) + + +def null_wait_for_socket(*args, **kwargs): + raise NoWayToWaitForSocketError("no select-equivalent available") + + +def _have_working_poll(): + # Apparently some systems have a select.poll that fails as soon as you try + # to use it, either due to strange configuration or broken monkeypatching + # from libraries like eventlet/greenlet. + try: + poll_obj = select.poll() + _retry_on_intr(poll_obj.poll, 0) + except (AttributeError, OSError): + return False + else: + return True + + +def wait_for_socket(*args, **kwargs): + # We delay choosing which implementation to use until the first time we're + # called. We could do it at import time, but then we might make the wrong + # decision if someone goes wild with monkeypatching select.poll after + # we're imported. + global wait_for_socket + if _have_working_poll(): + wait_for_socket = poll_wait_for_socket + elif hasattr(select, "select"): + wait_for_socket = select_wait_for_socket + else: # Platform-specific: Appengine. + wait_for_socket = null_wait_for_socket + return wait_for_socket(*args, **kwargs) + + +def wait_for_read(sock, timeout=None): + """ Waits for reading to be available on a given socket. + Returns True if the socket is readable, or False if the timeout expired. + """ + return wait_for_socket(sock, read=True, timeout=timeout) + + +def wait_for_write(sock, timeout=None): + """ Waits for writing to be available on a given socket. + Returns True if the socket is readable, or False if the timeout expired. + """ + return wait_for_socket(sock, write=True, timeout=timeout) diff --git a/venv2/lib/python2.7/site-packages/pip/_vendor/webencodings/__init__.py b/venv2/lib/python2.7/site-packages/pip/_vendor/webencodings/__init__.py new file mode 100644 index 0000000..d21d697 --- /dev/null +++ b/venv2/lib/python2.7/site-packages/pip/_vendor/webencodings/__init__.py @@ -0,0 +1,342 @@ +# coding: utf-8 +""" + + webencodings + ~~~~~~~~~~~~ + + This is a Python implementation of the `WHATWG Encoding standard + <http://encoding.spec.whatwg.org/>`. See README for details. + + :copyright: Copyright 2012 by Simon Sapin + :license: BSD, see LICENSE for details. + +""" + +from __future__ import unicode_literals + +import codecs + +from .labels import LABELS + + +VERSION = '0.5.1' + + +# Some names in Encoding are not valid Python aliases. Remap these. +PYTHON_NAMES = { + 'iso-8859-8-i': 'iso-8859-8', + 'x-mac-cyrillic': 'mac-cyrillic', + 'macintosh': 'mac-roman', + 'windows-874': 'cp874'} + +CACHE = {} + + +def ascii_lower(string): + r"""Transform (only) ASCII letters to lower case: A-Z is mapped to a-z. + + :param string: An Unicode string. + :returns: A new Unicode string. + + This is used for `ASCII case-insensitive + <http://encoding.spec.whatwg.org/#ascii-case-insensitive>`_ + matching of encoding labels. + The same matching is also used, among other things, + for `CSS keywords <http://dev.w3.org/csswg/css-values/#keywords>`_. + + This is different from the :meth:`~py:str.lower` method of Unicode strings + which also affect non-ASCII characters, + sometimes mapping them into the ASCII range: + + >>> keyword = u'Bac\N{KELVIN SIGN}ground' + >>> assert keyword.lower() == u'background' + >>> assert ascii_lower(keyword) != keyword.lower() + >>> assert ascii_lower(keyword) == u'bac\N{KELVIN SIGN}ground' + + """ + # This turns out to be faster than unicode.translate() + return string.encode('utf8').lower().decode('utf8') + + +def lookup(label): + """ + Look for an encoding by its label. + This is the spec’s `get an encoding + <http://encoding.spec.whatwg.org/#concept-encoding-get>`_ algorithm. + Supported labels are listed there. + + :param label: A string. + :returns: + An :class:`Encoding` object, or :obj:`None` for an unknown label. + + """ + # Only strip ASCII whitespace: U+0009, U+000A, U+000C, U+000D, and U+0020. + label = ascii_lower(label.strip('\t\n\f\r ')) + name = LABELS.get(label) + if name is None: + return None + encoding = CACHE.get(name) + if encoding is None: + if name == 'x-user-defined': + from .x_user_defined import codec_info + else: + python_name = PYTHON_NAMES.get(name, name) + # Any python_name value that gets to here should be valid. + codec_info = codecs.lookup(python_name) + encoding = Encoding(name, codec_info) + CACHE[name] = encoding + return encoding + + +def _get_encoding(encoding_or_label): + """ + Accept either an encoding object or label. + + :param encoding: An :class:`Encoding` object or a label string. + :returns: An :class:`Encoding` object. + :raises: :exc:`~exceptions.LookupError` for an unknown label. + + """ + if hasattr(encoding_or_label, 'codec_info'): + return encoding_or_label + + encoding = lookup(encoding_or_label) + if encoding is None: + raise LookupError('Unknown encoding label: %r' % encoding_or_label) + return encoding + + +class Encoding(object): + """Reresents a character encoding such as UTF-8, + that can be used for decoding or encoding. + + .. attribute:: name + + Canonical name of the encoding + + .. attribute:: codec_info + + The actual implementation of the encoding, + a stdlib :class:`~codecs.CodecInfo` object. + See :func:`codecs.register`. + + """ + def __init__(self, name, codec_info): + self.name = name + self.codec_info = codec_info + + def __repr__(self): + return '<Encoding %s>' % self.name + + +#: The UTF-8 encoding. Should be used for new content and formats. +UTF8 = lookup('utf-8') + +_UTF16LE = lookup('utf-16le') +_UTF16BE = lookup('utf-16be') + + +def decode(input, fallback_encoding, errors='replace'): + """ + Decode a single string. + + :param input: A byte string + :param fallback_encoding: + An :class:`Encoding` object or a label string. + The encoding to use if :obj:`input` does note have a BOM. + :param errors: Type of error handling. See :func:`codecs.register`. + :raises: :exc:`~exceptions.LookupError` for an unknown encoding label. + :return: + A ``(output, encoding)`` tuple of an Unicode string + and an :obj:`Encoding`. + + """ + # Fail early if `encoding` is an invalid label. + fallback_encoding = _get_encoding(fallback_encoding) + bom_encoding, input = _detect_bom(input) + encoding = bom_encoding or fallback_encoding + return encoding.codec_info.decode(input, errors)[0], encoding + + +def _detect_bom(input): + """Return (bom_encoding, input), with any BOM removed from the input.""" + if input.startswith(b'\xFF\xFE'): + return _UTF16LE, input[2:] + if input.startswith(b'\xFE\xFF'): + return _UTF16BE, input[2:] + if input.startswith(b'\xEF\xBB\xBF'): + return UTF8, input[3:] + return None, input + + +def encode(input, encoding=UTF8, errors='strict'): + """ + Encode a single string. + + :param input: An Unicode string. + :param encoding: An :class:`Encoding` object or a label string. + :param errors: Type of error handling. See :func:`codecs.register`. + :raises: :exc:`~exceptions.LookupError` for an unknown encoding label. + :return: A byte string. + + """ + return _get_encoding(encoding).codec_info.encode(input, errors)[0] + + +def iter_decode(input, fallback_encoding, errors='replace'): + """ + "Pull"-based decoder. + + :param input: + An iterable of byte strings. + + The input is first consumed just enough to determine the encoding + based on the precense of a BOM, + then consumed on demand when the return value is. + :param fallback_encoding: + An :class:`Encoding` object or a label string. + The encoding to use if :obj:`input` does note have a BOM. + :param errors: Type of error handling. See :func:`codecs.register`. + :raises: :exc:`~exceptions.LookupError` for an unknown encoding label. + :returns: + An ``(output, encoding)`` tuple. + :obj:`output` is an iterable of Unicode strings, + :obj:`encoding` is the :obj:`Encoding` that is being used. + + """ + + decoder = IncrementalDecoder(fallback_encoding, errors) + generator = _iter_decode_generator(input, decoder) + encoding = next(generator) + return generator, encoding + + +def _iter_decode_generator(input, decoder): + """Return a generator that first yields the :obj:`Encoding`, + then yields output chukns as Unicode strings. + + """ + decode = decoder.decode + input = iter(input) + for chunck in input: + output = decode(chunck) + if output: + assert decoder.encoding is not None + yield decoder.encoding + yield output + break + else: + # Input exhausted without determining the encoding + output = decode(b'', final=True) + assert decoder.encoding is not None + yield decoder.encoding + if output: + yield output + return + + for chunck in input: + output = decode(chunck) + if output: + yield output + output = decode(b'', final=True) + if output: + yield output + + +def iter_encode(input, encoding=UTF8, errors='strict'): + """ + “Pull”-based encoder. + + :param input: An iterable of Unicode strings. + :param encoding: An :class:`Encoding` object or a label string. + :param errors: Type of error handling. See :func:`codecs.register`. + :raises: :exc:`~exceptions.LookupError` for an unknown encoding label. + :returns: An iterable of byte strings. + + """ + # Fail early if `encoding` is an invalid label. + encode = IncrementalEncoder(encoding, errors).encode + return _iter_encode_generator(input, encode) + + +def _iter_encode_generator(input, encode): + for chunck in input: + output = encode(chunck) + if output: + yield output + output = encode('', final=True) + if output: + yield output + + +class IncrementalDecoder(object): + """ + “Push”-based decoder. + + :param fallback_encoding: + An :class:`Encoding` object or a label string. + The encoding to use if :obj:`input` does note have a BOM. + :param errors: Type of error handling. See :func:`codecs.register`. + :raises: :exc:`~exceptions.LookupError` for an unknown encoding label. + + """ + def __init__(self, fallback_encoding, errors='replace'): + # Fail early if `encoding` is an invalid label. + self._fallback_encoding = _get_encoding(fallback_encoding) + self._errors = errors + self._buffer = b'' + self._decoder = None + #: The actual :class:`Encoding` that is being used, + #: or :obj:`None` if that is not determined yet. + #: (Ie. if there is not enough input yet to determine + #: if there is a BOM.) + self.encoding = None # Not known yet. + + def decode(self, input, final=False): + """Decode one chunk of the input. + + :param input: A byte string. + :param final: + Indicate that no more input is available. + Must be :obj:`True` if this is the last call. + :returns: An Unicode string. + + """ + decoder = self._decoder + if decoder is not None: + return decoder(input, final) + + input = self._buffer + input + encoding, input = _detect_bom(input) + if encoding is None: + if len(input) < 3 and not final: # Not enough data yet. + self._buffer = input + return '' + else: # No BOM + encoding = self._fallback_encoding + decoder = encoding.codec_info.incrementaldecoder(self._errors).decode + self._decoder = decoder + self.encoding = encoding + return decoder(input, final) + + +class IncrementalEncoder(object): + """ + “Push”-based encoder. + + :param encoding: An :class:`Encoding` object or a label string. + :param errors: Type of error handling. See :func:`codecs.register`. + :raises: :exc:`~exceptions.LookupError` for an unknown encoding label. + + .. method:: encode(input, final=False) + + :param input: An Unicode string. + :param final: + Indicate that no more input is available. + Must be :obj:`True` if this is the last call. + :returns: A byte string. + + """ + def __init__(self, encoding=UTF8, errors='strict'): + encoding = _get_encoding(encoding) + self.encode = encoding.codec_info.incrementalencoder(errors).encode diff --git a/venv2/lib/python2.7/site-packages/pip/_vendor/webencodings/labels.py b/venv2/lib/python2.7/site-packages/pip/_vendor/webencodings/labels.py new file mode 100644 index 0000000..29cbf91 --- /dev/null +++ b/venv2/lib/python2.7/site-packages/pip/_vendor/webencodings/labels.py @@ -0,0 +1,231 @@ +""" + + webencodings.labels + ~~~~~~~~~~~~~~~~~~~ + + Map encoding labels to their name. + + :copyright: Copyright 2012 by Simon Sapin + :license: BSD, see LICENSE for details. + +""" + +# XXX Do not edit! +# This file is automatically generated by mklabels.py + +LABELS = { + 'unicode-1-1-utf-8': 'utf-8', + 'utf-8': 'utf-8', + 'utf8': 'utf-8', + '866': 'ibm866', + 'cp866': 'ibm866', + 'csibm866': 'ibm866', + 'ibm866': 'ibm866', + 'csisolatin2': 'iso-8859-2', + 'iso-8859-2': 'iso-8859-2', + 'iso-ir-101': 'iso-8859-2', + 'iso8859-2': 'iso-8859-2', + 'iso88592': 'iso-8859-2', + 'iso_8859-2': 'iso-8859-2', + 'iso_8859-2:1987': 'iso-8859-2', + 'l2': 'iso-8859-2', + 'latin2': 'iso-8859-2', + 'csisolatin3': 'iso-8859-3', + 'iso-8859-3': 'iso-8859-3', + 'iso-ir-109': 'iso-8859-3', + 'iso8859-3': 'iso-8859-3', + 'iso88593': 'iso-8859-3', + 'iso_8859-3': 'iso-8859-3', + 'iso_8859-3:1988': 'iso-8859-3', + 'l3': 'iso-8859-3', + 'latin3': 'iso-8859-3', + 'csisolatin4': 'iso-8859-4', + 'iso-8859-4': 'iso-8859-4', + 'iso-ir-110': 'iso-8859-4', + 'iso8859-4': 'iso-8859-4', + 'iso88594': 'iso-8859-4', + 'iso_8859-4': 'iso-8859-4', + 'iso_8859-4:1988': 'iso-8859-4', + 'l4': 'iso-8859-4', + 'latin4': 'iso-8859-4', + 'csisolatincyrillic': 'iso-8859-5', + 'cyrillic': 'iso-8859-5', + 'iso-8859-5': 'iso-8859-5', + 'iso-ir-144': 'iso-8859-5', + 'iso8859-5': 'iso-8859-5', + 'iso88595': 'iso-8859-5', + 'iso_8859-5': 'iso-8859-5', + 'iso_8859-5:1988': 'iso-8859-5', + 'arabic': 'iso-8859-6', + 'asmo-708': 'iso-8859-6', + 'csiso88596e': 'iso-8859-6', + 'csiso88596i': 'iso-8859-6', + 'csisolatinarabic': 'iso-8859-6', + 'ecma-114': 'iso-8859-6', + 'iso-8859-6': 'iso-8859-6', + 'iso-8859-6-e': 'iso-8859-6', + 'iso-8859-6-i': 'iso-8859-6', + 'iso-ir-127': 'iso-8859-6', + 'iso8859-6': 'iso-8859-6', + 'iso88596': 'iso-8859-6', + 'iso_8859-6': 'iso-8859-6', + 'iso_8859-6:1987': 'iso-8859-6', + 'csisolatingreek': 'iso-8859-7', + 'ecma-118': 'iso-8859-7', + 'elot_928': 'iso-8859-7', + 'greek': 'iso-8859-7', + 'greek8': 'iso-8859-7', + 'iso-8859-7': 'iso-8859-7', + 'iso-ir-126': 'iso-8859-7', + 'iso8859-7': 'iso-8859-7', + 'iso88597': 'iso-8859-7', + 'iso_8859-7': 'iso-8859-7', + 'iso_8859-7:1987': 'iso-8859-7', + 'sun_eu_greek': 'iso-8859-7', + 'csiso88598e': 'iso-8859-8', + 'csisolatinhebrew': 'iso-8859-8', + 'hebrew': 'iso-8859-8', + 'iso-8859-8': 'iso-8859-8', + 'iso-8859-8-e': 'iso-8859-8', + 'iso-ir-138': 'iso-8859-8', + 'iso8859-8': 'iso-8859-8', + 'iso88598': 'iso-8859-8', + 'iso_8859-8': 'iso-8859-8', + 'iso_8859-8:1988': 'iso-8859-8', + 'visual': 'iso-8859-8', + 'csiso88598i': 'iso-8859-8-i', + 'iso-8859-8-i': 'iso-8859-8-i', + 'logical': 'iso-8859-8-i', + 'csisolatin6': 'iso-8859-10', + 'iso-8859-10': 'iso-8859-10', + 'iso-ir-157': 'iso-8859-10', + 'iso8859-10': 'iso-8859-10', + 'iso885910': 'iso-8859-10', + 'l6': 'iso-8859-10', + 'latin6': 'iso-8859-10', + 'iso-8859-13': 'iso-8859-13', + 'iso8859-13': 'iso-8859-13', + 'iso885913': 'iso-8859-13', + 'iso-8859-14': 'iso-8859-14', + 'iso8859-14': 'iso-8859-14', + 'iso885914': 'iso-8859-14', + 'csisolatin9': 'iso-8859-15', + 'iso-8859-15': 'iso-8859-15', + 'iso8859-15': 'iso-8859-15', + 'iso885915': 'iso-8859-15', + 'iso_8859-15': 'iso-8859-15', + 'l9': 'iso-8859-15', + 'iso-8859-16': 'iso-8859-16', + 'cskoi8r': 'koi8-r', + 'koi': 'koi8-r', + 'koi8': 'koi8-r', + 'koi8-r': 'koi8-r', + 'koi8_r': 'koi8-r', + 'koi8-u': 'koi8-u', + 'csmacintosh': 'macintosh', + 'mac': 'macintosh', + 'macintosh': 'macintosh', + 'x-mac-roman': 'macintosh', + 'dos-874': 'windows-874', + 'iso-8859-11': 'windows-874', + 'iso8859-11': 'windows-874', + 'iso885911': 'windows-874', + 'tis-620': 'windows-874', + 'windows-874': 'windows-874', + 'cp1250': 'windows-1250', + 'windows-1250': 'windows-1250', + 'x-cp1250': 'windows-1250', + 'cp1251': 'windows-1251', + 'windows-1251': 'windows-1251', + 'x-cp1251': 'windows-1251', + 'ansi_x3.4-1968': 'windows-1252', + 'ascii': 'windows-1252', + 'cp1252': 'windows-1252', + 'cp819': 'windows-1252', + 'csisolatin1': 'windows-1252', + 'ibm819': 'windows-1252', + 'iso-8859-1': 'windows-1252', + 'iso-ir-100': 'windows-1252', + 'iso8859-1': 'windows-1252', + 'iso88591': 'windows-1252', + 'iso_8859-1': 'windows-1252', + 'iso_8859-1:1987': 'windows-1252', + 'l1': 'windows-1252', + 'latin1': 'windows-1252', + 'us-ascii': 'windows-1252', + 'windows-1252': 'windows-1252', + 'x-cp1252': 'windows-1252', + 'cp1253': 'windows-1253', + 'windows-1253': 'windows-1253', + 'x-cp1253': 'windows-1253', + 'cp1254': 'windows-1254', + 'csisolatin5': 'windows-1254', + 'iso-8859-9': 'windows-1254', + 'iso-ir-148': 'windows-1254', + 'iso8859-9': 'windows-1254', + 'iso88599': 'windows-1254', + 'iso_8859-9': 'windows-1254', + 'iso_8859-9:1989': 'windows-1254', + 'l5': 'windows-1254', + 'latin5': 'windows-1254', + 'windows-1254': 'windows-1254', + 'x-cp1254': 'windows-1254', + 'cp1255': 'windows-1255', + 'windows-1255': 'windows-1255', + 'x-cp1255': 'windows-1255', + 'cp1256': 'windows-1256', + 'windows-1256': 'windows-1256', + 'x-cp1256': 'windows-1256', + 'cp1257': 'windows-1257', + 'windows-1257': 'windows-1257', + 'x-cp1257': 'windows-1257', + 'cp1258': 'windows-1258', + 'windows-1258': 'windows-1258', + 'x-cp1258': 'windows-1258', + 'x-mac-cyrillic': 'x-mac-cyrillic', + 'x-mac-ukrainian': 'x-mac-cyrillic', + 'chinese': 'gbk', + 'csgb2312': 'gbk', + 'csiso58gb231280': 'gbk', + 'gb2312': 'gbk', + 'gb_2312': 'gbk', + 'gb_2312-80': 'gbk', + 'gbk': 'gbk', + 'iso-ir-58': 'gbk', + 'x-gbk': 'gbk', + 'gb18030': 'gb18030', + 'hz-gb-2312': 'hz-gb-2312', + 'big5': 'big5', + 'big5-hkscs': 'big5', + 'cn-big5': 'big5', + 'csbig5': 'big5', + 'x-x-big5': 'big5', + 'cseucpkdfmtjapanese': 'euc-jp', + 'euc-jp': 'euc-jp', + 'x-euc-jp': 'euc-jp', + 'csiso2022jp': 'iso-2022-jp', + 'iso-2022-jp': 'iso-2022-jp', + 'csshiftjis': 'shift_jis', + 'ms_kanji': 'shift_jis', + 'shift-jis': 'shift_jis', + 'shift_jis': 'shift_jis', + 'sjis': 'shift_jis', + 'windows-31j': 'shift_jis', + 'x-sjis': 'shift_jis', + 'cseuckr': 'euc-kr', + 'csksc56011987': 'euc-kr', + 'euc-kr': 'euc-kr', + 'iso-ir-149': 'euc-kr', + 'korean': 'euc-kr', + 'ks_c_5601-1987': 'euc-kr', + 'ks_c_5601-1989': 'euc-kr', + 'ksc5601': 'euc-kr', + 'ksc_5601': 'euc-kr', + 'windows-949': 'euc-kr', + 'csiso2022kr': 'iso-2022-kr', + 'iso-2022-kr': 'iso-2022-kr', + 'utf-16be': 'utf-16be', + 'utf-16': 'utf-16le', + 'utf-16le': 'utf-16le', + 'x-user-defined': 'x-user-defined', +} diff --git a/venv2/lib/python2.7/site-packages/pip/_vendor/webencodings/mklabels.py b/venv2/lib/python2.7/site-packages/pip/_vendor/webencodings/mklabels.py new file mode 100644 index 0000000..295dc92 --- /dev/null +++ b/venv2/lib/python2.7/site-packages/pip/_vendor/webencodings/mklabels.py @@ -0,0 +1,59 @@ +""" + + webencodings.mklabels + ~~~~~~~~~~~~~~~~~~~~~ + + Regenarate the webencodings.labels module. + + :copyright: Copyright 2012 by Simon Sapin + :license: BSD, see LICENSE for details. + +""" + +import json +try: + from urllib import urlopen +except ImportError: + from urllib.request import urlopen + + +def assert_lower(string): + assert string == string.lower() + return string + + +def generate(url): + parts = ['''\ +""" + + webencodings.labels + ~~~~~~~~~~~~~~~~~~~ + + Map encoding labels to their name. + + :copyright: Copyright 2012 by Simon Sapin + :license: BSD, see LICENSE for details. + +""" + +# XXX Do not edit! +# This file is automatically generated by mklabels.py + +LABELS = { +'''] + labels = [ + (repr(assert_lower(label)).lstrip('u'), + repr(encoding['name']).lstrip('u')) + for category in json.loads(urlopen(url).read().decode('ascii')) + for encoding in category['encodings'] + for label in encoding['labels']] + max_len = max(len(label) for label, name in labels) + parts.extend( + ' %s:%s %s,\n' % (label, ' ' * (max_len - len(label)), name) + for label, name in labels) + parts.append('}') + return ''.join(parts) + + +if __name__ == '__main__': + print(generate('http://encoding.spec.whatwg.org/encodings.json')) diff --git a/venv2/lib/python2.7/site-packages/pip/_vendor/webencodings/tests.py b/venv2/lib/python2.7/site-packages/pip/_vendor/webencodings/tests.py new file mode 100644 index 0000000..e12c10d --- /dev/null +++ b/venv2/lib/python2.7/site-packages/pip/_vendor/webencodings/tests.py @@ -0,0 +1,153 @@ +# coding: utf-8 +""" + + webencodings.tests + ~~~~~~~~~~~~~~~~~~ + + A basic test suite for Encoding. + + :copyright: Copyright 2012 by Simon Sapin + :license: BSD, see LICENSE for details. + +""" + +from __future__ import unicode_literals + +from . import (lookup, LABELS, decode, encode, iter_decode, iter_encode, + IncrementalDecoder, IncrementalEncoder, UTF8) + + +def assert_raises(exception, function, *args, **kwargs): + try: + function(*args, **kwargs) + except exception: + return + else: # pragma: no cover + raise AssertionError('Did not raise %s.' % exception) + + +def test_labels(): + assert lookup('utf-8').name == 'utf-8' + assert lookup('Utf-8').name == 'utf-8' + assert lookup('UTF-8').name == 'utf-8' + assert lookup('utf8').name == 'utf-8' + assert lookup('utf8').name == 'utf-8' + assert lookup('utf8 ').name == 'utf-8' + assert lookup(' \r\nutf8\t').name == 'utf-8' + assert lookup('u8') is None # Python label. + assert lookup('utf-8 ') is None # Non-ASCII white space. + + assert lookup('US-ASCII').name == 'windows-1252' + assert lookup('iso-8859-1').name == 'windows-1252' + assert lookup('latin1').name == 'windows-1252' + assert lookup('LATIN1').name == 'windows-1252' + assert lookup('latin-1') is None + assert lookup('LATİN1') is None # ASCII-only case insensitivity. + + +def test_all_labels(): + for label in LABELS: + assert decode(b'', label) == ('', lookup(label)) + assert encode('', label) == b'' + for repeat in [0, 1, 12]: + output, _ = iter_decode([b''] * repeat, label) + assert list(output) == [] + assert list(iter_encode([''] * repeat, label)) == [] + decoder = IncrementalDecoder(label) + assert decoder.decode(b'') == '' + assert decoder.decode(b'', final=True) == '' + encoder = IncrementalEncoder(label) + assert encoder.encode('') == b'' + assert encoder.encode('', final=True) == b'' + # All encoding names are valid labels too: + for name in set(LABELS.values()): + assert lookup(name).name == name + + +def test_invalid_label(): + assert_raises(LookupError, decode, b'\xEF\xBB\xBF\xc3\xa9', 'invalid') + assert_raises(LookupError, encode, 'é', 'invalid') + assert_raises(LookupError, iter_decode, [], 'invalid') + assert_raises(LookupError, iter_encode, [], 'invalid') + assert_raises(LookupError, IncrementalDecoder, 'invalid') + assert_raises(LookupError, IncrementalEncoder, 'invalid') + + +def test_decode(): + assert decode(b'\x80', 'latin1') == ('€', lookup('latin1')) + assert decode(b'\x80', lookup('latin1')) == ('€', lookup('latin1')) + assert decode(b'\xc3\xa9', 'utf8') == ('é', lookup('utf8')) + assert decode(b'\xc3\xa9', UTF8) == ('é', lookup('utf8')) + assert decode(b'\xc3\xa9', 'ascii') == ('é', lookup('ascii')) + assert decode(b'\xEF\xBB\xBF\xc3\xa9', 'ascii') == ('é', lookup('utf8')) # UTF-8 with BOM + + assert decode(b'\xFE\xFF\x00\xe9', 'ascii') == ('é', lookup('utf-16be')) # UTF-16-BE with BOM + assert decode(b'\xFF\xFE\xe9\x00', 'ascii') == ('é', lookup('utf-16le')) # UTF-16-LE with BOM + assert decode(b'\xFE\xFF\xe9\x00', 'ascii') == ('\ue900', lookup('utf-16be')) + assert decode(b'\xFF\xFE\x00\xe9', 'ascii') == ('\ue900', lookup('utf-16le')) + + assert decode(b'\x00\xe9', 'UTF-16BE') == ('é', lookup('utf-16be')) + assert decode(b'\xe9\x00', 'UTF-16LE') == ('é', lookup('utf-16le')) + assert decode(b'\xe9\x00', 'UTF-16') == ('é', lookup('utf-16le')) + + assert decode(b'\xe9\x00', 'UTF-16BE') == ('\ue900', lookup('utf-16be')) + assert decode(b'\x00\xe9', 'UTF-16LE') == ('\ue900', lookup('utf-16le')) + assert decode(b'\x00\xe9', 'UTF-16') == ('\ue900', lookup('utf-16le')) + + +def test_encode(): + assert encode('é', 'latin1') == b'\xe9' + assert encode('é', 'utf8') == b'\xc3\xa9' + assert encode('é', 'utf8') == b'\xc3\xa9' + assert encode('é', 'utf-16') == b'\xe9\x00' + assert encode('é', 'utf-16le') == b'\xe9\x00' + assert encode('é', 'utf-16be') == b'\x00\xe9' + + +def test_iter_decode(): + def iter_decode_to_string(input, fallback_encoding): + output, _encoding = iter_decode(input, fallback_encoding) + return ''.join(output) + assert iter_decode_to_string([], 'latin1') == '' + assert iter_decode_to_string([b''], 'latin1') == '' + assert iter_decode_to_string([b'\xe9'], 'latin1') == 'é' + assert iter_decode_to_string([b'hello'], 'latin1') == 'hello' + assert iter_decode_to_string([b'he', b'llo'], 'latin1') == 'hello' + assert iter_decode_to_string([b'hell', b'o'], 'latin1') == 'hello' + assert iter_decode_to_string([b'\xc3\xa9'], 'latin1') == 'é' + assert iter_decode_to_string([b'\xEF\xBB\xBF\xc3\xa9'], 'latin1') == 'é' + assert iter_decode_to_string([ + b'\xEF\xBB\xBF', b'\xc3', b'\xa9'], 'latin1') == 'é' + assert iter_decode_to_string([ + b'\xEF\xBB\xBF', b'a', b'\xc3'], 'latin1') == 'a\uFFFD' + assert iter_decode_to_string([ + b'', b'\xEF', b'', b'', b'\xBB\xBF\xc3', b'\xa9'], 'latin1') == 'é' + assert iter_decode_to_string([b'\xEF\xBB\xBF'], 'latin1') == '' + assert iter_decode_to_string([b'\xEF\xBB'], 'latin1') == 'ï»' + assert iter_decode_to_string([b'\xFE\xFF\x00\xe9'], 'latin1') == 'é' + assert iter_decode_to_string([b'\xFF\xFE\xe9\x00'], 'latin1') == 'é' + assert iter_decode_to_string([ + b'', b'\xFF', b'', b'', b'\xFE\xe9', b'\x00'], 'latin1') == 'é' + assert iter_decode_to_string([ + b'', b'h\xe9', b'llo'], 'x-user-defined') == 'h\uF7E9llo' + + +def test_iter_encode(): + assert b''.join(iter_encode([], 'latin1')) == b'' + assert b''.join(iter_encode([''], 'latin1')) == b'' + assert b''.join(iter_encode(['é'], 'latin1')) == b'\xe9' + assert b''.join(iter_encode(['', 'é', '', ''], 'latin1')) == b'\xe9' + assert b''.join(iter_encode(['', 'é', '', ''], 'utf-16')) == b'\xe9\x00' + assert b''.join(iter_encode(['', 'é', '', ''], 'utf-16le')) == b'\xe9\x00' + assert b''.join(iter_encode(['', 'é', '', ''], 'utf-16be')) == b'\x00\xe9' + assert b''.join(iter_encode([ + '', 'h\uF7E9', '', 'llo'], 'x-user-defined')) == b'h\xe9llo' + + +def test_x_user_defined(): + encoded = b'2,\x0c\x0b\x1aO\xd9#\xcb\x0f\xc9\xbbt\xcf\xa8\xca' + decoded = '2,\x0c\x0b\x1aO\uf7d9#\uf7cb\x0f\uf7c9\uf7bbt\uf7cf\uf7a8\uf7ca' + encoded = b'aa' + decoded = 'aa' + assert decode(encoded, 'x-user-defined') == (decoded, lookup('x-user-defined')) + assert encode(decoded, 'x-user-defined') == encoded diff --git a/venv2/lib/python2.7/site-packages/pip/_vendor/webencodings/x_user_defined.py b/venv2/lib/python2.7/site-packages/pip/_vendor/webencodings/x_user_defined.py new file mode 100644 index 0000000..d16e326 --- /dev/null +++ b/venv2/lib/python2.7/site-packages/pip/_vendor/webencodings/x_user_defined.py @@ -0,0 +1,325 @@ +# coding: utf-8 +""" + + webencodings.x_user_defined + ~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + An implementation of the x-user-defined encoding. + + :copyright: Copyright 2012 by Simon Sapin + :license: BSD, see LICENSE for details. + +""" + +from __future__ import unicode_literals + +import codecs + + +### Codec APIs + +class Codec(codecs.Codec): + + def encode(self, input, errors='strict'): + return codecs.charmap_encode(input, errors, encoding_table) + + def decode(self, input, errors='strict'): + return codecs.charmap_decode(input, errors, decoding_table) + + +class IncrementalEncoder(codecs.IncrementalEncoder): + def encode(self, input, final=False): + return codecs.charmap_encode(input, self.errors, encoding_table)[0] + + +class IncrementalDecoder(codecs.IncrementalDecoder): + def decode(self, input, final=False): + return codecs.charmap_decode(input, self.errors, decoding_table)[0] + + +class StreamWriter(Codec, codecs.StreamWriter): + pass + + +class StreamReader(Codec, codecs.StreamReader): + pass + + +### encodings module API + +codec_info = codecs.CodecInfo( + name='x-user-defined', + encode=Codec().encode, + decode=Codec().decode, + incrementalencoder=IncrementalEncoder, + incrementaldecoder=IncrementalDecoder, + streamreader=StreamReader, + streamwriter=StreamWriter, +) + + +### Decoding Table + +# Python 3: +# for c in range(256): print(' %r' % chr(c if c < 128 else c + 0xF700)) +decoding_table = ( + '\x00' + '\x01' + '\x02' + '\x03' + '\x04' + '\x05' + '\x06' + '\x07' + '\x08' + '\t' + '\n' + '\x0b' + '\x0c' + '\r' + '\x0e' + '\x0f' + '\x10' + '\x11' + '\x12' + '\x13' + '\x14' + '\x15' + '\x16' + '\x17' + '\x18' + '\x19' + '\x1a' + '\x1b' + '\x1c' + '\x1d' + '\x1e' + '\x1f' + ' ' + '!' + '"' + '#' + '$' + '%' + '&' + "'" + '(' + ')' + '*' + '+' + ',' + '-' + '.' + '/' + '0' + '1' + '2' + '3' + '4' + '5' + '6' + '7' + '8' + '9' + ':' + ';' + '<' + '=' + '>' + '?' + '@' + 'A' + 'B' + 'C' + 'D' + 'E' + 'F' + 'G' + 'H' + 'I' + 'J' + 'K' + 'L' + 'M' + 'N' + 'O' + 'P' + 'Q' + 'R' + 'S' + 'T' + 'U' + 'V' + 'W' + 'X' + 'Y' + 'Z' + '[' + '\\' + ']' + '^' + '_' + '`' + 'a' + 'b' + 'c' + 'd' + 'e' + 'f' + 'g' + 'h' + 'i' + 'j' + 'k' + 'l' + 'm' + 'n' + 'o' + 'p' + 'q' + 'r' + 's' + 't' + 'u' + 'v' + 'w' + 'x' + 'y' + 'z' + '{' + '|' + '}' + '~' + '\x7f' + '\uf780' + '\uf781' + '\uf782' + '\uf783' + '\uf784' + '\uf785' + '\uf786' + '\uf787' + '\uf788' + '\uf789' + '\uf78a' + '\uf78b' + '\uf78c' + '\uf78d' + '\uf78e' + '\uf78f' + '\uf790' + '\uf791' + '\uf792' + '\uf793' + '\uf794' + '\uf795' + '\uf796' + '\uf797' + '\uf798' + '\uf799' + '\uf79a' + '\uf79b' + '\uf79c' + '\uf79d' + '\uf79e' + '\uf79f' + '\uf7a0' + '\uf7a1' + '\uf7a2' + '\uf7a3' + '\uf7a4' + '\uf7a5' + '\uf7a6' + '\uf7a7' + '\uf7a8' + '\uf7a9' + '\uf7aa' + '\uf7ab' + '\uf7ac' + '\uf7ad' + '\uf7ae' + '\uf7af' + '\uf7b0' + '\uf7b1' + '\uf7b2' + '\uf7b3' + '\uf7b4' + '\uf7b5' + '\uf7b6' + '\uf7b7' + '\uf7b8' + '\uf7b9' + '\uf7ba' + '\uf7bb' + '\uf7bc' + '\uf7bd' + '\uf7be' + '\uf7bf' + '\uf7c0' + '\uf7c1' + '\uf7c2' + '\uf7c3' + '\uf7c4' + '\uf7c5' + '\uf7c6' + '\uf7c7' + '\uf7c8' + '\uf7c9' + '\uf7ca' + '\uf7cb' + '\uf7cc' + '\uf7cd' + '\uf7ce' + '\uf7cf' + '\uf7d0' + '\uf7d1' + '\uf7d2' + '\uf7d3' + '\uf7d4' + '\uf7d5' + '\uf7d6' + '\uf7d7' + '\uf7d8' + '\uf7d9' + '\uf7da' + '\uf7db' + '\uf7dc' + '\uf7dd' + '\uf7de' + '\uf7df' + '\uf7e0' + '\uf7e1' + '\uf7e2' + '\uf7e3' + '\uf7e4' + '\uf7e5' + '\uf7e6' + '\uf7e7' + '\uf7e8' + '\uf7e9' + '\uf7ea' + '\uf7eb' + '\uf7ec' + '\uf7ed' + '\uf7ee' + '\uf7ef' + '\uf7f0' + '\uf7f1' + '\uf7f2' + '\uf7f3' + '\uf7f4' + '\uf7f5' + '\uf7f6' + '\uf7f7' + '\uf7f8' + '\uf7f9' + '\uf7fa' + '\uf7fb' + '\uf7fc' + '\uf7fd' + '\uf7fe' + '\uf7ff' +) + +### Encoding table +encoding_table = codecs.charmap_build(decoding_table) diff --git a/venv2/lib/python2.7/site-packages/pkg_resources/__init__.py b/venv2/lib/python2.7/site-packages/pkg_resources/__init__.py new file mode 100644 index 0000000..97e08d6 --- /dev/null +++ b/venv2/lib/python2.7/site-packages/pkg_resources/__init__.py @@ -0,0 +1,3286 @@ +# coding: utf-8 +""" +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 errno +import tempfile +import textwrap +import itertools +import inspect +import ntpath +import posixpath +from pkgutil import get_importer + +try: + import _imp +except ImportError: + # Python 3.2 compatibility + import imp as _imp + +try: + FileExistsError +except NameError: + FileExistsError = OSError + +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 . import py31compat +from pkg_resources.extern import appdirs +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') + + +__metaclass__ = type + + +if (3, 0) < sys.version_info < (3, 4): + raise RuntimeError("Python 3.4 or later is required") + +if six.PY2: + # Those builtin exceptions are only defined in Python 3 + PermissionError = None + NotADirectoryError = None + +# declare some globals that will be defined later to +# satisfy the linters. +require = None +working_set = None +add_activation_listener = None +resources_stream = None +cleanup_resources = None +resource_dir = None +resource_stream = None +set_extraction_path = None +resource_isdir = None +resource_string = None +iter_entry_points = None +resource_listdir = None +resource_filename = None +resource_exists = None +_distribution_finders = None +_namespace_handlers = None +_namespace_packages = None + + +class PEP440Warning(RuntimeWarning): + """ + Used when there is an issue with a version or specifier not complying with + PEP 440. + """ + + +def parse_version(v): + try: + return packaging.version.Version(v) + except packaging.version.InvalidVersion: + return packaging.version.LegacyVersion(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', + + # Warnings + 'PkgResourcesDeprecationWarning', + + # 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. + """ + from sysconfig 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: + """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). + """ + return ( + entry + for dist in self + for entry in dist.get_entry_map(group).values() + if name is None or name == entry.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, extras=None): + """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. + + `extras` is a list of the extras to be used with these requirements. + This is important because extra requirements may look like `my_req; + extra = "my_extra"`, which would otherwise be interpreted as a purely + optional requirement. Instead, we want to be able to assert that these + requirements are truly required. + """ + + # 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, extras): + 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, + replace_conflicting=replace_conflicting + ) + 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, existing=True): + """Invoke `callback` for all distributions + + If `existing=True` (default), + call on all existing ones, as well. + """ + if callback in self.callbacks: + return + self.callbacks.append(callback) + if not existing: + return + 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, extras=None): + """ + 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, ()) + (extras or (None,)) + ) + return not req.marker or any(extra_evals) + + +class Environment: + """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.6'``); + 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. + """ + py_compat = ( + self.python is None + or dist.py_version is None + or dist.py_version == self.python + ) + return py_compat 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, replace_conflicting=False): + """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. + """ + try: + dist = working_set.find(req) + except VersionConflict: + if not replace_conflicting: + raise + dist = None + 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 Exception: + 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(): + """ + Return the ``PYTHON_EGG_CACHE`` environment variable + or a platform-relevant user cache dir for an app + named "Python-Eggs". + """ + return ( + os.environ.get('PYTHON_EGG_CACHE') + or appdirs.user_cache_dir(appname='Python-Eggs') + ) + + +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 _get_metadata_path(self, name): + return self._fn(self.egg_info, name) + + def has_metadata(self, name): + if not self.egg_info: + return self.egg_info + + path = self._get_metadata_path(name) + return self._has(path) + + def get_metadata(self, name): + if not self.egg_info: + return "" + value = self._get(self._fn(self.egg_info, name)) + return value.decode('utf-8') if six.PY3 else value + + 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( + "Script {script!r} not found in metadata at {self.egg_info!r}" + .format(**locals()), + ) + 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): + self._validate_resource_path(resource_name) + if resource_name: + return os.path.join(base, *resource_name.split('/')) + return base + + @staticmethod + def _validate_resource_path(path): + """ + Validate the resource paths according to the docs. + https://setuptools.readthedocs.io/en/latest/pkg_resources.html#basic-resource-access + + >>> warned = getfixture('recwarn') + >>> warnings.simplefilter('always') + >>> vrp = NullProvider._validate_resource_path + >>> vrp('foo/bar.txt') + >>> bool(warned) + False + >>> vrp('../foo/bar.txt') + >>> bool(warned) + True + >>> warned.clear() + >>> vrp('/foo/bar.txt') + >>> bool(warned) + True + >>> vrp('foo/../../bar.txt') + >>> bool(warned) + True + >>> warned.clear() + >>> vrp('foo/f../bar.txt') + >>> bool(warned) + False + + Windows path separators are straight-up disallowed. + >>> vrp(r'\\foo/bar.txt') + Traceback (most recent call last): + ... + ValueError: Use of .. or absolute path in a resource path \ +is not allowed. + + >>> vrp(r'C:\\foo/bar.txt') + Traceback (most recent call last): + ... + ValueError: Use of .. or absolute path in a resource path \ +is not allowed. + + Blank values are allowed + + >>> vrp('') + >>> bool(warned) + False + + Non-string values are not. + + >>> vrp(None) + Traceback (most recent call last): + ... + AttributeError: ... + """ + invalid = ( + os.path.pardir in path.split(posixpath.sep) or + posixpath.isabs(path) or + ntpath.isabs(path) + ) + if not invalid: + return + + msg = "Use of .. or absolute path in a resource path is not allowed." + + # Aggressively disallow Windows absolute paths + if ntpath.isabs(path) and not posixpath.isabs(path): + raise ValueError(msg) + + # for compatibility, warn; in future + # raise ValueError(msg) + warnings.warn( + msg[:-1] + " and will raise exceptions in a future release.", + DeprecationWarning, + stacklevel=4, + ) + + 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_egg_path(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_names = 'SourceFileLoader', 'SourcelessFileLoader', + for name in loader_names: + loader_cls = getattr(importlib_machinery, name, type(None)) + register_loader_type(loader_cls, cls) + + +DefaultProvider._register() + + +class EmptyProvider(NullProvider): + """Provider that returns nothing for all requests""" + + module_path = None + + _isdir = _has = lambda self, path: False + + def _get(self, path): + return '' + + def _listdir(self, path): + return [] + + 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 zipfile.ZipFile(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 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 + fspath = fspath.rstrip(os.sep) + if fspath == self.loader.archive: + return '' + 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 _get_metadata_path(self, name): + return self.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': + raise KeyError("No metadata except PKG-INFO is available") + + with io.open(self.path, encoding='utf-8', errors="replace") as f: + metadata = f.read() + self._warn_on_replacement(metadata) + return metadata + + def _warn_on_replacement(self, metadata): + # Python 2.7 compat for: replacement_char = '�' + replacement_char = b'\xef\xbf\xbd'.decode('utf-8') + if replacement_char in metadata: + tmpl = "{self.path} could not be properly decoded in UTF-8" + msg = tmpl.format(**locals()) + warnings.warn(msg) + + 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_egg_path(subitem): + subpath = os.path.join(path_item, subitem) + dists = find_eggs_in_zip(zipimport.zipimporter(subpath), subpath) + for dist in dists: + yield dist + elif subitem.lower().endswith('.dist-info'): + subpath = os.path.join(path_item, subitem) + submeta = EggMetadata(zipimport.zipimporter(subpath)) + submeta.egg_info = subpath + yield Distribution.from_location(path_item, subitem, submeta) + + +register_finder(zipimport.zipimporter, find_eggs_in_zip) + + +def find_nothing(importer, path_item, only=False): + return () + + +register_finder(object, find_nothing) + + +def _by_version_descending(names): + """ + Given a list of filenames, return them in descending order + by version number. + + >>> names = 'bar', 'foo', 'Python-2.7.10.egg', 'Python-2.7.2.egg' + >>> _by_version_descending(names) + ['Python-2.7.10.egg', 'Python-2.7.2.egg', 'foo', 'bar'] + >>> names = 'Setuptools-1.2.3b1.egg', 'Setuptools-1.2.3.egg' + >>> _by_version_descending(names) + ['Setuptools-1.2.3.egg', 'Setuptools-1.2.3b1.egg'] + >>> names = 'Setuptools-1.2.3b1.egg', 'Setuptools-1.2.3.post1.egg' + >>> _by_version_descending(names) + ['Setuptools-1.2.3.post1.egg', 'Setuptools-1.2.3b1.egg'] + """ + def _by_version(name): + """ + Parse each component of the filename + """ + name, ext = os.path.splitext(name) + parts = itertools.chain(name.split('-'), [ext]) + return [packaging.version.parse(part) for part in parts] + + return sorted(names, key=_by_version, reverse=True) + + +def find_on_path(importer, path_item, only=False): + """Yield distributions accessible on a sys.path directory""" + path_item = _normalize_cached(path_item) + + if _is_unpacked_egg(path_item): + yield Distribution.from_filename( + path_item, metadata=PathMetadata( + path_item, os.path.join(path_item, 'EGG-INFO') + ) + ) + return + + entries = safe_listdir(path_item) + + # for performance, before sorting by version, + # screen entries for only those that will yield + # distributions + filtered = ( + entry + for entry in entries + if dist_factory(path_item, entry, only) + ) + + # scan for .egg and .egg-info in directory + path_item_entries = _by_version_descending(filtered) + for entry in path_item_entries: + fullpath = os.path.join(path_item, entry) + factory = dist_factory(path_item, entry, only) + for dist in factory(fullpath): + yield dist + + +def dist_factory(path_item, entry, only): + """ + Return a dist_factory for a path_item and entry + """ + lower = entry.lower() + is_meta = any(map(lower.endswith, ('.egg-info', '.dist-info'))) + return ( + distributions_from_metadata + if is_meta else + find_distributions + if not only and _is_egg_path(entry) else + resolve_egg_link + if not only and lower.endswith('.egg-link') else + NoDists() + ) + + +class NoDists: + """ + >>> bool(NoDists()) + False + + >>> list(NoDists()('anything')) + [] + """ + def __bool__(self): + return False + if six.PY2: + __nonzero__ = __bool__ + + def __call__(self, fullpath): + return iter(()) + + +def safe_listdir(path): + """ + Attempt to list contents of path, but suppress some exceptions. + """ + try: + return os.listdir(path) + except (PermissionError, NotADirectoryError): + pass + except OSError as e: + # Ignore the directory if does not exist, not a directory or + # permission denied + ignorable = ( + e.errno in (errno.ENOTDIR, errno.EACCES, errno.ENOENT) + # Python 2 on Windows needs to be handled this way :( + or getattr(e, "winerror", None) == 267 + ) + if not ignorable: + raise + return () + + +def distributions_from_metadata(path): + root = os.path.dirname(path) + if os.path.isdir(path): + if len(os.listdir(path)) == 0: + # empty metadata dir; skip + return + metadata = PathMetadata(root, path) + else: + metadata = FileMetadata(path) + entry = os.path.basename(path) + yield Distribution.from_location( + root, entry, metadata, precedence=DEVELOP_DIST, + ) + + +def non_empty_lines(path): + """ + Yield non-empty lines from file at path + """ + with open(path) as f: + for line in f: + line = line.strip() + if line: + yield line + + +def resolve_egg_link(path): + """ + Given a path to an .egg-link, resolve distributions + present in the referenced path. + """ + referenced_paths = non_empty_lines(path) + resolved_paths = ( + os.path.join(os.path.dirname(path), ref) + for ref in referenced_paths + ) + dist_groups = map(find_distributions, resolved_paths) + return next(dist_groups, ()) + + +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 + + # capture warnings due to #1111 + with warnings.catch_warnings(): + warnings.simplefilter("ignore") + 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 safe_sys_path_index(entry): + """ + Workaround for #520 and #513. + """ + try: + return sys_path.index(entry) + except ValueError: + return float('inf') + + 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 safe_sys_path_index(_normalize_cached(os.sep.join(parts))) + + new_path = sorted(orig_path, key=position_in_sys_path) + new_path = [_normalize_cached(p) for p in new_path] + + if isinstance(module.__path__, list): + module.__path__[:] = new_path + else: + module.__path__ = new_path + + +def declare_namespace(packageName): + """Declare that package 'packageName' is a namespace package""" + + _imp.acquire_lock() + try: + if packageName in _namespace_packages: + return + + path = sys.path + parent, _, _ = packageName.rpartition('.') + + if parent: + 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 or None, []).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(os.path.normpath(_cygwin_patch(filename)))) + + +def _cygwin_patch(filename): # pragma: nocover + """ + Contrary to POSIX 2008, on Cygwin, getcwd (3) contains + symlink components. Using + os.path.abspath() works around this limitation. A fix in os.getcwd() + would probably better, in Cygwin even more so, except + that this seems to be by design... + """ + return os.path.abspath(filename) if sys.platform == 'cygwin' else filename + + +def _normalize_cached(filename, _cache={}): + try: + return _cache[filename] + except KeyError: + _cache[filename] = result = normalize_path(filename) + return result + + +def _is_egg_path(path): + """ + Determine if given path appears to be an egg. + """ + return path.lower().endswith('.egg') + + +def _is_unpacked_egg(path): + """ + Determine if given path appears to be an unpacked egg. + """ + return ( + _is_egg_path(path) and + os.path.isfile(os.path.join(path, 'EGG-INFO', 'PKG-INFO')) + ) + + +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 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 = tuple(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.", + PkgResourcesDeprecationWarning, + 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) + + # Get the requirements for this entry point with all its extras and + # then resolve them. We have to pass `extras` along when resolving so + # that the working set knows what extras we want. Otherwise, for + # dist-info distributions, the working set will assume that the + # requirements for that extra are purely optional and skip over them. + reqs = self.dist.requires(self.extras) + items = working_set.resolve(reqs, env, installer, extras=self.extras) + 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. + """ + def is_version_line(line): + return 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: + """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 = self._get_version() + if version is None: + path = self._get_metadata_path_for_display(self.PKG_INFO) + msg = ( + "Missing 'Version:' header and/or {} file at path: {}" + ).format(self.PKG_INFO, path) + raise ValueError(msg, self) + + return version + + @property + def _dep_map(self): + """ + A map of extra to its list of (direct) requirements + for this distribution, including the null extra. + """ + try: + return self.__dep_map + except AttributeError: + self.__dep_map = self._filter_extras(self._build_dep_map()) + return self.__dep_map + + @staticmethod + def _filter_extras(dm): + """ + Given a mapping of extras to dependencies, strip off + environment markers and filter out any dependencies + not matching the markers. + """ + for extra in list(filter(None, dm)): + new_extra = extra + reqs = dm.pop(extra) + new_extra, _, marker = extra.partition(':') + fails_marker = marker and ( + invalid_marker(marker) + or not evaluate_marker(marker) + ) + if fails_marker: + reqs = [] + new_extra = safe_extra(new_extra) or None + + dm.setdefault(new_extra, []).extend(reqs) + return dm + + def _build_dep_map(self): + dm = {} + for name in 'requires.txt', 'depends.txt': + for extra, reqs in split_sections(self._get_metadata(name)): + 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_path_for_display(self, name): + """ + Return the path to the given metadata file, if available. + """ + try: + # We need to access _get_metadata_path() on the provider object + # directly rather than through this class's __getattr__() + # since _get_metadata_path() is marked private. + path = self._provider._get_metadata_path(name) + + # Handle exceptions e.g. in case the distribution's metadata + # provider doesn't support _get_metadata_path(). + except Exception: + return '[could not detect]' + + return path + + def _get_metadata(self, name): + if self.has_metadata(name): + for line in self.get_metadata_lines(name): + yield line + + def _get_version(self): + lines = self._get_metadata(self.PKG_INFO) + version = _version_from_file(lines) + + return version + + def activate(self, path=None, replace=False): + """Ensure distribution is importable on `path` (default=sys.path)""" + if path is None: + path = sys.path + self.insert_on(path, replace=replace) + 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) + + def __dir__(self): + return list( + set(super(Distribution, self).__dir__()) + | set( + attr for attr in self._provider.__dir__() + if not attr.startswith('_') + ) + ) + + if not hasattr(object, '__dir__'): + # python 2.7 not supported + del __dir__ + + @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): + """Ensure self.location is on path + + If replace=False (default): + - If location is already in path anywhere, do nothing. + - Else: + - If it's an egg and its parent directory is on path, + insert just ahead of the parent. + - Else: add to the end of path. + If replace=True: + - If location is already on path anywhere (not eggs) + or higher priority than its parent (eggs) + do nothing. + - Else: + - If it's an egg and its parent directory is on path, + insert just ahead of the parent, + removing any lower-priority entries. + - Else: add it to the front of path. + """ + + 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: + if replace: + break + else: + # don't modify path (even removing duplicates) if + # found and not replace + return + elif item == bdir and self.precedence == EGG_DIST: + # if it's an .egg, give it precedence over its directory + # UNLESS it's already been added to sys.path and replace=False + if (not replace) and nloc in npath[p:]: + return + 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 = self._get_version() + 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 []: + s_extra = safe_extra(extra.strip()) + dm[s_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() + try: + line += next(lines) + except StopIteration: + return + 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 _always_object(classes): + """ + Ensure object appears in the mro even + for old-style classes. + """ + if object not in classes: + return classes + (object,) + return classes + + +def _find_adapter(registry, ob): + """Return an adapter factory for `ob` from `registry`""" + types = _always_object(inspect.getmro(getattr(ob, '__class__', type(ob)))) + for t in types: + if t in registry: + return registry[t] + + +def ensure_directory(path): + """Ensure that the parent directory of `path` exists""" + dirname = os.path.dirname(path) + py31compat.makedirs(dirname, exist_ok=True) + + +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) + try: + mkdir(dirname, 0o755) + except FileExistsError: + pass + + +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 + g.update( + (name, getattr(manager, name)) + for name in dir(manager) + if not name.startswith('_') + ) + + +@_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 with replace=False and + # ensure that all distributions added to the working set in the future + # (e.g. by calling ``require()``) will get activated as well, + # with higher priority (replace=True). + tuple( + dist.activate(replace=False) + for dist in working_set + ) + add_activation_listener( + lambda dist: dist.activate(replace=True), + existing=False, + ) + working_set.entries = [] + # match order + list(map(working_set.add_entry, sys.path)) + globals().update(locals()) + +class PkgResourcesDeprecationWarning(Warning): + """ + Base class for warning about deprecations in ``pkg_resources`` + + This class is not derived from ``DeprecationWarning``, and as such is + visible by default. + """ diff --git a/venv2/lib/python2.7/site-packages/pkg_resources/_vendor/__init__.py b/venv2/lib/python2.7/site-packages/pkg_resources/_vendor/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/venv2/lib/python2.7/site-packages/pkg_resources/_vendor/appdirs.py b/venv2/lib/python2.7/site-packages/pkg_resources/_vendor/appdirs.py new file mode 100644 index 0000000..ae67001 --- /dev/null +++ b/venv2/lib/python2.7/site-packages/pkg_resources/_vendor/appdirs.py @@ -0,0 +1,608 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +# Copyright (c) 2005-2010 ActiveState Software Inc. +# Copyright (c) 2013 Eddy Petrișor + +"""Utilities for determining application-specific dirs. + +See <http://github.com/ActiveState/appdirs> for details and usage. +""" +# Dev Notes: +# - MSDN on where to store app data files: +# http://support.microsoft.com/default.aspx?scid=kb;en-us;310294#XSLTH3194121123120121120120 +# - Mac OS X: http://developer.apple.com/documentation/MacOSX/Conceptual/BPFileSystem/index.html +# - XDG spec for Un*x: http://standards.freedesktop.org/basedir-spec/basedir-spec-latest.html + +__version_info__ = (1, 4, 3) +__version__ = '.'.join(map(str, __version_info__)) + + +import sys +import os + +PY3 = sys.version_info[0] == 3 + +if PY3: + unicode = str + +if sys.platform.startswith('java'): + import platform + os_name = platform.java_ver()[3][0] + if os_name.startswith('Windows'): # "Windows XP", "Windows 7", etc. + system = 'win32' + elif os_name.startswith('Mac'): # "Mac OS X", etc. + system = 'darwin' + else: # "Linux", "SunOS", "FreeBSD", etc. + # Setting this to "linux2" is not ideal, but only Windows or Mac + # are actually checked for and the rest of the module expects + # *sys.platform* style strings. + system = 'linux2' +else: + system = sys.platform + + + +def user_data_dir(appname=None, appauthor=None, version=None, roaming=False): + r"""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. + "appauthor" (only used on Windows) is the name of the + appauthor or distributing body for this application. Typically + it is the owning company name. This falls back to appname. You may + pass False to disable it. + "version" is an optional version path element to append to the + path. You might want to use this if you want multiple versions + of your app to be able to run independently. If used, this + would typically be "<major>.<minor>". + Only applied when appname is present. + "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\<AppAuthor>\<AppName> + Win XP (roaming): C:\Documents and Settings\<username>\Local Settings\Application Data\<AppAuthor>\<AppName> + Win 7 (not roaming): C:\Users\<username>\AppData\Local\<AppAuthor>\<AppName> + Win 7 (roaming): C:\Users\<username>\AppData\Roaming\<AppAuthor>\<AppName> + + For Unix, we follow the XDG spec and support $XDG_DATA_HOME. + That means, by default "~/.local/share/<AppName>". + """ + if system == "win32": + if appauthor is None: + appauthor = appname + const = roaming and "CSIDL_APPDATA" or "CSIDL_LOCAL_APPDATA" + path = os.path.normpath(_get_win_folder(const)) + if appname: + if appauthor is not False: + path = os.path.join(path, appauthor, appname) + else: + path = os.path.join(path, appname) + elif system == 'darwin': + path = os.path.expanduser('~/Library/Application Support/') + if appname: + path = os.path.join(path, appname) + else: + path = os.getenv('XDG_DATA_HOME', os.path.expanduser("~/.local/share")) + if appname: + path = os.path.join(path, appname) + if appname and version: + path = os.path.join(path, version) + return path + + +def site_data_dir(appname=None, appauthor=None, version=None, multipath=False): + r"""Return full path to the user-shared data dir for this application. + + "appname" is the name of application. + If None, just the system directory is returned. + "appauthor" (only used on Windows) is the name of the + appauthor or distributing body for this application. Typically + it is the owning company name. This falls back to appname. You may + pass False to disable it. + "version" is an optional version path element to append to the + path. You might want to use this if you want multiple versions + of your app to be able to run independently. If used, this + would typically be "<major>.<minor>". + Only applied when appname is present. + "multipath" is an optional parameter only applicable to *nix + which indicates that the entire list of data dirs should be + returned. By default, the first item from XDG_DATA_DIRS is + returned, or '/usr/local/share/<AppName>', + if XDG_DATA_DIRS is not set + + Typical site data directories are: + Mac OS X: /Library/Application Support/<AppName> + Unix: /usr/local/share/<AppName> or /usr/share/<AppName> + Win XP: C:\Documents and Settings\All Users\Application Data\<AppAuthor>\<AppName> + Vista: (Fail! "C:\ProgramData" is a hidden *system* directory on Vista.) + Win 7: C:\ProgramData\<AppAuthor>\<AppName> # Hidden, but writeable on Win 7. + + For Unix, this is using the $XDG_DATA_DIRS[0] default. + + WARNING: Do not use this on Windows. See the Vista-Fail note above for why. + """ + if system == "win32": + if appauthor is None: + appauthor = appname + path = os.path.normpath(_get_win_folder("CSIDL_COMMON_APPDATA")) + if appname: + if appauthor is not False: + path = os.path.join(path, appauthor, appname) + else: + path = os.path.join(path, appname) + elif system == 'darwin': + path = os.path.expanduser('/Library/Application Support') + if appname: + path = os.path.join(path, appname) + else: + # XDG default for $XDG_DATA_DIRS + # only first, if multipath is False + path = os.getenv('XDG_DATA_DIRS', + os.pathsep.join(['/usr/local/share', '/usr/share'])) + pathlist = [os.path.expanduser(x.rstrip(os.sep)) for x in path.split(os.pathsep)] + if appname: + if version: + appname = os.path.join(appname, version) + pathlist = [os.sep.join([x, appname]) for x in pathlist] + + if multipath: + path = os.pathsep.join(pathlist) + else: + path = pathlist[0] + return path + + if appname and version: + path = os.path.join(path, version) + return path + + +def user_config_dir(appname=None, appauthor=None, version=None, roaming=False): + r"""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. + "appauthor" (only used on Windows) is the name of the + appauthor or distributing body for this application. Typically + it is the owning company name. This falls back to appname. You may + pass False to disable it. + "version" is an optional version path element to append to the + path. You might want to use this if you want multiple versions + of your app to be able to run independently. If used, this + would typically be "<major>.<minor>". + Only applied when appname is present. + "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 config directories are: + Mac OS X: same as user_data_dir + Unix: ~/.config/<AppName> # or in $XDG_CONFIG_HOME, if defined + Win *: same as user_data_dir + + For Unix, we follow the XDG spec and support $XDG_CONFIG_HOME. + That means, by default "~/.config/<AppName>". + """ + if system in ["win32", "darwin"]: + path = user_data_dir(appname, appauthor, None, roaming) + else: + path = os.getenv('XDG_CONFIG_HOME', os.path.expanduser("~/.config")) + if appname: + path = os.path.join(path, appname) + if appname and version: + path = os.path.join(path, version) + return path + + +def site_config_dir(appname=None, appauthor=None, version=None, multipath=False): + r"""Return full path to the user-shared data dir for this application. + + "appname" is the name of application. + If None, just the system directory is returned. + "appauthor" (only used on Windows) is the name of the + appauthor or distributing body for this application. Typically + it is the owning company name. This falls back to appname. You may + pass False to disable it. + "version" is an optional version path element to append to the + path. You might want to use this if you want multiple versions + of your app to be able to run independently. If used, this + would typically be "<major>.<minor>". + Only applied when appname is present. + "multipath" is an optional parameter only applicable to *nix + which indicates that the entire list of config dirs should be + returned. By default, the first item from XDG_CONFIG_DIRS is + returned, or '/etc/xdg/<AppName>', if XDG_CONFIG_DIRS is not set + + Typical site config directories are: + Mac OS X: same as site_data_dir + Unix: /etc/xdg/<AppName> or $XDG_CONFIG_DIRS[i]/<AppName> for each value in + $XDG_CONFIG_DIRS + Win *: same as site_data_dir + Vista: (Fail! "C:\ProgramData" is a hidden *system* directory on Vista.) + + For Unix, this is using the $XDG_CONFIG_DIRS[0] default, if multipath=False + + WARNING: Do not use this on Windows. See the Vista-Fail note above for why. + """ + if system in ["win32", "darwin"]: + path = site_data_dir(appname, appauthor) + if appname and version: + path = os.path.join(path, version) + else: + # XDG default for $XDG_CONFIG_DIRS + # only first, if multipath is False + path = os.getenv('XDG_CONFIG_DIRS', '/etc/xdg') + pathlist = [os.path.expanduser(x.rstrip(os.sep)) for x in path.split(os.pathsep)] + if appname: + if version: + appname = os.path.join(appname, version) + pathlist = [os.sep.join([x, appname]) for x in pathlist] + + if multipath: + path = os.pathsep.join(pathlist) + else: + path = pathlist[0] + return path + + +def user_cache_dir(appname=None, appauthor=None, version=None, opinion=True): + r"""Return full path to the user-specific cache dir for this application. + + "appname" is the name of application. + If None, just the system directory is returned. + "appauthor" (only used on Windows) is the name of the + appauthor or distributing body for this application. Typically + it is the owning company name. This falls back to appname. You may + pass False to disable it. + "version" is an optional version path element to append to the + path. You might want to use this if you want multiple versions + of your app to be able to run independently. If used, this + would typically be "<major>.<minor>". + Only applied when appname is present. + "opinion" (boolean) can be False to disable the appending of + "Cache" to the base app data dir for Windows. See + discussion below. + + Typical user cache directories are: + Mac OS X: ~/Library/Caches/<AppName> + Unix: ~/.cache/<AppName> (XDG default) + Win XP: C:\Documents and Settings\<username>\Local Settings\Application Data\<AppAuthor>\<AppName>\Cache + Vista: C:\Users\<username>\AppData\Local\<AppAuthor>\<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` above). 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. + This can be disabled with the `opinion=False` option. + """ + if system == "win32": + if appauthor is None: + appauthor = appname + path = os.path.normpath(_get_win_folder("CSIDL_LOCAL_APPDATA")) + if appname: + if appauthor is not False: + path = os.path.join(path, appauthor, appname) + else: + path = os.path.join(path, appname) + if opinion: + path = os.path.join(path, "Cache") + elif system == 'darwin': + path = os.path.expanduser('~/Library/Caches') + if appname: + path = os.path.join(path, appname) + else: + path = os.getenv('XDG_CACHE_HOME', os.path.expanduser('~/.cache')) + if appname: + path = os.path.join(path, appname) + if appname and version: + path = os.path.join(path, version) + return path + + +def user_state_dir(appname=None, appauthor=None, version=None, roaming=False): + r"""Return full path to the user-specific state dir for this application. + + "appname" is the name of application. + If None, just the system directory is returned. + "appauthor" (only used on Windows) is the name of the + appauthor or distributing body for this application. Typically + it is the owning company name. This falls back to appname. You may + pass False to disable it. + "version" is an optional version path element to append to the + path. You might want to use this if you want multiple versions + of your app to be able to run independently. If used, this + would typically be "<major>.<minor>". + Only applied when appname is present. + "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 state directories are: + Mac OS X: same as user_data_dir + Unix: ~/.local/state/<AppName> # or in $XDG_STATE_HOME, if defined + Win *: same as user_data_dir + + For Unix, we follow this Debian proposal <https://wiki.debian.org/XDGBaseDirectorySpecification#state> + to extend the XDG spec and support $XDG_STATE_HOME. + + That means, by default "~/.local/state/<AppName>". + """ + if system in ["win32", "darwin"]: + path = user_data_dir(appname, appauthor, None, roaming) + else: + path = os.getenv('XDG_STATE_HOME', os.path.expanduser("~/.local/state")) + if appname: + path = os.path.join(path, appname) + if appname and version: + path = os.path.join(path, version) + return path + + +def user_log_dir(appname=None, appauthor=None, version=None, opinion=True): + r"""Return full path to the user-specific log dir for this application. + + "appname" is the name of application. + If None, just the system directory is returned. + "appauthor" (only used on Windows) is the name of the + appauthor or distributing body for this application. Typically + it is the owning company name. This falls back to appname. You may + pass False to disable it. + "version" is an optional version path element to append to the + path. You might want to use this if you want multiple versions + of your app to be able to run independently. If used, this + would typically be "<major>.<minor>". + Only applied when appname is present. + "opinion" (boolean) can be False to disable the appending of + "Logs" to the base app data dir for Windows, and "log" to the + base cache dir for Unix. See discussion below. + + Typical user log directories are: + Mac OS X: ~/Library/Logs/<AppName> + Unix: ~/.cache/<AppName>/log # or under $XDG_CACHE_HOME if defined + Win XP: C:\Documents and Settings\<username>\Local Settings\Application Data\<AppAuthor>\<AppName>\Logs + Vista: C:\Users\<username>\AppData\Local\<AppAuthor>\<AppName>\Logs + + On Windows the only suggestion in the MSDN docs is that local settings + go in the `CSIDL_LOCAL_APPDATA` directory. (Note: I'm interested in + examples of what some windows apps use for a logs dir.) + + OPINION: This function appends "Logs" to the `CSIDL_LOCAL_APPDATA` + value for Windows and appends "log" to the user cache dir for Unix. + This can be disabled with the `opinion=False` option. + """ + if system == "darwin": + path = os.path.join( + os.path.expanduser('~/Library/Logs'), + appname) + elif system == "win32": + path = user_data_dir(appname, appauthor, version) + version = False + if opinion: + path = os.path.join(path, "Logs") + else: + path = user_cache_dir(appname, appauthor, version) + version = False + if opinion: + path = os.path.join(path, "log") + if appname and version: + path = os.path.join(path, version) + return path + + +class AppDirs(object): + """Convenience wrapper for getting application dirs.""" + def __init__(self, appname=None, appauthor=None, version=None, + roaming=False, multipath=False): + self.appname = appname + self.appauthor = appauthor + self.version = version + self.roaming = roaming + self.multipath = multipath + + @property + def user_data_dir(self): + return user_data_dir(self.appname, self.appauthor, + version=self.version, roaming=self.roaming) + + @property + def site_data_dir(self): + return site_data_dir(self.appname, self.appauthor, + version=self.version, multipath=self.multipath) + + @property + def user_config_dir(self): + return user_config_dir(self.appname, self.appauthor, + version=self.version, roaming=self.roaming) + + @property + def site_config_dir(self): + return site_config_dir(self.appname, self.appauthor, + version=self.version, multipath=self.multipath) + + @property + def user_cache_dir(self): + return user_cache_dir(self.appname, self.appauthor, + version=self.version) + + @property + def user_state_dir(self): + return user_state_dir(self.appname, self.appauthor, + version=self.version) + + @property + def user_log_dir(self): + return user_log_dir(self.appname, self.appauthor, + version=self.version) + + +#---- internal support stuff + +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. + """ + if PY3: + import winreg as _winreg + else: + 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" + ) + dir, type = _winreg.QueryValueEx(key, shell_folder_name) + return dir + + +def _get_win_folder_with_pywin32(csidl_name): + from win32com.shell import shellcon, shell + dir = shell.SHGetFolderPath(0, getattr(shellcon, csidl_name), 0, 0) + # Try to make this a unicode path because SHGetFolderPath does + # not return unicode strings when there is unicode data in the + # path. + try: + dir = unicode(dir) + + # 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 dir: + if ord(c) > 255: + has_high_char = True + break + if has_high_char: + try: + import win32api + dir = win32api.GetShortPathName(dir) + except ImportError: + pass + except UnicodeError: + pass + return dir + + +def _get_win_folder_with_ctypes(csidl_name): + import ctypes + + 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 + +def _get_win_folder_with_jna(csidl_name): + import array + from com.sun import jna + from com.sun.jna.platform import win32 + + buf_size = win32.WinDef.MAX_PATH * 2 + buf = array.zeros('c', buf_size) + shell = win32.Shell32.INSTANCE + shell.SHGetFolderPath(None, getattr(win32.ShlObj, csidl_name), None, win32.ShlObj.SHGFP_TYPE_CURRENT, buf) + dir = jna.Native.toString(buf.tostring()).rstrip("\0") + + # 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 dir: + if ord(c) > 255: + has_high_char = True + break + if has_high_char: + buf = array.zeros('c', buf_size) + kernel = win32.Kernel32.INSTANCE + if kernel.GetShortPathName(dir, buf, buf_size): + dir = jna.Native.toString(buf.tostring()).rstrip("\0") + + return dir + +if system == "win32": + try: + import win32com.shell + _get_win_folder = _get_win_folder_with_pywin32 + except ImportError: + try: + from ctypes import windll + _get_win_folder = _get_win_folder_with_ctypes + except ImportError: + try: + import com.sun.jna + _get_win_folder = _get_win_folder_with_jna + except ImportError: + _get_win_folder = _get_win_folder_from_registry + + +#---- self test code + +if __name__ == "__main__": + appname = "MyApp" + appauthor = "MyCompany" + + props = ("user_data_dir", + "user_config_dir", + "user_cache_dir", + "user_state_dir", + "user_log_dir", + "site_data_dir", + "site_config_dir") + + print("-- app dirs %s --" % __version__) + + print("-- app dirs (with optional 'version')") + dirs = AppDirs(appname, appauthor, version="1.0") + for prop in props: + print("%s: %s" % (prop, getattr(dirs, prop))) + + print("\n-- app dirs (without optional 'version')") + dirs = AppDirs(appname, appauthor) + for prop in props: + print("%s: %s" % (prop, getattr(dirs, prop))) + + print("\n-- app dirs (without optional 'appauthor')") + dirs = AppDirs(appname) + for prop in props: + print("%s: %s" % (prop, getattr(dirs, prop))) + + print("\n-- app dirs (with disabled 'appauthor')") + dirs = AppDirs(appname, appauthor=False) + for prop in props: + print("%s: %s" % (prop, getattr(dirs, prop))) diff --git a/venv2/lib/python2.7/site-packages/pkg_resources/_vendor/packaging/__about__.py b/venv2/lib/python2.7/site-packages/pkg_resources/_vendor/packaging/__about__.py new file mode 100644 index 0000000..95d330e --- /dev/null +++ b/venv2/lib/python2.7/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.8" + +__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/venv2/lib/python2.7/site-packages/pkg_resources/_vendor/packaging/__init__.py b/venv2/lib/python2.7/site-packages/pkg_resources/_vendor/packaging/__init__.py new file mode 100644 index 0000000..5ee6220 --- /dev/null +++ b/venv2/lib/python2.7/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/venv2/lib/python2.7/site-packages/pkg_resources/_vendor/packaging/_compat.py b/venv2/lib/python2.7/site-packages/pkg_resources/_vendor/packaging/_compat.py new file mode 100644 index 0000000..210bb80 --- /dev/null +++ b/venv2/lib/python2.7/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/venv2/lib/python2.7/site-packages/pkg_resources/_vendor/packaging/_structures.py b/venv2/lib/python2.7/site-packages/pkg_resources/_vendor/packaging/_structures.py new file mode 100644 index 0000000..ccc2786 --- /dev/null +++ b/venv2/lib/python2.7/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/venv2/lib/python2.7/site-packages/pkg_resources/_vendor/packaging/markers.py b/venv2/lib/python2.7/site-packages/pkg_resources/_vendor/packaging/markers.py new file mode 100644 index 0000000..892e578 --- /dev/null +++ b/venv2/lib/python2.7/site-packages/pkg_resources/_vendor/packaging/markers.py @@ -0,0 +1,301 @@ +# 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)) + + def serialize(self): + raise NotImplementedError + + +class Variable(Node): + + def serialize(self): + return str(self) + + +class Value(Node): + + def serialize(self): + return '"{0}"'.format(self) + + +class Op(Node): + + def serialize(self): + return str(self) + + +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("python_implementation") | # undocumented setuptools legacy + L("extra") +) +ALIASES = { + 'os.name': 'os_name', + 'sys.platform': 'sys_platform', + 'platform.version': 'platform_version', + 'platform.machine': 'platform_machine', + 'platform.python_implementation': 'platform_python_implementation', + 'python_implementation': 'platform_python_implementation' +} +VARIABLE.setParseAction(lambda s, l, t: Variable(ALIASES.get(t[0], t[0]))) + +VERSION_CMP = ( + L("===") | + L("==") | + L(">=") | + L("<=") | + L("!=") | + L("~=") | + L(">") | + L("<") +) + +MARKER_OP = VERSION_CMP | L("not in") | L("in") +MARKER_OP.setParseAction(lambda s, l, t: Op(t[0])) + +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 " ".join([m.serialize() for m in 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.serialize(), rhs])) + except InvalidSpecifier: + pass + else: + return spec.contains(lhs) + + oper = _operators.get(op.serialize()) + 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/venv2/lib/python2.7/site-packages/pkg_resources/_vendor/packaging/requirements.py b/venv2/lib/python2.7/site-packages/pkg_resources/_vendor/packaging/requirements.py new file mode 100644 index 0000000..0c8c4a3 --- /dev/null +++ b/venv2/lib/python2.7/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/venv2/lib/python2.7/site-packages/pkg_resources/_vendor/packaging/specifiers.py b/venv2/lib/python2.7/site-packages/pkg_resources/_vendor/packaging/specifiers.py new file mode 100644 index 0000000..7f5a76c --- /dev/null +++ b/venv2/lib/python2.7/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/venv2/lib/python2.7/site-packages/pkg_resources/_vendor/packaging/utils.py b/venv2/lib/python2.7/site-packages/pkg_resources/_vendor/packaging/utils.py new file mode 100644 index 0000000..942387c --- /dev/null +++ b/venv2/lib/python2.7/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/venv2/lib/python2.7/site-packages/pkg_resources/_vendor/packaging/version.py b/venv2/lib/python2.7/site-packages/pkg_resources/_vendor/packaging/version.py new file mode 100644 index 0000000..83b5ee8 --- /dev/null +++ b/venv2/lib/python2.7/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/venv2/lib/python2.7/site-packages/pkg_resources/_vendor/pyparsing.py b/venv2/lib/python2.7/site-packages/pkg_resources/_vendor/pyparsing.py new file mode 100644 index 0000000..cf75e1e --- /dev/null +++ b/venv2/lib/python2.7/site-packages/pkg_resources/_vendor/pyparsing.py @@ -0,0 +1,5742 @@ +# module pyparsing.py +# +# Copyright (c) 2003-2018 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>!"}), built up using L{Word}, L{Literal}, and L{And} elements +(L{'+'<ParserElement.__add__>} operator gives L{And} expressions, strings are auto-converted to +L{Literal} expressions):: + + 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 L{ParseResults} object returned from L{ParserElement.parseString<ParserElement.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 + + +Getting Started - +----------------- +Visit the classes L{ParserElement} and L{ParseResults} to see the base classes that most other pyparsing +classes inherit from. Use the docstrings for examples of how to: + - construct literal match expressions from L{Literal} and L{CaselessLiteral} classes + - construct character word-group expressions using the L{Word} class + - see how to create repetitive expressions using L{ZeroOrMore} and L{OneOrMore} classes + - use L{'+'<And>}, L{'|'<MatchFirst>}, L{'^'<Or>}, and L{'&'<Each>} operators to combine simple expressions into more complex ones + - associate names with your parsed results using L{ParserElement.setResultsName} + - find some helpful expression short-cuts like L{delimitedList} and L{oneOf} + - find more useful common expressions in the L{pyparsing_common} namespace class +""" + +__version__ = "2.2.1" +__versionTime__ = "18 Sep 2018 00:49 UTC" +__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 traceback +import types +from datetime import datetime + +try: + from _thread import RLock +except ImportError: + from threading import RLock + +try: + # Python 3 + from collections.abc import Iterable + from collections.abc import MutableMapping +except ImportError: + # Python 2.7 + from collections import Iterable + from collections import MutableMapping + +try: + from collections import OrderedDict as _OrderedDict +except ImportError: + try: + from ordereddict import OrderedDict as _OrderedDict + except ImportError: + _OrderedDict = None + +#~ 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', +'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', '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', +'CloseMatch', 'tokenMap', 'pyparsing_common', +] + +system_version = tuple(sys.version_info)[:3] +PY_3 = system_version[0] == 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: + # Else encode it + ret = unicode(obj).encode(sys.getdefaultencoding(), 'xmlcharrefreplace') + xmlcharref = Regex(r'&#\d+;') + xmlcharref.setParseAction(lambda t: '\\u' + hex(int(t[0][2:-1]))[2:]) + return xmlcharref.transformString(ret) + + # 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_uppercase + string.ascii_lowercase +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 + self.args = (pstr, loc, msg) + + @classmethod + def _from_exception(cls, pe): + """ + internal factory method to simplify creating one type of ParseException + from another - avoids having __init__ signature conflicts among subclasses + """ + return cls(pe.pstr, pe.loc, pe.msg, pe.parserElement) + + 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 "lineno col line".split() + dir(type(self)) + +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 + + Example:: + try: + Word(nums).setName("integer").parseString("ABC") + except ParseException as pe: + print(pe) + print("column: {}".format(pe.col)) + + prints:: + Expected integer (at char 0), (line:1, col:1) + column: 1 + """ + pass + +class ParseFatalException(ParseBaseException): + """user-throwable exception thrown when inconsistent parse content + is found; stops all parsing immediately""" + pass + +class ParseSyntaxException(ParseFatalException): + """just like L{ParseFatalException}, but thrown internally when an + L{ErrorStop<And._ErrorStop>} ('-' operator) indicates that parsing is to stop + immediately because an unbacktrackable syntax error has been found""" + pass + +#~ 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 L{ParserElement.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[0]) + 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>} - see L{ParserElement.setResultsName}) + + Example:: + integer = Word(nums) + date_str = (integer.setResultsName("year") + '/' + + integer.setResultsName("month") + '/' + + integer.setResultsName("day")) + # equivalent form: + # date_str = integer("year") + '/' + integer("month") + '/' + integer("day") + + # parseString returns a ParseResults object + result = date_str.parseString("1999/12/31") + + def test(s, fn=repr): + print("%s -> %s" % (s, fn(eval(s)))) + test("list(result)") + test("result[0]") + test("result['month']") + test("result.day") + test("'month' in result") + test("'minutes' in result") + test("result.dump()", str) + prints:: + list(result) -> ['1999', '/', '12', '/', '31'] + result[0] -> '1999' + result['month'] -> '12' + result.day -> '31' + 'month' in result -> True + 'minutes' in result -> False + result.dump() -> ['1999', '/', '12', '/', '31'] + - day: 31 + - month: 12 + - year: 1999 + """ + def __new__(cls, toklist=None, 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=None, name=None, asList=True, modal=True, isinstance=isinstance ): + if self.__doinit: + self.__doinit = False + self.__name = None + self.__parent = None + self.__accumNames = {} + self.__asList = asList + self.__modal = modal + if toklist is None: + toklist = [] + 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,slice)): + 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,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 ( not not self.__toklist ) + __nonzero__ = __bool__ + def __iter__( self ): return iter( self.__toklist ) + def __reversed__( self ): return iter( self.__toklist[::-1] ) + def _iterkeys( self ): + if hasattr(self.__tokdict, "iterkeys"): + return self.__tokdict.iterkeys() + else: + return iter(self.__tokdict) + + def _itervalues( self ): + 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 + """Returns an iterator of all named result keys (Python 3.x only).""" + + values = _itervalues + """Returns an iterator of all named result values (Python 3.x only).""" + + items = _iteritems + """Returns an iterator of all named result key-value tuples (Python 3.x only).""" + + else: + iterkeys = _iterkeys + """Returns an iterator of all named result keys (Python 2.x only).""" + + itervalues = _itervalues + """Returns an iterator of all named result values (Python 2.x only).""" + + iteritems = _iteritems + """Returns an iterator of all named result key-value tuples (Python 2.x only).""" + + def keys( self ): + """Returns all named result keys (as a list in Python 2.x, as an iterator in Python 3.x).""" + return list(self.iterkeys()) + + def values( self ): + """Returns all named result values (as a list in Python 2.x, as an iterator in Python 3.x).""" + return list(self.itervalues()) + + def items( self ): + """Returns all named result key-values (as a list of tuples in Python 2.x, as an iterator in Python 3.x).""" + 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=C{last}). + Supports both C{list} and C{dict} semantics for C{pop()}. If passed no + argument or an integer argument, it will use C{list} semantics + and pop tokens from the list of parsed tokens. If passed a + non-integer argument (most likely a string), it will use C{dict} + semantics and pop the corresponding value from any defined + results names. A second default return value argument is + supported, just as in C{dict.pop()}. + + Example:: + def remove_first(tokens): + tokens.pop(0) + print(OneOrMore(Word(nums)).parseString("0 123 321")) # -> ['0', '123', '321'] + print(OneOrMore(Word(nums)).addParseAction(remove_first).parseString("0 123 321")) # -> ['123', '321'] + + label = Word(alphas) + patt = label("LABEL") + OneOrMore(Word(nums)) + print(patt.parseString("AAB 123 321").dump()) + + # Use pop() in a parse action to remove named result (note that corresponding value is not + # removed from list form of results) + def remove_LABEL(tokens): + tokens.pop("LABEL") + return tokens + patt.addParseAction(remove_LABEL) + print(patt.parseString("AAB 123 321").dump()) + prints:: + ['AAB', '123', '321'] + - LABEL: AAB + + ['AAB', '123', '321'] + """ + 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. + + Similar to C{dict.get()}. + + Example:: + integer = Word(nums) + date_str = integer("year") + '/' + integer("month") + '/' + integer("day") + + result = date_str.parseString("1999/12/31") + print(result.get("year")) # -> '1999' + print(result.get("hour", "not specified")) # -> 'not specified' + print(result.get("hour")) # -> None + """ + 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. + + Similar to C{list.insert()}. + + Example:: + print(OneOrMore(Word(nums)).parseString("0 123 321")) # -> ['0', '123', '321'] + + # use a parse action to insert the parse location in the front of the parsed results + def insert_locn(locn, tokens): + tokens.insert(0, locn) + print(OneOrMore(Word(nums)).addParseAction(insert_locn).parseString("0 123 321")) # -> [0, '0', '123', '321'] + """ + self.__toklist.insert(index, insStr) + # fixup indices in token dictionary + 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. + + Example:: + print(OneOrMore(Word(nums)).parseString("0 123 321")) # -> ['0', '123', '321'] + + # use a parse action to compute the sum of the parsed integers, and add it to the end + def append_sum(tokens): + tokens.append(sum(map(int, tokens))) + print(OneOrMore(Word(nums)).addParseAction(append_sum).parseString("0 123 321")) # -> ['0', '123', '321', 444] + """ + self.__toklist.append(item) + + def extend( self, itemseq ): + """ + Add sequence of elements to end of ParseResults list of elements. + + Example:: + patt = OneOrMore(Word(alphas)) + + # use a parse action to append the reverse of the matched strings, to make a palindrome + def make_palindrome(tokens): + tokens.extend(reversed([t[::-1] for t in tokens])) + return ''.join(tokens) + print(patt.addParseAction(make_palindrome).parseString("lskdj sdlkjf lksd")) # -> 'lskdjsdlkjflksddsklfjkldsjdksl' + """ + 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: + # useful for merging many ParseResults using sum() builtin + return self.copy() + else: + # this may raise a TypeError - so be it + return other + self + + 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. + + Example:: + patt = OneOrMore(Word(alphas)) + result = patt.parseString("sldkj lsdkj sldkj") + # even though the result prints in string-like form, it is actually a pyparsing ParseResults + print(type(result), result) # -> <class 'pyparsing.ParseResults'> ['sldkj', 'lsdkj', 'sldkj'] + + # Use asList() to create an actual list + result_list = result.asList() + print(type(result_list), result_list) # -> <class 'list'> ['sldkj', 'lsdkj', 'sldkj'] + """ + return [res.asList() if isinstance(res,ParseResults) else res for res in self.__toklist] + + def asDict( self ): + """ + Returns the named parse results as a nested dictionary. + + Example:: + integer = Word(nums) + date_str = integer("year") + '/' + integer("month") + '/' + integer("day") + + result = date_str.parseString('12/31/1999') + print(type(result), repr(result)) # -> <class 'pyparsing.ParseResults'> (['12', '/', '31', '/', '1999'], {'day': [('1999', 4)], 'year': [('12', 0)], 'month': [('31', 2)]}) + + result_dict = result.asDict() + print(type(result_dict), repr(result_dict)) # -> <class 'dict'> {'day': '1999', 'year': '12', 'month': '31'} + + # even though a ParseResults supports dict-like access, sometime you just need to have a dict + import json + print(json.dumps(result)) # -> Exception: TypeError: ... is not JSON serializable + print(json.dumps(result.asDict())) # -> {"month": "31", "day": "1999", "year": "12"} + """ + if PY_3: + item_fn = self.items + else: + item_fn = self.iteritems + + def toItem(obj): + if isinstance(obj, ParseResults): + if obj.haskeys(): + return obj.asDict() + else: + return [toItem(v) for v in obj] + else: + return obj + + return dict((k,toItem(v)) for k,v in item_fn()) + + 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 ): + """ + (Deprecated) 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): + r""" + Returns the results name for this token expression. Useful when several + different expressions might match at a particular location. + + Example:: + integer = Word(nums) + ssn_expr = Regex(r"\d\d\d-\d\d-\d\d\d\d") + house_number_expr = Suppress('#') + Word(nums, alphanums) + user_data = (Group(house_number_expr)("house_number") + | Group(ssn_expr)("ssn") + | Group(integer)("age")) + user_info = OneOrMore(user_data) + + result = user_info.parseString("22 111-22-3333 #221B") + for item in result: + print(item.getName(), ':', item[0]) + prints:: + age : 22 + ssn : 111-22-3333 + house_number : 221B + """ + 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 + next(iter(self.__tokdict.values()))[0][1] in (0,-1)): + return next(iter(self.__tokdict.keys())) + else: + return None + + def dump(self, indent='', depth=0, full=True): + """ + 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. + + Example:: + integer = Word(nums) + date_str = integer("year") + '/' + integer("month") + '/' + integer("day") + + result = date_str.parseString('12/31/1999') + print(result.dump()) + prints:: + ['12', '/', '31', '/', '1999'] + - day: 1999 + - month: 31 + - year: 12 + """ + out = [] + NL = '\n' + out.append( indent+_ustr(self.asList()) ) + if full: + if self.haskeys(): + items = sorted((str(k), v) for k,v in 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(repr(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}) + + Example:: + ident = Word(alphas, alphanums) + num = Word(nums) + func = Forward() + term = ident | num | Group('(' + func + ')') + func <<= ident + Group(Optional(delimitedList(term))) + result = func.parseString("fna a,b,(fnb c,d,200),100") + result.pprint(width=40) + prints:: + ['fna', + ['a', + 'b', + ['(', 'fnb', ['c', 'd', '200'], ')'], + '100']] + """ + 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 __getnewargs__(self): + return self.__toklist, self.__name, self.__asList, self.__modal + + def __dir__(self): + return (dir(type(self)) + list(self.keys())) + +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 0<loc<len(s) and s[loc-1] == '\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] + + # traceback return data structure changed in Py3.5 - normalize back to plain tuples + if system_version[:2] >= (3,5): + def extract_stack(limit=0): + # special handling for Python 3.5.0 - extra deep call stack by 1 + offset = -3 if system_version == (3,5,0) else -2 + frame_summary = traceback.extract_stack(limit=-offset+limit-1)[offset] + return [frame_summary[:2]] + def extract_tb(tb, limit=0): + frames = traceback.extract_tb(tb, limit=limit) + frame_summary = frames[-1] + return [frame_summary[:2]] + else: + extract_stack = traceback.extract_stack + extract_tb = traceback.extract_tb + + # synthesize what would be returned by traceback.extract_stack at the call to + # user's parse action 'func', so that we don't incur call penalty at parse time + + LINE_DIFF = 6 + # IF ANY CODE CHANGES, EVEN JUST COMMENTS OR BLANK LINES, BETWEEN THE NEXT LINE AND + # THE CALL TO FUNC INSIDE WRAPPER, LINE_DIFF MUST BE MODIFIED!!!! + this_line = extract_stack(limit=2)[-1] + pa_call_line_synth = (this_line[0], this_line[1]+LINE_DIFF) + + def wrapper(*args): + while 1: + try: + ret = func(*args[limit[0]:]) + foundArity[0] = True + return ret + except TypeError: + # re-raise TypeErrors if they did not come from our arity testing + if foundArity[0]: + raise + else: + try: + tb = sys.exc_info()[-1] + if not extract_tb(tb, limit=2)[-1][:2] == pa_call_line_synth: + raise + finally: + del tb + + if limit[0] <= maxargs: + limit[0] += 1 + continue + raise + + # copy func name to wrapper for sensible debug output + func_name = "<parse action>" + try: + func_name = getattr(func, '__name__', + getattr(func, '__class__').__name__) + except Exception: + func_name = str(func) + wrapper.__name__ = func_name + + return wrapper + +class ParserElement(object): + """Abstract base level parser element class.""" + DEFAULT_WHITE_CHARS = " \n\t\r" + verbose_stacktrace = False + + @staticmethod + def setDefaultWhitespaceChars( chars ): + r""" + Overrides the default whitespace chars + + Example:: + # default whitespace chars are space, <TAB> and newline + OneOrMore(Word(alphas)).parseString("abc def\nghi jkl") # -> ['abc', 'def', 'ghi', 'jkl'] + + # change to just treat newline as significant + ParserElement.setDefaultWhitespaceChars(" \t") + OneOrMore(Word(alphas)).parseString("abc def\nghi jkl") # -> ['abc', 'def'] + """ + ParserElement.DEFAULT_WHITE_CHARS = chars + + @staticmethod + def inlineLiteralsUsing(cls): + """ + Set class to be used for inclusion of string literals into a parser. + + Example:: + # default literal class used is Literal + integer = Word(nums) + date_str = integer("year") + '/' + integer("month") + '/' + integer("day") + + date_str.parseString("1999/12/31") # -> ['1999', '/', '12', '/', '31'] + + + # change to Suppress + ParserElement.inlineLiteralsUsing(Suppress) + date_str = integer("year") + '/' + integer("month") + '/' + integer("day") + + date_str.parseString("1999/12/31") # -> ['1999', '12', '31'] + """ + 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. + + Example:: + integer = Word(nums).setParseAction(lambda toks: int(toks[0])) + integerK = integer.copy().addParseAction(lambda toks: toks[0]*1024) + Suppress("K") + integerM = integer.copy().addParseAction(lambda toks: toks[0]*1024*1024) + Suppress("M") + + print(OneOrMore(integerK | integerM | integer).parseString("5K 100 640K 256M")) + prints:: + [5120, 100, 655360, 268435456] + Equivalent form of C{expr.copy()} is just C{expr()}:: + integerM = integer().addParseAction(lambda toks: toks[0]*1024*1024) + Suppress("M") + """ + 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, makes debugging and exception messages clearer. + + Example:: + Word(nums).parseString("ABC") # -> Exception: Expected W:(0123...) (at char 0), (line:1, col:1) + Word(nums).setName("integer").parseString("ABC") # -> Exception: Expected integer (at char 0), (line:1, col:1) + """ + 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__>}. + + Example:: + date_str = (integer.setResultsName("year") + '/' + + integer.setResultsName("month") + '/' + + integer.setResultsName("day")) + + # equivalent form: + date_str = integer("year") + '/' + integer("month") + '/' + integer("day") + """ + 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 one or more actions 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. + + Optional keyword arguments: + - callDuringTry = (default=C{False}) indicate if parse action should be run during lookaheads and alternate testing + + 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. + + Example:: + integer = Word(nums) + date_str = integer + '/' + integer + '/' + integer + + date_str.parseString("1999/12/31") # -> ['1999', '/', '12', '/', '31'] + + # use parse action to convert to ints at parse time + integer = Word(nums).setParseAction(lambda toks: int(toks[0])) + date_str = integer + '/' + integer + '/' + integer + + # note that integer fields are now ints, not strings + date_str.parseString("1999/12/31") # -> [1999, '/', 12, '/', 31] + """ + self.parseAction = list(map(_trim_arity, list(fns))) + self.callDuringTry = kwargs.get("callDuringTry", False) + return self + + def addParseAction( self, *fns, **kwargs ): + """ + Add one or more parse actions to expression's list of parse actions. See L{I{setParseAction}<setParseAction>}. + + See examples in L{I{copy}<copy>}. + """ + 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>} for function call signatures. Unlike C{setParseAction}, + functions passed to C{addCondition} need to return boolean success/fail of the condition. + + Optional keyword arguments: + - message = define a custom message to be used in the raised exception + - fatal = if True, will raise ParseFatalException to stop parsing immediately; otherwise will raise ParseException + + Example:: + integer = Word(nums).setParseAction(lambda toks: int(toks[0])) + year_int = integer.copy() + year_int.addCondition(lambda toks: toks[0] >= 2000, message="Only support years 2000 and later") + date_str = year_int + '/' + integer + '/' + integer + + result = date_str.parseString("1999/12/31") # -> Exception: Only support years 2000 and later (at char 0), (line:1, col:1) + """ + msg = kwargs.get("message", "failed user-defined condition") + exc_type = ParseFatalException if kwargs.get("fatal", False) else ParseException + for fn in fns: + def pa(s,l,t): + if not bool(_trim_arity(fn)(s,l,t)): + raise exc_type(s,l,msg) + 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 preloc >= 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) + + def canParseNext(self, instring, loc): + try: + self.tryParse(instring, loc) + except (ParseException, IndexError): + return False + else: + return True + + class _UnboundedCache(object): + def __init__(self): + cache = {} + self.not_in_cache = not_in_cache = object() + + def get(self, key): + return cache.get(key, not_in_cache) + + def set(self, key, value): + cache[key] = value + + def clear(self): + cache.clear() + + def cache_len(self): + return len(cache) + + self.get = types.MethodType(get, self) + self.set = types.MethodType(set, self) + self.clear = types.MethodType(clear, self) + self.__len__ = types.MethodType(cache_len, self) + + if _OrderedDict is not None: + class _FifoCache(object): + def __init__(self, size): + self.not_in_cache = not_in_cache = object() + + cache = _OrderedDict() + + def get(self, key): + return cache.get(key, not_in_cache) + + def set(self, key, value): + cache[key] = value + while len(cache) > size: + try: + cache.popitem(False) + except KeyError: + pass + + def clear(self): + cache.clear() + + def cache_len(self): + return len(cache) + + self.get = types.MethodType(get, self) + self.set = types.MethodType(set, self) + self.clear = types.MethodType(clear, self) + self.__len__ = types.MethodType(cache_len, self) + + else: + class _FifoCache(object): + def __init__(self, size): + self.not_in_cache = not_in_cache = object() + + cache = {} + key_fifo = collections.deque([], size) + + def get(self, key): + return cache.get(key, not_in_cache) + + def set(self, key, value): + cache[key] = value + while len(key_fifo) > size: + cache.pop(key_fifo.popleft(), None) + key_fifo.append(key) + + def clear(self): + cache.clear() + key_fifo.clear() + + def cache_len(self): + return len(cache) + + self.get = types.MethodType(get, self) + self.set = types.MethodType(set, self) + self.clear = types.MethodType(clear, self) + self.__len__ = types.MethodType(cache_len, self) + + # argument cache for optimizing repeated calls when backtracking through recursive expressions + packrat_cache = {} # this is set later by enabledPackrat(); this is here so that resetCache() doesn't fail + packrat_cache_lock = RLock() + packrat_cache_stats = [0, 0] + + # 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 ): + HIT, MISS = 0, 1 + lookup = (self, instring, loc, callPreParse, doActions) + with ParserElement.packrat_cache_lock: + cache = ParserElement.packrat_cache + value = cache.get(lookup) + if value is cache.not_in_cache: + ParserElement.packrat_cache_stats[MISS] += 1 + try: + value = self._parseNoCache(instring, loc, doActions, callPreParse) + except ParseBaseException as pe: + # cache a copy of the exception, without the traceback + cache.set(lookup, pe.__class__(*pe.args)) + raise + else: + cache.set(lookup, (value[0], value[1].copy())) + return value + else: + ParserElement.packrat_cache_stats[HIT] += 1 + if isinstance(value, Exception): + raise value + return (value[0], value[1].copy()) + + _parse = _parseNoCache + + @staticmethod + def resetCache(): + ParserElement.packrat_cache.clear() + ParserElement.packrat_cache_stats[:] = [0] * len(ParserElement.packrat_cache_stats) + + _packratEnabled = False + @staticmethod + def enablePackrat(cache_size_limit=128): + """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. + + Parameters: + - cache_size_limit - (default=C{128}) - if an integer value is provided + will limit the size of the packrat cache; if None is passed, then + the cache size will be unbounded; if 0 is passed, the cache will + be effectively disabled. + + 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. + + Example:: + import pyparsing + pyparsing.ParserElement.enablePackrat() + """ + if not ParserElement._packratEnabled: + ParserElement._packratEnabled = True + if cache_size_limit is None: + ParserElement.packrat_cache = ParserElement._UnboundedCache() + else: + ParserElement.packrat_cache = ParserElement._FifoCache(cache_size_limit) + 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} + + Example:: + Word('a').parseString('aaaaabaaa') # -> ['aaaaa'] + Word('a').parseString('aaaaabaaa', parseAll=True) # -> Exception: Expected end of text + """ + 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. + + Example:: + source = "sldjf123lsdjjkf345sldkjf879lkjsfd987" + print(source) + for tokens,start,end in Word(alphas).scanString(source): + print(' '*start + '^'*(end-start)) + print(' '*start + tokens[0]) + + prints:: + + sldjf123lsdjjkf345sldkjf879lkjsfd987 + ^^^^^ + sldjf + ^^^^^^^ + lsdjjkf + ^^^^^^ + sldkjf + ^^^^^^ + lkjsfd + """ + 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. + + Example:: + wd = Word(alphas) + wd.setParseAction(lambda toks: toks[0].title()) + + print(wd.transformString("now is the winter of our discontent made glorious summer by this sun of york.")) + Prints:: + Now Is The Winter Of Our Discontent Made Glorious Summer By This Sun Of York. + """ + 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. + + Example:: + # a capitalized word starts with an uppercase letter, followed by zero or more lowercase letters + cap_word = Word(alphas.upper(), alphas.lower()) + + print(cap_word.searchString("More than Iron, more than Lead, more than Gold I need Electricity")) + + # the sum() builtin can be used to merge results into a single ParseResults object + print(sum(cap_word.searchString("More than Iron, more than Lead, more than Gold I need Electricity"))) + prints:: + [['More'], ['Iron'], ['Lead'], ['Gold'], ['I'], ['Electricity']] + ['More', 'Iron', 'Lead', 'Gold', 'I', 'Electricity'] + """ + 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 split(self, instring, maxsplit=_MAX_INT, includeSeparators=False): + """ + Generator method to split a string using the given expression as a separator. + May be called with optional C{maxsplit} argument, to limit the number of splits; + and the optional C{includeSeparators} argument (default=C{False}), if the separating + matching text should be included in the split results. + + Example:: + punc = oneOf(list(".,;:/-!?")) + print(list(punc.split("This, this?, this sentence, is badly punctuated!"))) + prints:: + ['This', ' this', '', ' this sentence', ' is badly punctuated', ''] + """ + splits = 0 + last = 0 + for t,s,e in self.scanString(instring, maxMatches=maxsplit): + yield instring[last:s] + if includeSeparators: + yield t[0] + last = e + yield instring[last:] + + def __add__(self, other ): + """ + Implementation of + operator - returns C{L{And}}. Adding strings to a ParserElement + converts them to L{Literal}s by default. + + Example:: + greet = Word(alphas) + "," + Word(alphas) + "!" + hello = "Hello, World!" + print (hello, "->", greet.parseString(hello)) + Prints:: + Hello, World! -> ['Hello', ',', 'World', '!'] + """ + 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 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=False}. + + 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}}. + + Example:: + # these are equivalent + userdata = Word(alphas).setResultsName("name") + Word(nums+"-").setResultsName("socsecno") + userdata = Word(alphas)("name") + Word(nums+"-")("socsecno") + """ + 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. + + Example:: + patt = OneOrMore(Word(alphas)) + patt.parseString('ablaj /* comment */ lskjd') # -> ['ablaj'] + + patt.ignore(cStyleComment) + patt.parseString('ablaj /* comment */ lskjd') # -> ['ablaj', 'lskjd'] + """ + if isinstance(other, basestring): + other = Suppress(other) + + if isinstance( other, Suppress ): + if other not in self.ignoreExprs: + self.ignoreExprs.append(other) + 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. + + Example:: + wd = Word(alphas).setName("alphaword") + integer = Word(nums).setName("numword") + term = wd | integer + + # turn on debugging for wd + wd.setDebug() + + OneOrMore(term).parseString("abc 123 xyz 890") + + prints:: + Match alphaword at loc 0(1,1) + Matched alphaword -> ['abc'] + Match alphaword at loc 3(1,4) + Exception raised:Expected alphaword (at char 4), (line:1, col:5) + Match alphaword at loc 7(1,8) + Matched alphaword -> ['xyz'] + Match alphaword at loc 11(1,12) + Exception raised:Expected alphaword (at char 12), (line:1, col:13) + Match alphaword at loc 15(1,16) + Exception raised:Expected alphaword (at char 15), (line:1, col:16) + + The output shown is that produced by the default debug actions - custom debug actions can be + specified using L{setDebugActions}. Prior to attempting + to match the C{wd} expression, the debugging message C{"Match <exprname> at loc <n>(<line>,<col>)"} + is shown. Then if the parse succeeds, a C{"Matched"} message is shown, or an C{"Exception raised"} + message is shown. Also note the use of L{setName} to assign a human-readable name to the expression, + which makes debugging and exception messages easier to understand - for instance, the default + name created for the C{Word} expression without calling C{setName} is C{"W:(ABCD...)"}. + """ + 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: + with open(file_or_filename, "r") as f: + file_contents = f.read() + 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 vars(self) == vars(other) + elif isinstance(other, basestring): + return self.matches(other) + 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 matches(self, testString, parseAll=True): + """ + Method for quick testing of a parser against a test string. Good for simple + inline microtests of sub expressions while building up larger parser. + + Parameters: + - testString - to test against this expression for a match + - parseAll - (default=C{True}) - flag to pass to C{L{parseString}} when running tests + + Example:: + expr = Word(nums) + assert expr.matches("100") + """ + try: + self.parseString(_ustr(testString), parseAll=parseAll) + return True + except ParseBaseException: + return False + + def runTests(self, tests, parseAll=True, comment='#', fullDump=True, printResults=True, failureTests=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=C{True}) - flag to pass to C{L{parseString}} when running tests + - comment - (default=C{'#'}) - expression for indicating embedded comments in the test + string; pass None to disable comment filtering + - fullDump - (default=C{True}) - dump results as list followed by results names in nested outline; + if False, only dump nested list + - printResults - (default=C{True}) prints test output to stdout + - failureTests - (default=C{False}) indicates if these tests are expected to fail parsing + + Returns: a (success, results) tuple, where success indicates that all tests succeeded + (or failed if C{failureTests} is True), and the results contain a list of lines of each + test's output + + Example:: + number_expr = pyparsing_common.number.copy() + + result = number_expr.runTests(''' + # unsigned integer + 100 + # negative integer + -100 + # float with scientific notation + 6.02e23 + # integer with scientific notation + 1e-12 + ''') + print("Success" if result[0] else "Failed!") + + result = number_expr.runTests(''' + # stray character + 100Z + # missing leading digit before '.' + -.100 + # too many '.' + 3.14.159 + ''', failureTests=True) + print("Success" if result[0] else "Failed!") + prints:: + # unsigned integer + 100 + [100] + + # negative integer + -100 + [-100] + + # float with scientific notation + 6.02e23 + [6.02e+23] + + # integer with scientific notation + 1e-12 + [1e-12] + + Success + + # stray character + 100Z + ^ + FAIL: Expected end of text (at char 3), (line:1, col:4) + + # missing leading digit before '.' + -.100 + ^ + FAIL: Expected {real number with scientific notation | real number | signed integer} (at char 0), (line:1, col:1) + + # too many '.' + 3.14.159 + ^ + FAIL: Expected end of text (at char 4), (line:1, col:5) + + Success + + Each test string must be on a single line. If you want to test a string that spans multiple + lines, create a test like this:: + + expr.runTest(r"this is a test\\n of strings that spans \\n 3 lines") + + (Note that this is a raw string literal, you must include the leading 'r'.) + """ + if isinstance(tests, basestring): + tests = list(map(str.strip, tests.rstrip().splitlines())) + if isinstance(comment, basestring): + comment = Literal(comment) + allResults = [] + comments = [] + success = True + for t in tests: + if comment is not None and comment.matches(t, False) or comments and not t: + comments.append(t) + continue + if not t: + continue + out = ['\n'.join(comments), t] + comments = [] + try: + t = t.replace(r'\n','\n') + result = self.parseString(t, parseAll=parseAll) + out.append(result.dump(full=fullDump)) + success = success and not failureTests + except ParseBaseException as pe: + fatal = "(FATAL)" if isinstance(pe, ParseFatalException) else "" + if '\n' in t: + out.append(line(pe.loc, t)) + out.append(' '*(col(pe.loc,t)-1) + '^' + fatal) + else: + out.append(' '*pe.loc + '^' + fatal) + out.append("FAIL: " + str(pe)) + success = success and failureTests + result = pe + except Exception as exc: + out.append("FAIL-EXCEPTION: " + str(exc)) + success = success and failureTests + result = exc + + if printResults: + if fullDump: + out.append('') + print('\n'.join(out)) + + allResults.append((t, result)) + + return success, allResults + + +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. + + Example:: + Literal('blah').parseString('blah') # -> ['blah'] + Literal('blah').parseString('blahfooblah') # -> ['blah'] + Literal('blah').parseString('bla') # -> Exception: Expected "blah" + + For case-insensitive matching, use L{CaselessLiteral}. + + For keyword matching (force word break before and after the matched string), + use L{Keyword} or L{CaselessKeyword}. + """ + 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}}: + - C{Literal("if")} will match the leading C{'if'} in C{'ifAndOnlyIf'}. + - C{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}. + + Example:: + Keyword("start").parseString("start") # -> ['start'] + Keyword("start").parseString("starting") # -> Exception + + For case-insensitive matching, use L{CaselessKeyword}. + """ + DEFAULT_KEYWORD_CHARS = alphanums+"_$" + + def __init__( self, matchString, identChars=None, caseless=False ): + super(Keyword,self).__init__() + if identChars is None: + identChars = Keyword.DEFAULT_KEYWORD_CHARS + 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. + + Example:: + OneOrMore(CaselessLiteral("CMD")).parseString("cmd CMD Cmd10") # -> ['CMD', 'CMD', 'CMD'] + + (Contrast with example for L{CaselessKeyword}.) + """ + 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): + """ + Caseless version of L{Keyword}. + + Example:: + OneOrMore(CaselessKeyword("CMD")).parseString("cmd CMD Cmd10") # -> ['CMD', 'CMD'] + + (Contrast with example for L{CaselessLiteral}.) + """ + def __init__( self, matchString, identChars=None ): + 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 CloseMatch(Token): + """ + A variation on L{Literal} which matches "close" matches, that is, + strings with at most 'n' mismatching characters. C{CloseMatch} takes parameters: + - C{match_string} - string to be matched + - C{maxMismatches} - (C{default=1}) maximum number of mismatches allowed to count as a match + + The results from a successful parse will contain the matched text from the input string and the following named results: + - C{mismatches} - a list of the positions within the match_string where mismatches were found + - C{original} - the original match_string used to compare against the input string + + If C{mismatches} is an empty list, then the match was an exact match. + + Example:: + patt = CloseMatch("ATCATCGAATGGA") + patt.parseString("ATCATCGAAXGGA") # -> (['ATCATCGAAXGGA'], {'mismatches': [[9]], 'original': ['ATCATCGAATGGA']}) + patt.parseString("ATCAXCGAAXGGA") # -> Exception: Expected 'ATCATCGAATGGA' (with up to 1 mismatches) (at char 0), (line:1, col:1) + + # exact match + patt.parseString("ATCATCGAATGGA") # -> (['ATCATCGAATGGA'], {'mismatches': [[]], 'original': ['ATCATCGAATGGA']}) + + # close match allowing up to 2 mismatches + patt = CloseMatch("ATCATCGAATGGA", maxMismatches=2) + patt.parseString("ATCAXCGAAXGGA") # -> (['ATCAXCGAAXGGA'], {'mismatches': [[4, 9]], 'original': ['ATCATCGAATGGA']}) + """ + def __init__(self, match_string, maxMismatches=1): + super(CloseMatch,self).__init__() + self.name = match_string + self.match_string = match_string + self.maxMismatches = maxMismatches + self.errmsg = "Expected %r (with up to %d mismatches)" % (self.match_string, self.maxMismatches) + self.mayIndexError = False + self.mayReturnEmpty = False + + def parseImpl( self, instring, loc, doActions=True ): + start = loc + instrlen = len(instring) + maxloc = start + len(self.match_string) + + if maxloc <= instrlen: + match_string = self.match_string + match_stringloc = 0 + mismatches = [] + maxMismatches = self.maxMismatches + + for match_stringloc,s_m in enumerate(zip(instring[loc:maxloc], self.match_string)): + src,mat = s_m + if src != mat: + mismatches.append(match_stringloc) + if len(mismatches) > maxMismatches: + break + else: + loc = match_stringloc + 1 + results = ParseResults([instring[start:loc]]) + results['original'] = self.match_string + results['mismatches'] = mismatches + return loc, results + + 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{excludeChars} 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. + + L{srange} is useful for defining custom character set strings for defining + C{Word} expressions, using range notation from regular expression character sets. + + A common mistake is to use C{Word} to match a specific literal string, as in + C{Word("Address")}. Remember that C{Word} uses the string argument to define + I{sets} of matchable characters. This expression would match "Add", "AAA", + "dAred", or any other word made up of the characters 'A', 'd', 'r', 'e', and 's'. + To match an exact literal string, use L{Literal} or L{Keyword}. + + pyparsing includes helper strings for building Words: + - L{alphas} + - L{nums} + - L{alphanums} + - L{hexnums} + - L{alphas8bit} (alphabetic characters in ASCII range 128-255 - accented, tilded, umlauted, etc.) + - L{punc8bit} (non-alphabetic characters in ASCII range 128-255 - currency, symbols, superscripts, diacriticals, etc.) + - L{printables} (any non-whitespace character) + + Example:: + # a word composed of digits + integer = Word(nums) # equivalent to Word("0123456789") or Word(srange("0-9")) + + # a word with a leading capital, and zero or more lowercase + capital_word = Word(alphas.upper(), alphas.lower()) + + # hostnames are alphanumeric, with leading alpha, and '-' + hostname = Word(alphas, alphanums+'-') + + # roman numeral (not a strict parser, accepts invalid mix of characters) + roman = Word("IVXLCDM") + + # any string of non-whitespace characters, except for ',' + csv_value = Word(printables, excludeChars=",") + """ + 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 Exception: + 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 Exception: + 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): + r""" + 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. + If the given regex contains named groups (defined using C{(?P<name>...)}), these will be preserved as + named parse results. + + Example:: + realnum = Regex(r"[+-]?\d+\.\d*") + date = Regex(r'(?P<year>\d{4})-(?P<month>\d\d?)-(?P<day>\d\d?)') + # ref: http://stackoverflow.com/questions/267399/how-do-you-match-only-valid-roman-numerals-with-a-regular-expression + roman = Regex(r"M{0,4}(CM|CD|D?C{0,3})(XC|XL|L?X{0,3})(IX|IV|V?I{0,3})") + """ + 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 not pattern: + 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 Exception: + pass + + if self.strRepr is None: + self.strRepr = "Re:(%s)" % repr(self.pattern) + + return self.strRepr + + +class QuotedString(Token): + r""" + Token for matching strings that are delimited by quoting characters. + + 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=C{None}) + - escQuote - special quote sequence to escape an embedded quote string (such as SQL's "" to escape an embedded ") (default=C{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) + - convertWhitespaceEscapes - convert escaped whitespace (C{'\t'}, C{'\n'}, etc.) to actual whitespace (default=C{True}) + + Example:: + qs = QuotedString('"') + print(qs.searchString('lsjdf "This is the quote" sldjf')) + complex_qs = QuotedString('{{', endQuoteChar='}}') + print(complex_qs.searchString('lsjdf {{This is the "quote"}} sldjf')) + sql_qs = QuotedString('"', escQuote='""') + print(sql_qs.searchString('lsjdf "This is the quote with ""embedded"" quotes" sldjf')) + prints:: + [['This is the quote']] + [['This is the "quote"']] + [['This is the quote with "embedded" quotes']] + """ + def __init__( self, quoteChar, escChar=None, escQuote=None, multiline=False, unquoteResults=True, endQuoteChar=None, convertWhitespaceEscapes=True): + super(QuotedString,self).__init__() + + # remove white space from quote chars - wont work anyway + quoteChar = quoteChar.strip() + if not quoteChar: + warnings.warn("quoteChar cannot be the empty string",SyntaxWarning,stacklevel=2) + raise SyntaxError() + + if endQuoteChar is None: + endQuoteChar = quoteChar + else: + endQuoteChar = endQuoteChar.strip() + if not endQuoteChar: + 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 + self.convertWhitespaceEscapes = convertWhitespaceEscapes + + 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 whitespace + if '\\' in ret and self.convertWhitespaceEscapes: + ws_map = { + r'\t' : '\t', + r'\n' : '\n', + r'\f' : '\f', + r'\r' : '\r', + } + for wslit,wschar in ws_map.items(): + ret = ret.replace(wslit, wschar) + + # replace escaped characters + if self.escChar: + ret = re.sub(self.escCharReplacePattern, r"\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 Exception: + 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 I{not} in a given set (will + include whitespace in matched characters if not listed in the provided exclusion set - see example). + 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. + + Example:: + # define a comma-separated-value as anything that is not a ',' + csv_value = CharsNotIn(',') + print(delimitedList(csv_value).parseString("dkls,lsdkjf,s12 34,@!#,213")) + prints:: + ['dkls', 'lsdkjf', 's12 34', '@!#', '213'] + """ + 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 Exception: + 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 + + Example:: + + test = '''\ + AAA this line + AAA and this line + AAA but not this one + B AAA and definitely not this one + ''' + + for t in (LineStart() + 'AAA' + restOfLine).searchString(test): + print(t) + + Prints:: + ['AAA', ' this line'] + ['AAA', ' and this line'] + + """ + def __init__( self ): + super(LineStart,self).__init__() + self.errmsg = "Expected start of line" + + def parseImpl( self, instring, loc, doActions=True ): + if col(loc, instring) == 1: + return loc, [] + raise ParseException(instring, loc, self.errmsg, self) + +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 = [ ParserElement._literalStringClass( exprs ) ] + elif isinstance( exprs, Iterable ): + exprs = list(exprs) + # if sequence of strings provided, wrap with Literal + if all(isinstance(expr, basestring) for expr in exprs): + exprs = map(ParserElement._literalStringClass, 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 Exception: + 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 " + _ustr(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. + May also be constructed using the C{'-'} operator, which will suppress backtracking. + + Example:: + integer = Word(nums) + name_expr = OneOrMore(Word(alphas)) + + expr = And([integer("id"),name_expr("name"),integer("age")]) + # more easily written as: + expr = integer("id") + name_expr("name") + integer("age") + """ + + 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._from_exception(pe) + except IndexError: + raise ParseSyntaxException(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 = ParserElement._literalStringClass( 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. + + Example:: + # construct Or using '^' operator + + number = Word(nums) ^ Combine(Word(nums) + '.' + Word(nums)) + print(number.searchString("123 3.1416 789")) + prints:: + [['123'], ['3.1416'], ['789']] + """ + 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. + + Example:: + # construct MatchFirst using '|' operator + + # watch the order of expressions to match + number = Word(nums) | Combine(Word(nums) + '.' + Word(nums)) + print(number.searchString("123 3.1416 789")) # Fail! -> [['123'], ['3'], ['1416'], ['789']] + + # put more selective expression first + number = Combine(Word(nums) + '.' + Word(nums)) | Word(nums) + print(number.searchString("123 3.1416 789")) # Better -> [['123'], ['3.1416'], ['789']] + """ + 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. + + Example:: + color = oneOf("RED ORANGE YELLOW GREEN BLUE PURPLE BLACK WHITE BROWN") + shape_type = oneOf("SQUARE CIRCLE TRIANGLE STAR HEXAGON OCTAGON") + integer = Word(nums) + shape_attr = "shape:" + shape_type("shape") + posn_attr = "posn:" + Group(integer("x") + ',' + integer("y"))("posn") + color_attr = "color:" + color("color") + size_attr = "size:" + integer("size") + + # use Each (using operator '&') to accept attributes in any order + # (shape and posn are required, color and size are optional) + shape_spec = shape_attr & posn_attr & Optional(color_attr) & Optional(size_attr) + + shape_spec.runTests(''' + shape: SQUARE color: BLACK posn: 100, 120 + shape: CIRCLE size: 50 color: BLUE posn: 50,80 + color:GREEN size:20 shape:TRIANGLE posn:20,40 + ''' + ) + prints:: + shape: SQUARE color: BLACK posn: 100, 120 + ['shape:', 'SQUARE', 'color:', 'BLACK', 'posn:', ['100', ',', '120']] + - color: BLACK + - posn: ['100', ',', '120'] + - x: 100 + - y: 120 + - shape: SQUARE + + + shape: CIRCLE size: 50 color: BLUE posn: 50,80 + ['shape:', 'CIRCLE', 'size:', '50', 'color:', 'BLUE', 'posn:', ['50', ',', '80']] + - color: BLUE + - posn: ['50', ',', '80'] + - x: 50 + - y: 80 + - shape: CIRCLE + - size: 50 + + + color: GREEN size: 20 shape: TRIANGLE posn: 20,40 + ['color:', 'GREEN', 'size:', '20', 'shape:', 'TRIANGLE', 'posn:', ['20', ',', '40']] + - color: GREEN + - posn: ['20', ',', '40'] + - x: 20 + - y: 40 + - shape: TRIANGLE + - size: 20 + """ + 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 = sum(resultlist, ParseResults([])) + 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 ): + if issubclass(ParserElement._literalStringClass, Token): + expr = ParserElement._literalStringClass(expr) + else: + expr = ParserElement._literalStringClass(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 Exception: + 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 I{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. + + Example:: + # use FollowedBy to match a label only if it is followed by a ':' + data_word = Word(alphas) + label = data_word + FollowedBy(':') + attr_expr = Group(label + Suppress(':') + OneOrMore(data_word, stopOn=label).setParseAction(' '.join)) + + OneOrMore(attr_expr).parseString("shape: SQUARE color: BLACK posn: upper left").pprint() + prints:: + [['shape', 'SQUARE'], ['color', 'BLACK'], ['posn', 'upper left']] + """ + 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 I{not} advance the parsing position within the input string, it only + verifies that the specified parse expression does I{not} match at the current + position. Also, C{NotAny} does I{not} skip over leading whitespace. C{NotAny} + always returns a null token list. May be constructed using the '~' operator. + + Example:: + + """ + 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 ): + if self.expr.canParseNext(instring, loc): + 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 _MultipleMatch(ParseElementEnhance): + def __init__( self, expr, stopOn=None): + super(_MultipleMatch, self).__init__(expr) + self.saveAsList = True + ender = stopOn + if isinstance(ender, basestring): + ender = ParserElement._literalStringClass(ender) + self.not_ender = ~ender if ender is not None else None + + def parseImpl( self, instring, loc, doActions=True ): + self_expr_parse = self.expr._parse + self_skip_ignorables = self._skipIgnorables + check_ender = self.not_ender is not None + if check_ender: + try_not_ender = self.not_ender.tryParse + + # must be at least one (but first see if we are the stopOn sentinel; + # if so, fail) + if check_ender: + try_not_ender(instring, loc) + loc, tokens = self_expr_parse( instring, loc, doActions, callPreParse=False ) + try: + hasIgnoreExprs = (not not self.ignoreExprs) + while 1: + if check_ender: + try_not_ender(instring, loc) + if hasIgnoreExprs: + preloc = self_skip_ignorables( 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 + +class OneOrMore(_MultipleMatch): + """ + Repetition of one or more of the given expression. + + Parameters: + - expr - expression that must match one or more times + - stopOn - (default=C{None}) - expression for a terminating sentinel + (only required if the sentinel would ordinarily match the repetition + expression) + + Example:: + data_word = Word(alphas) + label = data_word + FollowedBy(':') + attr_expr = Group(label + Suppress(':') + OneOrMore(data_word).setParseAction(' '.join)) + + text = "shape: SQUARE posn: upper left color: BLACK" + OneOrMore(attr_expr).parseString(text).pprint() # Fail! read 'color' as data instead of next label -> [['shape', 'SQUARE color']] + + # use stopOn attribute for OneOrMore to avoid reading label string as part of the data + attr_expr = Group(label + Suppress(':') + OneOrMore(data_word, stopOn=label).setParseAction(' '.join)) + OneOrMore(attr_expr).parseString(text).pprint() # Better -> [['shape', 'SQUARE'], ['posn', 'upper left'], ['color', 'BLACK']] + + # could also be written as + (attr_expr * (1,)).parseString(text).pprint() + """ + + 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(_MultipleMatch): + """ + Optional repetition of zero or more of the given expression. + + Parameters: + - expr - expression that must match zero or more times + - stopOn - (default=C{None}) - expression for a terminating sentinel + (only required if the sentinel would ordinarily match the repetition + expression) + + Example: similar to L{OneOrMore} + """ + def __init__( self, expr, stopOn=None): + super(ZeroOrMore,self).__init__(expr, stopOn=stopOn) + self.mayReturnEmpty = True + + def parseImpl( self, instring, loc, doActions=True ): + try: + return super(ZeroOrMore, self).parseImpl(instring, loc, doActions) + except (ParseException,IndexError): + 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 _NullToken(object): + def __bool__(self): + return False + __nonzero__ = __bool__ + def __str__(self): + return "" + +_optionalNotMatched = _NullToken() +class Optional(ParseElementEnhance): + """ + Optional matching of the given expression. + + Parameters: + - expr - expression that must match zero or more times + - default (optional) - value to be returned if the optional expression is not found. + + Example:: + # US postal code can be a 5-digit zip, plus optional 4-digit qualifier + zip = Combine(Word(nums, exact=5) + Optional('-' + Word(nums, exact=4))) + zip.runTests(''' + # traditional ZIP code + 12345 + + # ZIP+4 form + 12101-0001 + + # invalid ZIP + 98765- + ''') + prints:: + # traditional ZIP code + 12345 + ['12345'] + + # ZIP+4 form + 12101-0001 + ['12101-0001'] + + # invalid ZIP + 98765- + ^ + FAIL: Expected end of text (at char 5), (line:1, col:6) + """ + def __init__( self, expr, default=_optionalNotMatched ): + super(Optional,self).__init__( expr, savelist=False ) + self.saveAsList = self.expr.saveAsList + 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. + + Parameters: + - expr - target expression marking the end of the data to be skipped + - include - (default=C{False}) if True, the target expression is also parsed + (the skipped text and target expression are returned as a 2-element list). + - ignore - (default=C{None}) used to define grammars (typically quoted strings and + comments) that might contain false matches to the target expression + - failOn - (default=C{None}) define expressions that are not allowed to be + included in the skipped test; if found before the target expression is found, + the SkipTo is not a match + + Example:: + report = ''' + Outstanding Issues Report - 1 Jan 2000 + + # | Severity | Description | Days Open + -----+----------+-------------------------------------------+----------- + 101 | Critical | Intermittent system crash | 6 + 94 | Cosmetic | Spelling error on Login ('log|n') | 14 + 79 | Minor | System slow when running too many reports | 47 + ''' + integer = Word(nums) + SEP = Suppress('|') + # use SkipTo to simply match everything up until the next SEP + # - ignore quoted strings, so that a '|' character inside a quoted string does not match + # - parse action will call token.strip() for each matched token, i.e., the description body + string_data = SkipTo(SEP, ignore=quotedString) + string_data.setParseAction(tokenMap(str.strip)) + ticket_expr = (integer("issue_num") + SEP + + string_data("sev") + SEP + + string_data("desc") + SEP + + integer("days_open")) + + for tkt in ticket_expr.searchString(report): + print tkt.dump() + prints:: + ['101', 'Critical', 'Intermittent system crash', '6'] + - days_open: 6 + - desc: Intermittent system crash + - issue_num: 101 + - sev: Critical + ['94', 'Cosmetic', "Spelling error on Login ('log|n')", '14'] + - days_open: 14 + - desc: Spelling error on Login ('log|n') + - issue_num: 94 + - sev: Cosmetic + ['79', 'Minor', 'System slow when running too many reports', '47'] + - days_open: 47 + - desc: System slow when running too many reports + - issue_num: 79 + - sev: Minor + """ + 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 isinstance(failOn, basestring): + self.failOn = ParserElement._literalStringClass(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 + expr_parse = self.expr._parse + self_failOn_canParseNext = self.failOn.canParseNext if self.failOn is not None else None + self_ignoreExpr_tryParse = self.ignoreExpr.tryParse if self.ignoreExpr is not None else None + + tmploc = loc + while tmploc <= instrlen: + if self_failOn_canParseNext is not None: + # break if failOn expression matches + if self_failOn_canParseNext(instring, tmploc): + break + + if self_ignoreExpr_tryParse is not None: + # advance past ignore expressions + while 1: + try: + tmploc = self_ignoreExpr_tryParse(instring, tmploc) + except ParseBaseException: + break + + try: + expr_parse(instring, tmploc, doActions=False, callPreParse=False) + except (ParseException, IndexError): + # no match, advance loc in string + tmploc += 1 + else: + # matched skipto expr, done + break + + else: + # ran off the end of the input string without matching skipto expr, fail + raise ParseException(instring, loc, self.errmsg, self) + + # build up return values + loc = tmploc + skiptext = instring[startloc:loc] + skipresult = ParseResults(skiptext) + + if self.includeMatch: + loc, mat = expr_parse(instring,loc,doActions,callPreParse=False) + skipresult += mat + + return loc, skipresult + +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. + + See L{ParseResults.pprint} for an example of a recursive parser created using + C{Forward}. + """ + 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.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 + return self.__class__.__name__ + ": ..." + + # stubbed out for now - creates awful memory and perf issues + 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 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. + + Example:: + real = Word(nums) + '.' + Word(nums) + print(real.parseString('3.1416')) # -> ['3', '.', '1416'] + # will also erroneously match the following + print(real.parseString('3. 1416')) # -> ['3', '.', '1416'] + + real = Combine(Word(nums) + '.' + Word(nums)) + print(real.parseString('3.1416')) # -> ['3.1416'] + # no match when there are internal spaces + print(real.parseString('3. 1416')) # -> Exception: Expected W:(0123...) + """ + 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. + + Example:: + ident = Word(alphas) + num = Word(nums) + term = ident | num + func = ident + Optional(delimitedList(term)) + print(func.parseString("fn a,b,100")) # -> ['fn', 'a', 'b', '100'] + + func = ident + Group(Optional(delimitedList(term))) + print(func.parseString("fn a,b,100")) # -> ['fn', ['a', 'b', '100']] + """ + 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. + + Example:: + data_word = Word(alphas) + label = data_word + FollowedBy(':') + attr_expr = Group(label + Suppress(':') + OneOrMore(data_word).setParseAction(' '.join)) + + text = "shape: SQUARE posn: upper left color: light blue texture: burlap" + attr_expr = (label + Suppress(':') + OneOrMore(data_word, stopOn=label).setParseAction(' '.join)) + + # print attributes as plain groups + print(OneOrMore(attr_expr).parseString(text).dump()) + + # instead of OneOrMore(expr), parse using Dict(OneOrMore(Group(expr))) - Dict will auto-assign names + result = Dict(OneOrMore(Group(attr_expr))).parseString(text) + print(result.dump()) + + # access named fields as dict entries, or output as dict + print(result['shape']) + print(result.asDict()) + prints:: + ['shape', 'SQUARE', 'posn', 'upper left', 'color', 'light blue', 'texture', 'burlap'] + + [['shape', 'SQUARE'], ['posn', 'upper left'], ['color', 'light blue'], ['texture', 'burlap']] + - color: light blue + - posn: upper left + - shape: SQUARE + - texture: burlap + SQUARE + {'color': 'light blue', 'posn': 'upper left', 'texture': 'burlap', 'shape': 'SQUARE'} + See more examples at L{ParseResults} of accessing fields by results name. + """ + 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. + + Example:: + source = "a, b, c,d" + wd = Word(alphas) + wd_list1 = wd + ZeroOrMore(',' + wd) + print(wd_list1.parseString(source)) + + # often, delimiters that are useful during parsing are just in the + # way afterward - use Suppress to keep them out of the parsed output + wd_list2 = wd + ZeroOrMore(Suppress(',') + wd) + print(wd_list2.parseString(source)) + prints:: + ['a', ',', 'b', ',', 'c', ',', 'd'] + ['a', 'b', 'c', 'd'] + (See also L{delimitedList}.) + """ + 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. + + When the parse action is called, this decorator will print C{">> entering I{method-name}(line:I{current_source_line}, I{parse_location}, I{matched_tokens})".} + When the parse action completes, the decorator will print C{"<<"} followed by the returned value, or any exception that the parse action raised. + + Example:: + wd = Word(alphas) + + @traceParseAction + def remove_duplicate_chars(tokens): + return ''.join(sorted(set(''.join(tokens)))) + + wds = OneOrMore(wd).setParseAction(remove_duplicate_chars) + print(wds.parseString("slkdjs sld sldd sdlf sdljf")) + prints:: + >>entering remove_duplicate_chars(line: 'slkdjs sld sldd sdlf sdljf', 0, (['slkdjs', 'sld', 'sldd', 'sdlf', 'sdljf'], {})) + <<leaving remove_duplicate_chars (ret: 'dfjkls') + ['dfjkls'] + """ + f = _trim_arity(f) + def z(*paArgs): + thisFunc = f.__name__ + s,l,t = paArgs[-3:] + if len(paArgs)>3: + thisFunc = paArgs[0].__class__.__name__ + '.' + thisFunc + sys.stderr.write( ">>entering %s(line: '%s', %d, %r)\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: %r)\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. + + Example:: + delimitedList(Word(alphas)).parseString("aa,bb,cc") # -> ['aa', 'bb', 'cc'] + delimitedList(Word(hexnums), delim=':', combine=True).parseString("AA:BB:CC:DD:EE") # -> ['AA:BB:CC:DD:EE'] + """ + 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. + + If C{intExpr} is specified, it should be a pyparsing expression that produces an integer value. + + Example:: + countedArray(Word(alphas)).parseString('2 ab cd ef') # -> ['ab', 'cd'] + + # in this parser, the leading integer value is given in binary, + # '10' indicating that 2 values are in the array + binaryConstant = Word('01').setParseAction(lambda t: int(t[0], 2)) + countedArray(Word(alphas), intExpr=binaryConstant).parseString('10 ab cd ef') # -> ['ab', 'cd'] + """ + 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 ).setName('(len) ' + _ustr(expr) + '...') + +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 I{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) + rep.setName('(prev) ' + _ustr(expr)) + 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 I{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 I{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) + rep.setName('(prev) ' + _ustr(expr)) + 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 collection of string literals + - caseless - (default=C{False}) - treat all literals as caseless + - useRegex - (default=C{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) + + Example:: + comp_oper = oneOf("< = > <= >= !=") + var = Word(alphas) + number = Word(nums) + term = var | number + comparison_expr = term + comp_oper + term + print(comparison_expr.searchString("B = 12 AA=23 B<=AA AA>12")) + prints:: + [['B', '=', '12'], ['AA', '=', '23'], ['B', '<=', 'AA'], ['AA', '>', '12']] + """ + 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, Iterable): + symbols = list(strs) + else: + warnings.warn("Invalid argument to oneOf, expected string or iterable", + 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) ).setName(' | '.join(symbols)) + else: + return Regex( "|".join(re.escape(sym) for sym in symbols) ).setName(' | '.join(symbols)) + except Exception: + 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).setName(' | '.join(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. + + Example:: + text = "shape: SQUARE posn: upper left color: light blue texture: burlap" + attr_expr = (label + Suppress(':') + OneOrMore(data_word, stopOn=label).setParseAction(' '.join)) + print(OneOrMore(attr_expr).parseString(text).dump()) + + attr_label = label + attr_value = Suppress(':') + OneOrMore(data_word, stopOn=label).setParseAction(' '.join) + + # similar to Dict, but simpler call format + result = dictOf(attr_label, attr_value).parseString(text) + print(result.dump()) + print(result['shape']) + print(result.shape) # object attribute access works too + print(result.asDict()) + prints:: + [['shape', 'SQUARE'], ['posn', 'upper left'], ['color', 'light blue'], ['texture', 'burlap']] + - color: light blue + - posn: upper left + - shape: SQUARE + - texture: burlap + SQUARE + SQUARE + {'color': 'light blue', 'shape': 'SQUARE', 'posn': 'upper left', 'texture': 'burlap'} + """ + 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. By default, returns astring 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. + + Example:: + src = "this is test <b> bold <i>text</i> </b> normal text " + for tag in ("b","i"): + opener,closer = makeHTMLTags(tag) + patt = originalTextFor(opener + SkipTo(closer) + closer) + print(patt.searchString(src)[0]) + prints:: + ['<b> bold <i>text</i> </b>'] + ['<i>text</i>'] + """ + 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): + t[:] = [s[t.pop('_original_start'):t.pop('_original_end')]] + matchExpr.setParseAction(extractText) + matchExpr.ignoreExprs = expr.ignoreExprs + 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}} + + Example:: + wd = Word(alphas) + for match in locatedExpr(wd).searchString("ljsdf123lksdjjf123lkkjj1222"): + print(match) + prints:: + [[0, 'ljsdf', 5]] + [[8, 'lksdjjf', 15]] + [[18, 'lkkjj', 23]] + """ + 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 | CharsNotIn(r'\]', exact=1) +_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 C{\-} or C{\]}) + - an escaped hex character with a leading C{'\x'} (C{\x21}, which is a C{'!'} character) + (C{\0x##} is also supported for backwards compatibility) + - an escaped octal character with a leading C{'\0'} (C{\041}, which is a C{'!'} character) + - a range of any of the above, separated by a dash (C{'a-z'}, etc.) + - any combination of the above (C{'aeiouy'}, C{'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 Exception: + 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>}()}. + + Example:: + num = Word(nums).setParseAction(lambda toks: int(toks[0])) + na = oneOf("N/A NA").setParseAction(replaceWith(math.nan)) + term = na | num + + OneOrMore(term).parseString("324 234 N/A 234") # -> [324, 234, nan, 234] + """ + return lambda s,l,t: [replStr] + +def removeQuotes(s,l,t): + """ + Helper parse action for removing quotation marks from parsed quoted strings. + + Example:: + # by default, quotation marks are included in parsed results + quotedString.parseString("'Now is the Winter of our Discontent'") # -> ["'Now is the Winter of our Discontent'"] + + # use removeQuotes to strip quotation marks from parsed results + quotedString.setParseAction(removeQuotes) + quotedString.parseString("'Now is the Winter of our Discontent'") # -> ["Now is the Winter of our Discontent"] + """ + return t[0][1:-1] + +def tokenMap(func, *args): + """ + Helper to define a parse action by mapping a function to all elements of a ParseResults list.If any additional + args are passed, they are forwarded to the given function as additional arguments after + the token, as in C{hex_integer = Word(hexnums).setParseAction(tokenMap(int, 16))}, which will convert the + parsed data to an integer using base 16. + + Example (compare the last to example in L{ParserElement.transformString}:: + hex_ints = OneOrMore(Word(hexnums)).setParseAction(tokenMap(int, 16)) + hex_ints.runTests(''' + 00 11 22 aa FF 0a 0d 1a + ''') + + upperword = Word(alphas).setParseAction(tokenMap(str.upper)) + OneOrMore(upperword).runTests(''' + my kingdom for a horse + ''') + + wd = Word(alphas).setParseAction(tokenMap(str.title)) + OneOrMore(wd).setParseAction(' '.join).runTests(''' + now is the winter of our discontent made glorious summer by this sun of york + ''') + prints:: + 00 11 22 aa FF 0a 0d 1a + [0, 17, 34, 170, 255, 10, 13, 26] + + my kingdom for a horse + ['MY', 'KINGDOM', 'FOR', 'A', 'HORSE'] + + now is the winter of our discontent made glorious summer by this sun of york + ['Now Is The Winter Of Our Discontent Made Glorious Summer By This Sun Of York'] + """ + def pa(s,l,t): + return [func(tokn, *args) for tokn in t] + + try: + func_name = getattr(func, '__name__', + getattr(func, '__class__').__name__) + except Exception: + func_name = str(func) + pa.__name__ = func_name + + return pa + +upcaseTokens = tokenMap(lambda t: _ustr(t).upper()) +"""(Deprecated) Helper parse action to convert tokens to upper case. Deprecated in favor of L{pyparsing_common.upcaseTokens}""" + +downcaseTokens = tokenMap(lambda t: _ustr(t).lower()) +"""(Deprecated) Helper parse action to convert tokens to lower case. Deprecated in favor of L{pyparsing_common.downcaseTokens}""" + +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>" % resname) + closeTag = closeTag.setResultsName("end"+"".join(resname.replace(":"," ").title().split())).setName("</%s>" % resname) + 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. Matches + tags in either upper or lower case, attributes with namespaces and with quoted or unquoted values. + + Example:: + text = '<td>More info at the <a href="http://pyparsing.wikispaces.com">pyparsing</a> wiki page</td>' + # makeHTMLTags returns pyparsing expressions for the opening and closing tags as a 2-tuple + a,a_end = makeHTMLTags("A") + link_expr = a + SkipTo(a_end)("link_text") + a_end + + for link in link_expr.searchString(text): + # attributes in the <A> tag (like "href" shown here) are also accessible as named results + print(link.link_text, '->', link.href) + prints:: + pyparsing -> http://pyparsing.wikispaces.com + """ + return _makeTags( tagStr, False ) + +def makeXMLTags(tagStr): + """ + Helper to construct opening and closing tag expressions for XML, given a tag name. Matches + tags only in the given upper/lower case. + + Example: similar to L{makeHTMLTags} + """ + 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. + + Example:: + html = ''' + <div> + Some text + <div type="grid">1 4 0 1 0</div> + <div type="graph">1,3 2,3 1,1</div> + <div>this has no type</div> + </div> + + ''' + div,div_end = makeHTMLTags("div") + + # only match div tag having a type attribute with value "grid" + div_grid = div().setParseAction(withAttribute(type="grid")) + grid_expr = div_grid + SkipTo(div | div_end)("body") + for grid_header in grid_expr.searchString(html): + print(grid_header.body) + + # construct a match with any div tag having a type attribute, regardless of the value + div_any_type = div().setParseAction(withAttribute(type=withAttribute.ANY_VALUE)) + div_expr = div_any_type + SkipTo(div | div_end)("body") + for div_header in div_expr.searchString(html): + print(div_header.body) + prints:: + 1 4 0 1 0 + + 1 4 0 1 0 + 1,3 2,3 1,1 + """ + 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. + + Example:: + html = ''' + <div> + Some text + <div class="grid">1 4 0 1 0</div> + <div class="graph">1,3 2,3 1,1</div> + <div>this <div> has no class</div> + </div> + + ''' + div,div_end = makeHTMLTags("div") + div_grid = div().setParseAction(withClass("grid")) + + grid_expr = div_grid + SkipTo(div | div_end)("body") + for grid_header in grid_expr.searchString(html): + print(grid_header.body) + + div_any_type = div().setParseAction(withClass(withAttribute.ANY_VALUE)) + div_expr = div_any_type + SkipTo(div | div_end)("body") + for div_header in div_expr.searchString(html): + print(div_header.body) + prints:: + 1 4 0 1 0 + + 1 4 0 1 0 + 1,3 2,3 1,1 + """ + 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. The generated parser will also recognize the use + of parentheses to override operator precedences (see example below). + + Note: if you define a deep operator list, you may see performance issues + when using infixNotation. See L{ParserElement.enablePackrat} for a + mechanism to potentially improve your parser performance. + + 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); if the parse action + is passed a tuple or list of functions, this is equivalent to + calling C{setParseAction(*fn)} (L{ParserElement.setParseAction}) + - lpar - expression for matching left-parentheses (default=C{Suppress('(')}) + - rpar - expression for matching right-parentheses (default=C{Suppress(')')}) + + Example:: + # simple example of four-function arithmetic with ints and variable names + integer = pyparsing_common.signed_integer + varname = pyparsing_common.identifier + + arith_expr = infixNotation(integer | varname, + [ + ('-', 1, opAssoc.RIGHT), + (oneOf('* /'), 2, opAssoc.LEFT), + (oneOf('+ -'), 2, opAssoc.LEFT), + ]) + + arith_expr.runTests(''' + 5+3*6 + (5+3)*6 + -2--11 + ''', fullDump=False) + prints:: + 5+3*6 + [[5, '+', [3, '*', 6]]] + + (5+3)*6 + [[[5, '+', 3], '*', 6]] + + -2--11 + [[['-', 2], '-', ['-', 11]]] + """ + ret = Forward() + lastExpr = baseExpr | ( lpar + ret + rpar ) + for i,operDef in enumerate(opList): + opExpr,arity,rightLeftAssoc,pa = (operDef + (None,))[:4] + termName = "%s term" % opExpr if arity < 3 else "%s%s term" % opExpr + 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(termName) + 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: + if isinstance(pa, (tuple, list)): + matchExpr.setParseAction(*pa) + else: + matchExpr.setParseAction(pa) + thisExpr <<= ( matchExpr.setName(termName) | lastExpr ) + lastExpr = thisExpr + ret <<= lastExpr + return ret + +operatorPrecedence = infixNotation +"""(Deprecated) Former name of C{L{infixNotation}}, will be dropped in a future release.""" + +dblQuotedString = Combine(Regex(r'"(?:[^"\n\r\\]|(?:"")|(?:\\(?:[^x]|x[0-9a-fA-F]+)))*')+'"').setName("string enclosed in double quotes") +sglQuotedString = Combine(Regex(r"'(?:[^'\n\r\\]|(?:'')|(?:\\(?:[^x]|x[0-9a-fA-F]+)))*")+"'").setName("string enclosed in single quotes") +quotedString = Combine(Regex(r'"(?:[^"\n\r\\]|(?:"")|(?:\\(?:[^x]|x[0-9a-fA-F]+)))*')+'"'| + Regex(r"'(?:[^'\n\r\\]|(?:'')|(?:\\(?:[^x]|x[0-9a-fA-F]+)))*")+"'").setName("quotedString using single or double quotes") +unicodeString = Combine(_L('u') + quotedString.copy()).setName("unicode string literal") + +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=C{"("}); can also be a pyparsing expression + - closer - closing character for a nested list (default=C{")"}); can also be a pyparsing expression + - content - expression for items within the nested lists (default=C{None}) + - ignoreExpr - expression for ignoring opening and closing delimiters (default=C{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. + + Example:: + data_type = oneOf("void int short long char float double") + decl_data_type = Combine(data_type + Optional(Word('*'))) + ident = Word(alphas+'_', alphanums+'_') + number = pyparsing_common.number + arg = Group(decl_data_type + ident) + LPAR,RPAR = map(Suppress, "()") + + code_body = nestedExpr('{', '}', ignoreExpr=(quotedString | cStyleComment)) + + c_function = (decl_data_type("type") + + ident("name") + + LPAR + Optional(delimitedList(arg), [])("args") + RPAR + + code_body("body")) + c_function.ignore(cStyleComment) + + source_code = ''' + int is_odd(int x) { + return (x%2); + } + + int dec_to_hex(char hchar) { + if (hchar >= '0' && hchar <= '9') { + return (ord(hchar)-ord('0')); + } else { + return (10+ord(hchar)-ord('A')); + } + } + ''' + for func in c_function.searchString(source_code): + print("%(name)s (%(type)s) args: %(args)s" % func) + + prints:: + is_odd (int) args: [['int', 'x']] + dec_to_hex (int) args: [['char', 'hchar']] + """ + 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) ) + ret.setName('nested %s%s expression' % (opener,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=C{True}) + + A valid block must contain at least one C{blockStatement}. + + Example:: + data = ''' + def A(z): + A1 + B = 100 + G = A2 + A2 + A3 + B + def BB(a,b,c): + BB1 + def BBA(): + bba1 + bba2 + bba3 + C + D + def spam(x,y): + def eggs(z): + pass + ''' + + + indentStack = [1] + stmt = Forward() + + identifier = Word(alphas, alphanums) + funcDecl = ("def" + identifier + Group( "(" + Optional( delimitedList(identifier) ) + ")" ) + ":") + func_body = indentedBlock(stmt, indentStack) + funcDef = Group( funcDecl + func_body ) + + rvalue = Forward() + funcCall = Group(identifier + "(" + Optional(delimitedList(rvalue)) + ")") + rvalue << (funcCall | identifier | Word(nums)) + assignment = Group(identifier + "=" + rvalue) + stmt << ( funcDef | assignment | identifier ) + + module_body = OneOrMore(stmt) + + parseTree = module_body.parseString(data) + parseTree.pprint() + prints:: + [['def', + 'A', + ['(', 'z', ')'], + ':', + [['A1'], [['B', '=', '100']], [['G', '=', 'A2']], ['A2'], ['A3']]], + 'B', + ['def', + 'BB', + ['(', 'a', 'b', 'c', ')'], + ':', + [['BB1'], [['def', 'BBA', ['(', ')'], ':', [['bba1'], ['bba2'], ['bba3']]]]]], + 'C', + 'D', + ['def', + 'spam', + ['(', 'x', 'y', ')'], + ':', + [[['def', 'eggs', ['(', 'z', ')'], ':', [['pass']]]]]]] + """ + 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)).setName('INDENT') + PEER = Empty().setParseAction(checkPeerIndent).setName('') + UNDENT = Empty().setParseAction(checkUnindent).setName('UNINDENT') + 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.setName('indented block') + +alphas8bit = srange(r"[\0xc0-\0xd6\0xd8-\0xf6\0xf8-\0xff]") +punc8bit = srange(r"[\0xa1-\0xbf\0xd7\0xf7]") + +anyOpenTag,anyCloseTag = makeHTMLTags(Word(alphas,alphanums+"_:").setName('any tag')) +_htmlEntityMap = dict(zip("gt lt amp nbsp quot apos".split(),'><& "\'')) +commonHTMLEntity = Regex('&(?P<entity>' + '|'.join(_htmlEntityMap.keys()) +");").setName("common HTML entity") +def replaceHTMLEntity(t): + """Helper parser action to replace common HTML entities with their special characters""" + return _htmlEntityMap.get(t.entity) + +# it's easy to get these comment structures wrong - they're very common, so may as well make them available +cStyleComment = Combine(Regex(r"/\*(?:[^*]|\*(?!/))*") + '*/').setName("C style comment") +"Comment of the form C{/* ... */}" + +htmlComment = Regex(r"<!--[\s\S]*?-->").setName("HTML comment") +"Comment of the form C{<!-- ... -->}" + +restOfLine = Regex(r".*").leaveWhitespace().setName("rest of line") +dblSlashComment = Regex(r"//(?:\\\n|[^\n])*").setName("// comment") +"Comment of the form C{// ... (to end of line)}" + +cppStyleComment = Combine(Regex(r"/\*(?:[^*]|\*(?!/))*") + '*/'| dblSlashComment).setName("C++ style comment") +"Comment of either form C{L{cStyleComment}} or C{L{dblSlashComment}}" + +javaStyleComment = cppStyleComment +"Same as C{L{cppStyleComment}}" + +pythonStyleComment = Regex(r"#.*").setName("Python style comment") +"Comment of the form C{# ... (to end of line)}" + +_commasepitem = Combine(OneOrMore(Word(printables, excludeChars=',') + + Optional( Word(" \t") + + ~Literal(",") + ~LineEnd() ) ) ).streamline().setName("commaItem") +commaSeparatedList = delimitedList( Optional( quotedString.copy() | _commasepitem, default="") ).setName("commaSeparatedList") +"""(Deprecated) Predefined expression of 1 or more printable words or quoted strings, separated by commas. + This expression is deprecated in favor of L{pyparsing_common.comma_separated_list}.""" + +# some other useful expressions - using lower-case class name since we are really using this as a namespace +class pyparsing_common: + """ + Here are some common low-level expressions that may be useful in jump-starting parser development: + - numeric forms (L{integers<integer>}, L{reals<real>}, L{scientific notation<sci_real>}) + - common L{programming identifiers<identifier>} + - network addresses (L{MAC<mac_address>}, L{IPv4<ipv4_address>}, L{IPv6<ipv6_address>}) + - ISO8601 L{dates<iso8601_date>} and L{datetime<iso8601_datetime>} + - L{UUID<uuid>} + - L{comma-separated list<comma_separated_list>} + Parse actions: + - C{L{convertToInteger}} + - C{L{convertToFloat}} + - C{L{convertToDate}} + - C{L{convertToDatetime}} + - C{L{stripHTMLTags}} + - C{L{upcaseTokens}} + - C{L{downcaseTokens}} + + Example:: + pyparsing_common.number.runTests(''' + # any int or real number, returned as the appropriate type + 100 + -100 + +100 + 3.14159 + 6.02e23 + 1e-12 + ''') + + pyparsing_common.fnumber.runTests(''' + # any int or real number, returned as float + 100 + -100 + +100 + 3.14159 + 6.02e23 + 1e-12 + ''') + + pyparsing_common.hex_integer.runTests(''' + # hex numbers + 100 + FF + ''') + + pyparsing_common.fraction.runTests(''' + # fractions + 1/2 + -3/4 + ''') + + pyparsing_common.mixed_integer.runTests(''' + # mixed fractions + 1 + 1/2 + -3/4 + 1-3/4 + ''') + + import uuid + pyparsing_common.uuid.setParseAction(tokenMap(uuid.UUID)) + pyparsing_common.uuid.runTests(''' + # uuid + 12345678-1234-5678-1234-567812345678 + ''') + prints:: + # any int or real number, returned as the appropriate type + 100 + [100] + + -100 + [-100] + + +100 + [100] + + 3.14159 + [3.14159] + + 6.02e23 + [6.02e+23] + + 1e-12 + [1e-12] + + # any int or real number, returned as float + 100 + [100.0] + + -100 + [-100.0] + + +100 + [100.0] + + 3.14159 + [3.14159] + + 6.02e23 + [6.02e+23] + + 1e-12 + [1e-12] + + # hex numbers + 100 + [256] + + FF + [255] + + # fractions + 1/2 + [0.5] + + -3/4 + [-0.75] + + # mixed fractions + 1 + [1] + + 1/2 + [0.5] + + -3/4 + [-0.75] + + 1-3/4 + [1.75] + + # uuid + 12345678-1234-5678-1234-567812345678 + [UUID('12345678-1234-5678-1234-567812345678')] + """ + + convertToInteger = tokenMap(int) + """ + Parse action for converting parsed integers to Python int + """ + + convertToFloat = tokenMap(float) + """ + Parse action for converting parsed numbers to Python float + """ + + integer = Word(nums).setName("integer").setParseAction(convertToInteger) + """expression that parses an unsigned integer, returns an int""" + + hex_integer = Word(hexnums).setName("hex integer").setParseAction(tokenMap(int,16)) + """expression that parses a hexadecimal integer, returns an int""" + + signed_integer = Regex(r'[+-]?\d+').setName("signed integer").setParseAction(convertToInteger) + """expression that parses an integer with optional leading sign, returns an int""" + + fraction = (signed_integer().setParseAction(convertToFloat) + '/' + signed_integer().setParseAction(convertToFloat)).setName("fraction") + """fractional expression of an integer divided by an integer, returns a float""" + fraction.addParseAction(lambda t: t[0]/t[-1]) + + mixed_integer = (fraction | signed_integer + Optional(Optional('-').suppress() + fraction)).setName("fraction or mixed integer-fraction") + """mixed integer of the form 'integer - fraction', with optional leading integer, returns float""" + mixed_integer.addParseAction(sum) + + real = Regex(r'[+-]?\d+\.\d*').setName("real number").setParseAction(convertToFloat) + """expression that parses a floating point number and returns a float""" + + sci_real = Regex(r'[+-]?\d+([eE][+-]?\d+|\.\d*([eE][+-]?\d+)?)').setName("real number with scientific notation").setParseAction(convertToFloat) + """expression that parses a floating point number with optional scientific notation and returns a float""" + + # streamlining this expression makes the docs nicer-looking + number = (sci_real | real | signed_integer).streamline() + """any numeric expression, returns the corresponding Python type""" + + fnumber = Regex(r'[+-]?\d+\.?\d*([eE][+-]?\d+)?').setName("fnumber").setParseAction(convertToFloat) + """any int or real number, returned as float""" + + identifier = Word(alphas+'_', alphanums+'_').setName("identifier") + """typical code identifier (leading alpha or '_', followed by 0 or more alphas, nums, or '_')""" + + ipv4_address = Regex(r'(25[0-5]|2[0-4][0-9]|1?[0-9]{1,2})(\.(25[0-5]|2[0-4][0-9]|1?[0-9]{1,2})){3}').setName("IPv4 address") + "IPv4 address (C{0.0.0.0 - 255.255.255.255})" + + _ipv6_part = Regex(r'[0-9a-fA-F]{1,4}').setName("hex_integer") + _full_ipv6_address = (_ipv6_part + (':' + _ipv6_part)*7).setName("full IPv6 address") + _short_ipv6_address = (Optional(_ipv6_part + (':' + _ipv6_part)*(0,6)) + "::" + Optional(_ipv6_part + (':' + _ipv6_part)*(0,6))).setName("short IPv6 address") + _short_ipv6_address.addCondition(lambda t: sum(1 for tt in t if pyparsing_common._ipv6_part.matches(tt)) < 8) + _mixed_ipv6_address = ("::ffff:" + ipv4_address).setName("mixed IPv6 address") + ipv6_address = Combine((_full_ipv6_address | _mixed_ipv6_address | _short_ipv6_address).setName("IPv6 address")).setName("IPv6 address") + "IPv6 address (long, short, or mixed form)" + + mac_address = Regex(r'[0-9a-fA-F]{2}([:.-])[0-9a-fA-F]{2}(?:\1[0-9a-fA-F]{2}){4}').setName("MAC address") + "MAC address xx:xx:xx:xx:xx (may also have '-' or '.' delimiters)" + + @staticmethod + def convertToDate(fmt="%Y-%m-%d"): + """ + Helper to create a parse action for converting parsed date string to Python datetime.date + + Params - + - fmt - format to be passed to datetime.strptime (default=C{"%Y-%m-%d"}) + + Example:: + date_expr = pyparsing_common.iso8601_date.copy() + date_expr.setParseAction(pyparsing_common.convertToDate()) + print(date_expr.parseString("1999-12-31")) + prints:: + [datetime.date(1999, 12, 31)] + """ + def cvt_fn(s,l,t): + try: + return datetime.strptime(t[0], fmt).date() + except ValueError as ve: + raise ParseException(s, l, str(ve)) + return cvt_fn + + @staticmethod + def convertToDatetime(fmt="%Y-%m-%dT%H:%M:%S.%f"): + """ + Helper to create a parse action for converting parsed datetime string to Python datetime.datetime + + Params - + - fmt - format to be passed to datetime.strptime (default=C{"%Y-%m-%dT%H:%M:%S.%f"}) + + Example:: + dt_expr = pyparsing_common.iso8601_datetime.copy() + dt_expr.setParseAction(pyparsing_common.convertToDatetime()) + print(dt_expr.parseString("1999-12-31T23:59:59.999")) + prints:: + [datetime.datetime(1999, 12, 31, 23, 59, 59, 999000)] + """ + def cvt_fn(s,l,t): + try: + return datetime.strptime(t[0], fmt) + except ValueError as ve: + raise ParseException(s, l, str(ve)) + return cvt_fn + + iso8601_date = Regex(r'(?P<year>\d{4})(?:-(?P<month>\d\d)(?:-(?P<day>\d\d))?)?').setName("ISO8601 date") + "ISO8601 date (C{yyyy-mm-dd})" + + iso8601_datetime = Regex(r'(?P<year>\d{4})-(?P<month>\d\d)-(?P<day>\d\d)[T ](?P<hour>\d\d):(?P<minute>\d\d)(:(?P<second>\d\d(\.\d*)?)?)?(?P<tz>Z|[+-]\d\d:?\d\d)?').setName("ISO8601 datetime") + "ISO8601 datetime (C{yyyy-mm-ddThh:mm:ss.s(Z|+-00:00)}) - trailing seconds, milliseconds, and timezone optional; accepts separating C{'T'} or C{' '}" + + uuid = Regex(r'[0-9a-fA-F]{8}(-[0-9a-fA-F]{4}){3}-[0-9a-fA-F]{12}').setName("UUID") + "UUID (C{xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx})" + + _html_stripper = anyOpenTag.suppress() | anyCloseTag.suppress() + @staticmethod + def stripHTMLTags(s, l, tokens): + """ + Parse action to remove HTML tags from web page HTML source + + Example:: + # strip HTML links from normal text + text = '<td>More info at the <a href="http://pyparsing.wikispaces.com">pyparsing</a> wiki page</td>' + td,td_end = makeHTMLTags("TD") + table_text = td + SkipTo(td_end).setParseAction(pyparsing_common.stripHTMLTags)("body") + td_end + + print(table_text.parseString(text).body) # -> 'More info at the pyparsing wiki page' + """ + return pyparsing_common._html_stripper.transformString(tokens[0]) + + _commasepitem = Combine(OneOrMore(~Literal(",") + ~LineEnd() + Word(printables, excludeChars=',') + + Optional( White(" \t") ) ) ).streamline().setName("commaItem") + comma_separated_list = delimitedList( Optional( quotedString.copy() | _commasepitem, default="") ).setName("comma separated list") + """Predefined expression of 1 or more printable words or quoted strings, separated by commas.""" + + upcaseTokens = staticmethod(tokenMap(lambda t: _ustr(t).upper())) + """Parse action to convert tokens to upper case.""" + + downcaseTokens = staticmethod(tokenMap(lambda t: _ustr(t).lower())) + """Parse action to convert tokens to lower case.""" + + +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") + columnSpec = ('*' | columnNameList) + + tableName = delimitedList(ident, ".", combine=True).setParseAction(upcaseTokens) + tableNameList = Group(delimitedList(tableName)).setName("tables") + + simpleSQL = selectToken("command") + columnSpec("columns") + fromToken + tableNameList("tables") + + # demo runTests method, including embedded comments in test string + simpleSQL.runTests(""" + # '*' as column list and dotted table name + select * from SYS.XYZZY + + # caseless match on "SELECT", and casts back to "select" + SELECT * from XYZZY, ABC + + # list of column names, and mixed case SELECT keyword + Select AA,BB,CC from Sys.dual + + # multiple tables + Select A, B, C from Sys.dual, Table2 + + # invalid SELECT keyword - should fail + Xelect A, B, C from Sys.dual + + # incomplete command - should fail + Select + + # invalid column name - should fail + Select ^^^ frox Sys.dual + + """) + + pyparsing_common.number.runTests(""" + 100 + -100 + +100 + 3.14159 + 6.02e23 + 1e-12 + """) + + # any int or real number, returned as float + pyparsing_common.fnumber.runTests(""" + 100 + -100 + +100 + 3.14159 + 6.02e23 + 1e-12 + """) + + pyparsing_common.hex_integer.runTests(""" + 100 + FF + """) + + import uuid + pyparsing_common.uuid.setParseAction(tokenMap(uuid.UUID)) + pyparsing_common.uuid.runTests(""" + 12345678-1234-5678-1234-567812345678 + """) diff --git a/venv2/lib/python2.7/site-packages/pkg_resources/_vendor/six.py b/venv2/lib/python2.7/site-packages/pkg_resources/_vendor/six.py new file mode 100644 index 0000000..190c023 --- /dev/null +++ b/venv2/lib/python2.7/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/venv2/lib/python2.7/site-packages/pkg_resources/extern/__init__.py b/venv2/lib/python2.7/site-packages/pkg_resources/extern/__init__.py new file mode 100644 index 0000000..c1eb9e9 --- /dev/null +++ b/venv2/lib/python2.7/site-packages/pkg_resources/extern/__init__.py @@ -0,0 +1,73 @@ +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 prefix and 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', 'appdirs' +VendorImporter(__name__, names).install() diff --git a/venv2/lib/python2.7/site-packages/pkg_resources/py31compat.py b/venv2/lib/python2.7/site-packages/pkg_resources/py31compat.py new file mode 100644 index 0000000..a381c42 --- /dev/null +++ b/venv2/lib/python2.7/site-packages/pkg_resources/py31compat.py @@ -0,0 +1,23 @@ +import os +import errno +import sys + +from .extern import six + + +def _makedirs_31(path, exist_ok=False): + try: + os.makedirs(path) + except OSError as exc: + if not exist_ok or exc.errno != errno.EEXIST: + raise + + +# rely on compatibility behavior until mode considerations +# and exists_ok considerations are disentangled. +# See https://github.com/pypa/setuptools/pull/1083#issuecomment-315168663 +needs_makedirs = ( + six.PY2 or + (3, 4) <= sys.version_info < (3, 4, 1) +) +makedirs = _makedirs_31 if needs_makedirs else os.makedirs diff --git a/venv2/lib/python2.7/site-packages/setuptools-41.0.1.dist-info/INSTALLER b/venv2/lib/python2.7/site-packages/setuptools-41.0.1.dist-info/INSTALLER new file mode 100644 index 0000000..a1b589e --- /dev/null +++ b/venv2/lib/python2.7/site-packages/setuptools-41.0.1.dist-info/INSTALLER @@ -0,0 +1 @@ +pip diff --git a/venv2/lib/python2.7/site-packages/setuptools-41.0.1.dist-info/LICENSE b/venv2/lib/python2.7/site-packages/setuptools-41.0.1.dist-info/LICENSE new file mode 100644 index 0000000..6e0693b --- /dev/null +++ b/venv2/lib/python2.7/site-packages/setuptools-41.0.1.dist-info/LICENSE @@ -0,0 +1,19 @@ +Copyright (C) 2016 Jason R Coombs <jaraco@jaraco.com> + +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/venv2/lib/python2.7/site-packages/setuptools-41.0.1.dist-info/METADATA b/venv2/lib/python2.7/site-packages/setuptools-41.0.1.dist-info/METADATA new file mode 100644 index 0000000..571a5cb --- /dev/null +++ b/venv2/lib/python2.7/site-packages/setuptools-41.0.1.dist-info/METADATA @@ -0,0 +1,77 @@ +Metadata-Version: 2.1 +Name: setuptools +Version: 41.0.1 +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 +Project-URL: Documentation, https://setuptools.readthedocs.io/ +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 +Classifier: Programming Language :: Python :: 2.7 +Classifier: Programming Language :: Python :: 3 +Classifier: Programming Language :: Python :: 3.4 +Classifier: Programming Language :: Python :: 3.5 +Classifier: Programming Language :: Python :: 3.6 +Classifier: Programming Language :: Python :: 3.7 +Classifier: Topic :: Software Development :: Libraries :: Python Modules +Classifier: Topic :: System :: Archiving :: Packaging +Classifier: Topic :: System :: Systems Administration +Classifier: Topic :: Utilities +Requires-Python: >=2.7,!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.* +Description-Content-Type: text/x-rst; charset=UTF-8 +Provides-Extra: certs +Requires-Dist: certifi (==2016.9.26) ; extra == 'certs' +Provides-Extra: ssl +Requires-Dist: wincertstore (==0.2) ; (sys_platform=='win32') and extra == 'ssl' + +.. image:: https://img.shields.io/pypi/v/setuptools.svg + :target: https://pypi.org/project/setuptools + +.. image:: https://img.shields.io/readthedocs/setuptools/latest.svg + :target: https://setuptools.readthedocs.io + +.. image:: https://img.shields.io/travis/pypa/setuptools/master.svg?label=Linux%20CI&logo=travis&logoColor=white + :target: https://travis-ci.org/pypa/setuptools + +.. image:: https://img.shields.io/appveyor/ci/pypa/setuptools/master.svg?label=Windows%20CI&logo=appveyor&logoColor=white + :target: https://ci.appveyor.com/project/pypa/setuptools/branch/master + +.. image:: https://img.shields.io/codecov/c/github/pypa/setuptools/master.svg?logo=codecov&logoColor=white + :target: https://codecov.io/gh/pypa/setuptools + +.. image:: https://tidelift.com/badges/github/pypa/setuptools?style=flat + :target: https://tidelift.com/subscription/pkg/pypi-setuptools?utm_source=pypi-setuptools&utm_medium=readme + +.. image:: https://img.shields.io/pypi/pyversions/setuptools.svg + +See the `Installation Instructions +<https://packaging.python.org/installing/>`_ in the Python Packaging +User's Guide for instructions on installing, upgrading, and uninstalling +Setuptools. + +Questions and comments should be directed to the `distutils-sig +mailing list <http://mail.python.org/pipermail/distutils-sig/>`_. +Bug reports and especially tested patches may be +submitted directly to the `bug tracker +<https://github.com/pypa/setuptools/issues>`_. + +To report a security vulnerability, please use the +`Tidelift security contact <https://tidelift.com/security>`_. +Tidelift will coordinate the fix and disclosure. + + +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 <https://www.pypa.io/en/latest/code-of-conduct/>`_. + + diff --git a/venv2/lib/python2.7/site-packages/setuptools-41.0.1.dist-info/RECORD b/venv2/lib/python2.7/site-packages/setuptools-41.0.1.dist-info/RECORD new file mode 100644 index 0000000..00582fe --- /dev/null +++ b/venv2/lib/python2.7/site-packages/setuptools-41.0.1.dist-info/RECORD @@ -0,0 +1,186 @@ +../../../bin/easy_install,sha256=OiyMOfM-g-pLIS1KjMdbhED4SQ7Qr6-xfGf8Nh6uYuo,266 +../../../bin/easy_install-2.7,sha256=OiyMOfM-g-pLIS1KjMdbhED4SQ7Qr6-xfGf8Nh6uYuo,266 +easy_install.py,sha256=MDC9vt5AxDsXX5qcKlBz2TnW6Tpuv_AobnfhCJ9X3PM,126 +easy_install.pyc,, +pkg_resources/__init__.py,sha256=pp8b7Asoaheso-q8lIMS1tpQp88xjAkYgCIRX-JsPlE,107982 +pkg_resources/__init__.pyc,, +pkg_resources/_vendor/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +pkg_resources/_vendor/__init__.pyc,, +pkg_resources/_vendor/appdirs.py,sha256=MievUEuv3l_mQISH5SF0shDk_BNhHHzYiAPrT3ITN4I,24701 +pkg_resources/_vendor/appdirs.pyc,, +pkg_resources/_vendor/packaging/__about__.py,sha256=zkcCPTN_6TcLW0Nrlg0176-R1QQ_WVPTm8sz1R4-HjM,720 +pkg_resources/_vendor/packaging/__about__.pyc,, +pkg_resources/_vendor/packaging/__init__.py,sha256=_vNac5TrzwsrzbOFIbF-5cHqc_Y2aPT2D7zrIR06BOo,513 +pkg_resources/_vendor/packaging/__init__.pyc,, +pkg_resources/_vendor/packaging/_compat.py,sha256=Vi_A0rAQeHbU-a9X0tt1yQm9RqkgQbDSxzRw8WlU9kA,860 +pkg_resources/_vendor/packaging/_compat.pyc,, +pkg_resources/_vendor/packaging/_structures.py,sha256=RImECJ4c_wTlaTYYwZYLHEiebDMaAJmK1oPARhw1T5o,1416 +pkg_resources/_vendor/packaging/_structures.pyc,, +pkg_resources/_vendor/packaging/markers.py,sha256=uEcBBtGvzqltgnArqb9c4RrcInXezDLos14zbBHhWJo,8248 +pkg_resources/_vendor/packaging/markers.pyc,, +pkg_resources/_vendor/packaging/requirements.py,sha256=SikL2UynbsT0qtY9ltqngndha_sfo0w6XGFhAhoSoaQ,4355 +pkg_resources/_vendor/packaging/requirements.pyc,, +pkg_resources/_vendor/packaging/specifiers.py,sha256=SAMRerzO3fK2IkFZCaZkuwZaL_EGqHNOz4pni4vhnN0,28025 +pkg_resources/_vendor/packaging/specifiers.pyc,, +pkg_resources/_vendor/packaging/utils.py,sha256=3m6WvPm6NNxE8rkTGmn0r75B_GZSGg7ikafxHsBN1WA,421 +pkg_resources/_vendor/packaging/utils.pyc,, +pkg_resources/_vendor/packaging/version.py,sha256=OwGnxYfr2ghNzYx59qWIBkrK3SnB6n-Zfd1XaLpnnM0,11556 +pkg_resources/_vendor/packaging/version.pyc,, +pkg_resources/_vendor/pyparsing.py,sha256=tmrp-lu-qO1i75ZzIN5A12nKRRD1Cm4Vpk-5LR9rims,232055 +pkg_resources/_vendor/pyparsing.pyc,, +pkg_resources/_vendor/six.py,sha256=A6hdJZVjI3t_geebZ9BzUvwRrIXo0lfwzQlM2LcKyas,30098 +pkg_resources/_vendor/six.pyc,, +pkg_resources/extern/__init__.py,sha256=cHiEfHuLmm6rs5Ve_ztBfMI7Lr31vss-D4wkqF5xzlI,2498 +pkg_resources/extern/__init__.pyc,, +pkg_resources/py31compat.py,sha256=-WQ0e4c3RG_acdhwC3gLiXhP_lg4G5q7XYkZkQg0gxU,558 +pkg_resources/py31compat.pyc,, +setuptools-41.0.1.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4 +setuptools-41.0.1.dist-info/LICENSE,sha256=wyo6w5WvYyHv0ovnPQagDw22q4h9HCHU_sRhKNIFbVo,1078 +setuptools-41.0.1.dist-info/METADATA,sha256=_i0otxacylu95CLISnMjTjG7DZ2vYZOvwedcoJ06JNE,3303 +setuptools-41.0.1.dist-info/RECORD,, +setuptools-41.0.1.dist-info/WHEEL,sha256=HX-v9-noUkyUoxyZ1PMSuS7auUxDAR4VBdoYLqD0xws,110 +setuptools-41.0.1.dist-info/dependency_links.txt,sha256=HlkCFkoK5TbZ5EMLbLKYhLcY_E31kBWD8TqW2EgmatQ,239 +setuptools-41.0.1.dist-info/entry_points.txt,sha256=jBqCYDlVjl__sjYFGXo1JQGIMAYFJE-prYWUtnMZEew,2990 +setuptools-41.0.1.dist-info/top_level.txt,sha256=2HUXVVwA4Pff1xgTFr3GsTXXKaPaO6vlG6oNJ_4u4Tg,38 +setuptools-41.0.1.dist-info/zip-safe,sha256=AbpHGcgLb-kRsJGnwFEktk7uzpZOCcBY74-YBdrKVGs,1 +setuptools/__init__.py,sha256=WBpCcn2lvdckotabeae1TTYonPOcgCIF3raD2zRWzBc,7283 +setuptools/__init__.pyc,, +setuptools/_deprecation_warning.py,sha256=jU9-dtfv6cKmtQJOXN8nP1mm7gONw5kKEtiPtbwnZyI,218 +setuptools/_deprecation_warning.pyc,, +setuptools/_vendor/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +setuptools/_vendor/__init__.pyc,, +setuptools/_vendor/packaging/__about__.py,sha256=zkcCPTN_6TcLW0Nrlg0176-R1QQ_WVPTm8sz1R4-HjM,720 +setuptools/_vendor/packaging/__about__.pyc,, +setuptools/_vendor/packaging/__init__.py,sha256=_vNac5TrzwsrzbOFIbF-5cHqc_Y2aPT2D7zrIR06BOo,513 +setuptools/_vendor/packaging/__init__.pyc,, +setuptools/_vendor/packaging/_compat.py,sha256=Vi_A0rAQeHbU-a9X0tt1yQm9RqkgQbDSxzRw8WlU9kA,860 +setuptools/_vendor/packaging/_compat.pyc,, +setuptools/_vendor/packaging/_structures.py,sha256=RImECJ4c_wTlaTYYwZYLHEiebDMaAJmK1oPARhw1T5o,1416 +setuptools/_vendor/packaging/_structures.pyc,, +setuptools/_vendor/packaging/markers.py,sha256=Gvpk9EY20yKaMTiKgQZ8yFEEpodqVgVYtfekoic1Yts,8239 +setuptools/_vendor/packaging/markers.pyc,, +setuptools/_vendor/packaging/requirements.py,sha256=t44M2HVWtr8phIz2OhnILzuGT3rTATaovctV1dpnVIg,4343 +setuptools/_vendor/packaging/requirements.pyc,, +setuptools/_vendor/packaging/specifiers.py,sha256=SAMRerzO3fK2IkFZCaZkuwZaL_EGqHNOz4pni4vhnN0,28025 +setuptools/_vendor/packaging/specifiers.pyc,, +setuptools/_vendor/packaging/utils.py,sha256=3m6WvPm6NNxE8rkTGmn0r75B_GZSGg7ikafxHsBN1WA,421 +setuptools/_vendor/packaging/utils.pyc,, +setuptools/_vendor/packaging/version.py,sha256=OwGnxYfr2ghNzYx59qWIBkrK3SnB6n-Zfd1XaLpnnM0,11556 +setuptools/_vendor/packaging/version.pyc,, +setuptools/_vendor/pyparsing.py,sha256=tmrp-lu-qO1i75ZzIN5A12nKRRD1Cm4Vpk-5LR9rims,232055 +setuptools/_vendor/pyparsing.pyc,, +setuptools/_vendor/six.py,sha256=A6hdJZVjI3t_geebZ9BzUvwRrIXo0lfwzQlM2LcKyas,30098 +setuptools/_vendor/six.pyc,, +setuptools/archive_util.py,sha256=kw8Ib_lKjCcnPKNbS7h8HztRVK0d5RacU3r_KRdVnmM,6592 +setuptools/archive_util.pyc,, +setuptools/build_meta.py,sha256=ioZE7tGSWY6vy7KLune0Iq334ugXuO39imcKC8prjBY,9387 +setuptools/build_meta.pyc,, +setuptools/cli-32.exe,sha256=dfEuovMNnA2HLa3jRfMPVi5tk4R7alCbpTvuxtCyw0Y,65536 +setuptools/cli-64.exe,sha256=KLABu5pyrnokJCv6skjXZ6GsXeyYHGcqOUT3oHI3Xpo,74752 +setuptools/cli.exe,sha256=dfEuovMNnA2HLa3jRfMPVi5tk4R7alCbpTvuxtCyw0Y,65536 +setuptools/command/__init__.py,sha256=NWzJ0A1BEengZpVeqUyWLNm2bk4P3F4iL5QUErHy7kA,594 +setuptools/command/__init__.pyc,, +setuptools/command/alias.py,sha256=KjpE0sz_SDIHv3fpZcIQK-sCkJz-SrC6Gmug6b9Nkc8,2426 +setuptools/command/alias.pyc,, +setuptools/command/bdist_egg.py,sha256=be-IBpr1zhS9i6GjKANJgzkbH3ChImdWY7S-j0r2BK8,18167 +setuptools/command/bdist_egg.pyc,, +setuptools/command/bdist_rpm.py,sha256=B7l0TnzCGb-0nLlm6rS00jWLkojASwVmdhW2w5Qz_Ak,1508 +setuptools/command/bdist_rpm.pyc,, +setuptools/command/bdist_wininst.py,sha256=_6dz3lpB1tY200LxKPLM7qgwTCceOMgaWFF-jW2-pm0,637 +setuptools/command/bdist_wininst.pyc,, +setuptools/command/build_clib.py,sha256=bQ9aBr-5ZSO-9fGsGsDLz0mnnFteHUZnftVLkhvHDq0,4484 +setuptools/command/build_clib.pyc,, +setuptools/command/build_ext.py,sha256=81CTgsqjBjNl_HOgCJ1lQ5vv1NIM3RBpcoVGpqT4N1M,12897 +setuptools/command/build_ext.pyc,, +setuptools/command/build_py.py,sha256=yWyYaaS9F3o9JbIczn064A5g1C5_UiKRDxGaTqYbtLE,9596 +setuptools/command/build_py.pyc,, +setuptools/command/develop.py,sha256=MQlnGS6uP19erK2JCNOyQYoYyquk3PADrqrrinqqLtA,8184 +setuptools/command/develop.pyc,, +setuptools/command/dist_info.py,sha256=5t6kOfrdgALT-P3ogss6PF9k-Leyesueycuk3dUyZnI,960 +setuptools/command/dist_info.pyc,, +setuptools/command/easy_install.py,sha256=telww7CuPsoTtvlpY-ktnZGT85cZ6xGCGZa0vHvFJ-Q,87273 +setuptools/command/easy_install.pyc,, +setuptools/command/egg_info.py,sha256=w73EdxYSOk2gsaAiHGL2dZrCldoPiuRr2eTfqcFvCds,25570 +setuptools/command/egg_info.pyc,, +setuptools/command/install.py,sha256=a0EZpL_A866KEdhicTGbuyD_TYl1sykfzdrri-zazT4,4683 +setuptools/command/install.pyc,, +setuptools/command/install_egg_info.py,sha256=bMgeIeRiXzQ4DAGPV1328kcjwQjHjOWU4FngAWLV78Q,2203 +setuptools/command/install_egg_info.pyc,, +setuptools/command/install_lib.py,sha256=11mxf0Ch12NsuYwS8PHwXBRvyh671QAM4cTRh7epzG0,3840 +setuptools/command/install_lib.pyc,, +setuptools/command/install_scripts.py,sha256=UD0rEZ6861mTYhIdzcsqKnUl8PozocXWl9VBQ1VTWnc,2439 +setuptools/command/install_scripts.pyc,, +setuptools/command/launcher manifest.xml,sha256=xlLbjWrB01tKC0-hlVkOKkiSPbzMml2eOPtJ_ucCnbE,628 +setuptools/command/py36compat.py,sha256=SzjZcOxF7zdFUT47Zv2n7AM3H8koDys_0OpS-n9gIfc,4986 +setuptools/command/py36compat.pyc,, +setuptools/command/register.py,sha256=LO3MvYKPE8dN1m-KkrBRHC68ZFoPvA_vI8Xgp7vv6zI,534 +setuptools/command/register.pyc,, +setuptools/command/rotate.py,sha256=co5C1EkI7P0GGT6Tqz-T2SIj2LBJTZXYELpmao6d4KQ,2164 +setuptools/command/rotate.pyc,, +setuptools/command/saveopts.py,sha256=za7QCBcQimKKriWcoCcbhxPjUz30gSB74zuTL47xpP4,658 +setuptools/command/saveopts.pyc,, +setuptools/command/sdist.py,sha256=gr5hFrDzUtGfp_0tu0sllzIyr3jMQegIkFmlDauQJxw,7388 +setuptools/command/sdist.pyc,, +setuptools/command/setopt.py,sha256=NTWDyx-gjDF-txf4dO577s7LOzHVoKR0Mq33rFxaRr8,5085 +setuptools/command/setopt.pyc,, +setuptools/command/test.py,sha256=oePJ49u17ENKtrM-rOrrLlRhtNnrzcSr0IW-gE9XVq0,9285 +setuptools/command/test.pyc,, +setuptools/command/upload.py,sha256=GxtNkIl7SA0r8mACkbDcSCN1m2_WPppK9gZXJmQSiow,6811 +setuptools/command/upload.pyc,, +setuptools/command/upload_docs.py,sha256=oXiGplM_cUKLwE4CWWw98RzCufAu8tBhMC97GegFcms,7311 +setuptools/command/upload_docs.pyc,, +setuptools/config.py,sha256=lz19l1AtoHctpp1_tbYZv176nrEj4Gpf7ykNIYTIkAQ,20425 +setuptools/config.pyc,, +setuptools/dep_util.py,sha256=fgixvC1R7sH3r13ktyf7N0FALoqEXL1cBarmNpSEoWg,935 +setuptools/dep_util.pyc,, +setuptools/depends.py,sha256=hC8QIDcM3VDpRXvRVA6OfL9AaQfxvhxHcN_w6sAyNq8,5837 +setuptools/depends.pyc,, +setuptools/dist.py,sha256=qYPmmVlLPWCLHrILR0J74bqoYgTSZh5ocLeyRKqnVyU,49913 +setuptools/dist.pyc,, +setuptools/extension.py,sha256=uc6nHI-MxwmNCNPbUiBnybSyqhpJqjbhvOQ-emdvt_E,1729 +setuptools/extension.pyc,, +setuptools/extern/__init__.py,sha256=TxeNKFMSfBMzBpBDiHx8Dh3RzsdVmvWaXhtZ03DZMs0,2499 +setuptools/extern/__init__.pyc,, +setuptools/glibc.py,sha256=X64VvGPL2AbURKwYRsWJOXXGAYOiF_v2qixeTkAULuU,3146 +setuptools/glibc.pyc,, +setuptools/glob.py,sha256=o75cHrOxYsvn854thSxE0x9k8JrKDuhP_rRXlVB00Q4,5084 +setuptools/glob.pyc,, +setuptools/gui-32.exe,sha256=XBr0bHMA6Hpz2s9s9Bzjl-PwXfa9nH4ie0rFn4V2kWA,65536 +setuptools/gui-64.exe,sha256=aYKMhX1IJLn4ULHgWX0sE0yREUt6B3TEHf_jOw6yNyE,75264 +setuptools/gui.exe,sha256=XBr0bHMA6Hpz2s9s9Bzjl-PwXfa9nH4ie0rFn4V2kWA,65536 +setuptools/launch.py,sha256=sd7ejwhBocCDx_wG9rIs0OaZ8HtmmFU8ZC6IR_S0Lvg,787 +setuptools/launch.pyc,, +setuptools/lib2to3_ex.py,sha256=t5e12hbR2pi9V4ezWDTB4JM-AISUnGOkmcnYHek3xjg,2013 +setuptools/lib2to3_ex.pyc,, +setuptools/monkey.py,sha256=FGc9fffh7gAxMLFmJs2DW_OYWpBjkdbNS2n14UAK4NA,5264 +setuptools/monkey.pyc,, +setuptools/msvc.py,sha256=uuRFaZzjJt5Fv3ZmyKUUuLtjx12_8G9RILigGec4irI,40838 +setuptools/msvc.pyc,, +setuptools/namespaces.py,sha256=F0Nrbv8KCT2OrO7rwa03om4N4GZKAlnce-rr-cgDQa8,3199 +setuptools/namespaces.pyc,, +setuptools/package_index.py,sha256=F9LBC-hQ5fkjeEVflxif0mo_DzRMrepahdFTPenOtGM,40587 +setuptools/package_index.pyc,, +setuptools/pep425tags.py,sha256=o_D_WVeWcXZiI2xjPSg7pouGOvaWRgGRxEDK9DzAXIA,10861 +setuptools/pep425tags.pyc,, +setuptools/py27compat.py,sha256=3mwxRMDk5Q5O1rSXOERbQDXhFqwDJhhUitfMW_qpUCo,536 +setuptools/py27compat.pyc,, +setuptools/py31compat.py,sha256=h2rtZghOfwoGYd8sQ0-auaKiF3TcL3qX0bX3VessqcE,838 +setuptools/py31compat.pyc,, +setuptools/py33compat.py,sha256=OubjldHJH1KGE1CKt1kRU-Q55keftHT3ea1YoL0ZSco,1195 +setuptools/py33compat.pyc,, +setuptools/sandbox.py,sha256=9UbwfEL5QY436oMI1LtFWohhoZ-UzwHvGyZjUH_qhkw,14276 +setuptools/sandbox.pyc,, +setuptools/script (dev).tmpl,sha256=RUzQzCQUaXtwdLtYHWYbIQmOaES5Brqq1FvUA_tu-5I,218 +setuptools/script.tmpl,sha256=WGTt5piezO27c-Dbx6l5Q4T3Ff20A5z7872hv3aAhYY,138 +setuptools/site-patch.py,sha256=OumkIHMuoSenRSW1382kKWI1VAwxNE86E5W8iDd34FY,2302 +setuptools/site-patch.pyc,, +setuptools/ssl_support.py,sha256=nLjPUBBw7RTTx6O4RJZ5eAMGgjJG8beiDbkFXDZpLuM,8493 +setuptools/ssl_support.pyc,, +setuptools/unicode_utils.py,sha256=NOiZ_5hD72A6w-4wVj8awHFM3n51Kmw1Ic_vx15XFqw,996 +setuptools/unicode_utils.pyc,, +setuptools/version.py,sha256=og_cuZQb0QI6ukKZFfZWPlr1HgJBPPn2vO2m_bI9ZTE,144 +setuptools/version.pyc,, +setuptools/wheel.py,sha256=94uqXsOaKt91d9hW5z6ZppZmNSs_nO66R4uiwhcr4V0,8094 +setuptools/wheel.pyc,, +setuptools/windows_support.py,sha256=5GrfqSP2-dLGJoZTq2g6dCKkyQxxa2n5IQiXlJCoYEE,714 +setuptools/windows_support.pyc,, diff --git a/venv2/lib/python2.7/site-packages/setuptools-41.0.1.dist-info/WHEEL b/venv2/lib/python2.7/site-packages/setuptools-41.0.1.dist-info/WHEEL new file mode 100644 index 0000000..c8240f0 --- /dev/null +++ b/venv2/lib/python2.7/site-packages/setuptools-41.0.1.dist-info/WHEEL @@ -0,0 +1,6 @@ +Wheel-Version: 1.0 +Generator: bdist_wheel (0.33.1) +Root-Is-Purelib: true +Tag: py2-none-any +Tag: py3-none-any + diff --git a/venv2/lib/python2.7/site-packages/setuptools-41.0.1.dist-info/dependency_links.txt b/venv2/lib/python2.7/site-packages/setuptools-41.0.1.dist-info/dependency_links.txt new file mode 100644 index 0000000..e87d021 --- /dev/null +++ b/venv2/lib/python2.7/site-packages/setuptools-41.0.1.dist-info/dependency_links.txt @@ -0,0 +1,2 @@ +https://files.pythonhosted.org/packages/source/c/certifi/certifi-2016.9.26.tar.gz#md5=baa81e951a29958563689d868ef1064d +https://files.pythonhosted.org/packages/source/w/wincertstore/wincertstore-0.2.zip#md5=ae728f2f007185648d0c7a8679b361e2 diff --git a/venv2/lib/python2.7/site-packages/setuptools-41.0.1.dist-info/entry_points.txt b/venv2/lib/python2.7/site-packages/setuptools-41.0.1.dist-info/entry_points.txt new file mode 100644 index 0000000..4159fd0 --- /dev/null +++ b/venv2/lib/python2.7/site-packages/setuptools-41.0.1.dist-info/entry_points.txt @@ -0,0 +1,65 @@ +[console_scripts] +easy_install = setuptools.command.easy_install:main +easy_install-3.6 = 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_clib = setuptools.command.build_clib:build_clib +build_ext = setuptools.command.build_ext:build_ext +build_py = setuptools.command.build_py:build_py +develop = setuptools.command.develop:develop +dist_info = setuptools.command.dist_info:dist_info +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 +python_requires = setuptools.dist:check_specifier +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/venv2/lib/python2.7/site-packages/setuptools-41.0.1.dist-info/top_level.txt b/venv2/lib/python2.7/site-packages/setuptools-41.0.1.dist-info/top_level.txt new file mode 100644 index 0000000..4577c6a --- /dev/null +++ b/venv2/lib/python2.7/site-packages/setuptools-41.0.1.dist-info/top_level.txt @@ -0,0 +1,3 @@ +easy_install +pkg_resources +setuptools diff --git a/venv2/lib/python2.7/site-packages/setuptools-41.0.1.dist-info/zip-safe b/venv2/lib/python2.7/site-packages/setuptools-41.0.1.dist-info/zip-safe new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/venv2/lib/python2.7/site-packages/setuptools-41.0.1.dist-info/zip-safe @@ -0,0 +1 @@ + diff --git a/venv2/lib/python2.7/site-packages/setuptools/__init__.py b/venv2/lib/python2.7/site-packages/setuptools/__init__.py new file mode 100644 index 0000000..a71b2bb --- /dev/null +++ b/venv2/lib/python2.7/site-packages/setuptools/__init__.py @@ -0,0 +1,228 @@ +"""Extensions to the 'distutils' for large or complex distributions""" + +import os +import sys +import functools +import distutils.core +import distutils.filelist +import re +from distutils.errors import DistutilsOptionError +from distutils.util import convert_path +from fnmatch import fnmatchcase + +from ._deprecation_warning import SetuptoolsDeprecationWarning + +from setuptools.extern.six import PY3, string_types +from setuptools.extern.six.moves import filter, map + +import setuptools.version +from setuptools.extension import Extension +from setuptools.dist import Distribution, Feature +from setuptools.depends import Require +from . import monkey + +__metaclass__ = type + + +__all__ = [ + 'setup', 'Distribution', 'Feature', 'Command', 'Extension', 'Require', + 'SetuptoolsDeprecationWarning', + 'find_packages' +] + +if PY3: + __all__.append('find_namespace_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: + """ + Generate a list of all Python packages found within a directory + """ + + @classmethod + def find(cls, where='.', exclude=(), include=('*',)): + """Return a list all Python packages found within directory 'where' + + 'where' is the root directory which will be searched for packages. It + 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'. + """ + + return list(cls._find_packages_iter( + convert_path(where), + cls._build_filter('ez_setup', '*__pycache__', *exclude), + cls._build_filter(*include))) + + @classmethod + def _find_packages_iter(cls, where, exclude, include): + """ + All the packages found in 'where' that pass the 'include' filter, but + not the 'exclude' filter. + """ + for root, dirs, files in os.walk(where, followlinks=True): + # Copy dirs to iterate over it, then empty dirs. + all_dirs = dirs[:] + dirs[:] = [] + + for dir in all_dirs: + full_path = os.path.join(root, dir) + rel_path = os.path.relpath(full_path, where) + package = rel_path.replace(os.path.sep, '.') + + # Skip directory trees that are not valid packages + if ('.' in dir or not cls._looks_like_package(full_path)): + continue + + # Should this package be included? + if include(package) and not exclude(package): + yield package + + # Keep searching subdirectories, as there may be more packages + # down there, even if the parent was excluded. + dirs.append(dir) + + @staticmethod + def _looks_like_package(path): + """Does a directory look like a package?""" + 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 at least 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 + +if PY3: + find_namespace_packages = PEP420PackageFinder.find + + +def _install_setup_requires(attrs): + # Note: do not use `setuptools.Distribution` directly, as + # our PEP 517 backend patch `distutils.core.Distribution`. + dist = distutils.core.Distribution(dict( + (k, v) for k, v in attrs.items() + if k in ('dependency_links', 'setup_requires') + )) + # Honor setup.cfg's options. + dist.parse_config_files(ignore_option_errors=True) + if dist.setup_requires: + dist.fetch_build_eggs(dist.setup_requires) + + +def setup(**attrs): + # Make sure we have any requirements needed to interpret 'attrs'. + _install_setup_requires(attrs) + return distutils.core.setup(**attrs) + +setup.__doc__ = distutils.core.setup.__doc__ + + +_Command = monkey.get_unpatched(distutils.core.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 _ensure_stringlike(self, option, what, default=None): + val = getattr(self, option) + if val is None: + setattr(self, option, default) + return default + elif not isinstance(val, string_types): + raise DistutilsOptionError("'%s' must be a %s (got `%s`)" + % (option, what, val)) + return val + + def ensure_string_list(self, option): + r"""Ensure that 'option' is a list of strings. If 'option' is + currently a string, we split it either on /,\s*/ or /\s+/, so + "foo bar baz", "foo,bar,baz", and "foo, bar baz" all become + ["foo", "bar", "baz"]. + """ + val = getattr(self, option) + if val is None: + return + elif isinstance(val, string_types): + setattr(self, option, re.split(r',\s*|\s+', val)) + else: + if isinstance(val, list): + ok = all(isinstance(v, string_types) for v in val) + else: + ok = False + if not ok: + raise DistutilsOptionError( + "'%s' must be a list of strings (got %r)" + % (option, val)) + + def reinitialize_command(self, command, reinit_subcommands=0, **kw): + cmd = _Command.reinitialize_command(self, command, reinit_subcommands) + vars(cmd).update(kw) + return cmd + + +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) + + +# Apply monkey patches +monkey.patch_all() diff --git a/venv2/lib/python2.7/site-packages/setuptools/_deprecation_warning.py b/venv2/lib/python2.7/site-packages/setuptools/_deprecation_warning.py new file mode 100644 index 0000000..086b64d --- /dev/null +++ b/venv2/lib/python2.7/site-packages/setuptools/_deprecation_warning.py @@ -0,0 +1,7 @@ +class SetuptoolsDeprecationWarning(Warning): + """ + Base class for warning deprecations in ``setuptools`` + + This class is not derived from ``DeprecationWarning``, and as such is + visible by default. + """ diff --git a/venv2/lib/python2.7/site-packages/setuptools/_vendor/__init__.py b/venv2/lib/python2.7/site-packages/setuptools/_vendor/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/venv2/lib/python2.7/site-packages/setuptools/_vendor/packaging/__about__.py b/venv2/lib/python2.7/site-packages/setuptools/_vendor/packaging/__about__.py new file mode 100644 index 0000000..95d330e --- /dev/null +++ b/venv2/lib/python2.7/site-packages/setuptools/_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.8" + +__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/venv2/lib/python2.7/site-packages/setuptools/_vendor/packaging/__init__.py b/venv2/lib/python2.7/site-packages/setuptools/_vendor/packaging/__init__.py new file mode 100644 index 0000000..5ee6220 --- /dev/null +++ b/venv2/lib/python2.7/site-packages/setuptools/_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/venv2/lib/python2.7/site-packages/setuptools/_vendor/packaging/_compat.py b/venv2/lib/python2.7/site-packages/setuptools/_vendor/packaging/_compat.py new file mode 100644 index 0000000..210bb80 --- /dev/null +++ b/venv2/lib/python2.7/site-packages/setuptools/_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/venv2/lib/python2.7/site-packages/setuptools/_vendor/packaging/_structures.py b/venv2/lib/python2.7/site-packages/setuptools/_vendor/packaging/_structures.py new file mode 100644 index 0000000..ccc2786 --- /dev/null +++ b/venv2/lib/python2.7/site-packages/setuptools/_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/venv2/lib/python2.7/site-packages/setuptools/_vendor/packaging/markers.py b/venv2/lib/python2.7/site-packages/setuptools/_vendor/packaging/markers.py new file mode 100644 index 0000000..031332a --- /dev/null +++ b/venv2/lib/python2.7/site-packages/setuptools/_vendor/packaging/markers.py @@ -0,0 +1,301 @@ +# 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 setuptools.extern.pyparsing import ParseException, ParseResults, stringStart, stringEnd +from setuptools.extern.pyparsing import ZeroOrMore, Group, Forward, QuotedString +from setuptools.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)) + + def serialize(self): + raise NotImplementedError + + +class Variable(Node): + + def serialize(self): + return str(self) + + +class Value(Node): + + def serialize(self): + return '"{0}"'.format(self) + + +class Op(Node): + + def serialize(self): + return str(self) + + +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("python_implementation") | # undocumented setuptools legacy + L("extra") +) +ALIASES = { + 'os.name': 'os_name', + 'sys.platform': 'sys_platform', + 'platform.version': 'platform_version', + 'platform.machine': 'platform_machine', + 'platform.python_implementation': 'platform_python_implementation', + 'python_implementation': 'platform_python_implementation' +} +VARIABLE.setParseAction(lambda s, l, t: Variable(ALIASES.get(t[0], t[0]))) + +VERSION_CMP = ( + L("===") | + L("==") | + L(">=") | + L("<=") | + L("!=") | + L("~=") | + L(">") | + L("<") +) + +MARKER_OP = VERSION_CMP | L("not in") | L("in") +MARKER_OP.setParseAction(lambda s, l, t: Op(t[0])) + +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 " ".join([m.serialize() for m in 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.serialize(), rhs])) + except InvalidSpecifier: + pass + else: + return spec.contains(lhs) + + oper = _operators.get(op.serialize()) + 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/venv2/lib/python2.7/site-packages/setuptools/_vendor/packaging/requirements.py b/venv2/lib/python2.7/site-packages/setuptools/_vendor/packaging/requirements.py new file mode 100644 index 0000000..5b49341 --- /dev/null +++ b/venv2/lib/python2.7/site-packages/setuptools/_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 setuptools.extern.pyparsing import stringStart, stringEnd, originalTextFor, ParseException +from setuptools.extern.pyparsing import ZeroOrMore, Word, Optional, Regex, Combine +from setuptools.extern.pyparsing import Literal as L # noqa +from setuptools.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/venv2/lib/python2.7/site-packages/setuptools/_vendor/packaging/specifiers.py b/venv2/lib/python2.7/site-packages/setuptools/_vendor/packaging/specifiers.py new file mode 100644 index 0000000..7f5a76c --- /dev/null +++ b/venv2/lib/python2.7/site-packages/setuptools/_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/venv2/lib/python2.7/site-packages/setuptools/_vendor/packaging/utils.py b/venv2/lib/python2.7/site-packages/setuptools/_vendor/packaging/utils.py new file mode 100644 index 0000000..942387c --- /dev/null +++ b/venv2/lib/python2.7/site-packages/setuptools/_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/venv2/lib/python2.7/site-packages/setuptools/_vendor/packaging/version.py b/venv2/lib/python2.7/site-packages/setuptools/_vendor/packaging/version.py new file mode 100644 index 0000000..83b5ee8 --- /dev/null +++ b/venv2/lib/python2.7/site-packages/setuptools/_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/venv2/lib/python2.7/site-packages/setuptools/_vendor/pyparsing.py b/venv2/lib/python2.7/site-packages/setuptools/_vendor/pyparsing.py new file mode 100644 index 0000000..cf75e1e --- /dev/null +++ b/venv2/lib/python2.7/site-packages/setuptools/_vendor/pyparsing.py @@ -0,0 +1,5742 @@ +# module pyparsing.py +# +# Copyright (c) 2003-2018 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>!"}), built up using L{Word}, L{Literal}, and L{And} elements +(L{'+'<ParserElement.__add__>} operator gives L{And} expressions, strings are auto-converted to +L{Literal} expressions):: + + 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 L{ParseResults} object returned from L{ParserElement.parseString<ParserElement.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 + + +Getting Started - +----------------- +Visit the classes L{ParserElement} and L{ParseResults} to see the base classes that most other pyparsing +classes inherit from. Use the docstrings for examples of how to: + - construct literal match expressions from L{Literal} and L{CaselessLiteral} classes + - construct character word-group expressions using the L{Word} class + - see how to create repetitive expressions using L{ZeroOrMore} and L{OneOrMore} classes + - use L{'+'<And>}, L{'|'<MatchFirst>}, L{'^'<Or>}, and L{'&'<Each>} operators to combine simple expressions into more complex ones + - associate names with your parsed results using L{ParserElement.setResultsName} + - find some helpful expression short-cuts like L{delimitedList} and L{oneOf} + - find more useful common expressions in the L{pyparsing_common} namespace class +""" + +__version__ = "2.2.1" +__versionTime__ = "18 Sep 2018 00:49 UTC" +__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 traceback +import types +from datetime import datetime + +try: + from _thread import RLock +except ImportError: + from threading import RLock + +try: + # Python 3 + from collections.abc import Iterable + from collections.abc import MutableMapping +except ImportError: + # Python 2.7 + from collections import Iterable + from collections import MutableMapping + +try: + from collections import OrderedDict as _OrderedDict +except ImportError: + try: + from ordereddict import OrderedDict as _OrderedDict + except ImportError: + _OrderedDict = None + +#~ 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', +'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', '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', +'CloseMatch', 'tokenMap', 'pyparsing_common', +] + +system_version = tuple(sys.version_info)[:3] +PY_3 = system_version[0] == 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: + # Else encode it + ret = unicode(obj).encode(sys.getdefaultencoding(), 'xmlcharrefreplace') + xmlcharref = Regex(r'&#\d+;') + xmlcharref.setParseAction(lambda t: '\\u' + hex(int(t[0][2:-1]))[2:]) + return xmlcharref.transformString(ret) + + # 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_uppercase + string.ascii_lowercase +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 + self.args = (pstr, loc, msg) + + @classmethod + def _from_exception(cls, pe): + """ + internal factory method to simplify creating one type of ParseException + from another - avoids having __init__ signature conflicts among subclasses + """ + return cls(pe.pstr, pe.loc, pe.msg, pe.parserElement) + + 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 "lineno col line".split() + dir(type(self)) + +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 + + Example:: + try: + Word(nums).setName("integer").parseString("ABC") + except ParseException as pe: + print(pe) + print("column: {}".format(pe.col)) + + prints:: + Expected integer (at char 0), (line:1, col:1) + column: 1 + """ + pass + +class ParseFatalException(ParseBaseException): + """user-throwable exception thrown when inconsistent parse content + is found; stops all parsing immediately""" + pass + +class ParseSyntaxException(ParseFatalException): + """just like L{ParseFatalException}, but thrown internally when an + L{ErrorStop<And._ErrorStop>} ('-' operator) indicates that parsing is to stop + immediately because an unbacktrackable syntax error has been found""" + pass + +#~ 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 L{ParserElement.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[0]) + 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>} - see L{ParserElement.setResultsName}) + + Example:: + integer = Word(nums) + date_str = (integer.setResultsName("year") + '/' + + integer.setResultsName("month") + '/' + + integer.setResultsName("day")) + # equivalent form: + # date_str = integer("year") + '/' + integer("month") + '/' + integer("day") + + # parseString returns a ParseResults object + result = date_str.parseString("1999/12/31") + + def test(s, fn=repr): + print("%s -> %s" % (s, fn(eval(s)))) + test("list(result)") + test("result[0]") + test("result['month']") + test("result.day") + test("'month' in result") + test("'minutes' in result") + test("result.dump()", str) + prints:: + list(result) -> ['1999', '/', '12', '/', '31'] + result[0] -> '1999' + result['month'] -> '12' + result.day -> '31' + 'month' in result -> True + 'minutes' in result -> False + result.dump() -> ['1999', '/', '12', '/', '31'] + - day: 31 + - month: 12 + - year: 1999 + """ + def __new__(cls, toklist=None, 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=None, name=None, asList=True, modal=True, isinstance=isinstance ): + if self.__doinit: + self.__doinit = False + self.__name = None + self.__parent = None + self.__accumNames = {} + self.__asList = asList + self.__modal = modal + if toklist is None: + toklist = [] + 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,slice)): + 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,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 ( not not self.__toklist ) + __nonzero__ = __bool__ + def __iter__( self ): return iter( self.__toklist ) + def __reversed__( self ): return iter( self.__toklist[::-1] ) + def _iterkeys( self ): + if hasattr(self.__tokdict, "iterkeys"): + return self.__tokdict.iterkeys() + else: + return iter(self.__tokdict) + + def _itervalues( self ): + 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 + """Returns an iterator of all named result keys (Python 3.x only).""" + + values = _itervalues + """Returns an iterator of all named result values (Python 3.x only).""" + + items = _iteritems + """Returns an iterator of all named result key-value tuples (Python 3.x only).""" + + else: + iterkeys = _iterkeys + """Returns an iterator of all named result keys (Python 2.x only).""" + + itervalues = _itervalues + """Returns an iterator of all named result values (Python 2.x only).""" + + iteritems = _iteritems + """Returns an iterator of all named result key-value tuples (Python 2.x only).""" + + def keys( self ): + """Returns all named result keys (as a list in Python 2.x, as an iterator in Python 3.x).""" + return list(self.iterkeys()) + + def values( self ): + """Returns all named result values (as a list in Python 2.x, as an iterator in Python 3.x).""" + return list(self.itervalues()) + + def items( self ): + """Returns all named result key-values (as a list of tuples in Python 2.x, as an iterator in Python 3.x).""" + 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=C{last}). + Supports both C{list} and C{dict} semantics for C{pop()}. If passed no + argument or an integer argument, it will use C{list} semantics + and pop tokens from the list of parsed tokens. If passed a + non-integer argument (most likely a string), it will use C{dict} + semantics and pop the corresponding value from any defined + results names. A second default return value argument is + supported, just as in C{dict.pop()}. + + Example:: + def remove_first(tokens): + tokens.pop(0) + print(OneOrMore(Word(nums)).parseString("0 123 321")) # -> ['0', '123', '321'] + print(OneOrMore(Word(nums)).addParseAction(remove_first).parseString("0 123 321")) # -> ['123', '321'] + + label = Word(alphas) + patt = label("LABEL") + OneOrMore(Word(nums)) + print(patt.parseString("AAB 123 321").dump()) + + # Use pop() in a parse action to remove named result (note that corresponding value is not + # removed from list form of results) + def remove_LABEL(tokens): + tokens.pop("LABEL") + return tokens + patt.addParseAction(remove_LABEL) + print(patt.parseString("AAB 123 321").dump()) + prints:: + ['AAB', '123', '321'] + - LABEL: AAB + + ['AAB', '123', '321'] + """ + 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. + + Similar to C{dict.get()}. + + Example:: + integer = Word(nums) + date_str = integer("year") + '/' + integer("month") + '/' + integer("day") + + result = date_str.parseString("1999/12/31") + print(result.get("year")) # -> '1999' + print(result.get("hour", "not specified")) # -> 'not specified' + print(result.get("hour")) # -> None + """ + 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. + + Similar to C{list.insert()}. + + Example:: + print(OneOrMore(Word(nums)).parseString("0 123 321")) # -> ['0', '123', '321'] + + # use a parse action to insert the parse location in the front of the parsed results + def insert_locn(locn, tokens): + tokens.insert(0, locn) + print(OneOrMore(Word(nums)).addParseAction(insert_locn).parseString("0 123 321")) # -> [0, '0', '123', '321'] + """ + self.__toklist.insert(index, insStr) + # fixup indices in token dictionary + 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. + + Example:: + print(OneOrMore(Word(nums)).parseString("0 123 321")) # -> ['0', '123', '321'] + + # use a parse action to compute the sum of the parsed integers, and add it to the end + def append_sum(tokens): + tokens.append(sum(map(int, tokens))) + print(OneOrMore(Word(nums)).addParseAction(append_sum).parseString("0 123 321")) # -> ['0', '123', '321', 444] + """ + self.__toklist.append(item) + + def extend( self, itemseq ): + """ + Add sequence of elements to end of ParseResults list of elements. + + Example:: + patt = OneOrMore(Word(alphas)) + + # use a parse action to append the reverse of the matched strings, to make a palindrome + def make_palindrome(tokens): + tokens.extend(reversed([t[::-1] for t in tokens])) + return ''.join(tokens) + print(patt.addParseAction(make_palindrome).parseString("lskdj sdlkjf lksd")) # -> 'lskdjsdlkjflksddsklfjkldsjdksl' + """ + 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: + # useful for merging many ParseResults using sum() builtin + return self.copy() + else: + # this may raise a TypeError - so be it + return other + self + + 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. + + Example:: + patt = OneOrMore(Word(alphas)) + result = patt.parseString("sldkj lsdkj sldkj") + # even though the result prints in string-like form, it is actually a pyparsing ParseResults + print(type(result), result) # -> <class 'pyparsing.ParseResults'> ['sldkj', 'lsdkj', 'sldkj'] + + # Use asList() to create an actual list + result_list = result.asList() + print(type(result_list), result_list) # -> <class 'list'> ['sldkj', 'lsdkj', 'sldkj'] + """ + return [res.asList() if isinstance(res,ParseResults) else res for res in self.__toklist] + + def asDict( self ): + """ + Returns the named parse results as a nested dictionary. + + Example:: + integer = Word(nums) + date_str = integer("year") + '/' + integer("month") + '/' + integer("day") + + result = date_str.parseString('12/31/1999') + print(type(result), repr(result)) # -> <class 'pyparsing.ParseResults'> (['12', '/', '31', '/', '1999'], {'day': [('1999', 4)], 'year': [('12', 0)], 'month': [('31', 2)]}) + + result_dict = result.asDict() + print(type(result_dict), repr(result_dict)) # -> <class 'dict'> {'day': '1999', 'year': '12', 'month': '31'} + + # even though a ParseResults supports dict-like access, sometime you just need to have a dict + import json + print(json.dumps(result)) # -> Exception: TypeError: ... is not JSON serializable + print(json.dumps(result.asDict())) # -> {"month": "31", "day": "1999", "year": "12"} + """ + if PY_3: + item_fn = self.items + else: + item_fn = self.iteritems + + def toItem(obj): + if isinstance(obj, ParseResults): + if obj.haskeys(): + return obj.asDict() + else: + return [toItem(v) for v in obj] + else: + return obj + + return dict((k,toItem(v)) for k,v in item_fn()) + + 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 ): + """ + (Deprecated) 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): + r""" + Returns the results name for this token expression. Useful when several + different expressions might match at a particular location. + + Example:: + integer = Word(nums) + ssn_expr = Regex(r"\d\d\d-\d\d-\d\d\d\d") + house_number_expr = Suppress('#') + Word(nums, alphanums) + user_data = (Group(house_number_expr)("house_number") + | Group(ssn_expr)("ssn") + | Group(integer)("age")) + user_info = OneOrMore(user_data) + + result = user_info.parseString("22 111-22-3333 #221B") + for item in result: + print(item.getName(), ':', item[0]) + prints:: + age : 22 + ssn : 111-22-3333 + house_number : 221B + """ + 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 + next(iter(self.__tokdict.values()))[0][1] in (0,-1)): + return next(iter(self.__tokdict.keys())) + else: + return None + + def dump(self, indent='', depth=0, full=True): + """ + 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. + + Example:: + integer = Word(nums) + date_str = integer("year") + '/' + integer("month") + '/' + integer("day") + + result = date_str.parseString('12/31/1999') + print(result.dump()) + prints:: + ['12', '/', '31', '/', '1999'] + - day: 1999 + - month: 31 + - year: 12 + """ + out = [] + NL = '\n' + out.append( indent+_ustr(self.asList()) ) + if full: + if self.haskeys(): + items = sorted((str(k), v) for k,v in 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(repr(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}) + + Example:: + ident = Word(alphas, alphanums) + num = Word(nums) + func = Forward() + term = ident | num | Group('(' + func + ')') + func <<= ident + Group(Optional(delimitedList(term))) + result = func.parseString("fna a,b,(fnb c,d,200),100") + result.pprint(width=40) + prints:: + ['fna', + ['a', + 'b', + ['(', 'fnb', ['c', 'd', '200'], ')'], + '100']] + """ + 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 __getnewargs__(self): + return self.__toklist, self.__name, self.__asList, self.__modal + + def __dir__(self): + return (dir(type(self)) + list(self.keys())) + +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 0<loc<len(s) and s[loc-1] == '\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] + + # traceback return data structure changed in Py3.5 - normalize back to plain tuples + if system_version[:2] >= (3,5): + def extract_stack(limit=0): + # special handling for Python 3.5.0 - extra deep call stack by 1 + offset = -3 if system_version == (3,5,0) else -2 + frame_summary = traceback.extract_stack(limit=-offset+limit-1)[offset] + return [frame_summary[:2]] + def extract_tb(tb, limit=0): + frames = traceback.extract_tb(tb, limit=limit) + frame_summary = frames[-1] + return [frame_summary[:2]] + else: + extract_stack = traceback.extract_stack + extract_tb = traceback.extract_tb + + # synthesize what would be returned by traceback.extract_stack at the call to + # user's parse action 'func', so that we don't incur call penalty at parse time + + LINE_DIFF = 6 + # IF ANY CODE CHANGES, EVEN JUST COMMENTS OR BLANK LINES, BETWEEN THE NEXT LINE AND + # THE CALL TO FUNC INSIDE WRAPPER, LINE_DIFF MUST BE MODIFIED!!!! + this_line = extract_stack(limit=2)[-1] + pa_call_line_synth = (this_line[0], this_line[1]+LINE_DIFF) + + def wrapper(*args): + while 1: + try: + ret = func(*args[limit[0]:]) + foundArity[0] = True + return ret + except TypeError: + # re-raise TypeErrors if they did not come from our arity testing + if foundArity[0]: + raise + else: + try: + tb = sys.exc_info()[-1] + if not extract_tb(tb, limit=2)[-1][:2] == pa_call_line_synth: + raise + finally: + del tb + + if limit[0] <= maxargs: + limit[0] += 1 + continue + raise + + # copy func name to wrapper for sensible debug output + func_name = "<parse action>" + try: + func_name = getattr(func, '__name__', + getattr(func, '__class__').__name__) + except Exception: + func_name = str(func) + wrapper.__name__ = func_name + + return wrapper + +class ParserElement(object): + """Abstract base level parser element class.""" + DEFAULT_WHITE_CHARS = " \n\t\r" + verbose_stacktrace = False + + @staticmethod + def setDefaultWhitespaceChars( chars ): + r""" + Overrides the default whitespace chars + + Example:: + # default whitespace chars are space, <TAB> and newline + OneOrMore(Word(alphas)).parseString("abc def\nghi jkl") # -> ['abc', 'def', 'ghi', 'jkl'] + + # change to just treat newline as significant + ParserElement.setDefaultWhitespaceChars(" \t") + OneOrMore(Word(alphas)).parseString("abc def\nghi jkl") # -> ['abc', 'def'] + """ + ParserElement.DEFAULT_WHITE_CHARS = chars + + @staticmethod + def inlineLiteralsUsing(cls): + """ + Set class to be used for inclusion of string literals into a parser. + + Example:: + # default literal class used is Literal + integer = Word(nums) + date_str = integer("year") + '/' + integer("month") + '/' + integer("day") + + date_str.parseString("1999/12/31") # -> ['1999', '/', '12', '/', '31'] + + + # change to Suppress + ParserElement.inlineLiteralsUsing(Suppress) + date_str = integer("year") + '/' + integer("month") + '/' + integer("day") + + date_str.parseString("1999/12/31") # -> ['1999', '12', '31'] + """ + 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. + + Example:: + integer = Word(nums).setParseAction(lambda toks: int(toks[0])) + integerK = integer.copy().addParseAction(lambda toks: toks[0]*1024) + Suppress("K") + integerM = integer.copy().addParseAction(lambda toks: toks[0]*1024*1024) + Suppress("M") + + print(OneOrMore(integerK | integerM | integer).parseString("5K 100 640K 256M")) + prints:: + [5120, 100, 655360, 268435456] + Equivalent form of C{expr.copy()} is just C{expr()}:: + integerM = integer().addParseAction(lambda toks: toks[0]*1024*1024) + Suppress("M") + """ + 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, makes debugging and exception messages clearer. + + Example:: + Word(nums).parseString("ABC") # -> Exception: Expected W:(0123...) (at char 0), (line:1, col:1) + Word(nums).setName("integer").parseString("ABC") # -> Exception: Expected integer (at char 0), (line:1, col:1) + """ + 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__>}. + + Example:: + date_str = (integer.setResultsName("year") + '/' + + integer.setResultsName("month") + '/' + + integer.setResultsName("day")) + + # equivalent form: + date_str = integer("year") + '/' + integer("month") + '/' + integer("day") + """ + 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 one or more actions 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. + + Optional keyword arguments: + - callDuringTry = (default=C{False}) indicate if parse action should be run during lookaheads and alternate testing + + 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. + + Example:: + integer = Word(nums) + date_str = integer + '/' + integer + '/' + integer + + date_str.parseString("1999/12/31") # -> ['1999', '/', '12', '/', '31'] + + # use parse action to convert to ints at parse time + integer = Word(nums).setParseAction(lambda toks: int(toks[0])) + date_str = integer + '/' + integer + '/' + integer + + # note that integer fields are now ints, not strings + date_str.parseString("1999/12/31") # -> [1999, '/', 12, '/', 31] + """ + self.parseAction = list(map(_trim_arity, list(fns))) + self.callDuringTry = kwargs.get("callDuringTry", False) + return self + + def addParseAction( self, *fns, **kwargs ): + """ + Add one or more parse actions to expression's list of parse actions. See L{I{setParseAction}<setParseAction>}. + + See examples in L{I{copy}<copy>}. + """ + 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>} for function call signatures. Unlike C{setParseAction}, + functions passed to C{addCondition} need to return boolean success/fail of the condition. + + Optional keyword arguments: + - message = define a custom message to be used in the raised exception + - fatal = if True, will raise ParseFatalException to stop parsing immediately; otherwise will raise ParseException + + Example:: + integer = Word(nums).setParseAction(lambda toks: int(toks[0])) + year_int = integer.copy() + year_int.addCondition(lambda toks: toks[0] >= 2000, message="Only support years 2000 and later") + date_str = year_int + '/' + integer + '/' + integer + + result = date_str.parseString("1999/12/31") # -> Exception: Only support years 2000 and later (at char 0), (line:1, col:1) + """ + msg = kwargs.get("message", "failed user-defined condition") + exc_type = ParseFatalException if kwargs.get("fatal", False) else ParseException + for fn in fns: + def pa(s,l,t): + if not bool(_trim_arity(fn)(s,l,t)): + raise exc_type(s,l,msg) + 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 preloc >= 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) + + def canParseNext(self, instring, loc): + try: + self.tryParse(instring, loc) + except (ParseException, IndexError): + return False + else: + return True + + class _UnboundedCache(object): + def __init__(self): + cache = {} + self.not_in_cache = not_in_cache = object() + + def get(self, key): + return cache.get(key, not_in_cache) + + def set(self, key, value): + cache[key] = value + + def clear(self): + cache.clear() + + def cache_len(self): + return len(cache) + + self.get = types.MethodType(get, self) + self.set = types.MethodType(set, self) + self.clear = types.MethodType(clear, self) + self.__len__ = types.MethodType(cache_len, self) + + if _OrderedDict is not None: + class _FifoCache(object): + def __init__(self, size): + self.not_in_cache = not_in_cache = object() + + cache = _OrderedDict() + + def get(self, key): + return cache.get(key, not_in_cache) + + def set(self, key, value): + cache[key] = value + while len(cache) > size: + try: + cache.popitem(False) + except KeyError: + pass + + def clear(self): + cache.clear() + + def cache_len(self): + return len(cache) + + self.get = types.MethodType(get, self) + self.set = types.MethodType(set, self) + self.clear = types.MethodType(clear, self) + self.__len__ = types.MethodType(cache_len, self) + + else: + class _FifoCache(object): + def __init__(self, size): + self.not_in_cache = not_in_cache = object() + + cache = {} + key_fifo = collections.deque([], size) + + def get(self, key): + return cache.get(key, not_in_cache) + + def set(self, key, value): + cache[key] = value + while len(key_fifo) > size: + cache.pop(key_fifo.popleft(), None) + key_fifo.append(key) + + def clear(self): + cache.clear() + key_fifo.clear() + + def cache_len(self): + return len(cache) + + self.get = types.MethodType(get, self) + self.set = types.MethodType(set, self) + self.clear = types.MethodType(clear, self) + self.__len__ = types.MethodType(cache_len, self) + + # argument cache for optimizing repeated calls when backtracking through recursive expressions + packrat_cache = {} # this is set later by enabledPackrat(); this is here so that resetCache() doesn't fail + packrat_cache_lock = RLock() + packrat_cache_stats = [0, 0] + + # 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 ): + HIT, MISS = 0, 1 + lookup = (self, instring, loc, callPreParse, doActions) + with ParserElement.packrat_cache_lock: + cache = ParserElement.packrat_cache + value = cache.get(lookup) + if value is cache.not_in_cache: + ParserElement.packrat_cache_stats[MISS] += 1 + try: + value = self._parseNoCache(instring, loc, doActions, callPreParse) + except ParseBaseException as pe: + # cache a copy of the exception, without the traceback + cache.set(lookup, pe.__class__(*pe.args)) + raise + else: + cache.set(lookup, (value[0], value[1].copy())) + return value + else: + ParserElement.packrat_cache_stats[HIT] += 1 + if isinstance(value, Exception): + raise value + return (value[0], value[1].copy()) + + _parse = _parseNoCache + + @staticmethod + def resetCache(): + ParserElement.packrat_cache.clear() + ParserElement.packrat_cache_stats[:] = [0] * len(ParserElement.packrat_cache_stats) + + _packratEnabled = False + @staticmethod + def enablePackrat(cache_size_limit=128): + """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. + + Parameters: + - cache_size_limit - (default=C{128}) - if an integer value is provided + will limit the size of the packrat cache; if None is passed, then + the cache size will be unbounded; if 0 is passed, the cache will + be effectively disabled. + + 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. + + Example:: + import pyparsing + pyparsing.ParserElement.enablePackrat() + """ + if not ParserElement._packratEnabled: + ParserElement._packratEnabled = True + if cache_size_limit is None: + ParserElement.packrat_cache = ParserElement._UnboundedCache() + else: + ParserElement.packrat_cache = ParserElement._FifoCache(cache_size_limit) + 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} + + Example:: + Word('a').parseString('aaaaabaaa') # -> ['aaaaa'] + Word('a').parseString('aaaaabaaa', parseAll=True) # -> Exception: Expected end of text + """ + 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. + + Example:: + source = "sldjf123lsdjjkf345sldkjf879lkjsfd987" + print(source) + for tokens,start,end in Word(alphas).scanString(source): + print(' '*start + '^'*(end-start)) + print(' '*start + tokens[0]) + + prints:: + + sldjf123lsdjjkf345sldkjf879lkjsfd987 + ^^^^^ + sldjf + ^^^^^^^ + lsdjjkf + ^^^^^^ + sldkjf + ^^^^^^ + lkjsfd + """ + 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. + + Example:: + wd = Word(alphas) + wd.setParseAction(lambda toks: toks[0].title()) + + print(wd.transformString("now is the winter of our discontent made glorious summer by this sun of york.")) + Prints:: + Now Is The Winter Of Our Discontent Made Glorious Summer By This Sun Of York. + """ + 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. + + Example:: + # a capitalized word starts with an uppercase letter, followed by zero or more lowercase letters + cap_word = Word(alphas.upper(), alphas.lower()) + + print(cap_word.searchString("More than Iron, more than Lead, more than Gold I need Electricity")) + + # the sum() builtin can be used to merge results into a single ParseResults object + print(sum(cap_word.searchString("More than Iron, more than Lead, more than Gold I need Electricity"))) + prints:: + [['More'], ['Iron'], ['Lead'], ['Gold'], ['I'], ['Electricity']] + ['More', 'Iron', 'Lead', 'Gold', 'I', 'Electricity'] + """ + 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 split(self, instring, maxsplit=_MAX_INT, includeSeparators=False): + """ + Generator method to split a string using the given expression as a separator. + May be called with optional C{maxsplit} argument, to limit the number of splits; + and the optional C{includeSeparators} argument (default=C{False}), if the separating + matching text should be included in the split results. + + Example:: + punc = oneOf(list(".,;:/-!?")) + print(list(punc.split("This, this?, this sentence, is badly punctuated!"))) + prints:: + ['This', ' this', '', ' this sentence', ' is badly punctuated', ''] + """ + splits = 0 + last = 0 + for t,s,e in self.scanString(instring, maxMatches=maxsplit): + yield instring[last:s] + if includeSeparators: + yield t[0] + last = e + yield instring[last:] + + def __add__(self, other ): + """ + Implementation of + operator - returns C{L{And}}. Adding strings to a ParserElement + converts them to L{Literal}s by default. + + Example:: + greet = Word(alphas) + "," + Word(alphas) + "!" + hello = "Hello, World!" + print (hello, "->", greet.parseString(hello)) + Prints:: + Hello, World! -> ['Hello', ',', 'World', '!'] + """ + 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 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=False}. + + 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}}. + + Example:: + # these are equivalent + userdata = Word(alphas).setResultsName("name") + Word(nums+"-").setResultsName("socsecno") + userdata = Word(alphas)("name") + Word(nums+"-")("socsecno") + """ + 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. + + Example:: + patt = OneOrMore(Word(alphas)) + patt.parseString('ablaj /* comment */ lskjd') # -> ['ablaj'] + + patt.ignore(cStyleComment) + patt.parseString('ablaj /* comment */ lskjd') # -> ['ablaj', 'lskjd'] + """ + if isinstance(other, basestring): + other = Suppress(other) + + if isinstance( other, Suppress ): + if other not in self.ignoreExprs: + self.ignoreExprs.append(other) + 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. + + Example:: + wd = Word(alphas).setName("alphaword") + integer = Word(nums).setName("numword") + term = wd | integer + + # turn on debugging for wd + wd.setDebug() + + OneOrMore(term).parseString("abc 123 xyz 890") + + prints:: + Match alphaword at loc 0(1,1) + Matched alphaword -> ['abc'] + Match alphaword at loc 3(1,4) + Exception raised:Expected alphaword (at char 4), (line:1, col:5) + Match alphaword at loc 7(1,8) + Matched alphaword -> ['xyz'] + Match alphaword at loc 11(1,12) + Exception raised:Expected alphaword (at char 12), (line:1, col:13) + Match alphaword at loc 15(1,16) + Exception raised:Expected alphaword (at char 15), (line:1, col:16) + + The output shown is that produced by the default debug actions - custom debug actions can be + specified using L{setDebugActions}. Prior to attempting + to match the C{wd} expression, the debugging message C{"Match <exprname> at loc <n>(<line>,<col>)"} + is shown. Then if the parse succeeds, a C{"Matched"} message is shown, or an C{"Exception raised"} + message is shown. Also note the use of L{setName} to assign a human-readable name to the expression, + which makes debugging and exception messages easier to understand - for instance, the default + name created for the C{Word} expression without calling C{setName} is C{"W:(ABCD...)"}. + """ + 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: + with open(file_or_filename, "r") as f: + file_contents = f.read() + 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 vars(self) == vars(other) + elif isinstance(other, basestring): + return self.matches(other) + 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 matches(self, testString, parseAll=True): + """ + Method for quick testing of a parser against a test string. Good for simple + inline microtests of sub expressions while building up larger parser. + + Parameters: + - testString - to test against this expression for a match + - parseAll - (default=C{True}) - flag to pass to C{L{parseString}} when running tests + + Example:: + expr = Word(nums) + assert expr.matches("100") + """ + try: + self.parseString(_ustr(testString), parseAll=parseAll) + return True + except ParseBaseException: + return False + + def runTests(self, tests, parseAll=True, comment='#', fullDump=True, printResults=True, failureTests=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=C{True}) - flag to pass to C{L{parseString}} when running tests + - comment - (default=C{'#'}) - expression for indicating embedded comments in the test + string; pass None to disable comment filtering + - fullDump - (default=C{True}) - dump results as list followed by results names in nested outline; + if False, only dump nested list + - printResults - (default=C{True}) prints test output to stdout + - failureTests - (default=C{False}) indicates if these tests are expected to fail parsing + + Returns: a (success, results) tuple, where success indicates that all tests succeeded + (or failed if C{failureTests} is True), and the results contain a list of lines of each + test's output + + Example:: + number_expr = pyparsing_common.number.copy() + + result = number_expr.runTests(''' + # unsigned integer + 100 + # negative integer + -100 + # float with scientific notation + 6.02e23 + # integer with scientific notation + 1e-12 + ''') + print("Success" if result[0] else "Failed!") + + result = number_expr.runTests(''' + # stray character + 100Z + # missing leading digit before '.' + -.100 + # too many '.' + 3.14.159 + ''', failureTests=True) + print("Success" if result[0] else "Failed!") + prints:: + # unsigned integer + 100 + [100] + + # negative integer + -100 + [-100] + + # float with scientific notation + 6.02e23 + [6.02e+23] + + # integer with scientific notation + 1e-12 + [1e-12] + + Success + + # stray character + 100Z + ^ + FAIL: Expected end of text (at char 3), (line:1, col:4) + + # missing leading digit before '.' + -.100 + ^ + FAIL: Expected {real number with scientific notation | real number | signed integer} (at char 0), (line:1, col:1) + + # too many '.' + 3.14.159 + ^ + FAIL: Expected end of text (at char 4), (line:1, col:5) + + Success + + Each test string must be on a single line. If you want to test a string that spans multiple + lines, create a test like this:: + + expr.runTest(r"this is a test\\n of strings that spans \\n 3 lines") + + (Note that this is a raw string literal, you must include the leading 'r'.) + """ + if isinstance(tests, basestring): + tests = list(map(str.strip, tests.rstrip().splitlines())) + if isinstance(comment, basestring): + comment = Literal(comment) + allResults = [] + comments = [] + success = True + for t in tests: + if comment is not None and comment.matches(t, False) or comments and not t: + comments.append(t) + continue + if not t: + continue + out = ['\n'.join(comments), t] + comments = [] + try: + t = t.replace(r'\n','\n') + result = self.parseString(t, parseAll=parseAll) + out.append(result.dump(full=fullDump)) + success = success and not failureTests + except ParseBaseException as pe: + fatal = "(FATAL)" if isinstance(pe, ParseFatalException) else "" + if '\n' in t: + out.append(line(pe.loc, t)) + out.append(' '*(col(pe.loc,t)-1) + '^' + fatal) + else: + out.append(' '*pe.loc + '^' + fatal) + out.append("FAIL: " + str(pe)) + success = success and failureTests + result = pe + except Exception as exc: + out.append("FAIL-EXCEPTION: " + str(exc)) + success = success and failureTests + result = exc + + if printResults: + if fullDump: + out.append('') + print('\n'.join(out)) + + allResults.append((t, result)) + + return success, allResults + + +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. + + Example:: + Literal('blah').parseString('blah') # -> ['blah'] + Literal('blah').parseString('blahfooblah') # -> ['blah'] + Literal('blah').parseString('bla') # -> Exception: Expected "blah" + + For case-insensitive matching, use L{CaselessLiteral}. + + For keyword matching (force word break before and after the matched string), + use L{Keyword} or L{CaselessKeyword}. + """ + 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}}: + - C{Literal("if")} will match the leading C{'if'} in C{'ifAndOnlyIf'}. + - C{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}. + + Example:: + Keyword("start").parseString("start") # -> ['start'] + Keyword("start").parseString("starting") # -> Exception + + For case-insensitive matching, use L{CaselessKeyword}. + """ + DEFAULT_KEYWORD_CHARS = alphanums+"_$" + + def __init__( self, matchString, identChars=None, caseless=False ): + super(Keyword,self).__init__() + if identChars is None: + identChars = Keyword.DEFAULT_KEYWORD_CHARS + 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. + + Example:: + OneOrMore(CaselessLiteral("CMD")).parseString("cmd CMD Cmd10") # -> ['CMD', 'CMD', 'CMD'] + + (Contrast with example for L{CaselessKeyword}.) + """ + 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): + """ + Caseless version of L{Keyword}. + + Example:: + OneOrMore(CaselessKeyword("CMD")).parseString("cmd CMD Cmd10") # -> ['CMD', 'CMD'] + + (Contrast with example for L{CaselessLiteral}.) + """ + def __init__( self, matchString, identChars=None ): + 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 CloseMatch(Token): + """ + A variation on L{Literal} which matches "close" matches, that is, + strings with at most 'n' mismatching characters. C{CloseMatch} takes parameters: + - C{match_string} - string to be matched + - C{maxMismatches} - (C{default=1}) maximum number of mismatches allowed to count as a match + + The results from a successful parse will contain the matched text from the input string and the following named results: + - C{mismatches} - a list of the positions within the match_string where mismatches were found + - C{original} - the original match_string used to compare against the input string + + If C{mismatches} is an empty list, then the match was an exact match. + + Example:: + patt = CloseMatch("ATCATCGAATGGA") + patt.parseString("ATCATCGAAXGGA") # -> (['ATCATCGAAXGGA'], {'mismatches': [[9]], 'original': ['ATCATCGAATGGA']}) + patt.parseString("ATCAXCGAAXGGA") # -> Exception: Expected 'ATCATCGAATGGA' (with up to 1 mismatches) (at char 0), (line:1, col:1) + + # exact match + patt.parseString("ATCATCGAATGGA") # -> (['ATCATCGAATGGA'], {'mismatches': [[]], 'original': ['ATCATCGAATGGA']}) + + # close match allowing up to 2 mismatches + patt = CloseMatch("ATCATCGAATGGA", maxMismatches=2) + patt.parseString("ATCAXCGAAXGGA") # -> (['ATCAXCGAAXGGA'], {'mismatches': [[4, 9]], 'original': ['ATCATCGAATGGA']}) + """ + def __init__(self, match_string, maxMismatches=1): + super(CloseMatch,self).__init__() + self.name = match_string + self.match_string = match_string + self.maxMismatches = maxMismatches + self.errmsg = "Expected %r (with up to %d mismatches)" % (self.match_string, self.maxMismatches) + self.mayIndexError = False + self.mayReturnEmpty = False + + def parseImpl( self, instring, loc, doActions=True ): + start = loc + instrlen = len(instring) + maxloc = start + len(self.match_string) + + if maxloc <= instrlen: + match_string = self.match_string + match_stringloc = 0 + mismatches = [] + maxMismatches = self.maxMismatches + + for match_stringloc,s_m in enumerate(zip(instring[loc:maxloc], self.match_string)): + src,mat = s_m + if src != mat: + mismatches.append(match_stringloc) + if len(mismatches) > maxMismatches: + break + else: + loc = match_stringloc + 1 + results = ParseResults([instring[start:loc]]) + results['original'] = self.match_string + results['mismatches'] = mismatches + return loc, results + + 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{excludeChars} 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. + + L{srange} is useful for defining custom character set strings for defining + C{Word} expressions, using range notation from regular expression character sets. + + A common mistake is to use C{Word} to match a specific literal string, as in + C{Word("Address")}. Remember that C{Word} uses the string argument to define + I{sets} of matchable characters. This expression would match "Add", "AAA", + "dAred", or any other word made up of the characters 'A', 'd', 'r', 'e', and 's'. + To match an exact literal string, use L{Literal} or L{Keyword}. + + pyparsing includes helper strings for building Words: + - L{alphas} + - L{nums} + - L{alphanums} + - L{hexnums} + - L{alphas8bit} (alphabetic characters in ASCII range 128-255 - accented, tilded, umlauted, etc.) + - L{punc8bit} (non-alphabetic characters in ASCII range 128-255 - currency, symbols, superscripts, diacriticals, etc.) + - L{printables} (any non-whitespace character) + + Example:: + # a word composed of digits + integer = Word(nums) # equivalent to Word("0123456789") or Word(srange("0-9")) + + # a word with a leading capital, and zero or more lowercase + capital_word = Word(alphas.upper(), alphas.lower()) + + # hostnames are alphanumeric, with leading alpha, and '-' + hostname = Word(alphas, alphanums+'-') + + # roman numeral (not a strict parser, accepts invalid mix of characters) + roman = Word("IVXLCDM") + + # any string of non-whitespace characters, except for ',' + csv_value = Word(printables, excludeChars=",") + """ + 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 Exception: + 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 Exception: + 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): + r""" + 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. + If the given regex contains named groups (defined using C{(?P<name>...)}), these will be preserved as + named parse results. + + Example:: + realnum = Regex(r"[+-]?\d+\.\d*") + date = Regex(r'(?P<year>\d{4})-(?P<month>\d\d?)-(?P<day>\d\d?)') + # ref: http://stackoverflow.com/questions/267399/how-do-you-match-only-valid-roman-numerals-with-a-regular-expression + roman = Regex(r"M{0,4}(CM|CD|D?C{0,3})(XC|XL|L?X{0,3})(IX|IV|V?I{0,3})") + """ + 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 not pattern: + 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 Exception: + pass + + if self.strRepr is None: + self.strRepr = "Re:(%s)" % repr(self.pattern) + + return self.strRepr + + +class QuotedString(Token): + r""" + Token for matching strings that are delimited by quoting characters. + + 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=C{None}) + - escQuote - special quote sequence to escape an embedded quote string (such as SQL's "" to escape an embedded ") (default=C{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) + - convertWhitespaceEscapes - convert escaped whitespace (C{'\t'}, C{'\n'}, etc.) to actual whitespace (default=C{True}) + + Example:: + qs = QuotedString('"') + print(qs.searchString('lsjdf "This is the quote" sldjf')) + complex_qs = QuotedString('{{', endQuoteChar='}}') + print(complex_qs.searchString('lsjdf {{This is the "quote"}} sldjf')) + sql_qs = QuotedString('"', escQuote='""') + print(sql_qs.searchString('lsjdf "This is the quote with ""embedded"" quotes" sldjf')) + prints:: + [['This is the quote']] + [['This is the "quote"']] + [['This is the quote with "embedded" quotes']] + """ + def __init__( self, quoteChar, escChar=None, escQuote=None, multiline=False, unquoteResults=True, endQuoteChar=None, convertWhitespaceEscapes=True): + super(QuotedString,self).__init__() + + # remove white space from quote chars - wont work anyway + quoteChar = quoteChar.strip() + if not quoteChar: + warnings.warn("quoteChar cannot be the empty string",SyntaxWarning,stacklevel=2) + raise SyntaxError() + + if endQuoteChar is None: + endQuoteChar = quoteChar + else: + endQuoteChar = endQuoteChar.strip() + if not endQuoteChar: + 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 + self.convertWhitespaceEscapes = convertWhitespaceEscapes + + 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 whitespace + if '\\' in ret and self.convertWhitespaceEscapes: + ws_map = { + r'\t' : '\t', + r'\n' : '\n', + r'\f' : '\f', + r'\r' : '\r', + } + for wslit,wschar in ws_map.items(): + ret = ret.replace(wslit, wschar) + + # replace escaped characters + if self.escChar: + ret = re.sub(self.escCharReplacePattern, r"\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 Exception: + 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 I{not} in a given set (will + include whitespace in matched characters if not listed in the provided exclusion set - see example). + 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. + + Example:: + # define a comma-separated-value as anything that is not a ',' + csv_value = CharsNotIn(',') + print(delimitedList(csv_value).parseString("dkls,lsdkjf,s12 34,@!#,213")) + prints:: + ['dkls', 'lsdkjf', 's12 34', '@!#', '213'] + """ + 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 Exception: + 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 + + Example:: + + test = '''\ + AAA this line + AAA and this line + AAA but not this one + B AAA and definitely not this one + ''' + + for t in (LineStart() + 'AAA' + restOfLine).searchString(test): + print(t) + + Prints:: + ['AAA', ' this line'] + ['AAA', ' and this line'] + + """ + def __init__( self ): + super(LineStart,self).__init__() + self.errmsg = "Expected start of line" + + def parseImpl( self, instring, loc, doActions=True ): + if col(loc, instring) == 1: + return loc, [] + raise ParseException(instring, loc, self.errmsg, self) + +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 = [ ParserElement._literalStringClass( exprs ) ] + elif isinstance( exprs, Iterable ): + exprs = list(exprs) + # if sequence of strings provided, wrap with Literal + if all(isinstance(expr, basestring) for expr in exprs): + exprs = map(ParserElement._literalStringClass, 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 Exception: + 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 " + _ustr(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. + May also be constructed using the C{'-'} operator, which will suppress backtracking. + + Example:: + integer = Word(nums) + name_expr = OneOrMore(Word(alphas)) + + expr = And([integer("id"),name_expr("name"),integer("age")]) + # more easily written as: + expr = integer("id") + name_expr("name") + integer("age") + """ + + 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._from_exception(pe) + except IndexError: + raise ParseSyntaxException(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 = ParserElement._literalStringClass( 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. + + Example:: + # construct Or using '^' operator + + number = Word(nums) ^ Combine(Word(nums) + '.' + Word(nums)) + print(number.searchString("123 3.1416 789")) + prints:: + [['123'], ['3.1416'], ['789']] + """ + 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. + + Example:: + # construct MatchFirst using '|' operator + + # watch the order of expressions to match + number = Word(nums) | Combine(Word(nums) + '.' + Word(nums)) + print(number.searchString("123 3.1416 789")) # Fail! -> [['123'], ['3'], ['1416'], ['789']] + + # put more selective expression first + number = Combine(Word(nums) + '.' + Word(nums)) | Word(nums) + print(number.searchString("123 3.1416 789")) # Better -> [['123'], ['3.1416'], ['789']] + """ + 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. + + Example:: + color = oneOf("RED ORANGE YELLOW GREEN BLUE PURPLE BLACK WHITE BROWN") + shape_type = oneOf("SQUARE CIRCLE TRIANGLE STAR HEXAGON OCTAGON") + integer = Word(nums) + shape_attr = "shape:" + shape_type("shape") + posn_attr = "posn:" + Group(integer("x") + ',' + integer("y"))("posn") + color_attr = "color:" + color("color") + size_attr = "size:" + integer("size") + + # use Each (using operator '&') to accept attributes in any order + # (shape and posn are required, color and size are optional) + shape_spec = shape_attr & posn_attr & Optional(color_attr) & Optional(size_attr) + + shape_spec.runTests(''' + shape: SQUARE color: BLACK posn: 100, 120 + shape: CIRCLE size: 50 color: BLUE posn: 50,80 + color:GREEN size:20 shape:TRIANGLE posn:20,40 + ''' + ) + prints:: + shape: SQUARE color: BLACK posn: 100, 120 + ['shape:', 'SQUARE', 'color:', 'BLACK', 'posn:', ['100', ',', '120']] + - color: BLACK + - posn: ['100', ',', '120'] + - x: 100 + - y: 120 + - shape: SQUARE + + + shape: CIRCLE size: 50 color: BLUE posn: 50,80 + ['shape:', 'CIRCLE', 'size:', '50', 'color:', 'BLUE', 'posn:', ['50', ',', '80']] + - color: BLUE + - posn: ['50', ',', '80'] + - x: 50 + - y: 80 + - shape: CIRCLE + - size: 50 + + + color: GREEN size: 20 shape: TRIANGLE posn: 20,40 + ['color:', 'GREEN', 'size:', '20', 'shape:', 'TRIANGLE', 'posn:', ['20', ',', '40']] + - color: GREEN + - posn: ['20', ',', '40'] + - x: 20 + - y: 40 + - shape: TRIANGLE + - size: 20 + """ + 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 = sum(resultlist, ParseResults([])) + 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 ): + if issubclass(ParserElement._literalStringClass, Token): + expr = ParserElement._literalStringClass(expr) + else: + expr = ParserElement._literalStringClass(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 Exception: + 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 I{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. + + Example:: + # use FollowedBy to match a label only if it is followed by a ':' + data_word = Word(alphas) + label = data_word + FollowedBy(':') + attr_expr = Group(label + Suppress(':') + OneOrMore(data_word, stopOn=label).setParseAction(' '.join)) + + OneOrMore(attr_expr).parseString("shape: SQUARE color: BLACK posn: upper left").pprint() + prints:: + [['shape', 'SQUARE'], ['color', 'BLACK'], ['posn', 'upper left']] + """ + 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 I{not} advance the parsing position within the input string, it only + verifies that the specified parse expression does I{not} match at the current + position. Also, C{NotAny} does I{not} skip over leading whitespace. C{NotAny} + always returns a null token list. May be constructed using the '~' operator. + + Example:: + + """ + 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 ): + if self.expr.canParseNext(instring, loc): + 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 _MultipleMatch(ParseElementEnhance): + def __init__( self, expr, stopOn=None): + super(_MultipleMatch, self).__init__(expr) + self.saveAsList = True + ender = stopOn + if isinstance(ender, basestring): + ender = ParserElement._literalStringClass(ender) + self.not_ender = ~ender if ender is not None else None + + def parseImpl( self, instring, loc, doActions=True ): + self_expr_parse = self.expr._parse + self_skip_ignorables = self._skipIgnorables + check_ender = self.not_ender is not None + if check_ender: + try_not_ender = self.not_ender.tryParse + + # must be at least one (but first see if we are the stopOn sentinel; + # if so, fail) + if check_ender: + try_not_ender(instring, loc) + loc, tokens = self_expr_parse( instring, loc, doActions, callPreParse=False ) + try: + hasIgnoreExprs = (not not self.ignoreExprs) + while 1: + if check_ender: + try_not_ender(instring, loc) + if hasIgnoreExprs: + preloc = self_skip_ignorables( 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 + +class OneOrMore(_MultipleMatch): + """ + Repetition of one or more of the given expression. + + Parameters: + - expr - expression that must match one or more times + - stopOn - (default=C{None}) - expression for a terminating sentinel + (only required if the sentinel would ordinarily match the repetition + expression) + + Example:: + data_word = Word(alphas) + label = data_word + FollowedBy(':') + attr_expr = Group(label + Suppress(':') + OneOrMore(data_word).setParseAction(' '.join)) + + text = "shape: SQUARE posn: upper left color: BLACK" + OneOrMore(attr_expr).parseString(text).pprint() # Fail! read 'color' as data instead of next label -> [['shape', 'SQUARE color']] + + # use stopOn attribute for OneOrMore to avoid reading label string as part of the data + attr_expr = Group(label + Suppress(':') + OneOrMore(data_word, stopOn=label).setParseAction(' '.join)) + OneOrMore(attr_expr).parseString(text).pprint() # Better -> [['shape', 'SQUARE'], ['posn', 'upper left'], ['color', 'BLACK']] + + # could also be written as + (attr_expr * (1,)).parseString(text).pprint() + """ + + 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(_MultipleMatch): + """ + Optional repetition of zero or more of the given expression. + + Parameters: + - expr - expression that must match zero or more times + - stopOn - (default=C{None}) - expression for a terminating sentinel + (only required if the sentinel would ordinarily match the repetition + expression) + + Example: similar to L{OneOrMore} + """ + def __init__( self, expr, stopOn=None): + super(ZeroOrMore,self).__init__(expr, stopOn=stopOn) + self.mayReturnEmpty = True + + def parseImpl( self, instring, loc, doActions=True ): + try: + return super(ZeroOrMore, self).parseImpl(instring, loc, doActions) + except (ParseException,IndexError): + 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 _NullToken(object): + def __bool__(self): + return False + __nonzero__ = __bool__ + def __str__(self): + return "" + +_optionalNotMatched = _NullToken() +class Optional(ParseElementEnhance): + """ + Optional matching of the given expression. + + Parameters: + - expr - expression that must match zero or more times + - default (optional) - value to be returned if the optional expression is not found. + + Example:: + # US postal code can be a 5-digit zip, plus optional 4-digit qualifier + zip = Combine(Word(nums, exact=5) + Optional('-' + Word(nums, exact=4))) + zip.runTests(''' + # traditional ZIP code + 12345 + + # ZIP+4 form + 12101-0001 + + # invalid ZIP + 98765- + ''') + prints:: + # traditional ZIP code + 12345 + ['12345'] + + # ZIP+4 form + 12101-0001 + ['12101-0001'] + + # invalid ZIP + 98765- + ^ + FAIL: Expected end of text (at char 5), (line:1, col:6) + """ + def __init__( self, expr, default=_optionalNotMatched ): + super(Optional,self).__init__( expr, savelist=False ) + self.saveAsList = self.expr.saveAsList + 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. + + Parameters: + - expr - target expression marking the end of the data to be skipped + - include - (default=C{False}) if True, the target expression is also parsed + (the skipped text and target expression are returned as a 2-element list). + - ignore - (default=C{None}) used to define grammars (typically quoted strings and + comments) that might contain false matches to the target expression + - failOn - (default=C{None}) define expressions that are not allowed to be + included in the skipped test; if found before the target expression is found, + the SkipTo is not a match + + Example:: + report = ''' + Outstanding Issues Report - 1 Jan 2000 + + # | Severity | Description | Days Open + -----+----------+-------------------------------------------+----------- + 101 | Critical | Intermittent system crash | 6 + 94 | Cosmetic | Spelling error on Login ('log|n') | 14 + 79 | Minor | System slow when running too many reports | 47 + ''' + integer = Word(nums) + SEP = Suppress('|') + # use SkipTo to simply match everything up until the next SEP + # - ignore quoted strings, so that a '|' character inside a quoted string does not match + # - parse action will call token.strip() for each matched token, i.e., the description body + string_data = SkipTo(SEP, ignore=quotedString) + string_data.setParseAction(tokenMap(str.strip)) + ticket_expr = (integer("issue_num") + SEP + + string_data("sev") + SEP + + string_data("desc") + SEP + + integer("days_open")) + + for tkt in ticket_expr.searchString(report): + print tkt.dump() + prints:: + ['101', 'Critical', 'Intermittent system crash', '6'] + - days_open: 6 + - desc: Intermittent system crash + - issue_num: 101 + - sev: Critical + ['94', 'Cosmetic', "Spelling error on Login ('log|n')", '14'] + - days_open: 14 + - desc: Spelling error on Login ('log|n') + - issue_num: 94 + - sev: Cosmetic + ['79', 'Minor', 'System slow when running too many reports', '47'] + - days_open: 47 + - desc: System slow when running too many reports + - issue_num: 79 + - sev: Minor + """ + 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 isinstance(failOn, basestring): + self.failOn = ParserElement._literalStringClass(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 + expr_parse = self.expr._parse + self_failOn_canParseNext = self.failOn.canParseNext if self.failOn is not None else None + self_ignoreExpr_tryParse = self.ignoreExpr.tryParse if self.ignoreExpr is not None else None + + tmploc = loc + while tmploc <= instrlen: + if self_failOn_canParseNext is not None: + # break if failOn expression matches + if self_failOn_canParseNext(instring, tmploc): + break + + if self_ignoreExpr_tryParse is not None: + # advance past ignore expressions + while 1: + try: + tmploc = self_ignoreExpr_tryParse(instring, tmploc) + except ParseBaseException: + break + + try: + expr_parse(instring, tmploc, doActions=False, callPreParse=False) + except (ParseException, IndexError): + # no match, advance loc in string + tmploc += 1 + else: + # matched skipto expr, done + break + + else: + # ran off the end of the input string without matching skipto expr, fail + raise ParseException(instring, loc, self.errmsg, self) + + # build up return values + loc = tmploc + skiptext = instring[startloc:loc] + skipresult = ParseResults(skiptext) + + if self.includeMatch: + loc, mat = expr_parse(instring,loc,doActions,callPreParse=False) + skipresult += mat + + return loc, skipresult + +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. + + See L{ParseResults.pprint} for an example of a recursive parser created using + C{Forward}. + """ + 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.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 + return self.__class__.__name__ + ": ..." + + # stubbed out for now - creates awful memory and perf issues + 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 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. + + Example:: + real = Word(nums) + '.' + Word(nums) + print(real.parseString('3.1416')) # -> ['3', '.', '1416'] + # will also erroneously match the following + print(real.parseString('3. 1416')) # -> ['3', '.', '1416'] + + real = Combine(Word(nums) + '.' + Word(nums)) + print(real.parseString('3.1416')) # -> ['3.1416'] + # no match when there are internal spaces + print(real.parseString('3. 1416')) # -> Exception: Expected W:(0123...) + """ + 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. + + Example:: + ident = Word(alphas) + num = Word(nums) + term = ident | num + func = ident + Optional(delimitedList(term)) + print(func.parseString("fn a,b,100")) # -> ['fn', 'a', 'b', '100'] + + func = ident + Group(Optional(delimitedList(term))) + print(func.parseString("fn a,b,100")) # -> ['fn', ['a', 'b', '100']] + """ + 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. + + Example:: + data_word = Word(alphas) + label = data_word + FollowedBy(':') + attr_expr = Group(label + Suppress(':') + OneOrMore(data_word).setParseAction(' '.join)) + + text = "shape: SQUARE posn: upper left color: light blue texture: burlap" + attr_expr = (label + Suppress(':') + OneOrMore(data_word, stopOn=label).setParseAction(' '.join)) + + # print attributes as plain groups + print(OneOrMore(attr_expr).parseString(text).dump()) + + # instead of OneOrMore(expr), parse using Dict(OneOrMore(Group(expr))) - Dict will auto-assign names + result = Dict(OneOrMore(Group(attr_expr))).parseString(text) + print(result.dump()) + + # access named fields as dict entries, or output as dict + print(result['shape']) + print(result.asDict()) + prints:: + ['shape', 'SQUARE', 'posn', 'upper left', 'color', 'light blue', 'texture', 'burlap'] + + [['shape', 'SQUARE'], ['posn', 'upper left'], ['color', 'light blue'], ['texture', 'burlap']] + - color: light blue + - posn: upper left + - shape: SQUARE + - texture: burlap + SQUARE + {'color': 'light blue', 'posn': 'upper left', 'texture': 'burlap', 'shape': 'SQUARE'} + See more examples at L{ParseResults} of accessing fields by results name. + """ + 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. + + Example:: + source = "a, b, c,d" + wd = Word(alphas) + wd_list1 = wd + ZeroOrMore(',' + wd) + print(wd_list1.parseString(source)) + + # often, delimiters that are useful during parsing are just in the + # way afterward - use Suppress to keep them out of the parsed output + wd_list2 = wd + ZeroOrMore(Suppress(',') + wd) + print(wd_list2.parseString(source)) + prints:: + ['a', ',', 'b', ',', 'c', ',', 'd'] + ['a', 'b', 'c', 'd'] + (See also L{delimitedList}.) + """ + 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. + + When the parse action is called, this decorator will print C{">> entering I{method-name}(line:I{current_source_line}, I{parse_location}, I{matched_tokens})".} + When the parse action completes, the decorator will print C{"<<"} followed by the returned value, or any exception that the parse action raised. + + Example:: + wd = Word(alphas) + + @traceParseAction + def remove_duplicate_chars(tokens): + return ''.join(sorted(set(''.join(tokens)))) + + wds = OneOrMore(wd).setParseAction(remove_duplicate_chars) + print(wds.parseString("slkdjs sld sldd sdlf sdljf")) + prints:: + >>entering remove_duplicate_chars(line: 'slkdjs sld sldd sdlf sdljf', 0, (['slkdjs', 'sld', 'sldd', 'sdlf', 'sdljf'], {})) + <<leaving remove_duplicate_chars (ret: 'dfjkls') + ['dfjkls'] + """ + f = _trim_arity(f) + def z(*paArgs): + thisFunc = f.__name__ + s,l,t = paArgs[-3:] + if len(paArgs)>3: + thisFunc = paArgs[0].__class__.__name__ + '.' + thisFunc + sys.stderr.write( ">>entering %s(line: '%s', %d, %r)\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: %r)\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. + + Example:: + delimitedList(Word(alphas)).parseString("aa,bb,cc") # -> ['aa', 'bb', 'cc'] + delimitedList(Word(hexnums), delim=':', combine=True).parseString("AA:BB:CC:DD:EE") # -> ['AA:BB:CC:DD:EE'] + """ + 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. + + If C{intExpr} is specified, it should be a pyparsing expression that produces an integer value. + + Example:: + countedArray(Word(alphas)).parseString('2 ab cd ef') # -> ['ab', 'cd'] + + # in this parser, the leading integer value is given in binary, + # '10' indicating that 2 values are in the array + binaryConstant = Word('01').setParseAction(lambda t: int(t[0], 2)) + countedArray(Word(alphas), intExpr=binaryConstant).parseString('10 ab cd ef') # -> ['ab', 'cd'] + """ + 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 ).setName('(len) ' + _ustr(expr) + '...') + +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 I{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) + rep.setName('(prev) ' + _ustr(expr)) + 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 I{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 I{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) + rep.setName('(prev) ' + _ustr(expr)) + 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 collection of string literals + - caseless - (default=C{False}) - treat all literals as caseless + - useRegex - (default=C{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) + + Example:: + comp_oper = oneOf("< = > <= >= !=") + var = Word(alphas) + number = Word(nums) + term = var | number + comparison_expr = term + comp_oper + term + print(comparison_expr.searchString("B = 12 AA=23 B<=AA AA>12")) + prints:: + [['B', '=', '12'], ['AA', '=', '23'], ['B', '<=', 'AA'], ['AA', '>', '12']] + """ + 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, Iterable): + symbols = list(strs) + else: + warnings.warn("Invalid argument to oneOf, expected string or iterable", + 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) ).setName(' | '.join(symbols)) + else: + return Regex( "|".join(re.escape(sym) for sym in symbols) ).setName(' | '.join(symbols)) + except Exception: + 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).setName(' | '.join(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. + + Example:: + text = "shape: SQUARE posn: upper left color: light blue texture: burlap" + attr_expr = (label + Suppress(':') + OneOrMore(data_word, stopOn=label).setParseAction(' '.join)) + print(OneOrMore(attr_expr).parseString(text).dump()) + + attr_label = label + attr_value = Suppress(':') + OneOrMore(data_word, stopOn=label).setParseAction(' '.join) + + # similar to Dict, but simpler call format + result = dictOf(attr_label, attr_value).parseString(text) + print(result.dump()) + print(result['shape']) + print(result.shape) # object attribute access works too + print(result.asDict()) + prints:: + [['shape', 'SQUARE'], ['posn', 'upper left'], ['color', 'light blue'], ['texture', 'burlap']] + - color: light blue + - posn: upper left + - shape: SQUARE + - texture: burlap + SQUARE + SQUARE + {'color': 'light blue', 'shape': 'SQUARE', 'posn': 'upper left', 'texture': 'burlap'} + """ + 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. By default, returns astring 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. + + Example:: + src = "this is test <b> bold <i>text</i> </b> normal text " + for tag in ("b","i"): + opener,closer = makeHTMLTags(tag) + patt = originalTextFor(opener + SkipTo(closer) + closer) + print(patt.searchString(src)[0]) + prints:: + ['<b> bold <i>text</i> </b>'] + ['<i>text</i>'] + """ + 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): + t[:] = [s[t.pop('_original_start'):t.pop('_original_end')]] + matchExpr.setParseAction(extractText) + matchExpr.ignoreExprs = expr.ignoreExprs + 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}} + + Example:: + wd = Word(alphas) + for match in locatedExpr(wd).searchString("ljsdf123lksdjjf123lkkjj1222"): + print(match) + prints:: + [[0, 'ljsdf', 5]] + [[8, 'lksdjjf', 15]] + [[18, 'lkkjj', 23]] + """ + 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 | CharsNotIn(r'\]', exact=1) +_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 C{\-} or C{\]}) + - an escaped hex character with a leading C{'\x'} (C{\x21}, which is a C{'!'} character) + (C{\0x##} is also supported for backwards compatibility) + - an escaped octal character with a leading C{'\0'} (C{\041}, which is a C{'!'} character) + - a range of any of the above, separated by a dash (C{'a-z'}, etc.) + - any combination of the above (C{'aeiouy'}, C{'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 Exception: + 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>}()}. + + Example:: + num = Word(nums).setParseAction(lambda toks: int(toks[0])) + na = oneOf("N/A NA").setParseAction(replaceWith(math.nan)) + term = na | num + + OneOrMore(term).parseString("324 234 N/A 234") # -> [324, 234, nan, 234] + """ + return lambda s,l,t: [replStr] + +def removeQuotes(s,l,t): + """ + Helper parse action for removing quotation marks from parsed quoted strings. + + Example:: + # by default, quotation marks are included in parsed results + quotedString.parseString("'Now is the Winter of our Discontent'") # -> ["'Now is the Winter of our Discontent'"] + + # use removeQuotes to strip quotation marks from parsed results + quotedString.setParseAction(removeQuotes) + quotedString.parseString("'Now is the Winter of our Discontent'") # -> ["Now is the Winter of our Discontent"] + """ + return t[0][1:-1] + +def tokenMap(func, *args): + """ + Helper to define a parse action by mapping a function to all elements of a ParseResults list.If any additional + args are passed, they are forwarded to the given function as additional arguments after + the token, as in C{hex_integer = Word(hexnums).setParseAction(tokenMap(int, 16))}, which will convert the + parsed data to an integer using base 16. + + Example (compare the last to example in L{ParserElement.transformString}:: + hex_ints = OneOrMore(Word(hexnums)).setParseAction(tokenMap(int, 16)) + hex_ints.runTests(''' + 00 11 22 aa FF 0a 0d 1a + ''') + + upperword = Word(alphas).setParseAction(tokenMap(str.upper)) + OneOrMore(upperword).runTests(''' + my kingdom for a horse + ''') + + wd = Word(alphas).setParseAction(tokenMap(str.title)) + OneOrMore(wd).setParseAction(' '.join).runTests(''' + now is the winter of our discontent made glorious summer by this sun of york + ''') + prints:: + 00 11 22 aa FF 0a 0d 1a + [0, 17, 34, 170, 255, 10, 13, 26] + + my kingdom for a horse + ['MY', 'KINGDOM', 'FOR', 'A', 'HORSE'] + + now is the winter of our discontent made glorious summer by this sun of york + ['Now Is The Winter Of Our Discontent Made Glorious Summer By This Sun Of York'] + """ + def pa(s,l,t): + return [func(tokn, *args) for tokn in t] + + try: + func_name = getattr(func, '__name__', + getattr(func, '__class__').__name__) + except Exception: + func_name = str(func) + pa.__name__ = func_name + + return pa + +upcaseTokens = tokenMap(lambda t: _ustr(t).upper()) +"""(Deprecated) Helper parse action to convert tokens to upper case. Deprecated in favor of L{pyparsing_common.upcaseTokens}""" + +downcaseTokens = tokenMap(lambda t: _ustr(t).lower()) +"""(Deprecated) Helper parse action to convert tokens to lower case. Deprecated in favor of L{pyparsing_common.downcaseTokens}""" + +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>" % resname) + closeTag = closeTag.setResultsName("end"+"".join(resname.replace(":"," ").title().split())).setName("</%s>" % resname) + 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. Matches + tags in either upper or lower case, attributes with namespaces and with quoted or unquoted values. + + Example:: + text = '<td>More info at the <a href="http://pyparsing.wikispaces.com">pyparsing</a> wiki page</td>' + # makeHTMLTags returns pyparsing expressions for the opening and closing tags as a 2-tuple + a,a_end = makeHTMLTags("A") + link_expr = a + SkipTo(a_end)("link_text") + a_end + + for link in link_expr.searchString(text): + # attributes in the <A> tag (like "href" shown here) are also accessible as named results + print(link.link_text, '->', link.href) + prints:: + pyparsing -> http://pyparsing.wikispaces.com + """ + return _makeTags( tagStr, False ) + +def makeXMLTags(tagStr): + """ + Helper to construct opening and closing tag expressions for XML, given a tag name. Matches + tags only in the given upper/lower case. + + Example: similar to L{makeHTMLTags} + """ + 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. + + Example:: + html = ''' + <div> + Some text + <div type="grid">1 4 0 1 0</div> + <div type="graph">1,3 2,3 1,1</div> + <div>this has no type</div> + </div> + + ''' + div,div_end = makeHTMLTags("div") + + # only match div tag having a type attribute with value "grid" + div_grid = div().setParseAction(withAttribute(type="grid")) + grid_expr = div_grid + SkipTo(div | div_end)("body") + for grid_header in grid_expr.searchString(html): + print(grid_header.body) + + # construct a match with any div tag having a type attribute, regardless of the value + div_any_type = div().setParseAction(withAttribute(type=withAttribute.ANY_VALUE)) + div_expr = div_any_type + SkipTo(div | div_end)("body") + for div_header in div_expr.searchString(html): + print(div_header.body) + prints:: + 1 4 0 1 0 + + 1 4 0 1 0 + 1,3 2,3 1,1 + """ + 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. + + Example:: + html = ''' + <div> + Some text + <div class="grid">1 4 0 1 0</div> + <div class="graph">1,3 2,3 1,1</div> + <div>this <div> has no class</div> + </div> + + ''' + div,div_end = makeHTMLTags("div") + div_grid = div().setParseAction(withClass("grid")) + + grid_expr = div_grid + SkipTo(div | div_end)("body") + for grid_header in grid_expr.searchString(html): + print(grid_header.body) + + div_any_type = div().setParseAction(withClass(withAttribute.ANY_VALUE)) + div_expr = div_any_type + SkipTo(div | div_end)("body") + for div_header in div_expr.searchString(html): + print(div_header.body) + prints:: + 1 4 0 1 0 + + 1 4 0 1 0 + 1,3 2,3 1,1 + """ + 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. The generated parser will also recognize the use + of parentheses to override operator precedences (see example below). + + Note: if you define a deep operator list, you may see performance issues + when using infixNotation. See L{ParserElement.enablePackrat} for a + mechanism to potentially improve your parser performance. + + 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); if the parse action + is passed a tuple or list of functions, this is equivalent to + calling C{setParseAction(*fn)} (L{ParserElement.setParseAction}) + - lpar - expression for matching left-parentheses (default=C{Suppress('(')}) + - rpar - expression for matching right-parentheses (default=C{Suppress(')')}) + + Example:: + # simple example of four-function arithmetic with ints and variable names + integer = pyparsing_common.signed_integer + varname = pyparsing_common.identifier + + arith_expr = infixNotation(integer | varname, + [ + ('-', 1, opAssoc.RIGHT), + (oneOf('* /'), 2, opAssoc.LEFT), + (oneOf('+ -'), 2, opAssoc.LEFT), + ]) + + arith_expr.runTests(''' + 5+3*6 + (5+3)*6 + -2--11 + ''', fullDump=False) + prints:: + 5+3*6 + [[5, '+', [3, '*', 6]]] + + (5+3)*6 + [[[5, '+', 3], '*', 6]] + + -2--11 + [[['-', 2], '-', ['-', 11]]] + """ + ret = Forward() + lastExpr = baseExpr | ( lpar + ret + rpar ) + for i,operDef in enumerate(opList): + opExpr,arity,rightLeftAssoc,pa = (operDef + (None,))[:4] + termName = "%s term" % opExpr if arity < 3 else "%s%s term" % opExpr + 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(termName) + 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: + if isinstance(pa, (tuple, list)): + matchExpr.setParseAction(*pa) + else: + matchExpr.setParseAction(pa) + thisExpr <<= ( matchExpr.setName(termName) | lastExpr ) + lastExpr = thisExpr + ret <<= lastExpr + return ret + +operatorPrecedence = infixNotation +"""(Deprecated) Former name of C{L{infixNotation}}, will be dropped in a future release.""" + +dblQuotedString = Combine(Regex(r'"(?:[^"\n\r\\]|(?:"")|(?:\\(?:[^x]|x[0-9a-fA-F]+)))*')+'"').setName("string enclosed in double quotes") +sglQuotedString = Combine(Regex(r"'(?:[^'\n\r\\]|(?:'')|(?:\\(?:[^x]|x[0-9a-fA-F]+)))*")+"'").setName("string enclosed in single quotes") +quotedString = Combine(Regex(r'"(?:[^"\n\r\\]|(?:"")|(?:\\(?:[^x]|x[0-9a-fA-F]+)))*')+'"'| + Regex(r"'(?:[^'\n\r\\]|(?:'')|(?:\\(?:[^x]|x[0-9a-fA-F]+)))*")+"'").setName("quotedString using single or double quotes") +unicodeString = Combine(_L('u') + quotedString.copy()).setName("unicode string literal") + +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=C{"("}); can also be a pyparsing expression + - closer - closing character for a nested list (default=C{")"}); can also be a pyparsing expression + - content - expression for items within the nested lists (default=C{None}) + - ignoreExpr - expression for ignoring opening and closing delimiters (default=C{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. + + Example:: + data_type = oneOf("void int short long char float double") + decl_data_type = Combine(data_type + Optional(Word('*'))) + ident = Word(alphas+'_', alphanums+'_') + number = pyparsing_common.number + arg = Group(decl_data_type + ident) + LPAR,RPAR = map(Suppress, "()") + + code_body = nestedExpr('{', '}', ignoreExpr=(quotedString | cStyleComment)) + + c_function = (decl_data_type("type") + + ident("name") + + LPAR + Optional(delimitedList(arg), [])("args") + RPAR + + code_body("body")) + c_function.ignore(cStyleComment) + + source_code = ''' + int is_odd(int x) { + return (x%2); + } + + int dec_to_hex(char hchar) { + if (hchar >= '0' && hchar <= '9') { + return (ord(hchar)-ord('0')); + } else { + return (10+ord(hchar)-ord('A')); + } + } + ''' + for func in c_function.searchString(source_code): + print("%(name)s (%(type)s) args: %(args)s" % func) + + prints:: + is_odd (int) args: [['int', 'x']] + dec_to_hex (int) args: [['char', 'hchar']] + """ + 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) ) + ret.setName('nested %s%s expression' % (opener,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=C{True}) + + A valid block must contain at least one C{blockStatement}. + + Example:: + data = ''' + def A(z): + A1 + B = 100 + G = A2 + A2 + A3 + B + def BB(a,b,c): + BB1 + def BBA(): + bba1 + bba2 + bba3 + C + D + def spam(x,y): + def eggs(z): + pass + ''' + + + indentStack = [1] + stmt = Forward() + + identifier = Word(alphas, alphanums) + funcDecl = ("def" + identifier + Group( "(" + Optional( delimitedList(identifier) ) + ")" ) + ":") + func_body = indentedBlock(stmt, indentStack) + funcDef = Group( funcDecl + func_body ) + + rvalue = Forward() + funcCall = Group(identifier + "(" + Optional(delimitedList(rvalue)) + ")") + rvalue << (funcCall | identifier | Word(nums)) + assignment = Group(identifier + "=" + rvalue) + stmt << ( funcDef | assignment | identifier ) + + module_body = OneOrMore(stmt) + + parseTree = module_body.parseString(data) + parseTree.pprint() + prints:: + [['def', + 'A', + ['(', 'z', ')'], + ':', + [['A1'], [['B', '=', '100']], [['G', '=', 'A2']], ['A2'], ['A3']]], + 'B', + ['def', + 'BB', + ['(', 'a', 'b', 'c', ')'], + ':', + [['BB1'], [['def', 'BBA', ['(', ')'], ':', [['bba1'], ['bba2'], ['bba3']]]]]], + 'C', + 'D', + ['def', + 'spam', + ['(', 'x', 'y', ')'], + ':', + [[['def', 'eggs', ['(', 'z', ')'], ':', [['pass']]]]]]] + """ + 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)).setName('INDENT') + PEER = Empty().setParseAction(checkPeerIndent).setName('') + UNDENT = Empty().setParseAction(checkUnindent).setName('UNINDENT') + 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.setName('indented block') + +alphas8bit = srange(r"[\0xc0-\0xd6\0xd8-\0xf6\0xf8-\0xff]") +punc8bit = srange(r"[\0xa1-\0xbf\0xd7\0xf7]") + +anyOpenTag,anyCloseTag = makeHTMLTags(Word(alphas,alphanums+"_:").setName('any tag')) +_htmlEntityMap = dict(zip("gt lt amp nbsp quot apos".split(),'><& "\'')) +commonHTMLEntity = Regex('&(?P<entity>' + '|'.join(_htmlEntityMap.keys()) +");").setName("common HTML entity") +def replaceHTMLEntity(t): + """Helper parser action to replace common HTML entities with their special characters""" + return _htmlEntityMap.get(t.entity) + +# it's easy to get these comment structures wrong - they're very common, so may as well make them available +cStyleComment = Combine(Regex(r"/\*(?:[^*]|\*(?!/))*") + '*/').setName("C style comment") +"Comment of the form C{/* ... */}" + +htmlComment = Regex(r"<!--[\s\S]*?-->").setName("HTML comment") +"Comment of the form C{<!-- ... -->}" + +restOfLine = Regex(r".*").leaveWhitespace().setName("rest of line") +dblSlashComment = Regex(r"//(?:\\\n|[^\n])*").setName("// comment") +"Comment of the form C{// ... (to end of line)}" + +cppStyleComment = Combine(Regex(r"/\*(?:[^*]|\*(?!/))*") + '*/'| dblSlashComment).setName("C++ style comment") +"Comment of either form C{L{cStyleComment}} or C{L{dblSlashComment}}" + +javaStyleComment = cppStyleComment +"Same as C{L{cppStyleComment}}" + +pythonStyleComment = Regex(r"#.*").setName("Python style comment") +"Comment of the form C{# ... (to end of line)}" + +_commasepitem = Combine(OneOrMore(Word(printables, excludeChars=',') + + Optional( Word(" \t") + + ~Literal(",") + ~LineEnd() ) ) ).streamline().setName("commaItem") +commaSeparatedList = delimitedList( Optional( quotedString.copy() | _commasepitem, default="") ).setName("commaSeparatedList") +"""(Deprecated) Predefined expression of 1 or more printable words or quoted strings, separated by commas. + This expression is deprecated in favor of L{pyparsing_common.comma_separated_list}.""" + +# some other useful expressions - using lower-case class name since we are really using this as a namespace +class pyparsing_common: + """ + Here are some common low-level expressions that may be useful in jump-starting parser development: + - numeric forms (L{integers<integer>}, L{reals<real>}, L{scientific notation<sci_real>}) + - common L{programming identifiers<identifier>} + - network addresses (L{MAC<mac_address>}, L{IPv4<ipv4_address>}, L{IPv6<ipv6_address>}) + - ISO8601 L{dates<iso8601_date>} and L{datetime<iso8601_datetime>} + - L{UUID<uuid>} + - L{comma-separated list<comma_separated_list>} + Parse actions: + - C{L{convertToInteger}} + - C{L{convertToFloat}} + - C{L{convertToDate}} + - C{L{convertToDatetime}} + - C{L{stripHTMLTags}} + - C{L{upcaseTokens}} + - C{L{downcaseTokens}} + + Example:: + pyparsing_common.number.runTests(''' + # any int or real number, returned as the appropriate type + 100 + -100 + +100 + 3.14159 + 6.02e23 + 1e-12 + ''') + + pyparsing_common.fnumber.runTests(''' + # any int or real number, returned as float + 100 + -100 + +100 + 3.14159 + 6.02e23 + 1e-12 + ''') + + pyparsing_common.hex_integer.runTests(''' + # hex numbers + 100 + FF + ''') + + pyparsing_common.fraction.runTests(''' + # fractions + 1/2 + -3/4 + ''') + + pyparsing_common.mixed_integer.runTests(''' + # mixed fractions + 1 + 1/2 + -3/4 + 1-3/4 + ''') + + import uuid + pyparsing_common.uuid.setParseAction(tokenMap(uuid.UUID)) + pyparsing_common.uuid.runTests(''' + # uuid + 12345678-1234-5678-1234-567812345678 + ''') + prints:: + # any int or real number, returned as the appropriate type + 100 + [100] + + -100 + [-100] + + +100 + [100] + + 3.14159 + [3.14159] + + 6.02e23 + [6.02e+23] + + 1e-12 + [1e-12] + + # any int or real number, returned as float + 100 + [100.0] + + -100 + [-100.0] + + +100 + [100.0] + + 3.14159 + [3.14159] + + 6.02e23 + [6.02e+23] + + 1e-12 + [1e-12] + + # hex numbers + 100 + [256] + + FF + [255] + + # fractions + 1/2 + [0.5] + + -3/4 + [-0.75] + + # mixed fractions + 1 + [1] + + 1/2 + [0.5] + + -3/4 + [-0.75] + + 1-3/4 + [1.75] + + # uuid + 12345678-1234-5678-1234-567812345678 + [UUID('12345678-1234-5678-1234-567812345678')] + """ + + convertToInteger = tokenMap(int) + """ + Parse action for converting parsed integers to Python int + """ + + convertToFloat = tokenMap(float) + """ + Parse action for converting parsed numbers to Python float + """ + + integer = Word(nums).setName("integer").setParseAction(convertToInteger) + """expression that parses an unsigned integer, returns an int""" + + hex_integer = Word(hexnums).setName("hex integer").setParseAction(tokenMap(int,16)) + """expression that parses a hexadecimal integer, returns an int""" + + signed_integer = Regex(r'[+-]?\d+').setName("signed integer").setParseAction(convertToInteger) + """expression that parses an integer with optional leading sign, returns an int""" + + fraction = (signed_integer().setParseAction(convertToFloat) + '/' + signed_integer().setParseAction(convertToFloat)).setName("fraction") + """fractional expression of an integer divided by an integer, returns a float""" + fraction.addParseAction(lambda t: t[0]/t[-1]) + + mixed_integer = (fraction | signed_integer + Optional(Optional('-').suppress() + fraction)).setName("fraction or mixed integer-fraction") + """mixed integer of the form 'integer - fraction', with optional leading integer, returns float""" + mixed_integer.addParseAction(sum) + + real = Regex(r'[+-]?\d+\.\d*').setName("real number").setParseAction(convertToFloat) + """expression that parses a floating point number and returns a float""" + + sci_real = Regex(r'[+-]?\d+([eE][+-]?\d+|\.\d*([eE][+-]?\d+)?)').setName("real number with scientific notation").setParseAction(convertToFloat) + """expression that parses a floating point number with optional scientific notation and returns a float""" + + # streamlining this expression makes the docs nicer-looking + number = (sci_real | real | signed_integer).streamline() + """any numeric expression, returns the corresponding Python type""" + + fnumber = Regex(r'[+-]?\d+\.?\d*([eE][+-]?\d+)?').setName("fnumber").setParseAction(convertToFloat) + """any int or real number, returned as float""" + + identifier = Word(alphas+'_', alphanums+'_').setName("identifier") + """typical code identifier (leading alpha or '_', followed by 0 or more alphas, nums, or '_')""" + + ipv4_address = Regex(r'(25[0-5]|2[0-4][0-9]|1?[0-9]{1,2})(\.(25[0-5]|2[0-4][0-9]|1?[0-9]{1,2})){3}').setName("IPv4 address") + "IPv4 address (C{0.0.0.0 - 255.255.255.255})" + + _ipv6_part = Regex(r'[0-9a-fA-F]{1,4}').setName("hex_integer") + _full_ipv6_address = (_ipv6_part + (':' + _ipv6_part)*7).setName("full IPv6 address") + _short_ipv6_address = (Optional(_ipv6_part + (':' + _ipv6_part)*(0,6)) + "::" + Optional(_ipv6_part + (':' + _ipv6_part)*(0,6))).setName("short IPv6 address") + _short_ipv6_address.addCondition(lambda t: sum(1 for tt in t if pyparsing_common._ipv6_part.matches(tt)) < 8) + _mixed_ipv6_address = ("::ffff:" + ipv4_address).setName("mixed IPv6 address") + ipv6_address = Combine((_full_ipv6_address | _mixed_ipv6_address | _short_ipv6_address).setName("IPv6 address")).setName("IPv6 address") + "IPv6 address (long, short, or mixed form)" + + mac_address = Regex(r'[0-9a-fA-F]{2}([:.-])[0-9a-fA-F]{2}(?:\1[0-9a-fA-F]{2}){4}').setName("MAC address") + "MAC address xx:xx:xx:xx:xx (may also have '-' or '.' delimiters)" + + @staticmethod + def convertToDate(fmt="%Y-%m-%d"): + """ + Helper to create a parse action for converting parsed date string to Python datetime.date + + Params - + - fmt - format to be passed to datetime.strptime (default=C{"%Y-%m-%d"}) + + Example:: + date_expr = pyparsing_common.iso8601_date.copy() + date_expr.setParseAction(pyparsing_common.convertToDate()) + print(date_expr.parseString("1999-12-31")) + prints:: + [datetime.date(1999, 12, 31)] + """ + def cvt_fn(s,l,t): + try: + return datetime.strptime(t[0], fmt).date() + except ValueError as ve: + raise ParseException(s, l, str(ve)) + return cvt_fn + + @staticmethod + def convertToDatetime(fmt="%Y-%m-%dT%H:%M:%S.%f"): + """ + Helper to create a parse action for converting parsed datetime string to Python datetime.datetime + + Params - + - fmt - format to be passed to datetime.strptime (default=C{"%Y-%m-%dT%H:%M:%S.%f"}) + + Example:: + dt_expr = pyparsing_common.iso8601_datetime.copy() + dt_expr.setParseAction(pyparsing_common.convertToDatetime()) + print(dt_expr.parseString("1999-12-31T23:59:59.999")) + prints:: + [datetime.datetime(1999, 12, 31, 23, 59, 59, 999000)] + """ + def cvt_fn(s,l,t): + try: + return datetime.strptime(t[0], fmt) + except ValueError as ve: + raise ParseException(s, l, str(ve)) + return cvt_fn + + iso8601_date = Regex(r'(?P<year>\d{4})(?:-(?P<month>\d\d)(?:-(?P<day>\d\d))?)?').setName("ISO8601 date") + "ISO8601 date (C{yyyy-mm-dd})" + + iso8601_datetime = Regex(r'(?P<year>\d{4})-(?P<month>\d\d)-(?P<day>\d\d)[T ](?P<hour>\d\d):(?P<minute>\d\d)(:(?P<second>\d\d(\.\d*)?)?)?(?P<tz>Z|[+-]\d\d:?\d\d)?').setName("ISO8601 datetime") + "ISO8601 datetime (C{yyyy-mm-ddThh:mm:ss.s(Z|+-00:00)}) - trailing seconds, milliseconds, and timezone optional; accepts separating C{'T'} or C{' '}" + + uuid = Regex(r'[0-9a-fA-F]{8}(-[0-9a-fA-F]{4}){3}-[0-9a-fA-F]{12}').setName("UUID") + "UUID (C{xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx})" + + _html_stripper = anyOpenTag.suppress() | anyCloseTag.suppress() + @staticmethod + def stripHTMLTags(s, l, tokens): + """ + Parse action to remove HTML tags from web page HTML source + + Example:: + # strip HTML links from normal text + text = '<td>More info at the <a href="http://pyparsing.wikispaces.com">pyparsing</a> wiki page</td>' + td,td_end = makeHTMLTags("TD") + table_text = td + SkipTo(td_end).setParseAction(pyparsing_common.stripHTMLTags)("body") + td_end + + print(table_text.parseString(text).body) # -> 'More info at the pyparsing wiki page' + """ + return pyparsing_common._html_stripper.transformString(tokens[0]) + + _commasepitem = Combine(OneOrMore(~Literal(",") + ~LineEnd() + Word(printables, excludeChars=',') + + Optional( White(" \t") ) ) ).streamline().setName("commaItem") + comma_separated_list = delimitedList( Optional( quotedString.copy() | _commasepitem, default="") ).setName("comma separated list") + """Predefined expression of 1 or more printable words or quoted strings, separated by commas.""" + + upcaseTokens = staticmethod(tokenMap(lambda t: _ustr(t).upper())) + """Parse action to convert tokens to upper case.""" + + downcaseTokens = staticmethod(tokenMap(lambda t: _ustr(t).lower())) + """Parse action to convert tokens to lower case.""" + + +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") + columnSpec = ('*' | columnNameList) + + tableName = delimitedList(ident, ".", combine=True).setParseAction(upcaseTokens) + tableNameList = Group(delimitedList(tableName)).setName("tables") + + simpleSQL = selectToken("command") + columnSpec("columns") + fromToken + tableNameList("tables") + + # demo runTests method, including embedded comments in test string + simpleSQL.runTests(""" + # '*' as column list and dotted table name + select * from SYS.XYZZY + + # caseless match on "SELECT", and casts back to "select" + SELECT * from XYZZY, ABC + + # list of column names, and mixed case SELECT keyword + Select AA,BB,CC from Sys.dual + + # multiple tables + Select A, B, C from Sys.dual, Table2 + + # invalid SELECT keyword - should fail + Xelect A, B, C from Sys.dual + + # incomplete command - should fail + Select + + # invalid column name - should fail + Select ^^^ frox Sys.dual + + """) + + pyparsing_common.number.runTests(""" + 100 + -100 + +100 + 3.14159 + 6.02e23 + 1e-12 + """) + + # any int or real number, returned as float + pyparsing_common.fnumber.runTests(""" + 100 + -100 + +100 + 3.14159 + 6.02e23 + 1e-12 + """) + + pyparsing_common.hex_integer.runTests(""" + 100 + FF + """) + + import uuid + pyparsing_common.uuid.setParseAction(tokenMap(uuid.UUID)) + pyparsing_common.uuid.runTests(""" + 12345678-1234-5678-1234-567812345678 + """) diff --git a/venv2/lib/python2.7/site-packages/setuptools/_vendor/six.py b/venv2/lib/python2.7/site-packages/setuptools/_vendor/six.py new file mode 100644 index 0000000..190c023 --- /dev/null +++ b/venv2/lib/python2.7/site-packages/setuptools/_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/venv2/lib/python2.7/site-packages/setuptools/archive_util.py b/venv2/lib/python2.7/site-packages/setuptools/archive_util.py new file mode 100644 index 0000000..8143604 --- /dev/null +++ b/venv2/lib/python2.7/site-packages/setuptools/archive_util.py @@ -0,0 +1,173 @@ +"""Utilities for extracting common archive formats""" + +import zipfile +import tarfile +import os +import shutil +import posixpath +import contextlib +from distutils.errors import DistutilsError + +from pkg_resources import ensure_directory + +__all__ = [ + "unpack_archive", "unpack_zipfile", "unpack_tarfile", "default_filter", + "UnrecognizedFormat", "extraction_drivers", "unpack_directory", +] + + +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 zipfile.ZipFile(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/venv2/lib/python2.7/site-packages/setuptools/build_meta.py b/venv2/lib/python2.7/site-packages/setuptools/build_meta.py new file mode 100644 index 0000000..e40904a --- /dev/null +++ b/venv2/lib/python2.7/site-packages/setuptools/build_meta.py @@ -0,0 +1,254 @@ +"""A PEP 517 interface to setuptools + +Previously, when a user or a command line tool (let's call it a "frontend") +needed to make a request of setuptools to take a certain action, for +example, generating a list of installation requirements, the frontend would +would call "setup.py egg_info" or "setup.py bdist_wheel" on the command line. + +PEP 517 defines a different method of interfacing with setuptools. Rather +than calling "setup.py" directly, the frontend should: + + 1. Set the current directory to the directory with a setup.py file + 2. Import this module into a safe python interpreter (one in which + setuptools can potentially set global variables or crash hard). + 3. Call one of the functions defined in PEP 517. + +What each function does is defined in PEP 517. However, here is a "casual" +definition of the functions (this definition should not be relied on for +bug reports or API stability): + + - `build_wheel`: build a wheel in the folder and return the basename + - `get_requires_for_build_wheel`: get the `setup_requires` to build + - `prepare_metadata_for_build_wheel`: get the `install_requires` + - `build_sdist`: build an sdist in the folder and return the basename + - `get_requires_for_build_sdist`: get the `setup_requires` to build + +Again, this is not a formal definition! Just a "taste" of the module. +""" + +import io +import os +import sys +import tokenize +import shutil +import contextlib + +import setuptools +import distutils +from setuptools.py31compat import TemporaryDirectory + +from pkg_resources import parse_requirements + +__all__ = ['get_requires_for_build_sdist', + 'get_requires_for_build_wheel', + 'prepare_metadata_for_build_wheel', + 'build_wheel', + 'build_sdist', + '__legacy__', + 'SetupRequirementsError'] + +class SetupRequirementsError(BaseException): + def __init__(self, specifiers): + self.specifiers = specifiers + + +class Distribution(setuptools.dist.Distribution): + def fetch_build_eggs(self, specifiers): + specifier_list = list(map(str, parse_requirements(specifiers))) + + raise SetupRequirementsError(specifier_list) + + @classmethod + @contextlib.contextmanager + def patch(cls): + """ + Replace + distutils.dist.Distribution with this class + for the duration of this context. + """ + orig = distutils.core.Distribution + distutils.core.Distribution = cls + try: + yield + finally: + distutils.core.Distribution = orig + + +def _to_str(s): + """ + Convert a filename to a string (on Python 2, explicitly + a byte string, not Unicode) as distutils checks for the + exact type str. + """ + if sys.version_info[0] == 2 and not isinstance(s, str): + # Assume it's Unicode, as that's what the PEP says + # should be provided. + return s.encode(sys.getfilesystemencoding()) + return s + + +def _get_immediate_subdirectories(a_dir): + return [name for name in os.listdir(a_dir) + if os.path.isdir(os.path.join(a_dir, name))] + + +def _file_with_extension(directory, extension): + matching = ( + f for f in os.listdir(directory) + if f.endswith(extension) + ) + file, = matching + return file + + +def _open_setup_script(setup_script): + if not os.path.exists(setup_script): + # Supply a default setup.py + return io.StringIO(u"from setuptools import setup; setup()") + + return getattr(tokenize, 'open', open)(setup_script) + + +class _BuildMetaBackend(object): + + def _fix_config(self, config_settings): + config_settings = config_settings or {} + config_settings.setdefault('--global-option', []) + return config_settings + + def _get_build_requires(self, config_settings, requirements): + config_settings = self._fix_config(config_settings) + + sys.argv = sys.argv[:1] + ['egg_info'] + \ + config_settings["--global-option"] + try: + with Distribution.patch(): + self.run_setup() + except SetupRequirementsError as e: + requirements += e.specifiers + + return requirements + + def run_setup(self, setup_script='setup.py'): + # Note that we can reuse our build directory between calls + # Correctness comes first, then optimization later + __file__ = setup_script + __name__ = '__main__' + + with _open_setup_script(__file__) as f: + code = f.read().replace(r'\r\n', r'\n') + + exec(compile(code, __file__, 'exec'), locals()) + + def get_requires_for_build_wheel(self, config_settings=None): + config_settings = self._fix_config(config_settings) + return self._get_build_requires(config_settings, requirements=['wheel']) + + def get_requires_for_build_sdist(self, config_settings=None): + config_settings = self._fix_config(config_settings) + return self._get_build_requires(config_settings, requirements=[]) + + def prepare_metadata_for_build_wheel(self, metadata_directory, + config_settings=None): + sys.argv = sys.argv[:1] + ['dist_info', '--egg-base', + _to_str(metadata_directory)] + self.run_setup() + + dist_info_directory = metadata_directory + while True: + dist_infos = [f for f in os.listdir(dist_info_directory) + if f.endswith('.dist-info')] + + if (len(dist_infos) == 0 and + len(_get_immediate_subdirectories(dist_info_directory)) == 1): + + dist_info_directory = os.path.join( + dist_info_directory, os.listdir(dist_info_directory)[0]) + continue + + assert len(dist_infos) == 1 + break + + # PEP 517 requires that the .dist-info directory be placed in the + # metadata_directory. To comply, we MUST copy the directory to the root + if dist_info_directory != metadata_directory: + shutil.move( + os.path.join(dist_info_directory, dist_infos[0]), + metadata_directory) + shutil.rmtree(dist_info_directory, ignore_errors=True) + + return dist_infos[0] + + def build_wheel(self, wheel_directory, config_settings=None, + metadata_directory=None): + config_settings = self._fix_config(config_settings) + wheel_directory = os.path.abspath(wheel_directory) + + # Build the wheel in a temporary directory, then copy to the target + with TemporaryDirectory(dir=wheel_directory) as tmp_dist_dir: + sys.argv = (sys.argv[:1] + + ['bdist_wheel', '--dist-dir', tmp_dist_dir] + + config_settings["--global-option"]) + self.run_setup() + + wheel_basename = _file_with_extension(tmp_dist_dir, '.whl') + wheel_path = os.path.join(wheel_directory, wheel_basename) + if os.path.exists(wheel_path): + # os.rename will fail overwriting on non-unix env + os.remove(wheel_path) + os.rename(os.path.join(tmp_dist_dir, wheel_basename), wheel_path) + + return wheel_basename + + def build_sdist(self, sdist_directory, config_settings=None): + config_settings = self._fix_config(config_settings) + sdist_directory = os.path.abspath(sdist_directory) + sys.argv = sys.argv[:1] + ['sdist', '--formats', 'gztar'] + \ + config_settings["--global-option"] + \ + ["--dist-dir", sdist_directory] + self.run_setup() + + return _file_with_extension(sdist_directory, '.tar.gz') + + +class _BuildMetaLegacyBackend(_BuildMetaBackend): + """Compatibility backend for setuptools + + This is a version of setuptools.build_meta that endeavors to maintain backwards + compatibility with pre-PEP 517 modes of invocation. It exists as a temporary + bridge between the old packaging mechanism and the new packaging mechanism, + and will eventually be removed. + """ + def run_setup(self, setup_script='setup.py'): + # In order to maintain compatibility with scripts assuming that + # the setup.py script is in a directory on the PYTHONPATH, inject + # '' into sys.path. (pypa/setuptools#1642) + sys_path = list(sys.path) # Save the original path + + script_dir = os.path.dirname(os.path.abspath(setup_script)) + if script_dir not in sys.path: + sys.path.insert(0, script_dir) + + try: + super(_BuildMetaLegacyBackend, + self).run_setup(setup_script=setup_script) + finally: + # While PEP 517 frontends should be calling each hook in a fresh + # subprocess according to the standard (and thus it should not be + # strictly necessary to restore the old sys.path), we'll restore + # the original path so that the path manipulation does not persist + # within the hook after run_setup is called. + sys.path[:] = sys_path + +# The primary backend +_BACKEND = _BuildMetaBackend() + +get_requires_for_build_wheel = _BACKEND.get_requires_for_build_wheel +get_requires_for_build_sdist = _BACKEND.get_requires_for_build_sdist +prepare_metadata_for_build_wheel = _BACKEND.prepare_metadata_for_build_wheel +build_wheel = _BACKEND.build_wheel +build_sdist = _BACKEND.build_sdist + + +# The legacy backend +__legacy__ = _BuildMetaLegacyBackend() diff --git a/venv2/lib/python2.7/site-packages/setuptools/cli-32.exe b/venv2/lib/python2.7/site-packages/setuptools/cli-32.exe new file mode 100644 index 0000000000000000000000000000000000000000..b1487b7819e7286577a043c7726fbe0ca1543083 GIT binary patch literal 65536 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[c}i<OXTz6k>I#RErv3nvcIClH@!{vM)zJ_weD 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 literal 0 HcmV?d00001 diff --git a/venv2/lib/python2.7/site-packages/setuptools/cli-64.exe b/venv2/lib/python2.7/site-packages/setuptools/cli-64.exe new file mode 100644 index 0000000000000000000000000000000000000000..675e6bf3743f3d3011c238657e7128ee9960ef7f GIT binary patch literal 74752 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 &sjKl@1{;unAiW2w^OujNoW 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 literal 0 HcmV?d00001 diff --git a/venv2/lib/python2.7/site-packages/setuptools/cli.exe b/venv2/lib/python2.7/site-packages/setuptools/cli.exe new file mode 100644 index 0000000000000000000000000000000000000000..b1487b7819e7286577a043c7726fbe0ca1543083 GIT binary patch literal 65536 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[c}i<OXTz6k>I#RErv3nvcIClH@!{vM)zJ_weD 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 literal 0 HcmV?d00001 diff --git a/venv2/lib/python2.7/site-packages/setuptools/command/__init__.py b/venv2/lib/python2.7/site-packages/setuptools/command/__init__.py new file mode 100644 index 0000000..fe619e2 --- /dev/null +++ b/venv2/lib/python2.7/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', 'build_clib', + 'dist_info', +] + +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/venv2/lib/python2.7/site-packages/setuptools/command/alias.py b/venv2/lib/python2.7/site-packages/setuptools/command/alias.py new file mode 100644 index 0000000..4532b1c --- /dev/null +++ b/venv2/lib/python2.7/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/venv2/lib/python2.7/site-packages/setuptools/command/bdist_egg.py b/venv2/lib/python2.7/site-packages/setuptools/command/bdist_egg.py new file mode 100644 index 0000000..9f8df91 --- /dev/null +++ b/venv2/lib/python2.7/site-packages/setuptools/command/bdist_egg.py @@ -0,0 +1,502 @@ +"""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 re +import textwrap +import marshal + +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 sorted_walk(dir): + """Do os.walk in a reproducible way, + independent of indeterministic filesystem readdir order + """ + for base, dirs, files in os.walk(dir): + dirs.sort() + files.sort() + yield base, dirs, files + + +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: + path = os.path.join(base, name) + + if name.endswith('.py'): + log.debug("Deleting %s", path) + os.unlink(path) + + if base.endswith('__pycache__'): + path_old = path + + pattern = r'(?P<name>.+)\.(?P<magic>[^.]+)\.pyc' + m = re.match(pattern, name) + path_new = os.path.join( + base, os.pardir, m.group('name') + '.pyc') + log.info( + "Renaming file from [%s] to [%s]" + % (path_old, path_new)) + try: + os.remove(path_new) + except OSError: + pass + os.rename(path_old, path_new) + + 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 sorted_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 = sorted_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 six.PY2: + skip = 8 # skip magic & date + elif sys.version_info < (3, 7): + skip = 12 # skip magic & date & file size + else: + skip = 16 # skip magic & reserved? & 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 sorted_walk(base_dir): + visit(z, dirname, files) + z.close() + else: + for dirname, dirs, files in sorted_walk(base_dir): + visit(None, dirname, files) + return zip_filename diff --git a/venv2/lib/python2.7/site-packages/setuptools/command/bdist_rpm.py b/venv2/lib/python2.7/site-packages/setuptools/command/bdist_rpm.py new file mode 100644 index 0000000..7073092 --- /dev/null +++ b/venv2/lib/python2.7/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/venv2/lib/python2.7/site-packages/setuptools/command/bdist_wininst.py b/venv2/lib/python2.7/site-packages/setuptools/command/bdist_wininst.py new file mode 100644 index 0000000..073de97 --- /dev/null +++ b/venv2/lib/python2.7/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/venv2/lib/python2.7/site-packages/setuptools/command/build_clib.py b/venv2/lib/python2.7/site-packages/setuptools/command/build_clib.py new file mode 100644 index 0000000..09caff6 --- /dev/null +++ b/venv2/lib/python2.7/site-packages/setuptools/command/build_clib.py @@ -0,0 +1,98 @@ +import distutils.command.build_clib as orig +from distutils.errors import DistutilsSetupError +from distutils import log +from setuptools.dep_util import newer_pairwise_group + + +class build_clib(orig.build_clib): + """ + Override the default build_clib behaviour to do the following: + + 1. Implement a rudimentary timestamp-based dependency system + so 'compile()' doesn't run every time. + 2. Add more keys to the 'build_info' dictionary: + * obj_deps - specify dependencies for each object compiled. + this should be a dictionary mapping a key + with the source filename to a list of + dependencies. Use an empty string for global + dependencies. + * cflags - specify a list of additional flags to pass to + the compiler. + """ + + def build_libraries(self, libraries): + for (lib_name, build_info) in libraries: + sources = build_info.get('sources') + if sources is None or not isinstance(sources, (list, tuple)): + raise DistutilsSetupError( + "in 'libraries' option (library '%s'), " + "'sources' must be present and must be " + "a list of source filenames" % lib_name) + sources = list(sources) + + log.info("building '%s' library", lib_name) + + # Make sure everything is the correct type. + # obj_deps should be a dictionary of keys as sources + # and a list/tuple of files that are its dependencies. + obj_deps = build_info.get('obj_deps', dict()) + if not isinstance(obj_deps, dict): + raise DistutilsSetupError( + "in 'libraries' option (library '%s'), " + "'obj_deps' must be a dictionary of " + "type 'source: list'" % lib_name) + dependencies = [] + + # Get the global dependencies that are specified by the '' key. + # These will go into every source's dependency list. + global_deps = obj_deps.get('', list()) + if not isinstance(global_deps, (list, tuple)): + raise DistutilsSetupError( + "in 'libraries' option (library '%s'), " + "'obj_deps' must be a dictionary of " + "type 'source: list'" % lib_name) + + # Build the list to be used by newer_pairwise_group + # each source will be auto-added to its dependencies. + for source in sources: + src_deps = [source] + src_deps.extend(global_deps) + extra_deps = obj_deps.get(source, list()) + if not isinstance(extra_deps, (list, tuple)): + raise DistutilsSetupError( + "in 'libraries' option (library '%s'), " + "'obj_deps' must be a dictionary of " + "type 'source: list'" % lib_name) + src_deps.extend(extra_deps) + dependencies.append(src_deps) + + expected_objects = self.compiler.object_filenames( + sources, + output_dir=self.build_temp + ) + + if newer_pairwise_group(dependencies, expected_objects) != ([], []): + # First, compile the source code to object files in the library + # directory. (This should probably change to putting object + # files in a temporary build directory.) + macros = build_info.get('macros') + include_dirs = build_info.get('include_dirs') + cflags = build_info.get('cflags') + objects = self.compiler.compile( + sources, + output_dir=self.build_temp, + macros=macros, + include_dirs=include_dirs, + extra_postargs=cflags, + debug=self.debug + ) + + # Now "link" the object files together into a static library. + # (On Unix at least, this isn't really linking -- it just + # builds an archive. Whatever.) + self.compiler.create_static_lib( + expected_objects, + lib_name, + output_dir=self.build_clib, + debug=self.debug + ) diff --git a/venv2/lib/python2.7/site-packages/setuptools/command/build_ext.py b/venv2/lib/python2.7/site-packages/setuptools/command/build_ext.py new file mode 100644 index 0000000..60a8a32 --- /dev/null +++ b/venv2/lib/python2.7/site-packages/setuptools/command/build_ext.py @@ -0,0 +1,321 @@ +import os +import sys +import itertools +import imp +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, get_config_var +from distutils.errors import DistutilsError +from distutils import log + +from setuptools.extension import Library +from setuptools.extern import six + +try: + # Attempt to use Cython for building extensions, if available + from Cython.Distutils.build_ext import build_ext as _build_ext + # Additionally, assert that the compiler module will load + # also. Ref #1229. + __import__('Cython.Compiler.Main') +except ImportError: + _build_ext = _du_build_ext + +# make sure _config_vars is initialized +get_config_var("LDSHARED") +from distutils.sysconfig import _config_vars as _CONFIG_VARS + + +def _customize_compiler_for_shlib(compiler): + if sys.platform == "darwin": + # building .dylib requires additional compiler flags on OSX; here we + # temporarily substitute the pyconfig.h variables so that distutils' + # 'customize_compiler' uses them before we build the shared libraries. + 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) + + +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 '' + + +def get_abi3_suffix(): + """Return the file extension for an abi3-compliant Extension()""" + for suffix, _, _ in (s for s in imp.get_suffixes() if s[2] == imp.C_EXTENSION): + if '.abi3' in suffix: # Unix + return suffix + elif suffix == '.pyd': # Windows + return suffix + + +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] + use_abi3 = ( + six.PY3 + and getattr(ext, 'py_limited_api') + and get_abi3_suffix() + ) + if use_abi3: + so_ext = get_config_var('EXT_SUFFIX') + filename = filename[:-len(so_ext)] + filename = filename + get_abi3_suffix() + 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 + ) + _customize_compiler_for_shlib(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/venv2/lib/python2.7/site-packages/setuptools/command/build_py.py b/venv2/lib/python2.7/site-packages/setuptools/command/build_py.py new file mode 100644 index 0000000..b0314fd --- /dev/null +++ b/venv2/lib/python2.7/site-packages/setuptools/command/build_py.py @@ -0,0 +1,270 @@ +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 itertools + +from setuptools.extern import six +from setuptools.extern.six.moves import map, filter, filterfalse + +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): + if six.PY2 and isinstance(package, six.string_types): + # avoid errors on Python 2 when unicode is passed (#190) + package = package.split('.') + 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'""" + patterns = self._get_platform_patterns( + self.package_data, + package, + src_dir, + ) + globs_expanded = map(glob, patterns) + # flatten the expanded globs into an iterable of matches + globs_matches = itertools.chain.from_iterable(globs_expanded) + glob_files = filter(os.path.isfile, globs_matches) + files = itertools.chain( + self.manifest_files.get(package, []), + glob_files, + ) + 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'""" + files = list(files) + patterns = self._get_platform_patterns( + self.exclude_package_data, + package, + src_dir, + ) + match_groups = ( + fnmatch.filter(files, pattern) + for pattern in patterns + ) + # flatten the groups of matches into an iterable of matches + matches = itertools.chain.from_iterable(match_groups) + bad = set(matches) + keepers = ( + fn + for fn in files + if fn not in bad + ) + # ditch dupes + return list(_unique_everseen(keepers)) + + @staticmethod + def _get_platform_patterns(spec, package, src_dir): + """ + yield platform-specific path patterns (suitable for glob + or fn_match) from a glob-based spec (such as + self.package_data or self.exclude_package_data) + matching package in src_dir. + """ + raw_patterns = itertools.chain( + spec.get('', []), + spec.get(package, []), + ) + return ( + # Each pattern has to be converted to a platform-specific path + os.path.join(src_dir, convert_path(pattern)) + for pattern in raw_patterns + ) + + +# from Python 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 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 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/venv2/lib/python2.7/site-packages/setuptools/command/develop.py b/venv2/lib/python2.7/site-packages/setuptools/command/develop.py new file mode 100644 index 0000000..009e4f9 --- /dev/null +++ b/venv2/lib/python2.7/site-packages/setuptools/command/develop.py @@ -0,0 +1,221 @@ +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 + +import pkg_resources +from setuptools.command.easy_install import easy_install +from setuptools import namespaces +import setuptools + +__metaclass__ = type + + +class develop(namespaces.DevelopInstaller, 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() + self.uninstall_namespaces() + 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 = pkg_resources.normalize_path(self.egg_base) + egg_path = pkg_resources.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 = pkg_resources.Distribution( + target, + pkg_resources.PathMetadata(target, os.path.abspath(ei.egg_info)), + project_name=ei.egg_name + ) + + self.setup_path = self._resolve_setup_path( + self.egg_base, + self.install_dir, + self.egg_path, + ) + + @staticmethod + def _resolve_setup_path(egg_base, install_dir, egg_path): + """ + Generate a path from egg_base back to '.' where the + setup script resides and ensure that path points to the + setup path from $install_dir/$egg_path. + """ + path_to_setup = egg_base.replace(os.sep, '/').rstrip('/') + if path_to_setup != os.curdir: + path_to_setup = '../' * (path_to_setup.count('/') + 1) + resolved = pkg_resources.normalize_path( + os.path.join(install_dir, egg_path, path_to_setup) + ) + if resolved != pkg_resources.normalize_path(os.curdir): + raise DistutilsOptionError( + "Can't get a consistent path to setup script from" + " installation directory", resolved, + pkg_resources.normalize_path(os.curdir)) + return path_to_setup + + 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 = pkg_resources.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 = pkg_resources.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 + + self.install_namespaces() + + # 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: + """ + Adapt a pkg_resources.Distribution to simply return the project + name as the 'requirement' so that scripts will work across + multiple versions. + + >>> from pkg_resources import Distribution + >>> 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/venv2/lib/python2.7/site-packages/setuptools/command/dist_info.py b/venv2/lib/python2.7/site-packages/setuptools/command/dist_info.py new file mode 100644 index 0000000..c45258f --- /dev/null +++ b/venv2/lib/python2.7/site-packages/setuptools/command/dist_info.py @@ -0,0 +1,36 @@ +""" +Create a dist_info directory +As defined in the wheel specification +""" + +import os + +from distutils.core import Command +from distutils import log + + +class dist_info(Command): + + description = 'create a .dist-info directory' + + user_options = [ + ('egg-base=', 'e', "directory containing .egg-info directories" + " (default: top of the source tree)"), + ] + + def initialize_options(self): + self.egg_base = None + + def finalize_options(self): + pass + + def run(self): + egg_info = self.get_finalized_command('egg_info') + egg_info.egg_base = self.egg_base + egg_info.finalize_options() + egg_info.run() + dist_info_dir = egg_info.egg_info[:-len('.egg-info')] + '.dist-info' + log.info("creating '{}'".format(os.path.abspath(dist_info_dir))) + + bdist_wheel = self.get_finalized_command('bdist_wheel') + bdist_wheel.egg2dist(egg_info.egg_info, dist_info_dir) diff --git a/venv2/lib/python2.7/site-packages/setuptools/command/easy_install.py b/venv2/lib/python2.7/site-packages/setuptools/command/easy_install.py new file mode 100644 index 0000000..06c9827 --- /dev/null +++ b/venv2/lib/python2.7/site-packages/setuptools/command/easy_install.py @@ -0,0 +1,2342 @@ +#!/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://setuptools.readthedocs.io/en/latest/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 textwrap +import warnings +import site +import struct +import contextlib +import subprocess +import shlex +import io + + +from sysconfig import get_config_vars, get_path + +from setuptools import SetuptoolsDeprecationWarning + +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.py27compat import rmtree_safe +from setuptools.command import setopt +from setuptools.archive_util import unpack_archive +from setuptools.package_index import ( + PackageIndex, parse_requirement_arg, URL_SCHEME, +) +from setuptools.command import bdist_egg, egg_info +from setuptools.wheel import Wheel +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.py31compat + +__metaclass__ = type + +# 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): + """ + Determine if two paths reference the same file. + + Augments os.path.samefile to work on Windows and + suppresses errors if the path doesn't exist. + """ + 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_bytes(s): + return s + + def isascii(s): + try: + six.text_type(s, 'ascii') + return True + except UnicodeError: + return False +else: + + def _to_bytes(s): + return s.encode('utf8') + + def isascii(s): + try: + s.encode('ascii') + return True + except UnicodeError: + return False + + +_one_liner = lambda text: textwrap.dedent(text).strip().replace('\n', '; ') + + +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"), + ('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' + ] + + 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 + # 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() + + 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 + # 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.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.""" + dirs = [ + 'install_purelib', + 'install_platlib', + 'install_lib', + 'install_headers', + 'install_scripts', + 'install_data', + ] + self._expand_attrs(dirs) + + 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 Exception: + 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 + + if instdir not in map(normalize_path, _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://setuptools.readthedocs.io/en/latest/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) + tmpl = _one_liner(""" + import os + f = open({ok_file!r}, 'w') + f.write('OK') + f.close() + """) + '\n' + try: + if ok_exists: + os.unlink(ok_file) + dirname = os.path.dirname(ok_file) + pkg_resources.py31compat.makedirs(dirname, exist_ok=True) + f = open(pth_file, 'w') + except (OSError, IOError): + self.cant_write_to_target() + else: + try: + f.write(tmpl.format(**locals())) + f.close() + f = None + executable = sys.executable + if os.name == 'nt': + dirname, basename = os.path.split(executable) + alt = os.path.join(dirname, 'pythonw.exe') + use_alt = ( + basename.lower() == 'python.exe' and + os.path.exists(alt) + ) + if use_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) + ) + + @contextlib.contextmanager + def _tmpdir(self): + tmpdir = tempfile.mkdtemp(prefix=u"easy_install-") + try: + # cast to str as workaround for #709 and #710 and #712 + yield str(tmpdir) + finally: + os.path.exists(tmpdir) and rmtree(rmtree_safe(tmpdir)) + + def easy_install(self, spec, deps=False): + if not self.editable: + self.install_site_py() + + with self._tmpdir() as tmpdir: + if not isinstance(spec, Requirement): + if URL_SCHEME(spec): + # It's a url, download it to tmpdir and process + self.not_editable(spec) + dl = self.package_index.download(spec, tmpdir) + return self.install_item(None, dl, 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) + + 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_bytes(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) + + if self.dry_run: + return + + mask = current_umask() + 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)] + elif dist_filename.lower().endswith('.whl'): + return [self.install_wheel(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 Exception: + 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 = os.path.join(tmpdir, dist.egg_name() + '.egg') + dist.location = egg_path + 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() + + def install_wheel(self, wheel_path, tmpdir): + wheel = Wheel(wheel_path) + assert wheel.is_compatible() + destination = os.path.join(self.install_dir, wheel.egg_name()) + destination = os.path.abspath(destination) + if not self.dry_run: + ensure_directory(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: + self.execute( + wheel.install_as_egg, + (destination,), + ("Installing %s to %s") % ( + os.path.basename(wheel_path), + os.path.dirname(destination) + ), + ) + finally: + update_dist_caches(destination, fix_zipimporter_caches=False) + self.add_output(destination) + return self.egg_distribution(destination) + + __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: + 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://setuptools.readthedocs.io/en/latest/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) + + INSTALL_SCHEMES = dict( + posix=dict( + install_dir='$base/lib/python$py_version_short/site-packages', + 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: + # Set default install_dir/scripts from --prefix + config_vars = config_vars.copy() + config_vars['base'] = self.prefix + scheme = self.INSTALL_SCHEMES.get(os.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 _pythonpath(): + items = os.environ.get('PYTHONPATH', '').split(os.pathsep) + return filter(None, items) + + +def get_site_dirs(): + """ + Return a list of 'site' dirs + """ + + sitedirs = [] + + # start with PYTHONPATH + sitedirs.extend(_pythonpath()) + + 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: + sitedirs.extend([ + prefix, + os.path.join(prefix, "lib", "site-packages"), + ]) + 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: + home_sp = os.path.join( + home, + 'Library', + 'Python', + sys.version[:3], + 'site-packages', + ) + sitedirs.append(home_sp) + 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) + + try: + sitedirs.extend(site.getsitepackages()) + except AttributeError: + pass + + 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)) + init = {'version': '', 'target_version': ''} + cfg = configparser.RawConfigParser(init) + 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 + + +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 + + prelude = _one_liner(""" + import sys + sys.__plen = len(sys.path) + """) + postlude = _one_liner(""" + 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', 'raw') == '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 in [os.unlink, 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 + # http://bit.ly/2h9itJX + 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 _strip_quotes(item): + _QUOTES = '"\'' + for q in _QUOTES: + if item.startswith(q) and item.endswith(q): + return item[1:-1] + return item + + @staticmethod + def _render(items): + cmdline = subprocess.list2cmdline( + CommandSpec._strip_quotes(item.strip()) for item in 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: + """ + Encapsulates behavior around writing entry point scripts for console and + gui apps. + """ + + template = textwrap.dedent(r""" + # EASY-INSTALL-ENTRY-SCRIPT: %(spec)r,%(group)r,%(name)r + __requires__ = %(spec)r + import re + import sys + from pkg_resources import load_entry_point + + if __name__ == '__main__': + sys.argv[0] = re.sub(r'(-script\.pyw?|\.exe)?$', '', sys.argv[0]) + 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", EasyInstallDeprecationWarning) + 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", EasyInstallDeprecationWarning, stacklevel=2) + if wininst: + executable = "python.exe" + return cls.get_header(script_text, executable) + + @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", EasyInstallDeprecationWarning) + 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", EasyInstallDeprecationWarning) + 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(';'): + msg = ( + "{ext} not listed in PATHEXT; scripts will not be " + "recognized as executables." + ).format(**locals()) + warnings.warn(msg, 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 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): + return shutil.rmtree(path, ignore_errors, onerror) + + +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 + +class EasyInstallDeprecationWarning(SetuptoolsDeprecationWarning): + """Class for warning about deprecations in EasyInstall in SetupTools. Not ignored by default, unlike DeprecationWarning.""" + diff --git a/venv2/lib/python2.7/site-packages/setuptools/command/egg_info.py b/venv2/lib/python2.7/site-packages/setuptools/command/egg_info.py new file mode 100644 index 0000000..5d8f451 --- /dev/null +++ b/venv2/lib/python2.7/site-packages/setuptools/command/egg_info.py @@ -0,0 +1,717 @@ +"""setuptools.command.egg_info + +Create a distribution's .egg-info directory and contents""" + +from distutils.filelist import FileList as _FileList +from distutils.errors import DistutilsInternalError +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 +import collections + +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 setuptools.glob import glob + +from setuptools.extern import packaging +from setuptools import SetuptoolsDeprecationWarning + +def translate_pattern(glob): + """ + Translate a file path glob like '*.txt' in to a regular expression. + This differs from fnmatch.translate which allows wildcards to match + directory separators. It also knows about '**/' which matches any number of + directories. + """ + pat = '' + + # This will split on '/' within [character classes]. This is deliberate. + chunks = glob.split(os.path.sep) + + sep = re.escape(os.sep) + valid_char = '[^%s]' % (sep,) + + for c, chunk in enumerate(chunks): + last_chunk = c == len(chunks) - 1 + + # Chunks that are a literal ** are globstars. They match anything. + if chunk == '**': + if last_chunk: + # Match anything if this is the last component + pat += '.*' + else: + # Match '(name/)*' + pat += '(?:%s+%s)*' % (valid_char, sep) + continue # Break here as the whole path component has been handled + + # Find any special characters in the remainder + i = 0 + chunk_len = len(chunk) + while i < chunk_len: + char = chunk[i] + if char == '*': + # Match any number of name characters + pat += valid_char + '*' + elif char == '?': + # Match a name character + pat += valid_char + elif char == '[': + # Character class + inner_i = i + 1 + # Skip initial !/] chars + if inner_i < chunk_len and chunk[inner_i] == '!': + inner_i = inner_i + 1 + if inner_i < chunk_len and chunk[inner_i] == ']': + inner_i = inner_i + 1 + + # Loop till the closing ] is found + while inner_i < chunk_len and chunk[inner_i] != ']': + inner_i = inner_i + 1 + + if inner_i >= chunk_len: + # Got to the end of the string without finding a closing ] + # Do not treat this as a matching group, but as a literal [ + pat += re.escape(char) + else: + # Grab the insides of the [brackets] + inner = chunk[i + 1:inner_i] + char_class = '' + + # Class negation + if inner[0] == '!': + char_class = '^' + inner = inner[1:] + + char_class += re.escape(inner) + pat += '[%s]' % (char_class,) + + # Skip to the end ] + i = inner_i + else: + pat += re.escape(char) + i += 1 + + # Join each chunk with the dir separator + if not last_chunk: + pat += sep + + pat += r'\Z' + return re.compile(pat, flags=re.MULTILINE|re.DOTALL) + + +class InfoCommon: + tag_build = None + tag_date = None + + @property + def name(self): + return safe_name(self.distribution.get_name()) + + 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 tags(self): + version = '' + if self.tag_build: + version += self.tag_build + if self.tag_date: + version += time.strftime("-%Y%m%d") + return version + vtags = property(tags) + + +class egg_info(InfoCommon, 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-date', 'd', "Add date stamp (e.g. 20050528) to version number"), + ('tag-build=', 'b', "Specify explicit tag to add to version number"), + ('no-date', 'D', "Don't include date stamp [default]"), + ] + + boolean_options = ['tag-date'] + negative_opt = { + 'no-date': 'tag-date', + } + + def initialize_options(self): + self.egg_base = None + self.egg_name = None + self.egg_info = None + self.egg_version = None + self.broken_egg_info = False + + #################################### + # allow the 'tag_svn_revision' to be detected and + # set, supporting sdists built on older Setuptools. + @property + def tag_svn_revision(self): + pass + + @tag_svn_revision.setter + def tag_svn_revision(self, value): + pass + #################################### + + def save_version_info(self, filename): + """ + Materialize the value of date into the + build tag. Install build keys in a deterministic order + to avoid arbitrary reordering on subsequent builds. + """ + egg_info = collections.OrderedDict() + # follow the order these keys would have been added + # when PYTHONHASHSEED=0 + egg_info['tag_build'] = self.tags() + egg_info['tag_date'] = 0 + edit_config(filename, dict(egg_info=egg_info)) + + def finalize_options(self): + # Note: we need to capture the current value returned + # by `self.tagged_version()`, so we can later update + # `self.distribution.metadata.version` without + # repercussions. + self.egg_name = self.name + 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 run(self): + self.mkpath(self.egg_info) + os.utime(self.egg_info, None) + 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 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): + # Implementations of the various MANIFEST.in commands + + def process_template_line(self, line): + # Parse the line: split it up, make sure the right number of words + # is there, and return the relevant words. 'action' is always + # defined: it's the first word of the line. Which of the other + # three are defined depends on the action; it'll be either + # patterns, (dir and patterns), or (dir_pattern). + (action, patterns, dir, dir_pattern) = self._parse_template_line(line) + + # OK, now we know that the action is valid and we have the + # right number of words on the line for that action -- so we + # can proceed with minimal error-checking. + if action == 'include': + self.debug_print("include " + ' '.join(patterns)) + for pattern in patterns: + if not self.include(pattern): + log.warn("warning: no files found matching '%s'", pattern) + + elif action == 'exclude': + self.debug_print("exclude " + ' '.join(patterns)) + for pattern in patterns: + if not self.exclude(pattern): + log.warn(("warning: no previously-included files " + "found matching '%s'"), pattern) + + elif action == 'global-include': + self.debug_print("global-include " + ' '.join(patterns)) + for pattern in patterns: + if not self.global_include(pattern): + log.warn(("warning: no files found matching '%s' " + "anywhere in distribution"), pattern) + + elif action == 'global-exclude': + self.debug_print("global-exclude " + ' '.join(patterns)) + for pattern in patterns: + if not self.global_exclude(pattern): + log.warn(("warning: no previously-included files matching " + "'%s' found anywhere in distribution"), + pattern) + + elif action == 'recursive-include': + self.debug_print("recursive-include %s %s" % + (dir, ' '.join(patterns))) + for pattern in patterns: + if not self.recursive_include(dir, pattern): + log.warn(("warning: no files found matching '%s' " + "under directory '%s'"), + pattern, dir) + + elif action == 'recursive-exclude': + self.debug_print("recursive-exclude %s %s" % + (dir, ' '.join(patterns))) + for pattern in patterns: + if not self.recursive_exclude(dir, pattern): + log.warn(("warning: no previously-included files matching " + "'%s' found under directory '%s'"), + pattern, dir) + + elif action == 'graft': + self.debug_print("graft " + dir_pattern) + if not self.graft(dir_pattern): + log.warn("warning: no directories found matching '%s'", + dir_pattern) + + elif action == 'prune': + self.debug_print("prune " + dir_pattern) + if not self.prune(dir_pattern): + log.warn(("no previously-included directories found " + "matching '%s'"), dir_pattern) + + else: + raise DistutilsInternalError( + "this cannot happen: invalid action '%s'" % action) + + def _remove_files(self, predicate): + """ + Remove all files from the file list that match the predicate. + Return True if any matching files were removed + """ + found = False + for i in range(len(self.files) - 1, -1, -1): + if predicate(self.files[i]): + self.debug_print(" removing " + self.files[i]) + del self.files[i] + found = True + return found + + def include(self, pattern): + """Include files that match 'pattern'.""" + found = [f for f in glob(pattern) if not os.path.isdir(f)] + self.extend(found) + return bool(found) + + def exclude(self, pattern): + """Exclude files that match 'pattern'.""" + match = translate_pattern(pattern) + return self._remove_files(match.match) + + def recursive_include(self, dir, pattern): + """ + Include all files anywhere in 'dir/' that match the pattern. + """ + full_pattern = os.path.join(dir, '**', pattern) + found = [f for f in glob(full_pattern, recursive=True) + if not os.path.isdir(f)] + self.extend(found) + return bool(found) + + def recursive_exclude(self, dir, pattern): + """ + Exclude any file anywhere in 'dir/' that match the pattern. + """ + match = translate_pattern(os.path.join(dir, '**', pattern)) + return self._remove_files(match.match) + + def graft(self, dir): + """Include all files from 'dir/'.""" + found = [ + item + for match_dir in glob(dir) + for item in distutils.filelist.findall(match_dir) + ] + self.extend(found) + return bool(found) + + def prune(self, dir): + """Filter out files from 'dir/'.""" + match = translate_pattern(os.path.join(dir, '**')) + return self._remove_files(match.match) + + def global_include(self, pattern): + """ + Include all files anywhere in the current directory that match the + pattern. This is very inefficient on large file trees. + """ + if self.allfiles is None: + self.findall() + match = translate_pattern(os.path.join('**', pattern)) + found = [f for f in self.allfiles if match.match(f)] + self.extend(found) + return bool(found) + + def global_exclude(self, pattern): + """ + Exclude all files anywhere that match the pattern. + """ + match = translate_pattern(os.path.join('**', pattern)) + return self._remove_files(match.match) + + 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.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): + if not self._should_suppress_warning(msg): + sdist.warn(self, msg) + + @staticmethod + def _should_suppress_warning(msg): + """ + suppress missing-file warnings from sdist + """ + return re.match(r"standard file .*not found", msg) + + def add_defaults(self): + sdist.add_defaults(self) + self.check_license() + 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() + + if os.path.exists("setup.py"): + # setup.py should be included by default, even if it's not + # the script called to create the sdist + self.filelist.append("setup.py") + + ei_cmd = self.get_finalized_command('egg_info') + self.filelist.graft(ei_cmd.egg_info) + + def prune_file_list(self): + build = self.get_finalized_command('build') + base_dir = self.distribution.get_fullname() + self.filelist.prune(build.build_base) + self.filelist.prune(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 = io.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.", EggInfoDeprecationWarning) + 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 + + +class EggInfoDeprecationWarning(SetuptoolsDeprecationWarning): + """Class for warning about deprecations in eggInfo in setupTools. Not ignored by default, unlike DeprecationWarning.""" diff --git a/venv2/lib/python2.7/site-packages/setuptools/command/install.py b/venv2/lib/python2.7/site-packages/setuptools/command/install.py new file mode 100644 index 0000000..31a5ddb --- /dev/null +++ b/venv2/lib/python2.7/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/venv2/lib/python2.7/site-packages/setuptools/command/install_egg_info.py b/venv2/lib/python2.7/site-packages/setuptools/command/install_egg_info.py new file mode 100644 index 0000000..edc4718 --- /dev/null +++ b/venv2/lib/python2.7/site-packages/setuptools/command/install_egg_info.py @@ -0,0 +1,62 @@ +from distutils import log, dir_util +import os + +from setuptools import Command +from setuptools import namespaces +from setuptools.archive_util import unpack_archive +import pkg_resources + + +class install_egg_info(namespaces.Installer, 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 + + def finalize_options(self): + self.set_undefined_options('install_lib', + ('install_dir', 'install_dir')) + 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' + 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 + self.outputs.append(dst) + log.debug("Copying %s to %s", src, dst) + return dst + + unpack_archive(self.source, self.target, skimmer) diff --git a/venv2/lib/python2.7/site-packages/setuptools/command/install_lib.py b/venv2/lib/python2.7/site-packages/setuptools/command/install_lib.py new file mode 100644 index 0000000..2b31c3e --- /dev/null +++ b/venv2/lib/python2.7/site-packages/setuptools/command/install_lib.py @@ -0,0 +1,121 @@ +import os +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 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: + 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 = [] + + def pf(src, dst): + if dst in exclude: + log.warn("Skipping installation of %s (namespace package)", + dst) + return False + + 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/venv2/lib/python2.7/site-packages/setuptools/command/install_scripts.py b/venv2/lib/python2.7/site-packages/setuptools/command/install_scripts.py new file mode 100644 index 0000000..1623427 --- /dev/null +++ b/venv2/lib/python2.7/site-packages/setuptools/command/install_scripts.py @@ -0,0 +1,65 @@ +from distutils import log +import distutils.command.install_scripts as orig +import os +import sys + +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 + if exec_param == sys.executable: + # In case the path to the Python executable contains a space, wrap + # it so it's not split up. + exec_param = [exec_param] + # 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/venv2/lib/python2.7/site-packages/setuptools/command/launcher manifest.xml b/venv2/lib/python2.7/site-packages/setuptools/command/launcher manifest.xml new file mode 100644 index 0000000..5972a96 --- /dev/null +++ b/venv2/lib/python2.7/site-packages/setuptools/command/launcher manifest.xml @@ -0,0 +1,15 @@ +<?xml version="1.0" encoding="UTF-8" standalone="yes"?> +<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0"> + <assemblyIdentity version="1.0.0.0" + processorArchitecture="X86" + name="%(name)s" + type="win32"/> + <!-- Identify the application security requirements. --> + <trustInfo xmlns="urn:schemas-microsoft-com:asm.v3"> + <security> + <requestedPrivileges> + <requestedExecutionLevel level="asInvoker" uiAccess="false"/> + </requestedPrivileges> + </security> + </trustInfo> +</assembly> diff --git a/venv2/lib/python2.7/site-packages/setuptools/command/py36compat.py b/venv2/lib/python2.7/site-packages/setuptools/command/py36compat.py new file mode 100644 index 0000000..61063e7 --- /dev/null +++ b/venv2/lib/python2.7/site-packages/setuptools/command/py36compat.py @@ -0,0 +1,136 @@ +import os +from glob import glob +from distutils.util import convert_path +from distutils.command import sdist + +from setuptools.extern.six.moves import filter + + +class sdist_add_defaults: + """ + Mix-in providing forward-compatibility for functionality as found in + distutils on Python 3.7. + + Do not edit the code in this class except to update functionality + as implemented in distutils. Instead, override in the subclass. + """ + + def add_defaults(self): + """Add all the default files to self.filelist: + - README or README.txt + - setup.py + - test/test*.py + - all pure Python modules mentioned in setup script + - all files pointed by package_data (build_py) + - all files defined in data_files. + - all files defined as scripts. + - all C sources listed as part of extensions or C libraries + in the setup script (doesn't catch C headers!) + Warns if (README or README.txt) or setup.py are missing; everything + else is optional. + """ + self._add_defaults_standards() + self._add_defaults_optional() + self._add_defaults_python() + self._add_defaults_data_files() + self._add_defaults_ext() + self._add_defaults_c_libs() + self._add_defaults_scripts() + + @staticmethod + def _cs_path_exists(fspath): + """ + Case-sensitive path existence check + + >>> sdist_add_defaults._cs_path_exists(__file__) + True + >>> sdist_add_defaults._cs_path_exists(__file__.upper()) + False + """ + 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) + + def _add_defaults_standards(self): + standards = [self.READMES, self.distribution.script_name] + for fn in standards: + if isinstance(fn, tuple): + alts = fn + got_it = False + for fn in alts: + if self._cs_path_exists(fn): + got_it = True + self.filelist.append(fn) + break + + if not got_it: + self.warn("standard file not found: should have one of " + + ', '.join(alts)) + else: + if self._cs_path_exists(fn): + self.filelist.append(fn) + else: + self.warn("standard file '%s' not found" % fn) + + def _add_defaults_optional(self): + optional = ['test/test*.py', 'setup.cfg'] + for pattern in optional: + files = filter(os.path.isfile, glob(pattern)) + self.filelist.extend(files) + + def _add_defaults_python(self): + # build_py is used to get: + # - python modules + # - files defined in package_data + build_py = self.get_finalized_command('build_py') + + # getting python files + if self.distribution.has_pure_modules(): + self.filelist.extend(build_py.get_source_files()) + + # getting package_data files + # (computed in build_py.data_files by build_py.finalize_options) + for pkg, src_dir, build_dir, filenames in build_py.data_files: + for filename in filenames: + self.filelist.append(os.path.join(src_dir, filename)) + + def _add_defaults_data_files(self): + # getting distribution.data_files + if self.distribution.has_data_files(): + for item in self.distribution.data_files: + if isinstance(item, str): + # plain file + item = convert_path(item) + if os.path.isfile(item): + self.filelist.append(item) + else: + # a (dirname, filenames) tuple + dirname, filenames = item + for f in filenames: + f = convert_path(f) + if os.path.isfile(f): + self.filelist.append(f) + + def _add_defaults_ext(self): + if self.distribution.has_ext_modules(): + build_ext = self.get_finalized_command('build_ext') + self.filelist.extend(build_ext.get_source_files()) + + def _add_defaults_c_libs(self): + if self.distribution.has_c_libraries(): + build_clib = self.get_finalized_command('build_clib') + self.filelist.extend(build_clib.get_source_files()) + + def _add_defaults_scripts(self): + if self.distribution.has_scripts(): + build_scripts = self.get_finalized_command('build_scripts') + self.filelist.extend(build_scripts.get_source_files()) + + +if hasattr(sdist.sdist, '_add_defaults_standards'): + # disable the functionality already available upstream + class sdist_add_defaults: + pass diff --git a/venv2/lib/python2.7/site-packages/setuptools/command/register.py b/venv2/lib/python2.7/site-packages/setuptools/command/register.py new file mode 100644 index 0000000..98bc015 --- /dev/null +++ b/venv2/lib/python2.7/site-packages/setuptools/command/register.py @@ -0,0 +1,18 @@ +from distutils import log +import distutils.command.register as orig + + +class register(orig.register): + __doc__ = orig.register.__doc__ + + def run(self): + try: + # Make sure that we are using valid current name/version info + self.run_command('egg_info') + orig.register.run(self) + finally: + self.announce( + "WARNING: Registering is deprecated, use twine to " + "upload instead (https://pypi.org/p/twine/)", + log.WARN + ) diff --git a/venv2/lib/python2.7/site-packages/setuptools/command/rotate.py b/venv2/lib/python2.7/site-packages/setuptools/command/rotate.py new file mode 100644 index 0000000..b89353f --- /dev/null +++ b/venv2/lib/python2.7/site-packages/setuptools/command/rotate.py @@ -0,0 +1,66 @@ +from distutils.util import convert_path +from distutils import log +from distutils.errors import DistutilsOptionError +import os +import shutil + +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: + if os.path.isdir(f): + shutil.rmtree(f) + else: + os.unlink(f) diff --git a/venv2/lib/python2.7/site-packages/setuptools/command/saveopts.py b/venv2/lib/python2.7/site-packages/setuptools/command/saveopts.py new file mode 100644 index 0000000..611cec5 --- /dev/null +++ b/venv2/lib/python2.7/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/venv2/lib/python2.7/site-packages/setuptools/command/sdist.py b/venv2/lib/python2.7/site-packages/setuptools/command/sdist.py new file mode 100644 index 0000000..dc25398 --- /dev/null +++ b/venv2/lib/python2.7/site-packages/setuptools/command/sdist.py @@ -0,0 +1,221 @@ +from distutils import log +import distutils.command.sdist as orig +import os +import sys +import io +import contextlib + +from setuptools.extern import six + +from .py36compat import sdist_add_defaults + +import pkg_resources + +_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(sdist_add_defaults, 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 = {} + + README_EXTENSIONS = ['', '.rst', '.txt', '.md'] + READMES = tuple('README{0}'.format(ext) for ext in README_EXTENSIONS) + + 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) + + 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 initialize_options(self): + orig.sdist.initialize_options(self) + + self._default_to_gztar() + + def _default_to_gztar(self): + # only needed on Python prior to 3.6. + if sys.version_info >= (3, 6, 0, 'beta', 1): + return + self.formats = ['gztar'] + + def make_distribution(self): + """ + Workaround for #516 + """ + with self._remove_os_link(): + orig.sdist.make_distribution(self) + + @staticmethod + @contextlib.contextmanager + def _remove_os_link(): + """ + In a context, remove and restore os.link if it exists + """ + + class NoValue: + pass + + orig_val = getattr(os, 'link', NoValue) + try: + del os.link + except Exception: + pass + try: + yield + finally: + if orig_val is not NoValue: + setattr(os, 'link', orig_val) + + 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 Exception: + _, _, 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_python(self): + """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]) + + def _add_defaults_data_files(self): + try: + if six.PY2: + sdist_add_defaults._add_defaults_data_files(self) + else: + super()._add_defaults_data_files() + except TypeError: + log.warn("data_files contains unexpected objects") + + def check_readme(self): + for f in self.READMES: + if os.path.exists(f): + return + else: + self.warn( + "standard file not found: should have one of " + + ', '.join(self.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, 'rb') + 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() + + def check_license(self): + """Checks if license_file' is configured and adds it to + 'self.filelist' if the value contains a valid path. + """ + + opts = self.distribution.get_option_dict('metadata') + + # ignore the source of the value + _, license_file = opts.get('license_file', (None, None)) + + if license_file is None: + log.debug("'license_file' option was not specified") + return + + if not os.path.exists(license_file): + log.warn("warning: Failed to find the configured license file '%s'", + license_file) + return + + self.filelist.append(license_file) diff --git a/venv2/lib/python2.7/site-packages/setuptools/command/setopt.py b/venv2/lib/python2.7/site-packages/setuptools/command/setopt.py new file mode 100644 index 0000000..7e57cc0 --- /dev/null +++ b/venv2/lib/python2.7/site-packages/setuptools/command/setopt.py @@ -0,0 +1,149 @@ +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/venv2/lib/python2.7/site-packages/setuptools/command/test.py b/venv2/lib/python2.7/site-packages/setuptools/command/test.py new file mode 100644 index 0000000..973e4eb --- /dev/null +++ b/venv2/lib/python2.7/site-packages/setuptools/command/test.py @@ -0,0 +1,271 @@ +import os +import operator +import sys +import contextlib +import itertools +import unittest +from distutils.errors import DistutilsError, DistutilsOptionError +from distutils import log +from unittest import TestLoader + +from setuptools.extern import six +from setuptools.extern.six.moves import map, filter + +from pkg_resources import (resource_listdir, resource_exists, normalize_path, + working_set, _namespace_packages, evaluate_marker, + add_activation_listener, require, EntryPoint) +from setuptools import Command +from .build_py import _unique_everseen + +__metaclass__ = type + + +class ScanningLoader(TestLoader): + + def __init__(self): + TestLoader.__init__(self) + self._visited = set() + + 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. + """ + if module in self._visited: + return None + self._visited.add(module) + + 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: + 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', + "Run single test, case or suite (e.g. '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 not self.test_suite and sys.version_info >= (2, 7): + yield 'discover' + if self.verbose: + yield '--verbose' + if self.test_suite: + yield self.test_suite + + def with_project_on_sys_path(self, func): + """ + Backward compatibility for project_on_sys_path context. + """ + with self.project_on_sys_path(): + func() + + @contextlib.contextmanager + def project_on_sys_path(self, include_dists=[]): + 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: + project_path = normalize_path(ei_cmd.egg_base) + sys.path.insert(0, project_path) + working_set.__init__() + add_activation_listener(lambda dist: dist.activate()) + require('%s==%s' % (ei_cmd.egg_name, ei_cmd.egg_version)) + with self.paths_on_pythonpath([project_path]): + yield + finally: + sys.path[:] = old_path + sys.modules.clear() + sys.modules.update(old_modules) + working_set.__init__() + + @staticmethod + @contextlib.contextmanager + def paths_on_pythonpath(paths): + """ + Add the indicated paths to the head of the PYTHONPATH environment + variable so that subprocesses will also see the packages at + these paths. + + Do this in a context that restores the value on exit. + """ + nothing = object() + orig_pythonpath = os.environ.get('PYTHONPATH', nothing) + current_pythonpath = os.environ.get('PYTHONPATH', '') + try: + prefix = os.pathsep.join(_unique_everseen(paths)) + to_join = filter(None, [prefix, current_pythonpath]) + new_path = os.pathsep.join(to_join) + if new_path: + os.environ['PYTHONPATH'] = new_path + yield + finally: + if orig_pythonpath is nothing: + os.environ.pop('PYTHONPATH', None) + else: + os.environ['PYTHONPATH'] = orig_pythonpath + + @staticmethod + def install_dists(dist): + """ + Install the requirements indicated by self.distribution and + return an iterable of the dists that were built. + """ + ir_d = dist.fetch_build_eggs(dist.install_requires) + tr_d = dist.fetch_build_eggs(dist.tests_require or []) + er_d = dist.fetch_build_eggs( + v for k, v in dist.extras_require.items() + if k.startswith(':') and evaluate_marker(k[1:]) + ) + return itertools.chain(ir_d, tr_d, er_d) + + def run(self): + installed_dists = self.install_dists(self.distribution) + + cmd = ' '.join(self._argv) + if self.dry_run: + self.announce('skipping "%s" (dry run)' % cmd) + return + + self.announce('running "%s"' % cmd) + + paths = map(operator.attrgetter('location'), installed_dists) + with self.paths_on_pythonpath(paths): + with self.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)) + + test = unittest.main( + None, None, self._argv, + testLoader=self._resolve_as_ep(self.test_loader), + testRunner=self._resolve_as_ep(self.test_runner), + exit=False, + ) + if not test.result.wasSuccessful(): + msg = 'Test failed: %s' % test.result + self.announce(msg, log.ERROR) + raise DistutilsError(msg) + + @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/venv2/lib/python2.7/site-packages/setuptools/command/upload.py b/venv2/lib/python2.7/site-packages/setuptools/command/upload.py new file mode 100644 index 0000000..6db8888 --- /dev/null +++ b/venv2/lib/python2.7/site-packages/setuptools/command/upload.py @@ -0,0 +1,196 @@ +import io +import os +import hashlib +import getpass + +from base64 import standard_b64encode + +from distutils import log +from distutils.command import upload as orig +from distutils.spawn import spawn + +from distutils.errors import DistutilsError + +from setuptools.extern.six.moves.urllib.request import urlopen, Request +from setuptools.extern.six.moves.urllib.error import HTTPError +from setuptools.extern.six.moves.urllib.parse import urlparse + + +class upload(orig.upload): + """ + Override default upload behavior to obtain password + in a variety of different ways. + """ + def run(self): + try: + orig.upload.run(self) + finally: + self.announce( + "WARNING: Uploading via this command is deprecated, use twine " + "to upload instead (https://pypi.org/p/twine/)", + log.WARN + ) + + def finalize_options(self): + orig.upload.finalize_options(self) + self.username = ( + self.username or + getpass.getuser() + ) + # Attempt to obtain password. Short circuit evaluation at the first + # sign of success. + self.password = ( + self.password or + self._load_password_from_keyring() or + self._prompt_for_password() + ) + + def upload_file(self, command, pyversion, filename): + # Makes sure the repository URL is compliant + schema, netloc, url, params, query, fragments = \ + urlparse(self.repository) + if params or query or fragments: + raise AssertionError("Incompatible url %s" % self.repository) + + if schema not in ('http', 'https'): + raise AssertionError("unsupported schema " + schema) + + # Sign if requested + if self.sign: + gpg_args = ["gpg", "--detach-sign", "-a", filename] + if self.identity: + gpg_args[2:2] = ["--local-user", self.identity] + spawn(gpg_args, + dry_run=self.dry_run) + + # Fill in the data - send all the meta-data in case we need to + # register a new release + with open(filename, 'rb') as f: + content = f.read() + + meta = self.distribution.metadata + + data = { + # action + ':action': 'file_upload', + 'protocol_version': '1', + + # identify release + 'name': meta.get_name(), + 'version': meta.get_version(), + + # file content + 'content': (os.path.basename(filename), content), + 'filetype': command, + 'pyversion': pyversion, + 'md5_digest': hashlib.md5(content).hexdigest(), + + # additional meta-data + 'metadata_version': str(meta.get_metadata_version()), + 'summary': meta.get_description(), + 'home_page': meta.get_url(), + 'author': meta.get_contact(), + 'author_email': meta.get_contact_email(), + 'license': meta.get_licence(), + 'description': meta.get_long_description(), + 'keywords': meta.get_keywords(), + 'platform': meta.get_platforms(), + 'classifiers': meta.get_classifiers(), + 'download_url': meta.get_download_url(), + # PEP 314 + 'provides': meta.get_provides(), + 'requires': meta.get_requires(), + 'obsoletes': meta.get_obsoletes(), + } + + data['comment'] = '' + + if self.sign: + data['gpg_signature'] = (os.path.basename(filename) + ".asc", + open(filename+".asc", "rb").read()) + + # set up the authentication + user_pass = (self.username + ":" + self.password).encode('ascii') + # The exact encoding of the authentication string is debated. + # Anyway PyPI only accepts ascii for both username or password. + auth = "Basic " + standard_b64encode(user_pass).decode('ascii') + + # Build up the MIME payload for the POST data + boundary = '--------------GHSKFJDLGDS7543FJKLFHRE75642756743254' + sep_boundary = b'\r\n--' + boundary.encode('ascii') + end_boundary = sep_boundary + b'--\r\n' + body = io.BytesIO() + for key, value in data.items(): + title = '\r\nContent-Disposition: form-data; name="%s"' % key + # handle multiple entries for the same name + if not isinstance(value, list): + value = [value] + for value in value: + if type(value) is tuple: + title += '; filename="%s"' % value[0] + value = value[1] + else: + value = str(value).encode('utf-8') + body.write(sep_boundary) + body.write(title.encode('utf-8')) + body.write(b"\r\n\r\n") + body.write(value) + body.write(end_boundary) + body = body.getvalue() + + msg = "Submitting %s to %s" % (filename, self.repository) + self.announce(msg, log.INFO) + + # build the Request + headers = { + 'Content-type': 'multipart/form-data; boundary=%s' % boundary, + 'Content-length': str(len(body)), + 'Authorization': auth, + } + + request = Request(self.repository, data=body, + headers=headers) + # send the data + try: + result = urlopen(request) + status = result.getcode() + reason = result.msg + except HTTPError as e: + status = e.code + reason = e.msg + except OSError as e: + self.announce(str(e), log.ERROR) + raise + + if status == 200: + self.announce('Server response (%s): %s' % (status, reason), + log.INFO) + if self.show_response: + text = getattr(self, '_read_pypi_response', + lambda x: None)(result) + if text is not None: + msg = '\n'.join(('-' * 75, text, '-' * 75)) + self.announce(msg, log.INFO) + else: + msg = 'Upload failed (%s): %s' % (status, reason) + self.announce(msg, log.ERROR) + raise DistutilsError(msg) + + def _load_password_from_keyring(self): + """ + Attempt to load password from keyring. Suppress Exceptions. + """ + try: + keyring = __import__('keyring') + return keyring.get_password(self.repository, self.username) + except Exception: + pass + + def _prompt_for_password(self): + """ + Prompt for a password on the tty. Suppress Exceptions. + """ + try: + return getpass.getpass() + except (Exception, KeyboardInterrupt): + pass diff --git a/venv2/lib/python2.7/site-packages/setuptools/command/upload_docs.py b/venv2/lib/python2.7/site-packages/setuptools/command/upload_docs.py new file mode 100644 index 0000000..07aa564 --- /dev/null +++ b/venv2/lib/python2.7/site-packages/setuptools/command/upload_docs.py @@ -0,0 +1,206 @@ +# -*- 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 +import itertools +import functools + +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 + + +def _encode(s): + errors = 'surrogateescape' if six.PY3 else 'strict' + return s.encode('utf-8', errors) + + +class upload_docs(upload): + # override the default repository as upload_docs isn't + # supported by Warehouse (and won't be). + DEFAULT_REPOSITORY = 'https://pypi.python.org/pypi/' + + 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 + if 'pypi.python.org' in self.repository: + log.warn("Upload_docs command is deprecated. Use RTD instead.") + 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: + tmpl = "no files found in upload directory '%s'" + raise DistutilsOptionError(tmpl % 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) + + @staticmethod + def _build_part(item, sep_boundary): + key, values = item + 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 isinstance(value, tuple): + title += '; filename="%s"' % value[0] + value = value[1] + else: + value = _encode(value) + yield sep_boundary + yield _encode(title) + yield b"\n\n" + yield value + if value and value[-1:] == b'\r': + yield b'\n' # write an extra newline (lurve Macs) + + @classmethod + def _build_multipart(cls, data): + """ + Build up the MIME payload for the POST data + """ + boundary = b'--------------GHSKFJDLGDS7543FJKLFHRE75642756743254' + sep_boundary = b'\n--' + boundary + end_boundary = sep_boundary + b'--' + end_items = end_boundary, b"\n", + builder = functools.partial( + cls._build_part, + sep_boundary=sep_boundary, + ) + part_groups = map(builder, data.items()) + parts = itertools.chain.from_iterable(part_groups) + body_items = itertools.chain(parts, end_items) + content_type = 'multipart/form-data; boundary=%s' % boundary.decode('ascii') + return b''.join(body_items), content_type + + def upload_file(self, filename): + with open(filename, 'rb') as f: + content = f.read() + meta = self.distribution.metadata + data = { + ':action': 'doc_upload', + 'name': meta.get_name(), + 'content': (os.path.basename(filename), content), + } + # set up the authentication + credentials = _encode(self.username + ':' + self.password) + credentials = standard_b64encode(credentials) + if six.PY3: + credentials = credentials.decode('ascii') + auth = "Basic " + credentials + + body, ct = self._build_multipart(data) + + msg = "Submitting documentation to %s" % (self.repository) + self.announce(msg, 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 = ct + 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: + msg = 'Server response (%s): %s' % (r.status, r.reason) + self.announce(msg, log.INFO) + elif r.status == 301: + location = r.getheader('Location') + if location is None: + location = 'https://pythonhosted.org/%s/' % meta.get_name() + msg = 'Upload successful. Visit %s' % location + self.announce(msg, log.INFO) + else: + msg = 'Upload failed (%s): %s' % (r.status, r.reason) + self.announce(msg, log.ERROR) + if self.show_response: + print('-' * 75, r.read(), '-' * 75) diff --git a/venv2/lib/python2.7/site-packages/setuptools/config.py b/venv2/lib/python2.7/site-packages/setuptools/config.py new file mode 100644 index 0000000..b662604 --- /dev/null +++ b/venv2/lib/python2.7/site-packages/setuptools/config.py @@ -0,0 +1,656 @@ +from __future__ import absolute_import, unicode_literals +import io +import os +import sys + +import warnings +import functools +from collections import defaultdict +from functools import partial +from functools import wraps +from importlib import import_module + +from distutils.errors import DistutilsOptionError, DistutilsFileError +from setuptools.extern.packaging.version import LegacyVersion, parse +from setuptools.extern.six import string_types, PY3 + + +__metaclass__ = type + + +def read_configuration( + filepath, find_others=False, ignore_option_errors=False): + """Read given configuration file and returns options from it as a dict. + + :param str|unicode filepath: Path to configuration file + to get options from. + + :param bool find_others: Whether to search for other configuration files + which could be on in various places. + + :param bool ignore_option_errors: Whether to silently ignore + options, values of which could not be resolved (e.g. due to exceptions + in directives such as file:, attr:, etc.). + If False exceptions are propagated as expected. + + :rtype: dict + """ + from setuptools.dist import Distribution, _Distribution + + filepath = os.path.abspath(filepath) + + if not os.path.isfile(filepath): + raise DistutilsFileError( + 'Configuration file %s does not exist.' % filepath) + + current_directory = os.getcwd() + os.chdir(os.path.dirname(filepath)) + + try: + dist = Distribution() + + filenames = dist.find_config_files() if find_others else [] + if filepath not in filenames: + filenames.append(filepath) + + _Distribution.parse_config_files(dist, filenames=filenames) + + handlers = parse_configuration( + dist, dist.command_options, + ignore_option_errors=ignore_option_errors) + + finally: + os.chdir(current_directory) + + return configuration_to_dict(handlers) + + +def _get_option(target_obj, key): + """ + Given a target object and option key, get that option from + the target object, either through a get_{key} method or + from an attribute directly. + """ + getter_name = 'get_{key}'.format(**locals()) + by_attribute = functools.partial(getattr, target_obj, key) + getter = getattr(target_obj, getter_name, by_attribute) + return getter() + + +def configuration_to_dict(handlers): + """Returns configuration data gathered by given handlers as a dict. + + :param list[ConfigHandler] handlers: Handlers list, + usually from parse_configuration() + + :rtype: dict + """ + config_dict = defaultdict(dict) + + for handler in handlers: + for option in handler.set_options: + value = _get_option(handler.target_obj, option) + config_dict[handler.section_prefix][option] = value + + return config_dict + + +def parse_configuration( + distribution, command_options, ignore_option_errors=False): + """Performs additional parsing of configuration options + for a distribution. + + Returns a list of used option handlers. + + :param Distribution distribution: + :param dict command_options: + :param bool ignore_option_errors: Whether to silently ignore + options, values of which could not be resolved (e.g. due to exceptions + in directives such as file:, attr:, etc.). + If False exceptions are propagated as expected. + :rtype: list + """ + options = ConfigOptionsHandler( + distribution, command_options, ignore_option_errors) + options.parse() + + meta = ConfigMetadataHandler( + distribution.metadata, command_options, ignore_option_errors, + distribution.package_dir) + meta.parse() + + return meta, options + + +class ConfigHandler: + """Handles metadata supplied in configuration files.""" + + section_prefix = None + """Prefix for config sections handled by this handler. + Must be provided by class heirs. + + """ + + aliases = {} + """Options aliases. + For compatibility with various packages. E.g.: d2to1 and pbr. + Note: `-` in keys is replaced with `_` by config parser. + + """ + + def __init__(self, target_obj, options, ignore_option_errors=False): + sections = {} + + section_prefix = self.section_prefix + for section_name, section_options in options.items(): + if not section_name.startswith(section_prefix): + continue + + section_name = section_name.replace(section_prefix, '').strip('.') + sections[section_name] = section_options + + self.ignore_option_errors = ignore_option_errors + self.target_obj = target_obj + self.sections = sections + self.set_options = [] + + @property + def parsers(self): + """Metadata item name to parser function mapping.""" + raise NotImplementedError( + '%s must provide .parsers property' % self.__class__.__name__) + + def __setitem__(self, option_name, value): + unknown = tuple() + target_obj = self.target_obj + + # Translate alias into real name. + option_name = self.aliases.get(option_name, option_name) + + current_value = getattr(target_obj, option_name, unknown) + + if current_value is unknown: + raise KeyError(option_name) + + if current_value: + # Already inhabited. Skipping. + return + + skip_option = False + parser = self.parsers.get(option_name) + if parser: + try: + value = parser(value) + + except Exception: + skip_option = True + if not self.ignore_option_errors: + raise + + if skip_option: + return + + setter = getattr(target_obj, 'set_%s' % option_name, None) + if setter is None: + setattr(target_obj, option_name, value) + else: + setter(value) + + self.set_options.append(option_name) + + @classmethod + def _parse_list(cls, value, separator=','): + """Represents value as a list. + + Value is split either by separator (defaults to comma) or by lines. + + :param value: + :param separator: List items separator character. + :rtype: list + """ + if isinstance(value, list): # _get_parser_compound case + return value + + if '\n' in value: + value = value.splitlines() + else: + value = value.split(separator) + + return [chunk.strip() for chunk in value if chunk.strip()] + + @classmethod + def _parse_dict(cls, value): + """Represents value as a dict. + + :param value: + :rtype: dict + """ + separator = '=' + result = {} + for line in cls._parse_list(value): + key, sep, val = line.partition(separator) + if sep != separator: + raise DistutilsOptionError( + 'Unable to parse option value to dict: %s' % value) + result[key.strip()] = val.strip() + + return result + + @classmethod + def _parse_bool(cls, value): + """Represents value as boolean. + + :param value: + :rtype: bool + """ + value = value.lower() + return value in ('1', 'true', 'yes') + + @classmethod + def _exclude_files_parser(cls, key): + """Returns a parser function to make sure field inputs + are not files. + + Parses a value after getting the key so error messages are + more informative. + + :param key: + :rtype: callable + """ + def parser(value): + exclude_directive = 'file:' + if value.startswith(exclude_directive): + raise ValueError( + 'Only strings are accepted for the {0} field, ' + 'files are not accepted'.format(key)) + return value + return parser + + @classmethod + def _parse_file(cls, value): + """Represents value as a string, allowing including text + from nearest files using `file:` directive. + + Directive is sandboxed and won't reach anything outside + directory with setup.py. + + Examples: + file: README.rst, CHANGELOG.md, src/file.txt + + :param str value: + :rtype: str + """ + include_directive = 'file:' + + if not isinstance(value, string_types): + return value + + if not value.startswith(include_directive): + return value + + spec = value[len(include_directive):] + filepaths = (os.path.abspath(path.strip()) for path in spec.split(',')) + return '\n'.join( + cls._read_file(path) + for path in filepaths + if (cls._assert_local(path) or True) + and os.path.isfile(path) + ) + + @staticmethod + def _assert_local(filepath): + if not filepath.startswith(os.getcwd()): + raise DistutilsOptionError( + '`file:` directive can not access %s' % filepath) + + @staticmethod + def _read_file(filepath): + with io.open(filepath, encoding='utf-8') as f: + return f.read() + + @classmethod + def _parse_attr(cls, value, package_dir=None): + """Represents value as a module attribute. + + Examples: + attr: package.attr + attr: package.module.attr + + :param str value: + :rtype: str + """ + attr_directive = 'attr:' + if not value.startswith(attr_directive): + return value + + attrs_path = value.replace(attr_directive, '').strip().split('.') + attr_name = attrs_path.pop() + + module_name = '.'.join(attrs_path) + module_name = module_name or '__init__' + + parent_path = os.getcwd() + if package_dir: + if attrs_path[0] in package_dir: + # A custom path was specified for the module we want to import + custom_path = package_dir[attrs_path[0]] + parts = custom_path.rsplit('/', 1) + if len(parts) > 1: + parent_path = os.path.join(os.getcwd(), parts[0]) + module_name = parts[1] + else: + module_name = custom_path + elif '' in package_dir: + # A custom parent directory was specified for all root modules + parent_path = os.path.join(os.getcwd(), package_dir['']) + sys.path.insert(0, parent_path) + try: + module = import_module(module_name) + value = getattr(module, attr_name) + + finally: + sys.path = sys.path[1:] + + return value + + @classmethod + def _get_parser_compound(cls, *parse_methods): + """Returns parser function to represents value as a list. + + Parses a value applying given methods one after another. + + :param parse_methods: + :rtype: callable + """ + def parse(value): + parsed = value + + for method in parse_methods: + parsed = method(parsed) + + return parsed + + return parse + + @classmethod + def _parse_section_to_dict(cls, section_options, values_parser=None): + """Parses section options into a dictionary. + + Optionally applies a given parser to values. + + :param dict section_options: + :param callable values_parser: + :rtype: dict + """ + value = {} + values_parser = values_parser or (lambda val: val) + for key, (_, val) in section_options.items(): + value[key] = values_parser(val) + return value + + def parse_section(self, section_options): + """Parses configuration file section. + + :param dict section_options: + """ + for (name, (_, value)) in section_options.items(): + try: + self[name] = value + + except KeyError: + pass # Keep silent for a new option may appear anytime. + + def parse(self): + """Parses configuration file items from one + or more related sections. + + """ + for section_name, section_options in self.sections.items(): + + method_postfix = '' + if section_name: # [section.option] variant + method_postfix = '_%s' % section_name + + section_parser_method = getattr( + self, + # Dots in section names are translated into dunderscores. + ('parse_section%s' % method_postfix).replace('.', '__'), + None) + + if section_parser_method is None: + raise DistutilsOptionError( + 'Unsupported distribution option section: [%s.%s]' % ( + self.section_prefix, section_name)) + + section_parser_method(section_options) + + def _deprecated_config_handler(self, func, msg, warning_class): + """ this function will wrap around parameters that are deprecated + + :param msg: deprecation message + :param warning_class: class of warning exception to be raised + :param func: function to be wrapped around + """ + @wraps(func) + def config_handler(*args, **kwargs): + warnings.warn(msg, warning_class) + return func(*args, **kwargs) + + return config_handler + + +class ConfigMetadataHandler(ConfigHandler): + + section_prefix = 'metadata' + + aliases = { + 'home_page': 'url', + 'summary': 'description', + 'classifier': 'classifiers', + 'platform': 'platforms', + } + + strict_mode = False + """We need to keep it loose, to be partially compatible with + `pbr` and `d2to1` packages which also uses `metadata` section. + + """ + + def __init__(self, target_obj, options, ignore_option_errors=False, + package_dir=None): + super(ConfigMetadataHandler, self).__init__(target_obj, options, + ignore_option_errors) + self.package_dir = package_dir + + @property + def parsers(self): + """Metadata item name to parser function mapping.""" + parse_list = self._parse_list + parse_file = self._parse_file + parse_dict = self._parse_dict + exclude_files_parser = self._exclude_files_parser + + return { + 'platforms': parse_list, + 'keywords': parse_list, + 'provides': parse_list, + 'requires': self._deprecated_config_handler( + parse_list, + "The requires parameter is deprecated, please use " + "install_requires for runtime dependencies.", + DeprecationWarning), + 'obsoletes': parse_list, + 'classifiers': self._get_parser_compound(parse_file, parse_list), + 'license': exclude_files_parser('license'), + 'description': parse_file, + 'long_description': parse_file, + 'version': self._parse_version, + 'project_urls': parse_dict, + } + + def _parse_version(self, value): + """Parses `version` option value. + + :param value: + :rtype: str + + """ + version = self._parse_file(value) + + if version != value: + version = version.strip() + # Be strict about versions loaded from file because it's easy to + # accidentally include newlines and other unintended content + if isinstance(parse(version), LegacyVersion): + tmpl = ( + 'Version loaded from {value} does not ' + 'comply with PEP 440: {version}' + ) + raise DistutilsOptionError(tmpl.format(**locals())) + + return version + + version = self._parse_attr(value, self.package_dir) + + if callable(version): + version = version() + + if not isinstance(version, string_types): + if hasattr(version, '__iter__'): + version = '.'.join(map(str, version)) + else: + version = '%s' % version + + return version + + +class ConfigOptionsHandler(ConfigHandler): + + section_prefix = 'options' + + @property + def parsers(self): + """Metadata item name to parser function mapping.""" + parse_list = self._parse_list + parse_list_semicolon = partial(self._parse_list, separator=';') + parse_bool = self._parse_bool + parse_dict = self._parse_dict + + return { + 'zip_safe': parse_bool, + 'use_2to3': parse_bool, + 'include_package_data': parse_bool, + 'package_dir': parse_dict, + 'use_2to3_fixers': parse_list, + 'use_2to3_exclude_fixers': parse_list, + 'convert_2to3_doctests': parse_list, + 'scripts': parse_list, + 'eager_resources': parse_list, + 'dependency_links': parse_list, + 'namespace_packages': parse_list, + 'install_requires': parse_list_semicolon, + 'setup_requires': parse_list_semicolon, + 'tests_require': parse_list_semicolon, + 'packages': self._parse_packages, + 'entry_points': self._parse_file, + 'py_modules': parse_list, + } + + def _parse_packages(self, value): + """Parses `packages` option value. + + :param value: + :rtype: list + """ + find_directives = ['find:', 'find_namespace:'] + trimmed_value = value.strip() + + if trimmed_value not in find_directives: + return self._parse_list(value) + + findns = trimmed_value == find_directives[1] + if findns and not PY3: + raise DistutilsOptionError( + 'find_namespace: directive is unsupported on Python < 3.3') + + # Read function arguments from a dedicated section. + find_kwargs = self.parse_section_packages__find( + self.sections.get('packages.find', {})) + + if findns: + from setuptools import find_namespace_packages as find_packages + else: + from setuptools import find_packages + + return find_packages(**find_kwargs) + + def parse_section_packages__find(self, section_options): + """Parses `packages.find` configuration file section. + + To be used in conjunction with _parse_packages(). + + :param dict section_options: + """ + section_data = self._parse_section_to_dict( + section_options, self._parse_list) + + valid_keys = ['where', 'include', 'exclude'] + + find_kwargs = dict( + [(k, v) for k, v in section_data.items() if k in valid_keys and v]) + + where = find_kwargs.get('where') + if where is not None: + find_kwargs['where'] = where[0] # cast list to single val + + return find_kwargs + + def parse_section_entry_points(self, section_options): + """Parses `entry_points` configuration file section. + + :param dict section_options: + """ + parsed = self._parse_section_to_dict(section_options, self._parse_list) + self['entry_points'] = parsed + + def _parse_package_data(self, section_options): + parsed = self._parse_section_to_dict(section_options, self._parse_list) + + root = parsed.get('*') + if root: + parsed[''] = root + del parsed['*'] + + return parsed + + def parse_section_package_data(self, section_options): + """Parses `package_data` configuration file section. + + :param dict section_options: + """ + self['package_data'] = self._parse_package_data(section_options) + + def parse_section_exclude_package_data(self, section_options): + """Parses `exclude_package_data` configuration file section. + + :param dict section_options: + """ + self['exclude_package_data'] = self._parse_package_data( + section_options) + + def parse_section_extras_require(self, section_options): + """Parses `extras_require` configuration file section. + + :param dict section_options: + """ + parse_list = partial(self._parse_list, separator=';') + self['extras_require'] = self._parse_section_to_dict( + section_options, parse_list) + + def parse_section_data_files(self, section_options): + """Parses `data_files` configuration file section. + + :param dict section_options: + """ + parsed = self._parse_section_to_dict(section_options, self._parse_list) + self['data_files'] = [(k, v) for k, v in parsed.items()] diff --git a/venv2/lib/python2.7/site-packages/setuptools/dep_util.py b/venv2/lib/python2.7/site-packages/setuptools/dep_util.py new file mode 100644 index 0000000..2931c13 --- /dev/null +++ b/venv2/lib/python2.7/site-packages/setuptools/dep_util.py @@ -0,0 +1,23 @@ +from distutils.dep_util import newer_group + +# yes, this is was almost entirely copy-pasted from +# 'newer_pairwise()', this is just another convenience +# function. +def newer_pairwise_group(sources_groups, targets): + """Walk both arguments in parallel, testing if each source group is newer + than its corresponding target. Returns a pair of lists (sources_groups, + targets) where sources is newer than target, according to the semantics + of 'newer_group()'. + """ + if len(sources_groups) != len(targets): + raise ValueError("'sources_group' and 'targets' must be the same length") + + # build a pair of lists (sources_groups, targets) where source is newer + n_sources = [] + n_targets = [] + for i in range(len(sources_groups)): + if newer_group(sources_groups[i], targets[i]): + n_sources.append(sources_groups[i]) + n_targets.append(targets[i]) + + return n_sources, n_targets diff --git a/venv2/lib/python2.7/site-packages/setuptools/depends.py b/venv2/lib/python2.7/site-packages/setuptools/depends.py new file mode 100644 index 0000000..45e7052 --- /dev/null +++ b/venv2/lib/python2.7/site-packages/setuptools/depends.py @@ -0,0 +1,186 @@ +import sys +import imp +import marshal +from distutils.version import StrictVersion +from imp import PKG_DIRECTORY, PY_COMPILED, PY_SOURCE, PY_FROZEN + +from .py33compat import Bytecode + + +__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 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 assignment + return None + + name_idx = list(code.co_names).index(symbol) + + STORE_NAME = 90 + STORE_GLOBAL = 97 + LOAD_CONST = 100 + + const = default + + for byte_code in Bytecode(code): + op = byte_code.opcode + arg = byte_code.arg + + 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/venv2/lib/python2.7/site-packages/setuptools/dist.py b/venv2/lib/python2.7/site-packages/setuptools/dist.py new file mode 100644 index 0000000..9a165de --- /dev/null +++ b/venv2/lib/python2.7/site-packages/setuptools/dist.py @@ -0,0 +1,1278 @@ +# -*- coding: utf-8 -*- +__all__ = ['Distribution'] + +import io +import sys +import re +import os +import warnings +import numbers +import distutils.log +import distutils.core +import distutils.cmd +import distutils.dist +from distutils.util import strtobool +from distutils.debug import DEBUG +from distutils.fancy_getopt import translate_longopt +import itertools + +from collections import defaultdict +from email import message_from_file + +from distutils.errors import ( + DistutilsOptionError, DistutilsPlatformError, DistutilsSetupError, +) +from distutils.util import rfc822_escape +from distutils.version import StrictVersion + +from setuptools.extern import six +from setuptools.extern import packaging +from setuptools.extern.six.moves import map, filter, filterfalse + +from . import SetuptoolsDeprecationWarning + +from setuptools.depends import Require +from setuptools import windows_support +from setuptools.monkey import get_unpatched +from setuptools.config import parse_configuration +import pkg_resources + +__import__('setuptools.extern.packaging.specifiers') +__import__('setuptools.extern.packaging.version') + + +def _get_unpatched(cls): + warnings.warn("Do not call this function", DistDeprecationWarning) + return get_unpatched(cls) + + +def get_metadata_version(self): + mv = getattr(self, 'metadata_version', None) + + if mv is None: + if self.long_description_content_type or self.provides_extras: + mv = StrictVersion('2.1') + elif (self.maintainer is not None or + self.maintainer_email is not None or + getattr(self, 'python_requires', None) is not None): + mv = StrictVersion('1.2') + elif (self.provides or self.requires or self.obsoletes or + self.classifiers or self.download_url): + mv = StrictVersion('1.1') + else: + mv = StrictVersion('1.0') + + self.metadata_version = mv + + return mv + + +def read_pkg_file(self, file): + """Reads the metadata values from a file object.""" + msg = message_from_file(file) + + def _read_field(name): + value = msg[name] + if value == 'UNKNOWN': + return None + return value + + def _read_list(name): + values = msg.get_all(name, None) + if values == []: + return None + return values + + self.metadata_version = StrictVersion(msg['metadata-version']) + self.name = _read_field('name') + self.version = _read_field('version') + self.description = _read_field('summary') + # we are filling author only. + self.author = _read_field('author') + self.maintainer = None + self.author_email = _read_field('author-email') + self.maintainer_email = None + self.url = _read_field('home-page') + self.license = _read_field('license') + + if 'download-url' in msg: + self.download_url = _read_field('download-url') + else: + self.download_url = None + + self.long_description = _read_field('description') + self.description = _read_field('summary') + + if 'keywords' in msg: + self.keywords = _read_field('keywords').split(',') + + self.platforms = _read_list('platform') + self.classifiers = _read_list('classifier') + + # PEP 314 - these fields only exist in 1.1 + if self.metadata_version == StrictVersion('1.1'): + self.requires = _read_list('requires') + self.provides = _read_list('provides') + self.obsoletes = _read_list('obsoletes') + else: + self.requires = None + self.provides = None + self.obsoletes = None + + +# Based on Python 3.5 version +def write_pkg_file(self, file): + """Write the PKG-INFO format data to a file object. + """ + version = self.get_metadata_version() + + if six.PY2: + def write_field(key, value): + file.write("%s: %s\n" % (key, self._encode_field(value))) + else: + def write_field(key, value): + file.write("%s: %s\n" % (key, value)) + + write_field('Metadata-Version', str(version)) + write_field('Name', self.get_name()) + write_field('Version', self.get_version()) + write_field('Summary', self.get_description()) + write_field('Home-page', self.get_url()) + + if version < StrictVersion('1.2'): + write_field('Author', self.get_contact()) + write_field('Author-email', self.get_contact_email()) + else: + optional_fields = ( + ('Author', 'author'), + ('Author-email', 'author_email'), + ('Maintainer', 'maintainer'), + ('Maintainer-email', 'maintainer_email'), + ) + + for field, attr in optional_fields: + attr_val = getattr(self, attr) + + if attr_val is not None: + write_field(field, attr_val) + + write_field('License', self.get_license()) + if self.download_url: + write_field('Download-URL', self.download_url) + for project_url in self.project_urls.items(): + write_field('Project-URL', '%s, %s' % project_url) + + long_desc = rfc822_escape(self.get_long_description()) + write_field('Description', long_desc) + + keywords = ','.join(self.get_keywords()) + if keywords: + write_field('Keywords', keywords) + + if version >= StrictVersion('1.2'): + for platform in self.get_platforms(): + write_field('Platform', platform) + else: + self._write_list(file, 'Platform', self.get_platforms()) + + self._write_list(file, 'Classifier', self.get_classifiers()) + + # PEP 314 + self._write_list(file, 'Requires', self.get_requires()) + self._write_list(file, 'Provides', self.get_provides()) + self._write_list(file, 'Obsoletes', self.get_obsoletes()) + + # Setuptools specific for PEP 345 + if hasattr(self, 'python_requires'): + write_field('Requires-Python', self.python_requires) + + # PEP 566 + if self.long_description_content_type: + write_field( + 'Description-Content-Type', + self.long_description_content_type + ) + if self.provides_extras: + for extra in self.provides_extras: + write_field('Provides-Extra', extra) + + +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""" + ns_packages = value + assert_string_list(dist, attr, ns_packages) + for nsp in ns_packages: + if not dist.has_contents_for(nsp): + raise DistutilsSetupError( + "Distribution contains no modules or packages for " + + "namespace package %r" % nsp + ) + parent, sep, child = nsp.rpartition('.') + if parent and parent not in ns_packages: + 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: + list(itertools.starmap(_check_extra, value.items())) + 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 _check_extra(extra, reqs): + name, sep, marker = extra.partition(':') + if marker and pkg_resources.invalid_marker(marker): + raise DistutilsSetupError("Invalid environment marker: " + marker) + list(pkg_resources.parse_requirements(reqs)) + + +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)) + if isinstance(value, (dict, set)): + raise TypeError("Unordered types are not allowed") + 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_specifier(dist, attr, value): + """Verify that value is a valid version specifier""" + try: + packaging.specifiers.SpecifierSet(value) + except packaging.specifiers.InvalidSpecifier as error: + tmpl = ( + "{attr!r} must be a string " + "containing valid version 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 + ) + + +_Distribution = get_unpatched(distutils.core.Distribution) + + +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. + """ + + _DISTUTILS_UNSUPPORTED_METADATA = { + 'long_description_content_type': None, + 'project_urls': dict, + 'provides_extras': set, + } + + _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 = attrs or {} + if 'features' in attrs or 'require_features' in attrs: + Feature.warn_deprecated() + self.require_features = [] + self.features = {} + self.dist_files = [] + # Filter-out setuptools' specific options. + self.src_root = attrs.pop("src_root", None) + self.patch_missing_pkg_info(attrs) + self.dependency_links = attrs.pop('dependency_links', []) + self.setup_requires = attrs.pop('setup_requires', []) + for ep in pkg_resources.iter_entry_points('distutils.setup_keywords'): + vars(self).setdefault(ep.name, None) + _Distribution.__init__(self, { + k: v for k, v in attrs.items() + if k not in self._DISTUTILS_UNSUPPORTED_METADATA + }) + + # Fill-in missing metadata fields not supported by distutils. + # Note some fields may have been set by other tools (e.g. pbr) + # above; they are taken preferrentially to setup() arguments + for option, default in self._DISTUTILS_UNSUPPORTED_METADATA.items(): + for source in self.metadata.__dict__, attrs: + if option in source: + value = source[option] + break + else: + value = default() if default else None + setattr(self.metadata, option, value) + + 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 + ) + self._finalize_requires() + + def _finalize_requires(self): + """ + Set `metadata.python_requires` and fix environment markers + in `install_requires` and `extras_require`. + """ + if getattr(self, 'python_requires', None): + self.metadata.python_requires = self.python_requires + + if getattr(self, 'extras_require', None): + for extra in self.extras_require.keys(): + # Since this gets called multiple times at points where the + # keys have become 'converted' extras, ensure that we are only + # truly adding extras we haven't seen before here. + extra = extra.split(':')[0] + if extra: + self.metadata.provides_extras.add(extra) + + self._convert_extras_requirements() + self._move_install_requirements_markers() + + def _convert_extras_requirements(self): + """ + Convert requirements in `extras_require` of the form + `"extra": ["barbazquux; {marker}"]` to + `"extra:{marker}": ["barbazquux"]`. + """ + spec_ext_reqs = getattr(self, 'extras_require', None) or {} + self._tmp_extras_require = defaultdict(list) + for section, v in spec_ext_reqs.items(): + # Do not strip empty sections. + self._tmp_extras_require[section] + for r in pkg_resources.parse_requirements(v): + suffix = self._suffix_for(r) + self._tmp_extras_require[section + suffix].append(r) + + @staticmethod + def _suffix_for(req): + """ + For a requirement, return the 'extras_require' suffix for + that requirement. + """ + return ':' + str(req.marker) if req.marker else '' + + def _move_install_requirements_markers(self): + """ + Move requirements in `install_requires` that are using environment + markers `extras_require`. + """ + + # divide the install_requires into two sets, simple ones still + # handled by install_requires and more complex ones handled + # by extras_require. + + def is_simple_req(req): + return not req.marker + + spec_inst_reqs = getattr(self, 'install_requires', None) or () + inst_reqs = list(pkg_resources.parse_requirements(spec_inst_reqs)) + simple_reqs = filter(is_simple_req, inst_reqs) + complex_reqs = filterfalse(is_simple_req, inst_reqs) + self.install_requires = list(map(str, simple_reqs)) + + for r in complex_reqs: + self._tmp_extras_require[':' + str(r.marker)].append(r) + self.extras_require = dict( + (k, [str(r) for r in map(self._clean_req, v)]) + for k, v in self._tmp_extras_require.items() + ) + + def _clean_req(self, req): + """ + Given a Requirement, remove environment markers and return it. + """ + req.marker = None + return req + + def _parse_config_files(self, filenames=None): + """ + Adapted from distutils.dist.Distribution.parse_config_files, + this method provides the same functionality in subtly-improved + ways. + """ + from setuptools.extern.six.moves.configparser import ConfigParser + + # Ignore install directory options if we have a venv + if six.PY3 and sys.prefix != sys.base_prefix: + ignore_options = [ + 'install-base', 'install-platbase', 'install-lib', + 'install-platlib', 'install-purelib', 'install-headers', + 'install-scripts', 'install-data', 'prefix', 'exec-prefix', + 'home', 'user', 'root'] + else: + ignore_options = [] + + ignore_options = frozenset(ignore_options) + + if filenames is None: + filenames = self.find_config_files() + + if DEBUG: + self.announce("Distribution.parse_config_files():") + + parser = ConfigParser() + for filename in filenames: + with io.open(filename, encoding='utf-8') as reader: + if DEBUG: + self.announce(" reading {filename}".format(**locals())) + (parser.read_file if six.PY3 else parser.readfp)(reader) + for section in parser.sections(): + options = parser.options(section) + opt_dict = self.get_option_dict(section) + + for opt in options: + if opt != '__name__' and opt not in ignore_options: + val = self._try_str(parser.get(section, opt)) + opt = opt.replace('-', '_') + opt_dict[opt] = (filename, val) + + # Make the ConfigParser forget everything (so we retain + # the original filenames that options come from) + parser.__init__() + + # If there was a "global" section in the config file, use it + # to set Distribution options. + + if 'global' in self.command_options: + for (opt, (src, val)) in self.command_options['global'].items(): + alias = self.negative_opt.get(opt) + try: + if alias: + setattr(self, alias, not strtobool(val)) + elif opt in ('verbose', 'dry_run'): # ugh! + setattr(self, opt, strtobool(val)) + else: + setattr(self, opt, val) + except ValueError as msg: + raise DistutilsOptionError(msg) + + @staticmethod + def _try_str(val): + """ + On Python 2, much of distutils relies on string values being of + type 'str' (bytes) and not unicode text. If the value can be safely + encoded to bytes using the default encoding, prefer that. + + Why the default encoding? Because that value can be implicitly + decoded back to text if needed. + + Ref #1653 + """ + if six.PY3: + return val + try: + return val.encode() + except UnicodeEncodeError: + pass + return val + + def _set_command_options(self, command_obj, option_dict=None): + """ + Set the options for 'command_obj' from 'option_dict'. Basically + this means copying elements of a dictionary ('option_dict') to + attributes of an instance ('command'). + + 'command_obj' must be a Command instance. If 'option_dict' is not + supplied, uses the standard option dictionary for this command + (from 'self.command_options'). + + (Adopted from distutils.dist.Distribution._set_command_options) + """ + command_name = command_obj.get_command_name() + if option_dict is None: + option_dict = self.get_option_dict(command_name) + + if DEBUG: + self.announce(" setting options for '%s' command:" % command_name) + for (option, (source, value)) in option_dict.items(): + if DEBUG: + self.announce(" %s = %s (from %s)" % (option, value, + source)) + try: + bool_opts = [translate_longopt(o) + for o in command_obj.boolean_options] + except AttributeError: + bool_opts = [] + try: + neg_opt = command_obj.negative_opt + except AttributeError: + neg_opt = {} + + try: + is_string = isinstance(value, six.string_types) + if option in neg_opt and is_string: + setattr(command_obj, neg_opt[option], not strtobool(value)) + elif option in bool_opts and is_string: + setattr(command_obj, option, strtobool(value)) + elif hasattr(command_obj, option): + setattr(command_obj, option, value) + else: + raise DistutilsOptionError( + "error in %s: command '%s' has no such option '%s'" + % (source, command_name, option)) + except ValueError as msg: + raise DistutilsOptionError(msg) + + def parse_config_files(self, filenames=None, ignore_option_errors=False): + """Parses configuration files from various levels + and loads configuration. + + """ + self._parse_config_files(filenames=filenames) + + parse_configuration(self, self.command_options, + ignore_option_errors=ignore_option_errors) + self._finalize_requires() + + 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) + return resolved_dists + + 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""" + from setuptools.command.easy_install import easy_install + dist = self.__class__({'script_args': ['easy_install']}) + opts = dist.get_option_dict('easy_install') + opts.clear() + opts.update( + (k, v) + for k, v in self.get_option_dict('easy_install').items() + if k in ( + # don't use any other settings + 'find_links', 'site_dirs', 'index_url', + 'optimize', 'site_dirs', 'allow_hosts', + )) + if self.dependency_links: + links = self.dependency_links[:] + if 'find_links' in opts: + links = opts['find_links'][1] + 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() + 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 + + new = ( + ('with-' + name, None, 'include ' + descr + incdef), + ('without-' + name, None, 'exclude ' + descr + excdef), + ) + go.extend(new) + 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] + + eps = pkg_resources.iter_entry_points('distutils.commands', command) + for ep in eps: + 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.include(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: + new = [item for item in value if item not in old] + setattr(self, name, old + new) + + 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) + 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) + + +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(): + msg = ( + "Features are deprecated and will be removed in a future " + "version. See https://github.com/pypa/setuptools/issues/65." + ) + warnings.warn(msg, DistDeprecationWarning, 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) + ) + + +class DistDeprecationWarning(SetuptoolsDeprecationWarning): + """Class for warning about deprecations in dist in + setuptools. Not ignored by default, unlike DeprecationWarning.""" diff --git a/venv2/lib/python2.7/site-packages/setuptools/extension.py b/venv2/lib/python2.7/site-packages/setuptools/extension.py new file mode 100644 index 0000000..2946889 --- /dev/null +++ b/venv2/lib/python2.7/site-packages/setuptools/extension.py @@ -0,0 +1,57 @@ +import re +import functools +import distutils.core +import distutils.errors +import distutils.extension + +from setuptools.extern.six.moves import map + +from .monkey import get_unpatched + + +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 + +_Extension = get_unpatched(distutils.core.Extension) + + +class Extension(_Extension): + """Extension that uses '.c' files in place of '.pyx' files""" + + def __init__(self, name, sources, *args, **kw): + # The *args is needed for compatibility as calls may use positional + # arguments. py_limited_api may be set only via keyword. + self.py_limited_api = kw.pop("py_limited_api", False) + _Extension.__init__(self, name, sources, *args, **kw) + + 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""" diff --git a/venv2/lib/python2.7/site-packages/setuptools/extern/__init__.py b/venv2/lib/python2.7/site-packages/setuptools/extern/__init__.py new file mode 100644 index 0000000..cb2fa32 --- /dev/null +++ b/venv2/lib/python2.7/site-packages/setuptools/extern/__init__.py @@ -0,0 +1,73 @@ +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, ): + 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 = 'six', 'packaging', 'pyparsing', +VendorImporter(__name__, names, 'setuptools._vendor').install() diff --git a/venv2/lib/python2.7/site-packages/setuptools/glibc.py b/venv2/lib/python2.7/site-packages/setuptools/glibc.py new file mode 100644 index 0000000..a134591 --- /dev/null +++ b/venv2/lib/python2.7/site-packages/setuptools/glibc.py @@ -0,0 +1,86 @@ +# This file originally from pip: +# https://github.com/pypa/pip/blob/8f4f15a5a95d7d5b511ceaee9ed261176c181970/src/pip/_internal/utils/glibc.py +from __future__ import absolute_import + +import ctypes +import re +import warnings + + +def glibc_version_string(): + "Returns glibc version string, or None if not using glibc." + + # 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 None + + # 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") + + return version_str + + +# Separated out from have_compatible_glibc for easier unit testing +def check_glibc_version(version_str, required_major, minimum_minor): + # Parse string and check against requested version. + # + # We use a regexp instead of str.split because we want to discard any + # random junk that might come after the minor version -- this might happen + # in patched/forked versions of glibc (e.g. Linaro's version of glibc + # uses version strings like "2.20-2014.11"). See gh-3588. + m = re.match(r"(?P<major>[0-9]+)\.(?P<minor>[0-9]+)", version_str) + if not m: + warnings.warn("Expected glibc version with 2 components major.minor," + " got: %s" % version_str, RuntimeWarning) + return False + return (int(m.group("major")) == required_major and + int(m.group("minor")) >= minimum_minor) + + +def have_compatible_glibc(required_major, minimum_minor): + version_str = glibc_version_string() + if version_str is None: + return False + return check_glibc_version(version_str, required_major, minimum_minor) + + +# platform.libc_ver regularly returns completely nonsensical glibc +# versions. E.g. on my computer, platform says: +# +# ~$ python2.7 -c 'import platform; print(platform.libc_ver())' +# ('glibc', '2.7') +# ~$ python3.5 -c 'import platform; print(platform.libc_ver())' +# ('glibc', '2.9') +# +# But the truth is: +# +# ~$ ldd --version +# ldd (Debian GLIBC 2.22-11) 2.22 +# +# This is unfortunate, because it means that the linehaul data on libc +# versions that was generated by pip 8.1.2 and earlier is useless and +# misleading. Solution: instead of using platform, use our code that actually +# works. +def libc_ver(): + """Try to determine the glibc version + + Returns a tuple of strings (lib, version) which default to empty strings + in case the lookup fails. + """ + glibc_version = glibc_version_string() + if glibc_version is None: + return ("", "") + else: + return ("glibc", glibc_version) diff --git a/venv2/lib/python2.7/site-packages/setuptools/glob.py b/venv2/lib/python2.7/site-packages/setuptools/glob.py new file mode 100644 index 0000000..9d7cbc5 --- /dev/null +++ b/venv2/lib/python2.7/site-packages/setuptools/glob.py @@ -0,0 +1,174 @@ +""" +Filename globbing utility. Mostly a copy of `glob` from Python 3.5. + +Changes include: + * `yield from` and PEP3102 `*` removed. + * Hidden files are not ignored. +""" + +import os +import re +import fnmatch + +__all__ = ["glob", "iglob", "escape"] + + +def glob(pathname, recursive=False): + """Return a list of paths matching a pathname pattern. + + The pattern may contain simple shell-style wildcards a la + fnmatch. However, unlike fnmatch, filenames starting with a + dot are special cases that are not matched by '*' and '?' + patterns. + + If recursive is true, the pattern '**' will match any files and + zero or more directories and subdirectories. + """ + return list(iglob(pathname, recursive=recursive)) + + +def iglob(pathname, recursive=False): + """Return an iterator which yields the paths matching a pathname pattern. + + The pattern may contain simple shell-style wildcards a la + fnmatch. However, unlike fnmatch, filenames starting with a + dot are special cases that are not matched by '*' and '?' + patterns. + + If recursive is true, the pattern '**' will match any files and + zero or more directories and subdirectories. + """ + it = _iglob(pathname, recursive) + if recursive and _isrecursive(pathname): + s = next(it) # skip empty string + assert not s + return it + + +def _iglob(pathname, recursive): + dirname, basename = os.path.split(pathname) + if not has_magic(pathname): + if basename: + if os.path.lexists(pathname): + yield pathname + else: + # Patterns ending with a slash should match only directories + if os.path.isdir(dirname): + yield pathname + return + if not dirname: + if recursive and _isrecursive(basename): + for x in glob2(dirname, basename): + yield x + else: + for x in glob1(dirname, basename): + yield x + return + # `os.path.split()` returns the argument itself as a dirname if it is a + # drive or UNC path. Prevent an infinite recursion if a drive or UNC path + # contains magic characters (i.e. r'\\?\C:'). + if dirname != pathname and has_magic(dirname): + dirs = _iglob(dirname, recursive) + else: + dirs = [dirname] + if has_magic(basename): + if recursive and _isrecursive(basename): + glob_in_dir = glob2 + else: + glob_in_dir = glob1 + else: + glob_in_dir = glob0 + for dirname in dirs: + for name in glob_in_dir(dirname, basename): + yield os.path.join(dirname, name) + + +# These 2 helper functions non-recursively glob inside a literal directory. +# They return a list of basenames. `glob1` accepts a pattern while `glob0` +# takes a literal basename (so it only has to check for its existence). + + +def glob1(dirname, pattern): + if not dirname: + if isinstance(pattern, bytes): + dirname = os.curdir.encode('ASCII') + else: + dirname = os.curdir + try: + names = os.listdir(dirname) + except OSError: + return [] + return fnmatch.filter(names, pattern) + + +def glob0(dirname, basename): + if not basename: + # `os.path.split()` returns an empty basename for paths ending with a + # directory separator. 'q*x/' should match only directories. + if os.path.isdir(dirname): + return [basename] + else: + if os.path.lexists(os.path.join(dirname, basename)): + return [basename] + return [] + + +# This helper function recursively yields relative pathnames inside a literal +# directory. + + +def glob2(dirname, pattern): + assert _isrecursive(pattern) + yield pattern[:0] + for x in _rlistdir(dirname): + yield x + + +# Recursively yields relative pathnames inside a literal directory. +def _rlistdir(dirname): + if not dirname: + if isinstance(dirname, bytes): + dirname = os.curdir.encode('ASCII') + else: + dirname = os.curdir + try: + names = os.listdir(dirname) + except os.error: + return + for x in names: + yield x + path = os.path.join(dirname, x) if dirname else x + for y in _rlistdir(path): + yield os.path.join(x, y) + + +magic_check = re.compile('([*?[])') +magic_check_bytes = re.compile(b'([*?[])') + + +def has_magic(s): + if isinstance(s, bytes): + match = magic_check_bytes.search(s) + else: + match = magic_check.search(s) + return match is not None + + +def _isrecursive(pattern): + if isinstance(pattern, bytes): + return pattern == b'**' + else: + return pattern == '**' + + +def escape(pathname): + """Escape all special characters. + """ + # Escaping is done by wrapping any of "*?[" between square brackets. + # Metacharacters do not work in the drive part and shouldn't be escaped. + drive, pathname = os.path.splitdrive(pathname) + if isinstance(pathname, bytes): + pathname = magic_check_bytes.sub(br'[\1]', pathname) + else: + pathname = magic_check.sub(r'[\1]', pathname) + return drive + pathname diff --git a/venv2/lib/python2.7/site-packages/setuptools/gui-32.exe b/venv2/lib/python2.7/site-packages/setuptools/gui-32.exe new file mode 100644 index 0000000000000000000000000000000000000000..f8d3509653ba8f80ca7f3aa7f95616142ba83a94 GIT binary patch literal 65536 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 literal 0 HcmV?d00001 diff --git a/venv2/lib/python2.7/site-packages/setuptools/gui-64.exe b/venv2/lib/python2.7/site-packages/setuptools/gui-64.exe new file mode 100644 index 0000000000000000000000000000000000000000..330c51a5dde15a0bb610a48cd0ca11770c914dae GIT binary patch literal 75264 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 literal 0 HcmV?d00001 diff --git a/venv2/lib/python2.7/site-packages/setuptools/gui.exe b/venv2/lib/python2.7/site-packages/setuptools/gui.exe new file mode 100644 index 0000000000000000000000000000000000000000..f8d3509653ba8f80ca7f3aa7f95616142ba83a94 GIT binary patch literal 65536 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 literal 0 HcmV?d00001 diff --git a/venv2/lib/python2.7/site-packages/setuptools/launch.py b/venv2/lib/python2.7/site-packages/setuptools/launch.py new file mode 100644 index 0000000..308283e --- /dev/null +++ b/venv2/lib/python2.7/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/venv2/lib/python2.7/site-packages/setuptools/lib2to3_ex.py b/venv2/lib/python2.7/site-packages/setuptools/lib2to3_ex.py new file mode 100644 index 0000000..4b1a73f --- /dev/null +++ b/venv2/lib/python2.7/site-packages/setuptools/lib2to3_ex.py @@ -0,0 +1,62 @@ +""" +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/venv2/lib/python2.7/site-packages/setuptools/monkey.py b/venv2/lib/python2.7/site-packages/setuptools/monkey.py new file mode 100644 index 0000000..3c77f8c --- /dev/null +++ b/venv2/lib/python2.7/site-packages/setuptools/monkey.py @@ -0,0 +1,179 @@ +""" +Monkey patching of distutils. +""" + +import sys +import distutils.filelist +import platform +import types +import functools +from importlib import import_module +import inspect + +from setuptools.extern import six + +import setuptools + +__all__ = [] +""" +Everything is private. Contact the project team +if you think you need this functionality. +""" + + +def _get_mro(cls): + """ + Returns the bases classes for cls sorted by the MRO. + + Works around an issue on Jython where inspect.getmro will not return all + base classes if multiple classes share the same name. Instead, this + function will return a tuple containing the class itself, and the contents + of cls.__bases__. See https://github.com/pypa/setuptools/issues/1024. + """ + if platform.python_implementation() == "Jython": + return (cls,) + cls.__bases__ + return inspect.getmro(cls) + + +def get_unpatched(item): + lookup = ( + get_unpatched_class if isinstance(item, six.class_types) else + get_unpatched_function if isinstance(item, types.FunctionType) else + lambda item: None + ) + return lookup(item) + + +def get_unpatched_class(cls): + """Protect against re-patching the distutils if reloaded + + Also ensures that no other distutils extension monkeypatched the distutils + first. + """ + external_bases = ( + cls + for cls in _get_mro(cls) + if not cls.__module__.startswith('setuptools') + ) + base = next(external_bases) + if not base.__module__.startswith('distutils'): + msg = "distutils has already been patched by %r" % cls + raise AssertionError(msg) + return base + + +def patch_all(): + # we can't patch distutils.cmd, alas + distutils.core.Command = setuptools.Command + + has_issue_12885 = sys.version_info <= (3, 5, 3) + + if has_issue_12885: + # fix findall bug in distutils (http://bugs.python.org/issue12885) + distutils.filelist.findall = setuptools.findall + + needs_warehouse = ( + sys.version_info < (2, 7, 13) + or + (3, 4) < sys.version_info < (3, 4, 6) + or + (3, 5) < sys.version_info <= (3, 5, 3) + ) + + if needs_warehouse: + warehouse = 'https://upload.pypi.org/legacy/' + distutils.config.PyPIRCCommand.DEFAULT_REPOSITORY = warehouse + + _patch_distribution_metadata() + + # Install Distribution throughout the distutils + for module in distutils.dist, distutils.core, distutils.cmd: + module.Distribution = setuptools.dist.Distribution + + # Install the patched Extension + distutils.core.Extension = setuptools.extension.Extension + distutils.extension.Extension = setuptools.extension.Extension + if 'distutils.command.build_ext' in sys.modules: + sys.modules['distutils.command.build_ext'].Extension = ( + setuptools.extension.Extension + ) + + patch_for_msvc_specialized_compiler() + + +def _patch_distribution_metadata(): + """Patch write_pkg_file and read_pkg_file for higher metadata standards""" + for attr in ('write_pkg_file', 'read_pkg_file', 'get_metadata_version'): + new_val = getattr(setuptools.dist, attr) + setattr(distutils.dist.DistributionMetadata, attr, new_val) + + +def patch_func(replacement, target_mod, func_name): + """ + Patch func_name in target_mod with replacement + + Important - original must be resolved by name to avoid + patching an already patched function. + """ + original = getattr(target_mod, func_name) + + # set the 'unpatched' attribute on the replacement to + # point to the original. + vars(replacement).setdefault('unpatched', original) + + # replace the function in the original module + setattr(target_mod, func_name, replacement) + + +def get_unpatched_function(candidate): + return getattr(candidate, 'unpatched') + + +def patch_for_msvc_specialized_compiler(): + """ + Patch functions in distutils to use standalone Microsoft Visual C++ + compilers. + """ + # import late to avoid circular imports on Python < 3.5 + msvc = import_module('setuptools.msvc') + + if platform.system() != 'Windows': + # Compilers only availables on Microsoft Windows + return + + def patch_params(mod_name, func_name): + """ + Prepare the parameters for patch_func to patch indicated function. + """ + repl_prefix = 'msvc9_' if 'msvc9' in mod_name else 'msvc14_' + repl_name = repl_prefix + func_name.lstrip('_') + repl = getattr(msvc, repl_name) + mod = import_module(mod_name) + if not hasattr(mod, func_name): + raise ImportError(func_name) + return repl, mod, func_name + + # Python 2.7 to 3.4 + msvc9 = functools.partial(patch_params, 'distutils.msvc9compiler') + + # Python 3.5+ + msvc14 = functools.partial(patch_params, 'distutils._msvccompiler') + + try: + # Patch distutils.msvc9compiler + patch_func(*msvc9('find_vcvarsall')) + patch_func(*msvc9('query_vcvarsall')) + except ImportError: + pass + + try: + # Patch distutils._msvccompiler._get_vc_env + patch_func(*msvc14('_get_vc_env')) + except ImportError: + pass + + try: + # Patch distutils._msvccompiler.gen_lib_options for Numpy + patch_func(*msvc14('gen_lib_options')) + except ImportError: + pass diff --git a/venv2/lib/python2.7/site-packages/setuptools/msvc.py b/venv2/lib/python2.7/site-packages/setuptools/msvc.py new file mode 100644 index 0000000..b9c472f --- /dev/null +++ b/venv2/lib/python2.7/site-packages/setuptools/msvc.py @@ -0,0 +1,1301 @@ +""" +Improved support for Microsoft Visual C++ compilers. + +Known supported compilers: +-------------------------- +Microsoft Visual C++ 9.0: + Microsoft Visual C++ Compiler for Python 2.7 (x86, amd64) + Microsoft Windows SDK 6.1 (x86, x64, ia64) + Microsoft Windows SDK 7.0 (x86, x64, ia64) + +Microsoft Visual C++ 10.0: + Microsoft Windows SDK 7.1 (x86, x64, ia64) + +Microsoft Visual C++ 14.0: + Microsoft Visual C++ Build Tools 2015 (x86, x64, arm) + Microsoft Visual Studio 2017 (x86, x64, arm, arm64) + Microsoft Visual Studio Build Tools 2017 (x86, x64, arm, arm64) +""" + +import os +import sys +import platform +import itertools +import distutils.errors +from setuptools.extern.packaging.version import LegacyVersion + +from setuptools.extern.six.moves import filterfalse + +from .monkey import get_unpatched + +if platform.system() == 'Windows': + from setuptools.extern.six.moves import winreg + safe_env = os.environ +else: + """ + Mock winreg and environ so the module can be imported + on this platform. + """ + + class winreg: + HKEY_USERS = None + HKEY_CURRENT_USER = None + HKEY_LOCAL_MACHINE = None + HKEY_CLASSES_ROOT = None + + safe_env = dict() + +_msvc9_suppress_errors = ( + # msvc9compiler isn't available on some platforms + ImportError, + + # msvc9compiler raises DistutilsPlatformError in some + # environments. See #1118. + distutils.errors.DistutilsPlatformError, +) + +try: + from distutils.msvc9compiler import Reg +except _msvc9_suppress_errors: + pass + + +def msvc9_find_vcvarsall(version): + """ + Patched "distutils.msvc9compiler.find_vcvarsall" to use the standalone + compiler build for Python (VCForPython). Fall back to original behavior + when the standalone compiler is not available. + + Redirect the path of "vcvarsall.bat". + + Known supported compilers + ------------------------- + Microsoft Visual C++ 9.0: + Microsoft Visual C++ Compiler for Python 2.7 (x86, amd64) + + Parameters + ---------- + version: float + Required Microsoft Visual C++ version. + + Return + ------ + vcvarsall.bat path: str + """ + 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: + vcvarsall = os.path.os.path.join(productdir, "vcvarsall.bat") + if os.path.isfile(vcvarsall): + return vcvarsall + + return get_unpatched(msvc9_find_vcvarsall)(version) + + +def msvc9_query_vcvarsall(ver, arch='x86', *args, **kwargs): + """ + Patched "distutils.msvc9compiler.query_vcvarsall" for support extra + compilers. + + Set environment without use of "vcvarsall.bat". + + Known supported compilers + ------------------------- + Microsoft Visual C++ 9.0: + Microsoft Visual C++ Compiler for Python 2.7 (x86, amd64) + Microsoft Windows SDK 6.1 (x86, x64, ia64) + Microsoft Windows SDK 7.0 (x86, x64, ia64) + + Microsoft Visual C++ 10.0: + Microsoft Windows SDK 7.1 (x86, x64, ia64) + + Parameters + ---------- + ver: float + Required Microsoft Visual C++ version. + arch: str + Target architecture. + + Return + ------ + environment: dict + """ + # Try to get environement from vcvarsall.bat (Classical way) + try: + orig = get_unpatched(msvc9_query_vcvarsall) + return orig(ver, arch, *args, **kwargs) + except distutils.errors.DistutilsPlatformError: + # Pass error if Vcvarsall.bat is missing + pass + except ValueError: + # Pass error if environment not set after executing vcvarsall.bat + pass + + # If error, try to set environment directly + try: + return EnvironmentInfo(arch, ver).return_env() + except distutils.errors.DistutilsPlatformError as exc: + _augment_exception(exc, ver, arch) + raise + + +def msvc14_get_vc_env(plat_spec): + """ + Patched "distutils._msvccompiler._get_vc_env" for support extra + compilers. + + Set environment without use of "vcvarsall.bat". + + Known supported compilers + ------------------------- + Microsoft Visual C++ 14.0: + Microsoft Visual C++ Build Tools 2015 (x86, x64, arm) + Microsoft Visual Studio 2017 (x86, x64, arm, arm64) + Microsoft Visual Studio Build Tools 2017 (x86, x64, arm, arm64) + + Parameters + ---------- + plat_spec: str + Target architecture. + + Return + ------ + environment: dict + """ + # Try to get environment from vcvarsall.bat (Classical way) + try: + return get_unpatched(msvc14_get_vc_env)(plat_spec) + except distutils.errors.DistutilsPlatformError: + # Pass error Vcvarsall.bat is missing + pass + + # If error, try to set environment directly + try: + return EnvironmentInfo(plat_spec, vc_min_ver=14.0).return_env() + except distutils.errors.DistutilsPlatformError as exc: + _augment_exception(exc, 14.0) + raise + + +def msvc14_gen_lib_options(*args, **kwargs): + """ + Patched "distutils._msvccompiler.gen_lib_options" for fix + compatibility between "numpy.distutils" and "distutils._msvccompiler" + (for Numpy < 1.11.2) + """ + if "numpy.distutils" in sys.modules: + import numpy as np + if LegacyVersion(np.__version__) < LegacyVersion('1.11.2'): + return np.distutils.ccompiler.gen_lib_options(*args, **kwargs) + return get_unpatched(msvc14_gen_lib_options)(*args, **kwargs) + + +def _augment_exception(exc, version, arch=''): + """ + Add details to the exception message to help guide the user + as to what action will resolve it. + """ + # Error if MSVC++ directory not found or environment not set + message = exc.args[0] + + if "vcvarsall" in message.lower() or "visual c" in message.lower(): + # Special error message if MSVC++ not installed + tmpl = 'Microsoft Visual C++ {version:0.1f} is required.' + message = tmpl.format(**locals()) + msdownload = 'www.microsoft.com/download/details.aspx?id=%d' + if version == 9.0: + if arch.lower().find('ia64') > -1: + # For VC++ 9.0, if IA64 support is needed, redirect user + # to Windows SDK 7.0 + message += ' Get it with "Microsoft Windows SDK 7.0": ' + message += msdownload % 3138 + else: + # For VC++ 9.0 redirect user to Vc++ for Python 2.7 : + # This redirection link is maintained by Microsoft. + # Contact vspython@microsoft.com if it needs updating. + message += ' Get it from http://aka.ms/vcpython27' + elif version == 10.0: + # For VC++ 10.0 Redirect user to Windows SDK 7.1 + message += ' Get it with "Microsoft Windows SDK 7.1": ' + message += msdownload % 8279 + elif version >= 14.0: + # For VC++ 14.0 Redirect user to Visual C++ Build Tools + message += (' Get it with "Microsoft Visual C++ Build Tools": ' + r'https://visualstudio.microsoft.com/downloads/') + + exc.args = (message, ) + + +class PlatformInfo: + """ + Current and Target Architectures informations. + + Parameters + ---------- + arch: str + Target architecture. + """ + current_cpu = safe_env.get('processor_architecture', '').lower() + + def __init__(self, arch): + self.arch = arch.lower().replace('x64', 'amd64') + + @property + def target_cpu(self): + return self.arch[self.arch.find('_') + 1:] + + def target_is_x86(self): + return self.target_cpu == 'x86' + + def current_is_x86(self): + return self.current_cpu == 'x86' + + def current_dir(self, hidex86=False, x64=False): + """ + Current platform specific subfolder. + + Parameters + ---------- + hidex86: bool + return '' and not '\x86' if architecture is x86. + x64: bool + return '\x64' and not '\amd64' if architecture is amd64. + + Return + ------ + subfolder: str + '\target', or '' (see hidex86 parameter) + """ + return ( + '' if (self.current_cpu == 'x86' and hidex86) else + r'\x64' if (self.current_cpu == 'amd64' and x64) else + r'\%s' % self.current_cpu + ) + + def target_dir(self, hidex86=False, x64=False): + r""" + Target platform specific subfolder. + + Parameters + ---------- + hidex86: bool + return '' and not '\x86' if architecture is x86. + x64: bool + return '\x64' and not '\amd64' if architecture is amd64. + + Return + ------ + subfolder: str + '\current', or '' (see hidex86 parameter) + """ + return ( + '' if (self.target_cpu == 'x86' and hidex86) else + r'\x64' if (self.target_cpu == 'amd64' and x64) else + r'\%s' % self.target_cpu + ) + + def cross_dir(self, forcex86=False): + r""" + Cross platform specific subfolder. + + Parameters + ---------- + forcex86: bool + Use 'x86' as current architecture even if current acritecture is + not x86. + + Return + ------ + subfolder: str + '' if target architecture is current architecture, + '\current_target' if not. + """ + current = 'x86' if forcex86 else self.current_cpu + return ( + '' if self.target_cpu == current else + self.target_dir().replace('\\', '\\%s_' % current) + ) + + +class RegistryInfo: + """ + Microsoft Visual Studio related registry informations. + + Parameters + ---------- + platform_info: PlatformInfo + "PlatformInfo" instance. + """ + HKEYS = (winreg.HKEY_USERS, + winreg.HKEY_CURRENT_USER, + winreg.HKEY_LOCAL_MACHINE, + winreg.HKEY_CLASSES_ROOT) + + def __init__(self, platform_info): + self.pi = platform_info + + @property + def visualstudio(self): + """ + Microsoft Visual Studio root registry key. + """ + return 'VisualStudio' + + @property + def sxs(self): + """ + Microsoft Visual Studio SxS registry key. + """ + return os.path.join(self.visualstudio, 'SxS') + + @property + def vc(self): + """ + Microsoft Visual C++ VC7 registry key. + """ + return os.path.join(self.sxs, 'VC7') + + @property + def vs(self): + """ + Microsoft Visual Studio VS7 registry key. + """ + return os.path.join(self.sxs, 'VS7') + + @property + def vc_for_python(self): + """ + Microsoft Visual C++ for Python registry key. + """ + return r'DevDiv\VCForPython' + + @property + def microsoft_sdk(self): + """ + Microsoft SDK registry key. + """ + return 'Microsoft SDKs' + + @property + def windows_sdk(self): + """ + Microsoft Windows/Platform SDK registry key. + """ + return os.path.join(self.microsoft_sdk, 'Windows') + + @property + def netfx_sdk(self): + """ + Microsoft .NET Framework SDK registry key. + """ + return os.path.join(self.microsoft_sdk, 'NETFXSDK') + + @property + def windows_kits_roots(self): + """ + Microsoft Windows Kits Roots registry key. + """ + return r'Windows Kits\Installed Roots' + + def microsoft(self, key, x86=False): + """ + Return key in Microsoft software registry. + + Parameters + ---------- + key: str + Registry key path where look. + x86: str + Force x86 software registry. + + Return + ------ + str: value + """ + node64 = '' if self.pi.current_is_x86() or x86 else 'Wow6432Node' + return os.path.join('Software', node64, 'Microsoft', key) + + def lookup(self, key, name): + """ + Look for values in registry in Microsoft software registry. + + Parameters + ---------- + key: str + Registry key path where look. + name: str + Value name to find. + + Return + ------ + str: value + """ + KEY_READ = winreg.KEY_READ + openkey = winreg.OpenKey + ms = self.microsoft + for hkey in self.HKEYS: + try: + bkey = openkey(hkey, ms(key), 0, KEY_READ) + except (OSError, IOError): + if not self.pi.current_is_x86(): + try: + bkey = openkey(hkey, ms(key, True), 0, KEY_READ) + except (OSError, IOError): + continue + else: + continue + try: + return winreg.QueryValueEx(bkey, name)[0] + except (OSError, IOError): + pass + + +class SystemInfo: + """ + Microsoft Windows and Visual Studio related system inormations. + + Parameters + ---------- + registry_info: RegistryInfo + "RegistryInfo" instance. + vc_ver: float + Required Microsoft Visual C++ version. + """ + + # Variables and properties in this class use originals CamelCase variables + # names from Microsoft source files for more easy comparaison. + WinDir = safe_env.get('WinDir', '') + ProgramFiles = safe_env.get('ProgramFiles', '') + ProgramFilesx86 = safe_env.get('ProgramFiles(x86)', ProgramFiles) + + def __init__(self, registry_info, vc_ver=None): + self.ri = registry_info + self.pi = self.ri.pi + self.vc_ver = vc_ver or self._find_latest_available_vc_ver() + + def _find_latest_available_vc_ver(self): + try: + return self.find_available_vc_vers()[-1] + except IndexError: + err = 'No Microsoft Visual C++ version found' + raise distutils.errors.DistutilsPlatformError(err) + + def find_available_vc_vers(self): + """ + Find all available Microsoft Visual C++ versions. + """ + ms = self.ri.microsoft + vckeys = (self.ri.vc, self.ri.vc_for_python, self.ri.vs) + vc_vers = [] + for hkey in self.ri.HKEYS: + for key in vckeys: + try: + bkey = winreg.OpenKey(hkey, ms(key), 0, winreg.KEY_READ) + except (OSError, IOError): + continue + subkeys, values, _ = winreg.QueryInfoKey(bkey) + for i in range(values): + try: + ver = float(winreg.EnumValue(bkey, i)[0]) + if ver not in vc_vers: + vc_vers.append(ver) + except ValueError: + pass + for i in range(subkeys): + try: + ver = float(winreg.EnumKey(bkey, i)) + if ver not in vc_vers: + vc_vers.append(ver) + except ValueError: + pass + return sorted(vc_vers) + + @property + def VSInstallDir(self): + """ + Microsoft Visual Studio directory. + """ + # Default path + name = 'Microsoft Visual Studio %0.1f' % self.vc_ver + default = os.path.join(self.ProgramFilesx86, name) + + # Try to get path from registry, if fail use default path + return self.ri.lookup(self.ri.vs, '%0.1f' % self.vc_ver) or default + + @property + def VCInstallDir(self): + """ + Microsoft Visual C++ directory. + """ + self.VSInstallDir + + guess_vc = self._guess_vc() or self._guess_vc_legacy() + + # Try to get "VC++ for Python" path from registry as default path + reg_path = os.path.join(self.ri.vc_for_python, '%0.1f' % self.vc_ver) + python_vc = self.ri.lookup(reg_path, 'installdir') + default_vc = os.path.join(python_vc, 'VC') if python_vc else guess_vc + + # Try to get path from registry, if fail use default path + path = self.ri.lookup(self.ri.vc, '%0.1f' % self.vc_ver) or default_vc + + if not os.path.isdir(path): + msg = 'Microsoft Visual C++ directory not found' + raise distutils.errors.DistutilsPlatformError(msg) + + return path + + def _guess_vc(self): + """ + Locate Visual C for 2017 + """ + if self.vc_ver <= 14.0: + return + + default = r'VC\Tools\MSVC' + guess_vc = os.path.join(self.VSInstallDir, default) + # Subdir with VC exact version as name + try: + vc_exact_ver = os.listdir(guess_vc)[-1] + return os.path.join(guess_vc, vc_exact_ver) + except (OSError, IOError, IndexError): + pass + + def _guess_vc_legacy(self): + """ + Locate Visual C for versions prior to 2017 + """ + default = r'Microsoft Visual Studio %0.1f\VC' % self.vc_ver + return os.path.join(self.ProgramFilesx86, default) + + @property + def WindowsSdkVersion(self): + """ + Microsoft Windows SDK versions for specified MSVC++ version. + """ + if self.vc_ver <= 9.0: + return ('7.0', '6.1', '6.0a') + elif self.vc_ver == 10.0: + return ('7.1', '7.0a') + elif self.vc_ver == 11.0: + return ('8.0', '8.0a') + elif self.vc_ver == 12.0: + return ('8.1', '8.1a') + elif self.vc_ver >= 14.0: + return ('10.0', '8.1') + + @property + def WindowsSdkLastVersion(self): + """ + Microsoft Windows SDK last version + """ + return self._use_last_dir_name(os.path.join( + self.WindowsSdkDir, 'lib')) + + @property + def WindowsSdkDir(self): + """ + Microsoft Windows SDK directory. + """ + sdkdir = '' + for ver in self.WindowsSdkVersion: + # Try to get it from registry + loc = os.path.join(self.ri.windows_sdk, 'v%s' % ver) + sdkdir = self.ri.lookup(loc, 'installationfolder') + if sdkdir: + break + if not sdkdir or not os.path.isdir(sdkdir): + # Try to get "VC++ for Python" version from registry + path = os.path.join(self.ri.vc_for_python, '%0.1f' % self.vc_ver) + install_base = self.ri.lookup(path, 'installdir') + if install_base: + sdkdir = os.path.join(install_base, 'WinSDK') + if not sdkdir or not os.path.isdir(sdkdir): + # If fail, use default new path + for ver in self.WindowsSdkVersion: + intver = ver[:ver.rfind('.')] + path = r'Microsoft SDKs\Windows Kits\%s' % (intver) + d = os.path.join(self.ProgramFiles, path) + if os.path.isdir(d): + sdkdir = d + if not sdkdir or not os.path.isdir(sdkdir): + # If fail, use default old path + for ver in self.WindowsSdkVersion: + path = r'Microsoft SDKs\Windows\v%s' % ver + d = os.path.join(self.ProgramFiles, path) + if os.path.isdir(d): + sdkdir = d + if not sdkdir: + # If fail, use Platform SDK + sdkdir = os.path.join(self.VCInstallDir, 'PlatformSDK') + return sdkdir + + @property + def WindowsSDKExecutablePath(self): + """ + Microsoft Windows SDK executable directory. + """ + # Find WinSDK NetFx Tools registry dir name + if self.vc_ver <= 11.0: + netfxver = 35 + arch = '' + else: + netfxver = 40 + hidex86 = True if self.vc_ver <= 12.0 else False + arch = self.pi.current_dir(x64=True, hidex86=hidex86) + fx = 'WinSDK-NetFx%dTools%s' % (netfxver, arch.replace('\\', '-')) + + # liste all possibles registry paths + regpaths = [] + if self.vc_ver >= 14.0: + for ver in self.NetFxSdkVersion: + regpaths += [os.path.join(self.ri.netfx_sdk, ver, fx)] + + for ver in self.WindowsSdkVersion: + regpaths += [os.path.join(self.ri.windows_sdk, 'v%sA' % ver, fx)] + + # Return installation folder from the more recent path + for path in regpaths: + execpath = self.ri.lookup(path, 'installationfolder') + if execpath: + break + return execpath + + @property + def FSharpInstallDir(self): + """ + Microsoft Visual F# directory. + """ + path = r'%0.1f\Setup\F#' % self.vc_ver + path = os.path.join(self.ri.visualstudio, path) + return self.ri.lookup(path, 'productdir') or '' + + @property + def UniversalCRTSdkDir(self): + """ + Microsoft Universal CRT SDK directory. + """ + # Set Kit Roots versions for specified MSVC++ version + if self.vc_ver >= 14.0: + vers = ('10', '81') + else: + vers = () + + # Find path of the more recent Kit + for ver in vers: + sdkdir = self.ri.lookup(self.ri.windows_kits_roots, + 'kitsroot%s' % ver) + if sdkdir: + break + return sdkdir or '' + + @property + def UniversalCRTSdkLastVersion(self): + """ + Microsoft Universal C Runtime SDK last version + """ + return self._use_last_dir_name(os.path.join( + self.UniversalCRTSdkDir, 'lib')) + + @property + def NetFxSdkVersion(self): + """ + Microsoft .NET Framework SDK versions. + """ + # Set FxSdk versions for specified MSVC++ version + if self.vc_ver >= 14.0: + return ('4.6.1', '4.6') + else: + return () + + @property + def NetFxSdkDir(self): + """ + Microsoft .NET Framework SDK directory. + """ + for ver in self.NetFxSdkVersion: + loc = os.path.join(self.ri.netfx_sdk, ver) + sdkdir = self.ri.lookup(loc, 'kitsinstallationfolder') + if sdkdir: + break + return sdkdir or '' + + @property + def FrameworkDir32(self): + """ + Microsoft .NET Framework 32bit directory. + """ + # Default path + guess_fw = os.path.join(self.WinDir, r'Microsoft.NET\Framework') + + # Try to get path from registry, if fail use default path + return self.ri.lookup(self.ri.vc, 'frameworkdir32') or guess_fw + + @property + def FrameworkDir64(self): + """ + Microsoft .NET Framework 64bit directory. + """ + # Default path + guess_fw = os.path.join(self.WinDir, r'Microsoft.NET\Framework64') + + # Try to get path from registry, if fail use default path + return self.ri.lookup(self.ri.vc, 'frameworkdir64') or guess_fw + + @property + def FrameworkVersion32(self): + """ + Microsoft .NET Framework 32bit versions. + """ + return self._find_dot_net_versions(32) + + @property + def FrameworkVersion64(self): + """ + Microsoft .NET Framework 64bit versions. + """ + return self._find_dot_net_versions(64) + + def _find_dot_net_versions(self, bits): + """ + Find Microsoft .NET Framework versions. + + Parameters + ---------- + bits: int + Platform number of bits: 32 or 64. + """ + # Find actual .NET version in registry + reg_ver = self.ri.lookup(self.ri.vc, 'frameworkver%d' % bits) + dot_net_dir = getattr(self, 'FrameworkDir%d' % bits) + ver = reg_ver or self._use_last_dir_name(dot_net_dir, 'v') or '' + + # Set .NET versions for specified MSVC++ version + if self.vc_ver >= 12.0: + frameworkver = (ver, 'v4.0') + elif self.vc_ver >= 10.0: + frameworkver = ('v4.0.30319' if ver.lower()[:2] != 'v4' else ver, + 'v3.5') + elif self.vc_ver == 9.0: + frameworkver = ('v3.5', 'v2.0.50727') + if self.vc_ver == 8.0: + frameworkver = ('v3.0', 'v2.0.50727') + return frameworkver + + def _use_last_dir_name(self, path, prefix=''): + """ + Return name of the last dir in path or '' if no dir found. + + Parameters + ---------- + path: str + Use dirs in this path + prefix: str + Use only dirs startings by this prefix + """ + matching_dirs = ( + dir_name + for dir_name in reversed(os.listdir(path)) + if os.path.isdir(os.path.join(path, dir_name)) and + dir_name.startswith(prefix) + ) + return next(matching_dirs, None) or '' + + +class EnvironmentInfo: + """ + Return environment variables for specified Microsoft Visual C++ version + and platform : Lib, Include, Path and libpath. + + This function is compatible with Microsoft Visual C++ 9.0 to 14.0. + + Script created by analysing Microsoft environment configuration files like + "vcvars[...].bat", "SetEnv.Cmd", "vcbuildtools.bat", ... + + Parameters + ---------- + arch: str + Target architecture. + vc_ver: float + Required Microsoft Visual C++ version. If not set, autodetect the last + version. + vc_min_ver: float + Minimum Microsoft Visual C++ version. + """ + + # Variables and properties in this class use originals CamelCase variables + # names from Microsoft source files for more easy comparaison. + + def __init__(self, arch, vc_ver=None, vc_min_ver=0): + self.pi = PlatformInfo(arch) + self.ri = RegistryInfo(self.pi) + self.si = SystemInfo(self.ri, vc_ver) + + if self.vc_ver < vc_min_ver: + err = 'No suitable Microsoft Visual C++ version found' + raise distutils.errors.DistutilsPlatformError(err) + + @property + def vc_ver(self): + """ + Microsoft Visual C++ version. + """ + return self.si.vc_ver + + @property + def VSTools(self): + """ + Microsoft Visual Studio Tools + """ + paths = [r'Common7\IDE', r'Common7\Tools'] + + if self.vc_ver >= 14.0: + arch_subdir = self.pi.current_dir(hidex86=True, x64=True) + paths += [r'Common7\IDE\CommonExtensions\Microsoft\TestWindow'] + paths += [r'Team Tools\Performance Tools'] + paths += [r'Team Tools\Performance Tools%s' % arch_subdir] + + return [os.path.join(self.si.VSInstallDir, path) for path in paths] + + @property + def VCIncludes(self): + """ + Microsoft Visual C++ & Microsoft Foundation Class Includes + """ + return [os.path.join(self.si.VCInstallDir, 'Include'), + os.path.join(self.si.VCInstallDir, r'ATLMFC\Include')] + + @property + def VCLibraries(self): + """ + Microsoft Visual C++ & Microsoft Foundation Class Libraries + """ + if self.vc_ver >= 15.0: + arch_subdir = self.pi.target_dir(x64=True) + else: + arch_subdir = self.pi.target_dir(hidex86=True) + paths = ['Lib%s' % arch_subdir, r'ATLMFC\Lib%s' % arch_subdir] + + if self.vc_ver >= 14.0: + paths += [r'Lib\store%s' % arch_subdir] + + return [os.path.join(self.si.VCInstallDir, path) for path in paths] + + @property + def VCStoreRefs(self): + """ + Microsoft Visual C++ store references Libraries + """ + if self.vc_ver < 14.0: + return [] + return [os.path.join(self.si.VCInstallDir, r'Lib\store\references')] + + @property + def VCTools(self): + """ + Microsoft Visual C++ Tools + """ + si = self.si + tools = [os.path.join(si.VCInstallDir, 'VCPackages')] + + forcex86 = True if self.vc_ver <= 10.0 else False + arch_subdir = self.pi.cross_dir(forcex86) + if arch_subdir: + tools += [os.path.join(si.VCInstallDir, 'Bin%s' % arch_subdir)] + + if self.vc_ver == 14.0: + path = 'Bin%s' % self.pi.current_dir(hidex86=True) + tools += [os.path.join(si.VCInstallDir, path)] + + elif self.vc_ver >= 15.0: + host_dir = (r'bin\HostX86%s' if self.pi.current_is_x86() else + r'bin\HostX64%s') + tools += [os.path.join( + si.VCInstallDir, host_dir % self.pi.target_dir(x64=True))] + + if self.pi.current_cpu != self.pi.target_cpu: + tools += [os.path.join( + si.VCInstallDir, host_dir % self.pi.current_dir(x64=True))] + + else: + tools += [os.path.join(si.VCInstallDir, 'Bin')] + + return tools + + @property + def OSLibraries(self): + """ + Microsoft Windows SDK Libraries + """ + if self.vc_ver <= 10.0: + arch_subdir = self.pi.target_dir(hidex86=True, x64=True) + return [os.path.join(self.si.WindowsSdkDir, 'Lib%s' % arch_subdir)] + + else: + arch_subdir = self.pi.target_dir(x64=True) + lib = os.path.join(self.si.WindowsSdkDir, 'lib') + libver = self._sdk_subdir + return [os.path.join(lib, '%sum%s' % (libver , arch_subdir))] + + @property + def OSIncludes(self): + """ + Microsoft Windows SDK Include + """ + include = os.path.join(self.si.WindowsSdkDir, 'include') + + if self.vc_ver <= 10.0: + return [include, os.path.join(include, 'gl')] + + else: + if self.vc_ver >= 14.0: + sdkver = self._sdk_subdir + else: + sdkver = '' + return [os.path.join(include, '%sshared' % sdkver), + os.path.join(include, '%sum' % sdkver), + os.path.join(include, '%swinrt' % sdkver)] + + @property + def OSLibpath(self): + """ + Microsoft Windows SDK Libraries Paths + """ + ref = os.path.join(self.si.WindowsSdkDir, 'References') + libpath = [] + + if self.vc_ver <= 9.0: + libpath += self.OSLibraries + + if self.vc_ver >= 11.0: + libpath += [os.path.join(ref, r'CommonConfiguration\Neutral')] + + if self.vc_ver >= 14.0: + libpath += [ + ref, + os.path.join(self.si.WindowsSdkDir, 'UnionMetadata'), + os.path.join( + ref, + 'Windows.Foundation.UniversalApiContract', + '1.0.0.0', + ), + os.path.join( + ref, + 'Windows.Foundation.FoundationContract', + '1.0.0.0', + ), + os.path.join( + ref, + 'Windows.Networking.Connectivity.WwanContract', + '1.0.0.0', + ), + os.path.join( + self.si.WindowsSdkDir, + 'ExtensionSDKs', + 'Microsoft.VCLibs', + '%0.1f' % self.vc_ver, + 'References', + 'CommonConfiguration', + 'neutral', + ), + ] + return libpath + + @property + def SdkTools(self): + """ + Microsoft Windows SDK Tools + """ + return list(self._sdk_tools()) + + def _sdk_tools(self): + """ + Microsoft Windows SDK Tools paths generator + """ + if self.vc_ver < 15.0: + bin_dir = 'Bin' if self.vc_ver <= 11.0 else r'Bin\x86' + yield os.path.join(self.si.WindowsSdkDir, bin_dir) + + if not self.pi.current_is_x86(): + arch_subdir = self.pi.current_dir(x64=True) + path = 'Bin%s' % arch_subdir + yield os.path.join(self.si.WindowsSdkDir, path) + + if self.vc_ver == 10.0 or self.vc_ver == 11.0: + if self.pi.target_is_x86(): + arch_subdir = '' + else: + arch_subdir = self.pi.current_dir(hidex86=True, x64=True) + path = r'Bin\NETFX 4.0 Tools%s' % arch_subdir + yield os.path.join(self.si.WindowsSdkDir, path) + + elif self.vc_ver >= 15.0: + path = os.path.join(self.si.WindowsSdkDir, 'Bin') + arch_subdir = self.pi.current_dir(x64=True) + sdkver = self.si.WindowsSdkLastVersion + yield os.path.join(path, '%s%s' % (sdkver, arch_subdir)) + + if self.si.WindowsSDKExecutablePath: + yield self.si.WindowsSDKExecutablePath + + @property + def _sdk_subdir(self): + """ + Microsoft Windows SDK version subdir + """ + ucrtver = self.si.WindowsSdkLastVersion + return ('%s\\' % ucrtver) if ucrtver else '' + + @property + def SdkSetup(self): + """ + Microsoft Windows SDK Setup + """ + if self.vc_ver > 9.0: + return [] + + return [os.path.join(self.si.WindowsSdkDir, 'Setup')] + + @property + def FxTools(self): + """ + Microsoft .NET Framework Tools + """ + pi = self.pi + si = self.si + + if self.vc_ver <= 10.0: + include32 = True + include64 = not pi.target_is_x86() and not pi.current_is_x86() + else: + include32 = pi.target_is_x86() or pi.current_is_x86() + include64 = pi.current_cpu == 'amd64' or pi.target_cpu == 'amd64' + + tools = [] + if include32: + tools += [os.path.join(si.FrameworkDir32, ver) + for ver in si.FrameworkVersion32] + if include64: + tools += [os.path.join(si.FrameworkDir64, ver) + for ver in si.FrameworkVersion64] + return tools + + @property + def NetFxSDKLibraries(self): + """ + Microsoft .Net Framework SDK Libraries + """ + if self.vc_ver < 14.0 or not self.si.NetFxSdkDir: + return [] + + arch_subdir = self.pi.target_dir(x64=True) + return [os.path.join(self.si.NetFxSdkDir, r'lib\um%s' % arch_subdir)] + + @property + def NetFxSDKIncludes(self): + """ + Microsoft .Net Framework SDK Includes + """ + if self.vc_ver < 14.0 or not self.si.NetFxSdkDir: + return [] + + return [os.path.join(self.si.NetFxSdkDir, r'include\um')] + + @property + def VsTDb(self): + """ + Microsoft Visual Studio Team System Database + """ + return [os.path.join(self.si.VSInstallDir, r'VSTSDB\Deploy')] + + @property + def MSBuild(self): + """ + Microsoft Build Engine + """ + if self.vc_ver < 12.0: + return [] + elif self.vc_ver < 15.0: + base_path = self.si.ProgramFilesx86 + arch_subdir = self.pi.current_dir(hidex86=True) + else: + base_path = self.si.VSInstallDir + arch_subdir = '' + + path = r'MSBuild\%0.1f\bin%s' % (self.vc_ver, arch_subdir) + build = [os.path.join(base_path, path)] + + if self.vc_ver >= 15.0: + # Add Roslyn C# & Visual Basic Compiler + build += [os.path.join(base_path, path, 'Roslyn')] + + return build + + @property + def HTMLHelpWorkshop(self): + """ + Microsoft HTML Help Workshop + """ + if self.vc_ver < 11.0: + return [] + + return [os.path.join(self.si.ProgramFilesx86, 'HTML Help Workshop')] + + @property + def UCRTLibraries(self): + """ + Microsoft Universal C Runtime SDK Libraries + """ + if self.vc_ver < 14.0: + return [] + + arch_subdir = self.pi.target_dir(x64=True) + lib = os.path.join(self.si.UniversalCRTSdkDir, 'lib') + ucrtver = self._ucrt_subdir + return [os.path.join(lib, '%sucrt%s' % (ucrtver, arch_subdir))] + + @property + def UCRTIncludes(self): + """ + Microsoft Universal C Runtime SDK Include + """ + if self.vc_ver < 14.0: + return [] + + include = os.path.join(self.si.UniversalCRTSdkDir, 'include') + return [os.path.join(include, '%sucrt' % self._ucrt_subdir)] + + @property + def _ucrt_subdir(self): + """ + Microsoft Universal C Runtime SDK version subdir + """ + ucrtver = self.si.UniversalCRTSdkLastVersion + return ('%s\\' % ucrtver) if ucrtver else '' + + @property + def FSharp(self): + """ + Microsoft Visual F# + """ + if self.vc_ver < 11.0 and self.vc_ver > 12.0: + return [] + + return self.si.FSharpInstallDir + + @property + def VCRuntimeRedist(self): + """ + Microsoft Visual C++ runtime redistribuable dll + """ + arch_subdir = self.pi.target_dir(x64=True) + if self.vc_ver < 15: + redist_path = self.si.VCInstallDir + vcruntime = 'redist%s\\Microsoft.VC%d0.CRT\\vcruntime%d0.dll' + else: + redist_path = self.si.VCInstallDir.replace('\\Tools', '\\Redist') + vcruntime = 'onecore%s\\Microsoft.VC%d0.CRT\\vcruntime%d0.dll' + + # Visual Studio 2017 is still Visual C++ 14.0 + dll_ver = 14.0 if self.vc_ver == 15 else self.vc_ver + + vcruntime = vcruntime % (arch_subdir, self.vc_ver, dll_ver) + return os.path.join(redist_path, vcruntime) + + def return_env(self, exists=True): + """ + Return environment dict. + + Parameters + ---------- + exists: bool + It True, only return existing paths. + """ + env = dict( + include=self._build_paths('include', + [self.VCIncludes, + self.OSIncludes, + self.UCRTIncludes, + self.NetFxSDKIncludes], + exists), + lib=self._build_paths('lib', + [self.VCLibraries, + self.OSLibraries, + self.FxTools, + self.UCRTLibraries, + self.NetFxSDKLibraries], + exists), + libpath=self._build_paths('libpath', + [self.VCLibraries, + self.FxTools, + self.VCStoreRefs, + self.OSLibpath], + exists), + path=self._build_paths('path', + [self.VCTools, + self.VSTools, + self.VsTDb, + self.SdkTools, + self.SdkSetup, + self.FxTools, + self.MSBuild, + self.HTMLHelpWorkshop, + self.FSharp], + exists), + ) + if self.vc_ver >= 14 and os.path.isfile(self.VCRuntimeRedist): + env['py_vcruntime_redist'] = self.VCRuntimeRedist + return env + + def _build_paths(self, name, spec_path_lists, exists): + """ + Given an environment variable name and specified paths, + return a pathsep-separated string of paths containing + unique, extant, directories from those paths and from + the environment variable. Raise an error if no paths + are resolved. + """ + # flatten spec_path_lists + spec_paths = itertools.chain.from_iterable(spec_path_lists) + env_paths = safe_env.get(name, '').split(os.pathsep) + paths = itertools.chain(spec_paths, env_paths) + extant_paths = list(filter(os.path.isdir, paths)) if exists else paths + if not extant_paths: + msg = "%s environment variable is empty" % name.upper() + raise distutils.errors.DistutilsPlatformError(msg) + unique_paths = self._unique_everseen(extant_paths) + return os.pathsep.join(unique_paths) + + # from Python docs + def _unique_everseen(self, 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 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 diff --git a/venv2/lib/python2.7/site-packages/setuptools/namespaces.py b/venv2/lib/python2.7/site-packages/setuptools/namespaces.py new file mode 100644 index 0000000..dc16106 --- /dev/null +++ b/venv2/lib/python2.7/site-packages/setuptools/namespaces.py @@ -0,0 +1,107 @@ +import os +from distutils import log +import itertools + +from setuptools.extern.six.moves import map + + +flatten = itertools.chain.from_iterable + + +class Installer: + + nspkg_ext = '-nspkg.pth' + + def install_namespaces(self): + nsp = self._get_all_ns_packages() + if not nsp: + return + filename, ext = os.path.splitext(self._get_target()) + filename += self.nspkg_ext + 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) + + def uninstall_namespaces(self): + filename, ext = os.path.splitext(self._get_target()) + filename += self.nspkg_ext + if not os.path.exists(filename): + return + log.info("Removing %s", filename) + os.remove(filename) + + def _get_target(self): + return self.target + + _nspkg_tmpl = ( + "import sys, types, os", + "has_mfs = sys.version_info > (3, 5)", + "p = os.path.join(%(root)s, *%(pth)r)", + "importlib = has_mfs and __import__('importlib.util')", + "has_mfs and __import__('importlib.machinery')", + "m = has_mfs and " + "sys.modules.setdefault(%(pkg)r, " + "importlib.util.module_from_spec(" + "importlib.machinery.PathFinder.find_spec(%(pkg)r, " + "[os.path.dirname(p)])))", + "m = m or " + "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" + + def _get_root(self): + return "sys._getframe(1).f_locals['sitedir']" + + def _gen_nspkg_line(self, pkg): + # ensure pkg is not a unicode string under Python 2.7 + pkg = str(pkg) + pth = tuple(pkg.split('.')) + root = self._get_root() + tmpl_lines = self._nspkg_tmpl + parent, sep, child = pkg.rpartition('.') + if parent: + tmpl_lines += self._nspkg_tmpl_multi + return ';'.join(tmpl_lines) % locals() + '\n' + + def _get_all_ns_packages(self): + """Return sorted list of all package namespaces""" + pkgs = self.distribution.namespace_packages or [] + return sorted(flatten(map(self._pkg_names, pkgs))) + + @staticmethod + def _pkg_names(pkg): + """ + Given a namespace package, yield the components of that + package. + + >>> names = Installer._pkg_names('a.b.c') + >>> set(names) == set(['a', 'a.b', 'a.b.c']) + True + """ + parts = pkg.split('.') + while parts: + yield '.'.join(parts) + parts.pop() + + +class DevelopInstaller(Installer): + def _get_root(self): + return repr(str(self.egg_path)) + + def _get_target(self): + return self.egg_link diff --git a/venv2/lib/python2.7/site-packages/setuptools/package_index.py b/venv2/lib/python2.7/site-packages/setuptools/package_index.py new file mode 100644 index 0000000..6b06f2c --- /dev/null +++ b/venv2/lib/python2.7/site-packages/setuptools/package_index.py @@ -0,0 +1,1136 @@ +"""PyPI and direct package downloading""" +import sys +import os +import re +import shutil +import socket +import base64 +import hashlib +import itertools +import warnings +from functools import wraps + +from setuptools.extern import six +from setuptools.extern.six.moves import urllib, http_client, configparser, map + +import setuptools +from pkg_resources import ( + CHECKOUT_DIST, Distribution, BINARY_DIST, normalize_path, SOURCE_DIST, + Environment, find_distributions, safe_name, safe_version, + to_filename, Requirement, DEVELOP_DIST, EGG_DIST, +) +from setuptools import ssl_support +from distutils import log +from distutils.errors import DistutilsError +from fnmatch import translate +from setuptools.py27compat import get_all_headers +from setuptools.py33compat import unescape +from setuptools.wheel import Wheel + +__metaclass__ = type + +EGG_FRAGMENT = re.compile(r'^egg=([-A-Za-z0-9_.+!]+)$') +HREF = re.compile(r"""href\s*=\s*['"]?([^'"> ]+)""", re.I) +PYPI_MD5 = re.compile( + r'<a href="([^"#]+)">([^<]+)</a>\n\s+\(<a (?:title="MD5 hash"\n\s+)' + r'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 + +_tmpl = "setuptools/{setuptools.__version__} Python-urllib/{py_major}" +user_agent = _tmpl.format(py_major=sys.version[:3], setuptools=setuptools) + + +def parse_requirement_arg(spec): + try: + return Requirement.parse(spec) + except ValueError: + raise DistutilsError( + "Not a URL, existing file, or requirement spec: %r" % (spec,) + ) + + +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('.whl') and '-' in basename: + wheel = Wheel(basename) + if not wheel.is_compatible(): + return [] + return [Distribution( + location=location, + project_name=wheel.project_name, + version=wheel.version, + # Increase priority over eggs. + precedence=EGG_DIST + 1, + )] + 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(r'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(r"""<([^>]*\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))) + + +class ContentChecker: + """ + 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.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 = [] + use_ssl = ( + verify_ssl + and ssl_support.is_available + and (ca_bundle or ssl_support.find_ca_bundle()) + ) + if use_ssl: + 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 + tmpl = "Download error on %s: %%s -- Some packages may not be found!" + f = self.open_url(url, tmpl % 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): + # In Python 3 and got bytes but 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) + is_file = s and s.group(1).lower() == 'file' + if is_file or self.allows(urllib.parse.urlparse(url)[1]): + return True + msg = ( + "\nNote: Bypassing %s (disallowed host; see " + "http://bit.ly/2hrImnY 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: + spec = parse_requirement_arg(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 + + test = ( + dist in req + and (dist.precedence <= SOURCE_DIST or not source) + ) + if test: + loc = self.download(dist.location, tmpdir) + dist.download_location = loc + if os.path.exists(dist.download_location): + return dist + + if force_scan: + self.prescan() + self.find_packages(requirement) + dist = find(requirement) + + if not dist and local_index is not None: + dist = 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 working 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=dist.download_location) + + 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 = None + try: + checker = HashChecker.from_url(url) + fp = self.open_url(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, socket.error) 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): + warnings.warn("SVN download support is deprecated", UserWarning) + 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("git -C %s 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("hg --cwd %s up -C -r %s -q" % ( + 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 decode_entity(match): + what = match.group(0) + return unescape(what) + + +def htmldecode(text): + """ + Decode HTML entities in the given text. + + >>> htmldecode( + ... 'https://../package_name-0.1.2.tar.gz' + ... '?tokena=A&tokenb=B">package_name-0.1.2.tar.gz') + 'https://../package_name-0.1.2.tar.gz?tokena=A&tokenb=B">package_name-0.1.2.tar.gz' + """ + 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() + encoded_bytes = base64.b64encode(auth_bytes) + # convert back to a string + encoded = encoded_bytes.decode() + # strip the trailing carriage return + return encoded.replace('\n', '') + + +class Credential: + """ + 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""" + + parsed = urllib.parse.urlparse(url) + scheme, netloc, path, params, query, frag = parsed + + # 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, address = _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, address, 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 == address: + parts = s2, netloc, path2, param2, query2, frag2 + fp.url = urllib.parse.urlunparse(parts) + + return fp + + +# copy of urllib.parse._splituser from Python 3.8 +def _splituser(host): + """splituser('user[:passwd]@host[:port]') --> 'user[:passwd]', 'host[:port]'.""" + user, delim, host = host.rpartition('@') + return (user if delim else None), host + + +# 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/venv2/lib/python2.7/site-packages/setuptools/pep425tags.py b/venv2/lib/python2.7/site-packages/setuptools/pep425tags.py new file mode 100644 index 0000000..48745a2 --- /dev/null +++ b/venv2/lib/python2.7/site-packages/setuptools/pep425tags.py @@ -0,0 +1,319 @@ +# This file originally from pip: +# https://github.com/pypa/pip/blob/8f4f15a5a95d7d5b511ceaee9ed261176c181970/src/pip/_internal/pep425tags.py +"""Generate and work with PEP 425 Compatibility Tags.""" +from __future__ import absolute_import + +import distutils.util +from distutils import log +import platform +import re +import sys +import sysconfig +import warnings +from collections import OrderedDict + +from .extern import six + +from . import glibc + +_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("{}".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 "{}{}".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: + log.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 + six.PY2)) \ + and six.PY2: + 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 significantly 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_{}_{}_{}'.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 glibc.have_compatible_glibc(2, 5) + + +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 a macOS machine. + """ + arches = [] + + def _supports_arch(major, minor, arch): + # Looking at the application support for macOS 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, platform=None, + impl=None, abi=None): + """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. + :param platform: specify the exact platform you want valid + tags for, or None. If None, use the local system platform. + :param impl: specify the exact implementation you want valid + tags for, or None. If None, use the local interpreter impl. + :param abi: specify the exact abi you want valid + tags for, or None. If None, use the local interpreter 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 = impl or get_abbr_impl() + + abis = [] + + abi = abi or 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 = platform or get_platform() + if arch.startswith('macosx'): + # 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 = '{}_{}_%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 platform is None and 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)) + + # abi3 modules compatible with older version of Python + for version in versions[1:]: + # abi3 was introduced in Python 3.2 + if version in {'31', '30'}: + break + for abi in abi3s: # empty set if not Python 3 + for arch in arches: + supported.append(("%s%s" % (impl, version), 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 + + +implementation_tag = get_impl_tag() diff --git a/venv2/lib/python2.7/site-packages/setuptools/py27compat.py b/venv2/lib/python2.7/site-packages/setuptools/py27compat.py new file mode 100644 index 0000000..2985011 --- /dev/null +++ b/venv2/lib/python2.7/site-packages/setuptools/py27compat.py @@ -0,0 +1,28 @@ +""" +Compatibility Support for Python 2.7 and earlier +""" + +import platform + +from setuptools.extern import six + + +def get_all_headers(message, key): + """ + Given an HTTPMessage, return all headers matching a given key. + """ + return message.get_all(key) + + +if six.PY2: + def get_all_headers(message, key): + return message.getheaders(key) + + +linux_py2_ascii = ( + platform.system() == 'Linux' and + six.PY2 +) + +rmtree_safe = str if linux_py2_ascii else lambda x: x +"""Workaround for http://bugs.python.org/issue24672""" diff --git a/venv2/lib/python2.7/site-packages/setuptools/py31compat.py b/venv2/lib/python2.7/site-packages/setuptools/py31compat.py new file mode 100644 index 0000000..e1da7ee --- /dev/null +++ b/venv2/lib/python2.7/site-packages/setuptools/py31compat.py @@ -0,0 +1,32 @@ +__all__ = [] + +__metaclass__ = type + + +try: + # Python >=3.2 + from tempfile import TemporaryDirectory +except ImportError: + import shutil + import tempfile + + class TemporaryDirectory: + """ + Very simple temporary directory context manager. + Will try to delete afterward, but will also ignore OS and similar + errors on deletion. + """ + + def __init__(self, **kwargs): + self.name = None # Handle mkdtemp raising an exception + self.name = tempfile.mkdtemp(**kwargs) + + 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 diff --git a/venv2/lib/python2.7/site-packages/setuptools/py33compat.py b/venv2/lib/python2.7/site-packages/setuptools/py33compat.py new file mode 100644 index 0000000..87cf539 --- /dev/null +++ b/venv2/lib/python2.7/site-packages/setuptools/py33compat.py @@ -0,0 +1,55 @@ +import dis +import array +import collections + +try: + import html +except ImportError: + html = None + +from setuptools.extern import six +from setuptools.extern.six.moves import html_parser + +__metaclass__ = type + +OpArg = collections.namedtuple('OpArg', 'opcode arg') + + +class Bytecode_compat: + def __init__(self, code): + self.code = code + + def __iter__(self): + """Yield '(op,arg)' pair for each operation in code object 'code'""" + + bytes = array.array('b', self.code.co_code) + eof = len(self.code.co_code) + + ptr = 0 + extended_arg = 0 + + while ptr < eof: + + op = bytes[ptr] + + if op >= dis.HAVE_ARGUMENT: + + arg = bytes[ptr + 1] + bytes[ptr + 2] * 256 + extended_arg + ptr += 3 + + if op == dis.EXTENDED_ARG: + long_type = six.integer_types[-1] + extended_arg = arg * long_type(65536) + continue + + else: + arg = None + ptr += 1 + + yield OpArg(op, arg) + + +Bytecode = getattr(dis, 'Bytecode', Bytecode_compat) + + +unescape = getattr(html, 'unescape', html_parser.HTMLParser().unescape) diff --git a/venv2/lib/python2.7/site-packages/setuptools/sandbox.py b/venv2/lib/python2.7/site-packages/setuptools/sandbox.py new file mode 100644 index 0000000..685f3f7 --- /dev/null +++ b/venv2/lib/python2.7/site-packages/setuptools/sandbox.py @@ -0,0 +1,491 @@ +import os +import sys +import tempfile +import operator +import functools +import itertools +import re +import contextlib +import pickle +import textwrap + +from setuptools.extern import six +from setuptools.extern.six.moves import builtins, map + +import pkg_resources.py31compat + +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() + 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 + """ + pkg_resources.py31compat.makedirs(replacement, exist_ok=True) + + 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(r'(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()) + + # __file__ should be a byte string on Python 2 (#712) + dunder_file = ( + setup_script + if isinstance(setup_script, str) else + setup_script.encode(sys.getfilesystemencoding()) + ) + + with DirectorySandbox(setup_dir): + ns = dict(__file__=dunder_file, __name__='__main__') + _execfile(setup_script, ns) + 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 __enter__(self): + self._copy(self) + if _file: + builtins.file = self._file + builtins.open = self._open + self._active = True + + def __exit__(self, exc_type, exc_value, traceback): + self._active = False + if _file: + builtins.file = _file + builtins.open = _open + self._copy(_os) + + def run(self, func): + """Run 'func' under os sandboxing""" + with self: + return func() + + 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 = [] + + +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) + r'.*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""" + + tmpl = textwrap.dedent(""" + SandboxViolation: {cmd}{args!r} {kwargs} + + 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. + """).lstrip() + + def __str__(self): + cmd, args, kwargs = self.args + return self.tmpl.format(**locals()) diff --git a/venv2/lib/python2.7/site-packages/setuptools/script (dev).tmpl b/venv2/lib/python2.7/site-packages/setuptools/script (dev).tmpl new file mode 100644 index 0000000..39a24b0 --- /dev/null +++ b/venv2/lib/python2.7/site-packages/setuptools/script (dev).tmpl @@ -0,0 +1,6 @@ +# EASY-INSTALL-DEV-SCRIPT: %(spec)r,%(script_name)r +__requires__ = %(spec)r +__import__('pkg_resources').require(%(spec)r) +__file__ = %(dev_path)r +with open(__file__) as f: + exec(compile(f.read(), __file__, 'exec')) diff --git a/venv2/lib/python2.7/site-packages/setuptools/script.tmpl b/venv2/lib/python2.7/site-packages/setuptools/script.tmpl new file mode 100644 index 0000000..ff5efbc --- /dev/null +++ b/venv2/lib/python2.7/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/venv2/lib/python2.7/site-packages/setuptools/site-patch.py b/venv2/lib/python2.7/site-packages/setuptools/site-patch.py new file mode 100644 index 0000000..40b00de --- /dev/null +++ b/venv2/lib/python2.7/site-packages/setuptools/site-patch.py @@ -0,0 +1,74 @@ +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__) + + 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 + 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") + + 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/venv2/lib/python2.7/site-packages/setuptools/ssl_support.py b/venv2/lib/python2.7/site-packages/setuptools/ssl_support.py new file mode 100644 index 0000000..226db69 --- /dev/null +++ b/venv2/lib/python2.7/site-packages/setuptools/ssl_support.py @@ -0,0 +1,260 @@ +import os +import socket +import atexit +import re +import functools + +from setuptools.extern.six.moves import urllib, http_client, map, filter + +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 +/etc/ssl/ca-bundle.pem +""".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 + + https://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 + + if hasattr(ssl, 'create_default_context'): + ctx = ssl.create_default_context(cafile=self.ca_bundle) + self.sock = ctx.wrap_socket(sock, server_hostname=actual_host) + else: + # This is for python < 2.7.9 and < 3.4? + 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 + + +# from jaraco.functools +def once(func): + @functools.wraps(func) + def wrapper(*args, **kwargs): + if not hasattr(func, 'always_returns'): + func.always_returns = func(*args, **kwargs) + return func.always_returns + return wrapper + + +@once +def get_win_certfile(): + try: + import wincertstore + except ImportError: + return None + + class CertFile(wincertstore.CertFile): + def __init__(self): + super(CertFile, self).__init__() + atexit.register(self.close) + + def close(self): + try: + super(CertFile, self).close() + except OSError: + pass + + _wincerts = CertFile() + _wincerts.addstore('CA') + _wincerts.addstore('ROOT') + return _wincerts.name + + +def find_ca_bundle(): + """Return an existing CA bundle path, or None""" + extant_cert_paths = filter(os.path.isfile, cert_paths) + return ( + get_win_certfile() + or next(extant_cert_paths, None) + or _certifi_where() + ) + + +def _certifi_where(): + try: + return __import__('certifi').where() + except (ImportError, ResolutionError, ExtractionError): + pass diff --git a/venv2/lib/python2.7/site-packages/setuptools/unicode_utils.py b/venv2/lib/python2.7/site-packages/setuptools/unicode_utils.py new file mode 100644 index 0000000..7c63efd --- /dev/null +++ b/venv2/lib/python2.7/site-packages/setuptools/unicode_utils.py @@ -0,0 +1,44 @@ +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/venv2/lib/python2.7/site-packages/setuptools/version.py b/venv2/lib/python2.7/site-packages/setuptools/version.py new file mode 100644 index 0000000..95e1869 --- /dev/null +++ b/venv2/lib/python2.7/site-packages/setuptools/version.py @@ -0,0 +1,6 @@ +import pkg_resources + +try: + __version__ = pkg_resources.get_distribution('setuptools').version +except Exception: + __version__ = 'unknown' diff --git a/venv2/lib/python2.7/site-packages/setuptools/wheel.py b/venv2/lib/python2.7/site-packages/setuptools/wheel.py new file mode 100644 index 0000000..e11f0a1 --- /dev/null +++ b/venv2/lib/python2.7/site-packages/setuptools/wheel.py @@ -0,0 +1,211 @@ +"""Wheels support.""" + +from distutils.util import get_platform +import email +import itertools +import os +import posixpath +import re +import zipfile + +import pkg_resources +import setuptools +from pkg_resources import parse_version +from setuptools.extern.packaging.utils import canonicalize_name +from setuptools.extern.six import PY3 +from setuptools import pep425tags +from setuptools.command.egg_info import write_requirements + + +__metaclass__ = type + + +WHEEL_NAME = re.compile( + r"""^(?P.+?)-(?P\d.*?) + ((-(?P\d.*?))?-(?P.+?)-(?P.+?)-(?P.+?) + )\.whl$""", + re.VERBOSE).match + +NAMESPACE_PACKAGE_INIT = '''\ +try: + __import__('pkg_resources').declare_namespace(__name__) +except ImportError: + __path__ = __import__('pkgutil').extend_path(__path__, __name__) +''' + + +def unpack(src_dir, dst_dir): + '''Move everything under `src_dir` to `dst_dir`, and delete the former.''' + for dirpath, dirnames, filenames in os.walk(src_dir): + subdir = os.path.relpath(dirpath, src_dir) + for f in filenames: + src = os.path.join(dirpath, f) + dst = os.path.join(dst_dir, subdir, f) + os.renames(src, dst) + for n, d in reversed(list(enumerate(dirnames))): + src = os.path.join(dirpath, d) + dst = os.path.join(dst_dir, subdir, d) + if not os.path.exists(dst): + # Directory does not exist in destination, + # rename it and prune it from os.walk list. + os.renames(src, dst) + del dirnames[n] + # Cleanup. + for dirpath, dirnames, filenames in os.walk(src_dir, topdown=True): + assert not filenames + os.rmdir(dirpath) + + +class Wheel: + + def __init__(self, filename): + match = WHEEL_NAME(os.path.basename(filename)) + if match is None: + raise ValueError('invalid wheel name: %r' % filename) + self.filename = filename + for k, v in match.groupdict().items(): + setattr(self, k, v) + + def tags(self): + '''List tags (py_version, abi, platform) supported by this wheel.''' + return itertools.product( + self.py_version.split('.'), + self.abi.split('.'), + self.platform.split('.'), + ) + + def is_compatible(self): + '''Is the wheel is compatible with the current platform?''' + supported_tags = pep425tags.get_supported() + return next((True for t in self.tags() if t in supported_tags), False) + + def egg_name(self): + return pkg_resources.Distribution( + project_name=self.project_name, version=self.version, + platform=(None if self.platform == 'any' else get_platform()), + ).egg_name() + '.egg' + + def get_dist_info(self, zf): + # find the correct name of the .dist-info dir in the wheel file + for member in zf.namelist(): + dirname = posixpath.dirname(member) + if (dirname.endswith('.dist-info') and + canonicalize_name(dirname).startswith( + canonicalize_name(self.project_name))): + return dirname + raise ValueError("unsupported wheel format. .dist-info not found") + + def install_as_egg(self, destination_eggdir): + '''Install wheel as an egg directory.''' + with zipfile.ZipFile(self.filename) as zf: + self._install_as_egg(destination_eggdir, zf) + + def _install_as_egg(self, destination_eggdir, zf): + dist_basename = '%s-%s' % (self.project_name, self.version) + dist_info = self.get_dist_info(zf) + dist_data = '%s.data' % dist_basename + egg_info = os.path.join(destination_eggdir, 'EGG-INFO') + + self._convert_metadata(zf, destination_eggdir, dist_info, egg_info) + self._move_data_entries(destination_eggdir, dist_data) + self._fix_namespace_packages(egg_info, destination_eggdir) + + @staticmethod + def _convert_metadata(zf, destination_eggdir, dist_info, egg_info): + def get_metadata(name): + with zf.open(posixpath.join(dist_info, name)) as fp: + value = fp.read().decode('utf-8') if PY3 else fp.read() + return email.parser.Parser().parsestr(value) + + wheel_metadata = get_metadata('WHEEL') + # Check wheel format version is supported. + wheel_version = parse_version(wheel_metadata.get('Wheel-Version')) + wheel_v1 = ( + parse_version('1.0') <= wheel_version < parse_version('2.0dev0') + ) + if not wheel_v1: + raise ValueError( + 'unsupported wheel format version: %s' % wheel_version) + # Extract to target directory. + os.mkdir(destination_eggdir) + zf.extractall(destination_eggdir) + # Convert metadata. + dist_info = os.path.join(destination_eggdir, dist_info) + dist = pkg_resources.Distribution.from_location( + destination_eggdir, dist_info, + metadata=pkg_resources.PathMetadata(destination_eggdir, dist_info), + ) + + # Note: Evaluate and strip markers now, + # as it's difficult to convert back from the syntax: + # foobar; "linux" in sys_platform and extra == 'test' + def raw_req(req): + req.marker = None + return str(req) + install_requires = list(sorted(map(raw_req, dist.requires()))) + extras_require = { + extra: sorted( + req + for req in map(raw_req, dist.requires((extra,))) + if req not in install_requires + ) + for extra in dist.extras + } + os.rename(dist_info, egg_info) + os.rename( + os.path.join(egg_info, 'METADATA'), + os.path.join(egg_info, 'PKG-INFO'), + ) + setup_dist = setuptools.Distribution( + attrs=dict( + install_requires=install_requires, + extras_require=extras_require, + ), + ) + write_requirements( + setup_dist.get_command_obj('egg_info'), + None, + os.path.join(egg_info, 'requires.txt'), + ) + + @staticmethod + def _move_data_entries(destination_eggdir, dist_data): + """Move data entries to their correct location.""" + dist_data = os.path.join(destination_eggdir, dist_data) + dist_data_scripts = os.path.join(dist_data, 'scripts') + if os.path.exists(dist_data_scripts): + egg_info_scripts = os.path.join( + destination_eggdir, 'EGG-INFO', 'scripts') + os.mkdir(egg_info_scripts) + for entry in os.listdir(dist_data_scripts): + # Remove bytecode, as it's not properly handled + # during easy_install scripts install phase. + if entry.endswith('.pyc'): + os.unlink(os.path.join(dist_data_scripts, entry)) + else: + os.rename( + os.path.join(dist_data_scripts, entry), + os.path.join(egg_info_scripts, entry), + ) + os.rmdir(dist_data_scripts) + for subdir in filter(os.path.exists, ( + os.path.join(dist_data, d) + for d in ('data', 'headers', 'purelib', 'platlib') + )): + unpack(subdir, destination_eggdir) + if os.path.exists(dist_data): + os.rmdir(dist_data) + + @staticmethod + def _fix_namespace_packages(egg_info, destination_eggdir): + namespace_packages = os.path.join( + egg_info, 'namespace_packages.txt') + if os.path.exists(namespace_packages): + with open(namespace_packages) as fp: + namespace_packages = fp.read().split() + for mod in namespace_packages: + mod_dir = os.path.join(destination_eggdir, *mod.split('.')) + mod_init = os.path.join(mod_dir, '__init__.py') + if os.path.exists(mod_dir) and not os.path.exists(mod_init): + with open(mod_init, 'w') as fp: + fp.write(NAMESPACE_PACKAGE_INIT) diff --git a/venv2/lib/python2.7/site-packages/setuptools/windows_support.py b/venv2/lib/python2.7/site-packages/setuptools/windows_support.py new file mode 100644 index 0000000..cb977cf --- /dev/null +++ b/venv2/lib/python2.7/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/venv2/lib/python2.7/site-packages/wheel-0.33.4.dist-info/INSTALLER b/venv2/lib/python2.7/site-packages/wheel-0.33.4.dist-info/INSTALLER new file mode 100644 index 0000000..a1b589e --- /dev/null +++ b/venv2/lib/python2.7/site-packages/wheel-0.33.4.dist-info/INSTALLER @@ -0,0 +1 @@ +pip diff --git a/venv2/lib/python2.7/site-packages/wheel-0.33.4.dist-info/LICENSE.txt b/venv2/lib/python2.7/site-packages/wheel-0.33.4.dist-info/LICENSE.txt new file mode 100644 index 0000000..c3441e6 --- /dev/null +++ b/venv2/lib/python2.7/site-packages/wheel-0.33.4.dist-info/LICENSE.txt @@ -0,0 +1,22 @@ +"wheel" copyright (c) 2012-2014 Daniel Holth and +contributors. + +The MIT License + +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/venv2/lib/python2.7/site-packages/wheel-0.33.4.dist-info/METADATA b/venv2/lib/python2.7/site-packages/wheel-0.33.4.dist-info/METADATA new file mode 100644 index 0000000..3685e11 --- /dev/null +++ b/venv2/lib/python2.7/site-packages/wheel-0.33.4.dist-info/METADATA @@ -0,0 +1,60 @@ +Metadata-Version: 2.1 +Name: wheel +Version: 0.33.4 +Summary: A built-package format for Python. +Home-page: https://github.com/pypa/wheel +Author: Daniel Holth +Author-email: dholth@fastmail.fm +Maintainer: Alex Grönholm +Maintainer-email: alex.gronholm@nextday.fi +License: MIT +Project-URL: Documentation, https://wheel.readthedocs.io/ +Project-URL: Changelog, https://wheel.readthedocs.io/en/stable/news.html +Project-URL: Issue Tracker, https://github.com/pypa/wheel/issues +Keywords: wheel,packaging +Platform: UNKNOWN +Classifier: Development Status :: 5 - Production/Stable +Classifier: Intended Audience :: Developers +Classifier: Topic :: System :: Archiving :: Packaging +Classifier: License :: OSI Approved :: MIT License +Classifier: Programming Language :: Python +Classifier: Programming Language :: Python :: 2 +Classifier: Programming Language :: Python :: 2.7 +Classifier: Programming Language :: Python :: 3 +Classifier: Programming Language :: Python :: 3.4 +Classifier: Programming Language :: Python :: 3.5 +Classifier: Programming Language :: Python :: 3.6 +Classifier: Programming Language :: Python :: 3.7 +Requires-Python: >=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.* +Provides-Extra: test +Requires-Dist: pytest (>=3.0.0) ; extra == 'test' +Requires-Dist: pytest-cov ; extra == 'test' + +wheel +===== + +This library is the reference implementation of the Python wheel packaging +standard, as defined in `PEP 427`_. + +It has two different roles: + +#. A setuptools_ extension for building wheels that provides the + ``bdist_wheel`` setuptools command +#. A command line tool for working with wheel files + +It should be noted that wheel is **not** intended to be used as a library, and +as such there is no stable, public API. + +.. _PEP 427: https://www.python.org/dev/peps/pep-0427/ +.. _setuptools: https://pypi.org/project/setuptools/ + + +Code of Conduct +--------------- + +Everyone interacting in the wheel 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/venv2/lib/python2.7/site-packages/wheel-0.33.4.dist-info/RECORD b/venv2/lib/python2.7/site-packages/wheel-0.33.4.dist-info/RECORD new file mode 100644 index 0000000..deca304 --- /dev/null +++ b/venv2/lib/python2.7/site-packages/wheel-0.33.4.dist-info/RECORD @@ -0,0 +1,32 @@ +../../../bin/wheel,sha256=tIBOR3sKUe3heoxaSjCebQvE9LLezH5OpmNr4YOr8AI,244 +wheel-0.33.4.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4 +wheel-0.33.4.dist-info/LICENSE.txt,sha256=zKniDGrx_Pv2lAjzd3aShsvuvN7TNhAMm0o_NfvmNeQ,1125 +wheel-0.33.4.dist-info/METADATA,sha256=wyJg2oitG9m-Uh5ia99JynhzPDzo2cJmrCSsqYpRMtc,2082 +wheel-0.33.4.dist-info/RECORD,, +wheel-0.33.4.dist-info/WHEEL,sha256=h_aVn5OB2IERUjMbi2pucmR_zzWJtk303YXvhh60NJ8,110 +wheel-0.33.4.dist-info/entry_points.txt,sha256=N8HbYFST3yrNQYeB2wXWBEPUhFsEtKNRPaCFGJPyqyc,108 +wheel-0.33.4.dist-info/top_level.txt,sha256=HxSBIbgEstMPe4eFawhA66Mq-QYHMopXVoAncfjb_1c,6 +wheel/__init__.py,sha256=gdfDa6ufU3P8afREFDBD26jo-ncuKtumgvlnZIGSmKc,96 +wheel/__init__.pyc,, +wheel/__main__.py,sha256=lF-YLO4hdQmoWuh4eWZd8YL1U95RSdm76sNLBXa0vjE,417 +wheel/__main__.pyc,, +wheel/bdist_wheel.py,sha256=U3ljAOGgc8UGMKqxOf5YEKE1W-nt99X6RemMi-poBB8,14757 +wheel/bdist_wheel.pyc,, +wheel/cli/__init__.py,sha256=GWSoGUpRabTf8bk3FsNTPrc5Fsr8YOv2dX55iY2W7eY,2572 +wheel/cli/__init__.pyc,, +wheel/cli/convert.py,sha256=me0l6G4gSw-EBVhzjSr7yWYWBp9spMz7mnXlyJTiXso,9497 +wheel/cli/convert.pyc,, +wheel/cli/pack.py,sha256=vkvZc4-rRZyWiwc6sHjpqIjzwDRMEF5u3JUNU9NY_jA,2263 +wheel/cli/pack.pyc,, +wheel/cli/unpack.py,sha256=0VWzT7U_xyenTPwEVavxqvdee93GPvAFHnR3Uu91aRc,673 +wheel/cli/unpack.pyc,, +wheel/metadata.py,sha256=uBv2aOz4U2sERF834C8DeNo235drcsp3ypTzT7MTWEA,4699 +wheel/metadata.pyc,, +wheel/pep425tags.py,sha256=Jdjbnq17kqwPRKJCMb2E1VccNgnC3H6iQL7VGaxkPao,5908 +wheel/pep425tags.pyc,, +wheel/pkginfo.py,sha256=GR76kupQzn1x9sKDaXuE6B6FsZ4OkfRtG7pndlXPvQ4,1257 +wheel/pkginfo.pyc,, +wheel/util.py,sha256=zwVIk-9qWVQLRMgkgQTMp4TRE4HY03-tCUxLrtCpsfU,924 +wheel/util.pyc,, +wheel/wheelfile.py,sha256=Mtt6mUaea-wABqtN5SW3Rn6bVvw-bdcSKaR2SjWZLG4,7222 +wheel/wheelfile.pyc,, diff --git a/venv2/lib/python2.7/site-packages/wheel-0.33.4.dist-info/WHEEL b/venv2/lib/python2.7/site-packages/wheel-0.33.4.dist-info/WHEEL new file mode 100644 index 0000000..78e6f69 --- /dev/null +++ b/venv2/lib/python2.7/site-packages/wheel-0.33.4.dist-info/WHEEL @@ -0,0 +1,6 @@ +Wheel-Version: 1.0 +Generator: bdist_wheel (0.33.4) +Root-Is-Purelib: true +Tag: py2-none-any +Tag: py3-none-any + diff --git a/venv2/lib/python2.7/site-packages/wheel-0.33.4.dist-info/entry_points.txt b/venv2/lib/python2.7/site-packages/wheel-0.33.4.dist-info/entry_points.txt new file mode 100644 index 0000000..b27acad --- /dev/null +++ b/venv2/lib/python2.7/site-packages/wheel-0.33.4.dist-info/entry_points.txt @@ -0,0 +1,6 @@ +[console_scripts] +wheel = wheel.cli:main + +[distutils.commands] +bdist_wheel = wheel.bdist_wheel:bdist_wheel + diff --git a/venv2/lib/python2.7/site-packages/wheel-0.33.4.dist-info/top_level.txt b/venv2/lib/python2.7/site-packages/wheel-0.33.4.dist-info/top_level.txt new file mode 100644 index 0000000..2309722 --- /dev/null +++ b/venv2/lib/python2.7/site-packages/wheel-0.33.4.dist-info/top_level.txt @@ -0,0 +1 @@ +wheel diff --git a/venv2/lib/python2.7/site-packages/wheel/__init__.py b/venv2/lib/python2.7/site-packages/wheel/__init__.py new file mode 100644 index 0000000..0f8ca70 --- /dev/null +++ b/venv2/lib/python2.7/site-packages/wheel/__init__.py @@ -0,0 +1,2 @@ +# __variables__ with double-quoted values will be available in setup.py: +__version__ = "0.33.4" diff --git a/venv2/lib/python2.7/site-packages/wheel/__main__.py b/venv2/lib/python2.7/site-packages/wheel/__main__.py new file mode 100644 index 0000000..b3773a2 --- /dev/null +++ b/venv2/lib/python2.7/site-packages/wheel/__main__.py @@ -0,0 +1,19 @@ +""" +Wheel command line tool (enable python -m wheel syntax) +""" + +import sys + + +def main(): # needed for console script + if __package__ == '': + # To be able to run 'python wheel-0.9.whl/wheel': + import os.path + path = os.path.dirname(os.path.dirname(__file__)) + sys.path[0:0] = [path] + import wheel.cli + sys.exit(wheel.cli.main()) + + +if __name__ == "__main__": + sys.exit(main()) diff --git a/venv2/lib/python2.7/site-packages/wheel/bdist_wheel.py b/venv2/lib/python2.7/site-packages/wheel/bdist_wheel.py new file mode 100644 index 0000000..c79307b --- /dev/null +++ b/venv2/lib/python2.7/site-packages/wheel/bdist_wheel.py @@ -0,0 +1,372 @@ +""" +Create a wheel (.whl) distribution. + +A wheel is a built archive format. +""" + +import os +import shutil +import sys +import re +from email.generator import Generator +from distutils.core import Command +from distutils.sysconfig import get_python_version +from distutils import log as logger +from glob import iglob +from shutil import rmtree +from warnings import warn + +import pkg_resources + +from .pep425tags import get_abbr_impl, get_impl_ver, get_abi_tag, get_platform +from .pkginfo import write_pkg_info +from .metadata import pkginfo_to_metadata +from .wheelfile import WheelFile +from . import pep425tags +from . import __version__ as wheel_version + + +safe_name = pkg_resources.safe_name +safe_version = pkg_resources.safe_version + +PY_LIMITED_API_PATTERN = r'cp3\d' + + +def safer_name(name): + return safe_name(name).replace('-', '_') + + +def safer_version(version): + return safe_version(version).replace('-', '_') + + +class bdist_wheel(Command): + + description = 'create a wheel 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_platform()), + ('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)"), + ('relative', None, + "build the archive using relative paths " + "(default: false)"), + ('owner=', 'u', + "Owner name used when creating a tar file" + " [default: current user]"), + ('group=', 'g', + "Group name used when creating a tar file" + " [default: current group]"), + ('universal', None, + "make a universal wheel" + " (default: false)"), + ('python-tag=', None, + "Python implementation compatibility tag" + " (default: py%s)" % get_impl_ver()[0]), + ('build-number=', None, + "Build number for this particular version. " + "As specified in PEP-0427, this must start with a digit. " + "[default: None]"), + ('py-limited-api=', None, + "Python tag (cp32|cp33|cpNN) for abi3 wheel tag" + " (default: false)"), + ] + + boolean_options = ['keep-temp', 'skip-build', 'relative', 'universal'] + + def initialize_options(self): + self.bdist_dir = None + self.data_dir = None + self.plat_name = None + self.plat_tag = None + self.format = 'zip' + self.keep_temp = False + self.dist_dir = None + self.egginfo_dir = None + self.root_is_pure = None + self.skip_build = None + self.relative = False + self.owner = None + self.group = None + self.universal = False + self.python_tag = 'py' + get_impl_ver()[0] + self.build_number = None + self.py_limited_api = False + self.plat_name_supplied = False + + def finalize_options(self): + if self.bdist_dir is None: + bdist_base = self.get_finalized_command('bdist').bdist_base + self.bdist_dir = os.path.join(bdist_base, 'wheel') + + self.data_dir = self.wheel_dist_name + '.data' + self.plat_name_supplied = self.plat_name is not None + + need_options = ('dist_dir', 'plat_name', 'skip_build') + + self.set_undefined_options('bdist', + *zip(need_options, need_options)) + + self.root_is_pure = not (self.distribution.has_ext_modules() + or self.distribution.has_c_libraries()) + + if self.py_limited_api and not re.match(PY_LIMITED_API_PATTERN, self.py_limited_api): + raise ValueError("py-limited-api must match '%s'" % PY_LIMITED_API_PATTERN) + + # Support legacy [wheel] section for setting universal + wheel = self.distribution.get_option_dict('wheel') + if 'universal' in wheel: + # please don't define this in your global configs + logger.warn('The [wheel] section is deprecated. Use [bdist_wheel] instead.') + val = wheel['universal'][1].strip() + if val.lower() in ('1', 'true', 'yes'): + self.universal = True + + if self.build_number is not None and not self.build_number[:1].isdigit(): + raise ValueError("Build tag (build-number) must start with a digit.") + + @property + def wheel_dist_name(self): + """Return distribution full name with - replaced with _""" + components = (safer_name(self.distribution.get_name()), + safer_version(self.distribution.get_version())) + if self.build_number: + components += (self.build_number,) + return '-'.join(components) + + def get_tag(self): + # bdist sets self.plat_name if unset, we should only use it for purepy + # wheels if the user supplied it. + if self.plat_name_supplied: + plat_name = self.plat_name + elif self.root_is_pure: + plat_name = 'any' + else: + plat_name = self.plat_name or get_platform() + if plat_name in ('linux-x86_64', 'linux_x86_64') and sys.maxsize == 2147483647: + plat_name = 'linux_i686' + plat_name = plat_name.replace('-', '_').replace('.', '_') + + if self.root_is_pure: + if self.universal: + impl = 'py2.py3' + else: + impl = self.python_tag + tag = (impl, 'none', plat_name) + else: + impl_name = get_abbr_impl() + impl_ver = get_impl_ver() + impl = impl_name + impl_ver + # We don't work on CPython 3.1, 3.0. + if self.py_limited_api and (impl_name + impl_ver).startswith('cp3'): + impl = self.py_limited_api + abi_tag = 'abi3' + else: + abi_tag = str(get_abi_tag()).lower() + tag = (impl, abi_tag, plat_name) + supported_tags = pep425tags.get_supported( + supplied_platform=plat_name if self.plat_name_supplied else None) + # XXX switch to this alternate implementation for non-pure: + if not self.py_limited_api: + assert tag == supported_tags[0], "%s != %s" % (tag, supported_tags[0]) + assert tag in supported_tags, "would build wheel with unsupported tag {}".format(tag) + return tag + + def run(self): + build_scripts = self.reinitialize_command('build_scripts') + build_scripts.executable = 'python' + build_scripts.force = True + + build_ext = self.reinitialize_command('build_ext') + build_ext.inplace = False + + if not self.skip_build: + self.run_command('build') + + install = self.reinitialize_command('install', + reinit_subcommands=True) + install.root = self.bdist_dir + install.compile = False + install.skip_build = self.skip_build + install.warn_dir = False + + # A wheel without setuptools scripts is more cross-platform. + # Use the (undocumented) `no_ep` option to setuptools' + # install_scripts command to avoid creating entry point scripts. + install_scripts = self.reinitialize_command('install_scripts') + install_scripts.no_ep = True + + # Use a custom scheme for the archive, because we have to decide + # at installation time which scheme to use. + for key in ('headers', 'scripts', 'data', 'purelib', 'platlib'): + setattr(install, + 'install_' + key, + os.path.join(self.data_dir, key)) + + basedir_observed = '' + + if os.name == 'nt': + # win32 barfs if any of these are ''; could be '.'? + # (distutils.command.install:change_roots bug) + basedir_observed = os.path.normpath(os.path.join(self.data_dir, '..')) + self.install_libbase = self.install_lib = basedir_observed + + setattr(install, + 'install_purelib' if self.root_is_pure else 'install_platlib', + basedir_observed) + + logger.info("installing to %s", self.bdist_dir) + + self.run_command('install') + + impl_tag, abi_tag, plat_tag = self.get_tag() + archive_basename = "{}-{}-{}-{}".format(self.wheel_dist_name, impl_tag, abi_tag, plat_tag) + if not self.relative: + archive_root = self.bdist_dir + else: + archive_root = os.path.join( + self.bdist_dir, + self._ensure_relative(install.install_base)) + + self.set_undefined_options('install_egg_info', ('target', 'egginfo_dir')) + distinfo_dirname = '{}-{}.dist-info'.format( + safer_name(self.distribution.get_name()), + safer_version(self.distribution.get_version())) + distinfo_dir = os.path.join(self.bdist_dir, distinfo_dirname) + self.egg2dist(self.egginfo_dir, distinfo_dir) + + self.write_wheelfile(distinfo_dir) + + # Make the archive + if not os.path.exists(self.dist_dir): + os.makedirs(self.dist_dir) + + wheel_path = os.path.join(self.dist_dir, archive_basename + '.whl') + with WheelFile(wheel_path, 'w') as wf: + wf.write_files(archive_root) + + # Add to 'Distribution.dist_files' so that the "upload" command works + getattr(self.distribution, 'dist_files', []).append( + ('bdist_wheel', get_python_version(), wheel_path)) + + if not self.keep_temp: + logger.info('removing %s', self.bdist_dir) + if not self.dry_run: + rmtree(self.bdist_dir) + + def write_wheelfile(self, wheelfile_base, generator='bdist_wheel (' + wheel_version + ')'): + from email.message import Message + msg = Message() + msg['Wheel-Version'] = '1.0' # of the spec + msg['Generator'] = generator + msg['Root-Is-Purelib'] = str(self.root_is_pure).lower() + if self.build_number is not None: + msg['Build'] = self.build_number + + # Doesn't work for bdist_wininst + impl_tag, abi_tag, plat_tag = self.get_tag() + for impl in impl_tag.split('.'): + for abi in abi_tag.split('.'): + for plat in plat_tag.split('.'): + msg['Tag'] = '-'.join((impl, abi, plat)) + + wheelfile_path = os.path.join(wheelfile_base, 'WHEEL') + logger.info('creating %s', wheelfile_path) + with open(wheelfile_path, 'w') as f: + Generator(f, maxheaderlen=0).flatten(msg) + + def _ensure_relative(self, path): + # copied from dir_util, deleted + drive, path = os.path.splitdrive(path) + if path[0:1] == os.sep: + path = drive + path[1:] + return path + + @property + def license_paths(self): + metadata = self.distribution.get_option_dict('metadata') + files = set() + patterns = sorted({ + option for option in metadata.get('license_files', ('', ''))[1].split() + }) + + if 'license_file' in metadata: + warn('The "license_file" option is deprecated. Use "license_files" instead.', + DeprecationWarning) + files.add(metadata['license_file'][1]) + + if 'license_file' not in metadata and 'license_files' not in metadata: + patterns = ('LICEN[CS]E*', 'COPYING*', 'NOTICE*', 'AUTHORS*') + + for pattern in patterns: + for path in iglob(pattern): + if path not in files and os.path.isfile(path): + logger.info('adding license file "%s" (matched pattern "%s")', path, pattern) + files.add(path) + + return files + + def egg2dist(self, egginfo_path, distinfo_path): + """Convert an .egg-info directory into a .dist-info directory""" + def adios(p): + """Appropriately delete directory, file or link.""" + if os.path.exists(p) and not os.path.islink(p) and os.path.isdir(p): + shutil.rmtree(p) + elif os.path.exists(p): + os.unlink(p) + + adios(distinfo_path) + + if not os.path.exists(egginfo_path): + # There is no egg-info. This is probably because the egg-info + # file/directory is not named matching the distribution name used + # to name the archive file. Check for this case and report + # accordingly. + import glob + pat = os.path.join(os.path.dirname(egginfo_path), '*.egg-info') + possible = glob.glob(pat) + err = "Egg metadata expected at %s but not found" % (egginfo_path,) + if possible: + alt = os.path.basename(possible[0]) + err += " (%s found - possible misnamed archive file?)" % (alt,) + + raise ValueError(err) + + if os.path.isfile(egginfo_path): + # .egg-info is a single file + pkginfo_path = egginfo_path + pkg_info = pkginfo_to_metadata(egginfo_path, egginfo_path) + os.mkdir(distinfo_path) + else: + # .egg-info is a directory + pkginfo_path = os.path.join(egginfo_path, 'PKG-INFO') + pkg_info = pkginfo_to_metadata(egginfo_path, pkginfo_path) + + # ignore common egg metadata that is useless to wheel + shutil.copytree(egginfo_path, distinfo_path, + ignore=lambda x, y: {'PKG-INFO', 'requires.txt', 'SOURCES.txt', + 'not-zip-safe'} + ) + + # delete dependency_links if it is only whitespace + dependency_links_path = os.path.join(distinfo_path, 'dependency_links.txt') + with open(dependency_links_path, 'r') as dependency_links_file: + dependency_links = dependency_links_file.read().strip() + if not dependency_links: + adios(dependency_links_path) + + write_pkg_info(os.path.join(distinfo_path, 'METADATA'), pkg_info) + + for license_path in self.license_paths: + filename = os.path.basename(license_path) + shutil.copy(license_path, os.path.join(distinfo_path, filename)) + + adios(egginfo_path) diff --git a/venv2/lib/python2.7/site-packages/wheel/cli/__init__.py b/venv2/lib/python2.7/site-packages/wheel/cli/__init__.py new file mode 100644 index 0000000..95740bf --- /dev/null +++ b/venv2/lib/python2.7/site-packages/wheel/cli/__init__.py @@ -0,0 +1,88 @@ +""" +Wheel command-line utility. +""" + +from __future__ import print_function + +import argparse +import os +import sys + + +def require_pkgresources(name): + try: + import pkg_resources # noqa: F401 + except ImportError: + raise RuntimeError("'{0}' needs pkg_resources (part of setuptools).".format(name)) + + +class WheelError(Exception): + pass + + +def unpack_f(args): + from .unpack import unpack + unpack(args.wheelfile, args.dest) + + +def pack_f(args): + from .pack import pack + pack(args.directory, args.dest_dir, args.build_number) + + +def convert_f(args): + from .convert import convert + convert(args.files, args.dest_dir, args.verbose) + + +def version_f(args): + from .. import __version__ + print("wheel %s" % __version__) + + +def parser(): + p = argparse.ArgumentParser() + s = p.add_subparsers(help="commands") + + unpack_parser = s.add_parser('unpack', help='Unpack wheel') + unpack_parser.add_argument('--dest', '-d', help='Destination directory', + default='.') + unpack_parser.add_argument('wheelfile', help='Wheel file') + unpack_parser.set_defaults(func=unpack_f) + + repack_parser = s.add_parser('pack', help='Repack wheel') + repack_parser.add_argument('directory', help='Root directory of the unpacked wheel') + repack_parser.add_argument('--dest-dir', '-d', default=os.path.curdir, + help="Directory to store the wheel (default %(default)s)") + repack_parser.add_argument('--build-number', help="Build tag to use in the wheel name") + repack_parser.set_defaults(func=pack_f) + + convert_parser = s.add_parser('convert', help='Convert egg or wininst to wheel') + convert_parser.add_argument('files', nargs='*', help='Files to convert') + convert_parser.add_argument('--dest-dir', '-d', default=os.path.curdir, + help="Directory to store wheels (default %(default)s)") + convert_parser.add_argument('--verbose', '-v', action='store_true') + convert_parser.set_defaults(func=convert_f) + + version_parser = s.add_parser('version', help='Print version and exit') + version_parser.set_defaults(func=version_f) + + help_parser = s.add_parser('help', help='Show this help') + help_parser.set_defaults(func=lambda args: p.print_help()) + + return p + + +def main(): + p = parser() + args = p.parse_args() + if not hasattr(args, 'func'): + p.print_help() + else: + try: + args.func(args) + return 0 + except WheelError as e: + print(e, file=sys.stderr) + + return 1 diff --git a/venv2/lib/python2.7/site-packages/wheel/cli/convert.py b/venv2/lib/python2.7/site-packages/wheel/cli/convert.py new file mode 100644 index 0000000..f1a793a --- /dev/null +++ b/venv2/lib/python2.7/site-packages/wheel/cli/convert.py @@ -0,0 +1,269 @@ +import os.path +import re +import shutil +import sys +import tempfile +import zipfile +from distutils import dist +from glob import iglob + +from ..bdist_wheel import bdist_wheel +from ..wheelfile import WheelFile +from . import WheelError, require_pkgresources + +egg_info_re = re.compile(r''' + (?P.+?)-(?P.+?) + (-(?Ppy\d\.\d) + (-(?P.+?))? + )?.egg$''', re.VERBOSE) + + +class _bdist_wheel_tag(bdist_wheel): + # allow the client to override the default generated wheel tag + # The default bdist_wheel implementation uses python and abi tags + # of the running python process. This is not suitable for + # generating/repackaging prebuild binaries. + + full_tag_supplied = False + full_tag = None # None or a (pytag, soabitag, plattag) triple + + def get_tag(self): + if self.full_tag_supplied and self.full_tag is not None: + return self.full_tag + else: + return bdist_wheel.get_tag(self) + + +def egg2wheel(egg_path, dest_dir): + filename = os.path.basename(egg_path) + match = egg_info_re.match(filename) + if not match: + raise WheelError('Invalid egg file name: {}'.format(filename)) + + egg_info = match.groupdict() + dir = tempfile.mkdtemp(suffix="_e2w") + if os.path.isfile(egg_path): + # assume we have a bdist_egg otherwise + with zipfile.ZipFile(egg_path) as egg: + egg.extractall(dir) + else: + # support buildout-style installed eggs directories + for pth in os.listdir(egg_path): + src = os.path.join(egg_path, pth) + if os.path.isfile(src): + shutil.copy2(src, dir) + else: + shutil.copytree(src, os.path.join(dir, pth)) + + pyver = egg_info['pyver'] + if pyver: + pyver = egg_info['pyver'] = pyver.replace('.', '') + + arch = (egg_info['arch'] or 'any').replace('.', '_').replace('-', '_') + + # assume all binary eggs are for CPython + abi = 'cp' + pyver[2:] if arch != 'any' else 'none' + + root_is_purelib = egg_info['arch'] is None + if root_is_purelib: + bw = bdist_wheel(dist.Distribution()) + else: + bw = _bdist_wheel_tag(dist.Distribution()) + + bw.root_is_pure = root_is_purelib + bw.python_tag = pyver + bw.plat_name_supplied = True + bw.plat_name = egg_info['arch'] or 'any' + if not root_is_purelib: + bw.full_tag_supplied = True + bw.full_tag = (pyver, abi, arch) + + dist_info_dir = os.path.join(dir, '{name}-{ver}.dist-info'.format(**egg_info)) + bw.egg2dist(os.path.join(dir, 'EGG-INFO'), dist_info_dir) + bw.write_wheelfile(dist_info_dir, generator='egg2wheel') + wheel_name = '{name}-{ver}-{pyver}-{}-{}.whl'.format(abi, arch, **egg_info) + with WheelFile(os.path.join(dest_dir, wheel_name), 'w') as wf: + wf.write_files(dir) + + shutil.rmtree(dir) + + +def parse_wininst_info(wininfo_name, egginfo_name): + """Extract metadata from filenames. + + Extracts the 4 metadataitems needed (name, version, pyversion, arch) from + the installer filename and the name of the egg-info directory embedded in + the zipfile (if any). + + The egginfo filename has the format:: + + name-ver(-pyver)(-arch).egg-info + + The installer filename has the format:: + + name-ver.arch(-pyver).exe + + Some things to note: + + 1. The installer filename is not definitive. An installer can be renamed + and work perfectly well as an installer. So more reliable data should + be used whenever possible. + 2. The egg-info data should be preferred for the name and version, because + these come straight from the distutils metadata, and are mandatory. + 3. The pyver from the egg-info data should be ignored, as it is + constructed from the version of Python used to build the installer, + which is irrelevant - the installer filename is correct here (even to + the point that when it's not there, any version is implied). + 4. The architecture must be taken from the installer filename, as it is + not included in the egg-info data. + 5. Architecture-neutral installers still have an architecture because the + installer format itself (being executable) is architecture-specific. We + should therefore ignore the architecture if the content is pure-python. + """ + + egginfo = None + if egginfo_name: + egginfo = egg_info_re.search(egginfo_name) + if not egginfo: + raise ValueError("Egg info filename %s is not valid" % (egginfo_name,)) + + # Parse the wininst filename + # 1. Distribution name (up to the first '-') + w_name, sep, rest = wininfo_name.partition('-') + if not sep: + raise ValueError("Installer filename %s is not valid" % (wininfo_name,)) + + # Strip '.exe' + rest = rest[:-4] + # 2. Python version (from the last '-', must start with 'py') + rest2, sep, w_pyver = rest.rpartition('-') + if sep and w_pyver.startswith('py'): + rest = rest2 + w_pyver = w_pyver.replace('.', '') + else: + # Not version specific - use py2.py3. While it is possible that + # pure-Python code is not compatible with both Python 2 and 3, there + # is no way of knowing from the wininst format, so we assume the best + # here (the user can always manually rename the wheel to be more + # restrictive if needed). + w_pyver = 'py2.py3' + # 3. Version and architecture + w_ver, sep, w_arch = rest.rpartition('.') + if not sep: + raise ValueError("Installer filename %s is not valid" % (wininfo_name,)) + + if egginfo: + w_name = egginfo.group('name') + w_ver = egginfo.group('ver') + + return {'name': w_name, 'ver': w_ver, 'arch': w_arch, 'pyver': w_pyver} + + +def wininst2wheel(path, dest_dir): + with zipfile.ZipFile(path) as bdw: + # Search for egg-info in the archive + egginfo_name = None + for filename in bdw.namelist(): + if '.egg-info' in filename: + egginfo_name = filename + break + + info = parse_wininst_info(os.path.basename(path), egginfo_name) + + root_is_purelib = True + for zipinfo in bdw.infolist(): + if zipinfo.filename.startswith('PLATLIB'): + root_is_purelib = False + break + if root_is_purelib: + paths = {'purelib': ''} + else: + paths = {'platlib': ''} + + dist_info = "%(name)s-%(ver)s" % info + datadir = "%s.data/" % dist_info + + # rewrite paths to trick ZipFile into extracting an egg + # XXX grab wininst .ini - between .exe, padding, and first zip file. + members = [] + egginfo_name = '' + for zipinfo in bdw.infolist(): + key, basename = zipinfo.filename.split('/', 1) + key = key.lower() + basepath = paths.get(key, None) + if basepath is None: + basepath = datadir + key.lower() + '/' + oldname = zipinfo.filename + newname = basepath + basename + zipinfo.filename = newname + del bdw.NameToInfo[oldname] + bdw.NameToInfo[newname] = zipinfo + # Collect member names, but omit '' (from an entry like "PLATLIB/" + if newname: + members.append(newname) + # Remember egg-info name for the egg2dist call below + if not egginfo_name: + if newname.endswith('.egg-info'): + egginfo_name = newname + elif '.egg-info/' in newname: + egginfo_name, sep, _ = newname.rpartition('/') + dir = tempfile.mkdtemp(suffix="_b2w") + bdw.extractall(dir, members) + + # egg2wheel + abi = 'none' + pyver = info['pyver'] + arch = (info['arch'] or 'any').replace('.', '_').replace('-', '_') + # Wininst installers always have arch even if they are not + # architecture-specific (because the format itself is). + # So, assume the content is architecture-neutral if root is purelib. + if root_is_purelib: + arch = 'any' + # If the installer is architecture-specific, it's almost certainly also + # CPython-specific. + if arch != 'any': + pyver = pyver.replace('py', 'cp') + wheel_name = '-'.join((dist_info, pyver, abi, arch)) + if root_is_purelib: + bw = bdist_wheel(dist.Distribution()) + else: + bw = _bdist_wheel_tag(dist.Distribution()) + + bw.root_is_pure = root_is_purelib + bw.python_tag = pyver + bw.plat_name_supplied = True + bw.plat_name = info['arch'] or 'any' + + if not root_is_purelib: + bw.full_tag_supplied = True + bw.full_tag = (pyver, abi, arch) + + dist_info_dir = os.path.join(dir, '%s.dist-info' % dist_info) + bw.egg2dist(os.path.join(dir, egginfo_name), dist_info_dir) + bw.write_wheelfile(dist_info_dir, generator='wininst2wheel') + + wheel_path = os.path.join(dest_dir, wheel_name) + with WheelFile(wheel_path, 'w') as wf: + wf.write_files(dir) + + shutil.rmtree(dir) + + +def convert(files, dest_dir, verbose): + # Only support wheel convert if pkg_resources is present + require_pkgresources('wheel convert') + + for pat in files: + for installer in iglob(pat): + if os.path.splitext(installer)[1] == '.egg': + conv = egg2wheel + else: + conv = wininst2wheel + + if verbose: + print("{}... ".format(installer)) + sys.stdout.flush() + + conv(installer, dest_dir) + if verbose: + print("OK") diff --git a/venv2/lib/python2.7/site-packages/wheel/cli/pack.py b/venv2/lib/python2.7/site-packages/wheel/cli/pack.py new file mode 100644 index 0000000..af6e81c --- /dev/null +++ b/venv2/lib/python2.7/site-packages/wheel/cli/pack.py @@ -0,0 +1,58 @@ +from __future__ import print_function + +import os.path +import re +import sys + +from wheel.cli import WheelError +from wheel.wheelfile import WheelFile + +DIST_INFO_RE = re.compile(r"^(?P(?P.+?)-(?P\d.*?))\.dist-info$") + + +def pack(directory, dest_dir, build_number): + """Repack a previously unpacked wheel directory into a new wheel file. + + The .dist-info/WHEEL file must contain one or more tags so that the target + wheel file name can be determined. + + :param directory: The unpacked wheel directory + :param dest_dir: Destination directory (defaults to the current directory) + """ + # Find the .dist-info directory + dist_info_dirs = [fn for fn in os.listdir(directory) + if os.path.isdir(os.path.join(directory, fn)) and DIST_INFO_RE.match(fn)] + if len(dist_info_dirs) > 1: + raise WheelError('Multiple .dist-info directories found in {}'.format(directory)) + elif not dist_info_dirs: + raise WheelError('No .dist-info directories found in {}'.format(directory)) + + # Determine the target wheel filename + dist_info_dir = dist_info_dirs[0] + name_version = DIST_INFO_RE.match(dist_info_dir).group('namever') + + # Add the build number if specific + if build_number: + name_version += '-' + build_number + + # Read the tags from .dist-info/WHEEL + with open(os.path.join(directory, dist_info_dir, 'WHEEL')) as f: + tags = [line.split(' ')[1].rstrip() for line in f if line.startswith('Tag: ')] + if not tags: + raise WheelError('No tags present in {}/WHEEL; cannot determine target wheel filename' + .format(dist_info_dir)) + + # Reassemble the tags for the wheel file + impls = sorted({tag.split('-')[0] for tag in tags}) + abivers = sorted({tag.split('-')[1] for tag in tags}) + platforms = sorted({tag.split('-')[2] for tag in tags}) + tagline = '-'.join(['.'.join(impls), '.'.join(abivers), '.'.join(platforms)]) + + # Repack the wheel + wheel_path = os.path.join(dest_dir, '{}-{}.whl'.format(name_version, tagline)) + with WheelFile(wheel_path, 'w') as wf: + print("Repacking wheel as {}...".format(wheel_path), end='') + sys.stdout.flush() + wf.write_files(directory) + + print('OK') diff --git a/venv2/lib/python2.7/site-packages/wheel/cli/unpack.py b/venv2/lib/python2.7/site-packages/wheel/cli/unpack.py new file mode 100644 index 0000000..2e9857a --- /dev/null +++ b/venv2/lib/python2.7/site-packages/wheel/cli/unpack.py @@ -0,0 +1,25 @@ +from __future__ import print_function + +import os.path +import sys + +from ..wheelfile import WheelFile + + +def unpack(path, dest='.'): + """Unpack a wheel. + + Wheel content will be unpacked to {dest}/{name}-{ver}, where {name} + is the package name and {ver} its version. + + :param path: The path to the wheel. + :param dest: Destination directory (default to current directory). + """ + with WheelFile(path) as wf: + namever = wf.parsed_filename.group('namever') + destination = os.path.join(dest, namever) + print("Unpacking to: {}...".format(destination), end='') + sys.stdout.flush() + wf.extractall(destination) + + print('OK') diff --git a/venv2/lib/python2.7/site-packages/wheel/metadata.py b/venv2/lib/python2.7/site-packages/wheel/metadata.py new file mode 100644 index 0000000..ab0c07e --- /dev/null +++ b/venv2/lib/python2.7/site-packages/wheel/metadata.py @@ -0,0 +1,141 @@ +""" +Tools for converting old- to new-style metadata. +""" + +import os.path +import re +import textwrap + +import pkg_resources + +from .pkginfo import read_pkg_info + +# Wheel itself is probably the only program that uses non-extras markers +# in METADATA/PKG-INFO. Support its syntax with the extra at the end only. +EXTRA_RE = re.compile( + r"""^(?P.*?)(;\s*(?P.*?)(extra == '(?P.*?)')?)$""") + + +def requires_to_requires_dist(requirement): + """Return the version specifier for a requirement in PEP 345/566 fashion.""" + if getattr(requirement, 'url', None): + return " @ " + requirement.url + + requires_dist = [] + for op, ver in requirement.specs: + requires_dist.append(op + ver) + if not requires_dist: + return '' + return " (%s)" % ','.join(sorted(requires_dist)) + + +def convert_requirements(requirements): + """Yield Requires-Dist: strings for parsed requirements strings.""" + for req in requirements: + parsed_requirement = pkg_resources.Requirement.parse(req) + spec = requires_to_requires_dist(parsed_requirement) + extras = ",".join(sorted(parsed_requirement.extras)) + if extras: + extras = "[%s]" % extras + yield (parsed_requirement.project_name + extras + spec) + + +def generate_requirements(extras_require): + """ + Convert requirements from a setup()-style dictionary to ('Requires-Dist', 'requirement') + and ('Provides-Extra', 'extra') tuples. + + extras_require is a dictionary of {extra: [requirements]} as passed to setup(), + using the empty extra {'': [requirements]} to hold install_requires. + """ + for extra, depends in extras_require.items(): + condition = '' + extra = extra or '' + if ':' in extra: # setuptools extra:condition syntax + extra, condition = extra.split(':', 1) + + extra = pkg_resources.safe_extra(extra) + if extra: + yield 'Provides-Extra', extra + if condition: + condition = "(" + condition + ") and " + condition += "extra == '%s'" % extra + + if condition: + condition = ' ; ' + condition + + for new_req in convert_requirements(depends): + yield 'Requires-Dist', new_req + condition + + +def pkginfo_to_metadata(egg_info_path, pkginfo_path): + """ + Convert .egg-info directory with PKG-INFO to the Metadata 2.1 format + """ + pkg_info = read_pkg_info(pkginfo_path) + pkg_info.replace_header('Metadata-Version', '2.1') + # Those will be regenerated from `requires.txt`. + del pkg_info['Provides-Extra'] + del pkg_info['Requires-Dist'] + requires_path = os.path.join(egg_info_path, 'requires.txt') + if os.path.exists(requires_path): + with open(requires_path) as requires_file: + requires = requires_file.read() + + parsed_requirements = sorted(pkg_resources.split_sections(requires), + key=lambda x: x[0] or '') + for extra, reqs in parsed_requirements: + for key, value in generate_requirements({extra: reqs}): + if (key, value) not in pkg_info.items(): + pkg_info[key] = value + + description = pkg_info['Description'] + if description: + pkg_info.set_payload(dedent_description(pkg_info)) + del pkg_info['Description'] + + return pkg_info + + +def pkginfo_unicode(pkg_info, field): + """Hack to coax Unicode out of an email Message() - Python 3.3+""" + text = pkg_info[field] + field = field.lower() + if not isinstance(text, str): + if not hasattr(pkg_info, 'raw_items'): # Python 3.2 + return str(text) + for item in pkg_info.raw_items(): + if item[0].lower() == field: + text = item[1].encode('ascii', 'surrogateescape') \ + .decode('utf-8') + break + + return text + + +def dedent_description(pkg_info): + """ + Dedent and convert pkg_info['Description'] to Unicode. + """ + description = pkg_info['Description'] + + # Python 3 Unicode handling, sorta. + surrogates = False + if not isinstance(description, str): + surrogates = True + description = pkginfo_unicode(pkg_info, 'Description') + + description_lines = description.splitlines() + description_dedent = '\n'.join( + # if the first line of long_description is blank, + # the first line here will be indented. + (description_lines[0].lstrip(), + textwrap.dedent('\n'.join(description_lines[1:])), + '\n')) + + if surrogates: + description_dedent = description_dedent \ + .encode("utf8") \ + .decode("ascii", "surrogateescape") + + return description_dedent diff --git a/venv2/lib/python2.7/site-packages/wheel/pep425tags.py b/venv2/lib/python2.7/site-packages/wheel/pep425tags.py new file mode 100644 index 0000000..13b5073 --- /dev/null +++ b/venv2/lib/python2.7/site-packages/wheel/pep425tags.py @@ -0,0 +1,185 @@ +"""Generate and work with PEP 425 Compatibility Tags.""" + +import distutils.util +import platform +import sys +import sysconfig +import warnings + +try: + from importlib.machinery import get_all_suffixes +except ImportError: + from imp import get_suffixes as get_all_suffixes + + +def get_config_var(var): + try: + return sysconfig.get_config_var(var) + except IOError as e: # pip Issue #1074 + warnings.warn("{0}".format(e), RuntimeWarning) + return None + + +def get_abbr_impl(): + """Return abbreviated implementation name.""" + impl = platform.python_implementation() + if impl == 'PyPy': + return 'pp' + elif impl == 'Jython': + return 'jy' + elif impl == 'IronPython': + return 'ip' + elif impl == 'CPython': + return 'cp' + + raise LookupError('Unknown Python implementation: ' + impl) + + +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_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: + warnings.warn("Config variable '{0}' is unset, Python ABI tag may " + "be incorrect".format(var), RuntimeWarning, 2) + 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 get_platform(): + """Return our platform name 'win32', 'linux_x86_64'""" + # XXX remove distutils dependency + result = distutils.util.get_platform().replace('.', '_').replace('-', '_') + if result == "linux_x86_64" and sys.maxsize == 2147483647: + # pip pull request #3497 + result = "linux_i686" + return result + + +def get_supported(versions=None, supplied_platform=None): + """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() + for suffix in get_all_suffixes(): + if suffix[0].startswith('.abi'): + abi3s.add(suffix[0].split('.', 2)[1]) + + abis.extend(sorted(list(abi3s))) + + abis.append('none') + + platforms = [] + if supplied_platform: + platforms.append(supplied_platform) + platforms.append(get_platform()) + + # Current version, current API (built specifically for our Python): + for abi in abis: + for arch in platforms: + supported.append(('%s%s' % (impl, versions[0]), abi, arch)) + + # abi3 modules compatible with older version of Python + for version in versions[1:]: + # abi3 was introduced in Python 3.2 + if version in ('31', '30'): + break + for abi in abi3s: # empty set if not Python 3 + for arch in platforms: + supported.append(("%s%s" % (impl, version), abi, arch)) + + # No abi / arch, but requires our implementation: + for i, version in enumerate(versions): + supported.append(('%s%s' % (impl, version), 'none', 'any')) + if i == 0: + # Tagged specifically as being cross-version compatible + # (with just the major version specified) + supported.append(('%s%s' % (impl, versions[0][0]), 'none', 'any')) + + # Major Python version + platform; e.g. binaries not using the Python API + for arch in platforms: + supported.append(('py%s' % (versions[0][0]), 'none', arch)) + + # 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 diff --git a/venv2/lib/python2.7/site-packages/wheel/pkginfo.py b/venv2/lib/python2.7/site-packages/wheel/pkginfo.py new file mode 100644 index 0000000..115be45 --- /dev/null +++ b/venv2/lib/python2.7/site-packages/wheel/pkginfo.py @@ -0,0 +1,43 @@ +"""Tools for reading and writing PKG-INFO / METADATA without caring +about the encoding.""" + +from email.parser import Parser + +try: + unicode + _PY3 = False +except NameError: + _PY3 = True + +if not _PY3: + from email.generator import Generator + + def read_pkg_info_bytes(bytestr): + return Parser().parsestr(bytestr) + + def read_pkg_info(path): + with open(path, "r") as headers: + message = Parser().parse(headers) + return message + + def write_pkg_info(path, message): + with open(path, 'w') as metadata: + Generator(metadata, mangle_from_=False, maxheaderlen=0).flatten(message) +else: + from email.generator import BytesGenerator + + def read_pkg_info_bytes(bytestr): + headers = bytestr.decode(encoding="ascii", errors="surrogateescape") + message = Parser().parsestr(headers) + return message + + def read_pkg_info(path): + with open(path, "r", + encoding="ascii", + errors="surrogateescape") as headers: + message = Parser().parse(headers) + return message + + def write_pkg_info(path, message): + with open(path, "wb") as out: + BytesGenerator(out, mangle_from_=False, maxheaderlen=0).flatten(message) diff --git a/venv2/lib/python2.7/site-packages/wheel/util.py b/venv2/lib/python2.7/site-packages/wheel/util.py new file mode 100644 index 0000000..0afb54a --- /dev/null +++ b/venv2/lib/python2.7/site-packages/wheel/util.py @@ -0,0 +1,46 @@ +import base64 +import io +import sys + + +if sys.version_info[0] < 3: + text_type = unicode # noqa: F821 + + StringIO = io.BytesIO + + def native(s, encoding='utf-8'): + if isinstance(s, unicode): + return s.encode(encoding) + return s +else: + text_type = str + + StringIO = io.StringIO + + def native(s, encoding='utf-8'): + if isinstance(s, bytes): + return s.decode(encoding) + return s + + +def urlsafe_b64encode(data): + """urlsafe_b64encode without padding""" + return base64.urlsafe_b64encode(data).rstrip(b'=') + + +def urlsafe_b64decode(data): + """urlsafe_b64decode without padding""" + pad = b'=' * (4 - (len(data) & 3)) + return base64.urlsafe_b64decode(data + pad) + + +def as_unicode(s): + if isinstance(s, bytes): + return s.decode('utf-8') + return s + + +def as_bytes(s): + if isinstance(s, text_type): + return s.encode('utf-8') + return s diff --git a/venv2/lib/python2.7/site-packages/wheel/wheelfile.py b/venv2/lib/python2.7/site-packages/wheel/wheelfile.py new file mode 100644 index 0000000..ddf8509 --- /dev/null +++ b/venv2/lib/python2.7/site-packages/wheel/wheelfile.py @@ -0,0 +1,169 @@ +from __future__ import print_function + +import csv +import hashlib +import os.path +import re +import stat +import time +from collections import OrderedDict +from distutils import log as logger +from zipfile import ZIP_DEFLATED, ZipInfo, ZipFile + +from wheel.cli import WheelError +from wheel.util import urlsafe_b64decode, as_unicode, native, urlsafe_b64encode, as_bytes, StringIO + +# Non-greedy matching of an optional build number may be too clever (more +# invalid wheel filenames will match). Separate regex for .dist-info? +WHEEL_INFO_RE = re.compile( + r"""^(?P(?P.+?)-(?P.+?))(-(?P\d[^-]*))? + -(?P.+?)-(?P.+?)-(?P.+?)\.whl$""", + re.VERBOSE) + + +def get_zipinfo_datetime(timestamp=None): + # Some applications need reproducible .whl files, but they can't do this without forcing + # the timestamp of the individual ZipInfo objects. See issue #143. + timestamp = int(os.environ.get('SOURCE_DATE_EPOCH', timestamp or time.time())) + return time.gmtime(timestamp)[0:6] + + +class WheelFile(ZipFile): + """A ZipFile derivative class that also reads SHA-256 hashes from + .dist-info/RECORD and checks any read files against those. + """ + + _default_algorithm = hashlib.sha256 + + def __init__(self, file, mode='r'): + basename = os.path.basename(file) + self.parsed_filename = WHEEL_INFO_RE.match(basename) + if not basename.endswith('.whl') or self.parsed_filename is None: + raise WheelError("Bad wheel filename {!r}".format(basename)) + + ZipFile.__init__(self, file, mode, compression=ZIP_DEFLATED, allowZip64=True) + + self.dist_info_path = '{}.dist-info'.format(self.parsed_filename.group('namever')) + self.record_path = self.dist_info_path + '/RECORD' + self._file_hashes = OrderedDict() + self._file_sizes = {} + if mode == 'r': + # Ignore RECORD and any embedded wheel signatures + self._file_hashes[self.record_path] = None, None + self._file_hashes[self.record_path + '.jws'] = None, None + self._file_hashes[self.record_path + '.p7s'] = None, None + + # Fill in the expected hashes by reading them from RECORD + try: + record = self.open(self.record_path) + except KeyError: + raise WheelError('Missing {} file'.format(self.record_path)) + + with record: + for line in record: + line = line.decode('utf-8') + path, hash_sum, size = line.rsplit(u',', 2) + if hash_sum: + algorithm, hash_sum = hash_sum.split(u'=') + try: + hashlib.new(algorithm) + except ValueError: + raise WheelError('Unsupported hash algorithm: {}'.format(algorithm)) + + if algorithm.lower() in {'md5', 'sha1'}: + raise WheelError( + 'Weak hash algorithm ({}) is not permitted by PEP 427' + .format(algorithm)) + + self._file_hashes[path] = ( + algorithm, urlsafe_b64decode(hash_sum.encode('ascii'))) + + def open(self, name_or_info, mode="r", pwd=None): + def _update_crc(newdata, eof=None): + if eof is None: + eof = ef._eof + update_crc_orig(newdata) + else: # Python 2 + update_crc_orig(newdata, eof) + + running_hash.update(newdata) + if eof and running_hash.digest() != expected_hash: + raise WheelError("Hash mismatch for file '{}'".format(native(ef_name))) + + ef = ZipFile.open(self, name_or_info, mode, pwd) + ef_name = as_unicode(name_or_info.filename if isinstance(name_or_info, ZipInfo) + else name_or_info) + if mode == 'r' and not ef_name.endswith('/'): + if ef_name not in self._file_hashes: + raise WheelError("No hash found for file '{}'".format(native(ef_name))) + + algorithm, expected_hash = self._file_hashes[ef_name] + if expected_hash is not None: + # Monkey patch the _update_crc method to also check for the hash from RECORD + running_hash = hashlib.new(algorithm) + update_crc_orig, ef._update_crc = ef._update_crc, _update_crc + + return ef + + def write_files(self, base_dir): + logger.info("creating '%s' and adding '%s' to it", self.filename, base_dir) + deferred = [] + for root, dirnames, filenames in os.walk(base_dir): + # Sort the directory names so that `os.walk` will walk them in a + # defined order on the next iteration. + dirnames.sort() + for name in sorted(filenames): + path = os.path.normpath(os.path.join(root, name)) + if os.path.isfile(path): + arcname = os.path.relpath(path, base_dir) + if arcname == self.record_path: + pass + elif root.endswith('.dist-info'): + deferred.append((path, arcname)) + else: + self.write(path, arcname) + + deferred.sort() + for path, arcname in deferred: + self.write(path, arcname) + + def write(self, filename, arcname=None, compress_type=None): + with open(filename, 'rb') as f: + st = os.fstat(f.fileno()) + data = f.read() + + zinfo = ZipInfo(arcname or filename, date_time=get_zipinfo_datetime(st.st_mtime)) + zinfo.external_attr = (stat.S_IMODE(st.st_mode) | stat.S_IFMT(st.st_mode)) << 16 + zinfo.compress_type = ZIP_DEFLATED + self.writestr(zinfo, data, compress_type) + + def writestr(self, zinfo_or_arcname, bytes, compress_type=None): + ZipFile.writestr(self, zinfo_or_arcname, bytes, compress_type) + fname = (zinfo_or_arcname.filename if isinstance(zinfo_or_arcname, ZipInfo) + else zinfo_or_arcname) + logger.info("adding '%s'", fname) + if fname != self.record_path: + hash_ = self._default_algorithm(bytes) + self._file_hashes[fname] = hash_.name, native(urlsafe_b64encode(hash_.digest())) + self._file_sizes[fname] = len(bytes) + + def close(self): + # Write RECORD + if self.fp is not None and self.mode == 'w' and self._file_hashes: + data = StringIO() + writer = csv.writer(data, delimiter=',', quotechar='"', lineterminator='\n') + writer.writerows(( + ( + fname, + algorithm + "=" + hash_, + self._file_sizes[fname] + ) + for fname, (algorithm, hash_) in self._file_hashes.items() + )) + writer.writerow((format(self.record_path), "", "")) + zinfo = ZipInfo(native(self.record_path), date_time=get_zipinfo_datetime()) + zinfo.compress_type = ZIP_DEFLATED + zinfo.external_attr = 0o664 << 16 + self.writestr(zinfo, as_bytes(data.getvalue())) + + ZipFile.close(self) diff --git a/venv2/lib/python2.7/site.py b/venv2/lib/python2.7/site.py new file mode 100644 index 0000000..ede3ca3 --- /dev/null +++ b/venv2/lib/python2.7/site.py @@ -0,0 +1,851 @@ +"""Append module search paths for third-party packages to sys.path. + +**************************************************************** +* This module is automatically imported during initialization. * +**************************************************************** + +In earlier versions of Python (up to 1.5a3), scripts or modules that +needed to use site-specific modules would place ``import site'' +somewhere near the top of their code. Because of the automatic +import, this is no longer necessary (but code that does it still +works). + +This will append site-specific paths to the module search path. On +Unix, it starts with sys.prefix and sys.exec_prefix (if different) and +appends lib/python/site-packages as well as lib/site-python. +It also supports the Debian convention of +lib/python/dist-packages. On other platforms (mainly Mac and +Windows), it uses just sys.prefix (and sys.exec_prefix, if different, +but this is unlikely). The resulting directories, if they exist, are +appended to sys.path, and also inspected for path configuration files. + +FOR DEBIAN, this sys.path is augmented with directories in /usr/local. +Local addons go into /usr/local/lib/python/site-packages +(resp. /usr/local/lib/site-python), Debian addons install into +/usr/{lib,share}/python/dist-packages. + +A path configuration file is a file whose name has the form +.pth; its contents are additional directories (one per line) +to be added to sys.path. Non-existing directories (or +non-directories) are never added to sys.path; no directory is added to +sys.path more than once. Blank lines and lines beginning with +'#' are skipped. Lines starting with 'import' are executed. + +For example, suppose sys.prefix and sys.exec_prefix are set to +/usr/local and there is a directory /usr/local/lib/python2.X/site-packages +with three subdirectories, foo, bar and spam, and two path +configuration files, foo.pth and bar.pth. Assume foo.pth contains the +following: + + # foo package configuration + foo + bar + bletch + +and bar.pth contains: + + # bar package configuration + bar + +Then the following directories are added to sys.path, in this order: + + /usr/local/lib/python2.X/site-packages/bar + /usr/local/lib/python2.X/site-packages/foo + +Note that bletch is omitted because it doesn't exist; bar precedes foo +because bar.pth comes alphabetically before foo.pth; and spam is +omitted because it is not mentioned in either path configuration file. + +After these path manipulations, an attempt is made to import a module +named sitecustomize, which can perform arbitrary additional +site-specific customizations. If this import fails with an +ImportError exception, it is silently ignored. + +""" + +import os +import sys + +try: + import __builtin__ as builtins +except ImportError: + import builtins +try: + set +except NameError: + from sets import Set as set + +# Prefixes for site-packages; add additional prefixes like /usr/local here +PREFIXES = [sys.prefix, sys.exec_prefix] +# Enable per user site-packages directory +# set it to False to disable the feature or True to force the feature +ENABLE_USER_SITE = None +# for distutils.commands.install +USER_SITE = None +USER_BASE = None + +_is_64bit = (getattr(sys, "maxsize", None) or getattr(sys, "maxint")) > 2 ** 32 +_is_pypy = hasattr(sys, "pypy_version_info") +_is_jython = sys.platform[:4] == "java" +if _is_jython: + ModuleType = type(os) + + +def makepath(*paths): + dir = os.path.join(*paths) + if _is_jython and (dir == "__classpath__" or dir.startswith("__pyclasspath__")): + return dir, dir + dir = os.path.abspath(dir) + return dir, os.path.normcase(dir) + + +def abs__file__(): + """Set all module' __file__ attribute to an absolute path""" + for m in sys.modules.values(): + if (_is_jython and not isinstance(m, ModuleType)) or hasattr(m, "__loader__"): + # only modules need the abspath in Jython. and don't mess + # with a PEP 302-supplied __file__ + continue + f = getattr(m, "__file__", None) + if f is None: + continue + m.__file__ = os.path.abspath(f) + + +def removeduppaths(): + """ Remove duplicate entries from sys.path along with making them + absolute""" + # This ensures that the initial path provided by the interpreter contains + # only absolute pathnames, even if we're running from the build directory. + L = [] + known_paths = set() + for dir in sys.path: + # Filter out duplicate paths (on case-insensitive file systems also + # if they only differ in case); turn relative paths into absolute + # paths. + dir, dircase = makepath(dir) + if not dircase in known_paths: + L.append(dir) + known_paths.add(dircase) + sys.path[:] = L + return known_paths + + +# XXX This should not be part of site.py, since it is needed even when +# using the -S option for Python. See http://www.python.org/sf/586680 +def addbuilddir(): + """Append ./build/lib. in case we're running in the build dir + (especially for Guido :-)""" + from distutils.util import get_platform + + s = "build/lib.{}-{:.3}".format(get_platform(), sys.version) + if hasattr(sys, "gettotalrefcount"): + s += "-pydebug" + s = os.path.join(os.path.dirname(sys.path[-1]), s) + sys.path.append(s) + + +def _init_pathinfo(): + """Return a set containing all existing directory entries from sys.path""" + d = set() + for dir in sys.path: + try: + if os.path.isdir(dir): + dir, dircase = makepath(dir) + d.add(dircase) + except TypeError: + continue + return d + + +def addpackage(sitedir, name, known_paths): + """Add a new path to known_paths by combining sitedir and 'name' or execute + sitedir if it starts with 'import'""" + if known_paths is None: + _init_pathinfo() + reset = 1 + else: + reset = 0 + fullname = os.path.join(sitedir, name) + try: + f = open(fullname, "r") + except IOError: + return + try: + for line in f: + if line.startswith("#"): + continue + if line.startswith("import"): + exec(line) + continue + line = line.rstrip() + dir, dircase = makepath(sitedir, line) + if not dircase in known_paths and os.path.exists(dir): + sys.path.append(dir) + known_paths.add(dircase) + finally: + f.close() + if reset: + known_paths = None + return known_paths + + +def addsitedir(sitedir, known_paths=None): + """Add 'sitedir' argument to sys.path if missing and handle .pth files in + 'sitedir'""" + if known_paths is None: + known_paths = _init_pathinfo() + reset = 1 + else: + reset = 0 + sitedir, sitedircase = makepath(sitedir) + if not sitedircase in known_paths: + sys.path.append(sitedir) # Add path component + try: + names = os.listdir(sitedir) + except os.error: + return + names.sort() + for name in names: + if name.endswith(os.extsep + "pth"): + addpackage(sitedir, name, known_paths) + if reset: + known_paths = None + return known_paths + + +def addsitepackages(known_paths, sys_prefix=sys.prefix, exec_prefix=sys.exec_prefix): + """Add site-packages (and possibly site-python) to sys.path""" + prefixes = [os.path.join(sys_prefix, "local"), sys_prefix] + if exec_prefix != sys_prefix: + prefixes.append(os.path.join(exec_prefix, "local")) + + for prefix in prefixes: + if prefix: + if sys.platform in ("os2emx", "riscos") or _is_jython: + sitedirs = [os.path.join(prefix, "Lib", "site-packages")] + elif _is_pypy: + sitedirs = [os.path.join(prefix, "site-packages")] + elif sys.platform == "darwin" and prefix == sys_prefix: + + if prefix.startswith("/System/Library/Frameworks/"): # Apple's Python + + sitedirs = [ + os.path.join("/Library/Python", sys.version[:3], "site-packages"), + os.path.join(prefix, "Extras", "lib", "python"), + ] + + else: # any other Python distros on OSX work this way + sitedirs = [os.path.join(prefix, "lib", "python" + sys.version[:3], "site-packages")] + + elif os.sep == "/": + sitedirs = [ + os.path.join(prefix, "lib", "python" + sys.version[:3], "site-packages"), + os.path.join(prefix, "lib", "site-python"), + os.path.join(prefix, "python" + sys.version[:3], "lib-dynload"), + ] + lib64_dir = os.path.join(prefix, "lib64", "python" + sys.version[:3], "site-packages") + if os.path.exists(lib64_dir) and os.path.realpath(lib64_dir) not in [ + os.path.realpath(p) for p in sitedirs + ]: + if _is_64bit: + sitedirs.insert(0, lib64_dir) + else: + sitedirs.append(lib64_dir) + try: + # sys.getobjects only available in --with-pydebug build + sys.getobjects + sitedirs.insert(0, os.path.join(sitedirs[0], "debug")) + except AttributeError: + pass + # Debian-specific dist-packages directories: + sitedirs.append(os.path.join(prefix, "local/lib", "python" + sys.version[:3], "dist-packages")) + if sys.version[0] == "2": + sitedirs.append(os.path.join(prefix, "lib", "python" + sys.version[:3], "dist-packages")) + else: + sitedirs.append(os.path.join(prefix, "lib", "python" + sys.version[0], "dist-packages")) + sitedirs.append(os.path.join(prefix, "lib", "dist-python")) + else: + sitedirs = [prefix, os.path.join(prefix, "lib", "site-packages")] + 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")) + for sitedir in sitedirs: + if os.path.isdir(sitedir): + addsitedir(sitedir, known_paths) + return None + + +def check_enableusersite(): + """Check if user site directory is safe for inclusion + + The function tests for the command line flag (including environment var), + process uid/gid equal to effective uid/gid. + + None: Disabled for security reasons + False: Disabled by user (command line option) + True: Safe and enabled + """ + if hasattr(sys, "flags") and getattr(sys.flags, "no_user_site", False): + return False + + if hasattr(os, "getuid") and hasattr(os, "geteuid"): + # check process uid == effective uid + if os.geteuid() != os.getuid(): + return None + if hasattr(os, "getgid") and hasattr(os, "getegid"): + # check process gid == effective gid + if os.getegid() != os.getgid(): + return None + + return True + + +def addusersitepackages(known_paths): + """Add a per user site-package to sys.path + + Each user has its own python directory with site-packages in the + home directory. + + USER_BASE is the root directory for all Python versions + + USER_SITE is the user specific site-packages directory + + USER_SITE/.. can be used for data. + """ + global USER_BASE, USER_SITE, ENABLE_USER_SITE + env_base = os.environ.get("PYTHONUSERBASE", None) + + def joinuser(*args): + return os.path.expanduser(os.path.join(*args)) + + # if sys.platform in ('os2emx', 'riscos'): + # # Don't know what to put here + # USER_BASE = '' + # USER_SITE = '' + if os.name == "nt": + base = os.environ.get("APPDATA") or "~" + if env_base: + USER_BASE = env_base + else: + USER_BASE = joinuser(base, "Python") + USER_SITE = os.path.join(USER_BASE, "Python" + sys.version[0] + sys.version[2], "site-packages") + else: + if env_base: + USER_BASE = env_base + else: + USER_BASE = joinuser("~", ".local") + USER_SITE = os.path.join(USER_BASE, "lib", "python" + sys.version[:3], "site-packages") + + if ENABLE_USER_SITE and os.path.isdir(USER_SITE): + addsitedir(USER_SITE, known_paths) + if ENABLE_USER_SITE: + for dist_libdir in ("lib", "local/lib"): + user_site = os.path.join(USER_BASE, dist_libdir, "python" + sys.version[:3], "dist-packages") + if os.path.isdir(user_site): + addsitedir(user_site, known_paths) + return known_paths + + +def setBEGINLIBPATH(): + """The OS/2 EMX port has optional extension modules that do double duty + as DLLs (and must use the .DLL file extension) for other extensions. + The library search path needs to be amended so these will be found + during module import. Use BEGINLIBPATH so that these are at the start + of the library search path. + + """ + dllpath = os.path.join(sys.prefix, "Lib", "lib-dynload") + libpath = os.environ["BEGINLIBPATH"].split(";") + if libpath[-1]: + libpath.append(dllpath) + else: + libpath[-1] = dllpath + os.environ["BEGINLIBPATH"] = ";".join(libpath) + + +def setquit(): + """Define new built-ins 'quit' and 'exit'. + These are simply strings that display a hint on how to exit. + + """ + if os.sep == ":": + eof = "Cmd-Q" + elif os.sep == "\\": + eof = "Ctrl-Z plus Return" + else: + eof = "Ctrl-D (i.e. EOF)" + + class Quitter(object): + def __init__(self, name): + self.name = name + + def __repr__(self): + return "Use {}() or {} to exit".format(self.name, eof) + + def __call__(self, code=None): + # Shells like IDLE catch the SystemExit, but listen when their + # stdin wrapper is closed. + try: + sys.stdin.close() + except: + pass + raise SystemExit(code) + + builtins.quit = Quitter("quit") + builtins.exit = Quitter("exit") + + +class _Printer(object): + """interactive prompt objects for printing the license text, a list of + contributors and the copyright notice.""" + + MAXLINES = 23 + + def __init__(self, name, data, files=(), dirs=()): + self.__name = name + self.__data = data + self.__files = files + self.__dirs = dirs + self.__lines = None + + def __setup(self): + if self.__lines: + return + data = None + for dir in self.__dirs: + for filename in self.__files: + filename = os.path.join(dir, filename) + try: + fp = open(filename, "r") + data = fp.read() + fp.close() + break + except IOError: + pass + if data: + break + if not data: + data = self.__data + self.__lines = data.split("\n") + self.__linecnt = len(self.__lines) + + def __repr__(self): + self.__setup() + if len(self.__lines) <= self.MAXLINES: + return "\n".join(self.__lines) + else: + return "Type %s() to see the full %s text" % ((self.__name,) * 2) + + def __call__(self): + self.__setup() + prompt = "Hit Return for more, or q (and Return) to quit: " + lineno = 0 + while 1: + try: + for i in range(lineno, lineno + self.MAXLINES): + print(self.__lines[i]) + except IndexError: + break + else: + lineno += self.MAXLINES + key = None + while key is None: + try: + key = raw_input(prompt) + except NameError: + key = input(prompt) + if key not in ("", "q"): + key = None + if key == "q": + break + + +def setcopyright(): + """Set 'copyright' and 'credits' in __builtin__""" + builtins.copyright = _Printer("copyright", sys.copyright) + if _is_jython: + builtins.credits = _Printer("credits", "Jython is maintained by the Jython developers (www.jython.org).") + elif _is_pypy: + builtins.credits = _Printer("credits", "PyPy is maintained by the PyPy developers: http://pypy.org/") + else: + builtins.credits = _Printer( + "credits", + """\ + Thanks to CWI, CNRI, BeOpen.com, Zope Corporation and a cast of thousands + for supporting Python development. See www.python.org for more information.""", + ) + here = os.path.dirname(os.__file__) + builtins.license = _Printer( + "license", + "See https://www.python.org/psf/license/", + ["LICENSE.txt", "LICENSE"], + [sys.prefix, os.path.join(here, os.pardir), here, os.curdir], + ) + + +class _Helper(object): + """Define the built-in 'help'. + This is a wrapper around pydoc.help (with a twist). + + """ + + def __repr__(self): + return "Type help() for interactive help, " "or help(object) for help about object." + + def __call__(self, *args, **kwds): + import pydoc + + return pydoc.help(*args, **kwds) + + +def sethelper(): + builtins.help = _Helper() + + +def aliasmbcs(): + """On Windows, some default encodings are not provided by Python, + while they are always available as "mbcs" in each locale. Make + them usable by aliasing to "mbcs" in such a case.""" + if sys.platform == "win32": + import locale, codecs + + enc = locale.getdefaultlocale()[1] + if enc.startswith("cp"): # "cp***" ? + try: + codecs.lookup(enc) + except LookupError: + import encodings + + encodings._cache[enc] = encodings._unknown + encodings.aliases.aliases[enc] = "mbcs" + + +def setencoding(): + """Set the string encoding used by the Unicode implementation. The + default is 'ascii', but if you're willing to experiment, you can + change this.""" + encoding = "ascii" # Default value set by _PyUnicode_Init() + if 0: + # Enable to support locale aware default string encodings. + import locale + + loc = locale.getdefaultlocale() + if loc[1]: + encoding = loc[1] + if 0: + # Enable to switch off string to Unicode coercion and implicit + # Unicode to string conversion. + encoding = "undefined" + if encoding != "ascii": + # On Non-Unicode builds this will raise an AttributeError... + sys.setdefaultencoding(encoding) # Needs Python Unicode build ! + + +def execsitecustomize(): + """Run custom site specific code, if available.""" + try: + import sitecustomize + except ImportError: + pass + + +def virtual_install_main_packages(): + f = open(os.path.join(os.path.dirname(__file__), "orig-prefix.txt")) + sys.real_prefix = f.read().strip() + f.close() + pos = 2 + hardcoded_relative_dirs = [] + if sys.path[0] == "": + pos += 1 + if _is_jython: + paths = [os.path.join(sys.real_prefix, "Lib")] + elif _is_pypy: + if sys.version_info > (3, 2): + cpyver = "%d" % sys.version_info[0] + elif sys.pypy_version_info >= (1, 5): + cpyver = "%d.%d" % sys.version_info[:2] + else: + cpyver = "%d.%d.%d" % sys.version_info[:3] + paths = [os.path.join(sys.real_prefix, "lib_pypy"), os.path.join(sys.real_prefix, "lib-python", cpyver)] + if sys.pypy_version_info < (1, 9): + paths.insert(1, os.path.join(sys.real_prefix, "lib-python", "modified-%s" % cpyver)) + hardcoded_relative_dirs = paths[:] # for the special 'darwin' case below + # + # This is hardcoded in the Python executable, but relative to sys.prefix: + for path in paths[:]: + plat_path = os.path.join(path, "plat-%s" % sys.platform) + if os.path.exists(plat_path): + paths.append(plat_path) + elif sys.platform == "win32": + paths = [os.path.join(sys.real_prefix, "Lib"), os.path.join(sys.real_prefix, "DLLs")] + else: + paths = [os.path.join(sys.real_prefix, "lib", "python" + sys.version[:3])] + hardcoded_relative_dirs = paths[:] # for the special 'darwin' case below + lib64_path = os.path.join(sys.real_prefix, "lib64", "python" + sys.version[:3]) + if os.path.exists(lib64_path): + if _is_64bit: + paths.insert(0, lib64_path) + else: + paths.append(lib64_path) + # This is hardcoded in the Python executable, but relative to + # sys.prefix. Debian change: we need to add the multiarch triplet + # here, which is where the real stuff lives. As per PEP 421, in + # Python 3.3+, this lives in sys.implementation, while in Python 2.7 + # it lives in sys. + try: + arch = getattr(sys, "implementation", sys)._multiarch + except AttributeError: + # This is a non-multiarch aware Python. Fallback to the old way. + arch = sys.platform + plat_path = os.path.join(sys.real_prefix, "lib", "python" + sys.version[:3], "plat-%s" % arch) + if os.path.exists(plat_path): + paths.append(plat_path) + # This is hardcoded in the Python executable, but + # relative to sys.prefix, so we have to fix up: + for path in list(paths): + tk_dir = os.path.join(path, "lib-tk") + if os.path.exists(tk_dir): + paths.append(tk_dir) + + # These are hardcoded in the Apple's Python executable, + # but relative to sys.prefix, so we have to fix them up: + if sys.platform == "darwin": + hardcoded_paths = [ + os.path.join(relative_dir, module) + for relative_dir in hardcoded_relative_dirs + for module in ("plat-darwin", "plat-mac", "plat-mac/lib-scriptpackages") + ] + + for path in hardcoded_paths: + if os.path.exists(path): + paths.append(path) + + sys.path.extend(paths) + + +def force_global_eggs_after_local_site_packages(): + """ + Force easy_installed eggs in the global environment to get placed + in sys.path after all packages inside the virtualenv. This + maintains the "least surprise" result that packages in the + virtualenv always mask global packages, never the other way + around. + + """ + egginsert = getattr(sys, "__egginsert", 0) + for i, path in enumerate(sys.path): + if i > egginsert and path.startswith(sys.prefix): + egginsert = i + sys.__egginsert = egginsert + 1 + + +def virtual_addsitepackages(known_paths): + force_global_eggs_after_local_site_packages() + return addsitepackages(known_paths, sys_prefix=sys.real_prefix) + + +def fixclasspath(): + """Adjust the special classpath sys.path entries for Jython. These + entries should follow the base virtualenv lib directories. + """ + paths = [] + classpaths = [] + for path in sys.path: + if path == "__classpath__" or path.startswith("__pyclasspath__"): + classpaths.append(path) + else: + paths.append(path) + sys.path = paths + sys.path.extend(classpaths) + + +def execusercustomize(): + """Run custom user specific code, if available.""" + try: + import usercustomize + except ImportError: + pass + + +def enablerlcompleter(): + """Enable default readline configuration on interactive prompts, by + registering a sys.__interactivehook__. + If the readline module can be imported, the hook will set the Tab key + as completion key and register ~/.python_history as history file. + This can be overridden in the sitecustomize or usercustomize module, + or in a PYTHONSTARTUP file. + """ + + def register_readline(): + import atexit + + try: + import readline + import rlcompleter + except ImportError: + return + + # Reading the initialization (config) file may not be enough to set a + # completion key, so we set one first and then read the file. + readline_doc = getattr(readline, "__doc__", "") + if readline_doc is not None and "libedit" in readline_doc: + readline.parse_and_bind("bind ^I rl_complete") + else: + readline.parse_and_bind("tab: complete") + + try: + readline.read_init_file() + except OSError: + # An OSError here could have many causes, but the most likely one + # is that there's no .inputrc file (or .editrc file in the case of + # Mac OS X + libedit) in the expected location. In that case, we + # want to ignore the exception. + pass + + if readline.get_current_history_length() == 0: + # If no history was loaded, default to .python_history. + # The guard is necessary to avoid doubling history size at + # each interpreter exit when readline was already configured + # through a PYTHONSTARTUP hook, see: + # http://bugs.python.org/issue5845#msg198636 + history = os.path.join(os.path.expanduser("~"), ".python_history") + try: + readline.read_history_file(history) + except OSError: + pass + + def write_history(): + try: + readline.write_history_file(history) + except (FileNotFoundError, PermissionError): + # home directory does not exist or is not writable + # https://bugs.python.org/issue19891 + pass + + atexit.register(write_history) + + sys.__interactivehook__ = register_readline + + +if _is_pypy: + + def import_builtin_stuff(): + """PyPy specific: some built-in modules should be pre-imported because + some programs expect them to be in sys.modules on startup. This is ported + from PyPy's site.py. + """ + import encodings + + if "exceptions" in sys.builtin_module_names: + import exceptions + + if "zipimport" in sys.builtin_module_names: + import zipimport + + +def main(): + global ENABLE_USER_SITE + virtual_install_main_packages() + if _is_pypy: + import_builtin_stuff() + abs__file__() + paths_in_sys = removeduppaths() + if os.name == "posix" and sys.path and os.path.basename(sys.path[-1]) == "Modules": + addbuilddir() + if _is_jython: + fixclasspath() + GLOBAL_SITE_PACKAGES = not os.path.exists(os.path.join(os.path.dirname(__file__), "no-global-site-packages.txt")) + if not GLOBAL_SITE_PACKAGES: + ENABLE_USER_SITE = False + if ENABLE_USER_SITE is None: + ENABLE_USER_SITE = check_enableusersite() + paths_in_sys = addsitepackages(paths_in_sys) + paths_in_sys = addusersitepackages(paths_in_sys) + if GLOBAL_SITE_PACKAGES: + paths_in_sys = virtual_addsitepackages(paths_in_sys) + if sys.platform == "os2emx": + setBEGINLIBPATH() + setquit() + setcopyright() + sethelper() + if sys.version_info[0] == 3: + enablerlcompleter() + aliasmbcs() + setencoding() + execsitecustomize() + if ENABLE_USER_SITE: + execusercustomize() + # Remove sys.setdefaultencoding() so that users cannot change the + # encoding after initialization. The test for presence is needed when + # this module is run as a script, because this code is executed twice. + if hasattr(sys, "setdefaultencoding"): + del sys.setdefaultencoding + + +main() + + +def _script(): + help = """\ + %s [--user-base] [--user-site] + + Without arguments print some useful information + With arguments print the value of USER_BASE and/or USER_SITE separated + by '%s'. + + Exit codes with --user-base or --user-site: + 0 - user site directory is enabled + 1 - user site directory is disabled by user + 2 - uses site directory is disabled by super user + or for security reasons + >2 - unknown error + """ + args = sys.argv[1:] + if not args: + print("sys.path = [") + for dir in sys.path: + print(" {!r},".format(dir)) + print("]") + + def exists(path): + if os.path.isdir(path): + return "exists" + else: + return "doesn't exist" + + print("USER_BASE: {!r} ({})".format(USER_BASE, exists(USER_BASE))) + print("USER_SITE: {!r} ({})".format(USER_SITE, exists(USER_SITE))) + print("ENABLE_USER_SITE: %r" % ENABLE_USER_SITE) + sys.exit(0) + + buffer = [] + if "--user-base" in args: + buffer.append(USER_BASE) + if "--user-site" in args: + buffer.append(USER_SITE) + + if buffer: + print(os.pathsep.join(buffer)) + if ENABLE_USER_SITE: + sys.exit(0) + elif ENABLE_USER_SITE is False: + sys.exit(1) + elif ENABLE_USER_SITE is None: + sys.exit(2) + else: + sys.exit(3) + else: + import textwrap + + print(textwrap.dedent(help % (sys.argv[0], os.pathsep))) + sys.exit(10) + + +if __name__ == "__main__": + _script() diff --git a/venv2/lib/python2.7/sre.py b/venv2/lib/python2.7/sre.py new file mode 120000 index 0000000..27f81b9 --- /dev/null +++ b/venv2/lib/python2.7/sre.py @@ -0,0 +1 @@ +/usr/lib/python2.7/sre.py \ No newline at end of file diff --git a/venv2/lib/python2.7/sre_compile.py b/venv2/lib/python2.7/sre_compile.py new file mode 120000 index 0000000..dce5da4 --- /dev/null +++ b/venv2/lib/python2.7/sre_compile.py @@ -0,0 +1 @@ +/usr/lib/python2.7/sre_compile.py \ No newline at end of file diff --git a/venv2/lib/python2.7/sre_constants.py b/venv2/lib/python2.7/sre_constants.py new file mode 120000 index 0000000..b9c9797 --- /dev/null +++ b/venv2/lib/python2.7/sre_constants.py @@ -0,0 +1 @@ +/usr/lib/python2.7/sre_constants.py \ No newline at end of file diff --git a/venv2/lib/python2.7/sre_parse.py b/venv2/lib/python2.7/sre_parse.py new file mode 120000 index 0000000..f33a572 --- /dev/null +++ b/venv2/lib/python2.7/sre_parse.py @@ -0,0 +1 @@ +/usr/lib/python2.7/sre_parse.py \ No newline at end of file diff --git a/venv2/lib/python2.7/stat.py b/venv2/lib/python2.7/stat.py new file mode 120000 index 0000000..c1d654c --- /dev/null +++ b/venv2/lib/python2.7/stat.py @@ -0,0 +1 @@ +/usr/lib/python2.7/stat.py \ No newline at end of file diff --git a/venv2/lib/python2.7/types.py b/venv2/lib/python2.7/types.py new file mode 120000 index 0000000..5546478 --- /dev/null +++ b/venv2/lib/python2.7/types.py @@ -0,0 +1 @@ +/usr/lib/python2.7/types.py \ No newline at end of file diff --git a/venv2/lib/python2.7/warnings.py b/venv2/lib/python2.7/warnings.py new file mode 120000 index 0000000..a9c4730 --- /dev/null +++ b/venv2/lib/python2.7/warnings.py @@ -0,0 +1 @@ +/usr/lib/python2.7/warnings.py \ No newline at end of file diff --git a/venv2/local/bin b/venv2/local/bin new file mode 120000 index 0000000..9cec038 --- /dev/null +++ b/venv2/local/bin @@ -0,0 +1 @@ +/home/sean/Downloads/BMS-View/venv2/bin \ No newline at end of file diff --git a/venv2/local/lib b/venv2/local/lib new file mode 120000 index 0000000..8ebdbfe --- /dev/null +++ b/venv2/local/lib @@ -0,0 +1 @@ +/home/sean/Downloads/BMS-View/venv2/lib \ No newline at end of file

    J0ec%{X00Gw zMrkzY6|W+2m%4$O;{M;ZCg746Z_*h1P!3<-=|a`@;C!REUK-)jaRJ5&e+B&8s3X>Q zQ(P($)p1MuHGj#Cthfr;=_oB7mM)kh`?tC_qdtoUYYmxn zWB>NNiz27`w~F=ScI!ty^TUE%+>h%iV&iDuLBNlPH_#=xWW>4xnOTj|F=2$bH#%mV zNXHnR4)d0wzy>f;G%tCvY+mcGsz-!w6a0BoLMG}i3uHqRa=Bv6^@kN?3bO=cR|(0f z`~Ah>>^=BnR9$M0?(Q$iKK;c&@CUO5f@VaJH#u3qxsPFHa1WIxh9fsi5t;!g7(faq zm_T#+D{pd&zQ4BtqK;~rLQjd>?m`)(A46IA_+R{9B#`m`Yp4f8+3Pa+L3K;Ca;WkCw*K9}E|RrD_E0Z~@B_6PKbOWBRof zO@ckv5gc8iJD-b)R3(u!8E*I``nCmU%#n6hOcXg;Xs~*LrWh$=OAX1uodd+fg;BB- z_`t_##^H!FWh-TX57vtZLhK@#baA3|Ofl_^@OMK{sw+6Q0p8Jx@O=!%H7&o5Gc>e_ zBXFxD&>4t31Mv$$JOPMf*CCpDa5PfJC+T(PiSw{RFUSewJzsmzr8HQ7m#P8w?y*63O@eyus*aPpQ`QHEBV0h-5-M8Gp#FbDk%Q0DsxKQJ60x)a0k{GP~g zd`=JrtvBaHWK2Y6>)F3bt@lu8H5^j~A)JTC;FmEbL>M>m7DFJjykULuz)ENqszH^9 z@U6dSsq(sTB|(+-EL9$1q8Nrlm&q!(z_ml{krpybXn;gXY@+^0C_=0VXf7aID#*D8 z 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%0DV7S>p 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%0AV0`Urr17lttc{8M!DE5<#+=py>(02^-rE?I}oo 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

iML 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