From 5ac05fc1aef54c5f160920b6806dee5d00750516 Mon Sep 17 00:00:00 2001 From: taylorlynn Date: Wed, 21 Jun 2023 16:29:44 -0400 Subject: [PATCH] changes --- .DS_Store | Bin 0 -> 8196 bytes data/.DS_Store | Bin 0 -> 6148 bytes server/.DS_Store | Bin 0 -> 6148 bytes server/.gitignore | 1 - transformers | 1 + venv/bin/easy_install | 8 + venv/bin/easy_install-3.8 | 8 + venv/bin/python | 1 + venv/bin/python3 | 1 + .../__pycache__/easy_install.cpython-38.pyc | Bin 0 -> 333 bytes .../python3.8/site-packages/easy_install.py | 5 + .../python3.8/site-packages/pip/__init__.py | 18 + .../python3.8/site-packages/pip/__main__.py | 26 + .../site-packages/pip/_internal/__init__.py | 17 + .../__pycache__/__init__.cpython-38.pyc | Bin 0 -> 730 bytes .../__pycache__/build_env.cpython-38.pyc | Bin 0 -> 7443 bytes .../__pycache__/cache.cpython-38.pyc | Bin 0 -> 9142 bytes .../__pycache__/configuration.cpython-38.pyc | Bin 0 -> 10860 bytes .../__pycache__/exceptions.cpython-38.pyc | Bin 0 -> 12588 bytes .../__pycache__/locations.cpython-38.pyc | Bin 0 -> 4544 bytes .../_internal/__pycache__/main.cpython-38.pyc | Bin 0 -> 667 bytes .../__pycache__/pyproject.cpython-38.pyc | Bin 0 -> 3784 bytes .../self_outdated_check.cpython-38.pyc | Bin 0 -> 5545 bytes .../__pycache__/wheel_builder.cpython-38.pyc | Bin 0 -> 6852 bytes .../site-packages/pip/_internal/build_env.py | 219 + .../site-packages/pip/_internal/cache.py | 349 + .../pip/_internal/cli/__init__.py | 4 + .../cli/__pycache__/__init__.cpython-38.pyc | Bin 0 -> 288 bytes .../__pycache__/autocompletion.cpython-38.pyc | Bin 0 -> 5005 bytes .../__pycache__/base_command.cpython-38.pyc | Bin 0 -> 5812 bytes .../cli/__pycache__/cmdoptions.cpython-38.pyc | Bin 0 -> 20589 bytes .../command_context.cpython-38.pyc | Bin 0 -> 1363 bytes .../cli/__pycache__/main.cpython-38.pyc | Bin 0 -> 1468 bytes .../__pycache__/main_parser.cpython-38.pyc | Bin 0 -> 2251 bytes .../cli/__pycache__/parser.cpython-38.pyc | Bin 0 -> 9018 bytes .../__pycache__/progress_bars.cpython-38.pyc | Bin 0 -> 7713 bytes .../__pycache__/req_command.cpython-38.pyc | Bin 0 -> 9970 bytes .../cli/__pycache__/spinners.cpython-38.pyc | Bin 0 -> 4808 bytes .../__pycache__/status_codes.cpython-38.pyc | Bin 0 -> 417 bytes .../pip/_internal/cli/autocompletion.py | 164 + .../pip/_internal/cli/base_command.py | 228 + .../pip/_internal/cli/cmdoptions.py | 962 +++ .../pip/_internal/cli/command_context.py | 36 + .../site-packages/pip/_internal/cli/main.py | 75 + .../pip/_internal/cli/main_parser.py | 99 + .../site-packages/pip/_internal/cli/parser.py | 266 + .../pip/_internal/cli/progress_bars.py | 277 + .../pip/_internal/cli/req_command.py | 408 + .../pip/_internal/cli/spinners.py | 173 + .../pip/_internal/cli/status_codes.py | 8 + .../pip/_internal/commands/__init__.py | 122 + .../__pycache__/__init__.cpython-38.pyc | Bin 0 -> 3018 bytes .../commands/__pycache__/cache.cpython-38.pyc | Bin 0 -> 4446 bytes .../commands/__pycache__/check.cpython-38.pyc | Bin 0 -> 1616 bytes .../__pycache__/completion.cpython-38.pyc | Bin 0 -> 3069 bytes .../__pycache__/configuration.cpython-38.pyc | Bin 0 -> 6607 bytes .../commands/__pycache__/debug.cpython-38.pyc | Bin 0 -> 6430 bytes .../__pycache__/download.cpython-38.pyc | Bin 0 -> 3942 bytes .../__pycache__/freeze.cpython-38.pyc | Bin 0 -> 2902 bytes .../commands/__pycache__/hash.cpython-38.pyc | Bin 0 -> 2052 bytes .../commands/__pycache__/help.cpython-38.pyc | Bin 0 -> 1250 bytes .../__pycache__/install.cpython-38.pyc | Bin 0 -> 16044 bytes .../commands/__pycache__/list.cpython-38.pyc | Bin 0 -> 8421 bytes .../__pycache__/search.cpython-38.pyc | Bin 0 -> 4625 bytes .../commands/__pycache__/show.cpython-38.pyc | Bin 0 -> 6376 bytes .../__pycache__/uninstall.cpython-38.pyc | Bin 0 -> 2838 bytes .../commands/__pycache__/wheel.cpython-38.pyc | Bin 0 -> 5124 bytes .../pip/_internal/commands/cache.py | 181 + .../pip/_internal/commands/check.py | 51 + .../pip/_internal/commands/completion.py | 95 + .../pip/_internal/commands/configuration.py | 233 + .../pip/_internal/commands/debug.py | 237 + .../pip/_internal/commands/download.py | 142 + .../pip/_internal/commands/freeze.py | 99 + .../pip/_internal/commands/hash.py | 58 + .../pip/_internal/commands/help.py | 41 + .../pip/_internal/commands/install.py | 691 ++ .../pip/_internal/commands/list.py | 299 + .../pip/_internal/commands/search.py | 146 + .../pip/_internal/commands/show.py | 180 + .../pip/_internal/commands/uninstall.py | 89 + .../pip/_internal/commands/wheel.py | 190 + .../pip/_internal/configuration.py | 426 + .../pip/_internal/distributions/__init__.py | 24 + .../__pycache__/__init__.cpython-38.pyc | Bin 0 -> 864 bytes .../__pycache__/base.cpython-38.pyc | Bin 0 -> 1980 bytes .../__pycache__/installed.cpython-38.pyc | Bin 0 -> 1260 bytes .../__pycache__/sdist.cpython-38.pyc | Bin 0 -> 3523 bytes .../__pycache__/wheel.cpython-38.pyc | Bin 0 -> 1612 bytes .../pip/_internal/distributions/base.py | 45 + .../pip/_internal/distributions/installed.py | 24 + .../pip/_internal/distributions/sdist.py | 104 + .../pip/_internal/distributions/wheel.py | 36 + .../site-packages/pip/_internal/exceptions.py | 308 + .../pip/_internal/index/__init__.py | 2 + .../index/__pycache__/__init__.cpython-38.pyc | Bin 0 -> 242 bytes .../__pycache__/collector.cpython-38.pyc | Bin 0 -> 17564 bytes .../__pycache__/package_finder.cpython-38.pyc | Bin 0 -> 25845 bytes .../pip/_internal/index/collector.py | 661 ++ .../pip/_internal/index/package_finder.py | 1016 +++ .../site-packages/pip/_internal/locations.py | 194 + .../site-packages/pip/_internal/main.py | 16 + .../pip/_internal/models/__init__.py | 2 + .../__pycache__/__init__.cpython-38.pyc | Bin 0 -> 276 bytes .../__pycache__/candidate.cpython-38.pyc | Bin 0 -> 1473 bytes .../__pycache__/direct_url.cpython-38.pyc | Bin 0 -> 6550 bytes .../__pycache__/format_control.cpython-38.pyc | Bin 0 -> 2468 bytes .../models/__pycache__/index.cpython-38.pyc | Bin 0 -> 1198 bytes .../models/__pycache__/link.cpython-38.pyc | Bin 0 -> 7140 bytes .../models/__pycache__/scheme.cpython-38.pyc | Bin 0 -> 914 bytes .../__pycache__/search_scope.cpython-38.pyc | Bin 0 -> 3438 bytes .../selection_prefs.cpython-38.pyc | Bin 0 -> 1648 bytes .../__pycache__/target_python.cpython-38.pyc | Bin 0 -> 3301 bytes .../models/__pycache__/wheel.cpython-38.pyc | Bin 0 -> 3244 bytes .../pip/_internal/models/candidate.py | 36 + .../pip/_internal/models/direct_url.py | 245 + .../pip/_internal/models/format_control.py | 84 + .../pip/_internal/models/index.py | 31 + .../pip/_internal/models/link.py | 236 + .../pip/_internal/models/scheme.py | 25 + .../pip/_internal/models/search_scope.py | 133 + .../pip/_internal/models/selection_prefs.py | 47 + .../pip/_internal/models/target_python.py | 110 + .../pip/_internal/models/wheel.py | 78 + .../pip/_internal/network/__init__.py | 2 + .../__pycache__/__init__.cpython-38.pyc | Bin 0 -> 264 bytes .../network/__pycache__/auth.cpython-38.pyc | Bin 0 -> 7037 bytes .../network/__pycache__/cache.cpython-38.pyc | Bin 0 -> 2743 bytes .../__pycache__/download.cpython-38.pyc | Bin 0 -> 4431 bytes .../__pycache__/session.cpython-38.pyc | Bin 0 -> 9269 bytes .../network/__pycache__/utils.cpython-38.pyc | Bin 0 -> 758 bytes .../network/__pycache__/xmlrpc.cpython-38.pyc | Bin 0 -> 1626 bytes .../pip/_internal/network/auth.py | 298 + .../pip/_internal/network/cache.py | 81 + .../pip/_internal/network/download.py | 200 + .../pip/_internal/network/session.py | 421 + .../pip/_internal/network/utils.py | 48 + .../pip/_internal/network/xmlrpc.py | 44 + .../pip/_internal/operations/__init__.py | 0 .../__pycache__/__init__.cpython-38.pyc | Bin 0 -> 212 bytes .../__pycache__/check.cpython-38.pyc | Bin 0 -> 3708 bytes .../__pycache__/freeze.cpython-38.pyc | Bin 0 -> 5947 bytes .../__pycache__/prepare.cpython-38.pyc | Bin 0 -> 10945 bytes .../_internal/operations/build/__init__.py | 0 .../build/__pycache__/__init__.cpython-38.pyc | Bin 0 -> 218 bytes .../build/__pycache__/metadata.cpython-38.pyc | Bin 0 -> 1260 bytes .../metadata_legacy.cpython-38.pyc | Bin 0 -> 2002 bytes .../build/__pycache__/wheel.cpython-38.pyc | Bin 0 -> 1358 bytes .../__pycache__/wheel_legacy.cpython-38.pyc | Bin 0 -> 2628 bytes .../_internal/operations/build/metadata.py | 40 + .../operations/build/metadata_legacy.py | 77 + .../pip/_internal/operations/build/wheel.py | 46 + .../operations/build/wheel_legacy.py | 115 + .../pip/_internal/operations/check.py | 163 + .../pip/_internal/operations/freeze.py | 272 + .../_internal/operations/install/__init__.py | 2 + .../__pycache__/__init__.cpython-38.pyc | Bin 0 -> 276 bytes .../editable_legacy.cpython-38.pyc | Bin 0 -> 1354 bytes .../install/__pycache__/legacy.cpython-38.pyc | Bin 0 -> 3374 bytes .../install/__pycache__/wheel.cpython-38.pyc | Bin 0 -> 15123 bytes .../operations/install/editable_legacy.py | 52 + .../_internal/operations/install/legacy.py | 142 + .../pip/_internal/operations/install/wheel.py | 631 ++ .../pip/_internal/operations/prepare.py | 568 ++ .../site-packages/pip/_internal/pyproject.py | 196 + .../pip/_internal/req/__init__.py | 92 + .../req/__pycache__/__init__.cpython-38.pyc | Bin 0 -> 2246 bytes .../__pycache__/constructors.cpython-38.pyc | Bin 0 -> 10899 bytes .../req/__pycache__/req_file.cpython-38.pyc | Bin 0 -> 12861 bytes .../__pycache__/req_install.cpython-38.pyc | Bin 0 -> 20470 bytes .../req/__pycache__/req_set.cpython-38.pyc | Bin 0 -> 5892 bytes .../__pycache__/req_tracker.cpython-38.pyc | Bin 0 -> 4107 bytes .../__pycache__/req_uninstall.cpython-38.pyc | Bin 0 -> 17512 bytes .../pip/_internal/req/constructors.py | 464 ++ .../pip/_internal/req/req_file.py | 582 ++ .../pip/_internal/req/req_install.py | 850 ++ .../pip/_internal/req/req_set.py | 202 + .../pip/_internal/req/req_tracker.py | 151 + .../pip/_internal/req/req_uninstall.py | 649 ++ .../pip/_internal/resolution/__init__.py | 0 .../__pycache__/__init__.cpython-38.pyc | Bin 0 -> 212 bytes .../__pycache__/base.cpython-38.pyc | Bin 0 -> 1044 bytes .../pip/_internal/resolution/base.py | 20 + .../_internal/resolution/legacy/__init__.py | 0 .../__pycache__/__init__.cpython-38.pyc | Bin 0 -> 219 bytes .../__pycache__/resolver.cpython-38.pyc | Bin 0 -> 11190 bytes .../_internal/resolution/legacy/resolver.py | 459 ++ .../resolution/resolvelib/__init__.py | 0 .../__pycache__/__init__.cpython-38.pyc | Bin 0 -> 223 bytes .../__pycache__/base.cpython-38.pyc | Bin 0 -> 2281 bytes .../__pycache__/candidates.cpython-38.pyc | Bin 0 -> 13378 bytes .../__pycache__/factory.cpython-38.pyc | Bin 0 -> 5313 bytes .../__pycache__/provider.cpython-38.pyc | Bin 0 -> 1967 bytes .../__pycache__/requirements.cpython-38.pyc | Bin 0 -> 4377 bytes .../__pycache__/resolver.cpython-38.pyc | Bin 0 -> 5549 bytes .../_internal/resolution/resolvelib/base.py | 52 + .../resolution/resolvelib/candidates.py | 450 ++ .../resolution/resolvelib/factory.py | 201 + .../resolution/resolvelib/provider.py | 54 + .../resolution/resolvelib/requirements.py | 119 + .../resolution/resolvelib/resolver.py | 174 + .../pip/_internal/self_outdated_check.py | 242 + .../pip/_internal/utils/__init__.py | 0 .../utils/__pycache__/__init__.cpython-38.pyc | Bin 0 -> 207 bytes .../utils/__pycache__/appdirs.cpython-38.pyc | Bin 0 -> 1397 bytes .../utils/__pycache__/compat.cpython-38.pyc | Bin 0 -> 6090 bytes .../compatibility_tags.cpython-38.pyc | Bin 0 -> 3651 bytes .../__pycache__/deprecation.cpython-38.pyc | Bin 0 -> 2878 bytes .../direct_url_helpers.cpython-38.pyc | Bin 0 -> 2728 bytes .../__pycache__/distutils_args.cpython-38.pyc | Bin 0 -> 1191 bytes .../utils/__pycache__/encoding.cpython-38.pyc | Bin 0 -> 1299 bytes .../__pycache__/entrypoints.cpython-38.pyc | Bin 0 -> 1351 bytes .../__pycache__/filesystem.cpython-38.pyc | Bin 0 -> 5655 bytes .../__pycache__/filetypes.cpython-38.pyc | Bin 0 -> 608 bytes .../utils/__pycache__/glibc.cpython-38.pyc | Bin 0 -> 1760 bytes .../utils/__pycache__/hashes.cpython-38.pyc | Bin 0 -> 4210 bytes .../inject_securetransport.cpython-38.pyc | Bin 0 -> 984 bytes .../utils/__pycache__/logging.cpython-38.pyc | Bin 0 -> 9231 bytes .../utils/__pycache__/misc.cpython-38.pyc | Bin 0 -> 24424 bytes .../utils/__pycache__/models.cpython-38.pyc | Bin 0 -> 1976 bytes .../__pycache__/packaging.cpython-38.pyc | Bin 0 -> 2660 bytes .../__pycache__/pkg_resources.cpython-38.pyc | Bin 0 -> 1874 bytes .../setuptools_build.cpython-38.pyc | Bin 0 -> 2973 bytes .../__pycache__/subprocess.cpython-38.pyc | Bin 0 -> 5642 bytes .../utils/__pycache__/temp_dir.cpython-38.pyc | Bin 0 -> 7089 bytes .../utils/__pycache__/typing.cpython-38.pyc | Bin 0 -> 1489 bytes .../__pycache__/unpacking.cpython-38.pyc | Bin 0 -> 6127 bytes .../utils/__pycache__/urls.cpython-38.pyc | Bin 0 -> 1559 bytes .../__pycache__/virtualenv.cpython-38.pyc | Bin 0 -> 3332 bytes .../utils/__pycache__/wheel.cpython-38.pyc | Bin 0 -> 6384 bytes .../pip/_internal/utils/appdirs.py | 44 + .../pip/_internal/utils/compat.py | 270 + .../pip/_internal/utils/compatibility_tags.py | 169 + .../pip/_internal/utils/deprecation.py | 104 + .../pip/_internal/utils/direct_url_helpers.py | 130 + .../pip/_internal/utils/distutils_args.py | 48 + .../pip/_internal/utils/encoding.py | 42 + .../pip/_internal/utils/entrypoints.py | 31 + .../pip/_internal/utils/filesystem.py | 222 + .../pip/_internal/utils/filetypes.py | 16 + .../pip/_internal/utils/glibc.py | 98 + .../pip/_internal/utils/hashes.py | 133 + .../_internal/utils/inject_securetransport.py | 36 + .../pip/_internal/utils/logging.py | 399 + .../site-packages/pip/_internal/utils/misc.py | 913 +++ .../pip/_internal/utils/models.py | 42 + .../pip/_internal/utils/packaging.py | 94 + .../pip/_internal/utils/pkg_resources.py | 44 + .../pip/_internal/utils/setuptools_build.py | 181 + .../pip/_internal/utils/subprocess.py | 277 + .../pip/_internal/utils/temp_dir.py | 271 + .../pip/_internal/utils/typing.py | 38 + .../pip/_internal/utils/unpacking.py | 272 + .../site-packages/pip/_internal/utils/urls.py | 55 + .../pip/_internal/utils/virtualenv.py | 116 + .../pip/_internal/utils/wheel.py | 225 + .../pip/_internal/vcs/__init__.py | 15 + .../vcs/__pycache__/__init__.cpython-38.pyc | Bin 0 -> 500 bytes .../vcs/__pycache__/bazaar.cpython-38.pyc | Bin 0 -> 3799 bytes .../vcs/__pycache__/git.cpython-38.pyc | Bin 0 -> 9686 bytes .../vcs/__pycache__/mercurial.cpython-38.pyc | Bin 0 -> 5123 bytes .../vcs/__pycache__/subversion.cpython-38.pyc | Bin 0 -> 8571 bytes .../__pycache__/versioncontrol.cpython-38.pyc | Bin 0 -> 19698 bytes .../site-packages/pip/_internal/vcs/bazaar.py | 120 + .../site-packages/pip/_internal/vcs/git.py | 394 + .../pip/_internal/vcs/mercurial.py | 161 + .../pip/_internal/vcs/subversion.py | 334 + .../pip/_internal/vcs/versioncontrol.py | 723 ++ .../pip/_internal/wheel_builder.py | 309 + .../site-packages/pip/_vendor/__init__.py | 114 + .../site-packages/pip/_vendor/appdirs.py | 633 ++ .../pip/_vendor/cachecontrol/compat.py | 29 + .../pip/_vendor/cachecontrol/controller.py | 376 + .../pip/_vendor/cachecontrol/serialize.py | 188 + .../pip/_vendor/cachecontrol/wrapper.py | 29 + .../pip/_vendor/chardet/__init__.py | 39 + .../__pycache__/__init__.cpython-38.pyc | Bin 0 -> 865 bytes .../__pycache__/big5freq.cpython-38.pyc | Bin 0 -> 27194 bytes .../__pycache__/big5prober.cpython-38.pyc | Bin 0 -> 1149 bytes .../chardistribution.cpython-38.pyc | Bin 0 -> 6235 bytes .../charsetgroupprober.cpython-38.pyc | Bin 0 -> 2266 bytes .../__pycache__/charsetprober.cpython-38.pyc | Bin 0 -> 3498 bytes .../codingstatemachine.cpython-38.pyc | Bin 0 -> 2925 bytes .../chardet/__pycache__/compat.cpython-38.pyc | Bin 0 -> 370 bytes .../__pycache__/cp949prober.cpython-38.pyc | Bin 0 -> 1156 bytes .../chardet/__pycache__/enums.cpython-38.pyc | Bin 0 -> 2663 bytes .../__pycache__/escprober.cpython-38.pyc | Bin 0 -> 2648 bytes .../chardet/__pycache__/escsm.cpython-38.pyc | Bin 0 -> 7489 bytes .../__pycache__/eucjpprober.cpython-38.pyc | Bin 0 -> 2466 bytes .../__pycache__/euckrfreq.cpython-38.pyc | Bin 0 -> 12078 bytes .../__pycache__/euckrprober.cpython-38.pyc | Bin 0 -> 1157 bytes .../__pycache__/euctwfreq.cpython-38.pyc | Bin 0 -> 27198 bytes .../__pycache__/euctwprober.cpython-38.pyc | Bin 0 -> 1157 bytes .../__pycache__/gb2312freq.cpython-38.pyc | Bin 0 -> 19122 bytes .../__pycache__/gb2312prober.cpython-38.pyc | Bin 0 -> 1165 bytes .../__pycache__/hebrewprober.cpython-38.pyc | Bin 0 -> 3038 bytes .../__pycache__/jisfreq.cpython-38.pyc | Bin 0 -> 22150 bytes .../chardet/__pycache__/jpcntx.cpython-38.pyc | Bin 0 -> 37623 bytes .../langbulgarianmodel.cpython-38.pyc | Bin 0 -> 23647 bytes .../langcyrillicmodel.cpython-38.pyc | Bin 0 -> 29111 bytes .../__pycache__/langgreekmodel.cpython-38.pyc | Bin 0 -> 23605 bytes .../langhebrewmodel.cpython-38.pyc | Bin 0 -> 22232 bytes .../langhungarianmodel.cpython-38.pyc | Bin 0 -> 23636 bytes .../__pycache__/langthaimodel.cpython-38.pyc | Bin 0 -> 22211 bytes .../langturkishmodel.cpython-38.pyc | Bin 0 -> 22234 bytes .../__pycache__/latin1prober.cpython-38.pyc | Bin 0 -> 3418 bytes .../mbcharsetprober.cpython-38.pyc | Bin 0 -> 2281 bytes .../mbcsgroupprober.cpython-38.pyc | Bin 0 -> 1146 bytes .../chardet/__pycache__/mbcssm.cpython-38.pyc | Bin 0 -> 16769 bytes .../sbcharsetprober.cpython-38.pyc | Bin 0 -> 3034 bytes .../sbcsgroupprober.cpython-38.pyc | Bin 0 -> 1644 bytes .../__pycache__/sjisprober.cpython-38.pyc | Bin 0 -> 2502 bytes .../universaldetector.cpython-38.pyc | Bin 0 -> 5846 bytes .../__pycache__/utf8prober.cpython-38.pyc | Bin 0 -> 2007 bytes .../__pycache__/version.cpython-38.pyc | Bin 0 -> 454 bytes .../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 + .../cli/__pycache__/__init__.cpython-38.pyc | Bin 0 -> 211 bytes .../cli/__pycache__/chardetect.cpython-38.pyc | Bin 0 -> 2712 bytes .../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 + .../__pycache__/__init__.cpython-38.pyc | Bin 0 -> 459 bytes .../colorama/__pycache__/ansi.cpython-38.pyc | Bin 0 -> 3244 bytes .../__pycache__/ansitowin32.cpython-38.pyc | Bin 0 -> 7753 bytes .../__pycache__/initialise.cpython-38.pyc | Bin 0 -> 1720 bytes .../colorama/__pycache__/win32.cpython-38.pyc | Bin 0 -> 3996 bytes .../__pycache__/winterm.cpython-38.pyc | Bin 0 -> 4680 bytes .../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 + .../site-packages/pip/_vendor/contextlib2.py | 518 ++ .../pip/_vendor/distlib/__init__.py | 23 + .../__pycache__/__init__.cpython-38.pyc | Bin 0 -> 1074 bytes .../distlib/__pycache__/compat.cpython-38.pyc | Bin 0 -> 32223 bytes .../__pycache__/database.cpython-38.pyc | Bin 0 -> 42136 bytes .../distlib/__pycache__/index.cpython-38.pyc | Bin 0 -> 17427 bytes .../__pycache__/locators.cpython-38.pyc | Bin 0 -> 38429 bytes .../__pycache__/manifest.cpython-38.pyc | Bin 0 -> 10249 bytes .../__pycache__/markers.cpython-38.pyc | Bin 0 -> 4513 bytes .../__pycache__/metadata.cpython-38.pyc | Bin 0 -> 26660 bytes .../__pycache__/resources.cpython-38.pyc | Bin 0 -> 11024 bytes .../__pycache__/scripts.cpython-38.pyc | Bin 0 -> 10889 bytes .../distlib/__pycache__/util.cpython-38.pyc | Bin 0 -> 48177 bytes .../__pycache__/version.cpython-38.pyc | Bin 0 -> 20383 bytes .../distlib/__pycache__/wheel.cpython-38.pyc | Bin 0 -> 25581 bytes .../pip/_vendor/distlib/_backport/__init__.py | 6 + .../__pycache__/__init__.cpython-38.pyc | Bin 0 -> 499 bytes .../_backport/__pycache__/misc.cpython-38.pyc | Bin 0 -> 1110 bytes .../__pycache__/shutil.cpython-38.pyc | Bin 0 -> 21503 bytes .../__pycache__/sysconfig.cpython-38.pyc | Bin 0 -> 15930 bytes .../__pycache__/tarfile.cpython-38.pyc | Bin 0 -> 62759 bytes .../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 | 786 ++ .../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 | 1302 +++ .../pip/_vendor/distlib/manifest.py | 393 + .../pip/_vendor/distlib/markers.py | 131 + .../pip/_vendor/distlib/metadata.py | 1096 +++ .../pip/_vendor/distlib/resources.py | 355 + .../pip/_vendor/distlib/scripts.py | 416 + .../site-packages/pip/_vendor/distlib/t32.exe | Bin 0 -> 96768 bytes .../site-packages/pip/_vendor/distlib/t64.exe | Bin 0 -> 105984 bytes .../site-packages/pip/_vendor/distlib/util.py | 1761 ++++ .../pip/_vendor/distlib/version.py | 736 ++ .../site-packages/pip/_vendor/distlib/w32.exe | Bin 0 -> 90112 bytes .../site-packages/pip/_vendor/distlib/w64.exe | Bin 0 -> 99840 bytes .../pip/_vendor/distlib/wheel.py | 1004 +++ .../site-packages/pip/_vendor/distro.py | 1230 +++ .../site-packages/pip/_vendor/ipaddress.py | 2420 ++++++ .../pip/_vendor/msgpack/__init__.py | 54 + .../__pycache__/__init__.cpython-38.pyc | Bin 0 -> 1420 bytes .../__pycache__/_version.cpython-38.pyc | Bin 0 -> 234 bytes .../__pycache__/exceptions.cpython-38.pyc | Bin 0 -> 1868 bytes .../msgpack/__pycache__/ext.cpython-38.pyc | Bin 0 -> 6267 bytes .../__pycache__/fallback.cpython-38.pyc | Bin 0 -> 25971 bytes .../pip/_vendor/msgpack/_version.py | 1 + .../pip/_vendor/msgpack/exceptions.py | 48 + .../site-packages/pip/_vendor/msgpack/ext.py | 191 + .../pip/_vendor/msgpack/fallback.py | 1063 +++ .../pip/_vendor/packaging/__about__.py | 27 + .../pip/_vendor/packaging/__init__.py | 26 + .../__pycache__/__about__.cpython-38.pyc | Bin 0 -> 749 bytes .../__pycache__/__init__.cpython-38.pyc | Bin 0 -> 587 bytes .../__pycache__/_compat.cpython-38.pyc | Bin 0 -> 1164 bytes .../__pycache__/_structures.cpython-38.pyc | Bin 0 -> 2913 bytes .../__pycache__/_typing.cpython-38.pyc | Bin 0 -> 1504 bytes .../__pycache__/markers.cpython-38.pyc | Bin 0 -> 9344 bytes .../__pycache__/requirements.cpython-38.pyc | Bin 0 -> 4119 bytes .../__pycache__/specifiers.cpython-38.pyc | Bin 0 -> 20342 bytes .../packaging/__pycache__/tags.cpython-38.pyc | Bin 0 -> 16916 bytes .../__pycache__/utils.cpython-38.pyc | Bin 0 -> 1581 bytes .../__pycache__/version.cpython-38.pyc | Bin 0 -> 13356 bytes .../pip/_vendor/packaging/_compat.py | 38 + .../pip/_vendor/packaging/_structures.py | 86 + .../pip/_vendor/packaging/_typing.py | 39 + .../pip/_vendor/packaging/markers.py | 328 + .../pip/_vendor/packaging/requirements.py | 145 + .../pip/_vendor/packaging/specifiers.py | 849 ++ .../pip/_vendor/packaging/tags.py | 739 ++ .../pip/_vendor/packaging/utils.py | 62 + .../pip/_vendor/packaging/version.py | 535 ++ .../pip/_vendor/pep517/__init__.py | 4 + .../__pycache__/__init__.cpython-38.pyc | Bin 0 -> 298 bytes .../__pycache__/_in_process.cpython-38.pyc | Bin 0 -> 8161 bytes .../pep517/__pycache__/build.cpython-38.pyc | Bin 0 -> 3412 bytes .../pep517/__pycache__/check.cpython-38.pyc | Bin 0 -> 4832 bytes .../__pycache__/colorlog.cpython-38.pyc | Bin 0 -> 2968 bytes .../pep517/__pycache__/compat.cpython-38.pyc | Bin 0 -> 1065 bytes .../__pycache__/dirtools.cpython-38.pyc | Bin 0 -> 1347 bytes .../__pycache__/envbuild.cpython-38.pyc | Bin 0 -> 4462 bytes .../pep517/__pycache__/meta.cpython-38.pyc | Bin 0 -> 2874 bytes .../__pycache__/wrappers.cpython-38.pyc | Bin 0 -> 10564 bytes .../pip/_vendor/pep517/_in_process.py | 280 + .../site-packages/pip/_vendor/pep517/build.py | 124 + .../site-packages/pip/_vendor/pep517/check.py | 203 + .../pip/_vendor/pep517/colorlog.py | 115 + .../pip/_vendor/pep517/compat.py | 34 + .../pip/_vendor/pep517/dirtools.py | 44 + .../pip/_vendor/pep517/envbuild.py | 167 + .../site-packages/pip/_vendor/pep517/meta.py | 92 + .../pip/_vendor/pep517/wrappers.py | 308 + .../pip/_vendor/progress/__init__.py | 177 + .../__pycache__/__init__.cpython-38.pyc | Bin 0 -> 5633 bytes .../progress/__pycache__/bar.cpython-38.pyc | Bin 0 -> 2657 bytes .../__pycache__/counter.cpython-38.pyc | Bin 0 -> 1489 bytes .../__pycache__/spinner.cpython-38.pyc | Bin 0 -> 1416 bytes .../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 | 7107 +++++++++++++++++ .../site-packages/pip/_vendor/retrying.py | 267 + .../site-packages/pip/_vendor/six.py | 980 +++ .../site-packages/pip/_vendor/toml.py | 1039 +++ .../pip/_vendor/toml/__init__.py | 21 + .../toml/__pycache__/__init__.cpython-38.pyc | Bin 0 -> 622 bytes .../toml/__pycache__/decoder.cpython-38.pyc | Bin 0 -> 19908 bytes .../toml/__pycache__/encoder.cpython-38.pyc | Bin 0 -> 6950 bytes .../toml/__pycache__/ordered.cpython-38.pyc | Bin 0 -> 987 bytes .../toml/__pycache__/tz.cpython-38.pyc | Bin 0 -> 1137 bytes .../site-packages/pip/_vendor/toml/decoder.py | 945 +++ .../site-packages/pip/_vendor/toml/encoder.py | 250 + .../site-packages/pip/_vendor/toml/ordered.py | 15 + .../site-packages/pip/_vendor/toml/tz.py | 21 + .../site-packages/pip/_vendor/vendor.txt | 24 + .../pip/_vendor/webencodings/__init__.py | 342 + .../__pycache__/__init__.cpython-38.pyc | Bin 0 -> 9755 bytes .../__pycache__/labels.cpython-38.pyc | Bin 0 -> 3853 bytes .../__pycache__/mklabels.cpython-38.pyc | Bin 0 -> 1949 bytes .../__pycache__/tests.cpython-38.pyc | Bin 0 -> 5115 bytes .../__pycache__/x_user_defined.cpython-38.pyc | Bin 0 -> 2683 bytes .../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 | 3307 ++++++++ .../__pycache__/__init__.cpython-38.pyc | Bin 0 -> 100662 bytes .../__pycache__/py2_warn.cpython-38.pyc | Bin 0 -> 653 bytes .../__pycache__/py31compat.cpython-38.pyc | Bin 0 -> 652 bytes .../pkg_resources/_vendor/__init__.py | 0 .../__pycache__/__init__.cpython-38.pyc | Bin 0 -> 209 bytes .../__pycache__/appdirs.cpython-38.pyc | Bin 0 -> 20562 bytes .../__pycache__/pyparsing.cpython-38.pyc | Bin 0 -> 201686 bytes .../_vendor/__pycache__/six.cpython-38.pyc | Bin 0 -> 24482 bytes .../pkg_resources/_vendor/appdirs.py | 608 ++ .../_vendor/packaging/__about__.py | 21 + .../_vendor/packaging/__init__.py | 14 + .../__pycache__/__about__.cpython-38.pyc | Bin 0 -> 759 bytes .../__pycache__/__init__.cpython-38.pyc | Bin 0 -> 597 bytes .../__pycache__/_compat.cpython-38.pyc | Bin 0 -> 1033 bytes .../__pycache__/_structures.cpython-38.pyc | Bin 0 -> 2815 bytes .../__pycache__/markers.cpython-38.pyc | Bin 0 -> 8971 bytes .../__pycache__/requirements.cpython-38.pyc | Bin 0 -> 3930 bytes .../__pycache__/specifiers.cpython-38.pyc | Bin 0 -> 19839 bytes .../__pycache__/utils.cpython-38.pyc | Bin 0 -> 518 bytes .../__pycache__/version.cpython-38.pyc | Bin 0 -> 10686 bytes .../_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 | 66 + .../__pycache__/__init__.cpython-38.pyc | Bin 0 -> 2406 bytes .../site-packages/pkg_resources/py2_warn.py | 16 + .../site-packages/pkg_resources/py31compat.py | 23 + .../setuptools-47.1.0.dist-info/INSTALLER | 1 + .../setuptools-47.1.0.dist-info/LICENSE | 19 + .../setuptools-47.1.0.dist-info/METADATA | 109 + .../setuptools-47.1.0.dist-info/RECORD | 196 + .../setuptools-47.1.0.dist-info/WHEEL | 5 + .../dependency_links.txt | 2 + .../entry_points.txt | 68 + .../setuptools-47.1.0.dist-info/top_level.txt | 3 + .../setuptools-47.1.0.dist-info/zip-safe | 1 + .../site-packages/setuptools/__init__.py | 232 + .../__pycache__/__init__.cpython-38.pyc | Bin 0 -> 7984 bytes .../_deprecation_warning.cpython-38.pyc | Bin 0 -> 568 bytes .../__pycache__/_imp.cpython-38.pyc | Bin 0 -> 2107 bytes .../__pycache__/archive_util.cpython-38.pyc | Bin 0 -> 5188 bytes .../__pycache__/build_meta.cpython-38.pyc | Bin 0 -> 8626 bytes .../__pycache__/config.cpython-38.pyc | Bin 0 -> 19362 bytes .../__pycache__/dep_util.cpython-38.pyc | Bin 0 -> 875 bytes .../__pycache__/depends.cpython-38.pyc | Bin 0 -> 5268 bytes .../__pycache__/dist.cpython-38.pyc | Bin 0 -> 32943 bytes .../__pycache__/errors.cpython-38.pyc | Bin 0 -> 868 bytes .../__pycache__/extension.cpython-38.pyc | Bin 0 -> 2013 bytes .../__pycache__/glob.cpython-38.pyc | Bin 0 -> 3785 bytes .../__pycache__/installer.cpython-38.pyc | Bin 0 -> 4154 bytes .../__pycache__/launch.cpython-38.pyc | Bin 0 -> 876 bytes .../__pycache__/lib2to3_ex.cpython-38.pyc | Bin 0 -> 2764 bytes .../__pycache__/monkey.cpython-38.pyc | Bin 0 -> 4696 bytes .../__pycache__/msvc.cpython-38.pyc | Bin 0 -> 43132 bytes .../__pycache__/namespaces.cpython-38.pyc | Bin 0 -> 3668 bytes .../__pycache__/package_index.cpython-38.pyc | Bin 0 -> 33117 bytes .../__pycache__/py27compat.cpython-38.pyc | Bin 0 -> 1801 bytes .../__pycache__/py31compat.cpython-38.pyc | Bin 0 -> 1243 bytes .../__pycache__/py33compat.cpython-38.pyc | Bin 0 -> 1460 bytes .../__pycache__/py34compat.cpython-38.pyc | Bin 0 -> 502 bytes .../__pycache__/sandbox.cpython-38.pyc | Bin 0 -> 15588 bytes .../__pycache__/site-patch.cpython-38.pyc | Bin 0 -> 1534 bytes .../__pycache__/ssl_support.cpython-38.pyc | Bin 0 -> 6925 bytes .../__pycache__/unicode_utils.cpython-38.pyc | Bin 0 -> 1203 bytes .../__pycache__/version.cpython-38.pyc | Bin 0 -> 344 bytes .../__pycache__/wheel.cpython-38.pyc | Bin 0 -> 7343 bytes .../windows_support.cpython-38.pyc | Bin 0 -> 1041 bytes .../setuptools/_deprecation_warning.py | 7 + .../site-packages/setuptools/_imp.py | 82 + .../setuptools/_vendor/__init__.py | 0 .../__pycache__/__init__.cpython-38.pyc | Bin 0 -> 206 bytes .../__pycache__/ordered_set.cpython-38.pyc | Bin 0 -> 16464 bytes .../__pycache__/pyparsing.cpython-38.pyc | Bin 0 -> 201683 bytes .../_vendor/__pycache__/six.cpython-38.pyc | Bin 0 -> 24479 bytes .../setuptools/_vendor/ordered_set.py | 488 ++ .../setuptools/_vendor/packaging/__about__.py | 27 + .../setuptools/_vendor/packaging/__init__.py | 26 + .../__pycache__/__about__.cpython-38.pyc | Bin 0 -> 756 bytes .../__pycache__/__init__.cpython-38.pyc | Bin 0 -> 594 bytes .../__pycache__/_compat.cpython-38.pyc | Bin 0 -> 1030 bytes .../__pycache__/_structures.cpython-38.pyc | Bin 0 -> 2812 bytes .../__pycache__/markers.cpython-38.pyc | Bin 0 -> 8979 bytes .../__pycache__/requirements.cpython-38.pyc | Bin 0 -> 4047 bytes .../__pycache__/specifiers.cpython-38.pyc | Bin 0 -> 19786 bytes .../packaging/__pycache__/tags.cpython-38.pyc | Bin 0 -> 10863 bytes .../__pycache__/utils.cpython-38.pyc | Bin 0 -> 1487 bytes .../__pycache__/version.cpython-38.pyc | Bin 0 -> 12117 bytes .../setuptools/_vendor/packaging/_compat.py | 31 + .../_vendor/packaging/_structures.py | 68 + .../setuptools/_vendor/packaging/markers.py | 296 + .../_vendor/packaging/requirements.py | 138 + .../_vendor/packaging/specifiers.py | 749 ++ .../setuptools/_vendor/packaging/tags.py | 404 + .../setuptools/_vendor/packaging/utils.py | 57 + .../setuptools/_vendor/packaging/version.py | 420 + .../setuptools/_vendor/pyparsing.py | 5742 +++++++++++++ .../site-packages/setuptools/_vendor/six.py | 868 ++ .../site-packages/setuptools/archive_util.py | 175 + .../site-packages/setuptools/build_meta.py | 272 + .../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 | 17 + .../__pycache__/__init__.cpython-38.pyc | Bin 0 -> 764 bytes .../command/__pycache__/alias.cpython-38.pyc | Bin 0 -> 2444 bytes .../__pycache__/bdist_egg.cpython-38.pyc | Bin 0 -> 14399 bytes .../__pycache__/bdist_rpm.cpython-38.pyc | Bin 0 -> 1836 bytes .../__pycache__/bdist_wininst.cpython-38.pyc | Bin 0 -> 1237 bytes .../__pycache__/build_clib.cpython-38.pyc | Bin 0 -> 2486 bytes .../__pycache__/build_ext.cpython-38.pyc | Bin 0 -> 9950 bytes .../__pycache__/build_py.cpython-38.pyc | Bin 0 -> 8895 bytes .../__pycache__/develop.cpython-38.pyc | Bin 0 -> 6553 bytes .../__pycache__/dist_info.cpython-38.pyc | Bin 0 -> 1413 bytes .../__pycache__/easy_install.cpython-38.pyc | Bin 0 -> 65417 bytes .../__pycache__/egg_info.cpython-38.pyc | Bin 0 -> 21770 bytes .../__pycache__/install.cpython-38.pyc | Bin 0 -> 4068 bytes .../install_egg_info.cpython-38.pyc | Bin 0 -> 2455 bytes .../__pycache__/install_lib.cpython-38.pyc | Bin 0 -> 4182 bytes .../install_scripts.cpython-38.pyc | Bin 0 -> 2376 bytes .../__pycache__/py36compat.cpython-38.pyc | Bin 0 -> 4662 bytes .../__pycache__/register.cpython-38.pyc | Bin 0 -> 863 bytes .../command/__pycache__/rotate.cpython-38.pyc | Bin 0 -> 2572 bytes .../__pycache__/saveopts.cpython-38.pyc | Bin 0 -> 941 bytes .../command/__pycache__/sdist.cpython-38.pyc | Bin 0 -> 7911 bytes .../command/__pycache__/setopt.cpython-38.pyc | Bin 0 -> 4589 bytes .../command/__pycache__/test.cpython-38.pyc | Bin 0 -> 8537 bytes .../command/__pycache__/upload.cpython-38.pyc | Bin 0 -> 836 bytes .../__pycache__/upload_docs.cpython-38.pyc | Bin 0 -> 6189 bytes .../site-packages/setuptools/command/alias.py | 80 + .../setuptools/command/bdist_egg.py | 509 ++ .../setuptools/command/bdist_rpm.py | 43 + .../setuptools/command/bdist_wininst.py | 30 + .../setuptools/command/build_clib.py | 101 + .../setuptools/command/build_ext.py | 330 + .../setuptools/command/build_py.py | 276 + .../setuptools/command/develop.py | 221 + .../setuptools/command/dist_info.py | 36 + .../setuptools/command/easy_install.py | 2354 ++++++ .../setuptools/command/egg_info.py | 721 ++ .../setuptools/command/install.py | 125 + .../setuptools/command/install_egg_info.py | 62 + .../setuptools/command/install_lib.py | 122 + .../setuptools/command/install_scripts.py | 68 + .../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 | 252 + .../setuptools/command/setopt.py | 149 + .../site-packages/setuptools/command/test.py | 280 + .../setuptools/command/upload.py | 17 + .../setuptools/command/upload_docs.py | 206 + .../site-packages/setuptools/config.py | 700 ++ .../site-packages/setuptools/dep_util.py | 25 + .../site-packages/setuptools/depends.py | 176 + .../site-packages/setuptools/dist.py | 1031 +++ .../site-packages/setuptools/errors.py | 16 + .../site-packages/setuptools/extension.py | 57 + .../setuptools/extern/__init__.py | 66 + .../__pycache__/__init__.cpython-38.pyc | Bin 0 -> 2429 bytes .../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/installer.py | 150 + .../site-packages/setuptools/launch.py | 35 + .../site-packages/setuptools/lib2to3_ex.py | 71 + .../site-packages/setuptools/monkey.py | 179 + .../site-packages/setuptools/msvc.py | 1825 +++++ .../site-packages/setuptools/namespaces.py | 111 + .../site-packages/setuptools/package_index.py | 1140 +++ .../site-packages/setuptools/py27compat.py | 60 + .../site-packages/setuptools/py31compat.py | 32 + .../site-packages/setuptools/py33compat.py | 59 + .../site-packages/setuptools/py34compat.py | 13 + .../site-packages/setuptools/sandbox.py | 492 ++ .../setuptools/script (dev).tmpl | 6 + .../site-packages/setuptools/script.tmpl | 3 + .../site-packages/setuptools/site-patch.py | 76 + .../site-packages/setuptools/ssl_support.py | 265 + .../site-packages/setuptools/unicode_utils.py | 44 + .../site-packages/setuptools/version.py | 6 + .../site-packages/setuptools/wheel.py | 217 + .../setuptools/windows_support.py | 29 + venv/pyvenv.cfg | 3 + 691 files changed, 105984 insertions(+), 1 deletion(-) create mode 100644 .DS_Store create mode 100644 data/.DS_Store create mode 100644 server/.DS_Store create mode 160000 transformers create mode 100755 venv/bin/easy_install create mode 100755 venv/bin/easy_install-3.8 create mode 120000 venv/bin/python create mode 120000 venv/bin/python3 create mode 100644 venv/lib/python3.8/site-packages/__pycache__/easy_install.cpython-38.pyc create mode 100644 venv/lib/python3.8/site-packages/easy_install.py create mode 100644 venv/lib/python3.8/site-packages/pip/__init__.py create mode 100644 venv/lib/python3.8/site-packages/pip/__main__.py create mode 100644 venv/lib/python3.8/site-packages/pip/_internal/__init__.py create mode 100644 venv/lib/python3.8/site-packages/pip/_internal/__pycache__/__init__.cpython-38.pyc create mode 100644 venv/lib/python3.8/site-packages/pip/_internal/__pycache__/build_env.cpython-38.pyc create mode 100644 venv/lib/python3.8/site-packages/pip/_internal/__pycache__/cache.cpython-38.pyc create mode 100644 venv/lib/python3.8/site-packages/pip/_internal/__pycache__/configuration.cpython-38.pyc create mode 100644 venv/lib/python3.8/site-packages/pip/_internal/__pycache__/exceptions.cpython-38.pyc create mode 100644 venv/lib/python3.8/site-packages/pip/_internal/__pycache__/locations.cpython-38.pyc create mode 100644 venv/lib/python3.8/site-packages/pip/_internal/__pycache__/main.cpython-38.pyc create mode 100644 venv/lib/python3.8/site-packages/pip/_internal/__pycache__/pyproject.cpython-38.pyc create mode 100644 venv/lib/python3.8/site-packages/pip/_internal/__pycache__/self_outdated_check.cpython-38.pyc create mode 100644 venv/lib/python3.8/site-packages/pip/_internal/__pycache__/wheel_builder.cpython-38.pyc create mode 100644 venv/lib/python3.8/site-packages/pip/_internal/build_env.py create mode 100644 venv/lib/python3.8/site-packages/pip/_internal/cache.py create mode 100644 venv/lib/python3.8/site-packages/pip/_internal/cli/__init__.py create mode 100644 venv/lib/python3.8/site-packages/pip/_internal/cli/__pycache__/__init__.cpython-38.pyc create mode 100644 venv/lib/python3.8/site-packages/pip/_internal/cli/__pycache__/autocompletion.cpython-38.pyc create mode 100644 venv/lib/python3.8/site-packages/pip/_internal/cli/__pycache__/base_command.cpython-38.pyc create mode 100644 venv/lib/python3.8/site-packages/pip/_internal/cli/__pycache__/cmdoptions.cpython-38.pyc create mode 100644 venv/lib/python3.8/site-packages/pip/_internal/cli/__pycache__/command_context.cpython-38.pyc create mode 100644 venv/lib/python3.8/site-packages/pip/_internal/cli/__pycache__/main.cpython-38.pyc create mode 100644 venv/lib/python3.8/site-packages/pip/_internal/cli/__pycache__/main_parser.cpython-38.pyc create mode 100644 venv/lib/python3.8/site-packages/pip/_internal/cli/__pycache__/parser.cpython-38.pyc create mode 100644 venv/lib/python3.8/site-packages/pip/_internal/cli/__pycache__/progress_bars.cpython-38.pyc create mode 100644 venv/lib/python3.8/site-packages/pip/_internal/cli/__pycache__/req_command.cpython-38.pyc create mode 100644 venv/lib/python3.8/site-packages/pip/_internal/cli/__pycache__/spinners.cpython-38.pyc create mode 100644 venv/lib/python3.8/site-packages/pip/_internal/cli/__pycache__/status_codes.cpython-38.pyc create mode 100644 venv/lib/python3.8/site-packages/pip/_internal/cli/autocompletion.py create mode 100644 venv/lib/python3.8/site-packages/pip/_internal/cli/base_command.py create mode 100644 venv/lib/python3.8/site-packages/pip/_internal/cli/cmdoptions.py create mode 100644 venv/lib/python3.8/site-packages/pip/_internal/cli/command_context.py create mode 100644 venv/lib/python3.8/site-packages/pip/_internal/cli/main.py create mode 100644 venv/lib/python3.8/site-packages/pip/_internal/cli/main_parser.py create mode 100644 venv/lib/python3.8/site-packages/pip/_internal/cli/parser.py create mode 100644 venv/lib/python3.8/site-packages/pip/_internal/cli/progress_bars.py create mode 100644 venv/lib/python3.8/site-packages/pip/_internal/cli/req_command.py create mode 100644 venv/lib/python3.8/site-packages/pip/_internal/cli/spinners.py create mode 100644 venv/lib/python3.8/site-packages/pip/_internal/cli/status_codes.py create mode 100644 venv/lib/python3.8/site-packages/pip/_internal/commands/__init__.py create mode 100644 venv/lib/python3.8/site-packages/pip/_internal/commands/__pycache__/__init__.cpython-38.pyc create mode 100644 venv/lib/python3.8/site-packages/pip/_internal/commands/__pycache__/cache.cpython-38.pyc create mode 100644 venv/lib/python3.8/site-packages/pip/_internal/commands/__pycache__/check.cpython-38.pyc create mode 100644 venv/lib/python3.8/site-packages/pip/_internal/commands/__pycache__/completion.cpython-38.pyc create mode 100644 venv/lib/python3.8/site-packages/pip/_internal/commands/__pycache__/configuration.cpython-38.pyc create mode 100644 venv/lib/python3.8/site-packages/pip/_internal/commands/__pycache__/debug.cpython-38.pyc create mode 100644 venv/lib/python3.8/site-packages/pip/_internal/commands/__pycache__/download.cpython-38.pyc create mode 100644 venv/lib/python3.8/site-packages/pip/_internal/commands/__pycache__/freeze.cpython-38.pyc create mode 100644 venv/lib/python3.8/site-packages/pip/_internal/commands/__pycache__/hash.cpython-38.pyc create mode 100644 venv/lib/python3.8/site-packages/pip/_internal/commands/__pycache__/help.cpython-38.pyc create mode 100644 venv/lib/python3.8/site-packages/pip/_internal/commands/__pycache__/install.cpython-38.pyc create mode 100644 venv/lib/python3.8/site-packages/pip/_internal/commands/__pycache__/list.cpython-38.pyc create mode 100644 venv/lib/python3.8/site-packages/pip/_internal/commands/__pycache__/search.cpython-38.pyc create mode 100644 venv/lib/python3.8/site-packages/pip/_internal/commands/__pycache__/show.cpython-38.pyc create mode 100644 venv/lib/python3.8/site-packages/pip/_internal/commands/__pycache__/uninstall.cpython-38.pyc create mode 100644 venv/lib/python3.8/site-packages/pip/_internal/commands/__pycache__/wheel.cpython-38.pyc create mode 100644 venv/lib/python3.8/site-packages/pip/_internal/commands/cache.py create mode 100644 venv/lib/python3.8/site-packages/pip/_internal/commands/check.py create mode 100644 venv/lib/python3.8/site-packages/pip/_internal/commands/completion.py create mode 100644 venv/lib/python3.8/site-packages/pip/_internal/commands/configuration.py create mode 100644 venv/lib/python3.8/site-packages/pip/_internal/commands/debug.py create mode 100644 venv/lib/python3.8/site-packages/pip/_internal/commands/download.py create mode 100644 venv/lib/python3.8/site-packages/pip/_internal/commands/freeze.py create mode 100644 venv/lib/python3.8/site-packages/pip/_internal/commands/hash.py create mode 100644 venv/lib/python3.8/site-packages/pip/_internal/commands/help.py create mode 100644 venv/lib/python3.8/site-packages/pip/_internal/commands/install.py create mode 100644 venv/lib/python3.8/site-packages/pip/_internal/commands/list.py create mode 100644 venv/lib/python3.8/site-packages/pip/_internal/commands/search.py create mode 100644 venv/lib/python3.8/site-packages/pip/_internal/commands/show.py create mode 100644 venv/lib/python3.8/site-packages/pip/_internal/commands/uninstall.py create mode 100644 venv/lib/python3.8/site-packages/pip/_internal/commands/wheel.py create mode 100644 venv/lib/python3.8/site-packages/pip/_internal/configuration.py create mode 100644 venv/lib/python3.8/site-packages/pip/_internal/distributions/__init__.py create mode 100644 venv/lib/python3.8/site-packages/pip/_internal/distributions/__pycache__/__init__.cpython-38.pyc create mode 100644 venv/lib/python3.8/site-packages/pip/_internal/distributions/__pycache__/base.cpython-38.pyc create mode 100644 venv/lib/python3.8/site-packages/pip/_internal/distributions/__pycache__/installed.cpython-38.pyc create mode 100644 venv/lib/python3.8/site-packages/pip/_internal/distributions/__pycache__/sdist.cpython-38.pyc create mode 100644 venv/lib/python3.8/site-packages/pip/_internal/distributions/__pycache__/wheel.cpython-38.pyc create mode 100644 venv/lib/python3.8/site-packages/pip/_internal/distributions/base.py create mode 100644 venv/lib/python3.8/site-packages/pip/_internal/distributions/installed.py create mode 100644 venv/lib/python3.8/site-packages/pip/_internal/distributions/sdist.py create mode 100644 venv/lib/python3.8/site-packages/pip/_internal/distributions/wheel.py create mode 100644 venv/lib/python3.8/site-packages/pip/_internal/exceptions.py create mode 100644 venv/lib/python3.8/site-packages/pip/_internal/index/__init__.py create mode 100644 venv/lib/python3.8/site-packages/pip/_internal/index/__pycache__/__init__.cpython-38.pyc create mode 100644 venv/lib/python3.8/site-packages/pip/_internal/index/__pycache__/collector.cpython-38.pyc create mode 100644 venv/lib/python3.8/site-packages/pip/_internal/index/__pycache__/package_finder.cpython-38.pyc create mode 100644 venv/lib/python3.8/site-packages/pip/_internal/index/collector.py create mode 100644 venv/lib/python3.8/site-packages/pip/_internal/index/package_finder.py create mode 100644 venv/lib/python3.8/site-packages/pip/_internal/locations.py create mode 100644 venv/lib/python3.8/site-packages/pip/_internal/main.py create mode 100644 venv/lib/python3.8/site-packages/pip/_internal/models/__init__.py create mode 100644 venv/lib/python3.8/site-packages/pip/_internal/models/__pycache__/__init__.cpython-38.pyc create mode 100644 venv/lib/python3.8/site-packages/pip/_internal/models/__pycache__/candidate.cpython-38.pyc create mode 100644 venv/lib/python3.8/site-packages/pip/_internal/models/__pycache__/direct_url.cpython-38.pyc create mode 100644 venv/lib/python3.8/site-packages/pip/_internal/models/__pycache__/format_control.cpython-38.pyc create mode 100644 venv/lib/python3.8/site-packages/pip/_internal/models/__pycache__/index.cpython-38.pyc create mode 100644 venv/lib/python3.8/site-packages/pip/_internal/models/__pycache__/link.cpython-38.pyc create mode 100644 venv/lib/python3.8/site-packages/pip/_internal/models/__pycache__/scheme.cpython-38.pyc create mode 100644 venv/lib/python3.8/site-packages/pip/_internal/models/__pycache__/search_scope.cpython-38.pyc create mode 100644 venv/lib/python3.8/site-packages/pip/_internal/models/__pycache__/selection_prefs.cpython-38.pyc create mode 100644 venv/lib/python3.8/site-packages/pip/_internal/models/__pycache__/target_python.cpython-38.pyc create mode 100644 venv/lib/python3.8/site-packages/pip/_internal/models/__pycache__/wheel.cpython-38.pyc create mode 100644 venv/lib/python3.8/site-packages/pip/_internal/models/candidate.py create mode 100644 venv/lib/python3.8/site-packages/pip/_internal/models/direct_url.py create mode 100644 venv/lib/python3.8/site-packages/pip/_internal/models/format_control.py create mode 100644 venv/lib/python3.8/site-packages/pip/_internal/models/index.py create mode 100644 venv/lib/python3.8/site-packages/pip/_internal/models/link.py create mode 100644 venv/lib/python3.8/site-packages/pip/_internal/models/scheme.py create mode 100644 venv/lib/python3.8/site-packages/pip/_internal/models/search_scope.py create mode 100644 venv/lib/python3.8/site-packages/pip/_internal/models/selection_prefs.py create mode 100644 venv/lib/python3.8/site-packages/pip/_internal/models/target_python.py create mode 100644 venv/lib/python3.8/site-packages/pip/_internal/models/wheel.py create mode 100644 venv/lib/python3.8/site-packages/pip/_internal/network/__init__.py create mode 100644 venv/lib/python3.8/site-packages/pip/_internal/network/__pycache__/__init__.cpython-38.pyc create mode 100644 venv/lib/python3.8/site-packages/pip/_internal/network/__pycache__/auth.cpython-38.pyc create mode 100644 venv/lib/python3.8/site-packages/pip/_internal/network/__pycache__/cache.cpython-38.pyc create mode 100644 venv/lib/python3.8/site-packages/pip/_internal/network/__pycache__/download.cpython-38.pyc create mode 100644 venv/lib/python3.8/site-packages/pip/_internal/network/__pycache__/session.cpython-38.pyc create mode 100644 venv/lib/python3.8/site-packages/pip/_internal/network/__pycache__/utils.cpython-38.pyc create mode 100644 venv/lib/python3.8/site-packages/pip/_internal/network/__pycache__/xmlrpc.cpython-38.pyc create mode 100644 venv/lib/python3.8/site-packages/pip/_internal/network/auth.py create mode 100644 venv/lib/python3.8/site-packages/pip/_internal/network/cache.py create mode 100644 venv/lib/python3.8/site-packages/pip/_internal/network/download.py create mode 100644 venv/lib/python3.8/site-packages/pip/_internal/network/session.py create mode 100644 venv/lib/python3.8/site-packages/pip/_internal/network/utils.py create mode 100644 venv/lib/python3.8/site-packages/pip/_internal/network/xmlrpc.py create mode 100644 venv/lib/python3.8/site-packages/pip/_internal/operations/__init__.py create mode 100644 venv/lib/python3.8/site-packages/pip/_internal/operations/__pycache__/__init__.cpython-38.pyc create mode 100644 venv/lib/python3.8/site-packages/pip/_internal/operations/__pycache__/check.cpython-38.pyc create mode 100644 venv/lib/python3.8/site-packages/pip/_internal/operations/__pycache__/freeze.cpython-38.pyc create mode 100644 venv/lib/python3.8/site-packages/pip/_internal/operations/__pycache__/prepare.cpython-38.pyc create mode 100644 venv/lib/python3.8/site-packages/pip/_internal/operations/build/__init__.py create mode 100644 venv/lib/python3.8/site-packages/pip/_internal/operations/build/__pycache__/__init__.cpython-38.pyc create mode 100644 venv/lib/python3.8/site-packages/pip/_internal/operations/build/__pycache__/metadata.cpython-38.pyc create mode 100644 venv/lib/python3.8/site-packages/pip/_internal/operations/build/__pycache__/metadata_legacy.cpython-38.pyc create mode 100644 venv/lib/python3.8/site-packages/pip/_internal/operations/build/__pycache__/wheel.cpython-38.pyc create mode 100644 venv/lib/python3.8/site-packages/pip/_internal/operations/build/__pycache__/wheel_legacy.cpython-38.pyc create mode 100644 venv/lib/python3.8/site-packages/pip/_internal/operations/build/metadata.py create mode 100644 venv/lib/python3.8/site-packages/pip/_internal/operations/build/metadata_legacy.py create mode 100644 venv/lib/python3.8/site-packages/pip/_internal/operations/build/wheel.py create mode 100644 venv/lib/python3.8/site-packages/pip/_internal/operations/build/wheel_legacy.py create mode 100644 venv/lib/python3.8/site-packages/pip/_internal/operations/check.py create mode 100644 venv/lib/python3.8/site-packages/pip/_internal/operations/freeze.py create mode 100644 venv/lib/python3.8/site-packages/pip/_internal/operations/install/__init__.py create mode 100644 venv/lib/python3.8/site-packages/pip/_internal/operations/install/__pycache__/__init__.cpython-38.pyc create mode 100644 venv/lib/python3.8/site-packages/pip/_internal/operations/install/__pycache__/editable_legacy.cpython-38.pyc create mode 100644 venv/lib/python3.8/site-packages/pip/_internal/operations/install/__pycache__/legacy.cpython-38.pyc create mode 100644 venv/lib/python3.8/site-packages/pip/_internal/operations/install/__pycache__/wheel.cpython-38.pyc create mode 100644 venv/lib/python3.8/site-packages/pip/_internal/operations/install/editable_legacy.py create mode 100644 venv/lib/python3.8/site-packages/pip/_internal/operations/install/legacy.py create mode 100644 venv/lib/python3.8/site-packages/pip/_internal/operations/install/wheel.py create mode 100644 venv/lib/python3.8/site-packages/pip/_internal/operations/prepare.py create mode 100644 venv/lib/python3.8/site-packages/pip/_internal/pyproject.py create mode 100644 venv/lib/python3.8/site-packages/pip/_internal/req/__init__.py create mode 100644 venv/lib/python3.8/site-packages/pip/_internal/req/__pycache__/__init__.cpython-38.pyc create mode 100644 venv/lib/python3.8/site-packages/pip/_internal/req/__pycache__/constructors.cpython-38.pyc create mode 100644 venv/lib/python3.8/site-packages/pip/_internal/req/__pycache__/req_file.cpython-38.pyc create mode 100644 venv/lib/python3.8/site-packages/pip/_internal/req/__pycache__/req_install.cpython-38.pyc create mode 100644 venv/lib/python3.8/site-packages/pip/_internal/req/__pycache__/req_set.cpython-38.pyc create mode 100644 venv/lib/python3.8/site-packages/pip/_internal/req/__pycache__/req_tracker.cpython-38.pyc create mode 100644 venv/lib/python3.8/site-packages/pip/_internal/req/__pycache__/req_uninstall.cpython-38.pyc create mode 100644 venv/lib/python3.8/site-packages/pip/_internal/req/constructors.py create mode 100644 venv/lib/python3.8/site-packages/pip/_internal/req/req_file.py create mode 100644 venv/lib/python3.8/site-packages/pip/_internal/req/req_install.py create mode 100644 venv/lib/python3.8/site-packages/pip/_internal/req/req_set.py create mode 100644 venv/lib/python3.8/site-packages/pip/_internal/req/req_tracker.py create mode 100644 venv/lib/python3.8/site-packages/pip/_internal/req/req_uninstall.py create mode 100644 venv/lib/python3.8/site-packages/pip/_internal/resolution/__init__.py create mode 100644 venv/lib/python3.8/site-packages/pip/_internal/resolution/__pycache__/__init__.cpython-38.pyc create mode 100644 venv/lib/python3.8/site-packages/pip/_internal/resolution/__pycache__/base.cpython-38.pyc create mode 100644 venv/lib/python3.8/site-packages/pip/_internal/resolution/base.py create mode 100644 venv/lib/python3.8/site-packages/pip/_internal/resolution/legacy/__init__.py create mode 100644 venv/lib/python3.8/site-packages/pip/_internal/resolution/legacy/__pycache__/__init__.cpython-38.pyc create mode 100644 venv/lib/python3.8/site-packages/pip/_internal/resolution/legacy/__pycache__/resolver.cpython-38.pyc create mode 100644 venv/lib/python3.8/site-packages/pip/_internal/resolution/legacy/resolver.py create mode 100644 venv/lib/python3.8/site-packages/pip/_internal/resolution/resolvelib/__init__.py create mode 100644 venv/lib/python3.8/site-packages/pip/_internal/resolution/resolvelib/__pycache__/__init__.cpython-38.pyc create mode 100644 venv/lib/python3.8/site-packages/pip/_internal/resolution/resolvelib/__pycache__/base.cpython-38.pyc create mode 100644 venv/lib/python3.8/site-packages/pip/_internal/resolution/resolvelib/__pycache__/candidates.cpython-38.pyc create mode 100644 venv/lib/python3.8/site-packages/pip/_internal/resolution/resolvelib/__pycache__/factory.cpython-38.pyc create mode 100644 venv/lib/python3.8/site-packages/pip/_internal/resolution/resolvelib/__pycache__/provider.cpython-38.pyc create mode 100644 venv/lib/python3.8/site-packages/pip/_internal/resolution/resolvelib/__pycache__/requirements.cpython-38.pyc create mode 100644 venv/lib/python3.8/site-packages/pip/_internal/resolution/resolvelib/__pycache__/resolver.cpython-38.pyc create mode 100644 venv/lib/python3.8/site-packages/pip/_internal/resolution/resolvelib/base.py create mode 100644 venv/lib/python3.8/site-packages/pip/_internal/resolution/resolvelib/candidates.py create mode 100644 venv/lib/python3.8/site-packages/pip/_internal/resolution/resolvelib/factory.py create mode 100644 venv/lib/python3.8/site-packages/pip/_internal/resolution/resolvelib/provider.py create mode 100644 venv/lib/python3.8/site-packages/pip/_internal/resolution/resolvelib/requirements.py create mode 100644 venv/lib/python3.8/site-packages/pip/_internal/resolution/resolvelib/resolver.py create mode 100644 venv/lib/python3.8/site-packages/pip/_internal/self_outdated_check.py create mode 100644 venv/lib/python3.8/site-packages/pip/_internal/utils/__init__.py create mode 100644 venv/lib/python3.8/site-packages/pip/_internal/utils/__pycache__/__init__.cpython-38.pyc create mode 100644 venv/lib/python3.8/site-packages/pip/_internal/utils/__pycache__/appdirs.cpython-38.pyc create mode 100644 venv/lib/python3.8/site-packages/pip/_internal/utils/__pycache__/compat.cpython-38.pyc create mode 100644 venv/lib/python3.8/site-packages/pip/_internal/utils/__pycache__/compatibility_tags.cpython-38.pyc create mode 100644 venv/lib/python3.8/site-packages/pip/_internal/utils/__pycache__/deprecation.cpython-38.pyc create mode 100644 venv/lib/python3.8/site-packages/pip/_internal/utils/__pycache__/direct_url_helpers.cpython-38.pyc create mode 100644 venv/lib/python3.8/site-packages/pip/_internal/utils/__pycache__/distutils_args.cpython-38.pyc create mode 100644 venv/lib/python3.8/site-packages/pip/_internal/utils/__pycache__/encoding.cpython-38.pyc create mode 100644 venv/lib/python3.8/site-packages/pip/_internal/utils/__pycache__/entrypoints.cpython-38.pyc create mode 100644 venv/lib/python3.8/site-packages/pip/_internal/utils/__pycache__/filesystem.cpython-38.pyc create mode 100644 venv/lib/python3.8/site-packages/pip/_internal/utils/__pycache__/filetypes.cpython-38.pyc create mode 100644 venv/lib/python3.8/site-packages/pip/_internal/utils/__pycache__/glibc.cpython-38.pyc create mode 100644 venv/lib/python3.8/site-packages/pip/_internal/utils/__pycache__/hashes.cpython-38.pyc create mode 100644 venv/lib/python3.8/site-packages/pip/_internal/utils/__pycache__/inject_securetransport.cpython-38.pyc create mode 100644 venv/lib/python3.8/site-packages/pip/_internal/utils/__pycache__/logging.cpython-38.pyc create mode 100644 venv/lib/python3.8/site-packages/pip/_internal/utils/__pycache__/misc.cpython-38.pyc create mode 100644 venv/lib/python3.8/site-packages/pip/_internal/utils/__pycache__/models.cpython-38.pyc create mode 100644 venv/lib/python3.8/site-packages/pip/_internal/utils/__pycache__/packaging.cpython-38.pyc create mode 100644 venv/lib/python3.8/site-packages/pip/_internal/utils/__pycache__/pkg_resources.cpython-38.pyc create mode 100644 venv/lib/python3.8/site-packages/pip/_internal/utils/__pycache__/setuptools_build.cpython-38.pyc create mode 100644 venv/lib/python3.8/site-packages/pip/_internal/utils/__pycache__/subprocess.cpython-38.pyc create mode 100644 venv/lib/python3.8/site-packages/pip/_internal/utils/__pycache__/temp_dir.cpython-38.pyc create mode 100644 venv/lib/python3.8/site-packages/pip/_internal/utils/__pycache__/typing.cpython-38.pyc create mode 100644 venv/lib/python3.8/site-packages/pip/_internal/utils/__pycache__/unpacking.cpython-38.pyc create mode 100644 venv/lib/python3.8/site-packages/pip/_internal/utils/__pycache__/urls.cpython-38.pyc create mode 100644 venv/lib/python3.8/site-packages/pip/_internal/utils/__pycache__/virtualenv.cpython-38.pyc create mode 100644 venv/lib/python3.8/site-packages/pip/_internal/utils/__pycache__/wheel.cpython-38.pyc create mode 100644 venv/lib/python3.8/site-packages/pip/_internal/utils/appdirs.py create mode 100644 venv/lib/python3.8/site-packages/pip/_internal/utils/compat.py create mode 100644 venv/lib/python3.8/site-packages/pip/_internal/utils/compatibility_tags.py create mode 100644 venv/lib/python3.8/site-packages/pip/_internal/utils/deprecation.py create mode 100644 venv/lib/python3.8/site-packages/pip/_internal/utils/direct_url_helpers.py create mode 100644 venv/lib/python3.8/site-packages/pip/_internal/utils/distutils_args.py create mode 100644 venv/lib/python3.8/site-packages/pip/_internal/utils/encoding.py create mode 100644 venv/lib/python3.8/site-packages/pip/_internal/utils/entrypoints.py create mode 100644 venv/lib/python3.8/site-packages/pip/_internal/utils/filesystem.py create mode 100644 venv/lib/python3.8/site-packages/pip/_internal/utils/filetypes.py create mode 100644 venv/lib/python3.8/site-packages/pip/_internal/utils/glibc.py create mode 100644 venv/lib/python3.8/site-packages/pip/_internal/utils/hashes.py create mode 100644 venv/lib/python3.8/site-packages/pip/_internal/utils/inject_securetransport.py create mode 100644 venv/lib/python3.8/site-packages/pip/_internal/utils/logging.py create mode 100644 venv/lib/python3.8/site-packages/pip/_internal/utils/misc.py create mode 100644 venv/lib/python3.8/site-packages/pip/_internal/utils/models.py create mode 100644 venv/lib/python3.8/site-packages/pip/_internal/utils/packaging.py create mode 100644 venv/lib/python3.8/site-packages/pip/_internal/utils/pkg_resources.py create mode 100644 venv/lib/python3.8/site-packages/pip/_internal/utils/setuptools_build.py create mode 100644 venv/lib/python3.8/site-packages/pip/_internal/utils/subprocess.py create mode 100644 venv/lib/python3.8/site-packages/pip/_internal/utils/temp_dir.py create mode 100644 venv/lib/python3.8/site-packages/pip/_internal/utils/typing.py create mode 100644 venv/lib/python3.8/site-packages/pip/_internal/utils/unpacking.py create mode 100644 venv/lib/python3.8/site-packages/pip/_internal/utils/urls.py create mode 100644 venv/lib/python3.8/site-packages/pip/_internal/utils/virtualenv.py create mode 100644 venv/lib/python3.8/site-packages/pip/_internal/utils/wheel.py create mode 100644 venv/lib/python3.8/site-packages/pip/_internal/vcs/__init__.py create mode 100644 venv/lib/python3.8/site-packages/pip/_internal/vcs/__pycache__/__init__.cpython-38.pyc create mode 100644 venv/lib/python3.8/site-packages/pip/_internal/vcs/__pycache__/bazaar.cpython-38.pyc create mode 100644 venv/lib/python3.8/site-packages/pip/_internal/vcs/__pycache__/git.cpython-38.pyc create mode 100644 venv/lib/python3.8/site-packages/pip/_internal/vcs/__pycache__/mercurial.cpython-38.pyc create mode 100644 venv/lib/python3.8/site-packages/pip/_internal/vcs/__pycache__/subversion.cpython-38.pyc create mode 100644 venv/lib/python3.8/site-packages/pip/_internal/vcs/__pycache__/versioncontrol.cpython-38.pyc create mode 100644 venv/lib/python3.8/site-packages/pip/_internal/vcs/bazaar.py create mode 100644 venv/lib/python3.8/site-packages/pip/_internal/vcs/git.py create mode 100644 venv/lib/python3.8/site-packages/pip/_internal/vcs/mercurial.py create mode 100644 venv/lib/python3.8/site-packages/pip/_internal/vcs/subversion.py create mode 100644 venv/lib/python3.8/site-packages/pip/_internal/vcs/versioncontrol.py create mode 100644 venv/lib/python3.8/site-packages/pip/_internal/wheel_builder.py create mode 100644 venv/lib/python3.8/site-packages/pip/_vendor/__init__.py create mode 100644 venv/lib/python3.8/site-packages/pip/_vendor/appdirs.py create mode 100644 venv/lib/python3.8/site-packages/pip/_vendor/cachecontrol/compat.py create mode 100644 venv/lib/python3.8/site-packages/pip/_vendor/cachecontrol/controller.py create mode 100644 venv/lib/python3.8/site-packages/pip/_vendor/cachecontrol/serialize.py create mode 100644 venv/lib/python3.8/site-packages/pip/_vendor/cachecontrol/wrapper.py create mode 100644 venv/lib/python3.8/site-packages/pip/_vendor/chardet/__init__.py create mode 100644 venv/lib/python3.8/site-packages/pip/_vendor/chardet/__pycache__/__init__.cpython-38.pyc create mode 100644 venv/lib/python3.8/site-packages/pip/_vendor/chardet/__pycache__/big5freq.cpython-38.pyc create mode 100644 venv/lib/python3.8/site-packages/pip/_vendor/chardet/__pycache__/big5prober.cpython-38.pyc create mode 100644 venv/lib/python3.8/site-packages/pip/_vendor/chardet/__pycache__/chardistribution.cpython-38.pyc create mode 100644 venv/lib/python3.8/site-packages/pip/_vendor/chardet/__pycache__/charsetgroupprober.cpython-38.pyc create mode 100644 venv/lib/python3.8/site-packages/pip/_vendor/chardet/__pycache__/charsetprober.cpython-38.pyc create mode 100644 venv/lib/python3.8/site-packages/pip/_vendor/chardet/__pycache__/codingstatemachine.cpython-38.pyc create mode 100644 venv/lib/python3.8/site-packages/pip/_vendor/chardet/__pycache__/compat.cpython-38.pyc create mode 100644 venv/lib/python3.8/site-packages/pip/_vendor/chardet/__pycache__/cp949prober.cpython-38.pyc create mode 100644 venv/lib/python3.8/site-packages/pip/_vendor/chardet/__pycache__/enums.cpython-38.pyc create mode 100644 venv/lib/python3.8/site-packages/pip/_vendor/chardet/__pycache__/escprober.cpython-38.pyc create mode 100644 venv/lib/python3.8/site-packages/pip/_vendor/chardet/__pycache__/escsm.cpython-38.pyc create mode 100644 venv/lib/python3.8/site-packages/pip/_vendor/chardet/__pycache__/eucjpprober.cpython-38.pyc create mode 100644 venv/lib/python3.8/site-packages/pip/_vendor/chardet/__pycache__/euckrfreq.cpython-38.pyc create mode 100644 venv/lib/python3.8/site-packages/pip/_vendor/chardet/__pycache__/euckrprober.cpython-38.pyc create mode 100644 venv/lib/python3.8/site-packages/pip/_vendor/chardet/__pycache__/euctwfreq.cpython-38.pyc create mode 100644 venv/lib/python3.8/site-packages/pip/_vendor/chardet/__pycache__/euctwprober.cpython-38.pyc create mode 100644 venv/lib/python3.8/site-packages/pip/_vendor/chardet/__pycache__/gb2312freq.cpython-38.pyc create mode 100644 venv/lib/python3.8/site-packages/pip/_vendor/chardet/__pycache__/gb2312prober.cpython-38.pyc create mode 100644 venv/lib/python3.8/site-packages/pip/_vendor/chardet/__pycache__/hebrewprober.cpython-38.pyc create mode 100644 venv/lib/python3.8/site-packages/pip/_vendor/chardet/__pycache__/jisfreq.cpython-38.pyc create mode 100644 venv/lib/python3.8/site-packages/pip/_vendor/chardet/__pycache__/jpcntx.cpython-38.pyc create mode 100644 venv/lib/python3.8/site-packages/pip/_vendor/chardet/__pycache__/langbulgarianmodel.cpython-38.pyc create mode 100644 venv/lib/python3.8/site-packages/pip/_vendor/chardet/__pycache__/langcyrillicmodel.cpython-38.pyc create mode 100644 venv/lib/python3.8/site-packages/pip/_vendor/chardet/__pycache__/langgreekmodel.cpython-38.pyc create mode 100644 venv/lib/python3.8/site-packages/pip/_vendor/chardet/__pycache__/langhebrewmodel.cpython-38.pyc create mode 100644 venv/lib/python3.8/site-packages/pip/_vendor/chardet/__pycache__/langhungarianmodel.cpython-38.pyc create mode 100644 venv/lib/python3.8/site-packages/pip/_vendor/chardet/__pycache__/langthaimodel.cpython-38.pyc create mode 100644 venv/lib/python3.8/site-packages/pip/_vendor/chardet/__pycache__/langturkishmodel.cpython-38.pyc create mode 100644 venv/lib/python3.8/site-packages/pip/_vendor/chardet/__pycache__/latin1prober.cpython-38.pyc create mode 100644 venv/lib/python3.8/site-packages/pip/_vendor/chardet/__pycache__/mbcharsetprober.cpython-38.pyc create mode 100644 venv/lib/python3.8/site-packages/pip/_vendor/chardet/__pycache__/mbcsgroupprober.cpython-38.pyc create mode 100644 venv/lib/python3.8/site-packages/pip/_vendor/chardet/__pycache__/mbcssm.cpython-38.pyc create mode 100644 venv/lib/python3.8/site-packages/pip/_vendor/chardet/__pycache__/sbcharsetprober.cpython-38.pyc create mode 100644 venv/lib/python3.8/site-packages/pip/_vendor/chardet/__pycache__/sbcsgroupprober.cpython-38.pyc create mode 100644 venv/lib/python3.8/site-packages/pip/_vendor/chardet/__pycache__/sjisprober.cpython-38.pyc create mode 100644 venv/lib/python3.8/site-packages/pip/_vendor/chardet/__pycache__/universaldetector.cpython-38.pyc create mode 100644 venv/lib/python3.8/site-packages/pip/_vendor/chardet/__pycache__/utf8prober.cpython-38.pyc create mode 100644 venv/lib/python3.8/site-packages/pip/_vendor/chardet/__pycache__/version.cpython-38.pyc create mode 100644 venv/lib/python3.8/site-packages/pip/_vendor/chardet/big5freq.py create mode 100644 venv/lib/python3.8/site-packages/pip/_vendor/chardet/big5prober.py create mode 100644 venv/lib/python3.8/site-packages/pip/_vendor/chardet/chardistribution.py create mode 100644 venv/lib/python3.8/site-packages/pip/_vendor/chardet/charsetgroupprober.py create mode 100644 venv/lib/python3.8/site-packages/pip/_vendor/chardet/charsetprober.py create mode 100644 venv/lib/python3.8/site-packages/pip/_vendor/chardet/cli/__init__.py create mode 100644 venv/lib/python3.8/site-packages/pip/_vendor/chardet/cli/__pycache__/__init__.cpython-38.pyc create mode 100644 venv/lib/python3.8/site-packages/pip/_vendor/chardet/cli/__pycache__/chardetect.cpython-38.pyc create mode 100644 venv/lib/python3.8/site-packages/pip/_vendor/chardet/cli/chardetect.py create mode 100644 venv/lib/python3.8/site-packages/pip/_vendor/chardet/codingstatemachine.py create mode 100644 venv/lib/python3.8/site-packages/pip/_vendor/chardet/compat.py create mode 100644 venv/lib/python3.8/site-packages/pip/_vendor/chardet/cp949prober.py create mode 100644 venv/lib/python3.8/site-packages/pip/_vendor/chardet/enums.py create mode 100644 venv/lib/python3.8/site-packages/pip/_vendor/chardet/escprober.py create mode 100644 venv/lib/python3.8/site-packages/pip/_vendor/chardet/escsm.py create mode 100644 venv/lib/python3.8/site-packages/pip/_vendor/chardet/eucjpprober.py create mode 100644 venv/lib/python3.8/site-packages/pip/_vendor/chardet/euckrfreq.py create mode 100644 venv/lib/python3.8/site-packages/pip/_vendor/chardet/euckrprober.py create mode 100644 venv/lib/python3.8/site-packages/pip/_vendor/chardet/euctwfreq.py create mode 100644 venv/lib/python3.8/site-packages/pip/_vendor/chardet/euctwprober.py create mode 100644 venv/lib/python3.8/site-packages/pip/_vendor/chardet/gb2312freq.py create mode 100644 venv/lib/python3.8/site-packages/pip/_vendor/chardet/gb2312prober.py create mode 100644 venv/lib/python3.8/site-packages/pip/_vendor/chardet/hebrewprober.py create mode 100644 venv/lib/python3.8/site-packages/pip/_vendor/chardet/jisfreq.py create mode 100644 venv/lib/python3.8/site-packages/pip/_vendor/chardet/jpcntx.py create mode 100644 venv/lib/python3.8/site-packages/pip/_vendor/chardet/langbulgarianmodel.py create mode 100644 venv/lib/python3.8/site-packages/pip/_vendor/chardet/langcyrillicmodel.py create mode 100644 venv/lib/python3.8/site-packages/pip/_vendor/chardet/langgreekmodel.py create mode 100644 venv/lib/python3.8/site-packages/pip/_vendor/chardet/langhebrewmodel.py create mode 100644 venv/lib/python3.8/site-packages/pip/_vendor/chardet/langhungarianmodel.py create mode 100644 venv/lib/python3.8/site-packages/pip/_vendor/chardet/langthaimodel.py create mode 100644 venv/lib/python3.8/site-packages/pip/_vendor/chardet/langturkishmodel.py create mode 100644 venv/lib/python3.8/site-packages/pip/_vendor/chardet/latin1prober.py create mode 100644 venv/lib/python3.8/site-packages/pip/_vendor/chardet/mbcharsetprober.py create mode 100644 venv/lib/python3.8/site-packages/pip/_vendor/chardet/mbcsgroupprober.py create mode 100644 venv/lib/python3.8/site-packages/pip/_vendor/chardet/mbcssm.py create mode 100644 venv/lib/python3.8/site-packages/pip/_vendor/chardet/sbcharsetprober.py create mode 100644 venv/lib/python3.8/site-packages/pip/_vendor/chardet/sbcsgroupprober.py create mode 100644 venv/lib/python3.8/site-packages/pip/_vendor/chardet/sjisprober.py create mode 100644 venv/lib/python3.8/site-packages/pip/_vendor/chardet/universaldetector.py create mode 100644 venv/lib/python3.8/site-packages/pip/_vendor/chardet/utf8prober.py create mode 100644 venv/lib/python3.8/site-packages/pip/_vendor/chardet/version.py create mode 100644 venv/lib/python3.8/site-packages/pip/_vendor/colorama/__init__.py create mode 100644 venv/lib/python3.8/site-packages/pip/_vendor/colorama/__pycache__/__init__.cpython-38.pyc create mode 100644 venv/lib/python3.8/site-packages/pip/_vendor/colorama/__pycache__/ansi.cpython-38.pyc create mode 100644 venv/lib/python3.8/site-packages/pip/_vendor/colorama/__pycache__/ansitowin32.cpython-38.pyc create mode 100644 venv/lib/python3.8/site-packages/pip/_vendor/colorama/__pycache__/initialise.cpython-38.pyc create mode 100644 venv/lib/python3.8/site-packages/pip/_vendor/colorama/__pycache__/win32.cpython-38.pyc create mode 100644 venv/lib/python3.8/site-packages/pip/_vendor/colorama/__pycache__/winterm.cpython-38.pyc create mode 100644 venv/lib/python3.8/site-packages/pip/_vendor/colorama/ansi.py create mode 100644 venv/lib/python3.8/site-packages/pip/_vendor/colorama/ansitowin32.py create mode 100644 venv/lib/python3.8/site-packages/pip/_vendor/colorama/initialise.py create mode 100644 venv/lib/python3.8/site-packages/pip/_vendor/colorama/win32.py create mode 100644 venv/lib/python3.8/site-packages/pip/_vendor/colorama/winterm.py create mode 100644 venv/lib/python3.8/site-packages/pip/_vendor/contextlib2.py create mode 100644 venv/lib/python3.8/site-packages/pip/_vendor/distlib/__init__.py create mode 100644 venv/lib/python3.8/site-packages/pip/_vendor/distlib/__pycache__/__init__.cpython-38.pyc create mode 100644 venv/lib/python3.8/site-packages/pip/_vendor/distlib/__pycache__/compat.cpython-38.pyc create mode 100644 venv/lib/python3.8/site-packages/pip/_vendor/distlib/__pycache__/database.cpython-38.pyc create mode 100644 venv/lib/python3.8/site-packages/pip/_vendor/distlib/__pycache__/index.cpython-38.pyc create mode 100644 venv/lib/python3.8/site-packages/pip/_vendor/distlib/__pycache__/locators.cpython-38.pyc create mode 100644 venv/lib/python3.8/site-packages/pip/_vendor/distlib/__pycache__/manifest.cpython-38.pyc create mode 100644 venv/lib/python3.8/site-packages/pip/_vendor/distlib/__pycache__/markers.cpython-38.pyc create mode 100644 venv/lib/python3.8/site-packages/pip/_vendor/distlib/__pycache__/metadata.cpython-38.pyc create mode 100644 venv/lib/python3.8/site-packages/pip/_vendor/distlib/__pycache__/resources.cpython-38.pyc create mode 100644 venv/lib/python3.8/site-packages/pip/_vendor/distlib/__pycache__/scripts.cpython-38.pyc create mode 100644 venv/lib/python3.8/site-packages/pip/_vendor/distlib/__pycache__/util.cpython-38.pyc create mode 100644 venv/lib/python3.8/site-packages/pip/_vendor/distlib/__pycache__/version.cpython-38.pyc create mode 100644 venv/lib/python3.8/site-packages/pip/_vendor/distlib/__pycache__/wheel.cpython-38.pyc create mode 100644 venv/lib/python3.8/site-packages/pip/_vendor/distlib/_backport/__init__.py create mode 100644 venv/lib/python3.8/site-packages/pip/_vendor/distlib/_backport/__pycache__/__init__.cpython-38.pyc create mode 100644 venv/lib/python3.8/site-packages/pip/_vendor/distlib/_backport/__pycache__/misc.cpython-38.pyc create mode 100644 venv/lib/python3.8/site-packages/pip/_vendor/distlib/_backport/__pycache__/shutil.cpython-38.pyc create mode 100644 venv/lib/python3.8/site-packages/pip/_vendor/distlib/_backport/__pycache__/sysconfig.cpython-38.pyc create mode 100644 venv/lib/python3.8/site-packages/pip/_vendor/distlib/_backport/__pycache__/tarfile.cpython-38.pyc create mode 100644 venv/lib/python3.8/site-packages/pip/_vendor/distlib/_backport/misc.py create mode 100644 venv/lib/python3.8/site-packages/pip/_vendor/distlib/_backport/shutil.py create mode 100644 venv/lib/python3.8/site-packages/pip/_vendor/distlib/_backport/sysconfig.cfg create mode 100644 venv/lib/python3.8/site-packages/pip/_vendor/distlib/_backport/sysconfig.py create mode 100644 venv/lib/python3.8/site-packages/pip/_vendor/distlib/_backport/tarfile.py create mode 100644 venv/lib/python3.8/site-packages/pip/_vendor/distlib/compat.py create mode 100644 venv/lib/python3.8/site-packages/pip/_vendor/distlib/database.py create mode 100644 venv/lib/python3.8/site-packages/pip/_vendor/distlib/index.py create mode 100644 venv/lib/python3.8/site-packages/pip/_vendor/distlib/locators.py create mode 100644 venv/lib/python3.8/site-packages/pip/_vendor/distlib/manifest.py create mode 100644 venv/lib/python3.8/site-packages/pip/_vendor/distlib/markers.py create mode 100644 venv/lib/python3.8/site-packages/pip/_vendor/distlib/metadata.py create mode 100644 venv/lib/python3.8/site-packages/pip/_vendor/distlib/resources.py create mode 100644 venv/lib/python3.8/site-packages/pip/_vendor/distlib/scripts.py create mode 100644 venv/lib/python3.8/site-packages/pip/_vendor/distlib/t32.exe create mode 100644 venv/lib/python3.8/site-packages/pip/_vendor/distlib/t64.exe create mode 100644 venv/lib/python3.8/site-packages/pip/_vendor/distlib/util.py create mode 100644 venv/lib/python3.8/site-packages/pip/_vendor/distlib/version.py create mode 100644 venv/lib/python3.8/site-packages/pip/_vendor/distlib/w32.exe create mode 100644 venv/lib/python3.8/site-packages/pip/_vendor/distlib/w64.exe create mode 100644 venv/lib/python3.8/site-packages/pip/_vendor/distlib/wheel.py create mode 100644 venv/lib/python3.8/site-packages/pip/_vendor/distro.py create mode 100644 venv/lib/python3.8/site-packages/pip/_vendor/ipaddress.py create mode 100644 venv/lib/python3.8/site-packages/pip/_vendor/msgpack/__init__.py create mode 100644 venv/lib/python3.8/site-packages/pip/_vendor/msgpack/__pycache__/__init__.cpython-38.pyc create mode 100644 venv/lib/python3.8/site-packages/pip/_vendor/msgpack/__pycache__/_version.cpython-38.pyc create mode 100644 venv/lib/python3.8/site-packages/pip/_vendor/msgpack/__pycache__/exceptions.cpython-38.pyc create mode 100644 venv/lib/python3.8/site-packages/pip/_vendor/msgpack/__pycache__/ext.cpython-38.pyc create mode 100644 venv/lib/python3.8/site-packages/pip/_vendor/msgpack/__pycache__/fallback.cpython-38.pyc create mode 100644 venv/lib/python3.8/site-packages/pip/_vendor/msgpack/_version.py create mode 100644 venv/lib/python3.8/site-packages/pip/_vendor/msgpack/exceptions.py create mode 100644 venv/lib/python3.8/site-packages/pip/_vendor/msgpack/ext.py create mode 100644 venv/lib/python3.8/site-packages/pip/_vendor/msgpack/fallback.py create mode 100644 venv/lib/python3.8/site-packages/pip/_vendor/packaging/__about__.py create mode 100644 venv/lib/python3.8/site-packages/pip/_vendor/packaging/__init__.py create mode 100644 venv/lib/python3.8/site-packages/pip/_vendor/packaging/__pycache__/__about__.cpython-38.pyc create mode 100644 venv/lib/python3.8/site-packages/pip/_vendor/packaging/__pycache__/__init__.cpython-38.pyc create mode 100644 venv/lib/python3.8/site-packages/pip/_vendor/packaging/__pycache__/_compat.cpython-38.pyc create mode 100644 venv/lib/python3.8/site-packages/pip/_vendor/packaging/__pycache__/_structures.cpython-38.pyc create mode 100644 venv/lib/python3.8/site-packages/pip/_vendor/packaging/__pycache__/_typing.cpython-38.pyc create mode 100644 venv/lib/python3.8/site-packages/pip/_vendor/packaging/__pycache__/markers.cpython-38.pyc create mode 100644 venv/lib/python3.8/site-packages/pip/_vendor/packaging/__pycache__/requirements.cpython-38.pyc create mode 100644 venv/lib/python3.8/site-packages/pip/_vendor/packaging/__pycache__/specifiers.cpython-38.pyc create mode 100644 venv/lib/python3.8/site-packages/pip/_vendor/packaging/__pycache__/tags.cpython-38.pyc create mode 100644 venv/lib/python3.8/site-packages/pip/_vendor/packaging/__pycache__/utils.cpython-38.pyc create mode 100644 venv/lib/python3.8/site-packages/pip/_vendor/packaging/__pycache__/version.cpython-38.pyc create mode 100644 venv/lib/python3.8/site-packages/pip/_vendor/packaging/_compat.py create mode 100644 venv/lib/python3.8/site-packages/pip/_vendor/packaging/_structures.py create mode 100644 venv/lib/python3.8/site-packages/pip/_vendor/packaging/_typing.py create mode 100644 venv/lib/python3.8/site-packages/pip/_vendor/packaging/markers.py create mode 100644 venv/lib/python3.8/site-packages/pip/_vendor/packaging/requirements.py create mode 100644 venv/lib/python3.8/site-packages/pip/_vendor/packaging/specifiers.py create mode 100644 venv/lib/python3.8/site-packages/pip/_vendor/packaging/tags.py create mode 100644 venv/lib/python3.8/site-packages/pip/_vendor/packaging/utils.py create mode 100644 venv/lib/python3.8/site-packages/pip/_vendor/packaging/version.py create mode 100644 venv/lib/python3.8/site-packages/pip/_vendor/pep517/__init__.py create mode 100644 venv/lib/python3.8/site-packages/pip/_vendor/pep517/__pycache__/__init__.cpython-38.pyc create mode 100644 venv/lib/python3.8/site-packages/pip/_vendor/pep517/__pycache__/_in_process.cpython-38.pyc create mode 100644 venv/lib/python3.8/site-packages/pip/_vendor/pep517/__pycache__/build.cpython-38.pyc create mode 100644 venv/lib/python3.8/site-packages/pip/_vendor/pep517/__pycache__/check.cpython-38.pyc create mode 100644 venv/lib/python3.8/site-packages/pip/_vendor/pep517/__pycache__/colorlog.cpython-38.pyc create mode 100644 venv/lib/python3.8/site-packages/pip/_vendor/pep517/__pycache__/compat.cpython-38.pyc create mode 100644 venv/lib/python3.8/site-packages/pip/_vendor/pep517/__pycache__/dirtools.cpython-38.pyc create mode 100644 venv/lib/python3.8/site-packages/pip/_vendor/pep517/__pycache__/envbuild.cpython-38.pyc create mode 100644 venv/lib/python3.8/site-packages/pip/_vendor/pep517/__pycache__/meta.cpython-38.pyc create mode 100644 venv/lib/python3.8/site-packages/pip/_vendor/pep517/__pycache__/wrappers.cpython-38.pyc create mode 100644 venv/lib/python3.8/site-packages/pip/_vendor/pep517/_in_process.py create mode 100644 venv/lib/python3.8/site-packages/pip/_vendor/pep517/build.py create mode 100644 venv/lib/python3.8/site-packages/pip/_vendor/pep517/check.py create mode 100644 venv/lib/python3.8/site-packages/pip/_vendor/pep517/colorlog.py create mode 100644 venv/lib/python3.8/site-packages/pip/_vendor/pep517/compat.py create mode 100644 venv/lib/python3.8/site-packages/pip/_vendor/pep517/dirtools.py create mode 100644 venv/lib/python3.8/site-packages/pip/_vendor/pep517/envbuild.py create mode 100644 venv/lib/python3.8/site-packages/pip/_vendor/pep517/meta.py create mode 100644 venv/lib/python3.8/site-packages/pip/_vendor/pep517/wrappers.py create mode 100644 venv/lib/python3.8/site-packages/pip/_vendor/progress/__init__.py create mode 100644 venv/lib/python3.8/site-packages/pip/_vendor/progress/__pycache__/__init__.cpython-38.pyc create mode 100644 venv/lib/python3.8/site-packages/pip/_vendor/progress/__pycache__/bar.cpython-38.pyc create mode 100644 venv/lib/python3.8/site-packages/pip/_vendor/progress/__pycache__/counter.cpython-38.pyc create mode 100644 venv/lib/python3.8/site-packages/pip/_vendor/progress/__pycache__/spinner.cpython-38.pyc create mode 100644 venv/lib/python3.8/site-packages/pip/_vendor/progress/bar.py create mode 100644 venv/lib/python3.8/site-packages/pip/_vendor/progress/counter.py create mode 100644 venv/lib/python3.8/site-packages/pip/_vendor/progress/spinner.py create mode 100644 venv/lib/python3.8/site-packages/pip/_vendor/pyparsing.py create mode 100644 venv/lib/python3.8/site-packages/pip/_vendor/retrying.py create mode 100644 venv/lib/python3.8/site-packages/pip/_vendor/six.py create mode 100644 venv/lib/python3.8/site-packages/pip/_vendor/toml.py create mode 100644 venv/lib/python3.8/site-packages/pip/_vendor/toml/__init__.py create mode 100644 venv/lib/python3.8/site-packages/pip/_vendor/toml/__pycache__/__init__.cpython-38.pyc create mode 100644 venv/lib/python3.8/site-packages/pip/_vendor/toml/__pycache__/decoder.cpython-38.pyc create mode 100644 venv/lib/python3.8/site-packages/pip/_vendor/toml/__pycache__/encoder.cpython-38.pyc create mode 100644 venv/lib/python3.8/site-packages/pip/_vendor/toml/__pycache__/ordered.cpython-38.pyc create mode 100644 venv/lib/python3.8/site-packages/pip/_vendor/toml/__pycache__/tz.cpython-38.pyc create mode 100644 venv/lib/python3.8/site-packages/pip/_vendor/toml/decoder.py create mode 100644 venv/lib/python3.8/site-packages/pip/_vendor/toml/encoder.py create mode 100644 venv/lib/python3.8/site-packages/pip/_vendor/toml/ordered.py create mode 100644 venv/lib/python3.8/site-packages/pip/_vendor/toml/tz.py create mode 100644 venv/lib/python3.8/site-packages/pip/_vendor/vendor.txt create mode 100644 venv/lib/python3.8/site-packages/pip/_vendor/webencodings/__init__.py create mode 100644 venv/lib/python3.8/site-packages/pip/_vendor/webencodings/__pycache__/__init__.cpython-38.pyc create mode 100644 venv/lib/python3.8/site-packages/pip/_vendor/webencodings/__pycache__/labels.cpython-38.pyc create mode 100644 venv/lib/python3.8/site-packages/pip/_vendor/webencodings/__pycache__/mklabels.cpython-38.pyc create mode 100644 venv/lib/python3.8/site-packages/pip/_vendor/webencodings/__pycache__/tests.cpython-38.pyc create mode 100644 venv/lib/python3.8/site-packages/pip/_vendor/webencodings/__pycache__/x_user_defined.cpython-38.pyc create mode 100644 venv/lib/python3.8/site-packages/pip/_vendor/webencodings/labels.py create mode 100644 venv/lib/python3.8/site-packages/pip/_vendor/webencodings/mklabels.py create mode 100644 venv/lib/python3.8/site-packages/pip/_vendor/webencodings/tests.py create mode 100644 venv/lib/python3.8/site-packages/pip/_vendor/webencodings/x_user_defined.py create mode 100644 venv/lib/python3.8/site-packages/pkg_resources/__init__.py create mode 100644 venv/lib/python3.8/site-packages/pkg_resources/__pycache__/__init__.cpython-38.pyc create mode 100644 venv/lib/python3.8/site-packages/pkg_resources/__pycache__/py2_warn.cpython-38.pyc create mode 100644 venv/lib/python3.8/site-packages/pkg_resources/__pycache__/py31compat.cpython-38.pyc create mode 100644 venv/lib/python3.8/site-packages/pkg_resources/_vendor/__init__.py create mode 100644 venv/lib/python3.8/site-packages/pkg_resources/_vendor/__pycache__/__init__.cpython-38.pyc create mode 100644 venv/lib/python3.8/site-packages/pkg_resources/_vendor/__pycache__/appdirs.cpython-38.pyc create mode 100644 venv/lib/python3.8/site-packages/pkg_resources/_vendor/__pycache__/pyparsing.cpython-38.pyc create mode 100644 venv/lib/python3.8/site-packages/pkg_resources/_vendor/__pycache__/six.cpython-38.pyc create mode 100644 venv/lib/python3.8/site-packages/pkg_resources/_vendor/appdirs.py create mode 100644 venv/lib/python3.8/site-packages/pkg_resources/_vendor/packaging/__about__.py create mode 100644 venv/lib/python3.8/site-packages/pkg_resources/_vendor/packaging/__init__.py create mode 100644 venv/lib/python3.8/site-packages/pkg_resources/_vendor/packaging/__pycache__/__about__.cpython-38.pyc create mode 100644 venv/lib/python3.8/site-packages/pkg_resources/_vendor/packaging/__pycache__/__init__.cpython-38.pyc create mode 100644 venv/lib/python3.8/site-packages/pkg_resources/_vendor/packaging/__pycache__/_compat.cpython-38.pyc create mode 100644 venv/lib/python3.8/site-packages/pkg_resources/_vendor/packaging/__pycache__/_structures.cpython-38.pyc create mode 100644 venv/lib/python3.8/site-packages/pkg_resources/_vendor/packaging/__pycache__/markers.cpython-38.pyc create mode 100644 venv/lib/python3.8/site-packages/pkg_resources/_vendor/packaging/__pycache__/requirements.cpython-38.pyc create mode 100644 venv/lib/python3.8/site-packages/pkg_resources/_vendor/packaging/__pycache__/specifiers.cpython-38.pyc create mode 100644 venv/lib/python3.8/site-packages/pkg_resources/_vendor/packaging/__pycache__/utils.cpython-38.pyc create mode 100644 venv/lib/python3.8/site-packages/pkg_resources/_vendor/packaging/__pycache__/version.cpython-38.pyc create mode 100644 venv/lib/python3.8/site-packages/pkg_resources/_vendor/packaging/_compat.py create mode 100644 venv/lib/python3.8/site-packages/pkg_resources/_vendor/packaging/_structures.py create mode 100644 venv/lib/python3.8/site-packages/pkg_resources/_vendor/packaging/markers.py create mode 100644 venv/lib/python3.8/site-packages/pkg_resources/_vendor/packaging/requirements.py create mode 100644 venv/lib/python3.8/site-packages/pkg_resources/_vendor/packaging/specifiers.py create mode 100644 venv/lib/python3.8/site-packages/pkg_resources/_vendor/packaging/utils.py create mode 100644 venv/lib/python3.8/site-packages/pkg_resources/_vendor/packaging/version.py create mode 100644 venv/lib/python3.8/site-packages/pkg_resources/_vendor/pyparsing.py create mode 100644 venv/lib/python3.8/site-packages/pkg_resources/_vendor/six.py create mode 100644 venv/lib/python3.8/site-packages/pkg_resources/extern/__init__.py create mode 100644 venv/lib/python3.8/site-packages/pkg_resources/extern/__pycache__/__init__.cpython-38.pyc create mode 100644 venv/lib/python3.8/site-packages/pkg_resources/py2_warn.py create mode 100644 venv/lib/python3.8/site-packages/pkg_resources/py31compat.py create mode 100644 venv/lib/python3.8/site-packages/setuptools-47.1.0.dist-info/INSTALLER create mode 100644 venv/lib/python3.8/site-packages/setuptools-47.1.0.dist-info/LICENSE create mode 100644 venv/lib/python3.8/site-packages/setuptools-47.1.0.dist-info/METADATA create mode 100644 venv/lib/python3.8/site-packages/setuptools-47.1.0.dist-info/RECORD create mode 100644 venv/lib/python3.8/site-packages/setuptools-47.1.0.dist-info/WHEEL create mode 100644 venv/lib/python3.8/site-packages/setuptools-47.1.0.dist-info/dependency_links.txt create mode 100644 venv/lib/python3.8/site-packages/setuptools-47.1.0.dist-info/entry_points.txt create mode 100644 venv/lib/python3.8/site-packages/setuptools-47.1.0.dist-info/top_level.txt create mode 100644 venv/lib/python3.8/site-packages/setuptools-47.1.0.dist-info/zip-safe create mode 100644 venv/lib/python3.8/site-packages/setuptools/__init__.py create mode 100644 venv/lib/python3.8/site-packages/setuptools/__pycache__/__init__.cpython-38.pyc create mode 100644 venv/lib/python3.8/site-packages/setuptools/__pycache__/_deprecation_warning.cpython-38.pyc create mode 100644 venv/lib/python3.8/site-packages/setuptools/__pycache__/_imp.cpython-38.pyc create mode 100644 venv/lib/python3.8/site-packages/setuptools/__pycache__/archive_util.cpython-38.pyc create mode 100644 venv/lib/python3.8/site-packages/setuptools/__pycache__/build_meta.cpython-38.pyc create mode 100644 venv/lib/python3.8/site-packages/setuptools/__pycache__/config.cpython-38.pyc create mode 100644 venv/lib/python3.8/site-packages/setuptools/__pycache__/dep_util.cpython-38.pyc create mode 100644 venv/lib/python3.8/site-packages/setuptools/__pycache__/depends.cpython-38.pyc create mode 100644 venv/lib/python3.8/site-packages/setuptools/__pycache__/dist.cpython-38.pyc create mode 100644 venv/lib/python3.8/site-packages/setuptools/__pycache__/errors.cpython-38.pyc create mode 100644 venv/lib/python3.8/site-packages/setuptools/__pycache__/extension.cpython-38.pyc create mode 100644 venv/lib/python3.8/site-packages/setuptools/__pycache__/glob.cpython-38.pyc create mode 100644 venv/lib/python3.8/site-packages/setuptools/__pycache__/installer.cpython-38.pyc create mode 100644 venv/lib/python3.8/site-packages/setuptools/__pycache__/launch.cpython-38.pyc create mode 100644 venv/lib/python3.8/site-packages/setuptools/__pycache__/lib2to3_ex.cpython-38.pyc create mode 100644 venv/lib/python3.8/site-packages/setuptools/__pycache__/monkey.cpython-38.pyc create mode 100644 venv/lib/python3.8/site-packages/setuptools/__pycache__/msvc.cpython-38.pyc create mode 100644 venv/lib/python3.8/site-packages/setuptools/__pycache__/namespaces.cpython-38.pyc create mode 100644 venv/lib/python3.8/site-packages/setuptools/__pycache__/package_index.cpython-38.pyc create mode 100644 venv/lib/python3.8/site-packages/setuptools/__pycache__/py27compat.cpython-38.pyc create mode 100644 venv/lib/python3.8/site-packages/setuptools/__pycache__/py31compat.cpython-38.pyc create mode 100644 venv/lib/python3.8/site-packages/setuptools/__pycache__/py33compat.cpython-38.pyc create mode 100644 venv/lib/python3.8/site-packages/setuptools/__pycache__/py34compat.cpython-38.pyc create mode 100644 venv/lib/python3.8/site-packages/setuptools/__pycache__/sandbox.cpython-38.pyc create mode 100644 venv/lib/python3.8/site-packages/setuptools/__pycache__/site-patch.cpython-38.pyc create mode 100644 venv/lib/python3.8/site-packages/setuptools/__pycache__/ssl_support.cpython-38.pyc create mode 100644 venv/lib/python3.8/site-packages/setuptools/__pycache__/unicode_utils.cpython-38.pyc create mode 100644 venv/lib/python3.8/site-packages/setuptools/__pycache__/version.cpython-38.pyc create mode 100644 venv/lib/python3.8/site-packages/setuptools/__pycache__/wheel.cpython-38.pyc create mode 100644 venv/lib/python3.8/site-packages/setuptools/__pycache__/windows_support.cpython-38.pyc create mode 100644 venv/lib/python3.8/site-packages/setuptools/_deprecation_warning.py create mode 100644 venv/lib/python3.8/site-packages/setuptools/_imp.py create mode 100644 venv/lib/python3.8/site-packages/setuptools/_vendor/__init__.py create mode 100644 venv/lib/python3.8/site-packages/setuptools/_vendor/__pycache__/__init__.cpython-38.pyc create mode 100644 venv/lib/python3.8/site-packages/setuptools/_vendor/__pycache__/ordered_set.cpython-38.pyc create mode 100644 venv/lib/python3.8/site-packages/setuptools/_vendor/__pycache__/pyparsing.cpython-38.pyc create mode 100644 venv/lib/python3.8/site-packages/setuptools/_vendor/__pycache__/six.cpython-38.pyc create mode 100644 venv/lib/python3.8/site-packages/setuptools/_vendor/ordered_set.py create mode 100644 venv/lib/python3.8/site-packages/setuptools/_vendor/packaging/__about__.py create mode 100644 venv/lib/python3.8/site-packages/setuptools/_vendor/packaging/__init__.py create mode 100644 venv/lib/python3.8/site-packages/setuptools/_vendor/packaging/__pycache__/__about__.cpython-38.pyc create mode 100644 venv/lib/python3.8/site-packages/setuptools/_vendor/packaging/__pycache__/__init__.cpython-38.pyc create mode 100644 venv/lib/python3.8/site-packages/setuptools/_vendor/packaging/__pycache__/_compat.cpython-38.pyc create mode 100644 venv/lib/python3.8/site-packages/setuptools/_vendor/packaging/__pycache__/_structures.cpython-38.pyc create mode 100644 venv/lib/python3.8/site-packages/setuptools/_vendor/packaging/__pycache__/markers.cpython-38.pyc create mode 100644 venv/lib/python3.8/site-packages/setuptools/_vendor/packaging/__pycache__/requirements.cpython-38.pyc create mode 100644 venv/lib/python3.8/site-packages/setuptools/_vendor/packaging/__pycache__/specifiers.cpython-38.pyc create mode 100644 venv/lib/python3.8/site-packages/setuptools/_vendor/packaging/__pycache__/tags.cpython-38.pyc create mode 100644 venv/lib/python3.8/site-packages/setuptools/_vendor/packaging/__pycache__/utils.cpython-38.pyc create mode 100644 venv/lib/python3.8/site-packages/setuptools/_vendor/packaging/__pycache__/version.cpython-38.pyc create mode 100644 venv/lib/python3.8/site-packages/setuptools/_vendor/packaging/_compat.py create mode 100644 venv/lib/python3.8/site-packages/setuptools/_vendor/packaging/_structures.py create mode 100644 venv/lib/python3.8/site-packages/setuptools/_vendor/packaging/markers.py create mode 100644 venv/lib/python3.8/site-packages/setuptools/_vendor/packaging/requirements.py create mode 100644 venv/lib/python3.8/site-packages/setuptools/_vendor/packaging/specifiers.py create mode 100644 venv/lib/python3.8/site-packages/setuptools/_vendor/packaging/tags.py create mode 100644 venv/lib/python3.8/site-packages/setuptools/_vendor/packaging/utils.py create mode 100644 venv/lib/python3.8/site-packages/setuptools/_vendor/packaging/version.py create mode 100644 venv/lib/python3.8/site-packages/setuptools/_vendor/pyparsing.py create mode 100644 venv/lib/python3.8/site-packages/setuptools/_vendor/six.py create mode 100644 venv/lib/python3.8/site-packages/setuptools/archive_util.py create mode 100644 venv/lib/python3.8/site-packages/setuptools/build_meta.py create mode 100644 venv/lib/python3.8/site-packages/setuptools/cli-32.exe create mode 100644 venv/lib/python3.8/site-packages/setuptools/cli-64.exe create mode 100644 venv/lib/python3.8/site-packages/setuptools/cli.exe create mode 100644 venv/lib/python3.8/site-packages/setuptools/command/__init__.py create mode 100644 venv/lib/python3.8/site-packages/setuptools/command/__pycache__/__init__.cpython-38.pyc create mode 100644 venv/lib/python3.8/site-packages/setuptools/command/__pycache__/alias.cpython-38.pyc create mode 100644 venv/lib/python3.8/site-packages/setuptools/command/__pycache__/bdist_egg.cpython-38.pyc create mode 100644 venv/lib/python3.8/site-packages/setuptools/command/__pycache__/bdist_rpm.cpython-38.pyc create mode 100644 venv/lib/python3.8/site-packages/setuptools/command/__pycache__/bdist_wininst.cpython-38.pyc create mode 100644 venv/lib/python3.8/site-packages/setuptools/command/__pycache__/build_clib.cpython-38.pyc create mode 100644 venv/lib/python3.8/site-packages/setuptools/command/__pycache__/build_ext.cpython-38.pyc create mode 100644 venv/lib/python3.8/site-packages/setuptools/command/__pycache__/build_py.cpython-38.pyc create mode 100644 venv/lib/python3.8/site-packages/setuptools/command/__pycache__/develop.cpython-38.pyc create mode 100644 venv/lib/python3.8/site-packages/setuptools/command/__pycache__/dist_info.cpython-38.pyc create mode 100644 venv/lib/python3.8/site-packages/setuptools/command/__pycache__/easy_install.cpython-38.pyc create mode 100644 venv/lib/python3.8/site-packages/setuptools/command/__pycache__/egg_info.cpython-38.pyc create mode 100644 venv/lib/python3.8/site-packages/setuptools/command/__pycache__/install.cpython-38.pyc create mode 100644 venv/lib/python3.8/site-packages/setuptools/command/__pycache__/install_egg_info.cpython-38.pyc create mode 100644 venv/lib/python3.8/site-packages/setuptools/command/__pycache__/install_lib.cpython-38.pyc create mode 100644 venv/lib/python3.8/site-packages/setuptools/command/__pycache__/install_scripts.cpython-38.pyc create mode 100644 venv/lib/python3.8/site-packages/setuptools/command/__pycache__/py36compat.cpython-38.pyc create mode 100644 venv/lib/python3.8/site-packages/setuptools/command/__pycache__/register.cpython-38.pyc create mode 100644 venv/lib/python3.8/site-packages/setuptools/command/__pycache__/rotate.cpython-38.pyc create mode 100644 venv/lib/python3.8/site-packages/setuptools/command/__pycache__/saveopts.cpython-38.pyc create mode 100644 venv/lib/python3.8/site-packages/setuptools/command/__pycache__/sdist.cpython-38.pyc create mode 100644 venv/lib/python3.8/site-packages/setuptools/command/__pycache__/setopt.cpython-38.pyc create mode 100644 venv/lib/python3.8/site-packages/setuptools/command/__pycache__/test.cpython-38.pyc create mode 100644 venv/lib/python3.8/site-packages/setuptools/command/__pycache__/upload.cpython-38.pyc create mode 100644 venv/lib/python3.8/site-packages/setuptools/command/__pycache__/upload_docs.cpython-38.pyc create mode 100644 venv/lib/python3.8/site-packages/setuptools/command/alias.py create mode 100644 venv/lib/python3.8/site-packages/setuptools/command/bdist_egg.py create mode 100644 venv/lib/python3.8/site-packages/setuptools/command/bdist_rpm.py create mode 100644 venv/lib/python3.8/site-packages/setuptools/command/bdist_wininst.py create mode 100644 venv/lib/python3.8/site-packages/setuptools/command/build_clib.py create mode 100644 venv/lib/python3.8/site-packages/setuptools/command/build_ext.py create mode 100644 venv/lib/python3.8/site-packages/setuptools/command/build_py.py create mode 100644 venv/lib/python3.8/site-packages/setuptools/command/develop.py create mode 100644 venv/lib/python3.8/site-packages/setuptools/command/dist_info.py create mode 100644 venv/lib/python3.8/site-packages/setuptools/command/easy_install.py create mode 100644 venv/lib/python3.8/site-packages/setuptools/command/egg_info.py create mode 100644 venv/lib/python3.8/site-packages/setuptools/command/install.py create mode 100644 venv/lib/python3.8/site-packages/setuptools/command/install_egg_info.py create mode 100644 venv/lib/python3.8/site-packages/setuptools/command/install_lib.py create mode 100644 venv/lib/python3.8/site-packages/setuptools/command/install_scripts.py create mode 100644 venv/lib/python3.8/site-packages/setuptools/command/launcher manifest.xml create mode 100644 venv/lib/python3.8/site-packages/setuptools/command/py36compat.py create mode 100644 venv/lib/python3.8/site-packages/setuptools/command/register.py create mode 100644 venv/lib/python3.8/site-packages/setuptools/command/rotate.py create mode 100644 venv/lib/python3.8/site-packages/setuptools/command/saveopts.py create mode 100644 venv/lib/python3.8/site-packages/setuptools/command/sdist.py create mode 100644 venv/lib/python3.8/site-packages/setuptools/command/setopt.py create mode 100644 venv/lib/python3.8/site-packages/setuptools/command/test.py create mode 100644 venv/lib/python3.8/site-packages/setuptools/command/upload.py create mode 100644 venv/lib/python3.8/site-packages/setuptools/command/upload_docs.py create mode 100644 venv/lib/python3.8/site-packages/setuptools/config.py create mode 100644 venv/lib/python3.8/site-packages/setuptools/dep_util.py create mode 100644 venv/lib/python3.8/site-packages/setuptools/depends.py create mode 100644 venv/lib/python3.8/site-packages/setuptools/dist.py create mode 100644 venv/lib/python3.8/site-packages/setuptools/errors.py create mode 100644 venv/lib/python3.8/site-packages/setuptools/extension.py create mode 100644 venv/lib/python3.8/site-packages/setuptools/extern/__init__.py create mode 100644 venv/lib/python3.8/site-packages/setuptools/extern/__pycache__/__init__.cpython-38.pyc create mode 100644 venv/lib/python3.8/site-packages/setuptools/glob.py create mode 100644 venv/lib/python3.8/site-packages/setuptools/gui-32.exe create mode 100644 venv/lib/python3.8/site-packages/setuptools/gui-64.exe create mode 100644 venv/lib/python3.8/site-packages/setuptools/gui.exe create mode 100644 venv/lib/python3.8/site-packages/setuptools/installer.py create mode 100644 venv/lib/python3.8/site-packages/setuptools/launch.py create mode 100644 venv/lib/python3.8/site-packages/setuptools/lib2to3_ex.py create mode 100644 venv/lib/python3.8/site-packages/setuptools/monkey.py create mode 100644 venv/lib/python3.8/site-packages/setuptools/msvc.py create mode 100644 venv/lib/python3.8/site-packages/setuptools/namespaces.py create mode 100644 venv/lib/python3.8/site-packages/setuptools/package_index.py create mode 100644 venv/lib/python3.8/site-packages/setuptools/py27compat.py create mode 100644 venv/lib/python3.8/site-packages/setuptools/py31compat.py create mode 100644 venv/lib/python3.8/site-packages/setuptools/py33compat.py create mode 100644 venv/lib/python3.8/site-packages/setuptools/py34compat.py create mode 100644 venv/lib/python3.8/site-packages/setuptools/sandbox.py create mode 100644 venv/lib/python3.8/site-packages/setuptools/script (dev).tmpl create mode 100644 venv/lib/python3.8/site-packages/setuptools/script.tmpl create mode 100644 venv/lib/python3.8/site-packages/setuptools/site-patch.py create mode 100644 venv/lib/python3.8/site-packages/setuptools/ssl_support.py create mode 100644 venv/lib/python3.8/site-packages/setuptools/unicode_utils.py create mode 100644 venv/lib/python3.8/site-packages/setuptools/version.py create mode 100644 venv/lib/python3.8/site-packages/setuptools/wheel.py create mode 100644 venv/lib/python3.8/site-packages/setuptools/windows_support.py create mode 100644 venv/pyvenv.cfg diff --git a/.DS_Store b/.DS_Store new file mode 100644 index 0000000000000000000000000000000000000000..5463aa84c4f00c96c65579bc69e88b256e26e9bc GIT binary patch literal 8196 zcmeHMU279T6ur~NW<#t*Q0R-W;A^Ncm16NFO%tI|Kj?}+sH~grwh3-`Lb7QdLLkrj zAN&ix`b+#TKIyqL)3Dj3PX#GD6K2lN%(**v&RlkL6Cx7LQTUi>i-;Ul#!4B@io)|; zm&%$MxeFAqr^8sDbYj^-zp})(16Bd6fK|XMU=^?m+yn*i&gSA=@!r?7wzUda1^!C~ z`1#x_qe zljr~F^yA(zt<`>twe`ZSjiOU@%FaixE5}|Mq@!Uo7`*4+8!6+?<#o_`+l$72<=%lz z(x8__17*+)BTzoP>m{KaH{~b^hsxH|6Hdt~`IW87q+Z*tx;wj1r&V{dSFcswCp){- zY00_&@X@nl|8snv$S-=U!aOc$Q}^fXS6t2abKwn=SSDu}V}6Nxlt7P&26PH#PAJML z^Bt_LGDSL~Lpq=~y(Ev?i)`^A$KIn?)W_Wu@?j&PKCgF%-YI$m;0v{CSB*#LAAucU zCYdvz(}-exeD!JR^P|_sXP@d+L#@*`Rto7f=MnEd>;ER!)gGN>{Mn`xVxXp3-6t3j{BQd(L z|6H6pN}Ci)_M!apS#jKD^(iw4>=Y3pX^d!EWyP_PwJL(oF)CM`uHbmcvK_Doh(?ZR z71fJprlKLszis#leACf?uFiW!T!=fFxUlbk2ycs3;2H|7Yuhc(|Jzsp{(p@Xtf^JN zDsa;k5QUc4YQoFQFUxs{IoEbkKcRAA-b|x1L8H=fpi0Mq^FIu6?SjfS4TCd{7(uZ= O1SlD7V-@(T3j6^QQb-U0 literal 0 HcmV?d00001 diff --git a/data/.DS_Store b/data/.DS_Store new file mode 100644 index 0000000000000000000000000000000000000000..60f47f4adfed8608079a77196db6171e8a0c1cef GIT binary patch literal 6148 zcmeHK%Sr=55Ukc5f_hjyc@gFd0{+1o;=!{Y5VMIGa3#W`;BEiN@AIV9-J`eXZ$=%WI?Fwfr1SObm+yd@gkGgi%{8VqBc_hKC2j{dhyHEzZ^a@yk* zHcLaT8?Jf987r2}xBS;0pUtpPAQT7%LV7iqkp+G1Q3VbS{`$J+@EHk!_ zwso+uCje2e(PpgcRarSHW0|paXUyF{^EY^z%6^o}2pPOQJcqY_DMPfA z6;1P@VSSH=X_6Q1_771xsNT4F=o~tA=YxMKr+yI@Y>; zy^vWECfQ)52uVCZ%KNuT7RzZ@jl8z=0v^G0ta~li zaZR~z$Xr(L4oPGUD1+*K#4#m^FSc>TgEj75Fh}6@!O!sv7EiFHXTc*`?zu+ibcQ$C zqgTKdT~WOHtm5Yyqe>I?q?6UW3M;UD=SAM0y#K{`*4{XVy*Fn5|4w~xMq~$K6|f5Y zEd_XeaN&%e!BV4II#8%10I-T`Wr+D>fjO>0&tRz$Eij>}Kus0qh#@o`^{&bD43-)- zorF1j2-CALClsN2$NgQEPQue@ORIoYU{!$wec9ms|7`d9f0bmftO8bny;49_dw#Er zA(_2(V{p9Jy6}&1HqI+GDhdj-9jk!1;v={+w7DDrJ%go2)WGbAfRe!$R)K%2z;CA> B-dO+u literal 0 HcmV?d00001 diff --git a/server/.gitignore b/server/.gitignore index fe40627..06909b3 100644 --- a/server/.gitignore +++ b/server/.gitignore @@ -1,4 +1,3 @@ -/database/data_options.db /outputs /__pycache__ /venv \ No newline at end of file diff --git a/transformers b/transformers new file mode 160000 index 0000000..061580c --- /dev/null +++ b/transformers @@ -0,0 +1 @@ +Subproject commit 061580c82c2db1de9139528243e105953793f7a2 diff --git a/venv/bin/easy_install b/venv/bin/easy_install new file mode 100755 index 0000000..00d940b --- /dev/null +++ b/venv/bin/easy_install @@ -0,0 +1,8 @@ +#!/Users/taylorlynncurtis/Desktop/RA/OpenCoding/OpenCodingForMachineLearning/venv/bin/python3 +# -*- coding: utf-8 -*- +import re +import sys +from setuptools.command.easy_install import main +if __name__ == '__main__': + sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0]) + sys.exit(main()) diff --git a/venv/bin/easy_install-3.8 b/venv/bin/easy_install-3.8 new file mode 100755 index 0000000..00d940b --- /dev/null +++ b/venv/bin/easy_install-3.8 @@ -0,0 +1,8 @@ +#!/Users/taylorlynncurtis/Desktop/RA/OpenCoding/OpenCodingForMachineLearning/venv/bin/python3 +# -*- coding: utf-8 -*- +import re +import sys +from setuptools.command.easy_install import main +if __name__ == '__main__': + sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0]) + sys.exit(main()) diff --git a/venv/bin/python b/venv/bin/python new file mode 120000 index 0000000..b8a0adb --- /dev/null +++ b/venv/bin/python @@ -0,0 +1 @@ +python3 \ No newline at end of file diff --git a/venv/bin/python3 b/venv/bin/python3 new file mode 120000 index 0000000..e283c4c --- /dev/null +++ b/venv/bin/python3 @@ -0,0 +1 @@ +/Users/taylorlynncurtis/opt/anaconda3/bin/python3 \ No newline at end of file diff --git a/venv/lib/python3.8/site-packages/__pycache__/easy_install.cpython-38.pyc b/venv/lib/python3.8/site-packages/__pycache__/easy_install.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..cd992ab09580468ed28ae550d399c47d64b729d3 GIT binary patch literal 333 zcmYj~y-ve06ou_1K!oTMARvM%L*h_DRV0L17+4tiS+ZDeu1llZj%6pQnt_FlH{q4C zGVuybIEA4{y65OfSKoOVjrNh#+0h5Qx%?W+wmesE-Pu!TfO5n!;H77d!2@4-($9U2 zxc`nYg5U|oJKf=9qoR7jqdR8T_sZ5xh^VNfWD5F_Qprk1>9@n7827 int + """This is an internal API only meant for use by pip's own console scripts. + + For additional details, see https://github.com/pypa/pip/issues/7498. + """ + from pip._internal.utils.entrypoints import _wrapper + + return _wrapper(args) diff --git a/venv/lib/python3.8/site-packages/pip/__main__.py b/venv/lib/python3.8/site-packages/pip/__main__.py new file mode 100644 index 0000000..7c2505f --- /dev/null +++ b/venv/lib/python3.8/site-packages/pip/__main__.py @@ -0,0 +1,26 @@ +from __future__ import absolute_import + +import os +import sys + +# Remove '' and current working directory from the first entry +# of sys.path, if present to avoid using current directory +# in pip commands check, freeze, install, list and show, +# when invoked as python -m pip +if sys.path[0] in ('', os.getcwd()): + sys.path.pop(0) + +# 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.cli.main import main as _main # isort:skip # noqa + +if __name__ == '__main__': + sys.exit(_main()) diff --git a/venv/lib/python3.8/site-packages/pip/_internal/__init__.py b/venv/lib/python3.8/site-packages/pip/_internal/__init__.py new file mode 100644 index 0000000..264c2ca --- /dev/null +++ b/venv/lib/python3.8/site-packages/pip/_internal/__init__.py @@ -0,0 +1,17 @@ +import pip._internal.utils.inject_securetransport # noqa +from pip._internal.utils.typing import MYPY_CHECK_RUNNING + +if MYPY_CHECK_RUNNING: + from typing import Optional, List + + +def main(args=None): + # type: (Optional[List[str]]) -> int + """This is preserved for old console scripts that may still be referencing + it. + + For additional details, see https://github.com/pypa/pip/issues/7498. + """ + from pip._internal.utils.entrypoints import _wrapper + + return _wrapper(args) diff --git a/venv/lib/python3.8/site-packages/pip/_internal/__pycache__/__init__.cpython-38.pyc b/venv/lib/python3.8/site-packages/pip/_internal/__pycache__/__init__.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..f37e1b6e33de91a3622b72f9b467e52d94a7a21a GIT binary patch literal 730 zcmZuvO>Yx15VdzVX^2`0Ax=mLA2<+1)>SE`K?os5dK z2~PY7?);^_a^l3D6XO(BJ#^$5&sZ~__r^Zl+UgRNySKl~Ka`MPA-F6t2G3F59vVp` zEy$SS9u-j;k7G(8w)ZfOi==FgTV;FPrbM=7B3s|+ctg_9q_^=KYssibn;Y+sJ|1Pm z*Dr=|vrmVk(VNk$Uet6x7_TZViY9rdoX-)*g+%BD9iqCMXe7nnq@-UcC1>=Ed<`Aq z(;o+OoOGO z)9%`NiB||I)j2#S!3$Nm2h0Jmx%b9B;e4k2d@)J$s^q3Nf*WPHa&7^RKi(ber|XMs z;DiI{WGgF-0gJTnU}l;r?ZG1Vbb)AG3fkM+RCwxaE7U|yBJAuu_(^OB&;Dv|tXc{W zd?_rSB1y20o1E{h%yM@}4|cqidlR?6JuOemN84+eX?_sE^%vTR`~+lwH3umAC!#*C z1`q2s@C!IRLs$7MOHJJ*rBFI_b_p7?u^HL$YkG9l>ooVTqM`IL B$khM< literal 0 HcmV?d00001 diff --git a/venv/lib/python3.8/site-packages/pip/_internal/__pycache__/build_env.cpython-38.pyc b/venv/lib/python3.8/site-packages/pip/_internal/__pycache__/build_env.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..c94889723b12f9d9d628991a4fa06b80e8e0f7e5 GIT binary patch literal 7443 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 literal 0 HcmV?d00001 diff --git a/venv/lib/python3.8/site-packages/pip/_internal/__pycache__/cache.cpython-38.pyc b/venv/lib/python3.8/site-packages/pip/_internal/__pycache__/cache.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..f796803a4c5144f89bcbb388ab09d70b7251df32 GIT binary patch literal 9142 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 literal 0 HcmV?d00001 diff --git a/venv/lib/python3.8/site-packages/pip/_internal/__pycache__/locations.cpython-38.pyc b/venv/lib/python3.8/site-packages/pip/_internal/__pycache__/locations.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..a2ea6d53b098be2c7a91f9306e1221a3241f7a22 GIT binary patch literal 4544 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 literal 0 HcmV?d00001 diff --git a/venv/lib/python3.8/site-packages/pip/_internal/__pycache__/main.cpython-38.pyc b/venv/lib/python3.8/site-packages/pip/_internal/__pycache__/main.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..62cd28bc4be9b106a670522ac0db5f78f4e0aaba GIT binary patch literal 667 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 literal 0 HcmV?d00001 diff --git a/venv/lib/python3.8/site-packages/pip/_internal/__pycache__/wheel_builder.cpython-38.pyc b/venv/lib/python3.8/site-packages/pip/_internal/__pycache__/wheel_builder.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..49ec5c0c145eb9e55194d6c20798357ef642b3b9 GIT binary patch literal 6852 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 a$u9p`D!B`8(Ji@`-AOm^o^@}!lm7#XtNwBT literal 0 HcmV?d00001 diff --git a/venv/lib/python3.8/site-packages/pip/_internal/build_env.py b/venv/lib/python3.8/site-packages/pip/_internal/build_env.py new file mode 100644 index 0000000..b8f005f --- /dev/null +++ b/venv/lib/python3.8/site-packages/pip/_internal/build_env.py @@ -0,0 +1,219 @@ +"""Build Environment used for isolation during sdist building +""" + +# The following comment should be removed at some point in the future. +# mypy: strict-optional=False +# mypy: disallow-untyped-defs=False + +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.cli.spinners import open_spinner +from pip._internal.utils.subprocess import call_subprocess +from pip._internal.utils.temp_dir import TempDirectory, tempdir_kinds +from pip._internal.utils.typing import MYPY_CHECK_RUNNING + +if MYPY_CHECK_RUNNING: + from typing import Tuple, Set, Iterable, Optional, List + from pip._internal.index.package_finder 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 + temp_dir = TempDirectory( + kind=tempdir_kinds.BUILD_ENV, globally_managed=True + ) + + self._prefixes = OrderedDict(( + (name, _Prefix(os.path.join(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(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 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:'})))) + + index_urls = finder.index_urls + if index_urls: + args.extend(['-i', index_urls[0]]) + for extra_index in 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.trusted_hosts: + 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/venv/lib/python3.8/site-packages/pip/_internal/cache.py b/venv/lib/python3.8/site-packages/pip/_internal/cache.py new file mode 100644 index 0000000..b534f0c --- /dev/null +++ b/venv/lib/python3.8/site-packages/pip/_internal/cache.py @@ -0,0 +1,349 @@ +"""Cache Management +""" + +# The following comment should be removed at some point in the future. +# mypy: strict-optional=False + +import hashlib +import json +import logging +import os + +from pip._vendor.packaging.tags import interpreter_name, interpreter_version +from pip._vendor.packaging.utils import canonicalize_name + +from pip._internal.exceptions import InvalidWheelFilename +from pip._internal.models.link import Link +from pip._internal.models.wheel import Wheel +from pip._internal.utils.temp_dir import TempDirectory, tempdir_kinds +from pip._internal.utils.typing import MYPY_CHECK_RUNNING +from pip._internal.utils.urls import path_to_url + +if MYPY_CHECK_RUNNING: + from typing import Optional, Set, List, Any, Dict + + from pip._vendor.packaging.tags import Tag + + from pip._internal.models.format_control import FormatControl + +logger = logging.getLogger(__name__) + + +def _hash_dict(d): + # type: (Dict[str, str]) -> str + """Return a stable sha224 of a dictionary.""" + s = json.dumps(d, sort_keys=True, separators=(",", ":"), ensure_ascii=True) + return hashlib.sha224(s.encode("ascii")).hexdigest() + + +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__() + assert not cache_dir or os.path.isabs(cache_dir) + self.cache_dir = cache_dir or 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_legacy(self, link): + # type: (Link) -> List[str] + """Get parts of part that must be os.path.joined with cache_dir + + Legacy cache key (pip < 20) for compatibility with older caches. + """ + + # 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_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 = {"url": link.url_without_fragment} + if link.hash_name is not None and link.hash is not None: + key_parts[link.hash_name] = link.hash + if link.subdirectory_fragment: + key_parts["subdirectory"] = link.subdirectory_fragment + + # Include interpreter name, major and minor version in cache key + # to cope with ill-behaved sdists that build a different wheel + # depending on the python version their setup.py is being run on, + # and don't encode the difference in compatibility tags. + # https://github.com/pypa/pip/issues/7296 + key_parts["interpreter_name"] = interpreter_name() + key_parts["interpreter_version"] = interpreter_version() + + # 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 = _hash_dict(key_parts) + + # 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, canonical_package_name): + # type: (Link, Optional[str]) -> List[Any] + can_not_cache = ( + not self.cache_dir or + not canonical_package_name or + not link + ) + if can_not_cache: + return [] + + formats = self.format_control.get_allowed_formats( + canonical_package_name + ) + if not self.allowed_formats.intersection(formats): + return [] + + candidates = [] + path = self.get_path_for_link(link) + if os.path.isdir(path): + for candidate in os.listdir(path): + candidates.append((candidate, path)) + # TODO remove legacy path lookup in pip>=21 + legacy_path = self.get_path_for_link_legacy(link) + if os.path.isdir(legacy_path): + for candidate in os.listdir(legacy_path): + candidates.append((candidate, legacy_path)) + return candidates + + def get_path_for_link_legacy(self, link): + # type: (Link) -> str + raise NotImplementedError() + + 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, # type: Link + package_name, # type: Optional[str] + supported_tags, # type: List[Tag] + ): + # type: (...) -> Link + """Returns a link to a cached item if it exists, otherwise returns the + passed link. + """ + raise NotImplementedError() + + +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_legacy(self, link): + # type: (Link) -> str + parts = self._get_cache_path_parts_legacy(link) + return os.path.join(self.cache_dir, "wheels", *parts) + + 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, # type: Link + package_name, # type: Optional[str] + supported_tags, # type: List[Tag] + ): + # type: (...) -> Link + candidates = [] + + if not package_name: + return link + + canonical_package_name = canonicalize_name(package_name) + for wheel_name, wheel_dir in self._get_candidates( + link, canonical_package_name + ): + try: + wheel = Wheel(wheel_name) + except InvalidWheelFilename: + continue + if canonicalize_name(wheel.name) != canonical_package_name: + logger.debug( + "Ignoring cached wheel {} for {} as it " + "does not match the expected distribution name {}.".format( + wheel_name, link, package_name + ) + ) + continue + if not wheel.supported(supported_tags): + # Built for a different python/arch/etc + continue + candidates.append( + ( + wheel.support_index_min(supported_tags), + wheel_name, + wheel_dir, + ) + ) + + if not candidates: + return link + + _, wheel_name, wheel_dir = min(candidates) + return Link(path_to_url(os.path.join(wheel_dir, wheel_name))) + + +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=tempdir_kinds.EPHEM_WHEEL_CACHE, + globally_managed=True, + ) + + super(EphemWheelCache, self).__init__( + self._temp_dir.path, format_control + ) + + +class CacheEntry(object): + def __init__( + self, + link, # type: Link + persistent, # type: bool + ): + self.link = link + self.persistent = persistent + + +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_legacy(self, link): + # type: (Link) -> str + return self._wheel_cache.get_path_for_link_legacy(link) + + 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, # type: Link + package_name, # type: Optional[str] + supported_tags, # type: List[Tag] + ): + # type: (...) -> Link + cache_entry = self.get_cache_entry(link, package_name, supported_tags) + if cache_entry is None: + return link + return cache_entry.link + + def get_cache_entry( + self, + link, # type: Link + package_name, # type: Optional[str] + supported_tags, # type: List[Tag] + ): + # type: (...) -> Optional[CacheEntry] + """Returns a CacheEntry with a link to a cached item if it exists or + None. The cache entry indicates if the item was found in the persistent + or ephemeral cache. + """ + retval = self._wheel_cache.get( + link=link, + package_name=package_name, + supported_tags=supported_tags, + ) + if retval is not link: + return CacheEntry(retval, persistent=True) + + retval = self._ephem_cache.get( + link=link, + package_name=package_name, + supported_tags=supported_tags, + ) + if retval is not link: + return CacheEntry(retval, persistent=False) + + return None diff --git a/venv/lib/python3.8/site-packages/pip/_internal/cli/__init__.py b/venv/lib/python3.8/site-packages/pip/_internal/cli/__init__.py new file mode 100644 index 0000000..e589bb9 --- /dev/null +++ b/venv/lib/python3.8/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/venv/lib/python3.8/site-packages/pip/_internal/cli/__pycache__/__init__.cpython-38.pyc b/venv/lib/python3.8/site-packages/pip/_internal/cli/__pycache__/__init__.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..cab5908cebb8802939f2ad554a9c0502000f0c8d GIT binary patch literal 288 zcmYjMJxc>Y5Zz0Y5Xesuwh$!6xgo}2K*Y|@Cfe96o1M9H&3>`>5iYfr|HQwP)}LT! zmAR{Y@Md^31Mki2Y&Klb?i=)dl#Uw_|Sj-OJrwLh*Wj} literal 0 HcmV?d00001 diff --git a/venv/lib/python3.8/site-packages/pip/_internal/cli/__pycache__/autocompletion.cpython-38.pyc b/venv/lib/python3.8/site-packages/pip/_internal/cli/__pycache__/autocompletion.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..a7d566b37b79155d0d42ec69fadb63cb87be2d7e GIT binary patch literal 5005 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 literal 0 HcmV?d00001 diff --git a/venv/lib/python3.8/site-packages/pip/_internal/cli/__pycache__/base_command.cpython-38.pyc b/venv/lib/python3.8/site-packages/pip/_internal/cli/__pycache__/base_command.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..6e7dad1c2ee70ef5e55c6b08eea3d94b5764d9e5 GIT binary patch literal 5812 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 H^lj&FYne_C literal 0 HcmV?d00001 diff --git a/venv/lib/python3.8/site-packages/pip/_internal/cli/__pycache__/main.cpython-38.pyc b/venv/lib/python3.8/site-packages/pip/_internal/cli/__pycache__/main.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..1f452e021ea879f05a0288a89f1b444b4a09d16b GIT binary patch literal 1468 zcmZuw-EUhp6t{gpa+4-$mVWF5I^|(~$U~Dd*t8*pKve}3A*%}0)+?l|+t>}a_5G0T zv`a`Pq`?b<{~?ih;s4;T@DmbG`xkiP*eP2F*z)o5Irj0-zvJ_yR;wUrceZ|_4?z3V zEUq>O7N5Y7zk;ABL=;oPaY{mhjqY$KbnNVguARNmv$G%iFuS~z24P_Ldc2%g!iv>> zzLHkMs?|%pme#|%)dRkoHo}J0%lul}44W9SruZ3RRrp%E9Qr9ATTmP8)%aNEe56>E zq(v@NA8gflr1C+Y7Mv*t>sk>D$s(|%aYn&V9|#u1kvEp$ASw2R$OU*;C@Tb8-U-X* zmq!Oj(cb6#dtXF{Px}2w{m zih%_5Be{uYNRhd-Ko}|aCCEubc~kTfe?w+?MxLUq=OX2wRmArg%^dJ|;B;voP%9KN@DaNU~vz8D?ACvUTw-%UY5tWvo0O#GFkkJ39%a5b|MC6TH?pv?Iq- zyRjIaixNcFn@J{B%z1PXiwq(}JRc4rw)x<~HNr5jeW@r|wRa&B#k7Bti!@f6HE(4@X)$i>^pZ0p%v%kUYFjr`gvg6TEcqYD^#!1%pEi$QtK}w@6 zPT5iev{F0l`9Nc)OP3$)Hze-B$-lC0A(C@=Y4?61^5^1##A?^7J%ry2GIc!ohwBMC1R7LxBk1$V2x~n3Y$Z{`L8|8 zs+CLbZ@ldUo|&ycX;UArz3m>Ugv;gMx%EGujv5!{8G(6$f4L}|H<{|ywmA}fl;(8A*~g|DnyOz*T4~#Br}N&t8rPGBbkSQx8d;$Q4fBj~It)2XxIu(5DwjVZ zZW_dyTLhF5>O2SMwCxTOPMG4n;4x@+bg{IRXZ<+(QVH&;rQ*W<0teFKHPR1;31>G+ zQryUC8gNc1D7C^oO<_0Y<2dAEIU?L=aT+H9^^c}tr{-3*>^h~nRGXLUin#Fi2xYLp zs4D@@BUrrj#e+K!{H>eUw?6m3y1%`BYx^@$vF;6vgb4F?%(z$zDG507rCWaGmT0N> z(XOF%@~D{5v7v53WgHrrPp%zm*x(3S^JiHL6$6-b+8>FT*LLJ8lRgqr`k2`5S`Y>25wARr7;C-e!E1FH|OBW zmO3_5`JsdlX~s^bMZrZr^fTCUI4TBoEU0&(ve%%AARG@@zR|~M ztYdAU^)(r+XXr=$TNB(t2ksMT?Zfr=$L^Ea$^1C$=Up}gP|(pB(q>nC3p>ffAYmOt z=-oAX3e1bsy8X|=<^LnCyHWYF37sP)ft=1fm0GkR8yQS-7RM~uNl3LbE5H+i z{eVV{*5!O{hJGsV9kG}OyJVI!Kce}t5Uq%0ga*lMxF8c-<#Z-7r5jXcO;|A}DHG-( zk25ic2@7egsN69{qoRnvyD#h+TYzwxw8iLU&{napn)7nlg60fVb`cuQ){%}(S^u)@ z4yx;Qqm3NHHXPmlyKXl1mvAIqlP6MS`yD!xXo^&*)TRjVq>qhKALw+G8v}C!BpN6> zRU;r;O_GZnle#2fX;7~;FgmMYeOIRg-YAVjxQ1mPsOTu7zE~^Qjm^^JbGtSA8*i58 zAsSoQ1S*JAG#?CGG0jych&7I zsR#?Fy0R1^UY9lvHe$kN9OpwWtbGbO6?)D@Er~P2NFhw}IOf7gNCwIj5Uq%=5J2S( zM^~g7J17d0VOo=tOG-jXn(vWLU6^XOx1|tn;R;@}%S1HfoKF%)L>;~jzPc=|Xh;|n zM#`c}stic79s^7ph95y?5>1+JH`<1c+VIqkS5X~W01W`A2{2mF+eQ;PdgqL&`997= z-=}M!L|38l7J;hW7gX&6RV#L!`JcR7Az4vPF;A?VSTp-H{TCf^!U}mDlIr`XLUnR_ z$m4`{(-^2EL9LiA&b_=zwT6!h_()RucOmTA0Ddqu+LRJ-Ge{C}Q__mcP#xo&3Zx{R hsBfe>9wuZ{Vv0$*X;eX5Fc(l0LDeCA1_bkse*q_uc-a5| literal 0 HcmV?d00001 diff --git a/venv/lib/python3.8/site-packages/pip/_internal/cli/__pycache__/parser.cpython-38.pyc b/venv/lib/python3.8/site-packages/pip/_internal/cli/__pycache__/parser.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..238bf3fde6501fac10f49c6835120d181c85dbc2 GIT binary patch literal 9018 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 l1KQ None + """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 = '' + + parser = create_main_parser() + subcommands = list(commands_dict) + options = [] + + # subcommand + subcommand_name = None # type: Optional[str] + for word in cwords: + if word in subcommands: + subcommand_name = word + break + # subcommand options + if subcommand_name is not None: + # 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 = create_command(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: + paths = auto_complete_paths(current, completion_type) + options = [(path, 0) for path in paths] + 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) + flattened_opts = chain.from_iterable(opts) + if current.startswith('-'): + for opt in flattened_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, + flattened_opts) + if completion_type: + subcommands = list(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): + # type: (List[str], int, Iterable[Any]) -> Optional[str] + """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 None + 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 + return None + + +def auto_complete_paths(current, completion_type): + # type: (str, str) -> Iterable[str] + """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/venv/lib/python3.8/site-packages/pip/_internal/cli/base_command.py b/venv/lib/python3.8/site-packages/pip/_internal/cli/base_command.py new file mode 100644 index 0000000..1fa5ba0 --- /dev/null +++ b/venv/lib/python3.8/site-packages/pip/_internal/cli/base_command.py @@ -0,0 +1,228 @@ +"""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.command_context import CommandContextMixIn +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.exceptions import ( + BadCommand, + CommandError, + InstallationError, + PreviousBuildDirError, + UninstallationError, +) +from pip._internal.utils.deprecation import deprecated +from pip._internal.utils.filesystem import check_path_owner +from pip._internal.utils.logging import BrokenStdoutLoggingError, setup_logging +from pip._internal.utils.misc import get_prog, normalize_path +from pip._internal.utils.temp_dir import ( + global_tempdir_manager, + tempdir_registry, +) +from pip._internal.utils.typing import MYPY_CHECK_RUNNING +from pip._internal.utils.virtualenv import running_under_virtualenv + +if MYPY_CHECK_RUNNING: + from typing import List, Optional, Tuple, Any + from optparse import Values + + from pip._internal.utils.temp_dir import ( + TempDirectoryTypeRegistry as TempDirRegistry + ) + +__all__ = ['Command'] + +logger = logging.getLogger(__name__) + + +class Command(CommandContextMixIn): + usage = None # type: str + ignore_require_venv = False # type: bool + + def __init__(self, name, summary, isolated=False): + # type: (str, str, bool) -> None + super(Command, self).__init__() + parser_kw = { + 'usage': self.usage, + 'prog': '{} {}'.format(get_prog(), name), + 'formatter': UpdatingDefaultsHelpFormatter(), + 'add_help_option': False, + 'name': name, + 'description': self.__doc__, + 'isolated': isolated, + } + + self.name = name + self.summary = summary + self.parser = ConfigOptionParser(**parser_kw) + + self.tempdir_registry = None # type: Optional[TempDirRegistry] + + # Commands should add options to this option group + optgroup_name = '{} Options'.format(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 handle_pip_version_check(self, options): + # type: (Values) -> None + """ + This is a no-op so that commands by default do not do the pip version + check. + """ + # Make sure we do the pip version check if the index_group options + # are present. + assert not hasattr(options, 'no_index') + + def run(self, options, args): + # type: (Values, List[Any]) -> Any + raise NotImplementedError + + def parse_args(self, args): + # type: (List[str]) -> Tuple[Any, Any] + # factored out for testability + return self.parser.parse_args(args) + + def main(self, args): + # type: (List[str]) -> int + try: + with self.main_context(): + return self._main(args) + finally: + logging.shutdown() + + def _main(self, args): + # type: (List[str]) -> int + # We must initialize this before the tempdir manager, otherwise the + # configuration would not be accessible by the time we clean up the + # tempdir manager. + self.tempdir_registry = self.enter_context(tempdir_registry()) + # Intentionally set as early as possible so globally-managed temporary + # directories are available to the rest of the code. + self.enter_context(global_tempdir_manager()) + + 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] == (2, 7) and + not options.no_python_version_warning + ): + message = ( + "pip 21.0 will drop support for Python 2.7 in January 2021. " + "More details about Python 2 support in pip, can be found at " + "https://pip.pypa.io/en/latest/development/release-process/#python-2-support" # noqa + ) + if platform.python_implementation() == "CPython": + message = ( + "Python 2.7 reached the end of its life on January " + "1st, 2020. Please upgrade your Python as Python 2.7 " + "is no longer maintained. " + ) + 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) + + if options.cache_dir: + options.cache_dir = normalize_path(options.cache_dir) + if not check_path_owner(options.cache_dir): + logger.warning( + "The directory '%s' or its parent directory is not owned " + "or is not writable by the current user. The cache " + "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 + + 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: + self.handle_pip_version_check(options) + + return SUCCESS diff --git a/venv/lib/python3.8/site-packages/pip/_internal/cli/cmdoptions.py b/venv/lib/python3.8/site-packages/pip/_internal/cli/cmdoptions.py new file mode 100644 index 0000000..5b5647d --- /dev/null +++ b/venv/lib/python3.8/site-packages/pip/_internal/cli/cmdoptions.py @@ -0,0 +1,962 @@ +""" +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. +""" + +# The following comment should be removed at some point in the future. +# mypy: strict-optional=False + +from __future__ import absolute_import + +import logging +import os +import textwrap +import warnings +from distutils.util import strtobool +from functools import partial +from optparse import SUPPRESS_HELP, Option, OptionGroup +from textwrap import dedent + +from pip._internal.cli.progress_bars import BAR_TYPES +from pip._internal.exceptions import CommandError +from pip._internal.locations import USER_CACHE_DIR, get_src_prefix +from pip._internal.models.format_control import FormatControl +from pip._internal.models.index import PyPI +from pip._internal.models.target_python import TargetPython +from pip._internal.utils.hashes import STRONG_HASHES +from pip._internal.utils.typing import MYPY_CHECK_RUNNING + +if MYPY_CHECK_RUNNING: + from typing import Any, Callable, Dict, Optional, Tuple + from optparse import OptionParser, Values + from pip._internal.cli.parser import ConfigOptionParser + +logger = logging.getLogger(__name__) + + +def raise_option_error(parser, option, msg): + # type: (OptionParser, Option, str) -> None + """ + 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): + # type: (str) -> Optional[Any] + 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-option ' + '/ --global-option / --install-option.', 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'" + ) + + +def _path_option_check(option, opt, value): + # type: (Option, str, str) -> str + return os.path.expanduser(value) + + +class PipOption(Option): + TYPES = Option.TYPES + ("path",) + TYPE_CHECKER = Option.TYPE_CHECKER.copy() + TYPE_CHECKER["path"] = _path_option_check + + +########### +# 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( + PipOption, + "--log", "--log-file", "--local-log", + dest="log", + metavar="path", + type="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] + + +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( + PipOption, + '--cert', + dest='cert', + type='path', + metavar='path', + help="Path to alternate CA bundle.", +) # type: Callable[..., Option] + +client_cert = partial( + PipOption, + '--client-cert', + dest='client_cert', + type='path', + 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 the 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(): + # type: () -> Option + 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 such as sdist (.tar.gz) or wheel (.whl) files. " + "If a local path or file:// URL that's a directory, " + "then look for archives in the directory listing. " + "Links to VCS project URLs are not supported.", + ) + + +def trusted_host(): + # type: () -> Option + return Option( + "--trusted-host", + dest="trusted_hosts", + action="append", + metavar="HOSTNAME", + default=[], + help="Mark this host or host:port pair 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.'), + ) + + +def _handle_src(option, opt_str, value, parser): + # type: (Option, str, str, OptionParser) -> None + value = os.path.abspath(value) + setattr(parser.values, option.dest, value) + + +src = partial( + PipOption, + '--src', '--source', '--source-dir', '--source-directory', + dest='src_dir', + type='path', + metavar='dir', + default=get_src_prefix(), + action='callback', + callback=_handle_src, + 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 (notice ' + 'the colons), or one or more package names with commas between ' + 'them (no colons). 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] + + +# This was made a separate function for unit-testing purposes. +def _convert_python_version(value): + # type: (str) -> Tuple[Tuple[int, ...], Optional[str]] + """ + Convert a version string like "3", "37", or "3.7.3" into a tuple of ints. + + :return: A 2-tuple (version_info, error_msg), where `error_msg` is + non-None if and only if there was a parsing error. + """ + if not value: + # The empty string is the same as not providing a value. + return (None, None) + + parts = value.split('.') + if len(parts) > 3: + return ((), 'at most three version parts are allowed') + + if len(parts) == 1: + # Then we are in the case of "3" or "37". + value = parts[0] + if len(value) > 1: + parts = [value[0], value[1:]] + + try: + version_info = tuple(int(part) for part in parts) + except ValueError: + return ((), 'each version part must be an integer') + + return (version_info, None) + + +def _handle_python_version(option, opt_str, value, parser): + # type: (Option, str, str, OptionParser) -> None + """ + Handle a provided --python-version value. + """ + version_info, error_msg = _convert_python_version(value) + if error_msg is not None: + msg = ( + 'invalid --python-version value: {!r}: {}'.format( + value, error_msg, + ) + ) + raise_option_error(parser, option=option, msg=msg) + + parser.values.python_version = version_info + + +python_version = partial( + Option, + '--python-version', + dest='python_version', + metavar='python_version', + action='callback', + callback=_handle_python_version, type='str', + default=None, + help=dedent("""\ + The Python interpreter version to use for wheel and "Requires-Python" + compatibility checks. Defaults to a version derived from the running + interpreter. The version can be specified using up to three dot-separated + integers (e.g. "3" for 3.0.0, "3.7" for 3.7.0, or "3.7.3"). A major-minor + version can also be given as a string without dots (e.g. "37" for 3.7.0). + """), +) # 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 add_target_python_options(cmd_opts): + # type: (OptionGroup) -> None + cmd_opts.add_option(platform()) + cmd_opts.add_option(python_version()) + cmd_opts.add_option(implementation()) + cmd_opts.add_option(abi()) + + +def make_target_python(options): + # type: (Values) -> TargetPython + target_python = TargetPython( + platform=options.platform, + py_version_info=options.python_version, + abi=options.abi, + implementation=options.implementation, + ) + + return target_python + + +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( + PipOption, + "--cache-dir", + dest="cache_dir", + default=USER_CACHE_DIR, + metavar="dir", + type='path', + help="Store the cache data in ." +) # type: Callable[..., Option] + + +def _handle_no_cache_dir(option, opt, value, parser): + # type: (Option, str, str, OptionParser) -> None + """ + 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=_handle_no_cache_dir, + 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] + + +def _handle_build_dir(option, opt, value, parser): + # type: (Option, str, str, OptionParser) -> None + if value: + value = os.path.abspath(value) + setattr(parser.values, option.dest, value) + + +build_dir = partial( + PipOption, + '-b', '--build', '--build-dir', '--build-directory', + dest='build_dir', + type='path', + metavar='dir', + action='callback', + callback=_handle_build_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 _handle_no_use_pep517(option, opt, value, parser): + # type: (Option, str, str, OptionParser) -> None + """ + 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=_handle_no_use_pep517, + 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 _handle_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 {} must be a hash name ' + 'followed by a value, like --hash=sha256:' + 'abcde...'.format(opt_str)) + if algo not in STRONG_HASHES: + parser.error('Allowed hash algorithms for {} are {}.'.format( + 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=_handle_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] + + +list_path = partial( + PipOption, + '--path', + dest='path', + type='path', + action='append', + help='Restrict to the specified installation path for listing ' + 'packages (can be used multiple times).' +) # type: Callable[..., Option] + + +def check_list_path_option(options): + # type: (Values) -> None + if options.path and (options.user or options.local): + raise CommandError( + "Cannot combine '--path' with '--user' or '--local'" + ) + + +no_python_version_warning = partial( + Option, + '--no-python-version-warning', + dest='no_python_version_warning', + action='store_true', + default=False, + help='Silence deprecation warnings for upcoming unsupported Pythons.', +) # type: Callable[..., Option] + + +unstable_feature = partial( + Option, + '--unstable-feature', + dest='unstable_features', + metavar='feature', + action='append', + default=[], + choices=['resolver'], + help=SUPPRESS_HELP, # TODO: Enable this when the resolver actually works. + # help='Enable unstable feature(s) that may be backward incompatible.', +) # type: Callable[..., Option] + + +########## +# groups # +########## + +general_group = { + 'name': 'General Options', + 'options': [ + help_, + isolated_mode, + require_virtualenv, + verbose, + version, + quiet, + log, + no_input, + proxy, + retries, + timeout, + exists_action, + trusted_host, + cert, + client_cert, + cache_dir, + no_cache, + disable_pip_version_check, + no_color, + no_python_version_warning, + unstable_feature, + ] +} # 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/venv/lib/python3.8/site-packages/pip/_internal/cli/command_context.py b/venv/lib/python3.8/site-packages/pip/_internal/cli/command_context.py new file mode 100644 index 0000000..d1a64a7 --- /dev/null +++ b/venv/lib/python3.8/site-packages/pip/_internal/cli/command_context.py @@ -0,0 +1,36 @@ +from contextlib import contextmanager + +from pip._vendor.contextlib2 import ExitStack + +from pip._internal.utils.typing import MYPY_CHECK_RUNNING + +if MYPY_CHECK_RUNNING: + from typing import Iterator, ContextManager, TypeVar + + _T = TypeVar('_T', covariant=True) + + +class CommandContextMixIn(object): + def __init__(self): + # type: () -> None + super(CommandContextMixIn, self).__init__() + self._in_main_context = False + self._main_context = ExitStack() + + @contextmanager + def main_context(self): + # type: () -> Iterator[None] + assert not self._in_main_context + + self._in_main_context = True + try: + with self._main_context: + yield + finally: + self._in_main_context = False + + def enter_context(self, context_provider): + # type: (ContextManager[_T]) -> _T + assert self._in_main_context + + return self._main_context.enter_context(context_provider) diff --git a/venv/lib/python3.8/site-packages/pip/_internal/cli/main.py b/venv/lib/python3.8/site-packages/pip/_internal/cli/main.py new file mode 100644 index 0000000..172f30d --- /dev/null +++ b/venv/lib/python3.8/site-packages/pip/_internal/cli/main.py @@ -0,0 +1,75 @@ +"""Primary application entrypoint. +""" +from __future__ import absolute_import + +import locale +import logging +import os +import sys + +from pip._internal.cli.autocompletion import autocomplete +from pip._internal.cli.main_parser import parse_command +from pip._internal.commands import create_command +from pip._internal.exceptions import PipError +from pip._internal.utils import deprecation +from pip._internal.utils.typing import MYPY_CHECK_RUNNING + +if MYPY_CHECK_RUNNING: + from typing import List, Optional + +logger = logging.getLogger(__name__) + + +# Do not import and use main() directly! Using it directly is actively +# discouraged by pip's maintainers. The name, location and behavior of +# this function is subject to change, so calling it directly is not +# portable across different pip versions. + +# In addition, running pip in-process is unsupported and unsafe. This is +# elaborated in detail at +# https://pip.pypa.io/en/stable/user_guide/#using-pip-from-your-program. +# That document also provides suggestions that should work for nearly +# all users that are considering importing and using main() directly. + +# However, we know that certain users will still want to invoke pip +# in-process. If you understand and accept the implications of using pip +# in an unsupported manner, the best approach is to use runpy to avoid +# depending on the exact location of this entry point. + +# The following example shows how to use runpy to invoke pip in that +# case: +# +# sys.argv = ["pip", your, args, here] +# runpy.run_module("pip", run_name="__main__") +# +# Note that this will exit the process after running, unlike a direct +# call to main. As it is not safe to do any processing after calling +# main, this should not be an issue in practice. + +def main(args=None): + # type: (Optional[List[str]]) -> int + 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: {}".format(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 = create_command(cmd_name, isolated=("--isolated" in cmd_args)) + + return command.main(cmd_args) diff --git a/venv/lib/python3.8/site-packages/pip/_internal/cli/main_parser.py b/venv/lib/python3.8/site-packages/pip/_internal/cli/main_parser.py new file mode 100644 index 0000000..08c82c1 --- /dev/null +++ b/venv/lib/python3.8/site-packages/pip/_internal/cli/main_parser.py @@ -0,0 +1,99 @@ +"""A single place for constructing and exposing the main parser +""" + +import os +import sys + +from pip._internal.cli import cmdoptions +from pip._internal.cli.parser import ( + ConfigOptionParser, + UpdatingDefaultsHelpFormatter, +) +from pip._internal.commands import commands_dict, get_similar_commands +from pip._internal.exceptions import CommandError +from pip._internal.utils.misc import get_pip_version, 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() + + parser.version = get_pip_version() + + # 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 + description = [''] + [ + '{name:27} {command_info.summary}'.format(**locals()) + for name, command_info in commands_dict.items() + ] + 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 "{}"'.format(cmd_name)] + if guess: + msg.append('maybe you meant "{}"'.format(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/venv/lib/python3.8/site-packages/pip/_internal/cli/parser.py b/venv/lib/python3.8/site-packages/pip/_internal/cli/parser.py new file mode 100644 index 0000000..04e00b7 --- /dev/null +++ b/venv/lib/python3.8/site-packages/pip/_internal/cli/parser.py @@ -0,0 +1,266 @@ +"""Base option parser setup""" + +# The following comment should be removed at some point in the future. +# mypy: disallow-untyped-defs=False + +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) + + def _format_option_strings(self, option, mvarfmt=' <{}>', 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 + :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.format(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: {}\n'.format( + 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 = '{}:\n{}\n'.format(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: {}".format(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, "{}\n".format(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/venv/lib/python3.8/site-packages/pip/_internal/cli/progress_bars.py b/venv/lib/python3.8/site-packages/pip/_internal/cli/progress_bars.py new file mode 100644 index 0000000..7ed2247 --- /dev/null +++ b/venv/lib/python3.8/site-packages/pip/_internal/cli/progress_bars.py @@ -0,0 +1,277 @@ +from __future__ import division + +import itertools +import sys +from signal import SIGINT, default_int_handler, signal + +from pip._vendor import six +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, Dict, List + +try: + from pip._vendor import colorama +# Lots of different errors can come from this, including SystemError and +# ImportError. +except Exception: + colorama = None + + +def _select_progress_class(preferred, fallback): + # type: (Bar, Bar) -> Bar + 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): + # type: (List[Any], Dict[Any, Any]) -> None + """ + Save the original SIGINT handler for later. + """ + super(InterruptibleMixin, self).__init__( # type: ignore + *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): + # type: () -> None + """ + 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() # type: ignore + signal(SIGINT, self.original_handler) + + def handle_sigint(self, signum, frame): # type: ignore + """ + 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): + # type: () -> None + 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): + # type: (List[Any], Dict[Any, Any]) -> None + super(DownloadProgressMixin, self).__init__( # type: ignore + *args, + **kwargs + ) + self.message = (" " * ( + get_indentation() + 2 + )) + self.message # type: str + + @property + def downloaded(self): + # type: () -> str + return format_size(self.index) # type: ignore + + @property + def download_speed(self): + # type: () -> str + # Avoid zero division errors... + if self.avg == 0.0: # type: ignore + return "..." + return format_size(1 / self.avg) + "/s" # type: ignore + + @property + def pretty_eta(self): + # type: () -> str + if self.eta: # type: ignore + return "eta {}".format(self.eta_td) # type: ignore + return "" + + def iter(self, it): # type: ignore + for x in it: + yield x + self.next(len(x)) + self.finish() + + +class WindowsMixin(object): + + def __init__(self, *args, **kwargs): + # type: (List[Any], Dict[Any, Any]) -> None + # 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: # type: ignore + self.hide_cursor = False + + super(WindowsMixin, self).__init__(*args, **kwargs) # type: ignore + + # 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) # type: ignore + # 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 DownloadBar(BaseDownloadProgressBar, # type: ignore + Bar): + 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): # type: ignore + if not hasattr(self, "_phaser"): + self._phaser = itertools.cycle(self.phases) + return next(self._phaser) + + def update(self): + # type: () -> None + 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": (DownloadBar, DownloadProgressSpinner), + "pretty": (DownloadFillingCirclesBar, DownloadProgressSpinner), + "emoji": (DownloadBlueEmojiProgressBar, DownloadProgressSpinner) +} + + +def DownloadProgressProvider(progress_bar, max=None): # type: ignore + if max is None or max == 0: + return BAR_TYPES[progress_bar][1]().iter + else: + return BAR_TYPES[progress_bar][0](max=max).iter diff --git a/venv/lib/python3.8/site-packages/pip/_internal/cli/req_command.py b/venv/lib/python3.8/site-packages/pip/_internal/cli/req_command.py new file mode 100644 index 0000000..104b033 --- /dev/null +++ b/venv/lib/python3.8/site-packages/pip/_internal/cli/req_command.py @@ -0,0 +1,408 @@ +"""Contains the Command base classes that depend on PipSession. + +The classes in this module are in a separate module so the commands not +needing download / PackageFinder capability don't unnecessarily import the +PackageFinder machinery and all its vendored dependencies, etc. +""" + +import logging +import os +from functools import partial + +from pip._internal.cli import cmdoptions +from pip._internal.cli.base_command import Command +from pip._internal.cli.command_context import CommandContextMixIn +from pip._internal.exceptions import CommandError, PreviousBuildDirError +from pip._internal.index.package_finder import PackageFinder +from pip._internal.models.selection_prefs import SelectionPreferences +from pip._internal.network.download import Downloader +from pip._internal.network.session import PipSession +from pip._internal.operations.prepare import RequirementPreparer +from pip._internal.req.constructors import ( + install_req_from_editable, + install_req_from_line, + install_req_from_parsed_requirement, + install_req_from_req_string, +) +from pip._internal.req.req_file import parse_requirements +from pip._internal.req.req_set import RequirementSet +from pip._internal.self_outdated_check import ( + make_link_collector, + pip_self_version_check, +) +from pip._internal.utils.temp_dir import tempdir_kinds +from pip._internal.utils.typing import MYPY_CHECK_RUNNING + +if MYPY_CHECK_RUNNING: + from optparse import Values + from typing import Any, List, Optional, Tuple + + from pip._internal.cache import WheelCache + from pip._internal.models.target_python import TargetPython + from pip._internal.req.req_install import InstallRequirement + from pip._internal.req.req_tracker import RequirementTracker + from pip._internal.resolution.base import BaseResolver + from pip._internal.utils.temp_dir import ( + TempDirectory, + TempDirectoryTypeRegistry, + ) + + +logger = logging.getLogger(__name__) + + +class SessionCommandMixin(CommandContextMixIn): + + """ + A class mixin for command classes needing _build_session(). + """ + def __init__(self): + # type: () -> None + super(SessionCommandMixin, self).__init__() + self._session = None # Optional[PipSession] + + @classmethod + def _get_index_urls(cls, options): + # type: (Values) -> Optional[List[str]] + """Return a list of index urls from user-provided options.""" + index_urls = [] + if not getattr(options, "no_index", False): + url = getattr(options, "index_url", None) + if url: + index_urls.append(url) + urls = getattr(options, "extra_index_urls", None) + if urls: + index_urls.extend(urls) + # Return None rather than an empty list + return index_urls or None + + def get_default_session(self, options): + # type: (Values) -> PipSession + """Get a default-managed session.""" + if self._session is None: + self._session = self.enter_context(self._build_session(options)) + # there's no type annotation on requests.Session, so it's + # automatically ContextManager[Any] and self._session becomes Any, + # then https://github.com/python/mypy/issues/7696 kicks in + assert self._session is not None + return self._session + + def _build_session(self, options, retries=None, timeout=None): + # type: (Values, Optional[int], Optional[int]) -> PipSession + assert not options.cache_dir or os.path.isabs(options.cache_dir) + session = PipSession( + cache=( + os.path.join(options.cache_dir, "http") + if options.cache_dir else None + ), + retries=retries if retries is not None else options.retries, + trusted_hosts=options.trusted_hosts, + index_urls=self._get_index_urls(options), + ) + + # 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 + + +class IndexGroupCommand(Command, SessionCommandMixin): + + """ + Abstract base class for commands with the index_group options. + + This also corresponds to the commands that permit the pip version check. + """ + + def handle_pip_version_check(self, options): + # type: (Values) -> None + """ + Do the pip version check if not disabled. + + This overrides the default behavior of not doing the check. + """ + # Make sure the index_group options are present. + assert hasattr(options, 'no_index') + + if options.disable_pip_version_check or options.no_index: + return + + # Otherwise, check if we're using the latest version of pip available. + session = self._build_session( + options, + retries=0, + timeout=min(5, options.timeout) + ) + with session: + pip_self_version_check(session, options) + + +KEEPABLE_TEMPDIR_TYPES = [ + tempdir_kinds.BUILD_ENV, + tempdir_kinds.EPHEM_WHEEL_CACHE, + tempdir_kinds.REQ_BUILD, +] + + +def with_cleanup(func): + # type: (Any) -> Any + """Decorator for common logic related to managing temporary + directories. + """ + def configure_tempdir_registry(registry): + # type: (TempDirectoryTypeRegistry) -> None + for t in KEEPABLE_TEMPDIR_TYPES: + registry.set_delete(t, False) + + def wrapper(self, options, args): + # type: (RequirementCommand, Values, List[Any]) -> Optional[int] + assert self.tempdir_registry is not None + if options.no_clean: + configure_tempdir_registry(self.tempdir_registry) + + try: + return func(self, options, args) + except PreviousBuildDirError: + # This kind of conflict can occur when the user passes an explicit + # build directory with a pre-existing folder. In that case we do + # not want to accidentally remove it. + configure_tempdir_registry(self.tempdir_registry) + raise + + return wrapper + + +class RequirementCommand(IndexGroupCommand): + + def __init__(self, *args, **kw): + # type: (Any, Any) -> None + super(RequirementCommand, self).__init__(*args, **kw) + + self.cmd_opts.add_option(cmdoptions.no_clean()) + + @staticmethod + def make_requirement_preparer( + temp_build_dir, # type: TempDirectory + options, # type: Values + req_tracker, # type: RequirementTracker + session, # type: PipSession + finder, # type: PackageFinder + use_user_site, # type: bool + download_dir=None, # type: str + wheel_download_dir=None, # type: str + ): + # type: (...) -> RequirementPreparer + """ + Create a RequirementPreparer instance for the given parameters. + """ + downloader = Downloader(session, progress_bar=options.progress_bar) + + temp_build_dir_path = temp_build_dir.path + assert temp_build_dir_path is not None + + return RequirementPreparer( + build_dir=temp_build_dir_path, + src_dir=options.src_dir, + download_dir=download_dir, + wheel_download_dir=wheel_download_dir, + build_isolation=options.build_isolation, + req_tracker=req_tracker, + downloader=downloader, + finder=finder, + require_hashes=options.require_hashes, + use_user_site=use_user_site, + ) + + @staticmethod + def make_resolver( + preparer, # type: RequirementPreparer + finder, # type: PackageFinder + options, # type: Values + wheel_cache=None, # type: Optional[WheelCache] + use_user_site=False, # type: bool + ignore_installed=True, # type: bool + ignore_requires_python=False, # type: bool + force_reinstall=False, # type: bool + upgrade_strategy="to-satisfy-only", # type: str + use_pep517=None, # type: Optional[bool] + py_version_info=None # type: Optional[Tuple[int, ...]] + ): + # type: (...) -> BaseResolver + """ + Create a Resolver instance for the given parameters. + """ + make_install_req = partial( + install_req_from_req_string, + isolated=options.isolated_mode, + use_pep517=use_pep517, + ) + # The long import name and duplicated invocation is needed to convince + # Mypy into correctly typechecking. Otherwise it would complain the + # "Resolver" class being redefined. + if 'resolver' in options.unstable_features: + import pip._internal.resolution.resolvelib.resolver + return pip._internal.resolution.resolvelib.resolver.Resolver( + preparer=preparer, + finder=finder, + wheel_cache=wheel_cache, + make_install_req=make_install_req, + use_user_site=use_user_site, + ignore_dependencies=options.ignore_dependencies, + ignore_installed=ignore_installed, + ignore_requires_python=ignore_requires_python, + force_reinstall=force_reinstall, + upgrade_strategy=upgrade_strategy, + py_version_info=py_version_info, + ) + import pip._internal.resolution.legacy.resolver + return pip._internal.resolution.legacy.resolver.Resolver( + preparer=preparer, + finder=finder, + wheel_cache=wheel_cache, + make_install_req=make_install_req, + use_user_site=use_user_site, + ignore_dependencies=options.ignore_dependencies, + ignore_installed=ignore_installed, + ignore_requires_python=ignore_requires_python, + force_reinstall=force_reinstall, + upgrade_strategy=upgrade_strategy, + py_version_info=py_version_info, + ) + + def get_requirements( + self, + args, # type: List[str] + options, # type: Values + finder, # type: PackageFinder + session, # type: PipSession + check_supported_wheels=True, # type: bool + ): + # type: (...) -> List[InstallRequirement] + """ + Parse command-line arguments into the corresponding requirements. + """ + requirement_set = RequirementSet( + check_supported_wheels=check_supported_wheels + ) + for filename in options.constraints: + for parsed_req in parse_requirements( + filename, + constraint=True, finder=finder, options=options, + session=session): + req_to_add = install_req_from_parsed_requirement( + parsed_req, + isolated=options.isolated_mode, + ) + 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, + ) + 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, + ) + req_to_add.is_direct = True + requirement_set.add_requirement(req_to_add) + + # NOTE: options.require_hashes may be set if --require-hashes is True + for filename in options.requirements: + for parsed_req in parse_requirements( + filename, + finder=finder, options=options, session=session): + req_to_add = install_req_from_parsed_requirement( + parsed_req, + isolated=options.isolated_mode, + use_pep517=options.use_pep517 + ) + req_to_add.is_direct = True + requirement_set.add_requirement(req_to_add) + + # If any requirement has hash options, enable hash checking. + requirements = requirement_set.all_requirements + if any(req.has_hash_options for req in requirements): + options.require_hashes = True + + if not (args or options.editables or options.requirements): + opts = {'name': self.name} + if options.find_links: + raise CommandError( + 'You must give at least one requirement to {name} ' + '(maybe you meant "pip {name} {links}"?)'.format( + **dict(opts, links=' '.join(options.find_links)))) + else: + raise CommandError( + 'You must give at least one requirement to {name} ' + '(see "pip help {name}")'.format(**opts)) + + return requirements + + @staticmethod + def trace_basic_info(finder): + # type: (PackageFinder) -> None + """ + Trace basic information about the provided objects. + """ + # Display where finder is looking for packages + search_scope = finder.search_scope + locations = search_scope.get_formatted_locations() + if locations: + logger.info(locations) + + def _build_package_finder( + self, + options, # type: Values + session, # type: PipSession + target_python=None, # type: Optional[TargetPython] + ignore_requires_python=None, # type: Optional[bool] + ): + # type: (...) -> PackageFinder + """ + Create a package finder appropriate to this requirement command. + + :param ignore_requires_python: Whether to ignore incompatible + "Requires-Python" values in links. Defaults to False. + """ + link_collector = make_link_collector(session, options=options) + selection_prefs = SelectionPreferences( + allow_yanked=True, + format_control=options.format_control, + allow_all_prereleases=options.pre, + prefer_binary=options.prefer_binary, + ignore_requires_python=ignore_requires_python, + ) + + return PackageFinder.create( + link_collector=link_collector, + selection_prefs=selection_prefs, + target_python=target_python, + ) diff --git a/venv/lib/python3.8/site-packages/pip/_internal/cli/spinners.py b/venv/lib/python3.8/site-packages/pip/_internal/cli/spinners.py new file mode 100644 index 0000000..c6c4c5c --- /dev/null +++ b/venv/lib/python3.8/site-packages/pip/_internal/cli/spinners.py @@ -0,0 +1,173 @@ +from __future__ import absolute_import, division + +import contextlib +import itertools +import logging +import sys +import time + +from pip._vendor.progress import HIDE_CURSOR, SHOW_CURSOR + +from pip._internal.utils.compat import WINDOWS +from pip._internal.utils.logging import get_indentation +from pip._internal.utils.typing import MYPY_CHECK_RUNNING + +if MYPY_CHECK_RUNNING: + from typing import Iterator, IO + +logger = logging.getLogger(__name__) + + +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): + # type: (str, IO[str], str, float) -> None + 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): + # type: (str) -> None + 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): + # type: (str) -> None + 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 '{final_status}'".format(**locals())) + self._finished = True + + +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() + + +@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") + + +@contextlib.contextmanager +def hidden_cursor(file): + # type: (IO[str]) -> 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) diff --git a/venv/lib/python3.8/site-packages/pip/_internal/cli/status_codes.py b/venv/lib/python3.8/site-packages/pip/_internal/cli/status_codes.py new file mode 100644 index 0000000..275360a --- /dev/null +++ b/venv/lib/python3.8/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/venv/lib/python3.8/site-packages/pip/_internal/commands/__init__.py b/venv/lib/python3.8/site-packages/pip/_internal/commands/__init__.py new file mode 100644 index 0000000..6825fa6 --- /dev/null +++ b/venv/lib/python3.8/site-packages/pip/_internal/commands/__init__.py @@ -0,0 +1,122 @@ +""" +Package containing all pip commands +""" + +# The following comment should be removed at some point in the future. +# mypy: disallow-untyped-defs=False +# There is currently a bug in python/typeshed mentioned at +# https://github.com/python/typeshed/issues/3906 which causes the +# return type of difflib.get_close_matches to be reported +# as List[Sequence[str]] whereas it should have been List[str] + +from __future__ import absolute_import + +import importlib +from collections import OrderedDict, namedtuple + +from pip._internal.utils.typing import MYPY_CHECK_RUNNING + +if MYPY_CHECK_RUNNING: + from typing import Any + from pip._internal.cli.base_command import Command + + +CommandInfo = namedtuple('CommandInfo', 'module_path, class_name, summary') + +# The ordering matters for help display. +# Also, even though the module path starts with the same +# "pip._internal.commands" prefix in each case, we include the full path +# because it makes testing easier (specifically when modifying commands_dict +# in test setup / teardown by adding info for a FakeCommand class defined +# in a test-related module). +# Finally, we need to pass an iterable of pairs here rather than a dict +# so that the ordering won't be lost when using Python 2.7. +commands_dict = OrderedDict([ + ('install', CommandInfo( + 'pip._internal.commands.install', 'InstallCommand', + 'Install packages.', + )), + ('download', CommandInfo( + 'pip._internal.commands.download', 'DownloadCommand', + 'Download packages.', + )), + ('uninstall', CommandInfo( + 'pip._internal.commands.uninstall', 'UninstallCommand', + 'Uninstall packages.', + )), + ('freeze', CommandInfo( + 'pip._internal.commands.freeze', 'FreezeCommand', + 'Output installed packages in requirements format.', + )), + ('list', CommandInfo( + 'pip._internal.commands.list', 'ListCommand', + 'List installed packages.', + )), + ('show', CommandInfo( + 'pip._internal.commands.show', 'ShowCommand', + 'Show information about installed packages.', + )), + ('check', CommandInfo( + 'pip._internal.commands.check', 'CheckCommand', + 'Verify installed packages have compatible dependencies.', + )), + ('config', CommandInfo( + 'pip._internal.commands.configuration', 'ConfigurationCommand', + 'Manage local and global configuration.', + )), + ('search', CommandInfo( + 'pip._internal.commands.search', 'SearchCommand', + 'Search PyPI for packages.', + )), + ('cache', CommandInfo( + 'pip._internal.commands.cache', 'CacheCommand', + "Inspect and manage pip's wheel cache.", + )), + ('wheel', CommandInfo( + 'pip._internal.commands.wheel', 'WheelCommand', + 'Build wheels from your requirements.', + )), + ('hash', CommandInfo( + 'pip._internal.commands.hash', 'HashCommand', + 'Compute hashes of package archives.', + )), + ('completion', CommandInfo( + 'pip._internal.commands.completion', 'CompletionCommand', + 'A helper command used for command completion.', + )), + ('debug', CommandInfo( + 'pip._internal.commands.debug', 'DebugCommand', + 'Show information useful for debugging.', + )), + ('help', CommandInfo( + 'pip._internal.commands.help', 'HelpCommand', + 'Show help for commands.', + )), +]) # type: OrderedDict[str, CommandInfo] + + +def create_command(name, **kwargs): + # type: (str, **Any) -> Command + """ + Create an instance of the Command class with the given name. + """ + module_path, class_name, summary = commands_dict[name] + module = importlib.import_module(module_path) + command_class = getattr(module, class_name) + command = command_class(name=name, summary=summary, **kwargs) + + return command + + +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 diff --git a/venv/lib/python3.8/site-packages/pip/_internal/commands/__pycache__/__init__.cpython-38.pyc b/venv/lib/python3.8/site-packages/pip/_internal/commands/__pycache__/__init__.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..3a6d4fafcc6886afdfd3c162c32ebf67d3ac08e1 GIT binary patch literal 3018 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 IXL{!U0IP6e>i_@% literal 0 HcmV?d00001 diff --git a/venv/lib/python3.8/site-packages/pip/_internal/commands/__pycache__/check.cpython-38.pyc b/venv/lib/python3.8/site-packages/pip/_internal/commands/__pycache__/check.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..9070e06a61b675828e0d00f48553bda8be58cb60 GIT binary patch literal 1616 zcmZuxUvC>l5a0WMKA#~;LqtN04v9V(k#kf+p%OwxCMt;HqJ&iH(du-*UE7!3zwGWg zvUPrPpDH2ozy|<{7rxTI^2Ar*0Wo`VQx|p5JD!=H*`4|A%${~S4ubLFtrz%b6QRGP zus(DU?!#m+fMA4`6b15MO_fXyR7LVys%3hhXGUN&vYwim6Pu<-uhh$-X<=c!Y)?2k!=Q>*;cUCSla20Y&+Oi&;x{9*m;hyL)4`fyn~e! z)NTI_@t{Fh5mvv*vM9%0RTxLd$B&MMb#mJ8ADx_lW;33U_&hA5_&geukP$9Eh$)FU zS=WUlT4Z68GajWW!LX@&NfWRsDqdC`u?7*m z935aMmO93c5JB*sD!gJ$%i_T>0F5sx6#Fd*Om7>7fB2`fvO6h%8O@==<^Tna8Mo{^Y` zd6W?1dKLne6tazaSDe;&=wihG~TJ$`7f?{MR<5X6srbwJK;`MF-pY#(F3C zu-yRK!w#El!%#S3m=(B63Gi+hUQ|&kAp*$?LtMnb7!`w-r|&@k4HRo+PwHGCH()Y( zpEjJTYmKhEBdoL-k0t)^L!$n5qJEqv{xD)BTxCpUZ@aEk+>-}vG=BBJ}V(+YRi~fW5ORo5`>M9$`hVH`cI;sx5tNjPs CsJl1- literal 0 HcmV?d00001 diff --git a/venv/lib/python3.8/site-packages/pip/_internal/commands/__pycache__/completion.cpython-38.pyc b/venv/lib/python3.8/site-packages/pip/_internal/commands/__pycache__/completion.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..400fc856ec5ceb594e1bcc52dcc50fafe2774f2d GIT binary patch literal 3069 zcmai0-ESMm5#ReHMNwAleAIB8V5{gs7vxCGZjGd{jJmQ~7)>lel97aoaX8&BsiTf} z=iNOiqR1y12vEQU@}39jG5^xO_NnN9&^#nFM@pnDDLLZyW@mS2XMgjX;Xa?6s}Xn> z-~ShTJxj=curvMG(D^I8@=ItKVMaoFM$0fzHWRaDmS3x7!PiRcp3`#5F*|X4l~x60 zr&VPxtFS7Y`N3+<{DZI>n|(#tEO(BrR*jik#H;@otP{`B^Wl!nl0xwy?&X*Wboah6i4LZOaiB;s5RubstHRtrBc z2&vKW6M(&7VrR)aA2~WY7jf?D`sW*)Ya5?^wX#GPm&$JN#ro!xt(Aqvqaz$0b-(`T z#c;SlzqzpZY`5{ZoV|*xR~9Goudms7x)D5nzP%pcg3s2r*H)J4;xfJWsLA+#GcA&Y zdUWFbaCq*(_;j558D+d37Kx(%-{~7Cr$-3!XjS<6G(_zpjY^C!t(ZnA$)Yf!2bo}! zM)*13pHjg?M*YXpX!z1VlwPqSPY$m@y0i}vUkmUA&XvWbrIB5KnR?!J=w6BIXq)91 z7va4Y-AEQKsmmr*&7)hLBL_NB&aQ%{! zV|3|?v-d)^i?&h~#;K%!Onv_i+sdOOc9t~v0L6=>p1^^$K6CXFo^<)5e@et@hsr#O z75#>m+wy&&&eUFCFTa==$DBROp9z)QAzfGsF2m<7P(PaHvS(>~CzQL|>O*5A-VxTF z>Bky(m+K%s`JjP7t+NX}?Go2bXff%?_hj({NN4;J7myz}tN1R6z@F(Aig*BlrF z^WTO+2Bd2Zt%23GnZZnS5S?R{*Dj2q7D_K@YoMK-($?7=Xm5aaeyk;3=fq`i4XjfW zi#QzW=Ci)*`v_X!^mp{ERGHvF(o`zpfHT@D>u>-4wI0N9atII^FAs=oJzKkBSva&) zuGKcP7IgO13G9FAg3p0u?cijLIg-hsFY zHh(nqOb|p#C}j}*NdAA=%tgE(D&E`=MYEkHj0@S!qvnHyeE(1P(ogRx#vbg*PwuvR z{obGNmQN>bZZ{$C{UXi7Xpb}hV3+g6-wO}B*{AoxW}G*{b;U&*Ce1OUBnspIzD9m1 z-U4?%fmc3&h8VX@*R&0jn8x_`lk5IQztX?6dTtUVbb|K83lbFv6-p(wV-$0hL5as&@G$5@zjE-HG=( z+zs%ya3Q};@EUV$3y<&)G#$vN7xf_|{=oPh`QChK!xjec%@=oI*fX`+xGV71RQ5;B z@1d(}50h9bjK-tq(3fb|B_`>6m*Ese+IU$0QOZXkoUPM%39|9;7xr;ScbIao{_VBR z&GpT{(66$B_6n(J4qT&0!&={_P)DA5^ZM5KKJaw?S+KRb`E+A@OWS+=P=Y16OL10O z(pAO3RRwaYl;snQl)tF#N{2>uELdaWIxFAU88?eW&y*);>qYD<>Z6wp~{=Rm}Y zYrC7psppD!ababYEKR1Mp-&H9~E>&>DE?Lrk`I1orL z@ewwQ(C7~!g&XH7+=!C60R<8(YMEsMlS|*eIH^!^A{)IJ*nqJS9yWi(1~;E_59wLD fr>hS~5In*KC9CUzXw|G6b<=jt8FSYBpZno2t3)n& literal 0 HcmV?d00001 diff --git a/venv/lib/python3.8/site-packages/pip/_internal/commands/__pycache__/configuration.cpython-38.pyc b/venv/lib/python3.8/site-packages/pip/_internal/commands/__pycache__/configuration.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..44a744bf7cfd14a3391276585ebb63530ae402ff GIT binary patch literal 6607 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 RVPRlnrjc$gm?(Kk{}*r^z~%q| literal 0 HcmV?d00001 diff --git a/venv/lib/python3.8/site-packages/pip/_internal/commands/__pycache__/debug.cpython-38.pyc b/venv/lib/python3.8/site-packages/pip/_internal/commands/__pycache__/debug.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..b7bd65826b09cee129d8ed541e514364c489e434 GIT binary patch literal 6430 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 O{->Q4r{Y|47XJ_H?vUO9 literal 0 HcmV?d00001 diff --git a/venv/lib/python3.8/site-packages/pip/_internal/commands/__pycache__/download.cpython-38.pyc b/venv/lib/python3.8/site-packages/pip/_internal/commands/__pycache__/download.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..851cd6767f672d094f2cc228b4f63818b8f98dfa GIT binary patch literal 3942 zcmbVPTaVku6(%W)q^Qf@Y_FZf01-5G1yow+Vy7v#3j;P<6flgSc8fwmN^m)&6?b{_ z47nREv`=fmKmiBnKUhEJFU@P8`VZPPt@|B{T1)FT4<*6ToH;XR=G?wH`hBuw@c^Iv77f#w9&3p{(sk+^u`x2L-^Qqc-$raE%~7-BnX#4FBfIK1 zV<%~iT8TSyE82?NNoUll`gYt+dZS*lHd;&8N9)y?6K^D&qfK4o#>E!D_F5lZ|Bc34 z%zdFTm$%LvqZ`b4s`cA{!AI6CEi9uyOZ5fRRll|^0_5Yv6ell&3(3X4okLpu2K)2In~7agvR zbf&?yI?QT}%$M3&XANe)a7ImTu_m)#Xy^LKeyN$-$blhjwe~1GN#iVFwHm==UDI3- z&w}CQICgNrr8gE?@=Ld34!kd?U;ftn85H-jV#0+NrHp^WW#21hlpc6XihDBWVKk1o zki&ZMA09sSa*-WEDX$dq<@n<)4B|@;%u{I{@hcND0r^Q*a8G8X2)P%Ca1tHUv}%Jq z%Do_#nJ3Gf+$ud*Ye`2VHM|d(4hYSnn9C1;?ww4ca6UkeQ@`3kN9bI30yGw_7Yo$H!tJ>s) z%BX|O`NQE{=DU&xVnx_oqyI*6_&(;1pV+&$M&n(3t5n<6FSTOgn6xHD-+qvuIveueH}SHmB?r%2}aWE0nuJ zwO6Rl3e{bqdMni03bnpMZLCn6E7aBsb!|Z%noMT~mhP+3e>J4@r~g6QUgkGWWo4Jks^z?kaQTlkYePB^xt zycJwZe;-P1lDKNu2$x4?NadCi_|5ZsAAhd8;4hIzCY2SI0wz+oNVAp8bF0Wtcr8FuV`ehrRpYFQ znQs0^wC#>|W?_D(=$-?d&g`N`qcaPMtuynLdFFt-HX}f5BB{-@7Ls^<2DvjE@<>vX z0N|B<=7O^^b7>5to6N@QHdb%VnicG|53kKykm|fL&N{4hahchyx___LR6{s zd5$5OQ2d}eV82E&(JN4^)VYCh7x!Rh)kZQcY9Dc*MyxT#r4U^rx|AyTgfCI|074vO zAYvW-J@Dr;*7B);i~z+E5nkgAb)y(ZVn7Ck!Bc$lCK@ehIds1I#mni&1YDu z+}${m1!6wmdr6yFXe>mm_2%@>)d&@3I-K90au1!wy3_ZbE^WS4N_Zd1{w=j09iUi5 ztqL#<;uvRYaTXQ9K8lw)P}W9a5Qx$fkQs#pfr0|RGPiK(s9RK;*3j(Nt`}6WtLwG* z>H@@{drCQgw5qZdw`f)usRj{@M+5W=4s#~)FqVTQi z&8zE$pVd0m%N&R+qp)(ivY`^qRpwz9rGmVvtm7aqp*~d&{hk=mQ7G)hkQxd%@i{dV zOR7^}7F8gNU(rwtaa!MZCQd2E0~&SeorGFjKDy+Ea^Hr;)d*<7txvFA{t1m{SPjc? zjSfE7a0s~Q{g>73VXOi&qJOaLb%MCd7yW-&?qb9QpTG_P_{K6_-Ff4frtTW2TmL1b z`YpOnDNJ#ly1t)etctSln@5lJE$ zR!K6?8B$PPIoNz+1$6{+Rp7S}_>Z9^P|K$!7HM49I7=oZjHyhhoZY#1_9*QnU5Xt9 RiDPv1j=qlXnvTF}{0G!GlOg~B literal 0 HcmV?d00001 diff --git a/venv/lib/python3.8/site-packages/pip/_internal/commands/__pycache__/freeze.cpython-38.pyc b/venv/lib/python3.8/site-packages/pip/_internal/commands/__pycache__/freeze.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..49d8b0a4c794613b1591e999925ec648e1240b30 GIT binary patch literal 2902 zcmZWrZI9fx5!Tykwc3|^voEO=I|7ol-Qu)sIBC!XX;HvVf}*VxG;s^42n3zT-PIlS zVv@S^se7N!K#Bs+Z@BxI|I%N;ul>|tP{1kB&QQCTJx2?2hchHKoO$MhP zvR>{-zLvYJpAVvemOVDiN6`r69`Q+^3>Hqb^_fA2Wc1u1BkEn)(KfNZG{#&11`mv} zskV|sQLVo_mo?T+)4RMBQ%;$A~;Qod>>YS60q8&-?(M&udxL zGRO)c6UHbBsw6#5=2Usbk>0Rz`zB`ipSG!Zm}-Dn{) znVrx;Kmfs5azgn;uWk1AUl6=o@p2ygbrs03$JWbj`b>T_or*ItfoM_AIY7op&5bZ% z6nfCMpoxD%Ck<&XjSK6@TAEAiTR_gzII@o%ViJqk#F-nUgVA`=y|#E73upm6+^_&D z$QIcqJ7kyay#SQ0_PRxGYwbR0JFWK8k)2ET%DDRBjjxf{Vb)JByX4NLCwo`sl}X-s z0k~c5@+P?p_IuYl|FTcsLfe&vScNPFj~grG%`gl%Vy^}p1EJics%Sx!Gs_rl&YuVx z$OUa9j7h-3976wU{$O?hC=D`5W(tVaSp}#S!JHP9Co(JML80!!7O6ZWz*MDXR$$DamAV z$f$yH)0%j#Y2N*$)dXwJ`aQvnm-!kU91C(hP#sZMP^1GGDKCz*B9n2fJRrvypaUs} zOrZq8;K=i;hg1<)iCh3NYCciUQJEFWd-Tbl<1c>y*_X<#5{{AELlWU~wLG5lvaZIq zas*{F&Q!fPPFW&E9DirLtfv*vP7+C{0KMs~WI!TfTBXwuPOFpm9~2)x zkc512D1LQ+mN)q?@9Sq?Ouw1}A%}H=QKcl*1RNeGXGi6S@13D-3a68l7YUnU?LZxg zX`7%)b;j?(;qF2c1UkdqwOrFOEc5#J((V07MogUl!E;HT?L0{!ry^`Sk4CjJf`Qssqi7uLBgfiREkr46zpP3c^i=N-^=#DgUe zo9rU7N$;|^1R4X$2U{Q70NN1R2-+63?WMECbvwj-(K~nL?#x)a-&h6(OWlXmOy@RYr;q1q|H>D`8zjf)iQ(a;EjNn7J?deNSYg=`5~j;$8Gd~gSZqJ Qq=DsG14!)ZXASKC0}3NS0ssI2 literal 0 HcmV?d00001 diff --git a/venv/lib/python3.8/site-packages/pip/_internal/commands/__pycache__/hash.cpython-38.pyc b/venv/lib/python3.8/site-packages/pip/_internal/commands/__pycache__/hash.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..46852793d83b29ebeb32c8902efd9a20fcf89683 GIT binary patch literal 2052 zcmZ`(TaObr6t+Fr+;+EsC`E-1E!#FylOPJHAc{bgLM61Q+3iJBq0waQB(poWwLQDt zH2cy;LaIdj7qXB1nf?I3_Njk?7kWI&LMpY6>^a9icFy_Ecg}aMRvp2(eBn3xr;X5G zT3H?@D0g9sUtyt$;smAG$5_iGAwH?ThHq3~(>LL3Bvxwsc4cEGPU`w@C0j`?t^4(~ z;WsMXPMT@UZ>1~#3P#MBt+KUa?7#C6QHQ$E5OtY-Wca6Ojn3=sNMJ*{6KB2 zR>OhFlTxxEPK%t&4cK$;=V=;d6h!MmzyGA)CaU%Iz3-p&zu9^aJi52}NVPV%`cF0< zR?34-IB#$k(jXd_*^W@neICP$c`1uhM*5$Yhp|w^6%ZKmF+~tRhS&+jZTQx6G{n@T z)-%_)sZAa5+IfylywDrNnU@biO7bX7ydsQt!V#SC zXdLgcZoRVK8Z*JXD9pS8^Gd;}w;#)~*XdN(;|#8Iwp+%Wr7V-e8^#IqWbV-%_6ruu zaFDR-r8pBZOcK$pw8{DPg6AXer@WAHo{6X4ZGH8Q*X?$WV12v{-+t@ZKri2>OqNcQ|HFKyvbES2Rk1=&#IF(gxR;RAEJ|>zF08(vZ zLOS83(dmTAD2HT>Q&oE_T0-WWO)dirbHLsl=^z1*TV9?q4dX9|6Y`nYwv@d;+Du=RRSSC`LW0n+LN7!yCOOyrUyat+bgCNdg83f9WQW^j-Le)cBN$>)P zYC+u6mi92`X$Vhk=W(X&BIJT`bs7+6T5{DC4jf^=|AJd)KaJH?K-cZw$m2SBB{%y?&({qpY{p zE8?P4W|}sPcJ{}NC7qq{U^~D0`GG#`K{S%_EKGXQyh=n*lcrl7@KX@Q1(@PIEC`>% zHYSMRlb?y**W{J$PR=Y5xO)Omq#1ECG#)O@`!K~FSf)grg$$z^mIU$)?2zUrn&NHi z(3q01(9?5=njd&-P7Uek^@tpq80~r$AIQdJZC%`|&k6n|i0h*5@^|57)c~Y28I%Ib zL>b}$k}D*@#kp=;T=zAueNnAI2O8wVKy$in@%Qwuv!FWHL08twEJ8Wv7qwn3A80#+ zmsuMGaRwHIO}v6lVq+I1013ITC#%b_XzQ9LPZp*O$Wsr3G^b^vnk3OLwf*wV+AFk|YS0ynG){xpuYUt)xJI?~X2rioZ?b0&`u!rHR+@8mSv=AN~Wn*&*5h literal 0 HcmV?d00001 diff --git a/venv/lib/python3.8/site-packages/pip/_internal/commands/__pycache__/help.cpython-38.pyc b/venv/lib/python3.8/site-packages/pip/_internal/commands/__pycache__/help.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..2da5c73233a075daa583dbbcaaf7c663d381d521 GIT binary patch literal 1250 zcmZuwPjA~c6esoHijx*w5TwI42*q~rA$HNNMGF)~fwb6m$;B%We zfURDFqKM)GaU5f8Y^$)i9owC47Y=u0x3itX<9_U8^b?{k_1+`uG5f}j1L}{_Aov?R zp#j!+$hj(ure;abt5Vhg`KKl4L{MXn-<+Pl8jr#3Z_rm#mNGTBcg2Fk3)m_G#ZZhX ziYE}dqaH-}_1@2{s5TxKN8^j~D!Kq9nv^n1*WuOTD2zJ!zLI4Y zJ=@^VU#B(%*@|HxJs-0;lpI~HG z?Rw~UG)BLn)@$vlFRxp-^=|Oer}nMadMljaWzhQavJGy1>Hy}i@Cw+k1M1%P+F*rm z@E^E!x?8=rcNyO9tvb(8t9K?bWHqq${iRr}@8+R88te!NgIf)`M{T lUr+2hW$PAqBhh8BldEZZpyOctE{oEw#GI`5hc&KJx;sXr${zgebK3E z(JWWbU`N^=ZvsuU17w2aAs`pYgA*VbED~UIL6DaO`!v|x_Mwo61OXP9hX6qWYyjJ1 z=llOt)y<~lonYCl>T~_if6n>Oe?5I~csQ@&@8sdXA>OZP+W(-;?mrWmH}QD?Th}z9 z3EkBidRf;gZMa6+;O|s9#ouPx>h2{Wt-C@?y<)4@^Ma&x+fYZ%O^QK=ALStE}!P~ zxO=8?wtSY;6YgB&x$<+Io^+pY%$MgI#d5JxDwi4y?fG8?TgK(KYAAC%XJYtj-Kp=hgD7++x;!t?_#K^~R<0rN$fO zHyW4Am%03a`<=#{QTE5EZLx6a#d=1c2+t-~NJ9_!1I4q7l(#mhWrHP}$ zexwQ8+4nG2ep?(9$5DPuoDe7R{arCHPKz^-^zu96te8W|yW%$R#|51dM~+Hg>q|NffexYw$+HO|!XKSd9CQ=T|mm-LEdYR4?bu zDDO1Aj^tdlk4A{cc0}cYBRxzaI@^{l->G4t>g~#kY&9y4mZ+~_xoM6S{BPEqqP6K^ zu>jnuv^svf;{%i7yH2Bh9V=S%TXG8p6YqU+`-950w{Ki~r*h}s;^M8vw+P_}b?JAi zuG4%_X|^h>Zfm*f(poQf>aK`vZ>`mFMTK*fm6l9!#G>&g0fhB_)$M>5%qVrGxfNw^ z`HrMnM&`R%`Y8K7-g-aLTbqu!EgeA< zc4}Zyy*~q~RQz~jNc>b=(SLBdY;y{O_@g&-Mr|}B4bxr04n)*7@#zVcU zf2M7xLVZW?8o~&T4U56tjjUxxi1w_5?QJ{li!Zz^(*P2gMDo!jZ68RF@TFzKB)z#* zlmg8}BfS&C#{=hVu-)f+G@`l?CDOutu6^-fp)KnVFzJN{Rk^Uzas?=2p

eX|w&{ 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 literal 0 HcmV?d00001 diff --git a/venv/lib/python3.8/site-packages/pip/_internal/commands/__pycache__/show.cpython-38.pyc b/venv/lib/python3.8/site-packages/pip/_internal/commands/__pycache__/show.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..a11393dac914e815a2cd94ce8e2212e027bdaceb GIT binary patch literal 6376 zcmb_gOLH5?5#C)a7K;Z#h@xdmeyyJpQUd7MjuR`45`_{i#WJNLWye9RAWO^=1h{x; zW+9ndixZWyoQe~tDu*0$0CS4}NPa<1drVHb6e- z&j)jJ1r5Jfp8KWv^o*wcjXu&p6CXdoqkfM83U&>JV; z)kIzt?rWmp756iZMNt%``EQ4wV^e_wB$62_)hJ@I#Nr&aZM-rZ6Ie-wDlPH#An z!8&M#HO~__U8y{YuesrFyD2?27|E8WzSQFrEw?}DcUrFB3B6|D?Rn^xTOIUzZeLW5 zID2Jdo8#}S%;^xla-gNo!lY)aEQJhYHdmwvm&>8d{cMFR< zgTChsq|+Nn53MS2ecuz#&~5FyZBNw;JoeTOIx(kCN1>auv9?-k4SGYrHHU+dcA(DrKh$|JGb%7<53q;1X`d+ z+P={>B0VyG4Xuf^Zl-GrT^J%mGNAo`ZZSPOv4laWRYJFjoNmYtJStUzNg}B z@+znRJ8lMYm+h1|cnodL@!ZzVOgiYsfklpTt>s zGN2A1H`{VB8dfv0sXTu>HeK0Pv9Ws(nwM`jTfVDQv-zd=P%RH-=dK%g%XeM5ygl%7 zBA~UF-@Z4z`_>!%cisqucza8I=k@Jg*!$M&{A>4@Z!Hfy!`i4%$Kr|Fy&cc z6F_UG<1{KeeOB>S-$4->Y9Y`jy3h}B;sNY4GRB!86J_@Gu^E_=8R^|D9!nUXXVhEN zBgo;rGwWfUjqQSS)AwBEIncek9pO3d?A#o(Pf~h%t2)fHKu6w8G$VJ{lUoDj!HO(@ z&~AG&&fRlmzte9^Qp|W!d9G~jG-oyf(QFR@2@)bAP{8>sSwc_gP?1Mb&B`Ub;@s2% zq;k?T)ea?Pd~v_Q^|6bv2u*6x!*Nbk<5gZEvpf0s{Io+Do7 zrdrnWe81UDm}E|$L8~NoS-nXE6!`*MH!agJjZ%^6$1J>p_8gvmH`dZ2an z%i8UQu^#CU^Z>Tr#YwMdcM4?Z_hIvwHJrQ=n4juEKH%VeJIF@RihYAx&<5R*KMSl$ zjxb?jN0|eiQGn5LoQ$(+T*M0K=v@}2108h>^?9m~Z867O+st*I(YnlQ=TnsV6otl? zMTO_-qh6uVn=ALn?!r! zacYJ3UKUSt>z}z5b)3sf<6=~toH&5Y}A8u#+Y-Mx5uFfC~y(eBSW~E%u=29pGSUGRBX( zbM4TJCT?SDQp$088lb7u_WGU#Obh4iq0deHQ$6@3I7=RuyiuAF<(#d1?RJg8<}6Pn zV5cp3vAx9E9lY6zPgCYY}hf*2=!?S@*3({6wKM;$Ho}e9T^X_ z-5gH;7>5Kj)r_Q2NPZJVoI&_SuJ2HGN2n>^K!?kCl!d~=-`2vVn@KoxPF0=$Ab=wu z^~D9}RrSy~?;PWfJT`H7$Q|SbbooqU3nnD81HH@m!M^6$U=lj5%7Y0ud27yCe}GU# z=m0T&q9wqxYtbgV(`{Cb$L1js@!_4C%~MVeuY3IXs)nQ`SuUuPjADDLapEgS$+B5T zkE1i({P$@1wX{96TfRWdzD31hJi({G0n*em z3|PivYmx4=+AW~MYZYj%ZhD2PF5f}xOxrTT8$L*+cOZA z6z#>unaVb|_SR!-673^gm1Jh&%ZsV$p_U<0$%T+3Tj+`%l(?f^_5!+WyxGVmG}~Xy1JA z-P*PFwHsl@>j!eLImCemioA4ON>k$ewe`(gm#$yGvT@{>WYv0D=^^qEZs68ts-IIM zWKeSNVSZ=O^J+s1zb$tZ>@I;&mJ45**wY@|IiCkWyf)q2czd}dxLHAo;ym%Ro`x?Rw zde4HRxU}P{=4>zV0zu&HtLb<8$gE3g8te{Zhmrs#Hn(wk5@%aJ(yBOvZgGLFG8uP-11J*#-spAf(Zq(Vv9!YR)05L%f?UXVUV zxH{T3>LTnIefiz*z4tKuSFz$)GIeJ4{!~|J=Y^$mOy>wu=lHny%8Xy zx1H)8iC3d6!de>f8jZM9iZTcv&xWfU$R}9`1^lFev+KEkm#HKXY!dgc-C|c4W@@!s zXs;uJr^q&Ow^~^IFmbp{Bcl(Cn+g0dg`9LacXftCO#6svg-Z-bO!x^ADhVM$CMGv{ zw(AK*G3BR3DP5nYM@%A!2}>Kvt%73G+aNLOg!5CnsBP_W6X|fc`2Gwr)4YF3O>@_# z-Uu*=J$}HGA7P`qz(yOIxN%hB+!aFXT7uXY*z`kdjV=K%@bygi;;P%Hbbv#V)a~by zr>kb;yw@KgDMGR)>0(2+sbB~dsNuF{?pT&4=_X7%C>rzAdz~iV3;_W34&z8%IKuBZ z%T7c}8pR&@$SuDD+HdeElA&e-RsbRl3IrRe|B}80Z~zEBGK~fFr~pQ&O#0KC;!#QS zRgJZ^>eF;Dv^@$&xaVm~17vs_+;kD5QrrN~M((GcU}FN|MWn5Dx)l-w$<8KzCPhJ4 zx-Z5Ab)?e1VkZ;5rLovtpN^(VZkz6R>wUuzkX6q#aX*?B_ Lg-qpT`=fsW6g@)= literal 0 HcmV?d00001 diff --git a/venv/lib/python3.8/site-packages/pip/_internal/commands/__pycache__/uninstall.cpython-38.pyc b/venv/lib/python3.8/site-packages/pip/_internal/commands/__pycache__/uninstall.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..2312becd9f9380382a80f1bbf0de29461b7c444c GIT binary patch literal 2838 zcmZuzUymEN5$EnwyDOdK`9CCg$vyXed4uR*j z+kfX@b_w|xPBxzwOg?~D&0*k#J2@$w$Z?Q%bGPs!&(dB_i&oUKG|l}YhyqKu@~~(} zZA<%kr`U?NEFI+Cq8IgwThT2$5A*F}C)#ny9|>>s&QroWq9uY08tw9}N2K5VH|$UP zj@eEQRh2hd#92{SQUhx@P0Fgw(j?F3A}*6c05kZoDvG4!K<+;hN@Z2K;(U@l$x2lF zQ>k>4=ZS{3KS)_gVD8n4R3et*w8^9>M5$HZH9yLhqR5ZOvMS;{D~0*hOKhvnH`T%B zo3*U8NOfFi^-{a2cs2%yapxHR)2!sxRHb;v&Bt3!)LTFZ5;>eiE_Wi2yAl10j2-TA z`ZSDMyv6;eB=Wx^ltcmCS73G?mn-*H=T3g_5Yl1rtXEi_q$kOdP{WR000&v7SaO&P zrYqK5Sztw_G+UVy{928DTvpSPi6^P3@i3q(#d}L*1NN6j3dS>~8=d89t+iojn&}BU zIH+fOQk6^z-PFT+b|r9d@D`g+vUFmdV=NcRnP7)v0@uaLiWNd9JkbgI%7qrPfT(~* zqS-jhgw^|7?kN6hv^4=xnC2(8Me0~d8pSzfIfRgP8b9W%; zy}@9^DbwAUDC6TTmE2Ig()sZ9cF5SL6TsN= z+8CrR1e7%j6pJR;S&h)k3V>w4WhfU)g9j;M%LH7E6Ob_h2L3FOhE7CY!x3il)?hFb z%6K?*l&++RwQR)v?nhPmYt0gM!p4rJciT2z6rM!`s&+CAk99YPe_ddnCnNfYU;jcs1o_u z&_o_7s(>zfsj=v?}JsH(9J4>_}!6`@MD(8UHQJs$NP3yDY-Yws~t2w`S zsDAhMxR@93yltPOa`fp4GI-FGh(y5$(}@uI;3SzHSMUC2hHE468uFw}@=?01k7~5e za>zXSV>sL$c-3n#5NFTv9hbPy`uCmR-r)Sl_vb%dBVtu&hF4dUUX?F2g>A-%_d|Hq zKVevqXJp|#ch0?xoYRH-5&7bc^A^CowQ!E1$X&YU{=z@@sn z^n2%l_7?#p1eC)|&+3OdScEt9AKuV^c>2MDs(F6y^7xcvE ztxHTW`mFn$SgYtq53FMHY;*sFLZZ3n?L}LDvuHotc}^U1`i)Jf@B!5OFoO;S!Q;?5 zf>n7ifm$}9`NPku2A~f06f=ZDt%9-!vkFSrMz&y0`x~NUhz;$Wy{Uv?cd$Ss<~Nt_ z^o<96*tlo=$}B67u+=PK>zZ!T=qg?z-vnnu9#%^9cg_Bh(D5p#;+1A2Jh5!{5n@IBwzaXME}|8IvP=;{A_>O&e(pnZqFa9_|CP%nJi z1s)|K=uv0>%1cQA8}&mh6X+$BiB61n+2lxfv@8$;f48tN*sRf|DgN-Y@#z1&~8Ho4sCZ6`z@%o&C^$NU|+|o5A2XqP)9?0Kt?|0>&l;;-oOdcnLK%nr=xW&6m4p zC2MOp2SgQBsZxsEa>zldoVe$hzof4@<&s-c6`&x$*Rv~Y9YS_%di%ZZe%H|ZuXnHUx`|AyWiIRYBV1&^cUjA{-UO9(TVtE z|D^8Mqf_xxe@XY}qSNu2{+ak}|Ey{7CGl76-H6V`=lkdNcr&^XU+iCuFZC~B+&XxQ zyN71~>9-8lV(m{2*5>uSQvWiu?itJTf5-b7%cg4kt1^uSIrqXiOGSPcy!!nO&ZAp? zu)&E6VwPrkm?m=BQj72Lhl5b?m?!zIG>-j*sm4y2Z+Jn({bZ0~MPo8>TZmMEKE2L! zPdr}d<-!lPxPZ7iPvk&wkA*_Dl2pWg6b`wU`S}JGHs9xQ_9k`<@>J}CvG|Jzzk1-^ z`q}MU?|AQhaQE)pci)1L^6DUrn7Sl4(m}*LO?hjn@OJ1uyr4KHmOH=pqXDPaD7~KS zD*N3~<^d_<_@^`>>@`?)q%oPjZ@&uDMB?Ya!5O8fRe?ChNt{ zGF{7ee8F8d5X9u$T)lt1H!0*4)30V>=4!@O zcOaoSdcRc$^A$EeCc)*8ayS*-)m;;5tY_+#`oL3_<*C*o!;7+TJ7fbtD&$XQRO(Ni9_vSHExiaHHxE6A;;&!{;|FNPYW){KfVZ^_0!ABjAeYOMXV((2j zzdI1o|6dTL0p4P|ghJ^1*4&&pA<6C?O%C9$N1rl!RXlAxGQvo1PUn@)Gtk)CLu+i)@4-2?G_nsZ%$-stqL=|BIem9%b#{hxosM?j z4nbs1cQsm;Ko8$7P(^bkd>L+i6a1BHsXXugW0h*NMnv4loBen8nroB=UxcRK4)y6Q*M zL$nHVdWvDc*tFoflOuh{&ZLW@%@H^ejs_qtbawR^wp?{53FPQh7p6xmj#Tb+GQm|{ zB1v)NwgJC0eDHP>L<7bVj=Uo{QlkVQYh2&vQJRrHTp%ssl`u*e%_XQylV}$( zkud23hgT#1c1RAAxI^HLU#GQDnqYah(%DU>Um)j=ANgj$ljzIsp>w{J?yBg zR4NCa0OJcPc~mV)z11-B#jcv8&8I{Y=SM=Bl8ZEr5}6mGYVgS{st!Q*GM>Hk{Et-= zx=06^nu8+wqY2?;zK~p~6G(Afd_baDT@SbRC<@AenN7UDl zfiZl)vCA!Uc<4ZZpv)_e7QuILj`9yIdi9ums9lDEJukIF^r z*|*14jJ8LVoNUOq$2Gv}{HO}LGAe-wdCX?z{mQtGk%dv6cq7_%5zh(c>^tK*$T$g5 zri3-Od1_QcTC40^;|3+CPmNJybLrspq46flzHu`@GivJfXS08-8<24>``5a`>ihO@ ztZ{4H9<|;yJ|2zdNAsKKKR3lW`rW+nxhXH@7e6sUT|)mk>JLxmPaU|#p;I0r3EVf5 z72C+4-l}8e$TY?arZK#fUp{z-&0*fNqlLo~&e%S;Ngx|EDR~3|IglNhk>_U(&YWW^ZcakXenND0y0th_9 zlVQ%69aRO!ASi)dZ$aWjY_Tw)JBa8Jdl?N%_$4rcEst^tZY!J>^`D%nTP#c^RJ(^q z5=6o@#>ewxGlz-xOaY_+!Fp)lGsFr8mrc>4A4^%?>&g_GSu{TZU)66$bWK3jyz*zz z(m~@OV5;8^pPdN_F-W>MbrB|4(3vg*V84SU0@qDLTtqW0?gEs2^;_y&z!j8*sINJD z)x9D$qz^CO(F~ot^yMKmSx_wO&xi>Y?smYv4 zJ?u^FRR!GYdz^TlPD`<^YT5|J4XwCo;6|0?B8>#yrNs$qYBaNnqz1FnD2i}FrK2D? zfIH1z0Hr#sQTkXO@TN|nnv)?U7$nHY##i%nZR7xb83q8ywNzD*1@x>A?c!|f+ns3A zzDL?tHtsrGsv63|RaEPBNx%;`)%R1?)+U1nqT=4E9Jw*b!${ddn(eB}FYgueoG4vi zhfh~x9R*gvt(}7m_#BJi&j!H0P zLnBiBoVeBLyQ%hZHKAltO`7H9lT@tQg?S~L_sOyOQ%*ZQqZ@K?I1QB zr${PQv-)-2shUeBNjzyT>Xc*~);G58VmHkCb~yiJ94*(VgdzQ+gsFR8oU#GTN%x+W6 z(O^$7uOs-}cTU;ONEHpz!ui<=8lDP_K@97NB#>2A)KEnMMwd$EOd2cn2Z{#D~_4(u?tsk7M`&}Mwge-G%R?WS%nWb%y~RVExZ%`QoZyaR+Q|a literal 0 HcmV?d00001 diff --git a/venv/lib/python3.8/site-packages/pip/_internal/commands/cache.py b/venv/lib/python3.8/site-packages/pip/_internal/commands/cache.py new file mode 100644 index 0000000..ca6d437 --- /dev/null +++ b/venv/lib/python3.8/site-packages/pip/_internal/commands/cache.py @@ -0,0 +1,181 @@ +from __future__ import absolute_import + +import logging +import os +import textwrap + +import pip._internal.utils.filesystem as filesystem +from pip._internal.cli.base_command import Command +from pip._internal.cli.status_codes import ERROR, SUCCESS +from pip._internal.exceptions import CommandError, PipError +from pip._internal.utils.typing import MYPY_CHECK_RUNNING + +if MYPY_CHECK_RUNNING: + from optparse import Values + from typing import Any, List + + +logger = logging.getLogger(__name__) + + +class CacheCommand(Command): + """ + Inspect and manage pip's wheel cache. + + Subcommands: + + dir: Show the cache directory. + info: Show information about the cache. + list: List filenames of packages stored in the cache. + remove: Remove one or more package from the cache. + purge: Remove all items from the cache. + + can be a glob expression or a package name. + """ + + ignore_require_venv = True + usage = """ + %prog dir + %prog info + %prog list [] + %prog remove + %prog purge + """ + + def run(self, options, args): + # type: (Values, List[Any]) -> int + handlers = { + "dir": self.get_cache_dir, + "info": self.get_cache_info, + "list": self.list_cache_items, + "remove": self.remove_cache_items, + "purge": self.purge_cache, + } + + if not options.cache_dir: + logger.error("pip cache commands can not " + "function since cache is disabled.") + return ERROR + + # 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] + + # 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 get_cache_dir(self, options, args): + # type: (Values, List[Any]) -> None + if args: + raise CommandError('Too many arguments') + + logger.info(options.cache_dir) + + def get_cache_info(self, options, args): + # type: (Values, List[Any]) -> None + if args: + raise CommandError('Too many arguments') + + num_packages = len(self._find_wheels(options, '*')) + + cache_location = self._wheels_cache_dir(options) + cache_size = filesystem.format_directory_size(cache_location) + + message = textwrap.dedent(""" + Location: {location} + Size: {size} + Number of wheels: {package_count} + """).format( + location=cache_location, + package_count=num_packages, + size=cache_size, + ).strip() + + logger.info(message) + + def list_cache_items(self, options, args): + # type: (Values, List[Any]) -> None + if len(args) > 1: + raise CommandError('Too many arguments') + + if args: + pattern = args[0] + else: + pattern = '*' + + files = self._find_wheels(options, pattern) + + if not files: + logger.info('Nothing cached.') + return + + results = [] + for filename in files: + wheel = os.path.basename(filename) + size = filesystem.format_file_size(filename) + results.append(' - {} ({})'.format(wheel, size)) + logger.info('Cache contents:\n') + logger.info('\n'.join(sorted(results))) + + def remove_cache_items(self, options, args): + # type: (Values, List[Any]) -> None + if len(args) > 1: + raise CommandError('Too many arguments') + + if not args: + raise CommandError('Please provide a pattern') + + files = self._find_wheels(options, args[0]) + if not files: + raise CommandError('No matching packages') + + for filename in files: + os.unlink(filename) + logger.debug('Removed %s', filename) + logger.info('Files removed: %s', len(files)) + + def purge_cache(self, options, args): + # type: (Values, List[Any]) -> None + if args: + raise CommandError('Too many arguments') + + return self.remove_cache_items(options, ['*']) + + def _wheels_cache_dir(self, options): + # type: (Values) -> str + return os.path.join(options.cache_dir, 'wheels') + + def _find_wheels(self, options, pattern): + # type: (Values, str) -> List[str] + wheel_dir = self._wheels_cache_dir(options) + + # The wheel filename format, as specified in PEP 427, is: + # {distribution}-{version}(-{build})?-{python}-{abi}-{platform}.whl + # + # Additionally, non-alphanumeric values in the distribution are + # normalized to underscores (_), meaning hyphens can never occur + # before `-{version}`. + # + # Given that information: + # - If the pattern we're given contains a hyphen (-), the user is + # providing at least the version. Thus, we can just append `*.whl` + # to match the rest of it. + # - If the pattern we're given doesn't contain a hyphen (-), the + # user is only providing the name. Thus, we append `-*.whl` to + # match the hyphen before the version, followed by anything else. + # + # PEP 427: https://www.python.org/dev/peps/pep-0427/ + pattern = pattern + ("*.whl" if "-" in pattern else "-*.whl") + + return filesystem.find_files(wheel_dir, pattern) diff --git a/venv/lib/python3.8/site-packages/pip/_internal/commands/check.py b/venv/lib/python3.8/site-packages/pip/_internal/commands/check.py new file mode 100644 index 0000000..b557ca6 --- /dev/null +++ b/venv/lib/python3.8/site-packages/pip/_internal/commands/check.py @@ -0,0 +1,51 @@ +import logging + +from pip._internal.cli.base_command import Command +from pip._internal.cli.status_codes import ERROR, SUCCESS +from pip._internal.operations.check import ( + check_package_set, + create_package_set_from_installed, +) +from pip._internal.utils.misc import write_output +from pip._internal.utils.typing import MYPY_CHECK_RUNNING + +logger = logging.getLogger(__name__) + +if MYPY_CHECK_RUNNING: + from typing import List, Any + from optparse import Values + + +class CheckCommand(Command): + """Verify installed packages have compatible dependencies.""" + + usage = """ + %prog [options]""" + + def run(self, options, args): + # type: (Values, List[Any]) -> int + + 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]: + write_output( + "%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]: + write_output( + "%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 ERROR + else: + write_output("No broken requirements found.") + return SUCCESS diff --git a/venv/lib/python3.8/site-packages/pip/_internal/commands/completion.py b/venv/lib/python3.8/site-packages/pip/_internal/commands/completion.py new file mode 100644 index 0000000..910fcbf --- /dev/null +++ b/venv/lib/python3.8/site-packages/pip/_internal/commands/completion.py @@ -0,0 +1,95 @@ +# The following comment should be removed at some point in the future. +# mypy: disallow-untyped-defs=False + +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} completion start{script}# pip {shell} completion end +""" + +COMPLETION_SCRIPTS = { + 'bash': """ + _pip_completion() + {{ + COMPREPLY=( $( COMP_WORDS="${{COMP_WORDS[*]}}" \\ + COMP_CWORD=$COMP_CWORD \\ + PIP_AUTO_COMPLETE=1 $1 2>/dev/null ) ) + }} + complete -o default -F _pip_completion {prog} + """, + '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] 2>/dev/null )) + }} + compctl -K _pip_completion {prog} + """, + '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} + """, +} + + +class CompletionCommand(Command): + """A helper command to be 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, '').format( + prog=get_prog()) + ) + print(BASE_COMPLETION.format(script=script, shell=options.shell)) + else: + sys.stderr.write( + 'ERROR: You must pass {}\n' .format(' or '.join(shell_options)) + ) diff --git a/venv/lib/python3.8/site-packages/pip/_internal/commands/configuration.py b/venv/lib/python3.8/site-packages/pip/_internal/commands/configuration.py new file mode 100644 index 0000000..b801be6 --- /dev/null +++ b/venv/lib/python3.8/site-packages/pip/_internal/commands/configuration.py @@ -0,0 +1,233 @@ +# The following comment should be removed at some point in the future. +# mypy: disallow-untyped-defs=False + +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, + get_configuration_files, + kinds, +) +from pip._internal.exceptions import PipError +from pip._internal.utils.misc import get_prog, write_output + +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. + """ + + ignore_require_venv = True + usage = """ + %prog [] list + %prog [] [--editor ] edit + + %prog [] get name + %prog [] set name value + %prog [] unset name + """ + + 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.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): + 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 any( + os.path.exists(site_config_file) + for site_config_file in get_configuration_files()[kinds.SITE] + ): + 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()): + write_output("%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) + + write_output("%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/venv/lib/python3.8/site-packages/pip/_internal/commands/debug.py b/venv/lib/python3.8/site-packages/pip/_internal/commands/debug.py new file mode 100644 index 0000000..b7c92fa --- /dev/null +++ b/venv/lib/python3.8/site-packages/pip/_internal/commands/debug.py @@ -0,0 +1,237 @@ +# The following comment should be removed at some point in the future. +# mypy: disallow-untyped-defs=False + +from __future__ import absolute_import + +import locale +import logging +import os +import sys + +import pip._vendor +from pip._vendor import pkg_resources +from pip._vendor.certifi import where + +from pip import __file__ as pip_location +from pip._internal.cli import cmdoptions +from pip._internal.cli.base_command import Command +from pip._internal.cli.cmdoptions import make_target_python +from pip._internal.cli.status_codes import SUCCESS +from pip._internal.utils.logging import indent_log +from pip._internal.utils.misc import get_pip_version +from pip._internal.utils.typing import MYPY_CHECK_RUNNING + +if MYPY_CHECK_RUNNING: + from types import ModuleType + from typing import Any, List, Optional, Dict + from optparse import Values + +logger = logging.getLogger(__name__) + + +def show_value(name, value): + # type: (str, Optional[str]) -> None + logger.info('{}: {}'.format(name, value)) + + +def show_sys_implementation(): + # type: () -> None + logger.info('sys.implementation:') + if hasattr(sys, 'implementation'): + implementation = sys.implementation # type: ignore + implementation_name = implementation.name + else: + implementation_name = '' + + with indent_log(): + show_value('name', implementation_name) + + +def create_vendor_txt_map(): + # type: () -> Dict[str, str] + vendor_txt_path = os.path.join( + os.path.dirname(pip_location), + '_vendor', + 'vendor.txt' + ) + + with open(vendor_txt_path) as f: + # Purge non version specifying lines. + # Also, remove any space prefix or suffixes (including comments). + lines = [line.strip().split(' ', 1)[0] + for line in f.readlines() if '==' in line] + + # Transform into "module" -> version dict. + return dict(line.split('==', 1) for line in lines) # type: ignore + + +def get_module_from_module_name(module_name): + # type: (str) -> ModuleType + + # Module name can be uppercase in vendor.txt for some reason... + module_name = module_name.lower() + # PATCH: setuptools is actually only pkg_resources. + if module_name == 'setuptools': + module_name = 'pkg_resources' + + __import__( + 'pip._vendor.{}'.format(module_name), + globals(), + locals(), + level=0 + ) + return getattr(pip._vendor, module_name) + + +def get_vendor_version_from_module(module_name): + # type: (str) -> str + + module = get_module_from_module_name(module_name) + version = getattr(module, '__version__', None) + + if not version: + # Try to find version in debundled module info + pkg_set = pkg_resources.WorkingSet( + [os.path.dirname(getattr(module, '__file__'))] + ) + package = pkg_set.find(pkg_resources.Requirement.parse(module_name)) + version = getattr(package, 'version', None) + + return version + + +def show_actual_vendor_versions(vendor_txt_versions): + # type: (Dict[str, str]) -> None + # Logs the actual version and print extra info + # if there is a conflict or if the actual version could not be imported. + + for module_name, expected_version in vendor_txt_versions.items(): + extra_message = '' + actual_version = get_vendor_version_from_module(module_name) + if not actual_version: + extra_message = ' (Unable to locate actual module version, using'\ + ' vendor.txt specified version)' + actual_version = expected_version + elif actual_version != expected_version: + extra_message = ' (CONFLICT: vendor.txt suggests version should'\ + ' be {})'.format(expected_version) + + logger.info( + '{name}=={actual}{extra}'.format( + name=module_name, + actual=actual_version, + extra=extra_message + ) + ) + + +def show_vendor_versions(): + # type: () -> None + logger.info('vendored library versions:') + + vendor_txt_versions = create_vendor_txt_map() + with indent_log(): + show_actual_vendor_versions(vendor_txt_versions) + + +def show_tags(options): + # type: (Values) -> None + tag_limit = 10 + + target_python = make_target_python(options) + tags = target_python.get_tags() + + # Display the target options that were explicitly provided. + formatted_target = target_python.format_given() + suffix = '' + if formatted_target: + suffix = ' (target: {})'.format(formatted_target) + + msg = 'Compatible tags: {}{}'.format(len(tags), suffix) + logger.info(msg) + + if options.verbose < 1 and len(tags) > tag_limit: + tags_limited = True + tags = tags[:tag_limit] + else: + tags_limited = False + + with indent_log(): + for tag in tags: + logger.info(str(tag)) + + if tags_limited: + msg = ( + '...\n' + '[First {tag_limit} tags shown. Pass --verbose to show all.]' + ).format(tag_limit=tag_limit) + logger.info(msg) + + +def ca_bundle_info(config): + levels = set() + for key, value in config.items(): + levels.add(key.split('.')[0]) + + if not levels: + return "Not specified" + + levels_that_override_global = ['install', 'wheel', 'download'] + global_overriding_level = [ + level for level in levels if level in levels_that_override_global + ] + if not global_overriding_level: + return 'global' + + if 'global' in levels: + levels.remove('global') + return ", ".join(levels) + + +class DebugCommand(Command): + """ + Display debug information. + """ + + usage = """ + %prog """ + ignore_require_venv = True + + def __init__(self, *args, **kw): + super(DebugCommand, self).__init__(*args, **kw) + + cmd_opts = self.cmd_opts + cmdoptions.add_target_python_options(cmd_opts) + self.parser.insert_option_group(0, cmd_opts) + self.parser.config.load() + + def run(self, options, args): + # type: (Values, List[Any]) -> int + logger.warning( + "This command is only meant for debugging. " + "Do not use this with automation for parsing and getting these " + "details, since the output and options of this command may " + "change without notice." + ) + show_value('pip version', get_pip_version()) + show_value('sys.version', sys.version) + show_value('sys.executable', sys.executable) + show_value('sys.getdefaultencoding', sys.getdefaultencoding()) + show_value('sys.getfilesystemencoding', sys.getfilesystemencoding()) + show_value( + 'locale.getpreferredencoding', locale.getpreferredencoding(), + ) + show_value('sys.platform', sys.platform) + show_sys_implementation() + + show_value("'cert' config value", ca_bundle_info(self.parser.config)) + show_value("REQUESTS_CA_BUNDLE", os.environ.get('REQUESTS_CA_BUNDLE')) + show_value("CURL_CA_BUNDLE", os.environ.get('CURL_CA_BUNDLE')) + show_value("pip._vendor.certifi.where()", where()) + show_value("pip._vendor.DEBUNDLED", pip._vendor.DEBUNDLED) + + show_vendor_versions() + + show_tags(options) + + return SUCCESS diff --git a/venv/lib/python3.8/site-packages/pip/_internal/commands/download.py b/venv/lib/python3.8/site-packages/pip/_internal/commands/download.py new file mode 100644 index 0000000..c829550 --- /dev/null +++ b/venv/lib/python3.8/site-packages/pip/_internal/commands/download.py @@ -0,0 +1,142 @@ +# The following comment should be removed at some point in the future. +# mypy: disallow-untyped-defs=False + +from __future__ import absolute_import + +import logging +import os + +from pip._internal.cli import cmdoptions +from pip._internal.cli.cmdoptions import make_target_python +from pip._internal.cli.req_command import RequirementCommand, with_cleanup +from pip._internal.req.req_tracker import get_requirement_tracker +from pip._internal.utils.misc import ensure_dir, normalize_path, write_output +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. + """ + + usage = """ + %prog [options] [package-index-options] ... + %prog [options] -r [package-index-options] ... + %prog [options] ... + %prog [options] ... + %prog [options] ...""" + + 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.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

."), + ) + + cmdoptions.add_target_python_options(cmd_opts) + + 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) + + @with_cleanup + 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 = [] + + cmdoptions.check_dist_restriction(options) + + options.download_dir = normalize_path(options.download_dir) + + ensure_dir(options.download_dir) + + session = self.get_default_session(options) + + target_python = make_target_python(options) + finder = self._build_package_finder( + options=options, + session=session, + target_python=target_python, + ) + build_delete = (not (options.no_clean or options.build_dir)) + + req_tracker = self.enter_context(get_requirement_tracker()) + + directory = TempDirectory( + options.build_dir, + delete=build_delete, + kind="download", + globally_managed=True, + ) + + reqs = self.get_requirements(args, options, finder, session) + + preparer = self.make_requirement_preparer( + temp_build_dir=directory, + options=options, + req_tracker=req_tracker, + session=session, + finder=finder, + download_dir=options.download_dir, + use_user_site=False, + ) + + resolver = self.make_resolver( + preparer=preparer, + finder=finder, + options=options, + py_version_info=options.python_version, + ) + + self.trace_basic_info(finder) + + requirement_set = resolver.resolve( + reqs, check_supported_wheels=True + ) + + downloaded = ' '.join([ + req.name for req in requirement_set.requirements.values() + if req.successfully_downloaded + ]) + if downloaded: + write_output('Successfully downloaded %s', downloaded) + + return requirement_set diff --git a/venv/lib/python3.8/site-packages/pip/_internal/commands/freeze.py b/venv/lib/python3.8/site-packages/pip/_internal/commands/freeze.py new file mode 100644 index 0000000..1317177 --- /dev/null +++ b/venv/lib/python3.8/site-packages/pip/_internal/commands/freeze.py @@ -0,0 +1,99 @@ +# The following comment should be removed at some point in the future. +# mypy: disallow-untyped-defs=False + +from __future__ import absolute_import + +import sys + +from pip._internal.cache import WheelCache +from pip._internal.cli import cmdoptions +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. + """ + + usage = """ + %prog [options]""" + 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(cmdoptions.list_path()) + self.cmd_opts.add_option( + '--all', + dest='freeze_all', + action='store_true', + help='Do not skip these packages in the output:' + ' {}'.format(', '.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) + + cmdoptions.check_list_path_option(options) + + freeze_kwargs = dict( + requirement=options.requirements, + find_links=options.find_links, + local_only=options.local, + user_only=options.user, + paths=options.path, + isolated=options.isolated_mode, + wheel_cache=wheel_cache, + skip=skip, + exclude_editable=options.exclude_editable, + ) + + for line in freeze(**freeze_kwargs): + sys.stdout.write(line + '\n') diff --git a/venv/lib/python3.8/site-packages/pip/_internal/commands/hash.py b/venv/lib/python3.8/site-packages/pip/_internal/commands/hash.py new file mode 100644 index 0000000..f266861 --- /dev/null +++ b/venv/lib/python3.8/site-packages/pip/_internal/commands/hash.py @@ -0,0 +1,58 @@ +# The following comment should be removed at some point in the future. +# mypy: disallow-untyped-defs=False + +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, write_output + +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. + """ + + usage = '%prog [options] ...' + 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 {}'.format( + ', '.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: + write_output('%s:\n--hash=%s:%s', + path, algorithm, _hash_of_file(path, algorithm)) + + +def _hash_of_file(path, algorithm): + """Return the hash digest of a file.""" + with open(path, 'rb') as archive: + hash = hashlib.new(algorithm) + for chunk in read_chunks(archive): + hash.update(chunk) + return hash.hexdigest() diff --git a/venv/lib/python3.8/site-packages/pip/_internal/commands/help.py b/venv/lib/python3.8/site-packages/pip/_internal/commands/help.py new file mode 100644 index 0000000..c17d7a4 --- /dev/null +++ b/venv/lib/python3.8/site-packages/pip/_internal/commands/help.py @@ -0,0 +1,41 @@ +# The following comment should be removed at some point in the future. +# mypy: disallow-untyped-defs=False + +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""" + + usage = """ + %prog """ + ignore_require_venv = True + + def run(self, options, args): + from pip._internal.commands import ( + commands_dict, create_command, 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 "{}"'.format(cmd_name)] + if guess: + msg.append('maybe you meant "{}"'.format(guess)) + + raise CommandError(' - '.join(msg)) + + command = create_command(cmd_name) + command.parser.print_help() + + return SUCCESS diff --git a/venv/lib/python3.8/site-packages/pip/_internal/commands/install.py b/venv/lib/python3.8/site-packages/pip/_internal/commands/install.py new file mode 100644 index 0000000..70bda2e --- /dev/null +++ b/venv/lib/python3.8/site-packages/pip/_internal/commands/install.py @@ -0,0 +1,691 @@ +# The following comment should be removed at some point in the future. +# It's included for now because without it InstallCommand.run() has a +# couple errors where we have to know req.name is str rather than +# Optional[str] for the InstallRequirement req. +# mypy: strict-optional=False +# mypy: disallow-untyped-defs=False + +from __future__ import absolute_import + +import errno +import logging +import operator +import os +import shutil +import site +from optparse import SUPPRESS_HELP + +from pip._vendor import pkg_resources +from pip._vendor.packaging.utils import canonicalize_name + +from pip._internal.cache import WheelCache +from pip._internal.cli import cmdoptions +from pip._internal.cli.cmdoptions import make_target_python +from pip._internal.cli.req_command import RequirementCommand, with_cleanup +from pip._internal.cli.status_codes import ERROR, SUCCESS +from pip._internal.exceptions import CommandError, InstallationError +from pip._internal.locations import distutils_scheme +from pip._internal.operations.check import check_install_conflicts +from pip._internal.req import install_given_reqs +from pip._internal.req.req_tracker import get_requirement_tracker +from pip._internal.utils.deprecation import deprecated +from pip._internal.utils.distutils_args import parse_distutils_args +from pip._internal.utils.filesystem import test_writable_dir +from pip._internal.utils.misc import ( + ensure_dir, + get_installed_version, + protect_pip_from_modification_on_windows, + write_output, +) +from pip._internal.utils.temp_dir import TempDirectory +from pip._internal.utils.typing import MYPY_CHECK_RUNNING +from pip._internal.utils.virtualenv import virtualenv_no_global +from pip._internal.wheel_builder import build, should_build_for_install_command + +if MYPY_CHECK_RUNNING: + from optparse import Values + from typing import Any, Iterable, List, Optional + + from pip._internal.models.format_control import FormatControl + from pip._internal.req.req_install import InstallRequirement + from pip._internal.wheel_builder import BinaryAllowedPredicate + + +logger = logging.getLogger(__name__) + + +def get_check_binary_allowed(format_control): + # type: (FormatControl) -> BinaryAllowedPredicate + def check_binary_allowed(req): + # type: (InstallRequirement) -> bool + if req.use_pep517: + return True + canonical_name = canonicalize_name(req.name) + allowed_formats = format_control.get_allowed_formats(canonical_name) + return "binary" in allowed_formats + + return check_binary_allowed + + +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. + """ + + usage = """ + %prog [options] [package-index-options] ... + %prog [options] -r [package-index-options] ... + %prog [options] [-e] ... + %prog [options] [-e] ... + %prog [options] ...""" + + 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.' + ) + cmdoptions.add_target_python_options(cmd_opts) + + 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, overwriting them. ' + 'This can break your system if the existing package ' + 'is of a different version or was installed ' + 'with a different package manager!' + ) + + 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.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) + + @with_cleanup + def run(self, options, args): + # type: (Values, List[Any]) -> int + if options.use_user_site and options.target_dir is not None: + raise CommandError("Can not combine '--user' and '--target'") + + cmdoptions.check_install_build_global(options) + upgrade_strategy = "to-satisfy-only" + if options.upgrade: + upgrade_strategy = options.upgrade_strategy + + cmdoptions.check_dist_restriction(options, check_target=True) + + install_options = options.install_options or [] + + options.use_user_site = decide_user_install( + options.use_user_site, + prefix_path=options.prefix_path, + target_dir=options.target_dir, + root_path=options.root_path, + isolated_mode=options.isolated_mode, + ) + + target_temp_dir = None # type: Optional[TempDirectory] + target_temp_dir_path = None # type: Optional[str] + 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 = TempDirectory(kind="target") + target_temp_dir_path = target_temp_dir.path + + global_options = options.global_options or [] + + session = self.get_default_session(options) + + target_python = make_target_python(options) + finder = self._build_package_finder( + options=options, + session=session, + target_python=target_python, + ignore_requires_python=options.ignore_requires_python, + ) + build_delete = (not (options.no_clean or options.build_dir)) + wheel_cache = WheelCache(options.cache_dir, options.format_control) + + req_tracker = self.enter_context(get_requirement_tracker()) + + directory = TempDirectory( + options.build_dir, + delete=build_delete, + kind="install", + globally_managed=True, + ) + + try: + reqs = self.get_requirements( + args, options, finder, session, + check_supported_wheels=not options.target_dir, + ) + + warn_deprecated_install_options( + reqs, options.install_options + ) + + preparer = self.make_requirement_preparer( + temp_build_dir=directory, + options=options, + req_tracker=req_tracker, + session=session, + finder=finder, + use_user_site=options.use_user_site, + ) + resolver = self.make_resolver( + preparer=preparer, + finder=finder, + options=options, + wheel_cache=wheel_cache, + use_user_site=options.use_user_site, + ignore_installed=options.ignore_installed, + ignore_requires_python=options.ignore_requires_python, + force_reinstall=options.force_reinstall, + upgrade_strategy=upgrade_strategy, + use_pep517=options.use_pep517, + ) + + self.trace_basic_info(finder) + + requirement_set = resolver.resolve( + reqs, check_supported_wheels=not options.target_dir + ) + + try: + pip_req = requirement_set.get_requirement("pip") + except KeyError: + modifying_pip = None + else: + # If we're not replacing an already installed pip, + # we're not modifying it. + modifying_pip = pip_req.satisfied_by is None + protect_pip_from_modification_on_windows( + modifying_pip=modifying_pip + ) + + check_binary_allowed = get_check_binary_allowed( + finder.format_control + ) + + reqs_to_build = [ + r for r in requirement_set.requirements.values() + if should_build_for_install_command( + r, check_binary_allowed + ) + ] + + _, build_failures = build( + reqs_to_build, + wheel_cache=wheel_cache, + build_options=[], + global_options=[], + ) + + # If we're using PEP 517, we cannot do a direct install + # so we fail here. + # We don't care about failures building legacy + # requirements, as we'll fall through to a direct + # install for those. + pep517_build_failures = [ + r for r in build_failures if r.use_pep517 + ] + if pep517_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 pep517_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) + + installed.sort(key=operator.attrgetter('name')) + items = [] + for result in installed: + item = result.name + try: + installed_version = get_installed_version( + result.name, working_set=working_set + ) + if installed_version: + item += '-' + installed_version + except Exception: + pass + items.append(item) + installed_desc = ' '.join(items) + if installed_desc: + write_output( + 'Successfully installed %s', installed_desc, + ) + 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 + + if options.target_dir: + self._handle_target_dir( + options.target_dir, target_temp_dir, options.upgrade + ) + + return SUCCESS + + 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 site_packages_writable(**kwargs): + return all( + test_writable_dir(d) for d in set(get_lib_location_guesses(**kwargs)) + ) + + +def decide_user_install( + use_user_site, # type: Optional[bool] + prefix_path=None, # type: Optional[str] + target_dir=None, # type: Optional[str] + root_path=None, # type: Optional[str] + isolated_mode=False, # type: bool +): + # type: (...) -> bool + """Determine whether to do a user install based on the input options. + + If use_user_site is False, no additional checks are done. + If use_user_site is True, it is checked for compatibility with other + options. + If use_user_site is None, the default behaviour depends on the environment, + which is provided by the other arguments. + """ + # In some cases (config from tox), use_user_site can be set to an integer + # rather than a bool, which 'use_user_site is False' wouldn't catch. + if (use_user_site is not None) and (not use_user_site): + logger.debug("Non-user install by explicit request") + return False + + if use_user_site: + if 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." + ) + logger.debug("User install by explicit request") + return True + + # If we are here, user installs have not been explicitly requested/avoided + assert use_user_site is None + + # user install incompatible with --prefix/--target + if prefix_path or target_dir: + logger.debug("Non-user install due to --prefix or --target option") + return False + + # If user installs are not enabled, choose a non-user install + if not site.ENABLE_USER_SITE: + logger.debug("Non-user install because user site-packages disabled") + return False + + # If we have permission for a non-user install, do that, + # otherwise do a user install. + if site_packages_writable(root=root_path, isolated=isolated_mode): + logger.debug("Non-user install because site-packages writeable") + return False + + logger.info("Defaulting to user installation because normal site-packages " + "is not writeable") + return True + + +def warn_deprecated_install_options(requirements, options): + # type: (List[InstallRequirement], Optional[List[str]]) -> None + """If any location-changing --install-option arguments were passed for + requirements or on the command-line, then show a deprecation warning. + """ + def format_options(option_names): + # type: (Iterable[str]) -> List[str] + return ["--{}".format(name.replace("_", "-")) for name in option_names] + + offenders = [] + + for requirement in requirements: + install_options = requirement.install_options + location_options = parse_distutils_args(install_options) + if location_options: + offenders.append( + "{!r} from {}".format( + format_options(location_options.keys()), requirement + ) + ) + + if options: + location_options = parse_distutils_args(options) + if location_options: + offenders.append( + "{!r} from command line".format( + format_options(location_options.keys()) + ) + ) + + if not offenders: + return + + deprecated( + reason=( + "Location-changing options found in --install-option: {}. " + "This configuration may cause unexpected behavior and is " + "unsupported.".format( + "; ".join(offenders) + ) + ), + replacement=( + "using pip-level options like --user, --prefix, --root, and " + "--target" + ), + gone_in="20.2", + issue=7309, + ) + + +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/venv/lib/python3.8/site-packages/pip/_internal/commands/list.py b/venv/lib/python3.8/site-packages/pip/_internal/commands/list.py new file mode 100644 index 0000000..2aa3075 --- /dev/null +++ b/venv/lib/python3.8/site-packages/pip/_internal/commands/list.py @@ -0,0 +1,299 @@ +# The following comment should be removed at some point in the future. +# mypy: disallow-untyped-defs=False + +from __future__ import absolute_import + +import json +import logging + +from pip._vendor import six + +from pip._internal.cli import cmdoptions +from pip._internal.cli.req_command import IndexGroupCommand +from pip._internal.exceptions import CommandError +from pip._internal.index.package_finder import PackageFinder +from pip._internal.models.selection_prefs import SelectionPreferences +from pip._internal.self_outdated_check import make_link_collector +from pip._internal.utils.misc import ( + dist_is_editable, + get_installed_distributions, + tabulate, + write_output, +) +from pip._internal.utils.packaging import get_installer + +logger = logging.getLogger(__name__) + + +class ListCommand(IndexGroupCommand): + """ + List installed packages, including editables. + + Packages are listed in a case-insensitive sorted order. + """ + + usage = """ + %prog [options]""" + + 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(cmdoptions.list_path()) + 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, session): + """ + Create a package finder appropriate to this list command. + """ + link_collector = make_link_collector(session, options=options) + + # Pass allow_yanked=False to ignore yanked versions. + selection_prefs = SelectionPreferences( + allow_yanked=False, + allow_all_prereleases=options.pre, + ) + + return PackageFinder.create( + link_collector=link_collector, + selection_prefs=selection_prefs, + ) + + def run(self, options, args): + if options.outdated and options.uptodate: + raise CommandError( + "Options --outdated and --uptodate cannot be combined.") + + cmdoptions.check_list_path_option(options) + + packages = get_installed_distributions( + local_only=options.local, + user_only=options.user, + editables_only=options.editable, + include_editables=options.include_editable, + paths=options.path, + ) + + # 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): + with self._build_session(options) as session: + finder = self._build_package_finder(options, session) + + def latest_info(dist): + 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.make_candidate_evaluator( + project_name=dist.project_name, + ) + best_candidate = evaluator.sort_best_candidate(all_candidates) + if best_candidate is None: + return None + + remote_version = best_candidate.version + if best_candidate.link.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 + return dist + + for dist in map(latest_info, packages): + if dist is not None: + 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: + write_output("%s==%s (%s)", dist.project_name, + dist.version, dist.location) + else: + write_output("%s==%s", dist.project_name, dist.version) + elif options.list_format == 'json': + write_output(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: + write_output(val) + + +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/venv/lib/python3.8/site-packages/pip/_internal/commands/search.py b/venv/lib/python3.8/site-packages/pip/_internal/commands/search.py new file mode 100644 index 0000000..e5f286e --- /dev/null +++ b/venv/lib/python3.8/site-packages/pip/_internal/commands/search.py @@ -0,0 +1,146 @@ +# The following comment should be removed at some point in the future. +# mypy: disallow-untyped-defs=False + +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.req_command import SessionCommandMixin +from pip._internal.cli.status_codes import NO_MATCHES_FOUND, SUCCESS +from pip._internal.exceptions import CommandError +from pip._internal.models.index import PyPI +from pip._internal.network.xmlrpc import PipXmlrpcTransport +from pip._internal.utils.compat import get_terminal_size +from pip._internal.utils.logging import indent_log +from pip._internal.utils.misc import write_output + +logger = logging.getLogger(__name__) + + +class SearchCommand(Command, SessionCommandMixin): + """Search for PyPI packages whose name or summary contains .""" + + usage = """ + %prog [options] """ + 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 + + session = self.get_default_session(options) + + 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 = '{name_latest:{name_column_width}} - {summary}'.format( + name_latest='{name} ({latest})'.format(**locals()), + **locals()) + try: + write_output(line) + if name in installed_packages: + dist = pkg_resources.get_distribution(name) + with indent_log(): + if dist.version == latest: + write_output('INSTALLED: %s (latest)', dist.version) + else: + write_output('INSTALLED: %s', dist.version) + if parse_version(latest).pre: + write_output('LATEST: %s (pre-release; install' + ' with "pip install --pre")', latest) + else: + write_output('LATEST: %s', latest) + except UnicodeEncodeError: + pass + + +def highest_version(versions): + return max(versions, key=parse_version) diff --git a/venv/lib/python3.8/site-packages/pip/_internal/commands/show.py b/venv/lib/python3.8/site-packages/pip/_internal/commands/show.py new file mode 100644 index 0000000..a61294b --- /dev/null +++ b/venv/lib/python3.8/site-packages/pip/_internal/commands/show.py @@ -0,0 +1,180 @@ +# The following comment should be removed at some point in the future. +# mypy: disallow-untyped-defs=False + +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 +from pip._internal.utils.misc import write_output + +logger = logging.getLogger(__name__) + + +class ShowCommand(Command): + """ + Show information about one or more installed packages. + + The output is in RFC-compliant mail header format. + """ + + usage = """ + %prog [options] ...""" + 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] + missing = sorted( + [name for name, pkg in zip(query, query_names) if pkg not in installed] + ) + if missing: + logger.warning('Package(s) not found: %s', ', '.join(missing)) + + def get_requiring_packages(package_name): + canonical_name = canonicalize_name(package_name) + return [ + pkg.project_name for pkg in pkg_resources.working_set + if canonical_name in + [canonicalize_name(required.name) for required in + pkg.requires()] + ] + + 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()], + 'required_by': get_requiring_packages(dist.project_name) + } + 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 information from installed distributions found. + """ + results_printed = False + for i, dist in enumerate(distributions): + results_printed = True + if i > 0: + write_output("---") + + write_output("Name: %s", dist.get('name', '')) + write_output("Version: %s", dist.get('version', '')) + write_output("Summary: %s", dist.get('summary', '')) + write_output("Home-page: %s", dist.get('home-page', '')) + write_output("Author: %s", dist.get('author', '')) + write_output("Author-email: %s", dist.get('author-email', '')) + write_output("License: %s", dist.get('license', '')) + write_output("Location: %s", dist.get('location', '')) + write_output("Requires: %s", ', '.join(dist.get('requires', []))) + write_output("Required-by: %s", ', '.join(dist.get('required_by', []))) + + if verbose: + write_output("Metadata-Version: %s", + dist.get('metadata-version', '')) + write_output("Installer: %s", dist.get('installer', '')) + write_output("Classifiers:") + for classifier in dist.get('classifiers', []): + write_output(" %s", classifier) + write_output("Entry-points:") + for entry in dist.get('entry_points', []): + write_output(" %s", entry.strip()) + if list_files: + write_output("Files:") + for line in dist.get('files', []): + write_output(" %s", line.strip()) + if "files" not in dist: + write_output("Cannot locate installed-files.txt") + return results_printed diff --git a/venv/lib/python3.8/site-packages/pip/_internal/commands/uninstall.py b/venv/lib/python3.8/site-packages/pip/_internal/commands/uninstall.py new file mode 100644 index 0000000..5db4fb4 --- /dev/null +++ b/venv/lib/python3.8/site-packages/pip/_internal/commands/uninstall.py @@ -0,0 +1,89 @@ +# The following comment should be removed at some point in the future. +# mypy: disallow-untyped-defs=False + +from __future__ import absolute_import + +from pip._vendor.packaging.utils import canonicalize_name + +from pip._internal.cli.base_command import Command +from pip._internal.cli.req_command import SessionCommandMixin +from pip._internal.exceptions import InstallationError +from pip._internal.req import parse_requirements +from pip._internal.req.constructors import ( + install_req_from_line, + install_req_from_parsed_requirement, +) +from pip._internal.utils.misc import protect_pip_from_modification_on_windows + + +class UninstallCommand(Command, SessionCommandMixin): + """ + 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``. + """ + + usage = """ + %prog [options] ... + %prog [options] -r ...""" + + 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): + session = self.get_default_session(options) + + 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 parsed_req in parse_requirements( + filename, + options=options, + session=session): + req = install_req_from_parsed_requirement( + parsed_req, + isolated=options.isolated_mode + ) + 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 {self.name} (see ' + '"pip help {self.name}")'.format(**locals()) + ) + + 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/venv/lib/python3.8/site-packages/pip/_internal/commands/wheel.py b/venv/lib/python3.8/site-packages/pip/_internal/commands/wheel.py new file mode 100644 index 0000000..48f3bfa --- /dev/null +++ b/venv/lib/python3.8/site-packages/pip/_internal/commands/wheel.py @@ -0,0 +1,190 @@ +# -*- coding: utf-8 -*- + +# The following comment should be removed at some point in the future. +# mypy: disallow-untyped-defs=False + +from __future__ import absolute_import + +import logging +import os +import shutil + +from pip._internal.cache import WheelCache +from pip._internal.cli import cmdoptions +from pip._internal.cli.req_command import RequirementCommand, with_cleanup +from pip._internal.exceptions import CommandError +from pip._internal.req.req_tracker import get_requirement_tracker +from pip._internal.utils.misc import ensure_dir, normalize_path +from pip._internal.utils.temp_dir import TempDirectory +from pip._internal.utils.typing import MYPY_CHECK_RUNNING +from pip._internal.wheel_builder import build, should_build_for_wheel_command + +if MYPY_CHECK_RUNNING: + from optparse import Values + from typing import Any, List + + +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. + + """ + + usage = """ + %prog [options] ... + %prog [options] -r ... + %prog [options] [-e] ... + %prog [options] [-e] ... + %prog [options] ...""" + + 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.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) + + @with_cleanup + def run(self, options, args): + # type: (Values, List[Any]) -> None + cmdoptions.check_install_build_global(options) + + session = self.get_default_session(options) + + 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) + + options.wheel_dir = normalize_path(options.wheel_dir) + ensure_dir(options.wheel_dir) + + req_tracker = self.enter_context(get_requirement_tracker()) + + directory = TempDirectory( + options.build_dir, + delete=build_delete, + kind="wheel", + globally_managed=True, + ) + + reqs = self.get_requirements(args, options, finder, session) + + preparer = self.make_requirement_preparer( + temp_build_dir=directory, + options=options, + req_tracker=req_tracker, + session=session, + finder=finder, + wheel_download_dir=options.wheel_dir, + use_user_site=False, + ) + + resolver = self.make_resolver( + preparer=preparer, + finder=finder, + options=options, + wheel_cache=wheel_cache, + ignore_requires_python=options.ignore_requires_python, + use_pep517=options.use_pep517, + ) + + self.trace_basic_info(finder) + + requirement_set = resolver.resolve( + reqs, check_supported_wheels=True + ) + + reqs_to_build = [ + r for r in requirement_set.requirements.values() + if should_build_for_wheel_command(r) + ] + + # build wheels + build_successes, build_failures = build( + reqs_to_build, + wheel_cache=wheel_cache, + build_options=options.build_options or [], + global_options=options.global_options or [], + ) + for req in build_successes: + assert req.link and req.link.is_wheel + assert req.local_file_path + # copy from cache to target directory + try: + shutil.copy(req.local_file_path, options.wheel_dir) + except OSError as e: + logger.warning( + "Building wheel for %s failed: %s", + req.name, e, + ) + build_failures.append(req) + if len(build_failures) != 0: + raise CommandError( + "Failed to build one or more wheels" + ) diff --git a/venv/lib/python3.8/site-packages/pip/_internal/configuration.py b/venv/lib/python3.8/site-packages/pip/_internal/configuration.py new file mode 100644 index 0000000..2648b8a --- /dev/null +++ b/venv/lib/python3.8/site-packages/pip/_internal/configuration.py @@ -0,0 +1,426 @@ +"""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 +""" + +# The following comment should be removed at some point in the future. +# mypy: strict-optional=False + +import locale +import logging +import os +import sys + +from pip._vendor.six.moves import configparser + +from pip._internal.exceptions import ( + ConfigurationError, + ConfigurationFileCouldNotBeLoaded, +) +from pip._internal.utils import appdirs +from pip._internal.utils.compat import WINDOWS, expanduser +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] + if "." not in name: + error_message = ( + "Key does not contain dot separated section and key. " + "Perhaps you wanted to use 'global.{}' instead?" + ).format(name) + raise ConfigurationError(error_message) + 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 +) + + +CONFIG_BASENAME = 'pip.ini' if WINDOWS else 'pip.conf' + + +def get_configuration_files(): + # type: () -> Dict[Kind, List[str]] + 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) + legacy_config_file = os.path.join( + expanduser('~'), + 'pip' if WINDOWS else '.pip', + CONFIG_BASENAME, + ) + new_config_file = os.path.join( + appdirs.user_config_dir("pip"), CONFIG_BASENAME + ) + return { + kinds.GLOBAL: global_config_files, + kinds.SITE: [site_config_file], + kinds.USER: [legacy_config_file, new_config_file], + } + + +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, [] + + config_files = get_configuration_files() + + # at the base we have any global configuration + yield kinds.GLOBAL, config_files[kinds.GLOBAL] + + # 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, config_files[kinds.USER] + + # finally virtualenv configuration first trumping others + yield kinds.SITE, config_files[kinds.SITE] + + 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) + + def __repr__(self): + # type: () -> str + return "{}({!r})".format(self.__class__.__name__, self._dictionary) diff --git a/venv/lib/python3.8/site-packages/pip/_internal/distributions/__init__.py b/venv/lib/python3.8/site-packages/pip/_internal/distributions/__init__.py new file mode 100644 index 0000000..d5c1afc --- /dev/null +++ b/venv/lib/python3.8/site-packages/pip/_internal/distributions/__init__.py @@ -0,0 +1,24 @@ +from pip._internal.distributions.sdist import SourceDistribution +from pip._internal.distributions.wheel import WheelDistribution +from pip._internal.utils.typing import MYPY_CHECK_RUNNING + +if MYPY_CHECK_RUNNING: + from pip._internal.distributions.base import AbstractDistribution + from pip._internal.req.req_install import InstallRequirement + + +def make_distribution_for_install_requirement(install_req): + # type: (InstallRequirement) -> AbstractDistribution + """Returns a Distribution for the given InstallRequirement + """ + # Editable requirements will always be source distributions. They use the + # legacy logic until we create a modern standard for them. + if install_req.editable: + return SourceDistribution(install_req) + + # If it's a wheel, it's a WheelDistribution + if install_req.is_wheel: + return WheelDistribution(install_req) + + # Otherwise, a SourceDistribution + return SourceDistribution(install_req) diff --git a/venv/lib/python3.8/site-packages/pip/_internal/distributions/__pycache__/__init__.cpython-38.pyc b/venv/lib/python3.8/site-packages/pip/_internal/distributions/__pycache__/__init__.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..2b1e096f49144829df1e351e5168f4200ba48f59 GIT binary patch literal 864 zcmaJ<&2G~`5Z;aR<0LH-6(>X`BlY4#?5aRTr3w`V1QigWlnT`sYm?oi+pO2_?mAVf z+~CBU@D#j4AP4T2+3{T2*G z6bqE#2xIHlL@jY5r}Q0BPa07JBi2wyP^*h((u!JTts&f`9ks!4($;4bb*OuS{Pqtx z5&5{-I!SXiW-qulijQ)`QwiC-AI}*Rw~|}$&fcHJ{ns!1Z{p$UU~oKm1<-rXNAN2d zo7?DPsSOcg$S!lPSi+xIiGH)JH3vh0P+b$n%;Rj>d%kS@QucdXdu zSETHMelEjI@i{RpoD&sJQbC#0VKxr;ud?~$J$bNaDBU0Fr@NEnI(f2N%2b9Q!i;CV zTxMiEW3*TD^k!snksdr+*jgBKX_%5kgtW2&9mY`LCXRz_QTPd&vA9}~p;-Ww^R@R} z4cJ~*mrttM#I9piZ9s0|cZ-KW7yO|Ms>kU-mti@O|Kk2ms<^*?9=sf(1GC7uoT@r- z7T!8wop3}nQw@Mc+v_msCIDS^r<#B%dnh+iFh+DC)XtY7|j|*g0e`1hu53wYiUw zT*)$Wstp9_t^c4s`Y-LZr~ZYWI;12=N)1yWH5_utZ)U#XzHGG`2-@dczq3Co2>op) zs||GM2k1(|z!1X%h34N8IB`M;8`%|Z;)NbY+>-||_e3SBhE=;(5w*k*eJfW*J!ymu zE7!!eq!~8B#^LC&b&8)L=Ck@4Vs*Z8>Yn0oli_{TZu|rHqJA5fEy@^4xTcYyN`d0$ z(^t=5k=BT1Rrrg(b+8g4ZX=JTklU>fUj zptvsbHDA8Iz($42mH?O6zmGGPYsU7_tkBEonQ#Dq;yE>zhg?9oV*nX%6?g~-QI(}8 zT(U?zItCTvIR|#6QOs5Qs^CRntRa@cYKDVsa0uZE_;H>o0|l;HVxWU;0yzL)GpcFV zsv{u8MKiMDpy}#E7&OxO4LWuHbpApZy#blH4DF+H+;4kjMHM-h(ua+uPe`1`nvl{3 zmb9JHQ(O#X6V}dgStrEOjgWKn?{P1e@dTjvCRFx-CQu5hmq)z^(|mG&JAJsV8GA5L z-`*W2C&@Q=Z5yS%gI*rzogy`%n9_Vz>cP`RRyjk{^wTQmerwso65~W5er6Ql?iI7{JsNQx#T&Od=0jJG!41Ff)khcw=czirFpwy z2xZ;?AXI~pBx8jza+8qP1r>{x8X+u;2(gJRT{?)$4Z3*tfac?j$=d)#egH$+T5vGp zLV(G;d~BF4CMlN5n;}te9T2LkXF1fMGzloHi;3Z0_|fVaYc!DNGZ@}jKMhJsR?Q)wt?Nyyj=PJuh0HT2 vYi46@=f~ZrQ0ZlL{vcQnR-Cr(TPFIC=F$D$P(&Fj_)Zgk9)=Oa&-L+tH#9M~ literal 0 HcmV?d00001 diff --git a/venv/lib/python3.8/site-packages/pip/_internal/distributions/__pycache__/installed.cpython-38.pyc b/venv/lib/python3.8/site-packages/pip/_internal/distributions/__pycache__/installed.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..7cd3ecd8c0cba78653fddb228a473ee24e7f9e16 GIT binary patch literal 1260 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 Pe0%c&LY8p^>Ck@x$X^l? literal 0 HcmV?d00001 diff --git a/venv/lib/python3.8/site-packages/pip/_internal/distributions/__pycache__/wheel.cpython-38.pyc b/venv/lib/python3.8/site-packages/pip/_internal/distributions/__pycache__/wheel.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..f52575f3a273801636923856266d250425a38671 GIT binary patch literal 1612 zcmaJ>L2n#26t-t(cQ)Hi+D0G&Qjrz)vJ#nzN*hrLp;n+oWsKgn@ZXC#<471RWLm8EwtmDU#?3QsBVZ`#hlLa{eCPhySj}?#c>(WYJhO$W zV1D!K=TDx~y)Pc@eMR@5jYeON9>MI*`eaJAFlwfAVJL6yp%bO)`H>J3{047CTYtjx z6E+nOi;4@KyENJ@fRB674FMxihB?Xtj}7KmII8!cn*%T}&@np3W?+LKu^)%#JvX+|a)o0a{2ctU^#it@ z7Z)DOm6zcm3eX+&4cbS)p{M@+E#e_m9xZ--tQa@M9tlzk%Q&-)OtdQf%(ChU5KD^6 zD)1;#!tQA`6*DodGx{ zpuWV!Lk#@)nUv}rRJMD2H0(AZI5t$BUps=K!==euL`$9C4JkznC%lbKUQ}?hL}a2QUFZoipjJs8k2h0q@Fh)PKBlaul`L) z&$t{>JO~ECCJu0bx~Tj2V(0KpttM{|m4`0OMRQ}PGp{RmTX0as5RW__!%%O)RMVaK z->SJfo)wbQ!YJvb?46e8y7RlSl(|^_Z!dMU1un*+kAg+|@1) None + super(AbstractDistribution, self).__init__() + self.req = req + + @abc.abstractmethod + def get_pkg_resources_distribution(self): + # type: () -> Optional[Distribution] + raise NotImplementedError() + + @abc.abstractmethod + def prepare_distribution_metadata(self, finder, build_isolation): + # type: (PackageFinder, bool) -> None + raise NotImplementedError() diff --git a/venv/lib/python3.8/site-packages/pip/_internal/distributions/installed.py b/venv/lib/python3.8/site-packages/pip/_internal/distributions/installed.py new file mode 100644 index 0000000..0d15bf4 --- /dev/null +++ b/venv/lib/python3.8/site-packages/pip/_internal/distributions/installed.py @@ -0,0 +1,24 @@ +from pip._internal.distributions.base import AbstractDistribution +from pip._internal.utils.typing import MYPY_CHECK_RUNNING + +if MYPY_CHECK_RUNNING: + from typing import Optional + + from pip._vendor.pkg_resources import Distribution + from pip._internal.index.package_finder import PackageFinder + + +class InstalledDistribution(AbstractDistribution): + """Represents an installed package. + + This does not need any preparation as the required information has already + been computed. + """ + + def get_pkg_resources_distribution(self): + # type: () -> Optional[Distribution] + return self.req.satisfied_by + + def prepare_distribution_metadata(self, finder, build_isolation): + # type: (PackageFinder, bool) -> None + pass diff --git a/venv/lib/python3.8/site-packages/pip/_internal/distributions/sdist.py b/venv/lib/python3.8/site-packages/pip/_internal/distributions/sdist.py new file mode 100644 index 0000000..be3d7d9 --- /dev/null +++ b/venv/lib/python3.8/site-packages/pip/_internal/distributions/sdist.py @@ -0,0 +1,104 @@ +import logging + +from pip._internal.build_env import BuildEnvironment +from pip._internal.distributions.base import AbstractDistribution +from pip._internal.exceptions import InstallationError +from pip._internal.utils.subprocess import runner_with_spinner_message +from pip._internal.utils.typing import MYPY_CHECK_RUNNING + +if MYPY_CHECK_RUNNING: + from typing import Set, Tuple + + from pip._vendor.pkg_resources import Distribution + from pip._internal.index.package_finder import PackageFinder + + +logger = logging.getLogger(__name__) + + +class SourceDistribution(AbstractDistribution): + """Represents a source distribution. + + The preparation step for these needs metadata for the packages to be + generated, either using PEP 517 or using the legacy `setup.py egg_info`. + """ + + def get_pkg_resources_distribution(self): + # type: () -> Distribution + return self.req.get_dist() + + def prepare_distribution_metadata(self, finder, build_isolation): + # type: (PackageFinder, bool) -> None + # Load pyproject.toml, to determine whether PEP 517 is to be used + self.req.load_pyproject_toml() + + # Set up the build isolation, if this requirement should be isolated + should_isolate = self.req.use_pep517 and build_isolation + if should_isolate: + self._setup_isolation(finder) + + self.req.prepare_metadata() + + def _setup_isolation(self, finder): + # type: (PackageFinder) -> None + def _raise_conflicts(conflicting_with, conflicting_reqs): + # type: (str, Set[Tuple[str, str]]) -> None + format_string = ( + "Some build dependencies for {requirement} " + "conflict with {conflicting_with}: {description}." + ) + error_message = format_string.format( + requirement=self.req, + conflicting_with=conflicting_with, + description=', '.join( + '{} is incompatible with {}'.format(installed, wanted) + for installed, wanted in sorted(conflicting) + ) + ) + raise InstallationError(error_message) + + # Isolate in a BuildEnvironment and install the build-time + # requirements. + pyproject_requires = self.req.pyproject_requires + assert pyproject_requires is not None + + self.req.build_env = BuildEnvironment() + self.req.build_env.install_requirements( + finder, pyproject_requires, 'overlay', + "Installing build dependencies" + ) + conflicting, missing = self.req.build_env.check_requirements( + self.req.requirements_to_check + ) + if conflicting: + _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. + with self.req.build_env: + runner = runner_with_spinner_message( + "Getting requirements to build wheel" + ) + backend = self.req.pep517_backend + assert backend is not None + with backend.subprocess_runner(runner): + reqs = backend.get_requires_for_build_wheel() + + conflicting, missing = self.req.build_env.check_requirements(reqs) + if conflicting: + _raise_conflicts("the backend dependencies", conflicting) + self.req.build_env.install_requirements( + finder, missing, 'normal', + "Installing backend dependencies" + ) diff --git a/venv/lib/python3.8/site-packages/pip/_internal/distributions/wheel.py b/venv/lib/python3.8/site-packages/pip/_internal/distributions/wheel.py new file mode 100644 index 0000000..bf3482b --- /dev/null +++ b/venv/lib/python3.8/site-packages/pip/_internal/distributions/wheel.py @@ -0,0 +1,36 @@ +from zipfile import ZipFile + +from pip._internal.distributions.base import AbstractDistribution +from pip._internal.utils.typing import MYPY_CHECK_RUNNING +from pip._internal.utils.wheel import pkg_resources_distribution_for_wheel + +if MYPY_CHECK_RUNNING: + from pip._vendor.pkg_resources import Distribution + from pip._internal.index.package_finder import PackageFinder + + +class WheelDistribution(AbstractDistribution): + """Represents a wheel distribution. + + This does not need any preparation as wheels can be directly unpacked. + """ + + def get_pkg_resources_distribution(self): + # type: () -> Distribution + """Loads the metadata from the wheel file into memory and returns a + Distribution that uses it, not relying on the wheel file or + requirement. + """ + # Set as part of preparation during download. + assert self.req.local_file_path + # Wheels are never unnamed. + assert self.req.name + + with ZipFile(self.req.local_file_path, allowZip64=True) as z: + return pkg_resources_distribution_for_wheel( + z, self.req.name, self.req.local_file_path + ) + + def prepare_distribution_metadata(self, finder, build_isolation): + # type: (PackageFinder, bool) -> None + pass diff --git a/venv/lib/python3.8/site-packages/pip/_internal/exceptions.py b/venv/lib/python3.8/site-packages/pip/_internal/exceptions.py new file mode 100644 index 0000000..8ac8548 --- /dev/null +++ b/venv/lib/python3.8/site-packages/pip/_internal/exceptions.py @@ -0,0 +1,308 @@ +"""Exceptions used throughout package""" + +# The following comment should be removed at some point in the future. +# mypy: disallow-untyped-defs=False + +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._vendor.pkg_resources import Distribution + 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 NoneMetadataError(PipError): + """ + Raised when accessing "METADATA" or "PKG-INFO" metadata for a + pip._vendor.pkg_resources.Distribution object and + `dist.has_metadata('METADATA')` returns True but + `dist.get_metadata('METADATA')` returns None (and similarly for + "PKG-INFO"). + """ + + def __init__(self, dist, metadata_name): + # type: (Distribution, str) -> None + """ + :param dist: A Distribution object. + :param metadata_name: The name of the metadata being accessed + (can be "METADATA" or "PKG-INFO"). + """ + self.dist = dist + self.metadata_name = metadata_name + + def __str__(self): + # type: () -> str + # Use `dist` in the error message because its stringification + # includes more information, like the version and location. + return ( + 'None {} metadata found for distribution: {}'.format( + self.metadata_name, self.dist, + ) + ) + + +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 + its link already populated by the resolver's _populate_link(). + + """ + return ' {}'.format(self._requirement_name()) + + def __str__(self): + return '{}\n{}'.format(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 ' {} --hash={}:{}'.format(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 ' {}:\n{}'.format(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 {} {}'.format(next(prefix), e)) + for e in expecteds) + lines.append(' Got {}\n'.format( + self.gots[hash_name].hexdigest())) + 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/venv/lib/python3.8/site-packages/pip/_internal/index/__init__.py b/venv/lib/python3.8/site-packages/pip/_internal/index/__init__.py new file mode 100644 index 0000000..7a17b7b --- /dev/null +++ b/venv/lib/python3.8/site-packages/pip/_internal/index/__init__.py @@ -0,0 +1,2 @@ +"""Index interaction code +""" diff --git a/venv/lib/python3.8/site-packages/pip/_internal/index/__pycache__/__init__.cpython-38.pyc b/venv/lib/python3.8/site-packages/pip/_internal/index/__pycache__/__init__.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..354d1393da8d612305e875406cdf98b37c2ce4bc GIT binary patch literal 242 zcmWIL<>g`k0u|Y=6geRM7{oyaj6jY95EpX*i4=w?h7`tN22G|aanHPz)Cz^nypq(S z#N?99{5*x^{FGEKKTXD4?D6p_`N{F|D;bKIfV#lMuM+)&qRg_yl2rYKE){G623 zqGJ7mWPP*pf-)1MJWHdJloYe1Vhe+`+^Sqt129O>(+|-v$Slw;%_~Su&Q49yEzd|z z&C$(Htjx-{G_C~6>BoZ|k(ZdGp9yl4emqbhvm`!Vub}c4hfQvNN@-529mpS_ftUdR D8>vH3 literal 0 HcmV?d00001 diff --git a/venv/lib/python3.8/site-packages/pip/_internal/index/__pycache__/collector.cpython-38.pyc b/venv/lib/python3.8/site-packages/pip/_internal/index/__pycache__/collector.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..201a341046bc93732a370c74c46eeefe041a9be5 GIT binary patch literal 17564 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 z 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 Okb4>5FBqNt!T$?X5d*~l literal 0 HcmV?d00001 diff --git a/venv/lib/python3.8/site-packages/pip/_internal/index/collector.py b/venv/lib/python3.8/site-packages/pip/_internal/index/collector.py new file mode 100644 index 0000000..e2c800c --- /dev/null +++ b/venv/lib/python3.8/site-packages/pip/_internal/index/collector.py @@ -0,0 +1,661 @@ +""" +The main purpose of this module is to expose LinkCollector.collect_links(). +""" + +import cgi +import functools +import itertools +import logging +import mimetypes +import os +import re +from collections import OrderedDict + +from pip._vendor import html5lib, requests +from pip._vendor.distlib.compat import unescape +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.models.link import Link +from pip._internal.utils.filetypes import ARCHIVE_EXTENSIONS +from pip._internal.utils.misc import pairwise, redact_auth_from_url +from pip._internal.utils.typing import MYPY_CHECK_RUNNING +from pip._internal.utils.urls import path_to_url, url_to_path +from pip._internal.vcs import is_url, vcs + +if MYPY_CHECK_RUNNING: + from typing import ( + Callable, Iterable, List, MutableMapping, Optional, + Protocol, Sequence, Tuple, TypeVar, Union, + ) + import xml.etree.ElementTree + + from pip._vendor.requests import Response + + from pip._internal.models.search_scope import SearchScope + from pip._internal.network.session import PipSession + + HTMLElement = xml.etree.ElementTree.Element + ResponseHeaders = MutableMapping[str, str] + + # Used in the @lru_cache polyfill. + F = TypeVar('F') + + class LruCache(Protocol): + def __call__(self, maxsize=None): + # type: (Optional[int]) -> Callable[[F], F] + raise NotImplementedError + + +logger = logging.getLogger(__name__) + + +# Fallback to noop_lru_cache in Python 2 +# TODO: this can be removed when python 2 support is dropped! +def noop_lru_cache(maxsize=None): + # type: (Optional[int]) -> Callable[[F], F] + def _wrapper(f): + # type: (F) -> F + return f + return _wrapper + + +_lru_cache = getattr(functools, "lru_cache", noop_lru_cache) # type: LruCache + + +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. + """ + for scheme in vcs.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_auth_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 _get_encoding_from_headers(headers): + # type: (ResponseHeaders) -> Optional[str] + """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 _determine_base_url(document, page_url): + # type: (HTMLElement, str) -> str + """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 _clean_url_path_part(part): + # type: (str) -> str + """ + Clean a "part" of a URL path (i.e. after splitting on "@" characters). + """ + # We unquote prior to quoting to make sure nothing is double quoted. + return urllib_parse.quote(urllib_parse.unquote(part)) + + +def _clean_file_url_path(part): + # type: (str) -> str + """ + Clean the first part of a URL path that corresponds to a local + filesystem path (i.e. the first part after splitting on "@" characters). + """ + # We unquote prior to quoting to make sure nothing is double quoted. + # Also, 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. + return urllib_request.pathname2url(urllib_request.url2pathname(part)) + + +# percent-encoded: / +_reserved_chars_re = re.compile('(@|%2F)', re.IGNORECASE) + + +def _clean_url_path(path, is_local_path): + # type: (str, bool) -> str + """ + Clean the path portion of a URL. + """ + if is_local_path: + clean_func = _clean_file_url_path + else: + clean_func = _clean_url_path_part + + # Split on the reserved characters prior to cleaning so that + # revision strings in VCS URLs are properly preserved. + parts = _reserved_chars_re.split(path) + + cleaned_parts = [] + for to_clean, reserved in pairwise(itertools.chain(parts, [''])): + cleaned_parts.append(clean_func(to_clean)) + # Normalize %xx escapes (e.g. %2f -> %2F) + cleaned_parts.append(reserved.upper()) + + return ''.join(cleaned_parts) + + +def _clean_link(url): + # type: (str) -> str + """ + Make sure a link is fully quoted. + For example, if ' ' occurs in the URL, it will be replaced with "%20", + and without double-quoting other characters. + """ + # Split the URL into parts according to the general structure + # `scheme://netloc/path;parameters?query#fragment`. + result = urllib_parse.urlparse(url) + # If the netloc is empty, then the URL refers to a local filesystem path. + is_local_path = not result.netloc + path = _clean_url_path(result.path, is_local_path=is_local_path) + return urllib_parse.urlunparse(result._replace(path=path)) + + +def _create_link_from_element( + anchor, # type: HTMLElement + page_url, # type: str + base_url, # type: str +): + # type: (...) -> Optional[Link] + """ + Convert an anchor element in a simple repository page to a Link. + """ + href = anchor.get("href") + if not href: + return None + + url = _clean_link(urllib_parse.urljoin(base_url, href)) + pyrequire = anchor.get('data-requires-python') + pyrequire = unescape(pyrequire) if pyrequire else None + + yanked_reason = anchor.get('data-yanked') + if yanked_reason: + # This is a unicode string in Python 2 (and 3). + yanked_reason = unescape(yanked_reason) + + link = Link( + url, + comes_from=page_url, + requires_python=pyrequire, + yanked_reason=yanked_reason, + ) + + return link + + +class CacheablePageContent(object): + def __init__(self, page): + # type: (HTMLPage) -> None + assert page.cache_link_parsing + self.page = page + + def __eq__(self, other): + # type: (object) -> bool + return (isinstance(other, type(self)) and + self.page.url == other.page.url) + + def __hash__(self): + # type: () -> int + return hash(self.page.url) + + +def with_cached_html_pages( + fn, # type: Callable[[HTMLPage], Iterable[Link]] +): + # type: (...) -> Callable[[HTMLPage], List[Link]] + """ + Given a function that parses an Iterable[Link] from an HTMLPage, cache the + function's result (keyed by CacheablePageContent), unless the HTMLPage + `page` has `page.cache_link_parsing == False`. + """ + + @_lru_cache(maxsize=None) + def wrapper(cacheable_page): + # type: (CacheablePageContent) -> List[Link] + return list(fn(cacheable_page.page)) + + @functools.wraps(fn) + def wrapper_wrapper(page): + # type: (HTMLPage) -> List[Link] + if page.cache_link_parsing: + return wrapper(CacheablePageContent(page)) + return list(fn(page)) + + return wrapper_wrapper + + +@with_cached_html_pages +def parse_links(page): + # type: (HTMLPage) -> Iterable[Link] + """ + Parse an HTML document, and yield its anchor elements as Link objects. + """ + document = html5lib.parse( + page.content, + transport_encoding=page.encoding, + namespaceHTMLElements=False, + ) + + url = page.url + base_url = _determine_base_url(document, url) + for anchor in document.findall(".//a"): + link = _create_link_from_element( + anchor, + page_url=url, + base_url=base_url, + ) + if link is None: + continue + yield link + + +class HTMLPage(object): + """Represents one page, along with its URL""" + + def __init__( + self, + content, # type: bytes + encoding, # type: Optional[str] + url, # type: str + cache_link_parsing=True, # type: bool + ): + # type: (...) -> None + """ + :param encoding: the encoding to decode the given content. + :param url: the URL from which the HTML was downloaded. + :param cache_link_parsing: whether links parsed from this page's url + should be cached. PyPI index urls should + have this set to False, for example. + """ + self.content = content + self.encoding = encoding + self.url = url + self.cache_link_parsing = cache_link_parsing + + def __str__(self): + # type: () -> str + return redact_auth_from_url(self.url) + + +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 _make_html_page(response, cache_link_parsing=True): + # type: (Response, bool) -> HTMLPage + encoding = _get_encoding_from_headers(response.headers) + return HTMLPage( + response.content, + encoding=encoding, + url=response.url, + cache_link_parsing=cache_link_parsing) + + +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: {}".format(exc)) + except requests.Timeout: + _handle_get_page_fail(link, "timed out") + else: + return _make_html_page(resp, + cache_link_parsing=link.cache_link_parsing) + return None + + +def _remove_duplicate_links(links): + # type: (Iterable[Link]) -> List[Link] + """ + Return a list of links, with duplicates removed and ordering preserved. + """ + # We preserve the ordering when removing duplicates because we can. + return list(OrderedDict.fromkeys(links)) + + +def group_locations(locations, expand_dir=False): + # type: (Sequence[str], bool) -> Tuple[List[str], List[str]] + """ + Divide a list of locations into two groups: "files" (archives) and "urls." + + :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): + # type: (str) -> None + 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 + + +class CollectedLinks(object): + + """ + Encapsulates the return value of a call to LinkCollector.collect_links(). + + The return value includes both URLs to project pages containing package + links, as well as individual package Link objects collected from other + sources. + + This info is stored separately as: + + (1) links from the configured file locations, + (2) links from the configured find_links, and + (3) urls to HTML project pages, as described by the PEP 503 simple + repository API. + """ + + def __init__( + self, + files, # type: List[Link] + find_links, # type: List[Link] + project_urls, # type: List[Link] + ): + # type: (...) -> None + """ + :param files: Links from file locations. + :param find_links: Links from find_links. + :param project_urls: URLs to HTML project pages, as described by + the PEP 503 simple repository API. + """ + self.files = files + self.find_links = find_links + self.project_urls = project_urls + + +class LinkCollector(object): + + """ + Responsible for collecting Link objects from all configured locations, + making network requests as needed. + + The class's main method is its collect_links() method. + """ + + def __init__( + self, + session, # type: PipSession + search_scope, # type: SearchScope + ): + # type: (...) -> None + self.search_scope = search_scope + self.session = session + + @property + def find_links(self): + # type: () -> List[str] + return self.search_scope.find_links + + def fetch_page(self, location): + # type: (Link) -> Optional[HTMLPage] + """ + Fetch an HTML page containing package links. + """ + return _get_html_page(location, session=self.session) + + def collect_links(self, project_name): + # type: (str) -> CollectedLinks + """Find all available links for the given project name. + + :return: All the Link objects (unfiltered), as a CollectedLinks object. + """ + search_scope = self.search_scope + index_locations = search_scope.get_index_urls_locations(project_name) + index_file_loc, index_url_loc = group_locations(index_locations) + fl_file_loc, fl_url_loc = group_locations( + self.find_links, expand_dir=True, + ) + + file_links = [ + Link(url) for url in itertools.chain(index_file_loc, fl_file_loc) + ] + + # We trust every directly linked archive in find_links + find_link_links = [Link(url, '-f') for url in self.find_links] + + # 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 anything that does not have a secure origin. + url_locations = [ + link for link in itertools.chain( + # Mark PyPI indices as "cache_link_parsing == False" -- this + # will avoid caching the result of parsing the page for links. + (Link(url, cache_link_parsing=False) for url in index_url_loc), + (Link(url) for url in fl_url_loc), + ) + if self.session.is_secure_origin(link) + ] + + url_locations = _remove_duplicate_links(url_locations) + lines = [ + '{} location(s) to search for versions of {}:'.format( + len(url_locations), project_name, + ), + ] + for link in url_locations: + lines.append('* {}'.format(link)) + logger.debug('\n'.join(lines)) + + return CollectedLinks( + files=file_links, + find_links=find_link_links, + project_urls=url_locations, + ) diff --git a/venv/lib/python3.8/site-packages/pip/_internal/index/package_finder.py b/venv/lib/python3.8/site-packages/pip/_internal/index/package_finder.py new file mode 100644 index 0000000..e88ad9f --- /dev/null +++ b/venv/lib/python3.8/site-packages/pip/_internal/index/package_finder.py @@ -0,0 +1,1016 @@ +"""Routines related to PyPI, indexes""" + +# The following comment should be removed at some point in the future. +# mypy: strict-optional=False + +from __future__ import absolute_import + +import logging +import re + +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._internal.exceptions import ( + BestVersionAlreadyInstalled, + DistributionNotFound, + InvalidWheelFilename, + UnsupportedWheel, +) +from pip._internal.index.collector import parse_links +from pip._internal.models.candidate import InstallationCandidate +from pip._internal.models.format_control import FormatControl +from pip._internal.models.link import Link +from pip._internal.models.selection_prefs import SelectionPreferences +from pip._internal.models.target_python import TargetPython +from pip._internal.models.wheel import Wheel +from pip._internal.utils.filetypes import WHEEL_EXTENSION +from pip._internal.utils.logging import indent_log +from pip._internal.utils.misc import build_netloc +from pip._internal.utils.packaging import check_requires_python +from pip._internal.utils.typing import MYPY_CHECK_RUNNING +from pip._internal.utils.unpacking import SUPPORTED_EXTENSIONS +from pip._internal.utils.urls import url_to_path + +if MYPY_CHECK_RUNNING: + from typing import ( + FrozenSet, Iterable, List, Optional, Set, Text, Tuple, Union, + ) + + from pip._vendor.packaging.tags import Tag + from pip._vendor.packaging.version import _BaseVersion + + from pip._internal.index.collector import LinkCollector + from pip._internal.models.search_scope import SearchScope + from pip._internal.req import InstallRequirement + from pip._internal.utils.hashes import Hashes + + BuildTag = Union[Tuple[()], Tuple[int, str]] + CandidateSortingKey = ( + Tuple[int, int, int, _BaseVersion, BuildTag, Optional[int]] + ) + + +__all__ = ['FormatControl', 'BestCandidateResult', 'PackageFinder'] + + +logger = logging.getLogger(__name__) + + +def _check_link_requires_python( + link, # type: Link + version_info, # type: Tuple[int, int, int] + ignore_requires_python=False, # type: bool +): + # type: (...) -> bool + """ + Return whether the given Python version is compatible with a link's + "Requires-Python" value. + + :param version_info: A 3-tuple of ints representing the Python + major-minor-micro version to check. + :param ignore_requires_python: Whether to ignore the "Requires-Python" + value if the given Python version isn't compatible. + """ + try: + is_compatible = check_requires_python( + link.requires_python, version_info=version_info, + ) + except specifiers.InvalidSpecifier: + logger.debug( + "Ignoring invalid Requires-Python (%r) for link: %s", + link.requires_python, link, + ) + else: + if not is_compatible: + version = '.'.join(map(str, version_info)) + if not ignore_requires_python: + logger.debug( + 'Link requires a different Python (%s not in: %r): %s', + version, link.requires_python, link, + ) + return False + + logger.debug( + 'Ignoring failed Requires-Python check (%s not in: %r) ' + 'for link: %s', + version, link.requires_python, link, + ) + + return True + + +class LinkEvaluator(object): + + """ + Responsible for evaluating links for a particular project. + """ + + _py_version_re = re.compile(r'-py([123]\.?[0-9]?)$') + + # Don't include an allow_yanked default value to make sure each call + # site considers whether yanked releases are allowed. This also causes + # that decision to be made explicit in the calling code, which helps + # people when reading the code. + def __init__( + self, + project_name, # type: str + canonical_name, # type: str + formats, # type: FrozenSet[str] + target_python, # type: TargetPython + allow_yanked, # type: bool + ignore_requires_python=None, # type: Optional[bool] + ): + # type: (...) -> None + """ + :param project_name: The user supplied package name. + :param canonical_name: The canonical package name. + :param formats: The formats allowed for this package. Should be a set + with 'binary' or 'source' or both in it. + :param target_python: The target Python interpreter to use when + evaluating link compatibility. This is used, for example, to + check wheel compatibility, as well as when checking the Python + version, e.g. the Python version embedded in a link filename + (or egg fragment) and against an HTML link's optional PEP 503 + "data-requires-python" attribute. + :param allow_yanked: Whether files marked as yanked (in the sense + of PEP 592) are permitted to be candidates for install. + :param ignore_requires_python: Whether to ignore incompatible + PEP 503 "data-requires-python" values in HTML links. Defaults + to False. + """ + if ignore_requires_python is None: + ignore_requires_python = False + + self._allow_yanked = allow_yanked + self._canonical_name = canonical_name + self._ignore_requires_python = ignore_requires_python + self._formats = formats + self._target_python = target_python + + self.project_name = project_name + + def evaluate_link(self, link): + # type: (Link) -> Tuple[bool, Optional[Text]] + """ + Determine whether a link is a candidate for installation. + + :return: A tuple (is_candidate, result), where `result` is (1) a + version string if `is_candidate` is True, and (2) if + `is_candidate` is False, an optional string to log the reason + the link fails to qualify. + """ + version = None + if link.is_yanked and not self._allow_yanked: + reason = link.yanked_reason or '' + # Mark this as a unicode string to prevent "UnicodeEncodeError: + # 'ascii' codec can't encode character" in Python 2 when + # the reason contains non-ascii characters. + return (False, u'yanked for reason: {}'.format(reason)) + + if link.egg_fragment: + egg_info = link.egg_fragment + ext = link.ext + else: + egg_info, ext = link.splitext() + if not ext: + return (False, 'not a file') + if ext not in SUPPORTED_EXTENSIONS: + return (False, 'unsupported archive format: {}'.format(ext)) + if "binary" not in self._formats and ext == WHEEL_EXTENSION: + reason = 'No binaries permitted for {}'.format( + self.project_name) + return (False, reason) + if "macosx10" in link.path and ext == '.zip': + return (False, 'macosx10 one') + if ext == WHEEL_EXTENSION: + try: + wheel = Wheel(link.filename) + except InvalidWheelFilename: + return (False, 'invalid wheel filename') + if canonicalize_name(wheel.name) != self._canonical_name: + reason = 'wrong project name (not {})'.format( + self.project_name) + return (False, reason) + + supported_tags = self._target_python.get_tags() + if not wheel.supported(supported_tags): + # Include the wheel's tags in the reason string to + # simplify troubleshooting compatibility issues. + file_tags = wheel.get_formatted_file_tags() + reason = ( + "none of the wheel's tags match: {}".format( + ', '.join(file_tags) + ) + ) + return (False, reason) + + version = wheel.version + + # This should be up by the self.ok_binary check, but see issue 2700. + if "source" not in self._formats and ext != WHEEL_EXTENSION: + reason = 'No sources permitted for {}'.format(self.project_name) + return (False, reason) + + if not version: + version = _extract_version_from_fragment( + egg_info, self._canonical_name, + ) + if not version: + reason = 'Missing project version for {}'.format(self.project_name) + return (False, reason) + + match = self._py_version_re.search(version) + if match: + version = version[:match.start()] + py_version = match.group(1) + if py_version != self._target_python.py_version: + return (False, 'Python version is incorrect') + + supports_python = _check_link_requires_python( + link, version_info=self._target_python.py_version_info, + ignore_requires_python=self._ignore_requires_python, + ) + if not supports_python: + # Return None for the reason text to suppress calling + # _log_skipped_link(). + return (False, None) + + logger.debug('Found link %s, version: %s', link, version) + + return (True, version) + + +def filter_unallowed_hashes( + candidates, # type: List[InstallationCandidate] + hashes, # type: Hashes + project_name, # type: str +): + # type: (...) -> List[InstallationCandidate] + """ + Filter out candidates whose hashes aren't allowed, and return a new + list of candidates. + + If at least one candidate has an allowed hash, then all candidates with + either an allowed hash or no hash specified are returned. Otherwise, + the given candidates are returned. + + Including the candidates with no hash specified when there is a match + allows a warning to be logged if there is a more preferred candidate + with no hash specified. Returning all candidates in the case of no + matches lets pip report the hash of the candidate that would otherwise + have been installed (e.g. permitting the user to more easily update + their requirements file with the desired hash). + """ + if not hashes: + logger.debug( + 'Given no hashes to check %s links for project %r: ' + 'discarding no candidates', + len(candidates), + project_name, + ) + # Make sure we're not returning back the given value. + return list(candidates) + + matches_or_no_digest = [] + # Collect the non-matches for logging purposes. + non_matches = [] + match_count = 0 + for candidate in candidates: + link = candidate.link + if not link.has_hash: + pass + elif link.is_hash_allowed(hashes=hashes): + match_count += 1 + else: + non_matches.append(candidate) + continue + + matches_or_no_digest.append(candidate) + + if match_count: + filtered = matches_or_no_digest + else: + # Make sure we're not returning back the given value. + filtered = list(candidates) + + if len(filtered) == len(candidates): + discard_message = 'discarding no candidates' + else: + discard_message = 'discarding {} non-matches:\n {}'.format( + len(non_matches), + '\n '.join(str(candidate.link) for candidate in non_matches) + ) + + logger.debug( + 'Checked %s links for project %r against %s hashes ' + '(%s matches, %s no digest): %s', + len(candidates), + project_name, + hashes.digest_count, + match_count, + len(matches_or_no_digest) - match_count, + discard_message + ) + + return filtered + + +class CandidatePreferences(object): + + """ + Encapsulates some of the preferences for filtering and sorting + InstallationCandidate objects. + """ + + def __init__( + self, + prefer_binary=False, # type: bool + allow_all_prereleases=False, # type: bool + ): + # type: (...) -> None + """ + :param allow_all_prereleases: Whether to allow all pre-releases. + """ + self.allow_all_prereleases = allow_all_prereleases + self.prefer_binary = prefer_binary + + +class BestCandidateResult(object): + """A collection of candidates, returned by `PackageFinder.find_best_candidate`. + + This class is only intended to be instantiated by CandidateEvaluator's + `compute_best_candidate()` method. + """ + + def __init__( + self, + candidates, # type: List[InstallationCandidate] + applicable_candidates, # type: List[InstallationCandidate] + best_candidate, # type: Optional[InstallationCandidate] + ): + # type: (...) -> None + """ + :param candidates: A sequence of all available candidates found. + :param applicable_candidates: The applicable candidates. + :param best_candidate: The most preferred candidate found, or None + if no applicable candidates were found. + """ + assert set(applicable_candidates) <= set(candidates) + + if best_candidate is None: + assert not applicable_candidates + else: + assert best_candidate in applicable_candidates + + self._applicable_candidates = applicable_candidates + self._candidates = candidates + + self.best_candidate = best_candidate + + def iter_all(self): + # type: () -> Iterable[InstallationCandidate] + """Iterate through all candidates. + """ + return iter(self._candidates) + + def iter_applicable(self): + # type: () -> Iterable[InstallationCandidate] + """Iterate through the applicable candidates. + """ + return iter(self._applicable_candidates) + + +class CandidateEvaluator(object): + + """ + Responsible for filtering and sorting candidates for installation based + on what tags are valid. + """ + + @classmethod + def create( + cls, + project_name, # type: str + target_python=None, # type: Optional[TargetPython] + prefer_binary=False, # type: bool + allow_all_prereleases=False, # type: bool + specifier=None, # type: Optional[specifiers.BaseSpecifier] + hashes=None, # type: Optional[Hashes] + ): + # type: (...) -> CandidateEvaluator + """Create a CandidateEvaluator object. + + :param target_python: The target Python interpreter to use when + checking compatibility. If None (the default), a TargetPython + object will be constructed from the running Python. + :param specifier: An optional object implementing `filter` + (e.g. `packaging.specifiers.SpecifierSet`) to filter applicable + versions. + :param hashes: An optional collection of allowed hashes. + """ + if target_python is None: + target_python = TargetPython() + if specifier is None: + specifier = specifiers.SpecifierSet() + + supported_tags = target_python.get_tags() + + return cls( + project_name=project_name, + supported_tags=supported_tags, + specifier=specifier, + prefer_binary=prefer_binary, + allow_all_prereleases=allow_all_prereleases, + hashes=hashes, + ) + + def __init__( + self, + project_name, # type: str + supported_tags, # type: List[Tag] + specifier, # type: specifiers.BaseSpecifier + prefer_binary=False, # type: bool + allow_all_prereleases=False, # type: bool + hashes=None, # type: Optional[Hashes] + ): + # type: (...) -> None + """ + :param supported_tags: The PEP 425 tags supported by the target + Python in order of preference (most preferred first). + """ + self._allow_all_prereleases = allow_all_prereleases + self._hashes = hashes + self._prefer_binary = prefer_binary + self._project_name = project_name + self._specifier = specifier + self._supported_tags = supported_tags + + def get_applicable_candidates( + self, + candidates, # type: List[InstallationCandidate] + ): + # type: (...) -> List[InstallationCandidate] + """ + Return the applicable candidates from a list of candidates. + """ + # Using None infers from the specifier instead. + allow_prereleases = self._allow_all_prereleases or None + specifier = self._specifier + 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=allow_prereleases, + ) + } + + # Again, converting version to str to deal with debundling. + applicable_candidates = [ + c for c in candidates if str(c.version) in versions + ] + + filtered_applicable_candidates = filter_unallowed_hashes( + candidates=applicable_candidates, + hashes=self._hashes, + project_name=self._project_name, + ) + + return sorted(filtered_applicable_candidates, key=self._sort_key) + + def _sort_key(self, candidate): + # type: (InstallationCandidate) -> CandidateSortingKey + """ + Function to pass as the `key` argument to a call to sorted() to sort + InstallationCandidates by preference. + + Returns a tuple such that tuples sorting as greater using Python's + default comparison operator are more preferred. + + The preference is as follows: + + First and foremost, candidates with allowed (matching) hashes are + always preferred over candidates without matching hashes. This is + because e.g. if the only candidate with an allowed hash is yanked, + we still want to use that candidate. + + Second, excepting hash considerations, candidates that have been + yanked (in the sense of PEP 592) are always less preferred than + candidates that haven't been yanked. Then: + + If not finding wheels, they are 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._supported_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 + """ + valid_tags = self._supported_tags + support_num = len(valid_tags) + build_tag = () # type: BuildTag + binary_preference = 0 + link = candidate.link + if link.is_wheel: + # can raise InvalidWheelFilename + wheel = Wheel(link.filename) + if not wheel.supported(valid_tags): + raise UnsupportedWheel( + "{} is not a supported wheel for this platform. It " + "can't be sorted.".format(wheel.filename) + ) + if self._prefer_binary: + binary_preference = 1 + pri = -(wheel.support_index_min(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) + has_allowed_hash = int(link.is_hash_allowed(self._hashes)) + yank_value = -1 * int(link.is_yanked) # -1 for yanked. + return ( + has_allowed_hash, yank_value, binary_preference, candidate.version, + build_tag, pri, + ) + + def sort_best_candidate( + self, + candidates, # type: List[InstallationCandidate] + ): + # type: (...) -> Optional[InstallationCandidate] + """ + Return the best candidate per the instance's sort order, or None if + no candidate is acceptable. + """ + if not candidates: + return None + + best_candidate = max(candidates, key=self._sort_key) + + # Log a warning per PEP 592 if necessary before returning. + link = best_candidate.link + if link.is_yanked: + reason = link.yanked_reason or '' + msg = ( + # Mark this as a unicode string to prevent + # "UnicodeEncodeError: 'ascii' codec can't encode character" + # in Python 2 when the reason contains non-ascii characters. + u'The candidate selected for download or install is a ' + 'yanked version: {candidate}\n' + 'Reason for being yanked: {reason}' + ).format(candidate=best_candidate, reason=reason) + logger.warning(msg) + + return best_candidate + + def compute_best_candidate( + self, + candidates, # type: List[InstallationCandidate] + ): + # type: (...) -> BestCandidateResult + """ + Compute and return a `BestCandidateResult` instance. + """ + applicable_candidates = self.get_applicable_candidates(candidates) + + best_candidate = self.sort_best_candidate(applicable_candidates) + + return BestCandidateResult( + candidates, + applicable_candidates=applicable_candidates, + best_candidate=best_candidate, + ) + + +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, + link_collector, # type: LinkCollector + target_python, # type: TargetPython + allow_yanked, # type: bool + format_control=None, # type: Optional[FormatControl] + candidate_prefs=None, # type: CandidatePreferences + ignore_requires_python=None, # type: Optional[bool] + ): + # type: (...) -> None + """ + This constructor is primarily meant to be used by the create() class + method and from tests. + + :param format_control: A FormatControl object, used to control + the selection of source packages / binary packages when consulting + the index and links. + :param candidate_prefs: Options to use when creating a + CandidateEvaluator object. + """ + if candidate_prefs is None: + candidate_prefs = CandidatePreferences() + + format_control = format_control or FormatControl(set(), set()) + + self._allow_yanked = allow_yanked + self._candidate_prefs = candidate_prefs + self._ignore_requires_python = ignore_requires_python + self._link_collector = link_collector + self._target_python = target_python + + self.format_control = format_control + + # These are boring links that have already been logged somehow. + self._logged_links = set() # type: Set[Link] + + # Don't include an allow_yanked default value to make sure each call + # site considers whether yanked releases are allowed. This also causes + # that decision to be made explicit in the calling code, which helps + # people when reading the code. + @classmethod + def create( + cls, + link_collector, # type: LinkCollector + selection_prefs, # type: SelectionPreferences + target_python=None, # type: Optional[TargetPython] + ): + # type: (...) -> PackageFinder + """Create a PackageFinder. + + :param selection_prefs: The candidate selection preferences, as a + SelectionPreferences object. + :param target_python: The target Python interpreter to use when + checking compatibility. If None (the default), a TargetPython + object will be constructed from the running Python. + """ + if target_python is None: + target_python = TargetPython() + + candidate_prefs = CandidatePreferences( + prefer_binary=selection_prefs.prefer_binary, + allow_all_prereleases=selection_prefs.allow_all_prereleases, + ) + + return cls( + candidate_prefs=candidate_prefs, + link_collector=link_collector, + target_python=target_python, + allow_yanked=selection_prefs.allow_yanked, + format_control=selection_prefs.format_control, + ignore_requires_python=selection_prefs.ignore_requires_python, + ) + + @property + def search_scope(self): + # type: () -> SearchScope + return self._link_collector.search_scope + + @search_scope.setter + def search_scope(self, search_scope): + # type: (SearchScope) -> None + self._link_collector.search_scope = search_scope + + @property + def find_links(self): + # type: () -> List[str] + return self._link_collector.find_links + + @property + def index_urls(self): + # type: () -> List[str] + return self.search_scope.index_urls + + @property + def trusted_hosts(self): + # type: () -> Iterable[str] + for host_port in self._link_collector.session.pip_trusted_origins: + yield build_netloc(*host_port) + + @property + def allow_all_prereleases(self): + # type: () -> bool + return self._candidate_prefs.allow_all_prereleases + + def set_allow_all_prereleases(self): + # type: () -> None + self._candidate_prefs.allow_all_prereleases = True + + def make_link_evaluator(self, project_name): + # type: (str) -> LinkEvaluator + canonical_name = canonicalize_name(project_name) + formats = self.format_control.get_allowed_formats(canonical_name) + + return LinkEvaluator( + project_name=project_name, + canonical_name=canonical_name, + formats=formats, + target_python=self._target_python, + allow_yanked=self._allow_yanked, + ignore_requires_python=self._ignore_requires_python, + ) + + 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 _log_skipped_link(self, link, reason): + # type: (Link, Text) -> None + if link not in self._logged_links: + # Mark this as a unicode string to prevent "UnicodeEncodeError: + # 'ascii' codec can't encode character" in Python 2 when + # the reason contains non-ascii characters. + # Also, put the link at the end so the reason is more visible + # and because the link string is usually very long. + logger.debug(u'Skipping link: %s: %s', reason, link) + self._logged_links.add(link) + + def get_install_candidate(self, link_evaluator, link): + # type: (LinkEvaluator, Link) -> Optional[InstallationCandidate] + """ + If the link is a candidate for install, convert it to an + InstallationCandidate and return it. Otherwise, return None. + """ + is_candidate, result = link_evaluator.evaluate_link(link) + if not is_candidate: + if result: + self._log_skipped_link(link, reason=result) + return None + + return InstallationCandidate( + name=link_evaluator.project_name, + link=link, + # Convert the Text result to str since InstallationCandidate + # accepts str. + version=str(result), + ) + + def evaluate_links(self, link_evaluator, links): + # type: (LinkEvaluator, Iterable[Link]) -> List[InstallationCandidate] + """ + Convert links that are candidates to InstallationCandidate objects. + """ + candidates = [] + for link in self._sort_links(links): + candidate = self.get_install_candidate(link_evaluator, link) + if candidate is not None: + candidates.append(candidate) + + return candidates + + def process_project_url(self, project_url, link_evaluator): + # type: (Link, LinkEvaluator) -> List[InstallationCandidate] + logger.debug( + 'Fetching project page and analyzing links: %s', project_url, + ) + html_page = self._link_collector.fetch_page(project_url) + if html_page is None: + return [] + + page_links = list(parse_links(html_page)) + + with indent_log(): + package_links = self.evaluate_links( + link_evaluator, + links=page_links, + ) + + return package_links + + def find_all_candidates(self, project_name): + # type: (str) -> List[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 LinkEvaluator.evaluate_link() for details on which files + are accepted. + """ + collected_links = self._link_collector.collect_links(project_name) + + link_evaluator = self.make_link_evaluator(project_name) + + find_links_versions = self.evaluate_links( + link_evaluator, + links=collected_links.find_links, + ) + + page_versions = [] + for project_url in collected_links.project_urls: + package_links = self.process_project_url( + project_url, link_evaluator=link_evaluator, + ) + page_versions.extend(package_links) + + file_versions = self.evaluate_links( + link_evaluator, + links=collected_links.files, + ) + if file_versions: + file_versions.sort(reverse=True) + logger.debug( + 'Local files found: %s', + ', '.join([ + url_to_path(candidate.link.url) + for candidate in file_versions + ]) + ) + + # This is an intentional priority ordering + return file_versions + find_links_versions + page_versions + + def make_candidate_evaluator( + self, + project_name, # type: str + specifier=None, # type: Optional[specifiers.BaseSpecifier] + hashes=None, # type: Optional[Hashes] + ): + # type: (...) -> CandidateEvaluator + """Create a CandidateEvaluator object to use. + """ + candidate_prefs = self._candidate_prefs + return CandidateEvaluator.create( + project_name=project_name, + target_python=self._target_python, + prefer_binary=candidate_prefs.prefer_binary, + allow_all_prereleases=candidate_prefs.allow_all_prereleases, + specifier=specifier, + hashes=hashes, + ) + + def find_best_candidate( + self, + project_name, # type: str + specifier=None, # type: Optional[specifiers.BaseSpecifier] + hashes=None, # type: Optional[Hashes] + ): + # type: (...) -> BestCandidateResult + """Find matches for the given project and specifier. + + :param specifier: An optional object implementing `filter` + (e.g. `packaging.specifiers.SpecifierSet`) to filter applicable + versions. + + :return: A `BestCandidateResult` instance. + """ + candidates = self.find_all_candidates(project_name) + candidate_evaluator = self.make_candidate_evaluator( + project_name=project_name, + specifier=specifier, + hashes=hashes, + ) + return candidate_evaluator.compute_best_candidate(candidates) + + 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 + """ + hashes = req.hashes(trust_internet=False) + best_candidate_result = self.find_best_candidate( + req.name, specifier=req.specifier, hashes=hashes, + ) + best_candidate = best_candidate_result.best_candidate + + 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): + # type: (Iterable[InstallationCandidate]) -> str + # 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(best_candidate_result.iter_all()), + ) + + raise DistributionNotFound( + 'No matching distribution found for {}'.format( + 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(best_candidate_result.iter_applicable()), + ) + raise BestVersionAlreadyInstalled + + logger.debug( + 'Using version %s (newest of versions: %s)', + best_candidate.version, + _format_versions(best_candidate_result.iter_applicable()), + ) + return best_candidate.link + + +def _find_name_version_sep(fragment, canonical_name): + # type: (str, str) -> int + """Find the separator's index based on the package's canonical name. + + :param fragment: A + filename "fragment" (stem) or + egg fragment. + :param canonical_name: 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:: + + >>> fragment = 'foo__bar-1.0' + >>> canonical_name = 'foo-bar' + >>> _find_name_version_sep(fragment, 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(fragment): + if c != "-": + continue + if canonicalize_name(fragment[:i]) == canonical_name: + return i + raise ValueError("{} does not match {}".format(fragment, canonical_name)) + + +def _extract_version_from_fragment(fragment, canonical_name): + # type: (str, str) -> Optional[str] + """Parse the version string from a + filename + "fragment" (stem) or egg fragment. + + :param fragment: 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(fragment, canonical_name) + 1 + except ValueError: + return None + version = fragment[version_start:] + if not version: + return None + return version diff --git a/venv/lib/python3.8/site-packages/pip/_internal/locations.py b/venv/lib/python3.8/site-packages/pip/_internal/locations.py new file mode 100644 index 0000000..0c11553 --- /dev/null +++ b/venv/lib/python3.8/site-packages/pip/_internal/locations.py @@ -0,0 +1,194 @@ +"""Locations where we look for configs, install stuff, etc""" + +# The following comment should be removed at some point in the future. +# mypy: strict-optional=False + +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 distutils.command.install import install as distutils_install_command + +from pip._internal.models.scheme import Scheme +from pip._internal.utils import appdirs +from pip._internal.utils.compat import WINDOWS +from pip._internal.utils.typing import MYPY_CHECK_RUNNING, cast +from pip._internal.utils.virtualenv import running_under_virtualenv + +if MYPY_CHECK_RUNNING: + from typing import Dict, List, Optional, Union + + from distutils.cmd import Command as DistutilsCommand + + +# Application Directories +USER_CACHE_DIR = appdirs.user_cache_dir("pip") + + +def get_major_minor_version(): + # type: () -> str + """ + Return the major-minor version of the current Python as a string, e.g. + "3.7" or "3.10". + """ + return '{}.{}'.format(*sys.version_info) + + +def get_src_prefix(): + # type: () -> str + 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/.. + return 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 + +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') +else: + bin_py = os.path.join(sys.prefix, 'bin') + bin_user = os.path.join(user_site, 'bin') + + # 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' + + +def distutils_scheme( + dist_name, user=False, home=None, root=None, isolated=False, prefix=None +): + # type:(str, bool, str, str, bool, str) -> Dict[str, str] + """ + Return a distutils install scheme + """ + from distutils.dist import Distribution + + dist_args = {'name': dist_name} # type: Dict[str, Union[str, List[str]]] + if isolated: + dist_args["script_args"] = ["--no-user-cfg"] + + d = Distribution(dist_args) + d.parse_config_files() + obj = None # type: Optional[DistutilsCommand] + obj = d.get_command_obj('install', create=True) + assert obj is not None + i = cast(distutils_install_command, obj) + # 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) + assert not (home and prefix), "home={} prefix={}".format(home, prefix) + i.user = user or i.user + if user or home: + i.prefix = "" + i.prefix = prefix or i.prefix + i.home = home or i.home + i.root = root or i.root + i.finalize_options() + + scheme = {} + for key in SCHEME_KEYS: + scheme[key] = getattr(i, 'install_' + key) + + # install_lib specified in setup.cfg should install *everything* + # into there (i.e. it takes precedence over both purelib and + # platlib). Note, i.install_lib is *always* set after + # finalize_options(); we only want to override here if the user + # has explicitly requested it hence going back to the config + if 'install_lib' in d.get_option_dict('install'): + scheme.update(dict(purelib=i.install_lib, platlib=i.install_lib)) + + if running_under_virtualenv(): + scheme['headers'] = os.path.join( + sys.prefix, + 'include', + 'site', + 'python{}'.format(get_major_minor_version()), + 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 + + +def get_scheme( + dist_name, # type: str + user=False, # type: bool + home=None, # type: Optional[str] + root=None, # type: Optional[str] + isolated=False, # type: bool + prefix=None, # type: Optional[str] +): + # type: (...) -> Scheme + """ + Get the "scheme" corresponding to the input parameters. The distutils + documentation provides the context for the available schemes: + https://docs.python.org/3/install/index.html#alternate-installation + + :param dist_name: the name of the package to retrieve the scheme for, used + in the headers scheme path + :param user: indicates to use the "user" scheme + :param home: indicates to use the "home" scheme and provides the base + directory for the same + :param root: root under which other directories are re-based + :param isolated: equivalent to --no-user-cfg, i.e. do not consider + ~/.pydistutils.cfg (posix) or ~/pydistutils.cfg (non-posix) for + scheme paths + :param prefix: indicates to use the "prefix" scheme and provides the + base directory for the same + """ + scheme = distutils_scheme( + dist_name, user, home, root, isolated, prefix + ) + return Scheme( + platlib=scheme["platlib"], + purelib=scheme["purelib"], + headers=scheme["headers"], + scripts=scheme["scripts"], + data=scheme["data"], + ) diff --git a/venv/lib/python3.8/site-packages/pip/_internal/main.py b/venv/lib/python3.8/site-packages/pip/_internal/main.py new file mode 100644 index 0000000..3208d5b --- /dev/null +++ b/venv/lib/python3.8/site-packages/pip/_internal/main.py @@ -0,0 +1,16 @@ +from pip._internal.utils.typing import MYPY_CHECK_RUNNING + +if MYPY_CHECK_RUNNING: + from typing import Optional, List + + +def main(args=None): + # type: (Optional[List[str]]) -> int + """This is preserved for old console scripts that may still be referencing + it. + + For additional details, see https://github.com/pypa/pip/issues/7498. + """ + from pip._internal.utils.entrypoints import _wrapper + + return _wrapper(args) diff --git a/venv/lib/python3.8/site-packages/pip/_internal/models/__init__.py b/venv/lib/python3.8/site-packages/pip/_internal/models/__init__.py new file mode 100644 index 0000000..7855226 --- /dev/null +++ b/venv/lib/python3.8/site-packages/pip/_internal/models/__init__.py @@ -0,0 +1,2 @@ +"""A package that contains models that represent entities. +""" diff --git a/venv/lib/python3.8/site-packages/pip/_internal/models/__pycache__/__init__.cpython-38.pyc b/venv/lib/python3.8/site-packages/pip/_internal/models/__pycache__/__init__.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..dace3644464f3ed762731eee91440099594f42d5 GIT binary patch literal 276 zcmYjMyKVw85cJUmA@K=FF49mOiwHp=MS1){nuLbNa?T4l_-xDe9XdWCf0AFK%_pRz zVh&_KKI#R&?7KD-2Peo#0dEv zCl?2-k^5lgDL6p{RivPaMKQLNU9g08v8|lKO z;hhlSNp@@}J>h>t!i_(mG6`wx)TuTSg3FKO?DNz}F{}z$laKQs^AgtIdHnqA=Y06( z7sE&V$+OYu;phRZ_BrN$E49fh%wk{VRLUzMXkS%K3My6(duOx#Usmsu>&>) zGnc^$`YZAbf3V>CH@jo_D7?G2>dlwe^{ZRKxexE1vepF!6lwDx?(`+NLBD(E@Is_q zgQhsY1~bz&r^P4(d9c6#|1FCnZ}yh!2sb$6Gq}0bL-6udkT&2$eFZVTcLubB+@bY2 zHw}Od)_~n!OA!#?oq(Q#r=lJ?Z+o1>w@wtsea?TFq-wbX7llB8$pacmd zm<1?XGe%o2zmI6z->I_w$)IutDJn5d<62$gjOzibGxgT%I^ITLw9LB6 zsLu>Ct!zD~%9+4wZR61eWF#ams^wdNsKG= z+&ZgIajU-1(>M0>{3fd(-~~R$i+r4y7BcmNH#9!M$DV0?%zIWA(%Q z7@y+%P&&f*^8?Sc`kVY9e*^EM_~sBV;hU0o4Bs3_{V*R#eO%SwLj4FYqFz*W8}z>P zCcU>j+dJMg4r}$dG2%^Lz=(nxaf0i2waW2c-o8C|+rIG5S^H~ZTCKvQ5&2K>%5+>2 zc_>WX`tIj=nDYydqW8}>TfNqMXVtUNo|&KN z_?_u)yW=*NJwE+-(er}oWp{Ncym)?<`eq!z4TgiuGa$nYqM2w0u8 z&TzJoMNRLsO=F#X!TKgKs(1c|7uzdt(Dm%52wOG>HV6!2)ppz5Uh#zAT(!L?9f;rK zb_{4#d(%(f7HjsR8`<6VjxM_y3j67reL8J@x>l*=1Z_$hw?91xoW?>Hk@@Wfhh~dp z!H@iQ6ua$)r`RB~n5E^$FEWyGIX;|9jh4o;w<>eoYr5SamKjC)mi)v)TF7)3y!Zq$ z>nJkKu$aj#R$^IJU^>dWhTkjwm6;JVY8S7G--_3#QHY7d416=z%{B0oYvAvx7=~y= z@9UdviQDnhTLK$h_th`pw*vo*Yp#k)w+A!G}f`m^#H>qFg^^8)z#O8*>KdckUHPZeG2)w9y{ zp|Yc!EL;lwC~kzU&gGxed<5z&c429~laBYqZN$N<6Sh4kY*sG?VZ#lg%P0)L>CLqK z2v}LLF~$y?wwss?$cjiK@Y=F8 z8%3T-O%+*MS~MEn;l#)`eufz$0woC&$YVgAQ5?L>Oq@YagSv+gt!yBDj1)~F8;W&9 z+f<^3h>V_n%U$-Au0k~%L6;L#YQz<#5K(o93~9bphGb-XlHtJz`WjmUhKF+O>%Ui$ z6sIsGk)K950&#sSCJwzFG}An zQKaql$tp6kf$PSgq@x7if;J;9nQ1ArZt(2$j505KQeVmJ-DM~PD98XQYx+W$Mk9#C zF=`mub-^J|<`C3eqMo!UaRNFVz~J{yZz|n?I(&!LkS6Ak6bHy;lP6e$=$9b!B?x~B zLSKSkRr}Iw;$4iXWEDD-jVR$LF{KiE!Ndm`HcGPP#GxX%jn0V7j;52R8vMT5Cal50 ziczc8URZ)QC-zacADJ{MrV(UyEk-=ML zHHP2ILJ15j{+~6CmXO8mmb`L>@)Rcto7*;}_Cx#cBP9mI z?CswcPwox()a{dr+zakpe@+pmQ4?z6ISd6QWEB!{UZT<#j}xjbHzvu&_Mt$8@3@LgOap8|4N;<13^g zG|GcB$KJ3=Z2$@R^|0VNH=di^RC3AG1TB2M!i0^tpnx3|oy1F;kHRnzdtu}c7!oNf ztwL6vm0tCxQu@9JMo3TzMmhzK$YF?+0SO8xiYa^HWP>Om?bGz$7H0++!7=mt-XJ02 zeMz5>fW+DxBy|i=J@phZ5ndX;nrGmd$y>uq54^R-^W?2Pi%#XLEl+22v=z`+;uETE zOr6QKdQqJM_la>VP}>pSQy4`1Qz}KuIq>D=#hLQx2!{s#8^=cj2VU`G48xJ7`v}Oy zDJT@H6*I9t6r`zc@d=ei6px_xN2EmUv&!j`I57v#!LJ7#(ML(qcLxQIKwBO4pz?jT zp~AsU9if}PW>8$eq{qg(F0N2%$>hc|mBC5%#y4>W$37fWM6FLV;J{chYcJUqL1!Cj zEch|51u{`5@MD3Cn89`^98x|^7UpXAo%^$QoI7(egF{(kaj3ZQi-N=5cXH58Q zll4w)W8WUr?C{~l%2a>J3CG%oV%jFd2}av6`fSNWSx0*YWv~i)4#`5wH#6un6w_Gm zPr!ox*pC-&+S>=T;m4uiHf~&EJ0XVSJREpYWc#u0M|2o&;ml0m6zsv{wO4!*wp(7C zj@`m1dllI@Nk=$g`%NF?{r2#B=ZuZxbTZe&hY;1nhv_^R6fb&q!;O-Ykd1Ep-kndX z>4-`}7D000KZ0;@DI_h_5Uc!$SAQ*ZIj`yFV$3C=2P=1a|3%NUSs7D<0qF+-d~BTfvMZ3mBa2`&WFres+F+@ z-NRLj3V;D3lqdi;N!wzo=O_%`WU-aj5e%cAPwNPYH`xNt=Ln2RD|0oW#G)+8H7q7> zP<9>}PNG{~WQ{5XW!gq!Vs)4lC(W_XasG7qWpg#B_K-z=kK!S6zffH5kCOMF1cp#p^_^xj_?)J z#SYuVFZBcBA^N0AH=i;Sgf5Rj4&7i#HcHF@h&1${7?2>$*u_Qa-@(OQP^bGmkfU!Z zlmDD9evR5ZPRscy6tRPAun4&Dh=>gDwmOkw!)q~53PB70D;>`IV>&lLMnC z3?k${eSJiY8{WTGyRjccRop$|(nJt)Wp+>@EQl;+gWor1zHAmI3zjuuWy_WFf$|$BD;fU<;8Er$&jnIs&wna2aq*9%+}^ESm$b!p^X>_|7mLH(AJM zA0{I5vgIVsL=p~G9`Ufpg}asr7nktXgLqvM5J}xNkworxC-6J2=k`2*!&~PrT;Q&9 zH;B{B3q#Ht6&J1LOS30U&UN%<^z;UXAuh4uh`CJL(?(>dE9#1(^dT8)6g-UJKha*0 z7W`$T(o1ndSA*CSJNZl!hdW0Emd%uhYhnhfMHRyfP0ZnQxZCWBV9U#RbITLWwIpO* zq|Kh+ytCcgx_v9Ydn;q?&T9JQ&9!I{eSTB5^|<+SvlsO0{a9X-v-)<2^RT|*?R1m7 zpY6!CCJo|@3&gJ(C5(qDluIX3zi?!ux05>*LP199JWNTYS(@$;o1p798JwT;d}AW) zOGizboF6+*Y8Yx=IIpX1AbO$<1`-VCu)xPTFLW^rR&FI(hYRI_VmKqYHl=)%(#x2q z>I{9bc-jOh@P+|0dMa{-0b>|&K&pt$kpW^60%Ac9(&^Jj#+=e?;Qr^4QotvWm^3-J z(AAOWF<}HpYF*MrN^}i$^T0sv>a9U#cl6Qj=!UyHs@1IAE<%u(p)mB)lv3#$S(TKY zNDZQ#blOx%3f>cxet;8Gg)X{@V41}H=gm+RqEk68O+GgJFAzg*4AT#YSjY&|_sH+a z>#@#^12RI{4vh_4{5I6b#(_rQ(c`bl%mV<`&|v6eOH$0bp#7nL1rWlnlDHzPV)M$- z_(uEX*2o;1%-+z&V4`D_W%khABUsVLrD36sj3FoWZZf&MR%#uR`rwOtJx=OHMY&D4 zUYA8Rp%VpAuHc*fK=9Q4)b+%AKjLxLI3za?ttFKE(n}ewfxQG6gS#J3u_e5@ZjOA7kGyYu29L!5BNPSN*gwNXY!amP!C|Kv2I475lm~ZE% z4B_c`o$ml*Lr$Z9*7rinpZj5-@$?hyNucG}7$SENF|(E1N7!elNniL}T*c`%Qvsg1j=4(mybPGeQv$rK83i899Y0g5 z8gd)q?1$NDm!5;$DT118b4H6=O4Ti8eM{)7km`vJ@4Y|my|?WD|H>{(1g=m{I#6%2 zs_f!whNw+$Rb292NuI)(d1R0>x_Ar%uHO9P3f47KGhYp^OSuu;@vM`u!t$nEyeWs*Myr_w zWZ__tc%4eqn`y~T2#?RB{6xC z8Yl*?iZ?B4@Y3|M+Stf;de9=|KPA*$K}h@(wj4@|ove1bpD9D$c7xoxS2*fxsYSWG Ms#i%Bz`1Dr3m{uoL;wH) literal 0 HcmV?d00001 diff --git a/venv/lib/python3.8/site-packages/pip/_internal/models/__pycache__/index.cpython-38.pyc b/venv/lib/python3.8/site-packages/pip/_internal/models/__pycache__/index.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..8794bdd37e9130b1f1bc6a38902fab02adbeda2f GIT binary patch literal 1198 zcmZuwO>Yx15VgIV%_gLUQs{vXX%9WHhh$NKsznGPfrPkJ6_m?ftnJ!qHr?zR+ew?K zH;|B!_z&r^f5}%)`~@yBb{Y{RM)Ks1XFZ;I&-u8v)<$6455KW(AE6&MST)!~uVAS? za11eAq7hCBMsE=#%=?0v$Gy*9>T{p@tigisBy9k0ve4mxwLYM@{S!{37@LO3q~f`a zR?WK()FS{KrI?|VFr0c{Q6IZ|!qnCOg3`uUXD=R$JD zE3Kj|x=uwd7iATTgsdk8<0|5rD!7cYJm*S9x{i3oL|wqSb_A9$+p%3)yj1l&g1f?|MQL7UN>O@+e#u10Vv=c|Ofs4D z>ymM)5|JmnXJYbnr`q4qjO`w)y~q90Z1n80vy&=0N<<;L<4S-)oORCzoR{6xY&xv> zpG<8ofeAI2RaPdWn(cd=PuG1^cE6{0u>LH+)RwExYMk2IVHeg}Y;s?c**iPvfk-&NL|~kW=brb@f5S1?@R6OGx}}VZ*lI z`mk@q&ZYuzA@;q_ntLy^je*ueJx>y=cCVHv$!yEYt$Iqx}6u<+OmY)T=L47>rxn4YH|8RQv m&V)x$+M<=&+>~}U6TVnb^%C0wZ-eW2x3&l-i2M$`#qS?m6g^r1 literal 0 HcmV?d00001 diff --git a/venv/lib/python3.8/site-packages/pip/_internal/models/__pycache__/link.cpython-38.pyc b/venv/lib/python3.8/site-packages/pip/_internal/models/__pycache__/link.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..4e6ff070c6875ec470f94137809ac68cefdfb6f5 GIT binary patch literal 7140 zcmb_gOLH5?5#AR-00c>irXJM8T1k#9*d!=Xq9Q4F{7`5~u>`XuDz;(Ct0iVgEV&P8 zc0maccu1(St6Y`jm@9eIA-Ck{OD;L&l3S{_a!KXnzo3ir^(>ww4sunN+(BDnkXT4lG=Z%&}B`)RWy|MC`gwyW0H&LFD@Q8cBJ6JyGO_nEBg-?hb z@XEMvcvIym?@;-Wces4mJ5oL(X|wK8Z@N4!;hcNSJ6=AnDmN82%JMH1mgi&JdiexX z?<<9|AE3EXDyVU)W{Z#mnE3qG;^G}^@r&if(*4_?l?r-1B{;Jyk!9DTsKj+tA_cke9O zYcz%QEjz68utIa|f5>0-gdeJk!WHrn{G*n2_VaI*Rh1bm^&(p~SelKzP|7KmVOjj9 z`3TFg(HF|LTFwB@voQ&00gtl@3FiPGUlmWDRxN0W9%?H0)FGb9c9xJ zHv#w7%oG1EnlNudJNl3m$=W6aw=&Cv1S zI`hH3J2?|iX}OY4{32U|$x%Aq@FAL(x`U;)tBT1;ZK>PpmKte0dJCAAVi_%JQ!Nc` zx}}TxmhNkezD8OG+Dw#=Y~ zcbytDST&<6H!;1SE-Kut1&$waW=4T|zAE@?hk&MA*vq97cs!Kr<+|zkVPyN@0FIqV zHJ?_w;8K7z*Hb$*SA)9GX1bh6r$CrtBpiRO*hRP16#Plufo@i<5mf{Kx_MvHnU%ng zY{$nC$^7xXHr?!!q(QHSp*+ebVj-b{`J>tZX%t;1FA|$fcW=uOA;KHzSLElqg;libT zQ*YS*24|Mwc1ROXoJ6n$R~^Jx73N&w+&2^28Pk!@9FR4f5Ed^5KGaRT-y!nfj`|t) zbRsiK1Hne5$)@errKjoRjI>Fj!%kHcZ!D~V2M1g4Zj8-&n59-yg zlnFZzu<`>$uQUvWS4{+)4&!ihL=FwBLEUBKQ__@swW!gFHS<2_1B@sX7Ou@BLWSuC zh+D@8=-HH&P20zs0E>3;R3x@%pmw}g>`Ike6zj<9B1C*%H$O60U7O<6j!bA&$6jG_ zH{|WL5txieTzHN@s12EX$b(T(Lv$Bx2nfN)H^EhABROr-U|?=TI1#F69;rDinoEtP z+n6}=7^R1f0bhsL8uz)dUAKX_+$4LG`FTW{c_AGeA$M0}9iqiF=*2N=j#Kj{ znwM&vwG#a;>!tGQ$y`l1n|8$KHf=Gt8n}#$aIRLFyYjTQd3oNyHXku|WhK0NVbyDT z3m4?G=Fct9)tuUF-LGN);cOPM!QI&nyRjZzyVRh*IcSOCLQyg21q@L)N0CviHN;7% zLHdM~Xp~bqHKXPAw3b%K)wDLHHOKA{VP}>VloQ=Y1C+vh18?{VnwGMzh$*J-V5eGF zcCml?2Gh3aOB0uwzN1DuU}IZiu&?R&l}L}UM-grtWg1gxrz$lMndduBXy?}J&$kM( z(j0%jWuBMujc@#?rA6a(vOp6X)1E4hqKS2ECk0KMB3KhOah7^y^A@2-EDHpd^=o{D zQ)m>Lw`Qnm)lf6oo3$Sey_s)YK#~p^fQ&(#w=@71(3m=muuJu(R%&M7qmkzGtzQ;Y zaSq)tRjJBeH4sC{e$eALpb5#UO3&^|&pxpvT8gD2f~@id5d>|+#^Mpz?JbP z0`z7^P78rKz?=L@_LIDq01)pTm8z0nHL|6(v~VUu)v=!1Rv&6x`j*imU#53PS{f*+ z|4x))+In_XiE`V@AK=~JsV%BQ^wKMJu6YhMk@=h!N0HXwX|Y8ao7F2wUr6M%5&5>q z3!||?WpSJqFuzlav$RV*lc>xJ%55SNq;#B4igIy?K1S;PlX?*Gc>JR<5K6<31BejTu!Od*(|S#8VaVtam(ZQarO+5EmpXon zx8s1UGzqz7pHy0*QtDv-Jg_uPzB9f@Z1!^u*BNdnGtfqR#*AtV=^_xRZsQYpvNb!`v1qERe2V|2t z^`y>411nP4YaUKydzb1II9OB)1oa3flZ5Dx7#UJ}Q}hFBbGnlO_t7;czB+sE5no$- z|NK{9pL_I9VPKAA#%?H?_@Fr^!9-cIrG|%TAH!)*Cl@2QegPt!>a~~c{VIim6Fp}!G-=41alW6!pMVKzMJtql@Yg&jr_@i=JW5ju>njZB zrwY~A|KB-HTm8KICnqQdlKtpc1T9jtXF7+54&DXj;Jl6{8K-X+T}jRP^I_G#*g`Wu zf0=$RU6nuc3s>lC;o^MDV+#dL+n!y$FnjIMyFbyL;zZAtvOvLVp%{3HyBZ5;uPQwG zI+Hbs_RoWJ%c;#Ibx_|=i?Y3H$tV+_l9h*@hTc%^<&F#6J(tjIM}YS}8VYXGC(7Ib>LJ8*y-b0h+({iA(CfcE>(x zIw++{sBR=_0WNLU>AD8RY%i&$#4`G0EeK^Qk5f*Fg($88IdtL^kjI}nVT2>21SDn0}5^7QbJuH25@J3RveNmy4PIh8`fRXn6Y zTs~oUZK><}Mp~AAG7xZQsc%+7B*TL^mK3|kk|L1eOHzw`6p!NkXe9+k*e>9c-m^p^ z8<`H%&swi_kNMz*v6tI5%9ScGK3FF6MOi-Zvygk-XC zx?Mk2S_w>s`vGfFrj~#A2}_P*ot}zRfz-f5uz^=bv2u04<&``+9Unb}-nwTP=SXpfbZMVm1Lh`+(xOj{) z|G^uQAC(l<-{UyDHM@&sfR|A!m6i)<>9|fA5XbeLW#J@TM=fo|dCPiIx7~IRPK7L} z;7mqGV^N^yT{Lk92Naz9qefDkU!@MYjt47~q=J&m>?0bcVVfVp?LrOdMSM(yZc=lL zn%mUep@vM5SmYi-lr+TW)OSm;N&EB{BR<%532=G#M3tT3*YmQ>na~ OR}bJe<0yXf`u_mJf0cLu literal 0 HcmV?d00001 diff --git a/venv/lib/python3.8/site-packages/pip/_internal/models/__pycache__/scheme.cpython-38.pyc b/venv/lib/python3.8/site-packages/pip/_internal/models/__pycache__/scheme.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..24f2c2bd73e7dbdde029c08d37d078182245d5a3 GIT binary patch literal 914 zcmZWn&2AGh5cdA$hZTh@7bFX~L?Y{|g^D7CKuB<_kP4SvEN4B*y7jIX+ew-zH+tel z5U;?a?3K6R#CX$+AZE3n$76du-#7kzaF7wSCl9~L?=d02!erl4oV>x`JwT9TLOu`~ z$@rSc7^1JqB$mlJ8Ks+_*?VKDUpL^WaLyD;c#w3Zd`VU9ywF;BWoqh*5-MBmBQur-!7-Y+Q63S{<=p$>mJ1DQm~JC@NTk;wL%Oj4O5=U&@C z@3?IITe|aTX|!~-5x#VERjQ(-!UA15R6@8wfKG)2DwT!88>>*PnXwr~6V|JlDE$BO z3Hq^PhIxxiF^9a{U-b8C4t}Wm9sceS!Zz9xPPQ>(v`rA>ZHky|dx)tdSbube6b22p zMXM<~3k#i7f{x@l8?LcQfbcYIFF?5d2eCsOemmA!wM3QK zQdl-aK`3U;z;md}pr(<3kC$<)0#oK>6Z`C(`Cntfu8YQ~E?kOVnil>mBtDb^R} z^mrZC7+2VNY@lY9k)R#hJtgdWJLI^iJm&{kg6m%NupjNd&HY_-^4mRuHy)@(1)R4T z=a_DxgMXLvFAJe>RtB8oyW-r2d>y$xxD#R#LU3G=Vi=`K7daax_8tb>zL{QNpS$4R W(IArz$J*hm-PNx{A{UfRhkpPu&iKLr literal 0 HcmV?d00001 diff --git a/venv/lib/python3.8/site-packages/pip/_internal/models/__pycache__/search_scope.cpython-38.pyc b/venv/lib/python3.8/site-packages/pip/_internal/models/__pycache__/search_scope.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..ab0bfd41f036e1b49d13af990c6028e81ec74b22 GIT binary patch literal 3438 zcmb7G-ESMm5x>1J9#0f)$%^eHEpS!b)C6o&NE7EvPy^U$8dMS7NC^-E!R2(jB#*i; z?Jg;cITi(^B58p>^tBHK^;W<3&$-t=^xtWS)!2%cl18_o#&*1%G`r2@O81JAYw^`&rMp5jw!#b0;l!)SweB^L$!v|i zaz?t>KhbENx~Cd-*^LvUdy|q~t+n)Duut1*k#Z#r(kzX_AdV)?PlJTPq?HF;;J|!% z{P0uwuMGm>%UHDZvYBQ)sdndq9F%LE(IAw5Fp>km$FsyA@fcoN{p_oUU-|cc_woJT z`=5WYv-9cBC#Y~c5;DY(FFpncA3+muL&r396}X9X4Q5iE8mHO`=~~pJ)~VLDsZDF} zU!xAK!@omaJW#o`%K{z_cEc=Zli#`?JRhfFkc&|qNG3cvU|yVsfsC>gr9gUllzWlz z!Yu7Y{SlnQlbI)!nzm99qG9Jx4F(wB`_ROnpzCW>qaY8ouQ#SS-iylj8SLg@uP$+&kOw6mjP5ZSQt=cen3&gY1Zn z;e+ULA!0Ad=qP4hlzI=3lk;c{5$TKr-sxpAWn6UfuygMyAHV%p`ujy^UTnN#5B|Pe1MKb-?L& zM`<2}2aMi58ZZ{$JqV76+55i+ol)NLqZH1U25|@c%3{%}0P70`emg%d?NCACbudG` z37vMuBo1+*HHb+XrcJJb(XGC2u$P9=$bEz?F6#5wuTX5G2v5kDm|37E?P~lMEVT&7 zL`~6QhNut22T;-`_ZGv~XTTOL6jCxsGUs`>y@|tEY_%_x zl*DZ{70WoP0=h>D_yO6kEa5kB7D+4eIFkHT6z!Br(8ON@fg{pOyZRbmL%|$oQL6CQ zviUD@#sbeUQC_Qx6__+nF}#HvR$pF$By-8}Z~UL5eFj=Y8#+xlfm>_D)g8mFA?J{3 zz%{d>JH&Zv8#c@|iTTulxyc(p!fhc&@ccQCri!Qsv)_>(p@izE&R?|eG#DGyJav!^ z2u4*W{{1TfN)8 zhzp6hR0S5&ajQb87o_w;%Lq^{2axwKbU>vAnA1@RFpuMTO}TjGB?KcZuR#-cpp)7R z(vQ?^tA4~_+?qu`D z5YHWb88x0)`jQ`zCCU#7^*(aBF(uzCF8^IWG=axhfvWCTFakzzUQDdRsB|}DyoF^+ zzV&C26@_g`?vwX^nxS(w@C~e!;5K9%{}4O00w&bA)r9)?O{C9d=g(k!RSTR(b8_p) zShCZqV=ci;tM8Yt?^ibs7&mqf{0%e`3nYIByDjYI zo>L>VnB$HA6=UAjBZGQ}y%6JW;@BoOtb+?a9!a@@Cu`^seR{#A9i8kujpDO%@;PPPqZ<> zfvhe?H=ox~ZAc;!swJZ3nb#LOoK|?VWmv(jIRGzua gnWeK?#b}Gbc7g9eO2nVuAPsDAIcWfU8is5951T2YMgRZ+ literal 0 HcmV?d00001 diff --git a/venv/lib/python3.8/site-packages/pip/_internal/models/__pycache__/selection_prefs.cpython-38.pyc b/venv/lib/python3.8/site-packages/pip/_internal/models/__pycache__/selection_prefs.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..113b81277d8e2cb5eb51bb7f529a955dc268a61b GIT binary patch literal 1648 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 Xa`%X|3)-tbzu>i4i+k)P-g5sBwYzh` literal 0 HcmV?d00001 diff --git a/venv/lib/python3.8/site-packages/pip/_internal/models/__pycache__/wheel.cpython-38.pyc b/venv/lib/python3.8/site-packages/pip/_internal/models/__pycache__/wheel.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..34a13b9a7daa86f9d43d963e885eefe1afe52a4d GIT binary patch literal 3244 zcmbVO-E-SS5Z6hvEjv#7L5GGyJL*iC#-))%zoreDq|l}x1B6V!pr)-*d}mvUEGc(# zK8*81GDC+4nBgx--su0s(+zJt@h^0kDZ8gwwMokZ^5}fGd$+r{``cYT933rb@SHjM z6a5`#f8t{BF=25XUhx11s?{_~s2-3Sfqx^=+eXbGNHYVoovY>4JQw8Kg<64VtiZ1W zFCSR#Vy#HD+ZruUYe%CND{ULK5vt$UD#h-1_gKuCU|}L`&t7ja7T66xV73=hJLb`v zPZ@CPbtZ(JM0V0*_L|52s3Yvy<8Uf!Ah8sBZ3YuBv0L66v)jxI{jlkj{sP;zg$j|z z1Fs2x$L@w}Uf|QOQPnNbga?6s^vlPKkKG%0=5KuN-g~&PaChN0P|YuVkiLHAJEw+^xJ2XPmw>T zsPU#c&_C@h`L9LAftRRNChCc^-U@n891548^utC}IaAjM9ALm4eSrVsHyEC4%i1pK zkv+1d_jGY80dMRYxZWl|0R6e*T=-S0iJ7IFe5xxs+Oh zIi#G*k0a|nWLf=7;7Z_bEmtNc-u9Jgw%6(8qrpDssY}Js<|NIh0YPR(f~cF4-j3c4~jEQ7P5_)&t5@s zh;kD`-JMFW+GL5F#tY0!!->WYM7@8!qRV`0J^v7;D>o}WuazBti@1xZT9r4Wd-6XO z4;4W?krhFVc%rGo13hxb8g{$)2gGN<^Fg%Ez{P$@*+$y4qLA%3vb1JESo(mnAsv?d z&`S_;S+()%EgxC~mLyfBn*JKpt1NPzI8I}!{8h@1-Gai!HoSHmu=BRBIwyx-3-481 z3>{Jpc)%<)M?oc%1+}i>b9E180xCa`v(V-#%BGyxnx~;#du=6QT5VsPU$ie>xSYZd zI(xmv>MM$)fN6V8&ks=|+%V!$dosn?@YemLWw#^H4xbMV#yQ|tSu?0|zra88f)1PK zJmS;#-3AosH#%Lh~6ai3f+YjHIAqH?% zdO%9567@04RS7bMGgX$391wODm8$$0>mGlL!ULI+6{9gY4cV~&c~FMZP$Au)_mcr4 z4Q}NFQ3p_A`3JNw0p3)U2f~%2{9l;{O@Ew z$#)M8`GnC0_@;y6j;-ExWyy7+$#tL;xw7oKs~s=c=M-F*Ms* None + self.name = name + self.version = parse_version(version) # type: _BaseVersion + self.link = link + + super(InstallationCandidate, self).__init__( + key=(self.name, self.version, self.link), + defining_class=InstallationCandidate + ) + + def __repr__(self): + # type: () -> str + return "".format( + self.name, self.version, self.link, + ) + + def __str__(self): + # type: () -> str + return '{!r} candidate (version {} at {})'.format( + self.name, self.version, self.link, + ) diff --git a/venv/lib/python3.8/site-packages/pip/_internal/models/direct_url.py b/venv/lib/python3.8/site-packages/pip/_internal/models/direct_url.py new file mode 100644 index 0000000..87bd9fe --- /dev/null +++ b/venv/lib/python3.8/site-packages/pip/_internal/models/direct_url.py @@ -0,0 +1,245 @@ +""" PEP 610 """ +import json +import re + +from pip._vendor import six +from pip._vendor.six.moves.urllib import parse as urllib_parse + +from pip._internal.utils.typing import MYPY_CHECK_RUNNING + +if MYPY_CHECK_RUNNING: + from typing import ( + Any, Dict, Iterable, Optional, Type, TypeVar, Union + ) + + T = TypeVar("T") + + +DIRECT_URL_METADATA_NAME = "direct_url.json" +ENV_VAR_RE = re.compile(r"^\$\{[A-Za-z0-9-_]+\}(:\$\{[A-Za-z0-9-_]+\})?$") + +__all__ = [ + "DirectUrl", + "DirectUrlValidationError", + "DirInfo", + "ArchiveInfo", + "VcsInfo", +] + + +class DirectUrlValidationError(Exception): + pass + + +def _get(d, expected_type, key, default=None): + # type: (Dict[str, Any], Type[T], str, Optional[T]) -> Optional[T] + """Get value from dictionary and verify expected type.""" + if key not in d: + return default + value = d[key] + if six.PY2 and expected_type is str: + expected_type = six.string_types # type: ignore + if not isinstance(value, expected_type): + raise DirectUrlValidationError( + "{!r} has unexpected type for {} (expected {})".format( + value, key, expected_type + ) + ) + return value + + +def _get_required(d, expected_type, key, default=None): + # type: (Dict[str, Any], Type[T], str, Optional[T]) -> T + value = _get(d, expected_type, key, default) + if value is None: + raise DirectUrlValidationError("{} must have a value".format(key)) + return value + + +def _exactly_one_of(infos): + # type: (Iterable[Optional[InfoType]]) -> InfoType + infos = [info for info in infos if info is not None] + if not infos: + raise DirectUrlValidationError( + "missing one of archive_info, dir_info, vcs_info" + ) + if len(infos) > 1: + raise DirectUrlValidationError( + "more than one of archive_info, dir_info, vcs_info" + ) + assert infos[0] is not None + return infos[0] + + +def _filter_none(**kwargs): + # type: (Any) -> Dict[str, Any] + """Make dict excluding None values.""" + return {k: v for k, v in kwargs.items() if v is not None} + + +class VcsInfo(object): + name = "vcs_info" + + def __init__( + self, + vcs, # type: str + commit_id, # type: str + requested_revision=None, # type: Optional[str] + resolved_revision=None, # type: Optional[str] + resolved_revision_type=None, # type: Optional[str] + ): + self.vcs = vcs + self.requested_revision = requested_revision + self.commit_id = commit_id + self.resolved_revision = resolved_revision + self.resolved_revision_type = resolved_revision_type + + @classmethod + def _from_dict(cls, d): + # type: (Optional[Dict[str, Any]]) -> Optional[VcsInfo] + if d is None: + return None + return cls( + vcs=_get_required(d, str, "vcs"), + commit_id=_get_required(d, str, "commit_id"), + requested_revision=_get(d, str, "requested_revision"), + resolved_revision=_get(d, str, "resolved_revision"), + resolved_revision_type=_get(d, str, "resolved_revision_type"), + ) + + def _to_dict(self): + # type: () -> Dict[str, Any] + return _filter_none( + vcs=self.vcs, + requested_revision=self.requested_revision, + commit_id=self.commit_id, + resolved_revision=self.resolved_revision, + resolved_revision_type=self.resolved_revision_type, + ) + + +class ArchiveInfo(object): + name = "archive_info" + + def __init__( + self, + hash=None, # type: Optional[str] + ): + self.hash = hash + + @classmethod + def _from_dict(cls, d): + # type: (Optional[Dict[str, Any]]) -> Optional[ArchiveInfo] + if d is None: + return None + return cls(hash=_get(d, str, "hash")) + + def _to_dict(self): + # type: () -> Dict[str, Any] + return _filter_none(hash=self.hash) + + +class DirInfo(object): + name = "dir_info" + + def __init__( + self, + editable=False, # type: bool + ): + self.editable = editable + + @classmethod + def _from_dict(cls, d): + # type: (Optional[Dict[str, Any]]) -> Optional[DirInfo] + if d is None: + return None + return cls( + editable=_get_required(d, bool, "editable", default=False) + ) + + def _to_dict(self): + # type: () -> Dict[str, Any] + return _filter_none(editable=self.editable or None) + + +if MYPY_CHECK_RUNNING: + InfoType = Union[ArchiveInfo, DirInfo, VcsInfo] + + +class DirectUrl(object): + + def __init__( + self, + url, # type: str + info, # type: InfoType + subdirectory=None, # type: Optional[str] + ): + self.url = url + self.info = info + self.subdirectory = subdirectory + + def _remove_auth_from_netloc(self, netloc): + # type: (str) -> str + if "@" not in netloc: + return netloc + user_pass, netloc_no_user_pass = netloc.split("@", 1) + if ( + isinstance(self.info, VcsInfo) and + self.info.vcs == "git" and + user_pass == "git" + ): + return netloc + if ENV_VAR_RE.match(user_pass): + return netloc + return netloc_no_user_pass + + @property + def redacted_url(self): + # type: () -> str + """url with user:password part removed unless it is formed with + environment variables as specified in PEP 610, or it is ``git`` + in the case of a git URL. + """ + purl = urllib_parse.urlsplit(self.url) + netloc = self._remove_auth_from_netloc(purl.netloc) + surl = urllib_parse.urlunsplit( + (purl.scheme, netloc, purl.path, purl.query, purl.fragment) + ) + return surl + + def validate(self): + # type: () -> None + self.from_dict(self.to_dict()) + + @classmethod + def from_dict(cls, d): + # type: (Dict[str, Any]) -> DirectUrl + return DirectUrl( + url=_get_required(d, str, "url"), + subdirectory=_get(d, str, "subdirectory"), + info=_exactly_one_of( + [ + ArchiveInfo._from_dict(_get(d, dict, "archive_info")), + DirInfo._from_dict(_get(d, dict, "dir_info")), + VcsInfo._from_dict(_get(d, dict, "vcs_info")), + ] + ), + ) + + def to_dict(self): + # type: () -> Dict[str, Any] + res = _filter_none( + url=self.redacted_url, + subdirectory=self.subdirectory, + ) + res[self.info.name] = self.info._to_dict() + return res + + @classmethod + def from_json(cls, s): + # type: (str) -> DirectUrl + return cls.from_dict(json.loads(s)) + + def to_json(self): + # type: () -> str + return json.dumps(self.to_dict(), sort_keys=True) diff --git a/venv/lib/python3.8/site-packages/pip/_internal/models/format_control.py b/venv/lib/python3.8/site-packages/pip/_internal/models/format_control.py new file mode 100644 index 0000000..2e13727 --- /dev/null +++ b/venv/lib/python3.8/site-packages/pip/_internal/models/format_control.py @@ -0,0 +1,84 @@ +# The following comment should be removed at some point in the future. +# mypy: strict-optional=False + +from pip._vendor.packaging.utils import canonicalize_name + +from pip._internal.exceptions import CommandError +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[str]], Optional[Set[str]]) -> 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): + # type: (object) -> bool + return self.__dict__ == other.__dict__ + + def __ne__(self, other): + # type: (object) -> bool + return not self.__eq__(other) + + def __repr__(self): + # type: () -> str + return "{}({}, {})".format( + self.__class__.__name__, + self.no_binary, + self.only_binary + ) + + @staticmethod + def handle_mutual_excludes(value, target, other): + # type: (str, Optional[Set[str]], Optional[Set[str]]) -> None + if value.startswith('-'): + raise CommandError( + "--no-binary / --only-binary option requires 1 argument." + ) + 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[str] + 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/venv/lib/python3.8/site-packages/pip/_internal/models/index.py b/venv/lib/python3.8/site-packages/pip/_internal/models/index.py new file mode 100644 index 0000000..ead1efb --- /dev/null +++ b/venv/lib/python3.8/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/venv/lib/python3.8/site-packages/pip/_internal/models/link.py b/venv/lib/python3.8/site-packages/pip/_internal/models/link.py new file mode 100644 index 0000000..df4f8f0 --- /dev/null +++ b/venv/lib/python3.8/site-packages/pip/_internal/models/link.py @@ -0,0 +1,236 @@ +import os +import posixpath +import re + +from pip._vendor.six.moves.urllib import parse as urllib_parse + +from pip._internal.utils.filetypes import WHEEL_EXTENSION +from pip._internal.utils.misc import ( + redact_auth_from_url, + split_auth_from_netloc, + splitext, +) +from pip._internal.utils.models import KeyBasedCompareMixin +from pip._internal.utils.typing import MYPY_CHECK_RUNNING +from pip._internal.utils.urls import path_to_url, url_to_path + +if MYPY_CHECK_RUNNING: + from typing import Optional, Text, Tuple, Union + from pip._internal.index.collector import HTMLPage + from pip._internal.utils.hashes import Hashes + + +class Link(KeyBasedCompareMixin): + """Represents a parsed link from a Package Index's simple URL + """ + + def __init__( + self, + url, # type: str + comes_from=None, # type: Optional[Union[str, HTMLPage]] + requires_python=None, # type: Optional[str] + yanked_reason=None, # type: Optional[Text] + cache_link_parsing=True, # type: bool + ): + # type: (...) -> None + """ + :param url: url of the resource pointed to (href of the link) + :param comes_from: instance of HTMLPage where the link was found, + or string. + :param 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. + :param yanked_reason: the reason the file has been yanked, if the + file has been yanked, or None if the file hasn't been yanked. + This is the value of the "data-yanked" attribute, if present, in + a simple repository HTML link. If the file has been yanked but + no reason was provided, this should be the empty string. See + PEP 592 for more information and the specification. + :param cache_link_parsing: A flag that is used elsewhere to determine + whether resources retrieved from this link + should be cached. PyPI index urls should + generally have this set to False, for + example. + """ + + # url can be a UNC windows share + if url.startswith('\\\\'): + url = path_to_url(url) + + self._parsed_url = urllib_parse.urlsplit(url) + # Store the url as a private attribute to prevent accidentally + # trying to set a new value. + self._url = url + + self.comes_from = comes_from + self.requires_python = requires_python if requires_python else None + self.yanked_reason = yanked_reason + + super(Link, self).__init__(key=url, defining_class=Link) + + self.cache_link_parsing = cache_link_parsing + + def __str__(self): + # type: () -> str + if self.requires_python: + rp = ' (requires-python:{})'.format(self.requires_python) + else: + rp = '' + if self.comes_from: + return '{} (from {}){}'.format( + redact_auth_from_url(self._url), self.comes_from, rp) + else: + return redact_auth_from_url(str(self._url)) + + def __repr__(self): + # type: () -> str + return ''.format(self) + + @property + def url(self): + # type: () -> str + return self._url + + @property + def filename(self): + # type: () -> str + path = self.path.rstrip('/') + name = posixpath.basename(path) + if not name: + # Make sure we don't leak auth information if the netloc + # includes a username and password. + netloc, user_pass = split_auth_from_netloc(self.netloc) + return netloc + + name = urllib_parse.unquote(name) + assert name, ( + 'URL {self._url!r} produced no filename'.format(**locals())) + return name + + @property + def file_path(self): + # type: () -> str + return url_to_path(self.url) + + @property + def scheme(self): + # type: () -> str + return self._parsed_url.scheme + + @property + def netloc(self): + # type: () -> str + """ + This can contain auth information. + """ + return self._parsed_url.netloc + + @property + def path(self): + # type: () -> str + return urllib_parse.unquote(self._parsed_url.path) + + 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 = self._parsed_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: () -> str + return posixpath.basename(self._url.split('#', 1)[0].split('?', 1)[0]) + + @property + def is_file(self): + # type: () -> bool + return self.scheme == 'file' + + def is_existing_dir(self): + # type: () -> bool + return self.is_file and os.path.isdir(self.file_path) + + @property + def is_wheel(self): + # type: () -> bool + return self.ext == WHEEL_EXTENSION + + @property + def is_vcs(self): + # type: () -> bool + from pip._internal.vcs import vcs + + return self.scheme in vcs.all_schemes + + @property + def is_yanked(self): + # type: () -> bool + return self.yanked_reason is not None + + @property + def has_hash(self): + # type: () -> bool + return self.hash_name is not None + + def is_hash_allowed(self, hashes): + # type: (Optional[Hashes]) -> bool + """ + Return True if the link has a hash and it is allowed. + """ + if hashes is None or not self.has_hash: + return False + # Assert non-None so mypy knows self.hash_name and self.hash are str. + assert self.hash_name is not None + assert self.hash is not None + + return hashes.is_hash_allowed(self.hash_name, hex_digest=self.hash) diff --git a/venv/lib/python3.8/site-packages/pip/_internal/models/scheme.py b/venv/lib/python3.8/site-packages/pip/_internal/models/scheme.py new file mode 100644 index 0000000..af07b40 --- /dev/null +++ b/venv/lib/python3.8/site-packages/pip/_internal/models/scheme.py @@ -0,0 +1,25 @@ +""" +For types associated with installation schemes. + +For a general overview of available schemes and their context, see +https://docs.python.org/3/install/index.html#alternate-installation. +""" + + +class Scheme(object): + """A Scheme holds paths which are used as the base directories for + artifacts associated with a Python package. + """ + def __init__( + self, + platlib, # type: str + purelib, # type: str + headers, # type: str + scripts, # type: str + data, # type: str + ): + self.platlib = platlib + self.purelib = purelib + self.headers = headers + self.scripts = scripts + self.data = data diff --git a/venv/lib/python3.8/site-packages/pip/_internal/models/search_scope.py b/venv/lib/python3.8/site-packages/pip/_internal/models/search_scope.py new file mode 100644 index 0000000..7a0008e --- /dev/null +++ b/venv/lib/python3.8/site-packages/pip/_internal/models/search_scope.py @@ -0,0 +1,133 @@ +import itertools +import logging +import os +import posixpath + +from pip._vendor.packaging.utils import canonicalize_name +from pip._vendor.six.moves.urllib import parse as urllib_parse + +from pip._internal.models.index import PyPI +from pip._internal.utils.compat import has_tls +from pip._internal.utils.misc import normalize_path, redact_auth_from_url +from pip._internal.utils.typing import MYPY_CHECK_RUNNING + +if MYPY_CHECK_RUNNING: + from typing import List + + +logger = logging.getLogger(__name__) + + +class SearchScope(object): + + """ + Encapsulates the locations that pip is configured to search. + """ + + @classmethod + def create( + cls, + find_links, # type: List[str] + index_urls, # type: List[str] + ): + # type: (...) -> SearchScope + """ + Create a SearchScope object after normalizing the `find_links`. + """ + # 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 ~... + built_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 + built_find_links.append(link) + + # 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(index_urls, built_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 + + return cls( + find_links=built_find_links, + index_urls=index_urls, + ) + + def __init__( + self, + find_links, # type: List[str] + index_urls, # type: List[str] + ): + # type: (...) -> None + self.find_links = find_links + self.index_urls = index_urls + + def get_formatted_locations(self): + # type: () -> str + lines = [] + redacted_index_urls = [] + if self.index_urls and self.index_urls != [PyPI.simple_url]: + for url in self.index_urls: + + redacted_index_url = redact_auth_from_url(url) + + # Parse the URL + purl = urllib_parse.urlsplit(redacted_index_url) + + # URL is generally invalid if scheme and netloc is missing + # 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 purl.scheme and not purl.netloc: + logger.warning( + 'The index url "{}" seems invalid, ' + 'please provide a scheme.'.format(redacted_index_url)) + + redacted_index_urls.append(redacted_index_url) + + lines.append('Looking in indexes: {}'.format( + ', '.join(redacted_index_urls))) + + if self.find_links: + lines.append( + 'Looking in links: {}'.format(', '.join( + redact_auth_from_url(url) for url in self.find_links)) + ) + return '\n'.join(lines) + + 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): + # type: (str) -> str + 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] diff --git a/venv/lib/python3.8/site-packages/pip/_internal/models/selection_prefs.py b/venv/lib/python3.8/site-packages/pip/_internal/models/selection_prefs.py new file mode 100644 index 0000000..f58fdce --- /dev/null +++ b/venv/lib/python3.8/site-packages/pip/_internal/models/selection_prefs.py @@ -0,0 +1,47 @@ +from pip._internal.utils.typing import MYPY_CHECK_RUNNING + +if MYPY_CHECK_RUNNING: + from typing import Optional + from pip._internal.models.format_control import FormatControl + + +class SelectionPreferences(object): + + """ + Encapsulates the candidate selection preferences for downloading + and installing files. + """ + + # Don't include an allow_yanked default value to make sure each call + # site considers whether yanked releases are allowed. This also causes + # that decision to be made explicit in the calling code, which helps + # people when reading the code. + def __init__( + self, + allow_yanked, # type: bool + allow_all_prereleases=False, # type: bool + format_control=None, # type: Optional[FormatControl] + prefer_binary=False, # type: bool + ignore_requires_python=None, # type: Optional[bool] + ): + # type: (...) -> None + """Create a SelectionPreferences object. + + :param allow_yanked: Whether files marked as yanked (in the sense + of PEP 592) are permitted to be candidates for install. + :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 prefer_binary: Whether to prefer an old, but valid, binary + dist over a new source dist. + :param ignore_requires_python: Whether to ignore incompatible + "Requires-Python" values in links. Defaults to False. + """ + if ignore_requires_python is None: + ignore_requires_python = False + + self.allow_yanked = allow_yanked + self.allow_all_prereleases = allow_all_prereleases + self.format_control = format_control + self.prefer_binary = prefer_binary + self.ignore_requires_python = ignore_requires_python diff --git a/venv/lib/python3.8/site-packages/pip/_internal/models/target_python.py b/venv/lib/python3.8/site-packages/pip/_internal/models/target_python.py new file mode 100644 index 0000000..84f1c20 --- /dev/null +++ b/venv/lib/python3.8/site-packages/pip/_internal/models/target_python.py @@ -0,0 +1,110 @@ +import sys + +from pip._internal.utils.compatibility_tags import ( + get_supported, + version_info_to_nodot, +) +from pip._internal.utils.misc import normalize_version_info +from pip._internal.utils.typing import MYPY_CHECK_RUNNING + +if MYPY_CHECK_RUNNING: + from typing import List, Optional, Tuple + + from pip._vendor.packaging.tags import Tag + + +class TargetPython(object): + + """ + Encapsulates the properties of a Python interpreter one is targeting + for a package install, download, etc. + """ + + def __init__( + self, + platform=None, # type: Optional[str] + py_version_info=None, # type: Optional[Tuple[int, ...]] + abi=None, # type: Optional[str] + implementation=None, # type: Optional[str] + ): + # type: (...) -> None + """ + :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 py_version_info: An optional tuple of ints representing the + Python version information to use (e.g. `sys.version_info[:3]`). + This can have length 1, 2, or 3 when provided. + :param abi: A string or None. This is passed to compatibility_tags.py's + get_supported() function as is. + :param implementation: A string or None. This is passed to + compatibility_tags.py's get_supported() function as is. + """ + # Store the given py_version_info for when we call get_supported(). + self._given_py_version_info = py_version_info + + if py_version_info is None: + py_version_info = sys.version_info[:3] + else: + py_version_info = normalize_version_info(py_version_info) + + py_version = '.'.join(map(str, py_version_info[:2])) + + self.abi = abi + self.implementation = implementation + self.platform = platform + self.py_version = py_version + self.py_version_info = py_version_info + + # This is used to cache the return value of get_tags(). + self._valid_tags = None # type: Optional[List[Tag]] + + def format_given(self): + # type: () -> str + """ + Format the given, non-None attributes for display. + """ + display_version = None + if self._given_py_version_info is not None: + display_version = '.'.join( + str(part) for part in self._given_py_version_info + ) + + key_values = [ + ('platform', self.platform), + ('version_info', display_version), + ('abi', self.abi), + ('implementation', self.implementation), + ] + return ' '.join( + '{}={!r}'.format(key, value) for key, value in key_values + if value is not None + ) + + def get_tags(self): + # type: () -> List[Tag] + """ + Return the supported PEP 425 tags to check wheel candidates against. + + The tags are returned in order of preference (most preferred first). + """ + if self._valid_tags is None: + # Pass versions=None if no py_version_info was given since + # versions=None uses special default logic. + py_version_info = self._given_py_version_info + if py_version_info is None: + version = None + else: + version = version_info_to_nodot(py_version_info) + + tags = get_supported( + version=version, + platform=self.platform, + abi=self.abi, + impl=self.implementation, + ) + self._valid_tags = tags + + return self._valid_tags diff --git a/venv/lib/python3.8/site-packages/pip/_internal/models/wheel.py b/venv/lib/python3.8/site-packages/pip/_internal/models/wheel.py new file mode 100644 index 0000000..4d4068f --- /dev/null +++ b/venv/lib/python3.8/site-packages/pip/_internal/models/wheel.py @@ -0,0 +1,78 @@ +"""Represents a wheel file and provides access to the various parts of the +name that have meaning. +""" +import re + +from pip._vendor.packaging.tags import Tag + +from pip._internal.exceptions import InvalidWheelFilename +from pip._internal.utils.typing import MYPY_CHECK_RUNNING + +if MYPY_CHECK_RUNNING: + from typing import List + + +class Wheel(object): + """A wheel file""" + + wheel_file_re = re.compile( + r"""^(?P(?P.+?)-(?P.*?)) + ((-(?P\d[^-]*?))?-(?P.+?)-(?P.+?)-(?P.+?) + \.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( + "{} is not a valid wheel filename.".format(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 = { + Tag(x, y, z) for x in self.pyversions + for y in self.abis for z in self.plats + } + + def get_formatted_file_tags(self): + # type: () -> List[str] + """Return the wheel's tags as a sorted list of strings.""" + return sorted(str(tag) for tag in self.file_tags) + + def support_index_min(self, tags): + # type: (List[Tag]) -> int + """Return the lowest index that one of the wheel's file_tag combinations + achieves in the given list of supported tags. + + For example, if there are 8 supported tags and one of the file tags + is first in the list, then return 0. + + :param tags: the PEP 425 tags to check the wheel against, in order + with most preferred first. + + :raises ValueError: If none of the wheel's file tags match one of + the supported tags. + """ + return min(tags.index(tag) for tag in self.file_tags if tag in tags) + + def supported(self, tags): + # type: (List[Tag]) -> bool + """Return whether the wheel is compatible with one of the given tags. + + :param tags: the PEP 425 tags to check the wheel against. + """ + return not self.file_tags.isdisjoint(tags) diff --git a/venv/lib/python3.8/site-packages/pip/_internal/network/__init__.py b/venv/lib/python3.8/site-packages/pip/_internal/network/__init__.py new file mode 100644 index 0000000..b51bde9 --- /dev/null +++ b/venv/lib/python3.8/site-packages/pip/_internal/network/__init__.py @@ -0,0 +1,2 @@ +"""Contains purely network-related utilities. +""" diff --git a/venv/lib/python3.8/site-packages/pip/_internal/network/__pycache__/__init__.cpython-38.pyc b/venv/lib/python3.8/site-packages/pip/_internal/network/__pycache__/__init__.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..7cf5edc955c8a28dca5d40d64759d791bee7ffad GIT binary patch literal 264 zcmYjM!AiqG5Z&}*DfAPB9J~}sr$j9lLE35`MtAAX&ZCj1~Nl# zDfKSY1sjzJ^EPRtlcu!V`Gyi`IHo)r3X=PGpK&4IgfEF^n^i+9nEC8Th*kB)4>u Ta_NWt^rmsx8oC|7`(cU$S^Z2~ literal 0 HcmV?d00001 diff --git a/venv/lib/python3.8/site-packages/pip/_internal/network/__pycache__/auth.cpython-38.pyc b/venv/lib/python3.8/site-packages/pip/_internal/network/__pycache__/auth.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..566adca948fd54dea900be14a4d54910b27ce65c GIT binary patch literal 7037 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 NnI*+o<^|Ke@@Gr|aC`s& literal 0 HcmV?d00001 diff --git a/venv/lib/python3.8/site-packages/pip/_internal/network/__pycache__/cache.cpython-38.pyc b/venv/lib/python3.8/site-packages/pip/_internal/network/__pycache__/cache.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..582ba404800f0e478f1ad1924921de9eb03fce4f GIT binary patch literal 2743 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 WHG$S6X2o({_d4Dgcfq|hJM}+~3ueIp literal 0 HcmV?d00001 diff --git a/venv/lib/python3.8/site-packages/pip/_internal/network/__pycache__/session.cpython-38.pyc b/venv/lib/python3.8/site-packages/pip/_internal/network/__pycache__/session.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..394d13bbb8a3cba429b8c3c4f51b69eb46a2296d GIT binary patch literal 9269 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 F{{Tp*zLx+1 literal 0 HcmV?d00001 diff --git a/venv/lib/python3.8/site-packages/pip/_internal/network/auth.py b/venv/lib/python3.8/site-packages/pip/_internal/network/auth.py new file mode 100644 index 0000000..94da3d4 --- /dev/null +++ b/venv/lib/python3.8/site-packages/pip/_internal/network/auth.py @@ -0,0 +1,298 @@ +"""Network Authentication Helpers + +Contains interface (MultiDomainBasicAuth) and associated glue code for +providing credentials in the context of network requests. +""" + +# The following comment should be removed at some point in the future. +# mypy: disallow-untyped-defs=False + +import logging + +from pip._vendor.requests.auth import AuthBase, HTTPBasicAuth +from pip._vendor.requests.utils import get_netrc_auth +from pip._vendor.six.moves.urllib import parse as urllib_parse + +from pip._internal.utils.misc import ( + ask, + ask_input, + ask_password, + remove_auth_from_url, + split_auth_netloc_from_url, +) +from pip._internal.utils.typing import MYPY_CHECK_RUNNING + +if MYPY_CHECK_RUNNING: + from optparse import Values + from typing import Dict, Optional, Tuple + + from pip._internal.vcs.versioncontrol import AuthInfo + + Credentials = Tuple[str, str, str] + +logger = logging.getLogger(__name__) + +try: + import keyring # noqa +except ImportError: + keyring = None +except Exception as exc: + logger.warning( + "Keyring is skipped due to an exception: %s", str(exc), + ) + keyring = None + + +def get_keyring_auth(url, username): + """Return the tuple auth for a given url from keyring.""" + if not url or not keyring: + return None + + try: + try: + get_credential = keyring.get_credential + except AttributeError: + pass + else: + logger.debug("Getting credentials from keyring for %s", url) + cred = get_credential(url, username) + if cred is not None: + return cred.username, cred.password + return None + + if username: + logger.debug("Getting password from keyring for %s", url) + password = keyring.get_password(url, username) + if password: + return username, password + + except Exception as exc: + logger.warning( + "Keyring is skipped due to an exception: %s", str(exc), + ) + + +class MultiDomainBasicAuth(AuthBase): + + def __init__(self, prompting=True, index_urls=None): + # type: (bool, Optional[Values]) -> None + self.prompting = prompting + self.index_urls = index_urls + self.passwords = {} # type: Dict[str, AuthInfo] + # When the user is prompted to enter credentials and keyring is + # available, we will offer to save them. If the user accepts, + # this value is set to the credentials they entered. After the + # request authenticates, the caller should call + # ``save_credentials`` to save these. + self._credentials_to_save = None # type: Optional[Credentials] + + def _get_index_url(self, url): + """Return the original index URL matching the requested URL. + + Cached or dynamically generated credentials may work against + the original index URL rather than just the netloc. + + The provided url should have had its username and password + removed already. If the original index url had credentials then + they will be included in the return value. + + Returns None if no matching index was found, or if --no-index + was specified by the user. + """ + if not url or not self.index_urls: + return None + + for u in self.index_urls: + prefix = remove_auth_from_url(u).rstrip("/") + "/" + if url.startswith(prefix): + return u + + def _get_new_credentials(self, original_url, allow_netrc=True, + allow_keyring=True): + """Find and return credentials for the specified URL.""" + # Split the credentials and netloc from the url. + url, netloc, url_user_password = split_auth_netloc_from_url( + original_url, + ) + + # Start with the credentials embedded in the url + username, password = url_user_password + if username is not None and password is not None: + logger.debug("Found credentials in url for %s", netloc) + return url_user_password + + # Find a matching index url for this request + index_url = self._get_index_url(url) + if index_url: + # Split the credentials from the url. + index_info = split_auth_netloc_from_url(index_url) + if index_info: + index_url, _, index_url_user_password = index_info + logger.debug("Found index url %s", index_url) + + # If an index URL was found, try its embedded credentials + if index_url and index_url_user_password[0] is not None: + username, password = index_url_user_password + if username is not None and password is not None: + logger.debug("Found credentials in index url for %s", netloc) + return index_url_user_password + + # Get creds from netrc if we still don't have them + if allow_netrc: + netrc_auth = get_netrc_auth(original_url) + if netrc_auth: + logger.debug("Found credentials in netrc for %s", netloc) + return netrc_auth + + # If we don't have a password and keyring is available, use it. + if allow_keyring: + # The index url is more specific than the netloc, so try it first + kr_auth = ( + get_keyring_auth(index_url, username) or + get_keyring_auth(netloc, username) + ) + if kr_auth: + logger.debug("Found credentials in keyring for %s", netloc) + return kr_auth + + return username, password + + def _get_url_and_credentials(self, original_url): + """Return the credentials to use for the provided URL. + + If allowed, netrc and keyring may be used to obtain the + correct credentials. + + Returns (url_without_credentials, username, password). Note + that even if the original URL contains credentials, this + function may return a different username and password. + """ + url, netloc, _ = split_auth_netloc_from_url(original_url) + + # Use any stored credentials that we have for this netloc + username, password = self.passwords.get(netloc, (None, None)) + + if username is None and password is None: + # No stored credentials. Acquire new credentials without prompting + # the user. (e.g. from netrc, keyring, or the URL itself) + username, password = self._get_new_credentials(original_url) + + if username is not None or password is not None: + # Convert the username and password if they're None, so that + # this netloc will show up as "cached" in the conditional above. + # Further, HTTPBasicAuth doesn't accept None, so it makes sense to + # cache the value that is going to be used. + username = username or "" + password = password or "" + + # Store any acquired credentials. + self.passwords[netloc] = (username, password) + + assert ( + # Credentials were found + (username is not None and password is not None) or + # Credentials were not found + (username is None and password is None) + ), "Could not load credentials from url: {}".format(original_url) + + return url, username, password + + def __call__(self, req): + # Get credentials for this request + url, username, password = self._get_url_and_credentials(req.url) + + # Set the url of the request to the url without any credentials + req.url = url + + if username is not None and password is not None: + # Send the basic auth with this request + req = HTTPBasicAuth(username, password)(req) + + # Attach a hook to handle 401 responses + req.register_hook("response", self.handle_401) + + return req + + # Factored out to allow for easy patching in tests + def _prompt_for_password(self, netloc): + username = ask_input("User for {}: ".format(netloc)) + if not username: + return None, None + auth = get_keyring_auth(netloc, username) + if auth: + return auth[0], auth[1], False + password = ask_password("Password: ") + return username, password, True + + # Factored out to allow for easy patching in tests + def _should_save_password_to_keyring(self): + if not keyring: + return False + return ask("Save credentials to keyring [y/N]: ", ["y", "n"]) == "y" + + 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, password, save = self._prompt_for_password(parsed.netloc) + + # Store the new username and password to use for future requests + self._credentials_to_save = None + if username is not None and password is not None: + self.passwords[parsed.netloc] = (username, password) + + # Prompt to save the password to keyring + if save and self._should_save_password_to_keyring(): + self._credentials_to_save = (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) + + # On successful request, save the credentials that were used to + # keyring. (Note that if the user responded "no" above, this member + # is not set and nothing will be saved.) + if self._credentials_to_save: + req.register_hook("response", self.save_credentials) + + # 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): + """Response callback to warn about incorrect credentials.""" + if resp.status_code == 401: + logger.warning( + '401 Error, Credentials not correct for %s', resp.request.url, + ) + + def save_credentials(self, resp, **kwargs): + """Response callback to save credentials on success.""" + assert keyring is not None, "should never reach here without keyring" + if not keyring: + return + + creds = self._credentials_to_save + self._credentials_to_save = None + if creds and resp.status_code < 400: + try: + logger.info('Saving credentials to keyring') + keyring.set_password(*creds) + except Exception: + logger.exception('Failed to save credentials') diff --git a/venv/lib/python3.8/site-packages/pip/_internal/network/cache.py b/venv/lib/python3.8/site-packages/pip/_internal/network/cache.py new file mode 100644 index 0000000..c9386e1 --- /dev/null +++ b/venv/lib/python3.8/site-packages/pip/_internal/network/cache.py @@ -0,0 +1,81 @@ +"""HTTP cache implementation. +""" + +# The following comment should be removed at some point in the future. +# mypy: disallow-untyped-defs=False + +import os +from contextlib import contextmanager + +from pip._vendor.cachecontrol.cache import BaseCache +from pip._vendor.cachecontrol.caches import FileCache +from pip._vendor.requests.models import Response + +from pip._internal.utils.filesystem import adjacent_tmp_file, replace +from pip._internal.utils.misc import ensure_dir +from pip._internal.utils.typing import MYPY_CHECK_RUNNING + +if MYPY_CHECK_RUNNING: + from typing import Optional + + +def is_from_cache(response): + # type: (Response) -> bool + return getattr(response, "from_cache", False) + + +@contextmanager +def suppressed_cache_errors(): + """If we can't access the cache then we can just skip caching and process + requests as if caching wasn't enabled. + """ + try: + yield + except (OSError, IOError): + pass + + +class SafeFileCache(BaseCache): + """ + A file based cache which is safe to use even when the target directory may + not be accessible or writable. + """ + + def __init__(self, directory): + # type: (str) -> None + assert directory is not None, "Cache directory must not be None." + super(SafeFileCache, self).__init__() + self.directory = directory + + def _get_cache_path(self, name): + # type: (str) -> str + # From cachecontrol.caches.file_cache.FileCache._fn, brought into our + # class for backwards-compatibility and to avoid using a non-public + # method. + hashed = FileCache.encode(name) + parts = list(hashed[:5]) + [hashed] + return os.path.join(self.directory, *parts) + + def get(self, key): + # type: (str) -> Optional[bytes] + path = self._get_cache_path(key) + with suppressed_cache_errors(): + with open(path, 'rb') as f: + return f.read() + + def set(self, key, value): + # type: (str, bytes) -> None + path = self._get_cache_path(key) + with suppressed_cache_errors(): + ensure_dir(os.path.dirname(path)) + + with adjacent_tmp_file(path) as f: + f.write(value) + + replace(f.name, path) + + def delete(self, key): + # type: (str) -> None + path = self._get_cache_path(key) + with suppressed_cache_errors(): + os.remove(path) diff --git a/venv/lib/python3.8/site-packages/pip/_internal/network/download.py b/venv/lib/python3.8/site-packages/pip/_internal/network/download.py new file mode 100644 index 0000000..2f3e08a --- /dev/null +++ b/venv/lib/python3.8/site-packages/pip/_internal/network/download.py @@ -0,0 +1,200 @@ +"""Download files with progress indicators. +""" +import cgi +import logging +import mimetypes +import os + +from pip._vendor import requests +from pip._vendor.requests.models import CONTENT_CHUNK_SIZE + +from pip._internal.cli.progress_bars import DownloadProgressProvider +from pip._internal.models.index import PyPI +from pip._internal.network.cache import is_from_cache +from pip._internal.network.utils import response_chunks +from pip._internal.utils.misc import ( + format_size, + redact_auth_from_url, + splitext, +) +from pip._internal.utils.typing import MYPY_CHECK_RUNNING + +if MYPY_CHECK_RUNNING: + from typing import Iterable, Optional + + from pip._vendor.requests.models import Response + + from pip._internal.models.link import Link + from pip._internal.network.session import PipSession + +logger = logging.getLogger(__name__) + + +def _get_http_response_size(resp): + # type: (Response) -> Optional[int] + try: + return int(resp.headers['content-length']) + except (ValueError, KeyError, TypeError): + return None + + +def _prepare_download( + resp, # type: Response + link, # type: Link + progress_bar # type: str +): + # type: (...) -> Iterable[bytes] + total_length = _get_http_response_size(resp) + + if link.netloc == PyPI.file_storage_domain: + url = link.show_url + else: + url = link.url_without_fragment + + logged_url = redact_auth_from_url(url) + + if total_length: + logged_url = '{} ({})'.format(logged_url, format_size(total_length)) + + if is_from_cache(resp): + logger.info("Using cached %s", logged_url) + else: + logger.info("Downloading %s", logged_url) + + if logger.getEffectiveLevel() > logging.INFO: + show_progress = False + elif is_from_cache(resp): + show_progress = False + elif not total_length: + show_progress = True + elif total_length > (40 * 1000): + show_progress = True + else: + show_progress = False + + chunks = response_chunks(resp, CONTENT_CHUNK_SIZE) + + if not show_progress: + return chunks + + return DownloadProgressProvider( + progress_bar, max=total_length + )(chunks) + + +def sanitize_content_filename(filename): + # type: (str) -> str + """ + Sanitize the "filename" value from a Content-Disposition header. + """ + return os.path.basename(filename) + + +def parse_content_disposition(content_disposition, default_filename): + # type: (str, str) -> str + """ + Parse the "filename" value from a Content-Disposition header, and + return the default filename if the result is empty. + """ + _type, params = cgi.parse_header(content_disposition) + filename = params.get('filename') + if filename: + # We need to sanitize the filename to prevent directory traversal + # in case the filename contains ".." path parts. + filename = sanitize_content_filename(filename) + return filename or default_filename + + +def _get_http_response_filename(resp, link): + # type: (Response, Link) -> str + """Get an ideal filename from the given HTTP response, falling back to + the link filename if not provided. + """ + 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: + filename = parse_content_disposition(content_disposition, filename) + ext = splitext(filename)[1] # type: Optional[str] + if not ext: + ext = mimetypes.guess_extension( + resp.headers.get('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 + return filename + + +def _http_get_download(session, link): + # type: (PipSession, Link) -> Response + target_url = link.url.split('#', 1)[0] + 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() + return resp + + +class Download(object): + def __init__( + self, + response, # type: Response + filename, # type: str + chunks, # type: Iterable[bytes] + ): + # type: (...) -> None + self.response = response + self.filename = filename + self.chunks = chunks + + +class Downloader(object): + def __init__( + self, + session, # type: PipSession + progress_bar, # type: str + ): + # type: (...) -> None + self._session = session + self._progress_bar = progress_bar + + def __call__(self, link): + # type: (Link) -> Download + try: + resp = _http_get_download(self._session, link) + except requests.HTTPError as e: + logger.critical( + "HTTP error %s while getting %s", e.response.status_code, link + ) + raise + + return Download( + resp, + _get_http_response_filename(resp, link), + _prepare_download(resp, link, self._progress_bar), + ) diff --git a/venv/lib/python3.8/site-packages/pip/_internal/network/session.py b/venv/lib/python3.8/site-packages/pip/_internal/network/session.py new file mode 100644 index 0000000..39a4a54 --- /dev/null +++ b/venv/lib/python3.8/site-packages/pip/_internal/network/session.py @@ -0,0 +1,421 @@ +"""PipSession and supporting code, containing all pip-specific +network request configuration and behavior. +""" + +# The following comment should be removed at some point in the future. +# mypy: disallow-untyped-defs=False + +import email.utils +import json +import logging +import mimetypes +import os +import platform +import sys +import warnings + +from pip._vendor import requests, six, urllib3 +from pip._vendor.cachecontrol import CacheControlAdapter +from pip._vendor.requests.adapters import BaseAdapter, HTTPAdapter +from pip._vendor.requests.models import Response +from pip._vendor.requests.structures import CaseInsensitiveDict +from pip._vendor.six.moves.urllib import parse as urllib_parse +from pip._vendor.urllib3.exceptions import InsecureRequestWarning + +from pip import __version__ +from pip._internal.network.auth import MultiDomainBasicAuth +from pip._internal.network.cache import SafeFileCache +# Import ssl from compat so the initial import occurs in only one place. +from pip._internal.utils.compat import has_tls, ipaddress +from pip._internal.utils.glibc import libc_ver +from pip._internal.utils.misc import ( + build_url_from_netloc, + get_installed_version, + parse_netloc, +) +from pip._internal.utils.typing import MYPY_CHECK_RUNNING +from pip._internal.utils.urls import url_to_path + +if MYPY_CHECK_RUNNING: + from typing import ( + Iterator, List, Optional, Tuple, Union, + ) + + from pip._internal.models.link import Link + + SecureOrigin = Tuple[str, str, Optional[Union[int, str]]] + + +logger = logging.getLogger(__name__) + + +# Ignore warning raised when using --trusted-host. +warnings.filterwarnings("ignore", category=InsecureRequestWarning) + + +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] + + +# 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', + # Explicit environment variable. + 'PIP_IS_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": __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(): + import _ssl as ssl + 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 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 InsecureHTTPAdapter(HTTPAdapter): + + def cert_verify(self, conn, url, verify, cert): + super(InsecureHTTPAdapter, self).cert_verify( + conn=conn, url=url, verify=False, cert=cert + ) + + +class InsecureCacheControlAdapter(CacheControlAdapter): + + def cert_verify(self, conn, url, verify, cert): + super(InsecureCacheControlAdapter, self).cert_verify( + conn=conn, url=url, verify=False, cert=cert + ) + + +class PipSession(requests.Session): + + timeout = None # type: Optional[int] + + def __init__(self, *args, **kwargs): + """ + :param trusted_hosts: Domains not to emit warnings for when not using + HTTPS. + """ + retries = kwargs.pop("retries", 0) + cache = kwargs.pop("cache", None) + trusted_hosts = kwargs.pop("trusted_hosts", []) # type: List[str] + index_urls = kwargs.pop("index_urls", None) + + super(PipSession, self).__init__(*args, **kwargs) + + # Namespace the attribute with "pip_" just in case to prevent + # possible conflicts with the base class. + self.pip_trusted_origins = [] # type: List[Tuple[str, Optional[int]]] + + # Attach our User Agent to the request + self.headers["User-Agent"] = user_agent() + + # Attach our Authentication handler to the session + self.auth = MultiDomainBasicAuth(index_urls=index_urls) + + # 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, + ) + + # Our Insecure HTTPAdapter disables HTTPS validation. It does not + # support caching so we'll use it for all http:// URLs. + # If caching is disabled, we will also use it for + # https:// hosts that we've marked as ignoring + # TLS errors for (trusted-hosts). + insecure_adapter = InsecureHTTPAdapter(max_retries=retries) + + # We want to _only_ cache responses on securely fetched origins or when + # the host is specified as trusted. We do this because + # we can't validate the response of an insecurely/untrusted 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), + max_retries=retries, + ) + self._trusted_host_adapter = InsecureCacheControlAdapter( + cache=SafeFileCache(cache), + max_retries=retries, + ) + else: + secure_adapter = HTTPAdapter(max_retries=retries) + self._trusted_host_adapter = insecure_adapter + + self.mount("https://", secure_adapter) + self.mount("http://", insecure_adapter) + + # Enable file:// urls + self.mount("file://", LocalFSAdapter()) + + for host in trusted_hosts: + self.add_trusted_host(host, suppress_logging=True) + + def add_trusted_host(self, host, source=None, suppress_logging=False): + # type: (str, Optional[str], bool) -> None + """ + :param host: It is okay to provide a host that has previously been + added. + :param source: An optional source string, for logging where the host + string came from. + """ + if not suppress_logging: + msg = 'adding trusted host: {!r}'.format(host) + if source is not None: + msg += ' (from {})'.format(source) + logger.info(msg) + + host_port = parse_netloc(host) + if host_port not in self.pip_trusted_origins: + self.pip_trusted_origins.append(host_port) + + self.mount( + build_url_from_netloc(host) + '/', + self._trusted_host_adapter + ) + if not host_port[1]: + # Mount wildcard ports for the same host. + self.mount( + build_url_from_netloc(host) + ':', + self._trusted_host_adapter + ) + + def iter_secure_origins(self): + # type: () -> Iterator[SecureOrigin] + for secure_origin in SECURE_ORIGINS: + yield secure_origin + for host, port in self.pip_trusted_origins: + yield ('*', host, '*' if port is None else port) + + def is_secure_origin(self, location): + # type: (Link) -> bool + # Determine if this url used a secure transport mechanism + parsed = urllib_parse.urlparse(str(location)) + origin_protocol, origin_host, origin_port = ( + 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.) + origin_protocol = origin_protocol.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 self.iter_secure_origins(): + secure_protocol, secure_host, secure_port = secure_origin + if origin_protocol != secure_protocol and secure_protocol != "*": + continue + + try: + addr = ipaddress.ip_address( + None + if origin_host is None + else six.ensure_text(origin_host) + ) + network = ipaddress.ip_network( + six.ensure_text(secure_host) + ) + except ValueError: + # We don't have both a valid address or a valid network, so + # we'll check this origin against hostnames. + if ( + origin_host and + origin_host.lower() != secure_host.lower() and + secure_host != "*" + ): + 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 matches. + if ( + origin_port != secure_port and + secure_port != "*" and + secure_port 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'.", + origin_host, + origin_host, + ) + + return False + + 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) diff --git a/venv/lib/python3.8/site-packages/pip/_internal/network/utils.py b/venv/lib/python3.8/site-packages/pip/_internal/network/utils.py new file mode 100644 index 0000000..a19050b --- /dev/null +++ b/venv/lib/python3.8/site-packages/pip/_internal/network/utils.py @@ -0,0 +1,48 @@ +from pip._vendor.requests.models import CONTENT_CHUNK_SIZE, Response + +from pip._internal.utils.typing import MYPY_CHECK_RUNNING + +if MYPY_CHECK_RUNNING: + from typing import Iterator + + +def response_chunks(response, chunk_size=CONTENT_CHUNK_SIZE): + # type: (Response, int) -> Iterator[bytes] + """Given a requests Response, provide the data chunks. + """ + try: + # Special case for urllib3. + for chunk in response.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 = response.raw.read(chunk_size) + if not chunk: + break + yield chunk diff --git a/venv/lib/python3.8/site-packages/pip/_internal/network/xmlrpc.py b/venv/lib/python3.8/site-packages/pip/_internal/network/xmlrpc.py new file mode 100644 index 0000000..121edd9 --- /dev/null +++ b/venv/lib/python3.8/site-packages/pip/_internal/network/xmlrpc.py @@ -0,0 +1,44 @@ +"""xmlrpclib.Transport implementation +""" + +# The following comment should be removed at some point in the future. +# mypy: disallow-untyped-defs=False + +import logging + +from pip._vendor import requests +# 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 + +logger = logging.getLogger(__name__) + + +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 diff --git a/venv/lib/python3.8/site-packages/pip/_internal/operations/__init__.py b/venv/lib/python3.8/site-packages/pip/_internal/operations/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/venv/lib/python3.8/site-packages/pip/_internal/operations/__pycache__/__init__.cpython-38.pyc b/venv/lib/python3.8/site-packages/pip/_internal/operations/__pycache__/__init__.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..c741018251c8171d3422997eb62cebaffd2d2dcb GIT binary patch literal 212 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 literal 0 HcmV?d00001 diff --git a/venv/lib/python3.8/site-packages/pip/_internal/operations/__pycache__/freeze.cpython-38.pyc b/venv/lib/python3.8/site-packages/pip/_internal/operations/__pycache__/freeze.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..9e2ea3983ceb1044b13f8b90226126abfa734a7b GIT binary patch literal 5947 zcmaJ_TXP#ncJ3Jr27?Pp5WG_tk8U=x7D!o^wd*ZgwY9ozT(N0K7t5YqXEH=Ls3GS9 z^$a8t9&A!Y=VhzB(L;U!n76IuVJnqikO%*WdCilnQqDsjw#vy$`A&l%L8&T(nuETa z?mm6G`}@A$8qjQs~?CZ94gzrdS(pfSc7*8&!5j;2vs4|GRY{{^R@ z{td^#e<3J_rei7{BPfNIW2tm8D2ElNqS9ti4QoycZoC(i5 zXTvv~H$vO7!*kBL@Vs+Ayx?32FFF@h>#5*U_@?uwN>9VWx16`aHD?XIofl`sMbQ*% z9estZF!2*{Ry0>wr?A4Dw?$!iS+ou`XPqzcS>E_Yb2j)KpZ|tASANC#0$==w@kQ~@ ze!;oQ^?R(j{5|=#spT`?RuTujRJeZFjb*xtvO?lN!{2P%i{i*{dx75ZZr{;1HE;<4M2ap(%} zr`}c|@`cx`0zabBDmOp<E8T{+c*Exz58Hu z^P|mQHOsm6Q7WXD#xl1)?UJe&H-Db8X17@`3g_)7ayh&jyuL%XF@1h0NfEZw+W|4XXX=R38?y>VY<>J!H|(43-+Z z6`5&lfK$+4O^ZX58(B4*Iw)xDA^Vd3lll1SL-rm{+hFRqQ(vGY(!XF?N&YUY?La=h zn$@xj($)tIY4%)~{j|K#2GeO}SWRmfX(nU0*mLcxzZ=xksjR-QJ=gvr7|dp~s`Rf1 zjjS=8K49{P(f@FU7Z0?Aax>gKfVQk7T|Z!?nN9DO>L=IrlrmT0^|BU;TGX@gt)+HvV{j#prFBb~=sMPB_@AIwF|=|Z|V zT;es1sZ-l=O*Xf)j1m6@ep}&~8%9Gd`A(HglZB7JOIJy6UmMJ4^I2uLgc-faW~gih zeo6jQd3`3UzcL2u4-Ab(^|9{oR8~Li@icrszryx4@(X!@@*lF-HEHesirr;ju*ZK) z&kWCIGY7O|uKh+IEDRRYMz)CA*LMx9+YM#MY_`A~!#7epEmPgTcA(LGp6icq@HrYO zwX@3b+yUeBe1Q<=TOIw`gQaX~$I2|sPM5z=PiITVwxR9sIRgSMZT5?;j{WsOp2j|I z8hIs%0X1$M1$()X?E2lj+)IQU6%@N(`Y7pNzZv%eZbxxyZwnkWFBLZ6WDH1lCzkf) zF}5$spWBy`AGHh3T3>I;{#2_qp^qY`Kiz71!BcN9Y4xJM-$hxcUu(5Ge#BdVq1^-} ze!tpkp+G!q0Zd?ct1AVyOl2=g1#dlyleE7|#c^9CNsEiFfO^#4JM3e&)e_HA=}q*a zw?w~!;zJbt9w(XosC@`zuzX-L=Eif@e|QhB==#yNO_*r=(a8z>fv|slDf!CY5^WFu zviA!E=YaZwJ1+l&T;b&fZ*V`7G0C_u&?evkb zAKd+zlHQZp=P2+K`>7`*7}D(jasr*~&XEc25&k9U{jv=lwY#HUu?jYt)2bbz?e}1C z)_;4lqe}^@+d+>DZil@f^?{pq>W3mxGwkb^?fy?MT(IxMxK11d@l*23#B#zyP=Jj| zp@*$KyX$xDjufIVe%_|x#t)^*+4t}!6mev1hYhs@6#PNk1Qr`ac6kva2#Mv;o~b>m z#E(xmy3&7w#o2h`$qm@Sg-kZO?TzbCyHBoNjo!bSa(;a)`Pr3D*bm>kqMq$&~HOQdpq&_@9t6E2B-+s0zTP@yR@~5hBij_uXp!y>w~}xx43uXpJDNT z<7NHj!`ctj(MfKsk5yoFVOQ)m&*vtNMChe*8v6O{9U1pUG~x8Tte9AxnIfr}LA<>U z1}hydc5aRLXFh!t52#=OVFF05+Qv$Nl*!n<%0VFqbGV60>&m3Zep; zCQtqJQEtjnSpmzH7Xm1mfhv&}?2trwlGX;p^tR+0r3^p9Yz#`8UY8CSDpM&7R8scg zJiK9&8#}R&?h=GEI6~$VFX)M+xtv!g_DWu%1MaE=F3+F?`34D_#6^g_s#eHt6Fob1 z@l0*Zqu4>7B^^vL+I6N5H}z=6a)X?i7cf>{Kr?4XgnkM`+*pMRiBm?EOQ$Arrp8H^ z@GZB-S=F;}EIRTOFF8x&6bw2H%G4r^98(%vrkz52<&0_!L`d4MC7)z$SV3G+Osm5hFJ?v5LXVrnsSJH=YXdXWcqub! zjD90ChSQmb{$~#KK@ri^KN>76Ae#Dj4J?WN4}0U*a4M~5#=f>Qi+=RUeoVwu_ZXtE z2HO2FYKPJ1I4sMIoq07r#-w>2=`QS65J8+q%vM6oR#*C!taPAL#D+*s8BCS_94ZN>BlAn0w38=fZaeY zm>s1u4(zqB2hIEa=0uCH2d_24KzR0DVFt=y8{E=g2K~68B_VaW^<_ z4KKNgoJmbbQvt6N1T_0!s)>TPMP#RPk0=f-jGM}d2~wa#en|EZeuRo}JGgL>_7(_+ zn)IS0*Nixz-#U(-j_sAA8z(;QGkZ3%2m!eb+YunNz_sVjVe!Wy*Z%*tG6vTj#?$MJ*kz~5RvWBrBPA3k?wVI7jsq4b)`u|zDt4#MwTF&mi&a$ z4vAxvs<3culH5dHZjJZ4{FGEFiX90BxAQcG33D;kPW(scB-Al80iF7yUWX(Y1^h8U z@C&18y)+6HUDpT(Ex@A1^p}9a;*v!6BMO zg#RMwjI>VHz3OGGq#yaI>;4xSs!)!BM2M73t-ty@=8VJ{O+c@j%oj+2MKF!u z6bAaB07k%9B{0JEtdLwEO>h7{kk?b4SYfBIuMfaAM|yWMZD@cw-~g(HBk0zxeQUs- zI=-$P&QYzCtYI`scX|0YB))=Znt7oeB%@7ua`t)U_yfo*o{;aL>7Vc>^mTxl(_n42 zt*|k`xCLw^-O~G~UU$h644+_D@((St@(C>sg~<5Az;-mbi0=!!VoNXuxK;7$zLtU! z_rZ)y>;UCUSng(l?sx)syj-g^OB3r%=T+Vz@jeM!c6ke;sml(f)x2LhQ6<+!u;;op zkVHbv&(tzkwEoKLvpUozf2-?dNp7J*UUA(p=Dk3;ZeDZU$2~6~TmTYmQ+2f+cu5k9 z^ij-}N98VxCtR&eqVuiJ#Scg*OK_MiU8~Hi(q?&+o;#p3HT1hQYNrR*q;bfx@~04a zi6U(Pw)~uORfUI70C@3XKznMUWPNm#g7!K-Phoe^;c-?@XzXqSZ$?a$NLssm`6R|7 z7!{)sbcB0^cKO+pIzX&-kRcr1Yl9P#<6zubeO(jUDv%uo>TjM@P=>A_?^ySd2ym}S zM*E$1{;LL#T7-VmR#Y;&v%E6wNCz-Zo|~ag%)?9_897U$L1K=?Jc$JoizJpv&;iNk zPTJb0C||Bn^(u)|BxpnC##S5$&dl9o;`s^+E|K=RD7z=b#BI6L^{Xl zIkATnJy;)%_Tdd$sN@C&omc!2=i&!qs2erBkN~vHh?nce*+Ruo(c~yL9LYab{+1Q2 G8~+D7ZEZaO literal 0 HcmV?d00001 diff --git a/venv/lib/python3.8/site-packages/pip/_internal/operations/__pycache__/prepare.cpython-38.pyc b/venv/lib/python3.8/site-packages/pip/_internal/operations/__pycache__/prepare.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..e4b923587431a323283c087bd6425fa8e1ea7dbb GIT binary patch literal 10945 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 q;)moU3nJw$NmqERHxqn literal 0 HcmV?d00001 diff --git a/venv/lib/python3.8/site-packages/pip/_internal/operations/build/__init__.py b/venv/lib/python3.8/site-packages/pip/_internal/operations/build/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/venv/lib/python3.8/site-packages/pip/_internal/operations/build/__pycache__/__init__.cpython-38.pyc b/venv/lib/python3.8/site-packages/pip/_internal/operations/build/__pycache__/__init__.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..ff991878a6c8c66f51340bf9504ab13659a6dccf GIT binary patch literal 218 zcmWIL<>g`k0u|Y=6cGIwL?8o3AjbiSi&=m~3PUi1CZpd};mRORi zUzS*;pO&AKl3G-(Uy!VCR$fqMVw7iTRFaZnmQ-wEkd|AOYia-n>3RAg`URNN2F(oCAal4Y3!0MDv<;E_ym|J!#FZSAfr|edK>|?}ybqbhQZdARv z*J9oBy{aGg0mE0D?EPyTZ&9Ca{Ep&nN`66u-u%HcW>T7(G{_leDm8`RK`HVg3(kZJ zw5V0a0$OOJicxJ*9rpeKFVXP;H+!n)a4Wee%sA1qXoZSto#qVS%~Mv%?+e8;Bh(BI z?mT-ne3cwNJv#g`IeC73{KN4RINkoPE=qdDr-c%{V%*rXA(IDRJbEg``C(d?Ol8Cx zy%`>?meE7_^h=mb%NTY3B$s4CCiv1>pw}TrhEVr6eCaOmm9ucK@#}4jVDB!RYkb*x zfez5m=mfn)w>#6B;45$8EIQYgq0YQ~vLuED)rzWG7d!_s8>hU`l@2J=8SuksQ1D=Q zGz`G?u-6Mt^+e>D~dsF{S;lX6Sms(tx8a)`2I@abU+&Syx_Fi&Py=@DrQNQ za^RQFcZZ6}R9Rmx#*CHgR|j2XE2?p2YR1YE*X-zeB$db@H_0+-O(5IrELqt~Amho% z<|1j|YlzT|I}rsRc+JKVV0_wiM`?D>z@@GAO;bsTr0K5@tmNGo>f7=u_?bxH^e?3D zfhPBLWg3{{;#mkjDmj)YW7-m3JDS_lx-V@7b=xNhE}$7nvI;9 zpTKjb8b3*RS}};lv#~JM#@MFlt?2`M%l{f`vo6^;7$j|z=sG_3Ngw#>6Stjv&VLZ; BdoBO~ literal 0 HcmV?d00001 diff --git a/venv/lib/python3.8/site-packages/pip/_internal/operations/build/__pycache__/metadata_legacy.cpython-38.pyc b/venv/lib/python3.8/site-packages/pip/_internal/operations/build/__pycache__/metadata_legacy.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..c14268edc03a9fabc916d82f0b0a3a572fffd570 GIT binary patch literal 2002 zcmah}TW=dh6rR~#uh))qZKZ^&tyWcA5v15rN-GLcRFQ^KL8FqUf?7h8_0D*+$?nWD zvre#acpw!IND<;6B#->1;gP5Q1tbV(*0JTFNX%;IdggMz^PTVYikgUGY9+Xdi`Hsd(AtTeI!yp;`5m6laZ6W=HMM2p0mFUQj`7~C!j9VRJr0Ugo-~ifO#CrBQS9y>m z#qJF$g@nmlX|T_TVtFUag-8@(QAA?i79^070)y!gwh+~6Wm1GpnW@QbmS#_5$--R7 zE=|2zhQ`|>q_$fNZN4SsG{oJDVscq(t@lQB+`p{Bm-;qyXr8KsJ_DZL2AN4VH-$Q2x zxN`5oXECRK!2LS-wPgJA!4*1uyB~9ZXEfTWSA8f~KJ%4%<_)MK)JNaWnWvosFV3)p z2mNM!=fHKiR~cOc{(Fp5br|Qnij2OqBR$16Fdk6P-Z<#yUjA1)Uq;*IS_7H3Nxo(17Pqf3Xoj3rC^L3d9)e$X{* z4G3M%BoC5?$S!!I8Z8rtMtVUZGGI$R>+0%i0$d42nyt+n|6HhCY@sUZ-63s2Zp4QA zY9fFLY7IKWExkLNVhq<2j6lf_fDX|9aP-cEytnj1Nap9e5C_58|Lu`x&th3yLg})( z4NjJZF4_gMr$MgmgmLX;LB6YKe?cn@}Og z#ooE=IM{;Laca;)|J*uvTxpOA4O52&a@Fz*y>>SbO$S z$?&yb>f5w*VjlrJf4vSCrM}y@s7uQyVE;9Gh`vHw=o^%~{n9Z8B6&UjY>?;7ABN5E zsF+87k*N^7rBj)WZ$U;&t^jm4)C@2m9X3L1K>6OvR)_QP z^ZoY?ydU3Fi~dj%3aK&F3nJ#iD0n-LdJ9=M-vvP%`Zl@|cG}?~)it0f97YLcDgpdCTd_=DY#D2p_*Zkg{*&^Qqi7!4z%X`+jSwcF8VWNc^|A7uj?`)RD^^8(ZMAM zfSiT}y#UbGN4Z>Q--j$K!m5AkE4ua+pVv>^(c^g)WKow|=;cP@w78&cUrh;}7 awq`UzEkNbi9`>vnzW6K$*Q~1T&HoEG^g&zz literal 0 HcmV?d00001 diff --git a/venv/lib/python3.8/site-packages/pip/_internal/operations/build/__pycache__/wheel.cpython-38.pyc b/venv/lib/python3.8/site-packages/pip/_internal/operations/build/__pycache__/wheel.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..c2a93ec005bc1b6202796e1b74d37a813d8de86a GIT binary patch literal 1358 zcmZuxPmdcl6t^cclSwwaELDZnE`Kc4N{vJ^rLct}grH%|R%_qe<*!XEWm& zY|n;->;*U=q>3xY{0TWww~6(RC^=^$nk4Etl0{;Lj7b@f7^p8j zJUTdvI{SN_&!fY~-R}MFXH7@D4-%<#@TgEp&f^s9whvgb`}TYLc|PvMY0AU^D4vfh z{Hww2z?8qjGIuB**JMMX@A14Mt|`25s53`H{0drPpa39ecVV|eD|2`0V5D5?F1$Ht zf52zn954L2x5U3(#%PXDs&f}2de59Q{|VYfU!X(u6g@%wO&6(PTo*rKWPUb_4>2I# zku>t=6(t*NMXV31INlHo18M)7;0y2l}n=%nBV_cC^CVWKHF(U_i2Lu?C zzBMmIK7gXN>vaO-kf~B|Ns3q<6P1(6F=Hu7hD4SFV>T?)bZR|Sn>>POVvVtKtRPwt7Y-Da@!h`00zM%AfH|Nlu4EF*tCpL z;4@IUn0^3pW^Y)^hS-bn-<^>g>&Q)W4l<@<8ml-ucb&a@FHTZMSIGYBeO)#idg~J@ znK1eGgvf>V(|j~yLVL`L9%cP#`# z9u^`wjTH+|V-XJXlrka1Vi4Y*6sPar;&*N-N^ke&`!|Q#EZe@;IXkgdz zvHRS2T}<0ak0d9J zyM8Le14DvXxU+#8?BHfM^?{3GU+b=W5h3pQK?&+Q3_UosLw@xX>PH)IFuk`bA{>Ce&4T|oy z?l2v$jEZ5LY;m??cv{(tcW;$%y|#<*x=~svE&zfqASq%5LUuPsX~w!WeHkCb$!=%3 ztB=$>FhfjmIm*-#Sf5;mj(9!|EQbbk0qz0y=^;xz0rCVvI%v3;Gq4m4reFbH^3)72 zw{n$L*_Cq_5pk8h=Tz=Kg>1C066GR_%m&aln1eW0WZ#?bp4VRBa-qfI<-~mv9>QMm-jNWr59B|yakJbdQdvnS0Pq&V#m@eM!o^#seLX=|s%!L>@X0@|_H+@2`w_OoJti|)#9m8%0 zV7n#oSg*Op)2im$Wqe~a5Li`obM$(`#~yfPPy=IVUC$d= zqTz02k|B`gNF*Gc7bj5j1)St!PB0zRB@qu~-9oP-$VCaU6m6X4)rH)w|6nc}eKcp5 z9!``x^zVbdJOdq>x9Ifuo7V^+RUlCgB)vnMUe@1UCyvn7>6I)Sv)6&DM|j@ gdC5i@e+SE str + """Generate metadata using mechanisms described in PEP 517. + + Returns the generated metadata directory. + """ + metadata_tmpdir = TempDirectory( + kind="modern-metadata", globally_managed=True + ) + + metadata_dir = metadata_tmpdir.path + + with 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. + runner = runner_with_spinner_message("Preparing wheel metadata") + with backend.subprocess_runner(runner): + distinfo_dir = backend.prepare_metadata_for_build_wheel( + metadata_dir + ) + + return os.path.join(metadata_dir, distinfo_dir) diff --git a/venv/lib/python3.8/site-packages/pip/_internal/operations/build/metadata_legacy.py b/venv/lib/python3.8/site-packages/pip/_internal/operations/build/metadata_legacy.py new file mode 100644 index 0000000..14762ae --- /dev/null +++ b/venv/lib/python3.8/site-packages/pip/_internal/operations/build/metadata_legacy.py @@ -0,0 +1,77 @@ +"""Metadata generation logic for legacy source distributions. +""" + +import logging +import os + +from pip._internal.exceptions import InstallationError +from pip._internal.utils.setuptools_build import make_setuptools_egg_info_args +from pip._internal.utils.subprocess import call_subprocess +from pip._internal.utils.temp_dir import TempDirectory +from pip._internal.utils.typing import MYPY_CHECK_RUNNING + +if MYPY_CHECK_RUNNING: + from pip._internal.build_env import BuildEnvironment + +logger = logging.getLogger(__name__) + + +def _find_egg_info(directory): + # type: (str) -> str + """Find an .egg-info subdirectory in `directory`. + """ + filenames = [ + f for f in os.listdir(directory) if f.endswith(".egg-info") + ] + + if not filenames: + raise InstallationError( + "No .egg-info directory found in {}".format(directory) + ) + + if len(filenames) > 1: + raise InstallationError( + "More than one .egg-info directory found in {}".format( + directory + ) + ) + + return os.path.join(directory, filenames[0]) + + +def generate_metadata( + build_env, # type: BuildEnvironment + setup_py_path, # type: str + source_dir, # type: str + isolated, # type: bool + details, # type: str +): + # type: (...) -> str + """Generate metadata using setup.py-based defacto mechanisms. + + Returns the generated metadata directory. + """ + logger.debug( + 'Running setup.py (path:%s) egg_info for package %s', + setup_py_path, details, + ) + + egg_info_dir = TempDirectory( + kind="pip-egg-info", globally_managed=True + ).path + + args = make_setuptools_egg_info_args( + setup_py_path, + egg_info_dir=egg_info_dir, + no_user_config=isolated, + ) + + with build_env: + call_subprocess( + args, + cwd=source_dir, + command_desc='python setup.py egg_info', + ) + + # Return the .egg-info directory. + return _find_egg_info(egg_info_dir) diff --git a/venv/lib/python3.8/site-packages/pip/_internal/operations/build/wheel.py b/venv/lib/python3.8/site-packages/pip/_internal/operations/build/wheel.py new file mode 100644 index 0000000..1266ce0 --- /dev/null +++ b/venv/lib/python3.8/site-packages/pip/_internal/operations/build/wheel.py @@ -0,0 +1,46 @@ +import logging +import os + +from pip._internal.utils.subprocess import runner_with_spinner_message +from pip._internal.utils.typing import MYPY_CHECK_RUNNING + +if MYPY_CHECK_RUNNING: + from typing import List, Optional + from pip._vendor.pep517.wrappers import Pep517HookCaller + +logger = logging.getLogger(__name__) + + +def build_wheel_pep517( + name, # type: str + backend, # type: Pep517HookCaller + metadata_directory, # type: str + build_options, # type: List[str] + tempd, # type: str +): + # type: (...) -> Optional[str] + """Build one InstallRequirement using the PEP 517 build process. + + Returns path to wheel if successfully built. Otherwise, returns None. + """ + assert metadata_directory is not None + if build_options: + # PEP 517 does not support --build-options + logger.error('Cannot build wheel for %s using PEP 517 when ' + '--build-option is present' % (name,)) + return None + try: + logger.debug('Destination directory: %s', tempd) + + runner = runner_with_spinner_message( + 'Building wheel for {} (PEP 517)'.format(name) + ) + with backend.subprocess_runner(runner): + wheel_name = backend.build_wheel( + tempd, + metadata_directory=metadata_directory, + ) + except Exception: + logger.error('Failed building wheel for %s', name) + return None + return os.path.join(tempd, wheel_name) diff --git a/venv/lib/python3.8/site-packages/pip/_internal/operations/build/wheel_legacy.py b/venv/lib/python3.8/site-packages/pip/_internal/operations/build/wheel_legacy.py new file mode 100644 index 0000000..37dc876 --- /dev/null +++ b/venv/lib/python3.8/site-packages/pip/_internal/operations/build/wheel_legacy.py @@ -0,0 +1,115 @@ +import logging +import os.path + +from pip._internal.cli.spinners import open_spinner +from pip._internal.utils.setuptools_build import ( + make_setuptools_bdist_wheel_args, +) +from pip._internal.utils.subprocess import ( + LOG_DIVIDER, + call_subprocess, + format_command_args, +) +from pip._internal.utils.typing import MYPY_CHECK_RUNNING + +if MYPY_CHECK_RUNNING: + from typing import List, Optional, Text + +logger = logging.getLogger(__name__) + + +def format_command_result( + command_args, # type: List[str] + command_output, # type: Text +): + # 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 + name, # type: str + command_args, # type: List[str] + command_output, # type: Text +): + # 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(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(name, names) + msg += format_command_result(command_args, command_output) + logger.warning(msg) + + return os.path.join(temp_dir, names[0]) + + +def build_wheel_legacy( + name, # type: str + setup_py_path, # type: str + source_dir, # type: str + global_options, # type: List[str] + build_options, # type: List[str] + tempd, # type: str +): + # type: (...) -> Optional[str] + """Build one unpacked package using the "legacy" build process. + + Returns path to wheel if successfully built. Otherwise, returns None. + """ + wheel_args = make_setuptools_bdist_wheel_args( + setup_py_path, + global_options=global_options, + build_options=build_options, + destination_dir=tempd, + ) + + spin_message = 'Building wheel for {} (setup.py)'.format(name) + with open_spinner(spin_message) as spinner: + logger.debug('Destination directory: %s', tempd) + + try: + output = call_subprocess( + wheel_args, + cwd=source_dir, + spinner=spinner, + ) + except Exception: + spinner.finish("error") + logger.error('Failed building wheel for %s', name) + return None + + names = os.listdir(tempd) + wheel_path = get_legacy_build_wheel_path( + names=names, + temp_dir=tempd, + name=name, + command_args=wheel_args, + command_output=output, + ) + return wheel_path diff --git a/venv/lib/python3.8/site-packages/pip/_internal/operations/check.py b/venv/lib/python3.8/site-packages/pip/_internal/operations/check.py new file mode 100644 index 0000000..b85a123 --- /dev/null +++ b/venv/lib/python3.8/site-packages/pip/_internal/operations/check.py @@ -0,0 +1,163 @@ +"""Validation of dependencies of packages +""" + +# The following comment should be removed at some point in the future. +# mypy: strict-optional=False +# mypy: disallow-untyped-defs=False + +import logging +from collections import namedtuple + +from pip._vendor.packaging.utils import canonicalize_name +from pip._vendor.pkg_resources import RequirementParseError + +from pip._internal.distributions import ( + make_distribution_for_install_requirement, +) +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 + logger.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 = {} + conflicting = {} + + 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: + abstract_dist = make_distribution_for_install_requirement(inst_req) + dist = abstract_dist.get_pkg_resources_distribution() + + 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/venv/lib/python3.8/site-packages/pip/_internal/operations/freeze.py b/venv/lib/python3.8/site-packages/pip/_internal/operations/freeze.py new file mode 100644 index 0000000..aa6b052 --- /dev/null +++ b/venv/lib/python3.8/site-packages/pip/_internal/operations/freeze.py @@ -0,0 +1,272 @@ +# The following comment should be removed at some point in the future. +# mypy: strict-optional=False +# mypy: disallow-untyped-defs=False + +from __future__ import absolute_import + +import collections +import logging +import os + +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.direct_url_helpers import ( + direct_url_as_pep440_direct_reference, + dist_get_direct_url, +) +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] + paths=None, # type: Optional[List[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 [] + + for link in find_links: + yield '-f {}'.format(link) + installations = {} # type: Dict[str, FrozenRequirement] + for dist in get_installed_distributions(local_only=local_only, + skip=(), + user_only=user_only, + paths=paths): + try: + req = FrozenRequirement.from_dist(dist) + except RequirementParseError as exc: + # We include dist rather than dist.project_name because the + # dist string includes more information, like the version and + # location. We also include the exception message to aid + # troubleshooting. + logger.warning( + 'Could not generate requirement for distribution %r: %s', + dist, exc + ) + continue + if exclude_editable and req.editable: + continue + installations[req.canonical_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 + 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, + ) + else: + line_req = install_req_from_line( + COMMENT_RE.sub('', line).strip(), + isolated=isolated, + ) + + 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)" + ) + else: + line_req_canonical_name = canonicalize_name( + line_req.name) + if line_req_canonical_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_canonical_name]).rstrip() + del installations[line_req_canonical_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 installation.canonical_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 + vcs_backend = vcs.get_backend_for_dir(location) + + if vcs_backend is None: + req = dist.as_requirement() + logger.debug( + 'No VCS found for editable requirement "%s" in: %r', req, + location, + ) + comments = [ + '# Editable install with no version control ({})'.format(req) + ] + return (location, True, comments) + + try: + req = vcs_backend.get_src_requirement(location, dist.project_name) + except RemoteNotFoundError: + req = dist.as_requirement() + comments = [ + '# Editable {} install with no remote ({})'.format( + type(vcs_backend).__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, + vcs_backend.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.canonical_name = canonicalize_name(name) + self.req = req + self.editable = editable + self.comments = comments + + @classmethod + def from_dist(cls, dist): + # type: (Distribution) -> FrozenRequirement + # TODO `get_requirement_info` is taking care of editable requirements. + # TODO This should be refactored when we will add detection of + # editable that provide .dist-info metadata. + req, editable, comments = get_requirement_info(dist) + if req is None and not editable: + # if PEP 610 metadata is present, attempt to use it + direct_url = dist_get_direct_url(dist) + if direct_url: + req = direct_url_as_pep440_direct_reference( + direct_url, dist.project_name + ) + comments = [] + if req is None: + # name==version requirement + req = dist.as_requirement() + + return cls(dist.project_name, req, editable, comments=comments) + + def __str__(self): + req = self.req + if self.editable: + req = '-e {}'.format(req) + return '\n'.join(list(self.comments) + [str(req)]) + '\n' diff --git a/venv/lib/python3.8/site-packages/pip/_internal/operations/install/__init__.py b/venv/lib/python3.8/site-packages/pip/_internal/operations/install/__init__.py new file mode 100644 index 0000000..24d6a5d --- /dev/null +++ b/venv/lib/python3.8/site-packages/pip/_internal/operations/install/__init__.py @@ -0,0 +1,2 @@ +"""For modules related to installing packages. +""" diff --git a/venv/lib/python3.8/site-packages/pip/_internal/operations/install/__pycache__/__init__.cpython-38.pyc b/venv/lib/python3.8/site-packages/pip/_internal/operations/install/__pycache__/__init__.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..58b6613aeee71771d4507a2558e42a16fb4f0184 GIT binary patch literal 276 zcmYjMyKVw85cJUmA@K=FF47?7SOf$GDT-31Op~NZW0~9K;^4C_+Z;N&wE2_#5-p#Q zE)_mP!ALW!(P&?9{eF)nTo1l+n(Co7m&lTNObp#5$le)dFKpP^-8@;(D~lUVfqT+2 z67p!dG9gN>l_|NC#afmWM!n}@w!abrZ6U-D^PM#6B!6GvysA?#)Y8MsY9t@P6)@kr zdNwhONkp74!`*mQ?W)`H38jHoaLVNy)2<-Qw;x10U(4pxE~ZVJflwxrH&TOj=kmd;kB%L;LA(B0B=ZNl=Uc literal 0 HcmV?d00001 diff --git a/venv/lib/python3.8/site-packages/pip/_internal/operations/install/__pycache__/editable_legacy.cpython-38.pyc b/venv/lib/python3.8/site-packages/pip/_internal/operations/install/__pycache__/editable_legacy.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..c4450976fdf622caa9167adc3e09f5df15da1617 GIT binary patch literal 1354 zcmZuxTaVl{6m~9?$z*1Bw%e6ZTP_i5A4Xy)RiPD?5Fl0+RBQ*>wt|KSZzlF+!a8xV zomplTp5Or?Rs9R?BmahHzw*RSfCt2JcJ@+f9r=sTEk6Fvah?u`0fKS!t>4L?9fbZe z!o}f$@F`5W1Ry9z#3DAcVhi35vkND7ETeOoTXf=1yY4Wr=*Hc4?J>XT#XSqro;U!% zE(?l&+%E?4pcux(*2iZX#V8(ukM(ksUU_B3Tck&VUr~IO*pJb;fBeZF&60E#Qj)7= z#%P$AQYDNfD(7Wb37%3Z-wX2zorDLHs=As~tB}woWxP6=1b;*FXgaoZke7s(3NxO8 z^k$J9Qr!A0&Y8sP2%Lyaf_;?29$411^$wugdi3n;XL$FE2fJV5{ioCE!|CT^N4tBu zRNDWlGWjG-`;Y05H7!#L5u2aYIU^6sWiEJGz~*VoxXB1cAEuFaVag`}3cUnwo6alS zLdtHu#%fU0ZS2NrTw*`({v0Uh7xdy?3pM_WE%0%hpy?6kIrx8z?xJtdKKky$inzdF z=lJ`FCya$jSS9IUk{J%q`7AgA>K<{aLO6SE(v+~G;4I~iS_oceiy(xdcz90Oq*Wci zvtO5GUS=nj3)F{mF2Y-K?CFh+@maz!Zz+;`w65TpVx#1^mQ>)Bm-9T+UM1)}|4F+G zUQj&*(}`K;%CPFBM`SwgXb&oxQK8+uguP6+3fq7SFzyROuQ2?Ip@9tpBLMA|iTP}M z6kM%vm8gaGbIGBnDAB!{DH^8bQeUe}!x$x4@>--6lU&eLak0{_>61T@xCUSR_qV7L z`4Xa|Wg?t8ozqQrjuw=% zox^1H1Hb?N%Ge^Dmx>DLtB6-rv^_7QHGk3RmB6gMB0#?0JmvP(b`|oIApqJO*tYfB z^N$z!mUp82Puu{X@wV<_On8d1?izZbvib%jGC50RQmdTF$=cX5u5VuEao#GpWwfo3 ze*03_vuoBCPhY>xNv$f#M!=s6X>TofAc(F3->BbsJ?^ZKqGznVjHn6ni2yREs=*JdFavQ+4eS2WFkL_C9_P`08{{bMrkT3uM literal 0 HcmV?d00001 diff --git a/venv/lib/python3.8/site-packages/pip/_internal/operations/install/__pycache__/legacy.cpython-38.pyc b/venv/lib/python3.8/site-packages/pip/_internal/operations/install/__pycache__/legacy.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..870a2b847cdd9b7aa64d8c22da3f81ca9f46a672 GIT binary patch literal 3374 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 MYv0%2;XJ None + """Install a package in editable mode. Most arguments are pass-through + to setuptools. + """ + logger.info('Running setup.py develop for %s', name) + + args = make_setuptools_develop_args( + setup_py_path, + global_options=global_options, + install_options=install_options, + no_user_config=isolated, + prefix=prefix, + home=home, + use_user_site=use_user_site, + ) + + with indent_log(): + with build_env: + call_subprocess( + args, + cwd=unpacked_source_directory, + ) diff --git a/venv/lib/python3.8/site-packages/pip/_internal/operations/install/legacy.py b/venv/lib/python3.8/site-packages/pip/_internal/operations/install/legacy.py new file mode 100644 index 0000000..0fac905 --- /dev/null +++ b/venv/lib/python3.8/site-packages/pip/_internal/operations/install/legacy.py @@ -0,0 +1,142 @@ +"""Legacy installation process, i.e. `setup.py install`. +""" + +import logging +import os +import sys +from distutils.util import change_root + +from pip._internal.utils.deprecation import deprecated +from pip._internal.utils.logging import indent_log +from pip._internal.utils.misc import ensure_dir +from pip._internal.utils.setuptools_build import make_setuptools_install_args +from pip._internal.utils.subprocess import runner_with_spinner_message +from pip._internal.utils.temp_dir import TempDirectory +from pip._internal.utils.typing import MYPY_CHECK_RUNNING + +if MYPY_CHECK_RUNNING: + from typing import List, Optional, Sequence + + from pip._internal.build_env import BuildEnvironment + from pip._internal.models.scheme import Scheme + + +logger = logging.getLogger(__name__) + + +class LegacyInstallFailure(Exception): + def __init__(self): + # type: () -> None + self.parent = sys.exc_info() + + +def install( + install_options, # type: List[str] + global_options, # type: Sequence[str] + root, # type: Optional[str] + home, # type: Optional[str] + prefix, # type: Optional[str] + use_user_site, # type: bool + pycompile, # type: bool + scheme, # type: Scheme + setup_py_path, # type: str + isolated, # type: bool + req_name, # type: str + build_env, # type: BuildEnvironment + unpacked_source_directory, # type: str + req_description, # type: str +): + # type: (...) -> bool + + header_dir = scheme.headers + + with TempDirectory(kind="record") as temp_dir: + try: + record_filename = os.path.join(temp_dir.path, 'install-record.txt') + install_args = make_setuptools_install_args( + setup_py_path, + global_options=global_options, + install_options=install_options, + record_filename=record_filename, + root=root, + prefix=prefix, + header_dir=header_dir, + home=home, + use_user_site=use_user_site, + no_user_config=isolated, + pycompile=pycompile, + ) + + runner = runner_with_spinner_message( + "Running setup.py install for {}".format(req_name) + ) + with indent_log(), build_env: + runner( + cmd=install_args, + cwd=unpacked_source_directory, + ) + + if not os.path.exists(record_filename): + logger.debug('Record file %s not found', record_filename) + # Signal to the caller that we didn't install the new package + return False + + except Exception: + # Signal to the caller that we didn't install the new package + raise LegacyInstallFailure + + # At this point, we have successfully installed the requirement. + + # We intentionally do not use any encoding to read the file because + # setuptools writes the file using distutils.file_util.write_file, + # which does not specify an encoding. + with open(record_filename) as f: + record_lines = f.read().splitlines() + + 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) + + for line in record_lines: + directory = os.path.dirname(line) + if directory.endswith('.egg-info'): + egg_info_dir = prepend_root(directory) + break + else: + deprecated( + reason=( + "{} did not indicate that it installed an " + ".egg-info directory. Only setup.py projects " + "generating .egg-info directories are supported." + ).format(req_description), + replacement=( + "for maintainers: updating the setup.py of {0}. " + "For users: contact the maintainers of {0} to let " + "them know to update their setup.py.".format( + req_name + ) + ), + gone_in="20.2", + issue=6998, + ) + # FIXME: put the record somewhere + return True + + new_lines = [] + for line in record_lines: + 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') + + return True diff --git a/venv/lib/python3.8/site-packages/pip/_internal/operations/install/wheel.py b/venv/lib/python3.8/site-packages/pip/_internal/operations/install/wheel.py new file mode 100644 index 0000000..2fb86b8 --- /dev/null +++ b/venv/lib/python3.8/site-packages/pip/_internal/operations/install/wheel.py @@ -0,0 +1,631 @@ +"""Support for installing and building the "wheel" binary package format. +""" + +# The following comment should be removed at some point in the future. +# mypy: strict-optional=False + +from __future__ import absolute_import + +import collections +import compileall +import contextlib +import csv +import logging +import os.path +import re +import shutil +import stat +import sys +import warnings +from base64 import urlsafe_b64encode +from itertools import starmap +from zipfile import ZipFile + +from pip._vendor import pkg_resources +from pip._vendor.distlib.scripts import ScriptMaker +from pip._vendor.distlib.util import get_export_entry +from pip._vendor.six import StringIO + +from pip._internal.exceptions import InstallationError +from pip._internal.locations import get_major_minor_version +from pip._internal.models.direct_url import DIRECT_URL_METADATA_NAME, DirectUrl +from pip._internal.utils.filesystem import adjacent_tmp_file, replace +from pip._internal.utils.misc import captured_stdout, ensure_dir, hash_file +from pip._internal.utils.temp_dir import TempDirectory +from pip._internal.utils.typing import MYPY_CHECK_RUNNING +from pip._internal.utils.unpacking import current_umask, unpack_file +from pip._internal.utils.wheel import parse_wheel + +if MYPY_CHECK_RUNNING: + from email.message import Message + from typing import ( + Dict, List, Optional, Sequence, Tuple, Any, + Iterable, Iterator, Callable, Set, + ) + + from pip._internal.models.scheme import Scheme + from pip._internal.utils.filesystem import NamedTemporaryFileResult + + InstalledCSVRow = Tuple[str, ...] + + +logger = logging.getLogger(__name__) + + +def normpath(src, p): + # type: (str, str) -> str + return os.path.relpath(src, p).replace(os.path.sep, '/') + + +def rehash(path, blocksize=1 << 20): + # type: (str, int) -> Tuple[str, str] + """Return (encoded_digest, length) for path using hashlib.sha256()""" + h, length = hash_file(path, blocksize) + digest = 'sha256=' + urlsafe_b64encode( + h.digest() + ).decode('latin1').rstrip('=') + # unicode/str python2 issues + return (digest, str(length)) # type: ignore + + +def csv_io_kwargs(mode): + # type: (str) -> Dict[str, Any] + """Return keyword arguments to properly open a CSV file + in the given mode. + """ + if sys.version_info.major < 3: + return {'mode': '{}b'.format(mode)} + else: + return {'mode': mode, 'newline': ''} + + +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 + + +def wheel_root_is_purelib(metadata): + # type: (Message) -> bool + return metadata.get("Root-Is-Purelib", "").lower() == "true" + + +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): + # type: (pkg_resources.EntryPoint) -> Tuple[str, str] + """get the string representation of EntryPoint, + remove space and split on '=' + """ + split_parts = str(s).replace(" ", "").split("=") + return split_parts[0], split_parts[1] + + # 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[str]] + 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 + } # type: Dict[str, Set[str]] + if not warn_for: + return None + + # Format a message + msg_lines = [] + for parent_dir, dir_scripts in warn_for.items(): + sorted_scripts = sorted(dir_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")) + + # Add a note if any directory starts with ~ + warn_for_tilde = any( + i[0] == "~" for i in os.environ.get("PATH", "").split(os.pathsep) if i + ) + if warn_for_tilde: + tilde_warning_msg = ( + "NOTE: The current PATH contains path(s) starting with `~`, " + "which may not be expanded by all applications." + ) + msg_lines.append(tilde_warning_msg) + + # 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[str] + 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 + + +class MissingCallableSuffix(Exception): + pass + + +def _raise_for_invalid_entrypoint(specification): + # type: (str) -> None + entry = get_export_entry(specification) + if entry is not None and entry.suffix is None: + raise MissingCallableSuffix(str(entry)) + + +class PipScriptMaker(ScriptMaker): + def make(self, specification, options=None): + # type: (str, Dict[str, Any]) -> List[str] + _raise_for_invalid_entrypoint(specification) + return super(PipScriptMaker, self).make(specification, options) + + +def install_unpacked_wheel( + name, # type: str + wheeldir, # type: str + wheel_zip, # type: ZipFile + scheme, # type: Scheme + req_description, # type: str + pycompile=True, # type: bool + warn_script_location=True, # type: bool + direct_url=None, # type: Optional[DirectUrl] +): + # type: (...) -> None + """Install a wheel. + + :param name: Name of the project to install + :param wheeldir: Base directory of the unpacked wheel + :param wheel_zip: open ZipFile for wheel being installed + :param scheme: Distutils scheme dictating the install directories + :param req_description: String used in place of the requirement, for + logging + :param pycompile: Whether to byte-compile installed Python files + :param warn_script_location: Whether to check that scripts are installed + into a directory on PATH + :raises UnsupportedWheel: + * when the directory holds an unpacked wheel with incompatible + Wheel-Version + * when the .dist-info dir does not match the wheel + """ + # TODO: Investigate and break this up. + # TODO: Look into moving this into a dedicated class for representing an + # installation. + + source = wheeldir.rstrip(os.path.sep) + os.path.sep + + info_dir, metadata = parse_wheel(wheel_zip, name) + + if wheel_root_is_purelib(metadata): + lib_dir = scheme.purelib + else: + lib_dir = scheme.platlib + + subdirs = os.listdir(source) + data_dirs = [s for s in subdirs if s.endswith('.data')] + + # 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): + # type: (str, str, bool) -> None + """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, # type: str + dest, # type: str + is_base, # type: bool + fixer=None, # type: Optional[Callable[[str], Any]] + filter=None # type: Optional[Callable[[str], bool]] + ): + # type: (...) -> 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 == '': + subdirs[:] = [s for s in subdirs if not s.endswith('.data')] + 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) + + dest_info_dir = os.path.join(lib_dir, info_dir) + + # Get the defined entry points + ep_file = os.path.join(dest_info_dir, 'entry_points.txt') + console, gui = get_entrypoints(ep_file) + + def is_entrypoint_wrapper(name): + # type: (str) -> bool + # 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 = getattr(scheme, subdir) + clobber(source, dest, False, fixer=fixer, filter=filter) + + maker = PipScriptMaker(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 + + scripts_to_generate = [] + + # 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: + scripts_to_generate.append('pip = ' + pip_script) + + if os.environ.get("ENSUREPIP_OPTIONS", "") != "altinstall": + scripts_to_generate.append( + 'pip{} = {}'.format(sys.version_info[0], pip_script) + ) + + scripts_to_generate.append( + 'pip{} = {}'.format(get_major_minor_version(), pip_script) + ) + # 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: + scripts_to_generate.append( + 'easy_install = ' + easy_install_script + ) + + scripts_to_generate.append( + 'easy_install-{} = {}'.format( + get_major_minor_version(), easy_install_script + ) + ) + # 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 + scripts_to_generate.extend(starmap('{} = {}'.format, console.items())) + + gui_scripts_to_generate = list(starmap('{} = {}'.format, gui.items())) + + generated_console_scripts = [] # type: List[str] + + try: + generated_console_scripts = maker.make_multiple(scripts_to_generate) + generated.extend(generated_console_scripts) + + generated.extend( + maker.make_multiple(gui_scripts_to_generate, {'gui': True}) + ) + except MissingCallableSuffix as e: + entry = e.args[0] + raise InstallationError( + "Invalid script entry point: {} for req: {} - A callable " + "suffix is required. Cf https://packaging.python.org/" + "specifications/entry-points/#use-for-scripts for more " + "information.".format(entry, req_description) + ) + + if warn_script_location: + msg = message_about_scripts_not_on_PATH(generated_console_scripts) + if msg is not None: + logger.warning(msg) + + generated_file_mode = 0o666 & ~current_umask() + + @contextlib.contextmanager + def _generate_file(path, **kwargs): + # type: (str, **Any) -> Iterator[NamedTemporaryFileResult] + with adjacent_tmp_file(path, **kwargs) as f: + yield f + os.chmod(f.name, generated_file_mode) + replace(f.name, path) + + # Record pip as the installer + installer_path = os.path.join(dest_info_dir, 'INSTALLER') + with _generate_file(installer_path) as installer_file: + installer_file.write(b'pip\n') + generated.append(installer_path) + + # Record the PEP 610 direct URL reference + if direct_url is not None: + direct_url_path = os.path.join(dest_info_dir, DIRECT_URL_METADATA_NAME) + with _generate_file(direct_url_path) as direct_url_file: + direct_url_file.write(direct_url.to_json().encode("utf-8")) + generated.append(direct_url_path) + + # Record details of all files installed + record_path = os.path.join(dest_info_dir, 'RECORD') + with open(record_path, **csv_io_kwargs('r')) as record_file: + rows = get_csv_rows_for_installed( + csv.reader(record_file), + installed=installed, + changed=changed, + generated=generated, + lib_dir=lib_dir) + with _generate_file(record_path, **csv_io_kwargs('w')) as record_file: + writer = csv.writer(record_file) + writer.writerows(sorted_outrows(rows)) # sort to simplify testing + + +def install_wheel( + name, # type: str + wheel_path, # type: str + scheme, # type: Scheme + req_description, # type: str + pycompile=True, # type: bool + warn_script_location=True, # type: bool + _temp_dir_for_testing=None, # type: Optional[str] + direct_url=None, # type: Optional[DirectUrl] +): + # type: (...) -> None + with TempDirectory( + path=_temp_dir_for_testing, kind="unpacked-wheel" + ) as unpacked_dir, ZipFile(wheel_path, allowZip64=True) as z: + unpack_file(wheel_path, unpacked_dir.path) + install_unpacked_wheel( + name=name, + wheeldir=unpacked_dir.path, + wheel_zip=z, + scheme=scheme, + req_description=req_description, + pycompile=pycompile, + warn_script_location=warn_script_location, + direct_url=direct_url, + ) diff --git a/venv/lib/python3.8/site-packages/pip/_internal/operations/prepare.py b/venv/lib/python3.8/site-packages/pip/_internal/operations/prepare.py new file mode 100644 index 0000000..1fcbb77 --- /dev/null +++ b/venv/lib/python3.8/site-packages/pip/_internal/operations/prepare.py @@ -0,0 +1,568 @@ +"""Prepares a distribution for installation +""" + +# The following comment should be removed at some point in the future. +# mypy: strict-optional=False + +import logging +import mimetypes +import os +import shutil + +from pip._vendor import requests +from pip._vendor.six import PY2 + +from pip._internal.distributions import ( + make_distribution_for_install_requirement, +) +from pip._internal.distributions.installed import InstalledDistribution +from pip._internal.exceptions import ( + DirectoryUrlHashUnsupported, + HashMismatch, + HashUnpinned, + InstallationError, + PreviousBuildDirError, + VcsHashUnsupported, +) +from pip._internal.utils.filesystem import copy2_fixed +from pip._internal.utils.hashes import MissingHashes +from pip._internal.utils.logging import indent_log +from pip._internal.utils.misc import ( + display_path, + hide_url, + path_to_display, + rmtree, +) +from pip._internal.utils.temp_dir import TempDirectory +from pip._internal.utils.typing import MYPY_CHECK_RUNNING +from pip._internal.utils.unpacking import unpack_file +from pip._internal.vcs import vcs + +if MYPY_CHECK_RUNNING: + from typing import ( + Callable, List, Optional, Tuple, + ) + + from mypy_extensions import TypedDict + + from pip._internal.distributions import AbstractDistribution + from pip._internal.index.package_finder import PackageFinder + from pip._internal.models.link import Link + from pip._internal.network.download import Downloader + from pip._internal.req.req_install import InstallRequirement + from pip._internal.req.req_tracker import RequirementTracker + from pip._internal.utils.hashes import Hashes + + if PY2: + CopytreeKwargs = TypedDict( + 'CopytreeKwargs', + { + 'ignore': Callable[[str, List[str]], List[str]], + 'symlinks': bool, + }, + total=False, + ) + else: + CopytreeKwargs = TypedDict( + 'CopytreeKwargs', + { + 'copy_function': Callable[[str, str], None], + 'ignore': Callable[[str, List[str]], List[str]], + 'ignore_dangling_symlinks': bool, + 'symlinks': bool, + }, + total=False, + ) + +logger = logging.getLogger(__name__) + + +def _get_prepared_distribution( + req, # type: InstallRequirement + req_tracker, # type: RequirementTracker + finder, # type: PackageFinder + build_isolation # type: bool +): + # type: (...) -> AbstractDistribution + """Prepare a distribution for installation. + """ + abstract_dist = make_distribution_for_install_requirement(req) + with req_tracker.track(req): + abstract_dist.prepare_distribution_metadata(finder, build_isolation) + return abstract_dist + + +def unpack_vcs_link(link, location): + # type: (Link, str) -> None + vcs_backend = vcs.get_backend_for_scheme(link.scheme) + assert vcs_backend is not None + vcs_backend.unpack(location, url=hide_url(link.url)) + + +class File(object): + def __init__(self, path, content_type): + # type: (str, str) -> None + self.path = path + self.content_type = content_type + + +def get_http_url( + link, # type: Link + downloader, # type: Downloader + download_dir=None, # type: Optional[str] + hashes=None, # type: Optional[Hashes] +): + # type: (...) -> File + temp_dir = TempDirectory(kind="unpack", globally_managed=True) + # 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, downloader, temp_dir.path, hashes + ) + + return File(from_path, content_type) + + +def _copy2_ignoring_special_files(src, dest): + # type: (str, str) -> None + """Copying special files is not supported, but as a convenience to users + we skip errors copying them. This supports tools that may create e.g. + socket files in the project source directory. + """ + try: + copy2_fixed(src, dest) + except shutil.SpecialFileError as e: + # SpecialFileError may be raised due to either the source or + # destination. If the destination was the cause then we would actually + # care, but since the destination directory is deleted prior to + # copy we ignore all of them assuming it is caused by the source. + logger.warning( + "Ignoring special file error '%s' encountered copying %s to %s.", + str(e), + path_to_display(src), + path_to_display(dest), + ) + + +def _copy_source_tree(source, target): + # type: (str, str) -> None + target_abspath = os.path.abspath(target) + target_basename = os.path.basename(target_abspath) + target_dirname = os.path.dirname(target_abspath) + + def ignore(d, names): + # type: (str, List[str]) -> List[str] + skipped = [] # type: List[str] + if d == source: + # Pulling in those directories can potentially be very slow, + # exclude the following directories if they appear in the top + # level dir (and only it). + # See discussion at https://github.com/pypa/pip/pull/6770 + skipped += ['.tox', '.nox'] + if os.path.abspath(d) == target_dirname: + # Prevent an infinite recursion if the target is in source. + # This can happen when TMPDIR is set to ${PWD}/... + # and we copy PWD to TMPDIR. + skipped += [target_basename] + return skipped + + kwargs = dict(ignore=ignore, symlinks=True) # type: CopytreeKwargs + + if not PY2: + # Python 2 does not support copy_function, so we only ignore + # errors on special file copy in Python 3. + kwargs['copy_function'] = _copy2_ignoring_special_files + + shutil.copytree(source, target, **kwargs) + + +def get_file_url( + link, # type: Link + download_dir=None, # type: Optional[str] + hashes=None # type: Optional[Hashes] +): + # type: (...) -> File + """Get file and optionally check its hash. + """ + # 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.file_path + + # 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(from_path) + + content_type = mimetypes.guess_type(from_path)[0] + + return File(from_path, content_type) + + +def unpack_url( + link, # type: Link + location, # type: str + downloader, # type: Downloader + download_dir=None, # type: Optional[str] + hashes=None, # type: Optional[Hashes] +): + # type: (...) -> Optional[File] + """Unpack link into location, downloading if required. + + :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 link.is_vcs: + unpack_vcs_link(link, location) + return None + + # If it's a url to a local directory + if link.is_existing_dir(): + if os.path.isdir(location): + rmtree(location) + _copy_source_tree(link.file_path, location) + return None + + # file urls + if link.is_file: + file = get_file_url(link, download_dir, hashes=hashes) + + # http urls + else: + file = get_http_url( + link, + downloader, + download_dir, + hashes=hashes, + ) + + # unpack the archive to the build dir location. even when only downloading + # archives, they have to be unpacked to parse dependencies + unpack_file(file.path, location, file.content_type) + + return file + + +def _download_http_url( + link, # type: Link + downloader, # type: Downloader + temp_dir, # type: str + hashes, # type: Optional[Hashes] +): + # type: (...) -> Tuple[str, str] + """Download link url into temp_dir using provided session""" + download = downloader(link) + + file_path = os.path.join(temp_dir, download.filename) + with open(file_path, 'wb') as content_file: + for chunk in download.chunks: + content_file.write(chunk) + + if hashes: + hashes.check_against_path(file_path) + + return file_path, download.response.headers.get('content-type', '') + + +def _check_download_dir(link, download_dir, hashes): + # type: (Link, str, Optional[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 not os.path.exists(download_path): + return None + + # 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 + + +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] + build_isolation, # type: bool + req_tracker, # type: RequirementTracker + downloader, # type: Downloader + finder, # type: PackageFinder + require_hashes, # type: bool + use_user_site, # type: bool + ): + # type: (...) -> None + super(RequirementPreparer, self).__init__() + + self.src_dir = src_dir + self.build_dir = build_dir + self.req_tracker = req_tracker + self.downloader = downloader + self.finder = finder + + # 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. + 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'. + + # Is build isolation allowed? + self.build_isolation = build_isolation + + # Should hash-checking be required? + self.require_hashes = require_hashes + + # Should install in user site-packages? + self.use_user_site = use_user_site + + @property + def _download_should_save(self): + # type: () -> bool + if not self.download_dir: + return False + + if os.path.exists(self.download_dir): + return True + + logger.critical('Could not find download directory') + raise InstallationError( + "Could not find or access download directory '{}'" + .format(self.download_dir)) + + def prepare_linked_requirement( + self, + req, # type: InstallRequirement + ): + # type: (...) -> AbstractDistribution + """Prepare a requirement that would be obtained from req.link + """ + assert req.link + link = req.link + + # TODO: Breakup into smaller functions + if link.scheme == 'file': + path = link.file_path + logger.info('Processing %s', display_path(path)) + else: + logger.info('Collecting %s', req.req or req) + + download_dir = self.download_dir + if 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 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 + else: + # We always delete unpacked sdists after pip runs. + autodelete_unpacked = True + + with indent_log(): + # Since source_dir is only set for editable requirements. + assert req.source_dir is None + req.ensure_has_source_dir(self.build_dir, autodelete_unpacked) + # 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` + if os.path.exists(os.path.join(req.source_dir, 'setup.py')): + raise PreviousBuildDirError( + "pip can't proceed with requirements '{}' due to a" + " pre-existing build directory ({}). 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." + .format(req, req.source_dir) + ) + + # 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 self.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 link.is_vcs: + raise VcsHashUnsupported() + elif link.is_existing_dir(): + 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 self.require_hashes) + if self.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: + local_file = unpack_url( + link, req.source_dir, self.downloader, download_dir, + hashes=hashes, + ) + except requests.HTTPError as exc: + logger.critical( + 'Could not install requirement %s because of error %s', + req, + exc, + ) + raise InstallationError( + 'Could not install requirement {} because of HTTP ' + 'error {} for URL {}'.format(req, exc, link) + ) + + # For use in later processing, preserve the file path on the + # requirement. + if local_file: + req.local_file_path = local_file.path + + abstract_dist = _get_prepared_distribution( + req, self.req_tracker, self.finder, self.build_isolation, + ) + + if download_dir: + if link.is_existing_dir(): + logger.info('Link is a directory, ignoring download_dir') + elif local_file: + download_location = os.path.join( + download_dir, link.filename + ) + if not os.path.exists(download_location): + shutil.copy(local_file.path, download_location) + logger.info( + 'Saved %s', display_path(download_location) + ) + + if self._download_should_save: + # Make a .zip of the source_dir we already created. + if link.is_vcs: + req.archive(self.download_dir) + return abstract_dist + + def prepare_editable_requirement( + self, + req, # type: InstallRequirement + ): + # type: (...) -> AbstractDistribution + """Prepare an editable requirement + """ + assert req.editable, "cannot prepare a non-editable req as editable" + + logger.info('Obtaining %s', req) + + with indent_log(): + if self.require_hashes: + raise InstallationError( + 'The editable requirement {} cannot be installed when ' + 'requiring hashes, because there is no single file to ' + 'hash.'.format(req) + ) + req.ensure_has_source_dir(self.src_dir) + req.update_editable(not self._download_should_save) + + abstract_dist = _get_prepared_distribution( + req, self.req_tracker, self.finder, self.build_isolation, + ) + + if self._download_should_save: + req.archive(self.download_dir) + req.check_if_exists(self.use_user_site) + + return abstract_dist + + def prepare_installed_requirement( + self, + req, # type: InstallRequirement + skip_reason # type: str + ): + # type: (...) -> AbstractDistribution + """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 {}".format(req.satisfied_by) + ) + logger.info( + 'Requirement %s: %s (%s)', + skip_reason, req, req.satisfied_by.version + ) + with indent_log(): + if self.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 = InstalledDistribution(req) + + return abstract_dist diff --git a/venv/lib/python3.8/site-packages/pip/_internal/pyproject.py b/venv/lib/python3.8/site-packages/pip/_internal/pyproject.py new file mode 100644 index 0000000..6b4faf7 --- /dev/null +++ b/venv/lib/python3.8/site-packages/pip/_internal/pyproject.py @@ -0,0 +1,196 @@ +from __future__ import absolute_import + +import io +import os +import sys +from collections import namedtuple + +from pip._vendor import six, toml +from pip._vendor.packaging.requirements import InvalidRequirement, Requirement + +from pip._internal.exceptions import InstallationError +from pip._internal.utils.typing import MYPY_CHECK_RUNNING + +if MYPY_CHECK_RUNNING: + from typing import Any, 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(unpacked_source_directory): + # type: (str) -> str + path = os.path.join(unpacked_source_directory, 'pyproject.toml') + + # Python2 __file__ should not be unicode + if six.PY2 and isinstance(path, six.text_type): + path = path.encode(sys.getfilesystemencoding()) + + return path + + +BuildSystemDetails = namedtuple('BuildSystemDetails', [ + 'requires', 'backend', 'check', 'backend_path' +]) + + +def load_pyproject_toml( + use_pep517, # type: Optional[bool] + pyproject_toml, # type: str + setup_py, # type: str + req_name # type: str +): + # type: (...) -> Optional[BuildSystemDetails] + """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 + directory paths to import the backend from (backend-path), + relative to the project root. + ) + """ + 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 = toml.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.", + )) + + # Each requirement must be valid as per PEP 508 + for requirement in requires: + try: + Requirement(requirement) + except InvalidRequirement: + raise InstallationError( + error_template.format( + package=req_name, + reason=( + "'build-system.requires' contains an invalid " + "requirement: {!r}".format(requirement) + ), + ) + ) + + backend = build_system.get("build-backend") + backend_path = build_system.get("backend-path", []) + 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 BuildSystemDetails(requires, backend, check, backend_path) diff --git a/venv/lib/python3.8/site-packages/pip/_internal/req/__init__.py b/venv/lib/python3.8/site-packages/pip/_internal/req/__init__.py new file mode 100644 index 0000000..d2d027a --- /dev/null +++ b/venv/lib/python3.8/site-packages/pip/_internal/req/__init__.py @@ -0,0 +1,92 @@ +# The following comment should be removed at some point in the future. +# mypy: strict-optional=False + +from __future__ import absolute_import + +import logging + +from pip._internal.utils.logging import indent_log +from pip._internal.utils.typing import MYPY_CHECK_RUNNING + +from .req_file import parse_requirements +from .req_install import InstallRequirement +from .req_set import RequirementSet + +if MYPY_CHECK_RUNNING: + from typing import Any, List, Sequence + +__all__ = [ + "RequirementSet", "InstallRequirement", + "parse_requirements", "install_given_reqs", +] + +logger = logging.getLogger(__name__) + + +class InstallationResult(object): + def __init__(self, name): + # type: (str) -> None + self.name = name + + def __repr__(self): + # type: () -> str + return "InstallationResult(name={!r})".format(self.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[InstallationResult] + """ + 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]), + ) + + installed = [] + + with indent_log(): + for requirement in to_install: + if requirement.should_reinstall: + logger.info('Attempting uninstall: %s', requirement.name) + with indent_log(): + uninstalled_pathset = requirement.uninstall( + auto_confirm=True + ) + try: + requirement.install( + install_options, + global_options, + *args, + **kwargs + ) + except Exception: + should_rollback = ( + requirement.should_reinstall and + not requirement.install_succeeded + ) + # if install did not succeed, rollback previous uninstall + if should_rollback: + uninstalled_pathset.rollback() + raise + else: + should_commit = ( + requirement.should_reinstall and + requirement.install_succeeded + ) + if should_commit: + uninstalled_pathset.commit() + + installed.append(InstallationResult(requirement.name)) + + return installed diff --git a/venv/lib/python3.8/site-packages/pip/_internal/req/__pycache__/__init__.cpython-38.pyc b/venv/lib/python3.8/site-packages/pip/_internal/req/__pycache__/__init__.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..91b9e4da78139b8bb38e4ece9e795ba360d037b6 GIT binary patch literal 2246 zcmaJCO>Y}TbY^zFUOWCsoVHZ`5H2dHmZ%*ST2WN0R&7;2N`RzQ+m))-#xr&{*{{rw z6JqQO?Fk7GSAEgi1)egW;T2x(o6ekII}I_vOPxbE39s?` zBf{(A@I%8n!W(>!S07R5HO`hvYyK%%NLp0ay_GZ$21>Yon8Z>o0KYWm11MyOA&%hiUJ=+sq%tg={XswwFAdx zp<0GEu14G1yyd4#+snW!qOOoOXqH6;769GwC%W9ezbPVoDeVINg~i}>1$KG@fFKU# z1R_m2dqf3a*k za2TFn$W}k<-0mcPa&{0UUUyyavs?vN2#qq`K`WsAd;uWCAj{(tb* zDLHOI6s%q>Lr-OKXi+%}I@rIFWL!|jL-PHv6nL+b2am_r$XYXHFOw<&UO>K#rvW_Hn#C<% z{FaW3cgcD31z95Z$nYr8i%?vw$417_Y-D~1W8yA}7EGe-^|Jhh68xd&_(y=H3K`oY zTRt8YcgnkP^EJ6oqGR_a9KRvQxHKxs-{F1+??;85imC#=fRvy9dx@wK&Z7XA$}_mUTuL>p|7;% zC5ec5R_(2_E`ve2s(LOTXZliI>j&|Q7r6P^^khHP*7{cVElt_snHdOWKH>HJzLkQz ziKkXmp>#d>D7U9NB}~kEDGw5d&1p=)#{fvpq$Xvwv1b-c2E9rxS_25Qe`XDq-LXAp zuxHFppC0Ij1qHhKd!kT5nR7LP7sE+A|AI84;` z{zPpLlpmz+>E$Fl$Nq~$Z6_ctv0xzTV#DqEftdV9VRZ5!XMbI~h!hA*_HU-GscZ>k zCC(d(14kEK7y1asTB$G8t*lby5xhN#w^qEe28}V9S{SYaA>5yG+P*Xyj+fD{^bG(N ZX0r;d;09>YLk1&^!iM0MjoB)B_AjeLRQmt` literal 0 HcmV?d00001 diff --git a/venv/lib/python3.8/site-packages/pip/_internal/req/__pycache__/constructors.cpython-38.pyc b/venv/lib/python3.8/site-packages/pip/_internal/req/__pycache__/constructors.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..6c244c9a2658ccc2eef9064d38bc9e760ab4f67c GIT binary patch literal 10899 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 oh|}K#_nkZj-tNftRQ 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 F{{bi|$!-7u literal 0 HcmV?d00001 diff --git a/venv/lib/python3.8/site-packages/pip/_internal/req/__pycache__/req_set.cpython-38.pyc b/venv/lib/python3.8/site-packages/pip/_internal/req/__pycache__/req_set.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..4095974813af8a517938659890800c3bff04af25 GIT binary patch literal 5892 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 literal 0 HcmV?d00001 diff --git a/venv/lib/python3.8/site-packages/pip/_internal/req/__pycache__/req_tracker.cpython-38.pyc b/venv/lib/python3.8/site-packages/pip/_internal/req/__pycache__/req_tracker.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..8f5585b280dcdd4aec4286f24b1a9459b090226c GIT binary patch literal 4107 zcmZ`+TXP$?6$bXk8naMAp`j$V^AA#3C^)K`#{SH>NEk#)l4zR%41K?b~bNFCprpEBR zdG+t&Z}W`(hhAQMJiL5}Ru~Y$1W#FycQ~i9lR6#8_HM_uz1Q(%B&&!I^BR3HX9^rq}E=?OG+B?ag)OdgnUldh?z6-b6{)_vuHaD~JMvyt8;aLiTTgIy7yId`wz>vKaJKNtghZ!{lv1VxGOi}?(X_QU$z6|eVlZ)3GZmB zVti@ByM3MHS)3a8zSPDeHse3YFh@e~WQC5?bWQFJuvbrJ8nfO?lI?cstkb800oiaD zt@sy+W;=Y$wR6bEPQu3Sh)vuh_A&d@2VXm5Z|o1}MttO5Vk39tz0ba6*%gmzZ?~pa zIHTXlDObK?ne&j1eByqe?Ohz!1p5N(U$UF{_E_ekl^KkJk+U1Br^Nd^wwG#u6zJg4 zDZbN{CmgG#jPxP==05wLueL+u%d<#6P6`dBMZH+b^B~(OY2Mhqmvl)w~etga;9$bIa^~7t;3WfGJZQ)HM95b z+>6$hKabYeKDzzs^4hSD`(Hc0KY96Xl8H7~Z{Uo>)}1WTNt`A_DOwwYBo!^onPgk7 zShwCP+6^@aMs*IvxNrbP7d6%8$BDkLp;I#hKagswNkyP$ynd{=t&U8P=3CIL@rB$N zY?=8NLTu|wkX*o!P_U?-oPP$9g#264VK9_&b)#9 z{H$sT0^pH1(49VNHKM!0zoO5uTF{;h@i)A3=1GrPyOxbu@z9vmZ&lyG0GQ2=fVHG% zHH!%ZMWvg{I2-iao;`&L<>PLoDQ8q@1Yk3Po!U5kKUP z61|-dbhN4BElO-Sc-GAY0C`(J774Iimm&RgY-hZkJjqG}xaHNo6j{&&BwLEpx2;=G zJBTR0|40|MnsyfW@YS;ng6&nweB##Av}#TAZ-^n2;MRnd&akC{azMK#(yL8{V~^6} z6z{U@$0_w|&c;4x!}*cF_BU>hS6}<5p6%;uH}t4q6+M0GRMt-)em`HvG}>3M3t**BrhshKVeNA&b$QBY<* zi9l)vlK+fWyaB?3hKJULDeKZ1$|?n) zz;M#nGwk^_UNqux2VReIwn>!=X^u3tS9_M9d_wnU`DuFH7YYZPpHw?g*CDXt60cgiOylm7$lI3YFZDwof=$}v>`$o7P#UHbl2kP=bo^krdU55Ooa+Iz)$I@b(GVK(Z0+?uFBHyik+Dj*oy<5q{Sls zSo9;2sIvUKd?w35pQKWRLbFJ%g`&y>e=N!cN(w3@QYAvQw5k)KoZY#4Qlyf5i)big zEY7MK@R&;{d7yrYquDrX(s~sozMMb)^nmS9xCVaoN=?4@y g#m_(*4*n37Kc@loY5=_&yoHAP)Ae}mGTQ9_0UNOa0ssI2 literal 0 HcmV?d00001 diff --git a/venv/lib/python3.8/site-packages/pip/_internal/req/__pycache__/req_uninstall.cpython-38.pyc b/venv/lib/python3.8/site-packages/pip/_internal/req/__pycache__/req_uninstall.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..e72698c3e38e2cc52a952308e158fb67c5a62f3d GIT binary patch literal 17512 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 literal 0 HcmV?d00001 diff --git a/venv/lib/python3.8/site-packages/pip/_internal/req/constructors.py b/venv/lib/python3.8/site-packages/pip/_internal/req/constructors.py new file mode 100644 index 0000000..c9f1fe7 --- /dev/null +++ b/venv/lib/python3.8/site-packages/pip/_internal/req/constructors.py @@ -0,0 +1,464 @@ +"""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. +""" + +# The following comment should be removed at some point in the future. +# mypy: strict-optional=False + +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.exceptions import InstallationError +from pip._internal.models.index import PyPI, TestPyPI +from pip._internal.models.link import Link +from pip._internal.models.wheel import Wheel +from pip._internal.pyproject import make_pyproject_path +from pip._internal.req.req_install import InstallRequirement +from pip._internal.utils.filetypes import ARCHIVE_EXTENSIONS +from pip._internal.utils.misc import is_installable_dir, splitext +from pip._internal.utils.typing import MYPY_CHECK_RUNNING +from pip._internal.utils.urls import path_to_url +from pip._internal.vcs import is_url, vcs + +if MYPY_CHECK_RUNNING: + from typing import ( + Any, Dict, Optional, Set, Tuple, Union, + ) + from pip._internal.req.req_file import ParsedRequirement + + +__all__ = [ + "install_req_from_editable", "install_req_from_line", + "parse_editable" +] + +logger = logging.getLogger(__name__) +operators = Specifier._operators.keys() + + +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 _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 convert_extras(extras): + # type: (Optional[str]) -> Set[str] + if not extras: + return set() + return Requirement("placeholder" + extras.lower()).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('{}:'.format(version_control)): + url = '{}+{}'.format(version_control, url) + break + + if '+' not in url: + raise InstallationError( + '{} is not a valid editable requirement. ' + 'It should either be a path to a local project or a VCS URL ' + '(beginning with svn+, git+, hg+, or bzr+).'.format(editable_req) + ) + + vc_type = url.split('+', 1)[0].lower() + + if not vcs.get_backend(vc_type): + backends = ", ".join([bends.name + '+URL' for bends in vcs.backends]) + error_message = "For --editable={}, " \ + "only {} are currently supported".format( + editable_req, backends) + raise InstallationError(error_message) + + package_name = Link(url).egg_fragment + if not package_name: + raise InstallationError( + "Could not detect requirement name for '{}', please specify one " + "with #egg=your_package_name".format(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 " + "({}) appears to be a" + " requirements file. If that is the" + " case, use the '-r' flag to install" + " the packages specified within it." + ).format(req) + except RequirementParseError: + logger.debug("Cannot parse '{}' as requirements \ + file".format(req), exc_info=True) + else: + msg += " File '{}' does not exist.".format(req) + return msg + + +class RequirementParts(object): + def __init__( + self, + requirement, # type: Optional[Requirement] + link, # type: Optional[Link] + markers, # type: Optional[Marker] + extras, # type: Set[str] + ): + self.requirement = requirement + self.link = link + self.markers = markers + self.extras = extras + + +def parse_req_from_editable(editable_req): + # type: (str) -> RequirementParts + name, url, extras_override = parse_editable(editable_req) + + if name is not None: + try: + req = Requirement(name) + except InvalidRequirement: + raise InstallationError("Invalid requirement: '{}'".format(name)) + else: + req = None + + link = Link(url) + + return RequirementParts(req, link, None, extras_override) + + +# ---- The actual constructors follow ---- + + +def install_req_from_editable( + editable_req, # type: str + comes_from=None, # type: Optional[Union[InstallRequirement, str]] + use_pep517=None, # type: Optional[bool] + isolated=False, # type: bool + options=None, # type: Optional[Dict[str, Any]] + constraint=False # type: bool +): + # type: (...) -> InstallRequirement + + parts = parse_req_from_editable(editable_req) + + return InstallRequirement( + parts.requirement, + comes_from=comes_from, + editable=True, + link=parts.link, + constraint=constraint, + use_pep517=use_pep517, + isolated=isolated, + install_options=options.get("install_options", []) if options else [], + global_options=options.get("global_options", []) if options else [], + hash_options=options.get("hashes", {}) if options else {}, + extras=parts.extras, + ) + + +def _looks_like_path(name): + # type: (str) -> bool + """Checks whether the string "looks like" a path on the filesystem. + + This does not check whether the target actually exists, only judge from the + appearance. + + Returns true if any of the following conditions is true: + * a path separator is found (either os.path.sep or os.path.altsep); + * a dot is found (which represents the current directory). + """ + if os.path.sep in name: + return True + if os.path.altsep is not None and os.path.altsep in name: + return True + if name.startswith("."): + return True + return False + + +def _get_url_from_path(path, name): + # type: (str, str) -> str + """ + First, it checks whether a provided path is an installable directory + (e.g. it has a setup.py). If it is, returns the path. + + If false, check if the path is an archive file (such as a .whl). + The function checks if the path is a file. If false, if the path has + an @, it will treat it as a PEP 440 URL requirement and return the path. + """ + if _looks_like_path(name) and os.path.isdir(path): + if is_installable_dir(path): + return path_to_url(path) + raise InstallationError( + "Directory {name!r} is not installable. Neither 'setup.py' " + "nor 'pyproject.toml' found.".format(**locals()) + ) + if not is_archive_file(path): + return None + if os.path.isfile(path): + return path_to_url(path) + urlreq_parts = name.split('@', 1) + if len(urlreq_parts) >= 2 and not _looks_like_path(urlreq_parts[0]): + # If the path contains '@' and the part before it does not look + # like a path, try to treat it as a PEP 440 URL req instead. + return None + logger.warning( + 'Requirement %r looks like a filename, but the ' + 'file does not exist', + name + ) + return path_to_url(path) + + +def parse_req_from_line(name, line_source): + # type: (str, Optional[str]) -> RequirementParts + 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) + url = _get_url_from_path(p, name) + if url is not None: + link = Link(url) + + # 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 = "{wheel.name}=={wheel.version}".format(**locals()) + 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 + + extras = convert_extras(extras_as_string) + + def with_source(text): + # type: (str) -> str + if not line_source: + return text + return '{} (from {})'.format(text, line_source) + + 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 = '' + msg = with_source( + 'Invalid requirement: {!r}'.format(req_as_string) + ) + if add_msg: + msg += '\nHint: {}'.format(add_msg) + raise InstallationError(msg) + else: + req = None + + return RequirementParts(req, link, markers, extras) + + +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]] + constraint=False, # type: bool + line_source=None, # type: Optional[str] +): + # type: (...) -> InstallRequirement + """Creates an InstallRequirement from a name, which might be a + requirement, directory containing 'setup.py', filename, or URL. + + :param line_source: An optional string describing where the line is from, + for logging purposes in case of an error. + """ + parts = parse_req_from_line(name, line_source) + + return InstallRequirement( + parts.requirement, comes_from, link=parts.link, markers=parts.markers, + use_pep517=use_pep517, isolated=isolated, + install_options=options.get("install_options", []) if options else [], + global_options=options.get("global_options", []) if options else [], + hash_options=options.get("hashes", {}) if options else {}, + constraint=constraint, + extras=parts.extras, + ) + + +def install_req_from_req_string( + req_string, # type: str + comes_from=None, # type: Optional[InstallRequirement] + isolated=False, # type: bool + use_pep517=None # type: Optional[bool] +): + # type: (...) -> InstallRequirement + try: + req = Requirement(req_string) + except InvalidRequirement: + raise InstallationError("Invalid requirement: '{}'".format(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" + "{} depends on {} ".format(comes_from.name, req) + ) + + return InstallRequirement( + req, comes_from, isolated=isolated, use_pep517=use_pep517 + ) + + +def install_req_from_parsed_requirement( + parsed_req, # type: ParsedRequirement + isolated=False, # type: bool + use_pep517=None # type: Optional[bool] +): + # type: (...) -> InstallRequirement + if parsed_req.is_editable: + req = install_req_from_editable( + parsed_req.requirement, + comes_from=parsed_req.comes_from, + use_pep517=use_pep517, + constraint=parsed_req.constraint, + isolated=isolated, + ) + + else: + req = install_req_from_line( + parsed_req.requirement, + comes_from=parsed_req.comes_from, + use_pep517=use_pep517, + isolated=isolated, + options=parsed_req.options, + constraint=parsed_req.constraint, + line_source=parsed_req.line_source, + ) + return req diff --git a/venv/lib/python3.8/site-packages/pip/_internal/req/req_file.py b/venv/lib/python3.8/site-packages/pip/_internal/req/req_file.py new file mode 100644 index 0000000..63cab76 --- /dev/null +++ b/venv/lib/python3.8/site-packages/pip/_internal/req/req_file.py @@ -0,0 +1,582 @@ +""" +Requirements file parsing +""" + +# The following comment should be removed at some point in the future. +# mypy: strict-optional=False + +from __future__ import absolute_import + +import optparse +import os +import re +import shlex +import sys + +from pip._vendor.six.moves.urllib import parse as urllib_parse + +from pip._internal.cli import cmdoptions +from pip._internal.exceptions import ( + InstallationError, + RequirementsFileParseError, +) +from pip._internal.models.search_scope import SearchScope +from pip._internal.utils.encoding import auto_decode +from pip._internal.utils.typing import MYPY_CHECK_RUNNING +from pip._internal.utils.urls import get_url_scheme + +if MYPY_CHECK_RUNNING: + from optparse import Values + from typing import ( + Any, Callable, Dict, Iterator, List, NoReturn, Optional, Text, Tuple, + ) + + from pip._internal.index.package_finder import PackageFinder + from pip._internal.network.session import PipSession + + ReqFileLines = Iterator[Tuple[int, Text]] + + LineParser = Callable[[Text], Tuple[str, Values]] + + +__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.index_url, + cmdoptions.extra_index_url, + cmdoptions.no_index, + cmdoptions.constraints, + cmdoptions.requirements, + cmdoptions.editable, + cmdoptions.find_links, + cmdoptions.no_binary, + cmdoptions.only_binary, + cmdoptions.require_hashes, + cmdoptions.pre, + cmdoptions.trusted_host, + cmdoptions.always_unzip, # Deprecated +] # 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] + + +class ParsedRequirement(object): + def __init__( + self, + requirement, # type:str + is_editable, # type: bool + comes_from, # type: str + constraint, # type: bool + options=None, # type: Optional[Dict[str, Any]] + line_source=None, # type: Optional[str] + ): + # type: (...) -> None + self.requirement = requirement + self.is_editable = is_editable + self.comes_from = comes_from + self.options = options + self.constraint = constraint + self.line_source = line_source + + +class ParsedLine(object): + def __init__( + self, + filename, # type: str + lineno, # type: int + comes_from, # type: str + args, # type: str + opts, # type: Values + constraint, # type: bool + ): + # type: (...) -> None + self.filename = filename + self.lineno = lineno + self.comes_from = comes_from + self.opts = opts + self.constraint = constraint + + if args: + self.is_requirement = True + self.is_editable = False + self.requirement = args + elif opts.editables: + self.is_requirement = True + self.is_editable = True + # We don't support multiple -e on one line + self.requirement = opts.editables[0] + else: + self.is_requirement = False + + +def parse_requirements( + filename, # type: str + session, # type: PipSession + finder=None, # type: Optional[PackageFinder] + comes_from=None, # type: Optional[str] + options=None, # type: Optional[optparse.Values] + constraint=False, # type: bool +): + # type: (...) -> Iterator[ParsedRequirement] + """Parse a requirements file and yield InstallRequirement instances. + + :param filename: Path or url of requirements file. + :param session: PipSession instance. + :param finder: Instance of pip.index.PackageFinder. + :param comes_from: Origin description of requirements. + :param options: cli options. + :param constraint: If true, parsing a constraint file rather than + requirements file. + """ + line_parser = get_line_parser(finder) + parser = RequirementsFileParser(session, line_parser, comes_from) + + for parsed_line in parser.parse(filename, constraint): + parsed_req = handle_line( + parsed_line, + options=options, + finder=finder, + session=session + ) + if parsed_req is not None: + yield parsed_req + + +def preprocess(content): + # type: (Text) -> ReqFileLines + """Split, filter, and join lines, and return a line iterator + + :param content: the content of the requirements file + """ + lines_enum = enumerate(content.splitlines(), start=1) # type: ReqFileLines + lines_enum = join_lines(lines_enum) + lines_enum = ignore_comments(lines_enum) + lines_enum = expand_env_variables(lines_enum) + return lines_enum + + +def handle_requirement_line( + line, # type: ParsedLine + options=None, # type: Optional[optparse.Values] +): + # type: (...) -> ParsedRequirement + + # preserve for the nested code path + line_comes_from = '{} {} (line {})'.format( + '-c' if line.constraint else '-r', line.filename, line.lineno, + ) + + assert line.is_requirement + + if line.is_editable: + # For editable requirements, we don't support per-requirement + # options, so just return the parsed requirement. + return ParsedRequirement( + requirement=line.requirement, + is_editable=line.is_editable, + comes_from=line_comes_from, + constraint=line.constraint, + ) + else: + if options: + # Disable wheels if the user has specified build options + cmdoptions.check_install_build_global(options, line.opts) + + # get the options that apply to requirements + req_options = {} + for dest in SUPPORTED_OPTIONS_REQ_DEST: + if dest in line.opts.__dict__ and line.opts.__dict__[dest]: + req_options[dest] = line.opts.__dict__[dest] + + line_source = 'line {} of {}'.format(line.lineno, line.filename) + return ParsedRequirement( + requirement=line.requirement, + is_editable=line.is_editable, + comes_from=line_comes_from, + constraint=line.constraint, + options=req_options, + line_source=line_source, + ) + + +def handle_option_line( + opts, # type: Values + filename, # type: str + lineno, # type: int + finder=None, # type: Optional[PackageFinder] + options=None, # type: Optional[optparse.Values] + session=None, # type: Optional[PipSession] +): + # type: (...) -> None + + # percolate hash-checking option upward + if opts.require_hashes: + options.require_hashes = opts.require_hashes + + # set finder options + elif finder: + find_links = finder.find_links + index_urls = finder.index_urls + if opts.index_url: + index_urls = [opts.index_url] + if opts.no_index is True: + index_urls = [] + if opts.extra_index_urls: + 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 + find_links.append(value) + + search_scope = SearchScope( + find_links=find_links, + index_urls=index_urls, + ) + finder.search_scope = search_scope + + if opts.pre: + finder.set_allow_all_prereleases() + + if session: + for host in opts.trusted_hosts or []: + source = 'line {} of {}'.format(lineno, filename) + session.add_trusted_host(host, source=source) + + +def handle_line( + line, # type: ParsedLine + options=None, # type: Optional[optparse.Values] + finder=None, # type: Optional[PackageFinder] + session=None, # type: Optional[PipSession] +): + # type: (...) -> Optional[ParsedRequirement] + """Handle a single parsed requirements line; This can result in + creating/yielding requirements, or updating the finder. + + :param line: The parsed line to be processed. + :param options: CLI options. + :param finder: The finder - updated by non-requirement lines. + :param session: The session - updated by non-requirement lines. + + Returns a ParsedRequirement object if the line is a requirement line, + otherwise returns None. + + 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. + """ + + if line.is_requirement: + parsed_req = handle_requirement_line(line, options) + return parsed_req + else: + handle_option_line( + line.opts, + line.filename, + line.lineno, + finder, + options, + session, + ) + return None + + +class RequirementsFileParser(object): + def __init__( + self, + session, # type: PipSession + line_parser, # type: LineParser + comes_from, # type: str + ): + # type: (...) -> None + self._session = session + self._line_parser = line_parser + self._comes_from = comes_from + + def parse(self, filename, constraint): + # type: (str, bool) -> Iterator[ParsedLine] + """Parse a given file, yielding parsed lines. + """ + for line in self._parse_and_recurse(filename, constraint): + yield line + + def _parse_and_recurse(self, filename, constraint): + # type: (str, bool) -> Iterator[ParsedLine] + for line in self._parse_file(filename, constraint): + if ( + not line.is_requirement and + (line.opts.requirements or line.opts.constraints) + ): + # parse a nested requirements file + if line.opts.requirements: + req_path = line.opts.requirements[0] + nested_constraint = False + else: + req_path = line.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, + ) + + for inner_line in self._parse_and_recurse( + req_path, nested_constraint, + ): + yield inner_line + else: + yield line + + def _parse_file(self, filename, constraint): + # type: (str, bool) -> Iterator[ParsedLine] + _, content = get_file_content( + filename, self._session, comes_from=self._comes_from + ) + + lines_enum = preprocess(content) + + for line_number, line in lines_enum: + try: + args_str, opts = self._line_parser(line) + except OptionParsingError as e: + # add offending line + msg = 'Invalid requirement: {}\n{}'.format(line, e.msg) + raise RequirementsFileParseError(msg) + + yield ParsedLine( + filename, + line_number, + self._comes_from, + args_str, + opts, + constraint, + ) + + +def get_line_parser(finder): + # type: (Optional[PackageFinder]) -> LineParser + def parse_line(line): + # type: (Text) -> Tuple[str, Values] + # Build new parser for each line since it accumulates appendable + # options. + parser = build_parser() + 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 + + return args_str, opts + + return parse_line + + +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 + + +class OptionParsingError(Exception): + def __init__(self, msg): + # type: (str) -> None + self.msg = msg + + +def build_parser(): + # type: () -> 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 + raise OptionParsingError(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 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 + + +def get_file_content(url, session, comes_from=None): + # type: (str, PipSession, Optional[str]) -> 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. + Respects # -*- coding: declarations on the retrieved files. + + :param url: File path or url. + :param session: PipSession instance. + :param comes_from: Origin description of requirements. + """ + scheme = get_url_scheme(url) + + if scheme in ['http', 'https']: + # FIXME: catch some errors + resp = session.get(url) + resp.raise_for_status() + return resp.url, resp.text + + elif scheme == 'file': + if comes_from and comes_from.startswith('http'): + raise InstallationError( + 'Requirements file {} references URL {}, ' + 'which is local'.format(comes_from, url) + ) + + 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 + + try: + with open(url, 'rb') as f: + content = auto_decode(f.read()) + except IOError as exc: + raise InstallationError( + 'Could not open requirements file: {}'.format(exc) + ) + return url, content + + +_url_slash_drive_re = re.compile(r'/*([a-z])\|', re.I) diff --git a/venv/lib/python3.8/site-packages/pip/_internal/req/req_install.py b/venv/lib/python3.8/site-packages/pip/_internal/req/req_install.py new file mode 100644 index 0000000..3b28209 --- /dev/null +++ b/venv/lib/python3.8/site-packages/pip/_internal/req/req_install.py @@ -0,0 +1,850 @@ +# The following comment should be removed at some point in the future. +# mypy: strict-optional=False + +from __future__ import absolute_import + +import logging +import os +import shutil +import sys +import zipfile + +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.build_env import NoOpBuildEnvironment +from pip._internal.exceptions import InstallationError +from pip._internal.locations import get_scheme +from pip._internal.models.link import Link +from pip._internal.operations.build.metadata import generate_metadata +from pip._internal.operations.build.metadata_legacy import \ + generate_metadata as generate_metadata_legacy +from pip._internal.operations.install.editable_legacy import \ + install_editable as install_editable_legacy +from pip._internal.operations.install.legacy import LegacyInstallFailure +from pip._internal.operations.install.legacy import install as install_legacy +from pip._internal.operations.install.wheel import install_wheel +from pip._internal.pyproject import load_pyproject_toml, make_pyproject_path +from pip._internal.req.req_uninstall import UninstallPathSet +from pip._internal.utils.deprecation import deprecated +from pip._internal.utils.direct_url_helpers import direct_url_from_link +from pip._internal.utils.hashes import Hashes +from pip._internal.utils.logging import indent_log +from pip._internal.utils.misc import ( + ask_path_exists, + backup_dir, + display_path, + dist_in_site_packages, + dist_in_usersite, + get_installed_version, + hide_url, + redact_auth_from_url, +) +from pip._internal.utils.packaging import get_metadata +from pip._internal.utils.temp_dir import TempDirectory, tempdir_kinds +from pip._internal.utils.typing import MYPY_CHECK_RUNNING +from pip._internal.utils.virtualenv import running_under_virtualenv +from pip._internal.vcs import vcs + +if MYPY_CHECK_RUNNING: + from typing import ( + Any, Dict, Iterable, List, Optional, Sequence, Union, + ) + from pip._internal.build_env import BuildEnvironment + 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__) + + +def _get_dist(metadata_directory): + # type: (str) -> Distribution + """Return a pkg_resources.Distribution for the provided + metadata directory. + """ + dist_dir = metadata_directory.rstrip(os.sep) + + # Build a PathMetadata object, from path to metadata. :wink: + base_dir, dist_dir_name = os.path.split(dist_dir) + metadata = pkg_resources.PathMetadata(base_dir, dist_dir) + + # Determine the correct Distribution object type. + if dist_dir.endswith(".egg-info"): + dist_cls = pkg_resources.Distribution + dist_name = os.path.splitext(dist_dir_name)[0] + else: + assert dist_dir.endswith(".dist-info") + dist_cls = pkg_resources.DistInfoDistribution + dist_name = os.path.splitext(dist_dir_name)[0].split("-")[0] + + return dist_cls( + base_dir, + project_name=dist_name, + metadata=metadata, + ) + + +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]] + editable=False, # type: bool + link=None, # type: Optional[Link] + markers=None, # type: Optional[Marker] + use_pep517=None, # type: Optional[bool] + isolated=False, # type: bool + install_options=None, # type: Optional[List[str]] + global_options=None, # type: Optional[List[str]] + hash_options=None, # type: Optional[Dict[str, List[str]]] + 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 + self.editable = editable + + # source_dir is the local directory where the linked requirement is + # located, or unpacked. In case unpacking is needed, creating and + # populating source_dir is done by the RequirementPreparer. Note this + # is not necessarily the directory where pyproject.toml or setup.py is + # located - that one is obtained via unpacked_source_directory. + self.source_dir = None # type: Optional[str] + if self.editable: + assert link + if link.is_file: + self.source_dir = os.path.normpath( + os.path.abspath(link.file_path) + ) + + if link is None and req and req.url: + # PEP 508 URL requirement + link = Link(req.url) + self.link = self.original_link = link + self.original_link_is_in_wheel_cache = False + + # Path to any downloaded or already-existing package. + self.local_file_path = None # type: Optional[str] + if self.link and self.link.is_file: + self.local_file_path = self.link.file_path + + 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 + + # This holds the pkg_resources.Distribution object if this requirement + # is already available: + self.satisfied_by = None # type: Optional[Distribution] + # Whether the installation process should try to uninstall an existing + # distribution before installing this requirement. + self.should_reinstall = False + # Temporary build location + self._temp_build_dir = None # type: Optional[TempDirectory] + # Set to True after successful installation + self.install_succeeded = None # type: Optional[bool] + # Supplied options + self.install_options = install_options if install_options else [] + self.global_options = global_options if global_options else [] + self.hash_options = hash_options if hash_options else {} + # Set to True after successful preparation of this requirement + self.prepared = False + self.is_direct = False + + # Set by the legacy resolver when the requirement has been downloaded + # TODO: This introduces a strong coupling between the resolver and the + # requirement (the coupling was previously between the resolver + # and the requirement set). This should be refactored to allow + # the requirement to decide for itself when it has been + # successfully downloaded - but that is more tricky to get right, + # se we are making the change in stages. + self.successfully_downloaded = 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 {}'.format(redact_auth_from_url(self.link.url)) + elif self.link: + s = redact_auth_from_url(self.link.url) + else: + s = '' + if self.satisfied_by is not None: + s += ' in {}'.format(display_path(self.satisfied_by.location)) + if self.comes_from: + if isinstance(self.comes_from, six.string_types): + comes_from = self.comes_from # type: Optional[str] + else: + comes_from = self.comes_from.from_path() + if comes_from: + s += ' (from {})'.format(comes_from) + return s + + def __repr__(self): + # type: () -> str + return '<{} object: {} editable={!r}>'.format( + self.__class__.__name__, str(self), self.editable) + + def format_debug(self): + # type: () -> str + """An un-tested helper for getting state, for debugging. + """ + attributes = vars(self) + names = sorted(attributes) + + state = ( + "{}={!r}".format(attr, attributes[attr]) for attr in sorted(names) + ) + return '<{name} object: {{{state}}}>'.format( + name=self.__class__.__name__, + state=", ".join(state), + ) + + # Things that are valid for all kinds of requirements? + @property + def name(self): + # type: () -> Optional[str] + if self.req is None: + return None + return six.ensure_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.hash_options) + + 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.hash_options.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 ensure_build_location(self, build_dir, autodelete): + # type: (str, bool) -> str + assert build_dir is not None + if self._temp_build_dir is not None: + assert self._temp_build_dir.path + return self._temp_build_dir.path + if self.req is None: + # 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 = TempDirectory( + kind=tempdir_kinds.REQ_BUILD, globally_managed=True + ) + + 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) + os.makedirs(build_dir) + actual_build_dir = os.path.join(build_dir, name) + # `None` indicates that we respect the globally-configured deletion + # settings, which is what we actually want when auto-deleting. + delete_arg = None if autodelete else False + return TempDirectory( + path=actual_build_dir, + delete=delete_arg, + kind=tempdir_kinds.REQ_BUILD, + globally_managed=True, + ).path + + def _set_requirement(self): + # type: () -> None + """Set requirement after generating metadata. + """ + assert self.req is None + assert self.metadata is not None + assert self.source_dir is not None + + # Construct a Requirement object from the generated metadata + if isinstance(parse_version(self.metadata["Version"]), Version): + op = "==" + else: + op = "===" + + self.req = Requirement( + "".join([ + self.metadata["Name"], + op, + self.metadata["Version"], + ]) + ) + + def warn_on_mismatching_name(self): + # type: () -> None + metadata_name = canonicalize_name(self.metadata["Name"]) + if canonicalize_name(self.req.name) == metadata_name: + # Everything is fine. + return + + # If we're here, there's a mismatch. Log a warning about it. + 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 check_if_exists(self, use_user_site): + # type: (bool) -> None + """Find an installed distribution that satisfies or conflicts + with this requirement, and set self.satisfied_by or + self.should_reinstall appropriately. + """ + if self.req is None: + return + # 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 + try: + self.satisfied_by = pkg_resources.get_distribution(str(no_marker)) + except pkg_resources.DistributionNotFound: + return + except pkg_resources.VersionConflict: + existing_dist = pkg_resources.get_distribution( + self.req.name + ) + if use_user_site: + if dist_in_usersite(existing_dist): + self.should_reinstall = True + 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 {} in {}".format( + existing_dist.project_name, existing_dist.location) + ) + else: + self.should_reinstall = True + else: + if self.editable and self.satisfied_by: + self.should_reinstall = True + # when installing editables, nothing pre-existing should ever + # satisfy + self.satisfied_by = None + + # Things valid for wheels + @property + def is_wheel(self): + # type: () -> bool + if not self.link: + return False + return self.link.is_wheel + + # Things valid for sdists + @property + def unpacked_source_directory(self): + # type: () -> str + return os.path.join( + self.source_dir, + self.link and self.link.subdirectory_fragment or '') + + @property + def setup_py_path(self): + # type: () -> str + assert self.source_dir, "No source dir for {}".format(self) + setup_py = os.path.join(self.unpacked_source_directory, '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_path(self): + # type: () -> str + assert self.source_dir, "No source dir for {}".format(self) + return make_pyproject_path(self.unpacked_source_directory) + + 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. + """ + pyproject_toml_data = load_pyproject_toml( + self.use_pep517, + self.pyproject_toml_path, + self.setup_py_path, + str(self) + ) + + if pyproject_toml_data is None: + self.use_pep517 = False + return + + self.use_pep517 = True + requires, backend, check, backend_path = pyproject_toml_data + self.requirements_to_check = check + self.pyproject_requires = requires + self.pep517_backend = Pep517HookCaller( + self.unpacked_source_directory, backend, backend_path=backend_path, + ) + + def _generate_metadata(self): + # type: () -> str + """Invokes metadata generator functions, with the required arguments. + """ + if not self.use_pep517: + assert self.unpacked_source_directory + + return generate_metadata_legacy( + build_env=self.build_env, + setup_py_path=self.setup_py_path, + source_dir=self.unpacked_source_directory, + isolated=self.isolated, + details=self.name or "from {}".format(self.link) + ) + + assert self.pep517_backend is not None + + return generate_metadata( + build_env=self.build_env, + backend=self.pep517_backend, + ) + + 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(): + self.metadata_directory = self._generate_metadata() + + # Act on the newly generated metadata, based on the name and version. + if not self.name: + self._set_requirement() + else: + self.warn_on_mismatching_name() + + self.assert_source_matches_version() + + @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 _get_dist(self.metadata_directory) + + 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, autodelete=False): + # type: (str, bool) -> None + """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.ensure_build_location( + parent_dir, autodelete + ) + + # For editable installations + 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: {self.link.url!r}".format(**locals()) + vc_type, url = self.link.url.split('+', 1) + vcs_backend = vcs.get_backend(vc_type) + if vcs_backend: + if not self.link.is_vcs: + reason = ( + "This form of VCS requirement is being deprecated: {}." + ).format( + self.link.url + ) + replacement = None + if self.link.url.startswith("git+git@"): + replacement = ( + "git+https://git@example.com/..., " + "git+ssh://git@example.com/..., " + "or the insecure git+git://git@example.com/..." + ) + deprecated(reason, replacement, gone_in="21.0", issue=7554) + hidden_url = hide_url(self.link.url) + if obtain: + vcs_backend.obtain(self.source_dir, url=hidden_url) + else: + vcs_backend.export(self.source_dir, url=hidden_url) + else: + assert 0, ( + 'Unexpected version control type (in {}): {}'.format( + self.link, vc_type)) + + # Top-level Actions + def uninstall(self, auto_confirm=False, verbose=False): + # type: (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. + + """ + assert self.req + try: + dist = pkg_resources.get_distribution(self.req.name) + except pkg_resources.DistributionNotFound: + logger.warning("Skipping %s as it is not installed.", self.name) + return None + else: + logger.info('Found existing installation: %s', dist) + + uninstalled_pathset = UninstallPathSet.from_dist(dist) + uninstalled_pathset.remove(auto_confirm, verbose) + return uninstalled_pathset + + def _get_archive_name(self, path, parentdir, rootdir): + # type: (str, str, str) -> str + + def _clean_zip_name(name, prefix): + # type: (str, str) -> str + assert name.startswith(prefix + os.path.sep), ( + "name {name!r} doesn't start with prefix {prefix!r}" + .format(**locals()) + ) + name = name[len(prefix) + 1:] + name = name.replace(os.path.sep, '/') + return name + + path = os.path.join(parentdir, path) + name = _clean_zip_name(path, rootdir) + return self.name + '/' + name + + def archive(self, build_dir): + # type: (str) -> None + """Saves archive to provided build_dir. + + Used for saving downloaded VCS requirements as part of `pip download`. + """ + assert self.source_dir + + create_archive = True + archive_name = '{}-{}.zip'.format(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 {} exists. (i)gnore, (w)ipe, ' + '(b)ackup, (a)bort '.format( + 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 not create_archive: + return + + zip_output = zipfile.ZipFile( + archive_path, 'w', zipfile.ZIP_DEFLATED, allowZip64=True, + ) + with zip_output: + dir = os.path.normcase( + os.path.abspath(self.unpacked_source_directory) + ) + for dirpath, dirnames, filenames in os.walk(dir): + 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_output.writestr(zipdir, '') + for filename in filenames: + file_arcname = self._get_archive_name( + filename, parentdir=dirpath, rootdir=dir, + ) + filename = os.path.join(dirpath, filename) + zip_output.write(filename, file_arcname) + + 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 + scheme = get_scheme( + self.name, + user=use_user_site, + home=home, + root=root, + isolated=self.isolated, + prefix=prefix, + ) + + global_options = global_options if global_options is not None else [] + if self.editable: + install_editable_legacy( + install_options, + global_options, + prefix=prefix, + home=home, + use_user_site=use_user_site, + name=self.name, + setup_py_path=self.setup_py_path, + isolated=self.isolated, + build_env=self.build_env, + unpacked_source_directory=self.unpacked_source_directory, + ) + self.install_succeeded = True + return + + if self.is_wheel: + assert self.local_file_path + direct_url = None + if self.original_link: + direct_url = direct_url_from_link( + self.original_link, + self.source_dir, + self.original_link_is_in_wheel_cache, + ) + install_wheel( + self.name, + self.local_file_path, + scheme=scheme, + req_description=str(self.req), + pycompile=pycompile, + warn_script_location=warn_script_location, + direct_url=direct_url, + ) + self.install_succeeded = True + return + + # TODO: Why don't we do this for editable installs? + + # 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.global_options + install_options = list(install_options) + self.install_options + + try: + success = install_legacy( + install_options=install_options, + global_options=global_options, + root=root, + home=home, + prefix=prefix, + use_user_site=use_user_site, + pycompile=pycompile, + scheme=scheme, + setup_py_path=self.setup_py_path, + isolated=self.isolated, + req_name=self.name, + build_env=self.build_env, + unpacked_source_directory=self.unpacked_source_directory, + req_description=str(self.req), + ) + except LegacyInstallFailure as exc: + self.install_succeeded = False + six.reraise(*exc.parent) + except Exception: + self.install_succeeded = True + raise + + self.install_succeeded = success diff --git a/venv/lib/python3.8/site-packages/pip/_internal/req/req_set.py b/venv/lib/python3.8/site-packages/pip/_internal/req/req_set.py new file mode 100644 index 0000000..f168ce1 --- /dev/null +++ b/venv/lib/python3.8/site-packages/pip/_internal/req/req_set.py @@ -0,0 +1,202 @@ +# The following comment should be removed at some point in the future. +# mypy: strict-optional=False + +from __future__ import absolute_import + +import logging +from collections import OrderedDict + +from pip._vendor.packaging.utils import canonicalize_name + +from pip._internal.exceptions import InstallationError +from pip._internal.models.wheel import Wheel +from pip._internal.utils import compatibility_tags +from pip._internal.utils.typing import MYPY_CHECK_RUNNING + +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, check_supported_wheels=True): + # type: (bool) -> None + """Create a RequirementSet. + """ + + self.requirements = OrderedDict() # type: Dict[str, InstallRequirement] # noqa: E501 + self.check_supported_wheels = check_supported_wheels + + self.unnamed_requirements = [] # type: List[InstallRequirement] + + def __str__(self): + # type: () -> str + requirements = sorted( + (req for req in self.requirements.values() if not req.comes_from), + key=lambda req: canonicalize_name(req.name), + ) + return ' '.join(str(req.req) for req in requirements) + + def __repr__(self): + # type: () -> str + requirements = sorted( + self.requirements.values(), + key=lambda req: canonicalize_name(req.name), + ) + + format_string = '<{classname} object; {count} requirement(s): {reqs}>' + return format_string.format( + classname=self.__class__.__name__, + count=len(requirements), + reqs=', '.join(str(req.req) for req in requirements), + ) + + def add_unnamed_requirement(self, install_req): + # type: (InstallRequirement) -> None + assert not install_req.name + self.unnamed_requirements.append(install_req) + + def add_named_requirement(self, install_req): + # type: (InstallRequirement) -> None + assert install_req.name + + project_name = canonicalize_name(install_req.name) + self.requirements[project_name] = install_req + + 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. + """ + # 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", + install_req.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) + tags = compatibility_tags.get_supported() + if (self.check_supported_wheels and not wheel.supported(tags)): + raise InstallationError( + "{} is not a supported wheel on this platform.".format( + 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 install_req.name: + self.add_unnamed_requirement(install_req) + return [install_req], None + + try: + existing_req = self.get_requirement(install_req.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: {} (already in {}, name={!r})" + .format(install_req, existing_req, install_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.add_named_requirement(install_req) + # We'd want to rescan this requirement 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: + raise InstallationError( + "Could not satisfy constraints for '{}': " + "installation from path or url cannot be " + "constrained to a version".format(install_req.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, name): + # type: (str) -> bool + project_name = canonicalize_name(name) + + return ( + project_name in self.requirements and + not self.requirements[project_name].constraint + ) + + def get_requirement(self, name): + # type: (str) -> InstallRequirement + project_name = canonicalize_name(name) + + if project_name in self.requirements: + return self.requirements[project_name] + + raise KeyError("No project with the name {name!r}".format(**locals())) + + @property + def all_requirements(self): + # type: () -> List[InstallRequirement] + return self.unnamed_requirements + list(self.requirements.values()) diff --git a/venv/lib/python3.8/site-packages/pip/_internal/req/req_tracker.py b/venv/lib/python3.8/site-packages/pip/_internal/req/req_tracker.py new file mode 100644 index 0000000..14adeab --- /dev/null +++ b/venv/lib/python3.8/site-packages/pip/_internal/req/req_tracker.py @@ -0,0 +1,151 @@ +# The following comment should be removed at some point in the future. +# mypy: strict-optional=False + +from __future__ import absolute_import + +import contextlib +import errno +import hashlib +import logging +import os + +from pip._vendor import contextlib2 + +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 Dict, Iterator, Optional, Set, Type, Union + from pip._internal.req.req_install import InstallRequirement + from pip._internal.models.link import Link + +logger = logging.getLogger(__name__) + + +@contextlib.contextmanager +def update_env_context_manager(**changes): + # type: (str) -> Iterator[None] + target = os.environ + + # Save values from the target and change them. + non_existent_marker = object() + saved_values = {} # type: Dict[str, Union[object, str]] + for name, new_value in changes.items(): + try: + saved_values[name] = target[name] + except KeyError: + saved_values[name] = non_existent_marker + target[name] = new_value + + try: + yield + finally: + # Restore original values in the target. + for name, original_value in saved_values.items(): + if original_value is non_existent_marker: + del target[name] + else: + assert isinstance(original_value, str) # for mypy + target[name] = original_value + + +@contextlib.contextmanager +def get_requirement_tracker(): + # type: () -> Iterator[RequirementTracker] + root = os.environ.get('PIP_REQ_TRACKER') + with contextlib2.ExitStack() as ctx: + if root is None: + root = ctx.enter_context( + TempDirectory(kind='req-tracker') + ).path + ctx.enter_context(update_env_context_manager(PIP_REQ_TRACKER=root)) + logger.debug("Initialized build tracking at %s", root) + + with RequirementTracker(root) as tracker: + yield tracker + + +class RequirementTracker(object): + + def __init__(self, root): + # type: (str) -> None + self._root = root + self._entries = set() # type: Set[InstallRequirement] + logger.debug("Created build tracker: %s", self._root) + + def __enter__(self): + # type: () -> RequirementTracker + logger.debug("Entered build tracker: %s", self._root) + 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 + """Add an InstallRequirement to build tracking. + """ + + # Get the file to write information about this requirement. + entry_path = self._entry_path(req.link) + + # Try reading from the file. If it exists and can be read from, a build + # is already in progress, so a LookupError is raised. + try: + with open(entry_path) as fp: + contents = fp.read() + except IOError as e: + # if the error is anything other than "file does not exist", raise. + if e.errno != errno.ENOENT: + raise + else: + message = '{} is already being built: {}'.format( + req.link, contents) + raise LookupError(message) + + # If we're here, req should really not be building already. + assert req not in self._entries + + # Start tracking this requirement. + with open(entry_path, 'w') as fp: + fp.write(str(req)) + self._entries.add(req) + + logger.debug('Added %s to build tracker %r', req, self._root) + + def remove(self, req): + # type: (InstallRequirement) -> None + """Remove an InstallRequirement from build tracking. + """ + + # Delete the created file and the corresponding entries. + os.unlink(self._entry_path(req.link)) + self._entries.remove(req) + + 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) + + logger.debug("Removed build tracker: %r", self._root) + + @contextlib.contextmanager + def track(self, req): + # type: (InstallRequirement) -> Iterator[None] + self.add(req) + yield + self.remove(req) diff --git a/venv/lib/python3.8/site-packages/pip/_internal/req/req_uninstall.py b/venv/lib/python3.8/site-packages/pip/_internal/req/req_uninstall.py new file mode 100644 index 0000000..559061a --- /dev/null +++ b/venv/lib/python3.8/site-packages/pip/_internal/req/req_uninstall.py @@ -0,0 +1,649 @@ +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[..., Iterator[Any]]) -> 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 + except OSError: + save_dir = TempDirectory(kind="uninstall") + 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') + 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. + Handle symlinks as files to avoid modifying the symlink targets. + """ + path_is_dir = os.path.isdir(path) and not os.path.islink(path) + if path_is_dir: + new_path = self._get_directory_stash(path) + else: + new_path = self._get_file_stash(path) + + self._moves.append((path, new_path)) + if (path_is_dir 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: + logger.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) or os.path.islink(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 {} does not match installed location of {} ' + '(at {})'.format( + 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 + 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. + # os.path.splitdrive is used instead of os.path.isabs because isabs + # treats non-absolute paths with drive letter markings like c:foo\bar + # as absolute paths. It also does not recognize UNC paths if they don't + # have more than "\\sever\share". Valid examples: "\\server\share\" or + # "\\server\share\folder". Python 2.7.8+ support UNC in splitdrive. + 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) + + # If the file doesn't exist, log a warning and return + if not os.path.isfile(self.file): + logger.warning( + "Cannot remove entries from nonexistent file {}".format( + self.file) + ) + return + 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/venv/lib/python3.8/site-packages/pip/_internal/resolution/__init__.py b/venv/lib/python3.8/site-packages/pip/_internal/resolution/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/venv/lib/python3.8/site-packages/pip/_internal/resolution/__pycache__/__init__.cpython-38.pyc b/venv/lib/python3.8/site-packages/pip/_internal/resolution/__pycache__/__init__.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..d50de077753cf686a069c347308636091ad062fd GIT binary patch literal 212 zcmWIL<>g`k0u|Y=6cGIwL?8o3AjbiSi&=m~3PUi1CZpd};mRORi zUzS*;pO&AKl3G-(Uy!VCR$fqMVw7iTRFaZnmQ-wEkd|AOYia-n>3RAg`URN_Y1gLFj_u=Ct3lA7uYKd+Ekb^nVAdQA-r-VTaN~qi zK~fr0N`T5ej4ffMc4((g=ulI)MJ;thxAGlPPrcB?UK{4X9a!M@trZeBb_uU>_nL4Q z=CACq$?HeN_kQ9Nh)>Im)ac3~zw0v(&7vxX$6gr1@UB&3{#7N^|0CZWx3?%?V08Uk|5=KDy&A*fu8 zLB{sX7~@l19bC#NsL2)iinET$4ej30@^LrU`)MI6SipCr%%yLajsnpy=VYF1CSj<` z<@glhbEZZ`kxLDnU7P|4bwlI=PW?3s3Ym-}4Z%2)K|dE9qzZ~S*t#gjFE_L8P0jgM zPrcsgrHmS+8o%*Rc|&jpja-E^b*$YkUYDGDuVHp(YdTThB48%U!=>D~tBOOfV!xOV>v z@<$bicU)!7^sGGGV{~%OR9k5ouKxSsAIN;1piHHk_s$^J70!n!mEN1F5^v3$Qiguh Qa_Ay$(xzq6#&4(g2ZI(6n*aa+ literal 0 HcmV?d00001 diff --git a/venv/lib/python3.8/site-packages/pip/_internal/resolution/base.py b/venv/lib/python3.8/site-packages/pip/_internal/resolution/base.py new file mode 100644 index 0000000..2fa118b --- /dev/null +++ b/venv/lib/python3.8/site-packages/pip/_internal/resolution/base.py @@ -0,0 +1,20 @@ +from pip._internal.utils.typing import MYPY_CHECK_RUNNING + +if MYPY_CHECK_RUNNING: + from typing import Callable, List + from pip._internal.req.req_install import InstallRequirement + from pip._internal.req.req_set import RequirementSet + + InstallRequirementProvider = Callable[ + [str, InstallRequirement], InstallRequirement + ] + + +class BaseResolver(object): + def resolve(self, root_reqs, check_supported_wheels): + # type: (List[InstallRequirement], bool) -> RequirementSet + raise NotImplementedError() + + def get_installation_order(self, req_set): + # type: (RequirementSet) -> List[InstallRequirement] + raise NotImplementedError() diff --git a/venv/lib/python3.8/site-packages/pip/_internal/resolution/legacy/__init__.py b/venv/lib/python3.8/site-packages/pip/_internal/resolution/legacy/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/venv/lib/python3.8/site-packages/pip/_internal/resolution/legacy/__pycache__/__init__.cpython-38.pyc b/venv/lib/python3.8/site-packages/pip/_internal/resolution/legacy/__pycache__/__init__.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..a6be3d898fc89e145417068f41d97a8c90ab561e GIT binary patch literal 219 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 literal 0 HcmV?d00001 diff --git a/venv/lib/python3.8/site-packages/pip/_internal/resolution/legacy/resolver.py b/venv/lib/python3.8/site-packages/pip/_internal/resolution/legacy/resolver.py new file mode 100644 index 0000000..cdb44d1 --- /dev/null +++ b/venv/lib/python3.8/site-packages/pip/_internal/resolution/legacy/resolver.py @@ -0,0 +1,459 @@ +"""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) +""" + +# The following comment should be removed at some point in the future. +# mypy: strict-optional=False +# mypy: disallow-untyped-defs=False + +import logging +import sys +from collections import defaultdict +from itertools import chain + +from pip._vendor.packaging import specifiers + +from pip._internal.exceptions import ( + BestVersionAlreadyInstalled, + DistributionNotFound, + HashError, + HashErrors, + UnsupportedPythonVersion, +) +from pip._internal.req.req_set import RequirementSet +from pip._internal.resolution.base import BaseResolver +from pip._internal.utils.compatibility_tags import get_supported +from pip._internal.utils.logging import indent_log +from pip._internal.utils.misc import dist_in_usersite, normalize_version_info +from pip._internal.utils.packaging import ( + check_requires_python, + get_requires_python, +) +from pip._internal.utils.typing import MYPY_CHECK_RUNNING + +if MYPY_CHECK_RUNNING: + from typing import DefaultDict, List, Optional, Set, Tuple + from pip._vendor import pkg_resources + + from pip._internal.cache import WheelCache + from pip._internal.distributions import AbstractDistribution + from pip._internal.index.package_finder import PackageFinder + from pip._internal.operations.prepare import RequirementPreparer + from pip._internal.req.req_install import InstallRequirement + from pip._internal.resolution.base import InstallRequirementProvider + + DiscoveredDependencies = DefaultDict[str, List[InstallRequirement]] + +logger = logging.getLogger(__name__) + + +def _check_dist_requires_python( + dist, # type: pkg_resources.Distribution + version_info, # type: Tuple[int, int, int] + ignore_requires_python=False, # type: bool +): + # type: (...) -> None + """ + Check whether the given Python version is compatible with a distribution's + "Requires-Python" value. + + :param version_info: A 3-tuple of ints representing the Python + major-minor-micro version to check. + :param ignore_requires_python: Whether to ignore the "Requires-Python" + value if the given Python version isn't compatible. + + :raises UnsupportedPythonVersion: When the given Python version isn't + compatible. + """ + requires_python = get_requires_python(dist) + try: + is_compatible = check_requires_python( + requires_python, version_info=version_info, + ) + except specifiers.InvalidSpecifier as exc: + logger.warning( + "Package %r has an invalid Requires-Python: %s", + dist.project_name, exc, + ) + return + + if is_compatible: + return + + version = '.'.join(map(str, version_info)) + if ignore_requires_python: + logger.debug( + 'Ignoring failed Requires-Python check for package %r: ' + '%s not in %r', + dist.project_name, version, requires_python, + ) + return + + raise UnsupportedPythonVersion( + 'Package {!r} requires a different Python: {} not in {!r}'.format( + dist.project_name, version, requires_python, + )) + + +class Resolver(BaseResolver): + """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 + finder, # type: PackageFinder + wheel_cache, # type: Optional[WheelCache] + make_install_req, # type: InstallRequirementProvider + use_user_site, # type: bool + ignore_dependencies, # type: bool + ignore_installed, # type: bool + ignore_requires_python, # type: bool + force_reinstall, # type: bool + upgrade_strategy, # type: str + py_version_info=None, # type: Optional[Tuple[int, ...]] + ): + # type: (...) -> None + super(Resolver, self).__init__() + assert upgrade_strategy in self._allowed_strategies + + if py_version_info is None: + py_version_info = sys.version_info[:3] + else: + py_version_info = normalize_version_info(py_version_info) + + self._py_version_info = py_version_info + + self.preparer = preparer + self.finder = finder + self.wheel_cache = wheel_cache + + self.upgrade_strategy = upgrade_strategy + self.force_reinstall = force_reinstall + 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._make_install_req = make_install_req + + self._discovered_dependencies = \ + defaultdict(list) # type: DiscoveredDependencies + + def resolve(self, root_reqs, check_supported_wheels): + # type: (List[InstallRequirement], bool) -> RequirementSet + """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. + """ + requirement_set = RequirementSet( + check_supported_wheels=check_supported_wheels + ) + for req in root_reqs: + requirement_set.add_requirement(req) + + # Actually prepare the files, and collect any exceptions. Most hash + # exceptions cannot be checked ahead of time, because + # _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 + + return requirement_set + + 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.should_reinstall = True + req.satisfied_by = None + + 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 _populate_link(self, req): + # type: (InstallRequirement) -> None + """Ensure that if a link can be found for this, that it is found. + + Note that req.link may still be None - if the requirement is already + installed and not needed to be upgraded based on the return value of + _is_upgrade_allowed(). + + If preparer.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. + """ + upgrade = self._is_upgrade_allowed(req) + if req.link is None: + req.link = self.finder.find_requirement(req, upgrade) + + if self.wheel_cache is None or self.preparer.require_hashes: + return + cache_entry = self.wheel_cache.get_cache_entry( + link=req.link, + package_name=req.name, + supported_tags=get_supported(), + ) + if cache_entry is not None: + logger.debug('Using cached wheel link: %s', cache_entry.link) + if req.link is req.original_link and cache_entry.persistent: + req.original_link_is_in_wheel_cache = True + req.link = cache_entry.link + + def _get_abstract_dist_for(self, req): + # type: (InstallRequirement) -> AbstractDistribution + """Takes a InstallRequirement and returns a single AbstractDist \ + representing a prepared variant of the same. + """ + if req.editable: + return self.preparer.prepare_editable_requirement(req) + + # 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, skip_reason + ) + + # We eagerly populate the link, since that's our "legacy" behavior. + self._populate_link(req) + abstract_dist = self.preparer.prepare_linked_requirement(req) + + # 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 + + abstract_dist = self._get_abstract_dist_for(req_to_install) + + # Parse and return dependencies + dist = abstract_dist.get_pkg_resources_distribution() + # This will raise UnsupportedPythonVersion if the given Python + # version isn't compatible with the distribution's Requires-Python. + _check_dist_requires_python( + dist, version_info=self._py_version_info, + ignore_requires_python=self.ignore_requires_python, + ) + + more_reqs = [] # type: List[InstallRequirement] + + def add_req(subreq, extras_requested): + sub_install_req = self._make_install_req( + str(subreq), + req_to_install, + ) + 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 + # 'unnamed' requirements can only come from being directly + # provided by the user. + assert req_to_install.is_direct + 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. + req_to_install.successfully_downloaded = True + + 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/venv/lib/python3.8/site-packages/pip/_internal/resolution/resolvelib/__init__.py b/venv/lib/python3.8/site-packages/pip/_internal/resolution/resolvelib/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/venv/lib/python3.8/site-packages/pip/_internal/resolution/resolvelib/__pycache__/__init__.cpython-38.pyc b/venv/lib/python3.8/site-packages/pip/_internal/resolution/resolvelib/__pycache__/__init__.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..c9ce7cbf3bb8382cd64871bcbad3b8c1abfcea69 GIT binary patch literal 223 zcmWIL<>g`k0u|Y=6cGIwL?8o3AjbiSi&=m~3PUi1CZpd};mRORi zUzS*;pO&AKl3G-(Uy!VCR$fqMVw7iTRFaZnmQ-wEkd|AOYia-n>3RAg`URNK3=b&@)n0pZhlH>PO2Tqb)SKl0RWV7JjMV3 literal 0 HcmV?d00001 diff --git a/venv/lib/python3.8/site-packages/pip/_internal/resolution/resolvelib/__pycache__/base.cpython-38.pyc b/venv/lib/python3.8/site-packages/pip/_internal/resolution/resolvelib/__pycache__/base.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..28149345aec075daeca2e304bda1a01ae0c1b056 GIT binary patch literal 2281 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 literal 0 HcmV?d00001 diff --git a/venv/lib/python3.8/site-packages/pip/_internal/resolution/resolvelib/__pycache__/factory.cpython-38.pyc b/venv/lib/python3.8/site-packages/pip/_internal/resolution/resolvelib/__pycache__/factory.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..db3d9507b04d30dd8442f082dc475c4242aa1160 GIT binary patch literal 5313 zcma)ATXWmS6~>JqL{SoTmnzNBsw#kFjAf=-G;+?53iAgNu* zu@ve_e(H>8I@3;{?T0>Onuq*_{*8U@Q~p6Goz(r#0;DKLO$!bV7JI{zmiP)lgX4nWg6W3qbXj)>|Q*b z%=k0OtUsH~`E$vSX;ZZ_Te}1o>42P#Sdb^ zL$;THkFy(Li$yFm)QdQ4K-U$ z-+I!CqehhGT@v0()#CB~gH1E*9o~pGBQ6GNYp!x`MvYW?cTz6G6lU>0>5!UXtn783 zs>1!|S0e(hb+ejL%1mKy0>OpP8NH%5y- zYBg^(2tI?CjhE}VnoT)bJfP&sC?z;lFE*d(4MHpQm#9Az_X7SA#>3BIa$J8ZxKdut63AM9OJws7CTEgwR3 z&8`s`T?^7ot#0v<-tDdp>2w{)Lf3_KBLlLirPnRUkC@4ui1X@ojqz>L9_zFfb9yRC5Ky9e9Yr$V`Vcl zZoRXoN>>>m-)JYDYuc(VR_0pPOV8LppZlO5V7@2~g8J3C4JMJ->PLfWqsraXpjBQc z$EPTh2_}P%CX%4h5BjAb3AcHVO9H`nf=$s*0*Po;m0+_i8XUDb*Qog*demwQs>%XC zpHa;QF{0yWVw4(NTrCDXCzatQQJ@u7dSA|2Ox8T*NFHyBH_$BJ)P06ms3wM&RmoSC zFOxGh_g~gI(b)?gXFQ5eW1hbA#YOs<*R$*;yvb7##;9w$W(hZb7Tpfr|1K5wE5q(+ z5GH#UDM+_=Vig6#CItdBpVTe`Q6-Vamfd%f+v zH=fiRmTIL$8=0kCZK;|qeu8f+hcc)t<~~Kj@Op?7Z=+p|kXR)#Neo z)juV1ox}$uu8|l*#>p0?DTwyuTMz`5l4+S1C{h1jmhG}NTvPggtWNVbY??E_$l~O+ z25$K^M8(D#^rkb{_LORb2UHgT3;ItfO0AoheZ5D|UoA3#w2RTAi`^;oEP*~^| zo|j&LazDEQJ7diDh}h~n&C#ygEN@$4YiA8}jNxq@lulknJ#(HF9vY^Bwrs>#dW4>T z+ZA77#025GYqG*Kn|f*rch_zsA&#eFSJLK%=nx6o78jt58X>~b>0h~9q1@sUm?wmt z(qXkX11|c(rlupi4}tlj?ecoE^brD52@osge3)Ghqf)W z4N~I>FbQJHv6VsNCiT|~`x#2L9+a>N+R4^)9o7DLpNaJ5OcgfUyDjEBn7K6!tXchC z-Qj3matG7M_aO|c4CWcP9O54QO5eGKvgw(nmzHG#N)7=IKj&Yil4q7to7a4|zuXJ# z6Ti@)wAi}{YJIXI;{hm!I15)IWSd569im1PmzCNGMC=K4;NlSQ4D^Nk48A%FC0cmT zfn+gdOL$=nB{4xwcE)EPqwG86jA5gk(NvUY8QME$1eTkX($AkUR#qSk%XMG|><6ru zuI-uowVvAlbB<#XM%+y|Iakg>bdBf4*wzb+BJ4TP{{m`%X_Bgx9>lL9YEGsK;YgK` zwYk(w6{Op+R;Ny05n=y1x=Z>z`FsED534`}j=7IqNiNiDe+30N&XD79nX&DODT+Vj z8e(Y8q2eC8zzHjOh;b4`Uru0OBvH+*p&KImkj>36p*%i6f-i_JO4(4wdCN=Qw{e|8V zhdUjE1w}X#{Es`+J zOhdlZt%$l*WDG~h%6AcJbii#o^C>UoNe72dBvRm4Vx7atU4{wiEIPdL3pgECzsBrY zEEcVo7DXe7$^%jDU+CHR2ge`70O7cA@KwAJ29Tl7A6US!)dT?@S^|fN=g2Y0YgV)H z!m^NM){PyPke=ZLe?&CGUCE^?MN;!dtHH%@F<*xG-%*M3eojWp4T5XK>nzVTd0#yovq|J6?Dwl&`XE%(q z7V#dY7nEtlO%k7wAlec4NjxC&8xmTZ4JsW)fG!bY#W@Hq6mNz9O#h48BH+1|w&N(wrq z`g4Qr`Xs22e`1k7a8@_=ZAwSDC%N@`ke%oRok&#nm3~J+hsUz6ce6{>o+;FCZ+Gaw z6Z&tc-a8MTrp06m^}E54b=t literal 0 HcmV?d00001 diff --git a/venv/lib/python3.8/site-packages/pip/_internal/resolution/resolvelib/__pycache__/provider.cpython-38.pyc b/venv/lib/python3.8/site-packages/pip/_internal/resolution/resolvelib/__pycache__/provider.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..1dd0cca5ee3ef5e78487f2bbd7276371654da18f GIT binary patch literal 1967 zcmZ`(&5zSY6rZu3#CDS1u+W0&B5i>lg0u-*T2!Q}0&QE0s-`QJJ>bjK@l1jjf9;GD zlqfg$)W4#fD*mOp_Qb!?Q{QuN;?2TX^XBV$-fw>Ius zF?5xK;DpnN#MGk{=SF12re{(jO!+6Q&B%&tUM;SB^|;|RV%xK0$8!o@D{|we*MxbE z*P~Y4_S*55w^ghg(RSSNI+Q#m+~&?X;f|nhP45bKk4d-r0W6a))tx6pl}kU!k7POx zxsb58^ZU!Am+bJ@pAUay&rb$}XM?BRnl_&#Gj0Eoa4c_D}7;WC>TQE9&8{Xd0&7&|Y-y4{4 zZ`p8;1`naD4u}O^5JnaTNHBa%-qQg*!)Bu;T78X$<0O@Wagm9H10SJKT~k|1L?hiO z)vkU*>phWQfQA3g`N}*(R;!h|!hAH0Hq{^+V^#eEx~ zd7RI{@Tic;x3V2YXP;fS!bot16z2D?d*SM(Bc^66ua1fB+m_hqfWFAcPKjV--Z0{!6yxT{zuD^$qMD zKvx)*#Hdq?&UdTGHZL1=UCd34SjM!=Se)`ng!2|-XA?hKp2+KPU6D8vgH`UKK$l45 zL893T6s$Y4NVP4PVOJ<@YCE&pxQ*Lm(Q{=8B27ru76x$e#jaMBtbS%VBzp?UAzH;4a}XDwL?0fqphK@glytJ jEfiN#v{4}3PrXswKP)->5lcex(3a6MEV>J_LtEzm%x%~) literal 0 HcmV?d00001 diff --git a/venv/lib/python3.8/site-packages/pip/_internal/resolution/resolvelib/__pycache__/requirements.cpython-38.pyc b/venv/lib/python3.8/site-packages/pip/_internal/resolution/resolvelib/__pycache__/requirements.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..6f10e1032908050273c0cab48992bec30901a098 GIT binary patch literal 4377 zcma)9TXP#l7M`m#y7&?&WC_8bYyyiYEU{rp5sKP1WSAGYDT31i61b#e;!@!Ti8TVp;%rUw4ukXJ8&fEIugRQT;N8bzv z4+mfVuG4-aZ9U@q2ch6&9w%vQI1yt%$&WePSmSX(pJ;DSc@PdmF1C3>pL`h0#E+uV zam+Th{FsHzPv}Vf3qMFE;wT`}%EiQB6IFhKhHI|QG}mA{GoER#N#ew>#cgJ@+B5A` zcWbQ99L&{OgEjH%uohTur&k~RFpa_>OiDxq2ELgW>Qmi9m89xp{aEv$y>0EeK6nm# zA+3zw=SFHt9t{QTKzpub4#%_c#~-S)6ZaqYr{Q$tAfEcc9%mch?{Xe(?DEuu!;z{~|A#7)e-g_=p!n!R2nQ9AwVTX*tX#WGz~aa-CRhB5QtV8JezcZzF` zS*PmS^5$1t^+?-;3KiQK0jop>S#e3`E1~qHpM(;A%)BQ@@0Dn{T}8u%{ex~pypA(z z)APm?b`U|wY1{Mm5BzAZaWXFP2JLaCVlw3-IZ{#g#HD^Sr9$ABEqoI+cj-xx4 zQ#bUc?O19hz~s4NVzh}Wsab>|8Gg7npW_FRq=X=>4J2u>CD{FPdaXdWGL+CQd7*v* zB_CjL3=d-3o+H;1o$06gkGcXE;}{<0nOL<(HhMOJP1hLA-hR9r$`1bhP87y_A9UhL zg1*dQwOdOqT1b~aml9$FAUqIaB2wD}JnzF_h8diuCJk%+heY_YYbxh*Ew!gU-%=8> zk}fHwhdOt&HMm(#pRHFhy%;|V?ny<-96e5RtM8hrDH9>cti`+3RCz$%^RUVDJ|<#v z6k9-s+IY$D3KSz7J43O=gy%!zr-&jsmaU)1vJ#|bXeM66C(>4FzFQO5iFAS_W$2ji|C9(*{M2(ui zpaJX=;HJw(Dxl!_qEXPHO;kxD)y!pmcB4`gm*9^sKc@x)-Cv1D6-<6JGMdj!!xr!0 zi2GWIkmn622LQn$u*i%JqMPhrCOP#+m|UcoAmj_wjACO=pWUjYcp-9xalk02Ff+m`6L>c~Nu(ykDV83JaRCtXsOJS$`ORShc1;yHhE~OW@zke1Za|c$*pp z_U}`V%vN-$QMUMUu)l?U8>o^ZCBVL3z`ZSKEkHe2NN<~{@-sANaF1jW+Mr~SSS zK|R1;C5=cKSwkg@Dw_t>w*kaU>5WHOY9+ruN_Hpld_?K*?EdHH^hB=8B^JVXr<0%B z$c}Z!Jn@m|`n_fcB_Im(LVfa)EmS4%EbCN(0jcg;a`IPfKqxJIAy=cCw^g2SQI8~X zm%gi<)&PqCIj8IrKZ~Kf@Lme$7eTZPp%>D@!UjtKDu~*yT?w4DR22u5xp>6JS>ETo zcsVN-z)6|kOS8XwbiXplieRJ`hC~D@OEYJ0EtcdZ45Vud9h31Bii{Kn?jeZKh!Vn` zLffWL#C(LUN@A5_A-CB>m4xNwrH(#(cQI89agHb}bWBO!9Myn!K)UM*9$RPVhBdp4 zYS#t;uDO*LrukU(@fk!OB~8W3ZA@gW+@+r4tRm@DIp02Z%uV*XHR~>B>6Mt1B86a+ z0FQ?v)BL}2hq8`uiINO=EW@!?nrS&TL2HBV)$}G_XFczb$7~{c^nUYq;Ql>4&?2eA zZu*AWk-y)vm0of*#Wq2%pIT1<=7QpkipgnhzF5bM+o}G3;aO6UOStRjFA;pdhl)3# zdUT5heViV&g0w4I)KFTc;8XEZ0*)ffPMy!Pto`p~zLHX%w$V0L@$RBu{ON6T_2%mP F{{wEk&XNEC literal 0 HcmV?d00001 diff --git a/venv/lib/python3.8/site-packages/pip/_internal/resolution/resolvelib/__pycache__/resolver.cpython-38.pyc b/venv/lib/python3.8/site-packages/pip/_internal/resolution/resolvelib/__pycache__/resolver.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..48038634d4f207f1a0619cb3bc946db618cafd02 GIT binary patch literal 5549 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 RwTM!GQA?p(X#S2B{tMm2`{4ip literal 0 HcmV?d00001 diff --git a/venv/lib/python3.8/site-packages/pip/_internal/resolution/resolvelib/base.py b/venv/lib/python3.8/site-packages/pip/_internal/resolution/resolvelib/base.py new file mode 100644 index 0000000..5f99618 --- /dev/null +++ b/venv/lib/python3.8/site-packages/pip/_internal/resolution/resolvelib/base.py @@ -0,0 +1,52 @@ +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, Sequence, Set + + from pip._internal.req.req_install import InstallRequirement + from pip._vendor.packaging.version import _BaseVersion + + +def format_name(project, extras): + # type: (str, Set[str]) -> str + if not extras: + return project + canonical_extras = sorted(canonicalize_name(e) for e in extras) + return "{}[{}]".format(project, ",".join(canonical_extras)) + + +class Requirement(object): + @property + def name(self): + # type: () -> str + raise NotImplementedError("Subclass should override") + + def find_matches(self): + # type: () -> Sequence[Candidate] + raise NotImplementedError("Subclass should override") + + def is_satisfied_by(self, candidate): + # type: (Candidate) -> bool + return False + + +class Candidate(object): + @property + def name(self): + # type: () -> str + raise NotImplementedError("Override in subclass") + + @property + def version(self): + # type: () -> _BaseVersion + raise NotImplementedError("Override in subclass") + + def get_dependencies(self): + # type: () -> Sequence[Requirement] + raise NotImplementedError("Override in subclass") + + def get_install_requirement(self): + # type: () -> Optional[InstallRequirement] + raise NotImplementedError("Override in subclass") diff --git a/venv/lib/python3.8/site-packages/pip/_internal/resolution/resolvelib/candidates.py b/venv/lib/python3.8/site-packages/pip/_internal/resolution/resolvelib/candidates.py new file mode 100644 index 0000000..f8461ad --- /dev/null +++ b/venv/lib/python3.8/site-packages/pip/_internal/resolution/resolvelib/candidates.py @@ -0,0 +1,450 @@ +import logging +import sys + +from pip._vendor.packaging.specifiers import InvalidSpecifier, SpecifierSet +from pip._vendor.packaging.utils import canonicalize_name +from pip._vendor.packaging.version import Version + +from pip._internal.req.constructors import ( + install_req_from_editable, + install_req_from_line, +) +from pip._internal.req.req_install import InstallRequirement +from pip._internal.utils.misc import normalize_version_info +from pip._internal.utils.packaging import get_requires_python +from pip._internal.utils.typing import MYPY_CHECK_RUNNING + +from .base import Candidate, format_name + +if MYPY_CHECK_RUNNING: + from typing import Any, Optional, Sequence, Set, Tuple, Union + + from pip._vendor.packaging.version import _BaseVersion + from pip._vendor.pkg_resources import Distribution + + from pip._internal.distributions import AbstractDistribution + from pip._internal.models.link import Link + + from .base import Requirement + from .factory import Factory + + BaseCandidate = Union[ + "AlreadyInstalledCandidate", + "EditableCandidate", + "LinkCandidate", + ] + + +logger = logging.getLogger(__name__) + + +def make_install_req_from_link(link, parent): + # type: (Link, InstallRequirement) -> InstallRequirement + assert not parent.editable, "parent is editable" + return install_req_from_line( + link.url, + comes_from=parent.comes_from, + use_pep517=parent.use_pep517, + isolated=parent.isolated, + constraint=parent.constraint, + options=dict( + install_options=parent.install_options, + global_options=parent.global_options, + hashes=parent.hash_options + ), + ) + + +def make_install_req_from_editable(link, parent): + # type: (Link, InstallRequirement) -> InstallRequirement + assert parent.editable, "parent not editable" + return install_req_from_editable( + link.url, + comes_from=parent.comes_from, + use_pep517=parent.use_pep517, + isolated=parent.isolated, + constraint=parent.constraint, + options=dict( + install_options=parent.install_options, + global_options=parent.global_options, + hashes=parent.hash_options + ), + ) + + +def make_install_req_from_dist(dist, parent): + # type: (Distribution, InstallRequirement) -> InstallRequirement + ireq = install_req_from_line( + "{}=={}".format( + canonicalize_name(dist.project_name), + dist.parsed_version, + ), + comes_from=parent.comes_from, + use_pep517=parent.use_pep517, + isolated=parent.isolated, + constraint=parent.constraint, + options=dict( + install_options=parent.install_options, + global_options=parent.global_options, + hashes=parent.hash_options + ), + ) + ireq.satisfied_by = dist + return ireq + + +class _InstallRequirementBackedCandidate(Candidate): + def __init__( + self, + link, # type: Link + ireq, # type: InstallRequirement + factory, # type: Factory + name=None, # type: Optional[str] + version=None, # type: Optional[_BaseVersion] + ): + # type: (...) -> None + self.link = link + self._factory = factory + self._ireq = ireq + self._name = name + self._version = version + self._dist = None # type: Optional[Distribution] + + def __repr__(self): + # type: () -> str + return "{class_name}({link!r})".format( + class_name=self.__class__.__name__, + link=str(self.link), + ) + + def __eq__(self, other): + # type: (Any) -> bool + if isinstance(other, self.__class__): + return self.link == other.link + return False + + # Needed for Python 2, which does not implement this by default + def __ne__(self, other): + # type: (Any) -> bool + return not self.__eq__(other) + + @property + def name(self): + # type: () -> str + """The normalised name of the project the candidate refers to""" + if self._name is None: + self._name = canonicalize_name(self.dist.project_name) + return self._name + + @property + def version(self): + # type: () -> _BaseVersion + if self._version is None: + self._version = self.dist.parsed_version + return self._version + + def _prepare_abstract_distribution(self): + # type: () -> AbstractDistribution + raise NotImplementedError("Override in subclass") + + def _prepare(self): + # type: () -> None + if self._dist is not None: + return + + abstract_dist = self._prepare_abstract_distribution() + self._dist = abstract_dist.get_pkg_resources_distribution() + assert self._dist is not None, "Distribution already installed" + + # TODO: Abort cleanly here, as the resolution has been + # based on the wrong name/version until now, and + # so is wrong. + # TODO: (Longer term) Rather than abort, reject this candidate + # and backtrack. This would need resolvelib support. + # These should be "proper" errors, not just asserts, as they + # can result from user errors like a requirement "foo @ URL" + # when the project at URL has a name of "bar" in its metadata. + assert ( + self._name is None or + self._name == canonicalize_name(self._dist.project_name) + ), "Name mismatch: {!r} vs {!r}".format( + self._name, canonicalize_name(self._dist.project_name), + ) + assert ( + self._version is None or + self._version == self._dist.parsed_version + ), "Version mismatch: {!r} vs {!r}".format( + self._version, self._dist.parsed_version, + ) + + @property + def dist(self): + # type: () -> Distribution + self._prepare() + return self._dist + + def _get_requires_python_specifier(self): + # type: () -> Optional[SpecifierSet] + requires_python = get_requires_python(self.dist) + if requires_python is None: + return None + try: + spec = SpecifierSet(requires_python) + except InvalidSpecifier as e: + logger.warning( + "Package %r has an invalid Requires-Python: %s", self.name, e, + ) + return None + return spec + + def get_dependencies(self): + # type: () -> Sequence[Requirement] + deps = [ + self._factory.make_requirement_from_spec(str(r), self._ireq) + for r in self.dist.requires() + ] + python_dep = self._factory.make_requires_python_requirement( + self._get_requires_python_specifier(), + ) + if python_dep: + deps.append(python_dep) + return deps + + def get_install_requirement(self): + # type: () -> Optional[InstallRequirement] + self._prepare() + return self._ireq + + +class LinkCandidate(_InstallRequirementBackedCandidate): + def __init__( + self, + link, # type: Link + parent, # type: InstallRequirement + factory, # type: Factory + name=None, # type: Optional[str] + version=None, # type: Optional[_BaseVersion] + ): + # type: (...) -> None + super(LinkCandidate, self).__init__( + link=link, + ireq=make_install_req_from_link(link, parent), + factory=factory, + name=name, + version=version, + ) + + def _prepare_abstract_distribution(self): + # type: () -> AbstractDistribution + return self._factory.preparer.prepare_linked_requirement(self._ireq) + + +class EditableCandidate(_InstallRequirementBackedCandidate): + def __init__( + self, + link, # type: Link + parent, # type: InstallRequirement + factory, # type: Factory + name=None, # type: Optional[str] + version=None, # type: Optional[_BaseVersion] + ): + # type: (...) -> None + super(EditableCandidate, self).__init__( + link=link, + ireq=make_install_req_from_editable(link, parent), + factory=factory, + name=name, + version=version, + ) + + def _prepare_abstract_distribution(self): + # type: () -> AbstractDistribution + return self._factory.preparer.prepare_editable_requirement(self._ireq) + + +class AlreadyInstalledCandidate(Candidate): + def __init__( + self, + dist, # type: Distribution + parent, # type: InstallRequirement + factory, # type: Factory + ): + # type: (...) -> None + self.dist = dist + self._ireq = make_install_req_from_dist(dist, parent) + self._factory = factory + + # This is just logging some messages, so we can do it eagerly. + # The returned dist would be exactly the same as self.dist because we + # set satisfied_by in make_install_req_from_dist. + # TODO: Supply reason based on force_reinstall and upgrade_strategy. + skip_reason = "already satisfied" + factory.preparer.prepare_installed_requirement(self._ireq, skip_reason) + + def __repr__(self): + # type: () -> str + return "{class_name}({distribution!r})".format( + class_name=self.__class__.__name__, + distribution=self.dist, + ) + + def __eq__(self, other): + # type: (Any) -> bool + if isinstance(other, self.__class__): + return self.name == other.name and self.version == other.version + return False + + # Needed for Python 2, which does not implement this by default + def __ne__(self, other): + # type: (Any) -> bool + return not self.__eq__(other) + + @property + def name(self): + # type: () -> str + return canonicalize_name(self.dist.project_name) + + @property + def version(self): + # type: () -> _BaseVersion + return self.dist.parsed_version + + def get_dependencies(self): + # type: () -> Sequence[Requirement] + return [ + self._factory.make_requirement_from_spec(str(r), self._ireq) + for r in self.dist.requires() + ] + + def get_install_requirement(self): + # type: () -> Optional[InstallRequirement] + return None + + +class ExtrasCandidate(Candidate): + """A candidate that has 'extras', indicating additional dependencies. + + Requirements can be for a project with dependencies, something like + foo[extra]. The extras don't affect the project/version being installed + directly, but indicate that we need additional dependencies. We model that + by having an artificial ExtrasCandidate that wraps the "base" candidate. + + The ExtrasCandidate differs from the base in the following ways: + + 1. It has a unique name, of the form foo[extra]. This causes the resolver + to treat it as a separate node in the dependency graph. + 2. When we're getting the candidate's dependencies, + a) We specify that we want the extra dependencies as well. + b) We add a dependency on the base candidate (matching the name and + version). See below for why this is needed. + 3. We return None for the underlying InstallRequirement, as the base + candidate will provide it, and we don't want to end up with duplicates. + + The dependency on the base candidate is needed so that the resolver can't + decide that it should recommend foo[extra1] version 1.0 and foo[extra2] + version 2.0. Having those candidates depend on foo=1.0 and foo=2.0 + respectively forces the resolver to recognise that this is a conflict. + """ + def __init__( + self, + base, # type: BaseCandidate + extras, # type: Set[str] + ): + # type: (...) -> None + self.base = base + self.extras = extras + + def __repr__(self): + # type: () -> str + return "{class_name}(base={base!r}, extras={extras!r})".format( + class_name=self.__class__.__name__, + base=self.base, + extras=self.extras, + ) + + def __eq__(self, other): + # type: (Any) -> bool + if isinstance(other, self.__class__): + return self.base == other.base and self.extras == other.extras + return False + + # Needed for Python 2, which does not implement this by default + def __ne__(self, other): + # type: (Any) -> bool + return not self.__eq__(other) + + @property + def name(self): + # type: () -> str + """The normalised name of the project the candidate refers to""" + return format_name(self.base.name, self.extras) + + @property + def version(self): + # type: () -> _BaseVersion + return self.base.version + + def get_dependencies(self): + # type: () -> Sequence[Requirement] + factory = self.base._factory + + # The user may have specified extras that the candidate doesn't + # support. We ignore any unsupported extras here. + valid_extras = self.extras.intersection(self.base.dist.extras) + invalid_extras = self.extras.difference(self.base.dist.extras) + if invalid_extras: + logger.warning( + "Invalid extras specified in %s: %s", + self.name, + ','.join(sorted(invalid_extras)) + ) + + deps = [ + factory.make_requirement_from_spec(str(r), self.base._ireq) + for r in self.base.dist.requires(valid_extras) + ] + # Add a dependency on the exact base. + # (See note 2b in the class docstring) + spec = "{}=={}".format(self.base.name, self.base.version) + deps.append(factory.make_requirement_from_spec(spec, self.base._ireq)) + return deps + + def get_install_requirement(self): + # type: () -> Optional[InstallRequirement] + # We don't return anything here, because we always + # depend on the base candidate, and we'll get the + # install requirement from that. + return None + + +class RequiresPythonCandidate(Candidate): + def __init__(self, py_version_info): + # type: (Optional[Tuple[int, ...]]) -> None + if py_version_info is not None: + version_info = normalize_version_info(py_version_info) + else: + version_info = sys.version_info[:3] + self._version = Version(".".join(str(c) for c in version_info)) + + # We don't need to implement __eq__() and __ne__() since there is always + # only one RequiresPythonCandidate in a resolution, i.e. the host Python. + # The built-in object.__eq__() and object.__ne__() do exactly what we want. + + @property + def name(self): + # type: () -> str + # Avoid conflicting with the PyPI package "Python". + return "" + + @property + def version(self): + # type: () -> _BaseVersion + return self._version + + def get_dependencies(self): + # type: () -> Sequence[Requirement] + return [] + + def get_install_requirement(self): + # type: () -> Optional[InstallRequirement] + return None diff --git a/venv/lib/python3.8/site-packages/pip/_internal/resolution/resolvelib/factory.py b/venv/lib/python3.8/site-packages/pip/_internal/resolution/resolvelib/factory.py new file mode 100644 index 0000000..23686f7 --- /dev/null +++ b/venv/lib/python3.8/site-packages/pip/_internal/resolution/resolvelib/factory.py @@ -0,0 +1,201 @@ +from pip._vendor.packaging.utils import canonicalize_name + +from pip._internal.exceptions import ( + InstallationError, + UnsupportedPythonVersion, +) +from pip._internal.utils.misc import get_installed_distributions +from pip._internal.utils.typing import MYPY_CHECK_RUNNING + +from .candidates import ( + AlreadyInstalledCandidate, + EditableCandidate, + ExtrasCandidate, + LinkCandidate, + RequiresPythonCandidate, +) +from .requirements import ( + ExplicitRequirement, + RequiresPythonRequirement, + SpecifierRequirement, +) + +if MYPY_CHECK_RUNNING: + from typing import Dict, Iterator, Optional, Set, Tuple, TypeVar + + from pip._vendor.packaging.specifiers import SpecifierSet + from pip._vendor.packaging.version import _BaseVersion + from pip._vendor.pkg_resources import Distribution + from pip._vendor.resolvelib import ResolutionImpossible + + from pip._internal.index.package_finder import PackageFinder + from pip._internal.models.link import Link + from pip._internal.operations.prepare import RequirementPreparer + from pip._internal.req.req_install import InstallRequirement + from pip._internal.resolution.base import InstallRequirementProvider + + from .base import Candidate, Requirement + from .candidates import BaseCandidate + + C = TypeVar("C") + Cache = Dict[Link, C] + + +class Factory(object): + def __init__( + self, + finder, # type: PackageFinder + preparer, # type: RequirementPreparer + make_install_req, # type: InstallRequirementProvider + force_reinstall, # type: bool + ignore_installed, # type: bool + ignore_requires_python, # type: bool + py_version_info=None, # type: Optional[Tuple[int, ...]] + ): + # type: (...) -> None + self.finder = finder + self.preparer = preparer + self._python_candidate = RequiresPythonCandidate(py_version_info) + self._make_install_req_from_spec = make_install_req + self._force_reinstall = force_reinstall + self._ignore_requires_python = ignore_requires_python + + self._link_candidate_cache = {} # type: Cache[LinkCandidate] + self._editable_candidate_cache = {} # type: Cache[EditableCandidate] + + if not ignore_installed: + self._installed_dists = { + canonicalize_name(dist.project_name): dist + for dist in get_installed_distributions() + } + else: + self._installed_dists = {} + + def _make_candidate_from_dist( + self, + dist, # type: Distribution + extras, # type: Set[str] + parent, # type: InstallRequirement + ): + # type: (...) -> Candidate + base = AlreadyInstalledCandidate(dist, parent, factory=self) + if extras: + return ExtrasCandidate(base, extras) + return base + + def _make_candidate_from_link( + self, + link, # type: Link + extras, # type: Set[str] + parent, # type: InstallRequirement + name=None, # type: Optional[str] + version=None, # type: Optional[_BaseVersion] + ): + # type: (...) -> Candidate + # TODO: Check already installed candidate, and use it if the link and + # editable flag match. + if parent.editable: + if link not in self._editable_candidate_cache: + self._editable_candidate_cache[link] = EditableCandidate( + link, parent, factory=self, name=name, version=version, + ) + base = self._editable_candidate_cache[link] # type: BaseCandidate + else: + if link not in self._link_candidate_cache: + self._link_candidate_cache[link] = LinkCandidate( + link, parent, factory=self, name=name, version=version, + ) + base = self._link_candidate_cache[link] + if extras: + return ExtrasCandidate(base, extras) + return base + + def iter_found_candidates(self, ireq, extras): + # type: (InstallRequirement, Set[str]) -> Iterator[Candidate] + name = canonicalize_name(ireq.req.name) + if not self._force_reinstall: + installed_dist = self._installed_dists.get(name) + else: + installed_dist = None + + found = self.finder.find_best_candidate( + project_name=ireq.req.name, + specifier=ireq.req.specifier, + hashes=ireq.hashes(trust_internet=False), + ) + for ican in found.iter_applicable(): + if (installed_dist is not None and + installed_dist.parsed_version == ican.version): + continue + yield self._make_candidate_from_link( + link=ican.link, + extras=extras, + parent=ireq, + name=name, + version=ican.version, + ) + + # Return installed distribution if it matches the specifier. This is + # done last so the resolver will prefer it over downloading links. + if (installed_dist is not None and + installed_dist.parsed_version in ireq.req.specifier): + yield self._make_candidate_from_dist( + dist=installed_dist, + extras=extras, + parent=ireq, + ) + + def make_requirement_from_install_req(self, ireq): + # type: (InstallRequirement) -> Requirement + if ireq.link: + # TODO: Get name and version from ireq, if possible? + # Specifically, this might be needed in "name @ URL" + # syntax - need to check where that syntax is handled. + cand = self._make_candidate_from_link( + ireq.link, extras=set(), parent=ireq, + ) + return ExplicitRequirement(cand) + return SpecifierRequirement(ireq, factory=self) + + def make_requirement_from_spec(self, specifier, comes_from): + # type: (str, InstallRequirement) -> Requirement + ireq = self._make_install_req_from_spec(specifier, comes_from) + return self.make_requirement_from_install_req(ireq) + + def make_requires_python_requirement(self, specifier): + # type: (Optional[SpecifierSet]) -> Optional[Requirement] + if self._ignore_requires_python or specifier is None: + return None + return RequiresPythonRequirement(specifier, self._python_candidate) + + def should_reinstall(self, candidate): + # type: (Candidate) -> bool + # TODO: Are there more cases this needs to return True? Editable? + return candidate.name in self._installed_dists + + def _report_requires_python_error( + self, + requirement, # type: RequiresPythonRequirement + parent, # type: Candidate + ): + # type: (...) -> UnsupportedPythonVersion + template = ( + "Package {package!r} requires a different Python: " + "{version} not in {specifier!r}" + ) + message = template.format( + package=parent.name, + version=self._python_candidate.version, + specifier=str(requirement.specifier), + ) + return UnsupportedPythonVersion(message) + + def get_installation_error(self, e): + # type: (ResolutionImpossible) -> Optional[InstallationError] + for cause in e.causes: + if isinstance(cause.requirement, RequiresPythonRequirement): + return self._report_requires_python_error( + cause.requirement, + cause.parent, + ) + return None diff --git a/venv/lib/python3.8/site-packages/pip/_internal/resolution/resolvelib/provider.py b/venv/lib/python3.8/site-packages/pip/_internal/resolution/resolvelib/provider.py new file mode 100644 index 0000000..5c3d210 --- /dev/null +++ b/venv/lib/python3.8/site-packages/pip/_internal/resolution/resolvelib/provider.py @@ -0,0 +1,54 @@ +from pip._vendor.resolvelib.providers import AbstractProvider + +from pip._internal.utils.typing import MYPY_CHECK_RUNNING + +if MYPY_CHECK_RUNNING: + from typing import Any, Optional, Sequence, Tuple, Union + + from pip._internal.req.req_install import InstallRequirement + + from .base import Requirement, Candidate + from .factory import Factory + + +class PipProvider(AbstractProvider): + def __init__( + self, + factory, # type: Factory + ignore_dependencies, # type: bool + ): + # type: (...) -> None + self._factory = factory + self._ignore_dependencies = ignore_dependencies + + def get_install_requirement(self, c): + # type: (Candidate) -> Optional[InstallRequirement] + return c.get_install_requirement() + + def identify(self, dependency): + # type: (Union[Requirement, Candidate]) -> str + return dependency.name + + def get_preference( + self, + resolution, # type: Optional[Candidate] + candidates, # type: Sequence[Candidate] + information # type: Sequence[Tuple[Requirement, Candidate]] + ): + # type: (...) -> Any + # Use the "usual" value for now + return len(candidates) + + def find_matches(self, requirement): + # type: (Requirement) -> Sequence[Candidate] + return requirement.find_matches() + + def is_satisfied_by(self, requirement, candidate): + # type: (Requirement, Candidate) -> bool + return requirement.is_satisfied_by(candidate) + + def get_dependencies(self, candidate): + # type: (Candidate) -> Sequence[Requirement] + if self._ignore_dependencies: + return [] + return candidate.get_dependencies() diff --git a/venv/lib/python3.8/site-packages/pip/_internal/resolution/resolvelib/requirements.py b/venv/lib/python3.8/site-packages/pip/_internal/resolution/resolvelib/requirements.py new file mode 100644 index 0000000..d2e4479 --- /dev/null +++ b/venv/lib/python3.8/site-packages/pip/_internal/resolution/resolvelib/requirements.py @@ -0,0 +1,119 @@ +from pip._vendor.packaging.utils import canonicalize_name + +from pip._internal.utils.typing import MYPY_CHECK_RUNNING + +from .base import Requirement, format_name + +if MYPY_CHECK_RUNNING: + from typing import Sequence + + from pip._vendor.packaging.specifiers import SpecifierSet + + from pip._internal.req.req_install import InstallRequirement + + from .base import Candidate + from .factory import Factory + + +class ExplicitRequirement(Requirement): + def __init__(self, candidate): + # type: (Candidate) -> None + self.candidate = candidate + + def __repr__(self): + # type: () -> str + return "{class_name}({candidate!r})".format( + class_name=self.__class__.__name__, + candidate=self.candidate, + ) + + @property + def name(self): + # type: () -> str + # No need to canonicalise - the candidate did this + return self.candidate.name + + def find_matches(self): + # type: () -> Sequence[Candidate] + return [self.candidate] + + def is_satisfied_by(self, candidate): + # type: (Candidate) -> bool + return candidate == self.candidate + + +class SpecifierRequirement(Requirement): + def __init__(self, ireq, factory): + # type: (InstallRequirement, Factory) -> None + assert ireq.link is None, "This is a link, not a specifier" + self._ireq = ireq + self._factory = factory + self.extras = ireq.req.extras + + def __str__(self): + # type: () -> str + return str(self._ireq.req) + + def __repr__(self): + # type: () -> str + return "{class_name}({requirement!r})".format( + class_name=self.__class__.__name__, + requirement=str(self._ireq.req), + ) + + @property + def name(self): + # type: () -> str + canonical_name = canonicalize_name(self._ireq.req.name) + return format_name(canonical_name, self.extras) + + def find_matches(self): + # type: () -> Sequence[Candidate] + it = self._factory.iter_found_candidates(self._ireq, self.extras) + return list(it) + + def is_satisfied_by(self, candidate): + # type: (Candidate) -> bool + assert candidate.name == self.name, \ + "Internal issue: Candidate is not for this requirement " \ + " {} vs {}".format(candidate.name, self.name) + # We can safely always allow prereleases here since PackageFinder + # already implements the prerelease logic, and would have filtered out + # prerelease candidates if the user does not expect them. + spec = self._ireq.req.specifier + return spec.contains(candidate.version, prereleases=True) + + +class RequiresPythonRequirement(Requirement): + """A requirement representing Requires-Python metadata. + """ + def __init__(self, specifier, match): + # type: (SpecifierSet, Candidate) -> None + self.specifier = specifier + self._candidate = match + + def __repr__(self): + # type: () -> str + return "{class_name}({specifier!r})".format( + class_name=self.__class__.__name__, + specifier=str(self.specifier), + ) + + @property + def name(self): + # type: () -> str + return self._candidate.name + + def find_matches(self): + # type: () -> Sequence[Candidate] + if self._candidate.version in self.specifier: + return [self._candidate] + return [] + + def is_satisfied_by(self, candidate): + # type: (Candidate) -> bool + assert candidate.name == self._candidate.name, "Not Python candidate" + # We can safely always allow prereleases here since PackageFinder + # already implements the prerelease logic, and would have filtered out + # prerelease candidates if the user does not expect them. + return self.specifier.contains(candidate.version, prereleases=True) diff --git a/venv/lib/python3.8/site-packages/pip/_internal/resolution/resolvelib/resolver.py b/venv/lib/python3.8/site-packages/pip/_internal/resolution/resolvelib/resolver.py new file mode 100644 index 0000000..cba5a49 --- /dev/null +++ b/venv/lib/python3.8/site-packages/pip/_internal/resolution/resolvelib/resolver.py @@ -0,0 +1,174 @@ +import functools +import logging + +from pip._vendor import six +from pip._vendor.packaging.utils import canonicalize_name +from pip._vendor.resolvelib import BaseReporter, ResolutionImpossible +from pip._vendor.resolvelib import Resolver as RLResolver + +from pip._internal.exceptions import InstallationError +from pip._internal.req.req_set import RequirementSet +from pip._internal.resolution.base import BaseResolver +from pip._internal.resolution.resolvelib.provider import PipProvider +from pip._internal.utils.typing import MYPY_CHECK_RUNNING + +from .factory import Factory + +if MYPY_CHECK_RUNNING: + from typing import Dict, List, Optional, Tuple + + from pip._vendor.resolvelib.resolvers import Result + + from pip._internal.cache import WheelCache + from pip._internal.index.package_finder import PackageFinder + from pip._internal.operations.prepare import RequirementPreparer + from pip._internal.req.req_install import InstallRequirement + from pip._internal.resolution.base import InstallRequirementProvider + + +logger = logging.getLogger(__name__) + + +class Resolver(BaseResolver): + def __init__( + self, + preparer, # type: RequirementPreparer + finder, # type: PackageFinder + wheel_cache, # type: Optional[WheelCache] + make_install_req, # type: InstallRequirementProvider + use_user_site, # type: bool + ignore_dependencies, # type: bool + ignore_installed, # type: bool + ignore_requires_python, # type: bool + force_reinstall, # type: bool + upgrade_strategy, # type: str + py_version_info=None, # type: Optional[Tuple[int, ...]] + ): + super(Resolver, self).__init__() + self.factory = Factory( + finder=finder, + preparer=preparer, + make_install_req=make_install_req, + force_reinstall=force_reinstall, + ignore_installed=ignore_installed, + ignore_requires_python=ignore_requires_python, + py_version_info=py_version_info, + ) + self.ignore_dependencies = ignore_dependencies + self._result = None # type: Optional[Result] + + def resolve(self, root_reqs, check_supported_wheels): + # type: (List[InstallRequirement], bool) -> RequirementSet + + # FIXME: Implement constraints. + if any(r.constraint for r in root_reqs): + raise InstallationError("Constraints are not yet supported.") + + provider = PipProvider( + factory=self.factory, + ignore_dependencies=self.ignore_dependencies, + ) + reporter = BaseReporter() + resolver = RLResolver(provider, reporter) + + requirements = [ + self.factory.make_requirement_from_install_req(r) + for r in root_reqs + ] + + try: + self._result = resolver.resolve(requirements) + + except ResolutionImpossible as e: + error = self.factory.get_installation_error(e) + if not error: + # TODO: This needs fixing, we need to look at the + # factory.get_installation_error infrastructure, as that + # doesn't really allow for the logger.critical calls I'm + # using here. + for req, parent in e.causes: + logger.critical( + "Could not find a version that satisfies " + + "the requirement " + + str(req) + + ("" if parent is None else " (from {})".format( + parent.name + )) + ) + raise InstallationError( + "No matching distribution found for " + + ", ".join([r.name for r, _ in e.causes]) + ) + raise + six.raise_from(error, e) + + req_set = RequirementSet(check_supported_wheels=check_supported_wheels) + for candidate in self._result.mapping.values(): + ireq = provider.get_install_requirement(candidate) + if ireq is None: + continue + ireq.should_reinstall = self.factory.should_reinstall(candidate) + req_set.add_named_requirement(ireq) + + return req_set + + def get_installation_order(self, req_set): + # type: (RequirementSet) -> List[InstallRequirement] + """Create a list that orders given requirements for installation. + + The returned list should contain all requirements in ``req_set``, + so the caller can loop through it and have a requirement installed + before the requiring thing. + + The current implementation walks the resolved dependency graph, and + make sure every node has a greater "weight" than all its parents. + """ + assert self._result is not None, "must call resolve() first" + weights = {} # type: Dict[Optional[str], int] + + graph = self._result.graph + key_count = len(self._result.mapping) + 1 # Packages plus sentinal. + while len(weights) < key_count: + progressed = False + for key in graph: + if key in weights: + continue + parents = list(graph.iter_parents(key)) + if not all(p in weights for p in parents): + continue + if parents: + weight = max(weights[p] for p in parents) + 1 + else: + weight = 0 + weights[key] = weight + progressed = True + + # FIXME: This check will fail if there are unbreakable cycles. + # Implement something to forcifully break them up to continue. + if not progressed: + raise InstallationError( + "Could not determine installation order due to cicular " + "dependency." + ) + + sorted_items = sorted( + req_set.requirements.items(), + key=functools.partial(_req_set_item_sorter, weights=weights), + reverse=True, + ) + return [ireq for _, ireq in sorted_items] + + +def _req_set_item_sorter( + item, # type: Tuple[str, InstallRequirement] + weights, # type: Dict[Optional[str], int] +): + # type: (...) -> Tuple[int, str] + """Key function used to sort install requirements for installation. + + Based on the "weight" mapping calculated in ``get_installation_order()``. + The canonical package name is returned as the second member as a tie- + breaker to ensure the result is predictable, which is useful in tests. + """ + name = canonicalize_name(item[0]) + return weights[name], name diff --git a/venv/lib/python3.8/site-packages/pip/_internal/self_outdated_check.py b/venv/lib/python3.8/site-packages/pip/_internal/self_outdated_check.py new file mode 100644 index 0000000..8fc3c59 --- /dev/null +++ b/venv/lib/python3.8/site-packages/pip/_internal/self_outdated_check.py @@ -0,0 +1,242 @@ +# The following comment should be removed at some point in the future. +# mypy: disallow-untyped-defs=False + +from __future__ import absolute_import + +import datetime +import hashlib +import json +import logging +import os.path +import sys + +from pip._vendor import pkg_resources +from pip._vendor.packaging import version as packaging_version +from pip._vendor.six import ensure_binary + +from pip._internal.index.collector import LinkCollector +from pip._internal.index.package_finder import PackageFinder +from pip._internal.models.search_scope import SearchScope +from pip._internal.models.selection_prefs import SelectionPreferences +from pip._internal.utils.filesystem import ( + adjacent_tmp_file, + check_path_owner, + replace, +) +from pip._internal.utils.misc import ( + ensure_dir, + get_installed_version, + redact_auth_from_url, +) +from pip._internal.utils.packaging import get_installer +from pip._internal.utils.typing import MYPY_CHECK_RUNNING + +if MYPY_CHECK_RUNNING: + import optparse + from optparse import Values + from typing import Any, Dict, Text, Union + + from pip._internal.network.session import PipSession + + +SELFCHECK_DATE_FMT = "%Y-%m-%dT%H:%M:%SZ" + + +logger = logging.getLogger(__name__) + + +def make_link_collector( + session, # type: PipSession + options, # type: Values + suppress_no_index=False, # type: bool +): + # type: (...) -> LinkCollector + """ + :param session: The Session to use to make requests. + :param suppress_no_index: Whether to ignore the --no-index option + when constructing the SearchScope object. + """ + index_urls = [options.index_url] + options.extra_index_urls + if options.no_index and not suppress_no_index: + logger.debug( + 'Ignoring indexes: %s', + ','.join(redact_auth_from_url(url) for url in index_urls), + ) + index_urls = [] + + # Make sure find_links is a list before passing to create(). + find_links = options.find_links or [] + + search_scope = SearchScope.create( + find_links=find_links, index_urls=index_urls, + ) + + link_collector = LinkCollector(session=session, search_scope=search_scope) + + return link_collector + + +def _get_statefile_name(key): + # type: (Union[str, Text]) -> str + key_bytes = ensure_binary(key) + name = hashlib.sha224(key_bytes).hexdigest() + return 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", _get_statefile_name(self.key) + ) + try: + with open(self.statefile_path) as statefile: + self.state = json.load(statefile) + except (IOError, ValueError, KeyError): + # Explicitly suppressing exceptions, since we don't want to + # error out if the cache file is invalid. + pass + + @property + def key(self): + return sys.prefix + + 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)) + + state = { + # Include the key so it's easy to tell which pip wrote the + # file. + "key": self.key, + "last_check": current_time.strftime(SELFCHECK_DATE_FMT), + "pypi_version": pypi_version, + } + + text = json.dumps(state, sort_keys=True, separators=(",", ":")) + + with adjacent_tmp_file(self.statefile_path) as f: + f.write(ensure_binary(text)) + + try: + # Since we have a prefix-specific state file, we can just + # overwrite whatever is there, no need to check. + replace(f.name, self.statefile_path) + except OSError: + # Best effort. + pass + + +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 "pip" == get_installer(dist) + except pkg_resources.DistributionNotFound: + return False + + +def pip_self_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 + link_collector = make_link_collector( + session, + options=options, + suppress_no_index=True, + ) + + # Pass allow_yanked=False so we don't suggest upgrading to a + # yanked version. + selection_prefs = SelectionPreferences( + allow_yanked=False, + allow_all_prereleases=False, # Explicitly set to False + ) + + finder = PackageFinder.create( + link_collector=link_collector, + selection_prefs=selection_prefs, + ) + best_candidate = finder.find_best_candidate("pip").best_candidate + if best_candidate is None: + return + pypi_version = str(best_candidate.version) + + # save that we've performed a check + state.save(pypi_version, current_time) + + remote_version = packaging_version.parse(pypi_version) + + local_version_is_older = ( + pip_version < remote_version and + pip_version.base_version != remote_version.base_version and + was_installed_by_pip('pip') + ) + + # Determine if our pypi_version is older + if not local_version_is_older: + return + + # We cannot tell how the current pip is available in the current + # command context, so be pragmatic here and suggest the command + # that's always available. This does not accommodate spaces in + # `sys.executable`. + pip_cmd = "{} -m pip".format(sys.executable) + logger.warning( + "You are using pip version %s; however, version %s is " + "available.\nYou should consider upgrading via the " + "'%s install --upgrade pip' command.", + pip_version, pypi_version, pip_cmd + ) + except Exception: + logger.debug( + "There was an error checking the latest version of pip", + exc_info=True, + ) diff --git a/venv/lib/python3.8/site-packages/pip/_internal/utils/__init__.py b/venv/lib/python3.8/site-packages/pip/_internal/utils/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/venv/lib/python3.8/site-packages/pip/_internal/utils/__pycache__/__init__.cpython-38.pyc b/venv/lib/python3.8/site-packages/pip/_internal/utils/__pycache__/__init__.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..d53426727ad08bfa0e8d1463bb78435543e00748 GIT binary patch literal 207 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 literal 0 HcmV?d00001 diff --git a/venv/lib/python3.8/site-packages/pip/_internal/utils/__pycache__/compatibility_tags.cpython-38.pyc b/venv/lib/python3.8/site-packages/pip/_internal/utils/__pycache__/compatibility_tags.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..209b5011a1f120ff7216f7dbf21fa8c9592c3ae6 GIT binary patch literal 3651 zcmb_fTXPf16`r0MjmENUz{VI@Haj7t3S~j|+Q!8aLf9png$hwzz@}A z>1p8$qr8|@E>(HR`##u@`%m&4`ZZ7dgrt(%e5WNDiR@c4E}iZ^-F?pK?_9e5#oS!Y zz<2HaKXbWc7=Ne1mf#r7iB zSjXqXr4!b^5ZcGf+(qvqpW(IFM*9+<<#n`|`5d1|`yM~T&!WA;&+!Gc@AGB8$j`rK z?eFo6e2HH`?*ncL81*600Xrqt% zRs0^{S6l?)?AVY@nk52&o4+?N8As*`1B_Nn8E;IiAOl`xQL3yY$dy${0nPQ%5&S6a zX5SbB;br{ucPqJwUIO=(mw{O6W-*6hD|u&Sb1#3nv6gOzH3yYH7+r=SH($HbE-R&dWd6)JVgo02N~4FibTpDvD#;WVzeQ4c4Wwt3 z#&50+7&nfMKN=;I5P1ZlCXmXC!xTfa zxvK0ybauXB%BHB(ZV?xtL)Gra2qMDm-iM@^4((LUZx65fQ$g6g8)uy$F7BZ--q5J1 zIiGZrY#*tkD$v^5QBF@}{k%{X+#_Znu~8LQ(1`DY(4rTaKog~CW`mCG2eFo+W=ZEb z3mg&~7DHp-r|sI}`Dw#`h>2nW#4u~jW@qtNtGfSiXB{RQ;7(y&0fSDc!~6w0+8uE0 zF#`UCJ-3GLEKP3in&NS3b8FWTUAP-nuC$ho(q2XoFn9x;$|NVfj9oT%4OdUim6SM6 zOEI5PJ3WY_w7{IjB$0+52rxc0U-YYfR6tzIXxL4*)`_w-ACFO*{h zPAl#fRu=OhhKXpxpUY|{;^{UxQ)i~KNin6*OlV1*8ac{#ZE;;9t~eD=WG;HhV_im3 zAfBSMj_10F4Q}Xi@P@<*A~@=0>i>ZHOjSoI@vilY+WOITGg|W;TM7rnFzf7!DSnJz zN0&L`;iLX(4iF|(;&)v!FO4H(fXLRPE0SS^lN`fWB|Cx54YPGZW&I%MYfqxsVt>T3*o z#sH6xL4}~*oe>v@&<`qrmcq*%IGC-Lc&K1@qqIbXT`gU1{l%h$PtR*ED!1m^L$2mkvC_fH%9uyV9y|rkPeR!jsTX)u zGQF(p75$tZ4}`i$P!#=y z49v#XdRbq1pM3hLIcdN+SMH2#KF~_TeN=OAJm(!`eQz&FrS~$3BYvuXe0JD6w&YP) zn{>nL7onDTC}fzN3Y@Zl0`9xCPY2@vZo@>NdL(f+}h z#-eT7s0XfD)2%Za&EulCnK}IXxBac-PLAyA)W{W&APL2*g$o5=!&6n?=UK=1Rn7Oi zedK89`-1ACs>WHbhjdb=2-RggF`Iafq&yQ%dV~fv)uiwh7FKISCIEU{6zR<~+OC~| z7iv?#P@D1~hYUg0OgW>$EXk@9nIl3MkD37@k6}wFlL|djd979l(C5uMV6R!Y^_ScW OZrycjHG}EDfBgsY_^W>a literal 0 HcmV?d00001 diff --git a/venv/lib/python3.8/site-packages/pip/_internal/utils/__pycache__/deprecation.cpython-38.pyc b/venv/lib/python3.8/site-packages/pip/_internal/utils/__pycache__/deprecation.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..24e01b08a04d0726d633e26be4f95dcba3af0810 GIT binary patch literal 2878 zcmaJ@-EJGl72ZFRD~hsZJ9b+m0;Yh0RBcTfY8y0$VMMT9$7sVCj`Cxo#df$eBv;zM z%*>J|P2E(!Do~)0U|;q@`VxEFtGqxjf&%Gxb}5REiw?or`JeNBbLKnq#m-LKLb|p0 zSMl$TW&MjPo5@4vWBm2skqK*H30pX^oj3!>rm`Em1GoBm0}o#>ZY0e?v+8Tat;8Q} zRpn;fPPPZzwk5aK*BIA|JIT&qC%H7ZgnoZ;`LQLoMEjK`+H&{I9b6GEzbdYXZP9sU z53Y$Fap{#ccu!mvm&Gnxt_$~bYyaA!-#%nXCW=@xGvS6sNgm5YrbaW9WpR{_@ntgQ zLyVAI&)JNt6wR9PVOAI>WUge$O_Zg&+x`|Pt$n+^%!fLQ3nK$8m?_goTO;R6OBA+) zU@Dcyj37Yu>Zi}2J`Wyz^60_ugJ)m#`%n6h_np!`Oy{NlSxyV{c(L>F(bH#-9vmJ$ z`K*7Jg|y9PvQhaMe~mM+q&2WXwIggmbjs^bqx|88m0#8N_xIh>4}z2@G6>3c5Y#N7 z+zEnj3Le)z|F9G-S!LhNdbx_G+{oUPtKKM!1z_~@u=n9CpZ@wF{pi35@!?Sa_Rc6- zB)_>+rE%Ij>g7>>yGU~$o=9_B_6ifly0^hY;I8!};FWr_E*abz^D0vC)6^-8sS{M*EAKu(`Xoxw~Pl-fx!IgG@!^2xtO5 z$!05uDch0O#W0My))j!V6=!1%8<$&SY5st+RAp;5dcRe+5~(#G%hHE?$#JIO52GlC zFD9~V!Zl@@l^(T}9@WrRt#NT+Y6nc`;5z6GdN4$v55nR8? z^IWC^ocZV1s_f|M4gwcEq9RIN!WQNB28zn04V;yWR$y;LsWv>0gVlOL&B*~)(Bwsy zyX*OO(^d@SkksiskvpwHO;A|^mauZ5qeTWy5%+RYa8VLCQA&C;a*o8Rt@w+MK%+ z1m{Jh4QbG*NW)qQA~Wx{+YCuTjC}nzKh3Kxf%VQo^+W79Cx`6k>)GYl!`$Aqh9cl`-oXYiXZ($>A=PcwY znOtu?&Qi$eCQtxc2`I}(40n-9NY z!fG|cW|fIJps8dWVTF*7ikMY&vr(qjd(bITTw9L6&~*DQznVCMQLf5}$#R&y)U$%qpEeJ+l>$%Gn?Xi6POZi5Ko=%ozk81 zxb%(0JAxc$9Xx9--nuuIseF;E`+ug5NOL^9Y5CS-Z++`U_gjf%N)OXFI|G?ZFh!{i^LYhPfvk8VwY!@Ajq+%_rFeDD0n+iK(M zH-2pUjv`S;GzEwK{TpQ~2t*dvZ;?@93WaxxBGhV^vdY-KD(+4ZGBVYr*952d*tNNXE5rOKU!)OFqz5)Is m{P_3kXViW2E&V<+uhnr7X`PPKL1gvpUDrbh^1SvHzw=)^@FY3_ literal 0 HcmV?d00001 diff --git a/venv/lib/python3.8/site-packages/pip/_internal/utils/__pycache__/direct_url_helpers.cpython-38.pyc b/venv/lib/python3.8/site-packages/pip/_internal/utils/__pycache__/direct_url_helpers.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..7b948f9e840cf28f5c2fbbf3517d3b8717df6251 GIT binary patch literal 2728 zcmZWr-H+SG5$BQ=MNuDjC!as!I4#?J0BU2&aNT?%34FLaw{C4Yuy8HPtvCJ{mNzu*-#A%qHcW2A%l{5tsD&ifJpAf0Npw$749`eR&rC{QDX}~&DSKst zd?U7#idRu%Gj@`yS0&nX9XO@9mejpE(Z0~qE2c&pM-Cej4IjGZ)gNoA{-s7M>`x$T z#p}t2x1qF@<7RTfyP(E)yqR3|F2V|Jeu-XQ5bsJ@37xMs@3n9}tcCTjiFY+@glj-~ zJtU8`*2aIpey!WGFoKcNxo=ECWYLmp|`u9JGjR-hcerV}JXXyW79^AMSU%ce}p; zsxj$t_^kcv(Sz;|?PVcV8h~U!7@@)-hOv1sN{`@M+ljbf(ZLuwJsnM7HWR)UyxZ{d ze?gaPb8P{>%SlKsYcqYI%?v?u?NArSGo5{!>!Ch3aei2W-cqCT!gwEK)4ipn4U%!L zBOmEX25ulX=1wtQ5Td2;ETyZ@be@aa@6|4cb@m4}UR~W_f5Ek(0jnFAHC&w=>>pr@ z8J6a2xv>CS)lNFxjWxZoouw1Xgd4bLzIH_h(@`{X8U5`TZXltlaM3O4pglF*+wRmp znf>%+cKc*Dt@H6gsBS4^$Dh0)Z@wV!wAQ6PDK3mkC*o1cMUeKWG$XhvRtD!u3kFFb zSOq7>7%scg%aSA#eiSkTiRLiiLum!05luseu9uB1&cWC-6xcM3L~syOSz9$jnul4G zwoK_1miegOOoN2V>dKL1rO$!^9*+N2V=ip);;YVxMUz0#&Lm)+einz6@y@8%dGG0H za_eUL;Y|^S?;Y?DZuFCB^79+24bskD2i$dioQ{It5e=_D9a0)!KMIZyvk!lEjJ%E? zrGhd5TL+*O^Uk?t{*cBa*rq)?mT#|+1MZLL=+>O&nQd9r#Vcehkugdg$2GhUo{dVjEumPv`(L8jA&)BcK2% zGb15oxGfv zLh>9zSq=5)#v=@#+Cl>W+d#8K{Tc4Q z7zSr+0M4au%TRz~7vVUviFZx8prOy`1Ynq@z~2NGYhrg5x@qZT_Q}btMc5^nwVKk3 zxDOelz~!X@75)Lvw zXR?N}r^YZ$DJmM+8>r~wDu&P3tbs-JcaWAK{16I7C{!>ye8e=nfNIcu6*nzMnlU6Z z>F{jKdK8z)OGxoSQIz@$oPIBWqsS^8_4I&+XWm3hh_5_YD18?-yJy{xP+)Tfr+tO}7A&w(fjuo2F$Gt7HQdb@iCozcfqcf31oQV-ug3mSqA|P0~;lfb>gt z6ZG^5YFjo;#)w&k*FFp|0SiDHZ(yOBHibS1gw0K)Eyx$zBZvr55|$|U4JdU_h&F?C zIK3w9c_q|UN_7CKCKklU+Mi9-5|SDDihK!kEQYFp8PmT#I1oXUx~n-7PYs08(E#qV z@N|2%cC;PGh4zq&F-y7I%~I+{eOC;r8?KZX7p~DwGxy24L)(WuOP{z26#-Pd;9aFB zW4QYNr6TS|smir&CD)z4uF9J`3K-bR5wGPDi~reglojD z3y_|Plu!iyE}Q#lg!OZ!ib^Za1_KK9_-VjW*iY6*EIWkqt)SV`rHQANgnl1E7m)5_ zIGmq6hF!P|omO@L3_9ek0yO%|Mo9q&ssA%A_A$t`^zNP3HCaXraOWRFnr)w@*>(b! z#=L#5y*R7^D~rHW2g9;b6r`)KE>~H)EsjT^n&H2g+;~;sEJ>K?w8BHxyU8eHqWDE| z;Gbn&{A@^Tsi=yIUVBy1(ol>bRs+6Q@F$RSGS*%N7-w}{UjeV(E8K4T%Fn*f-Up6K y@+xqYgH~MbB3DJO{W0t}j_IcuS{y5ht-E%O)N~z6MMHOVC?`5Tj$vPOZhi+@E%2=X literal 0 HcmV?d00001 diff --git a/venv/lib/python3.8/site-packages/pip/_internal/utils/__pycache__/distutils_args.cpython-38.pyc b/venv/lib/python3.8/site-packages/pip/_internal/utils/__pycache__/distutils_args.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..7241e6b74a6c3c3354edc530dc3c1d01bd6d5ab0 GIT binary patch literal 1191 zcmZ`(&2Q5%6u0xyW?eSAG3|f^$^ju&q7C9>+9ZaUHol=kdAWE&bC%9 z7dRpDKfsZHDOXP1IdS4STh|`Ov;1Pe-?QJR^LB2ohJdbK{z@O(2>sHVX>wt6AExX9 zqln@dCAfpJUK_EIm>siRo3WMH9UCKNi&em^xRO*m)x_yIfZMb}tJI-0v_@yCOXui3 zy+G?-w==^`HuD*GYIK1venFjCdhs=CEd2&+sDX=xtw^d|MX}rv-3LOX0)(><2@m^^ zm`XDR+r<~#FSmou#}7821+U(=+fUk$8b)DlMWHIJ=WxDZ4qSH3!e%DeZgjlfE)a<9 zUYfA=wy0`L=3p-3Qjs`r?htvNm=Z-M+V!z^j}gj*oFMKPiKCr~^h`gCiTV$c3wAyt zLlI?a%0_h@$k=&Xq^UZyR#>@Y;v}*V>r9>!6d29G+=MBg0*lZH578F-cz0wBmKEB= zL!*l+{)RsoBXeZELww0X%Gj@o6^!)QMhDJ&G{oP{p|xlIfJQiQh8Rj_3_iUiLNYHC z>0v|}^@!-^3FAs$_XJb9;1Tb7gnQ}E9t)MHdPI3WB4NR59#BG5*qc7rs+IB$-uH68 z&(kA5xf52*i@H1&jJ8TO=mZN)%7$Gm1a#asLAPv^qH^*T21H#{@(jw!3MT-?Pz6E5 z7CJ9QMKGDisxaZA0tba%`t%ctML_=!{7giLaL_*_!rx6}U3otX{gtEa@b*o?VWc&W)0~+<)U|QP#|PM#6nYn@2sy;^sc-@1<+E`Wo|th$|*IiT$$I{fYZJ zqE^-~mNK2(skk(71-z1VV5n+g2OD@9&s!B^9*ZTAwHt0xKf9^L^y`r)30R(pclBF3 z&5|(TSaAtFYMpu1Dm`kceg@?emR6{oZge||ik3J5JjE#GQs}=)X}wI@6uVgJ-Az)O c$LyZ&N_h>Ki(SJtEL^t?@c*3g$69dAe+Ilku>b%7 literal 0 HcmV?d00001 diff --git a/venv/lib/python3.8/site-packages/pip/_internal/utils/__pycache__/encoding.cpython-38.pyc b/venv/lib/python3.8/site-packages/pip/_internal/utils/__pycache__/encoding.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..2dd2b783efb0fc0d137f93af40533c893753c0ad GIT binary patch literal 1299 zcmZuv&u<$=6rR~%w%1MxX<9*PrIqL*25h%BL@5GzW5zcg1)Idg&wyjdrf4~(_%d*6F+-~0S_jyE=31n1#{w`9jg=ub`7 zPaeqE@Q7c*5JWIT{t~f|Gj8}shNf=<8q6BmzKs#Jc?-D7oKeotjq-keRPYNBM{itW z^$oxxu`?_YC&LYr%kUP-XZQ{&WVlJ(zA?!8?^0vFMT!gT-zFup@iX$b2|h&STUVeB zb;?+6J$v=yRk!(VyZL?h<#DI;UFVy!sjP!YNM#?5Q%03_M1Pc1do25vy{V(&@$R&+ z#7d8@Tt=7KMtvD-)K^(>85MV7W|)vD9=!hi%hMC_(a!72$wl?_<8p|#p6g=)!eUVq z9`QREiCXCNlM~|-Um7zs$1}XZ+?pY2%hNmXojn$KHsExIq; z$`Lf+;ZQjPo{UqWoTWy!{eL2rDJDW~L5k3RFlO>bec4hL31pzmUNTboo3OH26w^Nt z{{YnfBWo#-&I3tn=K-(v6GkW(wKS~lUZm%r*5fC2Nyu(b>^pA(Tco6NF{G4L&VtFCZ#2lne`Ro7Zd^``dC|^FQh$V@v=5 literal 0 HcmV?d00001 diff --git a/venv/lib/python3.8/site-packages/pip/_internal/utils/__pycache__/entrypoints.cpython-38.pyc b/venv/lib/python3.8/site-packages/pip/_internal/utils/__pycache__/entrypoints.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..aec8e7b8eab2cd5eface666b8263445169a41262 GIT binary patch literal 1351 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 ooB@5NIA&Ff-ZAfpS3rncj zC-5iyQeJuD7kB|DtDs!#9Qm9h>-yT)olYC5dj9Mic>@9XQHkwR9yn9TE)@}g0nrF# zS`0DN0EOZ}=>}`WI@UEnbZig-YM6Z7MxM3JF7cpLuomA zAe@=(D<_{x+mL4Me3g~)z)WsTwQ1EUlX0MO8F9O@$rkm22M&a1KPpY%C+Xbx%cnfz zjvpmC6-mIHB9B<+sC!4rLR9ZlHaDVCi6@_ju6yabH&fS}j6Qm0_X79-$w}F{!P~Nn zM_2#p>G^nD@0^bZ!_g;q%li&QN42WySAn>QmO)O@G7uy2{Yskx@M|3OC=CIecAO cCvi#&Mo%?$lWbR!4j)5ZtDqLN6uQ6v0Ingc3IG5A literal 0 HcmV?d00001 diff --git a/venv/lib/python3.8/site-packages/pip/_internal/utils/__pycache__/glibc.cpython-38.pyc b/venv/lib/python3.8/site-packages/pip/_internal/utils/__pycache__/glibc.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..084b53e7cc4559c7bea29b660f207be4ed404382 GIT binary patch literal 1760 zcmb7E%Wfk@6s@X$rekLy3=v`iY5@W)P3$NGBO(nbU@}8Qa7KB|5DTg4bd{5i`$biC zB3t$Ql-2dT_3%0OoTGZ(Lr^|??>G8q4WYmE;C3M} z*n&^|3>8HbCn&{TjCG$Rq)V#S=o-)(iJ4km3nOOnConb>J9WBF>ULd-x2XLq>Ml@+ zy3l%bfqKv`(nVT>wnoVTYSjORTTlb5d*O-5lTxxEPK%t&4$Lk8aQyr@Xn(ie{yx}0 z>U4HG&l*IzdxebiEKHPjSQZJ3^a-~MhCv%X0e=rkbcG5};wuEV;o;^!lO@lDf11Q6 zk$=IsfU|rdd7Pa-^mFca@{IX$&(Ct{mjY%dp-s4Xwb~R+HwBYTL6m1b=&A>=UzLM` zi9Zl`;W1V9&o8Zl#}}bwt&5PidU-ysi+BAc28rZ z=|%?EIdKO_J+CJ#AfG}#8$PD4KMf#`&#ER&(;)6aVor04LRl8}q$6zHb1F5~Rc zMn}EXJ_w$5j)L8tZ`#3&?frwDy-vebh8PIt7D*_3IZqYIg>q&jS)xc{sg}N$!1xKI z+cxJpSI*wSq~pB^lX5Z%gyTRtQ?_66#}@z%ZbS7pd}0YI_J~}e zu_2w2A>At?_{g|k&?JqGkx_yF< zJ;HV59lrG6)B>pZdLKI4MaF0ZZrFGEvQB4ej8IRvlcfIr5E5GEK4l=lGzJTiXY8)Y zc%Bbs=0Bk?wR!2RpZJLIKLw~B&Mc|nU!KL$864OP%S7t4Sz5@!EV?=@&ip78le>~U z@0W$&3qknJYPACyjQEG}d}USoZ{SMj;H>pKelUlea%Y^qguOy1E^=(_Vw3QXq2H)0 zF9>=i7%2+^u7Tp(OL!eB^)47u^FJeMRz}nWlEF#2p#$vcK#PJ~y3#&ViKb$ry5rW9 kD&y3eQ|^;Ar)9#v(p!bjr5ec1B{gE=Hw@Qym)-S$0a%pIi2wiq literal 0 HcmV?d00001 diff --git a/venv/lib/python3.8/site-packages/pip/_internal/utils/__pycache__/hashes.cpython-38.pyc b/venv/lib/python3.8/site-packages/pip/_internal/utils/__pycache__/hashes.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..b6eaf54c1a1a676f043251f4f6a08d4f0a17be9b GIT binary patch literal 4210 zcmbVPTW=f36`t7}mlR1|EGxbiTOh5=A|@T*Y8!@Y$5w5n4V_4~6EEtlS35&;&An-M zX-O>gVhIRPpeT?ZP)O)Y{V)0x`n<1w>R;$fd(JFLUE-iXmzdd^oij6M&V1)PhtF%Z z3W4Y2i+^O_&l2)a>>PbG=-h{r{sSsT7!62BZA!7N1d6S|TMg7uv$asS_0X^lIi>}r z(6r64Y?s4|U6J4PU?!~ERoOOzT3EO1l<>Ov3#?HJX2UsqE}XaL!v%XGJY}B>7wttU zYr^`c?b9H8T6_<(?KN~D)cJQ`v`k%#y-PlSe4bfs(tn`VRbgUN7yVsx2xLc zp+Cpwp+C>hL;nI()<|RFUvMhYp!ux3p2oo-}6i@DxOUeCa%Vvi$J3&g%1ad!_xjq37Bo-^+6CcYd1X=4XE7itUxBx!I0ac{UJHZmxg| zZYCF0zkGQO7SbKuS~hyn$c?n;UcGTszOUW7F5fpkz3Sm#@275TaCDR7|4gKaDip`=StjVPmiQ&fEtDI+=} z4&WNlpbTR<8yCKlxWWy`s@$<2SPU-3iaVAYbYnn8FSH^z1TB-;k21J$D~qiFt}HEt zf_Rg&>1w!Qk8i;uT@E7C?`^m;(*cyAbVJFtln0&MC=BqPh;yLqzeOt%{#!TWt+%dd zb>e_=k+u@Ab#pU$d;My3`)bD6&Gq!wl}w5GAhH=j`%kkMrPi z-`#!_-@dkuV=V_%=OS{0)*$nPv{mfeOtwKO81pm7X`s!dF^%DDaevLpm`7o7@?aau zLK^uDG%_$MQ})P+yrKKb0eJ)v+C?*~?PC*hal~PYMcxj1Ro|f*+|n+6K`7Y(j#(q` zDH=-+cm#G*npg7D*qG8tMRgvQ$jf)TJmOo4xOW{_M4dFa{^8HZ+85ItFCIi$^KKA( zZjjz@;E7J|R6V-Xr?{CklsY{1fvr3|b&X_NrgxNnMSM=-rYXq`M)yl7wM*a8mmu>Elo76R zVMoi%ks{Zt@8~0azdX_oDB$DUjn%9&9%dUJ0|kjgYwFC*&D>_e zKj`nPuAg!XjW-Dpmfx`=Sksc>;)v+*^z%qEndBQuEO)Fw?6ey-f$%MEwS>wA{(7!P zd^4}Sz!)UsN?v}pofJ)Bf_QFpVgXwSL|ASN5>Si_gdCuOZxsm04M(7YjZ%RVqc~5l zb>l1*mrz)jpK=ZN%~kk*5;cpGm7TG*#$oN(Fg5)NRK(P1ohozzehM}JQ`JmbrFCTv z`X(KoN6YZE`JpwAVQaz<5?gbyZhUXi*_M z2z1kzPzrP_!`fJl!Wuu=c*!JwP6#L-)s_<60d*;Fklx4`^9`!@cbP zLIxb`SEzOwDrh4MNmN89_4j<4>T2EX=^te?GGV z3*M=vA^MT?=D3sxE`LNj0rvDzu`RW%HbgwQlMHWjrXy9z-2J=`Wx< znuUHxI*>vzb7zKhg!yS_oF}|~U4Z%ZYpcKMif*$(#aURT-OzG99l&ji$IvbAzxJC4 zFP^ThJX?02JXm`o@%NtQWykRXH%$R0hnI|a89^x!uMd}w-N9jMC^I3D!{-=-FTw|8 zm5iPxm=_D`58Gr$-0|UT%<+tl4qA8{Dh!P&zk^TPey|L-0^ja;cPnrXbI%L z+!%LeP_-&nHLNg-3HX~i-hSYi4p4W8H5o~=G@Kb9a^UdxRlZb_dlz;bvGQ$XbA m-GXB7Apy(4UsX(HQCZZhw5n+If?B1J2q;xu(<|!SSN{dqV+u6@ literal 0 HcmV?d00001 diff --git a/venv/lib/python3.8/site-packages/pip/_internal/utils/__pycache__/inject_securetransport.cpython-38.pyc b/venv/lib/python3.8/site-packages/pip/_internal/utils/__pycache__/inject_securetransport.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..0597643452d6e98adcff0583617488575ba2ffdb GIT binary patch literal 984 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 WGWEiUZH`~Dt?{28$&Tg53jYtO)6FUX literal 0 HcmV?d00001 diff --git a/venv/lib/python3.8/site-packages/pip/_internal/utils/__pycache__/misc.cpython-38.pyc b/venv/lib/python3.8/site-packages/pip/_internal/utils/__pycache__/misc.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..2640a484c243cca2938822334f8109c63128b8df GIT binary patch literal 24424 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 literal 0 HcmV?d00001 diff --git a/venv/lib/python3.8/site-packages/pip/_internal/utils/__pycache__/models.cpython-38.pyc b/venv/lib/python3.8/site-packages/pip/_internal/utils/__pycache__/models.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..4ddaeb6f2679704cbda7fd942cf5369f50135880 GIT binary patch literal 1976 zcmb_cOK;Oa5Z<+&G){RBggv9I)Vo^X$C;xIl= zz}gHrI5BVYmkeC+P8oR0IdR)AINPjMIllGYMsZ{#W&BR6eNde!iIQEvpMr`_{Tpgw zTOrNo$-$pZ9OeT~inTeu6QfTP-JVi1HyN zP)j~1#$FO(ERAWoxo1?|$z3{~{q^vS>1ovaT?d(t4y9GWq0~VqjX`M>WMT05C_8+( zo;+Q*0FQUfllz_iasSc%f_9VOU64iD+Ch@Za8JS7QCF#WZBGt*>C*=TItxUUSfvvg z2L~8R6O@s=mksioz_Z8-(IoRIGZ?S(@&v6@}Bbr zbGXFe;xnTd<+U_ZTG~|SRUu+D|I8_X<(1TSmA-&aQ`nbrjG^6};2zTS55P1*)^j)& zaFh_59B~oF67F5c@mCb6;L0d=(Ud=;AV1u=1PW4cbrh3)v4RLzFN5NL_(JVZ@WnMm z;9mj-DY&vPl1aXxoVa}n6r|v{)q1~BJBVrB5Muv8#-o!dAs`JgYBQ)WK`skP9)p<6ZXxizomp8qsSv^-7z8`W^S(^H`>4$i47w3qE}qlEp8dS}se Iy-Lmf0ncuv0ssI2 literal 0 HcmV?d00001 diff --git a/venv/lib/python3.8/site-packages/pip/_internal/utils/__pycache__/packaging.cpython-38.pyc b/venv/lib/python3.8/site-packages/pip/_internal/utils/__pycache__/packaging.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..8d862fb31608b948df975da36fa8de8993bd0049 GIT binary patch literal 2660 zcmaJ@%Wm676y=bVM9GxpR~$Q!b{e!n1jJIBq(Ng8MGVJDjl_y!J3zxCpcru^(dH{N zBfB1C#>id3kx~-g6H1x?C=4aD9677yr*G zP5VnuCNBdfPvB;MK;vsZ3bhD12+6S?>W&V*5gL){m}<=ob5Y*Ot8p$YM3!SkMW?9d z`LGmCIa6v}2<@osl-1Y@r=yBfK^m#hU%_87oQY=!{#kRb`O|&{`s@CTKMVa0f6kwW{-&?*YSo3mA%3ljM8$i@lCZ~# z8$@YBc>~r;&j|6iJ<14$SvfuExRkJ@M_Ytdbx~p|X$9?oP$mk8&<05icC(EnCR>Di zzQ?@{N)!38?FTFky<<1^co*h#TW_}Cxa;3+tiNz~UN;(>jpyLedX;i;;)No&*GoeJ zhJ`J{nAed*`)R;94c_&nO-q-74=)q?0^I9xvxm_1p&|zswdEPPHb8yt9G&W19~izd zFnNyW`{+zRN3d%2jlO=6hg_K6Z%ETh7_Rj$X&vC8jd_>gPH;$KynW2ONsNc*#F58a zUBa-3Z|#uxy#Nxkl9}AXL%G+w{Q7;-j>IQK;S;cGk!3vj$#7P?mF=v>P6ugj_ z2XP1dX5N$o8B*l!Cv+tWV%4^2GV+Icf*}-$_YSFbYPiXcS#21mde?WW58gJbqgW{6 zJsPj#<{s^l=Kr3Nc|2fHU#8MPvtvdOU~L@LD@YJM3K+Qy;Y-K3`OFI$X^vm#!6w6- z@u3$6{_Y4dt9Y}Gn?sk(O;vK6xa&biV@QXx#_fbcp0l?#_5`X`3p5Ai753QCF5xsU zmvWF~MufSa1hFt8FBJx$rD}@GrMC!sGC5-T(zZLUo|w+Ua)BXtnIw1ba zQJ0W#<-j}MPaZ!wmV0$57bg_(Uk8kb;1evp4#1(79*Zlg$iTo1ZNr=`LPShyRoT)_ zRMHm_y#{M7fb#f~{E&H5{P+O|J~~AlSi;f1ex}pgeQjX$femV8%F(GF-;=98u!KJG z^;6)`V6?NybA!BZ@IqhPx6aVH4jN>h>V4hM0qeTF*w-Y#enPx-rb&hkruyIqX)v=m^;^Rv7!(3GJA_YK4B^SJ zd{c%fVQp>ftvy}aTRX9~Up!ygY&?5)aGLFZKbA_%kN7Mzt(T0SjWry|_iF zOsZIrnzRXop428`PlhM#$xuW-OgbGxMd8S!F_e~C(vKA;Enr&`Q>kOrkZWce_tRaqp6sZ)*!D^trbqQW?8z!(ASPAZ*hPC>_7`8HcnhsmYqx& zq>_Fpm80+_)v<>si+vZvlB|~U0$Lm=91j&TE8t0G=c4pBs)mYR7?5&053$063_1IF zN#S+Wmz2Rs*lOha7EvF0rjRZ!Z7bcY zMFDGNytsDp>1>-Hr;<2D8Bu;XkrGJZpBj`p3p>jGH&7eaoaGBqPX3RfW%*1Ivh_0K zrR6GSK{h^-E~S<#OsTr7q)nt+eh;Xmn6SQz62BLcuchuX42=z(vVmhWaKjJ8c}Dg| L$*>l!>(=alcKh!S literal 0 HcmV?d00001 diff --git a/venv/lib/python3.8/site-packages/pip/_internal/utils/__pycache__/pkg_resources.cpython-38.pyc b/venv/lib/python3.8/site-packages/pip/_internal/utils/__pycache__/pkg_resources.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..312b3873423d690a44fda622e5a60e20d6986504 GIT binary patch literal 1874 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 Vd#be*72lKQ>*h;l*F0%1{0le&{-*!{ literal 0 HcmV?d00001 diff --git a/venv/lib/python3.8/site-packages/pip/_internal/utils/__pycache__/subprocess.cpython-38.pyc b/venv/lib/python3.8/site-packages/pip/_internal/utils/__pycache__/subprocess.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..2b78de2a667016ed90e4d20c90c54cec9842d6ee GIT binary patch literal 5642 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 literal 0 HcmV?d00001 diff --git a/venv/lib/python3.8/site-packages/pip/_internal/utils/__pycache__/typing.cpython-38.pyc b/venv/lib/python3.8/site-packages/pip/_internal/utils/__pycache__/typing.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..ac0cacc3f7eb0c7de97580582ced80c8609ddc6e GIT binary patch literal 1489 zcmZWpL2u+V6izzRZWU=ca6};auz)H}CQAzyTdfdK(Pf1gt(NX#dzpITHxtvuj%;U! zAdZ~)C;X+ka^f#=;(N}t+ugM$Q9OSB-uJ%u*{>cx%o2=8pZulz{UrH^C-=u5Cg0-m zpW&sFt3;(&DW&cswWmh)UNgEHrHMIIW3~Thay3#1za&Qw{pJ!yq$-3?* zZ!&CbS1Yj~F37q-O2{Bwhq@Z(shE`Fl0t=rrSuh2iagUn$TD~#@e{`8wtaV_vS4a# z5S3grRL`(^#I*S(ltq>aJ^3+Nv z1N60>E0J+&UG}066<8h@yN{EpxU4!qK@q?zt)hj5;-xeZn0{-i3^BW9DOyJ!N=~y& zD>l~M25?bKFcOwIaqXr^Ivlv2yP<5Q}CbkvF$`X2b&3Y;AP$6uqI>)JY z4TkEMtjJ(95Zn2SJ-A~oR-JUw0BEx=2^a=GL&lvIyhUPAmstsH+W}2#*igf3k+maP zR-z(UHWEkMxedtuJRXz@i@7@<`A$qnHF}D)QB*H{b!*c|2P^R1(#9s7WtqT;Qu4W* zBRe@9i)j~h?Wb?|=jYciUeC{e{Nenk`O8BM!Hx*Hx-^l(~`{bnOwc<|3BFb0{#ZTMb zG0{(%WMrSl`Rr)CeK4OZTh8a(58qdEdob*e!Eg8vUk3o&9zGph_zi2|5Ahm*JRXhH H561rkNz>a% literal 0 HcmV?d00001 diff --git a/venv/lib/python3.8/site-packages/pip/_internal/utils/__pycache__/unpacking.cpython-38.pyc b/venv/lib/python3.8/site-packages/pip/_internal/utils/__pycache__/unpacking.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..d5298998334927aa969c56f4a14078348571f081 GIT binary patch literal 6127 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 I{h{{guP_dIKmY&$ literal 0 HcmV?d00001 diff --git a/venv/lib/python3.8/site-packages/pip/_internal/utils/__pycache__/urls.cpython-38.pyc b/venv/lib/python3.8/site-packages/pip/_internal/utils/__pycache__/urls.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..b1c869cbf429586d116e07f4199cfe011819a0a7 GIT binary patch literal 1559 zcmZ8hOK%%D5GJ`#OR|z04vZ8jx-E*rDqtyanxHm||<>eZJ^~EQD(ffL(L z=r-U?NTWYdcbUd?3HBzf(K_rmD1MGwjW=)`+HGMIl&Mq=`MNFW|JcH=D7%>Tt@hX}BgXA(;FUn3Svtg=P zXPC;)vB)Wts#Eqm4@Twi>rKA3sVRMMq`tX(Tuh4lcilSSoxM((l^Yc=)BYKw8>0bZ z`Nml~J{4Q{#@5#9Wn41}$#p87<*Eahs&+Xx%@d}3AoNs!zzSwxp+X>t#MsC325=u# zz_!!cT?M`ZMx#?aCyshkbdJyQ3~7HJP^`mwi6b_5?XsgpD*Q$0wu z0PZ-;*>`6OG_6BTW8D)8%9I>QGDNE8LCGtuG!)Y=|hs?!#SqaVP;y83#tF}{L z1TJ7JHbf}n+j~sRg)AHwK`h5KAoDys>N$*<2&7Yyanrn1Un0gd>|vf3Y!eJEB3B@9 z*|5acYeAa8nYumL)52DC6%>n7hO2l5%iAD-P)Z9Vn3zQ=C%`NKbdJ>>4KO+-$4Gmx z(ckzr{sY^R+F~9CGdv?R4=A1z`EUwgJ0tQ(>dy%OQirDz4M2WD!zo!qpj*)~jjqL7 zn%Aae>a9V%nK$*IHSuKqT5H}lWYHg+*rK%ZCOmbrbs(ywpYlZTe4OY?^2-)N<|+ZO z2`Ag4sbrlCo%{}#&*ZFSmU7Wg^MO#>AQOx-es6O^4i6{aaltp7&A9>?9;+mkEV16I zDoY`4s|8Q=AX7=1r}`K=y8SM4-PDw}7#L;H?&c3l0m@*6A4w^s3GLxjo6udM;IK=*2^49lPNrQyAMrwpy>4GMf>u{=dQ+@=Sv@YE{Fv9KLkZ~$xTV=1>g%~o` zhVX!1X=l=-4FAp)c(@yQE0TYQO>Z!Ugu6?|kPw`(l2+PT;!t z-k!2KV_4v^RO3&q8~P z&+&O^Z}SDd2<GT5)V7b+3tQgov?EKIqX8x50CclynGox!!+j}+^}f>p{E{tEpa(|ZZMblD}^Kd8%HO7Qmzag+31_K)(#nsJV9idqXYx)OxF9XHg1iErLI$x=u^6<1WnE}fp=>T%WEI=YQn8BI zMjBozV$RY`GvEPlg`JL2iUE|Y8^t3ihxN&xn~j;$$yDj)q3)ONeio&cDn4LQx>+vL z(#21ebp<)D&Gu#O>9-qFW)cXtXulPJ{Q+)K((Y7v48z_DAiaaBb><^Y*gzp+h_^%? zTBrBu0+sK=_!7S=FyX>&0<;Ak(#2+dyEGrR??p3C(NP%_M3f=rX&c%Rpfe zZ3PN$gEH1cm5WZ)jX))JKhz9VD82su5YhqTrHVYy6r{J#*q$)SJcv}Z7enY|mavN) znHJYo_IeV_*DNW3B*>aDN^Q`yIL;11%gpx>_g`2uQOkd*+^{d1sV#Q(BhDjRq~fDh z7H2?Jb{I+&N#vbn84kuogO@>AZRTzc|Ga*|$`K(~CRVR7E>sSs?s^Xa3;b}{(a}IO z*(0Tj1SfiNwg-jgN-UIta%2xeXK(l}H6^sH8W$foJVU$E1IKzombH;< zo`TGAnW@Y%`b% zHceb?Bm>gEof2slSQnJnC0m>ipNOx{eRYu}pm`@4-JE{Cj+%dR^4| z`!E=(7iw5VyZdmgfh4fA9;OT^nmGlW)i6Cwefo!GK7{f8G+VPaT|Rm=^&9;@6K_C) z6okVlHsTJ&($d2m3X%vF0fZ{+MIFQ)E7VkJH5tSO6kdoQMi5d0C4Q6@hMiCQP06!uED5rr>q0;1)LECK#znjYnX??5EHc0 zkwRh#H)W}O7tU|Zi}PF4oZp)ID97a$eAycfux3cijdBL6&oi6Z5MvBIY=mc;vW5p& zs9Ex-xEO1Zbq!AmMiNZMnOGQOuXWdd2-GNI@fAi#{Tv>1kg=nmGv~N;$*EJD%l?dC M_iO%w{}X@Vf7h9&Qvd(} literal 0 HcmV?d00001 diff --git a/venv/lib/python3.8/site-packages/pip/_internal/utils/__pycache__/wheel.cpython-38.pyc b/venv/lib/python3.8/site-packages/pip/_internal/utils/__pycache__/wheel.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..28130159eaf3434f9474020b6dcbf6865bc51a55 GIT binary patch literal 6384 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 r!0i=iS6M5AiXo(_vBM!w!POrZkO#Td-s0 str + return _appdirs.user_cache_dir(appname, appauthor=False) + + +def user_config_dir(appname, roaming=True): + # type: (str, bool) -> str + path = _appdirs.user_config_dir(appname, appauthor=False, roaming=roaming) + if _appdirs.system == "darwin" and not os.path.isdir(path): + path = os.path.expanduser('~/.config/') + if appname: + path = os.path.join(path, appname) + return path + + +# for the discussion regarding site_config_dir locations +# see +def site_config_dirs(appname): + # type: (str) -> List[str] + dirval = _appdirs.site_config_dir(appname, appauthor=False, multipath=True) + if _appdirs.system not in ["win32", "darwin"]: + # always look in /etc directly as well + return dirval.split(os.pathsep) + ['/etc'] + return [dirval] diff --git a/venv/lib/python3.8/site-packages/pip/_internal/utils/compat.py b/venv/lib/python3.8/site-packages/pip/_internal/utils/compat.py new file mode 100644 index 0000000..d939e21 --- /dev/null +++ b/venv/lib/python3.8/site-packages/pip/_internal/utils/compat.py @@ -0,0 +1,270 @@ +"""Stuff that differs in different Python versions and platform +distributions.""" + +# The following comment should be removed at some point in the future. +# mypy: disallow-untyped-defs=False + +from __future__ import absolute_import, division + +import codecs +import locale +import logging +import os +import shutil +import sys + +from pip._vendor.six import PY2, text_type + +from pip._internal.utils.typing import MYPY_CHECK_RUNNING + +if MYPY_CHECK_RUNNING: + from typing import Optional, Text, Tuple, Union + +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", + "get_path_uid", "stdlib_pkgs", "WINDOWS", "samefile", "get_terminal_size", +] + + +logger = logging.getLogger(__name__) + +if PY2: + 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 +else: + uses_pycache = True + from importlib.util import cache_from_source + + +if PY2: + # In Python 2.7, 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)) + # Python 2 gave us characters - convert to numeric bytes + raw_bytes = (ord(b) for b in raw_bytes) + return u"".join(map(u"\\x{:x}".format, raw_bytes)), err.end + codecs.register_error( + "backslashreplace_decode", + backslashreplace_decode_fn, + ) + backslashreplace_decode = "backslashreplace_decode" +else: + backslashreplace_decode = "backslashreplace" + + +def has_tls(): + # type: () -> bool + try: + import _ssl # noqa: F401 # ignore unused + return True + except ImportError: + pass + + from pip._vendor.urllib3.util import IS_PYOPENSSL + return IS_PYOPENSSL + + +def str_to_display(data, desc=None): + # type: (Union[bytes, Text], Optional[str]) -> Text + """ + For display or logging purposes, convert a bytes object (or text) to + text (e.g. unicode in Python 2) safe for output. + + :param desc: An optional phrase describing the input data, for use in + the log message if a warning is logged. Defaults to "Bytes object". + + This function should never error out and so can take a best effort + approach. It is okay to be lossy if needed since the return value is + just for display. + + 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. + """ + if isinstance(data, text_type): + return data + + # Otherwise, data is a bytes object (str in Python 2). + # 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: + if desc is None: + desc = 'Bytes object' + msg_format = '{} does not appear to be encoded as %s'.format(desc) + logger.warning(msg_format, 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 + + +def console_to_str(data): + # type: (bytes) -> Text + """Return a string, safe for output, of subprocess output. + """ + return str_to_display(data, desc='Subprocess output') + + +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( + "{} is a symlink; Will not return uid for symlinks".format( + path) + ) + return file_uid + + +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: + if sys.platform != "win32": + 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/venv/lib/python3.8/site-packages/pip/_internal/utils/compatibility_tags.py b/venv/lib/python3.8/site-packages/pip/_internal/utils/compatibility_tags.py new file mode 100644 index 0000000..47d04f0 --- /dev/null +++ b/venv/lib/python3.8/site-packages/pip/_internal/utils/compatibility_tags.py @@ -0,0 +1,169 @@ +"""Generate and work with PEP 425 Compatibility Tags. +""" + +from __future__ import absolute_import + +import logging +import re + +from pip._vendor.packaging.tags import ( + Tag, + compatible_tags, + cpython_tags, + generic_tags, + interpreter_name, + interpreter_version, + mac_platforms, +) + +from pip._internal.utils.typing import MYPY_CHECK_RUNNING + +if MYPY_CHECK_RUNNING: + from typing import List, Optional, Tuple + + from pip._vendor.packaging.tags import PythonVersion + +logger = logging.getLogger(__name__) + +_osx_arch_pat = re.compile(r'(.+)_(\d+)_(\d+)_(.+)') + + +def version_info_to_nodot(version_info): + # type: (Tuple[int, ...]) -> str + # Only use up to the first two numbers. + return ''.join(map(str, version_info[:2])) + + +def _mac_platforms(arch): + # type: (str) -> List[str] + match = _osx_arch_pat.match(arch) + if match: + name, major, minor, actual_arch = match.groups() + mac_version = (int(major), int(minor)) + arches = [ + # Since we have always only checked that the platform starts + # with "macosx", for backwards-compatibility we extract the + # actual prefix provided by the user in case they provided + # something like "macosxcustom_". It may be good to remove + # this as undocumented or deprecate it in the future. + '{}_{}'.format(name, arch[len('macosx_'):]) + for arch in mac_platforms(mac_version, actual_arch) + ] + else: + # arch pattern didn't match (?!) + arches = [arch] + return arches + + +def _custom_manylinux_platforms(arch): + # type: (str) -> List[str] + arches = [arch] + arch_prefix, arch_sep, arch_suffix = arch.partition('_') + if arch_prefix == 'manylinux2014': + # manylinux1/manylinux2010 wheels run on most manylinux2014 systems + # with the exception of wheels depending on ncurses. PEP 599 states + # manylinux1/manylinux2010 wheels should be considered + # manylinux2014 wheels: + # https://www.python.org/dev/peps/pep-0599/#backwards-compatibility-with-manylinux2010-wheels + if arch_suffix in {'i686', 'x86_64'}: + arches.append('manylinux2010' + arch_sep + arch_suffix) + arches.append('manylinux1' + arch_sep + arch_suffix) + 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.append('manylinux1' + arch_sep + arch_suffix) + return arches + + +def _get_custom_platforms(arch): + # type: (str) -> List[str] + arch_prefix, arch_sep, arch_suffix = arch.partition('_') + if arch.startswith('macosx'): + arches = _mac_platforms(arch) + elif arch_prefix in ['manylinux2014', 'manylinux2010']: + arches = _custom_manylinux_platforms(arch) + else: + arches = [arch] + return arches + + +def _get_python_version(version): + # type: (str) -> PythonVersion + if len(version) > 1: + return int(version[0]), int(version[1:]) + else: + return (int(version[0]),) + + +def _get_custom_interpreter(implementation=None, version=None): + # type: (Optional[str], Optional[str]) -> str + if implementation is None: + implementation = interpreter_name() + if version is None: + version = interpreter_version() + return "{}{}".format(implementation, version) + + +def get_supported( + version=None, # type: Optional[str] + platform=None, # type: Optional[str] + impl=None, # type: Optional[str] + abi=None # type: Optional[str] +): + # type: (...) -> List[Tag] + """Return a list of supported tags for each version specified in + `versions`. + + :param version: a string version, of the form "33" or "32", + or None. The 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 = [] # type: List[Tag] + + python_version = None # type: Optional[PythonVersion] + if version is not None: + python_version = _get_python_version(version) + + interpreter = _get_custom_interpreter(impl, version) + + abis = None # type: Optional[List[str]] + if abi is not None: + abis = [abi] + + platforms = None # type: Optional[List[str]] + if platform is not None: + platforms = _get_custom_platforms(platform) + + is_cpython = (impl or interpreter_name()) == "cp" + if is_cpython: + supported.extend( + cpython_tags( + python_version=python_version, + abis=abis, + platforms=platforms, + ) + ) + else: + supported.extend( + generic_tags( + interpreter=interpreter, + abis=abis, + platforms=platforms, + ) + ) + supported.extend( + compatible_tags( + python_version=python_version, + interpreter=interpreter, + platforms=platforms, + ) + ) + + return supported diff --git a/venv/lib/python3.8/site-packages/pip/_internal/utils/deprecation.py b/venv/lib/python3.8/site-packages/pip/_internal/utils/deprecation.py new file mode 100644 index 0000000..2f20cfd --- /dev/null +++ b/venv/lib/python3.8/site-packages/pip/_internal/utils/deprecation.py @@ -0,0 +1,104 @@ +""" +A module that implements tooling to enable easy warnings about deprecations. +""" + +# The following comment should be removed at some point in the future. +# mypy: disallow-untyped-defs=False + +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 eagerly formatted as we want it to get logged as if someone + # typed this entire message out. + sentences = [ + (reason, DEPRECATION_MSG_PREFIX + "{}"), + (gone_in, "pip {} will remove support for this functionality."), + (replacement, "A possible replacement is {}."), + (issue, ( + "You can find discussion regarding this at " + "https://github.com/pypa/pip/issues/{}." + )), + ] + message = " ".join( + template.format(val) for val, template in sentences if val is not None + ) + + # 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/venv/lib/python3.8/site-packages/pip/_internal/utils/direct_url_helpers.py b/venv/lib/python3.8/site-packages/pip/_internal/utils/direct_url_helpers.py new file mode 100644 index 0000000..f1fe209 --- /dev/null +++ b/venv/lib/python3.8/site-packages/pip/_internal/utils/direct_url_helpers.py @@ -0,0 +1,130 @@ +import logging + +from pip._internal.models.direct_url import ( + DIRECT_URL_METADATA_NAME, + ArchiveInfo, + DirectUrl, + DirectUrlValidationError, + DirInfo, + VcsInfo, +) +from pip._internal.utils.typing import MYPY_CHECK_RUNNING +from pip._internal.vcs import vcs + +try: + from json import JSONDecodeError +except ImportError: + # PY2 + JSONDecodeError = ValueError # type: ignore + +if MYPY_CHECK_RUNNING: + from typing import Optional + + from pip._internal.models.link import Link + + from pip._vendor.pkg_resources import Distribution + +logger = logging.getLogger(__name__) + + +def direct_url_as_pep440_direct_reference(direct_url, name): + # type: (DirectUrl, str) -> str + """Convert a DirectUrl to a pip requirement string.""" + direct_url.validate() # if invalid, this is a pip bug + requirement = name + " @ " + fragments = [] + if isinstance(direct_url.info, VcsInfo): + requirement += "{}+{}@{}".format( + direct_url.info.vcs, direct_url.url, direct_url.info.commit_id + ) + elif isinstance(direct_url.info, ArchiveInfo): + requirement += direct_url.url + if direct_url.info.hash: + fragments.append(direct_url.info.hash) + else: + assert isinstance(direct_url.info, DirInfo) + # pip should never reach this point for editables, since + # pip freeze inspects the editable project location to produce + # the requirement string + assert not direct_url.info.editable + requirement += direct_url.url + if direct_url.subdirectory: + fragments.append("subdirectory=" + direct_url.subdirectory) + if fragments: + requirement += "#" + "&".join(fragments) + return requirement + + +def direct_url_from_link(link, source_dir=None, link_is_in_wheel_cache=False): + # type: (Link, Optional[str], bool) -> DirectUrl + if link.is_vcs: + vcs_backend = vcs.get_backend_for_scheme(link.scheme) + assert vcs_backend + url, requested_revision, _ = ( + vcs_backend.get_url_rev_and_auth(link.url_without_fragment) + ) + # For VCS links, we need to find out and add commit_id. + if link_is_in_wheel_cache: + # If the requested VCS link corresponds to a cached + # wheel, it means the requested revision was an + # immutable commit hash, otherwise it would not have + # been cached. In that case we don't have a source_dir + # with the VCS checkout. + assert requested_revision + commit_id = requested_revision + else: + # If the wheel was not in cache, it means we have + # had to checkout from VCS to build and we have a source_dir + # which we can inspect to find out the commit id. + assert source_dir + commit_id = vcs_backend.get_revision(source_dir) + return DirectUrl( + url=url, + info=VcsInfo( + vcs=vcs_backend.name, + commit_id=commit_id, + requested_revision=requested_revision, + ), + subdirectory=link.subdirectory_fragment, + ) + elif link.is_existing_dir(): + return DirectUrl( + url=link.url_without_fragment, + info=DirInfo(), + subdirectory=link.subdirectory_fragment, + ) + else: + hash = None + hash_name = link.hash_name + if hash_name: + hash = "{}={}".format(hash_name, link.hash) + return DirectUrl( + url=link.url_without_fragment, + info=ArchiveInfo(hash=hash), + subdirectory=link.subdirectory_fragment, + ) + + +def dist_get_direct_url(dist): + # type: (Distribution) -> Optional[DirectUrl] + """Obtain a DirectUrl from a pkg_resource.Distribution. + + Returns None if the distribution has no `direct_url.json` metadata, + or if `direct_url.json` is invalid. + """ + if not dist.has_metadata(DIRECT_URL_METADATA_NAME): + return None + try: + return DirectUrl.from_json(dist.get_metadata(DIRECT_URL_METADATA_NAME)) + except ( + DirectUrlValidationError, + JSONDecodeError, + UnicodeDecodeError + ) as e: + logger.warning( + "Error parsing %s for %s: %s", + DIRECT_URL_METADATA_NAME, + dist.project_name, + e, + ) + return None diff --git a/venv/lib/python3.8/site-packages/pip/_internal/utils/distutils_args.py b/venv/lib/python3.8/site-packages/pip/_internal/utils/distutils_args.py new file mode 100644 index 0000000..e38e402 --- /dev/null +++ b/venv/lib/python3.8/site-packages/pip/_internal/utils/distutils_args.py @@ -0,0 +1,48 @@ +from distutils.errors import DistutilsArgError +from distutils.fancy_getopt import FancyGetopt + +from pip._internal.utils.typing import MYPY_CHECK_RUNNING + +if MYPY_CHECK_RUNNING: + from typing import Dict, List + + +_options = [ + ("exec-prefix=", None, ""), + ("home=", None, ""), + ("install-base=", None, ""), + ("install-data=", None, ""), + ("install-headers=", None, ""), + ("install-lib=", None, ""), + ("install-platlib=", None, ""), + ("install-purelib=", None, ""), + ("install-scripts=", None, ""), + ("prefix=", None, ""), + ("root=", None, ""), + ("user", None, ""), +] + + +# typeshed doesn't permit Tuple[str, None, str], see python/typeshed#3469. +_distutils_getopt = FancyGetopt(_options) # type: ignore + + +def parse_distutils_args(args): + # type: (List[str]) -> Dict[str, str] + """Parse provided arguments, returning an object that has the + matched arguments. + + Any unknown arguments are ignored. + """ + result = {} + for arg in args: + try: + _, match = _distutils_getopt.getopt(args=[arg]) + except DistutilsArgError: + # We don't care about any other options, which here may be + # considered unrecognized since our option list is not + # exhaustive. + pass + else: + result.update(match.__dict__) + return result diff --git a/venv/lib/python3.8/site-packages/pip/_internal/utils/encoding.py b/venv/lib/python3.8/site-packages/pip/_internal/utils/encoding.py new file mode 100644 index 0000000..ab4d4b9 --- /dev/null +++ b/venv/lib/python3.8/site-packages/pip/_internal/utils/encoding.py @@ -0,0 +1,42 @@ +# The following comment should be removed at some point in the future. +# mypy: strict-optional=False + +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/venv/lib/python3.8/site-packages/pip/_internal/utils/entrypoints.py b/venv/lib/python3.8/site-packages/pip/_internal/utils/entrypoints.py new file mode 100644 index 0000000..befd01c --- /dev/null +++ b/venv/lib/python3.8/site-packages/pip/_internal/utils/entrypoints.py @@ -0,0 +1,31 @@ +import sys + +from pip._internal.cli.main import main +from pip._internal.utils.typing import MYPY_CHECK_RUNNING + +if MYPY_CHECK_RUNNING: + from typing import Optional, List + + +def _wrapper(args=None): + # type: (Optional[List[str]]) -> int + """Central wrapper for all old entrypoints. + + Historically pip has had several entrypoints defined. Because of issues + arising from PATH, sys.path, multiple Pythons, their interactions, and most + of them having a pip installed, users suffer every time an entrypoint gets + moved. + + To alleviate this pain, and provide a mechanism for warning users and + directing them to an appropriate place for help, we now define all of + our old entrypoints as wrappers for the current one. + """ + sys.stderr.write( + "WARNING: pip is being invoked by an old script wrapper. This will " + "fail in a future version of pip.\n" + "Please see https://github.com/pypa/pip/issues/5599 for advice on " + "fixing the underlying issue.\n" + "To avoid this problem you can invoke Python with '-m pip' instead of " + "running pip directly.\n" + ) + return main(args) diff --git a/venv/lib/python3.8/site-packages/pip/_internal/utils/filesystem.py b/venv/lib/python3.8/site-packages/pip/_internal/utils/filesystem.py new file mode 100644 index 0000000..437a7fd --- /dev/null +++ b/venv/lib/python3.8/site-packages/pip/_internal/utils/filesystem.py @@ -0,0 +1,222 @@ +import errno +import fnmatch +import os +import os.path +import random +import shutil +import stat +import sys +from contextlib import contextmanager +from tempfile import NamedTemporaryFile + +# 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._internal.utils.compat import get_path_uid +from pip._internal.utils.misc import format_size +from pip._internal.utils.typing import MYPY_CHECK_RUNNING, cast + +if MYPY_CHECK_RUNNING: + from typing import Any, BinaryIO, Iterator, List, Union + + class NamedTemporaryFileResult(BinaryIO): + @property + def file(self): + # type: () -> BinaryIO + pass + + +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 sys.platform == "win32" or not hasattr(os, "geteuid"): + return True + + assert os.path.isabs(path) + + 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 + + +def copy2_fixed(src, dest): + # type: (str, str) -> None + """Wrap shutil.copy2() but map errors copying socket files to + SpecialFileError as expected. + + See also https://bugs.python.org/issue37700. + """ + try: + shutil.copy2(src, dest) + except (OSError, IOError): + for f in [src, dest]: + try: + is_socket_file = is_socket(f) + except OSError: + # An error has already occurred. Another error here is not + # a problem and we can ignore it. + pass + else: + if is_socket_file: + raise shutil.SpecialFileError( + "`{f}` is a socket".format(**locals())) + + raise + + +def is_socket(path): + # type: (str) -> bool + return stat.S_ISSOCK(os.lstat(path).st_mode) + + +@contextmanager +def adjacent_tmp_file(path, **kwargs): + # type: (str, **Any) -> Iterator[NamedTemporaryFileResult] + """Return a file-like object pointing to a tmp file next to path. + + The file is created securely and is ensured to be written to disk + after the context reaches its end. + + kwargs will be passed to tempfile.NamedTemporaryFile to control + the way the temporary file will be opened. + """ + with NamedTemporaryFile( + delete=False, + dir=os.path.dirname(path), + prefix=os.path.basename(path), + suffix='.tmp', + **kwargs + ) as f: + result = cast('NamedTemporaryFileResult', f) + try: + yield result + finally: + result.file.flush() + os.fsync(result.file.fileno()) + + +_replace_retry = retry(stop_max_delay=1000, wait_fixed=250) + +if PY2: + @_replace_retry + def replace(src, dest): + # type: (str, str) -> None + try: + os.rename(src, dest) + except OSError: + os.remove(dest) + os.rename(src, dest) + +else: + replace = _replace_retry(os.replace) + + +# test_writable_dir and _test_writable_dir_win are copied from Flit, +# with the author's agreement to also place them under pip's license. +def test_writable_dir(path): + # type: (str) -> bool + """Check if a directory is writable. + + Uses os.access() on POSIX, tries creating files on Windows. + """ + # If the directory doesn't exist, find the closest parent that does. + while not os.path.isdir(path): + parent = os.path.dirname(path) + if parent == path: + break # Should never get here, but infinite loops are bad + path = parent + + if os.name == 'posix': + return os.access(path, os.W_OK) + + return _test_writable_dir_win(path) + + +def _test_writable_dir_win(path): + # type: (str) -> bool + # os.access doesn't work on Windows: http://bugs.python.org/issue2528 + # and we can't use tempfile: http://bugs.python.org/issue22107 + basename = 'accesstest_deleteme_fishfingers_custard_' + alphabet = 'abcdefghijklmnopqrstuvwxyz0123456789' + for i in range(10): + name = basename + ''.join(random.choice(alphabet) for _ in range(6)) + file = os.path.join(path, name) + try: + fd = os.open(file, os.O_RDWR | os.O_CREAT | os.O_EXCL) + # Python 2 doesn't support FileExistsError and PermissionError. + except OSError as e: + # exception FileExistsError + if e.errno == errno.EEXIST: + continue + # exception PermissionError + if e.errno == errno.EPERM or e.errno == errno.EACCES: + # This could be because there's a directory with the same name. + # But it's highly unlikely there's a directory called that, + # so we'll assume it's because the parent dir is not writable. + return False + raise + else: + os.close(fd) + os.unlink(file) + return True + + # This should never be reached + raise EnvironmentError( + 'Unexpected condition testing for writable directory' + ) + + +def find_files(path, pattern): + # type: (str, str) -> List[str] + """Returns a list of absolute paths of files beneath path, recursively, + with filenames which match the UNIX-style shell glob pattern.""" + result = [] # type: List[str] + for root, dirs, files in os.walk(path): + matches = fnmatch.filter(files, pattern) + result.extend(os.path.join(root, f) for f in matches) + return result + + +def file_size(path): + # type: (str) -> Union[int, float] + # If it's a symlink, return 0. + if os.path.islink(path): + return 0 + return os.path.getsize(path) + + +def format_file_size(path): + # type: (str) -> str + return format_size(file_size(path)) + + +def directory_size(path): + # type: (str) -> Union[int, float] + size = 0.0 + for root, _dirs, files in os.walk(path): + for filename in files: + file_path = os.path.join(root, filename) + size += file_size(file_path) + return size + + +def format_directory_size(path): + # type: (str) -> str + return format_size(directory_size(path)) diff --git a/venv/lib/python3.8/site-packages/pip/_internal/utils/filetypes.py b/venv/lib/python3.8/site-packages/pip/_internal/utils/filetypes.py new file mode 100644 index 0000000..daa0ca7 --- /dev/null +++ b/venv/lib/python3.8/site-packages/pip/_internal/utils/filetypes.py @@ -0,0 +1,16 @@ +"""Filetype information. +""" +from pip._internal.utils.typing import MYPY_CHECK_RUNNING + +if MYPY_CHECK_RUNNING: + from typing import Tuple + +WHEEL_EXTENSION = '.whl' +BZ2_EXTENSIONS = ('.tar.bz2', '.tbz') # type: Tuple[str, ...] +XZ_EXTENSIONS = ('.tar.xz', '.txz', '.tlz', + '.tar.lz', '.tar.lzma') # type: Tuple[str, ...] +ZIP_EXTENSIONS = ('.zip', WHEEL_EXTENSION) # type: Tuple[str, ...] +TAR_EXTENSIONS = ('.tar.gz', '.tgz', '.tar') # type: Tuple[str, ...] +ARCHIVE_EXTENSIONS = ( + ZIP_EXTENSIONS + BZ2_EXTENSIONS + TAR_EXTENSIONS + XZ_EXTENSIONS +) diff --git a/venv/lib/python3.8/site-packages/pip/_internal/utils/glibc.py b/venv/lib/python3.8/site-packages/pip/_internal/utils/glibc.py new file mode 100644 index 0000000..3610424 --- /dev/null +++ b/venv/lib/python3.8/site-packages/pip/_internal/utils/glibc.py @@ -0,0 +1,98 @@ +# The following comment should be removed at some point in the future. +# mypy: strict-optional=False + +from __future__ import absolute_import + +import os +import sys + +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." + return glibc_version_string_confstr() or glibc_version_string_ctypes() + + +def glibc_version_string_confstr(): + # type: () -> Optional[str] + "Primary implementation of glibc_version_string using os.confstr." + # os.confstr is quite a bit faster than ctypes.DLL. It's also less likely + # to be broken or missing. This strategy is used in the standard library + # platform module: + # https://github.com/python/cpython/blob/fcf1d003bf4f0100c9d0921ff3d70e1127ca1b71/Lib/platform.py#L175-L183 + if sys.platform == "win32": + return None + try: + # os.confstr("CS_GNU_LIBC_VERSION") returns a string like "glibc 2.17": + _, version = os.confstr("CS_GNU_LIBC_VERSION").split() + except (AttributeError, OSError, ValueError): + # os.confstr() or CS_GNU_LIBC_VERSION not available (or a bad value)... + return None + return version + + +def glibc_version_string_ctypes(): + # type: () -> Optional[str] + "Fallback implementation of glibc_version_string using ctypes." + + try: + import ctypes + except ImportError: + return None + + # 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 + + +# 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/venv/lib/python3.8/site-packages/pip/_internal/utils/hashes.py b/venv/lib/python3.8/site-packages/pip/_internal/utils/hashes.py new file mode 100644 index 0000000..396cf82 --- /dev/null +++ b/venv/lib/python3.8/site-packages/pip/_internal/utils/hashes.py @@ -0,0 +1,133 @@ +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 + + @property + def digest_count(self): + # type: () -> int + return sum(len(digests) for digests in self._allowed.values()) + + def is_hash_allowed( + self, + hash_name, # type: str + hex_digest, # type: str + ): + # type: (...) -> bool + """Return whether the given hex digest is allowed.""" + return hex_digest in self._allowed.get(hash_name, []) + + 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: {}'.format(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/venv/lib/python3.8/site-packages/pip/_internal/utils/inject_securetransport.py b/venv/lib/python3.8/site-packages/pip/_internal/utils/inject_securetransport.py new file mode 100644 index 0000000..5b93b1d --- /dev/null +++ b/venv/lib/python3.8/site-packages/pip/_internal/utils/inject_securetransport.py @@ -0,0 +1,36 @@ +"""A helper module that injects SecureTransport, on import. + +The import should be done as early as possible, to ensure all requests and +sessions (or whatever) are created after injecting SecureTransport. + +Note that we only do the injection on macOS, when the linked OpenSSL is too +old to handle TLSv1.2. +""" + +import sys + + +def inject_securetransport(): + # type: () -> None + # Only relevant on macOS + if sys.platform != "darwin": + return + + try: + import ssl + except ImportError: + return + + # Checks for OpenSSL 1.0.1 + if ssl.OPENSSL_VERSION_NUMBER >= 0x1000100f: + return + + try: + from pip._vendor.urllib3.contrib import securetransport + except (ImportError, OSError): + return + + securetransport.inject_into_urllib3() + + +inject_securetransport() diff --git a/venv/lib/python3.8/site-packages/pip/_internal/utils/logging.py b/venv/lib/python3.8/site-packages/pip/_internal/utils/logging.py new file mode 100644 index 0000000..9a017cf --- /dev/null +++ b/venv/lib/python3.8/site-packages/pip/_internal/utils/logging.py @@ -0,0 +1,399 @@ +# The following comment should be removed at some point in the future. +# mypy: disallow-untyped-defs=False + +from __future__ import absolute_import + +import contextlib +import errno +import logging +import logging.handlers +import os +import sys +from logging import Filter, getLogger + +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 + +try: + import threading +except ImportError: + import dummy_threading as threading # type: ignore + + +try: + # Use "import as" and set colorama in the else clause to avoid mypy + # errors and get the following correct revealed type for colorama: + # `Union[_importlib_modulespec.ModuleType, None]` + # Otherwise, we get an error like the following in the except block: + # > Incompatible types in assignment (expression has type "None", + # variable has type Module) + # TODO: eliminate the need to use "import as" once mypy addresses some + # of its issues with conditional imports. Here is an umbrella issue: + # https://github.com/python/mypy/issues/1297 + from pip._vendor import colorama as _colorama +# Lots of different errors can come from this, including SystemError and +# ImportError. +except Exception: + colorama = None +else: + # Import Fore explicitly rather than accessing below as colorama.Fore + # to avoid the following error running mypy: + # > Module has no attribute "Fore" + # TODO: eliminate the need to import Fore once mypy addresses some of its + # issues with conditional imports. This particular case could be an + # instance of the following issue (but also see the umbrella issue above): + # https://github.com/python/mypy/issues/3500 + from pip._vendor.colorama import Fore + + colorama = _colorama + + +_log_state = threading.local() +subprocess_logger = getLogger('pip.subprocessor') + + +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. + """ + # For thread-safety + _log_state.indentation = get_indentation() + _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 message + lines 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: + # TODO: Use Formatter.default_time_format after dropping PY2. + t = self.formatTime(record, "%Y-%m-%dT%H:%M:%S") + prefix = '{t},{record.msecs:03.0f} '.format(**locals()) + 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(Fore.RED)), + (logging.WARNING, _color_wrap(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/venv/lib/python3.8/site-packages/pip/_internal/utils/misc.py b/venv/lib/python3.8/site-packages/pip/_internal/utils/misc.py new file mode 100644 index 0000000..0903182 --- /dev/null +++ b/venv/lib/python3.8/site-packages/pip/_internal/utils/misc.py @@ -0,0 +1,913 @@ +# The following comment should be removed at some point in the future. +# mypy: strict-optional=False +# mypy: disallow-untyped-defs=False + +from __future__ import absolute_import + +import contextlib +import errno +import getpass +import hashlib +import io +import logging +import os +import posixpath +import shutil +import stat +import sys +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, text_type +from pip._vendor.six.moves import input, map, zip_longest +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 import __version__ +from pip._internal.exceptions import CommandError +from pip._internal.locations import ( + get_major_minor_version, + site_packages, + user_site, +) +from pip._internal.utils.compat import ( + WINDOWS, + expanduser, + stdlib_pkgs, + str_to_display, +) +from pip._internal.utils.typing import MYPY_CHECK_RUNNING, cast +from pip._internal.utils.virtualenv import ( + running_under_virtualenv, + virtualenv_no_global, +) + +if PY2: + from io import BytesIO as StringIO +else: + from io import StringIO + +if MYPY_CHECK_RUNNING: + from typing import ( + Any, AnyStr, Container, Iterable, Iterator, List, Optional, Text, + Tuple, Union, + ) + from pip._vendor.pkg_resources import Distribution + + VersionInfo = Tuple[int, int, int] + + +__all__ = ['rmtree', 'display_path', 'backup_dir', + 'ask', 'splitext', + 'format_size', 'is_installable_dir', + 'normalize_path', + 'renames', 'get_prog', + 'captured_stdout', 'ensure_dir', + 'get_installed_version', 'remove_auth_from_url'] + + +logger = logging.getLogger(__name__) + + +def get_pip_version(): + # type: () -> str + pip_pkg_dir = os.path.join(os.path.dirname(__file__), "..", "..") + pip_pkg_dir = os.path.abspath(pip_pkg_dir) + + return ( + 'pip {} from {} (python {})'.format( + __version__, pip_pkg_dir, get_major_minor_version(), + ) + ) + + +def normalize_version_info(py_version_info): + # type: (Tuple[int, ...]) -> Tuple[int, int, int] + """ + Convert a tuple of ints representing a Python version to one of length + three. + + :param py_version_info: a tuple of ints representing a Python version, + or None to specify no version. The tuple can have any length. + + :return: a tuple of length three if `py_version_info` is non-None. + Otherwise, return `py_version_info` unchanged (i.e. None). + """ + if len(py_version_info) < 3: + py_version_info += (3 - len(py_version_info)) * (0,) + elif len(py_version_info) > 3: + py_version_info = py_version_info[:3] + + return cast('VersionInfo', py_version_info) + + +def ensure_dir(path): + # type: (AnyStr) -> None + """os.path.makedirs without EEXIST.""" + try: + os.makedirs(path) + except OSError as e: + # Windows can raise spurious ENOTEMPTY errors. See #6426. + if e.errno != errno.EEXIST and e.errno != errno.ENOTEMPTY: + raise + + +def get_prog(): + # type: () -> str + try: + prog = os.path.basename(sys.argv[0]) + if prog in ('__main__.py', '-c'): + return "{} -m pip".format(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.""" + try: + has_attr_readonly = not (os.stat(path).st_mode & stat.S_IWRITE) + except (IOError, OSError): + # it's equivalent to os.path.exists + return + + if has_attr_readonly: + # convert to read/write + os.chmod(path, stat.S_IWRITE) + # use the original function to repeat the operation + func(path) + return + else: + raise + + +def path_to_display(path): + # type: (Optional[Union[str, Text]]) -> Optional[Text] + """ + Convert a bytes (or text) path to text (unicode in Python 2) for display + and logging purposes. + + This function should never error out. Also, this function is mainly needed + for Python 2 since in Python 3 str paths are already text. + """ + if path is None: + return None + if isinstance(path, text_type): + return path + # Otherwise, path is a bytes object (str in Python 2). + try: + display_path = path.decode(sys.getfilesystemencoding(), 'strict') + except UnicodeDecodeError: + # Include the full bytes to make troubleshooting easier, even though + # it may not be very human readable. + if PY2: + # Convert the bytes to a readable str representation using + # repr(), and then convert the str to unicode. + # Also, we add the prefix "b" to the repr() return value both + # to make the Python 2 output look like the Python 3 output, and + # to signal to the user that this is a bytes representation. + display_path = str_to_display('b{!r}'.format(path)) + else: + # Silence the "F821 undefined name 'ascii'" flake8 error since + # in Python 3 ascii() is a built-in. + display_path = ascii(path) # noqa: F821 + + return display_path + + +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 _check_no_input(message): + # type: (str) -> None + """Raise an error if no input is allowed.""" + if os.environ.get('PIP_NO_INPUT'): + raise Exception( + 'No input was expected ($PIP_NO_INPUT set); question: {}'.format( + message) + ) + + +def ask(message, options): + # type: (str, Iterable[str]) -> str + """Ask the message interactively, with the given possible responses""" + while 1: + _check_no_input(message) + response = input(message) + response = response.strip().lower() + if response not in options: + print( + 'Your response ({!r}) was not one of the expected responses: ' + '{}'.format(response, ', '.join(options)) + ) + else: + return response + + +def ask_input(message): + # type: (str) -> str + """Ask for input interactively.""" + _check_no_input(message) + return input(message) + + +def ask_password(message): + # type: (str) -> str + """Ask for a password interactively.""" + _check_no_input(message) + return getpass.getpass(message) + + +def format_size(bytes): + # type: (float) -> str + if bytes > 1000 * 1000: + return '{:.1f} MB'.format(bytes / 1000.0 / 1000) + elif bytes > 10 * 1000: + return '{} kB'.format(int(bytes / 1000)) + elif bytes > 1000: + return '{:.1f} kB'.format(bytes / 1000.0) + else: + return '{} bytes'.format(int(bytes)) + + +def tabulate(rows): + # type: (Iterable[Iterable[Any]]) -> Tuple[List[str], List[int]] + """Return a list of formatted rows and a list of column sizes. + + For example:: + + >>> tabulate([['foobar', 2000], [0xdeadbeef]]) + (['foobar 2000', '3735928559'], [10, 4]) + """ + rows = [tuple(map(str, row)) for row in rows] + sizes = [max(map(len, col)) for col in zip_longest(*rows, fillvalue='')] + table = [" ".join(map(str.ljust, row, sizes)).rstrip() for row in rows] + return table, sizes + + +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 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 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." + + Caution: this function assumes the head of path has been normalized + with normalize_path. + """ + if not running_under_virtualenv(): + return True + return 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. + """ + return dist_location(dist).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 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, # type: bool + skip=stdlib_pkgs, # type: Container[str] + include_editables=True, # type: bool + editables_only=False, # type: bool + user_only=False, # type: bool + paths=None # type: Optional[List[str]] +): + # type: (...) -> 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 ``paths`` is set, only report the distributions present at the + specified list of locations. + """ + if paths: + working_set = pkg_resources.WorkingSet(paths) + else: + working_set = pkg_resources.working_set + + if local_only: + local_test = dist_is_local + else: + def local_test(d): + return True + + if include_editables: + def editable_test(d): + return True + else: + def editable_test(d): + return not dist_is_editable(d) + + if editables_only: + def editables_only_test(d): + return dist_is_editable(d) + else: + def editables_only_test(d): + return True + + if user_only: + user_test = dist_in_usersite + else: + def user_test(d): + return True + + return [d for d in working_set + if local_test(d) and + d.key not in skip and + editable_test(d) and + editables_only_test(d) and + user_test(d) + ] + + +def egg_link_path(dist): + # 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(): + sites.append(site_packages) + if not virtualenv_no_global() and 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. + + The returned location is normalized (in particular, with symlinks removed). + """ + egg_link = egg_link_path(dist) + if egg_link: + return normalize_path(egg_link) + return normalize_path(dist.location) + + +def write_output(msg, *args): + # type: (str, str) -> None + logger.info(msg, *args) + + +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 build_netloc(host, port): + # type: (str, Optional[int]) -> str + """ + Build a netloc from a host-port pair + """ + if port is None: + return host + if ':' in host: + # Only wrap host with square brackets when it is IPv6 + host = '[{}]'.format(host) + return '{}:{}'.format(host, port) + + +def build_url_from_netloc(netloc, scheme='https'): + # type: (str, str) -> str + """ + Build a full URL from a netloc. + """ + if netloc.count(':') >= 2 and '@' not in netloc and '[' not in netloc: + # It must be a bare IPv6 address, so wrap it with brackets. + netloc = '[{}]'.format(netloc) + return '{}://{}'.format(scheme, netloc) + + +def parse_netloc(netloc): + # type: (str) -> Tuple[str, Optional[int]] + """ + Return the host-port pair from a netloc. + """ + url = build_url_from_netloc(netloc) + parsed = urllib_parse.urlparse(url) + return parsed.hostname, parsed.port + + +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 sensitive data in a netloc with "****", if it exists. + + For example: + - "user:pass@example.com" returns "user:****@example.com" + - "accesstoken@example.com" returns "****@example.com" + """ + netloc, (user, password) = split_auth_from_netloc(netloc) + if user is None: + return netloc + if password is None: + user = '****' + password = '' + else: + user = urllib_parse.quote(user) + password = ':****' + return '{user}{password}@{netloc}'.format(user=user, + password=password, + netloc=netloc) + + +def _transform_url(url, transform_netloc): + """Transform and replace netloc in a url. + + transform_netloc is a function taking the netloc and returning a + tuple. The first element of this tuple is the new netloc. The + entire tuple is returned. + + Returns a tuple containing the transformed url as item 0 and the + original tuple returned by transform_netloc as item 1. + """ + purl = urllib_parse.urlsplit(url) + netloc_tuple = transform_netloc(purl.netloc) + # stripped url + url_pieces = ( + purl.scheme, netloc_tuple[0], purl.path, purl.query, purl.fragment + ) + surl = urllib_parse.urlunsplit(url_pieces) + return surl, netloc_tuple + + +def _get_netloc(netloc): + return split_auth_from_netloc(netloc) + + +def _redact_netloc(netloc): + return (redact_netloc(netloc),) + + +def split_auth_netloc_from_url(url): + # type: (str) -> Tuple[str, str, Tuple[str, str]] + """ + Parse a url into separate netloc, auth, and url with no auth. + + Returns: (url_without_auth, netloc, (username, password)) + """ + url_without_auth, (netloc, auth) = _transform_url(url, _get_netloc) + return url_without_auth, netloc, auth + + +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)[0] + + +def redact_auth_from_url(url): + # type: (str) -> str + """Replace the password in a given url with ****.""" + return _transform_url(url, _redact_netloc)[0] + + +class HiddenText(object): + def __init__( + self, + secret, # type: str + redacted, # type: str + ): + # type: (...) -> None + self.secret = secret + self.redacted = redacted + + def __repr__(self): + # type: (...) -> str + return ''.format(str(self)) + + def __str__(self): + # type: (...) -> str + return self.redacted + + # This is useful for testing. + def __eq__(self, other): + # type: (Any) -> bool + if type(self) != type(other): + return False + + # The string being used for redaction doesn't also have to match, + # just the raw, original string. + return (self.secret == other.secret) + + # We need to provide an explicit __ne__ implementation for Python 2. + # TODO: remove this when we drop PY2 support. + def __ne__(self, other): + # type: (Any) -> bool + return not self == other + + +def hide_value(value): + # type: (str) -> HiddenText + return HiddenText(value, redacted='****') + + +def hide_url(url): + # type: (str) -> HiddenText + redacted = redact_auth_from_url(url) + return HiddenText(url, redacted=redacted) + + +def protect_pip_from_modification_on_windows(modifying_pip): + # type: (bool) -> None + """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)) + ) + + +def is_console_interactive(): + # type: () -> bool + """Is this console interactive? + """ + return sys.stdin is not None and sys.stdin.isatty() + + +def hash_file(path, blocksize=1 << 20): + # type: (str, int) -> Tuple[Any, int] + """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) + return h, length + + +def is_wheel_installed(): + """ + Return whether the wheel package is installed. + """ + try: + import wheel # noqa: F401 + except ImportError: + return False + + return True + + +def pairwise(iterable): + # type: (Iterable[Any]) -> Iterator[Tuple[Any, Any]] + """ + Return paired elements. + + For example: + s -> (s0, s1), (s2, s3), (s4, s5), ... + """ + iterable = iter(iterable) + return zip_longest(iterable, iterable) diff --git a/venv/lib/python3.8/site-packages/pip/_internal/utils/models.py b/venv/lib/python3.8/site-packages/pip/_internal/utils/models.py new file mode 100644 index 0000000..29e1441 --- /dev/null +++ b/venv/lib/python3.8/site-packages/pip/_internal/utils/models.py @@ -0,0 +1,42 @@ +"""Utilities for defining models +""" +# The following comment should be removed at some point in the future. +# mypy: disallow-untyped-defs=False + +import operator + + +class KeyBasedCompareMixin(object): + """Provides comparison 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/venv/lib/python3.8/site-packages/pip/_internal/utils/packaging.py b/venv/lib/python3.8/site-packages/pip/_internal/utils/packaging.py new file mode 100644 index 0000000..68aa86e --- /dev/null +++ b/venv/lib/python3.8/site-packages/pip/_internal/utils/packaging.py @@ -0,0 +1,94 @@ +from __future__ import absolute_import + +import logging +from email.parser import FeedParser + +from pip._vendor import pkg_resources +from pip._vendor.packaging import specifiers, version + +from pip._internal.exceptions import NoneMetadataError +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, Tuple + from email.message import Message + from pip._vendor.pkg_resources import Distribution + + +logger = logging.getLogger(__name__) + + +def check_requires_python(requires_python, version_info): + # type: (Optional[str], Tuple[int, ...]) -> bool + """ + Check if the given Python version matches a "Requires-Python" specifier. + + :param version_info: A 3-tuple of ints representing a Python + major-minor-micro version to check (e.g. `sys.version_info[:3]`). + + :return: `True` if the given Python version satisfies the requirement. + Otherwise, return `False`. + + :raises InvalidSpecifier: If `requires_python` has an invalid format. + """ + if requires_python is None: + # The package provides no information + return True + requires_python_specifier = specifiers.SpecifierSet(requires_python) + + python_version = version.parse('.'.join(map(str, version_info))) + return python_version in requires_python_specifier + + +def get_metadata(dist): + # type: (Distribution) -> Message + """ + :raises NoneMetadataError: if the distribution reports `has_metadata()` + True but `get_metadata()` returns None. + """ + metadata_name = 'METADATA' + if (isinstance(dist, pkg_resources.DistInfoDistribution) and + dist.has_metadata(metadata_name)): + metadata = dist.get_metadata(metadata_name) + elif dist.has_metadata('PKG-INFO'): + metadata_name = 'PKG-INFO' + metadata = dist.get_metadata(metadata_name) + else: + logger.warning("No metadata found in %s", display_path(dist.location)) + metadata = '' + + if metadata is None: + raise NoneMetadataError(dist, metadata_name) + + feed_parser = FeedParser() + # The following line errors out if with a "NoneType" TypeError if + # passed metadata=None. + feed_parser.feed(metadata) + return feed_parser.close() + + +def get_requires_python(dist): + # type: (pkg_resources.Distribution) -> Optional[str] + """ + Return the "Requires-Python" metadata for a distribution, or None + if not present. + """ + pkg_info_dict = get_metadata(dist) + requires_python = pkg_info_dict.get('Requires-Python') + + if requires_python is not None: + # Convert to a str to satisfy the type checker, since requires_python + # can be a Header object. + requires_python = str(requires_python) + + return requires_python + + +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/venv/lib/python3.8/site-packages/pip/_internal/utils/pkg_resources.py b/venv/lib/python3.8/site-packages/pip/_internal/utils/pkg_resources.py new file mode 100644 index 0000000..0bc129a --- /dev/null +++ b/venv/lib/python3.8/site-packages/pip/_internal/utils/pkg_resources.py @@ -0,0 +1,44 @@ +from pip._vendor.pkg_resources import yield_lines +from pip._vendor.six import ensure_str + +from pip._internal.utils.typing import MYPY_CHECK_RUNNING + +if MYPY_CHECK_RUNNING: + from typing import Dict, Iterable, List + + +class DictMetadata(object): + """IMetadataProvider that reads metadata files from a dictionary. + """ + def __init__(self, metadata): + # type: (Dict[str, bytes]) -> None + self._metadata = metadata + + def has_metadata(self, name): + # type: (str) -> bool + return name in self._metadata + + def get_metadata(self, name): + # type: (str) -> str + try: + return ensure_str(self._metadata[name]) + except UnicodeDecodeError as e: + # Mirrors handling done in pkg_resources.NullProvider. + e.reason += " in {} file".format(name) + raise + + def get_metadata_lines(self, name): + # type: (str) -> Iterable[str] + return yield_lines(self.get_metadata(name)) + + def metadata_isdir(self, name): + # type: (str) -> bool + return False + + def metadata_listdir(self, name): + # type: (str) -> List[str] + return [] + + def run_script(self, script_name, namespace): + # type: (str, str) -> None + pass diff --git a/venv/lib/python3.8/site-packages/pip/_internal/utils/setuptools_build.py b/venv/lib/python3.8/site-packages/pip/_internal/utils/setuptools_build.py new file mode 100644 index 0000000..2a664b0 --- /dev/null +++ b/venv/lib/python3.8/site-packages/pip/_internal/utils/setuptools_build.py @@ -0,0 +1,181 @@ +import sys + +from pip._internal.utils.typing import MYPY_CHECK_RUNNING + +if MYPY_CHECK_RUNNING: + from typing import List, Optional, Sequence + +# Shim to wrap setup.py invocation with setuptools +# +# We set sys.argv[0] to the path to the underlying setup.py file so +# setuptools / distutils don't take the path to the setup.py to be "-c" when +# invoking via the shim. This avoids e.g. the following manifest_maker +# warning: "warning: manifest_maker: standard file '-c' not found". +_SETUPTOOLS_SHIM = ( + "import sys, setuptools, tokenize; sys.argv[0] = {0!r}; __file__={0!r};" + "f=getattr(tokenize, 'open', open)(__file__);" + "code=f.read().replace('\\r\\n', '\\n');" + "f.close();" + "exec(compile(code, __file__, 'exec'))" +) + + +def make_setuptools_shim_args( + setup_py_path, # type: str + global_options=None, # type: Sequence[str] + no_user_config=False, # type: bool + unbuffered_output=False # type: bool +): + # type: (...) -> List[str] + """ + Get setuptools command arguments with shim wrapped setup file invocation. + + :param setup_py_path: The path to setup.py to be wrapped. + :param global_options: Additional global options. + :param no_user_config: If True, disables personal user configuration. + :param unbuffered_output: If True, adds the unbuffered switch to the + argument list. + """ + args = [sys.executable] + if unbuffered_output: + args += ["-u"] + args += ["-c", _SETUPTOOLS_SHIM.format(setup_py_path)] + if global_options: + args += global_options + if no_user_config: + args += ["--no-user-cfg"] + return args + + +def make_setuptools_bdist_wheel_args( + setup_py_path, # type: str + global_options, # type: Sequence[str] + build_options, # type: Sequence[str] + destination_dir, # type: str +): + # type: (...) -> List[str] + # 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. + args = make_setuptools_shim_args( + setup_py_path, + global_options=global_options, + unbuffered_output=True + ) + args += ["bdist_wheel", "-d", destination_dir] + args += build_options + return args + + +def make_setuptools_clean_args( + setup_py_path, # type: str + global_options, # type: Sequence[str] +): + # type: (...) -> List[str] + args = make_setuptools_shim_args( + setup_py_path, + global_options=global_options, + unbuffered_output=True + ) + args += ["clean", "--all"] + return args + + +def make_setuptools_develop_args( + setup_py_path, # type: str + global_options, # type: Sequence[str] + install_options, # type: Sequence[str] + no_user_config, # type: bool + prefix, # type: Optional[str] + home, # type: Optional[str] + use_user_site, # type: bool +): + # type: (...) -> List[str] + assert not (use_user_site and prefix) + + args = make_setuptools_shim_args( + setup_py_path, + global_options=global_options, + no_user_config=no_user_config, + ) + + args += ["develop", "--no-deps"] + + args += install_options + + if prefix: + args += ["--prefix", prefix] + if home is not None: + args += ["--home", home] + + if use_user_site: + args += ["--user", "--prefix="] + + return args + + +def make_setuptools_egg_info_args( + setup_py_path, # type: str + egg_info_dir, # type: Optional[str] + no_user_config, # type: bool +): + # type: (...) -> List[str] + args = make_setuptools_shim_args( + setup_py_path, no_user_config=no_user_config + ) + + args += ["egg_info"] + + if egg_info_dir: + args += ["--egg-base", egg_info_dir] + + return args + + +def make_setuptools_install_args( + setup_py_path, # type: str + global_options, # type: Sequence[str] + install_options, # type: Sequence[str] + record_filename, # type: str + root, # type: Optional[str] + prefix, # type: Optional[str] + header_dir, # type: Optional[str] + home, # type: Optional[str] + use_user_site, # type: bool + no_user_config, # type: bool + pycompile # type: bool +): + # type: (...) -> List[str] + assert not (use_user_site and prefix) + assert not (use_user_site and root) + + args = make_setuptools_shim_args( + setup_py_path, + global_options=global_options, + no_user_config=no_user_config, + unbuffered_output=True + ) + args += ["install", "--record", record_filename] + args += ["--single-version-externally-managed"] + + if root is not None: + args += ["--root", root] + if prefix is not None: + args += ["--prefix", prefix] + if home is not None: + args += ["--home", home] + if use_user_site: + args += ["--user", "--prefix="] + + if pycompile: + args += ["--compile"] + else: + args += ["--no-compile"] + + if header_dir: + args += ["--install-headers", header_dir] + + args += install_options + + return args diff --git a/venv/lib/python3.8/site-packages/pip/_internal/utils/subprocess.py b/venv/lib/python3.8/site-packages/pip/_internal/utils/subprocess.py new file mode 100644 index 0000000..55c82da --- /dev/null +++ b/venv/lib/python3.8/site-packages/pip/_internal/utils/subprocess.py @@ -0,0 +1,277 @@ +# The following comment should be removed at some point in the future. +# mypy: strict-optional=False + +from __future__ import absolute_import + +import logging +import os +import subprocess + +from pip._vendor.six.moves import shlex_quote + +from pip._internal.cli.spinners import SpinnerInterface, open_spinner +from pip._internal.exceptions import InstallationError +from pip._internal.utils.compat import console_to_str, str_to_display +from pip._internal.utils.logging import subprocess_logger +from pip._internal.utils.misc import HiddenText, path_to_display +from pip._internal.utils.typing import MYPY_CHECK_RUNNING + +if MYPY_CHECK_RUNNING: + from typing import ( + Any, Callable, Iterable, List, Mapping, Optional, Text, Union, + ) + + CommandArgs = List[Union[str, HiddenText]] + + +LOG_DIVIDER = '----------------------------------------' + + +def make_command(*args): + # type: (Union[str, HiddenText, CommandArgs]) -> CommandArgs + """ + Create a CommandArgs object. + """ + command_args = [] # type: CommandArgs + for arg in args: + # Check for list instead of CommandArgs since CommandArgs is + # only known during type-checking. + if isinstance(arg, list): + command_args.extend(arg) + else: + # Otherwise, arg is str or HiddenText. + command_args.append(arg) + + return command_args + + +def format_command_args(args): + # type: (Union[List[str], CommandArgs]) -> str + """ + Format command arguments for display. + """ + # For HiddenText arguments, display the redacted form by calling str(). + # Also, we don't apply str() to arguments that aren't HiddenText since + # this can trigger a UnicodeDecodeError in Python 2 if the argument + # has type unicode and includes a non-ascii character. (The type + # checker doesn't ensure the annotations are correct in all cases.) + return ' '.join( + shlex_quote(str(arg)) if isinstance(arg, HiddenText) + else shlex_quote(arg) for arg in args + ) + + +def reveal_command_args(args): + # type: (Union[List[str], CommandArgs]) -> List[str] + """ + Return the arguments in their raw, unredacted form. + """ + return [ + arg.secret if isinstance(arg, HiddenText) else arg for arg in args + ] + + +def make_subprocess_output_error( + cmd_args, # type: Union[List[str], CommandArgs] + cwd, # type: Optional[str] + lines, # type: List[Text] + exit_status, # type: int +): + # type: (...) -> Text + """ + Create and return the error message to use to log a subprocess error + with command output. + + :param lines: A list of lines, each ending with a newline. + """ + command = format_command_args(cmd_args) + # Convert `command` and `cwd` to text (unicode in Python 2) so we can use + # them as arguments in the unicode format string below. This avoids + # "UnicodeDecodeError: 'ascii' codec can't decode byte ..." in Python 2 + # if either contains a non-ascii character. + command_display = str_to_display(command, desc='command bytes') + cwd_display = path_to_display(cwd) + + # We know the joined output value ends in a newline. + output = ''.join(lines) + msg = ( + # Use a unicode string to avoid "UnicodeEncodeError: 'ascii' + # codec can't encode character ..." in Python 2 when a format + # argument (e.g. `output`) has a non-ascii character. + u'Command errored out with exit status {exit_status}:\n' + ' command: {command_display}\n' + ' cwd: {cwd_display}\n' + 'Complete output ({line_count} lines):\n{output}{divider}' + ).format( + exit_status=exit_status, + command_display=command_display, + cwd_display=cwd_display, + line_count=len(lines), + output=output, + divider=LOG_DIVIDER, + ) + return msg + + +def call_subprocess( + cmd, # type: Union[List[str], CommandArgs] + 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] + log_failed_cmd=True # type: Optional[bool] +): + # type: (...) -> 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(). + log_failed_cmd: if false, failed commands are not logged, only raised. + """ + 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 = 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 = 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( + # Convert HiddenText objects to the underlying str. + reveal_command_args(cmd), + stderr=subprocess.STDOUT, stdin=subprocess.PIPE, + stdout=subprocess.PIPE, cwd=cwd, env=env, + ) + proc.stdin.close() + except Exception as exc: + if log_failed_cmd: + subprocess_logger.critical( + "Error %s while executing command %s", exc, command_desc, + ) + raise + all_output = [] + while True: + # The "line" value is a unicode string in Python 2. + 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 and log_failed_cmd: + # Then the subprocess streams haven't been logged to the + # console yet. + msg = make_subprocess_output_error( + cmd_args=cmd, + cwd=cwd, + lines=all_output, + exit_status=proc.returncode, + ) + subprocess_logger.error(msg) + exc_msg = ( + 'Command errored out with exit status {}: {} ' + 'Check the logs for full command output.' + ).format(proc.returncode, command_desc) + raise InstallationError(exc_msg) + elif on_returncode == 'warn': + subprocess_logger.warning( + 'Command "{}" had error code {} in {}'.format( + command_desc, proc.returncode, cwd) + ) + elif on_returncode == 'ignore': + pass + else: + raise ValueError('Invalid value: on_returncode={!r}'.format( + on_returncode)) + return ''.join(all_output) + + +def runner_with_spinner_message(message): + # type: (str) -> Callable[..., None] + """Provide a subprocess_runner that shows a spinner message. + + Intended for use with for pep517's Pep517HookCaller. Thus, the runner has + an API that matches what's expected by Pep517HookCaller.subprocess_runner. + """ + + def runner( + cmd, # type: List[str] + cwd=None, # type: Optional[str] + extra_environ=None # type: Optional[Mapping[str, Any]] + ): + # type: (...) -> None + with open_spinner(message) as spinner: + call_subprocess( + cmd, + cwd=cwd, + extra_environ=extra_environ, + spinner=spinner, + ) + + return runner diff --git a/venv/lib/python3.8/site-packages/pip/_internal/utils/temp_dir.py b/venv/lib/python3.8/site-packages/pip/_internal/utils/temp_dir.py new file mode 100644 index 0000000..201ba6d --- /dev/null +++ b/venv/lib/python3.8/site-packages/pip/_internal/utils/temp_dir.py @@ -0,0 +1,271 @@ +from __future__ import absolute_import + +import errno +import itertools +import logging +import os.path +import tempfile +from contextlib import contextmanager + +from pip._vendor.contextlib2 import ExitStack + +from pip._internal.utils.misc import enum, rmtree +from pip._internal.utils.typing import MYPY_CHECK_RUNNING + +if MYPY_CHECK_RUNNING: + from typing import Any, Dict, Iterator, Optional, TypeVar, Union + + _T = TypeVar('_T', bound='TempDirectory') + + +logger = logging.getLogger(__name__) + + +# Kinds of temporary directories. Only needed for ones that are +# globally-managed. +tempdir_kinds = enum( + BUILD_ENV="build-env", + EPHEM_WHEEL_CACHE="ephem-wheel-cache", + REQ_BUILD="req-build", +) + + +_tempdir_manager = None # type: Optional[ExitStack] + + +@contextmanager +def global_tempdir_manager(): + # type: () -> Iterator[None] + global _tempdir_manager + with ExitStack() as stack: + old_tempdir_manager, _tempdir_manager = _tempdir_manager, stack + try: + yield + finally: + _tempdir_manager = old_tempdir_manager + + +class TempDirectoryTypeRegistry(object): + """Manages temp directory behavior + """ + + def __init__(self): + # type: () -> None + self._should_delete = {} # type: Dict[str, bool] + + def set_delete(self, kind, value): + # type: (str, bool) -> None + """Indicate whether a TempDirectory of the given kind should be + auto-deleted. + """ + self._should_delete[kind] = value + + def get_delete(self, kind): + # type: (str) -> bool + """Get configured auto-delete flag for a given TempDirectory type, + default True. + """ + return self._should_delete.get(kind, True) + + +_tempdir_registry = None # type: Optional[TempDirectoryTypeRegistry] + + +@contextmanager +def tempdir_registry(): + # type: () -> Iterator[TempDirectoryTypeRegistry] + """Provides a scoped global tempdir registry that can be used to dictate + whether directories should be deleted. + """ + global _tempdir_registry + old_tempdir_registry = _tempdir_registry + _tempdir_registry = TempDirectoryTypeRegistry() + try: + yield _tempdir_registry + finally: + _tempdir_registry = old_tempdir_registry + + +class _Default(object): + pass + + +_default = _Default() + + +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 + delete + Whether the directory should be deleted when exiting + (when used as a contextmanager) + + Methods: + cleanup() + Deletes the temporary directory + + When used as a context manager, if the delete attribute is True, on + exiting the context the temporary directory is deleted. + """ + + def __init__( + self, + path=None, # type: Optional[str] + delete=_default, # type: Union[bool, None, _Default] + kind="temp", # type: str + globally_managed=False, # type: bool + ): + super(TempDirectory, self).__init__() + + if delete is _default: + if path is not None: + # If we were given an explicit directory, resolve delete option + # now. + delete = False + else: + # Otherwise, we wait until cleanup and see what + # tempdir_registry says. + delete = None + + if path is None: + path = self._create(kind) + + self._path = path + self._deleted = False + self.delete = delete + self.kind = kind + + if globally_managed: + assert _tempdir_manager is not None + _tempdir_manager.enter_context(self) + + @property + def path(self): + # type: () -> str + assert not self._deleted, ( + "Attempted to access deleted path: {}".format(self._path) + ) + return self._path + + def __repr__(self): + # type: () -> str + return "<{} {!r}>".format(self.__class__.__name__, self.path) + + def __enter__(self): + # type: (_T) -> _T + return self + + def __exit__(self, exc, value, tb): + # type: (Any, Any, Any) -> None + if self.delete is not None: + delete = self.delete + elif _tempdir_registry: + delete = _tempdir_registry.get_delete(self.kind) + else: + delete = True + + if delete: + self.cleanup() + + def _create(self, kind): + # type: (str) -> str + """Create a temporary directory and store its path in self.path + """ + # 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. + path = os.path.realpath( + tempfile.mkdtemp(prefix="pip-{}-".format(kind)) + ) + logger.debug("Created temporary directory: {}".format(path)) + return path + + def cleanup(self): + # type: () -> None + """Remove the temporary directory created and reset state + """ + self._deleted = True + if os.path.exists(self._path): + rmtree(self._path) + + +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): + # type: (str, Optional[bool]) -> None + self.original = original.rstrip('/\\') + super(AdjacentTempDirectory, self).__init__(delete=delete) + + @classmethod + def _generate_names(cls, name): + # type: (str) -> Iterator[str] + """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, kind): + # type: (str) -> str + 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: + path = os.path.realpath(path) + break + else: + # Final fallback on the default behavior. + path = os.path.realpath( + tempfile.mkdtemp(prefix="pip-{}-".format(kind)) + ) + + logger.debug("Created temporary directory: {}".format(path)) + return path diff --git a/venv/lib/python3.8/site-packages/pip/_internal/utils/typing.py b/venv/lib/python3.8/site-packages/pip/_internal/utils/typing.py new file mode 100644 index 0000000..8505a29 --- /dev/null +++ b/venv/lib/python3.8/site-packages/pip/_internal/utils/typing.py @@ -0,0 +1,38 @@ +"""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 + + +if MYPY_CHECK_RUNNING: + from typing import cast +else: + # typing's cast() is needed at runtime, but we don't want to import typing. + # Thus, we use a dummy no-op version, which we tell mypy to ignore. + def cast(type_, value): # type: ignore + return value diff --git a/venv/lib/python3.8/site-packages/pip/_internal/utils/unpacking.py b/venv/lib/python3.8/site-packages/pip/_internal/utils/unpacking.py new file mode 100644 index 0000000..7252dc2 --- /dev/null +++ b/venv/lib/python3.8/site-packages/pip/_internal/utils/unpacking.py @@ -0,0 +1,272 @@ +"""Utilities related archives. +""" + +# The following comment should be removed at some point in the future. +# mypy: strict-optional=False +# mypy: disallow-untyped-defs=False + +from __future__ import absolute_import + +import logging +import os +import shutil +import stat +import tarfile +import zipfile + +from pip._internal.exceptions import InstallationError +from pip._internal.utils.filetypes import ( + BZ2_EXTENSIONS, + TAR_EXTENSIONS, + XZ_EXTENSIONS, + ZIP_EXTENSIONS, +) +from pip._internal.utils.misc import ensure_dir +from pip._internal.utils.typing import MYPY_CHECK_RUNNING + +if MYPY_CHECK_RUNNING: + from typing import Iterable, List, Optional, Text, Union + + +logger = logging.getLogger(__name__) + + +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 current_umask(): + """Get the current umask which involves having to set it temporarily.""" + mask = os.umask(0) + os.umask(mask) + return mask + + +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 is_within_directory(directory, target): + # type: ((Union[str, Text]), (Union[str, Text])) -> bool + """ + Return true if the absolute path of target is within the directory + """ + abs_directory = os.path.abspath(directory) + abs_target = os.path.abspath(target) + + prefix = os.path.commonprefix([abs_directory, abs_target]) + return prefix == abs_directory + + +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 not is_within_directory(location, fn): + message = ( + 'The zip file ({}) has a file ({}) trying to install ' + 'outside target directory ({})' + ) + raise InstallationError(message.format(filename, fn, location)) + 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 not is_within_directory(location, path): + message = ( + 'The tar file ({}) has a file ({}) trying to install ' + 'outside target directory ({})' + ) + raise InstallationError( + message.format(filename, path, location) + ) + 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=None, # type: Optional[str] +): + # 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) + 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 {}'.format(location) + ) diff --git a/venv/lib/python3.8/site-packages/pip/_internal/utils/urls.py b/venv/lib/python3.8/site-packages/pip/_internal/utils/urls.py new file mode 100644 index 0000000..f37bc8f --- /dev/null +++ b/venv/lib/python3.8/site-packages/pip/_internal/utils/urls.py @@ -0,0 +1,55 @@ +import os +import sys + +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.utils.typing import MYPY_CHECK_RUNNING + +if MYPY_CHECK_RUNNING: + from typing import Optional, Text, Union + + +def get_url_scheme(url): + # type: (Union[str, Text]) -> Optional[Text] + if ':' not in url: + return None + return url.split(':', 1)[0].lower() + + +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 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 {url!r})" + .format(**locals())) + + _, 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: {url!r}' + .format(**locals()) + ) + + path = urllib_request.url2pathname(netloc + path) + return path diff --git a/venv/lib/python3.8/site-packages/pip/_internal/utils/virtualenv.py b/venv/lib/python3.8/site-packages/pip/_internal/utils/virtualenv.py new file mode 100644 index 0000000..596a69a --- /dev/null +++ b/venv/lib/python3.8/site-packages/pip/_internal/utils/virtualenv.py @@ -0,0 +1,116 @@ +from __future__ import absolute_import + +import logging +import os +import re +import site +import sys + +from pip._internal.utils.typing import MYPY_CHECK_RUNNING + +if MYPY_CHECK_RUNNING: + from typing import List, Optional + +logger = logging.getLogger(__name__) +_INCLUDE_SYSTEM_SITE_PACKAGES_REGEX = re.compile( + r"include-system-site-packages\s*=\s*(?Ptrue|false)" +) + + +def _running_under_venv(): + # type: () -> bool + """Checks if sys.base_prefix and sys.prefix match. + + This handles PEP 405 compliant virtual environments. + """ + return sys.prefix != getattr(sys, "base_prefix", sys.prefix) + + +def _running_under_regular_virtualenv(): + # type: () -> bool + """Checks if sys.real_prefix is set. + + This handles virtual environments created with pypa's virtualenv. + """ + # pypa/virtualenv case + return hasattr(sys, 'real_prefix') + + +def running_under_virtualenv(): + # type: () -> bool + """Return True if we're running inside a virtualenv, False otherwise. + """ + return _running_under_venv() or _running_under_regular_virtualenv() + + +def _get_pyvenv_cfg_lines(): + # type: () -> Optional[List[str]] + """Reads {sys.prefix}/pyvenv.cfg and returns its contents as list of lines + + Returns None, if it could not read/access the file. + """ + pyvenv_cfg_file = os.path.join(sys.prefix, 'pyvenv.cfg') + try: + with open(pyvenv_cfg_file) as f: + return f.read().splitlines() # avoids trailing newlines + except IOError: + return None + + +def _no_global_under_venv(): + # type: () -> bool + """Check `{sys.prefix}/pyvenv.cfg` for system site-packages inclusion + + PEP 405 specifies that when system site-packages are not supposed to be + visible from a virtual environment, `pyvenv.cfg` must contain the following + line: + + include-system-site-packages = false + + Additionally, log a warning if accessing the file fails. + """ + cfg_lines = _get_pyvenv_cfg_lines() + if cfg_lines is None: + # We're not in a "sane" venv, so assume there is no system + # site-packages access (since that's PEP 405's default state). + logger.warning( + "Could not access 'pyvenv.cfg' despite a virtual environment " + "being active. Assuming global site-packages is not accessible " + "in this environment." + ) + return True + + for line in cfg_lines: + match = _INCLUDE_SYSTEM_SITE_PACKAGES_REGEX.match(line) + if match is not None and match.group('value') == 'false': + return True + return False + + +def _no_global_under_regular_virtualenv(): + # type: () -> bool + """Check if "no-global-site-packages.txt" exists beside site.py + + This mirrors logic in pypa/virtualenv for determining whether system + site-packages are visible in the virtual environment. + """ + site_mod_dir = os.path.dirname(os.path.abspath(site.__file__)) + no_global_site_packages_file = os.path.join( + site_mod_dir, 'no-global-site-packages.txt', + ) + return os.path.exists(no_global_site_packages_file) + + +def virtualenv_no_global(): + # type: () -> bool + """Returns a boolean, whether running in venv with no system site-packages. + """ + # PEP 405 compliance needs to be checked first since virtualenv >=20 would + # return True for both checks, but is only able to use the PEP 405 config. + if _running_under_venv(): + return _no_global_under_venv() + + if _running_under_regular_virtualenv(): + return _no_global_under_regular_virtualenv() + + return False diff --git a/venv/lib/python3.8/site-packages/pip/_internal/utils/wheel.py b/venv/lib/python3.8/site-packages/pip/_internal/utils/wheel.py new file mode 100644 index 0000000..3ebb771 --- /dev/null +++ b/venv/lib/python3.8/site-packages/pip/_internal/utils/wheel.py @@ -0,0 +1,225 @@ +"""Support functions for working with wheel files. +""" + +from __future__ import absolute_import + +import logging +from email.parser import Parser +from zipfile import ZipFile + +from pip._vendor.packaging.utils import canonicalize_name +from pip._vendor.pkg_resources import DistInfoDistribution +from pip._vendor.six import PY2, ensure_str + +from pip._internal.exceptions import UnsupportedWheel +from pip._internal.utils.pkg_resources import DictMetadata +from pip._internal.utils.typing import MYPY_CHECK_RUNNING + +if MYPY_CHECK_RUNNING: + from email.message import Message + from typing import Dict, Tuple + + from pip._vendor.pkg_resources import Distribution + +if PY2: + from zipfile import BadZipfile as BadZipFile +else: + from zipfile import BadZipFile + + +VERSION_COMPATIBLE = (1, 0) + + +logger = logging.getLogger(__name__) + + +class WheelMetadata(DictMetadata): + """Metadata provider that maps metadata decoding exceptions to our + internal exception type. + """ + def __init__(self, metadata, wheel_name): + # type: (Dict[str, bytes], str) -> None + super(WheelMetadata, self).__init__(metadata) + self._wheel_name = wheel_name + + def get_metadata(self, name): + # type: (str) -> str + try: + return super(WheelMetadata, self).get_metadata(name) + except UnicodeDecodeError as e: + # Augment the default error with the origin of the file. + raise UnsupportedWheel( + "Error decoding metadata for {}: {}".format( + self._wheel_name, e + ) + ) + + +def pkg_resources_distribution_for_wheel(wheel_zip, name, location): + # type: (ZipFile, str, str) -> Distribution + """Get a pkg_resources distribution given a wheel. + + :raises UnsupportedWheel: on any errors + """ + info_dir, _ = parse_wheel(wheel_zip, name) + + metadata_files = [ + p for p in wheel_zip.namelist() if p.startswith("{}/".format(info_dir)) + ] + + metadata_text = {} # type: Dict[str, bytes] + for path in metadata_files: + # If a flag is set, namelist entries may be unicode in Python 2. + # We coerce them to native str type to match the types used in the rest + # of the code. This cannot fail because unicode can always be encoded + # with UTF-8. + full_path = ensure_str(path) + _, metadata_name = full_path.split("/", 1) + + try: + metadata_text[metadata_name] = read_wheel_metadata_file( + wheel_zip, full_path + ) + except UnsupportedWheel as e: + raise UnsupportedWheel( + "{} has an invalid wheel, {}".format(name, str(e)) + ) + + metadata = WheelMetadata(metadata_text, location) + + return DistInfoDistribution( + location=location, metadata=metadata, project_name=name + ) + + +def parse_wheel(wheel_zip, name): + # type: (ZipFile, str) -> Tuple[str, Message] + """Extract information from the provided wheel, ensuring it meets basic + standards. + + Returns the name of the .dist-info directory and the parsed WHEEL metadata. + """ + try: + info_dir = wheel_dist_info_dir(wheel_zip, name) + metadata = wheel_metadata(wheel_zip, info_dir) + version = wheel_version(metadata) + except UnsupportedWheel as e: + raise UnsupportedWheel( + "{} has an invalid wheel, {}".format(name, str(e)) + ) + + check_compatibility(version, name) + + return info_dir, metadata + + +def wheel_dist_info_dir(source, name): + # type: (ZipFile, str) -> str + """Returns the name of the contained .dist-info directory. + + Raises AssertionError or UnsupportedWheel if not found, >1 found, or + it doesn't match the provided name. + """ + # Zip file path separators must be / + subdirs = list(set(p.split("/")[0] for p in source.namelist())) + + info_dirs = [s for s in subdirs if s.endswith('.dist-info')] + + if not info_dirs: + raise UnsupportedWheel(".dist-info directory not found") + + if len(info_dirs) > 1: + raise UnsupportedWheel( + "multiple .dist-info directories found: {}".format( + ", ".join(info_dirs) + ) + ) + + info_dir = info_dirs[0] + + info_dir_name = canonicalize_name(info_dir) + canonical_name = canonicalize_name(name) + if not info_dir_name.startswith(canonical_name): + raise UnsupportedWheel( + ".dist-info directory {!r} does not start with {!r}".format( + info_dir, canonical_name + ) + ) + + # Zip file paths can be unicode or str depending on the zip entry flags, + # so normalize it. + return ensure_str(info_dir) + + +def read_wheel_metadata_file(source, path): + # type: (ZipFile, str) -> bytes + try: + return source.read(path) + # BadZipFile for general corruption, KeyError for missing entry, + # and RuntimeError for password-protected files + except (BadZipFile, KeyError, RuntimeError) as e: + raise UnsupportedWheel( + "could not read {!r} file: {!r}".format(path, e) + ) + + +def wheel_metadata(source, dist_info_dir): + # type: (ZipFile, str) -> Message + """Return the WHEEL metadata of an extracted wheel, if possible. + Otherwise, raise UnsupportedWheel. + """ + path = "{}/WHEEL".format(dist_info_dir) + # Zip file path separators must be / + wheel_contents = read_wheel_metadata_file(source, path) + + try: + wheel_text = ensure_str(wheel_contents) + except UnicodeDecodeError as e: + raise UnsupportedWheel("error decoding {!r}: {!r}".format(path, e)) + + # FeedParser (used by Parser) does not raise any exceptions. The returned + # message may have .defects populated, but for backwards-compatibility we + # currently ignore them. + return Parser().parsestr(wheel_text) + + +def wheel_version(wheel_data): + # type: (Message) -> Tuple[int, ...] + """Given WHEEL metadata, return the parsed Wheel-Version. + Otherwise, raise UnsupportedWheel. + """ + version_text = wheel_data["Wheel-Version"] + if version_text is None: + raise UnsupportedWheel("WHEEL is missing Wheel-Version") + + version = version_text.strip() + + try: + return tuple(map(int, version.split('.'))) + except ValueError: + raise UnsupportedWheel("invalid Wheel-Version: {!r}".format(version)) + + +def check_compatibility(version, name): + # type: (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 version[0] > VERSION_COMPATIBLE[0]: + raise UnsupportedWheel( + "{}'s Wheel-Version ({}) is not compatible with this version " + "of pip".format(name, '.'.join(map(str, version))) + ) + elif version > VERSION_COMPATIBLE: + logger.warning( + 'Installing from a newer Wheel-Version (%s)', + '.'.join(map(str, version)), + ) diff --git a/venv/lib/python3.8/site-packages/pip/_internal/vcs/__init__.py b/venv/lib/python3.8/site-packages/pip/_internal/vcs/__init__.py new file mode 100644 index 0000000..2a4eb13 --- /dev/null +++ b/venv/lib/python3.8/site-packages/pip/_internal/vcs/__init__.py @@ -0,0 +1,15 @@ +# Expose a limited set of classes and functions so callers outside of +# the vcs package don't need to import deeper than `pip._internal.vcs`. +# (The test directory and imports protected by MYPY_CHECK_RUNNING may +# still need to import from a vcs sub-package.) +# Import all vcs modules to register each VCS in the VcsSupport object. +import pip._internal.vcs.bazaar +import pip._internal.vcs.git +import pip._internal.vcs.mercurial +import pip._internal.vcs.subversion # noqa: F401 +from pip._internal.vcs.versioncontrol import ( # noqa: F401 + RemoteNotFoundError, + is_url, + make_vcs_requirement_url, + vcs, +) diff --git a/venv/lib/python3.8/site-packages/pip/_internal/vcs/__pycache__/__init__.cpython-38.pyc b/venv/lib/python3.8/site-packages/pip/_internal/vcs/__pycache__/__init__.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..97caa4b8af0ff9234c3c0a01da094d1ffce32dee GIT binary patch literal 500 zcmZWlyH3L}6m^4@_#qX4>5AT+urK4YtX%I0s>SlB`%< zBv}mXE$6@_Q>Jo!C-ytGm+~}>xiSnC5up>qIoT2d)_0CQ+P9b7*yDX!GDvdZMA(yk zGt1`%gN$os&-UG&m1t!^3n)VZzl3@T4F?|x4$5;0-+)u#1u+a41n@!&>PF)pF literal 0 HcmV?d00001 diff --git a/venv/lib/python3.8/site-packages/pip/_internal/vcs/__pycache__/bazaar.cpython-38.pyc b/venv/lib/python3.8/site-packages/pip/_internal/vcs/__pycache__/bazaar.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..8b32f94bae0943798acb64f790c3183e3a38b73c GIT binary patch literal 3799 zcmZ`+-EteZ72ba?DN_HoWyf~eHcm??5?wcO6E{t3yK&-4qhwOMGucVHT@jGD)-HDq zfRe?buH>1_blN^aFQiMqN1p<>z49w$I(5H;C1pu*mjmnpaDY8H-}&RCR;yv*dGpF& z*>KLX{!N3EkB7nU(B!|+F^k!mmD`bR6YXS9z8o#*=c03lr-5_N zN9PSa1O5xqg?uGi0l#y2kzYEpqc=XWSd+D$S**nu4&3N6Up&0RFCUT2&L6GL>ezZN*^osxR6I`evJh$=bAFi!$vcj2vQ(B?vKN<$>gzz{N^lN_W}a;GxL4$PGGG`l ze*X2BU&r@8{rKKz@mG)5*B`8Zg4r3`6{{j1iVTO%{)r2j7K3}mK#3yL?oLl)I{czk zm`yV6KOB}B9pCsgWsDCV@~4Uz?hIA`!Cg_RqDTG>9k(KzS%Ad` zBu?ZpH}aVm1>aelHuG8VER1SAWHlB(vkvU2&g!i3%!(RphBfh?VJ$X`cazPrdAwVE z*6dthi_b#v&+%Ed#Fow8dC=$Bc|$LNzQ9%tz4)EwSN|mGfl#-(wky7@nRk?{(Getx|YgyT|>CThK#hcUs3%2A9 zbjnip*g9|?J7at7`~%o4S9uJoIdH{0%6se|2F%&Aja66>)m_)Eq2w~&6vW+Rq}D*$#Jw&7||@@!LkiP(}l z*xsf0b6eNrxR)hT#_@CO`?6b#bSF`~yOW6SW|1*iO1JEFZ|#;l@7^4Ia8ogMYeT+& zV>2J+@7*xZ)}Z^aTc+jea8M?_ZO&GA`zI~woaKyGCZGKQQ;+bRK7?xx&3ykZ2xUFC4}t5EEtZu# zwhw^r*gEva&XN6%v+wM?W9zZ60>}yM$L?Y6$U5+t!`#0+hyflX_eYN!?aA};Q*!>c z>hpFbPh0S^kf|!fUOTB4fO1j_Nr*YpQcht;o~xTiEfYcjfM&n5Tu?F!@)Gj2m(si5KKsa9Zy`MaX9_9cCi^7 zpx?>vGav{;dksy_qO*dg9XhkNXV2Tte@Cqe1eMsmX(%y_=#KUN5eCqtb!6>(3fhNm zov~MG|Hy-P8U2F8KeDi5ANcz|FbT%~v4?r&#K%vDnBddyR?BKLDjWE z>0nqggtjk0OJiBiynSqO)mqPl0x-}+Jnp1P(!(^0SKj`OyKfuswhvuK-v4vl#=cB|jD5O} zq#zL9r4(1_9CO7sQNKp_5Y5Cy%R6s3?9d*)aU!Noy?XkkQ7!khwj*e60U>th+_Ul%Q&4+PAC4N;Cu(RW7&w_ zHozVGujnV&pIl-Dc(5*hfp6b`?ua+>7Vo3$IHTnrIVw46vDvQj_3id(4p|PF@p$3| zr3U12@H7&J#qT z`$ndzxa4r!8zebDE9xSkqly=$Ps(EcWSC?E%vB6>M;9kz{*bG)Of?f7iJGYq$9chq z8PUx+rcdx6lH|HhVf(>6)8EH59m}px~x!l*Ewp#5Ihf%c#xP;vE!-g;O@lM@7y&uKo+QgQ6)xQ~0C!y(Iqz)$M&d>~*vuIIAb%&tRT-Rare=s>+ zd`Xj3s!wWb!M9Kpq11krM2=&v4nM3+=OZ$5Nj};{q7I!VGD_3@=HWO*&$|tG{?Gpd D@h!4b literal 0 HcmV?d00001 diff --git a/venv/lib/python3.8/site-packages/pip/_internal/vcs/__pycache__/git.cpython-38.pyc b/venv/lib/python3.8/site-packages/pip/_internal/vcs/__pycache__/git.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..f90a9c9bcf8b86379fdd74787433baa4e9573313 GIT binary patch literal 9686 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 literal 0 HcmV?d00001 diff --git a/venv/lib/python3.8/site-packages/pip/_internal/vcs/__pycache__/mercurial.cpython-38.pyc b/venv/lib/python3.8/site-packages/pip/_internal/vcs/__pycache__/mercurial.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..577b9c72926a1039abaf06260baed0da8d0168a0 GIT binary patch literal 5123 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 S>(hG$2@qd)&2IkLu>S{&WH7b> literal 0 HcmV?d00001 diff --git a/venv/lib/python3.8/site-packages/pip/_internal/vcs/__pycache__/subversion.cpython-38.pyc b/venv/lib/python3.8/site-packages/pip/_internal/vcs/__pycache__/subversion.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..6b4798641f0fc7fbbaee5c35e468eef007ae71db GIT binary patch literal 8571 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 UK>ZAI>s5<^9Kg@m&)B8^1~C$?`2YX_ literal 0 HcmV?d00001 diff --git a/venv/lib/python3.8/site-packages/pip/_internal/vcs/__pycache__/versioncontrol.cpython-38.pyc b/venv/lib/python3.8/site-packages/pip/_internal/vcs/__pycache__/versioncontrol.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..0ec81894caaa1ecd064b1a2dc8fd2359424faa2b GIT binary patch literal 19698 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 L?2XwUDdheyC9X#5 literal 0 HcmV?d00001 diff --git a/venv/lib/python3.8/site-packages/pip/_internal/vcs/bazaar.py b/venv/lib/python3.8/site-packages/pip/_internal/vcs/bazaar.py new file mode 100644 index 0000000..347c06f --- /dev/null +++ b/venv/lib/python3.8/site-packages/pip/_internal/vcs/bazaar.py @@ -0,0 +1,120 @@ +# The following comment should be removed at some point in the future. +# mypy: disallow-untyped-defs=False + +from __future__ import absolute_import + +import logging +import os + +from pip._vendor.six.moves.urllib import parse as urllib_parse + +from pip._internal.utils.misc import display_path, rmtree +from pip._internal.utils.subprocess import make_command +from pip._internal.utils.typing import MYPY_CHECK_RUNNING +from pip._internal.utils.urls import path_to_url +from pip._internal.vcs.versioncontrol import VersionControl, vcs + +if MYPY_CHECK_RUNNING: + from typing import Optional, Tuple + from pip._internal.utils.misc import HiddenText + from pip._internal.vcs.versioncontrol import AuthInfo, RevOptions + + +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, *args, **kwargs): + super(Bazaar, self).__init__(*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, url): + # type: (str, HiddenText) -> None + """ + 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(url) + self.run_command( + make_command('export', location, url, rev_options.to_args()), + show_stdout=False, + ) + + def fetch_new(self, dest, url, rev_options): + # type: (str, HiddenText, RevOptions) -> None + rev_display = rev_options.to_display() + logger.info( + 'Checking out %s%s to %s', + url, + rev_display, + display_path(dest), + ) + cmd_args = ( + make_command('branch', '-q', rev_options.to_args(), url, dest) + ) + self.run_command(cmd_args) + + def switch(self, dest, url, rev_options): + # type: (str, HiddenText, RevOptions) -> None + self.run_command(make_command('switch', url), cwd=dest) + + def update(self, dest, url, rev_options): + # type: (str, HiddenText, RevOptions) -> None + cmd_args = make_command('pull', '-q', rev_options.to_args()) + self.run_command(cmd_args, cwd=dest) + + @classmethod + def get_url_rev_and_auth(cls, url): + # type: (str) -> Tuple[str, Optional[str], AuthInfo] + # 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/venv/lib/python3.8/site-packages/pip/_internal/vcs/git.py b/venv/lib/python3.8/site-packages/pip/_internal/vcs/git.py new file mode 100644 index 0000000..e173ec8 --- /dev/null +++ b/venv/lib/python3.8/site-packages/pip/_internal/vcs/git.py @@ -0,0 +1,394 @@ +# The following comment should be removed at some point in the future. +# mypy: disallow-untyped-defs=False + +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, InstallationError +from pip._internal.utils.misc import display_path, hide_url +from pip._internal.utils.subprocess import make_command +from pip._internal.utils.temp_dir import TempDirectory +from pip._internal.utils.typing import MYPY_CHECK_RUNNING +from pip._internal.vcs.versioncontrol import ( + RemoteNotFoundError, + VersionControl, + find_path_to_setup_from_repo_root, + vcs, +) + +if MYPY_CHECK_RUNNING: + from typing import Optional, Tuple + from pip._internal.utils.misc import HiddenText + from pip._internal.vcs.versioncontrol import AuthInfo, RevOptions + + +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' + + @staticmethod + def get_base_rev_args(rev): + return [rev] + + def is_immutable_rev_checkout(self, url, dest): + # type: (str, str) -> bool + _, rev_options = self.get_url_rev_options(hide_url(url)) + if not rev_options.rev: + return False + if not self.is_commit_id_equal(dest, rev_options.rev): + # the current commit is different from rev, + # which means rev was something else than a commit hash + return False + # return False in the rare case rev is both a commit hash + # and a tag or a branch; we don't want to cache in that case + # because that branch/tag could point to something else in the future + is_tag_or_branch = bool( + self.get_revision_sha(dest, rev_options.rev)[0] + ) + return not is_tag_or_branch + + 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, url): + # type: (str, HiddenText) -> None + """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, url=url) + 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): + # type: (str, HiddenText, RevOptions) -> RevOptions + """ + 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 + # The arg_rev property's implementation for Git ensures that the + # rev return value is always non-None. + assert rev is not None + + 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( + make_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 + + def fetch_new(self, dest, url, rev_options): + # type: (str, HiddenText, RevOptions) -> None + rev_display = rev_options.to_display() + logger.info('Cloning %s%s to %s', url, rev_display, display_path(dest)) + self.run_command(make_command('clone', '-q', url, dest)) + + if rev_options.rev: + # Then a specific revision was requested. + rev_options = self.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 self.is_commit_id_equal(dest, rev_options.rev): + cmd_args = make_command( + 'checkout', '-q', rev_options.to_args(), + ) + self.run_command(cmd_args, cwd=dest) + elif self.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, + ] + self.run_command(cmd_args, cwd=dest) + + #: repo may contain submodules + self.update_submodules(dest) + + def switch(self, dest, url, rev_options): + # type: (str, HiddenText, RevOptions) -> None + self.run_command( + make_command('config', 'remote.origin.url', url), + cwd=dest, + ) + cmd_args = make_command('checkout', '-q', rev_options.to_args()) + self.run_command(cmd_args, cwd=dest) + + self.update_submodules(dest) + + def update(self, dest, url, rev_options): + # type: (str, HiddenText, RevOptions) -> None + # 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 = make_command('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): + """ + Return the path to setup.py, relative to the repo root. + Return None if setup.py is in the repo root. + """ + # 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) + repo_root = os.path.abspath(os.path.join(git_dir, '..')) + return find_path_to_setup_from_repo_root(location, repo_root) + + @classmethod + def get_url_rev_and_auth(cls, url): + # type: (str) -> Tuple[str, Optional[str], AuthInfo] + """ + 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. + """ + # Works around an apparent Git bug + # (see https://article.gmane.org/gmane.comp.version-control.git/146500) + 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), + ) + + 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 get_repository_root(cls, location): + loc = super(Git, cls).get_repository_root(location) + if loc: + return loc + try: + r = cls.run_command( + ['rev-parse', '--show-toplevel'], + cwd=location, + show_stdout=False, + on_returncode='raise', + log_failed_cmd=False, + ) + except BadCommand: + logger.debug("could not determine if %s is under git control " + "because git is not available", location) + return None + except InstallationError: + return None + return os.path.normpath(r.rstrip('\r\n')) + + +vcs.register(Git) diff --git a/venv/lib/python3.8/site-packages/pip/_internal/vcs/mercurial.py b/venv/lib/python3.8/site-packages/pip/_internal/vcs/mercurial.py new file mode 100644 index 0000000..75e903c --- /dev/null +++ b/venv/lib/python3.8/site-packages/pip/_internal/vcs/mercurial.py @@ -0,0 +1,161 @@ +# The following comment should be removed at some point in the future. +# mypy: disallow-untyped-defs=False + +from __future__ import absolute_import + +import logging +import os + +from pip._vendor.six.moves import configparser + +from pip._internal.exceptions import BadCommand, InstallationError +from pip._internal.utils.misc import display_path +from pip._internal.utils.subprocess import make_command +from pip._internal.utils.temp_dir import TempDirectory +from pip._internal.utils.typing import MYPY_CHECK_RUNNING +from pip._internal.utils.urls import path_to_url +from pip._internal.vcs.versioncontrol import ( + VersionControl, + find_path_to_setup_from_repo_root, + vcs, +) + +if MYPY_CHECK_RUNNING: + from pip._internal.utils.misc import HiddenText + from pip._internal.vcs.versioncontrol import RevOptions + + +logger = logging.getLogger(__name__) + + +class Mercurial(VersionControl): + name = 'hg' + dirname = '.hg' + repo_name = 'clone' + schemes = ( + 'hg', 'hg+file', 'hg+http', 'hg+https', 'hg+ssh', 'hg+static-http', + ) + + @staticmethod + def get_base_rev_args(rev): + return [rev] + + def export(self, location, url): + # type: (str, HiddenText) -> None + """Export the Hg repository at the url to the destination location""" + with TempDirectory(kind="export") as temp_dir: + self.unpack(temp_dir.path, url=url) + + self.run_command( + ['archive', location], show_stdout=False, cwd=temp_dir.path + ) + + def fetch_new(self, dest, url, rev_options): + # type: (str, HiddenText, RevOptions) -> None + rev_display = rev_options.to_display() + logger.info( + 'Cloning hg %s%s to %s', + url, + rev_display, + display_path(dest), + ) + self.run_command(make_command('clone', '--noupdate', '-q', url, dest)) + self.run_command( + make_command('update', '-q', rev_options.to_args()), + cwd=dest, + ) + + def switch(self, dest, url, rev_options): + # type: (str, HiddenText, RevOptions) -> None + repo_config = os.path.join(dest, self.dirname, 'hgrc') + config = configparser.RawConfigParser() + try: + config.read(repo_config) + config.set('paths', 'default', url.secret) + 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 = make_command('update', '-q', rev_options.to_args()) + self.run_command(cmd_args, cwd=dest) + + def update(self, dest, url, rev_options): + # type: (str, HiddenText, RevOptions) -> None + self.run_command(['pull', '-q'], cwd=dest) + cmd_args = make_command('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 + + @classmethod + def get_subdirectory(cls, location): + """ + Return the path to setup.py, relative to the repo root. + Return None if setup.py is in the repo root. + """ + # find the repo root + repo_root = cls.run_command( + ['root'], show_stdout=False, cwd=location).strip() + if not os.path.isabs(repo_root): + repo_root = os.path.abspath(os.path.join(location, repo_root)) + return find_path_to_setup_from_repo_root(location, repo_root) + + @classmethod + def get_repository_root(cls, location): + loc = super(Mercurial, cls).get_repository_root(location) + if loc: + return loc + try: + r = cls.run_command( + ['root'], + cwd=location, + show_stdout=False, + on_returncode='raise', + log_failed_cmd=False, + ) + except BadCommand: + logger.debug("could not determine if %s is under hg control " + "because hg is not available", location) + return None + except InstallationError: + return None + return os.path.normpath(r.rstrip('\r\n')) + + +vcs.register(Mercurial) diff --git a/venv/lib/python3.8/site-packages/pip/_internal/vcs/subversion.py b/venv/lib/python3.8/site-packages/pip/_internal/vcs/subversion.py new file mode 100644 index 0000000..0ec6597 --- /dev/null +++ b/venv/lib/python3.8/site-packages/pip/_internal/vcs/subversion.py @@ -0,0 +1,334 @@ +# The following comment should be removed at some point in the future. +# mypy: disallow-untyped-defs=False + +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, + is_console_interactive, + rmtree, + split_auth_from_netloc, +) +from pip._internal.utils.subprocess import make_command +from pip._internal.utils.typing import MYPY_CHECK_RUNNING +from pip._internal.vcs.versioncontrol 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'(.*)') + + +if MYPY_CHECK_RUNNING: + from typing import Optional, Tuple + from pip._internal.utils.subprocess import CommandArgs + from pip._internal.utils.misc import HiddenText + from pip._internal.vcs.versioncontrol import AuthInfo, RevOptions + + +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] + + @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): + # type: (str) -> Tuple[str, Optional[str], AuthInfo] + # 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): + # type: (Optional[str], Optional[HiddenText]) -> CommandArgs + extra_args = [] # type: CommandArgs + 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('= 1.7 + # Note that using get_remote_call_options is not necessary here + # because `svn info` is being run against a local directory. + # We don't need to worry about making sure interactive mode + # is being used to prompt for passwords, because passwords + # are only potentially needed for remote server requests. + 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 + + def __init__(self, use_interactive=None): + # type: (bool) -> None + if use_interactive is None: + use_interactive = is_console_interactive() + self.use_interactive = use_interactive + + # This member is used to cache the fetched version of the current + # ``svn`` client. + # Special value definitions: + # None: Not evaluated yet. + # Empty tuple: Could not parse version. + self._vcs_version = None # type: Optional[Tuple[int, ...]] + + super(Subversion, self).__init__() + + def call_vcs_version(self): + # type: () -> Tuple[int, ...] + """Query the version of the currently installed Subversion client. + + :return: A tuple containing the parts of the version information or + ``()`` 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 () + + version = version[len(version_prefix):].split()[0] + version_list = version.split('.') + try: + parsed_version = tuple(map(int, version_list)) + except ValueError: + return () + + return parsed_version + + def get_vcs_version(self): + # type: () -> Tuple[int, ...] + """Return the version of the currently installed Subversion client. + + If the version of the Subversion client has already been queried, + a cached value will be used. + + :return: A tuple containing the parts of the version information or + ``()`` if the version returned from ``svn`` could not be parsed. + :raises: BadCommand: If ``svn`` is not installed. + """ + if self._vcs_version is not None: + # Use cached version, if available. + # If parsing the version failed previously (empty tuple), + # do not attempt to parse it again. + return self._vcs_version + + vcs_version = self.call_vcs_version() + self._vcs_version = vcs_version + return vcs_version + + def get_remote_call_options(self): + # type: () -> CommandArgs + """Return options to be used on calls to Subversion that contact the server. + + These options are applicable for the following ``svn`` subcommands used + in this class. + + - checkout + - export + - switch + - update + + :return: A list of command line arguments to pass to ``svn``. + """ + if not self.use_interactive: + # --non-interactive switch is available since Subversion 0.14.4. + # Subversion < 1.8 runs in interactive mode by default. + return ['--non-interactive'] + + svn_version = self.get_vcs_version() + # By default, Subversion >= 1.8 runs in non-interactive mode if + # stdin is not a TTY. Since that is how pip invokes SVN, in + # call_subprocess(), pip must pass --force-interactive to ensure + # the user can be prompted for a password, if required. + # SVN added the --force-interactive option in SVN 1.8. Since + # e.g. RHEL/CentOS 7, which is supported until 2024, ships with + # SVN 1.7, pip should continue to support SVN 1.7. Therefore, pip + # can't safely add the option if the SVN version is < 1.8 (or unknown). + if svn_version >= (1, 8): + return ['--force-interactive'] + + return [] + + def export(self, location, url): + # type: (str, HiddenText) -> None + """Export the svn repository at the url to the destination location""" + url, rev_options = self.get_url_rev_options(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 = make_command( + 'export', self.get_remote_call_options(), + rev_options.to_args(), url, location, + ) + self.run_command(cmd_args, show_stdout=False) + + def fetch_new(self, dest, url, rev_options): + # type: (str, HiddenText, RevOptions) -> None + rev_display = rev_options.to_display() + logger.info( + 'Checking out %s%s to %s', + url, + rev_display, + display_path(dest), + ) + cmd_args = make_command( + 'checkout', '-q', self.get_remote_call_options(), + rev_options.to_args(), url, dest, + ) + self.run_command(cmd_args) + + def switch(self, dest, url, rev_options): + # type: (str, HiddenText, RevOptions) -> None + cmd_args = make_command( + 'switch', self.get_remote_call_options(), rev_options.to_args(), + url, dest, + ) + self.run_command(cmd_args) + + def update(self, dest, url, rev_options): + # type: (str, HiddenText, RevOptions) -> None + cmd_args = make_command( + 'update', self.get_remote_call_options(), rev_options.to_args(), + dest, + ) + self.run_command(cmd_args) + + +vcs.register(Subversion) diff --git a/venv/lib/python3.8/site-packages/pip/_internal/vcs/versioncontrol.py b/venv/lib/python3.8/site-packages/pip/_internal/vcs/versioncontrol.py new file mode 100644 index 0000000..71b4650 --- /dev/null +++ b/venv/lib/python3.8/site-packages/pip/_internal/vcs/versioncontrol.py @@ -0,0 +1,723 @@ +"""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, InstallationError +from pip._internal.utils.compat import samefile +from pip._internal.utils.misc import ( + ask_path_exists, + backup_dir, + display_path, + hide_url, + hide_value, + rmtree, +) +from pip._internal.utils.subprocess import call_subprocess, make_command +from pip._internal.utils.typing import MYPY_CHECK_RUNNING +from pip._internal.utils.urls import get_url_scheme + +if MYPY_CHECK_RUNNING: + from typing import ( + Any, Dict, Iterable, Iterator, List, Mapping, Optional, Text, Tuple, + Type, Union + ) + from pip._internal.cli.spinners import SpinnerInterface + from pip._internal.utils.misc import HiddenText + from pip._internal.utils.subprocess import CommandArgs + + AuthInfo = Tuple[Optional[str], Optional[str]] + + +__all__ = ['vcs'] + + +logger = logging.getLogger(__name__) + + +def is_url(name): + # type: (Union[str, Text]) -> bool + """ + Return true if the name looks like a URL. + """ + scheme = get_url_scheme(name) + if scheme is None: + return False + return scheme in ['http', 'https', 'file', 'ftp'] + vcs.all_schemes + + +def make_vcs_requirement_url(repo_url, rev, project_name, subdir=None): + # type: (str, str, str, Optional[str]) -> str + """ + 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 + + +def find_path_to_setup_from_repo_root(location, repo_root): + # type: (str, str) -> Optional[str] + """ + Find the path to `setup.py` by searching up the filesystem from `location`. + Return the path to `setup.py` relative to `repo_root`. + Return None if `setup.py` is in `repo_root` or cannot be found. + """ + # 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 + + if samefile(repo_root, location): + return None + + return os.path.relpath(location, repo_root) + + +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[CommandArgs] + ): + # 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 + self.branch_name = None # type: Optional[str] + + def __repr__(self): + # type: () -> str + return ''.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: () -> CommandArgs + """ + Return the VCS-specific command arguments. + """ + args = [] # type: CommandArgs + 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, 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): + # type: () -> Iterator[str] + return self._registry.__iter__() + + @property + def backends(self): + # type: () -> List[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, name): + # type: (str) -> None + if name in self._registry: + del self._registry[name] + + def get_backend_for_dir(self, location): + # type: (str) -> Optional[VersionControl] + """ + Return a VersionControl object if a repository of that type is found + at the given directory. + """ + vcs_backends = {} + for vcs_backend in self._registry.values(): + repo_path = vcs_backend.get_repository_root(location) + if not repo_path: + continue + logger.debug('Determine that %s uses VCS: %s', + location, vcs_backend.name) + vcs_backends[repo_path] = vcs_backend + + if not vcs_backends: + return None + + # Choose the VCS in the inner-most directory. Since all repository + # roots found here would be either `location` or one of its + # parents, the longest path should have the most path components, + # i.e. the backend representing the inner-most repository. + inner_most_repo_path = max(vcs_backends, key=len) + return vcs_backends[inner_most_repo_path] + + def get_backend_for_scheme(self, scheme): + # type: (str) -> Optional[VersionControl] + """ + Return a VersionControl object or None. + """ + for vcs_backend in self._registry.values(): + if scheme in vcs_backend.schemes: + return vcs_backend + return None + + def get_backend(self, name): + # type: (str) -> Optional[VersionControl] + """ + Return a VersionControl object or None. + """ + name = name.lower() + return self._registry.get(name) + + +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): + # type: (str) -> bool + """ + 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, location): + # type: (str) -> Optional[str] + """ + Return the path to setup.py, relative to the repo root. + Return None if setup.py is in the repo root. + """ + return None + + @classmethod + def get_requirement_revision(cls, repo_dir): + # type: (str) -> str + """ + 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): + # type: (str, str) -> Optional[str] + """ + 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 + + @staticmethod + def get_base_rev_args(rev): + # type: (str) -> List[str] + """ + Return the base revision arguments for a vcs command. + + Args: + rev: the name of a revision to install. Cannot be None. + """ + raise NotImplementedError + + def is_immutable_rev_checkout(self, url, dest): + # type: (str, str) -> bool + """ + Return true if the commit hash checked out at dest matches + the revision in url. + + Always return False, if the VCS does not support immutable commit + hashes. + + This method does not check if there are local uncommitted changes + in dest after checkout, as pip currently has no use case for that. + """ + return False + + @classmethod + def make_rev_options(cls, rev=None, extra_args=None): + # type: (Optional[str], Optional[CommandArgs]) -> 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, url): + # type: (str, HiddenText) -> None + """ + Export the repository at the url to the destination location + i.e. only download the files, without vcs informations + + :param url: the repository URL starting with a vcs prefix. + """ + raise NotImplementedError + + @classmethod + def get_netloc_and_auth(cls, netloc, scheme): + # type: (str, str) -> Tuple[str, Tuple[Optional[str], Optional[str]]] + """ + 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 +://, " + "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) + if not rev: + raise InstallationError( + "The URL {!r} has an empty revision (after @) " + "which is not supported. Include a revision after @ " + "or remove @ from the URL.".format(url) + ) + url = urllib_parse.urlunsplit((scheme, netloc, path, query, '')) + return url, rev, user_pass + + @staticmethod + def make_rev_args(username, password): + # type: (Optional[str], Optional[HiddenText]) -> CommandArgs + """ + Return the RevOptions "extra arguments" to use in obtain(). + """ + return [] + + def get_url_rev_options(self, url): + # type: (HiddenText) -> Tuple[HiddenText, RevOptions] + """ + Return the URL and RevOptions object to use in obtain() and in + some cases export(), as a tuple (url, rev_options). + """ + secret_url, rev, user_pass = self.get_url_rev_and_auth(url.secret) + username, secret_password = user_pass + password = None # type: Optional[HiddenText] + if secret_password is not None: + password = hide_value(secret_password) + extra_args = self.make_rev_args(username, password) + rev_options = self.make_rev_options(rev, extra_args=extra_args) + + return hide_url(secret_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)) + + def fetch_new(self, dest, url, rev_options): + # type: (str, HiddenText, RevOptions) -> None + """ + 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): + # type: (str, HiddenText, RevOptions) -> None + """ + Switch the repo at ``dest`` to point to ``URL``. + + Args: + rev_options: a RevOptions object. + """ + raise NotImplementedError + + def update(self, dest, url, rev_options): + # type: (str, HiddenText, RevOptions) -> None + """ + 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): + # type: (str, Optional[str]) -> bool + """ + 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, url): + # type: (str, HiddenText) -> None + """ + Install or update in editable mode the package represented by this + VersionControl object. + + :param dest: the repository directory in which to install or update. + :param url: the repository URL starting with a vcs prefix. + """ + url, rev_options = self.get_url_rev_options(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.secret): + 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? {}'.format( + 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, url): + # type: (str, HiddenText) -> None + """ + Clean up current location and download the url repository + (and vcs infos) into location + + :param url: the repository URL starting with a vcs prefix. + """ + if os.path.exists(location): + rmtree(location) + self.obtain(location, url=url) + + @classmethod + def get_remote_url(cls, location): + # type: (str) -> str + """ + 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): + # type: (str) -> str + """ + Return the current commit id of the files at the given location. + """ + raise NotImplementedError + + @classmethod + def run_command( + cls, + cmd, # type: Union[List[str], CommandArgs] + 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] + log_failed_cmd=True # type: bool + ): + # type: (...) -> 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 = make_command(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, + log_failed_cmd=log_failed_cmd) + 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 {cls.name!r} - do you have ' + '{cls.name!r} installed and in your ' + 'PATH?'.format(**locals())) + 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 get_repository_root(cls, location): + # type: (str) -> Optional[str] + """ + Return the "root" (top-level) directory controlled by the vcs, + or `None` if the directory is not in any. + + 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. + """ + if cls.is_repository_directory(location): + return location + return None diff --git a/venv/lib/python3.8/site-packages/pip/_internal/wheel_builder.py b/venv/lib/python3.8/site-packages/pip/_internal/wheel_builder.py new file mode 100644 index 0000000..fcaeeb6 --- /dev/null +++ b/venv/lib/python3.8/site-packages/pip/_internal/wheel_builder.py @@ -0,0 +1,309 @@ +"""Orchestrator for building wheels from InstallRequirements. +""" + +# The following comment should be removed at some point in the future. +# mypy: strict-optional=False + +import logging +import os.path +import re +import shutil + +from pip._internal.models.link import Link +from pip._internal.operations.build.wheel import build_wheel_pep517 +from pip._internal.operations.build.wheel_legacy import build_wheel_legacy +from pip._internal.utils.logging import indent_log +from pip._internal.utils.misc import ensure_dir, hash_file, is_wheel_installed +from pip._internal.utils.setuptools_build import make_setuptools_clean_args +from pip._internal.utils.subprocess import call_subprocess +from pip._internal.utils.temp_dir import TempDirectory +from pip._internal.utils.typing import MYPY_CHECK_RUNNING +from pip._internal.utils.urls import path_to_url +from pip._internal.vcs import vcs + +if MYPY_CHECK_RUNNING: + from typing import ( + Any, Callable, Iterable, List, Optional, Pattern, Tuple, + ) + + from pip._internal.cache import WheelCache + from pip._internal.req.req_install import InstallRequirement + + BinaryAllowedPredicate = Callable[[InstallRequirement], bool] + BuildResult = Tuple[List[InstallRequirement], List[InstallRequirement]] + +logger = logging.getLogger(__name__) + + +def _contains_egg_info( + s, _egg_info_re=re.compile(r'([a-z0-9_.]+)-([a-z0-9_.!+-]+)', re.I)): + # type: (str, Pattern[str]) -> bool + """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_build( + req, # type: InstallRequirement + need_wheel, # type: bool + check_binary_allowed, # type: BinaryAllowedPredicate +): + # type: (...) -> bool + """Return whether an InstallRequirement should be built into a wheel.""" + if req.constraint: + # never build requirements that are merely constraints + return False + if req.is_wheel: + if need_wheel: + logger.info( + 'Skipping %s, due to already being wheel.', req.name, + ) + return False + + if need_wheel: + # i.e. pip wheel, not pip install + return True + + # From this point, this concerns the pip install command only + # (need_wheel=False). + + if req.editable or not req.source_dir: + return False + + if not check_binary_allowed(req): + logger.info( + "Skipping wheel build for %s, due to binaries " + "being disabled for it.", req.name, + ) + return False + + if not req.use_pep517 and not is_wheel_installed(): + # we don't build legacy requirements if wheel is not installed + logger.info( + "Using legacy setup.py install for %s, " + "since package 'wheel' is not installed.", req.name, + ) + return False + + return True + + +def should_build_for_wheel_command( + req, # type: InstallRequirement +): + # type: (...) -> bool + return _should_build( + req, need_wheel=True, check_binary_allowed=_always_true + ) + + +def should_build_for_install_command( + req, # type: InstallRequirement + check_binary_allowed, # type: BinaryAllowedPredicate +): + # type: (...) -> bool + return _should_build( + req, need_wheel=False, check_binary_allowed=check_binary_allowed + ) + + +def _should_cache( + req, # type: InstallRequirement +): + # type: (...) -> Optional[bool] + """ + Return whether a built InstallRequirement can be stored in the persistent + wheel cache, assuming the wheel cache is available, and _should_build() + has determined a wheel needs to be built. + """ + if not should_build_for_install_command( + req, check_binary_allowed=_always_true + ): + # never cache if pip install would not have built + # (editable mode, etc) + return False + + if req.link and req.link.is_vcs: + # VCS checkout. Do not cache + # unless it points to an immutable commit hash. + assert not req.editable + assert req.source_dir + vcs_backend = vcs.get_backend_for_scheme(req.link.scheme) + assert vcs_backend + if vcs_backend.is_immutable_rev_checkout(req.link.url, req.source_dir): + return True + return False + + base, ext = req.link.splitext() + if _contains_egg_info(base): + return True + + # Otherwise, do not cache. + return False + + +def _get_cache_dir( + req, # type: InstallRequirement + wheel_cache, # type: WheelCache +): + # type: (...) -> str + """Return the persistent or temporary cache directory where the built + wheel need to be stored. + """ + cache_available = bool(wheel_cache.cache_dir) + if cache_available and _should_cache(req): + cache_dir = wheel_cache.get_path_for_link(req.link) + else: + cache_dir = wheel_cache.get_ephem_path_for_link(req.link) + return cache_dir + + +def _always_true(_): + # type: (Any) -> bool + return True + + +def _build_one( + req, # type: InstallRequirement + output_dir, # type: str + build_options, # type: List[str] + global_options, # type: List[str] +): + # type: (...) -> Optional[str] + """Build one wheel. + + :return: The filename of the built wheel, or None if the build failed. + """ + try: + ensure_dir(output_dir) + except OSError as e: + logger.warning( + "Building wheel for %s failed: %s", + req.name, e, + ) + return None + + # Install build deps into temporary directory (PEP 518) + with req.build_env: + return _build_one_inside_env( + req, output_dir, build_options, global_options + ) + + +def _build_one_inside_env( + req, # type: InstallRequirement + output_dir, # type: str + build_options, # type: List[str] + global_options, # type: List[str] +): + # type: (...) -> Optional[str] + with TempDirectory(kind="wheel") as temp_dir: + if req.use_pep517: + wheel_path = build_wheel_pep517( + name=req.name, + backend=req.pep517_backend, + metadata_directory=req.metadata_directory, + build_options=build_options, + tempd=temp_dir.path, + ) + else: + wheel_path = build_wheel_legacy( + name=req.name, + setup_py_path=req.setup_py_path, + source_dir=req.unpacked_source_directory, + global_options=global_options, + build_options=build_options, + tempd=temp_dir.path, + ) + + if wheel_path is not None: + wheel_name = os.path.basename(wheel_path) + dest_path = os.path.join(output_dir, wheel_name) + try: + wheel_hash, length = hash_file(wheel_path) + shutil.move(wheel_path, dest_path) + logger.info('Created wheel for %s: ' + 'filename=%s size=%d sha256=%s', + req.name, wheel_name, length, + wheel_hash.hexdigest()) + logger.info('Stored in directory: %s', output_dir) + return dest_path + except Exception as e: + logger.warning( + "Building wheel for %s failed: %s", + req.name, e, + ) + # Ignore return, we can't do anything else useful. + if not req.use_pep517: + _clean_one_legacy(req, global_options) + return None + + +def _clean_one_legacy(req, global_options): + # type: (InstallRequirement, List[str]) -> bool + clean_args = make_setuptools_clean_args( + req.setup_py_path, + global_options=global_options, + ) + + logger.info('Running setup.py clean for %s', req.name) + 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( + requirements, # type: Iterable[InstallRequirement] + wheel_cache, # type: WheelCache + build_options, # type: List[str] + global_options, # type: List[str] +): + # type: (...) -> BuildResult + """Build wheels. + + :return: The list of InstallRequirement that succeeded to build and + the list of InstallRequirement that failed to build. + """ + if not requirements: + return [], [] + + # Build the wheels. + logger.info( + 'Building wheels for collected packages: %s', + ', '.join(req.name for req in requirements), + ) + + with indent_log(): + build_successes, build_failures = [], [] + for req in requirements: + cache_dir = _get_cache_dir(req, wheel_cache) + wheel_file = _build_one( + req, cache_dir, build_options, global_options + ) + if wheel_file: + # Update the link for this. + req.link = Link(path_to_url(wheel_file)) + req.local_file_path = req.link.file_path + assert req.link.is_wheel + build_successes.append(req) + else: + build_failures.append(req) + + # notify success/failure + if build_successes: + logger.info( + 'Successfully built %s', + ' '.join([req.name for req in build_successes]), + ) + if build_failures: + logger.info( + 'Failed to build %s', + ' '.join([req.name for req in build_failures]), + ) + # Return a list of requirements that failed to build + return build_successes, build_failures diff --git a/venv/lib/python3.8/site-packages/pip/_vendor/__init__.py b/venv/lib/python3.8/site-packages/pip/_vendor/__init__.py new file mode 100644 index 0000000..c3db83f --- /dev/null +++ b/venv/lib/python3.8/site-packages/pip/_vendor/__init__.py @@ -0,0 +1,114 @@ +""" +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("appdirs") + vendored("cachecontrol") + vendored("certifi") + vendored("colorama") + vendored("contextlib2") + vendored("distlib") + vendored("distro") + vendored("html5lib") + 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("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("resolvelib") + vendored("toml") + vendored("toml.encoder") + vendored("toml.decoder") + vendored("urllib3") diff --git a/venv/lib/python3.8/site-packages/pip/_vendor/appdirs.py b/venv/lib/python3.8/site-packages/pip/_vendor/appdirs.py new file mode 100644 index 0000000..8bd9c9c --- /dev/null +++ b/venv/lib/python3.8/site-packages/pip/_vendor/appdirs.py @@ -0,0 +1,633 @@ +#!/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 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' +elif sys.platform == 'cli' and os.name == 'nt': + # Detect Windows in IronPython to match pip._internal.utils.compat.WINDOWS + # Discussion: + system = 'win32' +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 ".". + 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 + + for a discussion of issues. + + Typical user data directories are: + Mac OS X: ~/Library/Application Support/ # or ~/.config/, if the other does not exist + 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 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 ".". + 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/', + if XDG_DATA_DIRS is not set + + Typical site data directories are: + Mac OS X: /Library/Application Support/ + Unix: /usr/local/share/ or /usr/share/ + Win XP: C:\Documents and Settings\All Users\Application Data\\ + Vista: (Fail! "C:\ProgramData" is a hidden *system* directory on Vista.) + Win 7: C:\ProgramData\\ # 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.path.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 ".". + 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 + + for a discussion of issues. + + Typical user config directories are: + Mac OS X: same as user_data_dir + Unix: ~/.config/ # 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/". + """ + 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 + + +# for the discussion regarding site_config_dir locations +# see +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 ".". + 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/', if XDG_CONFIG_DIRS is not set + + Typical site config directories are: + Mac OS X: same as site_data_dir + Unix: /etc/xdg/ or $XDG_CONFIG_DIRS[i]/ 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 (missing or empty) + # see + # only first, if multipath is False + path = os.getenv('XDG_CONFIG_DIRS') or '/etc/xdg' + pathlist = [os.path.expanduser(x.rstrip(os.sep)) for x in path.split(os.pathsep) if x] + if appname: + if version: + appname = os.path.join(appname, version) + pathlist = [os.path.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 ".". + 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/ + Unix: ~/.cache/ (XDG default) + Win XP: C:\Documents and Settings\\Local Settings\Application Data\\\Cache + Vista: 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` above). 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. + 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")) + # 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 not PY3 and isinstance(path, unicode): + path = _win_path_to_bytes(path) + 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 ".". + 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 + + for a discussion of issues. + + Typical user state directories are: + Mac OS X: same as user_data_dir + Unix: ~/.local/state/ # or in $XDG_STATE_HOME, if defined + Win *: same as user_data_dir + + For Unix, we follow this Debian proposal + to extend the XDG spec and support $XDG_STATE_HOME. + + That means, by default "~/.local/state/". + """ + 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 ".". + 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/ + Unix: ~/.cache//log # or under $XDG_CACHE_HOME if defined + Win XP: C:\Documents and Settings\\Local Settings\Application Data\\\Logs + Vista: C:\Users\\AppData\Local\\\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 + # . + 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 + # . + 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 + # . + 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 + + +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 + + +#---- 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/venv/lib/python3.8/site-packages/pip/_vendor/cachecontrol/compat.py b/venv/lib/python3.8/site-packages/pip/_vendor/cachecontrol/compat.py new file mode 100644 index 0000000..33b5aed --- /dev/null +++ b/venv/lib/python3.8/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/venv/lib/python3.8/site-packages/pip/_vendor/cachecontrol/controller.py b/venv/lib/python3.8/site-packages/pip/_vendor/cachecontrol/controller.py new file mode 100644 index 0000000..dafe55c --- /dev/null +++ b/venv/lib/python3.8/site-packages/pip/_vendor/cachecontrol/controller.py @@ -0,0 +1,376 @@ +""" +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 = DictCache() if cache is None else cache + 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 + + # https://tools.ietf.org/html/rfc7234#section-4.1: + # A Vary header field-value of "*" always fails to match. + # Storing such a response leads to a deserialization warning + # during cache lookup and is not allowed to ever be served, + # so storing it can be avoided. + if "*" in response_headers.get("vary", ""): + logger.debug('Response header has "Vary: *"') + 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/venv/lib/python3.8/site-packages/pip/_vendor/cachecontrol/serialize.py b/venv/lib/python3.8/site-packages/pip/_vendor/cachecontrol/serialize.py new file mode 100644 index 0000000..3b6ec2d --- /dev/null +++ b/venv/lib/python3.8/site-packages/pip/_vendor/cachecontrol/serialize.py @@ -0,0 +1,188 @@ +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. + # This case is also handled in the controller code when creating + # a cache entry, but is left here for backwards compatibility. + 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, raw=False) + except ValueError: + return + + return self.prepare_response(request, cached) diff --git a/venv/lib/python3.8/site-packages/pip/_vendor/cachecontrol/wrapper.py b/venv/lib/python3.8/site-packages/pip/_vendor/cachecontrol/wrapper.py new file mode 100644 index 0000000..d8e6fc6 --- /dev/null +++ b/venv/lib/python3.8/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 = DictCache() if cache is None else cache + 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/venv/lib/python3.8/site-packages/pip/_vendor/chardet/__init__.py b/venv/lib/python3.8/site-packages/pip/_vendor/chardet/__init__.py new file mode 100644 index 0000000..0f9f820 --- /dev/null +++ b/venv/lib/python3.8/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/venv/lib/python3.8/site-packages/pip/_vendor/chardet/__pycache__/__init__.cpython-38.pyc b/venv/lib/python3.8/site-packages/pip/_vendor/chardet/__pycache__/__init__.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..3c6212b28fda14d68f2e5570a37024d1b62ef947 GIT binary patch literal 865 zcmYjQPjAyO6t|N!NduEMIKTzT2M$zXOSOO6CWMe+5?lsi8xp;wUhKBn5<56qMsvS@a zF`T0UcW~i!9Bh3jcMGrM)wY{A3cuqQK_>v;V~ww<6Ego0wSr$5;4R0v?>??uwb8<6 zs=!4&(I~F;;Ueat}#Ynh1UpJI+O1a0h!YwdcX-6 zsbrbx6WOc%J}?&KWUM)gC0rM0M4p_2QFB|=sVvCiYsgd5mwJzU zZeO;VCd#TzRGI=2)BKlW8g)0*Jc1JYN=aq>o>T?Z#tdMM&^XIlrWpkJDUA*l61yZu{HUIzs literal 0 HcmV?d00001 diff --git a/venv/lib/python3.8/site-packages/pip/_vendor/chardet/__pycache__/big5freq.cpython-38.pyc b/venv/lib/python3.8/site-packages/pip/_vendor/chardet/__pycache__/big5freq.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..796be5e12e61a1966ae0d19b03cb9446f0a7fb11 GIT binary patch literal 27194 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 OzI=cMAqg6^eC*#oR26Ih literal 0 HcmV?d00001 diff --git a/venv/lib/python3.8/site-packages/pip/_vendor/chardet/__pycache__/chardistribution.cpython-38.pyc b/venv/lib/python3.8/site-packages/pip/_vendor/chardet/__pycache__/chardistribution.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..45f44beb1e99eed5856bac93b5d077b1cc8d2cae GIT binary patch literal 6235 zcmcgwNpBp-74B^|uERxKw2l_on!ple$xF(LiYA-Hb{m& zOHil)Uk| z>8sb?zS>UUK%=Wk;@Q2XqePBQ&(B?NC$L|2G>JPCHXJ2venJ1KcEvcMrtXY+Y!!A#~dmhHVMcjtI++iin6l)$Evvi8$VXjp)KVA-Y8m-d&5G?eQWK< z!>m5NRX3qSo`BhVSf_?FDBcwiq17}eq|g*~Ls3&vOVLP8dlr4J@enL(yLu)a3x=FL zG;#cjvnt(3PDKh=2HlRITPeDZSE#zdaesTWkk1tz(WI^;bI>}7IHFQ^gC5NHF@mBz z%vPd8CL&$W^>_8a^4#L;t|m`G)&JBcHf7;)&UYss=j6m{rHFs^CN}dEH=b-he&=fW z=2c&a8!O(sS5`~a()BCqSu0O0O>7o6FKv}KbNLNdTzc}*b&Hoaa@*^bo7cA4H{m>X z%c3GD`0v8?!*`S7mp8YAxZ@Pc1>bQl({_CzniQYSv?)jNmSZI2BqvB{x{#b^ z?KsHyQM@RKWYww3(cVKhOI{)9V^nHD_l6;dffB8zKi7B6vL%civt|l& zBO;e-z~;IxtnUJNj$Sis+Pbl!$t3|?tz*=bX_d6{%c`+mg%s zu1q~C_zzPX<;s(Cs_2&2{D*a-x&93vnLveeMXnwr&mvPG$H4;Y&O z*a4frp?f2!0R*s*E>F@ri>i;U8>Y-OdfBFrky^vr32jHOaZUs_&D3-?BLeFg)r->g zw`4h$uas8{!Y$|B)Jo2SK`Z5y`zg>}D6geN&IgpBL^^r^oM7bsd}jXs@_py_)Y6o* zbT>P@cz5C6?I5Xf@8Cph2^=shgQ_?hy?v>72k2o zm94dh4(%f_-{Y=nX7yrcEn6q$)dWt$!#Y(}hlVFeV58DTMxH}m*=)~*@s({KF4RA4 zNqHV43n-x?k?Oh5itK?tlkTP0D(6bB;{;ufQ>ut9IA{m1*Lk#+D~3Jt5|#K4Nt)y$ z$r%#*3i&3cHs*RAkp#QWokcwamjk4TQlqqICc34FW@ zAbauhb8S@@aDq?ch?}Oc5Cg5x;YVyDI}(hme5PF>d;uI3&!WWpnvbLMneohEIP>9( z9Lt9ra-x0=G|m7Qf+i6}ysb@F%4?V#8iRWnIuFW|%9n5})mczDLcB^iPQIA)JjV%b zq%5OsiIH9$L<8m-FskP|F5)0wG-GIQB{P)7&|QOx#%L%jwU8@D@;Q+%mHto4u)OqxO ziV`j=5{D}sGpgr1FKXWx??xrnmv56?B~b>Vz@WEbzr0&4?%7a5>^jG(Kjw}+Lh)$) zxZV?0dz1&$@|X?of5p{D*>4qB@*U_UKOj-7jlFRK-}MhYh`xTKd5``uCJem{ zs4-pqWq2tpJTQ2IUzP~L0$+Dg_nJl?1q_`@;UN*7!-7S zF0uyE(ewP8>M3^`8Pt>duu;8m#7bWRKa&vn1%>Y8hb^xJKV#E{wd=0|J>0SoJ>9G+ zr^1vdL{BekM>>_?V?--@&UVuLC9v}`q;Z``z3(HUHBcv?gNP} zuLekq_!jXRfOHoMhd|;jHw02YYswAr`g1gp{(=#QfYeFz_CR7#G*M_dPl9v=(iONK z3%8{!2Y}hJ%5R`G3fGjyJBHq@>Wz-!1KlXnBaE;_0yibAMu*ikh86&_8~4z%NT80p zL%OFKVn2lr_1}>EiR3GiuSpn;L!8Z#{Rvs(EFC$>qLfFI)9iX;&#~sOj^hkJ6i80@W{>lGsW-Yku9$Tk4(cqeDE3o`2ZUKKY$Fleibd6 zlkfJbw?`z=$TCAo%w9u89!kCu`c(2AQOS4PKv;i<$zi&rG9{HW?FT}9!r>>0Fc4l~ zt&P+A3hj;9*OYa4Sj|fygx38hW$q({Et&MmAmMK1{U`h`Ck-@7T0nA?>e-O0M`f@o zd1c-jbgixMyRYh^$)!qQn7ah=6$C=np#qeOO$ literal 0 HcmV?d00001 diff --git a/venv/lib/python3.8/site-packages/pip/_vendor/chardet/__pycache__/charsetgroupprober.cpython-38.pyc b/venv/lib/python3.8/site-packages/pip/_vendor/chardet/__pycache__/charsetgroupprober.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..1de2e4a5c91611732c6538fa0064a87560f0a05a GIT binary patch literal 2266 zcmbtV&2Jk;6rY*>@Y=D{5YjfZAW~FB7ty)|D6Lc|(9#GbT-2l&OK9YH#;zTIWoDf! zX2YeHDj^|p;15WSz4Ra9KiC`M)Dw43{NAiXl9C%^zqjwr%+9=z-+TM0QYkTv%WwUx zexGOTPg0H#AIcXX`vI6@iuYKb*EuIy^hDpQdz`H?o1H;>J!vEwdgbophFRAMO>5J{rW1p1q+uU|X;$Zo)rFdW##&qn?yB@kRw%eazT~U+?3TLRaO-|i_e(P>Lu89CR>~tux5tiQEo-8Dcq$w zBiWmgy#gU&2~XKm(G@9I{1Fgl5)t!c^wuU{WjjfKF)f-PnJ0GtOZmlLt-Pp7@uebV>{wLSn zXb-9z)lp}3bvzg~np;|3eeziA-qo$fZg+U=qg`67${jsWLsM-cV@fCPX?4=3z~zQr z8||71HeCW)5*T0PA%2sK|BdpsL&uv0jl)6FrnpeV?YCfu(cTva43S3UrI)guTuAwB4f&8kir2fI@;+q(JGU>me1t9^e$9k9al<$)7EA$3o`k??cw&k)K_!rd zc`D2tlvtRuo6DUv720)%*aETFz}^E{(ioqIyCv@P2+svRSw6*aci9QA$sqFlF37Hc z!9!-3Q?}2^>s%urNa~7pWW5%9&bn;AtrOWC4q6?h2Th%oDL?Q=4jO&^oIBfdbB}C3 zhbum$yIj4C&{^yA$=jz`d;GZnXS7+ul{2%=S;$#L#W8NQwK)&n6FYQZGfs9 z3M2vvpp^nvLO@YSz!7kZl(e)qKlY8Fa28T&arqs`q!W6GG0R(D8R7)<*zd?YbjA}FC zU4c+7DUQIA=0ST$0l&)I6Rt9S>Qugb&SxG@fE?H6l*OqO2!b*KH~5{c70 zZ5Zpur1Gv^84Qz^yzVDwq2hm3Rx6)DkIQ*jDMRK0u{ViPlQ@>eus3W2(OIDM=D2OB z)*RXm;W-ZSf~53hMc3Nt5|^{gcj7Baqe6KEvhRa2mm=RK%%91W!UZHT;^#nl=RqxB zIwf@{TSbf}GDjaE>J<0@m89$s)woA;S<3BkqnEF^rtTVD7NH5^+m-C*evwOtV3pT% zW%ea%>RwOEWAJl5)3r;g#}$xkml2;2%O&8iKol*xZd{9lEYSD}+PoAhvko+Czw~8r Y`;Hy|FT}IxcJ9LGlo?Bxm3i6w2SPyV6#xJL literal 0 HcmV?d00001 diff --git a/venv/lib/python3.8/site-packages/pip/_vendor/chardet/__pycache__/charsetprober.cpython-38.pyc b/venv/lib/python3.8/site-packages/pip/_vendor/chardet/__pycache__/charsetprober.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..df6613e795df3fa903cb577f06a51fb1e9ae0aa9 GIT binary patch literal 3498 zcmai1Pj4H?6`$FEqG(EXnQDjF*_qil@87)No5ycf zR;mQrCm;Qp|NAl_|G>%V=D_3*Jn9;BoNyYGR`I4S$}MikR^M*flzdIN!`&BzyTW;H zw_HwlN!@!5yU5OK3VogCwcp6J9VOje9cU5ar_;@b$sKr90-Yehp0q4ZTQ8*FnY9{>-{EU!yaMCP z{9Q9%%6?&Y4Z(AxY$93)-86UAD{d?`-5U2sIc+!cBo#;{iL{UqoM-_Je#DrDWt z9VOz9TmnI_h+Ki!e};`rMhC#KaS+HxCylv~s*#0_>xbFFPrpcRe4#nN-c~=q*6EM> zKf7jHH)%8*S(I%Ik}L@K1>ZRA2@!AX2g9f7jW2;`lr{VVk?>SDLhu6@+Psy}neoSF zHbe!ZB+|bBBedRwj!+MNqpNeS+nkbxc<;eO}sWvbQ4Ue+aEx#=6{Y4?1UMI~^hTm!(WPTAP=NpU*cCeK#gg zOYc(SO}WTO*F@;`Qj&Gnm*)HVqKq#Q?$%Bhn3#6HOIIP}{V+{B5f@1)ZUXuA2tS-R zaGvlj`4}|jEZ1P5$qD659^y8TLlQNI^;dwzSUg`Xzz@E1U-&`@Hn($UBBiZ0wSr!VS?Ct~#d9Sk&iPL8Oi zCy?PlXY0rsTldJ5J4g1Ip4ej>Q|q6s_sL623Cv)Qvm55{HgN@PMjn#=jkJeEZkgzJ4J0g#-}?LJoBg zk{}bY672Es3kehQQcklcfO$613?R}Gl7&IS+Gt8T==L*ke3pNvhcZ+^ZE@mgREou9oSWO^JR>xK+0e!--nfa2_lb!F1aWEpy7(imm+22 zs_q*6<(0zWg6FRBckbXTUyi}MKI(TCq3aX8&3WB7dw&TM)m7-oMbGu9^~SL)bj_>L zOVpt?YxLceKUqsaJ z3m!o&6c0DOQkGD=eGXbCxugIMUksXQc4?=&H-$|pZU!IYi6X)QzIS)G z9zHx}>*tWYG=p?gsHMTj#?b&p#CpkZZ*Ln?YVwn!3f??=OkPj=|e))8A^! zk8l~Ww*!FsF1(>Of>IOixL73ed-#<>SNDqB?)ymLMTxJ2Zti4K9OMovl~*7b7Q*d= z3EU1N-Sa0mgC7fx=^gKTg)B+U1B5x&CHPgKD6CM&zGT(xHLFUiwgc}`eIfQ1ajiq; zd{N>blf3Ht{ge-4tcz>D|7;M%lPY;__rb3pHMhQL`ppNA?!#pJp1cTKB$fkt1?tvJ zNIjJBuZGAg*kN9f@8Q=PRy0xA7jTPk`2nu~5Id~!PGuXcxFwdG5^w&G;Zf+|ise{U zw_;mh*{R-v`6m`NwBT*5J&FwIie%7NMRn_??WZEthG>zbymG6s_corZsj@4y0*?GI D%YlQ5 literal 0 HcmV?d00001 diff --git a/venv/lib/python3.8/site-packages/pip/_vendor/chardet/__pycache__/codingstatemachine.cpython-38.pyc b/venv/lib/python3.8/site-packages/pip/_vendor/chardet/__pycache__/codingstatemachine.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..a596c79bb6ec34eededefe2241ea5951f46afdf0 GIT binary patch literal 2925 zcmb7GO>Y}T7@pnrR~*Nbeu!33G$4d((Zo>;Ev*o=w1o=|CVSxt7v_NMDycV_G~ zQOcz~@dG%w$Nr_ga_V2;0?#|^P2$8O$LGFg$;mrPbw@FM6{q_@R7wYON!Hr{%uJ6_&; zETUnYsx2$5ifHO&3DCKR$2>%(SkIHJ=S!~_keQbZkj_A_Dr>TieoZ#y68`J5DVOoz zP)o8US6;FgUau+JF#leD{y`?=bl?omjEg^UmL^BJ&kYgbqkghzKk>dSKV{9z6jiX5-G{>j|UgJMx+JPy66uazDaj9@Xi(OhpDxNBHoyV5%#Yw{Z zN=a^q!p>G_T)`@AMivVl);ujS6>%bV5_O$tsVa71x8OD&sWrY0bMqUbp-2bHz@j+; zw<8P;XDJQyu~ySKo0!>7krJgDHi(w29lBOr;JJB>1?Uv^=dHRU(ZZ~cX{)p{mLwHe zIe7vK*dHhG-+msI5_E_sDVi4J>#Irmq+DVyarJO zpNb@w99~jc%?Fq5GO6Ml6r6l-7$Ybncp%DBE8sLsLKDT)aAl~I$FamkD##^DATpd2^XJV{*}~x`gngH$@KJK?`_rcQe409mkJIz`G>eXM7Ar-J zojO|~+A-wAGPj*b>RFUb5M-q6S-5f+@9S(tUN8t24GYLaHw4EaW70LZHS%%um4(^? zo(Fi$I;unOkcGH;QG2fTB||%~l~+|;drjl@L-wlint8aPasBxl+FiU$c`Y0v5t6(e zMiZ?=3V;w^Ec+CGVWQG}xtMq1o!66WFu>)RFAbFa32mkGMi{1Iq{1+-(JV%)$a7^o zeqJ#u>F2%~yGkCcdX+n0;W3^y4%kZoZNKBQTNu~c>3jy^v(?{<*gm_^?HAF zF#2lUl|kCw?vCT}>LeYD=!uf6dqbs?)hA+qH@kClpXR#ZRHZW0UE+->>lSyhV?Dde z*T(zAHBMt2hPO$<7Aoe|X1|tq@Zs54!&+c3ne7!Ew3oC9uz`T!#g;iSN#wu6XMT~yHo(Ha9#XPM;KpnCQ+pWmjZ5-7h z(CfRtkuOJyFebG0{&CTix65cL2J=?2qK|7e)A_RV!fZVqvs2Sm{5e^iPWmEsYgE-~ zzT%|Hy(n+GxO8xN(*>#nD+HvFaw_J{R6VnyoBAGuhVWR>_FC}mIlt}s|5W^QUfVmk zx0&FIZQ4`11u67Hm1g z9?(?KjDV!z6)(K)?{1>qW`~DBRMuKmTiNMXk zVQrCS`cq8&c0|Qrn206HMrT3ZfWgtuB=DpCgiEY_#O85l+gkQ?a z)L&r2frV@NJiU9RtM@z{_KDNk=?92_kgs<6FW)V9?wb}sz$-{hkZe5gw?v-T)U;QwuO(nd?bbzL!^%U%d*pWGYwVolrd8B8>NI#A3YV^+qmcb(^uI rLIF~xOWvMI`w->5zqMK5}uE5(9cY!%_h-lvn+>5-9|2KaCZT@1b literal 0 HcmV?d00001 diff --git a/venv/lib/python3.8/site-packages/pip/_vendor/chardet/__pycache__/cp949prober.cpython-38.pyc b/venv/lib/python3.8/site-packages/pip/_vendor/chardet/__pycache__/cp949prober.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..c1ad4b63be71e722aba19f10eb50d7eeea6d3187 GIT binary patch literal 1156 zcma)5OHUL*5bmDG?tlv-iqS+b>V+r_K_qTsd@aNSE)rKRbLq@Z_bwyzvb%T5Zn!N8 ziN6Am{H3{i@-KL@su#i#V`5Lb=d0>RRe$yM{@mOwf%fF#cmAeM$WJt?%>m;rbhQNo zC!8iEr2(agt;9<0z&6-UoYW0mgPo+7)`L1F9|(7O?S$}}a3^-)@p^|Wd%q|gwM@(V z+xxAZ&s(w5GVTs^oM-Q{Fd3;>!Q^5q=W*8W=unGx7#+l!0Po%QAkp#rkru6kP<;w;vTNjy-x=$nZQA55jvLVJlA`=7u_y_ZV(gs!KJp704}npC7)rmFCtOJ77m>etR17!Pv*x>T?&F3SW^Vv{jJePh1W#K~mQ32H`OmS9= zk@O(t$Iul5Lg(qM?a}d_3ZJW&cm9i5B)5foQHq=bVb*TxnyqpHT(W_KL1wrGX4$|* zfC-j`sdx$=g{%>4e6Jd_y6FGn)^>6!!tCFHy(G-~gRn3BO3-T;y!d7 z(wq+x#0|#24Z>tPW2h@Vpc3SvNAfltE0=PoK6i6jB3ToEvjOOOSlrbhr2zHyH> zJPU}osPUud7EHM+LYiu8ajG?iZ!ev`J)^|(X4<`=Y+720B`Is*RiI1MsX4lm%T-0Z RSJMl=et-#~4jR;a_8*2e6tVyS literal 0 HcmV?d00001 diff --git a/venv/lib/python3.8/site-packages/pip/_vendor/chardet/__pycache__/enums.cpython-38.pyc b/venv/lib/python3.8/site-packages/pip/_vendor/chardet/__pycache__/enums.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..94d571c0385b320305facb2bba13c7e1c252bdb1 GIT binary patch literal 2663 zcmbtWOK;mo5GM7YD8|miJ+?&`Xj)W2A_Zw1G;UB>587cYQ-(wrjV=_;T}w@t;6FEkLt}vq=?r?W@9^de2bJI{@-2U_z`m?4ezoT$* z6hPPk5+h(#=_`8*RcY>(LUT-gneXR7&eHaC-P09t3OH>D0mvRy0GBrRp zq^yB_jc$OvA>|U?>?xJ&Gj7zwki;h>88Ko;=~%$bONhrANd==I@i-lh;xv&e{>bB$ zC1m3H&%Gh58pgJlCZm{dla?2eM(9OSg?vT5sCv8-cZOpx2&;ZPt{6Vt@QtB@v;!nQ z0mhWR3ir=ZwV$WCeu3uuMOx@cm_y%k& z*&i=z6CRv`akW#A*G}S)f_1ftUwd#iIsN8-^zeQ{>4RhO-My3XZ2av#IfhZqsZD~( z-88}|FnafF#8`Ouxi@_lKm2-%Yc=X%u86&p(5Uv`c=7p7WS;@E zD;vBB{~W4P;U#3KkZm;|TejIV-JW%D*tT{pvz2|cdggTNEz7YwcDp4S;_9$Zu7~0man(lS!wxFmWM~@v0uzK@x1H6ZUKocU=Wf5hGg~ARk^+voDV6Q zjYA$E2hng*Hhb^u`qyPMf!Enusc_7XbLT>-T&si9Z;+W2U zy4p42YfWq$9zlfhe5h#x_!Y)ykY^Hp0gF$AI29z0piLlBzvZMk8t!8ruLjPMnm__; zm8t+9x!LwMXGzKx*DiCj+QxaI@BG0>AcK;kyA ztMXz2-{5IIyVYDpv$^o+boSfT>2Tg<&mBl!gEq8F77EA=E_WFWdXOF=YfKY{Ckd7y zG%-FH5NPY?xr9MXV)(kiys1ai1S)q(LMG(rNkp0*+v#@N1X{lzMK^g=QaS&_cN{c^q|pcXT_Ga2hdeo zo_%SMumtNs0^1O%n4ItqE0h6kOkN^Mx literal 0 HcmV?d00001 diff --git a/venv/lib/python3.8/site-packages/pip/_vendor/chardet/__pycache__/escprober.cpython-38.pyc b/venv/lib/python3.8/site-packages/pip/_vendor/chardet/__pycache__/escprober.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..0d7adbd7e98a2ccb6b75812fb8b798131a5e03b4 GIT binary patch literal 2648 zcma)7OK%)S5bmCtot=H!F-~w02}1~nMI;*xL<5E=8gHI)mPQeOQk%4cJtbg zJe(rrPy9LC4ET8fJ^B_1C!G2upbn+TYrYofj;?s!H-e0l2~5WfvQAdb8NLJqZ3+n!GYdW530sZW zjm3)VwL77x=t=pR8@2{+ODuK#SjeP^`V+OJI961PDn?Rx?6AfPTUlMKJxgYm8>{no z=I0mc7w4X=U7UNmelq959nUp%4z}nUAc8oQ6G!9J(fQO%(xhDH#zD?86puz*PDYqK z%dG>lPn|6AIi6R%#S6T6K%5*e@iL70m&71W0bDbkOxGgsoLh#!=WS>k?M`H$6WD`D zM7C?+@OoU>k=GW1xM92fzU+bP>}F5eT*ShQ!Bw^hK?|7ZW3}6hwvb|T*A{Nn@po-0 zyk0Bp4Bbs%*dp@WzOZ3=)b2E6I}+OiSdM2&0jhpSaJv`UaZhd4;UbJX&0RZgiwIUv z4!W(Rbx%`HO9}GmMjd)|2qY#k9g%<Bok9KLgTQWJH)gB3&cSbj=4uE&{I~W@9VP z?b9RBIQh??{G)CGbiY5570<~^ARC>?%BhUwNmGhWD|$)MnGrdB>xdwxAs|{EU|H}1 zfUK#|SD8&R(V#D+EWrH}i?L4Fi5W|Bse@S*$RbE2+6jPuyj-s}YOJxmvi59wX}PwT zm=Gcna*0lmStJ&!T2CIXJw#oZ!(a31>U!;AU1Gu}Q!YetN5J09>kYyX??JUMJ3F9U-En2L z+4H%OQMKY(LCHO4>mhf+F zxx3xo{ZDsMR%JV2ZBJJ5qHyaf;0quM34go4E6ZTsN6@2pfe<=FO@8?y-m3#V#uqoiKWaFE1;(wNgxDE-An@251a`vpd01Ztje^q94~s`w<4SN^ z8OEtM*K8X&1O77VOA;~k8ag~eDORropX5XFL!4F-iQq~ywe|JY^%Uc1Xkyk^H`q!| zzOAHby)lVwq4c zO|jynzG6jt6HrD|2;P8AB7_^E7GzK!G6|`v^2&l*l2`K@J&!R}z*HV57F0EO7vf#{0a~L>`VhHKfFz}HvGs)S z!?M$4R3$-G1AI_$sj3DRHO%sKu>dyc*ez4loO-34l*aj(R#Ay*o6L-#cXZCgvpNbg yA{+#fdW|H;YaB%pdGJu3ygus9QEyFJ*-o!_CTYJ+eeo4eMfheFp-RotIpZISDRw6S literal 0 HcmV?d00001 diff --git a/venv/lib/python3.8/site-packages/pip/_vendor/chardet/__pycache__/escsm.cpython-38.pyc b/venv/lib/python3.8/site-packages/pip/_vendor/chardet/__pycache__/escsm.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..af4730d9c2efbaeaab5d2b9e532069d6a3fc1413 GIT binary patch literal 7489 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 Fe*qA%R7U^+ literal 0 HcmV?d00001 diff --git a/venv/lib/python3.8/site-packages/pip/_vendor/chardet/__pycache__/euckrfreq.cpython-38.pyc b/venv/lib/python3.8/site-packages/pip/_vendor/chardet/__pycache__/euckrfreq.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..9a356a6fefbe79dcdcabf9d190f2c0dbb849af4b GIT binary patch literal 12078 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 literal 0 HcmV?d00001 diff --git a/venv/lib/python3.8/site-packages/pip/_vendor/chardet/__pycache__/euckrprober.cpython-38.pyc b/venv/lib/python3.8/site-packages/pip/_vendor/chardet/__pycache__/euckrprober.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..afa4f4b2d013f32586d95392871ae784b6aa27a9 GIT binary patch literal 1157 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 Z`}XfTv`@DR`NKO64!_fAv0}!G@js-W)uI3Z literal 0 HcmV?d00001 diff --git a/venv/lib/python3.8/site-packages/pip/_vendor/chardet/__pycache__/euctwprober.cpython-38.pyc b/venv/lib/python3.8/site-packages/pip/_vendor/chardet/__pycache__/euctwprober.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..08f5fc3b1f1a5e0226ed18564aea2c6c1517c3f1 GIT binary patch literal 1157 zcma)5L2uJA6tb@Kw*jXrk#UnOYatcS4D_b!iH7=4f|H;9EW= z7an>zfxz^jwp2K3kP9iH1*Gs8i?diWCNZFNaBV3vlrWV_Y)NsKFYv%ZBw`|D;Ygu` zzNAG-iKGkPCB~v8REn`n@_XdxG9Ch{{xFn&x1Vq!m7hoc&L|&lZ)Lk%n)98GdbQb2 zC+W*gGkTfd_VYNe53)RrP6V%yj)h3-C*k-@fA_^0eLfqCjQ6DkY!5Kpo7J-Z<%eL(RiZ zV||Pj6~@v&A0&vYjC~!1$;@N8D?H#5^r6Rc4Z_NGFvap!Ya@{)pp|O^R&WclVC3`| zfWk}e(W-0FifvJ|j;gzzcCILx^-3a1#oA133a?)}yM9KA#j0uVg79WqbRmiYVWLn? Y=!IIYJL0{b9q`RVEC_{ArRK5z04OULod5s; literal 0 HcmV?d00001 diff --git a/venv/lib/python3.8/site-packages/pip/_vendor/chardet/__pycache__/gb2312freq.cpython-38.pyc b/venv/lib/python3.8/site-packages/pip/_vendor/chardet/__pycache__/gb2312freq.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..56ab2e9e4e54426f02f504f7b26555f78d8e7eb0 GIT binary patch literal 19122 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 Zz4~+-)U$Ji0^uV3$4_w*#7hwGe*kcnhnoNZ literal 0 HcmV?d00001 diff --git a/venv/lib/python3.8/site-packages/pip/_vendor/chardet/__pycache__/gb2312prober.cpython-38.pyc b/venv/lib/python3.8/site-packages/pip/_vendor/chardet/__pycache__/gb2312prober.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..f11c8908315a9b5c58ba0da9116dad19055e7885 GIT binary patch literal 1165 zcma)5O>fgc5Z(1x+$b%js7N3E5y#2hmSSK)^Jp9f}kC2}@nI8^J-oUH& z!EnN9N-`Q!ir7l6%nogX?bONK&^6dey{s12C^;b9<=z?Lo}g1Z^m(mA*8E=-LaovA zZhM&OWN)lR>nM^Pp+Cw&S4i-zv<5uM`yCx=(T?JyBp0xH|6s4V^}M;CC@qujP$z@@ zZ62j#l_>Dus=TbzX6+CA@7~9dw9ZM7a>}hUKeUCzZSI_rDGgn~ zrgFgEIdMpcmDkEfm82>(#&gUDV$6WAdIm-lO;5?xy0Cr_O0=z=Da@Ixy(xij?UY>j z=-~t+)5Eoe(pAGkNC{marO#NBCz>&d0i}y;3$bB^sZ3&5%2htc1dEW0iIAl$g%bLb zmW~o>555bG#c8AzW0&Ojx1f;82zU)fkqml+lnbeXA`W&=iqUp6-)(Bnce?83W-ps$ zFE-8S=fS6-NQ(7gUPSS+;Oi$xLZs`*(fG??cWaEkfQ>}X2QrAEnOx|g-^Fefrl%Xl zSo%=&V|Wz;LYHWr`gF27&*;YN)PGTn^tSM4JUOR8nRQrt)xUBX)?@>XLB`yMY1zP9 zfDD#LnRp5-$^t{I$-Ns{=Xd>I=2{Cisl>li`)QQ-hf!ak7G`O09(J~4&Bu0QiHwzX z#uMp<>FO;W09sHm}?G}a0?P)H1!C8!prW{ zhG)^bZBet1(z}&)uSlr+C6T9cr4pRNYnWBn(8#e|Ha(tGUnNKnq9{-&s?~&^tL3^W S{;TSMuODDRD24_#kM##ilNKZZ literal 0 HcmV?d00001 diff --git a/venv/lib/python3.8/site-packages/pip/_vendor/chardet/__pycache__/hebrewprober.cpython-38.pyc b/venv/lib/python3.8/site-packages/pip/_vendor/chardet/__pycache__/hebrewprober.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..9314e8c7041a7daa86422507ef5220c19404dc63 GIT binary patch literal 3038 zcmZuzTTdHD6rP#AuwE92Yr-YhD!sT0I5$#g+Oz~5f*MSONh_^Xt&C@F3})@j8VIf< zRSHt7{)9XcsV{knsy_4=^jFMV)hAl@BK7i+7kbXD?bwjD=9_co<}>r1b7mg&^wjaNVa*G*{pg(87HXeBx40T*j%ZaRw`EuEBNJ zPPDs&U%}{u-w=LB_ygfjguf8}M)(KeUxfb<41mH{gD>ZQuI+tNvllW;<5N?U z)8kV$XQvqO@=iE@4YtfaI4}Afs9Mb^NK;S>Una+%NK)e$;$zWUa4Ft=0FK^W#P-=_O9(> zxYg=0yo&I*rQ@yoc^DaKCtA}h$`fSVYfNhBhVVnb`hrS5^h_m?*q8LSeK=>4#4U2anp{(aMn+9kc11s&$TK|YUI0Sl z)NK3JhT8d`Xr9|Kx)@BI#YIj7a3ITzka7kxQwWrSOKIr?7(!mr^9fxHfFuSHh7e97 zj3S(E!8w#h0KS6~S^xy?(@k3I@32iBZ>||%I1c5OVYs`xGOUg!LEL4Cz{Ug^^FEuo(-ap|i%l{8AT6zE!2Oi$j--{$ z%-q5Y7VVYhj_$}RhLP|80H!pKJWh3@A0Bas=D1^fz^GSyz(YO0hrIsUeqGw>rRBxO z2AVSNpef@PVvRBPQ{sJC@~FiQSQ3vWEM`ORZihwKgg0rCQpCZ53<12pqF70Y#4aJ`GO55N(< zu7}tMxk>eb41KXiUo5!PSb=q{@Jy|*R&5`RYk(K)%9qWD=fOH#1(&!0b_;bkbwkd> zJ4oQZq?uk?@fK5ZC|@i=o3~ahta+=uRo^QIrQIm>@D_|BQ3d7|R53bbY%aBunq65) z&o?8)Qd7gKkXmj_9OXmL`#%2(W`^$okTHju%w`s~n5{XimpWRnW>fo+QS*>#aSA

HMXP_cum>?UVu?<6vy=|Ah~UtIvnX&%glK4LRDz`o@b~IV#9rqxd1`SX?L`qbdv|8pn_I}N z%%o>ia^T+5{K71__}$d)<&R2;^_T$=% zOLM7vtz}zOs4FboT9uYcLm(aH!@=Z_sVB&8`iEe7hzXYH4z(dgIn>mEfkT}&G~I#( zTkDLP_7ysKL0`k7c){@D6g-ior5W)V+46?0F0E2FC~Rd5{wT~3RUTwVGE8&Cv43pk z_4nW(fgQ+@Rkp4{B))}Qz1FZbLjd8`7Ug?0%J)}cq6!Uh4JFJ}ViMsErV1Q<7Ohn8 zN$^qr=TtrDlvWnj+P^ASo6TE(^yW&&qy^Vldl0A(# zNX{>nJg?1d@E#ZH?nO$!h7w)}u$WGd{S4jEv7I@#Wm`J@;?_+@?NKNiqw39-P8hPM p;X~0iDUd>G_`%j@81aDr)MkQu1E2}g`Y1ZYTWFQKSsm)+{s)7SvuOYT literal 0 HcmV?d00001 diff --git a/venv/lib/python3.8/site-packages/pip/_vendor/chardet/__pycache__/jisfreq.cpython-38.pyc b/venv/lib/python3.8/site-packages/pip/_vendor/chardet/__pycache__/jisfreq.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..fbdaaf92c1c5d06fdf92603bda35e392df936f41 GIT binary patch literal 22150 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 YzWt+z_UTr=Xm|~Nr4|)8PTbi414sAV>;M1& literal 0 HcmV?d00001 diff --git a/venv/lib/python3.8/site-packages/pip/_vendor/chardet/__pycache__/jpcntx.cpython-38.pyc b/venv/lib/python3.8/site-packages/pip/_vendor/chardet/__pycache__/jpcntx.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..1479c3ddf657e29cee166554f4a9b1998847fa05 GIT binary patch literal 37623 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 Re<>mORP6ow@zcj2{vSIZEBOEb literal 0 HcmV?d00001 diff --git a/venv/lib/python3.8/site-packages/pip/_vendor/chardet/__pycache__/langbulgarianmodel.cpython-38.pyc b/venv/lib/python3.8/site-packages/pip/_vendor/chardet/__pycache__/langbulgarianmodel.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..4396fcc7725fa3e751a5afe8f6bdbca61fde2d32 GIT binary patch literal 23647 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 Q?f7royahY#S+lqO2M=h%AOHXW literal 0 HcmV?d00001 diff --git a/venv/lib/python3.8/site-packages/pip/_vendor/chardet/__pycache__/langcyrillicmodel.cpython-38.pyc b/venv/lib/python3.8/site-packages/pip/_vendor/chardet/__pycache__/langcyrillicmodel.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..bcccb3cc0a83805238c326d334d8fcf88ca9253d GIT binary patch literal 29111 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 literal 0 HcmV?d00001 diff --git a/venv/lib/python3.8/site-packages/pip/_vendor/chardet/__pycache__/langgreekmodel.cpython-38.pyc b/venv/lib/python3.8/site-packages/pip/_vendor/chardet/__pycache__/langgreekmodel.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..28cbd38b5ac0099c7a4f98f7a0d6f02790aaadbd GIT binary patch literal 23605 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 E-$bFCK>z>% literal 0 HcmV?d00001 diff --git a/venv/lib/python3.8/site-packages/pip/_vendor/chardet/__pycache__/langhungarianmodel.cpython-38.pyc b/venv/lib/python3.8/site-packages/pip/_vendor/chardet/__pycache__/langhungarianmodel.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..d80aac36369012cd4f5a669a167913ec59dfff97 GIT binary patch literal 23636 zcmdU%cbJ`J5ydx~LJ|@sgd!llCE~JSAt6zu2nt9EN)TPky5wGxC7a##+f72Eg1unx zs3^Vns#vl2uBapAIm zwevZ(c?Yz$-L2&R|83Ds4sZT<_@E}#+Pd3n6S^nV+PmB9ZT0rr#M-3V-r7UuEijl}WW}C%4U=KBsL$TU+Bl&HkbpREEaC?=Nn@f7*&wg?AhOlKsSSgT_B) zHK`SN;Kv(}Z2C3k2o?xB1iJ~k1kKN{t+9__U%`CAUV^!TJp_9SItBX+juz}E*j=!< zV3A;+;BdiFf`bM629#f@q0gwXwO~iVEP+0##!iA6f^7uT1ltMp{c21R%oONgjj4j| z1xE_>1#L_g=t7Nc1+xW52o4o&CD^hF+4&mhOLBqWLcv9Xiv^bmE)`rRxLk0B;7Y+& zf~y7B2(A@eC%9g4gWyKNO@f;Rw+L<(+$MOh;C+JI1@9MpK=47qhXfxMd_-`E;G=?% z2|h0Pgy55c;{=})d|L1s!Dj`Z6MSCq1;G~uiv?d2d|B`n!B+)e6MS9p4Z$}BcM85G z__p9Xf>FVD1$PO)Cpb{>WWo0ZCklQb_@UrOf*%VWEx23o6Twdfj}!b%@N>a01P#G2 z1&qPL`;TBTS*BA2`r}bd6-*OM7i=k*A($!9H=@wT zU2G+oCD>XpTdgDo+Ws;phs}IpjU93V5y)c zs0&UPEE6mjJWp_jV1=Mxuu?D}7!(W%o+~H>!-5gPD#2>O8o`-@wSskmiv`aYyhm`B z;B3JQ1uqi3Snv|TO9d|zyj<`K!7Bx?5}YG=wcs^^*9u-Ic)j2af;S4@BzUvn-Ga9Y z-X?gv;9S9_f_Dm@BREg6t>7Jk^99!mE)-lOxI)m^goZzg%amTC)E~-~O0N=JEx1PT zF2Qwz>jgImZWP=kxLI(E;8wwHf=PmjO_AqEOWNV8jpJ7xQ`oR?NLertY#9hU)8>dYu((*_(Sl!U`tll9<+^f~3zh4_ zxE0cHl57W83z971Nnss@>AhC@ZhkGawVsy0fnV6IYRVptX!Fw4oQ9L6Sl2uiR@4}` zLNvy<;9S+kTljqrf^1Z&-)etXF_=6xQ09bR7M#O0Alzh4r%%JEvIJ za7pRH{kZAITy2cKTqtD#n_YE`gqIGzp%zjO>q$u+rKT^hjnCQ3U8>$MyK30dse$j6 zdjVJ^p~n48l&_FI;uzb49~TNlNIhvec%UGmMua3)gY{@g92qS8 zh3UO?_`VG+-OAIdn~#r6c#T7Zh}R&zrBhf(5N3IUo6_ML5{S(r zV!^O9XUa<4VF)*%H%LOtu}wI|YO}a$cGZ@y7VZ*mU@J=k;S_@Vh1z(36SJR8mdH-v zNjYL_LD!HtV%5NA4g8S7y>>wi;Rqr-c$RQ)maR%ubzu)XyNwkbu^TLHu#AR*I|76b zSPY1GlE?moefeGATDREV_(`BjT$$=G^Jx^vu@)JdU+g{ z*HSe)f{O{>#RVcnK@!reed;?DdPB+{Hnl-Gi6sCMTR}JtTf!1qLYf*VyI>H9G_?5* zBq23Ht-KeP5CsE{1K|kpLk4O@wyz)w9V}x4dkELU^t_fLLM>|$bfjSn2qKT`)TeVt zXhXtoh+RX1flUJ&-o+2Gqu@dfgH+_U6hSTfq$TVTI!MDCbTG3c6R@P{Fb!;qz$(}~ zbw}34EIRB1k&?I;h>!-CU|*iH7B2pBu?!MNeD&Oc4g;QqC1QHlQeq@TDIEqx=!opX z1SUuwr6d?+Bi;>HV{k{Lyh{xUEK`NYc{1#Q&VIqlZ0gC-hAupWE*oKQ2@|9`QY>FC zSjKR~tx{XUh6(WV8|4FqJuIfiK)9r8utcoj8dH%5F=;B|UC-Cj{dk;+Eg1;b5fZ0N zg$@ZWp+*}-h=P5&6}>pq5o)Bo5xy4o12``7`BUW{kofKV4 z0?Q%LrB32nc+x3iBv}FpZGM-OsN4meBV1B#u!yO-BYgGD8q6XN27zFzKscpF8X_C8 zLMq}7Mneb7Yv`y2T^J*q+KO9cb=Wf+VpvBIMA?~xBv|oUAV`te3WCT4I*1TqKolfS z18ZkPAP^%#XR(0~fDTwD$kvbq0|@t$mPa14Tw*q*OATo;6A)qG8Y%XvS{RKdaSl&r zJF)~;o*)IG*OJs8L02K=af_V-)ExO;e}|A@;E3siqyihPQYkKh^}E@9Lz`0!vb{k< znu>Tl#c~%%mH^zDS+Y7l3Ji#_axF~d8gC$>M#qC3v0(Ng=Ci~dw3$WX2uW6FD|$o3 zEchXT#f6%6q^T$?32%fnyq0xAmo=c5$hQi;VfqT^#DLx~25Bl{3?i(En1u*7yA~2h zsR^hBNf;=+5!!eNf{x&vl2A*DnFUkVAeewCu=FBv6m+Q?y+pyqX^<JeCHy~0ExdzZijW%kShKR>$!xT2O zAtB%tqMH|6IV9YJCu zK?O}sU1;T!`r8;EPo|=~A`UJM6+hA>s`PIzmL65{DYjEk$-I@8F^EO)PKQ z7?vlQkI2qQNQn@mE3Z`@mm37<@)Y}^3q&fdFgOwnHq9tZ8m z^sTP<6ul#TgQK%o)aye%^?_ymeZ$Lp`s*Vj^!VZqdk2=S>Rnb} zH)BoTKy7f%aK`~%^XHv7cXIbG$2Uy|x_UOub7Zq|$>3s5azgJ=_b$x_X>fWOB^uhk-5W}5A zOFI{=8Crc%*TAB#ky>rRX~PTWoxXD2%K7vBZ`nZSlFp&Np^jApL%mB^)N37Ume=e3 z9V>d*o-w%Sz_sE!dsf$*dl#KLORYZAse>&4-$$@gQ|~{tc690?&2UwJ{m?y{(+mqH a&7QQwq_f*5w@=vcpY>CwZ-^(fum3O612g>q literal 0 HcmV?d00001 diff --git a/venv/lib/python3.8/site-packages/pip/_vendor/chardet/__pycache__/langthaimodel.cpython-38.pyc b/venv/lib/python3.8/site-packages/pip/_vendor/chardet/__pycache__/langthaimodel.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..e94fdae98496549b05a9eb18d119591ed03a740b GIT binary patch literal 22211 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 z zeh_dj;Gux;2YfH!y8+(`_;$d(0pAMvX23TBz8>(kfUgF8C7>7Z<$x~*d@;OznL5BN;LWWc8bJ{53Fz$XJf5%BSVj|F@*;DLaT1bjH){(zGKw+D;| z90|BH;6nlX1I`BA6Y#!(g@F44-WxCxa3_80DMFMg*O`#qFG4G-_!Xi_zs@NlDhH}lO zRAD${pD}f0AhAKFvm8%i1sl$(JVc9Avo@Bbs^Nr^*dVHiVXER>JSHpHshUfUjx49D zI?x$Y8{22Bcxom#KZZ-p_F{xs*?#E6RJBvH&~j$lBE&$}2XhI@NoQho+CWv7WbrG6 z72jp~1zluqXjl z3HMq-_Eb4qOa(Tu$3bioW>B~sj8|hda%{kUMV6f^u?n=wlu*+I7%-O{R#erd0;bva zSq_)S!*GU~p9}bTCDjn0*%Nm5z;Y^1Z9hza)=8)cIU9pNm#%SYwFzahZrDKfLrVLVn+qgqH>A14U)FxmMVrF1!Ip#7F zrYbwPW$e3781nG3@aEN$_aFl9YdHgdl!Xr#>yBS8PggX zWNnzTsTtTEs+yRP^UUld#puX}W8Z?wj~T@dbVbQZ73{mxlK|Pr zz^hrWPM|7gIj*!Hh0rAUQFuJ8?N5P@JIJwty@AA*Bcv)TLP^MBUu5c#f!eaw)GJBR zkwIRO5jQwEQYmkH4!^(hT#K;jUr?aeTeuTUME*ZmA&ip)x8EK~U z78xCO0#Cw7%VA0{2ht{;LZ;E%oWmJb>@}+u4S6d)3A^w@%@<)HoxPuO7%;F0X2$tqnjt1DShl2zk*L5f7D}fa9f*~qfR4gtV;ar|FgCFF zQ-K@?JSQ%^t{A=8i_t-W9E>VV)$DbVTuF?l(^iDMAFozC%Z^sa6LYU}stOn>Ci~(H zNod3L@-kkCV_2O1sB*6u25M8t2^fg+8qAP53+bh*oB;+Dlp}=6N}(}2j43Pv=W{vJ z$YH7~e%z*9bBFcfA|@-#VM=ettT&S=2k?G~%_ZB#a@qUg0%I=IxQ$n%Lsb*2ij_$a z!z95}1wvy*=WK44v$^b-6;##bvf>SjVs#iu%pKO-9GVp_R!}IbqF=Fq6%U4=So2&A zGM0URnDVB|u_R2xvtNM@TmeJ}RJnxRt#3dwPdwedKTU|{=UC1$%YjX_xs z`{I(>Tr)LEF=PWxV`nkIwqiBrW{fsAAjdP=8Vc#KDuXhe>?L7I=_p{p49npRGqF7m zRTYYDren$G{j7?M7*(5#y%h+>Fp!^eNSHwZMpdjW6yMM0;%5aj$WEY_s<}wgT(wiJ zL%n872UTWR4sp?iJ79J`1=%x=0)v!8r-FU9>>`%=nX^LL2rWk&5^|YXISLuWpe^J5 z#Ige!lx?aGUd;@w*Q(}66^2AsDhLQAA!j2Mk|Tr_d*$2=_I|bt&bTmFISf=#hlvTP z!tf(U2MisI4sz-sgkhCQ$Ynaq5puqm3ffAb980RIg)l&81tc)6U|E%%So0eoW&`WU z3YNoVS&SFr{hAG!E1e#~0E0|S)fjJTZ46R@DonjrRU0`Z_!+}b1*?)PG9H3nHh`H< zVur&g6n`EQ+m&pJ4n`Gb2F+sHU@{~Lp2@0aTCA#I zR@U2GMV#r#!RS~P<2lu)#W_p%%z#O3q2;nFs}-v@JTtXkg~hf^Y^LoQV|xh8HA@^moP5CbwlRc!+d=&+oH=&lfE7ZtbC9RfPC z+sMH%wVZ|MjJ4HS+Y=ZzZ@`meBdu4VI%pH4AV!GJ>?I*b8@aZ|=e zp}1eMu%hce35H+f!}0+?3Ep7YCrtd~uD$2ZZTi#N>W@`36H~R@*i^?Dlc6#4#{h62< zJvQ2(?2nwD>37FZRlSijC#z~|a+jVp%dUu`;8 kyP?0Zxbdc%o}Q|1j{mHi+g8Kc?Q0+CtXsY6(SIKK4^GquXaE2J literal 0 HcmV?d00001 diff --git a/venv/lib/python3.8/site-packages/pip/_vendor/chardet/__pycache__/latin1prober.cpython-38.pyc b/venv/lib/python3.8/site-packages/pip/_vendor/chardet/__pycache__/latin1prober.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..1db0e88521404e8a08bfb8605ea074a1caab505a GIT binary patch literal 3418 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 xtlo8#X6x|Ql}mPcdC~yD( literal 0 HcmV?d00001 diff --git a/venv/lib/python3.8/site-packages/pip/_vendor/chardet/__pycache__/mbcsgroupprober.cpython-38.pyc b/venv/lib/python3.8/site-packages/pip/_vendor/chardet/__pycache__/mbcsgroupprober.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..412cc1a4fbbc16762245e0e7bc290442461b20eb GIT binary patch literal 1146 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 literal 0 HcmV?d00001 diff --git a/venv/lib/python3.8/site-packages/pip/_vendor/chardet/__pycache__/sbcharsetprober.cpython-38.pyc b/venv/lib/python3.8/site-packages/pip/_vendor/chardet/__pycache__/sbcharsetprober.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..759a0b628435486c91ef928d2f33e8e332de927a GIT binary patch literal 3034 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 R&?*ai4i!*r*CHjayt`7k zD<>9|lU)SpDW4!6^AkPvGkEREzmQ8h!$fVM1R_?#*o!q+{KfIfAe*Z9eaHkawq95+)){ZOnN-yZ$>c>65 zc`W#?S4TpGw~qbOPWR#Wr--X~CnDxuS!sgXaFJG$PLTAp@7(U4N*9>D2|dwHPfN5w z9ry;?MKX2Yo~q*WdjASuxT^Od=@^*>P`?siFPm}>Caqt&fY3Y+*KkIL2abN)Qq(i5KnWm&%Q~}t)i~UGsbT!FKx~p*Y4!*%) zAZ)NYLv{A@=ZPc390-?LRXI=no*#qNGs(1|R|zU7^y7BlZwvI?Db-osPw`F75po3$ zJuC-Zi(3W3weIN|d|TwV4{sQV&ql_O4%s>x(>KNtqM&0PGx;1a_9(|8B03KA{E&@J zh>}8Q4q^k6&^!9ldFkr&ML6F9Oz9KMJYZZ+V_{6*m_x=H?EF21NMA?QU*pCjoEJw+ zdSwpHHHfo65NnPZMcfw|E?Wb|>@;x9ja4|cMgX!h=#zW;ls*AxFR}0n<@SUFra4#! z{#TCKJ1TcHh1D@gi127_j9jcAo4B4z2|LSRyIgf}{bsTOzbsHTj)HUpBD*U${B+|G zaxma7&=L~8?gDLdfqCNMBee3D5bI!8p^Q+(vIzUC5JRf<5=_w&{Zm=n2M7BH$_~5j zwvfu=;;`SA=znq@2Q;s;tNV3tZ(G^fO=a)aYq)lP*?&>p@^(! zQp7NWmjhu{@F2Tn%gq1HmJnP*O#||p6_wxGu5WMFcdJj-e7l=^BJTFvM;`Z6UzPNm z-MAHS5jTZetnKU{)Hh$$z533GzIWfQ6(7M+#S1R@MHodRhL*x#QiAI g-ml{%D;AghY6$sN&ZA5xKVt1ku#ABdOuhxe0Y<_ju}{zCE1Ftwn?my*h(eYwWOwDkt|o7A+^+U zm!4fpl}Z%>oc7Q|;ZuPY0V)B#7-kV+T@6Rf@UVG_h?Dy9cqEsMRk@?EEbIG3VSRk~QKEQ?X{$;hXP;3OCA;jirpwK;?}fJGIbPKaH@Ug)Ion(W zwmZ*5UJiX>vQDAw+CgyD+}U)>o2C;;@rH2fw%E}I&>iy3p-7F*_h$E`wxc?AZ9j_sNaUxa4poA-AiUfZzgY}H}NX^@xIqTYnezp+gR-Cf z66>R^e2M8%3TiYwhcT@3@Q(1Cjr+pi;G&R-vWs4$8LozQ$fNv{?Nyt0mA~t_As11O zr0^cu3Jrq`LAkA@-CvDzOLpjZ6W#j2^6cDd?<>@3VD&hwOlI|78C2fE9e5yIfg>s8 zL~tOjnB=Jgoh1&{Vp^7xC>gSpLMg*^mSzUakYlhM>u32-jbfJf)p8Y;4Y0va)M7sy zV#A*(#XLL3PUAVi&ae?Y2iaM;&$(#mgp=40$)ikMYw)2`>4X5>Aa;?a$GC7E9=lK1 zO~zs`gIJ~Ml|#q(q&C}QSYoUWmODfr+jYsi$WfV7sc^x)(4@r{qgM}1vmJ?$D>a_40M#PywLjbj|v^;uRq+&z3+I;-w8%1CT~n0t6e)*n>bdR zK31DLR=aVmcKulG&4RI)bpn6%=FJ}`1@|!*7n?2=QdwoYbr^*_} z*}15kz&(dMSOH;5ORMa$m>CiaHgxNzX_ep-Pr5yAhDgaXj z9UdH!Doti0gr$0j{~E(v3th1Z82W)UF^SLyip#X-f3)KpY(1`hKx;b1UIi$>U;GY z*W^|8#@EIhPGhv`HSF>hXQMltoV%l2_HNC;HML7^ik-LdH!y7!$N*K2=ql=jIq3w!7{Mh1DgI@szz*No1zdH8T`FBQ17Bj1@436C z2k3yxt}^Na{pigcfmjU@$1^v0xWl;zShL;T0GJ89JiD?-+umZxXI9OsduLDjirfIlP=OGEa8WwSTW}jJ zwIZ^#xFHt%ks6H1ver_=#3L=Z5+;ZXM^I|X;0w4zD%5M~Lsh&72SZ}UQqsi?Z@b-= zL3_5MLBd5;?Uh&~2}XT?idfs*G9z0uXgNRx?rkyKTKk|iC$;_#5xPf$0hKJccgksp_fi3N^D#lP7JQ!-Jhbn4G@Q2a2-+SYP)G0_QwbL#2tRiK{ zAbW;nTPn*l^(WeS!q3y~erKfiU!XSfSPec3&!Tkh5#iYJ3P`h~WjqtTc zuvl(js$Dv&VLQ+4Yc1`VHLCckrNI{MfGp9TwL?`NYY#6!Rv&%^?U&m#hY?6CUzdO| zV1M07a%k(U*;|y5Ap2-~TeQh+v$MHmN0@=X0}_kwgsrv>y4284N3^{>Pz`5yE zq>eVI$mGcJpnzULuY$^M=jSJMbzdM2Lu|WhBCCcz3>IO@tlNxZHW~toj#4=q4AOjQ zYN-@QNhD=my9({RjpUGFd49-|VNtDpeSn;-eUz}>9eXzhO-zZ*nV~6{6n2*iW_RX_a2T(}LJ4j&LgF>i#2ofO2OpIB4hU6 zvIXOAO4N>W?dsH|wY1P%n_Y->Y6Dk3XBT1wrvZuAK%(Khi+Ao?vmdN2Sn~^O3v+At z9z=S8_m?-LOt&q{Ev(LU1!Mq-GQ^6bf{X=e(n)Dblq81}l(t63yZ6>t=B>rGDA&oe zDDR2#NQJDUa3o%(&J;)lor45{e3V|fw_?q`_nk<$8x7?` zRFBY~+6=UP&eHoTGxjw+k$%-Oz}{E)ldWV>dsu2E53+{}B~~qMD=A7X6{wutPoO0s zK5uD|?*sqyR-%^MPZ6b@dtYxQWWRJvKgctRlz)KafTR@D^j7LngM6x$MxWu1U64r} zoH|s()3rECdElrli%6N?G7vYYP9yAVM-8&Iv~RS~7F+=G=faVe5uSZS@$-JmsGUQ* ziLu1Ja;;>I?%{Y;he<>X_2E_T!vAe=&vT)_Cz)Ig{YIRf?X_{%EH{Nb$#zjGKKItS z*=(4!mjS00`r^^W!y|C*fz27g!=~_pna(l%B+6certX1QYNpV!#dm|Q>JHxGS?Xki z2sovq4Tu^USn6bjJqWqBH{|&;C?Lp{Ild*aSE3ZYtvHUj3^x?76Cu1@5if)88J$C& z1iL)|a5shCL_Db@W3D&r6ht||-(`DeE(Iq3N7mq2d?_fLkF!ykm&%m%EVM@{#y6VP zXJAa+AR8ope3OKRmKRqRm)DoAwYv`%R`1?hnin&&v+M70A+C}%CAd)^;M^iGS-yQg zN&u835=BEOE#PAWV%;`h;@jL6l(|N!`Gwi_JK_!MpCCrMeK;s&V&oZ$>pjpGovGVD zY#%Or+1Vp@j8f9TVQFCZ2ufHfUZEG|s`SOGt>+m;i&8RP{uy#X4MfSvXeM+^U`){ z%SvHL(8(~$T2@_tmBdjuXIT%Mw%b1VrY#Gb!?MII$!UT|>9SvMI4&1c;D~7=ZxNX% zB11yr_R^AgiK_DCK}as>>p;(hNU)cOjy^Pyrhoz+Y#yYs(@)OG5&f5*$@d#WMqN;k z!f8S0S}{t57F|35iH6(ZOD;qEkRv@H#l2=dkS8T+V;uJ~__ENJqPQ64T=|8t&5|)} zpuSNKdesr8M+3n#Tnc)T?r+rKjMBj8jWsg970>cpG;(>$O7_tb8ydn7mQVf?{{_-c BDL4QC literal 0 HcmV?d00001 diff --git a/venv/lib/python3.8/site-packages/pip/_vendor/chardet/__pycache__/utf8prober.cpython-38.pyc b/venv/lib/python3.8/site-packages/pip/_vendor/chardet/__pycache__/utf8prober.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..ba542297e711fb83e369ce0215bfd2e610665623 GIT binary patch literal 2007 zcma)7OK&4Z5bmCr$Idtj$%=;r92T_5XcI3ByCf1KEQvRXw6RzAduHtP zjI+dYPKmSQ_~LUG;glSS%3uKKc2P`Iu{RdId4B^x4rE(d!;mJH>g){Q%jw3Q0w?X-gZZY;C~+ZWac z{A%%X2`}08pRTaR4%?}1RGtNJ^R3Ukv=Sc_L7J44rom~`d`a4r8{9l~o0irsZi8+M zhdVrXN=|4q2fEAiT6cMY&zurS!Q(~vExaTaY0f}8v#Nk0WJz=~cfYO$nB&!lItGFx z`5Z(_QaT|g#+h+WC`rxKIsu#6oPcyD zrGs<%KtsclNEKN;ideMA0suErwiJm-UsGizB5cENp0OZoB?)7%$zO-%q3j+4*yY2P zEVlYn&1cwj3j;FA5IECjks$&~+Us}oT)~>XdYgfwCFC|hIw<7Tco;E-bWBlHf zzv(^y%dNW1=HEQJVJq&8S{?Cd%G)&hSL=a}1fziubHbq${27!9-5@`-j}5?`oY9GK zPLIurnHoK_Z^-+p)w8jhDLDuaEIprt`3;zN-s#{G)qS`F05_`_7_`(JGp}D+6=LyY$`WK1i5V!nQm@zQ z68lj()tb+CD$2<=C}-PmpzS`b?N&F~wy*4l|D^7##SXktFp^Tl@C$VvidYvAw5KMl z{6@vEtoz&5O*O0c4&ru~i#QN!p|Mq~`|G4@azToW0)1xMXb?QBzzEPRml;bcP}C?!OU7qlQVJzinh6fP$Rnyi zBoL=q5o-KIi5wQpq>O{6rjUWG0CNKCL9+$Ah$N2Zct&;L`=_`tsZghIi8)+kn6KAh zpI{zDe{wv_G0TEjA literal 0 HcmV?d00001 diff --git a/venv/lib/python3.8/site-packages/pip/_vendor/chardet/big5freq.py b/venv/lib/python3.8/site-packages/pip/_vendor/chardet/big5freq.py new file mode 100644 index 0000000..38f3251 --- /dev/null +++ b/venv/lib/python3.8/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 +# +# +# 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/venv/lib/python3.8/site-packages/pip/_vendor/chardet/big5prober.py b/venv/lib/python3.8/site-packages/pip/_vendor/chardet/big5prober.py new file mode 100644 index 0000000..98f9970 --- /dev/null +++ b/venv/lib/python3.8/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/venv/lib/python3.8/site-packages/pip/_vendor/chardet/chardistribution.py b/venv/lib/python3.8/site-packages/pip/_vendor/chardet/chardistribution.py new file mode 100644 index 0000000..c0395f4 --- /dev/null +++ b/venv/lib/python3.8/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/venv/lib/python3.8/site-packages/pip/_vendor/chardet/charsetgroupprober.py b/venv/lib/python3.8/site-packages/pip/_vendor/chardet/charsetgroupprober.py new file mode 100644 index 0000000..8b3738e --- /dev/null +++ b/venv/lib/python3.8/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/venv/lib/python3.8/site-packages/pip/_vendor/chardet/charsetprober.py b/venv/lib/python3.8/site-packages/pip/_vendor/chardet/charsetprober.py new file mode 100644 index 0000000..eac4e59 --- /dev/null +++ b/venv/lib/python3.8/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/venv/lib/python3.8/site-packages/pip/_vendor/chardet/cli/__init__.py b/venv/lib/python3.8/site-packages/pip/_vendor/chardet/cli/__init__.py new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/venv/lib/python3.8/site-packages/pip/_vendor/chardet/cli/__init__.py @@ -0,0 +1 @@ + diff --git a/venv/lib/python3.8/site-packages/pip/_vendor/chardet/cli/__pycache__/__init__.cpython-38.pyc b/venv/lib/python3.8/site-packages/pip/_vendor/chardet/cli/__pycache__/__init__.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..d87ddad0b9d39754329cb4adf5a6ab03b8eb9480 GIT binary patch literal 211 zcmWIL<>g`k0u|Y=6hn$%Tn`F@{9D7GZKqZQcLucb29bg<1_OzOXB18 W3My}L*yQG?l;)(`fn4+%h#3Hx+c$Io literal 0 HcmV?d00001 diff --git a/venv/lib/python3.8/site-packages/pip/_vendor/chardet/cli/__pycache__/chardetect.cpython-38.pyc b/venv/lib/python3.8/site-packages/pip/_vendor/chardet/cli/__pycache__/chardetect.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..dbbb2e7e2d4a34417f72fdef286e680798c7fbef GIT binary patch literal 2712 zcma)8-)|eo5x!qMQYVU5(zJln#Mp;8H3N~9)!1l=qG%DfXn;U45~V=crspGfDV}sc z=-nk*($tgsRMh{2h(7fn>chU~ssBRJ!2M=PDm0vjo}j(i-PyUFnQy+~R|f}?gXe?y z|Hl6X>06rYK0YSD#=nvnxYKjE%e~Cay`Go*JwFe6flF(CR_RsjJM4vc2U#_*^=fvn zlI`VDFS2r&?dSDg9r6LM@x8ygy#wCh5#NW@5O;X}(vyW~TszS4zjT^+l6$V>L|+bM zI@WA9N(UpRlS`pkSqN52mX}hnVVVgxPV`8zq~J`7aVa&aFg+5fWL#)5(1J&z7?eCM z&Q%os_Ag03&P2BxMGVh6<%AUzOQJi#W|G0H!gPX`$ocq!{S5EX?7gNa4STvs4S2Wa_l( zn*BJQ3aPLk$C&z0zdph1t*?rdmXqv>ja?~C#eQ$#(bqOl1@935pW6I z6XzRtpWorFY>=AmRDZ^9ZZK3VFs_5dbb@$iveHo_vb1l@a*&@kBmz`py?agffs4N3;ksDgI-`lafK7(7@H4~=i z1&YDcw%1s^_u}}f%U(RZdINDUPOeP!JzN(Js|ox`p(dF=ZSKn|(rNbSqKTBr+yp}* zxCuDw&Qu0jse}pF+L9n(LM{kf##eJ?evFWi6OgsYw-++uu#|bCn-x>B&X|CFFz&cZr~o`U3bG*fiHgwjREND_6Z6d zF3{~>c--fKc5ZxLS-CvafqbAV7hw_7cu_rfdG%!l6+)U|OCH_S^d2<#U;4BP{5vZb z_2ymdqvAe7rTI7Yl_RJ6fY)K8!4F<~uA>k1oh9}z4zImyRN?bK@dk10;#-TH--9!e zDxfXTi9RV6ERp9EBo2&2OGu{z#ECgk7KsFxzFrnkpLW^jo65O8d<4d0DN1Q92I(*r ze8b7xZ?))K>sp~2yL~{0rvTa!d-e={cHn<&^x3m_}OE$B?w*4g?F zJJoCV7JoSVfBOY$c&Az1UgkAni)BUBxdRhZvCO>q^Y5q^%|@t?@A+Vcm2#3%XfFjZ z2K5=4lkcw?e{=GHi(!I_?5^3qX*}6C?nlkQ1S64+O+cQTN&(-Mscw`Pd#%=n(@k|F zEb8|kfcwsyilNq->Rf4(Rywwwu_n&?GFcRk;|VzfpP%tsR`{#X=lo~ zL(5<5ftbStd6E{7A*l~BID3KXy$b!%^}L$nxo`hBtbkKqg5M(vk*$tRik6fqjU=4_ literal 0 HcmV?d00001 diff --git a/venv/lib/python3.8/site-packages/pip/_vendor/chardet/cli/chardetect.py b/venv/lib/python3.8/site-packages/pip/_vendor/chardet/cli/chardetect.py new file mode 100644 index 0000000..c61136b --- /dev/null +++ b/venv/lib/python3.8/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/venv/lib/python3.8/site-packages/pip/_vendor/chardet/codingstatemachine.py b/venv/lib/python3.8/site-packages/pip/_vendor/chardet/codingstatemachine.py new file mode 100644 index 0000000..68fba44 --- /dev/null +++ b/venv/lib/python3.8/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/venv/lib/python3.8/site-packages/pip/_vendor/chardet/compat.py b/venv/lib/python3.8/site-packages/pip/_vendor/chardet/compat.py new file mode 100644 index 0000000..ddd7468 --- /dev/null +++ b/venv/lib/python3.8/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/venv/lib/python3.8/site-packages/pip/_vendor/chardet/cp949prober.py b/venv/lib/python3.8/site-packages/pip/_vendor/chardet/cp949prober.py new file mode 100644 index 0000000..efd793a --- /dev/null +++ b/venv/lib/python3.8/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/venv/lib/python3.8/site-packages/pip/_vendor/chardet/enums.py b/venv/lib/python3.8/site-packages/pip/_vendor/chardet/enums.py new file mode 100644 index 0000000..0451207 --- /dev/null +++ b/venv/lib/python3.8/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/venv/lib/python3.8/site-packages/pip/_vendor/chardet/escprober.py b/venv/lib/python3.8/site-packages/pip/_vendor/chardet/escprober.py new file mode 100644 index 0000000..c70493f --- /dev/null +++ b/venv/lib/python3.8/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/venv/lib/python3.8/site-packages/pip/_vendor/chardet/escsm.py b/venv/lib/python3.8/site-packages/pip/_vendor/chardet/escsm.py new file mode 100644 index 0000000..0069523 --- /dev/null +++ b/venv/lib/python3.8/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/venv/lib/python3.8/site-packages/pip/_vendor/chardet/eucjpprober.py b/venv/lib/python3.8/site-packages/pip/_vendor/chardet/eucjpprober.py new file mode 100644 index 0000000..20ce8f7 --- /dev/null +++ b/venv/lib/python3.8/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/venv/lib/python3.8/site-packages/pip/_vendor/chardet/euckrfreq.py b/venv/lib/python3.8/site-packages/pip/_vendor/chardet/euckrfreq.py new file mode 100644 index 0000000..b68078c --- /dev/null +++ b/venv/lib/python3.8/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/venv/lib/python3.8/site-packages/pip/_vendor/chardet/euckrprober.py b/venv/lib/python3.8/site-packages/pip/_vendor/chardet/euckrprober.py new file mode 100644 index 0000000..345a060 --- /dev/null +++ b/venv/lib/python3.8/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/venv/lib/python3.8/site-packages/pip/_vendor/chardet/euctwfreq.py b/venv/lib/python3.8/site-packages/pip/_vendor/chardet/euctwfreq.py new file mode 100644 index 0000000..ed7a995 --- /dev/null +++ b/venv/lib/python3.8/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 +# + +# 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/venv/lib/python3.8/site-packages/pip/_vendor/chardet/euctwprober.py b/venv/lib/python3.8/site-packages/pip/_vendor/chardet/euctwprober.py new file mode 100644 index 0000000..35669cc --- /dev/null +++ b/venv/lib/python3.8/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/venv/lib/python3.8/site-packages/pip/_vendor/chardet/gb2312freq.py b/venv/lib/python3.8/site-packages/pip/_vendor/chardet/gb2312freq.py new file mode 100644 index 0000000..697837b --- /dev/null +++ b/venv/lib/python3.8/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/venv/lib/python3.8/site-packages/pip/_vendor/chardet/gb2312prober.py b/venv/lib/python3.8/site-packages/pip/_vendor/chardet/gb2312prober.py new file mode 100644 index 0000000..8446d2d --- /dev/null +++ b/venv/lib/python3.8/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/venv/lib/python3.8/site-packages/pip/_vendor/chardet/hebrewprober.py b/venv/lib/python3.8/site-packages/pip/_vendor/chardet/hebrewprober.py new file mode 100644 index 0000000..b0e1bf4 --- /dev/null +++ b/venv/lib/python3.8/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/venv/lib/python3.8/site-packages/pip/_vendor/chardet/jisfreq.py b/venv/lib/python3.8/site-packages/pip/_vendor/chardet/jisfreq.py new file mode 100644 index 0000000..83fc082 --- /dev/null +++ b/venv/lib/python3.8/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/venv/lib/python3.8/site-packages/pip/_vendor/chardet/jpcntx.py b/venv/lib/python3.8/site-packages/pip/_vendor/chardet/jpcntx.py new file mode 100644 index 0000000..20044e4 --- /dev/null +++ b/venv/lib/python3.8/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/venv/lib/python3.8/site-packages/pip/_vendor/chardet/langbulgarianmodel.py b/venv/lib/python3.8/site-packages/pip/_vendor/chardet/langbulgarianmodel.py new file mode 100644 index 0000000..2aa4fb2 --- /dev/null +++ b/venv/lib/python3.8/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/venv/lib/python3.8/site-packages/pip/_vendor/chardet/langcyrillicmodel.py b/venv/lib/python3.8/site-packages/pip/_vendor/chardet/langcyrillicmodel.py new file mode 100644 index 0000000..e5f9a1f --- /dev/null +++ b/venv/lib/python3.8/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/venv/lib/python3.8/site-packages/pip/_vendor/chardet/langgreekmodel.py b/venv/lib/python3.8/site-packages/pip/_vendor/chardet/langgreekmodel.py new file mode 100644 index 0000000..5332221 --- /dev/null +++ b/venv/lib/python3.8/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/venv/lib/python3.8/site-packages/pip/_vendor/chardet/langhebrewmodel.py b/venv/lib/python3.8/site-packages/pip/_vendor/chardet/langhebrewmodel.py new file mode 100644 index 0000000..58f4c87 --- /dev/null +++ b/venv/lib/python3.8/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/venv/lib/python3.8/site-packages/pip/_vendor/chardet/langhungarianmodel.py b/venv/lib/python3.8/site-packages/pip/_vendor/chardet/langhungarianmodel.py new file mode 100644 index 0000000..bb7c095 --- /dev/null +++ b/venv/lib/python3.8/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/venv/lib/python3.8/site-packages/pip/_vendor/chardet/langthaimodel.py b/venv/lib/python3.8/site-packages/pip/_vendor/chardet/langthaimodel.py new file mode 100644 index 0000000..15f94c2 --- /dev/null +++ b/venv/lib/python3.8/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/venv/lib/python3.8/site-packages/pip/_vendor/chardet/langturkishmodel.py b/venv/lib/python3.8/site-packages/pip/_vendor/chardet/langturkishmodel.py new file mode 100644 index 0000000..a427a45 --- /dev/null +++ b/venv/lib/python3.8/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/venv/lib/python3.8/site-packages/pip/_vendor/chardet/latin1prober.py b/venv/lib/python3.8/site-packages/pip/_vendor/chardet/latin1prober.py new file mode 100644 index 0000000..7d1e8c2 --- /dev/null +++ b/venv/lib/python3.8/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/venv/lib/python3.8/site-packages/pip/_vendor/chardet/mbcharsetprober.py b/venv/lib/python3.8/site-packages/pip/_vendor/chardet/mbcharsetprober.py new file mode 100644 index 0000000..6256ecf --- /dev/null +++ b/venv/lib/python3.8/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/venv/lib/python3.8/site-packages/pip/_vendor/chardet/mbcsgroupprober.py b/venv/lib/python3.8/site-packages/pip/_vendor/chardet/mbcsgroupprober.py new file mode 100644 index 0000000..530abe7 --- /dev/null +++ b/venv/lib/python3.8/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/venv/lib/python3.8/site-packages/pip/_vendor/chardet/mbcssm.py b/venv/lib/python3.8/site-packages/pip/_vendor/chardet/mbcssm.py new file mode 100644 index 0000000..8360d0f --- /dev/null +++ b/venv/lib/python3.8/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/venv/lib/python3.8/site-packages/pip/_vendor/chardet/sbcharsetprober.py b/venv/lib/python3.8/site-packages/pip/_vendor/chardet/sbcharsetprober.py new file mode 100644 index 0000000..0adb51d --- /dev/null +++ b/venv/lib/python3.8/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/venv/lib/python3.8/site-packages/pip/_vendor/chardet/sbcsgroupprober.py b/venv/lib/python3.8/site-packages/pip/_vendor/chardet/sbcsgroupprober.py new file mode 100644 index 0000000..98e95dc --- /dev/null +++ b/venv/lib/python3.8/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/venv/lib/python3.8/site-packages/pip/_vendor/chardet/sjisprober.py b/venv/lib/python3.8/site-packages/pip/_vendor/chardet/sjisprober.py new file mode 100644 index 0000000..9e29623 --- /dev/null +++ b/venv/lib/python3.8/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/venv/lib/python3.8/site-packages/pip/_vendor/chardet/universaldetector.py b/venv/lib/python3.8/site-packages/pip/_vendor/chardet/universaldetector.py new file mode 100644 index 0000000..7b4e92d --- /dev/null +++ b/venv/lib/python3.8/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/venv/lib/python3.8/site-packages/pip/_vendor/chardet/utf8prober.py b/venv/lib/python3.8/site-packages/pip/_vendor/chardet/utf8prober.py new file mode 100644 index 0000000..6c3196c --- /dev/null +++ b/venv/lib/python3.8/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/venv/lib/python3.8/site-packages/pip/_vendor/chardet/version.py b/venv/lib/python3.8/site-packages/pip/_vendor/chardet/version.py new file mode 100644 index 0000000..bb2a34a --- /dev/null +++ b/venv/lib/python3.8/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/venv/lib/python3.8/site-packages/pip/_vendor/colorama/__init__.py b/venv/lib/python3.8/site-packages/pip/_vendor/colorama/__init__.py new file mode 100644 index 0000000..34c263c --- /dev/null +++ b/venv/lib/python3.8/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.3' diff --git a/venv/lib/python3.8/site-packages/pip/_vendor/colorama/__pycache__/__init__.cpython-38.pyc b/venv/lib/python3.8/site-packages/pip/_vendor/colorama/__pycache__/__init__.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..5b8a0f7214775fbf58e9a94423a49ece1de003ab GIT binary patch literal 459 zcmYk2J5R$f5XX}=Ngrv&+J%iJL+h$76_pU;B}SGOA$hSv?SiQvk(0EvAAxVeSMtil z#Lk4T6%r@;cfN(3vDu) zk&?ZY&~~Q{v*C5NSlMV+ZUwXPO=SvgQr{+5s^D||$kk}Liw6C1e{`RAYzs#6Oz?sk zP+)3+5D+=oFmumL3~?RM05qYJK&8|h7-(QMHX&rqOu@AxEc$1oU%Mro=l-USql?5qS7 zhRk$^JoQ(!kNHbGee09{LY{ig3KMgic6jM(_VXT{^*MX4+T~o%RA5~H;CK30T2cN+ zVtD94Ou-EP0HBJkP+}9R+A7s-jp{VHnT)qPSrsuoeplY@myIWol!~R{%Vt#eU+wl5~dTzDH*DRzgl5>Xx95ZwguFt8 zjxygUJOzJv`_h8bZi6}2w#%*6UY9Z+SpAN5d#Ats&7^;4GNknO^WeLitDAe9-`!$g`Yn=F=Xxq!Zjt*K+;9tM+$A4)r@> z&q3D(Svno;rE20v67<>q47uG{M3|=!IP-4UEA6 z86q99(2<-1kOQ4LSVySil;VH)><=9GyaAU&Zk*DW?R1&TouI=R^Gic2aeV=8kEAw^ zaXx998LR4gKD#`mXcQH}JT}nQDVPCrnF5ukK;=RGLA7aAgQU?#x(2bTgH6G)bE36L zn05U7ZGaF$aX@C2XE$DveewsP`-FG*cNNl^^tyA7M9LMv$&~dKf z;V|Heo+Tzcd!-|5s)J1}FcSWO@*GE)juRI*th0{ua?9<;9R4AQ8FDt33`f}<-FyY6 zOe2Fx`jo5DUsJg0KERQya}*jKv@wn5WwRsD>{64sKp7Z@@ueF5P!%tSt9E4!e_uatc|_gJB`)l zg$wALj#Cz8o>ZmJ%&)XQA5vBrQii+>sH>l^F7;r3aiz96QyJ12=Y@1=^^ko5r8DO% z)u(IqM_&$U9JUd89SqrE;QAf~=Ew zAS>n*U|fe8;N`#|rW!vS-*;dh8<^iyFoOkvqsqaWQK4Eis1qG71PQ2(q;1fYou-Cu z(zKnSrk$l3J4ds2p62WV&D%v`jZMM-`stLXRq}CFNm4|an4Ut%Ev>Qtz*PNSuxFkh5jUPouk{}9+ zm?uRdnU}m+G}76_zb-eBDoE;3)jKw-L@JggRReiu7;k}nU&9R0Ne0nRKHBj&9QXe} z;hl!7Npu6$N0_3~7YLZA(N_qVtWg!gLO_!uiog(75!Mja5j=z!2pa$`-T*w)J1fAL zh528-gZt0-4mN@FlOGZ}8GotU;VxWvFkyKfAR0q}skADolZlv|Uc_7y+RWTzk!;N` zKCU%ITBey(Yc|ic3eCq{m!H)^%sIZr=7A~)KP8cJ9NOzR4#!(Q$6DtZgfs$Pwm4pY zI9@(PviJN2>xA;-Df@uT7LHDl(uIyC3Izg@6bWgiNY-V;KI@#q_&)xO-&a9GPzKNw XG95Jo9#b>*Vy2iYj=_^m5`6vv|C@K& literal 0 HcmV?d00001 diff --git a/venv/lib/python3.8/site-packages/pip/_vendor/colorama/__pycache__/ansitowin32.cpython-38.pyc b/venv/lib/python3.8/site-packages/pip/_vendor/colorama/__pycache__/ansitowin32.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..78504246a6656c0d3322dda1949675409c91a679 GIT binary patch literal 7753 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 literal 0 HcmV?d00001 diff --git a/venv/lib/python3.8/site-packages/pip/_vendor/colorama/__pycache__/win32.cpython-38.pyc b/venv/lib/python3.8/site-packages/pip/_vendor/colorama/__pycache__/win32.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..b0b001d5927bdb6e42199f127f8dbe1d6361cb87 GIT binary patch literal 3996 zcmai1Npl;y6$a1?o77@T-eg&}X?YnlWs{CQaWaYH8e5W=3P*B@bTreIR9CA3N~D_I zG#ZpF6@8LVsY)uhTs_D68}l>zn$w(a++XnYAJ`JNKOiAy+j>0X61ZiLKbP@5s^7_LEyFa&Fx`_#3d*MeT# zwJBL6QN<)8C!8kRDo9QV{AxZAA3jmm>AzT+$mwTr`xC9Gax;?LB$#G9B}8Mp3#yE?yPQ4im83N z!NjbVUxYf7=iNCix7(LMIyaEc4WyUdD_U9v*_DCp%0O0fuWH!@(6gc_Cb5P+EOB)( z^6FsZyn9WLggrhPdu=fG+Fcq(0-5{1ns$L7J2o`P4h= zUf|RGv_|m~Kg-WS&t-m|&p^*Deu2+IJP@&np=xlGfE zT5DbUa<>-yTuA6}c3P3z?TRG8A%`D009cNYXHXIo2q$&&9bNtoNJ>3vL6;^!%4%1( zwtXe4+rF$e;}Dijs@&b8KHamNrU)|N;MpSK7yRGh>Uexu2 zEy3^XYzh(H+46VW@fY`ZQC9V~MZ{xS4dO7Ce#ftZruXkvy1S|KF!Vd?+K^T=BznmNsR z9$@Erm!Xl&0wI)9o7lRTNb!gDhrRieEl?ROvkZoEJTLM)!t>IC=XGM<3y~l9yw^QH z97teW9tY5EEv&4pE=mJ+G1q`~3-zxa*J|GC(n154YPLkk6HlUQBf1~?Df%cRaFQT0 z4mBAlOC9*xAIzLY=`j=HM(Ft3gmb_svQOSp1twIiPxt9Qd&`v3XZy5HbcmEqy-Iq~ zdnVLE93^om>VXs@deUn)g?te;V>t#ZrRI7Zhh-zRH~ol*A~l0#y_M#YUJ!^R8PQ%N zKkk96`rVcXPN@C_^AmI}0@^dmh<#u(`5CkW>>T~@=vsR;nF1H&>>W{5fyO|MecEJ; z1kM5amyP9Phi5PY0Z6c%1JWmdo`k~_Joq0W$-QQn+p=R*y?vRbta3M{n;)y6f4Ctc z@un*up@upynuL+P%Oj_%JPhN&50gig;b6!lh9B*w6NqMA@#h&-cD=E<@~V+yy|lVI zvf0qm5Vc2mt>d1UUkF%w@<3LBJ9bpG@zBmCXuu%>6|^+io1v{UD}%kk+J}cW+>YN6Wjh1T=^kDgw$IzWM5R+%Ex0!nu|)nXv@=K09$)=6 zY@T3J25i|GYMSRDZ53#d78zjuJLnHI@;Cy%+@NT8cmj=qJDA`O^ga7Fb8F+h*=OHU zOkVFzPJy$O)i?X-GKRAI7Ni90E!6FtzHc;aWhe9Z30O6?1>BtQVnH*f)*>e3bk2L) z3d2FxTj{B8Pc6Wm?*~fA$6401w*9at%DD_NQ$GppP(&MQGqn=sOQq3C$D&@x+mS7$ zMD+8abMjXEpi+W{LvI|X1zKR3B(tARm5U%d8MWVn^a!>05JVau`W`}GV+NeY%vL5~ zW@*fOQmjnpoSn}-ZxLvv#Adtyg z21u!#*~8B4fhLe8B)5mWWeXKQ$Kbjy3i*X>*Tfn3d8k1 zLBKHq^p9Yd_yjtZbukJtli@xI-f|h}huM{;QzIeQXsJ-F!NbH-$bGEWsiW}GBmaSG z>NH8^AAm2@M)?<6a95xS&r$t!mpCM~;RUx?tED#F89WR#pPn3=Y)Q&kreiNU-B>E! z&?!z|BN|YNcd^8T;6J4#ik*SO>O~q*kLvcZyG(@Tk zP>MBBr!)0aXOjK}>8H{a2;|I*<6WnwUo782naXMwiIYK&>C8rQk;QsaiN|IMfw7@OR}*ivH?W1HtN z&Z)7*olUKrKVx`InMv!g)wy@Kfx*y4+->#TnLWf*y6I6ETR89cLC1n@A3%S}sV(^E&lC-}9vF zd3#;n3#ec8yg&EqLCUcf*A|w(k>=vs_KLJtH&#}v(p=hEs7hmFWm)E)EUd0nw-%(m zv$D3f{+%=-vhzw4Z{r<`{1jF8MeCp*`IUpZs5H9)_eEIQZ&dCd?jL+|xAX9B#QFW* z@WGwt-qGHtchvQ?Q`xHQxAt%KI{Wp;bD!TjeCGSXt>^U@?e4>SFNjz14*U-9ib{hF zukY0>@N49Yz1#aQ1ldi-h9wY(nL|IteY5{(vE*X^!`GJb^=9-U@P+cL1L?EBf>g`4 zDBv!NM8-g*U0vUJvalxY#f`_SUvJ6Dh(5+tuZPqmteXttAB}(L%ByQC`ix zrZQPMPl7*!3{>X&OAW9wxQRP}$!**%KF1xNf5`x%U+@AiqL<_E^D#b-p2M&630^`k z&u{QaK82pkFY#%tEbtHbTl{Uz6!}d)!)MVO;~(<0d3L->AYvDa++;&G?hmG=PP~Gpa11rZ*|au8 zJsr(hL|?h+jIy9?iW|6zn?yb$@-dNL66vR_P|D?>X0D(WA$e9~N0XUOeQrZ>h>MH* z{m9?*~zV@S-46 zcH>~6porikY8#@A#YtW(J&iO&R5iL#$A?(3iWbrqmWF^Gjb+YP3y0Jrj8R)c3-5rW z7%Ne(oC0MLYwO1bP&S92aco5F#EgxYo$AOM!V#;MEm6TrRjtJwda5k1Y;4Gx&3@_L z^1qMzH9CcqAhakur2VYk;n;tNtCk?MpP;4Hlq#u>ecxtBm-;phGIA*Uha69-N4paA zCVSv)WDK(5Gs+EEhmo0%%vewMAllYo8r4AY9*8XQ!=;`GyW-n!*os=+j%0t3Y^R)r zMf{dzoc)wHVu>tLh0U1T5KTi=G{&hGOlRht7khK0$euJ(YR|-O%T$!E(7uZ(X(z_{ zD2dOp0A5$d%%LxS10rV!@fo%(3(x|Is$?MRMs2#Js-bd8-?TqLE{m2yky6omH#6^o z6+_j4b_zPyDm*TtqbvjBL`hjjX9*+5N#fXwtu|tXt4P)Kei!b$7XDE>`p%z z2|~+8^$|+CUFYpy7?pD)P7w4KF;C?e1~$>c#k@7p};m$63T zlSJ?-7Ns6Zyy3EGcJx-}ks(>N;|zs8kJWT$R1WzMI?5rGkcmUmP{Sc+Y_?6ff~Y;M z!9m$LM`Mg~ZEHq5wgDXWTo*N(InKpaY|m=PPUivUb<9Tqj{^rH#lf|~xdx5_&W}SJ z3mlV50A};pTFlsqbEhVwu?z5NCxufjn@gYYZ}5tcwX*V2wv)r2`o0kq`JdPvHdRVK zMbcBCsw)b#NiN#_aI?0KO&kjhKR#I8@fo0mjXx=qpD!(pK- zxExOeqORwQdN^9vkWNIJnc_t+>9eZxxu8Yll%j@-`Dy0pgW_@iF+(V# zwCbbiUl{xhiV}2uPUuk>vM8kpQ$UH``8q|(9}&qM4(-HofRq$>khqz~`Y#tq;8~Cu zMvWa?uc0QLQwK6@6d~ewFrZu<&10(33I*aD>ZxR=Q&W=Lw2O!;5!$QL1Q+WxF{p3f z(r}Z=7Lno2NV$3$oc6u&33LuBA-)l;9$ z*TA4rNZ$#L?i3xvv0Te7;Agvz&E|VjOw7a`)zY(dw1k^{i61eCc V()l9!ljKVZgF2fR%~GLc{SPi6xi> 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/venv/lib/python3.8/site-packages/pip/_vendor/contextlib2.py b/venv/lib/python3.8/site-packages/pip/_vendor/contextlib2.py new file mode 100644 index 0000000..3aae8f4 --- /dev/null +++ b/venv/lib/python3.8/site-packages/pip/_vendor/contextlib2.py @@ -0,0 +1,518 @@ +"""contextlib2 - backports and enhancements to the contextlib module""" + +import abc +import sys +import warnings +from collections import deque +from functools import wraps + +__all__ = ["contextmanager", "closing", "nullcontext", + "AbstractContextManager", + "ContextDecorator", "ExitStack", + "redirect_stdout", "redirect_stderr", "suppress"] + +# Backwards compatibility +__all__ += ["ContextStack"] + + +# Backport abc.ABC +if sys.version_info[:2] >= (3, 4): + _abc_ABC = abc.ABC +else: + _abc_ABC = abc.ABCMeta('ABC', (object,), {'__slots__': ()}) + + +# Backport classic class MRO +def _classic_mro(C, result): + if C in result: + return + result.append(C) + for B in C.__bases__: + _classic_mro(B, result) + return result + + +# Backport _collections_abc._check_methods +def _check_methods(C, *methods): + try: + mro = C.__mro__ + except AttributeError: + mro = tuple(_classic_mro(C, [])) + + for method in methods: + for B in mro: + if method in B.__dict__: + if B.__dict__[method] is None: + return NotImplemented + break + else: + return NotImplemented + return True + + +class AbstractContextManager(_abc_ABC): + """An abstract base class for context managers.""" + + def __enter__(self): + """Return `self` upon entering the runtime context.""" + return self + + @abc.abstractmethod + def __exit__(self, exc_type, exc_value, traceback): + """Raise any exception triggered within the runtime context.""" + return None + + @classmethod + def __subclasshook__(cls, C): + """Check whether subclass is considered a subclass of this ABC.""" + if cls is AbstractContextManager: + return _check_methods(C, "__enter__", "__exit__") + return NotImplemented + + +class ContextDecorator(object): + """A base class or mixin that enables context managers to work as decorators.""" + + def refresh_cm(self): + """Returns the context manager used to actually wrap the call to the + decorated function. + + The default implementation just returns *self*. + + Overriding this method allows otherwise one-shot context managers + like _GeneratorContextManager to support use as decorators via + implicit recreation. + + DEPRECATED: refresh_cm was never added to the standard library's + ContextDecorator API + """ + warnings.warn("refresh_cm was never added to the standard library", + DeprecationWarning) + return self._recreate_cm() + + def _recreate_cm(self): + """Return a recreated instance of self. + + Allows an otherwise one-shot context manager like + _GeneratorContextManager to support use as + a decorator via implicit recreation. + + This is a private interface just for _GeneratorContextManager. + See issue #11647 for details. + """ + return self + + def __call__(self, func): + @wraps(func) + def inner(*args, **kwds): + with self._recreate_cm(): + return func(*args, **kwds) + return inner + + +class _GeneratorContextManager(ContextDecorator): + """Helper for @contextmanager decorator.""" + + def __init__(self, func, args, kwds): + self.gen = func(*args, **kwds) + self.func, self.args, self.kwds = func, args, kwds + # Issue 19330: ensure context manager instances have good docstrings + doc = getattr(func, "__doc__", None) + if doc is None: + doc = type(self).__doc__ + self.__doc__ = doc + # Unfortunately, this still doesn't provide good help output when + # inspecting the created context manager instances, since pydoc + # currently bypasses the instance docstring and shows the docstring + # for the class instead. + # See http://bugs.python.org/issue19404 for more details. + + def _recreate_cm(self): + # _GCM instances are one-shot context managers, so the + # CM must be recreated each time a decorated function is + # called + return self.__class__(self.func, self.args, self.kwds) + + def __enter__(self): + try: + return next(self.gen) + except StopIteration: + raise RuntimeError("generator didn't yield") + + def __exit__(self, type, value, traceback): + if type is None: + try: + next(self.gen) + except StopIteration: + return + else: + raise RuntimeError("generator didn't stop") + else: + if value is None: + # Need to force instantiation so we can reliably + # tell if we get the same exception back + value = type() + try: + self.gen.throw(type, value, traceback) + raise RuntimeError("generator didn't stop after throw()") + except StopIteration as exc: + # Suppress StopIteration *unless* it's the same exception that + # was passed to throw(). This prevents a StopIteration + # raised inside the "with" statement from being suppressed. + return exc is not value + except RuntimeError as exc: + # Don't re-raise the passed in exception + if exc is value: + return False + # Likewise, avoid suppressing if a StopIteration exception + # was passed to throw() and later wrapped into a RuntimeError + # (see PEP 479). + if _HAVE_EXCEPTION_CHAINING and exc.__cause__ is value: + return False + raise + except: + # only re-raise if it's *not* the exception that was + # passed to throw(), because __exit__() must not raise + # an exception unless __exit__() itself failed. But throw() + # has to raise the exception to signal propagation, so this + # fixes the impedance mismatch between the throw() protocol + # and the __exit__() protocol. + # + if sys.exc_info()[1] is not value: + raise + + +def contextmanager(func): + """@contextmanager decorator. + + Typical usage: + + @contextmanager + def some_generator(): + + try: + yield + finally: + + + This makes this: + + with some_generator() as : + + + equivalent to this: + + + try: + = + + finally: + + + """ + @wraps(func) + def helper(*args, **kwds): + return _GeneratorContextManager(func, args, kwds) + return helper + + +class closing(object): + """Context to automatically close something at the end of a block. + + Code like this: + + with closing(.open()) as f: + + + is equivalent to this: + + f = .open() + try: + + finally: + f.close() + + """ + def __init__(self, thing): + self.thing = thing + + def __enter__(self): + return self.thing + + def __exit__(self, *exc_info): + self.thing.close() + + +class _RedirectStream(object): + + _stream = None + + def __init__(self, new_target): + self._new_target = new_target + # We use a list of old targets to make this CM re-entrant + self._old_targets = [] + + def __enter__(self): + self._old_targets.append(getattr(sys, self._stream)) + setattr(sys, self._stream, self._new_target) + return self._new_target + + def __exit__(self, exctype, excinst, exctb): + setattr(sys, self._stream, self._old_targets.pop()) + + +class redirect_stdout(_RedirectStream): + """Context manager for temporarily redirecting stdout to another file. + + # How to send help() to stderr + with redirect_stdout(sys.stderr): + help(dir) + + # How to write help() to a file + with open('help.txt', 'w') as f: + with redirect_stdout(f): + help(pow) + """ + + _stream = "stdout" + + +class redirect_stderr(_RedirectStream): + """Context manager for temporarily redirecting stderr to another file.""" + + _stream = "stderr" + + +class suppress(object): + """Context manager to suppress specified exceptions + + After the exception is suppressed, execution proceeds with the next + statement following the with statement. + + with suppress(FileNotFoundError): + os.remove(somefile) + # Execution still resumes here if the file was already removed + """ + + def __init__(self, *exceptions): + self._exceptions = exceptions + + def __enter__(self): + pass + + def __exit__(self, exctype, excinst, exctb): + # Unlike isinstance and issubclass, CPython exception handling + # currently only looks at the concrete type hierarchy (ignoring + # the instance and subclass checking hooks). While Guido considers + # that a bug rather than a feature, it's a fairly hard one to fix + # due to various internal implementation details. suppress provides + # the simpler issubclass based semantics, rather than trying to + # exactly reproduce the limitations of the CPython interpreter. + # + # See http://bugs.python.org/issue12029 for more details + return exctype is not None and issubclass(exctype, self._exceptions) + + +# Context manipulation is Python 3 only +_HAVE_EXCEPTION_CHAINING = sys.version_info[0] >= 3 +if _HAVE_EXCEPTION_CHAINING: + def _make_context_fixer(frame_exc): + def _fix_exception_context(new_exc, old_exc): + # Context may not be correct, so find the end of the chain + while 1: + exc_context = new_exc.__context__ + if exc_context is old_exc: + # Context is already set correctly (see issue 20317) + return + if exc_context is None or exc_context is frame_exc: + break + new_exc = exc_context + # Change the end of the chain to point to the exception + # we expect it to reference + new_exc.__context__ = old_exc + return _fix_exception_context + + def _reraise_with_existing_context(exc_details): + try: + # bare "raise exc_details[1]" replaces our carefully + # set-up context + fixed_ctx = exc_details[1].__context__ + raise exc_details[1] + except BaseException: + exc_details[1].__context__ = fixed_ctx + raise +else: + # No exception context in Python 2 + def _make_context_fixer(frame_exc): + return lambda new_exc, old_exc: None + + # Use 3 argument raise in Python 2, + # but use exec to avoid SyntaxError in Python 3 + def _reraise_with_existing_context(exc_details): + exc_type, exc_value, exc_tb = exc_details + exec("raise exc_type, exc_value, exc_tb") + +# Handle old-style classes if they exist +try: + from types import InstanceType +except ImportError: + # Python 3 doesn't have old-style classes + _get_type = type +else: + # Need to handle old-style context managers on Python 2 + def _get_type(obj): + obj_type = type(obj) + if obj_type is InstanceType: + return obj.__class__ # Old-style class + return obj_type # New-style class + + +# Inspired by discussions on http://bugs.python.org/issue13585 +class ExitStack(object): + """Context manager for dynamic management of a stack of exit callbacks + + For example: + + with ExitStack() as stack: + files = [stack.enter_context(open(fname)) for fname in filenames] + # All opened files will automatically be closed at the end of + # the with statement, even if attempts to open files later + # in the list raise an exception + + """ + def __init__(self): + self._exit_callbacks = deque() + + def pop_all(self): + """Preserve the context stack by transferring it to a new instance""" + new_stack = type(self)() + new_stack._exit_callbacks = self._exit_callbacks + self._exit_callbacks = deque() + return new_stack + + def _push_cm_exit(self, cm, cm_exit): + """Helper to correctly register callbacks to __exit__ methods""" + def _exit_wrapper(*exc_details): + return cm_exit(cm, *exc_details) + _exit_wrapper.__self__ = cm + self.push(_exit_wrapper) + + def push(self, exit): + """Registers a callback with the standard __exit__ method signature + + Can suppress exceptions the same way __exit__ methods can. + + Also accepts any object with an __exit__ method (registering a call + to the method instead of the object itself) + """ + # We use an unbound method rather than a bound method to follow + # the standard lookup behaviour for special methods + _cb_type = _get_type(exit) + try: + exit_method = _cb_type.__exit__ + except AttributeError: + # Not a context manager, so assume its a callable + self._exit_callbacks.append(exit) + else: + self._push_cm_exit(exit, exit_method) + return exit # Allow use as a decorator + + def callback(self, callback, *args, **kwds): + """Registers an arbitrary callback and arguments. + + Cannot suppress exceptions. + """ + def _exit_wrapper(exc_type, exc, tb): + callback(*args, **kwds) + # We changed the signature, so using @wraps is not appropriate, but + # setting __wrapped__ may still help with introspection + _exit_wrapper.__wrapped__ = callback + self.push(_exit_wrapper) + return callback # Allow use as a decorator + + def enter_context(self, cm): + """Enters the supplied context manager + + If successful, also pushes its __exit__ method as a callback and + returns the result of the __enter__ method. + """ + # We look up the special methods on the type to match the with statement + _cm_type = _get_type(cm) + _exit = _cm_type.__exit__ + result = _cm_type.__enter__(cm) + self._push_cm_exit(cm, _exit) + return result + + def close(self): + """Immediately unwind the context stack""" + self.__exit__(None, None, None) + + def __enter__(self): + return self + + def __exit__(self, *exc_details): + received_exc = exc_details[0] is not None + + # We manipulate the exception state so it behaves as though + # we were actually nesting multiple with statements + frame_exc = sys.exc_info()[1] + _fix_exception_context = _make_context_fixer(frame_exc) + + # Callbacks are invoked in LIFO order to match the behaviour of + # nested context managers + suppressed_exc = False + pending_raise = False + while self._exit_callbacks: + cb = self._exit_callbacks.pop() + try: + if cb(*exc_details): + suppressed_exc = True + pending_raise = False + exc_details = (None, None, None) + except: + new_exc_details = sys.exc_info() + # simulate the stack of exceptions by setting the context + _fix_exception_context(new_exc_details[1], exc_details[1]) + pending_raise = True + exc_details = new_exc_details + if pending_raise: + _reraise_with_existing_context(exc_details) + return received_exc and suppressed_exc + + +# Preserve backwards compatibility +class ContextStack(ExitStack): + """Backwards compatibility alias for ExitStack""" + + def __init__(self): + warnings.warn("ContextStack has been renamed to ExitStack", + DeprecationWarning) + super(ContextStack, self).__init__() + + def register_exit(self, callback): + return self.push(callback) + + def register(self, callback, *args, **kwds): + return self.callback(callback, *args, **kwds) + + def preserve(self): + return self.pop_all() + + +class nullcontext(AbstractContextManager): + """Context manager that does no additional processing. + Used as a stand-in for a normal context manager, when a particular + block of code is only sometimes used with a normal context manager: + cm = optional_cm if condition else nullcontext() + with cm: + # Perform operation, using optional_cm if condition is True + """ + + def __init__(self, enter_result=None): + self.enter_result = enter_result + + def __enter__(self): + return self.enter_result + + def __exit__(self, *excinfo): + pass diff --git a/venv/lib/python3.8/site-packages/pip/_vendor/distlib/__init__.py b/venv/lib/python3.8/site-packages/pip/_vendor/distlib/__init__.py new file mode 100644 index 0000000..e19aebd --- /dev/null +++ b/venv/lib/python3.8/site-packages/pip/_vendor/distlib/__init__.py @@ -0,0 +1,23 @@ +# -*- coding: utf-8 -*- +# +# Copyright (C) 2012-2019 Vinay Sajip. +# Licensed to the Python Software Foundation under a contributor agreement. +# See LICENSE.txt and CONTRIBUTORS.txt. +# +import logging + +__version__ = '0.3.0' + +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/venv/lib/python3.8/site-packages/pip/_vendor/distlib/__pycache__/__init__.cpython-38.pyc b/venv/lib/python3.8/site-packages/pip/_vendor/distlib/__pycache__/__init__.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..dffbf2919e3cbb901e1c01ced58dc28735330174 GIT binary patch literal 1074 zcmah|O>fgc5S_Ih$8p*c3PN1CRY*BBj#{Xwgis}@0IAA>f&gDuu6LWdacr}8QW|k< zPyCAZ*uS(_PMo=MVrJcss30-cj%G45`)1!vJ}fT!fU@%FyV$k>evmUS9y(ii$_$kN z!BPl8SRs22!WPac2uE5cc4*H!7;U65c0z~2N5~#KAR8lJy<<|7 zA+tV#gtMmf&RN)!tutRbCveUf;_O1#`-zw^m^J#n^aOTX@O^R1!iaAhb-O zr8}>ZvPzS^?XR&cswB?_U0ZvcXVF-4u6@qOxtOG6FL3^45~X$I0+frl8TGpg3YAQw zN(R$N1^am_q$-0V4mOU8>65i=bFC6$W3PPH-yhG$Py0rOS@1q6l45m|6;V8rV)f`i z%5-%U9UtbK>&Fxe_*7;hSAnRz3^-4+q~g3+94njxwCP!7c+B`+rrU!_n!brLkxKO! z6_mO~H7lrYs1VjEgmyir&=C#cqTdir;#SdQV=BB7vWe4|cuEJAfL*v?14G7zrJYiy z``T49&Xu^KNi{K5y9cJVWwd|cf!Xq0zr8y6e>By?()wyz%iAE194FO13M>&sXC8t= zF|~gsWYd$(bLch!-+-G(85rc$N%MHrWs2y&jpfHm;^=oU<35EA;)8j_n}k8vRW>CN zYpOwNk<=YhcS&6@Rc#SvW&ji{Dd;i!%x~ScZl?Kgm}EoU=C~@V#0ADpZC!7(SqhV^ z+i%B3uBvUNa;2M>3!+;?S?%B*QkllIOBYI|{YZ$*oPYJCz327+;steBcBnDSu{yTJ LI&6tOZ20UCb8X@K literal 0 HcmV?d00001 diff --git a/venv/lib/python3.8/site-packages/pip/_vendor/distlib/__pycache__/compat.cpython-38.pyc b/venv/lib/python3.8/site-packages/pip/_vendor/distlib/__pycache__/compat.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..68308bc7021e60ccc213ccd454dc4ba40c778668 GIT binary patch literal 32223 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⁣hUI4Aj>rsLZx9 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 D^t+bB literal 0 HcmV?d00001 diff --git a/venv/lib/python3.8/site-packages/pip/_vendor/distlib/__pycache__/index.cpython-38.pyc b/venv/lib/python3.8/site-packages/pip/_vendor/distlib/__pycache__/index.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..a91ed91fe20214ef3fbec6b2fa1407e749261269 GIT binary patch literal 17427 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 literal 0 HcmV?d00001 diff --git a/venv/lib/python3.8/site-packages/pip/_vendor/distlib/__pycache__/manifest.cpython-38.pyc b/venv/lib/python3.8/site-packages/pip/_vendor/distlib/__pycache__/manifest.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..a9d07b6a2df31c4d447133963e68975bdc1f7f29 GIT binary patch literal 10249 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 literal 0 HcmV?d00001 diff --git a/venv/lib/python3.8/site-packages/pip/_vendor/distlib/__pycache__/markers.cpython-38.pyc b/venv/lib/python3.8/site-packages/pip/_vendor/distlib/__pycache__/markers.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..713f46478e225ffcaaf15032ad6b94f0088cd5fc GIT binary patch literal 4513 zcmb7H&2!tv6~_V~2vQV9TZ&uHO2=f?I8=j!Ea>($3tn6~qnfi?~KN^+d-$B`TKlh7wo zUKYfWv*6 zq_2ZyTCQbmtc!ZCrKdT&i8|NMV#|6KmTB&MB52I_`djBPSv5$5D9ya6< zg=e8fYJq61&P*oi;5t;Df0j}C0442$_!D+)3z<@wYidu@66}1(uC|Xr? zNzt03%Zk<&UHPU4Gi>lvFrpGaja^)omF+_>9C%qQhQF*jcy{-L)ais?nmS+xDU2eE z9lz-1PwdC>k8sE-3?N-sqdGd2o<77=QDjZNzz(qr z^QN|dg(SZ910-q-Nqqa|5}#T9Vv?ILmpohjV*B~d%OyUe>td3B_Cm>Y zyUbZ|Y~{Fv7C8Hhi%Luum3Zle5})-l+>Hz9xc&nqmKT!Pno4ZehWggl(AWaeze4%R z*7t04sBhwReW+jGl7<&?smDU7+?WYSnyE}cm)RN zJxiPi4@mtRFkxie{b$Z{y;m03GM$%lc19bszoI2a?~xJxLrE~kdU_)>(ayMqb~86~ z?VyBFQD*USZcauqZsRMY@Cx2mRtATbF%#b^9<7Skc{MLVhSfDDBee%MTJ(qdsGJjT zDc+`8a`UW~mveipkBl5X-&`7w)zwLt?&Om59dGV}6~ztf04fK9z69avZvf|RFnH7i%+Hpa~^!R^xhHHbU z7sdA?2Y4BHd!fJKTu5%Nu@nS zr)EW(sULQw349W=JXx)@u$6-_6R%?Kw&_DxP$(;2BV{PscnXOG0ninclo+j4Wi?jU zZP2P-XEvj{jo**iX}t!%!RCQ#s?JW+lD6wcUf*|JS#{lh%m*RyHP^j6@WRQLvg`7= z<5oeGpxBv`sja;4^ z5UkvI%zmW3M}2TjBRm}17t(e0mnFouUZ^Y`CsOKr2Qau&KZuGK0%2Jbe&~C!9ske| zrGZdZTCl{x3laL7LDY?9`4CYfEK!=hAmVc;q(YZf1lxTtbGw5uL{zAH?$AWQ0S-0{Nn`ucMO$of;!HoGjf$Fx(;np}PIn=&(b=BdW1&gD2Q zIy92d%V;XZ2aF*eR(fhJ}l(Akrky!G)Acd)ix;Ckqm^Ke9Yk)+%%q2G{t+wZxJDD6Dk~1&NlNnx$@Lu{T>}t>ZmchOm{$4H<@YZOh;6I;)^zT#QEP@ zb@4g+J`r7#24oV-5C0Bk*q}^j%>Jy8fDB(~(G?S((yNM(7!ZnS!!;lp3_mx!dVw-u zXjg%NBl9csw)RIY(r;_IA$V?(D{vEz!4(Fgbvhz5G80}6L=(fj^Z@UNjFAtQ;nk&w zlr~z!|0wwQY?_@pGbjRFPcR5=#Z8KQF+t{obk70G%;lHsRV8HuH^0}PVA%$8ido`- zTw<007AUkq3FE9QJa1ltf8=)tbpKENr4Tl8o+3S=G?(nb3FEjoNam$JXL#RB66mca z88&Z41aFk#%(R*!aH`OWu#3u0k!`X!hwls`%6>W-Wk$jaLfQrHWgekzkpNByHEmJG z5hKYu>i7vQL%e~yaHw(RSKFU_c~7!QmDV7E7++Q<_z??*1-P=TLTvlg zGwTtQX-E^Nl1b(Zl_flB#DYTYyC^B8JX%EuST)!=MFAp!vL@SUu8E8ID1Jla*F=5` zB6Z`^Ad8>A#u~b%+oeX!e#2rq`2Pcd;}TZ@ literal 0 HcmV?d00001 diff --git a/venv/lib/python3.8/site-packages/pip/_vendor/distlib/__pycache__/metadata.cpython-38.pyc b/venv/lib/python3.8/site-packages/pip/_vendor/distlib/__pycache__/metadata.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..151d979aa6bf93410ea8a0774d22066274f085f5 GIT binary patch literal 26660 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 I75}3D2M6-8E&u=k literal 0 HcmV?d00001 diff --git a/venv/lib/python3.8/site-packages/pip/_vendor/distlib/__pycache__/resources.cpython-38.pyc b/venv/lib/python3.8/site-packages/pip/_vendor/distlib/__pycache__/resources.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..2de2987afe0279c6ad173c9f13b04deff9931f62 GIT binary patch literal 11024 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 literal 0 HcmV?d00001 diff --git a/venv/lib/python3.8/site-packages/pip/_vendor/distlib/__pycache__/util.cpython-38.pyc b/venv/lib/python3.8/site-packages/pip/_vendor/distlib/__pycache__/util.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..2f0be40e697c93f6c143c4b028e110fdbf6c7ecd GIT binary patch literal 48177 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 literal 0 HcmV?d00001 diff --git a/venv/lib/python3.8/site-packages/pip/_vendor/distlib/__pycache__/version.cpython-38.pyc b/venv/lib/python3.8/site-packages/pip/_vendor/distlib/__pycache__/version.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..bbddea5feae0e9ab362c063405d795881dd7deae GIT binary patch literal 20383 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 literal 0 HcmV?d00001 diff --git a/venv/lib/python3.8/site-packages/pip/_vendor/distlib/__pycache__/wheel.cpython-38.pyc b/venv/lib/python3.8/site-packages/pip/_vendor/distlib/__pycache__/wheel.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..08160dfceebe9dda0170cc8c6ca0391bf843a8d6 GIT binary patch literal 25581 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 literal 0 HcmV?d00001 diff --git a/venv/lib/python3.8/site-packages/pip/_vendor/distlib/_backport/__pycache__/misc.cpython-38.pyc b/venv/lib/python3.8/site-packages/pip/_vendor/distlib/_backport/__pycache__/misc.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..a223269a8cc57932b78cee89a92be92c676800de GIT binary patch literal 1110 zcmZV+O^e$`blym^W!Yf)AWBnW$VN*(mIOj+yW~(PzI00)U4%R{cI1^bVrJ~D zjgvz+r~ZoM5-9Ya3z=o{B36^A_(8!_=~^y2>IZIFCj$X z2Soi35>8UWfzuLFX#X^&+~YnEW?t&8cTv|V{VGTUAitCPrcXrYBvQWsaUUP>kbAEo z4W5yj{z2BE=%!utyN!M!`n}rv0q>Q)DoP`aiMaooq}^>qBw*f8`xtW_V+I)WZX46* z0s8gN{W$%VjCR(^PdPhjq%u05N);D1FO~&gTHpPMEt0X_ zV>x>+vZ<0)rsYC0VMCUeWj-m9n`%)r$;Aj1MfYRo=mp>eiIa~I^(7=5P$7pV;`Alm zkPW>`urhCKfmx7$Bb#=cRW>c~<$#OHVrD&YYE&0PTk>h0G^$wUMkLEzB~w{)p>)!) z`*sIq$c7j= zb9;dCh}x*oMXgO2Amw%)={4{{-%KK*#HA;MpUh(sAsl!ylTW#rKGaHLDq&@R_B$-GB-OH z;H3Yz?b`YVQ4f(2h`>i2QXj(4BRC%I*lw0_$+FB+sg+xa)8Had;}uoYZp!w5uH3P= z+1p9l-WZdeSJP**EU{a6=I(c>n+a literal 0 HcmV?d00001 diff --git a/venv/lib/python3.8/site-packages/pip/_vendor/distlib/_backport/__pycache__/shutil.cpython-38.pyc b/venv/lib/python3.8/site-packages/pip/_vendor/distlib/_backport/__pycache__/shutil.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..62eac1f6cc3e3c3e36e31fbf20108327a84b4040 GIT binary patch literal 21503 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 literal 0 HcmV?d00001 diff --git a/venv/lib/python3.8/site-packages/pip/_vendor/distlib/_backport/__pycache__/sysconfig.cpython-38.pyc b/venv/lib/python3.8/site-packages/pip/_vendor/distlib/_backport/__pycache__/sysconfig.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..e86215bde8087aa28337c3759a4c00b056118f99 GIT binary patch literal 15930 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 ToNs9sQop4=57H&+fX4YhOuD>D literal 0 HcmV?d00001 diff --git a/venv/lib/python3.8/site-packages/pip/_vendor/distlib/_backport/__pycache__/tarfile.cpython-38.pyc b/venv/lib/python3.8/site-packages/pip/_vendor/distlib/_backport/__pycache__/tarfile.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..c19726703e80b5d5ed32ec783e0c3419e9ae8ccd GIT binary patch literal 62759 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 P%(jlz25t&g6!ZTFAH|Bz literal 0 HcmV?d00001 diff --git a/venv/lib/python3.8/site-packages/pip/_vendor/distlib/_backport/misc.py b/venv/lib/python3.8/site-packages/pip/_vendor/distlib/_backport/misc.py new file mode 100644 index 0000000..cfb318d --- /dev/null +++ b/venv/lib/python3.8/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/venv/lib/python3.8/site-packages/pip/_vendor/distlib/_backport/shutil.py b/venv/lib/python3.8/site-packages/pip/_vendor/distlib/_backport/shutil.py new file mode 100644 index 0000000..159e49e --- /dev/null +++ b/venv/lib/python3.8/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/venv/lib/python3.8/site-packages/pip/_vendor/distlib/_backport/sysconfig.cfg b/venv/lib/python3.8/site-packages/pip/_vendor/distlib/_backport/sysconfig.cfg new file mode 100644 index 0000000..1746bd0 --- /dev/null +++ b/venv/lib/python3.8/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/venv/lib/python3.8/site-packages/pip/_vendor/distlib/_backport/sysconfig.py b/venv/lib/python3.8/site-packages/pip/_vendor/distlib/_backport/sysconfig.py new file mode 100644 index 0000000..b470a37 --- /dev/null +++ b/venv/lib/python3.8/site-packages/pip/_vendor/distlib/_backport/sysconfig.py @@ -0,0 +1,786 @@ +# -*- 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) + +_PY_VERSION = '%s.%s.%s' % sys.version_info[:3] +_PY_VERSION_SHORT = '%s.%s' % sys.version_info[:2] +_PY_VERSION_SHORT_NO_DOT = '%s%s' % sys.version_info[: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'ProductUserVisibleVersion\s*' + r'(.*?)', 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/venv/lib/python3.8/site-packages/pip/_vendor/distlib/_backport/tarfile.py b/venv/lib/python3.8/site-packages/pip/_vendor/distlib/_backport/tarfile.py new file mode 100644 index 0000000..d66d856 --- /dev/null +++ b/venv/lib/python3.8/site-packages/pip/_vendor/distlib/_backport/tarfile.py @@ -0,0 +1,2607 @@ +#------------------------------------------------------------------- +# tarfile.py +#------------------------------------------------------------------- +# Copyright (C) 2002 Lars Gustaebel +# 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(" 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("= 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/venv/lib/python3.8/site-packages/pip/_vendor/distlib/compat.py b/venv/lib/python3.8/site-packages/pip/_vendor/distlib/compat.py new file mode 100644 index 0000000..ff328c8 --- /dev/null +++ b/venv/lib/python3.8/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[a-z]+)://(?P.*)$') + + WORD_PATTERN = re.compile(r'^\s*(\w+)\s*') + DOT_PATTERN = re.compile(r'^\.\s*(\w+)\s*') + INDEX_PATTERN = re.compile(r'^\[\s*(\w+)\s*\]\s*') + DIGIT_PATTERN = re.compile(r'^\d+$') + + value_converters = { + 'ext' : 'ext_convert', + 'cfg' : 'cfg_convert', + } + + # We might want to use a different one, e.g. importlib + importer = 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/venv/lib/python3.8/site-packages/pip/_vendor/distlib/database.py b/venv/lib/python3.8/site-packages/pip/_vendor/distlib/database.py new file mode 100644 index 0000000..c16c0c8 --- /dev/null +++ b/venv/lib/python3.8/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 '' % (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().decode('utf-8') + self.modules = data.splitlines() + + def __repr__(self): + return '' % ( + 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 '' % ( + 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/venv/lib/python3.8/site-packages/pip/_vendor/distlib/index.py b/venv/lib/python3.8/site-packages/pip/_vendor/distlib/index.py new file mode 100644 index 0000000..7a87cdc --- /dev/null +++ b/venv/lib/python3.8/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.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/venv/lib/python3.8/site-packages/pip/_vendor/distlib/locators.py b/venv/lib/python3.8/site-packages/pip/_vendor/distlib/locators.py new file mode 100644 index 0000000..12a1d06 --- /dev/null +++ b/venv/lib/python3.8/site-packages/pip/_vendor/distlib/locators.py @@ -0,0 +1,1302 @@ +# -*- 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.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.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 a "digests" dictionary + or 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 + if 'digests' in info: + digests = info['digests'] + for algo in ('sha256', 'md5'): + if algo in digests: + result = (algo, digests[algo]) + break + if not result: + 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[^"]*)"|'(?P[^']*)'|(?P[^>\\s\n]*))\\s+)? +href\\s*=\\s*(?:"(?P[^"]*)"|'(?P[^']*)'|(?P[^>\\s\n]*)) +(\\s+rel\\s*=\\s*(?:"(?P[^"]*)"|'(?P[^']*)'|(?P[^>\\s\n]*)))? +""", re.I | re.S | re.X) + _base = re.compile(r"""]+)""", 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(']*>([^<]+)<') + + 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.org/simple/', + timeout=3.0), + scheme='legacy') + +locate = default_locator.locate + +NAME_VERSION_RE = re.compile(r'(?P[\w-]+)\s*' + r'\(\s*(==\s*)?(?P[^)]+)\)$') + +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/venv/lib/python3.8/site-packages/pip/_vendor/distlib/manifest.py b/venv/lib/python3.8/site-packages/pip/_vendor/distlib/manifest.py new file mode 100644 index 0000000..ca0fe44 --- /dev/null +++ b/venv/lib/python3.8/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 ...' % 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

...' % 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 ' % 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'((? 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/venv/lib/python3.8/site-packages/pip/_vendor/distlib/metadata.py b/venv/lib/python3.8/site-packages/pip/_vendor/distlib/metadata.py new file mode 100644 index 0000000..2d61378 --- /dev/null +++ b/venv/lib/python3.8/site-packages/pip/_vendor/distlib/metadata.py @@ -0,0 +1,1096 @@ +# -*- 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' and 'Provides' occur wrongly in +# the metadata. Include them in the tuple literal below to allow them +# (for now). +_566_FIELDS = _426_FIELDS + ('Description-Content-Type', + 'Requires', 'Provides') + +_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/venv/lib/python3.8/site-packages/pip/_vendor/distlib/resources.py b/venv/lib/python3.8/site-packages/pip/_vendor/distlib/resources.py new file mode 100644 index 0000000..1884016 --- /dev/null +++ b/venv/lib/python3.8/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/venv/lib/python3.8/site-packages/pip/_vendor/distlib/scripts.py b/venv/lib/python3.8/site-packages/pip/_vendor/distlib/scripts.py new file mode 100644 index 0000000..5185974 --- /dev/null +++ b/venv/lib/python3.8/site-packages/pip/_vendor/distlib/scripts.py @@ -0,0 +1,416 @@ +# -*- 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 = ''' + + + + + + + + + + + + +'''.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 -*- +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()) +''' + + +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 - COMMENTED OUT + # executable = os.path.normcase(executable) + # N.B. The normalising operation above has been commented out: See + # issue #124. Although paths in Windows are generally case-insensitive, + # they aren't always. For example, a path containing a ẞ (which is a + # LATIN CAPITAL LETTER SHARP S - U+1E9E) is normcased to ß (which is a + # LATIN SMALL LETTER SHARP S' - U+00DF). The two are not considered by + # Windows as equivalent in path names. + + # 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, + import_name=entry.suffix.split('.')[0], + 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_info[0])) + if 'X.Y' in self.variants: + scriptnames.add('%s-%s.%s' % (name, sys.version_info[0], + sys.version_info[1])) + 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] + resource = finder(distlib_package).find(name) + if not resource: + msg = ('Unable to find resource %s in package %s' % (name, + distlib_package)) + raise ValueError(msg) + return resource.bytes + + # 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/venv/lib/python3.8/site-packages/pip/_vendor/distlib/t32.exe b/venv/lib/python3.8/site-packages/pip/_vendor/distlib/t32.exe new file mode 100644 index 0000000000000000000000000000000000000000..8932a18e4596952373a38c60b81b7116d4ef9ee8 GIT binary patch literal 96768 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 F{{j_Hb~FG0 literal 0 HcmV?d00001 diff --git a/venv/lib/python3.8/site-packages/pip/_vendor/distlib/t64.exe b/venv/lib/python3.8/site-packages/pip/_vendor/distlib/t64.exe new file mode 100644 index 0000000000000000000000000000000000000000..325b8057c08cf7113d4fd889991fa5638d443793 GIT binary patch literal 105984 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

^h^N4aZyJe%k(7BL*-gn=9``8`j0CuR45%cHI z_uuQ8!-|TEvJ}r=zF@%uKc8U@W1eNxUymJ(e45Tb6KDNieQcKe?L-gR8zZj^wFmi= z{5sAxrfP3BOZz~T$3h=Gi%jFg1%D>!6t*l^`zH2G#1PiYtvOBSI#q&y?8qN57P^LA zq9U)rQU+*y!XEgsGMCJM7yWOS+9lW~^axz>9gyv{Pu^qsBg%ZkfzkaN`$zV#>=oFn zwANnf4&g*eu~pAMC~1dl8FZ-^aeQgZ7=osPU=58@oke z55pen;eU@Z`iL!`$;1-VA&$VF4gN7ttU>relx5d-_x=|95B47HeeiYJZ$$38(ddJH zcrW3>{OR2@KF^H}gAbdZDX=AzyZHSizB_(9`v&$-69)D4WBjSaY@YVD`kl8;nl#Cu z5h1U}Tp}-l|Nde9w|3Pc@Aps8-X~fh_EGq!b-~*$a&nv>05-_n;)z{t+vW|PpX{Oj zKE#i|Gsq9Jhpor%Fqiu6y5}jjnz?*$b)h|UO;3NGd-k|9?ZqeVXL!9~vaIO0E8bVb zejzv5ZG0}~1A7{a3!hob11v4ihxvzh!S5>3I?4E~N9+^m8@sHve^M+wb{f3t2VUsD z*C*C&;z_`=&t~mbE@mHC`k7cGl3rKU9U84p?fzGxTg1q z)-Ai@eQSs49?#VDZ(BQ5_sXt#*VAn)X1Lk5l>kvHP6SDZX>#ITM7@`jxR;sQ>OG+Pe$CuXbGOdj zGq+|zTtQMnhk{-O{R=KF7*}vZ!OVhr1xpLo6l^NkTCk^}W?@_*Z|>hH7&o`>+{q8j zm_Kv=-1+n7FPgt}{>u4l=C7T3KtbFEnHc+rVzeuEi5hE<2hHiD6S}>D5l co{tw5U0O7=?|={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 '' % (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(\w|[-.+])+) + \s*=\s*(?P(\w+)([:\.]\w+)*) + \s*(\[\s*(?P[\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.rsplit('@', 1) + if ':' not in prefix: + username = prefix + else: + username, password = prefix.split(':', 1) + if username: + username = unquote(username) + if password: + password = unquote(password) + 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[\w .-]+)\s*' + r'\(\s*(?P[^\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) + if hasattr(ssl, 'OP_NO_SSLv2'): + 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/venv/lib/python3.8/site-packages/pip/_vendor/distlib/version.py b/venv/lib/python3.8/site-packages/pip/_vendor/distlib/version.py new file mode 100644 index 0000000..3eebe18 --- /dev/null +++ b/venv/lib/python3.8/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/venv/lib/python3.8/site-packages/pip/_vendor/distlib/w32.exe b/venv/lib/python3.8/site-packages/pip/_vendor/distlib/w32.exe new file mode 100644 index 0000000000000000000000000000000000000000..e6439e9e45897365d5ac6a85a46864c158a225fd GIT binary patch literal 90112 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�gPVRVBXt_FM7e4<}XM;HRH{62JZSn^<*L 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 OB+yBqlfeHwf&T)jA)JE% literal 0 HcmV?d00001 diff --git a/venv/lib/python3.8/site-packages/pip/_vendor/distlib/w64.exe b/venv/lib/python3.8/site-packages/pip/_vendor/distlib/w64.exe new file mode 100644 index 0000000000000000000000000000000000000000..46139dbf9400b7bc0b64e6756ce17b4eb5fd7436 GIT binary patch literal 99840 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[^-]+) +-(?P\d+[^-]*) +(-(?P\d+[^-]*))? +-(?P\w+\d+(\.\w+\d+)*) +-(?P\w+) +-(?P\w+(\.\w+)*) +\.whl$ +''', re.IGNORECASE | re.VERBOSE) + +NAME_VERSION_RE = re.compile(r''' +(?P[^-]+) +-(?P\d+[^-]*) +(-(?P\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 skip_entry(self, arcname): + """ + Determine whether an archive entry should be skipped when verifying + or installing. + """ + # The signature file won't be in RECORD, + # and we don't currently don't do anything with it + # We also skip directories, as they won't be in RECORD + # either. See: + # + # https://github.com/pypa/wheel/issues/294 + # https://github.com/pypa/wheel/issues/287 + # https://github.com/pypa/wheel/pull/289 + # + return arcname.endswith(('/', '/RECORD.jws')) + + 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') + if self.skip_entry(u_arcname): + 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'), + '%s.%s' % sys.version_info[:2]) + 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') + # See issue #115: some wheels have .. in their entries, but + # in the filename ... e.g. __main__..py ! So the check is + # updated to look for .. in the directory portions + p = u_arcname.split('/') + if '..' in p: + raise DistlibException('invalid entry in ' + 'wheel: %r' % u_arcname) + + if self.skip_entry(u_arcname): + 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/venv/lib/python3.8/site-packages/pip/_vendor/distro.py b/venv/lib/python3.8/site-packages/pip/_vendor/distro.py new file mode 100644 index 0000000..0611b62 --- /dev/null +++ b/venv/lib/python3.8/site-packages/pip/_vendor/distro.py @@ -0,0 +1,1230 @@ +# 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 `_ 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 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 = { + 'enterpriseenterpriseas': 'oracle', # Oracle Enterprise Linux 4 + 'enterpriseenterpriseserver': 'oracle', # Oracle Linux 5 + 'redhatenterpriseworkstation': 'rhel', # RHEL 6, 7 Workstation + 'redhatenterpriseserver': 'rhel', # RHEL 6, 7 Server + 'redhatenterprisecomputenode': 'rhel', # RHEL 6 ComputeNode +} + +#: 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', + 'plesk-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 + "midnightbsd" MidnightBSD + ============== ========================================= + + 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 "" 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 "" field of the distro release file, appended + with the value of the pretty version ("" and "" + 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 "" 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 + `_. + """ + 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 "" 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_uname`` (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 = '{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) + 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 = self._to_str(stdout).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 = self._to_str(stdout).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 + + @staticmethod + def _to_str(text): + encoding = sys.getfilesystemencoding() + encoding = 'utf-8' if encoding == 'ascii' else encoding + + if sys.version_info[0] >= 3: + if isinstance(text, bytes): + return text.decode(encoding) + else: + if isinstance(text, unicode): # noqa + return text.encode(encoding) + + return text + + @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. + """ + 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/venv/lib/python3.8/site-packages/pip/_vendor/ipaddress.py b/venv/lib/python3.8/site-packages/pip/_vendor/ipaddress.py new file mode 100644 index 0000000..3e6f9e4 --- /dev/null +++ b/venv/lib/python3.8/site-packages/pip/_vendor/ipaddress.py @@ -0,0 +1,2420 @@ +# 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.23' + +# 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/venv/lib/python3.8/site-packages/pip/_vendor/msgpack/__init__.py b/venv/lib/python3.8/site-packages/pip/_vendor/msgpack/__init__.py new file mode 100644 index 0000000..d6705e2 --- /dev/null +++ b/venv/lib/python3.8/site-packages/pip/_vendor/msgpack/__init__.py @@ -0,0 +1,54 @@ +# coding: utf-8 +from ._version import version +from .exceptions import * +from .ext import ExtType, Timestamp + +import os +import sys + + +if os.environ.get("MSGPACK_PUREPYTHON") or sys.version_info[0] == 2: + from .fallback import Packer, unpackb, Unpacker +else: + try: + from ._cmsgpack import Packer, unpackb, Unpacker + except ImportError: + from .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/venv/lib/python3.8/site-packages/pip/_vendor/msgpack/__pycache__/__init__.cpython-38.pyc b/venv/lib/python3.8/site-packages/pip/_vendor/msgpack/__pycache__/__init__.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..90e312bcfe5e965f1ab4eaf93f49c634512064c7 GIT binary patch literal 1420 zcmb7EO>Y}F5GD6pYf1h}T{l3AMNt${0DFTZ4Vs`R+9pO@1a*yAMv^^5S}iGOt=;8< zq~*v^Zao!6euEA4lE0H9uRZxM^wc3YHk=|ogaU^{?vR}K=23f{PKcm=dF>DK+(GDX zJ-OThn0yaiyahuL!38RDg0c3k!s_eMF6`1toYGBPSa*n9c!_65Kk=z|5S+A#cZ$U} z^$$Yge1?cWu{eR9L9tM_lXlrjI%PNMVpRQjyl&PP-(P=%s_PD-OOufQg^~V!X!kQz zSx*sZk??nvERzM&hH-^-NEgOcx|&>}o#Yd`oUGB6e24Fp`{3+!kXBD{k zM$c0I)=Sx+)l{|mc}ay#%lZr=(RQ-(>+VlGKRkFe*x7r$wezh1^KaW{mOjK*-cFiL zC|CYrRf9KD!Jcs_&ve9j1EP@&{SkEWIT(q?Xl}j6uk1NK!E^h>nxj*Es%gfMq;>o> zjG*b$BQ`pqnT&>P7^M}7j(9Fr6XecD7X*mo{^e&~*{j5lZxIb+28b)KrBUVem zDK<^ecF*Rz#>zDZDM#nsa};LEYfe+%785Ey<3uyyCd<<&2f2Letpxl{VMLqbzW~Y1W~ea^n%i2W0KAe*uC2` z{Vg6$X+;>1OR=x7j0c0f%H?3NS6Cwv02G<5zm`u+J{6ji(hMI zfmPrWA3ynDJ~@?#yowA~5{+h(ijSbPXBjq4Qs8&scD|^MrmQh5;3-$1Ke`64Fv9S- z2K62c4dDXD6`0J?2|7iuEQ#mV7|#Kaf%2z;(&!8najuUsXXS+;HX8_!(_Byy4MEyG zeVEF07yp~i|Jg`ACMC~90w#wcncB+X?0;R1Sp5VegCDw zYh>^hL^TEV1I*X-)^!zt49ttDg`k0u|Y=6cHf(7{oyaj6jY95EpX*i4=w?h7`tN22CbS=9eIumq5%=!~`V# zG#PKPm!%dJXXfXvWGG?*iGzq=CHe(LnPrJ3srqG!Mfz#^IVnIz`UT1QX5|HCCPsOd zMkOgJW=X{s25GrfxuynSke;U>qF<0%pj(<(keHmEnxb2tk(!#Lo1Ivhm2YWW36j%~ oFH6l!$uH8+Elvk10`bAt>lIYq;;_lhPbtkwwF9~GGY~TX0HJF=Hvj+t literal 0 HcmV?d00001 diff --git a/venv/lib/python3.8/site-packages/pip/_vendor/msgpack/__pycache__/exceptions.cpython-38.pyc b/venv/lib/python3.8/site-packages/pip/_vendor/msgpack/__pycache__/exceptions.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..b28e477768b71c4bad1bd1d9294424e2994015c2 GIT binary patch literal 1868 zcmb7E&2HO95GF-Rq9`koT_CpxduV|OsKp>{gSsdJBdt^9A{U6=122m;cWE&px#}({ zS%PoLsc+(E=^OBxx5%k8Oa0JpTp$bVVCQdVc6Pow9}Ij8);C}O&5vBm`iM+(wShT? zt*?OmZgGp-A7BgXy%XBNx3~kmBW&PXz`MK+eB1C2@EzU-zAIdixM3S49`Avqx0ZB- z8+JkJ^FB!Xq6_zU!U{nj`l281@x3?JXb?g4TgwLS7`7e)0ehQUVT;?L!&_kQR1eSd z8H?VXTt#A5Br;$9^O9*nqLgV(VyTFhnIK}L)D;)LM`_M7L22bvnn_-!c;2J*uac!}O_x$GBT8>9)rB${@@XE- zR5E9U2lP5tHvIkHx(7zPZ-p;yRsPE| zj)i(vrs?FyQ4UxrXjw~PkM15=pFEWHfM9!XLL&b~A$zA~aT@=|3N}%A(PyLdLGhpw z1^N4<+|#f9S5jrHIKd8BJ$jwbS(;S?%YJ^4pKVdQHV>BW_c7KXc#o-aU_0S00yaWH-t$HU{g<@Fr)%)#{ zBn^m)9H1tYKp~hWDIk?_a9G3jYzm5P_uXR?-d@x~xR5N*~5R3;zv;x=MsvU*%+@Z@4dl!!z!huLOQ zsq19Ik8HIME_YLbsR8AnbObts9LrwqH}f#s$*6M|f*rgJyHI^~`&u)63?dOHk6~-H z^=!A}xe8^I(E$H_J*>@aFz8hXrpo`5rKK>9sj#yZ_E|MNH&eQy!<(wXAI-U6JwSG6 yQw>h%LdB_EnCq&2RNoRc%CXJ6@_wmD>gx5?m^pihC|nO?SBFPE~0q= literal 0 HcmV?d00001 diff --git a/venv/lib/python3.8/site-packages/pip/_vendor/msgpack/__pycache__/ext.cpython-38.pyc b/venv/lib/python3.8/site-packages/pip/_vendor/msgpack/__pycache__/ext.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..27dd167f74bef1aa0a8e3ddf8f6f754ce9e2777b GIT binary patch literal 6267 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 M(!B|D%AOkkA2#k#+yDRo literal 0 HcmV?d00001 diff --git a/venv/lib/python3.8/site-packages/pip/_vendor/msgpack/__pycache__/fallback.cpython-38.pyc b/venv/lib/python3.8/site-packages/pip/_vendor/msgpack/__pycache__/fallback.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..2a39f67e31d5a0efa37aa01495abdf9aab8eb24f GIT binary patch literal 25971 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 literal 0 HcmV?d00001 diff --git a/venv/lib/python3.8/site-packages/pip/_vendor/msgpack/_version.py b/venv/lib/python3.8/site-packages/pip/_vendor/msgpack/_version.py new file mode 100644 index 0000000..9f55cf5 --- /dev/null +++ b/venv/lib/python3.8/site-packages/pip/_vendor/msgpack/_version.py @@ -0,0 +1 @@ +version = (1, 0, 0) diff --git a/venv/lib/python3.8/site-packages/pip/_vendor/msgpack/exceptions.py b/venv/lib/python3.8/site-packages/pip/_vendor/msgpack/exceptions.py new file mode 100644 index 0000000..d6d2615 --- /dev/null +++ b/venv/lib/python3.8/site-packages/pip/_vendor/msgpack/exceptions.py @@ -0,0 +1,48 @@ +class UnpackException(Exception): + """Base class for some exceptions raised while unpacking. + + NOTE: unpack may raise exception other than subclass of + UnpackException. If you want to catch all error, catch + Exception instead. + """ + + +class BufferFull(UnpackException): + pass + + +class OutOfData(UnpackException): + pass + + +class FormatError(ValueError, UnpackException): + """Invalid msgpack format""" + + +class StackError(ValueError, UnpackException): + """Too nested""" + + +# Deprecated. Use ValueError instead +UnpackValueError = ValueError + + +class ExtraData(UnpackValueError): + """ExtraData is raised when there is trailing data. + + This exception is raised while only one-shot (not streaming) + unpack. + """ + + def __init__(self, unpacked, extra): + self.unpacked = unpacked + self.extra = extra + + def __str__(self): + return "unpack(b) received extra data." + + +# Deprecated. Use Exception instead to catch all exception during packing. +PackException = Exception +PackValueError = ValueError +PackOverflowError = OverflowError diff --git a/venv/lib/python3.8/site-packages/pip/_vendor/msgpack/ext.py b/venv/lib/python3.8/site-packages/pip/_vendor/msgpack/ext.py new file mode 100644 index 0000000..8341c68 --- /dev/null +++ b/venv/lib/python3.8/site-packages/pip/_vendor/msgpack/ext.py @@ -0,0 +1,191 @@ +# coding: utf-8 +from collections import namedtuple +import datetime +import sys +import struct + + +PY2 = sys.version_info[0] == 2 + +if PY2: + int_types = (int, long) + _utc = None +else: + int_types = int + try: + _utc = datetime.timezone.utc + except AttributeError: + _utc = datetime.timezone(datetime.timedelta(0)) + + +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) + + +class Timestamp(object): + """Timestamp represents the Timestamp extension type in msgpack. + + When built with Cython, msgpack uses C methods to pack and unpack `Timestamp`. When using pure-Python + msgpack, :func:`to_bytes` and :func:`from_bytes` are used to pack and unpack `Timestamp`. + + This class is immutable: Do not override seconds and nanoseconds. + """ + + __slots__ = ["seconds", "nanoseconds"] + + def __init__(self, seconds, nanoseconds=0): + """Initialize a Timestamp object. + + :param int seconds: + Number of seconds since the UNIX epoch (00:00:00 UTC Jan 1 1970, minus leap seconds). + May be negative. + + :param int nanoseconds: + Number of nanoseconds to add to `seconds` to get fractional time. + Maximum is 999_999_999. Default is 0. + + Note: Negative times (before the UNIX epoch) are represented as negative seconds + positive ns. + """ + if not isinstance(seconds, int_types): + raise TypeError("seconds must be an interger") + if not isinstance(nanoseconds, int_types): + raise TypeError("nanoseconds must be an integer") + if not (0 <= nanoseconds < 10 ** 9): + raise ValueError( + "nanoseconds must be a non-negative integer less than 999999999." + ) + self.seconds = seconds + self.nanoseconds = nanoseconds + + def __repr__(self): + """String representation of Timestamp.""" + return "Timestamp(seconds={0}, nanoseconds={1})".format( + self.seconds, self.nanoseconds + ) + + def __eq__(self, other): + """Check for equality with another Timestamp object""" + if type(other) is self.__class__: + return ( + self.seconds == other.seconds and self.nanoseconds == other.nanoseconds + ) + return False + + def __ne__(self, other): + """not-equals method (see :func:`__eq__()`)""" + return not self.__eq__(other) + + def __hash__(self): + return hash((self.seconds, self.nanoseconds)) + + @staticmethod + def from_bytes(b): + """Unpack bytes into a `Timestamp` object. + + Used for pure-Python msgpack unpacking. + + :param b: Payload from msgpack ext message with code -1 + :type b: bytes + + :returns: Timestamp object unpacked from msgpack ext payload + :rtype: Timestamp + """ + if len(b) == 4: + seconds = struct.unpack("!L", b)[0] + nanoseconds = 0 + elif len(b) == 8: + data64 = struct.unpack("!Q", b)[0] + seconds = data64 & 0x00000003FFFFFFFF + nanoseconds = data64 >> 34 + elif len(b) == 12: + nanoseconds, seconds = struct.unpack("!Iq", b) + else: + raise ValueError( + "Timestamp type can only be created from 32, 64, or 96-bit byte objects" + ) + return Timestamp(seconds, nanoseconds) + + def to_bytes(self): + """Pack this Timestamp object into bytes. + + Used for pure-Python msgpack packing. + + :returns data: Payload for EXT message with code -1 (timestamp type) + :rtype: bytes + """ + if (self.seconds >> 34) == 0: # seconds is non-negative and fits in 34 bits + data64 = self.nanoseconds << 34 | self.seconds + if data64 & 0xFFFFFFFF00000000 == 0: + # nanoseconds is zero and seconds < 2**32, so timestamp 32 + data = struct.pack("!L", data64) + else: + # timestamp 64 + data = struct.pack("!Q", data64) + else: + # timestamp 96 + data = struct.pack("!Iq", self.nanoseconds, self.seconds) + return data + + @staticmethod + def from_unix(unix_sec): + """Create a Timestamp from posix timestamp in seconds. + + :param unix_float: Posix timestamp in seconds. + :type unix_float: int or float. + """ + seconds = int(unix_sec // 1) + nanoseconds = int((unix_sec % 1) * 10 ** 9) + return Timestamp(seconds, nanoseconds) + + def to_unix(self): + """Get the timestamp as a floating-point value. + + :returns: posix timestamp + :rtype: float + """ + return self.seconds + self.nanoseconds / 1e9 + + @staticmethod + def from_unix_nano(unix_ns): + """Create a Timestamp from posix timestamp in nanoseconds. + + :param int unix_ns: Posix timestamp in nanoseconds. + :rtype: Timestamp + """ + return Timestamp(*divmod(unix_ns, 10 ** 9)) + + def to_unix_nano(self): + """Get the timestamp as a unixtime in nanoseconds. + + :returns: posix timestamp in nanoseconds + :rtype: int + """ + return self.seconds * 10 ** 9 + self.nanoseconds + + def to_datetime(self): + """Get the timestamp as a UTC datetime. + + Python 2 is not supported. + + :rtype: datetime. + """ + return datetime.datetime.fromtimestamp(self.to_unix(), _utc) + + @staticmethod + def from_datetime(dt): + """Create a Timestamp from datetime with tzinfo. + + Python 2 is not supported. + + :rtype: Timestamp + """ + return Timestamp.from_unix(dt.timestamp()) diff --git a/venv/lib/python3.8/site-packages/pip/_vendor/msgpack/fallback.py b/venv/lib/python3.8/site-packages/pip/_vendor/msgpack/fallback.py new file mode 100644 index 0000000..9f6665b --- /dev/null +++ b/venv/lib/python3.8/site-packages/pip/_vendor/msgpack/fallback.py @@ -0,0 +1,1063 @@ +"""Fallback pure Python implementation of msgpack""" + +from datetime import datetime as _DateTime +import sys +import struct + + +PY2 = sys.version_info[0] == 2 +if PY2: + int_types = (int, long) + + def dict_iteritems(d): + return d.iteritems() + + +else: + int_types = int + unicode = str + xrange = range + + def dict_iteritems(d): + return d.items() + + +if sys.version_info < (3, 5): + # Ugly hack... + RecursionError = RuntimeError + + def _is_recursionerror(e): + return ( + len(e.args) == 1 + and isinstance(e.args[0], str) + and e.args[0].startswith("maximum recursion depth exceeded") + ) + + +else: + + def _is_recursionerror(e): + return True + + +if hasattr(sys, "pypy_version_info"): + # StringIO 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 .exceptions import BufferFull, OutOfData, ExtraData, FormatError, StackError + +from .ext import ExtType, Timestamp + + +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): + view = memoryview(obj) + if view.itemsize != 1: + raise ValueError("cannot unpack from multi-byte object") + return view + + +def unpackb(packed, **kwargs): + """ + Unpack an object from `packed`. + + Raises ``ExtraData`` when *packed* contains extra bytes. + Raises ``ValueError`` when *packed* is incomplete. + Raises ``FormatError`` when *packed* is not valid msgpack. + Raises ``StackError`` when *packed* contains too nested. + Other exceptions can be raised during unpacking. + + See :class:`Unpacker` for options. + """ + unpacker = Unpacker(None, max_buffer_size=len(packed), **kwargs) + unpacker.feed(packed) + try: + ret = unpacker._unpack() + except OutOfData: + raise ValueError("Unpack failed: incomplete input") + except RecursionError as e: + if _is_recursionerror(e): + raise StackError + raise + if unpacker._got_extradata(): + raise ExtraData(ret, unpacker._get_extradata()) + return ret + + +if sys.version_info < (2, 7, 6): + + def _unpack_from(f, b, o=0): + """Explicit type cast for legacy struct.unpack_from""" + return struct.unpack_from(f, bytes(b), o) + + +else: + _unpack_from = struct.unpack_from + + +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. + Otherwise, unpack to Python str by decoding with UTF-8 encoding (default). + + :param int timestamp: + Control how timestamp type is unpacked: + + 0 - Timestamp + 1 - float (Seconds from the EPOCH) + 2 - int (Nanoseconds from the EPOCH) + 3 - datetime.datetime (UTC). Python 2 is not supported. + + :param bool strict_map_key: + If true (default), only str or bytes are accepted for map (dict) keys. + + :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 unicode_errors: + The error handler for decoding unicode. (default: 'strict') + This option should be used only when you have msgpack data which + contains invalid UTF-8 string. + + :param int max_buffer_size: + Limits size of data waiting unpacked. 0 means 2**32-1. + The default value is 100*1024*1024 (100MiB). + Raises `BufferFull` exception when it is insufficient. + You should set this parameter when unpacking data from untrusted source. + + :param int max_str_len: + Deprecated, use *max_buffer_size* instead. + Limits max length of str. (default: max_buffer_size) + + :param int max_bin_len: + Deprecated, use *max_buffer_size* instead. + Limits max length of bin. (default: max_buffer_size) + + :param int max_array_len: + Limits max length of array. + (default: max_buffer_size) + + :param int max_map_len: + Limits max length of map. + (default: max_buffer_size//2) + + :param int max_ext_len: + Deprecated, use *max_buffer_size* instead. + Limits max size of ext type. (default: max_buffer_size) + + Example of streaming deserialize from file-like object:: + + unpacker = Unpacker(file_like) + for o in unpacker: + process(o) + + Example of streaming deserialize from socket:: + + unpacker = Unpacker(max_buffer_size) + while True: + buf = sock.recv(1024**2) + if not buf: + break + unpacker.feed(buf) + for o in unpacker: + process(o) + + Raises ``ExtraData`` when *packed* contains extra bytes. + Raises ``OutOfData`` when *packed* is incomplete. + Raises ``FormatError`` when *packed* is not valid msgpack. + Raises ``StackError`` when *packed* contains too nested. + Other exceptions can be raised during unpacking. + """ + + def __init__( + self, + file_like=None, + read_size=0, + use_list=True, + raw=False, + timestamp=0, + strict_map_key=True, + object_hook=None, + object_pairs_hook=None, + list_hook=None, + unicode_errors=None, + max_buffer_size=100 * 1024 * 1024, + ext_hook=ExtType, + max_str_len=-1, + max_bin_len=-1, + max_array_len=-1, + max_map_len=-1, + max_ext_len=-1, + ): + 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() + #: 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 + + if not max_buffer_size: + max_buffer_size = 2 ** 31 - 1 + if max_str_len == -1: + max_str_len = max_buffer_size + if max_bin_len == -1: + max_bin_len = max_buffer_size + if max_array_len == -1: + max_array_len = max_buffer_size + if max_map_len == -1: + max_map_len = max_buffer_size // 2 + if max_ext_len == -1: + max_ext_len = max_buffer_size + + self._max_buffer_size = max_buffer_size + 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._strict_map_key = bool(strict_map_key) + self._unicode_errors = unicode_errors + self._use_list = use_list + if not (0 <= timestamp <= 3): + raise ValueError("timestamp must be 0..3") + self._timestamp = timestamp + 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 + + # Use extend here: INPLACE_ADD += doesn't reliably typecast memoryview in jython + self._buffer.extend(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): + ret = self._read(n) + self._consume() + return ret + + 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 ValueError("%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 ValueError("%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 ValueError("%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 ValueError("%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 = _unpack_from(">H", self._buffer, self._buff_i)[0] + self._buff_i += 2 + if n > self._max_bin_len: + raise ValueError("%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 = _unpack_from(">I", self._buffer, self._buff_i)[0] + self._buff_i += 4 + if n > self._max_bin_len: + raise ValueError("%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 = _unpack_from("Bb", self._buffer, self._buff_i) + self._buff_i += 2 + if L > self._max_ext_len: + raise ValueError("%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 = _unpack_from(">Hb", self._buffer, self._buff_i) + self._buff_i += 3 + if L > self._max_ext_len: + raise ValueError("%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 = _unpack_from(">Ib", self._buffer, self._buff_i) + self._buff_i += 5 + if L > self._max_ext_len: + raise ValueError("%s exceeds max_ext_len(%s)" % (L, self._max_ext_len)) + obj = self._read(L) + elif b == 0xCA: + self._reserve(4) + obj = _unpack_from(">f", self._buffer, self._buff_i)[0] + self._buff_i += 4 + elif b == 0xCB: + self._reserve(8) + obj = _unpack_from(">d", self._buffer, 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 = _unpack_from(">H", self._buffer, self._buff_i)[0] + self._buff_i += 2 + elif b == 0xCE: + self._reserve(4) + obj = _unpack_from(">I", self._buffer, self._buff_i)[0] + self._buff_i += 4 + elif b == 0xCF: + self._reserve(8) + obj = _unpack_from(">Q", self._buffer, self._buff_i)[0] + self._buff_i += 8 + elif b == 0xD0: + self._reserve(1) + obj = _unpack_from("b", self._buffer, self._buff_i)[0] + self._buff_i += 1 + elif b == 0xD1: + self._reserve(2) + obj = _unpack_from(">h", self._buffer, self._buff_i)[0] + self._buff_i += 2 + elif b == 0xD2: + self._reserve(4) + obj = _unpack_from(">i", self._buffer, self._buff_i)[0] + self._buff_i += 4 + elif b == 0xD3: + self._reserve(8) + obj = _unpack_from(">q", self._buffer, self._buff_i)[0] + self._buff_i += 8 + elif b == 0xD4: # fixext 1 + typ = TYPE_EXT + if self._max_ext_len < 1: + raise ValueError("%s exceeds max_ext_len(%s)" % (1, self._max_ext_len)) + self._reserve(2) + n, obj = _unpack_from("b1s", self._buffer, self._buff_i) + self._buff_i += 2 + elif b == 0xD5: # fixext 2 + typ = TYPE_EXT + if self._max_ext_len < 2: + raise ValueError("%s exceeds max_ext_len(%s)" % (2, self._max_ext_len)) + self._reserve(3) + n, obj = _unpack_from("b2s", self._buffer, self._buff_i) + self._buff_i += 3 + elif b == 0xD6: # fixext 4 + typ = TYPE_EXT + if self._max_ext_len < 4: + raise ValueError("%s exceeds max_ext_len(%s)" % (4, self._max_ext_len)) + self._reserve(5) + n, obj = _unpack_from("b4s", self._buffer, self._buff_i) + self._buff_i += 5 + elif b == 0xD7: # fixext 8 + typ = TYPE_EXT + if self._max_ext_len < 8: + raise ValueError("%s exceeds max_ext_len(%s)" % (8, self._max_ext_len)) + self._reserve(9) + n, obj = _unpack_from("b8s", self._buffer, self._buff_i) + self._buff_i += 9 + elif b == 0xD8: # fixext 16 + typ = TYPE_EXT + if self._max_ext_len < 16: + raise ValueError("%s exceeds max_ext_len(%s)" % (16, self._max_ext_len)) + self._reserve(17) + n, obj = _unpack_from("b16s", self._buffer, 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 ValueError("%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,) = _unpack_from(">H", self._buffer, self._buff_i) + self._buff_i += 2 + if n > self._max_str_len: + raise ValueError("%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,) = _unpack_from(">I", self._buffer, self._buff_i) + self._buff_i += 4 + if n > self._max_str_len: + raise ValueError("%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,) = _unpack_from(">H", self._buffer, self._buff_i) + self._buff_i += 2 + if n > self._max_array_len: + raise ValueError("%s exceeds max_array_len(%s)", n, self._max_array_len) + elif b == 0xDD: + typ = TYPE_ARRAY + self._reserve(4) + (n,) = _unpack_from(">I", self._buffer, self._buff_i) + self._buff_i += 4 + if n > self._max_array_len: + raise ValueError("%s exceeds max_array_len(%s)", n, self._max_array_len) + elif b == 0xDE: + self._reserve(2) + (n,) = _unpack_from(">H", self._buffer, self._buff_i) + self._buff_i += 2 + if n > self._max_map_len: + raise ValueError("%s exceeds max_map_len(%s)", n, self._max_map_len) + typ = TYPE_MAP + elif b == 0xDF: + self._reserve(4) + (n,) = _unpack_from(">I", self._buffer, self._buff_i) + self._buff_i += 4 + if n > self._max_map_len: + raise ValueError("%s exceeds max_map_len(%s)", n, self._max_map_len) + typ = TYPE_MAP + else: + raise FormatError("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 ValueError("Expected array") + return n + if execute == EX_READ_MAP_HEADER: + if typ != TYPE_MAP: + raise ValueError("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) + if self._strict_map_key and type(key) not in (unicode, bytes): + raise ValueError( + "%s is not allowed for map key" % str(type(key)) + ) + if not PY2 and type(key) is str: + key = sys.intern(key) + 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._raw: + obj = bytes(obj) + else: + obj = obj.decode("utf_8", self._unicode_errors) + return obj + if typ == TYPE_BIN: + return bytes(obj) + if typ == TYPE_EXT: + if n == -1: # timestamp + ts = Timestamp.from_bytes(bytes(obj)) + if self._timestamp == 1: + return ts.to_unix() + elif self._timestamp == 2: + return ts.to_unix_nano() + elif self._timestamp == 3: + return ts.to_datetime() + else: + return ts + else: + return self._ext_hook(n, 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 + except RecursionError: + raise StackError + + next = __next__ + + def skip(self): + self._unpack(EX_SKIP) + self._consume() + + def unpack(self): + try: + ret = self._unpack(EX_CONSTRUCT) + except RecursionError: + raise StackError + self._consume() + return ret + + def read_array_header(self): + ret = self._unpack(EX_READ_ARRAY_HEADER) + self._consume() + return ret + + def read_map_header(self): + ret = self._unpack(EX_READ_MAP_HEADER) + 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. (default: True) + + :param bool strict_types: + If set to true, types will be checked to be exact. Derived classes + from serializable 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 bool datetime: + If set to true, datetime with tzinfo is packed into Timestamp type. + Note that the tzinfo is stripped in the timestamp. + You can get UTC datetime with `timestamp=3` option of the Unpacker. + (Python 2 is not supported). + + :param str unicode_errors: + The error handler for encoding unicode. (default: 'strict') + DO NOT USE THIS!! This option is kept for very specific usage. + """ + + def __init__( + self, + default=None, + use_single_float=False, + autoreset=True, + use_bin_type=True, + strict_types=False, + datetime=False, + unicode_errors=None, + ): + self._strict_types = strict_types + self._use_float = use_single_float + self._autoreset = autoreset + self._use_bin_type = use_bin_type + self._buffer = StringIO() + if PY2 and datetime: + raise ValueError("datetime is not supported in Python 2") + self._datetime = bool(datetime) + self._unicode_errors = unicode_errors or "strict" + 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 ValueError("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 OverflowError("Integer value out of range") + if check(obj, (bytes, bytearray)): + n = len(obj) + if n >= 2 ** 32: + raise ValueError("%s is too large" % type(obj).__name__) + self._pack_bin_header(n) + return self._buffer.write(obj) + if check(obj, unicode): + obj = obj.encode("utf-8", self._unicode_errors) + n = len(obj) + if n >= 2 ** 32: + raise ValueError("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 ValueError("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, Timestamp)): + if check(obj, Timestamp): + code = -1 + data = obj.to_bytes() + else: + 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 self._datetime and check(obj, _DateTime): + obj = Timestamp.from_datetime(obj) + default_used = 1 + continue + + 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 + if self._autoreset: + ret = self._buffer.getvalue() + self._buffer = StringIO() + return ret + + def pack_map_pairs(self, pairs): + self._pack_map_pairs(len(pairs), pairs) + if self._autoreset: + ret = self._buffer.getvalue() + self._buffer = StringIO() + return ret + + def pack_array_header(self, n): + if n >= 2 ** 32: + raise ValueError + self._pack_array_header(n) + if self._autoreset: + ret = self._buffer.getvalue() + self._buffer = StringIO() + return ret + + def pack_map_header(self, n): + if n >= 2 ** 32: + raise ValueError + self._pack_map_header(n) + if self._autoreset: + ret = self._buffer.getvalue() + self._buffer = StringIO() + 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 ValueError("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 ValueError("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 ValueError("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 ValueError("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 ValueError("Bin is too large") + + def bytes(self): + """Return internal buffer contents as bytes object""" + return self._buffer.getvalue() + + def reset(self): + """Reset internal buffer. + + This method is useful only when autoreset=False. + """ + self._buffer = StringIO() + + def getbuffer(self): + """Return view of internal buffer.""" + if USING_STRINGBUILDER or PY2: + return memoryview(self.bytes()) + else: + return self._buffer.getbuffer() diff --git a/venv/lib/python3.8/site-packages/pip/_vendor/packaging/__about__.py b/venv/lib/python3.8/site-packages/pip/_vendor/packaging/__about__.py new file mode 100644 index 0000000..5161d14 --- /dev/null +++ b/venv/lib/python3.8/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__ = "20.3" + +__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/venv/lib/python3.8/site-packages/pip/_vendor/packaging/__init__.py b/venv/lib/python3.8/site-packages/pip/_vendor/packaging/__init__.py new file mode 100644 index 0000000..a0cf67d --- /dev/null +++ b/venv/lib/python3.8/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/venv/lib/python3.8/site-packages/pip/_vendor/packaging/__pycache__/__about__.cpython-38.pyc b/venv/lib/python3.8/site-packages/pip/_vendor/packaging/__pycache__/__about__.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..c62a0e47505d02935323b0b6adaea09cf961cb8e GIT binary patch literal 749 zcmY+COK%e~5P)}+w0Sjcd8jueQ^6q;i9=c{iV!NaM}#UBIPAr8cD#w{uD$Xj(e6*+ zpYWG*MihMy#=p`*;{unxMnD8{9u;vC z7mcJ*G?Qi=K?9o5f*sg}Hgupn?<6hgB|Fehc404R!yxIve$s`5qz8vdAMT0nQIzb# z{hMg`;3r0nhRyW>pIM!ICzvcsZQQy8aw)CUYQ0|?sT|9^N?owKj5+CY!PvUbnDs@$ zO@&6AF>mDdbSX@@i$;$z?%hHg+~ZAAaG7s?a+wNcLzn?$sV*xc=L;9yN}k^Gxm5G| z_);4|ypy?fQdp8{L#`_fr^wbx*!uCpxzfI(bS~Y(&&H`PXjzq<-i2vRC*zZPc%>E3 zf!sKsWsYzKM5<6Gc%G9~D`(`)J8f)z2piW6yKx;$T_3-@xgz-bTa3C8Z8lQRgmfA`RNF8PM+Ei!$D|6=AAcKuE8-!rWw#egud|{dt!FN(QZIHAWYtL z1HvC-`T={0Z;?3+`+(moiqlzgC+LzJn&})cA6lmLY*j8#r|Nv_0M2IiWW;>Bbm zb45SWQkEmHLXQNDRtq8W(JimO=<|~*e4}hB6linTMT&XOw8sgJ%WB%q_ zXp_w^*nY4octNG^t@CriAh_N~x>k90v<>Yd`~70bnhz z1mqE4HW%RK5PWd4nTv}=E(0tgzzf^k5N(}{q2yVIQ~%SXe?!0yQ_=h*axXy*_%q_T zhLL%JN9ORsDjtpEzQxxM{rG-oZg!sY>;3TY>Y#t<4_Bw?48Fs??6<-6ti2a}`*0LO zZSUFi-QK*L;2)-=Fx)x}3f4)}VM?Wu(ec}Unu}X)c~IgL+-A6pxJ=)y)8xPN`!6Et Bnxy~$ literal 0 HcmV?d00001 diff --git a/venv/lib/python3.8/site-packages/pip/_vendor/packaging/__pycache__/_compat.cpython-38.pyc b/venv/lib/python3.8/site-packages/pip/_vendor/packaging/__pycache__/_compat.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..9ef925f3abdf90332653be5fdd8eba05ec80e729 GIT binary patch literal 1164 zcmah}Pfyf96rY)Pm+rDG3P{9+WHH8M4^RS1Bq2lt290q;6b|$_?V?%2Mtfp+LNRD@@_aLdthxinZs9g|mLa@)IUB7`GNZT^w{=QD5B9}ajvsC5A z5n(UXVVam`rotrW-69Ecusb&tuTV4bac_H%Z@hcE@qvHZ?R4IEw%WF_-XwivZ-zl` zDmz6MNn`KyGkJqm-&i05y#ANaQ3W0W*3Us~kxOz(yG&5QE*!zG=(+Wk<>UuFW7v<7 z{IntO%Bfp#0c|Mh=d$Iu_I)i|LF8-QItlYb@We9rOUFX_W&qty<3CmoEGZ>;AqcdGmvtYF+G3joR7YdSASii!h!H=34OZPQ22;xE06z;I8-Ws^f%0W>> zfqw(j=)uI`>)~P2O^vm^w}fIDU~**|AOHoyUGz9J#<7dSDq!O%x+41$gJy*uB`?9Au9o1d>UEZ3Jm!IvsypJ{TqlyI_%HGYN- zFfe+oZ?uiR**5!D+cMZo1}0b^8Caq;w%TP;9AV-Wdd*!nsE`4i^(An^@y5Yy@i?z}hAl zV18t63oIyMkN8l*z6@2cv9CZ4>eyFd4jS0oFb@mZ*WeT^Vqb^Ta0dH1IEz{~lg6u{ z;|IPPZH*$~83>dX8dH6WH6{mvfj7)nVy9OoY+@v(SoAs)oyv6cZRDU;LstoB=t`#( z_CUzki9F~2UNpRSC)l{70Pb(c57s*U{r>uzF5SS{aw0!k9R!iFG>0@gC#{4LDLD&>3SCQ^n|As!(&uv ze8R~Sv-Ls)OiWqB14%hlJ3=1EtZ?2_oG;>u6!GjaV`=~Qc=9xgm`XqMbEIEX+eSEo z^JX3n?w`Op=F$Un>8>$0-!e%~LZ{X-4T4y?fhUq03h23Uj1QEdgZRMt`5YZ+KU;?d zys22KLxcU$LY~9*EW`k;Gip=5dD>X5{_aYUj4<`CTF zj0g>gHe2!<$(hAt#N=xvCxgdmzqL{$D(wvDFA{!PDefc%o}~YCNhRFg6`s%fK2R%Q&N6Rw__rirnPvZg7rI0T_QpN literal 0 HcmV?d00001 diff --git a/venv/lib/python3.8/site-packages/pip/_vendor/packaging/__pycache__/_typing.cpython-38.pyc b/venv/lib/python3.8/site-packages/pip/_vendor/packaging/__pycache__/_typing.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..cd088bca0d3ec480419bb134ea3a6ab938d1dd63 GIT binary patch literal 1504 zcmZXU&5qkP6ouuVnGOVG({&1PQM4VvveKD$&bMO7`5nnxelqMLTe)6|^dypjm@Z{kbV)8vM z{{?O;nI~#6AJD)(Rzo$ahVvl}-;CxXbEL-V;ICvpQlsCJ<3suDI7u$86NY4{I-%>P zqMA(5W-WY>K^G!)4bQX@jVx~D8k1?7Ue{gIU5iYFk{;|NA&sm$ue}J?R$@z{^@MG> z<1g&J?)9!kZB<**L~Ki4lxgGaMl0+otRt~%O%b$(lnx!wl;o^r1y#Y3KlgWg`h1>| zAt$SnjSZrd8l=N)RukiNf@ILq&+uEu&~0 zd61l@S5|DTyAje^;EiujB3c28cH1}Ww)c@AvxmZxN{tt!xRjNrtV?avw~E&xk5Xd1 zm9sTtG3=iAz(bd^!q(hWB#m4l%-u+#;iv}0Xcj|g5UP$7eu-_xdu0J0UNbMH9V&!* zRqHs^zQLe=#YB3WA+f!`*n|7b#k!R)8USt91wq5WXNb7Bg0~2a)MZ*g+qO`X8aCAM zTFly!EJ{%lEE^L?+qn&h{rth_GGV0qk0je8byT6V2p-XT;mcdI#-y+U_pNMf#d(?v zj0m*R&2-VbWqSWTF5Yd_FRx#`UR?b21N zl2+%U459JodA`=6Y?o6sFK?n1bN(K4?R`r=KRx;KEWP{}hfp#r_)xkX{5-&Qfy?92 zN&Xz*02<8RCdb3w0k?LsJJ`soCC6Li?Y}l}oZf&_zL72m4Moo9O_85%o6T1z=IKdL z>TK!1d9teisK0&^$J*pqIWT3dVL+s^ZAnyRH?n(UpPqKSmhTbfcL?)6!fDg(#)b6Z zGfp&CFmQ)>J|6ErS}c?;7K`1-A5z>M_Tb{)`x5#moY=|k=-b|k-!TTy$NBj2csL#$ I4UfkE12z}pa{vGU literal 0 HcmV?d00001 diff --git a/venv/lib/python3.8/site-packages/pip/_vendor/packaging/__pycache__/markers.cpython-38.pyc b/venv/lib/python3.8/site-packages/pip/_vendor/packaging/__pycache__/markers.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..2b5d9b1e80951784744fb32348d2142e2715da03 GIT binary patch literal 9344 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 JgNh(<~6_p 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 literal 0 HcmV?d00001 diff --git a/venv/lib/python3.8/site-packages/pip/_vendor/packaging/_compat.py b/venv/lib/python3.8/site-packages/pip/_vendor/packaging/_compat.py new file mode 100644 index 0000000..a145f7e --- /dev/null +++ b/venv/lib/python3.8/site-packages/pip/_vendor/packaging/_compat.py @@ -0,0 +1,38 @@ +# 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 + +from ._typing import MYPY_CHECK_RUNNING + +if MYPY_CHECK_RUNNING: # pragma: no cover + from typing import Any, Dict, Tuple, Type + + +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): + # type: (Type[Any], Tuple[Type[Any], ...]) -> Any + """ + 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): # type: ignore + def __new__(cls, name, this_bases, d): + # type: (Type[Any], str, Tuple[Any], Dict[Any, Any]) -> Any + return meta(name, bases, d) + + return type.__new__(metaclass, "temporary_class", (), {}) diff --git a/venv/lib/python3.8/site-packages/pip/_vendor/packaging/_structures.py b/venv/lib/python3.8/site-packages/pip/_vendor/packaging/_structures.py new file mode 100644 index 0000000..800d5c5 --- /dev/null +++ b/venv/lib/python3.8/site-packages/pip/_vendor/packaging/_structures.py @@ -0,0 +1,86 @@ +# 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 InfinityType(object): + def __repr__(self): + # type: () -> str + return "Infinity" + + def __hash__(self): + # type: () -> int + return hash(repr(self)) + + def __lt__(self, other): + # type: (object) -> bool + return False + + def __le__(self, other): + # type: (object) -> bool + return False + + def __eq__(self, other): + # type: (object) -> bool + return isinstance(other, self.__class__) + + def __ne__(self, other): + # type: (object) -> bool + return not isinstance(other, self.__class__) + + def __gt__(self, other): + # type: (object) -> bool + return True + + def __ge__(self, other): + # type: (object) -> bool + return True + + def __neg__(self): + # type: (object) -> NegativeInfinityType + return NegativeInfinity + + +Infinity = InfinityType() + + +class NegativeInfinityType(object): + def __repr__(self): + # type: () -> str + return "-Infinity" + + def __hash__(self): + # type: () -> int + return hash(repr(self)) + + def __lt__(self, other): + # type: (object) -> bool + return True + + def __le__(self, other): + # type: (object) -> bool + return True + + def __eq__(self, other): + # type: (object) -> bool + return isinstance(other, self.__class__) + + def __ne__(self, other): + # type: (object) -> bool + return not isinstance(other, self.__class__) + + def __gt__(self, other): + # type: (object) -> bool + return False + + def __ge__(self, other): + # type: (object) -> bool + return False + + def __neg__(self): + # type: (object) -> InfinityType + return Infinity + + +NegativeInfinity = NegativeInfinityType() diff --git a/venv/lib/python3.8/site-packages/pip/_vendor/packaging/_typing.py b/venv/lib/python3.8/site-packages/pip/_vendor/packaging/_typing.py new file mode 100644 index 0000000..945b39c --- /dev/null +++ b/venv/lib/python3.8/site-packages/pip/_vendor/packaging/_typing.py @@ -0,0 +1,39 @@ +"""For neatly implementing static typing in packaging. + +`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 packaging, all static-typing related imports should be guarded as follows: + + from pip._vendor.packaging._typing import MYPY_CHECK_RUNNING + + if MYPY_CHECK_RUNNING: + from typing import ... + +Ref: https://github.com/python/mypy/issues/3216 +""" + +MYPY_CHECK_RUNNING = False + +if MYPY_CHECK_RUNNING: # pragma: no cover + import typing + + cast = typing.cast +else: + # typing's cast() is needed at runtime, but we don't want to import typing. + # Thus, we use a dummy no-op version, which we tell mypy to ignore. + def cast(type_, value): # type: ignore + return value diff --git a/venv/lib/python3.8/site-packages/pip/_vendor/packaging/markers.py b/venv/lib/python3.8/site-packages/pip/_vendor/packaging/markers.py new file mode 100644 index 0000000..b24f8ed --- /dev/null +++ b/venv/lib/python3.8/site-packages/pip/_vendor/packaging/markers.py @@ -0,0 +1,328 @@ +# 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 ._typing import MYPY_CHECK_RUNNING +from .specifiers import Specifier, InvalidSpecifier + +if MYPY_CHECK_RUNNING: # pragma: no cover + from typing import Any, Callable, Dict, List, Optional, Tuple, Union + + Operator = Callable[[str, str], bool] + + +__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): + # type: (Any) -> None + self.value = value + + def __str__(self): + # type: () -> str + return str(self.value) + + def __repr__(self): + # type: () -> str + return "<{0}({1!r})>".format(self.__class__.__name__, str(self)) + + def serialize(self): + # type: () -> str + raise NotImplementedError + + +class Variable(Node): + def serialize(self): + # type: () -> str + return str(self) + + +class Value(Node): + def serialize(self): + # type: () -> str + return '"{0}"'.format(self) + + +class Op(Node): + def serialize(self): + # type: () -> str + 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") # PEP-508 +) +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): + # type: (Union[ParseResults, List[Any]]) -> List[Any] + if isinstance(results, ParseResults): + return [_coerce_parse_result(i) for i in results] + else: + return results + + +def _format_marker(marker, first=True): + # type: (Union[List[str], Tuple[Node, ...], str], Optional[bool]) -> str + + 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, +} # type: Dict[str, Operator] + + +def _eval_op(lhs, op, rhs): + # type: (str, Op, str) -> bool + try: + spec = Specifier("".join([op.serialize(), rhs])) + except InvalidSpecifier: + pass + else: + return spec.contains(lhs) + + oper = _operators.get(op.serialize()) # type: Optional[Operator] + if oper is None: + raise UndefinedComparison( + "Undefined {0!r} on {1!r} and {2!r}.".format(op, lhs, rhs) + ) + + return oper(lhs, rhs) + + +class Undefined(object): + pass + + +_undefined = Undefined() + + +def _get_env(environment, name): + # type: (Dict[str, str], str) -> str + value = environment.get(name, _undefined) # type: Union[str, Undefined] + + if isinstance(value, Undefined): + raise UndefinedEnvironmentName( + "{0!r} does not exist in evaluation environment.".format(name) + ) + + return value + + +def _evaluate_markers(markers, environment): + # type: (List[Any], Dict[str, str]) -> bool + groups = [[]] # type: List[List[bool]] + + 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): + # type: (sys._version_info) -> str + 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(): + # type: () -> Dict[str, str] + if hasattr(sys, "implementation"): + # Ignoring the `sys.implementation` reference for type checking due to + # mypy not liking that the attribute doesn't exist in Python 2.7 when + # run with the `--py27` flag. + iver = format_full_version(sys.implementation.version) # type: ignore + implementation_name = sys.implementation.name # type: ignore + 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": ".".join(platform.python_version_tuple()[:2]), + "sys_platform": sys.platform, + } + + +class Marker(object): + def __init__(self, marker): + # type: (str) -> None + 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): + # type: () -> str + return _format_marker(self._markers) + + def __repr__(self): + # type: () -> str + return "".format(str(self)) + + def evaluate(self, environment=None): + # type: (Optional[Dict[str, str]]) -> bool + """Evaluate a marker. + + Return the boolean from evaluating the given marker against the + environment. environment is an optional argument to override all or + part of the determined environment. + + The environment is determined from the current Python process. + """ + current_environment = default_environment() + if environment is not None: + current_environment.update(environment) + + return _evaluate_markers(self._markers, current_environment) diff --git a/venv/lib/python3.8/site-packages/pip/_vendor/packaging/requirements.py b/venv/lib/python3.8/site-packages/pip/_vendor/packaging/requirements.py new file mode 100644 index 0000000..1e32a93 --- /dev/null +++ b/venv/lib/python3.8/site-packages/pip/_vendor/packaging/requirements.py @@ -0,0 +1,145 @@ +# 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 ._typing import MYPY_CHECK_RUNNING +from .markers import MARKER_EXPR, Marker +from .specifiers import LegacySpecifier, Specifier, SpecifierSet + +if MYPY_CHECK_RUNNING: # pragma: no cover + from typing import List + + +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): + # type: (str) -> None + 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): + # type: () -> str + parts = [self.name] # type: List[str] + + 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): + # type: () -> str + return "".format(str(self)) diff --git a/venv/lib/python3.8/site-packages/pip/_vendor/packaging/specifiers.py b/venv/lib/python3.8/site-packages/pip/_vendor/packaging/specifiers.py new file mode 100644 index 0000000..9498748 --- /dev/null +++ b/venv/lib/python3.8/site-packages/pip/_vendor/packaging/specifiers.py @@ -0,0 +1,849 @@ +# 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 ._typing import MYPY_CHECK_RUNNING +from .version import Version, LegacyVersion, parse + +if MYPY_CHECK_RUNNING: # pragma: no cover + from typing import ( + List, + Dict, + Union, + Iterable, + Iterator, + Optional, + Callable, + Tuple, + FrozenSet, + ) + + ParsedVersion = Union[Version, LegacyVersion] + UnparsedVersion = Union[Version, LegacyVersion, str] + CallableOperator = Callable[[ParsedVersion, str], bool] + + +class InvalidSpecifier(ValueError): + """ + An invalid specifier was found, users should refer to PEP 440. + """ + + +class BaseSpecifier(with_metaclass(abc.ABCMeta, object)): # type: ignore + @abc.abstractmethod + def __str__(self): + # type: () -> str + """ + Returns the str representation of this Specifier like object. This + should be representative of the Specifier itself. + """ + + @abc.abstractmethod + def __hash__(self): + # type: () -> int + """ + Returns a hash value for this Specifier like object. + """ + + @abc.abstractmethod + def __eq__(self, other): + # type: (object) -> bool + """ + Returns a boolean representing whether or not the two Specifier like + objects are equal. + """ + + @abc.abstractmethod + def __ne__(self, other): + # type: (object) -> bool + """ + Returns a boolean representing whether or not the two Specifier like + objects are not equal. + """ + + @abc.abstractproperty + def prereleases(self): + # type: () -> Optional[bool] + """ + Returns whether or not pre-releases as a whole are allowed by this + specifier. + """ + + @prereleases.setter + def prereleases(self, value): + # type: (bool) -> None + """ + Sets whether or not pre-releases as a whole are allowed by this + specifier. + """ + + @abc.abstractmethod + def contains(self, item, prereleases=None): + # type: (str, Optional[bool]) -> bool + """ + Determines if the given item is contained within this specifier. + """ + + @abc.abstractmethod + def filter(self, iterable, prereleases=None): + # type: (Iterable[UnparsedVersion], Optional[bool]) -> Iterable[UnparsedVersion] + """ + 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 = {} # type: Dict[str, str] + + def __init__(self, spec="", prereleases=None): + # type: (str, Optional[bool]) -> 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(), + ) # type: Tuple[str, str] + + # Store whether or not this Specifier should accept prereleases + self._prereleases = prereleases + + def __repr__(self): + # type: () -> str + 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): + # type: () -> str + return "{0}{1}".format(*self._spec) + + def __hash__(self): + # type: () -> int + return hash(self._spec) + + def __eq__(self, other): + # type: (object) -> bool + if isinstance(other, string_types): + try: + other = self.__class__(str(other)) + except InvalidSpecifier: + return NotImplemented + elif not isinstance(other, self.__class__): + return NotImplemented + + return self._spec == other._spec + + def __ne__(self, other): + # type: (object) -> bool + if isinstance(other, string_types): + try: + other = self.__class__(str(other)) + except InvalidSpecifier: + return NotImplemented + elif not isinstance(other, self.__class__): + return NotImplemented + + return self._spec != other._spec + + def _get_operator(self, op): + # type: (str) -> CallableOperator + operator_callable = getattr( + self, "_compare_{0}".format(self._operators[op]) + ) # type: CallableOperator + return operator_callable + + def _coerce_version(self, version): + # type: (UnparsedVersion) -> ParsedVersion + if not isinstance(version, (LegacyVersion, Version)): + version = parse(version) + return version + + @property + def operator(self): + # type: () -> str + return self._spec[0] + + @property + def version(self): + # type: () -> str + return self._spec[1] + + @property + def prereleases(self): + # type: () -> Optional[bool] + return self._prereleases + + @prereleases.setter + def prereleases(self, value): + # type: (bool) -> None + self._prereleases = value + + def __contains__(self, item): + # type: (str) -> bool + return self.contains(item) + + def contains(self, item, prereleases=None): + # type: (UnparsedVersion, Optional[bool]) -> bool + + # 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") + normalized_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 normalized_item.is_prerelease and not prereleases: + return False + + # Actually do the comparison to determine if this item is contained + # within this Specifier or not. + operator_callable = self._get_operator(self.operator) # type: CallableOperator + return operator_callable(normalized_item, self.version) + + def filter(self, iterable, prereleases=None): + # type: (Iterable[UnparsedVersion], Optional[bool]) -> Iterable[UnparsedVersion] + + 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(==|!=|<=|>=|<|>)) + \s* + (?P + [^,;\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): + # type: (Union[ParsedVersion, str]) -> LegacyVersion + if not isinstance(version, LegacyVersion): + version = LegacyVersion(str(version)) + return version + + def _compare_equal(self, prospective, spec): + # type: (LegacyVersion, str) -> bool + return prospective == self._coerce_version(spec) + + def _compare_not_equal(self, prospective, spec): + # type: (LegacyVersion, str) -> bool + return prospective != self._coerce_version(spec) + + def _compare_less_than_equal(self, prospective, spec): + # type: (LegacyVersion, str) -> bool + return prospective <= self._coerce_version(spec) + + def _compare_greater_than_equal(self, prospective, spec): + # type: (LegacyVersion, str) -> bool + return prospective >= self._coerce_version(spec) + + def _compare_less_than(self, prospective, spec): + # type: (LegacyVersion, str) -> bool + return prospective < self._coerce_version(spec) + + def _compare_greater_than(self, prospective, spec): + # type: (LegacyVersion, str) -> bool + return prospective > self._coerce_version(spec) + + +def _require_version_compare( + fn # type: (Callable[[Specifier, ParsedVersion, str], bool]) +): + # type: (...) -> Callable[[Specifier, ParsedVersion, str], bool] + @functools.wraps(fn) + def wrapped(self, prospective, spec): + # type: (Specifier, ParsedVersion, str) -> bool + if not isinstance(prospective, Version): + return False + return fn(self, prospective, spec) + + return wrapped + + +class Specifier(_IndividualSpecifier): + + _regex_str = r""" + (?P(~=|==|!=|<=|>=|<|>|===)) + (?P + (?: + # 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. + (?=": "greater_than_equal", + "<": "less_than", + ">": "greater_than", + "===": "arbitrary", + } + + @_require_version_compare + def _compare_compatible(self, prospective, spec): + # type: (ParsedVersion, str) -> bool + + # 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): + # type: (ParsedVersion, str) -> bool + + # 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. + split_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. + split_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. + shortened_prospective = split_prospective[: len(split_spec)] + + # Pad out our two sides with zeros so that they both equal the same + # length. + padded_spec, padded_prospective = _pad_version( + split_spec, shortened_prospective + ) + + return padded_prospective == padded_spec + else: + # Convert our spec string into a Version + spec_version = 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_version.local: + prospective = Version(prospective.public) + + return prospective == spec_version + + @_require_version_compare + def _compare_not_equal(self, prospective, spec): + # type: (ParsedVersion, str) -> bool + return not self._compare_equal(prospective, spec) + + @_require_version_compare + def _compare_less_than_equal(self, prospective, spec): + # type: (ParsedVersion, str) -> bool + return prospective <= Version(spec) + + @_require_version_compare + def _compare_greater_than_equal(self, prospective, spec): + # type: (ParsedVersion, str) -> bool + return prospective >= Version(spec) + + @_require_version_compare + def _compare_less_than(self, prospective, spec_str): + # type: (ParsedVersion, str) -> bool + + # Convert our spec to a Version instance, since we'll want to work with + # it as a version. + spec = Version(spec_str) + + # 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_str): + # type: (ParsedVersion, str) -> bool + + # Convert our spec to a Version instance, since we'll want to work with + # it as a version. + spec = Version(spec_str) + + # 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): + # type: (Version, str) -> bool + return str(prospective).lower() == str(spec).lower() + + @property + def prereleases(self): + # type: () -> bool + + # 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): + # type: (bool) -> None + self._prereleases = value + + +_prefix_regex = re.compile(r"^([0-9]+)((?:a|b|c|rc)[0-9]+)$") + + +def _version_split(version): + # type: (str) -> List[str] + result = [] # type: List[str] + 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): + # type: (List[str], List[str]) -> Tuple[List[str], List[str]] + 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): + # type: (str, Optional[bool]) -> None + + # Split on , to break each individual specifier into it's own item, and + # strip each item to remove leading/trailing whitespace. + split_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 split_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): + # type: () -> str + pre = ( + ", prereleases={0!r}".format(self.prereleases) + if self._prereleases is not None + else "" + ) + + return "".format(str(self), pre) + + def __str__(self): + # type: () -> str + return ",".join(sorted(str(s) for s in self._specs)) + + def __hash__(self): + # type: () -> int + return hash(self._specs) + + def __and__(self, other): + # type: (Union[SpecifierSet, str]) -> SpecifierSet + 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): + # type: (object) -> bool + if isinstance(other, (string_types, _IndividualSpecifier)): + other = SpecifierSet(str(other)) + elif not isinstance(other, SpecifierSet): + return NotImplemented + + return self._specs == other._specs + + def __ne__(self, other): + # type: (object) -> bool + if isinstance(other, (string_types, _IndividualSpecifier)): + other = SpecifierSet(str(other)) + elif not isinstance(other, SpecifierSet): + return NotImplemented + + return self._specs != other._specs + + def __len__(self): + # type: () -> int + return len(self._specs) + + def __iter__(self): + # type: () -> Iterator[FrozenSet[_IndividualSpecifier]] + return iter(self._specs) + + @property + def prereleases(self): + # type: () -> Optional[bool] + + # 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): + # type: (bool) -> None + self._prereleases = value + + def __contains__(self, item): + # type: (Union[ParsedVersion, str]) -> bool + return self.contains(item) + + def contains(self, item, prereleases=None): + # type: (Union[ParsedVersion, str], Optional[bool]) -> bool + + # 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, # type: Iterable[Union[ParsedVersion, str]] + prereleases=None, # type: Optional[bool] + ): + # type: (...) -> Iterable[Union[ParsedVersion, str]] + + # 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 = [] # type: List[Union[ParsedVersion, str]] + found_prereleases = [] # type: List[Union[ParsedVersion, str]] + + for item in iterable: + # Ensure that we some kind of Version class for this item. + if not isinstance(item, (LegacyVersion, Version)): + parsed_version = parse(item) + else: + parsed_version = item + + # Filter out any item which is parsed as a LegacyVersion + if isinstance(parsed_version, LegacyVersion): + continue + + # Store any item which is a pre-release for later unless we've + # already found a final version or we are accepting prereleases + if parsed_version.is_prerelease and not prereleases: + if not filtered: + found_prereleases.append(item) + else: + filtered.append(item) + + # If we've found no items except for pre-releases, then we'll go + # ahead and use the pre-releases + if not filtered and found_prereleases and prereleases is None: + return found_prereleases + + return filtered diff --git a/venv/lib/python3.8/site-packages/pip/_vendor/packaging/tags.py b/venv/lib/python3.8/site-packages/pip/_vendor/packaging/tags.py new file mode 100644 index 0000000..300faab --- /dev/null +++ b/venv/lib/python3.8/site-packages/pip/_vendor/packaging/tags.py @@ -0,0 +1,739 @@ +# 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 + +import distutils.util + +try: + from importlib.machinery import EXTENSION_SUFFIXES +except ImportError: # pragma: no cover + import imp + + EXTENSION_SUFFIXES = [x[0] for x in imp.get_suffixes()] + del imp +import logging +import os +import platform +import re +import struct +import sys +import sysconfig +import warnings + +from ._typing import MYPY_CHECK_RUNNING, cast + +if MYPY_CHECK_RUNNING: # pragma: no cover + from typing import ( + Dict, + FrozenSet, + IO, + Iterable, + Iterator, + List, + Optional, + Sequence, + Tuple, + Union, + ) + + PythonVersion = Sequence[int] + MacVersion = Tuple[int, int] + GlibcVersion = Tuple[int, int] + + +logger = logging.getLogger(__name__) + +INTERPRETER_SHORT_NAMES = { + "python": "py", # Generic. + "cpython": "cp", + "pypy": "pp", + "ironpython": "ip", + "jython": "jy", +} # type: Dict[str, str] + + +_32_BIT_INTERPRETER = sys.maxsize <= 2 ** 32 + + +class Tag(object): + + __slots__ = ["_interpreter", "_abi", "_platform"] + + def __init__(self, interpreter, abi, platform): + # type: (str, str, str) -> None + self._interpreter = interpreter.lower() + self._abi = abi.lower() + self._platform = platform.lower() + + @property + def interpreter(self): + # type: () -> str + return self._interpreter + + @property + def abi(self): + # type: () -> str + return self._abi + + @property + def platform(self): + # type: () -> str + return self._platform + + def __eq__(self, other): + # type: (object) -> bool + if not isinstance(other, Tag): + return NotImplemented + + return ( + (self.platform == other.platform) + and (self.abi == other.abi) + and (self.interpreter == other.interpreter) + ) + + def __hash__(self): + # type: () -> int + return hash((self._interpreter, self._abi, self._platform)) + + def __str__(self): + # type: () -> str + return "{}-{}-{}".format(self._interpreter, self._abi, self._platform) + + def __repr__(self): + # type: () -> str + return "<{self} @ {self_id}>".format(self=self, self_id=id(self)) + + +def parse_tag(tag): + # type: (str) -> FrozenSet[Tag] + tags = set() + interpreters, abis, platforms = tag.split("-") + for interpreter in interpreters.split("."): + for abi in abis.split("."): + for platform_ in platforms.split("."): + tags.add(Tag(interpreter, abi, platform_)) + return frozenset(tags) + + +def _warn_keyword_parameter(func_name, kwargs): + # type: (str, Dict[str, bool]) -> bool + """ + Backwards-compatibility with Python 2.7 to allow treating 'warn' as keyword-only. + """ + if not kwargs: + return False + elif len(kwargs) > 1 or "warn" not in kwargs: + kwargs.pop("warn", None) + arg = next(iter(kwargs.keys())) + raise TypeError( + "{}() got an unexpected keyword argument {!r}".format(func_name, arg) + ) + return kwargs["warn"] + + +def _get_config_var(name, warn=False): + # type: (str, bool) -> Union[int, str, None] + value = sysconfig.get_config_var(name) + if value is None and warn: + logger.debug( + "Config variable '%s' is unset, Python ABI tag may be incorrect", name + ) + return value + + +def _normalize_string(string): + # type: (str) -> str + return string.replace(".", "_").replace("-", "_") + + +def _abi3_applies(python_version): + # type: (PythonVersion) -> bool + """ + Determine if the Python version supports abi3. + + PEP 384 was first implemented in Python 3.2. + """ + return len(python_version) > 1 and tuple(python_version) >= (3, 2) + + +def _cpython_abis(py_version, warn=False): + # type: (PythonVersion, bool) -> List[str] + py_version = tuple(py_version) # To allow for version comparison. + abis = [] + version = _version_nodot(py_version[:2]) + debug = pymalloc = ucs4 = "" + with_debug = _get_config_var("Py_DEBUG", warn) + has_refcount = hasattr(sys, "gettotalrefcount") + # Windows doesn't set Py_DEBUG, so checking for support of debug-compiled + # extension modules is the best option. + # https://github.com/pypa/pip/issues/3383#issuecomment-173267692 + has_ext = "_d.pyd" in EXTENSION_SUFFIXES + if with_debug or (with_debug is None and (has_refcount or has_ext)): + debug = "d" + if py_version < (3, 8): + with_pymalloc = _get_config_var("WITH_PYMALLOC", warn) + if with_pymalloc or with_pymalloc is None: + pymalloc = "m" + if py_version < (3, 3): + unicode_size = _get_config_var("Py_UNICODE_SIZE", warn) + if unicode_size == 4 or ( + unicode_size is None and sys.maxunicode == 0x10FFFF + ): + ucs4 = "u" + elif debug: + # Debug builds can also load "normal" extension modules. + # We can also assume no UCS-4 or pymalloc requirement. + abis.append("cp{version}".format(version=version)) + abis.insert( + 0, + "cp{version}{debug}{pymalloc}{ucs4}".format( + version=version, debug=debug, pymalloc=pymalloc, ucs4=ucs4 + ), + ) + return abis + + +def cpython_tags( + python_version=None, # type: Optional[PythonVersion] + abis=None, # type: Optional[Iterable[str]] + platforms=None, # type: Optional[Iterable[str]] + **kwargs # type: bool +): + # type: (...) -> Iterator[Tag] + """ + Yields the tags for a CPython interpreter. + + The tags consist of: + - cp-- + - cp-abi3- + - cp-none- + - cp-abi3- # Older Python versions down to 3.2. + + If python_version only specifies a major version then user-provided ABIs and + the 'none' ABItag will be used. + + If 'abi3' or 'none' are specified in 'abis' then they will be yielded at + their normal position and not at the beginning. + """ + warn = _warn_keyword_parameter("cpython_tags", kwargs) + if not python_version: + python_version = sys.version_info[:2] + + interpreter = "cp{}".format(_version_nodot(python_version[:2])) + + if abis is None: + if len(python_version) > 1: + abis = _cpython_abis(python_version, warn) + else: + abis = [] + abis = list(abis) + # 'abi3' and 'none' are explicitly handled later. + for explicit_abi in ("abi3", "none"): + try: + abis.remove(explicit_abi) + except ValueError: + pass + + platforms = list(platforms or _platform_tags()) + for abi in abis: + for platform_ in platforms: + yield Tag(interpreter, abi, platform_) + if _abi3_applies(python_version): + for tag in (Tag(interpreter, "abi3", platform_) for platform_ in platforms): + yield tag + for tag in (Tag(interpreter, "none", platform_) for platform_ in platforms): + yield tag + + if _abi3_applies(python_version): + for minor_version in range(python_version[1] - 1, 1, -1): + for platform_ in platforms: + interpreter = "cp{version}".format( + version=_version_nodot((python_version[0], minor_version)) + ) + yield Tag(interpreter, "abi3", platform_) + + +def _generic_abi(): + # type: () -> Iterator[str] + abi = sysconfig.get_config_var("SOABI") + if abi: + yield _normalize_string(abi) + + +def generic_tags( + interpreter=None, # type: Optional[str] + abis=None, # type: Optional[Iterable[str]] + platforms=None, # type: Optional[Iterable[str]] + **kwargs # type: bool +): + # type: (...) -> Iterator[Tag] + """ + Yields the tags for a generic interpreter. + + The tags consist of: + - -- + + The "none" ABI will be added if it was not explicitly provided. + """ + warn = _warn_keyword_parameter("generic_tags", kwargs) + if not interpreter: + interp_name = interpreter_name() + interp_version = interpreter_version(warn=warn) + interpreter = "".join([interp_name, interp_version]) + if abis is None: + abis = _generic_abi() + platforms = list(platforms or _platform_tags()) + abis = list(abis) + if "none" not in abis: + abis.append("none") + for abi in abis: + for platform_ in platforms: + yield Tag(interpreter, abi, platform_) + + +def _py_interpreter_range(py_version): + # type: (PythonVersion) -> Iterator[str] + """ + Yields Python versions in descending order. + + After the latest version, the major-only version will be yielded, and then + all previous versions of that major version. + """ + if len(py_version) > 1: + yield "py{version}".format(version=_version_nodot(py_version[:2])) + yield "py{major}".format(major=py_version[0]) + if len(py_version) > 1: + for minor in range(py_version[1] - 1, -1, -1): + yield "py{version}".format(version=_version_nodot((py_version[0], minor))) + + +def compatible_tags( + python_version=None, # type: Optional[PythonVersion] + interpreter=None, # type: Optional[str] + platforms=None, # type: Optional[Iterable[str]] +): + # type: (...) -> Iterator[Tag] + """ + Yields the sequence of tags that are compatible with a specific version of Python. + + The tags consist of: + - py*-none- + - -none-any # ... if `interpreter` is provided. + - py*-none-any + """ + if not python_version: + python_version = sys.version_info[:2] + platforms = list(platforms or _platform_tags()) + for version in _py_interpreter_range(python_version): + for platform_ in platforms: + yield Tag(version, "none", platform_) + if interpreter: + yield Tag(interpreter, "none", "any") + for version in _py_interpreter_range(python_version): + yield Tag(version, "none", "any") + + +def _mac_arch(arch, is_32bit=_32_BIT_INTERPRETER): + # type: (str, bool) -> str + if not is_32bit: + return arch + + if arch.startswith("ppc"): + return "ppc" + + return "i386" + + +def _mac_binary_formats(version, cpu_arch): + # type: (MacVersion, str) -> List[str] + formats = [cpu_arch] + if cpu_arch == "x86_64": + if version < (10, 4): + return [] + formats.extend(["intel", "fat64", "fat32"]) + + elif cpu_arch == "i386": + if version < (10, 4): + return [] + formats.extend(["intel", "fat32", "fat"]) + + elif cpu_arch == "ppc64": + # TODO: Need to care about 32-bit PPC for ppc64 through 10.2? + if version > (10, 5) or version < (10, 4): + return [] + formats.append("fat64") + + elif cpu_arch == "ppc": + if version > (10, 6): + return [] + formats.extend(["fat32", "fat"]) + + formats.append("universal") + return formats + + +def mac_platforms(version=None, arch=None): + # type: (Optional[MacVersion], Optional[str]) -> Iterator[str] + """ + Yields the platform tags for a macOS system. + + The `version` parameter is a two-item tuple specifying the macOS version to + generate platform tags for. The `arch` parameter is the CPU architecture to + generate platform tags for. Both parameters default to the appropriate value + for the current system. + """ + version_str, _, cpu_arch = platform.mac_ver() # type: ignore + if version is None: + version = cast("MacVersion", tuple(map(int, version_str.split(".")[:2]))) + else: + version = version + if arch is None: + arch = _mac_arch(cpu_arch) + else: + arch = arch + for minor_version in range(version[1], -1, -1): + compat_version = version[0], minor_version + binary_formats = _mac_binary_formats(compat_version, arch) + for binary_format in binary_formats: + yield "macosx_{major}_{minor}_{binary_format}".format( + major=compat_version[0], + minor=compat_version[1], + binary_format=binary_format, + ) + + +# From PEP 513. +def _is_manylinux_compatible(name, glibc_version): + # type: (str, GlibcVersion) -> bool + # Check for presence of _manylinux module. + try: + import _manylinux # noqa + + return bool(getattr(_manylinux, name + "_compatible")) + except (ImportError, AttributeError): + # Fall through to heuristic check below. + pass + + return _have_compatible_glibc(*glibc_version) + + +def _glibc_version_string(): + # type: () -> Optional[str] + # Returns glibc version string, or None if not using glibc. + return _glibc_version_string_confstr() or _glibc_version_string_ctypes() + + +def _glibc_version_string_confstr(): + # type: () -> Optional[str] + """ + Primary implementation of glibc_version_string using os.confstr. + """ + # os.confstr is quite a bit faster than ctypes.DLL. It's also less likely + # to be broken or missing. This strategy is used in the standard library + # platform module. + # https://github.com/python/cpython/blob/fcf1d003bf4f0100c9d0921ff3d70e1127ca1b71/Lib/platform.py#L175-L183 + try: + # os.confstr("CS_GNU_LIBC_VERSION") returns a string like "glibc 2.17". + version_string = os.confstr( # type: ignore[attr-defined] # noqa: F821 + "CS_GNU_LIBC_VERSION" + ) + assert version_string is not None + _, version = version_string.split() # type: Tuple[str, str] + except (AssertionError, AttributeError, OSError, ValueError): + # os.confstr() or CS_GNU_LIBC_VERSION not available (or a bad value)... + return None + return version + + +def _glibc_version_string_ctypes(): + # type: () -> Optional[str] + """ + Fallback implementation of glibc_version_string using ctypes. + """ + try: + import ctypes + except ImportError: + return None + + # 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. + # + # Note: typeshed is wrong here so we are ignoring this line. + process_namespace = ctypes.CDLL(None) # type: ignore + 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() # type: str + # 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() + if version_str is None: + return False + return _check_glibc_version(version_str, required_major, minimum_minor) + + +# Python does not provide platform information at sufficient granularity to +# identify the architecture of the running executable in some cases, so we +# determine it dynamically by reading the information from the running +# process. This only applies on Linux, which uses the ELF format. +class _ELFFileHeader(object): + # https://en.wikipedia.org/wiki/Executable_and_Linkable_Format#File_header + class _InvalidELFFileHeader(ValueError): + """ + An invalid ELF file header was found. + """ + + ELF_MAGIC_NUMBER = 0x7F454C46 + ELFCLASS32 = 1 + ELFCLASS64 = 2 + ELFDATA2LSB = 1 + ELFDATA2MSB = 2 + EM_386 = 3 + EM_S390 = 22 + EM_ARM = 40 + EM_X86_64 = 62 + EF_ARM_ABIMASK = 0xFF000000 + EF_ARM_ABI_VER5 = 0x05000000 + EF_ARM_ABI_FLOAT_HARD = 0x00000400 + + def __init__(self, file): + # type: (IO[bytes]) -> None + def unpack(fmt): + # type: (str) -> int + try: + result, = struct.unpack( + fmt, file.read(struct.calcsize(fmt)) + ) # type: (int, ) + except struct.error: + raise _ELFFileHeader._InvalidELFFileHeader() + return result + + self.e_ident_magic = unpack(">I") + if self.e_ident_magic != self.ELF_MAGIC_NUMBER: + raise _ELFFileHeader._InvalidELFFileHeader() + self.e_ident_class = unpack("B") + if self.e_ident_class not in {self.ELFCLASS32, self.ELFCLASS64}: + raise _ELFFileHeader._InvalidELFFileHeader() + self.e_ident_data = unpack("B") + if self.e_ident_data not in {self.ELFDATA2LSB, self.ELFDATA2MSB}: + raise _ELFFileHeader._InvalidELFFileHeader() + self.e_ident_version = unpack("B") + self.e_ident_osabi = unpack("B") + self.e_ident_abiversion = unpack("B") + self.e_ident_pad = file.read(7) + format_h = "H" + format_i = "I" + format_q = "Q" + format_p = format_i if self.e_ident_class == self.ELFCLASS32 else format_q + self.e_type = unpack(format_h) + self.e_machine = unpack(format_h) + self.e_version = unpack(format_i) + self.e_entry = unpack(format_p) + self.e_phoff = unpack(format_p) + self.e_shoff = unpack(format_p) + self.e_flags = unpack(format_i) + self.e_ehsize = unpack(format_h) + self.e_phentsize = unpack(format_h) + self.e_phnum = unpack(format_h) + self.e_shentsize = unpack(format_h) + self.e_shnum = unpack(format_h) + self.e_shstrndx = unpack(format_h) + + +def _get_elf_header(): + # type: () -> Optional[_ELFFileHeader] + try: + with open(sys.executable, "rb") as f: + elf_header = _ELFFileHeader(f) + except (IOError, OSError, TypeError, _ELFFileHeader._InvalidELFFileHeader): + return None + return elf_header + + +def _is_linux_armhf(): + # type: () -> bool + # hard-float ABI can be detected from the ELF header of the running + # process + # https://static.docs.arm.com/ihi0044/g/aaelf32.pdf + elf_header = _get_elf_header() + if elf_header is None: + return False + result = elf_header.e_ident_class == elf_header.ELFCLASS32 + result &= elf_header.e_ident_data == elf_header.ELFDATA2LSB + result &= elf_header.e_machine == elf_header.EM_ARM + result &= ( + elf_header.e_flags & elf_header.EF_ARM_ABIMASK + ) == elf_header.EF_ARM_ABI_VER5 + result &= ( + elf_header.e_flags & elf_header.EF_ARM_ABI_FLOAT_HARD + ) == elf_header.EF_ARM_ABI_FLOAT_HARD + return result + + +def _is_linux_i686(): + # type: () -> bool + elf_header = _get_elf_header() + if elf_header is None: + return False + result = elf_header.e_ident_class == elf_header.ELFCLASS32 + result &= elf_header.e_ident_data == elf_header.ELFDATA2LSB + result &= elf_header.e_machine == elf_header.EM_386 + return result + + +def _have_compatible_manylinux_abi(arch): + # type: (str) -> bool + if arch == "armv7l": + return _is_linux_armhf() + if arch == "i686": + return _is_linux_i686() + return True + + +def _linux_platforms(is_32bit=_32_BIT_INTERPRETER): + # type: (bool) -> Iterator[str] + linux = _normalize_string(distutils.util.get_platform()) + if is_32bit: + if linux == "linux_x86_64": + linux = "linux_i686" + elif linux == "linux_aarch64": + linux = "linux_armv7l" + manylinux_support = [] + _, arch = linux.split("_", 1) + if _have_compatible_manylinux_abi(arch): + if arch in {"x86_64", "i686", "aarch64", "armv7l", "ppc64", "ppc64le", "s390x"}: + manylinux_support.append( + ("manylinux2014", (2, 17)) + ) # CentOS 7 w/ glibc 2.17 (PEP 599) + if arch in {"x86_64", "i686"}: + manylinux_support.append( + ("manylinux2010", (2, 12)) + ) # CentOS 6 w/ glibc 2.12 (PEP 571) + manylinux_support.append( + ("manylinux1", (2, 5)) + ) # CentOS 5 w/ glibc 2.5 (PEP 513) + manylinux_support_iter = iter(manylinux_support) + for name, glibc_version in manylinux_support_iter: + if _is_manylinux_compatible(name, glibc_version): + yield linux.replace("linux", name) + break + # Support for a later manylinux implies support for an earlier version. + for name, _ in manylinux_support_iter: + yield linux.replace("linux", name) + yield linux + + +def _generic_platforms(): + # type: () -> Iterator[str] + yield _normalize_string(distutils.util.get_platform()) + + +def _platform_tags(): + # type: () -> Iterator[str] + """ + Provides the platform tags for this installation. + """ + if platform.system() == "Darwin": + return mac_platforms() + elif platform.system() == "Linux": + return _linux_platforms() + else: + return _generic_platforms() + + +def interpreter_name(): + # type: () -> str + """ + Returns the name of the running interpreter. + """ + try: + name = sys.implementation.name # type: ignore + except AttributeError: # pragma: no cover + # Python 2.7 compatibility. + name = platform.python_implementation().lower() + return INTERPRETER_SHORT_NAMES.get(name) or name + + +def interpreter_version(**kwargs): + # type: (bool) -> str + """ + Returns the version of the running interpreter. + """ + warn = _warn_keyword_parameter("interpreter_version", kwargs) + version = _get_config_var("py_version_nodot", warn=warn) + if version: + version = str(version) + else: + version = _version_nodot(sys.version_info[:2]) + return version + + +def _version_nodot(version): + # type: (PythonVersion) -> str + if any(v >= 10 for v in version): + sep = "_" + else: + sep = "" + return sep.join(map(str, version)) + + +def sys_tags(**kwargs): + # type: (bool) -> Iterator[Tag] + """ + Returns the sequence of tag triples for the running interpreter. + + The order of the sequence corresponds to priority order for the + interpreter, from most to least important. + """ + warn = _warn_keyword_parameter("sys_tags", kwargs) + + interp_name = interpreter_name() + if interp_name == "cp": + for tag in cpython_tags(warn=warn): + yield tag + else: + for tag in generic_tags(): + yield tag + + for tag in compatible_tags(): + yield tag diff --git a/venv/lib/python3.8/site-packages/pip/_vendor/packaging/utils.py b/venv/lib/python3.8/site-packages/pip/_vendor/packaging/utils.py new file mode 100644 index 0000000..44f1bf9 --- /dev/null +++ b/venv/lib/python3.8/site-packages/pip/_vendor/packaging/utils.py @@ -0,0 +1,62 @@ +# 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 ._typing import MYPY_CHECK_RUNNING +from .version import InvalidVersion, Version + +if MYPY_CHECK_RUNNING: # pragma: no cover + from typing import Union + +_canonicalize_regex = re.compile(r"[-_.]+") + + +def canonicalize_name(name): + # type: (str) -> str + # This is taken from PEP 503. + return _canonicalize_regex.sub("-", name).lower() + + +def canonicalize_version(_version): + # type: (str) -> Union[Version, str] + """ + This is very similar to Version.__str__, but has one subtle difference + 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/venv/lib/python3.8/site-packages/pip/_vendor/packaging/version.py b/venv/lib/python3.8/site-packages/pip/_vendor/packaging/version.py new file mode 100644 index 0000000..f39a2a1 --- /dev/null +++ b/venv/lib/python3.8/site-packages/pip/_vendor/packaging/version.py @@ -0,0 +1,535 @@ +# 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, NegativeInfinity +from ._typing import MYPY_CHECK_RUNNING + +if MYPY_CHECK_RUNNING: # pragma: no cover + from typing import Callable, Iterator, List, Optional, SupportsInt, Tuple, Union + + from ._structures import InfinityType, NegativeInfinityType + + InfiniteTypes = Union[InfinityType, NegativeInfinityType] + PrePostDevType = Union[InfiniteTypes, Tuple[str, int]] + SubLocalType = Union[InfiniteTypes, int, str] + LocalType = Union[ + NegativeInfinityType, + Tuple[ + Union[ + SubLocalType, + Tuple[SubLocalType, str], + Tuple[NegativeInfinityType, SubLocalType], + ], + ..., + ], + ] + CmpKey = Tuple[ + int, Tuple[int, ...], PrePostDevType, PrePostDevType, PrePostDevType, LocalType + ] + LegacyCmpKey = Tuple[int, Tuple[str, ...]] + VersionComparisonMethod = Callable[ + [Union[CmpKey, LegacyCmpKey], Union[CmpKey, LegacyCmpKey]], bool + ] + +__all__ = ["parse", "Version", "LegacyVersion", "InvalidVersion", "VERSION_PATTERN"] + + +_Version = collections.namedtuple( + "_Version", ["epoch", "release", "dev", "pre", "post", "local"] +) + + +def parse(version): + # type: (str) -> Union[LegacyVersion, 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): + _key = None # type: Union[CmpKey, LegacyCmpKey] + + def __hash__(self): + # type: () -> int + return hash(self._key) + + def __lt__(self, other): + # type: (_BaseVersion) -> bool + return self._compare(other, lambda s, o: s < o) + + def __le__(self, other): + # type: (_BaseVersion) -> bool + return self._compare(other, lambda s, o: s <= o) + + def __eq__(self, other): + # type: (object) -> bool + return self._compare(other, lambda s, o: s == o) + + def __ge__(self, other): + # type: (_BaseVersion) -> bool + return self._compare(other, lambda s, o: s >= o) + + def __gt__(self, other): + # type: (_BaseVersion) -> bool + return self._compare(other, lambda s, o: s > o) + + def __ne__(self, other): + # type: (object) -> bool + return self._compare(other, lambda s, o: s != o) + + def _compare(self, other, method): + # type: (object, VersionComparisonMethod) -> Union[bool, NotImplemented] + if not isinstance(other, _BaseVersion): + return NotImplemented + + return method(self._key, other._key) + + +class LegacyVersion(_BaseVersion): + def __init__(self, version): + # type: (str) -> None + self._version = str(version) + self._key = _legacy_cmpkey(self._version) + + def __str__(self): + # type: () -> str + return self._version + + def __repr__(self): + # type: () -> str + return "".format(repr(str(self))) + + @property + def public(self): + # type: () -> str + return self._version + + @property + def base_version(self): + # type: () -> str + return self._version + + @property + def epoch(self): + # type: () -> int + return -1 + + @property + def release(self): + # type: () -> None + return None + + @property + def pre(self): + # type: () -> None + return None + + @property + def post(self): + # type: () -> None + return None + + @property + def dev(self): + # type: () -> None + return None + + @property + def local(self): + # type: () -> None + return None + + @property + def is_prerelease(self): + # type: () -> bool + return False + + @property + def is_postrelease(self): + # type: () -> bool + return False + + @property + def is_devrelease(self): + # type: () -> bool + 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): + # type: (str) -> Iterator[str] + 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): + # type: (str) -> LegacyCmpKey + + # 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 = [] # type: List[str] + 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) + + return epoch, tuple(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[0-9]+)!)? # epoch + (?P[0-9]+(?:\.[0-9]+)*) # release segment + (?P

                                          # pre-release
+            [-_\.]?
+            (?P(a|b|c|rc|alpha|beta|pre|preview))
+            [-_\.]?
+            (?P[0-9]+)?
+        )?
+        (?P                                         # post release
+            (?:-(?P[0-9]+))
+            |
+            (?:
+                [-_\.]?
+                (?Ppost|rev|r)
+                [-_\.]?
+                (?P[0-9]+)?
+            )
+        )?
+        (?P                                          # dev release
+            [-_\.]?
+            (?Pdev)
+            [-_\.]?
+            (?P[0-9]+)?
+        )?
+    )
+    (?:\+(?P[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):
+        # type: (str) -> None
+
+        # 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):
+        # type: () -> str
+        return "".format(repr(str(self)))
+
+    def __str__(self):
+        # type: () -> str
+        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):
+        # type: () -> int
+        _epoch = self._version.epoch  # type: int
+        return _epoch
+
+    @property
+    def release(self):
+        # type: () -> Tuple[int, ...]
+        _release = self._version.release  # type: Tuple[int, ...]
+        return _release
+
+    @property
+    def pre(self):
+        # type: () -> Optional[Tuple[str, int]]
+        _pre = self._version.pre  # type: Optional[Tuple[str, int]]
+        return _pre
+
+    @property
+    def post(self):
+        # type: () -> Optional[Tuple[str, int]]
+        return self._version.post[1] if self._version.post else None
+
+    @property
+    def dev(self):
+        # type: () -> Optional[Tuple[str, int]]
+        return self._version.dev[1] if self._version.dev else None
+
+    @property
+    def local(self):
+        # type: () -> Optional[str]
+        if self._version.local:
+            return ".".join(str(x) for x in self._version.local)
+        else:
+            return None
+
+    @property
+    def public(self):
+        # type: () -> str
+        return str(self).split("+", 1)[0]
+
+    @property
+    def base_version(self):
+        # type: () -> str
+        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):
+        # type: () -> bool
+        return self.dev is not None or self.pre is not None
+
+    @property
+    def is_postrelease(self):
+        # type: () -> bool
+        return self.post is not None
+
+    @property
+    def is_devrelease(self):
+        # type: () -> bool
+        return self.dev is not None
+
+    @property
+    def major(self):
+        # type: () -> int
+        return self.release[0] if len(self.release) >= 1 else 0
+
+    @property
+    def minor(self):
+        # type: () -> int
+        return self.release[1] if len(self.release) >= 2 else 0
+
+    @property
+    def micro(self):
+        # type: () -> int
+        return self.release[2] if len(self.release) >= 3 else 0
+
+
+def _parse_letter_version(
+    letter,  # type: str
+    number,  # type: Union[str, bytes, SupportsInt]
+):
+    # type: (...) -> Optional[Tuple[str, int]]
+
+    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)
+
+    return None
+
+
+_local_version_separators = re.compile(r"[\._-]")
+
+
+def _parse_local_version(local):
+    # type: (str) -> Optional[LocalType]
+    """
+    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)
+        )
+    return None
+
+
+def _cmpkey(
+    epoch,  # type: int
+    release,  # type: Tuple[int, ...]
+    pre,  # type: Optional[Tuple[str, int]]
+    post,  # type: Optional[Tuple[str, int]]
+    dev,  # type: Optional[Tuple[str, int]]
+    local,  # type: Optional[Tuple[SubLocalType]]
+):
+    # type: (...) -> CmpKey
+
+    # 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 = NegativeInfinity  # type: PrePostDevType
+    # Versions without a pre-release (except as noted above) should sort after
+    # those with one.
+    elif pre is None:
+        _pre = Infinity
+    else:
+        _pre = pre
+
+    # Versions without a post segment should sort before those with one.
+    if post is None:
+        _post = NegativeInfinity  # type: PrePostDevType
+
+    else:
+        _post = post
+
+    # Versions without a development segment should sort after those with one.
+    if dev is None:
+        _dev = Infinity  # type: PrePostDevType
+
+    else:
+        _dev = dev
+
+    if local is None:
+        # Versions without a local segment should sort before those with one.
+        _local = NegativeInfinity  # type: LocalType
+    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 (NegativeInfinity, i) for i in local
+        )
+
+    return epoch, _release, _pre, _post, _dev, _local
diff --git a/venv/lib/python3.8/site-packages/pip/_vendor/pep517/__init__.py b/venv/lib/python3.8/site-packages/pip/_vendor/pep517/__init__.py
new file mode 100644
index 0000000..7355b68
--- /dev/null
+++ b/venv/lib/python3.8/site-packages/pip/_vendor/pep517/__init__.py
@@ -0,0 +1,4 @@
+"""Wrappers to build Python packages using PEP 517 hooks
+"""
+
+__version__ = '0.8.2'
diff --git a/venv/lib/python3.8/site-packages/pip/_vendor/pep517/__pycache__/__init__.cpython-38.pyc b/venv/lib/python3.8/site-packages/pip/_vendor/pep517/__pycache__/__init__.cpython-38.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..70211ed89774bb6b31ef0a7cbe10072e7c30248e
GIT binary patch
literal 298
zcmYjM%TB{E5X=JvD)bYOeBjbU;t*(PC4_q6iXu^j)Qjb2-NYoZEjvk-d;)*MFXhT#
zaKh<{k#<(2UG2=i&*wA8;`ZhXwzmIF;y+v~Hnz;QC2)2QxH}iTH)r7w$%m#&F>O#I
z(Z1ysptNraDUhOBNpogUYq-c!`jVpM-6JZbEX`~$Pxojt^*
zAmrQ8zBN11ufa<5jy4Q;RO5rJz^-AH;pC{ghghuR1|Zp+RdlF_dKsON3%tdOtDqHA
y5(dGsV5|yC+JDOReLu~xH54E_LrAFl@$#vb&{phu`rde!&ip^wYE&)&

literal 0
HcmV?d00001

diff --git a/venv/lib/python3.8/site-packages/pip/_vendor/pep517/__pycache__/_in_process.cpython-38.pyc b/venv/lib/python3.8/site-packages/pip/_vendor/pep517/__pycache__/_in_process.cpython-38.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..e4db6a9f9261e9e1c5c5d29c73e3fa6056922557
GIT binary patch
literal 8161
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

literal 0
HcmV?d00001

diff --git a/venv/lib/python3.8/site-packages/pip/_vendor/pep517/__pycache__/build.cpython-38.pyc b/venv/lib/python3.8/site-packages/pip/_vendor/pep517/__pycache__/build.cpython-38.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..6919d0b21b100a4954bd610310c829e163147953
GIT binary patch
literal 3412
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

literal 0
HcmV?d00001

diff --git a/venv/lib/python3.8/site-packages/pip/_vendor/pep517/__pycache__/colorlog.cpython-38.pyc b/venv/lib/python3.8/site-packages/pip/_vendor/pep517/__pycache__/colorlog.cpython-38.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..fcf04f61928cfb43435d0b85fb339d184902d349
GIT binary patch
literal 2968
zcmZWr-EteZ6$Y@sTv3XnNn*!s(w%AAG1FLboW@Nwo=i2e0%Vh%1&N%egge}QLAWdEz`d{{559hna*z8jXw=~B8EG}nciU-K$S^DVVXu(GM5#3I
zhbO5z47){EjAXSE1RDhxFf3ES!=5aL;eH|Wgcl!$>t)hC6k!HR@=EZP;3XJO+Fhix
zMu9=+HlSC31H$QqC?fY1Rcv5!cENxL?$W-3+4J`mxpc0;DfqhFx}w}ZvgI0gCIlRR
z%O=VL$NyS8UOPP|;Edbe(r#LT3uoFz<7Fjr@BNk!WAH>&+B-~8Kx(TxtF&7wE~JzU
zq@qS8)TmTK4kKTseWCKvP&YPDyP{NSk+-OJjM9G*iHZEFS}kRIoG7t+oXFK)k%20h
z>#jaJDUbj7F#r6a;{4G;_1S~o@O=2`1JnBX>i%k(miI?FMD9rN`zMD&WcQDfvqACs
zCujKIYJ4nmUdYu_l%Ib5r&Y5nBxt2P)9=K!$#FF*%R;IuHvj$_462ra5NgxAlu@59
zneI~AgxMSUK(TqTeE|bOpvXwXIE^e}4;;>bLgayZ+y?4%JGG%?7N*4$@CFZ{$QHN@
zac}D8PSM}0t4GN5-@|BDOi)_aYAKvr9fZ(cV$@$+NKBU6UF2uY1IC4JisnDSC^Db}
zHYQ^*^G8NWO2#C%##H`W*%t(;1I5CwX!*!+P&VZwZci-kC~x4~#3DyTwz&)R+uWuk
z4^(4paqogntT7n`mkU?q3HdJkmQC!5qZ**4Ikv{m6+;=2u*UXd@_o}hOmc2!pk(V>
zG_$ikjhx_fMO>61h?W<*u_6=4A`8>rO=rWj0)f|Fm=GB&jlybeDn10CKv`9z5}c7f
zs;+gSn{b%ZJedY|ZbTR0Y(6lmr_tnanoUHX0Hf03FT?p9Kn`O(t8d({)47*aT@-IY
zJjyKTibI12VT!>jrRb%nG3f1dUVF5%5*`Cy0I+lqle{nZ+FJO@%EP%?>zu<*osL;H
z4f51H47%)(h9Xx0&R9X%0H3_6K;!_FCB4XoPfwE;lW)KQGFaHMYJLePa8^5Ddzyl5
zUp|msU8}qZR{WwnZRK>CO>TFjAU!)CdrmzXIlz4J=bLt?h=oJ9C
zfVxXhHn-NF@9f7LyF0sk&$N>&F{~m-m5CgJ${ax?i#*(;>dMFm7#=34XQ#S>xjad^
zI*eSXpp!Ijc^Vwdda?nV$v2Gwy?$*K*p79T7uv>TM;0hEs9loe9dusQ-#l_KLB)G+@hsqyb^CnrxXNq6W;h&fkXPHV??kEFN7%vspv%a-bXV`X?}o
zjOl=0&@^1LyVp9r6~L*tre2pbhI>
z_}4pfWsYAQze^?#@czWjeeT*Y0ziU2$2RD6pj*)0+jviU?ekkdV1&~oo812)PljSr
z7YEZO@-VM$oc@W1=PV2*-Yl(#wla}xZ;CY9g<}Bk1>LAa4dU{*ASd5P5+b>SpQTPO)?5Fj#=P%9b%lEo
z8qg-SU%8ODJIrQH82QX+=XYehSx7%C0+WUHauf5%Vs-4Zfz1=o5My53W@l~7qEI?WIG+jmAhd(WtHrcq
zl8?_Squ^&A>f6FxWhS8`*&AhXF;eA76<~FRZx8~n6ikzR416n)=#H_>K!|}Ay|6vR
zr_QF7g{;x$9hAbj$d8fW+G{(TP-B3#|F}-&7Z^VCKC+j6C}(E!IlJD+D%PIXQe^bsqajw
zcDV>8MB-p-XPSTy_YFof($vsF=pt2D(1+AyGclx@Ee}!C
zbu>s+KG&S6+A+=OS2m&F=ssaHioPR3e^I5B>7Ytzrj|1AL>czA9m@cwOwF6hWn&B@
zg;_EiDvAe?<6OY>H_mIAT|CeRt`-A*&rWWhA0{xcB06DTE*L5swUvXX3%q3$T{7lR
zSUF8su|5MdM=$2=Hf}38LoENp7i(&!+RFT1E&rppxq1!tSMSw|F8e;n(Dw^gt#q8~
zxgoxyND4m^9hn1G=A>TQi(=lC*`6E-c%+n6y3!2#ya>yWpXR~23+GqQhvDAqOBlrb
azIh<2!DdsJx}^o?+EvH3-7>>`f&K)a{@mOE

literal 0
HcmV?d00001

diff --git a/venv/lib/python3.8/site-packages/pip/_vendor/pep517/__pycache__/dirtools.cpython-38.pyc b/venv/lib/python3.8/site-packages/pip/_vendor/pep517/__pycache__/dirtools.cpython-38.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..0b8329dc397c80d2f2a6e7e2f1f772d745d6eda1
GIT binary patch
literal 1347
zcmah}Pj4eN6t_Lj%rvFds#*aO0_LzniqvLGp$jV^gyZfmPekRH7Mn^2sPznt+V
z1!#PQtIm)KB4|OfTS+tOZI&@FgDmipXWYwH*77pULdiyvU?O-)ex+F~xM;m3*@g&3
zgt9H@8R^7-V}5cnaQu!aDjT?*vwncgkQKclOR{J;F?fE{%L)l1X0XmlC=s9d2WHdZ-F%qsdq}!-n@bvJ@K9N>+y-f0knHM(d*P
zR&yJ#Ol5K~6m4DLGRe4Ya2?${}=`Z2P_wjm?hkcR$F&&e6#*t~HYzNBQy
zC|T?@Y!sMa#K!@2fAP%~bjf~Z7vu+0B44-n{984a$usPcf@ix)e`*q;Wu27TB#^5b
z8(${!#Xwd@=`vBn#9YaCg7ohY@&ZRB=6FoirtbRPlTOPrUE7w=Y@^e&V*m}dB>_rp
z!{g(NCudJPf#o?|);1i^ybSOvKagAIdg2GDpjX{@f87I|GrCRh(YGkX4FHA3VOqq%?ELtyj8{*f+g1Ji+VEmq~;L{1!}
z7ph7eaFd}11o!JUcP*X0qNydFP0N(F(bsb$>nEo+!iZa>XR7)JBR!rhpXJ5a@+vo1
zmXEY5Z3MFLW;_Fhu?ENCwN7j~Xl-oRpALr-Y>X*;J}1Zfaf(i_;XUkU77gp(Z5B-D
zwf_!m8)JG#_ttfN=tl7_GQwh<*?k)0^tS2FTHd4Z5$<(1ecA9f>bAXp(uL{+LkHm3
zjKG#-E|J|{@2_9He=Z&iKGhrbxVH@+Me!FCEvALU2ZH4|EX7#B>tDcWdn4Zb2kY@y
AGynhq

literal 0
HcmV?d00001

diff --git a/venv/lib/python3.8/site-packages/pip/_vendor/pep517/__pycache__/envbuild.cpython-38.pyc b/venv/lib/python3.8/site-packages/pip/_vendor/pep517/__pycache__/envbuild.cpython-38.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..88d9d91df51660cf7914cdf746e1ceafd45091a7
GIT binary patch
literal 4462
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

literal 0
HcmV?d00001

diff --git a/venv/lib/python3.8/site-packages/pip/_vendor/pep517/__pycache__/wrappers.cpython-38.pyc b/venv/lib/python3.8/site-packages/pip/_vendor/pep517/__pycache__/wrappers.cpython-38.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..e2f44a77ceaf82ca97bfa9b5f69531577bf45593
GIT binary patch
literal 10564
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
J=Ks>T^q=v*jEDdL

literal 0
HcmV?d00001

diff --git a/venv/lib/python3.8/site-packages/pip/_vendor/pep517/_in_process.py b/venv/lib/python3.8/site-packages/pip/_vendor/pep517/_in_process.py
new file mode 100644
index 0000000..a536b03
--- /dev/null
+++ b/venv/lib/python3.8/site-packages/pip/_vendor/pep517/_in_process.py
@@ -0,0 +1,280 @@
+"""This is invoked in a subprocess to call the build backend hooks.
+
+It expects:
+- Command line args: hook_name, control_dir
+- Environment variables:
+      PEP517_BUILD_BACKEND=entry.point:spec
+      PEP517_BACKEND_PATH=paths (separated with os.pathsep)
+- control_dir/input.json:
+  - {"kwargs": {...}}
+
+Results:
+- control_dir/output.json
+  - {"return_val": ...}
+"""
+from glob import glob
+from importlib import import_module
+import json
+import os
+import os.path
+from os.path import join as pjoin
+import re
+import shutil
+import sys
+import traceback
+
+# This file is run as a script, and `import compat` is not zip-safe, so we
+# include write_json() and read_json() from compat.py.
+#
+# Handle reading and writing JSON in UTF-8, on Python 3 and 2.
+
+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)
+
+
+class BackendUnavailable(Exception):
+    """Raised if we cannot import the backend"""
+    def __init__(self, traceback):
+        self.traceback = traceback
+
+
+class BackendInvalid(Exception):
+    """Raised if the backend is invalid"""
+    def __init__(self, message):
+        self.message = message
+
+
+class HookMissing(Exception):
+    """Raised if a hook is missing and we are not executing the fallback"""
+
+
+def contained_in(filename, directory):
+    """Test if a file is located within the given directory."""
+    filename = os.path.normcase(os.path.abspath(filename))
+    directory = os.path.normcase(os.path.abspath(directory))
+    return os.path.commonprefix([filename, directory]) == directory
+
+
+def _build_backend():
+    """Find and load the build backend"""
+    # Add in-tree backend directories to the front of sys.path.
+    backend_path = os.environ.get('PEP517_BACKEND_PATH')
+    if backend_path:
+        extra_pathitems = backend_path.split(os.pathsep)
+        sys.path[:0] = extra_pathitems
+
+    ep = os.environ['PEP517_BUILD_BACKEND']
+    mod_path, _, obj_path = ep.partition(':')
+    try:
+        obj = import_module(mod_path)
+    except ImportError:
+        raise BackendUnavailable(traceback.format_exc())
+
+    if backend_path:
+        if not any(
+            contained_in(obj.__file__, path)
+            for path in extra_pathitems
+        ):
+            raise BackendInvalid("Backend was not loaded from backend-path")
+
+    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, _allow_fallback):
+    """Invoke optional prepare_metadata_for_build_wheel
+
+    Implements a fallback by building a wheel if the hook isn't defined,
+    unless _allow_fallback is False in which case HookMissing is raised.
+    """
+    backend = _build_backend()
+    try:
+        hook = backend.prepare_metadata_for_build_wheel
+    except AttributeError:
+        if not _allow_fallback:
+            raise HookMissing()
+        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 __init__(self, traceback):
+        self.traceback = traceback
+
+
+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(traceback.format_exc())
+
+
+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 = 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 as e:
+        json_out['no_backend'] = True
+        json_out['traceback'] = e.traceback
+    except BackendInvalid as e:
+        json_out['backend_invalid'] = True
+        json_out['backend_error'] = e.message
+    except GotUnsupportedOperation as e:
+        json_out['unsupported'] = True
+        json_out['traceback'] = e.traceback
+    except HookMissing:
+        json_out['hook_missing'] = True
+
+    write_json(json_out, pjoin(control_dir, 'output.json'), indent=2)
+
+
+if __name__ == '__main__':
+    main()
diff --git a/venv/lib/python3.8/site-packages/pip/_vendor/pep517/build.py b/venv/lib/python3.8/site-packages/pip/_vendor/pep517/build.py
new file mode 100644
index 0000000..2643014
--- /dev/null
+++ b/venv/lib/python3.8/site-packages/pip/_vendor/pep517/build.py
@@ -0,0 +1,124 @@
+"""Build a project using PEP 517 hooks.
+"""
+import argparse
+import logging
+import os
+from pip._vendor import toml
+import shutil
+
+from .envbuild import BuildEnvironment
+from .wrappers import Pep517HookCaller
+from .dirtools import tempdir, mkdir_p
+from .compat import FileNotFoundError
+
+log = logging.getLogger(__name__)
+
+
+def validate_system(system):
+    """
+    Ensure build system has the requisite fields.
+    """
+    required = {'requires', 'build-backend'}
+    if not (required <= set(system)):
+        message = "Missing required fields: {missing}".format(
+            missing=required-set(system),
+        )
+        raise ValueError(message)
+
+
+def load_system(source_dir):
+    """
+    Load the build system from a source dir (pyproject.toml).
+    """
+    pyproject = os.path.join(source_dir, 'pyproject.toml')
+    with open(pyproject) as f:
+        pyproject_data = toml.load(f)
+    return pyproject_data['build-system']
+
+
+def compat_system(source_dir):
+    """
+    Given a source dir, attempt to get a build system backend
+    and requirements from pyproject.toml. Fallback to
+    setuptools but only if the file was not found or a build
+    system was not indicated.
+    """
+    try:
+        system = load_system(source_dir)
+    except (FileNotFoundError, KeyError):
+        system = {}
+    system.setdefault(
+        'build-backend',
+        'setuptools.build_meta:__legacy__',
+    )
+    system.setdefault('requires', ['setuptools', 'wheel'])
+    return system
+
+
+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 build(source_dir, dist, dest=None, system=None):
+    system = system or load_system(source_dir)
+    dest = os.path.join(source_dir, dest or 'dist')
+    mkdir_p(dest)
+
+    validate_system(system)
+    hooks = Pep517HookCaller(
+        source_dir, system['build-backend'], system.get('backend-path')
+    )
+
+    with BuildEnvironment() as env:
+        env.pip_install(system['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/venv/lib/python3.8/site-packages/pip/_vendor/pep517/check.py b/venv/lib/python3.8/site-packages/pip/_vendor/pep517/check.py
new file mode 100644
index 0000000..13e722a
--- /dev/null
+++ b/venv/lib/python3.8/site-packages/pip/_vendor/pep517/check.py
@@ -0,0 +1,203 @@
+"""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.toml import TomlDecodeError, 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']
+        backend_path = buildsys.get('backend-path')
+        log.info('Loaded pyproject.toml')
+    except (TomlDecodeError, KeyError):
+        log.error("Invalid pyproject.toml", exc_info=True)
+        return False
+
+    hooks = Pep517HookCaller(source_dir, backend, backend_path)
+
+    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/venv/lib/python3.8/site-packages/pip/_vendor/pep517/colorlog.py b/venv/lib/python3.8/site-packages/pip/_vendor/pep517/colorlog.py
new file mode 100644
index 0000000..69c8a59
--- /dev/null
+++ b/venv/lib/python3.8/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/venv/lib/python3.8/site-packages/pip/_vendor/pep517/compat.py b/venv/lib/python3.8/site-packages/pip/_vendor/pep517/compat.py
new file mode 100644
index 0000000..8432acb
--- /dev/null
+++ b/venv/lib/python3.8/site-packages/pip/_vendor/pep517/compat.py
@@ -0,0 +1,34 @@
+"""Python 2/3 compatibility"""
+import json
+import sys
+
+
+# Handle reading and writing JSON in UTF-8, on Python 3 and 2.
+
+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)
+
+
+# FileNotFoundError
+
+try:
+    FileNotFoundError = FileNotFoundError
+except NameError:
+    FileNotFoundError = IOError
diff --git a/venv/lib/python3.8/site-packages/pip/_vendor/pep517/dirtools.py b/venv/lib/python3.8/site-packages/pip/_vendor/pep517/dirtools.py
new file mode 100644
index 0000000..58c6ca0
--- /dev/null
+++ b/venv/lib/python3.8/site-packages/pip/_vendor/pep517/dirtools.py
@@ -0,0 +1,44 @@
+import os
+import io
+import contextlib
+import tempfile
+import shutil
+import errno
+import zipfile
+
+
+@contextlib.contextmanager
+def tempdir():
+    """Create a temporary directory in a context manager."""
+    td = tempfile.mkdtemp()
+    try:
+        yield td
+    finally:
+        shutil.rmtree(td)
+
+
+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 dir_to_zipfile(root):
+    """Construct an in-memory zip file for a directory."""
+    buffer = io.BytesIO()
+    zip_file = zipfile.ZipFile(buffer, 'w')
+    for root, dirs, files in os.walk(root):
+        for path in dirs:
+            fs_path = os.path.join(root, path)
+            rel_path = os.path.relpath(fs_path, root)
+            zip_file.writestr(rel_path + '/', '')
+        for path in files:
+            fs_path = os.path.join(root, path)
+            rel_path = os.path.relpath(fs_path, root)
+            zip_file.write(fs_path, rel_path)
+    return zip_file
diff --git a/venv/lib/python3.8/site-packages/pip/_vendor/pep517/envbuild.py b/venv/lib/python3.8/site-packages/pip/_vendor/pep517/envbuild.py
new file mode 100644
index 0000000..4088dcd
--- /dev/null
+++ b/venv/lib/python3.8/site-packages/pip/_vendor/pep517/envbuild.py
@@ -0,0 +1,167 @@
+"""Build wheels/sdists by installing build deps to a temporary environment.
+"""
+
+import os
+import logging
+from pip._vendor import toml
+import shutil
+from subprocess import check_call
+import sys
+from sysconfig import get_paths
+from tempfile import mkdtemp
+
+from .wrappers import Pep517HookCaller, LoggerWrapper
+
+log = logging.getLogger(__name__)
+
+
+def _load_pyproject(source_dir):
+    with open(os.path.join(source_dir, 'pyproject.toml')) as f:
+        pyproject_data = toml.load(f)
+    buildsys = pyproject_data['build-system']
+    return (
+        buildsys['requires'],
+        buildsys['build-backend'],
+        buildsys.get('backend-path'),
+    )
+
+
+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)
+        cmd = [
+            sys.executable, '-m', 'pip', 'install', '--ignore-installed',
+            '--prefix', self.path] + list(reqs)
+        check_call(
+            cmd,
+            stdout=LoggerWrapper(log, logging.INFO),
+            stderr=LoggerWrapper(log, logging.ERROR),
+        )
+
+    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, backend_path = _load_pyproject(source_dir)
+    hooks = Pep517HookCaller(source_dir, backend, backend_path)
+
+    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, backend_path = _load_pyproject(source_dir)
+    hooks = Pep517HookCaller(source_dir, backend, backend_path)
+
+    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/venv/lib/python3.8/site-packages/pip/_vendor/pep517/meta.py b/venv/lib/python3.8/site-packages/pip/_vendor/pep517/meta.py
new file mode 100644
index 0000000..d525de5
--- /dev/null
+++ b/venv/lib/python3.8/site-packages/pip/_vendor/pep517/meta.py
@@ -0,0 +1,92 @@
+"""Build metadata for a project using PEP 517 hooks.
+"""
+import argparse
+import logging
+import os
+import shutil
+import functools
+
+try:
+    import importlib.metadata as imp_meta
+except ImportError:
+    import importlib_metadata as imp_meta
+
+try:
+    from zipfile import Path
+except ImportError:
+    from zipp import Path
+
+from .envbuild import BuildEnvironment
+from .wrappers import Pep517HookCaller, quiet_subprocess_runner
+from .dirtools import tempdir, mkdir_p, dir_to_zipfile
+from .build import validate_system, load_system, compat_system
+
+log = logging.getLogger(__name__)
+
+
+def _prep_meta(hooks, env, dest):
+    reqs = hooks.get_requires_for_build_wheel({})
+    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 metadata in %s', td)
+        filename = hooks.prepare_metadata_for_build_wheel(td, {})
+        source = os.path.join(td, filename)
+        shutil.move(source, os.path.join(dest, os.path.basename(filename)))
+
+
+def build(source_dir='.', dest=None, system=None):
+    system = system or load_system(source_dir)
+    dest = os.path.join(source_dir, dest or 'dist')
+    mkdir_p(dest)
+    validate_system(system)
+    hooks = Pep517HookCaller(
+        source_dir, system['build-backend'], system.get('backend-path')
+    )
+
+    with hooks.subprocess_runner(quiet_subprocess_runner):
+        with BuildEnvironment() as env:
+            env.pip_install(system['requires'])
+            _prep_meta(hooks, env, dest)
+
+
+def build_as_zip(builder=build):
+    with tempdir() as out_dir:
+        builder(dest=out_dir)
+        return dir_to_zipfile(out_dir)
+
+
+def load(root):
+    """
+    Given a source directory (root) of a package,
+    return an importlib.metadata.Distribution object
+    with metadata build from that package.
+    """
+    root = os.path.expanduser(root)
+    system = compat_system(root)
+    builder = functools.partial(build, source_dir=root, system=system)
+    path = Path(build_as_zip(builder))
+    return imp_meta.PathDistribution(path)
+
+
+parser = argparse.ArgumentParser()
+parser.add_argument(
+    'source_dir',
+    help="A directory containing pyproject.toml",
+)
+parser.add_argument(
+    '--out-dir', '-o',
+    help="Destination in which to save the builds relative to source dir",
+)
+
+
+def main():
+    args = parser.parse_args()
+    build(args.source_dir, args.out_dir)
+
+
+if __name__ == '__main__':
+    main()
diff --git a/venv/lib/python3.8/site-packages/pip/_vendor/pep517/wrappers.py b/venv/lib/python3.8/site-packages/pip/_vendor/pep517/wrappers.py
new file mode 100644
index 0000000..00a3d1a
--- /dev/null
+++ b/venv/lib/python3.8/site-packages/pip/_vendor/pep517/wrappers.py
@@ -0,0 +1,308 @@
+import threading
+from contextlib import contextmanager
+import os
+from os.path import dirname, abspath, join as pjoin
+import shutil
+from subprocess import check_call, check_output, STDOUT
+import sys
+from tempfile import mkdtemp
+
+from . import compat
+
+
+try:
+    import importlib.resources as resources
+
+    def _in_proc_script_path():
+        return resources.path(__package__, '_in_process.py')
+except ImportError:
+    @contextmanager
+    def _in_proc_script_path():
+        yield 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."""
+    def __init__(self, traceback):
+        self.traceback = traceback
+
+
+class BackendInvalid(Exception):
+    """Will be raised if the backend is invalid."""
+    def __init__(self, backend_name, backend_path, message):
+        self.backend_name = backend_name
+        self.backend_path = backend_path
+        self.message = message
+
+
+class HookMissing(Exception):
+    """Will be raised on missing hooks."""
+    def __init__(self, hook_name):
+        super(HookMissing, self).__init__(hook_name)
+        self.hook_name = hook_name
+
+
+class UnsupportedOperation(Exception):
+    """May be raised by build_sdist if the backend indicates that it can't."""
+    def __init__(self, traceback):
+        self.traceback = traceback
+
+
+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)
+
+
+def quiet_subprocess_runner(cmd, cwd=None, extra_environ=None):
+    """A method of calling the wrapper subprocess while suppressing output."""
+    env = os.environ.copy()
+    if extra_environ:
+        env.update(extra_environ)
+
+    check_output(cmd, cwd=cwd, env=env, stderr=STDOUT)
+
+
+def norm_and_check(source_tree, requested):
+    """Normalise and check a backend path.
+
+    Ensure that the requested backend path is specified as a relative path,
+    and resolves to a location under the given source tree.
+
+    Return an absolute version of the requested path.
+    """
+    if os.path.isabs(requested):
+        raise ValueError("paths must be relative")
+
+    abs_source = os.path.abspath(source_tree)
+    abs_requested = os.path.normpath(os.path.join(abs_source, requested))
+    # We have to use commonprefix for Python 2.7 compatibility. So we
+    # normalise case to avoid problems because commonprefix is a character
+    # based comparison :-(
+    norm_source = os.path.normcase(abs_source)
+    norm_requested = os.path.normcase(abs_requested)
+    if os.path.commonprefix([norm_source, norm_requested]) != norm_source:
+        raise ValueError("paths must be inside source tree")
+
+    return abs_requested
+
+
+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.
+    build_backend : The build backend spec, as per PEP 517, from
+        pyproject.toml.
+    backend_path : The backend path, as per PEP 517, from pyproject.toml.
+    runner : A callable that invokes the wrapper subprocess.
+
+    The 'runner', if provided, must expect the following:
+        cmd : a list of strings representing the command and arguments to
+            execute, as would be passed to e.g. 'subprocess.check_call'.
+        cwd : a string representing the working directory that must be
+            used for the subprocess. Corresponds to the provided source_dir.
+        extra_environ : a dict mapping environment variable names to values
+            which must be set for the subprocess execution.
+    """
+    def __init__(
+            self,
+            source_dir,
+            build_backend,
+            backend_path=None,
+            runner=None,
+    ):
+        if runner is None:
+            runner = default_subprocess_runner
+
+        self.source_dir = abspath(source_dir)
+        self.build_backend = build_backend
+        if backend_path:
+            backend_path = [
+                norm_and_check(self.source_dir, p) for p in backend_path
+            ]
+        self.backend_path = backend_path
+        self._subprocess_runner = runner
+
+    @contextmanager
+    def subprocess_runner(self, runner):
+        """A context manager for temporarily overriding the default subprocess
+        runner.
+        """
+        prev = self._subprocess_runner
+        self._subprocess_runner = runner
+        try:
+            yield
+        finally:
+            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,
+            _allow_fallback=True):
+        """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 (unless _allow_fallback is
+        False).
+        """
+        return self._call_hook('prepare_metadata_for_build_wheel', {
+            'metadata_directory': abspath(metadata_directory),
+            'config_settings': config_settings,
+            '_allow_fallback': _allow_fallback,
+        })
+
+    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).
+        # For backend_path, we use sys.getfilesystemencoding.
+        if sys.version_info[0] == 2:
+            build_backend = self.build_backend.encode('ASCII')
+        else:
+            build_backend = self.build_backend
+        extra_environ = {'PEP517_BUILD_BACKEND': build_backend}
+
+        if self.backend_path:
+            backend_path = os.pathsep.join(self.backend_path)
+            if sys.version_info[0] == 2:
+                backend_path = backend_path.encode(sys.getfilesystemencoding())
+            extra_environ['PEP517_BACKEND_PATH'] = backend_path
+
+        with tempdir() as td:
+            hook_input = {'kwargs': kwargs}
+            compat.write_json(hook_input, pjoin(td, 'input.json'),
+                              indent=2)
+
+            # Run the hook in a subprocess
+            with _in_proc_script_path() as script:
+                self._subprocess_runner(
+                    [sys.executable, str(script), hook_name, td],
+                    cwd=self.source_dir,
+                    extra_environ=extra_environ
+                )
+
+            data = compat.read_json(pjoin(td, 'output.json'))
+            if data.get('unsupported'):
+                raise UnsupportedOperation(data.get('traceback', ''))
+            if data.get('no_backend'):
+                raise BackendUnavailable(data.get('traceback', ''))
+            if data.get('backend_invalid'):
+                raise BackendInvalid(
+                    backend_name=self.build_backend,
+                    backend_path=self.backend_path,
+                    message=data.get('backend_error', '')
+                )
+            if data.get('hook_missing'):
+                raise HookMissing(hook_name)
+            return data['return_val']
+
+
+class LoggerWrapper(threading.Thread):
+    """
+    Read messages from a pipe and redirect them
+    to a logger (see python's logging module).
+    """
+
+    def __init__(self, logger, level):
+        threading.Thread.__init__(self)
+        self.daemon = True
+
+        self.logger = logger
+        self.level = level
+
+        # create the pipe and reader
+        self.fd_read, self.fd_write = os.pipe()
+        self.reader = os.fdopen(self.fd_read)
+
+        self.start()
+
+    def fileno(self):
+        return self.fd_write
+
+    @staticmethod
+    def remove_newline(msg):
+        return msg[:-1] if msg.endswith(os.linesep) else msg
+
+    def run(self):
+        for line in self.reader:
+            self._write(self.remove_newline(line))
+
+    def _write(self, message):
+        self.logger.log(self.level, message)
diff --git a/venv/lib/python3.8/site-packages/pip/_vendor/progress/__init__.py b/venv/lib/python3.8/site-packages/pip/_vendor/progress/__init__.py
new file mode 100644
index 0000000..e434c25
--- /dev/null
+++ b/venv/lib/python3.8/site-packages/pip/_vendor/progress/__init__.py
@@ -0,0 +1,177 @@
+# Copyright (c) 2012 Giorgos Verigakis 
+#
+# 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/venv/lib/python3.8/site-packages/pip/_vendor/progress/__pycache__/__init__.cpython-38.pyc b/venv/lib/python3.8/site-packages/pip/_vendor/progress/__pycache__/__init__.cpython-38.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..38725e7fc9c87a8104bbef6bcbd3d46797d6a469
GIT binary patch
literal 5633
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
KvG9AYV*U#!)hWdQ

literal 0
HcmV?d00001

diff --git a/venv/lib/python3.8/site-packages/pip/_vendor/progress/__pycache__/counter.cpython-38.pyc b/venv/lib/python3.8/site-packages/pip/_vendor/progress/__pycache__/counter.cpython-38.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..e8bc72faf052d3ca8c3defce13f8ce031cc22b25
GIT binary patch
literal 1489
zcmbVMOK;Oa5Z?9LiIXNJKtU}Cfe;6LXk8VksDx0Z9uSwPQsELI8I#?#Zf(b`?Sxk8
z0a6}<1480)g=7BG9^ur#&=WI`)27fKV56Dw?Ci|$%zTr_)oPi*x^d+_e_{~w3zhMu
zgR%@1KLy4Kr#=a2gHpmZ&U`Ik4HoDPostKH>)hBQ+z_nK8V2YEUIe`;bkGZ+o4f>i
zDbtIfm-z(f6QT%qlULS=Ro#bMh(%Q`4ZUWY3&-~oAzeS-r)bcW`7msGA#5shRkqiq
zh~p;uj4ut8WuSNpm>>n1Ra|MOA%t#X+|j~sOn3z-bMBGvB%wXB
zsrBg(Oq#5b0j*n18C{8wU~wXq;e}kh7*M6h!fy?Ttb+eRVn@>JxQVbkuC!Zip9>k=
zQPW=RMxEOW;nG6F`Qo$q?tClQ3GU2iYdy3d*^w8`reWkZw*;TK1h{K&1F$1gvp*xd#s&hkAeV{ATaY8o`j-$$s6SR5i
z<9Eezo~N!qw8%5yoMke`CULI->;h00-=vxTUp**g5994_`0rhiMR`R-WRFcwVk5WI`^-e1NbvP3vatJNcX}VL%sxT_OWyrH&lIM_}M>d1(R0txx
zuL8w5ttQnqi93I_1m}4fD8|knZwa)5DYohf%NSPk+lO3!&gG|Ee#zy>x}~4^$|JC3
zUm4o)aX0a1oIyYg>UFDx2a+ffLsT+i;Twa$A1#pIC0cW`aSOu)H9N=7x
zGl27~0Jsq2EZ`z50WQT@V1LO^%oA|F*AhGk^dO0P
z++T?@q0ivXQ49{FB(zLdN7A&6C!2N1lxCW))#j!d>mg#Er(t#xB)~4vs6hPR919OT
z&mAWDA|>@Qwa-)gsxC0#136)>E`#iHLcEU?=Cy`S!>=I88{|%EW%hDcxNp0|$=m#r
z@YwvC+Ha}-eh9C#aNQNWGR+m@eY`YStfA8-Q2*AGsrcW)w3Y_ka`1}sQE|pz=FU*k
zdOs2}y|lu;_bGmp*sY(5eV5u^8bt3f^5=lgRpgbDM_jMuu-ltqClt`_0=FU}uqESW
kjk`f$`US=+@SB4w7HPMV7jqkXD!^w_q(!-$D=YZ>1CD+wegFUf

literal 0
HcmV?d00001

diff --git a/venv/lib/python3.8/site-packages/pip/_vendor/progress/bar.py b/venv/lib/python3.8/site-packages/pip/_vendor/progress/bar.py
new file mode 100644
index 0000000..8819efd
--- /dev/null
+++ b/venv/lib/python3.8/site-packages/pip/_vendor/progress/bar.py
@@ -0,0 +1,91 @@
+# -*- coding: utf-8 -*-
+
+# Copyright (c) 2012 Giorgos Verigakis 
+#
+# 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/venv/lib/python3.8/site-packages/pip/_vendor/progress/counter.py b/venv/lib/python3.8/site-packages/pip/_vendor/progress/counter.py
new file mode 100644
index 0000000..d955ca4
--- /dev/null
+++ b/venv/lib/python3.8/site-packages/pip/_vendor/progress/counter.py
@@ -0,0 +1,41 @@
+# -*- coding: utf-8 -*-
+
+# Copyright (c) 2012 Giorgos Verigakis 
+#
+# 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/venv/lib/python3.8/site-packages/pip/_vendor/progress/spinner.py b/venv/lib/python3.8/site-packages/pip/_vendor/progress/spinner.py
new file mode 100644
index 0000000..4e100ca
--- /dev/null
+++ b/venv/lib/python3.8/site-packages/pip/_vendor/progress/spinner.py
@@ -0,0 +1,43 @@
+# -*- coding: utf-8 -*-
+
+# Copyright (c) 2012 Giorgos Verigakis 
+#
+# 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/venv/lib/python3.8/site-packages/pip/_vendor/pyparsing.py b/venv/lib/python3.8/site-packages/pip/_vendor/pyparsing.py
new file mode 100644
index 0000000..7ebc7eb
--- /dev/null
+++ b/venv/lib/python3.8/site-packages/pip/_vendor/pyparsing.py
@@ -0,0 +1,7107 @@
+# -*- 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
+``", !"``), built up using :class:`Word`,
+:class:`Literal`, and :class:`And` elements
+(the :class:`'+'` 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:`'+'`, :class:`'|'`, :class:`'^'`,
+   and :class:`'&'` operators to combine simple expressions into
+   more complex ones
+ - associate names with your parsed results using
+   :class:`ParserElement.setResultsName`
+ - access the parsed data, which is returned as a :class:`ParseResults`
+   object
+ - 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.7"
+__versionTime__ = "30 Mar 2020 00:43 UTC"
+__author__ = "Paul McGuire "
+
+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
+from operator import itemgetter
+import itertools
+from functools import wraps
+from contextlib import contextmanager
+
+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, Mapping
+except ImportError:
+    # Python 2.7
+    from collections import Iterable
+    from collections import MutableMapping, Mapping
+
+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 released in pyparsing 2.3.0, or False to preserve
+       pre-2.3.0 handling of named results
+"""
+__compat__.collect_all_And_tokens = True
+
+__diag__ = SimpleNamespace()
+__diag__.__doc__ = """
+Diagnostic configuration (all default to False)
+     - warn_multiple_tokens_in_named_alternation - flag to enable warnings when a results
+       name is defined on a MatchFirst or Or expression with one or more And subexpressions
+       (only warns if __compat__.collect_all_And_tokens is False)
+     - warn_ungrouped_named_tokens_in_collection - flag to enable warnings when a results
+       name is defined on a containing expression with ungrouped subexpressions that also
+       have results names
+     - warn_name_set_on_empty_Forward - flag to enable warnings whan a Forward is defined
+       with a results name, but has no contents defined
+     - warn_on_multiple_string_args_to_oneof - flag to enable warnings whan oneOf is
+       incorrectly called with multiple str arguments
+     - enable_debug_on_named_expressions - flag to auto-enable debug on all subsequent
+       calls to ParserElement.setName()
+"""
+__diag__.warn_multiple_tokens_in_named_alternation = False
+__diag__.warn_ungrouped_named_tokens_in_collection = False
+__diag__.warn_name_set_on_empty_Forward = False
+__diag__.warn_on_multiple_string_args_to_oneof = False
+__diag__.enable_debug_on_named_expressions = False
+__diag__._all_names = [nm for nm in vars(__diag__) if nm.startswith("enable_") or nm.startswith("warn_")]
+
+def _enable_all_warnings():
+    __diag__.warn_multiple_tokens_in_named_alternation = True
+    __diag__.warn_ungrouped_named_tokens_in_collection = True
+    __diag__.warn_name_set_on_empty_Forward = True
+    __diag__.warn_on_multiple_string_args_to_oneof = True
+__diag__.enable_all_warnings = _enable_all_warnings
+
+
+__all__ = ['__version__', '__versionTime__', '__author__', '__compat__', '__diag__',
+           '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',
+           'conditionAsParseAction', 're',
+           ]
+
+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)
+
+
+def conditionAsParseAction(fn, message=None, fatal=False):
+    msg = message if message is not None else "failed user-defined condition"
+    exc_type = ParseFatalException if fatal else ParseException
+    fn = _trim_arity(fn)
+
+    @wraps(fn)
+    def pa(s, l, t):
+        if not bool(fn(s, l, t)):
+            raise exc_type(s, l, msg)
+
+    return pa
+
+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):
+        if self.pstr:
+            if self.loc >= len(self.pstr):
+                foundstr = ', found end of text'
+            else:
+                foundstr = (', found %r' % self.pstr[self.loc:self.loc + 1]).replace(r'\\', '\\')
+        else:
+            foundstr = ''
+        return ("%s%s  (at char %d), (line:%d, col:%d)" %
+                   (self.msg, foundstr, 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', ''):
+                        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` ('-' 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.`` - 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 ""
+
+    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) # ->  ['sldkj', 'lsdkj', 'sldkj']
+
+            # Use asList() to create an actual list
+            result_list = result.asList()
+            print(type(result_list), result_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)) # ->  (['12', '/', '31', '/', '1999'], {'day': [('1999', 4)], 'year': [('12', 0)], 'month': [('31', 2)]})
+
+            result_dict = result.asDict()
+            print(type(result_dict), repr(result_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,
+                                                ""]
+
+        out += [nl, indent, ""]
+        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='', full=True, include_list=True, _depth=0):
+        """
+        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'
+        if include_list:
+            out.append(indent + _ustr(self.asList()))
+        else:
+            out.append('')
+
+        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=indent, full=full, include_list=include_list, _depth=_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=indent,
+                                                                    full=full,
+                                                                    include_list=include_list,
+                                                                    _depth=_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 `_ module.
+        Accepts additional positional or keyword args as defined for
+        `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())
+
+    @classmethod
+    def from_dict(cls, other, name=None):
+        """
+        Helper classmethod to construct a ParseResults from a dict, preserving the
+        name-value relations as results names. If an optional 'name' argument is
+        given, a nested ParseResults will be returned
+        """
+        def is_iterable(obj):
+            try:
+                iter(obj)
+            except Exception:
+                return False
+            else:
+                if PY_3:
+                    return not isinstance(obj, (str, bytes))
+                else:
+                    return not isinstance(obj, basestring)
+
+        ret = cls([])
+        for k, v in other.items():
+            if isinstance(v, Mapping):
+                ret += cls.from_dict(v, name=k)
+            else:
+                ret += cls([v], name=k, asList=is_iterable(v))
+        if name is not None:
+            ret = cls([ret], name=name)
+        return ret
+
+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 ```` 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 ```` 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:
+                        try:
+                            del tb
+                        except NameError:
+                            pass
+
+                if limit[0] <= maxargs:
+                    limit[0] += 1
+                    continue
+                raise
+
+    # copy func name to wrapper for sensible debug output
+    func_name = ""
+    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,  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
+
+    @classmethod
+    def _trim_traceback(cls, tb):
+        while tb.tb_next:
+            tb = tb.tb_next
+        return tb
+
+    def __init__(self, savelist=False):
+        self.parseAction = list()
+        self.failAction = None
+        # ~ self.name = ""  # 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 __diag__.enable_debug_on_named_expressions:
+            self.setDebug()
+        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")
+        """
+        return self._setResultsName(name, listAllMatches)
+
+    def _setResultsName(self, name, listAllMatches=False):
+        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
+                # this call to pdb.set_trace() is intentional, not a checkin error
+                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.
+
+        If None is passed as the parse action, all previously added parse actions for this
+        expression are cleared.
+
+        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 ```` 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]
+        """
+        if list(fns) == [None,]:
+            self.parseAction = []
+        else:
+            if not all(callable(fn) for fn in fns):
+                raise TypeError("parse actions must be callable")
+            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)
+        """
+        for fn in fns:
+            self.parseAction.append(conditionAsParseAction(fn, message=kwargs.get('message'),
+                                                           fatal=kwargs.get('fatal', False)))
+
+        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):
+        TRY, MATCH, FAIL = 0, 1, 2
+        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[TRY]:
+                self.debugActions[TRY](instring, loc, self)
+            try:
+                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)
+            except Exception as err:
+                # ~ print ("Exception raised:", err)
+                if self.debugActions[FAIL]:
+                    self.debugActions[FAIL](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 Exception as err:
+                    # ~ print "Exception raised in user parse action:", err
+                    if self.debugActions[FAIL]:
+                        self.debugActions[FAIL](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[MATCH]:
+                self.debugActions[MATCH](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.
+
+        Returns the parsed data as a :class:`ParseResults` object, which may be
+        accessed as a list, or as a dict or object with attributes if the given parser
+        includes results names.
+
+        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, clearing out pyparsing internal stack trace
+                if getattr(exc, '__traceback__', None) is not None:
+                    exc.__traceback__ = self._trim_traceback(exc.__traceback__)
+                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, clearing out pyparsing internal stack trace
+                if getattr(exc, '__traceback__', None) is not None:
+                    exc.__traceback__ = self._trim_traceback(exc.__traceback__)
+                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 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, clearing out pyparsing internal stack trace
+                if getattr(exc, '__traceback__', None) is not None:
+                    exc.__traceback__ = self._trim_traceback(exc.__traceback__)
+                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, clearing out pyparsing internal stack trace
+                if getattr(exc, '__traceback__', None) is not None:
+                    exc.__traceback__ = self._trim_traceback(exc.__traceback__)
+                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', '!']
+
+        ``...`` may be used as a parse expression as a short form of :class:`SkipTo`.
+
+            Literal('start') + ... + Literal('end')
+
+        is equivalent to:
+
+            Literal('start') + SkipTo('end')("_skipped*") + Literal('end')
+
+        Note that the skipped text is returned with '_skipped' as a results name,
+        and to support having multiple skips in the same parser, the value returned is
+        a list of all skipped text.
+        """
+        if other is Ellipsis:
+            return _PendingSkip(self)
+
+        if isinstance(other, basestring):
+            other = self._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 other is Ellipsis:
+            return SkipTo(self)("_skipped*") + self
+
+        if isinstance(other, basestring):
+            other = self._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 = self._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 = self._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 other is Ellipsis:
+            other = (0, None)
+        elif isinstance(other, tuple) and other[:1] == (Ellipsis,):
+            other = ((0, ) + other[1:] + (None,))[:2]
+
+        if isinstance(other, int):
+            minElements, optElements = other, 0
+        elif isinstance(other, tuple):
+            other = tuple(o if o is not Ellipsis else None for o in other)
+            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 other is Ellipsis:
+            return _PendingSkip(self, must_skip=True)
+
+        if isinstance(other, basestring):
+            other = self._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 = self._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 = self._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 = self._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 = self._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 = self._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 __iter__(self):
+        # must implement __iter__ to override legacy use of sequential access to __getitem__ to
+        # iterate over a sequence
+        raise TypeError('%r object is not iterable' % self.__class__.__name__)
+
+    def __getitem__(self, key):
+        """
+        use ``[]`` indexing notation as a short form for expression repetition:
+         - ``expr[n]`` is equivalent to ``expr*n``
+         - ``expr[m, n]`` is equivalent to ``expr*(m, n)``
+         - ``expr[n, ...]`` or ``expr[n,]`` is equivalent
+              to ``expr*n + ZeroOrMore(expr)``
+              (read as "at least n instances of ``expr``")
+         - ``expr[..., n]`` is equivalent to ``expr*(0, n)``
+              (read as "0 to n instances of ``expr``")
+         - ``expr[...]`` and ``expr[0, ...]`` are equivalent to ``ZeroOrMore(expr)``
+         - ``expr[1, ...]`` is equivalent to ``OneOrMore(expr)``
+         ``None`` may be used in place of ``...``.
+
+        Note that ``expr[..., n]`` and ``expr[m, n]``do not raise an exception
+        if more than ``n`` ``expr``s exist in the input stream.  If this behavior is
+        desired, then write ``expr[..., n] + ~expr``.
+       """
+
+        # convert single arg keys to tuples
+        try:
+            if isinstance(key, str):
+                key = (key,)
+            iter(key)
+        except TypeError:
+            key = (key, key)
+
+        if len(key) > 2:
+            warnings.warn("only 1 or 2 index arguments supported ({0}{1})".format(key[:5],
+                                                                                '... [{0}]'.format(len(key))
+                                                                                if len(key) > 5 else ''))
+
+        # clip to 2 elements
+        ret = self * tuple(key[:2])
+        return ret
+
+    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 ````s to spaces before parsing the input string.
+        Must be called before ``parseString`` when the input grammar contains elements that
+        match ```` 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  at loc (,)"``
+        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=None):
+        """
+        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, clearing out pyparsing internal stack trace
+                if getattr(exc, '__traceback__', None) is not None:
+                    exc.__traceback__ = self._trim_traceback(exc.__traceback__)
+                raise exc
+
+    def __eq__(self, other):
+        if self is other:
+            return True
+        elif isinstance(other, basestring):
+            return self.matches(other)
+        elif isinstance(other, ParserElement):
+            return vars(self) == vars(other)
+        return False
+
+    def __ne__(self, other):
+        return not (self == other)
+
+    def __hash__(self):
+        return 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,
+                 file=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
+         - file - (default=``None``) optional file-like object to which test output will be written;
+              if None, will default to ``sys.stdout``
+
+        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)
+        if file is None:
+            file = sys.stdout
+        print_ = file.write
+
+        allResults = []
+        comments = []
+        success = True
+        NL = Literal(r'\n').addParseAction(replaceWith('\n')).ignore(quotedString)
+        BOM = u'\ufeff'
+        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' + '\n'.join(comments) if comments else '', t]
+            comments = []
+            try:
+                # convert newline marks to actual newlines, and strip leading BOM if present
+                t = NL.transformString(t.lstrip(BOM))
+                result = self.parseString(t, parseAll=parseAll)
+            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
+            else:
+                success = success and not failureTests
+                if postParse is not None:
+                    try:
+                        pp_value = postParse(t, result)
+                        if pp_value is not None:
+                            if isinstance(pp_value, ParseResults):
+                                out.append(pp_value.dump())
+                            else:
+                                out.append(str(pp_value))
+                        else:
+                            out.append(result.dump())
+                    except Exception as e:
+                        out.append(result.dump(full=fullDump))
+                        out.append("{0} failed: {1}: {2}".format(postParse.__name__, type(e).__name__, e))
+                else:
+                    out.append(result.dump(full=fullDump))
+
+            if printResults:
+                if fullDump:
+                    out.append('')
+                print_('\n'.join(out))
+
+            allResults.append((t, result))
+
+        return success, allResults
+
+
+class _PendingSkip(ParserElement):
+    # internal placeholder class to hold a place were '...' is added to a parser element,
+    # once another ParserElement is added, this placeholder will be replaced with a SkipTo
+    def __init__(self, expr, must_skip=False):
+        super(_PendingSkip, self).__init__()
+        self.strRepr = str(expr + Empty()).replace('Empty', '...')
+        self.name = self.strRepr
+        self.anchor = expr
+        self.must_skip = must_skip
+
+    def __add__(self, other):
+        skipper = SkipTo(other).setName("...")("_skipped*")
+        if self.must_skip:
+            def must_skip(t):
+                if not t._skipped or t._skipped.asList() == ['']:
+                    del t[0]
+                    t.pop("_skipped", None)
+            def show_skip(t):
+                if t._skipped.asList()[-1:] == ['']:
+                    skipped = t.pop('_skipped')
+                    t['_skipped'] = 'missing <' + repr(self.anchor) + '>'
+            return (self.anchor + skipper().addParseAction(must_skip)
+                    | skipper().addParseAction(show_skip)) + other
+
+        return self.anchor + skipper + other
+
+    def __repr__(self):
+        return self.strRepr
+
+    def parseImpl(self, *args):
+        raise Exception("use of `...` expression without following SkipTo target expression")
+
+
+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: modify __class__ to select
+        # a parseImpl optimized for single-character check
+        if self.matchLen == 1 and type(self) is Literal:
+            self.__class__ = _SingleCharLiteral
+
+    def parseImpl(self, instring, loc, doActions=True):
+        if instring[loc] == self.firstMatchChar and instring.startswith(self.match, loc):
+            return loc + self.matchLen, self.match
+        raise ParseException(instring, loc, self.errmsg, self)
+
+class _SingleCharLiteral(Literal):
+    def parseImpl(self, instring, loc, doActions=True):
+        if instring[loc] == self.firstMatchChar:
+            return loc + 1, 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:
+                if ((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], 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'] = 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
+            else:
+                self.re_match = self.re.match
+                self.__class__ = _WordRegex
+
+    def parseImpl(self, instring, loc, doActions=True):
+        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 _WordRegex(Word):
+    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()
+        return loc, result.group()
+
+
+class Char(_WordRegex):
+    """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(''.join(self.initChars))
+        if asKeyword:
+            self.reString = r"\b%s\b" % self.reString
+        self.re = re.compile(self.reString)
+        self.re_match = self.re.match
+
+
+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 `_.
+    If the given regex contains named groups (defined using ``(?P...)``),
+    these will be preserved as named parse results.
+
+    If instead of the Python stdlib re module you wish to use a different RE module
+    (such as the `regex` module), you can replace it by either building your
+    Regex object with a compiled RE that was compiled using regex:
+
+    Example::
+
+        realnum = Regex(r"[+-]?\d+\.\d*")
+        date = Regex(r'(?P\d{4})-(?P\d\d?)-(?P\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})")
+
+        # use regex module instead of stdlib re module to construct a Regex using
+        # a compiled regular expression
+        import regex
+        parser = pp.Regex(regex.compile(r'[0-9]'))
+
+    """
+    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 `_ 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 hasattr(pattern, 'pattern') and hasattr(pattern, 'match'):
+            self.re = pattern
+            self.pattern = self.reString = pattern.pattern
+            self.flags = flags
+
+        else:
+            raise TypeError("Regex may only be constructed with a string or a compiled RE object")
+
+        self.re_match = self.re.match
+
+        self.name = _ustr(self)
+        self.errmsg = "Expected " + self.name
+        self.mayIndexError = False
+        self.mayReturnEmpty = self.re_match("") is not None
+        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) `_.
+
+        Example::
+
+            make_html = Regex(r"(\w+):(.*?):").sub(r"<\1>\2")
+            print(make_html.transformString("h1:main title:"))
+            # prints "

main title

" + """ + 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 + self.re_match = self.re.match + 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 = { + ' ' : '', + '\t': '', + '\n': '', + '\r': '', + '\f': '', + u'\u00A0': '', + u'\u1680': '', + u'\u180E': '', + u'\u2000': '', + u'\u2001': '', + u'\u2002': '', + u'\u2003': '', + u'\u2004': '', + u'\u2005': '', + u'\u2006': '', + u'\u2007': '', + u'\u2008': '', + u'\u2009': '', + u'\u200A': '', + u'\u200B': '', + u'\u202F': '', + u'\u205F': '', + u'\u3000': '', + } + 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 = [self._literalStringClass(exprs)] + elif isinstance(exprs, ParserElement): + self.exprs = [exprs] + elif isinstance(exprs, Iterable): + exprs = list(exprs) + # if sequence of strings provided, wrap with Literal + if any(isinstance(expr, basestring) for expr in exprs): + exprs = (self._literalStringClass(e) if isinstance(e, basestring) else e for e in exprs) + self.exprs = list(exprs) + else: + try: + self.exprs = list(exprs) + except TypeError: + self.exprs = [exprs] + self.callPreparse = False + + 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=None): + tmp = (validateTrace if validateTrace is not None else [])[:] + [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 + + def _setResultsName(self, name, listAllMatches=False): + if __diag__.warn_ungrouped_named_tokens_in_collection: + for e in self.exprs: + if isinstance(e, ParserElement) and e.resultsName: + warnings.warn("{0}: setting results name {1!r} on {2} expression " + "collides with {3!r} on contained expression".format("warn_ungrouped_named_tokens_in_collection", + name, + type(self).__name__, + e.resultsName), + stacklevel=3) + + return super(ParseExpression, self)._setResultsName(name, listAllMatches) + + +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): + exprs = list(exprs) + if exprs and Ellipsis in exprs: + tmp = [] + for i, expr in enumerate(exprs): + if expr is Ellipsis: + if i < len(exprs) - 1: + skipto_arg = (Empty() + exprs[i + 1]).exprs[-1] + tmp.append(SkipTo(skipto_arg)("_skipped*")) + else: + raise Exception("cannot construct And with sequence ending in ...") + else: + tmp.append(expr) + exprs[:] = tmp + 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): + # collapse any _PendingSkip's + if self.exprs: + if any(isinstance(e, ParseExpression) and e.exprs and isinstance(e.exprs[-1], _PendingSkip) + for e in self.exprs[:-1]): + for i, e in enumerate(self.exprs[:-1]): + if e is None: + continue + if (isinstance(e, ParseExpression) + and e.exprs and isinstance(e.exprs[-1], _PendingSkip)): + e.exprs[-1] = e.exprs[-1] + self.exprs[i + 1] + self.exprs[i + 1] = None + self.exprs = [e for e in self.exprs if e is not None] + + 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 = self._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: + # re-evaluate all matches in descending order of length of match, in case attached actions + # might change whether or how much they match of the input. + matches.sort(key=itemgetter(0), reverse=True) + + if not doActions: + # no further conditions or parse actions to change the selection of + # alternative, so the first match will be the best match + best_expr = matches[0][1] + return best_expr._parse(instring, loc, doActions) + + longest = -1, None + for loc1, expr1 in matches: + if loc1 <= longest[0]: + # already have a longer match than this one will deliver, we are done + return longest + + try: + loc2, toks = expr1._parse(instring, loc, doActions) + except ParseException as err: + err.__traceback__ = None + if err.loc > maxExcLoc: + maxException = err + maxExcLoc = err.loc + else: + if loc2 >= loc1: + return loc2, toks + # didn't match as much as before + elif loc2 > longest[0]: + longest = loc2, toks + + if longest != (-1, None): + return longest + + 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 = self._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) + + def _setResultsName(self, name, listAllMatches=False): + if (not __compat__.collect_all_And_tokens + and __diag__.warn_multiple_tokens_in_named_alternation): + if any(isinstance(e, And) for e in self.exprs): + warnings.warn("{0}: setting results name {1!r} on {2} expression " + "may only return a single token for an And alternative, " + "in future will return the full list of tokens".format( + "warn_multiple_tokens_in_named_alternation", name, type(self).__name__), + stacklevel=3) + + return super(Or, self)._setResultsName(name, listAllMatches) + + +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 = self._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) + + def _setResultsName(self, name, listAllMatches=False): + if (not __compat__.collect_all_And_tokens + and __diag__.warn_multiple_tokens_in_named_alternation): + if any(isinstance(e, And) for e in self.exprs): + warnings.warn("{0}: setting results name {1!r} on {2} expression " + "may only return a single token for an And alternative, " + "in future will return the full list of tokens".format( + "warn_multiple_tokens_in_named_alternation", name, type(self).__name__), + stacklevel=3) + + return super(MatchFirst, self)._setResultsName(name, listAllMatches) + + +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, Regex))] + 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(self._literalStringClass, Token): + expr = self._literalStringClass(expr) + else: + expr = self._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=None): + if validateTrace is None: + 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): + # by using self._expr.parse and deleting the contents of the returned ParseResults list + # we keep any named results that were defined in the FollowedBy expression + _, 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 + self.parseAction.append(lambda s, l, t: t.__delitem__(slice(None, None))) + + 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[max(0, loc - self.retreat):loc] + last_expr = ParseException(instring, loc, self.errmsg) + for offset in range(1, min(loc, self.retreat + 1)+1): + try: + # print('trying', offset, instring_slice, repr(instring_slice[loc - offset:])) + _, ret = test_expr._parse(instring_slice, len(instring_slice) - offset) + except ParseBaseException as pbe: + last_expr = pbe + else: + break + else: + raise last_expr + 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 = self._literalStringClass(ender) + self.stopOn(ender) + + def stopOn(self, ender): + if isinstance(ender, basestring): + ender = self._literalStringClass(ender) + self.not_ender = ~ender if ender is not None else None + return self + + 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 + + def _setResultsName(self, name, listAllMatches=False): + if __diag__.warn_ungrouped_named_tokens_in_collection: + for e in [self.expr] + getattr(self.expr, 'exprs', []): + if isinstance(e, ParserElement) and e.resultsName: + warnings.warn("{0}: setting results name {1!r} on {2} expression " + "collides with {3!r} on contained expression".format("warn_ungrouped_named_tokens_in_collection", + name, + type(self).__name__, + e.resultsName), + stacklevel=3) + + return super(_MultipleMatch, self)._setResultsName(name, listAllMatches) + + +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 "" + +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) + """ + __optionalNotMatched = _NullToken() + + 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 self.__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 = self._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 = self._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=None): + if validateTrace is None: + 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 + if self.strRepr is not None: + return self.strRepr + + # Avoid infinite recursion by setting a temporary strRepr + self.strRepr = ": ..." + + # Use the string representation of main expression. + retString = '...' + try: + if self.expr is not None: + retString = _ustr(self.expr)[:1000] + else: + retString = "None" + finally: + self.strRepr = self.__class__.__name__ + ": " + retString + return self.strRepr + + def copy(self): + if self.expr is not None: + return super(Forward, self).copy() + else: + ret = Forward() + ret <<= self + return ret + + def _setResultsName(self, name, listAllMatches=False): + if __diag__.warn_name_set_on_empty_Forward: + if self.expr is None: + warnings.warn("{0}: setting results name {0!r} on {1} expression " + "that has no contained expression".format("warn_name_set_on_empty_Forward", + name, + type(self).__name__), + stacklevel=3) + + return super(Forward, self)._setResultsName(name, listAllMatches) + +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:, , )"``. + 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'], {})) + < 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("< ['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, asKeyword=False): + """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 ``asKeyword=True``, or if + creating a :class:`Regex` raises an exception) + - asKeyword - (default=``False``) - enforce Keyword-style matching on the + generated expressions + + 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 isinstance(caseless, basestring): + warnings.warn("More than one string argument passed to oneOf, pass " + "choices as a list or space-delimited string", stacklevel=2) + + if caseless: + isequal = (lambda a, b: a.upper() == b.upper()) + masks = (lambda a, b: b.upper().startswith(a.upper())) + parseElementClass = CaselessKeyword if asKeyword else CaselessLiteral + else: + isequal = (lambda a, b: a == b) + masks = (lambda a, b: b.startswith(a)) + parseElementClass = Keyword if asKeyword else 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() + + if not asKeyword: + # if not producing keywords, need to reorder to take care to avoid masking + # longer choices with shorter ones + 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) + break + else: + i += 1 + + if not (caseless or asKeyword) 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 bold text normal text " + for tag in ("b", "i"): + opener, closer = makeHTMLTags(tag) + patt = originalTextFor(opener + SkipTo(closer) + closer) + print(patt.searchString(src)[0]) + + prints:: + + [' bold text '] + ['text'] + """ + 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 ```` 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` (). + + 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("", adjacent=False) + + openTag.setName("<%s>" % resname) + # add start 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("" % 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 = 'More info at the
pyparsing wiki page' + # 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
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 ```` or ``
``. + + 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 = ''' +
+ Some text +
1 4 0 1 0
+
1,3 2,3 1,1
+
this has no type
+
+ + ''' + 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 = ''' +
+ Some text +
1 4 0 1 0
+
1,3 2,3 1,1
+
this <div> has no class
+
+ + ''' + 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 + OneOrMore(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 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 in indentStack): + raise ParseException(s, l, "not an unindent") + if curCol < indentStack[-1]: + indentStack.pop() + + NL = OneOrMore(LineEnd().setWhitespaceChars("\t ").suppress(), stopOn=StringEnd()) + INDENT = (Empty() + Empty().setParseAction(checkSubIndent)).setName('INDENT') + PEER = Empty().setParseAction(checkPeerIndent).setName('') + UNDENT = Empty().setParseAction(checkUnindent).setName('UNINDENT') + if indent: + smExpr = Group(Optional(NL) + + INDENT + + OneOrMore(PEER + Group(blockStatementExpr) + Optional(NL), stopOn=StringEnd()) + + UNDENT) + else: + smExpr = Group(Optional(NL) + + OneOrMore(PEER + Group(blockStatementExpr) + Optional(NL), stopOn=StringEnd()) + + UNDENT) + 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' + '|'.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"").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`, :class:`reals`, + :class:`scientific notation`) + - common :class:`programming identifiers` + - network addresses (:class:`MAC`, + :class:`IPv4`, :class:`IPv6`) + - ISO8601 :class:`dates` and + :class:`datetime` + - :class:`UUID` + - :class:`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*|\.\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+\.\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\d{4})(?:-(?P\d\d)(?:-(?P\d\d))?)?').setName("ISO8601 date") + "ISO8601 date (``yyyy-mm-dd``)" + + iso8601_datetime = Regex(r'(?P\d{4})-(?P\d\d)-(?P\d\d)[T ](?P\d\d):(?P\d\d)(:(?P\d\d(\.\d*)?)?)?(?PZ|[+-]\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 = 'More info at the
pyparsing wiki page' + 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, u"العربية", pyparsing_unicode.Arabic) + setattr(pyparsing_unicode, u"中文", pyparsing_unicode.Chinese) + setattr(pyparsing_unicode, u"кириллица", pyparsing_unicode.Cyrillic) + setattr(pyparsing_unicode, u"Ελληνικά", pyparsing_unicode.Greek) + setattr(pyparsing_unicode, u"עִברִית", pyparsing_unicode.Hebrew) + setattr(pyparsing_unicode, u"日本語", pyparsing_unicode.Japanese) + setattr(pyparsing_unicode.Japanese, u"漢字", pyparsing_unicode.Japanese.Kanji) + setattr(pyparsing_unicode.Japanese, u"カタカナ", pyparsing_unicode.Japanese.Katakana) + setattr(pyparsing_unicode.Japanese, u"ひらがな", pyparsing_unicode.Japanese.Hiragana) + setattr(pyparsing_unicode, u"한국어", pyparsing_unicode.Korean) + setattr(pyparsing_unicode, u"ไทย", pyparsing_unicode.Thai) + setattr(pyparsing_unicode, u"देवनागरी", pyparsing_unicode.Devanagari) + + +class pyparsing_test: + """ + namespace class for classes useful in writing unit tests + """ + + class reset_pyparsing_context: + """ + Context manager to be used when writing unit tests that modify pyparsing config values: + - packrat parsing + - default whitespace characters. + - default keyword characters + - literal string auto-conversion class + - __diag__ settings + + Example: + with reset_pyparsing_context(): + # test that literals used to construct a grammar are automatically suppressed + ParserElement.inlineLiteralsUsing(Suppress) + + term = Word(alphas) | Word(nums) + group = Group('(' + term[...] + ')') + + # assert that the '()' characters are not included in the parsed tokens + self.assertParseAndCheckLisst(group, "(abc 123 def)", ['abc', '123', 'def']) + + # after exiting context manager, literals are converted to Literal expressions again + """ + + def __init__(self): + self._save_context = {} + + def save(self): + self._save_context["default_whitespace"] = ParserElement.DEFAULT_WHITE_CHARS + self._save_context["default_keyword_chars"] = Keyword.DEFAULT_KEYWORD_CHARS + self._save_context[ + "literal_string_class" + ] = ParserElement._literalStringClass + self._save_context["packrat_enabled"] = ParserElement._packratEnabled + self._save_context["packrat_parse"] = ParserElement._parse + self._save_context["__diag__"] = { + name: getattr(__diag__, name) for name in __diag__._all_names + } + self._save_context["__compat__"] = { + "collect_all_And_tokens": __compat__.collect_all_And_tokens + } + return self + + def restore(self): + # reset pyparsing global state + if ( + ParserElement.DEFAULT_WHITE_CHARS + != self._save_context["default_whitespace"] + ): + ParserElement.setDefaultWhitespaceChars( + self._save_context["default_whitespace"] + ) + Keyword.DEFAULT_KEYWORD_CHARS = self._save_context["default_keyword_chars"] + ParserElement.inlineLiteralsUsing( + self._save_context["literal_string_class"] + ) + for name, value in self._save_context["__diag__"].items(): + setattr(__diag__, name, value) + ParserElement._packratEnabled = self._save_context["packrat_enabled"] + ParserElement._parse = self._save_context["packrat_parse"] + __compat__.collect_all_And_tokens = self._save_context["__compat__"] + + def __enter__(self): + return self.save() + + def __exit__(self, *args): + return self.restore() + + class TestParseResultsAsserts: + """ + A mixin class to add parse results assertion methods to normal unittest.TestCase classes. + """ + def assertParseResultsEquals( + self, result, expected_list=None, expected_dict=None, msg=None + ): + """ + Unit test assertion to compare a ParseResults object with an optional expected_list, + and compare any defined results names with an optional expected_dict. + """ + if expected_list is not None: + self.assertEqual(expected_list, result.asList(), msg=msg) + if expected_dict is not None: + self.assertEqual(expected_dict, result.asDict(), msg=msg) + + def assertParseAndCheckList( + self, expr, test_string, expected_list, msg=None, verbose=True + ): + """ + Convenience wrapper assert to test a parser element and input string, and assert that + the resulting ParseResults.asList() is equal to the expected_list. + """ + result = expr.parseString(test_string, parseAll=True) + if verbose: + print(result.dump()) + self.assertParseResultsEquals(result, expected_list=expected_list, msg=msg) + + def assertParseAndCheckDict( + self, expr, test_string, expected_dict, msg=None, verbose=True + ): + """ + Convenience wrapper assert to test a parser element and input string, and assert that + the resulting ParseResults.asDict() is equal to the expected_dict. + """ + result = expr.parseString(test_string, parseAll=True) + if verbose: + print(result.dump()) + self.assertParseResultsEquals(result, expected_dict=expected_dict, msg=msg) + + def assertRunTestResults( + self, run_tests_report, expected_parse_results=None, msg=None + ): + """ + Unit test assertion to evaluate output of ParserElement.runTests(). If a list of + list-dict tuples is given as the expected_parse_results argument, then these are zipped + with the report tuples returned by runTests and evaluated using assertParseResultsEquals. + Finally, asserts that the overall runTests() success value is True. + + :param run_tests_report: tuple(bool, [tuple(str, ParseResults or Exception)]) returned from runTests + :param expected_parse_results (optional): [tuple(str, list, dict, Exception)] + """ + run_test_success, run_test_results = run_tests_report + + if expected_parse_results is not None: + merged = [ + (rpt[0], rpt[1], expected) + for rpt, expected in zip(run_test_results, expected_parse_results) + ] + for test_string, result, expected in merged: + # expected should be a tuple containing a list and/or a dict or an exception, + # and optional failure message string + # an empty tuple will skip any result validation + fail_msg = next( + (exp for exp in expected if isinstance(exp, str)), None + ) + expected_exception = next( + ( + exp + for exp in expected + if isinstance(exp, type) and issubclass(exp, Exception) + ), + None, + ) + if expected_exception is not None: + with self.assertRaises( + expected_exception=expected_exception, msg=fail_msg or msg + ): + if isinstance(result, Exception): + raise result + else: + expected_list = next( + (exp for exp in expected if isinstance(exp, list)), None + ) + expected_dict = next( + (exp for exp in expected if isinstance(exp, dict)), None + ) + if (expected_list, expected_dict) != (None, None): + self.assertParseResultsEquals( + result, + expected_list=expected_list, + expected_dict=expected_dict, + msg=fail_msg or msg, + ) + else: + # warning here maybe? + print("no validation for {!r}".format(test_string)) + + # do this last, in case some specific test results can be reported instead + self.assertTrue( + run_test_success, msg=msg if msg is not None else "failed runTests" + ) + + @contextmanager + def assertRaisesParseException(self, exc_type=ParseException, msg=None): + with self.assertRaises(exc_type, msg=msg): + yield + + +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/venv/lib/python3.8/site-packages/pip/_vendor/retrying.py b/venv/lib/python3.8/site-packages/pip/_vendor/retrying.py new file mode 100644 index 0000000..6d1e627 --- /dev/null +++ b/venv/lib/python3.8/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/venv/lib/python3.8/site-packages/pip/_vendor/six.py b/venv/lib/python3.8/site-packages/pip/_vendor/six.py new file mode 100644 index 0000000..5fe9f8e --- /dev/null +++ b/venv/lib/python3.8/site-packages/pip/_vendor/six.py @@ -0,0 +1,980 @@ +# Copyright (c) 2010-2020 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 " +__version__ = "1.14.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("collections_abc", "collections", "collections.abc" if sys.version_info >= (3, 3) else "collections"), + MovedModule("copyreg", "copy_reg"), + MovedModule("dbm_gnu", "gdbm", "dbm.gnu"), + MovedModule("dbm_ndbm", "dbm", "dbm.ndbm"), + MovedModule("_dummy_thread", "dummy_thread", "_dummy_thread" if sys.version_info < (3, 9) else "_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 + del io + _assertCountEqual = "assertCountEqual" + if sys.version_info[1] <= 1: + _assertRaisesRegex = "assertRaisesRegexp" + _assertRegex = "assertRegexpMatches" + _assertNotRegex = "assertNotRegexpMatches" + else: + _assertRaisesRegex = "assertRaisesRegex" + _assertRegex = "assertRegex" + _assertNotRegex = "assertNotRegex" +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" + _assertNotRegex = "assertNotRegexpMatches" +_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) + + +def assertNotRegex(self, *args, **kwargs): + return getattr(self, _assertNotRegex)(*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,): + 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): + # This does exactly the same what the :func:`py3:functools.update_wrapper` + # function does on Python versions after 3.2. It sets the ``__wrapped__`` + # attribute on ``wrapper`` object and it doesn't raise an error if any of + # the attributes mentioned in ``assigned`` and ``updated`` are missing on + # ``wrapped`` object. + def _update_wrapper(wrapper, wrapped, + assigned=functools.WRAPPER_ASSIGNMENTS, + updated=functools.WRAPPER_UPDATES): + for attr in assigned: + try: + value = getattr(wrapped, attr) + except AttributeError: + continue + else: + setattr(wrapper, attr, value) + for attr in updated: + getattr(wrapper, attr).update(getattr(wrapped, attr, {})) + wrapper.__wrapped__ = wrapped + return wrapper + _update_wrapper.__doc__ = functools.update_wrapper.__doc__ + + def wraps(wrapped, assigned=functools.WRAPPER_ASSIGNMENTS, + updated=functools.WRAPPER_UPDATES): + return functools.partial(_update_wrapper, wrapped=wrapped, + assigned=assigned, updated=updated) + wraps.__doc__ = functools.wraps.__doc__ + +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): + if sys.version_info[:2] >= (3, 7): + # This version introduced PEP 560 that requires a bit + # of extra care (we mimic what is done by __build_class__). + resolved_bases = types.resolve_bases(bases) + if resolved_bases is not bases: + d['__orig_bases__'] = bases + else: + resolved_bases = bases + return meta(name, resolved_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 class decorator that defines __unicode__ and __str__ methods under Python 2. + Under Python 3 it does nothing. + + To support Python 2 and 3 with a single code base, define a __str__ method + returning text and apply this decorator to the class. + """ + if PY2: + if '__str__' not in klass.__dict__: + raise ValueError("@python_2_unicode_compatible cannot be applied " + "to %s because it doesn't define __str__()." % + klass.__name__) + klass.__unicode__ = klass.__str__ + klass.__str__ = lambda self: self.__unicode__().encode('utf-8') + return klass + + +# Complete the moves implementation. +# This code is at the end of this module to speed up module loading. +# Turn this module into a package. +__path__ = [] # required for PEP 302 and PEP 451 +__package__ = __name__ # see PEP 366 @ReservedAssignment +if globals().get("__spec__") is not None: + __spec__.submodule_search_locations = [] # PEP 451 @UndefinedVariable +# Remove other six meta path importers, since they cause problems. This can +# happen if six is removed from sys.modules and then reloaded. (Setuptools does +# this for some reason.) +if sys.meta_path: + for i, importer in enumerate(sys.meta_path): + # Here's some real nastiness: Another "instance" of the six module might + # be floating around. Therefore, we can't use isinstance() to check for + # the six meta path importer, since the other six instance will have + # inserted an importer with different class. + if (type(importer).__name__ == "_SixMetaPathImporter" and + importer.name == __name__): + del sys.meta_path[i] + break + del i, importer +# Finally, add the importer to the meta path import hook. +sys.meta_path.append(_importer) diff --git a/venv/lib/python3.8/site-packages/pip/_vendor/toml.py b/venv/lib/python3.8/site-packages/pip/_vendor/toml.py new file mode 100644 index 0000000..dac3988 --- /dev/null +++ b/venv/lib/python3.8/site-packages/pip/_vendor/toml.py @@ -0,0 +1,1039 @@ +"""Python module which parses and emits TOML. + +Released under the MIT license. +""" +import re +import io +import datetime +from os import linesep +import sys + +__version__ = "0.9.6" +_spec_ = "0.4.0" + + +class TomlDecodeError(Exception): + """Base toml Exception / Error.""" + pass + + +class TomlTz(datetime.tzinfo): + def __init__(self, toml_offset): + if toml_offset == "Z": + self._raw_offset = "+00:00" + else: + self._raw_offset = toml_offset + self._sign = -1 if self._raw_offset[0] == '-' else 1 + self._hours = int(self._raw_offset[1:3]) + self._minutes = int(self._raw_offset[4:6]) + + def tzname(self, dt): + return "UTC" + self._raw_offset + + def utcoffset(self, dt): + return self._sign * datetime.timedelta(hours=self._hours, + minutes=self._minutes) + + def dst(self, dt): + return datetime.timedelta(0) + + +class InlineTableDict(object): + """Sentinel subclass of dict for inline tables.""" + + +def _get_empty_inline_table(_dict): + class DynamicInlineTableDict(_dict, InlineTableDict): + """Concrete sentinel subclass for inline tables. + It is a subclass of _dict which is passed in dynamically at load time + It is also a subclass of InlineTableDict + """ + + return DynamicInlineTableDict() + + +try: + _range = xrange +except NameError: + unicode = str + _range = range + basestring = str + unichr = chr + +try: + FNFError = FileNotFoundError +except NameError: + FNFError = IOError + + +def load(f, _dict=dict): + """Parses named file or files as toml and returns a dictionary + + Args: + f: Path to the file to open, array of files to read into single dict + or a file descriptor + _dict: (optional) Specifies the class of the returned toml dictionary + + Returns: + Parsed toml file represented as a dictionary + + Raises: + TypeError -- When f is invalid type + TomlDecodeError: Error while decoding toml + IOError / FileNotFoundError -- When an array with no valid (existing) + (Python 2 / Python 3) file paths is passed + """ + + if isinstance(f, basestring): + with io.open(f, encoding='utf-8') as ffile: + return loads(ffile.read(), _dict) + elif isinstance(f, list): + from os import path as op + from warnings import warn + if not [path for path in f if op.exists(path)]: + error_msg = "Load expects a list to contain filenames only." + error_msg += linesep + error_msg += ("The list needs to contain the path of at least one " + "existing file.") + raise FNFError(error_msg) + d = _dict() + for l in f: + if op.exists(l): + d.update(load(l)) + else: + warn("Non-existent filename in list with at least one valid " + "filename") + return d + else: + try: + return loads(f.read(), _dict) + except AttributeError: + raise TypeError("You can only load a file descriptor, filename or " + "list") + + +_groupname_re = re.compile(r'^[A-Za-z0-9_-]+$') + + +def loads(s, _dict=dict): + """Parses string as toml + + Args: + s: String to be parsed + _dict: (optional) Specifies the class of the returned toml dictionary + + Returns: + Parsed toml file represented as a dictionary + + Raises: + TypeError: When a non-string is passed + TomlDecodeError: Error while decoding toml + """ + + implicitgroups = [] + retval = _dict() + currentlevel = retval + if not isinstance(s, basestring): + raise TypeError("Expecting something like a string") + + if not isinstance(s, unicode): + s = s.decode('utf8') + + sl = list(s) + openarr = 0 + openstring = False + openstrchar = "" + multilinestr = False + arrayoftables = False + beginline = True + keygroup = False + keyname = 0 + for i, item in enumerate(sl): + if item == '\r' and sl[i + 1] == '\n': + sl[i] = ' ' + continue + if keyname: + if item == '\n': + raise TomlDecodeError("Key name found without value." + " Reached end of line.") + if openstring: + if item == openstrchar: + keyname = 2 + openstring = False + openstrchar = "" + continue + elif keyname == 1: + if item.isspace(): + keyname = 2 + continue + elif item.isalnum() or item == '_' or item == '-': + continue + elif keyname == 2 and item.isspace(): + continue + if item == '=': + keyname = 0 + else: + raise TomlDecodeError("Found invalid character in key name: '" + + item + "'. Try quoting the key name.") + if item == "'" and openstrchar != '"': + k = 1 + try: + while sl[i - k] == "'": + k += 1 + if k == 3: + break + except IndexError: + pass + if k == 3: + multilinestr = not multilinestr + openstring = multilinestr + else: + openstring = not openstring + if openstring: + openstrchar = "'" + else: + openstrchar = "" + if item == '"' and openstrchar != "'": + oddbackslash = False + k = 1 + tripquote = False + try: + while sl[i - k] == '"': + k += 1 + if k == 3: + tripquote = True + break + if k == 1 or (k == 3 and tripquote): + while sl[i - k] == '\\': + oddbackslash = not oddbackslash + k += 1 + except IndexError: + pass + if not oddbackslash: + if tripquote: + multilinestr = not multilinestr + openstring = multilinestr + else: + openstring = not openstring + if openstring: + openstrchar = '"' + else: + openstrchar = "" + if item == '#' and (not openstring and not keygroup and + not arrayoftables): + j = i + try: + while sl[j] != '\n': + sl[j] = ' ' + j += 1 + except IndexError: + break + if item == '[' and (not openstring and not keygroup and + not arrayoftables): + if beginline: + if len(sl) > i + 1 and sl[i + 1] == '[': + arrayoftables = True + else: + keygroup = True + else: + openarr += 1 + if item == ']' and not openstring: + if keygroup: + keygroup = False + elif arrayoftables: + if sl[i - 1] == ']': + arrayoftables = False + else: + openarr -= 1 + if item == '\n': + if openstring or multilinestr: + if not multilinestr: + raise TomlDecodeError("Unbalanced quotes") + if ((sl[i - 1] == "'" or sl[i - 1] == '"') and ( + sl[i - 2] == sl[i - 1])): + sl[i] = sl[i - 1] + if sl[i - 3] == sl[i - 1]: + sl[i - 3] = ' ' + elif openarr: + sl[i] = ' ' + else: + beginline = True + elif beginline and sl[i] != ' ' and sl[i] != '\t': + beginline = False + if not keygroup and not arrayoftables: + if sl[i] == '=': + raise TomlDecodeError("Found empty keyname. ") + keyname = 1 + s = ''.join(sl) + s = s.split('\n') + multikey = None + multilinestr = "" + multibackslash = False + for line in s: + if not multilinestr or multibackslash or '\n' not in multilinestr: + line = line.strip() + if line == "" and (not multikey or multibackslash): + continue + if multikey: + if multibackslash: + multilinestr += line + else: + multilinestr += line + multibackslash = False + if len(line) > 2 and (line[-1] == multilinestr[0] and + line[-2] == multilinestr[0] and + line[-3] == multilinestr[0]): + try: + value, vtype = _load_value(multilinestr, _dict) + except ValueError as err: + raise TomlDecodeError(str(err)) + currentlevel[multikey] = value + multikey = None + multilinestr = "" + else: + k = len(multilinestr) - 1 + while k > -1 and multilinestr[k] == '\\': + multibackslash = not multibackslash + k -= 1 + if multibackslash: + multilinestr = multilinestr[:-1] + else: + multilinestr += "\n" + continue + if line[0] == '[': + arrayoftables = False + if len(line) == 1: + raise TomlDecodeError("Opening key group bracket on line by " + "itself.") + if line[1] == '[': + arrayoftables = True + line = line[2:] + splitstr = ']]' + else: + line = line[1:] + splitstr = ']' + i = 1 + quotesplits = _get_split_on_quotes(line) + quoted = False + for quotesplit in quotesplits: + if not quoted and splitstr in quotesplit: + break + i += quotesplit.count(splitstr) + quoted = not quoted + line = line.split(splitstr, i) + if len(line) < i + 1 or line[-1].strip() != "": + raise TomlDecodeError("Key group not on a line by itself.") + groups = splitstr.join(line[:-1]).split('.') + i = 0 + while i < len(groups): + groups[i] = groups[i].strip() + if len(groups[i]) > 0 and (groups[i][0] == '"' or + groups[i][0] == "'"): + groupstr = groups[i] + j = i + 1 + while not groupstr[0] == groupstr[-1]: + j += 1 + if j > len(groups) + 2: + raise TomlDecodeError("Invalid group name '" + + groupstr + "' Something " + + "went wrong.") + groupstr = '.'.join(groups[i:j]).strip() + groups[i] = groupstr[1:-1] + groups[i + 1:j] = [] + else: + if not _groupname_re.match(groups[i]): + raise TomlDecodeError("Invalid group name '" + + groups[i] + "'. Try quoting it.") + i += 1 + currentlevel = retval + for i in _range(len(groups)): + group = groups[i] + if group == "": + raise TomlDecodeError("Can't have a keygroup with an empty " + "name") + try: + currentlevel[group] + if i == len(groups) - 1: + if group in implicitgroups: + implicitgroups.remove(group) + if arrayoftables: + raise TomlDecodeError("An implicitly defined " + "table can't be an array") + elif arrayoftables: + currentlevel[group].append(_dict()) + else: + raise TomlDecodeError("What? " + group + + " already exists?" + + str(currentlevel)) + except TypeError: + currentlevel = currentlevel[-1] + try: + currentlevel[group] + except KeyError: + currentlevel[group] = _dict() + if i == len(groups) - 1 and arrayoftables: + currentlevel[group] = [_dict()] + except KeyError: + if i != len(groups) - 1: + implicitgroups.append(group) + currentlevel[group] = _dict() + if i == len(groups) - 1 and arrayoftables: + currentlevel[group] = [_dict()] + currentlevel = currentlevel[group] + if arrayoftables: + try: + currentlevel = currentlevel[-1] + except KeyError: + pass + elif line[0] == "{": + if line[-1] != "}": + raise TomlDecodeError("Line breaks are not allowed in inline" + "objects") + try: + _load_inline_object(line, currentlevel, _dict, multikey, + multibackslash) + except ValueError as err: + raise TomlDecodeError(str(err)) + elif "=" in line: + try: + ret = _load_line(line, currentlevel, _dict, multikey, + multibackslash) + except ValueError as err: + raise TomlDecodeError(str(err)) + if ret is not None: + multikey, multilinestr, multibackslash = ret + return retval + + +def _load_inline_object(line, currentlevel, _dict, multikey=False, + multibackslash=False): + candidate_groups = line[1:-1].split(",") + groups = [] + if len(candidate_groups) == 1 and not candidate_groups[0].strip(): + candidate_groups.pop() + while len(candidate_groups) > 0: + candidate_group = candidate_groups.pop(0) + try: + _, value = candidate_group.split('=', 1) + except ValueError: + raise ValueError("Invalid inline table encountered") + value = value.strip() + if ((value[0] == value[-1] and value[0] in ('"', "'")) or ( + value[0] in '-0123456789' or + value in ('true', 'false') or + (value[0] == "[" and value[-1] == "]") or + (value[0] == '{' and value[-1] == '}'))): + groups.append(candidate_group) + elif len(candidate_groups) > 0: + candidate_groups[0] = candidate_group + "," + candidate_groups[0] + else: + raise ValueError("Invalid inline table value encountered") + for group in groups: + status = _load_line(group, currentlevel, _dict, multikey, + multibackslash) + if status is not None: + break + + +# Matches a TOML number, which allows underscores for readability +_number_with_underscores = re.compile('([0-9])(_([0-9]))*') + + +def _strictly_valid_num(n): + n = n.strip() + if not n: + return False + if n[0] == '_': + return False + if n[-1] == '_': + return False + if "_." in n or "._" in n: + return False + if len(n) == 1: + return True + if n[0] == '0' and n[1] != '.': + return False + if n[0] == '+' or n[0] == '-': + n = n[1:] + if n[0] == '0' and n[1] != '.': + return False + if '__' in n: + return False + return True + + +def _get_split_on_quotes(line): + doublequotesplits = line.split('"') + quoted = False + quotesplits = [] + if len(doublequotesplits) > 1 and "'" in doublequotesplits[0]: + singlequotesplits = doublequotesplits[0].split("'") + doublequotesplits = doublequotesplits[1:] + while len(singlequotesplits) % 2 == 0 and len(doublequotesplits): + singlequotesplits[-1] += '"' + doublequotesplits[0] + doublequotesplits = doublequotesplits[1:] + if "'" in singlequotesplits[-1]: + singlequotesplits = (singlequotesplits[:-1] + + singlequotesplits[-1].split("'")) + quotesplits += singlequotesplits + for doublequotesplit in doublequotesplits: + if quoted: + quotesplits.append(doublequotesplit) + else: + quotesplits += doublequotesplit.split("'") + quoted = not quoted + return quotesplits + + +def _load_line(line, currentlevel, _dict, multikey, multibackslash): + i = 1 + quotesplits = _get_split_on_quotes(line) + quoted = False + for quotesplit in quotesplits: + if not quoted and '=' in quotesplit: + break + i += quotesplit.count('=') + quoted = not quoted + pair = line.split('=', i) + strictly_valid = _strictly_valid_num(pair[-1]) + if _number_with_underscores.match(pair[-1]): + pair[-1] = pair[-1].replace('_', '') + while len(pair[-1]) and (pair[-1][0] != ' ' and pair[-1][0] != '\t' and + pair[-1][0] != "'" and pair[-1][0] != '"' and + pair[-1][0] != '[' and pair[-1][0] != '{' and + pair[-1] != 'true' and pair[-1] != 'false'): + try: + float(pair[-1]) + break + except ValueError: + pass + if _load_date(pair[-1]) is not None: + break + i += 1 + prev_val = pair[-1] + pair = line.split('=', i) + if prev_val == pair[-1]: + raise ValueError("Invalid date or number") + if strictly_valid: + strictly_valid = _strictly_valid_num(pair[-1]) + pair = ['='.join(pair[:-1]).strip(), pair[-1].strip()] + if (pair[0][0] == '"' or pair[0][0] == "'") and \ + (pair[0][-1] == '"' or pair[0][-1] == "'"): + pair[0] = pair[0][1:-1] + if len(pair[1]) > 2 and ((pair[1][0] == '"' or pair[1][0] == "'") and + pair[1][1] == pair[1][0] and + pair[1][2] == pair[1][0] and + not (len(pair[1]) > 5 and + pair[1][-1] == pair[1][0] and + pair[1][-2] == pair[1][0] and + pair[1][-3] == pair[1][0])): + k = len(pair[1]) - 1 + while k > -1 and pair[1][k] == '\\': + multibackslash = not multibackslash + k -= 1 + if multibackslash: + multilinestr = pair[1][:-1] + else: + multilinestr = pair[1] + "\n" + multikey = pair[0] + else: + value, vtype = _load_value(pair[1], _dict, strictly_valid) + try: + currentlevel[pair[0]] + raise ValueError("Duplicate keys!") + except KeyError: + if multikey: + return multikey, multilinestr, multibackslash + else: + currentlevel[pair[0]] = value + + +def _load_date(val): + microsecond = 0 + tz = None + try: + if len(val) > 19: + if val[19] == '.': + if val[-1].upper() == 'Z': + subsecondval = val[20:-1] + tzval = "Z" + else: + subsecondvalandtz = val[20:] + if '+' in subsecondvalandtz: + splitpoint = subsecondvalandtz.index('+') + subsecondval = subsecondvalandtz[:splitpoint] + tzval = subsecondvalandtz[splitpoint:] + elif '-' in subsecondvalandtz: + splitpoint = subsecondvalandtz.index('-') + subsecondval = subsecondvalandtz[:splitpoint] + tzval = subsecondvalandtz[splitpoint:] + tz = TomlTz(tzval) + microsecond = int(int(subsecondval) * + (10 ** (6 - len(subsecondval)))) + else: + tz = TomlTz(val[19:]) + except ValueError: + tz = None + if "-" not in val[1:]: + return None + try: + d = datetime.datetime( + int(val[:4]), int(val[5:7]), + int(val[8:10]), int(val[11:13]), + int(val[14:16]), int(val[17:19]), microsecond, tz) + except ValueError: + return None + return d + + +def _load_unicode_escapes(v, hexbytes, prefix): + skip = False + i = len(v) - 1 + while i > -1 and v[i] == '\\': + skip = not skip + i -= 1 + for hx in hexbytes: + if skip: + skip = False + i = len(hx) - 1 + while i > -1 and hx[i] == '\\': + skip = not skip + i -= 1 + v += prefix + v += hx + continue + hxb = "" + i = 0 + hxblen = 4 + if prefix == "\\U": + hxblen = 8 + hxb = ''.join(hx[i:i + hxblen]).lower() + if hxb.strip('0123456789abcdef'): + raise ValueError("Invalid escape sequence: " + hxb) + if hxb[0] == "d" and hxb[1].strip('01234567'): + raise ValueError("Invalid escape sequence: " + hxb + + ". Only scalar unicode points are allowed.") + v += unichr(int(hxb, 16)) + v += unicode(hx[len(hxb):]) + return v + + +# Unescape TOML string values. + +# content after the \ +_escapes = ['0', 'b', 'f', 'n', 'r', 't', '"'] +# What it should be replaced by +_escapedchars = ['\0', '\b', '\f', '\n', '\r', '\t', '\"'] +# Used for substitution +_escape_to_escapedchars = dict(zip(_escapes, _escapedchars)) + + +def _unescape(v): + """Unescape characters in a TOML string.""" + i = 0 + backslash = False + while i < len(v): + if backslash: + backslash = False + if v[i] in _escapes: + v = v[:i - 1] + _escape_to_escapedchars[v[i]] + v[i + 1:] + elif v[i] == '\\': + v = v[:i - 1] + v[i:] + elif v[i] == 'u' or v[i] == 'U': + i += 1 + else: + raise ValueError("Reserved escape sequence used") + continue + elif v[i] == '\\': + backslash = True + i += 1 + return v + + +def _load_value(v, _dict, strictly_valid=True): + if not v: + raise ValueError("Empty value is invalid") + if v == 'true': + return (True, "bool") + elif v == 'false': + return (False, "bool") + elif v[0] == '"': + testv = v[1:].split('"') + triplequote = False + triplequotecount = 0 + if len(testv) > 1 and testv[0] == '' and testv[1] == '': + testv = testv[2:] + triplequote = True + closed = False + for tv in testv: + if tv == '': + if triplequote: + triplequotecount += 1 + else: + closed = True + else: + oddbackslash = False + try: + i = -1 + j = tv[i] + while j == '\\': + oddbackslash = not oddbackslash + i -= 1 + j = tv[i] + except IndexError: + pass + if not oddbackslash: + if closed: + raise ValueError("Stuff after closed string. WTF?") + else: + if not triplequote or triplequotecount > 1: + closed = True + else: + triplequotecount = 0 + escapeseqs = v.split('\\')[1:] + backslash = False + for i in escapeseqs: + if i == '': + backslash = not backslash + else: + if i[0] not in _escapes and (i[0] != 'u' and i[0] != 'U' and + not backslash): + raise ValueError("Reserved escape sequence used") + if backslash: + backslash = False + for prefix in ["\\u", "\\U"]: + if prefix in v: + hexbytes = v.split(prefix) + v = _load_unicode_escapes(hexbytes[0], hexbytes[1:], prefix) + v = _unescape(v) + if len(v) > 1 and v[1] == '"' and (len(v) < 3 or v[1] == v[2]): + v = v[2:-2] + return (v[1:-1], "str") + elif v[0] == "'": + if v[1] == "'" and (len(v) < 3 or v[1] == v[2]): + v = v[2:-2] + return (v[1:-1], "str") + elif v[0] == '[': + return (_load_array(v, _dict), "array") + elif v[0] == '{': + inline_object = _get_empty_inline_table(_dict) + _load_inline_object(v, inline_object, _dict) + return (inline_object, "inline_object") + else: + parsed_date = _load_date(v) + if parsed_date is not None: + return (parsed_date, "date") + if not strictly_valid: + raise ValueError("Weirdness with leading zeroes or " + "underscores in your number.") + itype = "int" + neg = False + if v[0] == '-': + neg = True + v = v[1:] + elif v[0] == '+': + v = v[1:] + v = v.replace('_', '') + if '.' in v or 'e' in v or 'E' in v: + if '.' in v and v.split('.', 1)[1] == '': + raise ValueError("This float is missing digits after " + "the point") + if v[0] not in '0123456789': + raise ValueError("This float doesn't have a leading digit") + v = float(v) + itype = "float" + else: + v = int(v) + if neg: + return (0 - v, itype) + return (v, itype) + + +def _bounded_string(s): + if len(s) == 0: + return True + if s[-1] != s[0]: + return False + i = -2 + backslash = False + while len(s) + i > 0: + if s[i] == "\\": + backslash = not backslash + i -= 1 + else: + break + return not backslash + + +def _load_array(a, _dict): + atype = None + retval = [] + a = a.strip() + if '[' not in a[1:-1] or "" != a[1:-1].split('[')[0].strip(): + strarray = False + tmpa = a[1:-1].strip() + if tmpa != '' and (tmpa[0] == '"' or tmpa[0] == "'"): + strarray = True + if not a[1:-1].strip().startswith('{'): + a = a[1:-1].split(',') + else: + # a is an inline object, we must find the matching parenthesis + # to define groups + new_a = [] + start_group_index = 1 + end_group_index = 2 + in_str = False + while end_group_index < len(a[1:]): + if a[end_group_index] == '"' or a[end_group_index] == "'": + if in_str: + backslash_index = end_group_index - 1 + while (backslash_index > -1 and + a[backslash_index] == '\\'): + in_str = not in_str + backslash_index -= 1 + in_str = not in_str + if in_str or a[end_group_index] != '}': + end_group_index += 1 + continue + + # Increase end_group_index by 1 to get the closing bracket + end_group_index += 1 + new_a.append(a[start_group_index:end_group_index]) + + # The next start index is at least after the closing bracket, a + # closing bracket can be followed by a comma since we are in + # an array. + start_group_index = end_group_index + 1 + while (start_group_index < len(a[1:]) and + a[start_group_index] != '{'): + start_group_index += 1 + end_group_index = start_group_index + 1 + a = new_a + b = 0 + if strarray: + while b < len(a) - 1: + ab = a[b].strip() + while (not _bounded_string(ab) or + (len(ab) > 2 and + ab[0] == ab[1] == ab[2] and + ab[-2] != ab[0] and + ab[-3] != ab[0])): + a[b] = a[b] + ',' + a[b + 1] + ab = a[b].strip() + if b < len(a) - 2: + a = a[:b + 1] + a[b + 2:] + else: + a = a[:b + 1] + b += 1 + else: + al = list(a[1:-1]) + a = [] + openarr = 0 + j = 0 + for i in _range(len(al)): + if al[i] == '[': + openarr += 1 + elif al[i] == ']': + openarr -= 1 + elif al[i] == ',' and not openarr: + a.append(''.join(al[j:i])) + j = i + 1 + a.append(''.join(al[j:])) + for i in _range(len(a)): + a[i] = a[i].strip() + if a[i] != '': + nval, ntype = _load_value(a[i], _dict) + if atype: + if ntype != atype: + raise ValueError("Not a homogeneous array") + else: + atype = ntype + retval.append(nval) + return retval + + +def dump(o, f): + """Writes out dict as toml to a file + + Args: + o: Object to dump into toml + f: File descriptor where the toml should be stored + + Returns: + String containing the toml corresponding to dictionary + + Raises: + TypeError: When anything other than file descriptor is passed + """ + + if not f.write: + raise TypeError("You can only dump an object to a file descriptor") + d = dumps(o) + f.write(d) + return d + + +def dumps(o, preserve=False): + """Stringifies input dict as toml + + Args: + o: Object to dump into toml + + preserve: Boolean parameter. If true, preserve inline tables. + + Returns: + String containing the toml corresponding to dict + """ + + retval = "" + addtoretval, sections = _dump_sections(o, "") + retval += addtoretval + while sections != {}: + newsections = {} + for section in sections: + addtoretval, addtosections = _dump_sections(sections[section], + section, preserve) + if addtoretval or (not addtoretval and not addtosections): + if retval and retval[-2:] != "\n\n": + retval += "\n" + retval += "[" + section + "]\n" + if addtoretval: + retval += addtoretval + for s in addtosections: + newsections[section + "." + s] = addtosections[s] + sections = newsections + return retval + + +def _dump_sections(o, sup, preserve=False): + retstr = "" + if sup != "" and sup[-1] != ".": + sup += '.' + retdict = o.__class__() + arraystr = "" + for section in o: + section = unicode(section) + qsection = section + if not re.match(r'^[A-Za-z0-9_-]+$', section): + if '"' in section: + qsection = "'" + section + "'" + else: + qsection = '"' + section + '"' + if not isinstance(o[section], dict): + arrayoftables = False + if isinstance(o[section], list): + for a in o[section]: + if isinstance(a, dict): + arrayoftables = True + if arrayoftables: + for a in o[section]: + arraytabstr = "\n" + arraystr += "[[" + sup + qsection + "]]\n" + s, d = _dump_sections(a, sup + qsection) + if s: + if s[0] == "[": + arraytabstr += s + else: + arraystr += s + while d != {}: + newd = {} + for dsec in d: + s1, d1 = _dump_sections(d[dsec], sup + qsection + + "." + dsec) + if s1: + arraytabstr += ("[" + sup + qsection + "." + + dsec + "]\n") + arraytabstr += s1 + for s1 in d1: + newd[dsec + "." + s1] = d1[s1] + d = newd + arraystr += arraytabstr + else: + if o[section] is not None: + retstr += (qsection + " = " + + unicode(_dump_value(o[section])) + '\n') + elif preserve and isinstance(o[section], InlineTableDict): + retstr += (qsection + " = " + _dump_inline_table(o[section])) + else: + retdict[qsection] = o[section] + retstr += arraystr + return (retstr, retdict) + + +def _dump_inline_table(section): + """Preserve inline table in its compact syntax instead of expanding + into subsection. + + https://github.com/toml-lang/toml#user-content-inline-table + """ + retval = "" + if isinstance(section, dict): + val_list = [] + for k, v in section.items(): + val = _dump_inline_table(v) + val_list.append(k + " = " + val) + retval += "{ " + ", ".join(val_list) + " }\n" + return retval + else: + return unicode(_dump_value(section)) + + +def _dump_value(v): + dump_funcs = { + str: _dump_str, + unicode: _dump_str, + list: _dump_list, + int: lambda v: v, + bool: lambda v: unicode(v).lower(), + float: _dump_float, + datetime.datetime: lambda v: v.isoformat().replace('+00:00', 'Z'), + } + # Lookup function corresponding to v's type + dump_fn = dump_funcs.get(type(v)) + if dump_fn is None and hasattr(v, '__iter__'): + dump_fn = dump_funcs[list] + # Evaluate function (if it exists) else return v + return dump_fn(v) if dump_fn is not None else dump_funcs[str](v) + + +def _dump_str(v): + if sys.version_info < (3,) and hasattr(v, 'decode') and isinstance(v, str): + v = v.decode('utf-8') + v = "%r" % v + if v[0] == 'u': + v = v[1:] + singlequote = v.startswith("'") + if singlequote or v.startswith('"'): + v = v[1:-1] + if singlequote: + v = v.replace("\\'", "'") + v = v.replace('"', '\\"') + v = v.split("\\x") + while len(v) > 1: + i = -1 + if not v[0]: + v = v[1:] + v[0] = v[0].replace("\\\\", "\\") + # No, I don't know why != works and == breaks + joinx = v[0][i] != "\\" + while v[0][:i] and v[0][i] == "\\": + joinx = not joinx + i -= 1 + if joinx: + joiner = "x" + else: + joiner = "u00" + v = [v[0] + joiner + v[1]] + v[2:] + return unicode('"' + v[0] + '"') + + +def _dump_list(v): + retval = "[" + for u in v: + retval += " " + unicode(_dump_value(u)) + "," + retval += "]" + return retval + + +def _dump_float(v): + return "{0:.16}".format(v).replace("e+0", "e+").replace("e-0", "e-") diff --git a/venv/lib/python3.8/site-packages/pip/_vendor/toml/__init__.py b/venv/lib/python3.8/site-packages/pip/_vendor/toml/__init__.py new file mode 100644 index 0000000..015d73c --- /dev/null +++ b/venv/lib/python3.8/site-packages/pip/_vendor/toml/__init__.py @@ -0,0 +1,21 @@ +"""Python module which parses and emits TOML. + +Released under the MIT license. +""" + +from pip._vendor.toml import encoder +from pip._vendor.toml import decoder + +__version__ = "0.10.0" +_spec_ = "0.5.0" + +load = decoder.load +loads = decoder.loads +TomlDecoder = decoder.TomlDecoder +TomlDecodeError = decoder.TomlDecodeError + +dump = encoder.dump +dumps = encoder.dumps +TomlEncoder = encoder.TomlEncoder +TomlArraySeparatorEncoder = encoder.TomlArraySeparatorEncoder +TomlPreserveInlineDictEncoder = encoder.TomlPreserveInlineDictEncoder diff --git a/venv/lib/python3.8/site-packages/pip/_vendor/toml/__pycache__/__init__.cpython-38.pyc b/venv/lib/python3.8/site-packages/pip/_vendor/toml/__pycache__/__init__.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..2d0ec182205cd27f13c6987b7444a98cbbb0fae1 GIT binary patch literal 622 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 literal 0 HcmV?d00001 diff --git a/venv/lib/python3.8/site-packages/pip/_vendor/toml/__pycache__/encoder.cpython-38.pyc b/venv/lib/python3.8/site-packages/pip/_vendor/toml/__pycache__/encoder.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..55ea5b9555d14c8fd95fbf952cb9cd3040262c72 GIT binary patch literal 6950 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 M3<>Hp{M=#Z57E`w)&Kwi literal 0 HcmV?d00001 diff --git a/venv/lib/python3.8/site-packages/pip/_vendor/toml/__pycache__/tz.cpython-38.pyc b/venv/lib/python3.8/site-packages/pip/_vendor/toml/__pycache__/tz.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..50a56d10d2339a878310a5941994f883bae27f39 GIT binary patch literal 1137 zcmZuwO>Yx15VgG@*`y5(qEIT)TMtB#Zd9P62qB~%xaC56$;DbSo3hD%;dN3q%B|&s z#1H9T@|9EmLN740o0LlJNFL96?0L^`yr<1(gQ4y3{erPz><@k1H5)%Z<0$sg0RtYh zgb#Qk27tINkyM~BW_174L zY`}pH1aJ^n469*1oNaKx#o2)xcsRRIN0f$f`&knA=R>NoHj0Vy365fbZoz?1gld6U zFv(JP!D(KwF>>BzU}NqjIH>>^*SW@Bm~o-@Q|z)!+H0-WJgDEU*}#ZF&+x(Aebw!L z=ynxHup8c4{zY5T$I>E4WR@Rtj$AYK1Pk3v{9Hw|K!^S;Q2r>3AykE*5B;O_eD?M*JwDU`N8gI~ z-BB`6-gQemOZ~o|M|o$O=D~0hLg)N@7{;ARa52t~-&|16m$NX1O!)-r>-j-`fs|yF zMp{Y-p^Hs)jC|pEp;aRpuTfF)bmVVL*VIV5zef@ZVy)f=7v+O7+@W zbXzs?grdHwEpjb#JLE|CJEZDUJ*@gjwAZ{^D_*zBd(>{#@bH*MJuZ2vMfrMAixT=F zKnn^@a;%{mD=1vgrMlf s(kO@{wz}?Fo>i{)+P3ikA0GYBitbVorri&fGTz6E%P0CDO6o1^A1$Tw)c^nh literal 0 HcmV?d00001 diff --git a/venv/lib/python3.8/site-packages/pip/_vendor/toml/decoder.py b/venv/lib/python3.8/site-packages/pip/_vendor/toml/decoder.py new file mode 100644 index 0000000..20be459 --- /dev/null +++ b/venv/lib/python3.8/site-packages/pip/_vendor/toml/decoder.py @@ -0,0 +1,945 @@ +import datetime +import io +from os import linesep +import re +import sys + +from pip._vendor.toml.tz import TomlTz + +if sys.version_info < (3,): + _range = xrange # noqa: F821 +else: + unicode = str + _range = range + basestring = str + unichr = chr + + +def _detect_pathlib_path(p): + if (3, 4) <= sys.version_info: + import pathlib + if isinstance(p, pathlib.PurePath): + return True + return False + + +def _ispath(p): + if isinstance(p, basestring): + return True + return _detect_pathlib_path(p) + + +def _getpath(p): + if (3, 6) <= sys.version_info: + import os + return os.fspath(p) + if _detect_pathlib_path(p): + return str(p) + return p + + +try: + FNFError = FileNotFoundError +except NameError: + FNFError = IOError + + +TIME_RE = re.compile("([0-9]{2}):([0-9]{2}):([0-9]{2})(\.([0-9]{3,6}))?") + + +class TomlDecodeError(ValueError): + """Base toml Exception / Error.""" + + def __init__(self, msg, doc, pos): + lineno = doc.count('\n', 0, pos) + 1 + colno = pos - doc.rfind('\n', 0, pos) + emsg = '{} (line {} column {} char {})'.format(msg, lineno, colno, pos) + ValueError.__init__(self, emsg) + self.msg = msg + self.doc = doc + self.pos = pos + self.lineno = lineno + self.colno = colno + + +# Matches a TOML number, which allows underscores for readability +_number_with_underscores = re.compile('([0-9])(_([0-9]))*') + + +def _strictly_valid_num(n): + n = n.strip() + if not n: + return False + if n[0] == '_': + return False + if n[-1] == '_': + return False + if "_." in n or "._" in n: + return False + if len(n) == 1: + return True + if n[0] == '0' and n[1] not in ['.', 'o', 'b', 'x']: + return False + if n[0] == '+' or n[0] == '-': + n = n[1:] + if len(n) > 1 and n[0] == '0' and n[1] != '.': + return False + if '__' in n: + return False + return True + + +def load(f, _dict=dict, decoder=None): + """Parses named file or files as toml and returns a dictionary + + Args: + f: Path to the file to open, array of files to read into single dict + or a file descriptor + _dict: (optional) Specifies the class of the returned toml dictionary + + Returns: + Parsed toml file represented as a dictionary + + Raises: + TypeError -- When f is invalid type + TomlDecodeError: Error while decoding toml + IOError / FileNotFoundError -- When an array with no valid (existing) + (Python 2 / Python 3) file paths is passed + """ + + if _ispath(f): + with io.open(_getpath(f), encoding='utf-8') as ffile: + return loads(ffile.read(), _dict, decoder) + elif isinstance(f, list): + from os import path as op + from warnings import warn + if not [path for path in f if op.exists(path)]: + error_msg = "Load expects a list to contain filenames only." + error_msg += linesep + error_msg += ("The list needs to contain the path of at least one " + "existing file.") + raise FNFError(error_msg) + if decoder is None: + decoder = TomlDecoder() + d = decoder.get_empty_table() + for l in f: + if op.exists(l): + d.update(load(l, _dict, decoder)) + else: + warn("Non-existent filename in list with at least one valid " + "filename") + return d + else: + try: + return loads(f.read(), _dict, decoder) + except AttributeError: + raise TypeError("You can only load a file descriptor, filename or " + "list") + + +_groupname_re = re.compile(r'^[A-Za-z0-9_-]+$') + + +def loads(s, _dict=dict, decoder=None): + """Parses string as toml + + Args: + s: String to be parsed + _dict: (optional) Specifies the class of the returned toml dictionary + + Returns: + Parsed toml file represented as a dictionary + + Raises: + TypeError: When a non-string is passed + TomlDecodeError: Error while decoding toml + """ + + implicitgroups = [] + if decoder is None: + decoder = TomlDecoder(_dict) + retval = decoder.get_empty_table() + currentlevel = retval + if not isinstance(s, basestring): + raise TypeError("Expecting something like a string") + + if not isinstance(s, unicode): + s = s.decode('utf8') + + original = s + sl = list(s) + openarr = 0 + openstring = False + openstrchar = "" + multilinestr = False + arrayoftables = False + beginline = True + keygroup = False + dottedkey = False + keyname = 0 + for i, item in enumerate(sl): + if item == '\r' and sl[i + 1] == '\n': + sl[i] = ' ' + continue + if keyname: + if item == '\n': + raise TomlDecodeError("Key name found without value." + " Reached end of line.", original, i) + if openstring: + if item == openstrchar: + keyname = 2 + openstring = False + openstrchar = "" + continue + elif keyname == 1: + if item.isspace(): + keyname = 2 + continue + elif item == '.': + dottedkey = True + continue + elif item.isalnum() or item == '_' or item == '-': + continue + elif (dottedkey and sl[i - 1] == '.' and + (item == '"' or item == "'")): + openstring = True + openstrchar = item + continue + elif keyname == 2: + if item.isspace(): + if dottedkey: + nextitem = sl[i + 1] + if not nextitem.isspace() and nextitem != '.': + keyname = 1 + continue + if item == '.': + dottedkey = True + nextitem = sl[i + 1] + if not nextitem.isspace() and nextitem != '.': + keyname = 1 + continue + if item == '=': + keyname = 0 + dottedkey = False + else: + raise TomlDecodeError("Found invalid character in key name: '" + + item + "'. Try quoting the key name.", + original, i) + if item == "'" and openstrchar != '"': + k = 1 + try: + while sl[i - k] == "'": + k += 1 + if k == 3: + break + except IndexError: + pass + if k == 3: + multilinestr = not multilinestr + openstring = multilinestr + else: + openstring = not openstring + if openstring: + openstrchar = "'" + else: + openstrchar = "" + if item == '"' and openstrchar != "'": + oddbackslash = False + k = 1 + tripquote = False + try: + while sl[i - k] == '"': + k += 1 + if k == 3: + tripquote = True + break + if k == 1 or (k == 3 and tripquote): + while sl[i - k] == '\\': + oddbackslash = not oddbackslash + k += 1 + except IndexError: + pass + if not oddbackslash: + if tripquote: + multilinestr = not multilinestr + openstring = multilinestr + else: + openstring = not openstring + if openstring: + openstrchar = '"' + else: + openstrchar = "" + if item == '#' and (not openstring and not keygroup and + not arrayoftables): + j = i + try: + while sl[j] != '\n': + sl[j] = ' ' + j += 1 + except IndexError: + break + if item == '[' and (not openstring and not keygroup and + not arrayoftables): + if beginline: + if len(sl) > i + 1 and sl[i + 1] == '[': + arrayoftables = True + else: + keygroup = True + else: + openarr += 1 + if item == ']' and not openstring: + if keygroup: + keygroup = False + elif arrayoftables: + if sl[i - 1] == ']': + arrayoftables = False + else: + openarr -= 1 + if item == '\n': + if openstring or multilinestr: + if not multilinestr: + raise TomlDecodeError("Unbalanced quotes", original, i) + if ((sl[i - 1] == "'" or sl[i - 1] == '"') and ( + sl[i - 2] == sl[i - 1])): + sl[i] = sl[i - 1] + if sl[i - 3] == sl[i - 1]: + sl[i - 3] = ' ' + elif openarr: + sl[i] = ' ' + else: + beginline = True + elif beginline and sl[i] != ' ' and sl[i] != '\t': + beginline = False + if not keygroup and not arrayoftables: + if sl[i] == '=': + raise TomlDecodeError("Found empty keyname. ", original, i) + keyname = 1 + s = ''.join(sl) + s = s.split('\n') + multikey = None + multilinestr = "" + multibackslash = False + pos = 0 + for idx, line in enumerate(s): + if idx > 0: + pos += len(s[idx - 1]) + 1 + if not multilinestr or multibackslash or '\n' not in multilinestr: + line = line.strip() + if line == "" and (not multikey or multibackslash): + continue + if multikey: + if multibackslash: + multilinestr += line + else: + multilinestr += line + multibackslash = False + if len(line) > 2 and (line[-1] == multilinestr[0] and + line[-2] == multilinestr[0] and + line[-3] == multilinestr[0]): + try: + value, vtype = decoder.load_value(multilinestr) + except ValueError as err: + raise TomlDecodeError(str(err), original, pos) + currentlevel[multikey] = value + multikey = None + multilinestr = "" + else: + k = len(multilinestr) - 1 + while k > -1 and multilinestr[k] == '\\': + multibackslash = not multibackslash + k -= 1 + if multibackslash: + multilinestr = multilinestr[:-1] + else: + multilinestr += "\n" + continue + if line[0] == '[': + arrayoftables = False + if len(line) == 1: + raise TomlDecodeError("Opening key group bracket on line by " + "itself.", original, pos) + if line[1] == '[': + arrayoftables = True + line = line[2:] + splitstr = ']]' + else: + line = line[1:] + splitstr = ']' + i = 1 + quotesplits = decoder._get_split_on_quotes(line) + quoted = False + for quotesplit in quotesplits: + if not quoted and splitstr in quotesplit: + break + i += quotesplit.count(splitstr) + quoted = not quoted + line = line.split(splitstr, i) + if len(line) < i + 1 or line[-1].strip() != "": + raise TomlDecodeError("Key group not on a line by itself.", + original, pos) + groups = splitstr.join(line[:-1]).split('.') + i = 0 + while i < len(groups): + groups[i] = groups[i].strip() + if len(groups[i]) > 0 and (groups[i][0] == '"' or + groups[i][0] == "'"): + groupstr = groups[i] + j = i + 1 + while not groupstr[0] == groupstr[-1]: + j += 1 + if j > len(groups) + 2: + raise TomlDecodeError("Invalid group name '" + + groupstr + "' Something " + + "went wrong.", original, pos) + groupstr = '.'.join(groups[i:j]).strip() + groups[i] = groupstr[1:-1] + groups[i + 1:j] = [] + else: + if not _groupname_re.match(groups[i]): + raise TomlDecodeError("Invalid group name '" + + groups[i] + "'. Try quoting it.", + original, pos) + i += 1 + currentlevel = retval + for i in _range(len(groups)): + group = groups[i] + if group == "": + raise TomlDecodeError("Can't have a keygroup with an empty " + "name", original, pos) + try: + currentlevel[group] + if i == len(groups) - 1: + if group in implicitgroups: + implicitgroups.remove(group) + if arrayoftables: + raise TomlDecodeError("An implicitly defined " + "table can't be an array", + original, pos) + elif arrayoftables: + currentlevel[group].append(decoder.get_empty_table() + ) + else: + raise TomlDecodeError("What? " + group + + " already exists?" + + str(currentlevel), + original, pos) + except TypeError: + currentlevel = currentlevel[-1] + if group not in currentlevel: + currentlevel[group] = decoder.get_empty_table() + if i == len(groups) - 1 and arrayoftables: + currentlevel[group] = [decoder.get_empty_table()] + except KeyError: + if i != len(groups) - 1: + implicitgroups.append(group) + currentlevel[group] = decoder.get_empty_table() + if i == len(groups) - 1 and arrayoftables: + currentlevel[group] = [decoder.get_empty_table()] + currentlevel = currentlevel[group] + if arrayoftables: + try: + currentlevel = currentlevel[-1] + except KeyError: + pass + elif line[0] == "{": + if line[-1] != "}": + raise TomlDecodeError("Line breaks are not allowed in inline" + "objects", original, pos) + try: + decoder.load_inline_object(line, currentlevel, multikey, + multibackslash) + except ValueError as err: + raise TomlDecodeError(str(err), original, pos) + elif "=" in line: + try: + ret = decoder.load_line(line, currentlevel, multikey, + multibackslash) + except ValueError as err: + raise TomlDecodeError(str(err), original, pos) + if ret is not None: + multikey, multilinestr, multibackslash = ret + return retval + + +def _load_date(val): + microsecond = 0 + tz = None + try: + if len(val) > 19: + if val[19] == '.': + if val[-1].upper() == 'Z': + subsecondval = val[20:-1] + tzval = "Z" + else: + subsecondvalandtz = val[20:] + if '+' in subsecondvalandtz: + splitpoint = subsecondvalandtz.index('+') + subsecondval = subsecondvalandtz[:splitpoint] + tzval = subsecondvalandtz[splitpoint:] + elif '-' in subsecondvalandtz: + splitpoint = subsecondvalandtz.index('-') + subsecondval = subsecondvalandtz[:splitpoint] + tzval = subsecondvalandtz[splitpoint:] + else: + tzval = None + subsecondval = subsecondvalandtz + if tzval is not None: + tz = TomlTz(tzval) + microsecond = int(int(subsecondval) * + (10 ** (6 - len(subsecondval)))) + else: + tz = TomlTz(val[19:]) + except ValueError: + tz = None + if "-" not in val[1:]: + return None + try: + if len(val) == 10: + d = datetime.date( + int(val[:4]), int(val[5:7]), + int(val[8:10])) + else: + d = datetime.datetime( + int(val[:4]), int(val[5:7]), + int(val[8:10]), int(val[11:13]), + int(val[14:16]), int(val[17:19]), microsecond, tz) + except ValueError: + return None + return d + + +def _load_unicode_escapes(v, hexbytes, prefix): + skip = False + i = len(v) - 1 + while i > -1 and v[i] == '\\': + skip = not skip + i -= 1 + for hx in hexbytes: + if skip: + skip = False + i = len(hx) - 1 + while i > -1 and hx[i] == '\\': + skip = not skip + i -= 1 + v += prefix + v += hx + continue + hxb = "" + i = 0 + hxblen = 4 + if prefix == "\\U": + hxblen = 8 + hxb = ''.join(hx[i:i + hxblen]).lower() + if hxb.strip('0123456789abcdef'): + raise ValueError("Invalid escape sequence: " + hxb) + if hxb[0] == "d" and hxb[1].strip('01234567'): + raise ValueError("Invalid escape sequence: " + hxb + + ". Only scalar unicode points are allowed.") + v += unichr(int(hxb, 16)) + v += unicode(hx[len(hxb):]) + return v + + +# Unescape TOML string values. + +# content after the \ +_escapes = ['0', 'b', 'f', 'n', 'r', 't', '"'] +# What it should be replaced by +_escapedchars = ['\0', '\b', '\f', '\n', '\r', '\t', '\"'] +# Used for substitution +_escape_to_escapedchars = dict(zip(_escapes, _escapedchars)) + + +def _unescape(v): + """Unescape characters in a TOML string.""" + i = 0 + backslash = False + while i < len(v): + if backslash: + backslash = False + if v[i] in _escapes: + v = v[:i - 1] + _escape_to_escapedchars[v[i]] + v[i + 1:] + elif v[i] == '\\': + v = v[:i - 1] + v[i:] + elif v[i] == 'u' or v[i] == 'U': + i += 1 + else: + raise ValueError("Reserved escape sequence used") + continue + elif v[i] == '\\': + backslash = True + i += 1 + return v + + +class InlineTableDict(object): + """Sentinel subclass of dict for inline tables.""" + + +class TomlDecoder(object): + + def __init__(self, _dict=dict): + self._dict = _dict + + def get_empty_table(self): + return self._dict() + + def get_empty_inline_table(self): + class DynamicInlineTableDict(self._dict, InlineTableDict): + """Concrete sentinel subclass for inline tables. + It is a subclass of _dict which is passed in dynamically at load + time + + It is also a subclass of InlineTableDict + """ + + return DynamicInlineTableDict() + + def load_inline_object(self, line, currentlevel, multikey=False, + multibackslash=False): + candidate_groups = line[1:-1].split(",") + groups = [] + if len(candidate_groups) == 1 and not candidate_groups[0].strip(): + candidate_groups.pop() + while len(candidate_groups) > 0: + candidate_group = candidate_groups.pop(0) + try: + _, value = candidate_group.split('=', 1) + except ValueError: + raise ValueError("Invalid inline table encountered") + value = value.strip() + if ((value[0] == value[-1] and value[0] in ('"', "'")) or ( + value[0] in '-0123456789' or + value in ('true', 'false') or + (value[0] == "[" and value[-1] == "]") or + (value[0] == '{' and value[-1] == '}'))): + groups.append(candidate_group) + elif len(candidate_groups) > 0: + candidate_groups[0] = (candidate_group + "," + + candidate_groups[0]) + else: + raise ValueError("Invalid inline table value encountered") + for group in groups: + status = self.load_line(group, currentlevel, multikey, + multibackslash) + if status is not None: + break + + def _get_split_on_quotes(self, line): + doublequotesplits = line.split('"') + quoted = False + quotesplits = [] + if len(doublequotesplits) > 1 and "'" in doublequotesplits[0]: + singlequotesplits = doublequotesplits[0].split("'") + doublequotesplits = doublequotesplits[1:] + while len(singlequotesplits) % 2 == 0 and len(doublequotesplits): + singlequotesplits[-1] += '"' + doublequotesplits[0] + doublequotesplits = doublequotesplits[1:] + if "'" in singlequotesplits[-1]: + singlequotesplits = (singlequotesplits[:-1] + + singlequotesplits[-1].split("'")) + quotesplits += singlequotesplits + for doublequotesplit in doublequotesplits: + if quoted: + quotesplits.append(doublequotesplit) + else: + quotesplits += doublequotesplit.split("'") + quoted = not quoted + return quotesplits + + def load_line(self, line, currentlevel, multikey, multibackslash): + i = 1 + quotesplits = self._get_split_on_quotes(line) + quoted = False + for quotesplit in quotesplits: + if not quoted and '=' in quotesplit: + break + i += quotesplit.count('=') + quoted = not quoted + pair = line.split('=', i) + strictly_valid = _strictly_valid_num(pair[-1]) + if _number_with_underscores.match(pair[-1]): + pair[-1] = pair[-1].replace('_', '') + while len(pair[-1]) and (pair[-1][0] != ' ' and pair[-1][0] != '\t' and + pair[-1][0] != "'" and pair[-1][0] != '"' and + pair[-1][0] != '[' and pair[-1][0] != '{' and + pair[-1] != 'true' and pair[-1] != 'false'): + try: + float(pair[-1]) + break + except ValueError: + pass + if _load_date(pair[-1]) is not None: + break + i += 1 + prev_val = pair[-1] + pair = line.split('=', i) + if prev_val == pair[-1]: + raise ValueError("Invalid date or number") + if strictly_valid: + strictly_valid = _strictly_valid_num(pair[-1]) + pair = ['='.join(pair[:-1]).strip(), pair[-1].strip()] + if '.' in pair[0]: + if '"' in pair[0] or "'" in pair[0]: + quotesplits = self._get_split_on_quotes(pair[0]) + quoted = False + levels = [] + for quotesplit in quotesplits: + if quoted: + levels.append(quotesplit) + else: + levels += [level.strip() for level in + quotesplit.split('.')] + quoted = not quoted + else: + levels = pair[0].split('.') + while levels[-1] == "": + levels = levels[:-1] + for level in levels[:-1]: + if level == "": + continue + if level not in currentlevel: + currentlevel[level] = self.get_empty_table() + currentlevel = currentlevel[level] + pair[0] = levels[-1].strip() + elif (pair[0][0] == '"' or pair[0][0] == "'") and \ + (pair[0][-1] == pair[0][0]): + pair[0] = pair[0][1:-1] + if len(pair[1]) > 2 and ((pair[1][0] == '"' or pair[1][0] == "'") and + pair[1][1] == pair[1][0] and + pair[1][2] == pair[1][0] and + not (len(pair[1]) > 5 and + pair[1][-1] == pair[1][0] and + pair[1][-2] == pair[1][0] and + pair[1][-3] == pair[1][0])): + k = len(pair[1]) - 1 + while k > -1 and pair[1][k] == '\\': + multibackslash = not multibackslash + k -= 1 + if multibackslash: + multilinestr = pair[1][:-1] + else: + multilinestr = pair[1] + "\n" + multikey = pair[0] + else: + value, vtype = self.load_value(pair[1], strictly_valid) + try: + currentlevel[pair[0]] + raise ValueError("Duplicate keys!") + except TypeError: + raise ValueError("Duplicate keys!") + except KeyError: + if multikey: + return multikey, multilinestr, multibackslash + else: + currentlevel[pair[0]] = value + + def load_value(self, v, strictly_valid=True): + if not v: + raise ValueError("Empty value is invalid") + if v == 'true': + return (True, "bool") + elif v == 'false': + return (False, "bool") + elif v[0] == '"' or v[0] == "'": + quotechar = v[0] + testv = v[1:].split(quotechar) + triplequote = False + triplequotecount = 0 + if len(testv) > 1 and testv[0] == '' and testv[1] == '': + testv = testv[2:] + triplequote = True + closed = False + for tv in testv: + if tv == '': + if triplequote: + triplequotecount += 1 + else: + closed = True + else: + oddbackslash = False + try: + i = -1 + j = tv[i] + while j == '\\': + oddbackslash = not oddbackslash + i -= 1 + j = tv[i] + except IndexError: + pass + if not oddbackslash: + if closed: + raise ValueError("Stuff after closed string. WTF?") + else: + if not triplequote or triplequotecount > 1: + closed = True + else: + triplequotecount = 0 + if quotechar == '"': + escapeseqs = v.split('\\')[1:] + backslash = False + for i in escapeseqs: + if i == '': + backslash = not backslash + else: + if i[0] not in _escapes and (i[0] != 'u' and + i[0] != 'U' and + not backslash): + raise ValueError("Reserved escape sequence used") + if backslash: + backslash = False + for prefix in ["\\u", "\\U"]: + if prefix in v: + hexbytes = v.split(prefix) + v = _load_unicode_escapes(hexbytes[0], hexbytes[1:], + prefix) + v = _unescape(v) + if len(v) > 1 and v[1] == quotechar and (len(v) < 3 or + v[1] == v[2]): + v = v[2:-2] + return (v[1:-1], "str") + elif v[0] == '[': + return (self.load_array(v), "array") + elif v[0] == '{': + inline_object = self.get_empty_inline_table() + self.load_inline_object(v, inline_object) + return (inline_object, "inline_object") + elif TIME_RE.match(v): + h, m, s, _, ms = TIME_RE.match(v).groups() + time = datetime.time(int(h), int(m), int(s), int(ms) if ms else 0) + return (time, "time") + else: + parsed_date = _load_date(v) + if parsed_date is not None: + return (parsed_date, "date") + if not strictly_valid: + raise ValueError("Weirdness with leading zeroes or " + "underscores in your number.") + itype = "int" + neg = False + if v[0] == '-': + neg = True + v = v[1:] + elif v[0] == '+': + v = v[1:] + v = v.replace('_', '') + lowerv = v.lower() + if '.' in v or ('x' not in v and ('e' in v or 'E' in v)): + if '.' in v and v.split('.', 1)[1] == '': + raise ValueError("This float is missing digits after " + "the point") + if v[0] not in '0123456789': + raise ValueError("This float doesn't have a leading " + "digit") + v = float(v) + itype = "float" + elif len(lowerv) == 3 and (lowerv == 'inf' or lowerv == 'nan'): + v = float(v) + itype = "float" + if itype == "int": + v = int(v, 0) + if neg: + return (0 - v, itype) + return (v, itype) + + def bounded_string(self, s): + if len(s) == 0: + return True + if s[-1] != s[0]: + return False + i = -2 + backslash = False + while len(s) + i > 0: + if s[i] == "\\": + backslash = not backslash + i -= 1 + else: + break + return not backslash + + def load_array(self, a): + atype = None + retval = [] + a = a.strip() + if '[' not in a[1:-1] or "" != a[1:-1].split('[')[0].strip(): + strarray = False + tmpa = a[1:-1].strip() + if tmpa != '' and (tmpa[0] == '"' or tmpa[0] == "'"): + strarray = True + if not a[1:-1].strip().startswith('{'): + a = a[1:-1].split(',') + else: + # a is an inline object, we must find the matching parenthesis + # to define groups + new_a = [] + start_group_index = 1 + end_group_index = 2 + in_str = False + while end_group_index < len(a[1:]): + if a[end_group_index] == '"' or a[end_group_index] == "'": + if in_str: + backslash_index = end_group_index - 1 + while (backslash_index > -1 and + a[backslash_index] == '\\'): + in_str = not in_str + backslash_index -= 1 + in_str = not in_str + if in_str or a[end_group_index] != '}': + end_group_index += 1 + continue + + # Increase end_group_index by 1 to get the closing bracket + end_group_index += 1 + + new_a.append(a[start_group_index:end_group_index]) + + # The next start index is at least after the closing + # bracket, a closing bracket can be followed by a comma + # since we are in an array. + start_group_index = end_group_index + 1 + while (start_group_index < len(a[1:]) and + a[start_group_index] != '{'): + start_group_index += 1 + end_group_index = start_group_index + 1 + a = new_a + b = 0 + if strarray: + while b < len(a) - 1: + ab = a[b].strip() + while (not self.bounded_string(ab) or + (len(ab) > 2 and + ab[0] == ab[1] == ab[2] and + ab[-2] != ab[0] and + ab[-3] != ab[0])): + a[b] = a[b] + ',' + a[b + 1] + ab = a[b].strip() + if b < len(a) - 2: + a = a[:b + 1] + a[b + 2:] + else: + a = a[:b + 1] + b += 1 + else: + al = list(a[1:-1]) + a = [] + openarr = 0 + j = 0 + for i in _range(len(al)): + if al[i] == '[': + openarr += 1 + elif al[i] == ']': + openarr -= 1 + elif al[i] == ',' and not openarr: + a.append(''.join(al[j:i])) + j = i + 1 + a.append(''.join(al[j:])) + for i in _range(len(a)): + a[i] = a[i].strip() + if a[i] != '': + nval, ntype = self.load_value(a[i]) + if atype: + if ntype != atype: + raise ValueError("Not a homogeneous array") + else: + atype = ntype + retval.append(nval) + return retval diff --git a/venv/lib/python3.8/site-packages/pip/_vendor/toml/encoder.py b/venv/lib/python3.8/site-packages/pip/_vendor/toml/encoder.py new file mode 100644 index 0000000..53b0bd5 --- /dev/null +++ b/venv/lib/python3.8/site-packages/pip/_vendor/toml/encoder.py @@ -0,0 +1,250 @@ +import datetime +import re +import sys + +from pip._vendor.toml.decoder import InlineTableDict + +if sys.version_info >= (3,): + unicode = str + + +def dump(o, f): + """Writes out dict as toml to a file + + Args: + o: Object to dump into toml + f: File descriptor where the toml should be stored + + Returns: + String containing the toml corresponding to dictionary + + Raises: + TypeError: When anything other than file descriptor is passed + """ + + if not f.write: + raise TypeError("You can only dump an object to a file descriptor") + d = dumps(o) + f.write(d) + return d + + +def dumps(o, encoder=None): + """Stringifies input dict as toml + + Args: + o: Object to dump into toml + + preserve: Boolean parameter. If true, preserve inline tables. + + Returns: + String containing the toml corresponding to dict + """ + + retval = "" + if encoder is None: + encoder = TomlEncoder(o.__class__) + addtoretval, sections = encoder.dump_sections(o, "") + retval += addtoretval + while sections: + newsections = encoder.get_empty_table() + for section in sections: + addtoretval, addtosections = encoder.dump_sections( + sections[section], section) + + if addtoretval or (not addtoretval and not addtosections): + if retval and retval[-2:] != "\n\n": + retval += "\n" + retval += "[" + section + "]\n" + if addtoretval: + retval += addtoretval + for s in addtosections: + newsections[section + "." + s] = addtosections[s] + sections = newsections + return retval + + +def _dump_str(v): + if sys.version_info < (3,) and hasattr(v, 'decode') and isinstance(v, str): + v = v.decode('utf-8') + v = "%r" % v + if v[0] == 'u': + v = v[1:] + singlequote = v.startswith("'") + if singlequote or v.startswith('"'): + v = v[1:-1] + if singlequote: + v = v.replace("\\'", "'") + v = v.replace('"', '\\"') + v = v.split("\\x") + while len(v) > 1: + i = -1 + if not v[0]: + v = v[1:] + v[0] = v[0].replace("\\\\", "\\") + # No, I don't know why != works and == breaks + joinx = v[0][i] != "\\" + while v[0][:i] and v[0][i] == "\\": + joinx = not joinx + i -= 1 + if joinx: + joiner = "x" + else: + joiner = "u00" + v = [v[0] + joiner + v[1]] + v[2:] + return unicode('"' + v[0] + '"') + + +def _dump_float(v): + return "{0:.16}".format(v).replace("e+0", "e+").replace("e-0", "e-") + + +def _dump_time(v): + utcoffset = v.utcoffset() + if utcoffset is None: + return v.isoformat() + # The TOML norm specifies that it's local time thus we drop the offset + return v.isoformat()[:-6] + + +class TomlEncoder(object): + + def __init__(self, _dict=dict, preserve=False): + self._dict = _dict + self.preserve = preserve + self.dump_funcs = { + str: _dump_str, + unicode: _dump_str, + list: self.dump_list, + bool: lambda v: unicode(v).lower(), + int: lambda v: v, + float: _dump_float, + datetime.datetime: lambda v: v.isoformat().replace('+00:00', 'Z'), + datetime.time: _dump_time, + datetime.date: lambda v: v.isoformat() + } + + def get_empty_table(self): + return self._dict() + + def dump_list(self, v): + retval = "[" + for u in v: + retval += " " + unicode(self.dump_value(u)) + "," + retval += "]" + return retval + + def dump_inline_table(self, section): + """Preserve inline table in its compact syntax instead of expanding + into subsection. + + https://github.com/toml-lang/toml#user-content-inline-table + """ + retval = "" + if isinstance(section, dict): + val_list = [] + for k, v in section.items(): + val = self.dump_inline_table(v) + val_list.append(k + " = " + val) + retval += "{ " + ", ".join(val_list) + " }\n" + return retval + else: + return unicode(self.dump_value(section)) + + def dump_value(self, v): + # Lookup function corresponding to v's type + dump_fn = self.dump_funcs.get(type(v)) + if dump_fn is None and hasattr(v, '__iter__'): + dump_fn = self.dump_funcs[list] + # Evaluate function (if it exists) else return v + return dump_fn(v) if dump_fn is not None else self.dump_funcs[str](v) + + def dump_sections(self, o, sup): + retstr = "" + if sup != "" and sup[-1] != ".": + sup += '.' + retdict = self._dict() + arraystr = "" + for section in o: + section = unicode(section) + qsection = section + if not re.match(r'^[A-Za-z0-9_-]+$', section): + if '"' in section: + qsection = "'" + section + "'" + else: + qsection = '"' + section + '"' + if not isinstance(o[section], dict): + arrayoftables = False + if isinstance(o[section], list): + for a in o[section]: + if isinstance(a, dict): + arrayoftables = True + if arrayoftables: + for a in o[section]: + arraytabstr = "\n" + arraystr += "[[" + sup + qsection + "]]\n" + s, d = self.dump_sections(a, sup + qsection) + if s: + if s[0] == "[": + arraytabstr += s + else: + arraystr += s + while d: + newd = self._dict() + for dsec in d: + s1, d1 = self.dump_sections(d[dsec], sup + + qsection + "." + + dsec) + if s1: + arraytabstr += ("[" + sup + qsection + + "." + dsec + "]\n") + arraytabstr += s1 + for s1 in d1: + newd[dsec + "." + s1] = d1[s1] + d = newd + arraystr += arraytabstr + else: + if o[section] is not None: + retstr += (qsection + " = " + + unicode(self.dump_value(o[section])) + '\n') + elif self.preserve and isinstance(o[section], InlineTableDict): + retstr += (qsection + " = " + + self.dump_inline_table(o[section])) + else: + retdict[qsection] = o[section] + retstr += arraystr + return (retstr, retdict) + + +class TomlPreserveInlineDictEncoder(TomlEncoder): + + def __init__(self, _dict=dict): + super(TomlPreserveInlineDictEncoder, self).__init__(_dict, True) + + +class TomlArraySeparatorEncoder(TomlEncoder): + + def __init__(self, _dict=dict, preserve=False, separator=","): + super(TomlArraySeparatorEncoder, self).__init__(_dict, preserve) + if separator.strip() == "": + separator = "," + separator + elif separator.strip(' \t\n\r,'): + raise ValueError("Invalid separator for arrays") + self.separator = separator + + def dump_list(self, v): + t = [] + retval = "[" + for u in v: + t.append(self.dump_value(u)) + while t != []: + s = [] + for u in t: + if isinstance(u, list): + for r in u: + s.append(r) + else: + retval += " " + unicode(u) + self.separator + t = s + retval += "]" + return retval diff --git a/venv/lib/python3.8/site-packages/pip/_vendor/toml/ordered.py b/venv/lib/python3.8/site-packages/pip/_vendor/toml/ordered.py new file mode 100644 index 0000000..6052016 --- /dev/null +++ b/venv/lib/python3.8/site-packages/pip/_vendor/toml/ordered.py @@ -0,0 +1,15 @@ +from collections import OrderedDict +from pip._vendor.toml import TomlEncoder +from pip._vendor.toml import TomlDecoder + + +class TomlOrderedDecoder(TomlDecoder): + + def __init__(self): + super(self.__class__, self).__init__(_dict=OrderedDict) + + +class TomlOrderedEncoder(TomlEncoder): + + def __init__(self): + super(self.__class__, self).__init__(_dict=OrderedDict) diff --git a/venv/lib/python3.8/site-packages/pip/_vendor/toml/tz.py b/venv/lib/python3.8/site-packages/pip/_vendor/toml/tz.py new file mode 100644 index 0000000..93c3c8a --- /dev/null +++ b/venv/lib/python3.8/site-packages/pip/_vendor/toml/tz.py @@ -0,0 +1,21 @@ +from datetime import tzinfo, timedelta + + +class TomlTz(tzinfo): + def __init__(self, toml_offset): + if toml_offset == "Z": + self._raw_offset = "+00:00" + else: + self._raw_offset = toml_offset + self._sign = -1 if self._raw_offset[0] == '-' else 1 + self._hours = int(self._raw_offset[1:3]) + self._minutes = int(self._raw_offset[4:6]) + + def tzname(self, dt): + return "UTC" + self._raw_offset + + def utcoffset(self, dt): + return self._sign * timedelta(hours=self._hours, minutes=self._minutes) + + def dst(self, dt): + return timedelta(0) diff --git a/venv/lib/python3.8/site-packages/pip/_vendor/vendor.txt b/venv/lib/python3.8/site-packages/pip/_vendor/vendor.txt new file mode 100644 index 0000000..74ecca4 --- /dev/null +++ b/venv/lib/python3.8/site-packages/pip/_vendor/vendor.txt @@ -0,0 +1,24 @@ +appdirs==1.4.3 +CacheControl==0.12.6 +colorama==0.4.3 +contextlib2==0.6.0.post1 +distlib==0.3.0 +distro==1.5.0 +html5lib==1.0.1 +ipaddress==1.0.23 # Only needed on 2.6 and 2.7 +msgpack==1.0.0 +packaging==20.3 +pep517==0.8.2 +progress==1.5 +pyparsing==2.4.7 +requests==2.23.0 + certifi==2020.04.05.1 + chardet==3.0.4 + idna==2.9 + urllib3==1.25.8 +resolvelib==0.3.0 +retrying==1.3.3 +setuptools==44.0.0 +six==1.14.0 +toml==0.10.0 +webencodings==0.5.1 diff --git a/venv/lib/python3.8/site-packages/pip/_vendor/webencodings/__init__.py b/venv/lib/python3.8/site-packages/pip/_vendor/webencodings/__init__.py new file mode 100644 index 0000000..d21d697 --- /dev/null +++ b/venv/lib/python3.8/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 + `. 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 + `_ + matching of encoding labels. + The same matching is also used, among other things, + for `CSS 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 + `_ 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 '' % 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/venv/lib/python3.8/site-packages/pip/_vendor/webencodings/__pycache__/__init__.cpython-38.pyc b/venv/lib/python3.8/site-packages/pip/_vendor/webencodings/__pycache__/__init__.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..6440843701ad9400d134b585786632dd98e43238 GIT binary patch literal 9755 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 literal 0 HcmV?d00001 diff --git a/venv/lib/python3.8/site-packages/pip/_vendor/webencodings/__pycache__/mklabels.cpython-38.pyc b/venv/lib/python3.8/site-packages/pip/_vendor/webencodings/__pycache__/mklabels.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..87e9041089de4631488d6200ecdd2f258db7282e GIT binary patch literal 1949 zcmcIl&2HO95Z)yzieh3zZ~`N4Qgq`aKn2*6>$FB=xJDD(hXAe*Roc2n1A@}7B*qj; z@2-EONE_>GOWzd=$d;bC0~((ch`+v=yTB)kuQBMnC^(HI&uxH8}q|ZRP*mwn{m>ULA#?@*~VnamX|(QW{n=J z2i-Vgbw3HB?BdlhXo^UQRrajDxxkbVZ2S4f)=qtkwPMM*(0&jqQ2jf^i|kZrT0E3t zoPZw`Pc88DBwse?G$k}{T{ z*j8FXWX_2!f`Zq*S|WoZaJzQo%UUZAxsa-sG;1rz$=)RG`sJd6^*xi9$QaYu-7ao_K~il00Nok3Fbjzq*`S-YH$+9Vy-q?g|GGuyn( zQuIbyKnN{US%UW!&(UieCcbk}(Ka0dsJMAXG#OEC49F=RnPW1txHYC@x(WNCp$ojg z?K3hkaDPgFA_F?Ie;_T&i*WAb=VJg7oEN!+D_$Bo1JHn=+VO8e)aU2tuff)TBP$p9 z%um<^jqCzQI!25Il0|;^f5ua0_ye}PyUR9X7R8zg9_ah-1NNd5DAo!>!Qky5>bUFc zpy`KUk3l5}tOSlwzLYW7-G2IPYrD=qVc)t}(u0Y^ny+Jewt=((m@hYnbU-wjP4J=}%U%5?@aP={qX(92YR`LeO<`>?@RhCz05)r&UYng=FGqySVr)|&B zX~X!-F&jWX$LF-F7&$6GpvzGsa Z*6~lEW`I~X9cn7Wn^7vhL zm)u6RDQb-se=>0Wz-RcS9luEZM1INfYt=ky=KXdpaP!MenV(lpOh#JAs?F9_SzEaf z9Lt~TzvgESP0!?)uI3kO^=2c#=(K7L?(3LWtGW%}J(fSYc>0;V@4ESgv!~`ui}T#J za0932`DnTa4{8NH9Bnsh7_wXO(7$v%zo(NR9a>(qx!P`p*20OC^9zfiC0x=AE&fC| z%#waZfl)VF>Y#IZ8akg>Ka{N0|yqhuWA8!(ACDZ>-sgFkb+U_>^fZ&`9?FyOQ+_$ z`A_(h>v}kR!uMSn)S8WXDVwrjhH3Xo)ol?H+RN=mmETN9uK1y~dYPWM*t|(r<;JRLM)wz1HdGy(lRbA;YTO|ET&% zRR6K=+k`MVMoXP!J6JBxb&}`R11ktvp6?*T{nag73PUnUBJv>5Su|VZR7ue) zG`*T1$TZbyY04~t3EvGu>yqQOU0-4|(+a6D$@3Um(rvj8q|J8FY6qcS2~$xZm0>Ch zqLNLvlVOC(;CP-Q#33}R2x03JpoFdO(MRDh*NkKBF%OdJKTtEwu z)qkUcBnEGY1Xcyu%xhsn1rj~Nwn^ud6nurmf)0Wq+kL4o2@=~R{UB+I6eKom5FFX& zJIW@_lEKz)DM==lAUv|oZ z+X)o^xc2tFh-7jcRrwhdVbbxdwc3X?<9Dy$S(gR8+@soqhg$ExP0@XNcFASv;e(#m zhdkGhw0cqFAN=BF1|B}pdfbcF!l#?EMXJOqZVny2TsB5NedY@j7bci&wNYY+;CGv_wseeIn4X1>tb8 zg3V50lM1sGMt(S{Fk4}1U|&{PMr2nILSRj(r_h`j8bGfpbXeJsK=TcSjSlqlONC)E zirp)S$7q%MzZ2to#F)bO0uL3Q6yrGHB*kN}vSO?M_N`P8_+l);VTJBf=;MqYROrO! zXrOycp-*fJ7ET3k>YVAz{g8t*(N27^S!*DzPu3c=vF=*NUb%$CyTWY2xlA5}c9`C* z^H#FCpX?nt)l`CJ1^YUzJQ@du0oqbJoSRn$6eo{+%_@#g&rJ@1o7TJnc$o4DG$D^t zK|#r<-SL9!dChW?A3lYx3$9P!4wSnj0WTk%5CF?*Dkyrf7WP&a-sN(gOQ%gkbvEAn z9alG45qmJVzHytScYl57ww$4+4x?bbhb({4lmIKf@wjQ&r>to{5Trz%bQ2URVeu{qtJ9aF@`jSu5n!SG;~A5^$<_KZQ~;m(%Yg* zZP2AGw#hR)T9RimhAqK?1{DW~f1?6^Asylrr=M-^(};A{QJ{?9{J<#;ID7}sZB?) z2;8{&v!^$1zBaOP^ZOe&ZU-ATf3$Jqt&N+n^6GJ%vf()FnVtJT5w2kL(|yrD0Eq1i zX*VyVD-~LV__u`@;ZIg^Jowj%5O55YO_uIMu0acoxApigX literal 0 HcmV?d00001 diff --git a/venv/lib/python3.8/site-packages/pip/_vendor/webencodings/__pycache__/x_user_defined.cpython-38.pyc b/venv/lib/python3.8/site-packages/pip/_vendor/webencodings/__pycache__/x_user_defined.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..de4576a6865dd4ce9bd75252c900602d64924f7c GIT binary patch literal 2683 zcmb_e*?SvB5}!ktWyx_&I06J_Ask|3D+CC{u&jx55DvrQuqXBtTA$fq0)>k#%RWnuHzdBmC7mIm~ z$LduF+<$j#+D~e+bae1yfGIq}%+)HItGkA;*NlpxtGVf$6?3cRrrh*C%}on)JT+rh zQY^{1S(ap3l4ePoB{{c)B^@luuq4BhyxYl=PLX0+b|zKHvaH}1SyoJBIhN&E*Wq@F z)QmQsPk5(Di;T#Lth?-Bwo-7rD@9=*?h?gWy|TIK40^!J^7v ztM$f|^mdFz{nlW6YOPy)={jp<${O}+Y-reNctNs@e&4H#AQb)9#^FtCtWXH+rfq|p zhlV#>qjhPyB62)GWYa%$5VVpWcQ*r%PbF;Mi-dIiaHy&@ONYse0jAJmCbWvq!86>f zYq;haS)?7-ck8>2$e3lc z=k%eH8E4dfs$tw!9dl&OY1j!OBJOG#iROuUb1=o3354HW@pD^7eAW zYxFjQhEv@s+}_DCA^hH*&eV8)!(~%SSGFfa;MQe%X-1Tf&4|9n6z@l+i#S~!)nKOS z(}e_?zLw(G03#J>B~xbjv4{(!#17)FYB*jJ%mh zs=PR6sMiLV!qb>7q*r<&y;7W1nYd?LP?fFP@i!;H$}J~Ha0Q#1Pc3cSh|F2j)mLhq zV8)QtSyxIWP|7UNWkInVvpG|$7A8BH#c3{W$3Ol|>crQzX$=X|S5< zM99qVqHiJBPJqlw(@5p)`EA3Dj9UK!85Pt4^^ywaLK5kmI!0U=j-+sEcSzOYhB(N_ z#bogy$t*d|S#0KFWlvQuJ5}r7;`BD*aNik{XY=<<5LU2I_F&L59gh~`hp8Dy0~=7AJ_EumHFDa?D8wtUwPGr{;RL~)3tvd zxNhU%rp;TnUcc>z8*jRK=$2dma@+9s+y8pU-|np3_4j}L)4tmosq)3PW6T@h>DPjK z;~p7C&56lfQ`7hE{?~nb2=)@(Pw)W248cBv2MHb`*iY~^@Cm`E1fLOnPVfc6mjquCd`<8T!M6n85qwYZ z1L0o6`w1T)oFUvt_#oj!g!>5}CVYhO0O6yAj}aaue4OwJ!b5~l5&@FT*H2|pqHl<+gc&k4UE{F3l1!mkOxA^eu`JHqb?e<*dwS=)B&)mEP$ZAMMW zeO{{6XthXF%~Ur^3NI-HsggAANG`K&0spU9xCi3Ax{<23r=+S?#Kra{KGO7jH)*pc zs)IOpP3w-iR(TFDVU{Wsj9j{>qbJ>yUXfanN$G~3;h)j^{U_6H{Lk2' + '?' + '@' + '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/venv/lib/python3.8/site-packages/pkg_resources/__init__.py b/venv/lib/python3.8/site-packages/pkg_resources/__init__.py new file mode 100644 index 0000000..2e7d505 --- /dev/null +++ b/venv/lib/python3.8/site-packages/pkg_resources/__init__.py @@ -0,0 +1,3307 @@ +# 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 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') +__import__('pkg_resources.py2_warn') + + +__metaclass__ = type + + +if (3, 0) < sys.version_info < (3, 5): + raise RuntimeError("Python 3.5 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 macOS that would be required to *use* extensions produced by + distutils. But what we want when checking compatibility is to know the + version of macOS that we are *running*. To allow usage of packages that + explicitly require a newer version of macOS, 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(_macos_vers()[:2]), m.group(3)) + except ValueError: + # not macOS + 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 = '{}.{}'.format(*sys.version_info) +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 _macos_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 _macos_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 macOS. + """ + from sysconfig import get_platform + + plat = get_platform() + if sys.platform == "darwin" and not plat.startswith('macosx-'): + try: + version = _macos_vers() + machine = os.uname()[4].replace(" ", "_") + return "macosx-%d.%d-%s" % ( + int(version[0]), int(version[1]), + _macos_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 + + # macOS 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 macOS 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 macOS 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 = ( + "Extraction path is writable by group/others " + "and vulnerable to attack when " + "used with get_resource_filename ({path}). " + "Consider a more secure " + "location (set with .set_extraction_path or the " + "PYTHON_EGG_CACHE environment variable)." + ).format(**locals()) + 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 "" + path = self._get_metadata_path(name) + value = self._get(path) + if six.PY2: + return value + try: + return value.decode('utf-8') + except UnicodeDecodeError as exc: + # Include the path in the error message to simplify + # troubleshooting, and without changing the exception type. + exc.reason += ' in {} file at path: {}'.format(name, path) + raise + + 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) + + +def _parents(path): + """ + yield all parents of path including path + """ + last = None + while path != last: + yield path + last = path + path, _ = os.path.split(path) + + +class EggProvider(NullProvider): + """Provider based on a virtual filesystem""" + + def __init__(self, module): + NullProvider.__init__(self, module) + self._setup_prefix() + + def _setup_prefix(self): + # Assume that metadata may be nested inside a "basket" + # of multiple eggs and use module_path instead of .archive. + eggs = filter(_is_egg_path, _parents(self.module_path)) + egg = next(eggs, None) + egg and self._set_egg(egg) + + def _set_egg(self, path): + self.egg_name = os.path.basename(path) + self.egg_info = os.path.join(path, 'EGG-INFO') + self.egg_root = 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 the given entry.""" + lower = entry.lower() + is_egg_info = lower.endswith('.egg-info') + is_dist_info = ( + lower.endswith('.dist-info') and + os.path.isdir(os.path.join(path_item, entry)) + ) + is_meta = is_egg_info or is_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 + + # use find_spec (PEP 451) and fall-back to find_module (PEP 302) + try: + loader = importer.find_spec(packageName).loader + except AttributeError: + # 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[^-]+) ( + -(?P[^-]+) ( + -py(?P[^-]+) ( + -(?P.+) + )? + )? + )? + """, + 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.+?)\s*' + r'=\s*' + r'(?P[\w.]+)\s*' + r'(:\s*(?P[\w.]+))?\s*' + r'(?P\[.*\])?\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 _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, + 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.url, + 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/venv/lib/python3.8/site-packages/pkg_resources/__pycache__/__init__.cpython-38.pyc b/venv/lib/python3.8/site-packages/pkg_resources/__pycache__/__init__.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..9338446ad92166b0457fb28a1e5e1bc16c44e998 GIT binary patch literal 100662 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 literal 0 HcmV?d00001 diff --git a/venv/lib/python3.8/site-packages/pkg_resources/__pycache__/py2_warn.cpython-38.pyc b/venv/lib/python3.8/site-packages/pkg_resources/__pycache__/py2_warn.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..8e8fad8049db7f1b5448b9fa9781977004b4d538 GIT binary patch literal 653 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 P_@+or`ldhnrkef_9XU@( literal 0 HcmV?d00001 diff --git a/venv/lib/python3.8/site-packages/pkg_resources/_vendor/__pycache__/pyparsing.cpython-38.pyc b/venv/lib/python3.8/site-packages/pkg_resources/_vendor/__pycache__/pyparsing.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..3ddd65d28582bae428144226ff0b8b78cb626444 GIT binary patch literal 201686 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�BYRaw^OXpb2 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⪙Eh|6a9Xud#FzSR!aU(eFOPg|F}-HKgt)*e#5>$ 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 literal 0 HcmV?d00001 diff --git a/venv/lib/python3.8/site-packages/pkg_resources/_vendor/__pycache__/six.cpython-38.pyc b/venv/lib/python3.8/site-packages/pkg_resources/_vendor/__pycache__/six.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..a7977700b4f634cad5423554b1bde4533fdbb8e2 GIT binary patch literal 24482 zcmb_^3wT_|b>8mXCl(730>PIgN~<>^kxTI{N+iXnNJ=CG5fmk@B(E1c7r+9G-R0a1 zl2`*$EXWr9l4&P#o^?paNloX`x^3Dtb(+>r(>Ar;rg7TjwyE2scB|TL)1*yZHHq^7 zXYRebiv{I}0(|%0Gw(BJ&YU@O=FC3b*O!Ul@2-s>R3D#5oKI zq!3Y2WmTiIR>6uAj#XoYn0(`fxO@|ZgnW~Qqf>uWFB8%z%S z<_U{=?9}uvfL7bLB45Ac+j#Xh)pISXf72dNnWZR7oj4v*y(;@^L}l&Umtuunv){hM z&MifjqS44Rk=kwXh<)cw=1Np2qY?fGC7+AbGS4#9$9kU6seZNU)kxtkHK1<7_iofX zs8*xi)l%<02oI?>2(Pj4K|A*%yjHD4cpdP(4>6k%vtDgL%m&FZjPT9s7KCq+8t%96 zw;w=Tn-PDj+KBj#_9m3uVh^j^@PE6?EyV%fq3%?7sk_x3O9_PTLFi6(uewJ<_o+=t z8&>y8=zfGASc)OnW+}5pZI<}F+KT@NRbE2dB$QX%B(z;>*rB#dXs6m__g%5{klIzV z)NZv$Fn9=g_Da1Et4Gv6wO?X$DyKkUv~~dhj|y~99rU3?>W~lZ=xE`vV0J_umiCTH zZ;q*>2;GaGY+ADLeM}vf?+JBMzK^RX)G5?|uR5(p)MfHjB)VL}Oq?D~D1e#P+sv^*|npQIcRaMo8X4R|@)l^N@ zCAFc>3wByL@^#gmd@rbr>Vo9Bq~;~2sotX&BxX^)q+S;22h@AjD*`R4SJnFjdcS&2 zT^8sA>VxV-0)1G0M7=K1N7WVeF@Zj=eo+08KtHTLp?*Z5A5}l**Zkw^C)7_$++S0l zR6ixqRdvm8<5Ttn>ZjL5)Tbr>XAt^Xg#NmOK7-KpbrJKHn9r)8Q$H`zFQ{Kse?y?Z zsXnLv)>3TpKp~Hrx>5b@8S6@1pRw~8pTDDiX~~+5?uj5Re>C#q_QF>CL3@k6b$Xk< zbt#6~w%y)p-@C*T=j_c_ci3Ci-(~8$$TdivEAjK^p5Ka^wiNLIkH5DL^R}>4a_&^W z%siMae_#CyX3MY0Y}p0=`v>at(w|}Vt0D9cL+IB+=pTj9KMtYKhtL;7=!+rr>mjsX zea3I&pM=nFgwQ_?p??-a|2%|#Glc#{2z@E6`O6{nl@R)uA@r{dG`X&@+ujX|4hcnf zwJ3TeasD%)*=~CmXhw?e0^KmeOHuV(>R(?2%|Wr>R$m3hzA6;k1B(5Q`ZqG>!|LCL z(C-2_@WEuP@R0pb*wUW1mXhZO(b7Zq9>1kMXzBN)B~Sf3zomb#ejhFUzO=L#E&T`7 z|26QGR&^JTqTOst{L+F2m z(EkjfKMJA$6+(X;LVprM|2u^KPYC^K2>ss>`gREYzYuyeg#Lfn%RdXDKM$e52%)zO zG2;E#bjQ1^tBY59hIEwei!ZEyWD?Enx z?S` zS8c>Uc`Z6UQJ8#Qp)XS^iZ&~DVtTqTV^<5alDAf<*|q6&_J2d!g=c0fS%gG z5qoMTidO#s(3yh9d#vEpviP2r(9;M_BXkZ=fYCF64Mycc8CXps%zi!xEGzi3Z_fjs zv13P+CHvL{auS{6T<=}DA zx20MVaZ80Igy-;FKzpy^OA1~B{62i=@iYOye=<^d&3>S8+5W&%<<+`3`{?ArC%SmUnjH_g{rov;R<$JSC*pF52#x}vwoenuRw!)z=x67 z{^)+}ov!qsKY%Cw{70X^!WyoA%-(zrbXBpj$flQ~kw|m*({80&aVxfyo2cvDMGd$% znJd?oopYy3ZcfkDoLs$@8=ZHj@Y$X#)l_cB8{DW4N4-_0ai?CLbM0bfwo%vaNb|nK zVEfW+1to0P)(*<-8~39QHl&h#T~8ipCb#9cJ(z#+4GT5B5o2|hjuYm~i%*v>lqzq% z_14Q}1Uvp%;2<7n2roNQz*<_cF!LuMh>+Kt=eIU=GDc_j)QpIA`t`&<1fP}-YMxxE0XY+pS2>O;T7Hg$hyIAxx#o}yT z%~c6!i^cPErK(BM*`N!p&gfporSRH^N2*R+60Rl?@z%@cKNTrUtBB$sVLn1R+A9{K z3z4Evzh>@`!X~p`o}0C6Zpp1+u-$sDl$)5Vl^M#9fJe|HWGNPhEicXlFNWYUywItV z!v3#begbWnep`Aq;I45Du^B|F8(9#;fg7E+79txW7}aQV&q?9MoT1I;oVMLLT_an& zQ??(QD@}kqC8An#Ty(AGJ>BZ{f)1Kuvyd-9jV?cr79AMW zFt5_l{-_lj#5);n4t8{3vW^TVbRRNmX4k9u+Q`?ve6>g_kW8+dk#G)=a6sBh$24QQ zI6(M=+46XW9FTr3G65l`Vz2flbWwTIT&oIj9RY z$pCS{_&D3~aw8a;$dYx{T7aSwjkqzrX(6T}GijY$h%Uw!VwlVET521a*28rm!YWBtJQGEyVwrD|0Rq!|zMUm)0G!A;> zv6@3|9cXdda&a9_b8<1K`E+y%Lfar{$<)&KBT4u0)fx>Mh}#&4C*bYQCMkQtYaKI+ zbmSw$0-lTu@1}eq?IkXhs&icu zq*oY|Ig3S@fg=w4Lgvgwv(e_dPGb43K$u2HhEpqYr}Ukp@AdhUrfgTM#bO(cwjk9k z2OQu@JWdj?wAJ4Oo@HDY&yuJ3=WN0&;922VF1%?jyhd_CscCU*!t0f3Ww^D2;2d98 z%Euwj%b67;9-|cLU`);C^-jKIDhksLFi2^trDMVi!UoLp5*{Ow!Gq1AHgs0zy#TKx zESJNJn#H8G2pMO(2)rUkvFPlHFb-2PF`@+nii(caDyV9VRSn`b39foE!(!8sk6GvA zU}r&=@*1n7m48rg1=u!H?1q=wSFJd1xjx%C@G8R6n)ca z0D@9jgl_^9LTnVtXCgcus9T&`UV>51@;13>v$|KJ8-aViZD=YkzFX3S@J<!Zo6(4$tV0emy+CN+p}uiab7sx%mL z$Qvq-RW6;ziZNPpr;N60>*iN7Is6^U%_1S!KuXT&qSRCCKAht=fm7L$jabEXa?aei zp{kS5NDXJFDo#$+B^IjMRL+q-H+pPz$AjB*6O|gaF;cu*F9FQwPP#c~sy+r)}Y5HD1!FJMmuT_3x|2|wQsDcB|MDG!g3wiK6U zA5p>dFJc21Fpp*QioTgz{bJ2uH$zFa3VrwEWCygIizx1Yf%O|Mf>O6}B|5iwoCpPf zEW>@!aXikQcr94iZLf!tME(Yv)3DX9i!8*}VgGbql%?y|h!@R6(LRLif_R`DaVmQ~ z6LVGBk2zjC0GFYR<&oPSGkl+8%Lesyw3%BO^;Y&S@{)ChyxbRe;0(OEHPTGg$M6a^ z2kdM&N>qLgO235kK!>^kLaodr9lHf$6Am~RKSRfvr0!n;F0CPt3i*a(M&=PCFHYv~ z!i#+C*$Nd56q1Glj5!#Eh)f-5fbL*khtoWDPIMXrM7uSFLvH@;xG zi6u)PT(GVtvC*@zQ^2$+rVuZ+>_aG+xoN$I-Qnt>PvhmSI414A^D-5uQghr=t!$f} z1|>MR;D)A9a@&}~ERQWELNudZ=Ja{uizqB6p-9xSNUS8*5&UDN!+NqJHu<*V9dsk( zawZCgP+D=py9&C&K!?2m$G`bW5ptB+XT-jcbM>6PIaiqwqXKOOD&PZ%V+!>0Oli{g zWi-n@W9Mo%a=Db(5c*Sf!1Wx)A2JvQ$ugvJ!!GBrvjyzrD2c;HeTlpyzcaVVv26^f+Iq_>#7%%4@0y`Wd{u468DRytaufOe%WKL1g+g9^uQh z6;Gw(_>W~0>14V;-2-ixIm&oC{ohmgOq3%x zb3w*BEXsbsI4jsOq?Z&3E28}z628v%Nl2g_)r|3a%466()Fw|p=~ulr&0u*&`AfHKA0w z-k8_+q?aZD+u0;6W#hBO$=aM3p9IuoFwbCLQO(WH&KI$s+UQ-jBh)|Tx(!U5`b@>1 zE@Au(2%0n{Dc?%Uwql!UyWfB;dfR#47MF+l7jHt)v{*|ViewK!XWO?(!XzY2=F&D9c{ zKYjA_vD0Cs$(m=@e3;c+*H%HTUJDbNYg=_dVzsI<1xz*V1-w;lHM#aB7rt%8O|Ur2 zu*qObGDAm+@seY=mpIIDdkJRn`Wb!V?Ag&VTSN8sX#pld!0S75{A4T58yFJ-+7^>4 zk5PQc4LbsS*0e&yLQ37K|b*7tcT>3>u1jLcIn zb=C*^{TP@DO4a(Lmpm%pW)PqE;|GI$kPx;Xmw61ZpFc>?x1|pSWndfUlv6^^?3f=C zY6p4p9eIM-q9erd+shhK`7H){@*R14gErmDB`Jjsxvzync!0PMgr-`X!tu`1#v%tPN^*XFA3QxVMj@mz~p9Ff$Yz2+`(hvv1CK zMEY5bxCslu^j+7>3z*2DfP}oHsjS)8QMWhfx1sCfbvKY?ZGonrA>S6~^P9&b zc0DFYap$K50x!tuPs>5m=Vwo!Ix~92OwO=`pCxgzQrofJOX4GoMbQ9Fm}6i`$TyP) zeJ>&58|x!f8`|{|Ul-YU+MGg~{W)qZcF=gw+iE8{92JK~jgG+?xgK9pJ8NwMTAJ3E zf!RUmO`^EsT$5q1ca#HvM$Uu0G;ork@OsCvh`2$t#1+pwUQYm3y&mMnnx#uvSOHGg zD>W(au%MSg6wh{1w-5XAq6`!p)j4QAb2XzHWPQLyvtqj@ifTp*LHg=>FD!6iTxWa9~DLlPLsU1{RzP_M3xdfYEAe9(2SR#{a7u zaWhH2j}e!GBl;^Cw-t5LZy;f+ZGahuf_3K9G0s&PCUkMT*Tcom#0^1dazVPY_4?Qp zoD(E=*{se>yUXY#am`LTN!<5Lr`vBdGqjEIa@^+L?|p9gMc;;2-vVtG+GxeFP^jLp zYt4Sc4SByFuU|IdI3i*ybxrEvDhzQG$=&RB6cYitJ&2L*Pea!)!F$9@oiRP~`k$0) zj$%g#{aJhKI3P#ui4wFh8G#eqq+|N2nSVU0bZI9vh<1q z5g726?Mn=GJAkSmVvP#)XU{G2xJ~>Wm|QmRGzZiBKn`M ziOn>~Jji;4+$}QpVg%s}p({2H+9|}!Rl8K1Yj{1FM*iUUKL&Hmd@G!7ko3jz`37j* zT+^}41swF|P=}NWSi2)O$ooCdpxljTkkf#oWH%rFex{Hq`!;iE73R+`w#^}iy%fx^ z@Li>WsH6!^dwTZfyK{NqmZ?0WVJX7?|HOr{ycdH{B*oxLUW3pxSE6mHqLJZiUCRs6bIBJPy$d!pd_GPIAEu7o|9EQFvMg$ zoY0F4)^g>idGZLh+^(G~b*VQw%2NmCw!p{EcI;W4w>V-t3@m%CT>9%kTz`|Veh^R( z@wE=Gj^k#8xcfQuN}Hn3AS^K}bfxAGZ_UB%(k;n7qw{K5^C{$!W{q7jf`GQzQJ zEJ0)a8Dp%+K1#eJ_S3%#e(K_1b*k;i0?tHh+YT-KqYJSeHg7p&6Ccy730sNE2 zhHlQ4r^JT1Id_ldd20>`nV#NB=u3Ffz^q{Mf@cZs{o-`Cyz9MM8j{|Blf9=^IAU3_ z&cpRejx_Wgh-Y22x54ZOh)$S*U=5^1ozrx0L^QQ#KsX_RYEEr}-eB~esn*9~Q$!$D znU!h^1XxQUE4BL77@MBdQ|Oa~pdvq?UsKn(aLi;}$c4r+C^+_@TOjN>@UXBdT|Q?6 zC3S(P8%5ehOHhV-QIkJ;c)rxl0U<}yZO;|olUEHFz`>DTC-i|Dj*WDnNc3WO96l@v zFOz{qoT}2#WF~nIuCM2Dip5h4p(9Rji$$8#aqxpUy$eOW#0>rGmJeXhzDekWmm2!O z5K$+3aUAP4Z-I+RwFHsX=57+?gmF4ns?B?GcfJ9KKKQ8WIvtkuT`0Z0fpk#O>Ne8s zScx=%GS5_ZfT zR4j8xXl2l&M=|Mj4KpTiH{6DNoI9NB5O^ysj`t(WmKKi9zM#E43&C-Fxh;bQ+Bzy- zma*NBga?9}V9Q{*x#KvF1?j&m=MrGzTz$^vQ4dZQ>y86=jrp9<=V6wPo{~nKI!};J zhAZgQFoA)1z1_7EM-w2F7oV}`o$Hnt#qr9_#Wp;NqL*eK<~R`G8Hwtn!1McRQa_54 zUQ*m69eoIaIm(jcqiyY-c!%xLBkyh6ljP%V?LAIX z(5$78@pXKq=3~K_W&u!om=O(l*9^-_M3wOxZMD`lblb&0hLK#lA=u_cDYG^Laglg)f@h{KIUc9*StmQT>xy%7SKgQb)pc zE$R7ubI02iF)ef#di3oIZ9+qvb5e8|E(R67LaT?ra|O?v#$UWq9UZ&@LbYUOM!4=O zR29lg`o*MZd#r8Q2AwErY@Jx2qg{(-bbO{oZT%D~?P`P#iD2$t^7WHQTrMS|bG5cA zp(#h2)`7#`02CXXOCN!L;2z^8nKxK*VbDaMDOF%TJ!4No%_*$*<0N#tfP3wghn(-I#SLmOsRYX(9rIAv@ zhRO%kNMg9(YgDo6T#0tvS47_bHb_ty$XHO!&>e=qCL-FXHKIvR3~~%MIE=bA-Zj|i z@p;$Yj^m&YwVKc`pjemU($x-~l|s*d{T#}mPqFd22`^e%&Hx#@HeO;|&g*Rr6;Y_* zbHPc9eBpv-_AoN$V9|$OP;F*;WEg}x#(*gCc@j^!a=P#Xf<|S%-liqwkpZ68hBsR5 zcHOu!dGV5-#M$}GMSho|uE&7mkGF7s2M7%M2(&S_E}{12Ek2F7PyUs(mRt+u?leaS9&`3r_)Pyb2y+ta}s-hxZwX%8PvYo$FK4f|t=yzaCffQnU z7y)1#1-AE7yQMjamuseZYXBQB#NUfhY|(ltR?GS^yWtoP7duShsBdkEH*Y-#s^DUi zxqgXru~N$hlpoP&fkpE+QZ8pU!#p~Z?XOeZoP*~`*(u`668-RCUBSi&XGgR2WC^y& z@Iz%TY?k!|>Kcw0lBS|U%C80yQjM38vdd|dVB^KlkwTP3yyTVvjYubRy+_xH<6i8m zaqCXVxyfz#>(l0(JHDTuU&9ZJmzpH!I+8QkdXTi5v~6mshGT+Lu=?yKS)(@R2p7$Y zXDWirT`P`WV{F7EcB?Fkva+?q!EJZgqf4^CNb8Eu4rWM9&ech;OdGGWp{VDpybHR}VB%MoRP zDR1WStz4nt-JSHJr4n>^MowBpE#6x|z8?KqiZqe+H+P=JRtwiFwm9y5)y@ft!+n)p zl@49vcZy5=_WZ6q?zh6I-EExVP^{)qD7?6Ka`A8md7%v5K+I)a%>#MQNg4@#?s8E_e+;GL!5eMv{pCKcqMO#CH=0c`Ngt>SloLWR&m0b8$Ar1?@HPJ8} zD*M%WME)MRlD4M^BtqK4^Oj|$|H6u8<(>&#m)8gl+wLbx&5r>IyK%O8E8e-;Io!m> z4Ft~L9B!kRasi^aBHg?duN!7*-d(9-N>@}43Ng(Q=PnLq*W*Xm&a({LRC|5PhAI9G|SU%!jVvdw=FA=yYL4OM=d z#O#3mo0K4Q+!wqVF@DSaO2iF_NX|y}Wi%5=RPtblj9>&G(Q0@lyj?~tLbtgpcV56; zF?fO_^>xmen0^Qg7#S1atBGqcl?lVeM}(2EZovCt>M`8ujbQ;YKH;yhx9`QvOBSWO z`U6BP-bPHGR!i(M!6#kl#cJTM$o(LM+>2au#w;&1&fAyDc0;b4uee5|v@s057I`iD znl-`S%)^yMuFx`5I#ey-A%`e3U3sUF3;Jd2a^y11Rc5+g#2>U`x!f>_k!-G&id%+O zQ&LMw5b@(A-4(t*#@7$>B~12FLNY-=3}_8&<#${`-{Vg<~`vt7bS~-?}(~bt|T{^&#_v?owd+3@G;y z2}GIYW$1!m6v?O$BNleAgoJRSgLc~oi8jEC#o~Ry-?!v9hrc6&I1rOkD_8-4lt{>U z{cs40WatUIcTdn3Y=J+9Ox`N&Veo4kFe#W@@uox2B3J_}gs%G|gz>XQ5h>Evf>dsF zpWDFa}k-yc4zQOhQ@3I-HQ&;ov2WAnJOwZD?eka_;6i z6uiO3g_M%tDA-vDK3oUCP0kX#r$UEJFkI3m34WRg%eeP-l;0cBXIH=^^n4F#+5OiJI1n^BO0(=l_*2*})Wm3!cjkO)yel(0fXMDIS-#vJm~GElX# zIyyPrd-rV?MNy?N5WEcUUx{&G*5-Z1P~Vcuo^FrZfs@Se2G$h#Ew~8DlgtKxmnJMN z4eXGsG+;{HKbnMgM|(+*BexkS5+@{eB4IWTt=|!$senI(13?eFyIkODaX$178tRR- zl9D_HlRh<{^M3=UxIOH2>%L{KT&nR78B`!vT;biIdhQ+v5#h&S%B0w3e%&994qR=eC=PlsU(rLJgh(MkGxSfVdU^i)4%wNB`f zGcsI4&24Tj(sD-+hlS$qB6aU%?8AH=077{R}PI^7H-6dV@r27@#4CpmCZO+b>|a5rMts? zEw`iYtn1lfx^oy$hKwAF%26{|1}p58cp|_!)!~Ye@q@gw)^A|UXz^+YvijZ#`hM0; zZvn4o1oj*=G&>l{Ltt}g%41i(moFX`!tD${3IP3^2YIXU^$%*L9V*J+s1jaZ`)2}&R zloLIfNSeO^q+cM%q-X+mPClvM&3uY48!s;fM=2MVRW!8_U10neU$mKO8Z*rnxyV$S zyu?B%<`~(r=no;zTm8(LL!+a|&J+)gjh#F`a{AcF*)eZb5c%}z(L-mCjfpBQDwiz4 zBC@nNNI}T>H!|tk5|C7skDq7tH-{Jh!C@s_{t(+9hZnx=RPE&cr+$^_aq9_s$(1+o zM3Nhn4rhs$q)qRel^K2jMIz3(AsORqvoQ4e|J-TD>Ao`vC@KGlvCI!ja7T*0tCfij z#OdTSVD&=~Td_1QJPzD6ux4=8z=nYX>DWMu7j*fb@xT4iwSc*~Gyf*lv>C*${0;I@ z;CKEfqZW)1=5108EZCd8zZO55g(Fh>tz?|P29*UCD1qz4+1Nm`v$pRLdLzCe9UYX? zaBYfMZ^h$&4Sya_+#qEczTcsR&fH&%-xbt~-&Tsm(capmp<4f+#gpA>*|*{t3xi=$ zVtrqKlqIu?Y_E(ZsKtMdX3~d~G0+D#1&nt9#gm{9s3cD|mW{1VVBDpqwm!s|%l`*Y z^do;OlqHQ#8FCbGklcXfSGmwvV^TKw4EZ>vt!zBY{H#kXCCtZvju_JLpN#ffgXw{6 wCb2e}<%75Z)R*10mgQ1eYe?E1NGD_2Y<3{KhPa~zaKK<<0K5QhM*g_ 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 ".". + 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 + + for a discussion of issues. + + Typical user data directories are: + Mac OS X: ~/Library/Application Support/ + 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 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 ".". + 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/', + if XDG_DATA_DIRS is not set + + Typical site data directories are: + Mac OS X: /Library/Application Support/ + Unix: /usr/local/share/ or /usr/share/ + Win XP: C:\Documents and Settings\All Users\Application Data\\ + Vista: (Fail! "C:\ProgramData" is a hidden *system* directory on Vista.) + Win 7: C:\ProgramData\\ # 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 ".". + 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 + + for a discussion of issues. + + Typical user config directories are: + Mac OS X: same as user_data_dir + Unix: ~/.config/ # 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/". + """ + 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 ".". + 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/', if XDG_CONFIG_DIRS is not set + + Typical site config directories are: + Mac OS X: same as site_data_dir + Unix: /etc/xdg/ or $XDG_CONFIG_DIRS[i]/ 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 ".". + 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/ + Unix: ~/.cache/ (XDG default) + Win XP: C:\Documents and Settings\\Local Settings\Application Data\\\Cache + Vista: 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` above). 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. + 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 ".". + 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 + + for a discussion of issues. + + Typical user state directories are: + Mac OS X: same as user_data_dir + Unix: ~/.local/state/ # or in $XDG_STATE_HOME, if defined + Win *: same as user_data_dir + + For Unix, we follow this Debian proposal + to extend the XDG spec and support $XDG_STATE_HOME. + + That means, by default "~/.local/state/". + """ + 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 ".". + 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/ + Unix: ~/.cache//log # or under $XDG_CACHE_HOME if defined + Win XP: C:\Documents and Settings\\Local Settings\Application Data\\\Logs + Vista: C:\Users\\AppData\Local\\\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 + # . + 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 + # . + 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 + # . + 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/venv/lib/python3.8/site-packages/pkg_resources/_vendor/packaging/__about__.py b/venv/lib/python3.8/site-packages/pkg_resources/_vendor/packaging/__about__.py new file mode 100644 index 0000000..95d330e --- /dev/null +++ b/venv/lib/python3.8/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/venv/lib/python3.8/site-packages/pkg_resources/_vendor/packaging/__init__.py b/venv/lib/python3.8/site-packages/pkg_resources/_vendor/packaging/__init__.py new file mode 100644 index 0000000..5ee6220 --- /dev/null +++ b/venv/lib/python3.8/site-packages/pkg_resources/_vendor/packaging/__init__.py @@ -0,0 +1,14 @@ +# This file is dual licensed under the terms of the Apache License, Version +# 2.0, and the BSD License. See the LICENSE file in the root of this repository +# for complete details. +from __future__ import absolute_import, division, print_function + +from .__about__ import ( + __author__, __copyright__, __email__, __license__, __summary__, __title__, + __uri__, __version__ +) + +__all__ = [ + "__title__", "__summary__", "__uri__", "__version__", "__author__", + "__email__", "__license__", "__copyright__", +] diff --git a/venv/lib/python3.8/site-packages/pkg_resources/_vendor/packaging/__pycache__/__about__.cpython-38.pyc b/venv/lib/python3.8/site-packages/pkg_resources/_vendor/packaging/__pycache__/__about__.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..bfdd1b84e0944869f63c65eeb756e055f810bcfc GIT binary patch literal 759 zcmYk4OK%e~5P)~{XkJa50_qLP6mf_|;xsBzK?oJvBSMu59QI;4JKl}eUEA_wtL%*{ z|Ac?TIaf~n1r7*10hJl~TaPs!&tpFghyBRQ)5l-odn1Z|1ml0{dgH<`bL{~U#6?ub zNnAFPM%hf7aRd!$LJQik2Oa1_FYhKT=qGI$Bzv%*bYPft;UMY3VbX_DGJqr2JC2fl zcyJSqAO7@Fqj7U{NS8(y&N9MFCAHmjfmhscDK-a6b74v5L~4WGBgArBFhVv1LX0a* zs@LA=5aKl7omNbTyWZ#%LY-Yn?f3pBD=9B_J_S#iFd@v4kW{L*=K0D7H$~H1nsbrY zCznz)RZaGb5iPc(Bd>^KDx|p8V<0~m> z0qDlMEVGCTKwN|}!O;SxQdrHG&Pr|S5o}#A%+__vWqtDQ<_h`O-};H zk_-G1E3PI^gdQ=N+^rZZCd>2LX}YLttZs9nnUPMX%wSS60aD-h6Z?#p()n3U)q2x? gT}p5TdlRZLCmxTQjp(n&+wbpIyA^wFcnysI01$ECP5=M^ literal 0 HcmV?d00001 diff --git a/venv/lib/python3.8/site-packages/pkg_resources/_vendor/packaging/__pycache__/__init__.cpython-38.pyc b/venv/lib/python3.8/site-packages/pkg_resources/_vendor/packaging/__pycache__/__init__.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..1be2f4851a664a55216b49daa01bfd0432ef5728 GIT binary patch literal 597 zcmYk4zi!(w5XLFV)<0V*3S{lnr5;?mL2INyQ1l79G+nq5v`EWDNQ$6H1riX5RIc*1vgpAP!bG&O>F`{pN4QiPsj@-j+DzQQxy0yyG{LOLOdPD!dW zk|l_zJX140Cv%;XTo#ZhA+RPRr3s; z(JGr>vE9(C;02Z1_Rdcwm!lkHZ>9_Dr15kgOzR-|nB(Gt+%lqx$Rpxf zToq9OzO62zmwoWTK{E%7L@tY%0iqYS>jT(27srwp9ZvmEll~8hc9;V5zmR(!)e*lR zF|1)^Uf_{AyzdnUqu921dvC{&SLXAT=X|>xt~Y!Apg(O+(HVS;t+eZ4ENkxsU*8{u zQ0v|G=0m$3AF#das1rjUTq_1lM`3vHaMZF+nhsMcjRf!S?Qtq^bkBnlU*dNSmw?Ok OU7n(U2mT;9fBpg?Af77# literal 0 HcmV?d00001 diff --git a/venv/lib/python3.8/site-packages/pkg_resources/_vendor/packaging/__pycache__/_compat.cpython-38.pyc b/venv/lib/python3.8/site-packages/pkg_resources/_vendor/packaging/__pycache__/_compat.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..6f66b8130dfbe130dc3c4705202fd513cccf0afc GIT binary patch literal 1033 zcmah{-D?v;5Z{kWa*b&!SQNh?A_#eCu2wA-N)Z%%D}o>3AuN~MO?vje+}%q^sQA!A zi+_ba_FvjppZYKO zBuz<1Lz=OWWjy2*b1d~jPkEmklH4YeAF{A{6?qV~R7<~7{)mPvc~iCVD;u(TLc(j3 zy(RtD1z?jtcWcqm6lrCZNV2lf*7-7-Bqk|x*DZCDTQREg*h0L2fy6F=F^MVOz7$L} zL0h2a5t!HHl$_ELlT@-3U$QfL%)c^Ae$XQ(x%3k9(*p{PdC=~GywobPsu%T!kx{)k zjg09{6MF!eOxdVT4C<3Hfd3;{8pYfJqew_43E6V-kV_7wy!it}6o2t{(;|*ivp2X( z#a<1yFzBP+05u&jXY`1kkt2SLaCHA1FddImwBFx$Yv|DjK}ARH8=j{@Vy1G4(=QH@UtA zQNM$vN0qHKkk;70#^#@ gQ=>2F;GUN{_IDRE+QSkC`L8pdcWKx2sLz`G4|tpg9{>OV literal 0 HcmV?d00001 diff --git a/venv/lib/python3.8/site-packages/pkg_resources/_vendor/packaging/__pycache__/_structures.cpython-38.pyc b/venv/lib/python3.8/site-packages/pkg_resources/_vendor/packaging/__pycache__/_structures.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..99380712b59fe609562416755897fded9cca31e4 GIT binary patch literal 2815 zcmcIm&2HO95MGiJMai-)Ck~plb&DW}LO>n5Mq@Z=5x8j4AQxTYTy$B`oR!Q(BAMN# zf(W@OhahjFSK_s&zCur(S%s-=lad=GE9?(Bv-|D*eblqf%@)IUZ|^<4t26c&JuJ5x zKAd1zf8YQNtPvYqeQRv@?XlB$EcTdz4bEEzj;PI?eqGdOtY62sHK^m;x_Rpg7vH+z zK4sm;-*^)1I{9{Rrjk*n1&_vQB6aRTG>KG{#Q9b#qgeAn7Ka+2L)x+2Y?f)tI`)PKjW$T?u4w%wEHJT_-@E3YXr8b}u(&G666M`I>|7XBoV z{va8FkjhU(|L9dZ`S~z@c&Gu6&eZY2U_2c^I52G(`=@>yrTbZ&2H^_<`>)Q0810`O zAN&*^O{RYOV#uXXNhU*~d_EB|B+@6{U>Ldo!Eb{?qnyhcaM25S6gwj>~eB@NDLdl~FP z8k!5C3N>0^@$lU4{b>%L+tNdYyq@TDAur>(oR2i;+o(~XZ7&%~vh`T9gWDG;?|fXD zcp94sLvVhh3Ik8kPCFi#) z@JUvUzlFOrEAZRw!wn>zR)rR$MYk)^NOlFSEPAIa9XiCod{Y5Ivh@)1YqG#ZslsVP zyy|yV7-R-ryJ)n&V1_PS)wS|YPYeScO+=AerMchWJQhRF@8VHKGIoj`SaC+!o42V* z;$Vz7$XlF`6UZpN@;2u$vtYE4NFr%TX)SNkuuB8QMBbr+TqnP!fec(KijP@q=!q&+ zQQmsC=Un_5d5EG5jY{zZ`xm5HxrgVkcKrv2oi_Ii?=37WAspkUF5TELiZ#7+IaHrO zb*4roF<4ogO;mnd19@GVTb;!w_WlyoS0&b6ljByFi^;FUbwE@z#?7cmjn=D)u99X< zEp)~1~ W%DrC~ton%dDEj-b>vZ;9?EeBCEBB=U literal 0 HcmV?d00001 diff --git a/venv/lib/python3.8/site-packages/pkg_resources/_vendor/packaging/__pycache__/markers.cpython-38.pyc b/venv/lib/python3.8/site-packages/pkg_resources/_vendor/packaging/__pycache__/markers.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..b7f61a3965fe6af51e0383b1ffc00a5536e12a38 GIT binary patch literal 8971 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 literal 0 HcmV?d00001 diff --git a/venv/lib/python3.8/site-packages/pkg_resources/_vendor/packaging/__pycache__/requirements.cpython-38.pyc b/venv/lib/python3.8/site-packages/pkg_resources/_vendor/packaging/__pycache__/requirements.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..b1460eea2cf98be79d133dac0fb721adebdf2135 GIT binary patch literal 3930 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 literal 0 HcmV?d00001 diff --git a/venv/lib/python3.8/site-packages/pkg_resources/_vendor/packaging/__pycache__/specifiers.cpython-38.pyc b/venv/lib/python3.8/site-packages/pkg_resources/_vendor/packaging/__pycache__/specifiers.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..e64c6a7f7b8a94f7042c8ef3f5a41f93efc9b098 GIT binary patch literal 19839 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 Q#_;HQBW)ZSB_GGcnU$VU$h@hk5S0n`m zf5|Nsf8i?ZE70+zd72r|=*^7ZpPtT9#^Xmn#7~US?-JZZm4nyW!YY$M0xP6(i#2J9 zuG$Kh98q;!Lw%Tt>WJH^sKw+vYEJ}tix$&AxeqO>bhq0CtA+?nYG-{+Ga*MAq&4Y| z^U_4N9Sn~-zSvJcH0PrO3q~>j^WJ1Ije=$pQmd_Ih4~+Tp;>*AIa*sqjq!VtnlM4K_ eq)X5BYB%UsDV@F4RtySWmBvtNACg%$ul@ooUx&K@ literal 0 HcmV?d00001 diff --git a/venv/lib/python3.8/site-packages/pkg_resources/_vendor/packaging/__pycache__/version.cpython-38.pyc b/venv/lib/python3.8/site-packages/pkg_resources/_vendor/packaging/__pycache__/version.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..7c8247e9354e0ab24ffedadde156ed89a72b15bf GIT binary patch literal 10686 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".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 "".format(str(self)) + + def evaluate(self, environment=None): + """Evaluate a marker. + + Return the boolean from evaluating the given marker against the + environment. environment is an optional argument to override all or + part of the determined environment. + + The environment is determined from the current Python process. + """ + current_environment = default_environment() + if environment is not None: + current_environment.update(environment) + + return _evaluate_markers(self._markers, current_environment) diff --git a/venv/lib/python3.8/site-packages/pkg_resources/_vendor/packaging/requirements.py b/venv/lib/python3.8/site-packages/pkg_resources/_vendor/packaging/requirements.py new file mode 100644 index 0000000..0c8c4a3 --- /dev/null +++ b/venv/lib/python3.8/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 "".format(str(self)) diff --git a/venv/lib/python3.8/site-packages/pkg_resources/_vendor/packaging/specifiers.py b/venv/lib/python3.8/site-packages/pkg_resources/_vendor/packaging/specifiers.py new file mode 100644 index 0000000..7f5a76c --- /dev/null +++ b/venv/lib/python3.8/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(==|!=|<=|>=|<|>)) + \s* + (?P + [^,;\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(~=|==|!=|<=|>=|<|>|===)) + (?P + (?: + # 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. + (?=": "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 "".format(str(self), pre) + + def __str__(self): + return ",".join(sorted(str(s) for s in self._specs)) + + def __hash__(self): + return hash(self._specs) + + def __and__(self, other): + if isinstance(other, string_types): + other = SpecifierSet(other) + elif not isinstance(other, SpecifierSet): + return NotImplemented + + specifier = SpecifierSet() + specifier._specs = frozenset(self._specs | other._specs) + + if self._prereleases is None and other._prereleases is not None: + specifier._prereleases = other._prereleases + elif self._prereleases is not None and other._prereleases is None: + specifier._prereleases = self._prereleases + elif self._prereleases == other._prereleases: + specifier._prereleases = self._prereleases + else: + raise ValueError( + "Cannot combine SpecifierSets with True and False prerelease " + "overrides." + ) + + return specifier + + def __eq__(self, other): + if isinstance(other, string_types): + other = SpecifierSet(other) + elif isinstance(other, _IndividualSpecifier): + other = SpecifierSet(str(other)) + elif not isinstance(other, SpecifierSet): + return NotImplemented + + return self._specs == other._specs + + def __ne__(self, other): + if isinstance(other, string_types): + other = SpecifierSet(other) + elif isinstance(other, _IndividualSpecifier): + other = SpecifierSet(str(other)) + elif not isinstance(other, SpecifierSet): + return NotImplemented + + return self._specs != other._specs + + def __len__(self): + return len(self._specs) + + def __iter__(self): + return iter(self._specs) + + @property + def prereleases(self): + # If we have been given an explicit prerelease modifier, then we'll + # pass that through here. + if self._prereleases is not None: + return self._prereleases + + # If we don't have any specifiers, and we don't have a forced value, + # then we'll just return None since we don't know if this should have + # pre-releases or not. + if not self._specs: + return None + + # Otherwise we'll see if any of the given specifiers accept + # prereleases, if any of them do we'll return True, otherwise False. + return any(s.prereleases for s in self._specs) + + @prereleases.setter + def prereleases(self, value): + self._prereleases = value + + def __contains__(self, item): + return self.contains(item) + + def contains(self, item, prereleases=None): + # Ensure that our item is a Version or LegacyVersion instance. + if not isinstance(item, (LegacyVersion, Version)): + item = parse(item) + + # Determine if we're forcing a prerelease or not, if we're not forcing + # one for this particular filter call, then we'll use whatever the + # SpecifierSet thinks for whether or not we should support prereleases. + if prereleases is None: + prereleases = self.prereleases + + # We can determine if we're going to allow pre-releases by looking to + # see if any of the underlying items supports them. If none of them do + # and this item is a pre-release then we do not allow it and we can + # short circuit that here. + # Note: This means that 1.0.dev1 would not be contained in something + # like >=1.0.devabc however it would be in >=1.0.debabc,>0.0.dev0 + if not prereleases and item.is_prerelease: + return False + + # We simply dispatch to the underlying specs here to make sure that the + # given version is contained within all of them. + # Note: This use of all() here means that an empty set of specifiers + # will always return True, this is an explicit design decision. + return all( + s.contains(item, prereleases=prereleases) + for s in self._specs + ) + + def filter(self, iterable, prereleases=None): + # Determine if we're forcing a prerelease or not, if we're not forcing + # one for this particular filter call, then we'll use whatever the + # SpecifierSet thinks for whether or not we should support prereleases. + if prereleases is None: + prereleases = self.prereleases + + # If we have any specifiers, then we want to wrap our iterable in the + # filter method for each one, this will act as a logical AND amongst + # each specifier. + if self._specs: + for spec in self._specs: + iterable = spec.filter(iterable, prereleases=bool(prereleases)) + return iterable + # If we do not have any specifiers, then we need to have a rough filter + # which will filter out any pre-releases, unless there are no final + # releases, and which will filter out LegacyVersion in general. + else: + filtered = [] + found_prereleases = [] + + for item in iterable: + # Ensure that we some kind of Version class for this item. + if not isinstance(item, (LegacyVersion, Version)): + parsed_version = parse(item) + else: + parsed_version = item + + # Filter out any item which is parsed as a LegacyVersion + if isinstance(parsed_version, LegacyVersion): + continue + + # Store any item which is a pre-release for later unless we've + # already found a final version or we are accepting prereleases + if parsed_version.is_prerelease and not prereleases: + if not filtered: + found_prereleases.append(item) + else: + filtered.append(item) + + # If we've found no items except for pre-releases, then we'll go + # ahead and use the pre-releases + if not filtered and found_prereleases and prereleases is None: + return found_prereleases + + return filtered diff --git a/venv/lib/python3.8/site-packages/pkg_resources/_vendor/packaging/utils.py b/venv/lib/python3.8/site-packages/pkg_resources/_vendor/packaging/utils.py new file mode 100644 index 0000000..942387c --- /dev/null +++ b/venv/lib/python3.8/site-packages/pkg_resources/_vendor/packaging/utils.py @@ -0,0 +1,14 @@ +# This file is dual licensed under the terms of the Apache License, Version +# 2.0, and the BSD License. See the LICENSE file in the root of this repository +# for complete details. +from __future__ import absolute_import, division, print_function + +import re + + +_canonicalize_regex = re.compile(r"[-_.]+") + + +def canonicalize_name(name): + # This is taken from PEP 503. + return _canonicalize_regex.sub("-", name).lower() diff --git a/venv/lib/python3.8/site-packages/pkg_resources/_vendor/packaging/version.py b/venv/lib/python3.8/site-packages/pkg_resources/_vendor/packaging/version.py new file mode 100644 index 0000000..83b5ee8 --- /dev/null +++ b/venv/lib/python3.8/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 "".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[0-9]+)!)? # epoch + (?P[0-9]+(?:\.[0-9]+)*) # release segment + (?P

                                          # pre-release
+            [-_\.]?
+            (?P(a|b|c|rc|alpha|beta|pre|preview))
+            [-_\.]?
+            (?P[0-9]+)?
+        )?
+        (?P                                         # post release
+            (?:-(?P[0-9]+))
+            |
+            (?:
+                [-_\.]?
+                (?Ppost|rev|r)
+                [-_\.]?
+                (?P[0-9]+)?
+            )
+        )?
+        (?P                                          # dev release
+            [-_\.]?
+            (?Pdev)
+            [-_\.]?
+            (?P[0-9]+)?
+        )?
+    )
+    (?:\+(?P[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 "".format(repr(str(self)))
+
+    def __str__(self):
+        parts = []
+
+        # Epoch
+        if self._version.epoch != 0:
+            parts.append("{0}!".format(self._version.epoch))
+
+        # Release segment
+        parts.append(".".join(str(x) for x in self._version.release))
+
+        # Pre-release
+        if self._version.pre is not None:
+            parts.append("".join(str(x) for x in self._version.pre))
+
+        # Post-release
+        if self._version.post is not None:
+            parts.append(".post{0}".format(self._version.post[1]))
+
+        # Development release
+        if self._version.dev is not None:
+            parts.append(".dev{0}".format(self._version.dev[1]))
+
+        # Local version segment
+        if self._version.local is not None:
+            parts.append(
+                "+{0}".format(".".join(str(x) for x in self._version.local))
+            )
+
+        return "".join(parts)
+
+    @property
+    def public(self):
+        return str(self).split("+", 1)[0]
+
+    @property
+    def base_version(self):
+        parts = []
+
+        # Epoch
+        if self._version.epoch != 0:
+            parts.append("{0}!".format(self._version.epoch))
+
+        # Release segment
+        parts.append(".".join(str(x) for x in self._version.release))
+
+        return "".join(parts)
+
+    @property
+    def local(self):
+        version_string = str(self)
+        if "+" in version_string:
+            return version_string.split("+", 1)[1]
+
+    @property
+    def is_prerelease(self):
+        return bool(self._version.dev or self._version.pre)
+
+    @property
+    def is_postrelease(self):
+        return bool(self._version.post)
+
+
+def _parse_letter_version(letter, number):
+    if letter:
+        # We consider there to be an implicit 0 in a pre-release if there is
+        # not a numeral associated with it.
+        if number is None:
+            number = 0
+
+        # We normalize any letters to their lower case form
+        letter = letter.lower()
+
+        # We consider some words to be alternate spellings of other words and
+        # in those cases we want to normalize the spellings to our preferred
+        # spelling.
+        if letter == "alpha":
+            letter = "a"
+        elif letter == "beta":
+            letter = "b"
+        elif letter in ["c", "pre", "preview"]:
+            letter = "rc"
+        elif letter in ["rev", "r"]:
+            letter = "post"
+
+        return letter, int(number)
+    if not letter and number:
+        # We assume if we are given a number, but we are not given a letter
+        # then this is using the implicit post release syntax (e.g. 1.0-1)
+        letter = "post"
+
+        return letter, int(number)
+
+
+_local_version_seperators = re.compile(r"[\._-]")
+
+
+def _parse_local_version(local):
+    """
+    Takes a string like abc.1.twelve and turns it into ("abc", 1, "twelve").
+    """
+    if local is not None:
+        return tuple(
+            part.lower() if not part.isdigit() else int(part)
+            for part in _local_version_seperators.split(local)
+        )
+
+
+def _cmpkey(epoch, release, pre, post, dev, local):
+    # When we compare a release version, we want to compare it with all of the
+    # trailing zeros removed. So we'll use a reverse the list, drop all the now
+    # leading zeros until we come to something non zero, then take the rest
+    # re-reverse it back into the correct order and make it a tuple and use
+    # that for our sorting key.
+    release = tuple(
+        reversed(list(
+            itertools.dropwhile(
+                lambda x: x == 0,
+                reversed(release),
+            )
+        ))
+    )
+
+    # We need to "trick" the sorting algorithm to put 1.0.dev0 before 1.0a0.
+    # We'll do this by abusing the pre segment, but we _only_ want to do this
+    # if there is not a pre or a post segment. If we have one of those then
+    # the normal sorting rules will handle this case correctly.
+    if pre is None and post is None and dev is not None:
+        pre = -Infinity
+    # Versions without a pre-release (except as noted above) should sort after
+    # those with one.
+    elif pre is None:
+        pre = Infinity
+
+    # Versions without a post segment should sort before those with one.
+    if post is None:
+        post = -Infinity
+
+    # Versions without a development segment should sort after those with one.
+    if dev is None:
+        dev = Infinity
+
+    if local is None:
+        # Versions without a local segment should sort before those with one.
+        local = -Infinity
+    else:
+        # Versions with a local segment need that segment parsed to implement
+        # the sorting rules in PEP440.
+        # - Alpha numeric segments sort before numeric segments
+        # - Alpha numeric segments sort lexicographically
+        # - Numeric segments sort numerically
+        # - Shorter versions sort before longer versions when the prefixes
+        #   match exactly
+        local = tuple(
+            (i, "") if isinstance(i, int) else (-Infinity, i)
+            for i in local
+        )
+
+    return epoch, release, pre, post, dev, local
diff --git a/venv/lib/python3.8/site-packages/pkg_resources/_vendor/pyparsing.py b/venv/lib/python3.8/site-packages/pkg_resources/_vendor/pyparsing.py
new file mode 100644
index 0000000..cf75e1e
--- /dev/null
+++ b/venv/lib/python3.8/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{", !"}), built up using L{Word}, L{Literal}, and L{And} elements 
+(L{'+'} 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} 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{'+'}, L{'|'}, L{'^'}, and L{'&'} 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 "
+
+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} ('-' 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.} - 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) # ->  ['sldkj', 'lsdkj', 'sldkj']
+            
+            # Use asList() to create an actual list
+            result_list = result.asList()
+            print(type(result_list), result_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)) # ->  (['12', '/', '31', '/', '1999'], {'day': [('1999', 4)], 'year': [('12', 0)], 'month': [('31', 2)]})
+            
+            result_dict = result.asDict()
+            print(type(result_dict), repr(result_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,
+                                                "" ]
+
+        out += [ nl, indent, "" ]
+        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}} for more information
+   on parsing strings containing C{}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} for more information
+   on parsing strings containing C{}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 = ""
+    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,  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 = ""  # 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}} for more information
+        on parsing strings containing C{}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}}.
+        
+        See examples in L{I{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}} 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}})
+         - 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}} 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 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{}s to spaces before parsing the input string.
+        Must be called before C{parseString} when the input grammar contains elements that
+        match C{} 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  at loc (,)"}
+        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 (loc4:
+                    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...)}), these will be preserved as 
+    named parse results.
+
+    Example::
+        realnum = Regex(r"[+-]?\d+\.\d*")
+        date = Regex(r'(?P\d{4})-(?P\d\d?)-(?P\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 = {
+        " " : "",
+        "\t": "",
+        "\n": "",
+        "\r": "",
+        "\f": "",
+        }
+    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):
+            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 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'], {}))
+        <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( "< ['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  bold text  normal text "
+        for tag in ("b","i"):
+            opener,closer = makeHTMLTags(tag)
+            patt = originalTextFor(opener + SkipTo(closer) + closer)
+            print(patt.searchString(src)[0])
+    prints::
+        [' bold text ']
+        ['text']
+    """
+    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{} 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}()}.
+
+    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("")
+
+    openTag = openTag.setResultsName("start"+"".join(resname.replace(":"," ").title().split())).setName("<%s>" % resname)
+    closeTag = closeTag.setResultsName("end"+"".join(resname.replace(":"," ").title().split())).setName("" % 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 = 'More info at the pyparsing wiki page'
+        # 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  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{} or C{
}. + + 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 = ''' +
+ Some text +
1 4 0 1 0
+
1,3 2,3 1,1
+
this has no type
+
+ + ''' + 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 = ''' +
+ Some text +
1 4 0 1 0
+
1,3 2,3 1,1
+
this <div> has no class
+
+ + ''' + 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' + '|'.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"").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}, L{reals}, L{scientific notation}) + - common L{programming identifiers} + - network addresses (L{MAC}, L{IPv4}, L{IPv6}) + - ISO8601 L{dates} and L{datetime} + - L{UUID} + - L{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\d{4})(?:-(?P\d\d)(?:-(?P\d\d))?)?').setName("ISO8601 date") + "ISO8601 date (C{yyyy-mm-dd})" + + iso8601_datetime = Regex(r'(?P\d{4})-(?P\d\d)-(?P\d\d)[T ](?P\d\d):(?P\d\d)(:(?P\d\d(\.\d*)?)?)?(?PZ|[+-]\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 = 'More info at the
pyparsing wiki page' + 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/venv/lib/python3.8/site-packages/pkg_resources/_vendor/six.py b/venv/lib/python3.8/site-packages/pkg_resources/_vendor/six.py new file mode 100644 index 0000000..190c023 --- /dev/null +++ b/venv/lib/python3.8/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 " +__version__ = "1.10.0" + + +# Useful for very coarse version differentiation. +PY2 = sys.version_info[0] == 2 +PY3 = sys.version_info[0] == 3 +PY34 = sys.version_info[0:2] >= (3, 4) + +if PY3: + string_types = str, + integer_types = int, + class_types = type, + text_type = str + binary_type = bytes + + MAXSIZE = sys.maxsize +else: + string_types = basestring, + integer_types = (int, long) + class_types = (type, types.ClassType) + text_type = unicode + binary_type = str + + if sys.platform.startswith("java"): + # Jython always uses 32 bits. + MAXSIZE = int((1 << 31) - 1) + else: + # It's possible to have sizeof(long) != sizeof(Py_ssize_t). + class X(object): + + def __len__(self): + return 1 << 31 + try: + len(X()) + except OverflowError: + # 32-bit + MAXSIZE = int((1 << 31) - 1) + else: + # 64-bit + MAXSIZE = int((1 << 63) - 1) + del X + + +def _add_doc(func, doc): + """Add documentation to a function.""" + func.__doc__ = doc + + +def _import_module(name): + """Import module, returning the module after the last dot.""" + __import__(name) + return sys.modules[name] + + +class _LazyDescr(object): + + def __init__(self, name): + self.name = name + + def __get__(self, obj, tp): + result = self._resolve() + setattr(obj, self.name, result) # Invokes __set__. + try: + # This is a bit ugly, but it avoids running this again by + # removing this descriptor. + delattr(obj.__class__, self.name) + except AttributeError: + pass + return result + + +class MovedModule(_LazyDescr): + + def __init__(self, name, old, new=None): + super(MovedModule, self).__init__(name) + if PY3: + if new is None: + new = name + self.mod = new + else: + self.mod = old + + def _resolve(self): + return _import_module(self.mod) + + def __getattr__(self, attr): + _module = self._resolve() + value = getattr(_module, attr) + setattr(self, attr, value) + return value + + +class _LazyModule(types.ModuleType): + + def __init__(self, name): + super(_LazyModule, self).__init__(name) + self.__doc__ = self.__class__.__doc__ + + def __dir__(self): + attrs = ["__doc__", "__name__"] + attrs += [attr.name for attr in self._moved_attributes] + return attrs + + # Subclasses should override this + _moved_attributes = [] + + +class MovedAttribute(_LazyDescr): + + def __init__(self, name, old_mod, new_mod, old_attr=None, new_attr=None): + super(MovedAttribute, self).__init__(name) + if PY3: + if new_mod is None: + new_mod = name + self.mod = new_mod + if new_attr is None: + if old_attr is None: + new_attr = name + else: + new_attr = old_attr + self.attr = new_attr + else: + self.mod = old_mod + if old_attr is None: + old_attr = name + self.attr = old_attr + + def _resolve(self): + module = _import_module(self.mod) + return getattr(module, self.attr) + + +class _SixMetaPathImporter(object): + + """ + A meta path importer to import six.moves and its submodules. + + This class implements a PEP302 finder and loader. It should be compatible + with Python 2.5 and all existing versions of Python3 + """ + + def __init__(self, six_module_name): + self.name = six_module_name + self.known_modules = {} + + def _add_module(self, mod, *fullnames): + for fullname in fullnames: + self.known_modules[self.name + "." + fullname] = mod + + def _get_module(self, fullname): + return self.known_modules[self.name + "." + fullname] + + def find_module(self, fullname, path=None): + if fullname in self.known_modules: + return self + return None + + def __get_module(self, fullname): + try: + return self.known_modules[fullname] + except KeyError: + raise ImportError("This loader does not know module " + fullname) + + def load_module(self, fullname): + try: + # in case of a reload + return sys.modules[fullname] + except KeyError: + pass + mod = self.__get_module(fullname) + if isinstance(mod, MovedModule): + mod = mod._resolve() + else: + mod.__loader__ = self + sys.modules[fullname] = mod + return mod + + def is_package(self, fullname): + """ + Return true, if the named module is a package. + + We need this method to get correct spec objects with + Python 3.4 (see PEP451) + """ + return hasattr(self.__get_module(fullname), "__path__") + + def get_code(self, fullname): + """Return None + + Required, if is_package is implemented""" + self.__get_module(fullname) # eventually raises ImportError + return None + get_source = get_code # same as get_code + +_importer = _SixMetaPathImporter(__name__) + + +class _MovedItems(_LazyModule): + + """Lazy loading of moved objects""" + __path__ = [] # mark as package + + +_moved_attributes = [ + MovedAttribute("cStringIO", "cStringIO", "io", "StringIO"), + MovedAttribute("filter", "itertools", "builtins", "ifilter", "filter"), + MovedAttribute("filterfalse", "itertools", "itertools", "ifilterfalse", "filterfalse"), + MovedAttribute("input", "__builtin__", "builtins", "raw_input", "input"), + MovedAttribute("intern", "__builtin__", "sys"), + MovedAttribute("map", "itertools", "builtins", "imap", "map"), + MovedAttribute("getcwd", "os", "os", "getcwdu", "getcwd"), + MovedAttribute("getcwdb", "os", "os", "getcwd", "getcwdb"), + MovedAttribute("range", "__builtin__", "builtins", "xrange", "range"), + MovedAttribute("reload_module", "__builtin__", "importlib" if PY34 else "imp", "reload"), + MovedAttribute("reduce", "__builtin__", "functools"), + MovedAttribute("shlex_quote", "pipes", "shlex", "quote"), + MovedAttribute("StringIO", "StringIO", "io"), + MovedAttribute("UserDict", "UserDict", "collections"), + MovedAttribute("UserList", "UserList", "collections"), + MovedAttribute("UserString", "UserString", "collections"), + MovedAttribute("xrange", "__builtin__", "builtins", "xrange", "range"), + MovedAttribute("zip", "itertools", "builtins", "izip", "zip"), + MovedAttribute("zip_longest", "itertools", "itertools", "izip_longest", "zip_longest"), + MovedModule("builtins", "__builtin__"), + MovedModule("configparser", "ConfigParser"), + MovedModule("copyreg", "copy_reg"), + MovedModule("dbm_gnu", "gdbm", "dbm.gnu"), + MovedModule("_dummy_thread", "dummy_thread", "_dummy_thread"), + MovedModule("http_cookiejar", "cookielib", "http.cookiejar"), + MovedModule("http_cookies", "Cookie", "http.cookies"), + MovedModule("html_entities", "htmlentitydefs", "html.entities"), + MovedModule("html_parser", "HTMLParser", "html.parser"), + MovedModule("http_client", "httplib", "http.client"), + MovedModule("email_mime_multipart", "email.MIMEMultipart", "email.mime.multipart"), + MovedModule("email_mime_nonmultipart", "email.MIMENonMultipart", "email.mime.nonmultipart"), + MovedModule("email_mime_text", "email.MIMEText", "email.mime.text"), + MovedModule("email_mime_base", "email.MIMEBase", "email.mime.base"), + MovedModule("BaseHTTPServer", "BaseHTTPServer", "http.server"), + MovedModule("CGIHTTPServer", "CGIHTTPServer", "http.server"), + MovedModule("SimpleHTTPServer", "SimpleHTTPServer", "http.server"), + MovedModule("cPickle", "cPickle", "pickle"), + MovedModule("queue", "Queue"), + MovedModule("reprlib", "repr"), + MovedModule("socketserver", "SocketServer"), + MovedModule("_thread", "thread", "_thread"), + MovedModule("tkinter", "Tkinter"), + MovedModule("tkinter_dialog", "Dialog", "tkinter.dialog"), + MovedModule("tkinter_filedialog", "FileDialog", "tkinter.filedialog"), + MovedModule("tkinter_scrolledtext", "ScrolledText", "tkinter.scrolledtext"), + MovedModule("tkinter_simpledialog", "SimpleDialog", "tkinter.simpledialog"), + MovedModule("tkinter_tix", "Tix", "tkinter.tix"), + MovedModule("tkinter_ttk", "ttk", "tkinter.ttk"), + MovedModule("tkinter_constants", "Tkconstants", "tkinter.constants"), + MovedModule("tkinter_dnd", "Tkdnd", "tkinter.dnd"), + MovedModule("tkinter_colorchooser", "tkColorChooser", + "tkinter.colorchooser"), + MovedModule("tkinter_commondialog", "tkCommonDialog", + "tkinter.commondialog"), + MovedModule("tkinter_tkfiledialog", "tkFileDialog", "tkinter.filedialog"), + MovedModule("tkinter_font", "tkFont", "tkinter.font"), + MovedModule("tkinter_messagebox", "tkMessageBox", "tkinter.messagebox"), + MovedModule("tkinter_tksimpledialog", "tkSimpleDialog", + "tkinter.simpledialog"), + MovedModule("urllib_parse", __name__ + ".moves.urllib_parse", "urllib.parse"), + MovedModule("urllib_error", __name__ + ".moves.urllib_error", "urllib.error"), + MovedModule("urllib", __name__ + ".moves.urllib", __name__ + ".moves.urllib"), + MovedModule("urllib_robotparser", "robotparser", "urllib.robotparser"), + MovedModule("xmlrpc_client", "xmlrpclib", "xmlrpc.client"), + MovedModule("xmlrpc_server", "SimpleXMLRPCServer", "xmlrpc.server"), +] +# Add windows specific modules. +if sys.platform == "win32": + _moved_attributes += [ + MovedModule("winreg", "_winreg"), + ] + +for attr in _moved_attributes: + setattr(_MovedItems, attr.name, attr) + if isinstance(attr, MovedModule): + _importer._add_module(attr, "moves." + attr.name) +del attr + +_MovedItems._moved_attributes = _moved_attributes + +moves = _MovedItems(__name__ + ".moves") +_importer._add_module(moves, "moves") + + +class Module_six_moves_urllib_parse(_LazyModule): + + """Lazy loading of moved objects in six.moves.urllib_parse""" + + +_urllib_parse_moved_attributes = [ + MovedAttribute("ParseResult", "urlparse", "urllib.parse"), + MovedAttribute("SplitResult", "urlparse", "urllib.parse"), + MovedAttribute("parse_qs", "urlparse", "urllib.parse"), + MovedAttribute("parse_qsl", "urlparse", "urllib.parse"), + MovedAttribute("urldefrag", "urlparse", "urllib.parse"), + MovedAttribute("urljoin", "urlparse", "urllib.parse"), + MovedAttribute("urlparse", "urlparse", "urllib.parse"), + MovedAttribute("urlsplit", "urlparse", "urllib.parse"), + MovedAttribute("urlunparse", "urlparse", "urllib.parse"), + MovedAttribute("urlunsplit", "urlparse", "urllib.parse"), + MovedAttribute("quote", "urllib", "urllib.parse"), + MovedAttribute("quote_plus", "urllib", "urllib.parse"), + MovedAttribute("unquote", "urllib", "urllib.parse"), + MovedAttribute("unquote_plus", "urllib", "urllib.parse"), + MovedAttribute("urlencode", "urllib", "urllib.parse"), + MovedAttribute("splitquery", "urllib", "urllib.parse"), + MovedAttribute("splittag", "urllib", "urllib.parse"), + MovedAttribute("splituser", "urllib", "urllib.parse"), + MovedAttribute("uses_fragment", "urlparse", "urllib.parse"), + MovedAttribute("uses_netloc", "urlparse", "urllib.parse"), + MovedAttribute("uses_params", "urlparse", "urllib.parse"), + MovedAttribute("uses_query", "urlparse", "urllib.parse"), + MovedAttribute("uses_relative", "urlparse", "urllib.parse"), +] +for attr in _urllib_parse_moved_attributes: + setattr(Module_six_moves_urllib_parse, attr.name, attr) +del attr + +Module_six_moves_urllib_parse._moved_attributes = _urllib_parse_moved_attributes + +_importer._add_module(Module_six_moves_urllib_parse(__name__ + ".moves.urllib_parse"), + "moves.urllib_parse", "moves.urllib.parse") + + +class Module_six_moves_urllib_error(_LazyModule): + + """Lazy loading of moved objects in six.moves.urllib_error""" + + +_urllib_error_moved_attributes = [ + MovedAttribute("URLError", "urllib2", "urllib.error"), + MovedAttribute("HTTPError", "urllib2", "urllib.error"), + MovedAttribute("ContentTooShortError", "urllib", "urllib.error"), +] +for attr in _urllib_error_moved_attributes: + setattr(Module_six_moves_urllib_error, attr.name, attr) +del attr + +Module_six_moves_urllib_error._moved_attributes = _urllib_error_moved_attributes + +_importer._add_module(Module_six_moves_urllib_error(__name__ + ".moves.urllib.error"), + "moves.urllib_error", "moves.urllib.error") + + +class Module_six_moves_urllib_request(_LazyModule): + + """Lazy loading of moved objects in six.moves.urllib_request""" + + +_urllib_request_moved_attributes = [ + MovedAttribute("urlopen", "urllib2", "urllib.request"), + MovedAttribute("install_opener", "urllib2", "urllib.request"), + MovedAttribute("build_opener", "urllib2", "urllib.request"), + MovedAttribute("pathname2url", "urllib", "urllib.request"), + MovedAttribute("url2pathname", "urllib", "urllib.request"), + MovedAttribute("getproxies", "urllib", "urllib.request"), + MovedAttribute("Request", "urllib2", "urllib.request"), + MovedAttribute("OpenerDirector", "urllib2", "urllib.request"), + MovedAttribute("HTTPDefaultErrorHandler", "urllib2", "urllib.request"), + MovedAttribute("HTTPRedirectHandler", "urllib2", "urllib.request"), + MovedAttribute("HTTPCookieProcessor", "urllib2", "urllib.request"), + MovedAttribute("ProxyHandler", "urllib2", "urllib.request"), + MovedAttribute("BaseHandler", "urllib2", "urllib.request"), + MovedAttribute("HTTPPasswordMgr", "urllib2", "urllib.request"), + MovedAttribute("HTTPPasswordMgrWithDefaultRealm", "urllib2", "urllib.request"), + MovedAttribute("AbstractBasicAuthHandler", "urllib2", "urllib.request"), + MovedAttribute("HTTPBasicAuthHandler", "urllib2", "urllib.request"), + MovedAttribute("ProxyBasicAuthHandler", "urllib2", "urllib.request"), + MovedAttribute("AbstractDigestAuthHandler", "urllib2", "urllib.request"), + MovedAttribute("HTTPDigestAuthHandler", "urllib2", "urllib.request"), + MovedAttribute("ProxyDigestAuthHandler", "urllib2", "urllib.request"), + MovedAttribute("HTTPHandler", "urllib2", "urllib.request"), + MovedAttribute("HTTPSHandler", "urllib2", "urllib.request"), + MovedAttribute("FileHandler", "urllib2", "urllib.request"), + MovedAttribute("FTPHandler", "urllib2", "urllib.request"), + MovedAttribute("CacheFTPHandler", "urllib2", "urllib.request"), + MovedAttribute("UnknownHandler", "urllib2", "urllib.request"), + MovedAttribute("HTTPErrorProcessor", "urllib2", "urllib.request"), + MovedAttribute("urlretrieve", "urllib", "urllib.request"), + MovedAttribute("urlcleanup", "urllib", "urllib.request"), + MovedAttribute("URLopener", "urllib", "urllib.request"), + MovedAttribute("FancyURLopener", "urllib", "urllib.request"), + MovedAttribute("proxy_bypass", "urllib", "urllib.request"), +] +for attr in _urllib_request_moved_attributes: + setattr(Module_six_moves_urllib_request, attr.name, attr) +del attr + +Module_six_moves_urllib_request._moved_attributes = _urllib_request_moved_attributes + +_importer._add_module(Module_six_moves_urllib_request(__name__ + ".moves.urllib.request"), + "moves.urllib_request", "moves.urllib.request") + + +class Module_six_moves_urllib_response(_LazyModule): + + """Lazy loading of moved objects in six.moves.urllib_response""" + + +_urllib_response_moved_attributes = [ + MovedAttribute("addbase", "urllib", "urllib.response"), + MovedAttribute("addclosehook", "urllib", "urllib.response"), + MovedAttribute("addinfo", "urllib", "urllib.response"), + MovedAttribute("addinfourl", "urllib", "urllib.response"), +] +for attr in _urllib_response_moved_attributes: + setattr(Module_six_moves_urllib_response, attr.name, attr) +del attr + +Module_six_moves_urllib_response._moved_attributes = _urllib_response_moved_attributes + +_importer._add_module(Module_six_moves_urllib_response(__name__ + ".moves.urllib.response"), + "moves.urllib_response", "moves.urllib.response") + + +class Module_six_moves_urllib_robotparser(_LazyModule): + + """Lazy loading of moved objects in six.moves.urllib_robotparser""" + + +_urllib_robotparser_moved_attributes = [ + MovedAttribute("RobotFileParser", "robotparser", "urllib.robotparser"), +] +for attr in _urllib_robotparser_moved_attributes: + setattr(Module_six_moves_urllib_robotparser, attr.name, attr) +del attr + +Module_six_moves_urllib_robotparser._moved_attributes = _urllib_robotparser_moved_attributes + +_importer._add_module(Module_six_moves_urllib_robotparser(__name__ + ".moves.urllib.robotparser"), + "moves.urllib_robotparser", "moves.urllib.robotparser") + + +class Module_six_moves_urllib(types.ModuleType): + + """Create a six.moves.urllib namespace that resembles the Python 3 namespace""" + __path__ = [] # mark as package + parse = _importer._get_module("moves.urllib_parse") + error = _importer._get_module("moves.urllib_error") + request = _importer._get_module("moves.urllib_request") + response = _importer._get_module("moves.urllib_response") + robotparser = _importer._get_module("moves.urllib_robotparser") + + def __dir__(self): + return ['parse', 'error', 'request', 'response', 'robotparser'] + +_importer._add_module(Module_six_moves_urllib(__name__ + ".moves.urllib"), + "moves.urllib") + + +def add_move(move): + """Add an item to six.moves.""" + setattr(_MovedItems, move.name, move) + + +def remove_move(name): + """Remove item from six.moves.""" + try: + delattr(_MovedItems, name) + except AttributeError: + try: + del moves.__dict__[name] + except KeyError: + raise AttributeError("no such move, %r" % (name,)) + + +if PY3: + _meth_func = "__func__" + _meth_self = "__self__" + + _func_closure = "__closure__" + _func_code = "__code__" + _func_defaults = "__defaults__" + _func_globals = "__globals__" +else: + _meth_func = "im_func" + _meth_self = "im_self" + + _func_closure = "func_closure" + _func_code = "func_code" + _func_defaults = "func_defaults" + _func_globals = "func_globals" + + +try: + advance_iterator = next +except NameError: + def advance_iterator(it): + return it.next() +next = advance_iterator + + +try: + callable = callable +except NameError: + def callable(obj): + return any("__call__" in klass.__dict__ for klass in type(obj).__mro__) + + +if PY3: + def get_unbound_function(unbound): + return unbound + + create_bound_method = types.MethodType + + def create_unbound_method(func, cls): + return func + + Iterator = object +else: + def get_unbound_function(unbound): + return unbound.im_func + + def create_bound_method(func, obj): + return types.MethodType(func, obj, obj.__class__) + + def create_unbound_method(func, cls): + return types.MethodType(func, None, cls) + + class Iterator(object): + + def next(self): + return type(self).__next__(self) + + callable = callable +_add_doc(get_unbound_function, + """Get the function out of a possibly unbound function""") + + +get_method_function = operator.attrgetter(_meth_func) +get_method_self = operator.attrgetter(_meth_self) +get_function_closure = operator.attrgetter(_func_closure) +get_function_code = operator.attrgetter(_func_code) +get_function_defaults = operator.attrgetter(_func_defaults) +get_function_globals = operator.attrgetter(_func_globals) + + +if PY3: + def iterkeys(d, **kw): + return iter(d.keys(**kw)) + + def itervalues(d, **kw): + return iter(d.values(**kw)) + + def iteritems(d, **kw): + return iter(d.items(**kw)) + + def iterlists(d, **kw): + return iter(d.lists(**kw)) + + viewkeys = operator.methodcaller("keys") + + viewvalues = operator.methodcaller("values") + + viewitems = operator.methodcaller("items") +else: + def iterkeys(d, **kw): + return d.iterkeys(**kw) + + def itervalues(d, **kw): + return d.itervalues(**kw) + + def iteritems(d, **kw): + return d.iteritems(**kw) + + def iterlists(d, **kw): + return d.iterlists(**kw) + + viewkeys = operator.methodcaller("viewkeys") + + viewvalues = operator.methodcaller("viewvalues") + + viewitems = operator.methodcaller("viewitems") + +_add_doc(iterkeys, "Return an iterator over the keys of a dictionary.") +_add_doc(itervalues, "Return an iterator over the values of a dictionary.") +_add_doc(iteritems, + "Return an iterator over the (key, value) pairs of a dictionary.") +_add_doc(iterlists, + "Return an iterator over the (key, [values]) pairs of a dictionary.") + + +if PY3: + def b(s): + return s.encode("latin-1") + + def u(s): + return s + unichr = chr + import struct + int2byte = struct.Struct(">B").pack + del struct + byte2int = operator.itemgetter(0) + indexbytes = operator.getitem + iterbytes = iter + import io + StringIO = io.StringIO + BytesIO = io.BytesIO + _assertCountEqual = "assertCountEqual" + if sys.version_info[1] <= 1: + _assertRaisesRegex = "assertRaisesRegexp" + _assertRegex = "assertRegexpMatches" + else: + _assertRaisesRegex = "assertRaisesRegex" + _assertRegex = "assertRegex" +else: + def b(s): + return s + # Workaround for standalone backslash + + def u(s): + return unicode(s.replace(r'\\', r'\\\\'), "unicode_escape") + unichr = unichr + int2byte = chr + + def byte2int(bs): + return ord(bs[0]) + + def indexbytes(buf, i): + return ord(buf[i]) + iterbytes = functools.partial(itertools.imap, ord) + import StringIO + StringIO = BytesIO = StringIO.StringIO + _assertCountEqual = "assertItemsEqual" + _assertRaisesRegex = "assertRaisesRegexp" + _assertRegex = "assertRegexpMatches" +_add_doc(b, """Byte literal""") +_add_doc(u, """Text literal""") + + +def assertCountEqual(self, *args, **kwargs): + return getattr(self, _assertCountEqual)(*args, **kwargs) + + +def assertRaisesRegex(self, *args, **kwargs): + return getattr(self, _assertRaisesRegex)(*args, **kwargs) + + +def assertRegex(self, *args, **kwargs): + return getattr(self, _assertRegex)(*args, **kwargs) + + +if PY3: + exec_ = getattr(moves.builtins, "exec") + + def reraise(tp, value, tb=None): + if value is None: + value = tp() + if value.__traceback__ is not tb: + raise value.with_traceback(tb) + raise value + +else: + def exec_(_code_, _globs_=None, _locs_=None): + """Execute code in a namespace.""" + if _globs_ is None: + frame = sys._getframe(1) + _globs_ = frame.f_globals + if _locs_ is None: + _locs_ = frame.f_locals + del frame + elif _locs_ is None: + _locs_ = _globs_ + exec("""exec _code_ in _globs_, _locs_""") + + exec_("""def reraise(tp, value, tb=None): + raise tp, value, tb +""") + + +if sys.version_info[:2] == (3, 2): + exec_("""def raise_from(value, from_value): + if from_value is None: + raise value + raise value from from_value +""") +elif sys.version_info[:2] > (3, 2): + exec_("""def raise_from(value, from_value): + raise value from from_value +""") +else: + def raise_from(value, from_value): + raise value + + +print_ = getattr(moves.builtins, "print", None) +if print_ is None: + def print_(*args, **kwargs): + """The new-style print function for Python 2.4 and 2.5.""" + fp = kwargs.pop("file", sys.stdout) + if fp is None: + return + + def write(data): + if not isinstance(data, basestring): + data = str(data) + # If the file has an encoding, encode unicode with it. + if (isinstance(fp, file) and + isinstance(data, unicode) and + fp.encoding is not None): + errors = getattr(fp, "errors", None) + if errors is None: + errors = "strict" + data = data.encode(fp.encoding, errors) + fp.write(data) + want_unicode = False + sep = kwargs.pop("sep", None) + if sep is not None: + if isinstance(sep, unicode): + want_unicode = True + elif not isinstance(sep, str): + raise TypeError("sep must be None or a string") + end = kwargs.pop("end", None) + if end is not None: + if isinstance(end, unicode): + want_unicode = True + elif not isinstance(end, str): + raise TypeError("end must be None or a string") + if kwargs: + raise TypeError("invalid keyword arguments to print()") + if not want_unicode: + for arg in args: + if isinstance(arg, unicode): + want_unicode = True + break + if want_unicode: + newline = unicode("\n") + space = unicode(" ") + else: + newline = "\n" + space = " " + if sep is None: + sep = space + if end is None: + end = newline + for i, arg in enumerate(args): + if i: + write(sep) + write(arg) + write(end) +if sys.version_info[:2] < (3, 3): + _print = print_ + + def print_(*args, **kwargs): + fp = kwargs.get("file", sys.stdout) + flush = kwargs.pop("flush", False) + _print(*args, **kwargs) + if flush and fp is not None: + fp.flush() + +_add_doc(reraise, """Reraise an exception.""") + +if sys.version_info[0:2] < (3, 4): + def wraps(wrapped, assigned=functools.WRAPPER_ASSIGNMENTS, + updated=functools.WRAPPER_UPDATES): + def wrapper(f): + f = functools.wraps(wrapped, assigned, updated)(f) + f.__wrapped__ = wrapped + return f + return wrapper +else: + wraps = functools.wraps + + +def with_metaclass(meta, *bases): + """Create a base class with a metaclass.""" + # This requires a bit of explanation: the basic idea is to make a dummy + # metaclass for one level of class instantiation that replaces itself with + # the actual metaclass. + class metaclass(meta): + + def __new__(cls, name, this_bases, d): + return meta(name, bases, d) + return type.__new__(metaclass, 'temporary_class', (), {}) + + +def add_metaclass(metaclass): + """Class decorator for creating a class with a metaclass.""" + def wrapper(cls): + orig_vars = cls.__dict__.copy() + slots = orig_vars.get('__slots__') + if slots is not None: + if isinstance(slots, str): + slots = [slots] + for slots_var in slots: + orig_vars.pop(slots_var) + orig_vars.pop('__dict__', None) + orig_vars.pop('__weakref__', None) + return metaclass(cls.__name__, cls.__bases__, orig_vars) + return wrapper + + +def python_2_unicode_compatible(klass): + """ + A decorator that defines __unicode__ and __str__ methods under Python 2. + Under Python 3 it does nothing. + + To support Python 2 and 3 with a single code base, define a __str__ method + returning text and apply this decorator to the class. + """ + if PY2: + if '__str__' not in klass.__dict__: + raise ValueError("@python_2_unicode_compatible cannot be applied " + "to %s because it doesn't define __str__()." % + klass.__name__) + klass.__unicode__ = klass.__str__ + klass.__str__ = lambda self: self.__unicode__().encode('utf-8') + return klass + + +# Complete the moves implementation. +# This code is at the end of this module to speed up module loading. +# Turn this module into a package. +__path__ = [] # required for PEP 302 and PEP 451 +__package__ = __name__ # see PEP 366 @ReservedAssignment +if globals().get("__spec__") is not None: + __spec__.submodule_search_locations = [] # PEP 451 @UndefinedVariable +# Remove other six meta path importers, since they cause problems. This can +# happen if six is removed from sys.modules and then reloaded. (Setuptools does +# this for some reason.) +if sys.meta_path: + for i, importer in enumerate(sys.meta_path): + # Here's some real nastiness: Another "instance" of the six module might + # be floating around. Therefore, we can't use isinstance() to check for + # the six meta path importer, since the other six instance will have + # inserted an importer with different class. + if (type(importer).__name__ == "_SixMetaPathImporter" and + importer.name == __name__): + del sys.meta_path[i] + break + del i, importer +# Finally, add the importer to the meta path import hook. +sys.meta_path.append(_importer) diff --git a/venv/lib/python3.8/site-packages/pkg_resources/extern/__init__.py b/venv/lib/python3.8/site-packages/pkg_resources/extern/__init__.py new file mode 100644 index 0000000..bf98d8f --- /dev/null +++ b/venv/lib/python3.8/site-packages/pkg_resources/extern/__init__.py @@ -0,0 +1,66 @@ +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 + 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/venv/lib/python3.8/site-packages/pkg_resources/extern/__pycache__/__init__.cpython-38.pyc b/venv/lib/python3.8/site-packages/pkg_resources/extern/__pycache__/__init__.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..b843adb408cffd35502d701c44db99565c47e044 GIT binary patch literal 2406 zcmZ`)TaOzx6t?G@WG^j9?9!@KmMT$2>LyERt5y_LR4S-IQPq|L5ok0UJG-+xnVH(o zZWAUiWW@syyz$|gRLPy7WW1m}2?Y`QBP`NSUIj?ed<&)=@CH3*bzANNjte<2&$#z*@=p@hL zG#zan+KmLa8^DxY?+JO7D8aH=52c-xWJ*IraM-eU5bp}b`Z6Cdnddsn;(=({)o$#n7AYQ?>G80CXe@)-i z7pECl32oNro?8!;38g5~xF<}b8iZ^E*50?sk7L9^BrxWjAX-Zx>0iHOw#|f^Qw|36XB? z+}-}Dcjs{2hNB}XR6dkFq1x45+EJ8biH@RHF*3Dz2Z^WxFoXuQLC2df-U7BYpVy-} zXI8N7Kt-CV4KO9y2mWo67sx}$TI7j0{>B1>+;}Ep+1mpKNTnHI%&I8O5yej#ixrFK zxX(u_Y;g-iTZVL+)W+})mFW9vDCG)t&5E+;M994h=$<8nze>lK&)jTjVh{0{7YxRN zHFu!{48aRhyx4od!;>!8JO~z|;#kE5Y2ej5KbXjf^{p3)86FKrbv$n}H3lsm=W~9){F-=%Vp< zVkJyaIdu*K%<`!6bQ2C%t6+$~K|T1r3-ufnOAC6)t9b!!B$zJ`p{f$hIVc&J6kfIC@qtnA#$VDY1ZXC5bjF0E<(#RKnSdo zM-j;12;{B;SecdT5qKnJE=>R`KZvynFaW7))~Y0Z4>vQ3-=LqtZ24q^^P?-UcX@Lu>GB*xx#>ISvh6?;q#ibL3ju;+%+K-dl2AhDJ#= zWcfiTH}L;8yay*hAp5L3Weidh>HRQSfX^D4MsS72JgXGC2JlC_Qn}mn@wsub~&hJr`bTcN38+u z0LKbAXs<@m%VBI)2zp@@@w^vB64TYx3Yix|>XAhFCB{c00utZRCC@N|R(T~RF6yRQ z2UDY--|!vU2pg-l4Lb&JA;=Ft-@ry_jzEhPHeHpxG9g@?!`smGZA|P63 W?_5*6UtQ=ke3k4|R&&=s_2_?6o^xyf literal 0 HcmV?d00001 diff --git a/venv/lib/python3.8/site-packages/pkg_resources/py2_warn.py b/venv/lib/python3.8/site-packages/pkg_resources/py2_warn.py new file mode 100644 index 0000000..6855aa2 --- /dev/null +++ b/venv/lib/python3.8/site-packages/pkg_resources/py2_warn.py @@ -0,0 +1,16 @@ +import sys +import warnings +import textwrap + + +msg = textwrap.dedent(""" + Encountered a version of Setuptools that no longer supports + this version of Python. Please head to + https://bit.ly/setuptools-py2-sunset for support. + """) + +pre = "Setuptools no longer works on Python 2\n" + +if sys.version_info < (3,): + warnings.warn(pre + "*" * 60 + msg + "*" * 60) + raise SystemExit(32) diff --git a/venv/lib/python3.8/site-packages/pkg_resources/py31compat.py b/venv/lib/python3.8/site-packages/pkg_resources/py31compat.py new file mode 100644 index 0000000..a381c42 --- /dev/null +++ b/venv/lib/python3.8/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/venv/lib/python3.8/site-packages/setuptools-47.1.0.dist-info/INSTALLER b/venv/lib/python3.8/site-packages/setuptools-47.1.0.dist-info/INSTALLER new file mode 100644 index 0000000..a1b589e --- /dev/null +++ b/venv/lib/python3.8/site-packages/setuptools-47.1.0.dist-info/INSTALLER @@ -0,0 +1 @@ +pip diff --git a/venv/lib/python3.8/site-packages/setuptools-47.1.0.dist-info/LICENSE b/venv/lib/python3.8/site-packages/setuptools-47.1.0.dist-info/LICENSE new file mode 100644 index 0000000..6e0693b --- /dev/null +++ b/venv/lib/python3.8/site-packages/setuptools-47.1.0.dist-info/LICENSE @@ -0,0 +1,19 @@ +Copyright (C) 2016 Jason R Coombs + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +of the Software, and to permit persons to whom the Software is furnished to do +so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/venv/lib/python3.8/site-packages/setuptools-47.1.0.dist-info/METADATA b/venv/lib/python3.8/site-packages/setuptools-47.1.0.dist-info/METADATA new file mode 100644 index 0000000..253c680 --- /dev/null +++ b/venv/lib/python3.8/site-packages/setuptools-47.1.0.dist-info/METADATA @@ -0,0 +1,109 @@ +Metadata-Version: 2.1 +Name: setuptools +Version: 47.1.0 +Summary: Easily download, build, install, upgrade, and uninstall Python packages +Home-page: https://github.com/pypa/setuptools +Author: Python Packaging Authority +Author-email: distutils-sig@python.org +License: UNKNOWN +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 :: 3 +Classifier: Programming Language :: Python :: 3 :: Only +Classifier: Programming Language :: Python :: 3.5 +Classifier: Programming Language :: Python :: 3.6 +Classifier: Programming Language :: Python :: 3.7 +Classifier: Programming Language :: Python :: 3.8 +Classifier: Topic :: Software Development :: Libraries :: Python Modules +Classifier: Topic :: System :: Archiving :: Packaging +Classifier: Topic :: System :: Systems Administration +Classifier: Topic :: Utilities +Requires-Python: >=3.5 +Description-Content-Type: text/x-rst; charset=UTF-8 +Provides-Extra: certs +Requires-Dist: certifi (==2016.9.26) ; extra == 'certs' +Provides-Extra: docs +Requires-Dist: sphinx ; extra == 'docs' +Requires-Dist: jaraco.packaging (>=6.1) ; extra == 'docs' +Requires-Dist: rst.linker (>=1.9) ; extra == 'docs' +Requires-Dist: pygments-github-lexers (==0.0.5) ; extra == 'docs' +Provides-Extra: ssl +Requires-Dist: wincertstore (==0.2) ; (sys_platform == "win32") and extra == 'ssl' +Provides-Extra: tests +Requires-Dist: mock ; extra == 'tests' +Requires-Dist: pytest-flake8 ; extra == 'tests' +Requires-Dist: virtualenv (>=13.0.0) ; extra == 'tests' +Requires-Dist: pytest-virtualenv (>=1.2.7) ; extra == 'tests' +Requires-Dist: pytest (>=3.7) ; extra == 'tests' +Requires-Dist: wheel ; extra == 'tests' +Requires-Dist: coverage (>=4.5.1) ; extra == 'tests' +Requires-Dist: pytest-cov (>=2.5.1) ; extra == 'tests' +Requires-Dist: pip (>=19.1) ; extra == 'tests' +Requires-Dist: futures ; (python_version == "2.7") and extra == 'tests' +Requires-Dist: flake8-2020 ; (python_version >= "3.6") and extra == 'tests' +Requires-Dist: paver ; (python_version >= "3.6") and extra == 'tests' + +.. image:: https://img.shields.io/pypi/v/setuptools.svg + :target: `PyPI link`_ + +.. image:: https://img.shields.io/pypi/pyversions/setuptools.svg + :target: `PyPI link`_ + +.. _PyPI link: https://pypi.org/project/setuptools + +.. image:: https://dev.azure.com/jaraco/setuptools/_apis/build/status/pypa.setuptools?branchName=master + :target: https://dev.azure.com/jaraco/setuptools/_build/latest?definitionId=1&branchName=master + +.. 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/readthedocs/setuptools/latest.svg + :target: https://setuptools.readthedocs.io + +.. 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 + +See the `Installation Instructions +`_ 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 `_. +Bug reports and especially tested patches may be +submitted directly to the `bug tracker +`_. + +To report a security vulnerability, please use the +`Tidelift security contact `_. +Tidelift will coordinate the fix and disclosure. + + +For Enterprise +============== + +Available as part of the Tidelift Subscription. + +Setuptools and the maintainers of thousands of other packages are working with Tidelift to deliver one enterprise subscription that covers all of the open source you use. + +`Learn more `_. + +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 `_. + + diff --git a/venv/lib/python3.8/site-packages/setuptools-47.1.0.dist-info/RECORD b/venv/lib/python3.8/site-packages/setuptools-47.1.0.dist-info/RECORD new file mode 100644 index 0000000..46a7577 --- /dev/null +++ b/venv/lib/python3.8/site-packages/setuptools-47.1.0.dist-info/RECORD @@ -0,0 +1,196 @@ +../../../bin/easy_install,sha256=nB66fQxUCdBMo2y6bKikdZkGX615GOyBaG27vF1q0Dk,305 +../../../bin/easy_install-3.8,sha256=nB66fQxUCdBMo2y6bKikdZkGX615GOyBaG27vF1q0Dk,305 +__pycache__/easy_install.cpython-38.pyc,, +easy_install.py,sha256=MDC9vt5AxDsXX5qcKlBz2TnW6Tpuv_AobnfhCJ9X3PM,126 +pkg_resources/__init__.py,sha256=AiHtsqVRO1T4Sfv_JH2WzHa_EywN1-MaIxEPIe0ky0g,108570 +pkg_resources/__pycache__/__init__.cpython-38.pyc,, +pkg_resources/__pycache__/py2_warn.cpython-38.pyc,, +pkg_resources/__pycache__/py31compat.cpython-38.pyc,, +pkg_resources/_vendor/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +pkg_resources/_vendor/__pycache__/__init__.cpython-38.pyc,, +pkg_resources/_vendor/__pycache__/appdirs.cpython-38.pyc,, +pkg_resources/_vendor/__pycache__/pyparsing.cpython-38.pyc,, +pkg_resources/_vendor/__pycache__/six.cpython-38.pyc,, +pkg_resources/_vendor/appdirs.py,sha256=MievUEuv3l_mQISH5SF0shDk_BNhHHzYiAPrT3ITN4I,24701 +pkg_resources/_vendor/packaging/__about__.py,sha256=zkcCPTN_6TcLW0Nrlg0176-R1QQ_WVPTm8sz1R4-HjM,720 +pkg_resources/_vendor/packaging/__init__.py,sha256=_vNac5TrzwsrzbOFIbF-5cHqc_Y2aPT2D7zrIR06BOo,513 +pkg_resources/_vendor/packaging/__pycache__/__about__.cpython-38.pyc,, +pkg_resources/_vendor/packaging/__pycache__/__init__.cpython-38.pyc,, +pkg_resources/_vendor/packaging/__pycache__/_compat.cpython-38.pyc,, +pkg_resources/_vendor/packaging/__pycache__/_structures.cpython-38.pyc,, +pkg_resources/_vendor/packaging/__pycache__/markers.cpython-38.pyc,, +pkg_resources/_vendor/packaging/__pycache__/requirements.cpython-38.pyc,, +pkg_resources/_vendor/packaging/__pycache__/specifiers.cpython-38.pyc,, +pkg_resources/_vendor/packaging/__pycache__/utils.cpython-38.pyc,, +pkg_resources/_vendor/packaging/__pycache__/version.cpython-38.pyc,, +pkg_resources/_vendor/packaging/_compat.py,sha256=Vi_A0rAQeHbU-a9X0tt1yQm9RqkgQbDSxzRw8WlU9kA,860 +pkg_resources/_vendor/packaging/_structures.py,sha256=RImECJ4c_wTlaTYYwZYLHEiebDMaAJmK1oPARhw1T5o,1416 +pkg_resources/_vendor/packaging/markers.py,sha256=uEcBBtGvzqltgnArqb9c4RrcInXezDLos14zbBHhWJo,8248 +pkg_resources/_vendor/packaging/requirements.py,sha256=SikL2UynbsT0qtY9ltqngndha_sfo0w6XGFhAhoSoaQ,4355 +pkg_resources/_vendor/packaging/specifiers.py,sha256=SAMRerzO3fK2IkFZCaZkuwZaL_EGqHNOz4pni4vhnN0,28025 +pkg_resources/_vendor/packaging/utils.py,sha256=3m6WvPm6NNxE8rkTGmn0r75B_GZSGg7ikafxHsBN1WA,421 +pkg_resources/_vendor/packaging/version.py,sha256=OwGnxYfr2ghNzYx59qWIBkrK3SnB6n-Zfd1XaLpnnM0,11556 +pkg_resources/_vendor/pyparsing.py,sha256=tmrp-lu-qO1i75ZzIN5A12nKRRD1Cm4Vpk-5LR9rims,232055 +pkg_resources/_vendor/six.py,sha256=A6hdJZVjI3t_geebZ9BzUvwRrIXo0lfwzQlM2LcKyas,30098 +pkg_resources/extern/__init__.py,sha256=w_3T8ntsvFFioQYOgYoGGqafDiv4sLzecQRDjsB5yeE,2101 +pkg_resources/extern/__pycache__/__init__.cpython-38.pyc,, +pkg_resources/py2_warn.py,sha256=p7Ch41rTuJvE4vi5vLQ47Fp0OLpvyF7Um8AKuX9Xt9o,396 +pkg_resources/py31compat.py,sha256=-WQ0e4c3RG_acdhwC3gLiXhP_lg4G5q7XYkZkQg0gxU,558 +setuptools-47.1.0.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4 +setuptools-47.1.0.dist-info/LICENSE,sha256=wyo6w5WvYyHv0ovnPQagDw22q4h9HCHU_sRhKNIFbVo,1078 +setuptools-47.1.0.dist-info/METADATA,sha256=httg_Q-F4BYN9eYMIbApa91ndE6pB7lo_k04-vuiBJE,4806 +setuptools-47.1.0.dist-info/RECORD,, +setuptools-47.1.0.dist-info/WHEEL,sha256=p46_5Uhzqz6AzeSosiOnxK-zmFja1i22CrQCjmYe8ec,92 +setuptools-47.1.0.dist-info/dependency_links.txt,sha256=HlkCFkoK5TbZ5EMLbLKYhLcY_E31kBWD8TqW2EgmatQ,239 +setuptools-47.1.0.dist-info/entry_points.txt,sha256=1K5Fr0-5Ph3ZRZFuwNaw8ERGiNLVqHvdKDNt3oXGS6w,3143 +setuptools-47.1.0.dist-info/top_level.txt,sha256=2HUXVVwA4Pff1xgTFr3GsTXXKaPaO6vlG6oNJ_4u4Tg,38 +setuptools-47.1.0.dist-info/zip-safe,sha256=AbpHGcgLb-kRsJGnwFEktk7uzpZOCcBY74-YBdrKVGs,1 +setuptools/__init__.py,sha256=olFlX8bEJQQXQce__CbY0RxyHxZJATVbYi_GWJuYXQg,7341 +setuptools/__pycache__/__init__.cpython-38.pyc,, +setuptools/__pycache__/_deprecation_warning.cpython-38.pyc,, +setuptools/__pycache__/_imp.cpython-38.pyc,, +setuptools/__pycache__/archive_util.cpython-38.pyc,, +setuptools/__pycache__/build_meta.cpython-38.pyc,, +setuptools/__pycache__/config.cpython-38.pyc,, +setuptools/__pycache__/dep_util.cpython-38.pyc,, +setuptools/__pycache__/depends.cpython-38.pyc,, +setuptools/__pycache__/dist.cpython-38.pyc,, +setuptools/__pycache__/errors.cpython-38.pyc,, +setuptools/__pycache__/extension.cpython-38.pyc,, +setuptools/__pycache__/glob.cpython-38.pyc,, +setuptools/__pycache__/installer.cpython-38.pyc,, +setuptools/__pycache__/launch.cpython-38.pyc,, +setuptools/__pycache__/lib2to3_ex.cpython-38.pyc,, +setuptools/__pycache__/monkey.cpython-38.pyc,, +setuptools/__pycache__/msvc.cpython-38.pyc,, +setuptools/__pycache__/namespaces.cpython-38.pyc,, +setuptools/__pycache__/package_index.cpython-38.pyc,, +setuptools/__pycache__/py27compat.cpython-38.pyc,, +setuptools/__pycache__/py31compat.cpython-38.pyc,, +setuptools/__pycache__/py33compat.cpython-38.pyc,, +setuptools/__pycache__/py34compat.cpython-38.pyc,, +setuptools/__pycache__/sandbox.cpython-38.pyc,, +setuptools/__pycache__/site-patch.cpython-38.pyc,, +setuptools/__pycache__/ssl_support.cpython-38.pyc,, +setuptools/__pycache__/unicode_utils.cpython-38.pyc,, +setuptools/__pycache__/version.cpython-38.pyc,, +setuptools/__pycache__/wheel.cpython-38.pyc,, +setuptools/__pycache__/windows_support.cpython-38.pyc,, +setuptools/_deprecation_warning.py,sha256=jU9-dtfv6cKmtQJOXN8nP1mm7gONw5kKEtiPtbwnZyI,218 +setuptools/_imp.py,sha256=Qx0LJzEBaWk_6PfICamJtfBN2rh5K9sJq1wXvtZW-mc,2388 +setuptools/_vendor/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +setuptools/_vendor/__pycache__/__init__.cpython-38.pyc,, +setuptools/_vendor/__pycache__/ordered_set.cpython-38.pyc,, +setuptools/_vendor/__pycache__/pyparsing.cpython-38.pyc,, +setuptools/_vendor/__pycache__/six.cpython-38.pyc,, +setuptools/_vendor/ordered_set.py,sha256=dbaCcs27dyN9gnMWGF5nA_BrVn6Q-NrjKYJpV9_fgBs,15130 +setuptools/_vendor/packaging/__about__.py,sha256=CpuMSyh1V7adw8QMjWKkY3LtdqRUkRX4MgJ6nF4stM0,744 +setuptools/_vendor/packaging/__init__.py,sha256=6enbp5XgRfjBjsI9-bn00HjHf5TH21PDMOKkJW8xw-w,562 +setuptools/_vendor/packaging/__pycache__/__about__.cpython-38.pyc,, +setuptools/_vendor/packaging/__pycache__/__init__.cpython-38.pyc,, +setuptools/_vendor/packaging/__pycache__/_compat.cpython-38.pyc,, +setuptools/_vendor/packaging/__pycache__/_structures.cpython-38.pyc,, +setuptools/_vendor/packaging/__pycache__/markers.cpython-38.pyc,, +setuptools/_vendor/packaging/__pycache__/requirements.cpython-38.pyc,, +setuptools/_vendor/packaging/__pycache__/specifiers.cpython-38.pyc,, +setuptools/_vendor/packaging/__pycache__/tags.cpython-38.pyc,, +setuptools/_vendor/packaging/__pycache__/utils.cpython-38.pyc,, +setuptools/_vendor/packaging/__pycache__/version.cpython-38.pyc,, +setuptools/_vendor/packaging/_compat.py,sha256=Ugdm-qcneSchW25JrtMIKgUxfEEBcCAz6WrEeXeqz9o,865 +setuptools/_vendor/packaging/_structures.py,sha256=pVd90XcXRGwpZRB_qdFuVEibhCHpX_bL5zYr9-N0mc8,1416 +setuptools/_vendor/packaging/markers.py,sha256=-meFl9Fr9V8rF5Rduzgett5EHK9wBYRUqssAV2pj0lw,8268 +setuptools/_vendor/packaging/requirements.py,sha256=3dwIJekt8RRGCUbgxX8reeAbgmZYjb0wcCRtmH63kxI,4742 +setuptools/_vendor/packaging/specifiers.py,sha256=0ZzQpcUnvrQ6LjR-mQRLzMr8G6hdRv-mY0VSf_amFtI,27778 +setuptools/_vendor/packaging/tags.py,sha256=EPLXhO6GTD7_oiWEO1U0l0PkfR8R_xivpMDHXnsTlts,12933 +setuptools/_vendor/packaging/utils.py,sha256=VaTC0Ei7zO2xl9ARiWmz2YFLFt89PuuhLbAlXMyAGms,1520 +setuptools/_vendor/packaging/version.py,sha256=Npdwnb8OHedj_2L86yiUqscujb7w_i5gmSK1PhOAFzg,11978 +setuptools/_vendor/pyparsing.py,sha256=tmrp-lu-qO1i75ZzIN5A12nKRRD1Cm4Vpk-5LR9rims,232055 +setuptools/_vendor/six.py,sha256=A6hdJZVjI3t_geebZ9BzUvwRrIXo0lfwzQlM2LcKyas,30098 +setuptools/archive_util.py,sha256=2VqSBaoRomeA2al-hYnyB-33ehP4exrknpZNy7qWin0,6626 +setuptools/build_meta.py,sha256=n3-3HF0uKfFAzNP02yz2V7q1ydlPaQ1b_IxHG33yWjU,9960 +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=QCAuA9whnq8Bnoc0bBaS6Lw_KAUO0DiHYZQXEMNn5hg,568 +setuptools/command/__pycache__/__init__.cpython-38.pyc,, +setuptools/command/__pycache__/alias.cpython-38.pyc,, +setuptools/command/__pycache__/bdist_egg.cpython-38.pyc,, +setuptools/command/__pycache__/bdist_rpm.cpython-38.pyc,, +setuptools/command/__pycache__/bdist_wininst.cpython-38.pyc,, +setuptools/command/__pycache__/build_clib.cpython-38.pyc,, +setuptools/command/__pycache__/build_ext.cpython-38.pyc,, +setuptools/command/__pycache__/build_py.cpython-38.pyc,, +setuptools/command/__pycache__/develop.cpython-38.pyc,, +setuptools/command/__pycache__/dist_info.cpython-38.pyc,, +setuptools/command/__pycache__/easy_install.cpython-38.pyc,, +setuptools/command/__pycache__/egg_info.cpython-38.pyc,, +setuptools/command/__pycache__/install.cpython-38.pyc,, +setuptools/command/__pycache__/install_egg_info.cpython-38.pyc,, +setuptools/command/__pycache__/install_lib.cpython-38.pyc,, +setuptools/command/__pycache__/install_scripts.cpython-38.pyc,, +setuptools/command/__pycache__/py36compat.cpython-38.pyc,, +setuptools/command/__pycache__/register.cpython-38.pyc,, +setuptools/command/__pycache__/rotate.cpython-38.pyc,, +setuptools/command/__pycache__/saveopts.cpython-38.pyc,, +setuptools/command/__pycache__/sdist.cpython-38.pyc,, +setuptools/command/__pycache__/setopt.cpython-38.pyc,, +setuptools/command/__pycache__/test.cpython-38.pyc,, +setuptools/command/__pycache__/upload.cpython-38.pyc,, +setuptools/command/__pycache__/upload_docs.cpython-38.pyc,, +setuptools/command/alias.py,sha256=KjpE0sz_SDIHv3fpZcIQK-sCkJz-SrC6Gmug6b9Nkc8,2426 +setuptools/command/bdist_egg.py,sha256=ozITJYsQaxztvsQYZisp9CJqken1ed_EIYci8qgmorU,18406 +setuptools/command/bdist_rpm.py,sha256=B7l0TnzCGb-0nLlm6rS00jWLkojASwVmdhW2w5Qz_Ak,1508 +setuptools/command/bdist_wininst.py,sha256=Tmqa9wW0F8i_72KHWpu9pDdnCN6Er_8uJUs2UmCAwTA,922 +setuptools/command/build_clib.py,sha256=fWHSFGkk10VCddBWCszvNhowbG9Z9CZXVjQ2uSInoOs,4415 +setuptools/command/build_ext.py,sha256=X7dGN2BEoRGvm2jkIcPQvLnKzdDiqhHvVXb5uNY9cdw,13048 +setuptools/command/build_py.py,sha256=fho10QRGOaJcc3vttQ5vk5KYMV6HdZwj9HUIob6NHDM,9737 +setuptools/command/develop.py,sha256=B3-ImHP30Bxnqx-s_9Dp-fxtHhE245ACE1W5hmKY9xE,8188 +setuptools/command/dist_info.py,sha256=5t6kOfrdgALT-P3ogss6PF9k-Leyesueycuk3dUyZnI,960 +setuptools/command/easy_install.py,sha256=EoKWBUyfUGkHv7Qzbd-YNzl9y3HgR9__1TI9Bmh5ItI,87657 +setuptools/command/egg_info.py,sha256=0mb9iAyE5yEMoIQxLcW_PSPEqTZOX2POFxaXGJHuvT8,25548 +setuptools/command/install.py,sha256=8doMxeQEDoK4Eco0mO2WlXXzzp9QnsGJQ7Z7yWkZPG8,4705 +setuptools/command/install_egg_info.py,sha256=bMgeIeRiXzQ4DAGPV1328kcjwQjHjOWU4FngAWLV78Q,2203 +setuptools/command/install_lib.py,sha256=Uz42McsyHZAjrB6cw9E7Bz0xsaTbzxnM1PI9CBhiPtE,3875 +setuptools/command/install_scripts.py,sha256=x7sdEICuyFpaf5LuWXcTp49oYt8EeNbwKkW2Pv-TVXI,2519 +setuptools/command/launcher manifest.xml,sha256=xlLbjWrB01tKC0-hlVkOKkiSPbzMml2eOPtJ_ucCnbE,628 +setuptools/command/py36compat.py,sha256=TKqF6CPv-vsEFpOJUYmjBzmck-mCv_zHJMXO500PEAI,4994 +setuptools/command/register.py,sha256=kk3DxXCb5lXTvqnhfwx2g6q7iwbUmgTyXUCaBooBOUk,468 +setuptools/command/rotate.py,sha256=co5C1EkI7P0GGT6Tqz-T2SIj2LBJTZXYELpmao6d4KQ,2164 +setuptools/command/saveopts.py,sha256=za7QCBcQimKKriWcoCcbhxPjUz30gSB74zuTL47xpP4,658 +setuptools/command/sdist.py,sha256=14kBw_QOZ9L_RQDqgf9DAlEuoj0zC30X5mfDWeiyZwU,8092 +setuptools/command/setopt.py,sha256=NTWDyx-gjDF-txf4dO577s7LOzHVoKR0Mq33rFxaRr8,5085 +setuptools/command/test.py,sha256=okVw2id6qYh8hFAVGziX6dEYekAbaYfMtEx7XhgsSbg,9623 +setuptools/command/upload.py,sha256=XT3YFVfYPAmA5qhGg0euluU98ftxRUW-PzKcODMLxUs,462 +setuptools/command/upload_docs.py,sha256=G2gHjeNPcUGe_pr3EEk_6AoVD0E6nCp52mZgU2nkCpU,7314 +setuptools/config.py,sha256=hY4kYv_ErKBLcz5HyOQHtLJvWKXvMAinboADYE9suOo,21757 +setuptools/dep_util.py,sha256=BDx1BkzNQntvAB4alypHbW5UVBzjqths000PrUL4Zqc,949 +setuptools/depends.py,sha256=qt2RWllArRvhnm8lxsyRpcthEZYp4GHQgREl1q0LkFw,5517 +setuptools/dist.py,sha256=OfBnX-zJFlDA-QRO7OGitdTi_eEVKaiirbxyYeVx1nI,39211 +setuptools/errors.py,sha256=MVOcv381HNSajDgEUWzOQ4J6B5BHCBMSjHfaWcEwA1o,524 +setuptools/extension.py,sha256=uc6nHI-MxwmNCNPbUiBnybSyqhpJqjbhvOQ-emdvt_E,1729 +setuptools/extern/__init__.py,sha256=BilMS9Hq18nBaUOzcCrzoI9HnIhju45iVJBscqTqlDI,2128 +setuptools/extern/__pycache__/__init__.cpython-38.pyc,, +setuptools/glob.py,sha256=o75cHrOxYsvn854thSxE0x9k8JrKDuhP_rRXlVB00Q4,5084 +setuptools/gui-32.exe,sha256=XBr0bHMA6Hpz2s9s9Bzjl-PwXfa9nH4ie0rFn4V2kWA,65536 +setuptools/gui-64.exe,sha256=aYKMhX1IJLn4ULHgWX0sE0yREUt6B3TEHf_jOw6yNyE,75264 +setuptools/gui.exe,sha256=XBr0bHMA6Hpz2s9s9Bzjl-PwXfa9nH4ie0rFn4V2kWA,65536 +setuptools/installer.py,sha256=8_Anc1VxnB92XKZjVmEAf8zfTfmjdv1cUPuTxHX_Q1I,5336 +setuptools/launch.py,sha256=sd7ejwhBocCDx_wG9rIs0OaZ8HtmmFU8ZC6IR_S0Lvg,787 +setuptools/lib2to3_ex.py,sha256=lrjhfs4QVtWp65PuATWjPBcXxwubg9d81e0qrv0qOpI,2384 +setuptools/monkey.py,sha256=FGc9fffh7gAxMLFmJs2DW_OYWpBjkdbNS2n14UAK4NA,5264 +setuptools/msvc.py,sha256=Np7hXrKnn3EzqV3TQBOVDejhrveWrOQOgflAWgI2PGU,50989 +setuptools/namespaces.py,sha256=QuvIR8S5-u_S8_fLjPpn_utruUIsu2twdRu_KJPrKU0,3223 +setuptools/package_index.py,sha256=C3pcRc1tM6OFExcy9DxLqKIPS25GiNjfzoKqaI5vZGQ,40737 +setuptools/py27compat.py,sha256=CWHkWWAYodu3QgiIAr8-34T-G6fiSgiVF0y7h11Ld7U,1504 +setuptools/py31compat.py,sha256=h2rtZghOfwoGYd8sQ0-auaKiF3TcL3qX0bX3VessqcE,838 +setuptools/py33compat.py,sha256=SMF9Z8wnGicTOkU1uRNwZ_kz5Z_bj29PUBbqdqeeNsc,1330 +setuptools/py34compat.py,sha256=KYOd6ybRxjBW8NJmYD8t_UyyVmysppFXqHpFLdslGXU,245 +setuptools/sandbox.py,sha256=TbqKGiEXwfLYrpQhd863Dch3_yGhOmlAkhCiBYaw-ec,14284 +setuptools/script (dev).tmpl,sha256=RUzQzCQUaXtwdLtYHWYbIQmOaES5Brqq1FvUA_tu-5I,218 +setuptools/script.tmpl,sha256=WGTt5piezO27c-Dbx6l5Q4T3Ff20A5z7872hv3aAhYY,138 +setuptools/site-patch.py,sha256=25aScCA0GXCqUtSCbv386cTSbBl_u_kp_DJTnJssh-E,2346 +setuptools/ssl_support.py,sha256=TNNOq3VyV-4wkRwm0dmyIzF-iXBeWv4yIQ99eWa_bV8,8543 +setuptools/unicode_utils.py,sha256=NOiZ_5hD72A6w-4wVj8awHFM3n51Kmw1Ic_vx15XFqw,996 +setuptools/version.py,sha256=og_cuZQb0QI6ukKZFfZWPlr1HgJBPPn2vO2m_bI9ZTE,144 +setuptools/wheel.py,sha256=YLN2fczDVxkX3wjHlt_EMh-4MfHO6Ns6ldRpnkn-aa8,8371 +setuptools/windows_support.py,sha256=5GrfqSP2-dLGJoZTq2g6dCKkyQxxa2n5IQiXlJCoYEE,714 diff --git a/venv/lib/python3.8/site-packages/setuptools-47.1.0.dist-info/WHEEL b/venv/lib/python3.8/site-packages/setuptools-47.1.0.dist-info/WHEEL new file mode 100644 index 0000000..3b5c403 --- /dev/null +++ b/venv/lib/python3.8/site-packages/setuptools-47.1.0.dist-info/WHEEL @@ -0,0 +1,5 @@ +Wheel-Version: 1.0 +Generator: bdist_wheel (0.33.6) +Root-Is-Purelib: true +Tag: py3-none-any + diff --git a/venv/lib/python3.8/site-packages/setuptools-47.1.0.dist-info/dependency_links.txt b/venv/lib/python3.8/site-packages/setuptools-47.1.0.dist-info/dependency_links.txt new file mode 100644 index 0000000..e87d021 --- /dev/null +++ b/venv/lib/python3.8/site-packages/setuptools-47.1.0.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/venv/lib/python3.8/site-packages/setuptools-47.1.0.dist-info/entry_points.txt b/venv/lib/python3.8/site-packages/setuptools-47.1.0.dist-info/entry_points.txt new file mode 100644 index 0000000..f57c6d2 --- /dev/null +++ b/venv/lib/python3.8/site-packages/setuptools-47.1.0.dist-info/entry_points.txt @@ -0,0 +1,68 @@ +[console_scripts] +easy_install = setuptools.command.easy_install:main +easy_install-3.8 = 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 +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_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.finalize_distribution_options] +2to3_doctests = setuptools.dist:Distribution._finalize_2to3_doctests +keywords = setuptools.dist:Distribution._finalize_setup_keywords +parent_finalize = setuptools.dist:_Distribution.finalize_options + +[setuptools.installation] +eggsecutable = setuptools.command.easy_install:bootstrap + diff --git a/venv/lib/python3.8/site-packages/setuptools-47.1.0.dist-info/top_level.txt b/venv/lib/python3.8/site-packages/setuptools-47.1.0.dist-info/top_level.txt new file mode 100644 index 0000000..4577c6a --- /dev/null +++ b/venv/lib/python3.8/site-packages/setuptools-47.1.0.dist-info/top_level.txt @@ -0,0 +1,3 @@ +easy_install +pkg_resources +setuptools diff --git a/venv/lib/python3.8/site-packages/setuptools-47.1.0.dist-info/zip-safe b/venv/lib/python3.8/site-packages/setuptools-47.1.0.dist-info/zip-safe new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/venv/lib/python3.8/site-packages/setuptools-47.1.0.dist-info/zip-safe @@ -0,0 +1 @@ + diff --git a/venv/lib/python3.8/site-packages/setuptools/__init__.py b/venv/lib/python3.8/site-packages/setuptools/__init__.py new file mode 100644 index 0000000..811f3fd --- /dev/null +++ b/venv/lib/python3.8/site-packages/setuptools/__init__.py @@ -0,0 +1,232 @@ +"""Extensions to the 'distutils' for large or complex distributions""" + +import os +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 +from setuptools.depends import Require +from . import monkey + +__metaclass__ = type + + +__all__ = [ + 'setup', 'Distribution', '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) + + +class sic(str): + """Treat this string as-is (https://en.wikipedia.org/wiki/Sic)""" + + +# Apply monkey patches +monkey.patch_all() diff --git a/venv/lib/python3.8/site-packages/setuptools/__pycache__/__init__.cpython-38.pyc b/venv/lib/python3.8/site-packages/setuptools/__pycache__/__init__.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..30f467cc48dbcfda0667cde2b7c40d4acff527a5 GIT binary patch literal 7984 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%0DXCKQ)=GiolK<0#HTQl`jud`$c< z$-0p?EdNMFA{kjX8hQhxs)s(Z704N>KiTo$SvG9D;b7b{Y;*q8hHV>2dYjIiW7o}j NvvI?z&(|B3{{|Nu_Cf#v literal 0 HcmV?d00001 diff --git a/venv/lib/python3.8/site-packages/setuptools/__pycache__/_deprecation_warning.cpython-38.pyc b/venv/lib/python3.8/site-packages/setuptools/__pycache__/_deprecation_warning.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..65aa607af2ae38353435bdd8703e5ad05ccd0089 GIT binary patch literal 568 zcmZWm!AiqG5Z$!JmSVw!M}-AX1>3Dfi-JfI!CNn_2)%5!*-g8c?5^1)rM>FO&+z6i z%}V|F#Kc+$_D+~ccr2D$J+eqiHDA9sG1x7$ z0($|o7W+EF%d&|1w%=l@uTfQYgvO*#X>=8)}%DlJPGo6pVH@q1)EBpQm#Ep zKTxy>QWWlcNepbmVnI?C5nl(eoh8O2gQZE&`|7G6b3T~a%bp*u!;4;Fb44b^NYhD_ zVcwJAo#jG^pfkJdoq2w8V;7v~d0-zr)fSykP literal 0 HcmV?d00001 diff --git a/venv/lib/python3.8/site-packages/setuptools/__pycache__/_imp.cpython-38.pyc b/venv/lib/python3.8/site-packages/setuptools/__pycache__/_imp.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..23d628a12c0f1c153d1832893aec9fbd31efbfac GIT binary patch literal 2107 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 literal 0 HcmV?d00001 diff --git a/venv/lib/python3.8/site-packages/setuptools/__pycache__/dep_util.cpython-38.pyc b/venv/lib/python3.8/site-packages/setuptools/__pycache__/dep_util.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..5b0d6902169df14d7100b523928af6bf2c73aae8 GIT binary patch literal 875 zcmY*X&1w`u5bmCtO*DUmAQBHkgNMz)W@i&jh#(@UR}cO|Ktk_y?QF9>J#BZ-E-ssk z>&d6kqYvQA%+-^xpa-p)*+5cIMO9bT_jOnG+wJWQg7V=0SMf6>eJyFLlP`hyhQ>%oU|gfi=%y(y``M z8w|ydxn>gk)52PCrqH5p^au>T&t8Eqt#*tfxU{SoF(n;VAGCMrA2ux^X$x-2i9 zXG3Tac@9j0&ipv;R3UoR#yhS`cwuc}tB&P516yykTI0rm7IEi%=LWp1)>qKgZGEvy z)$Y|Pl|P8xM2J6=#8^4y9+D}y$*52QtV>LqoX*Vj;jw;n?1eZTy0gJ3pXVooMlzke zNsKgyr8YdBfH=h2fjS(X4UW>&={#|`0ON~7xkP}uD7{pD^QpSi&Zb_(OtSncj#_lw=wfF4y!$ZJFJmh{|d92LQ~K@=9<~Urs~$?ybz^= MCln)BPi$-LKfpTtd;kCd literal 0 HcmV?d00001 diff --git a/venv/lib/python3.8/site-packages/setuptools/__pycache__/depends.cpython-38.pyc b/venv/lib/python3.8/site-packages/setuptools/__pycache__/depends.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..c983e8c294fc672ba33d89e7ed9413ec6dce3b03 GIT binary patch literal 5268 zcma)ATaVku73PrCjnZDcc3j74Qr1o?E7*&hi<1J08^@Q#s3W_!V>?1yh2o5M>7__{ zhFW_C3MjCCD$w>RZ++NBpZZt&2MX}DPyP!<(eyh*QhP}sN`a%{aCmsmnRCAH48LAj za10z5-~0pr<7$Qrn~b^mVfW>JOj#T1BNwzFrB+~Pf`G`--9X%{u ze1=nfiAEUYOgNJDESP5V3XHUQm7LM5kc04FyIHf}-^mxK-|tkU3E^42-{)Akem|>D#@tIpRt;m{3sq(-5e_>wxkA(I$Qz05 z6qzSfB6uk%tL7`(t8c|4G4?h^*6@mqUwghKM7TEi z`0~&FD?8H-C6dWFiQ`aha4{AUSL@?lvMY!J*mVYPR?BD%dzsm0ozc%W7uj^Vh^h5* zs!w5C9@L+&gNqCJ6;Y2BIL}S;F9nLzlFLKQ@jowo7hY zb)Rn(>(HH6-cf7ssE#Q~Pk98zqI7R*#b9`Ey@}Q)tYzwdV;^aGfIvvA zly2TX5J6vv#4Yr8qseF>q#F-QED+qylV{a!cjHL3R|zQETnxQQn6w?oEsh7mlm3?5 z1`uwkwu_`H9$a_b+e0^s6O2tFzUs<6{&I2o-Tcm6Rxu;l5^jN)P@`0Fp1H?0aRYiu zIcqLzKHGinDOZh!9}EKlEubIC-|@nUaD6W#1qZ_AA`x;FM0nxzAlZs1$y`)08pkpr zaHI!wuR#5WBqc{IH&A3a&;;n?dcGe^4XuD#JTezV00o*DAwt_~{%roBdkJ=U(Qaqe zokXEf$_)|{n|rI!dM-&##^Vs?94^TE{9YHZsH2?NNP@$Stm22U0_iZYQ+*~Y?!fe#)6FPoXm(OQCwmBIz%W!P^0NEldUit5LRW2tZuHD=b2+p zPnSt@r!4=KzC%%f#3REatB7`j~j^pjRk5sW9TkBfL|WAgp^*$GM_eMU2MD?jw4N>R-dG zvg!Gew>uDhomuOj3*P?l8cZ7Dn_vO{J7Hqjg)cky-?{NuUh zPGC-hB{7sYAm=W|MMbwW6PlcaIEwV?8AhbH9S&1&cqg-QykPAjL8 zYHpFdNHR72Q2b^#5l2?l<=2bMh6It>+gPj24x(Y4*|f>()pHsZ{6wg0BKj6`6k(ZB zGuc~g(X4B{a##zf1u9`H)*`!L9xJPez$~0P*k7WfOG9J3CKqY{rwc0}1F!&0GPAfj zv$-{^a4M9{SuL?r+`VD^_RCp4tv|JA4YZY+lQz;S!ncRQs+u~l8?)N8D+ohMgDLLW z+oqEEujuQV3HSimL*BD1de>R;mH<>Y^WB~<7|I1MN)D7*^T@zzjNCT4r-;$pL;VOr z>)|Xg+<+=5+O-`9QXvjmK(Tjk7d1n^s2C!{N>!j(*>y(~ zl~-Y?&pdZCj0aw*QxaB$!Xp_-+i2~=5d90T#$eR1h1a=o2N>Fh#D`>epmJm42-}19 zgNJwT-|Tm~F{2px;?CXAuia_8@t9nR#FZ!#w*+nNa%?G;(|#cZsdy|45WHKj z=DHti;#BRtEbG4NAwFMu)NnMNT2?QoAWs20Sq1qbUPwaoteU?)vpl((H84?!km@ug z#k%~Inu}<9by%y@bDmJSvb`DjZk7@IRNSbUQMb%X_MAG}Hiw>AN zuL&EwIBZgZ=tx9x6B6+3jL`>ylzobQ&VGf`l^M@2=37=G_q+s~3<{n+3>3u- zO6hn9A2TqT$9sxZ&qnPmtqfqY z&Ll+|H2MI4R0Y$2=d^r?Klw2=m(k#R8Db7!ctW*al=1q5OAlYQ-hV$=Lx-Yfj+|_W zVsbVi*3cEBd_p}cIC2)!TMAN0D{(}99bCFLt$&ge%vD{65r^d8JX&VUJjYtLiBbT+ R@9oOsje660(|NnT^j~usRq+4- literal 0 HcmV?d00001 diff --git a/venv/lib/python3.8/site-packages/setuptools/__pycache__/dist.cpython-38.pyc b/venv/lib/python3.8/site-packages/setuptools/__pycache__/dist.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..4f9958be2e46c585047c07f0f91f91b3146c6726 GIT binary patch literal 32943 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 literal 0 HcmV?d00001 diff --git a/venv/lib/python3.8/site-packages/setuptools/__pycache__/errors.cpython-38.pyc b/venv/lib/python3.8/site-packages/setuptools/__pycache__/errors.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..ad71b5fb069bd02036d006b4681b2c9652098083 GIT binary patch literal 868 zcmZ8f&5qMB5O#jbmZ*zX;*4Yo38@lo$_iGCKxm}}aa+(XTzW~Ic)GPViG%H=v{yLs zCcILvoOlIJjGbNDGLlCg&-j~fem)-_Mg-;YqaS?a6Y|R%yD~uI43B<-ijz6v9`_5c z^yj`u-V+}1@H^ol_z-*x=OG_lk@3CFxdzkJM#@4@K`E(p6n#{(6&y5$uQ}94$V$^j z1E<-VZo_FQc~gL%M!zv38GGG>w?do7h(f=!NV$F96%UOwJo*R~khzB}eeTTz?jx6= zdw2<@Y=NK4vSbx^AnXJFa<`tsLMoc~eof7i8M(J}*Z%T&CO z1qY?$I*(6Qb^H9ddUxG`yYZYM9`*vS&-O12%_QiOh4$$n5{*CK)Zb5y{$>%o R;p@KUH+Cm=h$T0e>MsE z8z;{<7bXut^b(XHf~F)`^)#UMoCQwZyX(GD_bhGBT0x5v;RrYNX6>L2_>nL*EXwz4u|%gG^NV;kt!umM*QKzO!AD!QO1X`Fe`Fpr0{Fg>W$T||9PTK zX_8d?!!k)lC{K-TZ-;>18>xJjg4NNNeaCjfaCJ}^ss`Y-?JrJaS(qfx`ZU;y>nqVD z{e!4B82pnC3YE+wBM0+H4Muq?q|$>T9^5-A=6CO8AKx)T+#Blq2cuav`|zM{<7{v= zD3W3y!xkS)v41j=GTk5EKlmWNH?Ia7TrEtVr+NU0Wu^guUo7lyIEm&mT;bTosP;e+ z*QJc@(I)MIs2x~&Aq{B4Kpuc-4oVVaPQU~OrZC|EBV6H%2K2xW2zd(~U*B7Ov>kFY zi3~5b)clqo-{PYrg<+EMB8_6n^ARi;i_`T+?7&r*XS^yp^4Q#06l8wj=mh@`UZQJKYc zQP@@yDU(F0WlCB{mqY7f4J%~1x&o^2s#l|Bj=Gr zzK`E~kNcCUsmQ}~^{rpazr5yof1{VvkB^t{qnY1S z*zwl``G%}Z|0}j0$~R?01{l@kgp9uO*6VUoHqkfal$=ICAS)oz@3lo4s3WV=#*FA@u4 zW00y$y2TqJ&ZT&G_u>0DmsiBb!iLbQSL`XdNDJ?$QmR~RK@cNitwdf}k#_Pz(}KUl zblzGO(9uc?|ie_d&tY=y2l`!^i`Ev$!c|2gA_hV&ubq=kv z)L7E0AKR`Gz1SvQQa^SKXA;a6NOjniD1wAas+FkO{pH;f%% zyq{(=iM512B8%PETe&-7QQR-~)t=JV#crOZJ4%cvt~&$Ju|^mhYfG~1r?x9b-%A*U zjAQyLN#jf;vB65)jYn*Q<)5mg~u__O?;DKaUn{-c!6x--s zbsd|XC^)}>9iT=w+7+7(UV12(Zp#Cu3sGp%gP(*d%wSTYbF2g zik0&Arn$4c)jQ~YuftVghV&WF5Y`dtFqbLx2sm<|2X$JqY`D=ETQ z{F_sd%S8(G#ITdt@1{vtxDX*PI>Y}Th5ri_234&+N+Zp7YK{R}1-W`=tLaaSQ3$>w zzpHhjTY;{_;|`P+Pi@7FL*|G~&=f)ATWH4Bg2{+ASp$D(AVgrUp(V8LpCH!TW{14= zj@Xxg(a^U(fymqgc$VETYT>_Gr9Z1=~fMw;HEBRmWG9 zOsd8v(xe-)XH@{MsU~TkcAB^G2B%ZXA@`i}DDAzBW^RFaYzE#8$g6&n)i9#*G>iB& z@q-!Gi$7?*mXqq|c&H#WwLZ`SK@;L^jXWm90VHU>LS1jN54+F`L*wD*s^fvSk zrY%1V2?j&{Q!l?1c(%3^>H7>$WnKq(`U1xMnP=;V!5Jw zR~*lG6oEmyA`r&29auqA?rw!nKTW6WIU=tUd4s07Q$SxK#w*^w3|<$_G(jLWyxib( z@U*6xGxCxbsU>J;1%y0^ux(m~e9t8AcfaJ9UC5#uAfR0!qAoc2zfIt8@imQ#;K8JX zYCQ!~O|6VCuY^IQqa&V!k90OcNdxOH41@73FN1L!*n7H0 zJLzvjtX8D2qSNmXA$02R5P6r#JV+HPO2Z?ck|*^I5O|vKsIL=oRwAEQZ1AeJXz|a{ z41IbQkrdSpczF&sbekU3uRUJ)`0210b2z68-PP_@?Ji*I_aw^* z3#dFCQAXS|ViPWo#imMiEcR>(#O@$(py$*(g!*gl7PPQ%z!w%E!LigWHo_$xH$knQLqa{GSK@B0;{^E%*Ic@A8anXtmp7jr1Vm9G9Ksw{B3E=I zZkOsrK}}h|@^N|96y4CBpiqUQWv{4Bn}3Zqs{7%L%XUrHV)y8OwyL+=vPjzPQPSla zX_Pht8#04U7IBvcS1nv}bQ#A5vqNN6Pl{e2KZ~3+PB`b?QRblYPa(odNB*c9ANC5l lo2ie;@kW3I4Z?bZM}9ON%{Ja^Of_a2jmG(iMLzoazW~fkbfW+O literal 0 HcmV?d00001 diff --git a/venv/lib/python3.8/site-packages/setuptools/__pycache__/installer.cpython-38.pyc b/venv/lib/python3.8/site-packages/setuptools/__pycache__/installer.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..420ab5cba0f27f0d19a6b7d42529304a1cf62e7b GIT binary patch literal 4154 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 literal 0 HcmV?d00001 diff --git a/venv/lib/python3.8/site-packages/setuptools/__pycache__/launch.cpython-38.pyc b/venv/lib/python3.8/site-packages/setuptools/__pycache__/launch.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..84ec1d050c957a2bac1a33fc4a7fc37bd60fb1ba GIT binary patch literal 876 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 JRqur7{1?Rj>^T4c literal 0 HcmV?d00001 diff --git a/venv/lib/python3.8/site-packages/setuptools/__pycache__/msvc.cpython-38.pyc b/venv/lib/python3.8/site-packages/setuptools/__pycache__/msvc.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..175b44aef38d568db66ef8184d3ff0f77d9164f3 GIT binary patch literal 43132 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 JhC}~v{J*&-@VX@*YrIq)U znW1bE)JZ-Rz4Tc0Mjidv^gh>~`Y+^^-#06flo|`}Y zJOAgBW&MXf<{uXyk5S|^RNUe$vwGCp8O!aS&FVg<=b+EY+}!JV%u1Yt#=%0*e`;}; zd#^3-o!Gr5z8ZW1Ukgd|#Obv-`^pOa|3a>{6*BE)Z0s<{6m-b*l6O6^v$+Sk6UvQ#CnlwLG5RV=VTH_iDY>R%{_sv0V(8*x=7 z1s68Xt=+U3l!C5BU%|ox;sIEcrA49_xHyW$u+VOnN~PTr%XI6dfF2r`VMDtz$p+fN zBJB^-EGgnV(GIEe%oNQAZ!GZ<#DAY}R3bf$RkCpyi;Y2<@kGdts=smnWp#M>i{h7G zD9-Qi%7=Fb`6$14$2@z*#`BFTt=5MH9XjFbFZYuqTi<Pq*VV^()7OT@+XwtP zMkKrk5UR0pfzIfY87A*qbO9BzGAafYGBcbX_Vk94XK2){e8cL!UEGm5RfrXmh-tCW z2Q#&Eln$Gt57N9Ug$m@6Tnp4um7po*=;nSbqkJHPM>Nts1ijL-h=}09X3+V?T5vBM z-KsELf*~-P?m?Lro!cEzmMVmd&u(|%C_)jA?$#QzbQiPF#l{5>qNpB;qE34{r#n<> z)(%JO|9@s4_xE8s;t13Km0uE0yUt5K%)nsqnkR#Jm?>-!78#1Q;G)8_snI}`c_gc( z-A&( zj;lD|ErdMC3G0p#v%M1rgkRo`BA)hD6wRNo9l@ok{U~a$VdChMP8AfT3eqCTtFv`g zxAt1tVaP^zb0ZBP#Y&0J9B|t>TP%`7g<@wdxZMHV87J`a@W**dW^95OEbkTxBfbp; z0rGj+9Yk5#k2AU5mhgN)s=ZU&(Wh8;*{qs|s>Gp+PNSU!Kom`u!%<_-#W+MapQPl?1^8GDeyGla9nO9L?Of| z|HQhEnEoNmFm$&x>weFErCBR%YDX%eJvh)T(Vht);wE$>gw#<$d%E@Is#GfK{zM^2WAu6hQ@#d}5yfT!6zy zYtIl)L=i~1mxKot_JVQ{AnXQbF*lfn6NJAdju|4|g###y-qq>YEJ8PGQF>vJfvQPS zhuT&|LDozU?HV!{J@PUT=f;HEp7t|XY}t@aP~Y+50fH@X9qurVl6mIlZ*#yKMDFJTyg=klZ2Pr zkJWVj8F1a!v}bd3vz7+;Fn5{5EUuHh;>xpXITOL4SM&8<8Q(L5JNo_N#<#_THqh9;M%+PycMo! zKQdPuMYRNbVC*e~ej^%C+}mG<*Q z?U!88ZsrJLq9=uNLFAxpKvJ;xZP)g%`o8bD*Imzd{6*ihnVCa8xbTpPJVueUp;5X?Qt&^Co&=X%`vUo(*CRMcQ`bKA&&Fq#QWAp&U*s{-jM^I0=Xp%eKBu_$lM4ij& zCPIxcd2R1waQ!UD?IN>IMA4P$D}?{UMAq&EUNnXiUN)%!EsS^Z@8cG~FcJOycK*;l91(_|8i716}~FB>(^b literal 0 HcmV?d00001 diff --git a/venv/lib/python3.8/site-packages/setuptools/__pycache__/package_index.cpython-38.pyc b/venv/lib/python3.8/site-packages/setuptools/__pycache__/package_index.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..84b7ab763a9b881410eb6f808b185f3131c8f9a4 GIT binary patch literal 33117 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 literal 0 HcmV?d00001 diff --git a/venv/lib/python3.8/site-packages/setuptools/__pycache__/py27compat.cpython-38.pyc b/venv/lib/python3.8/site-packages/setuptools/__pycache__/py27compat.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..6f370fd7725bf7cdfa810731974ff8497b44dbeb GIT binary patch literal 1801 zcma)6%}*Og6rY)0+soPp8%WfTq+O{>sU^S^wUAa-REa=Bngk1oT2_@-YtI-j-mlJ% z0bA=!n^O<{6Do4#pXsTmIaTT@=iYj#`erbJ&`W3a-n^NedGF0{es5k>t2To6(cPcv z9|eT|RDeG45xpUB0!?_!~=jk+^d5PT$tGjf}~s!2Z7|onwWo4c;b>xCy)U zkuH>?r1m)XCIXg)r$Ib8?O>R?Y|2)mZ$kOV(H`RJO0*vcf=_R_mDX3GAGN9f;0`SN z3k30LP_LDrhAGS4Af{fF&_T##xpm-e>^y62KizyNr(m$PyW?&?*XG{Ni`|XQ29b4z zN|z@i7JEtih;@WM+Qdi0;Ik@*ItiZ#^%h7fRh73sz!4aVN))IxCOP5VoSZ=|!pIQh z%*YKO88OlK#@HMeKo`fQu{EB`EkTaVKH;sLq+8TDRJ#kH@eek5u|P_-~xe=^8~69#?eECf-yeC_C;G!_<}_CPo%f#^AkCqQ5)yC@rpm$`yVTAmlg zf$+T1+=d@7Ks^C?&OPn~v2!nL6lEbxq28pCCaE+a1h1=Y)z&wUyocMnn;ZK(y9cto z9jRj6j8V zD&PW0rm#oE#wND$9q4Ur5fjf6ozh#E@}K$^=nUsb)wkwWgYfsk7rzAx><^Uvp;9+O ztN-gD7bSxA`i=n;mrdD9^zFX~hCoLUsEc#*EBOVO4fkr~=-o?FH%uK^mcztt8~<{Z{~Cx=Dq~U6zdSKVXkHxP>}Po&7l3Ev)~_Cf zWp*kUkGX?LSy zlBDwutdfK_OIy0AD=Y2zZflh5<EE4!%JD{Cc>!txzSCx*j0E) zSi_w*{U+Q6X9w;Jv2$STd(o?HK9XMdM$-2Bl~LNdUY+&U_v+ErwerTAS89FRU0>}N zlj7QHOM|kv)vNP*d05sm+tq4$@3q!udHee6#!+aI*FcyodGZmmzXJ{UiM2y;iW48e5L}oJ z!C{+Yy%<&~VH;ZsZ$kHa=yTJAw(wT#*6F7EWb+=jM8aF|o37r^{J7R>1j0y~;Z*Wg zX8O<2AgOvElX?c+a_uxzMwjS`|EG4UzmasYBG@uQH1k3fl^Pn9j|=f;D9zzbR|r*Q zLfB&fV2>j^i3~gF2t(v{)D}Q+n5>BMFyc|reg!ZvB#i<49Aj%108x9H8R;BY@66se o#~$K;fp0Tn(b^G|7PKvCbaU$OE$j$)0vKmJU~Ca0k7&ex0J_O4bpQYW literal 0 HcmV?d00001 diff --git a/venv/lib/python3.8/site-packages/setuptools/__pycache__/py33compat.cpython-38.pyc b/venv/lib/python3.8/site-packages/setuptools/__pycache__/py33compat.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..0822faa21dc6d74b75a8783b6f33e679322d7d9b GIT binary patch literal 1460 zcmZux-EZ7P5Z@2`&gb~jq(rn8AF@iMa1zOd6he`zs)3$bBpMK;rS*&D^KLG_^HP)681R@yhlW|MEf-n?Nb(X<)S3Qk=;`sTmzkpj-a3~$t6K2 zj`~G9XUpegycB@_Bk8#pP$${#QNyd`6+9LWbe4u?q$;V5{av}EMpL&a<3dP3Qll8% zuMz{}0dVy`2uZNXfC(ztYZ7q5g#}M26#lAZu6=c)C1wiaA}b>uWAG)Rj{G%ng&^0o zCLweVp%e?WFtW#tRmpT;wZP*Kq7d)DvVN(O<4DW?aiseDMJlAK`eoeTepw!Wwv~Um zrG?lYR(Ch|vuXDEW<#UAzt=C5a{VYTqj)04`pW|;)Aixq%}?X)<7vN=`l!@JkwU7I zJ9p+KZCLx#+qiU!DR^BHmTLhcjMeWTG&zHj*JMn`?3~T0 z=5zg&&X`~`4s6xDW)q@*13>sKGP7%2TVs1-t2cNe_ziLk!2+yN6ZQ(ZL2A%vEjVj^ zL1w&(Xib>oSNv`Ecs!*SlvQh|m&rxlBWe;HmE5l{6Z+NMd@#HHlb0I|tJsDP% zhJzAx(jp%<@y&|OMF%tB^flxT-vz7kK@h$~J!VmhIq-9tPeD?bdeq_;=>NMLO^V%~ zjrsB@lVNDwFw6>Zl%np1;o(t~&fh4!4(dY`xIk9~R;1uNM6Lr@*r-Dt-kcccawCm% zl*=lPO4+-vRxn~2gjtA-G==%V#42OqwKogjWM!fBK&e6n%l}@04FpN$4Z6X5|2f}) zPsc`990S%kO=BVMrVH&z9mQ!>RoET&BH&7;vYE)d$o2?av{vdi*7XrayN~yteEWU# p0lLy@)A)P<_vR?w$3?0T73djYW&rpib(x35U>;w*?Qsv#_y;CBZ2G8gzv@^(#TEk?X9JUI1OubuAM539eA_&KfHm6UF#m&J{ zYcK3f=3d5BE)?f6mYN;3L!O90?TVRRFMH_^B~SXMkGdJdLI5JoQr(JPi4wu3ilc#U z{JXokiekNbRfI03#b^t#jD>aer2PBTY%p6#s9F}OlLo2S GrTqoz;CnIv literal 0 HcmV?d00001 diff --git a/venv/lib/python3.8/site-packages/setuptools/__pycache__/sandbox.cpython-38.pyc b/venv/lib/python3.8/site-packages/setuptools/__pycache__/sandbox.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..f65e3f5cbb9f3cc96d33b9b84ed76a90af87cc35 GIT binary patch literal 15588 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 literal 0 HcmV?d00001 diff --git a/venv/lib/python3.8/site-packages/setuptools/__pycache__/site-patch.cpython-38.pyc b/venv/lib/python3.8/site-packages/setuptools/__pycache__/site-patch.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..75b640389d54c09bc1edb7474dbf96f76c3ea9f7 GIT binary patch literal 1534 zcmYjR%TF6e7@wJ4uh;7bU<`$-m3)YVhyqnMdKApmuw_wMjrp$_+WI1 z&iElp$pKQHsf+&$7#;8hT;-{{YTVfvy+V&bKbgG)kAwSIa)vr%{2i8av`XC@WU73M z#(0d2W?6lWVyM1Fu|j@Z5va0NIC5TrSDEjDLN+>WtJY&gz(RJEP4O}yye7p=F-uV~ z2Yqc%agUC{w_|)kJ708EN6n7NE#|+gIg`10L66%IeGYWZk#md1ANRUHReQ2v`iW86 zf%l@C1CC$>7T#ls1pWI-m9b!RU`jhcVN>dRZ_J)(@5SN%v#g6Q*quNp}6F7bVYUtQA)e=gmpO!;_iyku5^=(p2sZ22KoPE zcGrgc5^lM_L+IyGr@#^^=qN?Ykk5epNX~4?3coSFt5~~9Fk&W;RierIhPDAe`5AHZ zL-{A^19`B5G-S^pW7^Q-7$sJSJ2ig)>LX%ABDoZaSo~7Cv zGO67>4&)%?iLQt|j-;-d;swif71V*0T-!#Xt(c{{N+X^I3Dd6c52Bd)zOI{r_|pOC zdYlFKj0Ka__2=a{zv4XOx)R2jfRWNDl)MRfX)mDE6b-n%Y6MP@=Pae#nP$Jbz*`{Z zGlrOv_N_b$`&A*S$*NyTu8gbsv(fIm&C!lYgK=*DwzMN8tV+Tc4O5{^gk0B0X?B_V zWvSYMIp)w9O#39I{n|9LA3$?bc3B!}oNJQiN zZQLM+C)fiyTw*SP_Y!WEGJ*-|lv3AnvHjMy8sG~)q(x@2jcw31z``XR+yGsJ@HL3T lD?s*Er;Hhf8H(?}m!H{JUMA`8Ooy{urfIgpeePlh{|Bkhjs^e# literal 0 HcmV?d00001 diff --git a/venv/lib/python3.8/site-packages/setuptools/__pycache__/ssl_support.cpython-38.pyc b/venv/lib/python3.8/site-packages/setuptools/__pycache__/ssl_support.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..6eaa0f0d60f187505ff274f88465061c82833a92 GIT binary patch literal 6925 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 literal 0 HcmV?d00001 diff --git a/venv/lib/python3.8/site-packages/setuptools/__pycache__/unicode_utils.cpython-38.pyc b/venv/lib/python3.8/site-packages/setuptools/__pycache__/unicode_utils.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..a6db58a0fe0be72a0284bc341cb4976c5b34d359 GIT binary patch literal 1203 zcmZ8hQE$^Q5VoB(OS^20Z3u*f6roA15?#gwQ;`rtV&$RaxwIVX2eZPPbO= z6Fl)N+DHCUUg3qmuqVz*yA96r`FuV*_V?Z8aA~Q6KsFxzpuY`-e(A|=AuxFZUDiQS zL~(>##fV!N<|HCLqh(-34QhTzEt8h01!IXWP#Z>z;(b&t|AF18S;d(l!^;5c*-W%; zLw^fhegvh^2r2xPd?Vt4GA1U)BQz!ZXt*&#lM*FUJVrPEF+@+S!utvFbrY$Dvx@kD zk#5I`lOu?LD;Uj|(R{aeRZ3N-Rt4LuVBD(ut^+j6B~F~0S<&-3v|xS zMcda!unCi)1E``A)~M!)VnFoq98q0UM^KA^CK-Av72KWW!VM@PSF!86os+^h1|k1Hv7*F4V*w@b1eT?~G9n~TEpZ=gK;D8u z+yT|Pdy}eLP+3gyf)Sm6IRyEZTi?)CBXTV5SG|~~5qquw4pRT2Y{1VkZO2~w3yGN@ AVE_OC literal 0 HcmV?d00001 diff --git a/venv/lib/python3.8/site-packages/setuptools/__pycache__/version.cpython-38.pyc b/venv/lib/python3.8/site-packages/setuptools/__pycache__/version.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..6257cfcb4513ea03d6e774bb08ed13e79bcb81a9 GIT binary patch literal 344 zcmYjNyH3L}6t$D0ra?g+S=d-Q1cwTuA|b@W$^r=J5+(6%Nt4=^?Ie&-VB!;C=_HdSm3MHE+Pj=C|RcTAlRymIF5 zJ!1O<1cTC-5k`GbckVtS<~&&b4VnzTtrlGyUxBWL=Dbp7ShJGxmQ8j{Aj)MTLGfDV zpv-s)I-!NqvdC&(aAt-LNYxL!NwOW2#Eh<bn?=WY+;rs6IoUcm Z5--L(UvpYlaDnZ4)xXMf96ZK*_!rucVcGxy literal 0 HcmV?d00001 diff --git a/venv/lib/python3.8/site-packages/setuptools/__pycache__/wheel.cpython-38.pyc b/venv/lib/python3.8/site-packages/setuptools/__pycache__/wheel.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..3fa8f0971092e0be6706b15ee9682c0df9c1a0b8 GIT binary patch literal 7343 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 SHo5sJr8%i~Ajf+m`8AgS literal 0 HcmV?d00001 diff --git a/venv/lib/python3.8/site-packages/setuptools/_vendor/__pycache__/ordered_set.cpython-38.pyc b/venv/lib/python3.8/site-packages/setuptools/_vendor/__pycache__/ordered_set.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..7e7158d76c8cf30d04ab7a89e596ec5d19611417 GIT binary patch literal 16464 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 F_rC(kYwrL6 literal 0 HcmV?d00001 diff --git a/venv/lib/python3.8/site-packages/setuptools/_vendor/__pycache__/pyparsing.cpython-38.pyc b/venv/lib/python3.8/site-packages/setuptools/_vendor/__pycache__/pyparsing.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..1ab4fdf5b02dfa29766092ec1048a3722b9a801b GIT binary patch literal 201683 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%0DDhGmPR5BJ$n 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Ë< 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 literal 0 HcmV?d00001 diff --git a/venv/lib/python3.8/site-packages/setuptools/_vendor/__pycache__/six.cpython-38.pyc b/venv/lib/python3.8/site-packages/setuptools/_vendor/__pycache__/six.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..f4b5a4c43b02de02e980da9681234272cdd377ba GIT binary patch literal 24479 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 OrderedSet([1, 1, 2, 3, 2]) + OrderedSet([1, 2, 3]) + """ + + def __init__(self, iterable=None): + self.items = [] + self.map = {} + if iterable is not None: + self |= iterable + + def __len__(self): + """ + Returns the number of unique elements in the ordered set + + Example: + >>> len(OrderedSet([])) + 0 + >>> len(OrderedSet([1, 2])) + 2 + """ + return len(self.items) + + def __getitem__(self, index): + """ + Get the item at a given index. + + If `index` is a slice, you will get back that slice of items, as a + new OrderedSet. + + If `index` is a list or a similar iterable, you'll get a list of + items corresponding to those indices. This is similar to NumPy's + "fancy indexing". The result is not an OrderedSet because you may ask + for duplicate indices, and the number of elements returned should be + the number of elements asked for. + + Example: + >>> oset = OrderedSet([1, 2, 3]) + >>> oset[1] + 2 + """ + if isinstance(index, slice) and index == SLICE_ALL: + return self.copy() + elif is_iterable(index): + return [self.items[i] for i in index] + elif hasattr(index, "__index__") or isinstance(index, slice): + result = self.items[index] + if isinstance(result, list): + return self.__class__(result) + else: + return result + else: + raise TypeError("Don't know how to index an OrderedSet by %r" % index) + + def copy(self): + """ + Return a shallow copy of this object. + + Example: + >>> this = OrderedSet([1, 2, 3]) + >>> other = this.copy() + >>> this == other + True + >>> this is other + False + """ + return self.__class__(self) + + def __getstate__(self): + if len(self) == 0: + # The state can't be an empty list. + # We need to return a truthy value, or else __setstate__ won't be run. + # + # This could have been done more gracefully by always putting the state + # in a tuple, but this way is backwards- and forwards- compatible with + # previous versions of OrderedSet. + return (None,) + else: + return list(self) + + def __setstate__(self, state): + if state == (None,): + self.__init__([]) + else: + self.__init__(state) + + def __contains__(self, key): + """ + Test if the item is in this ordered set + + Example: + >>> 1 in OrderedSet([1, 3, 2]) + True + >>> 5 in OrderedSet([1, 3, 2]) + False + """ + return key in self.map + + def add(self, key): + """ + Add `key` as an item to this OrderedSet, then return its index. + + If `key` is already in the OrderedSet, return the index it already + had. + + Example: + >>> oset = OrderedSet() + >>> oset.append(3) + 0 + >>> print(oset) + OrderedSet([3]) + """ + if key not in self.map: + self.map[key] = len(self.items) + self.items.append(key) + return self.map[key] + + append = add + + def update(self, sequence): + """ + Update the set with the given iterable sequence, then return the index + of the last element inserted. + + Example: + >>> oset = OrderedSet([1, 2, 3]) + >>> oset.update([3, 1, 5, 1, 4]) + 4 + >>> print(oset) + OrderedSet([1, 2, 3, 5, 4]) + """ + item_index = None + try: + for item in sequence: + item_index = self.add(item) + except TypeError: + raise ValueError( + "Argument needs to be an iterable, got %s" % type(sequence) + ) + return item_index + + def index(self, key): + """ + Get the index of a given entry, raising an IndexError if it's not + present. + + `key` can be an iterable of entries that is not a string, in which case + this returns a list of indices. + + Example: + >>> oset = OrderedSet([1, 2, 3]) + >>> oset.index(2) + 1 + """ + if is_iterable(key): + return [self.index(subkey) for subkey in key] + return self.map[key] + + # Provide some compatibility with pd.Index + get_loc = index + get_indexer = index + + def pop(self): + """ + Remove and return the last element from the set. + + Raises KeyError if the set is empty. + + Example: + >>> oset = OrderedSet([1, 2, 3]) + >>> oset.pop() + 3 + """ + if not self.items: + raise KeyError("Set is empty") + + elem = self.items[-1] + del self.items[-1] + del self.map[elem] + return elem + + def discard(self, key): + """ + Remove an element. Do not raise an exception if absent. + + The MutableSet mixin uses this to implement the .remove() method, which + *does* raise an error when asked to remove a non-existent item. + + Example: + >>> oset = OrderedSet([1, 2, 3]) + >>> oset.discard(2) + >>> print(oset) + OrderedSet([1, 3]) + >>> oset.discard(2) + >>> print(oset) + OrderedSet([1, 3]) + """ + if key in self: + i = self.map[key] + del self.items[i] + del self.map[key] + for k, v in self.map.items(): + if v >= i: + self.map[k] = v - 1 + + def clear(self): + """ + Remove all items from this OrderedSet. + """ + del self.items[:] + self.map.clear() + + def __iter__(self): + """ + Example: + >>> list(iter(OrderedSet([1, 2, 3]))) + [1, 2, 3] + """ + return iter(self.items) + + def __reversed__(self): + """ + Example: + >>> list(reversed(OrderedSet([1, 2, 3]))) + [3, 2, 1] + """ + return reversed(self.items) + + def __repr__(self): + if not self: + return "%s()" % (self.__class__.__name__,) + return "%s(%r)" % (self.__class__.__name__, list(self)) + + def __eq__(self, other): + """ + Returns true if the containers have the same items. If `other` is a + Sequence, then order is checked, otherwise it is ignored. + + Example: + >>> oset = OrderedSet([1, 3, 2]) + >>> oset == [1, 3, 2] + True + >>> oset == [1, 2, 3] + False + >>> oset == [2, 3] + False + >>> oset == OrderedSet([3, 2, 1]) + False + """ + # In Python 2 deque is not a Sequence, so treat it as one for + # consistent behavior with Python 3. + if isinstance(other, (Sequence, deque)): + # Check that this OrderedSet contains the same elements, in the + # same order, as the other object. + return list(self) == list(other) + try: + other_as_set = set(other) + except TypeError: + # If `other` can't be converted into a set, it's not equal. + return False + else: + return set(self) == other_as_set + + def union(self, *sets): + """ + Combines all unique items. + Each items order is defined by its first appearance. + + Example: + >>> oset = OrderedSet.union(OrderedSet([3, 1, 4, 1, 5]), [1, 3], [2, 0]) + >>> print(oset) + OrderedSet([3, 1, 4, 5, 2, 0]) + >>> oset.union([8, 9]) + OrderedSet([3, 1, 4, 5, 2, 0, 8, 9]) + >>> oset | {10} + OrderedSet([3, 1, 4, 5, 2, 0, 10]) + """ + cls = self.__class__ if isinstance(self, OrderedSet) else OrderedSet + containers = map(list, it.chain([self], sets)) + items = it.chain.from_iterable(containers) + return cls(items) + + def __and__(self, other): + # the parent implementation of this is backwards + return self.intersection(other) + + def intersection(self, *sets): + """ + Returns elements in common between all sets. Order is defined only + by the first set. + + Example: + >>> oset = OrderedSet.intersection(OrderedSet([0, 1, 2, 3]), [1, 2, 3]) + >>> print(oset) + OrderedSet([1, 2, 3]) + >>> oset.intersection([2, 4, 5], [1, 2, 3, 4]) + OrderedSet([2]) + >>> oset.intersection() + OrderedSet([1, 2, 3]) + """ + cls = self.__class__ if isinstance(self, OrderedSet) else OrderedSet + if sets: + common = set.intersection(*map(set, sets)) + items = (item for item in self if item in common) + else: + items = self + return cls(items) + + def difference(self, *sets): + """ + Returns all elements that are in this set but not the others. + + Example: + >>> OrderedSet([1, 2, 3]).difference(OrderedSet([2])) + OrderedSet([1, 3]) + >>> OrderedSet([1, 2, 3]).difference(OrderedSet([2]), OrderedSet([3])) + OrderedSet([1]) + >>> OrderedSet([1, 2, 3]) - OrderedSet([2]) + OrderedSet([1, 3]) + >>> OrderedSet([1, 2, 3]).difference() + OrderedSet([1, 2, 3]) + """ + cls = self.__class__ + if sets: + other = set.union(*map(set, sets)) + items = (item for item in self if item not in other) + else: + items = self + return cls(items) + + def issubset(self, other): + """ + Report whether another set contains this set. + + Example: + >>> OrderedSet([1, 2, 3]).issubset({1, 2}) + False + >>> OrderedSet([1, 2, 3]).issubset({1, 2, 3, 4}) + True + >>> OrderedSet([1, 2, 3]).issubset({1, 4, 3, 5}) + False + """ + if len(self) > len(other): # Fast check for obvious cases + return False + return all(item in other for item in self) + + def issuperset(self, other): + """ + Report whether this set contains another set. + + Example: + >>> OrderedSet([1, 2]).issuperset([1, 2, 3]) + False + >>> OrderedSet([1, 2, 3, 4]).issuperset({1, 2, 3}) + True + >>> OrderedSet([1, 4, 3, 5]).issuperset({1, 2, 3}) + False + """ + if len(self) < len(other): # Fast check for obvious cases + return False + return all(item in self for item in other) + + def symmetric_difference(self, other): + """ + Return the symmetric difference of two OrderedSets as a new set. + That is, the new set will contain all elements that are in exactly + one of the sets. + + Their order will be preserved, with elements from `self` preceding + elements from `other`. + + Example: + >>> this = OrderedSet([1, 4, 3, 5, 7]) + >>> other = OrderedSet([9, 7, 1, 3, 2]) + >>> this.symmetric_difference(other) + OrderedSet([4, 5, 9, 2]) + """ + cls = self.__class__ if isinstance(self, OrderedSet) else OrderedSet + diff1 = cls(self).difference(other) + diff2 = cls(other).difference(self) + return diff1.union(diff2) + + def _update_items(self, items): + """ + Replace the 'items' list of this OrderedSet with a new one, updating + self.map accordingly. + """ + self.items = items + self.map = {item: idx for (idx, item) in enumerate(items)} + + def difference_update(self, *sets): + """ + Update this OrderedSet to remove items from one or more other sets. + + Example: + >>> this = OrderedSet([1, 2, 3]) + >>> this.difference_update(OrderedSet([2, 4])) + >>> print(this) + OrderedSet([1, 3]) + + >>> this = OrderedSet([1, 2, 3, 4, 5]) + >>> this.difference_update(OrderedSet([2, 4]), OrderedSet([1, 4, 6])) + >>> print(this) + OrderedSet([3, 5]) + """ + items_to_remove = set() + for other in sets: + items_to_remove |= set(other) + self._update_items([item for item in self.items if item not in items_to_remove]) + + def intersection_update(self, other): + """ + Update this OrderedSet to keep only items in another set, preserving + their order in this set. + + Example: + >>> this = OrderedSet([1, 4, 3, 5, 7]) + >>> other = OrderedSet([9, 7, 1, 3, 2]) + >>> this.intersection_update(other) + >>> print(this) + OrderedSet([1, 3, 7]) + """ + other = set(other) + self._update_items([item for item in self.items if item in other]) + + def symmetric_difference_update(self, other): + """ + Update this OrderedSet to remove items from another set, then + add items from the other set that were not present in this set. + + Example: + >>> this = OrderedSet([1, 4, 3, 5, 7]) + >>> other = OrderedSet([9, 7, 1, 3, 2]) + >>> this.symmetric_difference_update(other) + >>> print(this) + OrderedSet([4, 5, 9, 2]) + """ + items_to_add = [item for item in other if item not in self] + items_to_remove = set(other) + self._update_items( + [item for item in self.items if item not in items_to_remove] + items_to_add + ) diff --git a/venv/lib/python3.8/site-packages/setuptools/_vendor/packaging/__about__.py b/venv/lib/python3.8/site-packages/setuptools/_vendor/packaging/__about__.py new file mode 100644 index 0000000..dc95138 --- /dev/null +++ b/venv/lib/python3.8/site-packages/setuptools/_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.2" + +__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/venv/lib/python3.8/site-packages/setuptools/_vendor/packaging/__init__.py b/venv/lib/python3.8/site-packages/setuptools/_vendor/packaging/__init__.py new file mode 100644 index 0000000..a0cf67d --- /dev/null +++ b/venv/lib/python3.8/site-packages/setuptools/_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/venv/lib/python3.8/site-packages/setuptools/_vendor/packaging/__pycache__/__about__.cpython-38.pyc b/venv/lib/python3.8/site-packages/setuptools/_vendor/packaging/__pycache__/__about__.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..d227465f7a52ba9a1eb37723adfb72ec98c26080 GIT binary patch literal 756 zcmY+COK%e~5P)}+w0Sjcd8jue6Tu-8iPNZ5EkdZ!9ucY(;IJ3V+3_Y;y|(4YR@nnT zfPcbY%9RWJ1H=it5h~2cpFGxhJP&^w40=hBXHUMuk4BPwkH){!3C2ZO_BsF(NJUbn zSz0!-M%m1oX#x#sLJM|a7uwK)?y{4$pqK4HKih@9tPO*#1N&JQ4zeB`W_@_Tx<^U2 z2M_1T=+Vy*H5oNG2XtYT@Qx8)YGvG}1H9svE4kU%hD%2ZFLM{|E+LLP!3f#(39-H` zsaXf3O^7#qds;CQ?*^ks2=#8IOxTB;tfX9QeFV>$v@y(pkX-4t;mehaZkpydbjjti z9$qTLkat{g$C*WiGU$38!b!AsVzz#|a!%V<7%#b7`NbqxCDv6rM*gMw=SM~7S{0fEF--f6whTdAWr# bc){MpQtU9`akG*Br@vbLRvNSsG&21IlDytf literal 0 HcmV?d00001 diff --git a/venv/lib/python3.8/site-packages/setuptools/_vendor/packaging/__pycache__/__init__.cpython-38.pyc b/venv/lib/python3.8/site-packages/setuptools/_vendor/packaging/__pycache__/__init__.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..be799a5274ac7c824648494c15b72a5973daad21 GIT binary patch literal 594 zcmY+A&yLeD5XPOPZT>ZjkoL-r13eU%3aUj~A)bH>%f%PVjosA3jxEQ|sw?pVyve>& zuAKG_5GTg1gqE@LlPCIS=JR}OnhHU@e)W@YQ-prSnh18~F>g>*s^osv{% zBufxad8TH1PUd<+7CI-nE=ZwEQtFCSx+b+=k|pS7e9jj<=LIi$#p|vh4PSmmtL6zh zqg6J&VEe(S;02Z18t12i%TW%}^wX7fviI~5`qo4Kl*Ec+swic_AC04QYA9`uJvrGO zJ)|Y2LNlr0T2rcIEBZlPk|Ai#-033n(yRDCr!+Wu5sbpcsg$}J=D4^Zw~S~a77=kR zu8hb5UshMq%OUvSpqYb3B9}$X0MQHEn*g>h#j)gBhjah)q<=%A9j3tiBXTdJI^xd{ z3~Lyf7kFe2AB^H)6x$Z>?(O(~+kf18&UgFaW^>RF`orcNU61dum3AF^%i8aPukVjS zsP+D4^RC^E4|othSZ|CPFdaqDjl)sMI@x!aQrSx|{??wR;#M0Tl=u`MHCzL((>KdB I`S1Mx3!t5yMgRZ+ literal 0 HcmV?d00001 diff --git a/venv/lib/python3.8/site-packages/setuptools/_vendor/packaging/__pycache__/_compat.cpython-38.pyc b/venv/lib/python3.8/site-packages/setuptools/_vendor/packaging/__pycache__/_compat.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..ab4a8c50054ae3fdf389afcb4c144d7f6e2699e3 GIT binary patch literal 1030 zcmah{OKTKC5bnq9?quC;1cTxWRuEwi*@;m@gb)M;Z$a<@&ZU{1?oB3{m)$+P?6RH` zNbpzaF@LGAp7IwwSv4Et=43(DRMm8U^;K1WUR`Yykf)D-$cPj28-rzG5WE33U%*Hr zX-YC0(u{>H;~}S*W2qN<$~$UEa-T?k%);h%draOAe*H*%L$*|L}IxB92nCH@Hs4 zUJbP{=%d~NH61YLbWYF7oSz^Z-M;`#$K%vEFOM?i+V(Ip0vj-n$_wTi3*SR{jg$E^ z4`7^Q=rkBdIv5qHRN4e(9Bfa@@yo6J^_G=#duVnxN7*cUwOPwS9()SQq}-_TGK!Ct z+?X6Hm2M1oHlN4a<5^&otx8)IsR_hb<+9KL9*ho>{2&lY>lvO-oV`W+#@RZ+V5qi+Ll-L1x%Exa}G+{*QsA3|6Y z@Tp$MUu#z3!;AD()T^#<^_lbRv{W@>pX)Bdt)js@OC@Ub?#)KPy6fqC5cNAq zdQ{m;18I%@YkWHzPdRR;#&yQ94tV7v$w!6bAHHs(c>zDIb{#MD_lSp-GjK#><_wymIb(w+&NiTlvrRMW3KwTx zaG$XK)*pBh+jolX;7p})t_6?ASt@nmK{Sa}lqSViCZk02VV;B<$06-lE;bHMu&PrO zf(U1&oG+ZJp<7h4ZDVGG+fY(o#*4(z}UY&YO0 z-g39_eocl^66u$rg&<|2KGh$wDsm23xZ3VZbRL_txm7fjh=3yqy!>}aN~S8Qg@(B8S#pW8*_JW%IFgN~LhT=BQfvW-K6r4^iS;HV0A zp$*N2a0wOaUvcl;?*D8KpWBj5D4MB07xFTm%XzFh-@+9o+V+x>q+5?Ad$@gZ^3I2z z38t}$Fa+nMs>DFu6^u)6e1&d&YR&8?Op;^JksVC>MCl+2MbX9s;y@`3N)5hv&iQT) zKI!W5$upXj_+9q?2J$1EuR}}FqFXg+q`QJv6}{7y4jo}&KCXcv-FgT~+gD@YRrl&J z$PBu6S*U-(3|+cv-zs{2F$#1v5oKys=6;LwM2tAUi$|4-(kpl1i!;jpyi1QH3C4JX zqQm((g`Co>=yLu%58{PJ5<|;v5;sXu@a1h1cSw9f;!_fzk@!G7iymdI;U;>a>IMqW z@|=r5qYqJaq0m!2!TLXPtfY{vcKv&Lowg53?=9@AARObTujs~xQLO2m%c1%gRA=6( zBm%68vx&;rYap*nbE~u1#Qtyz>Q;qy*W|dB&&A}|y>&qJW{jJ0ArE+{!PLVSw+~0sRxEGptaV^u#kKOHj8f zGI2q0@^f5PNlEnbzoav2D2ep_{T8v}FsFYq_(7x`layqV{&*E`diGR=dT~!$Uj8+$TNmVwtR9q?YlqkpjBT_jg*SY4Ts#N7;case_8|C+U1|UF6*|mY{ z*VA9GyI;Sre*MwlVBWy*_;WuM*IdK+XDammawuFz3NM?6Aq>IlMuU}EgO_>3ESrq# zc-<;nI?t3dI=9QV&a>q#aocf7P#(p%Ewc5o#&~&Le>?SqjYH)_jfwIE$~lo21EL@XS2N|w_Y5&4a^Ij)4U{8XBs zPV-`#zess`O1vPZ#8L6QIE?>G-`eFDl zN%L3J{5<956Y>NwTuAF)({(51Nz8DuJHv>09W#uGH+1VMc?zv>c3Vfqte*dpZapoh z<7`9=96M$Pq%dTWzyy|itV&tU$`>GxSVylIq6@4 zmPP~Lv@FSKIVew4?IxQr<_zq8K=&z3808r;FRpxJlwTIh;vKQ@4J)4$@6v4IJ%Z9& zdj;uLr1MA@kX}Q&DBl0pDZehRiXUL?8{inD{HFLoTtjJATo;STFNqss3HhA(Pz)k} z3mkJ(6o9#)Iqb6F%SQ3mBa*G684r7R!l2%YWTn<<1}cgjQM+FYYk?mRHC4@zDl09& z8lk*cwBn&9Pla;fi>ho=Rb0^Jn=)+Gqc9!_BaB#Gjy&MWC4~iFU|hZ|Rd7RH50s2E z@2Q~GjI-|s>T^$txbR^sh@@E7V~Z@#UaLh?dG$w(R>4jedJP%d zonaW{VM`Uc+o4JX>;2L4E+d5_0MaNkVU)RGWm6N_RHLX~U&y=o&HHXGS*hF53v)mB zLU$!-`QnV*3Z)9&@NUqm3s=b%soW@Vmll@X^D`Gqdi>%;D|RXsA8V{s;(Vpj2t=z+ z--Sx$vzAv+Te6jk2&$FJLqk1_*)`Z{Pvac#dy$;J@2TmPpe``qbhA2r;q&JGSI_yc zpNoXJa3{Ptv(i{^oS)IZRe$={bhFkx+wz-U^_~=GKffzw{p_8KGcQ*!++Uv#Wz=d$ zK~N8;EBB=@0yRw?y;U&4bh62?)O?_+Kx+v(fWy8s6~~B*OM9spG{GUnftrwB6v;*t zJmjLnbHNfV=|*?G$Q6MM{Zo+}wwlD;ZY@d)swO)0*`<_vj_PQ_DWvba;Bkyy|537F zm(FF+5^=V7BptfgU83s;kt@Hbg;=2<)`WC}6|_o?rH2Q9=i+Jd)Bn%n=@2ec`x$^; zo)sL5!W5>kkXs@nY~&e{6%KM+TDt!sWQs#InOiN?;>2WhHOr=?1g*7?zQ-<*-rHrCiCTQ=15 zXv1VC6o#JbAydatwRcKL33^m2ub{0nVevf|FuFsX#q(<{VT|4T^MKzTPwS zkfuquX|b41Ht-DksKW%N2%I25Os8HXu)8V!rDf_U8m5s#TBXArlm3cot4Mu6I@`-g zA(47Nhl12lg}0%}y*qWej~rdjNXS9Qlad3x@HE2fbE zyL|~rE_Kj3Oi}rJ?K5+mG}*pQn!xuq;U|$xxN#fXBp%F6fd{9GOr1p2la5;NRS!=0 zI;`jD*{C{8L!WetB{WlbuaIC7KC#v+-Uw@8zEMF zuQLuCp>li({!bz)+OWW=DeVOvZPvYrM7h#@5Zw)Y@{@Y{#bf0(l{F1=93yEUEJc5515^)4Kjc(^+%d=SFU!7WSH(HT-eeWlYW&IX}g!VW%4WPWz2 zz0_&n=}u5e$M@8vQ|@%P3DX(a&H$hJ|OMt{ck!Y#62wVoJ68m<3 z^lc+)jwJ0E84=s!ZTL~o7zy0wB*PE+q9R>aG_^Rz+4*Z%=a(0jV{=tT56zf`F{{Tc z>S0{cJh3xd_ZoMEH+L7MUmzLkD!{sV>C&YgPH7Hl7U|ffb^g_*9X7YaW_N5d$TdIa zHGkiFY0Y}Q1$@Bz=(TD+L&5>sBX&{?1a_rJt3jGMp!y#G?66ZiZ0fsXC#icXcGbZi zo1QOX9;oj;L7ahOSC>zn;z4JM9d>$$7552pmpm>yl5vjMCRdY!k(1Fj-q9+#4J$>K zs%S3uAs{VaAuS_m3$ThIpB6S45W&X(jDKc9pA%W(U5r<%fmBslfny&^*iO2I!2a{n zOw4L~h#%)?>+r9txCYGq5n-okY`7BZr=NtTG+PgiE`LoubxpgR<)}ZznAoBk z^)aCxrt;8y2oHqrIofxN&ARlJr+cmiH6Pb^JE4^NF;!-2zAx3@6;b~`MG9R2gNB%0#=dRxkV^iJT zCkCYR{su|wyx#+HwimIg)5tv(CX6I08e`E>H|hIB0!sw06Zn9@B7tiJZUFQtLgG)_ zi{2DXvVJ%sSBl}R`8IU=D#T-xZI~Y${}1q7+pyXQKi+fHohU2#mP7X;2%5RczGiLoZ$qrK(u zxBKNeRF#T3MZpDCBcT1w1Ne%aYT!p66cIuJ$xS#=p?ZfpjS(Q{ub5E{>dS+s^6A40 z5jKe}>c5AgxOhlh)PU;e=olUXFc@ct!5B8r^C9M-RCsJzs)d%mGbHszGNb88)3yy< zj#z|C5mXJ27-%sS!9s4CZAKIY9n$O4bh2x@q4!;XU0O>kx!m_&eT$afA-0UDNfOgh zbVo>bYsLdTonwnUALUYWKlfm-a2P1A^Y>^B3jhWyaN40mJ0$DYx5FGNvPgP||A>!O zW0hfVFl3+pe8YsYG)V!c>Ut_w*QkR6y^{C_TNx;3?0*wH1cQ5JPkL|kL7_7wr#KFHXMJH${ShTf*Q0qUCjA*ww-A^n`VN$jp)-oJN!FZ z2fj@hihP@IWma|Y6>|>=-0z{#IT>Oy6h(XS(PR9ITz!P?soNc}PdU4H{;`c+Ef`I#FwJ{kbn^N)2x|#9B zf02vXj3Q?}W~E28!s39U<4`jM-XK6>8ucoH*9e>^@Ct#K30x%bIzVwa&ffJx1Su6l zQx8H-0QD!-{Ko{o0DzhfW2bXTBc@C)e{e37o1&du{t~R0X&Bjrc)b5ESkbOK{8X!k zqe|A>um2Ru?k|y21|^ovbBkFfhYGZCM!WxUX8o$~pfJoJ{=AG7mH_t0XZA!8^V)r6 zh#|tRgMtS!s879;_!EEVv;Q$Z*2mK;pf(o>Cz1(%7be((2`)2ZgEP475Q+>=A0n`3 zQxbf_&}Yh_lCy1Xa6dNAloFNLW!A-v%pT# z$34QMBWZYxOdb=+`i`>^90xZ>A6w@7@OAxa@O>kq+No3IDXZfI_OQeW)E%U`iOms4 z8S2jV&`S5(mGx+DIoZd-+9`JGoBZ zy6dUc7R6=hu_qvDrLaHDrW>g4n!sjJ`V`;_iB~Zwa=aV#PO-azTUdEd3(eOvM(>3- zS8XXp(=O@Ij@wj0Rfb{mvOw#{GoMxxvE6D4SYvIXH1i<#at!oNEag~=r}v3$4`ann z>dMI@bh;!+Mvja?o_9IA)1!#@g5MQh-loZSc~^hwxC&{NfzA{NFXt${Tx7S3^Ym7> z(!#;wtxOSnD`Fi*92`3x*IE6H>i&Yjp97SS?7nT45Ym#$M;weK9#30J%eYN_$tzT} zDMdb_A}yu{2#`~$1_5GrEzTmkfS1Q8xzOdw3D<9zVdB|qe;yAKICy&5yG`!Jf{xRV z5|%LnnxZR3I43pbaq z-dL=>eSIm;B`U0PV<|4ApSS0)eY6nUS8m+6hT43$7J4rIzItondhF068iE~RY+qZN zzqzm&+c%TE({16CrQI>}w{Bcle@Sa3)tTH{c|D4GUB+B~7IVZl`6{JIeMUXVD^vFg zkQ1J;9r+5H8@1(l(%~b*mpq2f(V>R_05A@xOaJ*Kn}m~9U?Ub;H~s&Ol^e0~&yEa@ zWbn6P?{FWsaUZsS%PsqVc*gz>x9uICwf~zt_OE%){x6=l|C0~czv2b^mweFv1s}5i zgAd#P&PVKj;|J`2<&XKn?hK6Y*v^nS_n2Gb2>Ogr;*QP>Sd9(UKF$lKlg%F?TMOIU T`TyE3I0tOg8E}p`jx+Ip&92@o literal 0 HcmV?d00001 diff --git a/venv/lib/python3.8/site-packages/setuptools/_vendor/packaging/__pycache__/requirements.cpython-38.pyc b/venv/lib/python3.8/site-packages/setuptools/_vendor/packaging/__pycache__/requirements.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..e2341552429ffd809581f85acf501e095545c36b GIT binary patch literal 4047 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 z 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∓;1-==Rxsd=@4eHg^3wVp5d_gNE}Do5uWM9wp#i? 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 Out#24ALevx@c#jIj$dm4 literal 0 HcmV?d00001 diff --git a/venv/lib/python3.8/site-packages/setuptools/_vendor/packaging/_compat.py b/venv/lib/python3.8/site-packages/setuptools/_vendor/packaging/_compat.py new file mode 100644 index 0000000..25da473 --- /dev/null +++ b/venv/lib/python3.8/site-packages/setuptools/_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/venv/lib/python3.8/site-packages/setuptools/_vendor/packaging/_structures.py b/venv/lib/python3.8/site-packages/setuptools/_vendor/packaging/_structures.py new file mode 100644 index 0000000..68dcca6 --- /dev/null +++ b/venv/lib/python3.8/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/venv/lib/python3.8/site-packages/setuptools/_vendor/packaging/markers.py b/venv/lib/python3.8/site-packages/setuptools/_vendor/packaging/markers.py new file mode 100644 index 0000000..4bdfdb2 --- /dev/null +++ b/venv/lib/python3.8/site-packages/setuptools/_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 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") + | 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": ".".join(platform.python_version_tuple()[:2]), + "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 "".format(str(self)) + + def evaluate(self, environment=None): + """Evaluate a marker. + + Return the boolean from evaluating the given marker against the + environment. environment is an optional argument to override all or + part of the determined environment. + + The environment is determined from the current Python process. + """ + current_environment = default_environment() + if environment is not None: + current_environment.update(environment) + + return _evaluate_markers(self._markers, current_environment) diff --git a/venv/lib/python3.8/site-packages/setuptools/_vendor/packaging/requirements.py b/venv/lib/python3.8/site-packages/setuptools/_vendor/packaging/requirements.py new file mode 100644 index 0000000..8a0c2cb --- /dev/null +++ b/venv/lib/python3.8/site-packages/setuptools/_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 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_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 +# setuptools.extern.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 "".format(str(self)) diff --git a/venv/lib/python3.8/site-packages/setuptools/_vendor/packaging/specifiers.py b/venv/lib/python3.8/site-packages/setuptools/_vendor/packaging/specifiers.py new file mode 100644 index 0000000..743576a --- /dev/null +++ b/venv/lib/python3.8/site-packages/setuptools/_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(==|!=|<=|>=|<|>)) + \s* + (?P + [^,;\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(~=|==|!=|<=|>=|<|>|===)) + (?P + (?: + # 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. + (?=": "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 "".format(str(self), pre) + + def __str__(self): + return ",".join(sorted(str(s) for s in self._specs)) + + def __hash__(self): + return hash(self._specs) + + def __and__(self, other): + if isinstance(other, string_types): + other = SpecifierSet(other) + elif not isinstance(other, SpecifierSet): + return NotImplemented + + specifier = SpecifierSet() + specifier._specs = frozenset(self._specs | other._specs) + + if self._prereleases is None and other._prereleases is not None: + specifier._prereleases = other._prereleases + elif self._prereleases is not None and other._prereleases is None: + specifier._prereleases = self._prereleases + elif self._prereleases == other._prereleases: + specifier._prereleases = self._prereleases + else: + raise ValueError( + "Cannot combine SpecifierSets with True and False prerelease " + "overrides." + ) + + return specifier + + def __eq__(self, other): + if isinstance(other, string_types): + other = SpecifierSet(other) + elif isinstance(other, _IndividualSpecifier): + other = SpecifierSet(str(other)) + elif not isinstance(other, SpecifierSet): + return NotImplemented + + return self._specs == other._specs + + def __ne__(self, other): + if isinstance(other, string_types): + other = SpecifierSet(other) + elif isinstance(other, _IndividualSpecifier): + other = SpecifierSet(str(other)) + elif not isinstance(other, SpecifierSet): + return NotImplemented + + return self._specs != other._specs + + def __len__(self): + return len(self._specs) + + def __iter__(self): + return iter(self._specs) + + @property + def prereleases(self): + # If we have been given an explicit prerelease modifier, then we'll + # pass that through here. + if self._prereleases is not None: + return self._prereleases + + # If we don't have any specifiers, and we don't have a forced value, + # then we'll just return None since we don't know if this should have + # pre-releases or not. + if not self._specs: + return None + + # Otherwise we'll see if any of the given specifiers accept + # prereleases, if any of them do we'll return True, otherwise False. + return any(s.prereleases for s in self._specs) + + @prereleases.setter + def prereleases(self, value): + self._prereleases = value + + def __contains__(self, item): + return self.contains(item) + + def contains(self, item, prereleases=None): + # Ensure that our item is a Version or LegacyVersion instance. + if not isinstance(item, (LegacyVersion, Version)): + item = parse(item) + + # Determine if we're forcing a prerelease or not, if we're not forcing + # one for this particular filter call, then we'll use whatever the + # SpecifierSet thinks for whether or not we should support prereleases. + if prereleases is None: + prereleases = self.prereleases + + # We can determine if we're going to allow pre-releases by looking to + # see if any of the underlying items supports them. If none of them do + # and this item is a pre-release then we do not allow it and we can + # short circuit that here. + # Note: This means that 1.0.dev1 would not be contained in something + # like >=1.0.devabc however it would be in >=1.0.debabc,>0.0.dev0 + if not prereleases and item.is_prerelease: + return False + + # We simply dispatch to the underlying specs here to make sure that the + # given version is contained within all of them. + # Note: This use of all() here means that an empty set of specifiers + # will always return True, this is an explicit design decision. + return all(s.contains(item, prereleases=prereleases) for s in self._specs) + + def filter(self, iterable, prereleases=None): + # Determine if we're forcing a prerelease or not, if we're not forcing + # one for this particular filter call, then we'll use whatever the + # SpecifierSet thinks for whether or not we should support prereleases. + if prereleases is None: + prereleases = self.prereleases + + # If we have any specifiers, then we want to wrap our iterable in the + # filter method for each one, this will act as a logical AND amongst + # each specifier. + if self._specs: + for spec in self._specs: + iterable = spec.filter(iterable, prereleases=bool(prereleases)) + return iterable + # If we do not have any specifiers, then we need to have a rough filter + # which will filter out any pre-releases, unless there are no final + # releases, and which will filter out LegacyVersion in general. + else: + filtered = [] + found_prereleases = [] + + for item in iterable: + # Ensure that we some kind of Version class for this item. + if not isinstance(item, (LegacyVersion, Version)): + parsed_version = parse(item) + else: + parsed_version = item + + # Filter out any item which is parsed as a LegacyVersion + if isinstance(parsed_version, LegacyVersion): + continue + + # Store any item which is a pre-release for later unless we've + # already found a final version or we are accepting prereleases + if parsed_version.is_prerelease and not prereleases: + if not filtered: + found_prereleases.append(item) + else: + filtered.append(item) + + # If we've found no items except for pre-releases, then we'll go + # ahead and use the pre-releases + if not filtered and found_prereleases and prereleases is None: + return found_prereleases + + return filtered diff --git a/venv/lib/python3.8/site-packages/setuptools/_vendor/packaging/tags.py b/venv/lib/python3.8/site-packages/setuptools/_vendor/packaging/tags.py new file mode 100644 index 0000000..ec9942f --- /dev/null +++ b/venv/lib/python3.8/site-packages/setuptools/_vendor/packaging/tags.py @@ -0,0 +1,404 @@ +# 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 + +import distutils.util + +try: + from importlib.machinery import EXTENSION_SUFFIXES +except ImportError: # pragma: no cover + import imp + + EXTENSION_SUFFIXES = [x[0] for x in imp.get_suffixes()] + del imp +import platform +import re +import sys +import sysconfig +import warnings + + +INTERPRETER_SHORT_NAMES = { + "python": "py", # Generic. + "cpython": "cp", + "pypy": "pp", + "ironpython": "ip", + "jython": "jy", +} + + +_32_BIT_INTERPRETER = sys.maxsize <= 2 ** 32 + + +class Tag(object): + + __slots__ = ["_interpreter", "_abi", "_platform"] + + def __init__(self, interpreter, abi, platform): + self._interpreter = interpreter.lower() + self._abi = abi.lower() + self._platform = platform.lower() + + @property + def interpreter(self): + return self._interpreter + + @property + def abi(self): + return self._abi + + @property + def platform(self): + return self._platform + + def __eq__(self, other): + return ( + (self.platform == other.platform) + and (self.abi == other.abi) + and (self.interpreter == other.interpreter) + ) + + def __hash__(self): + return hash((self._interpreter, self._abi, self._platform)) + + def __str__(self): + return "{}-{}-{}".format(self._interpreter, self._abi, self._platform) + + def __repr__(self): + return "<{self} @ {self_id}>".format(self=self, self_id=id(self)) + + +def parse_tag(tag): + tags = set() + interpreters, abis, platforms = tag.split("-") + for interpreter in interpreters.split("."): + for abi in abis.split("."): + for platform_ in platforms.split("."): + tags.add(Tag(interpreter, abi, platform_)) + return frozenset(tags) + + +def _normalize_string(string): + return string.replace(".", "_").replace("-", "_") + + +def _cpython_interpreter(py_version): + # TODO: Is using py_version_nodot for interpreter version critical? + return "cp{major}{minor}".format(major=py_version[0], minor=py_version[1]) + + +def _cpython_abis(py_version): + abis = [] + version = "{}{}".format(*py_version[:2]) + debug = pymalloc = ucs4 = "" + with_debug = sysconfig.get_config_var("Py_DEBUG") + has_refcount = hasattr(sys, "gettotalrefcount") + # Windows doesn't set Py_DEBUG, so checking for support of debug-compiled + # extension modules is the best option. + # https://github.com/pypa/pip/issues/3383#issuecomment-173267692 + has_ext = "_d.pyd" in EXTENSION_SUFFIXES + if with_debug or (with_debug is None and (has_refcount or has_ext)): + debug = "d" + if py_version < (3, 8): + with_pymalloc = sysconfig.get_config_var("WITH_PYMALLOC") + if with_pymalloc or with_pymalloc is None: + pymalloc = "m" + if py_version < (3, 3): + unicode_size = sysconfig.get_config_var("Py_UNICODE_SIZE") + if unicode_size == 4 or ( + unicode_size is None and sys.maxunicode == 0x10FFFF + ): + ucs4 = "u" + elif debug: + # Debug builds can also load "normal" extension modules. + # We can also assume no UCS-4 or pymalloc requirement. + abis.append("cp{version}".format(version=version)) + abis.insert( + 0, + "cp{version}{debug}{pymalloc}{ucs4}".format( + version=version, debug=debug, pymalloc=pymalloc, ucs4=ucs4 + ), + ) + return abis + + +def _cpython_tags(py_version, interpreter, abis, platforms): + for abi in abis: + for platform_ in platforms: + yield Tag(interpreter, abi, platform_) + for tag in (Tag(interpreter, "abi3", platform_) for platform_ in platforms): + yield tag + for tag in (Tag(interpreter, "none", platform_) for platform_ in platforms): + yield tag + # PEP 384 was first implemented in Python 3.2. + for minor_version in range(py_version[1] - 1, 1, -1): + for platform_ in platforms: + interpreter = "cp{major}{minor}".format( + major=py_version[0], minor=minor_version + ) + yield Tag(interpreter, "abi3", platform_) + + +def _pypy_interpreter(): + return "pp{py_major}{pypy_major}{pypy_minor}".format( + py_major=sys.version_info[0], + pypy_major=sys.pypy_version_info.major, + pypy_minor=sys.pypy_version_info.minor, + ) + + +def _generic_abi(): + abi = sysconfig.get_config_var("SOABI") + if abi: + return _normalize_string(abi) + else: + return "none" + + +def _pypy_tags(py_version, interpreter, abi, platforms): + for tag in (Tag(interpreter, abi, platform) for platform in platforms): + yield tag + for tag in (Tag(interpreter, "none", platform) for platform in platforms): + yield tag + + +def _generic_tags(interpreter, py_version, abi, platforms): + for tag in (Tag(interpreter, abi, platform) for platform in platforms): + yield tag + if abi != "none": + tags = (Tag(interpreter, "none", platform_) for platform_ in platforms) + for tag in tags: + yield tag + + +def _py_interpreter_range(py_version): + """ + Yield Python versions in descending order. + + After the latest version, the major-only version will be yielded, and then + all following versions up to 'end'. + """ + yield "py{major}{minor}".format(major=py_version[0], minor=py_version[1]) + yield "py{major}".format(major=py_version[0]) + for minor in range(py_version[1] - 1, -1, -1): + yield "py{major}{minor}".format(major=py_version[0], minor=minor) + + +def _independent_tags(interpreter, py_version, platforms): + """ + Return the sequence of tags that are consistent across implementations. + + The tags consist of: + - py*-none- + - -none-any + - py*-none-any + """ + for version in _py_interpreter_range(py_version): + for platform_ in platforms: + yield Tag(version, "none", platform_) + yield Tag(interpreter, "none", "any") + for version in _py_interpreter_range(py_version): + yield Tag(version, "none", "any") + + +def _mac_arch(arch, is_32bit=_32_BIT_INTERPRETER): + if not is_32bit: + return arch + + if arch.startswith("ppc"): + return "ppc" + + return "i386" + + +def _mac_binary_formats(version, cpu_arch): + formats = [cpu_arch] + if cpu_arch == "x86_64": + if version < (10, 4): + return [] + formats.extend(["intel", "fat64", "fat32"]) + + elif cpu_arch == "i386": + if version < (10, 4): + return [] + formats.extend(["intel", "fat32", "fat"]) + + elif cpu_arch == "ppc64": + # TODO: Need to care about 32-bit PPC for ppc64 through 10.2? + if version > (10, 5) or version < (10, 4): + return [] + formats.append("fat64") + + elif cpu_arch == "ppc": + if version > (10, 6): + return [] + formats.extend(["fat32", "fat"]) + + formats.append("universal") + return formats + + +def _mac_platforms(version=None, arch=None): + version_str, _, cpu_arch = platform.mac_ver() + if version is None: + version = tuple(map(int, version_str.split(".")[:2])) + if arch is None: + arch = _mac_arch(cpu_arch) + platforms = [] + for minor_version in range(version[1], -1, -1): + compat_version = version[0], minor_version + binary_formats = _mac_binary_formats(compat_version, arch) + for binary_format in binary_formats: + platforms.append( + "macosx_{major}_{minor}_{binary_format}".format( + major=compat_version[0], + minor=compat_version[1], + binary_format=binary_format, + ) + ) + return platforms + + +# From PEP 513. +def _is_manylinux_compatible(name, glibc_version): + # Check for presence of _manylinux module. + try: + import _manylinux + + return bool(getattr(_manylinux, name + "_compatible")) + except (ImportError, AttributeError): + # Fall through to heuristic check below. + pass + + return _have_compatible_glibc(*glibc_version) + + +def _glibc_version_string(): + # Returns glibc version string, or None if not using glibc. + import ctypes + + # 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[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): + version_str = _glibc_version_string() + if version_str is None: + return False + return _check_glibc_version(version_str, required_major, minimum_minor) + + +def _linux_platforms(is_32bit=_32_BIT_INTERPRETER): + linux = _normalize_string(distutils.util.get_platform()) + if linux == "linux_x86_64" and is_32bit: + linux = "linux_i686" + manylinux_support = ( + ("manylinux2014", (2, 17)), # CentOS 7 w/ glibc 2.17 (PEP 599) + ("manylinux2010", (2, 12)), # CentOS 6 w/ glibc 2.12 (PEP 571) + ("manylinux1", (2, 5)), # CentOS 5 w/ glibc 2.5 (PEP 513) + ) + manylinux_support_iter = iter(manylinux_support) + for name, glibc_version in manylinux_support_iter: + if _is_manylinux_compatible(name, glibc_version): + platforms = [linux.replace("linux", name)] + break + else: + platforms = [] + # Support for a later manylinux implies support for an earlier version. + platforms += [linux.replace("linux", name) for name, _ in manylinux_support_iter] + platforms.append(linux) + return platforms + + +def _generic_platforms(): + platform = _normalize_string(distutils.util.get_platform()) + return [platform] + + +def _interpreter_name(): + name = platform.python_implementation().lower() + return INTERPRETER_SHORT_NAMES.get(name) or name + + +def _generic_interpreter(name, py_version): + version = sysconfig.get_config_var("py_version_nodot") + if not version: + version = "".join(map(str, py_version[:2])) + return "{name}{version}".format(name=name, version=version) + + +def sys_tags(): + """ + Returns the sequence of tag triples for the running interpreter. + + The order of the sequence corresponds to priority order for the + interpreter, from most to least important. + """ + py_version = sys.version_info[:2] + interpreter_name = _interpreter_name() + if platform.system() == "Darwin": + platforms = _mac_platforms() + elif platform.system() == "Linux": + platforms = _linux_platforms() + else: + platforms = _generic_platforms() + + if interpreter_name == "cp": + interpreter = _cpython_interpreter(py_version) + abis = _cpython_abis(py_version) + for tag in _cpython_tags(py_version, interpreter, abis, platforms): + yield tag + elif interpreter_name == "pp": + interpreter = _pypy_interpreter() + abi = _generic_abi() + for tag in _pypy_tags(py_version, interpreter, abi, platforms): + yield tag + else: + interpreter = _generic_interpreter(interpreter_name, py_version) + abi = _generic_abi() + for tag in _generic_tags(interpreter, py_version, abi, platforms): + yield tag + for tag in _independent_tags(interpreter, py_version, platforms): + yield tag diff --git a/venv/lib/python3.8/site-packages/setuptools/_vendor/packaging/utils.py b/venv/lib/python3.8/site-packages/setuptools/_vendor/packaging/utils.py new file mode 100644 index 0000000..8841878 --- /dev/null +++ b/venv/lib/python3.8/site-packages/setuptools/_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/venv/lib/python3.8/site-packages/setuptools/_vendor/packaging/version.py b/venv/lib/python3.8/site-packages/setuptools/_vendor/packaging/version.py new file mode 100644 index 0000000..95157a1 --- /dev/null +++ b/venv/lib/python3.8/site-packages/setuptools/_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 "".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[0-9]+)!)? # epoch + (?P[0-9]+(?:\.[0-9]+)*) # release segment + (?P
                                          # pre-release
+            [-_\.]?
+            (?P(a|b|c|rc|alpha|beta|pre|preview))
+            [-_\.]?
+            (?P[0-9]+)?
+        )?
+        (?P                                         # post release
+            (?:-(?P[0-9]+))
+            |
+            (?:
+                [-_\.]?
+                (?Ppost|rev|r)
+                [-_\.]?
+                (?P[0-9]+)?
+            )
+        )?
+        (?P                                          # dev release
+            [-_\.]?
+            (?Pdev)
+            [-_\.]?
+            (?P[0-9]+)?
+        )?
+    )
+    (?:\+(?P[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 "".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/venv/lib/python3.8/site-packages/setuptools/_vendor/pyparsing.py b/venv/lib/python3.8/site-packages/setuptools/_vendor/pyparsing.py
new file mode 100644
index 0000000..cf75e1e
--- /dev/null
+++ b/venv/lib/python3.8/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{", !"}), built up using L{Word}, L{Literal}, and L{And} elements 
+(L{'+'} 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} 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{'+'}, L{'|'}, L{'^'}, and L{'&'} 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 "
+
+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} ('-' 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.} - 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) # ->  ['sldkj', 'lsdkj', 'sldkj']
+            
+            # Use asList() to create an actual list
+            result_list = result.asList()
+            print(type(result_list), result_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)) # ->  (['12', '/', '31', '/', '1999'], {'day': [('1999', 4)], 'year': [('12', 0)], 'month': [('31', 2)]})
+            
+            result_dict = result.asDict()
+            print(type(result_dict), repr(result_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,
+                                                "" ]
+
+        out += [ nl, indent, "" ]
+        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}} for more information
+   on parsing strings containing C{}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} for more information
+   on parsing strings containing C{}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 = ""
+    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,  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 = ""  # 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}} for more information
+        on parsing strings containing C{}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}}.
+        
+        See examples in L{I{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}} 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}})
+         - 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}} 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 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{}s to spaces before parsing the input string.
+        Must be called before C{parseString} when the input grammar contains elements that
+        match C{} 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  at loc (,)"}
+        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 (loc4:
+                    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...)}), these will be preserved as 
+    named parse results.
+
+    Example::
+        realnum = Regex(r"[+-]?\d+\.\d*")
+        date = Regex(r'(?P\d{4})-(?P\d\d?)-(?P\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 = {
+        " " : "",
+        "\t": "",
+        "\n": "",
+        "\r": "",
+        "\f": "",
+        }
+    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):
+            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 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'], {}))
+        <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( "< ['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  bold text  normal text "
+        for tag in ("b","i"):
+            opener,closer = makeHTMLTags(tag)
+            patt = originalTextFor(opener + SkipTo(closer) + closer)
+            print(patt.searchString(src)[0])
+    prints::
+        [' bold text ']
+        ['text']
+    """
+    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{} 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}()}.
+
+    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("")
+
+    openTag = openTag.setResultsName("start"+"".join(resname.replace(":"," ").title().split())).setName("<%s>" % resname)
+    closeTag = closeTag.setResultsName("end"+"".join(resname.replace(":"," ").title().split())).setName("" % 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 = 'More info at the pyparsing wiki page'
+        # 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  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{} or C{
}. + + 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 = ''' +
+ Some text +
1 4 0 1 0
+
1,3 2,3 1,1
+
this has no type
+
+ + ''' + 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 = ''' +
+ Some text +
1 4 0 1 0
+
1,3 2,3 1,1
+
this <div> has no class
+
+ + ''' + 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' + '|'.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"").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}, L{reals}, L{scientific notation}) + - common L{programming identifiers} + - network addresses (L{MAC}, L{IPv4}, L{IPv6}) + - ISO8601 L{dates} and L{datetime} + - L{UUID} + - L{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\d{4})(?:-(?P\d\d)(?:-(?P\d\d))?)?').setName("ISO8601 date") + "ISO8601 date (C{yyyy-mm-dd})" + + iso8601_datetime = Regex(r'(?P\d{4})-(?P\d\d)-(?P\d\d)[T ](?P\d\d):(?P\d\d)(:(?P\d\d(\.\d*)?)?)?(?PZ|[+-]\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 = 'More info at the
pyparsing wiki page' + 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/venv/lib/python3.8/site-packages/setuptools/_vendor/six.py b/venv/lib/python3.8/site-packages/setuptools/_vendor/six.py new file mode 100644 index 0000000..190c023 --- /dev/null +++ b/venv/lib/python3.8/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 " +__version__ = "1.10.0" + + +# Useful for very coarse version differentiation. +PY2 = sys.version_info[0] == 2 +PY3 = sys.version_info[0] == 3 +PY34 = sys.version_info[0:2] >= (3, 4) + +if PY3: + string_types = str, + integer_types = int, + class_types = type, + text_type = str + binary_type = bytes + + MAXSIZE = sys.maxsize +else: + string_types = basestring, + integer_types = (int, long) + class_types = (type, types.ClassType) + text_type = unicode + binary_type = str + + if sys.platform.startswith("java"): + # Jython always uses 32 bits. + MAXSIZE = int((1 << 31) - 1) + else: + # It's possible to have sizeof(long) != sizeof(Py_ssize_t). + class X(object): + + def __len__(self): + return 1 << 31 + try: + len(X()) + except OverflowError: + # 32-bit + MAXSIZE = int((1 << 31) - 1) + else: + # 64-bit + MAXSIZE = int((1 << 63) - 1) + del X + + +def _add_doc(func, doc): + """Add documentation to a function.""" + func.__doc__ = doc + + +def _import_module(name): + """Import module, returning the module after the last dot.""" + __import__(name) + return sys.modules[name] + + +class _LazyDescr(object): + + def __init__(self, name): + self.name = name + + def __get__(self, obj, tp): + result = self._resolve() + setattr(obj, self.name, result) # Invokes __set__. + try: + # This is a bit ugly, but it avoids running this again by + # removing this descriptor. + delattr(obj.__class__, self.name) + except AttributeError: + pass + return result + + +class MovedModule(_LazyDescr): + + def __init__(self, name, old, new=None): + super(MovedModule, self).__init__(name) + if PY3: + if new is None: + new = name + self.mod = new + else: + self.mod = old + + def _resolve(self): + return _import_module(self.mod) + + def __getattr__(self, attr): + _module = self._resolve() + value = getattr(_module, attr) + setattr(self, attr, value) + return value + + +class _LazyModule(types.ModuleType): + + def __init__(self, name): + super(_LazyModule, self).__init__(name) + self.__doc__ = self.__class__.__doc__ + + def __dir__(self): + attrs = ["__doc__", "__name__"] + attrs += [attr.name for attr in self._moved_attributes] + return attrs + + # Subclasses should override this + _moved_attributes = [] + + +class MovedAttribute(_LazyDescr): + + def __init__(self, name, old_mod, new_mod, old_attr=None, new_attr=None): + super(MovedAttribute, self).__init__(name) + if PY3: + if new_mod is None: + new_mod = name + self.mod = new_mod + if new_attr is None: + if old_attr is None: + new_attr = name + else: + new_attr = old_attr + self.attr = new_attr + else: + self.mod = old_mod + if old_attr is None: + old_attr = name + self.attr = old_attr + + def _resolve(self): + module = _import_module(self.mod) + return getattr(module, self.attr) + + +class _SixMetaPathImporter(object): + + """ + A meta path importer to import six.moves and its submodules. + + This class implements a PEP302 finder and loader. It should be compatible + with Python 2.5 and all existing versions of Python3 + """ + + def __init__(self, six_module_name): + self.name = six_module_name + self.known_modules = {} + + def _add_module(self, mod, *fullnames): + for fullname in fullnames: + self.known_modules[self.name + "." + fullname] = mod + + def _get_module(self, fullname): + return self.known_modules[self.name + "." + fullname] + + def find_module(self, fullname, path=None): + if fullname in self.known_modules: + return self + return None + + def __get_module(self, fullname): + try: + return self.known_modules[fullname] + except KeyError: + raise ImportError("This loader does not know module " + fullname) + + def load_module(self, fullname): + try: + # in case of a reload + return sys.modules[fullname] + except KeyError: + pass + mod = self.__get_module(fullname) + if isinstance(mod, MovedModule): + mod = mod._resolve() + else: + mod.__loader__ = self + sys.modules[fullname] = mod + return mod + + def is_package(self, fullname): + """ + Return true, if the named module is a package. + + We need this method to get correct spec objects with + Python 3.4 (see PEP451) + """ + return hasattr(self.__get_module(fullname), "__path__") + + def get_code(self, fullname): + """Return None + + Required, if is_package is implemented""" + self.__get_module(fullname) # eventually raises ImportError + return None + get_source = get_code # same as get_code + +_importer = _SixMetaPathImporter(__name__) + + +class _MovedItems(_LazyModule): + + """Lazy loading of moved objects""" + __path__ = [] # mark as package + + +_moved_attributes = [ + MovedAttribute("cStringIO", "cStringIO", "io", "StringIO"), + MovedAttribute("filter", "itertools", "builtins", "ifilter", "filter"), + MovedAttribute("filterfalse", "itertools", "itertools", "ifilterfalse", "filterfalse"), + MovedAttribute("input", "__builtin__", "builtins", "raw_input", "input"), + MovedAttribute("intern", "__builtin__", "sys"), + MovedAttribute("map", "itertools", "builtins", "imap", "map"), + MovedAttribute("getcwd", "os", "os", "getcwdu", "getcwd"), + MovedAttribute("getcwdb", "os", "os", "getcwd", "getcwdb"), + MovedAttribute("range", "__builtin__", "builtins", "xrange", "range"), + MovedAttribute("reload_module", "__builtin__", "importlib" if PY34 else "imp", "reload"), + MovedAttribute("reduce", "__builtin__", "functools"), + MovedAttribute("shlex_quote", "pipes", "shlex", "quote"), + MovedAttribute("StringIO", "StringIO", "io"), + MovedAttribute("UserDict", "UserDict", "collections"), + MovedAttribute("UserList", "UserList", "collections"), + MovedAttribute("UserString", "UserString", "collections"), + MovedAttribute("xrange", "__builtin__", "builtins", "xrange", "range"), + MovedAttribute("zip", "itertools", "builtins", "izip", "zip"), + MovedAttribute("zip_longest", "itertools", "itertools", "izip_longest", "zip_longest"), + MovedModule("builtins", "__builtin__"), + MovedModule("configparser", "ConfigParser"), + MovedModule("copyreg", "copy_reg"), + MovedModule("dbm_gnu", "gdbm", "dbm.gnu"), + MovedModule("_dummy_thread", "dummy_thread", "_dummy_thread"), + MovedModule("http_cookiejar", "cookielib", "http.cookiejar"), + MovedModule("http_cookies", "Cookie", "http.cookies"), + MovedModule("html_entities", "htmlentitydefs", "html.entities"), + MovedModule("html_parser", "HTMLParser", "html.parser"), + MovedModule("http_client", "httplib", "http.client"), + MovedModule("email_mime_multipart", "email.MIMEMultipart", "email.mime.multipart"), + MovedModule("email_mime_nonmultipart", "email.MIMENonMultipart", "email.mime.nonmultipart"), + MovedModule("email_mime_text", "email.MIMEText", "email.mime.text"), + MovedModule("email_mime_base", "email.MIMEBase", "email.mime.base"), + MovedModule("BaseHTTPServer", "BaseHTTPServer", "http.server"), + MovedModule("CGIHTTPServer", "CGIHTTPServer", "http.server"), + MovedModule("SimpleHTTPServer", "SimpleHTTPServer", "http.server"), + MovedModule("cPickle", "cPickle", "pickle"), + MovedModule("queue", "Queue"), + MovedModule("reprlib", "repr"), + MovedModule("socketserver", "SocketServer"), + MovedModule("_thread", "thread", "_thread"), + MovedModule("tkinter", "Tkinter"), + MovedModule("tkinter_dialog", "Dialog", "tkinter.dialog"), + MovedModule("tkinter_filedialog", "FileDialog", "tkinter.filedialog"), + MovedModule("tkinter_scrolledtext", "ScrolledText", "tkinter.scrolledtext"), + MovedModule("tkinter_simpledialog", "SimpleDialog", "tkinter.simpledialog"), + MovedModule("tkinter_tix", "Tix", "tkinter.tix"), + MovedModule("tkinter_ttk", "ttk", "tkinter.ttk"), + MovedModule("tkinter_constants", "Tkconstants", "tkinter.constants"), + MovedModule("tkinter_dnd", "Tkdnd", "tkinter.dnd"), + MovedModule("tkinter_colorchooser", "tkColorChooser", + "tkinter.colorchooser"), + MovedModule("tkinter_commondialog", "tkCommonDialog", + "tkinter.commondialog"), + MovedModule("tkinter_tkfiledialog", "tkFileDialog", "tkinter.filedialog"), + MovedModule("tkinter_font", "tkFont", "tkinter.font"), + MovedModule("tkinter_messagebox", "tkMessageBox", "tkinter.messagebox"), + MovedModule("tkinter_tksimpledialog", "tkSimpleDialog", + "tkinter.simpledialog"), + MovedModule("urllib_parse", __name__ + ".moves.urllib_parse", "urllib.parse"), + MovedModule("urllib_error", __name__ + ".moves.urllib_error", "urllib.error"), + MovedModule("urllib", __name__ + ".moves.urllib", __name__ + ".moves.urllib"), + MovedModule("urllib_robotparser", "robotparser", "urllib.robotparser"), + MovedModule("xmlrpc_client", "xmlrpclib", "xmlrpc.client"), + MovedModule("xmlrpc_server", "SimpleXMLRPCServer", "xmlrpc.server"), +] +# Add windows specific modules. +if sys.platform == "win32": + _moved_attributes += [ + MovedModule("winreg", "_winreg"), + ] + +for attr in _moved_attributes: + setattr(_MovedItems, attr.name, attr) + if isinstance(attr, MovedModule): + _importer._add_module(attr, "moves." + attr.name) +del attr + +_MovedItems._moved_attributes = _moved_attributes + +moves = _MovedItems(__name__ + ".moves") +_importer._add_module(moves, "moves") + + +class Module_six_moves_urllib_parse(_LazyModule): + + """Lazy loading of moved objects in six.moves.urllib_parse""" + + +_urllib_parse_moved_attributes = [ + MovedAttribute("ParseResult", "urlparse", "urllib.parse"), + MovedAttribute("SplitResult", "urlparse", "urllib.parse"), + MovedAttribute("parse_qs", "urlparse", "urllib.parse"), + MovedAttribute("parse_qsl", "urlparse", "urllib.parse"), + MovedAttribute("urldefrag", "urlparse", "urllib.parse"), + MovedAttribute("urljoin", "urlparse", "urllib.parse"), + MovedAttribute("urlparse", "urlparse", "urllib.parse"), + MovedAttribute("urlsplit", "urlparse", "urllib.parse"), + MovedAttribute("urlunparse", "urlparse", "urllib.parse"), + MovedAttribute("urlunsplit", "urlparse", "urllib.parse"), + MovedAttribute("quote", "urllib", "urllib.parse"), + MovedAttribute("quote_plus", "urllib", "urllib.parse"), + MovedAttribute("unquote", "urllib", "urllib.parse"), + MovedAttribute("unquote_plus", "urllib", "urllib.parse"), + MovedAttribute("urlencode", "urllib", "urllib.parse"), + MovedAttribute("splitquery", "urllib", "urllib.parse"), + MovedAttribute("splittag", "urllib", "urllib.parse"), + MovedAttribute("splituser", "urllib", "urllib.parse"), + MovedAttribute("uses_fragment", "urlparse", "urllib.parse"), + MovedAttribute("uses_netloc", "urlparse", "urllib.parse"), + MovedAttribute("uses_params", "urlparse", "urllib.parse"), + MovedAttribute("uses_query", "urlparse", "urllib.parse"), + MovedAttribute("uses_relative", "urlparse", "urllib.parse"), +] +for attr in _urllib_parse_moved_attributes: + setattr(Module_six_moves_urllib_parse, attr.name, attr) +del attr + +Module_six_moves_urllib_parse._moved_attributes = _urllib_parse_moved_attributes + +_importer._add_module(Module_six_moves_urllib_parse(__name__ + ".moves.urllib_parse"), + "moves.urllib_parse", "moves.urllib.parse") + + +class Module_six_moves_urllib_error(_LazyModule): + + """Lazy loading of moved objects in six.moves.urllib_error""" + + +_urllib_error_moved_attributes = [ + MovedAttribute("URLError", "urllib2", "urllib.error"), + MovedAttribute("HTTPError", "urllib2", "urllib.error"), + MovedAttribute("ContentTooShortError", "urllib", "urllib.error"), +] +for attr in _urllib_error_moved_attributes: + setattr(Module_six_moves_urllib_error, attr.name, attr) +del attr + +Module_six_moves_urllib_error._moved_attributes = _urllib_error_moved_attributes + +_importer._add_module(Module_six_moves_urllib_error(__name__ + ".moves.urllib.error"), + "moves.urllib_error", "moves.urllib.error") + + +class Module_six_moves_urllib_request(_LazyModule): + + """Lazy loading of moved objects in six.moves.urllib_request""" + + +_urllib_request_moved_attributes = [ + MovedAttribute("urlopen", "urllib2", "urllib.request"), + MovedAttribute("install_opener", "urllib2", "urllib.request"), + MovedAttribute("build_opener", "urllib2", "urllib.request"), + MovedAttribute("pathname2url", "urllib", "urllib.request"), + MovedAttribute("url2pathname", "urllib", "urllib.request"), + MovedAttribute("getproxies", "urllib", "urllib.request"), + MovedAttribute("Request", "urllib2", "urllib.request"), + MovedAttribute("OpenerDirector", "urllib2", "urllib.request"), + MovedAttribute("HTTPDefaultErrorHandler", "urllib2", "urllib.request"), + MovedAttribute("HTTPRedirectHandler", "urllib2", "urllib.request"), + MovedAttribute("HTTPCookieProcessor", "urllib2", "urllib.request"), + MovedAttribute("ProxyHandler", "urllib2", "urllib.request"), + MovedAttribute("BaseHandler", "urllib2", "urllib.request"), + MovedAttribute("HTTPPasswordMgr", "urllib2", "urllib.request"), + MovedAttribute("HTTPPasswordMgrWithDefaultRealm", "urllib2", "urllib.request"), + MovedAttribute("AbstractBasicAuthHandler", "urllib2", "urllib.request"), + MovedAttribute("HTTPBasicAuthHandler", "urllib2", "urllib.request"), + MovedAttribute("ProxyBasicAuthHandler", "urllib2", "urllib.request"), + MovedAttribute("AbstractDigestAuthHandler", "urllib2", "urllib.request"), + MovedAttribute("HTTPDigestAuthHandler", "urllib2", "urllib.request"), + MovedAttribute("ProxyDigestAuthHandler", "urllib2", "urllib.request"), + MovedAttribute("HTTPHandler", "urllib2", "urllib.request"), + MovedAttribute("HTTPSHandler", "urllib2", "urllib.request"), + MovedAttribute("FileHandler", "urllib2", "urllib.request"), + MovedAttribute("FTPHandler", "urllib2", "urllib.request"), + MovedAttribute("CacheFTPHandler", "urllib2", "urllib.request"), + MovedAttribute("UnknownHandler", "urllib2", "urllib.request"), + MovedAttribute("HTTPErrorProcessor", "urllib2", "urllib.request"), + MovedAttribute("urlretrieve", "urllib", "urllib.request"), + MovedAttribute("urlcleanup", "urllib", "urllib.request"), + MovedAttribute("URLopener", "urllib", "urllib.request"), + MovedAttribute("FancyURLopener", "urllib", "urllib.request"), + MovedAttribute("proxy_bypass", "urllib", "urllib.request"), +] +for attr in _urllib_request_moved_attributes: + setattr(Module_six_moves_urllib_request, attr.name, attr) +del attr + +Module_six_moves_urllib_request._moved_attributes = _urllib_request_moved_attributes + +_importer._add_module(Module_six_moves_urllib_request(__name__ + ".moves.urllib.request"), + "moves.urllib_request", "moves.urllib.request") + + +class Module_six_moves_urllib_response(_LazyModule): + + """Lazy loading of moved objects in six.moves.urllib_response""" + + +_urllib_response_moved_attributes = [ + MovedAttribute("addbase", "urllib", "urllib.response"), + MovedAttribute("addclosehook", "urllib", "urllib.response"), + MovedAttribute("addinfo", "urllib", "urllib.response"), + MovedAttribute("addinfourl", "urllib", "urllib.response"), +] +for attr in _urllib_response_moved_attributes: + setattr(Module_six_moves_urllib_response, attr.name, attr) +del attr + +Module_six_moves_urllib_response._moved_attributes = _urllib_response_moved_attributes + +_importer._add_module(Module_six_moves_urllib_response(__name__ + ".moves.urllib.response"), + "moves.urllib_response", "moves.urllib.response") + + +class Module_six_moves_urllib_robotparser(_LazyModule): + + """Lazy loading of moved objects in six.moves.urllib_robotparser""" + + +_urllib_robotparser_moved_attributes = [ + MovedAttribute("RobotFileParser", "robotparser", "urllib.robotparser"), +] +for attr in _urllib_robotparser_moved_attributes: + setattr(Module_six_moves_urllib_robotparser, attr.name, attr) +del attr + +Module_six_moves_urllib_robotparser._moved_attributes = _urllib_robotparser_moved_attributes + +_importer._add_module(Module_six_moves_urllib_robotparser(__name__ + ".moves.urllib.robotparser"), + "moves.urllib_robotparser", "moves.urllib.robotparser") + + +class Module_six_moves_urllib(types.ModuleType): + + """Create a six.moves.urllib namespace that resembles the Python 3 namespace""" + __path__ = [] # mark as package + parse = _importer._get_module("moves.urllib_parse") + error = _importer._get_module("moves.urllib_error") + request = _importer._get_module("moves.urllib_request") + response = _importer._get_module("moves.urllib_response") + robotparser = _importer._get_module("moves.urllib_robotparser") + + def __dir__(self): + return ['parse', 'error', 'request', 'response', 'robotparser'] + +_importer._add_module(Module_six_moves_urllib(__name__ + ".moves.urllib"), + "moves.urllib") + + +def add_move(move): + """Add an item to six.moves.""" + setattr(_MovedItems, move.name, move) + + +def remove_move(name): + """Remove item from six.moves.""" + try: + delattr(_MovedItems, name) + except AttributeError: + try: + del moves.__dict__[name] + except KeyError: + raise AttributeError("no such move, %r" % (name,)) + + +if PY3: + _meth_func = "__func__" + _meth_self = "__self__" + + _func_closure = "__closure__" + _func_code = "__code__" + _func_defaults = "__defaults__" + _func_globals = "__globals__" +else: + _meth_func = "im_func" + _meth_self = "im_self" + + _func_closure = "func_closure" + _func_code = "func_code" + _func_defaults = "func_defaults" + _func_globals = "func_globals" + + +try: + advance_iterator = next +except NameError: + def advance_iterator(it): + return it.next() +next = advance_iterator + + +try: + callable = callable +except NameError: + def callable(obj): + return any("__call__" in klass.__dict__ for klass in type(obj).__mro__) + + +if PY3: + def get_unbound_function(unbound): + return unbound + + create_bound_method = types.MethodType + + def create_unbound_method(func, cls): + return func + + Iterator = object +else: + def get_unbound_function(unbound): + return unbound.im_func + + def create_bound_method(func, obj): + return types.MethodType(func, obj, obj.__class__) + + def create_unbound_method(func, cls): + return types.MethodType(func, None, cls) + + class Iterator(object): + + def next(self): + return type(self).__next__(self) + + callable = callable +_add_doc(get_unbound_function, + """Get the function out of a possibly unbound function""") + + +get_method_function = operator.attrgetter(_meth_func) +get_method_self = operator.attrgetter(_meth_self) +get_function_closure = operator.attrgetter(_func_closure) +get_function_code = operator.attrgetter(_func_code) +get_function_defaults = operator.attrgetter(_func_defaults) +get_function_globals = operator.attrgetter(_func_globals) + + +if PY3: + def iterkeys(d, **kw): + return iter(d.keys(**kw)) + + def itervalues(d, **kw): + return iter(d.values(**kw)) + + def iteritems(d, **kw): + return iter(d.items(**kw)) + + def iterlists(d, **kw): + return iter(d.lists(**kw)) + + viewkeys = operator.methodcaller("keys") + + viewvalues = operator.methodcaller("values") + + viewitems = operator.methodcaller("items") +else: + def iterkeys(d, **kw): + return d.iterkeys(**kw) + + def itervalues(d, **kw): + return d.itervalues(**kw) + + def iteritems(d, **kw): + return d.iteritems(**kw) + + def iterlists(d, **kw): + return d.iterlists(**kw) + + viewkeys = operator.methodcaller("viewkeys") + + viewvalues = operator.methodcaller("viewvalues") + + viewitems = operator.methodcaller("viewitems") + +_add_doc(iterkeys, "Return an iterator over the keys of a dictionary.") +_add_doc(itervalues, "Return an iterator over the values of a dictionary.") +_add_doc(iteritems, + "Return an iterator over the (key, value) pairs of a dictionary.") +_add_doc(iterlists, + "Return an iterator over the (key, [values]) pairs of a dictionary.") + + +if PY3: + def b(s): + return s.encode("latin-1") + + def u(s): + return s + unichr = chr + import struct + int2byte = struct.Struct(">B").pack + del struct + byte2int = operator.itemgetter(0) + indexbytes = operator.getitem + iterbytes = iter + import io + StringIO = io.StringIO + BytesIO = io.BytesIO + _assertCountEqual = "assertCountEqual" + if sys.version_info[1] <= 1: + _assertRaisesRegex = "assertRaisesRegexp" + _assertRegex = "assertRegexpMatches" + else: + _assertRaisesRegex = "assertRaisesRegex" + _assertRegex = "assertRegex" +else: + def b(s): + return s + # Workaround for standalone backslash + + def u(s): + return unicode(s.replace(r'\\', r'\\\\'), "unicode_escape") + unichr = unichr + int2byte = chr + + def byte2int(bs): + return ord(bs[0]) + + def indexbytes(buf, i): + return ord(buf[i]) + iterbytes = functools.partial(itertools.imap, ord) + import StringIO + StringIO = BytesIO = StringIO.StringIO + _assertCountEqual = "assertItemsEqual" + _assertRaisesRegex = "assertRaisesRegexp" + _assertRegex = "assertRegexpMatches" +_add_doc(b, """Byte literal""") +_add_doc(u, """Text literal""") + + +def assertCountEqual(self, *args, **kwargs): + return getattr(self, _assertCountEqual)(*args, **kwargs) + + +def assertRaisesRegex(self, *args, **kwargs): + return getattr(self, _assertRaisesRegex)(*args, **kwargs) + + +def assertRegex(self, *args, **kwargs): + return getattr(self, _assertRegex)(*args, **kwargs) + + +if PY3: + exec_ = getattr(moves.builtins, "exec") + + def reraise(tp, value, tb=None): + if value is None: + value = tp() + if value.__traceback__ is not tb: + raise value.with_traceback(tb) + raise value + +else: + def exec_(_code_, _globs_=None, _locs_=None): + """Execute code in a namespace.""" + if _globs_ is None: + frame = sys._getframe(1) + _globs_ = frame.f_globals + if _locs_ is None: + _locs_ = frame.f_locals + del frame + elif _locs_ is None: + _locs_ = _globs_ + exec("""exec _code_ in _globs_, _locs_""") + + exec_("""def reraise(tp, value, tb=None): + raise tp, value, tb +""") + + +if sys.version_info[:2] == (3, 2): + exec_("""def raise_from(value, from_value): + if from_value is None: + raise value + raise value from from_value +""") +elif sys.version_info[:2] > (3, 2): + exec_("""def raise_from(value, from_value): + raise value from from_value +""") +else: + def raise_from(value, from_value): + raise value + + +print_ = getattr(moves.builtins, "print", None) +if print_ is None: + def print_(*args, **kwargs): + """The new-style print function for Python 2.4 and 2.5.""" + fp = kwargs.pop("file", sys.stdout) + if fp is None: + return + + def write(data): + if not isinstance(data, basestring): + data = str(data) + # If the file has an encoding, encode unicode with it. + if (isinstance(fp, file) and + isinstance(data, unicode) and + fp.encoding is not None): + errors = getattr(fp, "errors", None) + if errors is None: + errors = "strict" + data = data.encode(fp.encoding, errors) + fp.write(data) + want_unicode = False + sep = kwargs.pop("sep", None) + if sep is not None: + if isinstance(sep, unicode): + want_unicode = True + elif not isinstance(sep, str): + raise TypeError("sep must be None or a string") + end = kwargs.pop("end", None) + if end is not None: + if isinstance(end, unicode): + want_unicode = True + elif not isinstance(end, str): + raise TypeError("end must be None or a string") + if kwargs: + raise TypeError("invalid keyword arguments to print()") + if not want_unicode: + for arg in args: + if isinstance(arg, unicode): + want_unicode = True + break + if want_unicode: + newline = unicode("\n") + space = unicode(" ") + else: + newline = "\n" + space = " " + if sep is None: + sep = space + if end is None: + end = newline + for i, arg in enumerate(args): + if i: + write(sep) + write(arg) + write(end) +if sys.version_info[:2] < (3, 3): + _print = print_ + + def print_(*args, **kwargs): + fp = kwargs.get("file", sys.stdout) + flush = kwargs.pop("flush", False) + _print(*args, **kwargs) + if flush and fp is not None: + fp.flush() + +_add_doc(reraise, """Reraise an exception.""") + +if sys.version_info[0:2] < (3, 4): + def wraps(wrapped, assigned=functools.WRAPPER_ASSIGNMENTS, + updated=functools.WRAPPER_UPDATES): + def wrapper(f): + f = functools.wraps(wrapped, assigned, updated)(f) + f.__wrapped__ = wrapped + return f + return wrapper +else: + wraps = functools.wraps + + +def with_metaclass(meta, *bases): + """Create a base class with a metaclass.""" + # This requires a bit of explanation: the basic idea is to make a dummy + # metaclass for one level of class instantiation that replaces itself with + # the actual metaclass. + class metaclass(meta): + + def __new__(cls, name, this_bases, d): + return meta(name, bases, d) + return type.__new__(metaclass, 'temporary_class', (), {}) + + +def add_metaclass(metaclass): + """Class decorator for creating a class with a metaclass.""" + def wrapper(cls): + orig_vars = cls.__dict__.copy() + slots = orig_vars.get('__slots__') + if slots is not None: + if isinstance(slots, str): + slots = [slots] + for slots_var in slots: + orig_vars.pop(slots_var) + orig_vars.pop('__dict__', None) + orig_vars.pop('__weakref__', None) + return metaclass(cls.__name__, cls.__bases__, orig_vars) + return wrapper + + +def python_2_unicode_compatible(klass): + """ + A decorator that defines __unicode__ and __str__ methods under Python 2. + Under Python 3 it does nothing. + + To support Python 2 and 3 with a single code base, define a __str__ method + returning text and apply this decorator to the class. + """ + if PY2: + if '__str__' not in klass.__dict__: + raise ValueError("@python_2_unicode_compatible cannot be applied " + "to %s because it doesn't define __str__()." % + klass.__name__) + klass.__unicode__ = klass.__str__ + klass.__str__ = lambda self: self.__unicode__().encode('utf-8') + return klass + + +# Complete the moves implementation. +# This code is at the end of this module to speed up module loading. +# Turn this module into a package. +__path__ = [] # required for PEP 302 and PEP 451 +__package__ = __name__ # see PEP 366 @ReservedAssignment +if globals().get("__spec__") is not None: + __spec__.submodule_search_locations = [] # PEP 451 @UndefinedVariable +# Remove other six meta path importers, since they cause problems. This can +# happen if six is removed from sys.modules and then reloaded. (Setuptools does +# this for some reason.) +if sys.meta_path: + for i, importer in enumerate(sys.meta_path): + # Here's some real nastiness: Another "instance" of the six module might + # be floating around. Therefore, we can't use isinstance() to check for + # the six meta path importer, since the other six instance will have + # inserted an importer with different class. + if (type(importer).__name__ == "_SixMetaPathImporter" and + importer.name == __name__): + del sys.meta_path[i] + break + del i, importer +# Finally, add the importer to the meta path import hook. +sys.meta_path.append(_importer) diff --git a/venv/lib/python3.8/site-packages/setuptools/archive_util.py b/venv/lib/python3.8/site-packages/setuptools/archive_util.py new file mode 100644 index 0000000..64528ca --- /dev/null +++ b/venv/lib/python3.8/site-packages/setuptools/archive_util.py @@ -0,0 +1,175 @@ +"""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/venv/lib/python3.8/site-packages/setuptools/build_meta.py b/venv/lib/python3.8/site-packages/setuptools/build_meta.py new file mode 100644 index 0000000..a1c951c --- /dev/null +++ b/venv/lib/python3.8/site-packages/setuptools/build_meta.py @@ -0,0 +1,272 @@ +"""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 +from pkg_resources.py31compat import makedirs + +__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_with_temp_dir(self, setup_command, result_extension, + result_directory, config_settings): + config_settings = self._fix_config(config_settings) + result_directory = os.path.abspath(result_directory) + + # Build in a temporary directory, then copy to the target. + makedirs(result_directory, exist_ok=True) + with TemporaryDirectory(dir=result_directory) as tmp_dist_dir: + sys.argv = (sys.argv[:1] + setup_command + + ['--dist-dir', tmp_dist_dir] + + config_settings["--global-option"]) + self.run_setup() + + result_basename = _file_with_extension( + tmp_dist_dir, result_extension) + result_path = os.path.join(result_directory, result_basename) + if os.path.exists(result_path): + # os.rename will fail overwriting on non-Unix. + os.remove(result_path) + os.rename(os.path.join(tmp_dist_dir, result_basename), result_path) + + return result_basename + + def build_wheel(self, wheel_directory, config_settings=None, + metadata_directory=None): + return self._build_with_temp_dir(['bdist_wheel'], '.whl', + wheel_directory, config_settings) + + def build_sdist(self, sdist_directory, config_settings=None): + return self._build_with_temp_dir(['sdist', '--formats', 'gztar'], + '.tar.gz', sdist_directory, + config_settings) + + +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) + + # Some setup.py scripts (e.g. in pygame and numpy) use sys.argv[0] to + # get the directory of the source code. They expect it to refer to the + # setup.py script. + sys_argv_0 = sys.argv[0] + sys.argv[0] = setup_script + + 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 + sys.argv[0] = sys_argv_0 + + +# 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/venv/lib/python3.8/site-packages/setuptools/cli-32.exe b/venv/lib/python3.8/site-packages/setuptools/cli-32.exe new file mode 100644 index 0000000000000000000000000000000000000000..b1487b7819e7286577a043c7726fbe0ca1543083 GIT binary patch literal 65536 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

@|$jDp4t7maBT@by!vG1&J_?=DS4W3Hu6w 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[c}iI#RErv3nvcIClH@!{vM)zJ_weD zu_-L8NU*GlC{d0L!!VW10^+~>qmNB~Y8H+F}!P8_d(PpvjzMJQmr z)FkX;2B~<|3JfJeWv@IXo~nTtp$}Gjie> zs8UDG*kid(%i5QCBp~MA;#I186PI-nZ&k7!k8BiLJSuR>h7ArSYHD~B0I z=T6L{zqglekt0JjG5z&|GWb4?+B5+{p^fgTufl_KesA{@I&g7rNq==^SGc5GcM%$N zDBG2)qExz*Z;jGN_-iD-y8i2BCq)p}2lKcspLg>w-;qwg(()HXrZa3jd!}spuwBVX zwmX!iwU?#7uoQnunw|OlU~+c z^L5Ak3zWhaA4B^FhMMboO0k*O2GL)lD9_<$5b>czbCvKcSt+u*gA*=%dH>Q-Bc11h zzO7jbXN)&5mBf=w2anK6P$YcJZQoWa2#E!v{hFKxxm7Fc)Fc9iC35{|Lp7bIDjrhC zgMiGf4r2yquH{U7WdMio;XS4Y%Ry{q7#kv#gZ07i`7eo#MMh_o68E*Fd_#nrri^4b zX+slbsv>+8pmck%oLDUL()8NRJ#Z z8DReF_eq2zsjEXGs)yS{k}ykS1B!ZrY0f6O65^lslJv3g&wfpDg-&EwF8wrc=hSwm zPlV&n%%yE_@onOwK?)`GNJ6MQ0drMuBYWCH5dkD)uErh@*k}#GcFl<-;;TN+5vb|b zctkCv;*zL7f)A;QuO%(81r0)&aUz4EQu;kA!k@7i8RZ)koMaWW`5cC6n@{w!!J$5d zx}l)4VP4xL=BKi&c^{n_Qi`q@G{vimblcVR53b#*X$FUOQFm!A8JKahNSiBdY+x3bJZfD8n{--FLUM4+Mx@{vM_ep zkk)U=K8R(rhU(X_faI*ZO}cn`5t*O}lx^j8|0rt-)o=Axn^DGcQTi!#7hxLTq?|HQ zB;T6(nrsCeYK0_o%)IO+CP{n#+|;w1ZmvD2c-J{i88bp63RjyKOE!B!D3U{RCs*Zh z&^%65VM(J34230U4bHS}M@SYS9TEK}c%)2<$h1|T;##zRtjRt@#1T%J=kAhOiw+Z% z7DpyWVK@6%9K^uVD9LDKj)dR^aZK6$@Lt)l;sj@`QSzBm{TlLG{JKM_^60Zr2w~nr zr>P-BaV8OjjWm?hQ3$ZCx+lyD%q`~4iNF9xWKi$t&pzBhwN9Dq-o^v9@=abLR#|

KZqkLal4YCRR9VNhIM|rBqmzzcImvcx z66fD`zj4}M-A;gyA17cSC-oI$`q?*q&8~)Qv|C#(aSFd|hYbf}FFVB?n3Q?Svt+Td z#AW4x=9X}?aizE|`r{}3l-H&b6-{_j#STR!lD001vu;K>KT;*^ChCevBwCMFpg{JI zv``4YsjK1&142Pl%%A#u3rbGso1<_fngd1`+}!pMu@z5Me_5UFxiPYKqFL4_`WXmY zeWJrZUKzrrMuBcHupOq4Wr12sE*T-*CXh;FA=)Q+BMN(?DJ!kq?%Ww`xlG3e;lz2t zY?tl;i?gHO_79VwJ_cThq^>FqRUPlqS?IuI+CfSbNkv_1l~7eGaCwRmuOF|ic1ac2 z9ldo$TN~LhX~J01P75nyi&d8=Y@QNZ5e<=6v_R3rM}nN}5ae`^LV&sAD<=;*z=!~` zvJ0@i!orMuT*5kyXNzJnxfU!+#FTW(syy@yj7XX8#zD_9TWBSg(;KZ25VO;is;-&R zf(29n3U}agkC`j4sjX{=`D1EkCC@enOA~v{GOLYQKAdPN6+?W+QE4fLMhrW4RGbH5^K(rm4T}`=ra<6GP2}cRBE9K8^r(O+ZvKpJDL~qNguPmwQZp-8m7V@ zN^KFU8@Q*E7UJswZD=OYtct4KqA&NDKSOfc-#M>@o#)4;YLqtENdFS^3K9&dFBr|M z*loqE3X2sMmi8hv#7H5rqGc_y=ShEbHT^m7S`?4d%B+(-6dYGI-*t5E+< z^P3gqvBIHjFQNKiDKj-p;Y*MmMAXOK^8{gVhrBn?Un}%9(JqaGPiann?Ll$aX-{n1 z!AnTWyjwZ7y=hrziEYVZVX)-}D^!8a+Bc<5#*3h1xvWqS7I$WL>iwNNvp;P<;TX`| zOF6ZibFB4T(YJC~mj~?Ev*ln|9sgYVFTcLiEi{YE;!ZWj>X*aK9|va;HulW-D`RH9 zw=O#R&of(j+rwMS%oCi;+oFskQ}@q2q4x)O3k5e6yDx`kLvQs@M`+D)vGA+`X6%Dl9YOA?Qrurfg>XqT9E@^ zgWxOT&hX+yo>7=HCb!3BO$p54I3{j@qbN!+nu>Ti*O~vw`5RU!f_JXS+*x#-zFp@m zr}GGVhgT1=p-TFp#dtAVjM3QdpDoi{l*z?1s=d~(E;Fkn=*i8+oBcJ3Ib?Vh+rZWNZ$pO`dl8LcBv_cAA zc18lYB|rc<0u%wEdTGEup|%_S`L>@ui4LTkvnNApm#>+b4WIF<} z^J}=w7L&$J%unXCb|Wy{z3WVlMDNhz3o7S-3)6oqjx)7WX0HTEH{-=9>q+ zXXtoVPHKfVJMk8bt&h;MII}u~0l79^#`5CdW6Ef!eb|E&Q{UJ$n$yP;^Jd)qhw~ej zB?c~nN*%0zm%$}MD%|VZuS8W+Qtf zS+Uu?;oSPLL}G`jMH zn3`(J{6K%B(Gykos(!d}z)Wr!%sjC6=V@s)qG1MJN~uoVlq{jeI#XKPMI;@L^`RBZ z0Fhm zEI{|uQr0z1gk4W{mj*%4Z*00DBL5ko{4X}2{Dl0wAi#aSmq_r~FBHL|;}P&0k>OU! zhx64h5vSKwffV0W4JQs2dFBrfQx(B{AK=BGc`U!}S&BFnE6QSvw?`~m^}8j(4$IzQ z_WzjR?fD!VI8Aa=N;O96$fIWzW@IV2KtfOm4MwFVU~FM5pwL+-yY-+$4mvEEjvjP+5JUm8n(w zTE>U0(q9W!VAi2soP~_07HUw%Pt_tTYxD^79a6Fw-(PjP4xwLxv3Ycv!%RV}m`xvC zX`nx*(H@IF+EJ)392Ul)-t@Oj>L>VGb7%C~V}eWde6yYkCcYR2>L5_BFiz*D#3I_* zY)|v0XvW#xv=Y0=d;t!!=&NUW2H8t2>2H>>rUwQga=@Hd8s$Z+x+rNk0%K7J*cGvn za#2GFTwHgcx}(hY&AoeJJ>OtvvdouZfGLkWz?5@JX6KrhfDJ0`xz(qU+f2hY)2ykx zl5dMrs#`m^OO;aljpVNpXHI7j?NBazjFr-P<5NZ{lysyym6ILI!i}auR#r=s8-sHH zo|F}x&aDr!mLdRfA3dBON<#lrL!uSm7=o9syd*hDuX`F0HkX``(5Ixonj|KOyUg3^ zQc-Q1zi|oXoEJ7t`z@l)r8HbVnV=3@R147(4T%Z?MF>|u+vhb+dmd}f?PMV8SW8Om zNGeF;<~ukE61hiT7Fejt`7XmU^|R{ev+p#`i$*Qly)%e2TjDu=LV)p<*h6u5gyTBv zF2X}pxW+%;eRIVAvq#45Tg=WlQSFR|)0f>5G`p(9xM7}| zFKtPEbWZkN=1qLjD*3c&W=C5QZ78nOyIt7^bEIKqkTQs5B8y0Tx?-c7F3RU`pPOs` z_?hlA-(AYe*|k@#n%-mt4P66m+?M)nmWXqWP-^>As_PEzQPQQFQR8 z8-h3Q39C3Q91oVz2*#A-KL%2bY;8!cmJ9uHA`|C8 z$NX`>3!Xc-34zzMQ(s0p^HbkPL0@}t>MK)QkhQHnsYONA8Y3sjLq95yD8o_vXX;;L z>_rtUVz~Yrx{&>y!BX_$%=h%m(WLsmNbc^@hvIY`rx=`G3p{Y^ZC06YKwy@l-|)Hh zU=6u>PjJFvP!kJ(Tc+sbM_EIjrY|G=W}4NvvWB>k^nM4`K&TNt=8t0byviN1Lph6= zm_yLKL?eam;`vUGWXllNQpvgH+$3sPb_yL=Bg|EjmK*vv&mK-$JqW8%=|ASK>2#&P z_Hr|Y5Dkgu7#^X*C_?v-?p6bh!n7?WmSW!JeSwnSm}M7T5((zV1Sgd@d05#6N@`iq zIof-m%Wyrh&Os_zmvwFpf)UBIy{<8BeDtovo%NaL&_|tBV$bJ-C;E$apFPY)zG1$1 z&owMVml>CDJKAdL5zE6EYkt$pYmLfF?wDG0`I8N*#DQu4-A7E6KcN`U27=18Fz;s6 zgRIKZJ=&bE;>8osoUL9Ryh=TbC>SSDx$a_ae4Sb3Y{(ciQKVJ&x*C=an(TMl4xLH2 zXX$$5{C?<{&`X7#bw|C!?@WU>(wf=M60Egk4C)t`yyBd`(C=(qFld4VoFf6R4+pHN zK8Ll6cJ>?zJRuIOK|)?8A%{uGgm6egv3W?S%i_2=V{%GzdHk`#X)(c}lhxAXtow#+ zFHp)}cHUdTEBD@=-@HTIVx!PQ#~t7^T8*<#^hS~|xc9~6%di^At;m{`IHO;U1JyJ& z?$6LV#Y%45gWjnIu3a5-`VNydN5;meS;L)mKjUK-hMMbbbJA&Cbq9~|S=gw!q$wS} z>!$M`UNJWuIMmgl*gmkLk_ZS(?`c%lMZ(&XFK8NP#)0^vSl6vFEG>}Yt=qY z>WCarV-#iQR(@uObO3d9Zj~Ae<}6f(n;Hky?Oz`=r|lj-I0#^gmZN5;ee)19uN-uf zbLW7xnioz$Qqpv@afoy00q1WU|&pEgH8343To6masFPXZZ+i2fw zw(TOJh6NWV1zH#tgBTU7eP2E-U^0`E%lVvRweM3##v6R|Hc)r2ZWu6UP8uu_SKF^7 z5Ei+b&tX|(bW>KeN_C)b7q?VhC2@*pFT<#gaK20zQb%f_ppm8Xf&=AdHBgp?2g=0N zzUt06{THYVS>0fh!O|&%MP5GTWr9DpB_rmtxWJV%cw()yvDADh1(g)ek#K;gD6diD^_G>B>y~3*2ri=>?y@k#|fr6r^y=jEkKl3E7 z4M}aqf+KgXac<4$1&vT`xA250AV##H0=5ek@I!)vK3Iwme$0oDmHS)WNy*wIdYTYj zZRu7LFxIS58JMfP!&x-K4>+HK()5vW=nSz9Me#w3T`4{giqU44ixKrd!tunBaOeaO;`@Gg0VSi}FyYeUlc*jfuoTFFEd zOR8Z4RTBHrnM_v=qLS_KTIyGvYt1|?i!+C4y??`sV=b9MS0Ju6Q)C6T`W3;Z%o85d ziENh~l0#_RtCgzGELP8JHB9M!#^AHfT3W1T^h?P+q1$V+gEe9y%{FPzuSsRs@Ay-r z&&$%MWa*cg*GZ8R;SHL@d5gHczoSYe+a|;+l&uAZooROH4pP=g`GeNXPLfFzb`#S1 z2_-JE19Kg4B`^wb`OGw9drEbu!t~n%qeIJiU}$Ld55)5#)skz}?aZlPlQ8z#UJ#-| zYO^vmzd2P;V*j5ETWQQ}A;NIjCB|%xCEmF;jXrG6JdLv!xSAK@X@Sdl!B-26nk^;Q zowGGGn&>N2cRRN_tq77S`L(hZ^0u`V19Af$;OpSM*@-NJvG_@@hy5J^vd5CVZ8v5tF zwQ7lkRx1I6-#=R@`m)Md`q#Na+?08k)vz7fn~b?P7;2Kt8t}>IiMVUrKGxYujGZWb zLanz`MzcgG7IDuLahiX|7e$b)I}hh9p%{<(HOiH54&kp~Ytv~>ArTCn#S8~^$oQ)X zh^?`%yGTMs6NUtL_ntBL;MAmDP#8v#36b}%i_U$y`ln#i)B;*>S*Pvjco$ClL? z%=q~elnuXpj0WVh4c6?B5^b?x@W;C;BYJ#|yQV(-^BV8xS@qdyP_7}XGtF%KKWAjn zLectNCDB|O$s?N`pgU^fn(!runKLO{ZL*IDdN#goZ=z)9FDy|a4b+7tIf&rq{hz40 z&UP~#62@?Yv#|LPJJk&HQ3e)?F*x^tH_b5TT8Z=h%QKll3XntrekU{W1ucz%R_!vl zu6JTwtI@B2wku%k4*@aLHLf+aSdHs*_rgZ{Wh2W%`KXEPa`u}qU^8Nd`Gtzm`f-1-zBi0iySJ$H?3COIw5Sts}8 z<+Vm%m)h*yTBpLCW?Q^x1F!Vd+Cd-yYm=~2?%cW>C+BZ7&rJ{WkI2`jH+ zb9w~ZgNut( zRG;4bHiKMr_Jpiv$aIiF9yPwvac%awnv2~cp8C&!2=C}j(2#tMi zjAaHm5bPpSUwa%RYp-#*{ngfz;(tXArj2S*S=&8{L(57D#>Sy>ye}&aBu|6{WXYoR zJy=+9jhe&f&&Pd^I=}K3&D!?hXM~&KKNL|-rI@I}J}9IBm%CT4Pr(h2lA`RU!W}#z zTt1O71J@X3uEEEm16dpYC#BMwiUd{3p3PQWl4fnzvSl_Q9@M}hNeE;-!hE}nWGGc1 zPd%s4GDneKLvjGcS1HB`9XaviNE~IJ5)rQKQ@w;(FbQa{p*Dyv{NvkHXAi;5a-v(C z`r^gH3Wfzd%G^(xROzgOnu~kNc%v|Y{{$u`D4$wu6mDT|WDAsPz{x$PmVRmi?cZF+ z-U3yHJ4XL3ya%Jx{3B1Os@RU`W_KkhwTO`EP<`_mS~KR8U+7dTIE{Ja&Tt#Gon$nl zE(dWJp-%nLFGR6dIAy<_TXIXDnE(n>ay2-K8OIy5nAx_qmLyOgtQ6Fj%*-=qe@HKi z0nCq$syuW4!}7)5RiQ;?m+>J6id0FQbux>KbU4=#b?)3Fg%G{}A@pSk=NYO@J@Gx( z+{gD5$inzGt&2vIBM=9%&Ys$We)D#=;$X>?T(d~*H3&8|nSsg$L4-o()4BCDnT9d8 zE_0`&P_=OS)^ylwt2<5* zvwCk}v{^^0RD(Mo4Ce-R%T811{Z?J%>mVhkZSqsZUab`AH#ms$5NI#mLjx`}sob@d<%w|L( zocFxQ+iwIN$`Lbg(^wA>sk1CDaCHq1dn;88aoAtv)vqavty0V_rw}n1A$&%RTW^fp zY)}2T(vF=bG5SC~B*4=@Q8ksK&3H(1Umvsi=+-mqUO_!8b(bJ>RT_kck`^w4=oz2- zwmQq2dD6)hOs(rtPvK;BG z{Y=ms-NO?H{RWf<@R!l@1ap~PGv8k0k3-q__{PCC@7C5Fh^ikPxV*RPmYM_6 z0kfvSzBw?k$ERj&%~qlI8?ow$vto~Q!31rW=wT=8P}xDGS$oy?u<(xFOYiHeWgsP# zT)aFG=O0)ID^^KfcN36{h|5_lk9ol2Erhw1%VG`GJQ^J0PAl8jr?Yx*E!U4=K2it(Ud zQ6rhrtZtLI1dW*3;fTHQ-7(GY#w6b|7=sK8vsi6UF!k;QP1I`7T{{)D%r}j9f6JY_ z`axh=-H>^}`P?qy;er7j3=la1cXR(2P^}~G5U@)^Y9R^W~(Yf&ei6pNG>XS)n>Z@{y@SU?&+x_PP zwi4TIm{g4?h9h`GI^_uccL{tvDS( zC7i=<#ERSNqK5joFl%3Dof%|KBvEU5qQ@ea%d`kN0xVuIHgfZRyPgfKsk;4%Cssd! zRZy@kcG~O{Xfb=dB)TDUpTCpV$~J|+y5e-hioLf6Tpsho_n_hSP(E;qsV|s#j?^8BAB(5Hf@{N#z(eFM>tMXu;~1uk&K# zE;Rzpm%)M=;(^O${@GT2SY*Q}7pOi8US|%YNHQuI9Dx}gPKACg9BY2xSRbtn$9iuY9oSBsmKgV3c(wEn=%-nK zD|%o2NhvE{vveJc2sn-K3I^M)_Ob0-oNJyT-AUD_7&*4H{_58PGyIvmsB7>#GLE9O zM_%Yt+6~?L-bud7E~=~mV~m!R6?=_4{MCo0O}Rex{k}23X2mR8`5ssCbIoY$sMFI9 zV=R9en4=k(1bGJ`JxbOSr0X_SY1>&{IxnuM;$(R1rZhlZsNjrRzXB)?&li~var z?B}%klDLWDf^4)nO#Q>nX4L#{frSueKHj{6e&Bw?L>`d{`ZHFsWS3ZmQoc`R>p!Zt z)MWNo*@Q0+(@KUAHQ#)n2!1ZmKjktmg>5tXOlEwvo@l;@bE{CFH1qfBRZ%~VD0^FK zYxkW_5R7B$+uR~XI@m1DA|0`t2h;L9#E9HeM)1wN?ybHta2K0&yD%+>v34#tOPGE6 z`4T2CtnhJRUgKcr&fU(Poo6zxgN->hy>T#X%%RSme-YWd)|AY6vM0lNYNQ&yn% zUR-P#5K5nU)Yx-dWQHOQ5Jo1y$g%9Mk}!8IeeMr47nESfX>;2=StXRpPm!JqVOg!O zss1JtXWbeChf1w%MT>HGxYweE6iHzp10k|K23P|lvUm(HB!wrCOfHOAC+sN2t35LB zOh)u5B9syRTR=6tT`Fqj2nANt5guo2m zFRo1DZ{oTuaTy*M?|e>p@X=?|N4fNYq|h*m3`rtjb3S)K(tr~W*Ak!p*pjtM&|QE` z1g;w|3YQ_Trwmq5RfH^6ge+BrELDUoRfH^6gsiVr1gXj)W9({XO@BJWxitVf8QE40 zLOB2Ws z#?1K7`D%?yj@5<1AMJ1LLKc%*@PGU7yMNKNXMh&qIPd`w1JXJYmE39l%IX`-wm@a3j$7_kLoU_KWm1ZQ4y~+M(s#*}g5UJIHUI zPSYM7*7F_qSY1$D>MeBZW$%;b7krZdIkX zK=(%axhGU<{MY7`8>NNrvT{ksyGmSfD<~6()x~9nZqEk2sJu*h8hXL)rCx%Nv^H*R zh4Ps~G%44(vEA{?E4*bY)KyihDvK-hDHR(epUO-M>aj|vX=}79ZIxE8Rcc=TP0ZDN^GT57!tV(H)C zO3L#<8gjb@-_RT@i&pZ}wDlG1`8fyy(bwVN;ozTqYEO+#*R)Fkeo@gjd%u`iNB_71 z@dF1rU4t(gk}&k*OA?0-A2D*&=rQiGmyR1h;j+soUUB85$yZIeI_a8gr%szb28}9zb#_CO*6`47+OuE!lUR3AyZUP zMf}9 zGO)|^f>p#MMnvkDSGlWws z7zSx)=geOaF>~~y;wpDRRh4(m?WG&sg+^s@*&XgOl3FXppd!U(#d>i;Y4P1E`M9ML zo;e~F_7c;5yKx8K?hWNeWn@{WxaaF`g03mA(%q%ScX~-(s#EE$GD>xK`D*v7g3?mS zjFyrzUA3xwO@*4`6R%!XT6u+gwNbW8wW*rn1wDl-tI{itRXUaDzw*o|EzK?{E>m@v zdS5H`R@1wz+_9cwU0rLp)hM0cEx%T zdqSa%f;;<$zi_*RA{7?s1r%YR)#VY>Qce0w?_GwsN(v*Rd`W15p#xdT))X_L7cZUBTaR%G35qstwOO?!9I7T6x(TZ<$UVB&=$~^M);`yu*-yRjR=yteQ`& zS;TaiuobdCcdtZ}ge-4fHG(xQyLeS)c~$vp-JM&kYB^`pr0(`uU@dwqPg)%FVak*# z+AQ|&J1SYt$_iMKjj}t-%GZ@$PalSwFjLm(v2k&1q7rPTTO#x07|yMMVxr?D~p|brlu8 z_G7&NzyG75fN-+k}Y zzx?@qv+Z94r~mDP58FTb_m4Y1Idiu2)4zPy#pTGq`9O5x1J74F5dCM@|35qbzq$SY z+JW@K{^~&bpI!f~teI=p%&Zd9gjUFJvOAlfTV6Ks)3UR#E-bv77k-{>O-lzj6LXGJ zM`vwe`P%OHMVywzImcVUk<<#1Zrov1>6&(ZBmJ+sIZe9;i1gppryTXS_V$nL*F@;USBGfC;q?2K?~0NO$CrF(miG4V8~^$Z zz5OHem-q{7zuf=oExrBw_UHKT_4e3MojVc!>izt0p32|GQ&|!<&s*lL zgt#=vqLj_iD@!xiLc4)ag`Y0mhdDx04|5>O?0E&n`rPu$94I-ZUTbI6zNgJmypm8b zw#R?6K}3&8G^?PjuoMj96G=6@ywE81&V^XJ5Sk64-_kOLVn3%6QZdB99CllX;qZc@ z7kCTSdcWZQm!4Ftg!43Ql0B!?3odbKG&x8?(hCbA7K8uvi;85TR7l)8R(7W^M7e*=UzOp7hJJ^) z(nEEn>)w|f1UFHnFHL(gIt%)yVs2=UsdtN!af>R6N2;LxK6<|NfDkslh4af`eF+6m z)0!jQ!9K$7ITAO0jz`lHq%{_0X3P5tN(1MlxKNE5FdyxD`_j@X0$BW%S@IR)qI^x> zyE!eh_CDPVQi&xzl8mB*r zXq(Ugqj7T7_*7`$Qn*y{aBS?iP!3mTf-#?^-i5iIkYIy zvkydkGkwAIZ-|;(YE%_T+BX=hS9>d&X@8DhFekg9!fHo)VvMc3EtZyt8%Q%FL(vv# z)_jt-m-$7!IlWy7(ZP|O!=%4zS*IFa1D*?m7zHOeWzo6==yb4tsryrBtvuQggi z>ruM)a71ku8G41G%jkWeSExKKMrK~bDzG86%1Nf!ErdI}rlO$I+g;n--Y%5-n3OSM z9OV{N77Jr0UArlB$->M9oCgX^IV_dgmcUk!bT#ddR-D2`tF7dFDt#B-`T)nMV2ubY{4f4woL&rs$D}RvZs(Z@^aBP0$f0Qcfmk3O zaD<-XCf`y7@e`h0*iX`xxbj3Rhsr~yi?|I2E((F41EvhrZ{8zFFW^oFyUm zoY0eHTBV=QQ}SjxR_Uza=>}MEkw-%21CX*xJ)}G}fRwp5^xVQz{C$A<*8x%0>u9fK>QPF6ltGuoAKJcHblus#4r3Eeullm-+iBb z{ri6ZweT1652y2A@9DbW&#J5Yg1`S7ZE<0ygjK%_6UF~))L&|G!66XZ$uBqr-2Zjj zfSUY2J`{?Ef`>)h9gnkNt=zI<%h*uoJo%3Gvi%9`S^L8iUGkQ;sYX4YB7F0Xw|2NK z?=SqVMfO#GX`$z{Uom`oDEv;szw+3r$A)YF@|gM9%~oO&f4kG)v|Ysz-BF9*y7eu$ zcH3JeZ(SP^(t52udhAappr>84$%KX=g3d?)=o1`;TQ*b%AWlwPua^IJY^Ce ze?Lv_#ZU7T9HXA+5T3X26r5%}&tW{f{+y-_=ed{X2%h)y6kMT@=V+c8Jjd`n@h@qb zo99zJ$MSsURGP91=Hj`YZ;j^$9_{a?X?OEH!BYm?ah^e*2YDWXzWY^x;iK>2+=@jadL7(4y z#b1Zbp`VPADB?+6d4_+|PVRo+k#0QiPsT~)ucpF^-~N%s&+_Cfjr9Hxzk4$Nw)lss zmkZ@sGN!|sN4^W6LqL8q7E^(*12QhY4?GLJ27C+*reTtRg@9a?3CEd$=sSM?C)~1m4*&oF literal 0 HcmV?d00001 diff --git a/venv/lib/python3.8/site-packages/setuptools/cli-64.exe b/venv/lib/python3.8/site-packages/setuptools/cli-64.exe new file mode 100644 index 0000000000000000000000000000000000000000..675e6bf3743f3d3011c238657e7128ee9960ef7f GIT binary patch literal 74752 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 literal 0 HcmV?d00001 diff --git a/venv/lib/python3.8/site-packages/setuptools/cli.exe b/venv/lib/python3.8/site-packages/setuptools/cli.exe new file mode 100644 index 0000000000000000000000000000000000000000..b1487b7819e7286577a043c7726fbe0ca1543083 GIT binary patch literal 65536 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

@|$jDp4t7maBT@by!vG1&J_?=DS4W3Hu6w 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[c}iI#RErv3nvcIClH@!{vM)zJ_weD zu_-L8NU*GlC{d0L!!VW10^+~>qmNB~Y8H+F}!P8_d(PpvjzMJQmr z)FkX;2B~<|3JfJeWv@IXo~nTtp$}Gjie> zs8UDG*kid(%i5QCBp~MA;#I186PI-nZ&k7!k8BiLJSuR>h7ArSYHD~B0I z=T6L{zqglekt0JjG5z&|GWb4?+B5+{p^fgTufl_KesA{@I&g7rNq==^SGc5GcM%$N zDBG2)qExz*Z;jGN_-iD-y8i2BCq)p}2lKcspLg>w-;qwg(()HXrZa3jd!}spuwBVX zwmX!iwU?#7uoQnunw|OlU~+c z^L5Ak3zWhaA4B^FhMMboO0k*O2GL)lD9_<$5b>czbCvKcSt+u*gA*=%dH>Q-Bc11h zzO7jbXN)&5mBf=w2anK6P$YcJZQoWa2#E!v{hFKxxm7Fc)Fc9iC35{|Lp7bIDjrhC zgMiGf4r2yquH{U7WdMio;XS4Y%Ry{q7#kv#gZ07i`7eo#MMh_o68E*Fd_#nrri^4b zX+slbsv>+8pmck%oLDUL()8NRJ#Z z8DReF_eq2zsjEXGs)yS{k}ykS1B!ZrY0f6O65^lslJv3g&wfpDg-&EwF8wrc=hSwm zPlV&n%%yE_@onOwK?)`GNJ6MQ0drMuBYWCH5dkD)uErh@*k}#GcFl<-;;TN+5vb|b zctkCv;*zL7f)A;QuO%(81r0)&aUz4EQu;kA!k@7i8RZ)koMaWW`5cC6n@{w!!J$5d zx}l)4VP4xL=BKi&c^{n_Qi`q@G{vimblcVR53b#*X$FUOQFm!A8JKahNSiBdY+x3bJZfD8n{--FLUM4+Mx@{vM_ep zkk)U=K8R(rhU(X_faI*ZO}cn`5t*O}lx^j8|0rt-)o=Axn^DGcQTi!#7hxLTq?|HQ zB;T6(nrsCeYK0_o%)IO+CP{n#+|;w1ZmvD2c-J{i88bp63RjyKOE!B!D3U{RCs*Zh z&^%65VM(J34230U4bHS}M@SYS9TEK}c%)2<$h1|T;##zRtjRt@#1T%J=kAhOiw+Z% z7DpyWVK@6%9K^uVD9LDKj)dR^aZK6$@Lt)l;sj@`QSzBm{TlLG{JKM_^60Zr2w~nr zr>P-BaV8OjjWm?hQ3$ZCx+lyD%q`~4iNF9xWKi$t&pzBhwN9Dq-o^v9@=abLR#|

KZqkLal4YCRR9VNhIM|rBqmzzcImvcx z66fD`zj4}M-A;gyA17cSC-oI$`q?*q&8~)Qv|C#(aSFd|hYbf}FFVB?n3Q?Svt+Td z#AW4x=9X}?aizE|`r{}3l-H&b6-{_j#STR!lD001vu;K>KT;*^ChCevBwCMFpg{JI zv``4YsjK1&142Pl%%A#u3rbGso1<_fngd1`+}!pMu@z5Me_5UFxiPYKqFL4_`WXmY zeWJrZUKzrrMuBcHupOq4Wr12sE*T-*CXh;FA=)Q+BMN(?DJ!kq?%Ww`xlG3e;lz2t zY?tl;i?gHO_79VwJ_cThq^>FqRUPlqS?IuI+CfSbNkv_1l~7eGaCwRmuOF|ic1ac2 z9ldo$TN~LhX~J01P75nyi&d8=Y@QNZ5e<=6v_R3rM}nN}5ae`^LV&sAD<=;*z=!~` zvJ0@i!orMuT*5kyXNzJnxfU!+#FTW(syy@yj7XX8#zD_9TWBSg(;KZ25VO;is;-&R zf(29n3U}agkC`j4sjX{=`D1EkCC@enOA~v{GOLYQKAdPN6+?W+QE4fLMhrW4RGbH5^K(rm4T}`=ra<6GP2}cRBE9K8^r(O+ZvKpJDL~qNguPmwQZp-8m7V@ zN^KFU8@Q*E7UJswZD=OYtct4KqA&NDKSOfc-#M>@o#)4;YLqtENdFS^3K9&dFBr|M z*loqE3X2sMmi8hv#7H5rqGc_y=ShEbHT^m7S`?4d%B+(-6dYGI-*t5E+< z^P3gqvBIHjFQNKiDKj-p;Y*MmMAXOK^8{gVhrBn?Un}%9(JqaGPiann?Ll$aX-{n1 z!AnTWyjwZ7y=hrziEYVZVX)-}D^!8a+Bc<5#*3h1xvWqS7I$WL>iwNNvp;P<;TX`| zOF6ZibFB4T(YJC~mj~?Ev*ln|9sgYVFTcLiEi{YE;!ZWj>X*aK9|va;HulW-D`RH9 zw=O#R&of(j+rwMS%oCi;+oFskQ}@q2q4x)O3k5e6yDx`kLvQs@M`+D)vGA+`X6%Dl9YOA?Qrurfg>XqT9E@^ zgWxOT&hX+yo>7=HCb!3BO$p54I3{j@qbN!+nu>Ti*O~vw`5RU!f_JXS+*x#-zFp@m zr}GGVhgT1=p-TFp#dtAVjM3QdpDoi{l*z?1s=d~(E;Fkn=*i8+oBcJ3Ib?Vh+rZWNZ$pO`dl8LcBv_cAA zc18lYB|rc<0u%wEdTGEup|%_S`L>@ui4LTkvnNApm#>+b4WIF<} z^J}=w7L&$J%unXCb|Wy{z3WVlMDNhz3o7S-3)6oqjx)7WX0HTEH{-=9>q+ zXXtoVPHKfVJMk8bt&h;MII}u~0l79^#`5CdW6Ef!eb|E&Q{UJ$n$yP;^Jd)qhw~ej zB?c~nN*%0zm%$}MD%|VZuS8W+Qtf zS+Uu?;oSPLL}G`jMH zn3`(J{6K%B(Gykos(!d}z)Wr!%sjC6=V@s)qG1MJN~uoVlq{jeI#XKPMI;@L^`RBZ z0Fhm zEI{|uQr0z1gk4W{mj*%4Z*00DBL5ko{4X}2{Dl0wAi#aSmq_r~FBHL|;}P&0k>OU! zhx64h5vSKwffV0W4JQs2dFBrfQx(B{AK=BGc`U!}S&BFnE6QSvw?`~m^}8j(4$IzQ z_WzjR?fD!VI8Aa=N;O96$fIWzW@IV2KtfOm4MwFVU~FM5pwL+-yY-+$4mvEEjvjP+5JUm8n(w zTE>U0(q9W!VAi2soP~_07HUw%Pt_tTYxD^79a6Fw-(PjP4xwLxv3Ycv!%RV}m`xvC zX`nx*(H@IF+EJ)392Ul)-t@Oj>L>VGb7%C~V}eWde6yYkCcYR2>L5_BFiz*D#3I_* zY)|v0XvW#xv=Y0=d;t!!=&NUW2H8t2>2H>>rUwQga=@Hd8s$Z+x+rNk0%K7J*cGvn za#2GFTwHgcx}(hY&AoeJJ>OtvvdouZfGLkWz?5@JX6KrhfDJ0`xz(qU+f2hY)2ykx zl5dMrs#`m^OO;aljpVNpXHI7j?NBazjFr-P<5NZ{lysyym6ILI!i}auR#r=s8-sHH zo|F}x&aDr!mLdRfA3dBON<#lrL!uSm7=o9syd*hDuX`F0HkX``(5Ixonj|KOyUg3^ zQc-Q1zi|oXoEJ7t`z@l)r8HbVnV=3@R147(4T%Z?MF>|u+vhb+dmd}f?PMV8SW8Om zNGeF;<~ukE61hiT7Fejt`7XmU^|R{ev+p#`i$*Qly)%e2TjDu=LV)p<*h6u5gyTBv zF2X}pxW+%;eRIVAvq#45Tg=WlQSFR|)0f>5G`p(9xM7}| zFKtPEbWZkN=1qLjD*3c&W=C5QZ78nOyIt7^bEIKqkTQs5B8y0Tx?-c7F3RU`pPOs` z_?hlA-(AYe*|k@#n%-mt4P66m+?M)nmWXqWP-^>As_PEzQPQQFQR8 z8-h3Q39C3Q91oVz2*#A-KL%2bY;8!cmJ9uHA`|C8 z$NX`>3!Xc-34zzMQ(s0p^HbkPL0@}t>MK)QkhQHnsYONA8Y3sjLq95yD8o_vXX;;L z>_rtUVz~Yrx{&>y!BX_$%=h%m(WLsmNbc^@hvIY`rx=`G3p{Y^ZC06YKwy@l-|)Hh zU=6u>PjJFvP!kJ(Tc+sbM_EIjrY|G=W}4NvvWB>k^nM4`K&TNt=8t0byviN1Lph6= zm_yLKL?eam;`vUGWXllNQpvgH+$3sPb_yL=Bg|EjmK*vv&mK-$JqW8%=|ASK>2#&P z_Hr|Y5Dkgu7#^X*C_?v-?p6bh!n7?WmSW!JeSwnSm}M7T5((zV1Sgd@d05#6N@`iq zIof-m%Wyrh&Os_zmvwFpf)UBIy{<8BeDtovo%NaL&_|tBV$bJ-C;E$apFPY)zG1$1 z&owMVml>CDJKAdL5zE6EYkt$pYmLfF?wDG0`I8N*#DQu4-A7E6KcN`U27=18Fz;s6 zgRIKZJ=&bE;>8osoUL9Ryh=TbC>SSDx$a_ae4Sb3Y{(ciQKVJ&x*C=an(TMl4xLH2 zXX$$5{C?<{&`X7#bw|C!?@WU>(wf=M60Egk4C)t`yyBd`(C=(qFld4VoFf6R4+pHN zK8Ll6cJ>?zJRuIOK|)?8A%{uGgm6egv3W?S%i_2=V{%GzdHk`#X)(c}lhxAXtow#+ zFHp)}cHUdTEBD@=-@HTIVx!PQ#~t7^T8*<#^hS~|xc9~6%di^At;m{`IHO;U1JyJ& z?$6LV#Y%45gWjnIu3a5-`VNydN5;meS;L)mKjUK-hMMbbbJA&Cbq9~|S=gw!q$wS} z>!$M`UNJWuIMmgl*gmkLk_ZS(?`c%lMZ(&XFK8NP#)0^vSl6vFEG>}Yt=qY z>WCarV-#iQR(@uObO3d9Zj~Ae<}6f(n;Hky?Oz`=r|lj-I0#^gmZN5;ee)19uN-uf zbLW7xnioz$Qqpv@afoy00q1WU|&pEgH8343To6masFPXZZ+i2fw zw(TOJh6NWV1zH#tgBTU7eP2E-U^0`E%lVvRweM3##v6R|Hc)r2ZWu6UP8uu_SKF^7 z5Ei+b&tX|(bW>KeN_C)b7q?VhC2@*pFT<#gaK20zQb%f_ppm8Xf&=AdHBgp?2g=0N zzUt06{THYVS>0fh!O|&%MP5GTWr9DpB_rmtxWJV%cw()yvDADh1(g)ek#K;gD6diD^_G>B>y~3*2ri=>?y@k#|fr6r^y=jEkKl3E7 z4M}aqf+KgXac<4$1&vT`xA250AV##H0=5ek@I!)vK3Iwme$0oDmHS)WNy*wIdYTYj zZRu7LFxIS58JMfP!&x-K4>+HK()5vW=nSz9Me#w3T`4{giqU44ixKrd!tunBaOeaO;`@Gg0VSi}FyYeUlc*jfuoTFFEd zOR8Z4RTBHrnM_v=qLS_KTIyGvYt1|?i!+C4y??`sV=b9MS0Ju6Q)C6T`W3;Z%o85d ziENh~l0#_RtCgzGELP8JHB9M!#^AHfT3W1T^h?P+q1$V+gEe9y%{FPzuSsRs@Ay-r z&&$%MWa*cg*GZ8R;SHL@d5gHczoSYe+a|;+l&uAZooROH4pP=g`GeNXPLfFzb`#S1 z2_-JE19Kg4B`^wb`OGw9drEbu!t~n%qeIJiU}$Ld55)5#)skz}?aZlPlQ8z#UJ#-| zYO^vmzd2P;V*j5ETWQQ}A;NIjCB|%xCEmF;jXrG6JdLv!xSAK@X@Sdl!B-26nk^;Q zowGGGn&>N2cRRN_tq77S`L(hZ^0u`V19Af$;OpSM*@-NJvG_@@hy5J^vd5CVZ8v5tF zwQ7lkRx1I6-#=R@`m)Md`q#Na+?08k)vz7fn~b?P7;2Kt8t}>IiMVUrKGxYujGZWb zLanz`MzcgG7IDuLahiX|7e$b)I}hh9p%{<(HOiH54&kp~Ytv~>ArTCn#S8~^$oQ)X zh^?`%yGTMs6NUtL_ntBL;MAmDP#8v#36b}%i_U$y`ln#i)B;*>S*Pvjco$ClL? z%=q~elnuXpj0WVh4c6?B5^b?x@W;C;BYJ#|yQV(-^BV8xS@qdyP_7}XGtF%KKWAjn zLectNCDB|O$s?N`pgU^fn(!runKLO{ZL*IDdN#goZ=z)9FDy|a4b+7tIf&rq{hz40 z&UP~#62@?Yv#|LPJJk&HQ3e)?F*x^tH_b5TT8Z=h%QKll3XntrekU{W1ucz%R_!vl zu6JTwtI@B2wku%k4*@aLHLf+aSdHs*_rgZ{Wh2W%`KXEPa`u}qU^8Nd`Gtzm`f-1-zBi0iySJ$H?3COIw5Sts}8 z<+Vm%m)h*yTBpLCW?Q^x1F!Vd+Cd-yYm=~2?%cW>C+BZ7&rJ{WkI2`jH+ zb9w~ZgNut( zRG;4bHiKMr_Jpiv$aIiF9yPwvac%awnv2~cp8C&!2=C}j(2#tMi zjAaHm5bPpSUwa%RYp-#*{ngfz;(tXArj2S*S=&8{L(57D#>Sy>ye}&aBu|6{WXYoR zJy=+9jhe&f&&Pd^I=}K3&D!?hXM~&KKNL|-rI@I}J}9IBm%CT4Pr(h2lA`RU!W}#z zTt1O71J@X3uEEEm16dpYC#BMwiUd{3p3PQWl4fnzvSl_Q9@M}hNeE;-!hE}nWGGc1 zPd%s4GDneKLvjGcS1HB`9XaviNE~IJ5)rQKQ@w;(FbQa{p*Dyv{NvkHXAi;5a-v(C z`r^gH3Wfzd%G^(xROzgOnu~kNc%v|Y{{$u`D4$wu6mDT|WDAsPz{x$PmVRmi?cZF+ z-U3yHJ4XL3ya%Jx{3B1Os@RU`W_KkhwTO`EP<`_mS~KR8U+7dTIE{Ja&Tt#Gon$nl zE(dWJp-%nLFGR6dIAy<_TXIXDnE(n>ay2-K8OIy5nAx_qmLyOgtQ6Fj%*-=qe@HKi z0nCq$syuW4!}7)5RiQ;?m+>J6id0FQbux>KbU4=#b?)3Fg%G{}A@pSk=NYO@J@Gx( z+{gD5$inzGt&2vIBM=9%&Ys$We)D#=;$X>?T(d~*H3&8|nSsg$L4-o()4BCDnT9d8 zE_0`&P_=OS)^ylwt2<5* zvwCk}v{^^0RD(Mo4Ce-R%T811{Z?J%>mVhkZSqsZUab`AH#ms$5NI#mLjx`}sob@d<%w|L( zocFxQ+iwIN$`Lbg(^wA>sk1CDaCHq1dn;88aoAtv)vqavty0V_rw}n1A$&%RTW^fp zY)}2T(vF=bG5SC~B*4=@Q8ksK&3H(1Umvsi=+-mqUO_!8b(bJ>RT_kck`^w4=oz2- zwmQq2dD6)hOs(rtPvK;BG z{Y=ms-NO?H{RWf<@R!l@1ap~PGv8k0k3-q__{PCC@7C5Fh^ikPxV*RPmYM_6 z0kfvSzBw?k$ERj&%~qlI8?ow$vto~Q!31rW=wT=8P}xDGS$oy?u<(xFOYiHeWgsP# zT)aFG=O0)ID^^KfcN36{h|5_lk9ol2Erhw1%VG`GJQ^J0PAl8jr?Yx*E!U4=K2it(Ud zQ6rhrtZtLI1dW*3;fTHQ-7(GY#w6b|7=sK8vsi6UF!k;QP1I`7T{{)D%r}j9f6JY_ z`axh=-H>^}`P?qy;er7j3=la1cXR(2P^}~G5U@)^Y9R^W~(Yf&ei6pNG>XS)n>Z@{y@SU?&+x_PP zwi4TIm{g4?h9h`GI^_uccL{tvDS( zC7i=<#ERSNqK5joFl%3Dof%|KBvEU5qQ@ea%d`kN0xVuIHgfZRyPgfKsk;4%Cssd! zRZy@kcG~O{Xfb=dB)TDUpTCpV$~J|+y5e-hioLf6Tpsho_n_hSP(E;qsV|s#j?^8BAB(5Hf@{N#z(eFM>tMXu;~1uk&K# zE;Rzpm%)M=;(^O${@GT2SY*Q}7pOi8US|%YNHQuI9Dx}gPKACg9BY2xSRbtn$9iuY9oSBsmKgV3c(wEn=%-nK zD|%o2NhvE{vveJc2sn-K3I^M)_Ob0-oNJyT-AUD_7&*4H{_58PGyIvmsB7>#GLE9O zM_%Yt+6~?L-bud7E~=~mV~m!R6?=_4{MCo0O}Rex{k}23X2mR8`5ssCbIoY$sMFI9 zV=R9en4=k(1bGJ`JxbOSr0X_SY1>&{IxnuM;$(R1rZhlZsNjrRzXB)?&li~var z?B}%klDLWDf^4)nO#Q>nX4L#{frSueKHj{6e&Bw?L>`d{`ZHFsWS3ZmQoc`R>p!Zt z)MWNo*@Q0+(@KUAHQ#)n2!1ZmKjktmg>5tXOlEwvo@l;@bE{CFH1qfBRZ%~VD0^FK zYxkW_5R7B$+uR~XI@m1DA|0`t2h;L9#E9HeM)1wN?ybHta2K0&yD%+>v34#tOPGE6 z`4T2CtnhJRUgKcr&fU(Poo6zxgN->hy>T#X%%RSme-YWd)|AY6vM0lNYNQ&yn% zUR-P#5K5nU)Yx-dWQHOQ5Jo1y$g%9Mk}!8IeeMr47nESfX>;2=StXRpPm!JqVOg!O zss1JtXWbeChf1w%MT>HGxYweE6iHzp10k|K23P|lvUm(HB!wrCOfHOAC+sN2t35LB zOh)u5B9syRTR=6tT`Fqj2nANt5guo2m zFRo1DZ{oTuaTy*M?|e>p@X=?|N4fNYq|h*m3`rtjb3S)K(tr~W*Ak!p*pjtM&|QE` z1g;w|3YQ_Trwmq5RfH^6ge+BrELDUoRfH^6gsiVr1gXj)W9({XO@BJWxitVf8QE40 zLOB2Ws z#?1K7`D%?yj@5<1AMJ1LLKc%*@PGU7yMNKNXMh&qIPd`w1JXJYmE39l%IX`-wm@a3j$7_kLoU_KWm1ZQ4y~+M(s#*}g5UJIHUI zPSYM7*7F_qSY1$D>MeBZW$%;b7krZdIkX zK=(%axhGU<{MY7`8>NNrvT{ksyGmSfD<~6()x~9nZqEk2sJu*h8hXL)rCx%Nv^H*R zh4Ps~G%44(vEA{?E4*bY)KyihDvK-hDHR(epUO-M>aj|vX=}79ZIxE8Rcc=TP0ZDN^GT57!tV(H)C zO3L#<8gjb@-_RT@i&pZ}wDlG1`8fyy(bwVN;ozTqYEO+#*R)Fkeo@gjd%u`iNB_71 z@dF1rU4t(gk}&k*OA?0-A2D*&=rQiGmyR1h;j+soUUB85$yZIeI_a8gr%szb28}9zb#_CO*6`47+OuE!lUR3AyZUP zMf}9 zGO)|^f>p#MMnvkDSGlWws z7zSx)=geOaF>~~y;wpDRRh4(m?WG&sg+^s@*&XgOl3FXppd!U(#d>i;Y4P1E`M9ML zo;e~F_7c;5yKx8K?hWNeWn@{WxaaF`g03mA(%q%ScX~-(s#EE$GD>xK`D*v7g3?mS zjFyrzUA3xwO@*4`6R%!XT6u+gwNbW8wW*rn1wDl-tI{itRXUaDzw*o|EzK?{E>m@v zdS5H`R@1wz+_9cwU0rLp)hM0cEx%T zdqSa%f;;<$zi_*RA{7?s1r%YR)#VY>Qce0w?_GwsN(v*Rd`W15p#xdT))X_L7cZUBTaR%G35qstwOO?!9I7T6x(TZ<$UVB&=$~^M);`yu*-yRjR=yteQ`& zS;TaiuobdCcdtZ}ge-4fHG(xQyLeS)c~$vp-JM&kYB^`pr0(`uU@dwqPg)%FVak*# z+AQ|&J1SYt$_iMKjj}t-%GZ@$PalSwFjLm(v2k&1q7rPTTO#x07|yMMVxr?D~p|brlu8 z_G7&NzyG75fN-+k}Y zzx?@qv+Z94r~mDP58FTb_m4Y1Idiu2)4zPy#pTGq`9O5x1J74F5dCM@|35qbzq$SY z+JW@K{^~&bpI!f~teI=p%&Zd9gjUFJvOAlfTV6Ks)3UR#E-bv77k-{>O-lzj6LXGJ zM`vwe`P%OHMVywzImcVUk<<#1Zrov1>6&(ZBmJ+sIZe9;i1gppryTXS_V$nL*F@;USBGfC;q?2K?~0NO$CrF(miG4V8~^$Z zz5OHem-q{7zuf=oExrBw_UHKT_4e3MojVc!>izt0p32|GQ&|!<&s*lL zgt#=vqLj_iD@!xiLc4)ag`Y0mhdDx04|5>O?0E&n`rPu$94I-ZUTbI6zNgJmypm8b zw#R?6K}3&8G^?PjuoMj96G=6@ywE81&V^XJ5Sk64-_kOLVn3%6QZdB99CllX;qZc@ z7kCTSdcWZQm!4Ftg!43Ql0B!?3odbKG&x8?(hCbA7K8uvi;85TR7l)8R(7W^M7e*=UzOp7hJJ^) z(nEEn>)w|f1UFHnFHL(gIt%)yVs2=UsdtN!af>R6N2;LxK6<|NfDkslh4af`eF+6m z)0!jQ!9K$7ITAO0jz`lHq%{_0X3P5tN(1MlxKNE5FdyxD`_j@X0$BW%S@IR)qI^x> zyE!eh_CDPVQi&xzl8mB*r zXq(Ugqj7T7_*7`$Qn*y{aBS?iP!3mTf-#?^-i5iIkYIy zvkydkGkwAIZ-|;(YE%_T+BX=hS9>d&X@8DhFekg9!fHo)VvMc3EtZyt8%Q%FL(vv# z)_jt-m-$7!IlWy7(ZP|O!=%4zS*IFa1D*?m7zHOeWzo6==yb4tsryrBtvuQggi z>ruM)a71ku8G41G%jkWeSExKKMrK~bDzG86%1Nf!ErdI}rlO$I+g;n--Y%5-n3OSM z9OV{N77Jr0UArlB$->M9oCgX^IV_dgmcUk!bT#ddR-D2`tF7dFDt#B-`T)nMV2ubY{4f4woL&rs$D}RvZs(Z@^aBP0$f0Qcfmk3O zaD<-XCf`y7@e`h0*iX`xxbj3Rhsr~yi?|I2E((F41EvhrZ{8zFFW^oFyUm zoY0eHTBV=QQ}SjxR_Uza=>}MEkw-%21CX*xJ)}G}fRwp5^xVQz{C$A<*8x%0>u9fK>QPF6ltGuoAKJcHblus#4r3Eeullm-+iBb z{ri6ZweT1652y2A@9DbW&#J5Yg1`S7ZE<0ygjK%_6UF~))L&|G!66XZ$uBqr-2Zjj zfSUY2J`{?Ef`>)h9gnkNt=zI<%h*uoJo%3Gvi%9`S^L8iUGkQ;sYX4YB7F0Xw|2NK z?=SqVMfO#GX`$z{Uom`oDEv;szw+3r$A)YF@|gM9%~oO&f4kG)v|Ysz-BF9*y7eu$ zcH3JeZ(SP^(t52udhAappr>84$%KX=g3d?)=o1`;TQ*b%AWlwPua^IJY^Ce ze?Lv_#ZU7T9HXA+5T3X26r5%}&tW{f{+y-_=ed{X2%h)y6kMT@=V+c8Jjd`n@h@qb zo99zJ$MSsURGP91=Hj`YZ;j^$9_{a?X?OEH!BYm?ah^e*2YDWXzWY^x;iK>2+=@jadL7(4y z#b1Zbp`VPADB?+6d4_+|PVRo+k#0QiPsT~)ucpF^-~N%s&+_Cfjr9Hxzk4$Nw)lss zmkZ@sGN!|sN4^W6LqL8q7E^(*12QhY4?GLJ27C+*reTtRg@9a?3CEd$=sSM?C)~1m4*&oF literal 0 HcmV?d00001 diff --git a/venv/lib/python3.8/site-packages/setuptools/command/__init__.py b/venv/lib/python3.8/site-packages/setuptools/command/__init__.py new file mode 100644 index 0000000..743f558 --- /dev/null +++ b/venv/lib/python3.8/site-packages/setuptools/command/__init__.py @@ -0,0 +1,17 @@ +__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', + 'bdist_wininst', 'upload_docs', '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/venv/lib/python3.8/site-packages/setuptools/command/__pycache__/__init__.cpython-38.pyc b/venv/lib/python3.8/site-packages/setuptools/command/__pycache__/__init__.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..0cc004d7a2bf13758f81a51595f616f6641a80b8 GIT binary patch literal 764 zcmZuu%Z}496wRY)($}=iP)b2avtZLjn=*o`NF&4-AS7UuSy*YV+X>^umhGgppTJkZ zl3((c6~BO3aMK3`2}ij(_c-T1KXf`x3(wOhpJDD;*7wT!`}EOyjjt-vfCV-<;6e>N zsDlpyG@uDBXhR3ipbI^?2j_4B_gDSMf<8Q8cIHHmUEu+9BNx99SuOJ-54jzAnkSa~+YITHCWC)9t0imC?3HDHI zj1RP=j0@AIvD%O{S2||Q1gJ%4$&G)cS_eQ_x-fOgb*yO=sJNtDXk|QA<&5fzYV=J_ zQ>9JssQDAFdt_81(@NLb*Sb#g>LzLxg7Fv#@I;yBzRUzOMvYxdRY^Z^!q&((-cI>u zWXmoRk&8J-j>P3y+uqxazUBGl1i8y)%IK(Jd_u4@LZU$xsn97?lZ0nkoWo=nNnW7T zxN4)KbL^rJn)A~?`y|~ZmszaIL8zj$e+g5Mg`hcnv*ZAy?aWUV3YnHTuCR<{xa16| zL@o(uYf(PG%3oe-fZ0OLr^{@cy_oLKDi7a>A{FBz7jbe&VZ6Sjl#Lhj>9b^3Zo^ZF v;h~z4;L1`>$OIP~TxT2#MqgC}QFAoT>9PNdkL<6S-?js%?KJJC-FAKhu3O}j literal 0 HcmV?d00001 diff --git a/venv/lib/python3.8/site-packages/setuptools/command/__pycache__/alias.cpython-38.pyc b/venv/lib/python3.8/site-packages/setuptools/command/__pycache__/alias.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..73549de0acb879b3bccc6de3b1b035e87b7fa044 GIT binary patch literal 2444 zcmZ`*&vP3!6qdB>^(J=OP(t&gw6oAQ)PUnqLZ{3CQ)nq%2rwy^oiaP?mF#S?KO9L; zlO2ZxE*DPx3GK0W{t(W4<yI?l7NrOYMRtu!38tg<0^tb-`e zdU3yOXH5rjHWrUI~ zvS*LT6Z#Y^eY$JOjq2SNjKN7(Z2UOPweZ5s3uS+picEXGTzW~Uh4coYRB_h#x`&?H zPQ<~Q8YHp4(Q4Vp^xb3n-Z6c6Os}?BX-hGXWzD>Xp#^)DG6^hA9n2Q zhqBYl6E39c45H4hM}z&1o7tx~HRrdw>h^jsEz(ccP3dQy2c1DYXb-bN80`w)ezYw_ z((c|~|0ue(Uv!kv!-3B8M0KJ(O~Z_L!XysW+TgIPtL;5Jmx~BBp&`trEnDbjpjlvEZ=RoA;Q%mA$xd=VT#jO3ee$TOTmew}h^1qK>eGa^q-I*drbI4~x# zII@Orwrp90Ipu{oi**cJ79yyqt4bS!Uwz;iEo~)|o?$`N%)=`A7Bp9&DhC=uXQ@Mr zr7Cu>@Av;+z7LCEdO0FHOs;ARp7xk>Sf;0fMJ0$%3c%~D=1#w_Y4rIU3I8;as6M=z_B40|14(qk*|& z2p>Exe2!2g7LFKqEqQL?v+@aP(@0^n-9O#XrWhxCZS(~(_Hj;y^uaRrFW zFR!jQt1a$~*hLT>RLm`sUBexCeVH6F?mnq&SI-zrjg#NlG8r@Q)6%nGYj6rXTQQB% z@8;O5c4*CPmURP!P{sMXRf2n8B6-o!K3j@&MhzhKC;VOpND6N=uZEtOq<|IjFRZyCBqgR-W$*Jt1_!<4BuGmNk&EOqWd%$TZY}@lIZXEvk^) zJ9(T*Od_K&5@S@lsNP7Tvd-mUAcq;`&Jxaf8=DI_r#?yeR|ryCF#7|TpjM$Fw8Ro3sNf_!RK@$|PziaO^yj-E#3Ia23?|1VGF+nbQztV? zU}ON#j#XirxNr!Y+7^4knEj7!H=U4u`A8FW$*)Ki`EVSsnuGI_|3*pW428t%d| z1qxr`V97o-7N9>^*0~Lq6=SIx%U9e1i_7asT_8KqeUWf?k&LWGR1Bb?(@*klm=yEv zcGYWFm$Fydc>dyCyN%CNYc0v6Feynf*KVIg{&OJOW$?5`B??~4E^UO~5TJBaK9rG= z*Wt|+N(P{Jz;++1LX;r>@N-To+-DNsZ?+ocJaX6k!&npe=Rz6H@h`o+A=ktK+~?Vv ziVxQCa!`h*{_@m}Nxav<1WInmYXWV2t_Y28n2%05dK%8s{j4I`7noE=)0(Ww7A$vm Ge&b(>`d(fD literal 0 HcmV?d00001 diff --git a/venv/lib/python3.8/site-packages/setuptools/command/__pycache__/bdist_egg.cpython-38.pyc b/venv/lib/python3.8/site-packages/setuptools/command/__pycache__/bdist_egg.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..a22c410972dab61d8a0fe7b5faaf24d116c72507 GIT binary patch literal 14399 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 literal 0 HcmV?d00001 diff --git a/venv/lib/python3.8/site-packages/setuptools/command/__pycache__/bdist_rpm.cpython-38.pyc b/venv/lib/python3.8/site-packages/setuptools/command/__pycache__/bdist_rpm.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..53182632c45e7dc76716268752c0dfc9b4595486 GIT binary patch literal 1836 zcma)6OK;pZ5GEpxf@^OtmOFZls|AO(sJrCmSj7AOS{DRMaT@tYZbx4zy*&_4O-NA^n# zp`Xpq>1M;vedy{22!X~6cf+F=u5;1vz{YnaciGEvpmA^L)5chgEra?jXd25 z{M?7GC;p z4sxOCu^t`qy-j z1_`6!M@4Bu1K7x9nsF%(X&8tw7lG!iU)$R3)6c{~aHyyRlh>m}=|G6@n zcNex?{zP+`fX1O42T3sD47}Hd;AU{wU?jK!rf8JV2j6^U*0hX9xdtSvad`{i^GpOG zTt85I^dQoEG*1}74O74hCk-`A_(>j*V2h4{lM&b8-xyx{4AVFZbTo=Yqz@bWAuxU1 z%-n+hIdpXeM2Y5*1xFYojrYmiDv_Bp1o0xfRcA?R&#qR15g+WsMOkI1GMY#e|H^{( z9b*+-8u4gRO&XdV4eZbW=07HBxtnSM`(siJclf1x zpci;d>KyNs))(j*=#3aAo)I;2N^Bt_)h-Ik)tGs-$o>>T|`l zW~6R%3f_6|kYH`uR7sCiq(7`|0j#|uo@2(%TZ{2_oquu#sH(c#0;UxYp)Fw^Guw?s^-8{qcYjv!FIRf v)N4K4yJ|Ru2UDp2qU!s{sH*m2sa9_2Jh5EQ>Qv}#H}G~$%#^9Bx2?Yc?ppo| literal 0 HcmV?d00001 diff --git a/venv/lib/python3.8/site-packages/setuptools/command/__pycache__/bdist_wininst.cpython-38.pyc b/venv/lib/python3.8/site-packages/setuptools/command/__pycache__/bdist_wininst.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..7f9c5ec9232cc8c234f5c4e1df52af292a4da449 GIT binary patch literal 1237 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 Q*Kj2u>A`f->gk{T9}_&hPXGV_ literal 0 HcmV?d00001 diff --git a/venv/lib/python3.8/site-packages/setuptools/command/__pycache__/build_ext.cpython-38.pyc b/venv/lib/python3.8/site-packages/setuptools/command/__pycache__/build_ext.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..9d1c201053537bbb83e38c1a75b3643b90dc3883 GIT binary patch literal 9950 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 LR(>BJw3q(}k;S>v literal 0 HcmV?d00001 diff --git a/venv/lib/python3.8/site-packages/setuptools/command/__pycache__/develop.cpython-38.pyc b/venv/lib/python3.8/site-packages/setuptools/command/__pycache__/develop.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..dfb8ca95284512e2c7efc02afc709a90b3827d62 GIT binary patch literal 6553 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 Izc3#B6}~deRsaA1 literal 0 HcmV?d00001 diff --git a/venv/lib/python3.8/site-packages/setuptools/command/__pycache__/dist_info.cpython-38.pyc b/venv/lib/python3.8/site-packages/setuptools/command/__pycache__/dist_info.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..60cb4cf01d03f0197ce0afc95e9cad252efa2676 GIT binary patch literal 1413 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 H$mahAtgt_h literal 0 HcmV?d00001 diff --git a/venv/lib/python3.8/site-packages/setuptools/command/__pycache__/egg_info.cpython-38.pyc b/venv/lib/python3.8/site-packages/setuptools/command/__pycache__/egg_info.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..a65fffcfdf96702c20e7dfafd116f968bd9a4bf4 GIT binary patch literal 21770 zcmcJ1d5|2}d0$^M(=$7JU~z*aI0Uc+2E;BPh@>cpq$mLd4Vhfh07ytfQnRzuy}PqJ z$KrJ_j?t{72(&D#B#X9OvFx}~3rn(0MHR<#<*?!;jB{0-!%3Xvk1j_^C2=BEt|~`y zRp3O-@Atj#o&yWnPMig%`}OO0zwiFO*DsHc7YzIzdEm3&Kdc+ZzvIK;Uj`4);1_(` zGz`zE7@p}_O|xZHY?Eob>9jJHjHI1rww0^oB%Nuxt$Zaf>1@-fxG0-#n(@6*Dai9! zWlZkHN>T3Pm2tUGR3_v;S(%jkRAmbHTysZjx-u{p=`p1DSN2P~*gVj>uX3NH$D8-J9;iGZ>51mS)`OJ?Tct{=^-$%Z)}hLw*5S(G z){)AQ)=Xul^>F23DVJ>?Z5^u|lk{Y>+&W%4F6k+Z>5=gd-1f_pY!+Jaw;d0v(MX)oc+Fi%dS{>KH%Ml=llG9cs}Lb z?>&Ha-sK(i9>o2$SMnah{oURn?=bF18h&<;S}(2Y$G*9(Fp-V6NV2 zwQ6l|&R<@xHrh*_LgB3PYoTANmApm}s>b3**yyy61|=4mWf8nkem(4{nEm2~cG-a?dxK{`07d^qzCLqk(2{0fkh%)_hi}j>4NFs$KI{SY5A$D|oh>o#lDn zgT@WsTD5iDa%c7Mkjw-e4-&=ewdPt?`Pb^9YT|j!_Zndp%`G*S@t9v^xhe)*cBAR_ znhJarzPf=iw>ZowAJmroYP;6*qjCKj;|MUvs3>oer?Hz2zv)$*jkX^|g=gELy7^M4 z(GH^>4NRqqw$#n)I&*?(EbLU5Fm1MqF*wW3&LYRMUaPOwmK$vj?EXvXs-N@Mv4S-& z?3J2o<9!`?m;Oy4MT|U)pT)C(QP& zTjn|AP4o57y=HA@HnY7p-uJ8Ubg4%H#Q5sg6E=@U+S4X$8+~eoj1+4f&4u3zr}pE z)Bdl>DXdwj`*S_lv-cXCW4$r9roMx=#`YWD7|MUAH#YbVrQP^#QR=#Py+4t@w^8~; z^8QWpD$1h$406x+Os-O|aNCrzT;%B4PjBo;EzAP6Xmh+*^m2L>sByy^mmJ%h&?S1~ z(n3*M7{@A2_Qtu=Y_B)A>9!|^`(XQc=U)AIFBf1{dwkOCJH=G!th%hRn#Zs3LBree zc?%dh-TnPS34fPWtsOKq@)4R!#ElYRsnl$&`K6=B=fWG|(Nd#b3Ohhht##`om z*;dStii4ala+m#3*u`5Q?1A50lD*QG_prD_s^4R)7A}U>o$L=`j_-zs!7}MEPU8WC zFt*IwBq_$KrH=H>Et{#^raBTjJ(Gjuoq2m@sOk)=l{4ynxJ6?eUc$37*Fs=I#DO3!R4lUKl7gDKL zL_i}8OY6FBPTtk=Zs?DT_zZr*PvA0y3P1va|FTnYyzH`B$#_7629d(`@akOg zoqnQBHWwp9%%U*70vix$!}u1vvzONWdSmG(xofjgZ-gbh7ru+8x2b<8*Y3pqpX(kv z2a!1hqpmkMJb!Q`uf|h(UHu3)S6#+s2#H9gNFoG@4k6)zKveY=BoS##t{{s@JD!0( zvF6X0GkRaRygEg|RWG6e2*}v5ghEU%s?M6KFuAT@?W|Kw z4SoT|0#PD?NBUp)zNC@4yUX7-DNal#Md;?Rg^KB(w;RJ0mb)5UYg5v1P`3F7N5G{4 zh25X_JX)$QC0Gi7O#gs>%Q0chfS+&pBa5J3MdiPMU)<+V!#46W_{oG$ArWd4Zb3>1 zKk0#SJ!wEL2fjXOgf?Y9q@B;3w=C$58SGiM`>P@oQSN&IfU5Lz3uMc12<8I8P%t{o zPKyhS5*i_kwE^GFW=UtQ`8NX!hfv*owAjL~1Yx6I>L|z&X?3(#yVhxVrJ7o7fcHa< zQ@#`-0(RP^U}G_W!UP3Jw;Ky<9I2**Tv8#1Wm6z*9?&;d?=+jx3JELG*o*AZ_s%t- zqX}^wLzT$!@C+s^+{jJzC23-0@uM99jJDbK8qkY0L!m+i2`nU;zGmWsgI=(gG4VArZe#-oa(Y8L!{wF@F+bcEqdO4O~%c|c)8wYMit4;-1%;HCV1+pnV_5For%*I+9DVa84gOMLx8}P-Uc^8 z>~EYY%>=VEfv|iiQ(Q1~sUjOz#Cf z1A`8TnMQ`feE~AcgjIOQvMqD^9T%c})`ZmZj`dCXzrpSkSS(e^M2$O zAuOg?IoiYdI!R{o$8<_UpT|SbhMZ_@8IToW(70`zM*C43JRl8I7XG0gSRQcZ>7Elj zO|=~46(w1Yl0Ry~_(5zDJ;1L08W8=8f~lsoz=d8Q&t6Dq&H^FQ^P3=(rN&apZ>@(n z3n_)Xu$0t+9^LM=XXX6~5Mewk=({ipm5>xEgy3BZN(&3u*_l{@j+GY{w1NwgSsH3h zS^`-O(Qb%J^PRT8un;#r(qtW!9V9s&*201wvF;<8N_k;!q>sT?eV7V3YW%OE%h}F) z+T2FF38TT_n)o*Wg24&05UtABAx)s(b#0?!BaI-~@WDDn>mOuF9ksqvYvYv|E{>f*kK)QM-A6}N2io}Ws1=a@ z7z2#70+9V;* z1kcdc=h;jJAPhhsDVOBlb0A@4x?jI?Z}{i>bA2HzPypCymrJ!J2n{gXv^yK^ zI%R=I*vE+lE|qNN1_+UsGGMYTwE#o4Wr*ROQp1DDK#7?}x$TQSTjBI!AWq>YbBd0;h+x3p;M>)))iaA6M zRI_%Jkrjtsrr815>AhB;KrPv7^-0z@N7Z=sF=WH5q17}aOM*w85v0)U?i|@n0{M_Y zNJ*1hdKSN+giEr8w;}Lh!>r)`5F6Mtg+kHjXLp}CrwPyiRr;gtN>{_OUM&b;vN-BT z(W|1;8?!0kccZ-)%eZ>xkMpfuL^1t19(QxbTu}pdo~+s3mCkek)`!F?LTf5c{S+RA zXhVDuQHA6hY0ro!?yP2D_sn__U(Ok?KT3gWH3v(o^?AapMR)~7qYm=(fb_!F80+)t zwsi}{{p#_4u4mr{yvuX3=YT1E8hwv5#YNtl5ZI?i^N%C^>MG}Ju&FeVlO1ZiTRoux9l~wUZ@sv1@^epwk4y z7OMay0M+4zb|J-uj*8%iUR7(YK?0T3@FNRG%B&t`WVM^4&K8A-w#!BjDP%s1hmevUB+OOtQ!FP{6A%>0zQRwdAS+urkr^!*tBtDb zX=pw4oDcIQRq9JGzI^fQvzNGPFc!BPOMVavI(NqgUJ+@zh(eI{T8K6}kyNR+YHN_h z#E9i4nd)b_Afg*kj*FFr%8ulH>Sc+nR%>LT)E80r5iXSIsjy8Ff1xPsP8Vlz3x-yS z_)EYR{H0QkHGw3tS6k&^ty;AqZ9j}VT?zzHy>@)g606lqVQvd+C6=x{r4p3NWBm%u zspds$hK*|xZP%!T%>Ie~73Mpkf2Q;i*hET@PZgE7RG=Pk*sVZRsg;h-iW8v`g2zIB zd0#+~cmQOHMsO)kO}>(|<8MzQTLYw`r^i>k1Vd0uFeh_cuTFltp}PM4QY@j~_`gho(Ea6xf0 z0~731el7(`cQA(wOp>+*aUcKwz;Uydh%k;@ppg<5d&eDj-&NS!>#L zNBnsPzku|vZ?g0(m>O*wEpdn8(#ztWfzi^%J?rJY0`56)%q!yV#wN;qY@#f9JG^P+ zk9j-2UAPy$-QFJD$GyGYKHMj~{oVoGC%yZ;`*EN09sub-s5q7(=A1(Ye}dd4ycUiH zqu06DlKSwjN_~a;pGAFoRR5XO&)>5?>zKherG6o<|4r2Y5bDE| z3e}w|lnJZvy=DNtxro^I|thObbB3b?UJ^>Ep6@YW#U~i zASu8+@+L(qs)Kv*g#BngjT(DV!%|aHW8XlH;!utKagAa)?j7i1mbYM{5kh_6d<1u$ z7;n37V#U2sE_4QjQg|Me8;HxE_L@>6J312_jdK2txN@8*b>BN!6aLz3jm}2U zyorbkDXA5wZCmS3&;?v;CPllG!_TGe(}QhOHr=)zdSu}mRM1`9LpGYUgJjf62kGE1 z4t8+w!HerRS%i3D?%C-Mu$K)4UR?9jiS4~Bi%bQkr89&5-+O}C2xu^U#{&1FJJ3%u zzkz;|De8Xa2mATs&2ziHqs+2`R@KeQt<-&Fu=!+|bZ~43Lz#7ma(*`}w^H|U0Ni#X z`a_!O9u?kRr&<@5R%+`|kj?-Aw56^l23vvI@JI@%Ej*nm>xBqXg5TVIyBYcshuaEC&Mzb(=d9`gIzGE;i1+k z7`)zrn{Mc-kUUw|_X`f?%i=p()PS}~zIo5Q3CIv^FKZW2HcmU1nSG~w@H`Nf@ltc! z4swW-QIMyyCYx7{Y%ooK#$?w_wLD|#+P(v_cmSj#s zMfvm;d=i`1RnR~%&}{e9sjhy}Z*{K01O&k_#yUom(C)=YM5RFT52@`7=F1DfD9rqT zp1yK_5rI#tjqsKYv9>WVB&8I(S(rO1MzS+6?fksjj~RaV9sG3*`Ymb8Wq5`&h+|py zRT=MLvmwD#zle0%(EyWA2SJMtFjJkz+fkqxhii#FicS%Hfxs|4`7oB1W<_g zoKLoAsQA#f5Lo$>ZK3>$Fh)o|2rv+}S9B||Wo@4fp#syV4-3HI3$cSxufo7e9*qI{ z=-jqy$|dv>+>gsLWSQ4@L5b5!9b}pSMuwlXuEGK&^L@+iYhj2yK@gpK%ZjYIQ|fc5 zKDs1@C%L7JiFx|ZayWAdL{bU8Nz4rFZ!r0IkweKA znY6a4U*?5woRMA?>B-M0-5H2AfyHHNw7YvRI1TT5siDHkGBbDr3v6}|Kdbq0487Yf zFmz0YoT$HqOOz4792)W4d?})HUw-~F@_vCMCPEs(|AFzww5(2mNdBmVr0OMmOZE#^ zKA5l>!^iLzcnZb8|9R^iFcy|Q112M}Zonz!2+(A^^C^NP(9=iHL1udtTaBQsfjJXz zW4P)gr#gV){67Mj`palT{TeSL3-wo#_l;z00ZAa2-MMsYQ+A*5gzaGJG7UFrNw_`^ zvmaua=0*fK_1Ad$>$oI_l|Ro6LR_V|w9#xP9AaDedke+B#zE4oWjIsj9ym9;2kt%0 zp}ifV$t5&Edb~;L231KxI{S`fq*i|{1%=oc|mz`@;UI} z++|fF2!AVq4sdyM0(`!ED1{D@?Y0A4_3NmKUWE9`vTsNHzJfx3h0SZDRo2RWKMN0A zcVf7MyU1TkP7b1hL6C#ll(swqoRtFH189xbeFke1%ph`-lgEoJG(i2W(c!+0j8Opj zD)N3K1rX5cfq{yoy6r%@aRCDXAbo&2!i_ggN=-9kXoWV7YBZg3=s#}!n6ZR-Wta?L zeq1$#5^_4Rdr!wR1B)`(2)Zb={!ut+0MWodvHKnHNZ!k}2 zx(r0d@HOQ9J&vJ>gdyJGX-G?e*}eA|$+j?Y7L|iR;HAfFTi`vHZca)qQ-L(y=i%+V z2cHudSAP>dkHYmYA@8?SxOM>7cTMIGrFvXSL~jDXv8Q`L{t*Hsf#fCxbV!1Nm7q;{ z`zS!0{vhqKrpv;fzhjQ8UqKUKVISlO9b6ESD2EaNl`HC3nUNJ0&P(XZ-(gAFxe?C$ zn=H%;30=YNz+XVNHks~`ZfIz4NYGQlvJVf$z%7`+Ib2L39j*>Rb!14we4{L2ie|~b zk6LM96^9*+E;XU+-*RNaJ_bjmrN$v)1m*hf3yvt!b~vJ2pcgQ%!;v9e^>>6djI&x&hWVv2AK;bsu+Mg&pZAf$|B zAc?$66s{m{yI!S@sFK~F#JW<+!wLR`BuDmGAmG1of2jr{#2JPKnjsvCjl&%l{eAEZ zBIJ2z0>yKES~6eWG2)<5jCB+xNc1uFWj<0eR$u4kE4+M_7m|NPa;ZpSwB-Gdm=c)m zL!zXFVGz&HCPQ^?_X0c$SvTVr+#G%xw}`uo{DL;!qx8_9ji+bu3tq#e?^}RhL6j!= z74(S!co)1J`~Yb%v<`)aU!mxYBR}sKya{hof}_X0DQ^ezit!;n1VMv@>{eew7u~TJ zJ}`gb{Ii!XBd)bh0+jqQEtA1r)*UR1!MVIr*k^FACP<{{*1Q-n%1(vu9srTVFbA@V zmGRW0Nt#rdkiZkTM72A0yQp080~OGH9Go8q;Xahq(f-u`523i?WSU~foV#Im9~|j& zG|=m=HRBGR<(hf8gvKqfm8$qu!2#iWhSB061w8RO8`}F!kE^_OK_#BV+kE^M)IfZz z=GIu6D#q`m1yTsjp2h{9MCu>GQuVVKNHifn_jJ*tV$DMsUc5~>auAzAE8_@r_fpeg zG%0efSdCyIMbMrTbW~V@Ia91j-Uj2v!6|}mckyS?^G+@%%@&5>EDq8Db9t3ulj)J= zrAJ>@o)z%Nw1c0lsAh>Vod&hu5^F0z^oF zMrcg5GhLf%yQ5kXFZdwG&hBwM#Oxj#8S6F`hM<9vKwiUp@IhQYBLW8ZPsD!O&0OZK zS|a#E5x|p;!;KcCWhcz=sb?-xin7x{r#=V0A`wr+21;;%qp2Xk4TDicBC3U}r_l>3 z7p61YD=prPYb7&0+ULbXQ{r}TN+RCxK|;j)iN1J0V2wk-bc7PxRMO|%sc8R5vbZ$5 zb>EeAja5ac#N05rZl`i~53mgRTT)dkDddFnFr82VRzj>POKL2DuL_PBh;H;CCt{_D znszH#)*vB6l~MjS3LWMs#a5Cj*n0>D41aas03hVQ!KWl zebr#4vw^d?!Nxi)yD;O&eiTKKMV$Ky6#ou0`aDR6yotP%$-)ZM?i?L1SnklClF+9p zMl~l6=R#DINSg0Ufh8GIsfEx;Ec`54d=fDkYuR#gIjzy%&){Vb$1U~V%pIp^M$FWh z#lxJy+qeK!wwuhq!mD?4GWY@Rx<^N*a<8QtnjW!_zq9xSKaC5g*V9g3iHR2qtLd*q zpi|fuY7FNwWV2l8-lBmL^hSM1-sLGL!uh%LSHevVNmlZ;b|4 zD-P}qH;AxJXd`XM;YWm!@a|eg8wVuK?tMwmG0F_`o}tYm9Hy{X!2=P7%O{8HDtFW+Uri0 zk6k=_sdx63OTE|Ta2BVm1sZ(c0fFW?eeB;r49>SPBpt<71{?i9Imh)fba3G9NRPaxr$noJVu7^A}dr(Vt&;4e%8 z0>t9X-M?ZyX8ee8(fDu@2mG=0lMb3VF`kPok|zWjOARnPiQnSK9E8P`jwau9I9fVZ z6T+nr1l6G%FX2cXT*1pcH4a4;_C$JTOW+?kZzM;=<#=5q)a)`c3!+}BH!uh4upQjh zkMZ*3ynGfHpw7>Zx z3PLO@4#2N~?tegek88dQ2}AtWF5=;)EX$s6bBJQX2jq(-rBic{KU8=b1`>jB z9MB75BwzRlPQrwwY9vnboSYlLSvLSI3qz8hgG7871MBmGzGnZHt-ixo-{ob27s2Bc znWNnlJ)k7WP^+Cq1pVR=$@LU2u|^I^kNVH3JA}|A;pw`Zzyn>D>-b;}?Sfj9#R)Tf zG6LRVo;w2{9Yj#*k>Em(Z9X1&9Gc*0kpN?~y50AV>VUB&{dIosII^`d6*1 zPjT${VSl?1S2Yj~elVpkXQowvushf$aVqGoJ-@@N2EeK@$N^GBI2I7Zx5_ z$er7C^@)LH#3uy`ruse990D(PnZ-}QYZVC~93sdmIwF$Xfd`d-Hizdd>2F&<)G#(P zI4hBn`16Mmy@t~gp1JBS8DajG@e?A3NUcmSi#Q3?yLtkPPcCVpER;-9G`Oq~NX81U zeoR1Y^Yubh99*VyQJA1Uo#0*)8hPPGnLuS~6ijh=YzR)VY)~P6PskxmsZ_S0SK=!W zfkb!8M+oFtC}LuU0hC9)u;!n{QmFrdOO%nON0)p0?v4EnS6lN> z&d{n8c+*waz)SB9)*Z40NnpI<2K+eid4ge;LRiPvBq!xPt29)YTTF%jgac9KTqvp-+tWH|+`a;xtnb=MThSp=}oAffpN z<2W!uFxT7Siqc{fs2K3ngWC$A1qbhK*g6qVW5<$fW_}1!iCpYj5cPf}lBf-F7EqQ0 z0oZS7J+W*@=A6h`^6e9#?;+HHX7DZCB6DqIW5px%XE-_8BMVZ`F}(&jD&J(NwtUBj?8>EwkVlI3#_W}^9to_)--OZ)>%e2->c7w0quYcp zv8*kR`T(N%NY(Hdf0olAFOx4eSnl_ocbu%kq7LsAsVJGJ?9?<8{E!C^OM#X;@eWw? z5;mY$0>oeaCzKmmx@a81KPoNqW%iCAdRO?;nQ^|(6;1xQe<=q15FU~U4s5-NuL+Uz z7^`9ehbRyquEo(|3&#Z@lY97d2Xg7fgwqFNgnc_1Dh>sZk|%3_nCT<9;2RhGnhmk} zKQd1Q1X<@#;StLG2s->J*2!V6!s%aQm0{#i=+XEZXGxvN0HV|AGy(S`@`rGNL^j2R zui$}1b`=T)(VgzvTL`+1eFBGi2%v7adL#fsazCU$M1`8!xTfMs2`m8u zh&ORCXe%FP!U80z>=x{Jbm;)cNT$a@f>A07KfZbf0m%qF##;u)OdymE1R5u#lZDLA z%Nlm`{4`Ku2HqY0MUf($ws7IuyzC>Q9{^6Gb)HM~>N zxQ0p&9O0qA%sr!_53~#4*&(rZK&XrOw~vljOHFbISs?i%j&PR+N602MH48-wP8{x# z$d;%cQknR|)0^032^%y+cMBotw8K!XP{(lU-P#)Su=2NX!jY1&6{{Fu22HzqY(U}A zpEl4je9z(NX00@<4j(=YwPA^>!%IK>-0X$<^DmZO(WkW`lEDoovYZ?)Mf6KatR*Nc zdPj|4BLJ520}u!S)L(+onnm{r)y+gA2C>&KDLFzsgO?vBSZ}WDCVh5y$DN*;J3gz9 zz2-eqel0kDWPsEbBR4iI1zOeH|LpQIzkPG}_u57U$p4Kc)(XwTOudauWbc@gW`-ylw%oCd3f%NLgsM<`;Trriz3f>20vl#!|oEw zFlq-1bl;y$iIz@(rG{v9l~}D{i&CX{<~VSBa~)r(qW+SYy08^<)Z~U$XE+_JUZ;+4 z<@8}$Qldz((|^L*;0GO82Ek)8PbnTijHby~#OWs9S{iA^c3;U?6X#Vl)h`1#GY;@e zjBqs3&t?PSHP_KTqF^0Hf7)QHD}N3j zC!C{^KZr%&ehEc-S=ramNWZKo@T?72#K_A2?aKJzgrOSado3yreVtJhJbbMX+8>V@ z^1C;IeBwa9S0$fCkS{%Hsho65PA4jAZ9`)k@^6Y;pfWiy9G)wX08ag3g}rQuwg>r4 zkv>25B|g$pESjIFIa+$7%qUWoFl^zV!dQeh38c!VZwDQb`=tKt($m-&BcT54I0SSJ z>~xF_{38ko8tC`O;P8}Z+r2b#VEPlr>8Vc`&rZDrALCAV74z;G%q<1iDY)(qck2HD D@4!l( literal 0 HcmV?d00001 diff --git a/venv/lib/python3.8/site-packages/setuptools/command/__pycache__/install.cpython-38.pyc b/venv/lib/python3.8/site-packages/setuptools/command/__pycache__/install.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..2dd42fe12db0742d3fbcaef89e207f2324d1ae85 GIT binary patch literal 4068 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 L;1^$)UbOxTjnOY= literal 0 HcmV?d00001 diff --git a/venv/lib/python3.8/site-packages/setuptools/command/__pycache__/install_scripts.cpython-38.pyc b/venv/lib/python3.8/site-packages/setuptools/command/__pycache__/install_scripts.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..7744867c253d20dc9b366f310a76aa38ae113c31 GIT binary patch literal 2376 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 literal 0 HcmV?d00001 diff --git a/venv/lib/python3.8/site-packages/setuptools/command/__pycache__/register.cpython-38.pyc b/venv/lib/python3.8/site-packages/setuptools/command/__pycache__/register.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..245fa973b0f0a8034e9c2fa5c0e27e2b2730bf45 GIT binary patch literal 863 zcmYjP&2G~`5Z+zeO+zfn0JAowhy&~x@~xt+mf64 zlh@ivZp19CEN5Yd$SB zmH34GAQ+Y289v8f0SGkq4(A__C;X;GdPydnBsLszt{~HpeCp7IaZ$=PFR>L zfDGTqld>%AE~Uk+$otx4w4lx1?!0v{9KIXwhF!mICrar`rQA5EbIU^q3ZT z(W{i37H7TF3Fo3W+8J!7+tWF>qd->~d+x0kjV^HP5|VdFs5?U$GM PU%6efD_}Z!gLJ(A-Cg1V literal 0 HcmV?d00001 diff --git a/venv/lib/python3.8/site-packages/setuptools/command/__pycache__/rotate.cpython-38.pyc b/venv/lib/python3.8/site-packages/setuptools/command/__pycache__/rotate.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..bb5dd42a4c2dbc0a9afca36b2d99816e5314ad28 GIT binary patch literal 2572 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 K54GFXCjATIhyV}( literal 0 HcmV?d00001 diff --git a/venv/lib/python3.8/site-packages/setuptools/command/__pycache__/sdist.cpython-38.pyc b/venv/lib/python3.8/site-packages/setuptools/command/__pycache__/sdist.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..9da4337c81351bcc72affc8154f308339e889737 GIT binary patch literal 7911 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 literal 0 HcmV?d00001 diff --git a/venv/lib/python3.8/site-packages/setuptools/command/__pycache__/setopt.cpython-38.pyc b/venv/lib/python3.8/site-packages/setuptools/command/__pycache__/setopt.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..150c699ad5662efda2348dda6da01b597b39660d GIT binary patch literal 4589 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 literal 0 HcmV?d00001 diff --git a/venv/lib/python3.8/site-packages/setuptools/command/__pycache__/test.cpython-38.pyc b/venv/lib/python3.8/site-packages/setuptools/command/__pycache__/test.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..961eaa6c73b6539055712cb7e80081dd5344a75a GIT binary patch literal 8537 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 literal 0 HcmV?d00001 diff --git a/venv/lib/python3.8/site-packages/setuptools/command/alias.py b/venv/lib/python3.8/site-packages/setuptools/command/alias.py new file mode 100644 index 0000000..4532b1c --- /dev/null +++ b/venv/lib/python3.8/site-packages/setuptools/command/alias.py @@ -0,0 +1,80 @@ +from distutils.errors import DistutilsOptionError + +from setuptools.extern.six.moves import map + +from setuptools.command.setopt import edit_config, option_base, config_file + + +def shquote(arg): + """Quote an argument for later parsing by shlex.split()""" + for c in '"', "'", "\\", "#": + if c in arg: + return repr(arg) + if arg.split() != [arg]: + return repr(arg) + return arg + + +class alias(option_base): + """Define a shortcut that invokes one or more commands""" + + description = "define a shortcut to invoke one or more commands" + command_consumes_arguments = True + + user_options = [ + ('remove', 'r', 'remove (unset) the alias'), + ] + option_base.user_options + + boolean_options = option_base.boolean_options + ['remove'] + + def initialize_options(self): + option_base.initialize_options(self) + self.args = None + self.remove = None + + def finalize_options(self): + option_base.finalize_options(self) + if self.remove and len(self.args) != 1: + raise DistutilsOptionError( + "Must specify exactly one argument (the alias name) when " + "using --remove" + ) + + def run(self): + aliases = self.distribution.get_option_dict('aliases') + + if not self.args: + print("Command Aliases") + print("---------------") + for alias in aliases: + print("setup.py alias", format_alias(alias, aliases)) + return + + elif len(self.args) == 1: + alias, = self.args + if self.remove: + command = None + elif alias in aliases: + print("setup.py alias", format_alias(alias, aliases)) + return + else: + print("No alias definition found for %r" % alias) + return + else: + alias = self.args[0] + command = ' '.join(map(shquote, self.args[1:])) + + edit_config(self.filename, {'aliases': {alias: command}}, self.dry_run) + + +def format_alias(name, aliases): + source, command = aliases[name] + if source == config_file('global'): + source = '--global-config ' + elif source == config_file('user'): + source = '--user-config ' + elif source == config_file('local'): + source = '' + else: + source = '--filename=%r' % source + return source + name + ' ' + command diff --git a/venv/lib/python3.8/site-packages/setuptools/command/bdist_egg.py b/venv/lib/python3.8/site-packages/setuptools/command/bdist_egg.py new file mode 100644 index 0000000..1b28d4c --- /dev/null +++ b/venv/lib/python3.8/site-packages/setuptools/command/bdist_egg.py @@ -0,0 +1,509 @@ +"""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 +import warnings + +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, SetuptoolsDeprecationWarning + +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.+)\.(?P[^.]+)\.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. + + warnings.warn( + "Eggsecutables are deprecated and will be removed in a future " + "version.", + SetuptoolsDeprecationWarning + ) + + if not ep.attrs or ep.extras: + raise DistutilsSetupError( + "eggsecutable entry point (%r) cannot have 'extras' " + "or refer to a module" % (ep,) + ) + + pyver = '{}.{}'.format(*sys.version_info) + 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/venv/lib/python3.8/site-packages/setuptools/command/bdist_rpm.py b/venv/lib/python3.8/site-packages/setuptools/command/bdist_rpm.py new file mode 100644 index 0000000..7073092 --- /dev/null +++ b/venv/lib/python3.8/site-packages/setuptools/command/bdist_rpm.py @@ -0,0 +1,43 @@ +import distutils.command.bdist_rpm as orig + + +class bdist_rpm(orig.bdist_rpm): + """ + Override the default bdist_rpm behavior to do the following: + + 1. Run egg_info to ensure the name and version are properly calculated. + 2. Always run 'install' using --single-version-externally-managed to + disable eggs in RPM distributions. + 3. Replace dash with underscore in the version numbers for better RPM + compatibility. + """ + + def run(self): + # ensure distro name is up-to-date + self.run_command('egg_info') + + orig.bdist_rpm.run(self) + + def _make_spec_file(self): + version = self.distribution.get_version() + rpmversion = version.replace('-', '_') + spec = orig.bdist_rpm._make_spec_file(self) + line23 = '%define version ' + version + line24 = '%define version ' + rpmversion + spec = [ + line.replace( + "Source0: %{name}-%{version}.tar", + "Source0: %{name}-%{unmangled_version}.tar" + ).replace( + "setup.py install ", + "setup.py install --single-version-externally-managed " + ).replace( + "%setup", + "%setup -n %{name}-%{unmangled_version}" + ).replace(line23, line24) + for line in spec + ] + insert_loc = spec.index(line24) + 1 + unmangled_version = "%define unmangled_version " + version + spec.insert(insert_loc, unmangled_version) + return spec diff --git a/venv/lib/python3.8/site-packages/setuptools/command/bdist_wininst.py b/venv/lib/python3.8/site-packages/setuptools/command/bdist_wininst.py new file mode 100644 index 0000000..ff4b634 --- /dev/null +++ b/venv/lib/python3.8/site-packages/setuptools/command/bdist_wininst.py @@ -0,0 +1,30 @@ +import distutils.command.bdist_wininst as orig +import warnings + +from setuptools import SetuptoolsDeprecationWarning + + +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): + warnings.warn( + "bdist_wininst is deprecated and will be removed in a future " + "version. Use bdist_wheel (wheel packages) instead.", + SetuptoolsDeprecationWarning + ) + + self._is_running = True + try: + orig.bdist_wininst.run(self) + finally: + self._is_running = False diff --git a/venv/lib/python3.8/site-packages/setuptools/command/build_clib.py b/venv/lib/python3.8/site-packages/setuptools/command/build_clib.py new file mode 100644 index 0000000..67ce244 --- /dev/null +++ b/venv/lib/python3.8/site-packages/setuptools/command/build_clib.py @@ -0,0 +1,101 @@ +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') + 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/venv/lib/python3.8/site-packages/setuptools/command/build_ext.py b/venv/lib/python3.8/site-packages/setuptools/command/build_ext.py new file mode 100644 index 0000000..03b6f34 --- /dev/null +++ b/venv/lib/python3.8/site-packages/setuptools/command/build_ext.py @@ -0,0 +1,330 @@ +import os +import sys +import itertools +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 + +if six.PY2: + import imp + + EXTENSION_SUFFIXES = [ + s for s, _, tp in imp.get_suffixes() if tp == imp.C_EXTENSION] +else: + from importlib.machinery import EXTENSION_SUFFIXES + +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 # noqa + + +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 + + +def if_dl(s): + return s if have_rtld else '' + + +def get_abi3_suffix(): + """Return the file extension for an abi3-compliant Extension()""" + for suffix in EXTENSION_SUFFIXES: + 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 = ( + not six.PY2 + 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/venv/lib/python3.8/site-packages/setuptools/command/build_py.py b/venv/lib/python3.8/site-packages/setuptools/command/build_py.py new file mode 100644 index 0000000..9d0288a --- /dev/null +++ b/venv/lib/python3.8/site-packages/setuptools/command/build_py.py @@ -0,0 +1,276 @@ +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 +import stat + +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" + + +def make_writable(target): + os.chmod(target, os.stat(target).st_mode | stat.S_IWRITE) + + +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) + make_writable(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/venv/lib/python3.8/site-packages/setuptools/command/develop.py b/venv/lib/python3.8/site-packages/setuptools/command/develop.py new file mode 100644 index 0000000..b561924 --- /dev/null +++ b/venv/lib/python3.8/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 not six.PY2 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/venv/lib/python3.8/site-packages/setuptools/command/dist_info.py b/venv/lib/python3.8/site-packages/setuptools/command/dist_info.py new file mode 100644 index 0000000..c45258f --- /dev/null +++ b/venv/lib/python3.8/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/venv/lib/python3.8/site-packages/setuptools/command/easy_install.py b/venv/lib/python3.8/site-packages/setuptools/command/easy_install.py new file mode 100644 index 0000000..5a9576f --- /dev/null +++ b/venv/lib/python3.8/site-packages/setuptools/command/easy_install.py @@ -0,0 +1,2354 @@ +""" +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 + + +def _one_liner(text): + return 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"), + ('user', None, "install in user site-package '%s'" % site.USER_SITE) + ] + boolean_options = [ + 'zip-ok', 'multi-version', 'exclude-scripts', 'upgrade', 'always-copy', + 'editable', + 'no-deps', 'local-snapshots-ok', 'version', + '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 = '{}.{}'.format(*sys.version_info) + 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 + + elif self.user: + log.warn("WARNING: The user site-packages directory is disabled.") + + 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, show_deprecation=True): + if show_deprecation: + self.announce( + "WARNING: The easy_install command is deprecated " + "and will be removed in a future version.", + log.WARN, + ) + 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') + + if not os.path.exists(instdir): + try: + os.makedirs(instdir) + except (OSError, IOError): + self.cant_write_to_target() + + # 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 with easy_install + pythonpath = os.environ.get('PYTHONPATH', '') + log.warn(self.__no_default_msg, self.install_dir, pythonpath) + + 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() # noqa + + __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() # noqa + + __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() # noqa + + 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() # noqa + + __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.) + """) # noqa + + 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() # noqa + + 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', '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. + """).strip() + + 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{}.{}".format(*sys.version_info), + "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', + '{}.{}'.format(*sys.version_info), + '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("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 not six.PY2: + 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=''): + "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): + """ + Warning for EasyInstall deprecations, bypassing suppression. + """ diff --git a/venv/lib/python3.8/site-packages/setuptools/command/egg_info.py b/venv/lib/python3.8/site-packages/setuptools/command/egg_info.py new file mode 100644 index 0000000..7fa8954 --- /dev/null +++ b/venv/lib/python3.8/site-packages/setuptools/command/egg_info.py @@ -0,0 +1,721 @@ +"""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 not six.PY2: + 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 ()) + + def append_cr(line): + return 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): + """Deprecated behavior warning for EggInfo, bypassing suppression.""" diff --git a/venv/lib/python3.8/site-packages/setuptools/command/install.py b/venv/lib/python3.8/site-packages/setuptools/command/install.py new file mode 100644 index 0000000..72b9a3e --- /dev/null +++ b/venv/lib/python3.8/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(show_deprecation=False) + setuptools.bootstrap_install_from = None + + +# XXX Python 3.1 doesn't see _nc if this is inside the class +install.sub_commands = ( + [cmd for cmd in orig.install.sub_commands if cmd[0] not in install._nc] + + install.new_commands +) diff --git a/venv/lib/python3.8/site-packages/setuptools/command/install_egg_info.py b/venv/lib/python3.8/site-packages/setuptools/command/install_egg_info.py new file mode 100644 index 0000000..edc4718 --- /dev/null +++ b/venv/lib/python3.8/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/venv/lib/python3.8/site-packages/setuptools/command/install_lib.py b/venv/lib/python3.8/site-packages/setuptools/command/install_lib.py new file mode 100644 index 0000000..2e9d875 --- /dev/null +++ b/venv/lib/python3.8/site-packages/setuptools/command/install_lib.py @@ -0,0 +1,122 @@ +import os +import sys +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(sys, 'implementation'): + return + + base = os.path.join( + '__pycache__', '__init__.' + sys.implementation.cache_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/venv/lib/python3.8/site-packages/setuptools/command/install_scripts.py b/venv/lib/python3.8/site-packages/setuptools/command/install_scripts.py new file mode 100644 index 0000000..8c9a15e --- /dev/null +++ b/venv/lib/python3.8/site-packages/setuptools/command/install_scripts.py @@ -0,0 +1,68 @@ +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) + try: + bw_cmd = self.get_finalized_command("bdist_wininst") + is_wininst = getattr(bw_cmd, '_is_running', False) + except ImportError: + is_wininst = 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/venv/lib/python3.8/site-packages/setuptools/command/launcher manifest.xml b/venv/lib/python3.8/site-packages/setuptools/command/launcher manifest.xml new file mode 100644 index 0000000..5972a96 --- /dev/null +++ b/venv/lib/python3.8/site-packages/setuptools/command/launcher manifest.xml @@ -0,0 +1,15 @@ + + + + + + + + + + + + diff --git a/venv/lib/python3.8/site-packages/setuptools/command/py36compat.py b/venv/lib/python3.8/site-packages/setuptools/command/py36compat.py new file mode 100644 index 0000000..2886055 --- /dev/null +++ b/venv/lib/python3.8/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: # noqa + pass diff --git a/venv/lib/python3.8/site-packages/setuptools/command/register.py b/venv/lib/python3.8/site-packages/setuptools/command/register.py new file mode 100644 index 0000000..b8266b9 --- /dev/null +++ b/venv/lib/python3.8/site-packages/setuptools/command/register.py @@ -0,0 +1,18 @@ +from distutils import log +import distutils.command.register as orig + +from setuptools.errors import RemovedCommandError + + +class register(orig.register): + """Formerly used to register packages on PyPI.""" + + def run(self): + msg = ( + "The register command has been removed, use twine to upload " + + "instead (https://pypi.org/p/twine)" + ) + + self.announce("ERROR: " + msg, log.ERROR) + + raise RemovedCommandError(msg) diff --git a/venv/lib/python3.8/site-packages/setuptools/command/rotate.py b/venv/lib/python3.8/site-packages/setuptools/command/rotate.py new file mode 100644 index 0000000..b89353f --- /dev/null +++ b/venv/lib/python3.8/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/venv/lib/python3.8/site-packages/setuptools/command/saveopts.py b/venv/lib/python3.8/site-packages/setuptools/command/saveopts.py new file mode 100644 index 0000000..611cec5 --- /dev/null +++ b/venv/lib/python3.8/site-packages/setuptools/command/saveopts.py @@ -0,0 +1,22 @@ +from setuptools.command.setopt import edit_config, option_base + + +class saveopts(option_base): + """Save command-line options to a file""" + + description = "save supplied options to setup.cfg or other config file" + + def run(self): + dist = self.distribution + settings = {} + + for cmd in dist.command_options: + + if cmd == 'saveopts': + continue # don't save our own options! + + for opt, (src, val) in dist.get_option_dict(cmd).items(): + if src == "command line": + settings.setdefault(cmd, {})[opt] = val + + edit_config(self.filename, settings, self.dry_run) diff --git a/venv/lib/python3.8/site-packages/setuptools/command/sdist.py b/venv/lib/python3.8/site-packages/setuptools/command/sdist.py new file mode 100644 index 0000000..8c3438e --- /dev/null +++ b/venv/lib/python3.8/site-packages/setuptools/command/sdist.py @@ -0,0 +1,252 @@ +from distutils import log +import distutils.command.sdist as orig +import os +import sys +import io +import contextlib + +from setuptools.extern import six, ordered_set + +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_optional(self): + if six.PY2: + sdist_add_defaults._add_defaults_optional(self) + else: + super()._add_defaults_optional() + if os.path.isfile('pyproject.toml'): + self.filelist.append('pyproject.toml') + + 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()) + self._add_data_files(self._safe_data_files(build_py)) + + def _safe_data_files(self, build_py): + """ + Extracting data_files from build_py is known to cause + infinite recursion errors when `include_package_data` + is enabled, so suppress it in that case. + """ + if self.distribution.include_package_data: + return () + return build_py.data_files + + def _add_data_files(self, data_files): + """ + Add data files as found in build_py.data_files. + """ + self.filelist.extend( + os.path.join(src_dir, name) + for _, src_dir, _, filenames in data_files + for name 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 not six.PY2: + 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' or 'license_files' is configured and adds any + valid paths to 'self.filelist'. + """ + + files = ordered_set.OrderedSet() + + 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") + else: + files.add(license_file) + + try: + files.update(self.distribution.metadata.license_files) + except TypeError: + log.warn("warning: 'license_files' option is malformed") + + for f in files: + if not os.path.exists(f): + log.warn( + "warning: Failed to find the configured license file '%s'", + f) + files.remove(f) + + self.filelist.extend(files) diff --git a/venv/lib/python3.8/site-packages/setuptools/command/setopt.py b/venv/lib/python3.8/site-packages/setuptools/command/setopt.py new file mode 100644 index 0000000..7e57cc0 --- /dev/null +++ b/venv/lib/python3.8/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/venv/lib/python3.8/site-packages/setuptools/command/test.py b/venv/lib/python3.8/site-packages/setuptools/command/test.py new file mode 100644 index 0000000..2d83967 --- /dev/null +++ b/venv/lib/python3.8/site-packages/setuptools/command/test.py @@ -0,0 +1,280 @@ +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 (deprecated)" + + 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 = not six.PY2 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): + self.announce( + "WARNING: Testing via this command is deprecated and will be " + "removed in a future version. Users looking for a generic test " + "entry point independent of test runner are encouraged to use " + "tox.", + log.WARN, + ) + + 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 not six.PY2 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/venv/lib/python3.8/site-packages/setuptools/command/upload.py b/venv/lib/python3.8/site-packages/setuptools/command/upload.py new file mode 100644 index 0000000..ec7f81e --- /dev/null +++ b/venv/lib/python3.8/site-packages/setuptools/command/upload.py @@ -0,0 +1,17 @@ +from distutils import log +from distutils.command import upload as orig + +from setuptools.errors import RemovedCommandError + + +class upload(orig.upload): + """Formerly used to upload packages to PyPI.""" + + def run(self): + msg = ( + "The upload command has been removed, use twine to upload " + + "instead (https://pypi.org/p/twine)" + ) + + self.announce("ERROR: " + msg, log.ERROR) + raise RemovedCommandError(msg) diff --git a/venv/lib/python3.8/site-packages/setuptools/command/upload_docs.py b/venv/lib/python3.8/site-packages/setuptools/command/upload_docs.py new file mode 100644 index 0000000..0351da7 --- /dev/null +++ b/venv/lib/python3.8/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 = 'strict' if six.PY2 else 'surrogateescape' + 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 = '--------------GHSKFJDLGDS7543FJKLFHRE75642756743254' + sep_boundary = b'\n--' + boundary.encode('ascii') + 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 + 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 not six.PY2: + 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/venv/lib/python3.8/site-packages/setuptools/config.py b/venv/lib/python3.8/site-packages/setuptools/config.py new file mode 100644 index 0000000..45df2e3 --- /dev/null +++ b/venv/lib/python3.8/site-packages/setuptools/config.py @@ -0,0 +1,700 @@ +from __future__ import absolute_import, unicode_literals +import ast +import io +import os +import sys + +import warnings +import functools +import importlib +from collections import defaultdict +from functools import partial +from functools import wraps +import contextlib + +from distutils.errors import DistutilsOptionError, DistutilsFileError +from setuptools.extern.packaging.version import LegacyVersion, parse +from setuptools.extern.packaging.specifiers import SpecifierSet +from setuptools.extern.six import string_types, PY3 + + +__metaclass__ = type + + +class StaticModule: + """ + Attempt to load the module by the name + """ + def __init__(self, name): + spec = importlib.util.find_spec(name) + with open(spec.origin) as strm: + src = strm.read() + module = ast.parse(src) + vars(self).update(locals()) + del self.self + + def __getattr__(self, attr): + try: + return next( + ast.literal_eval(statement.value) + for statement in self.module.body + if isinstance(statement, ast.Assign) + for target in statement.targets + if isinstance(target, ast.Name) and target.id == attr + ) + except Exception: + raise AttributeError( + "{self.name} has no attribute {attr}".format(**locals())) + + +@contextlib.contextmanager +def patch_path(path): + """ + Add path to front of sys.path for the duration of the context. + """ + try: + sys.path.insert(0, path) + yield + finally: + sys.path.remove(path) + + +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['']) + + with patch_path(parent_path): + try: + # attempt to load value statically + return getattr(StaticModule(module_name), attr_name) + except Exception: + # fallback to simple import + module = importlib.import_module(module_name) + + return getattr(module, attr_name) + + @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'), + 'license_files': parse_list, + '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, + 'python_requires': SpecifierSet, + } + + 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/venv/lib/python3.8/site-packages/setuptools/dep_util.py b/venv/lib/python3.8/site-packages/setuptools/dep_util.py new file mode 100644 index 0000000..521eb71 --- /dev/null +++ b/venv/lib/python3.8/site-packages/setuptools/dep_util.py @@ -0,0 +1,25 @@ +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/venv/lib/python3.8/site-packages/setuptools/depends.py b/venv/lib/python3.8/site-packages/setuptools/depends.py new file mode 100644 index 0000000..a37675c --- /dev/null +++ b/venv/lib/python3.8/site-packages/setuptools/depends.py @@ -0,0 +1,176 @@ +import sys +import marshal +import contextlib +from distutils.version import StrictVersion + +from .py33compat import Bytecode + +from .py27compat import find_module, PY_COMPILED, PY_FROZEN, PY_SOURCE +from . import py27compat + + +__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 maybe_close(f): + @contextlib.contextmanager + def empty(): + yield + return + if not f: + return empty() + + return contextlib.closing(f) + + +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) = info = find_module(module, paths) + except ImportError: + # Module doesn't exist + return None + + with maybe_close(f): + if kind == PY_COMPILED: + f.read(8) # skip magic & date + code = marshal.load(f) + elif kind == PY_FROZEN: + code = py27compat.get_frozen_object(module, paths) + elif kind == PY_SOURCE: + code = compile(f.read(), path, 'exec') + else: + # Not something we can parse; we'll have to import it. :( + imported = py27compat.get_module(module, paths, info) + return getattr(imported, symbol, None) + + 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/venv/lib/python3.8/site-packages/setuptools/dist.py b/venv/lib/python3.8/site-packages/setuptools/dist.py new file mode 100644 index 0000000..fe64afa --- /dev/null +++ b/venv/lib/python3.8/site-packages/setuptools/dist.py @@ -0,0 +1,1031 @@ +# -*- 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, 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 import ordered_set +from setuptools.extern.six.moves import map, filter, filterfalse + +from . import SetuptoolsDeprecationWarning + +import setuptools +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 or + self.project_urls): + 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""" + try: + # verify that value is a list or tuple to exclude unordered + # or single-use iterables + assert isinstance(value, (list, tuple)) + # verify that elements of value are strings + 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 not isinstance(value, dict): + raise DistutilsSetupError( + "{!r} must be a dictionary mapping package names to lists of " + "string wildcard patterns".format(attr)) + for k, v in value.items(): + if not isinstance(k, six.string_types): + raise DistutilsSetupError( + "keys of {!r} dict must be strings (got {!r})" + .format(attr, k) + ) + assert_string_list(dist, 'values of {!r} dict'.format(attr), v) + + +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 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. + + '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. + """ + + _DISTUTILS_UNSUPPORTED_METADATA = { + 'long_description_content_type': None, + 'project_urls': dict, + 'provides_extras': ordered_set.OrderedSet, + 'license_files': ordered_set.OrderedSet, + } + + _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 {} + 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) + + self.metadata.version = self._normalize_version( + self._validate_version(self.metadata.version)) + self._finalize_requires() + + @staticmethod + def _normalize_version(version): + if isinstance(version, setuptools.sic) or version is None: + return version + + normalized = str(packaging.version.Version(version)) + if version != normalized: + tmpl = "Normalizing '{version}' to '{normalized}'" + warnings.warn(tmpl.format(**locals())) + return normalized + return version + + @staticmethod + def _validate_version(version): + if isinstance(version, numbers.Number): + # Some people apparently take "version number" too literally :) + version = str(version) + + if version is not None: + try: + packaging.version.Version(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." % version + ) + return setuptools.sic(version) + return version + + 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 not six.PY2 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.readfp if six.PY2 else parser.read_file)(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 not six.PY2: + 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 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): + """ + Allow plugins to apply arbitrary operations to the + distribution. Each hook may optionally define a 'order' + to influence the order of execution. Smaller numbers + go first and the default is 0. + """ + group = 'setuptools.finalize_distribution_options' + + def by_order(hook): + return getattr(hook, 'order', 0) + eps = map(lambda e: e.load(), pkg_resources.iter_entry_points(group)) + for ep in sorted(eps, key=by_order): + ep(self) + + def _finalize_setup_keywords(self): + 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) + + def _finalize_2to3_doctests(self): + 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.installer import fetch_build_egg + return fetch_build_egg(self, req) + + 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 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 DistDeprecationWarning(SetuptoolsDeprecationWarning): + """Class for warning about deprecations in dist in + setuptools. Not ignored by default, unlike DeprecationWarning.""" diff --git a/venv/lib/python3.8/site-packages/setuptools/errors.py b/venv/lib/python3.8/site-packages/setuptools/errors.py new file mode 100644 index 0000000..2701747 --- /dev/null +++ b/venv/lib/python3.8/site-packages/setuptools/errors.py @@ -0,0 +1,16 @@ +"""setuptools.errors + +Provides exceptions used by setuptools modules. +""" + +from distutils.errors import DistutilsError + + +class RemovedCommandError(DistutilsError, RuntimeError): + """Error used for commands that have been removed in setuptools. + + Since ``setuptools`` is built on ``distutils``, simply removing a command + from ``setuptools`` will make the behavior fall back to ``distutils``; this + error is raised if a command exists in ``distutils`` but has been actively + removed in ``setuptools``. + """ diff --git a/venv/lib/python3.8/site-packages/setuptools/extension.py b/venv/lib/python3.8/site-packages/setuptools/extension.py new file mode 100644 index 0000000..2946889 --- /dev/null +++ b/venv/lib/python3.8/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/venv/lib/python3.8/site-packages/setuptools/extern/__init__.py b/venv/lib/python3.8/site-packages/setuptools/extern/__init__.py new file mode 100644 index 0000000..4e79aa1 --- /dev/null +++ b/venv/lib/python3.8/site-packages/setuptools/extern/__init__.py @@ -0,0 +1,66 @@ +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 + 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', 'ordered_set', +VendorImporter(__name__, names, 'setuptools._vendor').install() diff --git a/venv/lib/python3.8/site-packages/setuptools/extern/__pycache__/__init__.cpython-38.pyc b/venv/lib/python3.8/site-packages/setuptools/extern/__pycache__/__init__.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..f83fb7a47385bba51d535db3f0b8fd0525626f40 GIT binary patch literal 2429 zcmZ`)TW=dh6rS0eFS)c-BB52OMgq06MC?#X)rx|ON(B`ts@hV(0C5I3HcK{ zr%M2x`%rZo3@4msq)Wae+~fWc;r`U?QXX_YL4_|o9!~vN!7-sQ?UeJ*k?)uTZnl@V%u)MQXTXOAB~W*=?dWCLLmRwQPZrL!rlFBR*jIZyK)CJU3w zJjt@r#(~>NaJK;5;bu+(v({O%!Ti@C255|L!H(lAuJI^{rT5Jq+bco;^hr2># z8{2m_KkVH;7zV%r40jsS>>WRocJkciDt6!ZxdG;4K_UTak0Gt9(m zT>T!ZiMY2{>l0I-lHci+{z9GP1>>(5a6AHOfQV)CKq$lsfOYhCdS@idWDkcC#Y`rA zf%X|p#(zA6wKsmQ967l$=R5+e#EW671pkEPQVmc9XYgvaJ<`4Bhb1EU~QMG4De7XscZxiKS+#?&;gll)vF+V8#hBPsCUp@#vP+ViGKRh>1>i4 zprPdFsD#d&QRzKcq_2S?!5S!)N9*uwy5B0TdmfGa;2-bbbL2X;#W@kfxOe2b0*#Uw zi1PiKTEqWWkPME2KrW|x<0mK3SDEO|SYv4psDoeHnD(6dAP~7UEXkBe_=v%CwaCp0 z1GZYex(eso#$t_)lA-_sRw!L(ol%?Rz`HP3$yxw{y>>>{W|LZ;TMlZu)oP;Lqt*a+ zfMW$5w3p-f#V~Oy1ie}ubJ>eyh2d)Jg_4C(W~2~)h5k{9fWjnt>SpLcr@RUS7j;vw zf~nIWY=$0f)|$)pH8%!{(AVip)F4dYKuKthK#(*vyGv3*+7mp6yfwablDyiB?CX5x gcCsNpMynfm_W#|-*6&ph{S32}OL297_0xd<2VkUp0{{R3 literal 0 HcmV?d00001 diff --git a/venv/lib/python3.8/site-packages/setuptools/glob.py b/venv/lib/python3.8/site-packages/setuptools/glob.py new file mode 100644 index 0000000..9d7cbc5 --- /dev/null +++ b/venv/lib/python3.8/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/venv/lib/python3.8/site-packages/setuptools/gui-32.exe b/venv/lib/python3.8/site-packages/setuptools/gui-32.exe new file mode 100644 index 0000000000000000000000000000000000000000..f8d3509653ba8f80ca7f3aa7f95616142ba83a94 GIT binary patch literal 65536 zcmeFae|%KMxj%k3yGc&SCTD>S1PQP}R5YmQ5=~qJi^+zl1UE)DtPsG8blp-*!#RLg z0>QIub24npZS_`f-)#|`^OhvIcH|hGc(UT^E}VYJoC(K^_@EDjE;rth;Yer@_4k$X3I);E0Tn+-Zb>&yT9Ew!oxAMfl)C z#Z+d`C?Ev=lGJ)}%Ksnx|0)G)SVf_n2-;d?f9!~MzIJJ-=wKb=iHfW2QCpC29wSNm zA=ztsPZ<@3t`2ENV!bW?>DIbrM&c*bCbqaRzr~R~Z-r)Gl=RG-p}ugUHp=<&@N<(0nQZ)pc;t^f@UfdU)Xs*a2q9hEj|W&QGS`}Q+V zaO>`-aSJ8yAtP2OBNk%M7Utt!$6gfgmQ40WtW_PKSW_r1oOg}p=vZj3XtBjwwJ#E} zLMNCsnAlP1f|%AM?kIHMo~S5v2kZEcbEs|ZrY(iCq{N>@V-R$%P-2fEhzyjmCh@Sy zXyr*PE_By~_)26%86IRFp9Ya zkBHB1hGv2=t60ZM@2flwcy2#L^lN{0=%0Q@MjzL)ErkWFb2Ro*N07ImOt!9YmgwvP zqh2yflmnST)@Q6JEa3kv=;e&Js^gRcx7ile@Me+Xh_`B=wJ3|47Z(=9j;P;M4jj9k ze|zYYnyGIobV=&smWsjxVw3XZ39!ke-gcWd&f8i_T!k-^@^CA0*s%-oQ>v?$_-7%o z(GNN8XT7J;F$I$PlNQv_oLiavAq4>E7I2dQhlE)vSn!y;BSSI+5(`L`#@q*i(+$dj ziMR82oKzstr3NgrEei6^p%m@2rUhVv>rK-H3%XZ<_rUh;c(a2dG)%uOg$_v@w_EZo zlu%GsR0^7TQkP%ahpqsf^)t)7t)|hz?tCY-06G}<$V~#?~heoED!!4L2akG@t z3k(cUbnpdgqwk%>`n0WAC7vv#rU2V~=4eiAwpse1#pRD3*UlGpF7&;UP%~^>-Uq9> zqqY#gDuX1JM-HRLrTl?xL1RW6Nzt8%&-UwXtnfuqbCmh#A4k1U7-%L3c7Zx(d zuhG+B-K2d4zoLVczO#ufnYJw*t5&k#)-NC8`0Z!%(?;tLH)1SS=)o%@p*m1Hza}bC zH<@{EP=$nZv|K=--J~^q2RFJ=UsK7|s*{A7>2riBOI3;B9VN6@g>xk)TvhhOKNMSeI?sb zNT@@qXG7GtAEH*Z*I7+?xX^=^+#cd{e*xu~c+oK%QC`k~8T1Fj`XSd4etuu)23Ly= znHbY_evF#lbUsH*M$@PjpbB6kZlDn4%Pfry7Wc9o2a;HxjOT7A9>$Ks0zkIpxF}-P z4%J+UwB{X!v+x4JvU3b1r4SD4dNJCLBe`P~a!!^eLzUU1z9JMV04G)5v%Ur4xPh4u|g#Tc-(r0PB00 z<2OM*Q-Cajywm3kTRsx?bLZ%s;?w6_FF__SF*1GDPvs6}`fAHZ`iq5gfrnJz3GS7o z zuc4jxwz7KJ_rCH-tFJ@z@NXc!Qxa$m*N_NRtT_d&`a7duuH`>P zd%}h`&|B{GYny6$%@oA-ep8*S_YbNQ*wMBx)7fGDgK2FaWZ0dLJaOehDVhGlqZp`r z7Zz^Qt{~7!1nOpo+s>!!UDMjSGVG3o1-MTD`U{)X0)7~njK(aO!mRqVS*o4ZX4diz z7)@AzBH#*!OwC!#-^rCEBXGL5j{ilBGXRTvrZEnIJKR9see4J z?c)sQ$RrZUz7CZ}&@|&(WWQ6oZG7`cz^_)daDP69Az2FAzJQhYnWChD$L)$+G%bx z&7w9mR1|a&sE6y@t-J-J@>a|Gc{fUJ9G}Xg6OuprJK#0?Jp<5bfq@`8o;q|BAqcJM zjQ48!rGWu;JZ~b>4p%t2&K3ny&6 z)6|T!KS#l1EVxey4i&6w$J3D-fJnmY;zyL&4M}ieC4Y4zD_DwoiJ30 z5_=SJD^>f%DnzwDB3tkBl@`9nM7`62cB()9jX5~Dm1WqE>OH3SAe#W)`7_C8+pfMB zJFd=-^{P|*4uT0K)k$y3)D9UFllj~KNTvgXauGr@LJse7Q7R@RDA(z2H9$+ML+eE& zl=voVrX{czY;0=zrsg&^7y3DBQcnlbCHkTK6wlSv)Ot^a>WupS(t25KWYtdJD_Ul0 zy-WLUG9529T3YX>gnVr^CFHB&()t2Q@MyPDf=8_?tuNH(m)6hH=0j$@t^Sg!YDQJ1 zuYFT*)BGE?V&5z3C3>UFt~~e`G$NV?B%)>wUwRqg;i@z=IXRJXAM6bDgMFlKS|1}* zTJt0-&ot@>P~uYMKt_iv`@icGQ&50s{!#;tR+P0W?sZB=UJS z28Qw#@F%T&Xsr_aIZ!Op21>PA8)rgy4p7O3{6Pz%JAtoM$hIO)F4a7n)$ z761{^!~%XE(hSewuU#=}f4+5c{H|(n(tWZhp^o;Mq!< zRjo5}SyjYX;$XSHob{6zO6oY4v*QvB236~|OfFpmxC~b5@TKpZgpU&#G7W#1xq3O3 z<3MV!e|?(f)~nX1p%Pni43kl^-$5TcR@NVMSZL^H&E-&ixCRksAc zLU`VdHD75rv;+qczU;=DL2Y_V&_vjEBUm9@4-7a;8wVN=CKo8r`Ay}yo6Te;LW2km zCg&ma6+&MnuR~}6p@HNqtG1-l;zB9z8^>xc|3Wh`P+C9Ga0W~Xtd-{^<+-e)w&b4$ z@#5nT;nQH;igvjVF^ojjTuW_pKostir4{9NA29mEyNid}uN|4TxhrlC)WdXd>FZ z?h-VBx_toZ4Q;2-s*De{^r4;Sf;^URlfi%h+fm{Ob0O76slOabjS9;G-(|(y5k&(3 zek#h$5I=h*8r>7(VIL+i{Pd0V+%%S+M@0Bp@q8Q%5#q(@z7U^EjPS`!G$(+(`k}%- z#O*6nN~f#>J!8|-`3^7o1-QI(ZAuFGL9cj-g!Tk8}ZggIXanNhBaH* z%$w8Ym-akCd{i@ElJ?9)6rRw2KnzPg>MHL zWA%sB4CVRi!%2H|Ot>Z(icp)l{Aa9616{Nh!pveS`i2Ma03DLWEO3U&EX$~V4~xO) zi_s8B{5_ln-a`((@w7x)Y?Ng>9x2X(W=@XB{D&Y@N&83*@i)+~?fi2zqnK&lp^`u!hZ&&FuC{jXb#dH{4o*tBfc6Xo9PY^qOa0PMpSJ{ZCzqsyow}p zf%MA>yy z&-gy^>=Dmb#gmKYQSodQ&%=1~zFyPB`l*;#0}pG&_qGPaB!9U}cE=Aq(N(&^msURe%fvtfy@-U04P7ip72!ds&zS{&BQP zfb0S1(?^*E(%8XXe_@jn|0by6J>q*uiPa<2GTum>1O`T;OFUo1v-y$F@r)f;V$*<6 zxxSwOBxBbhyp$c;NNYJb+cR(3rm@O_gUW%XWqQ=+o~LhwQWXHG_$SW z5jNrvBb%>H`Q9&KJunO7*TYN%sn3?(GrjM9l7u$cB1!?on^i zxm~?p=dyZfRh62Dm=dqUXFWmia`&ynVMq6Z;jpdSi|}><(*!Z>E*$=p)}4=V)0bCj zv$1@#`k8GT@C_RK2^%GGo{Z!or=xEdC3Sy{6c(r8w_3+22VPE8$VUwk?|v1ZjJ?#d z?luIe*vr0NEPYiH|0;?VH0b^(Q6Pm!7br@3K$LQ`y0q!bh+5I~B~(@{BERM z?U4}bzJtJg>$C~wsYFPs)mz=A_+;Vl>b`0??CGA4aEpE3_1cuC2W)e-iRD9CL7-ID zLCiMic?H0A0^lhkGFc%~0KX@IHA?JFdf%(WUZeMSFj1hlro{Hsd$SVTOYdb$?3Z{O zdx;woaT2be^4!6ovG*{7T!u=A;%kW$=Y`c7EJ1>o*h`$ppM(Z)v6oxb##)uwlhE!L zK|BbE?rM}zjMBeG`2mMsRATo-#`XSMNL zPiK55szNTw;(m*0{!-DMiCyRLQJA!hU8fN=;!ohIB&twBXPo+q?3dk7A=(!wGR*;f zmH4Ab9Mw+-q9dQRF(aRtkO%#|sinU_GzQmLfG(6X%$CM}s#}Tu+JSZPpq9P+VJHV9 zPKiuBJL5!5YDD)oz~~%Qe-}8Rt@jtTDY45@HnsU*=;L2kq0UjBUo;Smkm)WFrzQsz zaZ(FGek(>;EF>{BP3w%4xKbs_@hyu6ngw8|fTKh!qlHy>F)CtYnXuY`0oli@9KP4p zxmNRteU+CaBSCFY-H#O=Jk~#|5j}R|7;01ZpAg)=bGW@hevqcf-LE5A?_aO{-~#Ga zVjtqE_ur%Jcu}N(Q~CZ}jI(RqYcK--f` z*$u-u^BYl7987l&tm;-akLp~@;>4P3jf|vh1&xdm!gT*1BCt>!eya-TOo@qvzBZ|e zQ2iNDWtptbp?AvNZz7_NZTj+?+C3IKAuc7urGmA#W*FkVeLpeU9(>ulfC;|b-cb+0 z5TB6^X%XtM(`pIQ=fw7l3m7PqEu?nW_-d^ex*@!pOr$qxsd${!Og_Ogsu`H35A(O_T{B-&NY!RG*-ckbdHk+HO0|vjjb;+l<6Mq$Ue>zCnpS z2ekn9jv3VFG&VekjGbcGz8tU@^*K}|I^kYGwg>=6O-KB9C~8h~{7t+%<45rXFG$@q z7euEagA%`$O73*@wt3Wii!!}!nDQtuEgDEVNO&H@L}t+dCE6duOzQXu&}83R+a_*t z_&PR>?K`O-m-^lvXQA4JXT_&C#wmJUf{F~PzJ;U$!y{?@r5_;)a ze{z;kSR(>#DXe7X%}ph+4-@QPELf`|eLpD~P<#ctkO^UZ+OJ**V<{Lc%j&ADlKD^D zh9X7D?5ESzvDO!l)qQ}Km>9K-c6Fh+qFvOf78^LViKdv`C4?Z?Mm>D}Ux7K>T~>yb3k%G<(9(Q-eiF; zW^X3gPV@i@BfZ3523R;XaoaM4t4g?fQVe|xA*Ok~9;8Dmc9>rVFv`@;FdHt*cs>|&PpyPe0UP`2eD=g zvFfgbQ|!MPHa(pX@+5W&jIJDok-l1%npPJ!4WXp3E&+NLPGjwF!I|Z_iN$Cc<=?U^ znZZOzzo$!rJI}YV`NpupW2zzj{GeLXVuu9W`n0TN!|A}^<;Os!&SP2^>!5w2kEXSK zlwqH1ZHplztSactN=M`gEK3rV&LEFnX(6w~j-W+mrHrb}^}uPE_qw+H$a{*Nr4ow8 zzFGz?FS2RJF{5dTqbb?YQR&zY>tcGecNr|O?N!1;-1-;v**su^4QMcbISfGyV8u(} zHrJScDG^rhPt&Lre=8-P)A48e6~K=WdCcfqdgpaqO6I^4`F zK}}d6kG*)cjinU7J8j5RgJojK+lx)wDSSUVPHfMn%&-B(Q)XB@^Sg$Yn#i#yh~@O~ zVsRFx43?7=Ef)2sPGY2yYNLx2@%IoSZ-cY2)IzclGvc!#BZ>GNJRx94d^Q3p^_h5& z!jF)M8oNlT7}k16tTxu}c%&amYj-5hh}SOCB5QZV4~f@Pt>X1d63xedAT%NiI1<&4 zPEnH$n$emj7>RQLVK)z0v#L&k)I^8W+9{AF*2UBSh?;rJK)tBMPMUdlAe0b@qx*u0 zz--_|=gQGEUJdhoI6@_ud5iH05LI|VzDc?VJ|^iFrVO)~h{mtX2Rs^&JPJgM^)vaFePM&_EvDU)I+oE9Fs07GIqHqX z11^%P9Ja(^f5Yo6;XnHbcrS5cpTmkjM)3ePJsfM5_ylButt7FO8?^&$xs!Gcs?X>b z2Gv#YpGi2Dv&9d&6BQ4+j6e@0KF|+?vzxumV=x1vQd_)ri+|f97U*XuQLFZPQzNv0 zA%k>}M&Ys)3L$~QjeLSY;hfdNb|6kIP96bux0l|%;oDvCM=09?jfL4?gx*}APLf3? zdW9{Oqqf`4JW7W@2etzEbQtSkrV7NztT#^ri)SK{5ncM`jbVKA(V8A zqm5NETDO0WB>jd|L}{&4iQSGss@PZfoA}gSfE3HzR_E;{tLUXvReu=XF_)L7-vPGW zI1T&ug(LuD|W&H7y!uIhCFTlmu0not*lf@ z%PpJ;soA9gr~1Dvt?jQ$qirwINSJ_!P(z8X|80r;trDZo$YvUmPe56~N*V7}HN7l` zUbJiFQ3s!dfm&=5g!m1pD2!1O-JKPJcN0a2?d;iL6=5p90XQYcAZI!V9BvPRgvII= zWVx{*aQ%P2W9=~sEz*<6$Ha^)DE+C zm#>U`NgC@|U)x7%!fC|bQJSw-Fsaw?)Kw+OUnVmHjbnB*a9TIrTV@F`=E$%dDJoE{ zNHOPT@UOs6VaxZVAY)PTUsB>f>;z*ISlRduY1A6QU9eATGOKj5!%ZL9;a7P+P4oXu zhQz9+kmfozzo;Lh`0P4(oZbabsc?{gTtRZ;^mW2kS?P?m-mmCgUm2CoWTw8v>Cs;? zS0SUm)`78mC2JotUs5$NFlJ#(0K^R^uLEPJpG_u$FQLQ_~`{8sIac%$yfJ|br?mbEn9!Zyl#plAg(29qyxaq993=Nu)WqY^=ggyWgg5_M&Y zpdmD4((h4i*n9jYW9dMOmd~&%XK$OXUQ@bM*2V_;Erb~neJY5aoK)H1r@w}B5jB_~LP z2GvBz@Gwye!c#g`n=Ob@$5oF-2yJ2=AEdmT4d;TyC9{qB$;>+bA$=O^jVu&HK4E_b zWIKwTm7;yh4(lJs-b$e-^uex8 z_YNtpTlEe_{|I}9wEOK#Uk`1z=?18z#e^6*kkn=swo*x(4YhC;wXpuQ?+@x&e6FkI z8K=b5&i4oHt`OV^Qc7$M*n^!!;^NY>CiIo+4e=k6IRnWQ{b0wsmK&RX%S`$|=X#ookhCNZGc? zMGp@>=Fr1Wk03o((_?+&r6#oIX6-0LNq?%hiiHo%0Lbwe>-T3`g2EIsFYSshpOGWKvb0B0J;;R3Pr9Ne=4_JFJCASN1ch-~a<)#uLsJH92a?)!t@ ziGq7585s9aau52IEp^!s7afJ`bq(Jt%A&4Fp#vW95D%=z4hro*uT^HX!3zQ!R7%dI z%{YlkWf*Ybj#f5>UUqM5dusBp-*XyMDxo5XAHRVjECJKc!11LP6L%wU4tUl+zKk7) z-tcbWELAvkSWx|4Lu$xv}(&QQafl&5^VedHR?41qOhCL(SzYfG{apR7rXi zehd6DB<&$TH((+Lff_Licu&>&&Z=;Xa&GeQ02a#831Q&@0{)cwt77%-W*x#g6dew3 zZ&xR^NH?~t(2;R}5E$jTfD_!&veX^B!!|{mD)!dLfiakI7!4&)nwbF?Q56J6xBCB<2Ts%>w%swm z5p;*KBsC>VeZc1WcEMA_>6oUa+}=pE|FnRHTlYl^yFJg$z<7}J3wq`~P0uM$(zEyp zdX_zo=h_{4hs7)BMe&;QsCcD6EMAxH6tAmx;PvNY z?pKA-Fd&Lp!bN`fM?ZqJfYZweK*9>n#u>pxsO*bYa7Ws&dJ+>Tb%xFz>O`IAsLm=O zQ2QL1+O_W+C!P+B$?f~bQkVu*9G$TNH?NtfET{|e3vWV$wJOgaW^Kk+2kj|ub+&!r z%5F<+b^ZM3KYxLSLd)A|w*O+oYkHMGSoBW;P+hf!CE(DpM0 z5b}`~H#WHA9D{t&+~_d#B52-Al#k5v7eFU(YjZ4}1Rw7A4d+_op8>QZP6-}Zt*%b& z`Wy+$bBC4Z?7qXBCKR>#gNcW8=zG+2J1;>KfMPkenBcs6613dtOvDF}1+@iHGXVyL zyW9I-&s!VRgnTfUyT5WT@?XTEPx7$YC8f{O>dh`&23to zF~!xgBb|y(j-~lg9wm7w2?aIp$RKhh<&KyLNYvB=$&f|G&iHAR^HX5#J#vKzvqvZ; z5zD1q_M?eAJ^F=7o19IHb5YANYaSx^JC#C#K4-ABlVk?97?-pKri`J`C^lj@Tbt2mo!F*JPJ?y@BF^sVe{vm+d zqdEL61~0Kn00=xne8s}G?|LjIF2RCpJ-QOp0mYg#shJ`Ey|aMdO+dz?2ouoA2GDf? z9U76r98&W8OgoJV_Ce35rr%IF@VKibjibJerNfk0;jX6-4r)_7(zBJ1RbB^Yju~&e}L^~@^yQUlTv1@ zBA9`54bp31Vp;A`Vs+FFo;0-R!Oux1PR36uu}UPq&R(Gd?_QH z-I&v|IKQB|xp^Xe=(awPG&MqF<&%bKZr+(s-#&t279BQ>_IM%5!-)So5yF^4AhqV( zL(&Wq!DjXrC3Eh!|EY z7vSS$K1aFuPf!CESr0vX5x~160L22pe2&WF2S?JMN02hMS{W-)vY$P42(hb(MT7jG z0Kgu46=5+oFX{|(T_hbv62&x8SSw;YiXi4Zi37hwjAfQJW6M;XSo$borC~ii8Pgl{ z23`)Za5%9Q4#YA!CT!oYBo>+6HO(c(p3ZS!CvGTNzSBX%-rEqrFFu3 z0Co?&&;<_o%rvUkg%%s5cxToQ5N>rh48y<;K;Ii;b9{a3 ztU9BFw-Hxj#G4%AwBo~BI7~y{qtquD^1>whtP>}mT4}6p>h;5OwHsqC9ZqIF)>vD) z9`m%V7;6i79wo0|ml|-tf?lQpw*fhjoj*v*f!0om%5|)ayzKeCsC3kNR>)f$KpTZ# z(oS2Gu8>(A12ijc0u{}-(1z)|n~*@Jn~B)-r;p}a=23i*SyMmcD|z_=^+VW1hTN%f z(vZ(5bO4ecS%Xg)sAi!w$^tEC9))hiq5*bPOw_*ztWpE_|GlaQ{!Z2H$A+rj`9D={ z=EZ=LI3$p&*UY0PvmQ`%vRUl96ePQckb_@ts@ZwX1kkaveV8H>K#_cc^bsVyzH^9H z=5C@AQ7jit-+@eej-XrjZy-qM+$X4WAH<%?*C+=za1i?FCX6GUl`D33`!UI0WNdYV zc!d@**%TtCdBS*zs2`zLnixwFCz2Rj*LOTbOR4gXhi*l@yt6VwDin(KJ|WcL2{ELQ z01xS2_@d%yBd;a^VFhp+mFvhrvzs^vVRPd;PL|GLdruy6@N~4G9q0j96kkkAf_QJX z2+%UYGU1xVL=^aR|05&-o+3oyB@x=T#j51j9Ez_8cDG*jM$lQ1uh>l_uohmV!0kO(LP#4N@EEUEoXInA56`O0t{sKJlZJrhT*oyhB*gICN!iv3O#j32> zek-=3jJlF4`2{6_TwNHotTB0O1lr;fG+}riY+8d}9p6U4L%mdI_0qplMx>#0CAM`P z^3JT|XEDzY`-GsY?(L>fDo!{8YcSNAFr^I_G8MT({BkOn2e5fU5+J&7BR1$EhzL7* z)C!{q|C&MXejRWO7HlQ95-6}@;>JkpheGE@o~8F5C;HEPEAq66kR&1Ugosejns4c4 z1cAIHP*Ykbt&Ao)n-mt{*6AhKP?jY%94~Hblx12JK-Y@>_8|Ya z@ic!yo#WtT9ZhQv^f%X^?+AQJXI8yOn(O;J0_UZLCI zvK2;A{g4N$!BrACM+=}HS^&Y8>{gx+49pBTn;Or7&0)~d?^^%W(6Xq8yvIX)Ll=!e z*wS={pMFrA$mhcL+bNOhSZs5^_4yh!1ui~0e3JMy1D}!~Vl@W`hY4^|f7+$QzK1ln zMAo|oja+PzpfJ7bbNw(p+ns=bCHrT>9ey@n*N$Ez=Xur1SBo$?&gYQTNOpk^Xaw}_ zR6l~)D4|tHof2!J(sAHyexk~T(_~BXi~4W&UBF?rtyAjg)El2yL=?b=>p-$vKkPxR zwAFGyjIrd9F_|1PCa^X*UbAC3yDeO=Q^&Sbr?DL#6@K`&wKcp2YIo*AFcyszm!j5| zYPnfXPJl+OgQ-YV_ZoaNtm<&qO3g~q3GRleK3%mOhj1-}V-2>KW!mcyelxy;ubQEC z)hx0P>gL3T&+t(6O=xD+&fle0>-{z*HrGlxLJ6P* z6xe^eG3%&($pfjV<2y?PZeXVz>$Lmt-X}S6iyKo8lmZ5udmZUzmo0=mihCbW!DW$U zC?|3ujnvSR;S!V~*Z7@Q8ITD0$oqlgyp1Ix{w_Jpf9A7yMC~ukowZPk+<`)h4#N-~ zx`B|O;c=|D*FvM(Dgs8t-bfH|@N`=*_|`ds>J=6Y_VcmpvIB$y(5+twa-`bh^4O%v zERS{8j64{(^7QTCPawj{E9(rUYit}h7g@Mp(B+rD%YhBM7<1yhjko^ zmY)OsH;9v_@%1SW(nOfOU-XAWxkK-FG;FHl#i#~n`^z0+U;l=xeZq~Ye?uDUw0FXS zq=3~1_=XRtBH%J1u?Slf4StbYpGsA)ZM%?$#y!g4gc&=$hmLyDlC={t181roA^xKH zK*znnonf-!iY8+`hF#XfJ0bma#_17&frO%jJp_&EKzcMEXZ^8tMkn$yLF%Dl`Yw>4 z?>r1>nzNv;ej>%FDeTauQzHP|`F8+mk%?fR2YJXB3A>$Dv}_6O>pJI`4$z|xdtn_L z6oykV;-p@u!#CLQh0w8~eVm}^@jpS;!SMOKAImQEat9glJ8{GzLpNtNa1>+tdtj3z zb%M&K;`9!1SUAt#w!K80p86b@7Gy)H)|OV~D-R!J2Zb++b^AohUj#H{RrBnJmFE|_ zYeUNO-_7tI$E`+ke!O?%WY*}!{;KbMLl#>m+u!kBXc%*o-a5Rq4TZF7J( zuYC{P;2|#eZ$@ns1XCPM;#jMHR0+Iqo+R;gfNhVIEl0M?$&$E-bVmD-o(%ETU_qK5 zT9z0VTCrP2XVN;7yg+nn}yeXlfp_N`W@{h;sg2D!9UbKq>XwL38e zq{ncRI$BE>X#GOE<|NlX;M7fa82thi>H7$PRKC9C24uAi5c_&!R{iJ)Q_ zaOio=e%|+XW8t@sIN8<}`Wl?tU}fU-6#9IV{SQFMcVf#QS^WTZz_zX_`#$!*w5-m` zH6-xKm1R4J;@c^{qzuMH>wApi^UHoT6pvH<>axU8{6UIOE&IVx{2_|xmi>_8nJB*n zadYDu>~fw68(Y`FEdh`-aY0k5DhzSZlrYqH+z^mR0xLDTKk@=9OZhIIN2I@h;?I4VwyW0G+f1n&T$xSJly z)#j!Z>;$g|Bg4t3LuMJtJ6XHV6?LA@Gt{CgEVf(T88SN!jZ-e9VBAUm#{oibH$9RQ z4p5tS(<3?N0JVBIJyKhjK|TR(Falj++}F_91H2Y(BM>`j-*@0pxZq2!_fd z?y@N3(^ z%P&G^^+@ezF-7zQ!m|l?sHj(CaaV|o+_Jn!u--yr&%?AHVFkK)fvVRhFEUM$v!Pjt!3mawm z$cOr0u}Y{--h>0H$iPmPH_a~#tJg+twfrpT3RoIRmxOAAyzy!<5uD&a$ss{`>32d< zFhttVlHvaaQ((lOBmugVkdySwv9Nm*6o6ntcZQ)%Aof&0-zuOeDA7Fov^5QaM?$T) zHDqM6KVt{HldRJaBw5WOT@a8R#&`%%)BG8l3pXwW2L5XXF21XzDf>J#6V3{9OGa}V ze3hInQ%(rcr%lZo5J{5?QF>~1I}h!B`QF5u~Rs2ipwChpEX_Z;6|?t zS=vuglB44$6TCJcp=C;}8)#79sg8MBT1I8^?2_b%;sY6R>Fg;G#63WSpv$!3ShV*@ zGOco9)BF|cdBXNG>;YmXNOw+PuhiC5G6Ta+Pcp~b3eTUw0Nvgf7&z7qU(Rtii^|hh z+=K=l(Y~OzfCbd00!JAr+&V8yU4-lV%5dg32;iCgT~aG(WKK&4nrAi6#7b?brO6!r zd36tj-g!*n>Ku>RA*;8K@h7Y zXIh3Wy??VdCYrWv4}HK5RiXqes^Z%LMDA8rR&n*l%Sd9KYfGo8xqkmz7~juZuRpWm zXHXlQLW(+TkM;Y5b-30gaL#-SE+?SMHSnB!6a5C_AU3@g%m04N%g+IdY#Zd^Il#kc zJNa;7VgM`BFHjt7Pp*J_y$X}Q_Mn;fG$r-;&ML76&=B|Mj3IB23-stM>hK3q7yl4) z3c&~3PMC6^L=NGYg!)2t{NIa&T&F&eW9ZP*o&*eo19&q+r=wu++=r}t$W0CCrI8Bt z?;&^5lp@9Mtk@yd@97tUQ(O1al8^lV4HFH{2Y0GD@pd(<@8}+KbV#noom6OT-m8SZ zHsICz&Ah`1dwVQ1AiWQXI3})uYbChAId7oH+XLUP%mcTfl2|s9s?}qu+GD(o?7bga`z(b7AVKfwQ9bd&7(*ohyh+`4}Ub+Og zv~|&8Yi1q(z`|cSP+@cEU4GcPtrj1);c|rZ&7h1mZVgY->F%t)Hmt1SgWY1&+h`wk ziIt#zPP^Pv%D*f1Vm5JwRO$jLT-;(^AH~_i0pz?cc3Lg`8R!Yedb}i4O-sI(SZGo$ zMQ!bgg@ePPuZBYdsgTgG=p#sh=EN=;YjpX}YHr_!jV{m#ESP4%jjCI$Fh$&sGdARG zV{Y3xncoc?+o-#V&cN^r^5AYFTt<{n8}c7wSq7U?=`yzxe;l~sE+qF0w9H+L-P`LS zyb5Z{uB#34r~ixcI=Kr)c1o~lY7N}$NT3DGrK4abA)Kgo*3{O8qP9e}yQbEtcfuZK=8>=> zqZ=+=N_-_{sg~iAwcoHMUl`H~|DeR_&;rTZH|c#rd1w{h)U0FwDVo)N8{&f24QDbFm0TU4)q%80Ig4cVPW_N8w!k%Rwl;KX1G`F?VBP#ecb2HVzT!58yi4SA`b?HokcpJnUbfZl{PF zk>oRLejvmQH=%*0+DR7r7CLCtbRWUtdQMc0GX~zneB53WmY7JsxgPxBf|Zod2bsaC z^#TUXFw*vsD8s3eZn3<={BD8y-F)-Avv^(#5HmvD4qVGVp>f@NoD6p6G0b_;>7TGK zSQ~alR?VS_5WXJ4chmd`;}eKP*Ud!gqJH>H{=^E&IvG)+-cV%M^_&01SS0H0MKv$grs5Or# ze{;CeD&O0U=GE4*vNezey^K^nxg<}=whvsAzk~U#Wx3i9o(+e0lk$hTOUuO;4{qj4 zl2>04XBKhf3p<6i#H3_&!u-@$Y5C=joC$cF{3W!jqt2D3>B5^fj~M$Vm|SQkqX41q z2T%b2Y3>2D36oLt^mS3MHXxT;nz5fClr6_(g z&5ZNmC;~14*6HL!T?_*!%vVHtjCz-|@_{NWfYVq9UHf&K-&hC=^N&yg7CXr8M9E-I zy78zABU=W%n&G@W?8Qu0LFxuGkGjMv)ARK*Kbna$O|6T+L`^#69$NTe%8totm!w@g zstZths1|A@RqXFjEbE6;4?L#pWi+}9BOlnJ@if*Y@t06S%G-H%h(Gyfd?E*y<6uV~ z#6AVi5o+s34s={NLIlf5uA;m&lJFu6NR3z>mHe*2h>?FG+|6B3U|-OciP^-Shp#}#vXgWHA5YNa6U!+q zq};yuH@J$N+-9bU!#^pzU+qcXRI%2RJ6N!&X5ogfS!cW}_M>(lIwZ zfe*Ebf@|4$_;a(+fU&e6F5DR2dJoz(we3sCE&7)WHrk^L?qs(*e7DNlO|*U1q<`tz zFp0fyeZ{_t!7Obi5STtGS&+D;Yxv9K`^c{aAF<4kr-vQzf@8HZTke1_ zmA(3$ai@cpRCwMl!x0N;(N4*zTI>7u4{b*MIVBEz6z)~*XZ8JU7aY+A;K^H8`rhA| z#@@HXm?m-|yYDTeyybfrCsN?||6PagyRzmxAaK6m*)Wm4a^kbTx2CJWcd^}}O(&$T zOD1is$|nkYqPH#_KxLQx{SSvHo)AToTevB1O*7qscSN~{T$U_eed zkFhYIW!is2{v~+Ic>0#e+UgdNtGQYkY->h?AtOhv79Yn zC|3L;L^vY(C8_NL#a`w7Z<;&Q)?kGqzKblWva^D+h~g})^-+JanYz>}7pa3)3H#&j%?M%nM&-lef!)5j zxF+{ot!{W}P%Xn+lGGUvThXOjoAq?c<+5_^5yIE&whQ>kp@q=!7ai>|DzP=9c19f$ z$s>&8F1nuZB+A21Ac`DkZgdS-L#<8zL|-DCxMORp!%Qc{SfvY7W`--&hwRbd0Jad8 zc=lZv7M)4Ey|on+;3sDoV)i>|hh75n`- zH-jEcA%g)`CS%Vo^jhM_(t0R?r8p(9shquB^hR5^6FWQ$^{ReTZ$6`7g^<`efS2LI z`*Ubd|3D8#gO1K7jsQi{X>oV6_6pY4m`A6R=Sku=CoWqz7RrfR5Ri?94t>qPR0wyK z7ypI$rKPgGC^KCCKePnH(pwNhEInLUcsSYH zMK#c96Wcyf*vntjXy@2%131BRv+s+&8T)^0jzv~DGRt=!UY=RF%PA!+PSEVc;+x04jyWuz`9C8z0a zP;et3AKyt09HrxKlTn%hWp|r{ZIg}rF;RCFy>6=>AcKtZ{igs;$2D+d$8_A5SbQzE zWQCGl#p=%`3N9G+E+|OKU+*%)vT>_}G|H_qp1!cG)wL|ngccc3S|rnlI+%#ZR zT-V<{52V9tuLLh8L3{Ji5gV__imv8s%5AodpfBay=|iYK@SFKaA)n! z`gu>Nt}$DG-8}J`UfpjdbHH}`%ci&Y#3wXN=Lo&`4(0{54(6M=w14Jc_S@PRz1T~Rl^A0wq2=ksVQv3&T--P-z znVBn^D-8S%Dw>y7pTWRCJv%uY(qn<`5JRE`J$=%kf*e{lfB-uER!3^0(2sg#_74u@ zeg`UK|3HdCiDBCf3TcQlZ;=fE)DVDCBd73MX>n%uU>mry8C=>pv#Bv#(y|5XL25qF z^05&n9mv|!TtSltfaHuYXx0NX=SsY2p}M3?Oo~o?mUROZ8H~u;#u#JqSQ2{ZLaoPs zjN}?g*Fmh$vE0P{He)`F%a{13&^QZnW3DA83tFarDJ79wHRQxiju9p&yOE5s7iX5S zPAT9u2VnQ0f2q4R-q|na&DrhAn{dUUuHF#hhY!*=#Yui>7P*An_97irPU5O2oo*Uy zOh-vz=E?#LyJLd@1MDHwJ>lqR{3b&uuKRc$ zRa&(RM0m(TfwmKzbj_mbq{47k@OqTc9^%A+hT{dTmTLg5;Yh9^SeHWDVf^ zPG5p0ObJX>BS$}QtpRL@Mtm;(zl^;l;yDM;Qq3i-!QHSe;4YHOc?FQc!u3kLQijC| zsD%F~sDR}K4dDj>ip4gzraN(+OJc5dkxPd4`v&&TmSu%$r;c7Q_Rd1_&ATqgv*|(_ z?NHdXIT(ccj?t#VW&9LM1V(fCO9+gvYLQh{cRA|8$m z-~lI6RXK*E5J9AvdGFyn+a;(a3c&7Xd>(S*x&q~)n?QFXUV&&!oZ5%W|Ki_-47X%6 z(Q0oier1I=N8(f&F4phVH{(93yq4hH=B4MFtN%i`>qOJ&mZjva%7L~Zf16w=u@t|N zC8*A#SM1f;Df0UcD-S(|f&m-%BOMFxd07fk6SCe7GO?X$W$1$etD()gv9Vi~;F zCn%}JBUFzlG%bavdIc_e2^!)%?=Kt;>=SrU%PeegG`3XKr#yK6E3D-&$9I<7GTy?n z`3_|+%QY&LlI~o5@E#!+04sw(UjlbAOA19tfaBt{6O-buYH*haS#ZIU;3SqHLg-Hs zuSrFMHxltGM10k*4W;Z6`f7@B}+rAq7FL4k^cPF$PXBT7m8RsSpzmmpDjw z(ki70#|jhi*+>t9d8k}VN=CZ*CV?+O*aWS7?aGcDMH*FIBw7N4g!15Gl-=#Y7fUc8 z@=E*|8dge8sz&-qlL!y}Da!v>O{!#%h_6;(D$kEwxNxnGW=+sVv(lnD%hwwDe!ni- zoR)g6HC%rGcEK}))V{s{`}Tc9qC{HC`gjazkX!(kNl;e$`2}+?sVj5N5W~RbMG#Yeilh*{Kq7N- z`TBlJleBgEegUIi6-{4RDkK!Ye(|3$(WdsYeuJPfC%GUcy$8s6o4ht97ee3rVQ>{3 z*i>?fSUVT;29du2q~QO6pzaa7^iC!aDH2SyYB^>J-q%+0le@$TI#;BJhU*x>X_1dz zx5<3Im6y*H#lbF0#fZf#2J+6~4Y=t%4*)nya{)$p3vFvi*Ad5XiK~d{2YC_&;{G)_ z^N738ShjLt@wE>91DpC%ke8C8!RXHHy%lqCamNHAt94P%)%{coTzgL^C-6sytKd%{ zXq3?0V#s7l7}AWv0d&MKAn8;p*_K`XXxr1skZRj_e%o+C)TVz&PM8vp$=Ak8g~#pgOEkaztzB*z)dvpU#TW*zC*i%^otfUrgsgxN5v5AXO1A$2ZMX_kg%wV(7t+Gz<}TVG4u+y55@fqQ~6UsY}D@M)fS$(ouQTV5b`>jrzVexEzt|w)aI#N zy*R^HVsFpgJqzGszw-<~`_IG)*zc4z>|D6(fMAI483X=4!x@xnA5Z%tk@9F=du4^mXSwa*9zdvm_ucS4CD1|OA7qubHlHmx|ZnXXEN7wgnS z;0*lz@p~IMQ+O2fS>f%E3)S)CGy@y{NI!rx@H7_Z?IdD!#rd6>sbX_x)DhIFP=QW{8&p4&QuZtn=V zZZ64JWj}sasaHP&)^HcKRrvz$Mw{OVxOWpg+%}ZhFHktf{@9bmBIHp*J5%CknLM~! zDg$THjev(0pF!ntz^E@IzYsSTJS0hu-vSnn7@Eg&KT%>oK*H8?Yd@n8?Q0LdAhvwJ6fe`RYRwH-s~!y=QFLVp5(V+N``2PuwrW)S-D;7ncuuNm@@yQl^5 zq{4{+04@|hEdqVZ!7$Z_Giqz;*Q^}1waE+%5ds8dJ=VAn`)kNLqK&-#SD1*x6dLXh zi>|>AN)PEo(K~LOaHQYF8ty96%N`FY>%bYTCBzzVI`a7f9wl}PErhQVybREN)Ngz~ zK(XBinxh53W5rw$6x7C7i=e;-u05IF-tOm-duy5A-?ga(-DGv@1pdNwP-OsaOTX{T z6jbRHRG||$U!zJtr~(%S^;t9)hal$sQ0PuX&ztZJw0smo9EP4mYn}Lg zE^>m6i=>XkJzX#^h#3U`@gu{ROkxZINommdMu`JO2f|PrvQbQc$+@G%oE*SJV!9|q$nP8I z6q4UgyoLO71cdzNgDEnF{N|6yuZQHrRF!-bZb3l^*8N6734 zE>CLSUJ?$0JlMN{egkf}CFo+la0=L)c$Q$ zUfysYQH_xMymQ19{rHMwSr7e+IHEIg&za%wfAmLxqx*k|M0C99esJQ&eLrE4S_+%) zUwg>Vbb$Q-w?hbVkqe)I`pk_o&lPVc&k%1HAN&tWck^EH&gY-e`+EMdh#!v9UY=kcH7tsnB68~yxYkyOEVh<6o_iT7f@ zMZAMt74JLvI`Lk{*NFEDzCyfL^E-aqJUeD)>x5{UW_hw!w-dlJ9 z-h{$)P2e(~OR3MrC}3XE}-^0h*?;$R@I?@Z;n!79b&OJ9~sxztK=`_fmWQpQ^;`M&hksT7-)Qs7Hp zlS=su&r1?|-{HaPr;z-S7Q8-#O6UW^C%za^;g}z92r4(tvF!fmr5a zJS;8b)P|e0exUHohGYxhZ`mP@AX0KDZ5H&@jzzaO0|%#HqT8=uV2JGLdyRwY6Rw{P zZfILze29pq3yoW+h-X>*`ylx9UblY0a`M9B*I1homJT+iV-t39e{gq<^GEivs4|2< zxIctH(uR%w)Tfph=Ogy9)$eh8aj!dan?uoa!GU_A&X^QuR$}#!sT!$NiInD|WsypK z@cl@oUX5VR2hjPJdRQURhZNc?IBxwa}Ch{Aa>SxA)w3SZ@#Yhsy4 zP|l_8>llZfjds`wlS(vm=`-E#+XE-j-OE!V~k5Uu8(XsT{F^SjbV5Wo>62o zT<|wAW1Dc?Ktd9tk(*OB#{DS-|bmL}j7PX|FWyW+mHw#8tcSev`A9oJxVHI)r zIzJC}fBtuzsb`lhHyq2B7q(vsO*?GTbSPF)F~!QACEpi5d@MBfo5$}?)3ya#pOeb^ z+wDFs;M#2aFzVB}Ee+c~O(*3$?mBTD{FwqQ1;$A8#-k^weojo|>{!yRpA+kEvH4q7 z>MwSu&baIjt3t*2TVnmKu~LS|yF+cW!eGx;N{A6zzSehtC5^Ypb04q^cm{Y9*a18Q z+y?|QzjnMK^RDB#Ca#Hl0`~-N2W|)MN!*jTow%L2@I~+HYO)IpN3(UXHo2uY>8 z0LRzUv=IOkf7x;r-b;<6pRL-5ePmunw+PJ<3EQM!11~D2E8GcVdpcp@Cm%l6MZUG) zAeYeTH)!c(9!V?GCugianJ9g-g|ZMr0&lyA=VyR6pmDZs%%S=@HvfC7_1;&l_b*XN zOWDF4X9zb&)&27-M#UiQDHLcXkO|BK76Uf} z#lTvCwjM!SkHAgBO~M_5i$(9Rxo{B{{aPX}0;*qg;5u;axG3t6?i;I(wvpa_zz*P- zl6ItTX4`0isJ>9|)HbRgs2gD{zg~S8nQXY9Z@mqK)Iy6ygSF6p0HGslrCqpCm`1G2 z;9Z;(^RWclWeyq46nhzTuGJW9#yt`t)dX4tuLo}cfojU>0>2U&dF`0O*a&!`g`0xV z_4k;kA7(QOzN}0Egl%J6RIw(gU$yQ}!0lkN%H_SXAtlK|yb2Nn4zyTm#DsuFp&Ma7 zD86p=D&kt?qCiXFwf2KdgFYlWA0Z&oE$t3yk?7jCs|_Kz@3TpCaH_7c61cce0^hR| zfE^y#9lXh7R=MOj)kDYw_3Jrdm_JacpQ{0d!b{qMmzevB9VT=h;!((XN0kPz2uUxI znxI8Eu%ykLM9zxn_0N)pg_>Bl_LQ`Z`7HfVfMfuoFEsK%|J+1JYkHCh$OH%TVsAA&K4fHf7Uk66I`ltZsj&7R0VDxhlW0=Fkw-#@dXy@ zu!@b7A95+hI%W^S*JI9mhC12D9vA;dB$?1_9`icO^Puv)C+vBd<@uEIyf5rI5YK`~ z9^#E!3@LfgO5S6Bgp7W{BM;)gUH*W%EJztC!Sp#EGnYuAsq%&%{n?U&=mI&VUx|R@ z1a*oS)|At^uneK~6R^KLq1Q>g-zjw58~y8YXd<^3OxZ5wBHd(iksOFkOUX!ORB!u+=f$A>*d;LXqo()}ik#PvqOcQxo7xa^` z@U5Mxjg)?i`Azae-;PKbp!Cpg?s<&Vxbtd;>g7S8Gt!{6CPg@Gm!dqdbrnApUK0RyqDO0h8WWLVO``+2=Y<3G|DjLB=$9ia`_xPL_ArhHO^tYf=jil8$%&$eMWkI zi4vc`?|vp2)R?@>G_6q1mZ(4el)V47>MBBZ*W`WXWm}cJzboLGuqfaeyGU%~LYr}X zO59&AF>v!?iHD2!50OdOri9fKdp%8iV} z+*$}E{;UCe_Hu1u!_T<4aItl7A@gSrbFQo>^01tT;L}p!%(riK?L1{NizEOZ!g>MFyY+=aimhXD~B5Pl#LWVaj*8TN+T5|=FWEG;N3xQQDI zp@R`>{}80hh1PPy9JfV?0WL60S@XFHgl;qAN^|vty=6Q;f{xDws;%i1O)wTw7-IVo z7Oj+;A$lT+eC&q({2jXq%NZwf8%HrWFxKvW_Qw=GX5+;|faYRmnZsj>B|O3~3NX%n z_ddS!0S!0TV{e-=9M^d1oM3D1$5$Es{5eUnLBt*=8a6zktU`~x^G5O%`pcH<)x%il zT`4@k75PH#$H`DPvxY#6hn&+GKXV<{Jf_V9jV=?aCN2TCS58VA02|^dqCPIZ-x?;7#1{bN-}o zi0uuSK2r4nwDHiU9o!Ay5o65qx5euH>!5ZZySBDJwVVjmf6aLFMYs^BvXWw2H3q!~ z(;%lS6m;T)pvO`cGg}L5FC9yR#x_hBf8BPvu&Y-G!c+(*MZzTa`h*7T?%V$yJG&R< zlsGYzZp4?Y8_s}3d(e-V;|z>mx-JBb`a7IgHZbhZcV4;YyWqYN+&KEYvg11nH-1#U zgCkE6_Zj?-0}fug&mf<5UXj$nXS>6m`@EvcaNhGuIE?^Ftplon5?}?e6z~Aq066a7 z;k+W51wvBk9|O+-FN#kDC;q>7UP*pP@>S=Rw(p(yyfTGPa-t#dwoIN&fNenJjB(EM ziiG}r=M|N1B&}|&{TYjGTJnR>t)#{$@V%5uk7VPX)tx)}9i~;_$vBro~X_@fGK`p*c(6Shm z_ccfy4kG%9JhMigIdnL{Oju?TtP=+pgkUA)nQwrAeEPsq(87sB6bdBfn??76cEAp| zFgA55t4gq}O8mn|j^XANy!bhC48jd_s9~TBmfYvWp%H)+$2)KWtZ>$eqk?x*}%En;RExS~IXSp9J;Iv|J~YrNURrg*tQC773oWE%2dA{FNFz}RpRg_uvaG0X<4 z)KO#ha9-1rjzt~`h)KCbm8#yvWnIKul`Kc%2BF2HVwY^#;84=0h8L9xUmS)sI5efu zrMsq&67AV?*ESC6u?BQ53x=+at{vtpUy=Tn>%hjPRv@fb>>NZei@|TH*Pe_fyaRH> z+qn}v>wgrKRZayp#0=C6%HTf}vvC}PLL1zZe+v)J`OV#n=)i?}W&PEaUEz{$-9>27 zp&VDLisExmUlyYe57bJ0b^X`NPKqF`ALem;0ng^WuokSF$I*omA&wcc<->L*C)w^$ z#@105(>pikRtXe*PBn`NCWH?v<}230wAUWEut~0FW8dub!7=*+d&g-odQ$iK5(3Qy z_h7xtK6cMla=P5A1>046G*w|;{F2`5r2AUC14SawNdSxguK5Tff1wp(ReX7WYCr5Ogjhy&`?wYGR z=ANe%{=|N?Z*Zu2VNWTB^VlE?Ocdog(hMR#lw^kPwpNPcxZNv7g4Sid) z6wVlH{)&i*#y*M@7L64NAM;8{S4rUpV*{F;2Dw!$>r^WrA`-cQ)8U#`$0fv znZuaInX8j&uMF()eo2pcLnnx>(zYf-IaoN1od1%^SY&iYDsf*+$~R27Y08`qCv9kw zOjU%BzDgnXV4bl>PIk|Hi{z}OM`r1#lo2###z@=|#HAWZB~MBt)U+%SQ46WK zB&rYRMQY-2Nega9LlI`8$l&K}0|k3jgm`SaHx-?&M0K8 zpVK~(`KfGoUd_k~D_z%%ni5q-x@~s`2G{LYmD*i>aUc7g{$0pyv;}|H{B9h!nN)WL zUiKfmwE0-SaEG;II_xp|W(#Pq)Xsjc&7=7)dXaWM%_h<lRvOXO z85-I}-KDi;2ThPg+FW5{1GBi~x37s}lTPVLNDgi}h!h;*XoQB5g8>Z+<530+()tZK zFJd{Zq2?7VEIGFRYp3 zk*$D3t&n7nnB$*kl5`ZzPCdQxrn<9=cb(gmIV~)raJ6}nWV089VtQEacB93s}thilfElNyKiX5FB zh20b=d=UdqBPF8|xe|g0#4%;}rNMjB4)Fa%gu-8S<#aM?jA+JXZZks&=UkaMtsY8^M%zQqUB);D>DSY`Fu^Sbnz z9EH?R_5+6qyE$#m!}kwpE@*%Aj0mNMed8m(d-3J$gc?6^mj*7%!t#ONljFiJRIp#u zw`n$PCsp?OyU0~523dloHJmcFbU zP~8$~Hm(%6$A0)&fb!Z@qM~U}s(4aSiKMN|60DmM&JR=xyNS9Y5{cTQLKM`#N~?$Q zo0C4SFd!5($($SLEhu>i$`o5mG-d%t7uwW*Kd}{0RewR9?YS|sW`dc}C;Hbv9UcDh ziZCuU5_E%s?J)f;3)E6_$qeH*!BiRx(LTW&J?5NP%1SGDICsWdK2z~QIB`xW$E7>K z;_T?p{nv?5AA`?EQ&$y+s*d;QL_}$vSwe}zd#92F?PyRHRFw)|o?;~GN9$@_QpL50 zmld|RlMRz5f)(wwup+itb$P<(DYKQ(5NRdz6g_+d$jKvuobFKwFjsu#0fOAh6Kav3!dXq z?80KUg~bXBPJ0m=Vx*8_SeLKkt19#q93Pg=6hqVamD`4n}uFnm#d z-PMxyNw@NAd()E6GTWks!eGk_RjC4-b#F+Uj1@sg>J}2h;?As2y}xs3&Y9*m$AIQu z%CF^|W3A_kzLm?mJYc_`1BZ|K{dD@z{%NOMXcprWjyJ~Zm&45;17{F6_KbIZ{bu}e zZEWm2Gg^7t!&A$QHqPbkF~*_E`)9Q2{lOhWAz$q2Hv-K!375J1@D*NnHdIKnx(>RWaAK)m75saoPQOP!}E< ze1oA{77AS_p%^*SP=cQ4F^^FR8A&yRA*$-stIIql@yG$)hLVY~J-k8+UUo_X?2-UM z371>VH8VBt}wcFL?3AnC^RvY2N?V43;m0q+?)mX(uQ zq0UY|3&z$*Xj!~joxy-y8^^P}1W>JPEimlCNvW@I9L4Elk$Dq-frAANOOk>YK&1}V zyv^VeArC9o6YOa ztq(}POI+yjj9uDpkXY(L=UuCDxd^z?US;MKty& zqGQGZ=N%wsAuIB+;7gXkrXY{5TxbhO8@?u2qF;d{xFy6G{I!TRZ+&ZHnkB3Jp~xyD zt~uP1+KQa@_)|34UWyzgXZ`3-1_)l!IBlC{*+^9KIJfK|Swu41)K-aUUX`gVK zj-MbS2)iEdE)9a7U)gwlRQ}V#`Cnu{{t@|iL4fAIVq0 zSiD|Q1yX!hHJmt9k~u!L34tz=Iv!Bbg~%oQ*tDag5`PK7=eUZUS9p}s(3~%va&`GH@`wk7UTQ#F4tl7D>yozE_0YEh!wNxgDVXT z^lP-oqmXtastbojFsL^IEfeDeUu*7+J$*!Qsh)S%Q^CX+qM#iF>Sf01?38#!8=LKE z{uIqPotIW-_m~Bn)v%J~8DuZ1tiSmtofaH~-8AOB(pWEA+eHby5gd&=z^}3FcG=(Id)dkFi2JZ*0m)g_4diCv&o6S-8O*OjcG)lN*C_|DKe> zPUqJ9SW6KAxSHWn5Kcn>eM6EJ-?)%Z7=huFBnRnrPXof{k`og8l=P{IV&b^VyoD|m z-KGT_7GW-We$$j+A=;cs!xfMT>ZV1t5G~P=q!3VqaOJgQPSccUuom4x2BMF(tjvz2 zf+TKk!b_0IJ^GU1d{xf38J4LZ*TkOwL(`mC)S}%vjX1L;p3^S`7*Cl!95*8p*SX~a zK8Oz2#Ag}?i^>ipZHB2zN*k?1rwGJWr9UgJAPqSn#-g-1&3$uTp7|uwx8k2~e(-8| zjOha{LEEVit?4$=cF;Pp#g=t~yHuy&7{34Xp)vawvNKLlJEP(B=bXgCWlaP(%s0=F zg*1uI$-c`BN`@FXpiQ$*wwKU`;wzKQ@?{&$m4=l;${>=7EF$sgij8i%C|{sscAoiz zCwZ{SeHl{%nV_`31>ORATngM8mTc+X_hl7PSLVJ^ta6nbg~kN)I2DYZ@a0y8qvt3E z(GfB`Dbz_0IEfzfF1o0o05xVi51q=qcBEauB(2dke2I4vFvme2^slp8n#QjKhFSgw`}{Rtuy`-1-Rmi_v|u&`}#z>)mGp5{Ng z@&+6UB>Xyb_UuLkUQbVc0qM*${trU_j?meh>y_ZW%a&VZz8-;Dihlhk zmctry)1J_{gP^dEB9 zbgEKdd%5{4AsUj*U*LobqX^v@l7L#!+7}W_G4Jv}Magf>wu>%_A?96HDh7^~U9ha~ zFZAc8wI1j)Tuw_`c9Ao9xU*#o~1#2$fy~hb z7ztQga~5kD9qc(0cw7QlgM=I}A%{uGA(4=TV)Kwt;}f_zV{%Gzc>?jFDg8o2uT)Eu zbIVs`dx28+g7eNQ9=Z4K{OYaZ7axNjI_?0U(rTSsL~kVdf_q;?z6`5@+={GCNigDS z9jKw%ROkZ%zM_bzwPMM@T4? zpg-GU8yJXh%n70CCN4NGweY0TPknd@d&?n?V)W6GSER#T%G*x(49X+gK{n4};01>U z;;q`JNga^`YK)=m+{({7DIGu^om-`bf;kJ7;l{=RTlTN(m(hL)FB}B0bjwk*)4u6K zGWQL-(YbR#TJ5uKkd!ptY`oC9^MLbL4f4t7EMbB`R_1o$S?AUO1Az8v_gik@;>r8D zjrPrE+b$Ann0HZfu!T`Eh*7c1|JlO=CNn9yoKHJe`Oh#iUgw>sfx2^5!+?y8G*}?6 z_NOEe7QdR$V!2~fQ+BLMb)bJ2w^Uta35sVg!)OcP{8=ufj?_RwBTMIb2g*%qpe%_D zlnJZ+HJu6izo0T?RfA0iOQ#GLc{szvxIlbMX20nQx@(%G7g<#wxK9KNUw~JOGJa; z`4oF7p>eKfv|6V0K4b9dW-TpVGvZRR+H`wuPN-Hau-PW=d5%f_#k@9=3S)C-4ChR7p z^M{nV#Lmohz!!j#fXi>D8QW88Iu)kh5gZj>&Vxh4tA8+&2dS1^qwZi%Jx9XWe|uJl z2C2=;l>MeuJ(>OgO4v%5&JrRFhh1XK(pci1Thr*n)~pkFYr(5|Af6T+&jVkz;K*50 za@{#gL!*hlB6YWOtJ8`gnUY^CYavftTQN{K&;h;<-kX!eG8oSn34`Ii3+i%C@?@{e zp}H}eKc@rT@(}8DTmPDqJKT})jv(5DPmrA!e0+yXkGEpE%twyVxcx*v_o;+ zj6SZ;+bN@2q7#d_=ZH8ZFzwSKNYl&3-*^SK!zr=?8iA}P5C{!_6uMu z>r%`F28JjbfdyC%C}10`-5(>`Vn6kr&rO-JV{6^D^*Nu^dOyjo&q0H7Em@svX50TM zBZC%-)o(A0<g9vVZ z{UbHk*={a@gmH<%S=hXvoobr-5CezT7;c&ouct1DHajH58i8tvh((V#~ACbJv(=lGD=vyeyU=ORe5lh28~WP4z*#s_HE3Q}BM8M~WU^k|;Ko%bPN1fzwP=H$50VDt;~T zZJjAKCpNvsAQzoIVY3-B9b}NljBRvWn{&4I*rsHm9G)|TV5@MtUAvCO*S@_e;Xpk? zW1kqKnE?(2yNJ}+AP33XYaQ-DjkTl%URHx?gIZM9bWh^&vQmaIb7&mz%1Q&t6CnXv zvM7BI7WVDcY7U<}ANN`6{PLSLYx{j46K-1IrKoBu#Y7GEL16{B+`URV18z`Bin5yu zcd$*kd?H~6t})W=&lhW}wl@B|%cZ*&3ChQw%~oBOW^LB8Wi}xm)W9N12xL4We7g%| zDAgQIJ*&?&pCx|7^dO3_Qj9hoIq{=N9AzCB5w4u$y@XgWIcTq?Hi#~K=PjzUhhXLa zieqi+3l|D27#8qI(@UDFbXGylf4{A}j5i1a`1fF9g7T@gM&TCb2DU({2Atd@YU!sY z(EiOO>@84LxMNf!ya%JxG;pD+VmqRn-8Dq1MTAU;>YI}5{bFXWZooNo>R1u454oWxAviCN5S+ge9!p*~nCs4tt5Z_aw3 zUK9hH9~#y9=G+J5jk~Kti~4sN2x6f~mBhJ4W^suQ=Nh8UZF{8LqW3?HzWf9-Bvq!K zd_B_K=j+|p*QT|xNOA-dAlBJaThMRb!B!k9o0Mmkh`k2EhOT6wazPNGPy1H++{A5 zL^^FXodxC^4ranbMx##W#M8D8u!s|vieB!Mp=7G&>zm3>D;0{}X%>P$s#-Yxt54eN zYEHHhvu1B_l<6i_s==KPhI0eEWv40heyc9>RxXWQ<0wcGd$`gBH{l`5L!iBM4-L4` zsL~Ff??Jbqrdokmiu0%py6FY|g#aZ7% z!)!tn!gohXnZXk5o;iXw&YO+}HKnba?BjwJ)QdmAXri*(wdfLrIGi zVFf75tu}tV%dFEx3vE<+~hpHUppdnPU9AUdD@*%~N+pf$wDXN9d35AqN z0X;L0SW32h`1ugPPsHd#n3gJHv68V0+cdzxPr`#7Z?0xl(=9nvufwsYXb==`ySgkxc2S3+5<85gM*j%_T5~2 zAU0^$7TGri2ljla9bLOssQpH~I^q=WkuDgg?GiogWF0O$h%{@j+8+M2s`t|C zcG1#cLSSGqtXL&^-AzC)AueaJeC7qGEEdC|2s7xejTeE1Yy?-e8;KmnVnEmE^x$;! zJERBQ(2opeX(F(S>`hIn%;+4*DG^L#ken^ zsFBQQR=0^>EanSTn;ftK5L z#X(?L)sS_-`SdQ~;@>JA&+K}U)q9JJFsUClBnPryY|6GbZAiv4c<06xx$Ydsxxq7R zc7=8~dhDlm!*i}5%yJeVjH@5!=j4>tnGS;}#pv8{fJCMjhV&~*Y4UI75aB;-tFZ^p z25n`w<(OPmxx^uT#6tPCx~40(S=MBCG;fhgpooLJIeJ7QjoiH>cuX}6`ly9 z63$^a;>GVZQA2%Hn68du-KX zSRGa3Bn>%jXfb=VEVdzQU!arL$}xq%T6m(NaPP99%VS>q4aQxoU2IAQ;!#3moM5wQ zFkUndFj5fHrGNV2I|dAt;WVYYJmyUGC=Dlr>1vxs#X4xY6AYVQfZ zH@J;W8{%UE{ZvV}i!DkDmtmf`3&vddZ7QV>O_ST==AWew6nqq{pLTC7gHUP_sM&`? zr)h#Rd_eJMw=ZGnA=3?ZF`*I3y4o|d^h@*1B=SQ-_c+!CVpL8|Q?PwwP#P0%W$&{}&bHEhk=%U><{ln2%<%(NFhdFH0)R7dsT zI(t^AJ_=oD4x>miDi|EWX&z360WA`1Zr@l<-Ld|-jSlP}PD?-cY!_4vqJACP_iVNErc=6xh!R zvrzm*aX}7R947zkP3G;{-2w|?%zUi*duj%~Z!b1qY@SqV`^VY#0zq zpK;jOvphOOkp_q$lb_~TDs07nLbQs)z)`yV9$+pg!HyHACUvt^ev0%|7|UvXMfEqC zIJc}OaJbaU7PTmMhkGqrNRbr2l=?@v$M=`1u@zlBh8L2;<47hCMywNdl;YJMnsX{M zb|mstU3y02#Z-#x6kWlkaBvCr+f@VDDEF@ld@zRqt5U06zC`|Bu(sbSTh)-@G@dW= zCG$6F?HBO5BskXjwD90#PotijVI&!nM9}7Z`hcVXCmyaPU;1NA)+#}F0kROd zZoD8;hWwr~SV2`0vQ-hXRS~jP5wcYgvQ-hXKUWc?DlZwMS21h)(;3dKLD0$Qwqg*< zxnTG%E=Om}2PDQV4WaLLGo&M(G={jWmA&p}i3F#}Z_-DY?cN{y^Ajj!Ld^XAn8vKc zPk3vMnI5kTgFiOV+J!78v!L(q!M|`%9C!&h4x9o8fh3LvW&(?W5}*p$3~U1)2A%?1 zfY*TIKo{WZA|8+iECYPNX5eeU1Hj|JuYlKpHsAzs7D)U=(~^MkKr)a9z;KHvf1 zDd0um9iR)i2=dQZ;96iFa5LZo?gZ`w9tU;;Ex-}r1keRs09olWUg#w?c)ws(Pibv`U{;wSF!6__8Rd$10tst=6iwm0G3d)4cqfq!nxB{L{1v zT7_n)=PM*xZ9;`nUT!@KBcPu&p-Z#%)B44_>{(e^aq^p*ta(&m_jJ$Fc!zdfa&o>0 zQjFUz`@7~?QL=)crmd@5$In3sh^!6=j)Q;ls_ht^PA3EWVq$IfxPI}D{s{vT2M%(& z248UDkf9e{oHXo`;Uh+ly3{@TvN2=FjlX=t6a$y26IyKZ{QjMSO4 zzWAlI^y@P+vu4l9o_oWM^K#}d@GM-EyBG_ZOAG$#rke|wEniV|%gSQ!s#{A+%Wf-Q zT~S$eyRTX|)~sE({>xw4P_uE9BI{;VNSAslODlA*k22k;Wifu{^LL&$S-X}N%j9XE zDsQH@ci7qG)w6wGuZElJ)$@wV4fQ-H>N&l1war>+@Cm+?qC!&Rslj zL2j<)Bd=QS-1&2&UbV~xIq7rf_xLQDmOOdNz=ZS)cTrVUdFjd`y_6wSQdI3;UBs{~ z!e7_DtE+SwvgMUU4BZm1JHs8xyS(%kUy*OUyOcWneBPCM`T9u-o^o$dwU>cip%<+r zCNZK?zr5OAZB$iN`uO54TJ2s%;a6AsyrjY7YE^Lw$~Spn!d33{o?;lJos&Cv zUewIdOG>NVMb*{b)wh(dcNZJJ(u!N%6(qGria|w6D@yg!qVm!&tK<_FOL*ppRM<;Q z_btY)yt~&|8oubVPIAxH-2`1-S*^RvOKU#Ktv1SacjYSg%A)de$&8kgGF`Q@ za&?uO;uEf3S?;^Sy~?OqsoGS{@S>hVRaEOfW2H{z`L8}^mY3%gl~$;_OTDj^daLPO zQEA*-;;ybLTFFX5a0WmT(>bcaqTB15KJC?AcdylXixyk$t(Q>f%8HfVNuR$xBp)eT zvgDCLN>aX_42r|wubnR6jS98uFmifAxJ$f6RaR+9=i2K&qmFA!qavz)>xnn*yz#2_ z;?IaTRpM0{jJ7qUKHVrP@97}vNtJ<=i#c(gwqIUZA;a#)xz3cu4_^xUQfN% zddfVguB5w)y=zKWdV9i#+sM1Fih0APAT84~GgUiZquR$H$8ea{47*ajggv2HM!{`; z!=Jxh!jX!L^dgEd(CYH2X{jc?&wIP!t(L;bC|?v_VCX`URaRH7(%pHbs+JiOCw8~TJZsTodD0S?50fTM(q^)E-|AyE zt0-bcHY#qbs9am|Mfxz@gjupik4{Kn6O~{y+!C1|CzV~0(baDx&%#KT-@Q@KO+2g3 z5Px(|bU!05+5NmN>KW!*w?DG^-Ot~MdhS)#gb)Bk#huhV+|#b}@JUvvtawVr>m5R*U8zes%d|M>pb zKGpwjG%Ef-9sx0R-Tx3U{#?IE4~n}vrsrR5%;)=Kdc|G=+r_|I3{o=`5W=h=FSiIGWATesQ2W$PVZt#4=y+}ZTCySCl^^>5ts&3nIf z-~A7K`@!#g_j?a*fB2C{AA9`!JAUxPAN}~BpZLj>KmC`VJ@xaQPe1eQbHDiI^S}D_ zuIAl)_Wq`&b>IF2FTD7#FTH&5&~FdF^6G1^A9>@=w~qeq_kUGk6IwC9E8RK#-14xVpO%wzb#d|4Jn-}6Xj(eJnV55&Iy!6fE7x>C zFW|H!-nrf?j-*zAbmLZ|TGzB2jB=I64dBX>R(h4MRA>@8MZT3KxU;>t_zVuJ^6iGA z3iU`nlD~ zXta3eR92|3xklJ6(j~4&JdN-g;UtX4ca1}Sn8uRN(X?`HuC5L};=iQY>sxS38Rvw# zJ%?nWc<^mrQMI1V8FLLJhbp5=`C0E)GFlEarJ`HC*H^Af*OugFEt-7oq|AAcAIOue zDFFqcJQRx>TJ1xXsW}ZmJJ1}o3XMY>(NwgUG#tN-1@jjySv*#o#Fr{jxOxbuAhpb9pK?62tatqAe$8HI;A z*M0W)UvKXHy>EX$_08Vj`=+0B-)Db6zPY*O}qIFnS_5Aagx&7B5%Fj|K+XxZM>C5F>|~XULQoJ42xox zq5I0S)RYTwi{6wf3ajBWBKHi+p_ ziDnm76qkcZd?cynR2CcM-q{ds=R><8^qX3iQ0_B)kc=S;=CbQT6xXzqvGcq|YrLQG z|4UCQR>Jw3HqoA2?ggi~ES4OkAnC=$5RJiu;$otiDOD0TqjL3XN;I#ug6wBX47Pr# zlU1_Wr)wQjdMjmEKGGUrw89iyo^Y)s6{*4E^;KTv-ZQ=BURtqF1+KF%j!^NsTkwY} ze*@BeMFjcKvh7PMN>mFKXRTWavPJDlTro2)wNsY!ets=>Zgr*?TKcVCpNHy7*S#w_ z2#%siU~uYUv!Qb;CWrR0dbSuEH>;9(q{`ZFV&_T^2!YdEJhuWCm{9UGtvT8sEF|Ke zD{<2^JeoE{T4q63jy$(f8aODW#cIre0cl^fFD|bpfW=ptDQ{tJ%9rH1o8vM|-c%7! zO4~=3{)wpeTCB*hbHQ=GWzVOr)fm!F#m<9{7$y-inx3P~VctXE9!ak#&aEn~usZd| z7|AfJhr*ew3m2n0UE3vje)@wp?>sT`wJrAi(qeB$Ns(`HWsXpcuV1fwwcY1Vhtc|| z>IZAqXj+jy&!Ua17AUYSG`zm`9H%-;Y#{a!bEV=`yv9^2%y&c)H$cjh66wl&(DxRhtEd zUS;SqdhhKODqrg-GcQ-~p7ZO&tDIzty+F9MtE-B9-tOAw_4c9EN2H8V<0!AlS1Jse zbnV8hMf0=faV{t>=g?GPTLgPS($%zAtvJOCR$1@kr7gmpEAtpkL`ts;p)+7_G2o}s zX8-&9|FZ>li2^!);#w4{a5-IJH_Ab&!om zNmFB|{B7`Sfa6oBRs`+F{GJhhXJJ=y7KQzD!!FCSO1}VC z@@5%U>8!?e11z-K2*3wOS*0FQo?1Z4To-mX@cVXLDc_@j z5#wK(q(2=Cz0y z?uEEF;|fkQ7IzqK*E?z2CAfQWhvVLfE4V^2?kL<$+)HuW{w+;&VYjlEwB!#0!o0J0S}N3%mk(bQ-EaPN?-yo7H|V2fFxiD-~ti>JJ9)O`UEfm z3Ezf$1ULxn1%3%U2|Nls1Uv|A12zCvK!1BrpG%)kqCT1Q`JGq%b=VaC$ryH_z)OO!z2Uq0lAnGi8F(51;AS1Uf?O~U+nGoydR|7Ez-Vp(B= z`n?rQQSV)(BIV?J_#uF(@5z23B>s6Uma-|8bMIE~#`s@=DAZ}W5P$pd*Y95dWHH6e zX8H7TBzS<6;dt5w=6Z7?U&E9NGo$Du`fABS@~H3RL)QQQ-~X2wP@;3ZP9^%FH(QCS z-W(;m*z1vJ%Qwk4EBY6nF#AZ++YDbrh@D(ZgZK3-O82fg)0y z4wrw`Y#Fb_O08kmS!*o4R~lPQ{gS0sS(B@e&C%>ebK?B!W8*bXZP(IaLDu~G9EELR zr}>XjgJL_7+tqBFqZmzzG+!4A*(WQ;CcK9HhwBQB#j8Mc>& zVsB})ZG3Z~)uOOD-av>oEBZ!{e5ZVeJf~@E>L2wt=N6^ri!w|Cg*o0Dg8aUXN;Kjv z5ixre)+ntSsIcRaHg)I<#b~HLcClt}4j6Olosl-}OC=WZ27rrjY`HgpnHP=)y#XaQ z+na~}DAAzT!*3W24zbvqXOU`O0S*uh%#k9`A^1NP-eDFVg2E=!l^6;FF{EjJP7+sd5;F?+^aO$e;nNSM7Vh4KHH zz7)3C>}r@DQrL-DiBk|5y1~1_r+tRPj>^#`7HNGZ$g0TqsS?fM_oBJl2GuQ%4O);g z(+V=-B_dMmlvd^9H4r(h-X4(FZ{zu9W=B!&r)nrreToRNC9xNw@!Ie}SBq5}aI@#7A(7jyshLwYD>yb|O>C7$v25F|AlJMg%xi2)9U zg}o*EW+UqO6>2fuccBguN7PDi8}4AL+ULw_C#R|%{R7oT%nqO3Tz~%1k00JbywK!? zag$QlQFlV@RH&STR{j4`*wAjSns%R}!^fW!s8 z%m9?JLR@a4(RK2|N*i-zp$UW{O&wqXZFA*(t4Z zT!&DdoJIZjQazWVZGP-HX1BRMIEpf(hZ_aWsI&_R-t|W2HH9C(6Z& z(&88!%*{8vCCGwR&Kr(C?^O^Eqo1_)6vZZAxfXNPBFBoXv>Z2r>J_$)Xli_qVd$r= zp{U&(!hkuKdKA6MX>3mLl8M-2>B0C+LCe7 z*a(^-%Fp_cw;&7Xu3v`52XzPzXxfBTX#tg6Eb4_J_8!3DYySc~Sd;yPR7sr-vrT*f zG70=9h8M9-$;^+QB;>Sm`GjGFS+c{-?686-4X}dchsagI@)M<1s%9h6vwW9)=Uun= zXMhTG-+zwP!d!RZR~9@n-Xj{onqLB;M{$Ouft+wu@yxmzvmJ9CgLKTdpB-gQihqmr zs|J6Qc0ONmp2gB4gk9pO9+S=acKh1+e^0bn^j0J8COSircT+{~_`xDo$s!-4`{CGJ zZv`h}UeR@JPC%;t6(Wg7KA(VkdkpnLz2`LOt{gLav(k9X5so=pF0fkkkH;zx>@E%2 zhJngm6Em!q#9#!@K|o>P9gb&_scT05GHoK&GKy+()0AM1N@I^h{|Lp~P&})lOU|!W z$MaVJ)c5yrqZg2DH~dGn3kk5|p)^B_*;c{mXM5*UWSJY0oeJB7sb(35&QRn(2_+!<&hN^nHm$p8tgAYER2G?~BL5ih1-iU5( zHE|&pX4iudwG{u}%Bet9XF7%37f!*tp{)Mv%i`aKO71SD`;gLj+$IPjeswH7IGazy zK2}=$K#r8iP+~Ll4EHQ-_>zE__3OumDQw>oNpH;NgZk&b4!I}x64Qa-X#^P4NL z1St0kP+Aw}N^5_TBPqF?`@z#4KO2}=(PzM+H=^cu-xY9>R6_Uw6iXy&ZDo#t;|Vik zj6is~H)9gsx!!;&T=VC!870n%fgfD}aYJ=;Y~_g%)J)zr9z+)Q2BIJcup|@pspUNR zoHsAUzd-&Wy~kNOOIo!%w8onJ7m{Axh3G)#xk~q5{iAesKsdKiiDpCCE@rJEz2oXo zV|;*CV7{c|#ikCPH*emG6-sn4QB}xj)4nMNJQ;O^6{9g^v}#>V(%687GU0!y=9uLi zi=`@$@<(rkgmGgw$_4Oj$6p7^ZE!se|7f3Qsfh2JH`e;uBIbJ z`#g~qVogm-)Q%2r0B+MlI(Jr{7g}SS7XOxpZIE4dhV-wEV&AUN8jFd`n&R4BYFkKe za7qz|I+NAY>XEE|QRLG)?_gC+zTU4i@@$byy(bxUvzcR7^7Y!j9D!uiWoC{`lCKkc zs~DS%8ER(8HeaRMX*5l#Keo+^Z#Tv|yRxXOF zp@gb~=n{pTl>?JwP9++gh_Y6ui&0M;r53g(=W`Lu!F&s|Hd+6qNA9xN!)%v2RAvEZ zae0ZoyFF~%1s)fkuq#yFbR8R(t+2vurZ^SbOlOyDlhiC}m2A^HI+dph(Z0cg6<5T*pX;hBP-R91VLtAl@+Bpg^AHX_GJ-V9QNg#r`0S zJUKVf@<$tgNQe3tkUO9EzKB5!W5s=%29F(sZ0Orv%#N|m(b?V##eZDQ2>ZX*q_BU3 zDy;#7v&7%RFTEZK`!{P@O2Jd!6^Pb81~*8C)epk{LuS%SN@_8aD6Fmv`#(05{y|B9 zGm|K+t~7hc4&)D2GsR9AOYMe*N2>i(waI`&9fvWsNsnVWu*hq$j0jl@eGOp~Hxz8f zw_AxlW=%LLuT8ESuF#J2YXudKQ17KJ+CJdKw;QlKAlf8G)Z3S=y2n7(_ zsQ9}p!@z_(F3h$kD_Du53w}Z}pn!WDzg-jtQq&S9_d})N886{t!S%G;U|3hFcU$@8 z$dv#vs7uK`K)FOklSHoGx}@H^>~h^OudgBgU#N?1PT0XbE5a<|t;RcH2Y_x^Kqw-B zU8!-Sm=V;-Ac|RuybDm#O(^lP86`jyb%QdriTutnL}PQk9?Lq?5%x(;*uqzW7qX_r z5D>{8emOF(0TZ`Gosdni4PFG&%p*~bR5y3sc?YJHpi^*7l{T~b7bPK*qmP?nzrv1? zI9QDuNVw^453$DL(ff-hv?Gi)p?LIe+NpxqhQ0a46LyN&7KLJ=w4tdnDI{Wnu;S4T z3SvDFWMsVqE9`c@Pe_Y%Xg8`t*3mbX^eQ)cS!^GFRs62|v18H(D~*lW^ST=iLrXi_ zq%^i=$NzlBTHh?^U;*1L)jkfm`Q=cjD$znPffWtZkLXZ^)nO-u&`j`Nmm`zb;$7-+ zR^5u&TF2snXvE0}`X~$Fbd)=hqoB~KjuwohPGoc4MA-)NLzn=l9yJwacZnL(G`BAD zq%{}jU|JlN9!WbYEwlDtL&Z8A(5EjPiAklD@6`aF<8}y`(wp{Dy~CNfnRW~w-)?>$ z*pGr8yGLK0g}m0K!)e>*5ds_p!Yi+^Sc0rQf%4S>qz9!p&nX34bV4(hZ&9Vsw?A5bsDQ<;Hy{zq&h^as89R@S~KgR~5JP^cxuUM|nq#+RWF0<^L- z_7^4z^o>8s02)NJF!=Ji)RIUG&DeVDjQU{%vD{4Epxr{t?Dg1qUZ-?7(pE|P=(^aj zf%9rUHl%qq$9trOyA)={sxS~tPTM3T3@kmNwW+mt0T$&>BW&9p@@)v!HmQvO)Ys6Y zfPD3KqbagmJwMW=PEZ;TWg|Qq;StHOgm9)AZI5(mbyN(UFl8>bm)}r;es1BOD}gHJ z`uizhChrnVP}qiO$?)8+7#;ocW6SYh+ei^}v<>O#{76WSk01s+IOvO#k#@Gl*eOb% z(bk(70HnBgARFpj<3tQsoU^=0Qltf_)%hG#)>S{J$NJreP0Lk=@Y0q zbu0>wqPqWpy3tDs1nX;)VvKS7z}8Q&3Mqx|WvsoFbrHmG~ZtW9__&p3!vU zT{N0W^{zJ)@cIq5?fg}|hOzy0g#BDaLq}N_{Ru|u9vCJ!QeEvSxt$UPm$H)%|b(epDcg5CRlTT(< zHPg30YKkI>>(^vL)|ywK_vVC4L ziBpHdEH2gl8;!wY5LH^CBimVUmGlJEFCdsZvshtI*xw;N{sMBa!jlx%e~+;KnB5{p zNV3%ZR&^wJG*Oqr-VfPYjGbT~bwn6TtK^y`mh!5HIv1U^cpy&1QZR_J34)mD#4A@%^CRSL$dKg&qTwu`;lLjUN&>c%BcbX&*;44G0xgA3dO#ROuFRU5IcbBF1}B(n8_cx` z23YWXSX_m*6$@;hQ1MA?@5zCHx3B6PY*l$9m{?7Dj`1aQ)8$?e>ID3iXQ#MRN)G9o zkpoP%Lo(EVnvGd48 zyL)L^$N+t|ZLy+<*s&1nWcvd3aoT9H4+8buj4iwt6ro>jsP@|Z%MK>{16hz*e1K{+ z=NDER%%qg9T+}Cb1qf8LQia9UtdPD)fNUL{xDrtK>Wjrzlzo6^&P6k@YojG?1fLF! z>iHLHgH1qQyP6xAvH)P)4*)>@Ib)k%^Tp0Ij0$sf9mT`6Vz(lOhGZ{Ez4J-*!3LgN1 zPY9PcAY&CWLj8(e*I3eW7eCNYT5OB7Rl}a2$bjAgSxS%v_=ZaR0xEqjl^!V+;~PjD z4z0GS5r3+YN|JMpktp7mwrRA;25i9DLR=RMABCX#vLt4Mw z*$GVOA4v(D%r-0K88XtDZ!DI^<94()hi#VqyQRpZ00$~&DN=_8NdzuV z1rn*GeW}38RNyygRzGHi3Jd|*#5d_ZbEPMjf;~u)YJjQt$WnxMWqMDc6xm6m*;6D% zrihqprN~4Pn590X_moPJPsQ79>Il8(ZYe@G551>cioAegam7w783u5D6AVWi)Qc5X zioibgJXu=%X{Pj!rE17;vEM2|DNF8#T|Mz3C_&gPi8~Qe*qGuYsOJb2TypouJai6I zUt0S`W{BNkDe`yAta%M)&@w3qCGI9C@?;~A6d~n0+DTQdNWn2#s0b7n{~Ar5Raak0 zb#jsPW^oT$5gU+?W=gP_HSymB#JJ1o!x&UrO7JFz%JoG(cni{7T_joJ8S#u417xI; zlb9t?y~!i%TLVQHe5}+Bh?3b+DRxmB0_!mdmiPk*>OJ>L%iSoa_uRL1hu(9)6amb5 zdsvG6O9UQ~BEJ)X3iV#Sr%H-^3;v+@Xi{XWh+ZVszK@DlpO3f1ETeT^uwXDu8+v0J zAlJT9aYxQF zvIrU!xoe|Gb1ex zYI?EsPEk){1jY}KY!Nr0xEx`75i5ea6?t66{tZiAa3?wNs+b$d1W&h@74%Dqe^MQOJ z%-QZEknLhK^7Nj9r8e2tQfE_)Es34v?L$?_?|^EJ+$Jawsr`Y#Yf#cjt3o6;u-cy| zMIh&bV{9>y)NIR(p9K1~L2y&KPm_~C79;_bYfe9h)TI~5vGsRQsq!8CQOKC&!}K%~ zu&Ar)*g>%F!~l6cWu-}pz0`{12!i^-1WqaC*sVnbx8fz^P>5EEAcGGQwq|vy10a|RL<>7{@f@lam!GhV|QmJ+(`X>hS5<;A_DxE0sqC_U* ztZFvB4~ zNbJFEoP$Moe+!Ty)-zfGvC`Fg;k*#cH#Pet0xUO0fIqjQ;!{vdBZ7nwGR=Q^2=WdV zMGxjVO!OqJ^h&w-W+>QwyBS99_Epz6Z!LhaW?6Pbx8tFL}ggMFrjUb7O_U=-Q$ zg_uYPc;XKuP)~f~3u)RF+OXD|Ppo(8c+v_rN04nmTD48ASG)(iNne-089H|$3gZXlLzLvx zzBLRW3Qz~8ekn!LK)+{Z7>x|Tc>K5E<>>8&+Q=fNiD?OjB*lJ%=pxn~e-h8aSk@|9 zu!AvG*%@CVQofFBse)tVBzMH1gDhrCvD=UY_G{)>G7i!(zm9?4d$GL$PjPASNd!a0Il!L1|~ z1Ki=*hk>R?}r>7 z45xehT)Bxk9-%Fv(c*7f908$>DZ^_b9l%h$%naFoVChmtzsgV_!0&1GUTl6XR`pJL zI5C;nAj2JggBGtAH54vCNIqr|zOjamEq>rri0xi5fdS-r1d+)iLsoExFl5&VaUctU{TQxo3#8! zyffEufN8irXad`F8}gH?hDa9Me-F0)&`>;6NzGN zqGzx3W{Kf$d7V)8jMqucV|fl>Rl!{4r5_uBBSUP_L%!@FzvB2Z$YurPBSjfNRagJOB`#ejSq!>pg=P4p@!Nsimo= zF$l_9Jse^E*dSTD21cHzWfp9-LzheXzJ(^RFj2=G2R{SG?NAYAqpeABhC%u*{nEFj z(uaxkUYn1vU!E6w^T19!3JGwCdJ=Jj5PLXQk_~~wPsAThLnWkAPU)}C(2J0x@ezF+ zez)_vJ`^|IcP14$Zu=IdV-Km)TVEyC{U;9LAm|@61MxCDAzgdQe@cS}yjT4KiUJ~& zhMnHEVLsM|3g|Q!;kW`i>Y)Z<&W~eZ!ukpVpz-4OLjX%QePMy)z&B`mJT+Z>M$;{b zN7J%&?Mc~xQbXas#vw(LO*91oX}5kDhAv@h5-`AmOaOTL`hKwjw{bvms|m$+%)3_z z0e?&)Ko(FO1r*=N{%^GP{|``n7w;)wWnY&dj}sh%df%t@<-YF%v-PMz34ob; z1~6|R9=lcm^R4XvR$JGPj7@9^wU{u_H<2~%N}=ovlL6n=10^+irB|ay%+V2i7UTqs zg5jQr7)YHbupxxeI!Qh$`hjg<3}v3LD|Wq={}__NirAet(mMIaTsG8dS#p24{1Yt0 zPB^Arr%&s!s3q62td1@@M_04?>*yTu`T<5Wq ztJ#eFh|8elFdMT9?=yApCl;fLnoB$>yjl1`@Iw-4#WaS`6d=w60VMfI(ig$QLrnXQ*QMYAdtkkQOu(i6PHoU^3f!-A2{F9%;pOy)mEH!wdPv_PCI ztu4m-9gmkFJ7I6Bvx)93dSWJhq$!W;tX{|cXh zTu^B2F#OYB!6`N=_5>Qmc^@Emsa1>wx2Qjcv6@3|tE*+Oh}7?ay#ncXQaa1xVu&u6 z;f|~g;|0V$umVrS`WZyy-o)sl+AeK4GNoZ0N14g86zm3!liPC@oXt;>iVvB~gX)cy38Z+Tb(j;=n(@;b2+`$+U5^_u)0&V%dP@xoMb5u*S3F`}XNhd|(OU)&^= z@#fG0o_vDGoG~Du@)pI`5YoLHNlMt?3(Fb&6V~E!07Z#ibQ@L7PAKe3rM62QtuJ$0 z;mFG{V|TtxDckvC@=(#wNAoS&ivQGNxLgYhcb4eE0K@$PWdv+=KmZenm}wt}Gqu}7 z^XPcx05aOz6o&2@6LY8-<^$-Y7f<3a1bjh+-UPOrOrfY4!E;7Jxq1B<&aqMnUjaV6 zgQ)(5VuSo~(M_m0q%S^&iD75WiO1GV0uAvdkY|!ROMD7mTEsCyVC6PpG~@G-YlT@( zyI2eZQT5Xvldn*?noN5~v0+aZ?Mh^aqH|7J5^&kt!tX&U=+LzQ%^PmzrPOpr|IZkd zJIpyPH2UbA5}W=!og=aBSM+HI;LO8G^9EK1QDZRQ^&vr>b)auz0#~0xNg{AXb->co zPAdWU;-%zwHlqU?BE{cQ<>iX-yr1j!^xF@apz}Mrg;nYfMSAs^Nj|lPA_aS}nCV8x z!W{JDk5Hn(^BEl7a9@btU{TgC(x?9#(H5w}F+tuMD{!+#sok%>-eSWsIZNVYdKqB8 z5YR-3B#C^#JVc8qAeSO1P?kKDBBVp5<#jJPw~UkP;nS&(BE1$|lJ-bXyhVZ7t=2kg zvu!FgIgo0K(Q{d@F0ep!qzQ3a(tnLy^=WX&B;8n3^;C=Y89W+!dp_Kw^DkD1R_D)w zADPHp^^kcKkeqPJ2#F&TLy{@8>aC(Yl$WSogX~5|4rIBc-U_I4r%h4EC$mm!w&AcA zoXnE%IcFD*U29eR%?q-di$IG1z}8_MW;49#n{6~NC-6T|6bW8uOXLuYUc)XvwGLt` zohjh;%^4zw0NV$Le6eSh*)f@Q@}9j!Ktb=MptNeg99e7|qm9MX#-t9C=UE-`vl;NQ zx^+S`acpAjf*yLkrJ$nIO?3+mCzzdzgIjP!pfP0|*e-bu)=sd7RtQ3ZPj20sili-g zTl_YY2hzSn>^AtV zY$upwSG(Eld=%c63|AQL*Z%@Vx8oV)Ggp&WCV|><-su;J2L@(hni=jTc+saXKqiZp zVdi@R`3(0QB&?;T#E#<{DpRwOfc*iv7!w7C(D-^RX#kttIN?5b-!9S#?N?$;vgO#! z0kZUFQ!sjm9e+;zWz9SKS8${s{Tn56Pu1JUnlk{$b~G3mV(^!-tffBI+Y9R8pW3MC zhbZNH*}RzZSn_bxm;67f9R!8r%{_RS=EDjRbA*N9?F#jc;okDR#R5k*;wn;PI-cg( zSJb89(1WqT-&FZ+eb9R|RI%_bz&WFv6BkIUZn1*28-j4q9WLkYgp&NaSlEsuhcm3N zd-$U}LHcZ8ng-`6?Tms+bNS&BHjvY4wAkyf@JvbuNM2lS&LBdX<8z^TMH}BK0uFX&5%`lLE?H^{O40V6AW*Qh zVN2a*v#MFu1GDQR!>B#7JJ{0HA=Lvt6oaC5HH4`|db4;!$I?jt=Xw*iN(rm>PU31> z4Xz&pMEpsP1w4As$c0YS7n|WpWXbe42z6n(IIA9?^a?Ly4)*92)fl@z+Z;o zqcJ?w6NLDWaFg}$|76er_pqcp=rvdeq4?ETH-JLn$)K>OS0j*kc#R7W-i^fx%jKUa zjw*qt!I(@egldphkaIe9n*m)u&L8ciTFJ4)--<&mCt*7V6@By{D)lo_m^t1RZy3)` z-2$&tRA#n8x^2{krF5o;KLK$rxw{g+19zF{f&%6lRoGYf*7soYn)p6uwM9R1TASG7 zXhs-F#@q`$i?u^|kj@g&Bza<@NI!8(8`9!bbwDaeP?83Eb0HDvpO+&T1Pj>>qA!66(;5jtsI11ma(dyrjv z6T8*B{){a{lN33K2%45+_k3wGvROo4e-5d9h^z3C+pxP@YLDKT6)b?DAw3ZjIfCBv z^5=NZQ!mOdwW^b(Rr%5?#p*w{(4D&jbzV6J099w$L$>!qxm&ew0a#joj`pq+yXM?A zr%^$*(;2dD6lv^wdrka#Obd0A9=EIK=y8{tE&I1Zv};O?T5ZSTlNh?1Y`cl9)pjQy zj@5(l7QH4b7@g-#*rInr$F?*ZY;Mf}R1N+X@4&NQ%$HxF$F*-l*uqXG{sH1JUHW=< z^;VEe?7@eC*)fmpN22YpycQK(ietgU+2lQtpQB!qf2&oUEUg-h^AlG8&V^(wxpa(N z54+rZveQbj#kQ^foeO~c#>%d90gb0CcJ-5R?3+*P)CfT3;ktQ9azx8;7gNMJ+ zE=8UMEv)f?4EY>*+d#~Q2uGUf#fVqfugz)NDz6qW7gJN^TY@b*rI`QkZzbPHDsYWJlVn4&o=jg5w(W#}i*gloA!dfLB<%o@hn6G^rL&=$0-= z>po0esrDq|Ojc0$4SBT{+M|w)1i&wJMjZ|j$cj2F6xc)RHXLQV4M5y(~_9C^-+x`@?tVQ;37Xxmt05c60v3P#iV z$Vgf{DOVo++RSZb;zP{v5#VoNTL!%NnJWV?)K3Q=hJGs1F~`~|)n+w2(eyPspGyu% z=K%wM2X6@Z{|)Opb|0St@B9|HXqmQ-gu@54ekIeX?_P}p_Jxpu<_h^OPsTn3Iy-&3 zi$rd1*cuFk!H?j##nFAlWP7w5Al)9=v$-!bH!ZAY68a+a0uAb;kXx!~1LJR0A5xf3 zidoX%-L2Qt@+qPwPE3UF5_y<{sCTLnq2%u1Z<}!?lnt-1n6Fd~f7T3_Qc}#} z0W+l)XOzCC3^4@x-Oy~H3Ch4V${c&FRJd3m``s8PrQq65bqIWoX^)UWy>;+n%BL^u zp_P!`;Ov*;6DchoIufnDjUh}5QM6ao;RF^Rf(%=?VkTfkt04pkt*E)e)tE?ymNfZp zqOk8hg%~qECYPG#VfaG{`KzF$lTJcpW6MQVq~XNsBEX0x1xH=`;=~~|tA;fVQH zuO?hrg&l!*ZBGL+GLG7J2CZ1$`vDoWf++g|X}rE9700knLq}uIOKU2 zkRtAEAcNLAf)dAb2+ouaYaew>Cj3tev%z5)!!M?zb!;>L9aaFGuT{r}@G=pTK-RHg z#QA2&GguVD{+*bO#|7u3`(kKDkRsZwm&Zj*?J1e(M<@aB{glizh_{LKryGE%MD7~e zA@kFi*(;P7qc|v>euJ*^o6#(|rkUYCMCU1~W#@KEApt?Czqexhzv;K|3WsIWn7EEY z(CHWx*HDP&Gjq*Dh59i=bs26-*Ily_0V0H(t|3Uu+>0ltvN){}bKLkGfQiCtr!NQYvY z%zBPL0aZ#=7g0byH%~n$u zY`k&6qD>tm7TOUgQnnq@DKUEh{}sxuFbiIfMa3MHpjky~7}Z=-0v(0gOYu+NiN#1A zg^KQbm)h=82kBSiG#KT08_Kriu%?j@F;=T91h{jOtgdgK^1F9n5!wn*4h&HlR+hhu zABnC$eO_0)E5kqWljBov%Dr~25zJ$3RAZeM#dF`)-uJl}NfzTSAr!d^>5tkh2 z)kM}9>@Aqqy)&A0qy5#QWlH%moZH0qE&z{K{%R`(mDpWYx#k4TiiJXh5=d%Lpg?&v z{wGw*x=CgZG@gdz)2i+KDtB^63HZ(p)V<-Q-Fl$zEpHUh=7_f*4_IZcvnGa8ETtlr z5^;tNSGb^U$Q=3Mq*8*(!^Eyt#)g@ago*=OS#!5~I8UhKhUY`aVV-jeMVO!T=k=mIlCIOr3iJDjtS}? zorXhrbY>3h6iCxMzS3LMV5xXXIF?_`ed{sGrZYN3z=`Ht89Ab7Ld?B?s4#K}F=!Xo zXgH*kRYZ!=UW9>2XJzL;kPXc!t{$+k0uRy(+?AcISd`OV4Nu`4(ER;i%#NrB)7nF zg$ejwST9D^fMpnppijiBLYMtORy$=ahrXGz726taV8Lc5AN51o-~Uix;TOLrEM$A& zP=dRKS3%Ba-6}s>EQA(Wi$uVz43b(>U|z!5d8* z%I^>&DIq1>hy%5;>vH(F!no23Hp`ciLM7^W_cK5cb!?;u1QkaNM#TYizM_wr_U##x zHZQXJK|p~X_6T3rEY>0yLk0XQ)QLNUu=`Qz^5Da0osAY8)g50{qL|3C*g+ETXY@x{4~ zSfeSX4s(mL#rnq%Ia34op8D1rET=K zt6-`+lw7{`4cSU#hh4EX61~PLs`s_Zj$F7Q=-m*mc#7bF2}~k0oW-Phl>ihpdljU;JkKJAR_(=)>kkmF^|qRM`Ju)H~yQj zjUhEi}_A`llr{{tWdE9*nf9p;jIcRJ39x3SpBB z>P>8h()3n4Y4jVR{!9`pF1Bl}Qj3N9Rse5sL2;6YIF5PId*L#3wWk`9KRf? zx~Gq$$Drxs>5)F&68NoE8^C`CMf6r78}#yE@YmPCUk&$f>V%n(cx&I<<}(VWFZd7m zi-X^iAi^A@;0?RWbr?d39B@@=ul9Qu;y8;%^Q72Eu-AVCi8!(yC0p0DBa4 zfjj`nG{18ivLjG$gC+22a@p=xFMJ9wY|GiYY0i~<` z(_8VjY~Syf z*eByX=q|-cFKLzG5!tMbfgi;n9B8&y=Z{As$Fo+BBfRX!LMUJrSq~8UGK%~FtAZm|I zuZFoLwV#8#X|tp91Ed@75-jPUFybdlbo%cwB``e*vlh)pF7>dqE8=tzIfIZk#?)23 zO`DB!ocvMN08;ulR`DOHnxm9sqoY85S#={0r^1hESEWKqS_jd!xm$uZ#NOFgukd|M z)_Nam4GKDrPCw8}lFSxgLohmK2g1Tdp0H4oa$yk;(!I8?vwVC5%=IgD8SaVj&XZ%R z7v~(eYL^=BcSMJ2f1+l!I37YCBI?9A!~HF!Am+LYF?!D;DYzYS1cm81>{?`jsYY`f z?q$8@#gYeCQ{e9e4t7j{?Z9>#f%CQQRNzZ;n9Qf2JSF#pvJ0zalW%u0c7qkyc_0>- zt<9z5DdVZqaxVM7fQ}nni_+?$X9T~ApuMefFZ>%DxQN1;ue&oi^Xu=BpBMRbEz$)1w`dwsA8aKYl{WGj9eP$gIojR zz`t-Cf{YH55<5Tgpvk9lQAeD#kC-D9$i*Yi^i3kNYlWK--Qfy~9e|u-SrhWSpnG#4 z#vG&nh0^fe$g?Q#T>9*Ri+&3>3p*y1Y2A<{9d;xq7Le*K&u|}vj7m@<_#T2-fkVFi zxZk5+_zlW}+z?XC#NQ)=eE9Rj*o>|wWYT9a!V}t+)xKnNVgG?J7PoM8%+KEd&2+zu z&~k*#`HQWkkO+FWWC--#2L&gab~{*@ub~*`0iq1L&}tI@_4O!Uvyswh`KL0HxbIOQ z5(>tgAo690S{i8)PdJl#R`g{CdEuXs9Uyb)$4+Z5eh8{sQ|FiXQEl6zDSlT3$get2 zcz3#2&_J-p{wg!vZ7Qt~I-%YRB*ycw=7Hqla@^3Q->3j>t$Srd*G=+GJUK=LX1E@dyAdlI z?xPgfY84=SaWXs(;SpwZ2Cmgw17>K2kb~dT;`fyJJt=-qh~MMl_n7$Yp;i5o*G;Lb z&8if*-r5O;-&5Fa)4q0I5LDs81&vq+%5Y(cIHp1-4FCJu(6E2gfFxZPm$5-FM{6zO3nIJ}L5354;2Na= z?$dDh^Li+wJN~GyLe#Zz8ut>g3PGh=Q*5uTUKAtQ!CyXYzHW z1t6L6AoiI=pefCJ`~!-JMTBZU`Zw{A*-X3X(1T{6!!>&<3xfu3$;VChVjaf0x24!n zY*L38nB}BeiNHXczksRg=Y~77gqE70O10h8$anFx_$A<{5WV<;4wi1|?cjZ9!+kSF z^!aRlWGV;qoAiml-GT0Y*CzlUS2)(OaIx6jL8+ohMaMvAw?fl|H{3j44mo}exV(j5 z0#lZ$a=c4SLf2);BnH)RH!dc&A-18D3mmyffQSXj^+vdTfvvj|f8~{cI_brHUvH4s zsUbWUx%iKIBTb)x?-=a&`QlW9({D4s^*Q-)~AgwE~^E9?iX=3wa z)ds?QsC(y&R&|Bk6_jA&a>2y4MVPpLhlz~7eg$1Ux#}KC17Pr%K>gP-dndA|JFBJ0 zK1A~tXl_XLjzim6up2PO$XSV;1-A|(AaL`OBt6w+xLq=E4nd`~sP?cFS%?(UgCoLqVecL02N&vs-Z`>97fA%>oJ5GOdfFoTrd|eTN+q``WW%Q| zU_JZ!4r&83UC=Cw$-yrNWeRiO0!o9b;T+jy6qq=alMhQ}xQQ|d4`fry#1d6XI~m-4 zfNLmHD*!~*Ne;pj)^t-uFI)t4b3%@}T@e275bpqq>-^2g$+Dmo$DI-ae!?iMi-!B( z3r&p9K(jb;n0wN;*c&K#&>NPP11lDRIGl!(BCk?wv}&0GS)lGgx`V*A6}vf6Z7^1Z zEkRaeZ}m8Dm#q796oo5(*t+;J9I+1IdpGxjgsg&u(zFrMn>Gx^JiRAl9=d{?Tb{yI z!cA%YvRom(NjRE+9(*(X$RgE3Ic$M9BOt@2ZrkQz1_XI1m8>l?TBsq`BF~bN(bK>pr0I0W#qDISg zEc`7UA(z6}u^>V%!SoWK&O)^({$jX?EkL+E@oVw^XOQt(0V;MTHJKMI0wa9dweA_5qpqo-%IsuJbETd{ZQX7 z!JRoE`Aum=0-7{0I$YM9;iXD{jpA=!6qZB0)*L%c-Q4v3-IQDY7v20qHR=62fc}GB z-3LkLtgc>7UEP3qF|H{%!6C-|k&KL2Lw)gPWZ7#pn*MPNQjG4dCe9 zXYUkM%C}>fvxpRmuQF0y`6C4JTf9#J6@$H zTS5Npl-XPG2N|vij}IVhyov;>LaZ)=s?2Yu81A1XtHh36@$HX4iH!JOPo9KGnEq(5*d@nilpTloPGceTT^NU2& z1JN|Cl0?rw!+$_p{%3^zW7ciN4n+SI!npSpYbPz5;n?)I5UqcXZ<%zJ&Sds(X?-}) zsefeEa{1{7aFcw#2M?3Kh|6gENe_qL5$kc{A)x15$W<$-g05g5&Q}gDVjJOBfCRc9 z2%acz{$y`G{CQC`u@Zvr4mjGQe{?OSi6n#4J-tonTj++=tAJkYF(>d)Z-Tk3^&5^m&9(_YWdb$0`aO9@ zkz`ef@2PEpm#3kcvnxp5|BY%OGcO=Xdk@_ljWbfvJ&?Ot^|R)lHebfUSc^6iepd>X z>q5A%3Ae7)`H`tgY!Cqd7iQuEQ8R#nF?RCb--6F(fV!02y`rqSqYb3=8mK7+ zeF@3g(1pdP8Gw}b@ckUwXfjZbifAiOH%E$Z5$rAYZ_@^a%%Ar)4?1xb-qaBx|N9Gu zP@*GPcR_*|`!{JTDe3Cq|kG=j1q8LIA zpa171UW6rMOHsiCPR$c$JD>{WrEq!)V)w47ubqLT=Wr$!msr-*awtxn$x}C}Q^e7; zMB=kQhGfI4-3kLGDLcddPbx=AtDwq< zV-`Ojk~8EAy0dP(;y+sTxy&}^HbV-&u&8dbmw)q?VXTEbXNhK;pbAApYFKc?@=>gk z0$yw#Pgxh-pv2VN(+WF{x~LV&Y^4z%Fv(VS&~EB;)|}gdMm)i~DZTYV%t<=%tu8@} z@uyLBuLpnPX%Z;r{*b)=RBCgIaX@IcT^ffz3l5seUPA*4gEkP2qIZ-i zQLR*oE-AyV=;wa|&GiYEbAd{fKL~*z2Rtab}(9m|9;9W~-Go=@ z?SoSAgJ9JCFT91>9k@oJxFYD^vGj78wc&#+a_+W3e!iL!vTgG3(2l_MU1p8BjdJcL z+26P%BMATFV6?a*feU(DqeUqBffShor~#T3nT0?RkzqB(u)oxyH@LaVe^5)u{p>+j zX7Bz3O%&V;iIXv-lbRsx)%A~^vh97t{X8HIm-htya4npMI+S&=LeoDoq2}}z%0@>dwMaGFbZ=wq!KhCJ~v)XE4LiR)U z!97tHO7%)~2Iw^0H~bjgg`I0=XRzQB&B1M$ zbV}@oS$rj_V}(d=HHq zr}IOkPFR7$VYXxu4I>@anud4Z{&1|gg6(8G&=IpYycWesCkJOa+#!!te29fLpu*lP zhT95g!{x0YetXcr1^0}fh-afZgiX?1dJmklLZl(QmHbB_?GvdkybMQ_L6LhGX7tgr zqJM%#s)?_^l?LV$nAC|j_p1|=1C!0G6GWH7>AP=KitS{VxBK=d^y2bHARGeIV^4t% zG8}F;p~hg5D+GMVnv>&n-Th$XMRtf6b|3EBG6xG7!1t4yXh`s77P^QDRLz%-#ds`1 zLI=Dxa0Ph~SGk&FGl|~^BW7ZpSvuJkl?IALS;PJDd=%~>SHz=qTx&bO93`;s(7mB2 zVQ+>%;snHy+*_QZ__pzJzoRaKA2RSm27Va3*OQXpzULb?6?7euIQNe=c&`j~nFSTF zh?l(mgOHsY@T3K}gb+ZE;O*e=ngZUAJ~>|hEx-}H-5F%AFrXBA zW8eN_)){2SaUpzcp_K?}ItBxPyZ;U$kl=y)>#F;}51LeGbowxqOI%^N7tff@<7hR$LZ@zZTIl(6+D);k9R z=Jjg)*faX9x5k3h0Y4n?Dp5_28zUJ*}xX?=w{uGERApEmWOpxRa zOqrkLC_Bp{+h-5N_wV3-EQ?Sot1af$9b-xBM_PO_6&TNM@X|>jcKqJGDPSc zXLyB9p{voZy38oMh_M&r+klO6hjybGu&Fp*ZqHCeqWC0WXGrfz$E_(ec1=z6JwUV} z8bCv^KOzzz2&8|h?-L@J`d*+1mRp>kwBz>k*%?l-Xpa(=JHqstKo-pCq}U$u-9Q;y zV|@GXJv25p{u9U^{p(wy)Ep;Q?8<+wMuiqB$DSeO1Tz9kO=C6Q0mc_NoJl!W2k;(d zS!R1-sc9hoZgk?3j*M(-EC;WlY>LaFI1j~PHZ%q(zJubS9}g!1Gg>LOlVW?cmqRt2 zT7W&09+FN#nqMkh1IhQh{Ra+Kglw&64-mc!o*E-DK#Cqu>o-VZfDmWz9i-F%mGlje z9tTy^K*Jhu)p`dAT!#h-O26JF{+Htu%;+IZbfRGzAe;rkcN#H3K-@6185y6L9jv`C zhNsFLp1$!G;{%?x&>SC(1r1B@Fqz}i*l&Eo$@U1pJ%nFSLO27cpPfO25aJZqL2>OA zw-a!Q5u)L{5d#@EAu|WaiO9kK)A+2Voe7%fE&cf66oh=rVdfG`x!%;u+HDu%Tu zhks)RJUn3rCh?EWKpx*K0-1c584=*EW}3J1+FEwen|4F7||lg%)eE(`aV z;RXs1GsCSEcADXx6h8S6LI7*0aHkpWpzx<=m{Yjj40lp^s~PU0aDy2phb8`o8K#3$ z{6#ZN0vmtE4ChdIg&FoxIAVsyvF$}>IFI5VG{gB6E;GXc3ePsfboiPpX1IjH(fpmg34D#t?;2~y*v*)1#JJ6vuU}2oBxr^f$G*BkImq}8 zc95v7jWV*CIQro_WX8N{#!Ny?hZ*x1GX^WN>jN|9mu5^pVz!zwHD*izF&oU7N6Z-L z&|Ry|m^&yY**(+eBoANZB-^BmltfPA&y$07R{poYB^4@XtCpbAYWOQH$)uOMy@~F% zg4-%iMTm=bVEuE*b%PV{;ASj*30SaqxD!I5f#d`k2PGu)>#6qfz(`^xR_TAiSw;B2 z;5yiLT$cqmEc0i#(EMCY;Ef>ghEO6jKLerpNdap69{?TE4^Vt@6kpDOh;L{)xBw#r zAH}+~kg);KO~%4z)ea?aMeiB$_7(3K?OX}NupRee1|2gY3d|TjGo%#&l zJAI$u!-x0i`+HdYoXHRHwIrm}$M_4HG1f?#@lG!O0A#2Pn91n`i|r;NyJI$^xFH!vhdB~ zRz+%qV#92`&*#7c#XmMf^p(wgYzKQ_bb&qqS8ec%Uh30J;~vXfm^ft{^iHGC5|Gxp z3~B+0fccbtsNo)Yn=qsdgy+GfD4M{P2pBH-Q@LOG8!AnHCcnec+*hv7f`l;%n&p#>DWv`*6wGh z7>elcGgM6GH=#aQ4yN=~OPkw%n(^QZ#K3@(p8#Pqfv|p-iXpw03c54l|Fm}|@KqJp zJhJc-NFZT-NK_Psu-FCy^*wme7fCci5VS4{Sxht}F}aV$A_NkY@JN5w@>5&2 zTC3JpTm4%Xv}zM}+=v^Zb)l{|eW-B*+<5=*nR{On0<`{?{`&dOs=FXkv%$YMY zXJ*cvLAnnOHs2+@y`}mk&K6Ez=)DTrK=ZR%akBZg_BQ|69kB0a#q)PrSqiZ#kG5N( z`!07lR^1|LzG_`7^%?2uo1{c7h*QT-`}(NRAYM2hJ{$c(siHt#+%I z`nb8}3zG4MUm{f8ei{QOL0pf0m=^j0saEOib{Uh*(euO~sc--EAaKl=kKa?f%LTb>wUCWJohXU)&5?JE=QyL}l^_hqB0>TdcnYDH4h zm(hX2!PxYhpu@yqY%;JVDPG>jm@e6I?6Y5GZ~0`R@k8^VO=G{1^kgJG!F&_nV?_Au zSMrGlHPA9xeCDrNWy4@`oK&x*!u_Mdrk(GvlK~AK-n(PPg3*s}K(m}HBjfpI9%8%F z42aScl!|{;hBdRE*Zr}V5-iHNL~218G@N$nJkn*BnBoS zf11CUE4O;rjTak^=(y#zUhMEjt^gjY`A%-k&}VMUNwgUqE;KMNsILK*Z&+zy3C0Nt zot|~$L{sOC*A{}vw0xsa#%LzEbsod7<8drPd?k!nH3u9JL>+kRD7%-83nRN(!jsL`sO)a`Y#&+Y;aJL)iwq*$ zi9h0O+&kR|tEKHtZp#hsK6RNP2s`$+RzoAPv{u7>9M)hABkAL5mauR= z#mO1*-mgShSch8+3-9E$e}h)Tsqf?6EiCxnQ@zw0P9!~~1=XEw-=TZ(tror|;64&c zAS{rArPq*v-_?f@v=4>`m`@PU#!QO`KO?YKW!S<8vbd%Dd*3Yn@C&QMg&f5q98^-B z7%!8fk(OK_nxaSr#&I~D1_n>_lFi+)DOW!pz%~t(WYFizNlbnaRjepMJmienQ=6cK zWm~bZX~uD!D^?W{*ke>M#F)II(R?V7Xg;4H6ieD|`LO@>sE|+(526|4lO0`;rSivl zC@NoOFfD{>n(^#Uv`xCTyoA$UJ_oOZO9NLm9sdyi_zWYkBoxsS5)~kQUW%r0gf^gX zIpPdptTLoW3WU0zYI`KA^XiMn4P->lw zn{7YTctrunj|MNj=NGWj^tfM)^EVcirX@rJwXKeK{rQQsyP;ClUp>Ttj>s9W=11QjI<+Gy?gN0sDfuhPSQ&H z;D*cTo4_-On+*l&^xDJV$@Mxx-?#J+qU3WX=%$AaPt%M)t`u}nIt<-mM?qJ_rh^3< z;cqEyVzemV3^q${>c)66&Lc3^$jW#j%{k4SV}&tK?v56^2-GL$ByITxsGsC7Wg{)A z12^`qd)@WPN^bjpUox1pr5cmWO$bgqrMi++MLv&Mh4f3UVigh@R8!zNJ=^L_ z0a8ikSkv*9BxBeA5%)TH^5kBW;65~ed)KMNzPYkrHX=||8f z$13*ClCbtbtc_f+w5v_ykl^EpwJ6Mv4MlU&k`>|dTSfPCe?SN4Tuq*pGC~Q_*#;&?(~i=d+^HVPLKQ(^}jE^>PpOCk+Jw|Sh{MR0HP^p9^UPNdzm zkv%DdcDH{JE3<#hlX6lovW9W_PSN3O+r~jX2l9&_0cuSfw_SXLIZ+91)!kG^W!t!D zu|AwB98?Dfd8`dOYi<;b-T5Q1u*TT2BBQ&#+Fc?wl}$)t5&dN{4fPsfY`1ih7Nx+)!x(yE_)WA{ItcAEXU z(f%B`aywU)@q$nvHj25U5~Y|Q{{|1CWcQvhmN8t{{8W5f^ZR%23s)a&UwBtGA!T3K zR(F_gt2>-6iVU}J4~JWqIzrdy2A@GS!B)E2MSVned)IwN=X}Y>z*lD6K@tJWq+%GkH}TW31&>~W|(EDxEwk5=mmmhKeeaQhfl5$ z0K+Twe!r~cJn2V7!(+)qG6BnKTAHc?V~}6$JFQ0W&6>bn&|5kR<+~mhy$n&9jEZJj zVQWvqYT>PBm$WQSE}(;HIN`GxG^KWp+jF#upk-3^Xfh;1ksh;WlndVk#B^)mL^D8{ zj#1oo*Kv256eTo5_A*|w52P-6+FU>n8ge3Snb+g8`V!J+z$@dZH-E;W@J}fyP*UCb z!st8Yz&?5cnu%I-`O*@*`)WYb7Qdc9jAcTwReNA*6`j*BxhF83mLnm9Np~Fa;W+uw zB(~M;F*9=hkb53vjRp$}r>_<82{x2bV;ae-;}7t_Aka7_kaUmd5oEXofu3hc#c{*n zbLP6ult;Kk-@!Ao0=XtOiKDq1uXjcm&>mWbyf z)vV?rTZQpx$`VbPX$CP`q4NLHnSOsu0{N(>(giFPB35liM`>%`Pn|gkonQI zoCtVW3My9z2}{`4;y8VzqmMCf`Ww;jBYNmcDex0gfqLClt9n()LggBc8|W@8zcn*T zRH??+5J=lh;RdK#q-!5>%*Gi^7h^#jk9bL-MW!x)-XmU*#^~%&qT5X*c(V1SER~bw~wF&Tsg>vUeVbfzW197ZKmyxj0 zQrX#MUd{fJ{w&L}t38BZ-DfFg%Rnp{AK5~6JsgwWX+l5RkfnviZP}6A1GabmMY9lT zM%Kf=7yMWnXJPxdVu$ou^INNx4`y6eO8)uFq@)2E8%dWq}W^MPH9`EuONrs9Thb31T)qcy6kU?S&yPVw06H$2&TF0QFc%4|Lv1Mt?Zii65 zSkAn16Oz?O<^?gSw#PhJuPZW;!F>crSVir;kNjv%fobM&sqj8*YcEMo{BbWOAR+Q? zJBaqJ)z{RC<&}2-s;_k?x=|?PZ(4@N|Db$EKw%fI=6lX;?+1M+LMlw&2^~B_ED-|p zx#oML18GRsJ;vhWHv1Enx?kVab_g=`)jhJUwTjYRZ;P!mmo%kukOX^7)pF;GTp>Y` zIM&Geev?#RG-9KxS7t|dS&l~@fR%DFO2jlH5S|&dYirN!{kC)+|eqB!PwbXfWB5Uq`!XRZfebk zn(jOmOnVk4_5M+~UUUw>^tI%o+4%|DiO$^C(s0g;T9G^($rN!&3S%2vvBm>R!|GqW zH~3O6(wZZb5l;JZ1`Q!?Nq4HO^B^<7D9XYuX~lT^f~~hn{y9&tIA80MZ}*OShCBGU zM52FQ^cGYdKMp>}A%J!tX7*aFu)#I=>nNK={d@|7j#V7H)LFP%7!6@_5xY z#J@XfeZHJ+%emeW3xfAiQh~n)dUIY1yy*-6PGmP6q&yF`J0VVNSTA zC-T#FTw9A+CnX7pp4I?iin7dY#p+Tt?EQ3F9&%QFK>C#NMWrHb2vW>j-R<1VrH( z(A4u!y`URT+cjOt=4$?2sw3DcrH?FS9bTZj2pG{q-7Yk`G*XPuS;&s6UvQZI>BM8r zGcG;FE)4>^=v}U~bx#Lb`;Z6|y-U)gerlZ8ja{x&_X4^g^c#A`7P~sSAS{Z{iwPFc zZcugK)>|L-Jia3zqIlXZZ%1EUt+dFP@XMUMO z$>ET%Wjx4N;IrmLU{EF-Omm+#CsYe9%Cq}SHV&5;d+E5^dfw?o69w-s(w$_ zu1=b)fwPho8FGL7DMI59f*z-)2jeUR&_izD@%Cr5P$X>5yMUI3M&~k-PL4YM9)m9F z2sz2UY&jpZgYm)@~4gud=YNzHcyx;)giM8Ce>R5qaN)~qUL-UODt>bFrTGc7IC_1 zJN@-m#^zjXnQaZco8K})MBq9G=B56Y(^ilpIaymD-kcAOsrge+U52NTWmX)pj=NoE zK1e~WGV5jKn=>29ObgNa-c+`Au+Nj5^Q|H3wu)_McjiDoez4jAeW z#(5i;$Eq2w=G)2Gn|)!d!Ul!LkizSmUF5px)2@@0Io5~i=mT$2&2n&h{d&UXPhCWe z)e@uh0CLI~$~+6|N`Wf!r&fQVj1jQo7o_FDYNY5fwaCJKc$@whFj?h@7zPuIcpa`L zy@C`>a+9NXqbA1{kb?>^mWLWZC9VgRPGsFM=H9+g1uf%47m=xJjR@vocNH74t!GBD z46|N#9P&%sda}vqlvPs=z7|6ut-7onT+K3bW>G7@C36Sdy2DAjka@#0m<~Gb$nf^T%H>CDy3+An?MQDL}SKhdn z{Lw{Rthe@LmQW}O`_`O*8~Qyd&DOvGj{2HaO~Ohi3$5u@-+={$%rN>h=5AiVm7(Nk z3-E<|5NVeXXXl75XcLqku#DhC)A&(XDWf7Yrr$9rP)J&+ru-|0Y!?LR} zA_m3`Z}wzQHg0r19PN5!XZv5A2|L&UPm)8+p~qd1v~#J4HkP?nyIpJOAdZF;YH^*E ziCrx@ldN!s;-+mv|25pc&LOr}(Tc>>v|jcKAHQG{>)prSuK(V_U;0g3r)HfngPxJ} zu!&8LTZP#4AE8mA9{aK^_jLG!QBqku8nczLnVikl10^+CHx~WBWZ62Odw2)E!23A- z4THCPv4_CXnJEYf*$5AT4D%Fn*L&*GIINxP&QYvJpm!PfWf0IOV`zvXlA zW9$$#ufugWmNr&P;yJGvFZk9ipO}pSPO39ED(vkDdtFcNlFhv|{%{S(W^JkGo~CyW zvHuV%v)^xeKIF~W<8{s411q$XkrrmQ2Zoua=v)&?&h%=hbS<4T1cCLLx8c@{oDTE; z-9&0l@_Hohp4q`>T_$d3&GJNEFkax@7*7=0_vgg%%{bTPXZ80^+riCnyhwqr0eaUK zs7NGl(^Fw@^lN#o^BmsR$^)qRX7%??3mXd~0Z3sgDH!LXk5;fYKH^OrIs~E|lqgfd z-4Pfc`AD2;5@!T)GJ4`z5xyj<#F-YU7?Bs)Q>MuzPPAp%O%uVErRrTW;Fhww$+_M2 zn|L7T^63~D`7610Nd-%>8(q!I_y#)I{+8JcbvD4;c$JC|#5H0jA|@2u zSeE7d+Fy#I+8YJI_c%c;!?`Cv$8j)=VMp13H0iX)4XwS?T>EcOjPt+oeu~P244v! zH+>beG96^=2l3e({R%za%3Xu+A#V^T)pT4H8E3rg*meGdw8L#V zn*tbF-h`3m(8ay+^BXy2)$~==T3W#Jly%V&Lg5RMrZ#;Q9XP^wnxr&tPbk$U)`8b@ z5mriHFekmp6ald{Klr$o@V(>Sc;4iQ8gh$>^OIlD7G&(rk~QPuQ|zM!28YwCn6olm zUA>%qb>fP1dX% z)47TKI9A*F)zMg2W_uRvLUvBkZHcmZcL=4WtOK|&`4n-v*8H9T!oRNOJ8;2H>vQ_@ z@EN*r6;n6pgR#c!ik5LOu;dY`CShc}M8&6<*VITAuPw@&7Md@7o_bhPf!K$T$y555 zZnjV49t|jMkydlQuGR>HP%Cnr_*wHMUGv`@!k)oK4WTR#qAlEb(NU?`C_R$ zEa;iUUL^Wf)|%we?DKF%xwg-vZO;rhA0~;(f942GYj-ZB*qH`PP5v`SVAsD5qB%2$ zT_pqWZXs&$gZ$tD+dj{5yuD5Djw-nPU2UL;W}NTVhG@o{7m^_9p4OeNAk|y(efCm~ zedljT6$1>GHB;C1ZA|^gnIo;(2MA*g)qP_pS+PSkNTO+PvNa<1eX!-?MqMNYV_jn4 zXP4Q)GziVWH1qd5AwAF9jI$-(GF{U|Oib6Dq`!mhHQmAb=6A~yi`GoiD@FQ~t@pzW z{8;Pb$@wjwbU&AO^%i_q?Q5irZ00`L=#>@o*S34^PRFOU*3q)`X4x9p!<)Zl>HWFQ z&v4Fq=|=Cv$)PyblCnSAE)nZORje66LDp znMH~Wjp*F?&tNK3>sL1g{@1IE36Jj0uE862;Uc8S>=h4)e%q<)I86$r( zsF*4~O#Su`#VoDk=V|UTrXHCpXdd9I5R%sElD?H_Qtw0qIsVcFv{tj2gC1^QIxpzk zs^sX+p>Wz|C+Okt8o1G%$)8|$=Q9wW8C*E+(D8cUD6rBom;SAEj??L|vNeN5Wd5`u zoOa%c#D6RBYqOL6z3nQA@`ZjblZJlY#^*et{!Is?12H(6<74xZ3zm-GBXbNv`bXWF z=>=3#x$(t+suB02cjH@YI1wr^=bdHEuchRj-1wN_d46_U*SBY_SjM9S37cbDIcQU-NW89;*>RF2pnE5gbW{jxm zY&v;{asevxua78C(f~-yXeS3*sxx!RKs@f(h0s`tHJV4Iy|4KskPN#NjcJ#|9v=+| zMJ03vw~cA%CJ-<C07aQ=@Ii>V zdZh%;*|&H=)3-5;vzxxfT4Xg|t|!;)ye!Ez__22!(;2r8yTi3c4zse!=?foXzC^L3)QxW>^p<4~Bjuc5+QNEc=?>pr@tY)QxN$~z!4L(mG0(I~LxU|wg{ zp{w~zM)L>}JB5iNSk_q~LOD4fFTMh5xUT*Nl%R;~n!jqa;Vw$|%N@FOuI4u_Pt6eP z#gk$Lvh{L{kVUZfJ}za1(Mq=x8Fq{D`NnNE&%WO-^CECTD=z1~m4CKp2c-#~b@%GB zT1~*y_}Gtk8`0m(sz=S|CNB^vK1f4fH5nu4(HY>P|cqBZ{dAO*Jng z4C@!PUJ}g)Flxz?#h)nRH*HxUmiv0nH?t13$y(6kSk{?@J;oB!g`y)OsmzmAqnG^* zrEVE}7Km;J`x)3+*t4<~3%;F0=xTl0 z6AiA0+ig6@s#hL1Sho4HvyAq~E~F03#fWB)F`b8RpJi3wtl-_A3$s7AMpkF?at^uH z+=j#3I)5s`%sKl6f3D~tz*_vpZ~U#Ya{7wDbwRW&Bz`OelN|!~*cuRQsJ7}U67of% zQ~(_uFNpLK2sQTRGi(XvqY7&o5&vu3F@oJGJ4dZ6qC!dF#xf&1Oyqjdk6a9=w9cJi z-pW9WCWVyt1UjN*mafPU+xMVbpe<;Mp2ZjRDO8Boh%u{wp-Yh8S{y4&z^CdG=t4F> zN30$-phwz|fz|*)i)4=@rTo?@apo5+dKQd(-xtizYmJ%Cy=H|AL5u3GD+tD9a`&)Y z8(B$mFx8RwjsEE}rZ-}}$2>PdYedM+%lk`YUc1l9)L0gH>aKbyG}3G(pM2!6M(||r zKolQyuOU|HB!SPRFl=lfWjtqopi9O=)`fbvu4f{^UW)J_y|30g?|sJ&=k-KG#Em`3 z$spz9zABErwo{L?MkwQZA%0PEtF3ttzS@g3+i$Q?;b%qf$L*jNPP=WSaF>`2X`K%) zJM@O<*Tbc**f!lBm}qW-hPDFMBRGS6xlme7wFs4%Y?eJFZhY{_rks-SK$yuT-Wb{+VH%a9ud<(_u&mBY92r;BrY>Q?kMg~T<&UMU0h$; zK;K~L*;zHA536&J_mDti_03XR09KK`qB-N(#k2XWrU7_cIRBbh7Wv>wev zjsoVX9&LjC**qvjYdc*-ITv=eD8OZ~46c$4au5;T6-= z>`Ix}=aMFS^}!J_U`@B224Devf*6+NBEvqDiI_HIBBv9Mc{=%}neT(Vzr|WLqlLSguO>pyTWEdKU&+Ki zpL>j3{V{p1MbR-U=A+CaHnmzuthiiQi4L;OYoDqqK%OaxPTlNXH`94{asXphd2Hge zM1|r!Yp42~;=>e~T_fykJM(0hqrF!SzG)vDle{^vcjtuF_II$Qxnc;bU3PSdsN-XO zWS{p*26ODvKwz26iXj`S0DA?D_gKemlTO?(FLg5L@j@5X%%OE?&AcL8e6qCOjtD#W z(xLc<(EMoi-vA{|DLg%vxd1MMvM7i>W5$qQsJ`jjsDNB!d0rv=VmTiN#)+^{$ZRc~ zb^xB!Z?aG?31hckyh+XUxyszu3eG5Z zQZ=qeVz1_#w1@>2Ei;|#Vwdp>bFZDr1u9&yt``RO3!$<^0LT{C6a+F(Nm$whuUs!p zaI>>@c^p~`(TwK-69q1zC?cU$g4s+d@>=5L({XZW++0~6DVDiGJEbZ`80tjO7J&_o zKg??)7I;}O7dd29)4{>6HR}l0)6Oh`B&U=LF(iDYIa_dnhS}cM=`m8xg@|Fun3M63 zM%_YteB^4rK)3+42S&dTX4iI@1MNcOwwA?2O7Vd|nD*EOB3$ie#qf@wNYWkbmfxlQ zwgrad1zjlUnbY8if|l<~!8&CHDL44hA7=QnCmCbcMR5nsw9UpS^MQYt*lCv&HMg}o z){$4bmAh7w*Ezh?wgukE4StbV`fO-|C;JMAk=3{?YFgmr?DL}o$9r4Ph~d6TfAmvk zot45#It8O&Y#s*Vqo2yoFrM;?&e0o~to23j^|9&c@lOpX<3x)hQ*|`GHc*LzllcWw zE(6LOsWJc5$$?jW(I3Fpy1LBQ%PjIO@NE`I&w*?UqYZ?nQs}Zu6l>jv=xo z+KIVIOs68`eRW&38(k5(po3!nK`@rfXcugo6;|7#5!g=WaE7Z{w7ql3QCA|r`J>Zr zUI2HLzA2sdU+&XX@<)H2FVvsy4Ze;l84QLry~{uDmAvR7=4fy_s!YAKSPEF6%%DCE zu@#jbDdj;)DzMQvl@{k(a~-txmtL4zXtfVg4ZdhT_wX^2Jf0*OIxf&Xnc!fa{?IXk zeszge>)Fy)PSi#%bc6xNim+26M1LKUn?OXm$L{#)VwU^+TvjQ6gGo*ErP(}(t*#L^ zOL6DnX^Xmj4J01lB+PcIyzmQs>5C^##SeXO(O93$8Ehnu8VwaD?jSvX539-@9B7U5Bzr@FNQ%Ymnnh-6QZ zh5?Wx`J-iumWIztDCwm;5 zcP#hMW*4{utrxykBq>GtZ*TX|#ZoG$DSxk^DUY0E4Dj+-GDiS(KX0DaRTq}#YRu*%uEaqBS z%+*XpR?okc~?^MR8q*fYUw9!hta6w^M+{!3;UT2;V4sL**W9+<}38x`KsO`zU& zd6X0U`fU0X;$a?*YF+0*j`B`x3+%^cWgbV@VzN^LpJ%7!yL{~kbTY~8{`Ima*0hhM zkJL=GMKYZQVp^K3CiBO26u4%-Se_poemt{AP7=P@Fu20I>TT6k(0Y^VqSv7d#W%pt zAaO;8M+{FU50Bhrkfkp$C@3~JO{JJDvs|=U`_m!+wMlQOC@kb?S#JY_j!Z0jg%BAf z_tFr0X+SnEg@~;=NQUOg@)v;8MKs(V&y>}%LnLEcN>>}549QVDkT zdCcf+jYA}+_y-FL&Bpelco*CA=ff)7I=X$o?%lhS*W{PocJqer4@c02wHIYB>He;Z zE%`sn8n`kqwmw7<^XTHTcKQ9LtNbD-mCnP9Y1Jls@$#;V88P}UUPcG!d4f-w547ph zcrMyZ%Kz(sZE|}Vzt?T}sSTZ}mj6&2PO_ojhQ&5qYQyz5++f4IZ1|uJx7l!y4d1un zK^ruk8fhHGtjqYZy=!^dp6&4#;ec*ut7GV1Zmvf)`aEVkj5HoVq`zp&v(8}6{- zn>IXX!+x^g#c!|;$J%hZ4fAcd(1!IkY_{R`HoV)0kJ)gW4PUb1yEgpFhVdCzzC&#| z)`rt;m~TVFhK)A7)`qv+P$U00{wy6T`;%BRnrp$kFR`Gr(t>@X?zq?Tzi`;mzemDX zlvGuhm${8v_od~AyL@St;V!K$D|c7a*Di9`)z_AmH#Cf=^Xds#T3=pbl=uGTKE6Tm zU;k#+2CB>4HMNpfd8vG{{Yz@Zv!be|%w4$5sI0Bg0Rl$J!s>E@N&hInF{A7B*YQNR z-nF-yWyPiEI9vyA6|IT#g`P9EG#W6ueh|b z>axqL7uD3(T~Xg)1Qst@y6nmyEx&5TO1=Foh}8#bjH*TD?(+Kj+IqKANp^)4<)1Tm zuH~z}=H{J!X0KP}JEy>#cXp4@obP2#o{|*rt#Oys)m2xOmKar3b!AC|dr=8&Rf4}^ zlrO3?gypJhOJKdqa`!BEB>(EFh4m%%%iL8prM30-<)udTvhneS)#W7(q40BIM@&CBn_ z`9@_`gS(`mp?uN8>SgY-Kz&usrS2M%S}bT#kgA$0qpGC3>Pnq_e368Qx23@4#B?tV zT*|w9S#6-cH?HH|d4`*yi)tGTcXid}<)kjfsV{E`R2%Nv3U_Hqb+u#$r39x_OKTU^ z=_WdMLTPpVN$!e3O{u1-ZlNVTNYykL^?_1@!t-B$^i@|ElvLH|vP-!qNx5~?tf>uL zTIp`6D=DR=6TG^XY!4$?Z+cDaL$B_#ms^!Lr^uqWQ3=wuHKpa_zdJp8=aVJ*%px_x zu_u!<2?PF-RvDG_?`6Ufm-mh% z=^mRtcBHZrqofBFolla*3cZ@E?hNY7uLzVk2y(*xbL`HCN;S&s7gf>FU`F8qX$FCs zK!XrS3r5PG+mF{9?EN|$=aGlen7q2q|B9md~|#~1EK_*=GL_#n^3Av<{FV7+lXywp>_XI zE;;PImG{WlC4qk2=bf_@hkd`c&pXx=4*Sj$;9>7S?epHRvGMB0RgDb5(N{NKy}B_q zHkJ{1&6+hJo|V;D*tk|X)z}lW3+Fd7zA^|G7On*?_t?g@jl@z6!b6bF04p#v&70|N4G8+Pfdg=x_aNR!9CjJp3xv^UtBa+rQo^tX4h$qS(Iu zF8?C&-T$lW-YWc&wOaW<%>j;8-Txfl@fWEsx>PZ`c0h zx}R?N_v>%C@n=83>E>I0aqDfry!}^q+anC z*dHH%;>ka?wQt(IW$U)>J9a+x^fS*sx2xm%7hZhn<=wCBdG)nFzy8LXZ|(id+wZ*l z-uoYzoqrAO`|zWWyFU5!v(LZSf8gMkUw!=zmP*xsbpmwk3C?$#0R6Me|Ig06TJZFrln$g7s2Zz$PD${Cwr5 z%n{4$tv994u3dcC`#H?W@lrO9gFd?>I)mbGq`jvboFGc#2wjxbQkEe$C%OovHM-gA*sJSIZpuUU`{LZMa zvRz6QRR-!Cy5E$VUtU&I-piv1FI@y>5e4vABF#L?LV4) zy0|UjIdpR}xsq1i#eF;59Lf5fNH6)7+LCv;|L}flIR2^lJIl^G{F^gMIg92TmTrc- zpBmtpt>U_3_eR%6WeGl6Z0x2Ck5$7Lrne2QODj&zQfluwQL9sGeTGu!4`t)`ZHo|& zjChqX#icUlq;(D2o6_NGOR7sOPAGKri&FjSqp}>SQ7ZL;YK4jEr{eN=}w9&>_0G0 z4J=Dn1E&m810AU<0a{8NP*+hWD>Z;e@VyVek8%G5cqM5Fbhs0hyDUYyi;|U_eBJfK zyR6ztt#c&zQ^`ggXLEs*65AZ;j`W`to8?G%s`N6RqBxb#xAaMbO?9eN{8I5t#V>VI za$Uwr32MlcGBw0;flBTgus5+IzRg(|SKP1As_Pvf*x#L`+*>k~+einGA>c4rxg7&l zM%R$NX&pVZesCHSC>|-tg&ZPr^p95k9gnLh>O<4r=&v%!KZE=;$UkFJTAL$19z1#A zyL9*tJT*NX@litWtQ09r}TkY1#I zBQ*Y@PpMz>+-HYB4)>EhZ`tpTG^a{4c*^2b8n~rRN@+_u(yt?u|F6za>K&egk@%Xn z@zAzEw1viVlIt8U_@^uZK8jbadiW?YN+mi{R7R%o!h`U_AK-=iH7^Js*DAIAED)&eDCBr!wz!@_wnfNR7Bzoicy26#Hm4(T)JIEf!FHu zmAaoN5@##!Z+IecELtTiSCLD(9)MOuoN5U84=DnY){seq>U15wlt4YjQ%BU*oRqz~ z-g}pIQrg}@9Vy*>GN4$gT|6so+#E3u6*Ci_wqc~)XD+0@@!Uo@fqlRK48L1=gtrBz z42cK7WN>q-A@zg0Quew!lG+khc{ouJ|HSmD}bxFmEg;u)#;ZLV>NxG4EbNbck{%}rIVT$et3B&gY?yoFX z>MuNDyKET~z42xI8$_A)mQDO6(Nye#3 zxuc9!@*hNf4OD|>4R|2F%el8-M@(Ck-Os_k%A!XK^nedvNT|!0m~`40BUz22zaK_= zLnaTbAJCP!H@?H!7U>_Q%~|o_Tf%7G9T24kOp4F?du4w32HFu%q|A=N@oF%*4hB?&M^fOCWO&2{%?GFv*I7K0qT5RnxD!X0VKw}t`)>LP`VhOX=f3MUHy?Ll8Ma91W z52eZ&$vheQrb1t20jnP`N`xNt<@NAIX8dV`C#P)ci;du``AGN>9!j5++SOBw@pgMl zA|2AYPTDavz5Q@GB%ZPI@A1vPZAy*Y-ivQW$E(p(GSui#hjyj!9o&)HHn1+GI5{HI z6sDv`tJK?*>s-Y>{m-sl^uIj!M`$2CF$ekQ=>1SvPe0Vd7mnB{6+4Ahv*G>KaOA*V zB`Hjx92sL65Bt_yp(V2|l{(Y3hQ>un&^l42UYA^#l_I@?^{bHm=&s1yk?>#o5*Dqp zY<-4*=}TDj_-E-$%ypbuUQ=GrhS4l*M{Jf+U!A*{y%^NF`DTb#z$|ubyEOyqW9FAs z8E4ei&t+Gpy4;$Hs_WG(t=C`&^D6aV^xSe{>TNbj)L&9lR?STQ3rV%0wk%Lxeg+$} zXS4r8=s&C68uqSc)w3vDtmBlS#E#{UUmfQ9Z9_36t;K zrRsAjil9t;R$wB4fTQGDC-J(TTH3DqWtWMo>5=U zy36g_?X70VQ(dIXQYa);MdJ3(DnxDzSX%QR210-;`^=0zo-Q1OO|06%B zf@8#(uhz!QuPQ5_RasJBR9hfB$upN3z5Ul*K17clcK89%WZzXw->!^)`VblHJ9t9nIg1XybYSeajDsXxDt}f%Nu5PAsGbqsUGAdV2r<;# zy+cuMkJa*o&eGP1H|ua8!gNah`C2K%YR+n(@Q36cVKa4)MZc;m!Oo{KE&FYhkxd58Oe z^&5g?FP=HCq`pd&HN0we?wqr8^I4xOt7d_-GI|aw29hrA$%<2UPKEV;g3!XQKxv~& zJuTR4Bn+5yVF3LaX!hUr+na0YV@1-7ydSnpk{tPZY$!6e9vlqEvsK7$Wg(q41uH9|xbL+k9GYfJgMgJPnqxnbtqz_;TUbk(* zA=-Aw0MmJ5d6Ibg@yG%CIG#ivrwzqV-UU7RmcSGFCh1CCfi50NU%DpoOW|P|K|kU@ znn(OkyDC-I{m)*^nLG}|G(b<5fn&1=FiH_eazoK z0-OK&G>@&EVc~LY<$(WrT>nuy9+L%Zsq&aC;QmKp^iNIq|8AU*2iKRk!Z_MqHj1jT+uf`1W7D_A9sb`G~)(4q09v8$R?M!+Y)U z4-KNxDkevJ4#jm;5C9hrf+N2}Hzqsekyqd-U zy8Rv@w$px3zsbtyzYEfw^*rDunfGzuA6YZbQR{l{PH4VWAB@Hq5r+6dR7UVX_UC4f`{Ji?lf*e55^&x2mE0 zug7lJ)iW(R{a4{i`xogi1P948f{XA+q>T#_jZDzwTh}L6KTtTgNWA~kze3-CE&g7c z9`4B&J^J=fecxqVkzWLgTiSdM&jmcvUT@%ei037q&v<0}GK=SIo<&l4evx>nMk$%g zF5$VJ=Ruwqc|PSyChP>B0v@rh`~So5?`fAu_4!5Hzew4$`&sprWy7&Hblb2uuSMeg zKMm(;3GWG;1>8Y&*Qic0v9nTPLFda&U~v27!WH5I27l&RTGck z&gW z&A@qdl2dRm0Jie@a9<02g-6oa13YPhQu+9w0{kscG46YTKcUkwaBl#vLZ|XZ+|59_ z`%dVy1=#rm#sK{H0k1fny6f*yj{{%l5qt!GW4i^;^jP`&fcNuAUIHh3iGzCz@KM|S zIM6rK;wyoxcoIp!88~GY`;oW>{*LE1I9>I#7NjTz&UwVxr%_hcsdCG4KVpEiw*)WmePdYLxGd#S!FcNV<(75 z%J>Y>JD)ltd@*nhkAz!u_-mf^l0I+?kL0xjczu~g+bzJ;E~Wp$zYw^F=XKoI0ypspK3jmX zl~!ErLnycH7WgwB!RKb+(^XdeJ_Eeza>`CRHv_L@(Kj6)*Z@4EhC0IS2X5f;h(GYm zTC4@(E(SL9EWo`5I2rjv+Q<~(G9Kw4mIF82?%S?_{~IU^;RSBtk?_v|R~uGcHv{Jf zEcXK7r#y9p{~UPLVv9c;f%|zj;C~Q!-U|2z_X1$cN@#+6J@D2>>M@D>1zxaKEB zOlYPoxD$bSE#QwkANV#;Bkp&BXRn7ZaTfw#<=Kck^IG)AuY-SZCj%GoNZScTE&^_~-IdpKT{n-^g$Oj?zmfJun%Tf0kJRIOVB^mf89FrVz%8^A zIQS;ZoeA8^lTMf&z_WfqedEptF6WW(0<&+m@)B5h8~%h5cny!_wHA2uFQGGTfl0qY zh6H~%a2JoX>ki=ZJCs^W7=h|eD}8}?@!W`i2XNo7p$~3>r{7Iq0}dYG*586B?&0^K z>wK@3eiksuBY3U{Zs+mg#(s&4{+-3cF~B={q_4Xh_~+l#XA$Ogf%h{;;}-bC{{t`L zE(4zT0Qlfu0G#v)^GDoMfKTv9J+=W~-e|e^0M|Ya&V&(ofJgZ4An>Cntg$ciNn}VK z!E-6_z*g|beGqurcFG8D)xgVkL2GdX&+mXga9;@ggh%+{b70^_%0;~|1tz}?&iD(w zi$|9cxOg}11plSLM|dRPjliZ?!5RN%VDX#q3~qs4Jd(b^H{P;vHi7s2#iDZ;@CR?h zPt=Q?%aF4Y>!rN_<;=rN;3H6U`^7C#^!CLq@MYWm7Etu>#b2Q4$BSE_=&y@g;2E}C z;3c-Z0w_A+5=P)pZMW!ux7%)kqMt3e2^4*22`^CekHuf0=u!2bmiE2a|w literal 0 HcmV?d00001 diff --git a/venv/lib/python3.8/site-packages/setuptools/gui.exe b/venv/lib/python3.8/site-packages/setuptools/gui.exe new file mode 100644 index 0000000000000000000000000000000000000000..f8d3509653ba8f80ca7f3aa7f95616142ba83a94 GIT binary patch literal 65536 zcmeFae|%KMxj%k3yGc&SCTD>S1PQP}R5YmQ5=~qJi^+zl1UE)DtPsG8blp-*!#RLg z0>QIub24npZS_`f-)#|`^OhvIcH|hGc(UT^E}VYJoC(K^_@EDjE;rth;Yer@_4k$X3I);E0Tn+-Zb>&yT9Ew!oxAMfl)C z#Z+d`C?Ev=lGJ)}%Ksnx|0)G)SVf_n2-;d?f9!~MzIJJ-=wKb=iHfW2QCpC29wSNm zA=ztsPZ<@3t`2ENV!bW?>DIbrM&c*bCbqaRzr~R~Z-r)Gl=RG-p}ugUHp=<&@N<(0nQZ)pc;t^f@UfdU)Xs*a2q9hEj|W&QGS`}Q+V zaO>`-aSJ8yAtP2OBNk%M7Utt!$6gfgmQ40WtW_PKSW_r1oOg}p=vZj3XtBjwwJ#E} zLMNCsnAlP1f|%AM?kIHMo~S5v2kZEcbEs|ZrY(iCq{N>@V-R$%P-2fEhzyjmCh@Sy zXyr*PE_By~_)26%86IRFp9Ya zkBHB1hGv2=t60ZM@2flwcy2#L^lN{0=%0Q@MjzL)ErkWFb2Ro*N07ImOt!9YmgwvP zqh2yflmnST)@Q6JEa3kv=;e&Js^gRcx7ile@Me+Xh_`B=wJ3|47Z(=9j;P;M4jj9k ze|zYYnyGIobV=&smWsjxVw3XZ39!ke-gcWd&f8i_T!k-^@^CA0*s%-oQ>v?$_-7%o z(GNN8XT7J;F$I$PlNQv_oLiavAq4>E7I2dQhlE)vSn!y;BSSI+5(`L`#@q*i(+$dj ziMR82oKzstr3NgrEei6^p%m@2rUhVv>rK-H3%XZ<_rUh;c(a2dG)%uOg$_v@w_EZo zlu%GsR0^7TQkP%ahpqsf^)t)7t)|hz?tCY-06G}<$V~#?~heoED!!4L2akG@t z3k(cUbnpdgqwk%>`n0WAC7vv#rU2V~=4eiAwpse1#pRD3*UlGpF7&;UP%~^>-Uq9> zqqY#gDuX1JM-HRLrTl?xL1RW6Nzt8%&-UwXtnfuqbCmh#A4k1U7-%L3c7Zx(d zuhG+B-K2d4zoLVczO#ufnYJw*t5&k#)-NC8`0Z!%(?;tLH)1SS=)o%@p*m1Hza}bC zH<@{EP=$nZv|K=--J~^q2RFJ=UsK7|s*{A7>2riBOI3;B9VN6@g>xk)TvhhOKNMSeI?sb zNT@@qXG7GtAEH*Z*I7+?xX^=^+#cd{e*xu~c+oK%QC`k~8T1Fj`XSd4etuu)23Ly= znHbY_evF#lbUsH*M$@PjpbB6kZlDn4%Pfry7Wc9o2a;HxjOT7A9>$Ks0zkIpxF}-P z4%J+UwB{X!v+x4JvU3b1r4SD4dNJCLBe`P~a!!^eLzUU1z9JMV04G)5v%Ur4xPh4u|g#Tc-(r0PB00 z<2OM*Q-Cajywm3kTRsx?bLZ%s;?w6_FF__SF*1GDPvs6}`fAHZ`iq5gfrnJz3GS7o z zuc4jxwz7KJ_rCH-tFJ@z@NXc!Qxa$m*N_NRtT_d&`a7duuH`>P zd%}h`&|B{GYny6$%@oA-ep8*S_YbNQ*wMBx)7fGDgK2FaWZ0dLJaOehDVhGlqZp`r z7Zz^Qt{~7!1nOpo+s>!!UDMjSGVG3o1-MTD`U{)X0)7~njK(aO!mRqVS*o4ZX4diz z7)@AzBH#*!OwC!#-^rCEBXGL5j{ilBGXRTvrZEnIJKR9see4J z?c)sQ$RrZUz7CZ}&@|&(WWQ6oZG7`cz^_)daDP69Az2FAzJQhYnWChD$L)$+G%bx z&7w9mR1|a&sE6y@t-J-J@>a|Gc{fUJ9G}Xg6OuprJK#0?Jp<5bfq@`8o;q|BAqcJM zjQ48!rGWu;JZ~b>4p%t2&K3ny&6 z)6|T!KS#l1EVxey4i&6w$J3D-fJnmY;zyL&4M}ieC4Y4zD_DwoiJ30 z5_=SJD^>f%DnzwDB3tkBl@`9nM7`62cB()9jX5~Dm1WqE>OH3SAe#W)`7_C8+pfMB zJFd=-^{P|*4uT0K)k$y3)D9UFllj~KNTvgXauGr@LJse7Q7R@RDA(z2H9$+ML+eE& zl=voVrX{czY;0=zrsg&^7y3DBQcnlbCHkTK6wlSv)Ot^a>WupS(t25KWYtdJD_Ul0 zy-WLUG9529T3YX>gnVr^CFHB&()t2Q@MyPDf=8_?tuNH(m)6hH=0j$@t^Sg!YDQJ1 zuYFT*)BGE?V&5z3C3>UFt~~e`G$NV?B%)>wUwRqg;i@z=IXRJXAM6bDgMFlKS|1}* zTJt0-&ot@>P~uYMKt_iv`@icGQ&50s{!#;tR+P0W?sZB=UJS z28Qw#@F%T&Xsr_aIZ!Op21>PA8)rgy4p7O3{6Pz%JAtoM$hIO)F4a7n)$ z761{^!~%XE(hSewuU#=}f4+5c{H|(n(tWZhp^o;Mq!< zRjo5}SyjYX;$XSHob{6zO6oY4v*QvB236~|OfFpmxC~b5@TKpZgpU&#G7W#1xq3O3 z<3MV!e|?(f)~nX1p%Pni43kl^-$5TcR@NVMSZL^H&E-&ixCRksAc zLU`VdHD75rv;+qczU;=DL2Y_V&_vjEBUm9@4-7a;8wVN=CKo8r`Ay}yo6Te;LW2km zCg&ma6+&MnuR~}6p@HNqtG1-l;zB9z8^>xc|3Wh`P+C9Ga0W~Xtd-{^<+-e)w&b4$ z@#5nT;nQH;igvjVF^ojjTuW_pKostir4{9NA29mEyNid}uN|4TxhrlC)WdXd>FZ z?h-VBx_toZ4Q;2-s*De{^r4;Sf;^URlfi%h+fm{Ob0O76slOabjS9;G-(|(y5k&(3 zek#h$5I=h*8r>7(VIL+i{Pd0V+%%S+M@0Bp@q8Q%5#q(@z7U^EjPS`!G$(+(`k}%- z#O*6nN~f#>J!8|-`3^7o1-QI(ZAuFGL9cj-g!Tk8}ZggIXanNhBaH* z%$w8Ym-akCd{i@ElJ?9)6rRw2KnzPg>MHL zWA%sB4CVRi!%2H|Ot>Z(icp)l{Aa9616{Nh!pveS`i2Ma03DLWEO3U&EX$~V4~xO) zi_s8B{5_ln-a`((@w7x)Y?Ng>9x2X(W=@XB{D&Y@N&83*@i)+~?fi2zqnK&lp^`u!hZ&&FuC{jXb#dH{4o*tBfc6Xo9PY^qOa0PMpSJ{ZCzqsyow}p zf%MA>yy z&-gy^>=Dmb#gmKYQSodQ&%=1~zFyPB`l*;#0}pG&_qGPaB!9U}cE=Aq(N(&^msURe%fvtfy@-U04P7ip72!ds&zS{&BQP zfb0S1(?^*E(%8XXe_@jn|0by6J>q*uiPa<2GTum>1O`T;OFUo1v-y$F@r)f;V$*<6 zxxSwOBxBbhyp$c;NNYJb+cR(3rm@O_gUW%XWqQ=+o~LhwQWXHG_$SW z5jNrvBb%>H`Q9&KJunO7*TYN%sn3?(GrjM9l7u$cB1!?on^i zxm~?p=dyZfRh62Dm=dqUXFWmia`&ynVMq6Z;jpdSi|}><(*!Z>E*$=p)}4=V)0bCj zv$1@#`k8GT@C_RK2^%GGo{Z!or=xEdC3Sy{6c(r8w_3+22VPE8$VUwk?|v1ZjJ?#d z?luIe*vr0NEPYiH|0;?VH0b^(Q6Pm!7br@3K$LQ`y0q!bh+5I~B~(@{BERM z?U4}bzJtJg>$C~wsYFPs)mz=A_+;Vl>b`0??CGA4aEpE3_1cuC2W)e-iRD9CL7-ID zLCiMic?H0A0^lhkGFc%~0KX@IHA?JFdf%(WUZeMSFj1hlro{Hsd$SVTOYdb$?3Z{O zdx;woaT2be^4!6ovG*{7T!u=A;%kW$=Y`c7EJ1>o*h`$ppM(Z)v6oxb##)uwlhE!L zK|BbE?rM}zjMBeG`2mMsRATo-#`XSMNL zPiK55szNTw;(m*0{!-DMiCyRLQJA!hU8fN=;!ohIB&twBXPo+q?3dk7A=(!wGR*;f zmH4Ab9Mw+-q9dQRF(aRtkO%#|sinU_GzQmLfG(6X%$CM}s#}Tu+JSZPpq9P+VJHV9 zPKiuBJL5!5YDD)oz~~%Qe-}8Rt@jtTDY45@HnsU*=;L2kq0UjBUo;Smkm)WFrzQsz zaZ(FGek(>;EF>{BP3w%4xKbs_@hyu6ngw8|fTKh!qlHy>F)CtYnXuY`0oli@9KP4p zxmNRteU+CaBSCFY-H#O=Jk~#|5j}R|7;01ZpAg)=bGW@hevqcf-LE5A?_aO{-~#Ga zVjtqE_ur%Jcu}N(Q~CZ}jI(RqYcK--f` z*$u-u^BYl7987l&tm;-akLp~@;>4P3jf|vh1&xdm!gT*1BCt>!eya-TOo@qvzBZ|e zQ2iNDWtptbp?AvNZz7_NZTj+?+C3IKAuc7urGmA#W*FkVeLpeU9(>ulfC;|b-cb+0 z5TB6^X%XtM(`pIQ=fw7l3m7PqEu?nW_-d^ex*@!pOr$qxsd${!Og_Ogsu`H35A(O_T{B-&NY!RG*-ckbdHk+HO0|vjjb;+l<6Mq$Ue>zCnpS z2ekn9jv3VFG&VekjGbcGz8tU@^*K}|I^kYGwg>=6O-KB9C~8h~{7t+%<45rXFG$@q z7euEagA%`$O73*@wt3Wii!!}!nDQtuEgDEVNO&H@L}t+dCE6duOzQXu&}83R+a_*t z_&PR>?K`O-m-^lvXQA4JXT_&C#wmJUf{F~PzJ;U$!y{?@r5_;)a ze{z;kSR(>#DXe7X%}ph+4-@QPELf`|eLpD~P<#ctkO^UZ+OJ**V<{Lc%j&ADlKD^D zh9X7D?5ESzvDO!l)qQ}Km>9K-c6Fh+qFvOf78^LViKdv`C4?Z?Mm>D}Ux7K>T~>yb3k%G<(9(Q-eiF; zW^X3gPV@i@BfZ3523R;XaoaM4t4g?fQVe|xA*Ok~9;8Dmc9>rVFv`@;FdHt*cs>|&PpyPe0UP`2eD=g zvFfgbQ|!MPHa(pX@+5W&jIJDok-l1%npPJ!4WXp3E&+NLPGjwF!I|Z_iN$Cc<=?U^ znZZOzzo$!rJI}YV`NpupW2zzj{GeLXVuu9W`n0TN!|A}^<;Os!&SP2^>!5w2kEXSK zlwqH1ZHplztSactN=M`gEK3rV&LEFnX(6w~j-W+mrHrb}^}uPE_qw+H$a{*Nr4ow8 zzFGz?FS2RJF{5dTqbb?YQR&zY>tcGecNr|O?N!1;-1-;v**su^4QMcbISfGyV8u(} zHrJScDG^rhPt&Lre=8-P)A48e6~K=WdCcfqdgpaqO6I^4`F zK}}d6kG*)cjinU7J8j5RgJojK+lx)wDSSUVPHfMn%&-B(Q)XB@^Sg$Yn#i#yh~@O~ zVsRFx43?7=Ef)2sPGY2yYNLx2@%IoSZ-cY2)IzclGvc!#BZ>GNJRx94d^Q3p^_h5& z!jF)M8oNlT7}k16tTxu}c%&amYj-5hh}SOCB5QZV4~f@Pt>X1d63xedAT%NiI1<&4 zPEnH$n$emj7>RQLVK)z0v#L&k)I^8W+9{AF*2UBSh?;rJK)tBMPMUdlAe0b@qx*u0 zz--_|=gQGEUJdhoI6@_ud5iH05LI|VzDc?VJ|^iFrVO)~h{mtX2Rs^&JPJgM^)vaFePM&_EvDU)I+oE9Fs07GIqHqX z11^%P9Ja(^f5Yo6;XnHbcrS5cpTmkjM)3ePJsfM5_ylButt7FO8?^&$xs!Gcs?X>b z2Gv#YpGi2Dv&9d&6BQ4+j6e@0KF|+?vzxumV=x1vQd_)ri+|f97U*XuQLFZPQzNv0 zA%k>}M&Ys)3L$~QjeLSY;hfdNb|6kIP96bux0l|%;oDvCM=09?jfL4?gx*}APLf3? zdW9{Oqqf`4JW7W@2etzEbQtSkrV7NztT#^ri)SK{5ncM`jbVKA(V8A zqm5NETDO0WB>jd|L}{&4iQSGss@PZfoA}gSfE3HzR_E;{tLUXvReu=XF_)L7-vPGW zI1T&ug(LuD|W&H7y!uIhCFTlmu0not*lf@ z%PpJ;soA9gr~1Dvt?jQ$qirwINSJ_!P(z8X|80r;trDZo$YvUmPe56~N*V7}HN7l` zUbJiFQ3s!dfm&=5g!m1pD2!1O-JKPJcN0a2?d;iL6=5p90XQYcAZI!V9BvPRgvII= zWVx{*aQ%P2W9=~sEz*<6$Ha^)DE+C zm#>U`NgC@|U)x7%!fC|bQJSw-Fsaw?)Kw+OUnVmHjbnB*a9TIrTV@F`=E$%dDJoE{ zNHOPT@UOs6VaxZVAY)PTUsB>f>;z*ISlRduY1A6QU9eATGOKj5!%ZL9;a7P+P4oXu zhQz9+kmfozzo;Lh`0P4(oZbabsc?{gTtRZ;^mW2kS?P?m-mmCgUm2CoWTw8v>Cs;? zS0SUm)`78mC2JotUs5$NFlJ#(0K^R^uLEPJpG_u$FQLQ_~`{8sIac%$yfJ|br?mbEn9!Zyl#plAg(29qyxaq993=Nu)WqY^=ggyWgg5_M&Y zpdmD4((h4i*n9jYW9dMOmd~&%XK$OXUQ@bM*2V_;Erb~neJY5aoK)H1r@w}B5jB_~LP z2GvBz@Gwye!c#g`n=Ob@$5oF-2yJ2=AEdmT4d;TyC9{qB$;>+bA$=O^jVu&HK4E_b zWIKwTm7;yh4(lJs-b$e-^uex8 z_YNtpTlEe_{|I}9wEOK#Uk`1z=?18z#e^6*kkn=swo*x(4YhC;wXpuQ?+@x&e6FkI z8K=b5&i4oHt`OV^Qc7$M*n^!!;^NY>CiIo+4e=k6IRnWQ{b0wsmK&RX%S`$|=X#ookhCNZGc? zMGp@>=Fr1Wk03o((_?+&r6#oIX6-0LNq?%hiiHo%0Lbwe>-T3`g2EIsFYSshpOGWKvb0B0J;;R3Pr9Ne=4_JFJCASN1ch-~a<)#uLsJH92a?)!t@ ziGq7585s9aau52IEp^!s7afJ`bq(Jt%A&4Fp#vW95D%=z4hro*uT^HX!3zQ!R7%dI z%{YlkWf*Ybj#f5>UUqM5dusBp-*XyMDxo5XAHRVjECJKc!11LP6L%wU4tUl+zKk7) z-tcbWELAvkSWx|4Lu$xv}(&QQafl&5^VedHR?41qOhCL(SzYfG{apR7rXi zehd6DB<&$TH((+Lff_Licu&>&&Z=;Xa&GeQ02a#831Q&@0{)cwt77%-W*x#g6dew3 zZ&xR^NH?~t(2;R}5E$jTfD_!&veX^B!!|{mD)!dLfiakI7!4&)nwbF?Q56J6xBCB<2Ts%>w%swm z5p;*KBsC>VeZc1WcEMA_>6oUa+}=pE|FnRHTlYl^yFJg$z<7}J3wq`~P0uM$(zEyp zdX_zo=h_{4hs7)BMe&;QsCcD6EMAxH6tAmx;PvNY z?pKA-Fd&Lp!bN`fM?ZqJfYZweK*9>n#u>pxsO*bYa7Ws&dJ+>Tb%xFz>O`IAsLm=O zQ2QL1+O_W+C!P+B$?f~bQkVu*9G$TNH?NtfET{|e3vWV$wJOgaW^Kk+2kj|ub+&!r z%5F<+b^ZM3KYxLSLd)A|w*O+oYkHMGSoBW;P+hf!CE(DpM0 z5b}`~H#WHA9D{t&+~_d#B52-Al#k5v7eFU(YjZ4}1Rw7A4d+_op8>QZP6-}Zt*%b& z`Wy+$bBC4Z?7qXBCKR>#gNcW8=zG+2J1;>KfMPkenBcs6613dtOvDF}1+@iHGXVyL zyW9I-&s!VRgnTfUyT5WT@?XTEPx7$YC8f{O>dh`&23to zF~!xgBb|y(j-~lg9wm7w2?aIp$RKhh<&KyLNYvB=$&f|G&iHAR^HX5#J#vKzvqvZ; z5zD1q_M?eAJ^F=7o19IHb5YANYaSx^JC#C#K4-ABlVk?97?-pKri`J`C^lj@Tbt2mo!F*JPJ?y@BF^sVe{vm+d zqdEL61~0Kn00=xne8s}G?|LjIF2RCpJ-QOp0mYg#shJ`Ey|aMdO+dz?2ouoA2GDf? z9U76r98&W8OgoJV_Ce35rr%IF@VKibjibJerNfk0;jX6-4r)_7(zBJ1RbB^Yju~&e}L^~@^yQUlTv1@ zBA9`54bp31Vp;A`Vs+FFo;0-R!Oux1PR36uu}UPq&R(Gd?_QH z-I&v|IKQB|xp^Xe=(awPG&MqF<&%bKZr+(s-#&t279BQ>_IM%5!-)So5yF^4AhqV( zL(&Wq!DjXrC3Eh!|EY z7vSS$K1aFuPf!CESr0vX5x~160L22pe2&WF2S?JMN02hMS{W-)vY$P42(hb(MT7jG z0Kgu46=5+oFX{|(T_hbv62&x8SSw;YiXi4Zi37hwjAfQJW6M;XSo$borC~ii8Pgl{ z23`)Za5%9Q4#YA!CT!oYBo>+6HO(c(p3ZS!CvGTNzSBX%-rEqrFFu3 z0Co?&&;<_o%rvUkg%%s5cxToQ5N>rh48y<;K;Ii;b9{a3 ztU9BFw-Hxj#G4%AwBo~BI7~y{qtquD^1>whtP>}mT4}6p>h;5OwHsqC9ZqIF)>vD) z9`m%V7;6i79wo0|ml|-tf?lQpw*fhjoj*v*f!0om%5|)ayzKeCsC3kNR>)f$KpTZ# z(oS2Gu8>(A12ijc0u{}-(1z)|n~*@Jn~B)-r;p}a=23i*SyMmcD|z_=^+VW1hTN%f z(vZ(5bO4ecS%Xg)sAi!w$^tEC9))hiq5*bPOw_*ztWpE_|GlaQ{!Z2H$A+rj`9D={ z=EZ=LI3$p&*UY0PvmQ`%vRUl96ePQckb_@ts@ZwX1kkaveV8H>K#_cc^bsVyzH^9H z=5C@AQ7jit-+@eej-XrjZy-qM+$X4WAH<%?*C+=za1i?FCX6GUl`D33`!UI0WNdYV zc!d@**%TtCdBS*zs2`zLnixwFCz2Rj*LOTbOR4gXhi*l@yt6VwDin(KJ|WcL2{ELQ z01xS2_@d%yBd;a^VFhp+mFvhrvzs^vVRPd;PL|GLdruy6@N~4G9q0j96kkkAf_QJX z2+%UYGU1xVL=^aR|05&-o+3oyB@x=T#j51j9Ez_8cDG*jM$lQ1uh>l_uohmV!0kO(LP#4N@EEUEoXInA56`O0t{sKJlZJrhT*oyhB*gICN!iv3O#j32> zek-=3jJlF4`2{6_TwNHotTB0O1lr;fG+}riY+8d}9p6U4L%mdI_0qplMx>#0CAM`P z^3JT|XEDzY`-GsY?(L>fDo!{8YcSNAFr^I_G8MT({BkOn2e5fU5+J&7BR1$EhzL7* z)C!{q|C&MXejRWO7HlQ95-6}@;>JkpheGE@o~8F5C;HEPEAq66kR&1Ugosejns4c4 z1cAIHP*Ykbt&Ao)n-mt{*6AhKP?jY%94~Hblx12JK-Y@>_8|Ya z@ic!yo#WtT9ZhQv^f%X^?+AQJXI8yOn(O;J0_UZLCI zvK2;A{g4N$!BrACM+=}HS^&Y8>{gx+49pBTn;Or7&0)~d?^^%W(6Xq8yvIX)Ll=!e z*wS={pMFrA$mhcL+bNOhSZs5^_4yh!1ui~0e3JMy1D}!~Vl@W`hY4^|f7+$QzK1ln zMAo|oja+PzpfJ7bbNw(p+ns=bCHrT>9ey@n*N$Ez=Xur1SBo$?&gYQTNOpk^Xaw}_ zR6l~)D4|tHof2!J(sAHyexk~T(_~BXi~4W&UBF?rtyAjg)El2yL=?b=>p-$vKkPxR zwAFGyjIrd9F_|1PCa^X*UbAC3yDeO=Q^&Sbr?DL#6@K`&wKcp2YIo*AFcyszm!j5| zYPnfXPJl+OgQ-YV_ZoaNtm<&qO3g~q3GRleK3%mOhj1-}V-2>KW!mcyelxy;ubQEC z)hx0P>gL3T&+t(6O=xD+&fle0>-{z*HrGlxLJ6P* z6xe^eG3%&($pfjV<2y?PZeXVz>$Lmt-X}S6iyKo8lmZ5udmZUzmo0=mihCbW!DW$U zC?|3ujnvSR;S!V~*Z7@Q8ITD0$oqlgyp1Ix{w_Jpf9A7yMC~ukowZPk+<`)h4#N-~ zx`B|O;c=|D*FvM(Dgs8t-bfH|@N`=*_|`ds>J=6Y_VcmpvIB$y(5+twa-`bh^4O%v zERS{8j64{(^7QTCPawj{E9(rUYit}h7g@Mp(B+rD%YhBM7<1yhjko^ zmY)OsH;9v_@%1SW(nOfOU-XAWxkK-FG;FHl#i#~n`^z0+U;l=xeZq~Ye?uDUw0FXS zq=3~1_=XRtBH%J1u?Slf4StbYpGsA)ZM%?$#y!g4gc&=$hmLyDlC={t181roA^xKH zK*znnonf-!iY8+`hF#XfJ0bma#_17&frO%jJp_&EKzcMEXZ^8tMkn$yLF%Dl`Yw>4 z?>r1>nzNv;ej>%FDeTauQzHP|`F8+mk%?fR2YJXB3A>$Dv}_6O>pJI`4$z|xdtn_L z6oykV;-p@u!#CLQh0w8~eVm}^@jpS;!SMOKAImQEat9glJ8{GzLpNtNa1>+tdtj3z zb%M&K;`9!1SUAt#w!K80p86b@7Gy)H)|OV~D-R!J2Zb++b^AohUj#H{RrBnJmFE|_ zYeUNO-_7tI$E`+ke!O?%WY*}!{;KbMLl#>m+u!kBXc%*o-a5Rq4TZF7J( zuYC{P;2|#eZ$@ns1XCPM;#jMHR0+Iqo+R;gfNhVIEl0M?$&$E-bVmD-o(%ETU_qK5 zT9z0VTCrP2XVN;7yg+nn}yeXlfp_N`W@{h;sg2D!9UbKq>XwL38e zq{ncRI$BE>X#GOE<|NlX;M7fa82thi>H7$PRKC9C24uAi5c_&!R{iJ)Q_ zaOio=e%|+XW8t@sIN8<}`Wl?tU}fU-6#9IV{SQFMcVf#QS^WTZz_zX_`#$!*w5-m` zH6-xKm1R4J;@c^{qzuMH>wApi^UHoT6pvH<>axU8{6UIOE&IVx{2_|xmi>_8nJB*n zadYDu>~fw68(Y`FEdh`-aY0k5DhzSZlrYqH+z^mR0xLDTKk@=9OZhIIN2I@h;?I4VwyW0G+f1n&T$xSJly z)#j!Z>;$g|Bg4t3LuMJtJ6XHV6?LA@Gt{CgEVf(T88SN!jZ-e9VBAUm#{oibH$9RQ z4p5tS(<3?N0JVBIJyKhjK|TR(Falj++}F_91H2Y(BM>`j-*@0pxZq2!_fd z?y@N3(^ z%P&G^^+@ezF-7zQ!m|l?sHj(CaaV|o+_Jn!u--yr&%?AHVFkK)fvVRhFEUM$v!Pjt!3mawm z$cOr0u}Y{--h>0H$iPmPH_a~#tJg+twfrpT3RoIRmxOAAyzy!<5uD&a$ss{`>32d< zFhttVlHvaaQ((lOBmugVkdySwv9Nm*6o6ntcZQ)%Aof&0-zuOeDA7Fov^5QaM?$T) zHDqM6KVt{HldRJaBw5WOT@a8R#&`%%)BG8l3pXwW2L5XXF21XzDf>J#6V3{9OGa}V ze3hInQ%(rcr%lZo5J{5?QF>~1I}h!B`QF5u~Rs2ipwChpEX_Z;6|?t zS=vuglB44$6TCJcp=C;}8)#79sg8MBT1I8^?2_b%;sY6R>Fg;G#63WSpv$!3ShV*@ zGOco9)BF|cdBXNG>;YmXNOw+PuhiC5G6Ta+Pcp~b3eTUw0Nvgf7&z7qU(Rtii^|hh z+=K=l(Y~OzfCbd00!JAr+&V8yU4-lV%5dg32;iCgT~aG(WKK&4nrAi6#7b?brO6!r zd36tj-g!*n>Ku>RA*;8K@h7Y zXIh3Wy??VdCYrWv4}HK5RiXqes^Z%LMDA8rR&n*l%Sd9KYfGo8xqkmz7~juZuRpWm zXHXlQLW(+TkM;Y5b-30gaL#-SE+?SMHSnB!6a5C_AU3@g%m04N%g+IdY#Zd^Il#kc zJNa;7VgM`BFHjt7Pp*J_y$X}Q_Mn;fG$r-;&ML76&=B|Mj3IB23-stM>hK3q7yl4) z3c&~3PMC6^L=NGYg!)2t{NIa&T&F&eW9ZP*o&*eo19&q+r=wu++=r}t$W0CCrI8Bt z?;&^5lp@9Mtk@yd@97tUQ(O1al8^lV4HFH{2Y0GD@pd(<@8}+KbV#noom6OT-m8SZ zHsICz&Ah`1dwVQ1AiWQXI3})uYbChAId7oH+XLUP%mcTfl2|s9s?}qu+GD(o?7bga`z(b7AVKfwQ9bd&7(*ohyh+`4}Ub+Og zv~|&8Yi1q(z`|cSP+@cEU4GcPtrj1);c|rZ&7h1mZVgY->F%t)Hmt1SgWY1&+h`wk ziIt#zPP^Pv%D*f1Vm5JwRO$jLT-;(^AH~_i0pz?cc3Lg`8R!Yedb}i4O-sI(SZGo$ zMQ!bgg@ePPuZBYdsgTgG=p#sh=EN=;YjpX}YHr_!jV{m#ESP4%jjCI$Fh$&sGdARG zV{Y3xncoc?+o-#V&cN^r^5AYFTt<{n8}c7wSq7U?=`yzxe;l~sE+qF0w9H+L-P`LS zyb5Z{uB#34r~ixcI=Kr)c1o~lY7N}$NT3DGrK4abA)Kgo*3{O8qP9e}yQbEtcfuZK=8>=> zqZ=+=N_-_{sg~iAwcoHMUl`H~|DeR_&;rTZH|c#rd1w{h)U0FwDVo)N8{&f24QDbFm0TU4)q%80Ig4cVPW_N8w!k%Rwl;KX1G`F?VBP#ecb2HVzT!58yi4SA`b?HokcpJnUbfZl{PF zk>oRLejvmQH=%*0+DR7r7CLCtbRWUtdQMc0GX~zneB53WmY7JsxgPxBf|Zod2bsaC z^#TUXFw*vsD8s3eZn3<={BD8y-F)-Avv^(#5HmvD4qVGVp>f@NoD6p6G0b_;>7TGK zSQ~alR?VS_5WXJ4chmd`;}eKP*Ud!gqJH>H{=^E&IvG)+-cV%M^_&01SS0H0MKv$grs5Or# ze{;CeD&O0U=GE4*vNezey^K^nxg<}=whvsAzk~U#Wx3i9o(+e0lk$hTOUuO;4{qj4 zl2>04XBKhf3p<6i#H3_&!u-@$Y5C=joC$cF{3W!jqt2D3>B5^fj~M$Vm|SQkqX41q z2T%b2Y3>2D36oLt^mS3MHXxT;nz5fClr6_(g z&5ZNmC;~14*6HL!T?_*!%vVHtjCz-|@_{NWfYVq9UHf&K-&hC=^N&yg7CXr8M9E-I zy78zABU=W%n&G@W?8Qu0LFxuGkGjMv)ARK*Kbna$O|6T+L`^#69$NTe%8totm!w@g zstZths1|A@RqXFjEbE6;4?L#pWi+}9BOlnJ@if*Y@t06S%G-H%h(Gyfd?E*y<6uV~ z#6AVi5o+s34s={NLIlf5uA;m&lJFu6NR3z>mHe*2h>?FG+|6B3U|-OciP^-Shp#}#vXgWHA5YNa6U!+q zq};yuH@J$N+-9bU!#^pzU+qcXRI%2RJ6N!&X5ogfS!cW}_M>(lIwZ zfe*Ebf@|4$_;a(+fU&e6F5DR2dJoz(we3sCE&7)WHrk^L?qs(*e7DNlO|*U1q<`tz zFp0fyeZ{_t!7Obi5STtGS&+D;Yxv9K`^c{aAF<4kr-vQzf@8HZTke1_ zmA(3$ai@cpRCwMl!x0N;(N4*zTI>7u4{b*MIVBEz6z)~*XZ8JU7aY+A;K^H8`rhA| z#@@HXm?m-|yYDTeyybfrCsN?||6PagyRzmxAaK6m*)Wm4a^kbTx2CJWcd^}}O(&$T zOD1is$|nkYqPH#_KxLQx{SSvHo)AToTevB1O*7qscSN~{T$U_eed zkFhYIW!is2{v~+Ic>0#e+UgdNtGQYkY->h?AtOhv79Yn zC|3L;L^vY(C8_NL#a`w7Z<;&Q)?kGqzKblWva^D+h~g})^-+JanYz>}7pa3)3H#&j%?M%nM&-lef!)5j zxF+{ot!{W}P%Xn+lGGUvThXOjoAq?c<+5_^5yIE&whQ>kp@q=!7ai>|DzP=9c19f$ z$s>&8F1nuZB+A21Ac`DkZgdS-L#<8zL|-DCxMORp!%Qc{SfvY7W`--&hwRbd0Jad8 zc=lZv7M)4Ey|on+;3sDoV)i>|hh75n`- zH-jEcA%g)`CS%Vo^jhM_(t0R?r8p(9shquB^hR5^6FWQ$^{ReTZ$6`7g^<`efS2LI z`*Ubd|3D8#gO1K7jsQi{X>oV6_6pY4m`A6R=Sku=CoWqz7RrfR5Ri?94t>qPR0wyK z7ypI$rKPgGC^KCCKePnH(pwNhEInLUcsSYH zMK#c96Wcyf*vntjXy@2%131BRv+s+&8T)^0jzv~DGRt=!UY=RF%PA!+PSEVc;+x04jyWuz`9C8z0a zP;et3AKyt09HrxKlTn%hWp|r{ZIg}rF;RCFy>6=>AcKtZ{igs;$2D+d$8_A5SbQzE zWQCGl#p=%`3N9G+E+|OKU+*%)vT>_}G|H_qp1!cG)wL|ngccc3S|rnlI+%#ZR zT-V<{52V9tuLLh8L3{Ji5gV__imv8s%5AodpfBay=|iYK@SFKaA)n! z`gu>Nt}$DG-8}J`UfpjdbHH}`%ci&Y#3wXN=Lo&`4(0{54(6M=w14Jc_S@PRz1T~Rl^A0wq2=ksVQv3&T--P-z znVBn^D-8S%Dw>y7pTWRCJv%uY(qn<`5JRE`J$=%kf*e{lfB-uER!3^0(2sg#_74u@ zeg`UK|3HdCiDBCf3TcQlZ;=fE)DVDCBd73MX>n%uU>mry8C=>pv#Bv#(y|5XL25qF z^05&n9mv|!TtSltfaHuYXx0NX=SsY2p}M3?Oo~o?mUROZ8H~u;#u#JqSQ2{ZLaoPs zjN}?g*Fmh$vE0P{He)`F%a{13&^QZnW3DA83tFarDJ79wHRQxiju9p&yOE5s7iX5S zPAT9u2VnQ0f2q4R-q|na&DrhAn{dUUuHF#hhY!*=#Yui>7P*An_97irPU5O2oo*Uy zOh-vz=E?#LyJLd@1MDHwJ>lqR{3b&uuKRc$ zRa&(RM0m(TfwmKzbj_mbq{47k@OqTc9^%A+hT{dTmTLg5;Yh9^SeHWDVf^ zPG5p0ObJX>BS$}QtpRL@Mtm;(zl^;l;yDM;Qq3i-!QHSe;4YHOc?FQc!u3kLQijC| zsD%F~sDR}K4dDj>ip4gzraN(+OJc5dkxPd4`v&&TmSu%$r;c7Q_Rd1_&ATqgv*|(_ z?NHdXIT(ccj?t#VW&9LM1V(fCO9+gvYLQh{cRA|8$m z-~lI6RXK*E5J9AvdGFyn+a;(a3c&7Xd>(S*x&q~)n?QFXUV&&!oZ5%W|Ki_-47X%6 z(Q0oier1I=N8(f&F4phVH{(93yq4hH=B4MFtN%i`>qOJ&mZjva%7L~Zf16w=u@t|N zC8*A#SM1f;Df0UcD-S(|f&m-%BOMFxd07fk6SCe7GO?X$W$1$etD()gv9Vi~;F zCn%}JBUFzlG%bavdIc_e2^!)%?=Kt;>=SrU%PeegG`3XKr#yK6E3D-&$9I<7GTy?n z`3_|+%QY&LlI~o5@E#!+04sw(UjlbAOA19tfaBt{6O-buYH*haS#ZIU;3SqHLg-Hs zuSrFMHxltGM10k*4W;Z6`f7@B}+rAq7FL4k^cPF$PXBT7m8RsSpzmmpDjw z(ki70#|jhi*+>t9d8k}VN=CZ*CV?+O*aWS7?aGcDMH*FIBw7N4g!15Gl-=#Y7fUc8 z@=E*|8dge8sz&-qlL!y}Da!v>O{!#%h_6;(D$kEwxNxnGW=+sVv(lnD%hwwDe!ni- zoR)g6HC%rGcEK}))V{s{`}Tc9qC{HC`gjazkX!(kNl;e$`2}+?sVj5N5W~RbMG#Yeilh*{Kq7N- z`TBlJleBgEegUIi6-{4RDkK!Ye(|3$(WdsYeuJPfC%GUcy$8s6o4ht97ee3rVQ>{3 z*i>?fSUVT;29du2q~QO6pzaa7^iC!aDH2SyYB^>J-q%+0le@$TI#;BJhU*x>X_1dz zx5<3Im6y*H#lbF0#fZf#2J+6~4Y=t%4*)nya{)$p3vFvi*Ad5XiK~d{2YC_&;{G)_ z^N738ShjLt@wE>91DpC%ke8C8!RXHHy%lqCamNHAt94P%)%{coTzgL^C-6sytKd%{ zXq3?0V#s7l7}AWv0d&MKAn8;p*_K`XXxr1skZRj_e%o+C)TVz&PM8vp$=Ak8g~#pgOEkaztzB*z)dvpU#TW*zC*i%^otfUrgsgxN5v5AXO1A$2ZMX_kg%wV(7t+Gz<}TVG4u+y55@fqQ~6UsY}D@M)fS$(ouQTV5b`>jrzVexEzt|w)aI#N zy*R^HVsFpgJqzGszw-<~`_IG)*zc4z>|D6(fMAI483X=4!x@xnA5Z%tk@9F=du4^mXSwa*9zdvm_ucS4CD1|OA7qubHlHmx|ZnXXEN7wgnS z;0*lz@p~IMQ+O2fS>f%E3)S)CGy@y{NI!rx@H7_Z?IdD!#rd6>sbX_x)DhIFP=QW{8&p4&QuZtn=V zZZ64JWj}sasaHP&)^HcKRrvz$Mw{OVxOWpg+%}ZhFHktf{@9bmBIHp*J5%CknLM~! zDg$THjev(0pF!ntz^E@IzYsSTJS0hu-vSnn7@Eg&KT%>oK*H8?Yd@n8?Q0LdAhvwJ6fe`RYRwH-s~!y=QFLVp5(V+N``2PuwrW)S-D;7ncuuNm@@yQl^5 zq{4{+04@|hEdqVZ!7$Z_Giqz;*Q^}1waE+%5ds8dJ=VAn`)kNLqK&-#SD1*x6dLXh zi>|>AN)PEo(K~LOaHQYF8ty96%N`FY>%bYTCBzzVI`a7f9wl}PErhQVybREN)Ngz~ zK(XBinxh53W5rw$6x7C7i=e;-u05IF-tOm-duy5A-?ga(-DGv@1pdNwP-OsaOTX{T z6jbRHRG||$U!zJtr~(%S^;t9)hal$sQ0PuX&ztZJw0smo9EP4mYn}Lg zE^>m6i=>XkJzX#^h#3U`@gu{ROkxZINommdMu`JO2f|PrvQbQc$+@G%oE*SJV!9|q$nP8I z6q4UgyoLO71cdzNgDEnF{N|6yuZQHrRF!-bZb3l^*8N6734 zE>CLSUJ?$0JlMN{egkf}CFo+la0=L)c$Q$ zUfysYQH_xMymQ19{rHMwSr7e+IHEIg&za%wfAmLxqx*k|M0C99esJQ&eLrE4S_+%) zUwg>Vbb$Q-w?hbVkqe)I`pk_o&lPVc&k%1HAN&tWck^EH&gY-e`+EMdh#!v9UY=kcH7tsnB68~yxYkyOEVh<6o_iT7f@ zMZAMt74JLvI`Lk{*NFEDzCyfL^E-aqJUeD)>x5{UW_hw!w-dlJ9 z-h{$)P2e(~OR3MrC}3XE}-^0h*?;$R@I?@Z;n!79b&OJ9~sxztK=`_fmWQpQ^;`M&hksT7-)Qs7Hp zlS=su&r1?|-{HaPr;z-S7Q8-#O6UW^C%za^;g}z92r4(tvF!fmr5a zJS;8b)P|e0exUHohGYxhZ`mP@AX0KDZ5H&@jzzaO0|%#HqT8=uV2JGLdyRwY6Rw{P zZfILze29pq3yoW+h-X>*`ylx9UblY0a`M9B*I1homJT+iV-t39e{gq<^GEivs4|2< zxIctH(uR%w)Tfph=Ogy9)$eh8aj!dan?uoa!GU_A&X^QuR$}#!sT!$NiInD|WsypK z@cl@oUX5VR2hjPJdRQURhZNc?IBxwa}Ch{Aa>SxA)w3SZ@#Yhsy4 zP|l_8>llZfjds`wlS(vm=`-E#+XE-j-OE!V~k5Uu8(XsT{F^SjbV5Wo>62o zT<|wAW1Dc?Ktd9tk(*OB#{DS-|bmL}j7PX|FWyW+mHw#8tcSev`A9oJxVHI)r zIzJC}fBtuzsb`lhHyq2B7q(vsO*?GTbSPF)F~!QACEpi5d@MBfo5$}?)3ya#pOeb^ z+wDFs;M#2aFzVB}Ee+c~O(*3$?mBTD{FwqQ1;$A8#-k^weojo|>{!yRpA+kEvH4q7 z>MwSu&baIjt3t*2TVnmKu~LS|yF+cW!eGx;N{A6zzSehtC5^Ypb04q^cm{Y9*a18Q z+y?|QzjnMK^RDB#Ca#Hl0`~-N2W|)MN!*jTow%L2@I~+HYO)IpN3(UXHo2uY>8 z0LRzUv=IOkf7x;r-b;<6pRL-5ePmunw+PJ<3EQM!11~D2E8GcVdpcp@Cm%l6MZUG) zAeYeTH)!c(9!V?GCugianJ9g-g|ZMr0&lyA=VyR6pmDZs%%S=@HvfC7_1;&l_b*XN zOWDF4X9zb&)&27-M#UiQDHLcXkO|BK76Uf} z#lTvCwjM!SkHAgBO~M_5i$(9Rxo{B{{aPX}0;*qg;5u;axG3t6?i;I(wvpa_zz*P- zl6ItTX4`0isJ>9|)HbRgs2gD{zg~S8nQXY9Z@mqK)Iy6ygSF6p0HGslrCqpCm`1G2 z;9Z;(^RWclWeyq46nhzTuGJW9#yt`t)dX4tuLo}cfojU>0>2U&dF`0O*a&!`g`0xV z_4k;kA7(QOzN}0Egl%J6RIw(gU$yQ}!0lkN%H_SXAtlK|yb2Nn4zyTm#DsuFp&Ma7 zD86p=D&kt?qCiXFwf2KdgFYlWA0Z&oE$t3yk?7jCs|_Kz@3TpCaH_7c61cce0^hR| zfE^y#9lXh7R=MOj)kDYw_3Jrdm_JacpQ{0d!b{qMmzevB9VT=h;!((XN0kPz2uUxI znxI8Eu%ykLM9zxn_0N)pg_>Bl_LQ`Z`7HfVfMfuoFEsK%|J+1JYkHCh$OH%TVsAA&K4fHf7Uk66I`ltZsj&7R0VDxhlW0=Fkw-#@dXy@ zu!@b7A95+hI%W^S*JI9mhC12D9vA;dB$?1_9`icO^Puv)C+vBd<@uEIyf5rI5YK`~ z9^#E!3@LfgO5S6Bgp7W{BM;)gUH*W%EJztC!Sp#EGnYuAsq%&%{n?U&=mI&VUx|R@ z1a*oS)|At^uneK~6R^KLq1Q>g-zjw58~y8YXd<^3OxZ5wBHd(iksOFkOUX!ORB!u+=f$A>*d;LXqo()}ik#PvqOcQxo7xa^` z@U5Mxjg)?i`Azae-;PKbp!Cpg?s<&Vxbtd;>g7S8Gt!{6CPg@Gm!dqdbrnApUK0RyqDO0h8WWLVO``+2=Y<3G|DjLB=$9ia`_xPL_ArhHO^tYf=jil8$%&$eMWkI zi4vc`?|vp2)R?@>G_6q1mZ(4el)V47>MBBZ*W`WXWm}cJzboLGuqfaeyGU%~LYr}X zO59&AF>v!?iHD2!50OdOri9fKdp%8iV} z+*$}E{;UCe_Hu1u!_T<4aItl7A@gSrbFQo>^01tT;L}p!%(riK?L1{NizEOZ!g>MFyY+=aimhXD~B5Pl#LWVaj*8TN+T5|=FWEG;N3xQQDI zp@R`>{}80hh1PPy9JfV?0WL60S@XFHgl;qAN^|vty=6Q;f{xDws;%i1O)wTw7-IVo z7Oj+;A$lT+eC&q({2jXq%NZwf8%HrWFxKvW_Qw=GX5+;|faYRmnZsj>B|O3~3NX%n z_ddS!0S!0TV{e-=9M^d1oM3D1$5$Es{5eUnLBt*=8a6zktU`~x^G5O%`pcH<)x%il zT`4@k75PH#$H`DPvxY#6hn&+GKXV<{Jf_V9jV=?aCN2TCS58VA02|^dqCPIZ-x?;7#1{bN-}o zi0uuSK2r4nwDHiU9o!Ay5o65qx5euH>!5ZZySBDJwVVjmf6aLFMYs^BvXWw2H3q!~ z(;%lS6m;T)pvO`cGg}L5FC9yR#x_hBf8BPvu&Y-G!c+(*MZzTa`h*7T?%V$yJG&R< zlsGYzZp4?Y8_s}3d(e-V;|z>mx-JBb`a7IgHZbhZcV4;YyWqYN+&KEYvg11nH-1#U zgCkE6_Zj?-0}fug&mf<5UXj$nXS>6m`@EvcaNhGuIE?^Ftplon5?}?e6z~Aq066a7 z;k+W51wvBk9|O+-FN#kDC;q>7UP*pP@>S=Rw(p(yyfTGPa-t#dwoIN&fNenJjB(EM ziiG}r=M|N1B&}|&{TYjGTJnR>t)#{$@V%5uk7VPX)tx)}9i~;_$vBro~X_@fGK`p*c(6Shm z_ccfy4kG%9JhMigIdnL{Oju?TtP=+pgkUA)nQwrAeEPsq(87sB6bdBfn??76cEAp| zFgA55t4gq}O8mn|j^XANy!bhC48jd_s9~TBmfYvWp%H)+$2)KWtZ>$eqk?x*}%En;RExS~IXSp9J;Iv|J~YrNURrg*tQC773oWE%2dA{FNFz}RpRg_uvaG0X<4 z)KO#ha9-1rjzt~`h)KCbm8#yvWnIKul`Kc%2BF2HVwY^#;84=0h8L9xUmS)sI5efu zrMsq&67AV?*ESC6u?BQ53x=+at{vtpUy=Tn>%hjPRv@fb>>NZei@|TH*Pe_fyaRH> z+qn}v>wgrKRZayp#0=C6%HTf}vvC}PLL1zZe+v)J`OV#n=)i?}W&PEaUEz{$-9>27 zp&VDLisExmUlyYe57bJ0b^X`NPKqF`ALem;0ng^WuokSF$I*omA&wcc<->L*C)w^$ z#@105(>pikRtXe*PBn`NCWH?v<}230wAUWEut~0FW8dub!7=*+d&g-odQ$iK5(3Qy z_h7xtK6cMla=P5A1>046G*w|;{F2`5r2AUC14SawNdSxguK5Tff1wp(ReX7WYCr5Ogjhy&`?wYGR z=ANe%{=|N?Z*Zu2VNWTB^VlE?Ocdog(hMR#lw^kPwpNPcxZNv7g4Sid) z6wVlH{)&i*#y*M@7L64NAM;8{S4rUpV*{F;2Dw!$>r^WrA`-cQ)8U#`$0fv znZuaInX8j&uMF()eo2pcLnnx>(zYf-IaoN1od1%^SY&iYDsf*+$~R27Y08`qCv9kw zOjU%BzDgnXV4bl>PIk|Hi{z}OM`r1#lo2###z@=|#HAWZB~MBt)U+%SQ46WK zB&rYRMQY-2Nega9LlI`8$l&K}0|k3jgm`SaHx-?&M0K8 zpVK~(`KfGoUd_k~D_z%%ni5q-x@~s`2G{LYmD*i>aUc7g{$0pyv;}|H{B9h!nN)WL zUiKfmwE0-SaEG;II_xp|W(#Pq)Xsjc&7=7)dXaWM%_h<lRvOXO z85-I}-KDi;2ThPg+FW5{1GBi~x37s}lTPVLNDgi}h!h;*XoQB5g8>Z+<530+()tZK zFJd{Zq2?7VEIGFRYp3 zk*$D3t&n7nnB$*kl5`ZzPCdQxrn<9=cb(gmIV~)raJ6}nWV089VtQEacB93s}thilfElNyKiX5FB zh20b=d=UdqBPF8|xe|g0#4%;}rNMjB4)Fa%gu-8S<#aM?jA+JXZZks&=UkaMtsY8^M%zQqUB);D>DSY`Fu^Sbnz z9EH?R_5+6qyE$#m!}kwpE@*%Aj0mNMed8m(d-3J$gc?6^mj*7%!t#ONljFiJRIp#u zw`n$PCsp?OyU0~523dloHJmcFbU zP~8$~Hm(%6$A0)&fb!Z@qM~U}s(4aSiKMN|60DmM&JR=xyNS9Y5{cTQLKM`#N~?$Q zo0C4SFd!5($($SLEhu>i$`o5mG-d%t7uwW*Kd}{0RewR9?YS|sW`dc}C;Hbv9UcDh ziZCuU5_E%s?J)f;3)E6_$qeH*!BiRx(LTW&J?5NP%1SGDICsWdK2z~QIB`xW$E7>K z;_T?p{nv?5AA`?EQ&$y+s*d;QL_}$vSwe}zd#92F?PyRHRFw)|o?;~GN9$@_QpL50 zmld|RlMRz5f)(wwup+itb$P<(DYKQ(5NRdz6g_+d$jKvuobFKwFjsu#0fOAh6Kav3!dXq z?80KUg~bXBPJ0m=Vx*8_SeLKkt19#q93Pg=6hqVamD`4n}uFnm#d z-PMxyNw@NAd()E6GTWks!eGk_RjC4-b#F+Uj1@sg>J}2h;?As2y}xs3&Y9*m$AIQu z%CF^|W3A_kzLm?mJYc_`1BZ|K{dD@z{%NOMXcprWjyJ~Zm&45;17{F6_KbIZ{bu}e zZEWm2Gg^7t!&A$QHqPbkF~*_E`)9Q2{lOhWAz$q2Hv-K!375J1@D*NnHdIKnx(>RWaAK)m75saoPQOP!}E< ze1oA{77AS_p%^*SP=cQ4F^^FR8A&yRA*$-stIIql@yG$)hLVY~J-k8+UUo_X?2-UM z371>VH8VBt}wcFL?3AnC^RvY2N?V43;m0q+?)mX(uQ zq0UY|3&z$*Xj!~joxy-y8^^P}1W>JPEimlCNvW@I9L4Elk$Dq-frAANOOk>YK&1}V zyv^VeArC9o6YOa ztq(}POI+yjj9uDpkXY(L=UuCDxd^z?US;MKty& zqGQGZ=N%wsAuIB+;7gXkrXY{5TxbhO8@?u2qF;d{xFy6G{I!TRZ+&ZHnkB3Jp~xyD zt~uP1+KQa@_)|34UWyzgXZ`3-1_)l!IBlC{*+^9KIJfK|Swu41)K-aUUX`gVK zj-MbS2)iEdE)9a7U)gwlRQ}V#`Cnu{{t@|iL4fAIVq0 zSiD|Q1yX!hHJmt9k~u!L34tz=Iv!Bbg~%oQ*tDag5`PK7=eUZUS9p}s(3~%va&`GH@`wk7UTQ#F4tl7D>yozE_0YEh!wNxgDVXT z^lP-oqmXtastbojFsL^IEfeDeUu*7+J$*!Qsh)S%Q^CX+qM#iF>Sf01?38#!8=LKE z{uIqPotIW-_m~Bn)v%J~8DuZ1tiSmtofaH~-8AOB(pWEA+eHby5gd&=z^}3FcG=(Id)dkFi2JZ*0m)g_4diCv&o6S-8O*OjcG)lN*C_|DKe> zPUqJ9SW6KAxSHWn5Kcn>eM6EJ-?)%Z7=huFBnRnrPXof{k`og8l=P{IV&b^VyoD|m z-KGT_7GW-We$$j+A=;cs!xfMT>ZV1t5G~P=q!3VqaOJgQPSccUuom4x2BMF(tjvz2 zf+TKk!b_0IJ^GU1d{xf38J4LZ*TkOwL(`mC)S}%vjX1L;p3^S`7*Cl!95*8p*SX~a zK8Oz2#Ag}?i^>ipZHB2zN*k?1rwGJWr9UgJAPqSn#-g-1&3$uTp7|uwx8k2~e(-8| zjOha{LEEVit?4$=cF;Pp#g=t~yHuy&7{34Xp)vawvNKLlJEP(B=bXgCWlaP(%s0=F zg*1uI$-c`BN`@FXpiQ$*wwKU`;wzKQ@?{&$m4=l;${>=7EF$sgij8i%C|{sscAoiz zCwZ{SeHl{%nV_`31>ORATngM8mTc+X_hl7PSLVJ^ta6nbg~kN)I2DYZ@a0y8qvt3E z(GfB`Dbz_0IEfzfF1o0o05xVi51q=qcBEauB(2dke2I4vFvme2^slp8n#QjKhFSgw`}{Rtuy`-1-Rmi_v|u&`}#z>)mGp5{Ng z@&+6UB>Xyb_UuLkUQbVc0qM*${trU_j?meh>y_ZW%a&VZz8-;Dihlhk zmctry)1J_{gP^dEB9 zbgEKdd%5{4AsUj*U*LobqX^v@l7L#!+7}W_G4Jv}Magf>wu>%_A?96HDh7^~U9ha~ zFZAc8wI1j)Tuw_`c9Ao9xU*#o~1#2$fy~hb z7ztQga~5kD9qc(0cw7QlgM=I}A%{uGA(4=TV)Kwt;}f_zV{%Gzc>?jFDg8o2uT)Eu zbIVs`dx28+g7eNQ9=Z4K{OYaZ7axNjI_?0U(rTSsL~kVdf_q;?z6`5@+={GCNigDS z9jKw%ROkZ%zM_bzwPMM@T4? zpg-GU8yJXh%n70CCN4NGweY0TPknd@d&?n?V)W6GSER#T%G*x(49X+gK{n4};01>U z;;q`JNga^`YK)=m+{({7DIGu^om-`bf;kJ7;l{=RTlTN(m(hL)FB}B0bjwk*)4u6K zGWQL-(YbR#TJ5uKkd!ptY`oC9^MLbL4f4t7EMbB`R_1o$S?AUO1Az8v_gik@;>r8D zjrPrE+b$Ann0HZfu!T`Eh*7c1|JlO=CNn9yoKHJe`Oh#iUgw>sfx2^5!+?y8G*}?6 z_NOEe7QdR$V!2~fQ+BLMb)bJ2w^Uta35sVg!)OcP{8=ufj?_RwBTMIb2g*%qpe%_D zlnJZ+HJu6izo0T?RfA0iOQ#GLc{szvxIlbMX20nQx@(%G7g<#wxK9KNUw~JOGJa; z`4oF7p>eKfv|6V0K4b9dW-TpVGvZRR+H`wuPN-Hau-PW=d5%f_#k@9=3S)C-4ChR7p z^M{nV#Lmohz!!j#fXi>D8QW88Iu)kh5gZj>&Vxh4tA8+&2dS1^qwZi%Jx9XWe|uJl z2C2=;l>MeuJ(>OgO4v%5&JrRFhh1XK(pci1Thr*n)~pkFYr(5|Af6T+&jVkz;K*50 za@{#gL!*hlB6YWOtJ8`gnUY^CYavftTQN{K&;h;<-kX!eG8oSn34`Ii3+i%C@?@{e zp}H}eKc@rT@(}8DTmPDqJKT})jv(5DPmrA!e0+yXkGEpE%twyVxcx*v_o;+ zj6SZ;+bN@2q7#d_=ZH8ZFzwSKNYl&3-*^SK!zr=?8iA}P5C{!_6uMu z>r%`F28JjbfdyC%C}10`-5(>`Vn6kr&rO-JV{6^D^*Nu^dOyjo&q0H7Em@svX50TM zBZC%-)o(A0<g9vVZ z{UbHk*={a@gmH<%S=hXvoobr-5CezT7;c&ouct1DHajH58i8tvh((V#~ACbJv(=lGD=vyeyU=ORe5lh28~WP4z*#s_HE3Q}BM8M~WU^k|;Ko%bPN1fzwP=H$50VDt;~T zZJjAKCpNvsAQzoIVY3-B9b}NljBRvWn{&4I*rsHm9G)|TV5@MtUAvCO*S@_e;Xpk? zW1kqKnE?(2yNJ}+AP33XYaQ-DjkTl%URHx?gIZM9bWh^&vQmaIb7&mz%1Q&t6CnXv zvM7BI7WVDcY7U<}ANN`6{PLSLYx{j46K-1IrKoBu#Y7GEL16{B+`URV18z`Bin5yu zcd$*kd?H~6t})W=&lhW}wl@B|%cZ*&3ChQw%~oBOW^LB8Wi}xm)W9N12xL4We7g%| zDAgQIJ*&?&pCx|7^dO3_Qj9hoIq{=N9AzCB5w4u$y@XgWIcTq?Hi#~K=PjzUhhXLa zieqi+3l|D27#8qI(@UDFbXGylf4{A}j5i1a`1fF9g7T@gM&TCb2DU({2Atd@YU!sY z(EiOO>@84LxMNf!ya%JxG;pD+VmqRn-8Dq1MTAU;>YI}5{bFXWZooNo>R1u454oWxAviCN5S+ge9!p*~nCs4tt5Z_aw3 zUK9hH9~#y9=G+J5jk~Kti~4sN2x6f~mBhJ4W^suQ=Nh8UZF{8LqW3?HzWf9-Bvq!K zd_B_K=j+|p*QT|xNOA-dAlBJaThMRb!B!k9o0Mmkh`k2EhOT6wazPNGPy1H++{A5 zL^^FXodxC^4ranbMx##W#M8D8u!s|vieB!Mp=7G&>zm3>D;0{}X%>P$s#-Yxt54eN zYEHHhvu1B_l<6i_s==KPhI0eEWv40heyc9>RxXWQ<0wcGd$`gBH{l`5L!iBM4-L4` zsL~Ff??Jbqrdokmiu0%py6FY|g#aZ7% z!)!tn!gohXnZXk5o;iXw&YO+}HKnba?BjwJ)QdmAXri*(wdfLrIGi zVFf75tu}tV%dFEx3vE<+~hpHUppdnPU9AUdD@*%~N+pf$wDXN9d35AqN z0X;L0SW32h`1ugPPsHd#n3gJHv68V0+cdzxPr`#7Z?0xl(=9nvufwsYXb==`ySgkxc2S3+5<85gM*j%_T5~2 zAU0^$7TGri2ljla9bLOssQpH~I^q=WkuDgg?GiogWF0O$h%{@j+8+M2s`t|C zcG1#cLSSGqtXL&^-AzC)AueaJeC7qGEEdC|2s7xejTeE1Yy?-e8;KmnVnEmE^x$;! zJERBQ(2opeX(F(S>`hIn%;+4*DG^L#ken^ zsFBQQR=0^>EanSTn;ftK5L z#X(?L)sS_-`SdQ~;@>JA&+K}U)q9JJFsUClBnPryY|6GbZAiv4c<06xx$Ydsxxq7R zc7=8~dhDlm!*i}5%yJeVjH@5!=j4>tnGS;}#pv8{fJCMjhV&~*Y4UI75aB;-tFZ^p z25n`w<(OPmxx^uT#6tPCx~40(S=MBCG;fhgpooLJIeJ7QjoiH>cuX}6`ly9 z63$^a;>GVZQA2%Hn68du-KX zSRGa3Bn>%jXfb=VEVdzQU!arL$}xq%T6m(NaPP99%VS>q4aQxoU2IAQ;!#3moM5wQ zFkUndFj5fHrGNV2I|dAt;WVYYJmyUGC=Dlr>1vxs#X4xY6AYVQfZ zH@J;W8{%UE{ZvV}i!DkDmtmf`3&vddZ7QV>O_ST==AWew6nqq{pLTC7gHUP_sM&`? zr)h#Rd_eJMw=ZGnA=3?ZF`*I3y4o|d^h@*1B=SQ-_c+!CVpL8|Q?PwwP#P0%W$&{}&bHEhk=%U><{ln2%<%(NFhdFH0)R7dsT zI(t^AJ_=oD4x>miDi|EWX&z360WA`1Zr@l<-Ld|-jSlP}PD?-cY!_4vqJACP_iVNErc=6xh!R zvrzm*aX}7R947zkP3G;{-2w|?%zUi*duj%~Z!b1qY@SqV`^VY#0zq zpK;jOvphOOkp_q$lb_~TDs07nLbQs)z)`yV9$+pg!HyHACUvt^ev0%|7|UvXMfEqC zIJc}OaJbaU7PTmMhkGqrNRbr2l=?@v$M=`1u@zlBh8L2;<47hCMywNdl;YJMnsX{M zb|mstU3y02#Z-#x6kWlkaBvCr+f@VDDEF@ld@zRqt5U06zC`|Bu(sbSTh)-@G@dW= zCG$6F?HBO5BskXjwD90#PotijVI&!nM9}7Z`hcVXCmyaPU;1NA)+#}F0kROd zZoD8;hWwr~SV2`0vQ-hXRS~jP5wcYgvQ-hXKUWc?DlZwMS21h)(;3dKLD0$Qwqg*< zxnTG%E=Om}2PDQV4WaLLGo&M(G={jWmA&p}i3F#}Z_-DY?cN{y^Ajj!Ld^XAn8vKc zPk3vMnI5kTgFiOV+J!78v!L(q!M|`%9C!&h4x9o8fh3LvW&(?W5}*p$3~U1)2A%?1 zfY*TIKo{WZA|8+iECYPNX5eeU1Hj|JuYlKpHsAzs7D)U=(~^MkKr)a9z;KHvf1 zDd0um9iR)i2=dQZ;96iFa5LZo?gZ`w9tU;;Ex-}r1keRs09olWUg#w?c)ws(Pibv`U{;wSF!6__8Rd$10tst=6iwm0G3d)4cqfq!nxB{L{1v zT7_n)=PM*xZ9;`nUT!@KBcPu&p-Z#%)B44_>{(e^aq^p*ta(&m_jJ$Fc!zdfa&o>0 zQjFUz`@7~?QL=)crmd@5$In3sh^!6=j)Q;ls_ht^PA3EWVq$IfxPI}D{s{vT2M%(& z248UDkf9e{oHXo`;Uh+ly3{@TvN2=FjlX=t6a$y26IyKZ{QjMSO4 zzWAlI^y@P+vu4l9o_oWM^K#}d@GM-EyBG_ZOAG$#rke|wEniV|%gSQ!s#{A+%Wf-Q zT~S$eyRTX|)~sE({>xw4P_uE9BI{;VNSAslODlA*k22k;Wifu{^LL&$S-X}N%j9XE zDsQH@ci7qG)w6wGuZElJ)$@wV4fQ-H>N&l1war>+@Cm+?qC!&Rslj zL2j<)Bd=QS-1&2&UbV~xIq7rf_xLQDmOOdNz=ZS)cTrVUdFjd`y_6wSQdI3;UBs{~ z!e7_DtE+SwvgMUU4BZm1JHs8xyS(%kUy*OUyOcWneBPCM`T9u-o^o$dwU>cip%<+r zCNZK?zr5OAZB$iN`uO54TJ2s%;a6AsyrjY7YE^Lw$~Spn!d33{o?;lJos&Cv zUewIdOG>NVMb*{b)wh(dcNZJJ(u!N%6(qGria|w6D@yg!qVm!&tK<_FOL*ppRM<;Q z_btY)yt~&|8oubVPIAxH-2`1-S*^RvOKU#Ktv1SacjYSg%A)de$&8kgGF`Q@ za&?uO;uEf3S?;^Sy~?OqsoGS{@S>hVRaEOfW2H{z`L8}^mY3%gl~$;_OTDj^daLPO zQEA*-;;ybLTFFX5a0WmT(>bcaqTB15KJC?AcdylXixyk$t(Q>f%8HfVNuR$xBp)eT zvgDCLN>aX_42r|wubnR6jS98uFmifAxJ$f6RaR+9=i2K&qmFA!qavz)>xnn*yz#2_ z;?IaTRpM0{jJ7qUKHVrP@97}vNtJ<=i#c(gwqIUZA;a#)xz3cu4_^xUQfN% zddfVguB5w)y=zKWdV9i#+sM1Fih0APAT84~GgUiZquR$H$8ea{47*ajggv2HM!{`; z!=Jxh!jX!L^dgEd(CYH2X{jc?&wIP!t(L;bC|?v_VCX`URaRH7(%pHbs+JiOCw8~TJZsTodD0S?50fTM(q^)E-|AyE zt0-bcHY#qbs9am|Mfxz@gjupik4{Kn6O~{y+!C1|CzV~0(baDx&%#KT-@Q@KO+2g3 z5Px(|bU!05+5NmN>KW!*w?DG^-Ot~MdhS)#gb)Bk#huhV+|#b}@JUvvtawVr>m5R*U8zes%d|M>pb zKGpwjG%Ef-9sx0R-Tx3U{#?IE4~n}vrsrR5%;)=Kdc|G=+r_|I3{o=`5W=h=FSiIGWATesQ2W$PVZt#4=y+}ZTCySCl^^>5ts&3nIf z-~A7K`@!#g_j?a*fB2C{AA9`!JAUxPAN}~BpZLj>KmC`VJ@xaQPe1eQbHDiI^S}D_ zuIAl)_Wq`&b>IF2FTD7#FTH&5&~FdF^6G1^A9>@=w~qeq_kUGk6IwC9E8RK#-14xVpO%wzb#d|4Jn-}6Xj(eJnV55&Iy!6fE7x>C zFW|H!-nrf?j-*zAbmLZ|TGzB2jB=I64dBX>R(h4MRA>@8MZT3KxU;>t_zVuJ^6iGA z3iU`nlD~ zXta3eR92|3xklJ6(j~4&JdN-g;UtX4ca1}Sn8uRN(X?`HuC5L};=iQY>sxS38Rvw# zJ%?nWc<^mrQMI1V8FLLJhbp5=`C0E)GFlEarJ`HC*H^Af*OugFEt-7oq|AAcAIOue zDFFqcJQRx>TJ1xXsW}ZmJJ1}o3XMY>(NwgUG#tN-1@jjySv*#o#Fr{jxOxbuAhpb9pK?62tatqAe$8HI;A z*M0W)UvKXHy>EX$_08Vj`=+0B-)Db6zPY*O}qIFnS_5Aagx&7B5%Fj|K+XxZM>C5F>|~XULQoJ42xox zq5I0S)RYTwi{6wf3ajBWBKHi+p_ ziDnm76qkcZd?cynR2CcM-q{ds=R><8^qX3iQ0_B)kc=S;=CbQT6xXzqvGcq|YrLQG z|4UCQR>Jw3HqoA2?ggi~ES4OkAnC=$5RJiu;$otiDOD0TqjL3XN;I#ug6wBX47Pr# zlU1_Wr)wQjdMjmEKGGUrw89iyo^Y)s6{*4E^;KTv-ZQ=BURtqF1+KF%j!^NsTkwY} ze*@BeMFjcKvh7PMN>mFKXRTWavPJDlTro2)wNsY!ets=>Zgr*?TKcVCpNHy7*S#w_ z2#%siU~uYUv!Qb;CWrR0dbSuEH>;9(q{`ZFV&_T^2!YdEJhuWCm{9UGtvT8sEF|Ke zD{<2^JeoE{T4q63jy$(f8aODW#cIre0cl^fFD|bpfW=ptDQ{tJ%9rH1o8vM|-c%7! zO4~=3{)wpeTCB*hbHQ=GWzVOr)fm!F#m<9{7$y-inx3P~VctXE9!ak#&aEn~usZd| z7|AfJhr*ew3m2n0UE3vje)@wp?>sT`wJrAi(qeB$Ns(`HWsXpcuV1fwwcY1Vhtc|| z>IZAqXj+jy&!Ua17AUYSG`zm`9H%-;Y#{a!bEV=`yv9^2%y&c)H$cjh66wl&(DxRhtEd zUS;SqdhhKODqrg-GcQ-~p7ZO&tDIzty+F9MtE-B9-tOAw_4c9EN2H8V<0!AlS1Jse zbnV8hMf0=faV{t>=g?GPTLgPS($%zAtvJOCR$1@kr7gmpEAtpkL`ts;p)+7_G2o}s zX8-&9|FZ>li2^!);#w4{a5-IJH_Ab&!om zNmFB|{B7`Sfa6oBRs`+F{GJhhXJJ=y7KQzD!!FCSO1}VC z@@5%U>8!?e11z-K2*3wOS*0FQo?1Z4To-mX@cVXLDc_@j z5#wK(q(2=Cz0y z?uEEF;|fkQ7IzqK*E?z2CAfQWhvVLfE4V^2?kL<$+)HuW{w+;&VYjlEwB!#0!o0J0S}N3%mk(bQ-EaPN?-yo7H|V2fFxiD-~ti>JJ9)O`UEfm z3Ezf$1ULxn1%3%U2|Nls1Uv|A12zCvK!1BrpG%)kqCT1Q`JGq%b=VaC$ryH_z)OO!z2Uq0lAnGi8F(51;AS1Uf?O~U+= 14 and version > best_version: + best_version, best_dir = version, vc_dir + return best_version, best_dir + + +def _msvc14_find_vc2017(): + """Python 3.8 "distutils/_msvccompiler.py" backport + + Returns "15, path" based on the result of invoking vswhere.exe + If no install is found, returns "None, None" + + The version is returned to avoid unnecessarily changing the function + result. It may be ignored when the path is not None. + + If vswhere.exe is not available, by definition, VS 2017 is not + installed. + """ + root = environ.get("ProgramFiles(x86)") or environ.get("ProgramFiles") + if not root: + return None, None + + try: + path = subprocess.check_output([ + join(root, "Microsoft Visual Studio", "Installer", "vswhere.exe"), + "-latest", + "-prerelease", + "-requires", "Microsoft.VisualStudio.Component.VC.Tools.x86.x64", + "-property", "installationPath", + "-products", "*", + ]).decode(encoding="mbcs", errors="strict").strip() + except (subprocess.CalledProcessError, OSError, UnicodeDecodeError): + return None, None + + path = join(path, "VC", "Auxiliary", "Build") + if isdir(path): + return 15, path + + return None, None + + +PLAT_SPEC_TO_RUNTIME = { + 'x86': 'x86', + 'x86_amd64': 'x64', + 'x86_arm': 'arm', + 'x86_arm64': 'arm64' +} + + +def _msvc14_find_vcvarsall(plat_spec): + """Python 3.8 "distutils/_msvccompiler.py" backport""" + _, best_dir = _msvc14_find_vc2017() + vcruntime = None + + if plat_spec in PLAT_SPEC_TO_RUNTIME: + vcruntime_plat = PLAT_SPEC_TO_RUNTIME[plat_spec] + else: + vcruntime_plat = 'x64' if 'amd64' in plat_spec else 'x86' + + if best_dir: + vcredist = join(best_dir, "..", "..", "redist", "MSVC", "**", + vcruntime_plat, "Microsoft.VC14*.CRT", + "vcruntime140.dll") + try: + import glob + vcruntime = glob.glob(vcredist, recursive=True)[-1] + except (ImportError, OSError, LookupError): + vcruntime = None + + if not best_dir: + best_version, best_dir = _msvc14_find_vc2015() + if best_version: + vcruntime = join(best_dir, 'redist', vcruntime_plat, + "Microsoft.VC140.CRT", "vcruntime140.dll") + + if not best_dir: + return None, None + + vcvarsall = join(best_dir, "vcvarsall.bat") + if not isfile(vcvarsall): + return None, None + + if not vcruntime or not isfile(vcruntime): + vcruntime = None + + return vcvarsall, vcruntime + + +def _msvc14_get_vc_env(plat_spec): + """Python 3.8 "distutils/_msvccompiler.py" backport""" + if "DISTUTILS_USE_SDK" in environ: + return { + key.lower(): value + for key, value in environ.items() + } + + vcvarsall, vcruntime = _msvc14_find_vcvarsall(plat_spec) + if not vcvarsall: + raise distutils.errors.DistutilsPlatformError( + "Unable to find vcvarsall.bat" + ) + + try: + out = subprocess.check_output( + 'cmd /u /c "{}" {} && set'.format(vcvarsall, plat_spec), + stderr=subprocess.STDOUT, + ).decode('utf-16le', errors='replace') + except subprocess.CalledProcessError as exc: + raise distutils.errors.DistutilsPlatformError( + "Error executing {}".format(exc.cmd) + ) + + env = { + key.lower(): value + for key, _, value in + (line.partition('=') for line in out.splitlines()) + if key and value + } + + if vcruntime: + env['py_vcruntime_redist'] = vcruntime + return env + + +def msvc14_get_vc_env(plat_spec): + """ + Patched "distutils._msvccompiler._get_vc_env" for support extra + Microsoft Visual C++ 14.X compilers. + + Set environment without use of "vcvarsall.bat". + + Parameters + ---------- + plat_spec: str + Target architecture. + + Return + ------ + dict + environment + """ + + # Always use backport from CPython 3.8 + try: + return _msvc14_get_vc_env(plat_spec) + 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. + # Note: No download link available from Microsoft. + message += ' Get it with "Microsoft Windows SDK 7.0"' + 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.X Redirect user to latest Visual C++ Build Tools + message += (' Get it with "Build Tools for Visual Studio": ' + r'https://visualstudio.microsoft.com/downloads/') + + exc.args = (message, ) + + +class PlatformInfo: + """ + Current and Target Architectures information. + + Parameters + ---------- + arch: str + Target architecture. + """ + current_cpu = environ.get('processor_architecture', '').lower() + + def __init__(self, arch): + self.arch = arch.lower().replace('x64', 'amd64') + + @property + def target_cpu(self): + """ + Return Target CPU architecture. + + Return + ------ + str + Target CPU + """ + return self.arch[self.arch.find('_') + 1:] + + def target_is_x86(self): + """ + Return True if target CPU is x86 32 bits.. + + Return + ------ + bool + CPU is x86 32 bits + """ + return self.target_cpu == 'x86' + + def current_is_x86(self): + """ + Return True if current CPU is x86 32 bits.. + + Return + ------ + bool + CPU is x86 32 bits + """ + 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 + ------ + str + subfolder: '\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 + ------ + str + subfolder: '\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 architecture is + not x86. + + Return + ------ + str + subfolder: '' 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 information. + + 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 + ------ + str + Registry key + """ + return 'VisualStudio' + + @property + def sxs(self): + """ + Microsoft Visual Studio SxS registry key. + + Return + ------ + str + Registry key + """ + return join(self.visualstudio, 'SxS') + + @property + def vc(self): + """ + Microsoft Visual C++ VC7 registry key. + + Return + ------ + str + Registry key + """ + return join(self.sxs, 'VC7') + + @property + def vs(self): + """ + Microsoft Visual Studio VS7 registry key. + + Return + ------ + str + Registry key + """ + return join(self.sxs, 'VS7') + + @property + def vc_for_python(self): + """ + Microsoft Visual C++ for Python registry key. + + Return + ------ + str + Registry key + """ + return r'DevDiv\VCForPython' + + @property + def microsoft_sdk(self): + """ + Microsoft SDK registry key. + + Return + ------ + str + Registry key + """ + return 'Microsoft SDKs' + + @property + def windows_sdk(self): + """ + Microsoft Windows/Platform SDK registry key. + + Return + ------ + str + Registry key + """ + return join(self.microsoft_sdk, 'Windows') + + @property + def netfx_sdk(self): + """ + Microsoft .NET Framework SDK registry key. + + Return + ------ + str + Registry key + """ + return join(self.microsoft_sdk, 'NETFXSDK') + + @property + def windows_kits_roots(self): + """ + Microsoft Windows Kits Roots registry key. + + Return + ------ + str + 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 + Registry key + """ + node64 = '' if self.pi.current_is_x86() or x86 else 'Wow6432Node' + return 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 information. + + 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 comparison. + WinDir = environ.get('WinDir', '') + ProgramFiles = environ.get('ProgramFiles', '') + ProgramFilesx86 = environ.get('ProgramFiles(x86)', ProgramFiles) + + def __init__(self, registry_info, vc_ver=None): + self.ri = registry_info + self.pi = self.ri.pi + + self.known_vs_paths = self.find_programdata_vs_vers() + + # Except for VS15+, VC version is aligned with VS version + self.vs_ver = self.vc_ver = ( + vc_ver or self._find_latest_available_vs_ver()) + + def _find_latest_available_vs_ver(self): + """ + Find the latest VC version + + Return + ------ + float + version + """ + reg_vc_vers = self.find_reg_vs_vers() + + if not (reg_vc_vers or self.known_vs_paths): + raise distutils.errors.DistutilsPlatformError( + 'No Microsoft Visual C++ version found') + + vc_vers = set(reg_vc_vers) + vc_vers.update(self.known_vs_paths) + return sorted(vc_vers)[-1] + + def find_reg_vs_vers(self): + """ + Find Microsoft Visual Studio versions available in registry. + + Return + ------ + list of float + Versions + """ + ms = self.ri.microsoft + vckeys = (self.ri.vc, self.ri.vc_for_python, self.ri.vs) + vs_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 vs_vers: + vs_vers.append(ver) + except ValueError: + pass + for i in range(subkeys): + try: + ver = float(winreg.EnumKey(bkey, i)) + if ver not in vs_vers: + vs_vers.append(ver) + except ValueError: + pass + return sorted(vs_vers) + + def find_programdata_vs_vers(self): + r""" + Find Visual studio 2017+ versions from information in + "C:\ProgramData\Microsoft\VisualStudio\Packages\_Instances". + + Return + ------ + dict + float version as key, path as value. + """ + vs_versions = {} + instances_dir = \ + r'C:\ProgramData\Microsoft\VisualStudio\Packages\_Instances' + + try: + hashed_names = listdir(instances_dir) + + except (OSError, IOError): + # Directory not exists with all Visual Studio versions + return vs_versions + + for name in hashed_names: + try: + # Get VS installation path from "state.json" file + state_path = join(instances_dir, name, 'state.json') + with open(state_path, 'rt', encoding='utf-8') as state_file: + state = json.load(state_file) + vs_path = state['installationPath'] + + # Raises OSError if this VS installation does not contain VC + listdir(join(vs_path, r'VC\Tools\MSVC')) + + # Store version and path + vs_versions[self._as_float_version( + state['installationVersion'])] = vs_path + + except (OSError, IOError, KeyError): + # Skip if "state.json" file is missing or bad format + continue + + return vs_versions + + @staticmethod + def _as_float_version(version): + """ + Return a string version as a simplified float version (major.minor) + + Parameters + ---------- + version: str + Version. + + Return + ------ + float + version + """ + return float('.'.join(version.split('.')[:2])) + + @property + def VSInstallDir(self): + """ + Microsoft Visual Studio directory. + + Return + ------ + str + path + """ + # Default path + default = join(self.ProgramFilesx86, + 'Microsoft Visual Studio %0.1f' % self.vs_ver) + + # Try to get path from registry, if fail use default path + return self.ri.lookup(self.ri.vs, '%0.1f' % self.vs_ver) or default + + @property + def VCInstallDir(self): + """ + Microsoft Visual C++ directory. + + Return + ------ + str + path + """ + path = self._guess_vc() or self._guess_vc_legacy() + + if not isdir(path): + msg = 'Microsoft Visual C++ directory not found' + raise distutils.errors.DistutilsPlatformError(msg) + + return path + + def _guess_vc(self): + """ + Locate Visual C++ for VS2017+. + + Return + ------ + str + path + """ + if self.vs_ver <= 14.0: + return '' + + try: + # First search in known VS paths + vs_dir = self.known_vs_paths[self.vs_ver] + except KeyError: + # Else, search with path from registry + vs_dir = self.VSInstallDir + + guess_vc = join(vs_dir, r'VC\Tools\MSVC') + + # Subdir with VC exact version as name + try: + # Update the VC version with real one instead of VS version + vc_ver = listdir(guess_vc)[-1] + self.vc_ver = self._as_float_version(vc_ver) + return join(guess_vc, vc_ver) + except (OSError, IOError, IndexError): + return '' + + def _guess_vc_legacy(self): + """ + Locate Visual C++ for versions prior to 2017. + + Return + ------ + str + path + """ + default = join(self.ProgramFilesx86, + r'Microsoft Visual Studio %0.1f\VC' % self.vs_ver) + + # Try to get "VC++ for Python" path from registry as default path + reg_path = join(self.ri.vc_for_python, '%0.1f' % self.vs_ver) + python_vc = self.ri.lookup(reg_path, 'installdir') + default_vc = join(python_vc, 'VC') if python_vc else default + + # Try to get path from registry, if fail use default path + return self.ri.lookup(self.ri.vc, '%0.1f' % self.vs_ver) or default_vc + + @property + def WindowsSdkVersion(self): + """ + Microsoft Windows SDK versions for specified MSVC++ version. + + Return + ------ + tuple of str + versions + """ + if self.vs_ver <= 9.0: + return '7.0', '6.1', '6.0a' + elif self.vs_ver == 10.0: + return '7.1', '7.0a' + elif self.vs_ver == 11.0: + return '8.0', '8.0a' + elif self.vs_ver == 12.0: + return '8.1', '8.1a' + elif self.vs_ver >= 14.0: + return '10.0', '8.1' + + @property + def WindowsSdkLastVersion(self): + """ + Microsoft Windows SDK last version. + + Return + ------ + str + version + """ + return self._use_last_dir_name(join(self.WindowsSdkDir, 'lib')) + + @property + def WindowsSdkDir(self): + """ + Microsoft Windows SDK directory. + + Return + ------ + str + path + """ + sdkdir = '' + for ver in self.WindowsSdkVersion: + # Try to get it from registry + loc = join(self.ri.windows_sdk, 'v%s' % ver) + sdkdir = self.ri.lookup(loc, 'installationfolder') + if sdkdir: + break + if not sdkdir or not isdir(sdkdir): + # Try to get "VC++ for Python" version from registry + path = join(self.ri.vc_for_python, '%0.1f' % self.vc_ver) + install_base = self.ri.lookup(path, 'installdir') + if install_base: + sdkdir = join(install_base, 'WinSDK') + if not sdkdir or not 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 = join(self.ProgramFiles, path) + if isdir(d): + sdkdir = d + if not sdkdir or not isdir(sdkdir): + # If fail, use default old path + for ver in self.WindowsSdkVersion: + path = r'Microsoft SDKs\Windows\v%s' % ver + d = join(self.ProgramFiles, path) + if isdir(d): + sdkdir = d + if not sdkdir: + # If fail, use Platform SDK + sdkdir = join(self.VCInstallDir, 'PlatformSDK') + return sdkdir + + @property + def WindowsSDKExecutablePath(self): + """ + Microsoft Windows SDK executable directory. + + Return + ------ + str + path + """ + # Find WinSDK NetFx Tools registry dir name + if self.vs_ver <= 11.0: + netfxver = 35 + arch = '' + else: + netfxver = 40 + hidex86 = True if self.vs_ver <= 12.0 else False + arch = self.pi.current_dir(x64=True, hidex86=hidex86) + fx = 'WinSDK-NetFx%dTools%s' % (netfxver, arch.replace('\\', '-')) + + # list all possibles registry paths + regpaths = [] + if self.vs_ver >= 14.0: + for ver in self.NetFxSdkVersion: + regpaths += [join(self.ri.netfx_sdk, ver, fx)] + + for ver in self.WindowsSdkVersion: + regpaths += [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: + return execpath + + @property + def FSharpInstallDir(self): + """ + Microsoft Visual F# directory. + + Return + ------ + str + path + """ + path = join(self.ri.visualstudio, r'%0.1f\Setup\F#' % self.vs_ver) + return self.ri.lookup(path, 'productdir') or '' + + @property + def UniversalCRTSdkDir(self): + """ + Microsoft Universal CRT SDK directory. + + Return + ------ + str + path + """ + # Set Kit Roots versions for specified MSVC++ version + vers = ('10', '81') if self.vs_ver >= 14.0 else () + + # 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: + return sdkdir or '' + + @property + def UniversalCRTSdkLastVersion(self): + """ + Microsoft Universal C Runtime SDK last version. + + Return + ------ + str + version + """ + return self._use_last_dir_name(join(self.UniversalCRTSdkDir, 'lib')) + + @property + def NetFxSdkVersion(self): + """ + Microsoft .NET Framework SDK versions. + + Return + ------ + tuple of str + versions + """ + # Set FxSdk versions for specified VS version + return (('4.7.2', '4.7.1', '4.7', + '4.6.2', '4.6.1', '4.6', + '4.5.2', '4.5.1', '4.5') + if self.vs_ver >= 14.0 else ()) + + @property + def NetFxSdkDir(self): + """ + Microsoft .NET Framework SDK directory. + + Return + ------ + str + path + """ + sdkdir = '' + for ver in self.NetFxSdkVersion: + loc = join(self.ri.netfx_sdk, ver) + sdkdir = self.ri.lookup(loc, 'kitsinstallationfolder') + if sdkdir: + break + return sdkdir + + @property + def FrameworkDir32(self): + """ + Microsoft .NET Framework 32bit directory. + + Return + ------ + str + path + """ + # Default path + guess_fw = 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. + + Return + ------ + str + path + """ + # Default path + guess_fw = 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 + ------ + tuple of str + versions + """ + return self._find_dot_net_versions(32) + + @property + def FrameworkVersion64(self): + """ + Microsoft .NET Framework 64bit versions. + + Return + ------ + tuple of str + 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. + + Return + ------ + tuple of str + versions + """ + # 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.vs_ver >= 12.0: + return ver, 'v4.0' + elif self.vs_ver >= 10.0: + return 'v4.0.30319' if ver.lower()[:2] != 'v4' else ver, 'v3.5' + elif self.vs_ver == 9.0: + return 'v3.5', 'v2.0.50727' + elif self.vs_ver == 8.0: + return 'v3.0', 'v2.0.50727' + + @staticmethod + def _use_last_dir_name(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 starting by this prefix + + Return + ------ + str + name + """ + matching_dirs = ( + dir_name + for dir_name in reversed(listdir(path)) + if isdir(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.X. + + 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 comparison. + + 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 vs_ver(self): + """ + Microsoft Visual Studio. + + Return + ------ + float + version + """ + return self.si.vs_ver + + @property + def vc_ver(self): + """ + Microsoft Visual C++ version. + + Return + ------ + float + version + """ + return self.si.vc_ver + + @property + def VSTools(self): + """ + Microsoft Visual Studio Tools. + + Return + ------ + list of str + paths + """ + paths = [r'Common7\IDE', r'Common7\Tools'] + + if self.vs_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 [join(self.si.VSInstallDir, path) for path in paths] + + @property + def VCIncludes(self): + """ + Microsoft Visual C++ & Microsoft Foundation Class Includes. + + Return + ------ + list of str + paths + """ + return [join(self.si.VCInstallDir, 'Include'), + join(self.si.VCInstallDir, r'ATLMFC\Include')] + + @property + def VCLibraries(self): + """ + Microsoft Visual C++ & Microsoft Foundation Class Libraries. + + Return + ------ + list of str + paths + """ + if self.vs_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.vs_ver >= 14.0: + paths += [r'Lib\store%s' % arch_subdir] + + return [join(self.si.VCInstallDir, path) for path in paths] + + @property + def VCStoreRefs(self): + """ + Microsoft Visual C++ store references Libraries. + + Return + ------ + list of str + paths + """ + if self.vs_ver < 14.0: + return [] + return [join(self.si.VCInstallDir, r'Lib\store\references')] + + @property + def VCTools(self): + """ + Microsoft Visual C++ Tools. + + Return + ------ + list of str + paths + """ + si = self.si + tools = [join(si.VCInstallDir, 'VCPackages')] + + forcex86 = True if self.vs_ver <= 10.0 else False + arch_subdir = self.pi.cross_dir(forcex86) + if arch_subdir: + tools += [join(si.VCInstallDir, 'Bin%s' % arch_subdir)] + + if self.vs_ver == 14.0: + path = 'Bin%s' % self.pi.current_dir(hidex86=True) + tools += [join(si.VCInstallDir, path)] + + elif self.vs_ver >= 15.0: + host_dir = (r'bin\HostX86%s' if self.pi.current_is_x86() else + r'bin\HostX64%s') + tools += [join( + si.VCInstallDir, host_dir % self.pi.target_dir(x64=True))] + + if self.pi.current_cpu != self.pi.target_cpu: + tools += [join( + si.VCInstallDir, host_dir % self.pi.current_dir(x64=True))] + + else: + tools += [join(si.VCInstallDir, 'Bin')] + + return tools + + @property + def OSLibraries(self): + """ + Microsoft Windows SDK Libraries. + + Return + ------ + list of str + paths + """ + if self.vs_ver <= 10.0: + arch_subdir = self.pi.target_dir(hidex86=True, x64=True) + return [join(self.si.WindowsSdkDir, 'Lib%s' % arch_subdir)] + + else: + arch_subdir = self.pi.target_dir(x64=True) + lib = join(self.si.WindowsSdkDir, 'lib') + libver = self._sdk_subdir + return [join(lib, '%sum%s' % (libver, arch_subdir))] + + @property + def OSIncludes(self): + """ + Microsoft Windows SDK Include. + + Return + ------ + list of str + paths + """ + include = join(self.si.WindowsSdkDir, 'include') + + if self.vs_ver <= 10.0: + return [include, join(include, 'gl')] + + else: + if self.vs_ver >= 14.0: + sdkver = self._sdk_subdir + else: + sdkver = '' + return [join(include, '%sshared' % sdkver), + join(include, '%sum' % sdkver), + join(include, '%swinrt' % sdkver)] + + @property + def OSLibpath(self): + """ + Microsoft Windows SDK Libraries Paths. + + Return + ------ + list of str + paths + """ + ref = join(self.si.WindowsSdkDir, 'References') + libpath = [] + + if self.vs_ver <= 9.0: + libpath += self.OSLibraries + + if self.vs_ver >= 11.0: + libpath += [join(ref, r'CommonConfiguration\Neutral')] + + if self.vs_ver >= 14.0: + libpath += [ + ref, + join(self.si.WindowsSdkDir, 'UnionMetadata'), + join( + ref, 'Windows.Foundation.UniversalApiContract', '1.0.0.0'), + join(ref, 'Windows.Foundation.FoundationContract', '1.0.0.0'), + join( + ref, 'Windows.Networking.Connectivity.WwanContract', + '1.0.0.0'), + join( + self.si.WindowsSdkDir, 'ExtensionSDKs', 'Microsoft.VCLibs', + '%0.1f' % self.vs_ver, 'References', 'CommonConfiguration', + 'neutral'), + ] + return libpath + + @property + def SdkTools(self): + """ + Microsoft Windows SDK Tools. + + Return + ------ + list of str + paths + """ + return list(self._sdk_tools()) + + def _sdk_tools(self): + """ + Microsoft Windows SDK Tools paths generator. + + Return + ------ + generator of str + paths + """ + if self.vs_ver < 15.0: + bin_dir = 'Bin' if self.vs_ver <= 11.0 else r'Bin\x86' + yield 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 join(self.si.WindowsSdkDir, path) + + if self.vs_ver in (10.0, 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 join(self.si.WindowsSdkDir, path) + + elif self.vs_ver >= 15.0: + path = join(self.si.WindowsSdkDir, 'Bin') + arch_subdir = self.pi.current_dir(x64=True) + sdkver = self.si.WindowsSdkLastVersion + yield 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. + + Return + ------ + str + subdir + """ + ucrtver = self.si.WindowsSdkLastVersion + return ('%s\\' % ucrtver) if ucrtver else '' + + @property + def SdkSetup(self): + """ + Microsoft Windows SDK Setup. + + Return + ------ + list of str + paths + """ + if self.vs_ver > 9.0: + return [] + + return [join(self.si.WindowsSdkDir, 'Setup')] + + @property + def FxTools(self): + """ + Microsoft .NET Framework Tools. + + Return + ------ + list of str + paths + """ + pi = self.pi + si = self.si + + if self.vs_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 += [join(si.FrameworkDir32, ver) + for ver in si.FrameworkVersion32] + if include64: + tools += [join(si.FrameworkDir64, ver) + for ver in si.FrameworkVersion64] + return tools + + @property + def NetFxSDKLibraries(self): + """ + Microsoft .Net Framework SDK Libraries. + + Return + ------ + list of str + paths + """ + if self.vs_ver < 14.0 or not self.si.NetFxSdkDir: + return [] + + arch_subdir = self.pi.target_dir(x64=True) + return [join(self.si.NetFxSdkDir, r'lib\um%s' % arch_subdir)] + + @property + def NetFxSDKIncludes(self): + """ + Microsoft .Net Framework SDK Includes. + + Return + ------ + list of str + paths + """ + if self.vs_ver < 14.0 or not self.si.NetFxSdkDir: + return [] + + return [join(self.si.NetFxSdkDir, r'include\um')] + + @property + def VsTDb(self): + """ + Microsoft Visual Studio Team System Database. + + Return + ------ + list of str + paths + """ + return [join(self.si.VSInstallDir, r'VSTSDB\Deploy')] + + @property + def MSBuild(self): + """ + Microsoft Build Engine. + + Return + ------ + list of str + paths + """ + if self.vs_ver < 12.0: + return [] + elif self.vs_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.vs_ver, arch_subdir) + build = [join(base_path, path)] + + if self.vs_ver >= 15.0: + # Add Roslyn C# & Visual Basic Compiler + build += [join(base_path, path, 'Roslyn')] + + return build + + @property + def HTMLHelpWorkshop(self): + """ + Microsoft HTML Help Workshop. + + Return + ------ + list of str + paths + """ + if self.vs_ver < 11.0: + return [] + + return [join(self.si.ProgramFilesx86, 'HTML Help Workshop')] + + @property + def UCRTLibraries(self): + """ + Microsoft Universal C Runtime SDK Libraries. + + Return + ------ + list of str + paths + """ + if self.vs_ver < 14.0: + return [] + + arch_subdir = self.pi.target_dir(x64=True) + lib = join(self.si.UniversalCRTSdkDir, 'lib') + ucrtver = self._ucrt_subdir + return [join(lib, '%sucrt%s' % (ucrtver, arch_subdir))] + + @property + def UCRTIncludes(self): + """ + Microsoft Universal C Runtime SDK Include. + + Return + ------ + list of str + paths + """ + if self.vs_ver < 14.0: + return [] + + include = join(self.si.UniversalCRTSdkDir, 'include') + return [join(include, '%sucrt' % self._ucrt_subdir)] + + @property + def _ucrt_subdir(self): + """ + Microsoft Universal C Runtime SDK version subdir. + + Return + ------ + str + subdir + """ + ucrtver = self.si.UniversalCRTSdkLastVersion + return ('%s\\' % ucrtver) if ucrtver else '' + + @property + def FSharp(self): + """ + Microsoft Visual F#. + + Return + ------ + list of str + paths + """ + if 11.0 > self.vs_ver > 12.0: + return [] + + return [self.si.FSharpInstallDir] + + @property + def VCRuntimeRedist(self): + """ + Microsoft Visual C++ runtime redistributable dll. + + Return + ------ + str + path + """ + vcruntime = 'vcruntime%d0.dll' % self.vc_ver + arch_subdir = self.pi.target_dir(x64=True).strip('\\') + + # Installation prefixes candidates + prefixes = [] + tools_path = self.si.VCInstallDir + redist_path = dirname(tools_path.replace(r'\Tools', r'\Redist')) + if isdir(redist_path): + # Redist version may not be exactly the same as tools + redist_path = join(redist_path, listdir(redist_path)[-1]) + prefixes += [redist_path, join(redist_path, 'onecore')] + + prefixes += [join(tools_path, 'redist')] # VS14 legacy path + + # CRT directory + crt_dirs = ('Microsoft.VC%d.CRT' % (self.vc_ver * 10), + # Sometime store in directory with VS version instead of VC + 'Microsoft.VC%d.CRT' % (int(self.vs_ver) * 10)) + + # vcruntime path + for prefix, crt_dir in itertools.product(prefixes, crt_dirs): + path = join(prefix, arch_subdir, crt_dir, vcruntime) + if isfile(path): + return path + + def return_env(self, exists=True): + """ + Return environment dict. + + Parameters + ---------- + exists: bool + It True, only return existing paths. + + Return + ------ + dict + environment + """ + 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.vs_ver >= 14 and 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. + + Parameters + ---------- + name: str + Environment variable name + spec_path_lists: list of str + Paths + exists: bool + It True, only return existing paths. + + Return + ------ + str + Pathsep-separated paths + """ + # flatten spec_path_lists + spec_paths = itertools.chain.from_iterable(spec_path_lists) + env_paths = environ.get(name, '').split(pathsep) + paths = itertools.chain(spec_paths, env_paths) + extant_paths = list(filter(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 pathsep.join(unique_paths) + + # from Python docs + @staticmethod + 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 diff --git a/venv/lib/python3.8/site-packages/setuptools/namespaces.py b/venv/lib/python3.8/site-packages/setuptools/namespaces.py new file mode 100644 index 0000000..5f403c9 --- /dev/null +++ b/venv/lib/python3.8/site-packages/setuptools/namespaces.py @@ -0,0 +1,111 @@ +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/venv/lib/python3.8/site-packages/setuptools/package_index.py b/venv/lib/python3.8/site-packages/setuptools/package_index.py new file mode 100644 index 0000000..0744ea2 --- /dev/null +++ b/venv/lib/python3.8/site-packages/setuptools/package_index.py @@ -0,0 +1,1140 @@ +"""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'([^<]+)\n\s+\(md5\)' +) +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='{}.{}'.format(*sys.version_info), 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 ("Home Page", "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'(?Psha1|sha224|sha384|sha256|sha512|md5)=' + r'(?P[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 + if isinstance(f, urllib.error.HTTPError) and f.code == 401: + self.info("Authentication error: %s" % f.msg) + 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: '%s' % 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'([^- ]+ - )?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 macOS 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/venv/lib/python3.8/site-packages/setuptools/py27compat.py b/venv/lib/python3.8/site-packages/setuptools/py27compat.py new file mode 100644 index 0000000..ba39af5 --- /dev/null +++ b/venv/lib/python3.8/site-packages/setuptools/py27compat.py @@ -0,0 +1,60 @@ +""" +Compatibility Support for Python 2.7 and earlier +""" + +import sys +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): # noqa + 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""" + + +try: + from ._imp import find_module, PY_COMPILED, PY_FROZEN, PY_SOURCE + from ._imp import get_frozen_object, get_module +except ImportError: + import imp + from imp import PY_COMPILED, PY_FROZEN, PY_SOURCE # noqa + + 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 == imp.PKG_DIRECTORY: + parts = parts or ['__init__'] + paths = [path] + + elif parts: + raise ImportError("Can't find %r in %s" % (parts, module)) + + return info + + def get_frozen_object(module, paths): + return imp.get_frozen_object(module) + + def get_module(module, paths, info): + imp.load_module(module, *info) + return sys.modules[module] diff --git a/venv/lib/python3.8/site-packages/setuptools/py31compat.py b/venv/lib/python3.8/site-packages/setuptools/py31compat.py new file mode 100644 index 0000000..e1da7ee --- /dev/null +++ b/venv/lib/python3.8/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/venv/lib/python3.8/site-packages/setuptools/py33compat.py b/venv/lib/python3.8/site-packages/setuptools/py33compat.py new file mode 100644 index 0000000..cb69443 --- /dev/null +++ b/venv/lib/python3.8/site-packages/setuptools/py33compat.py @@ -0,0 +1,59 @@ +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', None) +if unescape is None: + # HTMLParser.unescape is deprecated since Python 3.4, and will be removed + # from 3.9. + unescape = html_parser.HTMLParser().unescape diff --git a/venv/lib/python3.8/site-packages/setuptools/py34compat.py b/venv/lib/python3.8/site-packages/setuptools/py34compat.py new file mode 100644 index 0000000..3ad9172 --- /dev/null +++ b/venv/lib/python3.8/site-packages/setuptools/py34compat.py @@ -0,0 +1,13 @@ +import importlib + +try: + import importlib.util +except ImportError: + pass + + +try: + module_from_spec = importlib.util.module_from_spec +except AttributeError: + def module_from_spec(spec): + return spec.loader.load_module(spec.name) diff --git a/venv/lib/python3.8/site-packages/setuptools/sandbox.py b/venv/lib/python3.8/site-packages/setuptools/sandbox.py new file mode 100644 index 0000000..e46dfc8 --- /dev/null +++ b/venv/lib/python3.8/site-packages/setuptools/sandbox.py @@ -0,0 +1,492 @@ +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 +from distutils.errors import DistutilsError +from pkg_resources import working_set + +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 + + +__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/venv/lib/python3.8/site-packages/setuptools/script (dev).tmpl b/venv/lib/python3.8/site-packages/setuptools/script (dev).tmpl new file mode 100644 index 0000000..39a24b0 --- /dev/null +++ b/venv/lib/python3.8/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/venv/lib/python3.8/site-packages/setuptools/script.tmpl b/venv/lib/python3.8/site-packages/setuptools/script.tmpl new file mode 100644 index 0000000..ff5efbc --- /dev/null +++ b/venv/lib/python3.8/site-packages/setuptools/script.tmpl @@ -0,0 +1,3 @@ +# EASY-INSTALL-SCRIPT: %(spec)r,%(script_name)r +__requires__ = %(spec)r +__import__('pkg_resources').run_script(%(spec)r, %(script_name)r) diff --git a/venv/lib/python3.8/site-packages/setuptools/site-patch.py b/venv/lib/python3.8/site-packages/setuptools/site-patch.py new file mode 100644 index 0000000..be0d43d --- /dev/null +++ b/venv/lib/python3.8/site-packages/setuptools/site-patch.py @@ -0,0 +1,76 @@ +def __boot(): + import sys + import os + PYTHONPATH = os.environ.get('PYTHONPATH') + if PYTHONPATH is None or (sys.platform == 'win32' and not PYTHONPATH): + PYTHONPATH = [] + else: + PYTHONPATH = PYTHONPATH.split(os.pathsep) + + pic = getattr(sys, 'path_importer_cache', {}) + stdpath = sys.path[len(PYTHONPATH):] + mydir = os.path.dirname(__file__) + + 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") + + # 2.2 comp + known_paths = dict([( + makepath(item)[1], 1) for item in sys.path]) # noqa + + oldpos = getattr(sys, '__egginsert', 0) # save old insertion position + sys.__egginsert = 0 # and reset the current one + + for item in PYTHONPATH: + addsitedir(item) # noqa + + sys.__egginsert += oldpos # restore effective old position + + d, nd = makepath(stdpath[0]) # noqa + insert_at = None + new_path = [] + + for item in sys.path: + p, np = makepath(item) # noqa + + if np == nd and insert_at is None: + # We've hit the first 'system' path entry, so added entries go here + insert_at = len(new_path) + + if np in known_paths or insert_at is None: + new_path.append(item) + else: + # new path after the insert point, back-insert it + new_path.insert(insert_at, item) + insert_at += 1 + + sys.path[:] = new_path + + +if __name__ == 'site': + __boot() + del __boot diff --git a/venv/lib/python3.8/site-packages/setuptools/ssl_support.py b/venv/lib/python3.8/site-packages/setuptools/ssl_support.py new file mode 100644 index 0000000..17c14c4 --- /dev/null +++ b/venv/lib/python3.8/site-packages/setuptools/ssl_support.py @@ -0,0 +1,265 @@ +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/venv/lib/python3.8/site-packages/setuptools/unicode_utils.py b/venv/lib/python3.8/site-packages/setuptools/unicode_utils.py new file mode 100644 index 0000000..7c63efd --- /dev/null +++ b/venv/lib/python3.8/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/venv/lib/python3.8/site-packages/setuptools/version.py b/venv/lib/python3.8/site-packages/setuptools/version.py new file mode 100644 index 0000000..95e1869 --- /dev/null +++ b/venv/lib/python3.8/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/venv/lib/python3.8/site-packages/setuptools/wheel.py b/venv/lib/python3.8/site-packages/setuptools/wheel.py new file mode 100644 index 0000000..ca09bd1 --- /dev/null +++ b/venv/lib/python3.8/site-packages/setuptools/wheel.py @@ -0,0 +1,217 @@ +"""Wheels support.""" + +from distutils.util import get_platform +from distutils import log +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.tags import sys_tags +from setuptools.extern.packaging.utils import canonicalize_name +from setuptools.extern.six import PY3 +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 = \ + "__import__('pkg_resources').declare_namespace(__name__)\n" + + +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 = set( + (t.interpreter, t.abi, t.platform) for t in sys_tags()) + 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, + ), + ) + # Temporarily disable info traces. + log_threshold = log._global_log.threshold + log.set_threshold(log.WARN) + try: + write_requirements( + setup_dist.get_command_obj('egg_info'), + None, + os.path.join(egg_info, 'requires.txt'), + ) + finally: + log.set_threshold(log_threshold) + + @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 not os.path.exists(mod_dir): + os.mkdir(mod_dir) + if not os.path.exists(mod_init): + with open(mod_init, 'w') as fp: + fp.write(NAMESPACE_PACKAGE_INIT) diff --git a/venv/lib/python3.8/site-packages/setuptools/windows_support.py b/venv/lib/python3.8/site-packages/setuptools/windows_support.py new file mode 100644 index 0000000..cb977cf --- /dev/null +++ b/venv/lib/python3.8/site-packages/setuptools/windows_support.py @@ -0,0 +1,29 @@ +import platform +import ctypes + + +def windows_only(func): + if platform.system() != 'Windows': + return lambda *args, **kwargs: None + return func + + +@windows_only +def hide_file(path): + """ + Set the hidden attribute on a file or directory. + + From http://stackoverflow.com/questions/19622133/ + + `path` must be text. + """ + __import__('ctypes.wintypes') + SetFileAttributes = ctypes.windll.kernel32.SetFileAttributesW + SetFileAttributes.argtypes = ctypes.wintypes.LPWSTR, ctypes.wintypes.DWORD + SetFileAttributes.restype = ctypes.wintypes.BOOL + + FILE_ATTRIBUTE_HIDDEN = 0x02 + + ret = SetFileAttributes(path, FILE_ATTRIBUTE_HIDDEN) + if not ret: + raise ctypes.WinError() diff --git a/venv/pyvenv.cfg b/venv/pyvenv.cfg new file mode 100644 index 0000000..e7c56d4 --- /dev/null +++ b/venv/pyvenv.cfg @@ -0,0 +1,3 @@ +home = /Users/taylorlynncurtis/opt/anaconda3/bin +include-system-site-packages = false +version = 3.8.5