From ef4f3fa3eabadedf55fe5fe8bdd362c67e372695 Mon Sep 17 00:00:00 2001 From: elukjanovica Date: Sun, 28 Apr 2024 10:06:39 +0300 Subject: [PATCH] --- main.py | 80 +- templates/admin/login.html | 79 + .../site-packages/_distutils_hack/__init__.py | 220 + .../__pycache__/__init__.cpython-312.pyc | Bin 0 -> 9862 bytes .../__pycache__/override.cpython-312.pyc | Bin 0 -> 302 bytes .../site-packages/_distutils_hack/override.py | 1 + .../site-packages/distutils-precedence.pth | 1 + .../site-packages/pkg_resources/__init__.py | 3325 ++++++++++++ .../__pycache__/__init__.cpython-312.pyc | Bin 0 -> 146172 bytes .../pkg_resources/_vendor/__init__.py | 0 .../__pycache__/__init__.cpython-312.pyc | Bin 0 -> 207 bytes .../typing_extensions.cpython-312.pyc | Bin 0 -> 89845 bytes .../_vendor/__pycache__/zipp.cpython-312.pyc | Bin 0 -> 14887 bytes .../_vendor/backports/__init__.py | 0 .../__pycache__/__init__.cpython-312.pyc | Bin 0 -> 217 bytes .../__pycache__/tarfile.cpython-312.pyc | Bin 0 -> 119465 bytes .../_vendor/backports/tarfile.py | 2900 ++++++++++ .../_vendor/importlib_resources/__init__.py | 36 + .../__pycache__/__init__.cpython-312.pyc | Bin 0 -> 694 bytes .../__pycache__/_adapters.cpython-312.pyc | Bin 0 -> 9739 bytes .../__pycache__/_common.cpython-312.pyc | Bin 0 -> 8788 bytes .../__pycache__/_compat.cpython-312.pyc | Bin 0 -> 5060 bytes .../__pycache__/_itertools.cpython-312.pyc | Bin 0 -> 1206 bytes .../__pycache__/_legacy.cpython-312.pyc | Bin 0 -> 5822 bytes .../__pycache__/abc.cpython-312.pyc | Bin 0 -> 8904 bytes .../__pycache__/readers.cpython-312.pyc | Bin 0 -> 7677 bytes .../__pycache__/simple.cpython-312.pyc | Bin 0 -> 5530 bytes .../_vendor/importlib_resources/_adapters.py | 170 + .../_vendor/importlib_resources/_common.py | 207 + .../_vendor/importlib_resources/_compat.py | 108 + .../_vendor/importlib_resources/_itertools.py | 35 + .../_vendor/importlib_resources/_legacy.py | 120 + .../_vendor/importlib_resources/abc.py | 170 + .../_vendor/importlib_resources/py.typed | 0 .../_vendor/importlib_resources/readers.py | 120 + .../_vendor/importlib_resources/simple.py | 106 + .../pkg_resources/_vendor/jaraco/__init__.py | 0 .../__pycache__/__init__.cpython-312.pyc | Bin 0 -> 214 bytes .../__pycache__/context.cpython-312.pyc | Bin 0 -> 14380 bytes .../pkg_resources/_vendor/jaraco/context.py | 361 ++ .../_vendor/jaraco/functools/__init__.py | 633 +++ .../_vendor/jaraco/functools/__init__.pyi | 128 + .../__pycache__/__init__.cpython-312.pyc | Bin 0 -> 23025 bytes .../_vendor/jaraco/functools/py.typed | 0 .../_vendor/jaraco/text/__init__.py | 599 +++ .../text/__pycache__/__init__.cpython-312.pyc | Bin 0 -> 24461 bytes .../_vendor/more_itertools/__init__.py | 6 + .../_vendor/more_itertools/__init__.pyi | 2 + .../__pycache__/__init__.cpython-312.pyc | Bin 0 -> 376 bytes .../__pycache__/more.cpython-312.pyc | Bin 0 -> 167473 bytes .../__pycache__/recipes.cpython-312.pyc | Bin 0 -> 35497 bytes .../_vendor/more_itertools/more.py | 4655 +++++++++++++++++ .../_vendor/more_itertools/more.pyi | 695 +++ .../_vendor/more_itertools/py.typed | 0 .../_vendor/more_itertools/recipes.py | 1012 ++++ .../_vendor/more_itertools/recipes.pyi | 128 + .../_vendor/packaging/__init__.py | 15 + .../__pycache__/__init__.cpython-312.pyc | Bin 0 -> 582 bytes .../__pycache__/_elffile.cpython-312.pyc | Bin 0 -> 5050 bytes .../__pycache__/_manylinux.cpython-312.pyc | Bin 0 -> 9930 bytes .../__pycache__/_musllinux.cpython-312.pyc | Bin 0 -> 4613 bytes .../__pycache__/_parser.cpython-312.pyc | Bin 0 -> 14088 bytes .../__pycache__/_structures.cpython-312.pyc | Bin 0 -> 3265 bytes .../__pycache__/_tokenizer.cpython-312.pyc | Bin 0 -> 7961 bytes .../__pycache__/markers.cpython-312.pyc | Bin 0 -> 10523 bytes .../__pycache__/metadata.cpython-312.pyc | Bin 0 -> 26969 bytes .../__pycache__/requirements.cpython-312.pyc | Bin 0 -> 4478 bytes .../__pycache__/specifiers.cpython-312.pyc | Bin 0 -> 39575 bytes .../__pycache__/tags.cpython-312.pyc | Bin 0 -> 21785 bytes .../__pycache__/utils.cpython-312.pyc | Bin 0 -> 7308 bytes .../__pycache__/version.cpython-312.pyc | Bin 0 -> 20021 bytes .../_vendor/packaging/_elffile.py | 108 + .../_vendor/packaging/_manylinux.py | 260 + .../_vendor/packaging/_musllinux.py | 83 + .../_vendor/packaging/_parser.py | 356 ++ .../_vendor/packaging/_structures.py | 61 + .../_vendor/packaging/_tokenizer.py | 192 + .../_vendor/packaging/markers.py | 252 + .../_vendor/packaging/metadata.py | 825 +++ .../pkg_resources/_vendor/packaging/py.typed | 0 .../_vendor/packaging/requirements.py | 90 + .../_vendor/packaging/specifiers.py | 1017 ++++ .../pkg_resources/_vendor/packaging/tags.py | 571 ++ .../pkg_resources/_vendor/packaging/utils.py | 172 + .../_vendor/packaging/version.py | 563 ++ .../_vendor/platformdirs/__init__.py | 342 ++ .../_vendor/platformdirs/__main__.py | 46 + .../__pycache__/__init__.cpython-312.pyc | Bin 0 -> 13224 bytes .../__pycache__/__main__.cpython-312.pyc | Bin 0 -> 1812 bytes .../__pycache__/android.cpython-312.pyc | Bin 0 -> 5809 bytes .../__pycache__/api.cpython-312.pyc | Bin 0 -> 6631 bytes .../__pycache__/macos.cpython-312.pyc | Bin 0 -> 4255 bytes .../__pycache__/unix.cpython-312.pyc | Bin 0 -> 9992 bytes .../__pycache__/version.cpython-312.pyc | Bin 0 -> 335 bytes .../__pycache__/windows.cpython-312.pyc | Bin 0 -> 9296 bytes .../_vendor/platformdirs/android.py | 120 + .../pkg_resources/_vendor/platformdirs/api.py | 156 + .../_vendor/platformdirs/macos.py | 64 + .../_vendor/platformdirs/py.typed | 0 .../_vendor/platformdirs/unix.py | 181 + .../_vendor/platformdirs/version.py | 4 + .../_vendor/platformdirs/windows.py | 184 + .../_vendor/typing_extensions.py | 2209 ++++++++ .../pkg_resources/_vendor/zipp.py | 329 ++ .../pkg_resources/extern/__init__.py | 81 + .../__pycache__/__init__.cpython-312.pyc | Bin 0 -> 4068 bytes .../setuptools-69.5.1.dist-info/INSTALLER | 1 + .../setuptools-69.5.1.dist-info/LICENSE | 17 + .../setuptools-69.5.1.dist-info/METADATA | 130 + .../setuptools-69.5.1.dist-info/RECORD | 501 ++ .../setuptools-69.5.1.dist-info/REQUESTED | 0 .../setuptools-69.5.1.dist-info/WHEEL | 5 + .../entry_points.txt | 56 + .../setuptools-69.5.1.dist-info/top_level.txt | 3 + venv/Lib/site-packages/setuptools/__init__.py | 271 + .../__pycache__/__init__.cpython-312.pyc | Bin 0 -> 12726 bytes .../_core_metadata.cpython-312.pyc | Bin 0 -> 12734 bytes .../__pycache__/_entry_points.cpython-312.pyc | Bin 0 -> 4685 bytes .../__pycache__/_imp.cpython-312.pyc | Bin 0 -> 3583 bytes .../__pycache__/_importlib.cpython-312.pyc | Bin 0 -> 1807 bytes .../__pycache__/_itertools.cpython-312.pyc | Bin 0 -> 1092 bytes .../_normalization.cpython-312.pyc | Bin 0 -> 6380 bytes .../__pycache__/_path.cpython-312.pyc | Bin 0 -> 2089 bytes .../__pycache__/_reqs.cpython-312.pyc | Bin 0 -> 1865 bytes .../__pycache__/archive_util.cpython-312.pyc | Bin 0 -> 9212 bytes .../__pycache__/build_meta.cpython-312.pyc | Bin 0 -> 23456 bytes .../__pycache__/dep_util.cpython-312.pyc | Bin 0 -> 883 bytes .../__pycache__/depends.cpython-312.pyc | Bin 0 -> 7469 bytes .../__pycache__/discovery.cpython-312.pyc | Bin 0 -> 29059 bytes .../__pycache__/dist.cpython-312.pyc | Bin 0 -> 45469 bytes .../__pycache__/errors.cpython-312.pyc | Bin 0 -> 3462 bytes .../__pycache__/extension.cpython-312.pyc | Bin 0 -> 6598 bytes .../__pycache__/glob.cpython-312.pyc | Bin 0 -> 5987 bytes .../__pycache__/installer.cpython-312.pyc | Bin 0 -> 6474 bytes .../__pycache__/launch.cpython-312.pyc | Bin 0 -> 1324 bytes .../__pycache__/logging.cpython-312.pyc | Bin 0 -> 2061 bytes .../__pycache__/modified.cpython-312.pyc | Bin 0 -> 359 bytes .../__pycache__/monkey.cpython-312.pyc | Bin 0 -> 6015 bytes .../__pycache__/msvc.cpython-312.pyc | Bin 0 -> 60692 bytes .../__pycache__/namespaces.cpython-312.pyc | Bin 0 -> 5310 bytes .../__pycache__/package_index.cpython-312.pyc | Bin 0 -> 52741 bytes .../__pycache__/sandbox.cpython-312.pyc | Bin 0 -> 24024 bytes .../__pycache__/unicode_utils.cpython-312.pyc | Bin 0 -> 1665 bytes .../__pycache__/version.cpython-312.pyc | Bin 0 -> 435 bytes .../__pycache__/warnings.cpython-312.pyc | Bin 0 -> 4957 bytes .../__pycache__/wheel.cpython-312.pyc | Bin 0 -> 13491 bytes .../windows_support.cpython-312.pyc | Bin 0 -> 1460 bytes .../setuptools/_core_metadata.py | 268 + .../setuptools/_distutils/__init__.py | 14 + .../__pycache__/__init__.cpython-312.pyc | Bin 0 -> 543 bytes .../__pycache__/_collections.cpython-312.pyc | Bin 0 -> 8050 bytes .../__pycache__/_functools.cpython-312.pyc | Bin 0 -> 2605 bytes .../__pycache__/_itertools.cpython-312.pyc | Bin 0 -> 1789 bytes .../__pycache__/_log.cpython-312.pyc | Bin 0 -> 291 bytes .../__pycache__/_macos_compat.cpython-312.pyc | Bin 0 -> 580 bytes .../__pycache__/_modified.cpython-312.pyc | Bin 0 -> 3899 bytes .../__pycache__/_msvccompiler.cpython-312.pyc | Bin 0 -> 22453 bytes .../__pycache__/archive_util.cpython-312.pyc | Bin 0 -> 9776 bytes .../__pycache__/bcppcompiler.cpython-312.pyc | Bin 0 -> 11963 bytes .../__pycache__/ccompiler.cpython-312.pyc | Bin 0 -> 44475 bytes .../__pycache__/cmd.cpython-312.pyc | Bin 0 -> 17511 bytes .../__pycache__/config.cpython-312.pyc | Bin 0 -> 5874 bytes .../__pycache__/core.cpython-312.pyc | Bin 0 -> 9045 bytes .../cygwinccompiler.cpython-312.pyc | Bin 0 -> 12011 bytes .../__pycache__/debug.cpython-312.pyc | Bin 0 -> 334 bytes .../__pycache__/dep_util.cpython-312.pyc | Bin 0 -> 728 bytes .../__pycache__/dir_util.cpython-312.pyc | Bin 0 -> 9623 bytes .../__pycache__/dist.cpython-312.pyc | Bin 0 -> 50509 bytes .../__pycache__/errors.cpython-312.pyc | Bin 0 -> 5766 bytes .../__pycache__/extension.cpython-312.pyc | Bin 0 -> 9443 bytes .../__pycache__/fancy_getopt.cpython-312.pyc | Bin 0 -> 15645 bytes .../__pycache__/file_util.cpython-312.pyc | Bin 0 -> 9523 bytes .../__pycache__/filelist.cpython-312.pyc | Bin 0 -> 15771 bytes .../__pycache__/log.cpython-312.pyc | Bin 0 -> 2629 bytes .../__pycache__/msvc9compiler.cpython-312.pyc | Bin 0 -> 30744 bytes .../__pycache__/msvccompiler.cpython-312.pyc | Bin 0 -> 24974 bytes .../__pycache__/py38compat.cpython-312.pyc | Bin 0 -> 548 bytes .../__pycache__/py39compat.cpython-312.pyc | Bin 0 -> 2711 bytes .../__pycache__/spawn.cpython-312.pyc | Bin 0 -> 4052 bytes .../__pycache__/sysconfig.cpython-312.pyc | Bin 0 -> 21297 bytes .../__pycache__/text_file.cpython-312.pyc | Bin 0 -> 10799 bytes .../__pycache__/unixccompiler.cpython-312.pyc | Bin 0 -> 15236 bytes .../__pycache__/util.cpython-312.pyc | Bin 0 -> 18817 bytes .../__pycache__/version.cpython-312.pyc | Bin 0 -> 10686 bytes .../versionpredicate.cpython-312.pyc | Bin 0 -> 6912 bytes .../__pycache__/zosccompiler.cpython-312.pyc | Bin 0 -> 6288 bytes .../setuptools/_distutils/_collections.py | 203 + .../setuptools/_distutils/_functools.py | 73 + .../setuptools/_distutils/_itertools.py | 52 + .../setuptools/_distutils/_log.py | 3 + .../setuptools/_distutils/_macos_compat.py | 12 + .../setuptools/_distutils/_modified.py | 72 + .../setuptools/_distutils/_msvccompiler.py | 568 ++ .../setuptools/_distutils/archive_util.py | 280 + .../setuptools/_distutils/bcppcompiler.py | 397 ++ .../setuptools/_distutils/ccompiler.py | 1252 +++++ .../setuptools/_distutils/cmd.py | 433 ++ .../setuptools/_distutils/command/__init__.py | 25 + .../__pycache__/__init__.cpython-312.pyc | Bin 0 -> 544 bytes .../_framework_compat.cpython-312.pyc | Bin 0 -> 2630 bytes .../command/__pycache__/bdist.cpython-312.pyc | Bin 0 -> 5634 bytes .../__pycache__/bdist_dumb.cpython-312.pyc | Bin 0 -> 5541 bytes .../__pycache__/bdist_rpm.cpython-312.pyc | Bin 0 -> 21501 bytes .../command/__pycache__/build.cpython-312.pyc | Bin 0 -> 5865 bytes .../__pycache__/build_clib.cpython-312.pyc | Bin 0 -> 7381 bytes .../__pycache__/build_ext.cpython-312.pyc | Bin 0 -> 29415 bytes .../__pycache__/build_py.cpython-312.pyc | Bin 0 -> 16063 bytes .../__pycache__/build_scripts.cpython-312.pyc | Bin 0 -> 7101 bytes .../command/__pycache__/check.cpython-312.pyc | Bin 0 -> 6939 bytes .../command/__pycache__/clean.cpython-312.pyc | Bin 0 -> 3037 bytes .../__pycache__/config.cpython-312.pyc | Bin 0 -> 15268 bytes .../__pycache__/install.cpython-312.pyc | Bin 0 -> 27168 bytes .../__pycache__/install_data.cpython-312.pyc | Bin 0 -> 3530 bytes .../install_egg_info.cpython-312.pyc | Bin 0 -> 4910 bytes .../install_headers.cpython-312.pyc | Bin 0 -> 2247 bytes .../__pycache__/install_lib.cpython-312.pyc | Bin 0 -> 8133 bytes .../install_scripts.cpython-312.pyc | Bin 0 -> 2970 bytes .../__pycache__/register.cpython-312.pyc | Bin 0 -> 14408 bytes .../command/__pycache__/sdist.cpython-312.pyc | Bin 0 -> 22453 bytes .../__pycache__/upload.cpython-312.pyc | Bin 0 -> 9647 bytes .../_distutils/command/_framework_compat.py | 54 + .../setuptools/_distutils/command/bdist.py | 154 + .../_distutils/command/bdist_dumb.py | 141 + .../_distutils/command/bdist_rpm.py | 599 +++ .../setuptools/_distutils/command/build.py | 153 + .../_distutils/command/build_clib.py | 208 + .../_distutils/command/build_ext.py | 800 +++ .../setuptools/_distutils/command/build_py.py | 406 ++ .../_distutils/command/build_scripts.py | 170 + .../setuptools/_distutils/command/check.py | 155 + .../setuptools/_distutils/command/clean.py | 76 + .../setuptools/_distutils/command/config.py | 369 ++ .../setuptools/_distutils/command/install.py | 813 +++ .../_distutils/command/install_data.py | 84 + .../_distutils/command/install_egg_info.py | 92 + .../_distutils/command/install_headers.py | 44 + .../_distutils/command/install_lib.py | 236 + .../_distutils/command/install_scripts.py | 61 + .../setuptools/_distutils/command/register.py | 323 ++ .../setuptools/_distutils/command/sdist.py | 528 ++ .../setuptools/_distutils/command/upload.py | 208 + .../setuptools/_distutils/compat/__init__.py | 15 + .../__pycache__/__init__.cpython-312.pyc | Bin 0 -> 1181 bytes .../compat/__pycache__/py38.cpython-312.pyc | Bin 0 -> 1058 bytes .../setuptools/_distutils/compat/py38.py | 23 + .../setuptools/_distutils/config.py | 151 + .../setuptools/_distutils/core.py | 290 + .../setuptools/_distutils/cygwinccompiler.py | 355 ++ .../setuptools/_distutils/debug.py | 5 + .../setuptools/_distutils/dep_util.py | 14 + .../setuptools/_distutils/dir_util.py | 238 + .../setuptools/_distutils/dist.py | 1287 +++++ .../setuptools/_distutils/errors.py | 127 + .../setuptools/_distutils/extension.py | 242 + .../setuptools/_distutils/fancy_getopt.py | 469 ++ .../setuptools/_distutils/file_util.py | 235 + .../setuptools/_distutils/filelist.py | 369 ++ .../setuptools/_distutils/log.py | 56 + .../setuptools/_distutils/msvc9compiler.py | 824 +++ .../setuptools/_distutils/msvccompiler.py | 689 +++ .../setuptools/_distutils/py38compat.py | 8 + .../setuptools/_distutils/py39compat.py | 66 + .../setuptools/_distutils/spawn.py | 105 + .../setuptools/_distutils/sysconfig.py | 555 ++ .../setuptools/_distutils/text_file.py | 286 + .../setuptools/_distutils/unixccompiler.py | 400 ++ .../setuptools/_distutils/util.py | 510 ++ .../setuptools/_distutils/version.py | 349 ++ .../setuptools/_distutils/versionpredicate.py | 175 + .../setuptools/_distutils/zosccompiler.py | 229 + .../site-packages/setuptools/_entry_points.py | 88 + venv/Lib/site-packages/setuptools/_imp.py | 88 + .../site-packages/setuptools/_importlib.py | 51 + .../site-packages/setuptools/_itertools.py | 23 + .../setuptools/_normalization.py | 144 + venv/Lib/site-packages/setuptools/_path.py | 40 + venv/Lib/site-packages/setuptools/_reqs.py | 38 + .../setuptools/_vendor/__init__.py | 0 .../__pycache__/__init__.cpython-312.pyc | Bin 0 -> 204 bytes .../__pycache__/ordered_set.cpython-312.pyc | Bin 0 -> 19546 bytes .../typing_extensions.cpython-312.pyc | Bin 0 -> 98146 bytes .../_vendor/__pycache__/zipp.cpython-312.pyc | Bin 0 -> 14884 bytes .../setuptools/_vendor/backports/__init__.py | 0 .../__pycache__/__init__.cpython-312.pyc | Bin 0 -> 214 bytes .../__pycache__/tarfile.cpython-312.pyc | Bin 0 -> 119462 bytes .../setuptools/_vendor/backports/tarfile.py | 2900 ++++++++++ .../_vendor/importlib_metadata/__init__.py | 904 ++++ .../__pycache__/__init__.cpython-312.pyc | Bin 0 -> 45262 bytes .../__pycache__/_adapters.cpython-312.pyc | Bin 0 -> 3972 bytes .../__pycache__/_collections.cpython-312.pyc | Bin 0 -> 1959 bytes .../__pycache__/_compat.cpython-312.pyc | Bin 0 -> 2481 bytes .../__pycache__/_functools.cpython-312.pyc | Bin 0 -> 3517 bytes .../__pycache__/_itertools.cpython-312.pyc | Bin 0 -> 2441 bytes .../__pycache__/_meta.cpython-312.pyc | Bin 0 -> 2725 bytes .../__pycache__/_py39compat.cpython-312.pyc | Bin 0 -> 1673 bytes .../__pycache__/_text.cpython-312.pyc | Bin 0 -> 3918 bytes .../_vendor/importlib_metadata/_adapters.py | 90 + .../importlib_metadata/_collections.py | 30 + .../_vendor/importlib_metadata/_compat.py | 72 + .../_vendor/importlib_metadata/_functools.py | 104 + .../_vendor/importlib_metadata/_itertools.py | 73 + .../_vendor/importlib_metadata/_meta.py | 49 + .../_vendor/importlib_metadata/_py39compat.py | 35 + .../_vendor/importlib_metadata/_text.py | 99 + .../_vendor/importlib_metadata/py.typed | 0 .../_vendor/importlib_resources/__init__.py | 36 + .../__pycache__/__init__.cpython-312.pyc | Bin 0 -> 691 bytes .../__pycache__/_adapters.cpython-312.pyc | Bin 0 -> 9736 bytes .../__pycache__/_common.cpython-312.pyc | Bin 0 -> 8785 bytes .../__pycache__/_compat.cpython-312.pyc | Bin 0 -> 5057 bytes .../__pycache__/_itertools.cpython-312.pyc | Bin 0 -> 1203 bytes .../__pycache__/_legacy.cpython-312.pyc | Bin 0 -> 5819 bytes .../__pycache__/abc.cpython-312.pyc | Bin 0 -> 8901 bytes .../__pycache__/readers.cpython-312.pyc | Bin 0 -> 7674 bytes .../__pycache__/simple.cpython-312.pyc | Bin 0 -> 5527 bytes .../_vendor/importlib_resources/_adapters.py | 170 + .../_vendor/importlib_resources/_common.py | 207 + .../_vendor/importlib_resources/_compat.py | 108 + .../_vendor/importlib_resources/_itertools.py | 35 + .../_vendor/importlib_resources/_legacy.py | 120 + .../_vendor/importlib_resources/abc.py | 170 + .../_vendor/importlib_resources/py.typed | 0 .../_vendor/importlib_resources/readers.py | 120 + .../_vendor/importlib_resources/simple.py | 106 + .../setuptools/_vendor/jaraco/__init__.py | 0 .../__pycache__/__init__.cpython-312.pyc | Bin 0 -> 211 bytes .../__pycache__/context.cpython-312.pyc | Bin 0 -> 14374 bytes .../setuptools/_vendor/jaraco/context.py | 361 ++ .../_vendor/jaraco/functools/__init__.py | 633 +++ .../_vendor/jaraco/functools/__init__.pyi | 128 + .../__pycache__/__init__.cpython-312.pyc | Bin 0 -> 23010 bytes .../_vendor/jaraco/functools/py.typed | 0 .../_vendor/jaraco/text/__init__.py | 599 +++ .../text/__pycache__/__init__.cpython-312.pyc | Bin 0 -> 24449 bytes .../_vendor/more_itertools/__init__.py | 4 + .../_vendor/more_itertools/__init__.pyi | 2 + .../__pycache__/__init__.cpython-312.pyc | Bin 0 -> 296 bytes .../__pycache__/more.cpython-312.pyc | Bin 0 -> 138053 bytes .../__pycache__/recipes.cpython-312.pyc | Bin 0 -> 21809 bytes .../setuptools/_vendor/more_itertools/more.py | 3824 ++++++++++++++ .../_vendor/more_itertools/more.pyi | 480 ++ .../_vendor/more_itertools/py.typed | 0 .../_vendor/more_itertools/recipes.py | 620 +++ .../_vendor/more_itertools/recipes.pyi | 103 + .../setuptools/_vendor/ordered_set.py | 488 ++ .../setuptools/_vendor/packaging/__init__.py | 15 + .../__pycache__/__init__.cpython-312.pyc | Bin 0 -> 579 bytes .../__pycache__/_elffile.cpython-312.pyc | Bin 0 -> 5047 bytes .../__pycache__/_manylinux.cpython-312.pyc | Bin 0 -> 9927 bytes .../__pycache__/_musllinux.cpython-312.pyc | Bin 0 -> 4610 bytes .../__pycache__/_parser.cpython-312.pyc | Bin 0 -> 14085 bytes .../__pycache__/_structures.cpython-312.pyc | Bin 0 -> 3262 bytes .../__pycache__/_tokenizer.cpython-312.pyc | Bin 0 -> 7958 bytes .../__pycache__/markers.cpython-312.pyc | Bin 0 -> 10520 bytes .../__pycache__/metadata.cpython-312.pyc | Bin 0 -> 26966 bytes .../__pycache__/requirements.cpython-312.pyc | Bin 0 -> 4475 bytes .../__pycache__/specifiers.cpython-312.pyc | Bin 0 -> 39572 bytes .../__pycache__/tags.cpython-312.pyc | Bin 0 -> 21782 bytes .../__pycache__/utils.cpython-312.pyc | Bin 0 -> 7305 bytes .../__pycache__/version.cpython-312.pyc | Bin 0 -> 20018 bytes .../setuptools/_vendor/packaging/_elffile.py | 108 + .../_vendor/packaging/_manylinux.py | 260 + .../_vendor/packaging/_musllinux.py | 83 + .../setuptools/_vendor/packaging/_parser.py | 356 ++ .../_vendor/packaging/_structures.py | 61 + .../_vendor/packaging/_tokenizer.py | 192 + .../setuptools/_vendor/packaging/markers.py | 252 + .../setuptools/_vendor/packaging/metadata.py | 825 +++ .../setuptools/_vendor/packaging/py.typed | 0 .../_vendor/packaging/requirements.py | 90 + .../_vendor/packaging/specifiers.py | 1017 ++++ .../setuptools/_vendor/packaging/tags.py | 571 ++ .../setuptools/_vendor/packaging/utils.py | 172 + .../setuptools/_vendor/packaging/version.py | 563 ++ .../setuptools/_vendor/tomli/__init__.py | 11 + .../__pycache__/__init__.cpython-312.pyc | Bin 0 -> 406 bytes .../tomli/__pycache__/_parser.cpython-312.pyc | Bin 0 -> 26949 bytes .../tomli/__pycache__/_re.cpython-312.pyc | Bin 0 -> 3930 bytes .../tomli/__pycache__/_types.cpython-312.pyc | Bin 0 -> 388 bytes .../setuptools/_vendor/tomli/_parser.py | 691 +++ .../setuptools/_vendor/tomli/_re.py | 107 + .../setuptools/_vendor/tomli/_types.py | 10 + .../setuptools/_vendor/tomli/py.typed | 1 + .../setuptools/_vendor/typing_extensions.py | 2296 ++++++++ .../site-packages/setuptools/_vendor/zipp.py | 329 ++ .../site-packages/setuptools/archive_util.py | 216 + .../site-packages/setuptools/build_meta.py | 514 ++ venv/Lib/site-packages/setuptools/cli-32.exe | Bin 0 -> 11776 bytes venv/Lib/site-packages/setuptools/cli-64.exe | Bin 0 -> 14336 bytes .../site-packages/setuptools/cli-arm64.exe | Bin 0 -> 13824 bytes venv/Lib/site-packages/setuptools/cli.exe | Bin 0 -> 11776 bytes .../setuptools/command/__init__.py | 12 + .../__pycache__/__init__.cpython-312.pyc | Bin 0 -> 663 bytes .../__pycache__/_requirestxt.cpython-312.pyc | Bin 0 -> 6582 bytes .../command/__pycache__/alias.cpython-312.pyc | Bin 0 -> 3549 bytes .../__pycache__/bdist_egg.cpython-312.pyc | Bin 0 -> 23569 bytes .../__pycache__/bdist_rpm.cpython-312.pyc | Bin 0 -> 1996 bytes .../command/__pycache__/build.cpython-312.pyc | Bin 0 -> 6328 bytes .../__pycache__/build_clib.cpython-312.pyc | Bin 0 -> 3866 bytes .../__pycache__/build_ext.cpython-312.pyc | Bin 0 -> 22670 bytes .../__pycache__/build_py.cpython-312.pyc | Bin 0 -> 22095 bytes .../__pycache__/develop.cpython-312.pyc | Bin 0 -> 10109 bytes .../__pycache__/dist_info.cpython-312.pyc | Bin 0 -> 5436 bytes .../__pycache__/easy_install.cpython-312.pyc | Bin 0 -> 109832 bytes .../editable_wheel.cpython-312.pyc | Bin 0 -> 47723 bytes .../__pycache__/egg_info.cpython-312.pyc | Bin 0 -> 35408 bytes .../__pycache__/install.cpython-312.pyc | Bin 0 -> 6883 bytes .../install_egg_info.cpython-312.pyc | Bin 0 -> 3767 bytes .../__pycache__/install_lib.cpython-312.pyc | Bin 0 -> 5722 bytes .../install_scripts.cpython-312.pyc | Bin 0 -> 3716 bytes .../__pycache__/register.cpython-312.pyc | Bin 0 -> 1013 bytes .../__pycache__/rotate.cpython-312.pyc | Bin 0 -> 3674 bytes .../__pycache__/saveopts.cpython-312.pyc | Bin 0 -> 1275 bytes .../command/__pycache__/sdist.cpython-312.pyc | Bin 0 -> 11394 bytes .../__pycache__/setopt.cpython-312.pyc | Bin 0 -> 6974 bytes .../command/__pycache__/test.cpython-312.pyc | Bin 0 -> 13215 bytes .../__pycache__/upload.cpython-312.pyc | Bin 0 -> 983 bytes .../__pycache__/upload_docs.cpython-312.pyc | Bin 0 -> 11210 bytes .../setuptools/command/_requirestxt.py | 129 + .../site-packages/setuptools/command/alias.py | 78 + .../setuptools/command/bdist_egg.py | 461 ++ .../setuptools/command/bdist_rpm.py | 39 + .../site-packages/setuptools/command/build.py | 140 + .../setuptools/command/build_clib.py | 104 + .../setuptools/command/build_ext.py | 459 ++ .../setuptools/command/build_py.py | 393 ++ .../setuptools/command/develop.py | 192 + .../setuptools/command/dist_info.py | 106 + .../setuptools/command/easy_install.py | 2361 +++++++++ .../setuptools/command/editable_wheel.py | 906 ++++ .../setuptools/command/egg_info.py | 737 +++ .../setuptools/command/install.py | 155 + .../setuptools/command/install_egg_info.py | 57 + .../setuptools/command/install_lib.py | 125 + .../setuptools/command/install_scripts.py | 66 + .../setuptools/command/launcher manifest.xml | 15 + .../setuptools/command/register.py | 18 + .../setuptools/command/rotate.py | 63 + .../setuptools/command/saveopts.py | 21 + .../site-packages/setuptools/command/sdist.py | 204 + .../setuptools/command/setopt.py | 138 + .../site-packages/setuptools/command/test.py | 250 + .../setuptools/command/upload.py | 17 + .../setuptools/command/upload_docs.py | 221 + .../setuptools/compat/__init__.py | 0 .../__pycache__/__init__.cpython-312.pyc | Bin 0 -> 203 bytes .../compat/__pycache__/py310.cpython-312.pyc | Bin 0 -> 389 bytes .../compat/__pycache__/py311.cpython-312.pyc | Bin 0 -> 833 bytes .../compat/__pycache__/py39.cpython-312.pyc | Bin 0 -> 332 bytes .../site-packages/setuptools/compat/py310.py | 10 + .../site-packages/setuptools/compat/py311.py | 12 + .../site-packages/setuptools/compat/py39.py | 9 + .../setuptools/config/__init__.py | 43 + .../__pycache__/__init__.cpython-312.pyc | Bin 0 -> 2028 bytes .../_apply_pyprojecttoml.cpython-312.pyc | Bin 0 -> 20664 bytes .../config/__pycache__/expand.cpython-312.pyc | Bin 0 -> 25014 bytes .../__pycache__/pyprojecttoml.cpython-312.pyc | Bin 0 -> 22636 bytes .../__pycache__/setupcfg.cpython-312.pyc | Bin 0 -> 31612 bytes .../setuptools/config/_apply_pyprojecttoml.py | 439 ++ .../config/_validate_pyproject/__init__.py | 34 + .../__pycache__/__init__.cpython-312.pyc | Bin 0 -> 1880 bytes .../error_reporting.cpython-312.pyc | Bin 0 -> 17803 bytes .../extra_validations.cpython-312.pyc | Bin 0 -> 1701 bytes .../fastjsonschema_exceptions.cpython-312.pyc | Bin 0 -> 2857 bytes ...fastjsonschema_validations.cpython-312.pyc | Bin 0 -> 173366 bytes .../__pycache__/formats.cpython-312.pyc | Bin 0 -> 13472 bytes .../_validate_pyproject/error_reporting.py | 318 ++ .../_validate_pyproject/extra_validations.py | 36 + .../fastjsonschema_exceptions.py | 51 + .../fastjsonschema_validations.py | 1051 ++++ .../config/_validate_pyproject/formats.py | 275 + .../site-packages/setuptools/config/expand.py | 462 ++ .../setuptools/config/pyprojecttoml.py | 441 ++ .../setuptools/config/setupcfg.py | 776 +++ venv/Lib/site-packages/setuptools/dep_util.py | 16 + venv/Lib/site-packages/setuptools/depends.py | 180 + .../Lib/site-packages/setuptools/discovery.py | 613 +++ venv/Lib/site-packages/setuptools/dist.py | 972 ++++ venv/Lib/site-packages/setuptools/errors.py | 66 + .../Lib/site-packages/setuptools/extension.py | 152 + .../setuptools/extern/__init__.py | 85 + .../__pycache__/__init__.cpython-312.pyc | Bin 0 -> 4141 bytes venv/Lib/site-packages/setuptools/glob.py | 165 + venv/Lib/site-packages/setuptools/gui-32.exe | Bin 0 -> 11776 bytes venv/Lib/site-packages/setuptools/gui-64.exe | Bin 0 -> 14336 bytes .../site-packages/setuptools/gui-arm64.exe | Bin 0 -> 13824 bytes venv/Lib/site-packages/setuptools/gui.exe | Bin 0 -> 11776 bytes .../Lib/site-packages/setuptools/installer.py | 144 + venv/Lib/site-packages/setuptools/launch.py | 36 + venv/Lib/site-packages/setuptools/logging.py | 38 + venv/Lib/site-packages/setuptools/modified.py | 8 + venv/Lib/site-packages/setuptools/monkey.py | 155 + venv/Lib/site-packages/setuptools/msvc.py | 1742 ++++++ .../site-packages/setuptools/namespaces.py | 105 + .../site-packages/setuptools/package_index.py | 1134 ++++ venv/Lib/site-packages/setuptools/sandbox.py | 529 ++ .../setuptools/script (dev).tmpl | 6 + venv/Lib/site-packages/setuptools/script.tmpl | 3 + .../site-packages/setuptools/unicode_utils.py | 44 + venv/Lib/site-packages/setuptools/version.py | 6 + venv/Lib/site-packages/setuptools/warnings.py | 105 + venv/Lib/site-packages/setuptools/wheel.py | 234 + .../setuptools/windows_support.py | 30 + 503 files changed, 82401 insertions(+), 33 deletions(-) create mode 100644 templates/admin/login.html create mode 100644 venv/Lib/site-packages/_distutils_hack/__init__.py create mode 100644 venv/Lib/site-packages/_distutils_hack/__pycache__/__init__.cpython-312.pyc create mode 100644 venv/Lib/site-packages/_distutils_hack/__pycache__/override.cpython-312.pyc create mode 100644 venv/Lib/site-packages/_distutils_hack/override.py create mode 100644 venv/Lib/site-packages/distutils-precedence.pth create mode 100644 venv/Lib/site-packages/pkg_resources/__init__.py create mode 100644 venv/Lib/site-packages/pkg_resources/__pycache__/__init__.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pkg_resources/_vendor/__init__.py create mode 100644 venv/Lib/site-packages/pkg_resources/_vendor/__pycache__/__init__.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pkg_resources/_vendor/__pycache__/typing_extensions.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pkg_resources/_vendor/__pycache__/zipp.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pkg_resources/_vendor/backports/__init__.py create mode 100644 venv/Lib/site-packages/pkg_resources/_vendor/backports/__pycache__/__init__.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pkg_resources/_vendor/backports/__pycache__/tarfile.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pkg_resources/_vendor/backports/tarfile.py create mode 100644 venv/Lib/site-packages/pkg_resources/_vendor/importlib_resources/__init__.py create mode 100644 venv/Lib/site-packages/pkg_resources/_vendor/importlib_resources/__pycache__/__init__.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pkg_resources/_vendor/importlib_resources/__pycache__/_adapters.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pkg_resources/_vendor/importlib_resources/__pycache__/_common.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pkg_resources/_vendor/importlib_resources/__pycache__/_compat.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pkg_resources/_vendor/importlib_resources/__pycache__/_itertools.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pkg_resources/_vendor/importlib_resources/__pycache__/_legacy.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pkg_resources/_vendor/importlib_resources/__pycache__/abc.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pkg_resources/_vendor/importlib_resources/__pycache__/readers.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pkg_resources/_vendor/importlib_resources/__pycache__/simple.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pkg_resources/_vendor/importlib_resources/_adapters.py create mode 100644 venv/Lib/site-packages/pkg_resources/_vendor/importlib_resources/_common.py create mode 100644 venv/Lib/site-packages/pkg_resources/_vendor/importlib_resources/_compat.py create mode 100644 venv/Lib/site-packages/pkg_resources/_vendor/importlib_resources/_itertools.py create mode 100644 venv/Lib/site-packages/pkg_resources/_vendor/importlib_resources/_legacy.py create mode 100644 venv/Lib/site-packages/pkg_resources/_vendor/importlib_resources/abc.py create mode 100644 venv/Lib/site-packages/pkg_resources/_vendor/importlib_resources/py.typed create mode 100644 venv/Lib/site-packages/pkg_resources/_vendor/importlib_resources/readers.py create mode 100644 venv/Lib/site-packages/pkg_resources/_vendor/importlib_resources/simple.py create mode 100644 venv/Lib/site-packages/pkg_resources/_vendor/jaraco/__init__.py create mode 100644 venv/Lib/site-packages/pkg_resources/_vendor/jaraco/__pycache__/__init__.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pkg_resources/_vendor/jaraco/__pycache__/context.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pkg_resources/_vendor/jaraco/context.py create mode 100644 venv/Lib/site-packages/pkg_resources/_vendor/jaraco/functools/__init__.py create mode 100644 venv/Lib/site-packages/pkg_resources/_vendor/jaraco/functools/__init__.pyi create mode 100644 venv/Lib/site-packages/pkg_resources/_vendor/jaraco/functools/__pycache__/__init__.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pkg_resources/_vendor/jaraco/functools/py.typed create mode 100644 venv/Lib/site-packages/pkg_resources/_vendor/jaraco/text/__init__.py create mode 100644 venv/Lib/site-packages/pkg_resources/_vendor/jaraco/text/__pycache__/__init__.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pkg_resources/_vendor/more_itertools/__init__.py create mode 100644 venv/Lib/site-packages/pkg_resources/_vendor/more_itertools/__init__.pyi create mode 100644 venv/Lib/site-packages/pkg_resources/_vendor/more_itertools/__pycache__/__init__.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pkg_resources/_vendor/more_itertools/__pycache__/more.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pkg_resources/_vendor/more_itertools/__pycache__/recipes.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pkg_resources/_vendor/more_itertools/more.py create mode 100644 venv/Lib/site-packages/pkg_resources/_vendor/more_itertools/more.pyi create mode 100644 venv/Lib/site-packages/pkg_resources/_vendor/more_itertools/py.typed create mode 100644 venv/Lib/site-packages/pkg_resources/_vendor/more_itertools/recipes.py create mode 100644 venv/Lib/site-packages/pkg_resources/_vendor/more_itertools/recipes.pyi create mode 100644 venv/Lib/site-packages/pkg_resources/_vendor/packaging/__init__.py create mode 100644 venv/Lib/site-packages/pkg_resources/_vendor/packaging/__pycache__/__init__.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pkg_resources/_vendor/packaging/__pycache__/_elffile.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pkg_resources/_vendor/packaging/__pycache__/_manylinux.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pkg_resources/_vendor/packaging/__pycache__/_musllinux.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pkg_resources/_vendor/packaging/__pycache__/_parser.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pkg_resources/_vendor/packaging/__pycache__/_structures.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pkg_resources/_vendor/packaging/__pycache__/_tokenizer.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pkg_resources/_vendor/packaging/__pycache__/markers.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pkg_resources/_vendor/packaging/__pycache__/metadata.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pkg_resources/_vendor/packaging/__pycache__/requirements.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pkg_resources/_vendor/packaging/__pycache__/specifiers.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pkg_resources/_vendor/packaging/__pycache__/tags.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pkg_resources/_vendor/packaging/__pycache__/utils.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pkg_resources/_vendor/packaging/__pycache__/version.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pkg_resources/_vendor/packaging/_elffile.py create mode 100644 venv/Lib/site-packages/pkg_resources/_vendor/packaging/_manylinux.py create mode 100644 venv/Lib/site-packages/pkg_resources/_vendor/packaging/_musllinux.py create mode 100644 venv/Lib/site-packages/pkg_resources/_vendor/packaging/_parser.py create mode 100644 venv/Lib/site-packages/pkg_resources/_vendor/packaging/_structures.py create mode 100644 venv/Lib/site-packages/pkg_resources/_vendor/packaging/_tokenizer.py create mode 100644 venv/Lib/site-packages/pkg_resources/_vendor/packaging/markers.py create mode 100644 venv/Lib/site-packages/pkg_resources/_vendor/packaging/metadata.py create mode 100644 venv/Lib/site-packages/pkg_resources/_vendor/packaging/py.typed create mode 100644 venv/Lib/site-packages/pkg_resources/_vendor/packaging/requirements.py create mode 100644 venv/Lib/site-packages/pkg_resources/_vendor/packaging/specifiers.py create mode 100644 venv/Lib/site-packages/pkg_resources/_vendor/packaging/tags.py create mode 100644 venv/Lib/site-packages/pkg_resources/_vendor/packaging/utils.py create mode 100644 venv/Lib/site-packages/pkg_resources/_vendor/packaging/version.py create mode 100644 venv/Lib/site-packages/pkg_resources/_vendor/platformdirs/__init__.py create mode 100644 venv/Lib/site-packages/pkg_resources/_vendor/platformdirs/__main__.py create mode 100644 venv/Lib/site-packages/pkg_resources/_vendor/platformdirs/__pycache__/__init__.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pkg_resources/_vendor/platformdirs/__pycache__/__main__.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pkg_resources/_vendor/platformdirs/__pycache__/android.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pkg_resources/_vendor/platformdirs/__pycache__/api.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pkg_resources/_vendor/platformdirs/__pycache__/macos.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pkg_resources/_vendor/platformdirs/__pycache__/unix.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pkg_resources/_vendor/platformdirs/__pycache__/version.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pkg_resources/_vendor/platformdirs/__pycache__/windows.cpython-312.pyc create mode 100644 venv/Lib/site-packages/pkg_resources/_vendor/platformdirs/android.py create mode 100644 venv/Lib/site-packages/pkg_resources/_vendor/platformdirs/api.py create mode 100644 venv/Lib/site-packages/pkg_resources/_vendor/platformdirs/macos.py create mode 100644 venv/Lib/site-packages/pkg_resources/_vendor/platformdirs/py.typed create mode 100644 venv/Lib/site-packages/pkg_resources/_vendor/platformdirs/unix.py create mode 100644 venv/Lib/site-packages/pkg_resources/_vendor/platformdirs/version.py create mode 100644 venv/Lib/site-packages/pkg_resources/_vendor/platformdirs/windows.py create mode 100644 venv/Lib/site-packages/pkg_resources/_vendor/typing_extensions.py create mode 100644 venv/Lib/site-packages/pkg_resources/_vendor/zipp.py create mode 100644 venv/Lib/site-packages/pkg_resources/extern/__init__.py create mode 100644 venv/Lib/site-packages/pkg_resources/extern/__pycache__/__init__.cpython-312.pyc create mode 100644 venv/Lib/site-packages/setuptools-69.5.1.dist-info/INSTALLER create mode 100644 venv/Lib/site-packages/setuptools-69.5.1.dist-info/LICENSE create mode 100644 venv/Lib/site-packages/setuptools-69.5.1.dist-info/METADATA create mode 100644 venv/Lib/site-packages/setuptools-69.5.1.dist-info/RECORD create mode 100644 venv/Lib/site-packages/setuptools-69.5.1.dist-info/REQUESTED create mode 100644 venv/Lib/site-packages/setuptools-69.5.1.dist-info/WHEEL create mode 100644 venv/Lib/site-packages/setuptools-69.5.1.dist-info/entry_points.txt create mode 100644 venv/Lib/site-packages/setuptools-69.5.1.dist-info/top_level.txt create mode 100644 venv/Lib/site-packages/setuptools/__init__.py create mode 100644 venv/Lib/site-packages/setuptools/__pycache__/__init__.cpython-312.pyc create mode 100644 venv/Lib/site-packages/setuptools/__pycache__/_core_metadata.cpython-312.pyc create mode 100644 venv/Lib/site-packages/setuptools/__pycache__/_entry_points.cpython-312.pyc create mode 100644 venv/Lib/site-packages/setuptools/__pycache__/_imp.cpython-312.pyc create mode 100644 venv/Lib/site-packages/setuptools/__pycache__/_importlib.cpython-312.pyc create mode 100644 venv/Lib/site-packages/setuptools/__pycache__/_itertools.cpython-312.pyc create mode 100644 venv/Lib/site-packages/setuptools/__pycache__/_normalization.cpython-312.pyc create mode 100644 venv/Lib/site-packages/setuptools/__pycache__/_path.cpython-312.pyc create mode 100644 venv/Lib/site-packages/setuptools/__pycache__/_reqs.cpython-312.pyc create mode 100644 venv/Lib/site-packages/setuptools/__pycache__/archive_util.cpython-312.pyc create mode 100644 venv/Lib/site-packages/setuptools/__pycache__/build_meta.cpython-312.pyc create mode 100644 venv/Lib/site-packages/setuptools/__pycache__/dep_util.cpython-312.pyc create mode 100644 venv/Lib/site-packages/setuptools/__pycache__/depends.cpython-312.pyc create mode 100644 venv/Lib/site-packages/setuptools/__pycache__/discovery.cpython-312.pyc create mode 100644 venv/Lib/site-packages/setuptools/__pycache__/dist.cpython-312.pyc create mode 100644 venv/Lib/site-packages/setuptools/__pycache__/errors.cpython-312.pyc create mode 100644 venv/Lib/site-packages/setuptools/__pycache__/extension.cpython-312.pyc create mode 100644 venv/Lib/site-packages/setuptools/__pycache__/glob.cpython-312.pyc create mode 100644 venv/Lib/site-packages/setuptools/__pycache__/installer.cpython-312.pyc create mode 100644 venv/Lib/site-packages/setuptools/__pycache__/launch.cpython-312.pyc create mode 100644 venv/Lib/site-packages/setuptools/__pycache__/logging.cpython-312.pyc create mode 100644 venv/Lib/site-packages/setuptools/__pycache__/modified.cpython-312.pyc create mode 100644 venv/Lib/site-packages/setuptools/__pycache__/monkey.cpython-312.pyc create mode 100644 venv/Lib/site-packages/setuptools/__pycache__/msvc.cpython-312.pyc create mode 100644 venv/Lib/site-packages/setuptools/__pycache__/namespaces.cpython-312.pyc create mode 100644 venv/Lib/site-packages/setuptools/__pycache__/package_index.cpython-312.pyc create mode 100644 venv/Lib/site-packages/setuptools/__pycache__/sandbox.cpython-312.pyc create mode 100644 venv/Lib/site-packages/setuptools/__pycache__/unicode_utils.cpython-312.pyc create mode 100644 venv/Lib/site-packages/setuptools/__pycache__/version.cpython-312.pyc create mode 100644 venv/Lib/site-packages/setuptools/__pycache__/warnings.cpython-312.pyc create mode 100644 venv/Lib/site-packages/setuptools/__pycache__/wheel.cpython-312.pyc create mode 100644 venv/Lib/site-packages/setuptools/__pycache__/windows_support.cpython-312.pyc create mode 100644 venv/Lib/site-packages/setuptools/_core_metadata.py create mode 100644 venv/Lib/site-packages/setuptools/_distutils/__init__.py create mode 100644 venv/Lib/site-packages/setuptools/_distutils/__pycache__/__init__.cpython-312.pyc create mode 100644 venv/Lib/site-packages/setuptools/_distutils/__pycache__/_collections.cpython-312.pyc create mode 100644 venv/Lib/site-packages/setuptools/_distutils/__pycache__/_functools.cpython-312.pyc create mode 100644 venv/Lib/site-packages/setuptools/_distutils/__pycache__/_itertools.cpython-312.pyc create mode 100644 venv/Lib/site-packages/setuptools/_distutils/__pycache__/_log.cpython-312.pyc create mode 100644 venv/Lib/site-packages/setuptools/_distutils/__pycache__/_macos_compat.cpython-312.pyc create mode 100644 venv/Lib/site-packages/setuptools/_distutils/__pycache__/_modified.cpython-312.pyc create mode 100644 venv/Lib/site-packages/setuptools/_distutils/__pycache__/_msvccompiler.cpython-312.pyc create mode 100644 venv/Lib/site-packages/setuptools/_distutils/__pycache__/archive_util.cpython-312.pyc create mode 100644 venv/Lib/site-packages/setuptools/_distutils/__pycache__/bcppcompiler.cpython-312.pyc create mode 100644 venv/Lib/site-packages/setuptools/_distutils/__pycache__/ccompiler.cpython-312.pyc create mode 100644 venv/Lib/site-packages/setuptools/_distutils/__pycache__/cmd.cpython-312.pyc create mode 100644 venv/Lib/site-packages/setuptools/_distutils/__pycache__/config.cpython-312.pyc create mode 100644 venv/Lib/site-packages/setuptools/_distutils/__pycache__/core.cpython-312.pyc create mode 100644 venv/Lib/site-packages/setuptools/_distutils/__pycache__/cygwinccompiler.cpython-312.pyc create mode 100644 venv/Lib/site-packages/setuptools/_distutils/__pycache__/debug.cpython-312.pyc create mode 100644 venv/Lib/site-packages/setuptools/_distutils/__pycache__/dep_util.cpython-312.pyc create mode 100644 venv/Lib/site-packages/setuptools/_distutils/__pycache__/dir_util.cpython-312.pyc create mode 100644 venv/Lib/site-packages/setuptools/_distutils/__pycache__/dist.cpython-312.pyc create mode 100644 venv/Lib/site-packages/setuptools/_distutils/__pycache__/errors.cpython-312.pyc create mode 100644 venv/Lib/site-packages/setuptools/_distutils/__pycache__/extension.cpython-312.pyc create mode 100644 venv/Lib/site-packages/setuptools/_distutils/__pycache__/fancy_getopt.cpython-312.pyc create mode 100644 venv/Lib/site-packages/setuptools/_distutils/__pycache__/file_util.cpython-312.pyc create mode 100644 venv/Lib/site-packages/setuptools/_distutils/__pycache__/filelist.cpython-312.pyc create mode 100644 venv/Lib/site-packages/setuptools/_distutils/__pycache__/log.cpython-312.pyc create mode 100644 venv/Lib/site-packages/setuptools/_distutils/__pycache__/msvc9compiler.cpython-312.pyc create mode 100644 venv/Lib/site-packages/setuptools/_distutils/__pycache__/msvccompiler.cpython-312.pyc create mode 100644 venv/Lib/site-packages/setuptools/_distutils/__pycache__/py38compat.cpython-312.pyc create mode 100644 venv/Lib/site-packages/setuptools/_distutils/__pycache__/py39compat.cpython-312.pyc create mode 100644 venv/Lib/site-packages/setuptools/_distutils/__pycache__/spawn.cpython-312.pyc create mode 100644 venv/Lib/site-packages/setuptools/_distutils/__pycache__/sysconfig.cpython-312.pyc create mode 100644 venv/Lib/site-packages/setuptools/_distutils/__pycache__/text_file.cpython-312.pyc create mode 100644 venv/Lib/site-packages/setuptools/_distutils/__pycache__/unixccompiler.cpython-312.pyc create mode 100644 venv/Lib/site-packages/setuptools/_distutils/__pycache__/util.cpython-312.pyc create mode 100644 venv/Lib/site-packages/setuptools/_distutils/__pycache__/version.cpython-312.pyc create mode 100644 venv/Lib/site-packages/setuptools/_distutils/__pycache__/versionpredicate.cpython-312.pyc create mode 100644 venv/Lib/site-packages/setuptools/_distutils/__pycache__/zosccompiler.cpython-312.pyc create mode 100644 venv/Lib/site-packages/setuptools/_distutils/_collections.py create mode 100644 venv/Lib/site-packages/setuptools/_distutils/_functools.py create mode 100644 venv/Lib/site-packages/setuptools/_distutils/_itertools.py create mode 100644 venv/Lib/site-packages/setuptools/_distutils/_log.py create mode 100644 venv/Lib/site-packages/setuptools/_distutils/_macos_compat.py create mode 100644 venv/Lib/site-packages/setuptools/_distutils/_modified.py create mode 100644 venv/Lib/site-packages/setuptools/_distutils/_msvccompiler.py create mode 100644 venv/Lib/site-packages/setuptools/_distutils/archive_util.py create mode 100644 venv/Lib/site-packages/setuptools/_distutils/bcppcompiler.py create mode 100644 venv/Lib/site-packages/setuptools/_distutils/ccompiler.py create mode 100644 venv/Lib/site-packages/setuptools/_distutils/cmd.py create mode 100644 venv/Lib/site-packages/setuptools/_distutils/command/__init__.py create mode 100644 venv/Lib/site-packages/setuptools/_distutils/command/__pycache__/__init__.cpython-312.pyc create mode 100644 venv/Lib/site-packages/setuptools/_distutils/command/__pycache__/_framework_compat.cpython-312.pyc create mode 100644 venv/Lib/site-packages/setuptools/_distutils/command/__pycache__/bdist.cpython-312.pyc create mode 100644 venv/Lib/site-packages/setuptools/_distutils/command/__pycache__/bdist_dumb.cpython-312.pyc create mode 100644 venv/Lib/site-packages/setuptools/_distutils/command/__pycache__/bdist_rpm.cpython-312.pyc create mode 100644 venv/Lib/site-packages/setuptools/_distutils/command/__pycache__/build.cpython-312.pyc create mode 100644 venv/Lib/site-packages/setuptools/_distutils/command/__pycache__/build_clib.cpython-312.pyc create mode 100644 venv/Lib/site-packages/setuptools/_distutils/command/__pycache__/build_ext.cpython-312.pyc create mode 100644 venv/Lib/site-packages/setuptools/_distutils/command/__pycache__/build_py.cpython-312.pyc create mode 100644 venv/Lib/site-packages/setuptools/_distutils/command/__pycache__/build_scripts.cpython-312.pyc create mode 100644 venv/Lib/site-packages/setuptools/_distutils/command/__pycache__/check.cpython-312.pyc create mode 100644 venv/Lib/site-packages/setuptools/_distutils/command/__pycache__/clean.cpython-312.pyc create mode 100644 venv/Lib/site-packages/setuptools/_distutils/command/__pycache__/config.cpython-312.pyc create mode 100644 venv/Lib/site-packages/setuptools/_distutils/command/__pycache__/install.cpython-312.pyc create mode 100644 venv/Lib/site-packages/setuptools/_distutils/command/__pycache__/install_data.cpython-312.pyc create mode 100644 venv/Lib/site-packages/setuptools/_distutils/command/__pycache__/install_egg_info.cpython-312.pyc create mode 100644 venv/Lib/site-packages/setuptools/_distutils/command/__pycache__/install_headers.cpython-312.pyc create mode 100644 venv/Lib/site-packages/setuptools/_distutils/command/__pycache__/install_lib.cpython-312.pyc create mode 100644 venv/Lib/site-packages/setuptools/_distutils/command/__pycache__/install_scripts.cpython-312.pyc create mode 100644 venv/Lib/site-packages/setuptools/_distutils/command/__pycache__/register.cpython-312.pyc create mode 100644 venv/Lib/site-packages/setuptools/_distutils/command/__pycache__/sdist.cpython-312.pyc create mode 100644 venv/Lib/site-packages/setuptools/_distutils/command/__pycache__/upload.cpython-312.pyc create mode 100644 venv/Lib/site-packages/setuptools/_distutils/command/_framework_compat.py create mode 100644 venv/Lib/site-packages/setuptools/_distutils/command/bdist.py create mode 100644 venv/Lib/site-packages/setuptools/_distutils/command/bdist_dumb.py create mode 100644 venv/Lib/site-packages/setuptools/_distutils/command/bdist_rpm.py create mode 100644 venv/Lib/site-packages/setuptools/_distutils/command/build.py create mode 100644 venv/Lib/site-packages/setuptools/_distutils/command/build_clib.py create mode 100644 venv/Lib/site-packages/setuptools/_distutils/command/build_ext.py create mode 100644 venv/Lib/site-packages/setuptools/_distutils/command/build_py.py create mode 100644 venv/Lib/site-packages/setuptools/_distutils/command/build_scripts.py create mode 100644 venv/Lib/site-packages/setuptools/_distutils/command/check.py create mode 100644 venv/Lib/site-packages/setuptools/_distutils/command/clean.py create mode 100644 venv/Lib/site-packages/setuptools/_distutils/command/config.py create mode 100644 venv/Lib/site-packages/setuptools/_distutils/command/install.py create mode 100644 venv/Lib/site-packages/setuptools/_distutils/command/install_data.py create mode 100644 venv/Lib/site-packages/setuptools/_distutils/command/install_egg_info.py create mode 100644 venv/Lib/site-packages/setuptools/_distutils/command/install_headers.py create mode 100644 venv/Lib/site-packages/setuptools/_distutils/command/install_lib.py create mode 100644 venv/Lib/site-packages/setuptools/_distutils/command/install_scripts.py create mode 100644 venv/Lib/site-packages/setuptools/_distutils/command/register.py create mode 100644 venv/Lib/site-packages/setuptools/_distutils/command/sdist.py create mode 100644 venv/Lib/site-packages/setuptools/_distutils/command/upload.py create mode 100644 venv/Lib/site-packages/setuptools/_distutils/compat/__init__.py create mode 100644 venv/Lib/site-packages/setuptools/_distutils/compat/__pycache__/__init__.cpython-312.pyc create mode 100644 venv/Lib/site-packages/setuptools/_distutils/compat/__pycache__/py38.cpython-312.pyc create mode 100644 venv/Lib/site-packages/setuptools/_distutils/compat/py38.py create mode 100644 venv/Lib/site-packages/setuptools/_distutils/config.py create mode 100644 venv/Lib/site-packages/setuptools/_distutils/core.py create mode 100644 venv/Lib/site-packages/setuptools/_distutils/cygwinccompiler.py create mode 100644 venv/Lib/site-packages/setuptools/_distutils/debug.py create mode 100644 venv/Lib/site-packages/setuptools/_distutils/dep_util.py create mode 100644 venv/Lib/site-packages/setuptools/_distutils/dir_util.py create mode 100644 venv/Lib/site-packages/setuptools/_distutils/dist.py create mode 100644 venv/Lib/site-packages/setuptools/_distutils/errors.py create mode 100644 venv/Lib/site-packages/setuptools/_distutils/extension.py create mode 100644 venv/Lib/site-packages/setuptools/_distutils/fancy_getopt.py create mode 100644 venv/Lib/site-packages/setuptools/_distutils/file_util.py create mode 100644 venv/Lib/site-packages/setuptools/_distutils/filelist.py create mode 100644 venv/Lib/site-packages/setuptools/_distutils/log.py create mode 100644 venv/Lib/site-packages/setuptools/_distutils/msvc9compiler.py create mode 100644 venv/Lib/site-packages/setuptools/_distutils/msvccompiler.py create mode 100644 venv/Lib/site-packages/setuptools/_distutils/py38compat.py create mode 100644 venv/Lib/site-packages/setuptools/_distutils/py39compat.py create mode 100644 venv/Lib/site-packages/setuptools/_distutils/spawn.py create mode 100644 venv/Lib/site-packages/setuptools/_distutils/sysconfig.py create mode 100644 venv/Lib/site-packages/setuptools/_distutils/text_file.py create mode 100644 venv/Lib/site-packages/setuptools/_distutils/unixccompiler.py create mode 100644 venv/Lib/site-packages/setuptools/_distutils/util.py create mode 100644 venv/Lib/site-packages/setuptools/_distutils/version.py create mode 100644 venv/Lib/site-packages/setuptools/_distutils/versionpredicate.py create mode 100644 venv/Lib/site-packages/setuptools/_distutils/zosccompiler.py create mode 100644 venv/Lib/site-packages/setuptools/_entry_points.py create mode 100644 venv/Lib/site-packages/setuptools/_imp.py create mode 100644 venv/Lib/site-packages/setuptools/_importlib.py create mode 100644 venv/Lib/site-packages/setuptools/_itertools.py create mode 100644 venv/Lib/site-packages/setuptools/_normalization.py create mode 100644 venv/Lib/site-packages/setuptools/_path.py create mode 100644 venv/Lib/site-packages/setuptools/_reqs.py create mode 100644 venv/Lib/site-packages/setuptools/_vendor/__init__.py create mode 100644 venv/Lib/site-packages/setuptools/_vendor/__pycache__/__init__.cpython-312.pyc create mode 100644 venv/Lib/site-packages/setuptools/_vendor/__pycache__/ordered_set.cpython-312.pyc create mode 100644 venv/Lib/site-packages/setuptools/_vendor/__pycache__/typing_extensions.cpython-312.pyc create mode 100644 venv/Lib/site-packages/setuptools/_vendor/__pycache__/zipp.cpython-312.pyc create mode 100644 venv/Lib/site-packages/setuptools/_vendor/backports/__init__.py create mode 100644 venv/Lib/site-packages/setuptools/_vendor/backports/__pycache__/__init__.cpython-312.pyc create mode 100644 venv/Lib/site-packages/setuptools/_vendor/backports/__pycache__/tarfile.cpython-312.pyc create mode 100644 venv/Lib/site-packages/setuptools/_vendor/backports/tarfile.py create mode 100644 venv/Lib/site-packages/setuptools/_vendor/importlib_metadata/__init__.py create mode 100644 venv/Lib/site-packages/setuptools/_vendor/importlib_metadata/__pycache__/__init__.cpython-312.pyc create mode 100644 venv/Lib/site-packages/setuptools/_vendor/importlib_metadata/__pycache__/_adapters.cpython-312.pyc create mode 100644 venv/Lib/site-packages/setuptools/_vendor/importlib_metadata/__pycache__/_collections.cpython-312.pyc create mode 100644 venv/Lib/site-packages/setuptools/_vendor/importlib_metadata/__pycache__/_compat.cpython-312.pyc create mode 100644 venv/Lib/site-packages/setuptools/_vendor/importlib_metadata/__pycache__/_functools.cpython-312.pyc create mode 100644 venv/Lib/site-packages/setuptools/_vendor/importlib_metadata/__pycache__/_itertools.cpython-312.pyc create mode 100644 venv/Lib/site-packages/setuptools/_vendor/importlib_metadata/__pycache__/_meta.cpython-312.pyc create mode 100644 venv/Lib/site-packages/setuptools/_vendor/importlib_metadata/__pycache__/_py39compat.cpython-312.pyc create mode 100644 venv/Lib/site-packages/setuptools/_vendor/importlib_metadata/__pycache__/_text.cpython-312.pyc create mode 100644 venv/Lib/site-packages/setuptools/_vendor/importlib_metadata/_adapters.py create mode 100644 venv/Lib/site-packages/setuptools/_vendor/importlib_metadata/_collections.py create mode 100644 venv/Lib/site-packages/setuptools/_vendor/importlib_metadata/_compat.py create mode 100644 venv/Lib/site-packages/setuptools/_vendor/importlib_metadata/_functools.py create mode 100644 venv/Lib/site-packages/setuptools/_vendor/importlib_metadata/_itertools.py create mode 100644 venv/Lib/site-packages/setuptools/_vendor/importlib_metadata/_meta.py create mode 100644 venv/Lib/site-packages/setuptools/_vendor/importlib_metadata/_py39compat.py create mode 100644 venv/Lib/site-packages/setuptools/_vendor/importlib_metadata/_text.py create mode 100644 venv/Lib/site-packages/setuptools/_vendor/importlib_metadata/py.typed create mode 100644 venv/Lib/site-packages/setuptools/_vendor/importlib_resources/__init__.py create mode 100644 venv/Lib/site-packages/setuptools/_vendor/importlib_resources/__pycache__/__init__.cpython-312.pyc create mode 100644 venv/Lib/site-packages/setuptools/_vendor/importlib_resources/__pycache__/_adapters.cpython-312.pyc create mode 100644 venv/Lib/site-packages/setuptools/_vendor/importlib_resources/__pycache__/_common.cpython-312.pyc create mode 100644 venv/Lib/site-packages/setuptools/_vendor/importlib_resources/__pycache__/_compat.cpython-312.pyc create mode 100644 venv/Lib/site-packages/setuptools/_vendor/importlib_resources/__pycache__/_itertools.cpython-312.pyc create mode 100644 venv/Lib/site-packages/setuptools/_vendor/importlib_resources/__pycache__/_legacy.cpython-312.pyc create mode 100644 venv/Lib/site-packages/setuptools/_vendor/importlib_resources/__pycache__/abc.cpython-312.pyc create mode 100644 venv/Lib/site-packages/setuptools/_vendor/importlib_resources/__pycache__/readers.cpython-312.pyc create mode 100644 venv/Lib/site-packages/setuptools/_vendor/importlib_resources/__pycache__/simple.cpython-312.pyc create mode 100644 venv/Lib/site-packages/setuptools/_vendor/importlib_resources/_adapters.py create mode 100644 venv/Lib/site-packages/setuptools/_vendor/importlib_resources/_common.py create mode 100644 venv/Lib/site-packages/setuptools/_vendor/importlib_resources/_compat.py create mode 100644 venv/Lib/site-packages/setuptools/_vendor/importlib_resources/_itertools.py create mode 100644 venv/Lib/site-packages/setuptools/_vendor/importlib_resources/_legacy.py create mode 100644 venv/Lib/site-packages/setuptools/_vendor/importlib_resources/abc.py create mode 100644 venv/Lib/site-packages/setuptools/_vendor/importlib_resources/py.typed create mode 100644 venv/Lib/site-packages/setuptools/_vendor/importlib_resources/readers.py create mode 100644 venv/Lib/site-packages/setuptools/_vendor/importlib_resources/simple.py create mode 100644 venv/Lib/site-packages/setuptools/_vendor/jaraco/__init__.py create mode 100644 venv/Lib/site-packages/setuptools/_vendor/jaraco/__pycache__/__init__.cpython-312.pyc create mode 100644 venv/Lib/site-packages/setuptools/_vendor/jaraco/__pycache__/context.cpython-312.pyc create mode 100644 venv/Lib/site-packages/setuptools/_vendor/jaraco/context.py create mode 100644 venv/Lib/site-packages/setuptools/_vendor/jaraco/functools/__init__.py create mode 100644 venv/Lib/site-packages/setuptools/_vendor/jaraco/functools/__init__.pyi create mode 100644 venv/Lib/site-packages/setuptools/_vendor/jaraco/functools/__pycache__/__init__.cpython-312.pyc create mode 100644 venv/Lib/site-packages/setuptools/_vendor/jaraco/functools/py.typed create mode 100644 venv/Lib/site-packages/setuptools/_vendor/jaraco/text/__init__.py create mode 100644 venv/Lib/site-packages/setuptools/_vendor/jaraco/text/__pycache__/__init__.cpython-312.pyc create mode 100644 venv/Lib/site-packages/setuptools/_vendor/more_itertools/__init__.py create mode 100644 venv/Lib/site-packages/setuptools/_vendor/more_itertools/__init__.pyi create mode 100644 venv/Lib/site-packages/setuptools/_vendor/more_itertools/__pycache__/__init__.cpython-312.pyc create mode 100644 venv/Lib/site-packages/setuptools/_vendor/more_itertools/__pycache__/more.cpython-312.pyc create mode 100644 venv/Lib/site-packages/setuptools/_vendor/more_itertools/__pycache__/recipes.cpython-312.pyc create mode 100644 venv/Lib/site-packages/setuptools/_vendor/more_itertools/more.py create mode 100644 venv/Lib/site-packages/setuptools/_vendor/more_itertools/more.pyi create mode 100644 venv/Lib/site-packages/setuptools/_vendor/more_itertools/py.typed create mode 100644 venv/Lib/site-packages/setuptools/_vendor/more_itertools/recipes.py create mode 100644 venv/Lib/site-packages/setuptools/_vendor/more_itertools/recipes.pyi create mode 100644 venv/Lib/site-packages/setuptools/_vendor/ordered_set.py create mode 100644 venv/Lib/site-packages/setuptools/_vendor/packaging/__init__.py create mode 100644 venv/Lib/site-packages/setuptools/_vendor/packaging/__pycache__/__init__.cpython-312.pyc create mode 100644 venv/Lib/site-packages/setuptools/_vendor/packaging/__pycache__/_elffile.cpython-312.pyc create mode 100644 venv/Lib/site-packages/setuptools/_vendor/packaging/__pycache__/_manylinux.cpython-312.pyc create mode 100644 venv/Lib/site-packages/setuptools/_vendor/packaging/__pycache__/_musllinux.cpython-312.pyc create mode 100644 venv/Lib/site-packages/setuptools/_vendor/packaging/__pycache__/_parser.cpython-312.pyc create mode 100644 venv/Lib/site-packages/setuptools/_vendor/packaging/__pycache__/_structures.cpython-312.pyc create mode 100644 venv/Lib/site-packages/setuptools/_vendor/packaging/__pycache__/_tokenizer.cpython-312.pyc create mode 100644 venv/Lib/site-packages/setuptools/_vendor/packaging/__pycache__/markers.cpython-312.pyc create mode 100644 venv/Lib/site-packages/setuptools/_vendor/packaging/__pycache__/metadata.cpython-312.pyc create mode 100644 venv/Lib/site-packages/setuptools/_vendor/packaging/__pycache__/requirements.cpython-312.pyc create mode 100644 venv/Lib/site-packages/setuptools/_vendor/packaging/__pycache__/specifiers.cpython-312.pyc create mode 100644 venv/Lib/site-packages/setuptools/_vendor/packaging/__pycache__/tags.cpython-312.pyc create mode 100644 venv/Lib/site-packages/setuptools/_vendor/packaging/__pycache__/utils.cpython-312.pyc create mode 100644 venv/Lib/site-packages/setuptools/_vendor/packaging/__pycache__/version.cpython-312.pyc create mode 100644 venv/Lib/site-packages/setuptools/_vendor/packaging/_elffile.py create mode 100644 venv/Lib/site-packages/setuptools/_vendor/packaging/_manylinux.py create mode 100644 venv/Lib/site-packages/setuptools/_vendor/packaging/_musllinux.py create mode 100644 venv/Lib/site-packages/setuptools/_vendor/packaging/_parser.py create mode 100644 venv/Lib/site-packages/setuptools/_vendor/packaging/_structures.py create mode 100644 venv/Lib/site-packages/setuptools/_vendor/packaging/_tokenizer.py create mode 100644 venv/Lib/site-packages/setuptools/_vendor/packaging/markers.py create mode 100644 venv/Lib/site-packages/setuptools/_vendor/packaging/metadata.py create mode 100644 venv/Lib/site-packages/setuptools/_vendor/packaging/py.typed create mode 100644 venv/Lib/site-packages/setuptools/_vendor/packaging/requirements.py create mode 100644 venv/Lib/site-packages/setuptools/_vendor/packaging/specifiers.py create mode 100644 venv/Lib/site-packages/setuptools/_vendor/packaging/tags.py create mode 100644 venv/Lib/site-packages/setuptools/_vendor/packaging/utils.py create mode 100644 venv/Lib/site-packages/setuptools/_vendor/packaging/version.py create mode 100644 venv/Lib/site-packages/setuptools/_vendor/tomli/__init__.py create mode 100644 venv/Lib/site-packages/setuptools/_vendor/tomli/__pycache__/__init__.cpython-312.pyc create mode 100644 venv/Lib/site-packages/setuptools/_vendor/tomli/__pycache__/_parser.cpython-312.pyc create mode 100644 venv/Lib/site-packages/setuptools/_vendor/tomli/__pycache__/_re.cpython-312.pyc create mode 100644 venv/Lib/site-packages/setuptools/_vendor/tomli/__pycache__/_types.cpython-312.pyc create mode 100644 venv/Lib/site-packages/setuptools/_vendor/tomli/_parser.py create mode 100644 venv/Lib/site-packages/setuptools/_vendor/tomli/_re.py create mode 100644 venv/Lib/site-packages/setuptools/_vendor/tomli/_types.py create mode 100644 venv/Lib/site-packages/setuptools/_vendor/tomli/py.typed create mode 100644 venv/Lib/site-packages/setuptools/_vendor/typing_extensions.py create mode 100644 venv/Lib/site-packages/setuptools/_vendor/zipp.py create mode 100644 venv/Lib/site-packages/setuptools/archive_util.py create mode 100644 venv/Lib/site-packages/setuptools/build_meta.py create mode 100644 venv/Lib/site-packages/setuptools/cli-32.exe create mode 100644 venv/Lib/site-packages/setuptools/cli-64.exe create mode 100644 venv/Lib/site-packages/setuptools/cli-arm64.exe create mode 100644 venv/Lib/site-packages/setuptools/cli.exe create mode 100644 venv/Lib/site-packages/setuptools/command/__init__.py create mode 100644 venv/Lib/site-packages/setuptools/command/__pycache__/__init__.cpython-312.pyc create mode 100644 venv/Lib/site-packages/setuptools/command/__pycache__/_requirestxt.cpython-312.pyc create mode 100644 venv/Lib/site-packages/setuptools/command/__pycache__/alias.cpython-312.pyc create mode 100644 venv/Lib/site-packages/setuptools/command/__pycache__/bdist_egg.cpython-312.pyc create mode 100644 venv/Lib/site-packages/setuptools/command/__pycache__/bdist_rpm.cpython-312.pyc create mode 100644 venv/Lib/site-packages/setuptools/command/__pycache__/build.cpython-312.pyc create mode 100644 venv/Lib/site-packages/setuptools/command/__pycache__/build_clib.cpython-312.pyc create mode 100644 venv/Lib/site-packages/setuptools/command/__pycache__/build_ext.cpython-312.pyc create mode 100644 venv/Lib/site-packages/setuptools/command/__pycache__/build_py.cpython-312.pyc create mode 100644 venv/Lib/site-packages/setuptools/command/__pycache__/develop.cpython-312.pyc create mode 100644 venv/Lib/site-packages/setuptools/command/__pycache__/dist_info.cpython-312.pyc create mode 100644 venv/Lib/site-packages/setuptools/command/__pycache__/easy_install.cpython-312.pyc create mode 100644 venv/Lib/site-packages/setuptools/command/__pycache__/editable_wheel.cpython-312.pyc create mode 100644 venv/Lib/site-packages/setuptools/command/__pycache__/egg_info.cpython-312.pyc create mode 100644 venv/Lib/site-packages/setuptools/command/__pycache__/install.cpython-312.pyc create mode 100644 venv/Lib/site-packages/setuptools/command/__pycache__/install_egg_info.cpython-312.pyc create mode 100644 venv/Lib/site-packages/setuptools/command/__pycache__/install_lib.cpython-312.pyc create mode 100644 venv/Lib/site-packages/setuptools/command/__pycache__/install_scripts.cpython-312.pyc create mode 100644 venv/Lib/site-packages/setuptools/command/__pycache__/register.cpython-312.pyc create mode 100644 venv/Lib/site-packages/setuptools/command/__pycache__/rotate.cpython-312.pyc create mode 100644 venv/Lib/site-packages/setuptools/command/__pycache__/saveopts.cpython-312.pyc create mode 100644 venv/Lib/site-packages/setuptools/command/__pycache__/sdist.cpython-312.pyc create mode 100644 venv/Lib/site-packages/setuptools/command/__pycache__/setopt.cpython-312.pyc create mode 100644 venv/Lib/site-packages/setuptools/command/__pycache__/test.cpython-312.pyc create mode 100644 venv/Lib/site-packages/setuptools/command/__pycache__/upload.cpython-312.pyc create mode 100644 venv/Lib/site-packages/setuptools/command/__pycache__/upload_docs.cpython-312.pyc create mode 100644 venv/Lib/site-packages/setuptools/command/_requirestxt.py create mode 100644 venv/Lib/site-packages/setuptools/command/alias.py create mode 100644 venv/Lib/site-packages/setuptools/command/bdist_egg.py create mode 100644 venv/Lib/site-packages/setuptools/command/bdist_rpm.py create mode 100644 venv/Lib/site-packages/setuptools/command/build.py create mode 100644 venv/Lib/site-packages/setuptools/command/build_clib.py create mode 100644 venv/Lib/site-packages/setuptools/command/build_ext.py create mode 100644 venv/Lib/site-packages/setuptools/command/build_py.py create mode 100644 venv/Lib/site-packages/setuptools/command/develop.py create mode 100644 venv/Lib/site-packages/setuptools/command/dist_info.py create mode 100644 venv/Lib/site-packages/setuptools/command/easy_install.py create mode 100644 venv/Lib/site-packages/setuptools/command/editable_wheel.py create mode 100644 venv/Lib/site-packages/setuptools/command/egg_info.py create mode 100644 venv/Lib/site-packages/setuptools/command/install.py create mode 100644 venv/Lib/site-packages/setuptools/command/install_egg_info.py create mode 100644 venv/Lib/site-packages/setuptools/command/install_lib.py create mode 100644 venv/Lib/site-packages/setuptools/command/install_scripts.py create mode 100644 venv/Lib/site-packages/setuptools/command/launcher manifest.xml create mode 100644 venv/Lib/site-packages/setuptools/command/register.py create mode 100644 venv/Lib/site-packages/setuptools/command/rotate.py create mode 100644 venv/Lib/site-packages/setuptools/command/saveopts.py create mode 100644 venv/Lib/site-packages/setuptools/command/sdist.py create mode 100644 venv/Lib/site-packages/setuptools/command/setopt.py create mode 100644 venv/Lib/site-packages/setuptools/command/test.py create mode 100644 venv/Lib/site-packages/setuptools/command/upload.py create mode 100644 venv/Lib/site-packages/setuptools/command/upload_docs.py create mode 100644 venv/Lib/site-packages/setuptools/compat/__init__.py create mode 100644 venv/Lib/site-packages/setuptools/compat/__pycache__/__init__.cpython-312.pyc create mode 100644 venv/Lib/site-packages/setuptools/compat/__pycache__/py310.cpython-312.pyc create mode 100644 venv/Lib/site-packages/setuptools/compat/__pycache__/py311.cpython-312.pyc create mode 100644 venv/Lib/site-packages/setuptools/compat/__pycache__/py39.cpython-312.pyc create mode 100644 venv/Lib/site-packages/setuptools/compat/py310.py create mode 100644 venv/Lib/site-packages/setuptools/compat/py311.py create mode 100644 venv/Lib/site-packages/setuptools/compat/py39.py create mode 100644 venv/Lib/site-packages/setuptools/config/__init__.py create mode 100644 venv/Lib/site-packages/setuptools/config/__pycache__/__init__.cpython-312.pyc create mode 100644 venv/Lib/site-packages/setuptools/config/__pycache__/_apply_pyprojecttoml.cpython-312.pyc create mode 100644 venv/Lib/site-packages/setuptools/config/__pycache__/expand.cpython-312.pyc create mode 100644 venv/Lib/site-packages/setuptools/config/__pycache__/pyprojecttoml.cpython-312.pyc create mode 100644 venv/Lib/site-packages/setuptools/config/__pycache__/setupcfg.cpython-312.pyc create mode 100644 venv/Lib/site-packages/setuptools/config/_apply_pyprojecttoml.py create mode 100644 venv/Lib/site-packages/setuptools/config/_validate_pyproject/__init__.py create mode 100644 venv/Lib/site-packages/setuptools/config/_validate_pyproject/__pycache__/__init__.cpython-312.pyc create mode 100644 venv/Lib/site-packages/setuptools/config/_validate_pyproject/__pycache__/error_reporting.cpython-312.pyc create mode 100644 venv/Lib/site-packages/setuptools/config/_validate_pyproject/__pycache__/extra_validations.cpython-312.pyc create mode 100644 venv/Lib/site-packages/setuptools/config/_validate_pyproject/__pycache__/fastjsonschema_exceptions.cpython-312.pyc create mode 100644 venv/Lib/site-packages/setuptools/config/_validate_pyproject/__pycache__/fastjsonschema_validations.cpython-312.pyc create mode 100644 venv/Lib/site-packages/setuptools/config/_validate_pyproject/__pycache__/formats.cpython-312.pyc create mode 100644 venv/Lib/site-packages/setuptools/config/_validate_pyproject/error_reporting.py create mode 100644 venv/Lib/site-packages/setuptools/config/_validate_pyproject/extra_validations.py create mode 100644 venv/Lib/site-packages/setuptools/config/_validate_pyproject/fastjsonschema_exceptions.py create mode 100644 venv/Lib/site-packages/setuptools/config/_validate_pyproject/fastjsonschema_validations.py create mode 100644 venv/Lib/site-packages/setuptools/config/_validate_pyproject/formats.py create mode 100644 venv/Lib/site-packages/setuptools/config/expand.py create mode 100644 venv/Lib/site-packages/setuptools/config/pyprojecttoml.py create mode 100644 venv/Lib/site-packages/setuptools/config/setupcfg.py create mode 100644 venv/Lib/site-packages/setuptools/dep_util.py create mode 100644 venv/Lib/site-packages/setuptools/depends.py create mode 100644 venv/Lib/site-packages/setuptools/discovery.py create mode 100644 venv/Lib/site-packages/setuptools/dist.py create mode 100644 venv/Lib/site-packages/setuptools/errors.py create mode 100644 venv/Lib/site-packages/setuptools/extension.py create mode 100644 venv/Lib/site-packages/setuptools/extern/__init__.py create mode 100644 venv/Lib/site-packages/setuptools/extern/__pycache__/__init__.cpython-312.pyc create mode 100644 venv/Lib/site-packages/setuptools/glob.py create mode 100644 venv/Lib/site-packages/setuptools/gui-32.exe create mode 100644 venv/Lib/site-packages/setuptools/gui-64.exe create mode 100644 venv/Lib/site-packages/setuptools/gui-arm64.exe create mode 100644 venv/Lib/site-packages/setuptools/gui.exe create mode 100644 venv/Lib/site-packages/setuptools/installer.py create mode 100644 venv/Lib/site-packages/setuptools/launch.py create mode 100644 venv/Lib/site-packages/setuptools/logging.py create mode 100644 venv/Lib/site-packages/setuptools/modified.py create mode 100644 venv/Lib/site-packages/setuptools/monkey.py create mode 100644 venv/Lib/site-packages/setuptools/msvc.py create mode 100644 venv/Lib/site-packages/setuptools/namespaces.py create mode 100644 venv/Lib/site-packages/setuptools/package_index.py create mode 100644 venv/Lib/site-packages/setuptools/sandbox.py create mode 100644 venv/Lib/site-packages/setuptools/script (dev).tmpl create mode 100644 venv/Lib/site-packages/setuptools/script.tmpl create mode 100644 venv/Lib/site-packages/setuptools/unicode_utils.py create mode 100644 venv/Lib/site-packages/setuptools/version.py create mode 100644 venv/Lib/site-packages/setuptools/warnings.py create mode 100644 venv/Lib/site-packages/setuptools/wheel.py create mode 100644 venv/Lib/site-packages/setuptools/windows_support.py diff --git a/main.py b/main.py index 18e41e5..db94720 100644 --- a/main.py +++ b/main.py @@ -1,33 +1,19 @@ from flask import Flask, render_template, redirect, request, session, url_for from flask_sqlalchemy import SQLAlchemy -from flask_admin import Admin +from flask_admin import Admin, AdminIndexView, expose from flask_admin.contrib.sqla import ModelView -from flask_security import Security, SQLAlchemyUserDatastore, UserMixin, RoleMixin + app = Flask(__name__) app.secret_key = 'bebra' app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///Picture_Puzzle_web.db' -app.config['SECURITY_PASSWORD_SALT'] = app.config['SECRET_KEY'] db = SQLAlchemy(app) -# Define models for Flask-Security -roles_users = db.Table('roles_users', - db.Column('user_id', db.Integer(), db.ForeignKey('user.id')), - db.Column('role_id', db.Integer(), db.ForeignKey('role.id')) -) - -class Role(db.Model, RoleMixin): - id = db.Column(db.Integer(), primary_key=True) - name = db.Column(db.String(80), unique=True) - -class User(db.Model, UserMixin): +class User(db.Model): id = db.Column(db.Integer, primary_key=True) - username = db.Column(db.String(255), unique=True) - email = db.Column(db.String(255), unique=True) - password = db.Column(db.String(255)) - active = db.Column(db.Boolean()) - roles = db.relationship('Role', secondary=roles_users, - backref=db.backref('users', lazy='dynamic')) + username = db.Column(db.String(100), unique=True, nullable=False) + password = db.Column(db.String(100), nullable=False) + email = db.Column(db.String(100), unique=True, nullable=False) class Post(db.Model): id = db.Column(db.Integer, primary_key=True) @@ -36,21 +22,50 @@ class Post(db.Model): title = db.Column(db.String(100), nullable=False) image = db.Column(db.String(100), nullable=False) -user_datastore = SQLAlchemyUserDatastore(db, User, Role) -security = Security(app, user_datastore) +class MyAdminIndexView(AdminIndexView): + @expose('/') + def index(self): + if not session.get('admin_logged_in'): + return redirect(url_for('admin_login')) + return self.render('admin/index.html') -admin = Admin(app, name='Admin Panel', template_mode='bootstrap3') -admin.add_view(ModelView(User, db.session)) -admin.add_view(ModelView(Role, db.session)) +class UserAdminView(ModelView): + column_exclude_list = ['password'] + form_excluded_columns = ['password'] -def create_tables(): - with app.app_context(): - db.create_all() +class PostAdminView(ModelView): + pass -def register_admin_views(): - admin = Admin(app, name='Admin Panel', template_mode='bootstrap3') - admin.add_view(ModelView(User, db.session)) - admin.add_view(ModelView(Post, db.session)) +class TableAdminView(ModelView): + can_delete = True + can_create = True + can_edit = True + column_display_pk = True + +admin = Admin(app, name='Admin Panel', template_mode='bootstrap3', index_view=MyAdminIndexView()) + +admin.add_view(UserAdminView(User, db.session)) +admin.add_view(PostAdminView(Post, db.session)) + +ADMIN_USERNAME = 'eluk' +ADMIN_PASSWORD = '1234' + +@app.route("/admin/login", methods=["GET", "POST"]) +def admin_login(): + if request.method == "POST": + username = request.form["username"] + password = request.form["password"] + if username == ADMIN_USERNAME and password == ADMIN_PASSWORD: + session["admin_logged_in"] = True + return redirect(url_for("admin.index")) + else: + return render_template("admin/login.html", error_msg="Invalid credentials") + return render_template("admin/login.html", error_msg=None) + +@app.route("/admin/logout") +def admin_logout(): + session.pop("admin_logged_in", None) + return redirect(url_for("index")) @app.route("/") def index(): @@ -115,5 +130,4 @@ def register(): return render_template("auth/register.html") if __name__ == '__main__': - register_admin_views() app.run(debug=True) \ No newline at end of file diff --git a/templates/admin/login.html b/templates/admin/login.html new file mode 100644 index 0000000..9748ea1 --- /dev/null +++ b/templates/admin/login.html @@ -0,0 +1,79 @@ + + + + + + Admin Login + + + +
+

Admin Login

+ {% if error_msg %} +

{{ error_msg }}

+ {% endif %} + + + + + +
+ + diff --git a/venv/Lib/site-packages/_distutils_hack/__init__.py b/venv/Lib/site-packages/_distutils_hack/__init__.py new file mode 100644 index 0000000..4d3f09b --- /dev/null +++ b/venv/Lib/site-packages/_distutils_hack/__init__.py @@ -0,0 +1,220 @@ +# don't import any costly modules +import sys +import os + + +def warn_distutils_present(): + if 'distutils' not in sys.modules: + return + import warnings + + warnings.warn( + "Distutils was imported before Setuptools, but importing Setuptools " + "also replaces the `distutils` module in `sys.modules`. This may lead " + "to undesirable behaviors or errors. To avoid these issues, avoid " + "using distutils directly, ensure that setuptools is installed in the " + "traditional way (e.g. not an editable install), and/or make sure " + "that setuptools is always imported before distutils." + ) + + +def clear_distutils(): + if 'distutils' not in sys.modules: + return + import warnings + + warnings.warn("Setuptools is replacing distutils.") + mods = [ + name + for name in sys.modules + if name == "distutils" or name.startswith("distutils.") + ] + for name in mods: + del sys.modules[name] + + +def enabled(): + """ + Allow selection of distutils by environment variable. + """ + which = os.environ.get('SETUPTOOLS_USE_DISTUTILS', 'local') + return which == 'local' + + +def ensure_local_distutils(): + import importlib + + clear_distutils() + + # With the DistutilsMetaFinder in place, + # perform an import to cause distutils to be + # loaded from setuptools._distutils. Ref #2906. + with shim(): + importlib.import_module('distutils') + + # check that submodules load as expected + core = importlib.import_module('distutils.core') + assert '_distutils' in core.__file__, core.__file__ + assert 'setuptools._distutils.log' not in sys.modules + + +def do_override(): + """ + Ensure that the local copy of distutils is preferred over stdlib. + + See https://github.com/pypa/setuptools/issues/417#issuecomment-392298401 + for more motivation. + """ + if enabled(): + warn_distutils_present() + ensure_local_distutils() + + +class _TrivialRe: + def __init__(self, *patterns): + self._patterns = patterns + + def match(self, string): + return all(pat in string for pat in self._patterns) + + +class DistutilsMetaFinder: + def find_spec(self, fullname, path, target=None): + # optimization: only consider top level modules and those + # found in the CPython test suite. + if path is not None and not fullname.startswith('test.'): + return None + + method_name = 'spec_for_{fullname}'.format(**locals()) + method = getattr(self, method_name, lambda: None) + return method() + + def spec_for_distutils(self): + if self.is_cpython(): + return None + + import importlib + import importlib.abc + import importlib.util + + try: + mod = importlib.import_module('setuptools._distutils') + except Exception: + # There are a couple of cases where setuptools._distutils + # may not be present: + # - An older Setuptools without a local distutils is + # taking precedence. Ref #2957. + # - Path manipulation during sitecustomize removes + # setuptools from the path but only after the hook + # has been loaded. Ref #2980. + # In either case, fall back to stdlib behavior. + return None + + class DistutilsLoader(importlib.abc.Loader): + def create_module(self, spec): + mod.__name__ = 'distutils' + return mod + + def exec_module(self, module): + pass + + return importlib.util.spec_from_loader( + 'distutils', DistutilsLoader(), origin=mod.__file__ + ) + + @staticmethod + def is_cpython(): + """ + Suppress supplying distutils for CPython (build and tests). + Ref #2965 and #3007. + """ + return os.path.isfile('pybuilddir.txt') + + def spec_for_pip(self): + """ + Ensure stdlib distutils when running under pip. + See pypa/pip#8761 for rationale. + """ + if sys.version_info >= (3, 12) or self.pip_imported_during_build(): + return + clear_distutils() + self.spec_for_distutils = lambda: None + + @classmethod + def pip_imported_during_build(cls): + """ + Detect if pip is being imported in a build script. Ref #2355. + """ + import traceback + + return any( + cls.frame_file_is_setup(frame) for frame, line in traceback.walk_stack(None) + ) + + @staticmethod + def frame_file_is_setup(frame): + """ + Return True if the indicated frame suggests a setup.py file. + """ + # some frames may not have __file__ (#2940) + return frame.f_globals.get('__file__', '').endswith('setup.py') + + def spec_for_sensitive_tests(self): + """ + Ensure stdlib distutils when running select tests under CPython. + + python/cpython#91169 + """ + clear_distutils() + self.spec_for_distutils = lambda: None + + sensitive_tests = ( + [ + 'test.test_distutils', + 'test.test_peg_generator', + 'test.test_importlib', + ] + if sys.version_info < (3, 10) + else [ + 'test.test_distutils', + ] + ) + + +for name in DistutilsMetaFinder.sensitive_tests: + setattr( + DistutilsMetaFinder, + f'spec_for_{name}', + DistutilsMetaFinder.spec_for_sensitive_tests, + ) + + +DISTUTILS_FINDER = DistutilsMetaFinder() + + +def add_shim(): + DISTUTILS_FINDER in sys.meta_path or insert_shim() + + +class shim: + def __enter__(self): + insert_shim() + + def __exit__(self, exc, value, tb): + _remove_shim() + + +def insert_shim(): + sys.meta_path.insert(0, DISTUTILS_FINDER) + + +def _remove_shim(): + try: + sys.meta_path.remove(DISTUTILS_FINDER) + except ValueError: + pass + + +if sys.version_info < (3, 12): + # DistutilsMetaFinder can only be disabled in Python < 3.12 (PEP 632) + remove_shim = _remove_shim diff --git a/venv/Lib/site-packages/_distutils_hack/__pycache__/__init__.cpython-312.pyc b/venv/Lib/site-packages/_distutils_hack/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..00a2cde111aeda5e4d987fdb6de162f8b6889b34 GIT binary patch literal 9862 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 H!Cn6g&P2M; literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/_distutils_hack/__pycache__/override.cpython-312.pyc b/venv/Lib/site-packages/_distutils_hack/__pycache__/override.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..291c0d3e1931cc32f8ceeae74115e837d02dc171 GIT binary patch literal 302 zcmX@j%ge<81Vs;Z({zCJV-N=hn4yf%20+Gi1}277h7^X?j1bW*m@G^+kX{XCGcr^% zYBJs8k59=gE-5X^%qfn~NKDT51B!9Q$7kjik$&b%3OD!tOOi5kI@EN4} zSBA4yOlWaxQ8Ac|ami0E%}vcKDUNZ@Psz+nj|s?3E-5Wa)eR`Es>(^#El*7<&MZld zDND^Oi}A@!iUIL-3xJj-rl%IipgBMvW}99?(?:[0-9]+!)?[0-9]+(?:\.[0-9]+)*)", re.I) + + +class PEP440Warning(RuntimeWarning): + """ + Used when there is an issue with a version or specifier not complying with + PEP 440. + """ + + +parse_version = packaging.version.Version + + +_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) + + +@functools.lru_cache(maxsize=None) +def _macos_vers(): + version = platform.mac_ver()[0] + # fallback for MacPorts + if version == '': + plist = '/System/Library/CoreServices/SystemVersion.plist' + if os.path.exists(plist): + with open(plist, 'rb') as fh: + plist_content = plistlib.load(fh) + if 'ProductVersion' in plist_content: + version = plist_content['ProductVersion'] + return version.split('.') + + +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 get_distribution(dist): + """Return a current distribution object for a Requirement or string""" + if isinstance(dist, str): + 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(Protocol): + def has_metadata(self, name) -> bool: + """Does the package's distribution contain the named metadata?""" + + def get_metadata(self, name): + """The named metadata resource as a string""" + + def get_metadata_lines(self, 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(self, name) -> bool: + """Is the named metadata a directory? (like ``os.path.isdir()``)""" + + def metadata_listdir(self, name): + """List of metadata names in the directory (like ``os.listdir()``)""" + + def run_script(self, script_name, namespace): + """Execute the named script in the supplied namespace dictionary""" + + +class IResourceProvider(IMetadataProvider, Protocol): + """An object that provides access to package resources""" + + def get_resource_filename(self, manager, resource_name): + """Return a true filesystem path for `resource_name` + + `manager` must be an ``IResourceManager``""" + + def get_resource_stream(self, manager, resource_name): + """Return a readable file-like object for `resource_name` + + `manager` must be an ``IResourceManager``""" + + def get_resource_string(self, manager, resource_name) -> bytes: + """Return the contents of `resource_name` as :obj:`bytes` + + `manager` must be an ``IResourceManager``""" + + def has_resource(self, resource_name): + """Does the package contain the named resource?""" + + def resource_isdir(self, resource_name): + """Is the named resource a directory? (like ``os.path.isdir()``)""" + + def resource_listdir(self, 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.normalized_to_canonical_keys = {} + 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 None: + canonical_key = self.normalized_to_canonical_keys.get(req.key) + + if canonical_key is not None: + req.key = canonical_key + dist = self.by_key.get(canonical_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 + normalized_name = packaging.utils.canonicalize_name(dist.key) + self.normalized_to_canonical_keys[normalized_name] = dist.key + 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 = self._resolve_dist( + req, best, replace_conflicting, env, installer, required_by, to_activate + ) + + # 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 _resolve_dist( + self, req, best, replace_conflicting, env, installer, required_by, to_activate + ): + 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) + return dist + + 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.normalized_to_canonical_keys.copy(), + self.callbacks[:], + ) + + def __setstate__(self, e_k_b_n_c): + entries, keys, by_key, normalized_to_canonical_keys, callbacks = e_k_b_n_c + self.entries = entries[:] + self.entry_keys = keys.copy() + self.by_key = by_key.copy() + self.normalized_to_canonical_keys = normalized_to_canonical_keys.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.""" + return installer(requirement) if installer else None + + 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) -> bytes: + """Return specified resource as :obj:`bytes`""" + 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) -> List[str]: + """ + 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 + return [] + + +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 platformdirs.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 _forgiving_version(version): + """Fallback when ``safe_version`` is not safe enough + >>> parse_version(_forgiving_version('0.23ubuntu1')) + + >>> parse_version(_forgiving_version('0.23-')) + + >>> parse_version(_forgiving_version('0.-_')) + + >>> parse_version(_forgiving_version('42.+?1')) + + >>> parse_version(_forgiving_version('hello world')) + + """ + version = version.replace(' ', '.') + match = _PEP440_FALLBACK.search(version) + if match: + safe = match["safe"] + rest = version[len(safe) :] + else: + safe = "0" + rest = version + local = f"sanitized.{_safe_segment(rest)}".strip(".") + return f"{safe}.dev0+{local}" + + +def _safe_segment(segment): + """Convert an arbitrary string into a safe segment""" + segment = re.sub('[^A-Za-z0-9.]+', '-', segment) + segment = re.sub('-[^A-Za-z0-9]+', '-', segment) + return re.sub(r'\.[^A-Za-z0-9]+', '.', segment).strip(".-") + + +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) from 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) -> bytes: + 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) -> bool: + if not self.egg_info: + return False + + 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) + 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) -> bool: + return bool(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): + with open(script_filename) as fid: + source = fid.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) -> bool: + raise NotImplementedError( + "Can't perform this operation for unregistered loader type" + ) + + def _isdir(self, path) -> bool: + 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.pypa.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) + issue_warning( + msg[:-1] + " and will raise exceptions in a future release.", + DeprecationWarning, + ) + + def _get(self, path) -> bytes: + 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): + super().__init__(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) -> bool: + return os.path.exists(path) + + def _isdir(self, path) -> bool: + 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) -> bytes: + 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) -> bytes: + return b'' + + 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): + super().__init__(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 + + # FIXME: 'ZipProvider._extract_resource' is too complex (12) + def _extract_resource(self, manager, zip_path): # noqa: C901 + 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 OSError( + '"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 OSError: + 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 OSError: + # 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) -> bool: + zip_path = self._zipinfo_name(fspath) + return zip_path in self.zipinfo or zip_path in self._index() + + def _isdir(self, fspath) -> bool: + 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) -> bool: + 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 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): + replacement_char = '�' + 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) + yield from dists + elif subitem.lower().endswith(('.dist-info', '.egg-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 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 = (os.path.join(path_item, child) for child in safe_listdir(path_item)) + + # scan for .egg and .egg-info in directory + for entry in sorted(entries): + fullpath = os.path.join(path_item, entry) + factory = dist_factory(path_item, entry, only) + yield from factory(fullpath) + + +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 + + 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 + if e.errno not in (errno.ENOTDIR, errno.EACCES, errno.ENOENT): + 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, ()) + + +if hasattr(pkgutil, 'ImpImporter'): + register_finder(pkgutil.ImpImporter, find_on_path) + +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: + spec = importer.find_spec(packageName) + except AttributeError: + # capture warnings due to #1111 + with warnings.catch_warnings(): + warnings.simplefilter("ignore") + loader = importer.find_module(packageName) + else: + loader = spec.loader if spec else None + + 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) + importlib.import_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""" + + msg = ( + f"Deprecated call to `pkg_resources.declare_namespace({packageName!r})`.\n" + "Implementing implicit namespace packages (as specified in PEP 420) " + "is preferred to `pkg_resources.declare_namespace`. " + "See https://setuptools.pypa.io/en/latest/references/" + "keywords.html#keyword-namespace-packages" + ) + warnings.warn(msg, DeprecationWarning, stacklevel=2) + + _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 as e: + raise TypeError("Not a package:", parent) from e + + # 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 + + +if hasattr(pkgutil, 'ImpImporter'): + register_namespace_handler(pkgutil.ImpImporter, file_ns_handler) + +register_namespace_handler(zipimport.zipimporter, file_ns_handler) +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 + + +@functools.lru_cache(maxsize=None) +def _normalize_cached(filename): + return normalize_path(filename) + + +def _is_egg_path(path): + """ + Determine if given path appears to be an egg. + """ + return _is_zip_egg(path) or _is_unpacked_egg(path) + + +def _is_zip_egg(path): + return ( + path.lower().endswith('.egg') + and os.path.isfile(path) + and zipfile.is_zipfile(path) + ) + + +def _is_unpacked_egg(path): + """ + Determine if given path appears to be an unpacked egg. + """ + return path.lower().endswith('.egg') 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]) + + +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)) from 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._forgiving_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"): + try: + self._parsed_version = parse_version(self.version) + except packaging.version.InvalidVersion as ex: + info = f"(package: {self.project_name})" + if hasattr(ex, "add_note"): + ex.add_note(info) # PEP 678 + raise + raise packaging.version.InvalidVersion(f"{str(ex)} {info}") from None + + return self._parsed_version + + @property + def _forgiving_parsed_version(self): + try: + return self.parsed_version + except packaging.version.InvalidVersion as ex: + self._parsed_version = parse_version(_forgiving_version(self.version)) + + notes = "\n".join(getattr(ex, "__notes__", [])) # PEP 678 + msg = f"""!!\n\n + ************************************************************************* + {str(ex)}\n{notes} + + This is a long overdue deprecation. + For the time being, `pkg_resources` will use `{self._parsed_version}` + as a replacement to avoid breaking existing environments, + but no future compatibility is guaranteed. + + If you maintain package {self.project_name} you should implement + the relevant changes to adequate the project to PEP 440 immediately. + ************************************************************************* + \n\n!! + """ + warnings.warn(msg, DeprecationWarning) + + return self._parsed_version + + @property + def version(self): + try: + return self._version + except AttributeError as e: + 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) from e + + 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 as e: + raise UnknownExtra( + "%s has no such extra feature %r" % (self, ext) + ) from e + 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): + yield from self.get_metadata_lines(name) + + def _get_version(self): + lines = self._get_metadata(self.PKG_INFO) + return _version_from_file(lines) + + 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().__dir__()) + | set(attr for attr in self._provider.__dir__() if not attr.startswith('_')) + ) + + @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) + + # FIXME: 'Distribution.insert_on' is too complex (13) + def insert_on(self, path, loc=None, replace=False): # noqa: C901 + """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 + except SystemError: + # TODO: remove this except clause when python/cpython#103632 is fixed. + 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 = types.MappingProxyType(dict.fromkeys(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] = [r for r in reqs_for_extra(extra) if r not in 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) + + +def parse_requirements(strs): + """ + Yield ``Requirement`` objects for each specification in `strs`. + + `strs` must be a string, or a (possibly-nested) iterable thereof. + """ + return map(Requirement, join_continuation(map(drop_comment, yield_lines(strs)))) + + +class RequirementParseError(packaging.requirements.InvalidRequirement): + "Compatibility wrapper for InvalidRequirement" + + +class Requirement(packaging.requirements.Requirement): + def __init__(self, requirement_string): + """DO NOT CALL THIS UNDOCUMENTED METHOD; use Requirement.parse()!""" + super().__init__(requirement_string) + 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] + # _find_adapter would previously return None, and immediately be called. + # So we're raising a TypeError to keep backward compatibility if anyone depended on that behaviour. + raise TypeError(f"Could not find adapter for {registry} and {ob}") + + +def ensure_directory(path): + """Ensure that the parent directory of `path` exists""" + dirname = os.path.dirname(path) + os.makedirs(dirname, exist_ok=True) + + +def _bypass_ensure_directory(path): + """Sandbox-bypassing version of ensure_directory()""" + if not WRITE_SUPPORT: + raise OSError('"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('_') + ) + + +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. + """ + + +@_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()) diff --git a/venv/Lib/site-packages/pkg_resources/__pycache__/__init__.cpython-312.pyc b/venv/Lib/site-packages/pkg_resources/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..43cc34fd78c16465fafd4a0d3ad5aaabf48da7f0 GIT binary patch literal 146172 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 xuNwca*#@mF<`#aRv3gf;`FWnGZy5Z)Lc-fw~ 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 z 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 RH`7o~@BQAN-RDO7{{UXp43Yo< literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/pkg_resources/_vendor/__pycache__/zipp.cpython-312.pyc b/venv/Lib/site-packages/pkg_resources/_vendor/__pycache__/zipp.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..8e4b30d9646f38492cff613cb677d66b07662497 GIT binary patch literal 14887 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 literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/pkg_resources/_vendor/backports/__init__.py b/venv/Lib/site-packages/pkg_resources/_vendor/backports/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/venv/Lib/site-packages/pkg_resources/_vendor/backports/__pycache__/__init__.cpython-312.pyc b/venv/Lib/site-packages/pkg_resources/_vendor/backports/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..e37083c870f2aa94f4cf6b8ddbdc9afc3be05481 GIT binary patch literal 217 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 literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/pkg_resources/_vendor/backports/tarfile.py b/venv/Lib/site-packages/pkg_resources/_vendor/backports/tarfile.py new file mode 100644 index 0000000..a7a9a6e --- /dev/null +++ b/venv/Lib/site-packages/pkg_resources/_vendor/backports/tarfile.py @@ -0,0 +1,2900 @@ +#!/usr/bin/env python3 +#------------------------------------------------------------------- +# 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. +# +"""Read from and write to tar format archives. +""" + +version = "0.9.0" +__author__ = "Lars Gust\u00e4bel (lars@gustaebel.de)" +__credits__ = "Gustavo Niemeyer, Niels Gust\u00e4bel, Richard Townsend." + +#--------- +# Imports +#--------- +from builtins import open as bltn_open +import sys +import os +import io +import shutil +import stat +import time +import struct +import copy +import re +import warnings + +try: + import pwd +except ImportError: + pwd = None +try: + import grp +except ImportError: + grp = None + +# os.symlink on Windows prior to 6.0 raises NotImplementedError +# OSError (winerror=1314) will be raised if the caller does not hold the +# SeCreateSymbolicLinkPrivilege privilege +symlink_exception = (AttributeError, NotImplementedError, OSError) + +# from tarfile import * +__all__ = ["TarFile", "TarInfo", "is_tarfile", "TarError", "ReadError", + "CompressionError", "StreamError", "ExtractError", "HeaderError", + "ENCODING", "USTAR_FORMAT", "GNU_FORMAT", "PAX_FORMAT", + "DEFAULT_FORMAT", "open","fully_trusted_filter", "data_filter", + "tar_filter", "FilterError", "AbsoluteLinkError", + "OutsideDestinationError", "SpecialFileError", "AbsolutePathError", + "LinkOutsideDestinationError"] + + +#--------------------------------------------------------- +# 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 = PAX_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 = {"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 +} + +#--------------------------------------------------------- +# initialization +#--------------------------------------------------------- +if os.name == "nt": + 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. + """ + if s is None: + raise ValueError("metadata cannot contain None") + 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] in (0o200, 0o377): + n = 0 + for i in range(len(s) - 1): + n <<= 8 + n += s[i + 1] + if s[0] == 0o377: + n = -(256 ** (len(s) - 1) - n) + else: + try: + s = nts(s, "ascii", "strict") + n = int(s.strip() or "0", 8) + except ValueError: + raise InvalidHeaderError("invalid header") + 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 or 0o377 byte indicate this + # particular encoding, the following digits-1 bytes are a big-endian + # base-256 representation. This allows values up to (256**(digits-1))-1. + # A 0o200 byte indicates a positive number, a 0o377 byte a negative + # number. + original_n = n + n = int(n) + if 0 <= n < 8 ** (digits - 1): + s = bytes("%0*o" % (digits - 1, n), "ascii") + NUL + elif format == GNU_FORMAT and -256 ** (digits - 1) <= n < 256 ** (digits - 1): + if n >= 0: + s = bytearray([0o200]) + else: + s = bytearray([0o377]) + n = 256 ** digits + n + + for i in range(digits - 1): + s.insert(1, n & 0o377) + n >>= 8 + else: + raise ValueError("overflow in number field") + + 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_from("148B8x356B", buf)) + signed_chksum = 256 + sum(struct.unpack_from("148b8x356b", buf)) + return unsigned_chksum, signed_chksum + +def copyfileobj(src, dst, length=None, exception=OSError, bufsize=None): + """Copy length bytes from fileobj src to fileobj dst. + If length is None, copy the entire content. + """ + bufsize = bufsize or 16 * 1024 + if length == 0: + return + if length is None: + shutil.copyfileobj(src, dst, bufsize) + return + + blocks, remainder = divmod(length, bufsize) + for b in range(blocks): + buf = src.read(bufsize) + if len(buf) < bufsize: + raise exception("unexpected end of data") + dst.write(buf) + + if remainder != 0: + buf = src.read(remainder) + if len(buf) < remainder: + raise exception("unexpected end of data") + dst.write(buf) + return + +def _safe_print(s): + encoding = getattr(sys.stdout, 'encoding', None) + if encoding is not None: + s = s.encode(encoding, 'backslashreplace').decode(encoding) + print(s, end=' ') + + +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: + """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: + """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 that works with bytes, + and the method is accessed blockwise. + Use of gzip or bzip2 compression is possible. + A stream-like object could be for example: sys.stdin.buffer, + sys.stdout.buffer, a socket, a tape device etc. + + _Stream is intended to be used only internally. + """ + + def __init__(self, name, mode, comptype, fileobj, bufsize, + compresslevel): + """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") from None + self.zlib = zlib + self.crc = zlib.crc32(b"") + if mode == "r": + self.exception = zlib.error + self._init_read_gz() + else: + self._init_write_gz(compresslevel) + + elif comptype == "bz2": + try: + import bz2 + except ImportError: + raise CompressionError("bz2 module is not available") from None + if mode == "r": + self.dbuf = b"" + self.cmp = bz2.BZ2Decompressor() + self.exception = OSError + else: + self.cmp = bz2.BZ2Compressor(compresslevel) + + elif comptype == "xz": + try: + import lzma + except ImportError: + raise CompressionError("lzma module is not available") from None + if mode == "r": + self.dbuf = b"" + self.cmp = lzma.LZMADecompressor() + self.exception = lzma.LZMAError + else: + self.cmp = lzma.LZMACompressor() + + elif comptype != "tar": + raise CompressionError("unknown compression type %r" % comptype) + + 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, compresslevel): + """Initialize for writing with gzip compression. + """ + self.cmp = self.zlib.compressobj(compresslevel, + 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 + + self.closed = True + try: + 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": + 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): + """Return the next size number of bytes from the stream.""" + assert size is not None + 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) + t = [self.dbuf] + while c < size: + # Skip underlying buffer to avoid unaligned double buffering. + if self.buf: + buf = self.buf + self.buf = b"" + else: + buf = self.fileobj.read(self.bufsize) + if not buf: + break + try: + buf = self.cmp.decompress(buf) + except self.exception as e: + raise ReadError("invalid compressed data") from e + t.append(buf) + c += len(buf) + t = b"".join(t) + self.dbuf = t[size:] + return t[:size] + + def __read(self, size): + """Return size bytes from stream. If internal buffer is empty, + read another block from the stream. + """ + c = len(self.buf) + t = [self.buf] + while c < size: + buf = self.fileobj.read(self.bufsize) + if not buf: + break + t.append(buf) + c += len(buf) + t = b"".join(t) + self.buf = t[size:] + return t[:size] +# 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"\x1f\x8b\x08"): + return "gz" + elif self.buf[0:3] == b"BZh" and self.buf[4:10] == b"1AY&SY": + return "bz2" + elif self.buf.startswith((b"\x5d\x00\x00\x80", b"\xfd7zXZ")): + return "xz" + else: + return "tar" + + def close(self): + self.fileobj.close() +# class StreamProxy + +#------------------------ +# 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, name, blockinfo=None): + self.fileobj = fileobj + self.offset = offset + self.size = size + self.position = 0 + self.name = name + self.closed = False + + 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 flush(self): + pass + + def readable(self): + return True + + def writable(self): + return False + + def seekable(self): + return self.fileobj.seekable() + + def tell(self): + """Return the current file position. + """ + return self.position + + def seek(self, position, whence=io.SEEK_SET): + """Seek to a position in the file. + """ + if whence == io.SEEK_SET: + self.position = min(max(position, 0), self.size) + elif whence == io.SEEK_CUR: + if position < 0: + self.position = max(self.position + position, 0) + else: + self.position = min(self.position + position, self.size) + elif whence == io.SEEK_END: + self.position = max(min(self.size + position, self.size), 0) + else: + raise ValueError("Invalid argument") + return self.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)) + b = self.fileobj.read(length) + if len(b) != length: + raise ReadError("unexpected end of data") + buf += b + else: + buf += NUL * length + size -= length + self.position += length + return buf + + def readinto(self, b): + buf = self.read(len(b)) + b[:len(buf)] = buf + return len(buf) + + def close(self): + self.closed = True +#class _FileInFile + +class ExFileObject(io.BufferedReader): + + def __init__(self, tarfile, tarinfo): + fileobj = _FileInFile(tarfile.fileobj, tarinfo.offset_data, + tarinfo.size, tarinfo.name, tarinfo.sparse) + super().__init__(fileobj) +#class ExFileObject + + +#----------------------------- +# extraction filters (PEP 706) +#----------------------------- + +class FilterError(TarError): + pass + +class AbsolutePathError(FilterError): + def __init__(self, tarinfo): + self.tarinfo = tarinfo + super().__init__(f'member {tarinfo.name!r} has an absolute path') + +class OutsideDestinationError(FilterError): + def __init__(self, tarinfo, path): + self.tarinfo = tarinfo + self._path = path + super().__init__(f'{tarinfo.name!r} would be extracted to {path!r}, ' + + 'which is outside the destination') + +class SpecialFileError(FilterError): + def __init__(self, tarinfo): + self.tarinfo = tarinfo + super().__init__(f'{tarinfo.name!r} is a special file') + +class AbsoluteLinkError(FilterError): + def __init__(self, tarinfo): + self.tarinfo = tarinfo + super().__init__(f'{tarinfo.name!r} is a link to an absolute path') + +class LinkOutsideDestinationError(FilterError): + def __init__(self, tarinfo, path): + self.tarinfo = tarinfo + self._path = path + super().__init__(f'{tarinfo.name!r} would link to {path!r}, ' + + 'which is outside the destination') + +def _get_filtered_attrs(member, dest_path, for_data=True): + new_attrs = {} + name = member.name + dest_path = os.path.realpath(dest_path) + # Strip leading / (tar's directory separator) from filenames. + # Include os.sep (target OS directory separator) as well. + if name.startswith(('/', os.sep)): + name = new_attrs['name'] = member.path.lstrip('/' + os.sep) + if os.path.isabs(name): + # Path is absolute even after stripping. + # For example, 'C:/foo' on Windows. + raise AbsolutePathError(member) + # Ensure we stay in the destination + target_path = os.path.realpath(os.path.join(dest_path, name)) + if os.path.commonpath([target_path, dest_path]) != dest_path: + raise OutsideDestinationError(member, target_path) + # Limit permissions (no high bits, and go-w) + mode = member.mode + if mode is not None: + # Strip high bits & group/other write bits + mode = mode & 0o755 + if for_data: + # For data, handle permissions & file types + if member.isreg() or member.islnk(): + if not mode & 0o100: + # Clear executable bits if not executable by user + mode &= ~0o111 + # Ensure owner can read & write + mode |= 0o600 + elif member.isdir() or member.issym(): + # Ignore mode for directories & symlinks + mode = None + else: + # Reject special files + raise SpecialFileError(member) + if mode != member.mode: + new_attrs['mode'] = mode + if for_data: + # Ignore ownership for 'data' + if member.uid is not None: + new_attrs['uid'] = None + if member.gid is not None: + new_attrs['gid'] = None + if member.uname is not None: + new_attrs['uname'] = None + if member.gname is not None: + new_attrs['gname'] = None + # Check link destination for 'data' + if member.islnk() or member.issym(): + if os.path.isabs(member.linkname): + raise AbsoluteLinkError(member) + if member.issym(): + target_path = os.path.join(dest_path, + os.path.dirname(name), + member.linkname) + else: + target_path = os.path.join(dest_path, + member.linkname) + target_path = os.path.realpath(target_path) + if os.path.commonpath([target_path, dest_path]) != dest_path: + raise LinkOutsideDestinationError(member, target_path) + return new_attrs + +def fully_trusted_filter(member, dest_path): + return member + +def tar_filter(member, dest_path): + new_attrs = _get_filtered_attrs(member, dest_path, False) + if new_attrs: + return member.replace(**new_attrs, deep=False) + return member + +def data_filter(member, dest_path): + new_attrs = _get_filtered_attrs(member, dest_path, True) + if new_attrs: + return member.replace(**new_attrs, deep=False) + return member + +_NAMED_FILTERS = { + "fully_trusted": fully_trusted_filter, + "tar": tar_filter, + "data": data_filter, +} + +#------------------ +# Exported Classes +#------------------ + +# Sentinel for replace() defaults, meaning "don't change the attribute" +_KEEP = object() + +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__ = dict( + name = 'Name of the archive member.', + mode = 'Permission bits.', + uid = 'User ID of the user who originally stored this member.', + gid = 'Group ID of the user who originally stored this member.', + size = 'Size in bytes.', + mtime = 'Time of last modification.', + chksum = 'Header checksum.', + type = ('File type. type is usually one of these constants: ' + 'REGTYPE, AREGTYPE, LNKTYPE, SYMTYPE, DIRTYPE, FIFOTYPE, ' + 'CONTTYPE, CHRTYPE, BLKTYPE, GNUTYPE_SPARSE.'), + linkname = ('Name of the target file name, which is only present ' + 'in TarInfo objects of type LNKTYPE and SYMTYPE.'), + uname = 'User name.', + gname = 'Group name.', + devmajor = 'Device major number.', + devminor = 'Device minor number.', + offset = 'The tar header starts here.', + offset_data = "The file's data starts here.", + pax_headers = ('A dictionary containing key-value pairs of an ' + 'associated pax extended header.'), + sparse = 'Sparse member information.', + tarfile = None, + _sparse_structs = None, + _link_target = None, + ) + + 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 + + @property + def path(self): + 'In pax headers, "name" is called "path".' + return self.name + + @path.setter + def path(self, name): + self.name = name + + @property + def linkpath(self): + 'In pax headers, "linkname" is called "linkpath".' + return self.linkname + + @linkpath.setter + def linkpath(self, linkname): + self.linkname = linkname + + def __repr__(self): + return "<%s %r at %#x>" % (self.__class__.__name__,self.name,id(self)) + + def replace(self, *, + name=_KEEP, mtime=_KEEP, mode=_KEEP, linkname=_KEEP, + uid=_KEEP, gid=_KEEP, uname=_KEEP, gname=_KEEP, + deep=True, _KEEP=_KEEP): + """Return a deep copy of self with the given attributes replaced. + """ + if deep: + result = copy.deepcopy(self) + else: + result = copy.copy(self) + if name is not _KEEP: + result.name = name + if mtime is not _KEEP: + result.mtime = mtime + if mode is not _KEEP: + result.mode = mode + if linkname is not _KEEP: + result.linkname = linkname + if uid is not _KEEP: + result.uid = uid + if gid is not _KEEP: + result.gid = gid + if uname is not _KEEP: + result.uname = uname + if gname is not _KEEP: + result.gname = gname + return result + + def get_info(self): + """Return the TarInfo's attributes as a dictionary. + """ + if self.mode is None: + mode = None + else: + mode = self.mode & 0o7777 + info = { + "name": self.name, + "mode": mode, + "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() + for name, value in info.items(): + if value is None: + raise ValueError("%s may not be None" % name) + + 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"].encode(encoding, errors)) > LENGTH_LINK: + raise ValueError("linkname is too long") + + if len(info["name"].encode(encoding, errors)) > LENGTH_NAME: + info["prefix"], info["name"] = self._posix_split_name(info["name"], encoding, errors) + + 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"].encode(encoding, errors)) > LENGTH_LINK: + buf += self._create_gnu_long_header(info["linkname"], GNUTYPE_LONGLINK, encoding, errors) + + if len(info["name"].encode(encoding, errors)) > 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)): + needs_pax = False + + val = info[name] + val_is_float = isinstance(val, float) + val_int = round(val) if val_is_float else val + if not 0 <= val_int < 8 ** (digits - 1): + # Avoid overflow. + info[name] = 0 + needs_pax = True + elif val_is_float: + # Put rounded value in ustar header, and full + # precision value in pax header. + info[name] = val_int + needs_pax = True + + # The existing pax header has priority. + if needs_pax and name not in pax_headers: + pax_headers[name] = str(val) + + # 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, "utf-8") + + def _posix_split_name(self, name, encoding, errors): + """Split a name longer than 100 chars into a prefix + and a name part. + """ + components = name.split("/") + for i in range(1, len(components)): + prefix = "/".join(components[:i]) + name = "/".join(components[i:]) + if len(prefix.encode(encoding, errors)) <= LENGTH_PREFIX and \ + len(name.encode(encoding, errors)) <= LENGTH_NAME: + break + else: + 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. + """ + has_device_fields = info.get("type") in (CHRTYPE, BLKTYPE) + if has_device_fields: + devmajor = itn(info.get("devmajor", 0), 8, format) + devminor = itn(info.get("devminor", 0), 8, format) + else: + devmajor = stn("", 8, encoding, errors) + devminor = stn("", 8, encoding, errors) + + # None values in metadata should cause ValueError. + # itn()/stn() do this for all fields except type. + filetype = info.get("type", REGTYPE) + if filetype is None: + raise ValueError("TarInfo.type must not be None") + + 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 + filetype, + 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), + devmajor, + devminor, + stn(info.get("prefix", ""), 155, encoding, errors) + ] + + buf = struct.pack("%ds" % BLOCKSIZE, b"".join(parts)) + chksum = calc_chksums(buf[-BLOCKSIZE:])[0] + buf = buf[:-364] + bytes("%06o\0" % chksum, "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("utf-8", "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("utf-8") + 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("utf-8") + + 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 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) + + # Remove redundant slashes from directories. This is to be consistent + # with frombuf(). + if self.isdir(): + self.name = self.name.rstrip("/") + + 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 as e: + raise SubsequentHeaderError(str(e)) from None + + # 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) + + # Remove redundant slashes from directories. This is to be consistent + # with frombuf(). + if next.isdir(): + next.name = next.name.removesuffix("/") + + 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("utf-8") + + # 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 = "utf-8" + + # 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 match := regex.match(buf, pos): + length, keyword = match.groups() + length = int(length) + if length == 0: + raise InvalidHeaderError("invalid header") + value = buf[match.end(2) + 1:match.start(1) + length - 1] + + # Normally, we could just use "utf-8" 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, "utf-8", "utf-8", + 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, "utf-8", "utf-8", + tarfile.errors) + + pax_headers[keyword] = value + pos += length + + # Fetch the next header. + try: + next = self.fromtarfile(tarfile) + except HeaderError as e: + raise SubsequentHeaderError(str(e)) from None + + # 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 True if the Tarinfo object is a regular file.' + return self.type in REGULAR_TYPES + + def isfile(self): + 'Return True if the Tarinfo object is a regular file.' + return self.isreg() + + def isdir(self): + 'Return True if it is a directory.' + return self.type == DIRTYPE + + def issym(self): + 'Return True if it is a symbolic link.' + return self.type == SYMTYPE + + def islnk(self): + 'Return True if it is a hard link.' + return self.type == LNKTYPE + + def ischr(self): + 'Return True if it is a character device.' + return self.type == CHRTYPE + + def isblk(self): + 'Return True if it is a block device.' + return self.type == BLKTYPE + + def isfifo(self): + 'Return True if it is a FIFO.' + return self.type == FIFOTYPE + + def issparse(self): + return self.sparse is not None + + def isdev(self): + 'Return True if it is one of character device, block device or FIFO.' + 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 file-object for extractfile(). + + extraction_filter = None # The default filter for extraction. + + 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, copybufsize=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. + """ + modes = {"r": "rb", "a": "r+b", "w": "wb", "x": "xb"} + if mode not in modes: + raise ValueError("mode must be 'r', 'a', 'w' or 'x'") + self.mode = mode + self._mode = modes[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") and + isinstance(fileobj.name, (str, bytes))): + 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.copybufsize = copybufsize + 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)) from None + + if self.mode in ("a", "w", "x"): + 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): + r"""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 + 'r:xz' open for reading with lzma 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 + 'w:xz' open for writing with lzma compression + + 'x' or 'x:' create a tarfile exclusively without compression, raise + an exception if the file is already created + 'x:gz' create a gzip compressed tarfile, raise an exception + if the file is already created + 'x:bz2' create a bzip2 compressed tarfile, raise an exception + if the file is already created + 'x:xz' create an lzma compressed tarfile, raise an exception + if the file is already created + + '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 + 'r|xz' open an lzma 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 + 'w|xz' open an lzma 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. + def not_compressed(comptype): + return cls.OPEN_METH[comptype] == 'taropen' + error_msgs = [] + for comptype in sorted(cls.OPEN_METH, key=not_compressed): + 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: + error_msgs.append(f'- method {comptype}: {e!r}') + if fileobj is not None: + fileobj.seek(saved_pos) + continue + error_msgs_summary = '\n'.join(error_msgs) + raise ReadError(f"file could not be opened successfully:\n{error_msgs_summary}") + + 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 ("r", "w"): + raise ValueError("mode must be 'r' or 'w'") + + compresslevel = kwargs.pop("compresslevel", 9) + stream = _Stream(name, filemode, comptype, fileobj, bufsize, + compresslevel) + try: + t = cls(name, filemode, stream, **kwargs) + except: + stream.close() + raise + t._extfileobj = False + return t + + elif mode in ("a", "w", "x"): + 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 mode not in ("r", "a", "w", "x"): + raise ValueError("mode must be 'r', 'a', 'w' or 'x'") + 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 mode not in ("r", "w", "x"): + raise ValueError("mode must be 'r', 'w' or 'x'") + + try: + from gzip import GzipFile + except ImportError: + raise CompressionError("gzip module is not available") from None + + try: + fileobj = GzipFile(name, mode + "b", compresslevel, fileobj) + except OSError as e: + if fileobj is not None and mode == 'r': + raise ReadError("not a gzip file") from e + raise + + try: + t = cls.taropen(name, mode, fileobj, **kwargs) + except OSError as e: + fileobj.close() + if mode == 'r': + raise ReadError("not a gzip file") from e + raise + except: + fileobj.close() + raise + t._extfileobj = False + 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 mode not in ("r", "w", "x"): + raise ValueError("mode must be 'r', 'w' or 'x'") + + try: + from bz2 import BZ2File + except ImportError: + raise CompressionError("bz2 module is not available") from None + + fileobj = BZ2File(fileobj or name, mode, compresslevel=compresslevel) + + try: + t = cls.taropen(name, mode, fileobj, **kwargs) + except (OSError, EOFError) as e: + fileobj.close() + if mode == 'r': + raise ReadError("not a bzip2 file") from e + raise + except: + fileobj.close() + raise + t._extfileobj = False + return t + + @classmethod + def xzopen(cls, name, mode="r", fileobj=None, preset=None, **kwargs): + """Open lzma compressed tar archive name for reading or writing. + Appending is not allowed. + """ + if mode not in ("r", "w", "x"): + raise ValueError("mode must be 'r', 'w' or 'x'") + + try: + from lzma import LZMAFile, LZMAError + except ImportError: + raise CompressionError("lzma module is not available") from None + + fileobj = LZMAFile(fileobj or name, mode, preset=preset) + + try: + t = cls.taropen(name, mode, fileobj, **kwargs) + except (LZMAError, EOFError) as e: + fileobj.close() + if mode == 'r': + raise ReadError("not an lzma file") from e + raise + except: + fileobj.close() + raise + 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 + "xz": "xzopen" # lzma 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 + + self.closed = True + try: + if self.mode in ("a", "w", "x"): + 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)) + finally: + if not self._extfileobj: + self.fileobj.close() + + 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.rstrip('/')) + 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 from the result of os.stat or equivalent + on an existing file. The file is either named by ``name``, or + specified as a file object ``fileobj`` with a file descriptor. If + given, ``arcname`` specifies an alternative name for the file in the + archive, otherwise, the name is taken from the 'name' attribute of + 'fileobj', or the 'name' argument. The name should be a text + string. + """ + self._check("awx") + + # 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 # Not needed + + # Use os.stat or os.lstat, depending on if symlinks shall be resolved. + if fileobj is None: + if 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, *, members=None): + """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. ``members`` is optional and must be a subset of the + list returned by getmembers(). + """ + self._check() + + if members is None: + members = self + for tarinfo in members: + if verbose: + if tarinfo.mode is None: + _safe_print("??????????") + else: + _safe_print(stat.filemode(tarinfo.mode)) + _safe_print("%s/%s" % (tarinfo.uname or tarinfo.uid, + tarinfo.gname or tarinfo.gid)) + if tarinfo.ischr() or tarinfo.isblk(): + _safe_print("%10s" % + ("%d,%d" % (tarinfo.devmajor, tarinfo.devminor))) + else: + _safe_print("%10d" % tarinfo.size) + if tarinfo.mtime is None: + _safe_print("????-??-?? ??:??:??") + else: + _safe_print("%d-%02d-%02d %02d:%02d:%02d" \ + % time.localtime(tarinfo.mtime)[:6]) + + _safe_print(tarinfo.name + ("/" if tarinfo.isdir() else "")) + + if verbose: + if tarinfo.issym(): + _safe_print("-> " + tarinfo.linkname) + if tarinfo.islnk(): + _safe_print("link to " + tarinfo.linkname) + print() + + def add(self, name, arcname=None, recursive=True, *, 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. ``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("awx") + + if arcname is None: + arcname = name + + # 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(): + with bltn_open(name, "rb") as f: + self.addfile(tarinfo, f) + + elif tarinfo.isdir(): + self.addfile(tarinfo) + if recursive: + for f in sorted(os.listdir(name)): + self.add(os.path.join(name, f), os.path.join(arcname, f), + recursive, filter=filter) + + else: + self.addfile(tarinfo) + + def addfile(self, tarinfo, fileobj=None): + """Add the TarInfo object ``tarinfo`` to the archive. If ``fileobj`` is + given, it should be a binary file, and tarinfo.size bytes are read + from it and added to the archive. You can create TarInfo objects + directly, or by using gettarinfo(). + """ + self._check("awx") + + tarinfo = copy.copy(tarinfo) + + buf = tarinfo.tobuf(self.format, self.encoding, self.errors) + self.fileobj.write(buf) + self.offset += len(buf) + bufsize=self.copybufsize + # If there's data to follow, append it. + if fileobj is not None: + copyfileobj(fileobj, self.fileobj, tarinfo.size, bufsize=bufsize) + 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 _get_filter_function(self, filter): + if filter is None: + filter = self.extraction_filter + if filter is None: + warnings.warn( + 'Python 3.14 will, by default, filter extracted tar ' + + 'archives and reject files or modify their metadata. ' + + 'Use the filter argument to control this behavior.', + DeprecationWarning) + return fully_trusted_filter + if isinstance(filter, str): + raise TypeError( + 'String names are not supported for ' + + 'TarFile.extraction_filter. Use a function such as ' + + 'tarfile.data_filter directly.') + return filter + if callable(filter): + return filter + try: + return _NAMED_FILTERS[filter] + except KeyError: + raise ValueError(f"filter {filter!r} not found") from None + + def extractall(self, path=".", members=None, *, numeric_owner=False, + filter=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(). If `numeric_owner` is True, only + the numbers for user/group names are used and not the names. + + The `filter` function will be called on each member just + before extraction. + It can return a changed TarInfo or None to skip the member. + String names of common filters are accepted. + """ + directories = [] + + filter_function = self._get_filter_function(filter) + if members is None: + members = self + + for member in members: + tarinfo = self._get_extract_tarinfo(member, filter_function, path) + if tarinfo is None: + continue + if tarinfo.isdir(): + # For directories, delay setting attributes until later, + # since permissions can interfere with extraction and + # extracting contents can reset mtime. + directories.append(tarinfo) + self._extract_one(tarinfo, path, set_attrs=not tarinfo.isdir(), + numeric_owner=numeric_owner) + + # Reverse sort directories. + directories.sort(key=lambda a: a.name, reverse=True) + + # Set correct owner, mtime and filemode on directories. + for tarinfo in directories: + dirpath = os.path.join(path, tarinfo.name) + try: + self.chown(tarinfo, dirpath, numeric_owner=numeric_owner) + self.utime(tarinfo, dirpath) + self.chmod(tarinfo, dirpath) + except ExtractError as e: + self._handle_nonfatal_error(e) + + def extract(self, member, path="", set_attrs=True, *, numeric_owner=False, + filter=None): + """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. If `numeric_owner` + is True, only the numbers for user/group names are used and not + the names. + + The `filter` function will be called before extraction. + It can return a changed TarInfo or None to skip the member. + String names of common filters are accepted. + """ + filter_function = self._get_filter_function(filter) + tarinfo = self._get_extract_tarinfo(member, filter_function, path) + if tarinfo is not None: + self._extract_one(tarinfo, path, set_attrs, numeric_owner) + + def _get_extract_tarinfo(self, member, filter_function, path): + """Get filtered TarInfo (or None) from member, which might be a str""" + if isinstance(member, str): + tarinfo = self.getmember(member) + else: + tarinfo = member + + unfiltered = tarinfo + try: + tarinfo = filter_function(tarinfo, path) + except (OSError, FilterError) as e: + self._handle_fatal_error(e) + except ExtractError as e: + self._handle_nonfatal_error(e) + if tarinfo is None: + self._dbg(2, "tarfile: Excluded %r" % unfiltered.name) + return None + # Prepare the link target for makelink(). + if tarinfo.islnk(): + tarinfo = copy.copy(tarinfo) + tarinfo._link_target = os.path.join(path, tarinfo.linkname) + return tarinfo + + def _extract_one(self, tarinfo, path, set_attrs, numeric_owner): + """Extract from filtered tarinfo to disk""" + self._check("r") + + try: + self._extract_member(tarinfo, os.path.join(path, tarinfo.name), + set_attrs=set_attrs, + numeric_owner=numeric_owner) + except OSError as e: + self._handle_fatal_error(e) + except ExtractError as e: + self._handle_nonfatal_error(e) + + def _handle_nonfatal_error(self, e): + """Handle non-fatal error (ExtractError) according to errorlevel""" + if self.errorlevel > 1: + raise + else: + self._dbg(1, "tarfile: %s" % e) + + def _handle_fatal_error(self, e): + """Handle "fatal" error according to self.errorlevel""" + if self.errorlevel > 0: + raise + elif isinstance(e, OSError): + if e.filename is None: + self._dbg(1, "tarfile: %s" % e.strerror) + else: + self._dbg(1, "tarfile: %s %r" % (e.strerror, e.filename)) + else: + self._dbg(1, "tarfile: %s %s" % (type(e).__name__, 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 or + a link, an io.BufferedReader object is returned. For all other + existing members, None is returned. If ``member`` does not appear + in the archive, KeyError is raised. + """ + self._check("r") + + if isinstance(member, str): + tarinfo = self.getmember(member) + else: + tarinfo = member + + if tarinfo.isreg() or tarinfo.type not in SUPPORTED_TYPES: + # Members with unknown types are treated as regular files. + 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, + numeric_owner=False): + """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, numeric_owner) + 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: + if tarinfo.mode is None: + # Use the system's default mode + os.mkdir(targetpath) + else: + # Use a safe mode for the directory, the real mode is set + # later in _extract_member(). + os.mkdir(targetpath, 0o700) + except FileExistsError: + if not os.path.isdir(targetpath): + raise + + def makefile(self, tarinfo, targetpath): + """Make a file called targetpath. + """ + source = self.fileobj + source.seek(tarinfo.offset_data) + bufsize = self.copybufsize + with bltn_open(targetpath, "wb") as target: + if tarinfo.sparse is not None: + for offset, size in tarinfo.sparse: + target.seek(offset) + copyfileobj(source, target, size, ReadError, bufsize) + target.seek(tarinfo.size) + target.truncate() + else: + copyfileobj(source, target, tarinfo.size, ReadError, bufsize) + + 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 mode is None: + # Use mknod's default + mode = 0o600 + 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(): + if os.path.lexists(targetpath): + # Avoid FileExistsError on following os.symlink. + os.unlink(targetpath) + os.symlink(tarinfo.linkname, targetpath) + else: + 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: + try: + self._extract_member(self._find_link_target(tarinfo), + targetpath) + except KeyError: + raise ExtractError("unable to resolve link inside archive") from None + + def chown(self, tarinfo, targetpath, numeric_owner): + """Set owner of targetpath according to tarinfo. If numeric_owner + is True, use .gid/.uid instead of .gname/.uname. If numeric_owner + is False, fall back to .gid/.uid when the search based on name + fails. + """ + if hasattr(os, "geteuid") and os.geteuid() == 0: + # We have to be root to do so. + g = tarinfo.gid + u = tarinfo.uid + if not numeric_owner: + try: + if grp and tarinfo.gname: + g = grp.getgrnam(tarinfo.gname)[2] + except KeyError: + pass + try: + if pwd and tarinfo.uname: + u = pwd.getpwnam(tarinfo.uname)[2] + except KeyError: + pass + if g is None: + g = -1 + if u is None: + u = -1 + try: + if tarinfo.issym() and hasattr(os, "lchown"): + os.lchown(targetpath, u, g) + else: + os.chown(targetpath, u, g) + except OSError as e: + raise ExtractError("could not change owner") from e + + def chmod(self, tarinfo, targetpath): + """Set file permissions of targetpath according to tarinfo. + """ + if tarinfo.mode is None: + return + try: + os.chmod(targetpath, tarinfo.mode) + except OSError as e: + raise ExtractError("could not change mode") from e + + def utime(self, tarinfo, targetpath): + """Set modification time of targetpath according to tarinfo. + """ + mtime = tarinfo.mtime + if mtime is None: + return + if not hasattr(os, 'utime'): + return + try: + os.utime(targetpath, (mtime, mtime)) + except OSError as e: + raise ExtractError("could not change modification time") from e + + #-------------------------------------------------------------------------- + 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 + + # Advance the file pointer. + if self.offset != self.fileobj.tell(): + if self.offset == 0: + return None + self.fileobj.seek(self.offset - 1) + if not self.fileobj.read(1): + raise ReadError("unexpected end of data") + + # Read the next block. + 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)) from None + except EmptyHeaderError: + if self.offset == 0: + raise ReadError("empty file") from None + except TruncatedHeaderError as e: + if self.offset == 0: + raise ReadError(str(e)) from None + except SubsequentHeaderError as e: + raise ReadError(str(e)) from None + except Exception as e: + try: + import zlib + if isinstance(e, zlib.error): + raise ReadError(f'zlib error: {e}') from None + else: + raise e + except ImportError: + raise 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. + skipping = False + if tarinfo is not None: + try: + index = members.index(tarinfo) + except ValueError: + # The given starting point might be a (modified) copy. + # We'll later skip members until we find an equivalent. + skipping = True + else: + # Happy fast path + members = members[:index] + + if normalize: + name = os.path.normpath(name) + + for member in reversed(members): + if skipping: + if tarinfo.offset == member.offset: + skipping = False + continue + if normalize: + member_name = os.path.normpath(member.name) + else: + member_name = member.name + + if name == member_name: + return member + + if skipping: + # Starting point was not found + raise ValueError(tarinfo) + + def _load(self): + """Read through the entire archive file and look for readable + members. + """ + while self.next() is not None: + pass + 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 OSError("%s is closed" % self.__class__.__name__) + if mode is not None and self.mode not in mode: + raise OSError("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 = "/".join(filter(None, (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: + yield from self.members + return + + # Yield items using TarFile's next() method. + # When all members have been read, set TarFile as _loaded. + index = 0 + # Fix for SF #1100429: Under rare circumstances it can + # happen that getmembers() is called during iteration, + # which will have already exhausted the next() method. + if self.firstmember is not None: + tarinfo = self.next() + index += 1 + yield tarinfo + + while True: + if index < len(self.members): + tarinfo = self.members[index] + elif not self._loaded: + tarinfo = self.next() + if not tarinfo: + self._loaded = True + return + else: + return + index += 1 + yield tarinfo + + 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 + +#-------------------- +# exported functions +#-------------------- + +def is_tarfile(name): + """Return True if name points to a tar archive that we + are able to handle, else return False. + + 'name' should be a string, file, or file-like object. + """ + try: + if hasattr(name, "read"): + pos = name.tell() + t = open(fileobj=name) + name.seek(pos) + else: + t = open(name) + t.close() + return True + except TarError: + return False + +open = TarFile.open + + +def main(): + import argparse + + description = 'A simple command-line interface for tarfile module.' + parser = argparse.ArgumentParser(description=description) + parser.add_argument('-v', '--verbose', action='store_true', default=False, + help='Verbose output') + parser.add_argument('--filter', metavar='', + choices=_NAMED_FILTERS, + help='Filter for extraction') + + group = parser.add_mutually_exclusive_group(required=True) + group.add_argument('-l', '--list', metavar='', + help='Show listing of a tarfile') + group.add_argument('-e', '--extract', nargs='+', + metavar=('', ''), + help='Extract tarfile into target dir') + group.add_argument('-c', '--create', nargs='+', + metavar=('', ''), + help='Create tarfile from sources') + group.add_argument('-t', '--test', metavar='', + help='Test if a tarfile is valid') + + args = parser.parse_args() + + if args.filter and args.extract is None: + parser.exit(1, '--filter is only valid for extraction\n') + + if args.test is not None: + src = args.test + if is_tarfile(src): + with open(src, 'r') as tar: + tar.getmembers() + print(tar.getmembers(), file=sys.stderr) + if args.verbose: + print('{!r} is a tar archive.'.format(src)) + else: + parser.exit(1, '{!r} is not a tar archive.\n'.format(src)) + + elif args.list is not None: + src = args.list + if is_tarfile(src): + with TarFile.open(src, 'r:*') as tf: + tf.list(verbose=args.verbose) + else: + parser.exit(1, '{!r} is not a tar archive.\n'.format(src)) + + elif args.extract is not None: + if len(args.extract) == 1: + src = args.extract[0] + curdir = os.curdir + elif len(args.extract) == 2: + src, curdir = args.extract + else: + parser.exit(1, parser.format_help()) + + if is_tarfile(src): + with TarFile.open(src, 'r:*') as tf: + tf.extractall(path=curdir, filter=args.filter) + if args.verbose: + if curdir == '.': + msg = '{!r} file is extracted.'.format(src) + else: + msg = ('{!r} file is extracted ' + 'into {!r} directory.').format(src, curdir) + print(msg) + else: + parser.exit(1, '{!r} is not a tar archive.\n'.format(src)) + + elif args.create is not None: + tar_name = args.create.pop(0) + _, ext = os.path.splitext(tar_name) + compressions = { + # gz + '.gz': 'gz', + '.tgz': 'gz', + # xz + '.xz': 'xz', + '.txz': 'xz', + # bz2 + '.bz2': 'bz2', + '.tbz': 'bz2', + '.tbz2': 'bz2', + '.tb2': 'bz2', + } + tar_mode = 'w:' + compressions[ext] if ext in compressions else 'w' + tar_files = args.create + + with TarFile.open(tar_name, tar_mode) as tf: + for file_name in tar_files: + tf.add(file_name) + + if args.verbose: + print('{!r} file created.'.format(tar_name)) + +if __name__ == '__main__': + main() diff --git a/venv/Lib/site-packages/pkg_resources/_vendor/importlib_resources/__init__.py b/venv/Lib/site-packages/pkg_resources/_vendor/importlib_resources/__init__.py new file mode 100644 index 0000000..34e3a99 --- /dev/null +++ b/venv/Lib/site-packages/pkg_resources/_vendor/importlib_resources/__init__.py @@ -0,0 +1,36 @@ +"""Read resources contained within a package.""" + +from ._common import ( + as_file, + files, + Package, +) + +from ._legacy import ( + contents, + open_binary, + read_binary, + open_text, + read_text, + is_resource, + path, + Resource, +) + +from .abc import ResourceReader + + +__all__ = [ + 'Package', + 'Resource', + 'ResourceReader', + 'as_file', + 'contents', + 'files', + 'is_resource', + 'open_binary', + 'open_text', + 'path', + 'read_binary', + 'read_text', +] diff --git a/venv/Lib/site-packages/pkg_resources/_vendor/importlib_resources/__pycache__/__init__.cpython-312.pyc b/venv/Lib/site-packages/pkg_resources/_vendor/importlib_resources/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..6d06b71ecbfe2c61fd0397a6092f899ad9793355 GIT binary patch literal 694 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 Rw8F$!@ut%)y=@5be*hFN@b>@! literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/pkg_resources/_vendor/importlib_resources/__pycache__/_common.cpython-312.pyc b/venv/Lib/site-packages/pkg_resources/_vendor/importlib_resources/__pycache__/_common.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..5b3a9c44aec9bec40c1b9af554d440e3cfffc321 GIT binary patch literal 8788 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 literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/pkg_resources/_vendor/importlib_resources/__pycache__/_compat.cpython-312.pyc b/venv/Lib/site-packages/pkg_resources/_vendor/importlib_resources/__pycache__/_compat.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..96688aea7271c4dbcf5dcd049a275ecd116e1c9d GIT binary patch literal 5060 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 literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/pkg_resources/_vendor/importlib_resources/__pycache__/abc.cpython-312.pyc b/venv/Lib/site-packages/pkg_resources/_vendor/importlib_resources/__pycache__/abc.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..c72004c5ea3a6bdb2cc001ed2d10497140e825d7 GIT binary patch literal 8904 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 KuNiJqRR0UkeFJ0w literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/pkg_resources/_vendor/importlib_resources/__pycache__/readers.cpython-312.pyc b/venv/Lib/site-packages/pkg_resources/_vendor/importlib_resources/__pycache__/readers.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..af8b0d370d756f115eb8de3bfc8e97f173402234 GIT binary patch literal 7677 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 literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/pkg_resources/_vendor/importlib_resources/__pycache__/simple.cpython-312.pyc b/venv/Lib/site-packages/pkg_resources/_vendor/importlib_resources/__pycache__/simple.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..00e239b0ca3fa048bad7193372bfc6aee0d5f94d GIT binary patch literal 5530 zcma)AU2Gf25#BrAkw;P@MeEmg8lP-O4kL>;oWM?0Td6I_jar9xZ3k^xwd|31l1?Id z%-%^33Au4o1C@caaey{*-&CMLZ5S=mr=&0KOW*n;RRLt24fOA!^_ya>0=e;29_gMKL25gP9;lR6!LBTrrdh6{U>C5uSXNsKFaV4Qbr$E>A%Hc`xAfGZDrq z0cThX`J+)b8iCPh!)ON^?SRovP0}Lw1b2_l3ZINsha(duOQTatUXvA7DMR0srwl3= zjH@FBZ9yx@R8v$KO+_Bz%5;*qr89a-p_PdXws2lItw$V;!*KKpRTh9#$rUuar{T{f z%{1m|UNiHKY=?^OpV~j5vrIGs1|w*!Oh6Mfakit8+3Bmi7MkMKzzyM^JLx?$(vB;_ zD%lO(uuiD2%S3kO*FrA8*uWp%fj_8)K&y@^K@Bx=L-EmnjmJ8Y? zX3*+4A~LiqGn!n~OL}p>c(}pny6+XLy)~~>O_ispQIr*Mhht2(x=f7)9lVob|Lh>u zta)0>qeB`S#!U?!zf3AzlCNH5?4J0NQq*K)O140ivXY-urZpA}QO|>_ z+Q`NEYPFz^T-9=>ZfTPXT4`bOyq=rHe=G($4sij)l7iAydJk9&wxonYsoA@$bL#=#`-&>m`lad|;k0Lna+ zca8|=63qKeM-a7UF^!IbnC2Eq)_Teaf3V zfveE@*UA09XO@JYM85!--R09)+CyegH?x4T)q(92otaS(Rl{-wQtk&p6$|55oY8+KaOwP;a%Smi3#)cS+|0zyhf=(z1I^II&Ip)xvThe9pYwJ0dr`zZ@VvW{56niVw7Dc z4`ch*V}q;GAX71&jL>~BPGuylXxNWEj9Ypb$q^(NVf0lbXOXmI7UmZu$KYpT0>{k4 zLuQdU@k_TCYXlyTgPhodr1x47n z0QB(okOL-1Ni-)(`lweNUK3Pqkz3$sujS+R zEc;AR=f&P4uam{VuehIb*MvoZVY1ETl0nCxN#LkgVK%QDcIOrCZENDfH>pyFx-Ka? zSS3ImsL?aXJ`ThVLS3gRMHBUwu{m`cp15jvi`no{e+wo|#8ZfbMDOz65B6>(4y+{( zY$T4XC60U~d>sB=cs((?6ucje-BNG9`mncuqxZSB-se`V)!yTG7S?)?FP*y|?_KKL zZgq@p7T*Zfjc8RbrQd~XZ@Ri~Tq%_dONIg!KU8RNnAj1cZXpGErQ_0k$(W8N;U`Sm+=c z12YC|Xj;mmy9Ln@HyC(KK*vO0AJKSm_v*f*n*^Sp1Zwzr3@GOyTP-MveYFlw9Aiqc z9rCc)ZyqSPNucoqjmiUR36(%nuoDf{ru${%PYlCO%S|)xHv>7Rm~gE$N=+B$l#gh# z_esNKn-rCb3~{F~z;zD4f3OQ^zMz;Uf}_jJzFf2u#*%Q!bP z0f!NOXb9&5r_YQ#d6vZ|3ppmPIToKtC!PD^82vUZ!w51j1KA9490ysJ6KWFSq7O*; z0qOcH8T=PHwiVpZ4c~fk`NRh&wg_~$zQ*-%`)^0L2t2k@ecafs?`#oxY#o!hL#qRa M?-c$+pvPY4|FV|7{{R30 literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/pkg_resources/_vendor/importlib_resources/_adapters.py b/venv/Lib/site-packages/pkg_resources/_vendor/importlib_resources/_adapters.py new file mode 100644 index 0000000..ea363d8 --- /dev/null +++ b/venv/Lib/site-packages/pkg_resources/_vendor/importlib_resources/_adapters.py @@ -0,0 +1,170 @@ +from contextlib import suppress +from io import TextIOWrapper + +from . import abc + + +class SpecLoaderAdapter: + """ + Adapt a package spec to adapt the underlying loader. + """ + + def __init__(self, spec, adapter=lambda spec: spec.loader): + self.spec = spec + self.loader = adapter(spec) + + def __getattr__(self, name): + return getattr(self.spec, name) + + +class TraversableResourcesLoader: + """ + Adapt a loader to provide TraversableResources. + """ + + def __init__(self, spec): + self.spec = spec + + def get_resource_reader(self, name): + return CompatibilityFiles(self.spec)._native() + + +def _io_wrapper(file, mode='r', *args, **kwargs): + if mode == 'r': + return TextIOWrapper(file, *args, **kwargs) + elif mode == 'rb': + return file + raise ValueError( + "Invalid mode value '{}', only 'r' and 'rb' are supported".format(mode) + ) + + +class CompatibilityFiles: + """ + Adapter for an existing or non-existent resource reader + to provide a compatibility .files(). + """ + + class SpecPath(abc.Traversable): + """ + Path tied to a module spec. + Can be read and exposes the resource reader children. + """ + + def __init__(self, spec, reader): + self._spec = spec + self._reader = reader + + def iterdir(self): + if not self._reader: + return iter(()) + return iter( + CompatibilityFiles.ChildPath(self._reader, path) + for path in self._reader.contents() + ) + + def is_file(self): + return False + + is_dir = is_file + + def joinpath(self, other): + if not self._reader: + return CompatibilityFiles.OrphanPath(other) + return CompatibilityFiles.ChildPath(self._reader, other) + + @property + def name(self): + return self._spec.name + + def open(self, mode='r', *args, **kwargs): + return _io_wrapper(self._reader.open_resource(None), mode, *args, **kwargs) + + class ChildPath(abc.Traversable): + """ + Path tied to a resource reader child. + Can be read but doesn't expose any meaningful children. + """ + + def __init__(self, reader, name): + self._reader = reader + self._name = name + + def iterdir(self): + return iter(()) + + def is_file(self): + return self._reader.is_resource(self.name) + + def is_dir(self): + return not self.is_file() + + def joinpath(self, other): + return CompatibilityFiles.OrphanPath(self.name, other) + + @property + def name(self): + return self._name + + def open(self, mode='r', *args, **kwargs): + return _io_wrapper( + self._reader.open_resource(self.name), mode, *args, **kwargs + ) + + class OrphanPath(abc.Traversable): + """ + Orphan path, not tied to a module spec or resource reader. + Can't be read and doesn't expose any meaningful children. + """ + + def __init__(self, *path_parts): + if len(path_parts) < 1: + raise ValueError('Need at least one path part to construct a path') + self._path = path_parts + + def iterdir(self): + return iter(()) + + def is_file(self): + return False + + is_dir = is_file + + def joinpath(self, other): + return CompatibilityFiles.OrphanPath(*self._path, other) + + @property + def name(self): + return self._path[-1] + + def open(self, mode='r', *args, **kwargs): + raise FileNotFoundError("Can't open orphan path") + + def __init__(self, spec): + self.spec = spec + + @property + def _reader(self): + with suppress(AttributeError): + return self.spec.loader.get_resource_reader(self.spec.name) + + def _native(self): + """ + Return the native reader if it supports files(). + """ + reader = self._reader + return reader if hasattr(reader, 'files') else self + + def __getattr__(self, attr): + return getattr(self._reader, attr) + + def files(self): + return CompatibilityFiles.SpecPath(self.spec, self._reader) + + +def wrap_spec(package): + """ + Construct a package spec with traversable compatibility + on the spec/loader/reader. + """ + return SpecLoaderAdapter(package.__spec__, TraversableResourcesLoader) diff --git a/venv/Lib/site-packages/pkg_resources/_vendor/importlib_resources/_common.py b/venv/Lib/site-packages/pkg_resources/_vendor/importlib_resources/_common.py new file mode 100644 index 0000000..3c6de1c --- /dev/null +++ b/venv/Lib/site-packages/pkg_resources/_vendor/importlib_resources/_common.py @@ -0,0 +1,207 @@ +import os +import pathlib +import tempfile +import functools +import contextlib +import types +import importlib +import inspect +import warnings +import itertools + +from typing import Union, Optional, cast +from .abc import ResourceReader, Traversable + +from ._compat import wrap_spec + +Package = Union[types.ModuleType, str] +Anchor = Package + + +def package_to_anchor(func): + """ + Replace 'package' parameter as 'anchor' and warn about the change. + + Other errors should fall through. + + >>> files('a', 'b') + Traceback (most recent call last): + TypeError: files() takes from 0 to 1 positional arguments but 2 were given + """ + undefined = object() + + @functools.wraps(func) + def wrapper(anchor=undefined, package=undefined): + if package is not undefined: + if anchor is not undefined: + return func(anchor, package) + warnings.warn( + "First parameter to files is renamed to 'anchor'", + DeprecationWarning, + stacklevel=2, + ) + return func(package) + elif anchor is undefined: + return func() + return func(anchor) + + return wrapper + + +@package_to_anchor +def files(anchor: Optional[Anchor] = None) -> Traversable: + """ + Get a Traversable resource for an anchor. + """ + return from_package(resolve(anchor)) + + +def get_resource_reader(package: types.ModuleType) -> Optional[ResourceReader]: + """ + Return the package's loader if it's a ResourceReader. + """ + # We can't use + # a issubclass() check here because apparently abc.'s __subclasscheck__() + # hook wants to create a weak reference to the object, but + # zipimport.zipimporter does not support weak references, resulting in a + # TypeError. That seems terrible. + spec = package.__spec__ + reader = getattr(spec.loader, 'get_resource_reader', None) # type: ignore + if reader is None: + return None + return reader(spec.name) # type: ignore + + +@functools.singledispatch +def resolve(cand: Optional[Anchor]) -> types.ModuleType: + return cast(types.ModuleType, cand) + + +@resolve.register +def _(cand: str) -> types.ModuleType: + return importlib.import_module(cand) + + +@resolve.register +def _(cand: None) -> types.ModuleType: + return resolve(_infer_caller().f_globals['__name__']) + + +def _infer_caller(): + """ + Walk the stack and find the frame of the first caller not in this module. + """ + + def is_this_file(frame_info): + return frame_info.filename == __file__ + + def is_wrapper(frame_info): + return frame_info.function == 'wrapper' + + not_this_file = itertools.filterfalse(is_this_file, inspect.stack()) + # also exclude 'wrapper' due to singledispatch in the call stack + callers = itertools.filterfalse(is_wrapper, not_this_file) + return next(callers).frame + + +def from_package(package: types.ModuleType): + """ + Return a Traversable object for the given package. + + """ + spec = wrap_spec(package) + reader = spec.loader.get_resource_reader(spec.name) + return reader.files() + + +@contextlib.contextmanager +def _tempfile( + reader, + suffix='', + # gh-93353: Keep a reference to call os.remove() in late Python + # finalization. + *, + _os_remove=os.remove, +): + # Not using tempfile.NamedTemporaryFile as it leads to deeper 'try' + # blocks due to the need to close the temporary file to work on Windows + # properly. + fd, raw_path = tempfile.mkstemp(suffix=suffix) + try: + try: + os.write(fd, reader()) + finally: + os.close(fd) + del reader + yield pathlib.Path(raw_path) + finally: + try: + _os_remove(raw_path) + except FileNotFoundError: + pass + + +def _temp_file(path): + return _tempfile(path.read_bytes, suffix=path.name) + + +def _is_present_dir(path: Traversable) -> bool: + """ + Some Traversables implement ``is_dir()`` to raise an + exception (i.e. ``FileNotFoundError``) when the + directory doesn't exist. This function wraps that call + to always return a boolean and only return True + if there's a dir and it exists. + """ + with contextlib.suppress(FileNotFoundError): + return path.is_dir() + return False + + +@functools.singledispatch +def as_file(path): + """ + Given a Traversable object, return that object as a + path on the local file system in a context manager. + """ + return _temp_dir(path) if _is_present_dir(path) else _temp_file(path) + + +@as_file.register(pathlib.Path) +@contextlib.contextmanager +def _(path): + """ + Degenerate behavior for pathlib.Path objects. + """ + yield path + + +@contextlib.contextmanager +def _temp_path(dir: tempfile.TemporaryDirectory): + """ + Wrap tempfile.TemporyDirectory to return a pathlib object. + """ + with dir as result: + yield pathlib.Path(result) + + +@contextlib.contextmanager +def _temp_dir(path): + """ + Given a traversable dir, recursively replicate the whole tree + to the file system in a context manager. + """ + assert path.is_dir() + with _temp_path(tempfile.TemporaryDirectory()) as temp_dir: + yield _write_contents(temp_dir, path) + + +def _write_contents(target, source): + child = target.joinpath(source.name) + if source.is_dir(): + child.mkdir() + for item in source.iterdir(): + _write_contents(child, item) + else: + child.write_bytes(source.read_bytes()) + return child diff --git a/venv/Lib/site-packages/pkg_resources/_vendor/importlib_resources/_compat.py b/venv/Lib/site-packages/pkg_resources/_vendor/importlib_resources/_compat.py new file mode 100644 index 0000000..8b5b1d2 --- /dev/null +++ b/venv/Lib/site-packages/pkg_resources/_vendor/importlib_resources/_compat.py @@ -0,0 +1,108 @@ +# flake8: noqa + +import abc +import os +import sys +import pathlib +from contextlib import suppress +from typing import Union + + +if sys.version_info >= (3, 10): + from zipfile import Path as ZipPath # type: ignore +else: + from ..zipp import Path as ZipPath # type: ignore + + +try: + from typing import runtime_checkable # type: ignore +except ImportError: + + def runtime_checkable(cls): # type: ignore + return cls + + +try: + from typing import Protocol # type: ignore +except ImportError: + Protocol = abc.ABC # type: ignore + + +class TraversableResourcesLoader: + """ + Adapt loaders to provide TraversableResources and other + compatibility. + + Used primarily for Python 3.9 and earlier where the native + loaders do not yet implement TraversableResources. + """ + + def __init__(self, spec): + self.spec = spec + + @property + def path(self): + return self.spec.origin + + def get_resource_reader(self, name): + from . import readers, _adapters + + def _zip_reader(spec): + with suppress(AttributeError): + return readers.ZipReader(spec.loader, spec.name) + + def _namespace_reader(spec): + with suppress(AttributeError, ValueError): + return readers.NamespaceReader(spec.submodule_search_locations) + + def _available_reader(spec): + with suppress(AttributeError): + return spec.loader.get_resource_reader(spec.name) + + def _native_reader(spec): + reader = _available_reader(spec) + return reader if hasattr(reader, 'files') else None + + def _file_reader(spec): + try: + path = pathlib.Path(self.path) + except TypeError: + return None + if path.exists(): + return readers.FileReader(self) + + return ( + # native reader if it supplies 'files' + _native_reader(self.spec) + or + # local ZipReader if a zip module + _zip_reader(self.spec) + or + # local NamespaceReader if a namespace module + _namespace_reader(self.spec) + or + # local FileReader + _file_reader(self.spec) + # fallback - adapt the spec ResourceReader to TraversableReader + or _adapters.CompatibilityFiles(self.spec) + ) + + +def wrap_spec(package): + """ + Construct a package spec with traversable compatibility + on the spec/loader/reader. + + Supersedes _adapters.wrap_spec to use TraversableResourcesLoader + from above for older Python compatibility (<3.10). + """ + from . import _adapters + + return _adapters.SpecLoaderAdapter(package.__spec__, TraversableResourcesLoader) + + +if sys.version_info >= (3, 9): + StrPath = Union[str, os.PathLike[str]] +else: + # PathLike is only subscriptable at runtime in 3.9+ + StrPath = Union[str, "os.PathLike[str]"] diff --git a/venv/Lib/site-packages/pkg_resources/_vendor/importlib_resources/_itertools.py b/venv/Lib/site-packages/pkg_resources/_vendor/importlib_resources/_itertools.py new file mode 100644 index 0000000..cce0558 --- /dev/null +++ b/venv/Lib/site-packages/pkg_resources/_vendor/importlib_resources/_itertools.py @@ -0,0 +1,35 @@ +from itertools import filterfalse + +from typing import ( + Callable, + Iterable, + Iterator, + Optional, + Set, + TypeVar, + Union, +) + +# Type and type variable definitions +_T = TypeVar('_T') +_U = TypeVar('_U') + + +def unique_everseen( + iterable: Iterable[_T], key: Optional[Callable[[_T], _U]] = None +) -> Iterator[_T]: + "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[Union[_T, _U]] = 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/site-packages/pkg_resources/_vendor/importlib_resources/_legacy.py b/venv/Lib/site-packages/pkg_resources/_vendor/importlib_resources/_legacy.py new file mode 100644 index 0000000..b1ea810 --- /dev/null +++ b/venv/Lib/site-packages/pkg_resources/_vendor/importlib_resources/_legacy.py @@ -0,0 +1,120 @@ +import functools +import os +import pathlib +import types +import warnings + +from typing import Union, Iterable, ContextManager, BinaryIO, TextIO, Any + +from . import _common + +Package = Union[types.ModuleType, str] +Resource = str + + +def deprecated(func): + @functools.wraps(func) + def wrapper(*args, **kwargs): + warnings.warn( + f"{func.__name__} is deprecated. Use files() instead. " + "Refer to https://importlib-resources.readthedocs.io" + "/en/latest/using.html#migrating-from-legacy for migration advice.", + DeprecationWarning, + stacklevel=2, + ) + return func(*args, **kwargs) + + return wrapper + + +def normalize_path(path: Any) -> str: + """Normalize a path by ensuring it is a string. + + If the resulting string contains path separators, an exception is raised. + """ + str_path = str(path) + parent, file_name = os.path.split(str_path) + if parent: + raise ValueError(f'{path!r} must be only a file name') + return file_name + + +@deprecated +def open_binary(package: Package, resource: Resource) -> BinaryIO: + """Return a file-like object opened for binary reading of the resource.""" + return (_common.files(package) / normalize_path(resource)).open('rb') + + +@deprecated +def read_binary(package: Package, resource: Resource) -> bytes: + """Return the binary contents of the resource.""" + return (_common.files(package) / normalize_path(resource)).read_bytes() + + +@deprecated +def open_text( + package: Package, + resource: Resource, + encoding: str = 'utf-8', + errors: str = 'strict', +) -> TextIO: + """Return a file-like object opened for text reading of the resource.""" + return (_common.files(package) / normalize_path(resource)).open( + 'r', encoding=encoding, errors=errors + ) + + +@deprecated +def read_text( + package: Package, + resource: Resource, + encoding: str = 'utf-8', + errors: str = 'strict', +) -> str: + """Return the decoded string of the resource. + + The decoding-related arguments have the same semantics as those of + bytes.decode(). + """ + with open_text(package, resource, encoding, errors) as fp: + return fp.read() + + +@deprecated +def contents(package: Package) -> Iterable[str]: + """Return an iterable of entries in `package`. + + Note that not all entries are resources. Specifically, directories are + not considered resources. Use `is_resource()` on each entry returned here + to check if it is a resource or not. + """ + return [path.name for path in _common.files(package).iterdir()] + + +@deprecated +def is_resource(package: Package, name: str) -> bool: + """True if `name` is a resource inside `package`. + + Directories are *not* resources. + """ + resource = normalize_path(name) + return any( + traversable.name == resource and traversable.is_file() + for traversable in _common.files(package).iterdir() + ) + + +@deprecated +def path( + package: Package, + resource: Resource, +) -> ContextManager[pathlib.Path]: + """A context manager providing a file path object to the resource. + + If the resource does not already exist on its own on the file system, + a temporary file will be created. If the file was created, the file + will be deleted upon exiting the context manager (no exception is + raised if the file was deleted prior to the context manager + exiting). + """ + return _common.as_file(_common.files(package) / normalize_path(resource)) diff --git a/venv/Lib/site-packages/pkg_resources/_vendor/importlib_resources/abc.py b/venv/Lib/site-packages/pkg_resources/_vendor/importlib_resources/abc.py new file mode 100644 index 0000000..23b6aea --- /dev/null +++ b/venv/Lib/site-packages/pkg_resources/_vendor/importlib_resources/abc.py @@ -0,0 +1,170 @@ +import abc +import io +import itertools +import pathlib +from typing import Any, BinaryIO, Iterable, Iterator, NoReturn, Text, Optional + +from ._compat import runtime_checkable, Protocol, StrPath + + +__all__ = ["ResourceReader", "Traversable", "TraversableResources"] + + +class ResourceReader(metaclass=abc.ABCMeta): + """Abstract base class for loaders to provide resource reading support.""" + + @abc.abstractmethod + def open_resource(self, resource: Text) -> BinaryIO: + """Return an opened, file-like object for binary reading. + + The 'resource' argument is expected to represent only a file name. + If the resource cannot be found, FileNotFoundError is raised. + """ + # This deliberately raises FileNotFoundError instead of + # NotImplementedError so that if this method is accidentally called, + # it'll still do the right thing. + raise FileNotFoundError + + @abc.abstractmethod + def resource_path(self, resource: Text) -> Text: + """Return the file system path to the specified resource. + + The 'resource' argument is expected to represent only a file name. + If the resource does not exist on the file system, raise + FileNotFoundError. + """ + # This deliberately raises FileNotFoundError instead of + # NotImplementedError so that if this method is accidentally called, + # it'll still do the right thing. + raise FileNotFoundError + + @abc.abstractmethod + def is_resource(self, path: Text) -> bool: + """Return True if the named 'path' is a resource. + + Files are resources, directories are not. + """ + raise FileNotFoundError + + @abc.abstractmethod + def contents(self) -> Iterable[str]: + """Return an iterable of entries in `package`.""" + raise FileNotFoundError + + +class TraversalError(Exception): + pass + + +@runtime_checkable +class Traversable(Protocol): + """ + An object with a subset of pathlib.Path methods suitable for + traversing directories and opening files. + + Any exceptions that occur when accessing the backing resource + may propagate unaltered. + """ + + @abc.abstractmethod + def iterdir(self) -> Iterator["Traversable"]: + """ + Yield Traversable objects in self + """ + + def read_bytes(self) -> bytes: + """ + Read contents of self as bytes + """ + with self.open('rb') as strm: + return strm.read() + + def read_text(self, encoding: Optional[str] = None) -> str: + """ + Read contents of self as text + """ + with self.open(encoding=encoding) as strm: + return strm.read() + + @abc.abstractmethod + def is_dir(self) -> bool: + """ + Return True if self is a directory + """ + + @abc.abstractmethod + def is_file(self) -> bool: + """ + Return True if self is a file + """ + + def joinpath(self, *descendants: StrPath) -> "Traversable": + """ + Return Traversable resolved with any descendants applied. + + Each descendant should be a path segment relative to self + and each may contain multiple levels separated by + ``posixpath.sep`` (``/``). + """ + if not descendants: + return self + names = itertools.chain.from_iterable( + path.parts for path in map(pathlib.PurePosixPath, descendants) + ) + target = next(names) + matches = ( + traversable for traversable in self.iterdir() if traversable.name == target + ) + try: + match = next(matches) + except StopIteration: + raise TraversalError( + "Target not found during traversal.", target, list(names) + ) + return match.joinpath(*names) + + def __truediv__(self, child: StrPath) -> "Traversable": + """ + Return Traversable child in self + """ + return self.joinpath(child) + + @abc.abstractmethod + def open(self, mode='r', *args, **kwargs): + """ + mode may be 'r' or 'rb' to open as text or binary. Return a handle + suitable for reading (same as pathlib.Path.open). + + When opening as text, accepts encoding parameters such as those + accepted by io.TextIOWrapper. + """ + + @property + @abc.abstractmethod + def name(self) -> str: + """ + The base name of this object without any parent references. + """ + + +class TraversableResources(ResourceReader): + """ + The required interface for providing traversable + resources. + """ + + @abc.abstractmethod + def files(self) -> "Traversable": + """Return a Traversable object for the loaded package.""" + + def open_resource(self, resource: StrPath) -> io.BufferedReader: + return self.files().joinpath(resource).open('rb') + + def resource_path(self, resource: Any) -> NoReturn: + raise FileNotFoundError(resource) + + def is_resource(self, path: StrPath) -> bool: + return self.files().joinpath(path).is_file() + + def contents(self) -> Iterator[str]: + return (item.name for item in self.files().iterdir()) diff --git a/venv/Lib/site-packages/pkg_resources/_vendor/importlib_resources/py.typed b/venv/Lib/site-packages/pkg_resources/_vendor/importlib_resources/py.typed new file mode 100644 index 0000000..e69de29 diff --git a/venv/Lib/site-packages/pkg_resources/_vendor/importlib_resources/readers.py b/venv/Lib/site-packages/pkg_resources/_vendor/importlib_resources/readers.py new file mode 100644 index 0000000..ab34db7 --- /dev/null +++ b/venv/Lib/site-packages/pkg_resources/_vendor/importlib_resources/readers.py @@ -0,0 +1,120 @@ +import collections +import pathlib +import operator + +from . import abc + +from ._itertools import unique_everseen +from ._compat import ZipPath + + +def remove_duplicates(items): + return iter(collections.OrderedDict.fromkeys(items)) + + +class FileReader(abc.TraversableResources): + def __init__(self, loader): + self.path = pathlib.Path(loader.path).parent + + def resource_path(self, resource): + """ + Return the file system path to prevent + `resources.path()` from creating a temporary + copy. + """ + return str(self.path.joinpath(resource)) + + def files(self): + return self.path + + +class ZipReader(abc.TraversableResources): + def __init__(self, loader, module): + _, _, name = module.rpartition('.') + self.prefix = loader.prefix.replace('\\', '/') + name + '/' + self.archive = loader.archive + + def open_resource(self, resource): + try: + return super().open_resource(resource) + except KeyError as exc: + raise FileNotFoundError(exc.args[0]) + + def is_resource(self, path): + # workaround for `zipfile.Path.is_file` returning true + # for non-existent paths. + target = self.files().joinpath(path) + return target.is_file() and target.exists() + + def files(self): + return ZipPath(self.archive, self.prefix) + + +class MultiplexedPath(abc.Traversable): + """ + Given a series of Traversable objects, implement a merged + version of the interface across all objects. Useful for + namespace packages which may be multihomed at a single + name. + """ + + def __init__(self, *paths): + self._paths = list(map(pathlib.Path, remove_duplicates(paths))) + if not self._paths: + message = 'MultiplexedPath must contain at least one path' + raise FileNotFoundError(message) + if not all(path.is_dir() for path in self._paths): + raise NotADirectoryError('MultiplexedPath only supports directories') + + def iterdir(self): + files = (file for path in self._paths for file in path.iterdir()) + return unique_everseen(files, key=operator.attrgetter('name')) + + def read_bytes(self): + raise FileNotFoundError(f'{self} is not a file') + + def read_text(self, *args, **kwargs): + raise FileNotFoundError(f'{self} is not a file') + + def is_dir(self): + return True + + def is_file(self): + return False + + def joinpath(self, *descendants): + try: + return super().joinpath(*descendants) + except abc.TraversalError: + # One of the paths did not resolve (a directory does not exist). + # Just return something that will not exist. + return self._paths[0].joinpath(*descendants) + + def open(self, *args, **kwargs): + raise FileNotFoundError(f'{self} is not a file') + + @property + def name(self): + return self._paths[0].name + + def __repr__(self): + paths = ', '.join(f"'{path}'" for path in self._paths) + return f'MultiplexedPath({paths})' + + +class NamespaceReader(abc.TraversableResources): + def __init__(self, namespace_path): + if 'NamespacePath' not in str(namespace_path): + raise ValueError('Invalid path') + self.path = MultiplexedPath(*list(namespace_path)) + + def resource_path(self, resource): + """ + Return the file system path to prevent + `resources.path()` from creating a temporary + copy. + """ + return str(self.path.joinpath(resource)) + + def files(self): + return self.path diff --git a/venv/Lib/site-packages/pkg_resources/_vendor/importlib_resources/simple.py b/venv/Lib/site-packages/pkg_resources/_vendor/importlib_resources/simple.py new file mode 100644 index 0000000..7770c92 --- /dev/null +++ b/venv/Lib/site-packages/pkg_resources/_vendor/importlib_resources/simple.py @@ -0,0 +1,106 @@ +""" +Interface adapters for low-level readers. +""" + +import abc +import io +import itertools +from typing import BinaryIO, List + +from .abc import Traversable, TraversableResources + + +class SimpleReader(abc.ABC): + """ + The minimum, low-level interface required from a resource + provider. + """ + + @property + @abc.abstractmethod + def package(self) -> str: + """ + The name of the package for which this reader loads resources. + """ + + @abc.abstractmethod + def children(self) -> List['SimpleReader']: + """ + Obtain an iterable of SimpleReader for available + child containers (e.g. directories). + """ + + @abc.abstractmethod + def resources(self) -> List[str]: + """ + Obtain available named resources for this virtual package. + """ + + @abc.abstractmethod + def open_binary(self, resource: str) -> BinaryIO: + """ + Obtain a File-like for a named resource. + """ + + @property + def name(self): + return self.package.split('.')[-1] + + +class ResourceContainer(Traversable): + """ + Traversable container for a package's resources via its reader. + """ + + def __init__(self, reader: SimpleReader): + self.reader = reader + + def is_dir(self): + return True + + def is_file(self): + return False + + def iterdir(self): + files = (ResourceHandle(self, name) for name in self.reader.resources) + dirs = map(ResourceContainer, self.reader.children()) + return itertools.chain(files, dirs) + + def open(self, *args, **kwargs): + raise IsADirectoryError() + + +class ResourceHandle(Traversable): + """ + Handle to a named resource in a ResourceReader. + """ + + def __init__(self, parent: ResourceContainer, name: str): + self.parent = parent + self.name = name # type: ignore + + def is_file(self): + return True + + def is_dir(self): + return False + + def open(self, mode='r', *args, **kwargs): + stream = self.parent.reader.open_binary(self.name) + if 'b' not in mode: + stream = io.TextIOWrapper(*args, **kwargs) + return stream + + def joinpath(self, name): + raise RuntimeError("Cannot traverse into a resource") + + +class TraversableReader(TraversableResources, SimpleReader): + """ + A TraversableResources based on SimpleReader. Resource providers + may derive from this class to provide the TraversableResources + interface by supplying the SimpleReader interface. + """ + + def files(self): + return ResourceContainer(self) diff --git a/venv/Lib/site-packages/pkg_resources/_vendor/jaraco/__init__.py b/venv/Lib/site-packages/pkg_resources/_vendor/jaraco/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/venv/Lib/site-packages/pkg_resources/_vendor/jaraco/__pycache__/__init__.cpython-312.pyc b/venv/Lib/site-packages/pkg_resources/_vendor/jaraco/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..3202007db4f908404d911af5d41b8565ea89ab88 GIT binary patch literal 214 zcmZ8bK@Ng25Tv4sG2tN|kh?Kn@MsJdZ%rB~E2%(BwiE*&@eRJkR~TPlxN)-e=p;Kk zGnvCYgJ9)Z;48ei|913`ac+~l*L!PndU!aqZgic=#9MmSfcnSuptz|)wwlH&7qXy< z;H^Oj6Eh4Igg3~vXn}ShJ9-oubuMgDUZw?T+LQ%D(8?eO9WmR=6-L(-Q?4S$gcL1f bo2H+ZyISR@f;}$m3Sv(?@$m^E=v(y#2U$Jb literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/pkg_resources/_vendor/jaraco/__pycache__/context.cpython-312.pyc b/venv/Lib/site-packages/pkg_resources/_vendor/jaraco/__pycache__/context.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..b37e8b32125d8b650bd09b8bb671a659d5d60b4e GIT binary patch literal 14380 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 literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/pkg_resources/_vendor/jaraco/context.py b/venv/Lib/site-packages/pkg_resources/_vendor/jaraco/context.py new file mode 100644 index 0000000..c42f613 --- /dev/null +++ b/venv/Lib/site-packages/pkg_resources/_vendor/jaraco/context.py @@ -0,0 +1,361 @@ +from __future__ import annotations + +import contextlib +import functools +import operator +import os +import shutil +import subprocess +import sys +import tempfile +import urllib.request +import warnings +from typing import Iterator + + +if sys.version_info < (3, 12): + from pkg_resources.extern.backports import tarfile +else: + import tarfile + + +@contextlib.contextmanager +def pushd(dir: str | os.PathLike) -> Iterator[str | os.PathLike]: + """ + >>> tmp_path = getfixture('tmp_path') + >>> with pushd(tmp_path): + ... assert os.getcwd() == os.fspath(tmp_path) + >>> assert os.getcwd() != os.fspath(tmp_path) + """ + + orig = os.getcwd() + os.chdir(dir) + try: + yield dir + finally: + os.chdir(orig) + + +@contextlib.contextmanager +def tarball( + url, target_dir: str | os.PathLike | None = None +) -> Iterator[str | os.PathLike]: + """ + Get a tarball, extract it, yield, then clean up. + + >>> import urllib.request + >>> url = getfixture('tarfile_served') + >>> target = getfixture('tmp_path') / 'out' + >>> tb = tarball(url, target_dir=target) + >>> import pathlib + >>> with tb as extracted: + ... contents = pathlib.Path(extracted, 'contents.txt').read_text(encoding='utf-8') + >>> assert not os.path.exists(extracted) + """ + if target_dir is None: + target_dir = os.path.basename(url).replace('.tar.gz', '').replace('.tgz', '') + # In the tar command, use --strip-components=1 to strip the first path and + # then + # use -C to cause the files to be extracted to {target_dir}. This ensures + # that we always know where the files were extracted. + os.mkdir(target_dir) + try: + req = urllib.request.urlopen(url) + with tarfile.open(fileobj=req, mode='r|*') as tf: + tf.extractall(path=target_dir, filter=strip_first_component) + yield target_dir + finally: + shutil.rmtree(target_dir) + + +def strip_first_component( + member: tarfile.TarInfo, + path, +) -> tarfile.TarInfo: + _, member.name = member.name.split('/', 1) + return member + + +def _compose(*cmgrs): + """ + Compose any number of dependent context managers into a single one. + + The last, innermost context manager may take arbitrary arguments, but + each successive context manager should accept the result from the + previous as a single parameter. + + Like :func:`jaraco.functools.compose`, behavior works from right to + left, so the context manager should be indicated from outermost to + innermost. + + Example, to create a context manager to change to a temporary + directory: + + >>> temp_dir_as_cwd = _compose(pushd, temp_dir) + >>> with temp_dir_as_cwd() as dir: + ... assert os.path.samefile(os.getcwd(), dir) + """ + + def compose_two(inner, outer): + def composed(*args, **kwargs): + with inner(*args, **kwargs) as saved, outer(saved) as res: + yield res + + return contextlib.contextmanager(composed) + + return functools.reduce(compose_two, reversed(cmgrs)) + + +tarball_cwd = _compose(pushd, tarball) + + +@contextlib.contextmanager +def tarball_context(*args, **kwargs): + warnings.warn( + "tarball_context is deprecated. Use tarball or tarball_cwd instead.", + DeprecationWarning, + stacklevel=2, + ) + pushd_ctx = kwargs.pop('pushd', pushd) + with tarball(*args, **kwargs) as tball, pushd_ctx(tball) as dir: + yield dir + + +def infer_compression(url): + """ + Given a URL or filename, infer the compression code for tar. + + >>> infer_compression('http://foo/bar.tar.gz') + 'z' + >>> infer_compression('http://foo/bar.tgz') + 'z' + >>> infer_compression('file.bz') + 'j' + >>> infer_compression('file.xz') + 'J' + """ + warnings.warn( + "infer_compression is deprecated with no replacement", + DeprecationWarning, + stacklevel=2, + ) + # cheat and just assume it's the last two characters + compression_indicator = url[-2:] + mapping = dict(gz='z', bz='j', xz='J') + # Assume 'z' (gzip) if no match + return mapping.get(compression_indicator, 'z') + + +@contextlib.contextmanager +def temp_dir(remover=shutil.rmtree): + """ + Create a temporary directory context. Pass a custom remover + to override the removal behavior. + + >>> import pathlib + >>> with temp_dir() as the_dir: + ... assert os.path.isdir(the_dir) + ... _ = pathlib.Path(the_dir).joinpath('somefile').write_text('contents', encoding='utf-8') + >>> assert not os.path.exists(the_dir) + """ + temp_dir = tempfile.mkdtemp() + try: + yield temp_dir + finally: + remover(temp_dir) + + +@contextlib.contextmanager +def repo_context(url, branch=None, quiet=True, dest_ctx=temp_dir): + """ + Check out the repo indicated by url. + + If dest_ctx is supplied, it should be a context manager + to yield the target directory for the check out. + """ + exe = 'git' if 'git' in url else 'hg' + with dest_ctx() as repo_dir: + cmd = [exe, 'clone', url, repo_dir] + if branch: + cmd.extend(['--branch', branch]) + devnull = open(os.path.devnull, 'w') + stdout = devnull if quiet else None + subprocess.check_call(cmd, stdout=stdout) + yield repo_dir + + +def null(): + """ + A null context suitable to stand in for a meaningful context. + + >>> with null() as value: + ... assert value is None + + This context is most useful when dealing with two or more code + branches but only some need a context. Wrap the others in a null + context to provide symmetry across all options. + """ + warnings.warn( + "null is deprecated. Use contextlib.nullcontext", + DeprecationWarning, + stacklevel=2, + ) + return contextlib.nullcontext() + + +class ExceptionTrap: + """ + A context manager that will catch certain exceptions and provide an + indication they occurred. + + >>> with ExceptionTrap() as trap: + ... raise Exception() + >>> bool(trap) + True + + >>> with ExceptionTrap() as trap: + ... pass + >>> bool(trap) + False + + >>> with ExceptionTrap(ValueError) as trap: + ... raise ValueError("1 + 1 is not 3") + >>> bool(trap) + True + >>> trap.value + ValueError('1 + 1 is not 3') + >>> trap.tb + + + >>> with ExceptionTrap(ValueError) as trap: + ... raise Exception() + Traceback (most recent call last): + ... + Exception + + >>> bool(trap) + False + """ + + exc_info = None, None, None + + def __init__(self, exceptions=(Exception,)): + self.exceptions = exceptions + + def __enter__(self): + return self + + @property + def type(self): + return self.exc_info[0] + + @property + def value(self): + return self.exc_info[1] + + @property + def tb(self): + return self.exc_info[2] + + def __exit__(self, *exc_info): + type = exc_info[0] + matches = type and issubclass(type, self.exceptions) + if matches: + self.exc_info = exc_info + return matches + + def __bool__(self): + return bool(self.type) + + def raises(self, func, *, _test=bool): + """ + Wrap func and replace the result with the truth + value of the trap (True if an exception occurred). + + First, give the decorator an alias to support Python 3.8 + Syntax. + + >>> raises = ExceptionTrap(ValueError).raises + + Now decorate a function that always fails. + + >>> @raises + ... def fail(): + ... raise ValueError('failed') + >>> fail() + True + """ + + @functools.wraps(func) + def wrapper(*args, **kwargs): + with ExceptionTrap(self.exceptions) as trap: + func(*args, **kwargs) + return _test(trap) + + return wrapper + + def passes(self, func): + """ + Wrap func and replace the result with the truth + value of the trap (True if no exception). + + First, give the decorator an alias to support Python 3.8 + Syntax. + + >>> passes = ExceptionTrap(ValueError).passes + + Now decorate a function that always fails. + + >>> @passes + ... def fail(): + ... raise ValueError('failed') + + >>> fail() + False + """ + return self.raises(func, _test=operator.not_) + + +class suppress(contextlib.suppress, contextlib.ContextDecorator): + """ + A version of contextlib.suppress with decorator support. + + >>> @suppress(KeyError) + ... def key_error(): + ... {}[''] + >>> key_error() + """ + + +class on_interrupt(contextlib.ContextDecorator): + """ + Replace a KeyboardInterrupt with SystemExit(1) + + >>> def do_interrupt(): + ... raise KeyboardInterrupt() + >>> on_interrupt('error')(do_interrupt)() + Traceback (most recent call last): + ... + SystemExit: 1 + >>> on_interrupt('error', code=255)(do_interrupt)() + Traceback (most recent call last): + ... + SystemExit: 255 + >>> on_interrupt('suppress')(do_interrupt)() + >>> with __import__('pytest').raises(KeyboardInterrupt): + ... on_interrupt('ignore')(do_interrupt)() + """ + + def __init__(self, action='error', /, code=1): + self.action = action + self.code = code + + def __enter__(self): + return self + + def __exit__(self, exctype, excinst, exctb): + if exctype is not KeyboardInterrupt or self.action == 'ignore': + return + elif self.action == 'error': + raise SystemExit(self.code) from excinst + return self.action == 'suppress' diff --git a/venv/Lib/site-packages/pkg_resources/_vendor/jaraco/functools/__init__.py b/venv/Lib/site-packages/pkg_resources/_vendor/jaraco/functools/__init__.py new file mode 100644 index 0000000..f523099 --- /dev/null +++ b/venv/Lib/site-packages/pkg_resources/_vendor/jaraco/functools/__init__.py @@ -0,0 +1,633 @@ +import collections.abc +import functools +import inspect +import itertools +import operator +import time +import types +import warnings + +import pkg_resources.extern.more_itertools + + +def compose(*funcs): + """ + Compose any number of unary functions into a single unary function. + + >>> import textwrap + >>> expected = str.strip(textwrap.dedent(compose.__doc__)) + >>> strip_and_dedent = compose(str.strip, textwrap.dedent) + >>> strip_and_dedent(compose.__doc__) == expected + True + + Compose also allows the innermost function to take arbitrary arguments. + + >>> round_three = lambda x: round(x, ndigits=3) + >>> f = compose(round_three, int.__truediv__) + >>> [f(3*x, x+1) for x in range(1,10)] + [1.5, 2.0, 2.25, 2.4, 2.5, 2.571, 2.625, 2.667, 2.7] + """ + + def compose_two(f1, f2): + return lambda *args, **kwargs: f1(f2(*args, **kwargs)) + + return functools.reduce(compose_two, funcs) + + +def once(func): + """ + Decorate func so it's only ever called the first time. + + This decorator can ensure that an expensive or non-idempotent function + will not be expensive on subsequent calls and is idempotent. + + >>> add_three = once(lambda a: a+3) + >>> add_three(3) + 6 + >>> add_three(9) + 6 + >>> add_three('12') + 6 + + To reset the stored value, simply clear the property ``saved_result``. + + >>> del add_three.saved_result + >>> add_three(9) + 12 + >>> add_three(8) + 12 + + Or invoke 'reset()' on it. + + >>> add_three.reset() + >>> add_three(-3) + 0 + >>> add_three(0) + 0 + """ + + @functools.wraps(func) + def wrapper(*args, **kwargs): + if not hasattr(wrapper, 'saved_result'): + wrapper.saved_result = func(*args, **kwargs) + return wrapper.saved_result + + wrapper.reset = lambda: vars(wrapper).__delitem__('saved_result') + return wrapper + + +def method_cache(method, cache_wrapper=functools.lru_cache()): + """ + Wrap lru_cache to support storing the cache data in the object instances. + + Abstracts the common paradigm where the method explicitly saves an + underscore-prefixed protected property on first call and returns that + subsequently. + + >>> class MyClass: + ... calls = 0 + ... + ... @method_cache + ... def method(self, value): + ... self.calls += 1 + ... return value + + >>> a = MyClass() + >>> a.method(3) + 3 + >>> for x in range(75): + ... res = a.method(x) + >>> a.calls + 75 + + Note that the apparent behavior will be exactly like that of lru_cache + except that the cache is stored on each instance, so values in one + instance will not flush values from another, and when an instance is + deleted, so are the cached values for that instance. + + >>> b = MyClass() + >>> for x in range(35): + ... res = b.method(x) + >>> b.calls + 35 + >>> a.method(0) + 0 + >>> a.calls + 75 + + Note that if method had been decorated with ``functools.lru_cache()``, + a.calls would have been 76 (due to the cached value of 0 having been + flushed by the 'b' instance). + + Clear the cache with ``.cache_clear()`` + + >>> a.method.cache_clear() + + Same for a method that hasn't yet been called. + + >>> c = MyClass() + >>> c.method.cache_clear() + + Another cache wrapper may be supplied: + + >>> cache = functools.lru_cache(maxsize=2) + >>> MyClass.method2 = method_cache(lambda self: 3, cache_wrapper=cache) + >>> a = MyClass() + >>> a.method2() + 3 + + Caution - do not subsequently wrap the method with another decorator, such + as ``@property``, which changes the semantics of the function. + + See also + http://code.activestate.com/recipes/577452-a-memoize-decorator-for-instance-methods/ + for another implementation and additional justification. + """ + + def wrapper(self, *args, **kwargs): + # it's the first call, replace the method with a cached, bound method + bound_method = types.MethodType(method, self) + cached_method = cache_wrapper(bound_method) + setattr(self, method.__name__, cached_method) + return cached_method(*args, **kwargs) + + # Support cache clear even before cache has been created. + wrapper.cache_clear = lambda: None + + return _special_method_cache(method, cache_wrapper) or wrapper + + +def _special_method_cache(method, cache_wrapper): + """ + Because Python treats special methods differently, it's not + possible to use instance attributes to implement the cached + methods. + + Instead, install the wrapper method under a different name + and return a simple proxy to that wrapper. + + https://github.com/jaraco/jaraco.functools/issues/5 + """ + name = method.__name__ + special_names = '__getattr__', '__getitem__' + + if name not in special_names: + return None + + wrapper_name = '__cached' + name + + def proxy(self, /, *args, **kwargs): + if wrapper_name not in vars(self): + bound = types.MethodType(method, self) + cache = cache_wrapper(bound) + setattr(self, wrapper_name, cache) + else: + cache = getattr(self, wrapper_name) + return cache(*args, **kwargs) + + return proxy + + +def apply(transform): + """ + Decorate a function with a transform function that is + invoked on results returned from the decorated function. + + >>> @apply(reversed) + ... def get_numbers(start): + ... "doc for get_numbers" + ... return range(start, start+3) + >>> list(get_numbers(4)) + [6, 5, 4] + >>> get_numbers.__doc__ + 'doc for get_numbers' + """ + + def wrap(func): + return functools.wraps(func)(compose(transform, func)) + + return wrap + + +def result_invoke(action): + r""" + Decorate a function with an action function that is + invoked on the results returned from the decorated + function (for its side effect), then return the original + result. + + >>> @result_invoke(print) + ... def add_two(a, b): + ... return a + b + >>> x = add_two(2, 3) + 5 + >>> x + 5 + """ + + def wrap(func): + @functools.wraps(func) + def wrapper(*args, **kwargs): + result = func(*args, **kwargs) + action(result) + return result + + return wrapper + + return wrap + + +def invoke(f, /, *args, **kwargs): + """ + Call a function for its side effect after initialization. + + The benefit of using the decorator instead of simply invoking a function + after defining it is that it makes explicit the author's intent for the + function to be called immediately. Whereas if one simply calls the + function immediately, it's less obvious if that was intentional or + incidental. It also avoids repeating the name - the two actions, defining + the function and calling it immediately are modeled separately, but linked + by the decorator construct. + + The benefit of having a function construct (opposed to just invoking some + behavior inline) is to serve as a scope in which the behavior occurs. It + avoids polluting the global namespace with local variables, provides an + anchor on which to attach documentation (docstring), keeps the behavior + logically separated (instead of conceptually separated or not separated at + all), and provides potential to re-use the behavior for testing or other + purposes. + + This function is named as a pithy way to communicate, "call this function + primarily for its side effect", or "while defining this function, also + take it aside and call it". It exists because there's no Python construct + for "define and call" (nor should there be, as decorators serve this need + just fine). The behavior happens immediately and synchronously. + + >>> @invoke + ... def func(): print("called") + called + >>> func() + called + + Use functools.partial to pass parameters to the initial call + + >>> @functools.partial(invoke, name='bingo') + ... def func(name): print('called with', name) + called with bingo + """ + f(*args, **kwargs) + return f + + +class Throttler: + """Rate-limit a function (or other callable).""" + + def __init__(self, func, max_rate=float('Inf')): + if isinstance(func, Throttler): + func = func.func + self.func = func + self.max_rate = max_rate + self.reset() + + def reset(self): + self.last_called = 0 + + def __call__(self, *args, **kwargs): + self._wait() + return self.func(*args, **kwargs) + + def _wait(self): + """Ensure at least 1/max_rate seconds from last call.""" + elapsed = time.time() - self.last_called + must_wait = 1 / self.max_rate - elapsed + time.sleep(max(0, must_wait)) + self.last_called = time.time() + + def __get__(self, obj, owner=None): + return first_invoke(self._wait, functools.partial(self.func, obj)) + + +def first_invoke(func1, func2): + """ + Return a function that when invoked will invoke func1 without + any parameters (for its side effect) and then invoke func2 + with whatever parameters were passed, returning its result. + """ + + def wrapper(*args, **kwargs): + func1() + return func2(*args, **kwargs) + + return wrapper + + +method_caller = first_invoke( + lambda: warnings.warn( + '`jaraco.functools.method_caller` is deprecated, ' + 'use `operator.methodcaller` instead', + DeprecationWarning, + stacklevel=3, + ), + operator.methodcaller, +) + + +def retry_call(func, cleanup=lambda: None, retries=0, trap=()): + """ + Given a callable func, trap the indicated exceptions + for up to 'retries' times, invoking cleanup on the + exception. On the final attempt, allow any exceptions + to propagate. + """ + attempts = itertools.count() if retries == float('inf') else range(retries) + for _ in attempts: + try: + return func() + except trap: + cleanup() + + return func() + + +def retry(*r_args, **r_kwargs): + """ + Decorator wrapper for retry_call. Accepts arguments to retry_call + except func and then returns a decorator for the decorated function. + + Ex: + + >>> @retry(retries=3) + ... def my_func(a, b): + ... "this is my funk" + ... print(a, b) + >>> my_func.__doc__ + 'this is my funk' + """ + + def decorate(func): + @functools.wraps(func) + def wrapper(*f_args, **f_kwargs): + bound = functools.partial(func, *f_args, **f_kwargs) + return retry_call(bound, *r_args, **r_kwargs) + + return wrapper + + return decorate + + +def print_yielded(func): + """ + Convert a generator into a function that prints all yielded elements. + + >>> @print_yielded + ... def x(): + ... yield 3; yield None + >>> x() + 3 + None + """ + print_all = functools.partial(map, print) + print_results = compose(more_itertools.consume, print_all, func) + return functools.wraps(func)(print_results) + + +def pass_none(func): + """ + Wrap func so it's not called if its first param is None. + + >>> print_text = pass_none(print) + >>> print_text('text') + text + >>> print_text(None) + """ + + @functools.wraps(func) + def wrapper(param, /, *args, **kwargs): + if param is not None: + return func(param, *args, **kwargs) + return None + + return wrapper + + +def assign_params(func, namespace): + """ + Assign parameters from namespace where func solicits. + + >>> def func(x, y=3): + ... print(x, y) + >>> assigned = assign_params(func, dict(x=2, z=4)) + >>> assigned() + 2 3 + + The usual errors are raised if a function doesn't receive + its required parameters: + + >>> assigned = assign_params(func, dict(y=3, z=4)) + >>> assigned() + Traceback (most recent call last): + TypeError: func() ...argument... + + It even works on methods: + + >>> class Handler: + ... def meth(self, arg): + ... print(arg) + >>> assign_params(Handler().meth, dict(arg='crystal', foo='clear'))() + crystal + """ + sig = inspect.signature(func) + params = sig.parameters.keys() + call_ns = {k: namespace[k] for k in params if k in namespace} + return functools.partial(func, **call_ns) + + +def save_method_args(method): + """ + Wrap a method such that when it is called, the args and kwargs are + saved on the method. + + >>> class MyClass: + ... @save_method_args + ... def method(self, a, b): + ... print(a, b) + >>> my_ob = MyClass() + >>> my_ob.method(1, 2) + 1 2 + >>> my_ob._saved_method.args + (1, 2) + >>> my_ob._saved_method.kwargs + {} + >>> my_ob.method(a=3, b='foo') + 3 foo + >>> my_ob._saved_method.args + () + >>> my_ob._saved_method.kwargs == dict(a=3, b='foo') + True + + The arguments are stored on the instance, allowing for + different instance to save different args. + + >>> your_ob = MyClass() + >>> your_ob.method({str('x'): 3}, b=[4]) + {'x': 3} [4] + >>> your_ob._saved_method.args + ({'x': 3},) + >>> my_ob._saved_method.args + () + """ + args_and_kwargs = collections.namedtuple('args_and_kwargs', 'args kwargs') + + @functools.wraps(method) + def wrapper(self, /, *args, **kwargs): + attr_name = '_saved_' + method.__name__ + attr = args_and_kwargs(args, kwargs) + setattr(self, attr_name, attr) + return method(self, *args, **kwargs) + + return wrapper + + +def except_(*exceptions, replace=None, use=None): + """ + Replace the indicated exceptions, if raised, with the indicated + literal replacement or evaluated expression (if present). + + >>> safe_int = except_(ValueError)(int) + >>> safe_int('five') + >>> safe_int('5') + 5 + + Specify a literal replacement with ``replace``. + + >>> safe_int_r = except_(ValueError, replace=0)(int) + >>> safe_int_r('five') + 0 + + Provide an expression to ``use`` to pass through particular parameters. + + >>> safe_int_pt = except_(ValueError, use='args[0]')(int) + >>> safe_int_pt('five') + 'five' + + """ + + def decorate(func): + @functools.wraps(func) + def wrapper(*args, **kwargs): + try: + return func(*args, **kwargs) + except exceptions: + try: + return eval(use) + except TypeError: + return replace + + return wrapper + + return decorate + + +def identity(x): + """ + Return the argument. + + >>> o = object() + >>> identity(o) is o + True + """ + return x + + +def bypass_when(check, *, _op=identity): + """ + Decorate a function to return its parameter when ``check``. + + >>> bypassed = [] # False + + >>> @bypass_when(bypassed) + ... def double(x): + ... return x * 2 + >>> double(2) + 4 + >>> bypassed[:] = [object()] # True + >>> double(2) + 2 + """ + + def decorate(func): + @functools.wraps(func) + def wrapper(param, /): + return param if _op(check) else func(param) + + return wrapper + + return decorate + + +def bypass_unless(check): + """ + Decorate a function to return its parameter unless ``check``. + + >>> enabled = [object()] # True + + >>> @bypass_unless(enabled) + ... def double(x): + ... return x * 2 + >>> double(2) + 4 + >>> del enabled[:] # False + >>> double(2) + 2 + """ + return bypass_when(check, _op=operator.not_) + + +@functools.singledispatch +def _splat_inner(args, func): + """Splat args to func.""" + return func(*args) + + +@_splat_inner.register +def _(args: collections.abc.Mapping, func): + """Splat kargs to func as kwargs.""" + return func(**args) + + +def splat(func): + """ + Wrap func to expect its parameters to be passed positionally in a tuple. + + Has a similar effect to that of ``itertools.starmap`` over + simple ``map``. + + >>> pairs = [(-1, 1), (0, 2)] + >>> pkg_resources.extern.more_itertools.consume(itertools.starmap(print, pairs)) + -1 1 + 0 2 + >>> pkg_resources.extern.more_itertools.consume(map(splat(print), pairs)) + -1 1 + 0 2 + + The approach generalizes to other iterators that don't have a "star" + equivalent, such as a "starfilter". + + >>> list(filter(splat(operator.add), pairs)) + [(0, 2)] + + Splat also accepts a mapping argument. + + >>> def is_nice(msg, code): + ... return "smile" in msg or code == 0 + >>> msgs = [ + ... dict(msg='smile!', code=20), + ... dict(msg='error :(', code=1), + ... dict(msg='unknown', code=0), + ... ] + >>> for msg in filter(splat(is_nice), msgs): + ... print(msg) + {'msg': 'smile!', 'code': 20} + {'msg': 'unknown', 'code': 0} + """ + return functools.wraps(func)(functools.partial(_splat_inner, func=func)) diff --git a/venv/Lib/site-packages/pkg_resources/_vendor/jaraco/functools/__init__.pyi b/venv/Lib/site-packages/pkg_resources/_vendor/jaraco/functools/__init__.pyi new file mode 100644 index 0000000..c2b9ab1 --- /dev/null +++ b/venv/Lib/site-packages/pkg_resources/_vendor/jaraco/functools/__init__.pyi @@ -0,0 +1,128 @@ +from collections.abc import Callable, Hashable, Iterator +from functools import partial +from operator import methodcaller +import sys +from typing import ( + Any, + Generic, + Protocol, + TypeVar, + overload, +) + +if sys.version_info >= (3, 10): + from typing import Concatenate, ParamSpec +else: + from typing_extensions import Concatenate, ParamSpec + +_P = ParamSpec('_P') +_R = TypeVar('_R') +_T = TypeVar('_T') +_R1 = TypeVar('_R1') +_R2 = TypeVar('_R2') +_V = TypeVar('_V') +_S = TypeVar('_S') +_R_co = TypeVar('_R_co', covariant=True) + +class _OnceCallable(Protocol[_P, _R]): + saved_result: _R + reset: Callable[[], None] + def __call__(self, *args: _P.args, **kwargs: _P.kwargs) -> _R: ... + +class _ProxyMethodCacheWrapper(Protocol[_R_co]): + cache_clear: Callable[[], None] + def __call__(self, *args: Hashable, **kwargs: Hashable) -> _R_co: ... + +class _MethodCacheWrapper(Protocol[_R_co]): + def cache_clear(self) -> None: ... + def __call__(self, *args: Hashable, **kwargs: Hashable) -> _R_co: ... + +# `compose()` overloads below will cover most use cases. + +@overload +def compose( + __func1: Callable[[_R], _T], + __func2: Callable[_P, _R], + /, +) -> Callable[_P, _T]: ... +@overload +def compose( + __func1: Callable[[_R], _T], + __func2: Callable[[_R1], _R], + __func3: Callable[_P, _R1], + /, +) -> Callable[_P, _T]: ... +@overload +def compose( + __func1: Callable[[_R], _T], + __func2: Callable[[_R2], _R], + __func3: Callable[[_R1], _R2], + __func4: Callable[_P, _R1], + /, +) -> Callable[_P, _T]: ... +def once(func: Callable[_P, _R]) -> _OnceCallable[_P, _R]: ... +def method_cache( + method: Callable[..., _R], + cache_wrapper: Callable[[Callable[..., _R]], _MethodCacheWrapper[_R]] = ..., +) -> _MethodCacheWrapper[_R] | _ProxyMethodCacheWrapper[_R]: ... +def apply( + transform: Callable[[_R], _T] +) -> Callable[[Callable[_P, _R]], Callable[_P, _T]]: ... +def result_invoke( + action: Callable[[_R], Any] +) -> Callable[[Callable[_P, _R]], Callable[_P, _R]]: ... +def invoke( + f: Callable[_P, _R], /, *args: _P.args, **kwargs: _P.kwargs +) -> Callable[_P, _R]: ... +def call_aside( + f: Callable[_P, _R], *args: _P.args, **kwargs: _P.kwargs +) -> Callable[_P, _R]: ... + +class Throttler(Generic[_R]): + last_called: float + func: Callable[..., _R] + max_rate: float + def __init__( + self, func: Callable[..., _R] | Throttler[_R], max_rate: float = ... + ) -> None: ... + def reset(self) -> None: ... + def __call__(self, *args: Any, **kwargs: Any) -> _R: ... + def __get__(self, obj: Any, owner: type[Any] | None = ...) -> Callable[..., _R]: ... + +def first_invoke( + func1: Callable[..., Any], func2: Callable[_P, _R] +) -> Callable[_P, _R]: ... + +method_caller: Callable[..., methodcaller] + +def retry_call( + func: Callable[..., _R], + cleanup: Callable[..., None] = ..., + retries: int | float = ..., + trap: type[BaseException] | tuple[type[BaseException], ...] = ..., +) -> _R: ... +def retry( + cleanup: Callable[..., None] = ..., + retries: int | float = ..., + trap: type[BaseException] | tuple[type[BaseException], ...] = ..., +) -> Callable[[Callable[..., _R]], Callable[..., _R]]: ... +def print_yielded(func: Callable[_P, Iterator[Any]]) -> Callable[_P, None]: ... +def pass_none( + func: Callable[Concatenate[_T, _P], _R] +) -> Callable[Concatenate[_T, _P], _R]: ... +def assign_params( + func: Callable[..., _R], namespace: dict[str, Any] +) -> partial[_R]: ... +def save_method_args( + method: Callable[Concatenate[_S, _P], _R] +) -> Callable[Concatenate[_S, _P], _R]: ... +def except_( + *exceptions: type[BaseException], replace: Any = ..., use: Any = ... +) -> Callable[[Callable[_P, Any]], Callable[_P, Any]]: ... +def identity(x: _T) -> _T: ... +def bypass_when( + check: _V, *, _op: Callable[[_V], Any] = ... +) -> Callable[[Callable[[_T], _R]], Callable[[_T], _T | _R]]: ... +def bypass_unless( + check: Any, +) -> Callable[[Callable[[_T], _R]], Callable[[_T], _T | _R]]: ... diff --git a/venv/Lib/site-packages/pkg_resources/_vendor/jaraco/functools/__pycache__/__init__.cpython-312.pyc b/venv/Lib/site-packages/pkg_resources/_vendor/jaraco/functools/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..ab4a5367885243e110eee43000f7a34798c1cdda GIT binary patch literal 23025 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%0Dia?Yk8}_Chjb)8!L~3)-au@ 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 multi_substitution(('foo', 'bar'), ('bar', 'baz'))('foo') + 'baz' + """ + substitutions = itertools.starmap(substitution, substitutions) + # compose function applies last function first, so reverse the + # substitutions to get the expected order. + substitutions = reversed(tuple(substitutions)) + return compose(*substitutions) + + +class FoldedCase(str): + """ + A case insensitive string class; behaves just like str + except compares equal when the only variation is case. + + >>> s = FoldedCase('hello world') + + >>> s == 'Hello World' + True + + >>> 'Hello World' == s + True + + >>> s != 'Hello World' + False + + >>> s.index('O') + 4 + + >>> s.split('O') + ['hell', ' w', 'rld'] + + >>> sorted(map(FoldedCase, ['GAMMA', 'alpha', 'Beta'])) + ['alpha', 'Beta', 'GAMMA'] + + Sequence membership is straightforward. + + >>> "Hello World" in [s] + True + >>> s in ["Hello World"] + True + + You may test for set inclusion, but candidate and elements + must both be folded. + + >>> FoldedCase("Hello World") in {s} + True + >>> s in {FoldedCase("Hello World")} + True + + String inclusion works as long as the FoldedCase object + is on the right. + + >>> "hello" in FoldedCase("Hello World") + True + + But not if the FoldedCase object is on the left: + + >>> FoldedCase('hello') in 'Hello World' + False + + In that case, use ``in_``: + + >>> FoldedCase('hello').in_('Hello World') + True + + >>> FoldedCase('hello') > FoldedCase('Hello') + False + """ + + def __lt__(self, other): + return self.lower() < other.lower() + + def __gt__(self, other): + return self.lower() > other.lower() + + def __eq__(self, other): + return self.lower() == other.lower() + + def __ne__(self, other): + return self.lower() != other.lower() + + def __hash__(self): + return hash(self.lower()) + + def __contains__(self, other): + return super().lower().__contains__(other.lower()) + + def in_(self, other): + "Does self appear in other?" + return self in FoldedCase(other) + + # cache lower since it's likely to be called frequently. + @method_cache + def lower(self): + return super().lower() + + def index(self, sub): + return self.lower().index(sub.lower()) + + def split(self, splitter=' ', maxsplit=0): + pattern = re.compile(re.escape(splitter), re.I) + return pattern.split(self, maxsplit) + + +# Python 3.8 compatibility +_unicode_trap = ExceptionTrap(UnicodeDecodeError) + + +@_unicode_trap.passes +def is_decodable(value): + r""" + Return True if the supplied value is decodable (using the default + encoding). + + >>> is_decodable(b'\xff') + False + >>> is_decodable(b'\x32') + True + """ + value.decode() + + +def is_binary(value): + r""" + Return True if the value appears to be binary (that is, it's a byte + string and isn't decodable). + + >>> is_binary(b'\xff') + True + >>> is_binary('\xff') + False + """ + return isinstance(value, bytes) and not is_decodable(value) + + +def trim(s): + r""" + Trim something like a docstring to remove the whitespace that + is common due to indentation and formatting. + + >>> trim("\n\tfoo = bar\n\t\tbar = baz\n") + 'foo = bar\n\tbar = baz' + """ + return textwrap.dedent(s).strip() + + +def wrap(s): + """ + Wrap lines of text, retaining existing newlines as + paragraph markers. + + >>> print(wrap(lorem_ipsum)) + Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do + eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad + minim veniam, quis nostrud exercitation ullamco laboris nisi ut + aliquip ex ea commodo consequat. Duis aute irure dolor in + reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla + pariatur. Excepteur sint occaecat cupidatat non proident, sunt in + culpa qui officia deserunt mollit anim id est laborum. + + Curabitur pretium tincidunt lacus. Nulla gravida orci a odio. Nullam + varius, turpis et commodo pharetra, est eros bibendum elit, nec luctus + magna felis sollicitudin mauris. Integer in mauris eu nibh euismod + gravida. Duis ac tellus et risus vulputate vehicula. Donec lobortis + risus a elit. Etiam tempor. Ut ullamcorper, ligula eu tempor congue, + eros est euismod turpis, id tincidunt sapien risus a quam. Maecenas + fermentum consequat mi. Donec fermentum. Pellentesque malesuada nulla + a mi. Duis sapien sem, aliquet nec, commodo eget, consequat quis, + neque. Aliquam faucibus, elit ut dictum aliquet, felis nisl adipiscing + sapien, sed malesuada diam lacus eget erat. Cras mollis scelerisque + nunc. Nullam arcu. Aliquam consequat. Curabitur augue lorem, dapibus + quis, laoreet et, pretium ac, nisi. Aenean magna nisl, mollis quis, + molestie eu, feugiat in, orci. In hac habitasse platea dictumst. + """ + paragraphs = s.splitlines() + wrapped = ('\n'.join(textwrap.wrap(para)) for para in paragraphs) + return '\n\n'.join(wrapped) + + +def unwrap(s): + r""" + Given a multi-line string, return an unwrapped version. + + >>> wrapped = wrap(lorem_ipsum) + >>> wrapped.count('\n') + 20 + >>> unwrapped = unwrap(wrapped) + >>> unwrapped.count('\n') + 1 + >>> print(unwrapped) + Lorem ipsum dolor sit amet, consectetur adipiscing ... + Curabitur pretium tincidunt lacus. Nulla gravida orci ... + + """ + paragraphs = re.split(r'\n\n+', s) + cleaned = (para.replace('\n', ' ') for para in paragraphs) + return '\n'.join(cleaned) + + + + +class Splitter(object): + """object that will split a string with the given arguments for each call + + >>> s = Splitter(',') + >>> s('hello, world, this is your, master calling') + ['hello', ' world', ' this is your', ' master calling'] + """ + + def __init__(self, *args): + self.args = args + + def __call__(self, s): + return s.split(*self.args) + + +def indent(string, prefix=' ' * 4): + """ + >>> indent('foo') + ' foo' + """ + return prefix + string + + +class WordSet(tuple): + """ + Given an identifier, return the words that identifier represents, + whether in camel case, underscore-separated, etc. + + >>> WordSet.parse("camelCase") + ('camel', 'Case') + + >>> WordSet.parse("under_sep") + ('under', 'sep') + + Acronyms should be retained + + >>> WordSet.parse("firstSNL") + ('first', 'SNL') + + >>> WordSet.parse("you_and_I") + ('you', 'and', 'I') + + >>> WordSet.parse("A simple test") + ('A', 'simple', 'test') + + Multiple caps should not interfere with the first cap of another word. + + >>> WordSet.parse("myABCClass") + ('my', 'ABC', 'Class') + + The result is a WordSet, so you can get the form you need. + + >>> WordSet.parse("myABCClass").underscore_separated() + 'my_ABC_Class' + + >>> WordSet.parse('a-command').camel_case() + 'ACommand' + + >>> WordSet.parse('someIdentifier').lowered().space_separated() + 'some identifier' + + Slices of the result should return another WordSet. + + >>> WordSet.parse('taken-out-of-context')[1:].underscore_separated() + 'out_of_context' + + >>> WordSet.from_class_name(WordSet()).lowered().space_separated() + 'word set' + + >>> example = WordSet.parse('figured it out') + >>> example.headless_camel_case() + 'figuredItOut' + >>> example.dash_separated() + 'figured-it-out' + + """ + + _pattern = re.compile('([A-Z]?[a-z]+)|([A-Z]+(?![a-z]))') + + def capitalized(self): + return WordSet(word.capitalize() for word in self) + + def lowered(self): + return WordSet(word.lower() for word in self) + + def camel_case(self): + return ''.join(self.capitalized()) + + def headless_camel_case(self): + words = iter(self) + first = next(words).lower() + new_words = itertools.chain((first,), WordSet(words).camel_case()) + return ''.join(new_words) + + def underscore_separated(self): + return '_'.join(self) + + def dash_separated(self): + return '-'.join(self) + + def space_separated(self): + return ' '.join(self) + + def trim_right(self, item): + """ + Remove the item from the end of the set. + + >>> WordSet.parse('foo bar').trim_right('foo') + ('foo', 'bar') + >>> WordSet.parse('foo bar').trim_right('bar') + ('foo',) + >>> WordSet.parse('').trim_right('bar') + () + """ + return self[:-1] if self and self[-1] == item else self + + def trim_left(self, item): + """ + Remove the item from the beginning of the set. + + >>> WordSet.parse('foo bar').trim_left('foo') + ('bar',) + >>> WordSet.parse('foo bar').trim_left('bar') + ('foo', 'bar') + >>> WordSet.parse('').trim_left('bar') + () + """ + return self[1:] if self and self[0] == item else self + + def trim(self, item): + """ + >>> WordSet.parse('foo bar').trim('foo') + ('bar',) + """ + return self.trim_left(item).trim_right(item) + + def __getitem__(self, item): + result = super(WordSet, self).__getitem__(item) + if isinstance(item, slice): + result = WordSet(result) + return result + + @classmethod + def parse(cls, identifier): + matches = cls._pattern.finditer(identifier) + return WordSet(match.group(0) for match in matches) + + @classmethod + def from_class_name(cls, subject): + return cls.parse(subject.__class__.__name__) + + +# for backward compatibility +words = WordSet.parse + + +def simple_html_strip(s): + r""" + Remove HTML from the string `s`. + + >>> str(simple_html_strip('')) + '' + + >>> print(simple_html_strip('A stormy day in paradise')) + A stormy day in paradise + + >>> print(simple_html_strip('Somebody tell the truth.')) + Somebody tell the truth. + + >>> print(simple_html_strip('What about
\nmultiple lines?')) + What about + multiple lines? + """ + html_stripper = re.compile('()|(<[^>]*>)|([^<]+)', re.DOTALL) + texts = (match.group(3) or '' for match in html_stripper.finditer(s)) + return ''.join(texts) + + +class SeparatedValues(str): + """ + A string separated by a separator. Overrides __iter__ for getting + the values. + + >>> list(SeparatedValues('a,b,c')) + ['a', 'b', 'c'] + + Whitespace is stripped and empty values are discarded. + + >>> list(SeparatedValues(' a, b , c, ')) + ['a', 'b', 'c'] + """ + + separator = ',' + + def __iter__(self): + parts = self.split(self.separator) + return filter(None, (part.strip() for part in parts)) + + +class Stripper: + r""" + Given a series of lines, find the common prefix and strip it from them. + + >>> lines = [ + ... 'abcdefg\n', + ... 'abc\n', + ... 'abcde\n', + ... ] + >>> res = Stripper.strip_prefix(lines) + >>> res.prefix + 'abc' + >>> list(res.lines) + ['defg\n', '\n', 'de\n'] + + If no prefix is common, nothing should be stripped. + + >>> lines = [ + ... 'abcd\n', + ... '1234\n', + ... ] + >>> res = Stripper.strip_prefix(lines) + >>> res.prefix = '' + >>> list(res.lines) + ['abcd\n', '1234\n'] + """ + + def __init__(self, prefix, lines): + self.prefix = prefix + self.lines = map(self, lines) + + @classmethod + def strip_prefix(cls, lines): + prefix_lines, lines = itertools.tee(lines) + prefix = functools.reduce(cls.common_prefix, prefix_lines) + return cls(prefix, lines) + + def __call__(self, line): + if not self.prefix: + return line + null, prefix, rest = line.partition(self.prefix) + return rest + + @staticmethod + def common_prefix(s1, s2): + """ + Return the common prefix of two lines. + """ + index = min(len(s1), len(s2)) + while s1[:index] != s2[:index]: + index -= 1 + return s1[:index] + + +def remove_prefix(text, prefix): + """ + Remove the prefix from the text if it exists. + + >>> remove_prefix('underwhelming performance', 'underwhelming ') + 'performance' + + >>> remove_prefix('something special', 'sample') + 'something special' + """ + null, prefix, rest = text.rpartition(prefix) + return rest + + +def remove_suffix(text, suffix): + """ + Remove the suffix from the text if it exists. + + >>> remove_suffix('name.git', '.git') + 'name' + + >>> remove_suffix('something special', 'sample') + 'something special' + """ + rest, suffix, null = text.partition(suffix) + return rest + + +def normalize_newlines(text): + r""" + Replace alternate newlines with the canonical newline. + + >>> normalize_newlines('Lorem Ipsum\u2029') + 'Lorem Ipsum\n' + >>> normalize_newlines('Lorem Ipsum\r\n') + 'Lorem Ipsum\n' + >>> normalize_newlines('Lorem Ipsum\x85') + 'Lorem Ipsum\n' + """ + newlines = ['\r\n', '\r', '\n', '\u0085', '\u2028', '\u2029'] + pattern = '|'.join(newlines) + return re.sub(pattern, '\n', text) + + +def _nonblank(str): + return str and not str.startswith('#') + + +@functools.singledispatch +def yield_lines(iterable): + r""" + Yield valid lines of a string or iterable. + + >>> list(yield_lines('')) + [] + >>> list(yield_lines(['foo', 'bar'])) + ['foo', 'bar'] + >>> list(yield_lines('foo\nbar')) + ['foo', 'bar'] + >>> list(yield_lines('\nfoo\n#bar\nbaz #comment')) + ['foo', 'baz #comment'] + >>> list(yield_lines(['foo\nbar', 'baz', 'bing\n\n\n'])) + ['foo', 'bar', 'baz', 'bing'] + """ + return itertools.chain.from_iterable(map(yield_lines, iterable)) + + +@yield_lines.register(str) +def _(text): + return filter(_nonblank, map(str.strip, text.splitlines())) + + +def drop_comment(line): + """ + Drop comments. + + >>> drop_comment('foo # bar') + 'foo' + + A hash without a space may be in a URL. + + >>> drop_comment('http://example.com/foo#bar') + 'http://example.com/foo#bar' + """ + return line.partition(' #')[0] + + +def join_continuation(lines): + r""" + Join lines continued by a trailing backslash. + + >>> list(join_continuation(['foo \\', 'bar', 'baz'])) + ['foobar', 'baz'] + >>> list(join_continuation(['foo \\', 'bar', 'baz'])) + ['foobar', 'baz'] + >>> list(join_continuation(['foo \\', 'bar \\', 'baz'])) + ['foobarbaz'] + + Not sure why, but... + The character preceeding the backslash is also elided. + + >>> list(join_continuation(['goo\\', 'dly'])) + ['godly'] + + A terrible idea, but... + If no line is available to continue, suppress the lines. + + >>> list(join_continuation(['foo', 'bar\\', 'baz\\'])) + ['foo'] + """ + lines = iter(lines) + for item in lines: + while item.endswith('\\'): + try: + item = item[:-2].strip() + next(lines) + except StopIteration: + return + yield item diff --git a/venv/Lib/site-packages/pkg_resources/_vendor/jaraco/text/__pycache__/__init__.cpython-312.pyc b/venv/Lib/site-packages/pkg_resources/_vendor/jaraco/text/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..9d78abdc4ecab07e678e766018713d5d362f1e45 GIT binary patch literal 24461 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 J`&&e${|7ut6;J>G literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/pkg_resources/_vendor/more_itertools/__init__.py b/venv/Lib/site-packages/pkg_resources/_vendor/more_itertools/__init__.py new file mode 100644 index 0000000..aff94a9 --- /dev/null +++ b/venv/Lib/site-packages/pkg_resources/_vendor/more_itertools/__init__.py @@ -0,0 +1,6 @@ +"""More routines for operating on iterables, beyond itertools""" + +from .more import * # noqa +from .recipes import * # noqa + +__version__ = '10.2.0' diff --git a/venv/Lib/site-packages/pkg_resources/_vendor/more_itertools/__init__.pyi b/venv/Lib/site-packages/pkg_resources/_vendor/more_itertools/__init__.pyi new file mode 100644 index 0000000..96f6e36 --- /dev/null +++ b/venv/Lib/site-packages/pkg_resources/_vendor/more_itertools/__init__.pyi @@ -0,0 +1,2 @@ +from .more import * +from .recipes import * diff --git a/venv/Lib/site-packages/pkg_resources/_vendor/more_itertools/__pycache__/__init__.cpython-312.pyc b/venv/Lib/site-packages/pkg_resources/_vendor/more_itertools/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..9d0522cd8180c2f5d43c8229fa81bd543e91cc37 GIT binary patch literal 376 zcmXw!u};G<6h-Z%P@pPHrOsZ9NLv^ygpkSzs0&MF$!g-)%4+Ht*-23&G4K(HZ(!g{ z_yQsZ5G-s+-8$g}c*FC(ckn&$x!K$!Zp?cyy?5_NY5vFi6Km$g1CeA*B$ZxFpFH}j zyh{0Fe^u*j<%3H!zzk-o(qP#X4Z{S?#Mv1`%~a}47=t}zA*@i#DoGJz`$b(vhZgi8 z-#+ShkGuV=POYd5Au$r7s4d*jMcqK860k)>h$R@SPz&)$)+OfVb~xbI)}__v=NM%R z(5dA^l&+tTRFq~0f>D;|F$6adTF1kepqKnYg}ih@GKuDs8CagoXTpHR%tT;&u1!lc zy^=zdS5(YBA(U3B5Zz?8**RID%wjk-4M$U6V_R;0`;^iba`s7DucZC9-@a>@U6=m= DUPX1r literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/pkg_resources/_vendor/more_itertools/__pycache__/more.cpython-312.pyc b/venv/Lib/site-packages/pkg_resources/_vendor/more_itertools/__pycache__/more.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..c4754043c0b60d14b6482984489c88a688ea20fd GIT binary patch literal 167473 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 Ihl=w50^FZ2LjV8( literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/pkg_resources/_vendor/more_itertools/more.py b/venv/Lib/site-packages/pkg_resources/_vendor/more_itertools/more.py new file mode 100644 index 0000000..d095768 --- /dev/null +++ b/venv/Lib/site-packages/pkg_resources/_vendor/more_itertools/more.py @@ -0,0 +1,4655 @@ +import warnings + +from collections import Counter, defaultdict, deque, abc +from collections.abc import Sequence +from functools import cached_property, partial, reduce, wraps +from heapq import heapify, heapreplace, heappop +from itertools import ( + chain, + compress, + count, + cycle, + dropwhile, + groupby, + islice, + repeat, + starmap, + takewhile, + tee, + zip_longest, + product, +) +from math import exp, factorial, floor, log, perm, comb +from queue import Empty, Queue +from random import random, randrange, uniform +from operator import itemgetter, mul, sub, gt, lt, ge, le +from sys import hexversion, maxsize +from time import monotonic + +from .recipes import ( + _marker, + _zip_equal, + UnequalIterablesError, + consume, + flatten, + pairwise, + powerset, + take, + unique_everseen, + all_equal, + batched, +) + +__all__ = [ + 'AbortThread', + 'SequenceView', + 'UnequalIterablesError', + 'adjacent', + 'all_unique', + 'always_iterable', + 'always_reversible', + 'bucket', + 'callback_iter', + 'chunked', + 'chunked_even', + 'circular_shifts', + 'collapse', + 'combination_index', + 'combination_with_replacement_index', + 'consecutive_groups', + 'constrained_batches', + 'consumer', + 'count_cycle', + 'countable', + 'difference', + 'distinct_combinations', + 'distinct_permutations', + 'distribute', + 'divide', + 'duplicates_everseen', + 'duplicates_justseen', + 'classify_unique', + 'exactly_n', + 'filter_except', + 'filter_map', + 'first', + 'gray_product', + 'groupby_transform', + 'ichunked', + 'iequals', + 'ilen', + 'interleave', + 'interleave_evenly', + 'interleave_longest', + 'intersperse', + 'is_sorted', + 'islice_extended', + 'iterate', + 'iter_suppress', + 'last', + 'locate', + 'longest_common_prefix', + 'lstrip', + 'make_decorator', + 'map_except', + 'map_if', + 'map_reduce', + 'mark_ends', + 'minmax', + 'nth_or_last', + 'nth_permutation', + 'nth_product', + 'nth_combination_with_replacement', + 'numeric_range', + 'one', + 'only', + 'outer_product', + 'padded', + 'partial_product', + 'partitions', + 'peekable', + 'permutation_index', + 'product_index', + 'raise_', + 'repeat_each', + 'repeat_last', + 'replace', + 'rlocate', + 'rstrip', + 'run_length', + 'sample', + 'seekable', + 'set_partitions', + 'side_effect', + 'sliced', + 'sort_together', + 'split_after', + 'split_at', + 'split_before', + 'split_into', + 'split_when', + 'spy', + 'stagger', + 'strip', + 'strictly_n', + 'substrings', + 'substrings_indexes', + 'takewhile_inclusive', + 'time_limited', + 'unique_in_window', + 'unique_to_each', + 'unzip', + 'value_chain', + 'windowed', + 'windowed_complete', + 'with_iter', + 'zip_broadcast', + 'zip_equal', + 'zip_offset', +] + + +def chunked(iterable, n, strict=False): + """Break *iterable* into lists of length *n*: + + >>> list(chunked([1, 2, 3, 4, 5, 6], 3)) + [[1, 2, 3], [4, 5, 6]] + + By the default, the last yielded list will have fewer than *n* elements + if the length of *iterable* is not divisible by *n*: + + >>> list(chunked([1, 2, 3, 4, 5, 6, 7, 8], 3)) + [[1, 2, 3], [4, 5, 6], [7, 8]] + + To use a fill-in value instead, see the :func:`grouper` recipe. + + If the length of *iterable* is not divisible by *n* and *strict* is + ``True``, then ``ValueError`` will be raised before the last + list is yielded. + + """ + iterator = iter(partial(take, n, iter(iterable)), []) + if strict: + if n is None: + raise ValueError('n must not be None when using strict mode.') + + def ret(): + for chunk in iterator: + if len(chunk) != n: + raise ValueError('iterable is not divisible by n.') + yield chunk + + return iter(ret()) + else: + return iterator + + +def first(iterable, default=_marker): + """Return the first item of *iterable*, or *default* if *iterable* is + empty. + + >>> first([0, 1, 2, 3]) + 0 + >>> first([], 'some default') + 'some default' + + If *default* is not provided and there are no items in the iterable, + raise ``ValueError``. + + :func:`first` is useful when you have a generator of expensive-to-retrieve + values and want any arbitrary one. It is marginally shorter than + ``next(iter(iterable), default)``. + + """ + for item in iterable: + return item + if default is _marker: + raise ValueError( + 'first() was called on an empty iterable, and no ' + 'default value was provided.' + ) + return default + + +def last(iterable, default=_marker): + """Return the last item of *iterable*, or *default* if *iterable* is + empty. + + >>> last([0, 1, 2, 3]) + 3 + >>> last([], 'some default') + 'some default' + + If *default* is not provided and there are no items in the iterable, + raise ``ValueError``. + """ + try: + if isinstance(iterable, Sequence): + return iterable[-1] + # Work around https://bugs.python.org/issue38525 + elif hasattr(iterable, '__reversed__') and (hexversion != 0x030800F0): + return next(reversed(iterable)) + else: + return deque(iterable, maxlen=1)[-1] + except (IndexError, TypeError, StopIteration): + if default is _marker: + raise ValueError( + 'last() was called on an empty iterable, and no default was ' + 'provided.' + ) + return default + + +def nth_or_last(iterable, n, default=_marker): + """Return the nth or the last item of *iterable*, + or *default* if *iterable* is empty. + + >>> nth_or_last([0, 1, 2, 3], 2) + 2 + >>> nth_or_last([0, 1], 2) + 1 + >>> nth_or_last([], 0, 'some default') + 'some default' + + If *default* is not provided and there are no items in the iterable, + raise ``ValueError``. + """ + return last(islice(iterable, n + 1), default=default) + + +class peekable: + """Wrap an iterator to allow lookahead and prepending elements. + + Call :meth:`peek` on the result to get the value that will be returned + by :func:`next`. This won't advance the iterator: + + >>> p = peekable(['a', 'b']) + >>> p.peek() + 'a' + >>> next(p) + 'a' + + Pass :meth:`peek` a default value to return that instead of raising + ``StopIteration`` when the iterator is exhausted. + + >>> p = peekable([]) + >>> p.peek('hi') + 'hi' + + peekables also offer a :meth:`prepend` method, which "inserts" items + at the head of the iterable: + + >>> p = peekable([1, 2, 3]) + >>> p.prepend(10, 11, 12) + >>> next(p) + 10 + >>> p.peek() + 11 + >>> list(p) + [11, 12, 1, 2, 3] + + peekables can be indexed. Index 0 is the item that will be returned by + :func:`next`, index 1 is the item after that, and so on: + The values up to the given index will be cached. + + >>> p = peekable(['a', 'b', 'c', 'd']) + >>> p[0] + 'a' + >>> p[1] + 'b' + >>> next(p) + 'a' + + Negative indexes are supported, but be aware that they will cache the + remaining items in the source iterator, which may require significant + storage. + + To check whether a peekable is exhausted, check its truth value: + + >>> p = peekable(['a', 'b']) + >>> if p: # peekable has items + ... list(p) + ['a', 'b'] + >>> if not p: # peekable is exhausted + ... list(p) + [] + + """ + + def __init__(self, iterable): + self._it = iter(iterable) + self._cache = deque() + + def __iter__(self): + return self + + def __bool__(self): + try: + self.peek() + except StopIteration: + return False + return True + + def peek(self, default=_marker): + """Return the item that will be next returned from ``next()``. + + Return ``default`` if there are no items left. If ``default`` is not + provided, raise ``StopIteration``. + + """ + if not self._cache: + try: + self._cache.append(next(self._it)) + except StopIteration: + if default is _marker: + raise + return default + return self._cache[0] + + def prepend(self, *items): + """Stack up items to be the next ones returned from ``next()`` or + ``self.peek()``. The items will be returned in + first in, first out order:: + + >>> p = peekable([1, 2, 3]) + >>> p.prepend(10, 11, 12) + >>> next(p) + 10 + >>> list(p) + [11, 12, 1, 2, 3] + + It is possible, by prepending items, to "resurrect" a peekable that + previously raised ``StopIteration``. + + >>> p = peekable([]) + >>> next(p) + Traceback (most recent call last): + ... + StopIteration + >>> p.prepend(1) + >>> next(p) + 1 + >>> next(p) + Traceback (most recent call last): + ... + StopIteration + + """ + self._cache.extendleft(reversed(items)) + + def __next__(self): + if self._cache: + return self._cache.popleft() + + return next(self._it) + + def _get_slice(self, index): + # Normalize the slice's arguments + step = 1 if (index.step is None) else index.step + if step > 0: + start = 0 if (index.start is None) else index.start + stop = maxsize if (index.stop is None) else index.stop + elif step < 0: + start = -1 if (index.start is None) else index.start + stop = (-maxsize - 1) if (index.stop is None) else index.stop + else: + raise ValueError('slice step cannot be zero') + + # If either the start or stop index is negative, we'll need to cache + # the rest of the iterable in order to slice from the right side. + if (start < 0) or (stop < 0): + self._cache.extend(self._it) + # Otherwise we'll need to find the rightmost index and cache to that + # point. + else: + n = min(max(start, stop) + 1, maxsize) + cache_len = len(self._cache) + if n >= cache_len: + self._cache.extend(islice(self._it, n - cache_len)) + + return list(self._cache)[index] + + def __getitem__(self, index): + if isinstance(index, slice): + return self._get_slice(index) + + cache_len = len(self._cache) + if index < 0: + self._cache.extend(self._it) + elif index >= cache_len: + self._cache.extend(islice(self._it, index + 1 - cache_len)) + + return self._cache[index] + + +def consumer(func): + """Decorator that automatically advances a PEP-342-style "reverse iterator" + to its first yield point so you don't have to call ``next()`` on it + manually. + + >>> @consumer + ... def tally(): + ... i = 0 + ... while True: + ... print('Thing number %s is %s.' % (i, (yield))) + ... i += 1 + ... + >>> t = tally() + >>> t.send('red') + Thing number 0 is red. + >>> t.send('fish') + Thing number 1 is fish. + + Without the decorator, you would have to call ``next(t)`` before + ``t.send()`` could be used. + + """ + + @wraps(func) + def wrapper(*args, **kwargs): + gen = func(*args, **kwargs) + next(gen) + return gen + + return wrapper + + +def ilen(iterable): + """Return the number of items in *iterable*. + + >>> ilen(x for x in range(1000000) if x % 3 == 0) + 333334 + + This consumes the iterable, so handle with care. + + """ + # This approach was selected because benchmarks showed it's likely the + # fastest of the known implementations at the time of writing. + # See GitHub tracker: #236, #230. + counter = count() + deque(zip(iterable, counter), maxlen=0) + return next(counter) + + +def iterate(func, start): + """Return ``start``, ``func(start)``, ``func(func(start))``, ... + + >>> from itertools import islice + >>> list(islice(iterate(lambda x: 2*x, 1), 10)) + [1, 2, 4, 8, 16, 32, 64, 128, 256, 512] + + """ + while True: + yield start + try: + start = func(start) + except StopIteration: + break + + +def with_iter(context_manager): + """Wrap an iterable in a ``with`` statement, so it closes once exhausted. + + For example, this will close the file when the iterator is exhausted:: + + upper_lines = (line.upper() for line in with_iter(open('foo'))) + + Any context manager which returns an iterable is a candidate for + ``with_iter``. + + """ + with context_manager as iterable: + yield from iterable + + +def one(iterable, too_short=None, too_long=None): + """Return the first item from *iterable*, which is expected to contain only + that item. Raise an exception if *iterable* is empty or has more than one + item. + + :func:`one` is useful for ensuring that an iterable contains only one item. + For example, it can be used to retrieve the result of a database query + that is expected to return a single row. + + If *iterable* is empty, ``ValueError`` will be raised. You may specify a + different exception with the *too_short* keyword: + + >>> it = [] + >>> one(it) # doctest: +IGNORE_EXCEPTION_DETAIL + Traceback (most recent call last): + ... + ValueError: too many items in iterable (expected 1)' + >>> too_short = IndexError('too few items') + >>> one(it, too_short=too_short) # doctest: +IGNORE_EXCEPTION_DETAIL + Traceback (most recent call last): + ... + IndexError: too few items + + Similarly, if *iterable* contains more than one item, ``ValueError`` will + be raised. You may specify a different exception with the *too_long* + keyword: + + >>> it = ['too', 'many'] + >>> one(it) # doctest: +IGNORE_EXCEPTION_DETAIL + Traceback (most recent call last): + ... + ValueError: Expected exactly one item in iterable, but got 'too', + 'many', and perhaps more. + >>> too_long = RuntimeError + >>> one(it, too_long=too_long) # doctest: +IGNORE_EXCEPTION_DETAIL + Traceback (most recent call last): + ... + RuntimeError + + Note that :func:`one` attempts to advance *iterable* twice to ensure there + is only one item. See :func:`spy` or :func:`peekable` to check iterable + contents less destructively. + + """ + it = iter(iterable) + + try: + first_value = next(it) + except StopIteration as e: + raise ( + too_short or ValueError('too few items in iterable (expected 1)') + ) from e + + try: + second_value = next(it) + except StopIteration: + pass + else: + msg = ( + 'Expected exactly one item in iterable, but got {!r}, {!r}, ' + 'and perhaps more.'.format(first_value, second_value) + ) + raise too_long or ValueError(msg) + + return first_value + + +def raise_(exception, *args): + raise exception(*args) + + +def strictly_n(iterable, n, too_short=None, too_long=None): + """Validate that *iterable* has exactly *n* items and return them if + it does. If it has fewer than *n* items, call function *too_short* + with those items. If it has more than *n* items, call function + *too_long* with the first ``n + 1`` items. + + >>> iterable = ['a', 'b', 'c', 'd'] + >>> n = 4 + >>> list(strictly_n(iterable, n)) + ['a', 'b', 'c', 'd'] + + Note that the returned iterable must be consumed in order for the check to + be made. + + By default, *too_short* and *too_long* are functions that raise + ``ValueError``. + + >>> list(strictly_n('ab', 3)) # doctest: +IGNORE_EXCEPTION_DETAIL + Traceback (most recent call last): + ... + ValueError: too few items in iterable (got 2) + + >>> list(strictly_n('abc', 2)) # doctest: +IGNORE_EXCEPTION_DETAIL + Traceback (most recent call last): + ... + ValueError: too many items in iterable (got at least 3) + + You can instead supply functions that do something else. + *too_short* will be called with the number of items in *iterable*. + *too_long* will be called with `n + 1`. + + >>> def too_short(item_count): + ... raise RuntimeError + >>> it = strictly_n('abcd', 6, too_short=too_short) + >>> list(it) # doctest: +IGNORE_EXCEPTION_DETAIL + Traceback (most recent call last): + ... + RuntimeError + + >>> def too_long(item_count): + ... print('The boss is going to hear about this') + >>> it = strictly_n('abcdef', 4, too_long=too_long) + >>> list(it) + The boss is going to hear about this + ['a', 'b', 'c', 'd'] + + """ + if too_short is None: + too_short = lambda item_count: raise_( + ValueError, + 'Too few items in iterable (got {})'.format(item_count), + ) + + if too_long is None: + too_long = lambda item_count: raise_( + ValueError, + 'Too many items in iterable (got at least {})'.format(item_count), + ) + + it = iter(iterable) + for i in range(n): + try: + item = next(it) + except StopIteration: + too_short(i) + return + else: + yield item + + try: + next(it) + except StopIteration: + pass + else: + too_long(n + 1) + + +def distinct_permutations(iterable, r=None): + """Yield successive distinct permutations of the elements in *iterable*. + + >>> sorted(distinct_permutations([1, 0, 1])) + [(0, 1, 1), (1, 0, 1), (1, 1, 0)] + + Equivalent to ``set(permutations(iterable))``, except duplicates are not + generated and thrown away. For larger input sequences this is much more + efficient. + + Duplicate permutations arise when there are duplicated elements in the + input iterable. The number of items returned is + `n! / (x_1! * x_2! * ... * x_n!)`, where `n` is the total number of + items input, and each `x_i` is the count of a distinct item in the input + sequence. + + If *r* is given, only the *r*-length permutations are yielded. + + >>> sorted(distinct_permutations([1, 0, 1], r=2)) + [(0, 1), (1, 0), (1, 1)] + >>> sorted(distinct_permutations(range(3), r=2)) + [(0, 1), (0, 2), (1, 0), (1, 2), (2, 0), (2, 1)] + + """ + + # Algorithm: https://w.wiki/Qai + def _full(A): + while True: + # Yield the permutation we have + yield tuple(A) + + # Find the largest index i such that A[i] < A[i + 1] + for i in range(size - 2, -1, -1): + if A[i] < A[i + 1]: + break + # If no such index exists, this permutation is the last one + else: + return + + # Find the largest index j greater than j such that A[i] < A[j] + for j in range(size - 1, i, -1): + if A[i] < A[j]: + break + + # Swap the value of A[i] with that of A[j], then reverse the + # sequence from A[i + 1] to form the new permutation + A[i], A[j] = A[j], A[i] + A[i + 1 :] = A[: i - size : -1] # A[i + 1:][::-1] + + # Algorithm: modified from the above + def _partial(A, r): + # Split A into the first r items and the last r items + head, tail = A[:r], A[r:] + right_head_indexes = range(r - 1, -1, -1) + left_tail_indexes = range(len(tail)) + + while True: + # Yield the permutation we have + yield tuple(head) + + # Starting from the right, find the first index of the head with + # value smaller than the maximum value of the tail - call it i. + pivot = tail[-1] + for i in right_head_indexes: + if head[i] < pivot: + break + pivot = head[i] + else: + return + + # Starting from the left, find the first value of the tail + # with a value greater than head[i] and swap. + for j in left_tail_indexes: + if tail[j] > head[i]: + head[i], tail[j] = tail[j], head[i] + break + # If we didn't find one, start from the right and find the first + # index of the head with a value greater than head[i] and swap. + else: + for j in right_head_indexes: + if head[j] > head[i]: + head[i], head[j] = head[j], head[i] + break + + # Reverse head[i + 1:] and swap it with tail[:r - (i + 1)] + tail += head[: i - r : -1] # head[i + 1:][::-1] + i += 1 + head[i:], tail[:] = tail[: r - i], tail[r - i :] + + items = sorted(iterable) + + size = len(items) + if r is None: + r = size + + if 0 < r <= size: + return _full(items) if (r == size) else _partial(items, r) + + return iter(() if r else ((),)) + + +def intersperse(e, iterable, n=1): + """Intersperse filler element *e* among the items in *iterable*, leaving + *n* items between each filler element. + + >>> list(intersperse('!', [1, 2, 3, 4, 5])) + [1, '!', 2, '!', 3, '!', 4, '!', 5] + + >>> list(intersperse(None, [1, 2, 3, 4, 5], n=2)) + [1, 2, None, 3, 4, None, 5] + + """ + if n == 0: + raise ValueError('n must be > 0') + elif n == 1: + # interleave(repeat(e), iterable) -> e, x_0, e, x_1, e, x_2... + # islice(..., 1, None) -> x_0, e, x_1, e, x_2... + return islice(interleave(repeat(e), iterable), 1, None) + else: + # interleave(filler, chunks) -> [e], [x_0, x_1], [e], [x_2, x_3]... + # islice(..., 1, None) -> [x_0, x_1], [e], [x_2, x_3]... + # flatten(...) -> x_0, x_1, e, x_2, x_3... + filler = repeat([e]) + chunks = chunked(iterable, n) + return flatten(islice(interleave(filler, chunks), 1, None)) + + +def unique_to_each(*iterables): + """Return the elements from each of the input iterables that aren't in the + other input iterables. + + For example, suppose you have a set of packages, each with a set of + dependencies:: + + {'pkg_1': {'A', 'B'}, 'pkg_2': {'B', 'C'}, 'pkg_3': {'B', 'D'}} + + If you remove one package, which dependencies can also be removed? + + If ``pkg_1`` is removed, then ``A`` is no longer necessary - it is not + associated with ``pkg_2`` or ``pkg_3``. Similarly, ``C`` is only needed for + ``pkg_2``, and ``D`` is only needed for ``pkg_3``:: + + >>> unique_to_each({'A', 'B'}, {'B', 'C'}, {'B', 'D'}) + [['A'], ['C'], ['D']] + + If there are duplicates in one input iterable that aren't in the others + they will be duplicated in the output. Input order is preserved:: + + >>> unique_to_each("mississippi", "missouri") + [['p', 'p'], ['o', 'u', 'r']] + + It is assumed that the elements of each iterable are hashable. + + """ + pool = [list(it) for it in iterables] + counts = Counter(chain.from_iterable(map(set, pool))) + uniques = {element for element in counts if counts[element] == 1} + return [list(filter(uniques.__contains__, it)) for it in pool] + + +def windowed(seq, n, fillvalue=None, step=1): + """Return a sliding window of width *n* over the given iterable. + + >>> all_windows = windowed([1, 2, 3, 4, 5], 3) + >>> list(all_windows) + [(1, 2, 3), (2, 3, 4), (3, 4, 5)] + + When the window is larger than the iterable, *fillvalue* is used in place + of missing values: + + >>> list(windowed([1, 2, 3], 4)) + [(1, 2, 3, None)] + + Each window will advance in increments of *step*: + + >>> list(windowed([1, 2, 3, 4, 5, 6], 3, fillvalue='!', step=2)) + [(1, 2, 3), (3, 4, 5), (5, 6, '!')] + + To slide into the iterable's items, use :func:`chain` to add filler items + to the left: + + >>> iterable = [1, 2, 3, 4] + >>> n = 3 + >>> padding = [None] * (n - 1) + >>> list(windowed(chain(padding, iterable), 3)) + [(None, None, 1), (None, 1, 2), (1, 2, 3), (2, 3, 4)] + """ + if n < 0: + raise ValueError('n must be >= 0') + if n == 0: + yield tuple() + return + if step < 1: + raise ValueError('step must be >= 1') + + window = deque(maxlen=n) + i = n + for _ in map(window.append, seq): + i -= 1 + if not i: + i = step + yield tuple(window) + + size = len(window) + if size == 0: + return + elif size < n: + yield tuple(chain(window, repeat(fillvalue, n - size))) + elif 0 < i < min(step, n): + window += (fillvalue,) * i + yield tuple(window) + + +def substrings(iterable): + """Yield all of the substrings of *iterable*. + + >>> [''.join(s) for s in substrings('more')] + ['m', 'o', 'r', 'e', 'mo', 'or', 're', 'mor', 'ore', 'more'] + + Note that non-string iterables can also be subdivided. + + >>> list(substrings([0, 1, 2])) + [(0,), (1,), (2,), (0, 1), (1, 2), (0, 1, 2)] + + """ + # The length-1 substrings + seq = [] + for item in iter(iterable): + seq.append(item) + yield (item,) + seq = tuple(seq) + item_count = len(seq) + + # And the rest + for n in range(2, item_count + 1): + for i in range(item_count - n + 1): + yield seq[i : i + n] + + +def substrings_indexes(seq, reverse=False): + """Yield all substrings and their positions in *seq* + + The items yielded will be a tuple of the form ``(substr, i, j)``, where + ``substr == seq[i:j]``. + + This function only works for iterables that support slicing, such as + ``str`` objects. + + >>> for item in substrings_indexes('more'): + ... print(item) + ('m', 0, 1) + ('o', 1, 2) + ('r', 2, 3) + ('e', 3, 4) + ('mo', 0, 2) + ('or', 1, 3) + ('re', 2, 4) + ('mor', 0, 3) + ('ore', 1, 4) + ('more', 0, 4) + + Set *reverse* to ``True`` to yield the same items in the opposite order. + + + """ + r = range(1, len(seq) + 1) + if reverse: + r = reversed(r) + return ( + (seq[i : i + L], i, i + L) for L in r for i in range(len(seq) - L + 1) + ) + + +class bucket: + """Wrap *iterable* and return an object that buckets the iterable into + child iterables based on a *key* function. + + >>> iterable = ['a1', 'b1', 'c1', 'a2', 'b2', 'c2', 'b3'] + >>> s = bucket(iterable, key=lambda x: x[0]) # Bucket by 1st character + >>> sorted(list(s)) # Get the keys + ['a', 'b', 'c'] + >>> a_iterable = s['a'] + >>> next(a_iterable) + 'a1' + >>> next(a_iterable) + 'a2' + >>> list(s['b']) + ['b1', 'b2', 'b3'] + + The original iterable will be advanced and its items will be cached until + they are used by the child iterables. This may require significant storage. + + By default, attempting to select a bucket to which no items belong will + exhaust the iterable and cache all values. + If you specify a *validator* function, selected buckets will instead be + checked against it. + + >>> from itertools import count + >>> it = count(1, 2) # Infinite sequence of odd numbers + >>> key = lambda x: x % 10 # Bucket by last digit + >>> validator = lambda x: x in {1, 3, 5, 7, 9} # Odd digits only + >>> s = bucket(it, key=key, validator=validator) + >>> 2 in s + False + >>> list(s[2]) + [] + + """ + + def __init__(self, iterable, key, validator=None): + self._it = iter(iterable) + self._key = key + self._cache = defaultdict(deque) + self._validator = validator or (lambda x: True) + + def __contains__(self, value): + if not self._validator(value): + return False + + try: + item = next(self[value]) + except StopIteration: + return False + else: + self._cache[value].appendleft(item) + + return True + + def _get_values(self, value): + """ + Helper to yield items from the parent iterator that match *value*. + Items that don't match are stored in the local cache as they + are encountered. + """ + while True: + # If we've cached some items that match the target value, emit + # the first one and evict it from the cache. + if self._cache[value]: + yield self._cache[value].popleft() + # Otherwise we need to advance the parent iterator to search for + # a matching item, caching the rest. + else: + while True: + try: + item = next(self._it) + except StopIteration: + return + item_value = self._key(item) + if item_value == value: + yield item + break + elif self._validator(item_value): + self._cache[item_value].append(item) + + def __iter__(self): + for item in self._it: + item_value = self._key(item) + if self._validator(item_value): + self._cache[item_value].append(item) + + yield from self._cache.keys() + + def __getitem__(self, value): + if not self._validator(value): + return iter(()) + + return self._get_values(value) + + +def spy(iterable, n=1): + """Return a 2-tuple with a list containing the first *n* elements of + *iterable*, and an iterator with the same items as *iterable*. + This allows you to "look ahead" at the items in the iterable without + advancing it. + + There is one item in the list by default: + + >>> iterable = 'abcdefg' + >>> head, iterable = spy(iterable) + >>> head + ['a'] + >>> list(iterable) + ['a', 'b', 'c', 'd', 'e', 'f', 'g'] + + You may use unpacking to retrieve items instead of lists: + + >>> (head,), iterable = spy('abcdefg') + >>> head + 'a' + >>> (first, second), iterable = spy('abcdefg', 2) + >>> first + 'a' + >>> second + 'b' + + The number of items requested can be larger than the number of items in + the iterable: + + >>> iterable = [1, 2, 3, 4, 5] + >>> head, iterable = spy(iterable, 10) + >>> head + [1, 2, 3, 4, 5] + >>> list(iterable) + [1, 2, 3, 4, 5] + + """ + it = iter(iterable) + head = take(n, it) + + return head.copy(), chain(head, it) + + +def interleave(*iterables): + """Return a new iterable yielding from each iterable in turn, + until the shortest is exhausted. + + >>> list(interleave([1, 2, 3], [4, 5], [6, 7, 8])) + [1, 4, 6, 2, 5, 7] + + For a version that doesn't terminate after the shortest iterable is + exhausted, see :func:`interleave_longest`. + + """ + return chain.from_iterable(zip(*iterables)) + + +def interleave_longest(*iterables): + """Return a new iterable yielding from each iterable in turn, + skipping any that are exhausted. + + >>> list(interleave_longest([1, 2, 3], [4, 5], [6, 7, 8])) + [1, 4, 6, 2, 5, 7, 3, 8] + + This function produces the same output as :func:`roundrobin`, but may + perform better for some inputs (in particular when the number of iterables + is large). + + """ + i = chain.from_iterable(zip_longest(*iterables, fillvalue=_marker)) + return (x for x in i if x is not _marker) + + +def interleave_evenly(iterables, lengths=None): + """ + Interleave multiple iterables so that their elements are evenly distributed + throughout the output sequence. + + >>> iterables = [1, 2, 3, 4, 5], ['a', 'b'] + >>> list(interleave_evenly(iterables)) + [1, 2, 'a', 3, 4, 'b', 5] + + >>> iterables = [[1, 2, 3], [4, 5], [6, 7, 8]] + >>> list(interleave_evenly(iterables)) + [1, 6, 4, 2, 7, 3, 8, 5] + + This function requires iterables of known length. Iterables without + ``__len__()`` can be used by manually specifying lengths with *lengths*: + + >>> from itertools import combinations, repeat + >>> iterables = [combinations(range(4), 2), ['a', 'b', 'c']] + >>> lengths = [4 * (4 - 1) // 2, 3] + >>> list(interleave_evenly(iterables, lengths=lengths)) + [(0, 1), (0, 2), 'a', (0, 3), (1, 2), 'b', (1, 3), (2, 3), 'c'] + + Based on Bresenham's algorithm. + """ + if lengths is None: + try: + lengths = [len(it) for it in iterables] + except TypeError: + raise ValueError( + 'Iterable lengths could not be determined automatically. ' + 'Specify them with the lengths keyword.' + ) + elif len(iterables) != len(lengths): + raise ValueError('Mismatching number of iterables and lengths.') + + dims = len(lengths) + + # sort iterables by length, descending + lengths_permute = sorted( + range(dims), key=lambda i: lengths[i], reverse=True + ) + lengths_desc = [lengths[i] for i in lengths_permute] + iters_desc = [iter(iterables[i]) for i in lengths_permute] + + # the longest iterable is the primary one (Bresenham: the longest + # distance along an axis) + delta_primary, deltas_secondary = lengths_desc[0], lengths_desc[1:] + iter_primary, iters_secondary = iters_desc[0], iters_desc[1:] + errors = [delta_primary // dims] * len(deltas_secondary) + + to_yield = sum(lengths) + while to_yield: + yield next(iter_primary) + to_yield -= 1 + # update errors for each secondary iterable + errors = [e - delta for e, delta in zip(errors, deltas_secondary)] + + # those iterables for which the error is negative are yielded + # ("diagonal step" in Bresenham) + for i, e in enumerate(errors): + if e < 0: + yield next(iters_secondary[i]) + to_yield -= 1 + errors[i] += delta_primary + + +def collapse(iterable, base_type=None, levels=None): + """Flatten an iterable with multiple levels of nesting (e.g., a list of + lists of tuples) into non-iterable types. + + >>> iterable = [(1, 2), ([3, 4], [[5], [6]])] + >>> list(collapse(iterable)) + [1, 2, 3, 4, 5, 6] + + Binary and text strings are not considered iterable and + will not be collapsed. + + To avoid collapsing other types, specify *base_type*: + + >>> iterable = ['ab', ('cd', 'ef'), ['gh', 'ij']] + >>> list(collapse(iterable, base_type=tuple)) + ['ab', ('cd', 'ef'), 'gh', 'ij'] + + Specify *levels* to stop flattening after a certain level: + + >>> iterable = [('a', ['b']), ('c', ['d'])] + >>> list(collapse(iterable)) # Fully flattened + ['a', 'b', 'c', 'd'] + >>> list(collapse(iterable, levels=1)) # Only one level flattened + ['a', ['b'], 'c', ['d']] + + """ + + def walk(node, level): + if ( + ((levels is not None) and (level > levels)) + or isinstance(node, (str, bytes)) + or ((base_type is not None) and isinstance(node, base_type)) + ): + yield node + return + + try: + tree = iter(node) + except TypeError: + yield node + return + else: + for child in tree: + yield from walk(child, level + 1) + + yield from walk(iterable, 0) + + +def side_effect(func, iterable, chunk_size=None, before=None, after=None): + """Invoke *func* on each item in *iterable* (or on each *chunk_size* group + of items) before yielding the item. + + `func` must be a function that takes a single argument. Its return value + will be discarded. + + *before* and *after* are optional functions that take no arguments. They + will be executed before iteration starts and after it ends, respectively. + + `side_effect` can be used for logging, updating progress bars, or anything + that is not functionally "pure." + + Emitting a status message: + + >>> from more_itertools import consume + >>> func = lambda item: print('Received {}'.format(item)) + >>> consume(side_effect(func, range(2))) + Received 0 + Received 1 + + Operating on chunks of items: + + >>> pair_sums = [] + >>> func = lambda chunk: pair_sums.append(sum(chunk)) + >>> list(side_effect(func, [0, 1, 2, 3, 4, 5], 2)) + [0, 1, 2, 3, 4, 5] + >>> list(pair_sums) + [1, 5, 9] + + Writing to a file-like object: + + >>> from io import StringIO + >>> from more_itertools import consume + >>> f = StringIO() + >>> func = lambda x: print(x, file=f) + >>> before = lambda: print(u'HEADER', file=f) + >>> after = f.close + >>> it = [u'a', u'b', u'c'] + >>> consume(side_effect(func, it, before=before, after=after)) + >>> f.closed + True + + """ + try: + if before is not None: + before() + + if chunk_size is None: + for item in iterable: + func(item) + yield item + else: + for chunk in chunked(iterable, chunk_size): + func(chunk) + yield from chunk + finally: + if after is not None: + after() + + +def sliced(seq, n, strict=False): + """Yield slices of length *n* from the sequence *seq*. + + >>> list(sliced((1, 2, 3, 4, 5, 6), 3)) + [(1, 2, 3), (4, 5, 6)] + + By the default, the last yielded slice will have fewer than *n* elements + if the length of *seq* is not divisible by *n*: + + >>> list(sliced((1, 2, 3, 4, 5, 6, 7, 8), 3)) + [(1, 2, 3), (4, 5, 6), (7, 8)] + + If the length of *seq* is not divisible by *n* and *strict* is + ``True``, then ``ValueError`` will be raised before the last + slice is yielded. + + This function will only work for iterables that support slicing. + For non-sliceable iterables, see :func:`chunked`. + + """ + iterator = takewhile(len, (seq[i : i + n] for i in count(0, n))) + if strict: + + def ret(): + for _slice in iterator: + if len(_slice) != n: + raise ValueError("seq is not divisible by n.") + yield _slice + + return iter(ret()) + else: + return iterator + + +def split_at(iterable, pred, maxsplit=-1, keep_separator=False): + """Yield lists of items from *iterable*, where each list is delimited by + an item where callable *pred* returns ``True``. + + >>> list(split_at('abcdcba', lambda x: x == 'b')) + [['a'], ['c', 'd', 'c'], ['a']] + + >>> list(split_at(range(10), lambda n: n % 2 == 1)) + [[0], [2], [4], [6], [8], []] + + At most *maxsplit* splits are done. If *maxsplit* is not specified or -1, + then there is no limit on the number of splits: + + >>> list(split_at(range(10), lambda n: n % 2 == 1, maxsplit=2)) + [[0], [2], [4, 5, 6, 7, 8, 9]] + + By default, the delimiting items are not included in the output. + To include them, set *keep_separator* to ``True``. + + >>> list(split_at('abcdcba', lambda x: x == 'b', keep_separator=True)) + [['a'], ['b'], ['c', 'd', 'c'], ['b'], ['a']] + + """ + if maxsplit == 0: + yield list(iterable) + return + + buf = [] + it = iter(iterable) + for item in it: + if pred(item): + yield buf + if keep_separator: + yield [item] + if maxsplit == 1: + yield list(it) + return + buf = [] + maxsplit -= 1 + else: + buf.append(item) + yield buf + + +def split_before(iterable, pred, maxsplit=-1): + """Yield lists of items from *iterable*, where each list ends just before + an item for which callable *pred* returns ``True``: + + >>> list(split_before('OneTwo', lambda s: s.isupper())) + [['O', 'n', 'e'], ['T', 'w', 'o']] + + >>> list(split_before(range(10), lambda n: n % 3 == 0)) + [[0, 1, 2], [3, 4, 5], [6, 7, 8], [9]] + + At most *maxsplit* splits are done. If *maxsplit* is not specified or -1, + then there is no limit on the number of splits: + + >>> list(split_before(range(10), lambda n: n % 3 == 0, maxsplit=2)) + [[0, 1, 2], [3, 4, 5], [6, 7, 8, 9]] + """ + if maxsplit == 0: + yield list(iterable) + return + + buf = [] + it = iter(iterable) + for item in it: + if pred(item) and buf: + yield buf + if maxsplit == 1: + yield [item] + list(it) + return + buf = [] + maxsplit -= 1 + buf.append(item) + if buf: + yield buf + + +def split_after(iterable, pred, maxsplit=-1): + """Yield lists of items from *iterable*, where each list ends with an + item where callable *pred* returns ``True``: + + >>> list(split_after('one1two2', lambda s: s.isdigit())) + [['o', 'n', 'e', '1'], ['t', 'w', 'o', '2']] + + >>> list(split_after(range(10), lambda n: n % 3 == 0)) + [[0], [1, 2, 3], [4, 5, 6], [7, 8, 9]] + + At most *maxsplit* splits are done. If *maxsplit* is not specified or -1, + then there is no limit on the number of splits: + + >>> list(split_after(range(10), lambda n: n % 3 == 0, maxsplit=2)) + [[0], [1, 2, 3], [4, 5, 6, 7, 8, 9]] + + """ + if maxsplit == 0: + yield list(iterable) + return + + buf = [] + it = iter(iterable) + for item in it: + buf.append(item) + if pred(item) and buf: + yield buf + if maxsplit == 1: + buf = list(it) + if buf: + yield buf + return + buf = [] + maxsplit -= 1 + if buf: + yield buf + + +def split_when(iterable, pred, maxsplit=-1): + """Split *iterable* into pieces based on the output of *pred*. + *pred* should be a function that takes successive pairs of items and + returns ``True`` if the iterable should be split in between them. + + For example, to find runs of increasing numbers, split the iterable when + element ``i`` is larger than element ``i + 1``: + + >>> list(split_when([1, 2, 3, 3, 2, 5, 2, 4, 2], lambda x, y: x > y)) + [[1, 2, 3, 3], [2, 5], [2, 4], [2]] + + At most *maxsplit* splits are done. If *maxsplit* is not specified or -1, + then there is no limit on the number of splits: + + >>> list(split_when([1, 2, 3, 3, 2, 5, 2, 4, 2], + ... lambda x, y: x > y, maxsplit=2)) + [[1, 2, 3, 3], [2, 5], [2, 4, 2]] + + """ + if maxsplit == 0: + yield list(iterable) + return + + it = iter(iterable) + try: + cur_item = next(it) + except StopIteration: + return + + buf = [cur_item] + for next_item in it: + if pred(cur_item, next_item): + yield buf + if maxsplit == 1: + yield [next_item] + list(it) + return + buf = [] + maxsplit -= 1 + + buf.append(next_item) + cur_item = next_item + + yield buf + + +def split_into(iterable, sizes): + """Yield a list of sequential items from *iterable* of length 'n' for each + integer 'n' in *sizes*. + + >>> list(split_into([1,2,3,4,5,6], [1,2,3])) + [[1], [2, 3], [4, 5, 6]] + + If the sum of *sizes* is smaller than the length of *iterable*, then the + remaining items of *iterable* will not be returned. + + >>> list(split_into([1,2,3,4,5,6], [2,3])) + [[1, 2], [3, 4, 5]] + + If the sum of *sizes* is larger than the length of *iterable*, fewer items + will be returned in the iteration that overruns *iterable* and further + lists will be empty: + + >>> list(split_into([1,2,3,4], [1,2,3,4])) + [[1], [2, 3], [4], []] + + When a ``None`` object is encountered in *sizes*, the returned list will + contain items up to the end of *iterable* the same way that itertools.slice + does: + + >>> list(split_into([1,2,3,4,5,6,7,8,9,0], [2,3,None])) + [[1, 2], [3, 4, 5], [6, 7, 8, 9, 0]] + + :func:`split_into` can be useful for grouping a series of items where the + sizes of the groups are not uniform. An example would be where in a row + from a table, multiple columns represent elements of the same feature + (e.g. a point represented by x,y,z) but, the format is not the same for + all columns. + """ + # convert the iterable argument into an iterator so its contents can + # be consumed by islice in case it is a generator + it = iter(iterable) + + for size in sizes: + if size is None: + yield list(it) + return + else: + yield list(islice(it, size)) + + +def padded(iterable, fillvalue=None, n=None, next_multiple=False): + """Yield the elements from *iterable*, followed by *fillvalue*, such that + at least *n* items are emitted. + + >>> list(padded([1, 2, 3], '?', 5)) + [1, 2, 3, '?', '?'] + + If *next_multiple* is ``True``, *fillvalue* will be emitted until the + number of items emitted is a multiple of *n*:: + + >>> list(padded([1, 2, 3, 4], n=3, next_multiple=True)) + [1, 2, 3, 4, None, None] + + If *n* is ``None``, *fillvalue* will be emitted indefinitely. + + """ + it = iter(iterable) + if n is None: + yield from chain(it, repeat(fillvalue)) + elif n < 1: + raise ValueError('n must be at least 1') + else: + item_count = 0 + for item in it: + yield item + item_count += 1 + + remaining = (n - item_count) % n if next_multiple else n - item_count + for _ in range(remaining): + yield fillvalue + + +def repeat_each(iterable, n=2): + """Repeat each element in *iterable* *n* times. + + >>> list(repeat_each('ABC', 3)) + ['A', 'A', 'A', 'B', 'B', 'B', 'C', 'C', 'C'] + """ + return chain.from_iterable(map(repeat, iterable, repeat(n))) + + +def repeat_last(iterable, default=None): + """After the *iterable* is exhausted, keep yielding its last element. + + >>> list(islice(repeat_last(range(3)), 5)) + [0, 1, 2, 2, 2] + + If the iterable is empty, yield *default* forever:: + + >>> list(islice(repeat_last(range(0), 42), 5)) + [42, 42, 42, 42, 42] + + """ + item = _marker + for item in iterable: + yield item + final = default if item is _marker else item + yield from repeat(final) + + +def distribute(n, iterable): + """Distribute the items from *iterable* among *n* smaller iterables. + + >>> group_1, group_2 = distribute(2, [1, 2, 3, 4, 5, 6]) + >>> list(group_1) + [1, 3, 5] + >>> list(group_2) + [2, 4, 6] + + If the length of *iterable* is not evenly divisible by *n*, then the + length of the returned iterables will not be identical: + + >>> children = distribute(3, [1, 2, 3, 4, 5, 6, 7]) + >>> [list(c) for c in children] + [[1, 4, 7], [2, 5], [3, 6]] + + If the length of *iterable* is smaller than *n*, then the last returned + iterables will be empty: + + >>> children = distribute(5, [1, 2, 3]) + >>> [list(c) for c in children] + [[1], [2], [3], [], []] + + This function uses :func:`itertools.tee` and may require significant + storage. If you need the order items in the smaller iterables to match the + original iterable, see :func:`divide`. + + """ + if n < 1: + raise ValueError('n must be at least 1') + + children = tee(iterable, n) + return [islice(it, index, None, n) for index, it in enumerate(children)] + + +def stagger(iterable, offsets=(-1, 0, 1), longest=False, fillvalue=None): + """Yield tuples whose elements are offset from *iterable*. + The amount by which the `i`-th item in each tuple is offset is given by + the `i`-th item in *offsets*. + + >>> list(stagger([0, 1, 2, 3])) + [(None, 0, 1), (0, 1, 2), (1, 2, 3)] + >>> list(stagger(range(8), offsets=(0, 2, 4))) + [(0, 2, 4), (1, 3, 5), (2, 4, 6), (3, 5, 7)] + + By default, the sequence will end when the final element of a tuple is the + last item in the iterable. To continue until the first element of a tuple + is the last item in the iterable, set *longest* to ``True``:: + + >>> list(stagger([0, 1, 2, 3], longest=True)) + [(None, 0, 1), (0, 1, 2), (1, 2, 3), (2, 3, None), (3, None, None)] + + By default, ``None`` will be used to replace offsets beyond the end of the + sequence. Specify *fillvalue* to use some other value. + + """ + children = tee(iterable, len(offsets)) + + return zip_offset( + *children, offsets=offsets, longest=longest, fillvalue=fillvalue + ) + + +def zip_equal(*iterables): + """``zip`` the input *iterables* together, but raise + ``UnequalIterablesError`` if they aren't all the same length. + + >>> it_1 = range(3) + >>> it_2 = iter('abc') + >>> list(zip_equal(it_1, it_2)) + [(0, 'a'), (1, 'b'), (2, 'c')] + + >>> it_1 = range(3) + >>> it_2 = iter('abcd') + >>> list(zip_equal(it_1, it_2)) # doctest: +IGNORE_EXCEPTION_DETAIL + Traceback (most recent call last): + ... + more_itertools.more.UnequalIterablesError: Iterables have different + lengths + + """ + if hexversion >= 0x30A00A6: + warnings.warn( + ( + 'zip_equal will be removed in a future version of ' + 'more-itertools. Use the builtin zip function with ' + 'strict=True instead.' + ), + DeprecationWarning, + ) + + return _zip_equal(*iterables) + + +def zip_offset(*iterables, offsets, longest=False, fillvalue=None): + """``zip`` the input *iterables* together, but offset the `i`-th iterable + by the `i`-th item in *offsets*. + + >>> list(zip_offset('0123', 'abcdef', offsets=(0, 1))) + [('0', 'b'), ('1', 'c'), ('2', 'd'), ('3', 'e')] + + This can be used as a lightweight alternative to SciPy or pandas to analyze + data sets in which some series have a lead or lag relationship. + + By default, the sequence will end when the shortest iterable is exhausted. + To continue until the longest iterable is exhausted, set *longest* to + ``True``. + + >>> list(zip_offset('0123', 'abcdef', offsets=(0, 1), longest=True)) + [('0', 'b'), ('1', 'c'), ('2', 'd'), ('3', 'e'), (None, 'f')] + + By default, ``None`` will be used to replace offsets beyond the end of the + sequence. Specify *fillvalue* to use some other value. + + """ + if len(iterables) != len(offsets): + raise ValueError("Number of iterables and offsets didn't match") + + staggered = [] + for it, n in zip(iterables, offsets): + if n < 0: + staggered.append(chain(repeat(fillvalue, -n), it)) + elif n > 0: + staggered.append(islice(it, n, None)) + else: + staggered.append(it) + + if longest: + return zip_longest(*staggered, fillvalue=fillvalue) + + return zip(*staggered) + + +def sort_together(iterables, key_list=(0,), key=None, reverse=False): + """Return the input iterables sorted together, with *key_list* as the + priority for sorting. All iterables are trimmed to the length of the + shortest one. + + This can be used like the sorting function in a spreadsheet. If each + iterable represents a column of data, the key list determines which + columns are used for sorting. + + By default, all iterables are sorted using the ``0``-th iterable:: + + >>> iterables = [(4, 3, 2, 1), ('a', 'b', 'c', 'd')] + >>> sort_together(iterables) + [(1, 2, 3, 4), ('d', 'c', 'b', 'a')] + + Set a different key list to sort according to another iterable. + Specifying multiple keys dictates how ties are broken:: + + >>> iterables = [(3, 1, 2), (0, 1, 0), ('c', 'b', 'a')] + >>> sort_together(iterables, key_list=(1, 2)) + [(2, 3, 1), (0, 0, 1), ('a', 'c', 'b')] + + To sort by a function of the elements of the iterable, pass a *key* + function. Its arguments are the elements of the iterables corresponding to + the key list:: + + >>> names = ('a', 'b', 'c') + >>> lengths = (1, 2, 3) + >>> widths = (5, 2, 1) + >>> def area(length, width): + ... return length * width + >>> sort_together([names, lengths, widths], key_list=(1, 2), key=area) + [('c', 'b', 'a'), (3, 2, 1), (1, 2, 5)] + + Set *reverse* to ``True`` to sort in descending order. + + >>> sort_together([(1, 2, 3), ('c', 'b', 'a')], reverse=True) + [(3, 2, 1), ('a', 'b', 'c')] + + """ + if key is None: + # if there is no key function, the key argument to sorted is an + # itemgetter + key_argument = itemgetter(*key_list) + else: + # if there is a key function, call it with the items at the offsets + # specified by the key function as arguments + key_list = list(key_list) + if len(key_list) == 1: + # if key_list contains a single item, pass the item at that offset + # as the only argument to the key function + key_offset = key_list[0] + key_argument = lambda zipped_items: key(zipped_items[key_offset]) + else: + # if key_list contains multiple items, use itemgetter to return a + # tuple of items, which we pass as *args to the key function + get_key_items = itemgetter(*key_list) + key_argument = lambda zipped_items: key( + *get_key_items(zipped_items) + ) + + return list( + zip(*sorted(zip(*iterables), key=key_argument, reverse=reverse)) + ) + + +def unzip(iterable): + """The inverse of :func:`zip`, this function disaggregates the elements + of the zipped *iterable*. + + The ``i``-th iterable contains the ``i``-th element from each element + of the zipped iterable. The first element is used to determine the + length of the remaining elements. + + >>> iterable = [('a', 1), ('b', 2), ('c', 3), ('d', 4)] + >>> letters, numbers = unzip(iterable) + >>> list(letters) + ['a', 'b', 'c', 'd'] + >>> list(numbers) + [1, 2, 3, 4] + + This is similar to using ``zip(*iterable)``, but it avoids reading + *iterable* into memory. Note, however, that this function uses + :func:`itertools.tee` and thus may require significant storage. + + """ + head, iterable = spy(iter(iterable)) + if not head: + # empty iterable, e.g. zip([], [], []) + return () + # spy returns a one-length iterable as head + head = head[0] + iterables = tee(iterable, len(head)) + + def itemgetter(i): + def getter(obj): + try: + return obj[i] + except IndexError: + # basically if we have an iterable like + # iter([(1, 2, 3), (4, 5), (6,)]) + # the second unzipped iterable would fail at the third tuple + # since it would try to access tup[1] + # same with the third unzipped iterable and the second tuple + # to support these "improperly zipped" iterables, + # we create a custom itemgetter + # which just stops the unzipped iterables + # at first length mismatch + raise StopIteration + + return getter + + return tuple(map(itemgetter(i), it) for i, it in enumerate(iterables)) + + +def divide(n, iterable): + """Divide the elements from *iterable* into *n* parts, maintaining + order. + + >>> group_1, group_2 = divide(2, [1, 2, 3, 4, 5, 6]) + >>> list(group_1) + [1, 2, 3] + >>> list(group_2) + [4, 5, 6] + + If the length of *iterable* is not evenly divisible by *n*, then the + length of the returned iterables will not be identical: + + >>> children = divide(3, [1, 2, 3, 4, 5, 6, 7]) + >>> [list(c) for c in children] + [[1, 2, 3], [4, 5], [6, 7]] + + If the length of the iterable is smaller than n, then the last returned + iterables will be empty: + + >>> children = divide(5, [1, 2, 3]) + >>> [list(c) for c in children] + [[1], [2], [3], [], []] + + This function will exhaust the iterable before returning and may require + significant storage. If order is not important, see :func:`distribute`, + which does not first pull the iterable into memory. + + """ + if n < 1: + raise ValueError('n must be at least 1') + + try: + iterable[:0] + except TypeError: + seq = tuple(iterable) + else: + seq = iterable + + q, r = divmod(len(seq), n) + + ret = [] + stop = 0 + for i in range(1, n + 1): + start = stop + stop += q + 1 if i <= r else q + ret.append(iter(seq[start:stop])) + + return ret + + +def always_iterable(obj, base_type=(str, bytes)): + """If *obj* is iterable, return an iterator over its items:: + + >>> obj = (1, 2, 3) + >>> list(always_iterable(obj)) + [1, 2, 3] + + If *obj* is not iterable, return a one-item iterable containing *obj*:: + + >>> obj = 1 + >>> list(always_iterable(obj)) + [1] + + If *obj* is ``None``, return an empty iterable: + + >>> obj = None + >>> list(always_iterable(None)) + [] + + By default, binary and text strings are not considered iterable:: + + >>> obj = 'foo' + >>> list(always_iterable(obj)) + ['foo'] + + If *base_type* is set, objects for which ``isinstance(obj, base_type)`` + returns ``True`` won't be considered iterable. + + >>> obj = {'a': 1} + >>> list(always_iterable(obj)) # Iterate over the dict's keys + ['a'] + >>> list(always_iterable(obj, base_type=dict)) # Treat dicts as a unit + [{'a': 1}] + + Set *base_type* to ``None`` to avoid any special handling and treat objects + Python considers iterable as iterable: + + >>> obj = 'foo' + >>> list(always_iterable(obj, base_type=None)) + ['f', 'o', 'o'] + """ + if obj is None: + return iter(()) + + if (base_type is not None) and isinstance(obj, base_type): + return iter((obj,)) + + try: + return iter(obj) + except TypeError: + return iter((obj,)) + + +def adjacent(predicate, iterable, distance=1): + """Return an iterable over `(bool, item)` tuples where the `item` is + drawn from *iterable* and the `bool` indicates whether + that item satisfies the *predicate* or is adjacent to an item that does. + + For example, to find whether items are adjacent to a ``3``:: + + >>> list(adjacent(lambda x: x == 3, range(6))) + [(False, 0), (False, 1), (True, 2), (True, 3), (True, 4), (False, 5)] + + Set *distance* to change what counts as adjacent. For example, to find + whether items are two places away from a ``3``: + + >>> list(adjacent(lambda x: x == 3, range(6), distance=2)) + [(False, 0), (True, 1), (True, 2), (True, 3), (True, 4), (True, 5)] + + This is useful for contextualizing the results of a search function. + For example, a code comparison tool might want to identify lines that + have changed, but also surrounding lines to give the viewer of the diff + context. + + The predicate function will only be called once for each item in the + iterable. + + See also :func:`groupby_transform`, which can be used with this function + to group ranges of items with the same `bool` value. + + """ + # Allow distance=0 mainly for testing that it reproduces results with map() + if distance < 0: + raise ValueError('distance must be at least 0') + + i1, i2 = tee(iterable) + padding = [False] * distance + selected = chain(padding, map(predicate, i1), padding) + adjacent_to_selected = map(any, windowed(selected, 2 * distance + 1)) + return zip(adjacent_to_selected, i2) + + +def groupby_transform(iterable, keyfunc=None, valuefunc=None, reducefunc=None): + """An extension of :func:`itertools.groupby` that can apply transformations + to the grouped data. + + * *keyfunc* is a function computing a key value for each item in *iterable* + * *valuefunc* is a function that transforms the individual items from + *iterable* after grouping + * *reducefunc* is a function that transforms each group of items + + >>> iterable = 'aAAbBBcCC' + >>> keyfunc = lambda k: k.upper() + >>> valuefunc = lambda v: v.lower() + >>> reducefunc = lambda g: ''.join(g) + >>> list(groupby_transform(iterable, keyfunc, valuefunc, reducefunc)) + [('A', 'aaa'), ('B', 'bbb'), ('C', 'ccc')] + + Each optional argument defaults to an identity function if not specified. + + :func:`groupby_transform` is useful when grouping elements of an iterable + using a separate iterable as the key. To do this, :func:`zip` the iterables + and pass a *keyfunc* that extracts the first element and a *valuefunc* + that extracts the second element:: + + >>> from operator import itemgetter + >>> keys = [0, 0, 1, 1, 1, 2, 2, 2, 3] + >>> values = 'abcdefghi' + >>> iterable = zip(keys, values) + >>> grouper = groupby_transform(iterable, itemgetter(0), itemgetter(1)) + >>> [(k, ''.join(g)) for k, g in grouper] + [(0, 'ab'), (1, 'cde'), (2, 'fgh'), (3, 'i')] + + Note that the order of items in the iterable is significant. + Only adjacent items are grouped together, so if you don't want any + duplicate groups, you should sort the iterable by the key function. + + """ + ret = groupby(iterable, keyfunc) + if valuefunc: + ret = ((k, map(valuefunc, g)) for k, g in ret) + if reducefunc: + ret = ((k, reducefunc(g)) for k, g in ret) + + return ret + + +class numeric_range(abc.Sequence, abc.Hashable): + """An extension of the built-in ``range()`` function whose arguments can + be any orderable numeric type. + + With only *stop* specified, *start* defaults to ``0`` and *step* + defaults to ``1``. The output items will match the type of *stop*: + + >>> list(numeric_range(3.5)) + [0.0, 1.0, 2.0, 3.0] + + With only *start* and *stop* specified, *step* defaults to ``1``. The + output items will match the type of *start*: + + >>> from decimal import Decimal + >>> start = Decimal('2.1') + >>> stop = Decimal('5.1') + >>> list(numeric_range(start, stop)) + [Decimal('2.1'), Decimal('3.1'), Decimal('4.1')] + + With *start*, *stop*, and *step* specified the output items will match + the type of ``start + step``: + + >>> from fractions import Fraction + >>> start = Fraction(1, 2) # Start at 1/2 + >>> stop = Fraction(5, 2) # End at 5/2 + >>> step = Fraction(1, 2) # Count by 1/2 + >>> list(numeric_range(start, stop, step)) + [Fraction(1, 2), Fraction(1, 1), Fraction(3, 2), Fraction(2, 1)] + + If *step* is zero, ``ValueError`` is raised. Negative steps are supported: + + >>> list(numeric_range(3, -1, -1.0)) + [3.0, 2.0, 1.0, 0.0] + + Be aware of the limitations of floating point numbers; the representation + of the yielded numbers may be surprising. + + ``datetime.datetime`` objects can be used for *start* and *stop*, if *step* + is a ``datetime.timedelta`` object: + + >>> import datetime + >>> start = datetime.datetime(2019, 1, 1) + >>> stop = datetime.datetime(2019, 1, 3) + >>> step = datetime.timedelta(days=1) + >>> items = iter(numeric_range(start, stop, step)) + >>> next(items) + datetime.datetime(2019, 1, 1, 0, 0) + >>> next(items) + datetime.datetime(2019, 1, 2, 0, 0) + + """ + + _EMPTY_HASH = hash(range(0, 0)) + + def __init__(self, *args): + argc = len(args) + if argc == 1: + (self._stop,) = args + self._start = type(self._stop)(0) + self._step = type(self._stop - self._start)(1) + elif argc == 2: + self._start, self._stop = args + self._step = type(self._stop - self._start)(1) + elif argc == 3: + self._start, self._stop, self._step = args + elif argc == 0: + raise TypeError( + 'numeric_range expected at least ' + '1 argument, got {}'.format(argc) + ) + else: + raise TypeError( + 'numeric_range expected at most ' + '3 arguments, got {}'.format(argc) + ) + + self._zero = type(self._step)(0) + if self._step == self._zero: + raise ValueError('numeric_range() arg 3 must not be zero') + self._growing = self._step > self._zero + + def __bool__(self): + if self._growing: + return self._start < self._stop + else: + return self._start > self._stop + + def __contains__(self, elem): + if self._growing: + if self._start <= elem < self._stop: + return (elem - self._start) % self._step == self._zero + else: + if self._start >= elem > self._stop: + return (self._start - elem) % (-self._step) == self._zero + + return False + + def __eq__(self, other): + if isinstance(other, numeric_range): + empty_self = not bool(self) + empty_other = not bool(other) + if empty_self or empty_other: + return empty_self and empty_other # True if both empty + else: + return ( + self._start == other._start + and self._step == other._step + and self._get_by_index(-1) == other._get_by_index(-1) + ) + else: + return False + + def __getitem__(self, key): + if isinstance(key, int): + return self._get_by_index(key) + elif isinstance(key, slice): + step = self._step if key.step is None else key.step * self._step + + if key.start is None or key.start <= -self._len: + start = self._start + elif key.start >= self._len: + start = self._stop + else: # -self._len < key.start < self._len + start = self._get_by_index(key.start) + + if key.stop is None or key.stop >= self._len: + stop = self._stop + elif key.stop <= -self._len: + stop = self._start + else: # -self._len < key.stop < self._len + stop = self._get_by_index(key.stop) + + return numeric_range(start, stop, step) + else: + raise TypeError( + 'numeric range indices must be ' + 'integers or slices, not {}'.format(type(key).__name__) + ) + + def __hash__(self): + if self: + return hash((self._start, self._get_by_index(-1), self._step)) + else: + return self._EMPTY_HASH + + def __iter__(self): + values = (self._start + (n * self._step) for n in count()) + if self._growing: + return takewhile(partial(gt, self._stop), values) + else: + return takewhile(partial(lt, self._stop), values) + + def __len__(self): + return self._len + + @cached_property + def _len(self): + if self._growing: + start = self._start + stop = self._stop + step = self._step + else: + start = self._stop + stop = self._start + step = -self._step + distance = stop - start + if distance <= self._zero: + return 0 + else: # distance > 0 and step > 0: regular euclidean division + q, r = divmod(distance, step) + return int(q) + int(r != self._zero) + + def __reduce__(self): + return numeric_range, (self._start, self._stop, self._step) + + def __repr__(self): + if self._step == 1: + return "numeric_range({}, {})".format( + repr(self._start), repr(self._stop) + ) + else: + return "numeric_range({}, {}, {})".format( + repr(self._start), repr(self._stop), repr(self._step) + ) + + def __reversed__(self): + return iter( + numeric_range( + self._get_by_index(-1), self._start - self._step, -self._step + ) + ) + + def count(self, value): + return int(value in self) + + def index(self, value): + if self._growing: + if self._start <= value < self._stop: + q, r = divmod(value - self._start, self._step) + if r == self._zero: + return int(q) + else: + if self._start >= value > self._stop: + q, r = divmod(self._start - value, -self._step) + if r == self._zero: + return int(q) + + raise ValueError("{} is not in numeric range".format(value)) + + def _get_by_index(self, i): + if i < 0: + i += self._len + if i < 0 or i >= self._len: + raise IndexError("numeric range object index out of range") + return self._start + i * self._step + + +def count_cycle(iterable, n=None): + """Cycle through the items from *iterable* up to *n* times, yielding + the number of completed cycles along with each item. If *n* is omitted the + process repeats indefinitely. + + >>> list(count_cycle('AB', 3)) + [(0, 'A'), (0, 'B'), (1, 'A'), (1, 'B'), (2, 'A'), (2, 'B')] + + """ + iterable = tuple(iterable) + if not iterable: + return iter(()) + counter = count() if n is None else range(n) + return ((i, item) for i in counter for item in iterable) + + +def mark_ends(iterable): + """Yield 3-tuples of the form ``(is_first, is_last, item)``. + + >>> list(mark_ends('ABC')) + [(True, False, 'A'), (False, False, 'B'), (False, True, 'C')] + + Use this when looping over an iterable to take special action on its first + and/or last items: + + >>> iterable = ['Header', 100, 200, 'Footer'] + >>> total = 0 + >>> for is_first, is_last, item in mark_ends(iterable): + ... if is_first: + ... continue # Skip the header + ... if is_last: + ... continue # Skip the footer + ... total += item + >>> print(total) + 300 + """ + it = iter(iterable) + + try: + b = next(it) + except StopIteration: + return + + try: + for i in count(): + a = b + b = next(it) + yield i == 0, False, a + + except StopIteration: + yield i == 0, True, a + + +def locate(iterable, pred=bool, window_size=None): + """Yield the index of each item in *iterable* for which *pred* returns + ``True``. + + *pred* defaults to :func:`bool`, which will select truthy items: + + >>> list(locate([0, 1, 1, 0, 1, 0, 0])) + [1, 2, 4] + + Set *pred* to a custom function to, e.g., find the indexes for a particular + item. + + >>> list(locate(['a', 'b', 'c', 'b'], lambda x: x == 'b')) + [1, 3] + + If *window_size* is given, then the *pred* function will be called with + that many items. This enables searching for sub-sequences: + + >>> iterable = [0, 1, 2, 3, 0, 1, 2, 3, 0, 1, 2, 3] + >>> pred = lambda *args: args == (1, 2, 3) + >>> list(locate(iterable, pred=pred, window_size=3)) + [1, 5, 9] + + Use with :func:`seekable` to find indexes and then retrieve the associated + items: + + >>> from itertools import count + >>> from more_itertools import seekable + >>> source = (3 * n + 1 if (n % 2) else n // 2 for n in count()) + >>> it = seekable(source) + >>> pred = lambda x: x > 100 + >>> indexes = locate(it, pred=pred) + >>> i = next(indexes) + >>> it.seek(i) + >>> next(it) + 106 + + """ + if window_size is None: + return compress(count(), map(pred, iterable)) + + if window_size < 1: + raise ValueError('window size must be at least 1') + + it = windowed(iterable, window_size, fillvalue=_marker) + return compress(count(), starmap(pred, it)) + + +def longest_common_prefix(iterables): + """Yield elements of the longest common prefix amongst given *iterables*. + + >>> ''.join(longest_common_prefix(['abcd', 'abc', 'abf'])) + 'ab' + + """ + return (c[0] for c in takewhile(all_equal, zip(*iterables))) + + +def lstrip(iterable, pred): + """Yield the items from *iterable*, but strip any from the beginning + for which *pred* returns ``True``. + + For example, to remove a set of items from the start of an iterable: + + >>> iterable = (None, False, None, 1, 2, None, 3, False, None) + >>> pred = lambda x: x in {None, False, ''} + >>> list(lstrip(iterable, pred)) + [1, 2, None, 3, False, None] + + This function is analogous to to :func:`str.lstrip`, and is essentially + an wrapper for :func:`itertools.dropwhile`. + + """ + return dropwhile(pred, iterable) + + +def rstrip(iterable, pred): + """Yield the items from *iterable*, but strip any from the end + for which *pred* returns ``True``. + + For example, to remove a set of items from the end of an iterable: + + >>> iterable = (None, False, None, 1, 2, None, 3, False, None) + >>> pred = lambda x: x in {None, False, ''} + >>> list(rstrip(iterable, pred)) + [None, False, None, 1, 2, None, 3] + + This function is analogous to :func:`str.rstrip`. + + """ + cache = [] + cache_append = cache.append + cache_clear = cache.clear + for x in iterable: + if pred(x): + cache_append(x) + else: + yield from cache + cache_clear() + yield x + + +def strip(iterable, pred): + """Yield the items from *iterable*, but strip any from the + beginning and end for which *pred* returns ``True``. + + For example, to remove a set of items from both ends of an iterable: + + >>> iterable = (None, False, None, 1, 2, None, 3, False, None) + >>> pred = lambda x: x in {None, False, ''} + >>> list(strip(iterable, pred)) + [1, 2, None, 3] + + This function is analogous to :func:`str.strip`. + + """ + return rstrip(lstrip(iterable, pred), pred) + + +class islice_extended: + """An extension of :func:`itertools.islice` that supports negative values + for *stop*, *start*, and *step*. + + >>> iterable = iter('abcdefgh') + >>> list(islice_extended(iterable, -4, -1)) + ['e', 'f', 'g'] + + Slices with negative values require some caching of *iterable*, but this + function takes care to minimize the amount of memory required. + + For example, you can use a negative step with an infinite iterator: + + >>> from itertools import count + >>> list(islice_extended(count(), 110, 99, -2)) + [110, 108, 106, 104, 102, 100] + + You can also use slice notation directly: + + >>> iterable = map(str, count()) + >>> it = islice_extended(iterable)[10:20:2] + >>> list(it) + ['10', '12', '14', '16', '18'] + + """ + + def __init__(self, iterable, *args): + it = iter(iterable) + if args: + self._iterable = _islice_helper(it, slice(*args)) + else: + self._iterable = it + + def __iter__(self): + return self + + def __next__(self): + return next(self._iterable) + + def __getitem__(self, key): + if isinstance(key, slice): + return islice_extended(_islice_helper(self._iterable, key)) + + raise TypeError('islice_extended.__getitem__ argument must be a slice') + + +def _islice_helper(it, s): + start = s.start + stop = s.stop + if s.step == 0: + raise ValueError('step argument must be a non-zero integer or None.') + step = s.step or 1 + + if step > 0: + start = 0 if (start is None) else start + + if start < 0: + # Consume all but the last -start items + cache = deque(enumerate(it, 1), maxlen=-start) + len_iter = cache[-1][0] if cache else 0 + + # Adjust start to be positive + i = max(len_iter + start, 0) + + # Adjust stop to be positive + if stop is None: + j = len_iter + elif stop >= 0: + j = min(stop, len_iter) + else: + j = max(len_iter + stop, 0) + + # Slice the cache + n = j - i + if n <= 0: + return + + for index, item in islice(cache, 0, n, step): + yield item + elif (stop is not None) and (stop < 0): + # Advance to the start position + next(islice(it, start, start), None) + + # When stop is negative, we have to carry -stop items while + # iterating + cache = deque(islice(it, -stop), maxlen=-stop) + + for index, item in enumerate(it): + cached_item = cache.popleft() + if index % step == 0: + yield cached_item + cache.append(item) + else: + # When both start and stop are positive we have the normal case + yield from islice(it, start, stop, step) + else: + start = -1 if (start is None) else start + + if (stop is not None) and (stop < 0): + # Consume all but the last items + n = -stop - 1 + cache = deque(enumerate(it, 1), maxlen=n) + len_iter = cache[-1][0] if cache else 0 + + # If start and stop are both negative they are comparable and + # we can just slice. Otherwise we can adjust start to be negative + # and then slice. + if start < 0: + i, j = start, stop + else: + i, j = min(start - len_iter, -1), None + + for index, item in list(cache)[i:j:step]: + yield item + else: + # Advance to the stop position + if stop is not None: + m = stop + 1 + next(islice(it, m, m), None) + + # stop is positive, so if start is negative they are not comparable + # and we need the rest of the items. + if start < 0: + i = start + n = None + # stop is None and start is positive, so we just need items up to + # the start index. + elif stop is None: + i = None + n = start + 1 + # Both stop and start are positive, so they are comparable. + else: + i = None + n = start - stop + if n <= 0: + return + + cache = list(islice(it, n)) + + yield from cache[i::step] + + +def always_reversible(iterable): + """An extension of :func:`reversed` that supports all iterables, not + just those which implement the ``Reversible`` or ``Sequence`` protocols. + + >>> print(*always_reversible(x for x in range(3))) + 2 1 0 + + If the iterable is already reversible, this function returns the + result of :func:`reversed()`. If the iterable is not reversible, + this function will cache the remaining items in the iterable and + yield them in reverse order, which may require significant storage. + """ + try: + return reversed(iterable) + except TypeError: + return reversed(list(iterable)) + + +def consecutive_groups(iterable, ordering=lambda x: x): + """Yield groups of consecutive items using :func:`itertools.groupby`. + The *ordering* function determines whether two items are adjacent by + returning their position. + + By default, the ordering function is the identity function. This is + suitable for finding runs of numbers: + + >>> iterable = [1, 10, 11, 12, 20, 30, 31, 32, 33, 40] + >>> for group in consecutive_groups(iterable): + ... print(list(group)) + [1] + [10, 11, 12] + [20] + [30, 31, 32, 33] + [40] + + For finding runs of adjacent letters, try using the :meth:`index` method + of a string of letters: + + >>> from string import ascii_lowercase + >>> iterable = 'abcdfgilmnop' + >>> ordering = ascii_lowercase.index + >>> for group in consecutive_groups(iterable, ordering): + ... print(list(group)) + ['a', 'b', 'c', 'd'] + ['f', 'g'] + ['i'] + ['l', 'm', 'n', 'o', 'p'] + + Each group of consecutive items is an iterator that shares it source with + *iterable*. When an an output group is advanced, the previous group is + no longer available unless its elements are copied (e.g., into a ``list``). + + >>> iterable = [1, 2, 11, 12, 21, 22] + >>> saved_groups = [] + >>> for group in consecutive_groups(iterable): + ... saved_groups.append(list(group)) # Copy group elements + >>> saved_groups + [[1, 2], [11, 12], [21, 22]] + + """ + for k, g in groupby( + enumerate(iterable), key=lambda x: x[0] - ordering(x[1]) + ): + yield map(itemgetter(1), g) + + +def difference(iterable, func=sub, *, initial=None): + """This function is the inverse of :func:`itertools.accumulate`. By default + it will compute the first difference of *iterable* using + :func:`operator.sub`: + + >>> from itertools import accumulate + >>> iterable = accumulate([0, 1, 2, 3, 4]) # produces 0, 1, 3, 6, 10 + >>> list(difference(iterable)) + [0, 1, 2, 3, 4] + + *func* defaults to :func:`operator.sub`, but other functions can be + specified. They will be applied as follows:: + + A, B, C, D, ... --> A, func(B, A), func(C, B), func(D, C), ... + + For example, to do progressive division: + + >>> iterable = [1, 2, 6, 24, 120] + >>> func = lambda x, y: x // y + >>> list(difference(iterable, func)) + [1, 2, 3, 4, 5] + + If the *initial* keyword is set, the first element will be skipped when + computing successive differences. + + >>> it = [10, 11, 13, 16] # from accumulate([1, 2, 3], initial=10) + >>> list(difference(it, initial=10)) + [1, 2, 3] + + """ + a, b = tee(iterable) + try: + first = [next(b)] + except StopIteration: + return iter([]) + + if initial is not None: + first = [] + + return chain(first, map(func, b, a)) + + +class SequenceView(Sequence): + """Return a read-only view of the sequence object *target*. + + :class:`SequenceView` objects are analogous to Python's built-in + "dictionary view" types. They provide a dynamic view of a sequence's items, + meaning that when the sequence updates, so does the view. + + >>> seq = ['0', '1', '2'] + >>> view = SequenceView(seq) + >>> view + SequenceView(['0', '1', '2']) + >>> seq.append('3') + >>> view + SequenceView(['0', '1', '2', '3']) + + Sequence views support indexing, slicing, and length queries. They act + like the underlying sequence, except they don't allow assignment: + + >>> view[1] + '1' + >>> view[1:-1] + ['1', '2'] + >>> len(view) + 4 + + Sequence views are useful as an alternative to copying, as they don't + require (much) extra storage. + + """ + + def __init__(self, target): + if not isinstance(target, Sequence): + raise TypeError + self._target = target + + def __getitem__(self, index): + return self._target[index] + + def __len__(self): + return len(self._target) + + def __repr__(self): + return '{}({})'.format(self.__class__.__name__, repr(self._target)) + + +class seekable: + """Wrap an iterator to allow for seeking backward and forward. This + progressively caches the items in the source iterable so they can be + re-visited. + + Call :meth:`seek` with an index to seek to that position in the source + iterable. + + To "reset" an iterator, seek to ``0``: + + >>> from itertools import count + >>> it = seekable((str(n) for n in count())) + >>> next(it), next(it), next(it) + ('0', '1', '2') + >>> it.seek(0) + >>> next(it), next(it), next(it) + ('0', '1', '2') + >>> next(it) + '3' + + You can also seek forward: + + >>> it = seekable((str(n) for n in range(20))) + >>> it.seek(10) + >>> next(it) + '10' + >>> it.relative_seek(-2) # Seeking relative to the current position + >>> next(it) + '9' + >>> it.seek(20) # Seeking past the end of the source isn't a problem + >>> list(it) + [] + >>> it.seek(0) # Resetting works even after hitting the end + >>> next(it), next(it), next(it) + ('0', '1', '2') + + Call :meth:`peek` to look ahead one item without advancing the iterator: + + >>> it = seekable('1234') + >>> it.peek() + '1' + >>> list(it) + ['1', '2', '3', '4'] + >>> it.peek(default='empty') + 'empty' + + Before the iterator is at its end, calling :func:`bool` on it will return + ``True``. After it will return ``False``: + + >>> it = seekable('5678') + >>> bool(it) + True + >>> list(it) + ['5', '6', '7', '8'] + >>> bool(it) + False + + You may view the contents of the cache with the :meth:`elements` method. + That returns a :class:`SequenceView`, a view that updates automatically: + + >>> it = seekable((str(n) for n in range(10))) + >>> next(it), next(it), next(it) + ('0', '1', '2') + >>> elements = it.elements() + >>> elements + SequenceView(['0', '1', '2']) + >>> next(it) + '3' + >>> elements + SequenceView(['0', '1', '2', '3']) + + By default, the cache grows as the source iterable progresses, so beware of + wrapping very large or infinite iterables. Supply *maxlen* to limit the + size of the cache (this of course limits how far back you can seek). + + >>> from itertools import count + >>> it = seekable((str(n) for n in count()), maxlen=2) + >>> next(it), next(it), next(it), next(it) + ('0', '1', '2', '3') + >>> list(it.elements()) + ['2', '3'] + >>> it.seek(0) + >>> next(it), next(it), next(it), next(it) + ('2', '3', '4', '5') + >>> next(it) + '6' + + """ + + def __init__(self, iterable, maxlen=None): + self._source = iter(iterable) + if maxlen is None: + self._cache = [] + else: + self._cache = deque([], maxlen) + self._index = None + + def __iter__(self): + return self + + def __next__(self): + if self._index is not None: + try: + item = self._cache[self._index] + except IndexError: + self._index = None + else: + self._index += 1 + return item + + item = next(self._source) + self._cache.append(item) + return item + + def __bool__(self): + try: + self.peek() + except StopIteration: + return False + return True + + def peek(self, default=_marker): + try: + peeked = next(self) + except StopIteration: + if default is _marker: + raise + return default + if self._index is None: + self._index = len(self._cache) + self._index -= 1 + return peeked + + def elements(self): + return SequenceView(self._cache) + + def seek(self, index): + self._index = index + remainder = index - len(self._cache) + if remainder > 0: + consume(self, remainder) + + def relative_seek(self, count): + index = len(self._cache) + self.seek(max(index + count, 0)) + + +class run_length: + """ + :func:`run_length.encode` compresses an iterable with run-length encoding. + It yields groups of repeated items with the count of how many times they + were repeated: + + >>> uncompressed = 'abbcccdddd' + >>> list(run_length.encode(uncompressed)) + [('a', 1), ('b', 2), ('c', 3), ('d', 4)] + + :func:`run_length.decode` decompresses an iterable that was previously + compressed with run-length encoding. It yields the items of the + decompressed iterable: + + >>> compressed = [('a', 1), ('b', 2), ('c', 3), ('d', 4)] + >>> list(run_length.decode(compressed)) + ['a', 'b', 'b', 'c', 'c', 'c', 'd', 'd', 'd', 'd'] + + """ + + @staticmethod + def encode(iterable): + return ((k, ilen(g)) for k, g in groupby(iterable)) + + @staticmethod + def decode(iterable): + return chain.from_iterable(repeat(k, n) for k, n in iterable) + + +def exactly_n(iterable, n, predicate=bool): + """Return ``True`` if exactly ``n`` items in the iterable are ``True`` + according to the *predicate* function. + + >>> exactly_n([True, True, False], 2) + True + >>> exactly_n([True, True, False], 1) + False + >>> exactly_n([0, 1, 2, 3, 4, 5], 3, lambda x: x < 3) + True + + The iterable will be advanced until ``n + 1`` truthy items are encountered, + so avoid calling it on infinite iterables. + + """ + return len(take(n + 1, filter(predicate, iterable))) == n + + +def circular_shifts(iterable): + """Return a list of circular shifts of *iterable*. + + >>> circular_shifts(range(4)) + [(0, 1, 2, 3), (1, 2, 3, 0), (2, 3, 0, 1), (3, 0, 1, 2)] + """ + lst = list(iterable) + return take(len(lst), windowed(cycle(lst), len(lst))) + + +def make_decorator(wrapping_func, result_index=0): + """Return a decorator version of *wrapping_func*, which is a function that + modifies an iterable. *result_index* is the position in that function's + signature where the iterable goes. + + This lets you use itertools on the "production end," i.e. at function + definition. This can augment what the function returns without changing the + function's code. + + For example, to produce a decorator version of :func:`chunked`: + + >>> from more_itertools import chunked + >>> chunker = make_decorator(chunked, result_index=0) + >>> @chunker(3) + ... def iter_range(n): + ... return iter(range(n)) + ... + >>> list(iter_range(9)) + [[0, 1, 2], [3, 4, 5], [6, 7, 8]] + + To only allow truthy items to be returned: + + >>> truth_serum = make_decorator(filter, result_index=1) + >>> @truth_serum(bool) + ... def boolean_test(): + ... return [0, 1, '', ' ', False, True] + ... + >>> list(boolean_test()) + [1, ' ', True] + + The :func:`peekable` and :func:`seekable` wrappers make for practical + decorators: + + >>> from more_itertools import peekable + >>> peekable_function = make_decorator(peekable) + >>> @peekable_function() + ... def str_range(*args): + ... return (str(x) for x in range(*args)) + ... + >>> it = str_range(1, 20, 2) + >>> next(it), next(it), next(it) + ('1', '3', '5') + >>> it.peek() + '7' + >>> next(it) + '7' + + """ + + # See https://sites.google.com/site/bbayles/index/decorator_factory for + # notes on how this works. + def decorator(*wrapping_args, **wrapping_kwargs): + def outer_wrapper(f): + def inner_wrapper(*args, **kwargs): + result = f(*args, **kwargs) + wrapping_args_ = list(wrapping_args) + wrapping_args_.insert(result_index, result) + return wrapping_func(*wrapping_args_, **wrapping_kwargs) + + return inner_wrapper + + return outer_wrapper + + return decorator + + +def map_reduce(iterable, keyfunc, valuefunc=None, reducefunc=None): + """Return a dictionary that maps the items in *iterable* to categories + defined by *keyfunc*, transforms them with *valuefunc*, and + then summarizes them by category with *reducefunc*. + + *valuefunc* defaults to the identity function if it is unspecified. + If *reducefunc* is unspecified, no summarization takes place: + + >>> keyfunc = lambda x: x.upper() + >>> result = map_reduce('abbccc', keyfunc) + >>> sorted(result.items()) + [('A', ['a']), ('B', ['b', 'b']), ('C', ['c', 'c', 'c'])] + + Specifying *valuefunc* transforms the categorized items: + + >>> keyfunc = lambda x: x.upper() + >>> valuefunc = lambda x: 1 + >>> result = map_reduce('abbccc', keyfunc, valuefunc) + >>> sorted(result.items()) + [('A', [1]), ('B', [1, 1]), ('C', [1, 1, 1])] + + Specifying *reducefunc* summarizes the categorized items: + + >>> keyfunc = lambda x: x.upper() + >>> valuefunc = lambda x: 1 + >>> reducefunc = sum + >>> result = map_reduce('abbccc', keyfunc, valuefunc, reducefunc) + >>> sorted(result.items()) + [('A', 1), ('B', 2), ('C', 3)] + + You may want to filter the input iterable before applying the map/reduce + procedure: + + >>> all_items = range(30) + >>> items = [x for x in all_items if 10 <= x <= 20] # Filter + >>> keyfunc = lambda x: x % 2 # Evens map to 0; odds to 1 + >>> categories = map_reduce(items, keyfunc=keyfunc) + >>> sorted(categories.items()) + [(0, [10, 12, 14, 16, 18, 20]), (1, [11, 13, 15, 17, 19])] + >>> summaries = map_reduce(items, keyfunc=keyfunc, reducefunc=sum) + >>> sorted(summaries.items()) + [(0, 90), (1, 75)] + + Note that all items in the iterable are gathered into a list before the + summarization step, which may require significant storage. + + The returned object is a :obj:`collections.defaultdict` with the + ``default_factory`` set to ``None``, such that it behaves like a normal + dictionary. + + """ + valuefunc = (lambda x: x) if (valuefunc is None) else valuefunc + + ret = defaultdict(list) + for item in iterable: + key = keyfunc(item) + value = valuefunc(item) + ret[key].append(value) + + if reducefunc is not None: + for key, value_list in ret.items(): + ret[key] = reducefunc(value_list) + + ret.default_factory = None + return ret + + +def rlocate(iterable, pred=bool, window_size=None): + """Yield the index of each item in *iterable* for which *pred* returns + ``True``, starting from the right and moving left. + + *pred* defaults to :func:`bool`, which will select truthy items: + + >>> list(rlocate([0, 1, 1, 0, 1, 0, 0])) # Truthy at 1, 2, and 4 + [4, 2, 1] + + Set *pred* to a custom function to, e.g., find the indexes for a particular + item: + + >>> iterable = iter('abcb') + >>> pred = lambda x: x == 'b' + >>> list(rlocate(iterable, pred)) + [3, 1] + + If *window_size* is given, then the *pred* function will be called with + that many items. This enables searching for sub-sequences: + + >>> iterable = [0, 1, 2, 3, 0, 1, 2, 3, 0, 1, 2, 3] + >>> pred = lambda *args: args == (1, 2, 3) + >>> list(rlocate(iterable, pred=pred, window_size=3)) + [9, 5, 1] + + Beware, this function won't return anything for infinite iterables. + If *iterable* is reversible, ``rlocate`` will reverse it and search from + the right. Otherwise, it will search from the left and return the results + in reverse order. + + See :func:`locate` to for other example applications. + + """ + if window_size is None: + try: + len_iter = len(iterable) + return (len_iter - i - 1 for i in locate(reversed(iterable), pred)) + except TypeError: + pass + + return reversed(list(locate(iterable, pred, window_size))) + + +def replace(iterable, pred, substitutes, count=None, window_size=1): + """Yield the items from *iterable*, replacing the items for which *pred* + returns ``True`` with the items from the iterable *substitutes*. + + >>> iterable = [1, 1, 0, 1, 1, 0, 1, 1] + >>> pred = lambda x: x == 0 + >>> substitutes = (2, 3) + >>> list(replace(iterable, pred, substitutes)) + [1, 1, 2, 3, 1, 1, 2, 3, 1, 1] + + If *count* is given, the number of replacements will be limited: + + >>> iterable = [1, 1, 0, 1, 1, 0, 1, 1, 0] + >>> pred = lambda x: x == 0 + >>> substitutes = [None] + >>> list(replace(iterable, pred, substitutes, count=2)) + [1, 1, None, 1, 1, None, 1, 1, 0] + + Use *window_size* to control the number of items passed as arguments to + *pred*. This allows for locating and replacing subsequences. + + >>> iterable = [0, 1, 2, 5, 0, 1, 2, 5] + >>> window_size = 3 + >>> pred = lambda *args: args == (0, 1, 2) # 3 items passed to pred + >>> substitutes = [3, 4] # Splice in these items + >>> list(replace(iterable, pred, substitutes, window_size=window_size)) + [3, 4, 5, 3, 4, 5] + + """ + if window_size < 1: + raise ValueError('window_size must be at least 1') + + # Save the substitutes iterable, since it's used more than once + substitutes = tuple(substitutes) + + # Add padding such that the number of windows matches the length of the + # iterable + it = chain(iterable, [_marker] * (window_size - 1)) + windows = windowed(it, window_size) + + n = 0 + for w in windows: + # If the current window matches our predicate (and we haven't hit + # our maximum number of replacements), splice in the substitutes + # and then consume the following windows that overlap with this one. + # For example, if the iterable is (0, 1, 2, 3, 4...) + # and the window size is 2, we have (0, 1), (1, 2), (2, 3)... + # If the predicate matches on (0, 1), we need to zap (0, 1) and (1, 2) + if pred(*w): + if (count is None) or (n < count): + n += 1 + yield from substitutes + consume(windows, window_size - 1) + continue + + # If there was no match (or we've reached the replacement limit), + # yield the first item from the window. + if w and (w[0] is not _marker): + yield w[0] + + +def partitions(iterable): + """Yield all possible order-preserving partitions of *iterable*. + + >>> iterable = 'abc' + >>> for part in partitions(iterable): + ... print([''.join(p) for p in part]) + ['abc'] + ['a', 'bc'] + ['ab', 'c'] + ['a', 'b', 'c'] + + This is unrelated to :func:`partition`. + + """ + sequence = list(iterable) + n = len(sequence) + for i in powerset(range(1, n)): + yield [sequence[i:j] for i, j in zip((0,) + i, i + (n,))] + + +def set_partitions(iterable, k=None): + """ + Yield the set partitions of *iterable* into *k* parts. Set partitions are + not order-preserving. + + >>> iterable = 'abc' + >>> for part in set_partitions(iterable, 2): + ... print([''.join(p) for p in part]) + ['a', 'bc'] + ['ab', 'c'] + ['b', 'ac'] + + + If *k* is not given, every set partition is generated. + + >>> iterable = 'abc' + >>> for part in set_partitions(iterable): + ... print([''.join(p) for p in part]) + ['abc'] + ['a', 'bc'] + ['ab', 'c'] + ['b', 'ac'] + ['a', 'b', 'c'] + + """ + L = list(iterable) + n = len(L) + if k is not None: + if k < 1: + raise ValueError( + "Can't partition in a negative or zero number of groups" + ) + elif k > n: + return + + def set_partitions_helper(L, k): + n = len(L) + if k == 1: + yield [L] + elif n == k: + yield [[s] for s in L] + else: + e, *M = L + for p in set_partitions_helper(M, k - 1): + yield [[e], *p] + for p in set_partitions_helper(M, k): + for i in range(len(p)): + yield p[:i] + [[e] + p[i]] + p[i + 1 :] + + if k is None: + for k in range(1, n + 1): + yield from set_partitions_helper(L, k) + else: + yield from set_partitions_helper(L, k) + + +class time_limited: + """ + Yield items from *iterable* until *limit_seconds* have passed. + If the time limit expires before all items have been yielded, the + ``timed_out`` parameter will be set to ``True``. + + >>> from time import sleep + >>> def generator(): + ... yield 1 + ... yield 2 + ... sleep(0.2) + ... yield 3 + >>> iterable = time_limited(0.1, generator()) + >>> list(iterable) + [1, 2] + >>> iterable.timed_out + True + + Note that the time is checked before each item is yielded, and iteration + stops if the time elapsed is greater than *limit_seconds*. If your time + limit is 1 second, but it takes 2 seconds to generate the first item from + the iterable, the function will run for 2 seconds and not yield anything. + As a special case, when *limit_seconds* is zero, the iterator never + returns anything. + + """ + + def __init__(self, limit_seconds, iterable): + if limit_seconds < 0: + raise ValueError('limit_seconds must be positive') + self.limit_seconds = limit_seconds + self._iterable = iter(iterable) + self._start_time = monotonic() + self.timed_out = False + + def __iter__(self): + return self + + def __next__(self): + if self.limit_seconds == 0: + self.timed_out = True + raise StopIteration + item = next(self._iterable) + if monotonic() - self._start_time > self.limit_seconds: + self.timed_out = True + raise StopIteration + + return item + + +def only(iterable, default=None, too_long=None): + """If *iterable* has only one item, return it. + If it has zero items, return *default*. + If it has more than one item, raise the exception given by *too_long*, + which is ``ValueError`` by default. + + >>> only([], default='missing') + 'missing' + >>> only([1]) + 1 + >>> only([1, 2]) # doctest: +IGNORE_EXCEPTION_DETAIL + Traceback (most recent call last): + ... + ValueError: Expected exactly one item in iterable, but got 1, 2, + and perhaps more.' + >>> only([1, 2], too_long=TypeError) # doctest: +IGNORE_EXCEPTION_DETAIL + Traceback (most recent call last): + ... + TypeError + + Note that :func:`only` attempts to advance *iterable* twice to ensure there + is only one item. See :func:`spy` or :func:`peekable` to check + iterable contents less destructively. + """ + it = iter(iterable) + first_value = next(it, default) + + try: + second_value = next(it) + except StopIteration: + pass + else: + msg = ( + 'Expected exactly one item in iterable, but got {!r}, {!r}, ' + 'and perhaps more.'.format(first_value, second_value) + ) + raise too_long or ValueError(msg) + + return first_value + + +class _IChunk: + def __init__(self, iterable, n): + self._it = islice(iterable, n) + self._cache = deque() + + def fill_cache(self): + self._cache.extend(self._it) + + def __iter__(self): + return self + + def __next__(self): + try: + return next(self._it) + except StopIteration: + if self._cache: + return self._cache.popleft() + else: + raise + + +def ichunked(iterable, n): + """Break *iterable* into sub-iterables with *n* elements each. + :func:`ichunked` is like :func:`chunked`, but it yields iterables + instead of lists. + + If the sub-iterables are read in order, the elements of *iterable* + won't be stored in memory. + If they are read out of order, :func:`itertools.tee` is used to cache + elements as necessary. + + >>> from itertools import count + >>> all_chunks = ichunked(count(), 4) + >>> c_1, c_2, c_3 = next(all_chunks), next(all_chunks), next(all_chunks) + >>> list(c_2) # c_1's elements have been cached; c_3's haven't been + [4, 5, 6, 7] + >>> list(c_1) + [0, 1, 2, 3] + >>> list(c_3) + [8, 9, 10, 11] + + """ + source = peekable(iter(iterable)) + ichunk_marker = object() + while True: + # Check to see whether we're at the end of the source iterable + item = source.peek(ichunk_marker) + if item is ichunk_marker: + return + + chunk = _IChunk(source, n) + yield chunk + + # Advance the source iterable and fill previous chunk's cache + chunk.fill_cache() + + +def iequals(*iterables): + """Return ``True`` if all given *iterables* are equal to each other, + which means that they contain the same elements in the same order. + + The function is useful for comparing iterables of different data types + or iterables that do not support equality checks. + + >>> iequals("abc", ['a', 'b', 'c'], ('a', 'b', 'c'), iter("abc")) + True + + >>> iequals("abc", "acb") + False + + Not to be confused with :func:`all_equal`, which checks whether all + elements of iterable are equal to each other. + + """ + return all(map(all_equal, zip_longest(*iterables, fillvalue=object()))) + + +def distinct_combinations(iterable, r): + """Yield the distinct combinations of *r* items taken from *iterable*. + + >>> list(distinct_combinations([0, 0, 1], 2)) + [(0, 0), (0, 1)] + + Equivalent to ``set(combinations(iterable))``, except duplicates are not + generated and thrown away. For larger input sequences this is much more + efficient. + + """ + if r < 0: + raise ValueError('r must be non-negative') + elif r == 0: + yield () + return + pool = tuple(iterable) + generators = [unique_everseen(enumerate(pool), key=itemgetter(1))] + current_combo = [None] * r + level = 0 + while generators: + try: + cur_idx, p = next(generators[-1]) + except StopIteration: + generators.pop() + level -= 1 + continue + current_combo[level] = p + if level + 1 == r: + yield tuple(current_combo) + else: + generators.append( + unique_everseen( + enumerate(pool[cur_idx + 1 :], cur_idx + 1), + key=itemgetter(1), + ) + ) + level += 1 + + +def filter_except(validator, iterable, *exceptions): + """Yield the items from *iterable* for which the *validator* function does + not raise one of the specified *exceptions*. + + *validator* is called for each item in *iterable*. + It should be a function that accepts one argument and raises an exception + if that item is not valid. + + >>> iterable = ['1', '2', 'three', '4', None] + >>> list(filter_except(int, iterable, ValueError, TypeError)) + ['1', '2', '4'] + + If an exception other than one given by *exceptions* is raised by + *validator*, it is raised like normal. + """ + for item in iterable: + try: + validator(item) + except exceptions: + pass + else: + yield item + + +def map_except(function, iterable, *exceptions): + """Transform each item from *iterable* with *function* and yield the + result, unless *function* raises one of the specified *exceptions*. + + *function* is called to transform each item in *iterable*. + It should accept one argument. + + >>> iterable = ['1', '2', 'three', '4', None] + >>> list(map_except(int, iterable, ValueError, TypeError)) + [1, 2, 4] + + If an exception other than one given by *exceptions* is raised by + *function*, it is raised like normal. + """ + for item in iterable: + try: + yield function(item) + except exceptions: + pass + + +def map_if(iterable, pred, func, func_else=lambda x: x): + """Evaluate each item from *iterable* using *pred*. If the result is + equivalent to ``True``, transform the item with *func* and yield it. + Otherwise, transform the item with *func_else* and yield it. + + *pred*, *func*, and *func_else* should each be functions that accept + one argument. By default, *func_else* is the identity function. + + >>> from math import sqrt + >>> iterable = list(range(-5, 5)) + >>> iterable + [-5, -4, -3, -2, -1, 0, 1, 2, 3, 4] + >>> list(map_if(iterable, lambda x: x > 3, lambda x: 'toobig')) + [-5, -4, -3, -2, -1, 0, 1, 2, 3, 'toobig'] + >>> list(map_if(iterable, lambda x: x >= 0, + ... lambda x: f'{sqrt(x):.2f}', lambda x: None)) + [None, None, None, None, None, '0.00', '1.00', '1.41', '1.73', '2.00'] + """ + for item in iterable: + yield func(item) if pred(item) else func_else(item) + + +def _sample_unweighted(iterable, k): + # Implementation of "Algorithm L" from the 1994 paper by Kim-Hung Li: + # "Reservoir-Sampling Algorithms of Time Complexity O(n(1+log(N/n)))". + + # Fill up the reservoir (collection of samples) with the first `k` samples + reservoir = take(k, iterable) + + # Generate random number that's the largest in a sample of k U(0,1) numbers + # Largest order statistic: https://en.wikipedia.org/wiki/Order_statistic + W = exp(log(random()) / k) + + # The number of elements to skip before changing the reservoir is a random + # number with a geometric distribution. Sample it using random() and logs. + next_index = k + floor(log(random()) / log(1 - W)) + + for index, element in enumerate(iterable, k): + if index == next_index: + reservoir[randrange(k)] = element + # The new W is the largest in a sample of k U(0, `old_W`) numbers + W *= exp(log(random()) / k) + next_index += floor(log(random()) / log(1 - W)) + 1 + + return reservoir + + +def _sample_weighted(iterable, k, weights): + # Implementation of "A-ExpJ" from the 2006 paper by Efraimidis et al. : + # "Weighted random sampling with a reservoir". + + # Log-transform for numerical stability for weights that are small/large + weight_keys = (log(random()) / weight for weight in weights) + + # Fill up the reservoir (collection of samples) with the first `k` + # weight-keys and elements, then heapify the list. + reservoir = take(k, zip(weight_keys, iterable)) + heapify(reservoir) + + # The number of jumps before changing the reservoir is a random variable + # with an exponential distribution. Sample it using random() and logs. + smallest_weight_key, _ = reservoir[0] + weights_to_skip = log(random()) / smallest_weight_key + + for weight, element in zip(weights, iterable): + if weight >= weights_to_skip: + # The notation here is consistent with the paper, but we store + # the weight-keys in log-space for better numerical stability. + smallest_weight_key, _ = reservoir[0] + t_w = exp(weight * smallest_weight_key) + r_2 = uniform(t_w, 1) # generate U(t_w, 1) + weight_key = log(r_2) / weight + heapreplace(reservoir, (weight_key, element)) + smallest_weight_key, _ = reservoir[0] + weights_to_skip = log(random()) / smallest_weight_key + else: + weights_to_skip -= weight + + # Equivalent to [element for weight_key, element in sorted(reservoir)] + return [heappop(reservoir)[1] for _ in range(k)] + + +def sample(iterable, k, weights=None): + """Return a *k*-length list of elements chosen (without replacement) + from the *iterable*. Like :func:`random.sample`, but works on iterables + of unknown length. + + >>> iterable = range(100) + >>> sample(iterable, 5) # doctest: +SKIP + [81, 60, 96, 16, 4] + + An iterable with *weights* may also be given: + + >>> iterable = range(100) + >>> weights = (i * i + 1 for i in range(100)) + >>> sampled = sample(iterable, 5, weights=weights) # doctest: +SKIP + [79, 67, 74, 66, 78] + + The algorithm can also be used to generate weighted random permutations. + The relative weight of each item determines the probability that it + appears late in the permutation. + + >>> data = "abcdefgh" + >>> weights = range(1, len(data) + 1) + >>> sample(data, k=len(data), weights=weights) # doctest: +SKIP + ['c', 'a', 'b', 'e', 'g', 'd', 'h', 'f'] + """ + if k == 0: + return [] + + iterable = iter(iterable) + if weights is None: + return _sample_unweighted(iterable, k) + else: + weights = iter(weights) + return _sample_weighted(iterable, k, weights) + + +def is_sorted(iterable, key=None, reverse=False, strict=False): + """Returns ``True`` if the items of iterable are in sorted order, and + ``False`` otherwise. *key* and *reverse* have the same meaning that they do + in the built-in :func:`sorted` function. + + >>> is_sorted(['1', '2', '3', '4', '5'], key=int) + True + >>> is_sorted([5, 4, 3, 1, 2], reverse=True) + False + + If *strict*, tests for strict sorting, that is, returns ``False`` if equal + elements are found: + + >>> is_sorted([1, 2, 2]) + True + >>> is_sorted([1, 2, 2], strict=True) + False + + The function returns ``False`` after encountering the first out-of-order + item. If there are no out-of-order items, the iterable is exhausted. + """ + + compare = (le if reverse else ge) if strict else (lt if reverse else gt) + it = iterable if key is None else map(key, iterable) + return not any(starmap(compare, pairwise(it))) + + +class AbortThread(BaseException): + pass + + +class callback_iter: + """Convert a function that uses callbacks to an iterator. + + Let *func* be a function that takes a `callback` keyword argument. + For example: + + >>> def func(callback=None): + ... for i, c in [(1, 'a'), (2, 'b'), (3, 'c')]: + ... if callback: + ... callback(i, c) + ... return 4 + + + Use ``with callback_iter(func)`` to get an iterator over the parameters + that are delivered to the callback. + + >>> with callback_iter(func) as it: + ... for args, kwargs in it: + ... print(args) + (1, 'a') + (2, 'b') + (3, 'c') + + The function will be called in a background thread. The ``done`` property + indicates whether it has completed execution. + + >>> it.done + True + + If it completes successfully, its return value will be available + in the ``result`` property. + + >>> it.result + 4 + + Notes: + + * If the function uses some keyword argument besides ``callback``, supply + *callback_kwd*. + * If it finished executing, but raised an exception, accessing the + ``result`` property will raise the same exception. + * If it hasn't finished executing, accessing the ``result`` + property from within the ``with`` block will raise ``RuntimeError``. + * If it hasn't finished executing, accessing the ``result`` property from + outside the ``with`` block will raise a + ``more_itertools.AbortThread`` exception. + * Provide *wait_seconds* to adjust how frequently the it is polled for + output. + + """ + + def __init__(self, func, callback_kwd='callback', wait_seconds=0.1): + self._func = func + self._callback_kwd = callback_kwd + self._aborted = False + self._future = None + self._wait_seconds = wait_seconds + # Lazily import concurrent.future + self._executor = __import__( + ).futures.__import__("concurrent.futures").futures.ThreadPoolExecutor(max_workers=1) + self._iterator = self._reader() + + def __enter__(self): + return self + + def __exit__(self, exc_type, exc_value, traceback): + self._aborted = True + self._executor.shutdown() + + def __iter__(self): + return self + + def __next__(self): + return next(self._iterator) + + @property + def done(self): + if self._future is None: + return False + return self._future.done() + + @property + def result(self): + if not self.done: + raise RuntimeError('Function has not yet completed') + + return self._future.result() + + def _reader(self): + q = Queue() + + def callback(*args, **kwargs): + if self._aborted: + raise AbortThread('canceled by user') + + q.put((args, kwargs)) + + self._future = self._executor.submit( + self._func, **{self._callback_kwd: callback} + ) + + while True: + try: + item = q.get(timeout=self._wait_seconds) + except Empty: + pass + else: + q.task_done() + yield item + + if self._future.done(): + break + + remaining = [] + while True: + try: + item = q.get_nowait() + except Empty: + break + else: + q.task_done() + remaining.append(item) + q.join() + yield from remaining + + +def windowed_complete(iterable, n): + """ + Yield ``(beginning, middle, end)`` tuples, where: + + * Each ``middle`` has *n* items from *iterable* + * Each ``beginning`` has the items before the ones in ``middle`` + * Each ``end`` has the items after the ones in ``middle`` + + >>> iterable = range(7) + >>> n = 3 + >>> for beginning, middle, end in windowed_complete(iterable, n): + ... print(beginning, middle, end) + () (0, 1, 2) (3, 4, 5, 6) + (0,) (1, 2, 3) (4, 5, 6) + (0, 1) (2, 3, 4) (5, 6) + (0, 1, 2) (3, 4, 5) (6,) + (0, 1, 2, 3) (4, 5, 6) () + + Note that *n* must be at least 0 and most equal to the length of + *iterable*. + + This function will exhaust the iterable and may require significant + storage. + """ + if n < 0: + raise ValueError('n must be >= 0') + + seq = tuple(iterable) + size = len(seq) + + if n > size: + raise ValueError('n must be <= len(seq)') + + for i in range(size - n + 1): + beginning = seq[:i] + middle = seq[i : i + n] + end = seq[i + n :] + yield beginning, middle, end + + +def all_unique(iterable, key=None): + """ + Returns ``True`` if all the elements of *iterable* are unique (no two + elements are equal). + + >>> all_unique('ABCB') + False + + If a *key* function is specified, it will be used to make comparisons. + + >>> all_unique('ABCb') + True + >>> all_unique('ABCb', str.lower) + False + + The function returns as soon as the first non-unique element is + encountered. Iterables with a mix of hashable and unhashable items can + be used, but the function will be slower for unhashable items. + """ + seenset = set() + seenset_add = seenset.add + seenlist = [] + seenlist_add = seenlist.append + for element in map(key, iterable) if key else iterable: + try: + if element in seenset: + return False + seenset_add(element) + except TypeError: + if element in seenlist: + return False + seenlist_add(element) + return True + + +def nth_product(index, *args): + """Equivalent to ``list(product(*args))[index]``. + + The products of *args* can be ordered lexicographically. + :func:`nth_product` computes the product at sort position *index* without + computing the previous products. + + >>> nth_product(8, range(2), range(2), range(2), range(2)) + (1, 0, 0, 0) + + ``IndexError`` will be raised if the given *index* is invalid. + """ + pools = list(map(tuple, reversed(args))) + ns = list(map(len, pools)) + + c = reduce(mul, ns) + + if index < 0: + index += c + + if not 0 <= index < c: + raise IndexError + + result = [] + for pool, n in zip(pools, ns): + result.append(pool[index % n]) + index //= n + + return tuple(reversed(result)) + + +def nth_permutation(iterable, r, index): + """Equivalent to ``list(permutations(iterable, r))[index]``` + + The subsequences of *iterable* that are of length *r* where order is + important can be ordered lexicographically. :func:`nth_permutation` + computes the subsequence at sort position *index* directly, without + computing the previous subsequences. + + >>> nth_permutation('ghijk', 2, 5) + ('h', 'i') + + ``ValueError`` will be raised If *r* is negative or greater than the length + of *iterable*. + ``IndexError`` will be raised if the given *index* is invalid. + """ + pool = list(iterable) + n = len(pool) + + if r is None or r == n: + r, c = n, factorial(n) + elif not 0 <= r < n: + raise ValueError + else: + c = perm(n, r) + + if index < 0: + index += c + + if not 0 <= index < c: + raise IndexError + + if c == 0: + return tuple() + + result = [0] * r + q = index * factorial(n) // c if r < n else index + for d in range(1, n + 1): + q, i = divmod(q, d) + if 0 <= n - d < r: + result[n - d] = i + if q == 0: + break + + return tuple(map(pool.pop, result)) + + +def nth_combination_with_replacement(iterable, r, index): + """Equivalent to + ``list(combinations_with_replacement(iterable, r))[index]``. + + + The subsequences with repetition of *iterable* that are of length *r* can + be ordered lexicographically. :func:`nth_combination_with_replacement` + computes the subsequence at sort position *index* directly, without + computing the previous subsequences with replacement. + + >>> nth_combination_with_replacement(range(5), 3, 5) + (0, 1, 1) + + ``ValueError`` will be raised If *r* is negative or greater than the length + of *iterable*. + ``IndexError`` will be raised if the given *index* is invalid. + """ + pool = tuple(iterable) + n = len(pool) + if (r < 0) or (r > n): + raise ValueError + + c = comb(n + r - 1, r) + + if index < 0: + index += c + + if (index < 0) or (index >= c): + raise IndexError + + result = [] + i = 0 + while r: + r -= 1 + while n >= 0: + num_combs = comb(n + r - 1, r) + if index < num_combs: + break + n -= 1 + i += 1 + index -= num_combs + result.append(pool[i]) + + return tuple(result) + + +def value_chain(*args): + """Yield all arguments passed to the function in the same order in which + they were passed. If an argument itself is iterable then iterate over its + values. + + >>> list(value_chain(1, 2, 3, [4, 5, 6])) + [1, 2, 3, 4, 5, 6] + + Binary and text strings are not considered iterable and are emitted + as-is: + + >>> list(value_chain('12', '34', ['56', '78'])) + ['12', '34', '56', '78'] + + + Multiple levels of nesting are not flattened. + + """ + for value in args: + if isinstance(value, (str, bytes)): + yield value + continue + try: + yield from value + except TypeError: + yield value + + +def product_index(element, *args): + """Equivalent to ``list(product(*args)).index(element)`` + + The products of *args* can be ordered lexicographically. + :func:`product_index` computes the first index of *element* without + computing the previous products. + + >>> product_index([8, 2], range(10), range(5)) + 42 + + ``ValueError`` will be raised if the given *element* isn't in the product + of *args*. + """ + index = 0 + + for x, pool in zip_longest(element, args, fillvalue=_marker): + if x is _marker or pool is _marker: + raise ValueError('element is not a product of args') + + pool = tuple(pool) + index = index * len(pool) + pool.index(x) + + return index + + +def combination_index(element, iterable): + """Equivalent to ``list(combinations(iterable, r)).index(element)`` + + The subsequences of *iterable* that are of length *r* can be ordered + lexicographically. :func:`combination_index` computes the index of the + first *element*, without computing the previous combinations. + + >>> combination_index('adf', 'abcdefg') + 10 + + ``ValueError`` will be raised if the given *element* isn't one of the + combinations of *iterable*. + """ + element = enumerate(element) + k, y = next(element, (None, None)) + if k is None: + return 0 + + indexes = [] + pool = enumerate(iterable) + for n, x in pool: + if x == y: + indexes.append(n) + tmp, y = next(element, (None, None)) + if tmp is None: + break + else: + k = tmp + else: + raise ValueError('element is not a combination of iterable') + + n, _ = last(pool, default=(n, None)) + + # Python versions below 3.8 don't have math.comb + index = 1 + for i, j in enumerate(reversed(indexes), start=1): + j = n - j + if i <= j: + index += comb(j, i) + + return comb(n + 1, k + 1) - index + + +def combination_with_replacement_index(element, iterable): + """Equivalent to + ``list(combinations_with_replacement(iterable, r)).index(element)`` + + The subsequences with repetition of *iterable* that are of length *r* can + be ordered lexicographically. :func:`combination_with_replacement_index` + computes the index of the first *element*, without computing the previous + combinations with replacement. + + >>> combination_with_replacement_index('adf', 'abcdefg') + 20 + + ``ValueError`` will be raised if the given *element* isn't one of the + combinations with replacement of *iterable*. + """ + element = tuple(element) + l = len(element) + element = enumerate(element) + + k, y = next(element, (None, None)) + if k is None: + return 0 + + indexes = [] + pool = tuple(iterable) + for n, x in enumerate(pool): + while x == y: + indexes.append(n) + tmp, y = next(element, (None, None)) + if tmp is None: + break + else: + k = tmp + if y is None: + break + else: + raise ValueError( + 'element is not a combination with replacement of iterable' + ) + + n = len(pool) + occupations = [0] * n + for p in indexes: + occupations[p] += 1 + + index = 0 + cumulative_sum = 0 + for k in range(1, n): + cumulative_sum += occupations[k - 1] + j = l + n - 1 - k - cumulative_sum + i = n - k + if i <= j: + index += comb(j, i) + + return index + + +def permutation_index(element, iterable): + """Equivalent to ``list(permutations(iterable, r)).index(element)``` + + The subsequences of *iterable* that are of length *r* where order is + important can be ordered lexicographically. :func:`permutation_index` + computes the index of the first *element* directly, without computing + the previous permutations. + + >>> permutation_index([1, 3, 2], range(5)) + 19 + + ``ValueError`` will be raised if the given *element* isn't one of the + permutations of *iterable*. + """ + index = 0 + pool = list(iterable) + for i, x in zip(range(len(pool), -1, -1), element): + r = pool.index(x) + index = index * i + r + del pool[r] + + return index + + +class countable: + """Wrap *iterable* and keep a count of how many items have been consumed. + + The ``items_seen`` attribute starts at ``0`` and increments as the iterable + is consumed: + + >>> iterable = map(str, range(10)) + >>> it = countable(iterable) + >>> it.items_seen + 0 + >>> next(it), next(it) + ('0', '1') + >>> list(it) + ['2', '3', '4', '5', '6', '7', '8', '9'] + >>> it.items_seen + 10 + """ + + def __init__(self, iterable): + self._it = iter(iterable) + self.items_seen = 0 + + def __iter__(self): + return self + + def __next__(self): + item = next(self._it) + self.items_seen += 1 + + return item + + +def chunked_even(iterable, n): + """Break *iterable* into lists of approximately length *n*. + Items are distributed such the lengths of the lists differ by at most + 1 item. + + >>> iterable = [1, 2, 3, 4, 5, 6, 7] + >>> n = 3 + >>> list(chunked_even(iterable, n)) # List lengths: 3, 2, 2 + [[1, 2, 3], [4, 5], [6, 7]] + >>> list(chunked(iterable, n)) # List lengths: 3, 3, 1 + [[1, 2, 3], [4, 5, 6], [7]] + + """ + + len_method = getattr(iterable, '__len__', None) + + if len_method is None: + return _chunked_even_online(iterable, n) + else: + return _chunked_even_finite(iterable, len_method(), n) + + +def _chunked_even_online(iterable, n): + buffer = [] + maxbuf = n + (n - 2) * (n - 1) + for x in iterable: + buffer.append(x) + if len(buffer) == maxbuf: + yield buffer[:n] + buffer = buffer[n:] + yield from _chunked_even_finite(buffer, len(buffer), n) + + +def _chunked_even_finite(iterable, N, n): + if N < 1: + return + + # Lists are either size `full_size <= n` or `partial_size = full_size - 1` + q, r = divmod(N, n) + num_lists = q + (1 if r > 0 else 0) + q, r = divmod(N, num_lists) + full_size = q + (1 if r > 0 else 0) + partial_size = full_size - 1 + num_full = N - partial_size * num_lists + num_partial = num_lists - num_full + + # Yield num_full lists of full_size + partial_start_idx = num_full * full_size + if full_size > 0: + for i in range(0, partial_start_idx, full_size): + yield list(islice(iterable, i, i + full_size)) + + # Yield num_partial lists of partial_size + if partial_size > 0: + for i in range( + partial_start_idx, + partial_start_idx + (num_partial * partial_size), + partial_size, + ): + yield list(islice(iterable, i, i + partial_size)) + + +def zip_broadcast(*objects, scalar_types=(str, bytes), strict=False): + """A version of :func:`zip` that "broadcasts" any scalar + (i.e., non-iterable) items into output tuples. + + >>> iterable_1 = [1, 2, 3] + >>> iterable_2 = ['a', 'b', 'c'] + >>> scalar = '_' + >>> list(zip_broadcast(iterable_1, iterable_2, scalar)) + [(1, 'a', '_'), (2, 'b', '_'), (3, 'c', '_')] + + The *scalar_types* keyword argument determines what types are considered + scalar. It is set to ``(str, bytes)`` by default. Set it to ``None`` to + treat strings and byte strings as iterable: + + >>> list(zip_broadcast('abc', 0, 'xyz', scalar_types=None)) + [('a', 0, 'x'), ('b', 0, 'y'), ('c', 0, 'z')] + + If the *strict* keyword argument is ``True``, then + ``UnequalIterablesError`` will be raised if any of the iterables have + different lengths. + """ + + def is_scalar(obj): + if scalar_types and isinstance(obj, scalar_types): + return True + try: + iter(obj) + except TypeError: + return True + else: + return False + + size = len(objects) + if not size: + return + + new_item = [None] * size + iterables, iterable_positions = [], [] + for i, obj in enumerate(objects): + if is_scalar(obj): + new_item[i] = obj + else: + iterables.append(iter(obj)) + iterable_positions.append(i) + + if not iterables: + yield tuple(objects) + return + + zipper = _zip_equal if strict else zip + for item in zipper(*iterables): + for i, new_item[i] in zip(iterable_positions, item): + pass + yield tuple(new_item) + + +def unique_in_window(iterable, n, key=None): + """Yield the items from *iterable* that haven't been seen recently. + *n* is the size of the lookback window. + + >>> iterable = [0, 1, 0, 2, 3, 0] + >>> n = 3 + >>> list(unique_in_window(iterable, n)) + [0, 1, 2, 3, 0] + + The *key* function, if provided, will be used to determine uniqueness: + + >>> list(unique_in_window('abAcda', 3, key=lambda x: x.lower())) + ['a', 'b', 'c', 'd', 'a'] + + The items in *iterable* must be hashable. + + """ + if n <= 0: + raise ValueError('n must be greater than 0') + + window = deque(maxlen=n) + counts = defaultdict(int) + use_key = key is not None + + for item in iterable: + if len(window) == n: + to_discard = window[0] + if counts[to_discard] == 1: + del counts[to_discard] + else: + counts[to_discard] -= 1 + + k = key(item) if use_key else item + if k not in counts: + yield item + counts[k] += 1 + window.append(k) + + +def duplicates_everseen(iterable, key=None): + """Yield duplicate elements after their first appearance. + + >>> list(duplicates_everseen('mississippi')) + ['s', 'i', 's', 's', 'i', 'p', 'i'] + >>> list(duplicates_everseen('AaaBbbCccAaa', str.lower)) + ['a', 'a', 'b', 'b', 'c', 'c', 'A', 'a', 'a'] + + This function is analogous to :func:`unique_everseen` and is subject to + the same performance considerations. + + """ + seen_set = set() + seen_list = [] + use_key = key is not None + + for element in iterable: + k = key(element) if use_key else element + try: + if k not in seen_set: + seen_set.add(k) + else: + yield element + except TypeError: + if k not in seen_list: + seen_list.append(k) + else: + yield element + + +def duplicates_justseen(iterable, key=None): + """Yields serially-duplicate elements after their first appearance. + + >>> list(duplicates_justseen('mississippi')) + ['s', 's', 'p'] + >>> list(duplicates_justseen('AaaBbbCccAaa', str.lower)) + ['a', 'a', 'b', 'b', 'c', 'c', 'a', 'a'] + + This function is analogous to :func:`unique_justseen`. + + """ + return flatten(g for _, g in groupby(iterable, key) for _ in g) + + +def classify_unique(iterable, key=None): + """Classify each element in terms of its uniqueness. + + For each element in the input iterable, return a 3-tuple consisting of: + + 1. The element itself + 2. ``False`` if the element is equal to the one preceding it in the input, + ``True`` otherwise (i.e. the equivalent of :func:`unique_justseen`) + 3. ``False`` if this element has been seen anywhere in the input before, + ``True`` otherwise (i.e. the equivalent of :func:`unique_everseen`) + + >>> list(classify_unique('otto')) # doctest: +NORMALIZE_WHITESPACE + [('o', True, True), + ('t', True, True), + ('t', False, False), + ('o', True, False)] + + This function is analogous to :func:`unique_everseen` and is subject to + the same performance considerations. + + """ + seen_set = set() + seen_list = [] + use_key = key is not None + previous = None + + for i, element in enumerate(iterable): + k = key(element) if use_key else element + is_unique_justseen = not i or previous != k + previous = k + is_unique_everseen = False + try: + if k not in seen_set: + seen_set.add(k) + is_unique_everseen = True + except TypeError: + if k not in seen_list: + seen_list.append(k) + is_unique_everseen = True + yield element, is_unique_justseen, is_unique_everseen + + +def minmax(iterable_or_value, *others, key=None, default=_marker): + """Returns both the smallest and largest items in an iterable + or the largest of two or more arguments. + + >>> minmax([3, 1, 5]) + (1, 5) + + >>> minmax(4, 2, 6) + (2, 6) + + If a *key* function is provided, it will be used to transform the input + items for comparison. + + >>> minmax([5, 30], key=str) # '30' sorts before '5' + (30, 5) + + If a *default* value is provided, it will be returned if there are no + input items. + + >>> minmax([], default=(0, 0)) + (0, 0) + + Otherwise ``ValueError`` is raised. + + This function is based on the + `recipe `__ by + Raymond Hettinger and takes care to minimize the number of comparisons + performed. + """ + iterable = (iterable_or_value, *others) if others else iterable_or_value + + it = iter(iterable) + + try: + lo = hi = next(it) + except StopIteration as e: + if default is _marker: + raise ValueError( + '`minmax()` argument is an empty iterable. ' + 'Provide a `default` value to suppress this error.' + ) from e + return default + + # Different branches depending on the presence of key. This saves a lot + # of unimportant copies which would slow the "key=None" branch + # significantly down. + if key is None: + for x, y in zip_longest(it, it, fillvalue=lo): + if y < x: + x, y = y, x + if x < lo: + lo = x + if hi < y: + hi = y + + else: + lo_key = hi_key = key(lo) + + for x, y in zip_longest(it, it, fillvalue=lo): + x_key, y_key = key(x), key(y) + + if y_key < x_key: + x, y, x_key, y_key = y, x, y_key, x_key + if x_key < lo_key: + lo, lo_key = x, x_key + if hi_key < y_key: + hi, hi_key = y, y_key + + return lo, hi + + +def constrained_batches( + iterable, max_size, max_count=None, get_len=len, strict=True +): + """Yield batches of items from *iterable* with a combined size limited by + *max_size*. + + >>> iterable = [b'12345', b'123', b'12345678', b'1', b'1', b'12', b'1'] + >>> list(constrained_batches(iterable, 10)) + [(b'12345', b'123'), (b'12345678', b'1', b'1'), (b'12', b'1')] + + If a *max_count* is supplied, the number of items per batch is also + limited: + + >>> iterable = [b'12345', b'123', b'12345678', b'1', b'1', b'12', b'1'] + >>> list(constrained_batches(iterable, 10, max_count = 2)) + [(b'12345', b'123'), (b'12345678', b'1'), (b'1', b'12'), (b'1',)] + + If a *get_len* function is supplied, use that instead of :func:`len` to + determine item size. + + If *strict* is ``True``, raise ``ValueError`` if any single item is bigger + than *max_size*. Otherwise, allow single items to exceed *max_size*. + """ + if max_size <= 0: + raise ValueError('maximum size must be greater than zero') + + batch = [] + batch_size = 0 + batch_count = 0 + for item in iterable: + item_len = get_len(item) + if strict and item_len > max_size: + raise ValueError('item size exceeds maximum size') + + reached_count = batch_count == max_count + reached_size = item_len + batch_size > max_size + if batch_count and (reached_size or reached_count): + yield tuple(batch) + batch.clear() + batch_size = 0 + batch_count = 0 + + batch.append(item) + batch_size += item_len + batch_count += 1 + + if batch: + yield tuple(batch) + + +def gray_product(*iterables): + """Like :func:`itertools.product`, but return tuples in an order such + that only one element in the generated tuple changes from one iteration + to the next. + + >>> list(gray_product('AB','CD')) + [('A', 'C'), ('B', 'C'), ('B', 'D'), ('A', 'D')] + + This function consumes all of the input iterables before producing output. + If any of the input iterables have fewer than two items, ``ValueError`` + is raised. + + For information on the algorithm, see + `this section `__ + of Donald Knuth's *The Art of Computer Programming*. + """ + all_iterables = tuple(tuple(x) for x in iterables) + iterable_count = len(all_iterables) + for iterable in all_iterables: + if len(iterable) < 2: + raise ValueError("each iterable must have two or more items") + + # This is based on "Algorithm H" from section 7.2.1.1, page 20. + # a holds the indexes of the source iterables for the n-tuple to be yielded + # f is the array of "focus pointers" + # o is the array of "directions" + a = [0] * iterable_count + f = list(range(iterable_count + 1)) + o = [1] * iterable_count + while True: + yield tuple(all_iterables[i][a[i]] for i in range(iterable_count)) + j = f[0] + f[0] = 0 + if j == iterable_count: + break + a[j] = a[j] + o[j] + if a[j] == 0 or a[j] == len(all_iterables[j]) - 1: + o[j] = -o[j] + f[j] = f[j + 1] + f[j + 1] = j + 1 + + +def partial_product(*iterables): + """Yields tuples containing one item from each iterator, with subsequent + tuples changing a single item at a time by advancing each iterator until it + is exhausted. This sequence guarantees every value in each iterable is + output at least once without generating all possible combinations. + + This may be useful, for example, when testing an expensive function. + + >>> list(partial_product('AB', 'C', 'DEF')) + [('A', 'C', 'D'), ('B', 'C', 'D'), ('B', 'C', 'E'), ('B', 'C', 'F')] + """ + + iterators = list(map(iter, iterables)) + + try: + prod = [next(it) for it in iterators] + except StopIteration: + return + yield tuple(prod) + + for i, it in enumerate(iterators): + for prod[i] in it: + yield tuple(prod) + + +def takewhile_inclusive(predicate, iterable): + """A variant of :func:`takewhile` that yields one additional element. + + >>> list(takewhile_inclusive(lambda x: x < 5, [1, 4, 6, 4, 1])) + [1, 4, 6] + + :func:`takewhile` would return ``[1, 4]``. + """ + for x in iterable: + yield x + if not predicate(x): + break + + +def outer_product(func, xs, ys, *args, **kwargs): + """A generalized outer product that applies a binary function to all + pairs of items. Returns a 2D matrix with ``len(xs)`` rows and ``len(ys)`` + columns. + Also accepts ``*args`` and ``**kwargs`` that are passed to ``func``. + + Multiplication table: + + >>> list(outer_product(mul, range(1, 4), range(1, 6))) + [(1, 2, 3, 4, 5), (2, 4, 6, 8, 10), (3, 6, 9, 12, 15)] + + Cross tabulation: + + >>> xs = ['A', 'B', 'A', 'A', 'B', 'B', 'A', 'A', 'B', 'B'] + >>> ys = ['X', 'X', 'X', 'Y', 'Z', 'Z', 'Y', 'Y', 'Z', 'Z'] + >>> rows = list(zip(xs, ys)) + >>> count_rows = lambda x, y: rows.count((x, y)) + >>> list(outer_product(count_rows, sorted(set(xs)), sorted(set(ys)))) + [(2, 3, 0), (1, 0, 4)] + + Usage with ``*args`` and ``**kwargs``: + + >>> animals = ['cat', 'wolf', 'mouse'] + >>> list(outer_product(min, animals, animals, key=len)) + [('cat', 'cat', 'cat'), ('cat', 'wolf', 'wolf'), ('cat', 'wolf', 'mouse')] + """ + ys = tuple(ys) + return batched( + starmap(lambda x, y: func(x, y, *args, **kwargs), product(xs, ys)), + n=len(ys), + ) + + +def iter_suppress(iterable, *exceptions): + """Yield each of the items from *iterable*. If the iteration raises one of + the specified *exceptions*, that exception will be suppressed and iteration + will stop. + + >>> from itertools import chain + >>> def breaks_at_five(x): + ... while True: + ... if x >= 5: + ... raise RuntimeError + ... yield x + ... x += 1 + >>> it_1 = iter_suppress(breaks_at_five(1), RuntimeError) + >>> it_2 = iter_suppress(breaks_at_five(2), RuntimeError) + >>> list(chain(it_1, it_2)) + [1, 2, 3, 4, 2, 3, 4] + """ + try: + yield from iterable + except exceptions: + return + + +def filter_map(func, iterable): + """Apply *func* to every element of *iterable*, yielding only those which + are not ``None``. + + >>> elems = ['1', 'a', '2', 'b', '3'] + >>> list(filter_map(lambda s: int(s) if s.isnumeric() else None, elems)) + [1, 2, 3] + """ + for x in iterable: + y = func(x) + if y is not None: + yield y diff --git a/venv/Lib/site-packages/pkg_resources/_vendor/more_itertools/more.pyi b/venv/Lib/site-packages/pkg_resources/_vendor/more_itertools/more.pyi new file mode 100644 index 0000000..9a5fc91 --- /dev/null +++ b/venv/Lib/site-packages/pkg_resources/_vendor/more_itertools/more.pyi @@ -0,0 +1,695 @@ +"""Stubs for more_itertools.more""" +from __future__ import annotations + +from types import TracebackType +from typing import ( + Any, + Callable, + Container, + ContextManager, + Generic, + Hashable, + Iterable, + Iterator, + overload, + Reversible, + Sequence, + Sized, + Type, + TypeVar, + type_check_only, +) +from typing_extensions import Protocol + +# Type and type variable definitions +_T = TypeVar('_T') +_T1 = TypeVar('_T1') +_T2 = TypeVar('_T2') +_U = TypeVar('_U') +_V = TypeVar('_V') +_W = TypeVar('_W') +_T_co = TypeVar('_T_co', covariant=True) +_GenFn = TypeVar('_GenFn', bound=Callable[..., Iterator[Any]]) +_Raisable = BaseException | Type[BaseException] + +@type_check_only +class _SizedIterable(Protocol[_T_co], Sized, Iterable[_T_co]): ... + +@type_check_only +class _SizedReversible(Protocol[_T_co], Sized, Reversible[_T_co]): ... + +@type_check_only +class _SupportsSlicing(Protocol[_T_co]): + def __getitem__(self, __k: slice) -> _T_co: ... + +def chunked( + iterable: Iterable[_T], n: int | None, strict: bool = ... +) -> Iterator[list[_T]]: ... +@overload +def first(iterable: Iterable[_T]) -> _T: ... +@overload +def first(iterable: Iterable[_T], default: _U) -> _T | _U: ... +@overload +def last(iterable: Iterable[_T]) -> _T: ... +@overload +def last(iterable: Iterable[_T], default: _U) -> _T | _U: ... +@overload +def nth_or_last(iterable: Iterable[_T], n: int) -> _T: ... +@overload +def nth_or_last(iterable: Iterable[_T], n: int, default: _U) -> _T | _U: ... + +class peekable(Generic[_T], Iterator[_T]): + def __init__(self, iterable: Iterable[_T]) -> None: ... + def __iter__(self) -> peekable[_T]: ... + def __bool__(self) -> bool: ... + @overload + def peek(self) -> _T: ... + @overload + def peek(self, default: _U) -> _T | _U: ... + def prepend(self, *items: _T) -> None: ... + def __next__(self) -> _T: ... + @overload + def __getitem__(self, index: int) -> _T: ... + @overload + def __getitem__(self, index: slice) -> list[_T]: ... + +def consumer(func: _GenFn) -> _GenFn: ... +def ilen(iterable: Iterable[_T]) -> int: ... +def iterate(func: Callable[[_T], _T], start: _T) -> Iterator[_T]: ... +def with_iter( + context_manager: ContextManager[Iterable[_T]], +) -> Iterator[_T]: ... +def one( + iterable: Iterable[_T], + too_short: _Raisable | None = ..., + too_long: _Raisable | None = ..., +) -> _T: ... +def raise_(exception: _Raisable, *args: Any) -> None: ... +def strictly_n( + iterable: Iterable[_T], + n: int, + too_short: _GenFn | None = ..., + too_long: _GenFn | None = ..., +) -> list[_T]: ... +def distinct_permutations( + iterable: Iterable[_T], r: int | None = ... +) -> Iterator[tuple[_T, ...]]: ... +def intersperse( + e: _U, iterable: Iterable[_T], n: int = ... +) -> Iterator[_T | _U]: ... +def unique_to_each(*iterables: Iterable[_T]) -> list[list[_T]]: ... +@overload +def windowed( + seq: Iterable[_T], n: int, *, step: int = ... +) -> Iterator[tuple[_T | None, ...]]: ... +@overload +def windowed( + seq: Iterable[_T], n: int, fillvalue: _U, step: int = ... +) -> Iterator[tuple[_T | _U, ...]]: ... +def substrings(iterable: Iterable[_T]) -> Iterator[tuple[_T, ...]]: ... +def substrings_indexes( + seq: Sequence[_T], reverse: bool = ... +) -> Iterator[tuple[Sequence[_T], int, int]]: ... + +class bucket(Generic[_T, _U], Container[_U]): + def __init__( + self, + iterable: Iterable[_T], + key: Callable[[_T], _U], + validator: Callable[[_U], object] | None = ..., + ) -> None: ... + def __contains__(self, value: object) -> bool: ... + def __iter__(self) -> Iterator[_U]: ... + def __getitem__(self, value: object) -> Iterator[_T]: ... + +def spy( + iterable: Iterable[_T], n: int = ... +) -> tuple[list[_T], Iterator[_T]]: ... +def interleave(*iterables: Iterable[_T]) -> Iterator[_T]: ... +def interleave_longest(*iterables: Iterable[_T]) -> Iterator[_T]: ... +def interleave_evenly( + iterables: list[Iterable[_T]], lengths: list[int] | None = ... +) -> Iterator[_T]: ... +def collapse( + iterable: Iterable[Any], + base_type: type | None = ..., + levels: int | None = ..., +) -> Iterator[Any]: ... +@overload +def side_effect( + func: Callable[[_T], object], + iterable: Iterable[_T], + chunk_size: None = ..., + before: Callable[[], object] | None = ..., + after: Callable[[], object] | None = ..., +) -> Iterator[_T]: ... +@overload +def side_effect( + func: Callable[[list[_T]], object], + iterable: Iterable[_T], + chunk_size: int, + before: Callable[[], object] | None = ..., + after: Callable[[], object] | None = ..., +) -> Iterator[_T]: ... +def sliced( + seq: _SupportsSlicing[_T], n: int, strict: bool = ... +) -> Iterator[_T]: ... +def split_at( + iterable: Iterable[_T], + pred: Callable[[_T], object], + maxsplit: int = ..., + keep_separator: bool = ..., +) -> Iterator[list[_T]]: ... +def split_before( + iterable: Iterable[_T], pred: Callable[[_T], object], maxsplit: int = ... +) -> Iterator[list[_T]]: ... +def split_after( + iterable: Iterable[_T], pred: Callable[[_T], object], maxsplit: int = ... +) -> Iterator[list[_T]]: ... +def split_when( + iterable: Iterable[_T], + pred: Callable[[_T, _T], object], + maxsplit: int = ..., +) -> Iterator[list[_T]]: ... +def split_into( + iterable: Iterable[_T], sizes: Iterable[int | None] +) -> Iterator[list[_T]]: ... +@overload +def padded( + iterable: Iterable[_T], + *, + n: int | None = ..., + next_multiple: bool = ..., +) -> Iterator[_T | None]: ... +@overload +def padded( + iterable: Iterable[_T], + fillvalue: _U, + n: int | None = ..., + next_multiple: bool = ..., +) -> Iterator[_T | _U]: ... +@overload +def repeat_last(iterable: Iterable[_T]) -> Iterator[_T]: ... +@overload +def repeat_last(iterable: Iterable[_T], default: _U) -> Iterator[_T | _U]: ... +def distribute(n: int, iterable: Iterable[_T]) -> list[Iterator[_T]]: ... +@overload +def stagger( + iterable: Iterable[_T], + offsets: _SizedIterable[int] = ..., + longest: bool = ..., +) -> Iterator[tuple[_T | None, ...]]: ... +@overload +def stagger( + iterable: Iterable[_T], + offsets: _SizedIterable[int] = ..., + longest: bool = ..., + fillvalue: _U = ..., +) -> Iterator[tuple[_T | _U, ...]]: ... + +class UnequalIterablesError(ValueError): + def __init__(self, details: tuple[int, int, int] | None = ...) -> None: ... + +@overload +def zip_equal(__iter1: Iterable[_T1]) -> Iterator[tuple[_T1]]: ... +@overload +def zip_equal( + __iter1: Iterable[_T1], __iter2: Iterable[_T2] +) -> Iterator[tuple[_T1, _T2]]: ... +@overload +def zip_equal( + __iter1: Iterable[_T], + __iter2: Iterable[_T], + __iter3: Iterable[_T], + *iterables: Iterable[_T], +) -> Iterator[tuple[_T, ...]]: ... +@overload +def zip_offset( + __iter1: Iterable[_T1], + *, + offsets: _SizedIterable[int], + longest: bool = ..., + fillvalue: None = None, +) -> Iterator[tuple[_T1 | None]]: ... +@overload +def zip_offset( + __iter1: Iterable[_T1], + __iter2: Iterable[_T2], + *, + offsets: _SizedIterable[int], + longest: bool = ..., + fillvalue: None = None, +) -> Iterator[tuple[_T1 | None, _T2 | None]]: ... +@overload +def zip_offset( + __iter1: Iterable[_T], + __iter2: Iterable[_T], + __iter3: Iterable[_T], + *iterables: Iterable[_T], + offsets: _SizedIterable[int], + longest: bool = ..., + fillvalue: None = None, +) -> Iterator[tuple[_T | None, ...]]: ... +@overload +def zip_offset( + __iter1: Iterable[_T1], + *, + offsets: _SizedIterable[int], + longest: bool = ..., + fillvalue: _U, +) -> Iterator[tuple[_T1 | _U]]: ... +@overload +def zip_offset( + __iter1: Iterable[_T1], + __iter2: Iterable[_T2], + *, + offsets: _SizedIterable[int], + longest: bool = ..., + fillvalue: _U, +) -> Iterator[tuple[_T1 | _U, _T2 | _U]]: ... +@overload +def zip_offset( + __iter1: Iterable[_T], + __iter2: Iterable[_T], + __iter3: Iterable[_T], + *iterables: Iterable[_T], + offsets: _SizedIterable[int], + longest: bool = ..., + fillvalue: _U, +) -> Iterator[tuple[_T | _U, ...]]: ... +def sort_together( + iterables: Iterable[Iterable[_T]], + key_list: Iterable[int] = ..., + key: Callable[..., Any] | None = ..., + reverse: bool = ..., +) -> list[tuple[_T, ...]]: ... +def unzip(iterable: Iterable[Sequence[_T]]) -> tuple[Iterator[_T], ...]: ... +def divide(n: int, iterable: Iterable[_T]) -> list[Iterator[_T]]: ... +def always_iterable( + obj: object, + base_type: type | tuple[type | tuple[Any, ...], ...] | None = ..., +) -> Iterator[Any]: ... +def adjacent( + predicate: Callable[[_T], bool], + iterable: Iterable[_T], + distance: int = ..., +) -> Iterator[tuple[bool, _T]]: ... +@overload +def groupby_transform( + iterable: Iterable[_T], + keyfunc: None = None, + valuefunc: None = None, + reducefunc: None = None, +) -> Iterator[tuple[_T, Iterator[_T]]]: ... +@overload +def groupby_transform( + iterable: Iterable[_T], + keyfunc: Callable[[_T], _U], + valuefunc: None, + reducefunc: None, +) -> Iterator[tuple[_U, Iterator[_T]]]: ... +@overload +def groupby_transform( + iterable: Iterable[_T], + keyfunc: None, + valuefunc: Callable[[_T], _V], + reducefunc: None, +) -> Iterable[tuple[_T, Iterable[_V]]]: ... +@overload +def groupby_transform( + iterable: Iterable[_T], + keyfunc: Callable[[_T], _U], + valuefunc: Callable[[_T], _V], + reducefunc: None, +) -> Iterable[tuple[_U, Iterator[_V]]]: ... +@overload +def groupby_transform( + iterable: Iterable[_T], + keyfunc: None, + valuefunc: None, + reducefunc: Callable[[Iterator[_T]], _W], +) -> Iterable[tuple[_T, _W]]: ... +@overload +def groupby_transform( + iterable: Iterable[_T], + keyfunc: Callable[[_T], _U], + valuefunc: None, + reducefunc: Callable[[Iterator[_T]], _W], +) -> Iterable[tuple[_U, _W]]: ... +@overload +def groupby_transform( + iterable: Iterable[_T], + keyfunc: None, + valuefunc: Callable[[_T], _V], + reducefunc: Callable[[Iterable[_V]], _W], +) -> Iterable[tuple[_T, _W]]: ... +@overload +def groupby_transform( + iterable: Iterable[_T], + keyfunc: Callable[[_T], _U], + valuefunc: Callable[[_T], _V], + reducefunc: Callable[[Iterable[_V]], _W], +) -> Iterable[tuple[_U, _W]]: ... + +class numeric_range(Generic[_T, _U], Sequence[_T], Hashable, Reversible[_T]): + @overload + def __init__(self, __stop: _T) -> None: ... + @overload + def __init__(self, __start: _T, __stop: _T) -> None: ... + @overload + def __init__(self, __start: _T, __stop: _T, __step: _U) -> None: ... + def __bool__(self) -> bool: ... + def __contains__(self, elem: object) -> bool: ... + def __eq__(self, other: object) -> bool: ... + @overload + def __getitem__(self, key: int) -> _T: ... + @overload + def __getitem__(self, key: slice) -> numeric_range[_T, _U]: ... + def __hash__(self) -> int: ... + def __iter__(self) -> Iterator[_T]: ... + def __len__(self) -> int: ... + def __reduce__( + self, + ) -> tuple[Type[numeric_range[_T, _U]], tuple[_T, _T, _U]]: ... + def __repr__(self) -> str: ... + def __reversed__(self) -> Iterator[_T]: ... + def count(self, value: _T) -> int: ... + def index(self, value: _T) -> int: ... # type: ignore + +def count_cycle( + iterable: Iterable[_T], n: int | None = ... +) -> Iterable[tuple[int, _T]]: ... +def mark_ends( + iterable: Iterable[_T], +) -> Iterable[tuple[bool, bool, _T]]: ... +def locate( + iterable: Iterable[_T], + pred: Callable[..., Any] = ..., + window_size: int | None = ..., +) -> Iterator[int]: ... +def lstrip( + iterable: Iterable[_T], pred: Callable[[_T], object] +) -> Iterator[_T]: ... +def rstrip( + iterable: Iterable[_T], pred: Callable[[_T], object] +) -> Iterator[_T]: ... +def strip( + iterable: Iterable[_T], pred: Callable[[_T], object] +) -> Iterator[_T]: ... + +class islice_extended(Generic[_T], Iterator[_T]): + def __init__(self, iterable: Iterable[_T], *args: int | None) -> None: ... + def __iter__(self) -> islice_extended[_T]: ... + def __next__(self) -> _T: ... + def __getitem__(self, index: slice) -> islice_extended[_T]: ... + +def always_reversible(iterable: Iterable[_T]) -> Iterator[_T]: ... +def consecutive_groups( + iterable: Iterable[_T], ordering: Callable[[_T], int] = ... +) -> Iterator[Iterator[_T]]: ... +@overload +def difference( + iterable: Iterable[_T], + func: Callable[[_T, _T], _U] = ..., + *, + initial: None = ..., +) -> Iterator[_T | _U]: ... +@overload +def difference( + iterable: Iterable[_T], func: Callable[[_T, _T], _U] = ..., *, initial: _U +) -> Iterator[_U]: ... + +class SequenceView(Generic[_T], Sequence[_T]): + def __init__(self, target: Sequence[_T]) -> None: ... + @overload + def __getitem__(self, index: int) -> _T: ... + @overload + def __getitem__(self, index: slice) -> Sequence[_T]: ... + def __len__(self) -> int: ... + +class seekable(Generic[_T], Iterator[_T]): + def __init__( + self, iterable: Iterable[_T], maxlen: int | None = ... + ) -> None: ... + def __iter__(self) -> seekable[_T]: ... + def __next__(self) -> _T: ... + def __bool__(self) -> bool: ... + @overload + def peek(self) -> _T: ... + @overload + def peek(self, default: _U) -> _T | _U: ... + def elements(self) -> SequenceView[_T]: ... + def seek(self, index: int) -> None: ... + def relative_seek(self, count: int) -> None: ... + +class run_length: + @staticmethod + def encode(iterable: Iterable[_T]) -> Iterator[tuple[_T, int]]: ... + @staticmethod + def decode(iterable: Iterable[tuple[_T, int]]) -> Iterator[_T]: ... + +def exactly_n( + iterable: Iterable[_T], n: int, predicate: Callable[[_T], object] = ... +) -> bool: ... +def circular_shifts(iterable: Iterable[_T]) -> list[tuple[_T, ...]]: ... +def make_decorator( + wrapping_func: Callable[..., _U], result_index: int = ... +) -> Callable[..., Callable[[Callable[..., Any]], Callable[..., _U]]]: ... +@overload +def map_reduce( + iterable: Iterable[_T], + keyfunc: Callable[[_T], _U], + valuefunc: None = ..., + reducefunc: None = ..., +) -> dict[_U, list[_T]]: ... +@overload +def map_reduce( + iterable: Iterable[_T], + keyfunc: Callable[[_T], _U], + valuefunc: Callable[[_T], _V], + reducefunc: None = ..., +) -> dict[_U, list[_V]]: ... +@overload +def map_reduce( + iterable: Iterable[_T], + keyfunc: Callable[[_T], _U], + valuefunc: None = ..., + reducefunc: Callable[[list[_T]], _W] = ..., +) -> dict[_U, _W]: ... +@overload +def map_reduce( + iterable: Iterable[_T], + keyfunc: Callable[[_T], _U], + valuefunc: Callable[[_T], _V], + reducefunc: Callable[[list[_V]], _W], +) -> dict[_U, _W]: ... +def rlocate( + iterable: Iterable[_T], + pred: Callable[..., object] = ..., + window_size: int | None = ..., +) -> Iterator[int]: ... +def replace( + iterable: Iterable[_T], + pred: Callable[..., object], + substitutes: Iterable[_U], + count: int | None = ..., + window_size: int = ..., +) -> Iterator[_T | _U]: ... +def partitions(iterable: Iterable[_T]) -> Iterator[list[list[_T]]]: ... +def set_partitions( + iterable: Iterable[_T], k: int | None = ... +) -> Iterator[list[list[_T]]]: ... + +class time_limited(Generic[_T], Iterator[_T]): + def __init__( + self, limit_seconds: float, iterable: Iterable[_T] + ) -> None: ... + def __iter__(self) -> islice_extended[_T]: ... + def __next__(self) -> _T: ... + +@overload +def only( + iterable: Iterable[_T], *, too_long: _Raisable | None = ... +) -> _T | None: ... +@overload +def only( + iterable: Iterable[_T], default: _U, too_long: _Raisable | None = ... +) -> _T | _U: ... +def ichunked(iterable: Iterable[_T], n: int) -> Iterator[Iterator[_T]]: ... +def distinct_combinations( + iterable: Iterable[_T], r: int +) -> Iterator[tuple[_T, ...]]: ... +def filter_except( + validator: Callable[[Any], object], + iterable: Iterable[_T], + *exceptions: Type[BaseException], +) -> Iterator[_T]: ... +def map_except( + function: Callable[[Any], _U], + iterable: Iterable[_T], + *exceptions: Type[BaseException], +) -> Iterator[_U]: ... +def map_if( + iterable: Iterable[Any], + pred: Callable[[Any], bool], + func: Callable[[Any], Any], + func_else: Callable[[Any], Any] | None = ..., +) -> Iterator[Any]: ... +def sample( + iterable: Iterable[_T], + k: int, + weights: Iterable[float] | None = ..., +) -> list[_T]: ... +def is_sorted( + iterable: Iterable[_T], + key: Callable[[_T], _U] | None = ..., + reverse: bool = False, + strict: bool = False, +) -> bool: ... + +class AbortThread(BaseException): + pass + +class callback_iter(Generic[_T], Iterator[_T]): + def __init__( + self, + func: Callable[..., Any], + callback_kwd: str = ..., + wait_seconds: float = ..., + ) -> None: ... + def __enter__(self) -> callback_iter[_T]: ... + def __exit__( + self, + exc_type: Type[BaseException] | None, + exc_value: BaseException | None, + traceback: TracebackType | None, + ) -> bool | None: ... + def __iter__(self) -> callback_iter[_T]: ... + def __next__(self) -> _T: ... + def _reader(self) -> Iterator[_T]: ... + @property + def done(self) -> bool: ... + @property + def result(self) -> Any: ... + +def windowed_complete( + iterable: Iterable[_T], n: int +) -> Iterator[tuple[_T, ...]]: ... +def all_unique( + iterable: Iterable[_T], key: Callable[[_T], _U] | None = ... +) -> bool: ... +def nth_product(index: int, *args: Iterable[_T]) -> tuple[_T, ...]: ... +def nth_combination_with_replacement( + iterable: Iterable[_T], r: int, index: int +) -> tuple[_T, ...]: ... +def nth_permutation( + iterable: Iterable[_T], r: int, index: int +) -> tuple[_T, ...]: ... +def value_chain(*args: _T | Iterable[_T]) -> Iterable[_T]: ... +def product_index(element: Iterable[_T], *args: Iterable[_T]) -> int: ... +def combination_index( + element: Iterable[_T], iterable: Iterable[_T] +) -> int: ... +def combination_with_replacement_index( + element: Iterable[_T], iterable: Iterable[_T] +) -> int: ... +def permutation_index( + element: Iterable[_T], iterable: Iterable[_T] +) -> int: ... +def repeat_each(iterable: Iterable[_T], n: int = ...) -> Iterator[_T]: ... + +class countable(Generic[_T], Iterator[_T]): + def __init__(self, iterable: Iterable[_T]) -> None: ... + def __iter__(self) -> countable[_T]: ... + def __next__(self) -> _T: ... + +def chunked_even(iterable: Iterable[_T], n: int) -> Iterator[list[_T]]: ... +def zip_broadcast( + *objects: _T | Iterable[_T], + scalar_types: type | tuple[type | tuple[Any, ...], ...] | None = ..., + strict: bool = ..., +) -> Iterable[tuple[_T, ...]]: ... +def unique_in_window( + iterable: Iterable[_T], n: int, key: Callable[[_T], _U] | None = ... +) -> Iterator[_T]: ... +def duplicates_everseen( + iterable: Iterable[_T], key: Callable[[_T], _U] | None = ... +) -> Iterator[_T]: ... +def duplicates_justseen( + iterable: Iterable[_T], key: Callable[[_T], _U] | None = ... +) -> Iterator[_T]: ... +def classify_unique( + iterable: Iterable[_T], key: Callable[[_T], _U] | None = ... +) -> Iterator[tuple[_T, bool, bool]]: ... + +class _SupportsLessThan(Protocol): + def __lt__(self, __other: Any) -> bool: ... + +_SupportsLessThanT = TypeVar("_SupportsLessThanT", bound=_SupportsLessThan) + +@overload +def minmax( + iterable_or_value: Iterable[_SupportsLessThanT], *, key: None = None +) -> tuple[_SupportsLessThanT, _SupportsLessThanT]: ... +@overload +def minmax( + iterable_or_value: Iterable[_T], *, key: Callable[[_T], _SupportsLessThan] +) -> tuple[_T, _T]: ... +@overload +def minmax( + iterable_or_value: Iterable[_SupportsLessThanT], + *, + key: None = None, + default: _U, +) -> _U | tuple[_SupportsLessThanT, _SupportsLessThanT]: ... +@overload +def minmax( + iterable_or_value: Iterable[_T], + *, + key: Callable[[_T], _SupportsLessThan], + default: _U, +) -> _U | tuple[_T, _T]: ... +@overload +def minmax( + iterable_or_value: _SupportsLessThanT, + __other: _SupportsLessThanT, + *others: _SupportsLessThanT, +) -> tuple[_SupportsLessThanT, _SupportsLessThanT]: ... +@overload +def minmax( + iterable_or_value: _T, + __other: _T, + *others: _T, + key: Callable[[_T], _SupportsLessThan], +) -> tuple[_T, _T]: ... +def longest_common_prefix( + iterables: Iterable[Iterable[_T]], +) -> Iterator[_T]: ... +def iequals(*iterables: Iterable[Any]) -> bool: ... +def constrained_batches( + iterable: Iterable[_T], + max_size: int, + max_count: int | None = ..., + get_len: Callable[[_T], object] = ..., + strict: bool = ..., +) -> Iterator[tuple[_T]]: ... +def gray_product(*iterables: Iterable[_T]) -> Iterator[tuple[_T, ...]]: ... +def partial_product(*iterables: Iterable[_T]) -> Iterator[tuple[_T, ...]]: ... +def takewhile_inclusive( + predicate: Callable[[_T], bool], iterable: Iterable[_T] +) -> Iterator[_T]: ... +def outer_product( + func: Callable[[_T, _U], _V], + xs: Iterable[_T], + ys: Iterable[_U], + *args: Any, + **kwargs: Any, +) -> Iterator[tuple[_V, ...]]: ... +def iter_suppress( + iterable: Iterable[_T], + *exceptions: Type[BaseException], +) -> Iterator[_T]: ... +def filter_map( + func: Callable[[_T], _V | None], + iterable: Iterable[_T], +) -> Iterator[_V]: ... diff --git a/venv/Lib/site-packages/pkg_resources/_vendor/more_itertools/py.typed b/venv/Lib/site-packages/pkg_resources/_vendor/more_itertools/py.typed new file mode 100644 index 0000000..e69de29 diff --git a/venv/Lib/site-packages/pkg_resources/_vendor/more_itertools/recipes.py b/venv/Lib/site-packages/pkg_resources/_vendor/more_itertools/recipes.py new file mode 100644 index 0000000..145e3cb --- /dev/null +++ b/venv/Lib/site-packages/pkg_resources/_vendor/more_itertools/recipes.py @@ -0,0 +1,1012 @@ +"""Imported from the recipes section of the itertools documentation. + +All functions taken from the recipes section of the itertools library docs +[1]_. +Some backward-compatible usability improvements have been made. + +.. [1] http://docs.python.org/library/itertools.html#recipes + +""" +import math +import operator + +from collections import deque +from collections.abc import Sized +from functools import partial, reduce +from itertools import ( + chain, + combinations, + compress, + count, + cycle, + groupby, + islice, + product, + repeat, + starmap, + tee, + zip_longest, +) +from random import randrange, sample, choice +from sys import hexversion + +__all__ = [ + 'all_equal', + 'batched', + 'before_and_after', + 'consume', + 'convolve', + 'dotproduct', + 'first_true', + 'factor', + 'flatten', + 'grouper', + 'iter_except', + 'iter_index', + 'matmul', + 'ncycles', + 'nth', + 'nth_combination', + 'padnone', + 'pad_none', + 'pairwise', + 'partition', + 'polynomial_eval', + 'polynomial_from_roots', + 'polynomial_derivative', + 'powerset', + 'prepend', + 'quantify', + 'reshape', + 'random_combination_with_replacement', + 'random_combination', + 'random_permutation', + 'random_product', + 'repeatfunc', + 'roundrobin', + 'sieve', + 'sliding_window', + 'subslices', + 'sum_of_squares', + 'tabulate', + 'tail', + 'take', + 'totient', + 'transpose', + 'triplewise', + 'unique_everseen', + 'unique_justseen', +] + +_marker = object() + + +# zip with strict is available for Python 3.10+ +try: + zip(strict=True) +except TypeError: + _zip_strict = zip +else: + _zip_strict = partial(zip, strict=True) + +# math.sumprod is available for Python 3.12+ +_sumprod = getattr(math, 'sumprod', lambda x, y: dotproduct(x, y)) + + +def take(n, iterable): + """Return first *n* items of the iterable as a list. + + >>> take(3, range(10)) + [0, 1, 2] + + If there are fewer than *n* items in the iterable, all of them are + returned. + + >>> take(10, range(3)) + [0, 1, 2] + + """ + return list(islice(iterable, n)) + + +def tabulate(function, start=0): + """Return an iterator over the results of ``func(start)``, + ``func(start + 1)``, ``func(start + 2)``... + + *func* should be a function that accepts one integer argument. + + If *start* is not specified it defaults to 0. It will be incremented each + time the iterator is advanced. + + >>> square = lambda x: x ** 2 + >>> iterator = tabulate(square, -3) + >>> take(4, iterator) + [9, 4, 1, 0] + + """ + return map(function, count(start)) + + +def tail(n, iterable): + """Return an iterator over the last *n* items of *iterable*. + + >>> t = tail(3, 'ABCDEFG') + >>> list(t) + ['E', 'F', 'G'] + + """ + # If the given iterable has a length, then we can use islice to get its + # final elements. Note that if the iterable is not actually Iterable, + # either islice or deque will throw a TypeError. This is why we don't + # check if it is Iterable. + if isinstance(iterable, Sized): + yield from islice(iterable, max(0, len(iterable) - n), None) + else: + yield from iter(deque(iterable, maxlen=n)) + + +def consume(iterator, n=None): + """Advance *iterable* by *n* steps. If *n* is ``None``, consume it + entirely. + + Efficiently exhausts an iterator without returning values. Defaults to + consuming the whole iterator, but an optional second argument may be + provided to limit consumption. + + >>> i = (x for x in range(10)) + >>> next(i) + 0 + >>> consume(i, 3) + >>> next(i) + 4 + >>> consume(i) + >>> next(i) + Traceback (most recent call last): + File "", line 1, in + StopIteration + + If the iterator has fewer items remaining than the provided limit, the + whole iterator will be consumed. + + >>> i = (x for x in range(3)) + >>> consume(i, 5) + >>> next(i) + Traceback (most recent call last): + File "", line 1, in + StopIteration + + """ + # Use functions that consume iterators at C speed. + if n is None: + # feed the entire iterator into a zero-length deque + deque(iterator, maxlen=0) + else: + # advance to the empty slice starting at position n + next(islice(iterator, n, n), None) + + +def nth(iterable, n, default=None): + """Returns the nth item or a default value. + + >>> l = range(10) + >>> nth(l, 3) + 3 + >>> nth(l, 20, "zebra") + 'zebra' + + """ + return next(islice(iterable, n, None), default) + + +def all_equal(iterable): + """ + Returns ``True`` if all the elements are equal to each other. + + >>> all_equal('aaaa') + True + >>> all_equal('aaab') + False + + """ + g = groupby(iterable) + return next(g, True) and not next(g, False) + + +def quantify(iterable, pred=bool): + """Return the how many times the predicate is true. + + >>> quantify([True, False, True]) + 2 + + """ + return sum(map(pred, iterable)) + + +def pad_none(iterable): + """Returns the sequence of elements and then returns ``None`` indefinitely. + + >>> take(5, pad_none(range(3))) + [0, 1, 2, None, None] + + Useful for emulating the behavior of the built-in :func:`map` function. + + See also :func:`padded`. + + """ + return chain(iterable, repeat(None)) + + +padnone = pad_none + + +def ncycles(iterable, n): + """Returns the sequence elements *n* times + + >>> list(ncycles(["a", "b"], 3)) + ['a', 'b', 'a', 'b', 'a', 'b'] + + """ + return chain.from_iterable(repeat(tuple(iterable), n)) + + +def dotproduct(vec1, vec2): + """Returns the dot product of the two iterables. + + >>> dotproduct([10, 10], [20, 20]) + 400 + + """ + return sum(map(operator.mul, vec1, vec2)) + + +def flatten(listOfLists): + """Return an iterator flattening one level of nesting in a list of lists. + + >>> list(flatten([[0, 1], [2, 3]])) + [0, 1, 2, 3] + + See also :func:`collapse`, which can flatten multiple levels of nesting. + + """ + return chain.from_iterable(listOfLists) + + +def repeatfunc(func, times=None, *args): + """Call *func* with *args* repeatedly, returning an iterable over the + results. + + If *times* is specified, the iterable will terminate after that many + repetitions: + + >>> from operator import add + >>> times = 4 + >>> args = 3, 5 + >>> list(repeatfunc(add, times, *args)) + [8, 8, 8, 8] + + If *times* is ``None`` the iterable will not terminate: + + >>> from random import randrange + >>> times = None + >>> args = 1, 11 + >>> take(6, repeatfunc(randrange, times, *args)) # doctest:+SKIP + [2, 4, 8, 1, 8, 4] + + """ + if times is None: + return starmap(func, repeat(args)) + return starmap(func, repeat(args, times)) + + +def _pairwise(iterable): + """Returns an iterator of paired items, overlapping, from the original + + >>> take(4, pairwise(count())) + [(0, 1), (1, 2), (2, 3), (3, 4)] + + On Python 3.10 and above, this is an alias for :func:`itertools.pairwise`. + + """ + a, b = tee(iterable) + next(b, None) + return zip(a, b) + + +try: + from itertools import pairwise as itertools_pairwise +except ImportError: + pairwise = _pairwise +else: + + def pairwise(iterable): + return itertools_pairwise(iterable) + + pairwise.__doc__ = _pairwise.__doc__ + + +class UnequalIterablesError(ValueError): + def __init__(self, details=None): + msg = 'Iterables have different lengths' + if details is not None: + msg += (': index 0 has length {}; index {} has length {}').format( + *details + ) + + super().__init__(msg) + + +def _zip_equal_generator(iterables): + for combo in zip_longest(*iterables, fillvalue=_marker): + for val in combo: + if val is _marker: + raise UnequalIterablesError() + yield combo + + +def _zip_equal(*iterables): + # Check whether the iterables are all the same size. + try: + first_size = len(iterables[0]) + for i, it in enumerate(iterables[1:], 1): + size = len(it) + if size != first_size: + raise UnequalIterablesError(details=(first_size, i, size)) + # All sizes are equal, we can use the built-in zip. + return zip(*iterables) + # If any one of the iterables didn't have a length, start reading + # them until one runs out. + except TypeError: + return _zip_equal_generator(iterables) + + +def grouper(iterable, n, incomplete='fill', fillvalue=None): + """Group elements from *iterable* into fixed-length groups of length *n*. + + >>> list(grouper('ABCDEF', 3)) + [('A', 'B', 'C'), ('D', 'E', 'F')] + + The keyword arguments *incomplete* and *fillvalue* control what happens for + iterables whose length is not a multiple of *n*. + + When *incomplete* is `'fill'`, the last group will contain instances of + *fillvalue*. + + >>> list(grouper('ABCDEFG', 3, incomplete='fill', fillvalue='x')) + [('A', 'B', 'C'), ('D', 'E', 'F'), ('G', 'x', 'x')] + + When *incomplete* is `'ignore'`, the last group will not be emitted. + + >>> list(grouper('ABCDEFG', 3, incomplete='ignore', fillvalue='x')) + [('A', 'B', 'C'), ('D', 'E', 'F')] + + When *incomplete* is `'strict'`, a subclass of `ValueError` will be raised. + + >>> it = grouper('ABCDEFG', 3, incomplete='strict') + >>> list(it) # doctest: +IGNORE_EXCEPTION_DETAIL + Traceback (most recent call last): + ... + UnequalIterablesError + + """ + args = [iter(iterable)] * n + if incomplete == 'fill': + return zip_longest(*args, fillvalue=fillvalue) + if incomplete == 'strict': + return _zip_equal(*args) + if incomplete == 'ignore': + return zip(*args) + else: + raise ValueError('Expected fill, strict, or ignore') + + +def roundrobin(*iterables): + """Yields an item from each iterable, alternating between them. + + >>> list(roundrobin('ABC', 'D', 'EF')) + ['A', 'D', 'E', 'B', 'F', 'C'] + + This function produces the same output as :func:`interleave_longest`, but + may perform better for some inputs (in particular when the number of + iterables is small). + + """ + # Recipe credited to George Sakkis + pending = len(iterables) + nexts = cycle(iter(it).__next__ for it in iterables) + while pending: + try: + for next in nexts: + yield next() + except StopIteration: + pending -= 1 + nexts = cycle(islice(nexts, pending)) + + +def partition(pred, iterable): + """ + Returns a 2-tuple of iterables derived from the input iterable. + The first yields the items that have ``pred(item) == False``. + The second yields the items that have ``pred(item) == True``. + + >>> is_odd = lambda x: x % 2 != 0 + >>> iterable = range(10) + >>> even_items, odd_items = partition(is_odd, iterable) + >>> list(even_items), list(odd_items) + ([0, 2, 4, 6, 8], [1, 3, 5, 7, 9]) + + If *pred* is None, :func:`bool` is used. + + >>> iterable = [0, 1, False, True, '', ' '] + >>> false_items, true_items = partition(None, iterable) + >>> list(false_items), list(true_items) + ([0, False, ''], [1, True, ' ']) + + """ + if pred is None: + pred = bool + + t1, t2, p = tee(iterable, 3) + p1, p2 = tee(map(pred, p)) + return (compress(t1, map(operator.not_, p1)), compress(t2, p2)) + + +def powerset(iterable): + """Yields all possible subsets of the iterable. + + >>> list(powerset([1, 2, 3])) + [(), (1,), (2,), (3,), (1, 2), (1, 3), (2, 3), (1, 2, 3)] + + :func:`powerset` will operate on iterables that aren't :class:`set` + instances, so repeated elements in the input will produce repeated elements + in the output. Use :func:`unique_everseen` on the input to avoid generating + duplicates: + + >>> seq = [1, 1, 0] + >>> list(powerset(seq)) + [(), (1,), (1,), (0,), (1, 1), (1, 0), (1, 0), (1, 1, 0)] + >>> from more_itertools import unique_everseen + >>> list(powerset(unique_everseen(seq))) + [(), (1,), (0,), (1, 0)] + + """ + s = list(iterable) + return chain.from_iterable(combinations(s, r) for r in range(len(s) + 1)) + + +def unique_everseen(iterable, key=None): + """ + Yield unique elements, preserving order. + + >>> list(unique_everseen('AAAABBBCCDAABBB')) + ['A', 'B', 'C', 'D'] + >>> list(unique_everseen('ABBCcAD', str.lower)) + ['A', 'B', 'C', 'D'] + + Sequences with a mix of hashable and unhashable items can be used. + The function will be slower (i.e., `O(n^2)`) for unhashable items. + + Remember that ``list`` objects are unhashable - you can use the *key* + parameter to transform the list to a tuple (which is hashable) to + avoid a slowdown. + + >>> iterable = ([1, 2], [2, 3], [1, 2]) + >>> list(unique_everseen(iterable)) # Slow + [[1, 2], [2, 3]] + >>> list(unique_everseen(iterable, key=tuple)) # Faster + [[1, 2], [2, 3]] + + Similarly, you may want to convert unhashable ``set`` objects with + ``key=frozenset``. For ``dict`` objects, + ``key=lambda x: frozenset(x.items())`` can be used. + + """ + seenset = set() + seenset_add = seenset.add + seenlist = [] + seenlist_add = seenlist.append + use_key = key is not None + + for element in iterable: + k = key(element) if use_key else element + try: + if k not in seenset: + seenset_add(k) + yield element + except TypeError: + if k not in seenlist: + seenlist_add(k) + yield element + + +def unique_justseen(iterable, key=None): + """Yields elements in order, ignoring serial duplicates + + >>> list(unique_justseen('AAAABBBCCDAABBB')) + ['A', 'B', 'C', 'D', 'A', 'B'] + >>> list(unique_justseen('ABBCcAD', str.lower)) + ['A', 'B', 'C', 'A', 'D'] + + """ + if key is None: + return map(operator.itemgetter(0), groupby(iterable)) + + return map(next, map(operator.itemgetter(1), groupby(iterable, key))) + + +def iter_except(func, exception, first=None): + """Yields results from a function repeatedly until an exception is raised. + + Converts a call-until-exception interface to an iterator interface. + Like ``iter(func, sentinel)``, but uses an exception instead of a sentinel + to end the loop. + + >>> l = [0, 1, 2] + >>> list(iter_except(l.pop, IndexError)) + [2, 1, 0] + + Multiple exceptions can be specified as a stopping condition: + + >>> l = [1, 2, 3, '...', 4, 5, 6] + >>> list(iter_except(lambda: 1 + l.pop(), (IndexError, TypeError))) + [7, 6, 5] + >>> list(iter_except(lambda: 1 + l.pop(), (IndexError, TypeError))) + [4, 3, 2] + >>> list(iter_except(lambda: 1 + l.pop(), (IndexError, TypeError))) + [] + + """ + try: + if first is not None: + yield first() + while 1: + yield func() + except exception: + pass + + +def first_true(iterable, default=None, pred=None): + """ + Returns the first true value in the iterable. + + If no true value is found, returns *default* + + If *pred* is not None, returns the first item for which + ``pred(item) == True`` . + + >>> first_true(range(10)) + 1 + >>> first_true(range(10), pred=lambda x: x > 5) + 6 + >>> first_true(range(10), default='missing', pred=lambda x: x > 9) + 'missing' + + """ + return next(filter(pred, iterable), default) + + +def random_product(*args, repeat=1): + """Draw an item at random from each of the input iterables. + + >>> random_product('abc', range(4), 'XYZ') # doctest:+SKIP + ('c', 3, 'Z') + + If *repeat* is provided as a keyword argument, that many items will be + drawn from each iterable. + + >>> random_product('abcd', range(4), repeat=2) # doctest:+SKIP + ('a', 2, 'd', 3) + + This equivalent to taking a random selection from + ``itertools.product(*args, **kwarg)``. + + """ + pools = [tuple(pool) for pool in args] * repeat + return tuple(choice(pool) for pool in pools) + + +def random_permutation(iterable, r=None): + """Return a random *r* length permutation of the elements in *iterable*. + + If *r* is not specified or is ``None``, then *r* defaults to the length of + *iterable*. + + >>> random_permutation(range(5)) # doctest:+SKIP + (3, 4, 0, 1, 2) + + This equivalent to taking a random selection from + ``itertools.permutations(iterable, r)``. + + """ + pool = tuple(iterable) + r = len(pool) if r is None else r + return tuple(sample(pool, r)) + + +def random_combination(iterable, r): + """Return a random *r* length subsequence of the elements in *iterable*. + + >>> random_combination(range(5), 3) # doctest:+SKIP + (2, 3, 4) + + This equivalent to taking a random selection from + ``itertools.combinations(iterable, r)``. + + """ + pool = tuple(iterable) + n = len(pool) + indices = sorted(sample(range(n), r)) + return tuple(pool[i] for i in indices) + + +def random_combination_with_replacement(iterable, r): + """Return a random *r* length subsequence of elements in *iterable*, + allowing individual elements to be repeated. + + >>> random_combination_with_replacement(range(3), 5) # doctest:+SKIP + (0, 0, 1, 2, 2) + + This equivalent to taking a random selection from + ``itertools.combinations_with_replacement(iterable, r)``. + + """ + pool = tuple(iterable) + n = len(pool) + indices = sorted(randrange(n) for i in range(r)) + return tuple(pool[i] for i in indices) + + +def nth_combination(iterable, r, index): + """Equivalent to ``list(combinations(iterable, r))[index]``. + + The subsequences of *iterable* that are of length *r* can be ordered + lexicographically. :func:`nth_combination` computes the subsequence at + sort position *index* directly, without computing the previous + subsequences. + + >>> nth_combination(range(5), 3, 5) + (0, 3, 4) + + ``ValueError`` will be raised If *r* is negative or greater than the length + of *iterable*. + ``IndexError`` will be raised if the given *index* is invalid. + """ + pool = tuple(iterable) + n = len(pool) + if (r < 0) or (r > n): + raise ValueError + + c = 1 + k = min(r, n - r) + for i in range(1, k + 1): + c = c * (n - k + i) // i + + if index < 0: + index += c + + if (index < 0) or (index >= c): + raise IndexError + + result = [] + while r: + c, n, r = c * r // n, n - 1, r - 1 + while index >= c: + index -= c + c, n = c * (n - r) // n, n - 1 + result.append(pool[-1 - n]) + + return tuple(result) + + +def prepend(value, iterator): + """Yield *value*, followed by the elements in *iterator*. + + >>> value = '0' + >>> iterator = ['1', '2', '3'] + >>> list(prepend(value, iterator)) + ['0', '1', '2', '3'] + + To prepend multiple values, see :func:`itertools.chain` + or :func:`value_chain`. + + """ + return chain([value], iterator) + + +def convolve(signal, kernel): + """Convolve the iterable *signal* with the iterable *kernel*. + + >>> signal = (1, 2, 3, 4, 5) + >>> kernel = [3, 2, 1] + >>> list(convolve(signal, kernel)) + [3, 8, 14, 20, 26, 14, 5] + + Note: the input arguments are not interchangeable, as the *kernel* + is immediately consumed and stored. + + """ + # This implementation intentionally doesn't match the one in the itertools + # documentation. + kernel = tuple(kernel)[::-1] + n = len(kernel) + window = deque([0], maxlen=n) * n + for x in chain(signal, repeat(0, n - 1)): + window.append(x) + yield _sumprod(kernel, window) + + +def before_and_after(predicate, it): + """A variant of :func:`takewhile` that allows complete access to the + remainder of the iterator. + + >>> it = iter('ABCdEfGhI') + >>> all_upper, remainder = before_and_after(str.isupper, it) + >>> ''.join(all_upper) + 'ABC' + >>> ''.join(remainder) # takewhile() would lose the 'd' + 'dEfGhI' + + Note that the first iterator must be fully consumed before the second + iterator can generate valid results. + """ + it = iter(it) + transition = [] + + def true_iterator(): + for elem in it: + if predicate(elem): + yield elem + else: + transition.append(elem) + return + + # Note: this is different from itertools recipes to allow nesting + # before_and_after remainders into before_and_after again. See tests + # for an example. + remainder_iterator = chain(transition, it) + + return true_iterator(), remainder_iterator + + +def triplewise(iterable): + """Return overlapping triplets from *iterable*. + + >>> list(triplewise('ABCDE')) + [('A', 'B', 'C'), ('B', 'C', 'D'), ('C', 'D', 'E')] + + """ + for (a, _), (b, c) in pairwise(pairwise(iterable)): + yield a, b, c + + +def sliding_window(iterable, n): + """Return a sliding window of width *n* over *iterable*. + + >>> list(sliding_window(range(6), 4)) + [(0, 1, 2, 3), (1, 2, 3, 4), (2, 3, 4, 5)] + + If *iterable* has fewer than *n* items, then nothing is yielded: + + >>> list(sliding_window(range(3), 4)) + [] + + For a variant with more features, see :func:`windowed`. + """ + it = iter(iterable) + window = deque(islice(it, n - 1), maxlen=n) + for x in it: + window.append(x) + yield tuple(window) + + +def subslices(iterable): + """Return all contiguous non-empty subslices of *iterable*. + + >>> list(subslices('ABC')) + [['A'], ['A', 'B'], ['A', 'B', 'C'], ['B'], ['B', 'C'], ['C']] + + This is similar to :func:`substrings`, but emits items in a different + order. + """ + seq = list(iterable) + slices = starmap(slice, combinations(range(len(seq) + 1), 2)) + return map(operator.getitem, repeat(seq), slices) + + +def polynomial_from_roots(roots): + """Compute a polynomial's coefficients from its roots. + + >>> roots = [5, -4, 3] # (x - 5) * (x + 4) * (x - 3) + >>> polynomial_from_roots(roots) # x^3 - 4 * x^2 - 17 * x + 60 + [1, -4, -17, 60] + """ + factors = zip(repeat(1), map(operator.neg, roots)) + return list(reduce(convolve, factors, [1])) + + +def iter_index(iterable, value, start=0, stop=None): + """Yield the index of each place in *iterable* that *value* occurs, + beginning with index *start* and ending before index *stop*. + + See :func:`locate` for a more general means of finding the indexes + associated with particular values. + + >>> list(iter_index('AABCADEAF', 'A')) + [0, 1, 4, 7] + >>> list(iter_index('AABCADEAF', 'A', 1)) # start index is inclusive + [1, 4, 7] + >>> list(iter_index('AABCADEAF', 'A', 1, 7)) # stop index is not inclusive + [1, 4] + """ + seq_index = getattr(iterable, 'index', None) + if seq_index is None: + # Slow path for general iterables + it = islice(iterable, start, stop) + for i, element in enumerate(it, start): + if element is value or element == value: + yield i + else: + # Fast path for sequences + stop = len(iterable) if stop is None else stop + i = start - 1 + try: + while True: + yield (i := seq_index(value, i + 1, stop)) + except ValueError: + pass + + +def sieve(n): + """Yield the primes less than n. + + >>> list(sieve(30)) + [2, 3, 5, 7, 11, 13, 17, 19, 23, 29] + """ + if n > 2: + yield 2 + start = 3 + data = bytearray((0, 1)) * (n // 2) + limit = math.isqrt(n) + 1 + for p in iter_index(data, 1, start, limit): + yield from iter_index(data, 1, start, p * p) + data[p * p : n : p + p] = bytes(len(range(p * p, n, p + p))) + start = p * p + yield from iter_index(data, 1, start) + + +def _batched(iterable, n, *, strict=False): + """Batch data into tuples of length *n*. If the number of items in + *iterable* is not divisible by *n*: + * The last batch will be shorter if *strict* is ``False``. + * :exc:`ValueError` will be raised if *strict* is ``True``. + + >>> list(batched('ABCDEFG', 3)) + [('A', 'B', 'C'), ('D', 'E', 'F'), ('G',)] + + On Python 3.13 and above, this is an alias for :func:`itertools.batched`. + """ + if n < 1: + raise ValueError('n must be at least one') + it = iter(iterable) + while batch := tuple(islice(it, n)): + if strict and len(batch) != n: + raise ValueError('batched(): incomplete batch') + yield batch + + +if hexversion >= 0x30D00A2: + from itertools import batched as itertools_batched + + def batched(iterable, n, *, strict=False): + return itertools_batched(iterable, n, strict=strict) + +else: + batched = _batched + + batched.__doc__ = _batched.__doc__ + + +def transpose(it): + """Swap the rows and columns of the input matrix. + + >>> list(transpose([(1, 2, 3), (11, 22, 33)])) + [(1, 11), (2, 22), (3, 33)] + + The caller should ensure that the dimensions of the input are compatible. + If the input is empty, no output will be produced. + """ + return _zip_strict(*it) + + +def reshape(matrix, cols): + """Reshape the 2-D input *matrix* to have a column count given by *cols*. + + >>> matrix = [(0, 1), (2, 3), (4, 5)] + >>> cols = 3 + >>> list(reshape(matrix, cols)) + [(0, 1, 2), (3, 4, 5)] + """ + return batched(chain.from_iterable(matrix), cols) + + +def matmul(m1, m2): + """Multiply two matrices. + + >>> list(matmul([(7, 5), (3, 5)], [(2, 5), (7, 9)])) + [(49, 80), (41, 60)] + + The caller should ensure that the dimensions of the input matrices are + compatible with each other. + """ + n = len(m2[0]) + return batched(starmap(_sumprod, product(m1, transpose(m2))), n) + + +def factor(n): + """Yield the prime factors of n. + + >>> list(factor(360)) + [2, 2, 2, 3, 3, 5] + """ + for prime in sieve(math.isqrt(n) + 1): + while not n % prime: + yield prime + n //= prime + if n == 1: + return + if n > 1: + yield n + + +def polynomial_eval(coefficients, x): + """Evaluate a polynomial at a specific value. + + Example: evaluating x^3 - 4 * x^2 - 17 * x + 60 at x = 2.5: + + >>> coefficients = [1, -4, -17, 60] + >>> x = 2.5 + >>> polynomial_eval(coefficients, x) + 8.125 + """ + n = len(coefficients) + if n == 0: + return x * 0 # coerce zero to the type of x + powers = map(pow, repeat(x), reversed(range(n))) + return _sumprod(coefficients, powers) + + +def sum_of_squares(it): + """Return the sum of the squares of the input values. + + >>> sum_of_squares([10, 20, 30]) + 1400 + """ + return _sumprod(*tee(it)) + + +def polynomial_derivative(coefficients): + """Compute the first derivative of a polynomial. + + Example: evaluating the derivative of x^3 - 4 * x^2 - 17 * x + 60 + + >>> coefficients = [1, -4, -17, 60] + >>> derivative_coefficients = polynomial_derivative(coefficients) + >>> derivative_coefficients + [3, -8, -17] + """ + n = len(coefficients) + powers = reversed(range(1, n)) + return list(map(operator.mul, coefficients, powers)) + + +def totient(n): + """Return the count of natural numbers up to *n* that are coprime with *n*. + + >>> totient(9) + 6 + >>> totient(12) + 4 + """ + for p in unique_justseen(factor(n)): + n = n // p * (p - 1) + + return n diff --git a/venv/Lib/site-packages/pkg_resources/_vendor/more_itertools/recipes.pyi b/venv/Lib/site-packages/pkg_resources/_vendor/more_itertools/recipes.pyi new file mode 100644 index 0000000..ed4c19d --- /dev/null +++ b/venv/Lib/site-packages/pkg_resources/_vendor/more_itertools/recipes.pyi @@ -0,0 +1,128 @@ +"""Stubs for more_itertools.recipes""" +from __future__ import annotations + +from typing import ( + Any, + Callable, + Iterable, + Iterator, + overload, + Sequence, + Type, + TypeVar, +) + +# Type and type variable definitions +_T = TypeVar('_T') +_T1 = TypeVar('_T1') +_T2 = TypeVar('_T2') +_U = TypeVar('_U') + +def take(n: int, iterable: Iterable[_T]) -> list[_T]: ... +def tabulate( + function: Callable[[int], _T], start: int = ... +) -> Iterator[_T]: ... +def tail(n: int, iterable: Iterable[_T]) -> Iterator[_T]: ... +def consume(iterator: Iterable[_T], n: int | None = ...) -> None: ... +@overload +def nth(iterable: Iterable[_T], n: int) -> _T | None: ... +@overload +def nth(iterable: Iterable[_T], n: int, default: _U) -> _T | _U: ... +def all_equal(iterable: Iterable[_T]) -> bool: ... +def quantify( + iterable: Iterable[_T], pred: Callable[[_T], bool] = ... +) -> int: ... +def pad_none(iterable: Iterable[_T]) -> Iterator[_T | None]: ... +def padnone(iterable: Iterable[_T]) -> Iterator[_T | None]: ... +def ncycles(iterable: Iterable[_T], n: int) -> Iterator[_T]: ... +def dotproduct(vec1: Iterable[_T1], vec2: Iterable[_T2]) -> Any: ... +def flatten(listOfLists: Iterable[Iterable[_T]]) -> Iterator[_T]: ... +def repeatfunc( + func: Callable[..., _U], times: int | None = ..., *args: Any +) -> Iterator[_U]: ... +def pairwise(iterable: Iterable[_T]) -> Iterator[tuple[_T, _T]]: ... +def grouper( + iterable: Iterable[_T], + n: int, + incomplete: str = ..., + fillvalue: _U = ..., +) -> Iterator[tuple[_T | _U, ...]]: ... +def roundrobin(*iterables: Iterable[_T]) -> Iterator[_T]: ... +def partition( + pred: Callable[[_T], object] | None, iterable: Iterable[_T] +) -> tuple[Iterator[_T], Iterator[_T]]: ... +def powerset(iterable: Iterable[_T]) -> Iterator[tuple[_T, ...]]: ... +def unique_everseen( + iterable: Iterable[_T], key: Callable[[_T], _U] | None = ... +) -> Iterator[_T]: ... +def unique_justseen( + iterable: Iterable[_T], key: Callable[[_T], object] | None = ... +) -> Iterator[_T]: ... +@overload +def iter_except( + func: Callable[[], _T], + exception: Type[BaseException] | tuple[Type[BaseException], ...], + first: None = ..., +) -> Iterator[_T]: ... +@overload +def iter_except( + func: Callable[[], _T], + exception: Type[BaseException] | tuple[Type[BaseException], ...], + first: Callable[[], _U], +) -> Iterator[_T | _U]: ... +@overload +def first_true( + iterable: Iterable[_T], *, pred: Callable[[_T], object] | None = ... +) -> _T | None: ... +@overload +def first_true( + iterable: Iterable[_T], + default: _U, + pred: Callable[[_T], object] | None = ..., +) -> _T | _U: ... +def random_product( + *args: Iterable[_T], repeat: int = ... +) -> tuple[_T, ...]: ... +def random_permutation( + iterable: Iterable[_T], r: int | None = ... +) -> tuple[_T, ...]: ... +def random_combination(iterable: Iterable[_T], r: int) -> tuple[_T, ...]: ... +def random_combination_with_replacement( + iterable: Iterable[_T], r: int +) -> tuple[_T, ...]: ... +def nth_combination( + iterable: Iterable[_T], r: int, index: int +) -> tuple[_T, ...]: ... +def prepend(value: _T, iterator: Iterable[_U]) -> Iterator[_T | _U]: ... +def convolve(signal: Iterable[_T], kernel: Iterable[_T]) -> Iterator[_T]: ... +def before_and_after( + predicate: Callable[[_T], bool], it: Iterable[_T] +) -> tuple[Iterator[_T], Iterator[_T]]: ... +def triplewise(iterable: Iterable[_T]) -> Iterator[tuple[_T, _T, _T]]: ... +def sliding_window( + iterable: Iterable[_T], n: int +) -> Iterator[tuple[_T, ...]]: ... +def subslices(iterable: Iterable[_T]) -> Iterator[list[_T]]: ... +def polynomial_from_roots(roots: Sequence[_T]) -> list[_T]: ... +def iter_index( + iterable: Iterable[_T], + value: Any, + start: int | None = ..., + stop: int | None = ..., +) -> Iterator[int]: ... +def sieve(n: int) -> Iterator[int]: ... +def batched( + iterable: Iterable[_T], n: int, *, strict: bool = False +) -> Iterator[tuple[_T]]: ... +def transpose( + it: Iterable[Iterable[_T]], +) -> Iterator[tuple[_T, ...]]: ... +def reshape( + matrix: Iterable[Iterable[_T]], cols: int +) -> Iterator[tuple[_T, ...]]: ... +def matmul(m1: Sequence[_T], m2: Sequence[_T]) -> Iterator[tuple[_T]]: ... +def factor(n: int) -> Iterator[int]: ... +def polynomial_eval(coefficients: Sequence[_T], x: _U) -> _U: ... +def sum_of_squares(it: Iterable[_T]) -> _T: ... +def polynomial_derivative(coefficients: Sequence[_T]) -> list[_T]: ... +def totient(n: int) -> int: ... diff --git a/venv/Lib/site-packages/pkg_resources/_vendor/packaging/__init__.py b/venv/Lib/site-packages/pkg_resources/_vendor/packaging/__init__.py new file mode 100644 index 0000000..e7c0aa1 --- /dev/null +++ b/venv/Lib/site-packages/pkg_resources/_vendor/packaging/__init__.py @@ -0,0 +1,15 @@ +# 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. + +__title__ = "packaging" +__summary__ = "Core utilities for Python packages" +__uri__ = "https://github.com/pypa/packaging" + +__version__ = "24.0" + +__author__ = "Donald Stufft and individual contributors" +__email__ = "donald@stufft.io" + +__license__ = "BSD-2-Clause or Apache-2.0" +__copyright__ = "2014 %s" % __author__ diff --git a/venv/Lib/site-packages/pkg_resources/_vendor/packaging/__pycache__/__init__.cpython-312.pyc b/venv/Lib/site-packages/pkg_resources/_vendor/packaging/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..a00025f9ebce286bc73536fd8ff378ea3c8c1491 GIT binary patch literal 582 zcmYLG&x_MQ6rQx*wn;ao2M?`Y3s7@@Oa7-c<>5-E{LYdsl zxELoqF9a->3ge&Xc5m$6&G<6}~hlT=X9Q`1rf`@|!D8P2)fH CP{1Jo literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/pkg_resources/_vendor/packaging/__pycache__/_elffile.cpython-312.pyc b/venv/Lib/site-packages/pkg_resources/_vendor/packaging/__pycache__/_elffile.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..d090ca7e896b379f7461b360f67c84819d4165fd GIT binary patch literal 5050 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 F{ts_gIllk^ literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/pkg_resources/_vendor/packaging/__pycache__/_manylinux.cpython-312.pyc b/venv/Lib/site-packages/pkg_resources/_vendor/packaging/__pycache__/_manylinux.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..54d42c18faec1a6e18e5988366cb794429072d99 GIT binary patch literal 9930 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 g70qq=pza(3Bl$9t~lATy}m!UNyn>Iyi?@)Hc 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 literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/pkg_resources/_vendor/packaging/__pycache__/_structures.cpython-312.pyc b/venv/Lib/site-packages/pkg_resources/_vendor/packaging/__pycache__/_structures.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..ca55d01af6cdc2000e437dcbb7c863e2aaf9cb1f GIT binary patch literal 3265 zcmc&#&2Jk;6rWwM9VfBlKpf&UY3nLf)PVB=2n4EnX;nUitV%5x%hJm6jvcp-ozAR- zY;wpU2P8m30x3C$6N>Vua0$uWTN8u`RFT4Ivjs+PCxGyqWjref;K^ zczlA<5PxLL`(eiZ1k)QMI^-7*(oE>-7@`lndNh8$PFiFGg zYlK=>CS2<3t6$&79)r16-{-ZT;6JVQJb{4gl)HHdk*4W%RTWJxhN_1Y?u9(nD5Yp`RzrN%ZjL<;cuE6u9A$DaPq1l0+o7q|@4qh;7$g z@?mBuI1t83uhR=nQdzy&oOlg3U{A~uIcJEEGDN~@?3f3^&3racVd_fDG#L#4jgmLFlO6zUZJoj2klUMD(NB}DA75H zd%yN`nFQLWUfd+#y$RdiY&gvt<}urT-YnL-DsjlK zMopwhBo;u@Ag_bG0rD1zs$>-rG^4;>6^YP9en(`FotZ}X+QCwX(eEr`gwuMS#(ehR z;Tg$i4_z$J28hW0uN!G@>q5ezAP2u-3LF9r0j}SL zjnU88e=>y92sT7Pe&1(E!L_fE{vDVFAnaV(0`%I80cE7{_(Rf=fJNZ(+}8;e(p+X* zyp8iz@{wJ!0s`a}D*8ZPu?F%ENFeGegdGLF3NS#H=#$?P(a^h~;dTKKIS9R%L(mb> zdpXQbY3NNIE&_Q|x`p9hE@SCuLd92kty<6jlDv%G)i2gPahEnBT@tZ73mJy-A~t8F h|6(BTu~_Pxd%MvWkvSs;QHn&#JEt?5U)i)W`VS9tkgEUy literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/pkg_resources/_vendor/packaging/__pycache__/_tokenizer.cpython-312.pyc b/venv/Lib/site-packages/pkg_resources/_vendor/packaging/__pycache__/_tokenizer.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..23df49b0e5fbca27b0129efc59c6b92e5fdfa63a GIT binary patch literal 7961 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 literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/pkg_resources/_vendor/packaging/__pycache__/metadata.cpython-312.pyc b/venv/Lib/site-packages/pkg_resources/_vendor/packaging/__pycache__/metadata.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..f1f0742f036bb01b929a80f863c68cfe73771d2b GIT binary patch literal 26969 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

GKMx>6yEInww?IpDR+Baaf@Rny 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 z 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 literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/pkg_resources/_vendor/packaging/__pycache__/tags.cpython-312.pyc b/venv/Lib/site-packages/pkg_resources/_vendor/packaging/__pycache__/tags.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..e3723125aaaa5da231e2f390676c406771b101fa GIT binary patch literal 21785 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 literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/pkg_resources/_vendor/packaging/__pycache__/utils.cpython-312.pyc b/venv/Lib/site-packages/pkg_resources/_vendor/packaging/__pycache__/utils.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..c1771d7cc9467116db6a42b56742a5ca9a8a1c4f GIT binary patch literal 7308 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 literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/pkg_resources/_vendor/packaging/__pycache__/version.cpython-312.pyc b/venv/Lib/site-packages/pkg_resources/_vendor/packaging/__pycache__/version.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..c5272a535d9dc5b043f1f45a1b2fb5328b10e083 GIT binary patch literal 20021 zcmdUXeQ;A(mgjr=wj^8f7ubA0+kh>=21Do&Lkz?~LSh_(=_aj^7P8*65y+DBo@}rp zZ=9aYmfhXcnC;9Urh5uAl`3GEnb0*`wPb6nhOOGF&D7S`N|-4;8mgAv?W&!w{YPN7 zW;=iF@7(vH4@*EY+3wi~zVF?0&pq$nbI(2ZoOAEff2yu_a=7IG);S#6$8rCIGWu1c zJuLN`IPNkha(+(aMN@?L^XzW&n{YQr%%c{+Wz_1oj@taTQM=zh>hL>8tNc}?PQR1q zM2l#RxJKQ6_h_}hdbGx0GwShscy5FfZGX!9y;7Crlxn5AVN)|+DB9J#zaG$P$%E25 zsaAyzn+e0;fD(c`@n_V-v!{@bR-=ffM!AvILtXt{QUm+fIBa9|S4dWAg|uSCB(2nH zG?}>9InnV6Css+eTV|Cn{$|lB)rl_AJ!}!HKe6~%F{~P3d8E|_)*1%&0$O_ue5A|2 zR;=@{lh*myS8=Sg9;I$+tx-o18zj3_$N15RUe!r0l0!5NTNuQJvI4FUY}h8QKnq$; zoMbwCn7yPZ5m(;gRe*nk*ksTqO7*P1MQld>jp8bBWi|0f=Z&~#`P;;`Wnk;d!01hk zzUY!x4A+S3@s3TCCZCXa&Dqr@#3d!JNb&Kpo*t)Dz%?w#Muo9p=v;6l93APJkYpts ziwfb}fRb@T-zk>F57$~p&vqmp=Hd<@Tyx5wh3 z5R7D9gX3dFML80UXRT_f^}A6N{+c-Bvt%7dqQl{6I6j$m>rW>p$E2)xKpF|g!xNGY z%{G)m2oDrI-#c`C@W|T(fnx_woH%rRAZuk!&f2vmXKRi`CxVf%sO7st>X@jhMawMx z2oIMziNiGG{HE`6l3B8d{9ZsIm}ZCp6KcTB3}yq=E}2y*4JJw~6-%rYOKe(+L!)7* z{G`KIm30L44p^nJSm;bw)-FpCDX2(UvnWku&115ZwT#6Sa5fSP1tSBjQXqp=7T@HS z0Oh_q-`8{My9&gBrKb+YLgS-SG_IWLi;0k%Q^&%g__!=}9ve?2B2wphX-EmjrBf49 zbmCNhc<2=6I#n@~lv88pMgp>=#Kz^2q-+ZSON`0e3@O|;p~_p=*reP*Eu&-u{*><{ z`F(EDRr7mqE>_pg?M#{HcFzVDENd34YUehl%&EbY~^)BN^Aa`<8WboBpn>U28$sCS$V5QMndHA#Fb6k>f}s(ce-NlFMA& zC`)qsydUIzeB!||nnFVSj3kUepre9DSpc7D!l|>cx3^cFYaQ*~UE8}b!rkpYHb3`j za!^>%t5)iPs0T-Nf<3|syrY=95Ra*3yBMt=IT%(XMJS#UJ;KQ|Xcx7xq;^;lAS6Oi zU^7TBRcZNn#uMB&f&mJ3Uyx~xWUrodQh^7_H21WQbJx$kyx>}$wyb7+4gqdlI`fvO zZhjt$yx$DfWtQszPW;VrYP~3&r`DoPnx<0uC{$`I>o~Dj=VNPFRS2yre^1E4kTeAS zCv=QrHVLv6f?Nv_|A-I?Dsf*Aqd=;%t4r;=A*Z^w>?&obme90<}@Xm`Ocn-G2oSiXF)(+!K?{>QHg0xQLS$0O(r!DUv)V zb&aO!F{@}IO(U8m+ptNrV2QAcR*~`!EUY%DYRfqajO&&rJ%ij$e!yqS zTFwNOGcwh^$!9H!6d5j%PAJv@l?MWe#-hgPU|Pu)62ockp{IWC;N>Hij{Nwev_sVn zS!)dQL=F`;gII3H-_mI$MVf$*8+f`MqS)g45OITCk~>REx8zFl<)G(4Ymk$>fPB-E zb%vF2REYP_||u7C$nI1(?*bB&z*4$=xWEcOp@!DS|phv$jAW5(f+FOISd_ ze&8+9oF)b|r#61J=Dw%vVSP*5-SWs&e{phlGPUb!&y}8xXJgv2QJu?1H&N}k#g0`5 zJLFw>t-Kq_vQA?CTi!_kj|2HRoji2)%`0zaJe$&vO~1QNcDz6*r4Pz399`$x(R5&?hkyKn!&7ioXFnKS~RNbT?Wo$~+X7ifR99RCS;wut`&K5xmQ zrCN=o`sDSLtfOQHlB{JY7K_NVWXZJ7WNopbvr;H7!`vN=QJ}1&@>& z(`baD&?*Ld&*_;n&kfEwX16U^nsXN3x<1vm#Nn26TdbQ>133=28}TK&5vW}SwX2|Z z6?B-e+UFUT24WQpvC7iYD?X+AYE!9|!9PUT*?K;)kKH=<WqJl&I_q#ROh zF1^~bNJVw7<=^DXKe89XTzl~zp+l;9$2e5fBk;9TCm4u!>n|>DYqGRTQN9vm2yT#$ z^imU&U`bLgCjLUzqdWAL<@rDr^Q1}`1TW&n`6ILmz}F?HsB@Ku3HTUsr1=(9>Im_G z&?}FsN`pjv3n4R~w)eeYXM)Wgz|~e55Z`8>F`e23hOV32&Jbp&ap?xuk_A>up+!vI z1%fx0vn(8E;IjwOB-s*p63vtXM-R*upaDvDV#$?&jjkJeehQem)W`trVYg^Qh{uAU z7TL=-$qn0^Z08#7ne1kdh*sQ}tyKoMp$D6~%6eXE$~8vFu&!-+HS8$<5G%R4ZEk z$ODdCtQV^HoeAKio29tqFpaN$mtV8hr}ts#jXmJ z9Xe)(@+!StSANf>=+tXepy<-eFYrn&U#Z7#9qWbPTdmiq@ZK7|+@M(h9Nu`08k)rX zx%ZZ~>?FB>vAo1r8g#g`6w)lY&Qd6`}%w1e*b2gzFPfm%3&XROdm{x@2LL1U{Vv(=Q5wY9T>c zg4wCS4rXC)Rh#Qk!}Djtp)cbojU;sWCu; zR|uyTHRs4MQ|G5oYcD>1S`cFh!BSi`6pMoBCT?k2Rf5m7!i|LB_Rwe3|M<2i9+vP@ZTBblvQLK%kH549NBERh|j%OWhyj!A+%#oPUE8yKxG)OyRn^`RDJoqUlY0|w zdf+b*piQ4Zpi4FLl-@1_=|6*zLK6QE-2a*8?lt$%zw?W>RPgGiOmqKlmZ-HqY+T99 zEAa3Y--X>C@;cO8G;1UK8d*ELN4xch9a$^0Rn=QmrrG1OsT?6nGEF?0tQ(o8C@UZE z)gw$MOC!>StWA-Ea_9_;!?L}ItaU_=jgPTzXRX*x!@ieQ8H zHh#Y4=9b&xh0XgPY(AXXeE6=iu=%a~&HW1v{SUnx=cViCuARHxzObq1&Y{14>x;L( zv@h&Ew%~o|VeO_xFKbWgjRoM1U;pshhqqr@Xn*xy`^meF|8e!dU;X9x7Y>|UGMnmZ z(3;v!l}XiY(yG6_(7tD(Ztv6Ds%Gbmy1oH#@%9M z)@Iymjm)}?d!3OfDj@<()%Hfv{(~wO7$}-sG${19M9u>?^Z6vFnhn?(GAE`Fz}YHQ zP_zl7jTz>CFfJ%(V&`F-LQOFX5ZkKIZoxRTkf9a|H#gYs(ROX?2m&D#S$JJ2Q7t?w zN;va{2{TI6P|S7`5n9Lc(nRe*MYUZ?=|wvyHvE44vn^*{~_{um89LzeGDYRtEp)+@BQki#tn} za>-SZrUNv~;4h)0DD0!!(x+`aCL1 zVkdTG$HpYAr$zG%7Db0PFFRT_M=Kxopjij+$*94W*{}E(`5o}HNF{t5%k52G7C`r= zNj{2OmcLNA-pX$SmT1lEeYCvnLi@jgx0GLK*Y3Gd`-{+g%hln`+C9IKj}hf>zv*%q zbY+_J->A*Mp^Eu73q3aud{#gI&h-@;p+{|V!rDb^f5Jjr=wI<$d=9ptW^F+XYE;Zx z&c?z~W<8W!^TASDS=kwqxKh|e+Mi{dOn6FI`w!Hn91^AjYwKsMIU9!+rf0S%Rr{lT zi|*Qs{j>e4gS4BJ+B^S2y4iQ%y%{)FH5c8p?zzFs?_PQrJij4+9=RF0zjn_;sISbvl6DC?>aO{Q>&@4i z=UwUMt@quX<*2P<>%{>cmz56@RK`S=CX5*PI?mg0acXwz zfu}X&X-zv?8FRXdrv5*PJ3$fEi9r-qibdhmC_0x~wIF?~9`)A?CbUB|#hT1uUw7Az zuAN|CX^g+4d#kWxtFSY#-ijEhvvFB9)Y*>z6{>0cXS0(At#Wl^{EkFJk#P~e62fv} z7LQ9vGWkwJ<8kIck&I{pBQv)N)|9?wA#9^?wiJnt(ME`{&7ex^!9Y^Rhlj%#D5gdd zK$k8B%mOc!0s`h0fYybE0y$V?$iaq;XG7Yt;ai?0L5n5^g+0|ad!9Y)V>oOSFI>XZ z!3UlEfDJ&f3`P_UI+K-PH^vPW43v)cVEa~~J5OMFouTD*hMreP4lS=Fhl*?I!^9l7 z(qSTC!lh4Ew0J|o5UnzXNXU4Ev_n`vM5LjZ9ux8Kb@Z6rT;4b>cWOoC$=@7A7?F7c zS7>q<$!2tXbV!oF*{DLs^P}>dVP#X2aU8XbvCZp6nQTX#t5LNhbCOFLPQZlQU^wXP zB#v(em?=kJ=jYWos~>dq-0SF>iK~jTCF5zCM}k>$@U8{PKaX+vLmG!d&GAnd29SC2 zQ2;Ux0&dS80~Vtw9RmX9g&t&ji-w?M>-DK?QxDpA-)rBk4*^Yp`Pz)9E$w*ij)-LW z5Rkpfros#!zSdOOD)MDL9uxC;K&|vDH%XRzL4(|+*=I>yrC}=C^n0<@uYww6lwNqO zwgiVl7%EoD7_4>x3p{}TCzk)S%x67ppf;;aWNlDaf@)-K_5S$}e$hA2Up<^zyAYqb@tTlW)*T;6?YH|XCu z`1#SBM;~n6m)W}S{_6b;_50KA{g3KbK11MoL9ysCopG$TTu^{Mj?lyPBKu?kTGf*H zTc%aVha%w+l&egqou92xmsv++ta5&o*-Drn=jAJ&CLG_W9y|9-i|n&sD#vJONG&r* z6RKID=l3Zo88C!I8LdQ(p`BU&Bn=t`ZE-Yr1?R1wd+EoM7eAW)D5cCFSnzC`{~+V> zr5(QI<6{JIeuc`H@j=P|-$9%g2q)Nr?}J$+NUcLSys8HeTEC_sQC&US`}M`?8`N%L zqDtbZi&X_8Dq?%yO(@e>>}SWxKw-k8vq0y7wnL5`32arvHm4m7SY1L)W)_XnTg_k= zMuYM>_$GOyn~AYJLh}1t*PHG7#3s92(d2GSfAgIcfTcoKR+SU|2 zfllC40tku>X@SU0Xh-z%NarXF=d6-Pue+pSB>?PF7cqZgfE=~unwR+}<*ORU2FllD zUdlJBsroA4sH$f0y+Bf%)do+9~bQRQnLc|aFtFQnpWPuo4u zp85Fo3)e2(j;GuAq#b*ft9(O9XuYUp1L{JM__Jq7K!NN1A|e3vuPXzFGef;EQ@=Q9NO+V%&F0O4%>yd_=1@&9^k{L9L0gy0x)4UIo9z)jTp z+RV(0Xqno}SXs_3VbYneTqK=&@Rd?V>e88Zv@MrM@<*6Ya)QZY!8UbS^>how>B6oO z6s+v2pv2g*I>R0RfY!j_u#69a@Zs7ht#m!XcwFu|tugU5t!}4JZ$FJgU&F$9G&q46 z6@3f&yrCEko{h=Il33zqvxN}*=;nx|MX5{eIqdhMe%=IU+0Tf7?dT({xf-r*)4X^+ zaxJpZy7Tr2_gcx_OU@=!2WIgV%RX%BI`;jxd1_|H_|J)_iiA@S;Yt6ji*BK8;H{gTG}x_`F3|6}4k)?^$g{I=ye z9Fk)O>vtMY#p^R;eIa52>uD~_KcR#TLM7Itb|LGLy#vXTeFp{)$=|0ETi@u|TR4NkzGe^rkwcV7 zl*Ex_9WZsrBso5*e(S)jKL?dE!;gHUa@$Js6ctQUa+s1IQt}BU|B{j)QSt|rT%x3v z66%opJwz=%QF0Z3N)1@_``lwwqt%h?;%b^^j$ic8`cwA#ZJDO-+plMu_S`xC*Zwd3 zU$*|2&cEr*yn1q>`rYZni%#$F_2(=btj>9Jj>GMSWr=RNe%^-9C-5l+ZaMFI>$+6^ z5{FyvHJ-E8lljL+A{ZU zN}BJxQ9u92l~|^+>j?*#hKDDZy6RgF@pETV%B4|y)gj(^*CAeenZw%0gH-6!`-IV# z$LP~BT-K(!3-jhnQ%^WNY83F4Tg6`Cv~EtBpK!QoMR>vi7<&5(^g8r}!%Zu~Q*Jve zs7@hlAp%!NHEu3TOCnk zT>9D2Dd38xa~%*svuK{;qK(K~w7eDhYUFJbRBA`ghMWUA9BQYUae61a^1JiH2zDnq zr8ddSo@ZlCVjq8iDTJ(*eN&RKm*r(< z)**{e7%;MQK`2bB`UIqlR`w>t=cv8&}L6#&ccM z*pr!Rj<`E>6KlX>aNAcX0<4pscu_rTs?F6 zk*D!fCH0-3eVArjgn#F%y)?||&e%$d9=SYhSQEDQPIU!3-)8~2qRm7j409aqr7`_@ zW8MxD5o67Nd0H+CoBu|=Y=o7^@ucHSlb;Bl!-iW>KTZ(|pTogSL=?Ka;^(EvgnFo* zK9EYg zql<0SnLnzX7d`FdVt4V$MRV33R>bf~I4*yNX6TZ_4pvxjkS|^&D(q}Ud%2T*Wda5{ z5+`&=J<0flGdDUPc^h(Ou4UtO_cixtH8TeZaaKKe6i_5IEEtc;3JdwciJ_=$!I3RFfILh77q#IfDQ*w~Xr0qr0~>$tb-~*^;JdSJFIPm9%_p zk5hD%T~qd`3nliraa;)y9O$321Gf;*0jPP(k+c-yRsm|6s!CdmaGlY*;^~&OE}uwK z&Lo$#CGAN^vMT8uw$VOc@f09?hy2GSY8%aB_$bh}6iiWdA2S9pz~@k2Fu-3q_C|uE zLt?Nu(X5>tEf7Gh$Oh^Pv0<9Kec(>*FAq1dg@2Ae0Hr`-XHz^@HXmD zCu*?`j3=CFrE~Gfo1Z4;TRt1SztT7J&YWrX_50iR-)%{6JGkIJ_}B~#Z8~l0;C*&= zIytaJATA~0Fi<3npHHcRK`h7dy{eL}5@FuqWH>BE)lqvZ~$h7iHJivd$5rW2t4)3Q8pN1c-@MZ9S zbI-qfT3@p^Xz2G>8Gq7IU^cyf69IojP=i+n88|E9HhpjtiEm%;T)ibLf zdRER_9(n3!2;gu}Cm*)$oTZaVwTt`&F?C$BQU#;IZYr`*f)UABX zm^(1|lvudLnd+Qa^`H}K=Q>gc=QrMHxG|A-?MhpAF&*J+m&w$YZ&UKGkYsJ~$uWG% zB-1>WNuS9iXzT}OWcpA>{c@N-lgYa1T-7-H@fbxWHpw&@RQ}UxKstGa`EEF9^B|Zxp0y55;>VO!^WkUos+*KB)yoF9m9lM=Y(Ro3 ztNtKQuY4C-3^=FYr=z%>ndkY(4hwIA65wn9o~!z=+&d4rcQV{N|C2kpz@7Z>&c12; zBXHjMb({H}>AFoBcl&hJqlV_x)^x|-JJQ|0FYE8V@kK1X>S(56AkEb-dKwljO^cSQ znT>PSxln57+?h+B`9{R}?tJ&|&M(b(cYhH`xA$jSj;6gwGtPnZvB9)+Fk?A^23fzy zV=3~Qrrj8Gr)SY(Cpv92%1p<{Z!cOJ_5a?S%WAEAY_>a1(>4g>>bCjAHx8%UcHTab zZhIxOswd5@d}_1t>t_;AIoxt}Rs5Q{ojDG-)GJGLd)j2@9drJt9B%*Eu!V1b%pu90 z=GQh@(wsNf%hk6{dvc~K-tx3*$inaEXChBI_KxIoUMxY z&O2_@U#q4jdT)2!c_p*s5Vg~rYud|y2R}QMGvod!|I0%;EAp6D_+24meDLc-IXi(J n9DZA<@aKkdRRne}!*&ta&7mvwOGLS9%GPi;_frp^m<;`2x5dM? literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/pkg_resources/_vendor/packaging/_elffile.py b/venv/Lib/site-packages/pkg_resources/_vendor/packaging/_elffile.py new file mode 100644 index 0000000..6fb19b3 --- /dev/null +++ b/venv/Lib/site-packages/pkg_resources/_vendor/packaging/_elffile.py @@ -0,0 +1,108 @@ +""" +ELF file parser. + +This provides a class ``ELFFile`` that parses an ELF executable in a similar +interface to ``ZipFile``. Only the read interface is implemented. + +Based on: https://gist.github.com/lyssdod/f51579ae8d93c8657a5564aefc2ffbca +ELF header: https://refspecs.linuxfoundation.org/elf/gabi4+/ch4.eheader.html +""" + +import enum +import os +import struct +from typing import IO, Optional, Tuple + + +class ELFInvalid(ValueError): + pass + + +class EIClass(enum.IntEnum): + C32 = 1 + C64 = 2 + + +class EIData(enum.IntEnum): + Lsb = 1 + Msb = 2 + + +class EMachine(enum.IntEnum): + I386 = 3 + S390 = 22 + Arm = 40 + X8664 = 62 + AArc64 = 183 + + +class ELFFile: + """ + Representation of an ELF executable. + """ + + def __init__(self, f: IO[bytes]) -> None: + self._f = f + + try: + ident = self._read("16B") + except struct.error: + raise ELFInvalid("unable to parse identification") + magic = bytes(ident[:4]) + if magic != b"\x7fELF": + raise ELFInvalid(f"invalid magic: {magic!r}") + + self.capacity = ident[4] # Format for program header (bitness). + self.encoding = ident[5] # Data structure encoding (endianness). + + try: + # e_fmt: Format for program header. + # p_fmt: Format for section header. + # p_idx: Indexes to find p_type, p_offset, and p_filesz. + e_fmt, self._p_fmt, self._p_idx = { + (1, 1): ("HHIIIIIHHH", ">IIIIIIII", (0, 1, 4)), # 32-bit MSB. + (2, 1): ("HHIQQQIHHH", ">IIQQQQQQ", (0, 2, 5)), # 64-bit MSB. + }[(self.capacity, self.encoding)] + except KeyError: + raise ELFInvalid( + f"unrecognized capacity ({self.capacity}) or " + f"encoding ({self.encoding})" + ) + + try: + ( + _, + self.machine, # Architecture type. + _, + _, + self._e_phoff, # Offset of program header. + _, + self.flags, # Processor-specific flags. + _, + self._e_phentsize, # Size of section. + self._e_phnum, # Number of sections. + ) = self._read(e_fmt) + except struct.error as e: + raise ELFInvalid("unable to parse machine and section information") from e + + def _read(self, fmt: str) -> Tuple[int, ...]: + return struct.unpack(fmt, self._f.read(struct.calcsize(fmt))) + + @property + def interpreter(self) -> Optional[str]: + """ + The path recorded in the ``PT_INTERP`` section header. + """ + for index in range(self._e_phnum): + self._f.seek(self._e_phoff + self._e_phentsize * index) + try: + data = self._read(self._p_fmt) + except struct.error: + continue + if data[self._p_idx[0]] != 3: # Not PT_INTERP. + continue + self._f.seek(data[self._p_idx[1]]) + return os.fsdecode(self._f.read(data[self._p_idx[2]])).strip("\0") + return None diff --git a/venv/Lib/site-packages/pkg_resources/_vendor/packaging/_manylinux.py b/venv/Lib/site-packages/pkg_resources/_vendor/packaging/_manylinux.py new file mode 100644 index 0000000..ad62505 --- /dev/null +++ b/venv/Lib/site-packages/pkg_resources/_vendor/packaging/_manylinux.py @@ -0,0 +1,260 @@ +import collections +import contextlib +import functools +import os +import re +import sys +import warnings +from typing import Dict, Generator, Iterator, NamedTuple, Optional, Sequence, Tuple + +from ._elffile import EIClass, EIData, ELFFile, EMachine + +EF_ARM_ABIMASK = 0xFF000000 +EF_ARM_ABI_VER5 = 0x05000000 +EF_ARM_ABI_FLOAT_HARD = 0x00000400 + + +# `os.PathLike` not a generic type until Python 3.9, so sticking with `str` +# as the type for `path` until then. +@contextlib.contextmanager +def _parse_elf(path: str) -> Generator[Optional[ELFFile], None, None]: + try: + with open(path, "rb") as f: + yield ELFFile(f) + except (OSError, TypeError, ValueError): + yield None + + +def _is_linux_armhf(executable: str) -> bool: + # hard-float ABI can be detected from the ELF header of the running + # process + # https://static.docs.arm.com/ihi0044/g/aaelf32.pdf + with _parse_elf(executable) as f: + return ( + f is not None + and f.capacity == EIClass.C32 + and f.encoding == EIData.Lsb + and f.machine == EMachine.Arm + and f.flags & EF_ARM_ABIMASK == EF_ARM_ABI_VER5 + and f.flags & EF_ARM_ABI_FLOAT_HARD == EF_ARM_ABI_FLOAT_HARD + ) + + +def _is_linux_i686(executable: str) -> bool: + with _parse_elf(executable) as f: + return ( + f is not None + and f.capacity == EIClass.C32 + and f.encoding == EIData.Lsb + and f.machine == EMachine.I386 + ) + + +def _have_compatible_abi(executable: str, archs: Sequence[str]) -> bool: + if "armv7l" in archs: + return _is_linux_armhf(executable) + if "i686" in archs: + return _is_linux_i686(executable) + allowed_archs = { + "x86_64", + "aarch64", + "ppc64", + "ppc64le", + "s390x", + "loongarch64", + "riscv64", + } + return any(arch in allowed_archs for arch in archs) + + +# If glibc ever changes its major version, we need to know what the last +# minor version was, so we can build the complete list of all versions. +# For now, guess what the highest minor version might be, assume it will +# be 50 for testing. Once this actually happens, update the dictionary +# with the actual value. +_LAST_GLIBC_MINOR: Dict[int, int] = collections.defaultdict(lambda: 50) + + +class _GLibCVersion(NamedTuple): + major: int + minor: int + + +def _glibc_version_string_confstr() -> 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/fcf1d003bf4f0100c/Lib/platform.py#L175-L183 + try: + # Should be a string like "glibc 2.17". + version_string: Optional[str] = os.confstr("CS_GNU_LIBC_VERSION") + assert version_string is not None + _, version = version_string.rsplit() + 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() -> 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. + # + # We must also handle the special case where the executable is not a + # dynamically linked executable. This can occur when using musl libc, + # for example. In this situation, dlopen() will error, leading to an + # OSError. Interestingly, at least in the case of musl, there is no + # errno set on the OSError. The single string argument used to construct + # OSError comes from libc itself and is therefore not portable to + # hard code here. In any case, failure to call dlopen() means we + # can proceed, so we bail on our attempt. + try: + process_namespace = ctypes.CDLL(None) + except OSError: + return 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: str = gnu_get_libc_version() + # py2 / py3 compatibility: + if not isinstance(version_str, str): + version_str = version_str.decode("ascii") + + return version_str + + +def _glibc_version_string() -> Optional[str]: + """Returns glibc version string, or None if not using glibc.""" + return _glibc_version_string_confstr() or _glibc_version_string_ctypes() + + +def _parse_glibc_version(version_str: str) -> Tuple[int, int]: + """Parse glibc 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( + f"Expected glibc version with 2 components major.minor," + f" got: {version_str}", + RuntimeWarning, + ) + return -1, -1 + return int(m.group("major")), int(m.group("minor")) + + +@functools.lru_cache() +def _get_glibc_version() -> Tuple[int, int]: + version_str = _glibc_version_string() + if version_str is None: + return (-1, -1) + return _parse_glibc_version(version_str) + + +# From PEP 513, PEP 600 +def _is_compatible(arch: str, version: _GLibCVersion) -> bool: + sys_glibc = _get_glibc_version() + if sys_glibc < version: + return False + # Check for presence of _manylinux module. + try: + import _manylinux + except ImportError: + return True + if hasattr(_manylinux, "manylinux_compatible"): + result = _manylinux.manylinux_compatible(version[0], version[1], arch) + if result is not None: + return bool(result) + return True + if version == _GLibCVersion(2, 5): + if hasattr(_manylinux, "manylinux1_compatible"): + return bool(_manylinux.manylinux1_compatible) + if version == _GLibCVersion(2, 12): + if hasattr(_manylinux, "manylinux2010_compatible"): + return bool(_manylinux.manylinux2010_compatible) + if version == _GLibCVersion(2, 17): + if hasattr(_manylinux, "manylinux2014_compatible"): + return bool(_manylinux.manylinux2014_compatible) + return True + + +_LEGACY_MANYLINUX_MAP = { + # CentOS 7 w/ glibc 2.17 (PEP 599) + (2, 17): "manylinux2014", + # CentOS 6 w/ glibc 2.12 (PEP 571) + (2, 12): "manylinux2010", + # CentOS 5 w/ glibc 2.5 (PEP 513) + (2, 5): "manylinux1", +} + + +def platform_tags(archs: Sequence[str]) -> Iterator[str]: + """Generate manylinux tags compatible to the current platform. + + :param archs: Sequence of compatible architectures. + The first one shall be the closest to the actual architecture and be the part of + platform tag after the ``linux_`` prefix, e.g. ``x86_64``. + The ``linux_`` prefix is assumed as a prerequisite for the current platform to + be manylinux-compatible. + + :returns: An iterator of compatible manylinux tags. + """ + if not _have_compatible_abi(sys.executable, archs): + return + # Oldest glibc to be supported regardless of architecture is (2, 17). + too_old_glibc2 = _GLibCVersion(2, 16) + if set(archs) & {"x86_64", "i686"}: + # On x86/i686 also oldest glibc to be supported is (2, 5). + too_old_glibc2 = _GLibCVersion(2, 4) + current_glibc = _GLibCVersion(*_get_glibc_version()) + glibc_max_list = [current_glibc] + # We can assume compatibility across glibc major versions. + # https://sourceware.org/bugzilla/show_bug.cgi?id=24636 + # + # Build a list of maximum glibc versions so that we can + # output the canonical list of all glibc from current_glibc + # down to too_old_glibc2, including all intermediary versions. + for glibc_major in range(current_glibc.major - 1, 1, -1): + glibc_minor = _LAST_GLIBC_MINOR[glibc_major] + glibc_max_list.append(_GLibCVersion(glibc_major, glibc_minor)) + for arch in archs: + for glibc_max in glibc_max_list: + if glibc_max.major == too_old_glibc2.major: + min_minor = too_old_glibc2.minor + else: + # For other glibc major versions oldest supported is (x, 0). + min_minor = -1 + for glibc_minor in range(glibc_max.minor, min_minor, -1): + glibc_version = _GLibCVersion(glibc_max.major, glibc_minor) + tag = "manylinux_{}_{}".format(*glibc_version) + if _is_compatible(arch, glibc_version): + yield f"{tag}_{arch}" + # Handle the legacy manylinux1, manylinux2010, manylinux2014 tags. + if glibc_version in _LEGACY_MANYLINUX_MAP: + legacy_tag = _LEGACY_MANYLINUX_MAP[glibc_version] + if _is_compatible(arch, glibc_version): + yield f"{legacy_tag}_{arch}" diff --git a/venv/Lib/site-packages/pkg_resources/_vendor/packaging/_musllinux.py b/venv/Lib/site-packages/pkg_resources/_vendor/packaging/_musllinux.py new file mode 100644 index 0000000..86419df --- /dev/null +++ b/venv/Lib/site-packages/pkg_resources/_vendor/packaging/_musllinux.py @@ -0,0 +1,83 @@ +"""PEP 656 support. + +This module implements logic to detect if the currently running Python is +linked against musl, and what musl version is used. +""" + +import functools +import re +import subprocess +import sys +from typing import Iterator, NamedTuple, Optional, Sequence + +from ._elffile import ELFFile + + +class _MuslVersion(NamedTuple): + major: int + minor: int + + +def _parse_musl_version(output: str) -> Optional[_MuslVersion]: + lines = [n for n in (n.strip() for n in output.splitlines()) if n] + if len(lines) < 2 or lines[0][:4] != "musl": + return None + m = re.match(r"Version (\d+)\.(\d+)", lines[1]) + if not m: + return None + return _MuslVersion(major=int(m.group(1)), minor=int(m.group(2))) + + +@functools.lru_cache() +def _get_musl_version(executable: str) -> Optional[_MuslVersion]: + """Detect currently-running musl runtime version. + + This is done by checking the specified executable's dynamic linking + information, and invoking the loader to parse its output for a version + string. If the loader is musl, the output would be something like:: + + musl libc (x86_64) + Version 1.2.2 + Dynamic Program Loader + """ + try: + with open(executable, "rb") as f: + ld = ELFFile(f).interpreter + except (OSError, TypeError, ValueError): + return None + if ld is None or "musl" not in ld: + return None + proc = subprocess.run([ld], stderr=subprocess.PIPE, text=True) + return _parse_musl_version(proc.stderr) + + +def platform_tags(archs: Sequence[str]) -> Iterator[str]: + """Generate musllinux tags compatible to the current platform. + + :param archs: Sequence of compatible architectures. + The first one shall be the closest to the actual architecture and be the part of + platform tag after the ``linux_`` prefix, e.g. ``x86_64``. + The ``linux_`` prefix is assumed as a prerequisite for the current platform to + be musllinux-compatible. + + :returns: An iterator of compatible musllinux tags. + """ + sys_musl = _get_musl_version(sys.executable) + if sys_musl is None: # Python not dynamically linked against musl. + return + for arch in archs: + for minor in range(sys_musl.minor, -1, -1): + yield f"musllinux_{sys_musl.major}_{minor}_{arch}" + + +if __name__ == "__main__": # pragma: no cover + import sysconfig + + plat = sysconfig.get_platform() + assert plat.startswith("linux-"), "not linux" + + print("plat:", plat) + print("musl:", _get_musl_version(sys.executable)) + print("tags:", end=" ") + for t in platform_tags(re.sub(r"[.-]", "_", plat.split("-", 1)[-1])): + print(t, end="\n ") diff --git a/venv/Lib/site-packages/pkg_resources/_vendor/packaging/_parser.py b/venv/Lib/site-packages/pkg_resources/_vendor/packaging/_parser.py new file mode 100644 index 0000000..684df75 --- /dev/null +++ b/venv/Lib/site-packages/pkg_resources/_vendor/packaging/_parser.py @@ -0,0 +1,356 @@ +"""Handwritten parser of dependency specifiers. + +The docstring for each __parse_* function contains ENBF-inspired grammar representing +the implementation. +""" + +import ast +from typing import Any, List, NamedTuple, Optional, Tuple, Union + +from ._tokenizer import DEFAULT_RULES, Tokenizer + + +class Node: + def __init__(self, value: str) -> None: + self.value = value + + def __str__(self) -> str: + return self.value + + def __repr__(self) -> str: + return f"<{self.__class__.__name__}('{self}')>" + + def serialize(self) -> str: + raise NotImplementedError + + +class Variable(Node): + def serialize(self) -> str: + return str(self) + + +class Value(Node): + def serialize(self) -> str: + return f'"{self}"' + + +class Op(Node): + def serialize(self) -> str: + return str(self) + + +MarkerVar = Union[Variable, Value] +MarkerItem = Tuple[MarkerVar, Op, MarkerVar] +# MarkerAtom = Union[MarkerItem, List["MarkerAtom"]] +# MarkerList = List[Union["MarkerList", MarkerAtom, str]] +# mypy does not support recursive type definition +# https://github.com/python/mypy/issues/731 +MarkerAtom = Any +MarkerList = List[Any] + + +class ParsedRequirement(NamedTuple): + name: str + url: str + extras: List[str] + specifier: str + marker: Optional[MarkerList] + + +# -------------------------------------------------------------------------------------- +# Recursive descent parser for dependency specifier +# -------------------------------------------------------------------------------------- +def parse_requirement(source: str) -> ParsedRequirement: + return _parse_requirement(Tokenizer(source, rules=DEFAULT_RULES)) + + +def _parse_requirement(tokenizer: Tokenizer) -> ParsedRequirement: + """ + requirement = WS? IDENTIFIER WS? extras WS? requirement_details + """ + tokenizer.consume("WS") + + name_token = tokenizer.expect( + "IDENTIFIER", expected="package name at the start of dependency specifier" + ) + name = name_token.text + tokenizer.consume("WS") + + extras = _parse_extras(tokenizer) + tokenizer.consume("WS") + + url, specifier, marker = _parse_requirement_details(tokenizer) + tokenizer.expect("END", expected="end of dependency specifier") + + return ParsedRequirement(name, url, extras, specifier, marker) + + +def _parse_requirement_details( + tokenizer: Tokenizer, +) -> Tuple[str, str, Optional[MarkerList]]: + """ + requirement_details = AT URL (WS requirement_marker?)? + | specifier WS? (requirement_marker)? + """ + + specifier = "" + url = "" + marker = None + + if tokenizer.check("AT"): + tokenizer.read() + tokenizer.consume("WS") + + url_start = tokenizer.position + url = tokenizer.expect("URL", expected="URL after @").text + if tokenizer.check("END", peek=True): + return (url, specifier, marker) + + tokenizer.expect("WS", expected="whitespace after URL") + + # The input might end after whitespace. + if tokenizer.check("END", peek=True): + return (url, specifier, marker) + + marker = _parse_requirement_marker( + tokenizer, span_start=url_start, after="URL and whitespace" + ) + else: + specifier_start = tokenizer.position + specifier = _parse_specifier(tokenizer) + tokenizer.consume("WS") + + if tokenizer.check("END", peek=True): + return (url, specifier, marker) + + marker = _parse_requirement_marker( + tokenizer, + span_start=specifier_start, + after=( + "version specifier" + if specifier + else "name and no valid version specifier" + ), + ) + + return (url, specifier, marker) + + +def _parse_requirement_marker( + tokenizer: Tokenizer, *, span_start: int, after: str +) -> MarkerList: + """ + requirement_marker = SEMICOLON marker WS? + """ + + if not tokenizer.check("SEMICOLON"): + tokenizer.raise_syntax_error( + f"Expected end or semicolon (after {after})", + span_start=span_start, + ) + tokenizer.read() + + marker = _parse_marker(tokenizer) + tokenizer.consume("WS") + + return marker + + +def _parse_extras(tokenizer: Tokenizer) -> List[str]: + """ + extras = (LEFT_BRACKET wsp* extras_list? wsp* RIGHT_BRACKET)? + """ + if not tokenizer.check("LEFT_BRACKET", peek=True): + return [] + + with tokenizer.enclosing_tokens( + "LEFT_BRACKET", + "RIGHT_BRACKET", + around="extras", + ): + tokenizer.consume("WS") + extras = _parse_extras_list(tokenizer) + tokenizer.consume("WS") + + return extras + + +def _parse_extras_list(tokenizer: Tokenizer) -> List[str]: + """ + extras_list = identifier (wsp* ',' wsp* identifier)* + """ + extras: List[str] = [] + + if not tokenizer.check("IDENTIFIER"): + return extras + + extras.append(tokenizer.read().text) + + while True: + tokenizer.consume("WS") + if tokenizer.check("IDENTIFIER", peek=True): + tokenizer.raise_syntax_error("Expected comma between extra names") + elif not tokenizer.check("COMMA"): + break + + tokenizer.read() + tokenizer.consume("WS") + + extra_token = tokenizer.expect("IDENTIFIER", expected="extra name after comma") + extras.append(extra_token.text) + + return extras + + +def _parse_specifier(tokenizer: Tokenizer) -> str: + """ + specifier = LEFT_PARENTHESIS WS? version_many WS? RIGHT_PARENTHESIS + | WS? version_many WS? + """ + with tokenizer.enclosing_tokens( + "LEFT_PARENTHESIS", + "RIGHT_PARENTHESIS", + around="version specifier", + ): + tokenizer.consume("WS") + parsed_specifiers = _parse_version_many(tokenizer) + tokenizer.consume("WS") + + return parsed_specifiers + + +def _parse_version_many(tokenizer: Tokenizer) -> str: + """ + version_many = (SPECIFIER (WS? COMMA WS? SPECIFIER)*)? + """ + parsed_specifiers = "" + while tokenizer.check("SPECIFIER"): + span_start = tokenizer.position + parsed_specifiers += tokenizer.read().text + if tokenizer.check("VERSION_PREFIX_TRAIL", peek=True): + tokenizer.raise_syntax_error( + ".* suffix can only be used with `==` or `!=` operators", + span_start=span_start, + span_end=tokenizer.position + 1, + ) + if tokenizer.check("VERSION_LOCAL_LABEL_TRAIL", peek=True): + tokenizer.raise_syntax_error( + "Local version label can only be used with `==` or `!=` operators", + span_start=span_start, + span_end=tokenizer.position, + ) + tokenizer.consume("WS") + if not tokenizer.check("COMMA"): + break + parsed_specifiers += tokenizer.read().text + tokenizer.consume("WS") + + return parsed_specifiers + + +# -------------------------------------------------------------------------------------- +# Recursive descent parser for marker expression +# -------------------------------------------------------------------------------------- +def parse_marker(source: str) -> MarkerList: + return _parse_full_marker(Tokenizer(source, rules=DEFAULT_RULES)) + + +def _parse_full_marker(tokenizer: Tokenizer) -> MarkerList: + retval = _parse_marker(tokenizer) + tokenizer.expect("END", expected="end of marker expression") + return retval + + +def _parse_marker(tokenizer: Tokenizer) -> MarkerList: + """ + marker = marker_atom (BOOLOP marker_atom)+ + """ + expression = [_parse_marker_atom(tokenizer)] + while tokenizer.check("BOOLOP"): + token = tokenizer.read() + expr_right = _parse_marker_atom(tokenizer) + expression.extend((token.text, expr_right)) + return expression + + +def _parse_marker_atom(tokenizer: Tokenizer) -> MarkerAtom: + """ + marker_atom = WS? LEFT_PARENTHESIS WS? marker WS? RIGHT_PARENTHESIS WS? + | WS? marker_item WS? + """ + + tokenizer.consume("WS") + if tokenizer.check("LEFT_PARENTHESIS", peek=True): + with tokenizer.enclosing_tokens( + "LEFT_PARENTHESIS", + "RIGHT_PARENTHESIS", + around="marker expression", + ): + tokenizer.consume("WS") + marker: MarkerAtom = _parse_marker(tokenizer) + tokenizer.consume("WS") + else: + marker = _parse_marker_item(tokenizer) + tokenizer.consume("WS") + return marker + + +def _parse_marker_item(tokenizer: Tokenizer) -> MarkerItem: + """ + marker_item = WS? marker_var WS? marker_op WS? marker_var WS? + """ + tokenizer.consume("WS") + marker_var_left = _parse_marker_var(tokenizer) + tokenizer.consume("WS") + marker_op = _parse_marker_op(tokenizer) + tokenizer.consume("WS") + marker_var_right = _parse_marker_var(tokenizer) + tokenizer.consume("WS") + return (marker_var_left, marker_op, marker_var_right) + + +def _parse_marker_var(tokenizer: Tokenizer) -> MarkerVar: + """ + marker_var = VARIABLE | QUOTED_STRING + """ + if tokenizer.check("VARIABLE"): + return process_env_var(tokenizer.read().text.replace(".", "_")) + elif tokenizer.check("QUOTED_STRING"): + return process_python_str(tokenizer.read().text) + else: + tokenizer.raise_syntax_error( + message="Expected a marker variable or quoted string" + ) + + +def process_env_var(env_var: str) -> Variable: + if env_var in ("platform_python_implementation", "python_implementation"): + return Variable("platform_python_implementation") + else: + return Variable(env_var) + + +def process_python_str(python_str: str) -> Value: + value = ast.literal_eval(python_str) + return Value(str(value)) + + +def _parse_marker_op(tokenizer: Tokenizer) -> Op: + """ + marker_op = IN | NOT IN | OP + """ + if tokenizer.check("IN"): + tokenizer.read() + return Op("in") + elif tokenizer.check("NOT"): + tokenizer.read() + tokenizer.expect("WS", expected="whitespace after 'not'") + tokenizer.expect("IN", expected="'in' after 'not'") + return Op("not in") + elif tokenizer.check("OP"): + return Op(tokenizer.read().text) + else: + return tokenizer.raise_syntax_error( + "Expected marker operator, one of " + "<=, <, !=, ==, >=, >, ~=, ===, in, not in" + ) diff --git a/venv/Lib/site-packages/pkg_resources/_vendor/packaging/_structures.py b/venv/Lib/site-packages/pkg_resources/_vendor/packaging/_structures.py new file mode 100644 index 0000000..90a6465 --- /dev/null +++ b/venv/Lib/site-packages/pkg_resources/_vendor/packaging/_structures.py @@ -0,0 +1,61 @@ +# 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. + + +class InfinityType: + def __repr__(self) -> str: + return "Infinity" + + def __hash__(self) -> int: + return hash(repr(self)) + + def __lt__(self, other: object) -> bool: + return False + + def __le__(self, other: object) -> bool: + return False + + def __eq__(self, other: object) -> bool: + return isinstance(other, self.__class__) + + def __gt__(self, other: object) -> bool: + return True + + def __ge__(self, other: object) -> bool: + return True + + def __neg__(self: object) -> "NegativeInfinityType": + return NegativeInfinity + + +Infinity = InfinityType() + + +class NegativeInfinityType: + def __repr__(self) -> str: + return "-Infinity" + + def __hash__(self) -> int: + return hash(repr(self)) + + def __lt__(self, other: object) -> bool: + return True + + def __le__(self, other: object) -> bool: + return True + + def __eq__(self, other: object) -> bool: + return isinstance(other, self.__class__) + + def __gt__(self, other: object) -> bool: + return False + + def __ge__(self, other: object) -> bool: + return False + + def __neg__(self: object) -> InfinityType: + return Infinity + + +NegativeInfinity = NegativeInfinityType() diff --git a/venv/Lib/site-packages/pkg_resources/_vendor/packaging/_tokenizer.py b/venv/Lib/site-packages/pkg_resources/_vendor/packaging/_tokenizer.py new file mode 100644 index 0000000..dd0d648 --- /dev/null +++ b/venv/Lib/site-packages/pkg_resources/_vendor/packaging/_tokenizer.py @@ -0,0 +1,192 @@ +import contextlib +import re +from dataclasses import dataclass +from typing import Dict, Iterator, NoReturn, Optional, Tuple, Union + +from .specifiers import Specifier + + +@dataclass +class Token: + name: str + text: str + position: int + + +class ParserSyntaxError(Exception): + """The provided source text could not be parsed correctly.""" + + def __init__( + self, + message: str, + *, + source: str, + span: Tuple[int, int], + ) -> None: + self.span = span + self.message = message + self.source = source + + super().__init__() + + def __str__(self) -> str: + marker = " " * self.span[0] + "~" * (self.span[1] - self.span[0]) + "^" + return "\n ".join([self.message, self.source, marker]) + + +DEFAULT_RULES: "Dict[str, Union[str, re.Pattern[str]]]" = { + "LEFT_PARENTHESIS": r"\(", + "RIGHT_PARENTHESIS": r"\)", + "LEFT_BRACKET": r"\[", + "RIGHT_BRACKET": r"\]", + "SEMICOLON": r";", + "COMMA": r",", + "QUOTED_STRING": re.compile( + r""" + ( + ('[^']*') + | + ("[^"]*") + ) + """, + re.VERBOSE, + ), + "OP": r"(===|==|~=|!=|<=|>=|<|>)", + "BOOLOP": r"\b(or|and)\b", + "IN": r"\bin\b", + "NOT": r"\bnot\b", + "VARIABLE": re.compile( + r""" + \b( + python_version + |python_full_version + |os[._]name + |sys[._]platform + |platform_(release|system) + |platform[._](version|machine|python_implementation) + |python_implementation + |implementation_(name|version) + |extra + )\b + """, + re.VERBOSE, + ), + "SPECIFIER": re.compile( + Specifier._operator_regex_str + Specifier._version_regex_str, + re.VERBOSE | re.IGNORECASE, + ), + "AT": r"\@", + "URL": r"[^ \t]+", + "IDENTIFIER": r"\b[a-zA-Z0-9][a-zA-Z0-9._-]*\b", + "VERSION_PREFIX_TRAIL": r"\.\*", + "VERSION_LOCAL_LABEL_TRAIL": r"\+[a-z0-9]+(?:[-_\.][a-z0-9]+)*", + "WS": r"[ \t]+", + "END": r"$", +} + + +class Tokenizer: + """Context-sensitive token parsing. + + Provides methods to examine the input stream to check whether the next token + matches. + """ + + def __init__( + self, + source: str, + *, + rules: "Dict[str, Union[str, re.Pattern[str]]]", + ) -> None: + self.source = source + self.rules: Dict[str, re.Pattern[str]] = { + name: re.compile(pattern) for name, pattern in rules.items() + } + self.next_token: Optional[Token] = None + self.position = 0 + + def consume(self, name: str) -> None: + """Move beyond provided token name, if at current position.""" + if self.check(name): + self.read() + + def check(self, name: str, *, peek: bool = False) -> bool: + """Check whether the next token has the provided name. + + By default, if the check succeeds, the token *must* be read before + another check. If `peek` is set to `True`, the token is not loaded and + would need to be checked again. + """ + assert ( + self.next_token is None + ), f"Cannot check for {name!r}, already have {self.next_token!r}" + assert name in self.rules, f"Unknown token name: {name!r}" + + expression = self.rules[name] + + match = expression.match(self.source, self.position) + if match is None: + return False + if not peek: + self.next_token = Token(name, match[0], self.position) + return True + + def expect(self, name: str, *, expected: str) -> Token: + """Expect a certain token name next, failing with a syntax error otherwise. + + The token is *not* read. + """ + if not self.check(name): + raise self.raise_syntax_error(f"Expected {expected}") + return self.read() + + def read(self) -> Token: + """Consume the next token and return it.""" + token = self.next_token + assert token is not None + + self.position += len(token.text) + self.next_token = None + + return token + + def raise_syntax_error( + self, + message: str, + *, + span_start: Optional[int] = None, + span_end: Optional[int] = None, + ) -> NoReturn: + """Raise ParserSyntaxError at the given position.""" + span = ( + self.position if span_start is None else span_start, + self.position if span_end is None else span_end, + ) + raise ParserSyntaxError( + message, + source=self.source, + span=span, + ) + + @contextlib.contextmanager + def enclosing_tokens( + self, open_token: str, close_token: str, *, around: str + ) -> Iterator[None]: + if self.check(open_token): + open_position = self.position + self.read() + else: + open_position = None + + yield + + if open_position is None: + return + + if not self.check(close_token): + self.raise_syntax_error( + f"Expected matching {close_token} for {open_token}, after {around}", + span_start=open_position, + ) + + self.read() diff --git a/venv/Lib/site-packages/pkg_resources/_vendor/packaging/markers.py b/venv/Lib/site-packages/pkg_resources/_vendor/packaging/markers.py new file mode 100644 index 0000000..8b98fca --- /dev/null +++ b/venv/Lib/site-packages/pkg_resources/_vendor/packaging/markers.py @@ -0,0 +1,252 @@ +# 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. + +import operator +import os +import platform +import sys +from typing import Any, Callable, Dict, List, Optional, Tuple, Union + +from ._parser import ( + MarkerAtom, + MarkerList, + Op, + Value, + Variable, + parse_marker as _parse_marker, +) +from ._tokenizer import ParserSyntaxError +from .specifiers import InvalidSpecifier, Specifier +from .utils import canonicalize_name + +__all__ = [ + "InvalidMarker", + "UndefinedComparison", + "UndefinedEnvironmentName", + "Marker", + "default_environment", +] + +Operator = Callable[[str, str], bool] + + +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. + """ + + +def _normalize_extra_values(results: Any) -> Any: + """ + Normalize extra values. + """ + if isinstance(results[0], tuple): + lhs, op, rhs = results[0] + if isinstance(lhs, Variable) and lhs.value == "extra": + normalized_extra = canonicalize_name(rhs.value) + rhs = Value(normalized_extra) + elif isinstance(rhs, Variable) and rhs.value == "extra": + normalized_extra = canonicalize_name(lhs.value) + lhs = Value(normalized_extra) + results[0] = lhs, op, rhs + return results + + +def _format_marker( + marker: Union[List[str], MarkerAtom, str], first: Optional[bool] = True +) -> str: + + assert isinstance(marker, (list, tuple, str)) + + # 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: Dict[str, Operator] = { + "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: str, op: Op, rhs: str) -> bool: + try: + spec = Specifier("".join([op.serialize(), rhs])) + except InvalidSpecifier: + pass + else: + return spec.contains(lhs, prereleases=True) + + oper: Optional[Operator] = _operators.get(op.serialize()) + if oper is None: + raise UndefinedComparison(f"Undefined {op!r} on {lhs!r} and {rhs!r}.") + + return oper(lhs, rhs) + + +def _normalize(*values: str, key: str) -> Tuple[str, ...]: + # PEP 685 – Comparison of extra names for optional distribution dependencies + # https://peps.python.org/pep-0685/ + # > When comparing extra names, tools MUST normalize the names being + # > compared using the semantics outlined in PEP 503 for names + if key == "extra": + return tuple(canonicalize_name(v) for v in values) + + # other environment markers don't have such standards + return values + + +def _evaluate_markers(markers: MarkerList, environment: Dict[str, str]) -> bool: + groups: List[List[bool]] = [[]] + + for marker in markers: + assert isinstance(marker, (list, tuple, str)) + + if isinstance(marker, list): + groups[-1].append(_evaluate_markers(marker, environment)) + elif isinstance(marker, tuple): + lhs, op, rhs = marker + + if isinstance(lhs, Variable): + environment_key = lhs.value + lhs_value = environment[environment_key] + rhs_value = rhs.value + else: + lhs_value = lhs.value + environment_key = rhs.value + rhs_value = environment[environment_key] + + lhs_value, rhs_value = _normalize(lhs_value, rhs_value, key=environment_key) + 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: "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() -> Dict[str, str]: + iver = format_full_version(sys.implementation.version) + implementation_name = sys.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: + def __init__(self, marker: str) -> None: + # Note: We create a Marker object without calling this constructor in + # packaging.requirements.Requirement. If any additional logic is + # added here, make sure to mirror/adapt Requirement. + try: + self._markers = _normalize_extra_values(_parse_marker(marker)) + # The attribute `_markers` can be described in terms of a recursive type: + # MarkerList = List[Union[Tuple[Node, ...], str, MarkerList]] + # + # For example, the following expression: + # python_version > "3.6" or (python_version == "3.6" and os_name == "unix") + # + # is parsed into: + # [ + # (, ')>, ), + # 'and', + # [ + # (, , ), + # 'or', + # (, , ) + # ] + # ] + except ParserSyntaxError as e: + raise InvalidMarker(str(e)) from e + + def __str__(self) -> str: + return _format_marker(self._markers) + + def __repr__(self) -> str: + return f"" + + def __hash__(self) -> int: + return hash((self.__class__.__name__, str(self))) + + def __eq__(self, other: Any) -> bool: + if not isinstance(other, Marker): + return NotImplemented + + return str(self) == str(other) + + def evaluate(self, environment: Optional[Dict[str, str]] = None) -> 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() + current_environment["extra"] = "" + if environment is not None: + current_environment.update(environment) + # The API used to allow setting extra to None. We need to handle this + # case for backwards compatibility. + if current_environment["extra"] is None: + current_environment["extra"] = "" + + return _evaluate_markers(self._markers, current_environment) diff --git a/venv/Lib/site-packages/pkg_resources/_vendor/packaging/metadata.py b/venv/Lib/site-packages/pkg_resources/_vendor/packaging/metadata.py new file mode 100644 index 0000000..fb27493 --- /dev/null +++ b/venv/Lib/site-packages/pkg_resources/_vendor/packaging/metadata.py @@ -0,0 +1,825 @@ +import email.feedparser +import email.header +import email.message +import email.parser +import email.policy +import sys +import typing +from typing import ( + Any, + Callable, + Dict, + Generic, + List, + Optional, + Tuple, + Type, + Union, + cast, +) + +from . import requirements, specifiers, utils, version as version_module + +T = typing.TypeVar("T") +if sys.version_info[:2] >= (3, 8): # pragma: no cover + from typing import Literal, TypedDict +else: # pragma: no cover + if typing.TYPE_CHECKING: + from typing_extensions import Literal, TypedDict + else: + try: + from typing_extensions import Literal, TypedDict + except ImportError: + + class Literal: + def __init_subclass__(*_args, **_kwargs): + pass + + class TypedDict: + def __init_subclass__(*_args, **_kwargs): + pass + + +try: + ExceptionGroup +except NameError: # pragma: no cover + + class ExceptionGroup(Exception): # noqa: N818 + """A minimal implementation of :external:exc:`ExceptionGroup` from Python 3.11. + + If :external:exc:`ExceptionGroup` is already defined by Python itself, + that version is used instead. + """ + + message: str + exceptions: List[Exception] + + def __init__(self, message: str, exceptions: List[Exception]) -> None: + self.message = message + self.exceptions = exceptions + + def __repr__(self) -> str: + return f"{self.__class__.__name__}({self.message!r}, {self.exceptions!r})" + +else: # pragma: no cover + ExceptionGroup = ExceptionGroup + + +class InvalidMetadata(ValueError): + """A metadata field contains invalid data.""" + + field: str + """The name of the field that contains invalid data.""" + + def __init__(self, field: str, message: str) -> None: + self.field = field + super().__init__(message) + + +# The RawMetadata class attempts to make as few assumptions about the underlying +# serialization formats as possible. The idea is that as long as a serialization +# formats offer some very basic primitives in *some* way then we can support +# serializing to and from that format. +class RawMetadata(TypedDict, total=False): + """A dictionary of raw core metadata. + + Each field in core metadata maps to a key of this dictionary (when data is + provided). The key is lower-case and underscores are used instead of dashes + compared to the equivalent core metadata field. Any core metadata field that + can be specified multiple times or can hold multiple values in a single + field have a key with a plural name. See :class:`Metadata` whose attributes + match the keys of this dictionary. + + Core metadata fields that can be specified multiple times are stored as a + list or dict depending on which is appropriate for the field. Any fields + which hold multiple values in a single field are stored as a list. + + """ + + # Metadata 1.0 - PEP 241 + metadata_version: str + name: str + version: str + platforms: List[str] + summary: str + description: str + keywords: List[str] + home_page: str + author: str + author_email: str + license: str + + # Metadata 1.1 - PEP 314 + supported_platforms: List[str] + download_url: str + classifiers: List[str] + requires: List[str] + provides: List[str] + obsoletes: List[str] + + # Metadata 1.2 - PEP 345 + maintainer: str + maintainer_email: str + requires_dist: List[str] + provides_dist: List[str] + obsoletes_dist: List[str] + requires_python: str + requires_external: List[str] + project_urls: Dict[str, str] + + # Metadata 2.0 + # PEP 426 attempted to completely revamp the metadata format + # but got stuck without ever being able to build consensus on + # it and ultimately ended up withdrawn. + # + # However, a number of tools had started emitting METADATA with + # `2.0` Metadata-Version, so for historical reasons, this version + # was skipped. + + # Metadata 2.1 - PEP 566 + description_content_type: str + provides_extra: List[str] + + # Metadata 2.2 - PEP 643 + dynamic: List[str] + + # Metadata 2.3 - PEP 685 + # No new fields were added in PEP 685, just some edge case were + # tightened up to provide better interoptability. + + +_STRING_FIELDS = { + "author", + "author_email", + "description", + "description_content_type", + "download_url", + "home_page", + "license", + "maintainer", + "maintainer_email", + "metadata_version", + "name", + "requires_python", + "summary", + "version", +} + +_LIST_FIELDS = { + "classifiers", + "dynamic", + "obsoletes", + "obsoletes_dist", + "platforms", + "provides", + "provides_dist", + "provides_extra", + "requires", + "requires_dist", + "requires_external", + "supported_platforms", +} + +_DICT_FIELDS = { + "project_urls", +} + + +def _parse_keywords(data: str) -> List[str]: + """Split a string of comma-separate keyboards into a list of keywords.""" + return [k.strip() for k in data.split(",")] + + +def _parse_project_urls(data: List[str]) -> Dict[str, str]: + """Parse a list of label/URL string pairings separated by a comma.""" + urls = {} + for pair in data: + # Our logic is slightly tricky here as we want to try and do + # *something* reasonable with malformed data. + # + # The main thing that we have to worry about, is data that does + # not have a ',' at all to split the label from the Value. There + # isn't a singular right answer here, and we will fail validation + # later on (if the caller is validating) so it doesn't *really* + # matter, but since the missing value has to be an empty str + # and our return value is dict[str, str], if we let the key + # be the missing value, then they'd have multiple '' values that + # overwrite each other in a accumulating dict. + # + # The other potentional issue is that it's possible to have the + # same label multiple times in the metadata, with no solid "right" + # answer with what to do in that case. As such, we'll do the only + # thing we can, which is treat the field as unparseable and add it + # to our list of unparsed fields. + parts = [p.strip() for p in pair.split(",", 1)] + parts.extend([""] * (max(0, 2 - len(parts)))) # Ensure 2 items + + # TODO: The spec doesn't say anything about if the keys should be + # considered case sensitive or not... logically they should + # be case-preserving and case-insensitive, but doing that + # would open up more cases where we might have duplicate + # entries. + label, url = parts + if label in urls: + # The label already exists in our set of urls, so this field + # is unparseable, and we can just add the whole thing to our + # unparseable data and stop processing it. + raise KeyError("duplicate labels in project urls") + urls[label] = url + + return urls + + +def _get_payload(msg: email.message.Message, source: Union[bytes, str]) -> str: + """Get the body of the message.""" + # If our source is a str, then our caller has managed encodings for us, + # and we don't need to deal with it. + if isinstance(source, str): + payload: str = msg.get_payload() + return payload + # If our source is a bytes, then we're managing the encoding and we need + # to deal with it. + else: + bpayload: bytes = msg.get_payload(decode=True) + try: + return bpayload.decode("utf8", "strict") + except UnicodeDecodeError: + raise ValueError("payload in an invalid encoding") + + +# The various parse_FORMAT functions here are intended to be as lenient as +# possible in their parsing, while still returning a correctly typed +# RawMetadata. +# +# To aid in this, we also generally want to do as little touching of the +# data as possible, except where there are possibly some historic holdovers +# that make valid data awkward to work with. +# +# While this is a lower level, intermediate format than our ``Metadata`` +# class, some light touch ups can make a massive difference in usability. + +# Map METADATA fields to RawMetadata. +_EMAIL_TO_RAW_MAPPING = { + "author": "author", + "author-email": "author_email", + "classifier": "classifiers", + "description": "description", + "description-content-type": "description_content_type", + "download-url": "download_url", + "dynamic": "dynamic", + "home-page": "home_page", + "keywords": "keywords", + "license": "license", + "maintainer": "maintainer", + "maintainer-email": "maintainer_email", + "metadata-version": "metadata_version", + "name": "name", + "obsoletes": "obsoletes", + "obsoletes-dist": "obsoletes_dist", + "platform": "platforms", + "project-url": "project_urls", + "provides": "provides", + "provides-dist": "provides_dist", + "provides-extra": "provides_extra", + "requires": "requires", + "requires-dist": "requires_dist", + "requires-external": "requires_external", + "requires-python": "requires_python", + "summary": "summary", + "supported-platform": "supported_platforms", + "version": "version", +} +_RAW_TO_EMAIL_MAPPING = {raw: email for email, raw in _EMAIL_TO_RAW_MAPPING.items()} + + +def parse_email(data: Union[bytes, str]) -> Tuple[RawMetadata, Dict[str, List[str]]]: + """Parse a distribution's metadata stored as email headers (e.g. from ``METADATA``). + + This function returns a two-item tuple of dicts. The first dict is of + recognized fields from the core metadata specification. Fields that can be + parsed and translated into Python's built-in types are converted + appropriately. All other fields are left as-is. Fields that are allowed to + appear multiple times are stored as lists. + + The second dict contains all other fields from the metadata. This includes + any unrecognized fields. It also includes any fields which are expected to + be parsed into a built-in type but were not formatted appropriately. Finally, + any fields that are expected to appear only once but are repeated are + included in this dict. + + """ + raw: Dict[str, Union[str, List[str], Dict[str, str]]] = {} + unparsed: Dict[str, List[str]] = {} + + if isinstance(data, str): + parsed = email.parser.Parser(policy=email.policy.compat32).parsestr(data) + else: + parsed = email.parser.BytesParser(policy=email.policy.compat32).parsebytes(data) + + # We have to wrap parsed.keys() in a set, because in the case of multiple + # values for a key (a list), the key will appear multiple times in the + # list of keys, but we're avoiding that by using get_all(). + for name in frozenset(parsed.keys()): + # Header names in RFC are case insensitive, so we'll normalize to all + # lower case to make comparisons easier. + name = name.lower() + + # We use get_all() here, even for fields that aren't multiple use, + # because otherwise someone could have e.g. two Name fields, and we + # would just silently ignore it rather than doing something about it. + headers = parsed.get_all(name) or [] + + # The way the email module works when parsing bytes is that it + # unconditionally decodes the bytes as ascii using the surrogateescape + # handler. When you pull that data back out (such as with get_all() ), + # it looks to see if the str has any surrogate escapes, and if it does + # it wraps it in a Header object instead of returning the string. + # + # As such, we'll look for those Header objects, and fix up the encoding. + value = [] + # Flag if we have run into any issues processing the headers, thus + # signalling that the data belongs in 'unparsed'. + valid_encoding = True + for h in headers: + # It's unclear if this can return more types than just a Header or + # a str, so we'll just assert here to make sure. + assert isinstance(h, (email.header.Header, str)) + + # If it's a header object, we need to do our little dance to get + # the real data out of it. In cases where there is invalid data + # we're going to end up with mojibake, but there's no obvious, good + # way around that without reimplementing parts of the Header object + # ourselves. + # + # That should be fine since, if mojibacked happens, this key is + # going into the unparsed dict anyways. + if isinstance(h, email.header.Header): + # The Header object stores it's data as chunks, and each chunk + # can be independently encoded, so we'll need to check each + # of them. + chunks: List[Tuple[bytes, Optional[str]]] = [] + for bin, encoding in email.header.decode_header(h): + try: + bin.decode("utf8", "strict") + except UnicodeDecodeError: + # Enable mojibake. + encoding = "latin1" + valid_encoding = False + else: + encoding = "utf8" + chunks.append((bin, encoding)) + + # Turn our chunks back into a Header object, then let that + # Header object do the right thing to turn them into a + # string for us. + value.append(str(email.header.make_header(chunks))) + # This is already a string, so just add it. + else: + value.append(h) + + # We've processed all of our values to get them into a list of str, + # but we may have mojibake data, in which case this is an unparsed + # field. + if not valid_encoding: + unparsed[name] = value + continue + + raw_name = _EMAIL_TO_RAW_MAPPING.get(name) + if raw_name is None: + # This is a bit of a weird situation, we've encountered a key that + # we don't know what it means, so we don't know whether it's meant + # to be a list or not. + # + # Since we can't really tell one way or another, we'll just leave it + # as a list, even though it may be a single item list, because that's + # what makes the most sense for email headers. + unparsed[name] = value + continue + + # If this is one of our string fields, then we'll check to see if our + # value is a list of a single item. If it is then we'll assume that + # it was emitted as a single string, and unwrap the str from inside + # the list. + # + # If it's any other kind of data, then we haven't the faintest clue + # what we should parse it as, and we have to just add it to our list + # of unparsed stuff. + if raw_name in _STRING_FIELDS and len(value) == 1: + raw[raw_name] = value[0] + # If this is one of our list of string fields, then we can just assign + # the value, since email *only* has strings, and our get_all() call + # above ensures that this is a list. + elif raw_name in _LIST_FIELDS: + raw[raw_name] = value + # Special Case: Keywords + # The keywords field is implemented in the metadata spec as a str, + # but it conceptually is a list of strings, and is serialized using + # ", ".join(keywords), so we'll do some light data massaging to turn + # this into what it logically is. + elif raw_name == "keywords" and len(value) == 1: + raw[raw_name] = _parse_keywords(value[0]) + # Special Case: Project-URL + # The project urls is implemented in the metadata spec as a list of + # specially-formatted strings that represent a key and a value, which + # is fundamentally a mapping, however the email format doesn't support + # mappings in a sane way, so it was crammed into a list of strings + # instead. + # + # We will do a little light data massaging to turn this into a map as + # it logically should be. + elif raw_name == "project_urls": + try: + raw[raw_name] = _parse_project_urls(value) + except KeyError: + unparsed[name] = value + # Nothing that we've done has managed to parse this, so it'll just + # throw it in our unparseable data and move on. + else: + unparsed[name] = value + + # We need to support getting the Description from the message payload in + # addition to getting it from the the headers. This does mean, though, there + # is the possibility of it being set both ways, in which case we put both + # in 'unparsed' since we don't know which is right. + try: + payload = _get_payload(parsed, data) + except ValueError: + unparsed.setdefault("description", []).append( + parsed.get_payload(decode=isinstance(data, bytes)) + ) + else: + if payload: + # Check to see if we've already got a description, if so then both + # it, and this body move to unparseable. + if "description" in raw: + description_header = cast(str, raw.pop("description")) + unparsed.setdefault("description", []).extend( + [description_header, payload] + ) + elif "description" in unparsed: + unparsed["description"].append(payload) + else: + raw["description"] = payload + + # We need to cast our `raw` to a metadata, because a TypedDict only support + # literal key names, but we're computing our key names on purpose, but the + # way this function is implemented, our `TypedDict` can only have valid key + # names. + return cast(RawMetadata, raw), unparsed + + +_NOT_FOUND = object() + + +# Keep the two values in sync. +_VALID_METADATA_VERSIONS = ["1.0", "1.1", "1.2", "2.1", "2.2", "2.3"] +_MetadataVersion = Literal["1.0", "1.1", "1.2", "2.1", "2.2", "2.3"] + +_REQUIRED_ATTRS = frozenset(["metadata_version", "name", "version"]) + + +class _Validator(Generic[T]): + """Validate a metadata field. + + All _process_*() methods correspond to a core metadata field. The method is + called with the field's raw value. If the raw value is valid it is returned + in its "enriched" form (e.g. ``version.Version`` for the ``Version`` field). + If the raw value is invalid, :exc:`InvalidMetadata` is raised (with a cause + as appropriate). + """ + + name: str + raw_name: str + added: _MetadataVersion + + def __init__( + self, + *, + added: _MetadataVersion = "1.0", + ) -> None: + self.added = added + + def __set_name__(self, _owner: "Metadata", name: str) -> None: + self.name = name + self.raw_name = _RAW_TO_EMAIL_MAPPING[name] + + def __get__(self, instance: "Metadata", _owner: Type["Metadata"]) -> T: + # With Python 3.8, the caching can be replaced with functools.cached_property(). + # No need to check the cache as attribute lookup will resolve into the + # instance's __dict__ before __get__ is called. + cache = instance.__dict__ + value = instance._raw.get(self.name) + + # To make the _process_* methods easier, we'll check if the value is None + # and if this field is NOT a required attribute, and if both of those + # things are true, we'll skip the the converter. This will mean that the + # converters never have to deal with the None union. + if self.name in _REQUIRED_ATTRS or value is not None: + try: + converter: Callable[[Any], T] = getattr(self, f"_process_{self.name}") + except AttributeError: + pass + else: + value = converter(value) + + cache[self.name] = value + try: + del instance._raw[self.name] # type: ignore[misc] + except KeyError: + pass + + return cast(T, value) + + def _invalid_metadata( + self, msg: str, cause: Optional[Exception] = None + ) -> InvalidMetadata: + exc = InvalidMetadata( + self.raw_name, msg.format_map({"field": repr(self.raw_name)}) + ) + exc.__cause__ = cause + return exc + + def _process_metadata_version(self, value: str) -> _MetadataVersion: + # Implicitly makes Metadata-Version required. + if value not in _VALID_METADATA_VERSIONS: + raise self._invalid_metadata(f"{value!r} is not a valid metadata version") + return cast(_MetadataVersion, value) + + def _process_name(self, value: str) -> str: + if not value: + raise self._invalid_metadata("{field} is a required field") + # Validate the name as a side-effect. + try: + utils.canonicalize_name(value, validate=True) + except utils.InvalidName as exc: + raise self._invalid_metadata( + f"{value!r} is invalid for {{field}}", cause=exc + ) + else: + return value + + def _process_version(self, value: str) -> version_module.Version: + if not value: + raise self._invalid_metadata("{field} is a required field") + try: + return version_module.parse(value) + except version_module.InvalidVersion as exc: + raise self._invalid_metadata( + f"{value!r} is invalid for {{field}}", cause=exc + ) + + def _process_summary(self, value: str) -> str: + """Check the field contains no newlines.""" + if "\n" in value: + raise self._invalid_metadata("{field} must be a single line") + return value + + def _process_description_content_type(self, value: str) -> str: + content_types = {"text/plain", "text/x-rst", "text/markdown"} + message = email.message.EmailMessage() + message["content-type"] = value + + content_type, parameters = ( + # Defaults to `text/plain` if parsing failed. + message.get_content_type().lower(), + message["content-type"].params, + ) + # Check if content-type is valid or defaulted to `text/plain` and thus was + # not parseable. + if content_type not in content_types or content_type not in value.lower(): + raise self._invalid_metadata( + f"{{field}} must be one of {list(content_types)}, not {value!r}" + ) + + charset = parameters.get("charset", "UTF-8") + if charset != "UTF-8": + raise self._invalid_metadata( + f"{{field}} can only specify the UTF-8 charset, not {list(charset)}" + ) + + markdown_variants = {"GFM", "CommonMark"} + variant = parameters.get("variant", "GFM") # Use an acceptable default. + if content_type == "text/markdown" and variant not in markdown_variants: + raise self._invalid_metadata( + f"valid Markdown variants for {{field}} are {list(markdown_variants)}, " + f"not {variant!r}", + ) + return value + + def _process_dynamic(self, value: List[str]) -> List[str]: + for dynamic_field in map(str.lower, value): + if dynamic_field in {"name", "version", "metadata-version"}: + raise self._invalid_metadata( + f"{value!r} is not allowed as a dynamic field" + ) + elif dynamic_field not in _EMAIL_TO_RAW_MAPPING: + raise self._invalid_metadata(f"{value!r} is not a valid dynamic field") + return list(map(str.lower, value)) + + def _process_provides_extra( + self, + value: List[str], + ) -> List[utils.NormalizedName]: + normalized_names = [] + try: + for name in value: + normalized_names.append(utils.canonicalize_name(name, validate=True)) + except utils.InvalidName as exc: + raise self._invalid_metadata( + f"{name!r} is invalid for {{field}}", cause=exc + ) + else: + return normalized_names + + def _process_requires_python(self, value: str) -> specifiers.SpecifierSet: + try: + return specifiers.SpecifierSet(value) + except specifiers.InvalidSpecifier as exc: + raise self._invalid_metadata( + f"{value!r} is invalid for {{field}}", cause=exc + ) + + def _process_requires_dist( + self, + value: List[str], + ) -> List[requirements.Requirement]: + reqs = [] + try: + for req in value: + reqs.append(requirements.Requirement(req)) + except requirements.InvalidRequirement as exc: + raise self._invalid_metadata(f"{req!r} is invalid for {{field}}", cause=exc) + else: + return reqs + + +class Metadata: + """Representation of distribution metadata. + + Compared to :class:`RawMetadata`, this class provides objects representing + metadata fields instead of only using built-in types. Any invalid metadata + will cause :exc:`InvalidMetadata` to be raised (with a + :py:attr:`~BaseException.__cause__` attribute as appropriate). + """ + + _raw: RawMetadata + + @classmethod + def from_raw(cls, data: RawMetadata, *, validate: bool = True) -> "Metadata": + """Create an instance from :class:`RawMetadata`. + + If *validate* is true, all metadata will be validated. All exceptions + related to validation will be gathered and raised as an :class:`ExceptionGroup`. + """ + ins = cls() + ins._raw = data.copy() # Mutations occur due to caching enriched values. + + if validate: + exceptions: List[Exception] = [] + try: + metadata_version = ins.metadata_version + metadata_age = _VALID_METADATA_VERSIONS.index(metadata_version) + except InvalidMetadata as metadata_version_exc: + exceptions.append(metadata_version_exc) + metadata_version = None + + # Make sure to check for the fields that are present, the required + # fields (so their absence can be reported). + fields_to_check = frozenset(ins._raw) | _REQUIRED_ATTRS + # Remove fields that have already been checked. + fields_to_check -= {"metadata_version"} + + for key in fields_to_check: + try: + if metadata_version: + # Can't use getattr() as that triggers descriptor protocol which + # will fail due to no value for the instance argument. + try: + field_metadata_version = cls.__dict__[key].added + except KeyError: + exc = InvalidMetadata(key, f"unrecognized field: {key!r}") + exceptions.append(exc) + continue + field_age = _VALID_METADATA_VERSIONS.index( + field_metadata_version + ) + if field_age > metadata_age: + field = _RAW_TO_EMAIL_MAPPING[key] + exc = InvalidMetadata( + field, + "{field} introduced in metadata version " + "{field_metadata_version}, not {metadata_version}", + ) + exceptions.append(exc) + continue + getattr(ins, key) + except InvalidMetadata as exc: + exceptions.append(exc) + + if exceptions: + raise ExceptionGroup("invalid metadata", exceptions) + + return ins + + @classmethod + def from_email( + cls, data: Union[bytes, str], *, validate: bool = True + ) -> "Metadata": + """Parse metadata from email headers. + + If *validate* is true, the metadata will be validated. All exceptions + related to validation will be gathered and raised as an :class:`ExceptionGroup`. + """ + raw, unparsed = parse_email(data) + + if validate: + exceptions: list[Exception] = [] + for unparsed_key in unparsed: + if unparsed_key in _EMAIL_TO_RAW_MAPPING: + message = f"{unparsed_key!r} has invalid data" + else: + message = f"unrecognized field: {unparsed_key!r}" + exceptions.append(InvalidMetadata(unparsed_key, message)) + + if exceptions: + raise ExceptionGroup("unparsed", exceptions) + + try: + return cls.from_raw(raw, validate=validate) + except ExceptionGroup as exc_group: + raise ExceptionGroup( + "invalid or unparsed metadata", exc_group.exceptions + ) from None + + metadata_version: _Validator[_MetadataVersion] = _Validator() + """:external:ref:`core-metadata-metadata-version` + (required; validated to be a valid metadata version)""" + name: _Validator[str] = _Validator() + """:external:ref:`core-metadata-name` + (required; validated using :func:`~packaging.utils.canonicalize_name` and its + *validate* parameter)""" + version: _Validator[version_module.Version] = _Validator() + """:external:ref:`core-metadata-version` (required)""" + dynamic: _Validator[Optional[List[str]]] = _Validator( + added="2.2", + ) + """:external:ref:`core-metadata-dynamic` + (validated against core metadata field names and lowercased)""" + platforms: _Validator[Optional[List[str]]] = _Validator() + """:external:ref:`core-metadata-platform`""" + supported_platforms: _Validator[Optional[List[str]]] = _Validator(added="1.1") + """:external:ref:`core-metadata-supported-platform`""" + summary: _Validator[Optional[str]] = _Validator() + """:external:ref:`core-metadata-summary` (validated to contain no newlines)""" + description: _Validator[Optional[str]] = _Validator() # TODO 2.1: can be in body + """:external:ref:`core-metadata-description`""" + description_content_type: _Validator[Optional[str]] = _Validator(added="2.1") + """:external:ref:`core-metadata-description-content-type` (validated)""" + keywords: _Validator[Optional[List[str]]] = _Validator() + """:external:ref:`core-metadata-keywords`""" + home_page: _Validator[Optional[str]] = _Validator() + """:external:ref:`core-metadata-home-page`""" + download_url: _Validator[Optional[str]] = _Validator(added="1.1") + """:external:ref:`core-metadata-download-url`""" + author: _Validator[Optional[str]] = _Validator() + """:external:ref:`core-metadata-author`""" + author_email: _Validator[Optional[str]] = _Validator() + """:external:ref:`core-metadata-author-email`""" + maintainer: _Validator[Optional[str]] = _Validator(added="1.2") + """:external:ref:`core-metadata-maintainer`""" + maintainer_email: _Validator[Optional[str]] = _Validator(added="1.2") + """:external:ref:`core-metadata-maintainer-email`""" + license: _Validator[Optional[str]] = _Validator() + """:external:ref:`core-metadata-license`""" + classifiers: _Validator[Optional[List[str]]] = _Validator(added="1.1") + """:external:ref:`core-metadata-classifier`""" + requires_dist: _Validator[Optional[List[requirements.Requirement]]] = _Validator( + added="1.2" + ) + """:external:ref:`core-metadata-requires-dist`""" + requires_python: _Validator[Optional[specifiers.SpecifierSet]] = _Validator( + added="1.2" + ) + """:external:ref:`core-metadata-requires-python`""" + # Because `Requires-External` allows for non-PEP 440 version specifiers, we + # don't do any processing on the values. + requires_external: _Validator[Optional[List[str]]] = _Validator(added="1.2") + """:external:ref:`core-metadata-requires-external`""" + project_urls: _Validator[Optional[Dict[str, str]]] = _Validator(added="1.2") + """:external:ref:`core-metadata-project-url`""" + # PEP 685 lets us raise an error if an extra doesn't pass `Name` validation + # regardless of metadata version. + provides_extra: _Validator[Optional[List[utils.NormalizedName]]] = _Validator( + added="2.1", + ) + """:external:ref:`core-metadata-provides-extra`""" + provides_dist: _Validator[Optional[List[str]]] = _Validator(added="1.2") + """:external:ref:`core-metadata-provides-dist`""" + obsoletes_dist: _Validator[Optional[List[str]]] = _Validator(added="1.2") + """:external:ref:`core-metadata-obsoletes-dist`""" + requires: _Validator[Optional[List[str]]] = _Validator(added="1.1") + """``Requires`` (deprecated)""" + provides: _Validator[Optional[List[str]]] = _Validator(added="1.1") + """``Provides`` (deprecated)""" + obsoletes: _Validator[Optional[List[str]]] = _Validator(added="1.1") + """``Obsoletes`` (deprecated)""" diff --git a/venv/Lib/site-packages/pkg_resources/_vendor/packaging/py.typed b/venv/Lib/site-packages/pkg_resources/_vendor/packaging/py.typed new file mode 100644 index 0000000..e69de29 diff --git a/venv/Lib/site-packages/pkg_resources/_vendor/packaging/requirements.py b/venv/Lib/site-packages/pkg_resources/_vendor/packaging/requirements.py new file mode 100644 index 0000000..bdc43a7 --- /dev/null +++ b/venv/Lib/site-packages/pkg_resources/_vendor/packaging/requirements.py @@ -0,0 +1,90 @@ +# 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 typing import Any, Iterator, Optional, Set + +from ._parser import parse_requirement as _parse_requirement +from ._tokenizer import ParserSyntaxError +from .markers import Marker, _normalize_extra_values +from .specifiers import SpecifierSet +from .utils import canonicalize_name + + +class InvalidRequirement(ValueError): + """ + An invalid requirement was found, users should refer to PEP 508. + """ + + +class Requirement: + """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: str) -> None: + try: + parsed = _parse_requirement(requirement_string) + except ParserSyntaxError as e: + raise InvalidRequirement(str(e)) from e + + self.name: str = parsed.name + self.url: Optional[str] = parsed.url or None + self.extras: Set[str] = set(parsed.extras or []) + self.specifier: SpecifierSet = SpecifierSet(parsed.specifier) + self.marker: Optional[Marker] = None + if parsed.marker is not None: + self.marker = Marker.__new__(Marker) + self.marker._markers = _normalize_extra_values(parsed.marker) + + def _iter_parts(self, name: str) -> Iterator[str]: + yield name + + if self.extras: + formatted_extras = ",".join(sorted(self.extras)) + yield f"[{formatted_extras}]" + + if self.specifier: + yield str(self.specifier) + + if self.url: + yield f"@ {self.url}" + if self.marker: + yield " " + + if self.marker: + yield f"; {self.marker}" + + def __str__(self) -> str: + return "".join(self._iter_parts(self.name)) + + def __repr__(self) -> str: + return f"" + + def __hash__(self) -> int: + return hash( + ( + self.__class__.__name__, + *self._iter_parts(canonicalize_name(self.name)), + ) + ) + + def __eq__(self, other: Any) -> bool: + if not isinstance(other, Requirement): + return NotImplemented + + return ( + canonicalize_name(self.name) == canonicalize_name(other.name) + and self.extras == other.extras + and self.specifier == other.specifier + and self.url == other.url + and self.marker == other.marker + ) diff --git a/venv/Lib/site-packages/pkg_resources/_vendor/packaging/specifiers.py b/venv/Lib/site-packages/pkg_resources/_vendor/packaging/specifiers.py new file mode 100644 index 0000000..2d015ba --- /dev/null +++ b/venv/Lib/site-packages/pkg_resources/_vendor/packaging/specifiers.py @@ -0,0 +1,1017 @@ +# 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. +""" +.. testsetup:: + + from packaging.specifiers import Specifier, SpecifierSet, InvalidSpecifier + from packaging.version import Version +""" + +import abc +import itertools +import re +from typing import Callable, Iterable, Iterator, List, Optional, Tuple, TypeVar, Union + +from .utils import canonicalize_version +from .version import Version + +UnparsedVersion = Union[Version, str] +UnparsedVersionVar = TypeVar("UnparsedVersionVar", bound=UnparsedVersion) +CallableOperator = Callable[[Version, str], bool] + + +def _coerce_version(version: UnparsedVersion) -> Version: + if not isinstance(version, Version): + version = Version(version) + return version + + +class InvalidSpecifier(ValueError): + """ + Raised when attempting to create a :class:`Specifier` with a specifier + string that is invalid. + + >>> Specifier("lolwat") + Traceback (most recent call last): + ... + packaging.specifiers.InvalidSpecifier: Invalid specifier: 'lolwat' + """ + + +class BaseSpecifier(metaclass=abc.ABCMeta): + @abc.abstractmethod + def __str__(self) -> str: + """ + Returns the str representation of this Specifier-like object. This + should be representative of the Specifier itself. + """ + + @abc.abstractmethod + def __hash__(self) -> int: + """ + Returns a hash value for this Specifier-like object. + """ + + @abc.abstractmethod + def __eq__(self, other: object) -> bool: + """ + Returns a boolean representing whether or not the two Specifier-like + objects are equal. + + :param other: The other object to check against. + """ + + @property + @abc.abstractmethod + def prereleases(self) -> Optional[bool]: + """Whether or not pre-releases as a whole are allowed. + + This can be set to either ``True`` or ``False`` to explicitly enable or disable + prereleases or it can be set to ``None`` (the default) to use default semantics. + """ + + @prereleases.setter + def prereleases(self, value: bool) -> None: + """Setter for :attr:`prereleases`. + + :param value: The value to set. + """ + + @abc.abstractmethod + def contains(self, item: str, prereleases: Optional[bool] = None) -> bool: + """ + Determines if the given item is contained within this specifier. + """ + + @abc.abstractmethod + def filter( + self, iterable: Iterable[UnparsedVersionVar], prereleases: Optional[bool] = None + ) -> Iterator[UnparsedVersionVar]: + """ + Takes an iterable of items and filters them so that only items which + are contained within this specifier are allowed in it. + """ + + +class Specifier(BaseSpecifier): + """This class abstracts handling of version specifiers. + + .. tip:: + + It is generally not required to instantiate this manually. You should instead + prefer to work with :class:`SpecifierSet` instead, which can parse + comma-separated version specifiers (which is what package metadata contains). + """ + + _operator_regex_str = r""" + (?P(~=|==|!=|<=|>=|<|>|===)) + """ + _version_regex_str = r""" + (?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;)]* # The arbitrary version can be just about anything, + # we match everything except for whitespace, a + # semi-colon for marker support, and a closing paren + # since versions can be enclosed in them. + ) + | + (?: + # 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 + + # You cannot use a wild card and a pre-release, post-release, a dev or + # local version together so group them with a | and make them optional. + (?: + \.\* # Wild card syntax of .* + | + (?: # pre release + [-_\.]? + (alpha|beta|preview|pre|a|b|c|rc) + [-_\.]? + [0-9]* + )? + (?: # post release + (?:-[0-9]+)|(?:[-_\.]?(post|rev|r)[-_\.]?[0-9]*) + )? + (?:[-_\.]?dev[-_\.]?[0-9]*)? # dev release + (?:\+[a-z0-9]+(?:[-_\.][a-z0-9]+)*)? # local + )? + ) + | + (?: + # 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 + [-_\.]? + (alpha|beta|preview|pre|a|b|c|rc) + [-_\.]? + [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", + } + + def __init__(self, spec: str = "", prereleases: Optional[bool] = None) -> None: + """Initialize a Specifier instance. + + :param spec: + The string representation of a specifier which will be parsed and + normalized before use. + :param prereleases: + This tells the specifier if it should accept prerelease versions if + applicable or not. The default of ``None`` will autodetect it from the + given specifiers. + :raises InvalidSpecifier: + If the given specifier is invalid (i.e. bad syntax). + """ + match = self._regex.search(spec) + if not match: + raise InvalidSpecifier(f"Invalid specifier: '{spec}'") + + self._spec: Tuple[str, str] = ( + match.group("operator").strip(), + match.group("version").strip(), + ) + + # Store whether or not this Specifier should accept prereleases + self._prereleases = prereleases + + # https://github.com/python/mypy/pull/13475#pullrequestreview-1079784515 + @property # type: ignore[override] + def prereleases(self) -> 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 Version(version).is_prerelease: + return True + + return False + + @prereleases.setter + def prereleases(self, value: bool) -> None: + self._prereleases = value + + @property + def operator(self) -> str: + """The operator of this specifier. + + >>> Specifier("==1.2.3").operator + '==' + """ + return self._spec[0] + + @property + def version(self) -> str: + """The version of this specifier. + + >>> Specifier("==1.2.3").version + '1.2.3' + """ + return self._spec[1] + + def __repr__(self) -> str: + """A representation of the Specifier that shows all internal state. + + >>> Specifier('>=1.0.0') + =1.0.0')> + >>> Specifier('>=1.0.0', prereleases=False) + =1.0.0', prereleases=False)> + >>> Specifier('>=1.0.0', prereleases=True) + =1.0.0', prereleases=True)> + """ + pre = ( + f", prereleases={self.prereleases!r}" + if self._prereleases is not None + else "" + ) + + return f"<{self.__class__.__name__}({str(self)!r}{pre})>" + + def __str__(self) -> str: + """A string representation of the Specifier that can be round-tripped. + + >>> str(Specifier('>=1.0.0')) + '>=1.0.0' + >>> str(Specifier('>=1.0.0', prereleases=False)) + '>=1.0.0' + """ + return "{}{}".format(*self._spec) + + @property + def _canonical_spec(self) -> Tuple[str, str]: + canonical_version = canonicalize_version( + self._spec[1], + strip_trailing_zero=(self._spec[0] != "~="), + ) + return self._spec[0], canonical_version + + def __hash__(self) -> int: + return hash(self._canonical_spec) + + def __eq__(self, other: object) -> bool: + """Whether or not the two Specifier-like objects are equal. + + :param other: The other object to check against. + + The value of :attr:`prereleases` is ignored. + + >>> Specifier("==1.2.3") == Specifier("== 1.2.3.0") + True + >>> (Specifier("==1.2.3", prereleases=False) == + ... Specifier("==1.2.3", prereleases=True)) + True + >>> Specifier("==1.2.3") == "==1.2.3" + True + >>> Specifier("==1.2.3") == Specifier("==1.2.4") + False + >>> Specifier("==1.2.3") == Specifier("~=1.2.3") + False + """ + if isinstance(other, str): + try: + other = self.__class__(str(other)) + except InvalidSpecifier: + return NotImplemented + elif not isinstance(other, self.__class__): + return NotImplemented + + return self._canonical_spec == other._canonical_spec + + def _get_operator(self, op: str) -> CallableOperator: + operator_callable: CallableOperator = getattr( + self, f"_compare_{self._operators[op]}" + ) + return operator_callable + + def _compare_compatible(self, prospective: Version, spec: 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 suffix segments. + prefix = _version_join( + list(itertools.takewhile(_is_not_suffix, _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 + ) + + def _compare_equal(self, prospective: Version, spec: 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. + normalized_prospective = canonicalize_version( + prospective.public, strip_trailing_zero=False + ) + # Get the normalized version string ignoring the trailing .* + normalized_spec = canonicalize_version(spec[:-2], strip_trailing_zero=False) + # Split the spec out by bangs and dots, and pretend that there is + # an implicit dot in between a release segment and a pre-release segment. + split_spec = _version_split(normalized_spec) + + # Split the prospective version out by bangs and dots, and pretend + # that there is an implicit dot in between a release segment and + # a pre-release segment. + split_prospective = _version_split(normalized_prospective) + + # 0-pad the prospective version before shortening it to get the correct + # shortened version. + padded_prospective, _ = _pad_version(split_prospective, split_spec) + + # 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 = padded_prospective[: len(split_spec)] + + return shortened_prospective == split_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 + + def _compare_not_equal(self, prospective: Version, spec: str) -> bool: + return not self._compare_equal(prospective, spec) + + def _compare_less_than_equal(self, prospective: Version, spec: str) -> bool: + + # NB: Local version identifiers are NOT permitted in the version + # specifier, so local version labels can be universally removed from + # the prospective version. + return Version(prospective.public) <= Version(spec) + + def _compare_greater_than_equal(self, prospective: Version, spec: str) -> bool: + + # NB: Local version identifiers are NOT permitted in the version + # specifier, so local version labels can be universally removed from + # the prospective version. + return Version(prospective.public) >= Version(spec) + + def _compare_less_than(self, prospective: Version, spec_str: 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 + + def _compare_greater_than(self, prospective: Version, spec_str: 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: Version, spec: str) -> bool: + return str(prospective).lower() == str(spec).lower() + + def __contains__(self, item: Union[str, Version]) -> bool: + """Return whether or not the item is contained in this specifier. + + :param item: The item to check for. + + This is used for the ``in`` operator and behaves the same as + :meth:`contains` with no ``prereleases`` argument passed. + + >>> "1.2.3" in Specifier(">=1.2.3") + True + >>> Version("1.2.3") in Specifier(">=1.2.3") + True + >>> "1.0.0" in Specifier(">=1.2.3") + False + >>> "1.3.0a1" in Specifier(">=1.2.3") + False + >>> "1.3.0a1" in Specifier(">=1.2.3", prereleases=True) + True + """ + return self.contains(item) + + def contains( + self, item: UnparsedVersion, prereleases: Optional[bool] = None + ) -> bool: + """Return whether or not the item is contained in this specifier. + + :param item: + The item to check for, which can be a version string or a + :class:`Version` instance. + :param prereleases: + Whether or not to match prereleases with this Specifier. If set to + ``None`` (the default), it uses :attr:`prereleases` to determine + whether or not prereleases are allowed. + + >>> Specifier(">=1.2.3").contains("1.2.3") + True + >>> Specifier(">=1.2.3").contains(Version("1.2.3")) + True + >>> Specifier(">=1.2.3").contains("1.0.0") + False + >>> Specifier(">=1.2.3").contains("1.3.0a1") + False + >>> Specifier(">=1.2.3", prereleases=True).contains("1.3.0a1") + True + >>> Specifier(">=1.2.3").contains("1.3.0a1", prereleases=True) + True + """ + + # Determine if prereleases are to be allowed or not. + if prereleases is None: + prereleases = self.prereleases + + # Normalize item to a Version, this allows us to have a shortcut for + # "2.0" in Specifier(">=2") + normalized_item = _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: CallableOperator = self._get_operator(self.operator) + return operator_callable(normalized_item, self.version) + + def filter( + self, iterable: Iterable[UnparsedVersionVar], prereleases: Optional[bool] = None + ) -> Iterator[UnparsedVersionVar]: + """Filter items in the given iterable, that match the specifier. + + :param iterable: + An iterable that can contain version strings and :class:`Version` instances. + The items in the iterable will be filtered according to the specifier. + :param prereleases: + Whether or not to allow prereleases in the returned iterator. If set to + ``None`` (the default), it will be intelligently decide whether to allow + prereleases or not (based on the :attr:`prereleases` attribute, and + whether the only versions matching are prereleases). + + This method is smarter than just ``filter(Specifier().contains, [...])`` + because it implements the rule from :pep:`440` that a prerelease item + SHOULD be accepted if no other versions match the given specifier. + + >>> list(Specifier(">=1.2.3").filter(["1.2", "1.3", "1.5a1"])) + ['1.3'] + >>> list(Specifier(">=1.2.3").filter(["1.2", "1.2.3", "1.3", Version("1.4")])) + ['1.2.3', '1.3', ] + >>> list(Specifier(">=1.2.3").filter(["1.2", "1.5a1"])) + ['1.5a1'] + >>> list(Specifier(">=1.2.3").filter(["1.3", "1.5a1"], prereleases=True)) + ['1.3', '1.5a1'] + >>> list(Specifier(">=1.2.3", prereleases=True).filter(["1.3", "1.5a1"])) + ['1.3', '1.5a1'] + """ + + 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 = _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 in case 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 + + +_prefix_regex = re.compile(r"^([0-9]+)((?:a|b|c|rc)[0-9]+)$") + + +def _version_split(version: str) -> List[str]: + """Split version into components. + + The split components are intended for version comparison. The logic does + not attempt to retain the original version string, so joining the + components back with :func:`_version_join` may not produce the original + version string. + """ + result: List[str] = [] + + epoch, _, rest = version.rpartition("!") + result.append(epoch or "0") + + for item in rest.split("."): + match = _prefix_regex.search(item) + if match: + result.extend(match.groups()) + else: + result.append(item) + return result + + +def _version_join(components: List[str]) -> str: + """Join split version components into a version string. + + This function assumes the input came from :func:`_version_split`, where the + first component must be the epoch (either empty or numeric), and all other + components numeric. + """ + epoch, *rest = components + return f"{epoch}!{'.'.join(rest)}" + + +def _is_not_suffix(segment: str) -> bool: + return not any( + segment.startswith(prefix) for prefix in ("dev", "a", "b", "rc", "post") + ) + + +def _pad_version(left: List[str], right: 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.from_iterable(left_split)), + list(itertools.chain.from_iterable(right_split)), + ) + + +class SpecifierSet(BaseSpecifier): + """This class abstracts handling of a set of version specifiers. + + It can be passed a single specifier (``>=3.0``), a comma-separated list of + specifiers (``>=3.0,!=3.1``), or no specifier at all. + """ + + def __init__( + self, specifiers: str = "", prereleases: Optional[bool] = None + ) -> None: + """Initialize a SpecifierSet instance. + + :param specifiers: + The string representation of a specifier or a comma-separated list of + specifiers which will be parsed and normalized before use. + :param prereleases: + This tells the SpecifierSet if it should accept prerelease versions if + applicable or not. The default of ``None`` will autodetect it from the + given specifiers. + + :raises InvalidSpecifier: + If the given ``specifiers`` are not parseable than this exception will be + raised. + """ + + # 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()] + + # Make each individual specifier a Specifier and save in a frozen set for later. + self._specs = frozenset(map(Specifier, split_specifiers)) + + # Store our prereleases value so we can use it later to determine if + # we accept prereleases or not. + self._prereleases = prereleases + + @property + def prereleases(self) -> 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: bool) -> None: + self._prereleases = value + + def __repr__(self) -> str: + """A representation of the specifier set that shows all internal state. + + Note that the ordering of the individual specifiers within the set may not + match the input string. + + >>> SpecifierSet('>=1.0.0,!=2.0.0') + =1.0.0')> + >>> SpecifierSet('>=1.0.0,!=2.0.0', prereleases=False) + =1.0.0', prereleases=False)> + >>> SpecifierSet('>=1.0.0,!=2.0.0', prereleases=True) + =1.0.0', prereleases=True)> + """ + pre = ( + f", prereleases={self.prereleases!r}" + if self._prereleases is not None + else "" + ) + + return f"" + + def __str__(self) -> str: + """A string representation of the specifier set that can be round-tripped. + + Note that the ordering of the individual specifiers within the set may not + match the input string. + + >>> str(SpecifierSet(">=1.0.0,!=1.0.1")) + '!=1.0.1,>=1.0.0' + >>> str(SpecifierSet(">=1.0.0,!=1.0.1", prereleases=False)) + '!=1.0.1,>=1.0.0' + """ + return ",".join(sorted(str(s) for s in self._specs)) + + def __hash__(self) -> int: + return hash(self._specs) + + def __and__(self, other: Union["SpecifierSet", str]) -> "SpecifierSet": + """Return a SpecifierSet which is a combination of the two sets. + + :param other: The other object to combine with. + + >>> SpecifierSet(">=1.0.0,!=1.0.1") & '<=2.0.0,!=2.0.1' + =1.0.0')> + >>> SpecifierSet(">=1.0.0,!=1.0.1") & SpecifierSet('<=2.0.0,!=2.0.1') + =1.0.0')> + """ + if isinstance(other, str): + 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: object) -> bool: + """Whether or not the two SpecifierSet-like objects are equal. + + :param other: The other object to check against. + + The value of :attr:`prereleases` is ignored. + + >>> SpecifierSet(">=1.0.0,!=1.0.1") == SpecifierSet(">=1.0.0,!=1.0.1") + True + >>> (SpecifierSet(">=1.0.0,!=1.0.1", prereleases=False) == + ... SpecifierSet(">=1.0.0,!=1.0.1", prereleases=True)) + True + >>> SpecifierSet(">=1.0.0,!=1.0.1") == ">=1.0.0,!=1.0.1" + True + >>> SpecifierSet(">=1.0.0,!=1.0.1") == SpecifierSet(">=1.0.0") + False + >>> SpecifierSet(">=1.0.0,!=1.0.1") == SpecifierSet(">=1.0.0,!=1.0.2") + False + """ + if isinstance(other, (str, Specifier)): + other = SpecifierSet(str(other)) + elif not isinstance(other, SpecifierSet): + return NotImplemented + + return self._specs == other._specs + + def __len__(self) -> int: + """Returns the number of specifiers in this specifier set.""" + return len(self._specs) + + def __iter__(self) -> Iterator[Specifier]: + """ + Returns an iterator over all the underlying :class:`Specifier` instances + in this specifier set. + + >>> sorted(SpecifierSet(">=1.0.0,!=1.0.1"), key=str) + [, =1.0.0')>] + """ + return iter(self._specs) + + def __contains__(self, item: UnparsedVersion) -> bool: + """Return whether or not the item is contained in this specifier. + + :param item: The item to check for. + + This is used for the ``in`` operator and behaves the same as + :meth:`contains` with no ``prereleases`` argument passed. + + >>> "1.2.3" in SpecifierSet(">=1.0.0,!=1.0.1") + True + >>> Version("1.2.3") in SpecifierSet(">=1.0.0,!=1.0.1") + True + >>> "1.0.1" in SpecifierSet(">=1.0.0,!=1.0.1") + False + >>> "1.3.0a1" in SpecifierSet(">=1.0.0,!=1.0.1") + False + >>> "1.3.0a1" in SpecifierSet(">=1.0.0,!=1.0.1", prereleases=True) + True + """ + return self.contains(item) + + def contains( + self, + item: UnparsedVersion, + prereleases: Optional[bool] = None, + installed: Optional[bool] = None, + ) -> bool: + """Return whether or not the item is contained in this SpecifierSet. + + :param item: + The item to check for, which can be a version string or a + :class:`Version` instance. + :param prereleases: + Whether or not to match prereleases with this SpecifierSet. If set to + ``None`` (the default), it uses :attr:`prereleases` to determine + whether or not prereleases are allowed. + + >>> SpecifierSet(">=1.0.0,!=1.0.1").contains("1.2.3") + True + >>> SpecifierSet(">=1.0.0,!=1.0.1").contains(Version("1.2.3")) + True + >>> SpecifierSet(">=1.0.0,!=1.0.1").contains("1.0.1") + False + >>> SpecifierSet(">=1.0.0,!=1.0.1").contains("1.3.0a1") + False + >>> SpecifierSet(">=1.0.0,!=1.0.1", prereleases=True).contains("1.3.0a1") + True + >>> SpecifierSet(">=1.0.0,!=1.0.1").contains("1.3.0a1", prereleases=True) + True + """ + # Ensure that our item is a Version instance. + if not isinstance(item, Version): + item = Version(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 + + if installed and item.is_prerelease: + item = Version(item.base_version) + + # 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: Iterable[UnparsedVersionVar], prereleases: Optional[bool] = None + ) -> Iterator[UnparsedVersionVar]: + """Filter items in the given iterable, that match the specifiers in this set. + + :param iterable: + An iterable that can contain version strings and :class:`Version` instances. + The items in the iterable will be filtered according to the specifier. + :param prereleases: + Whether or not to allow prereleases in the returned iterator. If set to + ``None`` (the default), it will be intelligently decide whether to allow + prereleases or not (based on the :attr:`prereleases` attribute, and + whether the only versions matching are prereleases). + + This method is smarter than just ``filter(SpecifierSet(...).contains, [...])`` + because it implements the rule from :pep:`440` that a prerelease item + SHOULD be accepted if no other versions match the given specifier. + + >>> list(SpecifierSet(">=1.2.3").filter(["1.2", "1.3", "1.5a1"])) + ['1.3'] + >>> list(SpecifierSet(">=1.2.3").filter(["1.2", "1.3", Version("1.4")])) + ['1.3', ] + >>> list(SpecifierSet(">=1.2.3").filter(["1.2", "1.5a1"])) + [] + >>> list(SpecifierSet(">=1.2.3").filter(["1.3", "1.5a1"], prereleases=True)) + ['1.3', '1.5a1'] + >>> list(SpecifierSet(">=1.2.3", prereleases=True).filter(["1.3", "1.5a1"])) + ['1.3', '1.5a1'] + + An "empty" SpecifierSet will filter items based on the presence of prerelease + versions in the set. + + >>> list(SpecifierSet("").filter(["1.3", "1.5a1"])) + ['1.3'] + >>> list(SpecifierSet("").filter(["1.5a1"])) + ['1.5a1'] + >>> list(SpecifierSet("", prereleases=True).filter(["1.3", "1.5a1"])) + ['1.3', '1.5a1'] + >>> list(SpecifierSet("").filter(["1.3", "1.5a1"], prereleases=True)) + ['1.3', '1.5a1'] + """ + # 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 iter(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. + else: + filtered: List[UnparsedVersionVar] = [] + found_prereleases: List[UnparsedVersionVar] = [] + + for item in iterable: + parsed_version = _coerce_version(item) + + # 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 iter(found_prereleases) + + return iter(filtered) diff --git a/venv/Lib/site-packages/pkg_resources/_vendor/packaging/tags.py b/venv/Lib/site-packages/pkg_resources/_vendor/packaging/tags.py new file mode 100644 index 0000000..89f1926 --- /dev/null +++ b/venv/Lib/site-packages/pkg_resources/_vendor/packaging/tags.py @@ -0,0 +1,571 @@ +# 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. + +import logging +import platform +import re +import struct +import subprocess +import sys +import sysconfig +from importlib.machinery import EXTENSION_SUFFIXES +from typing import ( + Dict, + FrozenSet, + Iterable, + Iterator, + List, + Optional, + Sequence, + Tuple, + Union, + cast, +) + +from . import _manylinux, _musllinux + +logger = logging.getLogger(__name__) + +PythonVersion = Sequence[int] +MacVersion = Tuple[int, int] + +INTERPRETER_SHORT_NAMES: Dict[str, str] = { + "python": "py", # Generic. + "cpython": "cp", + "pypy": "pp", + "ironpython": "ip", + "jython": "jy", +} + + +_32_BIT_INTERPRETER = struct.calcsize("P") == 4 + + +class Tag: + """ + A representation of the tag triple for a wheel. + + Instances are considered immutable and thus are hashable. Equality checking + is also supported. + """ + + __slots__ = ["_interpreter", "_abi", "_platform", "_hash"] + + def __init__(self, interpreter: str, abi: str, platform: str) -> None: + self._interpreter = interpreter.lower() + self._abi = abi.lower() + self._platform = platform.lower() + # The __hash__ of every single element in a Set[Tag] will be evaluated each time + # that a set calls its `.disjoint()` method, which may be called hundreds of + # times when scanning a page of links for packages with tags matching that + # Set[Tag]. Pre-computing the value here produces significant speedups for + # downstream consumers. + self._hash = hash((self._interpreter, self._abi, self._platform)) + + @property + def interpreter(self) -> str: + return self._interpreter + + @property + def abi(self) -> str: + return self._abi + + @property + def platform(self) -> str: + return self._platform + + def __eq__(self, other: object) -> bool: + if not isinstance(other, Tag): + return NotImplemented + + return ( + (self._hash == other._hash) # Short-circuit ASAP for perf reasons. + and (self._platform == other._platform) + and (self._abi == other._abi) + and (self._interpreter == other._interpreter) + ) + + def __hash__(self) -> int: + return self._hash + + def __str__(self) -> str: + return f"{self._interpreter}-{self._abi}-{self._platform}" + + def __repr__(self) -> str: + return f"<{self} @ {id(self)}>" + + +def parse_tag(tag: str) -> FrozenSet[Tag]: + """ + Parses the provided tag (e.g. `py3-none-any`) into a frozenset of Tag instances. + + Returning a set is required due to the possibility that the tag is a + compressed tag set. + """ + 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 _get_config_var(name: str, warn: bool = False) -> Union[int, str, None]: + value: Union[int, str, None] = 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: str) -> str: + return string.replace(".", "_").replace("-", "_").replace(" ", "_") + + +def _is_threaded_cpython(abis: List[str]) -> bool: + """ + Determine if the ABI corresponds to a threaded (`--disable-gil`) build. + + The threaded builds are indicated by a "t" in the abiflags. + """ + if len(abis) == 0: + return False + # expect e.g., cp313 + m = re.match(r"cp\d+(.*)", abis[0]) + if not m: + return False + abiflags = m.group(1) + return "t" in abiflags + + +def _abi3_applies(python_version: PythonVersion, threading: bool) -> bool: + """ + Determine if the Python version supports abi3. + + PEP 384 was first implemented in Python 3.2. The threaded (`--disable-gil`) + builds do not support abi3. + """ + return len(python_version) > 1 and tuple(python_version) >= (3, 2) and not threading + + +def _cpython_abis(py_version: PythonVersion, warn: bool = False) -> List[str]: + py_version = tuple(py_version) # To allow for version comparison. + abis = [] + version = _version_nodot(py_version[:2]) + threading = 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, 13) and _get_config_var("Py_GIL_DISABLED", warn): + threading = "t" + 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(f"cp{version}{threading}") + abis.insert(0, f"cp{version}{threading}{debug}{pymalloc}{ucs4}") + return abis + + +def cpython_tags( + python_version: Optional[PythonVersion] = None, + abis: Optional[Iterable[str]] = None, + platforms: Optional[Iterable[str]] = None, + *, + warn: bool = False, +) -> 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. + """ + if not python_version: + python_version = sys.version_info[:2] + + interpreter = f"cp{_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_) + + threading = _is_threaded_cpython(abis) + use_abi3 = _abi3_applies(python_version, threading) + if use_abi3: + yield from (Tag(interpreter, "abi3", platform_) for platform_ in platforms) + yield from (Tag(interpreter, "none", platform_) for platform_ in platforms) + + if use_abi3: + 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() -> List[str]: + """ + Return the ABI tag based on EXT_SUFFIX. + """ + # The following are examples of `EXT_SUFFIX`. + # We want to keep the parts which are related to the ABI and remove the + # parts which are related to the platform: + # - linux: '.cpython-310-x86_64-linux-gnu.so' => cp310 + # - mac: '.cpython-310-darwin.so' => cp310 + # - win: '.cp310-win_amd64.pyd' => cp310 + # - win: '.pyd' => cp37 (uses _cpython_abis()) + # - pypy: '.pypy38-pp73-x86_64-linux-gnu.so' => pypy38_pp73 + # - graalpy: '.graalpy-38-native-x86_64-darwin.dylib' + # => graalpy_38_native + + ext_suffix = _get_config_var("EXT_SUFFIX", warn=True) + if not isinstance(ext_suffix, str) or ext_suffix[0] != ".": + raise SystemError("invalid sysconfig.get_config_var('EXT_SUFFIX')") + parts = ext_suffix.split(".") + if len(parts) < 3: + # CPython3.7 and earlier uses ".pyd" on Windows. + return _cpython_abis(sys.version_info[:2]) + soabi = parts[1] + if soabi.startswith("cpython"): + # non-windows + abi = "cp" + soabi.split("-")[1] + elif soabi.startswith("cp"): + # windows + abi = soabi.split("-")[0] + elif soabi.startswith("pypy"): + abi = "-".join(soabi.split("-")[:2]) + elif soabi.startswith("graalpy"): + abi = "-".join(soabi.split("-")[:3]) + elif soabi: + # pyston, ironpython, others? + abi = soabi + else: + return [] + return [_normalize_string(abi)] + + +def generic_tags( + interpreter: Optional[str] = None, + abis: Optional[Iterable[str]] = None, + platforms: Optional[Iterable[str]] = None, + *, + warn: bool = False, +) -> 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. + """ + 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() + else: + abis = list(abis) + platforms = list(platforms or platform_tags()) + 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: 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 f"py{_version_nodot(py_version[:2])}" + yield f"py{py_version[0]}" + if len(py_version) > 1: + for minor in range(py_version[1] - 1, -1, -1): + yield f"py{_version_nodot((py_version[0], minor))}" + + +def compatible_tags( + python_version: Optional[PythonVersion] = None, + interpreter: Optional[str] = None, + platforms: Optional[Iterable[str]] = None, +) -> 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: str, is_32bit: bool = _32_BIT_INTERPRETER) -> str: + if not is_32bit: + return arch + + if arch.startswith("ppc"): + return "ppc" + + return "i386" + + +def _mac_binary_formats(version: MacVersion, cpu_arch: 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"]) + + if cpu_arch in {"arm64", "x86_64"}: + formats.append("universal2") + + if cpu_arch in {"x86_64", "i386", "ppc64", "ppc", "intel"}: + formats.append("universal") + + return formats + + +def mac_platforms( + version: Optional[MacVersion] = None, arch: Optional[str] = None +) -> 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() + if version is None: + version = cast("MacVersion", tuple(map(int, version_str.split(".")[:2]))) + if version == (10, 16): + # When built against an older macOS SDK, Python will report macOS 10.16 + # instead of the real version. + version_str = subprocess.run( + [ + sys.executable, + "-sS", + "-c", + "import platform; print(platform.mac_ver()[0])", + ], + check=True, + env={"SYSTEM_VERSION_COMPAT": "0"}, + stdout=subprocess.PIPE, + text=True, + ).stdout + 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 + + if (10, 0) <= version and version < (11, 0): + # Prior to Mac OS 11, each yearly release of Mac OS bumped the + # "minor" version number. The major version was always 10. + for minor_version in range(version[1], -1, -1): + compat_version = 10, minor_version + binary_formats = _mac_binary_formats(compat_version, arch) + for binary_format in binary_formats: + yield "macosx_{major}_{minor}_{binary_format}".format( + major=10, minor=minor_version, binary_format=binary_format + ) + + if version >= (11, 0): + # Starting with Mac OS 11, each yearly release bumps the major version + # number. The minor versions are now the midyear updates. + for major_version in range(version[0], 10, -1): + compat_version = major_version, 0 + binary_formats = _mac_binary_formats(compat_version, arch) + for binary_format in binary_formats: + yield "macosx_{major}_{minor}_{binary_format}".format( + major=major_version, minor=0, binary_format=binary_format + ) + + if version >= (11, 0): + # Mac OS 11 on x86_64 is compatible with binaries from previous releases. + # Arm64 support was introduced in 11.0, so no Arm binaries from previous + # releases exist. + # + # However, the "universal2" binary format can have a + # macOS version earlier than 11.0 when the x86_64 part of the binary supports + # that version of macOS. + if arch == "x86_64": + for minor_version in range(16, 3, -1): + compat_version = 10, 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, + ) + else: + for minor_version in range(16, 3, -1): + compat_version = 10, minor_version + binary_format = "universal2" + yield "macosx_{major}_{minor}_{binary_format}".format( + major=compat_version[0], + minor=compat_version[1], + binary_format=binary_format, + ) + + +def _linux_platforms(is_32bit: bool = _32_BIT_INTERPRETER) -> Iterator[str]: + linux = _normalize_string(sysconfig.get_platform()) + if not linux.startswith("linux_"): + # we should never be here, just yield the sysconfig one and return + yield linux + return + if is_32bit: + if linux == "linux_x86_64": + linux = "linux_i686" + elif linux == "linux_aarch64": + linux = "linux_armv8l" + _, arch = linux.split("_", 1) + archs = {"armv8l": ["armv8l", "armv7l"]}.get(arch, [arch]) + yield from _manylinux.platform_tags(archs) + yield from _musllinux.platform_tags(archs) + for arch in archs: + yield f"linux_{arch}" + + +def _generic_platforms() -> Iterator[str]: + yield _normalize_string(sysconfig.get_platform()) + + +def platform_tags() -> 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() -> str: + """ + Returns the name of the running interpreter. + + Some implementations have a reserved, two-letter abbreviation which will + be returned when appropriate. + """ + name = sys.implementation.name + return INTERPRETER_SHORT_NAMES.get(name) or name + + +def interpreter_version(*, warn: bool = False) -> str: + """ + Returns the version of the running interpreter. + """ + 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: PythonVersion) -> str: + return "".join(map(str, version)) + + +def sys_tags(*, warn: bool = False) -> 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. + """ + + interp_name = interpreter_name() + if interp_name == "cp": + yield from cpython_tags(warn=warn) + else: + yield from generic_tags() + + if interp_name == "pp": + interp = "pp3" + elif interp_name == "cp": + interp = "cp" + interpreter_version(warn=warn) + else: + interp = None + yield from compatible_tags(interpreter=interp) diff --git a/venv/Lib/site-packages/pkg_resources/_vendor/packaging/utils.py b/venv/Lib/site-packages/pkg_resources/_vendor/packaging/utils.py new file mode 100644 index 0000000..c2c2f75 --- /dev/null +++ b/venv/Lib/site-packages/pkg_resources/_vendor/packaging/utils.py @@ -0,0 +1,172 @@ +# 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. + +import re +from typing import FrozenSet, NewType, Tuple, Union, cast + +from .tags import Tag, parse_tag +from .version import InvalidVersion, Version + +BuildTag = Union[Tuple[()], Tuple[int, str]] +NormalizedName = NewType("NormalizedName", str) + + +class InvalidName(ValueError): + """ + An invalid distribution name; users should refer to the packaging user guide. + """ + + +class InvalidWheelFilename(ValueError): + """ + An invalid wheel filename was found, users should refer to PEP 427. + """ + + +class InvalidSdistFilename(ValueError): + """ + An invalid sdist filename was found, users should refer to the packaging user guide. + """ + + +# Core metadata spec for `Name` +_validate_regex = re.compile( + r"^([A-Z0-9]|[A-Z0-9][A-Z0-9._-]*[A-Z0-9])$", re.IGNORECASE +) +_canonicalize_regex = re.compile(r"[-_.]+") +_normalized_regex = re.compile(r"^([a-z0-9]|[a-z0-9]([a-z0-9-](?!--))*[a-z0-9])$") +# PEP 427: The build number must start with a digit. +_build_tag_regex = re.compile(r"(\d+)(.*)") + + +def canonicalize_name(name: str, *, validate: bool = False) -> NormalizedName: + if validate and not _validate_regex.match(name): + raise InvalidName(f"name is invalid: {name!r}") + # This is taken from PEP 503. + value = _canonicalize_regex.sub("-", name).lower() + return cast(NormalizedName, value) + + +def is_normalized_name(name: str) -> bool: + return _normalized_regex.match(name) is not None + + +def canonicalize_version( + version: Union[Version, str], *, strip_trailing_zero: bool = True +) -> str: + """ + This is very similar to Version.__str__, but has one subtle difference + with the way it handles the release segment. + """ + if isinstance(version, str): + try: + parsed = Version(version) + except InvalidVersion: + # Legacy versions cannot be normalized + return version + else: + parsed = version + + parts = [] + + # Epoch + if parsed.epoch != 0: + parts.append(f"{parsed.epoch}!") + + # Release segment + release_segment = ".".join(str(x) for x in parsed.release) + if strip_trailing_zero: + # NB: This strips trailing '.0's to normalize + release_segment = re.sub(r"(\.0)+$", "", release_segment) + parts.append(release_segment) + + # Pre-release + if parsed.pre is not None: + parts.append("".join(str(x) for x in parsed.pre)) + + # Post-release + if parsed.post is not None: + parts.append(f".post{parsed.post}") + + # Development release + if parsed.dev is not None: + parts.append(f".dev{parsed.dev}") + + # Local version segment + if parsed.local is not None: + parts.append(f"+{parsed.local}") + + return "".join(parts) + + +def parse_wheel_filename( + filename: str, +) -> Tuple[NormalizedName, Version, BuildTag, FrozenSet[Tag]]: + if not filename.endswith(".whl"): + raise InvalidWheelFilename( + f"Invalid wheel filename (extension must be '.whl'): {filename}" + ) + + filename = filename[:-4] + dashes = filename.count("-") + if dashes not in (4, 5): + raise InvalidWheelFilename( + f"Invalid wheel filename (wrong number of parts): {filename}" + ) + + parts = filename.split("-", dashes - 2) + name_part = parts[0] + # See PEP 427 for the rules on escaping the project name. + if "__" in name_part or re.match(r"^[\w\d._]*$", name_part, re.UNICODE) is None: + raise InvalidWheelFilename(f"Invalid project name: {filename}") + name = canonicalize_name(name_part) + + try: + version = Version(parts[1]) + except InvalidVersion as e: + raise InvalidWheelFilename( + f"Invalid wheel filename (invalid version): {filename}" + ) from e + + if dashes == 5: + build_part = parts[2] + build_match = _build_tag_regex.match(build_part) + if build_match is None: + raise InvalidWheelFilename( + f"Invalid build number: {build_part} in '{filename}'" + ) + build = cast(BuildTag, (int(build_match.group(1)), build_match.group(2))) + else: + build = () + tags = parse_tag(parts[-1]) + return (name, version, build, tags) + + +def parse_sdist_filename(filename: str) -> Tuple[NormalizedName, Version]: + if filename.endswith(".tar.gz"): + file_stem = filename[: -len(".tar.gz")] + elif filename.endswith(".zip"): + file_stem = filename[: -len(".zip")] + else: + raise InvalidSdistFilename( + f"Invalid sdist filename (extension must be '.tar.gz' or '.zip'):" + f" {filename}" + ) + + # We are requiring a PEP 440 version, which cannot contain dashes, + # so we split on the last dash. + name_part, sep, version_part = file_stem.rpartition("-") + if not sep: + raise InvalidSdistFilename(f"Invalid sdist filename: {filename}") + + name = canonicalize_name(name_part) + + try: + version = Version(version_part) + except InvalidVersion as e: + raise InvalidSdistFilename( + f"Invalid sdist filename (invalid version): {filename}" + ) from e + + return (name, version) diff --git a/venv/Lib/site-packages/pkg_resources/_vendor/packaging/version.py b/venv/Lib/site-packages/pkg_resources/_vendor/packaging/version.py new file mode 100644 index 0000000..5faab9b --- /dev/null +++ b/venv/Lib/site-packages/pkg_resources/_vendor/packaging/version.py @@ -0,0 +1,563 @@ +# 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. +""" +.. testsetup:: + + from packaging.version import parse, Version +""" + +import itertools +import re +from typing import Any, Callable, NamedTuple, Optional, SupportsInt, Tuple, Union + +from ._structures import Infinity, InfinityType, NegativeInfinity, NegativeInfinityType + +__all__ = ["VERSION_PATTERN", "parse", "Version", "InvalidVersion"] + +LocalType = Tuple[Union[int, str], ...] + +CmpPrePostDevType = Union[InfinityType, NegativeInfinityType, Tuple[str, int]] +CmpLocalType = Union[ + NegativeInfinityType, + Tuple[Union[Tuple[int, str], Tuple[NegativeInfinityType, Union[int, str]]], ...], +] +CmpKey = Tuple[ + int, + Tuple[int, ...], + CmpPrePostDevType, + CmpPrePostDevType, + CmpPrePostDevType, + CmpLocalType, +] +VersionComparisonMethod = Callable[[CmpKey, CmpKey], bool] + + +class _Version(NamedTuple): + epoch: int + release: Tuple[int, ...] + dev: Optional[Tuple[str, int]] + pre: Optional[Tuple[str, int]] + post: Optional[Tuple[str, int]] + local: Optional[LocalType] + + +def parse(version: str) -> "Version": + """Parse the given version string. + + >>> parse('1.0.dev1') + + + :param version: The version string to parse. + :raises InvalidVersion: When the version string is not a valid version. + """ + return Version(version) + + +class InvalidVersion(ValueError): + """Raised when a version string is not a valid version. + + >>> Version("invalid") + Traceback (most recent call last): + ... + packaging.version.InvalidVersion: Invalid version: 'invalid' + """ + + +class _BaseVersion: + _key: Tuple[Any, ...] + + def __hash__(self) -> int: + return hash(self._key) + + # Please keep the duplicated `isinstance` check + # in the six comparisons hereunder + # unless you find a way to avoid adding overhead function calls. + def __lt__(self, other: "_BaseVersion") -> bool: + if not isinstance(other, _BaseVersion): + return NotImplemented + + return self._key < other._key + + def __le__(self, other: "_BaseVersion") -> bool: + if not isinstance(other, _BaseVersion): + return NotImplemented + + return self._key <= other._key + + def __eq__(self, other: object) -> bool: + if not isinstance(other, _BaseVersion): + return NotImplemented + + return self._key == other._key + + def __ge__(self, other: "_BaseVersion") -> bool: + if not isinstance(other, _BaseVersion): + return NotImplemented + + return self._key >= other._key + + def __gt__(self, other: "_BaseVersion") -> bool: + if not isinstance(other, _BaseVersion): + return NotImplemented + + return self._key > other._key + + def __ne__(self, other: object) -> bool: + if not isinstance(other, _BaseVersion): + return NotImplemented + + return self._key != other._key + + +# 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
+            [-_\.]?
+            (?Palpha|a|beta|b|preview|pre|c|rc)
+            [-_\.]?
+            (?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
+"""
+
+VERSION_PATTERN = _VERSION_PATTERN
+"""
+A string containing the regular expression used to match a valid version.
+
+The pattern is not anchored at either end, and is intended for embedding in larger
+expressions (for example, matching a version number as part of a file name). The
+regular expression should be compiled with the ``re.VERBOSE`` and ``re.IGNORECASE``
+flags set.
+
+:meta hide-value:
+"""
+
+
+class Version(_BaseVersion):
+    """This class abstracts handling of a project's versions.
+
+    A :class:`Version` instance is comparison aware and can be compared and
+    sorted using the standard Python interfaces.
+
+    >>> v1 = Version("1.0a5")
+    >>> v2 = Version("1.0")
+    >>> v1
+    
+    >>> v2
+    
+    >>> v1 < v2
+    True
+    >>> v1 == v2
+    False
+    >>> v1 > v2
+    False
+    >>> v1 >= v2
+    False
+    >>> v1 <= v2
+    True
+    """
+
+    _regex = re.compile(r"^\s*" + VERSION_PATTERN + r"\s*$", re.VERBOSE | re.IGNORECASE)
+    _key: CmpKey
+
+    def __init__(self, version: str) -> None:
+        """Initialize a Version object.
+
+        :param version:
+            The string representation of a version which will be parsed and normalized
+            before use.
+        :raises InvalidVersion:
+            If the ``version`` does not conform to PEP 440 in any way then this
+            exception will be raised.
+        """
+
+        # Validate the version and parse it into pieces
+        match = self._regex.search(version)
+        if not match:
+            raise InvalidVersion(f"Invalid version: '{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) -> str:
+        """A representation of the Version that shows all internal state.
+
+        >>> Version('1.0.0')
+        
+        """
+        return f""
+
+    def __str__(self) -> str:
+        """A string representation of the version that can be rounded-tripped.
+
+        >>> str(Version("1.0a5"))
+        '1.0a5'
+        """
+        parts = []
+
+        # Epoch
+        if self.epoch != 0:
+            parts.append(f"{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(f".post{self.post}")
+
+        # Development release
+        if self.dev is not None:
+            parts.append(f".dev{self.dev}")
+
+        # Local version segment
+        if self.local is not None:
+            parts.append(f"+{self.local}")
+
+        return "".join(parts)
+
+    @property
+    def epoch(self) -> int:
+        """The epoch of the version.
+
+        >>> Version("2.0.0").epoch
+        0
+        >>> Version("1!2.0.0").epoch
+        1
+        """
+        return self._version.epoch
+
+    @property
+    def release(self) -> Tuple[int, ...]:
+        """The components of the "release" segment of the version.
+
+        >>> Version("1.2.3").release
+        (1, 2, 3)
+        >>> Version("2.0.0").release
+        (2, 0, 0)
+        >>> Version("1!2.0.0.post0").release
+        (2, 0, 0)
+
+        Includes trailing zeroes but not the epoch or any pre-release / development /
+        post-release suffixes.
+        """
+        return self._version.release
+
+    @property
+    def pre(self) -> Optional[Tuple[str, int]]:
+        """The pre-release segment of the version.
+
+        >>> print(Version("1.2.3").pre)
+        None
+        >>> Version("1.2.3a1").pre
+        ('a', 1)
+        >>> Version("1.2.3b1").pre
+        ('b', 1)
+        >>> Version("1.2.3rc1").pre
+        ('rc', 1)
+        """
+        return self._version.pre
+
+    @property
+    def post(self) -> Optional[int]:
+        """The post-release number of the version.
+
+        >>> print(Version("1.2.3").post)
+        None
+        >>> Version("1.2.3.post1").post
+        1
+        """
+        return self._version.post[1] if self._version.post else None
+
+    @property
+    def dev(self) -> Optional[int]:
+        """The development number of the version.
+
+        >>> print(Version("1.2.3").dev)
+        None
+        >>> Version("1.2.3.dev1").dev
+        1
+        """
+        return self._version.dev[1] if self._version.dev else None
+
+    @property
+    def local(self) -> Optional[str]:
+        """The local version segment of the version.
+
+        >>> print(Version("1.2.3").local)
+        None
+        >>> Version("1.2.3+abc").local
+        'abc'
+        """
+        if self._version.local:
+            return ".".join(str(x) for x in self._version.local)
+        else:
+            return None
+
+    @property
+    def public(self) -> str:
+        """The public portion of the version.
+
+        >>> Version("1.2.3").public
+        '1.2.3'
+        >>> Version("1.2.3+abc").public
+        '1.2.3'
+        >>> Version("1.2.3+abc.dev1").public
+        '1.2.3'
+        """
+        return str(self).split("+", 1)[0]
+
+    @property
+    def base_version(self) -> str:
+        """The "base version" of the version.
+
+        >>> Version("1.2.3").base_version
+        '1.2.3'
+        >>> Version("1.2.3+abc").base_version
+        '1.2.3'
+        >>> Version("1!1.2.3+abc.dev1").base_version
+        '1!1.2.3'
+
+        The "base version" is the public version of the project without any pre or post
+        release markers.
+        """
+        parts = []
+
+        # Epoch
+        if self.epoch != 0:
+            parts.append(f"{self.epoch}!")
+
+        # Release segment
+        parts.append(".".join(str(x) for x in self.release))
+
+        return "".join(parts)
+
+    @property
+    def is_prerelease(self) -> bool:
+        """Whether this version is a pre-release.
+
+        >>> Version("1.2.3").is_prerelease
+        False
+        >>> Version("1.2.3a1").is_prerelease
+        True
+        >>> Version("1.2.3b1").is_prerelease
+        True
+        >>> Version("1.2.3rc1").is_prerelease
+        True
+        >>> Version("1.2.3dev1").is_prerelease
+        True
+        """
+        return self.dev is not None or self.pre is not None
+
+    @property
+    def is_postrelease(self) -> bool:
+        """Whether this version is a post-release.
+
+        >>> Version("1.2.3").is_postrelease
+        False
+        >>> Version("1.2.3.post1").is_postrelease
+        True
+        """
+        return self.post is not None
+
+    @property
+    def is_devrelease(self) -> bool:
+        """Whether this version is a development release.
+
+        >>> Version("1.2.3").is_devrelease
+        False
+        >>> Version("1.2.3.dev1").is_devrelease
+        True
+        """
+        return self.dev is not None
+
+    @property
+    def major(self) -> int:
+        """The first item of :attr:`release` or ``0`` if unavailable.
+
+        >>> Version("1.2.3").major
+        1
+        """
+        return self.release[0] if len(self.release) >= 1 else 0
+
+    @property
+    def minor(self) -> int:
+        """The second item of :attr:`release` or ``0`` if unavailable.
+
+        >>> Version("1.2.3").minor
+        2
+        >>> Version("1").minor
+        0
+        """
+        return self.release[1] if len(self.release) >= 2 else 0
+
+    @property
+    def micro(self) -> int:
+        """The third item of :attr:`release` or ``0`` if unavailable.
+
+        >>> Version("1.2.3").micro
+        3
+        >>> Version("1").micro
+        0
+        """
+        return self.release[2] if len(self.release) >= 3 else 0
+
+
+def _parse_letter_version(
+    letter: Optional[str], number: Union[str, bytes, SupportsInt, None]
+) -> 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: Optional[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: int,
+    release: Tuple[int, ...],
+    pre: Optional[Tuple[str, int]],
+    post: Optional[Tuple[str, int]],
+    dev: Optional[Tuple[str, int]],
+    local: Optional[LocalType],
+) -> 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: CmpPrePostDevType = NegativeInfinity
+    # 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: CmpPrePostDevType = NegativeInfinity
+
+    else:
+        _post = post
+
+    # Versions without a development segment should sort after those with one.
+    if dev is None:
+        _dev: CmpPrePostDevType = Infinity
+
+    else:
+        _dev = dev
+
+    if local is None:
+        # Versions without a local segment should sort before those with one.
+        _local: CmpLocalType = NegativeInfinity
+    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/site-packages/pkg_resources/_vendor/platformdirs/__init__.py b/venv/Lib/site-packages/pkg_resources/_vendor/platformdirs/__init__.py
new file mode 100644
index 0000000..aef2821
--- /dev/null
+++ b/venv/Lib/site-packages/pkg_resources/_vendor/platformdirs/__init__.py
@@ -0,0 +1,342 @@
+"""
+Utilities for determining application-specific dirs. See  for details and
+usage.
+"""
+from __future__ import annotations
+
+import os
+import sys
+from pathlib import Path
+
+if sys.version_info >= (3, 8):  # pragma: no cover (py38+)
+    from typing import Literal
+else:  # pragma: no cover (py38+)
+    from ..typing_extensions import Literal
+
+from .api import PlatformDirsABC
+from .version import __version__
+from .version import __version_tuple__ as __version_info__
+
+
+def _set_platform_dir_class() -> type[PlatformDirsABC]:
+    if sys.platform == "win32":
+        from .windows import Windows as Result
+    elif sys.platform == "darwin":
+        from .macos import MacOS as Result
+    else:
+        from .unix import Unix as Result
+
+    if os.getenv("ANDROID_DATA") == "/data" and os.getenv("ANDROID_ROOT") == "/system":
+
+        if os.getenv("SHELL") or os.getenv("PREFIX"):
+            return Result
+
+        from .android import _android_folder
+
+        if _android_folder() is not None:
+            from .android import Android
+
+            return Android  # return to avoid redefinition of result
+
+    return Result
+
+
+PlatformDirs = _set_platform_dir_class()  #: Currently active platform
+AppDirs = PlatformDirs  #: Backwards compatibility with appdirs
+
+
+def user_data_dir(
+    appname: str | None = None,
+    appauthor: str | None | Literal[False] = None,
+    version: str | None = None,
+    roaming: bool = False,
+) -> str:
+    """
+    :param appname: See `appname `.
+    :param appauthor: See `appauthor `.
+    :param version: See `version `.
+    :param roaming: See `roaming `.
+    :returns: data directory tied to the user
+    """
+    return PlatformDirs(appname=appname, appauthor=appauthor, version=version, roaming=roaming).user_data_dir
+
+
+def site_data_dir(
+    appname: str | None = None,
+    appauthor: str | None | Literal[False] = None,
+    version: str | None = None,
+    multipath: bool = False,
+) -> str:
+    """
+    :param appname: See `appname `.
+    :param appauthor: See `appauthor `.
+    :param version: See `version `.
+    :param multipath: See `roaming `.
+    :returns: data directory shared by users
+    """
+    return PlatformDirs(appname=appname, appauthor=appauthor, version=version, multipath=multipath).site_data_dir
+
+
+def user_config_dir(
+    appname: str | None = None,
+    appauthor: str | None | Literal[False] = None,
+    version: str | None = None,
+    roaming: bool = False,
+) -> str:
+    """
+    :param appname: See `appname `.
+    :param appauthor: See `appauthor `.
+    :param version: See `version `.
+    :param roaming: See `roaming `.
+    :returns: config directory tied to the user
+    """
+    return PlatformDirs(appname=appname, appauthor=appauthor, version=version, roaming=roaming).user_config_dir
+
+
+def site_config_dir(
+    appname: str | None = None,
+    appauthor: str | None | Literal[False] = None,
+    version: str | None = None,
+    multipath: bool = False,
+) -> str:
+    """
+    :param appname: See `appname `.
+    :param appauthor: See `appauthor `.
+    :param version: See `version `.
+    :param multipath: See `roaming `.
+    :returns: config directory shared by the users
+    """
+    return PlatformDirs(appname=appname, appauthor=appauthor, version=version, multipath=multipath).site_config_dir
+
+
+def user_cache_dir(
+    appname: str | None = None,
+    appauthor: str | None | Literal[False] = None,
+    version: str | None = None,
+    opinion: bool = True,
+) -> str:
+    """
+    :param appname: See `appname `.
+    :param appauthor: See `appauthor `.
+    :param version: See `version `.
+    :param opinion: See `roaming `.
+    :returns: cache directory tied to the user
+    """
+    return PlatformDirs(appname=appname, appauthor=appauthor, version=version, opinion=opinion).user_cache_dir
+
+
+def user_state_dir(
+    appname: str | None = None,
+    appauthor: str | None | Literal[False] = None,
+    version: str | None = None,
+    roaming: bool = False,
+) -> str:
+    """
+    :param appname: See `appname `.
+    :param appauthor: See `appauthor `.
+    :param version: See `version `.
+    :param roaming: See `roaming `.
+    :returns: state directory tied to the user
+    """
+    return PlatformDirs(appname=appname, appauthor=appauthor, version=version, roaming=roaming).user_state_dir
+
+
+def user_log_dir(
+    appname: str | None = None,
+    appauthor: str | None | Literal[False] = None,
+    version: str | None = None,
+    opinion: bool = True,
+) -> str:
+    """
+    :param appname: See `appname `.
+    :param appauthor: See `appauthor `.
+    :param version: See `version `.
+    :param opinion: See `roaming `.
+    :returns: log directory tied to the user
+    """
+    return PlatformDirs(appname=appname, appauthor=appauthor, version=version, opinion=opinion).user_log_dir
+
+
+def user_documents_dir() -> str:
+    """
+    :returns: documents directory tied to the user
+    """
+    return PlatformDirs().user_documents_dir
+
+
+def user_runtime_dir(
+    appname: str | None = None,
+    appauthor: str | None | Literal[False] = None,
+    version: str | None = None,
+    opinion: bool = True,
+) -> str:
+    """
+    :param appname: See `appname `.
+    :param appauthor: See `appauthor `.
+    :param version: See `version `.
+    :param opinion: See `opinion `.
+    :returns: runtime directory tied to the user
+    """
+    return PlatformDirs(appname=appname, appauthor=appauthor, version=version, opinion=opinion).user_runtime_dir
+
+
+def user_data_path(
+    appname: str | None = None,
+    appauthor: str | None | Literal[False] = None,
+    version: str | None = None,
+    roaming: bool = False,
+) -> Path:
+    """
+    :param appname: See `appname `.
+    :param appauthor: See `appauthor `.
+    :param version: See `version `.
+    :param roaming: See `roaming `.
+    :returns: data path tied to the user
+    """
+    return PlatformDirs(appname=appname, appauthor=appauthor, version=version, roaming=roaming).user_data_path
+
+
+def site_data_path(
+    appname: str | None = None,
+    appauthor: str | None | Literal[False] = None,
+    version: str | None = None,
+    multipath: bool = False,
+) -> Path:
+    """
+    :param appname: See `appname `.
+    :param appauthor: See `appauthor `.
+    :param version: See `version `.
+    :param multipath: See `multipath `.
+    :returns: data path shared by users
+    """
+    return PlatformDirs(appname=appname, appauthor=appauthor, version=version, multipath=multipath).site_data_path
+
+
+def user_config_path(
+    appname: str | None = None,
+    appauthor: str | None | Literal[False] = None,
+    version: str | None = None,
+    roaming: bool = False,
+) -> Path:
+    """
+    :param appname: See `appname `.
+    :param appauthor: See `appauthor `.
+    :param version: See `version `.
+    :param roaming: See `roaming `.
+    :returns: config path tied to the user
+    """
+    return PlatformDirs(appname=appname, appauthor=appauthor, version=version, roaming=roaming).user_config_path
+
+
+def site_config_path(
+    appname: str | None = None,
+    appauthor: str | None | Literal[False] = None,
+    version: str | None = None,
+    multipath: bool = False,
+) -> Path:
+    """
+    :param appname: See `appname `.
+    :param appauthor: See `appauthor `.
+    :param version: See `version `.
+    :param multipath: See `roaming `.
+    :returns: config path shared by the users
+    """
+    return PlatformDirs(appname=appname, appauthor=appauthor, version=version, multipath=multipath).site_config_path
+
+
+def user_cache_path(
+    appname: str | None = None,
+    appauthor: str | None | Literal[False] = None,
+    version: str | None = None,
+    opinion: bool = True,
+) -> Path:
+    """
+    :param appname: See `appname `.
+    :param appauthor: See `appauthor `.
+    :param version: See `version `.
+    :param opinion: See `roaming `.
+    :returns: cache path tied to the user
+    """
+    return PlatformDirs(appname=appname, appauthor=appauthor, version=version, opinion=opinion).user_cache_path
+
+
+def user_state_path(
+    appname: str | None = None,
+    appauthor: str | None | Literal[False] = None,
+    version: str | None = None,
+    roaming: bool = False,
+) -> Path:
+    """
+    :param appname: See `appname `.
+    :param appauthor: See `appauthor `.
+    :param version: See `version `.
+    :param roaming: See `roaming `.
+    :returns: state path tied to the user
+    """
+    return PlatformDirs(appname=appname, appauthor=appauthor, version=version, roaming=roaming).user_state_path
+
+
+def user_log_path(
+    appname: str | None = None,
+    appauthor: str | None | Literal[False] = None,
+    version: str | None = None,
+    opinion: bool = True,
+) -> Path:
+    """
+    :param appname: See `appname `.
+    :param appauthor: See `appauthor `.
+    :param version: See `version `.
+    :param opinion: See `roaming `.
+    :returns: log path tied to the user
+    """
+    return PlatformDirs(appname=appname, appauthor=appauthor, version=version, opinion=opinion).user_log_path
+
+
+def user_documents_path() -> Path:
+    """
+    :returns: documents path tied to the user
+    """
+    return PlatformDirs().user_documents_path
+
+
+def user_runtime_path(
+    appname: str | None = None,
+    appauthor: str | None | Literal[False] = None,
+    version: str | None = None,
+    opinion: bool = True,
+) -> Path:
+    """
+    :param appname: See `appname `.
+    :param appauthor: See `appauthor `.
+    :param version: See `version `.
+    :param opinion: See `opinion `.
+    :returns: runtime path tied to the user
+    """
+    return PlatformDirs(appname=appname, appauthor=appauthor, version=version, opinion=opinion).user_runtime_path
+
+
+__all__ = [
+    "__version__",
+    "__version_info__",
+    "PlatformDirs",
+    "AppDirs",
+    "PlatformDirsABC",
+    "user_data_dir",
+    "user_config_dir",
+    "user_cache_dir",
+    "user_state_dir",
+    "user_log_dir",
+    "user_documents_dir",
+    "user_runtime_dir",
+    "site_data_dir",
+    "site_config_dir",
+    "user_data_path",
+    "user_config_path",
+    "user_cache_path",
+    "user_state_path",
+    "user_log_path",
+    "user_documents_path",
+    "user_runtime_path",
+    "site_data_path",
+    "site_config_path",
+]
diff --git a/venv/Lib/site-packages/pkg_resources/_vendor/platformdirs/__main__.py b/venv/Lib/site-packages/pkg_resources/_vendor/platformdirs/__main__.py
new file mode 100644
index 0000000..0fc1edd
--- /dev/null
+++ b/venv/Lib/site-packages/pkg_resources/_vendor/platformdirs/__main__.py
@@ -0,0 +1,46 @@
+from __future__ import annotations
+
+from platformdirs import PlatformDirs, __version__
+
+PROPS = (
+    "user_data_dir",
+    "user_config_dir",
+    "user_cache_dir",
+    "user_state_dir",
+    "user_log_dir",
+    "user_documents_dir",
+    "user_runtime_dir",
+    "site_data_dir",
+    "site_config_dir",
+)
+
+
+def main() -> None:
+    app_name = "MyApp"
+    app_author = "MyCompany"
+
+    print(f"-- platformdirs {__version__} --")
+
+    print("-- app dirs (with optional 'version')")
+    dirs = PlatformDirs(app_name, app_author, version="1.0")
+    for prop in PROPS:
+        print(f"{prop}: {getattr(dirs, prop)}")
+
+    print("\n-- app dirs (without optional 'version')")
+    dirs = PlatformDirs(app_name, app_author)
+    for prop in PROPS:
+        print(f"{prop}: {getattr(dirs, prop)}")
+
+    print("\n-- app dirs (without optional 'appauthor')")
+    dirs = PlatformDirs(app_name)
+    for prop in PROPS:
+        print(f"{prop}: {getattr(dirs, prop)}")
+
+    print("\n-- app dirs (with disabled 'appauthor')")
+    dirs = PlatformDirs(app_name, appauthor=False)
+    for prop in PROPS:
+        print(f"{prop}: {getattr(dirs, prop)}")
+
+
+if __name__ == "__main__":
+    main()
diff --git a/venv/Lib/site-packages/pkg_resources/_vendor/platformdirs/__pycache__/__init__.cpython-312.pyc b/venv/Lib/site-packages/pkg_resources/_vendor/platformdirs/__pycache__/__init__.cpython-312.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..b8b617f290b5a9c192a7ac1f589b36262d7bf2b2
GIT binary patch
literal 13224
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

literal 0
HcmV?d00001

diff --git a/venv/Lib/site-packages/pkg_resources/_vendor/platformdirs/__pycache__/__main__.cpython-312.pyc b/venv/Lib/site-packages/pkg_resources/_vendor/platformdirs/__pycache__/__main__.cpython-312.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..67b2b9a17bb18331bd605f1a3b83c732ea953e3b
GIT binary patch
literal 1812
zcmcIk&2QX96d&8;U9WdH4W-`%WRR-F6enpU0#p?eN{A35VFlu{EV7*SWMgIhp|PE6
z%|_Y-NImqB89S)?*b4+td0Ef7vU@y7PHmRba=l#x7s@6CI^-^`nN
z?}vI_MKI_u#>Op-(63w>4>1#j;}sCTK{m35E^>vYAaGgiimud@T-?N+xRQo
zERnJ;Z_NOvA|g`S&(Q?bEA>jc@(0&-fmJQf^CK&Ad@s~RR%>*vXv3%OvO_~wHO*~8
zLs&CSy~65oNT_LBk!9KrWv5cL?Ry(eCpAoD2CKbE3VI0WnO;lRyME?!8;!llakH3u=VRvz;F+{Z(L=5E72hLCPRe!VaWoM~H;6lPyJgG7-9i
zZ2YaXpRWn1;3XHV@;J7S`hs1$U)~1>K6b>Ylt~CJqc5hwB0a(AOMtILd`Ic%i+yQ7
z=e~oAytiJ#MefSKagU=(gbv_w~*iuL2@nc-Ld%N4d@#nGlufotYzXV_k7t#^s7O&2^@U`Lh5x-K(0ppF+&
zo@ELh1~Iwu$;XXPS-C@?KqIPSrhvccSuSBJmoid@`SBSJs2_wpP=oR4%cX0r&!8KG
z>D*c#S8;2}w;iw3YB=pEro?E(Nzx_84YJM)x3x{Y?bb)mdW&mDV70fb4hdVqR>!0y
z^kdp4;T-fF&-UqD@kZuM)3qFEL$g7TVJ=drICL7mP=gvCpa-YU4L*4A!qniy-*9DE
zo4Wh??bUlP4pn1>XSqeaI>HNw)$_NNz1P1dqw4j+^5Nv!zmOo$-^7PXefP|6X?OkZ
z`MooH(ZR(xh8K+C#OzPX+>rzxf0vM~4%NjGek+GKs2a}!@b(D5lL8!6Uww}7H%E9e
z4ZrvPsJie!0siC%IEF+ari`<=2CcqC30#rK@S5)tU16$eZp8f3Oq22+$7;n3NZ(BM
zdd|XPnthJPpNnaJhv>`P`Vvf!l^

literal 0
HcmV?d00001

diff --git a/venv/Lib/site-packages/pkg_resources/_vendor/platformdirs/__pycache__/android.cpython-312.pyc b/venv/Lib/site-packages/pkg_resources/_vendor/platformdirs/__pycache__/android.cpython-312.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..a2c9e54b5911610e9785ca4982fc0de5ebd34587
GIT binary patch
literal 5809
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

literal 0
HcmV?d00001

diff --git a/venv/Lib/site-packages/pkg_resources/_vendor/platformdirs/__pycache__/version.cpython-312.pyc b/venv/Lib/site-packages/pkg_resources/_vendor/platformdirs/__pycache__/version.cpython-312.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..a315ed4c5b0efccb4b67a49cf1eb9cfad764ad65
GIT binary patch
literal 335
zcmX@j%ge<81Vs;Z(-r{f#~=<2FhLogMSzUy3@Hp1j8P0xj46yjnkkC8l1Y=fiq%NZ
zOwUM@`6Wp4OEw@;1Y-GVvfSd1k1tCtD$dN$i;us>4rSgFM2MD@7UZM?<#=Jrzyd27
zJ_8M4_*La>6%$$vGyqJ-xa237=BDPA6vsH{r)1`(#{^_1my{Nz>IRfnRpq4WmZv5a
zXO^VKl%?jC#rR|<#ejIa1&PVoiRr1uF$LM_@kOb{`K3k4sm1z0hvlW@7wH$|B$lM*
z7v-j878UD5U7=S{`HRCQH$SB`C)KWq8|V~9ATE{x5+9fu85uvYF-R(0V32R%?nu9(
RqI-b>hy*A47I6X<002KHXFdP`

literal 0
HcmV?d00001

diff --git a/venv/Lib/site-packages/pkg_resources/_vendor/platformdirs/__pycache__/windows.cpython-312.pyc b/venv/Lib/site-packages/pkg_resources/_vendor/platformdirs/__pycache__/windows.cpython-312.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..ff1c3c4ae330d960de21e9f413052085b8973819
GIT binary patch
literal 9296
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
u`_. Makes use of the
+    `appname ` and
+    `version `.
+    """
+
+    @property
+    def user_data_dir(self) -> str:
+        """:return: data directory tied to the user, e.g. ``/data/user///files/``"""
+        return self._append_app_name_and_version(cast(str, _android_folder()), "files")
+
+    @property
+    def site_data_dir(self) -> str:
+        """:return: data directory shared by users, same as `user_data_dir`"""
+        return self.user_data_dir
+
+    @property
+    def user_config_dir(self) -> str:
+        """
+        :return: config directory tied to the user, e.g. ``/data/user///shared_prefs/``
+        """
+        return self._append_app_name_and_version(cast(str, _android_folder()), "shared_prefs")
+
+    @property
+    def site_config_dir(self) -> str:
+        """:return: config directory shared by the users, same as `user_config_dir`"""
+        return self.user_config_dir
+
+    @property
+    def user_cache_dir(self) -> str:
+        """:return: cache directory tied to the user, e.g. e.g. ``/data/user///cache/``"""
+        return self._append_app_name_and_version(cast(str, _android_folder()), "cache")
+
+    @property
+    def user_state_dir(self) -> str:
+        """:return: state directory tied to the user, same as `user_data_dir`"""
+        return self.user_data_dir
+
+    @property
+    def user_log_dir(self) -> str:
+        """
+        :return: log directory tied to the user, same as `user_cache_dir` if not opinionated else ``log`` in it,
+          e.g. ``/data/user///cache//log``
+        """
+        path = self.user_cache_dir
+        if self.opinion:
+            path = os.path.join(path, "log")
+        return path
+
+    @property
+    def user_documents_dir(self) -> str:
+        """
+        :return: documents directory tied to the user e.g. ``/storage/emulated/0/Documents``
+        """
+        return _android_documents_folder()
+
+    @property
+    def user_runtime_dir(self) -> str:
+        """
+        :return: runtime directory tied to the user, same as `user_cache_dir` if not opinionated else ``tmp`` in it,
+          e.g. ``/data/user///cache//tmp``
+        """
+        path = self.user_cache_dir
+        if self.opinion:
+            path = os.path.join(path, "tmp")
+        return path
+
+
+@lru_cache(maxsize=1)
+def _android_folder() -> str | None:
+    """:return: base folder for the Android OS or None if cannot be found"""
+    try:
+        # First try to get path to android app via pyjnius
+        from jnius import autoclass
+
+        Context = autoclass("android.content.Context")  # noqa: N806
+        result: str | None = Context.getFilesDir().getParentFile().getAbsolutePath()
+    except Exception:
+        # if fails find an android folder looking path on the sys.path
+        pattern = re.compile(r"/data/(data|user/\d+)/(.+)/files")
+        for path in sys.path:
+            if pattern.match(path):
+                result = path.split("/files")[0]
+                break
+        else:
+            result = None
+    return result
+
+
+@lru_cache(maxsize=1)
+def _android_documents_folder() -> str:
+    """:return: documents folder for the Android OS"""
+    # Get directories with pyjnius
+    try:
+        from jnius import autoclass
+
+        Context = autoclass("android.content.Context")  # noqa: N806
+        Environment = autoclass("android.os.Environment")  # noqa: N806
+        documents_dir: str = Context.getExternalFilesDir(Environment.DIRECTORY_DOCUMENTS).getAbsolutePath()
+    except Exception:
+        documents_dir = "/storage/emulated/0/Documents"
+
+    return documents_dir
+
+
+__all__ = [
+    "Android",
+]
diff --git a/venv/Lib/site-packages/pkg_resources/_vendor/platformdirs/api.py b/venv/Lib/site-packages/pkg_resources/_vendor/platformdirs/api.py
new file mode 100644
index 0000000..6f6e2c2
--- /dev/null
+++ b/venv/Lib/site-packages/pkg_resources/_vendor/platformdirs/api.py
@@ -0,0 +1,156 @@
+from __future__ import annotations
+
+import os
+import sys
+from abc import ABC, abstractmethod
+from pathlib import Path
+
+if sys.version_info >= (3, 8):  # pragma: no branch
+    from typing import Literal  # pragma: no cover
+
+
+class PlatformDirsABC(ABC):
+    """
+    Abstract base class for platform directories.
+    """
+
+    def __init__(
+        self,
+        appname: str | None = None,
+        appauthor: str | None | Literal[False] = None,
+        version: str | None = None,
+        roaming: bool = False,
+        multipath: bool = False,
+        opinion: bool = True,
+    ):
+        """
+        Create a new platform directory.
+
+        :param appname: See `appname`.
+        :param appauthor: See `appauthor`.
+        :param version: See `version`.
+        :param roaming: See `roaming`.
+        :param multipath: See `multipath`.
+        :param opinion: See `opinion`.
+        """
+        self.appname = appname  #: The name of application.
+        self.appauthor = appauthor
+        """
+        The name of the app author or distributing body for this application. Typically, it is the owning company name.
+        Defaults to `appname`. You may pass ``False`` to disable it.
+        """
+        self.version = version
+        """
+        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 ``.``.
+        """
+        self.roaming = roaming
+        """
+        Whether to use the roaming appdata directory on Windows. That means that for users on a Windows network setup
+        for roaming profiles, this user data will be synced on login (see
+        `here `_).
+        """
+        self.multipath = multipath
+        """
+        An optional parameter only applicable to Unix/Linux which indicates that the entire list of data dirs should be
+        returned. By default, the first item would only be returned.
+        """
+        self.opinion = opinion  #: A flag to indicating to use opinionated values.
+
+    def _append_app_name_and_version(self, *base: str) -> str:
+        params = list(base[1:])
+        if self.appname:
+            params.append(self.appname)
+            if self.version:
+                params.append(self.version)
+        return os.path.join(base[0], *params)
+
+    @property
+    @abstractmethod
+    def user_data_dir(self) -> str:
+        """:return: data directory tied to the user"""
+
+    @property
+    @abstractmethod
+    def site_data_dir(self) -> str:
+        """:return: data directory shared by users"""
+
+    @property
+    @abstractmethod
+    def user_config_dir(self) -> str:
+        """:return: config directory tied to the user"""
+
+    @property
+    @abstractmethod
+    def site_config_dir(self) -> str:
+        """:return: config directory shared by the users"""
+
+    @property
+    @abstractmethod
+    def user_cache_dir(self) -> str:
+        """:return: cache directory tied to the user"""
+
+    @property
+    @abstractmethod
+    def user_state_dir(self) -> str:
+        """:return: state directory tied to the user"""
+
+    @property
+    @abstractmethod
+    def user_log_dir(self) -> str:
+        """:return: log directory tied to the user"""
+
+    @property
+    @abstractmethod
+    def user_documents_dir(self) -> str:
+        """:return: documents directory tied to the user"""
+
+    @property
+    @abstractmethod
+    def user_runtime_dir(self) -> str:
+        """:return: runtime directory tied to the user"""
+
+    @property
+    def user_data_path(self) -> Path:
+        """:return: data path tied to the user"""
+        return Path(self.user_data_dir)
+
+    @property
+    def site_data_path(self) -> Path:
+        """:return: data path shared by users"""
+        return Path(self.site_data_dir)
+
+    @property
+    def user_config_path(self) -> Path:
+        """:return: config path tied to the user"""
+        return Path(self.user_config_dir)
+
+    @property
+    def site_config_path(self) -> Path:
+        """:return: config path shared by the users"""
+        return Path(self.site_config_dir)
+
+    @property
+    def user_cache_path(self) -> Path:
+        """:return: cache path tied to the user"""
+        return Path(self.user_cache_dir)
+
+    @property
+    def user_state_path(self) -> Path:
+        """:return: state path tied to the user"""
+        return Path(self.user_state_dir)
+
+    @property
+    def user_log_path(self) -> Path:
+        """:return: log path tied to the user"""
+        return Path(self.user_log_dir)
+
+    @property
+    def user_documents_path(self) -> Path:
+        """:return: documents path tied to the user"""
+        return Path(self.user_documents_dir)
+
+    @property
+    def user_runtime_path(self) -> Path:
+        """:return: runtime path tied to the user"""
+        return Path(self.user_runtime_dir)
diff --git a/venv/Lib/site-packages/pkg_resources/_vendor/platformdirs/macos.py b/venv/Lib/site-packages/pkg_resources/_vendor/platformdirs/macos.py
new file mode 100644
index 0000000..a01337c
--- /dev/null
+++ b/venv/Lib/site-packages/pkg_resources/_vendor/platformdirs/macos.py
@@ -0,0 +1,64 @@
+from __future__ import annotations
+
+import os
+
+from .api import PlatformDirsABC
+
+
+class MacOS(PlatformDirsABC):
+    """
+    Platform directories for the macOS operating system. Follows the guidance from `Apple documentation
+    `_.
+    Makes use of the `appname ` and
+    `version `.
+    """
+
+    @property
+    def user_data_dir(self) -> str:
+        """:return: data directory tied to the user, e.g. ``~/Library/Application Support/$appname/$version``"""
+        return self._append_app_name_and_version(os.path.expanduser("~/Library/Application Support/"))
+
+    @property
+    def site_data_dir(self) -> str:
+        """:return: data directory shared by users, e.g. ``/Library/Application Support/$appname/$version``"""
+        return self._append_app_name_and_version("/Library/Application Support")
+
+    @property
+    def user_config_dir(self) -> str:
+        """:return: config directory tied to the user, e.g. ``~/Library/Preferences/$appname/$version``"""
+        return self._append_app_name_and_version(os.path.expanduser("~/Library/Preferences/"))
+
+    @property
+    def site_config_dir(self) -> str:
+        """:return: config directory shared by the users, e.g. ``/Library/Preferences/$appname``"""
+        return self._append_app_name_and_version("/Library/Preferences")
+
+    @property
+    def user_cache_dir(self) -> str:
+        """:return: cache directory tied to the user, e.g. ``~/Library/Caches/$appname/$version``"""
+        return self._append_app_name_and_version(os.path.expanduser("~/Library/Caches"))
+
+    @property
+    def user_state_dir(self) -> str:
+        """:return: state directory tied to the user, same as `user_data_dir`"""
+        return self.user_data_dir
+
+    @property
+    def user_log_dir(self) -> str:
+        """:return: log directory tied to the user, e.g. ``~/Library/Logs/$appname/$version``"""
+        return self._append_app_name_and_version(os.path.expanduser("~/Library/Logs"))
+
+    @property
+    def user_documents_dir(self) -> str:
+        """:return: documents directory tied to the user, e.g. ``~/Documents``"""
+        return os.path.expanduser("~/Documents")
+
+    @property
+    def user_runtime_dir(self) -> str:
+        """:return: runtime directory tied to the user, e.g. ``~/Library/Caches/TemporaryItems/$appname/$version``"""
+        return self._append_app_name_and_version(os.path.expanduser("~/Library/Caches/TemporaryItems"))
+
+
+__all__ = [
+    "MacOS",
+]
diff --git a/venv/Lib/site-packages/pkg_resources/_vendor/platformdirs/py.typed b/venv/Lib/site-packages/pkg_resources/_vendor/platformdirs/py.typed
new file mode 100644
index 0000000..e69de29
diff --git a/venv/Lib/site-packages/pkg_resources/_vendor/platformdirs/unix.py b/venv/Lib/site-packages/pkg_resources/_vendor/platformdirs/unix.py
new file mode 100644
index 0000000..9aca5a0
--- /dev/null
+++ b/venv/Lib/site-packages/pkg_resources/_vendor/platformdirs/unix.py
@@ -0,0 +1,181 @@
+from __future__ import annotations
+
+import os
+import sys
+from configparser import ConfigParser
+from pathlib import Path
+
+from .api import PlatformDirsABC
+
+if sys.platform.startswith("linux"):  # pragma: no branch # no op check, only to please the type checker
+    from os import getuid
+else:
+
+    def getuid() -> int:
+        raise RuntimeError("should only be used on Linux")
+
+
+class Unix(PlatformDirsABC):
+    """
+    On Unix/Linux, we follow the
+    `XDG Basedir Spec `_. The spec allows
+    overriding directories with environment variables. The examples show are the default values, alongside the name of
+    the environment variable that overrides them. Makes use of the
+    `appname `,
+    `version `,
+    `multipath `,
+    `opinion `.
+    """
+
+    @property
+    def user_data_dir(self) -> str:
+        """
+        :return: data directory tied to the user, e.g. ``~/.local/share/$appname/$version`` or
+         ``$XDG_DATA_HOME/$appname/$version``
+        """
+        path = os.environ.get("XDG_DATA_HOME", "")
+        if not path.strip():
+            path = os.path.expanduser("~/.local/share")
+        return self._append_app_name_and_version(path)
+
+    @property
+    def site_data_dir(self) -> str:
+        """
+        :return: data directories shared by users (if `multipath ` is
+         enabled and ``XDG_DATA_DIR`` is set and a multi path the response is also a multi path separated by the OS
+         path separator), e.g. ``/usr/local/share/$appname/$version`` or ``/usr/share/$appname/$version``
+        """
+        # XDG default for $XDG_DATA_DIRS; only first, if multipath is False
+        path = os.environ.get("XDG_DATA_DIRS", "")
+        if not path.strip():
+            path = f"/usr/local/share{os.pathsep}/usr/share"
+        return self._with_multi_path(path)
+
+    def _with_multi_path(self, path: str) -> str:
+        path_list = path.split(os.pathsep)
+        if not self.multipath:
+            path_list = path_list[0:1]
+        path_list = [self._append_app_name_and_version(os.path.expanduser(p)) for p in path_list]
+        return os.pathsep.join(path_list)
+
+    @property
+    def user_config_dir(self) -> str:
+        """
+        :return: config directory tied to the user, e.g. ``~/.config/$appname/$version`` or
+         ``$XDG_CONFIG_HOME/$appname/$version``
+        """
+        path = os.environ.get("XDG_CONFIG_HOME", "")
+        if not path.strip():
+            path = os.path.expanduser("~/.config")
+        return self._append_app_name_and_version(path)
+
+    @property
+    def site_config_dir(self) -> str:
+        """
+        :return: config directories shared by users (if `multipath `
+         is enabled and ``XDG_DATA_DIR`` is set and a multi path the response is also a multi path separated by the OS
+         path separator), e.g. ``/etc/xdg/$appname/$version``
+        """
+        # XDG default for $XDG_CONFIG_DIRS only first, if multipath is False
+        path = os.environ.get("XDG_CONFIG_DIRS", "")
+        if not path.strip():
+            path = "/etc/xdg"
+        return self._with_multi_path(path)
+
+    @property
+    def user_cache_dir(self) -> str:
+        """
+        :return: cache directory tied to the user, e.g. ``~/.cache/$appname/$version`` or
+         ``~/$XDG_CACHE_HOME/$appname/$version``
+        """
+        path = os.environ.get("XDG_CACHE_HOME", "")
+        if not path.strip():
+            path = os.path.expanduser("~/.cache")
+        return self._append_app_name_and_version(path)
+
+    @property
+    def user_state_dir(self) -> str:
+        """
+        :return: state directory tied to the user, e.g. ``~/.local/state/$appname/$version`` or
+         ``$XDG_STATE_HOME/$appname/$version``
+        """
+        path = os.environ.get("XDG_STATE_HOME", "")
+        if not path.strip():
+            path = os.path.expanduser("~/.local/state")
+        return self._append_app_name_and_version(path)
+
+    @property
+    def user_log_dir(self) -> str:
+        """
+        :return: log directory tied to the user, same as `user_state_dir` if not opinionated else ``log`` in it
+        """
+        path = self.user_state_dir
+        if self.opinion:
+            path = os.path.join(path, "log")
+        return path
+
+    @property
+    def user_documents_dir(self) -> str:
+        """
+        :return: documents directory tied to the user, e.g. ``~/Documents``
+        """
+        documents_dir = _get_user_dirs_folder("XDG_DOCUMENTS_DIR")
+        if documents_dir is None:
+            documents_dir = os.environ.get("XDG_DOCUMENTS_DIR", "").strip()
+            if not documents_dir:
+                documents_dir = os.path.expanduser("~/Documents")
+
+        return documents_dir
+
+    @property
+    def user_runtime_dir(self) -> str:
+        """
+        :return: runtime directory tied to the user, e.g. ``/run/user/$(id -u)/$appname/$version`` or
+         ``$XDG_RUNTIME_DIR/$appname/$version``
+        """
+        path = os.environ.get("XDG_RUNTIME_DIR", "")
+        if not path.strip():
+            path = f"/run/user/{getuid()}"
+        return self._append_app_name_and_version(path)
+
+    @property
+    def site_data_path(self) -> Path:
+        """:return: data path shared by users. Only return first item, even if ``multipath`` is set to ``True``"""
+        return self._first_item_as_path_if_multipath(self.site_data_dir)
+
+    @property
+    def site_config_path(self) -> Path:
+        """:return: config path shared by the users. Only return first item, even if ``multipath`` is set to ``True``"""
+        return self._first_item_as_path_if_multipath(self.site_config_dir)
+
+    def _first_item_as_path_if_multipath(self, directory: str) -> Path:
+        if self.multipath:
+            # If multipath is True, the first path is returned.
+            directory = directory.split(os.pathsep)[0]
+        return Path(directory)
+
+
+def _get_user_dirs_folder(key: str) -> str | None:
+    """Return directory from user-dirs.dirs config file. See https://freedesktop.org/wiki/Software/xdg-user-dirs/"""
+    user_dirs_config_path = os.path.join(Unix().user_config_dir, "user-dirs.dirs")
+    if os.path.exists(user_dirs_config_path):
+        parser = ConfigParser()
+
+        with open(user_dirs_config_path) as stream:
+            # Add fake section header, so ConfigParser doesn't complain
+            parser.read_string(f"[top]\n{stream.read()}")
+
+        if key not in parser["top"]:
+            return None
+
+        path = parser["top"][key].strip('"')
+        # Handle relative home paths
+        path = path.replace("$HOME", os.path.expanduser("~"))
+        return path
+
+    return None
+
+
+__all__ = [
+    "Unix",
+]
diff --git a/venv/Lib/site-packages/pkg_resources/_vendor/platformdirs/version.py b/venv/Lib/site-packages/pkg_resources/_vendor/platformdirs/version.py
new file mode 100644
index 0000000..9f6eb98
--- /dev/null
+++ b/venv/Lib/site-packages/pkg_resources/_vendor/platformdirs/version.py
@@ -0,0 +1,4 @@
+# file generated by setuptools_scm
+# don't change, don't track in version control
+__version__ = version = '2.6.2'
+__version_tuple__ = version_tuple = (2, 6, 2)
diff --git a/venv/Lib/site-packages/pkg_resources/_vendor/platformdirs/windows.py b/venv/Lib/site-packages/pkg_resources/_vendor/platformdirs/windows.py
new file mode 100644
index 0000000..d5c27b3
--- /dev/null
+++ b/venv/Lib/site-packages/pkg_resources/_vendor/platformdirs/windows.py
@@ -0,0 +1,184 @@
+from __future__ import annotations
+
+import ctypes
+import os
+import sys
+from functools import lru_cache
+from typing import Callable
+
+from .api import PlatformDirsABC
+
+
+class Windows(PlatformDirsABC):
+    """`MSDN on where to store app data files
+    `_.
+    Makes use of the
+    `appname `,
+    `appauthor `,
+    `version `,
+    `roaming `,
+    `opinion `."""
+
+    @property
+    def user_data_dir(self) -> str:
+        """
+        :return: data directory tied to the user, e.g.
+         ``%USERPROFILE%\\AppData\\Local\\$appauthor\\$appname`` (not roaming) or
+         ``%USERPROFILE%\\AppData\\Roaming\\$appauthor\\$appname`` (roaming)
+        """
+        const = "CSIDL_APPDATA" if self.roaming else "CSIDL_LOCAL_APPDATA"
+        path = os.path.normpath(get_win_folder(const))
+        return self._append_parts(path)
+
+    def _append_parts(self, path: str, *, opinion_value: str | None = None) -> str:
+        params = []
+        if self.appname:
+            if self.appauthor is not False:
+                author = self.appauthor or self.appname
+                params.append(author)
+            params.append(self.appname)
+            if opinion_value is not None and self.opinion:
+                params.append(opinion_value)
+            if self.version:
+                params.append(self.version)
+        return os.path.join(path, *params)
+
+    @property
+    def site_data_dir(self) -> str:
+        """:return: data directory shared by users, e.g. ``C:\\ProgramData\\$appauthor\\$appname``"""
+        path = os.path.normpath(get_win_folder("CSIDL_COMMON_APPDATA"))
+        return self._append_parts(path)
+
+    @property
+    def user_config_dir(self) -> str:
+        """:return: config directory tied to the user, same as `user_data_dir`"""
+        return self.user_data_dir
+
+    @property
+    def site_config_dir(self) -> str:
+        """:return: config directory shared by the users, same as `site_data_dir`"""
+        return self.site_data_dir
+
+    @property
+    def user_cache_dir(self) -> str:
+        """
+        :return: cache directory tied to the user (if opinionated with ``Cache`` folder within ``$appname``) e.g.
+         ``%USERPROFILE%\\AppData\\Local\\$appauthor\\$appname\\Cache\\$version``
+        """
+        path = os.path.normpath(get_win_folder("CSIDL_LOCAL_APPDATA"))
+        return self._append_parts(path, opinion_value="Cache")
+
+    @property
+    def user_state_dir(self) -> str:
+        """:return: state directory tied to the user, same as `user_data_dir`"""
+        return self.user_data_dir
+
+    @property
+    def user_log_dir(self) -> str:
+        """
+        :return: log directory tied to the user, same as `user_data_dir` if not opinionated else ``Logs`` in it
+        """
+        path = self.user_data_dir
+        if self.opinion:
+            path = os.path.join(path, "Logs")
+        return path
+
+    @property
+    def user_documents_dir(self) -> str:
+        """
+        :return: documents directory tied to the user e.g. ``%USERPROFILE%\\Documents``
+        """
+        return os.path.normpath(get_win_folder("CSIDL_PERSONAL"))
+
+    @property
+    def user_runtime_dir(self) -> str:
+        """
+        :return: runtime directory tied to the user, e.g.
+         ``%USERPROFILE%\\AppData\\Local\\Temp\\$appauthor\\$appname``
+        """
+        path = os.path.normpath(os.path.join(get_win_folder("CSIDL_LOCAL_APPDATA"), "Temp"))
+        return self._append_parts(path)
+
+
+def get_win_folder_from_env_vars(csidl_name: str) -> str:
+    """Get folder from environment variables."""
+    if csidl_name == "CSIDL_PERSONAL":  # does not have an environment name
+        return os.path.join(os.path.normpath(os.environ["USERPROFILE"]), "Documents")
+
+    env_var_name = {
+        "CSIDL_APPDATA": "APPDATA",
+        "CSIDL_COMMON_APPDATA": "ALLUSERSPROFILE",
+        "CSIDL_LOCAL_APPDATA": "LOCALAPPDATA",
+    }.get(csidl_name)
+    if env_var_name is None:
+        raise ValueError(f"Unknown CSIDL name: {csidl_name}")
+    result = os.environ.get(env_var_name)
+    if result is None:
+        raise ValueError(f"Unset environment variable: {env_var_name}")
+    return result
+
+
+def get_win_folder_from_registry(csidl_name: str) -> str:
+    """Get folder from the registry.
+
+    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.
+    """
+    shell_folder_name = {
+        "CSIDL_APPDATA": "AppData",
+        "CSIDL_COMMON_APPDATA": "Common AppData",
+        "CSIDL_LOCAL_APPDATA": "Local AppData",
+        "CSIDL_PERSONAL": "Personal",
+    }.get(csidl_name)
+    if shell_folder_name is None:
+        raise ValueError(f"Unknown CSIDL name: {csidl_name}")
+    if sys.platform != "win32":  # only needed for mypy type checker to know that this code runs only on Windows
+        raise NotImplementedError
+    import winreg
+
+    key = winreg.OpenKey(winreg.HKEY_CURRENT_USER, r"Software\Microsoft\Windows\CurrentVersion\Explorer\Shell Folders")
+    directory, _ = winreg.QueryValueEx(key, shell_folder_name)
+    return str(directory)
+
+
+def get_win_folder_via_ctypes(csidl_name: str) -> str:
+    """Get folder with ctypes."""
+    csidl_const = {
+        "CSIDL_APPDATA": 26,
+        "CSIDL_COMMON_APPDATA": 35,
+        "CSIDL_LOCAL_APPDATA": 28,
+        "CSIDL_PERSONAL": 5,
+    }.get(csidl_name)
+    if csidl_const is None:
+        raise ValueError(f"Unknown CSIDL name: {csidl_name}")
+
+    buf = ctypes.create_unicode_buffer(1024)
+    windll = getattr(ctypes, "windll")  # noqa: B009 # using getattr to avoid false positive with mypy type checker
+    windll.shell32.SHGetFolderPathW(None, csidl_const, None, 0, buf)
+
+    # Downgrade to short path name if it has highbit chars.
+    if any(ord(c) > 255 for c in buf):
+        buf2 = ctypes.create_unicode_buffer(1024)
+        if windll.kernel32.GetShortPathNameW(buf.value, buf2, 1024):
+            buf = buf2
+
+    return buf.value
+
+
+def _pick_get_win_folder() -> Callable[[str], str]:
+    if hasattr(ctypes, "windll"):
+        return get_win_folder_via_ctypes
+    try:
+        import winreg  # noqa: F401
+    except ImportError:
+        return get_win_folder_from_env_vars
+    else:
+        return get_win_folder_from_registry
+
+
+get_win_folder = lru_cache(maxsize=None)(_pick_get_win_folder())
+
+__all__ = [
+    "Windows",
+]
diff --git a/venv/Lib/site-packages/pkg_resources/_vendor/typing_extensions.py b/venv/Lib/site-packages/pkg_resources/_vendor/typing_extensions.py
new file mode 100644
index 0000000..ef42417
--- /dev/null
+++ b/venv/Lib/site-packages/pkg_resources/_vendor/typing_extensions.py
@@ -0,0 +1,2209 @@
+import abc
+import collections
+import collections.abc
+import functools
+import operator
+import sys
+import types as _types
+import typing
+
+
+__all__ = [
+    # Super-special typing primitives.
+    'Any',
+    'ClassVar',
+    'Concatenate',
+    'Final',
+    'LiteralString',
+    'ParamSpec',
+    'ParamSpecArgs',
+    'ParamSpecKwargs',
+    'Self',
+    'Type',
+    'TypeVar',
+    'TypeVarTuple',
+    'Unpack',
+
+    # ABCs (from collections.abc).
+    'Awaitable',
+    'AsyncIterator',
+    'AsyncIterable',
+    'Coroutine',
+    'AsyncGenerator',
+    'AsyncContextManager',
+    'ChainMap',
+
+    # Concrete collection types.
+    'ContextManager',
+    'Counter',
+    'Deque',
+    'DefaultDict',
+    'NamedTuple',
+    'OrderedDict',
+    'TypedDict',
+
+    # Structural checks, a.k.a. protocols.
+    'SupportsIndex',
+
+    # One-off things.
+    'Annotated',
+    'assert_never',
+    'assert_type',
+    'clear_overloads',
+    'dataclass_transform',
+    'get_overloads',
+    'final',
+    'get_args',
+    'get_origin',
+    'get_type_hints',
+    'IntVar',
+    'is_typeddict',
+    'Literal',
+    'NewType',
+    'overload',
+    'override',
+    'Protocol',
+    'reveal_type',
+    'runtime',
+    'runtime_checkable',
+    'Text',
+    'TypeAlias',
+    'TypeGuard',
+    'TYPE_CHECKING',
+    'Never',
+    'NoReturn',
+    'Required',
+    'NotRequired',
+]
+
+# for backward compatibility
+PEP_560 = True
+GenericMeta = type
+
+# The functions below are modified copies of typing internal helpers.
+# They are needed by _ProtocolMeta and they provide support for PEP 646.
+
+_marker = object()
+
+
+def _check_generic(cls, parameters, elen=_marker):
+    """Check correct count for parameters of a generic cls (internal helper).
+    This gives a nice error message in case of count mismatch.
+    """
+    if not elen:
+        raise TypeError(f"{cls} is not a generic class")
+    if elen is _marker:
+        if not hasattr(cls, "__parameters__") or not cls.__parameters__:
+            raise TypeError(f"{cls} is not a generic class")
+        elen = len(cls.__parameters__)
+    alen = len(parameters)
+    if alen != elen:
+        if hasattr(cls, "__parameters__"):
+            parameters = [p for p in cls.__parameters__ if not _is_unpack(p)]
+            num_tv_tuples = sum(isinstance(p, TypeVarTuple) for p in parameters)
+            if (num_tv_tuples > 0) and (alen >= elen - num_tv_tuples):
+                return
+        raise TypeError(f"Too {'many' if alen > elen else 'few'} parameters for {cls};"
+                        f" actual {alen}, expected {elen}")
+
+
+if sys.version_info >= (3, 10):
+    def _should_collect_from_parameters(t):
+        return isinstance(
+            t, (typing._GenericAlias, _types.GenericAlias, _types.UnionType)
+        )
+elif sys.version_info >= (3, 9):
+    def _should_collect_from_parameters(t):
+        return isinstance(t, (typing._GenericAlias, _types.GenericAlias))
+else:
+    def _should_collect_from_parameters(t):
+        return isinstance(t, typing._GenericAlias) and not t._special
+
+
+def _collect_type_vars(types, typevar_types=None):
+    """Collect all type variable contained in types in order of
+    first appearance (lexicographic order). For example::
+
+        _collect_type_vars((T, List[S, T])) == (T, S)
+    """
+    if typevar_types is None:
+        typevar_types = typing.TypeVar
+    tvars = []
+    for t in types:
+        if (
+            isinstance(t, typevar_types) and
+            t not in tvars and
+            not _is_unpack(t)
+        ):
+            tvars.append(t)
+        if _should_collect_from_parameters(t):
+            tvars.extend([t for t in t.__parameters__ if t not in tvars])
+    return tuple(tvars)
+
+
+NoReturn = typing.NoReturn
+
+# Some unconstrained type variables.  These are used by the container types.
+# (These are not for export.)
+T = typing.TypeVar('T')  # Any type.
+KT = typing.TypeVar('KT')  # Key type.
+VT = typing.TypeVar('VT')  # Value type.
+T_co = typing.TypeVar('T_co', covariant=True)  # Any type covariant containers.
+T_contra = typing.TypeVar('T_contra', contravariant=True)  # Ditto contravariant.
+
+
+if sys.version_info >= (3, 11):
+    from typing import Any
+else:
+
+    class _AnyMeta(type):
+        def __instancecheck__(self, obj):
+            if self is Any:
+                raise TypeError("typing_extensions.Any cannot be used with isinstance()")
+            return super().__instancecheck__(obj)
+
+        def __repr__(self):
+            if self is Any:
+                return "typing_extensions.Any"
+            return super().__repr__()
+
+    class Any(metaclass=_AnyMeta):
+        """Special type indicating an unconstrained type.
+        - Any is compatible with every type.
+        - Any assumed to have all methods.
+        - All values assumed to be instances of Any.
+        Note that all the above statements are true from the point of view of
+        static type checkers. At runtime, Any should not be used with instance
+        checks.
+        """
+        def __new__(cls, *args, **kwargs):
+            if cls is Any:
+                raise TypeError("Any cannot be instantiated")
+            return super().__new__(cls, *args, **kwargs)
+
+
+ClassVar = typing.ClassVar
+
+# On older versions of typing there is an internal class named "Final".
+# 3.8+
+if hasattr(typing, 'Final') and sys.version_info[:2] >= (3, 7):
+    Final = typing.Final
+# 3.7
+else:
+    class _FinalForm(typing._SpecialForm, _root=True):
+
+        def __repr__(self):
+            return 'typing_extensions.' + self._name
+
+        def __getitem__(self, parameters):
+            item = typing._type_check(parameters,
+                                      f'{self._name} accepts only a single type.')
+            return typing._GenericAlias(self, (item,))
+
+    Final = _FinalForm('Final',
+                       doc="""A special typing construct to indicate that a name
+                       cannot be re-assigned or overridden in a subclass.
+                       For example:
+
+                           MAX_SIZE: Final = 9000
+                           MAX_SIZE += 1  # Error reported by type checker
+
+                           class Connection:
+                               TIMEOUT: Final[int] = 10
+                           class FastConnector(Connection):
+                               TIMEOUT = 1  # Error reported by type checker
+
+                       There is no runtime checking of these properties.""")
+
+if sys.version_info >= (3, 11):
+    final = typing.final
+else:
+    # @final exists in 3.8+, but we backport it for all versions
+    # before 3.11 to keep support for the __final__ attribute.
+    # See https://bugs.python.org/issue46342
+    def final(f):
+        """This decorator can be used to indicate to type checkers that
+        the decorated method cannot be overridden, and decorated class
+        cannot be subclassed. For example:
+
+            class Base:
+                @final
+                def done(self) -> None:
+                    ...
+            class Sub(Base):
+                def done(self) -> None:  # Error reported by type checker
+                    ...
+            @final
+            class Leaf:
+                ...
+            class Other(Leaf):  # Error reported by type checker
+                ...
+
+        There is no runtime checking of these properties. The decorator
+        sets the ``__final__`` attribute to ``True`` on the decorated object
+        to allow runtime introspection.
+        """
+        try:
+            f.__final__ = True
+        except (AttributeError, TypeError):
+            # Skip the attribute silently if it is not writable.
+            # AttributeError happens if the object has __slots__ or a
+            # read-only property, TypeError if it's a builtin class.
+            pass
+        return f
+
+
+def IntVar(name):
+    return typing.TypeVar(name)
+
+
+# 3.8+:
+if hasattr(typing, 'Literal'):
+    Literal = typing.Literal
+# 3.7:
+else:
+    class _LiteralForm(typing._SpecialForm, _root=True):
+
+        def __repr__(self):
+            return 'typing_extensions.' + self._name
+
+        def __getitem__(self, parameters):
+            return typing._GenericAlias(self, parameters)
+
+    Literal = _LiteralForm('Literal',
+                           doc="""A type that can be used to indicate to type checkers
+                           that the corresponding value has a value literally equivalent
+                           to the provided parameter. For example:
+
+                               var: Literal[4] = 4
+
+                           The type checker understands that 'var' is literally equal to
+                           the value 4 and no other value.
+
+                           Literal[...] cannot be subclassed. There is no runtime
+                           checking verifying that the parameter is actually a value
+                           instead of a type.""")
+
+
+_overload_dummy = typing._overload_dummy  # noqa
+
+
+if hasattr(typing, "get_overloads"):  # 3.11+
+    overload = typing.overload
+    get_overloads = typing.get_overloads
+    clear_overloads = typing.clear_overloads
+else:
+    # {module: {qualname: {firstlineno: func}}}
+    _overload_registry = collections.defaultdict(
+        functools.partial(collections.defaultdict, dict)
+    )
+
+    def overload(func):
+        """Decorator for overloaded functions/methods.
+
+        In a stub file, place two or more stub definitions for the same
+        function in a row, each decorated with @overload.  For example:
+
+        @overload
+        def utf8(value: None) -> None: ...
+        @overload
+        def utf8(value: bytes) -> bytes: ...
+        @overload
+        def utf8(value: str) -> bytes: ...
+
+        In a non-stub file (i.e. a regular .py file), do the same but
+        follow it with an implementation.  The implementation should *not*
+        be decorated with @overload.  For example:
+
+        @overload
+        def utf8(value: None) -> None: ...
+        @overload
+        def utf8(value: bytes) -> bytes: ...
+        @overload
+        def utf8(value: str) -> bytes: ...
+        def utf8(value):
+            # implementation goes here
+
+        The overloads for a function can be retrieved at runtime using the
+        get_overloads() function.
+        """
+        # classmethod and staticmethod
+        f = getattr(func, "__func__", func)
+        try:
+            _overload_registry[f.__module__][f.__qualname__][
+                f.__code__.co_firstlineno
+            ] = func
+        except AttributeError:
+            # Not a normal function; ignore.
+            pass
+        return _overload_dummy
+
+    def get_overloads(func):
+        """Return all defined overloads for *func* as a sequence."""
+        # classmethod and staticmethod
+        f = getattr(func, "__func__", func)
+        if f.__module__ not in _overload_registry:
+            return []
+        mod_dict = _overload_registry[f.__module__]
+        if f.__qualname__ not in mod_dict:
+            return []
+        return list(mod_dict[f.__qualname__].values())
+
+    def clear_overloads():
+        """Clear all overloads in the registry."""
+        _overload_registry.clear()
+
+
+# This is not a real generic class.  Don't use outside annotations.
+Type = typing.Type
+
+# Various ABCs mimicking those in collections.abc.
+# A few are simply re-exported for completeness.
+
+
+Awaitable = typing.Awaitable
+Coroutine = typing.Coroutine
+AsyncIterable = typing.AsyncIterable
+AsyncIterator = typing.AsyncIterator
+Deque = typing.Deque
+ContextManager = typing.ContextManager
+AsyncContextManager = typing.AsyncContextManager
+DefaultDict = typing.DefaultDict
+
+# 3.7.2+
+if hasattr(typing, 'OrderedDict'):
+    OrderedDict = typing.OrderedDict
+# 3.7.0-3.7.2
+else:
+    OrderedDict = typing._alias(collections.OrderedDict, (KT, VT))
+
+Counter = typing.Counter
+ChainMap = typing.ChainMap
+AsyncGenerator = typing.AsyncGenerator
+NewType = typing.NewType
+Text = typing.Text
+TYPE_CHECKING = typing.TYPE_CHECKING
+
+
+_PROTO_WHITELIST = ['Callable', 'Awaitable',
+                    'Iterable', 'Iterator', 'AsyncIterable', 'AsyncIterator',
+                    'Hashable', 'Sized', 'Container', 'Collection', 'Reversible',
+                    'ContextManager', 'AsyncContextManager']
+
+
+def _get_protocol_attrs(cls):
+    attrs = set()
+    for base in cls.__mro__[:-1]:  # without object
+        if base.__name__ in ('Protocol', 'Generic'):
+            continue
+        annotations = getattr(base, '__annotations__', {})
+        for attr in list(base.__dict__.keys()) + list(annotations.keys()):
+            if (not attr.startswith('_abc_') and attr not in (
+                    '__abstractmethods__', '__annotations__', '__weakref__',
+                    '_is_protocol', '_is_runtime_protocol', '__dict__',
+                    '__args__', '__slots__',
+                    '__next_in_mro__', '__parameters__', '__origin__',
+                    '__orig_bases__', '__extra__', '__tree_hash__',
+                    '__doc__', '__subclasshook__', '__init__', '__new__',
+                    '__module__', '_MutableMapping__marker', '_gorg')):
+                attrs.add(attr)
+    return attrs
+
+
+def _is_callable_members_only(cls):
+    return all(callable(getattr(cls, attr, None)) for attr in _get_protocol_attrs(cls))
+
+
+def _maybe_adjust_parameters(cls):
+    """Helper function used in Protocol.__init_subclass__ and _TypedDictMeta.__new__.
+
+    The contents of this function are very similar
+    to logic found in typing.Generic.__init_subclass__
+    on the CPython main branch.
+    """
+    tvars = []
+    if '__orig_bases__' in cls.__dict__:
+        tvars = typing._collect_type_vars(cls.__orig_bases__)
+        # Look for Generic[T1, ..., Tn] or Protocol[T1, ..., Tn].
+        # If found, tvars must be a subset of it.
+        # If not found, tvars is it.
+        # Also check for and reject plain Generic,
+        # and reject multiple Generic[...] and/or Protocol[...].
+        gvars = None
+        for base in cls.__orig_bases__:
+            if (isinstance(base, typing._GenericAlias) and
+                    base.__origin__ in (typing.Generic, Protocol)):
+                # for error messages
+                the_base = base.__origin__.__name__
+                if gvars is not None:
+                    raise TypeError(
+                        "Cannot inherit from Generic[...]"
+                        " and/or Protocol[...] multiple types.")
+                gvars = base.__parameters__
+        if gvars is None:
+            gvars = tvars
+        else:
+            tvarset = set(tvars)
+            gvarset = set(gvars)
+            if not tvarset <= gvarset:
+                s_vars = ', '.join(str(t) for t in tvars if t not in gvarset)
+                s_args = ', '.join(str(g) for g in gvars)
+                raise TypeError(f"Some type variables ({s_vars}) are"
+                                f" not listed in {the_base}[{s_args}]")
+            tvars = gvars
+    cls.__parameters__ = tuple(tvars)
+
+
+# 3.8+
+if hasattr(typing, 'Protocol'):
+    Protocol = typing.Protocol
+# 3.7
+else:
+
+    def _no_init(self, *args, **kwargs):
+        if type(self)._is_protocol:
+            raise TypeError('Protocols cannot be instantiated')
+
+    class _ProtocolMeta(abc.ABCMeta):  # noqa: B024
+        # This metaclass is a bit unfortunate and exists only because of the lack
+        # of __instancehook__.
+        def __instancecheck__(cls, instance):
+            # We need this method for situations where attributes are
+            # assigned in __init__.
+            if ((not getattr(cls, '_is_protocol', False) or
+                 _is_callable_members_only(cls)) and
+                    issubclass(instance.__class__, cls)):
+                return True
+            if cls._is_protocol:
+                if all(hasattr(instance, attr) and
+                       (not callable(getattr(cls, attr, None)) or
+                        getattr(instance, attr) is not None)
+                       for attr in _get_protocol_attrs(cls)):
+                    return True
+            return super().__instancecheck__(instance)
+
+    class Protocol(metaclass=_ProtocolMeta):
+        # There is quite a lot of overlapping code with typing.Generic.
+        # Unfortunately it is hard to avoid this while these live in two different
+        # modules. The duplicated code will be removed when Protocol is moved to typing.
+        """Base class for protocol classes. Protocol classes are defined as::
+
+            class Proto(Protocol):
+                def meth(self) -> int:
+                    ...
+
+        Such classes are primarily used with static type checkers that recognize
+        structural subtyping (static duck-typing), for example::
+
+            class C:
+                def meth(self) -> int:
+                    return 0
+
+            def func(x: Proto) -> int:
+                return x.meth()
+
+            func(C())  # Passes static type check
+
+        See PEP 544 for details. Protocol classes decorated with
+        @typing_extensions.runtime act as simple-minded runtime protocol that checks
+        only the presence of given attributes, ignoring their type signatures.
+
+        Protocol classes can be generic, they are defined as::
+
+            class GenProto(Protocol[T]):
+                def meth(self) -> T:
+                    ...
+        """
+        __slots__ = ()
+        _is_protocol = True
+
+        def __new__(cls, *args, **kwds):
+            if cls is Protocol:
+                raise TypeError("Type Protocol cannot be instantiated; "
+                                "it can only be used as a base class")
+            return super().__new__(cls)
+
+        @typing._tp_cache
+        def __class_getitem__(cls, params):
+            if not isinstance(params, tuple):
+                params = (params,)
+            if not params and cls is not typing.Tuple:
+                raise TypeError(
+                    f"Parameter list to {cls.__qualname__}[...] cannot be empty")
+            msg = "Parameters to generic types must be types."
+            params = tuple(typing._type_check(p, msg) for p in params)  # noqa
+            if cls is Protocol:
+                # Generic can only be subscripted with unique type variables.
+                if not all(isinstance(p, typing.TypeVar) for p in params):
+                    i = 0
+                    while isinstance(params[i], typing.TypeVar):
+                        i += 1
+                    raise TypeError(
+                        "Parameters to Protocol[...] must all be type variables."
+                        f" Parameter {i + 1} is {params[i]}")
+                if len(set(params)) != len(params):
+                    raise TypeError(
+                        "Parameters to Protocol[...] must all be unique")
+            else:
+                # Subscripting a regular Generic subclass.
+                _check_generic(cls, params, len(cls.__parameters__))
+            return typing._GenericAlias(cls, params)
+
+        def __init_subclass__(cls, *args, **kwargs):
+            if '__orig_bases__' in cls.__dict__:
+                error = typing.Generic in cls.__orig_bases__
+            else:
+                error = typing.Generic in cls.__bases__
+            if error:
+                raise TypeError("Cannot inherit from plain Generic")
+            _maybe_adjust_parameters(cls)
+
+            # Determine if this is a protocol or a concrete subclass.
+            if not cls.__dict__.get('_is_protocol', None):
+                cls._is_protocol = any(b is Protocol for b in cls.__bases__)
+
+            # Set (or override) the protocol subclass hook.
+            def _proto_hook(other):
+                if not cls.__dict__.get('_is_protocol', None):
+                    return NotImplemented
+                if not getattr(cls, '_is_runtime_protocol', False):
+                    if sys._getframe(2).f_globals['__name__'] in ['abc', 'functools']:
+                        return NotImplemented
+                    raise TypeError("Instance and class checks can only be used with"
+                                    " @runtime protocols")
+                if not _is_callable_members_only(cls):
+                    if sys._getframe(2).f_globals['__name__'] in ['abc', 'functools']:
+                        return NotImplemented
+                    raise TypeError("Protocols with non-method members"
+                                    " don't support issubclass()")
+                if not isinstance(other, type):
+                    # Same error as for issubclass(1, int)
+                    raise TypeError('issubclass() arg 1 must be a class')
+                for attr in _get_protocol_attrs(cls):
+                    for base in other.__mro__:
+                        if attr in base.__dict__:
+                            if base.__dict__[attr] is None:
+                                return NotImplemented
+                            break
+                        annotations = getattr(base, '__annotations__', {})
+                        if (isinstance(annotations, typing.Mapping) and
+                                attr in annotations and
+                                isinstance(other, _ProtocolMeta) and
+                                other._is_protocol):
+                            break
+                    else:
+                        return NotImplemented
+                return True
+            if '__subclasshook__' not in cls.__dict__:
+                cls.__subclasshook__ = _proto_hook
+
+            # We have nothing more to do for non-protocols.
+            if not cls._is_protocol:
+                return
+
+            # Check consistency of bases.
+            for base in cls.__bases__:
+                if not (base in (object, typing.Generic) or
+                        base.__module__ == 'collections.abc' and
+                        base.__name__ in _PROTO_WHITELIST or
+                        isinstance(base, _ProtocolMeta) and base._is_protocol):
+                    raise TypeError('Protocols can only inherit from other'
+                                    f' protocols, got {repr(base)}')
+            cls.__init__ = _no_init
+
+
+# 3.8+
+if hasattr(typing, 'runtime_checkable'):
+    runtime_checkable = typing.runtime_checkable
+# 3.7
+else:
+    def runtime_checkable(cls):
+        """Mark a protocol class as a runtime protocol, so that it
+        can be used with isinstance() and issubclass(). Raise TypeError
+        if applied to a non-protocol class.
+
+        This allows a simple-minded structural check very similar to the
+        one-offs in collections.abc such as Hashable.
+        """
+        if not isinstance(cls, _ProtocolMeta) or not cls._is_protocol:
+            raise TypeError('@runtime_checkable can be only applied to protocol classes,'
+                            f' got {cls!r}')
+        cls._is_runtime_protocol = True
+        return cls
+
+
+# Exists for backwards compatibility.
+runtime = runtime_checkable
+
+
+# 3.8+
+if hasattr(typing, 'SupportsIndex'):
+    SupportsIndex = typing.SupportsIndex
+# 3.7
+else:
+    @runtime_checkable
+    class SupportsIndex(Protocol):
+        __slots__ = ()
+
+        @abc.abstractmethod
+        def __index__(self) -> int:
+            pass
+
+
+if hasattr(typing, "Required"):
+    # The standard library TypedDict in Python 3.8 does not store runtime information
+    # about which (if any) keys are optional.  See https://bugs.python.org/issue38834
+    # The standard library TypedDict in Python 3.9.0/1 does not honour the "total"
+    # keyword with old-style TypedDict().  See https://bugs.python.org/issue42059
+    # The standard library TypedDict below Python 3.11 does not store runtime
+    # information about optional and required keys when using Required or NotRequired.
+    # Generic TypedDicts are also impossible using typing.TypedDict on Python <3.11.
+    TypedDict = typing.TypedDict
+    _TypedDictMeta = typing._TypedDictMeta
+    is_typeddict = typing.is_typeddict
+else:
+    def _check_fails(cls, other):
+        try:
+            if sys._getframe(1).f_globals['__name__'] not in ['abc',
+                                                              'functools',
+                                                              'typing']:
+                # Typed dicts are only for static structural subtyping.
+                raise TypeError('TypedDict does not support instance and class checks')
+        except (AttributeError, ValueError):
+            pass
+        return False
+
+    def _dict_new(*args, **kwargs):
+        if not args:
+            raise TypeError('TypedDict.__new__(): not enough arguments')
+        _, args = args[0], args[1:]  # allow the "cls" keyword be passed
+        return dict(*args, **kwargs)
+
+    _dict_new.__text_signature__ = '($cls, _typename, _fields=None, /, **kwargs)'
+
+    def _typeddict_new(*args, total=True, **kwargs):
+        if not args:
+            raise TypeError('TypedDict.__new__(): not enough arguments')
+        _, args = args[0], args[1:]  # allow the "cls" keyword be passed
+        if args:
+            typename, args = args[0], args[1:]  # allow the "_typename" keyword be passed
+        elif '_typename' in kwargs:
+            typename = kwargs.pop('_typename')
+            import warnings
+            warnings.warn("Passing '_typename' as keyword argument is deprecated",
+                          DeprecationWarning, stacklevel=2)
+        else:
+            raise TypeError("TypedDict.__new__() missing 1 required positional "
+                            "argument: '_typename'")
+        if args:
+            try:
+                fields, = args  # allow the "_fields" keyword be passed
+            except ValueError:
+                raise TypeError('TypedDict.__new__() takes from 2 to 3 '
+                                f'positional arguments but {len(args) + 2} '
+                                'were given')
+        elif '_fields' in kwargs and len(kwargs) == 1:
+            fields = kwargs.pop('_fields')
+            import warnings
+            warnings.warn("Passing '_fields' as keyword argument is deprecated",
+                          DeprecationWarning, stacklevel=2)
+        else:
+            fields = None
+
+        if fields is None:
+            fields = kwargs
+        elif kwargs:
+            raise TypeError("TypedDict takes either a dict or keyword arguments,"
+                            " but not both")
+
+        ns = {'__annotations__': dict(fields)}
+        try:
+            # Setting correct module is necessary to make typed dict classes pickleable.
+            ns['__module__'] = sys._getframe(1).f_globals.get('__name__', '__main__')
+        except (AttributeError, ValueError):
+            pass
+
+        return _TypedDictMeta(typename, (), ns, total=total)
+
+    _typeddict_new.__text_signature__ = ('($cls, _typename, _fields=None,'
+                                         ' /, *, total=True, **kwargs)')
+
+    class _TypedDictMeta(type):
+        def __init__(cls, name, bases, ns, total=True):
+            super().__init__(name, bases, ns)
+
+        def __new__(cls, name, bases, ns, total=True):
+            # Create new typed dict class object.
+            # This method is called directly when TypedDict is subclassed,
+            # or via _typeddict_new when TypedDict is instantiated. This way
+            # TypedDict supports all three syntaxes described in its docstring.
+            # Subclasses and instances of TypedDict return actual dictionaries
+            # via _dict_new.
+            ns['__new__'] = _typeddict_new if name == 'TypedDict' else _dict_new
+            # Don't insert typing.Generic into __bases__ here,
+            # or Generic.__init_subclass__ will raise TypeError
+            # in the super().__new__() call.
+            # Instead, monkey-patch __bases__ onto the class after it's been created.
+            tp_dict = super().__new__(cls, name, (dict,), ns)
+
+            if any(issubclass(base, typing.Generic) for base in bases):
+                tp_dict.__bases__ = (typing.Generic, dict)
+                _maybe_adjust_parameters(tp_dict)
+
+            annotations = {}
+            own_annotations = ns.get('__annotations__', {})
+            msg = "TypedDict('Name', {f0: t0, f1: t1, ...}); each t must be a type"
+            own_annotations = {
+                n: typing._type_check(tp, msg) for n, tp in own_annotations.items()
+            }
+            required_keys = set()
+            optional_keys = set()
+
+            for base in bases:
+                annotations.update(base.__dict__.get('__annotations__', {}))
+                required_keys.update(base.__dict__.get('__required_keys__', ()))
+                optional_keys.update(base.__dict__.get('__optional_keys__', ()))
+
+            annotations.update(own_annotations)
+            for annotation_key, annotation_type in own_annotations.items():
+                annotation_origin = get_origin(annotation_type)
+                if annotation_origin is Annotated:
+                    annotation_args = get_args(annotation_type)
+                    if annotation_args:
+                        annotation_type = annotation_args[0]
+                        annotation_origin = get_origin(annotation_type)
+
+                if annotation_origin is Required:
+                    required_keys.add(annotation_key)
+                elif annotation_origin is NotRequired:
+                    optional_keys.add(annotation_key)
+                elif total:
+                    required_keys.add(annotation_key)
+                else:
+                    optional_keys.add(annotation_key)
+
+            tp_dict.__annotations__ = annotations
+            tp_dict.__required_keys__ = frozenset(required_keys)
+            tp_dict.__optional_keys__ = frozenset(optional_keys)
+            if not hasattr(tp_dict, '__total__'):
+                tp_dict.__total__ = total
+            return tp_dict
+
+        __instancecheck__ = __subclasscheck__ = _check_fails
+
+    TypedDict = _TypedDictMeta('TypedDict', (dict,), {})
+    TypedDict.__module__ = __name__
+    TypedDict.__doc__ = \
+        """A simple typed name space. At runtime it is equivalent to a plain dict.
+
+        TypedDict creates a dictionary type that expects all of its
+        instances to have a certain set of keys, with each key
+        associated with a value of a consistent type. This expectation
+        is not checked at runtime but is only enforced by type checkers.
+        Usage::
+
+            class Point2D(TypedDict):
+                x: int
+                y: int
+                label: str
+
+            a: Point2D = {'x': 1, 'y': 2, 'label': 'good'}  # OK
+            b: Point2D = {'z': 3, 'label': 'bad'}           # Fails type check
+
+            assert Point2D(x=1, y=2, label='first') == dict(x=1, y=2, label='first')
+
+        The type info can be accessed via the Point2D.__annotations__ dict, and
+        the Point2D.__required_keys__ and Point2D.__optional_keys__ frozensets.
+        TypedDict supports two additional equivalent forms::
+
+            Point2D = TypedDict('Point2D', x=int, y=int, label=str)
+            Point2D = TypedDict('Point2D', {'x': int, 'y': int, 'label': str})
+
+        The class syntax is only supported in Python 3.6+, while two other
+        syntax forms work for Python 2.7 and 3.2+
+        """
+
+    if hasattr(typing, "_TypedDictMeta"):
+        _TYPEDDICT_TYPES = (typing._TypedDictMeta, _TypedDictMeta)
+    else:
+        _TYPEDDICT_TYPES = (_TypedDictMeta,)
+
+    def is_typeddict(tp):
+        """Check if an annotation is a TypedDict class
+
+        For example::
+            class Film(TypedDict):
+                title: str
+                year: int
+
+            is_typeddict(Film)  # => True
+            is_typeddict(Union[list, str])  # => False
+        """
+        return isinstance(tp, tuple(_TYPEDDICT_TYPES))
+
+
+if hasattr(typing, "assert_type"):
+    assert_type = typing.assert_type
+
+else:
+    def assert_type(__val, __typ):
+        """Assert (to the type checker) that the value is of the given type.
+
+        When the type checker encounters a call to assert_type(), it
+        emits an error if the value is not of the specified type::
+
+            def greet(name: str) -> None:
+                assert_type(name, str)  # ok
+                assert_type(name, int)  # type checker error
+
+        At runtime this returns the first argument unchanged and otherwise
+        does nothing.
+        """
+        return __val
+
+
+if hasattr(typing, "Required"):
+    get_type_hints = typing.get_type_hints
+else:
+    import functools
+    import types
+
+    # replaces _strip_annotations()
+    def _strip_extras(t):
+        """Strips Annotated, Required and NotRequired from a given type."""
+        if isinstance(t, _AnnotatedAlias):
+            return _strip_extras(t.__origin__)
+        if hasattr(t, "__origin__") and t.__origin__ in (Required, NotRequired):
+            return _strip_extras(t.__args__[0])
+        if isinstance(t, typing._GenericAlias):
+            stripped_args = tuple(_strip_extras(a) for a in t.__args__)
+            if stripped_args == t.__args__:
+                return t
+            return t.copy_with(stripped_args)
+        if hasattr(types, "GenericAlias") and isinstance(t, types.GenericAlias):
+            stripped_args = tuple(_strip_extras(a) for a in t.__args__)
+            if stripped_args == t.__args__:
+                return t
+            return types.GenericAlias(t.__origin__, stripped_args)
+        if hasattr(types, "UnionType") and isinstance(t, types.UnionType):
+            stripped_args = tuple(_strip_extras(a) for a in t.__args__)
+            if stripped_args == t.__args__:
+                return t
+            return functools.reduce(operator.or_, stripped_args)
+
+        return t
+
+    def get_type_hints(obj, globalns=None, localns=None, include_extras=False):
+        """Return type hints for an object.
+
+        This is often the same as obj.__annotations__, but it handles
+        forward references encoded as string literals, adds Optional[t] if a
+        default value equal to None is set and recursively replaces all
+        'Annotated[T, ...]', 'Required[T]' or 'NotRequired[T]' with 'T'
+        (unless 'include_extras=True').
+
+        The argument may be a module, class, method, or function. The annotations
+        are returned as a dictionary. For classes, annotations include also
+        inherited members.
+
+        TypeError is raised if the argument is not of a type that can contain
+        annotations, and an empty dictionary is returned if no annotations are
+        present.
+
+        BEWARE -- the behavior of globalns and localns is counterintuitive
+        (unless you are familiar with how eval() and exec() work).  The
+        search order is locals first, then globals.
+
+        - If no dict arguments are passed, an attempt is made to use the
+          globals from obj (or the respective module's globals for classes),
+          and these are also used as the locals.  If the object does not appear
+          to have globals, an empty dictionary is used.
+
+        - If one dict argument is passed, it is used for both globals and
+          locals.
+
+        - If two dict arguments are passed, they specify globals and
+          locals, respectively.
+        """
+        if hasattr(typing, "Annotated"):
+            hint = typing.get_type_hints(
+                obj, globalns=globalns, localns=localns, include_extras=True
+            )
+        else:
+            hint = typing.get_type_hints(obj, globalns=globalns, localns=localns)
+        if include_extras:
+            return hint
+        return {k: _strip_extras(t) for k, t in hint.items()}
+
+
+# Python 3.9+ has PEP 593 (Annotated)
+if hasattr(typing, 'Annotated'):
+    Annotated = typing.Annotated
+    # Not exported and not a public API, but needed for get_origin() and get_args()
+    # to work.
+    _AnnotatedAlias = typing._AnnotatedAlias
+# 3.7-3.8
+else:
+    class _AnnotatedAlias(typing._GenericAlias, _root=True):
+        """Runtime representation of an annotated type.
+
+        At its core 'Annotated[t, dec1, dec2, ...]' is an alias for the type 't'
+        with extra annotations. The alias behaves like a normal typing alias,
+        instantiating is the same as instantiating the underlying type, binding
+        it to types is also the same.
+        """
+        def __init__(self, origin, metadata):
+            if isinstance(origin, _AnnotatedAlias):
+                metadata = origin.__metadata__ + metadata
+                origin = origin.__origin__
+            super().__init__(origin, origin)
+            self.__metadata__ = metadata
+
+        def copy_with(self, params):
+            assert len(params) == 1
+            new_type = params[0]
+            return _AnnotatedAlias(new_type, self.__metadata__)
+
+        def __repr__(self):
+            return (f"typing_extensions.Annotated[{typing._type_repr(self.__origin__)}, "
+                    f"{', '.join(repr(a) for a in self.__metadata__)}]")
+
+        def __reduce__(self):
+            return operator.getitem, (
+                Annotated, (self.__origin__,) + self.__metadata__
+            )
+
+        def __eq__(self, other):
+            if not isinstance(other, _AnnotatedAlias):
+                return NotImplemented
+            if self.__origin__ != other.__origin__:
+                return False
+            return self.__metadata__ == other.__metadata__
+
+        def __hash__(self):
+            return hash((self.__origin__, self.__metadata__))
+
+    class Annotated:
+        """Add context specific metadata to a type.
+
+        Example: Annotated[int, runtime_check.Unsigned] indicates to the
+        hypothetical runtime_check module that this type is an unsigned int.
+        Every other consumer of this type can ignore this metadata and treat
+        this type as int.
+
+        The first argument to Annotated must be a valid type (and will be in
+        the __origin__ field), the remaining arguments are kept as a tuple in
+        the __extra__ field.
+
+        Details:
+
+        - It's an error to call `Annotated` with less than two arguments.
+        - Nested Annotated are flattened::
+
+            Annotated[Annotated[T, Ann1, Ann2], Ann3] == Annotated[T, Ann1, Ann2, Ann3]
+
+        - Instantiating an annotated type is equivalent to instantiating the
+        underlying type::
+
+            Annotated[C, Ann1](5) == C(5)
+
+        - Annotated can be used as a generic type alias::
+
+            Optimized = Annotated[T, runtime.Optimize()]
+            Optimized[int] == Annotated[int, runtime.Optimize()]
+
+            OptimizedList = Annotated[List[T], runtime.Optimize()]
+            OptimizedList[int] == Annotated[List[int], runtime.Optimize()]
+        """
+
+        __slots__ = ()
+
+        def __new__(cls, *args, **kwargs):
+            raise TypeError("Type Annotated cannot be instantiated.")
+
+        @typing._tp_cache
+        def __class_getitem__(cls, params):
+            if not isinstance(params, tuple) or len(params) < 2:
+                raise TypeError("Annotated[...] should be used "
+                                "with at least two arguments (a type and an "
+                                "annotation).")
+            allowed_special_forms = (ClassVar, Final)
+            if get_origin(params[0]) in allowed_special_forms:
+                origin = params[0]
+            else:
+                msg = "Annotated[t, ...]: t must be a type."
+                origin = typing._type_check(params[0], msg)
+            metadata = tuple(params[1:])
+            return _AnnotatedAlias(origin, metadata)
+
+        def __init_subclass__(cls, *args, **kwargs):
+            raise TypeError(
+                f"Cannot subclass {cls.__module__}.Annotated"
+            )
+
+# Python 3.8 has get_origin() and get_args() but those implementations aren't
+# Annotated-aware, so we can't use those. Python 3.9's versions don't support
+# ParamSpecArgs and ParamSpecKwargs, so only Python 3.10's versions will do.
+if sys.version_info[:2] >= (3, 10):
+    get_origin = typing.get_origin
+    get_args = typing.get_args
+# 3.7-3.9
+else:
+    try:
+        # 3.9+
+        from typing import _BaseGenericAlias
+    except ImportError:
+        _BaseGenericAlias = typing._GenericAlias
+    try:
+        # 3.9+
+        from typing import GenericAlias as _typing_GenericAlias
+    except ImportError:
+        _typing_GenericAlias = typing._GenericAlias
+
+    def get_origin(tp):
+        """Get the unsubscripted version of a type.
+
+        This supports generic types, Callable, Tuple, Union, Literal, Final, ClassVar
+        and Annotated. Return None for unsupported types. Examples::
+
+            get_origin(Literal[42]) is Literal
+            get_origin(int) is None
+            get_origin(ClassVar[int]) is ClassVar
+            get_origin(Generic) is Generic
+            get_origin(Generic[T]) is Generic
+            get_origin(Union[T, int]) is Union
+            get_origin(List[Tuple[T, T]][int]) == list
+            get_origin(P.args) is P
+        """
+        if isinstance(tp, _AnnotatedAlias):
+            return Annotated
+        if isinstance(tp, (typing._GenericAlias, _typing_GenericAlias, _BaseGenericAlias,
+                           ParamSpecArgs, ParamSpecKwargs)):
+            return tp.__origin__
+        if tp is typing.Generic:
+            return typing.Generic
+        return None
+
+    def get_args(tp):
+        """Get type arguments with all substitutions performed.
+
+        For unions, basic simplifications used by Union constructor are performed.
+        Examples::
+            get_args(Dict[str, int]) == (str, int)
+            get_args(int) == ()
+            get_args(Union[int, Union[T, int], str][int]) == (int, str)
+            get_args(Union[int, Tuple[T, int]][str]) == (int, Tuple[str, int])
+            get_args(Callable[[], T][int]) == ([], int)
+        """
+        if isinstance(tp, _AnnotatedAlias):
+            return (tp.__origin__,) + tp.__metadata__
+        if isinstance(tp, (typing._GenericAlias, _typing_GenericAlias)):
+            if getattr(tp, "_special", False):
+                return ()
+            res = tp.__args__
+            if get_origin(tp) is collections.abc.Callable and res[0] is not Ellipsis:
+                res = (list(res[:-1]), res[-1])
+            return res
+        return ()
+
+
+# 3.10+
+if hasattr(typing, 'TypeAlias'):
+    TypeAlias = typing.TypeAlias
+# 3.9
+elif sys.version_info[:2] >= (3, 9):
+    class _TypeAliasForm(typing._SpecialForm, _root=True):
+        def __repr__(self):
+            return 'typing_extensions.' + self._name
+
+    @_TypeAliasForm
+    def TypeAlias(self, parameters):
+        """Special marker indicating that an assignment should
+        be recognized as a proper type alias definition by type
+        checkers.
+
+        For example::
+
+            Predicate: TypeAlias = Callable[..., bool]
+
+        It's invalid when used anywhere except as in the example above.
+        """
+        raise TypeError(f"{self} is not subscriptable")
+# 3.7-3.8
+else:
+    class _TypeAliasForm(typing._SpecialForm, _root=True):
+        def __repr__(self):
+            return 'typing_extensions.' + self._name
+
+    TypeAlias = _TypeAliasForm('TypeAlias',
+                               doc="""Special marker indicating that an assignment should
+                               be recognized as a proper type alias definition by type
+                               checkers.
+
+                               For example::
+
+                                   Predicate: TypeAlias = Callable[..., bool]
+
+                               It's invalid when used anywhere except as in the example
+                               above.""")
+
+
+class _DefaultMixin:
+    """Mixin for TypeVarLike defaults."""
+
+    __slots__ = ()
+
+    def __init__(self, default):
+        if isinstance(default, (tuple, list)):
+            self.__default__ = tuple((typing._type_check(d, "Default must be a type")
+                                      for d in default))
+        elif default:
+            self.__default__ = typing._type_check(default, "Default must be a type")
+        else:
+            self.__default__ = None
+
+
+# Add default and infer_variance parameters from PEP 696 and 695
+class TypeVar(typing.TypeVar, _DefaultMixin, _root=True):
+    """Type variable."""
+
+    __module__ = 'typing'
+
+    def __init__(self, name, *constraints, bound=None,
+                 covariant=False, contravariant=False,
+                 default=None, infer_variance=False):
+        super().__init__(name, *constraints, bound=bound, covariant=covariant,
+                         contravariant=contravariant)
+        _DefaultMixin.__init__(self, default)
+        self.__infer_variance__ = infer_variance
+
+        # for pickling:
+        try:
+            def_mod = sys._getframe(1).f_globals.get('__name__', '__main__')
+        except (AttributeError, ValueError):
+            def_mod = None
+        if def_mod != 'typing_extensions':
+            self.__module__ = def_mod
+
+
+# Python 3.10+ has PEP 612
+if hasattr(typing, 'ParamSpecArgs'):
+    ParamSpecArgs = typing.ParamSpecArgs
+    ParamSpecKwargs = typing.ParamSpecKwargs
+# 3.7-3.9
+else:
+    class _Immutable:
+        """Mixin to indicate that object should not be copied."""
+        __slots__ = ()
+
+        def __copy__(self):
+            return self
+
+        def __deepcopy__(self, memo):
+            return self
+
+    class ParamSpecArgs(_Immutable):
+        """The args for a ParamSpec object.
+
+        Given a ParamSpec object P, P.args is an instance of ParamSpecArgs.
+
+        ParamSpecArgs objects have a reference back to their ParamSpec:
+
+        P.args.__origin__ is P
+
+        This type is meant for runtime introspection and has no special meaning to
+        static type checkers.
+        """
+        def __init__(self, origin):
+            self.__origin__ = origin
+
+        def __repr__(self):
+            return f"{self.__origin__.__name__}.args"
+
+        def __eq__(self, other):
+            if not isinstance(other, ParamSpecArgs):
+                return NotImplemented
+            return self.__origin__ == other.__origin__
+
+    class ParamSpecKwargs(_Immutable):
+        """The kwargs for a ParamSpec object.
+
+        Given a ParamSpec object P, P.kwargs is an instance of ParamSpecKwargs.
+
+        ParamSpecKwargs objects have a reference back to their ParamSpec:
+
+        P.kwargs.__origin__ is P
+
+        This type is meant for runtime introspection and has no special meaning to
+        static type checkers.
+        """
+        def __init__(self, origin):
+            self.__origin__ = origin
+
+        def __repr__(self):
+            return f"{self.__origin__.__name__}.kwargs"
+
+        def __eq__(self, other):
+            if not isinstance(other, ParamSpecKwargs):
+                return NotImplemented
+            return self.__origin__ == other.__origin__
+
+# 3.10+
+if hasattr(typing, 'ParamSpec'):
+
+    # Add default Parameter - PEP 696
+    class ParamSpec(typing.ParamSpec, _DefaultMixin, _root=True):
+        """Parameter specification variable."""
+
+        __module__ = 'typing'
+
+        def __init__(self, name, *, bound=None, covariant=False, contravariant=False,
+                     default=None):
+            super().__init__(name, bound=bound, covariant=covariant,
+                             contravariant=contravariant)
+            _DefaultMixin.__init__(self, default)
+
+            # for pickling:
+            try:
+                def_mod = sys._getframe(1).f_globals.get('__name__', '__main__')
+            except (AttributeError, ValueError):
+                def_mod = None
+            if def_mod != 'typing_extensions':
+                self.__module__ = def_mod
+
+# 3.7-3.9
+else:
+
+    # Inherits from list as a workaround for Callable checks in Python < 3.9.2.
+    class ParamSpec(list, _DefaultMixin):
+        """Parameter specification variable.
+
+        Usage::
+
+           P = ParamSpec('P')
+
+        Parameter specification variables exist primarily for the benefit of static
+        type checkers.  They are used to forward the parameter types of one
+        callable to another callable, a pattern commonly found in higher order
+        functions and decorators.  They are only valid when used in ``Concatenate``,
+        or s the first argument to ``Callable``. In Python 3.10 and higher,
+        they are also supported in user-defined Generics at runtime.
+        See class Generic for more information on generic types.  An
+        example for annotating a decorator::
+
+           T = TypeVar('T')
+           P = ParamSpec('P')
+
+           def add_logging(f: Callable[P, T]) -> Callable[P, T]:
+               '''A type-safe decorator to add logging to a function.'''
+               def inner(*args: P.args, **kwargs: P.kwargs) -> T:
+                   logging.info(f'{f.__name__} was called')
+                   return f(*args, **kwargs)
+               return inner
+
+           @add_logging
+           def add_two(x: float, y: float) -> float:
+               '''Add two numbers together.'''
+               return x + y
+
+        Parameter specification variables defined with covariant=True or
+        contravariant=True can be used to declare covariant or contravariant
+        generic types.  These keyword arguments are valid, but their actual semantics
+        are yet to be decided.  See PEP 612 for details.
+
+        Parameter specification variables can be introspected. e.g.:
+
+           P.__name__ == 'T'
+           P.__bound__ == None
+           P.__covariant__ == False
+           P.__contravariant__ == False
+
+        Note that only parameter specification variables defined in global scope can
+        be pickled.
+        """
+
+        # Trick Generic __parameters__.
+        __class__ = typing.TypeVar
+
+        @property
+        def args(self):
+            return ParamSpecArgs(self)
+
+        @property
+        def kwargs(self):
+            return ParamSpecKwargs(self)
+
+        def __init__(self, name, *, bound=None, covariant=False, contravariant=False,
+                     default=None):
+            super().__init__([self])
+            self.__name__ = name
+            self.__covariant__ = bool(covariant)
+            self.__contravariant__ = bool(contravariant)
+            if bound:
+                self.__bound__ = typing._type_check(bound, 'Bound must be a type.')
+            else:
+                self.__bound__ = None
+            _DefaultMixin.__init__(self, default)
+
+            # for pickling:
+            try:
+                def_mod = sys._getframe(1).f_globals.get('__name__', '__main__')
+            except (AttributeError, ValueError):
+                def_mod = None
+            if def_mod != 'typing_extensions':
+                self.__module__ = def_mod
+
+        def __repr__(self):
+            if self.__covariant__:
+                prefix = '+'
+            elif self.__contravariant__:
+                prefix = '-'
+            else:
+                prefix = '~'
+            return prefix + self.__name__
+
+        def __hash__(self):
+            return object.__hash__(self)
+
+        def __eq__(self, other):
+            return self is other
+
+        def __reduce__(self):
+            return self.__name__
+
+        # Hack to get typing._type_check to pass.
+        def __call__(self, *args, **kwargs):
+            pass
+
+
+# 3.7-3.9
+if not hasattr(typing, 'Concatenate'):
+    # Inherits from list as a workaround for Callable checks in Python < 3.9.2.
+    class _ConcatenateGenericAlias(list):
+
+        # Trick Generic into looking into this for __parameters__.
+        __class__ = typing._GenericAlias
+
+        # Flag in 3.8.
+        _special = False
+
+        def __init__(self, origin, args):
+            super().__init__(args)
+            self.__origin__ = origin
+            self.__args__ = args
+
+        def __repr__(self):
+            _type_repr = typing._type_repr
+            return (f'{_type_repr(self.__origin__)}'
+                    f'[{", ".join(_type_repr(arg) for arg in self.__args__)}]')
+
+        def __hash__(self):
+            return hash((self.__origin__, self.__args__))
+
+        # Hack to get typing._type_check to pass in Generic.
+        def __call__(self, *args, **kwargs):
+            pass
+
+        @property
+        def __parameters__(self):
+            return tuple(
+                tp for tp in self.__args__ if isinstance(tp, (typing.TypeVar, ParamSpec))
+            )
+
+
+# 3.7-3.9
+@typing._tp_cache
+def _concatenate_getitem(self, parameters):
+    if parameters == ():
+        raise TypeError("Cannot take a Concatenate of no types.")
+    if not isinstance(parameters, tuple):
+        parameters = (parameters,)
+    if not isinstance(parameters[-1], ParamSpec):
+        raise TypeError("The last parameter to Concatenate should be a "
+                        "ParamSpec variable.")
+    msg = "Concatenate[arg, ...]: each arg must be a type."
+    parameters = tuple(typing._type_check(p, msg) for p in parameters)
+    return _ConcatenateGenericAlias(self, parameters)
+
+
+# 3.10+
+if hasattr(typing, 'Concatenate'):
+    Concatenate = typing.Concatenate
+    _ConcatenateGenericAlias = typing._ConcatenateGenericAlias # noqa
+# 3.9
+elif sys.version_info[:2] >= (3, 9):
+    @_TypeAliasForm
+    def Concatenate(self, parameters):
+        """Used in conjunction with ``ParamSpec`` and ``Callable`` to represent a
+        higher order function which adds, removes or transforms parameters of a
+        callable.
+
+        For example::
+
+           Callable[Concatenate[int, P], int]
+
+        See PEP 612 for detailed information.
+        """
+        return _concatenate_getitem(self, parameters)
+# 3.7-8
+else:
+    class _ConcatenateForm(typing._SpecialForm, _root=True):
+        def __repr__(self):
+            return 'typing_extensions.' + self._name
+
+        def __getitem__(self, parameters):
+            return _concatenate_getitem(self, parameters)
+
+    Concatenate = _ConcatenateForm(
+        'Concatenate',
+        doc="""Used in conjunction with ``ParamSpec`` and ``Callable`` to represent a
+        higher order function which adds, removes or transforms parameters of a
+        callable.
+
+        For example::
+
+           Callable[Concatenate[int, P], int]
+
+        See PEP 612 for detailed information.
+        """)
+
+# 3.10+
+if hasattr(typing, 'TypeGuard'):
+    TypeGuard = typing.TypeGuard
+# 3.9
+elif sys.version_info[:2] >= (3, 9):
+    class _TypeGuardForm(typing._SpecialForm, _root=True):
+        def __repr__(self):
+            return 'typing_extensions.' + self._name
+
+    @_TypeGuardForm
+    def TypeGuard(self, parameters):
+        """Special typing form used to annotate the return type of a user-defined
+        type guard function.  ``TypeGuard`` only accepts a single type argument.
+        At runtime, functions marked this way should return a boolean.
+
+        ``TypeGuard`` aims to benefit *type narrowing* -- a technique used by static
+        type checkers to determine a more precise type of an expression within a
+        program's code flow.  Usually type narrowing is done by analyzing
+        conditional code flow and applying the narrowing to a block of code.  The
+        conditional expression here is sometimes referred to as a "type guard".
+
+        Sometimes it would be convenient to use a user-defined boolean function
+        as a type guard.  Such a function should use ``TypeGuard[...]`` as its
+        return type to alert static type checkers to this intention.
+
+        Using  ``-> TypeGuard`` tells the static type checker that for a given
+        function:
+
+        1. The return value is a boolean.
+        2. If the return value is ``True``, the type of its argument
+        is the type inside ``TypeGuard``.
+
+        For example::
+
+            def is_str(val: Union[str, float]):
+                # "isinstance" type guard
+                if isinstance(val, str):
+                    # Type of ``val`` is narrowed to ``str``
+                    ...
+                else:
+                    # Else, type of ``val`` is narrowed to ``float``.
+                    ...
+
+        Strict type narrowing is not enforced -- ``TypeB`` need not be a narrower
+        form of ``TypeA`` (it can even be a wider form) and this may lead to
+        type-unsafe results.  The main reason is to allow for things like
+        narrowing ``List[object]`` to ``List[str]`` even though the latter is not
+        a subtype of the former, since ``List`` is invariant.  The responsibility of
+        writing type-safe type guards is left to the user.
+
+        ``TypeGuard`` also works with type variables.  For more information, see
+        PEP 647 (User-Defined Type Guards).
+        """
+        item = typing._type_check(parameters, f'{self} accepts only a single type.')
+        return typing._GenericAlias(self, (item,))
+# 3.7-3.8
+else:
+    class _TypeGuardForm(typing._SpecialForm, _root=True):
+
+        def __repr__(self):
+            return 'typing_extensions.' + self._name
+
+        def __getitem__(self, parameters):
+            item = typing._type_check(parameters,
+                                      f'{self._name} accepts only a single type')
+            return typing._GenericAlias(self, (item,))
+
+    TypeGuard = _TypeGuardForm(
+        'TypeGuard',
+        doc="""Special typing form used to annotate the return type of a user-defined
+        type guard function.  ``TypeGuard`` only accepts a single type argument.
+        At runtime, functions marked this way should return a boolean.
+
+        ``TypeGuard`` aims to benefit *type narrowing* -- a technique used by static
+        type checkers to determine a more precise type of an expression within a
+        program's code flow.  Usually type narrowing is done by analyzing
+        conditional code flow and applying the narrowing to a block of code.  The
+        conditional expression here is sometimes referred to as a "type guard".
+
+        Sometimes it would be convenient to use a user-defined boolean function
+        as a type guard.  Such a function should use ``TypeGuard[...]`` as its
+        return type to alert static type checkers to this intention.
+
+        Using  ``-> TypeGuard`` tells the static type checker that for a given
+        function:
+
+        1. The return value is a boolean.
+        2. If the return value is ``True``, the type of its argument
+        is the type inside ``TypeGuard``.
+
+        For example::
+
+            def is_str(val: Union[str, float]):
+                # "isinstance" type guard
+                if isinstance(val, str):
+                    # Type of ``val`` is narrowed to ``str``
+                    ...
+                else:
+                    # Else, type of ``val`` is narrowed to ``float``.
+                    ...
+
+        Strict type narrowing is not enforced -- ``TypeB`` need not be a narrower
+        form of ``TypeA`` (it can even be a wider form) and this may lead to
+        type-unsafe results.  The main reason is to allow for things like
+        narrowing ``List[object]`` to ``List[str]`` even though the latter is not
+        a subtype of the former, since ``List`` is invariant.  The responsibility of
+        writing type-safe type guards is left to the user.
+
+        ``TypeGuard`` also works with type variables.  For more information, see
+        PEP 647 (User-Defined Type Guards).
+        """)
+
+
+# Vendored from cpython typing._SpecialFrom
+class _SpecialForm(typing._Final, _root=True):
+    __slots__ = ('_name', '__doc__', '_getitem')
+
+    def __init__(self, getitem):
+        self._getitem = getitem
+        self._name = getitem.__name__
+        self.__doc__ = getitem.__doc__
+
+    def __getattr__(self, item):
+        if item in {'__name__', '__qualname__'}:
+            return self._name
+
+        raise AttributeError(item)
+
+    def __mro_entries__(self, bases):
+        raise TypeError(f"Cannot subclass {self!r}")
+
+    def __repr__(self):
+        return f'typing_extensions.{self._name}'
+
+    def __reduce__(self):
+        return self._name
+
+    def __call__(self, *args, **kwds):
+        raise TypeError(f"Cannot instantiate {self!r}")
+
+    def __or__(self, other):
+        return typing.Union[self, other]
+
+    def __ror__(self, other):
+        return typing.Union[other, self]
+
+    def __instancecheck__(self, obj):
+        raise TypeError(f"{self} cannot be used with isinstance()")
+
+    def __subclasscheck__(self, cls):
+        raise TypeError(f"{self} cannot be used with issubclass()")
+
+    @typing._tp_cache
+    def __getitem__(self, parameters):
+        return self._getitem(self, parameters)
+
+
+if hasattr(typing, "LiteralString"):
+    LiteralString = typing.LiteralString
+else:
+    @_SpecialForm
+    def LiteralString(self, params):
+        """Represents an arbitrary literal string.
+
+        Example::
+
+          from typing_extensions import LiteralString
+
+          def query(sql: LiteralString) -> ...:
+              ...
+
+          query("SELECT * FROM table")  # ok
+          query(f"SELECT * FROM {input()}")  # not ok
+
+        See PEP 675 for details.
+
+        """
+        raise TypeError(f"{self} is not subscriptable")
+
+
+if hasattr(typing, "Self"):
+    Self = typing.Self
+else:
+    @_SpecialForm
+    def Self(self, params):
+        """Used to spell the type of "self" in classes.
+
+        Example::
+
+          from typing import Self
+
+          class ReturnsSelf:
+              def parse(self, data: bytes) -> Self:
+                  ...
+                  return self
+
+        """
+
+        raise TypeError(f"{self} is not subscriptable")
+
+
+if hasattr(typing, "Never"):
+    Never = typing.Never
+else:
+    @_SpecialForm
+    def Never(self, params):
+        """The bottom type, a type that has no members.
+
+        This can be used to define a function that should never be
+        called, or a function that never returns::
+
+            from typing_extensions import Never
+
+            def never_call_me(arg: Never) -> None:
+                pass
+
+            def int_or_str(arg: int | str) -> None:
+                never_call_me(arg)  # type checker error
+                match arg:
+                    case int():
+                        print("It's an int")
+                    case str():
+                        print("It's a str")
+                    case _:
+                        never_call_me(arg)  # ok, arg is of type Never
+
+        """
+
+        raise TypeError(f"{self} is not subscriptable")
+
+
+if hasattr(typing, 'Required'):
+    Required = typing.Required
+    NotRequired = typing.NotRequired
+elif sys.version_info[:2] >= (3, 9):
+    class _ExtensionsSpecialForm(typing._SpecialForm, _root=True):
+        def __repr__(self):
+            return 'typing_extensions.' + self._name
+
+    @_ExtensionsSpecialForm
+    def Required(self, parameters):
+        """A special typing construct to mark a key of a total=False TypedDict
+        as required. For example:
+
+            class Movie(TypedDict, total=False):
+                title: Required[str]
+                year: int
+
+            m = Movie(
+                title='The Matrix',  # typechecker error if key is omitted
+                year=1999,
+            )
+
+        There is no runtime checking that a required key is actually provided
+        when instantiating a related TypedDict.
+        """
+        item = typing._type_check(parameters, f'{self._name} accepts only a single type.')
+        return typing._GenericAlias(self, (item,))
+
+    @_ExtensionsSpecialForm
+    def NotRequired(self, parameters):
+        """A special typing construct to mark a key of a TypedDict as
+        potentially missing. For example:
+
+            class Movie(TypedDict):
+                title: str
+                year: NotRequired[int]
+
+            m = Movie(
+                title='The Matrix',  # typechecker error if key is omitted
+                year=1999,
+            )
+        """
+        item = typing._type_check(parameters, f'{self._name} accepts only a single type.')
+        return typing._GenericAlias(self, (item,))
+
+else:
+    class _RequiredForm(typing._SpecialForm, _root=True):
+        def __repr__(self):
+            return 'typing_extensions.' + self._name
+
+        def __getitem__(self, parameters):
+            item = typing._type_check(parameters,
+                                      f'{self._name} accepts only a single type.')
+            return typing._GenericAlias(self, (item,))
+
+    Required = _RequiredForm(
+        'Required',
+        doc="""A special typing construct to mark a key of a total=False TypedDict
+        as required. For example:
+
+            class Movie(TypedDict, total=False):
+                title: Required[str]
+                year: int
+
+            m = Movie(
+                title='The Matrix',  # typechecker error if key is omitted
+                year=1999,
+            )
+
+        There is no runtime checking that a required key is actually provided
+        when instantiating a related TypedDict.
+        """)
+    NotRequired = _RequiredForm(
+        'NotRequired',
+        doc="""A special typing construct to mark a key of a TypedDict as
+        potentially missing. For example:
+
+            class Movie(TypedDict):
+                title: str
+                year: NotRequired[int]
+
+            m = Movie(
+                title='The Matrix',  # typechecker error if key is omitted
+                year=1999,
+            )
+        """)
+
+
+if hasattr(typing, "Unpack"):  # 3.11+
+    Unpack = typing.Unpack
+elif sys.version_info[:2] >= (3, 9):
+    class _UnpackSpecialForm(typing._SpecialForm, _root=True):
+        def __repr__(self):
+            return 'typing_extensions.' + self._name
+
+    class _UnpackAlias(typing._GenericAlias, _root=True):
+        __class__ = typing.TypeVar
+
+    @_UnpackSpecialForm
+    def Unpack(self, parameters):
+        """A special typing construct to unpack a variadic type. For example:
+
+            Shape = TypeVarTuple('Shape')
+            Batch = NewType('Batch', int)
+
+            def add_batch_axis(
+                x: Array[Unpack[Shape]]
+            ) -> Array[Batch, Unpack[Shape]]: ...
+
+        """
+        item = typing._type_check(parameters, f'{self._name} accepts only a single type.')
+        return _UnpackAlias(self, (item,))
+
+    def _is_unpack(obj):
+        return isinstance(obj, _UnpackAlias)
+
+else:
+    class _UnpackAlias(typing._GenericAlias, _root=True):
+        __class__ = typing.TypeVar
+
+    class _UnpackForm(typing._SpecialForm, _root=True):
+        def __repr__(self):
+            return 'typing_extensions.' + self._name
+
+        def __getitem__(self, parameters):
+            item = typing._type_check(parameters,
+                                      f'{self._name} accepts only a single type.')
+            return _UnpackAlias(self, (item,))
+
+    Unpack = _UnpackForm(
+        'Unpack',
+        doc="""A special typing construct to unpack a variadic type. For example:
+
+            Shape = TypeVarTuple('Shape')
+            Batch = NewType('Batch', int)
+
+            def add_batch_axis(
+                x: Array[Unpack[Shape]]
+            ) -> Array[Batch, Unpack[Shape]]: ...
+
+        """)
+
+    def _is_unpack(obj):
+        return isinstance(obj, _UnpackAlias)
+
+
+if hasattr(typing, "TypeVarTuple"):  # 3.11+
+
+    # Add default Parameter - PEP 696
+    class TypeVarTuple(typing.TypeVarTuple, _DefaultMixin, _root=True):
+        """Type variable tuple."""
+
+        def __init__(self, name, *, default=None):
+            super().__init__(name)
+            _DefaultMixin.__init__(self, default)
+
+            # for pickling:
+            try:
+                def_mod = sys._getframe(1).f_globals.get('__name__', '__main__')
+            except (AttributeError, ValueError):
+                def_mod = None
+            if def_mod != 'typing_extensions':
+                self.__module__ = def_mod
+
+else:
+    class TypeVarTuple(_DefaultMixin):
+        """Type variable tuple.
+
+        Usage::
+
+            Ts = TypeVarTuple('Ts')
+
+        In the same way that a normal type variable is a stand-in for a single
+        type such as ``int``, a type variable *tuple* is a stand-in for a *tuple*
+        type such as ``Tuple[int, str]``.
+
+        Type variable tuples can be used in ``Generic`` declarations.
+        Consider the following example::
+
+            class Array(Generic[*Ts]): ...
+
+        The ``Ts`` type variable tuple here behaves like ``tuple[T1, T2]``,
+        where ``T1`` and ``T2`` are type variables. To use these type variables
+        as type parameters of ``Array``, we must *unpack* the type variable tuple using
+        the star operator: ``*Ts``. The signature of ``Array`` then behaves
+        as if we had simply written ``class Array(Generic[T1, T2]): ...``.
+        In contrast to ``Generic[T1, T2]``, however, ``Generic[*Shape]`` allows
+        us to parameterise the class with an *arbitrary* number of type parameters.
+
+        Type variable tuples can be used anywhere a normal ``TypeVar`` can.
+        This includes class definitions, as shown above, as well as function
+        signatures and variable annotations::
+
+            class Array(Generic[*Ts]):
+
+                def __init__(self, shape: Tuple[*Ts]):
+                    self._shape: Tuple[*Ts] = shape
+
+                def get_shape(self) -> Tuple[*Ts]:
+                    return self._shape
+
+            shape = (Height(480), Width(640))
+            x: Array[Height, Width] = Array(shape)
+            y = abs(x)  # Inferred type is Array[Height, Width]
+            z = x + x   #        ...    is Array[Height, Width]
+            x.get_shape()  #     ...    is tuple[Height, Width]
+
+        """
+
+        # Trick Generic __parameters__.
+        __class__ = typing.TypeVar
+
+        def __iter__(self):
+            yield self.__unpacked__
+
+        def __init__(self, name, *, default=None):
+            self.__name__ = name
+            _DefaultMixin.__init__(self, default)
+
+            # for pickling:
+            try:
+                def_mod = sys._getframe(1).f_globals.get('__name__', '__main__')
+            except (AttributeError, ValueError):
+                def_mod = None
+            if def_mod != 'typing_extensions':
+                self.__module__ = def_mod
+
+            self.__unpacked__ = Unpack[self]
+
+        def __repr__(self):
+            return self.__name__
+
+        def __hash__(self):
+            return object.__hash__(self)
+
+        def __eq__(self, other):
+            return self is other
+
+        def __reduce__(self):
+            return self.__name__
+
+        def __init_subclass__(self, *args, **kwds):
+            if '_root' not in kwds:
+                raise TypeError("Cannot subclass special typing classes")
+
+
+if hasattr(typing, "reveal_type"):
+    reveal_type = typing.reveal_type
+else:
+    def reveal_type(__obj: T) -> T:
+        """Reveal the inferred type of a variable.
+
+        When a static type checker encounters a call to ``reveal_type()``,
+        it will emit the inferred type of the argument::
+
+            x: int = 1
+            reveal_type(x)
+
+        Running a static type checker (e.g., ``mypy``) on this example
+        will produce output similar to 'Revealed type is "builtins.int"'.
+
+        At runtime, the function prints the runtime type of the
+        argument and returns it unchanged.
+
+        """
+        print(f"Runtime type is {type(__obj).__name__!r}", file=sys.stderr)
+        return __obj
+
+
+if hasattr(typing, "assert_never"):
+    assert_never = typing.assert_never
+else:
+    def assert_never(__arg: Never) -> Never:
+        """Assert to the type checker that a line of code is unreachable.
+
+        Example::
+
+            def int_or_str(arg: int | str) -> None:
+                match arg:
+                    case int():
+                        print("It's an int")
+                    case str():
+                        print("It's a str")
+                    case _:
+                        assert_never(arg)
+
+        If a type checker finds that a call to assert_never() is
+        reachable, it will emit an error.
+
+        At runtime, this throws an exception when called.
+
+        """
+        raise AssertionError("Expected code to be unreachable")
+
+
+if hasattr(typing, 'dataclass_transform'):
+    dataclass_transform = typing.dataclass_transform
+else:
+    def dataclass_transform(
+        *,
+        eq_default: bool = True,
+        order_default: bool = False,
+        kw_only_default: bool = False,
+        field_specifiers: typing.Tuple[
+            typing.Union[typing.Type[typing.Any], typing.Callable[..., typing.Any]],
+            ...
+        ] = (),
+        **kwargs: typing.Any,
+    ) -> typing.Callable[[T], T]:
+        """Decorator that marks a function, class, or metaclass as providing
+        dataclass-like behavior.
+
+        Example:
+
+            from typing_extensions import dataclass_transform
+
+            _T = TypeVar("_T")
+
+            # Used on a decorator function
+            @dataclass_transform()
+            def create_model(cls: type[_T]) -> type[_T]:
+                ...
+                return cls
+
+            @create_model
+            class CustomerModel:
+                id: int
+                name: str
+
+            # Used on a base class
+            @dataclass_transform()
+            class ModelBase: ...
+
+            class CustomerModel(ModelBase):
+                id: int
+                name: str
+
+            # Used on a metaclass
+            @dataclass_transform()
+            class ModelMeta(type): ...
+
+            class ModelBase(metaclass=ModelMeta): ...
+
+            class CustomerModel(ModelBase):
+                id: int
+                name: str
+
+        Each of the ``CustomerModel`` classes defined in this example will now
+        behave similarly to a dataclass created with the ``@dataclasses.dataclass``
+        decorator. For example, the type checker will synthesize an ``__init__``
+        method.
+
+        The arguments to this decorator can be used to customize this behavior:
+        - ``eq_default`` indicates whether the ``eq`` parameter is assumed to be
+          True or False if it is omitted by the caller.
+        - ``order_default`` indicates whether the ``order`` parameter is
+          assumed to be True or False if it is omitted by the caller.
+        - ``kw_only_default`` indicates whether the ``kw_only`` parameter is
+          assumed to be True or False if it is omitted by the caller.
+        - ``field_specifiers`` specifies a static list of supported classes
+          or functions that describe fields, similar to ``dataclasses.field()``.
+
+        At runtime, this decorator records its arguments in the
+        ``__dataclass_transform__`` attribute on the decorated object.
+
+        See PEP 681 for details.
+
+        """
+        def decorator(cls_or_fn):
+            cls_or_fn.__dataclass_transform__ = {
+                "eq_default": eq_default,
+                "order_default": order_default,
+                "kw_only_default": kw_only_default,
+                "field_specifiers": field_specifiers,
+                "kwargs": kwargs,
+            }
+            return cls_or_fn
+        return decorator
+
+
+if hasattr(typing, "override"):
+    override = typing.override
+else:
+    _F = typing.TypeVar("_F", bound=typing.Callable[..., typing.Any])
+
+    def override(__arg: _F) -> _F:
+        """Indicate that a method is intended to override a method in a base class.
+
+        Usage:
+
+            class Base:
+                def method(self) -> None: ...
+                    pass
+
+            class Child(Base):
+                @override
+                def method(self) -> None:
+                    super().method()
+
+        When this decorator is applied to a method, the type checker will
+        validate that it overrides a method with the same name on a base class.
+        This helps prevent bugs that may occur when a base class is changed
+        without an equivalent change to a child class.
+
+        See PEP 698 for details.
+
+        """
+        return __arg
+
+
+# We have to do some monkey patching to deal with the dual nature of
+# Unpack/TypeVarTuple:
+# - We want Unpack to be a kind of TypeVar so it gets accepted in
+#   Generic[Unpack[Ts]]
+# - We want it to *not* be treated as a TypeVar for the purposes of
+#   counting generic parameters, so that when we subscript a generic,
+#   the runtime doesn't try to substitute the Unpack with the subscripted type.
+if not hasattr(typing, "TypeVarTuple"):
+    typing._collect_type_vars = _collect_type_vars
+    typing._check_generic = _check_generic
+
+
+# Backport typing.NamedTuple as it exists in Python 3.11.
+# In 3.11, the ability to define generic `NamedTuple`s was supported.
+# This was explicitly disallowed in 3.9-3.10, and only half-worked in <=3.8.
+if sys.version_info >= (3, 11):
+    NamedTuple = typing.NamedTuple
+else:
+    def _caller():
+        try:
+            return sys._getframe(2).f_globals.get('__name__', '__main__')
+        except (AttributeError, ValueError):  # For platforms without _getframe()
+            return None
+
+    def _make_nmtuple(name, types, module, defaults=()):
+        fields = [n for n, t in types]
+        annotations = {n: typing._type_check(t, f"field {n} annotation must be a type")
+                       for n, t in types}
+        nm_tpl = collections.namedtuple(name, fields,
+                                        defaults=defaults, module=module)
+        nm_tpl.__annotations__ = nm_tpl.__new__.__annotations__ = annotations
+        # The `_field_types` attribute was removed in 3.9;
+        # in earlier versions, it is the same as the `__annotations__` attribute
+        if sys.version_info < (3, 9):
+            nm_tpl._field_types = annotations
+        return nm_tpl
+
+    _prohibited_namedtuple_fields = typing._prohibited
+    _special_namedtuple_fields = frozenset({'__module__', '__name__', '__annotations__'})
+
+    class _NamedTupleMeta(type):
+        def __new__(cls, typename, bases, ns):
+            assert _NamedTuple in bases
+            for base in bases:
+                if base is not _NamedTuple and base is not typing.Generic:
+                    raise TypeError(
+                        'can only inherit from a NamedTuple type and Generic')
+            bases = tuple(tuple if base is _NamedTuple else base for base in bases)
+            types = ns.get('__annotations__', {})
+            default_names = []
+            for field_name in types:
+                if field_name in ns:
+                    default_names.append(field_name)
+                elif default_names:
+                    raise TypeError(f"Non-default namedtuple field {field_name} "
+                                    f"cannot follow default field"
+                                    f"{'s' if len(default_names) > 1 else ''} "
+                                    f"{', '.join(default_names)}")
+            nm_tpl = _make_nmtuple(
+                typename, types.items(),
+                defaults=[ns[n] for n in default_names],
+                module=ns['__module__']
+            )
+            nm_tpl.__bases__ = bases
+            if typing.Generic in bases:
+                class_getitem = typing.Generic.__class_getitem__.__func__
+                nm_tpl.__class_getitem__ = classmethod(class_getitem)
+            # update from user namespace without overriding special namedtuple attributes
+            for key in ns:
+                if key in _prohibited_namedtuple_fields:
+                    raise AttributeError("Cannot overwrite NamedTuple attribute " + key)
+                elif key not in _special_namedtuple_fields and key not in nm_tpl._fields:
+                    setattr(nm_tpl, key, ns[key])
+            if typing.Generic in bases:
+                nm_tpl.__init_subclass__()
+            return nm_tpl
+
+    def NamedTuple(__typename, __fields=None, **kwargs):
+        if __fields is None:
+            __fields = kwargs.items()
+        elif kwargs:
+            raise TypeError("Either list of fields or keywords"
+                            " can be provided to NamedTuple, not both")
+        return _make_nmtuple(__typename, __fields, module=_caller())
+
+    NamedTuple.__doc__ = typing.NamedTuple.__doc__
+    _NamedTuple = type.__new__(_NamedTupleMeta, 'NamedTuple', (), {})
+
+    # On 3.8+, alter the signature so that it matches typing.NamedTuple.
+    # The signature of typing.NamedTuple on >=3.8 is invalid syntax in Python 3.7,
+    # so just leave the signature as it is on 3.7.
+    if sys.version_info >= (3, 8):
+        NamedTuple.__text_signature__ = '(typename, fields=None, /, **kwargs)'
+
+    def _namedtuple_mro_entries(bases):
+        assert NamedTuple in bases
+        return (_NamedTuple,)
+
+    NamedTuple.__mro_entries__ = _namedtuple_mro_entries
diff --git a/venv/Lib/site-packages/pkg_resources/_vendor/zipp.py b/venv/Lib/site-packages/pkg_resources/_vendor/zipp.py
new file mode 100644
index 0000000..26b723c
--- /dev/null
+++ b/venv/Lib/site-packages/pkg_resources/_vendor/zipp.py
@@ -0,0 +1,329 @@
+import io
+import posixpath
+import zipfile
+import itertools
+import contextlib
+import sys
+import pathlib
+
+if sys.version_info < (3, 7):
+    from collections import OrderedDict
+else:
+    OrderedDict = dict
+
+
+__all__ = ['Path']
+
+
+def _parents(path):
+    """
+    Given a path with elements separated by
+    posixpath.sep, generate all parents of that path.
+
+    >>> list(_parents('b/d'))
+    ['b']
+    >>> list(_parents('/b/d/'))
+    ['/b']
+    >>> list(_parents('b/d/f/'))
+    ['b/d', 'b']
+    >>> list(_parents('b'))
+    []
+    >>> list(_parents(''))
+    []
+    """
+    return itertools.islice(_ancestry(path), 1, None)
+
+
+def _ancestry(path):
+    """
+    Given a path with elements separated by
+    posixpath.sep, generate all elements of that path
+
+    >>> list(_ancestry('b/d'))
+    ['b/d', 'b']
+    >>> list(_ancestry('/b/d/'))
+    ['/b/d', '/b']
+    >>> list(_ancestry('b/d/f/'))
+    ['b/d/f', 'b/d', 'b']
+    >>> list(_ancestry('b'))
+    ['b']
+    >>> list(_ancestry(''))
+    []
+    """
+    path = path.rstrip(posixpath.sep)
+    while path and path != posixpath.sep:
+        yield path
+        path, tail = posixpath.split(path)
+
+
+_dedupe = OrderedDict.fromkeys
+"""Deduplicate an iterable in original order"""
+
+
+def _difference(minuend, subtrahend):
+    """
+    Return items in minuend not in subtrahend, retaining order
+    with O(1) lookup.
+    """
+    return itertools.filterfalse(set(subtrahend).__contains__, minuend)
+
+
+class CompleteDirs(zipfile.ZipFile):
+    """
+    A ZipFile subclass that ensures that implied directories
+    are always included in the namelist.
+    """
+
+    @staticmethod
+    def _implied_dirs(names):
+        parents = itertools.chain.from_iterable(map(_parents, names))
+        as_dirs = (p + posixpath.sep for p in parents)
+        return _dedupe(_difference(as_dirs, names))
+
+    def namelist(self):
+        names = super(CompleteDirs, self).namelist()
+        return names + list(self._implied_dirs(names))
+
+    def _name_set(self):
+        return set(self.namelist())
+
+    def resolve_dir(self, name):
+        """
+        If the name represents a directory, return that name
+        as a directory (with the trailing slash).
+        """
+        names = self._name_set()
+        dirname = name + '/'
+        dir_match = name not in names and dirname in names
+        return dirname if dir_match else name
+
+    @classmethod
+    def make(cls, source):
+        """
+        Given a source (filename or zipfile), return an
+        appropriate CompleteDirs subclass.
+        """
+        if isinstance(source, CompleteDirs):
+            return source
+
+        if not isinstance(source, zipfile.ZipFile):
+            return cls(_pathlib_compat(source))
+
+        # Only allow for FastLookup when supplied zipfile is read-only
+        if 'r' not in source.mode:
+            cls = CompleteDirs
+
+        source.__class__ = cls
+        return source
+
+
+class FastLookup(CompleteDirs):
+    """
+    ZipFile subclass to ensure implicit
+    dirs exist and are resolved rapidly.
+    """
+
+    def namelist(self):
+        with contextlib.suppress(AttributeError):
+            return self.__names
+        self.__names = super(FastLookup, self).namelist()
+        return self.__names
+
+    def _name_set(self):
+        with contextlib.suppress(AttributeError):
+            return self.__lookup
+        self.__lookup = super(FastLookup, self)._name_set()
+        return self.__lookup
+
+
+def _pathlib_compat(path):
+    """
+    For path-like objects, convert to a filename for compatibility
+    on Python 3.6.1 and earlier.
+    """
+    try:
+        return path.__fspath__()
+    except AttributeError:
+        return str(path)
+
+
+class Path:
+    """
+    A pathlib-compatible interface for zip files.
+
+    Consider a zip file with this structure::
+
+        .
+        ├── a.txt
+        └── b
+            ├── c.txt
+            └── d
+                └── e.txt
+
+    >>> data = io.BytesIO()
+    >>> zf = zipfile.ZipFile(data, 'w')
+    >>> zf.writestr('a.txt', 'content of a')
+    >>> zf.writestr('b/c.txt', 'content of c')
+    >>> zf.writestr('b/d/e.txt', 'content of e')
+    >>> zf.filename = 'mem/abcde.zip'
+
+    Path accepts the zipfile object itself or a filename
+
+    >>> root = Path(zf)
+
+    From there, several path operations are available.
+
+    Directory iteration (including the zip file itself):
+
+    >>> a, b = root.iterdir()
+    >>> a
+    Path('mem/abcde.zip', 'a.txt')
+    >>> b
+    Path('mem/abcde.zip', 'b/')
+
+    name property:
+
+    >>> b.name
+    'b'
+
+    join with divide operator:
+
+    >>> c = b / 'c.txt'
+    >>> c
+    Path('mem/abcde.zip', 'b/c.txt')
+    >>> c.name
+    'c.txt'
+
+    Read text:
+
+    >>> c.read_text()
+    'content of c'
+
+    existence:
+
+    >>> c.exists()
+    True
+    >>> (b / 'missing.txt').exists()
+    False
+
+    Coercion to string:
+
+    >>> import os
+    >>> str(c).replace(os.sep, posixpath.sep)
+    'mem/abcde.zip/b/c.txt'
+
+    At the root, ``name``, ``filename``, and ``parent``
+    resolve to the zipfile. Note these attributes are not
+    valid and will raise a ``ValueError`` if the zipfile
+    has no filename.
+
+    >>> root.name
+    'abcde.zip'
+    >>> str(root.filename).replace(os.sep, posixpath.sep)
+    'mem/abcde.zip'
+    >>> str(root.parent)
+    'mem'
+    """
+
+    __repr = "{self.__class__.__name__}({self.root.filename!r}, {self.at!r})"
+
+    def __init__(self, root, at=""):
+        """
+        Construct a Path from a ZipFile or filename.
+
+        Note: When the source is an existing ZipFile object,
+        its type (__class__) will be mutated to a
+        specialized type. If the caller wishes to retain the
+        original type, the caller should either create a
+        separate ZipFile object or pass a filename.
+        """
+        self.root = FastLookup.make(root)
+        self.at = at
+
+    def open(self, mode='r', *args, pwd=None, **kwargs):
+        """
+        Open this entry as text or binary following the semantics
+        of ``pathlib.Path.open()`` by passing arguments through
+        to io.TextIOWrapper().
+        """
+        if self.is_dir():
+            raise IsADirectoryError(self)
+        zip_mode = mode[0]
+        if not self.exists() and zip_mode == 'r':
+            raise FileNotFoundError(self)
+        stream = self.root.open(self.at, zip_mode, pwd=pwd)
+        if 'b' in mode:
+            if args or kwargs:
+                raise ValueError("encoding args invalid for binary operation")
+            return stream
+        return io.TextIOWrapper(stream, *args, **kwargs)
+
+    @property
+    def name(self):
+        return pathlib.Path(self.at).name or self.filename.name
+
+    @property
+    def suffix(self):
+        return pathlib.Path(self.at).suffix or self.filename.suffix
+
+    @property
+    def suffixes(self):
+        return pathlib.Path(self.at).suffixes or self.filename.suffixes
+
+    @property
+    def stem(self):
+        return pathlib.Path(self.at).stem or self.filename.stem
+
+    @property
+    def filename(self):
+        return pathlib.Path(self.root.filename).joinpath(self.at)
+
+    def read_text(self, *args, **kwargs):
+        with self.open('r', *args, **kwargs) as strm:
+            return strm.read()
+
+    def read_bytes(self):
+        with self.open('rb') as strm:
+            return strm.read()
+
+    def _is_child(self, path):
+        return posixpath.dirname(path.at.rstrip("/")) == self.at.rstrip("/")
+
+    def _next(self, at):
+        return self.__class__(self.root, at)
+
+    def is_dir(self):
+        return not self.at or self.at.endswith("/")
+
+    def is_file(self):
+        return self.exists() and not self.is_dir()
+
+    def exists(self):
+        return self.at in self.root._name_set()
+
+    def iterdir(self):
+        if not self.is_dir():
+            raise ValueError("Can't listdir a file")
+        subs = map(self._next, self.root.namelist())
+        return filter(self._is_child, subs)
+
+    def __str__(self):
+        return posixpath.join(self.root.filename, self.at)
+
+    def __repr__(self):
+        return self.__repr.format(self=self)
+
+    def joinpath(self, *other):
+        next = posixpath.join(self.at, *map(_pathlib_compat, other))
+        return self._next(self.root.resolve_dir(next))
+
+    __truediv__ = joinpath
+
+    @property
+    def parent(self):
+        if not self.at:
+            return self.filename.parent
+        parent_at = posixpath.dirname(self.at.rstrip('/'))
+        if parent_at:
+            parent_at += '/'
+        return self._next(parent_at)
diff --git a/venv/Lib/site-packages/pkg_resources/extern/__init__.py b/venv/Lib/site-packages/pkg_resources/extern/__init__.py
new file mode 100644
index 0000000..df96f7f
--- /dev/null
+++ b/venv/Lib/site-packages/pkg_resources/extern/__init__.py
@@ -0,0 +1,81 @@
+import importlib.util
+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 _module_matches_namespace(self, fullname):
+        """Figure out if the target module is vendored."""
+        root, base, target = fullname.partition(self.root_name + '.')
+        return not root and any(map(target.startswith, self.vendored_names))
+
+    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 create_module(self, spec):
+        return self.load_module(spec.name)
+
+    def exec_module(self, module):
+        pass
+
+    def find_spec(self, fullname, path=None, target=None):
+        """Return a module spec for vendored names."""
+        return (
+            importlib.util.spec_from_loader(fullname, self)
+            if self._module_matches_namespace(fullname)
+            else None
+        )
+
+    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',
+    'platformdirs',
+    'jaraco',
+    'importlib_resources',
+    'more_itertools',
+    'backports',
+)
+VendorImporter(__name__, names).install()
diff --git a/venv/Lib/site-packages/pkg_resources/extern/__pycache__/__init__.cpython-312.pyc b/venv/Lib/site-packages/pkg_resources/extern/__pycache__/__init__.cpython-312.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..d2f7d1aa9914097f82a29c6242a9585b3c32c084
GIT binary patch
literal 4068
zcmb_fU2GKB6~1@=_Q&hpH8uu27!TpcUciQy5=xUsA^ZdaY*A<<#d0+o?_A?CyE~gZ
zv)H?}B^3F=sA+*Jt*P1vr;1b%szg;)?PHYJzGN|x-3bq^`qI8FHd0cadd{7l_2M|~
zORwy+cka)*Gv|EgJLmqTy**B#@xKp^|0+hvKky-JN~5zj0-Xh75sT)@2o=B5h$N9W
zh$YPtOTI4!D@SBY8Brv{WTrAD9Gz?r6|Vo@Z*(cC(2V*oe9Gq5rwM6jBx_k1T_8-H
zJ0e*W&S+)l%k2sA)vA)HyN|u{4lTD*`fRMM1``gcXDJZB`tf5)~=a?kC@7
zMa$vm3MGeoj92I4I;R0W5L63C5>vm+3{*D|4~NF1j9cm<1|xSIsNT)yKJ0(7EGyM$s&=VX-@#
z3k{Bjz`hLsYZD+A$PAq!2K0jF;f!cA(tX%HQ&^iUFP}8@T$42pKC^YCOoM>?7I%S???xEll4jG1Bv^~QZE>-+!_)y*U_H)i3
z4kz01^$vl!PF8weoLAmYKHj-&ad>IxH|CW`(HB;F_dM*)-i<8}9cYYJyY^CU>W
z%@t=D2>NR81`D;!rE%X55dS1!UMKZ9Np;?IZa9l_ExG$)boWX!{p0Arg_Gag)(;~bx$w|#DjJUkv
ze@gy7`Eh6M@TsM~)3s>tyfSayIcX#2ek3oRS>AKBw&&;rZ~54pwPSBC
z>F1YrT=+D4fg>uO^EKLC`1&>Q@&C!!WU|RrpuN#fksrPSXi#HhMx9X*C9_%!0<_Sd
zfRH8@HuL|C)`NQ^Gm-zXj>2(dCX$mVxeO%jHIthJ5j=^pw4qtDO}vh9Fick;(Vx=r
z-QJ_SiHGDU+8)AXO5P#w7pBOx{0|0OA~wv*`gQuQ4tV83ZRpJY*}+(eq9Y
zr_G`TUDMJ-Y^_s)0BZH;-v=HU_)!3|*@2KT5Yn8zTedl~Ue=2aFJKCTgJ2x~#>zzt
z6B%MS7;G@G?m8GE6{oCYe2Dc^CNBb$AJlVB(Jki#>T0ZqOet1#-LhSe+hb*nnBjXQ
z6XV;!4nf-33Ozp#1TC_#VL*V|$D(=O7vvo6UU19c*I=wE1L*T9F`lZf1LVC+DZP+~b3Nk62x_8ez`;n4CsHqhF0
zvqxZ`i-3Woo$6dp_19AUi^^|fAI6qa`#+8D|0*@X+Tdf5O%_On@FD2%T_D0&(i#A3
zeW^A!&2r%@ZA_3YbC~i4XTu9c6T$JVyoG&X%L(Xwy)8q#L5w%^;b#1|S$&7KVwle^
zL+%v0Z161xfv#CLpNMEADVMqws!sH$N6cUEUt^6^PEfpT;c)maYb(
zpK(};E)+55E66}1zM3^&5JoX9n+@Ds-M+z<&6(q%#yti?giyL`IW<&E4c(o1lsfiQ
zChgrfvp2HKi37F7fxBlPSWAhQ>#`Dy*EO=GyB>jFVCPm;HZdF8*#qrC+YG1IC|EjN
zuxSl4*{m>{%b;2b{e%}8xXMV?x7L|DDEk}S8Qvr8B9Jt&*f8EPM7f2$S#&(z%meJL
ziVo!DLLt0P*x8KCcf+B6thvY6%u)%;Sl$KWt#w?aaoMPLZ!mRZ`~M}{}6^4bl!0>?h&1T{D1f?`W1?U*4xlxT6
ze!?)`Et{f_ftiS5SWeC`cnUW9(Gqt`jC&Q1j4t3Mn1CO~M~q>P2;kpEfk%mB1$;OL
zj1Xvl_cai6nZPMgs&ha~&VX%I|@RJ3HGXriQ+;g0~
z>&M36W`~zaHxu!b%^kxSH~c7^BpfI{!&gO|@mFzllMj9teI>BztNc7HK}@^f0s(AI
w>E~qY=Oq4xc7oD@Cj@2vtQ3h!*VQL!LR0h8ceXB({$(;yBLkn2jQG<30MpaMzyJUM

literal 0
HcmV?d00001

diff --git a/venv/Lib/site-packages/setuptools-69.5.1.dist-info/INSTALLER b/venv/Lib/site-packages/setuptools-69.5.1.dist-info/INSTALLER
new file mode 100644
index 0000000..a1b589e
--- /dev/null
+++ b/venv/Lib/site-packages/setuptools-69.5.1.dist-info/INSTALLER
@@ -0,0 +1 @@
+pip
diff --git a/venv/Lib/site-packages/setuptools-69.5.1.dist-info/LICENSE b/venv/Lib/site-packages/setuptools-69.5.1.dist-info/LICENSE
new file mode 100644
index 0000000..1bb5a44
--- /dev/null
+++ b/venv/Lib/site-packages/setuptools-69.5.1.dist-info/LICENSE
@@ -0,0 +1,17 @@
+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/site-packages/setuptools-69.5.1.dist-info/METADATA b/venv/Lib/site-packages/setuptools-69.5.1.dist-info/METADATA
new file mode 100644
index 0000000..010ae0a
--- /dev/null
+++ b/venv/Lib/site-packages/setuptools-69.5.1.dist-info/METADATA
@@ -0,0 +1,130 @@
+Metadata-Version: 2.1
+Name: setuptools
+Version: 69.5.1
+Summary: Easily download, build, install, upgrade, and uninstall Python packages
+Home-page: https://github.com/pypa/setuptools
+Author: Python Packaging Authority
+Author-email: distutils-sig@python.org
+Project-URL: Documentation, https://setuptools.pypa.io/
+Project-URL: Changelog, https://setuptools.pypa.io/en/stable/history.html
+Keywords: CPAN PyPI distutils eggs package management
+Classifier: Development Status :: 5 - Production/Stable
+Classifier: Intended Audience :: Developers
+Classifier: License :: OSI Approved :: MIT License
+Classifier: Programming Language :: Python :: 3
+Classifier: Programming Language :: Python :: 3 :: Only
+Classifier: Topic :: Software Development :: Libraries :: Python Modules
+Classifier: Topic :: System :: Archiving :: Packaging
+Classifier: Topic :: System :: Systems Administration
+Classifier: Topic :: Utilities
+Requires-Python: >=3.8
+License-File: LICENSE
+Provides-Extra: certs
+Provides-Extra: docs
+Requires-Dist: sphinx >=3.5 ; extra == 'docs'
+Requires-Dist: jaraco.packaging >=9.3 ; extra == 'docs'
+Requires-Dist: rst.linker >=1.9 ; extra == 'docs'
+Requires-Dist: furo ; extra == 'docs'
+Requires-Dist: sphinx-lint ; extra == 'docs'
+Requires-Dist: jaraco.tidelift >=1.4 ; extra == 'docs'
+Requires-Dist: pygments-github-lexers ==0.0.5 ; extra == 'docs'
+Requires-Dist: sphinx-favicon ; extra == 'docs'
+Requires-Dist: sphinx-inline-tabs ; extra == 'docs'
+Requires-Dist: sphinx-reredirects ; extra == 'docs'
+Requires-Dist: sphinxcontrib-towncrier ; extra == 'docs'
+Requires-Dist: sphinx-notfound-page <2,>=1 ; extra == 'docs'
+Provides-Extra: ssl
+Provides-Extra: testing
+Requires-Dist: pytest !=8.1.1,>=6 ; extra == 'testing'
+Requires-Dist: pytest-checkdocs >=2.4 ; extra == 'testing'
+Requires-Dist: pytest-mypy ; extra == 'testing'
+Requires-Dist: pytest-enabler >=2.2 ; extra == 'testing'
+Requires-Dist: virtualenv >=13.0.0 ; extra == 'testing'
+Requires-Dist: wheel ; extra == 'testing'
+Requires-Dist: pip >=19.1 ; extra == 'testing'
+Requires-Dist: packaging >=23.2 ; extra == 'testing'
+Requires-Dist: jaraco.envs >=2.2 ; extra == 'testing'
+Requires-Dist: pytest-xdist >=3 ; extra == 'testing'
+Requires-Dist: jaraco.path >=3.2.0 ; extra == 'testing'
+Requires-Dist: build[virtualenv] ; extra == 'testing'
+Requires-Dist: filelock >=3.4.0 ; extra == 'testing'
+Requires-Dist: ini2toml[lite] >=0.9 ; extra == 'testing'
+Requires-Dist: tomli-w >=1.0.0 ; extra == 'testing'
+Requires-Dist: pytest-timeout ; extra == 'testing'
+Requires-Dist: pytest-home >=0.5 ; extra == 'testing'
+Requires-Dist: mypy ==1.9 ; extra == 'testing'
+Requires-Dist: tomli ; extra == 'testing'
+Requires-Dist: importlib-metadata ; extra == 'testing'
+Provides-Extra: testing-integration
+Requires-Dist: pytest ; extra == 'testing-integration'
+Requires-Dist: pytest-xdist ; extra == 'testing-integration'
+Requires-Dist: pytest-enabler ; extra == 'testing-integration'
+Requires-Dist: virtualenv >=13.0.0 ; extra == 'testing-integration'
+Requires-Dist: tomli ; extra == 'testing-integration'
+Requires-Dist: wheel ; extra == 'testing-integration'
+Requires-Dist: jaraco.path >=3.2.0 ; extra == 'testing-integration'
+Requires-Dist: jaraco.envs >=2.2 ; extra == 'testing-integration'
+Requires-Dist: build[virtualenv] >=1.0.3 ; extra == 'testing-integration'
+Requires-Dist: filelock >=3.4.0 ; extra == 'testing-integration'
+Requires-Dist: packaging >=23.2 ; extra == 'testing-integration'
+Requires-Dist: pytest-cov ; (platform_python_implementation != "PyPy") and extra == 'testing'
+Requires-Dist: jaraco.develop >=7.21 ; (python_version >= "3.9" and sys_platform != "cygwin") and extra == 'testing'
+Requires-Dist: pytest-ruff >=0.2.1 ; (sys_platform != "cygwin") and extra == 'testing'
+Requires-Dist: pytest-perf ; (sys_platform != "cygwin") and extra == 'testing'
+
+.. image:: https://img.shields.io/pypi/v/setuptools.svg
+   :target: https://pypi.org/project/setuptools
+
+.. image:: https://img.shields.io/pypi/pyversions/setuptools.svg
+
+.. image:: https://github.com/pypa/setuptools/actions/workflows/main.yml/badge.svg
+   :target: https://github.com/pypa/setuptools/actions?query=workflow%3A%22tests%22
+   :alt: tests
+
+.. image:: https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/charliermarsh/ruff/main/assets/badge/v2.json
+    :target: https://github.com/astral-sh/ruff
+    :alt: Ruff
+
+.. image:: https://img.shields.io/readthedocs/setuptools/latest.svg
+    :target: https://setuptools.pypa.io
+
+.. image:: https://img.shields.io/badge/skeleton-2024-informational
+   :target: https://blog.jaraco.com/skeleton
+
+.. image:: https://img.shields.io/codecov/c/github/pypa/setuptools/master.svg?logo=codecov&logoColor=white
+   :target: https://codecov.io/gh/pypa/setuptools
+
+.. image:: https://tidelift.com/badges/github/pypa/setuptools?style=flat
+   :target: https://tidelift.com/subscription/pkg/pypi-setuptools?utm_source=pypi-setuptools&utm_medium=readme
+
+.. image:: https://img.shields.io/discord/803025117553754132
+   :target: https://discord.com/channels/803025117553754132/815945031150993468
+   :alt: Discord
+
+See the `Quickstart `_
+and the `User's Guide `_ for
+instructions on how to use Setuptools.
+
+Questions and comments should be directed to `GitHub Discussions
+`_.
+Bug reports and especially tested patches may be
+submitted directly to the `bug tracker
+`_.
+
+
+Code of Conduct
+===============
+
+Everyone interacting in the setuptools project's codebases, issue trackers,
+chat rooms, and fora is expected to follow the
+`PSF Code of Conduct `_.
+
+
+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 `_.
diff --git a/venv/Lib/site-packages/setuptools-69.5.1.dist-info/RECORD b/venv/Lib/site-packages/setuptools-69.5.1.dist-info/RECORD
new file mode 100644
index 0000000..832d599
--- /dev/null
+++ b/venv/Lib/site-packages/setuptools-69.5.1.dist-info/RECORD
@@ -0,0 +1,501 @@
+_distutils_hack/__init__.py,sha256=S_WwvI-K985wlulvcWfPT3dvLMCYP1yPh2yngLOmd4E,6002
+_distutils_hack/__pycache__/__init__.cpython-312.pyc,,
+_distutils_hack/__pycache__/override.cpython-312.pyc,,
+_distutils_hack/override.py,sha256=Eu_s-NF6VIZ4Cqd0tbbA5wtWky2IZPNd8et6GLt1mzo,44
+distutils-precedence.pth,sha256=JjjOniUA5XKl4N5_rtZmHrVp0baW_LoHsN0iPaX10iQ,151
+pkg_resources/__init__.py,sha256=7WnM_gj0UbuMN0knvkudaPeI-IhWbjshIx8JewOiDeM,108932
+pkg_resources/__pycache__/__init__.cpython-312.pyc,,
+pkg_resources/_vendor/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
+pkg_resources/_vendor/__pycache__/__init__.cpython-312.pyc,,
+pkg_resources/_vendor/__pycache__/typing_extensions.cpython-312.pyc,,
+pkg_resources/_vendor/__pycache__/zipp.cpython-312.pyc,,
+pkg_resources/_vendor/backports/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
+pkg_resources/_vendor/backports/__pycache__/__init__.cpython-312.pyc,,
+pkg_resources/_vendor/backports/__pycache__/tarfile.cpython-312.pyc,,
+pkg_resources/_vendor/backports/tarfile.py,sha256=IO3YX_ZYqn13VOi-3QLM0lnktn102U4d9wUrHc230LY,106920
+pkg_resources/_vendor/importlib_resources/__init__.py,sha256=evPm12kLgYqTm-pbzm60bOuumumT8IpBNWFp0uMyrzE,506
+pkg_resources/_vendor/importlib_resources/__pycache__/__init__.cpython-312.pyc,,
+pkg_resources/_vendor/importlib_resources/__pycache__/_adapters.cpython-312.pyc,,
+pkg_resources/_vendor/importlib_resources/__pycache__/_common.cpython-312.pyc,,
+pkg_resources/_vendor/importlib_resources/__pycache__/_compat.cpython-312.pyc,,
+pkg_resources/_vendor/importlib_resources/__pycache__/_itertools.cpython-312.pyc,,
+pkg_resources/_vendor/importlib_resources/__pycache__/_legacy.cpython-312.pyc,,
+pkg_resources/_vendor/importlib_resources/__pycache__/abc.cpython-312.pyc,,
+pkg_resources/_vendor/importlib_resources/__pycache__/readers.cpython-312.pyc,,
+pkg_resources/_vendor/importlib_resources/__pycache__/simple.cpython-312.pyc,,
+pkg_resources/_vendor/importlib_resources/_adapters.py,sha256=o51tP2hpVtohP33gSYyAkGNpLfYDBqxxYsadyiRZi1E,4504
+pkg_resources/_vendor/importlib_resources/_common.py,sha256=jSC4xfLdcMNbtbWHtpzbFkNa0W7kvf__nsYn14C_AEU,5457
+pkg_resources/_vendor/importlib_resources/_compat.py,sha256=L8HTWyAC_MIKuxWZuw0zvTq5qmUA0ttrvK941OzDKU8,2925
+pkg_resources/_vendor/importlib_resources/_itertools.py,sha256=WCdJ1Gs_kNFwKENyIG7TO0Y434IWCu0zjVVSsSbZwU8,884
+pkg_resources/_vendor/importlib_resources/_legacy.py,sha256=0TKdZixxLWA-xwtAZw4HcpqJmj4Xprx1Zkcty0gTRZY,3481
+pkg_resources/_vendor/importlib_resources/abc.py,sha256=Icr2IJ2QtH7vvAB9vC5WRJ9KBoaDyJa7KUs8McuROzo,5140
+pkg_resources/_vendor/importlib_resources/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
+pkg_resources/_vendor/importlib_resources/readers.py,sha256=PZsi5qacr2Qn3KHw4qw3Gm1MzrBblPHoTdjqjH7EKWw,3581
+pkg_resources/_vendor/importlib_resources/simple.py,sha256=0__2TQBTQoqkajYmNPt1HxERcReAT6boVKJA328pr04,2576
+pkg_resources/_vendor/jaraco/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
+pkg_resources/_vendor/jaraco/__pycache__/__init__.cpython-312.pyc,,
+pkg_resources/_vendor/jaraco/__pycache__/context.cpython-312.pyc,,
+pkg_resources/_vendor/jaraco/context.py,sha256=kScHctkohFywkhG-BhQXPo6dRH0Wj12FxxatDc_a5SY,9573
+pkg_resources/_vendor/jaraco/functools/__init__.py,sha256=wX5n1bAtZGFfApXarzPRqVAcNoOVzGVg9SLKyK8yYZA,16705
+pkg_resources/_vendor/jaraco/functools/__init__.pyi,sha256=N4lLbdhMtrmwiK3UuMGhYsiOLLZx69CUNOdmFPSVh6Q,3982
+pkg_resources/_vendor/jaraco/functools/__pycache__/__init__.cpython-312.pyc,,
+pkg_resources/_vendor/jaraco/functools/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
+pkg_resources/_vendor/jaraco/text/__init__.py,sha256=cN55bFcceW4wTHG5ruv5IuEDRarP-4hBYX8zl94_c30,15526
+pkg_resources/_vendor/jaraco/text/__pycache__/__init__.cpython-312.pyc,,
+pkg_resources/_vendor/more_itertools/__init__.py,sha256=VodgFyRJvpnHbAMgseYRiP7r928FFOAakmQrl6J88os,149
+pkg_resources/_vendor/more_itertools/__init__.pyi,sha256=5B3eTzON1BBuOLob1vCflyEb2lSd6usXQQ-Cv-hXkeA,43
+pkg_resources/_vendor/more_itertools/__pycache__/__init__.cpython-312.pyc,,
+pkg_resources/_vendor/more_itertools/__pycache__/more.cpython-312.pyc,,
+pkg_resources/_vendor/more_itertools/__pycache__/recipes.cpython-312.pyc,,
+pkg_resources/_vendor/more_itertools/more.py,sha256=Z4jylUFbhmn4gSZNFU2P0BgjzD_6uOcV8dfEmY3JZzY,143053
+pkg_resources/_vendor/more_itertools/more.pyi,sha256=KTHYeqr0rFbn1GWRnv0jY64JRNnKKT0kA3kmsah8DYQ,21044
+pkg_resources/_vendor/more_itertools/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
+pkg_resources/_vendor/more_itertools/recipes.py,sha256=Rb3OhzJTCn2biutDEUSImbuY-8NDS1lkHt0My-uCOf4,27548
+pkg_resources/_vendor/more_itertools/recipes.pyi,sha256=T1IuEVXCqw2NeJJNW036MtWi8BVfR8Ilpf7cBmvhBaQ,4436
+pkg_resources/_vendor/packaging/__init__.py,sha256=UzotcV07p8vcJzd80S-W0srhgY8NMVD_XvJcZ7JN-tA,496
+pkg_resources/_vendor/packaging/__pycache__/__init__.cpython-312.pyc,,
+pkg_resources/_vendor/packaging/__pycache__/_elffile.cpython-312.pyc,,
+pkg_resources/_vendor/packaging/__pycache__/_manylinux.cpython-312.pyc,,
+pkg_resources/_vendor/packaging/__pycache__/_musllinux.cpython-312.pyc,,
+pkg_resources/_vendor/packaging/__pycache__/_parser.cpython-312.pyc,,
+pkg_resources/_vendor/packaging/__pycache__/_structures.cpython-312.pyc,,
+pkg_resources/_vendor/packaging/__pycache__/_tokenizer.cpython-312.pyc,,
+pkg_resources/_vendor/packaging/__pycache__/markers.cpython-312.pyc,,
+pkg_resources/_vendor/packaging/__pycache__/metadata.cpython-312.pyc,,
+pkg_resources/_vendor/packaging/__pycache__/requirements.cpython-312.pyc,,
+pkg_resources/_vendor/packaging/__pycache__/specifiers.cpython-312.pyc,,
+pkg_resources/_vendor/packaging/__pycache__/tags.cpython-312.pyc,,
+pkg_resources/_vendor/packaging/__pycache__/utils.cpython-312.pyc,,
+pkg_resources/_vendor/packaging/__pycache__/version.cpython-312.pyc,,
+pkg_resources/_vendor/packaging/_elffile.py,sha256=hbmK8OD6Z7fY6hwinHEUcD1by7czkGiNYu7ShnFEk2k,3266
+pkg_resources/_vendor/packaging/_manylinux.py,sha256=1ng_TqyH49hY6s3W_zVHyoJIaogbJqbIF1jJ0fAehc4,9590
+pkg_resources/_vendor/packaging/_musllinux.py,sha256=kgmBGLFybpy8609-KTvzmt2zChCPWYvhp5BWP4JX7dE,2676
+pkg_resources/_vendor/packaging/_parser.py,sha256=zlsFB1FpMRjkUdQb6WLq7xON52ruQadxFpYsDXWhLb4,10347
+pkg_resources/_vendor/packaging/_structures.py,sha256=q3eVNmbWJGG_S0Dit_S3Ao8qQqz_5PYTXFAKBZe5yr4,1431
+pkg_resources/_vendor/packaging/_tokenizer.py,sha256=alCtbwXhOFAmFGZ6BQ-wCTSFoRAJ2z-ysIf7__MTJ_k,5292
+pkg_resources/_vendor/packaging/markers.py,sha256=eH-txS2zq1HdNpTd9LcZUcVIwewAiNU0grmq5wjKnOk,8208
+pkg_resources/_vendor/packaging/metadata.py,sha256=w7jPEg6mDf1FTZMn79aFxFuk4SKtynUJtxr2InTxlV4,33036
+pkg_resources/_vendor/packaging/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
+pkg_resources/_vendor/packaging/requirements.py,sha256=dgoBeVprPu2YE6Q8nGfwOPTjATHbRa_ZGLyXhFEln6Q,2933
+pkg_resources/_vendor/packaging/specifiers.py,sha256=dB2DwbmvSbEuVilEyiIQ382YfW5JfwzXTfRRPVtaENY,39784
+pkg_resources/_vendor/packaging/tags.py,sha256=fedHXiOHkBxNZTXotXv8uXPmMFU9ae-TKBujgYHigcA,18950
+pkg_resources/_vendor/packaging/utils.py,sha256=XgdmP3yx9-wQEFjO7OvMj9RjEf5JlR5HFFR69v7SQ9E,5268
+pkg_resources/_vendor/packaging/version.py,sha256=XjRBLNK17UMDgLeP8UHnqwiY3TdSi03xFQURtec211A,16236
+pkg_resources/_vendor/platformdirs/__init__.py,sha256=edi2JSKpLCapqir0AW_CjpHtinRE3hf6aDk5-VHggLk,12806
+pkg_resources/_vendor/platformdirs/__main__.py,sha256=VsC0t5m-6f0YVr96PVks93G3EDF8MSNY4KpUMvPahDA,1164
+pkg_resources/_vendor/platformdirs/__pycache__/__init__.cpython-312.pyc,,
+pkg_resources/_vendor/platformdirs/__pycache__/__main__.cpython-312.pyc,,
+pkg_resources/_vendor/platformdirs/__pycache__/android.cpython-312.pyc,,
+pkg_resources/_vendor/platformdirs/__pycache__/api.cpython-312.pyc,,
+pkg_resources/_vendor/platformdirs/__pycache__/macos.cpython-312.pyc,,
+pkg_resources/_vendor/platformdirs/__pycache__/unix.cpython-312.pyc,,
+pkg_resources/_vendor/platformdirs/__pycache__/version.cpython-312.pyc,,
+pkg_resources/_vendor/platformdirs/__pycache__/windows.cpython-312.pyc,,
+pkg_resources/_vendor/platformdirs/android.py,sha256=GKizhyS7ESRiU67u8UnBJLm46goau9937EchXWbPBlk,4068
+pkg_resources/_vendor/platformdirs/api.py,sha256=MXKHXOL3eh_-trSok-JUTjAR_zjmmKF3rjREVABjP8s,4910
+pkg_resources/_vendor/platformdirs/macos.py,sha256=-3UXQewbT0yMhMdkzRXfXGAntmLIH7Qt4a9Hlf8I5_Y,2655
+pkg_resources/_vendor/platformdirs/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
+pkg_resources/_vendor/platformdirs/unix.py,sha256=P-WQjSSieE38DXjMDa1t4XHnKJQ5idEaKT0PyXwm8KQ,6911
+pkg_resources/_vendor/platformdirs/version.py,sha256=qaN-fw_htIgKUVXoAuAEVgKxQu3tZ9qE2eiKkWIS7LA,160
+pkg_resources/_vendor/platformdirs/windows.py,sha256=LOrXLgI0CjQldDo2zhOZYGYZ6g4e_cJOCB_pF9aMRWQ,6596
+pkg_resources/_vendor/typing_extensions.py,sha256=ipqWiq5AHzrwczt6c26AP05Llh6a5_GaXRpOBqbogHA,80078
+pkg_resources/_vendor/zipp.py,sha256=ajztOH-9I7KA_4wqDYygtHa6xUBVZgFpmZ8FE74HHHI,8425
+pkg_resources/extern/__init__.py,sha256=pZcfqz80pENOUpghwVwLlGrsutYeYTvd_y5bbxqxLGQ,2459
+pkg_resources/extern/__pycache__/__init__.cpython-312.pyc,,
+setuptools-69.5.1.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4
+setuptools-69.5.1.dist-info/LICENSE,sha256=htoPAa6uRjSKPD1GUZXcHOzN55956HdppkuNoEsqR0E,1023
+setuptools-69.5.1.dist-info/METADATA,sha256=3R4RIeP-MfdERbIfjFbKN8JZyKLiFfrokQhw8vECzGY,6176
+setuptools-69.5.1.dist-info/RECORD,,
+setuptools-69.5.1.dist-info/REQUESTED,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
+setuptools-69.5.1.dist-info/WHEEL,sha256=GJ7t_kWBFywbagK5eo9IoUwLW6oyOeTKmQ-9iHFVNxQ,92
+setuptools-69.5.1.dist-info/entry_points.txt,sha256=Fe-UZkzgLTUZQOH94hbLTyP4HxM1nxlMuEZ_rS6zNnE,2676
+setuptools-69.5.1.dist-info/top_level.txt,sha256=d9yL39v_W7qmKDDSH6sT4bE0j_Ls1M3P161OGgdsm4g,41
+setuptools/__init__.py,sha256=-Z8Ch5HOLJYZzcKP0dx6LKPa84UWv5Bv9-5S41Dohic,9545
+setuptools/__pycache__/__init__.cpython-312.pyc,,
+setuptools/__pycache__/_core_metadata.cpython-312.pyc,,
+setuptools/__pycache__/_entry_points.cpython-312.pyc,,
+setuptools/__pycache__/_imp.cpython-312.pyc,,
+setuptools/__pycache__/_importlib.cpython-312.pyc,,
+setuptools/__pycache__/_itertools.cpython-312.pyc,,
+setuptools/__pycache__/_normalization.cpython-312.pyc,,
+setuptools/__pycache__/_path.cpython-312.pyc,,
+setuptools/__pycache__/_reqs.cpython-312.pyc,,
+setuptools/__pycache__/archive_util.cpython-312.pyc,,
+setuptools/__pycache__/build_meta.cpython-312.pyc,,
+setuptools/__pycache__/dep_util.cpython-312.pyc,,
+setuptools/__pycache__/depends.cpython-312.pyc,,
+setuptools/__pycache__/discovery.cpython-312.pyc,,
+setuptools/__pycache__/dist.cpython-312.pyc,,
+setuptools/__pycache__/errors.cpython-312.pyc,,
+setuptools/__pycache__/extension.cpython-312.pyc,,
+setuptools/__pycache__/glob.cpython-312.pyc,,
+setuptools/__pycache__/installer.cpython-312.pyc,,
+setuptools/__pycache__/launch.cpython-312.pyc,,
+setuptools/__pycache__/logging.cpython-312.pyc,,
+setuptools/__pycache__/modified.cpython-312.pyc,,
+setuptools/__pycache__/monkey.cpython-312.pyc,,
+setuptools/__pycache__/msvc.cpython-312.pyc,,
+setuptools/__pycache__/namespaces.cpython-312.pyc,,
+setuptools/__pycache__/package_index.cpython-312.pyc,,
+setuptools/__pycache__/sandbox.cpython-312.pyc,,
+setuptools/__pycache__/unicode_utils.cpython-312.pyc,,
+setuptools/__pycache__/version.cpython-312.pyc,,
+setuptools/__pycache__/warnings.cpython-312.pyc,,
+setuptools/__pycache__/wheel.cpython-312.pyc,,
+setuptools/__pycache__/windows_support.cpython-312.pyc,,
+setuptools/_core_metadata.py,sha256=9xBPVtBImw375XietaDA6TWy2s8br4URR1vrDbXDopE,9206
+setuptools/_distutils/__init__.py,sha256=xGYuhWwLG07J0Q49BVnEjPy6wyDcd6veJMDJX7ljlyM,359
+setuptools/_distutils/__pycache__/__init__.cpython-312.pyc,,
+setuptools/_distutils/__pycache__/_collections.cpython-312.pyc,,
+setuptools/_distutils/__pycache__/_functools.cpython-312.pyc,,
+setuptools/_distutils/__pycache__/_itertools.cpython-312.pyc,,
+setuptools/_distutils/__pycache__/_log.cpython-312.pyc,,
+setuptools/_distutils/__pycache__/_macos_compat.cpython-312.pyc,,
+setuptools/_distutils/__pycache__/_modified.cpython-312.pyc,,
+setuptools/_distutils/__pycache__/_msvccompiler.cpython-312.pyc,,
+setuptools/_distutils/__pycache__/archive_util.cpython-312.pyc,,
+setuptools/_distutils/__pycache__/bcppcompiler.cpython-312.pyc,,
+setuptools/_distutils/__pycache__/ccompiler.cpython-312.pyc,,
+setuptools/_distutils/__pycache__/cmd.cpython-312.pyc,,
+setuptools/_distutils/__pycache__/config.cpython-312.pyc,,
+setuptools/_distutils/__pycache__/core.cpython-312.pyc,,
+setuptools/_distutils/__pycache__/cygwinccompiler.cpython-312.pyc,,
+setuptools/_distutils/__pycache__/debug.cpython-312.pyc,,
+setuptools/_distutils/__pycache__/dep_util.cpython-312.pyc,,
+setuptools/_distutils/__pycache__/dir_util.cpython-312.pyc,,
+setuptools/_distutils/__pycache__/dist.cpython-312.pyc,,
+setuptools/_distutils/__pycache__/errors.cpython-312.pyc,,
+setuptools/_distutils/__pycache__/extension.cpython-312.pyc,,
+setuptools/_distutils/__pycache__/fancy_getopt.cpython-312.pyc,,
+setuptools/_distutils/__pycache__/file_util.cpython-312.pyc,,
+setuptools/_distutils/__pycache__/filelist.cpython-312.pyc,,
+setuptools/_distutils/__pycache__/log.cpython-312.pyc,,
+setuptools/_distutils/__pycache__/msvc9compiler.cpython-312.pyc,,
+setuptools/_distutils/__pycache__/msvccompiler.cpython-312.pyc,,
+setuptools/_distutils/__pycache__/py38compat.cpython-312.pyc,,
+setuptools/_distutils/__pycache__/py39compat.cpython-312.pyc,,
+setuptools/_distutils/__pycache__/spawn.cpython-312.pyc,,
+setuptools/_distutils/__pycache__/sysconfig.cpython-312.pyc,,
+setuptools/_distutils/__pycache__/text_file.cpython-312.pyc,,
+setuptools/_distutils/__pycache__/unixccompiler.cpython-312.pyc,,
+setuptools/_distutils/__pycache__/util.cpython-312.pyc,,
+setuptools/_distutils/__pycache__/version.cpython-312.pyc,,
+setuptools/_distutils/__pycache__/versionpredicate.cpython-312.pyc,,
+setuptools/_distutils/__pycache__/zosccompiler.cpython-312.pyc,,
+setuptools/_distutils/_collections.py,sha256=d7LOPjTlBpUHK7Wrkqo1Trjeo5sJ-ONRKpWIzfUzS0I,5440
+setuptools/_distutils/_functools.py,sha256=X0hb3XXNlzU0KNmF1oUO6rMpeAi12wxD5tSJZiXd3Zc,1771
+setuptools/_distutils/_itertools.py,sha256=dhBUItrpJTFwXeC-RdfkU_YP8JXRoUl6VluRV2op8kY,1453
+setuptools/_distutils/_log.py,sha256=i-lNTTcXS8TmWITJ6DODGvtW5z5tMattJQ76h8rZxQU,42
+setuptools/_distutils/_macos_compat.py,sha256=JzUGhF4E5yIITHbUaPobZEWjGHdrrcNV63z86S4RjBc,239
+setuptools/_distutils/_modified.py,sha256=jaEGgcY7bkqk3olA5cDI9O16usBAz80dajN5oUdkcAo,2411
+setuptools/_distutils/_msvccompiler.py,sha256=mbj-InpEOUD9R3TT8OPcN-s19nVc7SxNoeq8c0cpxfY,19613
+setuptools/_distutils/archive_util.py,sha256=hNweEX0yRwSBvKo1JyKOfqPyQENo6o98hH99jeXGbKw,8572
+setuptools/_distutils/bcppcompiler.py,sha256=ciMNFUbvcolFU7D5eC1FhHgZ4G4-9bhZZ3oD6bhAZq4,14662
+setuptools/_distutils/ccompiler.py,sha256=kYb8w9ZjqTpgsXQEfoRC9tWZFh8D0YwkQoGSHYkUbo4,48645
+setuptools/_distutils/cmd.py,sha256=4z-q1CBxUeE3cSsdT0qvIiqmRbyK2VDavWio96n8W3g,17801
+setuptools/_distutils/command/__init__.py,sha256=fVUps4DJhvShMAod0y7xl02m46bd7r31irEhNofPrrs,430
+setuptools/_distutils/command/__pycache__/__init__.cpython-312.pyc,,
+setuptools/_distutils/command/__pycache__/_framework_compat.cpython-312.pyc,,
+setuptools/_distutils/command/__pycache__/bdist.cpython-312.pyc,,
+setuptools/_distutils/command/__pycache__/bdist_dumb.cpython-312.pyc,,
+setuptools/_distutils/command/__pycache__/bdist_rpm.cpython-312.pyc,,
+setuptools/_distutils/command/__pycache__/build.cpython-312.pyc,,
+setuptools/_distutils/command/__pycache__/build_clib.cpython-312.pyc,,
+setuptools/_distutils/command/__pycache__/build_ext.cpython-312.pyc,,
+setuptools/_distutils/command/__pycache__/build_py.cpython-312.pyc,,
+setuptools/_distutils/command/__pycache__/build_scripts.cpython-312.pyc,,
+setuptools/_distutils/command/__pycache__/check.cpython-312.pyc,,
+setuptools/_distutils/command/__pycache__/clean.cpython-312.pyc,,
+setuptools/_distutils/command/__pycache__/config.cpython-312.pyc,,
+setuptools/_distutils/command/__pycache__/install.cpython-312.pyc,,
+setuptools/_distutils/command/__pycache__/install_data.cpython-312.pyc,,
+setuptools/_distutils/command/__pycache__/install_egg_info.cpython-312.pyc,,
+setuptools/_distutils/command/__pycache__/install_headers.cpython-312.pyc,,
+setuptools/_distutils/command/__pycache__/install_lib.cpython-312.pyc,,
+setuptools/_distutils/command/__pycache__/install_scripts.cpython-312.pyc,,
+setuptools/_distutils/command/__pycache__/register.cpython-312.pyc,,
+setuptools/_distutils/command/__pycache__/sdist.cpython-312.pyc,,
+setuptools/_distutils/command/__pycache__/upload.cpython-312.pyc,,
+setuptools/_distutils/command/_framework_compat.py,sha256=0iZdSJYzGRWCCvzRDKE-R0-_yaAYvFMd1ylXb2eYXug,1609
+setuptools/_distutils/command/bdist.py,sha256=1RJ0crzG6rfzT-rhUE6AERR_2x5ukhCrUQJpwalBKA4,5353
+setuptools/_distutils/command/bdist_dumb.py,sha256=CUHqSIXmRJjGrf-YcHtGIan7yqmjoDakFmww74L1vYw,4596
+setuptools/_distutils/command/bdist_rpm.py,sha256=VzZgWkpSz9Imu4eeQvvZWgsLSrr8h7arRjyZEZgO6Aw,21717
+setuptools/_distutils/command/build.py,sha256=etXMnBBx1ugT3knALXT3xH1kHvxE-Dkmp2okjxnWpuE,5575
+setuptools/_distutils/command/build_clib.py,sha256=KY94Cv6dpWyOjXLq1wiIpiGgkM_JajbQllm_voL0hNs,7686
+setuptools/_distutils/command/build_ext.py,sha256=ObN29w7LCc9UpxJmwFj6VAZgYYNit5BsvVs3qRpEQ-M,31793
+setuptools/_distutils/command/build_py.py,sha256=fI2fSFNlhQSi0nMKiTCJYWF6VEKx8aTviP3vzzipDJk,16539
+setuptools/_distutils/command/build_scripts.py,sha256=CYvH30nPYs0hFUIBsSXi4zsW5ZaZxZVaWodJdjzWJDo,5536
+setuptools/_distutils/command/check.py,sha256=eeoq60XhOfl7H8fy0aq0H6TCyZvL-JhnXtXAnsYSQgQ,4912
+setuptools/_distutils/command/clean.py,sha256=RFdfEwNlJZ-Sw_UPPYrcxqa0WIX9NHQE8pveS4BcS8A,2595
+setuptools/_distutils/command/config.py,sha256=Bhkk4KgD95HV7viLDPQqJF6kYCFbMle-2qZBTTWlPq0,13007
+setuptools/_distutils/command/install.py,sha256=FUQzZL_9dj8GuujA4l34jZiQHnew-p7Btfe6FTc6OhM,30128
+setuptools/_distutils/command/install_data.py,sha256=gl2h3cPwN4h388BKDazTtNt0J8KP9SXbbgB3MNVLLXg,2757
+setuptools/_distutils/command/install_egg_info.py,sha256=Sy2fuBqvCGlQVhJcUteGbdG0KCx2BBN7jAzWEMA9rp8,2788
+setuptools/_distutils/command/install_headers.py,sha256=v-QcVkjaWX5yf0xaup9_KySanVlmd6LhuzEhGpmTiTU,1180
+setuptools/_distutils/command/install_lib.py,sha256=x_6ULzcpTYXNEMrKe25h74tKlsaMe7exu2UCHL5tlok,8408
+setuptools/_distutils/command/install_scripts.py,sha256=hCYKJ2GzfuZK7RgSNDcVI53c1o4U7rHDFDB96N71Z0E,1933
+setuptools/_distutils/command/register.py,sha256=GIbedaPEuVJasaJ6LO3i9PJ1tkjQAWKFn20o9JznazQ,11805
+setuptools/_distutils/command/sdist.py,sha256=vDwW5w9ev4znK0ayOKYGysSzHWLhLz6_jaVKWdzHGEc,19188
+setuptools/_distutils/command/upload.py,sha256=65zhvH62G7Q3MdO5IXtMWtFNLNSYVengkh_LNDGiBng,7497
+setuptools/_distutils/compat/__init__.py,sha256=fdLzjO_CkfzBFwOJX9XIfXbBkeYKzMiVABd5T4dLZOU,417
+setuptools/_distutils/compat/__pycache__/__init__.cpython-312.pyc,,
+setuptools/_distutils/compat/__pycache__/py38.cpython-312.pyc,,
+setuptools/_distutils/compat/py38.py,sha256=2hge9_F3LivW4R4ptFqsG5sb6WrsQc9V30lwMWv86hg,567
+setuptools/_distutils/config.py,sha256=cCdL7BAavdh_2tDW0EZp1ro1vTeWhFZzLXN1P6HAl6U,5226
+setuptools/_distutils/core.py,sha256=WPZrrf2mbM9VPg4o_jjARPVgLzQNYd_atMTfNr2P1vI,9372
+setuptools/_distutils/cygwinccompiler.py,sha256=SBP-3cAMXplgToYmdeYh6riK3JMgrZPNa9dqOqKCiZ0,11945
+setuptools/_distutils/debug.py,sha256=N6MrTAqK6l9SVk6tWweR108PM8Ol7qNlfyV-nHcLhsY,139
+setuptools/_distutils/dep_util.py,sha256=xN75p6ZpHhMiHEc-rpL2XilJQynHnDNiafHteaZ4tjU,349
+setuptools/_distutils/dir_util.py,sha256=aO95MLDq2_Bw7PIheB-IeoYF2xc9yfo-CMlJg2OlwQY,7965
+setuptools/_distutils/dist.py,sha256=RGSfGMKDrILGwuylDbx7MLr-c298IzyCJKzwO2y1tPY,50116
+setuptools/_distutils/errors.py,sha256=ZtBwnhDpQA2bxIazPXNDQ25uNxM4p2omsaSRNpV3rpE,3589
+setuptools/_distutils/extension.py,sha256=qwg1CNJgN2Gq7bFFfQz2Jpb8LfL-Ec2FTli8Q5qhaD4,10197
+setuptools/_distutils/fancy_getopt.py,sha256=VxwHHtyJi8Qp8ZwKuJhzxSkX9xwqCqqAxq4d8VbkAWY,17831
+setuptools/_distutils/file_util.py,sha256=3A_9gykBBHFn0Qf8EzkJYaqYzmQb8-kqlHQhsakACvk,7926
+setuptools/_distutils/filelist.py,sha256=8EQ8bV9x3XAW8da6CIyMH5_qo2Ff0FJFHyVpiPYFWMM,13635
+setuptools/_distutils/log.py,sha256=VyBs5j7z4-K6XTEEBThUc9HyMpoPLGtQpERqbz5ylww,1200
+setuptools/_distutils/msvc9compiler.py,sha256=-YXqSr2-OTygm6ScHmKRrI_gLZlUZKEoWHsucNBteL0,30108
+setuptools/_distutils/msvccompiler.py,sha256=K4SnI248hv37lw8QujtHKtEXbTdWdJwQ5IzarCQW8Xk,23443
+setuptools/_distutils/py38compat.py,sha256=ip0qdoFG3-1dJ5SsLgSaZpycOjpg5Y_on7btsPDS8kw,205
+setuptools/_distutils/py39compat.py,sha256=hOsD6lwZLqZoMnacNJ3P6nUA-LJQhEpVtYTzVH0o96M,1964
+setuptools/_distutils/spawn.py,sha256=9LUwbJsZcwVrMx78ynZIeOFfeGTV41-bf9GgsEguR04,3431
+setuptools/_distutils/sysconfig.py,sha256=sDrPTFl-QzVlneWA4SaAf_AeTIZE-pytWnxGfRHqBIM,18485
+setuptools/_distutils/text_file.py,sha256=MSzP1kmjFZOwnrdQsqwG3ZWNa2wQ4OXK75J3SWOX05g,12100
+setuptools/_distutils/unixccompiler.py,sha256=pgmWd50imVfEshJuy-fNWqcRXDft_C8_UErW68c_n2I,15572
+setuptools/_distutils/util.py,sha256=HwvslM2iEnIvETbPf6NEc1RCxoYfRH-Pfu5KXzCf5IE,18120
+setuptools/_distutils/version.py,sha256=tSFYhuKjxom5kONNB3gOTNf52bORqzmUN64LxFT5QoU,12648
+setuptools/_distutils/versionpredicate.py,sha256=ub3qV0ImwzVR-Ie-s9OtN6QQ-HvDUPwhfsiJWHPAU7U,5205
+setuptools/_distutils/zosccompiler.py,sha256=mOyTYXmIwq5fE61etbj9QmD2uu3iPDftBKp9c1OauW0,6573
+setuptools/_entry_points.py,sha256=P-Utt8hvMGkkJdw7VPzZ00uijeA9dohUCMTcDbbeQkU,2235
+setuptools/_imp.py,sha256=1Y1gH0NOppV4nbr1eidD5iGQ8UVPfiVZi6rTqrfC06c,2433
+setuptools/_importlib.py,sha256=ZWlYbGHjb-QwRpH3SQ9uuxn_X-F2ihcQCS5HtT_W9lk,1468
+setuptools/_itertools.py,sha256=pZAgXNz6tRPUFnHAaKJ90xAgD0gLPemcE1396Zgz73o,675
+setuptools/_normalization.py,sha256=tHGXlQIPTDkp6Pxe4aKjRFwG0KOjS4httfHM2hzkLEQ,4567
+setuptools/_path.py,sha256=9tt6GFnNgFPDMIbupEYk8EiOX9k2Wj9hpdkK6kb1HeY,1178
+setuptools/_reqs.py,sha256=hUjP8Mh6D6BbLEhlFa0zksdLnLF2tMxgFPuQSJApEd4,1112
+setuptools/_vendor/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
+setuptools/_vendor/__pycache__/__init__.cpython-312.pyc,,
+setuptools/_vendor/__pycache__/ordered_set.cpython-312.pyc,,
+setuptools/_vendor/__pycache__/typing_extensions.cpython-312.pyc,,
+setuptools/_vendor/__pycache__/zipp.cpython-312.pyc,,
+setuptools/_vendor/backports/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
+setuptools/_vendor/backports/__pycache__/__init__.cpython-312.pyc,,
+setuptools/_vendor/backports/__pycache__/tarfile.cpython-312.pyc,,
+setuptools/_vendor/backports/tarfile.py,sha256=IO3YX_ZYqn13VOi-3QLM0lnktn102U4d9wUrHc230LY,106920
+setuptools/_vendor/importlib_metadata/__init__.py,sha256=fQEsJb7Gs_9Vq9V0xHICB0EFxNRGyxubr4w4ZFmGcxY,26498
+setuptools/_vendor/importlib_metadata/__pycache__/__init__.cpython-312.pyc,,
+setuptools/_vendor/importlib_metadata/__pycache__/_adapters.cpython-312.pyc,,
+setuptools/_vendor/importlib_metadata/__pycache__/_collections.cpython-312.pyc,,
+setuptools/_vendor/importlib_metadata/__pycache__/_compat.cpython-312.pyc,,
+setuptools/_vendor/importlib_metadata/__pycache__/_functools.cpython-312.pyc,,
+setuptools/_vendor/importlib_metadata/__pycache__/_itertools.cpython-312.pyc,,
+setuptools/_vendor/importlib_metadata/__pycache__/_meta.cpython-312.pyc,,
+setuptools/_vendor/importlib_metadata/__pycache__/_py39compat.cpython-312.pyc,,
+setuptools/_vendor/importlib_metadata/__pycache__/_text.cpython-312.pyc,,
+setuptools/_vendor/importlib_metadata/_adapters.py,sha256=i8S6Ib1OQjcILA-l4gkzktMZe18TaeUNI49PLRp6OBU,2454
+setuptools/_vendor/importlib_metadata/_collections.py,sha256=CJ0OTCHIjWA0ZIVS4voORAsn2R4R2cQBEtPsZEJpASY,743
+setuptools/_vendor/importlib_metadata/_compat.py,sha256=GtdqmFy_ykVSTkz6MdGL2g3V5kxvQKHTWxKZCk5Q59Q,1859
+setuptools/_vendor/importlib_metadata/_functools.py,sha256=PsY2-4rrKX4RVeRC1oGp1lB1pmC9eKN88_f-bD9uOoA,2895
+setuptools/_vendor/importlib_metadata/_itertools.py,sha256=cvr_2v8BRbxcIl5x5ldfqdHjhI8Yi8s8yk50G_nm6jQ,2068
+setuptools/_vendor/importlib_metadata/_meta.py,sha256=v5e1ZDG7yZTH3h7TjbS5bM5p8AGzMPVOu8skDMv4h6k,1165
+setuptools/_vendor/importlib_metadata/_py39compat.py,sha256=2Tk5twb_VgLCY-1NEAQjdZp_S9OFMC-pUzP2isuaPsQ,1098
+setuptools/_vendor/importlib_metadata/_text.py,sha256=HCsFksZpJLeTP3NEk_ngrAeXVRRtTrtyh9eOABoRP4A,2166
+setuptools/_vendor/importlib_metadata/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
+setuptools/_vendor/importlib_resources/__init__.py,sha256=evPm12kLgYqTm-pbzm60bOuumumT8IpBNWFp0uMyrzE,506
+setuptools/_vendor/importlib_resources/__pycache__/__init__.cpython-312.pyc,,
+setuptools/_vendor/importlib_resources/__pycache__/_adapters.cpython-312.pyc,,
+setuptools/_vendor/importlib_resources/__pycache__/_common.cpython-312.pyc,,
+setuptools/_vendor/importlib_resources/__pycache__/_compat.cpython-312.pyc,,
+setuptools/_vendor/importlib_resources/__pycache__/_itertools.cpython-312.pyc,,
+setuptools/_vendor/importlib_resources/__pycache__/_legacy.cpython-312.pyc,,
+setuptools/_vendor/importlib_resources/__pycache__/abc.cpython-312.pyc,,
+setuptools/_vendor/importlib_resources/__pycache__/readers.cpython-312.pyc,,
+setuptools/_vendor/importlib_resources/__pycache__/simple.cpython-312.pyc,,
+setuptools/_vendor/importlib_resources/_adapters.py,sha256=o51tP2hpVtohP33gSYyAkGNpLfYDBqxxYsadyiRZi1E,4504
+setuptools/_vendor/importlib_resources/_common.py,sha256=jSC4xfLdcMNbtbWHtpzbFkNa0W7kvf__nsYn14C_AEU,5457
+setuptools/_vendor/importlib_resources/_compat.py,sha256=L8HTWyAC_MIKuxWZuw0zvTq5qmUA0ttrvK941OzDKU8,2925
+setuptools/_vendor/importlib_resources/_itertools.py,sha256=WCdJ1Gs_kNFwKENyIG7TO0Y434IWCu0zjVVSsSbZwU8,884
+setuptools/_vendor/importlib_resources/_legacy.py,sha256=0TKdZixxLWA-xwtAZw4HcpqJmj4Xprx1Zkcty0gTRZY,3481
+setuptools/_vendor/importlib_resources/abc.py,sha256=Icr2IJ2QtH7vvAB9vC5WRJ9KBoaDyJa7KUs8McuROzo,5140
+setuptools/_vendor/importlib_resources/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
+setuptools/_vendor/importlib_resources/readers.py,sha256=PZsi5qacr2Qn3KHw4qw3Gm1MzrBblPHoTdjqjH7EKWw,3581
+setuptools/_vendor/importlib_resources/simple.py,sha256=0__2TQBTQoqkajYmNPt1HxERcReAT6boVKJA328pr04,2576
+setuptools/_vendor/jaraco/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
+setuptools/_vendor/jaraco/__pycache__/__init__.cpython-312.pyc,,
+setuptools/_vendor/jaraco/__pycache__/context.cpython-312.pyc,,
+setuptools/_vendor/jaraco/context.py,sha256=cSeYAAUQS_mPUABUWvk-FRHw1nWcDYEgd6SysLxvHTw,9570
+setuptools/_vendor/jaraco/functools/__init__.py,sha256=EMUExgjM40npn-5G6NgcUI5Mmc74WyYPavjtmxRATH0,16696
+setuptools/_vendor/jaraco/functools/__init__.pyi,sha256=N4lLbdhMtrmwiK3UuMGhYsiOLLZx69CUNOdmFPSVh6Q,3982
+setuptools/_vendor/jaraco/functools/__pycache__/__init__.cpython-312.pyc,,
+setuptools/_vendor/jaraco/functools/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
+setuptools/_vendor/jaraco/text/__init__.py,sha256=KfFGMerrkN_0V0rgtJVx-9dHt3tW7i_uJypjwEcLtC0,15517
+setuptools/_vendor/jaraco/text/__pycache__/__init__.cpython-312.pyc,,
+setuptools/_vendor/more_itertools/__init__.py,sha256=C7sXffHTXM3P-iaLPPfqfmDoxOflQMJLcM7ed9p3jak,82
+setuptools/_vendor/more_itertools/__init__.pyi,sha256=5B3eTzON1BBuOLob1vCflyEb2lSd6usXQQ-Cv-hXkeA,43
+setuptools/_vendor/more_itertools/__pycache__/__init__.cpython-312.pyc,,
+setuptools/_vendor/more_itertools/__pycache__/more.cpython-312.pyc,,
+setuptools/_vendor/more_itertools/__pycache__/recipes.cpython-312.pyc,,
+setuptools/_vendor/more_itertools/more.py,sha256=0rB_mibFR51sq33UlAI_bWfaNdsYNnJr1v6S0CaW7QA,117959
+setuptools/_vendor/more_itertools/more.pyi,sha256=r32pH2raBC1zih3evK4fyvAXvrUamJqc6dgV7QCRL_M,14977
+setuptools/_vendor/more_itertools/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
+setuptools/_vendor/more_itertools/recipes.py,sha256=UkNkrsZyqiwgLHANBTmvMhCvaNSvSNYhyOpz_Jc55DY,16256
+setuptools/_vendor/more_itertools/recipes.pyi,sha256=9BpeKd5_qalYVSnuHfqPSCfoGgqnQY2Xu9pNwrDlHU8,3551
+setuptools/_vendor/ordered_set.py,sha256=dbaCcs27dyN9gnMWGF5nA_BrVn6Q-NrjKYJpV9_fgBs,15130
+setuptools/_vendor/packaging/__init__.py,sha256=UzotcV07p8vcJzd80S-W0srhgY8NMVD_XvJcZ7JN-tA,496
+setuptools/_vendor/packaging/__pycache__/__init__.cpython-312.pyc,,
+setuptools/_vendor/packaging/__pycache__/_elffile.cpython-312.pyc,,
+setuptools/_vendor/packaging/__pycache__/_manylinux.cpython-312.pyc,,
+setuptools/_vendor/packaging/__pycache__/_musllinux.cpython-312.pyc,,
+setuptools/_vendor/packaging/__pycache__/_parser.cpython-312.pyc,,
+setuptools/_vendor/packaging/__pycache__/_structures.cpython-312.pyc,,
+setuptools/_vendor/packaging/__pycache__/_tokenizer.cpython-312.pyc,,
+setuptools/_vendor/packaging/__pycache__/markers.cpython-312.pyc,,
+setuptools/_vendor/packaging/__pycache__/metadata.cpython-312.pyc,,
+setuptools/_vendor/packaging/__pycache__/requirements.cpython-312.pyc,,
+setuptools/_vendor/packaging/__pycache__/specifiers.cpython-312.pyc,,
+setuptools/_vendor/packaging/__pycache__/tags.cpython-312.pyc,,
+setuptools/_vendor/packaging/__pycache__/utils.cpython-312.pyc,,
+setuptools/_vendor/packaging/__pycache__/version.cpython-312.pyc,,
+setuptools/_vendor/packaging/_elffile.py,sha256=hbmK8OD6Z7fY6hwinHEUcD1by7czkGiNYu7ShnFEk2k,3266
+setuptools/_vendor/packaging/_manylinux.py,sha256=1ng_TqyH49hY6s3W_zVHyoJIaogbJqbIF1jJ0fAehc4,9590
+setuptools/_vendor/packaging/_musllinux.py,sha256=kgmBGLFybpy8609-KTvzmt2zChCPWYvhp5BWP4JX7dE,2676
+setuptools/_vendor/packaging/_parser.py,sha256=zlsFB1FpMRjkUdQb6WLq7xON52ruQadxFpYsDXWhLb4,10347
+setuptools/_vendor/packaging/_structures.py,sha256=q3eVNmbWJGG_S0Dit_S3Ao8qQqz_5PYTXFAKBZe5yr4,1431
+setuptools/_vendor/packaging/_tokenizer.py,sha256=alCtbwXhOFAmFGZ6BQ-wCTSFoRAJ2z-ysIf7__MTJ_k,5292
+setuptools/_vendor/packaging/markers.py,sha256=eH-txS2zq1HdNpTd9LcZUcVIwewAiNU0grmq5wjKnOk,8208
+setuptools/_vendor/packaging/metadata.py,sha256=w7jPEg6mDf1FTZMn79aFxFuk4SKtynUJtxr2InTxlV4,33036
+setuptools/_vendor/packaging/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
+setuptools/_vendor/packaging/requirements.py,sha256=dgoBeVprPu2YE6Q8nGfwOPTjATHbRa_ZGLyXhFEln6Q,2933
+setuptools/_vendor/packaging/specifiers.py,sha256=dB2DwbmvSbEuVilEyiIQ382YfW5JfwzXTfRRPVtaENY,39784
+setuptools/_vendor/packaging/tags.py,sha256=fedHXiOHkBxNZTXotXv8uXPmMFU9ae-TKBujgYHigcA,18950
+setuptools/_vendor/packaging/utils.py,sha256=XgdmP3yx9-wQEFjO7OvMj9RjEf5JlR5HFFR69v7SQ9E,5268
+setuptools/_vendor/packaging/version.py,sha256=XjRBLNK17UMDgLeP8UHnqwiY3TdSi03xFQURtec211A,16236
+setuptools/_vendor/tomli/__init__.py,sha256=JhUwV66DB1g4Hvt1UQCVMdfCu-IgAV8FXmvDU9onxd4,396
+setuptools/_vendor/tomli/__pycache__/__init__.cpython-312.pyc,,
+setuptools/_vendor/tomli/__pycache__/_parser.cpython-312.pyc,,
+setuptools/_vendor/tomli/__pycache__/_re.cpython-312.pyc,,
+setuptools/_vendor/tomli/__pycache__/_types.cpython-312.pyc,,
+setuptools/_vendor/tomli/_parser.py,sha256=g9-ENaALS-B8dokYpCuzUFalWlog7T-SIYMjLZSWrtM,22633
+setuptools/_vendor/tomli/_re.py,sha256=dbjg5ChZT23Ka9z9DHOXfdtSpPwUfdgMXnj8NOoly-w,2943
+setuptools/_vendor/tomli/_types.py,sha256=-GTG2VUqkpxwMqzmVO4F7ybKddIbAnuAHXfmWQcTi3Q,254
+setuptools/_vendor/tomli/py.typed,sha256=8PjyZ1aVoQpRVvt71muvuq5qE-jTFZkK-GLHkhdebmc,26
+setuptools/_vendor/typing_extensions.py,sha256=1uqi_RSlI7gos4eJB_NEV3d5wQwzTUQHd3_jrkbTo8Q,87149
+setuptools/_vendor/zipp.py,sha256=ajztOH-9I7KA_4wqDYygtHa6xUBVZgFpmZ8FE74HHHI,8425
+setuptools/archive_util.py,sha256=lRK7l7GkpLJeNqnESJWUfDre4q4wR9x6Z8WD3cIagXc,7331
+setuptools/build_meta.py,sha256=905wNJ-znWf_rM4ApCVlrgueg3XYSaE-LZxl7fW8Bg8,18740
+setuptools/cli-32.exe,sha256=MqzBvFQxFsviz_EMuGd3LfLyVP8mNMhwrvC0bEtpb9s,11776
+setuptools/cli-64.exe,sha256=u7PeVwdinmpgoMI4zUd7KPB_AGaYL9qVP6b87DkHOko,14336
+setuptools/cli-arm64.exe,sha256=uafQjaiA36yLz1SOuksG-1m28JsX0zFIoPZhgyiSbGE,13824
+setuptools/cli.exe,sha256=MqzBvFQxFsviz_EMuGd3LfLyVP8mNMhwrvC0bEtpb9s,11776
+setuptools/command/__init__.py,sha256=HZlSppOB8Vro73ffvP-xrORuMrh4GnVkOqJspFRG8Pg,396
+setuptools/command/__pycache__/__init__.cpython-312.pyc,,
+setuptools/command/__pycache__/_requirestxt.cpython-312.pyc,,
+setuptools/command/__pycache__/alias.cpython-312.pyc,,
+setuptools/command/__pycache__/bdist_egg.cpython-312.pyc,,
+setuptools/command/__pycache__/bdist_rpm.cpython-312.pyc,,
+setuptools/command/__pycache__/build.cpython-312.pyc,,
+setuptools/command/__pycache__/build_clib.cpython-312.pyc,,
+setuptools/command/__pycache__/build_ext.cpython-312.pyc,,
+setuptools/command/__pycache__/build_py.cpython-312.pyc,,
+setuptools/command/__pycache__/develop.cpython-312.pyc,,
+setuptools/command/__pycache__/dist_info.cpython-312.pyc,,
+setuptools/command/__pycache__/easy_install.cpython-312.pyc,,
+setuptools/command/__pycache__/editable_wheel.cpython-312.pyc,,
+setuptools/command/__pycache__/egg_info.cpython-312.pyc,,
+setuptools/command/__pycache__/install.cpython-312.pyc,,
+setuptools/command/__pycache__/install_egg_info.cpython-312.pyc,,
+setuptools/command/__pycache__/install_lib.cpython-312.pyc,,
+setuptools/command/__pycache__/install_scripts.cpython-312.pyc,,
+setuptools/command/__pycache__/register.cpython-312.pyc,,
+setuptools/command/__pycache__/rotate.cpython-312.pyc,,
+setuptools/command/__pycache__/saveopts.cpython-312.pyc,,
+setuptools/command/__pycache__/sdist.cpython-312.pyc,,
+setuptools/command/__pycache__/setopt.cpython-312.pyc,,
+setuptools/command/__pycache__/test.cpython-312.pyc,,
+setuptools/command/__pycache__/upload.cpython-312.pyc,,
+setuptools/command/__pycache__/upload_docs.cpython-312.pyc,,
+setuptools/command/_requirestxt.py,sha256=XZG2b3CDbL_yGpNl7UL7f5kXMb9OpzQqfxkGnolkySs,4222
+setuptools/command/alias.py,sha256=1holrSsdYxp1Esoa2yfRHLjiYlCRi3jYZy2yWm62YVU,2383
+setuptools/command/bdist_egg.py,sha256=H7KHA5bWO27csk85g4AdAGHUswV8tfpEfRadJNmBzrs,16433
+setuptools/command/bdist_rpm.py,sha256=ilQc1XVNBxv1O-8URD3iSTqszndQ2SWBpZVmrR2awO0,1289
+setuptools/command/build.py,sha256=yO3zBGzbhMGOye1j2M6yLhUK5kD3rpkFhNLnnF-AkXs,6598
+setuptools/command/build_clib.py,sha256=0Ab0Kppziw4ng_51ODfQxF8HxU6ea89EEyBC8kWHtnc,4539
+setuptools/command/build_ext.py,sha256=uywy5kDcCUiOBO6NX3FYWX3MmVF1MvO90c_4yJTTEx0,17724
+setuptools/command/build_py.py,sha256=x-WK8RIooqdA4HI9Vwplb0M7-UN0U3QVYXI0wAgvA_4,15127
+setuptools/command/develop.py,sha256=TRIjVJmNB97k_ZehYU_EBR8_cgwB4cTjUosioMODq24,6834
+setuptools/command/dist_info.py,sha256=f3bldKO2zvrZPu9emGLnfKJDdSqLzIdVFqgDw3_-JWI,3507
+setuptools/command/easy_install.py,sha256=qHaippmb1ut9ZVT2qDSPVrc2AHt3xy_HlRuTEh3vi9s,87138
+setuptools/command/editable_wheel.py,sha256=GKPUR6nk41vhbeQjiZK2n-Db7K3QeIgky8Bp1wfTqXc,34488
+setuptools/command/egg_info.py,sha256=mtxpG6vx11vV4Pda_dFJ5OGszaaW09KkalTq0v6MJD4,26516
+setuptools/command/install.py,sha256=V0kDP09weAXRgEO5PwHpQ3APdOkG3rCrAAP-MtcAMvk,5815
+setuptools/command/install_egg_info.py,sha256=zpDDCmOJspfkEekUON7wU0ABFNW-0uXUZpzpHRYUdiI,2066
+setuptools/command/install_lib.py,sha256=gUEW1ACrDcK_Mq7_RiF3YUlKA-9e-Tq9AcQs7KA-glk,3870
+setuptools/command/install_scripts.py,sha256=n2toonBXHYFZcn2wkZ7eNl15c816kouMiNpNuTjIKSo,2359
+setuptools/command/launcher manifest.xml,sha256=xlLbjWrB01tKC0-hlVkOKkiSPbzMml2eOPtJ_ucCnbE,628
+setuptools/command/register.py,sha256=kk3DxXCb5lXTvqnhfwx2g6q7iwbUmgTyXUCaBooBOUk,468
+setuptools/command/rotate.py,sha256=P9qx7ddEo3iExuagaDgM1Z2r-x7Rd87IngXNkClu1Ig,2132
+setuptools/command/saveopts.py,sha256=mVAPMRIGE98gl6eXQ3C2Wo-qPOgl9lbH-Q_YsbLuqeg,657
+setuptools/command/sdist.py,sha256=xOp-NirTZBB2twdMB8bxVUiT3vE0qjyZBs4xwADrVEw,6811
+setuptools/command/setopt.py,sha256=CTNgVkgm2yV9c1bO9wem86_M8X6TGyuEtitUjBzGwBc,4927
+setuptools/command/test.py,sha256=DuDGCsBp2IjxCI8NHQ-71KseaN6ln1y-nIwBbLrxRu0,8101
+setuptools/command/upload.py,sha256=XT3YFVfYPAmA5qhGg0euluU98ftxRUW-PzKcODMLxUs,462
+setuptools/command/upload_docs.py,sha256=5zI6dqDdGl2ekSuMq1IcRRbh-9l-ivfb4Mq-UWM0rj4,7821
+setuptools/compat/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
+setuptools/compat/__pycache__/__init__.cpython-312.pyc,,
+setuptools/compat/__pycache__/py310.cpython-312.pyc,,
+setuptools/compat/__pycache__/py311.cpython-312.pyc,,
+setuptools/compat/__pycache__/py39.cpython-312.pyc,,
+setuptools/compat/py310.py,sha256=JSoeLHioO2fx1LVGZDL8IAU_7SDpRU33XBnPXLTMdyM,165
+setuptools/compat/py311.py,sha256=6qfRL57v2DWBBQdqv-w_T70KxK0iowZiCLVhESfj36Y,330
+setuptools/compat/py39.py,sha256=BJMtnkfcqyTfccqjYQxfoRtU2nTnWaEESBVkshTiXqY,493
+setuptools/config/__init__.py,sha256=aiPnL9BJn1O6MfmuNXyn8W2Lp8u9qizRVqwPiOdPIjY,1499
+setuptools/config/__pycache__/__init__.cpython-312.pyc,,
+setuptools/config/__pycache__/_apply_pyprojecttoml.cpython-312.pyc,,
+setuptools/config/__pycache__/expand.cpython-312.pyc,,
+setuptools/config/__pycache__/pyprojecttoml.cpython-312.pyc,,
+setuptools/config/__pycache__/setupcfg.cpython-312.pyc,,
+setuptools/config/_apply_pyprojecttoml.py,sha256=Rg4IDYrd6Rq2Lti8r2nahqH9cYN-RMtKX_3fOAEWHOo,14791
+setuptools/config/_validate_pyproject/__init__.py,sha256=5YXPW1sabVn5jpZ25sUjeF6ij3_4odJiwUWi4nRD2Dc,1038
+setuptools/config/_validate_pyproject/__pycache__/__init__.cpython-312.pyc,,
+setuptools/config/_validate_pyproject/__pycache__/error_reporting.cpython-312.pyc,,
+setuptools/config/_validate_pyproject/__pycache__/extra_validations.cpython-312.pyc,,
+setuptools/config/_validate_pyproject/__pycache__/fastjsonschema_exceptions.cpython-312.pyc,,
+setuptools/config/_validate_pyproject/__pycache__/fastjsonschema_validations.cpython-312.pyc,,
+setuptools/config/_validate_pyproject/__pycache__/formats.cpython-312.pyc,,
+setuptools/config/_validate_pyproject/error_reporting.py,sha256=-nThzvKypjeTDc-LcTzoyt7RNeYz3rXWCDLJt6FqiYQ,11266
+setuptools/config/_validate_pyproject/extra_validations.py,sha256=wHzrgfdZUMRPBR1ke1lg5mhqRsBSbjEYOMsuFXQH9jY,1153
+setuptools/config/_validate_pyproject/fastjsonschema_exceptions.py,sha256=w749JgqKi8clBFcObdcbZVqsmF4oJ_QByhZ1SGbUFNw,1612
+setuptools/config/_validate_pyproject/fastjsonschema_validations.py,sha256=RV4vL4C6X83K20PpdzlxlBd-9PzKhW3gY6a7Zv1P9OM,274893
+setuptools/config/_validate_pyproject/formats.py,sha256=-3f_VtIrcgY95yILC5-o-jh51Woj9Q0RhL3bmbOjJ-E,9160
+setuptools/config/expand.py,sha256=I7OGe4UsgZJA_efWGgdMDSnRfZ2FHHUZXXCAMdZtSfo,16456
+setuptools/config/pyprojecttoml.py,sha256=EUxSsdRQ-IMw7qlXj6XJVTvYXQ3Dd9obdmudrSMsCio,17383
+setuptools/config/setupcfg.py,sha256=9mffjaaI8OMxu-PLUizoYewbPskeztjc88odyKTzgkY,25630
+setuptools/dep_util.py,sha256=gwvE8CrtOResUCFoGRSJYQPPM2-llLYf1iWyXPsgJ8E,659
+setuptools/depends.py,sha256=fty6ArbOrU0QJxPIneqYNIvXB1nGL1NajjxcvWPztqQ,5599
+setuptools/discovery.py,sha256=_Wm2FkHoH5a8uF_EBauQF_MqtGxEKzWLEe0G7plyOrU,21152
+setuptools/dist.py,sha256=8h_njmvsFxiVR47Ps5Aa3hY_C8T7NWelAN_D33IqaVk,37504
+setuptools/errors.py,sha256=FS-3MTIzgv7ciswOrK71KMuEUcYh9kkWUYXd895Gbew,2669
+setuptools/extension.py,sha256=xabD6ozEdZy3H4O-TLO6fP4WW-RTDMZ76WTSbdmiIVQ,5807
+setuptools/extern/__init__.py,sha256=Zqg7sIqZuifvGRcT9YGiZ-Dv4T3vYYNWYdEkvXDGbSI,2556
+setuptools/extern/__pycache__/__init__.cpython-312.pyc,,
+setuptools/glob.py,sha256=BYhyV7s5INjv-JMsz4SNuQWGTzY4lZVLD5_euTYsFxc,4852
+setuptools/gui-32.exe,sha256=hdrh6V13hF8stZvKw9Sv50u-TJGpvMW_SnHNQxBNvnw,11776
+setuptools/gui-64.exe,sha256=NHG2FA6txkEid9u-_j_vjDRaDxpZd2CGuAo2GMOoPjs,14336
+setuptools/gui-arm64.exe,sha256=5pT0dDQFyLWSb_RX22_n8aEt7HwWqcOGR4TT9OB64Jc,13824
+setuptools/gui.exe,sha256=hdrh6V13hF8stZvKw9Sv50u-TJGpvMW_SnHNQxBNvnw,11776
+setuptools/installer.py,sha256=2U2xHSbylwS8lxyy6PvLpSJGmKaiDzN3SjTB-e0oL4I,4969
+setuptools/launch.py,sha256=TyPT-Ic1T2EnYvGO26gfNRP4ysBlrhpbRjQxWsiO414,812
+setuptools/logging.py,sha256=JA7DVtLlC3gskysgtORtm9-4UWh9kWr9FjbXbdQsIRo,1239
+setuptools/modified.py,sha256=lDbr7ds0ZpxYN8i8b4DmwuGJhDED5QmvQKEd49gZbAY,190
+setuptools/monkey.py,sha256=WR35s98H6ICfmJidgJWhyuIRY1MLnC6iYrLK2mQV6i8,4303
+setuptools/msvc.py,sha256=5OeUPPglRPWcCQZvJ66cLOVuweWt6FA9N83B5Izb2l0,47476
+setuptools/namespaces.py,sha256=hlrf8REOKRK9XttpNbfovk4WwGQLB7KEOtfvEeHC2Ys,3131
+setuptools/package_index.py,sha256=bXbq-oLIXDkkT9Zm7bxVcxAwmtB92sr1e9gy33-uNaY,38376
+setuptools/sandbox.py,sha256=1emYZJ-S-l5mOjjbabtp3HFHyxqA7mRCNfY5h-nmQoA,14699
+setuptools/script (dev).tmpl,sha256=RUzQzCQUaXtwdLtYHWYbIQmOaES5Brqq1FvUA_tu-5I,218
+setuptools/script.tmpl,sha256=WGTt5piezO27c-Dbx6l5Q4T3Ff20A5z7872hv3aAhYY,138
+setuptools/unicode_utils.py,sha256=FPrnanyVZhnCs_iP45Qxig7hJZJS7m5Yr4reo24-XLQ,962
+setuptools/version.py,sha256=WJCeUuyq74Aok2TeK9-OexZOu8XrlQy7-y0BEuWNovQ,161
+setuptools/warnings.py,sha256=e-R_k8T3HYIC2DScA4nzjcwigsXF8rn2lCsp3KUrYAo,3697
+setuptools/wheel.py,sha256=8SPjSexgMQ-FgxMtphFLdW9EoVMb-tH-JzC9XMcJ4wk,8628
+setuptools/windows_support.py,sha256=0qLEFTYEBly5quTV1EciqKwrvS4ZtU4oHWP0Z3-BOYI,720
diff --git a/venv/Lib/site-packages/setuptools-69.5.1.dist-info/REQUESTED b/venv/Lib/site-packages/setuptools-69.5.1.dist-info/REQUESTED
new file mode 100644
index 0000000..e69de29
diff --git a/venv/Lib/site-packages/setuptools-69.5.1.dist-info/WHEEL b/venv/Lib/site-packages/setuptools-69.5.1.dist-info/WHEEL
new file mode 100644
index 0000000..bab98d6
--- /dev/null
+++ b/venv/Lib/site-packages/setuptools-69.5.1.dist-info/WHEEL
@@ -0,0 +1,5 @@
+Wheel-Version: 1.0
+Generator: bdist_wheel (0.43.0)
+Root-Is-Purelib: true
+Tag: py3-none-any
+
diff --git a/venv/Lib/site-packages/setuptools-69.5.1.dist-info/entry_points.txt b/venv/Lib/site-packages/setuptools-69.5.1.dist-info/entry_points.txt
new file mode 100644
index 0000000..b429cbd
--- /dev/null
+++ b/venv/Lib/site-packages/setuptools-69.5.1.dist-info/entry_points.txt
@@ -0,0 +1,56 @@
+[distutils.commands]
+alias = setuptools.command.alias:alias
+bdist_egg = setuptools.command.bdist_egg:bdist_egg
+bdist_rpm = setuptools.command.bdist_rpm:bdist_rpm
+build = setuptools.command.build:build
+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
+editable_wheel = setuptools.command.editable_wheel:editable_wheel
+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]
+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:invalid_unless_false
+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
+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]
+keywords = setuptools.dist:Distribution._finalize_setup_keywords
+parent_finalize = setuptools.dist:_Distribution.finalize_options
diff --git a/venv/Lib/site-packages/setuptools-69.5.1.dist-info/top_level.txt b/venv/Lib/site-packages/setuptools-69.5.1.dist-info/top_level.txt
new file mode 100644
index 0000000..b5ac107
--- /dev/null
+++ b/venv/Lib/site-packages/setuptools-69.5.1.dist-info/top_level.txt
@@ -0,0 +1,3 @@
+_distutils_hack
+pkg_resources
+setuptools
diff --git a/venv/Lib/site-packages/setuptools/__init__.py b/venv/Lib/site-packages/setuptools/__init__.py
new file mode 100644
index 0000000..7c88c7e
--- /dev/null
+++ b/venv/Lib/site-packages/setuptools/__init__.py
@@ -0,0 +1,271 @@
+"""Extensions to the 'distutils' for large or complex distributions"""
+
+import functools
+import os
+import re
+from typing import TYPE_CHECKING
+
+import _distutils_hack.override  # noqa: F401
+import distutils.core
+from distutils.errors import DistutilsOptionError
+from distutils.util import convert_path as _convert_path
+
+from . import logging, monkey
+from . import version as _version_module
+from .depends import Require
+from .discovery import PackageFinder, PEP420PackageFinder
+from .dist import Distribution
+from .extension import Extension
+from .warnings import SetuptoolsDeprecationWarning
+
+__all__ = [
+    'setup',
+    'Distribution',
+    'Command',
+    'Extension',
+    'Require',
+    'SetuptoolsDeprecationWarning',
+    'find_packages',
+    'find_namespace_packages',
+]
+
+__version__ = _version_module.__version__
+
+bootstrap_install_from = None
+
+
+find_packages = PackageFinder.find
+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`.
+    class MinimalDistribution(distutils.core.Distribution):
+        """
+        A minimal version of a distribution for supporting the
+        fetch_build_eggs interface.
+        """
+
+        def __init__(self, attrs):
+            _incl = 'dependency_links', 'setup_requires'
+            filtered = {k: attrs[k] for k in set(_incl) & set(attrs)}
+            super().__init__(filtered)
+            # Prevent accidentally triggering discovery with incomplete set of attrs
+            self.set_defaults._disable()
+
+        def _get_project_config_files(self, filenames=None):
+            """Ignore ``pyproject.toml``, they are not related to setup_requires"""
+            try:
+                cfg, toml = super()._split_standard_project_metadata(filenames)
+                return cfg, ()
+            except Exception:
+                return filenames, ()
+
+        def finalize_options(self):
+            """
+            Disable finalize_options to avoid building the working set.
+            Ref #2158.
+            """
+
+    dist = MinimalDistribution(attrs)
+
+    # Honor setup.cfg's options.
+    dist.parse_config_files(ignore_option_errors=True)
+    if dist.setup_requires:
+        _fetch_build_eggs(dist)
+
+
+def _fetch_build_eggs(dist):
+    try:
+        dist.fetch_build_eggs(dist.setup_requires)
+    except Exception as ex:
+        msg = """
+        It is possible a package already installed in your system
+        contains an version that is invalid according to PEP 440.
+        You can try `pip install --use-pep517` as a workaround for this problem,
+        or rely on a new virtual environment.
+
+        If the problem refers to a package that is not installed yet,
+        please contact that package's maintainers or distributors.
+        """
+        if "InvalidVersion" in ex.__class__.__name__:
+            if hasattr(ex, "add_note"):
+                ex.add_note(msg)  # PEP 678
+            else:
+                dist.announce(f"\n{msg}\n")
+        raise
+
+
+def setup(**attrs):
+    # Make sure we have any requirements needed to interpret 'attrs'.
+    logging.configure()
+    _install_setup_requires(attrs)
+    return distutils.core.setup(**attrs)
+
+
+setup.__doc__ = distutils.core.setup.__doc__
+
+if TYPE_CHECKING:
+    # Work around a mypy issue where type[T] can't be used as a base: https://github.com/python/mypy/issues/10962
+    _Command = distutils.core.Command
+else:
+    _Command = monkey.get_unpatched(distutils.core.Command)
+
+
+class Command(_Command):
+    """
+    Setuptools internal actions are organized using a *command design pattern*.
+    This means that each action (or group of closely related actions) executed during
+    the build should be implemented as a ``Command`` subclass.
+
+    These commands are abstractions and do not necessarily correspond to a command that
+    can (or should) be executed via a terminal, in a CLI fashion (although historically
+    they would).
+
+    When creating a new command from scratch, custom defined classes **SHOULD** inherit
+    from ``setuptools.Command`` and implement a few mandatory methods.
+    Between these mandatory methods, are listed:
+
+    .. method:: initialize_options(self)
+
+        Set or (reset) all options/attributes/caches used by the command
+        to their default values. Note that these values may be overwritten during
+        the build.
+
+    .. method:: finalize_options(self)
+
+        Set final values for all options/attributes used by the command.
+        Most of the time, each option/attribute/cache should only be set if it does not
+        have any value yet (e.g. ``if self.attr is None: self.attr = val``).
+
+    .. method:: run(self)
+
+        Execute the actions intended by the command.
+        (Side effects **SHOULD** only take place when ``run`` is executed,
+        for example, creating new files or writing to the terminal output).
+
+    A useful analogy for command classes is to think of them as subroutines with local
+    variables called "options".  The options are "declared" in ``initialize_options()``
+    and "defined" (given their final values, aka "finalized") in ``finalize_options()``,
+    both of which must be defined by every command class. The "body" of the subroutine,
+    (where it does all the work) is the ``run()`` method.
+    Between ``initialize_options()`` and ``finalize_options()``, ``setuptools`` may set
+    the values for options/attributes based on user's input (or circumstance),
+    which means that the implementation should be careful to not overwrite values in
+    ``finalize_options`` unless necessary.
+
+    Please note that other commands (or other parts of setuptools) may also overwrite
+    the values of the command's options/attributes multiple times during the build
+    process.
+    Therefore it is important to consistently implement ``initialize_options()`` and
+    ``finalize_options()``. For example, all derived attributes (or attributes that
+    depend on the value of other attributes) **SHOULD** be recomputed in
+    ``finalize_options``.
+
+    When overwriting existing commands, custom defined classes **MUST** abide by the
+    same APIs implemented by the original class. They also **SHOULD** inherit from the
+    original class.
+    """
+
+    command_consumes_arguments = False
+    distribution: Distribution  # override distutils.dist.Distribution with setuptools.dist.Distribution
+
+    def __init__(self, dist: Distribution, **kw):
+        """
+        Construct the command for dist, updating
+        vars(self) with any keyword parameters.
+        """
+        super().__init__(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, str):
+            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"].
+
+        ..
+           TODO: This method seems to be similar to the one in ``distutils.cmd``
+           Probably it is just here for backward compatibility with old Python versions?
+
+        :meta private:
+        """
+        val = getattr(self, option)
+        if val is None:
+            return
+        elif isinstance(val, str):
+            setattr(self, option, re.split(r',\s*|\s+', val))
+        else:
+            if isinstance(val, list):
+                ok = all(isinstance(v, str) 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)
+
+
+@functools.wraps(_convert_path)
+def convert_path(pathname):
+    SetuptoolsDeprecationWarning.emit(
+        "Access to implementation detail",
+        """
+        The function `convert_path` is not provided by setuptools itself,
+        and therefore not part of the public API.
+
+        Its direct usage by 3rd-party packages is considered improper and the function
+        may be removed in the future.
+        """,
+        due_date=(2023, 12, 13),  # initial deprecation 2022-03-25, see #3201
+    )
+    return _convert_path(pathname)
+
+
+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/site-packages/setuptools/__pycache__/__init__.cpython-312.pyc b/venv/Lib/site-packages/setuptools/__pycache__/__init__.cpython-312.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..25b2a3f00ddb4eb9a6b8de0d039ae72dfb4dfc37
GIT binary patch
literal 12726
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

literal 0
HcmV?d00001

diff --git a/venv/Lib/site-packages/setuptools/__pycache__/_entry_points.cpython-312.pyc b/venv/Lib/site-packages/setuptools/__pycache__/_entry_points.cpython-312.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..1be30d33099220956593e910139808f95a418a8d
GIT binary patch
literal 4685
zcmaJ^U2Gf25#GH!QWSqAS)we`>5`=Qa#0)+enU%bm#QC6R$gxnw|F=>_zxlcMo
z<-0PZn2M=p)tok@Q3AS__2v9SeyRJi4Y|NjK7VW-C9vU$37kXs{JmXiYt28Vg4UyBC^
zv&;b1bpzvkDQ}D6Q4Hd!HMbVEIV|Wjfk|(
zhA*-FrQzq(Bg6Q1$Xz!R!-Clp1>3T+qBkj*my}x;upcK}LB%B_n>P6-_cs`s_?1TZ-&Vf0KIT&f#7cxOU_(#X4@
z(OuA3T!$zjECI06wXQ)Nl*2hV9&pKw)VOX49R?>59fJrs7pH6Vl`V
zqh-L#8&D0^dJ89*9OYInX#<*Kn&Xoza(rXlnkYCvdH5X-jRJQL7}7a<-U((c!%WI<
zd|wCT>TXu1;JXn>&BCZ~pX$&%XQY0xNGnSPC61p8hJ-
zwjAm%g}RqR`%9tyHyyJ?%a)wKfSmZ
z+PUcOWL!yLrul!
zq}$ec4B;s~_G?hUV<6pYqljjz^zZe8)Jd-(DyqJ$S-hlox|)c+!#GRr>S47*-iS=t
z(`wI|YQpu#$_P9qBj6q6(`n(7FmQFeo(m6Rjxi@vtnP>k*Xs{C;d}7W)#D(3K&S-z
zD{X`f1FUB$eyDX^pp*nduMP)goz*T@w4oqCya9fkwK@knA4h#!{7=biBGWdV7B
z*BVcZu{@hB@V@B|Jj?MDSu15^MPIyXIEI6C5dS9M7Rf!L9Ps~b&yj@}ZzkRj&K;fK
zvmDUxs?hi!$qjFGZlkdVrx8*Fmr23#rN)hP-ib(p!Da(8r{e=HCnRVj9q|tM0-R99
zm*ba!6#}>CQH_SndxSqV0T#oE7MfTst#oa!7?2j+~{-r;>d>>b}
zN|Yvl@yvZc2}j@QUykl8Mfcs@T#g4Gg@8P1Pd*2xir_r&T3
z#H9meL(c;vdg{9&9k{(rPD96GzGoqaeQh<6zdOteV*=ZQr3Ev!}PWZiey
z0w=OYZp1YD_!H3gdAJ1*Dw2h#isa#$(I#j91t|4_-iUB&^)^sOm3%hJ{xw3GAv5%2
z#XF<5(+c&5nyHcve7tg*8W5QBFi8O%d;{(Q-UD4Z=Aw11fsn;Rv3dqXl{Mh7d#qu?
zN}#@>_3ZIcg-K-HpHee7H83aPz29xn93jE*zxI4^Q-ef!}k8Vum2hoTrl^+4I
zj#xPULw{XfTmP#5qu^i0H-~GDA1|;{p@_Mm9qJd*lPPf{sT39aN%2nO1?4u%dbd
zJ|Oc7=n@aIs;VPy7YW_<(AfYFKZ0ol#;Aga#3zr=D<3wz+b}n^pe{W9oBm(+FC1TL
zkC&o}3KI78Q1~Q9mp_T(0T$vXuo1;YxWzUQSieUX#JOlN0I#&Qd
zX44~X^$iVPhLrV{(_R^$v*0hfX2bHtt->&w%PLH!O3B1dlYsxVEHl#r{!&xpGCev0
z&c>h+Vcb!n9FbfM#rqT-8N{~hn8#7W($y^k0w;1%o@7}cAA~kUr1%uXib^T{lC*qD
znnC`Hbl)Kd?vVH$qTeBV?vVZW{9)R2uPZ>0+}lj)j+KCqHqY%_ZrxjI-MdQQWu=8E
zp=H`xqMdh@2Kwl#N|dcDsI5ea;(v3;>pQNBGHtu71nD3}Yjj1bD_aTmy%~8ua?PB3
r?5)hZ2bRd*6+cY2qG@KzJzDMv4$5jlgO_ZLYa%cYo*BSRU

literal 0
HcmV?d00001

diff --git a/venv/Lib/site-packages/setuptools/__pycache__/_imp.cpython-312.pyc b/venv/Lib/site-packages/setuptools/__pycache__/_imp.cpython-312.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..7d64548365ca301a2b6b3d1b7976fe065eff48cd
GIT binary patch
literal 3583
zcmbsrO-vhSc4j<`#~%O07;r+^W=KfD5TF~hO|?mOr5KaaO#ua1B^K(e@eE;%8C&0c
zP6ODLchv)&AXTKWQl-ryhg7-jv3u&Z(q23`lIhfkteQP=OP8K<*?r#(V;f4E)$Rko
z@8`YmeedU+_uk+AelG=p{xP`lM-N4PPZka^2b29dm^`9nDnZG#%w*^UZH-LAWsR&g
zx-!giOQMCQWH!OcEeW^GC3xAL@W_0^D|
zl0!-=i4|D{Iih`N$h$|3n26Hm344beUzYTolG>wzvPX>Ea}mT5I=rP#0MW_8%G@Z4xNl_w$9=VDn|
z)$YzssVSUC%HULfbv2_5exl6lDpuz1DcZfco9g@=Sq7U`jMl29b3v5GtY&yKe{MSYn4B3@TaT%1%tm(;rpumr5*m#
zOMl1a#~UA4{pSt;`B!eLMfkx>`C7LwY@e?PVwn~9LD=E&x4?!Keg*=2DEv(hE;0qC
z6u^Yf0vX)D3gLft@L2vt7npSYVK`i%{Ck+ZHwy}k>~aEz`Z4e99~a`Z-=Q`}nT0z7
zjkPm#WEL5@r3l|^k;Ng0cKiw4>d@zIfm<9L0G96swvO7}Rk#+dGH%$-?*bKaNiIR%?VcfsBCZjSRZMC&NrqYxu|HCh&m
z{Qr2~jr$#`MVA(TtCWilcVh%C+<3@YgOsyxmGZ8`-55a%hc7wnqO0I)Xx5Ov$N*m$
z|H|Pja0T9pXpz-8_(tk4tiS@*UEm9@1^An$qQ2G3ALex|X4EA`>?IkgsYLZfdoPOf
zc`SaS;zjWwhlzSVN0RKsYkw@M^%%qXYp!w)&DRnYMm~?%1{N~K~gvn%elO?so$zvi*H*2B#-yZ^h!$
z^z7K!`0e<#CClD3L)Y)j#AB1AO;iDHicNkrHGVTbYPQC2&%`ID$0sKm$Ay?w-!<86
zPSK(sMDiL6TZY*MZ3!xu$u6iFFcRt_R#S+)O<3d?7S#JD3*k|8QnR73*?`&!gU}Ro
zbOm;P0YCk(knT&=OQCJ+_uI1-;aq8S*DaI^wZN(E;iqFp=cP(uq|A@(3c;$-wIg&r
zi9Bs95C86Y_sv@HMNJ&NAP*&VAnI9A4ewyN^G7!|1v3-1osx4?k$z_4uov&K*xD2;E%USX*anq41-~
zgGe>hZG^fX_f$f?PiZ66zs~J;bUpD@#SudssfsZJ{ySnD`ZsOFu+YOh)%HH4y|3E-
zz5)Nj`?YY_#^|m;{BWfj={F+%)yP!?{{2^L;dhP@t{IVQhW{E6)<<6m!H45ttd+w9
zhA>bIgPXSHO8DHy=!<5fx7vVjt8JTm%s!UOk%6)>P-X{E2So1phA#({pR4$KbH%6s
zyNaK#*Lx6t0EK#=0F4?mfk@(xvUZzu2IlA-Ad@GpZwlUH)YW*%h$Jzm^Rl02=qD{Q
z`YkYhL>S)#RHAB45A<6hc+B(Mys~knDx5KdGmp87&|7AEZ8y~$9FRkDkJT7W!Q_o@
zf)xDoZo;I&vY_B{%Gor9uE7=1~TL^|?l&~!_ZoJ~m*8i2JKfTVBt%tNFhwz8=iZfw^3
zXhd{I!vy`3pb>&dr9`9*+qRM1Va3N%^-IdWm*kk&{8ihC-=G_?B%P%G0iahdnx=nX
zTr|7yrD*SWRQq>S=Rc`|R~+RJlsvnEU`eo%&ksw2{>5>0_kQajeR2N}G)wpG2Uz;7
Hb<=+UNdE>U

literal 0
HcmV?d00001

diff --git a/venv/Lib/site-packages/setuptools/__pycache__/_importlib.cpython-312.pyc b/venv/Lib/site-packages/setuptools/__pycache__/_importlib.cpython-312.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..5b786c049e29620191599a19f54d2f250294d9fe
GIT binary patch
literal 1807
zcmah~O>7%g5T3WYw%0##nuIhY3hgFoKo+%KsKif&Xq%>pNDxJ$s5Elr*xu)Oll89k
z-rJ;(9I2`}fK;h1m-K?f6@@z&ErT%io0ne(?_NA-6VrU*Ki~bkK`49mKD50TE5?B$i!ah>C8uGONf)ny_B4`;#oXEUNpicaI(W)ApQjr}Ga<TOVN_MqY
z5Ju6YKEkxY1v83{Fl>uwV!rI6&6W$yb}U^h+f17{`PzxsGrop-kDT4{5}G1z-$zt9
zHDTK(zgS?FVJ)t`IMZ=pn1-l({?S!6v-Z6Y;05iCg-*!ncE}6b3S}I_&oU^uO()21Va)ijOP-`XkflbRX1ctl1CIWOEp;|Jv2i8TTG>O*uOWUml$rQtnJf
zr(TfnoH&MaO4{-Iz>fC|ND)6nDTxhvqNp!&2Re*lwz%!E66L8F^E#+cj+%xjd-)OV`3?wIslHjYQH
zQSd38nld5eA;fmWV`QTFJ^>}NEe~wT12-4%$uB)t&OB8nzpC6+X1J>@-;+mw
NR;GSaUS-&;;cwn>*}VV&

literal 0
HcmV?d00001

diff --git a/venv/Lib/site-packages/setuptools/__pycache__/_itertools.cpython-312.pyc b/venv/Lib/site-packages/setuptools/__pycache__/_itertools.cpython-312.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..c5c8c45c40a52830d1b5ae5c65da0db1c0f3d51e
GIT binary patch
literal 1092
zcmZuw&rcIU6rR~_X$2};v>w1X#6#IgYfSVYF--_HF@l7|pTQC`-5tovcDKTPRH
zLpVSnk%N(g@#IBAOgQ>Kcp>ou8$B2=dK1e*Ir(OxAkml1n|brzH}k!DZ$2avtq6pF
z$xOah5c(>OrhpwvqZ_6*WFP}usD#HbMuxJ0Cb6L|;4!5d(W2jFpoW8(?%2XBF&&4<
zeI?f_j7AHnHRPka5IXW!#^YHby=0FK_80C8#zm+Lc}Is+Y*!Qp9mBMz3Ztg(dYolO
zy;{v;nHg3TrppQyW>*TsX0afxOquG_bc%_BV6Ino9mf)vC&0nOWwu=1i{G$lsc6s~
zkHfW57($Y&qc=Tu)PNPzn^{Xk`Yys~JO?#|FtB$IGh2r}DRYXiJoy{7zm4WpALY@w
z1`!bj`Ph%FA7JOAe$-bghLMR6KUn++3N^M5XV?e2Xv}t4HM_%AHc>R@25mZ&*
zsWVuM&~tETD*<`PX_-))$b@OpV#IX_r>0b~JDv?-W@KB#6LYy7u}tCiGAJr%6Z@FnG%f0BO$$RFr)j#6q>EDN>G4C@K_C1F
zNt@7noa(FyB_q8hN4SJD9V$+AYFWgh!qxi2)3VuYI3K0ZPx4;bGIi<#X^Y7|Awc7J
z0`f&_$$tZDiDPjini{CkE`dr7BWOLkL~9A+&6f<+X;uv)f-yUYOFbcjV+VH!csgt%
zc_Se_2IUMA$6kz|fxWQV9>L-p`c+4}Db(Jve660}>gaqnyEI$Bwbj}66Gfw`#n^Ux
z$7*bOcoA>4wXa5&u765)Z(g{vk>2Qfe`oXj?T^W!&Gw<~_^H*?D{WgR&n_o++5q=E
zBr!kGG7!fyD{}M+vTPR0YG+Fh6!GvsaQP=gYFu*TC!~_>;g^&g!?Re9r@)j=E0Umo
Us2JnV=;}@!_4MtcBu<5xKOcP}H~;_u

literal 0
HcmV?d00001

diff --git a/venv/Lib/site-packages/setuptools/__pycache__/_normalization.cpython-312.pyc b/venv/Lib/site-packages/setuptools/__pycache__/_normalization.cpython-312.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..c160b6faaee8dcde1ab74aa14da23ac4323ea50f
GIT binary patch
literal 6380
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

literal 0
HcmV?d00001

diff --git a/venv/Lib/site-packages/setuptools/__pycache__/_path.cpython-312.pyc b/venv/Lib/site-packages/setuptools/__pycache__/_path.cpython-312.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..de00586efe5ac283f33f7273d7ff619269f766ff
GIT binary patch
literal 2089
zcma(SOH3O_bk@7J7i?Toh?)qhjwmVK3fm2>l&B)4;u8*u5GqL{S(ae!9mD#wE%B6=Kd+!B@s#Kd)O4VMtp^{TheKYt&5iLAv-)H8%H*el&
z_UE>?Rs_KQh-JUi5c*RHbqLg${U3q3hb&|%1(ZCXN>TyYSMU|pq^cmIg&kmTbV%?`p|bt3}HG
ztY)=>9I7ZrXjdFGVJM#emSa=L@a?Lwu-gJCqlmp4z}1o1*)IXQhuqpSLOEZ9Z7?~1
zgKaQN=uyqzmx|l)oU5m>p@-0tVkwV(k7|AuR8b9W@#2`n%Zy-m&U670TrwGPTx{8l
zWL(NBm}c=z$#mytF!{pfF24zv(j_}h^Am>V`6%}^q2UF=$}x+?(~D-FfWQqO6IW)U
z)?A#7@#~q9k5jieVO-+WD9x0Ma3oKSP|J2^Qzas6cvn#W!
zbcL>4+mUyd$6siHudd&@z82i*{Z;GR2QB3JN*Cc9@B$3ZU{2<6MH+Sc05qX)jm$Ue
zacE7c*C-!FXbKsM`7IcX+Sx3m0cRO4V#?z(d=B}`+698sBx9C2!8R0w5{@0}Vn#kM
zLt&su?i|N1Jt#@M6$4B{&I%RGT?R?SX_FHRM;(eYvje3X*nbPqGJ1tkxN~jz
z>%j+?H-^6-tcLowv_2LQM!G1e7>$1oe-O6h@OMKuoHYEFBASQ2E~IRYnGi0L{4Cs>Ho9)YFCkkp5zPGMktivtr-7_(q65^Q3T%>+z0
z7AeJ9$_{sVsmw~06CU>hnab>f?HIbJ@(TC#Qo(dVxyZyXLq1Gi&`fht?t{W-U}`*_
z5wjrHSO=&VdMvi
z%fEzrf0|n!eGzICYdfK?YN+eM@Wb)@<2&8`)$abspFX+0d3z^zr5d~P)Y^`XY?W7t-E)nsQB9gEc`nK71vqjP4&QQq!ma

literal 0
HcmV?d00001

diff --git a/venv/Lib/site-packages/setuptools/__pycache__/_reqs.cpython-312.pyc b/venv/Lib/site-packages/setuptools/__pycache__/_reqs.cpython-312.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..5fb072ca8fbf66c29fdfaa41c4c492f64b30bb6a
GIT binary patch
literal 1865
zcmZuyO>7%Q6rNqL*ZpBrPNP`OSN8zBli^
zZ|vVvsW^f~o@UEmNC^GKHmwzHD#zyqgdU<B~`M+YS@-l
z*^a0YJE}&xKV-%1xGoCFPH>r2liV&@DLbvE1tcOY8{u6*XG`tC5!{I--1!RZ$|93$
zm&l@oT^0Zfr`yrpID|Xr1)Od~;vPf33K2#GcNtL}ZlXv=Y)w*oakOnub9BjwuSqRb
zwXf+j2r$IC|YRD5??Fj8xR+E)!_(*LllWrQGRkxEh5^t(7C2hnlOva~r1
z2hj;1lM|Znk{~iu_3&pJ3Bt3E={iBwUBbk2HRA^0B&PAbib=4I9X}9@GeOML2*pK$
z?*$U|2@Pa|{R(mPX6smf#KskZ&+*qF9wNW(TM)tU!t%o{gWpong4|Zy$_bPf9ojyG
z?xWT?cAGg```gfum2cw;tUD&Q3t@;1H6MDRn2u6n
zRhDvb4$#;rc7^)N99FdE`4I(g7`l2?EX@SBZ
zJ>PXL`Zk*{&3V-z)fjiPP2?=3?PB?}AXd?{BuaK3BnE1Ufk$sVG7bhuYlEXd^&aFW
zYWay@FV*um>ldf$iCg>9tz$TVPLYHeI7#5K14*iAN4T+rNFVGP<&iU>24Q{!@*3#p
znTxSy6&+s5lXJ{1H{AM*#5#pc*h%s#h#islk6ZwqvNMXp({Ow)UT2Goc~>fVi}TQN>Q;!3q3x{~)?z6wm4HB+
zuT&_e3WW2C!GKG^OUCPrNV*{}9}npV?f*-=$ULm6Fu5p7cC0Ub_h4(Les-vy7~YqL
zd99fOnHX=0sR$2EwL4I}AfXD+{drd?a^#51LY{WXvTI}-vf2Jzx;pWGzu*Nh~YwWCdHBi32q8HyWvAlrPs7Re
znQy*a6%SM08}DpJw|c%$?f1M{OAW5Zk7P7_Whb*cyth~%8e5I6zqfIF?kP<)52!_2(j<2<-O1k501Hh)Q!aWAABP%dKxXbq
s=APWTwykf^?u~8Vsa+a{{o`vtPycxR37Tw1PeSy$foDh(MtGV30s$b;3IG5A

literal 0
HcmV?d00001

diff --git a/venv/Lib/site-packages/setuptools/__pycache__/archive_util.cpython-312.pyc b/venv/Lib/site-packages/setuptools/__pycache__/archive_util.cpython-312.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..45044393ac9b75688c738361438485b1e16a7d80
GIT binary patch
literal 9212
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

literal 0
HcmV?d00001

diff --git a/venv/Lib/site-packages/setuptools/__pycache__/dep_util.cpython-312.pyc b/venv/Lib/site-packages/setuptools/__pycache__/dep_util.cpython-312.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..4cb28a16e6a7e7c0b41f763189c3bcebb1399949
GIT binary patch
literal 883
zcmZuvPiqrV5PzH9WVekityND&*47>ZCJ_;#K`Ppcmr^QKgtD;R?3<)d_aE<1LqiWD
zeglQ}D@gq&URruEEbT$O*qac&^yF-sZ514N^Jd=r&CL7Fyf0&8qXdP2tF}HA2>G5l
zxu8Fg{tRxrgc7O+B-GY5Ewc+jA=KA(wDtF7-PkrN#ZwJ|6)hcc!@rXrjKa0qvgoci*&r$7?9jv#d@v^+Y?2!GT{Z5k^av-mb+`8Op
zf$G$t#m}Yv;jKobncqt*w=m<79unw@fhzg0q3XPXA}}w
zNfy{l*f}SlHT$IiyQLyOBPi?^k3(Brkgw%Eu~{S}c(m4ivKoMgF$&kle~=qF)Q(&K
z0P+WH)Vg;zrA$PlUT-tGshTxE4(rKw;?*zn)e{v2_4{`p%vW^w1G{iqMEzgK(zx+!6Z0a5N$cjtm$N4HSzp6UK@n-
z7lWK|E!j@Xt~-2!>+(sgo3Rm(P`xERvwSesI5Zo_cyWteIoO{%Xt}8go7C
zdCywzO)T^#Z~h)Ji=|%^YRU9F=)h~
s=qJLjBAid4Ic{06#NFGP0!Q`a=@gxoqI_xh!>c6$)<2WX7>w*UYD

literal 0
HcmV?d00001

diff --git a/venv/Lib/site-packages/setuptools/__pycache__/depends.cpython-312.pyc b/venv/Lib/site-packages/setuptools/__pycache__/depends.cpython-312.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..9a6390c1a8e4d0ea96fad12e00f6765def433274
GIT binary patch
literal 7469
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

literal 0
HcmV?d00001

diff --git a/venv/Lib/site-packages/setuptools/__pycache__/discovery.cpython-312.pyc b/venv/Lib/site-packages/setuptools/__pycache__/discovery.cpython-312.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..77078b0bf556271e50ba28cc12eb59ad898f5f61
GIT binary patch
literal 29059
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
Qxi$2c8)R

literal 0
HcmV?d00001

diff --git a/venv/Lib/site-packages/setuptools/__pycache__/dist.cpython-312.pyc b/venv/Lib/site-packages/setuptools/__pycache__/dist.cpython-312.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..1414160a0dc40d1e87892ca7b997389d40d76c2a
GIT binary patch
literal 45469
zcmeIbd30RYc_;W@EvPEg3ZSrWg&hR~Ai+f>!4(9+MI=Frixx-<4B{0)kcCC>6-1&`
zP-sP+fHq@+a$wHq
z8943=CvZc2FDLMVA;|ag?60BM!2TM0jqIaAk;*}>|On%)}R+XY9k
zcBHPiZlu1qp8a+P8%7#?8`-@p=pSk7Z5mnAyN3OC2b)J)dRsKF03zgPTXT^loAI1;MQ&?Y-?I+j_UL
z--W^LBRhI`u=}Fm&XHZcyV!kkaQ8?@Z^y`<-aR}Q=nQlX8hX2hl2^Ijy+SF{eL@-1
z{X#j?140GT$AwCy2L&I}LqZkO!$LLE9-#*55uq08QK1g$6GA=GV?qPc;{lgg7$`V1
zuL6Yr_ss9>_v&5mlcQw@F0kv%mw1nt%l1BL;P!Dsr9
zpPDrI*CZTW!%|2bJ~a^<9vl5TD$wKS6IN+rd|V7j67J2zL&33AxG^2=51sZ~63!FP
z9PRGw+~3`K@IcSLgsE$IAe1m2LOluB;fYZHsbJu6|M>Xu=upCXWSr{l4V{8Z%BBld9)prC5OhK8x#gnMFi_=Sl;U*N@nCg6ar#EK+VYO
zJTUrVe{fhh+%KLDh>0xuFJ5?*7stl~1H*&Ec<0b=sVJYYJc&Bd2yUHyfzgmS**88m
zJQ|`gbM=jmi6f{u+)w?$6LX&!c!6G72F6B4`bP!aT1Nt*exX0qkM^t*ed)m9P{KJr
zIWCT!2@HfnV_Y6Q~ly7=4C*E33`2qa9S{Ud>dc}N_a7!R-Y
zjZ8=(->HCacw~G`WZ>y*7#R~Lf`Rt_P)L*-e6#{ahkT7gVcrXhl7EZ
zbAeORa47KHi-FM>pF1>s>N)z=GTuLMwtpxfJ;#8G0l`{oxpjOp;Q_=9obA&GL)?tU
zTkucnMRI{#F=Ux@mYw;FPG8LFo13_?@n&ej*)?rg%FDm9_4Tb$YuWVF%+}AB9r?c%
zerM-*c1G=cRyiZexT+F+AOG|2sN>juqrshh*X>y`8*Dj0UoqnC11TTXzf$k#t`~O{
z8*dqMJG{nQUNi19gX0g7Jrx`yhL8r3Ar0gP4eu#WB?fW=e}+Ih?TQ#Q@G+dl7t47k
zO#sg?1j>+0tw;e+V<<;`@(A!W0z6Gq#+Qvr;Asw516>RcPWnQp`$Ikkr@mpy*RR4}
zkN|@V`!s0n_t7KY*r1X_lEG74gVuZewnX-@G(0MW`bP%<@T{~M2qcIfX8^EbE3%1e
zDOpEJ8zlsU*G=Mjy5B$vfuOhviA1AG@FzZon?!CugWf)QUG!1y;x4=(NRb32fD)6<
zoby>0=P8-3d#Cn&^IZ9Ydu`OZHtJq`*PZj)xr^s!CcggiT>fo$ebibXbJu5vm(AP$
z!^xf1U&Kh;V0;ksvCeE21G8DJP_oZH!cln8Ii&j65nFy~0OKnIKItsyUuR3sdH)
ztSL*xEY?NL5#x)zSf#_;84U@t##wU4so$%2f+=E(nDwvF5#$+~(zHu33t58YeXE}T
zQrN^z+Wgt!#$*^IBq;$?1biyG@b!xU0?lC|+(tXFN*4GA#zsT^fHvRgm`}!>l5b4Z
z%VF#Bg}o0|=&KVS@U7urd8*@B4=9Cp{3e(e0s}#`B>4IX{*}gkTIpsV(5RAibS%{F
z15o!%0dzvdHWtDHLLI}SK8D+X(ZXv_oDS&4`?RkC8Gug=1(ZhRO)wy|hPNM;YZL?H
zLHhpTkmQ??fG~aMhC`=mp9l^Q42SeOvG$I3AN6fqzs}zp-XI1BK|o;LNo&srCeMwD
zg4B9CG!m>)ezj_LC@31b5i7B5>&3D4<%sgH)1@OvM|kS)+ewy4uDHp1GB
zJW3dYV?znkIc(r!J!U*%3XBYguv4Y3=7fDz(ncU*9F@isS*Q*zB#aXF8uP&E;h-Q9
z%A@5@m%QX{^0t5iVh>(+pdf%v-ZBxnCDvIj84~zm8+<__A6I^Wxg|B
zzAk1hna-Lq&Yb+%o-;EzcQ9747Td{dhc6ypbX3F~6)OflXWLy*-jb)_Gn?7z`pm)E
z>K3j3nAN}J*sh6pVFNt^`W$3v!h+ojBn`|<
zP+&w#->?X=i93;`ZB34(qLO$L8BS5Aqey5|vVaV+Ixao4=&Xo2E8dLEpZwt2>(9RT
zeB8P1j&-}b2Sx4cS2;7ISXK-MXZ?Mn$(;=d_2jJBOpIW`ONC%*+sJ*#O9YG6p%W|`
z8G>fD4d_4P2R0z?j7C4>fpY0H%{B$h^ppvdwg!v(#=~-m>v(lhP
zg(5&9(c30958$l;oUS2adV>>;5e{^;=`Hh=IiwLt*jAL@3fwCOjhZ(1JmA~IWeN}E
z193Slj*YTO9$}nPyDx0;H8NtvZxOv{5KNFfHF5^A5YHGwV{jl=P!=;#kreP}CCu{F
zn}{b8{gm?%z0Z=%CX6G}kVNCk5R{Ce#N(8Wl2QDVhLK$0mWs-+xv#qCjq#%9m?{54
z*L2PFiz^0`xoz2+KQp*sE&t5QnQfQyUn{*>I&&g!E4gDT{rocv=O|v~4Cb~aPxJhf
z^ZRe?xiN8L_~r{ySI4vwWb9l`+*Lo{6}7Gd0c&o1AkjQrv3Ky?ExxkDXB+S)PL8s-2L0_Tyx-Up
zcF>p(4?*rgB#!(`mTk0RSPF=qdsq-4?nfusXk9m^Q4$jM80$|a54^*2T@X41Ar|Tz
zq-hh+Aj_}gpG1rXL7!*YTCr%YiCJsrLUV)jhoZGx7p(1f0g~C}bKbeExnQ(nLp*z9
z)U@&8VbV7371Wl>`RU-7Y17tuI~DMNM0FHr&?#43bw&%|T-W1;HoPBn|4g$6`TDBveyEE$8
zxnkrTO`m^CGLERdny7_i{ECTp9^vuE=ESz*u6`H?{GdB(ed6aUcFO%gqAl>1T^$DQ
zmciYz$#^TLvSXd`);crpQ*f~c_|qtm3ivu`0|yuZkQ(Ko@A88xK=J@c9qdh+=vM}i
z58-JBkj6CX(-dw^21pHWt5B#?fEv{o-uq}EdkCT)DlKWyBL*=HJ$|!Ddzx5G3E^Rx
zm>|#)$C0E1fm;Rwtv8~COm9-ggGdMhvM?#jR`1NoJJwQAf@N=xM(r)DoC%NbI-ZiJ
zHS3F})kaw#NV%x*6}yZ3hP#6|zE!#1_@`#vq|B%ZSs2DA44FEknPsXv;+o{sC^+I6
z^jXz}3vAA?GpBG2hSa?lM7buFCVqjC2qVErM&E;tu?*dV^k}SKNZ7j14+NOBULNQS
zw0EvTdneT@o6
z1EC-uJ&Qd5kpBhBb89m7lVpnCA+>&rr>hu1$x4zp@0~8uS}wq-4ZTc>svQOx*+((WzdG7)G8CC|@~}3!IEjjxX4&@U7A6U2)g$
zsC74?lIkzm?m(jd;twlH{;*79r-!)#2&lf0hlMB%RQGehD8!8m7Gi}sj|=y6;I=XAr}e?1O1|ahC<+HN2PExgguN?X0=nD8c3=9G*c(g
zYkxS8Vb9e3Jz$&&rbXgsl8_q_iQ*(BByt5BOq1~NXA}I3>DW(Mn#zatky1%~8O6VY
zf6~7~0xFj6bte|=)c{TiSey+%YG`@;#d+b|k^5#N_+QRwBPKYf;EMgS{hLnO^Y-%>
zP1F2z+p@cOw(3m?d8(L@XXm1)JLc*BQ20Sd+|vW$EeUxH5YuFFw$j;+(W=K5t9Hby
zcH9)=Rh=I`amTvv9=62&{KJcYSP6zGFW`nqV*uq=0!SSkGa*cYq`C^QiTtG!eKR~m
zj7{2=X(I$vtknQ;+Xissh>W&BcWzDNbFE1IP2p#gC6|nxQl%iWf`v&4x2Xck35Zq3
zpnLPRwm{Sd;bRCg&Q#D!5?ewWWDudHg3fN>wAhUe4t6fg@$XqZj7&kC3X^P84l
zi;&f86)ezz+k{rZ8psaVX*U;aLe`*3$bQw_>k#aC>l7SFT>-b?e3etV;a-p6!qXhV
zjWkzSD|lWt_2wZ@PEsB(>dk$X@68wTkgGuOA}tj1krv@i0p1h~h4@t>tP_gxt5hh)
zuQI0NFGpR_@@wr?Fl~P&)AswAw!eyL`>UC@zlLf1Yniscj%oYrnYO=yY5Nl2s18=N(6h4%AWkr`IK73q<}|5=1fNP|0Yf%ulH^vpYfWyb
zfhna}ErG#7$k>Np3;>M#1wq1_0GcK)f@FZBfpb2n-AR%d^eN(Q$PkBQ9VH>&1}2to
z^f$E2-SRc0b21ISmKI;Xs`tWINf2m~I6YbJD3fuM)TSbFWZk0=RX&Qk22C3Ek3LA#
zRVZA51UYA56nX*(9fQGu&`c@=`C~{;uGNM`EBH=L`Wmpnz-&ol6Cwortx7HUj#e7l
z0K}%l!64Hjt2Js>^iM!h5B2&08Y1y3nx%%+l@FRr>jf%1FzA~cn?QBaX&NuIuT`Wj
zWBJQSG|O<#i~YkvBG~8)j!qsuz(ghI`XO+ojSh1~6^c}oNYk%X*B_*<3RuV|jGY@L
zBF`5blkEVSSsw-fE0|
z4B%qzEA!MKDKO9qV}=Ib;BXM)9RgWYruR)(Ov?i+QD11(Xy?U@K(CDfgbJI%J?oe}
zDiQ`-Vro>qYsrjXs|t4MvTz1;8TNjLRp}!%xUQCqR;P~uhKgC00Q8euY?SYu!-Y?5n0X0raZ+dB5`As0339SOl(+`$eOgvO;L6)sF?hV%}aH76J@$
zkd1@Z!GZqq{!_!j;n1XyKpV5d+C#gfpo4ylRYV&ej4RkW8Xg3w$z7x=*J@I`$gtCiJ4caa&Jv5z3J%W;#RU-F9YfHg%6QhvY`5LOL
z8+?s43xSdG(4^Kgg*BzgC#)h@p-!X}=odl$2@J&0EwXXLYL?dx+wN2_*XqNU^^Z;}
z^)P~hCSKOs4X8yimtFdo500UWSfDa)3ncAUtT$`rkyHDmt!)S%VWEcw1${bwS8}&h
zXw$l-?n-13kdzr6=@|nUuzg-`hxSB335hgmnlw$kRuvUjRqC`PN5@80B|O%_DFKSp
zfzX&Z9FVXnL3m1?#9!KxfrNYwYa5c+09hyC0~Bh0Ohk_w_c2gm9ZA{u(JCf@;5;;@
zbqDqZAzEfFNrN0Zja@oK$RRX55@=Nr_W`yQv;Bp>4BtT$4suI1F(4qoQibV+gz`aY
z%tw$V(X3-bqMWdGI6WrFE0`u1R))jl6X=05cPYC7wrO>o@Kt-0HYhd)pxOq46G8w4
z5c?QHD^M@1&(yZq762T9h9`zjGixPuY8YeA3@aFMfsz<`Y%BvZ)uo_7Wp|=1Xj}}8
zz_w8Z6HJa`X3|VsVqlb1%`{_F*BF>A-=H`)!a7D1p1ixsOL2ghmE_BaANPrOx&l5=
z?8D7(!I8z#cF>g67fj%K14T!^xuvaZo`bd$Ga41w=--HMa
zzFVOcq&|6%O*mjEgV_o65mrq&lYdDGkAe*}9Y(^HO5gMhC@egE8%HuuHcc<1j}Qg$
zq7`gD{~|A$z^vo1n4(wBBo@$RR1w(K6R-Im>Y_{idxly`yHpNv%8;aa*DN_QY2MY^
zHbGLAq@#DGRg9my{GRM&zq%8)vYPdWo?AB=lUkAlA0%i_yHdW;km(B}#y+j@5$OE;
zz*WkZ0J~j*Q^h8-tw0+m@#hRY_YA&-o@myzOe>$TD9l#adi3DFmIFO|kN6!T$vR1OO?rUN7&OVj2SutI$!!xC*=eZ<=h!$3M*ld`Zg
zXYvAMq~ArI7r1-boF{)aYc3EgS~EZSp*gm;d&!Zz>@Ht)H^kfx?;Gd4Z`5Bu9B*uo
zySFX6J7VsRxVsbjVQ2nton9>15G&XacW%7LnQTqVd8Lbajj_DOd2c+gb-D|tQ&D&2
zyy*kib=QsXLgSugU+bc8Tgfc5eKfxPiMa3BjOn+WOLhJk(}Jh^r?5_G
z`YBb%>UQBcqbbXau=J
zttHDaj2QZqg+pd0=XnVrGR#f!L-a;lv@mDLu$%;g4rItp5Wy}O#%Q3ZK1()Yfpuo)
z`iw&w(4s#24t6~vw<#sJ)<0cSdnUuO63p*ssf!vC$Oa~XBztAKB^`Z`N-yogk|ukN
zv`ZfYNEiJkE>x;!z&McFCGApMhh?fsun5-EhA`T~FaAtrT2}m2QC1iCn)$V?0ple
zZ_7|xc|>S$0PsCvgr%>^dhXP64_`@2n_a51Vge#)fnZh?CUyk{KUx<2M!(YJK1ym
zO0(hqNA(hM2AS)2AFOo<3(U!V9Vi9Utb~zAEp?)3Ubdxf*N3J_@5}P
zNr{3!`l&jz=+ARCFPMOe&&6+2@m$me3F2@#pbZ=>SxH$dzZ$km%+oK_+z(k>9H--
zT?>x#B~SkA_SxKNGd(a}a^Ho`neD1=u4!I~7j3{3=aSW-7`=2aSeur7b?@5Vwndw^
z-#iuf!CDAbXiF6}*M_eSM;o`?td3Xgpl7Aaxh0Fa4YAyYXzTuk+yhXN+S;b~EIT|i
z-mgEt=%|W0s^+TSZFsw3e&Zd-I;cx+ZP2e2m(6x9mhFy}?T#09Orz=iXi0Olb!W7A
zSIoK#4;q@^ed+C&zCE>=f8=Ut_Jz5soBW66x3~1n^v~v87NYq_rn6)DN0^GXB4(|a
z4bAl~Sl4})%{8xmZxeN_;?uG<^Ih?>Hu_O~H|5*z`f3+_n`6GsH%l)W-$t%TzDXZK6Ff}^}dHZZ>iG(W}bG!T}qw%s6ua;Xa+=+7`YGIe=s
zZeV_6+`TU9-o9$)oSqdU-cMUsIQ&`3=JHBspN{3$OMgM9z`-vsE_=FdgO_zK(oT3ZX*QuM8so6gOl~c?N&euS`~R>L!H^9GX+M
z6_PVZGE8X1gI=EsOPQ2}5x_-8qQzioyhwQ;ZL(ks!P{ih!$HQ
zrBS9(hYk1^!XTeX`W00VLEo;Ndpu8v{2C
zEfHB;`x*nSL#@8?Q=(s&PMu(4B&CK%+BO=Anr>AA)elj2|5>o%kdi>?NjjM%VFqygJge8*1Hk$FqbH5Dt*naeJ05dQMo>?SlwJ8X?Ani+_w?X=Hll
z-KB>>w+FdlbmHQGmnu7Bm7R-~2V#{6;*|#poj$=|JTkp^W*@_;SIRG!#~qc6j;5HS
zY5uVroBsXQKiYcp$y-l-_nG*HBX=A};jn-bGUJW9wTnnd29N(IZU{rcZ<`wdmI8s4
z1Y5f2z!_Crs#O6}MZsdCL+2NDvT2GN^(5B?B1y?F86w;T{5zh+XC~WJsL-qa1ttT}
zNe*se#V>FZB71P?YvEnV3FfynJ8+O?hT~Xu&`W0Zhv-mwq$fO}*8J!q81$9&+tB2jl_6$T$iglFiM*@o*A_iEgM7HWL
z8xT@#Zp43!Ea_-W%d1iqPZKPV8N~(eZ>+^+=rHu=xoa<9eR;v&`e{M&Y{NH$7xyhY
z3ui5Jc?-_E`NH|irM!~qo=@|O-YlFej2HXk`AyT=OZMEEGq>%&Wk>0v!xwY-=62jD
zykWap@L~4N$Pe10RfiWGJxh7zQ4j_0f@wuho(wAl4-8gr;AuLOB^cDbM=X8B{={W6
zZ%tywxJ(R}iia3ModNdH7OkN0dr}_)P9=;&UjMp|(tJZEC-PuVHM)
ztmEB!`|6!88xc$-@1T5OY)UfsFC*(fK%90Zvmuvu16v#54Q~!#&~Ys=>Q_{}2rn~8
zI7c;N^(#ygIXHYigJ*=UK>@b^O1cP4F8P&IIoMZpTnZ6eYEwPpQ|P)#Km{|}KcV|S
zr^JUOk(WeZD@22&GZ+aI$OkE5mVmWG;%`y`BgCz+m}l6y7Y|Y#3Wx$rUuqB0>8EL_
z5w^d;fo62%&1{SoH_dk~*xQzx*DW^hjy3O|?jlF71&5!}j=q@Hw_vSa^0zGdcgFlX
zRZ_8jsbS4xLwl^DU42q}*X~}lSKPK&EESh87B|O=n-`1QV#RIo;tepgd+ysNL|O}`mUhM7Bj669UfK8uVVBRk%!?-h83c(
zqt}eDnG|6F{CHK%zDSby#xKz`lR5@!kJ5Vx@xm4pvClARM{hL@Gzr?AvDJ7EI@uKN
zI84Na1o!o11$v7ZppXtSS;1WBB?$e&3bREfTR}ikN!+5qfV)R#pKbDL*oKLO!%+XY
zyvJ1Fr)*=+80{1ElX2T-8R|9mTjw6rZ0W?mK$);Fm2%d%9Ce+&L<@mxhGku8_1wm&
zf7{Iy@zOoFOZR_xDrzlSF80lNqYayHdgH}AZx?rcSUv4p_SL>~{QbQ510S5dem2_v
zc)aysyzWrkcbGAWcX#(J?miLQeIma5$)6exmDwL}*?yD1**4vADfC;NZyMh`dF|P&
z&(7!ke&PJ7czIjgyFTvT5OZvR{at1Dv(dZ3bB>%gR6)|3b1Wz#h
zE}tTfx664TFXS?9CAyehtxf%#xwrb33Nqao-rhMjGLF~{ie{8l`A|hG+6+YneD3t{
z091icbjfOQML{OO)zK8TA3D(4-Scec@#njn!q(0sN1uVeb5q#TbL0eG;8DlP6Z?-G
zJKhAFPai%-e2tQCAW3AwjT0gz*+YUvjtLL6E8|dgk>r~ho2v9P6#_e`JY~hx^-Z9v
zNOaK_D{H&a7%$rvcWejbHUM%-c5ug9^DvSoSY$}riyI=nU&Z`)Y0^)SsD|)I*~C21
zwR7iArsi)<{noU>Y<#wXP1f=5hUfi$t#Hy)?b(;E5bW2)IJ0+Z_EGIkX;r7c@9v@2
zwRTbjBE+I@z`y&%2_3|$AQt%fBSiS951S9mbHK>nAWAcpr35b?rqc)-65jxdoW!D^#>?@d)>R9htz>aI`Ad0)S9V<90Tv;z
z?)~Zy8m>2d(0aY~rgx!fCp<=-IV(owTyl8s(chmjEc;4Fen%nCl(2qYT%fMg=qkKy
z9Jk@}FYNLa>K{QjluaJ$Txrc`X6g{UR=k&KXuvStgS9_TiS7sWpN#U{{6Z?*jy
zbt%``Mqneu5H|`{i(mxmH70SX8iEEo=P!6NBVd&V0gw`9#V(+HE~BVs@TLqv7xu?&)lwo6Ra6kHICs7s*ZQ~Z)pD2
z*qTgdNbQ++MKq-mysS<1pGq6C5?`ped0xa9Sx|3PTBZ02B@BTPt|=?NrAEF_PLp!j
zB36vg0p!h&WD7Q$OUV?;Mz9eMrtwa+KrDCK6|qFJ)xJ*I(T1kX(KDhBZG=%bcgC;3
zQ}5F9m?QQ1m9dUF9J#1}A|);c%L&kDuW$)yf)q_=Ewe%NL6x;R=z+
zg`DqfjEKc1U22VQi!tk0zNA4o-~9p(dE&D0pBp{s~UDz|aqdrUBh3^j!aBlHr`p;qnPb
zCu{R41qu-T&agw_pj%EM225BrDIxo!5PM=*-v@_Jh2828dY$?JOh?Lt)37{%fJ!eS
z8^B05fZ4qq#7npk^YSgE1-YNBD1&tAOgIRCB`Wj0YYvHmp&Me
z$(v4j!XZx*Yf=>d3J?Kl-J~S`HQm{1jQW&r+c4{4vepSDa>&3jWqL(Ip9yRK=qQXe
z1{9+XyDY0cPbO>$(-`PfQJ`#D@;K?5NYQ?
zgb_uZLO4NmBk{Xsor+BzT}d7ym+UF8*2se7&~#eWd}KpG5JGjb4-{8E}F73F6?i
zLhj_T%tmSgwbk>5kWT~iXmGjyvruQ%B
zoJqZmH^8&7_s30vUZ7_Hv2SiLJo{~!Eb{dXJjXTf_2A$t}*zL>{1SFqq|
z{HX=y;8a#teQnp(UGdW9RnG3*IAd8V@m*`Z+B#ntFKNTC(#plswXxE*H!SheEi=};
zrSQ16ntN(~AYQT_IZ7%POE$(zHr_aX
zvmsuxC*za4mU(IJ$jtuPLg(d@nVK01GN?swUCdjz==I0E{`s1?
zw{^PvW4Cu1Q_=ZGYqX&8vT-TL8^tu_v@RD^&KbWsI%9;ttaH<{r*PIZ8~E43IcdK0
zj%VGy98~?&%^10RH0(eBY(LL=E1=Op)`F(_ws=9yjkc&~Bl*xeH$9MOfBz_}gWu;j
ze?LEG-(!aF`_20{*uMXmZNJC-{audzcJmMHX56RvC=(sXXvy7xFLJ#TcpWD1x^6{@LXAldgi6t{&Z?@Y05$3|YqgRGRkR}xO~9FCtsLV`M$hsF#X_WAHxBdn=BF`=8eLZA^x1~B}r6u!qV3V5e;@oFKGuS#+l9&c$IlqkTDGW
zUqg)J6zh>+RyZjlKnvhqG$Tp4^v|Q6AYoN2NEZqI9Cf7a@fo+Vzb91vXUGhFx;+<2
zvoIS=*g{u$U|B2g%
zSZ!X;QxUb6KP-?Tm}fZS5^jERTm#(&Ka&P36Sg=`@vBg`d}R_#fcnMU4Z#BA8R#gr
zh*gFjW&x!@0=%Yus6xC#$?qdcn3=nR_$}OnQzr#TGN)dIc?(yi&~TaHstB8~1YH%T
z7eS_M1-emZSv>Et#k|f~Ugu)ofmq&wsN=xr%kIKOcXP~*J?ArKIWa3kjeunrUO*;r
zL$O`Zk%m*Nop45~09O1TD4d2UG7`3x1WCil%n$|BI?r{!d+_ap?;KvrD*&b_DkUSc
zH|*2SWoxY(`LKoJBGxX|Hq33Fubn?Pe-v2q1lm-4Hz
znBmcn#axbvcT
zjTB2&-N3Pd{-Q7yiwYBOQtexlS|L?>rdEsydWSIiku0R$0+r=ssE>oeS?ITd|B
zoCDV8Hsp9GDF><~CC(=eCteE>;
zgT`T=Is>0&l8!XU?-{d4PT+g`VFLr2L-)>8Hlolw`iChjsB3YUP*Fy3slYP+Smb7Xbx7wPQQM6*l^I_`(*S+(&T@
z04Qu@VaeELV{cdU!=kC6f%()S$gwm5U7s=n4I&YAeow+f@(i_8%=6^|UNFIlc%SZ0
zAW68DaD}R9LCm37Bytr0A4=LOnWW?&C?UCp_%0xFqssGR2^)2TZBQ`A3Jfvw);26DA-!ceg#!gu;+_7=LJCGv
zYu~91ZwBHhJxd#HX+%_kI4a_bY8DGO#0odW3pY(Wmh1&lSalGm-Wn@uosYyzwoe~S
z7DMd%usK%Jv*0+g)UbZBVN0xGOT3|dl`FAriaBbg_g+3S^W^NwPuyM*##fxLJO4w$
zNBMuA6|Fe3;5oWtFgh!jw(VWq_GE0^lYd$E)Q$dy^49qm?v%Gh%buFCAz8^XI?IK04Z#~?GXtXN%OrOWE)Cf*;2m#v?%E#(!@J{8NWyE
z|Ejd=cenp>0l4(yt%!tNS^$P1zu@zGYw%e=gJ!h+Y5w!&JP2EOd_Y;n>o(%&D}RpI
za$d+kclS)g{`1d#Unl}Nf~mZ<)z#H%_{fsm<vXkNh1wx;=*P
z`OSFry;ghIcJueP@s#eecRS60=HMyy*t(m{e^%`1t~38xof-EjM!B@B(_V1@uaH~;
z`Hh_(Q!QWEfid^#7sTA4uk@WdBekg+I+Akft1Y^b3}}jqfS#
zL*!yp+n8Z+4qsML)JONAj=9H_Zr3bwWbo~eqBDl-9)y-&%O3S4#Q
z9GTr7=sCcio;Y&&P}rrN+tEq~ccdtFY3PU?xW67rs$HPk)l5Su@TAQlHFrTu>Zk=|g0rPz&pb?4$BS)+NNcfPXa$2FzG@Ix_=`1qsX;;WH7U68H_$
z*pQG_N)*%S1+>>_y;JS&SfJpLl*WZ8iu)e*DdGAr_sQhDCXvSo!kT*vrfti$@W=b0
z{CfFqf9Ic8|0n;S_@mt?*F1fcQ&NS&NaQ68=oS7}{J-%=tY)ua&J&!(
z-k8Mxir=BvcaSJ99;{7qE9y*G;c3N|gSe5JFd|`=1@gWu@5s5yP0DLG%}(>-3}*?_
ze;rL?H@0$>HH#J7V-?$PJ`=Cl7qga5TW56Z%=s-*GSS@lsnxS+t&Lf0p&Xd0UCgbI
z<<>`cJn<8w0bC?Jk7!S_oq#dVvZGiQEX@ly_D6RfyW=>HO$x7&%jT5*e-^NU$QOv}
zyJM}FK^}e5E^SBg;Zdq70~rO-$sH=M1O#XdR~$odrXa-#!09(k=9g?lD5;j}Iemix
z_zU-)nn1|DzQE9s1m&4c(FQy5la~Tr|TEVaUQX(bpd|Tj!Hq>
zbS@B!+$pyKLj)}6_s!}&011F&p31`fWa-WeSmqA!ds%)<_;0gSaVLmFh!M@C8`tvIHz?w+6cpMMwL3ZYQ}WQJy*&96f(e!ab;vc8E#U^0CFo)xov{?o`jk8
zThWU%TUTHysGi5pCT;i$>#4~;mNC3GWee0gr4aD?X0I0bIzcQkCvbhtE1BImS2$l4
z&ufl4ni;@~zlT>Ty{Fa7dSA_Y4+v9W5kIv{Mcl{bC>y}96L^O|oA4*8<8l;?FWYA|
z1jh{Znb?*K-0Tb2UcCC^8|N>`lnGk+U+|oQYWnb(34KMTJFo-2!i_9y5gw6*RayKGuV_umWHGrs;o0fRq?rH0CjyIOy
zFkckUSr4skZqaPP+|vuWt%RQQr}up9a7~APb3eH=1J
zi?m}zdxr07%i}2uZpTKDOco%kV2;Q>cJ3WQ4h`yVZCEezkZEPvK24#=g0vhJjT0?P
z2xJtoJ;k`8&^Rm@jBY8C)QBt_CJR^uN}{C>1()0oBg9!8qCv#V)0gV;K)_eCe)D6S
znEYadG+6?y1aRV@o+sg49~#?0r}I#BE1c5DJUT&Z({z(Qih|P!Db5m)15na^ij{o7
zMYBs4N*5jAXhy+IT{)G$0(hQA2RI*0*?b1lobff
z47CI}A;K{1qWv=y!KXXGl7;96{!%%xIiJb-QC-Q8swsug+l?K7u$K65&^kjMstV@!
z>A{~+Lgr&KHjwvUHo8n;k-GI3fy7a~!x@(CQ(fr8btx$BQQ3Z%#{V!H17b_}u{PS)
z!N(drewXT79rv!A?p|_xWAOO)6wes0ST0+>X`8K`GrVhg+cKYj$FugH1;xH#w*D(-
zA&G+Fe-YQ4X|$90WbCo&SCZmANy46Kelu~ox^=uXqeI9UO(v_irso7<&Sz4qC+A4&
zGeK_JmFX*?h}WdepkQb>VOPW`XjBG_IpKtTCSQH0{}qzVEgz9XlhG5?lCch}x@KUq
znHCU-IX*&GkZy)Y&y~P9Hq9l{jew|#s|ksk9H12pI#HY9>|@;>U5C3{LkMcap9>G1
zpr~DH+%ZH_RF6`l%rd$|+#KK}B}6Brqj2QasSAeVh?WIz1a6XJV8Q&0(N=IM1{TW>
zcceE0qKV0KSu9G{&=}FpI3<#>>v(|q+o(;lD%S2t{2m}>HGUt3klr!zi+HOx)UrjOj9g91;nnlnqy;V2Ov`&l7t1Gj!tu_ldRc
z)1s1VmaCR)j;oG$j=lTL+t0uRsb~wdZt@|9uf2Tn<=K%1ck{Bt_1gZ6`)8hCaMUc3
ze`sCIQ8zcT;MllaT}x3K-fe!nd4B3ZpX}RJ6c9zagHpb_G1qVg3fr8p+>sG*PQH+I$=
zzgutaTnFV9K25z$xM91!4>w6D3ijd|nJnL4VxHK>K19)eDe@)THyJyHJzr(B+-dv|
z_R8Hf>%W3TWv`5Im0}N4&^vn)E=!rCw95V?&5+7Vwf@*mK`M^N-TtW6FJs~4Poy1>
zed6D;PlN<7BQXO#nLG2F2n!Sj2WzT@U73s(C={7~z!B}8*mG-%4})YegZ@$7JND^#`rBte}TxN=642(W@(v%kxJa{!d*6i>{iOt0vmC^TVeWT!(S~U~WDf
zLv4Hb={-y9*8h9UA6fpbE$Y}RJD|=s#2nRgPcP+_e(lJLnR1a}y=pOUeJpQ14sI%p
z=XFFK9S`s8+M@Zd=+Zxu3hN>&3_B;)k#+(4GVGEpN>kYvQ(y#bnM-GGi6HCJUO<#a
zNo1r7Gzt1~?m8&9epgpmHyQU(slWUeo7a|@y9Lpg+#90S4G+&Ntuj0Q-TwxXUu4$;EDoIv
z6R1A0#R**&qUx
zY?7ry=LAmd9fk9U61!IM8DVE+C{!~Ai$zdbc9baD7ieuMv@wQ4>!%XJ8Ab?n&NXaR
z)mZvOr_e~!w<+~9q6NQ6Oo2BnFpgGHQX?2(X3{`Slcx&X1=FJAkNO%A4~>Ps?Ry#)
z7tHfTcZWg1f6(s@cmood#*!JzLassgT2tul3H(-uK#iBI#8oHHNUe8NOg?ODoX#X<
zUzQY(7Zn{JgPSRB>e_S}NYoP(5xm<2g@G{O(-lZaD_hX6g7)csAG-@ah9NEg+wc*E{VlVy
zZJX=A<7ve4HMR`2Ct(`G823L#Hwe=RJYfv%*BNP0adYP9usJg-HfLorMH%6ly(fRk
z2I^A7NvK-6bS@k&ex(s3EYd0vf&mx|0T|Rwq*}{Bhp=8`c>d4a3k<(}p%)qG9`W(a
z&y#x5Dvw1*^&}e~*v=U^gTZqMgn?$ybY!mP^LnOs9VFCz_3Cym`_NU|@
z&B`D$W-9c}#HgMuy!L0G(2&ieh`?Vck+i|zF
z^4gB8JO18iD0eLt6wY_tD17hGhdJLbyH)msfjb??UguZo)pXaT!(d?HN3`o}yQmEO
zdr+p5mFd5+`MuzWZQtL1YkTzQ@jIO-SfNTPbc7XJw3pttm!fgl1Qq*Z#ePJZ^SSPN
z;e_qI^4R6aW}mv9S3Cd22Txvqa8`aS
z>8%(KX&3UPK+4^m{KcG>SWXMX8)V#GHr=u0_RQ@2S_E2|$khnEm@3zOTv$rx?qwwR
zRumE1N<)P<{9}W?4joJ8vLc2I=lbR7a!XjJ1J5$+bi-Sklg}$WEN(SEJXw289Qs!)
zlB|`C8R~Z>*Dsg}i@&E#IgZgYidDMd1JxZd0#Ct0irER{OQv99Oi@lSvDSzg*u~DU
zi&@p^VX{r^JXwv$hYhP2R!th+NLKgBR?!g?bLBO?Y|<@R6^ukNYO}%}`{lh@1r|i=
zXG9NCNoyH}B**E){!h{QL9J=KwY-aq;hJ{MigIKEI#1|Q`dI`GipVclAs_Mmz*i@=
z<1hxYA;+QS+C+gY&gFy&s;uz4h_m
z_1^X!yyG|oH0H|r{4*zQ3|t2)F>
zHR$rU9UU9ETV=LAd5&B4CQ3Kh_IQl9w!8P(jUU;~xKHsa(Wn&)o%k=k;x4$-R*a_I
zrnT@-T8)MbYtNXv_9&8bTKmRG7F*SraJr}$usZ)2SdR+*`Ae+Fl1Hvbc^QfnbE-#-
zAQh*OsG4hSVTiv`*hUtGz@Puu83_4w9lFEIO>;kN@qKLF9kX5Y
zCvN8be&6dGqq#e#JCUfh80QAww%x&D%%A0dxfPPjzm@uLzm?o$jLXo38HS8(vFm==Zhlw}7@p`L<_nfCmjAU#m9W}{A!rb!QE
z4L1`YQ0p3BNIYwDPaY{k0QcI>P%|;+DjCeY4^rw?U7Idcn%c(hG7L(b)Ze4)KNDCmJX=)Bj`{
zhVqU7(_|P*`#_8-G7R9nE-s**6k34KKpnf2=u~Jo;1OzgOS448A2sD4Kr!vGdaL8Si3FT`Z>#
z_MgsmcT~*->d-DtXE}$?F>`P2m41`S<_FRpl#nlsuzH*HH()+UiI5esA~;pXuhwf2ETlb)
zSicJ14qhWx7{FP3m=R{e!a}?Rq!UaDo+#0Dm^hmZ2jEJ5+CjJvR0l0^Q=^C!gcp)O
z8xqhyCSOL_0+$@vN3{}_UBe!rWrXWJ9Soco{|oL#rhe6f#>hTjupvai8GwOo6Phhs
zFG7jTiN8q+DP_g4(H$LFCT3GIgM?h*(T{`;%3ir260J~~Z=zII*Ih=|a9Z?J)x2Eq
zz)4yX{|AagWm{2wZS?BsV#T&t#Wr@b#ZsB?TF=#<#j?$@vd!!i39w3eg)=W)ZkV<#
zj4lwBJP3;*O~ul=t7IyFy3I
z@DkdLIp#09Ve+I26y#PYC6|8Hgd2-GOE8gFkvQ>)rJy4K3vy@-l3rh?<*xP+OVpZ4
z*{sK+k5~lru;#|BZv!az#iQg=zT8G}?jr0PAZ~F)Y*Qv7Ybpya%6U+EYv@7HOu-<)
z$r7yWn-R(Yha+V{ZLXP~>E9JGpkE52jF^}b{U##VMd8DOm^gl0KWRY~Q60?AU&1b*a()EbdtqWY5Nhy~4i9QBd@56PHT2-f-G(G(UypWFr`&=BgEMxp
zFf(XFFX6bOc+#GeF@b67Cd6#oo}m&Ji#h}BBfX5kIhwuXLy-5#Qon;^=Ym~FkC*SO>@yIW9vt;Kbs_SPvKQ60UvC;d_
z99(5fwB=B|;&8O+@ZJ2P*^bwrhHvmp=iQcdGyE0DWykFCx$3wFXU5$2G~HW+?^rS7
z16S5@o}6jx!+4l*UJd@;FTgOVA#*bkM{?vvf$s)Rd&4OGSg`(l&F82||0dpoawZqK5%K5DJMi*UZXF7E=&&z)MdHb$+Dc(RyR6U(c)
zo#&qu5n;|&h_i`6(bv2=vEZ%)tYW>GE)U-=EV*+2^7$DP4rzRC>f+Sw(+lqUr9ud@
zW{=HG;&E=l*B?h1Ia}e1jk7g;e%IRgK=Pq4uXy*^_;2xh%%&@axcftXkHz#s2i<;T
z;gP1;W@=2pUO=8|o5@ti86Z(CNHVt8pbAYYSF*wbri?_HunW+W0jiRSZkL)<|Et2M
z^p9#Yb4y?LJPwt95GZQxzfikic`}YhMl*2}+};3;bn(ztefiP<@@+(n$!(C?`7c{m
z4Uz)2Py#VOhys9-1|{scZ=8WGvm3r@tY^eVoopc+oNA3QQManbmX_0j;CPF&PfAP=
zgk2w^H~AnlrMOzqPe(BmS)dfq2npKanhAMOV=M<9n*jTDsDWtNGiM$7kW!}g+~$MV
zZUCm~qzBb%VJ=&`mk#5?5N1#OZUkwdzVYlMn;x~5d|4xsc^Z}{~tR!<#1+v16
zRHM`%b?=o`Pgzn(fO;%p7#>SFPXI+7IP#PTZ$yp*}TK2{4MJpW^IB$tsD{xvMN&X*8FMSs+J$?}<7godW
zc7A`GSE%habvD>)-X{cNw_v87jJbuztaZs}}}BW~;hz^V>X=k0?t~%|d@w
zlmTxWt@K6k_rD3`r@P9Ne2Ja+4V$AsLMiaq6PR1f=)|ZBCW^tj%sGDpO(_QJ7i5n3
z4`~XEx{~zjtx1JHqIsjxM4uy+7kNAv$E?LT-Vr9KHrGW9oULc~-Lcj(szvUILA-|S
z0OX#%{`Dd;(TVIn#pnZ~0Y_io3lsf8G9AG$1UiIUT3??yihN=LB}~rs2XyxaCBH)n
z5u74ndKNKP{9oygoXBueCk_}K9*~9S>@+Re8~raR2bq9~aZ2p?M)Bu#N1^ao7&Ebj
z?wI%c4!YYz38@Z6@_iGJQ}P@oWb-czB*@fPB+F1{mnD)Iju{y+`6$yni6r(Erzs&p
zgZMj?(5^4iLJ?`GVGt&ZCk6r(;StVwY)bHk+bvHBO}9wN0{%&x@aFSZxqF5-v-KWW
zw5G=p?RKfO?&G4`kBjRdivVqT&uwkUikfQRkC$J3;qa=-VQ#utR&K7IJw5kwtm3g1
z4)-6{-lspSJMHE*t0h+R6FjnK<=!(m&4u@kxLcvS)ruVRj@5dLIs1NYjyZd!o@H_1
zr<{fEDh~7JO{>3R@R=)CyLhv?VAbU|m*1~##?$BcY;*1XBDc9=wc2EETXlHM`75=o
z61%xtsRVbcB_{K_RY#t=e6`7BZd!F@oA>amMP~E*RhQYkanl~A
zz->OvuU6-v>-G6~)5tsS2L#SOe#&q2W
zuMLtU!1Y4L*9(bZuaRt5#nX5k7CVEmOJueN6a$mE(*9FqV2~8fnanyF?TEh&o$C%w
zt*|2iZ##q&b+8%GL5K38wTMAA8XU&?KwWxN=%?iR>4+7!PQ(w;L-7VBe?-Zzqd)S(
zBIzva9!vh5)+9+TUgcH|ym_yDsvmphe_AZ0qVtqIL8bmPB|oI(0wru0$tJEEdjUd*
zvm?zDCfY#dvn=SOiqrIh)P;$x(BwFVKqQ(>q$Wk8z2v3H7A46yMJJMk6Z?dH`fubVe*0T$NQxiih9u*EM*4mOIS;*m~XkHTq`ny2{
z3Y(;;D%i_dpsM87$Yq4Zr%L2wC<5YWD|yfM5kbzvt;&&J@~O&PB?I$B4+s>#i{Yw7
zk@7<3I51GHBmLr81YQ^ECSCl8-`^7e!k+QSi^FRjE%zhTCAlX
zRt3~0v5pGVQ^L5MZFI+U5Rk!fBAH5wJ(QfJ-tTuMjd|C8_3)3qRdfD#Ti57?quDqDraQl=od__z$BKUw3QN5^%{Jrr&%0`n_MjubJ;^wTg#F
ze&6gRpnlLy@vKaVr{yKj`_hZNz>EB7DfW}4pyYe3^q^d*x;3}}dDZa+6|6wMUT|&=
z%8+k4zMz6t$S)O~TZ1*opL2Xc1vSX86r5Xw4akY(3o58Xey!l#8f-#--SGt#G$4Pq
z;M^KqhJ45I1r_`e@|y+c*5EnFZ#%xAfcBu#Uj!j5T{NO5#=#ct;+wjr)__-Vhx1Wv3>hf
z=4l`9W#0v|XBP?WHF)=FzT5d;IWSjo{JCX0a}NLK1FONN99&Ne!gd
z{cn~htkLe*(Z5NQ&?Dbpy
zfxG}e=0!j#%{}%e%QvO&B_n^f7=6qVal#g2^p9V~XfZYIITB!O>(n0|zc_ziZmuPo{#dcsJT
zv9!R7{LhtLt5P9&KH+J|$iaaN|G@#_iga@xXGEmfinz-1jK}KWV23E4^y7gwi6j$>
zYBFApwVo8ouZ^AZI3@`_W`Lyu0T)Lx_9(<%WS?YW(aGPT$d$2YDnY}Ml_j9?abE#i?(5Z`fJ@)>;v~rYrX*%GGVY}SkVKKw
zBbFMQ3frkpx=v(aG~#HBOvph|P_sEm5`K7;5gIFjdg)>EX|pJoe#|mvuGMrm(y2{}
zO*m|)fG^UB>k(y~qX;4#3v~m&%36mlgA3(Q0D*##hcq>Xg7j0;i)q%JURoCg3}%$4
zwzQHw&Nz@D3ibjVgW9ya4nh}LQL?BX2t~gcGZc{vIgDVi!-YaPVrBs9Hjx~DfS7##
z*DpHTEwY#DSmENBb-5k4CU}U21VW4zmWBe8_z<^3aNw3b?8iLhnXyT`I$@_rJUnvF
zp@*i8a{xdp!Ofb>MYB&O#(Ac_Bm!#)mmuK)Kr!W-lA$12
zRDe~X_IVsV~-ej{g6Ld^j
ztI4146)NuxGPa9O;4w4pKdJvlvc5Z2kXEBHJG>iHrL3?rM^_gNZq3x+eaJ$qz3ND}
z{m&VORY(3|c6txtlPQg@h1w!p#&>*b`U9RGTk_KE?1$#h#!A=b8K?4*nLJaKdB!2w
zN7nQpv`V!_c4Hs;mm*|EL|m9Y(UZs?s?5>O@TUx;2o*6kyvWu+OOI1=n!1>*&+cUp
ztiD5)J!}6{^mlzy{WwoEo=oND+hK}m@dx4O^4oS?-<5Z8O8?xc6}+F6eBb|ZspK#J
zRPp@VPb%J}8{@{^QRD7^z53bawejYy(dMn;`dRHS}?e?&8wn4@l&Cy14SUs!0
zFs|MlRc{Vg=gInu#q;j=E=EZ
zTQ7~b?u@qX3^(Q-&nYynj~j1{8gBsQlNAr(syn0V&T#d7#rJQ0_58Vq<-F|s@B5Z{
f-#;&3^vhpuf89R!K%8HygWLQ1KRHJ9Bq;?Ae*+
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

literal 0
HcmV?d00001

diff --git a/venv/Lib/site-packages/setuptools/__pycache__/launch.cpython-312.pyc b/venv/Lib/site-packages/setuptools/__pycache__/launch.cpython-312.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..bf21874ce51e7373229729a003054b62428184e7
GIT binary patch
literal 1324
zcmah|&2QX96rb_f>-`GdO;VIEfh8_kC0P_i1OmjNX{Df6RX_r%sgdRN%qC8}wlyAa
zy34Luq#}aEp%F;QF$WGs;h%sjHzK6eX{z
z*jOHc@IRO8iv*#+q%#y{U|hTn#sQ*e15r#B4{zXfRW=k#?xPLDRI2XGLzb+97&#t%
zhEYptDHW}KH-F2HeP_!MTg+H*iLJmlB8R)7Fu;;QCulTmpBkR)GsCV6#`6&qaVUbo
ziwrk1YC#|(!R;_))M&c4;Wom6i)#L{B)jblDAOea8d^aY0?V?+Xb&SXYzb|vqwvU}
zcpB_368bRIK@?j2Q9YLVK)U!v87P@~5euPY7KvOxBSIn_fb^&)io@uLQBhDw>7Zli
zMal~5k`CFv()XU%!(;EN6c{Ku9`uUQ=jwL(d1T+F8LAI4T}@035giS>&W`vq;_7H8
z)4{_pV7TNpw2UIWt6+2oVfcNsa*y0WySS2TujdVrkK+D#2g7z@?5(J3^UdbnoLocmTxzt
zrCSya95B=%WPV#Ux#_nR(@#`(k2#e>QnajE?0UlWBR~E;3wh%nY
z3aG+&+liV1JI+Pi6wzM9%+a6om
zWwpo^%xp5hY2I>cru3G8e8=8ok@^2CdfiLkU^Q$dgvUOg1I{t{M@OJ~=-)ij%4g$O
z59ash5102BdNL(gDK7MGJj%Va_t{Bq_8eht?l3wnPaaI|PklSHmw84Y^2hkpL3O`+H1%-h
zWPBNj3gZvnJt{o>X@l&OItxA5bYS-S{(vq(BR&i2GW%X5ps~l^=dXe*FH*Ds>YQMVpC|;Y7bS#CzoYy=
dnF%~|zKGP~Ui()v{Ycfnym3yDI>TQ9{C}8DKgR$7

literal 0
HcmV?d00001

diff --git a/venv/Lib/site-packages/setuptools/__pycache__/logging.cpython-312.pyc b/venv/Lib/site-packages/setuptools/__pycache__/logging.cpython-312.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..e24ae549cd3da6d7709908dd478796fa86e95c62
GIT binary patch
literal 2061
zcmahKTWA|ca8Ei(Cn=H}C8-~6>cdGeXUA@#^rr+vleBh;>x8DEAQa5$Zf%`%x)ZxA
z8#y+(P$+>yJ`;n%{xvqF^sB#xLO%KvVF#Iw`%(JkzYwQC`ReRSD>=c?L7JJ}nVp%P
zotgVHlSv@}^7q(WQ%C3@u}~0cGe=i}d4yc#YCg)hsFv5{I+Bk_8qMo&#Etr~N<1Ie
zkgNZQ@(DKv*l^>3lXcBX914RohZawN94r*3y<$PE#;Ue+&z{G$
zKryRTSs42Cn7D(4
zvMeRFYEflnMO)U&nYU$P#jYg*Oe)5C%WCP
z5V(a%vxM^ditAF=r}*-K;<~i_VV75&ikE!D2Gw5KR9u&`A;ll_ae_Tp-xeM<+6^fYAxTnmFaZyqYxY!{@-LK9a!X143mv;)_)i{Szm63Sdm
zh&(Cwp^ZqLm7r`*>{F!?*4ViDUCENJFpo_ykVjbwn{zeMbmMZpl)PWTltO>4ywUDk
zI*)_#ip_Y8vbvAC0jFIbI+({~+w%-`;Vdf=OiQ8f&R98;fb8{B%d6d>EkHhuhay)6f?pL1AAQhrpdAev*&uOKS$F6^Q_0A+ucmb_qhjGKBmC&t0
zB+1V8xGq>8gu-oJi%Paodz8F+l-Sb(uU7q&Jp{9^o(c$)b=^KUc~i@%N|
zqoY{*xU8WLR;0t?V8f(-sN
z{lj!q7ukJ$X>(|8WKX|zknU@yhZ^aj&Chnz)-&CbX|Pg6j3n}bK&=|>RFG&8avlU6
z$qJH%C?8*X+(OcVjN^@QrB#3vWE2jFEKpG;FQb~Ky+ElKC@X(w|3YV9#Wn5nE90Ct
Mdh|&|8<9r-10CI_%m4rY

literal 0
HcmV?d00001

diff --git a/venv/Lib/site-packages/setuptools/__pycache__/modified.cpython-312.pyc b/venv/Lib/site-packages/setuptools/__pycache__/modified.cpython-312.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..988aeca09770a8f5201767c4232db45ee6cf7038
GIT binary patch
literal 359
zcmY+AJ5Iwu5I}eRNf05aB5?s47Y;o_G@%5PhQ?ZH<=7Kz#rmWD#7a6&K~KjexBxeB
z7g5n6Tp*=lY)HWrZ|2ReWH7vC-z}8T#*^OAT&cZ5A9^wo%(_n06%2Mj{!!2gjW`A^vW+hyy6Fjqf
bus%h6x|Gsy$EQw95ML8Ax}}dp)&1`ekpyZO

literal 0
HcmV?d00001

diff --git a/venv/Lib/site-packages/setuptools/__pycache__/monkey.cpython-312.pyc b/venv/Lib/site-packages/setuptools/__pycache__/monkey.cpython-312.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..11e848a3e0e97b3c2fb36eca58c7489a29090d0f
GIT binary patch
literal 6015
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

literal 0
HcmV?d00001

diff --git a/venv/Lib/site-packages/setuptools/__pycache__/msvc.cpython-312.pyc b/venv/Lib/site-packages/setuptools/__pycache__/msvc.cpython-312.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..811a8abf97f42c20d1465835389b8592aa3b5c9f
GIT binary patch
literal 60692
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

literal 0
HcmV?d00001

diff --git a/venv/Lib/site-packages/setuptools/__pycache__/namespaces.cpython-312.pyc b/venv/Lib/site-packages/setuptools/__pycache__/namespaces.cpython-312.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..b00b1e25a68b24680658be829b3234e6e4e822da
GIT binary patch
literal 5310
zcma)AU2Gf25#Hn7QAdi>=|BFBFO_8SYMFMLIIjQXq;h^#yNVkp0Yb|;(7cmGndGs1
zC(C4{KnVOG1ui7RL8Slz;-UeXz$uXDq7Ox0`%p1-A?D%&E}8<(nZi`^@s5qHOU3Ap@3h_y9An`DIU@YU9E
zfm1_Y;me51Ief+(|B8bbs0;bR6US0+HO1r#eTXwGdHToX~L2vXhyNL5tyo
zIV*op1fhZp*t`L)%VdU3)^n@9Jh8Lf3}{VT~Ck9KuR{;1zJ;$w2
zHa*-n6Sz~Ik0W--<4-b!dv9*3bI`ZCZP2&HH_BUvN6oZU|_#=HebdEaDGy
z(V49GfXtCfYsXq^wA30cx2p5}=W<6S(oyMrWUVt=>Wr2<)p@BRMHV9qku_;|N!q>g
z!d!U$|
zsDY7U^Gin7yz2VD+C*{LPQbJPQIqzB6Ev=YcUsJysf@fJ#r}pGppU>S-3i3PK3a2#MtlXOoB{$avBmuNOCpE
z7+MG|#r`Jk-UI=pv7P3CvrKI4z@ACpB3=t2lipERYeF1ufTw{Ub-=@?Xt
z-P@FQdmjK63p#KCxpVQ2g*R5ET}%~KDt4!{d6U|TRj>vWyO1}aFs))=UO5G299=a&
zX{K|r{unh)TLt!y`(tntt5hw<+`*ajW$3MG?wh8?qJDdP%1&pZYVong+-x#60pLpu
z(B;=z5CUq%v!*_k0R#kG)Qz#^RK|wM0o_$PpllUf?W&DYGpkv7BUMucdbnM!7?i%~qbV3(qoJ|grWI#td8oDho=bdh4`HZR1+A?%JQfIfJ{je~li
zKo*OG@1O#3QiW1NX1GS_n(l+G2NVDi4PnLu6qZV1!no~$#D|;oleZePHp3|BH7m{r
zXPah3D$fWoPlFV%2ua5$M_D=~V=sbl!zANShvT@dw2%uwY=hD#f
z@Xg_s)UA871POVY0S;1a*|
z%ID4Pl}OJ@-`cLjCHRjVp8t78?pVCGaBU@AmU}-K``tuIj(^fsl8>xPRgdz$2IfZ_
zF^^h-%Kt;wMu*ik&Vqz?9F85^KelZDsH!Clb~6ZO4|qg_8(A%l_pHCYya1419C!)R
zKbli4Kw(2yFlANDF$MBsrJ5lswe(MIaw0nPpy|s1tYG&JpI1wvgRUsFqwO?}WdI)_
z0mf^D6V@Z+w$}mp^x=bAamrt+_@$zC0SXMpD4*s
zd?p|Jgw6~1V@Ct9FVhw
zbB$JSQfSAg7MggcqfxyfMAY)J-*C$uhd|<+)0KnN3q#~n?#BK?pb+>24>uUT_}6gN
z@0I7%khv?#njJjL3@C-Pk;Q&7_5
zKmZ8LJov#$tcwUHTODB-+KXL{^m`dN){{W~O}?8W>s_S1YdLf?w9DCRu$D3W7H5FshT6Z2pv0-q1;bSi7u6Q)jaE%XqQ
zJ|vh>Qw(ZG(Bmy!({)m5+cP)3A+(9gMxQ9|*=P-j>W0LLyVrwqXK?Q9xPK<2^IdQLRbY!c|>|)}C5VtWKcCUO+2$CtvZv9RWu_I}_-zebV
zO~Sd|!g5;dle`Syz+S-Fn7^`lx9ajhI_XBuAl0BBZl!es$U4t)+!rMDce3*f()KU1
rV?9WkyA}^E99j!UOX28T)3?D_Ic~?-1nD<4AaZ+`3jZNcF#Z1rmdmyS

literal 0
HcmV?d00001

diff --git a/venv/Lib/site-packages/setuptools/__pycache__/package_index.cpython-312.pyc b/venv/Lib/site-packages/setuptools/__pycache__/package_index.cpython-312.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..30d4089d02a32c035eaa6ad925c425897d785803
GIT binary patch
literal 52741
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
Bh;#q|

literal 0
HcmV?d00001

diff --git a/venv/Lib/site-packages/setuptools/__pycache__/unicode_utils.cpython-312.pyc b/venv/Lib/site-packages/setuptools/__pycache__/unicode_utils.cpython-312.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..07f16728a810f56893e78885e092a4f30286da90
GIT binary patch
literal 1665
zcma)6&1)M+6o0d;l_goq%CcprX;W`(<3^>92|YA4rC=hb&=e6cKEx>4o86JUvAip0
zMzQ5cAW9Do6q3?|eJG_z7vBo~1KevbNmPil@u8);UZfMN^bU6f
z&EKQ$z5Ex>oxEI4luT>cT%1P;S~9$QpJzTTdkBlJWR){djG@w5AJV$)X+yaLVu6i
z2)*fVh9RD;-#9+fAb$1$2=0%>_j>jEji!c+6W)i%ZeN)if!&el)TFw5iJ*LgZjwF$
z-F`ylKk24O;QpN=JD5j~HNq8wMNlJEVv~YWQ>sc9Zl?ii>OT>&s&<@y_g@v5w(uD2C^$umx}ZoTh
z8ATnA;t)x-nS_kk=zW<;K1+;0NsKoV7oR3R-2SY7eXnn5Yv|E%{qx`Tw+>b>h>YLjl6h3pe!nztsc|uS!imHD(OHQbcAk(4x|;*g;ZIe#7cCHc43{O;<&;y
zOrubCE#Y{sSu~i3Rp-)NMq#oObzgou=m)KrSNt&c5DCs8hH!au=b2-IRU{nPL)hDB
ze@_NnL{W!kNYw`R^z_5pgWA?iL%;Aia^ZNt_q?pKf#-TZzK=F=!ewXsGT9H`Mu*k!
z$*`KySOiT>7G6;O0dzs~AK?&<5CD7<`DDej%SHMrdlzl;z4$p4n3a$hka_{JgGiJl
R56-4Y^6-6)46yU)ehsPJdrbfU

literal 0
HcmV?d00001

diff --git a/venv/Lib/site-packages/setuptools/__pycache__/version.cpython-312.pyc b/venv/Lib/site-packages/setuptools/__pycache__/version.cpython-312.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..644d559744d8a5940d8c3e6a8ccad6b09552527d
GIT binary patch
literal 435
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
PpAslnBQ9aTvpoL=B8UPm

literal 0
HcmV?d00001

diff --git a/venv/Lib/site-packages/setuptools/__pycache__/wheel.cpython-312.pyc b/venv/Lib/site-packages/setuptools/__pycache__/wheel.cpython-312.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..63fe650f146d62640fd5b4f967e8286e19c527b9
GIT binary patch
literal 13491
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
s-mQ@o$BXkYX1tsT%Fhd1KTq6V*mgE

literal 0
HcmV?d00001

diff --git a/venv/Lib/site-packages/setuptools/__pycache__/windows_support.cpython-312.pyc b/venv/Lib/site-packages/setuptools/__pycache__/windows_support.cpython-312.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..573e8893d77512bbed3ccc2b62c53b0b5c48ac0b
GIT binary patch
literal 1460
zcmZ`3O-~y~boRp^F~&}5)4FPk*Hmd`Mc4|UQc#3MFsP`=ARw}YEIGD!3~ScA?#vk1
zmQhF>sj5_|N77T3Q&jN>+Eaf(FLsDzXWB!f_L3W|Z#m`7;zb3NC+(Z}{oc%bZ+{;i
z4j=&XV!YIr5&By&E&%U9e+-U8q$6E2(H7F>hiFT}lCC^NM+1{Bxgo{Xmx4xY2ZJcN%I^M5YbYf-kP5xi4c_x?QJ5*W5j$K=f5Whp2(#
zQu=2Um${lJ+mw5E>*Ay$nWnueFU)4|QcS3Wvnjh+t6+=K?1HTu)^>KyD6$&C<7>5M
z)5PO-T%ZQS**$FSWmk+sR&e9hd~r9wjcJx*R;x1GHfgecT3U|Qs#Tk?M78nCf7{Gg
z3VMF7IqGU{%WNcWn|3j8(m5bb^up|s3@dordpab-$FZ}KO2ltPCkw?oQr
zb9yud(V@?hE}LjyVnXJCt{e^eCZU#87WNmQEG}^Y%Ki%iVE1M4cP8$_H}3VYpEf}JWnaJ-g?UD0+JtJ2wIk}?=C
zdk>S6Y1b1)yOR8}hAA^_izaW)d^$04YjQGq>UXc2XFK<_N{uqDfHj7{VhJZ85%s5=
z;Ua5PF-^dnIK)dKjt9B-RL%proKX?un#*yo7$(!?{#{HgY)($_5xC3}R6Orqf@Zki
z4aL1HYnhGBb?!-JR@YP94_oHs;Od>#)fMg|P%|Xw{)}NQ5@M6MhpVDqBKpAJx)hvW
zTUuUO%*}6Zt}oxYySbSAd^wd`Oml@`Mm^vo-ghwhSKxhdPCO?W1C8h*{RYqh>W?7L
z$UkbR9oC+yv0h~Kr{N!myOBgElITX}JCXUedaW1xTi5#HN3F_qXV;(e6yT
zGm~z|(x85(j`p;VpM)QWyV_Jon{q@S{&ihztM9)EeDEmM9-BT6e0H$#d??flz4L?p
zoqZgNw*Ao)1qH*-cEuGU=Goavr$Qpu!%;b_3;%|St=CLEM?M0D*ex0ZbfQR-^al$6
W=T)SuCqX2K{*~XBw7%j}PKeho7gUV^

literal 0
HcmV?d00001

diff --git a/venv/Lib/site-packages/setuptools/_core_metadata.py b/venv/Lib/site-packages/setuptools/_core_metadata.py
new file mode 100644
index 0000000..9b4f38d
--- /dev/null
+++ b/venv/Lib/site-packages/setuptools/_core_metadata.py
@@ -0,0 +1,268 @@
+"""
+Handling of Core Metadata for Python packages (including reading and writing).
+
+See: https://packaging.python.org/en/latest/specifications/core-metadata/
+"""
+
+import os
+import stat
+import textwrap
+from email import message_from_file
+from email.message import Message
+from tempfile import NamedTemporaryFile
+from typing import Optional, List
+
+from distutils.util import rfc822_escape
+
+from . import _normalization, _reqs
+from .extern.packaging.markers import Marker
+from .extern.packaging.requirements import Requirement
+from .extern.packaging.utils import canonicalize_name
+from .extern.packaging.version import Version
+from .warnings import SetuptoolsDeprecationWarning
+
+
+def get_metadata_version(self):
+    mv = getattr(self, 'metadata_version', None)
+    if mv is None:
+        mv = Version('2.1')
+        self.metadata_version = mv
+    return mv
+
+
+def rfc822_unescape(content: str) -> str:
+    """Reverse RFC-822 escaping by removing leading whitespaces from content."""
+    lines = content.splitlines()
+    if len(lines) == 1:
+        return lines[0].lstrip()
+    return '\n'.join((lines[0].lstrip(), textwrap.dedent('\n'.join(lines[1:]))))
+
+
+def _read_field_from_msg(msg: Message, field: str) -> Optional[str]:
+    """Read Message header field."""
+    value = msg[field]
+    if value == 'UNKNOWN':
+        return None
+    return value
+
+
+def _read_field_unescaped_from_msg(msg: Message, field: str) -> Optional[str]:
+    """Read Message header field and apply rfc822_unescape."""
+    value = _read_field_from_msg(msg, field)
+    if value is None:
+        return value
+    return rfc822_unescape(value)
+
+
+def _read_list_from_msg(msg: Message, field: str) -> Optional[List[str]]:
+    """Read Message header field and return all results as list."""
+    values = msg.get_all(field, None)
+    if values == []:
+        return None
+    return values
+
+
+def _read_payload_from_msg(msg: Message) -> Optional[str]:
+    value = str(msg.get_payload()).strip()
+    if value == 'UNKNOWN' or not value:
+        return None
+    return value
+
+
+def read_pkg_file(self, file):
+    """Reads the metadata values from a file object."""
+    msg = message_from_file(file)
+
+    self.metadata_version = Version(msg['metadata-version'])
+    self.name = _read_field_from_msg(msg, 'name')
+    self.version = _read_field_from_msg(msg, 'version')
+    self.description = _read_field_from_msg(msg, 'summary')
+    # we are filling author only.
+    self.author = _read_field_from_msg(msg, 'author')
+    self.maintainer = None
+    self.author_email = _read_field_from_msg(msg, 'author-email')
+    self.maintainer_email = None
+    self.url = _read_field_from_msg(msg, 'home-page')
+    self.download_url = _read_field_from_msg(msg, 'download-url')
+    self.license = _read_field_unescaped_from_msg(msg, 'license')
+
+    self.long_description = _read_field_unescaped_from_msg(msg, 'description')
+    if self.long_description is None and self.metadata_version >= Version('2.1'):
+        self.long_description = _read_payload_from_msg(msg)
+    self.description = _read_field_from_msg(msg, 'summary')
+
+    if 'keywords' in msg:
+        self.keywords = _read_field_from_msg(msg, 'keywords').split(',')
+
+    self.platforms = _read_list_from_msg(msg, 'platform')
+    self.classifiers = _read_list_from_msg(msg, 'classifier')
+
+    # PEP 314 - these fields only exist in 1.1
+    if self.metadata_version == Version('1.1'):
+        self.requires = _read_list_from_msg(msg, 'requires')
+        self.provides = _read_list_from_msg(msg, 'provides')
+        self.obsoletes = _read_list_from_msg(msg, 'obsoletes')
+    else:
+        self.requires = None
+        self.provides = None
+        self.obsoletes = None
+
+    self.license_files = _read_list_from_msg(msg, 'license-file')
+
+
+def single_line(val):
+    """
+    Quick and dirty validation for Summary pypa/setuptools#1390.
+    """
+    if '\n' in val:
+        # TODO: Replace with `raise ValueError("newlines not allowed")`
+        # after reviewing #2893.
+        msg = "newlines are not allowed in `summary` and will break in the future"
+        SetuptoolsDeprecationWarning.emit("Invalid config.", msg)
+        # due_date is undefined. Controversial change, there was a lot of push back.
+        val = val.strip().split('\n')[0]
+    return val
+
+
+def write_pkg_info(self, base_dir):
+    """Write the PKG-INFO file into the release tree."""
+    temp = ""
+    final = os.path.join(base_dir, 'PKG-INFO')
+    try:
+        # Use a temporary file while writing to avoid race conditions
+        # (e.g. `importlib.metadata` reading `.egg-info/PKG-INFO`):
+        with NamedTemporaryFile("w", encoding="utf-8", dir=base_dir, delete=False) as f:
+            temp = f.name
+            self.write_pkg_file(f)
+        permissions = stat.S_IMODE(os.lstat(temp).st_mode)
+        os.chmod(temp, permissions | stat.S_IRGRP | stat.S_IROTH)
+        os.replace(temp, final)  # atomic operation.
+    finally:
+        if temp and os.path.exists(temp):
+            os.remove(temp)
+
+
+# Based on Python 3.5 version
+def write_pkg_file(self, file):  # noqa: C901  # is too complex (14)  # FIXME
+    """Write the PKG-INFO format data to a file object."""
+    version = self.get_metadata_version()
+
+    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())
+
+    summary = self.get_description()
+    if summary:
+        write_field('Summary', single_line(summary))
+
+    optional_fields = (
+        ('Home-page', 'url'),
+        ('Download-URL', 'download_url'),
+        ('Author', 'author'),
+        ('Author-email', 'author_email'),
+        ('Maintainer', 'maintainer'),
+        ('Maintainer-email', 'maintainer_email'),
+    )
+
+    for field, attr in optional_fields:
+        attr_val = getattr(self, attr, None)
+        if attr_val is not None:
+            write_field(field, attr_val)
+
+    license = self.get_license()
+    if license:
+        write_field('License', rfc822_escape(license))
+
+    for project_url in self.project_urls.items():
+        write_field('Project-URL', '%s, %s' % project_url)
+
+    keywords = ','.join(self.get_keywords())
+    if keywords:
+        write_field('Keywords', keywords)
+
+    platforms = self.get_platforms() or []
+    for platform in platforms:
+        write_field('Platform', platform)
+
+    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)
+
+    self._write_list(file, 'License-File', self.license_files or [])
+    _write_requirements(self, file)
+
+    long_description = self.get_long_description()
+    if long_description:
+        file.write("\n%s" % long_description)
+        if not long_description.endswith("\n"):
+            file.write("\n")
+
+
+def _write_requirements(self, file):
+    for req in _reqs.parse(self.install_requires):
+        file.write(f"Requires-Dist: {req}\n")
+
+    processed_extras = {}
+    for augmented_extra, reqs in self.extras_require.items():
+        # Historically, setuptools allows "augmented extras": `:`
+        unsafe_extra, _, condition = augmented_extra.partition(":")
+        unsafe_extra = unsafe_extra.strip()
+        extra = _normalization.safe_extra(unsafe_extra)
+
+        if extra:
+            _write_provides_extra(file, processed_extras, extra, unsafe_extra)
+        for req in _reqs.parse_strings(reqs):
+            r = _include_extra(req, extra, condition.strip())
+            file.write(f"Requires-Dist: {r}\n")
+
+    return processed_extras
+
+
+def _include_extra(req: str, extra: str, condition: str) -> Requirement:
+    r = Requirement(req)  # create a fresh object that can be modified
+    parts = (
+        f"({r.marker})" if r.marker else None,
+        f"({condition})" if condition else None,
+        f"extra == {extra!r}" if extra else None,
+    )
+    r.marker = Marker(" and ".join(x for x in parts if x))
+    return r
+
+
+def _write_provides_extra(file, processed_extras, safe, unsafe):
+    previous = processed_extras.get(safe)
+    if previous == unsafe:
+        SetuptoolsDeprecationWarning.emit(
+            'Ambiguity during "extra" normalization for dependencies.',
+            f"""
+            {previous!r} and {unsafe!r} normalize to the same value:\n
+                {safe!r}\n
+            In future versions, setuptools might halt the build process.
+            """,
+            see_url="https://peps.python.org/pep-0685/",
+        )
+    else:
+        processed_extras[safe] = unsafe
+        file.write(f"Provides-Extra: {safe}\n")
+
+
+# from pypa/distutils#244; needed only until that logic is always available
+def get_fullname(self):
+    return "{}-{}".format(
+        canonicalize_name(self.get_name()).replace('-', '_'),
+        self.get_version(),
+    )
diff --git a/venv/Lib/site-packages/setuptools/_distutils/__init__.py b/venv/Lib/site-packages/setuptools/_distutils/__init__.py
new file mode 100644
index 0000000..e374d5c
--- /dev/null
+++ b/venv/Lib/site-packages/setuptools/_distutils/__init__.py
@@ -0,0 +1,14 @@
+import importlib
+import sys
+
+__version__, _, _ = sys.version.partition(' ')
+
+
+try:
+    # Allow Debian and pkgsrc (only) to customize system
+    # behavior. Ref pypa/distutils#2 and pypa/distutils#16.
+    # This hook is deprecated and no other environments
+    # should use it.
+    importlib.import_module('_distutils_system_mod')
+except ImportError:
+    pass
diff --git a/venv/Lib/site-packages/setuptools/_distutils/__pycache__/__init__.cpython-312.pyc b/venv/Lib/site-packages/setuptools/_distutils/__pycache__/__init__.cpython-312.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..3f827dd1c7a4d46398eec61d93261f028f612318
GIT binary patch
literal 543
zcmYjNu}&L75S`)9oX?I0L5@;DfkYY?h5{;tA|WRQB8r@Xg40SX+qYP2&UdHXJsVTG
zq(Vu>F9hWm(xgrkD~bq51EQj{pov^%7u$j<-n=(E^JaH{7797gA#TbCO$4|#VjA{@
zJe+EB20r*0fH%(QA;Z?RV>+R8%Xuay)SvmLy-g4cz_Y!KmvLbR7_?rD>(9}*rriiV
zTmk010bwERYt@A#YahB;vhU2u#}O{4uc*(ZN)!(yl|89go3jcY=2WoVw#NtMzRX<8W17>6WaX@2L?;=K@&IEM$ba}-uK$)40!k4WA{
zjik*&B}pandDtXX-cX5P^H$93_2wgGRm0Fi#4DJ;g4}iS_1W&1-HT#rP%M32`CVKc
zz{`IAxBqrsoS=@IA9_
JTR$w+_#e_7l2-r#

literal 0
HcmV?d00001

diff --git a/venv/Lib/site-packages/setuptools/_distutils/__pycache__/_collections.cpython-312.pyc b/venv/Lib/site-packages/setuptools/_distutils/__pycache__/_collections.cpython-312.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..493d1e664882bf6e6360131a2fd6f7368b8de699
GIT binary patch
literal 8050
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
R1r!}rTaDgR^dqYL{2#0>gEjyF

literal 0
HcmV?d00001

diff --git a/venv/Lib/site-packages/setuptools/_distutils/__pycache__/_itertools.cpython-312.pyc b/venv/Lib/site-packages/setuptools/_distutils/__pycache__/_itertools.cpython-312.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..b1c3aeba78fecf1191177104cf8368492df6c486
GIT binary patch
literal 1789
zcmbtV&ubJ(6t130U@|c~xGD&%@E}5G!b~K(mmN%Cjk_R-1e1kzGm@$4e(kjObPZM2
znY4}&!Gn;CH*ey>gCc?l|A9U4B`kXyJh<#XAnP6^Ctr2XFT*S<7Tu4k>i6S&Uwz&E
z$IMKg(U5={bLsWk?Bp|eM^&v3>jKp#?6SYZG$5O<@;3m>2aM^WZD+HA-F¨oyBtzg~x~GK%v4xUWiC0rq
zD)~J0#`5wqPzqRrxy2HkDZ$y|N6!LZ={X*>d8{`3CFUrgICR_S1zt9s>+S
zh-FIF96SR@gqr)IA6`#88V_;t%b|?{RjXYl0JYjEQ`l_jcmQWCKPKqY^^MmkH@*7j
zV{p;qQJ_mu_d_mYIv*D_Zfj6lQr`-=L@lUop!@_8c?hG$`6(1p@Zi5sf&JCu~thkq_R`wx-KK47uq7U
zHPmrjLkr^+c-!W71s3moW=jCGaM7quiZgS*C483-KL*u8$L9ew
zNC|;aexit^R__3k8mqBxh;TsS@J%;Z`mf|y>SMI;g9^4iy9BmKO-kc4R?K!MOzm!d
zsEh8@I;rR@R>J2l`znJ*;3eru&t{Y9ZviLG7`PSnIutLlnyiG%7Yu^ve*
zuSU0S1z2ulT~VFt7KU5ZOMbm-R^=9VZu0A>s-)JarG*I8=}niuRFU@0)F4ZC7g}+*
zaNz4D&HuunQNNPeVQ;TkEH}M#`*q>SgY&!F_X}@whxc+PpH!YYFMs%JcFF9oJ@|RI
zvzPns?L>C+gxOfxjrVfL-@l$d`Y*H6t;FX~^2yT1zA8}iXZi2ht7AVN%KkoWk-bc;
zW$A72PS#_MDgh#sKMwgaIYnkacdjYiD8Oa;4K2+tM;#_ZNiFNmDf{WM$LIcIXRH%)
HhIT&y`r7c^

literal 0
HcmV?d00001

diff --git a/venv/Lib/site-packages/setuptools/_distutils/__pycache__/_log.cpython-312.pyc b/venv/Lib/site-packages/setuptools/_distutils/__pycache__/_log.cpython-312.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..327610a9a9255a0528bcdce5b701827f7bedaad6
GIT binary patch
literal 291
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

literal 0
HcmV?d00001

diff --git a/venv/Lib/site-packages/setuptools/_distutils/__pycache__/archive_util.cpython-312.pyc b/venv/Lib/site-packages/setuptools/_distutils/__pycache__/archive_util.cpython-312.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..4c4513e130e55e4fa573e2995d7684d25cc524fd
GIT binary patch
literal 9776
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
V$7_U7LOi8m_wjb&lQtgde*xkTKVtv@

literal 0
HcmV?d00001

diff --git a/venv/Lib/site-packages/setuptools/_distutils/__pycache__/bcppcompiler.cpython-312.pyc b/venv/Lib/site-packages/setuptools/_distutils/__pycache__/bcppcompiler.cpython-312.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..1d80527bf1d8713b6b4cb4688bb307fe91c0143b
GIT binary patch
literal 11963
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
SL+hBD?8qJdf`7G
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
JwjhJ{{{b4Q^`rm*

literal 0
HcmV?d00001

diff --git a/venv/Lib/site-packages/setuptools/_distutils/__pycache__/cmd.cpython-312.pyc b/venv/Lib/site-packages/setuptools/_distutils/__pycache__/cmd.cpython-312.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..93f1f4f42c79f9048dec66e1134cf16502d0e4c9
GIT binary patch
literal 17511
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
JJgQS;`ad3VV>ti-

literal 0
HcmV?d00001

diff --git a/venv/Lib/site-packages/setuptools/_distutils/__pycache__/core.cpython-312.pyc b/venv/Lib/site-packages/setuptools/_distutils/__pycache__/core.cpython-312.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..37cc5c5b21fb4149f5779aee4b9f7988b35bd9c9
GIT binary patch
literal 9045
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

literal 0
HcmV?d00001

diff --git a/venv/Lib/site-packages/setuptools/_distutils/__pycache__/cygwinccompiler.cpython-312.pyc b/venv/Lib/site-packages/setuptools/_distutils/__pycache__/cygwinccompiler.cpython-312.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..f5d0c93659853f471923d8573803e406b3513ba1
GIT binary patch
literal 12011
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

literal 0
HcmV?d00001

diff --git a/venv/Lib/site-packages/setuptools/_distutils/__pycache__/debug.cpython-312.pyc b/venv/Lib/site-packages/setuptools/_distutils/__pycache__/debug.cpython-312.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..45edd37bc42c2de4f6be94d04c8fcdc75f647e39
GIT binary patch
literal 334
zcmX@j%ge<81Vs;Z)4GB5V-N=hn4yf%PC&+Vh7^Vr#vFzyhE#?uCYTZgoyCe^!&oUy
zs~I8eDCSB=P3D&%j^8bQ7ti33&=60b;CL5Tr%-oImRn5u#kbf~^U5-d^7C#nr>BXxS_6=#;D#*_h#jPc1#iUIL-3lfvF6Vp?RV~SHtN()Nz^K**z<5Mz=OG-;Jfy|WD
zq|$V~g34bUHo5sJr8%i~MchEUK|Uy!1ri^a85tRGGjM+3VBqBL=j!C@=j-IVAg*zl
ML$i^!h!dy+0F9tlc>n+a

literal 0
HcmV?d00001

diff --git a/venv/Lib/site-packages/setuptools/_distutils/__pycache__/dep_util.cpython-312.pyc b/venv/Lib/site-packages/setuptools/_distutils/__pycache__/dep_util.cpython-312.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..fe39a5357385ea352d45af3c910efaad2bd0af45
GIT binary patch
literal 728
zcmYLHPiqrF6rag%l5LVkZA3vtmexxSNzWErkXGFINbP_|fXVqJ;d^K`w=b
zGrxz<31NiMfOH5eFe9M-LZ?6pGe3}yaYUQNnJ(U&AqHvEbjj^UjCVOOUuSYDqGRAU
ze}n3T93$d@imLiwb4<<_{(Gu)WhRGa?p;|{P^CO##uxK!QB8?j%JVAYrX$2%uz5zB
z#l5DH7DE_8Ww0;@H9v4F{VYI~{b`!+|n@f8KVgbIVfVJ%x66|gg`ie)P
zw7Vke+Y(d~t0)Sj%|ofcW9^wf5|>et%JW|bFo2+0O6!l568s=h@K}gQq}I?AArE^p
zHMN(nZLF!G}7vc7XgYyWPX=f+$lRkt>I@@>yVb9s;
zzDfkN_LI>lfYuNWBv;@JARIVP`GKROR*VCBJ&?{7jdhoCsWf}Jp6{U@AEi~-T}0;PLz9w>Wd{A3hz6#_4OAD_Lwr+709)b~hMK&*j_S8TDg92a
O{UW#LC5ztAI{pDrf4`pq

literal 0
HcmV?d00001

diff --git a/venv/Lib/site-packages/setuptools/_distutils/__pycache__/dir_util.cpython-312.pyc b/venv/Lib/site-packages/setuptools/_distutils/__pycache__/dir_util.cpython-312.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..d1773a7ef16cbe7c5e422b9a4b560a5788bb79ca
GIT binary patch
literal 9623
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

literal 0
HcmV?d00001

diff --git a/venv/Lib/site-packages/setuptools/_distutils/__pycache__/dist.cpython-312.pyc b/venv/Lib/site-packages/setuptools/_distutils/__pycache__/dist.cpython-312.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..4a398d58037c70fed931a143505cd50acd0c03e6
GIT binary patch
literal 50509
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

literal 0
HcmV?d00001

diff --git a/venv/Lib/site-packages/setuptools/_distutils/__pycache__/errors.cpython-312.pyc b/venv/Lib/site-packages/setuptools/_distutils/__pycache__/errors.cpython-312.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..4e5668f63914738db46537849d4307573903ed62
GIT binary patch
literal 5766
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

literal 0
HcmV?d00001

diff --git a/venv/Lib/site-packages/setuptools/_distutils/__pycache__/filelist.cpython-312.pyc b/venv/Lib/site-packages/setuptools/_distutils/__pycache__/filelist.cpython-312.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..96c4ad5989ec6b3bb102b382c2de4337afd84e29
GIT binary patch
literal 15771
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
R^!U=7E7|4jQv%Sl{y!v{2XFuY

literal 0
HcmV?d00001

diff --git a/venv/Lib/site-packages/setuptools/_distutils/__pycache__/msvc9compiler.cpython-312.pyc b/venv/Lib/site-packages/setuptools/_distutils/__pycache__/msvc9compiler.cpython-312.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..f3f3d8273a4790c8454219012b6c5dd3ec10369b
GIT binary patch
literal 30744
zcmdtL32+ZVjuQKSy)pkz@cmDwt@DJDdb;^hP=iZEDa
z)p){WThk)#_K*r!Z?U=^TdcNYEl+r+HOEF**`DcgId+>6q#-JsGeb|ix5u#&v((Y<
zT6wSUe~CWo7S%LEDhM%Rc1jatt}UoI|cI*O0r*&BBa>c|)Eq56@}1uzsLMsZ{1Jn8Nwr
zL(S;oxv*iNPJYgIyZrFqp6w$)cNM^YR}TLo`0vZ%UjqMw&-=soP)^@hbNU{6-uJ4m
zt30e5IQD#qV7`u4-{eInTBjR0kt2rXdK<@eRfVfWqhRgYD%iTJ1$$S`ut9L(&51Wx
zSB>Bn@&wO2ny!$BJI)E-uW^FfZ(X&_&j&xh(wAM^guvIht~#LrZoNKO703>Acw5n)a>>_6!I6heij(Ltz9*`$vX@BUgjbYvEwel}J?V
z>5T@}$id#ho=C)Ubwp$l&h+<+BaxA-(cs1Y$XL%{uroR)^p6Cawr}6pV6j|49z!F-
z*kCx=9|=-Dko^_BjrT{d1uu3|yxk~XNI&mu2!3&7EZB<*UG0bOV7RZRcOn=W8yy`H
zqpZ}CK_M&#Z-m7N>KUPmBAfo<;Knv9O>VIKih8HxSLhWk#7o-2k-m^dGOO*8oPFWp
z?!o>m-6Nya@<>Q8+0s38R1`-+BJw+WNxL?$LD2*qsf7LLDQ7N5yb&Pc$sBzK9?u9-||{E8(CR9vZoUH#Ct(#-^|wwhh8x`c8>
zQxj|~KbRxxhB77X&3$_$eb11vXV(WB(T*7vGo!=mnNKgxGv!h`@X0m!%yVH5%Qr_$
zH*_nAvTf3a3AKiIMJ)rl`2Byhu)tI!p*(|hJ&Uw9Uw$ou^?|vU+eUiFXkCk3ZXFRY@1;+u{@hqBHW;oS4_}G&
zN5hwIgokfj#^-yP-s(qtdaw8Ng(H_E;po_CbYx^O(%7wjL5&-};D*r&$xL53t8dJX
zF=$X<&!AqB!*Hg!Rd2y#PHQfmZcDj5^UgWvqHa-0y0%TXNKV&m=R(UjUr+f13!b?f
z-?XnA5%H;sbNXi^-xyjp6+J-~+=j8z!Gn#}_wg{#nYn0sHgQ~>N6)6o^rW_ro74%K
zNxhlkWcpqk*Q;?1aovECO4xYC)w#%XalN38Va`wLZ&g-s(RA({SILQ5p8FyBd5MzL}(-qXNp_bY0ZJv{4I->OWI_9-Sn|_17~+m^C_Dhjw5efqqX~1
zy`>8`7QdYIhGuyRcicWPb7J-jw@x9}tb69zbj!M(b9z2|YUW&l#~g1CeCqM9YjOY0
zQwQfQhL_!!a^)kf%ROycH{!J>@201ifY1H8DaOdGyu5%*NzaF1%iv&e7z8E~1ib^dG7K&wO^63a#=|0`
zk!tMhS*jPep#CD3NRG`{nwJ-8!IktZrDRLz);)BkHxe>$nx6?5V%XvkVE`{5b+V>p<#mfvR9R&KxRyXQ_;9!R*h%<5*t
z3l}f}y@C1Wx#n*j#8OqWZKY;kqGsQ{{AA7H>6Yo<6-Q~pQMwRKIw}*ks?6ke{#KLQ
zBu{QDo7~i2Z`)c*xIb{U)Nvn`m|H^Hk7{^w>&z|twIA){;r6QQ8ltPyy9+O%P-~iCXmGMeehR*k<8VL?9Qm_73xUO5Nk`4J
zHf428AEsp_|5LAj!SJn9Yz?7xVEW6`rq7=0Id6~_i2Rhxn{v9aJm~TAEMh}mbEXpR
z56!jO5BOSbN5~?Xy1QxqbazXZ?rxd;#$0ZegUiyIU+p29lMr3Pa)O
zwGlxK!H3N))&|KIA(5b04*Y-<%P5#uPBBk$PU0cb4(Ds!r@Fn5b$WgABM0X$UT9Cc
zwobJ_);aa=N9EZb9(dGpp3;Tu$-LUBQ;&5?f7*
z%4Vy;5TzL_#z^TI<7Al7hYXVaObM+;YNfpE!M#i@HlcP
zL=s&jJa|ks%zh$d~fpZg?l@b)rY5bx2-eQ+2af0xwH38m2%tFVMGENM%MOW_|9XH2b4L6
z0Odl9xPk0c=>S0!!;_#@79B82xziK%%avaLTc$Te)qWO?nbOR(xS6hBM!yAKP_j8I
zrHT~r+^{vOPGG^JOxw44mZOd3xcdb;+F#HXN2XpcNa>6++>1I-aAjgoaDsbM6Qz}m
zT@W>ZyG&}qtU47x2pAcLRu}cC{yHH~nT?bBD1CD58t^MmnLFk29ZvAX_21$CP@Tiz
zT?~Sk<{oNI%;Tm@$ftRH>!c2I8AJo~TE4*c0%?g7S!S1D&Ge!|L=fMlgez?Ftq~Vzf$LCC
z;>bw!Z{%WwuS!le4uowgBrhUd&x(1YsbPB$&_C|n1Xlmy;CN3Ycx9}AFq(#Fo;b9ql85#rs4v-=%MGu2{2yTLy2^@#896&5#5tv3c0tEP>N9+$r8Z6HvN30gZ
zBhld1kujh^s8;kEV3Np0BpM!S2)17(+#-0b=SDc#I?)Fa9OwvZdswKmP!JUg**0Aj
z0y-EDat2SAb~e=ouS^6-0b@}>d3|qi2oK{so61R8W&$5Xx`sZv56fSbTxuU)lwqeB
z2&RfbN%k~?(J5S~aq#wzi6R8fbd|0$z$(^|16=^15md7d%42^{9(N`IJ>xHn$5D%z
zr?N(km##u6WEW2%Ol*TAnUEEuE+S2Qk!Gc2z0}@;F_*pb$$JYq<>U~p*t{AW*=o!R
zl=x95LrMHKwjwju^f}Q)MTbzsHZC98qT#S2S2l7af!mZt{Jzh%+A*pl>Zoz<>-
z{qtw$&Ojb)S=zbOb62}Gwq#Fw_ssIC-Fxm%{P6OuZr(a)T`t~n-_`WUNqK(uw1~-l
z2D4)|uZHqJ%|FrVJ&tuwZ+EO~;s2YbK`uaSp1b5zZ!ttecgeE|k;{(?YT8`fPfWJ9
z8qH5#`E6C&pVpY$cIkh*lZU&3r&0N18NtD#hV+}ST!dW_=CJ%?4qXPhn$!UA;Rh(k
zO&748%mT{aD$s!tb*4EUEu-y{9w^EXxE?VK+_N{FruE_;7V#%qwQ!C!8gtsc`ttKHlIMW89N}$T1c!8
zt`VQiNPY)^0n#vt`^jl0hoF7=1EN_h{w_JBR**SQ#^1h5ujJgqUxaAG*SN9ApL(fdu?rsolsWul7NVAmA;{U+oI+SgrV%uxpb+^9
zj7)}v$JIE*P)zb!EL;w@1G(r5bBg1FUN9)+RG9%$tJ0phM$ieyLbWX5K_KXa_obN-
zJ)R53ob~3qtY_yIcph|X-)vthBfxz?+_YRH;
z3~aX$0}6uEkdD<53{qC%9&xZAk<-2r=uQB$4~1EViqe6yVinxez_N)M&UCg0Z#4Ys
z|HkzzVj()7@j-@f{p$ZBFTiTtuV|!xMZ9R5@KnCahyX+wE$Sh!%R17EK#36^mh_R)
z!TzYE?H`UJiUA6QtNk|_e84_3NhgLQQOP#cGcY1vl*ty}`iIrG2vvkysW94Z0xgM>
z19_Dbh$5o=3;ady!U3XS;~c)JW2+`BvDImz!k07!r&>}*)AW(I$03`hOy*myvxaY+
znsH9GtUBDcug_e^cUe9?vf`*nI4Txfm)uFmwzrN?wM<1^KboAMW(HW$5(IvO2i#qaCDW*u
zs6Jr9OxJ|xZta&B%n%C=8njmi!g@KCJj+UQ3$2yH)+_?
z3t-Zaw{W(CWnJKN#`8lI^djjk
z16AjeR+Ukiu$hPu$^DkUd6nwdRVLOY?IjO{$n
zlzl;Uv=yxl#uOYaG!*^f$S`R$5FqyVK$F@K(u;#=y-3qnGO{s)Hj#!@(hZFC4?~}6
zQX)%6Fr%k|xl6|3@Hjolc%v0>7tR`3QAdh%R1llLrsen`*h!do2{9Oa|AX_LUhd`)xWGj({({U?Jv(ae-HR
zVC_P=T}5F)FrdDrEGf5(o%^@9o;}lX;q2MdowSD1+L>m|tW^;ea}hhxr?INnhG|(<
z>yuR%zzj;^9WmF%PFOo!&Eza;F1E&ss!)kYkf4hmTD$uNSZKhsBRV2Z#DYiVvV)mO
z02|0;qkovijd?movK!CPkC^>Nb|*e7-c-R2p&_T(idJNa_B7W-`b;Ig5WX_j$LNI2
z0OlNCt|{!ffkRKsYG$bw-1Lli|a^hap$LI%c$GR%Q>mt^c*0&fb!=Uu(-*Gh2j<_Hdt>p?-D8Q0
z=6k1;`R&t|N8ks1AUBupTzdD7J8vvCKPcIg@>DGu6Q0It(<(>`bXW6-<_;~^EVU&)
zJEl#)u)-GWUE>|&yY@Tw#cK}&jjIJk@A~if-z~mVym;+Lnm;xF(EP8O{%rq0-~XYI
zEI2iNQVB1+Q?^u?EZ7AfPp$dHE}Lh}%X!rgOf@N|XVsNAZ=5sE+vaQwT}x$2*Z!&Y
zRipXq<8O_B{mr-Dob7*L45plcWm8~PXZ(8mTkXq^x(B-Yl-2p!(?-t5$ZDaQ>HY_%
z=2U*cH!MU_3!g=3vHV8Cv1;z$R0oe+wSR8m;ckEkOo+iqX*uS_MirW}h)im(L0tV~
zeo{+X;<#qY8l@EM0^XfoYTo8E<^%&opf_ED4u%M-O75l0ik{)KhByzD*7%3|DAki)
z1L|_B_;B3khkoCr;LBw85Z4c=NOC3z1@K^kF0|Z;m4nbIv0xbpDM2jE^h4Y@pyJS(
zTr+5Trk9{S|J;~_LFZx+GMF;B(rGX`rYPxr*)>3r^`_s@)SS$5b6m?zelEo=1Eh}GbVY-T$3VIAl({SY
z1qi?*Xaq+gH))AhDdBNdO96nu%rN=i)4`wxqqbFvlerI&5_8j~)RRnE25L72;`Q^>
zL`k8`uDHr_W%;S!1e+z>qK!)I=nnO1r}6}2o88KD=C0IF>4(ktw|SBhD*l_bSS9Gr
z*x}PXW5d1Ig6hJ}$T^v9lH~!TvY=!@y!+AyX8qAfwzP7H5g;wBEM>}LOam>F5ne=K
zI>fJ`BVyI9pxy+^S8pt-DpDP+Nn;YPK$ZB6FOO-;gE2#JE8+Ffo*E)iVPq`I?qN}s
z4Tz}2pYfJw{3{G=5wGAqRK$o5gUA_O6#pT7B@2*7kow+mBqHh0jlh^l(w%ERcND7e
zk)fe6==&jiOSBuB0WKH8fJ!0=Q@%Je@)I8BJVLRw2$$*Ek
z!6-y`sxm8p&IFUt;{{*o}7ZmvsBgemVl`f1Y
zUDZ?VQr_OFQz^T1>e!mmwQLNmIx3f|T9b~pWmB6MXeZ)tRrRZ%>UnyJ70+y2Yz6Nfnf?6x1dPYLf+ZGmdG)
ztRZD}-EN*~{>H(D9rvxpz|5C?KPdU2Yh6D8O+B%FVBaj;%5<}
zGaoe_*>^m^{dC(dxPKnlbHbtRg|~Vsl>EuB;q`TJ02|FyyL9~=b`xm(_%1^qC+Pl&
z?}EuG-b{i4t~qQ78)HZrHZj*sZkI(cc3A~emrXEt*#%3NL$KcDyPRDv*vGqtqG|3b
zFWA3k?#dI21qbZeJwl1##NA6qiJ5;wso;WdzL|sFyBklwOjza#Om!Z@{DKE|9|5wi
z?jE4t0Ez|VMp{M^KvY}vj5$3k
zqyd&iFwc)^8hS(8n6{y}H>PXo9UUe2=FMl?2C)~uL*$Fk9PY@K0k~K);R=I;F`zSOdoqwVNXGWFqM6S5s0=UR+d8nnr%FOEMx|zJqE*NrkjDi%+d3m?PohAUw8IM
zbt~Akl9_DpnNiepsc2!PHMKw5FQcx&(~hBm%o67cR%*#8V2u>V
zfVjiXzc(zIySv%&b$6>!*_3P$bpq*P`PsEqEp*PK
zcB~czVW;Cr`Sy{y&YHgfH``i4iR@WOo~a^4d1y*wplP6hjROr24cL?ah@oK^DD&Rv
zh{lr`OohV$Cz0D8MROfFi-8PRFOb1^c4_Hsv*8is#!p!?$|$9jNvN3!Q6(HzbY$hp
z`20Uq(a*Pe!H~tx=YVi-KwT#oUm9l*>PniFO_!i@#bT9U!oQ*-t-6)bqmvgg2?
zS{HEBTsP5BcFpp`d$ZMVN{a>iRSoKDiK{I1;W5!VRrWz{F3Rom`&)1k501F*-y*J?
z;%4#k+~@KCLLO5KX3s(oMm7hRziAe}Wc1NSb>v?#Q&bQSan*i%LEMZyk~M2y5LboU
zkgqzYUJ%!oH74qe1Guf$CnqHL!tocpN`8nzQ$FNk17-5jv|flD4uWo}9TT
zWo6GN!8@QzLz%gkyB13Nm$jDW6C*j9FO3@@zI@XqZt0UzW0BO+#nN(A`$nUDHy
z4d=lx@La5djj<4?kojc3#GEUnP{ge0%!psi;;WLgP|`;=A-fKC38i>bUSun?TIE%>
z8v7qrX{@XxRv?TFR|7i*(FzWM{qM)tyR--H_ovEml-tPxP^#OoNf1Y|%9`jLl?MgOaOIRlKxC5Or2te
z-a+C#VkU?h*Vz+EbL_z&_R9Amc4u3qB)(fV@ti}M;y1}5!V9K=4ttmYE6EMG9JcWV+?2RkI0>tHWX`k#vc#~T@UG>85@U3urYAt
znYQt46Pz<`a4zk9W@>EfXghl3wc`LjKSERr7oO=5E{H#s-@EowtnTbs;*Sv|{)8MB
zN!NJ1vtInyc!wDZWh4F@a{gS2AbvtmACvPlI3X=cXghtHMU6Qd+dEp%A3bxl<3h{n
z=8m(!qBRWGG(V^4l-Z^j!y_?6BdZMyy$~uB|CFNs6LN^Ui3Fy}lIhp+B$*f_i-D9h
zy}gow1`{k84U(i0dnL_KujDv)y5&Omg|pokTQ9bp?-c)pa`+)RgXAy)_eb<}9nN$7
zS{gxTDoNQqZGz&)DQ+Ij7Sdw3vj0;2XB6N}51-;IK85=k8w?*Dmc(6z`rhKQ(dr72iGh
z$A=Q$rn{X9@4kO^=;Ki0z=eeO!n&4o7b4tO_1#NL{mJ}&)5q3|wk&SFb7-ZgF;UbA
zoZa$6OS0$yN;;hiZe1LE&wlI3?UOSnXU7t@l2j#?QMo(qHIcAw`6Yz!UGoR$4kn$!
z#g_Zds#Razx<-?KoL_4{^B1K*D}AKb;g(^%8sp9N(+XGwhvBp^I
zm~MM$+xpncRld%rJx%2KfO(c3nwDwsEi2_pFM@T
zMc#wv{?z6oV>!1B9sGHHi{?~0_w#b|sRr%O>v^~vBuipKn6Tl33t>Y$g*~nE4ns1X
znC0KVw+%yVM3b*v!b46|8dZO(!0|81LuKWZlE93ro5=*W-;`kWm*k?d`#E#r06`n#
zMzAU>)0ThDc
zP@Z7sLYwzgwSwXgx`IbV0idvDU;`hs{!rPjOnlVp(EnGpg8uoxcG)VUm}z0|9zof0
zm~)WX+Zcy-eRyO%cnyExTGI3j%HNP9O9^XYW_;js06~*MpCI<%z}CP1
z2vo0W2
z!C?Ts;cn9OQvRTb4AsWIXHl{MT^}8bvS%|Tmt&ZyhV&@gi)&HY2rUw6oH@OP$Att?
zf@gA$m29A!n}h#?sAn4-7>|Jl`j)N0hl{39ro6ssb1G2tgmalM^3x|)3ya^azEizY
z2n}rQ(!ONju9;KQ7iM>+b+s#=D%c7x3Q5nlrIv)J0RXVapQQ*El7Vf@f%;_@LeTI^
zVSNJso%L9{{T0gap?9UFGlBojoyjd19=cvjxxEXzmB6+H
z{=2vRR;m82>$SDtCU?YJ-$LE9UJe=#C9-TwmA%rbtN6m_Kt>T4)&*MYH(lRLzq8Fw
zDjJxHM7SbUbbQ=|?Y{=ue-f&}%ZNLvowQ&(v31fGH{9eSI1d0uclNkFmw_GNDog;n
zNMR=yBUW}B!YxKu?{t)=atqH1)EWxz}4Gs7@wk2aB0w-9nz*
zs`oM=$BA
z>P<=Tn$=U@zej3^QnN`Y5?y<6+O|6%6tGJ{@bRb8|~yQk!kh=lPNps#-ad
zGShmc9c{LZLAYRLFG55ABxn%B`dv-Pw@(gri4zf*`ak)+E3V~XrK!q)AXE#ap
z>;inkc6KB5ZxjFp)^5RcFr8iDn)GC9ANMFdjv8~5o;N+?*{53kH%%9^cEmJ|&;T`F
zV9qF>u8zbs_1EwxivI)J5G#B!n-S9y(}6U{MnVNL1n}>W1groQ=3}GX3OqvJtYlz3
zh9Lf1yogxyI9X07c2dS{eve|=yEiZq&Ti^4TMQ#+m^i;-w-)IuwTJxx1{2H>N8Y
z^KM{_(!T$R_@RH#&;;yjb9q;sW%r$Bw2AHP%-IK!oc%>B{@R4UHf8fI7#3|yeRsz{
zbO1;QSf*{@pbi|zMvA>qsv8$dm;86D*8G+C{k2OP?3^%pS4>3-Q_(`(1JjmIt*(?U
zuwpAq*vb}lE5YVOu=%0wz*@H7u0(Lx1KaL3PvH~pu(@Wsl};@vfPQMxbEkREG^?HM
ze1sEB@`;5inD3kGn;)4QS?o>vYG<`;GP6{Z@YN)oHKajry>sfD<}^1{mk87)+;yA1
z(@fG@aoManRZ_m#dFOX#Ewq2Gj`o9{oIAOwd*Aq;abaRjDIk`(s(%0c=$tYvau~$dUWpi
zY}>+?bhLK|?+h;Oxx4FL)ejFQOJAKmzPXg6cUwO=we(7&Y~Q`(_x2~s+8%h1J~E*l
zvBW6#I><%*7^@9eVc>{>x7MgUAQ>TL(0cKERt{3S||K3b6pV7}T;th}lWH%RR0e1QOnZvVQbdnAaU;y7gU1B|L1B@Gp
zEXu?fwu?Fu8z307V`Y&xI4C&>vkTI?iHJ^cnVt0m-VIr>7qFXZE$D~~kkV{Y>wI=d?LzajK;(M==qOo!!8W>PY_vW)QZK;bDg*;4
z246GU<-&4$vpX#;vPIrSCS^U)&nq%3jyxeX0S@#VzApYPD#MU$nG%tpkTLnfAv_EO
z{y*U>InvTCvSSpA5uSfYAy%R$*cUh019K~9I+|JBgE6=R|8-4l0CEHp2f5=Gk|x^`e0x4UGU=IwVIAKH$5_SBgEf-6^jr3;a7
z4K2GW*auwsjBS$rsIu%x5%=RF^O0TJA6HtBG-`j`sK;}!I#Z*QW>krJkrU>4-bnnOAx`^(t-@OOPsu&?)d)+gsH$uIPJI*hd)D
zsL)^>z$8F3{r7Z;fD6K(?yX?v%NfW4@#O;dAXH~IUDIlk&$8R#WN*x#<;8ywD+N`^
z%yqg2(~g$M%_51Tg&VSBFR;TU5Y#oi4dJTBGZRk@FG3!4GDwa*kQ&w#xl&KMApX1K
zCIt$Bh8ne<%!|8oSu$Yt$=le+w*p}KU9t!qcT*U)`oM+AqNRI!b@aTXueW5{qdZ}&
zv!1v%Wjp~`nBIE(CHbgAkdg~*9@IIXbCjzxF_kBbF;)_~lslWj-pTxz(T)6b
z2*+{NGJ?TLIfUVtv^v+=J=cT0Oc`^D$6T(Hia{AY&$SxX|C=v>TpqOt1M$FHRq+5D
zt%AuyY&+9J5H`gNNf4eaVzfC&4FqhC7p(Ducu}9K0wLN8TD)H=Z=hLu%G_a*q^<^O
zTHK_BGO^Yl-L1UG{mNWowAlZqKSPT_!~b6CLiUc>?)IQ;iNJ8~aoUX!nNt{%w=7q~
zW{!*!h~_aGO`=3*;
zS^N-xX)V`>(z90$(G&Ok~h~7kfcnfDR)OEhOXdTQzlJ||2t**jB>NcM*!gXj!}_}!6gg2
z5cOgV7h`ysgltS}+9hi`J6UcH-FipflF{TOq0}BL5
z=9ZzWDqp38p8#1MGB*`Vlt<;Oay-ba7TLUC#|a|Y8hP@%L=4p@aKKOnb4kxHV5ss7
zR`RQ%@Le+BJ-FQRS~CCQv?bNtMv`>F?CIt5J-@Ir85|qFQm*5xdHKt}+GO4~$XV{X
zr&_Ju@zlb3D;9Sx6)*2S_3;;zo=c>@heCgY9N2vKtI5*i(`U0-wTEEUoQ3c7uaq_=
zN}KLGn?CgfQl;f9rF#>ld++i0G|Qz2=8mtC&yGatj=Nj#mM@p?o;&`FtY}%`v&Yv8
zi>IOZuDb8vwuDXiynXkqHsvZ>ag`@r<%`XCwTX(I4{>r1q_Eu(1>J##8VdNGglqfV
zy$RSR^nL719J;jp%IgW&>(KJsy-)Kv=w4_C9qo|w-*tZ=?~+S7OO~CX)vYz}_r2G*
zvUN{l>z-AwZ~pk)@fB}n!dto6o%HSoFj1WcRrzdSVJ{@wvMnS#GoiToZggeu>BQdC
zAHSa5dofvhY1Rrm20H9#A+`j8e(wX`BT?(+;l~nn}%sa*`oGa!^^Hp#!XlK
z=4o+Sj1H|9;?#`qG%R~-n1mgA79ptgl6OH4^8`IgWZDhR(Tz)SzPl
zaB}VIWKx4x&`#=yJ+Sd6i_#oYQf8kV9T7CeBVQctb5#RHxxPc(fsTE~H_IEd`cM$g
zsjv`q8mSo(4@
zh*cQC=&b!y9N=t3To@vG131j$ozO~QL!z+ZzO?~k?aiNQV>hUxi-RjLtt#GqcPLrh
zHhm^#4J=!OP*A6w#S6l+vwYc9F3*JYD4{Z0$KRfY@8;8;l~JG6fZNpM8ujn7OxVoP
zi!<}
zRAshIqqAjin#oa2B>4gsmpMjV?spXfQ{q4_Rb>*$IoJ{L`F+oJdA=l0MxFJt+%hqv
zc}k5GyW)w=fyai9*lP!|^$ASq;6%GX2N*^t8fp&{9`;I2dAvHL?s>Z0aIy8W@Rg>?
z%MGpPFR(B~tZ6(HKiIfAheILpKcLiD?Lk=|gM3@kg&evZsXf^Eymzeq#q~JcOf{)#
z*mgKnBR^Bghnrtew#1L)KxCb~pOf~gii@ZW3|qsZ8AB);+RnnP_O$rBF8su%f?Z9MjP9#+%9cz%aC{cd@CCb%tzyHQX~`a1eYR{^
zeR8d8pVHD}KwK<6nrgF~Sgq29A?%AJY+q}yNR@@&{py{sCd;yXVdx;D1d^@nq4#>C-7+>-6!}qLNMC
zq+%#g^4a}dHm8eavvJwEmkvEBC|MX<>cnnEs-O-PR4TPt3(CrDcf@=fkPR
zcmj38iN)qdh@t0NE}Up)hXUf)KByKrvJE5rd;}v(jSxDB
z0}Y*p3!MpPaM=`meyOL*u?mv$NA@ud8OxZrd8+KeEFnB?Y)0pbq-8%GEXKrdY?P+i
z&CUkHxvC;Np>EqY0P#e-#bcOo8=6*_Lv~b}SiPZ%FOF!TrqZHA$DLQEY$w6>I4!Vk
zHd{WYy_KxJU@B0h%@=-WB<-u5H;KJv@CnHy#L)f?&2T6K0Pz$*?-MK9Q1y+0PO2{9
zXf!gO6=!9_S-IGmbcU8qA-TS~tojhSVdL{3@j$k)B901^`P#Uq50LXPju7kP!Ti87
z_Ki0{baA#Jo-X3)Wp=5#_-$gdRHB@zN5*1QVdo}|!~)A#PHsLIv!=q>U>VQHjr3bB
z%I~$@!WK|=khi4i7M^FY82|zc0&H53D5iuurly}m9b{d-8He%87K7q(jF#Ad5VrZB
zmeL#sgKoAdK^Yy2v`K67TzDRhT?E-qaZo?o+^K@%g(!A)cP$rU+6fQ>W7}%R8jdK
zbUlmpp(u$NVKJa*eOL-7@Q!
zdbBUehajp=SB8h(&R*8iu|AnbR3dWO}kVAnh{IEIb(a+eTjjQwbYAft>CbPU!J
z9JY$=hBIv%N@q8K-Dzr0>Ab08BVvCa&J#zk$eqc4#-%P;jc%@%kNToc!I2&W#D0i5
zjfLt*xNQat$j(Xp2033RhlEh&R|;U-98MpGd388KA1YOC!cq&FbHhq&d5?vvUL01illqb^pRDk`}X+E
zI7yrfR~`a;@)vy5Our0Oh=a27yz_-~g$u_L?ur$6DB%t*AyLEs`>Y}Ex;)b|ea25V
z5Vw=%c<~Ba%Y2Ei4;ec;6dN@
zPgcH-i1-8%#HZx^H93Dn&R>!9zmW5v;7Hz#YCU6(24A)zj6wgH+VX+*k~=Fdc%Y%4
zD7-Qe(_XMF3um%o#}SDCj>`Mr$RWW&^pev^4k1JgSP+T77f+CLlAP1zbdp2U1DXi=
z*nr+JknnEw$iIYd1i{>^@+A5`Wlqkk@QnN)c=+sV+#?OI-wDJZzi8?VXpJX->J;95
zg|o+|&Pcxcsk13x(d?zEj`R)St*>ISJCVQZ?wg7HqaQmG`49fSY!A^%Vw@cbi-M+o*wUxpg?X*_KQ-%l4JNwF2OGzPvuw(ivU_fS
zY355)7U1uypVfW);LO1(6Q!}P=<*Y~{FKhJqAO15irGU+LRX^ttef>_&6J+9_1uon
z#23aB&aL;Ihn9B!;J^n5?&T-89h$Ozs&(S)akla}x;F15PCIfQdh5iRV-LL^qSvOW
z6LkKyC*=-2(rfcHQ;xs09p)Pzad7^wO|y-6KjPqEO8a@&<6Sns;Bl#m_doUpdDoL;
hd^Ml<=pxTqi<6d;DdSVqO(XA_-T${7-PvUSe*ln@q*VX_

literal 0
HcmV?d00001

diff --git a/venv/Lib/site-packages/setuptools/_distutils/__pycache__/msvccompiler.cpython-312.pyc b/venv/Lib/site-packages/setuptools/_distutils/__pycache__/msvccompiler.cpython-312.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..4095a3891bed6915d87cd8bcfe2d8d7514e97db8
GIT binary patch
literal 24974
zcmdsfdvIIVncuy5KM8;U3BC#OVGV628mnGTnAM{-ZOrq$D1t
z)AskB`v6EmR<_NwGrbZI&OPTl=bp!RzSsHA`3tAh#^Div-hTe;U*NdEp%?WrDv<|&
zs^hpDoWKoo0x#&s_(7ii>IQY}S3js1^nzi`Fm4<)@*LvEG1Itt&^&G#w6J&6n04GX
zXdAZ=+Q%J(j&bLpbG&4*1bOB`msPISJ?IuJgRZc9&=Yo#HmQxs{|aOY+di0rsYLzJFsd{vCk@Pa8
zcN9tYBfaQ6MQfE-l<^$vOV{J21#7-9uX9nuW$udpb?!3%I(O}%8V^<^-xebA#AG5e
z7Vj93Um6~cjZZ|z!lKRA6N@H>BGGu@_`vC&9woUgFcb|$#wW(Y<6&ecBC%*7c0Q1}
z5DpBTizmdP;Y2_K4h)YC#pAZ~F_8frj|_{kcBnLtL7imPs*zkH;b>?qaxN5`pby4_M#-Tx=zu83M5(k_?Zbg9;bA$gTuVDS
zHk3dc#^rR&vB)_&W<3^(US!dOr;=YgGbBbM(UGn|&rq}_5fH;e0)i1#EM5&HVuACK
zsKB}r4G)Lo@gbzDV-<*p6A6^H9Ura5Xk5vVy~?tz%eOr3~K;2x*-?_&$OgSPuzh^>AE@OY!PjozjhP
zQ+h!+Wv~*AN3NE!23oH7Xdq?6;O4A+_(xrEjdtVTW0z0O_b
zRlMGiYoOeeet=W+gN9`7D`5;%G%$u4CKT4@`eOKMyrW;F+C}PI&>%Tp36GqZ2uF{G
zuO7G}S@0%fkd0!|$#JAhrlAQy2tku%IER6f^r%NNUBG0Gg(d!̕lV%&rZ1MGPu
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

literal 0
HcmV?d00001

diff --git a/venv/Lib/site-packages/setuptools/_distutils/__pycache__/spawn.cpython-312.pyc b/venv/Lib/site-packages/setuptools/_distutils/__pycache__/spawn.cpython-312.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..dd525fcd3a7900e7170fb022f93ae30f7648aac6
GIT binary patch
literal 4052
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

literal 0
HcmV?d00001

diff --git a/venv/Lib/site-packages/setuptools/_distutils/__pycache__/sysconfig.cpython-312.pyc b/venv/Lib/site-packages/setuptools/_distutils/__pycache__/sysconfig.cpython-312.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..3e9707c5835dcd85c287541c723f7ec2f563450f
GIT binary patch
literal 21297
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

literal 0
HcmV?d00001

diff --git a/venv/Lib/site-packages/setuptools/_distutils/__pycache__/text_file.cpython-312.pyc b/venv/Lib/site-packages/setuptools/_distutils/__pycache__/text_file.cpython-312.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..c75c053f582186df68bbe329b1e8159e2209128c
GIT binary patch
literal 10799
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
F{{z&O8UO$Q

literal 0
HcmV?d00001

diff --git a/venv/Lib/site-packages/setuptools/_distutils/__pycache__/unixccompiler.cpython-312.pyc b/venv/Lib/site-packages/setuptools/_distutils/__pycache__/unixccompiler.cpython-312.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..1a920acd7a4ef4357b0745bc0886abb9c9e437b8
GIT binary patch
literal 15236
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
D_CgPo

literal 0
HcmV?d00001

diff --git a/venv/Lib/site-packages/setuptools/_distutils/__pycache__/util.cpython-312.pyc b/venv/Lib/site-packages/setuptools/_distutils/__pycache__/util.cpython-312.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..dcae4ded6ee5f6a38e8451686c8737567c3a0bab
GIT binary patch
literal 18817
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
J#UQg6|36{DmRtY;

literal 0
HcmV?d00001

diff --git a/venv/Lib/site-packages/setuptools/_distutils/__pycache__/version.cpython-312.pyc b/venv/Lib/site-packages/setuptools/_distutils/__pycache__/version.cpython-312.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..fe843d331fdc6afbe4e4866081d646f0135fc0d6
GIT binary patch
literal 10686
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

literal 0
HcmV?d00001

diff --git a/venv/Lib/site-packages/setuptools/_distutils/__pycache__/versionpredicate.cpython-312.pyc b/venv/Lib/site-packages/setuptools/_distutils/__pycache__/versionpredicate.cpython-312.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..1ef76b4e7923b71dfd71623785c644f3938ed115
GIT binary patch
literal 6912
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

literal 0
HcmV?d00001

diff --git a/venv/Lib/site-packages/setuptools/_distutils/__pycache__/zosccompiler.cpython-312.pyc b/venv/Lib/site-packages/setuptools/_distutils/__pycache__/zosccompiler.cpython-312.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..ef0a192f485b368cddc7eeba2eb13d92d6062612
GIT binary patch
literal 6288
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

literal 0
HcmV?d00001

diff --git a/venv/Lib/site-packages/setuptools/_distutils/_collections.py b/venv/Lib/site-packages/setuptools/_distutils/_collections.py
new file mode 100644
index 0000000..d11a834
--- /dev/null
+++ b/venv/Lib/site-packages/setuptools/_distutils/_collections.py
@@ -0,0 +1,203 @@
+from __future__ import annotations
+
+import collections
+import functools
+import itertools
+import operator
+from collections.abc import Mapping
+from typing import Any
+
+
+# from jaraco.collections 3.5.1
+class DictStack(list, collections.abc.Mapping):
+    """
+    A stack of dictionaries that behaves as a view on those dictionaries,
+    giving preference to the last.
+
+    >>> stack = DictStack([dict(a=1, c=2), dict(b=2, a=2)])
+    >>> stack['a']
+    2
+    >>> stack['b']
+    2
+    >>> stack['c']
+    2
+    >>> len(stack)
+    3
+    >>> stack.push(dict(a=3))
+    >>> stack['a']
+    3
+    >>> set(stack.keys()) == set(['a', 'b', 'c'])
+    True
+    >>> set(stack.items()) == set([('a', 3), ('b', 2), ('c', 2)])
+    True
+    >>> dict(**stack) == dict(stack) == dict(a=3, c=2, b=2)
+    True
+    >>> d = stack.pop()
+    >>> stack['a']
+    2
+    >>> d = stack.pop()
+    >>> stack['a']
+    1
+    >>> stack.get('b', None)
+    >>> 'c' in stack
+    True
+    """
+
+    def __iter__(self):
+        dicts = list.__iter__(self)
+        return iter(set(itertools.chain.from_iterable(c.keys() for c in dicts)))
+
+    def __getitem__(self, key):
+        for scope in reversed(tuple(list.__iter__(self))):
+            if key in scope:
+                return scope[key]
+        raise KeyError(key)
+
+    push = list.append
+
+    def __contains__(self, other):
+        return collections.abc.Mapping.__contains__(self, other)
+
+    def __len__(self):
+        return len(list(iter(self)))
+
+
+# from jaraco.collections 5.0.1
+class RangeMap(dict):
+    """
+    A dictionary-like object that uses the keys as bounds for a range.
+    Inclusion of the value for that range is determined by the
+    key_match_comparator, which defaults to less-than-or-equal.
+    A value is returned for a key if it is the first key that matches in
+    the sorted list of keys.
+
+    One may supply keyword parameters to be passed to the sort function used
+    to sort keys (i.e. key, reverse) as sort_params.
+
+    Create a map that maps 1-3 -> 'a', 4-6 -> 'b'
+
+    >>> r = RangeMap({3: 'a', 6: 'b'})  # boy, that was easy
+    >>> r[1], r[2], r[3], r[4], r[5], r[6]
+    ('a', 'a', 'a', 'b', 'b', 'b')
+
+    Even float values should work so long as the comparison operator
+    supports it.
+
+    >>> r[4.5]
+    'b'
+
+    Notice that the way rangemap is defined, it must be open-ended
+    on one side.
+
+    >>> r[0]
+    'a'
+    >>> r[-1]
+    'a'
+
+    One can close the open-end of the RangeMap by using undefined_value
+
+    >>> r = RangeMap({0: RangeMap.undefined_value, 3: 'a', 6: 'b'})
+    >>> r[0]
+    Traceback (most recent call last):
+    ...
+    KeyError: 0
+
+    One can get the first or last elements in the range by using RangeMap.Item
+
+    >>> last_item = RangeMap.Item(-1)
+    >>> r[last_item]
+    'b'
+
+    .last_item is a shortcut for Item(-1)
+
+    >>> r[RangeMap.last_item]
+    'b'
+
+    Sometimes it's useful to find the bounds for a RangeMap
+
+    >>> r.bounds()
+    (0, 6)
+
+    RangeMap supports .get(key, default)
+
+    >>> r.get(0, 'not found')
+    'not found'
+
+    >>> r.get(7, 'not found')
+    'not found'
+
+    One often wishes to define the ranges by their left-most values,
+    which requires use of sort params and a key_match_comparator.
+
+    >>> r = RangeMap({1: 'a', 4: 'b'},
+    ...     sort_params=dict(reverse=True),
+    ...     key_match_comparator=operator.ge)
+    >>> r[1], r[2], r[3], r[4], r[5], r[6]
+    ('a', 'a', 'a', 'b', 'b', 'b')
+
+    That wasn't nearly as easy as before, so an alternate constructor
+    is provided:
+
+    >>> r = RangeMap.left({1: 'a', 4: 'b', 7: RangeMap.undefined_value})
+    >>> r[1], r[2], r[3], r[4], r[5], r[6]
+    ('a', 'a', 'a', 'b', 'b', 'b')
+
+    """
+
+    def __init__(
+        self,
+        source,
+        sort_params: Mapping[str, Any] = {},
+        key_match_comparator=operator.le,
+    ):
+        dict.__init__(self, source)
+        self.sort_params = sort_params
+        self.match = key_match_comparator
+
+    @classmethod
+    def left(cls, source):
+        return cls(
+            source, sort_params=dict(reverse=True), key_match_comparator=operator.ge
+        )
+
+    def __getitem__(self, item):
+        sorted_keys = sorted(self.keys(), **self.sort_params)
+        if isinstance(item, RangeMap.Item):
+            result = self.__getitem__(sorted_keys[item])
+        else:
+            key = self._find_first_match_(sorted_keys, item)
+            result = dict.__getitem__(self, key)
+            if result is RangeMap.undefined_value:
+                raise KeyError(key)
+        return result
+
+    def get(self, key, default=None):
+        """
+        Return the value for key if key is in the dictionary, else default.
+        If default is not given, it defaults to None, so that this method
+        never raises a KeyError.
+        """
+        try:
+            return self[key]
+        except KeyError:
+            return default
+
+    def _find_first_match_(self, keys, item):
+        is_match = functools.partial(self.match, item)
+        matches = list(filter(is_match, keys))
+        if matches:
+            return matches[0]
+        raise KeyError(item)
+
+    def bounds(self):
+        sorted_keys = sorted(self.keys(), **self.sort_params)
+        return (sorted_keys[RangeMap.first_item], sorted_keys[RangeMap.last_item])
+
+    # some special values for the RangeMap
+    undefined_value = type('RangeValueUndefined', (), {})()
+
+    class Item(int):
+        "RangeMap Item"
+
+    first_item = Item(0)
+    last_item = Item(-1)
diff --git a/venv/Lib/site-packages/setuptools/_distutils/_functools.py b/venv/Lib/site-packages/setuptools/_distutils/_functools.py
new file mode 100644
index 0000000..e03365e
--- /dev/null
+++ b/venv/Lib/site-packages/setuptools/_distutils/_functools.py
@@ -0,0 +1,73 @@
+import collections.abc
+import functools
+
+
+# from jaraco.functools 3.5
+def pass_none(func):
+    """
+    Wrap func so it's not called if its first param is None
+
+    >>> print_text = pass_none(print)
+    >>> print_text('text')
+    text
+    >>> print_text(None)
+    """
+
+    @functools.wraps(func)
+    def wrapper(param, *args, **kwargs):
+        if param is not None:
+            return func(param, *args, **kwargs)
+
+    return wrapper
+
+
+# from jaraco.functools 4.0
+@functools.singledispatch
+def _splat_inner(args, func):
+    """Splat args to func."""
+    return func(*args)
+
+
+@_splat_inner.register
+def _(args: collections.abc.Mapping, func):
+    """Splat kargs to func as kwargs."""
+    return func(**args)
+
+
+def splat(func):
+    """
+    Wrap func to expect its parameters to be passed positionally in a tuple.
+
+    Has a similar effect to that of ``itertools.starmap`` over
+    simple ``map``.
+
+    >>> import itertools, operator
+    >>> pairs = [(-1, 1), (0, 2)]
+    >>> _ = tuple(itertools.starmap(print, pairs))
+    -1 1
+    0 2
+    >>> _ = tuple(map(splat(print), pairs))
+    -1 1
+    0 2
+
+    The approach generalizes to other iterators that don't have a "star"
+    equivalent, such as a "starfilter".
+
+    >>> list(filter(splat(operator.add), pairs))
+    [(0, 2)]
+
+    Splat also accepts a mapping argument.
+
+    >>> def is_nice(msg, code):
+    ...     return "smile" in msg or code == 0
+    >>> msgs = [
+    ...     dict(msg='smile!', code=20),
+    ...     dict(msg='error :(', code=1),
+    ...     dict(msg='unknown', code=0),
+    ... ]
+    >>> for msg in filter(splat(is_nice), msgs):
+    ...     print(msg)
+    {'msg': 'smile!', 'code': 20}
+    {'msg': 'unknown', 'code': 0}
+    """
+    return functools.wraps(func)(functools.partial(_splat_inner, func=func))
diff --git a/venv/Lib/site-packages/setuptools/_distutils/_itertools.py b/venv/Lib/site-packages/setuptools/_distutils/_itertools.py
new file mode 100644
index 0000000..85b2951
--- /dev/null
+++ b/venv/Lib/site-packages/setuptools/_distutils/_itertools.py
@@ -0,0 +1,52 @@
+# from more_itertools 10.2
+def always_iterable(obj, base_type=(str, bytes)):
+    """If *obj* is iterable, return an iterator over its items::
+
+        >>> obj = (1, 2, 3)
+        >>> list(always_iterable(obj))
+        [1, 2, 3]
+
+    If *obj* is not iterable, return a one-item iterable containing *obj*::
+
+        >>> obj = 1
+        >>> list(always_iterable(obj))
+        [1]
+
+    If *obj* is ``None``, return an empty iterable:
+
+        >>> obj = None
+        >>> list(always_iterable(None))
+        []
+
+    By default, binary and text strings are not considered iterable::
+
+        >>> obj = 'foo'
+        >>> list(always_iterable(obj))
+        ['foo']
+
+    If *base_type* is set, objects for which ``isinstance(obj, base_type)``
+    returns ``True`` won't be considered iterable.
+
+        >>> obj = {'a': 1}
+        >>> list(always_iterable(obj))  # Iterate over the dict's keys
+        ['a']
+        >>> list(always_iterable(obj, base_type=dict))  # Treat dicts as a unit
+        [{'a': 1}]
+
+    Set *base_type* to ``None`` to avoid any special handling and treat objects
+    Python considers iterable as iterable:
+
+        >>> obj = 'foo'
+        >>> list(always_iterable(obj, base_type=None))
+        ['f', 'o', 'o']
+    """
+    if obj is None:
+        return iter(())
+
+    if (base_type is not None) and isinstance(obj, base_type):
+        return iter((obj,))
+
+    try:
+        return iter(obj)
+    except TypeError:
+        return iter((obj,))
diff --git a/venv/Lib/site-packages/setuptools/_distutils/_log.py b/venv/Lib/site-packages/setuptools/_distutils/_log.py
new file mode 100644
index 0000000..0148f15
--- /dev/null
+++ b/venv/Lib/site-packages/setuptools/_distutils/_log.py
@@ -0,0 +1,3 @@
+import logging
+
+log = logging.getLogger()
diff --git a/venv/Lib/site-packages/setuptools/_distutils/_macos_compat.py b/venv/Lib/site-packages/setuptools/_distutils/_macos_compat.py
new file mode 100644
index 0000000..76ecb96
--- /dev/null
+++ b/venv/Lib/site-packages/setuptools/_distutils/_macos_compat.py
@@ -0,0 +1,12 @@
+import importlib
+import sys
+
+
+def bypass_compiler_fixup(cmd, args):
+    return cmd
+
+
+if sys.platform == 'darwin':
+    compiler_fixup = importlib.import_module('_osx_support').compiler_fixup
+else:
+    compiler_fixup = bypass_compiler_fixup
diff --git a/venv/Lib/site-packages/setuptools/_distutils/_modified.py b/venv/Lib/site-packages/setuptools/_distutils/_modified.py
new file mode 100644
index 0000000..78485dc
--- /dev/null
+++ b/venv/Lib/site-packages/setuptools/_distutils/_modified.py
@@ -0,0 +1,72 @@
+"""Timestamp comparison of files and groups of files."""
+
+import functools
+import os.path
+
+from ._functools import splat
+from .errors import DistutilsFileError
+from .py39compat import zip_strict
+
+
+def _newer(source, target):
+    return not os.path.exists(target) or (
+        os.path.getmtime(source) > os.path.getmtime(target)
+    )
+
+
+def newer(source, target):
+    """
+    Is source modified more recently than target.
+
+    Returns True if 'source' is modified more recently than
+    'target' or if 'target' does not exist.
+
+    Raises DistutilsFileError if 'source' does not exist.
+    """
+    if not os.path.exists(source):
+        raise DistutilsFileError("file '%s' does not exist" % os.path.abspath(source))
+
+    return _newer(source, target)
+
+
+def newer_pairwise(sources, targets, newer=newer):
+    """
+    Filter filenames where sources are newer than targets.
+
+    Walk two filename iterables in parallel, testing if each source is newer
+    than its corresponding target.  Returns a pair of lists (sources,
+    targets) where source is newer than target, according to the semantics
+    of 'newer()'.
+    """
+    newer_pairs = filter(splat(newer), zip_strict(sources, targets))
+    return tuple(map(list, zip(*newer_pairs))) or ([], [])
+
+
+def newer_group(sources, target, missing='error'):
+    """
+    Is target out-of-date with respect to any file in sources.
+
+    Return True if 'target' is out-of-date with respect to any file
+    listed in 'sources'. In other words, if 'target' exists and is newer
+    than every file in 'sources', return False; otherwise return True.
+    ``missing`` controls how to handle a missing source file:
+
+    - error (default): allow the ``stat()`` call to fail.
+    - ignore: silently disregard any missing source files.
+    - newer: treat missing source files as "target out of date". This
+      mode is handy in "dry-run" mode: it will pretend to carry out
+      commands that wouldn't work because inputs are missing, but
+      that doesn't matter because dry-run won't run the commands.
+    """
+
+    def missing_as_newer(source):
+        return missing == 'newer' and not os.path.exists(source)
+
+    ignored = os.path.exists if missing == 'ignore' else None
+    return any(
+        missing_as_newer(source) or _newer(source, target)
+        for source in filter(ignored, sources)
+    )
+
+
+newer_pairwise_group = functools.partial(newer_pairwise, newer=newer_group)
diff --git a/venv/Lib/site-packages/setuptools/_distutils/_msvccompiler.py b/venv/Lib/site-packages/setuptools/_distutils/_msvccompiler.py
new file mode 100644
index 0000000..a2159fe
--- /dev/null
+++ b/venv/Lib/site-packages/setuptools/_distutils/_msvccompiler.py
@@ -0,0 +1,568 @@
+"""distutils._msvccompiler
+
+Contains MSVCCompiler, an implementation of the abstract CCompiler class
+for Microsoft Visual Studio 2015.
+
+The module is compatible with VS 2015 and later. You can find legacy support
+for older versions in distutils.msvc9compiler and distutils.msvccompiler.
+"""
+
+# Written by Perry Stoll
+# hacked by Robin Becker and Thomas Heller to do a better job of
+#   finding DevStudio (through the registry)
+# ported to VS 2005 and VS 2008 by Christian Heimes
+# ported to VS 2015 by Steve Dower
+
+import contextlib
+import os
+import subprocess
+import unittest.mock as mock
+import warnings
+
+with contextlib.suppress(ImportError):
+    import winreg
+
+from itertools import count
+
+from ._log import log
+from .ccompiler import CCompiler, gen_lib_options
+from .errors import (
+    CompileError,
+    DistutilsExecError,
+    DistutilsPlatformError,
+    LibError,
+    LinkError,
+)
+from .util import get_platform
+
+
+def _find_vc2015():
+    try:
+        key = winreg.OpenKeyEx(
+            winreg.HKEY_LOCAL_MACHINE,
+            r"Software\Microsoft\VisualStudio\SxS\VC7",
+            access=winreg.KEY_READ | winreg.KEY_WOW64_32KEY,
+        )
+    except OSError:
+        log.debug("Visual C++ is not registered")
+        return None, None
+
+    best_version = 0
+    best_dir = None
+    with key:
+        for i in count():
+            try:
+                v, vc_dir, vt = winreg.EnumValue(key, i)
+            except OSError:
+                break
+            if v and vt == winreg.REG_SZ and os.path.isdir(vc_dir):
+                try:
+                    version = int(float(v))
+                except (ValueError, TypeError):
+                    continue
+                if version >= 14 and version > best_version:
+                    best_version, best_dir = version, vc_dir
+    return best_version, best_dir
+
+
+def _find_vc2017():
+    """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 = os.environ.get("ProgramFiles(x86)") or os.environ.get("ProgramFiles")
+    if not root:
+        return None, None
+
+    try:
+        path = subprocess.check_output(
+            [
+                os.path.join(
+                    root, "Microsoft Visual Studio", "Installer", "vswhere.exe"
+                ),
+                "-latest",
+                "-prerelease",
+                "-requires",
+                "Microsoft.VisualStudio.Component.VC.Tools.x86.x64",
+                "-property",
+                "installationPath",
+                "-products",
+                "*",
+            ],
+            encoding="mbcs",
+            errors="strict",
+        ).strip()
+    except (subprocess.CalledProcessError, OSError, UnicodeDecodeError):
+        return None, None
+
+    path = os.path.join(path, "VC", "Auxiliary", "Build")
+    if os.path.isdir(path):
+        return 15, path
+
+    return None, None
+
+
+PLAT_SPEC_TO_RUNTIME = {
+    'x86': 'x86',
+    'x86_amd64': 'x64',
+    'x86_arm': 'arm',
+    'x86_arm64': 'arm64',
+}
+
+
+def _find_vcvarsall(plat_spec):
+    # bpo-38597: Removed vcruntime return value
+    _, best_dir = _find_vc2017()
+
+    if not best_dir:
+        best_version, best_dir = _find_vc2015()
+
+    if not best_dir:
+        log.debug("No suitable Visual C++ version found")
+        return None, None
+
+    vcvarsall = os.path.join(best_dir, "vcvarsall.bat")
+    if not os.path.isfile(vcvarsall):
+        log.debug("%s cannot be found", vcvarsall)
+        return None, None
+
+    return vcvarsall, None
+
+
+def _get_vc_env(plat_spec):
+    if os.getenv("DISTUTILS_USE_SDK"):
+        return {key.lower(): value for key, value in os.environ.items()}
+
+    vcvarsall, _ = _find_vcvarsall(plat_spec)
+    if not vcvarsall:
+        raise DistutilsPlatformError("Unable to find vcvarsall.bat")
+
+    try:
+        out = subprocess.check_output(
+            f'cmd /u /c "{vcvarsall}" {plat_spec} && set',
+            stderr=subprocess.STDOUT,
+        ).decode('utf-16le', errors='replace')
+    except subprocess.CalledProcessError as exc:
+        log.error(exc.output)
+        raise DistutilsPlatformError(f"Error executing {exc.cmd}")
+
+    env = {
+        key.lower(): value
+        for key, _, value in (line.partition('=') for line in out.splitlines())
+        if key and value
+    }
+
+    return env
+
+
+def _find_exe(exe, paths=None):
+    """Return path to an MSVC executable program.
+
+    Tries to find the program in several places: first, one of the
+    MSVC program search paths from the registry; next, the directories
+    in the PATH environment variable.  If any of those work, return an
+    absolute path that is known to exist.  If none of them work, just
+    return the original program name, 'exe'.
+    """
+    if not paths:
+        paths = os.getenv('path').split(os.pathsep)
+    for p in paths:
+        fn = os.path.join(os.path.abspath(p), exe)
+        if os.path.isfile(fn):
+            return fn
+    return exe
+
+
+# A map keyed by get_platform() return values to values accepted by
+# 'vcvarsall.bat'. Always cross-compile from x86 to work with the
+# lighter-weight MSVC installs that do not include native 64-bit tools.
+PLAT_TO_VCVARS = {
+    'win32': 'x86',
+    'win-amd64': 'x86_amd64',
+    'win-arm32': 'x86_arm',
+    'win-arm64': 'x86_arm64',
+}
+
+
+class MSVCCompiler(CCompiler):
+    """Concrete class that implements an interface to Microsoft Visual C++,
+    as defined by the CCompiler abstract class."""
+
+    compiler_type = 'msvc'
+
+    # Just set this so CCompiler's constructor doesn't barf.  We currently
+    # don't use the 'set_executables()' bureaucracy provided by CCompiler,
+    # as it really isn't necessary for this sort of single-compiler class.
+    # Would be nice to have a consistent interface with UnixCCompiler,
+    # though, so it's worth thinking about.
+    executables = {}
+
+    # Private class data (need to distinguish C from C++ source for compiler)
+    _c_extensions = ['.c']
+    _cpp_extensions = ['.cc', '.cpp', '.cxx']
+    _rc_extensions = ['.rc']
+    _mc_extensions = ['.mc']
+
+    # Needed for the filename generation methods provided by the
+    # base class, CCompiler.
+    src_extensions = _c_extensions + _cpp_extensions + _rc_extensions + _mc_extensions
+    res_extension = '.res'
+    obj_extension = '.obj'
+    static_lib_extension = '.lib'
+    shared_lib_extension = '.dll'
+    static_lib_format = shared_lib_format = '%s%s'
+    exe_extension = '.exe'
+
+    def __init__(self, verbose=0, dry_run=0, force=0):
+        super().__init__(verbose, dry_run, force)
+        # target platform (.plat_name is consistent with 'bdist')
+        self.plat_name = None
+        self.initialized = False
+
+    @classmethod
+    def _configure(cls, vc_env):
+        """
+        Set class-level include/lib dirs.
+        """
+        cls.include_dirs = cls._parse_path(vc_env.get('include', ''))
+        cls.library_dirs = cls._parse_path(vc_env.get('lib', ''))
+
+    @staticmethod
+    def _parse_path(val):
+        return [dir.rstrip(os.sep) for dir in val.split(os.pathsep) if dir]
+
+    def initialize(self, plat_name=None):
+        # multi-init means we would need to check platform same each time...
+        assert not self.initialized, "don't init multiple times"
+        if plat_name is None:
+            plat_name = get_platform()
+        # sanity check for platforms to prevent obscure errors later.
+        if plat_name not in PLAT_TO_VCVARS:
+            raise DistutilsPlatformError(
+                f"--plat-name must be one of {tuple(PLAT_TO_VCVARS)}"
+            )
+
+        # Get the vcvarsall.bat spec for the requested platform.
+        plat_spec = PLAT_TO_VCVARS[plat_name]
+
+        vc_env = _get_vc_env(plat_spec)
+        if not vc_env:
+            raise DistutilsPlatformError(
+                "Unable to find a compatible Visual Studio installation."
+            )
+        self._configure(vc_env)
+
+        self._paths = vc_env.get('path', '')
+        paths = self._paths.split(os.pathsep)
+        self.cc = _find_exe("cl.exe", paths)
+        self.linker = _find_exe("link.exe", paths)
+        self.lib = _find_exe("lib.exe", paths)
+        self.rc = _find_exe("rc.exe", paths)  # resource compiler
+        self.mc = _find_exe("mc.exe", paths)  # message compiler
+        self.mt = _find_exe("mt.exe", paths)  # message compiler
+
+        self.preprocess_options = None
+        # bpo-38597: Always compile with dynamic linking
+        # Future releases of Python 3.x will include all past
+        # versions of vcruntime*.dll for compatibility.
+        self.compile_options = ['/nologo', '/O2', '/W3', '/GL', '/DNDEBUG', '/MD']
+
+        self.compile_options_debug = [
+            '/nologo',
+            '/Od',
+            '/MDd',
+            '/Zi',
+            '/W3',
+            '/D_DEBUG',
+        ]
+
+        ldflags = ['/nologo', '/INCREMENTAL:NO', '/LTCG']
+
+        ldflags_debug = ['/nologo', '/INCREMENTAL:NO', '/LTCG', '/DEBUG:FULL']
+
+        self.ldflags_exe = [*ldflags, '/MANIFEST:EMBED,ID=1']
+        self.ldflags_exe_debug = [*ldflags_debug, '/MANIFEST:EMBED,ID=1']
+        self.ldflags_shared = [
+            *ldflags,
+            '/DLL',
+            '/MANIFEST:EMBED,ID=2',
+            '/MANIFESTUAC:NO',
+        ]
+        self.ldflags_shared_debug = [
+            *ldflags_debug,
+            '/DLL',
+            '/MANIFEST:EMBED,ID=2',
+            '/MANIFESTUAC:NO',
+        ]
+        self.ldflags_static = [*ldflags]
+        self.ldflags_static_debug = [*ldflags_debug]
+
+        self._ldflags = {
+            (CCompiler.EXECUTABLE, None): self.ldflags_exe,
+            (CCompiler.EXECUTABLE, False): self.ldflags_exe,
+            (CCompiler.EXECUTABLE, True): self.ldflags_exe_debug,
+            (CCompiler.SHARED_OBJECT, None): self.ldflags_shared,
+            (CCompiler.SHARED_OBJECT, False): self.ldflags_shared,
+            (CCompiler.SHARED_OBJECT, True): self.ldflags_shared_debug,
+            (CCompiler.SHARED_LIBRARY, None): self.ldflags_static,
+            (CCompiler.SHARED_LIBRARY, False): self.ldflags_static,
+            (CCompiler.SHARED_LIBRARY, True): self.ldflags_static_debug,
+        }
+
+        self.initialized = True
+
+    # -- Worker methods ------------------------------------------------
+
+    @property
+    def out_extensions(self):
+        return {
+            **super().out_extensions,
+            **{
+                ext: self.res_extension
+                for ext in self._rc_extensions + self._mc_extensions
+            },
+        }
+
+    def compile(  # noqa: C901
+        self,
+        sources,
+        output_dir=None,
+        macros=None,
+        include_dirs=None,
+        debug=0,
+        extra_preargs=None,
+        extra_postargs=None,
+        depends=None,
+    ):
+        if not self.initialized:
+            self.initialize()
+        compile_info = self._setup_compile(
+            output_dir, macros, include_dirs, sources, depends, extra_postargs
+        )
+        macros, objects, extra_postargs, pp_opts, build = compile_info
+
+        compile_opts = extra_preargs or []
+        compile_opts.append('/c')
+        if debug:
+            compile_opts.extend(self.compile_options_debug)
+        else:
+            compile_opts.extend(self.compile_options)
+
+        add_cpp_opts = False
+
+        for obj in objects:
+            try:
+                src, ext = build[obj]
+            except KeyError:
+                continue
+            if debug:
+                # pass the full pathname to MSVC in debug mode,
+                # this allows the debugger to find the source file
+                # without asking the user to browse for it
+                src = os.path.abspath(src)
+
+            if ext in self._c_extensions:
+                input_opt = "/Tc" + src
+            elif ext in self._cpp_extensions:
+                input_opt = "/Tp" + src
+                add_cpp_opts = True
+            elif ext in self._rc_extensions:
+                # compile .RC to .RES file
+                input_opt = src
+                output_opt = "/fo" + obj
+                try:
+                    self.spawn([self.rc] + pp_opts + [output_opt, input_opt])
+                except DistutilsExecError as msg:
+                    raise CompileError(msg)
+                continue
+            elif ext in self._mc_extensions:
+                # Compile .MC to .RC file to .RES file.
+                #   * '-h dir' specifies the directory for the
+                #     generated include file
+                #   * '-r dir' specifies the target directory of the
+                #     generated RC file and the binary message resource
+                #     it includes
+                #
+                # For now (since there are no options to change this),
+                # we use the source-directory for the include file and
+                # the build directory for the RC file and message
+                # resources. This works at least for win32all.
+                h_dir = os.path.dirname(src)
+                rc_dir = os.path.dirname(obj)
+                try:
+                    # first compile .MC to .RC and .H file
+                    self.spawn([self.mc, '-h', h_dir, '-r', rc_dir, src])
+                    base, _ = os.path.splitext(os.path.basename(src))
+                    rc_file = os.path.join(rc_dir, base + '.rc')
+                    # then compile .RC to .RES file
+                    self.spawn([self.rc, "/fo" + obj, rc_file])
+
+                except DistutilsExecError as msg:
+                    raise CompileError(msg)
+                continue
+            else:
+                # how to handle this file?
+                raise CompileError(f"Don't know how to compile {src} to {obj}")
+
+            args = [self.cc] + compile_opts + pp_opts
+            if add_cpp_opts:
+                args.append('/EHsc')
+            args.extend((input_opt, "/Fo" + obj))
+            args.extend(extra_postargs)
+
+            try:
+                self.spawn(args)
+            except DistutilsExecError as msg:
+                raise CompileError(msg)
+
+        return objects
+
+    def create_static_lib(
+        self, objects, output_libname, output_dir=None, debug=0, target_lang=None
+    ):
+        if not self.initialized:
+            self.initialize()
+        objects, output_dir = self._fix_object_args(objects, output_dir)
+        output_filename = self.library_filename(output_libname, output_dir=output_dir)
+
+        if self._need_link(objects, output_filename):
+            lib_args = objects + ['/OUT:' + output_filename]
+            if debug:
+                pass  # XXX what goes here?
+            try:
+                log.debug('Executing "%s" %s', self.lib, ' '.join(lib_args))
+                self.spawn([self.lib] + lib_args)
+            except DistutilsExecError as msg:
+                raise LibError(msg)
+        else:
+            log.debug("skipping %s (up-to-date)", output_filename)
+
+    def link(
+        self,
+        target_desc,
+        objects,
+        output_filename,
+        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,
+    ):
+        if not self.initialized:
+            self.initialize()
+        objects, output_dir = self._fix_object_args(objects, output_dir)
+        fixed_args = self._fix_lib_args(libraries, library_dirs, runtime_library_dirs)
+        libraries, library_dirs, runtime_library_dirs = fixed_args
+
+        if runtime_library_dirs:
+            self.warn(
+                "I don't know what to do with 'runtime_library_dirs': "
+                + str(runtime_library_dirs)
+            )
+
+        lib_opts = gen_lib_options(self, library_dirs, runtime_library_dirs, libraries)
+        if output_dir is not None:
+            output_filename = os.path.join(output_dir, output_filename)
+
+        if self._need_link(objects, output_filename):
+            ldflags = self._ldflags[target_desc, debug]
+
+            export_opts = ["/EXPORT:" + sym for sym in (export_symbols or [])]
+
+            ld_args = (
+                ldflags + lib_opts + export_opts + objects + ['/OUT:' + output_filename]
+            )
+
+            # The MSVC linker generates .lib and .exp files, which cannot be
+            # suppressed by any linker switches. The .lib files may even be
+            # needed! Make sure they are generated in the temporary build
+            # directory. Since they have different names for debug and release
+            # builds, they can go into the same directory.
+            build_temp = os.path.dirname(objects[0])
+            if export_symbols is not None:
+                (dll_name, dll_ext) = os.path.splitext(
+                    os.path.basename(output_filename)
+                )
+                implib_file = os.path.join(build_temp, self.library_filename(dll_name))
+                ld_args.append('/IMPLIB:' + implib_file)
+
+            if extra_preargs:
+                ld_args[:0] = extra_preargs
+            if extra_postargs:
+                ld_args.extend(extra_postargs)
+
+            output_dir = os.path.dirname(os.path.abspath(output_filename))
+            self.mkpath(output_dir)
+            try:
+                log.debug('Executing "%s" %s', self.linker, ' '.join(ld_args))
+                self.spawn([self.linker] + ld_args)
+            except DistutilsExecError as msg:
+                raise LinkError(msg)
+        else:
+            log.debug("skipping %s (up-to-date)", output_filename)
+
+    def spawn(self, cmd):
+        env = dict(os.environ, PATH=self._paths)
+        with self._fallback_spawn(cmd, env) as fallback:
+            return super().spawn(cmd, env=env)
+        return fallback.value
+
+    @contextlib.contextmanager
+    def _fallback_spawn(self, cmd, env):
+        """
+        Discovered in pypa/distutils#15, some tools monkeypatch the compiler,
+        so the 'env' kwarg causes a TypeError. Detect this condition and
+        restore the legacy, unsafe behavior.
+        """
+        bag = type('Bag', (), {})()
+        try:
+            yield bag
+        except TypeError as exc:
+            if "unexpected keyword argument 'env'" not in str(exc):
+                raise
+        else:
+            return
+        warnings.warn("Fallback spawn triggered. Please update distutils monkeypatch.")
+        with mock.patch.dict('os.environ', env):
+            bag.value = super().spawn(cmd)
+
+    # -- Miscellaneous methods -----------------------------------------
+    # These are all used by the 'gen_lib_options() function, in
+    # ccompiler.py.
+
+    def library_dir_option(self, dir):
+        return "/LIBPATH:" + dir
+
+    def runtime_library_dir_option(self, dir):
+        raise DistutilsPlatformError(
+            "don't know how to set runtime library search path for MSVC"
+        )
+
+    def library_option(self, lib):
+        return self.library_filename(lib)
+
+    def find_library_file(self, dirs, lib, debug=0):
+        # Prefer a debugging library if found (and requested), but deal
+        # with it if we don't have one.
+        if debug:
+            try_names = [lib + "_d", lib]
+        else:
+            try_names = [lib]
+        for dir in dirs:
+            for name in try_names:
+                libfile = os.path.join(dir, self.library_filename(name))
+                if os.path.isfile(libfile):
+                    return libfile
+        else:
+            # Oops, didn't find it in *any* of 'dirs'
+            return None
diff --git a/venv/Lib/site-packages/setuptools/_distutils/archive_util.py b/venv/Lib/site-packages/setuptools/_distutils/archive_util.py
new file mode 100644
index 0000000..052f6e4
--- /dev/null
+++ b/venv/Lib/site-packages/setuptools/_distutils/archive_util.py
@@ -0,0 +1,280 @@
+"""distutils.archive_util
+
+Utility functions for creating archive files (tarballs, zip files,
+that sort of thing)."""
+
+import os
+import sys
+from warnings import warn
+
+try:
+    import zipfile
+except ImportError:
+    zipfile = None
+
+
+from ._log import log
+from .dir_util import mkpath
+from .errors import DistutilsExecError
+from .spawn import spawn
+
+try:
+    from pwd import getpwnam
+except ImportError:
+    getpwnam = None
+
+try:
+    from grp import getgrnam
+except ImportError:
+    getgrnam = None
+
+
+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
+):
+    """Create a (possibly compressed) tar file from all the files under
+    'base_dir'.
+
+    'compress' must be "gzip" (the default), "bzip2", "xz", "compress", or
+    None.  ("compress" will be deprecated in Python 3.2)
+
+    '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_dir' +  ".tar", possibly plus
+    the appropriate compression extension (".gz", ".bz2", ".xz" or ".Z").
+
+    Returns the output filename.
+    """
+    tar_compression = {
+        'gzip': 'gz',
+        'bzip2': 'bz2',
+        'xz': 'xz',
+        None: '',
+        'compress': '',
+    }
+    compress_ext = {'gzip': '.gz', 'bzip2': '.bz2', 'xz': '.xz', 'compress': '.Z'}
+
+    # flags for compression program, each element of list will be an argument
+    if compress is not None and compress not in compress_ext.keys():
+        raise ValueError(
+            "bad value for 'compress': must be None, 'gzip', 'bzip2', "
+            "'xz' or 'compress'"
+        )
+
+    archive_name = base_name + '.tar'
+    if compress != 'compress':
+        archive_name += compress_ext.get(compress, '')
+
+    mkpath(os.path.dirname(archive_name), dry_run=dry_run)
+
+    # creating the tarball
+    import tarfile  # late import so Python build itself doesn't break
+
+    log.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()
+
+    # compression using `compress`
+    if compress == 'compress':
+        warn("'compress' is deprecated.", DeprecationWarning)
+        # the option varies depending on the platform
+        compressed_name = archive_name + compress_ext[compress]
+        if sys.platform == 'win32':
+            cmd = [compress, archive_name, compressed_name]
+        else:
+            cmd = [compress, '-f', archive_name]
+        spawn(cmd, dry_run=dry_run)
+        return compressed_name
+
+    return archive_name
+
+
+def make_zipfile(base_name, base_dir, verbose=0, dry_run=0):  # noqa: C901
+    """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 DistutilsExecError.  Returns the name of the output zip
+    file.
+    """
+    zip_filename = base_name + ".zip"
+    mkpath(os.path.dirname(zip_filename), dry_run=dry_run)
+
+    # If zipfile module is not available, try spawning an external
+    # 'zip' command.
+    if zipfile is None:
+        if verbose:
+            zipoptions = "-r"
+        else:
+            zipoptions = "-rq"
+
+        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 DistutilsExecError(
+                (
+                    "unable to create zip file '%s': "
+                    "could neither import the 'zipfile' module nor "
+                    "find a standalone zip utility"
+                )
+                % zip_filename
+            )
+
+    else:
+        log.info("creating '%s' and adding '%s' to it", zip_filename, base_dir)
+
+        if not dry_run:
+            try:
+                zip = zipfile.ZipFile(
+                    zip_filename, "w", compression=zipfile.ZIP_DEFLATED
+                )
+            except RuntimeError:
+                zip = zipfile.ZipFile(zip_filename, "w", compression=zipfile.ZIP_STORED)
+
+            with zip:
+                if base_dir != os.curdir:
+                    path = os.path.normpath(os.path.join(base_dir, ''))
+                    zip.write(path, path)
+                    log.info("adding '%s'", path)
+                for dirpath, dirnames, filenames in os.walk(base_dir):
+                    for name in dirnames:
+                        path = os.path.normpath(os.path.join(dirpath, name, ''))
+                        zip.write(path, path)
+                        log.info("adding '%s'", path)
+                    for name in filenames:
+                        path = os.path.normpath(os.path.join(dirpath, name))
+                        if os.path.isfile(path):
+                            zip.write(path, path)
+                            log.info("adding '%s'", path)
+
+    return zip_filename
+
+
+ARCHIVE_FORMATS = {
+    'gztar': (make_tarball, [('compress', 'gzip')], "gzip'ed tar-file"),
+    'bztar': (make_tarball, [('compress', 'bzip2')], "bzip2'ed tar-file"),
+    'xztar': (make_tarball, [('compress', 'xz')], "xz'ed tar-file"),
+    'ztar': (make_tarball, [('compress', 'compress')], "compressed tar file"),
+    'tar': (make_tarball, [('compress', None)], "uncompressed tar file"),
+    'zip': (make_zipfile, [], "ZIP file"),
+}
+
+
+def check_archive_formats(formats):
+    """Returns the first format from the 'format' list that is unknown.
+
+    If all formats are known, returns None
+    """
+    for format in formats:
+        if format not in ARCHIVE_FORMATS:
+            return format
+    return None
+
+
+def make_archive(
+    base_name,
+    format,
+    root_dir=None,
+    base_dir=None,
+    verbose=0,
+    dry_run=0,
+    owner=None,
+    group=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", "gztar",
+    "bztar", "xztar", or "ztar".
+
+    '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:
+        log.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}
+
+    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:
+            log.debug("changing back to '%s'", save_cwd)
+            os.chdir(save_cwd)
+
+    return filename
diff --git a/venv/Lib/site-packages/setuptools/_distutils/bcppcompiler.py b/venv/Lib/site-packages/setuptools/_distutils/bcppcompiler.py
new file mode 100644
index 0000000..c1341e4
--- /dev/null
+++ b/venv/Lib/site-packages/setuptools/_distutils/bcppcompiler.py
@@ -0,0 +1,397 @@
+"""distutils.bcppcompiler
+
+Contains BorlandCCompiler, an implementation of the abstract CCompiler class
+for the Borland C++ compiler.
+"""
+
+# This implementation by Lyle Johnson, based on the original msvccompiler.py
+# module and using the directions originally published by Gordon Williams.
+
+# XXX looks like there's a LOT of overlap between these two classes:
+# someone should sit down and factor out the common code as
+# WindowsCCompiler!  --GPW
+
+import os
+import warnings
+
+from ._log import log
+from ._modified import newer
+from .ccompiler import CCompiler, gen_preprocess_options
+from .errors import (
+    CompileError,
+    DistutilsExecError,
+    LibError,
+    LinkError,
+    UnknownFileError,
+)
+from .file_util import write_file
+
+warnings.warn(
+    "bcppcompiler is deprecated and slated to be removed "
+    "in the future. Please discontinue use or file an issue "
+    "with pypa/distutils describing your use case.",
+    DeprecationWarning,
+)
+
+
+class BCPPCompiler(CCompiler):
+    """Concrete class that implements an interface to the Borland C/C++
+    compiler, as defined by the CCompiler abstract class.
+    """
+
+    compiler_type = 'bcpp'
+
+    # Just set this so CCompiler's constructor doesn't barf.  We currently
+    # don't use the 'set_executables()' bureaucracy provided by CCompiler,
+    # as it really isn't necessary for this sort of single-compiler class.
+    # Would be nice to have a consistent interface with UnixCCompiler,
+    # though, so it's worth thinking about.
+    executables = {}
+
+    # Private class data (need to distinguish C from C++ source for compiler)
+    _c_extensions = ['.c']
+    _cpp_extensions = ['.cc', '.cpp', '.cxx']
+
+    # Needed for the filename generation methods provided by the
+    # base class, CCompiler.
+    src_extensions = _c_extensions + _cpp_extensions
+    obj_extension = '.obj'
+    static_lib_extension = '.lib'
+    shared_lib_extension = '.dll'
+    static_lib_format = shared_lib_format = '%s%s'
+    exe_extension = '.exe'
+
+    def __init__(self, verbose=0, dry_run=0, force=0):
+        super().__init__(verbose, dry_run, force)
+
+        # These executables are assumed to all be in the path.
+        # Borland doesn't seem to use any special registry settings to
+        # indicate their installation locations.
+
+        self.cc = "bcc32.exe"
+        self.linker = "ilink32.exe"
+        self.lib = "tlib.exe"
+
+        self.preprocess_options = None
+        self.compile_options = ['/tWM', '/O2', '/q', '/g0']
+        self.compile_options_debug = ['/tWM', '/Od', '/q', '/g0']
+
+        self.ldflags_shared = ['/Tpd', '/Gn', '/q', '/x']
+        self.ldflags_shared_debug = ['/Tpd', '/Gn', '/q', '/x']
+        self.ldflags_static = []
+        self.ldflags_exe = ['/Gn', '/q', '/x']
+        self.ldflags_exe_debug = ['/Gn', '/q', '/x', '/r']
+
+    # -- Worker methods ------------------------------------------------
+
+    def compile(  # noqa: C901
+        self,
+        sources,
+        output_dir=None,
+        macros=None,
+        include_dirs=None,
+        debug=0,
+        extra_preargs=None,
+        extra_postargs=None,
+        depends=None,
+    ):
+        macros, objects, extra_postargs, pp_opts, build = self._setup_compile(
+            output_dir, macros, include_dirs, sources, depends, extra_postargs
+        )
+        compile_opts = extra_preargs or []
+        compile_opts.append('-c')
+        if debug:
+            compile_opts.extend(self.compile_options_debug)
+        else:
+            compile_opts.extend(self.compile_options)
+
+        for obj in objects:
+            try:
+                src, ext = build[obj]
+            except KeyError:
+                continue
+            # XXX why do the normpath here?
+            src = os.path.normpath(src)
+            obj = os.path.normpath(obj)
+            # XXX _setup_compile() did a mkpath() too but before the normpath.
+            # Is it possible to skip the normpath?
+            self.mkpath(os.path.dirname(obj))
+
+            if ext == '.res':
+                # This is already a binary file -- skip it.
+                continue  # the 'for' loop
+            if ext == '.rc':
+                # This needs to be compiled to a .res file -- do it now.
+                try:
+                    self.spawn(["brcc32", "-fo", obj, src])
+                except DistutilsExecError as msg:
+                    raise CompileError(msg)
+                continue  # the 'for' loop
+
+            # The next two are both for the real compiler.
+            if ext in self._c_extensions:
+                input_opt = ""
+            elif ext in self._cpp_extensions:
+                input_opt = "-P"
+            else:
+                # Unknown file type -- no extra options.  The compiler
+                # will probably fail, but let it just in case this is a
+                # file the compiler recognizes even if we don't.
+                input_opt = ""
+
+            output_opt = "-o" + obj
+
+            # Compiler command line syntax is: "bcc32 [options] file(s)".
+            # Note that the source file names must appear at the end of
+            # the command line.
+            try:
+                self.spawn(
+                    [self.cc]
+                    + compile_opts
+                    + pp_opts
+                    + [input_opt, output_opt]
+                    + extra_postargs
+                    + [src]
+                )
+            except DistutilsExecError as msg:
+                raise CompileError(msg)
+
+        return objects
+
+    # compile ()
+
+    def create_static_lib(
+        self, objects, output_libname, output_dir=None, debug=0, target_lang=None
+    ):
+        (objects, output_dir) = self._fix_object_args(objects, output_dir)
+        output_filename = self.library_filename(output_libname, output_dir=output_dir)
+
+        if self._need_link(objects, output_filename):
+            lib_args = [output_filename, '/u'] + objects
+            if debug:
+                pass  # XXX what goes here?
+            try:
+                self.spawn([self.lib] + lib_args)
+            except DistutilsExecError as msg:
+                raise LibError(msg)
+        else:
+            log.debug("skipping %s (up-to-date)", output_filename)
+
+    # create_static_lib ()
+
+    def link(  # noqa: C901
+        self,
+        target_desc,
+        objects,
+        output_filename,
+        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 this ignores 'build_temp'!  should follow the lead of
+        # msvccompiler.py
+
+        (objects, output_dir) = self._fix_object_args(objects, output_dir)
+        (libraries, library_dirs, runtime_library_dirs) = self._fix_lib_args(
+            libraries, library_dirs, runtime_library_dirs
+        )
+
+        if runtime_library_dirs:
+            log.warning(
+                "I don't know what to do with 'runtime_library_dirs': %s",
+                str(runtime_library_dirs),
+            )
+
+        if output_dir is not None:
+            output_filename = os.path.join(output_dir, output_filename)
+
+        if self._need_link(objects, output_filename):
+            # Figure out linker args based on type of target.
+            if target_desc == CCompiler.EXECUTABLE:
+                startup_obj = 'c0w32'
+                if debug:
+                    ld_args = self.ldflags_exe_debug[:]
+                else:
+                    ld_args = self.ldflags_exe[:]
+            else:
+                startup_obj = 'c0d32'
+                if debug:
+                    ld_args = self.ldflags_shared_debug[:]
+                else:
+                    ld_args = self.ldflags_shared[:]
+
+            # Create a temporary exports file for use by the linker
+            if export_symbols is None:
+                def_file = ''
+            else:
+                head, tail = os.path.split(output_filename)
+                modname, ext = os.path.splitext(tail)
+                temp_dir = os.path.dirname(objects[0])  # preserve tree structure
+                def_file = os.path.join(temp_dir, '%s.def' % modname)
+                contents = ['EXPORTS']
+                for sym in export_symbols or []:
+                    contents.append(f'  {sym}=_{sym}')
+                self.execute(write_file, (def_file, contents), "writing %s" % def_file)
+
+            # Borland C++ has problems with '/' in paths
+            objects2 = map(os.path.normpath, objects)
+            # split objects in .obj and .res files
+            # Borland C++ needs them at different positions in the command line
+            objects = [startup_obj]
+            resources = []
+            for file in objects2:
+                (base, ext) = os.path.splitext(os.path.normcase(file))
+                if ext == '.res':
+                    resources.append(file)
+                else:
+                    objects.append(file)
+
+            for ell in library_dirs:
+                ld_args.append("/L%s" % os.path.normpath(ell))
+            ld_args.append("/L.")  # we sometimes use relative paths
+
+            # list of object files
+            ld_args.extend(objects)
+
+            # XXX the command-line syntax for Borland C++ is a bit wonky;
+            # certain filenames are jammed together in one big string, but
+            # comma-delimited.  This doesn't mesh too well with the
+            # Unix-centric attitude (with a DOS/Windows quoting hack) of
+            # 'spawn()', so constructing the argument list is a bit
+            # awkward.  Note that doing the obvious thing and jamming all
+            # the filenames and commas into one argument would be wrong,
+            # because 'spawn()' would quote any filenames with spaces in
+            # them.  Arghghh!.  Apparently it works fine as coded...
+
+            # name of dll/exe file
+            ld_args.extend([',', output_filename])
+            # no map file and start libraries
+            ld_args.append(',,')
+
+            for lib in libraries:
+                # see if we find it and if there is a bcpp specific lib
+                # (xxx_bcpp.lib)
+                libfile = self.find_library_file(library_dirs, lib, debug)
+                if libfile is None:
+                    ld_args.append(lib)
+                    # probably a BCPP internal library -- don't warn
+                else:
+                    # full name which prefers bcpp_xxx.lib over xxx.lib
+                    ld_args.append(libfile)
+
+            # some default libraries
+            ld_args.extend(('import32', 'cw32mt'))
+
+            # def file for export symbols
+            ld_args.extend([',', def_file])
+            # add resource files
+            ld_args.append(',')
+            ld_args.extend(resources)
+
+            if extra_preargs:
+                ld_args[:0] = extra_preargs
+            if extra_postargs:
+                ld_args.extend(extra_postargs)
+
+            self.mkpath(os.path.dirname(output_filename))
+            try:
+                self.spawn([self.linker] + ld_args)
+            except DistutilsExecError as msg:
+                raise LinkError(msg)
+
+        else:
+            log.debug("skipping %s (up-to-date)", output_filename)
+
+    # link ()
+
+    # -- Miscellaneous methods -----------------------------------------
+
+    def find_library_file(self, dirs, lib, debug=0):
+        # List of effective library names to try, in order of preference:
+        # xxx_bcpp.lib is better than xxx.lib
+        # and xxx_d.lib is better than xxx.lib if debug is set
+        #
+        # The "_bcpp" suffix is to handle a Python installation for people
+        # with multiple compilers (primarily Distutils hackers, I suspect
+        # ;-).  The idea is they'd have one static library for each
+        # compiler they care about, since (almost?) every Windows compiler
+        # seems to have a different format for static libraries.
+        if debug:
+            dlib = lib + "_d"
+            try_names = (dlib + "_bcpp", lib + "_bcpp", dlib, lib)
+        else:
+            try_names = (lib + "_bcpp", lib)
+
+        for dir in dirs:
+            for name in try_names:
+                libfile = os.path.join(dir, self.library_filename(name))
+                if os.path.exists(libfile):
+                    return libfile
+        else:
+            # Oops, didn't find it in *any* of 'dirs'
+            return None
+
+    # overwrite the one from CCompiler to support rc and res-files
+    def object_filenames(self, source_filenames, strip_dir=0, output_dir=''):
+        if output_dir is None:
+            output_dir = ''
+        obj_names = []
+        for src_name in source_filenames:
+            # use normcase to make sure '.rc' is really '.rc' and not '.RC'
+            (base, ext) = os.path.splitext(os.path.normcase(src_name))
+            if ext not in (self.src_extensions + ['.rc', '.res']):
+                raise UnknownFileError(f"unknown file type '{ext}' (from '{src_name}')")
+            if strip_dir:
+                base = os.path.basename(base)
+            if ext == '.res':
+                # these can go unchanged
+                obj_names.append(os.path.join(output_dir, base + ext))
+            elif ext == '.rc':
+                # these need to be compiled to .res-files
+                obj_names.append(os.path.join(output_dir, base + '.res'))
+            else:
+                obj_names.append(os.path.join(output_dir, base + self.obj_extension))
+        return obj_names
+
+    # object_filenames ()
+
+    def preprocess(
+        self,
+        source,
+        output_file=None,
+        macros=None,
+        include_dirs=None,
+        extra_preargs=None,
+        extra_postargs=None,
+    ):
+        (_, macros, include_dirs) = self._fix_compile_args(None, macros, include_dirs)
+        pp_opts = gen_preprocess_options(macros, include_dirs)
+        pp_args = ['cpp32.exe'] + pp_opts
+        if output_file is not None:
+            pp_args.append('-o' + output_file)
+        if extra_preargs:
+            pp_args[:0] = extra_preargs
+        if extra_postargs:
+            pp_args.extend(extra_postargs)
+        pp_args.append(source)
+
+        # We need to preprocess: either we're being forced to, or the
+        # source file is newer than the target (or the target doesn't
+        # exist).
+        if self.force or output_file is None or newer(source, output_file):
+            if output_file:
+                self.mkpath(os.path.dirname(output_file))
+            try:
+                self.spawn(pp_args)
+            except DistutilsExecError as msg:
+                print(msg)
+                raise CompileError(msg)
+
+    # preprocess()
diff --git a/venv/Lib/site-packages/setuptools/_distutils/ccompiler.py b/venv/Lib/site-packages/setuptools/_distutils/ccompiler.py
new file mode 100644
index 0000000..8876d73
--- /dev/null
+++ b/venv/Lib/site-packages/setuptools/_distutils/ccompiler.py
@@ -0,0 +1,1252 @@
+"""distutils.ccompiler
+
+Contains CCompiler, an abstract base class that defines the interface
+for the Distutils compiler abstraction model."""
+
+import os
+import re
+import sys
+import warnings
+
+from ._itertools import always_iterable
+from ._log import log
+from ._modified import newer_group
+from .dir_util import mkpath
+from .errors import (
+    CompileError,
+    DistutilsModuleError,
+    DistutilsPlatformError,
+    LinkError,
+    UnknownFileError,
+)
+from .file_util import move_file
+from .spawn import spawn
+from .util import execute, split_quoted
+
+
+class CCompiler:
+    """Abstract base class to define the interface that must be implemented
+    by real compiler classes.  Also has some utility methods used by
+    several compiler classes.
+
+    The basic idea behind a compiler abstraction class is that each
+    instance can be used for all the compile/link steps in building a
+    single project.  Thus, attributes common to all of those compile and
+    link steps -- include directories, macros to define, libraries to link
+    against, etc. -- are attributes of the compiler instance.  To allow for
+    variability in how individual files are treated, most of those
+    attributes may be varied on a per-compilation or per-link basis.
+    """
+
+    # 'compiler_type' is a class attribute that identifies this class.  It
+    # keeps code that wants to know what kind of compiler it's dealing with
+    # from having to import all possible compiler classes just to do an
+    # 'isinstance'.  In concrete CCompiler subclasses, 'compiler_type'
+    # should really, really be one of the keys of the 'compiler_class'
+    # dictionary (see below -- used by the 'new_compiler()' factory
+    # function) -- authors of new compiler interface classes are
+    # responsible for updating 'compiler_class'!
+    compiler_type = None
+
+    # XXX things not handled by this compiler abstraction model:
+    #   * client can't provide additional options for a compiler,
+    #     e.g. warning, optimization, debugging flags.  Perhaps this
+    #     should be the domain of concrete compiler abstraction classes
+    #     (UnixCCompiler, MSVCCompiler, etc.) -- or perhaps the base
+    #     class should have methods for the common ones.
+    #   * can't completely override the include or library searchg
+    #     path, ie. no "cc -I -Idir1 -Idir2" or "cc -L -Ldir1 -Ldir2".
+    #     I'm not sure how widely supported this is even by Unix
+    #     compilers, much less on other platforms.  And I'm even less
+    #     sure how useful it is; maybe for cross-compiling, but
+    #     support for that is a ways off.  (And anyways, cross
+    #     compilers probably have a dedicated binary with the
+    #     right paths compiled in.  I hope.)
+    #   * can't do really freaky things with the library list/library
+    #     dirs, e.g. "-Ldir1 -lfoo -Ldir2 -lfoo" to link against
+    #     different versions of libfoo.a in different locations.  I
+    #     think this is useless without the ability to null out the
+    #     library search path anyways.
+
+    # Subclasses that rely on the standard filename generation methods
+    # implemented below should override these; see the comment near
+    # those methods ('object_filenames()' et. al.) for details:
+    src_extensions = None  # list of strings
+    obj_extension = None  # string
+    static_lib_extension = None
+    shared_lib_extension = None  # string
+    static_lib_format = None  # format string
+    shared_lib_format = None  # prob. same as static_lib_format
+    exe_extension = None  # string
+
+    # Default language settings. language_map is used to detect a source
+    # file or Extension target language, checking source filenames.
+    # language_order is used to detect the language precedence, when deciding
+    # what language to use when mixing source types. For example, if some
+    # extension has two files with ".c" extension, and one with ".cpp", it
+    # is still linked as c++.
+    language_map = {
+        ".c": "c",
+        ".cc": "c++",
+        ".cpp": "c++",
+        ".cxx": "c++",
+        ".m": "objc",
+    }
+    language_order = ["c++", "objc", "c"]
+
+    include_dirs = []
+    """
+    include dirs specific to this compiler class
+    """
+
+    library_dirs = []
+    """
+    library dirs specific to this compiler class
+    """
+
+    def __init__(self, verbose=0, dry_run=0, force=0):
+        self.dry_run = dry_run
+        self.force = force
+        self.verbose = verbose
+
+        # 'output_dir': a common output directory for object, library,
+        # shared object, and shared library files
+        self.output_dir = None
+
+        # 'macros': a list of macro definitions (or undefinitions).  A
+        # macro definition is a 2-tuple (name, value), where the value is
+        # either a string or None (no explicit value).  A macro
+        # undefinition is a 1-tuple (name,).
+        self.macros = []
+
+        # 'include_dirs': a list of directories to search for include files
+        self.include_dirs = []
+
+        # 'libraries': a list of libraries to include in any link
+        # (library names, not filenames: eg. "foo" not "libfoo.a")
+        self.libraries = []
+
+        # 'library_dirs': a list of directories to search for libraries
+        self.library_dirs = []
+
+        # 'runtime_library_dirs': a list of directories to search for
+        # shared libraries/objects at runtime
+        self.runtime_library_dirs = []
+
+        # 'objects': a list of object files (or similar, such as explicitly
+        # named library files) to include on any link
+        self.objects = []
+
+        for key in self.executables.keys():
+            self.set_executable(key, self.executables[key])
+
+    def set_executables(self, **kwargs):
+        """Define the executables (and options for them) that will be run
+        to perform the various stages of compilation.  The exact set of
+        executables that may be specified here depends on the compiler
+        class (via the 'executables' class attribute), but most will have:
+          compiler      the C/C++ compiler
+          linker_so     linker used to create shared objects and libraries
+          linker_exe    linker used to create binary executables
+          archiver      static library creator
+
+        On platforms with a command-line (Unix, DOS/Windows), each of these
+        is a string that will be split into executable name and (optional)
+        list of arguments.  (Splitting the string is done similarly to how
+        Unix shells operate: words are delimited by spaces, but quotes and
+        backslashes can override this.  See
+        'distutils.util.split_quoted()'.)
+        """
+
+        # Note that some CCompiler implementation classes will define class
+        # attributes 'cpp', 'cc', etc. with hard-coded executable names;
+        # this is appropriate when a compiler class is for exactly one
+        # compiler/OS combination (eg. MSVCCompiler).  Other compiler
+        # classes (UnixCCompiler, in particular) are driven by information
+        # discovered at run-time, since there are many different ways to do
+        # basically the same things with Unix C compilers.
+
+        for key in kwargs:
+            if key not in self.executables:
+                raise ValueError(
+                    f"unknown executable '{key}' for class {self.__class__.__name__}"
+                )
+            self.set_executable(key, kwargs[key])
+
+    def set_executable(self, key, value):
+        if isinstance(value, str):
+            setattr(self, key, split_quoted(value))
+        else:
+            setattr(self, key, value)
+
+    def _find_macro(self, name):
+        i = 0
+        for defn in self.macros:
+            if defn[0] == name:
+                return i
+            i += 1
+        return None
+
+    def _check_macro_definitions(self, definitions):
+        """Ensures that every element of 'definitions' is a valid macro
+        definition, ie. either (name,value) 2-tuple or a (name,) tuple.  Do
+        nothing if all definitions are OK, raise TypeError otherwise.
+        """
+        for defn in definitions:
+            if not (
+                isinstance(defn, tuple)
+                and (
+                    len(defn) in (1, 2)
+                    and (isinstance(defn[1], str) or defn[1] is None)
+                )
+                and isinstance(defn[0], str)
+            ):
+                raise TypeError(
+                    ("invalid macro definition '%s': " % defn)
+                    + "must be tuple (string,), (string, string), or "
+                    + "(string, None)"
+                )
+
+    # -- Bookkeeping methods -------------------------------------------
+
+    def define_macro(self, name, value=None):
+        """Define a preprocessor macro for all compilations driven by this
+        compiler object.  The optional parameter 'value' should be a
+        string; if it is not supplied, then the macro will be defined
+        without an explicit value and the exact outcome depends on the
+        compiler used (XXX true? does ANSI say anything about this?)
+        """
+        # Delete from the list of macro definitions/undefinitions if
+        # already there (so that this one will take precedence).
+        i = self._find_macro(name)
+        if i is not None:
+            del self.macros[i]
+
+        self.macros.append((name, value))
+
+    def undefine_macro(self, name):
+        """Undefine a preprocessor macro for all compilations driven by
+        this compiler object.  If the same macro is defined by
+        'define_macro()' and undefined by 'undefine_macro()' the last call
+        takes precedence (including multiple redefinitions or
+        undefinitions).  If the macro is redefined/undefined on a
+        per-compilation basis (ie. in the call to 'compile()'), then that
+        takes precedence.
+        """
+        # Delete from the list of macro definitions/undefinitions if
+        # already there (so that this one will take precedence).
+        i = self._find_macro(name)
+        if i is not None:
+            del self.macros[i]
+
+        undefn = (name,)
+        self.macros.append(undefn)
+
+    def add_include_dir(self, dir):
+        """Add 'dir' to the list of directories that will be searched for
+        header files.  The compiler is instructed to search directories in
+        the order in which they are supplied by successive calls to
+        'add_include_dir()'.
+        """
+        self.include_dirs.append(dir)
+
+    def set_include_dirs(self, dirs):
+        """Set the list of directories that will be searched to 'dirs' (a
+        list of strings).  Overrides any preceding calls to
+        'add_include_dir()'; subsequence calls to 'add_include_dir()' add
+        to the list passed to 'set_include_dirs()'.  This does not affect
+        any list of standard include directories that the compiler may
+        search by default.
+        """
+        self.include_dirs = dirs[:]
+
+    def add_library(self, libname):
+        """Add 'libname' to the list of libraries that will be included in
+        all links driven by this compiler object.  Note that 'libname'
+        should *not* be the name of a file containing a library, but the
+        name of the library itself: the actual filename will be inferred by
+        the linker, the compiler, or the compiler class (depending on the
+        platform).
+
+        The linker will be instructed to link against libraries in the
+        order they were supplied to 'add_library()' and/or
+        'set_libraries()'.  It is perfectly valid to duplicate library
+        names; the linker will be instructed to link against libraries as
+        many times as they are mentioned.
+        """
+        self.libraries.append(libname)
+
+    def set_libraries(self, libnames):
+        """Set the list of libraries to be included in all links driven by
+        this compiler object to 'libnames' (a list of strings).  This does
+        not affect any standard system libraries that the linker may
+        include by default.
+        """
+        self.libraries = libnames[:]
+
+    def add_library_dir(self, dir):
+        """Add 'dir' to the list of directories that will be searched for
+        libraries specified to 'add_library()' and 'set_libraries()'.  The
+        linker will be instructed to search for libraries in the order they
+        are supplied to 'add_library_dir()' and/or 'set_library_dirs()'.
+        """
+        self.library_dirs.append(dir)
+
+    def set_library_dirs(self, dirs):
+        """Set the list of library search directories to 'dirs' (a list of
+        strings).  This does not affect any standard library search path
+        that the linker may search by default.
+        """
+        self.library_dirs = dirs[:]
+
+    def add_runtime_library_dir(self, dir):
+        """Add 'dir' to the list of directories that will be searched for
+        shared libraries at runtime.
+        """
+        self.runtime_library_dirs.append(dir)
+
+    def set_runtime_library_dirs(self, dirs):
+        """Set the list of directories to search for shared libraries at
+        runtime to 'dirs' (a list of strings).  This does not affect any
+        standard search path that the runtime linker may search by
+        default.
+        """
+        self.runtime_library_dirs = dirs[:]
+
+    def add_link_object(self, object):
+        """Add 'object' to the list of object files (or analogues, such as
+        explicitly named library files or the output of "resource
+        compilers") to be included in every link driven by this compiler
+        object.
+        """
+        self.objects.append(object)
+
+    def set_link_objects(self, objects):
+        """Set the list of object files (or analogues) to be included in
+        every link to 'objects'.  This does not affect any standard object
+        files that the linker may include by default (such as system
+        libraries).
+        """
+        self.objects = objects[:]
+
+    # -- Private utility methods --------------------------------------
+    # (here for the convenience of subclasses)
+
+    # Helper method to prep compiler in subclass compile() methods
+
+    def _setup_compile(self, outdir, macros, incdirs, sources, depends, extra):
+        """Process arguments and decide which source files to compile."""
+        outdir, macros, incdirs = self._fix_compile_args(outdir, macros, incdirs)
+
+        if extra is None:
+            extra = []
+
+        # Get the list of expected output (object) files
+        objects = self.object_filenames(sources, strip_dir=0, output_dir=outdir)
+        assert len(objects) == len(sources)
+
+        pp_opts = gen_preprocess_options(macros, incdirs)
+
+        build = {}
+        for i in range(len(sources)):
+            src = sources[i]
+            obj = objects[i]
+            ext = os.path.splitext(src)[1]
+            self.mkpath(os.path.dirname(obj))
+            build[obj] = (src, ext)
+
+        return macros, objects, extra, pp_opts, build
+
+    def _get_cc_args(self, pp_opts, debug, before):
+        # works for unixccompiler, cygwinccompiler
+        cc_args = pp_opts + ['-c']
+        if debug:
+            cc_args[:0] = ['-g']
+        if before:
+            cc_args[:0] = before
+        return cc_args
+
+    def _fix_compile_args(self, output_dir, macros, include_dirs):
+        """Typecheck and fix-up some of the arguments to the 'compile()'
+        method, and return fixed-up values.  Specifically: if 'output_dir'
+        is None, replaces it with 'self.output_dir'; ensures that 'macros'
+        is a list, and augments it with 'self.macros'; ensures that
+        'include_dirs' is a list, and augments it with 'self.include_dirs'.
+        Guarantees that the returned values are of the correct type,
+        i.e. for 'output_dir' either string or None, and for 'macros' and
+        'include_dirs' either list or None.
+        """
+        if output_dir is None:
+            output_dir = self.output_dir
+        elif not isinstance(output_dir, str):
+            raise TypeError("'output_dir' must be a string or None")
+
+        if macros is None:
+            macros = list(self.macros)
+        elif isinstance(macros, list):
+            macros = macros + (self.macros or [])
+        else:
+            raise TypeError("'macros' (if supplied) must be a list of tuples")
+
+        if include_dirs is None:
+            include_dirs = list(self.include_dirs)
+        elif isinstance(include_dirs, (list, tuple)):
+            include_dirs = list(include_dirs) + (self.include_dirs or [])
+        else:
+            raise TypeError("'include_dirs' (if supplied) must be a list of strings")
+
+        # add include dirs for class
+        include_dirs += self.__class__.include_dirs
+
+        return output_dir, macros, include_dirs
+
+    def _prep_compile(self, sources, output_dir, depends=None):
+        """Decide which source files must be recompiled.
+
+        Determine the list of object files corresponding to 'sources',
+        and figure out which ones really need to be recompiled.
+        Return a list of all object files and a dictionary telling
+        which source files can be skipped.
+        """
+        # Get the list of expected output (object) files
+        objects = self.object_filenames(sources, output_dir=output_dir)
+        assert len(objects) == len(sources)
+
+        # Return an empty dict for the "which source files can be skipped"
+        # return value to preserve API compatibility.
+        return objects, {}
+
+    def _fix_object_args(self, objects, output_dir):
+        """Typecheck and fix up some arguments supplied to various methods.
+        Specifically: ensure that 'objects' is a list; if output_dir is
+        None, replace with self.output_dir.  Return fixed versions of
+        'objects' and 'output_dir'.
+        """
+        if not isinstance(objects, (list, tuple)):
+            raise TypeError("'objects' must be a list or tuple of strings")
+        objects = list(objects)
+
+        if output_dir is None:
+            output_dir = self.output_dir
+        elif not isinstance(output_dir, str):
+            raise TypeError("'output_dir' must be a string or None")
+
+        return (objects, output_dir)
+
+    def _fix_lib_args(self, libraries, library_dirs, runtime_library_dirs):
+        """Typecheck and fix up some of the arguments supplied to the
+        'link_*' methods.  Specifically: ensure that all arguments are
+        lists, and augment them with their permanent versions
+        (eg. 'self.libraries' augments 'libraries').  Return a tuple with
+        fixed versions of all arguments.
+        """
+        if libraries is None:
+            libraries = list(self.libraries)
+        elif isinstance(libraries, (list, tuple)):
+            libraries = list(libraries) + (self.libraries or [])
+        else:
+            raise TypeError("'libraries' (if supplied) must be a list of strings")
+
+        if library_dirs is None:
+            library_dirs = list(self.library_dirs)
+        elif isinstance(library_dirs, (list, tuple)):
+            library_dirs = list(library_dirs) + (self.library_dirs or [])
+        else:
+            raise TypeError("'library_dirs' (if supplied) must be a list of strings")
+
+        # add library dirs for class
+        library_dirs += self.__class__.library_dirs
+
+        if runtime_library_dirs is None:
+            runtime_library_dirs = list(self.runtime_library_dirs)
+        elif isinstance(runtime_library_dirs, (list, tuple)):
+            runtime_library_dirs = list(runtime_library_dirs) + (
+                self.runtime_library_dirs or []
+            )
+        else:
+            raise TypeError(
+                "'runtime_library_dirs' (if supplied) must be a list of strings"
+            )
+
+        return (libraries, library_dirs, runtime_library_dirs)
+
+    def _need_link(self, objects, output_file):
+        """Return true if we need to relink the files listed in 'objects'
+        to recreate 'output_file'.
+        """
+        if self.force:
+            return True
+        else:
+            if self.dry_run:
+                newer = newer_group(objects, output_file, missing='newer')
+            else:
+                newer = newer_group(objects, output_file)
+            return newer
+
+    def detect_language(self, sources):
+        """Detect the language of a given file, or list of files. Uses
+        language_map, and language_order to do the job.
+        """
+        if not isinstance(sources, list):
+            sources = [sources]
+        lang = None
+        index = len(self.language_order)
+        for source in sources:
+            base, ext = os.path.splitext(source)
+            extlang = self.language_map.get(ext)
+            try:
+                extindex = self.language_order.index(extlang)
+                if extindex < index:
+                    lang = extlang
+                    index = extindex
+            except ValueError:
+                pass
+        return lang
+
+    # -- Worker methods ------------------------------------------------
+    # (must be implemented by subclasses)
+
+    def preprocess(
+        self,
+        source,
+        output_file=None,
+        macros=None,
+        include_dirs=None,
+        extra_preargs=None,
+        extra_postargs=None,
+    ):
+        """Preprocess a single C/C++ source file, named in 'source'.
+        Output will be written to file named 'output_file', or stdout if
+        'output_file' not supplied.  'macros' is a list of macro
+        definitions as for 'compile()', which will augment the macros set
+        with 'define_macro()' and 'undefine_macro()'.  'include_dirs' is a
+        list of directory names that will be added to the default list.
+
+        Raises PreprocessError on failure.
+        """
+        pass
+
+    def compile(
+        self,
+        sources,
+        output_dir=None,
+        macros=None,
+        include_dirs=None,
+        debug=0,
+        extra_preargs=None,
+        extra_postargs=None,
+        depends=None,
+    ):
+        """Compile one or more source files.
+
+        'sources' must be a list of filenames, most likely C/C++
+        files, but in reality anything that can be handled by a
+        particular compiler and compiler class (eg. MSVCCompiler can
+        handle resource files in 'sources').  Return a list of object
+        filenames, one per source filename in 'sources'.  Depending on
+        the implementation, not all source files will necessarily be
+        compiled, but all corresponding object filenames will be
+        returned.
+
+        If 'output_dir' is given, object files will be put under it, while
+        retaining their original path component.  That is, "foo/bar.c"
+        normally compiles to "foo/bar.o" (for a Unix implementation); if
+        'output_dir' is "build", then it would compile to
+        "build/foo/bar.o".
+
+        'macros', if given, must be a list of macro definitions.  A macro
+        definition is either a (name, value) 2-tuple or a (name,) 1-tuple.
+        The former defines a macro; if the value is None, the macro is
+        defined without an explicit value.  The 1-tuple case undefines a
+        macro.  Later definitions/redefinitions/ undefinitions take
+        precedence.
+
+        'include_dirs', if given, must be a list of strings, the
+        directories to add to the default include file search path for this
+        compilation only.
+
+        'debug' is a boolean; if true, the compiler will be instructed to
+        output debug symbols in (or alongside) the object file(s).
+
+        'extra_preargs' and 'extra_postargs' are implementation- dependent.
+        On platforms that have the notion of a command-line (e.g. Unix,
+        DOS/Windows), they are most likely lists of strings: extra
+        command-line arguments to prepend/append to the compiler command
+        line.  On other platforms, consult the implementation class
+        documentation.  In any event, they are intended as an escape hatch
+        for those occasions when the abstract compiler framework doesn't
+        cut the mustard.
+
+        'depends', if given, is a list of filenames that all targets
+        depend on.  If a source file is older than any file in
+        depends, then the source file will be recompiled.  This
+        supports dependency tracking, but only at a coarse
+        granularity.
+
+        Raises CompileError on failure.
+        """
+        # A concrete compiler class can either override this method
+        # entirely or implement _compile().
+        macros, objects, extra_postargs, pp_opts, build = self._setup_compile(
+            output_dir, macros, include_dirs, sources, depends, extra_postargs
+        )
+        cc_args = self._get_cc_args(pp_opts, debug, extra_preargs)
+
+        for obj in objects:
+            try:
+                src, ext = build[obj]
+            except KeyError:
+                continue
+            self._compile(obj, src, ext, cc_args, extra_postargs, pp_opts)
+
+        # Return *all* object filenames, not just the ones we just built.
+        return objects
+
+    def _compile(self, obj, src, ext, cc_args, extra_postargs, pp_opts):
+        """Compile 'src' to product 'obj'."""
+        # A concrete compiler class that does not override compile()
+        # should implement _compile().
+        pass
+
+    def create_static_lib(
+        self, objects, output_libname, output_dir=None, debug=0, target_lang=None
+    ):
+        """Link a bunch of stuff together to create a static library file.
+        The "bunch of stuff" consists of the list of object files supplied
+        as 'objects', the extra object files supplied to
+        'add_link_object()' and/or 'set_link_objects()', the libraries
+        supplied to 'add_library()' and/or 'set_libraries()', and the
+        libraries supplied as 'libraries' (if any).
+
+        'output_libname' should be a library name, not a filename; the
+        filename will be inferred from the library name.  'output_dir' is
+        the directory where the library file will be put.
+
+        'debug' is a boolean; if true, debugging information will be
+        included in the library (note that on most platforms, it is the
+        compile step where this matters: the 'debug' flag is included here
+        just for consistency).
+
+        'target_lang' is the target language for which the given objects
+        are being compiled. This allows specific linkage time treatment of
+        certain languages.
+
+        Raises LibError on failure.
+        """
+        pass
+
+    # values for target_desc parameter in link()
+    SHARED_OBJECT = "shared_object"
+    SHARED_LIBRARY = "shared_library"
+    EXECUTABLE = "executable"
+
+    def link(
+        self,
+        target_desc,
+        objects,
+        output_filename,
+        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,
+    ):
+        """Link a bunch of stuff together to create an executable or
+        shared library file.
+
+        The "bunch of stuff" consists of the list of object files supplied
+        as 'objects'.  'output_filename' should be a filename.  If
+        'output_dir' is supplied, 'output_filename' is relative to it
+        (i.e. 'output_filename' can provide directory components if
+        needed).
+
+        'libraries' is a list of libraries to link against.  These are
+        library names, not filenames, since they're translated into
+        filenames in a platform-specific way (eg. "foo" becomes "libfoo.a"
+        on Unix and "foo.lib" on DOS/Windows).  However, they can include a
+        directory component, which means the linker will look in that
+        specific directory rather than searching all the normal locations.
+
+        'library_dirs', if supplied, should be a list of directories to
+        search for libraries that were specified as bare library names
+        (ie. no directory component).  These are on top of the system
+        default and those supplied to 'add_library_dir()' and/or
+        'set_library_dirs()'.  'runtime_library_dirs' is a list of
+        directories that will be embedded into the shared library and used
+        to search for other shared libraries that *it* depends on at
+        run-time.  (This may only be relevant on Unix.)
+
+        'export_symbols' is a list of symbols that the shared library will
+        export.  (This appears to be relevant only on Windows.)
+
+        'debug' is as for 'compile()' and 'create_static_lib()', with the
+        slight distinction that it actually matters on most platforms (as
+        opposed to 'create_static_lib()', which includes a 'debug' flag
+        mostly for form's sake).
+
+        'extra_preargs' and 'extra_postargs' are as for 'compile()' (except
+        of course that they supply command-line arguments for the
+        particular linker being used).
+
+        'target_lang' is the target language for which the given objects
+        are being compiled. This allows specific linkage time treatment of
+        certain languages.
+
+        Raises LinkError on failure.
+        """
+        raise NotImplementedError
+
+    # Old 'link_*()' methods, rewritten to use the new 'link()' method.
+
+    def link_shared_lib(
+        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(
+            CCompiler.SHARED_LIBRARY,
+            objects,
+            self.library_filename(output_libname, lib_type='shared'),
+            output_dir,
+            libraries,
+            library_dirs,
+            runtime_library_dirs,
+            export_symbols,
+            debug,
+            extra_preargs,
+            extra_postargs,
+            build_temp,
+            target_lang,
+        )
+
+    def link_shared_object(
+        self,
+        objects,
+        output_filename,
+        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(
+            CCompiler.SHARED_OBJECT,
+            objects,
+            output_filename,
+            output_dir,
+            libraries,
+            library_dirs,
+            runtime_library_dirs,
+            export_symbols,
+            debug,
+            extra_preargs,
+            extra_postargs,
+            build_temp,
+            target_lang,
+        )
+
+    def link_executable(
+        self,
+        objects,
+        output_progname,
+        output_dir=None,
+        libraries=None,
+        library_dirs=None,
+        runtime_library_dirs=None,
+        debug=0,
+        extra_preargs=None,
+        extra_postargs=None,
+        target_lang=None,
+    ):
+        self.link(
+            CCompiler.EXECUTABLE,
+            objects,
+            self.executable_filename(output_progname),
+            output_dir,
+            libraries,
+            library_dirs,
+            runtime_library_dirs,
+            None,
+            debug,
+            extra_preargs,
+            extra_postargs,
+            None,
+            target_lang,
+        )
+
+    # -- Miscellaneous methods -----------------------------------------
+    # These are all used by the 'gen_lib_options() function; there is
+    # no appropriate default implementation so subclasses should
+    # implement all of these.
+
+    def library_dir_option(self, dir):
+        """Return the compiler option to add 'dir' to the list of
+        directories searched for libraries.
+        """
+        raise NotImplementedError
+
+    def runtime_library_dir_option(self, dir):
+        """Return the compiler option to add 'dir' to the list of
+        directories searched for runtime libraries.
+        """
+        raise NotImplementedError
+
+    def library_option(self, lib):
+        """Return the compiler option to add 'lib' to the list of libraries
+        linked into the shared library or executable.
+        """
+        raise NotImplementedError
+
+    def has_function(  # noqa: C901
+        self,
+        funcname,
+        includes=None,
+        include_dirs=None,
+        libraries=None,
+        library_dirs=None,
+    ):
+        """Return a boolean indicating whether funcname is provided as
+        a symbol on the current platform.  The optional arguments can
+        be used to augment the compilation environment.
+
+        The libraries argument is a list of flags to be passed to the
+        linker to make additional symbol definitions available for
+        linking.
+
+        The includes and include_dirs arguments are deprecated.
+        Usually, supplying include files with function declarations
+        will cause function detection to fail even in cases where the
+        symbol is available for linking.
+
+        """
+        # this can't be included at module scope because it tries to
+        # import math which might not be available at that point - maybe
+        # the necessary logic should just be inlined?
+        import tempfile
+
+        if includes is None:
+            includes = []
+        else:
+            warnings.warn("includes is deprecated", DeprecationWarning)
+        if include_dirs is None:
+            include_dirs = []
+        else:
+            warnings.warn("include_dirs is deprecated", DeprecationWarning)
+        if libraries is None:
+            libraries = []
+        if library_dirs is None:
+            library_dirs = []
+        fd, fname = tempfile.mkstemp(".c", funcname, text=True)
+        with os.fdopen(fd, "w", encoding='utf-8') as f:
+            for incl in includes:
+                f.write("""#include "%s"\n""" % incl)
+            if not includes:
+                # Use "char func(void);" as the prototype to follow
+                # what autoconf does.  This prototype does not match
+                # any well-known function the compiler might recognize
+                # as a builtin, so this ends up as a true link test.
+                # Without a fake prototype, the test would need to
+                # know the exact argument types, and the has_function
+                # interface does not provide that level of information.
+                f.write(
+                    """\
+#ifdef __cplusplus
+extern "C"
+#endif
+char %s(void);
+"""
+                    % funcname
+                )
+            f.write(
+                """\
+int main (int argc, char **argv) {
+    %s();
+    return 0;
+}
+"""
+                % funcname
+            )
+
+        try:
+            objects = self.compile([fname], include_dirs=include_dirs)
+        except CompileError:
+            return False
+        finally:
+            os.remove(fname)
+
+        try:
+            self.link_executable(
+                objects, "a.out", libraries=libraries, library_dirs=library_dirs
+            )
+        except (LinkError, TypeError):
+            return False
+        else:
+            os.remove(
+                self.executable_filename("a.out", output_dir=self.output_dir or '')
+            )
+        finally:
+            for fn in objects:
+                os.remove(fn)
+        return True
+
+    def find_library_file(self, dirs, lib, debug=0):
+        """Search the specified list of directories for a static or shared
+        library file 'lib' and return the full path to that file.  If
+        'debug' true, look for a debugging version (if that makes sense on
+        the current platform).  Return None if 'lib' wasn't found in any of
+        the specified directories.
+        """
+        raise NotImplementedError
+
+    # -- Filename generation methods -----------------------------------
+
+    # The default implementation of the filename generating methods are
+    # prejudiced towards the Unix/DOS/Windows view of the world:
+    #   * object files are named by replacing the source file extension
+    #     (eg. .c/.cpp -> .o/.obj)
+    #   * library files (shared or static) are named by plugging the
+    #     library name and extension into a format string, eg.
+    #     "lib%s.%s" % (lib_name, ".a") for Unix static libraries
+    #   * executables are named by appending an extension (possibly
+    #     empty) to the program name: eg. progname + ".exe" for
+    #     Windows
+    #
+    # To reduce redundant code, these methods expect to find
+    # several attributes in the current object (presumably defined
+    # as class attributes):
+    #   * src_extensions -
+    #     list of C/C++ source file extensions, eg. ['.c', '.cpp']
+    #   * obj_extension -
+    #     object file extension, eg. '.o' or '.obj'
+    #   * static_lib_extension -
+    #     extension for static library files, eg. '.a' or '.lib'
+    #   * shared_lib_extension -
+    #     extension for shared library/object files, eg. '.so', '.dll'
+    #   * static_lib_format -
+    #     format string for generating static library filenames,
+    #     eg. 'lib%s.%s' or '%s.%s'
+    #   * shared_lib_format
+    #     format string for generating shared library filenames
+    #     (probably same as static_lib_format, since the extension
+    #     is one of the intended parameters to the format string)
+    #   * exe_extension -
+    #     extension for executable files, eg. '' or '.exe'
+
+    def object_filenames(self, source_filenames, strip_dir=0, output_dir=''):
+        if output_dir is None:
+            output_dir = ''
+        return list(
+            self._make_out_path(output_dir, strip_dir, src_name)
+            for src_name in source_filenames
+        )
+
+    @property
+    def out_extensions(self):
+        return dict.fromkeys(self.src_extensions, self.obj_extension)
+
+    def _make_out_path(self, output_dir, strip_dir, src_name):
+        base, ext = os.path.splitext(src_name)
+        base = self._make_relative(base)
+        try:
+            new_ext = self.out_extensions[ext]
+        except LookupError:
+            raise UnknownFileError(f"unknown file type '{ext}' (from '{src_name}')")
+        if strip_dir:
+            base = os.path.basename(base)
+        return os.path.join(output_dir, base + new_ext)
+
+    @staticmethod
+    def _make_relative(base):
+        """
+        In order to ensure that a filename always honors the
+        indicated output_dir, make sure it's relative.
+        Ref python/cpython#37775.
+        """
+        # Chop off the drive
+        no_drive = os.path.splitdrive(base)[1]
+        # If abs, chop off leading /
+        return no_drive[os.path.isabs(no_drive) :]
+
+    def shared_object_filename(self, basename, strip_dir=0, output_dir=''):
+        assert output_dir is not None
+        if strip_dir:
+            basename = os.path.basename(basename)
+        return os.path.join(output_dir, basename + self.shared_lib_extension)
+
+    def executable_filename(self, basename, strip_dir=0, output_dir=''):
+        assert output_dir is not None
+        if strip_dir:
+            basename = os.path.basename(basename)
+        return os.path.join(output_dir, basename + (self.exe_extension or ''))
+
+    def library_filename(
+        self,
+        libname,
+        lib_type='static',
+        strip_dir=0,
+        output_dir='',  # or 'shared'
+    ):
+        assert output_dir is not None
+        expected = '"static", "shared", "dylib", "xcode_stub"'
+        if lib_type not in eval(expected):
+            raise ValueError(f"'lib_type' must be {expected}")
+        fmt = getattr(self, lib_type + "_lib_format")
+        ext = getattr(self, lib_type + "_lib_extension")
+
+        dir, base = os.path.split(libname)
+        filename = fmt % (base, ext)
+        if strip_dir:
+            dir = ''
+
+        return os.path.join(output_dir, dir, filename)
+
+    # -- Utility methods -----------------------------------------------
+
+    def announce(self, msg, level=1):
+        log.debug(msg)
+
+    def debug_print(self, msg):
+        from distutils.debug import DEBUG
+
+        if DEBUG:
+            print(msg)
+
+    def warn(self, msg):
+        sys.stderr.write("warning: %s\n" % msg)
+
+    def execute(self, func, args, msg=None, level=1):
+        execute(func, args, msg, self.dry_run)
+
+    def spawn(self, cmd, **kwargs):
+        spawn(cmd, dry_run=self.dry_run, **kwargs)
+
+    def move_file(self, src, dst):
+        return move_file(src, dst, dry_run=self.dry_run)
+
+    def mkpath(self, name, mode=0o777):
+        mkpath(name, mode, dry_run=self.dry_run)
+
+
+# Map a sys.platform/os.name ('posix', 'nt') to the default compiler
+# type for that platform. Keys are interpreted as re match
+# patterns. Order is important; platform mappings are preferred over
+# OS names.
+_default_compilers = (
+    # Platform string mappings
+    # on a cygwin built python we can use gcc like an ordinary UNIXish
+    # compiler
+    ('cygwin.*', 'unix'),
+    ('zos', 'zos'),
+    # OS name mappings
+    ('posix', 'unix'),
+    ('nt', 'msvc'),
+)
+
+
+def get_default_compiler(osname=None, platform=None):
+    """Determine the default compiler to use for the given platform.
+
+    osname should be one of the standard Python OS names (i.e. the
+    ones returned by os.name) and platform the common value
+    returned by sys.platform for the platform in question.
+
+    The default values are os.name and sys.platform in case the
+    parameters are not given.
+    """
+    if osname is None:
+        osname = os.name
+    if platform is None:
+        platform = sys.platform
+    for pattern, compiler in _default_compilers:
+        if (
+            re.match(pattern, platform) is not None
+            or re.match(pattern, osname) is not None
+        ):
+            return compiler
+    # Default to Unix compiler
+    return 'unix'
+
+
+# Map compiler types to (module_name, class_name) pairs -- ie. where to
+# find the code that implements an interface to this compiler.  (The module
+# is assumed to be in the 'distutils' package.)
+compiler_class = {
+    'unix': ('unixccompiler', 'UnixCCompiler', "standard UNIX-style compiler"),
+    'msvc': ('_msvccompiler', 'MSVCCompiler', "Microsoft Visual C++"),
+    'cygwin': (
+        'cygwinccompiler',
+        'CygwinCCompiler',
+        "Cygwin port of GNU C Compiler for Win32",
+    ),
+    'mingw32': (
+        'cygwinccompiler',
+        'Mingw32CCompiler',
+        "Mingw32 port of GNU C Compiler for Win32",
+    ),
+    'bcpp': ('bcppcompiler', 'BCPPCompiler', "Borland C++ Compiler"),
+    'zos': ('zosccompiler', 'zOSCCompiler', 'IBM XL C/C++ Compilers'),
+}
+
+
+def show_compilers():
+    """Print list of available compilers (used by the "--help-compiler"
+    options to "build", "build_ext", "build_clib").
+    """
+    # XXX this "knows" that the compiler option it's describing is
+    # "--compiler", which just happens to be the case for the three
+    # commands that use it.
+    from distutils.fancy_getopt import FancyGetopt
+
+    compilers = []
+    for compiler in compiler_class.keys():
+        compilers.append(("compiler=" + compiler, None, compiler_class[compiler][2]))
+    compilers.sort()
+    pretty_printer = FancyGetopt(compilers)
+    pretty_printer.print_help("List of available compilers:")
+
+
+def new_compiler(plat=None, compiler=None, verbose=0, dry_run=0, force=0):
+    """Generate an instance of some CCompiler subclass for the supplied
+    platform/compiler combination.  'plat' defaults to 'os.name'
+    (eg. 'posix', 'nt'), and 'compiler' defaults to the default compiler
+    for that platform.  Currently only 'posix' and 'nt' are supported, and
+    the default compilers are "traditional Unix interface" (UnixCCompiler
+    class) and Visual C++ (MSVCCompiler class).  Note that it's perfectly
+    possible to ask for a Unix compiler object under Windows, and a
+    Microsoft compiler object under Unix -- if you supply a value for
+    'compiler', 'plat' is ignored.
+    """
+    if plat is None:
+        plat = os.name
+
+    try:
+        if compiler is None:
+            compiler = get_default_compiler(plat)
+
+        (module_name, class_name, long_description) = compiler_class[compiler]
+    except KeyError:
+        msg = "don't know how to compile C/C++ code on platform '%s'" % plat
+        if compiler is not None:
+            msg = msg + " with '%s' compiler" % compiler
+        raise DistutilsPlatformError(msg)
+
+    try:
+        module_name = "distutils." + module_name
+        __import__(module_name)
+        module = sys.modules[module_name]
+        klass = vars(module)[class_name]
+    except ImportError:
+        raise DistutilsModuleError(
+            "can't compile C/C++ code: unable to load module '%s'" % module_name
+        )
+    except KeyError:
+        raise DistutilsModuleError(
+            f"can't compile C/C++ code: unable to find class '{class_name}' "
+            f"in module '{module_name}'"
+        )
+
+    # XXX The None is necessary to preserve backwards compatibility
+    # with classes that expect verbose to be the first positional
+    # argument.
+    return klass(None, dry_run, force)
+
+
+def gen_preprocess_options(macros, include_dirs):
+    """Generate C pre-processor options (-D, -U, -I) as used by at least
+    two types of compilers: the typical Unix compiler and Visual C++.
+    'macros' is the usual thing, a list of 1- or 2-tuples, where (name,)
+    means undefine (-U) macro 'name', and (name,value) means define (-D)
+    macro 'name' to 'value'.  'include_dirs' is just a list of directory
+    names to be added to the header file search path (-I).  Returns a list
+    of command-line options suitable for either Unix compilers or Visual
+    C++.
+    """
+    # XXX it would be nice (mainly aesthetic, and so we don't generate
+    # stupid-looking command lines) to go over 'macros' and eliminate
+    # redundant definitions/undefinitions (ie. ensure that only the
+    # latest mention of a particular macro winds up on the command
+    # line).  I don't think it's essential, though, since most (all?)
+    # Unix C compilers only pay attention to the latest -D or -U
+    # mention of a macro on their command line.  Similar situation for
+    # 'include_dirs'.  I'm punting on both for now.  Anyways, weeding out
+    # redundancies like this should probably be the province of
+    # CCompiler, since the data structures used are inherited from it
+    # and therefore common to all CCompiler classes.
+    pp_opts = []
+    for macro in macros:
+        if not (isinstance(macro, tuple) and 1 <= len(macro) <= 2):
+            raise TypeError(
+                "bad macro definition '%s': "
+                "each element of 'macros' list must be a 1- or 2-tuple" % macro
+            )
+
+        if len(macro) == 1:  # undefine this macro
+            pp_opts.append("-U%s" % macro[0])
+        elif len(macro) == 2:
+            if macro[1] is None:  # define with no explicit value
+                pp_opts.append("-D%s" % macro[0])
+            else:
+                # XXX *don't* need to be clever about quoting the
+                # macro value here, because we're going to avoid the
+                # shell at all costs when we spawn the command!
+                pp_opts.append("-D{}={}".format(*macro))
+
+    for dir in include_dirs:
+        pp_opts.append("-I%s" % dir)
+    return pp_opts
+
+
+def gen_lib_options(compiler, library_dirs, runtime_library_dirs, libraries):
+    """Generate linker options for searching library directories and
+    linking with specific libraries.  'libraries' and 'library_dirs' are,
+    respectively, lists of library names (not filenames!) and search
+    directories.  Returns a list of command-line options suitable for use
+    with some compiler (depending on the two format strings passed in).
+    """
+    lib_opts = []
+
+    for dir in library_dirs:
+        lib_opts.append(compiler.library_dir_option(dir))
+
+    for dir in runtime_library_dirs:
+        lib_opts.extend(always_iterable(compiler.runtime_library_dir_option(dir)))
+
+    # XXX it's important that we *not* remove redundant library mentions!
+    # sometimes you really do have to say "-lfoo -lbar -lfoo" in order to
+    # resolve all symbols.  I just hope we never have to say "-lfoo obj.o
+    # -lbar" to get things to work -- that's certainly a possibility, but a
+    # pretty nasty way to arrange your C code.
+
+    for lib in libraries:
+        (lib_dir, lib_name) = os.path.split(lib)
+        if lib_dir:
+            lib_file = compiler.find_library_file([lib_dir], lib_name)
+            if lib_file:
+                lib_opts.append(lib_file)
+            else:
+                compiler.warn(
+                    "no library file corresponding to '%s' found (skipping)" % lib
+                )
+        else:
+            lib_opts.append(compiler.library_option(lib))
+    return lib_opts
diff --git a/venv/Lib/site-packages/setuptools/_distutils/cmd.py b/venv/Lib/site-packages/setuptools/_distutils/cmd.py
new file mode 100644
index 0000000..02dbf16
--- /dev/null
+++ b/venv/Lib/site-packages/setuptools/_distutils/cmd.py
@@ -0,0 +1,433 @@
+"""distutils.cmd
+
+Provides the Command class, the base class for the command classes
+in the distutils.command package.
+"""
+
+import logging
+import os
+import re
+import sys
+
+from . import _modified, archive_util, dir_util, file_util, util
+from ._log import log
+from .errors import DistutilsOptionError
+
+
+class Command:
+    """Abstract base class for defining command classes, the "worker bees"
+    of the Distutils.  A useful analogy for command classes is to think of
+    them as subroutines with local variables called "options".  The options
+    are "declared" in 'initialize_options()' and "defined" (given their
+    final values, aka "finalized") in 'finalize_options()', both of which
+    must be defined by every command class.  The distinction between the
+    two is necessary because option values might come from the outside
+    world (command line, config file, ...), and any options dependent on
+    other options must be computed *after* these outside influences have
+    been processed -- hence 'finalize_options()'.  The "body" of the
+    subroutine, where it does all its work based on the values of its
+    options, is the 'run()' method, which must also be implemented by every
+    command class.
+    """
+
+    # 'sub_commands' formalizes the notion of a "family" of commands,
+    # eg. "install" as the parent with sub-commands "install_lib",
+    # "install_headers", etc.  The parent of a family of commands
+    # defines 'sub_commands' as a class attribute; it's a list of
+    #    (command_name : string, predicate : unbound_method | string | None)
+    # tuples, where 'predicate' is a method of the parent command that
+    # determines whether the corresponding command is applicable in the
+    # current situation.  (Eg. we "install_headers" is only applicable if
+    # we have any C header files to install.)  If 'predicate' is None,
+    # that command is always applicable.
+    #
+    # 'sub_commands' is usually defined at the *end* of a class, because
+    # predicates can be unbound methods, so they must already have been
+    # defined.  The canonical example is the "install" command.
+    sub_commands = []
+
+    # -- Creation/initialization methods -------------------------------
+
+    def __init__(self, dist):
+        """Create and initialize a new Command object.  Most importantly,
+        invokes the 'initialize_options()' method, which is the real
+        initializer and depends on the actual command being
+        instantiated.
+        """
+        # late import because of mutual dependence between these classes
+        from distutils.dist import Distribution
+
+        if not isinstance(dist, Distribution):
+            raise TypeError("dist must be a Distribution instance")
+        if self.__class__ is Command:
+            raise RuntimeError("Command is an abstract class")
+
+        self.distribution = dist
+        self.initialize_options()
+
+        # Per-command versions of the global flags, so that the user can
+        # customize Distutils' behaviour command-by-command and let some
+        # commands fall back on the Distribution's behaviour.  None means
+        # "not defined, check self.distribution's copy", while 0 or 1 mean
+        # false and true (duh).  Note that this means figuring out the real
+        # value of each flag is a touch complicated -- hence "self._dry_run"
+        # will be handled by __getattr__, below.
+        # XXX This needs to be fixed.
+        self._dry_run = None
+
+        # verbose is largely ignored, but needs to be set for
+        # backwards compatibility (I think)?
+        self.verbose = dist.verbose
+
+        # Some commands define a 'self.force' option to ignore file
+        # timestamps, but methods defined *here* assume that
+        # 'self.force' exists for all commands.  So define it here
+        # just to be safe.
+        self.force = None
+
+        # The 'help' flag is just used for command-line parsing, so
+        # none of that complicated bureaucracy is needed.
+        self.help = 0
+
+        # 'finalized' records whether or not 'finalize_options()' has been
+        # called.  'finalize_options()' itself should not pay attention to
+        # this flag: it is the business of 'ensure_finalized()', which
+        # always calls 'finalize_options()', to respect/update it.
+        self.finalized = 0
+
+    # XXX A more explicit way to customize dry_run would be better.
+    def __getattr__(self, attr):
+        if attr == 'dry_run':
+            myval = getattr(self, "_" + attr)
+            if myval is None:
+                return getattr(self.distribution, attr)
+            else:
+                return myval
+        else:
+            raise AttributeError(attr)
+
+    def ensure_finalized(self):
+        if not self.finalized:
+            self.finalize_options()
+        self.finalized = 1
+
+    # Subclasses must define:
+    #   initialize_options()
+    #     provide default values for all options; may be customized by
+    #     setup script, by options from config file(s), or by command-line
+    #     options
+    #   finalize_options()
+    #     decide on the final values for all options; this is called
+    #     after all possible intervention from the outside world
+    #     (command-line, option file, etc.) has been processed
+    #   run()
+    #     run the command: do whatever it is we're here to do,
+    #     controlled by the command's various option values
+
+    def initialize_options(self):
+        """Set default values for all the options that this command
+        supports.  Note that these defaults may be overridden by other
+        commands, by the setup script, by config files, or by the
+        command-line.  Thus, this is not the place to code dependencies
+        between options; generally, 'initialize_options()' implementations
+        are just a bunch of "self.foo = None" assignments.
+
+        This method must be implemented by all command classes.
+        """
+        raise RuntimeError(
+            "abstract method -- subclass %s must override" % self.__class__
+        )
+
+    def finalize_options(self):
+        """Set final values for all the options that this command supports.
+        This is always called as late as possible, ie.  after any option
+        assignments from the command-line or from other commands have been
+        done.  Thus, this is the place to code option dependencies: if
+        'foo' depends on 'bar', then it is safe to set 'foo' from 'bar' as
+        long as 'foo' still has the same value it was assigned in
+        'initialize_options()'.
+
+        This method must be implemented by all command classes.
+        """
+        raise RuntimeError(
+            "abstract method -- subclass %s must override" % self.__class__
+        )
+
+    def dump_options(self, header=None, indent=""):
+        from distutils.fancy_getopt import longopt_xlate
+
+        if header is None:
+            header = "command options for '%s':" % self.get_command_name()
+        self.announce(indent + header, level=logging.INFO)
+        indent = indent + "  "
+        for option, _, _ in self.user_options:
+            option = option.translate(longopt_xlate)
+            if option[-1] == "=":
+                option = option[:-1]
+            value = getattr(self, option)
+            self.announce(indent + f"{option} = {value}", level=logging.INFO)
+
+    def run(self):
+        """A command's raison d'etre: carry out the action it exists to
+        perform, controlled by the options initialized in
+        'initialize_options()', customized by other commands, the setup
+        script, the command-line, and config files, and finalized in
+        'finalize_options()'.  All terminal output and filesystem
+        interaction should be done by 'run()'.
+
+        This method must be implemented by all command classes.
+        """
+        raise RuntimeError(
+            "abstract method -- subclass %s must override" % self.__class__
+        )
+
+    def announce(self, msg, level=logging.DEBUG):
+        log.log(level, msg)
+
+    def debug_print(self, msg):
+        """Print 'msg' to stdout if the global DEBUG (taken from the
+        DISTUTILS_DEBUG environment variable) flag is true.
+        """
+        from distutils.debug import DEBUG
+
+        if DEBUG:
+            print(msg)
+            sys.stdout.flush()
+
+    # -- Option validation methods -------------------------------------
+    # (these are very handy in writing the 'finalize_options()' method)
+    #
+    # NB. the general philosophy here is to ensure that a particular option
+    # value meets certain type and value constraints.  If not, we try to
+    # force it into conformance (eg. if we expect a list but have a string,
+    # split the string on comma and/or whitespace).  If we can't force the
+    # option into conformance, raise DistutilsOptionError.  Thus, command
+    # classes need do nothing more than (eg.)
+    #   self.ensure_string_list('foo')
+    # and they can be guaranteed that thereafter, self.foo will be
+    # a list of strings.
+
+    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, str):
+            raise DistutilsOptionError(f"'{option}' must be a {what} (got `{val}`)")
+        return val
+
+    def ensure_string(self, option, default=None):
+        """Ensure that 'option' is a string; if not defined, set it to
+        'default'.
+        """
+        self._ensure_stringlike(option, "string", default)
+
+    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, str):
+            setattr(self, option, re.split(r',\s*|\s+', val))
+        else:
+            if isinstance(val, list):
+                ok = all(isinstance(v, str) for v in val)
+            else:
+                ok = False
+            if not ok:
+                raise DistutilsOptionError(
+                    f"'{option}' must be a list of strings (got {val!r})"
+                )
+
+    def _ensure_tested_string(self, option, tester, what, error_fmt, default=None):
+        val = self._ensure_stringlike(option, what, default)
+        if val is not None and not tester(val):
+            raise DistutilsOptionError(
+                ("error in '%s' option: " + error_fmt) % (option, val)
+            )
+
+    def ensure_filename(self, option):
+        """Ensure that 'option' is the name of an existing file."""
+        self._ensure_tested_string(
+            option, os.path.isfile, "filename", "'%s' does not exist or is not a file"
+        )
+
+    def ensure_dirname(self, option):
+        self._ensure_tested_string(
+            option,
+            os.path.isdir,
+            "directory name",
+            "'%s' does not exist or is not a directory",
+        )
+
+    # -- Convenience methods for commands ------------------------------
+
+    def get_command_name(self):
+        if hasattr(self, 'command_name'):
+            return self.command_name
+        else:
+            return self.__class__.__name__
+
+    def set_undefined_options(self, src_cmd, *option_pairs):
+        """Set the values of any "undefined" options from corresponding
+        option values in some other command object.  "Undefined" here means
+        "is None", which is the convention used to indicate that an option
+        has not been changed between 'initialize_options()' and
+        'finalize_options()'.  Usually called from 'finalize_options()' for
+        options that depend on some other command rather than another
+        option of the same command.  'src_cmd' is the other command from
+        which option values will be taken (a command object will be created
+        for it if necessary); the remaining arguments are
+        '(src_option,dst_option)' tuples which mean "take the value of
+        'src_option' in the 'src_cmd' command object, and copy it to
+        'dst_option' in the current command object".
+        """
+        # Option_pairs: list of (src_option, dst_option) tuples
+        src_cmd_obj = self.distribution.get_command_obj(src_cmd)
+        src_cmd_obj.ensure_finalized()
+        for src_option, dst_option in option_pairs:
+            if getattr(self, dst_option) is None:
+                setattr(self, dst_option, getattr(src_cmd_obj, src_option))
+
+    def get_finalized_command(self, command, create=1):
+        """Wrapper around Distribution's 'get_command_obj()' method: find
+        (create if necessary and 'create' is true) the command object for
+        'command', call its 'ensure_finalized()' method, and return the
+        finalized command object.
+        """
+        cmd_obj = self.distribution.get_command_obj(command, create)
+        cmd_obj.ensure_finalized()
+        return cmd_obj
+
+    # XXX rename to 'get_reinitialized_command()'? (should do the
+    # same in dist.py, if so)
+    def reinitialize_command(self, command, reinit_subcommands=0):
+        return self.distribution.reinitialize_command(command, reinit_subcommands)
+
+    def run_command(self, command):
+        """Run some other command: uses the 'run_command()' method of
+        Distribution, which creates and finalizes the command object if
+        necessary and then invokes its 'run()' method.
+        """
+        self.distribution.run_command(command)
+
+    def get_sub_commands(self):
+        """Determine the sub-commands that are relevant in the current
+        distribution (ie., that need to be run).  This is based on the
+        'sub_commands' class attribute: each tuple in that list may include
+        a method that we call to determine if the subcommand needs to be
+        run for the current distribution.  Return a list of command names.
+        """
+        commands = []
+        for cmd_name, method in self.sub_commands:
+            if method is None or method(self):
+                commands.append(cmd_name)
+        return commands
+
+    # -- External world manipulation -----------------------------------
+
+    def warn(self, msg):
+        log.warning("warning: %s: %s\n", self.get_command_name(), msg)
+
+    def execute(self, func, args, msg=None, level=1):
+        util.execute(func, args, msg, dry_run=self.dry_run)
+
+    def mkpath(self, name, mode=0o777):
+        dir_util.mkpath(name, mode, dry_run=self.dry_run)
+
+    def copy_file(
+        self, infile, outfile, preserve_mode=1, preserve_times=1, link=None, level=1
+    ):
+        """Copy a file respecting verbose, dry-run and force flags.  (The
+        former two default to whatever is in the Distribution object, and
+        the latter defaults to false for commands that don't define it.)"""
+        return file_util.copy_file(
+            infile,
+            outfile,
+            preserve_mode,
+            preserve_times,
+            not self.force,
+            link,
+            dry_run=self.dry_run,
+        )
+
+    def copy_tree(
+        self,
+        infile,
+        outfile,
+        preserve_mode=1,
+        preserve_times=1,
+        preserve_symlinks=0,
+        level=1,
+    ):
+        """Copy an entire directory tree respecting verbose, dry-run,
+        and force flags.
+        """
+        return dir_util.copy_tree(
+            infile,
+            outfile,
+            preserve_mode,
+            preserve_times,
+            preserve_symlinks,
+            not self.force,
+            dry_run=self.dry_run,
+        )
+
+    def move_file(self, src, dst, level=1):
+        """Move a file respecting dry-run flag."""
+        return file_util.move_file(src, dst, dry_run=self.dry_run)
+
+    def spawn(self, cmd, search_path=1, level=1):
+        """Spawn an external command respecting dry-run flag."""
+        from distutils.spawn import spawn
+
+        spawn(cmd, search_path, dry_run=self.dry_run)
+
+    def make_archive(
+        self, base_name, format, root_dir=None, base_dir=None, owner=None, group=None
+    ):
+        return archive_util.make_archive(
+            base_name,
+            format,
+            root_dir,
+            base_dir,
+            dry_run=self.dry_run,
+            owner=owner,
+            group=group,
+        )
+
+    def make_file(
+        self, infiles, outfile, func, args, exec_msg=None, skip_msg=None, level=1
+    ):
+        """Special case of 'execute()' for operations that process one or
+        more input files and generate one output file.  Works just like
+        'execute()', except the operation is skipped and a different
+        message printed if 'outfile' already exists and is newer than all
+        files listed in 'infiles'.  If the command defined 'self.force',
+        and it is true, then the command is unconditionally run -- does no
+        timestamp checks.
+        """
+        if skip_msg is None:
+            skip_msg = "skipping %s (inputs unchanged)" % outfile
+
+        # Allow 'infiles' to be a single string
+        if isinstance(infiles, str):
+            infiles = (infiles,)
+        elif not isinstance(infiles, (list, tuple)):
+            raise TypeError("'infiles' must be a string, or a list or tuple of strings")
+
+        if exec_msg is None:
+            exec_msg = "generating {} from {}".format(outfile, ', '.join(infiles))
+
+        # If 'outfile' must be regenerated (either because it doesn't
+        # exist, is out-of-date, or the 'force' flag is true) then
+        # perform the action that presumably regenerates it
+        if self.force or _modified.newer_group(infiles, outfile):
+            self.execute(func, args, exec_msg, level)
+        # Otherwise, print the "skip" message
+        else:
+            log.debug(skip_msg)
diff --git a/venv/Lib/site-packages/setuptools/_distutils/command/__init__.py b/venv/Lib/site-packages/setuptools/_distutils/command/__init__.py
new file mode 100644
index 0000000..028dcfa
--- /dev/null
+++ b/venv/Lib/site-packages/setuptools/_distutils/command/__init__.py
@@ -0,0 +1,25 @@
+"""distutils.command
+
+Package containing implementation of all the standard Distutils
+commands."""
+
+__all__ = [  # noqa: F822
+    'build',
+    'build_py',
+    'build_ext',
+    'build_clib',
+    'build_scripts',
+    'clean',
+    'install',
+    'install_lib',
+    'install_headers',
+    'install_scripts',
+    'install_data',
+    'sdist',
+    'register',
+    'bdist',
+    'bdist_dumb',
+    'bdist_rpm',
+    'check',
+    'upload',
+]
diff --git a/venv/Lib/site-packages/setuptools/_distutils/command/__pycache__/__init__.cpython-312.pyc b/venv/Lib/site-packages/setuptools/_distutils/command/__pycache__/__init__.cpython-312.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..aae3cb0906e6dd66d5c0cf29c872fb819971573e
GIT binary patch
literal 544
zcmYLGy>1&Z4A!0D*uB)~Plh7ftsZReMUW*Ki^5BnK!Bi=7+Z+$bkL7ed+Sql&(=#w>Y3D&(8`tkRDhvc5ep-Z2PV+$*cV3AZ;D7wI8s4JwEO3b!3zP$!<6OTfx
z4VT}ic^ok&jbJPRWW(4W@|<*i*<5_3KO9o%JiRm`B#|7wFcM{>OC@};Fk6P*PQ&aL
zDyKZq4Ky42O;t2yvvx1HqYiv%y)oJ?SYp$iWpPk*!5E&xGd6FxFXOY?$e`ieo+1sr
bIdPxR{U2mm_LvW{$y0H1oc|ixFIasA2Q$Fd

literal 0
HcmV?d00001

diff --git a/venv/Lib/site-packages/setuptools/_distutils/command/__pycache__/_framework_compat.cpython-312.pyc b/venv/Lib/site-packages/setuptools/_distutils/command/__pycache__/_framework_compat.cpython-312.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..e96da630790db3d320d92d5016ca7cab6dd29bb9
GIT binary patch
literal 2630
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

literal 0
HcmV?d00001

diff --git a/venv/Lib/site-packages/setuptools/_distutils/command/__pycache__/build.cpython-312.pyc b/venv/Lib/site-packages/setuptools/_distutils/command/__pycache__/build.cpython-312.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..b1a90bf900795055b01fe110c9dbd0536a165296
GIT binary patch
literal 5865
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

literal 0
HcmV?d00001

diff --git a/venv/Lib/site-packages/setuptools/_distutils/command/__pycache__/build_ext.cpython-312.pyc b/venv/Lib/site-packages/setuptools/_distutils/command/__pycache__/build_ext.cpython-312.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..19b530723fb7dafe0eaf01b69b77fa6113e15198
GIT binary patch
literal 29415
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

cFvtuaapO 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 literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/setuptools/_distutils/command/__pycache__/build_py.cpython-312.pyc b/venv/Lib/site-packages/setuptools/_distutils/command/__pycache__/build_py.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..b4c964472bb12896cf6f88fb577ce8b213ed6c80 GIT binary patch literal 16063 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 A;{X5v literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/setuptools/_distutils/command/__pycache__/build_scripts.cpython-312.pyc b/venv/Lib/site-packages/setuptools/_distutils/command/__pycache__/build_scripts.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..eac4c227b10ae64db9b11ea8785ec4438648e873 GIT binary patch literal 7101 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 Dv^hp7 literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/setuptools/_distutils/command/__pycache__/check.cpython-312.pyc b/venv/Lib/site-packages/setuptools/_distutils/command/__pycache__/check.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..1525d12697c86038fb5020ef6aefe50556d4ddbf GIT binary patch literal 6939 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 GdifV1iOo9z literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/setuptools/_distutils/command/__pycache__/config.cpython-312.pyc b/venv/Lib/site-packages/setuptools/_distutils/command/__pycache__/config.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..ba0630be3d78edfa4438dc2b3cf1e2ba51cc7d67 GIT binary patch literal 15268 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 Oes7O>5y9ly9{evK0?NJs literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/setuptools/_distutils/command/__pycache__/install.cpython-312.pyc b/venv/Lib/site-packages/setuptools/_distutils/command/__pycache__/install.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..54acb526f74532220362f9902211bc1dc8ca64ca GIT binary patch literal 27168 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 J^ks1Re*qpQHX8r{ literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/setuptools/_distutils/command/__pycache__/install_data.cpython-312.pyc b/venv/Lib/site-packages/setuptools/_distutils/command/__pycache__/install_data.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..6e621efdd5282803e57d4eb4d031e03401fd1a64 GIT binary patch literal 3530 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 literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/setuptools/_distutils/command/__pycache__/install_headers.cpython-312.pyc b/venv/Lib/site-packages/setuptools/_distutils/command/__pycache__/install_headers.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..f7a846c0fa8b531b582dbfca699e0955c3732082 GIT binary patch literal 2247 zcmb7F&2JM&6rb4-d%fP&)cGtaWg7y-q;YPQXlW`n5wwD&NT?Uf)yml!I~&%!?tTEa zVw3}iM4}=+RJruP5yYR;OWab~-BYWcxKtr27fyY%9}aQop>udUGw;ot-+S-(W`7s(aASv_ZZ^F&+X6$~^a)jo;ZWVPoo4X`2OtYLNH0d1^fz9#!c*$gW}4;wzmr#HeEyZai4@tb~U}5UkW2NQ+eniVmy3b&ctgY@sG9a4RpE zDd5Q_aRdJFjzF?~FAVc34g{B)RhRiuU$+-ODSzVv4?8cBHlB@%P%-n0 zjb@NHEW$$K#s+_M`@;nR>h%<_6w-mh$mbZV@bcMK zkd6r#r0TaD6>lvpV35)6MpI)w;V9rXNL3mQYLSBIyaP*zr94PQCj%b3C%~G^lA^K^ zm6LaTt2I1GSJ?aj?A$5{C|Z83n?KjdpX=slJNenI^zXT`o|6AVQXXZuvpY-Ok?GFJ z^mFNaZ|uwg;xZSuuJ*(}G+j-2TJb z=kbwl1{@>|&JA~SYA2_5b2FXX%rCj(lP~t9;z|6BQz8D-F!?Y12}T?i@C3Xpg8+pK z@l6g^=?[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 aZIk7E*W$@f@HrU1>tlQ#m)E~k@_zv)-B()x literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/setuptools/_distutils/command/__pycache__/upload.cpython-312.pyc b/venv/Lib/site-packages/setuptools/_distutils/command/__pycache__/upload.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..0bbbfe0ecf2f696929c8f0db26e482b82c5cd2cc GIT binary patch literal 9647 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/dumb", "build/bdist./rpm", etc.) + if self.bdist_base is None: + build_base = self.get_finalized_command('build').build_base + self.bdist_base = os.path.join(build_base, 'bdist.' + self.plat_name) + + self.ensure_string_list('formats') + if self.formats is None: + try: + self.formats = [self.default_format[os.name]] + except KeyError: + raise DistutilsPlatformError( + "don't know how to create built distributions " + "on platform %s" % os.name + ) + + if self.dist_dir is None: + self.dist_dir = "dist" + + def run(self): + # Figure out which sub-commands we need to run. + commands = [] + for format in self.formats: + try: + commands.append(self.format_commands[format][0]) + except KeyError: + raise DistutilsOptionError("invalid format '%s'" % format) + + # Reinitialize and run each command. + for i in range(len(self.formats)): + cmd_name = commands[i] + sub_cmd = self.reinitialize_command(cmd_name) + if cmd_name not in self.no_format_option: + sub_cmd.format = self.formats[i] + + # passing the owner and group names for tar archiving + if cmd_name == 'bdist_dumb': + sub_cmd.owner = self.owner + sub_cmd.group = self.group + + # If we're going to need to run this command again, tell it to + # keep its temporary files around so subsequent runs go faster. + if cmd_name in commands[i + 1 :]: + sub_cmd.keep_temp = 1 + self.run_command(cmd_name) diff --git a/venv/Lib/site-packages/setuptools/_distutils/command/bdist_dumb.py b/venv/Lib/site-packages/setuptools/_distutils/command/bdist_dumb.py new file mode 100644 index 0000000..06502d2 --- /dev/null +++ b/venv/Lib/site-packages/setuptools/_distutils/command/bdist_dumb.py @@ -0,0 +1,141 @@ +"""distutils.command.bdist_dumb + +Implements the Distutils 'bdist_dumb' command (create a "dumb" built +distribution -- i.e., just an archive to be unpacked under $prefix or +$exec_prefix).""" + +import os +from distutils._log import log + +from ..core import Command +from ..dir_util import ensure_relative, remove_tree +from ..errors import DistutilsPlatformError +from ..sysconfig import get_python_version +from ..util import get_platform + + +class bdist_dumb(Command): + description = "create a \"dumb\" built distribution" + + user_options = [ + ('bdist-dir=', 'd', "temporary directory for creating the distribution"), + ( + 'plat-name=', + 'p', + "platform name to embed in generated filenames " + "(default: %s)" % get_platform(), + ), + ( + 'format=', + 'f', + "archive format to create (tar, gztar, bztar, xztar, ztar, zip)", + ), + ( + 'keep-temp', + 'k', + "keep the pseudo-installation tree around after " + + "creating the distribution archive", + ), + ('dist-dir=', 'd', "directory to put final built distributions in"), + ('skip-build', None, "skip rebuilding everything (for testing/debugging)"), + ( + 'relative', + None, + "build the archive using relative paths (default: false)", + ), + ( + 'owner=', + 'u', + "Owner name used when creating a tar file [default: current user]", + ), + ( + 'group=', + 'g', + "Group name used when creating a tar file [default: current group]", + ), + ] + + boolean_options = ['keep-temp', 'skip-build', 'relative'] + + default_format = {'posix': 'gztar', 'nt': 'zip'} + + def initialize_options(self): + self.bdist_dir = None + self.plat_name = None + self.format = None + self.keep_temp = 0 + self.dist_dir = None + self.skip_build = None + self.relative = 0 + self.owner = None + self.group = None + + def finalize_options(self): + if self.bdist_dir is None: + bdist_base = self.get_finalized_command('bdist').bdist_base + self.bdist_dir = os.path.join(bdist_base, 'dumb') + + if self.format is None: + try: + self.format = self.default_format[os.name] + except KeyError: + raise DistutilsPlatformError( + "don't know how to create dumb built distributions " + "on platform %s" % os.name + ) + + self.set_undefined_options( + 'bdist', + ('dist_dir', 'dist_dir'), + ('plat_name', 'plat_name'), + ('skip_build', 'skip_build'), + ) + + def run(self): + if not self.skip_build: + self.run_command('build') + + install = self.reinitialize_command('install', reinit_subcommands=1) + install.root = self.bdist_dir + install.skip_build = self.skip_build + install.warn_dir = 0 + + log.info("installing to %s", self.bdist_dir) + self.run_command('install') + + # And make an archive relative to the root of the + # pseudo-installation tree. + archive_basename = f"{self.distribution.get_fullname()}.{self.plat_name}" + + pseudoinstall_root = os.path.join(self.dist_dir, archive_basename) + if not self.relative: + archive_root = self.bdist_dir + else: + if self.distribution.has_ext_modules() and ( + install.install_base != install.install_platbase + ): + raise DistutilsPlatformError( + "can't make a dumb built distribution where " + f"base and platbase are different ({repr(install.install_base)}, {repr(install.install_platbase)})" + ) + else: + archive_root = os.path.join( + self.bdist_dir, ensure_relative(install.install_base) + ) + + # Make the archive + filename = self.make_archive( + pseudoinstall_root, + self.format, + root_dir=archive_root, + owner=self.owner, + group=self.group, + ) + if self.distribution.has_ext_modules(): + pyversion = get_python_version() + else: + pyversion = 'any' + self.distribution.dist_files.append(('bdist_dumb', pyversion, filename)) + + if not self.keep_temp: + remove_tree(self.bdist_dir, dry_run=self.dry_run) diff --git a/venv/Lib/site-packages/setuptools/_distutils/command/bdist_rpm.py b/venv/Lib/site-packages/setuptools/_distutils/command/bdist_rpm.py new file mode 100644 index 0000000..649968a --- /dev/null +++ b/venv/Lib/site-packages/setuptools/_distutils/command/bdist_rpm.py @@ -0,0 +1,599 @@ +"""distutils.command.bdist_rpm + +Implements the Distutils 'bdist_rpm' command (create RPM source and binary +distributions).""" + +import os +import subprocess +import sys +from distutils._log import log + +from ..core import Command +from ..debug import DEBUG +from ..errors import ( + DistutilsExecError, + DistutilsFileError, + DistutilsOptionError, + DistutilsPlatformError, +) +from ..file_util import write_file +from ..sysconfig import get_python_version + + +class bdist_rpm(Command): + description = "create an RPM distribution" + + user_options = [ + ('bdist-base=', None, "base directory for creating built distributions"), + ( + 'rpm-base=', + None, + "base directory for creating RPMs (defaults to \"rpm\" under " + "--bdist-base; must be specified for RPM 2)", + ), + ( + 'dist-dir=', + 'd', + "directory to put final RPM files in (and .spec files if --spec-only)", + ), + ( + 'python=', + None, + "path to Python interpreter to hard-code in the .spec file " + "(default: \"python\")", + ), + ( + 'fix-python', + None, + "hard-code the exact path to the current Python interpreter in " + "the .spec file", + ), + ('spec-only', None, "only regenerate spec file"), + ('source-only', None, "only generate source RPM"), + ('binary-only', None, "only generate binary RPM"), + ('use-bzip2', None, "use bzip2 instead of gzip to create source distribution"), + # More meta-data: too RPM-specific to put in the setup script, + # but needs to go in the .spec file -- so we make these options + # to "bdist_rpm". The idea is that packagers would put this + # info in setup.cfg, although they are of course free to + # supply it on the command line. + ( + 'distribution-name=', + None, + "name of the (Linux) distribution to which this " + "RPM applies (*not* the name of the module distribution!)", + ), + ('group=', None, "package classification [default: \"Development/Libraries\"]"), + ('release=', None, "RPM release number"), + ('serial=', None, "RPM serial number"), + ( + 'vendor=', + None, + "RPM \"vendor\" (eg. \"Joe Blow \") " + "[default: maintainer or author from setup script]", + ), + ( + 'packager=', + None, + "RPM packager (eg. \"Jane Doe \") [default: vendor]", + ), + ('doc-files=', None, "list of documentation files (space or comma-separated)"), + ('changelog=', None, "RPM changelog"), + ('icon=', None, "name of icon file"), + ('provides=', None, "capabilities provided by this package"), + ('requires=', None, "capabilities required by this package"), + ('conflicts=', None, "capabilities which conflict with this package"), + ('build-requires=', None, "capabilities required to build this package"), + ('obsoletes=', None, "capabilities made obsolete by this package"), + ('no-autoreq', None, "do not automatically calculate dependencies"), + # Actions to take when building RPM + ('keep-temp', 'k', "don't clean up RPM build directory"), + ('no-keep-temp', None, "clean up RPM build directory [default]"), + ( + 'use-rpm-opt-flags', + None, + "compile with RPM_OPT_FLAGS when building from source RPM", + ), + ('no-rpm-opt-flags', None, "do not pass any RPM CFLAGS to compiler"), + ('rpm3-mode', None, "RPM 3 compatibility mode (default)"), + ('rpm2-mode', None, "RPM 2 compatibility mode"), + # Add the hooks necessary for specifying custom scripts + ('prep-script=', None, "Specify a script for the PREP phase of RPM building"), + ('build-script=', None, "Specify a script for the BUILD phase of RPM building"), + ( + 'pre-install=', + None, + "Specify a script for the pre-INSTALL phase of RPM building", + ), + ( + 'install-script=', + None, + "Specify a script for the INSTALL phase of RPM building", + ), + ( + 'post-install=', + None, + "Specify a script for the post-INSTALL phase of RPM building", + ), + ( + 'pre-uninstall=', + None, + "Specify a script for the pre-UNINSTALL phase of RPM building", + ), + ( + 'post-uninstall=', + None, + "Specify a script for the post-UNINSTALL phase of RPM building", + ), + ('clean-script=', None, "Specify a script for the CLEAN phase of RPM building"), + ( + 'verify-script=', + None, + "Specify a script for the VERIFY phase of the RPM build", + ), + # Allow a packager to explicitly force an architecture + ('force-arch=', None, "Force an architecture onto the RPM build process"), + ('quiet', 'q', "Run the INSTALL phase of RPM building in quiet mode"), + ] + + boolean_options = [ + 'keep-temp', + 'use-rpm-opt-flags', + 'rpm3-mode', + 'no-autoreq', + 'quiet', + ] + + negative_opt = { + 'no-keep-temp': 'keep-temp', + 'no-rpm-opt-flags': 'use-rpm-opt-flags', + 'rpm2-mode': 'rpm3-mode', + } + + def initialize_options(self): + self.bdist_base = None + self.rpm_base = None + self.dist_dir = None + self.python = None + self.fix_python = None + self.spec_only = None + self.binary_only = None + self.source_only = None + self.use_bzip2 = None + + self.distribution_name = None + self.group = None + self.release = None + self.serial = None + self.vendor = None + self.packager = None + self.doc_files = None + self.changelog = None + self.icon = None + + self.prep_script = None + self.build_script = None + self.install_script = None + self.clean_script = None + self.verify_script = None + self.pre_install = None + self.post_install = None + self.pre_uninstall = None + self.post_uninstall = None + self.prep = None + self.provides = None + self.requires = None + self.conflicts = None + self.build_requires = None + self.obsoletes = None + + self.keep_temp = 0 + self.use_rpm_opt_flags = 1 + self.rpm3_mode = 1 + self.no_autoreq = 0 + + self.force_arch = None + self.quiet = 0 + + def finalize_options(self): + self.set_undefined_options('bdist', ('bdist_base', 'bdist_base')) + if self.rpm_base is None: + if not self.rpm3_mode: + raise DistutilsOptionError("you must specify --rpm-base in RPM 2 mode") + self.rpm_base = os.path.join(self.bdist_base, "rpm") + + if self.python is None: + if self.fix_python: + self.python = sys.executable + else: + self.python = "python3" + elif self.fix_python: + raise DistutilsOptionError( + "--python and --fix-python are mutually exclusive options" + ) + + if os.name != 'posix': + raise DistutilsPlatformError( + "don't know how to create RPM distributions on platform %s" % os.name + ) + if self.binary_only and self.source_only: + raise DistutilsOptionError( + "cannot supply both '--source-only' and '--binary-only'" + ) + + # don't pass CFLAGS to pure python distributions + if not self.distribution.has_ext_modules(): + self.use_rpm_opt_flags = 0 + + self.set_undefined_options('bdist', ('dist_dir', 'dist_dir')) + self.finalize_package_data() + + def finalize_package_data(self): + self.ensure_string('group', "Development/Libraries") + self.ensure_string( + 'vendor', + f"{self.distribution.get_contact()} <{self.distribution.get_contact_email()}>", + ) + self.ensure_string('packager') + self.ensure_string_list('doc_files') + if isinstance(self.doc_files, list): + for readme in ('README', 'README.txt'): + if os.path.exists(readme) and readme not in self.doc_files: + self.doc_files.append(readme) + + self.ensure_string('release', "1") + self.ensure_string('serial') # should it be an int? + + self.ensure_string('distribution_name') + + self.ensure_string('changelog') + # Format changelog correctly + self.changelog = self._format_changelog(self.changelog) + + self.ensure_filename('icon') + + self.ensure_filename('prep_script') + self.ensure_filename('build_script') + self.ensure_filename('install_script') + self.ensure_filename('clean_script') + self.ensure_filename('verify_script') + self.ensure_filename('pre_install') + self.ensure_filename('post_install') + self.ensure_filename('pre_uninstall') + self.ensure_filename('post_uninstall') + + # XXX don't forget we punted on summaries and descriptions -- they + # should be handled here eventually! + + # Now *this* is some meta-data that belongs in the setup script... + self.ensure_string_list('provides') + self.ensure_string_list('requires') + self.ensure_string_list('conflicts') + self.ensure_string_list('build_requires') + self.ensure_string_list('obsoletes') + + self.ensure_string('force_arch') + + def run(self): # noqa: C901 + if DEBUG: + print("before _get_package_data():") + print("vendor =", self.vendor) + print("packager =", self.packager) + print("doc_files =", self.doc_files) + print("changelog =", self.changelog) + + # make directories + if self.spec_only: + spec_dir = self.dist_dir + self.mkpath(spec_dir) + else: + rpm_dir = {} + for d in ('SOURCES', 'SPECS', 'BUILD', 'RPMS', 'SRPMS'): + rpm_dir[d] = os.path.join(self.rpm_base, d) + self.mkpath(rpm_dir[d]) + spec_dir = rpm_dir['SPECS'] + + # Spec file goes into 'dist_dir' if '--spec-only specified', + # build/rpm. otherwise. + spec_path = os.path.join(spec_dir, "%s.spec" % self.distribution.get_name()) + self.execute( + write_file, (spec_path, self._make_spec_file()), "writing '%s'" % spec_path + ) + + if self.spec_only: # stop if requested + return + + # Make a source distribution and copy to SOURCES directory with + # optional icon. + saved_dist_files = self.distribution.dist_files[:] + sdist = self.reinitialize_command('sdist') + if self.use_bzip2: + sdist.formats = ['bztar'] + else: + sdist.formats = ['gztar'] + self.run_command('sdist') + self.distribution.dist_files = saved_dist_files + + source = sdist.get_archive_files()[0] + source_dir = rpm_dir['SOURCES'] + self.copy_file(source, source_dir) + + if self.icon: + if os.path.exists(self.icon): + self.copy_file(self.icon, source_dir) + else: + raise DistutilsFileError("icon file '%s' does not exist" % self.icon) + + # build package + log.info("building RPMs") + rpm_cmd = ['rpmbuild'] + + if self.source_only: # what kind of RPMs? + rpm_cmd.append('-bs') + elif self.binary_only: + rpm_cmd.append('-bb') + else: + rpm_cmd.append('-ba') + rpm_cmd.extend(['--define', '__python %s' % self.python]) + if self.rpm3_mode: + rpm_cmd.extend(['--define', '_topdir %s' % os.path.abspath(self.rpm_base)]) + if not self.keep_temp: + rpm_cmd.append('--clean') + + if self.quiet: + rpm_cmd.append('--quiet') + + rpm_cmd.append(spec_path) + # Determine the binary rpm names that should be built out of this spec + # file + # Note that some of these may not be really built (if the file + # list is empty) + nvr_string = "%{name}-%{version}-%{release}" + src_rpm = nvr_string + ".src.rpm" + non_src_rpm = "%{arch}/" + nvr_string + ".%{arch}.rpm" + q_cmd = rf"rpm -q --qf '{src_rpm} {non_src_rpm}\n' --specfile '{spec_path}'" + + out = os.popen(q_cmd) + try: + binary_rpms = [] + source_rpm = None + while True: + line = out.readline() + if not line: + break + ell = line.strip().split() + assert len(ell) == 2 + binary_rpms.append(ell[1]) + # The source rpm is named after the first entry in the spec file + if source_rpm is None: + source_rpm = ell[0] + + status = out.close() + if status: + raise DistutilsExecError("Failed to execute: %s" % repr(q_cmd)) + + finally: + out.close() + + self.spawn(rpm_cmd) + + if not self.dry_run: + if self.distribution.has_ext_modules(): + pyversion = get_python_version() + else: + pyversion = 'any' + + if not self.binary_only: + srpm = os.path.join(rpm_dir['SRPMS'], source_rpm) + assert os.path.exists(srpm) + self.move_file(srpm, self.dist_dir) + filename = os.path.join(self.dist_dir, source_rpm) + self.distribution.dist_files.append(('bdist_rpm', pyversion, filename)) + + if not self.source_only: + for rpm in binary_rpms: + rpm = os.path.join(rpm_dir['RPMS'], rpm) + if os.path.exists(rpm): + self.move_file(rpm, self.dist_dir) + filename = os.path.join(self.dist_dir, os.path.basename(rpm)) + self.distribution.dist_files.append(( + 'bdist_rpm', + pyversion, + filename, + )) + + def _dist_path(self, path): + return os.path.join(self.dist_dir, os.path.basename(path)) + + def _make_spec_file(self): # noqa: C901 + """Generate the text of an RPM spec file and return it as a + list of strings (one per line). + """ + # definitions and headers + spec_file = [ + '%define name ' + self.distribution.get_name(), + '%define version ' + self.distribution.get_version().replace('-', '_'), + '%define unmangled_version ' + self.distribution.get_version(), + '%define release ' + self.release.replace('-', '_'), + '', + 'Summary: ' + (self.distribution.get_description() or "UNKNOWN"), + ] + + # Workaround for #14443 which affects some RPM based systems such as + # RHEL6 (and probably derivatives) + vendor_hook = subprocess.getoutput('rpm --eval %{__os_install_post}') + # Generate a potential replacement value for __os_install_post (whilst + # normalizing the whitespace to simplify the test for whether the + # invocation of brp-python-bytecompile passes in __python): + vendor_hook = '\n'.join([ + ' %s \\' % line.strip() for line in vendor_hook.splitlines() + ]) + problem = "brp-python-bytecompile \\\n" + fixed = "brp-python-bytecompile %{__python} \\\n" + fixed_hook = vendor_hook.replace(problem, fixed) + if fixed_hook != vendor_hook: + spec_file.append('# Workaround for https://bugs.python.org/issue14443') + spec_file.append('%define __os_install_post ' + fixed_hook + '\n') + + # put locale summaries into spec file + # XXX not supported for now (hard to put a dictionary + # in a config file -- arg!) + # for locale in self.summaries.keys(): + # spec_file.append('Summary(%s): %s' % (locale, + # self.summaries[locale])) + + spec_file.extend([ + 'Name: %{name}', + 'Version: %{version}', + 'Release: %{release}', + ]) + + # XXX yuck! this filename is available from the "sdist" command, + # but only after it has run: and we create the spec file before + # running "sdist", in case of --spec-only. + if self.use_bzip2: + spec_file.append('Source0: %{name}-%{unmangled_version}.tar.bz2') + else: + spec_file.append('Source0: %{name}-%{unmangled_version}.tar.gz') + + spec_file.extend([ + 'License: ' + (self.distribution.get_license() or "UNKNOWN"), + 'Group: ' + self.group, + 'BuildRoot: %{_tmppath}/%{name}-%{version}-%{release}-buildroot', + 'Prefix: %{_prefix}', + ]) + + if not self.force_arch: + # noarch if no extension modules + if not self.distribution.has_ext_modules(): + spec_file.append('BuildArch: noarch') + else: + spec_file.append('BuildArch: %s' % self.force_arch) + + for field in ( + 'Vendor', + 'Packager', + 'Provides', + 'Requires', + 'Conflicts', + 'Obsoletes', + ): + val = getattr(self, field.lower()) + if isinstance(val, list): + spec_file.append('{}: {}'.format(field, ' '.join(val))) + elif val is not None: + spec_file.append(f'{field}: {val}') + + if self.distribution.get_url(): + spec_file.append('Url: ' + self.distribution.get_url()) + + if self.distribution_name: + spec_file.append('Distribution: ' + self.distribution_name) + + if self.build_requires: + spec_file.append('BuildRequires: ' + ' '.join(self.build_requires)) + + if self.icon: + spec_file.append('Icon: ' + os.path.basename(self.icon)) + + if self.no_autoreq: + spec_file.append('AutoReq: 0') + + spec_file.extend([ + '', + '%description', + self.distribution.get_long_description() or "", + ]) + + # put locale descriptions into spec file + # XXX again, suppressed because config file syntax doesn't + # easily support this ;-( + # for locale in self.descriptions.keys(): + # spec_file.extend([ + # '', + # '%description -l ' + locale, + # self.descriptions[locale], + # ]) + + # rpm scripts + # figure out default build script + def_setup_call = f"{self.python} {os.path.basename(sys.argv[0])}" + def_build = "%s build" % def_setup_call + if self.use_rpm_opt_flags: + def_build = 'env CFLAGS="$RPM_OPT_FLAGS" ' + def_build + + # insert contents of files + + # XXX this is kind of misleading: user-supplied options are files + # that we open and interpolate into the spec file, but the defaults + # are just text that we drop in as-is. Hmmm. + + install_cmd = ( + '%s install -O1 --root=$RPM_BUILD_ROOT ' '--record=INSTALLED_FILES' + ) % def_setup_call + + script_options = [ + ('prep', 'prep_script', "%setup -n %{name}-%{unmangled_version}"), + ('build', 'build_script', def_build), + ('install', 'install_script', install_cmd), + ('clean', 'clean_script', "rm -rf $RPM_BUILD_ROOT"), + ('verifyscript', 'verify_script', None), + ('pre', 'pre_install', None), + ('post', 'post_install', None), + ('preun', 'pre_uninstall', None), + ('postun', 'post_uninstall', None), + ] + + for rpm_opt, attr, default in script_options: + # Insert contents of file referred to, if no file is referred to + # use 'default' as contents of script + val = getattr(self, attr) + if val or default: + spec_file.extend([ + '', + '%' + rpm_opt, + ]) + if val: + with open(val) as f: + spec_file.extend(f.read().split('\n')) + else: + spec_file.append(default) + + # files section + spec_file.extend([ + '', + '%files -f INSTALLED_FILES', + '%defattr(-,root,root)', + ]) + + if self.doc_files: + spec_file.append('%doc ' + ' '.join(self.doc_files)) + + if self.changelog: + spec_file.extend([ + '', + '%changelog', + ]) + spec_file.extend(self.changelog) + + return spec_file + + def _format_changelog(self, changelog): + """Format the changelog correctly and convert it to a list of strings""" + if not changelog: + return changelog + new_changelog = [] + for line in changelog.strip().split('\n'): + line = line.strip() + if line[0] == '*': + new_changelog.extend(['', line]) + elif line[0] == '-': + new_changelog.append(line) + else: + new_changelog.append(' ' + line) + + # strip trailing newline inserted by first changelog entry + if not new_changelog[0]: + del new_changelog[0] + + return new_changelog diff --git a/venv/Lib/site-packages/setuptools/_distutils/command/build.py b/venv/Lib/site-packages/setuptools/_distutils/command/build.py new file mode 100644 index 0000000..d18ed50 --- /dev/null +++ b/venv/Lib/site-packages/setuptools/_distutils/command/build.py @@ -0,0 +1,153 @@ +"""distutils.command.build + +Implements the Distutils 'build' command.""" + +import os +import sys + +from ..core import Command +from ..errors import DistutilsOptionError +from ..util import get_platform + + +def show_compilers(): + from ..ccompiler import show_compilers + + show_compilers() + + +class build(Command): + description = "build everything needed to install" + + user_options = [ + ('build-base=', 'b', "base directory for build library"), + ('build-purelib=', None, "build directory for platform-neutral distributions"), + ('build-platlib=', None, "build directory for platform-specific distributions"), + ( + 'build-lib=', + None, + "build directory for all distribution (defaults to either " + + "build-purelib or build-platlib", + ), + ('build-scripts=', None, "build directory for scripts"), + ('build-temp=', 't', "temporary build directory"), + ( + 'plat-name=', + 'p', + "platform name to build for, if supported " + "(default: %s)" % get_platform(), + ), + ('compiler=', 'c', "specify the compiler type"), + ('parallel=', 'j', "number of parallel build jobs"), + ('debug', 'g', "compile extensions and libraries with debugging information"), + ('force', 'f', "forcibly build everything (ignore file timestamps)"), + ('executable=', 'e', "specify final destination interpreter path (build.py)"), + ] + + boolean_options = ['debug', 'force'] + + help_options = [ + ('help-compiler', None, "list available compilers", show_compilers), + ] + + def initialize_options(self): + self.build_base = 'build' + # these are decided only after 'build_base' has its final value + # (unless overridden by the user or client) + self.build_purelib = None + self.build_platlib = None + self.build_lib = None + self.build_temp = None + self.build_scripts = None + self.compiler = None + self.plat_name = None + self.debug = None + self.force = 0 + self.executable = None + self.parallel = None + + def finalize_options(self): # noqa: C901 + if self.plat_name is None: + self.plat_name = get_platform() + else: + # plat-name only supported for windows (other platforms are + # supported via ./configure flags, if at all). Avoid misleading + # other platforms. + if os.name != 'nt': + raise DistutilsOptionError( + "--plat-name only supported on Windows (try " + "using './configure --help' on your platform)" + ) + + plat_specifier = f".{self.plat_name}-{sys.implementation.cache_tag}" + + # Make it so Python 2.x and Python 2.x with --with-pydebug don't + # share the same build directories. Doing so confuses the build + # process for C modules + if hasattr(sys, 'gettotalrefcount'): + plat_specifier += '-pydebug' + + # 'build_purelib' and 'build_platlib' just default to 'lib' and + # 'lib.' under the base build directory. We only use one of + # them for a given distribution, though -- + if self.build_purelib is None: + self.build_purelib = os.path.join(self.build_base, 'lib') + if self.build_platlib is None: + self.build_platlib = os.path.join(self.build_base, 'lib' + plat_specifier) + + # 'build_lib' is the actual directory that we will use for this + # particular module distribution -- if user didn't supply it, pick + # one of 'build_purelib' or 'build_platlib'. + if self.build_lib is None: + if self.distribution.has_ext_modules(): + self.build_lib = self.build_platlib + else: + self.build_lib = self.build_purelib + + # 'build_temp' -- temporary directory for compiler turds, + # "build/temp." + if self.build_temp is None: + self.build_temp = os.path.join(self.build_base, 'temp' + plat_specifier) + if self.build_scripts is None: + self.build_scripts = os.path.join( + self.build_base, 'scripts-%d.%d' % sys.version_info[:2] + ) + + if self.executable is None and sys.executable: + self.executable = os.path.normpath(sys.executable) + + if isinstance(self.parallel, str): + try: + self.parallel = int(self.parallel) + except ValueError: + raise DistutilsOptionError("parallel should be an integer") + + def run(self): + # Run all relevant sub-commands. This will be some subset of: + # - build_py - pure Python modules + # - build_clib - standalone C libraries + # - build_ext - Python extensions + # - build_scripts - (Python) scripts + for cmd_name in self.get_sub_commands(): + self.run_command(cmd_name) + + # -- Predicates for the sub-command list --------------------------- + + def has_pure_modules(self): + return self.distribution.has_pure_modules() + + def has_c_libraries(self): + return self.distribution.has_c_libraries() + + def has_ext_modules(self): + return self.distribution.has_ext_modules() + + def has_scripts(self): + return self.distribution.has_scripts() + + sub_commands = [ + ('build_py', has_pure_modules), + ('build_clib', has_c_libraries), + ('build_ext', has_ext_modules), + ('build_scripts', has_scripts), + ] diff --git a/venv/Lib/site-packages/setuptools/_distutils/command/build_clib.py b/venv/Lib/site-packages/setuptools/_distutils/command/build_clib.py new file mode 100644 index 0000000..360575d --- /dev/null +++ b/venv/Lib/site-packages/setuptools/_distutils/command/build_clib.py @@ -0,0 +1,208 @@ +"""distutils.command.build_clib + +Implements the Distutils 'build_clib' command, to build a C/C++ library +that is included in the module distribution and needed by an extension +module.""" + + +# XXX this module has *lots* of code ripped-off quite transparently from +# build_ext.py -- not surprisingly really, as the work required to build +# a static library from a collection of C source files is not really all +# that different from what's required to build a shared object file from +# a collection of C source files. Nevertheless, I haven't done the +# necessary refactoring to account for the overlap in code between the +# two modules, mainly because a number of subtle details changed in the +# cut 'n paste. Sigh. + +import os +from distutils._log import log + +from ..core import Command +from ..errors import DistutilsSetupError +from ..sysconfig import customize_compiler + + +def show_compilers(): + from ..ccompiler import show_compilers + + show_compilers() + + +class build_clib(Command): + description = "build C/C++ libraries used by Python extensions" + + user_options = [ + ('build-clib=', 'b', "directory to build C/C++ libraries to"), + ('build-temp=', 't', "directory to put temporary build by-products"), + ('debug', 'g', "compile with debugging information"), + ('force', 'f', "forcibly build everything (ignore file timestamps)"), + ('compiler=', 'c', "specify the compiler type"), + ] + + boolean_options = ['debug', 'force'] + + help_options = [ + ('help-compiler', None, "list available compilers", show_compilers), + ] + + def initialize_options(self): + self.build_clib = None + self.build_temp = None + + # List of libraries to build + self.libraries = None + + # Compilation options for all libraries + self.include_dirs = None + self.define = None + self.undef = None + self.debug = None + self.force = 0 + self.compiler = None + + def finalize_options(self): + # This might be confusing: both build-clib and build-temp default + # to build-temp as defined by the "build" command. This is because + # I think that C libraries are really just temporary build + # by-products, at least from the point of view of building Python + # extensions -- but I want to keep my options open. + self.set_undefined_options( + 'build', + ('build_temp', 'build_clib'), + ('build_temp', 'build_temp'), + ('compiler', 'compiler'), + ('debug', 'debug'), + ('force', 'force'), + ) + + self.libraries = self.distribution.libraries + if self.libraries: + self.check_library_list(self.libraries) + + if self.include_dirs is None: + self.include_dirs = self.distribution.include_dirs or [] + if isinstance(self.include_dirs, str): + self.include_dirs = self.include_dirs.split(os.pathsep) + + # XXX same as for build_ext -- what about 'self.define' and + # 'self.undef' ? + + def run(self): + if not self.libraries: + return + + # Yech -- this is cut 'n pasted from build_ext.py! + from ..ccompiler import new_compiler + + self.compiler = new_compiler( + compiler=self.compiler, dry_run=self.dry_run, force=self.force + ) + customize_compiler(self.compiler) + + if self.include_dirs is not None: + self.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: + self.compiler.define_macro(name, value) + if self.undef is not None: + for macro in self.undef: + self.compiler.undefine_macro(macro) + + self.build_libraries(self.libraries) + + def check_library_list(self, libraries): + """Ensure that the list of libraries is valid. + + `library` is presumably provided as a command option 'libraries'. + This method checks that it is a list of 2-tuples, where the tuples + are (library_name, build_info_dict). + + Raise DistutilsSetupError if the structure is invalid anywhere; + just returns otherwise. + """ + if not isinstance(libraries, list): + raise DistutilsSetupError("'libraries' option must be a list of tuples") + + for lib in libraries: + if not isinstance(lib, tuple) and len(lib) != 2: + raise DistutilsSetupError("each element of 'libraries' must a 2-tuple") + + name, build_info = lib + + if not isinstance(name, str): + raise DistutilsSetupError( + "first element of each tuple in 'libraries' " + "must be a string (the library name)" + ) + + if '/' in name or (os.sep != '/' and os.sep in name): + raise DistutilsSetupError( + "bad library name '%s': " + "may not contain directory separators" % lib[0] + ) + + if not isinstance(build_info, dict): + raise DistutilsSetupError( + "second element of each tuple in 'libraries' " + "must be a dictionary (build info)" + ) + + def get_library_names(self): + # Assume the library list is valid -- 'check_library_list()' is + # called from 'finalize_options()', so it should be! + if not self.libraries: + return None + + lib_names = [] + for lib_name, _build_info in self.libraries: + lib_names.append(lib_name) + return lib_names + + def get_source_files(self): + self.check_library_list(self.libraries) + filenames = [] + for lib_name, build_info in self.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 + ) + + filenames.extend(sources) + return filenames + + 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) + + # 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') + objects = self.compiler.compile( + sources, + output_dir=self.build_temp, + macros=macros, + include_dirs=include_dirs, + 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( + objects, lib_name, output_dir=self.build_clib, debug=self.debug + ) diff --git a/venv/Lib/site-packages/setuptools/_distutils/command/build_ext.py b/venv/Lib/site-packages/setuptools/_distutils/command/build_ext.py new file mode 100644 index 0000000..06d949a --- /dev/null +++ b/venv/Lib/site-packages/setuptools/_distutils/command/build_ext.py @@ -0,0 +1,800 @@ +"""distutils.command.build_ext + +Implements the Distutils 'build_ext' command, for building extension +modules (currently limited to C extensions, should accommodate C++ +extensions ASAP).""" + +import contextlib +import os +import re +import sys +from distutils._log import log +from site import USER_BASE + +from .._modified import newer_group +from ..core import Command +from ..errors import ( + CCompilerError, + CompileError, + DistutilsError, + DistutilsOptionError, + DistutilsPlatformError, + DistutilsSetupError, +) +from ..extension import Extension +from ..sysconfig import customize_compiler, get_config_h_filename, get_python_version +from ..util import get_platform + +# An extension name is just a dot-separated list of Python NAMEs (ie. +# the same as a fully-qualified module name). +extension_name_re = re.compile(r'^[a-zA-Z_][a-zA-Z_0-9]*(\.[a-zA-Z_][a-zA-Z_0-9]*)*$') + + +def show_compilers(): + from ..ccompiler import show_compilers + + show_compilers() + + +class build_ext(Command): + description = "build C/C++ extensions (compile/link to build directory)" + + # XXX thoughts on how to deal with complex command-line options like + # these, i.e. how to make it so fancy_getopt can suck them off the + # command line and make it look like setup.py defined the appropriate + # lists of tuples of what-have-you. + # - each command needs a callback to process its command-line options + # - Command.__init__() needs access to its share of the whole + # command line (must ultimately come from + # Distribution.parse_command_line()) + # - it then calls the current command class' option-parsing + # callback to deal with weird options like -D, which have to + # parse the option text and churn out some custom data + # structure + # - that data structure (in this case, a list of 2-tuples) + # will then be present in the command object by the time + # we get to finalize_options() (i.e. the constructor + # takes care of both command-line and client options + # in between initialize_options() and finalize_options()) + + sep_by = " (separated by '%s')" % os.pathsep + user_options = [ + ('build-lib=', 'b', "directory for compiled extension modules"), + ('build-temp=', 't', "directory for temporary files (build by-products)"), + ( + 'plat-name=', + 'p', + "platform name to cross-compile for, if supported " + "(default: %s)" % get_platform(), + ), + ( + 'inplace', + 'i', + "ignore build-lib and put compiled extensions into the source " + + "directory alongside your pure Python modules", + ), + ( + 'include-dirs=', + 'I', + "list of directories to search for header files" + sep_by, + ), + ('define=', 'D', "C preprocessor macros to define"), + ('undef=', 'U', "C preprocessor macros to undefine"), + ('libraries=', 'l', "external C libraries to link with"), + ( + 'library-dirs=', + 'L', + "directories to search for external C libraries" + sep_by, + ), + ('rpath=', 'R', "directories to search for shared C libraries at runtime"), + ('link-objects=', 'O', "extra explicit link objects to include in the link"), + ('debug', 'g', "compile/link with debugging information"), + ('force', 'f', "forcibly build everything (ignore file timestamps)"), + ('compiler=', 'c', "specify the compiler type"), + ('parallel=', 'j', "number of parallel build jobs"), + ('swig-cpp', None, "make SWIG create C++ files (default is C)"), + ('swig-opts=', None, "list of SWIG command line options"), + ('swig=', None, "path to the SWIG executable"), + ('user', None, "add user include, library and rpath"), + ] + + boolean_options = ['inplace', 'debug', 'force', 'swig-cpp', 'user'] + + help_options = [ + ('help-compiler', None, "list available compilers", show_compilers), + ] + + def initialize_options(self): + self.extensions = None + self.build_lib = None + self.plat_name = None + self.build_temp = None + self.inplace = 0 + self.package = None + + self.include_dirs = None + self.define = None + self.undef = None + self.libraries = None + self.library_dirs = None + self.rpath = None + self.link_objects = None + self.debug = None + self.force = None + self.compiler = None + self.swig = None + self.swig_cpp = None + self.swig_opts = None + self.user = None + self.parallel = None + + @staticmethod + def _python_lib_dir(sysconfig): + """ + Resolve Python's library directory for building extensions + that rely on a shared Python library. + + See python/cpython#44264 and python/cpython#48686 + """ + if not sysconfig.get_config_var('Py_ENABLE_SHARED'): + return + + if sysconfig.python_build: + yield '.' + return + + if sys.platform == 'zos': + # On z/OS, a user is not required to install Python to + # a predetermined path, but can use Python portably + installed_dir = sysconfig.get_config_var('base') + lib_dir = sysconfig.get_config_var('platlibdir') + yield os.path.join(installed_dir, lib_dir) + else: + # building third party extensions + yield sysconfig.get_config_var('LIBDIR') + + def finalize_options(self): # noqa: C901 + from distutils import sysconfig + + self.set_undefined_options( + 'build', + ('build_lib', 'build_lib'), + ('build_temp', 'build_temp'), + ('compiler', 'compiler'), + ('debug', 'debug'), + ('force', 'force'), + ('parallel', 'parallel'), + ('plat_name', 'plat_name'), + ) + + if self.package is None: + self.package = self.distribution.ext_package + + self.extensions = self.distribution.ext_modules + + # Make sure Python's include directories (for Python.h, pyconfig.h, + # etc.) are in the include search path. + py_include = sysconfig.get_python_inc() + plat_py_include = sysconfig.get_python_inc(plat_specific=1) + if self.include_dirs is None: + self.include_dirs = self.distribution.include_dirs or [] + if isinstance(self.include_dirs, str): + self.include_dirs = self.include_dirs.split(os.pathsep) + + # If in a virtualenv, add its include directory + # Issue 16116 + if sys.exec_prefix != sys.base_exec_prefix: + self.include_dirs.append(os.path.join(sys.exec_prefix, 'include')) + + # Put the Python "system" include dir at the end, so that + # any local include dirs take precedence. + self.include_dirs.extend(py_include.split(os.path.pathsep)) + if plat_py_include != py_include: + self.include_dirs.extend(plat_py_include.split(os.path.pathsep)) + + self.ensure_string_list('libraries') + self.ensure_string_list('link_objects') + + # Life is easier if we're not forever checking for None, so + # simplify these options to empty lists if unset + if self.libraries is None: + self.libraries = [] + if self.library_dirs is None: + self.library_dirs = [] + elif isinstance(self.library_dirs, str): + self.library_dirs = self.library_dirs.split(os.pathsep) + + if self.rpath is None: + self.rpath = [] + elif isinstance(self.rpath, str): + self.rpath = self.rpath.split(os.pathsep) + + # for extensions under windows use different directories + # for Release and Debug builds. + # also Python's library directory must be appended to library_dirs + if os.name == 'nt': + # the 'libs' directory is for binary installs - we assume that + # must be the *native* platform. But we don't really support + # cross-compiling via a binary install anyway, so we let it go. + self.library_dirs.append(os.path.join(sys.exec_prefix, 'libs')) + if sys.base_exec_prefix != sys.prefix: # Issue 16116 + self.library_dirs.append(os.path.join(sys.base_exec_prefix, 'libs')) + if self.debug: + self.build_temp = os.path.join(self.build_temp, "Debug") + else: + self.build_temp = os.path.join(self.build_temp, "Release") + + # Append the source distribution include and library directories, + # this allows distutils on windows to work in the source tree + self.include_dirs.append(os.path.dirname(get_config_h_filename())) + self.library_dirs.append(sys.base_exec_prefix) + + # Use the .lib files for the correct architecture + if self.plat_name == 'win32': + suffix = 'win32' + else: + # win-amd64 + suffix = self.plat_name[4:] + new_lib = os.path.join(sys.exec_prefix, 'PCbuild') + if suffix: + new_lib = os.path.join(new_lib, suffix) + self.library_dirs.append(new_lib) + + # For extensions under Cygwin, Python's library directory must be + # appended to library_dirs + if sys.platform[:6] == 'cygwin': + if not sysconfig.python_build: + # building third party extensions + self.library_dirs.append( + os.path.join( + sys.prefix, "lib", "python" + get_python_version(), "config" + ) + ) + else: + # building python standard extensions + self.library_dirs.append('.') + + self.library_dirs.extend(self._python_lib_dir(sysconfig)) + + # The argument parsing will result in self.define being a string, but + # it has to be a list of 2-tuples. All the preprocessor symbols + # specified by the 'define' option will be set to '1'. Multiple + # symbols can be separated with commas. + + if self.define: + defines = self.define.split(',') + self.define = [(symbol, '1') for symbol in defines] + + # The option for macros to undefine is also a string from the + # option parsing, but has to be a list. Multiple symbols can also + # be separated with commas here. + if self.undef: + self.undef = self.undef.split(',') + + if self.swig_opts is None: + self.swig_opts = [] + else: + self.swig_opts = self.swig_opts.split(' ') + + # Finally add the user include and library directories if requested + if self.user: + user_include = os.path.join(USER_BASE, "include") + user_lib = os.path.join(USER_BASE, "lib") + if os.path.isdir(user_include): + self.include_dirs.append(user_include) + if os.path.isdir(user_lib): + self.library_dirs.append(user_lib) + self.rpath.append(user_lib) + + if isinstance(self.parallel, str): + try: + self.parallel = int(self.parallel) + except ValueError: + raise DistutilsOptionError("parallel should be an integer") + + def run(self): # noqa: C901 + from ..ccompiler import new_compiler + + # 'self.extensions', as supplied by setup.py, is a list of + # Extension instances. See the documentation for Extension (in + # distutils.extension) for details. + # + # For backwards compatibility with Distutils 0.8.2 and earlier, we + # also allow the 'extensions' list to be a list of tuples: + # (ext_name, build_info) + # where build_info is a dictionary containing everything that + # Extension instances do except the name, with a few things being + # differently named. We convert these 2-tuples to Extension + # instances as needed. + + if not self.extensions: + return + + # If we were asked to build any C/C++ libraries, make sure that the + # directory where we put them is in the library search path for + # linking extensions. + if self.distribution.has_c_libraries(): + build_clib = self.get_finalized_command('build_clib') + self.libraries.extend(build_clib.get_library_names() or []) + self.library_dirs.append(build_clib.build_clib) + + # Setup the CCompiler object that we'll use to do all the + # compiling and linking + self.compiler = new_compiler( + compiler=self.compiler, + verbose=self.verbose, + dry_run=self.dry_run, + force=self.force, + ) + customize_compiler(self.compiler) + # If we are cross-compiling, init the compiler now (if we are not + # cross-compiling, init would not hurt, but people may rely on + # late initialization of compiler even if they shouldn't...) + if os.name == 'nt' and self.plat_name != get_platform(): + self.compiler.initialize(self.plat_name) + + # And make sure that any compile/link-related options (which might + # come from the command-line or from the setup script) are set in + # that CCompiler object -- that way, they automatically apply to + # all compiling and linking done here. + if self.include_dirs is not None: + self.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: + self.compiler.define_macro(name, value) + if self.undef is not None: + for macro in self.undef: + self.compiler.undefine_macro(macro) + if self.libraries is not None: + self.compiler.set_libraries(self.libraries) + if self.library_dirs is not None: + self.compiler.set_library_dirs(self.library_dirs) + if self.rpath is not None: + self.compiler.set_runtime_library_dirs(self.rpath) + if self.link_objects is not None: + self.compiler.set_link_objects(self.link_objects) + + # Now actually compile and link everything. + self.build_extensions() + + def check_extensions_list(self, extensions): # noqa: C901 + """Ensure that the list of extensions (presumably provided as a + command option 'extensions') is valid, i.e. it is a list of + Extension objects. We also support the old-style list of 2-tuples, + where the tuples are (ext_name, build_info), which are converted to + Extension instances here. + + Raise DistutilsSetupError if the structure is invalid anywhere; + just returns otherwise. + """ + if not isinstance(extensions, list): + raise DistutilsSetupError( + "'ext_modules' option must be a list of Extension instances" + ) + + for i, ext in enumerate(extensions): + if isinstance(ext, Extension): + continue # OK! (assume type-checking done + # by Extension constructor) + + if not isinstance(ext, tuple) or len(ext) != 2: + raise DistutilsSetupError( + "each element of 'ext_modules' option must be an " + "Extension instance or 2-tuple" + ) + + ext_name, build_info = ext + + log.warning( + "old-style (ext_name, build_info) tuple found in " + "ext_modules for extension '%s' " + "-- please convert to Extension instance", + ext_name, + ) + + if not (isinstance(ext_name, str) and extension_name_re.match(ext_name)): + raise DistutilsSetupError( + "first element of each tuple in 'ext_modules' " + "must be the extension name (a string)" + ) + + if not isinstance(build_info, dict): + raise DistutilsSetupError( + "second element of each tuple in 'ext_modules' " + "must be a dictionary (build info)" + ) + + # OK, the (ext_name, build_info) dict is type-safe: convert it + # to an Extension instance. + ext = Extension(ext_name, build_info['sources']) + + # Easy stuff: one-to-one mapping from dict elements to + # instance attributes. + for key in ( + 'include_dirs', + 'library_dirs', + 'libraries', + 'extra_objects', + 'extra_compile_args', + 'extra_link_args', + ): + val = build_info.get(key) + if val is not None: + setattr(ext, key, val) + + # Medium-easy stuff: same syntax/semantics, different names. + ext.runtime_library_dirs = build_info.get('rpath') + if 'def_file' in build_info: + log.warning("'def_file' element of build info dict no longer supported") + + # Non-trivial stuff: 'macros' split into 'define_macros' + # and 'undef_macros'. + macros = build_info.get('macros') + if macros: + ext.define_macros = [] + ext.undef_macros = [] + for macro in macros: + if not (isinstance(macro, tuple) and len(macro) in (1, 2)): + raise DistutilsSetupError( + "'macros' element of build info dict " + "must be 1- or 2-tuple" + ) + if len(macro) == 1: + ext.undef_macros.append(macro[0]) + elif len(macro) == 2: + ext.define_macros.append(macro) + + extensions[i] = ext + + def get_source_files(self): + self.check_extensions_list(self.extensions) + filenames = [] + + # Wouldn't it be neat if we knew the names of header files too... + for ext in self.extensions: + filenames.extend(ext.sources) + return filenames + + def get_outputs(self): + # Sanity check the 'extensions' list -- can't assume this is being + # done in the same run as a 'build_extensions()' call (in fact, we + # can probably assume that it *isn't*!). + self.check_extensions_list(self.extensions) + + # And build the list of output (built) filenames. Note that this + # ignores the 'inplace' flag, and assumes everything goes in the + # "build" tree. + outputs = [] + for ext in self.extensions: + outputs.append(self.get_ext_fullpath(ext.name)) + return outputs + + def build_extensions(self): + # First, sanity-check the 'extensions' list + self.check_extensions_list(self.extensions) + if self.parallel: + self._build_extensions_parallel() + else: + self._build_extensions_serial() + + def _build_extensions_parallel(self): + workers = self.parallel + if self.parallel is True: + workers = os.cpu_count() # may return None + try: + from concurrent.futures import ThreadPoolExecutor + except ImportError: + workers = None + + if workers is None: + self._build_extensions_serial() + return + + with ThreadPoolExecutor(max_workers=workers) as executor: + futures = [ + executor.submit(self.build_extension, ext) for ext in self.extensions + ] + for ext, fut in zip(self.extensions, futures): + with self._filter_build_errors(ext): + fut.result() + + def _build_extensions_serial(self): + for ext in self.extensions: + with self._filter_build_errors(ext): + self.build_extension(ext) + + @contextlib.contextmanager + def _filter_build_errors(self, ext): + try: + yield + except (CCompilerError, DistutilsError, CompileError) as e: + if not ext.optional: + raise + self.warn(f'building extension "{ext.name}" failed: {e}') + + def build_extension(self, ext): + sources = ext.sources + if sources is None or not isinstance(sources, (list, tuple)): + raise DistutilsSetupError( + "in 'ext_modules' option (extension '%s'), " + "'sources' must be present and must be " + "a list of source filenames" % ext.name + ) + # sort to make the resulting .so file build reproducible + sources = sorted(sources) + + ext_path = self.get_ext_fullpath(ext.name) + depends = sources + ext.depends + if not (self.force or newer_group(depends, ext_path, 'newer')): + log.debug("skipping '%s' extension (up-to-date)", ext.name) + return + else: + log.info("building '%s' extension", ext.name) + + # First, scan the sources for SWIG definition files (.i), run + # SWIG on 'em to create .c files, and modify the sources list + # accordingly. + sources = self.swig_sources(sources, ext) + + # Next, compile the source code to object files. + + # XXX not honouring 'define_macros' or 'undef_macros' -- the + # CCompiler API needs to change to accommodate this, and I + # want to do one thing at a time! + + # Two possible sources for extra compiler arguments: + # - 'extra_compile_args' in Extension object + # - CFLAGS environment variable (not particularly + # elegant, but people seem to expect it and I + # guess it's useful) + # The environment variable should take precedence, and + # any sensible compiler will give precedence to later + # command line args. Hence we combine them in order: + extra_args = ext.extra_compile_args or [] + + macros = ext.define_macros[:] + for undef in ext.undef_macros: + macros.append((undef,)) + + objects = self.compiler.compile( + sources, + output_dir=self.build_temp, + macros=macros, + include_dirs=ext.include_dirs, + debug=self.debug, + extra_postargs=extra_args, + depends=ext.depends, + ) + + # XXX outdated variable, kept here in case third-part code + # needs it. + self._built_objects = objects[:] + + # Now link the object files together into a "shared object" -- + # of course, first we have to figure out all the other things + # that go into the mix. + if ext.extra_objects: + objects.extend(ext.extra_objects) + extra_args = ext.extra_link_args or [] + + # Detect target language, if not provided + language = ext.language or self.compiler.detect_language(sources) + + self.compiler.link_shared_object( + objects, + ext_path, + libraries=self.get_libraries(ext), + library_dirs=ext.library_dirs, + runtime_library_dirs=ext.runtime_library_dirs, + extra_postargs=extra_args, + export_symbols=self.get_export_symbols(ext), + debug=self.debug, + build_temp=self.build_temp, + target_lang=language, + ) + + def swig_sources(self, sources, extension): + """Walk the list of source files in 'sources', looking for SWIG + interface (.i) files. Run SWIG on all that are found, and + return a modified 'sources' list with SWIG source files replaced + by the generated C (or C++) files. + """ + new_sources = [] + swig_sources = [] + swig_targets = {} + + # XXX this drops generated C/C++ files into the source tree, which + # is fine for developers who want to distribute the generated + # source -- but there should be an option to put SWIG output in + # the temp dir. + + if self.swig_cpp: + log.warning("--swig-cpp is deprecated - use --swig-opts=-c++") + + if ( + self.swig_cpp + or ('-c++' in self.swig_opts) + or ('-c++' in extension.swig_opts) + ): + target_ext = '.cpp' + else: + target_ext = '.c' + + for source in sources: + (base, ext) = os.path.splitext(source) + if ext == ".i": # SWIG interface file + new_sources.append(base + '_wrap' + target_ext) + swig_sources.append(source) + swig_targets[source] = new_sources[-1] + else: + new_sources.append(source) + + if not swig_sources: + return new_sources + + swig = self.swig or self.find_swig() + swig_cmd = [swig, "-python"] + swig_cmd.extend(self.swig_opts) + if self.swig_cpp: + swig_cmd.append("-c++") + + # Do not override commandline arguments + if not self.swig_opts: + for o in extension.swig_opts: + swig_cmd.append(o) + + for source in swig_sources: + target = swig_targets[source] + log.info("swigging %s to %s", source, target) + self.spawn(swig_cmd + ["-o", target, source]) + + return new_sources + + def find_swig(self): + """Return the name of the SWIG executable. On Unix, this is + just "swig" -- it should be in the PATH. Tries a bit harder on + Windows. + """ + if os.name == "posix": + return "swig" + elif os.name == "nt": + # Look for SWIG in its standard installation directory on + # Windows (or so I presume!). If we find it there, great; + # if not, act like Unix and assume it's in the PATH. + for vers in ("1.3", "1.2", "1.1"): + fn = os.path.join("c:\\swig%s" % vers, "swig.exe") + if os.path.isfile(fn): + return fn + else: + return "swig.exe" + else: + raise DistutilsPlatformError( + "I don't know how to find (much less run) SWIG " + "on platform '%s'" % os.name + ) + + # -- Name generators ----------------------------------------------- + # (extension names, filenames, whatever) + def get_ext_fullpath(self, ext_name): + """Returns the path of the filename for a given extension. + + The file is located in `build_lib` or directly in the package + (inplace option). + """ + fullname = self.get_ext_fullname(ext_name) + modpath = fullname.split('.') + filename = self.get_ext_filename(modpath[-1]) + + if not self.inplace: + # no further work needed + # returning : + # build_dir/package/path/filename + filename = os.path.join(*modpath[:-1] + [filename]) + return os.path.join(self.build_lib, filename) + + # the inplace option requires to find the package directory + # using the build_py command for that + package = '.'.join(modpath[0:-1]) + build_py = self.get_finalized_command('build_py') + package_dir = os.path.abspath(build_py.get_package_dir(package)) + + # returning + # package_dir/filename + return os.path.join(package_dir, filename) + + def get_ext_fullname(self, ext_name): + """Returns the fullname of a given extension name. + + Adds the `package.` prefix""" + if self.package is None: + return ext_name + else: + return self.package + '.' + ext_name + + def get_ext_filename(self, ext_name): + r"""Convert the name of an extension (eg. "foo.bar") into the name + of the file from which it will be loaded (eg. "foo/bar.so", or + "foo\bar.pyd"). + """ + from ..sysconfig import get_config_var + + ext_path = ext_name.split('.') + ext_suffix = get_config_var('EXT_SUFFIX') + return os.path.join(*ext_path) + ext_suffix + + def get_export_symbols(self, ext): + """Return the list of symbols that a shared extension has to + export. This either uses 'ext.export_symbols' or, if it's not + provided, "PyInit_" + module_name. Only relevant on Windows, where + the .pyd file (DLL) must export the module "PyInit_" function. + """ + name = ext.name.split('.')[-1] + try: + # Unicode module name support as defined in PEP-489 + # https://peps.python.org/pep-0489/#export-hook-name + name.encode('ascii') + except UnicodeEncodeError: + suffix = 'U_' + name.encode('punycode').replace(b'-', b'_').decode('ascii') + else: + suffix = "_" + name + + initfunc_name = "PyInit" + suffix + if initfunc_name not in ext.export_symbols: + ext.export_symbols.append(initfunc_name) + return ext.export_symbols + + def get_libraries(self, ext): # noqa: C901 + """Return the list of libraries to link against when building a + shared extension. On most platforms, this is just 'ext.libraries'; + on Windows, we add the Python library (eg. python20.dll). + """ + # The python library is always needed on Windows. For MSVC, this + # is redundant, since the library is mentioned in a pragma in + # pyconfig.h that MSVC groks. The other Windows compilers all seem + # to need it mentioned explicitly, though, so that's what we do. + # Append '_d' to the python import library on debug builds. + if sys.platform == "win32": + from .._msvccompiler import MSVCCompiler + + if not isinstance(self.compiler, MSVCCompiler): + template = "python%d%d" + if self.debug: + template = template + '_d' + pythonlib = template % ( + sys.hexversion >> 24, + (sys.hexversion >> 16) & 0xFF, + ) + # don't extend ext.libraries, it may be shared with other + # extensions, it is a reference to the original list + return ext.libraries + [pythonlib] + else: + # On Android only the main executable and LD_PRELOADs are considered + # to be RTLD_GLOBAL, all the dependencies of the main executable + # remain RTLD_LOCAL and so the shared libraries must be linked with + # libpython when python is built with a shared python library (issue + # bpo-21536). + # On Cygwin (and if required, other POSIX-like platforms based on + # Windows like MinGW) it is simply necessary that all symbols in + # shared libraries are resolved at link time. + from ..sysconfig import get_config_var + + link_libpython = False + if get_config_var('Py_ENABLE_SHARED'): + # A native build on an Android device or on Cygwin + if hasattr(sys, 'getandroidapilevel'): + link_libpython = True + elif sys.platform == 'cygwin': + link_libpython = True + elif '_PYTHON_HOST_PLATFORM' in os.environ: + # We are cross-compiling for one of the relevant platforms + if get_config_var('ANDROID_API_LEVEL') != 0: + link_libpython = True + elif get_config_var('MACHDEP') == 'cygwin': + link_libpython = True + + if link_libpython: + ldversion = get_config_var('LDVERSION') + return ext.libraries + ['python' + ldversion] + + return ext.libraries diff --git a/venv/Lib/site-packages/setuptools/_distutils/command/build_py.py b/venv/Lib/site-packages/setuptools/_distutils/command/build_py.py new file mode 100644 index 0000000..56e6fa2 --- /dev/null +++ b/venv/Lib/site-packages/setuptools/_distutils/command/build_py.py @@ -0,0 +1,406 @@ +"""distutils.command.build_py + +Implements the Distutils 'build_py' command.""" + +import glob +import importlib.util +import os +import sys +from distutils._log import log + +from ..core import Command +from ..errors import DistutilsFileError, DistutilsOptionError +from ..util import convert_path + + +class build_py(Command): + description = "\"build\" pure Python modules (copy to build directory)" + + user_options = [ + ('build-lib=', 'd', "directory to \"build\" (copy) to"), + ('compile', 'c', "compile .py to .pyc"), + ('no-compile', None, "don't compile .py files [default]"), + ( + 'optimize=', + 'O', + "also compile with optimization: -O1 for \"python -O\", " + "-O2 for \"python -OO\", and -O0 to disable [default: -O0]", + ), + ('force', 'f', "forcibly build everything (ignore file timestamps)"), + ] + + boolean_options = ['compile', 'force'] + negative_opt = {'no-compile': 'compile'} + + def initialize_options(self): + self.build_lib = None + self.py_modules = None + self.package = None + self.package_data = None + self.package_dir = None + self.compile = 0 + self.optimize = 0 + self.force = None + + def finalize_options(self): + self.set_undefined_options( + 'build', ('build_lib', 'build_lib'), ('force', 'force') + ) + + # Get the distribution options that are aliases for build_py + # options -- list of packages and list of modules. + self.packages = self.distribution.packages + self.py_modules = self.distribution.py_modules + self.package_data = self.distribution.package_data + self.package_dir = {} + if self.distribution.package_dir: + for name, path in self.distribution.package_dir.items(): + self.package_dir[name] = convert_path(path) + self.data_files = self.get_data_files() + + # Ick, copied straight from install_lib.py (fancy_getopt needs a + # type system! Hell, *everything* needs a type system!!!) + if not isinstance(self.optimize, int): + try: + self.optimize = int(self.optimize) + assert 0 <= self.optimize <= 2 + except (ValueError, AssertionError): + raise DistutilsOptionError("optimize must be 0, 1, or 2") + + def run(self): + # XXX copy_file by default preserves atime and mtime. IMHO this is + # the right thing to do, but perhaps it should be an option -- in + # particular, a site administrator might want installed files to + # reflect the time of installation rather than the last + # modification time before the installed release. + + # XXX copy_file by default preserves mode, which appears to be the + # wrong thing to do: if a file is read-only in the working + # directory, we want it to be installed read/write so that the next + # installation of the same module distribution can overwrite it + # without problems. (This might be a Unix-specific issue.) Thus + # we turn off 'preserve_mode' when copying to the build directory, + # since the build directory is supposed to be exactly what the + # installation will look like (ie. we preserve mode when + # installing). + + # Two options control which modules will be installed: 'packages' + # and 'py_modules'. The former lets us work with whole packages, not + # specifying individual modules at all; the latter is for + # specifying modules one-at-a-time. + + if self.py_modules: + self.build_modules() + if self.packages: + self.build_packages() + self.build_package_data() + + self.byte_compile(self.get_outputs(include_bytecode=0)) + + def get_data_files(self): + """Generate list of '(package,src_dir,build_dir,filenames)' tuples""" + data = [] + if not self.packages: + return data + for package in self.packages: + # 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('.'))) + + # Length of path to strip from found files + plen = 0 + if src_dir: + plen = len(src_dir) + 1 + + # Strip directory from globbed filenames + filenames = [file[plen:] for file in self.find_data_files(package, src_dir)] + data.append((package, src_dir, build_dir, filenames)) + return data + + def find_data_files(self, package, src_dir): + """Return filenames for package's data files in 'src_dir'""" + globs = self.package_data.get('', []) + self.package_data.get(package, []) + files = [] + for pattern in globs: + # Each pattern has to be converted to a platform-specific path + filelist = glob.glob( + os.path.join(glob.escape(src_dir), convert_path(pattern)) + ) + # Files that match more than one pattern are only added once + files.extend([ + fn for fn in filelist if fn not in files and os.path.isfile(fn) + ]) + return 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)) + self.copy_file( + os.path.join(src_dir, filename), target, preserve_mode=False + ) + + def get_package_dir(self, package): + """Return the directory, relative to the top of the source + distribution, where package 'package' should be found + (at least according to the 'package_dir' option, if any).""" + path = package.split('.') + + if not self.package_dir: + if path: + return os.path.join(*path) + else: + return '' + else: + tail = [] + while path: + try: + pdir = self.package_dir['.'.join(path)] + except KeyError: + tail.insert(0, path[-1]) + del path[-1] + else: + tail.insert(0, pdir) + return os.path.join(*tail) + else: + # Oops, got all the way through 'path' without finding a + # match in package_dir. If package_dir defines a directory + # for the root (nameless) package, then fallback on it; + # otherwise, we might as well have not consulted + # package_dir at all, as we just use the directory implied + # by 'tail' (which should be the same as the original value + # of 'path' at this point). + pdir = self.package_dir.get('') + if pdir is not None: + tail.insert(0, pdir) + + if tail: + return os.path.join(*tail) + else: + return '' + + def check_package(self, package, package_dir): + # Empty dir name means current directory, which we can probably + # assume exists. Also, os.path.exists and isdir don't know about + # my "empty string means current dir" convention, so we have to + # circumvent them. + if package_dir != "": + if not os.path.exists(package_dir): + raise DistutilsFileError( + "package directory '%s' does not exist" % package_dir + ) + if not os.path.isdir(package_dir): + raise DistutilsFileError( + "supposed package directory '%s' exists, " + "but is not a directory" % package_dir + ) + + # Directories without __init__.py are namespace packages (PEP 420). + if package: + init_py = os.path.join(package_dir, "__init__.py") + if os.path.isfile(init_py): + return init_py + + # Either not in a package at all (__init__.py not expected), or + # __init__.py doesn't exist -- so don't return the filename. + return None + + def check_module(self, module, module_file): + if not os.path.isfile(module_file): + log.warning("file %s (for module %s) not found", module_file, module) + return False + else: + return True + + def find_package_modules(self, package, package_dir): + self.check_package(package, package_dir) + module_files = glob.glob(os.path.join(glob.escape(package_dir), "*.py")) + modules = [] + setup_script = os.path.abspath(self.distribution.script_name) + + for f in module_files: + abs_f = os.path.abspath(f) + if abs_f != setup_script: + module = os.path.splitext(os.path.basename(f))[0] + modules.append((package, module, f)) + else: + self.debug_print("excluding %s" % setup_script) + return modules + + def find_modules(self): + """Finds individually-specified Python modules, ie. those listed by + module name in 'self.py_modules'. Returns a list of tuples (package, + module_base, filename): 'package' is a tuple of the path through + package-space to the module; 'module_base' is the bare (no + packages, no dots) module name, and 'filename' is the path to the + ".py" file (relative to the distribution root) that implements the + module. + """ + # Map package names to tuples of useful info about the package: + # (package_dir, checked) + # package_dir - the directory where we'll find source files for + # this package + # checked - true if we have checked that the package directory + # is valid (exists, contains __init__.py, ... ?) + packages = {} + + # List of (package, module, filename) tuples to return + modules = [] + + # We treat modules-in-packages almost the same as toplevel modules, + # just the "package" for a toplevel is empty (either an empty + # string or empty list, depending on context). Differences: + # - don't check for __init__.py in directory for empty package + for module in self.py_modules: + path = module.split('.') + package = '.'.join(path[0:-1]) + module_base = path[-1] + + try: + (package_dir, checked) = packages[package] + except KeyError: + package_dir = self.get_package_dir(package) + checked = 0 + + if not checked: + init_py = self.check_package(package, package_dir) + packages[package] = (package_dir, 1) + if init_py: + modules.append((package, "__init__", init_py)) + + # XXX perhaps we should also check for just .pyc files + # (so greedy closed-source bastards can distribute Python + # modules too) + module_file = os.path.join(package_dir, module_base + ".py") + if not self.check_module(module, module_file): + continue + + modules.append((package, module_base, module_file)) + + return modules + + def find_all_modules(self): + """Compute the list of all modules that will be built, whether + they are specified one-module-at-a-time ('self.py_modules') or + by whole packages ('self.packages'). Return a list of tuples + (package, module, module_file), just like 'find_modules()' and + 'find_package_modules()' do.""" + modules = [] + if self.py_modules: + modules.extend(self.find_modules()) + if self.packages: + for package in self.packages: + package_dir = self.get_package_dir(package) + m = self.find_package_modules(package, package_dir) + modules.extend(m) + return modules + + def get_source_files(self): + return [module[-1] for module in self.find_all_modules()] + + def get_module_outfile(self, build_dir, package, module): + outfile_path = [build_dir] + list(package) + [module + ".py"] + return os.path.join(*outfile_path) + + def get_outputs(self, include_bytecode=1): + modules = self.find_all_modules() + outputs = [] + for package, module, _module_file in modules: + package = package.split('.') + filename = self.get_module_outfile(self.build_lib, package, module) + outputs.append(filename) + if include_bytecode: + if self.compile: + outputs.append( + importlib.util.cache_from_source(filename, optimization='') + ) + if self.optimize > 0: + outputs.append( + importlib.util.cache_from_source( + filename, optimization=self.optimize + ) + ) + + outputs += [ + os.path.join(build_dir, filename) + for package, src_dir, build_dir, filenames in self.data_files + for filename in filenames + ] + + return outputs + + def build_module(self, module, module_file, package): + if isinstance(package, str): + package = package.split('.') + elif not isinstance(package, (list, tuple)): + raise TypeError( + "'package' must be a string (dot-separated), list, or tuple" + ) + + # Now put the module source file into the "build" area -- this is + # easy, we just copy it somewhere under self.build_lib (the build + # directory for Python source). + outfile = self.get_module_outfile(self.build_lib, package, module) + dir = os.path.dirname(outfile) + self.mkpath(dir) + return self.copy_file(module_file, outfile, preserve_mode=0) + + def build_modules(self): + modules = self.find_modules() + for package, module, module_file in modules: + # Now "build" the module -- ie. copy the source file to + # self.build_lib (the build directory for Python source). + # (Actually, it gets copied to the directory for this package + # under self.build_lib.) + self.build_module(module, module_file, package) + + def build_packages(self): + for package in self.packages: + # Get list of (package, module, module_file) tuples based on + # scanning the package directory. 'package' is only included + # in the tuple so that 'find_modules()' and + # 'find_package_tuples()' have a consistent interface; it's + # ignored here (apart from a sanity check). Also, 'module' is + # the *unqualified* module name (ie. no dots, no package -- we + # already know its package!), and 'module_file' is the path to + # the .py file, relative to the current directory + # (ie. including 'package_dir'). + package_dir = self.get_package_dir(package) + modules = self.find_package_modules(package, package_dir) + + # Now loop over the modules we found, "building" each one (just + # copy it to self.build_lib). + for package_, module, module_file in modules: + assert package == package_ + self.build_module(module, module_file, package) + + def byte_compile(self, files): + if sys.dont_write_bytecode: + self.warn('byte-compiling is disabled, skipping.') + return + + from ..util import byte_compile + + prefix = self.build_lib + if prefix[-1] != os.sep: + prefix = prefix + os.sep + + # XXX this code is essentially the same as the 'byte_compile() + # method of the "install_lib" command, except for the determination + # of the 'prefix' string. Hmmm. + if self.compile: + byte_compile( + files, optimize=0, force=self.force, prefix=prefix, dry_run=self.dry_run + ) + if self.optimize > 0: + byte_compile( + files, + optimize=self.optimize, + force=self.force, + prefix=prefix, + dry_run=self.dry_run, + ) diff --git a/venv/Lib/site-packages/setuptools/_distutils/command/build_scripts.py b/venv/Lib/site-packages/setuptools/_distutils/command/build_scripts.py new file mode 100644 index 0000000..5f3902a --- /dev/null +++ b/venv/Lib/site-packages/setuptools/_distutils/command/build_scripts.py @@ -0,0 +1,170 @@ +"""distutils.command.build_scripts + +Implements the Distutils 'build_scripts' command.""" + +import os +import re +import tokenize +from distutils import sysconfig +from distutils._log import log +from stat import ST_MODE + +from .._modified import newer +from ..core import Command +from ..util import convert_path + +shebang_pattern = re.compile('^#!.*python[0-9.]*([ \t].*)?$') +""" +Pattern matching a Python interpreter indicated in first line of a script. +""" + +# for Setuptools compatibility +first_line_re = shebang_pattern + + +class build_scripts(Command): + description = "\"build\" scripts (copy and fixup #! line)" + + user_options = [ + ('build-dir=', 'd', "directory to \"build\" (copy) to"), + ('force', 'f', "forcibly build everything (ignore file timestamps"), + ('executable=', 'e', "specify final destination interpreter path"), + ] + + boolean_options = ['force'] + + def initialize_options(self): + self.build_dir = None + self.scripts = None + self.force = None + self.executable = None + + def finalize_options(self): + self.set_undefined_options( + 'build', + ('build_scripts', 'build_dir'), + ('force', 'force'), + ('executable', 'executable'), + ) + self.scripts = self.distribution.scripts + + def get_source_files(self): + return self.scripts + + def run(self): + if not self.scripts: + return + self.copy_scripts() + + def copy_scripts(self): + """ + Copy each script listed in ``self.scripts``. + + If a script is marked as a Python script (first line matches + 'shebang_pattern', i.e. starts with ``#!`` and contains + "python"), then adjust in the copy the first line to refer to + the current Python interpreter. + """ + self.mkpath(self.build_dir) + outfiles = [] + updated_files = [] + for script in self.scripts: + self._copy_script(script, outfiles, updated_files) + + self._change_modes(outfiles) + + return outfiles, updated_files + + def _copy_script(self, script, outfiles, updated_files): # noqa: C901 + shebang_match = None + script = convert_path(script) + outfile = os.path.join(self.build_dir, os.path.basename(script)) + outfiles.append(outfile) + + if not self.force and not newer(script, outfile): + log.debug("not copying %s (up-to-date)", script) + return + + # Always open the file, but ignore failures in dry-run mode + # in order to attempt to copy directly. + try: + f = tokenize.open(script) + except OSError: + if not self.dry_run: + raise + f = None + else: + first_line = f.readline() + if not first_line: + self.warn("%s is an empty file (skipping)" % script) + return + + shebang_match = shebang_pattern.match(first_line) + + updated_files.append(outfile) + if shebang_match: + log.info("copying and adjusting %s -> %s", script, self.build_dir) + if not self.dry_run: + if not sysconfig.python_build: + executable = self.executable + else: + executable = os.path.join( + sysconfig.get_config_var("BINDIR"), + "python{}{}".format( + sysconfig.get_config_var("VERSION"), + sysconfig.get_config_var("EXE"), + ), + ) + post_interp = shebang_match.group(1) or '' + shebang = "#!" + executable + post_interp + "\n" + self._validate_shebang(shebang, f.encoding) + with open(outfile, "w", encoding=f.encoding) as outf: + outf.write(shebang) + outf.writelines(f.readlines()) + if f: + f.close() + else: + if f: + f.close() + self.copy_file(script, outfile) + + def _change_modes(self, outfiles): + if os.name != 'posix': + return + + for file in outfiles: + self._change_mode(file) + + def _change_mode(self, file): + if self.dry_run: + log.info("changing mode of %s", file) + return + + oldmode = os.stat(file)[ST_MODE] & 0o7777 + newmode = (oldmode | 0o555) & 0o7777 + if newmode != oldmode: + log.info("changing mode of %s from %o to %o", file, oldmode, newmode) + os.chmod(file, newmode) + + @staticmethod + def _validate_shebang(shebang, encoding): + # 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 encodable to + # UTF-8. + try: + shebang.encode('utf-8') + except UnicodeEncodeError: + raise ValueError(f"The shebang ({shebang!r}) is not encodable to utf-8") + + # If the script is encoded to a custom encoding (use a + # #coding:xxx cookie), the shebang has to be encodable to + # the script encoding too. + try: + shebang.encode(encoding) + except UnicodeEncodeError: + raise ValueError( + f"The shebang ({shebang!r}) is not encodable " + f"to the script encoding ({encoding})" + ) diff --git a/venv/Lib/site-packages/setuptools/_distutils/command/check.py b/venv/Lib/site-packages/setuptools/_distutils/command/check.py new file mode 100644 index 0000000..28599e1 --- /dev/null +++ b/venv/Lib/site-packages/setuptools/_distutils/command/check.py @@ -0,0 +1,155 @@ +"""distutils.command.check + +Implements the Distutils 'check' command. +""" + +import contextlib + +from ..core import Command +from ..errors import DistutilsSetupError + +with contextlib.suppress(ImportError): + import docutils.frontend + import docutils.nodes + import docutils.parsers.rst + import docutils.utils + + class SilentReporter(docutils.utils.Reporter): + def __init__( + self, + source, + report_level, + halt_level, + stream=None, + debug=0, + encoding='ascii', + error_handler='replace', + ): + self.messages = [] + super().__init__( + source, report_level, halt_level, stream, debug, encoding, error_handler + ) + + def system_message(self, level, message, *children, **kwargs): + self.messages.append((level, message, children, kwargs)) + return docutils.nodes.system_message( + message, *children, level=level, type=self.levels[level], **kwargs + ) + + +class check(Command): + """This command checks the meta-data of the package.""" + + description = "perform some checks on the package" + user_options = [ + ('metadata', 'm', 'Verify meta-data'), + ( + 'restructuredtext', + 'r', + ( + 'Checks if long string meta-data syntax ' + 'are reStructuredText-compliant' + ), + ), + ('strict', 's', 'Will exit with an error if a check fails'), + ] + + boolean_options = ['metadata', 'restructuredtext', 'strict'] + + def initialize_options(self): + """Sets default values for options.""" + self.restructuredtext = 0 + self.metadata = 1 + self.strict = 0 + self._warnings = 0 + + def finalize_options(self): + pass + + def warn(self, msg): + """Counts the number of warnings that occurs.""" + self._warnings += 1 + return Command.warn(self, msg) + + def run(self): + """Runs the command.""" + # perform the various tests + if self.metadata: + self.check_metadata() + if self.restructuredtext: + if 'docutils' in globals(): + try: + self.check_restructuredtext() + except TypeError as exc: + raise DistutilsSetupError(str(exc)) + elif self.strict: + raise DistutilsSetupError('The docutils package is needed.') + + # let's raise an error in strict mode, if we have at least + # one warning + if self.strict and self._warnings > 0: + raise DistutilsSetupError('Please correct your package.') + + def check_metadata(self): + """Ensures that all required elements of meta-data are supplied. + + Required fields: + name, version + + Warns if any are missing. + """ + metadata = self.distribution.metadata + + missing = [] + for attr in 'name', 'version': + if not getattr(metadata, attr, None): + missing.append(attr) + + if missing: + self.warn("missing required meta-data: %s" % ', '.join(missing)) + + def check_restructuredtext(self): + """Checks if the long string fields are reST-compliant.""" + data = self.distribution.get_long_description() + for warning in self._check_rst_data(data): + line = warning[-1].get('line') + if line is None: + warning = warning[1] + else: + warning = f'{warning[1]} (line {line})' + self.warn(warning) + + def _check_rst_data(self, data): + """Returns warnings when the provided data doesn't compile.""" + # the include and csv_table directives need this to be a path + source_path = self.distribution.script_name or 'setup.py' + parser = docutils.parsers.rst.Parser() + settings = docutils.frontend.OptionParser( + components=(docutils.parsers.rst.Parser,) + ).get_default_values() + settings.tab_width = 4 + settings.pep_references = None + settings.rfc_references = None + reporter = SilentReporter( + source_path, + settings.report_level, + settings.halt_level, + stream=settings.warning_stream, + debug=settings.debug, + encoding=settings.error_encoding, + error_handler=settings.error_encoding_error_handler, + ) + + document = docutils.nodes.document(settings, reporter, source=source_path) + document.note_source(source_path, -1) + try: + parser.parse(data, document) + except AttributeError as e: + reporter.messages.append(( + -1, + 'Could not finish the parsing: %s.' % e, + '', + {}, + )) + + return reporter.messages diff --git a/venv/Lib/site-packages/setuptools/_distutils/command/clean.py b/venv/Lib/site-packages/setuptools/_distutils/command/clean.py new file mode 100644 index 0000000..4167a83 --- /dev/null +++ b/venv/Lib/site-packages/setuptools/_distutils/command/clean.py @@ -0,0 +1,76 @@ +"""distutils.command.clean + +Implements the Distutils 'clean' command.""" + +# contributed by Bastian Kleineidam , added 2000-03-18 + +import os +from distutils._log import log + +from ..core import Command +from ..dir_util import remove_tree + + +class clean(Command): + description = "clean up temporary files from 'build' command" + user_options = [ + ('build-base=', 'b', "base build directory (default: 'build.build-base')"), + ( + 'build-lib=', + None, + "build directory for all modules (default: 'build.build-lib')", + ), + ('build-temp=', 't', "temporary build directory (default: 'build.build-temp')"), + ( + 'build-scripts=', + None, + "build directory for scripts (default: 'build.build-scripts')", + ), + ('bdist-base=', None, "temporary directory for built distributions"), + ('all', 'a', "remove all build output, not just temporary by-products"), + ] + + boolean_options = ['all'] + + def initialize_options(self): + self.build_base = None + self.build_lib = None + self.build_temp = None + self.build_scripts = None + self.bdist_base = None + self.all = None + + def finalize_options(self): + self.set_undefined_options( + 'build', + ('build_base', 'build_base'), + ('build_lib', 'build_lib'), + ('build_scripts', 'build_scripts'), + ('build_temp', 'build_temp'), + ) + self.set_undefined_options('bdist', ('bdist_base', 'bdist_base')) + + def run(self): + # remove the build/temp. directory (unless it's already + # gone) + if os.path.exists(self.build_temp): + remove_tree(self.build_temp, dry_run=self.dry_run) + else: + log.debug("'%s' does not exist -- can't clean it", self.build_temp) + + if self.all: + # remove build directories + for directory in (self.build_lib, self.bdist_base, self.build_scripts): + if os.path.exists(directory): + remove_tree(directory, dry_run=self.dry_run) + else: + log.warning("'%s' does not exist -- can't clean it", directory) + + # just for the heck of it, try to remove the base build directory: + # we might have emptied it right now, but if not we don't care + if not self.dry_run: + try: + os.rmdir(self.build_base) + log.info("removing '%s'", self.build_base) + except OSError: + pass diff --git a/venv/Lib/site-packages/setuptools/_distutils/command/config.py b/venv/Lib/site-packages/setuptools/_distutils/command/config.py new file mode 100644 index 0000000..d4b2b0a --- /dev/null +++ b/venv/Lib/site-packages/setuptools/_distutils/command/config.py @@ -0,0 +1,369 @@ +"""distutils.command.config + +Implements the Distutils 'config' command, a (mostly) empty command class +that exists mainly to be sub-classed by specific module distributions and +applications. The idea is that while every "config" command is different, +at least they're all named the same, and users always see "config" in the +list of standard commands. Also, this is a good place to put common +configure-like tasks: "try to compile this C code", or "figure out where +this header file lives". +""" + +from __future__ import annotations + +import os +import pathlib +import re +from collections.abc import Sequence +from distutils._log import log + +from ..core import Command +from ..errors import DistutilsExecError +from ..sysconfig import customize_compiler + +LANG_EXT = {"c": ".c", "c++": ".cxx"} + + +class config(Command): + description = "prepare to build" + + user_options = [ + ('compiler=', None, "specify the compiler type"), + ('cc=', None, "specify the compiler executable"), + ('include-dirs=', 'I', "list of directories to search for header files"), + ('define=', 'D', "C preprocessor macros to define"), + ('undef=', 'U', "C preprocessor macros to undefine"), + ('libraries=', 'l', "external C libraries to link with"), + ('library-dirs=', 'L', "directories to search for external C libraries"), + ('noisy', None, "show every action (compile, link, run, ...) taken"), + ( + 'dump-source', + None, + "dump generated source files before attempting to compile them", + ), + ] + + # The three standard command methods: since the "config" command + # does nothing by default, these are empty. + + def initialize_options(self): + self.compiler = None + self.cc = None + self.include_dirs = None + self.libraries = None + self.library_dirs = None + + # maximal output for now + self.noisy = 1 + self.dump_source = 1 + + # list of temporary files generated along-the-way that we have + # to clean at some point + self.temp_files = [] + + def finalize_options(self): + if self.include_dirs is None: + self.include_dirs = self.distribution.include_dirs or [] + elif isinstance(self.include_dirs, str): + self.include_dirs = self.include_dirs.split(os.pathsep) + + if self.libraries is None: + self.libraries = [] + elif isinstance(self.libraries, str): + self.libraries = [self.libraries] + + if self.library_dirs is None: + self.library_dirs = [] + elif isinstance(self.library_dirs, str): + self.library_dirs = self.library_dirs.split(os.pathsep) + + def run(self): + pass + + # Utility methods for actual "config" commands. The interfaces are + # loosely based on Autoconf macros of similar names. Sub-classes + # may use these freely. + + def _check_compiler(self): + """Check that 'self.compiler' really is a CCompiler object; + if not, make it one. + """ + # We do this late, and only on-demand, because this is an expensive + # import. + from ..ccompiler import CCompiler, new_compiler + + if not isinstance(self.compiler, CCompiler): + self.compiler = new_compiler( + compiler=self.compiler, dry_run=self.dry_run, force=1 + ) + customize_compiler(self.compiler) + if self.include_dirs: + self.compiler.set_include_dirs(self.include_dirs) + if self.libraries: + self.compiler.set_libraries(self.libraries) + if self.library_dirs: + self.compiler.set_library_dirs(self.library_dirs) + + def _gen_temp_sourcefile(self, body, headers, lang): + filename = "_configtest" + LANG_EXT[lang] + with open(filename, "w", encoding='utf-8') as file: + if headers: + for header in headers: + file.write("#include <%s>\n" % header) + file.write("\n") + file.write(body) + if body[-1] != "\n": + file.write("\n") + return filename + + def _preprocess(self, body, headers, include_dirs, lang): + src = self._gen_temp_sourcefile(body, headers, lang) + out = "_configtest.i" + self.temp_files.extend([src, out]) + self.compiler.preprocess(src, out, include_dirs=include_dirs) + return (src, out) + + def _compile(self, body, headers, include_dirs, lang): + src = self._gen_temp_sourcefile(body, headers, lang) + if self.dump_source: + dump_file(src, "compiling '%s':" % src) + (obj,) = self.compiler.object_filenames([src]) + self.temp_files.extend([src, obj]) + self.compiler.compile([src], include_dirs=include_dirs) + return (src, obj) + + def _link(self, body, headers, include_dirs, libraries, library_dirs, lang): + (src, obj) = self._compile(body, headers, include_dirs, lang) + prog = os.path.splitext(os.path.basename(src))[0] + self.compiler.link_executable( + [obj], + prog, + libraries=libraries, + library_dirs=library_dirs, + target_lang=lang, + ) + + if self.compiler.exe_extension is not None: + prog = prog + self.compiler.exe_extension + self.temp_files.append(prog) + + return (src, obj, prog) + + def _clean(self, *filenames): + if not filenames: + filenames = self.temp_files + self.temp_files = [] + log.info("removing: %s", ' '.join(filenames)) + for filename in filenames: + try: + os.remove(filename) + except OSError: + pass + + # XXX these ignore the dry-run flag: what to do, what to do? even if + # you want a dry-run build, you still need some sort of configuration + # info. My inclination is to make it up to the real config command to + # consult 'dry_run', and assume a default (minimal) configuration if + # true. The problem with trying to do it here is that you'd have to + # return either true or false from all the 'try' methods, neither of + # which is correct. + + # XXX need access to the header search path and maybe default macros. + + def try_cpp(self, body=None, headers=None, include_dirs=None, lang="c"): + """Construct a source file from 'body' (a string containing lines + of C/C++ code) and 'headers' (a list of header files to include) + and run it through the preprocessor. Return true if the + preprocessor succeeded, false if there were any errors. + ('body' probably isn't of much use, but what the heck.) + """ + from ..ccompiler import CompileError + + self._check_compiler() + ok = True + try: + self._preprocess(body, headers, include_dirs, lang) + except CompileError: + ok = False + + self._clean() + return ok + + def search_cpp(self, pattern, body=None, headers=None, include_dirs=None, lang="c"): + """Construct a source file (just like 'try_cpp()'), run it through + the preprocessor, and return true if any line of the output matches + 'pattern'. 'pattern' should either be a compiled regex object or a + string containing a regex. If both 'body' and 'headers' are None, + preprocesses an empty file -- which can be useful to determine the + symbols the preprocessor and compiler set by default. + """ + self._check_compiler() + src, out = self._preprocess(body, headers, include_dirs, lang) + + if isinstance(pattern, str): + pattern = re.compile(pattern) + + with open(out, encoding='utf-8') as file: + match = any(pattern.search(line) for line in file) + + self._clean() + return match + + def try_compile(self, body, headers=None, include_dirs=None, lang="c"): + """Try to compile a source file built from 'body' and 'headers'. + Return true on success, false otherwise. + """ + from ..ccompiler import CompileError + + self._check_compiler() + try: + self._compile(body, headers, include_dirs, lang) + ok = True + except CompileError: + ok = False + + log.info(ok and "success!" or "failure.") + self._clean() + return ok + + def try_link( + self, + body, + headers=None, + include_dirs=None, + libraries=None, + library_dirs=None, + lang="c", + ): + """Try to compile and link a source file, built from 'body' and + 'headers', to executable form. Return true on success, false + otherwise. + """ + from ..ccompiler import CompileError, LinkError + + self._check_compiler() + try: + self._link(body, headers, include_dirs, libraries, library_dirs, lang) + ok = True + except (CompileError, LinkError): + ok = False + + log.info(ok and "success!" or "failure.") + self._clean() + return ok + + def try_run( + self, + body, + headers=None, + include_dirs=None, + libraries=None, + library_dirs=None, + lang="c", + ): + """Try to compile, link to an executable, and run a program + built from 'body' and 'headers'. Return true on success, false + otherwise. + """ + from ..ccompiler import CompileError, LinkError + + self._check_compiler() + try: + src, obj, exe = self._link( + body, headers, include_dirs, libraries, library_dirs, lang + ) + self.spawn([exe]) + ok = True + except (CompileError, LinkError, DistutilsExecError): + ok = False + + log.info(ok and "success!" or "failure.") + self._clean() + return ok + + # -- High-level methods -------------------------------------------- + # (these are the ones that are actually likely to be useful + # when implementing a real-world config command!) + + def check_func( + self, + func, + headers=None, + include_dirs=None, + libraries=None, + library_dirs=None, + decl=0, + call=0, + ): + """Determine if function 'func' is available by constructing a + source file that refers to 'func', and compiles and links it. + If everything succeeds, returns true; otherwise returns false. + + The constructed source file starts out by including the header + files listed in 'headers'. If 'decl' is true, it then declares + 'func' (as "int func()"); you probably shouldn't supply 'headers' + and set 'decl' true in the same call, or you might get errors about + a conflicting declarations for 'func'. Finally, the constructed + 'main()' function either references 'func' or (if 'call' is true) + calls it. 'libraries' and 'library_dirs' are used when + linking. + """ + self._check_compiler() + body = [] + if decl: + body.append("int %s ();" % func) + body.append("int main () {") + if call: + body.append(" %s();" % func) + else: + body.append(" %s;" % func) + body.append("}") + body = "\n".join(body) + "\n" + + return self.try_link(body, headers, include_dirs, libraries, library_dirs) + + def check_lib( + self, + library, + library_dirs=None, + headers=None, + include_dirs=None, + other_libraries: Sequence[str] = [], + ): + """Determine if 'library' is available to be linked against, + without actually checking that any particular symbols are provided + by it. 'headers' will be used in constructing the source file to + be compiled, but the only effect of this is to check if all the + header files listed are available. Any libraries listed in + 'other_libraries' will be included in the link, in case 'library' + has symbols that depend on other libraries. + """ + self._check_compiler() + return self.try_link( + "int main (void) { }", + headers, + include_dirs, + [library] + list(other_libraries), + library_dirs, + ) + + def check_header(self, header, include_dirs=None, library_dirs=None, lang="c"): + """Determine if the system header file named by 'header_file' + exists and can be found by the preprocessor; return true if so, + false otherwise. + """ + return self.try_cpp( + body="/* No body */", headers=[header], include_dirs=include_dirs + ) + + +def dump_file(filename, head=None): + """Dumps a file content into log.info. + + If head is not None, will be dumped before the file content. + """ + if head is None: + log.info('%s', filename) + else: + log.info(head) + log.info(pathlib.Path(filename).read_text(encoding='utf-8')) diff --git a/venv/Lib/site-packages/setuptools/_distutils/command/install.py b/venv/Lib/site-packages/setuptools/_distutils/command/install.py new file mode 100644 index 0000000..8e920be --- /dev/null +++ b/venv/Lib/site-packages/setuptools/_distutils/command/install.py @@ -0,0 +1,813 @@ +"""distutils.command.install + +Implements the Distutils 'install' command.""" + +import contextlib +import itertools +import os +import sys +import sysconfig +from distutils._log import log +from site import USER_BASE, USER_SITE + +from .. import _collections +from ..core import Command +from ..debug import DEBUG +from ..errors import DistutilsOptionError, DistutilsPlatformError +from ..file_util import write_file +from ..sysconfig import get_config_vars +from ..util import change_root, convert_path, get_platform, subst_vars +from . import _framework_compat as fw + +HAS_USER_SITE = True + +WINDOWS_SCHEME = { + 'purelib': '{base}/Lib/site-packages', + 'platlib': '{base}/Lib/site-packages', + 'headers': '{base}/Include/{dist_name}', + 'scripts': '{base}/Scripts', + 'data': '{base}', +} + +INSTALL_SCHEMES = { + 'posix_prefix': { + 'purelib': '{base}/lib/{implementation_lower}{py_version_short}/site-packages', + 'platlib': '{platbase}/{platlibdir}/{implementation_lower}' + '{py_version_short}/site-packages', + 'headers': '{base}/include/{implementation_lower}' + '{py_version_short}{abiflags}/{dist_name}', + 'scripts': '{base}/bin', + 'data': '{base}', + }, + 'posix_home': { + 'purelib': '{base}/lib/{implementation_lower}', + 'platlib': '{base}/{platlibdir}/{implementation_lower}', + 'headers': '{base}/include/{implementation_lower}/{dist_name}', + 'scripts': '{base}/bin', + 'data': '{base}', + }, + 'nt': WINDOWS_SCHEME, + 'pypy': { + 'purelib': '{base}/site-packages', + 'platlib': '{base}/site-packages', + 'headers': '{base}/include/{dist_name}', + 'scripts': '{base}/bin', + 'data': '{base}', + }, + 'pypy_nt': { + 'purelib': '{base}/site-packages', + 'platlib': '{base}/site-packages', + 'headers': '{base}/include/{dist_name}', + 'scripts': '{base}/Scripts', + 'data': '{base}', + }, +} + +# user site schemes +if HAS_USER_SITE: + INSTALL_SCHEMES['nt_user'] = { + 'purelib': '{usersite}', + 'platlib': '{usersite}', + 'headers': '{userbase}/{implementation}{py_version_nodot_plat}' + '/Include/{dist_name}', + 'scripts': '{userbase}/{implementation}{py_version_nodot_plat}/Scripts', + 'data': '{userbase}', + } + + INSTALL_SCHEMES['posix_user'] = { + 'purelib': '{usersite}', + 'platlib': '{usersite}', + 'headers': '{userbase}/include/{implementation_lower}' + '{py_version_short}{abiflags}/{dist_name}', + 'scripts': '{userbase}/bin', + 'data': '{userbase}', + } + + +INSTALL_SCHEMES.update(fw.schemes) + + +# The keys to an installation scheme; if any new types of files are to be +# installed, be sure to add an entry to every installation scheme above, +# and to SCHEME_KEYS here. +SCHEME_KEYS = ('purelib', 'platlib', 'headers', 'scripts', 'data') + + +def _load_sysconfig_schemes(): + with contextlib.suppress(AttributeError): + return { + scheme: sysconfig.get_paths(scheme, expand=False) + for scheme in sysconfig.get_scheme_names() + } + + +def _load_schemes(): + """ + Extend default schemes with schemes from sysconfig. + """ + + sysconfig_schemes = _load_sysconfig_schemes() or {} + + return { + scheme: { + **INSTALL_SCHEMES.get(scheme, {}), + **sysconfig_schemes.get(scheme, {}), + } + for scheme in set(itertools.chain(INSTALL_SCHEMES, sysconfig_schemes)) + } + + +def _get_implementation(): + if hasattr(sys, 'pypy_version_info'): + return 'PyPy' + else: + return 'Python' + + +def _select_scheme(ob, name): + scheme = _inject_headers(name, _load_scheme(_resolve_scheme(name))) + vars(ob).update(_remove_set(ob, _scheme_attrs(scheme))) + + +def _remove_set(ob, attrs): + """ + Include only attrs that are None in ob. + """ + return {key: value for key, value in attrs.items() if getattr(ob, key) is None} + + +def _resolve_scheme(name): + os_name, sep, key = name.partition('_') + try: + resolved = sysconfig.get_preferred_scheme(key) + except Exception: + resolved = fw.scheme(_pypy_hack(name)) + return resolved + + +def _load_scheme(name): + return _load_schemes()[name] + + +def _inject_headers(name, scheme): + """ + Given a scheme name and the resolved scheme, + if the scheme does not include headers, resolve + the fallback scheme for the name and use headers + from it. pypa/distutils#88 + """ + # Bypass the preferred scheme, which may not + # have defined headers. + fallback = _load_scheme(_pypy_hack(name)) + scheme.setdefault('headers', fallback['headers']) + return scheme + + +def _scheme_attrs(scheme): + """Resolve install directories by applying the install schemes.""" + return {f'install_{key}': scheme[key] for key in SCHEME_KEYS} + + +def _pypy_hack(name): + PY37 = sys.version_info < (3, 8) + old_pypy = hasattr(sys, 'pypy_version_info') and PY37 + prefix = not name.endswith(('_user', '_home')) + pypy_name = 'pypy' + '_nt' * (os.name == 'nt') + return pypy_name if old_pypy and prefix else name + + +class install(Command): + description = "install everything from build directory" + + user_options = [ + # Select installation scheme and set base director(y|ies) + ('prefix=', None, "installation prefix"), + ('exec-prefix=', None, "(Unix only) prefix for platform-specific files"), + ('home=', None, "(Unix only) home directory to install under"), + # Or, just set the base director(y|ies) + ( + 'install-base=', + None, + "base installation directory (instead of --prefix or --home)", + ), + ( + 'install-platbase=', + None, + "base installation directory for platform-specific files " + + "(instead of --exec-prefix or --home)", + ), + ('root=', None, "install everything relative to this alternate root directory"), + # Or, explicitly set the installation scheme + ( + 'install-purelib=', + None, + "installation directory for pure Python module distributions", + ), + ( + 'install-platlib=', + None, + "installation directory for non-pure module distributions", + ), + ( + 'install-lib=', + None, + "installation directory for all module distributions " + + "(overrides --install-purelib and --install-platlib)", + ), + ('install-headers=', None, "installation directory for C/C++ headers"), + ('install-scripts=', None, "installation directory for Python scripts"), + ('install-data=', None, "installation directory for data files"), + # Byte-compilation options -- see install_lib.py for details, as + # these are duplicated from there (but only install_lib does + # anything with them). + ('compile', 'c', "compile .py to .pyc [default]"), + ('no-compile', None, "don't compile .py files"), + ( + 'optimize=', + 'O', + "also compile with optimization: -O1 for \"python -O\", " + "-O2 for \"python -OO\", and -O0 to disable [default: -O0]", + ), + # Miscellaneous control options + ('force', 'f', "force installation (overwrite any existing files)"), + ('skip-build', None, "skip rebuilding everything (for testing/debugging)"), + # Where to install documentation (eventually!) + # ('doc-format=', None, "format of documentation to generate"), + # ('install-man=', None, "directory for Unix man pages"), + # ('install-html=', None, "directory for HTML documentation"), + # ('install-info=', None, "directory for GNU info files"), + ('record=', None, "filename in which to record list of installed files"), + ] + + boolean_options = ['compile', 'force', 'skip-build'] + + if HAS_USER_SITE: + user_options.append(( + 'user', + None, + "install in user site-package '%s'" % USER_SITE, + )) + boolean_options.append('user') + + negative_opt = {'no-compile': 'compile'} + + def initialize_options(self): + """Initializes options.""" + # High-level options: these select both an installation base + # and scheme. + self.prefix = None + self.exec_prefix = None + self.home = None + self.user = 0 + + # These select only the installation base; it's up to the user to + # specify the installation scheme (currently, that means supplying + # the --install-{platlib,purelib,scripts,data} options). + self.install_base = None + self.install_platbase = None + self.root = None + + # These options are the actual installation directories; if not + # supplied by the user, they are filled in using the installation + # scheme implied by prefix/exec-prefix/home and the contents of + # that installation scheme. + 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_userbase = USER_BASE + self.install_usersite = USER_SITE + + self.compile = None + self.optimize = None + + # Deprecated + # These two are for putting non-packagized distributions into their + # own directory and creating a .pth file if it makes sense. + # 'extra_path' comes from the setup file; 'install_path_file' can + # be turned off if it makes no sense to install a .pth file. (But + # better to install it uselessly than to guess wrong and not + # install it when it's necessary and would be used!) Currently, + # 'install_path_file' is always true unless some outsider meddles + # with it. + self.extra_path = None + self.install_path_file = 1 + + # 'force' forces installation, even if target files are not + # out-of-date. 'skip_build' skips running the "build" command, + # handy if you know it's not necessary. 'warn_dir' (which is *not* + # a user option, it's just there so the bdist_* commands can turn + # it off) determines whether we warn about installing to a + # directory not in sys.path. + self.force = 0 + self.skip_build = 0 + self.warn_dir = 1 + + # These are only here as a conduit from the 'build' command to the + # 'install_*' commands that do the real work. ('build_base' isn't + # actually used anywhere, but it might be useful in future.) They + # are not user options, because if the user told the install + # command where the build directory is, that wouldn't affect the + # build command. + self.build_base = None + self.build_lib = None + + # Not defined yet because we don't know anything about + # documentation yet. + # self.install_man = None + # self.install_html = None + # self.install_info = None + + self.record = None + + # -- Option finalizing methods ------------------------------------- + # (This is rather more involved than for most commands, + # because this is where the policy for installing third- + # party Python modules on various platforms given a wide + # array of user input is decided. Yes, it's quite complex!) + + def finalize_options(self): # noqa: C901 + """Finalizes options.""" + # This method (and its helpers, like 'finalize_unix()', + # 'finalize_other()', and 'select_scheme()') is where the default + # installation directories for modules, extension modules, and + # anything else we care to install from a Python module + # distribution. Thus, this code makes a pretty important policy + # statement about how third-party stuff is added to a Python + # installation! Note that the actual work of installation is done + # by the relatively simple 'install_*' commands; they just take + # their orders from the installation directory options determined + # here. + + # Check for errors/inconsistencies in the options; first, stuff + # that's wrong on any platform. + + if (self.prefix or self.exec_prefix or self.home) and ( + self.install_base or self.install_platbase + ): + raise DistutilsOptionError( + "must supply either prefix/exec-prefix/home or " + + "install-base/install-platbase -- not both" + ) + + if self.home and (self.prefix or self.exec_prefix): + raise DistutilsOptionError( + "must supply either home or prefix/exec-prefix -- not both" + ) + + if self.user and ( + self.prefix + or self.exec_prefix + or self.home + or self.install_base + or self.install_platbase + ): + raise DistutilsOptionError( + "can't combine user with prefix, " + "exec_prefix/home, or install_(plat)base" + ) + + # Next, stuff that's wrong (or dubious) only on certain platforms. + if os.name != "posix": + if self.exec_prefix: + self.warn("exec-prefix option ignored on this platform") + self.exec_prefix = None + + # Now the interesting logic -- so interesting that we farm it out + # to other methods. The goal of these methods is to set the final + # values for the install_{lib,scripts,data,...} options, using as + # input a heady brew of prefix, exec_prefix, home, install_base, + # install_platbase, user-supplied versions of + # install_{purelib,platlib,lib,scripts,data,...}, and the + # install schemes. Phew! + + self.dump_dirs("pre-finalize_{unix,other}") + + if os.name == 'posix': + self.finalize_unix() + else: + self.finalize_other() + + self.dump_dirs("post-finalize_{unix,other}()") + + # Expand configuration variables, tilde, etc. in self.install_base + # and self.install_platbase -- that way, we can use $base or + # $platbase in the other installation directories and not worry + # about needing recursive variable expansion (shudder). + + py_version = sys.version.split()[0] + (prefix, exec_prefix) = get_config_vars('prefix', 'exec_prefix') + try: + abiflags = sys.abiflags + except AttributeError: + # sys.abiflags may not be defined on all platforms. + abiflags = '' + local_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': '%d.%d' % sys.version_info[:2], + 'py_version_nodot': '%d%d' % sys.version_info[:2], + 'sys_prefix': prefix, + 'prefix': prefix, + 'sys_exec_prefix': exec_prefix, + 'exec_prefix': exec_prefix, + 'abiflags': abiflags, + 'platlibdir': getattr(sys, 'platlibdir', 'lib'), + 'implementation_lower': _get_implementation().lower(), + 'implementation': _get_implementation(), + } + + # vars for compatibility on older Pythons + compat_vars = dict( + # Python 3.9 and earlier + py_version_nodot_plat=getattr(sys, 'winver', '').replace('.', ''), + ) + + if HAS_USER_SITE: + local_vars['userbase'] = self.install_userbase + local_vars['usersite'] = self.install_usersite + + self.config_vars = _collections.DictStack([ + fw.vars(), + compat_vars, + sysconfig.get_config_vars(), + local_vars, + ]) + + self.expand_basedirs() + + self.dump_dirs("post-expand_basedirs()") + + # Now define config vars for the base directories so we can expand + # everything else. + local_vars['base'] = self.install_base + local_vars['platbase'] = self.install_platbase + + if DEBUG: + from pprint import pprint + + print("config vars:") + pprint(dict(self.config_vars)) + + # Expand "~" and configuration variables in the installation + # directories. + self.expand_dirs() + + self.dump_dirs("post-expand_dirs()") + + # Create directories in the home dir: + if self.user: + self.create_home_path() + + # Pick the actual directory to install all modules to: either + # install_purelib or install_platlib, depending on whether this + # module distribution is pure or not. Of course, if the user + # already specified install_lib, use their selection. + if self.install_lib is None: + if self.distribution.has_ext_modules(): # has extensions: non-pure + self.install_lib = self.install_platlib + else: + self.install_lib = self.install_purelib + + # Convert directories from Unix /-separated syntax to the local + # convention. + self.convert_paths( + 'lib', + 'purelib', + 'platlib', + 'scripts', + 'data', + 'headers', + 'userbase', + 'usersite', + ) + + # Deprecated + # Well, we're not actually fully completely finalized yet: we still + # have to deal with 'extra_path', which is the hack for allowing + # non-packagized module distributions (hello, Numerical Python!) to + # get their own directories. + self.handle_extra_path() + self.install_libbase = self.install_lib # needed for .pth file + self.install_lib = os.path.join(self.install_lib, self.extra_dirs) + + # If a new root directory was supplied, make all the installation + # dirs relative to it. + if self.root is not None: + self.change_roots( + 'libbase', 'lib', 'purelib', 'platlib', 'scripts', 'data', 'headers' + ) + + self.dump_dirs("after prepending root") + + # Find out the build directories, ie. where to install from. + self.set_undefined_options( + 'build', ('build_base', 'build_base'), ('build_lib', 'build_lib') + ) + + # Punt on doc directories for now -- after all, we're punting on + # documentation completely! + + def dump_dirs(self, msg): + """Dumps the list of user options.""" + if not DEBUG: + return + from ..fancy_getopt import longopt_xlate + + log.debug(msg + ":") + for opt in self.user_options: + opt_name = opt[0] + if opt_name[-1] == "=": + opt_name = opt_name[0:-1] + if opt_name in self.negative_opt: + opt_name = self.negative_opt[opt_name] + opt_name = opt_name.translate(longopt_xlate) + val = not getattr(self, opt_name) + else: + opt_name = opt_name.translate(longopt_xlate) + val = getattr(self, opt_name) + log.debug(" %s: %s", opt_name, val) + + def finalize_unix(self): + """Finalizes options for posix platforms.""" + if self.install_base is not None or self.install_platbase is not None: + incomplete_scheme = ( + ( + self.install_lib is None + and self.install_purelib is None + and self.install_platlib is None + ) + or self.install_headers is None + or self.install_scripts is None + or self.install_data is None + ) + if incomplete_scheme: + raise DistutilsOptionError( + "install-base or install-platbase supplied, but " + "installation scheme is incomplete" + ) + return + + if self.user: + if self.install_userbase is None: + raise DistutilsPlatformError("User base directory is not specified") + self.install_base = self.install_platbase = self.install_userbase + self.select_scheme("posix_user") + elif self.home is not None: + self.install_base = self.install_platbase = self.home + self.select_scheme("posix_home") + else: + if self.prefix is None: + if self.exec_prefix is not None: + raise DistutilsOptionError( + "must not supply exec-prefix without prefix" + ) + + # Allow Fedora to add components to the prefix + _prefix_addition = getattr(sysconfig, '_prefix_addition', "") + + self.prefix = os.path.normpath(sys.prefix) + _prefix_addition + self.exec_prefix = os.path.normpath(sys.exec_prefix) + _prefix_addition + + else: + if self.exec_prefix is None: + self.exec_prefix = self.prefix + + self.install_base = self.prefix + self.install_platbase = self.exec_prefix + self.select_scheme("posix_prefix") + + def finalize_other(self): + """Finalizes options for non-posix platforms""" + if self.user: + if self.install_userbase is None: + raise DistutilsPlatformError("User base directory is not specified") + self.install_base = self.install_platbase = self.install_userbase + self.select_scheme(os.name + "_user") + elif self.home is not None: + self.install_base = self.install_platbase = self.home + self.select_scheme("posix_home") + else: + if self.prefix is None: + self.prefix = os.path.normpath(sys.prefix) + + self.install_base = self.install_platbase = self.prefix + try: + self.select_scheme(os.name) + except KeyError: + raise DistutilsPlatformError( + "I don't know how to install stuff on '%s'" % os.name + ) + + def select_scheme(self, name): + _select_scheme(self, name) + + def _expand_attrs(self, attrs): + for attr in attrs: + val = getattr(self, attr) + if val is not None: + if os.name in ('posix', 'nt'): + val = os.path.expanduser(val) + val = subst_vars(val, self.config_vars) + setattr(self, attr, val) + + def expand_basedirs(self): + """Calls `os.path.expanduser` on install_base, install_platbase and + root.""" + self._expand_attrs(['install_base', 'install_platbase', 'root']) + + def expand_dirs(self): + """Calls `os.path.expanduser` on install dirs.""" + self._expand_attrs([ + 'install_purelib', + 'install_platlib', + 'install_lib', + 'install_headers', + 'install_scripts', + 'install_data', + ]) + + def convert_paths(self, *names): + """Call `convert_path` over `names`.""" + for name in names: + attr = "install_" + name + setattr(self, attr, convert_path(getattr(self, attr))) + + def handle_extra_path(self): + """Set `path_file` and `extra_dirs` using `extra_path`.""" + if self.extra_path is None: + self.extra_path = self.distribution.extra_path + + if self.extra_path is not None: + log.warning( + "Distribution option extra_path is deprecated. " + "See issue27919 for details." + ) + if isinstance(self.extra_path, str): + self.extra_path = self.extra_path.split(',') + + if len(self.extra_path) == 1: + path_file = extra_dirs = self.extra_path[0] + elif len(self.extra_path) == 2: + path_file, extra_dirs = self.extra_path + else: + raise DistutilsOptionError( + "'extra_path' option must be a list, tuple, or " + "comma-separated string with 1 or 2 elements" + ) + + # convert to local form in case Unix notation used (as it + # should be in setup scripts) + extra_dirs = convert_path(extra_dirs) + else: + path_file = None + extra_dirs = '' + + # XXX should we warn if path_file and not extra_dirs? (in which + # case the path file would be harmless but pointless) + self.path_file = path_file + self.extra_dirs = extra_dirs + + def change_roots(self, *names): + """Change the install directories pointed by name using root.""" + for name in names: + attr = "install_" + name + setattr(self, attr, change_root(self.root, getattr(self, attr))) + + def create_home_path(self): + """Create directories under ~.""" + if not self.user: + return + home = convert_path(os.path.expanduser("~")) + for _name, path in self.config_vars.items(): + if str(path).startswith(home) and not os.path.isdir(path): + self.debug_print("os.makedirs('%s', 0o700)" % path) + os.makedirs(path, 0o700) + + # -- Command execution methods ------------------------------------- + + def run(self): + """Runs the command.""" + # Obviously have to build before we can install + if not self.skip_build: + self.run_command('build') + # If we built for any other platform, we can't install. + build_plat = self.distribution.get_command_obj('build').plat_name + # check warn_dir - it is a clue that the 'install' is happening + # internally, and not to sys.path, so we don't check the platform + # matches what we are running. + if self.warn_dir and build_plat != get_platform(): + raise DistutilsPlatformError("Can't install when cross-compiling") + + # Run all sub-commands (at least those that need to be run) + for cmd_name in self.get_sub_commands(): + self.run_command(cmd_name) + + if self.path_file: + self.create_path_file() + + # write list of installed files, if requested. + if self.record: + outputs = self.get_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:] + self.execute( + write_file, + (self.record, outputs), + "writing list of installed files to '%s'" % self.record, + ) + + sys_path = map(os.path.normpath, sys.path) + sys_path = map(os.path.normcase, sys_path) + install_lib = os.path.normcase(os.path.normpath(self.install_lib)) + if ( + self.warn_dir + and not (self.path_file and self.install_path_file) + and install_lib not in sys_path + ): + log.debug( + ( + "modules installed to '%s', which is not in " + "Python's module search path (sys.path) -- " + "you'll have to change the search path yourself" + ), + self.install_lib, + ) + + def create_path_file(self): + """Creates the .pth file""" + filename = os.path.join(self.install_libbase, self.path_file + ".pth") + if self.install_path_file: + self.execute( + write_file, (filename, [self.extra_dirs]), "creating %s" % filename + ) + else: + self.warn("path file '%s' not created" % filename) + + # -- Reporting methods --------------------------------------------- + + def get_outputs(self): + """Assembles the outputs of all the sub-commands.""" + outputs = [] + for cmd_name in self.get_sub_commands(): + cmd = self.get_finalized_command(cmd_name) + # Add the contents of cmd.get_outputs(), ensuring + # that outputs doesn't contain duplicate entries + for filename in cmd.get_outputs(): + if filename not in outputs: + outputs.append(filename) + + if self.path_file and self.install_path_file: + outputs.append(os.path.join(self.install_libbase, self.path_file + ".pth")) + + return outputs + + def get_inputs(self): + """Returns the inputs of all the sub-commands""" + # XXX gee, this looks familiar ;-( + inputs = [] + for cmd_name in self.get_sub_commands(): + cmd = self.get_finalized_command(cmd_name) + inputs.extend(cmd.get_inputs()) + + return inputs + + # -- Predicates for sub-command list ------------------------------- + + def has_lib(self): + """Returns true if the current distribution has any Python + modules to install.""" + return ( + self.distribution.has_pure_modules() or self.distribution.has_ext_modules() + ) + + def has_headers(self): + """Returns true if the current distribution has any headers to + install.""" + return self.distribution.has_headers() + + def has_scripts(self): + """Returns true if the current distribution has any scripts to. + install.""" + return self.distribution.has_scripts() + + def has_data(self): + """Returns true if the current distribution has any data to. + install.""" + return self.distribution.has_data_files() + + # 'sub_commands': a list of commands this command might have to run to + # get its work done. See cmd.py for more info. + sub_commands = [ + ('install_lib', has_lib), + ('install_headers', has_headers), + ('install_scripts', has_scripts), + ('install_data', has_data), + ('install_egg_info', lambda self: True), + ] diff --git a/venv/Lib/site-packages/setuptools/_distutils/command/install_data.py b/venv/Lib/site-packages/setuptools/_distutils/command/install_data.py new file mode 100644 index 0000000..b63a1af --- /dev/null +++ b/venv/Lib/site-packages/setuptools/_distutils/command/install_data.py @@ -0,0 +1,84 @@ +"""distutils.command.install_data + +Implements the Distutils 'install_data' command, for installing +platform-independent data files.""" + +# contributed by Bastian Kleineidam + +import os + +from ..core import Command +from ..util import change_root, convert_path + + +class install_data(Command): + description = "install data files" + + user_options = [ + ( + 'install-dir=', + 'd', + "base directory for installing data files " + "(default: installation base dir)", + ), + ('root=', None, "install everything relative to this alternate root directory"), + ('force', 'f', "force installation (overwrite existing files)"), + ] + + boolean_options = ['force'] + + def initialize_options(self): + self.install_dir = None + self.outfiles = [] + self.root = None + self.force = 0 + self.data_files = self.distribution.data_files + self.warn_dir = 1 + + def finalize_options(self): + self.set_undefined_options( + 'install', + ('install_data', 'install_dir'), + ('root', 'root'), + ('force', 'force'), + ) + + def run(self): + self.mkpath(self.install_dir) + for f in self.data_files: + if isinstance(f, str): + # it's a simple file, so copy it + f = convert_path(f) + if self.warn_dir: + self.warn( + "setup script did not provide a directory for " + f"'{f}' -- installing right in '{self.install_dir}'" + ) + (out, _) = self.copy_file(f, self.install_dir) + self.outfiles.append(out) + else: + # it's a tuple with path to install to and a list of files + dir = convert_path(f[0]) + if not os.path.isabs(dir): + dir = os.path.join(self.install_dir, dir) + elif self.root: + dir = change_root(self.root, dir) + self.mkpath(dir) + + if f[1] == []: + # If there are no files listed, the user must be + # trying to create an empty directory, so add the + # directory to the list of output files. + self.outfiles.append(dir) + else: + # Copy files, adding them to the list of output files. + for data in f[1]: + data = convert_path(data) + (out, _) = self.copy_file(data, dir) + self.outfiles.append(out) + + def get_inputs(self): + return self.data_files or [] + + def get_outputs(self): + return self.outfiles diff --git a/venv/Lib/site-packages/setuptools/_distutils/command/install_egg_info.py b/venv/Lib/site-packages/setuptools/_distutils/command/install_egg_info.py new file mode 100644 index 0000000..4fbb344 --- /dev/null +++ b/venv/Lib/site-packages/setuptools/_distutils/command/install_egg_info.py @@ -0,0 +1,92 @@ +""" +distutils.command.install_egg_info + +Implements the Distutils 'install_egg_info' command, for installing +a package's PKG-INFO metadata. +""" + +import os +import re +import sys + +from .. import dir_util +from .._log import log +from ..cmd import Command + + +class install_egg_info(Command): + """Install an .egg-info file for the package""" + + description = "Install package's PKG-INFO metadata as an .egg-info file" + user_options = [ + ('install-dir=', 'd', "directory to install to"), + ] + + def initialize_options(self): + self.install_dir = None + + @property + def basename(self): + """ + Allow basename to be overridden by child class. + Ref pypa/distutils#2. + """ + return "%s-%s-py%d.%d.egg-info" % ( + to_filename(safe_name(self.distribution.get_name())), + to_filename(safe_version(self.distribution.get_version())), + *sys.version_info[:2], + ) + + def finalize_options(self): + self.set_undefined_options('install_lib', ('install_dir', 'install_dir')) + self.target = os.path.join(self.install_dir, self.basename) + self.outputs = [self.target] + + def run(self): + target = self.target + if os.path.isdir(target) and not os.path.islink(target): + dir_util.remove_tree(target, dry_run=self.dry_run) + elif os.path.exists(target): + self.execute(os.unlink, (self.target,), "Removing " + target) + elif not os.path.isdir(self.install_dir): + self.execute( + os.makedirs, (self.install_dir,), "Creating " + self.install_dir + ) + log.info("Writing %s", target) + if not self.dry_run: + with open(target, 'w', encoding='UTF-8') as f: + self.distribution.metadata.write_pkg_file(f) + + def get_outputs(self): + return self.outputs + + +# The following routines are taken from setuptools' pkg_resources module and +# can be replaced by importing them from pkg_resources once it is included +# in the stdlib. + + +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 + + Spaces become dots, and all other non-alphanumeric characters become + dashes, with runs of multiple dashes condensed to a single dash. + """ + version = version.replace(' ', '.') + return re.sub('[^A-Za-z0-9.]+', '-', version) + + +def to_filename(name): + """Convert a project or version name to its filename-escaped form + + Any '-' characters are currently replaced with '_'. + """ + return name.replace('-', '_') diff --git a/venv/Lib/site-packages/setuptools/_distutils/command/install_headers.py b/venv/Lib/site-packages/setuptools/_distutils/command/install_headers.py new file mode 100644 index 0000000..085272c --- /dev/null +++ b/venv/Lib/site-packages/setuptools/_distutils/command/install_headers.py @@ -0,0 +1,44 @@ +"""distutils.command.install_headers + +Implements the Distutils 'install_headers' command, to install C/C++ header +files to the Python include directory.""" + +from ..core import Command + + +# XXX force is never used +class install_headers(Command): + description = "install C/C++ header files" + + user_options = [ + ('install-dir=', 'd', "directory to install header files to"), + ('force', 'f', "force installation (overwrite existing files)"), + ] + + boolean_options = ['force'] + + def initialize_options(self): + self.install_dir = None + self.force = 0 + self.outfiles = [] + + def finalize_options(self): + self.set_undefined_options( + 'install', ('install_headers', 'install_dir'), ('force', 'force') + ) + + def run(self): + headers = self.distribution.headers + if not headers: + return + + self.mkpath(self.install_dir) + for header in headers: + (out, _) = self.copy_file(header, self.install_dir) + self.outfiles.append(out) + + def get_inputs(self): + return self.distribution.headers or [] + + def get_outputs(self): + return self.outfiles diff --git a/venv/Lib/site-packages/setuptools/_distutils/command/install_lib.py b/venv/Lib/site-packages/setuptools/_distutils/command/install_lib.py new file mode 100644 index 0000000..b1f346f --- /dev/null +++ b/venv/Lib/site-packages/setuptools/_distutils/command/install_lib.py @@ -0,0 +1,236 @@ +"""distutils.command.install_lib + +Implements the Distutils 'install_lib' command +(install all Python modules).""" + +import importlib.util +import os +import sys + +from ..core import Command +from ..errors import DistutilsOptionError + +# Extension for Python source files. +PYTHON_SOURCE_EXTENSION = ".py" + + +class install_lib(Command): + description = "install all Python modules (extensions and pure Python)" + + # The byte-compilation options are a tad confusing. Here are the + # possible scenarios: + # 1) no compilation at all (--no-compile --no-optimize) + # 2) compile .pyc only (--compile --no-optimize; default) + # 3) compile .pyc and "opt-1" .pyc (--compile --optimize) + # 4) compile "opt-1" .pyc only (--no-compile --optimize) + # 5) compile .pyc and "opt-2" .pyc (--compile --optimize-more) + # 6) compile "opt-2" .pyc only (--no-compile --optimize-more) + # + # The UI for this is two options, 'compile' and 'optimize'. + # 'compile' is strictly boolean, and only decides whether to + # generate .pyc files. 'optimize' is three-way (0, 1, or 2), and + # decides both whether to generate .pyc files and what level of + # optimization to use. + + user_options = [ + ('install-dir=', 'd', "directory to install to"), + ('build-dir=', 'b', "build directory (where to install from)"), + ('force', 'f', "force installation (overwrite existing files)"), + ('compile', 'c', "compile .py to .pyc [default]"), + ('no-compile', None, "don't compile .py files"), + ( + 'optimize=', + 'O', + "also compile with optimization: -O1 for \"python -O\", " + "-O2 for \"python -OO\", and -O0 to disable [default: -O0]", + ), + ('skip-build', None, "skip the build steps"), + ] + + boolean_options = ['force', 'compile', 'skip-build'] + negative_opt = {'no-compile': 'compile'} + + def initialize_options(self): + # let the 'install' command dictate our installation directory + self.install_dir = None + self.build_dir = None + self.force = 0 + self.compile = None + self.optimize = None + self.skip_build = None + + def finalize_options(self): + # Get all the information we need to install pure Python modules + # from the umbrella 'install' command -- build (source) directory, + # install (target) directory, and whether to compile .py files. + self.set_undefined_options( + 'install', + ('build_lib', 'build_dir'), + ('install_lib', 'install_dir'), + ('force', 'force'), + ('compile', 'compile'), + ('optimize', 'optimize'), + ('skip_build', 'skip_build'), + ) + + if self.compile is None: + self.compile = True + if self.optimize is None: + self.optimize = False + + if not isinstance(self.optimize, int): + try: + self.optimize = int(self.optimize) + if self.optimize not in (0, 1, 2): + raise AssertionError + except (ValueError, AssertionError): + raise DistutilsOptionError("optimize must be 0, 1, or 2") + + def run(self): + # Make sure we have built everything we need first + self.build() + + # Install everything: simply dump the entire contents of the build + # directory to the installation directory (that's the beauty of + # having a build directory!) + outfiles = self.install() + + # (Optionally) compile .py to .pyc + if outfiles is not None and self.distribution.has_pure_modules(): + self.byte_compile(outfiles) + + # -- Top-level worker functions ------------------------------------ + # (called from 'run()') + + def build(self): + if not self.skip_build: + if self.distribution.has_pure_modules(): + self.run_command('build_py') + if self.distribution.has_ext_modules(): + self.run_command('build_ext') + + def install(self): + if os.path.isdir(self.build_dir): + outfiles = self.copy_tree(self.build_dir, self.install_dir) + else: + self.warn( + "'%s' does not exist -- no Python modules to install" % self.build_dir + ) + return + return outfiles + + def byte_compile(self, files): + if sys.dont_write_bytecode: + self.warn('byte-compiling is disabled, skipping.') + return + + from ..util import byte_compile + + # Get the "--root" directory supplied to the "install" command, + # and use it as a prefix to strip off the purported filename + # encoded in bytecode files. This is far from complete, but it + # should at least generate usable bytecode in RPM distributions. + install_root = self.get_finalized_command('install').root + + if self.compile: + byte_compile( + files, + optimize=0, + force=self.force, + prefix=install_root, + dry_run=self.dry_run, + ) + if self.optimize > 0: + byte_compile( + files, + optimize=self.optimize, + force=self.force, + prefix=install_root, + verbose=self.verbose, + dry_run=self.dry_run, + ) + + # -- Utility methods ----------------------------------------------- + + def _mutate_outputs(self, has_any, build_cmd, cmd_option, output_dir): + if not has_any: + return [] + + build_cmd = self.get_finalized_command(build_cmd) + build_files = build_cmd.get_outputs() + build_dir = getattr(build_cmd, cmd_option) + + prefix_len = len(build_dir) + len(os.sep) + outputs = [] + for file in build_files: + outputs.append(os.path.join(output_dir, file[prefix_len:])) + + return outputs + + def _bytecode_filenames(self, py_filenames): + bytecode_files = [] + for py_file in py_filenames: + # Since build_py handles package data installation, the + # list of outputs can contain more than just .py files. + # Make sure we only report bytecode for the .py files. + ext = os.path.splitext(os.path.normcase(py_file))[1] + if ext != PYTHON_SOURCE_EXTENSION: + continue + if self.compile: + bytecode_files.append( + importlib.util.cache_from_source(py_file, optimization='') + ) + if self.optimize > 0: + bytecode_files.append( + importlib.util.cache_from_source( + py_file, optimization=self.optimize + ) + ) + + return bytecode_files + + # -- External interface -------------------------------------------- + # (called by outsiders) + + def get_outputs(self): + """Return the list of files that would be installed if this command + were actually run. Not affected by the "dry-run" flag or whether + modules have actually been built yet. + """ + pure_outputs = self._mutate_outputs( + self.distribution.has_pure_modules(), + 'build_py', + 'build_lib', + self.install_dir, + ) + if self.compile: + bytecode_outputs = self._bytecode_filenames(pure_outputs) + else: + bytecode_outputs = [] + + ext_outputs = self._mutate_outputs( + self.distribution.has_ext_modules(), + 'build_ext', + 'build_lib', + self.install_dir, + ) + + return pure_outputs + bytecode_outputs + ext_outputs + + def get_inputs(self): + """Get the list of files that are input to this command, ie. the + files that get installed as they are named in the build tree. + The files in this list correspond one-to-one to the output + filenames returned by 'get_outputs()'. + """ + inputs = [] + + if self.distribution.has_pure_modules(): + build_py = self.get_finalized_command('build_py') + inputs.extend(build_py.get_outputs()) + + if self.distribution.has_ext_modules(): + build_ext = self.get_finalized_command('build_ext') + inputs.extend(build_ext.get_outputs()) + + return inputs diff --git a/venv/Lib/site-packages/setuptools/_distutils/command/install_scripts.py b/venv/Lib/site-packages/setuptools/_distutils/command/install_scripts.py new file mode 100644 index 0000000..e66b13a --- /dev/null +++ b/venv/Lib/site-packages/setuptools/_distutils/command/install_scripts.py @@ -0,0 +1,61 @@ +"""distutils.command.install_scripts + +Implements the Distutils 'install_scripts' command, for installing +Python scripts.""" + +# contributed by Bastian Kleineidam + +import os +from distutils._log import log +from stat import ST_MODE + +from ..core import Command + + +class install_scripts(Command): + description = "install scripts (Python or otherwise)" + + user_options = [ + ('install-dir=', 'd', "directory to install scripts to"), + ('build-dir=', 'b', "build directory (where to install from)"), + ('force', 'f', "force installation (overwrite existing files)"), + ('skip-build', None, "skip the build steps"), + ] + + boolean_options = ['force', 'skip-build'] + + def initialize_options(self): + self.install_dir = None + self.force = 0 + self.build_dir = None + self.skip_build = None + + def finalize_options(self): + self.set_undefined_options('build', ('build_scripts', 'build_dir')) + self.set_undefined_options( + 'install', + ('install_scripts', 'install_dir'), + ('force', 'force'), + ('skip_build', 'skip_build'), + ) + + def run(self): + if not self.skip_build: + self.run_command('build_scripts') + self.outfiles = self.copy_tree(self.build_dir, self.install_dir) + if os.name == 'posix': + # Set the executable bits (owner, group, and world) on + # all the scripts we just installed. + for file in self.get_outputs(): + if self.dry_run: + log.info("changing mode of %s", file) + else: + mode = ((os.stat(file)[ST_MODE]) | 0o555) & 0o7777 + log.info("changing mode of %s to %o", file, mode) + os.chmod(file, mode) + + def get_inputs(self): + return self.distribution.scripts or [] + + def get_outputs(self): + return self.outfiles or [] diff --git a/venv/Lib/site-packages/setuptools/_distutils/command/register.py b/venv/Lib/site-packages/setuptools/_distutils/command/register.py new file mode 100644 index 0000000..ee6c54d --- /dev/null +++ b/venv/Lib/site-packages/setuptools/_distutils/command/register.py @@ -0,0 +1,323 @@ +"""distutils.command.register + +Implements the Distutils 'register' command (register with the repository). +""" + +# created 2002/10/21, Richard Jones + +import getpass +import io +import logging +import urllib.parse +import urllib.request +from distutils._log import log +from warnings import warn + +from .._itertools import always_iterable +from ..core import PyPIRCCommand + + +class register(PyPIRCCommand): + description = "register the distribution with the Python package index" + user_options = PyPIRCCommand.user_options + [ + ('list-classifiers', None, 'list the valid Trove classifiers'), + ( + 'strict', + None, + 'Will stop the registering if the meta-data are not fully compliant', + ), + ] + boolean_options = PyPIRCCommand.boolean_options + [ + 'verify', + 'list-classifiers', + 'strict', + ] + + sub_commands = [('check', lambda self: True)] + + def initialize_options(self): + PyPIRCCommand.initialize_options(self) + self.list_classifiers = 0 + self.strict = 0 + + def finalize_options(self): + PyPIRCCommand.finalize_options(self) + # setting options for the `check` subcommand + check_options = { + 'strict': ('register', self.strict), + 'restructuredtext': ('register', 1), + } + self.distribution.command_options['check'] = check_options + + def run(self): + self.finalize_options() + self._set_config() + + # Run sub commands + for cmd_name in self.get_sub_commands(): + self.run_command(cmd_name) + + if self.dry_run: + self.verify_metadata() + elif self.list_classifiers: + self.classifiers() + else: + self.send_metadata() + + def check_metadata(self): + """Deprecated API.""" + warn( + "distutils.command.register.check_metadata is deprecated; " + "use the check command instead", + DeprecationWarning, + ) + check = self.distribution.get_command_obj('check') + check.ensure_finalized() + check.strict = self.strict + check.restructuredtext = 1 + check.run() + + def _set_config(self): + """Reads the configuration file and set attributes.""" + config = self._read_pypirc() + if config != {}: + self.username = config['username'] + self.password = config['password'] + self.repository = config['repository'] + self.realm = config['realm'] + self.has_config = True + else: + if self.repository not in ('pypi', self.DEFAULT_REPOSITORY): + raise ValueError('%s not found in .pypirc' % self.repository) + if self.repository == 'pypi': + self.repository = self.DEFAULT_REPOSITORY + self.has_config = False + + def classifiers(self): + """Fetch the list of classifiers from the server.""" + url = self.repository + '?:action=list_classifiers' + response = urllib.request.urlopen(url) + log.info(self._read_pypi_response(response)) + + def verify_metadata(self): + """Send the metadata to the package index server to be checked.""" + # send the info to the server and report the result + (code, result) = self.post_to_server(self.build_post_data('verify')) + log.info('Server response (%s): %s', code, result) + + def send_metadata(self): # noqa: C901 + """Send the metadata to the package index server. + + Well, do the following: + 1. figure who the user is, and then + 2. send the data as a Basic auth'ed POST. + + First we try to read the username/password from $HOME/.pypirc, + which is a ConfigParser-formatted file with a section + [distutils] containing username and password entries (both + in clear text). Eg: + + [distutils] + index-servers = + pypi + + [pypi] + username: fred + password: sekrit + + Otherwise, to figure who the user is, we offer the user three + choices: + + 1. use existing login, + 2. register as a new user, or + 3. set the password to a random string and email the user. + + """ + # see if we can short-cut and get the username/password from the + # config + if self.has_config: + choice = '1' + username = self.username + password = self.password + else: + choice = 'x' + username = password = '' + + # get the user's login info + choices = '1 2 3 4'.split() + while choice not in choices: + self.announce( + """\ +We need to know who you are, so please choose either: + 1. use your existing login, + 2. register as a new user, + 3. have the server generate a new password for you (and email it to you), or + 4. quit +Your selection [default 1]: """, + logging.INFO, + ) + choice = input() + if not choice: + choice = '1' + elif choice not in choices: + print('Please choose one of the four options!') + + if choice == '1': + # get the username and password + while not username: + username = input('Username: ') + while not password: + password = getpass.getpass('Password: ') + + # set up the authentication + auth = urllib.request.HTTPPasswordMgr() + host = urllib.parse.urlparse(self.repository)[1] + auth.add_password(self.realm, host, username, password) + # send the info to the server and report the result + code, result = self.post_to_server(self.build_post_data('submit'), auth) + self.announce(f'Server response ({code}): {result}', logging.INFO) + + # possibly save the login + if code == 200: + if self.has_config: + # sharing the password in the distribution instance + # so the upload command can reuse it + self.distribution.password = password + else: + self.announce( + ( + 'I can store your PyPI login so future ' + 'submissions will be faster.' + ), + logging.INFO, + ) + self.announce( + '(the login will be stored in %s)' % self._get_rc_file(), + logging.INFO, + ) + choice = 'X' + while choice.lower() not in 'yn': + choice = input('Save your login (y/N)?') + if not choice: + choice = 'n' + if choice.lower() == 'y': + self._store_pypirc(username, password) + + elif choice == '2': + data = {':action': 'user'} + data['name'] = data['password'] = data['email'] = '' + data['confirm'] = None + while not data['name']: + data['name'] = input('Username: ') + while data['password'] != data['confirm']: + while not data['password']: + data['password'] = getpass.getpass('Password: ') + while not data['confirm']: + data['confirm'] = getpass.getpass(' Confirm: ') + if data['password'] != data['confirm']: + data['password'] = '' + data['confirm'] = None + print("Password and confirm don't match!") + while not data['email']: + data['email'] = input(' EMail: ') + code, result = self.post_to_server(data) + if code != 200: + log.info('Server response (%s): %s', code, result) + else: + log.info('You will receive an email shortly.') + log.info('Follow the instructions in it to ' 'complete registration.') + elif choice == '3': + data = {':action': 'password_reset'} + data['email'] = '' + while not data['email']: + data['email'] = input('Your email address: ') + code, result = self.post_to_server(data) + log.info('Server response (%s): %s', code, result) + + def build_post_data(self, action): + # figure the data to send - the metadata plus some additional + # information used by the package server + meta = self.distribution.metadata + data = { + ':action': action, + 'metadata_version': '1.0', + 'name': meta.get_name(), + 'version': meta.get_version(), + 'summary': meta.get_description(), + 'home_page': meta.get_url(), + 'author': meta.get_contact(), + 'author_email': meta.get_contact_email(), + 'license': meta.get_licence(), + 'description': meta.get_long_description(), + 'keywords': meta.get_keywords(), + 'platform': meta.get_platforms(), + 'classifiers': meta.get_classifiers(), + 'download_url': meta.get_download_url(), + # PEP 314 + 'provides': meta.get_provides(), + 'requires': meta.get_requires(), + 'obsoletes': meta.get_obsoletes(), + } + if data['provides'] or data['requires'] or data['obsoletes']: + data['metadata_version'] = '1.1' + return data + + def post_to_server(self, data, auth=None): # noqa: C901 + """Post a query to the server, and return a string response.""" + if 'name' in data: + self.announce( + 'Registering {} to {}'.format(data['name'], self.repository), + logging.INFO, + ) + # Build up the MIME payload for the urllib2 POST data + boundary = '--------------GHSKFJDLGDS7543FJKLFHRE75642756743254' + sep_boundary = '\n--' + boundary + end_boundary = sep_boundary + '--' + body = io.StringIO() + for key, values in data.items(): + for value in map(str, make_iterable(values)): + body.write(sep_boundary) + body.write('\nContent-Disposition: form-data; name="%s"' % key) + body.write("\n\n") + body.write(value) + if value and value[-1] == '\r': + body.write('\n') # write an extra newline (lurve Macs) + body.write(end_boundary) + body.write("\n") + body = body.getvalue().encode("utf-8") + + # build the Request + headers = { + 'Content-type': 'multipart/form-data; boundary=%s; charset=utf-8' + % boundary, + 'Content-length': str(len(body)), + } + req = urllib.request.Request(self.repository, body, headers) + + # handle HTTP and include the Basic Auth handler + opener = urllib.request.build_opener( + urllib.request.HTTPBasicAuthHandler(password_mgr=auth) + ) + data = '' + try: + result = opener.open(req) + except urllib.error.HTTPError as e: + if self.show_response: + data = e.fp.read() + result = e.code, e.msg + except urllib.error.URLError as e: + result = 500, str(e) + else: + if self.show_response: + data = self._read_pypi_response(result) + result = 200, 'OK' + if self.show_response: + msg = '\n'.join(('-' * 75, data, '-' * 75)) + self.announce(msg, logging.INFO) + return result + + +def make_iterable(values): + if values is None: + return [None] + return always_iterable(values) diff --git a/venv/Lib/site-packages/setuptools/_distutils/command/sdist.py b/venv/Lib/site-packages/setuptools/_distutils/command/sdist.py new file mode 100644 index 0000000..387d27c --- /dev/null +++ b/venv/Lib/site-packages/setuptools/_distutils/command/sdist.py @@ -0,0 +1,528 @@ +"""distutils.command.sdist + +Implements the Distutils 'sdist' command (create a source distribution).""" + +import os +import sys +from distutils import archive_util, dir_util, file_util +from distutils._log import log +from glob import glob +from itertools import filterfalse +from warnings import warn + +from ..core import Command +from ..errors import DistutilsOptionError, DistutilsTemplateError +from ..filelist import FileList +from ..text_file import TextFile +from ..util import convert_path + + +def show_formats(): + """Print all possible values for the 'formats' option (used by + the "--help-formats" command-line option). + """ + from ..archive_util import ARCHIVE_FORMATS + from ..fancy_getopt import FancyGetopt + + formats = [] + for format in ARCHIVE_FORMATS.keys(): + formats.append(("formats=" + format, None, ARCHIVE_FORMATS[format][2])) + formats.sort() + FancyGetopt(formats).print_help("List of available source distribution formats:") + + +class sdist(Command): + description = "create a source distribution (tarball, zip file, etc.)" + + def checking_metadata(self): + """Callable used for the check sub-command. + + Placed here so user_options can view it""" + return self.metadata_check + + user_options = [ + ('template=', 't', "name of manifest template file [default: MANIFEST.in]"), + ('manifest=', 'm', "name of manifest file [default: MANIFEST]"), + ( + 'use-defaults', + None, + "include the default file set in the manifest " + "[default; disable with --no-defaults]", + ), + ('no-defaults', None, "don't include the default file set"), + ( + 'prune', + None, + "specifically exclude files/directories that should not be " + "distributed (build tree, RCS/CVS dirs, etc.) " + "[default; disable with --no-prune]", + ), + ('no-prune', None, "don't automatically exclude anything"), + ( + 'manifest-only', + 'o', + "just regenerate the manifest and then stop (implies --force-manifest)", + ), + ( + 'force-manifest', + 'f', + "forcibly regenerate the manifest and carry on as usual. " + "Deprecated: now the manifest is always regenerated.", + ), + ('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]", + ), + ( + 'metadata-check', + None, + "Ensure that all required elements of meta-data " + "are supplied. Warn if any missing. [default]", + ), + ( + 'owner=', + 'u', + "Owner name used when creating a tar file [default: current user]", + ), + ( + 'group=', + 'g', + "Group name used when creating a tar file [default: current group]", + ), + ] + + boolean_options = [ + 'use-defaults', + 'prune', + 'manifest-only', + 'force-manifest', + 'keep-temp', + 'metadata-check', + ] + + help_options = [ + ('help-formats', None, "list available distribution formats", show_formats), + ] + + negative_opt = {'no-defaults': 'use-defaults', 'no-prune': 'prune'} + + sub_commands = [('check', checking_metadata)] + + READMES = ('README', 'README.txt', 'README.rst') + + def initialize_options(self): + # 'template' and 'manifest' are, respectively, the names of + # the manifest template and manifest file. + self.template = None + self.manifest = None + + # 'use_defaults': if true, we will include the default file set + # in the manifest + self.use_defaults = 1 + self.prune = 1 + + self.manifest_only = 0 + self.force_manifest = 0 + + self.formats = ['gztar'] + self.keep_temp = 0 + self.dist_dir = None + + self.archive_files = None + self.metadata_check = 1 + self.owner = None + self.group = None + + def finalize_options(self): + if self.manifest is None: + self.manifest = "MANIFEST" + if self.template is None: + self.template = "MANIFEST.in" + + self.ensure_string_list('formats') + + bad_format = archive_util.check_archive_formats(self.formats) + if bad_format: + raise DistutilsOptionError("unknown archive format '%s'" % bad_format) + + if self.dist_dir is None: + self.dist_dir = "dist" + + def run(self): + # 'filelist' contains the list of files that will make up the + # manifest + self.filelist = FileList() + + # Run sub commands + for cmd_name in self.get_sub_commands(): + self.run_command(cmd_name) + + # Do whatever it takes to get the list of files to process + # (process the manifest template, read an existing manifest, + # whatever). File list is accumulated in 'self.filelist'. + self.get_file_list() + + # If user just wanted us to regenerate the manifest, stop now. + if self.manifest_only: + return + + # Otherwise, go ahead and create the source distribution tarball, + # or zipfile, or whatever. + self.make_distribution() + + def check_metadata(self): + """Deprecated API.""" + warn( + "distutils.command.sdist.check_metadata is deprecated, \ + use the check command instead", + PendingDeprecationWarning, + ) + check = self.distribution.get_command_obj('check') + check.ensure_finalized() + check.run() + + def get_file_list(self): + """Figure out the list of files to include in the source + distribution, and put it in 'self.filelist'. This might involve + reading the manifest template (and writing the manifest), or just + reading the manifest, or just using the default file set -- it all + depends on the user's options. + """ + # new behavior when using a template: + # the file list is recalculated every time because + # even if MANIFEST.in or setup.py are not changed + # the user might have added some files in the tree that + # need to be included. + # + # This makes --force the default and only behavior with templates. + template_exists = os.path.isfile(self.template) + if not template_exists and self._manifest_is_not_generated(): + self.read_manifest() + self.filelist.sort() + self.filelist.remove_duplicates() + return + + if not template_exists: + self.warn( + ("manifest template '%s' does not exist " + "(using default file list)") + % self.template + ) + self.filelist.findall() + + if self.use_defaults: + self.add_defaults() + + if template_exists: + self.read_template() + + if self.prune: + self.prune_file_list() + + self.filelist.sort() + self.filelist.remove_duplicates() + self.write_manifest() + + def add_defaults(self): + """Add all the default files to self.filelist: + - README or README.txt + - setup.py + - tests/test*.py and 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._cs_path_exists(__file__) + True + >>> sdist._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 = ['tests/test*.py', '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()) + + def read_template(self): + """Read and parse manifest template file named by self.template. + + (usually "MANIFEST.in") The parsing and processing is done by + 'self.filelist', which updates itself accordingly. + """ + log.info("reading manifest template '%s'", self.template) + template = TextFile( + self.template, + strip_comments=1, + skip_blanks=1, + join_lines=1, + lstrip_ws=1, + rstrip_ws=1, + collapse_join=1, + ) + + try: + while True: + line = template.readline() + if line is None: # end of file + break + + try: + self.filelist.process_template_line(line) + # the call above can raise a DistutilsTemplateError for + # malformed lines, or a ValueError from the lower-level + # convert_path function + except (DistutilsTemplateError, ValueError) as msg: + self.warn( + "%s, line %d: %s" + % (template.filename, template.current_line, msg) + ) + finally: + template.close() + + def prune_file_list(self): + """Prune off branches that might slip into the file list as created + by 'read_template()', but really don't belong there: + * the build tree (typically "build") + * the release tree itself (only an issue if we ran "sdist" + previously with --keep-temp, or it aborted) + * any RCS, CVS, .svn, .hg, .git, .bzr, _darcs directories + """ + build = self.get_finalized_command('build') + base_dir = self.distribution.get_fullname() + + self.filelist.exclude_pattern(None, prefix=build.build_base) + self.filelist.exclude_pattern(None, prefix=base_dir) + + if sys.platform == 'win32': + seps = r'/|\\' + else: + seps = '/' + + vcs_dirs = ['RCS', 'CVS', r'\.svn', r'\.hg', r'\.git', r'\.bzr', '_darcs'] + vcs_ptrn = r'(^|{})({})({}).*'.format(seps, '|'.join(vcs_dirs), seps) + self.filelist.exclude_pattern(vcs_ptrn, is_regex=1) + + def write_manifest(self): + """Write the file list in 'self.filelist' (presumably as filled in + by 'add_defaults()' and 'read_template()') to the manifest file + named by 'self.manifest'. + """ + if self._manifest_is_not_generated(): + log.info( + "not writing to manually maintained " + "manifest file '%s'" % self.manifest + ) + return + + content = self.filelist.files[:] + content.insert(0, '# file GENERATED by distutils, do NOT edit') + self.execute( + file_util.write_file, + (self.manifest, content), + "writing manifest file '%s'" % self.manifest, + ) + + def _manifest_is_not_generated(self): + # check for special comment used in 3.1.3 and higher + if not os.path.isfile(self.manifest): + return False + + with open(self.manifest, encoding='utf-8') as fp: + first_line = next(fp) + return first_line != '# file GENERATED by distutils, do NOT edit\n' + + 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) + with open(self.manifest, encoding='utf-8') as lines: + self.filelist.extend( + # ignore comments and blank lines + filter(None, filterfalse(is_comment, map(str.strip, lines))) + ) + + def make_release_tree(self, base_dir, files): + """Create the directory tree that will become the source + distribution archive. All directories implied by the filenames in + 'files' are created under 'base_dir', and then we hard link or copy + (if hard linking is unavailable) those files into place. + Essentially, this duplicates the developer's source tree, but in a + directory named after the distribution, containing only the files + to be distributed. + """ + # Create all the directories under 'base_dir' necessary to + # put 'files' there; the 'mkpath()' is just so we don't die + # if the manifest happens to be empty. + self.mkpath(base_dir) + dir_util.create_tree(base_dir, files, dry_run=self.dry_run) + + # And walk over the list of files, either making a hard link (if + # os.link exists) to each one that doesn't already exist in its + # corresponding location under 'base_dir', or copying each file + # that's out-of-date in 'base_dir'. (Usually, all files will be + # out-of-date, because by default we blow away 'base_dir' when + # we're done making the distribution archives.) + + if hasattr(os, 'link'): # can make hard links on this system + link = 'hard' + msg = "making hard links in %s..." % base_dir + else: # nope, have to copy + link = None + msg = "copying files to %s..." % base_dir + + if not files: + log.warning("no files to distribute -- empty manifest?") + else: + log.info(msg) + for file in files: + if not os.path.isfile(file): + log.warning("'%s' not a regular file -- skipping", file) + else: + dest = os.path.join(base_dir, file) + self.copy_file(file, dest, link=link) + + self.distribution.metadata.write_pkg_info(base_dir) + + def make_distribution(self): + """Create the source distribution(s). First, we create the release + tree with 'make_release_tree()'; then, we create all required + archive files (according to 'self.formats') from the release tree. + Finally, we clean up by blowing away the release tree (unless + 'self.keep_temp' is true). The list of archive files created is + stored so it can be retrieved later by 'get_archive_files()'. + """ + # Don't warn about missing meta-data here -- should be (and is!) + # done elsewhere. + base_dir = self.distribution.get_fullname() + base_name = os.path.join(self.dist_dir, base_dir) + + self.make_release_tree(base_dir, self.filelist.files) + archive_files = [] # remember names of files we create + # tar archive must be created last to avoid overwrite and remove + if 'tar' in self.formats: + self.formats.append(self.formats.pop(self.formats.index('tar'))) + + for fmt in self.formats: + file = self.make_archive( + base_name, fmt, base_dir=base_dir, owner=self.owner, group=self.group + ) + archive_files.append(file) + self.distribution.dist_files.append(('sdist', '', file)) + + self.archive_files = archive_files + + if not self.keep_temp: + dir_util.remove_tree(base_dir, dry_run=self.dry_run) + + def get_archive_files(self): + """Return the list of archive files created when the command + was run, or None if the command hasn't run yet. + """ + return self.archive_files + + +def is_comment(line): + return line.startswith('#') diff --git a/venv/Lib/site-packages/setuptools/_distutils/command/upload.py b/venv/Lib/site-packages/setuptools/_distutils/command/upload.py new file mode 100644 index 0000000..cf541f8 --- /dev/null +++ b/venv/Lib/site-packages/setuptools/_distutils/command/upload.py @@ -0,0 +1,208 @@ +""" +distutils.command.upload + +Implements the Distutils 'upload' subcommand (upload package to a package +index). +""" + +import hashlib +import io +import logging +import os +from base64 import standard_b64encode +from urllib.parse import urlparse +from urllib.request import HTTPError, Request, urlopen + +from .._itertools import always_iterable +from ..core import PyPIRCCommand +from ..errors import DistutilsError, DistutilsOptionError +from ..spawn import spawn + +# PyPI Warehouse supports MD5, SHA256, and Blake2 (blake2-256) +# https://bugs.python.org/issue40698 +_FILE_CONTENT_DIGESTS = { + "md5_digest": getattr(hashlib, "md5", None), + "sha256_digest": getattr(hashlib, "sha256", None), + "blake2_256_digest": getattr(hashlib, "blake2b", None), +} + + +class upload(PyPIRCCommand): + description = "upload binary package to PyPI" + + user_options = PyPIRCCommand.user_options + [ + ('sign', 's', 'sign files to upload using gpg'), + ('identity=', 'i', 'GPG identity used to sign files'), + ] + + boolean_options = PyPIRCCommand.boolean_options + ['sign'] + + def initialize_options(self): + PyPIRCCommand.initialize_options(self) + self.username = '' + self.password = '' + self.show_response = 0 + self.sign = False + self.identity = None + + def finalize_options(self): + PyPIRCCommand.finalize_options(self) + if self.identity and not self.sign: + raise DistutilsOptionError("Must use --sign for --identity to have meaning") + config = self._read_pypirc() + if config != {}: + self.username = config['username'] + self.password = config['password'] + self.repository = config['repository'] + self.realm = config['realm'] + + # getting the password from the distribution + # if previously set by the register command + if not self.password and self.distribution.password: + self.password = self.distribution.password + + def run(self): + if not self.distribution.dist_files: + msg = ( + "Must create and upload files in one command " + "(e.g. setup.py sdist upload)" + ) + raise DistutilsOptionError(msg) + for command, pyversion, filename in self.distribution.dist_files: + self.upload_file(command, pyversion, filename) + + def upload_file(self, command, pyversion, filename): # noqa: C901 + # Makes sure the repository URL is compliant + schema, netloc, url, params, query, fragments = urlparse(self.repository) + if params or query or fragments: + raise AssertionError("Incompatible url %s" % self.repository) + + if schema not in ('http', 'https'): + raise AssertionError("unsupported schema " + schema) + + # Sign if requested + if self.sign: + gpg_args = ["gpg", "--detach-sign", "-a", filename] + if self.identity: + gpg_args[2:2] = ["--local-user", self.identity] + spawn(gpg_args, dry_run=self.dry_run) + + # Fill in the data - send all the meta-data in case we need to + # register a new release + f = open(filename, 'rb') + try: + content = f.read() + finally: + f.close() + + meta = self.distribution.metadata + data = { + # action + ':action': 'file_upload', + 'protocol_version': '1', + # identify release + 'name': meta.get_name(), + 'version': meta.get_version(), + # file content + 'content': (os.path.basename(filename), content), + 'filetype': command, + 'pyversion': pyversion, + # additional meta-data + 'metadata_version': '1.0', + 'summary': meta.get_description(), + 'home_page': meta.get_url(), + 'author': meta.get_contact(), + 'author_email': meta.get_contact_email(), + 'license': meta.get_licence(), + 'description': meta.get_long_description(), + 'keywords': meta.get_keywords(), + 'platform': meta.get_platforms(), + 'classifiers': meta.get_classifiers(), + 'download_url': meta.get_download_url(), + # PEP 314 + 'provides': meta.get_provides(), + 'requires': meta.get_requires(), + 'obsoletes': meta.get_obsoletes(), + } + + data['comment'] = '' + + # file content digests + for digest_name, digest_cons in _FILE_CONTENT_DIGESTS.items(): + if digest_cons is None: + continue + try: + data[digest_name] = digest_cons(content).hexdigest() + except ValueError: + # hash digest not available or blocked by security policy + pass + + if self.sign: + with open(filename + ".asc", "rb") as f: + data['gpg_signature'] = (os.path.basename(filename) + ".asc", f.read()) + + # set up the authentication + user_pass = (self.username + ":" + self.password).encode('ascii') + # The exact encoding of the authentication string is debated. + # Anyway PyPI only accepts ascii for both username or password. + auth = "Basic " + standard_b64encode(user_pass).decode('ascii') + + # Build up the MIME payload for the POST data + boundary = '--------------GHSKFJDLGDS7543FJKLFHRE75642756743254' + sep_boundary = b'\r\n--' + boundary.encode('ascii') + end_boundary = sep_boundary + b'--\r\n' + body = io.BytesIO() + for key, values in data.items(): + title = '\r\nContent-Disposition: form-data; name="%s"' % key + for value in make_iterable(values): + if type(value) is tuple: + title += '; filename="%s"' % value[0] + value = value[1] + else: + value = str(value).encode('utf-8') + body.write(sep_boundary) + body.write(title.encode('utf-8')) + body.write(b"\r\n\r\n") + body.write(value) + body.write(end_boundary) + body = body.getvalue() + + msg = f"Submitting {filename} to {self.repository}" + self.announce(msg, logging.INFO) + + # build the Request + headers = { + 'Content-type': 'multipart/form-data; boundary=%s' % boundary, + 'Content-length': str(len(body)), + 'Authorization': auth, + } + + request = Request(self.repository, data=body, headers=headers) + # send the data + try: + result = urlopen(request) + status = result.getcode() + reason = result.msg + except HTTPError as e: + status = e.code + reason = e.msg + except OSError as e: + self.announce(str(e), logging.ERROR) + raise + + if status == 200: + self.announce(f'Server response ({status}): {reason}', logging.INFO) + if self.show_response: + text = self._read_pypi_response(result) + msg = '\n'.join(('-' * 75, text, '-' * 75)) + self.announce(msg, logging.INFO) + else: + msg = f'Upload failed ({status}): {reason}' + self.announce(msg, logging.ERROR) + raise DistutilsError(msg) + + +def make_iterable(values): + if values is None: + return [None] + return always_iterable(values, base_type=(bytes, str, tuple)) diff --git a/venv/Lib/site-packages/setuptools/_distutils/compat/__init__.py b/venv/Lib/site-packages/setuptools/_distutils/compat/__init__.py new file mode 100644 index 0000000..b1ee3fe --- /dev/null +++ b/venv/Lib/site-packages/setuptools/_distutils/compat/__init__.py @@ -0,0 +1,15 @@ +from __future__ import annotations + +from .py38 import removeprefix + + +def consolidate_linker_args(args: list[str]) -> str: + """ + Ensure the return value is a string for backward compatibility. + + Retain until at least 2024-04-31. See pypa/distutils#246 + """ + + if not all(arg.startswith('-Wl,') for arg in args): + return args + return '-Wl,' + ','.join(removeprefix(arg, '-Wl,') for arg in args) diff --git a/venv/Lib/site-packages/setuptools/_distutils/compat/__pycache__/__init__.cpython-312.pyc b/venv/Lib/site-packages/setuptools/_distutils/compat/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..aba3fa961d42b15b7ee442032a0644e4c92bf7d3 GIT binary patch literal 1181 zcmb7@O=uHA6o6-vZM8{%{B0Fm8LS>6CarA=BBhrqDJVrOBGi?2o1N6Dlig)zHf=&I zMG96B)Qk1t#al}eJbLu*#cDxW@E{`SO{fRW$v4|HqUgbQc)Xc;Z|1%4y?NQzmOvol zL%Q$;BlJ-(wk5V@;LW38EF%Xwn4=<|#27g-C(dKV_+%XHX2)1GCXI4aYR4BHlEPuq zc3n@|ig~X50{5a-9JUHt^h(qhw7}-_dUIQvz=$J+H?M)Qj8x;y!HcMh&Y-or=UV+l z#;Rx{N@51${uD^(uTd3noIt6j%IyRJIqS+mP@=9=BB%<4OG-8mC}EP=L@L4DX;Sb6 znX>aYW^Lh+yjS$0q$$RkDrXYWB^Rl(nM(p!F-~knIJKoB1N{R->HeYg;K>Y`pp^J! z-|lmmQ~|ij-hrWP6#N?m1F{>`5Jtc_hcJMsqO1E6x{0fJrh(*Fu@2hC@enUVPwsXZ zsEnV+XYp#RW~49k6XVc{gjBXra+ay)pq z>MCiDdJfcYUSN43L(><6N`=$(ES-`}QL{wdl6js@nR=D>0gydSr6~cUuRM>-KC4k} z-xjt$%VI85mX-0#wU*&&>e4x1j8u;2VMIL699vO~Gxr7+mQFA;%)PwLLFN#2{kGykaYw&U1(d-jLa5N{po!9q`U4WIapupYCB@DLac!n-u# zUn5t=z_mX4b*_81xr!DD6#5#`3aSoKbm$GZ2K`SlI!Jbb+A5|y_k8fNxzf8*dfSs- z@6Odr={$f_G3XfQp)unz7aD44I#SOAiCzx7{y|W5f={!4<|L>E^sODWb^xVk`!?FM z8_Dduj=f9CcgfxNN0-MQjKP~Pj&5}4-t5b*Cv&fi+$IE}@suH81W~1h!}MN(UvV{T z-uf+J99F5OFkxARKo5y!3H=#E!!Hk>68hmNgHR)Yi`qN;+VC1pS=Ra=z-#86cz66%61p%9;`!Li*tJ2WT* zLx*;1IwchhtWp1zE}=yNxeKUMwhC`deRmFpRFzozhVSlu@7edg&*!h1OcFtmvyw3v zL+B?@c^RhB%V2d8x0+x*E;ybh&)8l|*L(nCj$x&7u>9f}5 zVyrL1+P?)*z5Xr<2v!Np0BzJ1NWO_S1xWsirtoFJ5~T^sJoXfit4a!uqZP!eI^T5# zI52LsI=1WDOLJXgS;?(bw<;T$Y7~3E>p6}^XV!F+dcJ4E zw(8W}iZ|mnW*W_KXQ4IsemR8xVN>Hka8qBv228!l&}{Rcc19*%*-2!09T=LVUWItv zQj!3w8zbyW1+q)@~ literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/setuptools/_distutils/compat/py38.py b/venv/Lib/site-packages/setuptools/_distutils/compat/py38.py new file mode 100644 index 0000000..0af3814 --- /dev/null +++ b/venv/Lib/site-packages/setuptools/_distutils/compat/py38.py @@ -0,0 +1,23 @@ +import sys + +if sys.version_info < (3, 9): + + def removesuffix(self, suffix): + # suffix='' should not call self[:-0]. + if suffix and self.endswith(suffix): + return self[: -len(suffix)] + else: + return self[:] + + def removeprefix(self, prefix): + if self.startswith(prefix): + return self[len(prefix) :] + else: + return self[:] +else: + + def removesuffix(self, suffix): + return self.removesuffix(suffix) + + def removeprefix(self, prefix): + return self.removeprefix(prefix) diff --git a/venv/Lib/site-packages/setuptools/_distutils/config.py b/venv/Lib/site-packages/setuptools/_distutils/config.py new file mode 100644 index 0000000..83f96a9 --- /dev/null +++ b/venv/Lib/site-packages/setuptools/_distutils/config.py @@ -0,0 +1,151 @@ +"""distutils.pypirc + +Provides the PyPIRCCommand class, the base class for the command classes +that uses .pypirc in the distutils.command package. +""" + +import email.message +import os +from configparser import RawConfigParser + +from .cmd import Command + +DEFAULT_PYPIRC = """\ +[distutils] +index-servers = + pypi + +[pypi] +username:%s +password:%s +""" + + +class PyPIRCCommand(Command): + """Base command that knows how to handle the .pypirc file""" + + DEFAULT_REPOSITORY = 'https://upload.pypi.org/legacy/' + DEFAULT_REALM = 'pypi' + repository = None + realm = None + + user_options = [ + ('repository=', 'r', "url of repository [default: %s]" % DEFAULT_REPOSITORY), + ('show-response', None, 'display full response text from server'), + ] + + boolean_options = ['show-response'] + + def _get_rc_file(self): + """Returns rc file path.""" + return os.path.join(os.path.expanduser('~'), '.pypirc') + + def _store_pypirc(self, username, password): + """Creates a default .pypirc file.""" + rc = self._get_rc_file() + raw = os.open(rc, os.O_CREAT | os.O_WRONLY, 0o600) + with os.fdopen(raw, 'w', encoding='utf-8') as f: + f.write(DEFAULT_PYPIRC % (username, password)) + + def _read_pypirc(self): # noqa: C901 + """Reads the .pypirc file.""" + rc = self._get_rc_file() + if os.path.exists(rc): + self.announce('Using PyPI login from %s' % rc) + repository = self.repository or self.DEFAULT_REPOSITORY + + config = RawConfigParser() + config.read(rc, encoding='utf-8') + sections = config.sections() + if 'distutils' in sections: + # let's get the list of servers + index_servers = config.get('distutils', 'index-servers') + _servers = [ + server.strip() + for server in index_servers.split('\n') + if server.strip() != '' + ] + if _servers == []: + # nothing set, let's try to get the default pypi + if 'pypi' in sections: + _servers = ['pypi'] + else: + # the file is not properly defined, returning + # an empty dict + return {} + for server in _servers: + current = {'server': server} + current['username'] = config.get(server, 'username') + + # optional params + for key, default in ( + ('repository', self.DEFAULT_REPOSITORY), + ('realm', self.DEFAULT_REALM), + ('password', None), + ): + if config.has_option(server, key): + current[key] = config.get(server, key) + else: + current[key] = default + + # work around people having "repository" for the "pypi" + # section of their config set to the HTTP (rather than + # HTTPS) URL + if server == 'pypi' and repository in ( + self.DEFAULT_REPOSITORY, + 'pypi', + ): + current['repository'] = self.DEFAULT_REPOSITORY + return current + + if ( + current['server'] == repository + or current['repository'] == repository + ): + return current + elif 'server-login' in sections: + # old format + server = 'server-login' + if config.has_option(server, 'repository'): + repository = config.get(server, 'repository') + else: + repository = self.DEFAULT_REPOSITORY + return { + 'username': config.get(server, 'username'), + 'password': config.get(server, 'password'), + 'repository': repository, + 'server': server, + 'realm': self.DEFAULT_REALM, + } + + return {} + + def _read_pypi_response(self, response): + """Read and decode a PyPI HTTP response.""" + content_type = response.getheader('content-type', 'text/plain') + return response.read().decode(_extract_encoding(content_type)) + + def initialize_options(self): + """Initialize options.""" + self.repository = None + self.realm = None + self.show_response = 0 + + def finalize_options(self): + """Finalizes options.""" + if self.repository is None: + self.repository = self.DEFAULT_REPOSITORY + if self.realm is None: + self.realm = self.DEFAULT_REALM + + +def _extract_encoding(content_type): + """ + >>> _extract_encoding('text/plain') + 'ascii' + >>> _extract_encoding('text/html; charset="utf8"') + 'utf8' + """ + msg = email.message.EmailMessage() + msg['content-type'] = content_type + return msg['content-type'].params.get('charset', 'ascii') diff --git a/venv/Lib/site-packages/setuptools/_distutils/core.py b/venv/Lib/site-packages/setuptools/_distutils/core.py new file mode 100644 index 0000000..309ce69 --- /dev/null +++ b/venv/Lib/site-packages/setuptools/_distutils/core.py @@ -0,0 +1,290 @@ +"""distutils.core + +The only module that needs to be imported to use the Distutils; provides +the 'setup' function (which is to be called from the setup script). Also +indirectly provides the Distribution and Command classes, although they are +really defined in distutils.dist and distutils.cmd. +""" + +import os +import sys +import tokenize + +from .cmd import Command +from .config import PyPIRCCommand +from .debug import DEBUG + +# Mainly import these so setup scripts can "from distutils.core import" them. +from .dist import Distribution +from .errors import ( + CCompilerError, + DistutilsArgError, + DistutilsError, + DistutilsSetupError, +) +from .extension import Extension + +__all__ = ['Distribution', 'Command', 'PyPIRCCommand', 'Extension', 'setup'] + +# This is a barebones help message generated displayed when the user +# runs the setup script with no arguments at all. More useful help +# is generated with various --help options: global help, list commands, +# and per-command help. +USAGE = """\ +usage: %(script)s [global_opts] cmd1 [cmd1_opts] [cmd2 [cmd2_opts] ...] + or: %(script)s --help [cmd1 cmd2 ...] + or: %(script)s --help-commands + or: %(script)s cmd --help +""" + + +def gen_usage(script_name): + script = os.path.basename(script_name) + return USAGE % locals() + + +# Some mild magic to control the behaviour of 'setup()' from 'run_setup()'. +_setup_stop_after = None +_setup_distribution = None + +# Legal keyword arguments for the setup() function +setup_keywords = ( + 'distclass', + 'script_name', + 'script_args', + 'options', + 'name', + 'version', + 'author', + 'author_email', + 'maintainer', + 'maintainer_email', + 'url', + 'license', + 'description', + 'long_description', + 'keywords', + 'platforms', + 'classifiers', + 'download_url', + 'requires', + 'provides', + 'obsoletes', +) + +# Legal keyword arguments for the Extension constructor +extension_keywords = ( + 'name', + 'sources', + 'include_dirs', + 'define_macros', + 'undef_macros', + 'library_dirs', + 'libraries', + 'runtime_library_dirs', + 'extra_objects', + 'extra_compile_args', + 'extra_link_args', + 'swig_opts', + 'export_symbols', + 'depends', + 'language', +) + + +def setup(**attrs): # noqa: C901 + """The gateway to the Distutils: do everything your setup script needs + to do, in a highly flexible and user-driven way. Briefly: create a + Distribution instance; find and parse config files; parse the command + line; run each Distutils command found there, customized by the options + supplied to 'setup()' (as keyword arguments), in config files, and on + the command line. + + The Distribution instance might be an instance of a class supplied via + the 'distclass' keyword argument to 'setup'; if no such class is + supplied, then the Distribution class (in dist.py) is instantiated. + All other arguments to 'setup' (except for 'cmdclass') are used to set + attributes of the Distribution instance. + + The 'cmdclass' argument, if supplied, is a dictionary mapping command + names to command classes. Each command encountered on the command line + will be turned into a command class, which is in turn instantiated; any + class found in 'cmdclass' is used in place of the default, which is + (for command 'foo_bar') class 'foo_bar' in module + 'distutils.command.foo_bar'. The command class must provide a + 'user_options' attribute which is a list of option specifiers for + 'distutils.fancy_getopt'. Any command-line options between the current + and the next command are used to set attributes of the current command + object. + + When the entire command-line has been successfully parsed, calls the + 'run()' method on each command object in turn. This method will be + driven entirely by the Distribution object (which each command object + has a reference to, thanks to its constructor), and the + command-specific options that became attributes of each command + object. + """ + + global _setup_stop_after, _setup_distribution + + # Determine the distribution class -- either caller-supplied or + # our Distribution (see below). + klass = attrs.get('distclass') + if klass: + attrs.pop('distclass') + else: + klass = Distribution + + if 'script_name' not in attrs: + attrs['script_name'] = os.path.basename(sys.argv[0]) + if 'script_args' not in attrs: + attrs['script_args'] = sys.argv[1:] + + # Create the Distribution instance, using the remaining arguments + # (ie. everything except distclass) to initialize it + try: + _setup_distribution = dist = klass(attrs) + except DistutilsSetupError as msg: + if 'name' not in attrs: + raise SystemExit("error in setup command: %s" % msg) + else: + raise SystemExit("error in {} setup command: {}".format(attrs['name'], msg)) + + if _setup_stop_after == "init": + return dist + + # Find and parse the config file(s): they will override options from + # the setup script, but be overridden by the command line. + dist.parse_config_files() + + if DEBUG: + print("options (after parsing config files):") + dist.dump_option_dicts() + + if _setup_stop_after == "config": + return dist + + # Parse the command line and override config files; any + # command-line errors are the end user's fault, so turn them into + # SystemExit to suppress tracebacks. + try: + ok = dist.parse_command_line() + except DistutilsArgError as msg: + raise SystemExit(gen_usage(dist.script_name) + "\nerror: %s" % msg) + + if DEBUG: + print("options (after parsing command line):") + dist.dump_option_dicts() + + if _setup_stop_after == "commandline": + return dist + + # And finally, run all the commands found on the command line. + if ok: + return run_commands(dist) + + return dist + + +# setup () + + +def run_commands(dist): + """Given a Distribution object run all the commands, + raising ``SystemExit`` errors in the case of failure. + + This function assumes that either ``sys.argv`` or ``dist.script_args`` + is already set accordingly. + """ + try: + dist.run_commands() + except KeyboardInterrupt: + raise SystemExit("interrupted") + except OSError as exc: + if DEBUG: + sys.stderr.write(f"error: {exc}\n") + raise + else: + raise SystemExit(f"error: {exc}") + + except (DistutilsError, CCompilerError) as msg: + if DEBUG: + raise + else: + raise SystemExit("error: " + str(msg)) + + return dist + + +def run_setup(script_name, script_args=None, stop_after="run"): + """Run a setup script in a somewhat controlled environment, and + return the Distribution instance that drives things. This is useful + if you need to find out the distribution meta-data (passed as + keyword args from 'script' to 'setup()', or the contents of the + config files or command-line. + + 'script_name' is a file that will be read and run with 'exec()'; + 'sys.argv[0]' will be replaced with 'script' for the duration of the + call. 'script_args' is a list of strings; if supplied, + 'sys.argv[1:]' will be replaced by 'script_args' for the duration of + the call. + + 'stop_after' tells 'setup()' when to stop processing; possible + values: + init + stop after the Distribution instance has been created and + populated with the keyword arguments to 'setup()' + config + stop after config files have been parsed (and their data + stored in the Distribution instance) + commandline + stop after the command-line ('sys.argv[1:]' or 'script_args') + have been parsed (and the data stored in the Distribution) + run [default] + stop after all commands have been run (the same as if 'setup()' + had been called in the usual way + + Returns the Distribution instance, which provides all information + used to drive the Distutils. + """ + if stop_after not in ('init', 'config', 'commandline', 'run'): + raise ValueError(f"invalid value for 'stop_after': {stop_after!r}") + + global _setup_stop_after, _setup_distribution + _setup_stop_after = stop_after + + save_argv = sys.argv.copy() + g = {'__file__': script_name, '__name__': '__main__'} + try: + try: + sys.argv[0] = script_name + if script_args is not None: + sys.argv[1:] = script_args + # tokenize.open supports automatic encoding detection + with tokenize.open(script_name) as f: + code = f.read().replace(r'\r\n', r'\n') + exec(code, g) + finally: + sys.argv = save_argv + _setup_stop_after = None + except SystemExit: + # Hmm, should we do something if exiting with a non-zero code + # (ie. error)? + pass + + if _setup_distribution is None: + raise RuntimeError( + ( + "'distutils.core.setup()' was never called -- " + "perhaps '%s' is not a Distutils setup script?" + ) + % script_name + ) + + # I wonder if the setup script's namespace -- g and l -- would be of + # any interest to callers? + # print "_setup_distribution:", _setup_distribution + return _setup_distribution + + +# run_setup () diff --git a/venv/Lib/site-packages/setuptools/_distutils/cygwinccompiler.py b/venv/Lib/site-packages/setuptools/_distutils/cygwinccompiler.py new file mode 100644 index 0000000..539f09d --- /dev/null +++ b/venv/Lib/site-packages/setuptools/_distutils/cygwinccompiler.py @@ -0,0 +1,355 @@ +"""distutils.cygwinccompiler + +Provides the CygwinCCompiler class, a subclass of UnixCCompiler that +handles the Cygwin port of the GNU C compiler to Windows. It also contains +the Mingw32CCompiler class which handles the mingw32 port of GCC (same as +cygwin in no-cygwin mode). +""" + +import copy +import os +import pathlib +import re +import shlex +import sys +import warnings +from subprocess import check_output + +from ._collections import RangeMap +from .errors import ( + CCompilerError, + CompileError, + DistutilsExecError, + DistutilsPlatformError, +) +from .file_util import write_file +from .unixccompiler import UnixCCompiler +from .version import LooseVersion, suppress_known_deprecation + +_msvcr_lookup = RangeMap.left( + { + # MSVC 7.0 + 1300: ['msvcr70'], + # MSVC 7.1 + 1310: ['msvcr71'], + # VS2005 / MSVC 8.0 + 1400: ['msvcr80'], + # VS2008 / MSVC 9.0 + 1500: ['msvcr90'], + # VS2010 / MSVC 10.0 + 1600: ['msvcr100'], + # VS2012 / MSVC 11.0 + 1700: ['msvcr110'], + # VS2013 / MSVC 12.0 + 1800: ['msvcr120'], + # VS2015 / MSVC 14.0 + 1900: ['vcruntime140'], + 2000: RangeMap.undefined_value, + }, +) + + +def get_msvcr(): + """Include the appropriate MSVC runtime library if Python was built + with MSVC 7.0 or later. + """ + match = re.search(r'MSC v\.(\d{4})', sys.version) + try: + msc_ver = int(match.group(1)) + except AttributeError: + return + try: + return _msvcr_lookup[msc_ver] + except KeyError: + raise ValueError("Unknown MS Compiler version %s " % msc_ver) + + +_runtime_library_dirs_msg = ( + "Unable to set runtime library search path on Windows, " + "usually indicated by `runtime_library_dirs` parameter to Extension" +) + + +class CygwinCCompiler(UnixCCompiler): + """Handles the Cygwin port of the GNU C compiler to Windows.""" + + compiler_type = 'cygwin' + obj_extension = ".o" + static_lib_extension = ".a" + shared_lib_extension = ".dll.a" + dylib_lib_extension = ".dll" + static_lib_format = "lib%s%s" + shared_lib_format = "lib%s%s" + dylib_lib_format = "cyg%s%s" + exe_extension = ".exe" + + def __init__(self, verbose=0, dry_run=0, force=0): + super().__init__(verbose, dry_run, force) + + status, details = check_config_h() + self.debug_print(f"Python's GCC status: {status} (details: {details})") + if status is not CONFIG_H_OK: + self.warn( + "Python's pyconfig.h doesn't seem to support your compiler. " + "Reason: %s. " + "Compiling may fail because of undefined preprocessor macros." % details + ) + + self.cc = os.environ.get('CC', 'gcc') + self.cxx = os.environ.get('CXX', 'g++') + + self.linker_dll = self.cc + shared_option = "-shared" + + self.set_executables( + compiler='%s -mcygwin -O -Wall' % self.cc, + compiler_so='%s -mcygwin -mdll -O -Wall' % self.cc, + compiler_cxx='%s -mcygwin -O -Wall' % self.cxx, + linker_exe='%s -mcygwin' % self.cc, + linker_so=(f'{self.linker_dll} -mcygwin {shared_option}'), + ) + + # Include the appropriate MSVC runtime library if Python was built + # with MSVC 7.0 or later. + self.dll_libraries = get_msvcr() + + @property + def gcc_version(self): + # Older numpy depended on this existing to check for ancient + # gcc versions. This doesn't make much sense with clang etc so + # just hardcode to something recent. + # https://github.com/numpy/numpy/pull/20333 + warnings.warn( + "gcc_version attribute of CygwinCCompiler is deprecated. " + "Instead of returning actual gcc version a fixed value 11.2.0 is returned.", + DeprecationWarning, + stacklevel=2, + ) + with suppress_known_deprecation(): + return LooseVersion("11.2.0") + + def _compile(self, obj, src, ext, cc_args, extra_postargs, pp_opts): + """Compiles the source by spawning GCC and windres if needed.""" + if ext in ('.rc', '.res'): + # gcc needs '.res' and '.rc' compiled to object files !!! + try: + self.spawn(["windres", "-i", src, "-o", obj]) + except DistutilsExecError as msg: + raise CompileError(msg) + else: # for other files use the C-compiler + try: + self.spawn( + self.compiler_so + cc_args + [src, '-o', obj] + extra_postargs + ) + except DistutilsExecError as msg: + raise CompileError(msg) + + def link( + self, + target_desc, + objects, + output_filename, + 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, + ): + """Link the objects.""" + # use separate copies, so we can modify the lists + extra_preargs = copy.copy(extra_preargs or []) + libraries = copy.copy(libraries or []) + objects = copy.copy(objects or []) + + if runtime_library_dirs: + self.warn(_runtime_library_dirs_msg) + + # Additional libraries + libraries.extend(self.dll_libraries) + + # handle export symbols by creating a def-file + # with executables this only works with gcc/ld as linker + if (export_symbols is not None) and ( + target_desc != self.EXECUTABLE or self.linker_dll == "gcc" + ): + # (The linker doesn't do anything if output is up-to-date. + # So it would probably better to check if we really need this, + # but for this we had to insert some unchanged parts of + # UnixCCompiler, and this is not what we want.) + + # we want to put some files in the same directory as the + # object files are, build_temp doesn't help much + # where are the object files + temp_dir = os.path.dirname(objects[0]) + # name of dll to give the helper files the same base name + (dll_name, dll_extension) = os.path.splitext( + os.path.basename(output_filename) + ) + + # generate the filenames for these files + def_file = os.path.join(temp_dir, dll_name + ".def") + + # Generate .def file + contents = ["LIBRARY %s" % os.path.basename(output_filename), "EXPORTS"] + for sym in export_symbols: + contents.append(sym) + self.execute(write_file, (def_file, contents), "writing %s" % def_file) + + # next add options for def-file + + # for gcc/ld the def-file is specified as any object files + objects.append(def_file) + + # end: if ((export_symbols is not None) and + # (target_desc != self.EXECUTABLE or self.linker_dll == "gcc")): + + # who wants symbols and a many times larger output file + # should explicitly switch the debug mode on + # otherwise we let ld strip the output file + # (On my machine: 10KiB < stripped_file < ??100KiB + # unstripped_file = stripped_file + XXX KiB + # ( XXX=254 for a typical python extension)) + if not debug: + extra_preargs.append("-s") + + UnixCCompiler.link( + self, + target_desc, + objects, + output_filename, + output_dir, + libraries, + library_dirs, + runtime_library_dirs, + None, # export_symbols, we do this in our def-file + debug, + extra_preargs, + extra_postargs, + build_temp, + target_lang, + ) + + def runtime_library_dir_option(self, dir): + # cygwin doesn't support rpath. While in theory we could error + # out like MSVC does, code might expect it to work like on Unix, so + # just warn and hope for the best. + self.warn(_runtime_library_dirs_msg) + return [] + + # -- Miscellaneous methods ----------------------------------------- + + def _make_out_path(self, output_dir, strip_dir, src_name): + # use normcase to make sure '.rc' is really '.rc' and not '.RC' + norm_src_name = os.path.normcase(src_name) + return super()._make_out_path(output_dir, strip_dir, norm_src_name) + + @property + def out_extensions(self): + """ + Add support for rc and res files. + """ + return { + **super().out_extensions, + **{ext: ext + self.obj_extension for ext in ('.res', '.rc')}, + } + + +# the same as cygwin plus some additional parameters +class Mingw32CCompiler(CygwinCCompiler): + """Handles the Mingw32 port of the GNU C compiler to Windows.""" + + compiler_type = 'mingw32' + + def __init__(self, verbose=0, dry_run=0, force=0): + super().__init__(verbose, dry_run, force) + + shared_option = "-shared" + + if is_cygwincc(self.cc): + raise CCompilerError('Cygwin gcc cannot be used with --compiler=mingw32') + + self.set_executables( + compiler='%s -O -Wall' % self.cc, + compiler_so='%s -mdll -O -Wall' % self.cc, + compiler_cxx='%s -O -Wall' % self.cxx, + linker_exe='%s' % self.cc, + linker_so=f'{self.linker_dll} {shared_option}', + ) + + def runtime_library_dir_option(self, dir): + raise DistutilsPlatformError(_runtime_library_dirs_msg) + + +# Because these compilers aren't configured in Python's pyconfig.h file by +# default, we should at least warn the user if he is using an unmodified +# version. + +CONFIG_H_OK = "ok" +CONFIG_H_NOTOK = "not ok" +CONFIG_H_UNCERTAIN = "uncertain" + + +def check_config_h(): + """Check if the current Python installation appears amenable to building + extensions with GCC. + + Returns a tuple (status, details), where 'status' is one of the following + constants: + + - CONFIG_H_OK: all is well, go ahead and compile + - CONFIG_H_NOTOK: doesn't look good + - CONFIG_H_UNCERTAIN: not sure -- unable to read pyconfig.h + + 'details' is a human-readable string explaining the situation. + + Note there are two ways to conclude "OK": either 'sys.version' contains + the string "GCC" (implying that this Python was built with GCC), or the + installed "pyconfig.h" contains the string "__GNUC__". + """ + + # XXX since this function also checks sys.version, it's not strictly a + # "pyconfig.h" check -- should probably be renamed... + + from distutils import sysconfig + + # if sys.version contains GCC then python was compiled with GCC, and the + # pyconfig.h file should be OK + if "GCC" in sys.version: + return CONFIG_H_OK, "sys.version mentions 'GCC'" + + # Clang would also work + if "Clang" in sys.version: + return CONFIG_H_OK, "sys.version mentions 'Clang'" + + # let's see if __GNUC__ is mentioned in python.h + fn = sysconfig.get_config_h_filename() + try: + config_h = pathlib.Path(fn).read_text(encoding='utf-8') + substring = '__GNUC__' + if substring in config_h: + code = CONFIG_H_OK + mention_inflected = 'mentions' + else: + code = CONFIG_H_NOTOK + mention_inflected = 'does not mention' + return code, f"{fn!r} {mention_inflected} {substring!r}" + except OSError as exc: + return (CONFIG_H_UNCERTAIN, f"couldn't read '{fn}': {exc.strerror}") + + +def is_cygwincc(cc): + """Try to determine if the compiler that would be used is from cygwin.""" + out_string = check_output(shlex.split(cc) + ['-dumpmachine']) + return out_string.strip().endswith(b'cygwin') + + +get_versions = None +""" +A stand-in for the previous get_versions() function to prevent failures +when monkeypatched. See pypa/setuptools#2969. +""" diff --git a/venv/Lib/site-packages/setuptools/_distutils/debug.py b/venv/Lib/site-packages/setuptools/_distutils/debug.py new file mode 100644 index 0000000..daf1660 --- /dev/null +++ b/venv/Lib/site-packages/setuptools/_distutils/debug.py @@ -0,0 +1,5 @@ +import os + +# If DISTUTILS_DEBUG is anything other than the empty string, we run in +# debug mode. +DEBUG = os.environ.get('DISTUTILS_DEBUG') diff --git a/venv/Lib/site-packages/setuptools/_distutils/dep_util.py b/venv/Lib/site-packages/setuptools/_distutils/dep_util.py new file mode 100644 index 0000000..09a8a2e --- /dev/null +++ b/venv/Lib/site-packages/setuptools/_distutils/dep_util.py @@ -0,0 +1,14 @@ +import warnings + +from . import _modified + + +def __getattr__(name): + if name not in ['newer', 'newer_group', 'newer_pairwise']: + raise AttributeError(name) + warnings.warn( + "dep_util is Deprecated. Use functions from setuptools instead.", + DeprecationWarning, + stacklevel=2, + ) + return getattr(_modified, name) diff --git a/venv/Lib/site-packages/setuptools/_distutils/dir_util.py b/venv/Lib/site-packages/setuptools/_distutils/dir_util.py new file mode 100644 index 0000000..370c6ff --- /dev/null +++ b/venv/Lib/site-packages/setuptools/_distutils/dir_util.py @@ -0,0 +1,238 @@ +"""distutils.dir_util + +Utility functions for manipulating directories and directory trees.""" + +import errno +import os + +from ._log import log +from .errors import DistutilsFileError, DistutilsInternalError + +# cache for by mkpath() -- in addition to cheapening redundant calls, +# eliminates redundant "creating /foo/bar/baz" messages in dry-run mode +_path_created = {} + + +def mkpath(name, mode=0o777, verbose=1, dry_run=0): # noqa: C901 + """Create a directory and any missing ancestor directories. + + If the directory already exists (or if 'name' is the empty string, which + means the current directory, which of course exists), then do nothing. + Raise DistutilsFileError if unable to create some directory along the way + (eg. some sub-path exists, but is a file rather than a directory). + If 'verbose' is true, print a one-line summary of each mkdir to stdout. + Return the list of directories actually created. + + os.makedirs is not used because: + + a) It's new to Python 1.5.2, and + b) it blows up if the directory already exists (in which case it should + silently succeed). + """ + + global _path_created + + # Detect a common bug -- name is None + if not isinstance(name, str): + raise DistutilsInternalError(f"mkpath: 'name' must be a string (got {name!r})") + + # XXX what's the better way to handle verbosity? print as we create + # each directory in the path (the current behaviour), or only announce + # the creation of the whole path? (quite easy to do the latter since + # we're not using a recursive algorithm) + + name = os.path.normpath(name) + created_dirs = [] + if os.path.isdir(name) or name == '': + return created_dirs + if _path_created.get(os.path.abspath(name)): + return created_dirs + + (head, tail) = os.path.split(name) + tails = [tail] # stack of lone dirs to create + + while head and tail and not os.path.isdir(head): + (head, tail) = os.path.split(head) + tails.insert(0, tail) # push next higher dir onto stack + + # now 'head' contains the deepest directory that already exists + # (that is, the child of 'head' in 'name' is the highest directory + # that does *not* exist) + for d in tails: + # print "head = %s, d = %s: " % (head, d), + head = os.path.join(head, d) + abs_head = os.path.abspath(head) + + if _path_created.get(abs_head): + continue + + if verbose >= 1: + log.info("creating %s", head) + + if not dry_run: + try: + os.mkdir(head, mode) + except OSError as exc: + if not (exc.errno == errno.EEXIST and os.path.isdir(head)): + raise DistutilsFileError( + f"could not create '{head}': {exc.args[-1]}" + ) + created_dirs.append(head) + + _path_created[abs_head] = 1 + return created_dirs + + +def create_tree(base_dir, files, mode=0o777, verbose=1, dry_run=0): + """Create all the empty directories under 'base_dir' needed to put 'files' + there. + + 'base_dir' is just the name of a directory which doesn't necessarily + exist yet; 'files' is a list of filenames to be interpreted relative to + 'base_dir'. 'base_dir' + the directory portion of every file in 'files' + will be created if it doesn't already exist. 'mode', 'verbose' and + 'dry_run' flags are as for 'mkpath()'. + """ + # First get the list of directories to create + need_dir = set(os.path.join(base_dir, os.path.dirname(file)) for file in files) + + # Now create them + for dir in sorted(need_dir): + mkpath(dir, mode, verbose=verbose, dry_run=dry_run) + + +def copy_tree( # noqa: C901 + src, + dst, + preserve_mode=1, + preserve_times=1, + preserve_symlinks=0, + update=0, + verbose=1, + dry_run=0, +): + """Copy an entire directory tree 'src' to a new location 'dst'. + + Both 'src' and 'dst' must be directory names. If 'src' is not a + directory, raise DistutilsFileError. If 'dst' does not exist, it is + created with 'mkpath()'. The end result of the copy is that every + file in 'src' is copied to 'dst', and directories under 'src' are + recursively copied to 'dst'. Return the list of files that were + copied or might have been copied, using their output name. The + return value is unaffected by 'update' or 'dry_run': it is simply + the list of all files under 'src', with the names changed to be + under 'dst'. + + 'preserve_mode' and 'preserve_times' are the same as for + 'copy_file'; note that they only apply to regular files, not to + directories. If 'preserve_symlinks' is true, symlinks will be + copied as symlinks (on platforms that support them!); otherwise + (the default), the destination of the symlink will be copied. + 'update' and 'verbose' are the same as for 'copy_file'. + """ + from distutils.file_util import copy_file + + if not dry_run and not os.path.isdir(src): + raise DistutilsFileError("cannot copy tree '%s': not a directory" % src) + try: + names = os.listdir(src) + except OSError as e: + if dry_run: + names = [] + else: + raise DistutilsFileError(f"error listing files in '{src}': {e.strerror}") + + if not dry_run: + mkpath(dst, verbose=verbose) + + outputs = [] + + for n in names: + src_name = os.path.join(src, n) + dst_name = os.path.join(dst, n) + + if n.startswith('.nfs'): + # skip NFS rename files + continue + + if preserve_symlinks and os.path.islink(src_name): + link_dest = os.readlink(src_name) + if verbose >= 1: + log.info("linking %s -> %s", dst_name, link_dest) + if not dry_run: + os.symlink(link_dest, dst_name) + outputs.append(dst_name) + + elif os.path.isdir(src_name): + outputs.extend( + copy_tree( + src_name, + dst_name, + preserve_mode, + preserve_times, + preserve_symlinks, + update, + verbose=verbose, + dry_run=dry_run, + ) + ) + else: + copy_file( + src_name, + dst_name, + preserve_mode, + preserve_times, + update, + verbose=verbose, + dry_run=dry_run, + ) + outputs.append(dst_name) + + return outputs + + +def _build_cmdtuple(path, cmdtuples): + """Helper for remove_tree().""" + for f in os.listdir(path): + real_f = os.path.join(path, f) + if os.path.isdir(real_f) and not os.path.islink(real_f): + _build_cmdtuple(real_f, cmdtuples) + else: + cmdtuples.append((os.remove, real_f)) + cmdtuples.append((os.rmdir, path)) + + +def remove_tree(directory, verbose=1, dry_run=0): + """Recursively remove an entire directory tree. + + Any errors are ignored (apart from being reported to stdout if 'verbose' + is true). + """ + global _path_created + + if verbose >= 1: + log.info("removing '%s' (and everything under it)", directory) + if dry_run: + return + cmdtuples = [] + _build_cmdtuple(directory, cmdtuples) + for cmd in cmdtuples: + try: + cmd[0](cmd[1]) + # remove dir from cache if it's already there + abspath = os.path.abspath(cmd[1]) + if abspath in _path_created: + _path_created.pop(abspath) + except OSError as exc: + log.warning("error removing %s: %s", directory, exc) + + +def ensure_relative(path): + """Take the full path 'path', and make it a relative path. + + This is useful to make 'path' the second argument to os.path.join(). + """ + drive, path = os.path.splitdrive(path) + if path[0:1] == os.sep: + path = drive + path[1:] + return path diff --git a/venv/Lib/site-packages/setuptools/_distutils/dist.py b/venv/Lib/site-packages/setuptools/_distutils/dist.py new file mode 100644 index 0000000..668ce7e --- /dev/null +++ b/venv/Lib/site-packages/setuptools/_distutils/dist.py @@ -0,0 +1,1287 @@ +"""distutils.dist + +Provides the Distribution class, which represents the module distribution +being built/installed/distributed. +""" + +import contextlib +import logging +import os +import pathlib +import re +import sys +from collections.abc import Iterable +from email import message_from_file + +try: + import warnings +except ImportError: + warnings = None + +from ._log import log +from .debug import DEBUG +from .errors import ( + DistutilsArgError, + DistutilsClassError, + DistutilsModuleError, + DistutilsOptionError, +) +from .fancy_getopt import FancyGetopt, translate_longopt +from .util import check_environ, rfc822_escape, strtobool + +# Regex to define acceptable Distutils command names. This is not *quite* +# the same as a Python NAME -- I don't allow leading underscores. The fact +# that they're very similar is no coincidence; the default naming scheme is +# to look for a Python module named after the command. +command_re = re.compile(r'^[a-zA-Z]([a-zA-Z0-9_]*)$') + + +def _ensure_list(value, fieldname): + if isinstance(value, str): + # a string containing comma separated values is okay. It will + # be converted to a list by Distribution.finalize_options(). + pass + elif not isinstance(value, list): + # passing a tuple or an iterator perhaps, warn and convert + typename = type(value).__name__ + msg = "Warning: '{fieldname}' should be a list, got type '{typename}'" + msg = msg.format(**locals()) + log.warning(msg) + value = list(value) + return value + + +class Distribution: + """The core of the Distutils. Most of the work hiding behind 'setup' + is really done within a Distribution instance, which farms the work out + to the Distutils commands specified on the command line. + + Setup scripts will almost never instantiate Distribution directly, + unless the 'setup()' function is totally inadequate to their needs. + However, it is conceivable that a setup script might wish to subclass + Distribution for some specialized purpose, and then pass the subclass + to 'setup()' as the 'distclass' keyword argument. If so, it is + necessary to respect the expectations that 'setup' has of Distribution. + See the code for 'setup()', in core.py, for details. + """ + + # 'global_options' describes the command-line options that may be + # supplied to the setup script prior to any actual commands. + # Eg. "./setup.py -n" or "./setup.py --quiet" both take advantage of + # these global options. This list should be kept to a bare minimum, + # since every global option is also valid as a command option -- and we + # don't want to pollute the commands with too many options that they + # have minimal control over. + # The fourth entry for verbose means that it can be repeated. + global_options = [ + ('verbose', 'v', "run verbosely (default)", 1), + ('quiet', 'q', "run quietly (turns verbosity off)"), + ('dry-run', 'n', "don't actually do anything"), + ('help', 'h', "show detailed help message"), + ('no-user-cfg', None, 'ignore pydistutils.cfg in your home directory'), + ] + + # 'common_usage' is a short (2-3 line) string describing the common + # usage of the setup script. + common_usage = """\ +Common commands: (see '--help-commands' for more) + + setup.py build will build the package underneath 'build/' + setup.py install will install the package +""" + + # options that are not propagated to the commands + display_options = [ + ('help-commands', None, "list all available commands"), + ('name', None, "print package name"), + ('version', 'V', "print package version"), + ('fullname', None, "print -"), + ('author', None, "print the author's name"), + ('author-email', None, "print the author's email address"), + ('maintainer', None, "print the maintainer's name"), + ('maintainer-email', None, "print the maintainer's email address"), + ('contact', None, "print the maintainer's name if known, else the author's"), + ( + 'contact-email', + None, + "print the maintainer's email address if known, else the author's", + ), + ('url', None, "print the URL for this package"), + ('license', None, "print the license of the package"), + ('licence', None, "alias for --license"), + ('description', None, "print the package description"), + ('long-description', None, "print the long package description"), + ('platforms', None, "print the list of platforms"), + ('classifiers', None, "print the list of classifiers"), + ('keywords', None, "print the list of keywords"), + ('provides', None, "print the list of packages/modules provided"), + ('requires', None, "print the list of packages/modules required"), + ('obsoletes', None, "print the list of packages/modules made obsolete"), + ] + display_option_names = [translate_longopt(x[0]) for x in display_options] + + # negative options are options that exclude other options + negative_opt = {'quiet': 'verbose'} + + # -- Creation/initialization methods ------------------------------- + + def __init__(self, attrs=None): # noqa: C901 + """Construct a new Distribution instance: initialize all the + attributes of a Distribution, and then use 'attrs' (a dictionary + mapping attribute names to values) to assign some of those + attributes their "real" values. (Any attributes not mentioned in + 'attrs' will be assigned to some null value: 0, None, an empty list + or dictionary, etc.) Most importantly, initialize the + 'command_obj' attribute to the empty dictionary; this will be + filled in with real command objects by 'parse_command_line()'. + """ + + # Default values for our command-line options + self.verbose = 1 + self.dry_run = 0 + self.help = 0 + for attr in self.display_option_names: + setattr(self, attr, 0) + + # Store the distribution meta-data (name, version, author, and so + # forth) in a separate object -- we're getting to have enough + # information here (and enough command-line options) that it's + # worth it. Also delegate 'get_XXX()' methods to the 'metadata' + # object in a sneaky and underhanded (but efficient!) way. + self.metadata = DistributionMetadata() + for basename in self.metadata._METHOD_BASENAMES: + method_name = "get_" + basename + setattr(self, method_name, getattr(self.metadata, method_name)) + + # 'cmdclass' maps command names to class objects, so we + # can 1) quickly figure out which class to instantiate when + # we need to create a new command object, and 2) have a way + # for the setup script to override command classes + self.cmdclass = {} + + # 'command_packages' is a list of packages in which commands + # are searched for. The factory for command 'foo' is expected + # to be named 'foo' in the module 'foo' in one of the packages + # named here. This list is searched from the left; an error + # is raised if no named package provides the command being + # searched for. (Always access using get_command_packages().) + self.command_packages = None + + # 'script_name' and 'script_args' are usually set to sys.argv[0] + # and sys.argv[1:], but they can be overridden when the caller is + # not necessarily a setup script run from the command-line. + self.script_name = None + self.script_args = None + + # 'command_options' is where we store command options between + # parsing them (from config files, the command-line, etc.) and when + # they are actually needed -- ie. when the command in question is + # instantiated. It is a dictionary of dictionaries of 2-tuples: + # command_options = { command_name : { option : (source, value) } } + self.command_options = {} + + # 'dist_files' is the list of (command, pyversion, file) that + # have been created by any dist commands run so far. This is + # filled regardless of whether the run is dry or not. pyversion + # gives sysconfig.get_python_version() if the dist file is + # specific to a Python version, 'any' if it is good for all + # Python versions on the target platform, and '' for a source + # file. pyversion should not be used to specify minimum or + # maximum required Python versions; use the metainfo for that + # instead. + self.dist_files = [] + + # These options are really the business of various commands, rather + # than of the Distribution itself. We provide aliases for them in + # Distribution as a convenience to the developer. + self.packages = None + self.package_data = {} + self.package_dir = None + self.py_modules = None + self.libraries = None + self.headers = None + self.ext_modules = None + self.ext_package = None + self.include_dirs = None + self.extra_path = None + self.scripts = None + self.data_files = None + self.password = '' + + # And now initialize bookkeeping stuff that can't be supplied by + # the caller at all. 'command_obj' maps command names to + # Command instances -- that's how we enforce that every command + # class is a singleton. + self.command_obj = {} + + # 'have_run' maps command names to boolean values; it keeps track + # of whether we have actually run a particular command, to make it + # cheap to "run" a command whenever we think we might need to -- if + # it's already been done, no need for expensive filesystem + # operations, we just check the 'have_run' dictionary and carry on. + # It's only safe to query 'have_run' for a command class that has + # been instantiated -- a false value will be inserted when the + # command object is created, and replaced with a true value when + # the command is successfully run. Thus it's probably best to use + # '.get()' rather than a straight lookup. + self.have_run = {} + + # Now we'll use the attrs dictionary (ultimately, keyword args from + # the setup script) to possibly override any or all of these + # distribution options. + + if attrs: + # Pull out the set of command options and work on them + # specifically. Note that this order guarantees that aliased + # command options will override any supplied redundantly + # through the general options dictionary. + options = attrs.get('options') + if options is not None: + del attrs['options'] + for command, cmd_options in options.items(): + opt_dict = self.get_option_dict(command) + for opt, val in cmd_options.items(): + opt_dict[opt] = ("setup script", val) + + if 'licence' in attrs: + attrs['license'] = attrs['licence'] + del attrs['licence'] + msg = "'licence' distribution option is deprecated; use 'license'" + if warnings is not None: + warnings.warn(msg) + else: + sys.stderr.write(msg + "\n") + + # Now work on the rest of the attributes. Any attribute that's + # not already defined is invalid! + for key, val in attrs.items(): + if hasattr(self.metadata, "set_" + key): + getattr(self.metadata, "set_" + key)(val) + elif hasattr(self.metadata, key): + setattr(self.metadata, key, val) + elif hasattr(self, key): + setattr(self, key, val) + else: + msg = "Unknown distribution option: %s" % repr(key) + warnings.warn(msg) + + # no-user-cfg is handled before other command line args + # because other args override the config files, and this + # one is needed before we can load the config files. + # If attrs['script_args'] wasn't passed, assume false. + # + # This also make sure we just look at the global options + self.want_user_cfg = True + + if self.script_args is not None: + for arg in self.script_args: + if not arg.startswith('-'): + break + if arg == '--no-user-cfg': + self.want_user_cfg = False + break + + self.finalize_options() + + def get_option_dict(self, command): + """Get the option dictionary for a given command. If that + command's option dictionary hasn't been created yet, then create it + and return the new dictionary; otherwise, return the existing + option dictionary. + """ + dict = self.command_options.get(command) + if dict is None: + dict = self.command_options[command] = {} + return dict + + def dump_option_dicts(self, header=None, commands=None, indent=""): + from pprint import pformat + + if commands is None: # dump all command option dicts + commands = sorted(self.command_options.keys()) + + if header is not None: + self.announce(indent + header) + indent = indent + " " + + if not commands: + self.announce(indent + "no commands known yet") + return + + for cmd_name in commands: + opt_dict = self.command_options.get(cmd_name) + if opt_dict is None: + self.announce(indent + "no option dict for '%s' command" % cmd_name) + else: + self.announce(indent + "option dict for '%s' command:" % cmd_name) + out = pformat(opt_dict) + for line in out.split('\n'): + self.announce(indent + " " + line) + + # -- Config file finding/parsing methods --------------------------- + + def find_config_files(self): + """Find as many configuration files as should be processed for this + platform, and return a list of filenames in the order in which they + should be parsed. The filenames returned are guaranteed to exist + (modulo nasty race conditions). + + There are multiple possible config files: + - distutils.cfg in the Distutils installation directory (i.e. + where the top-level Distutils __inst__.py file lives) + - a file in the user's home directory named .pydistutils.cfg + on Unix and pydistutils.cfg on Windows/Mac; may be disabled + with the ``--no-user-cfg`` option + - setup.cfg in the current directory + - a file named by an environment variable + """ + check_environ() + files = [str(path) for path in self._gen_paths() if os.path.isfile(path)] + + if DEBUG: + self.announce("using config files: %s" % ', '.join(files)) + + return files + + def _gen_paths(self): + # The system-wide Distutils config file + sys_dir = pathlib.Path(sys.modules['distutils'].__file__).parent + yield sys_dir / "distutils.cfg" + + # The per-user config file + prefix = '.' * (os.name == 'posix') + filename = prefix + 'pydistutils.cfg' + if self.want_user_cfg: + yield pathlib.Path('~').expanduser() / filename + + # All platforms support local setup.cfg + yield pathlib.Path('setup.cfg') + + # Additional config indicated in the environment + with contextlib.suppress(TypeError): + yield pathlib.Path(os.getenv("DIST_EXTRA_CONFIG")) + + def parse_config_files(self, filenames=None): # noqa: C901 + from configparser import ConfigParser + + # Ignore install directory options if we have a venv + if 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: + if DEBUG: + self.announce(" reading %s" % filename) + parser.read(filename, encoding='utf-8') + 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 = 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) + + # -- Command-line parsing methods ---------------------------------- + + def parse_command_line(self): + """Parse the setup script's command line, taken from the + 'script_args' instance attribute (which defaults to 'sys.argv[1:]' + -- see 'setup()' in core.py). This list is first processed for + "global options" -- options that set attributes of the Distribution + instance. Then, it is alternately scanned for Distutils commands + and options for that command. Each new command terminates the + options for the previous command. The allowed options for a + command are determined by the 'user_options' attribute of the + command class -- thus, we have to be able to load command classes + in order to parse the command line. Any error in that 'options' + attribute raises DistutilsGetoptError; any error on the + command-line raises DistutilsArgError. If no Distutils commands + were found on the command line, raises DistutilsArgError. Return + true if command-line was successfully parsed and we should carry + on with executing commands; false if no errors but we shouldn't + execute commands (currently, this only happens if user asks for + help). + """ + # + # We now have enough information to show the Macintosh dialog + # that allows the user to interactively specify the "command line". + # + toplevel_options = self._get_toplevel_options() + + # We have to parse the command line a bit at a time -- global + # options, then the first command, then its options, and so on -- + # because each command will be handled by a different class, and + # the options that are valid for a particular class aren't known + # until we have loaded the command class, which doesn't happen + # until we know what the command is. + + self.commands = [] + parser = FancyGetopt(toplevel_options + self.display_options) + parser.set_negative_aliases(self.negative_opt) + parser.set_aliases({'licence': 'license'}) + args = parser.getopt(args=self.script_args, object=self) + option_order = parser.get_option_order() + logging.getLogger().setLevel(logging.WARN - 10 * self.verbose) + + # for display options we return immediately + if self.handle_display_options(option_order): + return + while args: + args = self._parse_command_opts(parser, args) + if args is None: # user asked for help (and got it) + return + + # Handle the cases of --help as a "global" option, ie. + # "setup.py --help" and "setup.py --help command ...". For the + # former, we show global options (--verbose, --dry-run, etc.) + # and display-only options (--name, --version, etc.); for the + # latter, we omit the display-only options and show help for + # each command listed on the command line. + if self.help: + self._show_help( + parser, display_options=len(self.commands) == 0, commands=self.commands + ) + return + + # Oops, no commands found -- an end-user error + if not self.commands: + raise DistutilsArgError("no commands supplied") + + # All is well: return true + return True + + def _get_toplevel_options(self): + """Return the non-display options recognized at the top level. + + This includes options that are recognized *only* at the top + level as well as options recognized for commands. + """ + return self.global_options + [ + ( + "command-packages=", + None, + "list of packages that provide distutils commands", + ), + ] + + def _parse_command_opts(self, parser, args): # noqa: C901 + """Parse the command-line options for a single command. + 'parser' must be a FancyGetopt instance; 'args' must be the list + of arguments, starting with the current command (whose options + we are about to parse). Returns a new version of 'args' with + the next command at the front of the list; will be the empty + list if there are no more commands on the command line. Returns + None if the user asked for help on this command. + """ + # late import because of mutual dependence between these modules + from distutils.cmd import Command + + # Pull the current command from the head of the command line + command = args[0] + if not command_re.match(command): + raise SystemExit("invalid command name '%s'" % command) + self.commands.append(command) + + # Dig up the command class that implements this command, so we + # 1) know that it's a valid command, and 2) know which options + # it takes. + try: + cmd_class = self.get_command_class(command) + except DistutilsModuleError as msg: + raise DistutilsArgError(msg) + + # Require that the command class be derived from Command -- want + # to be sure that the basic "command" interface is implemented. + if not issubclass(cmd_class, Command): + raise DistutilsClassError( + "command class %s must subclass Command" % cmd_class + ) + + # Also make sure that the command object provides a list of its + # known options. + if not ( + hasattr(cmd_class, 'user_options') + and isinstance(cmd_class.user_options, list) + ): + msg = ( + "command class %s must provide " + "'user_options' attribute (a list of tuples)" + ) + raise DistutilsClassError(msg % cmd_class) + + # If the command class has a list of negative alias options, + # merge it in with the global negative aliases. + negative_opt = self.negative_opt + if hasattr(cmd_class, 'negative_opt'): + negative_opt = negative_opt.copy() + negative_opt.update(cmd_class.negative_opt) + + # Check for help_options in command class. They have a different + # format (tuple of four) so we need to preprocess them here. + if hasattr(cmd_class, 'help_options') and isinstance( + cmd_class.help_options, list + ): + help_options = fix_help_options(cmd_class.help_options) + else: + help_options = [] + + # All commands support the global options too, just by adding + # in 'global_options'. + parser.set_option_table( + self.global_options + cmd_class.user_options + help_options + ) + parser.set_negative_aliases(negative_opt) + (args, opts) = parser.getopt(args[1:]) + if hasattr(opts, 'help') and opts.help: + self._show_help(parser, display_options=0, commands=[cmd_class]) + return + + if hasattr(cmd_class, 'help_options') and isinstance( + cmd_class.help_options, list + ): + help_option_found = 0 + for help_option, _short, _desc, func in cmd_class.help_options: + if hasattr(opts, parser.get_attr_name(help_option)): + help_option_found = 1 + if callable(func): + func() + else: + raise DistutilsClassError( + f"invalid help function {func!r} for help option '{help_option}': " + "must be a callable object (function, etc.)" + ) + + if help_option_found: + return + + # Put the options from the command-line into their official + # holding pen, the 'command_options' dictionary. + opt_dict = self.get_option_dict(command) + for name, value in vars(opts).items(): + opt_dict[name] = ("command line", value) + + return args + + def finalize_options(self): + """Set final values for all the options on the Distribution + instance, analogous to the .finalize_options() method of Command + objects. + """ + for attr in ('keywords', 'platforms'): + value = getattr(self.metadata, attr) + if value is None: + continue + if isinstance(value, str): + value = [elm.strip() for elm in value.split(',')] + setattr(self.metadata, attr, value) + + def _show_help( + self, parser, global_options=1, display_options=1, commands: Iterable = () + ): + """Show help for the setup script command-line in the form of + several lists of command-line options. 'parser' should be a + FancyGetopt instance; do not expect it to be returned in the + same state, as its option table will be reset to make it + generate the correct help text. + + If 'global_options' is true, lists the global options: + --verbose, --dry-run, etc. If 'display_options' is true, lists + the "display-only" options: --name, --version, etc. Finally, + lists per-command help for every command name or command class + in 'commands'. + """ + # late import because of mutual dependence between these modules + from distutils.cmd import Command + from distutils.core import gen_usage + + if global_options: + if display_options: + options = self._get_toplevel_options() + else: + options = self.global_options + parser.set_option_table(options) + parser.print_help(self.common_usage + "\nGlobal options:") + print() + + if display_options: + parser.set_option_table(self.display_options) + parser.print_help( + "Information display options (just display " + + "information, ignore any commands)" + ) + print() + + for command in self.commands: + if isinstance(command, type) and issubclass(command, Command): + klass = command + else: + klass = self.get_command_class(command) + if hasattr(klass, 'help_options') and isinstance(klass.help_options, list): + parser.set_option_table( + klass.user_options + fix_help_options(klass.help_options) + ) + else: + parser.set_option_table(klass.user_options) + parser.print_help("Options for '%s' command:" % klass.__name__) + print() + + print(gen_usage(self.script_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. + """ + from distutils.core import gen_usage + + # User just wants a list of commands -- we'll print it out and stop + # processing now (ie. if they ran "setup --help-commands foo bar", + # we ignore "foo bar"). + if self.help_commands: + self.print_commands() + print() + print(gen_usage(self.script_name)) + return 1 + + # If user supplied any of the "display metadata" options, then + # display that metadata in the order in which the user supplied the + # metadata options. + any_display_options = 0 + is_display_option = {} + for option in self.display_options: + is_display_option[option[0]] = 1 + + for opt, val in option_order: + if val and is_display_option.get(opt): + opt = translate_longopt(opt) + value = getattr(self.metadata, "get_" + opt)() + if opt in ('keywords', 'platforms'): + print(','.join(value)) + elif opt in ('classifiers', 'provides', 'requires', 'obsoletes'): + print('\n'.join(value)) + else: + print(value) + any_display_options = 1 + + return any_display_options + + def print_command_list(self, commands, header, max_length): + """Print a subset of the list of all commands -- used by + 'print_commands()'. + """ + print(header + ":") + + for cmd in commands: + klass = self.cmdclass.get(cmd) + if not klass: + klass = self.get_command_class(cmd) + try: + description = klass.description + except AttributeError: + description = "(no description available)" + + print(" %-*s %s" % (max_length, cmd, description)) + + def print_commands(self): + """Print out a help message listing all available commands with a + description of each. The list is divided into "standard commands" + (listed in distutils.command.__all__) and "extra commands" + (mentioned in self.cmdclass, but not a standard command). The + descriptions come from the command class attribute + 'description'. + """ + import distutils.command + + std_commands = distutils.command.__all__ + is_std = {} + for cmd in std_commands: + is_std[cmd] = 1 + + extra_commands = [] + for cmd in self.cmdclass.keys(): + if not is_std.get(cmd): + extra_commands.append(cmd) + + max_length = 0 + for cmd in std_commands + extra_commands: + if len(cmd) > max_length: + max_length = len(cmd) + + self.print_command_list(std_commands, "Standard commands", max_length) + if extra_commands: + print() + self.print_command_list(extra_commands, "Extra commands", max_length) + + def get_command_list(self): + """Get a list of (command, description) tuples. + The list is divided into "standard commands" (listed in + distutils.command.__all__) and "extra commands" (mentioned in + self.cmdclass, but not a standard command). The descriptions come + from the command class attribute 'description'. + """ + # Currently this is only used on Mac OS, for the Mac-only GUI + # Distutils interface (by Jack Jansen) + import distutils.command + + std_commands = distutils.command.__all__ + is_std = {} + for cmd in std_commands: + is_std[cmd] = 1 + + extra_commands = [] + for cmd in self.cmdclass.keys(): + if not is_std.get(cmd): + extra_commands.append(cmd) + + rv = [] + for cmd in std_commands + extra_commands: + klass = self.cmdclass.get(cmd) + if not klass: + klass = self.get_command_class(cmd) + try: + description = klass.description + except AttributeError: + description = "(no description available)" + rv.append((cmd, description)) + return rv + + # -- Command class/object methods ---------------------------------- + + def get_command_packages(self): + """Return a list of packages from which commands are loaded.""" + pkgs = self.command_packages + if not isinstance(pkgs, list): + if pkgs is None: + pkgs = '' + pkgs = [pkg.strip() for pkg in pkgs.split(',') if pkg != ''] + if "distutils.command" not in pkgs: + pkgs.insert(0, "distutils.command") + self.command_packages = pkgs + return pkgs + + def get_command_class(self, command): + """Return the class that implements the Distutils command named by + 'command'. First we check the 'cmdclass' dictionary; if the + command is mentioned there, we fetch the class object from the + dictionary and return it. Otherwise we load the command module + ("distutils.command." + command) and fetch the command class from + the module. The loaded class is also stored in 'cmdclass' + to speed future calls to 'get_command_class()'. + + Raises DistutilsModuleError if the expected module could not be + found, or if that module does not define the expected class. + """ + klass = self.cmdclass.get(command) + if klass: + return klass + + for pkgname in self.get_command_packages(): + module_name = f"{pkgname}.{command}" + klass_name = command + + try: + __import__(module_name) + module = sys.modules[module_name] + except ImportError: + continue + + try: + klass = getattr(module, klass_name) + except AttributeError: + raise DistutilsModuleError( + f"invalid command '{command}' (no class '{klass_name}' in module '{module_name}')" + ) + + self.cmdclass[command] = klass + return klass + + raise DistutilsModuleError("invalid command '%s'" % command) + + def get_command_obj(self, command, create=1): + """Return the command object for 'command'. Normally this object + is cached on a previous call to 'get_command_obj()'; if no command + object for 'command' is in the cache, then we either create and + return it (if 'create' is true) or return None. + """ + cmd_obj = self.command_obj.get(command) + if not cmd_obj and create: + if DEBUG: + self.announce( + "Distribution.get_command_obj(): " + "creating '%s' command object" % command + ) + + klass = self.get_command_class(command) + cmd_obj = self.command_obj[command] = klass(self) + self.have_run[command] = 0 + + # Set any options that were supplied in config files + # or on the command line. (NB. support for error + # reporting is lame here: any errors aren't reported + # until 'finalize_options()' is called, which means + # we won't report the source of the error.) + options = self.command_options.get(command) + if options: + self._set_command_options(cmd_obj, options) + + return cmd_obj + + def _set_command_options(self, command_obj, option_dict=None): # noqa: C901 + """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'). + """ + 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(f" {option} = {value} (from {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, str) + 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( + f"error in {source}: command '{command_name}' has no such option '{option}'" + ) + except ValueError as msg: + raise DistutilsOptionError(msg) + + def reinitialize_command(self, command, reinit_subcommands=0): + """Reinitializes a command to the state it was in when first + returned by 'get_command_obj()': ie., initialized but not yet + finalized. This provides the opportunity to sneak option + values in programmatically, overriding or supplementing + user-supplied values from the config files and command line. + You'll have to re-finalize the command object (by calling + 'finalize_options()' or 'ensure_finalized()') before using it for + real. + + 'command' should be a command name (string) or command object. If + 'reinit_subcommands' is true, also reinitializes the command's + sub-commands, as declared by the 'sub_commands' class attribute (if + it has one). See the "install" command for an example. Only + reinitializes the sub-commands that actually matter, ie. those + whose test predicates return true. + + Returns the reinitialized command object. + """ + from distutils.cmd import Command + + if not isinstance(command, Command): + command_name = command + command = self.get_command_obj(command_name) + else: + command_name = command.get_command_name() + + if not command.finalized: + return command + command.initialize_options() + command.finalized = 0 + self.have_run[command_name] = 0 + self._set_command_options(command) + + if reinit_subcommands: + for sub in command.get_sub_commands(): + self.reinitialize_command(sub, reinit_subcommands) + + return command + + # -- Methods that operate on the Distribution ---------------------- + + def announce(self, msg, level=logging.INFO): + log.log(level, msg) + + def run_commands(self): + """Run each command that was seen on the setup script command line. + Uses the list of commands found and cache of command objects + created by 'get_command_obj()'. + """ + for cmd in self.commands: + self.run_command(cmd) + + # -- Methods that operate on its Commands -------------------------- + + def run_command(self, command): + """Do whatever it takes to run a command (including nothing at all, + if the command has already been run). Specifically: if we have + already created and run the command named by 'command', return + silently without doing anything. If the command named by 'command' + doesn't even have a command object yet, create one. Then invoke + 'run()' on that command object (or an existing one). + """ + # Already been here, done that? then return silently. + if self.have_run.get(command): + return + + log.info("running %s", command) + cmd_obj = self.get_command_obj(command) + cmd_obj.ensure_finalized() + cmd_obj.run() + self.have_run[command] = 1 + + # -- Distribution query methods ------------------------------------ + + def has_pure_modules(self): + return len(self.packages or self.py_modules or []) > 0 + + def has_ext_modules(self): + return self.ext_modules and len(self.ext_modules) > 0 + + def has_c_libraries(self): + return self.libraries and len(self.libraries) > 0 + + def has_modules(self): + return self.has_pure_modules() or self.has_ext_modules() + + def has_headers(self): + return self.headers and len(self.headers) > 0 + + def has_scripts(self): + return self.scripts and len(self.scripts) > 0 + + def has_data_files(self): + return self.data_files and len(self.data_files) > 0 + + def is_pure(self): + return ( + self.has_pure_modules() + and not self.has_ext_modules() + and not self.has_c_libraries() + ) + + # -- Metadata query methods ---------------------------------------- + + # If you're looking for 'get_name()', 'get_version()', and so forth, + # they are defined in a sneaky way: the constructor binds self.get_XXX + # to self.metadata.get_XXX. The actual code is in the + # DistributionMetadata class, below. + + +class DistributionMetadata: + """Dummy class to hold the distribution meta-data: name, version, + author, and so forth. + """ + + _METHOD_BASENAMES = ( + "name", + "version", + "author", + "author_email", + "maintainer", + "maintainer_email", + "url", + "license", + "description", + "long_description", + "keywords", + "platforms", + "fullname", + "contact", + "contact_email", + "classifiers", + "download_url", + # PEP 314 + "provides", + "requires", + "obsoletes", + ) + + def __init__(self, path=None): + if path is not None: + self.read_pkg_file(open(path)) + else: + self.name = None + self.version = None + self.author = None + self.author_email = None + self.maintainer = None + self.maintainer_email = None + self.url = None + self.license = None + self.description = None + self.long_description = None + self.keywords = None + self.platforms = None + self.classifiers = None + self.download_url = None + # PEP 314 + self.provides = None + self.requires = None + self.obsoletes = None + + 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 and value != "UNKNOWN": + return value + + def _read_list(name): + values = msg.get_all(name, None) + if values == []: + return None + return values + + metadata_version = 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 metadata_version == '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 + + def write_pkg_info(self, base_dir): + """Write the PKG-INFO file into the release tree.""" + with open( + os.path.join(base_dir, 'PKG-INFO'), 'w', encoding='UTF-8' + ) as pkg_info: + self.write_pkg_file(pkg_info) + + def write_pkg_file(self, file): + """Write the PKG-INFO format data to a file object.""" + version = '1.0' + if ( + self.provides + or self.requires + or self.obsoletes + or self.classifiers + or self.download_url + ): + version = '1.1' + + # required fields + file.write('Metadata-Version: %s\n' % version) + file.write('Name: %s\n' % self.get_name()) + file.write('Version: %s\n' % self.get_version()) + + def maybe_write(header, val): + if val: + file.write(f"{header}: {val}\n") + + # optional fields + maybe_write("Summary", self.get_description()) + maybe_write("Home-page", self.get_url()) + maybe_write("Author", self.get_contact()) + maybe_write("Author-email", self.get_contact_email()) + maybe_write("License", self.get_license()) + maybe_write("Download-URL", self.download_url) + maybe_write("Description", rfc822_escape(self.get_long_description() or "")) + maybe_write("Keywords", ",".join(self.get_keywords())) + + 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()) + + def _write_list(self, file, name, values): + values = values or [] + for value in values: + file.write(f'{name}: {value}\n') + + # -- Metadata query methods ---------------------------------------- + + def get_name(self): + return self.name or "UNKNOWN" + + def get_version(self): + return self.version or "0.0.0" + + def get_fullname(self): + return f"{self.get_name()}-{self.get_version()}" + + def get_author(self): + return self.author + + def get_author_email(self): + return self.author_email + + def get_maintainer(self): + return self.maintainer + + def get_maintainer_email(self): + return self.maintainer_email + + def get_contact(self): + return self.maintainer or self.author + + def get_contact_email(self): + return self.maintainer_email or self.author_email + + def get_url(self): + return self.url + + def get_license(self): + return self.license + + get_licence = get_license + + def get_description(self): + return self.description + + def get_long_description(self): + return self.long_description + + def get_keywords(self): + return self.keywords or [] + + def set_keywords(self, value): + self.keywords = _ensure_list(value, 'keywords') + + def get_platforms(self): + return self.platforms + + def set_platforms(self, value): + self.platforms = _ensure_list(value, 'platforms') + + def get_classifiers(self): + return self.classifiers or [] + + def set_classifiers(self, value): + self.classifiers = _ensure_list(value, 'classifiers') + + def get_download_url(self): + return self.download_url + + # PEP 314 + def get_requires(self): + return self.requires or [] + + def set_requires(self, value): + import distutils.versionpredicate + + for v in value: + distutils.versionpredicate.VersionPredicate(v) + self.requires = list(value) + + def get_provides(self): + return self.provides or [] + + def set_provides(self, value): + value = [v.strip() for v in value] + for v in value: + import distutils.versionpredicate + + distutils.versionpredicate.split_provision(v) + self.provides = value + + def get_obsoletes(self): + return self.obsoletes or [] + + def set_obsoletes(self, value): + import distutils.versionpredicate + + for v in value: + distutils.versionpredicate.VersionPredicate(v) + self.obsoletes = list(value) + + +def fix_help_options(options): + """Convert a 4-tuple 'help_options' list as found in various command + classes to the 3-tuple form required by FancyGetopt. + """ + new_options = [] + for help_tuple in options: + new_options.append(help_tuple[0:3]) + return new_options diff --git a/venv/Lib/site-packages/setuptools/_distutils/errors.py b/venv/Lib/site-packages/setuptools/_distutils/errors.py new file mode 100644 index 0000000..626254c --- /dev/null +++ b/venv/Lib/site-packages/setuptools/_distutils/errors.py @@ -0,0 +1,127 @@ +"""distutils.errors + +Provides exceptions used by the Distutils modules. Note that Distutils +modules may raise standard exceptions; in particular, SystemExit is +usually raised for errors that are obviously the end-user's fault +(eg. bad command-line arguments). + +This module is safe to use in "from ... import *" mode; it only exports +symbols whose names start with "Distutils" and end with "Error".""" + + +class DistutilsError(Exception): + """The root of all Distutils evil.""" + + pass + + +class DistutilsModuleError(DistutilsError): + """Unable to load an expected module, or to find an expected class + within some module (in particular, command modules and classes).""" + + pass + + +class DistutilsClassError(DistutilsError): + """Some command class (or possibly distribution class, if anyone + feels a need to subclass Distribution) is found not to be holding + up its end of the bargain, ie. implementing some part of the + "command "interface.""" + + pass + + +class DistutilsGetoptError(DistutilsError): + """The option table provided to 'fancy_getopt()' is bogus.""" + + pass + + +class DistutilsArgError(DistutilsError): + """Raised by fancy_getopt in response to getopt.error -- ie. an + error in the command line usage.""" + + pass + + +class DistutilsFileError(DistutilsError): + """Any problems in the filesystem: expected file not found, etc. + Typically this is for problems that we detect before OSError + could be raised.""" + + pass + + +class DistutilsOptionError(DistutilsError): + """Syntactic/semantic errors in command options, such as use of + mutually conflicting options, or inconsistent options, + badly-spelled values, etc. No distinction is made between option + values originating in the setup script, the command line, config + files, or what-have-you -- but if we *know* something originated in + the setup script, we'll raise DistutilsSetupError instead.""" + + pass + + +class DistutilsSetupError(DistutilsError): + """For errors that can be definitely blamed on the setup script, + such as invalid keyword arguments to 'setup()'.""" + + pass + + +class DistutilsPlatformError(DistutilsError): + """We don't know how to do something on the current platform (but + we do know how to do it on some platform) -- eg. trying to compile + C files on a platform not supported by a CCompiler subclass.""" + + pass + + +class DistutilsExecError(DistutilsError): + """Any problems executing an external program (such as the C + compiler, when compiling C files).""" + + pass + + +class DistutilsInternalError(DistutilsError): + """Internal inconsistencies or impossibilities (obviously, this + should never be seen if the code is working!).""" + + pass + + +class DistutilsTemplateError(DistutilsError): + """Syntax error in a file list template.""" + + +class DistutilsByteCompileError(DistutilsError): + """Byte compile error.""" + + +# Exception classes used by the CCompiler implementation classes +class CCompilerError(Exception): + """Some compile/link operation failed.""" + + +class PreprocessError(CCompilerError): + """Failure to preprocess one or more C/C++ files.""" + + +class CompileError(CCompilerError): + """Failure to compile one or more C/C++ source files.""" + + +class LibError(CCompilerError): + """Failure to create a static library from one or more C/C++ object + files.""" + + +class LinkError(CCompilerError): + """Failure to link one or more C/C++ object files into an executable + or shared library file.""" + + +class UnknownFileError(CCompilerError): + """Attempt to process an unknown file type.""" diff --git a/venv/Lib/site-packages/setuptools/_distutils/extension.py b/venv/Lib/site-packages/setuptools/_distutils/extension.py new file mode 100644 index 0000000..94e7163 --- /dev/null +++ b/venv/Lib/site-packages/setuptools/_distutils/extension.py @@ -0,0 +1,242 @@ +"""distutils.extension + +Provides the Extension class, used to describe C/C++ extension +modules in setup scripts.""" + +import os +import warnings + +# This class is really only used by the "build_ext" command, so it might +# make sense to put it in distutils.command.build_ext. However, that +# module is already big enough, and I want to make this class a bit more +# complex to simplify some common cases ("foo" module in "foo.c") and do +# better error-checking ("foo.c" actually exists). +# +# Also, putting this in build_ext.py means every setup script would have to +# import that large-ish module (indirectly, through distutils.core) in +# order to do anything. + + +class Extension: + """Just a collection of attributes that describes an extension + module and everything needed to build it (hopefully in a portable + way, but there are hooks that let you be as unportable as you need). + + Instance attributes: + name : string + the full name of the extension, including any packages -- ie. + *not* a filename or pathname, but Python dotted name + sources : [string] + list of source filenames, relative to the distribution root + (where the setup script lives), in Unix form (slash-separated) + for portability. Source files may be C, C++, SWIG (.i), + platform-specific resource files, or whatever else is recognized + by the "build_ext" command as source for a Python extension. + include_dirs : [string] + list of directories to search for C/C++ header files (in Unix + form for portability) + define_macros : [(name : string, value : string|None)] + list of macros to define; each macro is defined using a 2-tuple, + where 'value' is either the string to define it to or None to + define it without a particular value (equivalent of "#define + FOO" in source or -DFOO on Unix C compiler command line) + undef_macros : [string] + list of macros to undefine explicitly + library_dirs : [string] + list of directories to search for C/C++ libraries at link time + libraries : [string] + list of library names (not filenames or paths) to link against + runtime_library_dirs : [string] + list of directories to search for C/C++ libraries at run time + (for shared extensions, this is when the extension is loaded) + extra_objects : [string] + list of extra files to link with (eg. object files not implied + by 'sources', static library that must be explicitly specified, + binary resource files, etc.) + extra_compile_args : [string] + any extra platform- and compiler-specific information to use + when compiling the source files in 'sources'. For platforms and + compilers where "command line" makes sense, this is typically a + list of command-line arguments, but for other platforms it could + be anything. + extra_link_args : [string] + any extra platform- and compiler-specific information to use + when linking object files together to create the extension (or + to create a new static Python interpreter). Similar + interpretation as for 'extra_compile_args'. + export_symbols : [string] + list of symbols to be exported from a shared extension. Not + used on all platforms, and not generally necessary for Python + extensions, which typically export exactly one symbol: "init" + + extension_name. + swig_opts : [string] + any extra options to pass to SWIG if a source file has the .i + extension. + depends : [string] + list of files that the extension depends on + language : string + extension language (i.e. "c", "c++", "objc"). Will be detected + from the source extensions if not provided. + optional : boolean + specifies that a build failure in the extension should not abort the + build process, but simply not install the failing extension. + """ + + # When adding arguments to this constructor, be sure to update + # setup_keywords in core.py. + def __init__( + self, + name, + sources, + include_dirs=None, + define_macros=None, + undef_macros=None, + library_dirs=None, + libraries=None, + runtime_library_dirs=None, + extra_objects=None, + extra_compile_args=None, + extra_link_args=None, + export_symbols=None, + swig_opts=None, + depends=None, + language=None, + optional=None, + **kw, # To catch unknown keywords + ): + if not isinstance(name, str): + raise AssertionError("'name' must be a string") + if not (isinstance(sources, list) and all(isinstance(v, str) for v in sources)): + raise AssertionError("'sources' must be a list of strings") + + self.name = name + self.sources = sources + self.include_dirs = include_dirs or [] + self.define_macros = define_macros or [] + self.undef_macros = undef_macros or [] + self.library_dirs = library_dirs or [] + self.libraries = libraries or [] + self.runtime_library_dirs = runtime_library_dirs or [] + self.extra_objects = extra_objects or [] + self.extra_compile_args = extra_compile_args or [] + self.extra_link_args = extra_link_args or [] + self.export_symbols = export_symbols or [] + self.swig_opts = swig_opts or [] + self.depends = depends or [] + self.language = language + self.optional = optional + + # If there are unknown keyword options, warn about them + if len(kw) > 0: + options = [repr(option) for option in kw] + options = ', '.join(sorted(options)) + msg = "Unknown Extension options: %s" % options + warnings.warn(msg) + + def __repr__(self): + return f'<{self.__class__.__module__}.{self.__class__.__qualname__}({self.name!r}) at {id(self):#x}>' + + +def read_setup_file(filename): # noqa: C901 + """Reads a Setup file and returns Extension instances.""" + from distutils.sysconfig import _variable_rx, expand_makefile_vars, parse_makefile + from distutils.text_file import TextFile + from distutils.util import split_quoted + + # First pass over the file to gather "VAR = VALUE" assignments. + vars = parse_makefile(filename) + + # Second pass to gobble up the real content: lines of the form + # ... [ ...] [ ...] [ ...] + file = TextFile( + filename, + strip_comments=1, + skip_blanks=1, + join_lines=1, + lstrip_ws=1, + rstrip_ws=1, + ) + try: + extensions = [] + + while True: + line = file.readline() + if line is None: # eof + break + if _variable_rx.match(line): # VAR=VALUE, handled in first pass + continue + + if line[0] == line[-1] == "*": + file.warn("'%s' lines not handled yet" % line) + continue + + line = expand_makefile_vars(line, vars) + words = split_quoted(line) + + # NB. this parses a slightly different syntax than the old + # makesetup script: here, there must be exactly one extension per + # line, and it must be the first word of the line. I have no idea + # why the old syntax supported multiple extensions per line, as + # they all wind up being the same. + + module = words[0] + ext = Extension(module, []) + append_next_word = None + + for word in words[1:]: + if append_next_word is not None: + append_next_word.append(word) + append_next_word = None + continue + + suffix = os.path.splitext(word)[1] + switch = word[0:2] + value = word[2:] + + if suffix in (".c", ".cc", ".cpp", ".cxx", ".c++", ".m", ".mm"): + # hmm, should we do something about C vs. C++ sources? + # or leave it up to the CCompiler implementation to + # worry about? + ext.sources.append(word) + elif switch == "-I": + ext.include_dirs.append(value) + elif switch == "-D": + equals = value.find("=") + if equals == -1: # bare "-DFOO" -- no value + ext.define_macros.append((value, None)) + else: # "-DFOO=blah" + ext.define_macros.append((value[0:equals], value[equals + 2 :])) + elif switch == "-U": + ext.undef_macros.append(value) + elif switch == "-C": # only here 'cause makesetup has it! + ext.extra_compile_args.append(word) + elif switch == "-l": + ext.libraries.append(value) + elif switch == "-L": + ext.library_dirs.append(value) + elif switch == "-R": + ext.runtime_library_dirs.append(value) + elif word == "-rpath": + append_next_word = ext.runtime_library_dirs + elif word == "-Xlinker": + append_next_word = ext.extra_link_args + elif word == "-Xcompiler": + append_next_word = ext.extra_compile_args + elif switch == "-u": + ext.extra_link_args.append(word) + if not value: + append_next_word = ext.extra_link_args + elif suffix in (".a", ".so", ".sl", ".o", ".dylib"): + # NB. a really faithful emulation of makesetup would + # append a .o file to extra_objects only if it + # had a slash in it; otherwise, it would s/.o/.c/ + # and append it to sources. Hmmmm. + ext.extra_objects.append(word) + else: + file.warn("unrecognized argument '%s'" % word) + + extensions.append(ext) + finally: + file.close() + + return extensions diff --git a/venv/Lib/site-packages/setuptools/_distutils/fancy_getopt.py b/venv/Lib/site-packages/setuptools/_distutils/fancy_getopt.py new file mode 100644 index 0000000..e905aed --- /dev/null +++ b/venv/Lib/site-packages/setuptools/_distutils/fancy_getopt.py @@ -0,0 +1,469 @@ +"""distutils.fancy_getopt + +Wrapper around the standard getopt module that provides the following +additional features: + * short and long options are tied together + * options have help strings, so fancy_getopt could potentially + create a complete usage summary + * options set attributes of a passed-in object +""" + +import getopt +import re +import string +import sys +from typing import Any, Sequence + +from .errors import DistutilsArgError, DistutilsGetoptError + +# Much like command_re in distutils.core, this is close to but not quite +# the same as a Python NAME -- except, in the spirit of most GNU +# utilities, we use '-' in place of '_'. (The spirit of LISP lives on!) +# The similarities to NAME are again not a coincidence... +longopt_pat = r'[a-zA-Z](?:[a-zA-Z0-9-]*)' +longopt_re = re.compile(r'^%s$' % longopt_pat) + +# For recognizing "negative alias" options, eg. "quiet=!verbose" +neg_alias_re = re.compile(f"^({longopt_pat})=!({longopt_pat})$") + +# This is used to translate long options to legitimate Python identifiers +# (for use as attributes of some object). +longopt_xlate = str.maketrans('-', '_') + + +class FancyGetopt: + """Wrapper around the standard 'getopt()' module that provides some + handy extra functionality: + * short and long options are tied together + * options have help strings, and help text can be assembled + from them + * options set attributes of a passed-in object + * boolean options can have "negative aliases" -- eg. if + --quiet is the "negative alias" of --verbose, then "--quiet" + on the command line sets 'verbose' to false + """ + + def __init__(self, option_table=None): + # The option table is (currently) a list of tuples. The + # tuples may have 3 or four values: + # (long_option, short_option, help_string [, repeatable]) + # if an option takes an argument, its long_option should have '=' + # appended; short_option should just be a single character, no ':' + # in any case. If a long_option doesn't have a corresponding + # short_option, short_option should be None. All option tuples + # must have long options. + self.option_table = option_table + + # 'option_index' maps long option names to entries in the option + # table (ie. those 3-tuples). + self.option_index = {} + if self.option_table: + self._build_index() + + # 'alias' records (duh) alias options; {'foo': 'bar'} means + # --foo is an alias for --bar + self.alias = {} + + # 'negative_alias' keeps track of options that are the boolean + # opposite of some other option + self.negative_alias = {} + + # These keep track of the information in the option table. We + # don't actually populate these structures until we're ready to + # parse the command-line, since the 'option_table' passed in here + # isn't necessarily the final word. + self.short_opts = [] + self.long_opts = [] + self.short2long = {} + self.attr_name = {} + self.takes_arg = {} + + # And 'option_order' is filled up in 'getopt()'; it records the + # original order of options (and their values) on the command-line, + # but expands short options, converts aliases, etc. + self.option_order = [] + + def _build_index(self): + self.option_index.clear() + for option in self.option_table: + self.option_index[option[0]] = option + + def set_option_table(self, option_table): + self.option_table = option_table + self._build_index() + + def add_option(self, long_option, short_option=None, help_string=None): + if long_option in self.option_index: + raise DistutilsGetoptError( + "option conflict: already an option '%s'" % long_option + ) + else: + option = (long_option, short_option, help_string) + self.option_table.append(option) + self.option_index[long_option] = option + + def has_option(self, long_option): + """Return true if the option table for this parser has an + option with long name 'long_option'.""" + return long_option in self.option_index + + def get_attr_name(self, long_option): + """Translate long option name 'long_option' to the form it + has as an attribute of some object: ie., translate hyphens + to underscores.""" + return long_option.translate(longopt_xlate) + + def _check_alias_dict(self, aliases, what): + assert isinstance(aliases, dict) + for alias, opt in aliases.items(): + if alias not in self.option_index: + raise DistutilsGetoptError( + f"invalid {what} '{alias}': " f"option '{alias}' not defined" + ) + if opt not in self.option_index: + raise DistutilsGetoptError( + f"invalid {what} '{alias}': " f"aliased option '{opt}' not defined" + ) + + def set_aliases(self, alias): + """Set the aliases for this option parser.""" + self._check_alias_dict(alias, "alias") + self.alias = alias + + def set_negative_aliases(self, negative_alias): + """Set the negative aliases for this option parser. + 'negative_alias' should be a dictionary mapping option names to + option names, both the key and value must already be defined + in the option table.""" + self._check_alias_dict(negative_alias, "negative alias") + self.negative_alias = negative_alias + + def _grok_option_table(self): # noqa: C901 + """Populate the various data structures that keep tabs on the + option table. Called by 'getopt()' before it can do anything + worthwhile. + """ + self.long_opts = [] + self.short_opts = [] + self.short2long.clear() + self.repeat = {} + + for option in self.option_table: + if len(option) == 3: + long, short, help = option + repeat = 0 + elif len(option) == 4: + long, short, help, repeat = option + else: + # the option table is part of the code, so simply + # assert that it is correct + raise ValueError(f"invalid option tuple: {option!r}") + + # Type- and value-check the option names + if not isinstance(long, str) or len(long) < 2: + raise DistutilsGetoptError( + ("invalid long option '%s': must be a string of length >= 2") % long + ) + + if not ((short is None) or (isinstance(short, str) and len(short) == 1)): + raise DistutilsGetoptError( + "invalid short option '%s': " + "must a single character or None" % short + ) + + self.repeat[long] = repeat + self.long_opts.append(long) + + if long[-1] == '=': # option takes an argument? + if short: + short = short + ':' + long = long[0:-1] + self.takes_arg[long] = 1 + else: + # Is option is a "negative alias" for some other option (eg. + # "quiet" == "!verbose")? + alias_to = self.negative_alias.get(long) + if alias_to is not None: + if self.takes_arg[alias_to]: + raise DistutilsGetoptError( + f"invalid negative alias '{long}': " + f"aliased option '{alias_to}' takes a value" + ) + + self.long_opts[-1] = long # XXX redundant?! + self.takes_arg[long] = 0 + + # If this is an alias option, make sure its "takes arg" flag is + # the same as the option it's aliased to. + alias_to = self.alias.get(long) + if alias_to is not None: + if self.takes_arg[long] != self.takes_arg[alias_to]: + raise DistutilsGetoptError( + f"invalid alias '{long}': inconsistent with " + f"aliased option '{alias_to}' (one of them takes a value, " + "the other doesn't" + ) + + # Now enforce some bondage on the long option name, so we can + # later translate it to an attribute name on some object. Have + # to do this a bit late to make sure we've removed any trailing + # '='. + if not longopt_re.match(long): + raise DistutilsGetoptError( + "invalid long option name '%s' " + "(must be letters, numbers, hyphens only" % long + ) + + self.attr_name[long] = self.get_attr_name(long) + if short: + self.short_opts.append(short) + self.short2long[short[0]] = long + + def getopt(self, args=None, object=None): # noqa: C901 + """Parse command-line options in args. Store as attributes on object. + + If 'args' is None or not supplied, uses 'sys.argv[1:]'. If + 'object' is None or not supplied, creates a new OptionDummy + object, stores option values there, and returns a tuple (args, + object). If 'object' is supplied, it is modified in place and + 'getopt()' just returns 'args'; in both cases, the returned + 'args' is a modified copy of the passed-in 'args' list, which + is left untouched. + """ + if args is None: + args = sys.argv[1:] + if object is None: + object = OptionDummy() + created_object = True + else: + created_object = False + + self._grok_option_table() + + short_opts = ' '.join(self.short_opts) + try: + opts, args = getopt.getopt(args, short_opts, self.long_opts) + except getopt.error as msg: + raise DistutilsArgError(msg) + + for opt, val in opts: + if len(opt) == 2 and opt[0] == '-': # it's a short option + opt = self.short2long[opt[1]] + else: + assert len(opt) > 2 and opt[:2] == '--' + opt = opt[2:] + + alias = self.alias.get(opt) + if alias: + opt = alias + + if not self.takes_arg[opt]: # boolean option? + assert val == '', "boolean option can't have value" + alias = self.negative_alias.get(opt) + if alias: + opt = alias + val = 0 + else: + val = 1 + + attr = self.attr_name[opt] + # The only repeating option at the moment is 'verbose'. + # It has a negative option -q quiet, which should set verbose = 0. + if val and self.repeat.get(attr) is not None: + val = getattr(object, attr, 0) + 1 + setattr(object, attr, val) + self.option_order.append((opt, val)) + + # for opts + if created_object: + return args, object + else: + return args + + def get_option_order(self): + """Returns the list of (option, value) tuples processed by the + previous run of 'getopt()'. Raises RuntimeError if + 'getopt()' hasn't been called yet. + """ + if self.option_order is None: + raise RuntimeError("'getopt()' hasn't been called yet") + else: + return self.option_order + + def generate_help(self, header=None): # noqa: C901 + """Generate help text (a list of strings, one per suggested line of + output) from the option table for this FancyGetopt object. + """ + # Blithely assume the option table is good: probably wouldn't call + # 'generate_help()' unless you've already called 'getopt()'. + + # First pass: determine maximum length of long option names + max_opt = 0 + for option in self.option_table: + long = option[0] + short = option[1] + ell = len(long) + if long[-1] == '=': + ell = ell - 1 + if short is not None: + ell = ell + 5 # " (-x)" where short == 'x' + if ell > max_opt: + max_opt = ell + + opt_width = max_opt + 2 + 2 + 2 # room for indent + dashes + gutter + + # Typical help block looks like this: + # --foo controls foonabulation + # Help block for longest option looks like this: + # --flimflam set the flim-flam level + # and with wrapped text: + # --flimflam set the flim-flam level (must be between + # 0 and 100, except on Tuesdays) + # Options with short names will have the short name shown (but + # it doesn't contribute to max_opt): + # --foo (-f) controls foonabulation + # If adding the short option would make the left column too wide, + # we push the explanation off to the next line + # --flimflam (-l) + # set the flim-flam level + # Important parameters: + # - 2 spaces before option block start lines + # - 2 dashes for each long option name + # - min. 2 spaces between option and explanation (gutter) + # - 5 characters (incl. space) for short option name + + # Now generate lines of help text. (If 80 columns were good enough + # for Jesus, then 78 columns are good enough for me!) + line_width = 78 + text_width = line_width - opt_width + big_indent = ' ' * opt_width + if header: + lines = [header] + else: + lines = ['Option summary:'] + + for option in self.option_table: + long, short, help = option[:3] + text = wrap_text(help, text_width) + if long[-1] == '=': + long = long[0:-1] + + # Case 1: no short option at all (makes life easy) + if short is None: + if text: + lines.append(" --%-*s %s" % (max_opt, long, text[0])) + else: + lines.append(" --%-*s " % (max_opt, long)) + + # Case 2: we have a short option, so we have to include it + # just after the long option + else: + opt_names = f"{long} (-{short})" + if text: + lines.append(" --%-*s %s" % (max_opt, opt_names, text[0])) + else: + lines.append(" --%-*s" % opt_names) + + for ell in text[1:]: + lines.append(big_indent + ell) + return lines + + def print_help(self, header=None, file=None): + if file is None: + file = sys.stdout + for line in self.generate_help(header): + file.write(line + "\n") + + +def fancy_getopt(options, negative_opt, object, args): + parser = FancyGetopt(options) + parser.set_negative_aliases(negative_opt) + return parser.getopt(args, object) + + +WS_TRANS = {ord(_wschar): ' ' for _wschar in string.whitespace} + + +def wrap_text(text, width): + """wrap_text(text : string, width : int) -> [string] + + Split 'text' into multiple lines of no more than 'width' characters + each, and return the list of strings that results. + """ + if text is None: + return [] + if len(text) <= width: + return [text] + + text = text.expandtabs() + text = text.translate(WS_TRANS) + chunks = re.split(r'( +|-+)', text) + chunks = [ch for ch in chunks if ch] # ' - ' results in empty strings + lines = [] + + while chunks: + cur_line = [] # list of chunks (to-be-joined) + cur_len = 0 # length of current line + + while chunks: + ell = len(chunks[0]) + if cur_len + ell <= width: # can squeeze (at least) this chunk in + cur_line.append(chunks[0]) + del chunks[0] + cur_len = cur_len + ell + else: # this line is full + # drop last chunk if all space + if cur_line and cur_line[-1][0] == ' ': + del cur_line[-1] + break + + if chunks: # any chunks left to process? + # if the current line is still empty, then we had a single + # chunk that's too big too fit on a line -- so we break + # down and break it up at the line width + if cur_len == 0: + cur_line.append(chunks[0][0:width]) + chunks[0] = chunks[0][width:] + + # all-whitespace chunks at the end of a line can be discarded + # (and we know from the re.split above that if a chunk has + # *any* whitespace, it is *all* whitespace) + if chunks[0][0] == ' ': + del chunks[0] + + # and store this line in the list-of-all-lines -- as a single + # string, of course! + lines.append(''.join(cur_line)) + + return lines + + +def translate_longopt(opt): + """Convert a long option name to a valid Python identifier by + changing "-" to "_". + """ + return opt.translate(longopt_xlate) + + +class OptionDummy: + """Dummy class just used as a place to hold command-line option + values as instance attributes.""" + + def __init__(self, options: Sequence[Any] = []): + """Create a new OptionDummy instance. The attributes listed in + 'options' will be initialized to None.""" + for opt in options: + setattr(self, opt, None) + + +if __name__ == "__main__": + text = """\ +Tra-la-la, supercalifragilisticexpialidocious. +How *do* you spell that odd word, anyways? +(Someone ask Mary -- she'll know [or she'll +say, "How should I know?"].)""" + + for w in (10, 20, 30, 40): + print("width: %d" % w) + print("\n".join(wrap_text(text, w))) + print() diff --git a/venv/Lib/site-packages/setuptools/_distutils/file_util.py b/venv/Lib/site-packages/setuptools/_distutils/file_util.py new file mode 100644 index 0000000..960def9 --- /dev/null +++ b/venv/Lib/site-packages/setuptools/_distutils/file_util.py @@ -0,0 +1,235 @@ +"""distutils.file_util + +Utility functions for operating on single files. +""" + +import os + +from ._log import log +from .errors import DistutilsFileError + +# for generating verbose output in 'copy_file()' +_copy_action = {None: 'copying', 'hard': 'hard linking', 'sym': 'symbolically linking'} + + +def _copy_file_contents(src, dst, buffer_size=16 * 1024): # noqa: C901 + """Copy the file 'src' to 'dst'; both must be filenames. Any error + opening either file, reading from 'src', or writing to 'dst', raises + DistutilsFileError. Data is read/written in chunks of 'buffer_size' + bytes (default 16k). No attempt is made to handle anything apart from + regular files. + """ + # Stolen from shutil module in the standard library, but with + # custom error-handling added. + fsrc = None + fdst = None + try: + try: + fsrc = open(src, 'rb') + except OSError as e: + raise DistutilsFileError(f"could not open '{src}': {e.strerror}") + + if os.path.exists(dst): + try: + os.unlink(dst) + except OSError as e: + raise DistutilsFileError(f"could not delete '{dst}': {e.strerror}") + + try: + fdst = open(dst, 'wb') + except OSError as e: + raise DistutilsFileError(f"could not create '{dst}': {e.strerror}") + + while True: + try: + buf = fsrc.read(buffer_size) + except OSError as e: + raise DistutilsFileError(f"could not read from '{src}': {e.strerror}") + + if not buf: + break + + try: + fdst.write(buf) + except OSError as e: + raise DistutilsFileError(f"could not write to '{dst}': {e.strerror}") + finally: + if fdst: + fdst.close() + if fsrc: + fsrc.close() + + +def copy_file( # noqa: C901 + src, + dst, + preserve_mode=1, + preserve_times=1, + update=0, + link=None, + verbose=1, + dry_run=0, +): + """Copy a file 'src' to 'dst'. If 'dst' is a directory, then 'src' is + copied there with the same name; otherwise, it must be a filename. (If + the file exists, it will be ruthlessly clobbered.) If 'preserve_mode' + is true (the default), the file's mode (type and permission bits, or + whatever is analogous on the current platform) is copied. If + 'preserve_times' is true (the default), the last-modified and + last-access times are copied as well. If 'update' is true, 'src' will + only be copied if 'dst' does not exist, or if 'dst' does exist but is + older than 'src'. + + 'link' allows you to make hard links (os.link) or symbolic links + (os.symlink) instead of copying: set it to "hard" or "sym"; if it is + None (the default), files are copied. Don't set 'link' on systems that + don't support it: 'copy_file()' doesn't check if hard or symbolic + linking is available. If hardlink fails, falls back to + _copy_file_contents(). + + Under Mac OS, uses the native file copy function in macostools; on + other systems, uses '_copy_file_contents()' to copy file contents. + + Return a tuple (dest_name, copied): 'dest_name' is the actual name of + the output file, and 'copied' is true if the file was copied (or would + have been copied, if 'dry_run' true). + """ + # XXX if the destination file already exists, we clobber it if + # copying, but blow up if linking. Hmmm. And I don't know what + # macostools.copyfile() does. Should definitely be consistent, and + # should probably blow up if destination exists and we would be + # changing it (ie. it's not already a hard/soft link to src OR + # (not update) and (src newer than dst). + + from distutils._modified import newer + from stat import S_IMODE, ST_ATIME, ST_MODE, ST_MTIME + + if not os.path.isfile(src): + raise DistutilsFileError( + "can't copy '%s': doesn't exist or not a regular file" % src + ) + + if os.path.isdir(dst): + dir = dst + dst = os.path.join(dst, os.path.basename(src)) + else: + dir = os.path.dirname(dst) + + if update and not newer(src, dst): + if verbose >= 1: + log.debug("not copying %s (output up-to-date)", src) + return (dst, 0) + + try: + action = _copy_action[link] + except KeyError: + raise ValueError("invalid value '%s' for 'link' argument" % link) + + if verbose >= 1: + if os.path.basename(dst) == os.path.basename(src): + log.info("%s %s -> %s", action, src, dir) + else: + log.info("%s %s -> %s", action, src, dst) + + if dry_run: + return (dst, 1) + + # If linking (hard or symbolic), use the appropriate system call + # (Unix only, of course, but that's the caller's responsibility) + elif link == 'hard': + if not (os.path.exists(dst) and os.path.samefile(src, dst)): + try: + os.link(src, dst) + return (dst, 1) + except OSError: + # If hard linking fails, fall back on copying file + # (some special filesystems don't support hard linking + # even under Unix, see issue #8876). + pass + elif link == 'sym': + if not (os.path.exists(dst) and os.path.samefile(src, dst)): + os.symlink(src, dst) + return (dst, 1) + + # Otherwise (non-Mac, not linking), copy the file contents and + # (optionally) copy the times and mode. + _copy_file_contents(src, dst) + if preserve_mode or preserve_times: + st = os.stat(src) + + # According to David Ascher , utime() should be done + # before chmod() (at least under NT). + if preserve_times: + os.utime(dst, (st[ST_ATIME], st[ST_MTIME])) + if preserve_mode: + os.chmod(dst, S_IMODE(st[ST_MODE])) + + return (dst, 1) + + +# XXX I suspect this is Unix-specific -- need porting help! +def move_file(src, dst, verbose=1, dry_run=0): # noqa: C901 + """Move a file 'src' to 'dst'. If 'dst' is a directory, the file will + be moved into it with the same name; otherwise, 'src' is just renamed + to 'dst'. Return the new full name of the file. + + Handles cross-device moves on Unix using 'copy_file()'. What about + other systems??? + """ + import errno + from os.path import basename, dirname, exists, isdir, isfile + + if verbose >= 1: + log.info("moving %s -> %s", src, dst) + + if dry_run: + return dst + + if not isfile(src): + raise DistutilsFileError("can't move '%s': not a regular file" % src) + + if isdir(dst): + dst = os.path.join(dst, basename(src)) + elif exists(dst): + raise DistutilsFileError( + f"can't move '{src}': destination '{dst}' already exists" + ) + + if not isdir(dirname(dst)): + raise DistutilsFileError( + f"can't move '{src}': destination '{dst}' not a valid path" + ) + + copy_it = False + try: + os.rename(src, dst) + except OSError as e: + (num, msg) = e.args + if num == errno.EXDEV: + copy_it = True + else: + raise DistutilsFileError(f"couldn't move '{src}' to '{dst}': {msg}") + + if copy_it: + copy_file(src, dst, verbose=verbose) + try: + os.unlink(src) + except OSError as e: + (num, msg) = e.args + try: + os.unlink(dst) + except OSError: + pass + raise DistutilsFileError( + f"couldn't move '{src}' to '{dst}' by copy/delete: " + f"delete '{src}' failed: {msg}" + ) + return dst + + +def write_file(filename, contents): + """Create a file with the specified name and write 'contents' (a + sequence of strings without line terminators) to it. + """ + with open(filename, 'w', encoding='utf-8') as f: + f.writelines(line + '\n' for line in contents) diff --git a/venv/Lib/site-packages/setuptools/_distutils/filelist.py b/venv/Lib/site-packages/setuptools/_distutils/filelist.py new file mode 100644 index 0000000..71ffb2a --- /dev/null +++ b/venv/Lib/site-packages/setuptools/_distutils/filelist.py @@ -0,0 +1,369 @@ +"""distutils.filelist + +Provides the FileList class, used for poking about the filesystem +and building lists of files. +""" + +import fnmatch +import functools +import os +import re + +from ._log import log +from .errors import DistutilsInternalError, DistutilsTemplateError +from .util import convert_path + + +class FileList: + """A list of files built by on exploring the filesystem and filtered by + applying various patterns to what we find there. + + Instance attributes: + dir + directory from which files will be taken -- only used if + 'allfiles' not supplied to constructor + files + list of filenames currently being built/filtered/manipulated + allfiles + complete list of files under consideration (ie. without any + filtering applied) + """ + + def __init__(self, warn=None, debug_print=None): + # ignore argument to FileList, but keep them for backwards + # compatibility + self.allfiles = None + self.files = [] + + def set_allfiles(self, allfiles): + self.allfiles = allfiles + + def findall(self, dir=os.curdir): + self.allfiles = findall(dir) + + def debug_print(self, msg): + """Print 'msg' to stdout if the global DEBUG (taken from the + DISTUTILS_DEBUG environment variable) flag is true. + """ + from distutils.debug import DEBUG + + if DEBUG: + print(msg) + + # Collection methods + + def append(self, item): + self.files.append(item) + + def extend(self, items): + self.files.extend(items) + + def sort(self): + # Not a strict lexical sort! + sortable_files = sorted(map(os.path.split, self.files)) + self.files = [] + for sort_tuple in sortable_files: + self.files.append(os.path.join(*sort_tuple)) + + # Other miscellaneous utility methods + + def remove_duplicates(self): + # Assumes list has been sorted! + for i in range(len(self.files) - 1, 0, -1): + if self.files[i] == self.files[i - 1]: + del self.files[i] + + # "File template" methods + + def _parse_template_line(self, line): + words = line.split() + action = words[0] + + patterns = dir = dir_pattern = None + + if action in ('include', 'exclude', 'global-include', 'global-exclude'): + if len(words) < 2: + raise DistutilsTemplateError( + "'%s' expects ..." % action + ) + patterns = [convert_path(w) for w in words[1:]] + elif action in ('recursive-include', 'recursive-exclude'): + if len(words) < 3: + raise DistutilsTemplateError( + "'%s' expects

..." % action + ) + dir = convert_path(words[1]) + patterns = [convert_path(w) for w in words[2:]] + elif action in ('graft', 'prune'): + if len(words) != 2: + raise DistutilsTemplateError( + "'%s' expects a single " % action + ) + dir_pattern = convert_path(words[1]) + else: + raise DistutilsTemplateError("unknown action '%s'" % action) + + return (action, patterns, dir, dir_pattern) + + def process_template_line(self, line): # noqa: C901 + # 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(pattern, anchor=1): + log.warning("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(pattern, anchor=1): + log.warning( + ( + "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.include_pattern(pattern, anchor=0): + log.warning( + ( + "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.exclude_pattern(pattern, anchor=0): + log.warning( + ( + "warning: no previously-included files matching " + "'%s' found anywhere in distribution" + ), + pattern, + ) + + elif action == 'recursive-include': + self.debug_print("recursive-include {} {}".format(dir, ' '.join(patterns))) + for pattern in patterns: + if not self.include_pattern(pattern, prefix=dir): + msg = "warning: no files found matching '%s' under directory '%s'" + log.warning(msg, pattern, dir) + + elif action == 'recursive-exclude': + self.debug_print("recursive-exclude {} {}".format(dir, ' '.join(patterns))) + for pattern in patterns: + if not self.exclude_pattern(pattern, prefix=dir): + log.warning( + ( + "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.include_pattern(None, prefix=dir_pattern): + log.warning("warning: no directories found matching '%s'", dir_pattern) + + elif action == 'prune': + self.debug_print("prune " + dir_pattern) + if not self.exclude_pattern(None, prefix=dir_pattern): + log.warning( + ("no previously-included directories found matching '%s'"), + dir_pattern, + ) + else: + raise DistutilsInternalError( + "this cannot happen: invalid action '%s'" % action + ) + + # Filtering/selection methods + + def include_pattern(self, pattern, anchor=1, prefix=None, is_regex=0): + """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, False otherwise. + """ + # XXX docstring lying about what the special chars are? + files_found = False + pattern_re = translate_pattern(pattern, anchor, prefix, is_regex) + self.debug_print("include_pattern: applying regex r'%s'" % pattern_re.pattern) + + # delayed loading of allfiles list + if self.allfiles is None: + self.findall() + + for name in self.allfiles: + if pattern_re.search(name): + self.debug_print(" adding " + name) + self.files.append(name) + files_found = True + return files_found + + def exclude_pattern(self, pattern, anchor=1, prefix=None, is_regex=0): + """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, False otherwise. + """ + files_found = False + pattern_re = translate_pattern(pattern, anchor, prefix, is_regex) + self.debug_print("exclude_pattern: applying regex r'%s'" % pattern_re.pattern) + for i in range(len(self.files) - 1, -1, -1): + if pattern_re.search(self.files[i]): + self.debug_print(" removing " + self.files[i]) + del self.files[i] + files_found = True + return files_found + + +# Utility functions + + +def _find_all_simple(path): + """ + Find all files under 'path' + """ + all_unique = _UniqueDirs.filter(os.walk(path, followlinks=True)) + results = ( + os.path.join(base, file) for base, dirs, files in all_unique for file in files + ) + return filter(os.path.isfile, results) + + +class _UniqueDirs(set): + """ + Exclude previously-seen dirs from walk results, + avoiding infinite recursion. + Ref https://bugs.python.org/issue44497. + """ + + def __call__(self, walk_item): + """ + Given an item from an os.walk result, determine + if the item represents a unique dir for this instance + and if not, prevent further traversal. + """ + base, dirs, files = walk_item + stat = os.stat(base) + candidate = stat.st_dev, stat.st_ino + found = candidate in self + if found: + del dirs[:] + self.add(candidate) + return not found + + @classmethod + def filter(cls, items): + return filter(cls(), items) + + +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) + + +def glob_to_re(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'((?= 2: + set_threshold(logging.DEBUG) + + +class Log(logging.Logger): + """distutils.log.Log is deprecated, please use an alternative from `logging`.""" + + def __init__(self, threshold=WARN): + warnings.warn(Log.__doc__) # avoid DeprecationWarning to ensure warn is shown + super().__init__(__name__, level=threshold) + + @property + def threshold(self): + return self.level + + @threshold.setter + def threshold(self, level): + self.setLevel(level) + + warn = logging.Logger.warning diff --git a/venv/Lib/site-packages/setuptools/_distutils/msvc9compiler.py b/venv/Lib/site-packages/setuptools/_distutils/msvc9compiler.py new file mode 100644 index 0000000..6a0105e --- /dev/null +++ b/venv/Lib/site-packages/setuptools/_distutils/msvc9compiler.py @@ -0,0 +1,824 @@ +"""distutils.msvc9compiler + +Contains MSVCCompiler, an implementation of the abstract CCompiler class +for the Microsoft Visual Studio 2008. + +The module is compatible with VS 2005 and VS 2008. You can find legacy support +for older versions of VS in distutils.msvccompiler. +""" + +# Written by Perry Stoll +# hacked by Robin Becker and Thomas Heller to do a better job of +# finding DevStudio (through the registry) +# ported to VS2005 and VS 2008 by Christian Heimes + +import os +import re +import subprocess +import sys +import warnings +import winreg + +from ._log import log +from .ccompiler import CCompiler, gen_lib_options +from .errors import ( + CompileError, + DistutilsExecError, + DistutilsPlatformError, + LibError, + LinkError, +) +from .util import get_platform + +warnings.warn( + "msvc9compiler is deprecated and slated to be removed " + "in the future. Please discontinue use or file an issue " + "with pypa/distutils describing your use case.", + DeprecationWarning, +) + +RegOpenKeyEx = winreg.OpenKeyEx +RegEnumKey = winreg.EnumKey +RegEnumValue = winreg.EnumValue +RegError = winreg.error + +HKEYS = ( + winreg.HKEY_USERS, + winreg.HKEY_CURRENT_USER, + winreg.HKEY_LOCAL_MACHINE, + winreg.HKEY_CLASSES_ROOT, +) + +NATIVE_WIN64 = sys.platform == 'win32' and sys.maxsize > 2**32 +if NATIVE_WIN64: + # Visual C++ is a 32-bit application, so we need to look in + # the corresponding registry branch, if we're running a + # 64-bit Python on Win64 + VS_BASE = r"Software\Wow6432Node\Microsoft\VisualStudio\%0.1f" + WINSDK_BASE = r"Software\Wow6432Node\Microsoft\Microsoft SDKs\Windows" + NET_BASE = r"Software\Wow6432Node\Microsoft\.NETFramework" +else: + VS_BASE = r"Software\Microsoft\VisualStudio\%0.1f" + WINSDK_BASE = r"Software\Microsoft\Microsoft SDKs\Windows" + NET_BASE = r"Software\Microsoft\.NETFramework" + +# A map keyed by get_platform() return values to values accepted by +# 'vcvarsall.bat'. Note a cross-compile may combine these (eg, 'x86_amd64' is +# the param to cross-compile on x86 targeting amd64.) +PLAT_TO_VCVARS = { + 'win32': 'x86', + 'win-amd64': 'amd64', +} + + +class Reg: + """Helper class to read values from the registry""" + + def get_value(cls, path, key): + for base in HKEYS: + d = cls.read_values(base, path) + if d and key in d: + return d[key] + raise KeyError(key) + + get_value = classmethod(get_value) + + def read_keys(cls, base, key): + """Return list of registry keys.""" + try: + handle = RegOpenKeyEx(base, key) + except RegError: + return None + L = [] + i = 0 + while True: + try: + k = RegEnumKey(handle, i) + except RegError: + break + L.append(k) + i += 1 + return L + + read_keys = classmethod(read_keys) + + def read_values(cls, base, key): + """Return dict of registry keys and values. + + All names are converted to lowercase. + """ + try: + handle = RegOpenKeyEx(base, key) + except RegError: + return None + d = {} + i = 0 + while True: + try: + name, value, type = RegEnumValue(handle, i) + except RegError: + break + name = name.lower() + d[cls.convert_mbcs(name)] = cls.convert_mbcs(value) + i += 1 + return d + + read_values = classmethod(read_values) + + def convert_mbcs(s): + dec = getattr(s, "decode", None) + if dec is not None: + try: + s = dec("mbcs") + except UnicodeError: + pass + return s + + convert_mbcs = staticmethod(convert_mbcs) + + +class MacroExpander: + def __init__(self, version): + self.macros = {} + self.vsbase = VS_BASE % version + self.load_macros(version) + + def set_macro(self, macro, path, key): + self.macros["$(%s)" % macro] = Reg.get_value(path, key) + + def load_macros(self, version): + self.set_macro("VCInstallDir", self.vsbase + r"\Setup\VC", "productdir") + self.set_macro("VSInstallDir", self.vsbase + r"\Setup\VS", "productdir") + self.set_macro("FrameworkDir", NET_BASE, "installroot") + try: + if version >= 8.0: + self.set_macro("FrameworkSDKDir", NET_BASE, "sdkinstallrootv2.0") + else: + raise KeyError("sdkinstallrootv2.0") + except KeyError: + raise DistutilsPlatformError( + """Python was built with Visual Studio 2008; +extensions must be built with a compiler than can generate compatible binaries. +Visual Studio 2008 was not found on this system. If you have Cygwin installed, +you can try compiling with MingW32, by passing "-c mingw32" to setup.py.""" + ) + + if version >= 9.0: + self.set_macro("FrameworkVersion", self.vsbase, "clr version") + self.set_macro("WindowsSdkDir", WINSDK_BASE, "currentinstallfolder") + else: + p = r"Software\Microsoft\NET Framework Setup\Product" + for base in HKEYS: + try: + h = RegOpenKeyEx(base, p) + except RegError: + continue + key = RegEnumKey(h, 0) + d = Reg.get_value(base, rf"{p}\{key}") + self.macros["$(FrameworkVersion)"] = d["version"] + + def sub(self, s): + for k, v in self.macros.items(): + s = s.replace(k, v) + return s + + +def get_build_version(): + """Return the version of MSVC that was used to build Python. + + For Python 2.3 and up, the version number is included in + sys.version. For earlier versions, assume the compiler is MSVC 6. + """ + prefix = "MSC v." + i = sys.version.find(prefix) + if i == -1: + return 6 + i = i + len(prefix) + s, rest = sys.version[i:].split(" ", 1) + majorVersion = int(s[:-2]) - 6 + if majorVersion >= 13: + # v13 was skipped and should be v14 + majorVersion += 1 + minorVersion = int(s[2:3]) / 10.0 + # I don't think paths are affected by minor version in version 6 + if majorVersion == 6: + minorVersion = 0 + if majorVersion >= 6: + return majorVersion + minorVersion + # else we don't know what version of the compiler this is + return None + + +def normalize_and_reduce_paths(paths): + """Return a list of normalized paths with duplicates removed. + + The current order of paths is maintained. + """ + # Paths are normalized so things like: /a and /a/ aren't both preserved. + reduced_paths = [] + for p in paths: + np = os.path.normpath(p) + # XXX(nnorwitz): O(n**2), if reduced_paths gets long perhaps use a set. + if np not in reduced_paths: + reduced_paths.append(np) + return reduced_paths + + +def removeDuplicates(variable): + """Remove duplicate values of an environment variable.""" + oldList = variable.split(os.pathsep) + newList = [] + for i in oldList: + if i not in newList: + newList.append(i) + newVariable = os.pathsep.join(newList) + return newVariable + + +def find_vcvarsall(version): + """Find the vcvarsall.bat file + + At first it tries to find the productdir of VS 2008 in the registry. If + that fails it falls back to the VS90COMNTOOLS env var. + """ + vsbase = VS_BASE % version + try: + productdir = Reg.get_value(r"%s\Setup\VC" % vsbase, "productdir") + except KeyError: + log.debug("Unable to find productdir in registry") + productdir = None + + if not productdir or not os.path.isdir(productdir): + toolskey = "VS%0.f0COMNTOOLS" % version + toolsdir = os.environ.get(toolskey, None) + + if toolsdir and os.path.isdir(toolsdir): + productdir = os.path.join(toolsdir, os.pardir, os.pardir, "VC") + productdir = os.path.abspath(productdir) + if not os.path.isdir(productdir): + log.debug("%s is not a valid directory" % productdir) + return None + else: + log.debug("Env var %s is not set or invalid" % toolskey) + if not productdir: + log.debug("No productdir found") + return None + vcvarsall = os.path.join(productdir, "vcvarsall.bat") + if os.path.isfile(vcvarsall): + return vcvarsall + log.debug("Unable to find vcvarsall.bat") + return None + + +def query_vcvarsall(version, arch="x86"): + """Launch vcvarsall.bat and read the settings from its environment""" + vcvarsall = find_vcvarsall(version) + interesting = {"include", "lib", "libpath", "path"} + result = {} + + if vcvarsall is None: + raise DistutilsPlatformError("Unable to find vcvarsall.bat") + log.debug("Calling 'vcvarsall.bat %s' (version=%s)", arch, version) + popen = subprocess.Popen( + f'"{vcvarsall}" {arch} & set', + stdout=subprocess.PIPE, + stderr=subprocess.PIPE, + ) + try: + stdout, stderr = popen.communicate() + if popen.wait() != 0: + raise DistutilsPlatformError(stderr.decode("mbcs")) + + stdout = stdout.decode("mbcs") + for line in stdout.split("\n"): + line = Reg.convert_mbcs(line) + if '=' not in line: + continue + line = line.strip() + key, value = line.split('=', 1) + key = key.lower() + if key in interesting: + if value.endswith(os.pathsep): + value = value[:-1] + result[key] = removeDuplicates(value) + + finally: + popen.stdout.close() + popen.stderr.close() + + if len(result) != len(interesting): + raise ValueError(str(list(result.keys()))) + + return result + + +# More globals +VERSION = get_build_version() +# MACROS = MacroExpander(VERSION) + + +class MSVCCompiler(CCompiler): + """Concrete class that implements an interface to Microsoft Visual C++, + as defined by the CCompiler abstract class.""" + + compiler_type = 'msvc' + + # Just set this so CCompiler's constructor doesn't barf. We currently + # don't use the 'set_executables()' bureaucracy provided by CCompiler, + # as it really isn't necessary for this sort of single-compiler class. + # Would be nice to have a consistent interface with UnixCCompiler, + # though, so it's worth thinking about. + executables = {} + + # Private class data (need to distinguish C from C++ source for compiler) + _c_extensions = ['.c'] + _cpp_extensions = ['.cc', '.cpp', '.cxx'] + _rc_extensions = ['.rc'] + _mc_extensions = ['.mc'] + + # Needed for the filename generation methods provided by the + # base class, CCompiler. + src_extensions = _c_extensions + _cpp_extensions + _rc_extensions + _mc_extensions + res_extension = '.res' + obj_extension = '.obj' + static_lib_extension = '.lib' + shared_lib_extension = '.dll' + static_lib_format = shared_lib_format = '%s%s' + exe_extension = '.exe' + + def __init__(self, verbose=0, dry_run=0, force=0): + super().__init__(verbose, dry_run, force) + self.__version = VERSION + self.__root = r"Software\Microsoft\VisualStudio" + # self.__macros = MACROS + self.__paths = [] + # target platform (.plat_name is consistent with 'bdist') + self.plat_name = None + self.__arch = None # deprecated name + self.initialized = False + + def initialize(self, plat_name=None): # noqa: C901 + # multi-init means we would need to check platform same each time... + assert not self.initialized, "don't init multiple times" + if self.__version < 8.0: + raise DistutilsPlatformError( + "VC %0.1f is not supported by this module" % self.__version + ) + if plat_name is None: + plat_name = get_platform() + # sanity check for platforms to prevent obscure errors later. + ok_plats = 'win32', 'win-amd64' + if plat_name not in ok_plats: + raise DistutilsPlatformError(f"--plat-name must be one of {ok_plats}") + + if ( + "DISTUTILS_USE_SDK" in os.environ + and "MSSdk" in os.environ + and self.find_exe("cl.exe") + ): + # Assume that the SDK set up everything alright; don't try to be + # smarter + self.cc = "cl.exe" + self.linker = "link.exe" + self.lib = "lib.exe" + self.rc = "rc.exe" + self.mc = "mc.exe" + else: + # On x86, 'vcvars32.bat amd64' creates an env that doesn't work; + # to cross compile, you use 'x86_amd64'. + # On AMD64, 'vcvars32.bat amd64' is a native build env; to cross + # compile use 'x86' (ie, it runs the x86 compiler directly) + if plat_name in (get_platform(), 'win32'): + # native build or cross-compile to win32 + plat_spec = PLAT_TO_VCVARS[plat_name] + else: + # cross compile from win32 -> some 64bit + plat_spec = ( + PLAT_TO_VCVARS[get_platform()] + '_' + PLAT_TO_VCVARS[plat_name] + ) + + vc_env = query_vcvarsall(VERSION, plat_spec) + + self.__paths = vc_env['path'].split(os.pathsep) + os.environ['lib'] = vc_env['lib'] + os.environ['include'] = vc_env['include'] + + if len(self.__paths) == 0: + raise DistutilsPlatformError( + "Python was built with %s, " + "and extensions need to be built with the same " + "version of the compiler, but it isn't installed." % self.__product + ) + + self.cc = self.find_exe("cl.exe") + self.linker = self.find_exe("link.exe") + self.lib = self.find_exe("lib.exe") + self.rc = self.find_exe("rc.exe") # resource compiler + self.mc = self.find_exe("mc.exe") # message compiler + # self.set_path_env_var('lib') + # self.set_path_env_var('include') + + # extend the MSVC path with the current path + try: + for p in os.environ['path'].split(';'): + self.__paths.append(p) + except KeyError: + pass + self.__paths = normalize_and_reduce_paths(self.__paths) + os.environ['path'] = ";".join(self.__paths) + + self.preprocess_options = None + if self.__arch == "x86": + self.compile_options = ['/nologo', '/O2', '/MD', '/W3', '/DNDEBUG'] + self.compile_options_debug = [ + '/nologo', + '/Od', + '/MDd', + '/W3', + '/Z7', + '/D_DEBUG', + ] + else: + # Win64 + self.compile_options = ['/nologo', '/O2', '/MD', '/W3', '/GS-', '/DNDEBUG'] + self.compile_options_debug = [ + '/nologo', + '/Od', + '/MDd', + '/W3', + '/GS-', + '/Z7', + '/D_DEBUG', + ] + + self.ldflags_shared = ['/DLL', '/nologo', '/INCREMENTAL:NO'] + if self.__version >= 7: + self.ldflags_shared_debug = ['/DLL', '/nologo', '/INCREMENTAL:no', '/DEBUG'] + self.ldflags_static = ['/nologo'] + + self.initialized = True + + # -- Worker methods ------------------------------------------------ + + def object_filenames(self, source_filenames, strip_dir=0, output_dir=''): + # Copied from ccompiler.py, extended to return .res as 'object'-file + # for .rc input file + if output_dir is None: + output_dir = '' + obj_names = [] + for src_name in source_filenames: + (base, ext) = os.path.splitext(src_name) + base = os.path.splitdrive(base)[1] # Chop off the drive + base = base[os.path.isabs(base) :] # If abs, chop off leading / + if ext not in self.src_extensions: + # Better to raise an exception instead of silently continuing + # and later complain about sources and targets having + # different lengths + raise CompileError("Don't know how to compile %s" % src_name) + if strip_dir: + base = os.path.basename(base) + if ext in self._rc_extensions: + obj_names.append(os.path.join(output_dir, base + self.res_extension)) + elif ext in self._mc_extensions: + obj_names.append(os.path.join(output_dir, base + self.res_extension)) + else: + obj_names.append(os.path.join(output_dir, base + self.obj_extension)) + return obj_names + + def compile( # noqa: C901 + self, + sources, + output_dir=None, + macros=None, + include_dirs=None, + debug=0, + extra_preargs=None, + extra_postargs=None, + depends=None, + ): + if not self.initialized: + self.initialize() + compile_info = self._setup_compile( + output_dir, macros, include_dirs, sources, depends, extra_postargs + ) + macros, objects, extra_postargs, pp_opts, build = compile_info + + compile_opts = extra_preargs or [] + compile_opts.append('/c') + if debug: + compile_opts.extend(self.compile_options_debug) + else: + compile_opts.extend(self.compile_options) + + for obj in objects: + try: + src, ext = build[obj] + except KeyError: + continue + if debug: + # pass the full pathname to MSVC in debug mode, + # this allows the debugger to find the source file + # without asking the user to browse for it + src = os.path.abspath(src) + + if ext in self._c_extensions: + input_opt = "/Tc" + src + elif ext in self._cpp_extensions: + input_opt = "/Tp" + src + elif ext in self._rc_extensions: + # compile .RC to .RES file + input_opt = src + output_opt = "/fo" + obj + try: + self.spawn([self.rc] + pp_opts + [output_opt] + [input_opt]) + except DistutilsExecError as msg: + raise CompileError(msg) + continue + elif ext in self._mc_extensions: + # Compile .MC to .RC file to .RES file. + # * '-h dir' specifies the directory for the + # generated include file + # * '-r dir' specifies the target directory of the + # generated RC file and the binary message resource + # it includes + # + # For now (since there are no options to change this), + # we use the source-directory for the include file and + # the build directory for the RC file and message + # resources. This works at least for win32all. + h_dir = os.path.dirname(src) + rc_dir = os.path.dirname(obj) + try: + # first compile .MC to .RC and .H file + self.spawn([self.mc] + ['-h', h_dir, '-r', rc_dir] + [src]) + base, _ = os.path.splitext(os.path.basename(src)) + rc_file = os.path.join(rc_dir, base + '.rc') + # then compile .RC to .RES file + self.spawn([self.rc] + ["/fo" + obj] + [rc_file]) + + except DistutilsExecError as msg: + raise CompileError(msg) + continue + else: + # how to handle this file? + raise CompileError(f"Don't know how to compile {src} to {obj}") + + output_opt = "/Fo" + obj + try: + self.spawn( + [self.cc] + + compile_opts + + pp_opts + + [input_opt, output_opt] + + extra_postargs + ) + except DistutilsExecError as msg: + raise CompileError(msg) + + return objects + + def create_static_lib( + self, objects, output_libname, output_dir=None, debug=0, target_lang=None + ): + if not self.initialized: + self.initialize() + (objects, output_dir) = self._fix_object_args(objects, output_dir) + output_filename = self.library_filename(output_libname, output_dir=output_dir) + + if self._need_link(objects, output_filename): + lib_args = objects + ['/OUT:' + output_filename] + if debug: + pass # XXX what goes here? + try: + self.spawn([self.lib] + lib_args) + except DistutilsExecError as msg: + raise LibError(msg) + else: + log.debug("skipping %s (up-to-date)", output_filename) + + def link( # noqa: C901 + self, + target_desc, + objects, + output_filename, + 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, + ): + if not self.initialized: + self.initialize() + (objects, output_dir) = self._fix_object_args(objects, output_dir) + fixed_args = self._fix_lib_args(libraries, library_dirs, runtime_library_dirs) + (libraries, library_dirs, runtime_library_dirs) = fixed_args + + if runtime_library_dirs: + self.warn( + "I don't know what to do with 'runtime_library_dirs': " + + str(runtime_library_dirs) + ) + + lib_opts = gen_lib_options(self, library_dirs, runtime_library_dirs, libraries) + if output_dir is not None: + output_filename = os.path.join(output_dir, output_filename) + + if self._need_link(objects, output_filename): + if target_desc == CCompiler.EXECUTABLE: + if debug: + ldflags = self.ldflags_shared_debug[1:] + else: + ldflags = self.ldflags_shared[1:] + else: + if debug: + ldflags = self.ldflags_shared_debug + else: + ldflags = self.ldflags_shared + + export_opts = [] + for sym in export_symbols or []: + export_opts.append("/EXPORT:" + sym) + + ld_args = ( + ldflags + lib_opts + export_opts + objects + ['/OUT:' + output_filename] + ) + + # The MSVC linker generates .lib and .exp files, which cannot be + # suppressed by any linker switches. The .lib files may even be + # needed! Make sure they are generated in the temporary build + # directory. Since they have different names for debug and release + # builds, they can go into the same directory. + build_temp = os.path.dirname(objects[0]) + if export_symbols is not None: + (dll_name, dll_ext) = os.path.splitext( + os.path.basename(output_filename) + ) + implib_file = os.path.join(build_temp, self.library_filename(dll_name)) + ld_args.append('/IMPLIB:' + implib_file) + + self.manifest_setup_ldargs(output_filename, build_temp, ld_args) + + if extra_preargs: + ld_args[:0] = extra_preargs + if extra_postargs: + ld_args.extend(extra_postargs) + + self.mkpath(os.path.dirname(output_filename)) + try: + self.spawn([self.linker] + ld_args) + except DistutilsExecError as msg: + raise LinkError(msg) + + # embed the manifest + # XXX - this is somewhat fragile - if mt.exe fails, distutils + # will still consider the DLL up-to-date, but it will not have a + # manifest. Maybe we should link to a temp file? OTOH, that + # implies a build environment error that shouldn't go undetected. + mfinfo = self.manifest_get_embed_info(target_desc, ld_args) + if mfinfo is not None: + mffilename, mfid = mfinfo + out_arg = f'-outputresource:{output_filename};{mfid}' + try: + self.spawn(['mt.exe', '-nologo', '-manifest', mffilename, out_arg]) + except DistutilsExecError as msg: + raise LinkError(msg) + else: + log.debug("skipping %s (up-to-date)", output_filename) + + def manifest_setup_ldargs(self, output_filename, build_temp, ld_args): + # If we need a manifest at all, an embedded manifest is recommended. + # See MSDN article titled + # "Understanding manifest generation for C/C++ programs" + # (currently at https://learn.microsoft.com/en-us/cpp/build/understanding-manifest-generation-for-c-cpp-programs) + # Ask the linker to generate the manifest in the temp dir, so + # we can check it, and possibly embed it, later. + temp_manifest = os.path.join( + build_temp, os.path.basename(output_filename) + ".manifest" + ) + ld_args.append('/MANIFESTFILE:' + temp_manifest) + + def manifest_get_embed_info(self, target_desc, ld_args): + # If a manifest should be embedded, return a tuple of + # (manifest_filename, resource_id). Returns None if no manifest + # should be embedded. See https://bugs.python.org/issue7833 for why + # we want to avoid any manifest for extension modules if we can) + for arg in ld_args: + if arg.startswith("/MANIFESTFILE:"): + temp_manifest = arg.split(":", 1)[1] + break + else: + # no /MANIFESTFILE so nothing to do. + return None + if target_desc == CCompiler.EXECUTABLE: + # by default, executables always get the manifest with the + # CRT referenced. + mfid = 1 + else: + # Extension modules try and avoid any manifest if possible. + mfid = 2 + temp_manifest = self._remove_visual_c_ref(temp_manifest) + if temp_manifest is None: + return None + return temp_manifest, mfid + + def _remove_visual_c_ref(self, manifest_file): + try: + # Remove references to the Visual C runtime, so they will + # fall through to the Visual C dependency of Python.exe. + # This way, when installed for a restricted user (e.g. + # runtimes are not in WinSxS folder, but in Python's own + # folder), the runtimes do not need to be in every folder + # with .pyd's. + # Returns either the filename of the modified manifest or + # None if no manifest should be embedded. + manifest_f = open(manifest_file) + try: + manifest_buf = manifest_f.read() + finally: + manifest_f.close() + pattern = re.compile( + r"""|)""", + re.DOTALL, + ) + manifest_buf = re.sub(pattern, "", manifest_buf) + pattern = r"\s*" + manifest_buf = re.sub(pattern, "", manifest_buf) + # Now see if any other assemblies are referenced - if not, we + # don't want a manifest embedded. + pattern = re.compile( + r"""|)""", + re.DOTALL, + ) + if re.search(pattern, manifest_buf) is None: + return None + + manifest_f = open(manifest_file, 'w') + try: + manifest_f.write(manifest_buf) + return manifest_file + finally: + manifest_f.close() + except OSError: + pass + + # -- Miscellaneous methods ----------------------------------------- + # These are all used by the 'gen_lib_options() function, in + # ccompiler.py. + + def library_dir_option(self, dir): + return "/LIBPATH:" + dir + + def runtime_library_dir_option(self, dir): + raise DistutilsPlatformError( + "don't know how to set runtime library search path for MSVC++" + ) + + def library_option(self, lib): + return self.library_filename(lib) + + def find_library_file(self, dirs, lib, debug=0): + # Prefer a debugging library if found (and requested), but deal + # with it if we don't have one. + if debug: + try_names = [lib + "_d", lib] + else: + try_names = [lib] + for dir in dirs: + for name in try_names: + libfile = os.path.join(dir, self.library_filename(name)) + if os.path.exists(libfile): + return libfile + else: + # Oops, didn't find it in *any* of 'dirs' + return None + + # Helper methods for using the MSVC registry settings + + def find_exe(self, exe): + """Return path to an MSVC executable program. + + Tries to find the program in several places: first, one of the + MSVC program search paths from the registry; next, the directories + in the PATH environment variable. If any of those work, return an + absolute path that is known to exist. If none of them work, just + return the original program name, 'exe'. + """ + for p in self.__paths: + fn = os.path.join(os.path.abspath(p), exe) + if os.path.isfile(fn): + return fn + + # didn't find it; try existing path + for p in os.environ['Path'].split(';'): + fn = os.path.join(os.path.abspath(p), exe) + if os.path.isfile(fn): + return fn + + return exe diff --git a/venv/Lib/site-packages/setuptools/_distutils/msvccompiler.py b/venv/Lib/site-packages/setuptools/_distutils/msvccompiler.py new file mode 100644 index 0000000..ac8b68c --- /dev/null +++ b/venv/Lib/site-packages/setuptools/_distutils/msvccompiler.py @@ -0,0 +1,689 @@ +"""distutils.msvccompiler + +Contains MSVCCompiler, an implementation of the abstract CCompiler class +for the Microsoft Visual Studio. +""" + +# Written by Perry Stoll +# hacked by Robin Becker and Thomas Heller to do a better job of +# finding DevStudio (through the registry) + +import os +import sys +import warnings + +from ._log import log +from .ccompiler import CCompiler, gen_lib_options +from .errors import ( + CompileError, + DistutilsExecError, + DistutilsPlatformError, + LibError, + LinkError, +) + +_can_read_reg = False +try: + import winreg + + _can_read_reg = True + hkey_mod = winreg + + RegOpenKeyEx = winreg.OpenKeyEx + RegEnumKey = winreg.EnumKey + RegEnumValue = winreg.EnumValue + RegError = winreg.error + +except ImportError: + try: + import win32api + import win32con + + _can_read_reg = True + hkey_mod = win32con + + RegOpenKeyEx = win32api.RegOpenKeyEx + RegEnumKey = win32api.RegEnumKey + RegEnumValue = win32api.RegEnumValue + RegError = win32api.error + except ImportError: + log.info( + "Warning: Can't read registry to find the " + "necessary compiler setting\n" + "Make sure that Python modules winreg, " + "win32api or win32con are installed." + ) + pass + +if _can_read_reg: + HKEYS = ( + hkey_mod.HKEY_USERS, + hkey_mod.HKEY_CURRENT_USER, + hkey_mod.HKEY_LOCAL_MACHINE, + hkey_mod.HKEY_CLASSES_ROOT, + ) + + +warnings.warn( + "msvccompiler is deprecated and slated to be removed " + "in the future. Please discontinue use or file an issue " + "with pypa/distutils describing your use case.", + DeprecationWarning, +) + + +def read_keys(base, key): + """Return list of registry keys.""" + try: + handle = RegOpenKeyEx(base, key) + except RegError: + return None + L = [] + i = 0 + while True: + try: + k = RegEnumKey(handle, i) + except RegError: + break + L.append(k) + i += 1 + return L + + +def read_values(base, key): + """Return dict of registry keys and values. + + All names are converted to lowercase. + """ + try: + handle = RegOpenKeyEx(base, key) + except RegError: + return None + d = {} + i = 0 + while True: + try: + name, value, type = RegEnumValue(handle, i) + except RegError: + break + name = name.lower() + d[convert_mbcs(name)] = convert_mbcs(value) + i += 1 + return d + + +def convert_mbcs(s): + dec = getattr(s, "decode", None) + if dec is not None: + try: + s = dec("mbcs") + except UnicodeError: + pass + return s + + +class MacroExpander: + def __init__(self, version): + self.macros = {} + self.load_macros(version) + + def set_macro(self, macro, path, key): + for base in HKEYS: + d = read_values(base, path) + if d: + self.macros["$(%s)" % macro] = d[key] + break + + def load_macros(self, version): + vsbase = r"Software\Microsoft\VisualStudio\%0.1f" % version + self.set_macro("VCInstallDir", vsbase + r"\Setup\VC", "productdir") + self.set_macro("VSInstallDir", vsbase + r"\Setup\VS", "productdir") + net = r"Software\Microsoft\.NETFramework" + self.set_macro("FrameworkDir", net, "installroot") + try: + if version > 7.0: + self.set_macro("FrameworkSDKDir", net, "sdkinstallrootv1.1") + else: + self.set_macro("FrameworkSDKDir", net, "sdkinstallroot") + except KeyError: + raise DistutilsPlatformError( + """Python was built with Visual Studio 2003; +extensions must be built with a compiler than can generate compatible binaries. +Visual Studio 2003 was not found on this system. If you have Cygwin installed, +you can try compiling with MingW32, by passing "-c mingw32" to setup.py.""" + ) + + p = r"Software\Microsoft\NET Framework Setup\Product" + for base in HKEYS: + try: + h = RegOpenKeyEx(base, p) + except RegError: + continue + key = RegEnumKey(h, 0) + d = read_values(base, rf"{p}\{key}") + self.macros["$(FrameworkVersion)"] = d["version"] + + def sub(self, s): + for k, v in self.macros.items(): + s = s.replace(k, v) + return s + + +def get_build_version(): + """Return the version of MSVC that was used to build Python. + + For Python 2.3 and up, the version number is included in + sys.version. For earlier versions, assume the compiler is MSVC 6. + """ + prefix = "MSC v." + i = sys.version.find(prefix) + if i == -1: + return 6 + i = i + len(prefix) + s, rest = sys.version[i:].split(" ", 1) + majorVersion = int(s[:-2]) - 6 + if majorVersion >= 13: + # v13 was skipped and should be v14 + majorVersion += 1 + minorVersion = int(s[2:3]) / 10.0 + # I don't think paths are affected by minor version in version 6 + if majorVersion == 6: + minorVersion = 0 + if majorVersion >= 6: + return majorVersion + minorVersion + # else we don't know what version of the compiler this is + return None + + +def get_build_architecture(): + """Return the processor architecture. + + Possible results are "Intel" or "AMD64". + """ + + prefix = " bit (" + i = sys.version.find(prefix) + if i == -1: + return "Intel" + j = sys.version.find(")", i) + return sys.version[i + len(prefix) : j] + + +def normalize_and_reduce_paths(paths): + """Return a list of normalized paths with duplicates removed. + + The current order of paths is maintained. + """ + # Paths are normalized so things like: /a and /a/ aren't both preserved. + reduced_paths = [] + for p in paths: + np = os.path.normpath(p) + # XXX(nnorwitz): O(n**2), if reduced_paths gets long perhaps use a set. + if np not in reduced_paths: + reduced_paths.append(np) + return reduced_paths + + +class MSVCCompiler(CCompiler): + """Concrete class that implements an interface to Microsoft Visual C++, + as defined by the CCompiler abstract class.""" + + compiler_type = 'msvc' + + # Just set this so CCompiler's constructor doesn't barf. We currently + # don't use the 'set_executables()' bureaucracy provided by CCompiler, + # as it really isn't necessary for this sort of single-compiler class. + # Would be nice to have a consistent interface with UnixCCompiler, + # though, so it's worth thinking about. + executables = {} + + # Private class data (need to distinguish C from C++ source for compiler) + _c_extensions = ['.c'] + _cpp_extensions = ['.cc', '.cpp', '.cxx'] + _rc_extensions = ['.rc'] + _mc_extensions = ['.mc'] + + # Needed for the filename generation methods provided by the + # base class, CCompiler. + src_extensions = _c_extensions + _cpp_extensions + _rc_extensions + _mc_extensions + res_extension = '.res' + obj_extension = '.obj' + static_lib_extension = '.lib' + shared_lib_extension = '.dll' + static_lib_format = shared_lib_format = '%s%s' + exe_extension = '.exe' + + def __init__(self, verbose=0, dry_run=0, force=0): + super().__init__(verbose, dry_run, force) + self.__version = get_build_version() + self.__arch = get_build_architecture() + if self.__arch == "Intel": + # x86 + if self.__version >= 7: + self.__root = r"Software\Microsoft\VisualStudio" + self.__macros = MacroExpander(self.__version) + else: + self.__root = r"Software\Microsoft\Devstudio" + self.__product = "Visual Studio version %s" % self.__version + else: + # Win64. Assume this was built with the platform SDK + self.__product = "Microsoft SDK compiler %s" % (self.__version + 6) + + self.initialized = False + + def initialize(self): + self.__paths = [] + if ( + "DISTUTILS_USE_SDK" in os.environ + and "MSSdk" in os.environ + and self.find_exe("cl.exe") + ): + # Assume that the SDK set up everything alright; don't try to be + # smarter + self.cc = "cl.exe" + self.linker = "link.exe" + self.lib = "lib.exe" + self.rc = "rc.exe" + self.mc = "mc.exe" + else: + self.__paths = self.get_msvc_paths("path") + + if len(self.__paths) == 0: + raise DistutilsPlatformError( + "Python was built with %s, " + "and extensions need to be built with the same " + "version of the compiler, but it isn't installed." % self.__product + ) + + self.cc = self.find_exe("cl.exe") + self.linker = self.find_exe("link.exe") + self.lib = self.find_exe("lib.exe") + self.rc = self.find_exe("rc.exe") # resource compiler + self.mc = self.find_exe("mc.exe") # message compiler + self.set_path_env_var('lib') + self.set_path_env_var('include') + + # extend the MSVC path with the current path + try: + for p in os.environ['path'].split(';'): + self.__paths.append(p) + except KeyError: + pass + self.__paths = normalize_and_reduce_paths(self.__paths) + os.environ['path'] = ";".join(self.__paths) + + self.preprocess_options = None + if self.__arch == "Intel": + self.compile_options = ['/nologo', '/O2', '/MD', '/W3', '/GX', '/DNDEBUG'] + self.compile_options_debug = [ + '/nologo', + '/Od', + '/MDd', + '/W3', + '/GX', + '/Z7', + '/D_DEBUG', + ] + else: + # Win64 + self.compile_options = ['/nologo', '/O2', '/MD', '/W3', '/GS-', '/DNDEBUG'] + self.compile_options_debug = [ + '/nologo', + '/Od', + '/MDd', + '/W3', + '/GS-', + '/Z7', + '/D_DEBUG', + ] + + self.ldflags_shared = ['/DLL', '/nologo', '/INCREMENTAL:NO'] + if self.__version >= 7: + self.ldflags_shared_debug = ['/DLL', '/nologo', '/INCREMENTAL:no', '/DEBUG'] + else: + self.ldflags_shared_debug = [ + '/DLL', + '/nologo', + '/INCREMENTAL:no', + '/pdb:None', + '/DEBUG', + ] + self.ldflags_static = ['/nologo'] + + self.initialized = True + + # -- Worker methods ------------------------------------------------ + + def object_filenames(self, source_filenames, strip_dir=0, output_dir=''): + # Copied from ccompiler.py, extended to return .res as 'object'-file + # for .rc input file + if output_dir is None: + output_dir = '' + obj_names = [] + for src_name in source_filenames: + (base, ext) = os.path.splitext(src_name) + base = os.path.splitdrive(base)[1] # Chop off the drive + base = base[os.path.isabs(base) :] # If abs, chop off leading / + if ext not in self.src_extensions: + # Better to raise an exception instead of silently continuing + # and later complain about sources and targets having + # different lengths + raise CompileError("Don't know how to compile %s" % src_name) + if strip_dir: + base = os.path.basename(base) + if ext in self._rc_extensions: + obj_names.append(os.path.join(output_dir, base + self.res_extension)) + elif ext in self._mc_extensions: + obj_names.append(os.path.join(output_dir, base + self.res_extension)) + else: + obj_names.append(os.path.join(output_dir, base + self.obj_extension)) + return obj_names + + def compile( # noqa: C901 + self, + sources, + output_dir=None, + macros=None, + include_dirs=None, + debug=0, + extra_preargs=None, + extra_postargs=None, + depends=None, + ): + if not self.initialized: + self.initialize() + compile_info = self._setup_compile( + output_dir, macros, include_dirs, sources, depends, extra_postargs + ) + macros, objects, extra_postargs, pp_opts, build = compile_info + + compile_opts = extra_preargs or [] + compile_opts.append('/c') + if debug: + compile_opts.extend(self.compile_options_debug) + else: + compile_opts.extend(self.compile_options) + + for obj in objects: + try: + src, ext = build[obj] + except KeyError: + continue + if debug: + # pass the full pathname to MSVC in debug mode, + # this allows the debugger to find the source file + # without asking the user to browse for it + src = os.path.abspath(src) + + if ext in self._c_extensions: + input_opt = "/Tc" + src + elif ext in self._cpp_extensions: + input_opt = "/Tp" + src + elif ext in self._rc_extensions: + # compile .RC to .RES file + input_opt = src + output_opt = "/fo" + obj + try: + self.spawn([self.rc] + pp_opts + [output_opt] + [input_opt]) + except DistutilsExecError as msg: + raise CompileError(msg) + continue + elif ext in self._mc_extensions: + # Compile .MC to .RC file to .RES file. + # * '-h dir' specifies the directory for the + # generated include file + # * '-r dir' specifies the target directory of the + # generated RC file and the binary message resource + # it includes + # + # For now (since there are no options to change this), + # we use the source-directory for the include file and + # the build directory for the RC file and message + # resources. This works at least for win32all. + h_dir = os.path.dirname(src) + rc_dir = os.path.dirname(obj) + try: + # first compile .MC to .RC and .H file + self.spawn([self.mc] + ['-h', h_dir, '-r', rc_dir] + [src]) + base, _ = os.path.splitext(os.path.basename(src)) + rc_file = os.path.join(rc_dir, base + '.rc') + # then compile .RC to .RES file + self.spawn([self.rc] + ["/fo" + obj] + [rc_file]) + + except DistutilsExecError as msg: + raise CompileError(msg) + continue + else: + # how to handle this file? + raise CompileError(f"Don't know how to compile {src} to {obj}") + + output_opt = "/Fo" + obj + try: + self.spawn( + [self.cc] + + compile_opts + + pp_opts + + [input_opt, output_opt] + + extra_postargs + ) + except DistutilsExecError as msg: + raise CompileError(msg) + + return objects + + def create_static_lib( + self, objects, output_libname, output_dir=None, debug=0, target_lang=None + ): + if not self.initialized: + self.initialize() + (objects, output_dir) = self._fix_object_args(objects, output_dir) + output_filename = self.library_filename(output_libname, output_dir=output_dir) + + if self._need_link(objects, output_filename): + lib_args = objects + ['/OUT:' + output_filename] + if debug: + pass # XXX what goes here? + try: + self.spawn([self.lib] + lib_args) + except DistutilsExecError as msg: + raise LibError(msg) + else: + log.debug("skipping %s (up-to-date)", output_filename) + + def link( # noqa: C901 + self, + target_desc, + objects, + output_filename, + 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, + ): + if not self.initialized: + self.initialize() + (objects, output_dir) = self._fix_object_args(objects, output_dir) + fixed_args = self._fix_lib_args(libraries, library_dirs, runtime_library_dirs) + (libraries, library_dirs, runtime_library_dirs) = fixed_args + + if runtime_library_dirs: + self.warn( + "I don't know what to do with 'runtime_library_dirs': " + + str(runtime_library_dirs) + ) + + lib_opts = gen_lib_options(self, library_dirs, runtime_library_dirs, libraries) + if output_dir is not None: + output_filename = os.path.join(output_dir, output_filename) + + if self._need_link(objects, output_filename): + if target_desc == CCompiler.EXECUTABLE: + if debug: + ldflags = self.ldflags_shared_debug[1:] + else: + ldflags = self.ldflags_shared[1:] + else: + if debug: + ldflags = self.ldflags_shared_debug + else: + ldflags = self.ldflags_shared + + export_opts = [] + for sym in export_symbols or []: + export_opts.append("/EXPORT:" + sym) + + ld_args = ( + ldflags + lib_opts + export_opts + objects + ['/OUT:' + output_filename] + ) + + # The MSVC linker generates .lib and .exp files, which cannot be + # suppressed by any linker switches. The .lib files may even be + # needed! Make sure they are generated in the temporary build + # directory. Since they have different names for debug and release + # builds, they can go into the same directory. + if export_symbols is not None: + (dll_name, dll_ext) = os.path.splitext( + os.path.basename(output_filename) + ) + implib_file = os.path.join( + os.path.dirname(objects[0]), self.library_filename(dll_name) + ) + ld_args.append('/IMPLIB:' + implib_file) + + if extra_preargs: + ld_args[:0] = extra_preargs + if extra_postargs: + ld_args.extend(extra_postargs) + + self.mkpath(os.path.dirname(output_filename)) + try: + self.spawn([self.linker] + ld_args) + except DistutilsExecError as msg: + raise LinkError(msg) + + else: + log.debug("skipping %s (up-to-date)", output_filename) + + # -- Miscellaneous methods ----------------------------------------- + # These are all used by the 'gen_lib_options() function, in + # ccompiler.py. + + def library_dir_option(self, dir): + return "/LIBPATH:" + dir + + def runtime_library_dir_option(self, dir): + raise DistutilsPlatformError( + "don't know how to set runtime library search path for MSVC++" + ) + + def library_option(self, lib): + return self.library_filename(lib) + + def find_library_file(self, dirs, lib, debug=0): + # Prefer a debugging library if found (and requested), but deal + # with it if we don't have one. + if debug: + try_names = [lib + "_d", lib] + else: + try_names = [lib] + for dir in dirs: + for name in try_names: + libfile = os.path.join(dir, self.library_filename(name)) + if os.path.exists(libfile): + return libfile + else: + # Oops, didn't find it in *any* of 'dirs' + return None + + # Helper methods for using the MSVC registry settings + + def find_exe(self, exe): + """Return path to an MSVC executable program. + + Tries to find the program in several places: first, one of the + MSVC program search paths from the registry; next, the directories + in the PATH environment variable. If any of those work, return an + absolute path that is known to exist. If none of them work, just + return the original program name, 'exe'. + """ + for p in self.__paths: + fn = os.path.join(os.path.abspath(p), exe) + if os.path.isfile(fn): + return fn + + # didn't find it; try existing path + for p in os.environ['Path'].split(';'): + fn = os.path.join(os.path.abspath(p), exe) + if os.path.isfile(fn): + return fn + + return exe + + def get_msvc_paths(self, path, platform='x86'): + """Get a list of devstudio directories (include, lib or path). + + Return a list of strings. The list will be empty if unable to + access the registry or appropriate registry keys not found. + """ + if not _can_read_reg: + return [] + + path = path + " dirs" + if self.__version >= 7: + key = rf"{self.__root}\{self.__version:0.1f}\VC\VC_OBJECTS_PLATFORM_INFO\Win32\Directories" + else: + key = ( + rf"{self.__root}\6.0\Build System\Components\Platforms" + rf"\Win32 ({platform})\Directories" + ) + + for base in HKEYS: + d = read_values(base, key) + if d: + if self.__version >= 7: + return self.__macros.sub(d[path]).split(";") + else: + return d[path].split(";") + # MSVC 6 seems to create the registry entries we need only when + # the GUI is run. + if self.__version == 6: + for base in HKEYS: + if read_values(base, r"%s\6.0" % self.__root) is not None: + self.warn( + "It seems you have Visual Studio 6 installed, " + "but the expected registry settings are not present.\n" + "You must at least run the Visual Studio GUI once " + "so that these entries are created." + ) + break + return [] + + def set_path_env_var(self, name): + """Set environment variable 'name' to an MSVC path type value. + + This is equivalent to a SET command prior to execution of spawned + commands. + """ + + if name == "lib": + p = self.get_msvc_paths("library") + else: + p = self.get_msvc_paths(name) + if p: + os.environ[name] = ';'.join(p) + + +if get_build_version() >= 8.0: + log.debug("Importing new compiler from distutils.msvc9compiler") + OldMSVCCompiler = MSVCCompiler + # get_build_architecture not really relevant now we support cross-compile + from distutils.msvc9compiler import ( + MacroExpander, # noqa: F811 + MSVCCompiler, + ) diff --git a/venv/Lib/site-packages/setuptools/_distutils/py38compat.py b/venv/Lib/site-packages/setuptools/_distutils/py38compat.py new file mode 100644 index 0000000..ab12119 --- /dev/null +++ b/venv/Lib/site-packages/setuptools/_distutils/py38compat.py @@ -0,0 +1,8 @@ +def aix_platform(osname, version, release): + try: + import _aix_support + + return _aix_support.aix_platform() + except ImportError: + pass + return f"{osname}-{version}.{release}" diff --git a/venv/Lib/site-packages/setuptools/_distutils/py39compat.py b/venv/Lib/site-packages/setuptools/_distutils/py39compat.py new file mode 100644 index 0000000..1b436d7 --- /dev/null +++ b/venv/Lib/site-packages/setuptools/_distutils/py39compat.py @@ -0,0 +1,66 @@ +import functools +import itertools +import platform +import sys + + +def add_ext_suffix_39(vars): + """ + Ensure vars contains 'EXT_SUFFIX'. pypa/distutils#130 + """ + import _imp + + ext_suffix = _imp.extension_suffixes()[0] + vars.update( + EXT_SUFFIX=ext_suffix, + # sysconfig sets SO to match EXT_SUFFIX, so maintain + # that expectation. + # https://github.com/python/cpython/blob/785cc6770588de087d09e89a69110af2542be208/Lib/sysconfig.py#L671-L673 + SO=ext_suffix, + ) + + +needs_ext_suffix = sys.version_info < (3, 10) and platform.system() == 'Windows' +add_ext_suffix = add_ext_suffix_39 if needs_ext_suffix else lambda vars: None + + +# from more_itertools +class UnequalIterablesError(ValueError): + def __init__(self, details=None): + msg = 'Iterables have different lengths' + if details is not None: + msg += (': index 0 has length {}; index {} has length {}').format(*details) + + super().__init__(msg) + + +# from more_itertools +def _zip_equal_generator(iterables): + _marker = object() + for combo in itertools.zip_longest(*iterables, fillvalue=_marker): + for val in combo: + if val is _marker: + raise UnequalIterablesError() + yield combo + + +# from more_itertools +def _zip_equal(*iterables): + # Check whether the iterables are all the same size. + try: + first_size = len(iterables[0]) + for i, it in enumerate(iterables[1:], 1): + size = len(it) + if size != first_size: + raise UnequalIterablesError(details=(first_size, i, size)) + # All sizes are equal, we can use the built-in zip. + return zip(*iterables) + # If any one of the iterables didn't have a length, start reading + # them until one runs out. + except TypeError: + return _zip_equal_generator(iterables) + + +zip_strict = ( + _zip_equal if sys.version_info < (3, 10) else functools.partial(zip, strict=True) +) diff --git a/venv/Lib/site-packages/setuptools/_distutils/spawn.py b/venv/Lib/site-packages/setuptools/_distutils/spawn.py new file mode 100644 index 0000000..046b5bb --- /dev/null +++ b/venv/Lib/site-packages/setuptools/_distutils/spawn.py @@ -0,0 +1,105 @@ +"""distutils.spawn + +Provides the 'spawn()' function, a front-end to various platform- +specific functions for launching another program in a sub-process. +Also provides the 'find_executable()' to search the path for a given +executable name. +""" + +import os +import subprocess +import sys + +from ._log import log +from .debug import DEBUG +from .errors import DistutilsExecError + + +def spawn(cmd, search_path=1, verbose=0, dry_run=0, env=None): # noqa: C901 + """Run another program, specified as a command list 'cmd', in a new process. + + 'cmd' is just the argument list for the new process, ie. + cmd[0] is the program to run and cmd[1:] are the rest of its arguments. + There is no way to run a program with a name different from that of its + executable. + + If 'search_path' is true (the default), the system's executable + search path will be used to find the program; otherwise, cmd[0] + must be the exact path to the executable. If 'dry_run' is true, + the command will not actually be run. + + Raise DistutilsExecError if running the program fails in any way; just + return on success. + """ + # cmd is documented as a list, but just in case some code passes a tuple + # in, protect our %-formatting code against horrible death + cmd = list(cmd) + + log.info(subprocess.list2cmdline(cmd)) + if dry_run: + return + + if search_path: + executable = find_executable(cmd[0]) + if executable is not None: + cmd[0] = executable + + env = env if env is not None else dict(os.environ) + + if sys.platform == 'darwin': + from distutils.util import MACOSX_VERSION_VAR, get_macosx_target_ver + + macosx_target_ver = get_macosx_target_ver() + if macosx_target_ver: + env[MACOSX_VERSION_VAR] = macosx_target_ver + + try: + proc = subprocess.Popen(cmd, env=env) + proc.wait() + exitcode = proc.returncode + except OSError as exc: + if not DEBUG: + cmd = cmd[0] + raise DistutilsExecError(f"command {cmd!r} failed: {exc.args[-1]}") from exc + + if exitcode: + if not DEBUG: + cmd = cmd[0] + raise DistutilsExecError(f"command {cmd!r} failed with exit code {exitcode}") + + +def find_executable(executable, path=None): + """Tries to find 'executable' in the directories listed in 'path'. + + A string listing directories separated by 'os.pathsep'; defaults to + os.environ['PATH']. Returns the complete filename or None if not found. + """ + _, ext = os.path.splitext(executable) + if (sys.platform == 'win32') and (ext != '.exe'): + executable = executable + '.exe' + + if os.path.isfile(executable): + return executable + + if path is None: + path = os.environ.get('PATH', None) + if path is None: + try: + path = os.confstr("CS_PATH") + except (AttributeError, ValueError): + # os.confstr() or CS_PATH is not available + path = os.defpath + # bpo-35755: Don't use os.defpath if the PATH environment variable is + # set to an empty string + + # PATH='' doesn't match, whereas PATH=':' looks in the current directory + if not path: + return None + + paths = path.split(os.pathsep) + for p in paths: + f = os.path.join(p, executable) + if os.path.isfile(f): + # the file exists, we have a shot at spawn working + return f + return None diff --git a/venv/Lib/site-packages/setuptools/_distutils/sysconfig.py b/venv/Lib/site-packages/setuptools/_distutils/sysconfig.py new file mode 100644 index 0000000..1a38e9f --- /dev/null +++ b/venv/Lib/site-packages/setuptools/_distutils/sysconfig.py @@ -0,0 +1,555 @@ +"""Provide access to Python's configuration information. The specific +configuration variables available depend heavily on the platform and +configuration. The values may be retrieved using +get_config_var(name), and the list of variables is available via +get_config_vars().keys(). Additional convenience functions are also +available. + +Written by: Fred L. Drake, Jr. +Email: +""" + +import functools +import os +import pathlib +import re +import sys +import sysconfig + +from . import py39compat +from ._functools import pass_none +from .errors import DistutilsPlatformError + +IS_PYPY = '__pypy__' in sys.builtin_module_names + +# These are needed in a couple of spots, so just compute them once. +PREFIX = os.path.normpath(sys.prefix) +EXEC_PREFIX = os.path.normpath(sys.exec_prefix) +BASE_PREFIX = os.path.normpath(sys.base_prefix) +BASE_EXEC_PREFIX = os.path.normpath(sys.base_exec_prefix) + +# Path to the base directory of the project. On Windows the binary may +# live in project/PCbuild/win32 or project/PCbuild/amd64. +# set for cross builds +if "_PYTHON_PROJECT_BASE" in os.environ: + project_base = os.path.abspath(os.environ["_PYTHON_PROJECT_BASE"]) +else: + if sys.executable: + project_base = os.path.dirname(os.path.abspath(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 = os.getcwd() + + +def _is_python_source_dir(d): + """ + Return True if the target directory appears to point to an + un-installed Python. + """ + modules = pathlib.Path(d).joinpath('Modules') + return any(modules.joinpath(fn).is_file() for fn in ('Setup', 'Setup.local')) + + +_sys_home = getattr(sys, '_home', None) + + +def _is_parent(dir_a, dir_b): + """ + Return True if a is a parent of b. + """ + return os.path.normcase(dir_a).startswith(os.path.normcase(dir_b)) + + +if os.name == 'nt': + + @pass_none + def _fix_pcbuild(d): + # In a venv, sys._home will be inside BASE_PREFIX rather than PREFIX. + prefixes = PREFIX, BASE_PREFIX + matched = ( + prefix + for prefix in prefixes + if _is_parent(d, os.path.join(prefix, "PCbuild")) + ) + return next(matched, d) + + project_base = _fix_pcbuild(project_base) + _sys_home = _fix_pcbuild(_sys_home) + + +def _python_build(): + if _sys_home: + return _is_python_source_dir(_sys_home) + return _is_python_source_dir(project_base) + + +python_build = _python_build() + + +# Calculate the build qualifier flags if they are defined. Adding the flags +# to the include and lib directories only makes sense for an installation, not +# an in-source build. +build_flags = '' +try: + if not python_build: + build_flags = sys.abiflags +except AttributeError: + # It's not a configure-based build, so the sys module doesn't have + # this attribute, which is fine. + pass + + +def get_python_version(): + """Return a string containing the major and minor Python version, + leaving off the patchlevel. Sample return values could be '1.5' + or '2.2'. + """ + return '%d.%d' % sys.version_info[:2] + + +def get_python_inc(plat_specific=0, prefix=None): + """Return the directory containing installed Python header files. + + If 'plat_specific' is false (the default), this is the path to the + non-platform-specific header files, i.e. Python.h and so on; + otherwise, this is the path to platform-specific header files + (namely pyconfig.h). + + If 'prefix' is supplied, use it instead of sys.base_prefix or + sys.base_exec_prefix -- i.e., ignore 'plat_specific'. + """ + default_prefix = BASE_EXEC_PREFIX if plat_specific else BASE_PREFIX + resolved_prefix = prefix if prefix is not None else default_prefix + try: + getter = globals()[f'_get_python_inc_{os.name}'] + except KeyError: + raise DistutilsPlatformError( + "I don't know where Python installs its C header files " + "on platform '%s'" % os.name + ) + return getter(resolved_prefix, prefix, plat_specific) + + +@pass_none +def _extant(path): + """ + Replace path with None if it doesn't exist. + """ + return path if os.path.exists(path) else None + + +def _get_python_inc_posix(prefix, spec_prefix, plat_specific): + if IS_PYPY and sys.version_info < (3, 8): + return os.path.join(prefix, 'include') + return ( + _get_python_inc_posix_python(plat_specific) + or _extant(_get_python_inc_from_config(plat_specific, spec_prefix)) + or _get_python_inc_posix_prefix(prefix) + ) + + +def _get_python_inc_posix_python(plat_specific): + """ + Assume the executable is in the build directory. The + pyconfig.h file should be in the same directory. Since + the build directory may not be the source directory, + use "srcdir" from the makefile to find the "Include" + directory. + """ + if not python_build: + return + if plat_specific: + return _sys_home or project_base + incdir = os.path.join(get_config_var('srcdir'), 'Include') + return os.path.normpath(incdir) + + +def _get_python_inc_from_config(plat_specific, spec_prefix): + """ + If no prefix was explicitly specified, provide the include + directory from the config vars. Useful when + cross-compiling, since the config vars may come from + the host + platform Python installation, while the current Python + executable is from the build platform installation. + + >>> monkeypatch = getfixture('monkeypatch') + >>> gpifc = _get_python_inc_from_config + >>> monkeypatch.setitem(gpifc.__globals__, 'get_config_var', str.lower) + >>> gpifc(False, '/usr/bin/') + >>> gpifc(False, '') + >>> gpifc(False, None) + 'includepy' + >>> gpifc(True, None) + 'confincludepy' + """ + if spec_prefix is None: + return get_config_var('CONF' * plat_specific + 'INCLUDEPY') + + +def _get_python_inc_posix_prefix(prefix): + implementation = 'pypy' if IS_PYPY else 'python' + python_dir = implementation + get_python_version() + build_flags + return os.path.join(prefix, "include", python_dir) + + +def _get_python_inc_nt(prefix, spec_prefix, plat_specific): + if python_build: + # Include both include dirs to ensure we can find pyconfig.h + return ( + os.path.join(prefix, "include") + + os.path.pathsep + + os.path.dirname(sysconfig.get_config_h_filename()) + ) + return os.path.join(prefix, "include") + + +# allow this behavior to be monkey-patched. Ref pypa/distutils#2. +def _posix_lib(standard_lib, libpython, early_prefix, prefix): + if standard_lib: + return libpython + else: + return os.path.join(libpython, "site-packages") + + +def get_python_lib(plat_specific=0, standard_lib=0, prefix=None): + """Return the directory containing the Python library (standard or + site additions). + + If 'plat_specific' is true, return the directory containing + platform-specific modules, i.e. any module from a non-pure-Python + module distribution; otherwise, return the platform-shared library + directory. If 'standard_lib' is true, return the directory + containing standard Python library modules; otherwise, return the + directory for site-specific modules. + + If 'prefix' is supplied, use it instead of sys.base_prefix or + sys.base_exec_prefix -- i.e., ignore 'plat_specific'. + """ + + if IS_PYPY and sys.version_info < (3, 8): + # PyPy-specific schema + if prefix is None: + prefix = PREFIX + if standard_lib: + return os.path.join(prefix, "lib-python", sys.version[0]) + return os.path.join(prefix, 'site-packages') + + early_prefix = prefix + + if prefix is None: + if standard_lib: + prefix = plat_specific and BASE_EXEC_PREFIX or BASE_PREFIX + else: + prefix = plat_specific and EXEC_PREFIX or PREFIX + + if os.name == "posix": + if plat_specific or standard_lib: + # Platform-specific modules (any module from a non-pure-Python + # module distribution) or standard Python library modules. + libdir = getattr(sys, "platlibdir", "lib") + else: + # Pure Python + libdir = "lib" + implementation = 'pypy' if IS_PYPY else 'python' + libpython = os.path.join(prefix, libdir, implementation + get_python_version()) + return _posix_lib(standard_lib, libpython, early_prefix, prefix) + elif os.name == "nt": + if standard_lib: + return os.path.join(prefix, "Lib") + else: + return os.path.join(prefix, "Lib", "site-packages") + else: + raise DistutilsPlatformError( + "I don't know where Python installs its library " + "on platform '%s'" % os.name + ) + + +@functools.lru_cache +def _customize_macos(): + """ + Perform first-time customization of compiler-related + config vars on macOS. Use after a compiler is known + to be needed. This customization exists primarily to support Pythons + from binary installers. The kind and paths to build tools on + the user system may vary significantly from the system + that Python itself was built on. Also the user OS + version and build tools may not support the same set + of CPU architectures for universal builds. + """ + + sys.platform == "darwin" and __import__('_osx_support').customize_compiler( + get_config_vars() + ) + + +def customize_compiler(compiler): # noqa: C901 + """Do any platform-specific customization of a CCompiler instance. + + Mainly needed on Unix, so we can plug in the information that + varies across Unices and is stored in Python's Makefile. + """ + if compiler.compiler_type == "unix": + _customize_macos() + + ( + cc, + cxx, + cflags, + ccshared, + ldshared, + shlib_suffix, + ar, + ar_flags, + ) = get_config_vars( + 'CC', + 'CXX', + 'CFLAGS', + 'CCSHARED', + 'LDSHARED', + 'SHLIB_SUFFIX', + 'AR', + 'ARFLAGS', + ) + + if 'CC' in os.environ: + newcc = os.environ['CC'] + if 'LDSHARED' not in os.environ and ldshared.startswith(cc): + # If CC is overridden, use that as the default + # command for LDSHARED as well + ldshared = newcc + ldshared[len(cc) :] + cc = newcc + if 'CXX' in os.environ: + cxx = os.environ['CXX'] + if 'LDSHARED' in os.environ: + ldshared = os.environ['LDSHARED'] + if 'CPP' in os.environ: + cpp = os.environ['CPP'] + else: + cpp = cc + " -E" # not always + if 'LDFLAGS' in os.environ: + ldshared = ldshared + ' ' + os.environ['LDFLAGS'] + if 'CFLAGS' in os.environ: + cflags = cflags + ' ' + os.environ['CFLAGS'] + ldshared = ldshared + ' ' + os.environ['CFLAGS'] + if 'CPPFLAGS' in os.environ: + cpp = cpp + ' ' + os.environ['CPPFLAGS'] + cflags = cflags + ' ' + os.environ['CPPFLAGS'] + ldshared = ldshared + ' ' + os.environ['CPPFLAGS'] + if 'AR' in os.environ: + ar = os.environ['AR'] + if 'ARFLAGS' in os.environ: + archiver = ar + ' ' + os.environ['ARFLAGS'] + else: + archiver = ar + ' ' + ar_flags + + cc_cmd = cc + ' ' + cflags + compiler.set_executables( + preprocessor=cpp, + compiler=cc_cmd, + compiler_so=cc_cmd + ' ' + ccshared, + compiler_cxx=cxx, + linker_so=ldshared, + linker_exe=cc, + archiver=archiver, + ) + + if 'RANLIB' in os.environ and compiler.executables.get('ranlib', None): + compiler.set_executables(ranlib=os.environ['RANLIB']) + + compiler.shared_lib_extension = shlib_suffix + + +def get_config_h_filename(): + """Return full pathname of installed pyconfig.h file.""" + return sysconfig.get_config_h_filename() + + +def get_makefile_filename(): + """Return full pathname of installed Makefile from the Python build.""" + return sysconfig.get_makefile_filename() + + +def parse_config_h(fp, g=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. + """ + return sysconfig.parse_config_h(fp, vars=g) + + +# 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_]*)}") + + +def parse_makefile(fn, g=None): # noqa: C901 + """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. + """ + from distutils.text_file import TextFile + + fp = TextFile( + fn, strip_comments=1, skip_blanks=1, join_lines=1, errors="surrogateescape" + ) + + if g is None: + g = {} + done = {} + notdone = {} + + while True: + line = fp.readline() + if line is None: # eof + break + 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 + + # 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') + + # do variable interpolation here + while notdone: + for name in list(notdone): + value = notdone[name] + m = _findvar1_rx.search(value) or _findvar2_rx.search(value) + if m: + 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 + del notdone[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; just drop it since we can't deal + del notdone[name] + + fp.close() + + # strip spurious spaces + for k, v in done.items(): + if isinstance(v, str): + done[k] = v.strip() + + # save the results in the global dictionary + g.update(done) + return g + + +def expand_makefile_vars(s, vars): + """Expand Makefile-style variables -- "${foo}" or "$(foo)" -- in + 'string' according to 'vars' (a dictionary mapping variable names to + values). Variables not present in 'vars' are silently expanded to the + empty string. The variable values in 'vars' should not contain further + variable expansions; if 'vars' is the output of 'parse_makefile()', + you're fine. Returns a variable-expanded version of 's'. + """ + + # This algorithm does multiple expansion, so if vars['foo'] contains + # "${bar}", it will expand ${foo} to ${bar}, and then expand + # ${bar}... and so forth. This is fine as long as 'vars' comes from + # 'parse_makefile()', which takes care of such expansions eagerly, + # according to make's variable expansion semantics. + + while True: + m = _findvar1_rx.search(s) or _findvar2_rx.search(s) + if m: + (beg, end) = m.span() + s = s[0:beg] + vars.get(m.group(1)) + s[end:] + else: + break + return s + + +_config_vars = None + + +def get_config_vars(*args): + """With no arguments, return a dictionary of all configuration + variables relevant for the current platform. Generally this includes + everything needed to build extensions and install both pure modules and + extensions. On Unix, this means every variable defined in Python's + installed Makefile; on Windows 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 = sysconfig.get_config_vars().copy() + py39compat.add_ext_suffix(_config_vars) + + return [_config_vars.get(name) for name in args] if args else _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) + """ + if name == 'SO': + import warnings + + warnings.warn('SO is deprecated, use EXT_SUFFIX', DeprecationWarning, 2) + return get_config_vars().get(name) diff --git a/venv/Lib/site-packages/setuptools/_distutils/text_file.py b/venv/Lib/site-packages/setuptools/_distutils/text_file.py new file mode 100644 index 0000000..0f846e3 --- /dev/null +++ b/venv/Lib/site-packages/setuptools/_distutils/text_file.py @@ -0,0 +1,286 @@ +"""text_file + +provides the TextFile class, which gives an interface to text files +that (optionally) takes care of stripping comments, ignoring blank +lines, and joining lines with backslashes.""" + +import sys + + +class TextFile: + """Provides a file-like object that takes care of all the things you + commonly want to do when processing a text file that has some + line-by-line syntax: strip comments (as long as "#" is your + comment character), skip blank lines, join adjacent lines by + escaping the newline (ie. backslash at end of line), strip + leading and/or trailing whitespace. All of these are optional + and independently controllable. + + Provides a 'warn()' method so you can generate warning messages that + report physical line number, even if the logical line in question + spans multiple physical lines. Also provides 'unreadline()' for + implementing line-at-a-time lookahead. + + Constructor is called as: + + TextFile (filename=None, file=None, **options) + + It bombs (RuntimeError) if both 'filename' and 'file' are None; + 'filename' should be a string, and 'file' a file object (or + something that provides 'readline()' and 'close()' methods). It is + recommended that you supply at least 'filename', so that TextFile + can include it in warning messages. If 'file' is not supplied, + TextFile creates its own using 'io.open()'. + + The options are all boolean, and affect the value returned by + 'readline()': + strip_comments [default: true] + strip from "#" to end-of-line, as well as any whitespace + leading up to the "#" -- unless it is escaped by a backslash + lstrip_ws [default: false] + strip leading whitespace from each line before returning it + rstrip_ws [default: true] + strip trailing whitespace (including line terminator!) from + each line before returning it + skip_blanks [default: true} + skip lines that are empty *after* stripping comments and + whitespace. (If both lstrip_ws and rstrip_ws are false, + then some lines may consist of solely whitespace: these will + *not* be skipped, even if 'skip_blanks' is true.) + join_lines [default: false] + if a backslash is the last non-newline character on a line + after stripping comments and whitespace, join the following line + to it to form one "logical line"; if N consecutive lines end + with a backslash, then N+1 physical lines will be joined to + form one logical line. + collapse_join [default: false] + strip leading whitespace from lines that are joined to their + predecessor; only matters if (join_lines and not lstrip_ws) + errors [default: 'strict'] + error handler used to decode the file content + + Note that since 'rstrip_ws' can strip the trailing newline, the + semantics of 'readline()' must differ from those of the builtin file + object's 'readline()' method! In particular, 'readline()' returns + None for end-of-file: an empty string might just be a blank line (or + an all-whitespace line), if 'rstrip_ws' is true but 'skip_blanks' is + not.""" + + default_options = { + 'strip_comments': 1, + 'skip_blanks': 1, + 'lstrip_ws': 0, + 'rstrip_ws': 1, + 'join_lines': 0, + 'collapse_join': 0, + 'errors': 'strict', + } + + def __init__(self, filename=None, file=None, **options): + """Construct a new TextFile object. At least one of 'filename' + (a string) and 'file' (a file-like object) must be supplied. + They keyword argument options are described above and affect + the values returned by 'readline()'.""" + if filename is None and file is None: + raise RuntimeError( + "you must supply either or both of 'filename' and 'file'" + ) + + # set values for all options -- either from client option hash + # or fallback to default_options + for opt in self.default_options.keys(): + if opt in options: + setattr(self, opt, options[opt]) + else: + setattr(self, opt, self.default_options[opt]) + + # sanity check client option hash + for opt in options.keys(): + if opt not in self.default_options: + raise KeyError("invalid TextFile option '%s'" % opt) + + if file is None: + self.open(filename) + else: + self.filename = filename + self.file = file + self.current_line = 0 # assuming that file is at BOF! + + # 'linebuf' is a stack of lines that will be emptied before we + # actually read from the file; it's only populated by an + # 'unreadline()' operation + self.linebuf = [] + + def open(self, filename): + """Open a new file named 'filename'. This overrides both the + 'filename' and 'file' arguments to the constructor.""" + self.filename = filename + self.file = open(self.filename, errors=self.errors, encoding='utf-8') + self.current_line = 0 + + def close(self): + """Close the current file and forget everything we know about it + (filename, current line number).""" + file = self.file + self.file = None + self.filename = None + self.current_line = None + file.close() + + def gen_error(self, msg, line=None): + outmsg = [] + if line is None: + line = self.current_line + outmsg.append(self.filename + ", ") + if isinstance(line, (list, tuple)): + outmsg.append("lines %d-%d: " % tuple(line)) + else: + outmsg.append("line %d: " % line) + outmsg.append(str(msg)) + return "".join(outmsg) + + def error(self, msg, line=None): + raise ValueError("error: " + self.gen_error(msg, line)) + + def warn(self, msg, line=None): + """Print (to stderr) a warning message tied to the current logical + line in the current file. If the current logical line in the + file spans multiple physical lines, the warning refers to the + whole range, eg. "lines 3-5". If 'line' supplied, it overrides + the current line number; it may be a list or tuple to indicate a + range of physical lines, or an integer for a single physical + line.""" + sys.stderr.write("warning: " + self.gen_error(msg, line) + "\n") + + def readline(self): # noqa: C901 + """Read and return a single logical line from the current file (or + from an internal buffer if lines have previously been "unread" + with 'unreadline()'). If the 'join_lines' option is true, this + may involve reading multiple physical lines concatenated into a + single string. Updates the current line number, so calling + 'warn()' after 'readline()' emits a warning about the physical + line(s) just read. Returns None on end-of-file, since the empty + string can occur if 'rstrip_ws' is true but 'strip_blanks' is + not.""" + # If any "unread" lines waiting in 'linebuf', return the top + # one. (We don't actually buffer read-ahead data -- lines only + # get put in 'linebuf' if the client explicitly does an + # 'unreadline()'. + if self.linebuf: + line = self.linebuf[-1] + del self.linebuf[-1] + return line + + buildup_line = '' + + while True: + # read the line, make it None if EOF + line = self.file.readline() + if line == '': + line = None + + if self.strip_comments and line: + # Look for the first "#" in the line. If none, never + # mind. If we find one and it's the first character, or + # is not preceded by "\", then it starts a comment -- + # strip the comment, strip whitespace before it, and + # carry on. Otherwise, it's just an escaped "#", so + # unescape it (and any other escaped "#"'s that might be + # lurking in there) and otherwise leave the line alone. + + pos = line.find("#") + if pos == -1: # no "#" -- no comments + pass + + # It's definitely a comment -- either "#" is the first + # character, or it's elsewhere and unescaped. + elif pos == 0 or line[pos - 1] != "\\": + # Have to preserve the trailing newline, because it's + # the job of a later step (rstrip_ws) to remove it -- + # and if rstrip_ws is false, we'd better preserve it! + # (NB. this means that if the final line is all comment + # and has no trailing newline, we will think that it's + # EOF; I think that's OK.) + eol = (line[-1] == '\n') and '\n' or '' + line = line[0:pos] + eol + + # If all that's left is whitespace, then skip line + # *now*, before we try to join it to 'buildup_line' -- + # that way constructs like + # hello \\ + # # comment that should be ignored + # there + # result in "hello there". + if line.strip() == "": + continue + else: # it's an escaped "#" + line = line.replace("\\#", "#") + + # did previous line end with a backslash? then accumulate + if self.join_lines and buildup_line: + # oops: end of file + if line is None: + self.warn("continuation line immediately precedes end-of-file") + return buildup_line + + if self.collapse_join: + line = line.lstrip() + line = buildup_line + line + + # careful: pay attention to line number when incrementing it + if isinstance(self.current_line, list): + self.current_line[1] = self.current_line[1] + 1 + else: + self.current_line = [self.current_line, self.current_line + 1] + # just an ordinary line, read it as usual + else: + if line is None: # eof + return None + + # still have to be careful about incrementing the line number! + if isinstance(self.current_line, list): + self.current_line = self.current_line[1] + 1 + else: + self.current_line = self.current_line + 1 + + # strip whitespace however the client wants (leading and + # trailing, or one or the other, or neither) + if self.lstrip_ws and self.rstrip_ws: + line = line.strip() + elif self.lstrip_ws: + line = line.lstrip() + elif self.rstrip_ws: + line = line.rstrip() + + # blank line (whether we rstrip'ed or not)? skip to next line + # if appropriate + if line in ('', '\n') and self.skip_blanks: + continue + + if self.join_lines: + if line[-1] == '\\': + buildup_line = line[:-1] + continue + + if line[-2:] == '\\\n': + buildup_line = line[0:-2] + '\n' + continue + + # well, I guess there's some actual content there: return it + return line + + def readlines(self): + """Read and return the list of all logical lines remaining in the + current file.""" + lines = [] + while True: + line = self.readline() + if line is None: + return lines + lines.append(line) + + def unreadline(self, line): + """Push 'line' (a string) onto an internal buffer that will be + checked by future 'readline()' calls. Handy for implementing + a parser with line-at-a-time lookahead.""" + self.linebuf.append(line) diff --git a/venv/Lib/site-packages/setuptools/_distutils/unixccompiler.py b/venv/Lib/site-packages/setuptools/_distutils/unixccompiler.py new file mode 100644 index 0000000..0248bde --- /dev/null +++ b/venv/Lib/site-packages/setuptools/_distutils/unixccompiler.py @@ -0,0 +1,400 @@ +"""distutils.unixccompiler + +Contains the UnixCCompiler class, a subclass of CCompiler that handles +the "typical" Unix-style command-line C compiler: + * macros defined with -Dname[=value] + * macros undefined with -Uname + * include search directories specified with -Idir + * libraries specified with -lllib + * library search directories specified with -Ldir + * compile handled by 'cc' (or similar) executable with -c option: + compiles .c to .o + * link static library handled by 'ar' command (possibly with 'ranlib') + * link shared library handled by 'cc -shared' +""" + +from __future__ import annotations + +import itertools +import os +import re +import shlex +import sys + +from . import sysconfig +from .compat import consolidate_linker_args +from ._log import log +from ._macos_compat import compiler_fixup +from ._modified import newer +from .ccompiler import CCompiler, gen_lib_options, gen_preprocess_options +from .errors import CompileError, DistutilsExecError, LibError, LinkError + +# XXX Things not currently handled: +# * optimization/debug/warning flags; we just use whatever's in Python's +# Makefile and live with it. Is this adequate? If not, we might +# have to have a bunch of subclasses GNUCCompiler, SGICCompiler, +# SunCCompiler, and I suspect down that road lies madness. +# * even if we don't know a warning flag from an optimization flag, +# we need some way for outsiders to feed preprocessor/compiler/linker +# flags in to us -- eg. a sysadmin might want to mandate certain flags +# via a site config file, or a user might want to set something for +# compiling this module distribution only via the setup.py command +# line, whatever. As long as these options come from something on the +# current system, they can be as system-dependent as they like, and we +# should just happily stuff them into the preprocessor/compiler/linker +# options and carry on. + + +def _split_env(cmd): + """ + For macOS, split command into 'env' portion (if any) + and the rest of the linker command. + + >>> _split_env(['a', 'b', 'c']) + ([], ['a', 'b', 'c']) + >>> _split_env(['/usr/bin/env', 'A=3', 'gcc']) + (['/usr/bin/env', 'A=3'], ['gcc']) + """ + pivot = 0 + if os.path.basename(cmd[0]) == "env": + pivot = 1 + while '=' in cmd[pivot]: + pivot += 1 + return cmd[:pivot], cmd[pivot:] + + +def _split_aix(cmd): + """ + AIX platforms prefix the compiler with the ld_so_aix + script, so split that from the linker command. + + >>> _split_aix(['a', 'b', 'c']) + ([], ['a', 'b', 'c']) + >>> _split_aix(['/bin/foo/ld_so_aix', 'gcc']) + (['/bin/foo/ld_so_aix'], ['gcc']) + """ + pivot = os.path.basename(cmd[0]) == 'ld_so_aix' + return cmd[:pivot], cmd[pivot:] + + +def _linker_params(linker_cmd, compiler_cmd): + """ + The linker command usually begins with the compiler + command (possibly multiple elements), followed by zero or more + params for shared library building. + + If the LDSHARED env variable overrides the linker command, + however, the commands may not match. + + Return the best guess of the linker parameters by stripping + the linker command. If the compiler command does not + match the linker command, assume the linker command is + just the first element. + + >>> _linker_params('gcc foo bar'.split(), ['gcc']) + ['foo', 'bar'] + >>> _linker_params('gcc foo bar'.split(), ['other']) + ['foo', 'bar'] + >>> _linker_params('ccache gcc foo bar'.split(), 'ccache gcc'.split()) + ['foo', 'bar'] + >>> _linker_params(['gcc'], ['gcc']) + [] + """ + c_len = len(compiler_cmd) + pivot = c_len if linker_cmd[:c_len] == compiler_cmd else 1 + return linker_cmd[pivot:] + + +class UnixCCompiler(CCompiler): + compiler_type = 'unix' + + # These are used by CCompiler in two places: the constructor sets + # instance attributes 'preprocessor', 'compiler', etc. from them, and + # 'set_executable()' allows any of these to be set. The defaults here + # are pretty generic; they will probably have to be set by an outsider + # (eg. using information discovered by the sysconfig about building + # Python extensions). + executables = { + 'preprocessor': None, + 'compiler': ["cc"], + 'compiler_so': ["cc"], + 'compiler_cxx': ["cc"], + 'linker_so': ["cc", "-shared"], + 'linker_exe': ["cc"], + 'archiver': ["ar", "-cr"], + 'ranlib': None, + } + + if sys.platform[:6] == "darwin": + executables['ranlib'] = ["ranlib"] + + # Needed for the filename generation methods provided by the base + # class, CCompiler. NB. whoever instantiates/uses a particular + # UnixCCompiler instance should set 'shared_lib_ext' -- we set a + # reasonable common default here, but it's not necessarily used on all + # Unices! + + src_extensions = [".c", ".C", ".cc", ".cxx", ".cpp", ".m"] + obj_extension = ".o" + static_lib_extension = ".a" + shared_lib_extension = ".so" + dylib_lib_extension = ".dylib" + xcode_stub_lib_extension = ".tbd" + static_lib_format = shared_lib_format = dylib_lib_format = "lib%s%s" + xcode_stub_lib_format = dylib_lib_format + if sys.platform == "cygwin": + exe_extension = ".exe" + + def preprocess( + self, + source, + output_file=None, + macros=None, + include_dirs=None, + extra_preargs=None, + extra_postargs=None, + ): + fixed_args = self._fix_compile_args(None, macros, include_dirs) + ignore, macros, include_dirs = fixed_args + pp_opts = gen_preprocess_options(macros, include_dirs) + pp_args = self.preprocessor + pp_opts + if output_file: + pp_args.extend(['-o', output_file]) + if extra_preargs: + pp_args[:0] = extra_preargs + if extra_postargs: + pp_args.extend(extra_postargs) + pp_args.append(source) + + # reasons to preprocess: + # - force is indicated + # - output is directed to stdout + # - source file is newer than the target + preprocess = self.force or output_file is None or newer(source, output_file) + if not preprocess: + return + + if output_file: + self.mkpath(os.path.dirname(output_file)) + + try: + self.spawn(pp_args) + except DistutilsExecError as msg: + raise CompileError(msg) + + def _compile(self, obj, src, ext, cc_args, extra_postargs, pp_opts): + compiler_so = compiler_fixup(self.compiler_so, cc_args + extra_postargs) + try: + self.spawn(compiler_so + cc_args + [src, '-o', obj] + extra_postargs) + except DistutilsExecError as msg: + raise CompileError(msg) + + def create_static_lib( + self, objects, output_libname, output_dir=None, debug=0, target_lang=None + ): + objects, output_dir = self._fix_object_args(objects, output_dir) + + output_filename = self.library_filename(output_libname, output_dir=output_dir) + + if self._need_link(objects, output_filename): + self.mkpath(os.path.dirname(output_filename)) + self.spawn(self.archiver + [output_filename] + objects + self.objects) + + # Not many Unices required ranlib anymore -- SunOS 4.x is, I + # think the only major Unix that does. Maybe we need some + # platform intelligence here to skip ranlib if it's not + # needed -- or maybe Python's configure script took care of + # it for us, hence the check for leading colon. + if self.ranlib: + try: + self.spawn(self.ranlib + [output_filename]) + except DistutilsExecError as msg: + raise LibError(msg) + else: + log.debug("skipping %s (up-to-date)", output_filename) + + def link( + self, + target_desc, + objects, + output_filename, + 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, + ): + objects, output_dir = self._fix_object_args(objects, output_dir) + fixed_args = self._fix_lib_args(libraries, library_dirs, runtime_library_dirs) + libraries, library_dirs, runtime_library_dirs = fixed_args + + lib_opts = gen_lib_options(self, library_dirs, runtime_library_dirs, libraries) + if not isinstance(output_dir, (str, type(None))): + raise TypeError("'output_dir' must be a string or None") + if output_dir is not None: + output_filename = os.path.join(output_dir, output_filename) + + if self._need_link(objects, output_filename): + ld_args = objects + self.objects + lib_opts + ['-o', output_filename] + if debug: + ld_args[:0] = ['-g'] + if extra_preargs: + ld_args[:0] = extra_preargs + if extra_postargs: + ld_args.extend(extra_postargs) + self.mkpath(os.path.dirname(output_filename)) + try: + # Select a linker based on context: linker_exe when + # building an executable or linker_so (with shared options) + # when building a shared library. + building_exe = target_desc == CCompiler.EXECUTABLE + linker = (self.linker_exe if building_exe else self.linker_so)[:] + + if target_lang == "c++" and self.compiler_cxx: + env, linker_ne = _split_env(linker) + aix, linker_na = _split_aix(linker_ne) + _, compiler_cxx_ne = _split_env(self.compiler_cxx) + _, linker_exe_ne = _split_env(self.linker_exe) + + params = _linker_params(linker_na, linker_exe_ne) + linker = env + aix + compiler_cxx_ne + params + + linker = compiler_fixup(linker, ld_args) + + self.spawn(linker + ld_args) + except DistutilsExecError as msg: + raise LinkError(msg) + else: + log.debug("skipping %s (up-to-date)", output_filename) + + # -- Miscellaneous methods ----------------------------------------- + # These are all used by the 'gen_lib_options() function, in + # ccompiler.py. + + def library_dir_option(self, dir): + return "-L" + dir + + def _is_gcc(self): + cc_var = sysconfig.get_config_var("CC") + compiler = os.path.basename(shlex.split(cc_var)[0]) + return "gcc" in compiler or "g++" in compiler + + def runtime_library_dir_option(self, dir: str) -> str | list[str]: + # XXX Hackish, at the very least. See Python bug #445902: + # https://bugs.python.org/issue445902 + # Linkers on different platforms need different options to + # specify that directories need to be added to the list of + # directories searched for dependencies when a dynamic library + # is sought. GCC on GNU systems (Linux, FreeBSD, ...) has to + # be told to pass the -R option through to the linker, whereas + # other compilers and gcc on other systems just know this. + # Other compilers may need something slightly different. At + # this time, there's no way to determine this information from + # the configuration data stored in the Python installation, so + # we use this hack. + if sys.platform[:6] == "darwin": + from distutils.util import get_macosx_target_ver, split_version + + macosx_target_ver = get_macosx_target_ver() + if macosx_target_ver and split_version(macosx_target_ver) >= [10, 5]: + return "-Wl,-rpath," + dir + else: # no support for -rpath on earlier macOS versions + return "-L" + dir + elif sys.platform[:7] == "freebsd": + return "-Wl,-rpath=" + dir + elif sys.platform[:5] == "hp-ux": + return [ + "-Wl,+s" if self._is_gcc() else "+s", + "-L" + dir, + ] + + # For all compilers, `-Wl` is the presumed way to pass a + # compiler option to the linker + if sysconfig.get_config_var("GNULD") == "yes": + return consolidate_linker_args([ + # Force RUNPATH instead of RPATH + "-Wl,--enable-new-dtags", + "-Wl,-rpath," + dir, + ]) + else: + return "-Wl,-R" + dir + + def library_option(self, lib): + return "-l" + lib + + @staticmethod + def _library_root(dir): + """ + macOS users can specify an alternate SDK using'-isysroot'. + Calculate the SDK root if it is specified. + + Note that, as of Xcode 7, Apple SDKs may contain textual stub + libraries with .tbd extensions rather than the normal .dylib + shared libraries installed in /. The Apple compiler tool + chain handles this transparently but it can cause problems + for programs that are being built with an SDK and searching + for specific libraries. Callers of find_library_file need to + keep in mind that the base filename of the returned SDK library + file might have a different extension from that of the library + file installed on the running system, for example: + /Applications/Xcode.app/Contents/Developer/Platforms/ + MacOSX.platform/Developer/SDKs/MacOSX10.11.sdk/ + usr/lib/libedit.tbd + vs + /usr/lib/libedit.dylib + """ + cflags = sysconfig.get_config_var('CFLAGS') + match = re.search(r'-isysroot\s*(\S+)', cflags) + + apply_root = ( + sys.platform == 'darwin' + and match + and ( + dir.startswith('/System/') + or (dir.startswith('/usr/') and not dir.startswith('/usr/local/')) + ) + ) + + return os.path.join(match.group(1), dir[1:]) if apply_root else dir + + def find_library_file(self, dirs, lib, debug=0): + r""" + Second-guess the linker with not much hard + data to go on: GCC seems to prefer the shared library, so + assume that *all* Unix C compilers do, + ignoring even GCC's "-static" option. + + >>> compiler = UnixCCompiler() + >>> compiler._library_root = lambda dir: dir + >>> monkeypatch = getfixture('monkeypatch') + >>> monkeypatch.setattr(os.path, 'exists', lambda d: 'existing' in d) + >>> dirs = ('/foo/bar/missing', '/foo/bar/existing') + >>> compiler.find_library_file(dirs, 'abc').replace('\\', '/') + '/foo/bar/existing/libabc.dylib' + >>> compiler.find_library_file(reversed(dirs), 'abc').replace('\\', '/') + '/foo/bar/existing/libabc.dylib' + >>> monkeypatch.setattr(os.path, 'exists', + ... lambda d: 'existing' in d and '.a' in d) + >>> compiler.find_library_file(dirs, 'abc').replace('\\', '/') + '/foo/bar/existing/libabc.a' + >>> compiler.find_library_file(reversed(dirs), 'abc').replace('\\', '/') + '/foo/bar/existing/libabc.a' + """ + lib_names = ( + self.library_filename(lib, lib_type=type) + for type in 'dylib xcode_stub shared static'.split() + ) + + roots = map(self._library_root, dirs) + + searched = itertools.starmap(os.path.join, itertools.product(roots, lib_names)) + + found = filter(os.path.exists, searched) + + # Return None if it could not be found in any dir. + return next(found, None) diff --git a/venv/Lib/site-packages/setuptools/_distutils/util.py b/venv/Lib/site-packages/setuptools/_distutils/util.py new file mode 100644 index 0000000..9ee7772 --- /dev/null +++ b/venv/Lib/site-packages/setuptools/_distutils/util.py @@ -0,0 +1,510 @@ +"""distutils.util + +Miscellaneous utility functions -- anything that doesn't fit into +one of the other *util.py modules. +""" + +import functools +import importlib.util +import os +import re +import string +import subprocess +import sys +import sysconfig + +from ._log import log +from ._modified import newer +from .errors import DistutilsByteCompileError, DistutilsPlatformError +from .spawn import spawn + + +def get_host_platform(): + """ + Return a string that identifies the current platform. Use this + function to distinguish platform-specific build directories and + platform-specific built distributions. + """ + + # This function initially exposed platforms as defined in Python 3.9 + # even with older Python versions when distutils was split out. + # Now it delegates to stdlib sysconfig, but maintains compatibility. + + if sys.version_info < (3, 9): + if os.name == "posix" and hasattr(os, 'uname'): + osname, host, release, version, machine = os.uname() + if osname[:3] == "aix": + from .py38compat import aix_platform + + return aix_platform(osname, version, release) + + return sysconfig.get_platform() + + +def get_platform(): + if os.name == 'nt': + TARGET_TO_PLAT = { + 'x86': 'win32', + 'x64': 'win-amd64', + 'arm': 'win-arm32', + 'arm64': 'win-arm64', + } + target = os.environ.get('VSCMD_ARG_TGT_ARCH') + return TARGET_TO_PLAT.get(target) or get_host_platform() + return get_host_platform() + + +if sys.platform == 'darwin': + _syscfg_macosx_ver = None # cache the version pulled from sysconfig +MACOSX_VERSION_VAR = 'MACOSX_DEPLOYMENT_TARGET' + + +def _clear_cached_macosx_ver(): + """For testing only. Do not call.""" + global _syscfg_macosx_ver + _syscfg_macosx_ver = None + + +def get_macosx_target_ver_from_syscfg(): + """Get the version of macOS latched in the Python interpreter configuration. + Returns the version as a string or None if can't obtain one. Cached.""" + global _syscfg_macosx_ver + if _syscfg_macosx_ver is None: + from distutils import sysconfig + + ver = sysconfig.get_config_var(MACOSX_VERSION_VAR) or '' + if ver: + _syscfg_macosx_ver = ver + return _syscfg_macosx_ver + + +def get_macosx_target_ver(): + """Return the version of macOS for which we are building. + + The target version defaults to the version in sysconfig latched at time + the Python interpreter was built, unless overridden by an environment + variable. If neither source has a value, then None is returned""" + + syscfg_ver = get_macosx_target_ver_from_syscfg() + env_ver = os.environ.get(MACOSX_VERSION_VAR) + + if env_ver: + # Validate overridden version against sysconfig version, if have both. + # Ensure that the deployment target of the build process is not less + # than 10.3 if the interpreter was built for 10.3 or later. This + # ensures extension modules are built with correct compatibility + # values, specifically LDSHARED which can use + # '-undefined dynamic_lookup' which only works on >= 10.3. + if ( + syscfg_ver + and split_version(syscfg_ver) >= [10, 3] + and split_version(env_ver) < [10, 3] + ): + my_msg = ( + '$' + MACOSX_VERSION_VAR + ' mismatch: ' + f'now "{env_ver}" but "{syscfg_ver}" during configure; ' + 'must use 10.3 or later' + ) + raise DistutilsPlatformError(my_msg) + return env_ver + return syscfg_ver + + +def split_version(s): + """Convert a dot-separated string into a list of numbers for comparisons""" + return [int(n) for n in s.split('.')] + + +def convert_path(pathname): + """Return 'pathname' as a name that will work on the native filesystem, + i.e. split it on '/' and put it 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 '.' in paths: + paths.remove('.') + if not paths: + return os.curdir + return os.path.join(*paths) + + +# convert_path () + + +def change_root(new_root, pathname): + """Return 'pathname' with 'new_root' prepended. If 'pathname' is + relative, this is equivalent to "os.path.join(new_root,pathname)". + Otherwise, it requires making 'pathname' relative and then joining the + two, which is tricky on DOS/Windows and Mac OS. + """ + if os.name == 'posix': + if not os.path.isabs(pathname): + return os.path.join(new_root, pathname) + else: + return os.path.join(new_root, pathname[1:]) + + elif os.name == 'nt': + (drive, path) = os.path.splitdrive(pathname) + if path[0] == '\\': + path = path[1:] + return os.path.join(new_root, path) + + raise DistutilsPlatformError(f"nothing known about platform '{os.name}'") + + +@functools.lru_cache +def check_environ(): + """Ensure that 'os.environ' has all the environment variables we + guarantee that users can use in config files, command-line options, + etc. Currently this includes: + HOME - user's home directory (Unix only) + PLAT - description of the current platform, including hardware + and OS (see 'get_platform()') + """ + if os.name == 'posix' and 'HOME' not in os.environ: + try: + import pwd + + os.environ['HOME'] = pwd.getpwuid(os.getuid())[5] + except (ImportError, KeyError): + # bpo-10496: if the current user identifier doesn't exist in the + # password database, do nothing + pass + + if 'PLAT' not in os.environ: + os.environ['PLAT'] = get_platform() + + +def subst_vars(s, local_vars): + """ + Perform variable substitution on 'string'. + Variables are indicated by format-style braces ("{var}"). + Variable is substituted by the value found in the 'local_vars' + dictionary or in 'os.environ' if it's not in 'local_vars'. + 'os.environ' is first checked/augmented to guarantee that it contains + certain values: see 'check_environ()'. Raise ValueError for any + variables not found in either 'local_vars' or 'os.environ'. + """ + check_environ() + lookup = dict(os.environ) + lookup.update((name, str(value)) for name, value in local_vars.items()) + try: + return _subst_compat(s).format_map(lookup) + except KeyError as var: + raise ValueError(f"invalid variable {var}") + + +def _subst_compat(s): + """ + Replace shell/Perl-style variable substitution with + format-style. For compatibility. + """ + + def _subst(match): + return f'{{{match.group(1)}}}' + + repl = re.sub(r'\$([a-zA-Z_][a-zA-Z_0-9]*)', _subst, s) + if repl != s: + import warnings + + warnings.warn( + "shell/Perl-style substitutions are deprecated", + DeprecationWarning, + ) + return repl + + +def grok_environment_error(exc, prefix="error: "): + # Function kept for backward compatibility. + # Used to try clever things with EnvironmentErrors, + # but nowadays str(exception) produces good messages. + return prefix + str(exc) + + +# Needed by 'split_quoted()' +_wordchars_re = _squote_re = _dquote_re = None + + +def _init_regex(): + global _wordchars_re, _squote_re, _dquote_re + _wordchars_re = re.compile(r'[^\\\'\"%s ]*' % string.whitespace) + _squote_re = re.compile(r"'(?:[^'\\]|\\.)*'") + _dquote_re = re.compile(r'"(?:[^"\\]|\\.)*"') + + +def split_quoted(s): + """Split a string up according to Unix shell-like rules for quotes and + backslashes. In short: words are delimited by spaces, as long as those + spaces are not escaped by a backslash, or inside a quoted string. + Single and double quotes are equivalent, and the quote characters can + be backslash-escaped. The backslash is stripped from any two-character + escape sequence, leaving only the escaped character. The quote + characters are stripped from any quoted string. Returns a list of + words. + """ + + # This is a nice algorithm for splitting up a single string, since it + # doesn't require character-by-character examination. It was a little + # bit of a brain-bender to get it working right, though... + if _wordchars_re is None: + _init_regex() + + s = s.strip() + words = [] + pos = 0 + + while s: + m = _wordchars_re.match(s, pos) + end = m.end() + if end == len(s): + words.append(s[:end]) + break + + if s[end] in string.whitespace: + # unescaped, unquoted whitespace: now + # we definitely have a word delimiter + words.append(s[:end]) + s = s[end:].lstrip() + pos = 0 + + elif s[end] == '\\': + # preserve whatever is being escaped; + # will become part of the current word + s = s[:end] + s[end + 1 :] + pos = end + 1 + + else: + if s[end] == "'": # slurp singly-quoted string + m = _squote_re.match(s, end) + elif s[end] == '"': # slurp doubly-quoted string + m = _dquote_re.match(s, end) + else: + raise RuntimeError("this can't happen (bad char '%c')" % s[end]) + + if m is None: + raise ValueError("bad string (mismatched %s quotes?)" % s[end]) + + (beg, end) = m.span() + s = s[:beg] + s[beg + 1 : end - 1] + s[end:] + pos = m.end() - 2 + + if pos >= len(s): + words.append(s) + break + + return words + + +# split_quoted () + + +def execute(func, args, msg=None, verbose=0, dry_run=0): + """Perform some action that affects the outside world (eg. by + writing to the filesystem). Such actions are special because they + are disabled by the 'dry_run' flag. This method takes care of all + that bureaucracy for you; all you have to do is supply the + function to call and an argument tuple for it (to embody the + "external action" being performed), and an optional message to + print. + """ + if msg is None: + msg = f"{func.__name__}{args!r}" + if msg[-2:] == ',)': # correct for singleton tuple + msg = msg[0:-2] + ')' + + log.info(msg) + if not dry_run: + func(*args) + + +def strtobool(val): + """Convert a string representation of truth to true (1) or false (0). + + True values are 'y', 'yes', 't', 'true', 'on', and '1'; false values + are 'n', 'no', 'f', 'false', 'off', and '0'. Raises ValueError if + 'val' is anything else. + """ + val = val.lower() + if val in ('y', 'yes', 't', 'true', 'on', '1'): + return 1 + elif val in ('n', 'no', 'f', 'false', 'off', '0'): + return 0 + else: + raise ValueError(f"invalid truth value {val!r}") + + +def byte_compile( # noqa: C901 + py_files, + optimize=0, + force=0, + prefix=None, + base_dir=None, + verbose=1, + dry_run=0, + direct=None, +): + """Byte-compile a collection of Python source files to .pyc + files in a __pycache__ subdirectory. 'py_files' is a list + of files to compile; any files that don't end in ".py" are silently + skipped. 'optimize' must be one of the following: + 0 - don't optimize + 1 - normal optimization (like "python -O") + 2 - extra optimization (like "python -OO") + If 'force' is true, all files are recompiled regardless of + timestamps. + + The source filename encoded in each bytecode file defaults to the + filenames listed in 'py_files'; you can modify these with 'prefix' and + 'basedir'. 'prefix' is a string that will be stripped off of each + source filename, and 'base_dir' is a directory name that will be + prepended (after 'prefix' is stripped). You can supply either or both + (or neither) of 'prefix' and 'base_dir', as you wish. + + If 'dry_run' is true, doesn't actually do anything that would + affect the filesystem. + + Byte-compilation is either done directly in this interpreter process + with the standard py_compile module, or indirectly by writing a + temporary script and executing it. Normally, you should let + 'byte_compile()' figure out to use direct compilation or not (see + the source for details). The 'direct' flag is used by the script + generated in indirect mode; unless you know what you're doing, leave + it set to None. + """ + + # nothing is done if sys.dont_write_bytecode is True + if sys.dont_write_bytecode: + raise DistutilsByteCompileError('byte-compiling is disabled.') + + # First, if the caller didn't force us into direct or indirect mode, + # figure out which mode we should be in. We take a conservative + # approach: choose direct mode *only* if the current interpreter is + # in debug mode and optimize is 0. If we're not in debug mode (-O + # or -OO), we don't know which level of optimization this + # interpreter is running with, so we can't do direct + # byte-compilation and be certain that it's the right thing. Thus, + # always compile indirectly if the current interpreter is in either + # optimize mode, or if either optimization level was requested by + # the caller. + if direct is None: + direct = __debug__ and optimize == 0 + + # "Indirect" byte-compilation: write a temporary script and then + # run it with the appropriate flags. + if not direct: + try: + from tempfile import mkstemp + + (script_fd, script_name) = mkstemp(".py") + except ImportError: + from tempfile import mktemp + + (script_fd, script_name) = None, mktemp(".py") + log.info("writing byte-compilation script '%s'", script_name) + if not dry_run: + if script_fd is not None: + script = os.fdopen(script_fd, "w", encoding='utf-8') + else: # pragma: no cover + script = open(script_name, "w", encoding='utf-8') + + with script: + script.write( + """\ +from distutils.util import byte_compile +files = [ +""" + ) + + # XXX would be nice to write absolute filenames, just for + # safety's sake (script should be more robust in the face of + # chdir'ing before running it). But this requires abspath'ing + # 'prefix' as well, and that breaks the hack in build_lib's + # 'byte_compile()' method that carefully tacks on a trailing + # slash (os.sep really) to make sure the prefix here is "just + # right". This whole prefix business is rather delicate -- the + # problem is that it's really a directory, but I'm treating it + # as a dumb string, so trailing slashes and so forth matter. + + script.write(",\n".join(map(repr, py_files)) + "]\n") + script.write( + f""" +byte_compile(files, optimize={optimize!r}, force={force!r}, + prefix={prefix!r}, base_dir={base_dir!r}, + verbose={verbose!r}, dry_run=0, + direct=1) +""" + ) + + cmd = [sys.executable] + cmd.extend(subprocess._optim_args_from_interpreter_flags()) + cmd.append(script_name) + spawn(cmd, dry_run=dry_run) + execute(os.remove, (script_name,), "removing %s" % script_name, dry_run=dry_run) + + # "Direct" byte-compilation: use the py_compile module to compile + # right here, right now. Note that the script generated in indirect + # mode simply calls 'byte_compile()' in direct mode, a weird sort of + # cross-process recursion. Hey, it works! + else: + from py_compile import compile + + for file in py_files: + if file[-3:] != ".py": + # This lets us be lazy and not filter filenames in + # the "install_lib" command. + continue + + # Terminology from the py_compile module: + # cfile - byte-compiled file + # dfile - purported source filename (same as 'file' by default) + if optimize >= 0: + opt = '' if optimize == 0 else optimize + cfile = importlib.util.cache_from_source(file, optimization=opt) + else: + cfile = importlib.util.cache_from_source(file) + dfile = file + if prefix: + if file[: len(prefix)] != prefix: + raise ValueError( + f"invalid prefix: filename {file!r} doesn't start with {prefix!r}" + ) + dfile = dfile[len(prefix) :] + if base_dir: + dfile = os.path.join(base_dir, dfile) + + cfile_base = os.path.basename(cfile) + if direct: + if force or newer(file, cfile): + log.info("byte-compiling %s to %s", file, cfile_base) + if not dry_run: + compile(file, cfile, dfile) + else: + log.debug("skipping byte-compilation of %s to %s", file, cfile_base) + + +def rfc822_escape(header): + """Return a version of the string escaped for inclusion in an + RFC-822 header, by ensuring there are 8 spaces space after each newline. + """ + indent = 8 * " " + lines = header.splitlines(keepends=True) + + # Emulate the behaviour of `str.split` + # (the terminal line break in `splitlines` does not result in an extra line): + ends_in_newline = lines and lines[-1].splitlines()[0] != lines[-1] + suffix = indent if ends_in_newline else "" + + return indent.join(lines) + suffix diff --git a/venv/Lib/site-packages/setuptools/_distutils/version.py b/venv/Lib/site-packages/setuptools/_distutils/version.py new file mode 100644 index 0000000..806d233 --- /dev/null +++ b/venv/Lib/site-packages/setuptools/_distutils/version.py @@ -0,0 +1,349 @@ +# +# distutils/version.py +# +# Implements multiple version numbering conventions for the +# Python Module Distribution Utilities. +# +# $Id$ +# + +"""Provides classes to represent module version numbers (one class for +each style of version numbering). There are currently two such classes +implemented: StrictVersion and LooseVersion. + +Every version number class implements the following interface: + * the 'parse' method takes a string and parses it to some internal + representation; if the string is an invalid version number, + 'parse' raises a ValueError exception + * the class constructor takes an optional string argument which, + if supplied, is passed to 'parse' + * __str__ reconstructs the string that was passed to 'parse' (or + an equivalent string -- ie. one that will generate an equivalent + version number instance) + * __repr__ generates Python code to recreate the version number instance + * _cmp compares the current instance with either another instance + of the same class or a string (which will be parsed to an instance + of the same class, thus must follow the same rules) +""" + +import contextlib +import re +import warnings + + +@contextlib.contextmanager +def suppress_known_deprecation(): + with warnings.catch_warnings(record=True) as ctx: + warnings.filterwarnings( + action='default', + category=DeprecationWarning, + message="distutils Version classes are deprecated.", + ) + yield ctx + + +class Version: + """Abstract base class for version numbering classes. Just provides + constructor (__init__) and reproducer (__repr__), because those + seem to be the same for all version numbering classes; and route + rich comparisons to _cmp. + """ + + def __init__(self, vstring=None): + if vstring: + self.parse(vstring) + warnings.warn( + "distutils Version classes are deprecated. " + "Use packaging.version instead.", + DeprecationWarning, + stacklevel=2, + ) + + def __repr__(self): + return f"{self.__class__.__name__} ('{str(self)}')" + + def __eq__(self, other): + c = self._cmp(other) + if c is NotImplemented: + return c + return c == 0 + + def __lt__(self, other): + c = self._cmp(other) + if c is NotImplemented: + return c + return c < 0 + + def __le__(self, other): + c = self._cmp(other) + if c is NotImplemented: + return c + return c <= 0 + + def __gt__(self, other): + c = self._cmp(other) + if c is NotImplemented: + return c + return c > 0 + + def __ge__(self, other): + c = self._cmp(other) + if c is NotImplemented: + return c + return c >= 0 + + +# Interface for version-number classes -- must be implemented +# by the following classes (the concrete ones -- Version should +# be treated as an abstract class). +# __init__ (string) - create and take same action as 'parse' +# (string parameter is optional) +# parse (string) - convert a string representation to whatever +# internal representation is appropriate for +# this style of version numbering +# __str__ (self) - convert back to a string; should be very similar +# (if not identical to) the string supplied to parse +# __repr__ (self) - generate Python code to recreate +# the instance +# _cmp (self, other) - compare two version numbers ('other' may +# be an unparsed version string, or another +# instance of your version class) + + +class StrictVersion(Version): + """Version numbering for anal retentives and software idealists. + Implements the standard interface for version number classes as + described above. A version number consists of two or three + dot-separated numeric components, with an optional "pre-release" tag + on the end. The pre-release tag consists of the letter 'a' or 'b' + followed by a number. If the numeric components of two version + numbers are equal, then one with a pre-release tag will always + be deemed earlier (lesser) than one without. + + The following are valid version numbers (shown in the order that + would be obtained by sorting according to the supplied cmp function): + + 0.4 0.4.0 (these two are equivalent) + 0.4.1 + 0.5a1 + 0.5b3 + 0.5 + 0.9.6 + 1.0 + 1.0.4a3 + 1.0.4b1 + 1.0.4 + + The following are examples of invalid version numbers: + + 1 + 2.7.2.2 + 1.3.a4 + 1.3pl1 + 1.3c4 + + The rationale for this version numbering system will be explained + in the distutils documentation. + """ + + version_re = re.compile( + r'^(\d+) \. (\d+) (\. (\d+))? ([ab](\d+))?$', re.VERBOSE | re.ASCII + ) + + def parse(self, vstring): + match = self.version_re.match(vstring) + if not match: + raise ValueError("invalid version number '%s'" % vstring) + + (major, minor, patch, prerelease, prerelease_num) = match.group(1, 2, 4, 5, 6) + + if patch: + self.version = tuple(map(int, [major, minor, patch])) + else: + self.version = tuple(map(int, [major, minor])) + (0,) + + if prerelease: + self.prerelease = (prerelease[0], int(prerelease_num)) + else: + self.prerelease = None + + def __str__(self): + if self.version[2] == 0: + vstring = '.'.join(map(str, self.version[0:2])) + else: + vstring = '.'.join(map(str, self.version)) + + if self.prerelease: + vstring = vstring + self.prerelease[0] + str(self.prerelease[1]) + + return vstring + + def _cmp(self, other): + if isinstance(other, str): + with suppress_known_deprecation(): + other = StrictVersion(other) + elif not isinstance(other, StrictVersion): + return NotImplemented + + if self.version == other.version: + # versions match; pre-release drives the comparison + return self._cmp_prerelease(other) + + return -1 if self.version < other.version else 1 + + def _cmp_prerelease(self, other): + """ + case 1: self has prerelease, other doesn't; other is greater + case 2: self doesn't have prerelease, other does: self is greater + case 3: both or neither have prerelease: compare them! + """ + if self.prerelease and not other.prerelease: + return -1 + elif not self.prerelease and other.prerelease: + return 1 + + if self.prerelease == other.prerelease: + return 0 + elif self.prerelease < other.prerelease: + return -1 + else: + return 1 + + +# end class StrictVersion + + +# The rules according to Greg Stein: +# 1) a version number has 1 or more numbers separated by a period or by +# sequences of letters. If only periods, then these are compared +# left-to-right to determine an ordering. +# 2) sequences of letters are part of the tuple for comparison and are +# compared lexicographically +# 3) recognize the numeric components may have leading zeroes +# +# The LooseVersion class below implements these rules: a version number +# string is split up into a tuple of integer and string components, and +# comparison is a simple tuple comparison. This means that version +# numbers behave in a predictable and obvious way, but a way that might +# not necessarily be how people *want* version numbers to behave. There +# wouldn't be a problem if people could stick to purely numeric version +# numbers: just split on period and compare the numbers as tuples. +# However, people insist on putting letters into their version numbers; +# the most common purpose seems to be: +# - indicating a "pre-release" version +# ('alpha', 'beta', 'a', 'b', 'pre', 'p') +# - indicating a post-release patch ('p', 'pl', 'patch') +# but of course this can't cover all version number schemes, and there's +# no way to know what a programmer means without asking him. +# +# The problem is what to do with letters (and other non-numeric +# characters) in a version number. The current implementation does the +# obvious and predictable thing: keep them as strings and compare +# lexically within a tuple comparison. This has the desired effect if +# an appended letter sequence implies something "post-release": +# eg. "0.99" < "0.99pl14" < "1.0", and "5.001" < "5.001m" < "5.002". +# +# However, if letters in a version number imply a pre-release version, +# the "obvious" thing isn't correct. Eg. you would expect that +# "1.5.1" < "1.5.2a2" < "1.5.2", but under the tuple/lexical comparison +# implemented here, this just isn't so. +# +# Two possible solutions come to mind. The first is to tie the +# comparison algorithm to a particular set of semantic rules, as has +# been done in the StrictVersion class above. This works great as long +# as everyone can go along with bondage and discipline. Hopefully a +# (large) subset of Python module programmers will agree that the +# particular flavour of bondage and discipline provided by StrictVersion +# provides enough benefit to be worth using, and will submit their +# version numbering scheme to its domination. The free-thinking +# anarchists in the lot will never give in, though, and something needs +# to be done to accommodate them. +# +# Perhaps a "moderately strict" version class could be implemented that +# lets almost anything slide (syntactically), and makes some heuristic +# assumptions about non-digits in version number strings. This could +# sink into special-case-hell, though; if I was as talented and +# idiosyncratic as Larry Wall, I'd go ahead and implement a class that +# somehow knows that "1.2.1" < "1.2.2a2" < "1.2.2" < "1.2.2pl3", and is +# just as happy dealing with things like "2g6" and "1.13++". I don't +# think I'm smart enough to do it right though. +# +# In any case, I've coded the test suite for this module (see +# ../test/test_version.py) specifically to fail on things like comparing +# "1.2a2" and "1.2". That's not because the *code* is doing anything +# wrong, it's because the simple, obvious design doesn't match my +# complicated, hairy expectations for real-world version numbers. It +# would be a snap to fix the test suite to say, "Yep, LooseVersion does +# the Right Thing" (ie. the code matches the conception). But I'd rather +# have a conception that matches common notions about version numbers. + + +class LooseVersion(Version): + """Version numbering for anarchists and software realists. + Implements the standard interface for version number classes as + described above. A version number consists of a series of numbers, + separated by either periods or strings of letters. When comparing + version numbers, the numeric components will be compared + numerically, and the alphabetic components lexically. The following + are all valid version numbers, in no particular order: + + 1.5.1 + 1.5.2b2 + 161 + 3.10a + 8.02 + 3.4j + 1996.07.12 + 3.2.pl0 + 3.1.1.6 + 2g6 + 11g + 0.960923 + 2.2beta29 + 1.13++ + 5.5.kw + 2.0b1pl0 + + In fact, there is no such thing as an invalid version number under + this scheme; the rules for comparison are simple and predictable, + but may not always give the results you want (for some definition + of "want"). + """ + + component_re = re.compile(r'(\d+ | [a-z]+ | \.)', re.VERBOSE) + + def parse(self, vstring): + # I've given up on thinking I can reconstruct the version string + # from the parsed tuple -- so I just store the string here for + # use by __str__ + self.vstring = vstring + components = [x for x in self.component_re.split(vstring) if x and x != '.'] + for i, obj in enumerate(components): + try: + components[i] = int(obj) + except ValueError: + pass + + self.version = components + + def __str__(self): + return self.vstring + + def __repr__(self): + return "LooseVersion ('%s')" % str(self) + + def _cmp(self, other): + if isinstance(other, str): + other = LooseVersion(other) + elif not isinstance(other, LooseVersion): + return NotImplemented + + if self.version == other.version: + return 0 + if self.version < other.version: + return -1 + if self.version > other.version: + return 1 + + +# end class LooseVersion diff --git a/venv/Lib/site-packages/setuptools/_distutils/versionpredicate.py b/venv/Lib/site-packages/setuptools/_distutils/versionpredicate.py new file mode 100644 index 0000000..31c4201 --- /dev/null +++ b/venv/Lib/site-packages/setuptools/_distutils/versionpredicate.py @@ -0,0 +1,175 @@ +"""Module for parsing and testing package version predicate strings.""" + +import operator +import re + +from . import version + +re_validPackage = re.compile(r"(?i)^\s*([a-z_]\w*(?:\.[a-z_]\w*)*)(.*)", re.ASCII) +# (package) (rest) + +re_paren = re.compile(r"^\s*\((.*)\)\s*$") # (list) inside of parentheses +re_splitComparison = re.compile(r"^\s*(<=|>=|<|>|!=|==)\s*([^\s,]+)\s*$") +# (comp) (version) + + +def splitUp(pred): + """Parse a single version comparison. + + Return (comparison string, StrictVersion) + """ + res = re_splitComparison.match(pred) + if not res: + raise ValueError("bad package restriction syntax: %r" % pred) + comp, verStr = res.groups() + with version.suppress_known_deprecation(): + other = version.StrictVersion(verStr) + return (comp, other) + + +compmap = { + "<": operator.lt, + "<=": operator.le, + "==": operator.eq, + ">": operator.gt, + ">=": operator.ge, + "!=": operator.ne, +} + + +class VersionPredicate: + """Parse and test package version predicates. + + >>> v = VersionPredicate('pyepat.abc (>1.0, <3333.3a1, !=1555.1b3)') + + The `name` attribute provides the full dotted name that is given:: + + >>> v.name + 'pyepat.abc' + + The str() of a `VersionPredicate` provides a normalized + human-readable version of the expression:: + + >>> print(v) + pyepat.abc (> 1.0, < 3333.3a1, != 1555.1b3) + + The `satisfied_by()` method can be used to determine with a given + version number is included in the set described by the version + restrictions:: + + >>> v.satisfied_by('1.1') + True + >>> v.satisfied_by('1.4') + True + >>> v.satisfied_by('1.0') + False + >>> v.satisfied_by('4444.4') + False + >>> v.satisfied_by('1555.1b3') + False + + `VersionPredicate` is flexible in accepting extra whitespace:: + + >>> v = VersionPredicate(' pat( == 0.1 ) ') + >>> v.name + 'pat' + >>> v.satisfied_by('0.1') + True + >>> v.satisfied_by('0.2') + False + + If any version numbers passed in do not conform to the + restrictions of `StrictVersion`, a `ValueError` is raised:: + + >>> v = VersionPredicate('p1.p2.p3.p4(>=1.0, <=1.3a1, !=1.2zb3)') + Traceback (most recent call last): + ... + ValueError: invalid version number '1.2zb3' + + It the module or package name given does not conform to what's + allowed as a legal module or package name, `ValueError` is + raised:: + + >>> v = VersionPredicate('foo-bar') + Traceback (most recent call last): + ... + ValueError: expected parenthesized list: '-bar' + + >>> v = VersionPredicate('foo bar (12.21)') + Traceback (most recent call last): + ... + ValueError: expected parenthesized list: 'bar (12.21)' + + """ + + def __init__(self, versionPredicateStr): + """Parse a version predicate string.""" + # Fields: + # name: package name + # pred: list of (comparison string, StrictVersion) + + versionPredicateStr = versionPredicateStr.strip() + if not versionPredicateStr: + raise ValueError("empty package restriction") + match = re_validPackage.match(versionPredicateStr) + if not match: + raise ValueError("bad package name in %r" % versionPredicateStr) + self.name, paren = match.groups() + paren = paren.strip() + if paren: + match = re_paren.match(paren) + if not match: + raise ValueError("expected parenthesized list: %r" % paren) + str = match.groups()[0] + self.pred = [splitUp(aPred) for aPred in str.split(",")] + if not self.pred: + raise ValueError("empty parenthesized list in %r" % versionPredicateStr) + else: + self.pred = [] + + def __str__(self): + if self.pred: + seq = [cond + " " + str(ver) for cond, ver in self.pred] + return self.name + " (" + ", ".join(seq) + ")" + else: + return self.name + + def satisfied_by(self, version): + """True if version is compatible with all the predicates in self. + The parameter version must be acceptable to the StrictVersion + constructor. It may be either a string or StrictVersion. + """ + for cond, ver in self.pred: + if not compmap[cond](version, ver): + return False + return True + + +_provision_rx = None + + +def split_provision(value): + """Return the name and optional version number of a provision. + + The version number, if given, will be returned as a `StrictVersion` + instance, otherwise it will be `None`. + + >>> split_provision('mypkg') + ('mypkg', None) + >>> split_provision(' mypkg( 1.2 ) ') + ('mypkg', StrictVersion ('1.2')) + """ + global _provision_rx + if _provision_rx is None: + _provision_rx = re.compile( + r"([a-zA-Z_]\w*(?:\.[a-zA-Z_]\w*)*)(?:\s*\(\s*([^)\s]+)\s*\))?$", re.ASCII + ) + value = value.strip() + m = _provision_rx.match(value) + if not m: + raise ValueError("illegal provides specification: %r" % value) + ver = m.group(2) or None + if ver: + with version.suppress_known_deprecation(): + ver = version.StrictVersion(ver) + return m.group(1), ver diff --git a/venv/Lib/site-packages/setuptools/_distutils/zosccompiler.py b/venv/Lib/site-packages/setuptools/_distutils/zosccompiler.py new file mode 100644 index 0000000..c7a7ca6 --- /dev/null +++ b/venv/Lib/site-packages/setuptools/_distutils/zosccompiler.py @@ -0,0 +1,229 @@ +"""distutils.zosccompiler + +Contains the selection of the c & c++ compilers on z/OS. There are several +different c compilers on z/OS, all of them are optional, so the correct +one needs to be chosen based on the users input. This is compatible with +the following compilers: + +IBM C/C++ For Open Enterprise Languages on z/OS 2.0 +IBM Open XL C/C++ 1.1 for z/OS +IBM XL C/C++ V2.4.1 for z/OS 2.4 and 2.5 +IBM z/OS XL C/C++ +""" + +import os + +from . import sysconfig +from .errors import CompileError, DistutilsExecError +from .unixccompiler import UnixCCompiler + +_cc_args = { + 'ibm-openxl': [ + '-m64', + '-fvisibility=default', + '-fzos-le-char-mode=ascii', + '-fno-short-enums', + ], + 'ibm-xlclang': [ + '-q64', + '-qexportall', + '-qascii', + '-qstrict', + '-qnocsect', + '-Wa,asa,goff', + '-Wa,xplink', + '-qgonumber', + '-qenum=int', + '-Wc,DLL', + ], + 'ibm-xlc': [ + '-q64', + '-qexportall', + '-qascii', + '-qstrict', + '-qnocsect', + '-Wa,asa,goff', + '-Wa,xplink', + '-qgonumber', + '-qenum=int', + '-Wc,DLL', + '-qlanglvl=extc99', + ], +} + +_cxx_args = { + 'ibm-openxl': [ + '-m64', + '-fvisibility=default', + '-fzos-le-char-mode=ascii', + '-fno-short-enums', + ], + 'ibm-xlclang': [ + '-q64', + '-qexportall', + '-qascii', + '-qstrict', + '-qnocsect', + '-Wa,asa,goff', + '-Wa,xplink', + '-qgonumber', + '-qenum=int', + '-Wc,DLL', + ], + 'ibm-xlc': [ + '-q64', + '-qexportall', + '-qascii', + '-qstrict', + '-qnocsect', + '-Wa,asa,goff', + '-Wa,xplink', + '-qgonumber', + '-qenum=int', + '-Wc,DLL', + '-qlanglvl=extended0x', + ], +} + +_asm_args = { + 'ibm-openxl': ['-fasm', '-fno-integrated-as', '-Wa,--ASA', '-Wa,--GOFF'], + 'ibm-xlclang': [], + 'ibm-xlc': [], +} + +_ld_args = { + 'ibm-openxl': [], + 'ibm-xlclang': ['-Wl,dll', '-q64'], + 'ibm-xlc': ['-Wl,dll', '-q64'], +} + + +# Python on z/OS is built with no compiler specific options in it's CFLAGS. +# But each compiler requires it's own specific options to build successfully, +# though some of the options are common between them +class zOSCCompiler(UnixCCompiler): + src_extensions = ['.c', '.C', '.cc', '.cxx', '.cpp', '.m', '.s'] + _cpp_extensions = ['.cc', '.cpp', '.cxx', '.C'] + _asm_extensions = ['.s'] + + def _get_zos_compiler_name(self): + zos_compiler_names = [ + os.path.basename(binary) + for envvar in ('CC', 'CXX', 'LDSHARED') + if (binary := os.environ.get(envvar, None)) + ] + if len(zos_compiler_names) == 0: + return 'ibm-openxl' + + zos_compilers = {} + for compiler in ( + 'ibm-clang', + 'ibm-clang64', + 'ibm-clang++', + 'ibm-clang++64', + 'clang', + 'clang++', + 'clang-14', + ): + zos_compilers[compiler] = 'ibm-openxl' + + for compiler in ('xlclang', 'xlclang++', 'njsc', 'njsc++'): + zos_compilers[compiler] = 'ibm-xlclang' + + for compiler in ('xlc', 'xlC', 'xlc++'): + zos_compilers[compiler] = 'ibm-xlc' + + return zos_compilers.get(zos_compiler_names[0], 'ibm-openxl') + + def __init__(self, verbose=0, dry_run=0, force=0): + super().__init__(verbose, dry_run, force) + self.zos_compiler = self._get_zos_compiler_name() + sysconfig.customize_compiler(self) + + def _compile(self, obj, src, ext, cc_args, extra_postargs, pp_opts): + local_args = [] + if ext in self._cpp_extensions: + compiler = self.compiler_cxx + local_args.extend(_cxx_args[self.zos_compiler]) + elif ext in self._asm_extensions: + compiler = self.compiler_so + local_args.extend(_cc_args[self.zos_compiler]) + local_args.extend(_asm_args[self.zos_compiler]) + else: + compiler = self.compiler_so + local_args.extend(_cc_args[self.zos_compiler]) + local_args.extend(cc_args) + + try: + self.spawn(compiler + local_args + [src, '-o', obj] + extra_postargs) + except DistutilsExecError as msg: + raise CompileError(msg) + + def runtime_library_dir_option(self, dir): + return '-L' + dir + + def link( + self, + target_desc, + objects, + output_filename, + 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, + ): + # For a built module to use functions from cpython, it needs to use Pythons + # side deck file. The side deck is located beside the libpython3.xx.so + ldversion = sysconfig.get_config_var('LDVERSION') + if sysconfig.python_build: + side_deck_path = os.path.join( + sysconfig.get_config_var('abs_builddir'), + f'libpython{ldversion}.x', + ) + else: + side_deck_path = os.path.join( + sysconfig.get_config_var('installed_base'), + sysconfig.get_config_var('platlibdir'), + f'libpython{ldversion}.x', + ) + + if os.path.exists(side_deck_path): + if extra_postargs: + extra_postargs.append(side_deck_path) + else: + extra_postargs = [side_deck_path] + + # Check and replace libraries included side deck files + if runtime_library_dirs: + for dir in runtime_library_dirs: + for library in libraries[:]: + library_side_deck = os.path.join(dir, f'{library}.x') + if os.path.exists(library_side_deck): + libraries.remove(library) + extra_postargs.append(library_side_deck) + break + + # Any required ld args for the given compiler + extra_postargs.extend(_ld_args[self.zos_compiler]) + + super().link( + target_desc, + objects, + output_filename, + output_dir, + libraries, + library_dirs, + runtime_library_dirs, + export_symbols, + debug, + extra_preargs, + extra_postargs, + build_temp, + target_lang, + ) diff --git a/venv/Lib/site-packages/setuptools/_entry_points.py b/venv/Lib/site-packages/setuptools/_entry_points.py new file mode 100644 index 0000000..747a690 --- /dev/null +++ b/venv/Lib/site-packages/setuptools/_entry_points.py @@ -0,0 +1,88 @@ +import functools +import operator +import itertools + +from .errors import OptionError +from .extern.jaraco.text import yield_lines +from .extern.jaraco.functools import pass_none +from ._importlib import metadata +from ._itertools import ensure_unique +from .extern.more_itertools import consume + + +def ensure_valid(ep): + """ + Exercise one of the dynamic properties to trigger + the pattern match. + """ + try: + ep.extras + except AttributeError as ex: + msg = ( + f"Problems to parse {ep}.\nPlease ensure entry-point follows the spec: " + "https://packaging.python.org/en/latest/specifications/entry-points/" + ) + raise OptionError(msg) from ex + + +def load_group(value, group): + """ + Given a value of an entry point or series of entry points, + return each as an EntryPoint. + """ + # normalize to a single sequence of lines + lines = yield_lines(value) + text = f'[{group}]\n' + '\n'.join(lines) + return metadata.EntryPoints._from_text(text) + + +def by_group_and_name(ep): + return ep.group, ep.name + + +def validate(eps: metadata.EntryPoints): + """ + Ensure entry points are unique by group and name and validate each. + """ + consume(map(ensure_valid, ensure_unique(eps, key=by_group_and_name))) + return eps + + +@functools.singledispatch +def load(eps): + """ + Given a Distribution.entry_points, produce EntryPoints. + """ + groups = itertools.chain.from_iterable( + load_group(value, group) for group, value in eps.items() + ) + return validate(metadata.EntryPoints(groups)) + + +@load.register(str) +def _(eps): + r""" + >>> ep, = load('[console_scripts]\nfoo=bar') + >>> ep.group + 'console_scripts' + >>> ep.name + 'foo' + >>> ep.value + 'bar' + """ + return validate(metadata.EntryPoints(metadata.EntryPoints._from_text(eps))) + + +load.register(type(None), lambda x: x) + + +@pass_none +def render(eps: metadata.EntryPoints): + by_group = operator.attrgetter('group') + groups = itertools.groupby(sorted(eps, key=by_group), by_group) + + return '\n'.join(f'[{group}]\n{render_items(items)}\n' for group, items in groups) + + +def render_items(eps): + return '\n'.join(f'{ep.name} = {ep.value}' for ep in sorted(eps)) diff --git a/venv/Lib/site-packages/setuptools/_imp.py b/venv/Lib/site-packages/setuptools/_imp.py new file mode 100644 index 0000000..9d4ead0 --- /dev/null +++ b/venv/Lib/site-packages/setuptools/_imp.py @@ -0,0 +1,88 @@ +""" +Re-implementation of find_module and get_frozen_object +from the deprecated imp module. +""" + +import os +import importlib.util +import importlib.machinery + +from importlib.util import module_from_spec + + +PY_SOURCE = 1 +PY_COMPILED = 2 +C_EXTENSION = 3 +C_BUILTIN = 6 +PY_FROZEN = 7 + + +def find_spec(module, paths): + finder = ( + importlib.machinery.PathFinder().find_spec + if isinstance(paths, list) + else importlib.util.find_spec + ) + return finder(module, paths) + + +def find_module(module, paths=None): + """Just like 'imp.find_module()', but with package support""" + spec = find_spec(module, paths) + if spec is None: + raise ImportError("Can't find %s" % module) + if not spec.has_location and hasattr(spec, 'submodule_search_locations'): + spec = importlib.util.spec_from_loader('__init__.py', spec.loader) + + kind = -1 + file = None + static = isinstance(spec.loader, type) + if ( + spec.origin == 'frozen' + or static + and issubclass(spec.loader, importlib.machinery.FrozenImporter) + ): + kind = PY_FROZEN + path = None # imp compabilty + suffix = mode = '' # imp compatibility + elif ( + spec.origin == 'built-in' + or static + and issubclass(spec.loader, importlib.machinery.BuiltinImporter) + ): + kind = C_BUILTIN + path = None # imp compabilty + suffix = mode = '' # imp compatibility + elif spec.has_location: + path = spec.origin + suffix = os.path.splitext(path)[1] + mode = 'r' if suffix in importlib.machinery.SOURCE_SUFFIXES else 'rb' + + if suffix in importlib.machinery.SOURCE_SUFFIXES: + kind = PY_SOURCE + elif suffix in importlib.machinery.BYTECODE_SUFFIXES: + kind = PY_COMPILED + elif suffix in importlib.machinery.EXTENSION_SUFFIXES: + kind = C_EXTENSION + + if kind in {PY_SOURCE, PY_COMPILED}: + file = open(path, mode) + else: + path = None + suffix = mode = '' + + return file, path, (suffix, mode, kind) + + +def get_frozen_object(module, paths=None): + spec = find_spec(module, paths) + if not spec: + raise ImportError("Can't find %s" % module) + return spec.loader.get_code(module) + + +def get_module(module, paths, info): + spec = find_spec(module, paths) + if not spec: + raise ImportError("Can't find %s" % module) + return module_from_spec(spec) diff --git a/venv/Lib/site-packages/setuptools/_importlib.py b/venv/Lib/site-packages/setuptools/_importlib.py new file mode 100644 index 0000000..bd2b01e --- /dev/null +++ b/venv/Lib/site-packages/setuptools/_importlib.py @@ -0,0 +1,51 @@ +import sys + + +def disable_importlib_metadata_finder(metadata): + """ + Ensure importlib_metadata doesn't provide older, incompatible + Distributions. + + Workaround for #3102. + """ + try: + import importlib_metadata + except ImportError: + return + except AttributeError: + from .warnings import SetuptoolsWarning + + SetuptoolsWarning.emit( + "Incompatibility problem.", + """ + `importlib-metadata` version is incompatible with `setuptools`. + This problem is likely to be solved by installing an updated version of + `importlib-metadata`. + """, + see_url="https://github.com/python/importlib_metadata/issues/396", + ) # Ensure a descriptive message is shown. + raise # This exception can be suppressed by _distutils_hack + + if importlib_metadata is metadata: + return + to_remove = [ + ob + for ob in sys.meta_path + if isinstance(ob, importlib_metadata.MetadataPathFinder) + ] + for item in to_remove: + sys.meta_path.remove(item) + + +if sys.version_info < (3, 10): + from setuptools.extern import importlib_metadata as metadata + + disable_importlib_metadata_finder(metadata) +else: + import importlib.metadata as metadata # noqa: F401 + + +if sys.version_info < (3, 9): + from setuptools.extern import importlib_resources as resources +else: + import importlib.resources as resources # noqa: F401 diff --git a/venv/Lib/site-packages/setuptools/_itertools.py b/venv/Lib/site-packages/setuptools/_itertools.py new file mode 100644 index 0000000..b8bf6d2 --- /dev/null +++ b/venv/Lib/site-packages/setuptools/_itertools.py @@ -0,0 +1,23 @@ +from setuptools.extern.more_itertools import consume # noqa: F401 + + +# copied from jaraco.itertools 6.1 +def ensure_unique(iterable, key=lambda x: x): + """ + Wrap an iterable to raise a ValueError if non-unique values are encountered. + + >>> list(ensure_unique('abc')) + ['a', 'b', 'c'] + >>> consume(ensure_unique('abca')) + Traceback (most recent call last): + ... + ValueError: Duplicate element 'a' encountered. + """ + seen = set() + seen_add = seen.add + for element in iterable: + k = key(element) + if k in seen: + raise ValueError(f"Duplicate element {element!r} encountered.") + seen_add(k) + yield element diff --git a/venv/Lib/site-packages/setuptools/_normalization.py b/venv/Lib/site-packages/setuptools/_normalization.py new file mode 100644 index 0000000..e858052 --- /dev/null +++ b/venv/Lib/site-packages/setuptools/_normalization.py @@ -0,0 +1,144 @@ +""" +Helpers for normalization as expected in wheel/sdist/module file names +and core metadata +""" + +import re + +from .extern import packaging + +# https://packaging.python.org/en/latest/specifications/core-metadata/#name +_VALID_NAME = re.compile(r"^([A-Z0-9]|[A-Z0-9][A-Z0-9._-]*[A-Z0-9])$", re.I) +_UNSAFE_NAME_CHARS = re.compile(r"[^A-Z0-9._-]+", re.I) +_NON_ALPHANUMERIC = re.compile(r"[^A-Z0-9]+", re.I) +_PEP440_FALLBACK = re.compile(r"^v?(?P(?:[0-9]+!)?[0-9]+(?:\.[0-9]+)*)", re.I) + + +def safe_identifier(name: str) -> str: + """Make a string safe to be used as Python identifier. + >>> safe_identifier("12abc") + '_12abc' + >>> safe_identifier("__editable__.myns.pkg-78.9.3_local") + '__editable___myns_pkg_78_9_3_local' + """ + safe = re.sub(r'\W|^(?=\d)', '_', name) + assert safe.isidentifier() + return safe + + +def safe_name(component: str) -> str: + """Escape a component used as a project name according to Core Metadata. + >>> safe_name("hello world") + 'hello-world' + >>> safe_name("hello?world") + 'hello-world' + >>> safe_name("hello_world") + 'hello_world' + """ + # See pkg_resources.safe_name + return _UNSAFE_NAME_CHARS.sub("-", component) + + +def safe_version(version: str) -> str: + """Convert an arbitrary string into a valid version string. + Can still raise an ``InvalidVersion`` exception. + To avoid exceptions use ``best_effort_version``. + >>> safe_version("1988 12 25") + '1988.12.25' + >>> safe_version("v0.2.1") + '0.2.1' + >>> safe_version("v0.2?beta") + '0.2b0' + >>> safe_version("v0.2 beta") + '0.2b0' + >>> safe_version("ubuntu lts") + Traceback (most recent call last): + ... + setuptools.extern.packaging.version.InvalidVersion: Invalid version: 'ubuntu.lts' + """ + v = version.replace(' ', '.') + try: + return str(packaging.version.Version(v)) + except packaging.version.InvalidVersion: + attempt = _UNSAFE_NAME_CHARS.sub("-", v) + return str(packaging.version.Version(attempt)) + + +def best_effort_version(version: str) -> str: + """Convert an arbitrary string into a version-like string. + Fallback when ``safe_version`` is not safe enough. + >>> best_effort_version("v0.2 beta") + '0.2b0' + >>> best_effort_version("ubuntu lts") + '0.dev0+sanitized.ubuntu.lts' + >>> best_effort_version("0.23ubuntu1") + '0.23.dev0+sanitized.ubuntu1' + >>> best_effort_version("0.23-") + '0.23.dev0+sanitized' + >>> best_effort_version("0.-_") + '0.dev0+sanitized' + >>> best_effort_version("42.+?1") + '42.dev0+sanitized.1' + """ + # See pkg_resources._forgiving_version + try: + return safe_version(version) + except packaging.version.InvalidVersion: + v = version.replace(' ', '.') + match = _PEP440_FALLBACK.search(v) + if match: + safe = match["safe"] + rest = v[len(safe) :] + else: + safe = "0" + rest = version + safe_rest = _NON_ALPHANUMERIC.sub(".", rest).strip(".") + local = f"sanitized.{safe_rest}".strip(".") + return safe_version(f"{safe}.dev0+{local}") + + +def safe_extra(extra: str) -> str: + """Normalize extra name according to PEP 685 + >>> safe_extra("_FrIeNdLy-._.-bArD") + 'friendly-bard' + >>> safe_extra("FrIeNdLy-._.-bArD__._-") + 'friendly-bard' + """ + return _NON_ALPHANUMERIC.sub("-", extra).strip("-").lower() + + +def filename_component(value: str) -> str: + """Normalize each component of a filename (e.g. distribution/version part of wheel) + Note: ``value`` needs to be already normalized. + >>> filename_component("my-pkg") + 'my_pkg' + """ + return value.replace("-", "_").strip("_") + + +def filename_component_broken(value: str) -> str: + """ + Produce the incorrect filename component for compatibility. + + See pypa/setuptools#4167 for detailed analysis. + + TODO: replace this with filename_component after pip 24 is + nearly-ubiquitous. + + >>> filename_component_broken('foo_bar-baz') + 'foo-bar-baz' + """ + return value.replace('_', '-') + + +def safer_name(value: str) -> str: + """Like ``safe_name`` but can be used as filename component for wheel""" + # See bdist_wheel.safer_name + return filename_component(safe_name(value)) + + +def safer_best_effort_version(value: str) -> str: + """Like ``best_effort_version`` but can be used as filename component for wheel""" + # See bdist_wheel.safer_verion + # TODO: Replace with only safe_version in the future (no need for best effort) + return filename_component(best_effort_version(value)) diff --git a/venv/Lib/site-packages/setuptools/_path.py b/venv/Lib/site-packages/setuptools/_path.py new file mode 100644 index 0000000..fb8ef0e --- /dev/null +++ b/venv/Lib/site-packages/setuptools/_path.py @@ -0,0 +1,40 @@ +import os +import sys +from typing import Union + +if sys.version_info >= (3, 9): + StrPath = Union[str, os.PathLike[str]] # Same as _typeshed.StrPath +else: + StrPath = Union[str, os.PathLike] + + +def ensure_directory(path): + """Ensure that the parent directory of `path` exists""" + dirname = os.path.dirname(path) + os.makedirs(dirname, exist_ok=True) + + +def same_path(p1: StrPath, p2: StrPath) -> bool: + """Differs from os.path.samefile because it does not require paths to exist. + Purely string based (no comparison between i-nodes). + >>> same_path("a/b", "./a/b") + True + >>> same_path("a/b", "a/./b") + True + >>> same_path("a/b", "././a/b") + True + >>> same_path("a/b", "./a/b/c/..") + True + >>> same_path("a/b", "../a/b/c") + False + >>> same_path("a", "a/b") + False + """ + return normpath(p1) == normpath(p2) + + +def normpath(filename: StrPath) -> str: + """Normalize a file/dir name for comparison purposes.""" + # See pkg_resources.normalize_path for notes about cygwin + file = os.path.abspath(filename) if sys.platform == 'cygwin' else filename + return os.path.normcase(os.path.realpath(os.path.normpath(file))) diff --git a/venv/Lib/site-packages/setuptools/_reqs.py b/venv/Lib/site-packages/setuptools/_reqs.py new file mode 100644 index 0000000..9f83437 --- /dev/null +++ b/venv/Lib/site-packages/setuptools/_reqs.py @@ -0,0 +1,38 @@ +from functools import lru_cache +from typing import Callable, Iterable, Iterator, TypeVar, Union, overload + +import setuptools.extern.jaraco.text as text +from setuptools.extern.packaging.requirements import Requirement + +_T = TypeVar("_T") +_StrOrIter = Union[str, Iterable[str]] + + +parse_req: Callable[[str], Requirement] = lru_cache()(Requirement) +# Setuptools parses the same requirement many times +# (e.g. first for validation than for normalisation), +# so it might be worth to cache. + + +def parse_strings(strs: _StrOrIter) -> Iterator[str]: + """ + Yield requirement strings for each specification in `strs`. + + `strs` must be a string, or a (possibly-nested) iterable thereof. + """ + return text.join_continuation(map(text.drop_comment, text.yield_lines(strs))) + + +@overload +def parse(strs: _StrOrIter) -> Iterator[Requirement]: ... + + +@overload +def parse(strs: _StrOrIter, parser: Callable[[str], _T]) -> Iterator[_T]: ... + + +def parse(strs, parser=parse_req): + """ + Replacement for ``pkg_resources.parse_requirements`` that uses ``packaging``. + """ + return map(parser, parse_strings(strs)) diff --git a/venv/Lib/site-packages/setuptools/_vendor/__init__.py b/venv/Lib/site-packages/setuptools/_vendor/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/venv/Lib/site-packages/setuptools/_vendor/__pycache__/__init__.cpython-312.pyc b/venv/Lib/site-packages/setuptools/_vendor/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..c6e43a0e83e4d438a037a335994f838ac8f44355 GIT binary patch literal 204 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 literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/setuptools/_vendor/__pycache__/typing_extensions.cpython-312.pyc b/venv/Lib/site-packages/setuptools/_vendor/__pycache__/typing_extensions.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..b4a89cc4403196f641bbdb2160f82f5af743a02d GIT binary patch literal 98146 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%0A?}+MMER;dCd+q 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 literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/setuptools/_vendor/__pycache__/zipp.cpython-312.pyc b/venv/Lib/site-packages/setuptools/_vendor/__pycache__/zipp.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..e7455a8e2456c5fc99ef8ccfb432c263092232d8 GIT binary patch literal 14884 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 aG$+-rh!yAxMj$Q*F+MUgGBOr116cqR*ghKo literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/setuptools/_vendor/backports/__pycache__/tarfile.cpython-312.pyc b/venv/Lib/site-packages/setuptools/_vendor/backports/__pycache__/tarfile.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..8d53b180482c8710069ca531bfa36ef1f33251ff GIT binary patch literal 119462 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 literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/setuptools/_vendor/backports/tarfile.py b/venv/Lib/site-packages/setuptools/_vendor/backports/tarfile.py new file mode 100644 index 0000000..a7a9a6e --- /dev/null +++ b/venv/Lib/site-packages/setuptools/_vendor/backports/tarfile.py @@ -0,0 +1,2900 @@ +#!/usr/bin/env python3 +#------------------------------------------------------------------- +# 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. +# +"""Read from and write to tar format archives. +""" + +version = "0.9.0" +__author__ = "Lars Gust\u00e4bel (lars@gustaebel.de)" +__credits__ = "Gustavo Niemeyer, Niels Gust\u00e4bel, Richard Townsend." + +#--------- +# Imports +#--------- +from builtins import open as bltn_open +import sys +import os +import io +import shutil +import stat +import time +import struct +import copy +import re +import warnings + +try: + import pwd +except ImportError: + pwd = None +try: + import grp +except ImportError: + grp = None + +# os.symlink on Windows prior to 6.0 raises NotImplementedError +# OSError (winerror=1314) will be raised if the caller does not hold the +# SeCreateSymbolicLinkPrivilege privilege +symlink_exception = (AttributeError, NotImplementedError, OSError) + +# from tarfile import * +__all__ = ["TarFile", "TarInfo", "is_tarfile", "TarError", "ReadError", + "CompressionError", "StreamError", "ExtractError", "HeaderError", + "ENCODING", "USTAR_FORMAT", "GNU_FORMAT", "PAX_FORMAT", + "DEFAULT_FORMAT", "open","fully_trusted_filter", "data_filter", + "tar_filter", "FilterError", "AbsoluteLinkError", + "OutsideDestinationError", "SpecialFileError", "AbsolutePathError", + "LinkOutsideDestinationError"] + + +#--------------------------------------------------------- +# 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 = PAX_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 = {"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 +} + +#--------------------------------------------------------- +# initialization +#--------------------------------------------------------- +if os.name == "nt": + 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. + """ + if s is None: + raise ValueError("metadata cannot contain None") + 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] in (0o200, 0o377): + n = 0 + for i in range(len(s) - 1): + n <<= 8 + n += s[i + 1] + if s[0] == 0o377: + n = -(256 ** (len(s) - 1) - n) + else: + try: + s = nts(s, "ascii", "strict") + n = int(s.strip() or "0", 8) + except ValueError: + raise InvalidHeaderError("invalid header") + 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 or 0o377 byte indicate this + # particular encoding, the following digits-1 bytes are a big-endian + # base-256 representation. This allows values up to (256**(digits-1))-1. + # A 0o200 byte indicates a positive number, a 0o377 byte a negative + # number. + original_n = n + n = int(n) + if 0 <= n < 8 ** (digits - 1): + s = bytes("%0*o" % (digits - 1, n), "ascii") + NUL + elif format == GNU_FORMAT and -256 ** (digits - 1) <= n < 256 ** (digits - 1): + if n >= 0: + s = bytearray([0o200]) + else: + s = bytearray([0o377]) + n = 256 ** digits + n + + for i in range(digits - 1): + s.insert(1, n & 0o377) + n >>= 8 + else: + raise ValueError("overflow in number field") + + 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_from("148B8x356B", buf)) + signed_chksum = 256 + sum(struct.unpack_from("148b8x356b", buf)) + return unsigned_chksum, signed_chksum + +def copyfileobj(src, dst, length=None, exception=OSError, bufsize=None): + """Copy length bytes from fileobj src to fileobj dst. + If length is None, copy the entire content. + """ + bufsize = bufsize or 16 * 1024 + if length == 0: + return + if length is None: + shutil.copyfileobj(src, dst, bufsize) + return + + blocks, remainder = divmod(length, bufsize) + for b in range(blocks): + buf = src.read(bufsize) + if len(buf) < bufsize: + raise exception("unexpected end of data") + dst.write(buf) + + if remainder != 0: + buf = src.read(remainder) + if len(buf) < remainder: + raise exception("unexpected end of data") + dst.write(buf) + return + +def _safe_print(s): + encoding = getattr(sys.stdout, 'encoding', None) + if encoding is not None: + s = s.encode(encoding, 'backslashreplace').decode(encoding) + print(s, end=' ') + + +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: + """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: + """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 that works with bytes, + and the method is accessed blockwise. + Use of gzip or bzip2 compression is possible. + A stream-like object could be for example: sys.stdin.buffer, + sys.stdout.buffer, a socket, a tape device etc. + + _Stream is intended to be used only internally. + """ + + def __init__(self, name, mode, comptype, fileobj, bufsize, + compresslevel): + """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") from None + self.zlib = zlib + self.crc = zlib.crc32(b"") + if mode == "r": + self.exception = zlib.error + self._init_read_gz() + else: + self._init_write_gz(compresslevel) + + elif comptype == "bz2": + try: + import bz2 + except ImportError: + raise CompressionError("bz2 module is not available") from None + if mode == "r": + self.dbuf = b"" + self.cmp = bz2.BZ2Decompressor() + self.exception = OSError + else: + self.cmp = bz2.BZ2Compressor(compresslevel) + + elif comptype == "xz": + try: + import lzma + except ImportError: + raise CompressionError("lzma module is not available") from None + if mode == "r": + self.dbuf = b"" + self.cmp = lzma.LZMADecompressor() + self.exception = lzma.LZMAError + else: + self.cmp = lzma.LZMACompressor() + + elif comptype != "tar": + raise CompressionError("unknown compression type %r" % comptype) + + 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, compresslevel): + """Initialize for writing with gzip compression. + """ + self.cmp = self.zlib.compressobj(compresslevel, + 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 + + self.closed = True + try: + 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": + 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): + """Return the next size number of bytes from the stream.""" + assert size is not None + 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) + t = [self.dbuf] + while c < size: + # Skip underlying buffer to avoid unaligned double buffering. + if self.buf: + buf = self.buf + self.buf = b"" + else: + buf = self.fileobj.read(self.bufsize) + if not buf: + break + try: + buf = self.cmp.decompress(buf) + except self.exception as e: + raise ReadError("invalid compressed data") from e + t.append(buf) + c += len(buf) + t = b"".join(t) + self.dbuf = t[size:] + return t[:size] + + def __read(self, size): + """Return size bytes from stream. If internal buffer is empty, + read another block from the stream. + """ + c = len(self.buf) + t = [self.buf] + while c < size: + buf = self.fileobj.read(self.bufsize) + if not buf: + break + t.append(buf) + c += len(buf) + t = b"".join(t) + self.buf = t[size:] + return t[:size] +# 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"\x1f\x8b\x08"): + return "gz" + elif self.buf[0:3] == b"BZh" and self.buf[4:10] == b"1AY&SY": + return "bz2" + elif self.buf.startswith((b"\x5d\x00\x00\x80", b"\xfd7zXZ")): + return "xz" + else: + return "tar" + + def close(self): + self.fileobj.close() +# class StreamProxy + +#------------------------ +# 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, name, blockinfo=None): + self.fileobj = fileobj + self.offset = offset + self.size = size + self.position = 0 + self.name = name + self.closed = False + + 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 flush(self): + pass + + def readable(self): + return True + + def writable(self): + return False + + def seekable(self): + return self.fileobj.seekable() + + def tell(self): + """Return the current file position. + """ + return self.position + + def seek(self, position, whence=io.SEEK_SET): + """Seek to a position in the file. + """ + if whence == io.SEEK_SET: + self.position = min(max(position, 0), self.size) + elif whence == io.SEEK_CUR: + if position < 0: + self.position = max(self.position + position, 0) + else: + self.position = min(self.position + position, self.size) + elif whence == io.SEEK_END: + self.position = max(min(self.size + position, self.size), 0) + else: + raise ValueError("Invalid argument") + return self.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)) + b = self.fileobj.read(length) + if len(b) != length: + raise ReadError("unexpected end of data") + buf += b + else: + buf += NUL * length + size -= length + self.position += length + return buf + + def readinto(self, b): + buf = self.read(len(b)) + b[:len(buf)] = buf + return len(buf) + + def close(self): + self.closed = True +#class _FileInFile + +class ExFileObject(io.BufferedReader): + + def __init__(self, tarfile, tarinfo): + fileobj = _FileInFile(tarfile.fileobj, tarinfo.offset_data, + tarinfo.size, tarinfo.name, tarinfo.sparse) + super().__init__(fileobj) +#class ExFileObject + + +#----------------------------- +# extraction filters (PEP 706) +#----------------------------- + +class FilterError(TarError): + pass + +class AbsolutePathError(FilterError): + def __init__(self, tarinfo): + self.tarinfo = tarinfo + super().__init__(f'member {tarinfo.name!r} has an absolute path') + +class OutsideDestinationError(FilterError): + def __init__(self, tarinfo, path): + self.tarinfo = tarinfo + self._path = path + super().__init__(f'{tarinfo.name!r} would be extracted to {path!r}, ' + + 'which is outside the destination') + +class SpecialFileError(FilterError): + def __init__(self, tarinfo): + self.tarinfo = tarinfo + super().__init__(f'{tarinfo.name!r} is a special file') + +class AbsoluteLinkError(FilterError): + def __init__(self, tarinfo): + self.tarinfo = tarinfo + super().__init__(f'{tarinfo.name!r} is a link to an absolute path') + +class LinkOutsideDestinationError(FilterError): + def __init__(self, tarinfo, path): + self.tarinfo = tarinfo + self._path = path + super().__init__(f'{tarinfo.name!r} would link to {path!r}, ' + + 'which is outside the destination') + +def _get_filtered_attrs(member, dest_path, for_data=True): + new_attrs = {} + name = member.name + dest_path = os.path.realpath(dest_path) + # Strip leading / (tar's directory separator) from filenames. + # Include os.sep (target OS directory separator) as well. + if name.startswith(('/', os.sep)): + name = new_attrs['name'] = member.path.lstrip('/' + os.sep) + if os.path.isabs(name): + # Path is absolute even after stripping. + # For example, 'C:/foo' on Windows. + raise AbsolutePathError(member) + # Ensure we stay in the destination + target_path = os.path.realpath(os.path.join(dest_path, name)) + if os.path.commonpath([target_path, dest_path]) != dest_path: + raise OutsideDestinationError(member, target_path) + # Limit permissions (no high bits, and go-w) + mode = member.mode + if mode is not None: + # Strip high bits & group/other write bits + mode = mode & 0o755 + if for_data: + # For data, handle permissions & file types + if member.isreg() or member.islnk(): + if not mode & 0o100: + # Clear executable bits if not executable by user + mode &= ~0o111 + # Ensure owner can read & write + mode |= 0o600 + elif member.isdir() or member.issym(): + # Ignore mode for directories & symlinks + mode = None + else: + # Reject special files + raise SpecialFileError(member) + if mode != member.mode: + new_attrs['mode'] = mode + if for_data: + # Ignore ownership for 'data' + if member.uid is not None: + new_attrs['uid'] = None + if member.gid is not None: + new_attrs['gid'] = None + if member.uname is not None: + new_attrs['uname'] = None + if member.gname is not None: + new_attrs['gname'] = None + # Check link destination for 'data' + if member.islnk() or member.issym(): + if os.path.isabs(member.linkname): + raise AbsoluteLinkError(member) + if member.issym(): + target_path = os.path.join(dest_path, + os.path.dirname(name), + member.linkname) + else: + target_path = os.path.join(dest_path, + member.linkname) + target_path = os.path.realpath(target_path) + if os.path.commonpath([target_path, dest_path]) != dest_path: + raise LinkOutsideDestinationError(member, target_path) + return new_attrs + +def fully_trusted_filter(member, dest_path): + return member + +def tar_filter(member, dest_path): + new_attrs = _get_filtered_attrs(member, dest_path, False) + if new_attrs: + return member.replace(**new_attrs, deep=False) + return member + +def data_filter(member, dest_path): + new_attrs = _get_filtered_attrs(member, dest_path, True) + if new_attrs: + return member.replace(**new_attrs, deep=False) + return member + +_NAMED_FILTERS = { + "fully_trusted": fully_trusted_filter, + "tar": tar_filter, + "data": data_filter, +} + +#------------------ +# Exported Classes +#------------------ + +# Sentinel for replace() defaults, meaning "don't change the attribute" +_KEEP = object() + +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__ = dict( + name = 'Name of the archive member.', + mode = 'Permission bits.', + uid = 'User ID of the user who originally stored this member.', + gid = 'Group ID of the user who originally stored this member.', + size = 'Size in bytes.', + mtime = 'Time of last modification.', + chksum = 'Header checksum.', + type = ('File type. type is usually one of these constants: ' + 'REGTYPE, AREGTYPE, LNKTYPE, SYMTYPE, DIRTYPE, FIFOTYPE, ' + 'CONTTYPE, CHRTYPE, BLKTYPE, GNUTYPE_SPARSE.'), + linkname = ('Name of the target file name, which is only present ' + 'in TarInfo objects of type LNKTYPE and SYMTYPE.'), + uname = 'User name.', + gname = 'Group name.', + devmajor = 'Device major number.', + devminor = 'Device minor number.', + offset = 'The tar header starts here.', + offset_data = "The file's data starts here.", + pax_headers = ('A dictionary containing key-value pairs of an ' + 'associated pax extended header.'), + sparse = 'Sparse member information.', + tarfile = None, + _sparse_structs = None, + _link_target = None, + ) + + 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 + + @property + def path(self): + 'In pax headers, "name" is called "path".' + return self.name + + @path.setter + def path(self, name): + self.name = name + + @property + def linkpath(self): + 'In pax headers, "linkname" is called "linkpath".' + return self.linkname + + @linkpath.setter + def linkpath(self, linkname): + self.linkname = linkname + + def __repr__(self): + return "<%s %r at %#x>" % (self.__class__.__name__,self.name,id(self)) + + def replace(self, *, + name=_KEEP, mtime=_KEEP, mode=_KEEP, linkname=_KEEP, + uid=_KEEP, gid=_KEEP, uname=_KEEP, gname=_KEEP, + deep=True, _KEEP=_KEEP): + """Return a deep copy of self with the given attributes replaced. + """ + if deep: + result = copy.deepcopy(self) + else: + result = copy.copy(self) + if name is not _KEEP: + result.name = name + if mtime is not _KEEP: + result.mtime = mtime + if mode is not _KEEP: + result.mode = mode + if linkname is not _KEEP: + result.linkname = linkname + if uid is not _KEEP: + result.uid = uid + if gid is not _KEEP: + result.gid = gid + if uname is not _KEEP: + result.uname = uname + if gname is not _KEEP: + result.gname = gname + return result + + def get_info(self): + """Return the TarInfo's attributes as a dictionary. + """ + if self.mode is None: + mode = None + else: + mode = self.mode & 0o7777 + info = { + "name": self.name, + "mode": mode, + "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() + for name, value in info.items(): + if value is None: + raise ValueError("%s may not be None" % name) + + 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"].encode(encoding, errors)) > LENGTH_LINK: + raise ValueError("linkname is too long") + + if len(info["name"].encode(encoding, errors)) > LENGTH_NAME: + info["prefix"], info["name"] = self._posix_split_name(info["name"], encoding, errors) + + 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"].encode(encoding, errors)) > LENGTH_LINK: + buf += self._create_gnu_long_header(info["linkname"], GNUTYPE_LONGLINK, encoding, errors) + + if len(info["name"].encode(encoding, errors)) > 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)): + needs_pax = False + + val = info[name] + val_is_float = isinstance(val, float) + val_int = round(val) if val_is_float else val + if not 0 <= val_int < 8 ** (digits - 1): + # Avoid overflow. + info[name] = 0 + needs_pax = True + elif val_is_float: + # Put rounded value in ustar header, and full + # precision value in pax header. + info[name] = val_int + needs_pax = True + + # The existing pax header has priority. + if needs_pax and name not in pax_headers: + pax_headers[name] = str(val) + + # 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, "utf-8") + + def _posix_split_name(self, name, encoding, errors): + """Split a name longer than 100 chars into a prefix + and a name part. + """ + components = name.split("/") + for i in range(1, len(components)): + prefix = "/".join(components[:i]) + name = "/".join(components[i:]) + if len(prefix.encode(encoding, errors)) <= LENGTH_PREFIX and \ + len(name.encode(encoding, errors)) <= LENGTH_NAME: + break + else: + 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. + """ + has_device_fields = info.get("type") in (CHRTYPE, BLKTYPE) + if has_device_fields: + devmajor = itn(info.get("devmajor", 0), 8, format) + devminor = itn(info.get("devminor", 0), 8, format) + else: + devmajor = stn("", 8, encoding, errors) + devminor = stn("", 8, encoding, errors) + + # None values in metadata should cause ValueError. + # itn()/stn() do this for all fields except type. + filetype = info.get("type", REGTYPE) + if filetype is None: + raise ValueError("TarInfo.type must not be None") + + 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 + filetype, + 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), + devmajor, + devminor, + stn(info.get("prefix", ""), 155, encoding, errors) + ] + + buf = struct.pack("%ds" % BLOCKSIZE, b"".join(parts)) + chksum = calc_chksums(buf[-BLOCKSIZE:])[0] + buf = buf[:-364] + bytes("%06o\0" % chksum, "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("utf-8", "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("utf-8") + 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("utf-8") + + 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 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) + + # Remove redundant slashes from directories. This is to be consistent + # with frombuf(). + if self.isdir(): + self.name = self.name.rstrip("/") + + 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 as e: + raise SubsequentHeaderError(str(e)) from None + + # 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) + + # Remove redundant slashes from directories. This is to be consistent + # with frombuf(). + if next.isdir(): + next.name = next.name.removesuffix("/") + + 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("utf-8") + + # 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 = "utf-8" + + # 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 match := regex.match(buf, pos): + length, keyword = match.groups() + length = int(length) + if length == 0: + raise InvalidHeaderError("invalid header") + value = buf[match.end(2) + 1:match.start(1) + length - 1] + + # Normally, we could just use "utf-8" 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, "utf-8", "utf-8", + 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, "utf-8", "utf-8", + tarfile.errors) + + pax_headers[keyword] = value + pos += length + + # Fetch the next header. + try: + next = self.fromtarfile(tarfile) + except HeaderError as e: + raise SubsequentHeaderError(str(e)) from None + + # 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 True if the Tarinfo object is a regular file.' + return self.type in REGULAR_TYPES + + def isfile(self): + 'Return True if the Tarinfo object is a regular file.' + return self.isreg() + + def isdir(self): + 'Return True if it is a directory.' + return self.type == DIRTYPE + + def issym(self): + 'Return True if it is a symbolic link.' + return self.type == SYMTYPE + + def islnk(self): + 'Return True if it is a hard link.' + return self.type == LNKTYPE + + def ischr(self): + 'Return True if it is a character device.' + return self.type == CHRTYPE + + def isblk(self): + 'Return True if it is a block device.' + return self.type == BLKTYPE + + def isfifo(self): + 'Return True if it is a FIFO.' + return self.type == FIFOTYPE + + def issparse(self): + return self.sparse is not None + + def isdev(self): + 'Return True if it is one of character device, block device or FIFO.' + 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 file-object for extractfile(). + + extraction_filter = None # The default filter for extraction. + + 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, copybufsize=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. + """ + modes = {"r": "rb", "a": "r+b", "w": "wb", "x": "xb"} + if mode not in modes: + raise ValueError("mode must be 'r', 'a', 'w' or 'x'") + self.mode = mode + self._mode = modes[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") and + isinstance(fileobj.name, (str, bytes))): + 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.copybufsize = copybufsize + 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)) from None + + if self.mode in ("a", "w", "x"): + 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): + r"""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 + 'r:xz' open for reading with lzma 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 + 'w:xz' open for writing with lzma compression + + 'x' or 'x:' create a tarfile exclusively without compression, raise + an exception if the file is already created + 'x:gz' create a gzip compressed tarfile, raise an exception + if the file is already created + 'x:bz2' create a bzip2 compressed tarfile, raise an exception + if the file is already created + 'x:xz' create an lzma compressed tarfile, raise an exception + if the file is already created + + '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 + 'r|xz' open an lzma 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 + 'w|xz' open an lzma 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. + def not_compressed(comptype): + return cls.OPEN_METH[comptype] == 'taropen' + error_msgs = [] + for comptype in sorted(cls.OPEN_METH, key=not_compressed): + 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: + error_msgs.append(f'- method {comptype}: {e!r}') + if fileobj is not None: + fileobj.seek(saved_pos) + continue + error_msgs_summary = '\n'.join(error_msgs) + raise ReadError(f"file could not be opened successfully:\n{error_msgs_summary}") + + 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 ("r", "w"): + raise ValueError("mode must be 'r' or 'w'") + + compresslevel = kwargs.pop("compresslevel", 9) + stream = _Stream(name, filemode, comptype, fileobj, bufsize, + compresslevel) + try: + t = cls(name, filemode, stream, **kwargs) + except: + stream.close() + raise + t._extfileobj = False + return t + + elif mode in ("a", "w", "x"): + 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 mode not in ("r", "a", "w", "x"): + raise ValueError("mode must be 'r', 'a', 'w' or 'x'") + 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 mode not in ("r", "w", "x"): + raise ValueError("mode must be 'r', 'w' or 'x'") + + try: + from gzip import GzipFile + except ImportError: + raise CompressionError("gzip module is not available") from None + + try: + fileobj = GzipFile(name, mode + "b", compresslevel, fileobj) + except OSError as e: + if fileobj is not None and mode == 'r': + raise ReadError("not a gzip file") from e + raise + + try: + t = cls.taropen(name, mode, fileobj, **kwargs) + except OSError as e: + fileobj.close() + if mode == 'r': + raise ReadError("not a gzip file") from e + raise + except: + fileobj.close() + raise + t._extfileobj = False + 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 mode not in ("r", "w", "x"): + raise ValueError("mode must be 'r', 'w' or 'x'") + + try: + from bz2 import BZ2File + except ImportError: + raise CompressionError("bz2 module is not available") from None + + fileobj = BZ2File(fileobj or name, mode, compresslevel=compresslevel) + + try: + t = cls.taropen(name, mode, fileobj, **kwargs) + except (OSError, EOFError) as e: + fileobj.close() + if mode == 'r': + raise ReadError("not a bzip2 file") from e + raise + except: + fileobj.close() + raise + t._extfileobj = False + return t + + @classmethod + def xzopen(cls, name, mode="r", fileobj=None, preset=None, **kwargs): + """Open lzma compressed tar archive name for reading or writing. + Appending is not allowed. + """ + if mode not in ("r", "w", "x"): + raise ValueError("mode must be 'r', 'w' or 'x'") + + try: + from lzma import LZMAFile, LZMAError + except ImportError: + raise CompressionError("lzma module is not available") from None + + fileobj = LZMAFile(fileobj or name, mode, preset=preset) + + try: + t = cls.taropen(name, mode, fileobj, **kwargs) + except (LZMAError, EOFError) as e: + fileobj.close() + if mode == 'r': + raise ReadError("not an lzma file") from e + raise + except: + fileobj.close() + raise + 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 + "xz": "xzopen" # lzma 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 + + self.closed = True + try: + if self.mode in ("a", "w", "x"): + 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)) + finally: + if not self._extfileobj: + self.fileobj.close() + + 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.rstrip('/')) + 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 from the result of os.stat or equivalent + on an existing file. The file is either named by ``name``, or + specified as a file object ``fileobj`` with a file descriptor. If + given, ``arcname`` specifies an alternative name for the file in the + archive, otherwise, the name is taken from the 'name' attribute of + 'fileobj', or the 'name' argument. The name should be a text + string. + """ + self._check("awx") + + # 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 # Not needed + + # Use os.stat or os.lstat, depending on if symlinks shall be resolved. + if fileobj is None: + if 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, *, members=None): + """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. ``members`` is optional and must be a subset of the + list returned by getmembers(). + """ + self._check() + + if members is None: + members = self + for tarinfo in members: + if verbose: + if tarinfo.mode is None: + _safe_print("??????????") + else: + _safe_print(stat.filemode(tarinfo.mode)) + _safe_print("%s/%s" % (tarinfo.uname or tarinfo.uid, + tarinfo.gname or tarinfo.gid)) + if tarinfo.ischr() or tarinfo.isblk(): + _safe_print("%10s" % + ("%d,%d" % (tarinfo.devmajor, tarinfo.devminor))) + else: + _safe_print("%10d" % tarinfo.size) + if tarinfo.mtime is None: + _safe_print("????-??-?? ??:??:??") + else: + _safe_print("%d-%02d-%02d %02d:%02d:%02d" \ + % time.localtime(tarinfo.mtime)[:6]) + + _safe_print(tarinfo.name + ("/" if tarinfo.isdir() else "")) + + if verbose: + if tarinfo.issym(): + _safe_print("-> " + tarinfo.linkname) + if tarinfo.islnk(): + _safe_print("link to " + tarinfo.linkname) + print() + + def add(self, name, arcname=None, recursive=True, *, 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. ``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("awx") + + if arcname is None: + arcname = name + + # 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(): + with bltn_open(name, "rb") as f: + self.addfile(tarinfo, f) + + elif tarinfo.isdir(): + self.addfile(tarinfo) + if recursive: + for f in sorted(os.listdir(name)): + self.add(os.path.join(name, f), os.path.join(arcname, f), + recursive, filter=filter) + + else: + self.addfile(tarinfo) + + def addfile(self, tarinfo, fileobj=None): + """Add the TarInfo object ``tarinfo`` to the archive. If ``fileobj`` is + given, it should be a binary file, and tarinfo.size bytes are read + from it and added to the archive. You can create TarInfo objects + directly, or by using gettarinfo(). + """ + self._check("awx") + + tarinfo = copy.copy(tarinfo) + + buf = tarinfo.tobuf(self.format, self.encoding, self.errors) + self.fileobj.write(buf) + self.offset += len(buf) + bufsize=self.copybufsize + # If there's data to follow, append it. + if fileobj is not None: + copyfileobj(fileobj, self.fileobj, tarinfo.size, bufsize=bufsize) + 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 _get_filter_function(self, filter): + if filter is None: + filter = self.extraction_filter + if filter is None: + warnings.warn( + 'Python 3.14 will, by default, filter extracted tar ' + + 'archives and reject files or modify their metadata. ' + + 'Use the filter argument to control this behavior.', + DeprecationWarning) + return fully_trusted_filter + if isinstance(filter, str): + raise TypeError( + 'String names are not supported for ' + + 'TarFile.extraction_filter. Use a function such as ' + + 'tarfile.data_filter directly.') + return filter + if callable(filter): + return filter + try: + return _NAMED_FILTERS[filter] + except KeyError: + raise ValueError(f"filter {filter!r} not found") from None + + def extractall(self, path=".", members=None, *, numeric_owner=False, + filter=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(). If `numeric_owner` is True, only + the numbers for user/group names are used and not the names. + + The `filter` function will be called on each member just + before extraction. + It can return a changed TarInfo or None to skip the member. + String names of common filters are accepted. + """ + directories = [] + + filter_function = self._get_filter_function(filter) + if members is None: + members = self + + for member in members: + tarinfo = self._get_extract_tarinfo(member, filter_function, path) + if tarinfo is None: + continue + if tarinfo.isdir(): + # For directories, delay setting attributes until later, + # since permissions can interfere with extraction and + # extracting contents can reset mtime. + directories.append(tarinfo) + self._extract_one(tarinfo, path, set_attrs=not tarinfo.isdir(), + numeric_owner=numeric_owner) + + # Reverse sort directories. + directories.sort(key=lambda a: a.name, reverse=True) + + # Set correct owner, mtime and filemode on directories. + for tarinfo in directories: + dirpath = os.path.join(path, tarinfo.name) + try: + self.chown(tarinfo, dirpath, numeric_owner=numeric_owner) + self.utime(tarinfo, dirpath) + self.chmod(tarinfo, dirpath) + except ExtractError as e: + self._handle_nonfatal_error(e) + + def extract(self, member, path="", set_attrs=True, *, numeric_owner=False, + filter=None): + """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. If `numeric_owner` + is True, only the numbers for user/group names are used and not + the names. + + The `filter` function will be called before extraction. + It can return a changed TarInfo or None to skip the member. + String names of common filters are accepted. + """ + filter_function = self._get_filter_function(filter) + tarinfo = self._get_extract_tarinfo(member, filter_function, path) + if tarinfo is not None: + self._extract_one(tarinfo, path, set_attrs, numeric_owner) + + def _get_extract_tarinfo(self, member, filter_function, path): + """Get filtered TarInfo (or None) from member, which might be a str""" + if isinstance(member, str): + tarinfo = self.getmember(member) + else: + tarinfo = member + + unfiltered = tarinfo + try: + tarinfo = filter_function(tarinfo, path) + except (OSError, FilterError) as e: + self._handle_fatal_error(e) + except ExtractError as e: + self._handle_nonfatal_error(e) + if tarinfo is None: + self._dbg(2, "tarfile: Excluded %r" % unfiltered.name) + return None + # Prepare the link target for makelink(). + if tarinfo.islnk(): + tarinfo = copy.copy(tarinfo) + tarinfo._link_target = os.path.join(path, tarinfo.linkname) + return tarinfo + + def _extract_one(self, tarinfo, path, set_attrs, numeric_owner): + """Extract from filtered tarinfo to disk""" + self._check("r") + + try: + self._extract_member(tarinfo, os.path.join(path, tarinfo.name), + set_attrs=set_attrs, + numeric_owner=numeric_owner) + except OSError as e: + self._handle_fatal_error(e) + except ExtractError as e: + self._handle_nonfatal_error(e) + + def _handle_nonfatal_error(self, e): + """Handle non-fatal error (ExtractError) according to errorlevel""" + if self.errorlevel > 1: + raise + else: + self._dbg(1, "tarfile: %s" % e) + + def _handle_fatal_error(self, e): + """Handle "fatal" error according to self.errorlevel""" + if self.errorlevel > 0: + raise + elif isinstance(e, OSError): + if e.filename is None: + self._dbg(1, "tarfile: %s" % e.strerror) + else: + self._dbg(1, "tarfile: %s %r" % (e.strerror, e.filename)) + else: + self._dbg(1, "tarfile: %s %s" % (type(e).__name__, 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 or + a link, an io.BufferedReader object is returned. For all other + existing members, None is returned. If ``member`` does not appear + in the archive, KeyError is raised. + """ + self._check("r") + + if isinstance(member, str): + tarinfo = self.getmember(member) + else: + tarinfo = member + + if tarinfo.isreg() or tarinfo.type not in SUPPORTED_TYPES: + # Members with unknown types are treated as regular files. + 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, + numeric_owner=False): + """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, numeric_owner) + 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: + if tarinfo.mode is None: + # Use the system's default mode + os.mkdir(targetpath) + else: + # Use a safe mode for the directory, the real mode is set + # later in _extract_member(). + os.mkdir(targetpath, 0o700) + except FileExistsError: + if not os.path.isdir(targetpath): + raise + + def makefile(self, tarinfo, targetpath): + """Make a file called targetpath. + """ + source = self.fileobj + source.seek(tarinfo.offset_data) + bufsize = self.copybufsize + with bltn_open(targetpath, "wb") as target: + if tarinfo.sparse is not None: + for offset, size in tarinfo.sparse: + target.seek(offset) + copyfileobj(source, target, size, ReadError, bufsize) + target.seek(tarinfo.size) + target.truncate() + else: + copyfileobj(source, target, tarinfo.size, ReadError, bufsize) + + 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 mode is None: + # Use mknod's default + mode = 0o600 + 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(): + if os.path.lexists(targetpath): + # Avoid FileExistsError on following os.symlink. + os.unlink(targetpath) + os.symlink(tarinfo.linkname, targetpath) + else: + 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: + try: + self._extract_member(self._find_link_target(tarinfo), + targetpath) + except KeyError: + raise ExtractError("unable to resolve link inside archive") from None + + def chown(self, tarinfo, targetpath, numeric_owner): + """Set owner of targetpath according to tarinfo. If numeric_owner + is True, use .gid/.uid instead of .gname/.uname. If numeric_owner + is False, fall back to .gid/.uid when the search based on name + fails. + """ + if hasattr(os, "geteuid") and os.geteuid() == 0: + # We have to be root to do so. + g = tarinfo.gid + u = tarinfo.uid + if not numeric_owner: + try: + if grp and tarinfo.gname: + g = grp.getgrnam(tarinfo.gname)[2] + except KeyError: + pass + try: + if pwd and tarinfo.uname: + u = pwd.getpwnam(tarinfo.uname)[2] + except KeyError: + pass + if g is None: + g = -1 + if u is None: + u = -1 + try: + if tarinfo.issym() and hasattr(os, "lchown"): + os.lchown(targetpath, u, g) + else: + os.chown(targetpath, u, g) + except OSError as e: + raise ExtractError("could not change owner") from e + + def chmod(self, tarinfo, targetpath): + """Set file permissions of targetpath according to tarinfo. + """ + if tarinfo.mode is None: + return + try: + os.chmod(targetpath, tarinfo.mode) + except OSError as e: + raise ExtractError("could not change mode") from e + + def utime(self, tarinfo, targetpath): + """Set modification time of targetpath according to tarinfo. + """ + mtime = tarinfo.mtime + if mtime is None: + return + if not hasattr(os, 'utime'): + return + try: + os.utime(targetpath, (mtime, mtime)) + except OSError as e: + raise ExtractError("could not change modification time") from e + + #-------------------------------------------------------------------------- + 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 + + # Advance the file pointer. + if self.offset != self.fileobj.tell(): + if self.offset == 0: + return None + self.fileobj.seek(self.offset - 1) + if not self.fileobj.read(1): + raise ReadError("unexpected end of data") + + # Read the next block. + 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)) from None + except EmptyHeaderError: + if self.offset == 0: + raise ReadError("empty file") from None + except TruncatedHeaderError as e: + if self.offset == 0: + raise ReadError(str(e)) from None + except SubsequentHeaderError as e: + raise ReadError(str(e)) from None + except Exception as e: + try: + import zlib + if isinstance(e, zlib.error): + raise ReadError(f'zlib error: {e}') from None + else: + raise e + except ImportError: + raise 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. + skipping = False + if tarinfo is not None: + try: + index = members.index(tarinfo) + except ValueError: + # The given starting point might be a (modified) copy. + # We'll later skip members until we find an equivalent. + skipping = True + else: + # Happy fast path + members = members[:index] + + if normalize: + name = os.path.normpath(name) + + for member in reversed(members): + if skipping: + if tarinfo.offset == member.offset: + skipping = False + continue + if normalize: + member_name = os.path.normpath(member.name) + else: + member_name = member.name + + if name == member_name: + return member + + if skipping: + # Starting point was not found + raise ValueError(tarinfo) + + def _load(self): + """Read through the entire archive file and look for readable + members. + """ + while self.next() is not None: + pass + 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 OSError("%s is closed" % self.__class__.__name__) + if mode is not None and self.mode not in mode: + raise OSError("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 = "/".join(filter(None, (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: + yield from self.members + return + + # Yield items using TarFile's next() method. + # When all members have been read, set TarFile as _loaded. + index = 0 + # Fix for SF #1100429: Under rare circumstances it can + # happen that getmembers() is called during iteration, + # which will have already exhausted the next() method. + if self.firstmember is not None: + tarinfo = self.next() + index += 1 + yield tarinfo + + while True: + if index < len(self.members): + tarinfo = self.members[index] + elif not self._loaded: + tarinfo = self.next() + if not tarinfo: + self._loaded = True + return + else: + return + index += 1 + yield tarinfo + + 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 + +#-------------------- +# exported functions +#-------------------- + +def is_tarfile(name): + """Return True if name points to a tar archive that we + are able to handle, else return False. + + 'name' should be a string, file, or file-like object. + """ + try: + if hasattr(name, "read"): + pos = name.tell() + t = open(fileobj=name) + name.seek(pos) + else: + t = open(name) + t.close() + return True + except TarError: + return False + +open = TarFile.open + + +def main(): + import argparse + + description = 'A simple command-line interface for tarfile module.' + parser = argparse.ArgumentParser(description=description) + parser.add_argument('-v', '--verbose', action='store_true', default=False, + help='Verbose output') + parser.add_argument('--filter', metavar='', + choices=_NAMED_FILTERS, + help='Filter for extraction') + + group = parser.add_mutually_exclusive_group(required=True) + group.add_argument('-l', '--list', metavar='', + help='Show listing of a tarfile') + group.add_argument('-e', '--extract', nargs='+', + metavar=('', ''), + help='Extract tarfile into target dir') + group.add_argument('-c', '--create', nargs='+', + metavar=('', ''), + help='Create tarfile from sources') + group.add_argument('-t', '--test', metavar='', + help='Test if a tarfile is valid') + + args = parser.parse_args() + + if args.filter and args.extract is None: + parser.exit(1, '--filter is only valid for extraction\n') + + if args.test is not None: + src = args.test + if is_tarfile(src): + with open(src, 'r') as tar: + tar.getmembers() + print(tar.getmembers(), file=sys.stderr) + if args.verbose: + print('{!r} is a tar archive.'.format(src)) + else: + parser.exit(1, '{!r} is not a tar archive.\n'.format(src)) + + elif args.list is not None: + src = args.list + if is_tarfile(src): + with TarFile.open(src, 'r:*') as tf: + tf.list(verbose=args.verbose) + else: + parser.exit(1, '{!r} is not a tar archive.\n'.format(src)) + + elif args.extract is not None: + if len(args.extract) == 1: + src = args.extract[0] + curdir = os.curdir + elif len(args.extract) == 2: + src, curdir = args.extract + else: + parser.exit(1, parser.format_help()) + + if is_tarfile(src): + with TarFile.open(src, 'r:*') as tf: + tf.extractall(path=curdir, filter=args.filter) + if args.verbose: + if curdir == '.': + msg = '{!r} file is extracted.'.format(src) + else: + msg = ('{!r} file is extracted ' + 'into {!r} directory.').format(src, curdir) + print(msg) + else: + parser.exit(1, '{!r} is not a tar archive.\n'.format(src)) + + elif args.create is not None: + tar_name = args.create.pop(0) + _, ext = os.path.splitext(tar_name) + compressions = { + # gz + '.gz': 'gz', + '.tgz': 'gz', + # xz + '.xz': 'xz', + '.txz': 'xz', + # bz2 + '.bz2': 'bz2', + '.tbz': 'bz2', + '.tbz2': 'bz2', + '.tb2': 'bz2', + } + tar_mode = 'w:' + compressions[ext] if ext in compressions else 'w' + tar_files = args.create + + with TarFile.open(tar_name, tar_mode) as tf: + for file_name in tar_files: + tf.add(file_name) + + if args.verbose: + print('{!r} file created.'.format(tar_name)) + +if __name__ == '__main__': + main() diff --git a/venv/Lib/site-packages/setuptools/_vendor/importlib_metadata/__init__.py b/venv/Lib/site-packages/setuptools/_vendor/importlib_metadata/__init__.py new file mode 100644 index 0000000..8864214 --- /dev/null +++ b/venv/Lib/site-packages/setuptools/_vendor/importlib_metadata/__init__.py @@ -0,0 +1,904 @@ +import os +import re +import abc +import csv +import sys +from .. import zipp +import email +import pathlib +import operator +import textwrap +import warnings +import functools +import itertools +import posixpath +import collections + +from . import _adapters, _meta, _py39compat +from ._collections import FreezableDefaultDict, Pair +from ._compat import ( + NullFinder, + install, + pypy_partial, +) +from ._functools import method_cache, pass_none +from ._itertools import always_iterable, unique_everseen +from ._meta import PackageMetadata, SimplePath + +from contextlib import suppress +from importlib import import_module +from importlib.abc import MetaPathFinder +from itertools import starmap +from typing import List, Mapping, Optional + + +__all__ = [ + 'Distribution', + 'DistributionFinder', + 'PackageMetadata', + 'PackageNotFoundError', + 'distribution', + 'distributions', + 'entry_points', + 'files', + 'metadata', + 'packages_distributions', + 'requires', + 'version', +] + + +class PackageNotFoundError(ModuleNotFoundError): + """The package was not found.""" + + def __str__(self): + return f"No package metadata was found for {self.name}" + + @property + def name(self): + (name,) = self.args + return name + + +class Sectioned: + """ + A simple entry point config parser for performance + + >>> for item in Sectioned.read(Sectioned._sample): + ... print(item) + Pair(name='sec1', value='# comments ignored') + Pair(name='sec1', value='a = 1') + Pair(name='sec1', value='b = 2') + Pair(name='sec2', value='a = 2') + + >>> res = Sectioned.section_pairs(Sectioned._sample) + >>> item = next(res) + >>> item.name + 'sec1' + >>> item.value + Pair(name='a', value='1') + >>> item = next(res) + >>> item.value + Pair(name='b', value='2') + >>> item = next(res) + >>> item.name + 'sec2' + >>> item.value + Pair(name='a', value='2') + >>> list(res) + [] + """ + + _sample = textwrap.dedent( + """ + [sec1] + # comments ignored + a = 1 + b = 2 + + [sec2] + a = 2 + """ + ).lstrip() + + @classmethod + def section_pairs(cls, text): + return ( + section._replace(value=Pair.parse(section.value)) + for section in cls.read(text, filter_=cls.valid) + if section.name is not None + ) + + @staticmethod + def read(text, filter_=None): + lines = filter(filter_, map(str.strip, text.splitlines())) + name = None + for value in lines: + section_match = value.startswith('[') and value.endswith(']') + if section_match: + name = value.strip('[]') + continue + yield Pair(name, value) + + @staticmethod + def valid(line): + return line and not line.startswith('#') + + +class DeprecatedTuple: + """ + Provide subscript item access for backward compatibility. + + >>> recwarn = getfixture('recwarn') + >>> ep = EntryPoint(name='name', value='value', group='group') + >>> ep[:] + ('name', 'value', 'group') + >>> ep[0] + 'name' + >>> len(recwarn) + 1 + """ + + # Do not remove prior to 2023-05-01 or Python 3.13 + _warn = functools.partial( + warnings.warn, + "EntryPoint tuple interface is deprecated. Access members by name.", + DeprecationWarning, + stacklevel=pypy_partial(2), + ) + + def __getitem__(self, item): + self._warn() + return self._key()[item] + + +class EntryPoint(DeprecatedTuple): + """An entry point as defined by Python packaging conventions. + + See `the packaging docs on entry points + `_ + for more information. + + >>> ep = EntryPoint( + ... name=None, group=None, value='package.module:attr [extra1, extra2]') + >>> ep.module + 'package.module' + >>> ep.attr + 'attr' + >>> ep.extras + ['extra1', 'extra2'] + """ + + pattern = re.compile( + r'(?P[\w.]+)\s*' + r'(:\s*(?P[\w.]+)\s*)?' + r'((?P\[.*\])\s*)?$' + ) + """ + A regular expression describing the syntax for an entry point, + which might look like: + + - module + - package.module + - package.module:attribute + - package.module:object.attribute + - package.module:attr [extra1, extra2] + + Other combinations are possible as well. + + The expression is lenient about whitespace around the ':', + following the attr, and following any extras. + """ + + name: str + value: str + group: str + + dist: Optional['Distribution'] = None + + def __init__(self, name, value, group): + vars(self).update(name=name, value=value, group=group) + + def load(self): + """Load the entry point from its definition. If only a module + is indicated by the value, return that module. Otherwise, + return the named object. + """ + match = self.pattern.match(self.value) + module = import_module(match.group('module')) + attrs = filter(None, (match.group('attr') or '').split('.')) + return functools.reduce(getattr, attrs, module) + + @property + def module(self): + match = self.pattern.match(self.value) + return match.group('module') + + @property + def attr(self): + match = self.pattern.match(self.value) + return match.group('attr') + + @property + def extras(self): + match = self.pattern.match(self.value) + return re.findall(r'\w+', match.group('extras') or '') + + def _for(self, dist): + vars(self).update(dist=dist) + return self + + def matches(self, **params): + """ + EntryPoint matches the given parameters. + + >>> ep = EntryPoint(group='foo', name='bar', value='bing:bong [extra1, extra2]') + >>> ep.matches(group='foo') + True + >>> ep.matches(name='bar', value='bing:bong [extra1, extra2]') + True + >>> ep.matches(group='foo', name='other') + False + >>> ep.matches() + True + >>> ep.matches(extras=['extra1', 'extra2']) + True + >>> ep.matches(module='bing') + True + >>> ep.matches(attr='bong') + True + """ + attrs = (getattr(self, param) for param in params) + return all(map(operator.eq, params.values(), attrs)) + + def _key(self): + return self.name, self.value, self.group + + def __lt__(self, other): + return self._key() < other._key() + + def __eq__(self, other): + return self._key() == other._key() + + def __setattr__(self, name, value): + raise AttributeError("EntryPoint objects are immutable.") + + def __repr__(self): + return ( + f'EntryPoint(name={self.name!r}, value={self.value!r}, ' + f'group={self.group!r})' + ) + + def __hash__(self): + return hash(self._key()) + + +class EntryPoints(tuple): + """ + An immutable collection of selectable EntryPoint objects. + """ + + __slots__ = () + + def __getitem__(self, name): # -> EntryPoint: + """ + Get the EntryPoint in self matching name. + """ + try: + return next(iter(self.select(name=name))) + except StopIteration: + raise KeyError(name) + + def select(self, **params): + """ + Select entry points from self that match the + given parameters (typically group and/or name). + """ + return EntryPoints(ep for ep in self if _py39compat.ep_matches(ep, **params)) + + @property + def names(self): + """ + Return the set of all names of all entry points. + """ + return {ep.name for ep in self} + + @property + def groups(self): + """ + Return the set of all groups of all entry points. + """ + return {ep.group for ep in self} + + @classmethod + def _from_text_for(cls, text, dist): + return cls(ep._for(dist) for ep in cls._from_text(text)) + + @staticmethod + def _from_text(text): + return ( + EntryPoint(name=item.value.name, value=item.value.value, group=item.name) + for item in Sectioned.section_pairs(text or '') + ) + + +class PackagePath(pathlib.PurePosixPath): + """A reference to a path in a package""" + + def read_text(self, encoding='utf-8'): + with self.locate().open(encoding=encoding) as stream: + return stream.read() + + def read_binary(self): + with self.locate().open('rb') as stream: + return stream.read() + + def locate(self): + """Return a path-like object for this path""" + return self.dist.locate_file(self) + + +class FileHash: + def __init__(self, spec): + self.mode, _, self.value = spec.partition('=') + + def __repr__(self): + return f'' + + +class Distribution(metaclass=abc.ABCMeta): + """A Python distribution package.""" + + @abc.abstractmethod + def read_text(self, filename): + """Attempt to load metadata file given by the name. + + :param filename: The name of the file in the distribution info. + :return: The text if found, otherwise None. + """ + + @abc.abstractmethod + def locate_file(self, path): + """ + Given a path to a file in this distribution, return a path + to it. + """ + + @classmethod + def from_name(cls, name: str): + """Return the Distribution for the given package name. + + :param name: The name of the distribution package to search for. + :return: The Distribution instance (or subclass thereof) for the named + package, if found. + :raises PackageNotFoundError: When the named package's distribution + metadata cannot be found. + :raises ValueError: When an invalid value is supplied for name. + """ + if not name: + raise ValueError("A distribution name is required.") + try: + return next(cls.discover(name=name)) + except StopIteration: + raise PackageNotFoundError(name) + + @classmethod + def discover(cls, **kwargs): + """Return an iterable of Distribution objects for all packages. + + Pass a ``context`` or pass keyword arguments for constructing + a context. + + :context: A ``DistributionFinder.Context`` object. + :return: Iterable of Distribution objects for all packages. + """ + context = kwargs.pop('context', None) + if context and kwargs: + raise ValueError("cannot accept context and kwargs") + context = context or DistributionFinder.Context(**kwargs) + return itertools.chain.from_iterable( + resolver(context) for resolver in cls._discover_resolvers() + ) + + @staticmethod + def at(path): + """Return a Distribution for the indicated metadata path + + :param path: a string or path-like object + :return: a concrete Distribution instance for the path + """ + return PathDistribution(pathlib.Path(path)) + + @staticmethod + def _discover_resolvers(): + """Search the meta_path for resolvers.""" + declared = ( + getattr(finder, 'find_distributions', None) for finder in sys.meta_path + ) + return filter(None, declared) + + @property + def metadata(self) -> _meta.PackageMetadata: + """Return the parsed metadata for this Distribution. + + The returned object will have keys that name the various bits of + metadata. See PEP 566 for details. + """ + text = ( + self.read_text('METADATA') + or self.read_text('PKG-INFO') + # This last clause is here to support old egg-info files. Its + # effect is to just end up using the PathDistribution's self._path + # (which points to the egg-info file) attribute unchanged. + or self.read_text('') + ) + return _adapters.Message(email.message_from_string(text)) + + @property + def name(self): + """Return the 'Name' metadata for the distribution package.""" + return self.metadata['Name'] + + @property + def _normalized_name(self): + """Return a normalized version of the name.""" + return Prepared.normalize(self.name) + + @property + def version(self): + """Return the 'Version' metadata for the distribution package.""" + return self.metadata['Version'] + + @property + def entry_points(self): + return EntryPoints._from_text_for(self.read_text('entry_points.txt'), self) + + @property + def files(self): + """Files in this distribution. + + :return: List of PackagePath for this distribution or None + + Result is `None` if the metadata file that enumerates files + (i.e. RECORD for dist-info or SOURCES.txt for egg-info) is + missing. + Result may be empty if the metadata exists but is empty. + """ + + def make_file(name, hash=None, size_str=None): + result = PackagePath(name) + result.hash = FileHash(hash) if hash else None + result.size = int(size_str) if size_str else None + result.dist = self + return result + + @pass_none + def make_files(lines): + return list(starmap(make_file, csv.reader(lines))) + + return make_files(self._read_files_distinfo() or self._read_files_egginfo()) + + def _read_files_distinfo(self): + """ + Read the lines of RECORD + """ + text = self.read_text('RECORD') + return text and text.splitlines() + + def _read_files_egginfo(self): + """ + SOURCES.txt might contain literal commas, so wrap each line + in quotes. + """ + text = self.read_text('SOURCES.txt') + return text and map('"{}"'.format, text.splitlines()) + + @property + def requires(self): + """Generated requirements specified for this Distribution""" + reqs = self._read_dist_info_reqs() or self._read_egg_info_reqs() + return reqs and list(reqs) + + def _read_dist_info_reqs(self): + return self.metadata.get_all('Requires-Dist') + + def _read_egg_info_reqs(self): + source = self.read_text('requires.txt') + return pass_none(self._deps_from_requires_text)(source) + + @classmethod + def _deps_from_requires_text(cls, source): + return cls._convert_egg_info_reqs_to_simple_reqs(Sectioned.read(source)) + + @staticmethod + def _convert_egg_info_reqs_to_simple_reqs(sections): + """ + Historically, setuptools would solicit and store 'extra' + requirements, including those with environment markers, + in separate sections. More modern tools expect each + dependency to be defined separately, with any relevant + extras and environment markers attached directly to that + requirement. This method converts the former to the + latter. See _test_deps_from_requires_text for an example. + """ + + def make_condition(name): + return name and f'extra == "{name}"' + + def quoted_marker(section): + section = section or '' + extra, sep, markers = section.partition(':') + if extra and markers: + markers = f'({markers})' + conditions = list(filter(None, [markers, make_condition(extra)])) + return '; ' + ' and '.join(conditions) if conditions else '' + + def url_req_space(req): + """ + PEP 508 requires a space between the url_spec and the quoted_marker. + Ref python/importlib_metadata#357. + """ + # '@' is uniquely indicative of a url_req. + return ' ' * ('@' in req) + + for section in sections: + space = url_req_space(section.value) + yield section.value + space + quoted_marker(section.name) + + +class DistributionFinder(MetaPathFinder): + """ + A MetaPathFinder capable of discovering installed distributions. + """ + + class Context: + """ + Keyword arguments presented by the caller to + ``distributions()`` or ``Distribution.discover()`` + to narrow the scope of a search for distributions + in all DistributionFinders. + + Each DistributionFinder may expect any parameters + and should attempt to honor the canonical + parameters defined below when appropriate. + """ + + name = None + """ + Specific name for which a distribution finder should match. + A name of ``None`` matches all distributions. + """ + + def __init__(self, **kwargs): + vars(self).update(kwargs) + + @property + def path(self): + """ + The sequence of directory path that a distribution finder + should search. + + Typically refers to Python installed package paths such as + "site-packages" directories and defaults to ``sys.path``. + """ + return vars(self).get('path', sys.path) + + @abc.abstractmethod + def find_distributions(self, context=Context()): + """ + Find distributions. + + Return an iterable of all Distribution instances capable of + loading the metadata for packages matching the ``context``, + a DistributionFinder.Context instance. + """ + + +class FastPath: + """ + Micro-optimized class for searching a path for + children. + + >>> FastPath('').children() + ['...'] + """ + + @functools.lru_cache() # type: ignore + def __new__(cls, root): + return super().__new__(cls) + + def __init__(self, root): + self.root = root + + def joinpath(self, child): + return pathlib.Path(self.root, child) + + def children(self): + with suppress(Exception): + return os.listdir(self.root or '.') + with suppress(Exception): + return self.zip_children() + return [] + + def zip_children(self): + zip_path = zipp.Path(self.root) + names = zip_path.root.namelist() + self.joinpath = zip_path.joinpath + + return dict.fromkeys(child.split(posixpath.sep, 1)[0] for child in names) + + def search(self, name): + return self.lookup(self.mtime).search(name) + + @property + def mtime(self): + with suppress(OSError): + return os.stat(self.root).st_mtime + self.lookup.cache_clear() + + @method_cache + def lookup(self, mtime): + return Lookup(self) + + +class Lookup: + def __init__(self, path: FastPath): + base = os.path.basename(path.root).lower() + base_is_egg = base.endswith(".egg") + self.infos = FreezableDefaultDict(list) + self.eggs = FreezableDefaultDict(list) + + for child in path.children(): + low = child.lower() + if low.endswith((".dist-info", ".egg-info")): + # rpartition is faster than splitext and suitable for this purpose. + name = low.rpartition(".")[0].partition("-")[0] + normalized = Prepared.normalize(name) + self.infos[normalized].append(path.joinpath(child)) + elif base_is_egg and low == "egg-info": + name = base.rpartition(".")[0].partition("-")[0] + legacy_normalized = Prepared.legacy_normalize(name) + self.eggs[legacy_normalized].append(path.joinpath(child)) + + self.infos.freeze() + self.eggs.freeze() + + def search(self, prepared): + infos = ( + self.infos[prepared.normalized] + if prepared + else itertools.chain.from_iterable(self.infos.values()) + ) + eggs = ( + self.eggs[prepared.legacy_normalized] + if prepared + else itertools.chain.from_iterable(self.eggs.values()) + ) + return itertools.chain(infos, eggs) + + +class Prepared: + """ + A prepared search for metadata on a possibly-named package. + """ + + normalized = None + legacy_normalized = None + + def __init__(self, name): + self.name = name + if name is None: + return + self.normalized = self.normalize(name) + self.legacy_normalized = self.legacy_normalize(name) + + @staticmethod + def normalize(name): + """ + PEP 503 normalization plus dashes as underscores. + """ + return re.sub(r"[-_.]+", "-", name).lower().replace('-', '_') + + @staticmethod + def legacy_normalize(name): + """ + Normalize the package name as found in the convention in + older packaging tools versions and specs. + """ + return name.lower().replace('-', '_') + + def __bool__(self): + return bool(self.name) + + +@install +class MetadataPathFinder(NullFinder, DistributionFinder): + """A degenerate finder for distribution packages on the file system. + + This finder supplies only a find_distributions() method for versions + of Python that do not have a PathFinder find_distributions(). + """ + + def find_distributions(self, context=DistributionFinder.Context()): + """ + Find distributions. + + Return an iterable of all Distribution instances capable of + loading the metadata for packages matching ``context.name`` + (or all names if ``None`` indicated) along the paths in the list + of directories ``context.path``. + """ + found = self._search_paths(context.name, context.path) + return map(PathDistribution, found) + + @classmethod + def _search_paths(cls, name, paths): + """Find metadata directories in paths heuristically.""" + prepared = Prepared(name) + return itertools.chain.from_iterable( + path.search(prepared) for path in map(FastPath, paths) + ) + + def invalidate_caches(cls): + FastPath.__new__.cache_clear() + + +class PathDistribution(Distribution): + def __init__(self, path: SimplePath): + """Construct a distribution. + + :param path: SimplePath indicating the metadata directory. + """ + self._path = path + + def read_text(self, filename): + with suppress( + FileNotFoundError, + IsADirectoryError, + KeyError, + NotADirectoryError, + PermissionError, + ): + return self._path.joinpath(filename).read_text(encoding='utf-8') + + read_text.__doc__ = Distribution.read_text.__doc__ + + def locate_file(self, path): + return self._path.parent / path + + @property + def _normalized_name(self): + """ + Performance optimization: where possible, resolve the + normalized name from the file system path. + """ + stem = os.path.basename(str(self._path)) + return ( + pass_none(Prepared.normalize)(self._name_from_stem(stem)) + or super()._normalized_name + ) + + @staticmethod + def _name_from_stem(stem): + """ + >>> PathDistribution._name_from_stem('foo-3.0.egg-info') + 'foo' + >>> PathDistribution._name_from_stem('CherryPy-3.0.dist-info') + 'CherryPy' + >>> PathDistribution._name_from_stem('face.egg-info') + 'face' + >>> PathDistribution._name_from_stem('foo.bar') + """ + filename, ext = os.path.splitext(stem) + if ext not in ('.dist-info', '.egg-info'): + return + name, sep, rest = filename.partition('-') + return name + + +def distribution(distribution_name): + """Get the ``Distribution`` instance for the named package. + + :param distribution_name: The name of the distribution package as a string. + :return: A ``Distribution`` instance (or subclass thereof). + """ + return Distribution.from_name(distribution_name) + + +def distributions(**kwargs): + """Get all ``Distribution`` instances in the current environment. + + :return: An iterable of ``Distribution`` instances. + """ + return Distribution.discover(**kwargs) + + +def metadata(distribution_name) -> _meta.PackageMetadata: + """Get the metadata for the named package. + + :param distribution_name: The name of the distribution package to query. + :return: A PackageMetadata containing the parsed metadata. + """ + return Distribution.from_name(distribution_name).metadata + + +def version(distribution_name): + """Get the version string for the named package. + + :param distribution_name: The name of the distribution package to query. + :return: The version string for the package as defined in the package's + "Version" metadata key. + """ + return distribution(distribution_name).version + + +_unique = functools.partial( + unique_everseen, + key=_py39compat.normalized_name, +) +""" +Wrapper for ``distributions`` to return unique distributions by name. +""" + + +def entry_points(**params) -> EntryPoints: + """Return EntryPoint objects for all installed packages. + + Pass selection parameters (group or name) to filter the + result to entry points matching those properties (see + EntryPoints.select()). + + :return: EntryPoints for all installed packages. + """ + eps = itertools.chain.from_iterable( + dist.entry_points for dist in _unique(distributions()) + ) + return EntryPoints(eps).select(**params) + + +def files(distribution_name): + """Return a list of files for the named package. + + :param distribution_name: The name of the distribution package to query. + :return: List of files composing the distribution. + """ + return distribution(distribution_name).files + + +def requires(distribution_name): + """ + Return a list of requirements for the named package. + + :return: An iterator of requirements, suitable for + packaging.requirement.Requirement. + """ + return distribution(distribution_name).requires + + +def packages_distributions() -> Mapping[str, List[str]]: + """ + Return a mapping of top-level packages to their + distributions. + + >>> import collections.abc + >>> pkgs = packages_distributions() + >>> all(isinstance(dist, collections.abc.Sequence) for dist in pkgs.values()) + True + """ + pkg_to_dist = collections.defaultdict(list) + for dist in distributions(): + for pkg in _top_level_declared(dist) or _top_level_inferred(dist): + pkg_to_dist[pkg].append(dist.metadata['Name']) + return dict(pkg_to_dist) + + +def _top_level_declared(dist): + return (dist.read_text('top_level.txt') or '').split() + + +def _top_level_inferred(dist): + return { + f.parts[0] if len(f.parts) > 1 else f.with_suffix('').name + for f in always_iterable(dist.files) + if f.suffix == ".py" + } diff --git a/venv/Lib/site-packages/setuptools/_vendor/importlib_metadata/__pycache__/__init__.cpython-312.pyc b/venv/Lib/site-packages/setuptools/_vendor/importlib_metadata/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..07cdfbf6be26ea45017644605381b898da49f1d6 GIT binary patch literal 45262 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 literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/setuptools/_vendor/importlib_metadata/__pycache__/_adapters.cpython-312.pyc b/venv/Lib/site-packages/setuptools/_vendor/importlib_metadata/__pycache__/_adapters.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..9ba099901c4d7852a4366bc4f7067974a5b4a988 GIT binary patch literal 3972 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 literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/setuptools/_vendor/importlib_metadata/__pycache__/_meta.cpython-312.pyc b/venv/Lib/site-packages/setuptools/_vendor/importlib_metadata/__pycache__/_meta.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..f21e6e261a3d38efdb3bae9ebf747a34cef7316b GIT binary patch literal 2725 zcmbVOO>7%Q6rTO{`X}j6(zMVPmKH%Ra02~xPmjX8o64_dCaMi}h zXH35f%x%I*RidOSQ;8`JnaazM4po>2bG@Nb73MjriMa-I9p(n;@=U{UVMPuk1v&;P zXf)4?pewO4HjY|WY{Rl5{1 z9tm1IA380c1#wh67g9I4Qk#e4Y;jhZZ*@8ztK4Aq$c^^_wA&J-2T8S+FyLn{Kv~oMm||u&mDhE%{7m3cQ!T2IMCB!zlKF zA&CkeeMd%qWNb>5?*X z{kT%O#@bKn{va&eTL=vjf|m zr5ao^lBOEiK1*^9+x5cwRVQmtut~@0c}r!vGhZ;X@rkHJ)Al@b$@W?-GVLe|9oLQ- zHE+1_qG_AWFpAu|*Eao@7rPZA^O0>HR6`{h8pEzW^jc$KWYVD+lLffv4>S zlqvRsbsz8jtMvAEJV4Cj%picX<_8x)o~t;a-?U>0mYI!^`)1fM<3(m}6!X@Ax1GFt zH41~y9Y_Qe-Q~5)?8VdE>9$IQ+V>0l@u(?4{6Io9?JE4%l0yj;s!Ct)Dk6% z0pse>aa`Qr>6tyPV|spog9i*A|dKVBcfy76W>?Fd`(wihjTpwpVcGcbFX!O z;pd}2eg2Dl|MG)3KJ25aS0MU$PX_K5T{+R!y3Xp!d&-?N-QabHZZ9!#Bngm!%e z?AFjOjU3~3;5QhEeK`7`0U1U1szPO^0#+-4)f!cq&T=?M%oQ3Ho+iL`me1?}`C{8B=aa%k06LG&+wo%OJyAnvP2UY%-}cO?RR>_i%7IE+1Sp(FGn2FHEtf-6 z)Y~Qs&cjQ^-FhpA7b-0)J`Ta`Sh8fNp1&Hp0eb9A41=5A5Q-}wqM8;$Rr4pswc`-q zR$PmDi&1w8pk`{stz`}5Wmq%>nx{@rZ`vGQs?MGfFRAENj4FLK{x*uDFrL~=o->B$z0^aWr%+EA7SCQ7#(t735ALsUSIxqe{ZbZ`9loM^y%?_?^`NoXrS!d?|EBN|zB@XCpG zl&0;2ejo=3I)y!sXq)J9y9xgVQuq*fArMAD3ck>3LIP_9Xo!$Er}5aBO|k54J_{@G zWr|J#=_!&V{Yei0MP`2|vuirh$G%zk`tnVAU7wOlw-(n4eAbWe2L8axk?#)uM}QTv F{txQNVo3l1 literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/setuptools/_vendor/importlib_metadata/__pycache__/_py39compat.cpython-312.pyc b/venv/Lib/site-packages/setuptools/_vendor/importlib_metadata/__pycache__/_py39compat.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..d348e8f83ceb546478c9ee34ff285736ff6f4fde GIT binary patch literal 1673 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 HRSW$W8x&-) literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/setuptools/_vendor/importlib_metadata/_adapters.py b/venv/Lib/site-packages/setuptools/_vendor/importlib_metadata/_adapters.py new file mode 100644 index 0000000..e33cba5 --- /dev/null +++ b/venv/Lib/site-packages/setuptools/_vendor/importlib_metadata/_adapters.py @@ -0,0 +1,90 @@ +import functools +import warnings +import re +import textwrap +import email.message + +from ._text import FoldedCase +from ._compat import pypy_partial + + +# Do not remove prior to 2024-01-01 or Python 3.14 +_warn = functools.partial( + warnings.warn, + "Implicit None on return values is deprecated and will raise KeyErrors.", + DeprecationWarning, + stacklevel=pypy_partial(2), +) + + +class Message(email.message.Message): + multiple_use_keys = set( + map( + FoldedCase, + [ + 'Classifier', + 'Obsoletes-Dist', + 'Platform', + 'Project-URL', + 'Provides-Dist', + 'Provides-Extra', + 'Requires-Dist', + 'Requires-External', + 'Supported-Platform', + 'Dynamic', + ], + ) + ) + """ + Keys that may be indicated multiple times per PEP 566. + """ + + def __new__(cls, orig: email.message.Message): + res = super().__new__(cls) + vars(res).update(vars(orig)) + return res + + def __init__(self, *args, **kwargs): + self._headers = self._repair_headers() + + # suppress spurious error from mypy + def __iter__(self): + return super().__iter__() + + def __getitem__(self, item): + """ + Warn users that a ``KeyError`` can be expected when a + mising key is supplied. Ref python/importlib_metadata#371. + """ + res = super().__getitem__(item) + if res is None: + _warn() + return res + + def _repair_headers(self): + def redent(value): + "Correct for RFC822 indentation" + if not value or '\n' not in value: + return value + return textwrap.dedent(' ' * 8 + value) + + headers = [(key, redent(value)) for key, value in vars(self)['_headers']] + if self._payload: + headers.append(('Description', self.get_payload())) + return headers + + @property + def json(self): + """ + Convert PackageMetadata to a JSON-compatible format + per PEP 0566. + """ + + def transform(key): + value = self.get_all(key) if key in self.multiple_use_keys else self[key] + if key == 'Keywords': + value = re.split(r'\s+', value) + tk = key.lower().replace('-', '_') + return tk, value + + return dict(map(transform, map(FoldedCase, self))) diff --git a/venv/Lib/site-packages/setuptools/_vendor/importlib_metadata/_collections.py b/venv/Lib/site-packages/setuptools/_vendor/importlib_metadata/_collections.py new file mode 100644 index 0000000..cf0954e --- /dev/null +++ b/venv/Lib/site-packages/setuptools/_vendor/importlib_metadata/_collections.py @@ -0,0 +1,30 @@ +import collections + + +# from jaraco.collections 3.3 +class FreezableDefaultDict(collections.defaultdict): + """ + Often it is desirable to prevent the mutation of + a default dict after its initial construction, such + as to prevent mutation during iteration. + + >>> dd = FreezableDefaultDict(list) + >>> dd[0].append('1') + >>> dd.freeze() + >>> dd[1] + [] + >>> len(dd) + 1 + """ + + def __missing__(self, key): + return getattr(self, '_frozen', super().__missing__)(key) + + def freeze(self): + self._frozen = lambda key: self.default_factory() + + +class Pair(collections.namedtuple('Pair', 'name value')): + @classmethod + def parse(cls, text): + return cls(*map(str.strip, text.split("=", 1))) diff --git a/venv/Lib/site-packages/setuptools/_vendor/importlib_metadata/_compat.py b/venv/Lib/site-packages/setuptools/_vendor/importlib_metadata/_compat.py new file mode 100644 index 0000000..84f9eea --- /dev/null +++ b/venv/Lib/site-packages/setuptools/_vendor/importlib_metadata/_compat.py @@ -0,0 +1,72 @@ +import sys +import platform + + +__all__ = ['install', 'NullFinder', 'Protocol'] + + +try: + from typing import Protocol +except ImportError: # pragma: no cover + # Python 3.7 compatibility + from ..typing_extensions import Protocol # type: ignore + + +def install(cls): + """ + Class decorator for installation on sys.meta_path. + + Adds the backport DistributionFinder to sys.meta_path and + attempts to disable the finder functionality of the stdlib + DistributionFinder. + """ + sys.meta_path.append(cls()) + disable_stdlib_finder() + return cls + + +def disable_stdlib_finder(): + """ + Give the backport primacy for discovering path-based distributions + by monkey-patching the stdlib O_O. + + See #91 for more background for rationale on this sketchy + behavior. + """ + + def matches(finder): + return getattr( + finder, '__module__', None + ) == '_frozen_importlib_external' and hasattr(finder, 'find_distributions') + + for finder in filter(matches, sys.meta_path): # pragma: nocover + del finder.find_distributions + + +class NullFinder: + """ + A "Finder" (aka "MetaClassFinder") that never finds any modules, + but may find distributions. + """ + + @staticmethod + def find_spec(*args, **kwargs): + return None + + # In Python 2, the import system requires finders + # to have a find_module() method, but this usage + # is deprecated in Python 3 in favor of find_spec(). + # For the purposes of this finder (i.e. being present + # on sys.meta_path but having no other import + # system functionality), the two methods are identical. + find_module = find_spec + + +def pypy_partial(val): + """ + Adjust for variable stacklevel on partial under PyPy. + + Workaround for #327. + """ + is_pypy = platform.python_implementation() == 'PyPy' + return val + is_pypy diff --git a/venv/Lib/site-packages/setuptools/_vendor/importlib_metadata/_functools.py b/venv/Lib/site-packages/setuptools/_vendor/importlib_metadata/_functools.py new file mode 100644 index 0000000..71f66bd --- /dev/null +++ b/venv/Lib/site-packages/setuptools/_vendor/importlib_metadata/_functools.py @@ -0,0 +1,104 @@ +import types +import functools + + +# from jaraco.functools 3.3 +def method_cache(method, cache_wrapper=None): + """ + Wrap lru_cache to support storing the cache data in the object instances. + + Abstracts the common paradigm where the method explicitly saves an + underscore-prefixed protected property on first call and returns that + subsequently. + + >>> class MyClass: + ... calls = 0 + ... + ... @method_cache + ... def method(self, value): + ... self.calls += 1 + ... return value + + >>> a = MyClass() + >>> a.method(3) + 3 + >>> for x in range(75): + ... res = a.method(x) + >>> a.calls + 75 + + Note that the apparent behavior will be exactly like that of lru_cache + except that the cache is stored on each instance, so values in one + instance will not flush values from another, and when an instance is + deleted, so are the cached values for that instance. + + >>> b = MyClass() + >>> for x in range(35): + ... res = b.method(x) + >>> b.calls + 35 + >>> a.method(0) + 0 + >>> a.calls + 75 + + Note that if method had been decorated with ``functools.lru_cache()``, + a.calls would have been 76 (due to the cached value of 0 having been + flushed by the 'b' instance). + + Clear the cache with ``.cache_clear()`` + + >>> a.method.cache_clear() + + Same for a method that hasn't yet been called. + + >>> c = MyClass() + >>> c.method.cache_clear() + + Another cache wrapper may be supplied: + + >>> cache = functools.lru_cache(maxsize=2) + >>> MyClass.method2 = method_cache(lambda self: 3, cache_wrapper=cache) + >>> a = MyClass() + >>> a.method2() + 3 + + Caution - do not subsequently wrap the method with another decorator, such + as ``@property``, which changes the semantics of the function. + + See also + http://code.activestate.com/recipes/577452-a-memoize-decorator-for-instance-methods/ + for another implementation and additional justification. + """ + cache_wrapper = cache_wrapper or functools.lru_cache() + + def wrapper(self, *args, **kwargs): + # it's the first call, replace the method with a cached, bound method + bound_method = types.MethodType(method, self) + cached_method = cache_wrapper(bound_method) + setattr(self, method.__name__, cached_method) + return cached_method(*args, **kwargs) + + # Support cache clear even before cache has been created. + wrapper.cache_clear = lambda: None + + return wrapper + + +# From jaraco.functools 3.3 +def pass_none(func): + """ + Wrap func so it's not called if its first param is None + + >>> print_text = pass_none(print) + >>> print_text('text') + text + >>> print_text(None) + """ + + @functools.wraps(func) + def wrapper(param, *args, **kwargs): + if param is not None: + return func(param, *args, **kwargs) + + return wrapper diff --git a/venv/Lib/site-packages/setuptools/_vendor/importlib_metadata/_itertools.py b/venv/Lib/site-packages/setuptools/_vendor/importlib_metadata/_itertools.py new file mode 100644 index 0000000..d4ca9b9 --- /dev/null +++ b/venv/Lib/site-packages/setuptools/_vendor/importlib_metadata/_itertools.py @@ -0,0 +1,73 @@ +from itertools import filterfalse + + +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 + + +# copied from more_itertools 8.8 +def always_iterable(obj, base_type=(str, bytes)): + """If *obj* is iterable, return an iterator over its items:: + + >>> obj = (1, 2, 3) + >>> list(always_iterable(obj)) + [1, 2, 3] + + If *obj* is not iterable, return a one-item iterable containing *obj*:: + + >>> obj = 1 + >>> list(always_iterable(obj)) + [1] + + If *obj* is ``None``, return an empty iterable: + + >>> obj = None + >>> list(always_iterable(None)) + [] + + By default, binary and text strings are not considered iterable:: + + >>> obj = 'foo' + >>> list(always_iterable(obj)) + ['foo'] + + If *base_type* is set, objects for which ``isinstance(obj, base_type)`` + returns ``True`` won't be considered iterable. + + >>> obj = {'a': 1} + >>> list(always_iterable(obj)) # Iterate over the dict's keys + ['a'] + >>> list(always_iterable(obj, base_type=dict)) # Treat dicts as a unit + [{'a': 1}] + + Set *base_type* to ``None`` to avoid any special handling and treat objects + Python considers iterable as iterable: + + >>> obj = 'foo' + >>> list(always_iterable(obj, base_type=None)) + ['f', 'o', 'o'] + """ + if obj is None: + return iter(()) + + if (base_type is not None) and isinstance(obj, base_type): + return iter((obj,)) + + try: + return iter(obj) + except TypeError: + return iter((obj,)) diff --git a/venv/Lib/site-packages/setuptools/_vendor/importlib_metadata/_meta.py b/venv/Lib/site-packages/setuptools/_vendor/importlib_metadata/_meta.py new file mode 100644 index 0000000..259b15b --- /dev/null +++ b/venv/Lib/site-packages/setuptools/_vendor/importlib_metadata/_meta.py @@ -0,0 +1,49 @@ +from ._compat import Protocol +from typing import Any, Dict, Iterator, List, TypeVar, Union + + +_T = TypeVar("_T") + + +class PackageMetadata(Protocol): + def __len__(self) -> int: + ... # pragma: no cover + + def __contains__(self, item: str) -> bool: + ... # pragma: no cover + + def __getitem__(self, key: str) -> str: + ... # pragma: no cover + + def __iter__(self) -> Iterator[str]: + ... # pragma: no cover + + def get_all(self, name: str, failobj: _T = ...) -> Union[List[Any], _T]: + """ + Return all values associated with a possibly multi-valued key. + """ + + @property + def json(self) -> Dict[str, Union[str, List[str]]]: + """ + A JSON-compatible form of the metadata. + """ + + +class SimplePath(Protocol[_T]): + """ + A minimal subset of pathlib.Path required by PathDistribution. + """ + + def joinpath(self) -> _T: + ... # pragma: no cover + + def __truediv__(self, other: Union[str, _T]) -> _T: + ... # pragma: no cover + + @property + def parent(self) -> _T: + ... # pragma: no cover + + def read_text(self) -> str: + ... # pragma: no cover diff --git a/venv/Lib/site-packages/setuptools/_vendor/importlib_metadata/_py39compat.py b/venv/Lib/site-packages/setuptools/_vendor/importlib_metadata/_py39compat.py new file mode 100644 index 0000000..cde4558 --- /dev/null +++ b/venv/Lib/site-packages/setuptools/_vendor/importlib_metadata/_py39compat.py @@ -0,0 +1,35 @@ +""" +Compatibility layer with Python 3.8/3.9 +""" +from typing import TYPE_CHECKING, Any, Optional + +if TYPE_CHECKING: # pragma: no cover + # Prevent circular imports on runtime. + from . import Distribution, EntryPoint +else: + Distribution = EntryPoint = Any + + +def normalized_name(dist: Distribution) -> Optional[str]: + """ + Honor name normalization for distributions that don't provide ``_normalized_name``. + """ + try: + return dist._normalized_name + except AttributeError: + from . import Prepared # -> delay to prevent circular imports. + + return Prepared.normalize(getattr(dist, "name", None) or dist.metadata['Name']) + + +def ep_matches(ep: EntryPoint, **params) -> bool: + """ + Workaround for ``EntryPoint`` objects without the ``matches`` method. + """ + try: + return ep.matches(**params) + except AttributeError: + from . import EntryPoint # -> delay to prevent circular imports. + + # Reconstruct the EntryPoint object to make sure it is compatible. + return EntryPoint(ep.name, ep.value, ep.group).matches(**params) diff --git a/venv/Lib/site-packages/setuptools/_vendor/importlib_metadata/_text.py b/venv/Lib/site-packages/setuptools/_vendor/importlib_metadata/_text.py new file mode 100644 index 0000000..c88cfbb --- /dev/null +++ b/venv/Lib/site-packages/setuptools/_vendor/importlib_metadata/_text.py @@ -0,0 +1,99 @@ +import re + +from ._functools import method_cache + + +# from jaraco.text 3.5 +class FoldedCase(str): + """ + A case insensitive string class; behaves just like str + except compares equal when the only variation is case. + + >>> s = FoldedCase('hello world') + + >>> s == 'Hello World' + True + + >>> 'Hello World' == s + True + + >>> s != 'Hello World' + False + + >>> s.index('O') + 4 + + >>> s.split('O') + ['hell', ' w', 'rld'] + + >>> sorted(map(FoldedCase, ['GAMMA', 'alpha', 'Beta'])) + ['alpha', 'Beta', 'GAMMA'] + + Sequence membership is straightforward. + + >>> "Hello World" in [s] + True + >>> s in ["Hello World"] + True + + You may test for set inclusion, but candidate and elements + must both be folded. + + >>> FoldedCase("Hello World") in {s} + True + >>> s in {FoldedCase("Hello World")} + True + + String inclusion works as long as the FoldedCase object + is on the right. + + >>> "hello" in FoldedCase("Hello World") + True + + But not if the FoldedCase object is on the left: + + >>> FoldedCase('hello') in 'Hello World' + False + + In that case, use in_: + + >>> FoldedCase('hello').in_('Hello World') + True + + >>> FoldedCase('hello') > FoldedCase('Hello') + False + """ + + def __lt__(self, other): + return self.lower() < other.lower() + + def __gt__(self, other): + return self.lower() > other.lower() + + def __eq__(self, other): + return self.lower() == other.lower() + + def __ne__(self, other): + return self.lower() != other.lower() + + def __hash__(self): + return hash(self.lower()) + + def __contains__(self, other): + return super().lower().__contains__(other.lower()) + + def in_(self, other): + "Does self appear in other?" + return self in FoldedCase(other) + + # cache lower since it's likely to be called frequently. + @method_cache + def lower(self): + return super().lower() + + def index(self, sub): + return self.lower().index(sub.lower()) + + def split(self, splitter=' ', maxsplit=0): + pattern = re.compile(re.escape(splitter), re.I) + return pattern.split(self, maxsplit) diff --git a/venv/Lib/site-packages/setuptools/_vendor/importlib_metadata/py.typed b/venv/Lib/site-packages/setuptools/_vendor/importlib_metadata/py.typed new file mode 100644 index 0000000..e69de29 diff --git a/venv/Lib/site-packages/setuptools/_vendor/importlib_resources/__init__.py b/venv/Lib/site-packages/setuptools/_vendor/importlib_resources/__init__.py new file mode 100644 index 0000000..34e3a99 --- /dev/null +++ b/venv/Lib/site-packages/setuptools/_vendor/importlib_resources/__init__.py @@ -0,0 +1,36 @@ +"""Read resources contained within a package.""" + +from ._common import ( + as_file, + files, + Package, +) + +from ._legacy import ( + contents, + open_binary, + read_binary, + open_text, + read_text, + is_resource, + path, + Resource, +) + +from .abc import ResourceReader + + +__all__ = [ + 'Package', + 'Resource', + 'ResourceReader', + 'as_file', + 'contents', + 'files', + 'is_resource', + 'open_binary', + 'open_text', + 'path', + 'read_binary', + 'read_text', +] diff --git a/venv/Lib/site-packages/setuptools/_vendor/importlib_resources/__pycache__/__init__.cpython-312.pyc b/venv/Lib/site-packages/setuptools/_vendor/importlib_resources/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..7db7a64d3998e4e850eaf6ef24774441e87b022d GIT binary patch literal 691 zcmX|;KabNe7{=`+ZIY(x!2qmTgVZ}_gjkO8gsPK}kQK>F6JO6Fjw9Q-yLLf*3ijLh zEPMjIAtpAULML^?Z`$JFr}yvUNA}k=ogjDA_xaThBlH835{jlTH8dqOO8oAbPT&QHXiCjm~c&xE^N@1=GsP=2Uj_^vttZ) znaZbXLw zUYTC8`J&%!8aBUWOU(@{ZdiL$T<~Q9+5GSey3ow@osqK9XVi_1fzfTd){*2Sa K`xL4tZsQ+4Sir9U literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/setuptools/_vendor/importlib_resources/__pycache__/_adapters.cpython-312.pyc b/venv/Lib/site-packages/setuptools/_vendor/importlib_resources/__pycache__/_adapters.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..551d3e561b33d6ea081b77d03ebf0b23d02d6b00 GIT binary patch literal 9736 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 literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/setuptools/_vendor/importlib_resources/__pycache__/_compat.cpython-312.pyc b/venv/Lib/site-packages/setuptools/_vendor/importlib_resources/__pycache__/_compat.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..31a1391b456d9f432bca77eb4422512ab01dd870 GIT binary patch literal 5057 zcmcgwU2GiH6~1?V_Rrp3+v{X;oWw&Mf|rmrl>9U_g4j-z8b!v!0mR~Jy51SbV|I78 zcV_LZ<5Z!IgpHs{9>7UeDd|g_D3yRGD*8}`S6;j?h%Hh(KZg+%uf3BjjJ$@QPHc+1Uz(!4 zEu;kg6;mR7g{+j5Q!+&!G3(3uQ+`TxVYIj4B7N`yjNK>0i6gjoXO1qutk)4G3;A{cdC;SR|>$!1qN_>Tk)7luwrIS&`TXE%^ ziUN1a<4#29ai}jroq$y=6qs&VP?ulK8)iNs%!@EMkElSW)StFT=JANoXk^8_ZRB({ zGooi+OAlrB3;?rSu#R(Y`+&@lNismL)4uCCQ_N&73&)kEU-dja_@bpVi_^g)W~P|a z^R_kEV`@hJ^&!i!^}#VcKQ?&W7#hUZo9QuCBR6XNDW4`mufHTGe9o2= zEIkG%MvcAT$#mRIYdV|wB&0y;($WQ6$?`5su}!7G%rQgLm5pX9>Aa?x_K42-T=!_k zkdZa)QZmH*K*1WUVdT=x$d;60lPUcrd&JBuPbClX`FfgV4V@|Du#B$2VoE-38)G`3 z?g^rqO5U`Ul5Q)Y)vS&{Z75RGmBdpLDgo=<8k(R2)<7qTiv7Hl2!qQG=HWzU-LMSg@k90Ky)S@Kn5%kPtw#>4`l z;V$}leEVFyqa5$J*8E=U_15>b4~-9uO8oG*B94B`c@{Zcvd|q|+|@f7036^C-B zR?OHC`E(Dj-Ur5O{ZjjJx2J)4sQvk|O5;Ant>48h zS&k>K*>jzJ<<7oJ{CUKzpJUcf5wm`}#69i1tv?a}DtAeDLF`@F;r{mu@K{C`d z9N+)G{bA{YQYGGn=yY>*x;Z-CHFPdK@~rqbxht?19n>}VdwZdR_emhr!X)uL-5I*# z2PdgUJs-t{Njd;N3w*55=g7lGR>!!}Ka#f6;FJ<#?O}9%3)_Q7K%b>3HP;gXgT@%c zQ`gZSV9l%6c?LIn7zmj17n^onwJVz*pE~;0ww+gBy7uyC+xA`9KU@FoU-km|-)LZ2 z6@P^J?+7yx=C2%nqbOin+{jc{R5wXCdbGT-kTp&Vqv-0^m1)siVF28IJF%A^6bSi6 zd=N^+3*X)6z~}feihwYe;Wc4ffH=Vur9#d1`1DhTWm`_@D8S@;pM=B#;;j5#a3dE~ z0YFs4s9r@}J%o-tFhZ+V>4pug=Yaq|RY44I{VKkFX7b|X)gyoE{X=ghp1dGceWaoJ z{ORe_Z=KO~4m?VxgIY zTQBXsa`5uOO8p~?q%M45!AHUmU6QT@F9$32I~M)eRZqg(FKJhd%SNTXV=;_fO(fiM zsrgFF<(5i)`(g~bs+&ne%UpDKIlBAWk@tG9_g13&tF4h>h+m1mL;xdgd?QrV38`xB zjZ&R@RsCf#ovn@dRaG-Hs><1){) zfkzW3ZiNut9(amvR?OH+TB&833Z^K^xM7bd_DUkBtjW9hTu84lkwW%PHx}!3ePJyt z8z_RhAT!e~WjP>Cj+$qJe}#FF^5@Ve7D0OGpq^DGpi6fgx82yjN|Fp#ckg3oYc6vd4u zgUjNxBf$GA{Bu`)mVDDdY^j!F4FNssEivp5_A{%ZvakRFaNcG$I=XcC33{B?jC+_ zejwP`28DQXIsO5gVPl=4F9i;{SLPXZ0v5qQ()tJpI4Vlv_Yhk5B?*2>LSK@|Ez)+2 zJOrjM`RBw~S&Us6|5V)mcj?i8OS`H*nZ|BO9o2|P@Kcb)ig Qh$4+NcJB~BewYINFTH4*#sB~S literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/setuptools/_vendor/importlib_resources/__pycache__/_itertools.cpython-312.pyc b/venv/Lib/site-packages/setuptools/_vendor/importlib_resources/__pycache__/_itertools.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..472064734a8da62798d86cbf2b37b043e0a6e819 GIT binary patch literal 1203 zcmYjRO>Epm6rSuvCePRC&2=|eTTLb8sqcXKkd#D}sqZ%vFA5!>iC0O&3XAO~ zx#mzNFNI)cg1rcHX@RNU;HEMyL)&KOpL)1(ukQaC*Uzm>5H)MaOk1^cG@e+pb4p;&@?f z+lE3bV5E>7ViG^XRE{>#S)qS3!z(ebj-HR_x8~lomLQHeM{7Q+r+IwVT;xJ%))lV~ zk42`gq^qkwnYux0F}$YLB4NwA?A0u;O*NhRinB~&3xZ3VNfi0<85<~WM9+9ZGop#_ z)ojQ&N@<}U?$EKXZr;Ax zxzsHbJD)x}aq7F7-zR6bCug=kdU)c(6C`LauV36LzqIk;Z{@M>@X0UV+mpeztD;f; z36ivN*sUJhxOlI;KHD9tK9KIL?Nmn}9R0e~9T~e<+0%gXUtETJKbsw%eHH(FdORNnLPGQh5gF@B6Do=GYeo*{s}97d2*SzrA8dPmri%RK>-o5gSAJ@F_W?;aW1 sptt?qT%It#KfU$V*33`CTW@T?c6NK>TyFnEuB_K?Sx;r)(=oW~f2-Ozj{pDw literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/setuptools/_vendor/importlib_resources/__pycache__/_legacy.cpython-312.pyc b/venv/Lib/site-packages/setuptools/_vendor/importlib_resources/__pycache__/_legacy.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..faea17c18d2f2104ff30576fcc2942eac5724069 GIT binary patch literal 5819 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 literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/setuptools/_vendor/importlib_resources/__pycache__/abc.cpython-312.pyc b/venv/Lib/site-packages/setuptools/_vendor/importlib_resources/__pycache__/abc.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..bf3821e8ab09bf0cdb8d692c3ec0462208b36c3b GIT binary patch literal 8901 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 files('a', 'b') + Traceback (most recent call last): + TypeError: files() takes from 0 to 1 positional arguments but 2 were given + """ + undefined = object() + + @functools.wraps(func) + def wrapper(anchor=undefined, package=undefined): + if package is not undefined: + if anchor is not undefined: + return func(anchor, package) + warnings.warn( + "First parameter to files is renamed to 'anchor'", + DeprecationWarning, + stacklevel=2, + ) + return func(package) + elif anchor is undefined: + return func() + return func(anchor) + + return wrapper + + +@package_to_anchor +def files(anchor: Optional[Anchor] = None) -> Traversable: + """ + Get a Traversable resource for an anchor. + """ + return from_package(resolve(anchor)) + + +def get_resource_reader(package: types.ModuleType) -> Optional[ResourceReader]: + """ + Return the package's loader if it's a ResourceReader. + """ + # We can't use + # a issubclass() check here because apparently abc.'s __subclasscheck__() + # hook wants to create a weak reference to the object, but + # zipimport.zipimporter does not support weak references, resulting in a + # TypeError. That seems terrible. + spec = package.__spec__ + reader = getattr(spec.loader, 'get_resource_reader', None) # type: ignore + if reader is None: + return None + return reader(spec.name) # type: ignore + + +@functools.singledispatch +def resolve(cand: Optional[Anchor]) -> types.ModuleType: + return cast(types.ModuleType, cand) + + +@resolve.register +def _(cand: str) -> types.ModuleType: + return importlib.import_module(cand) + + +@resolve.register +def _(cand: None) -> types.ModuleType: + return resolve(_infer_caller().f_globals['__name__']) + + +def _infer_caller(): + """ + Walk the stack and find the frame of the first caller not in this module. + """ + + def is_this_file(frame_info): + return frame_info.filename == __file__ + + def is_wrapper(frame_info): + return frame_info.function == 'wrapper' + + not_this_file = itertools.filterfalse(is_this_file, inspect.stack()) + # also exclude 'wrapper' due to singledispatch in the call stack + callers = itertools.filterfalse(is_wrapper, not_this_file) + return next(callers).frame + + +def from_package(package: types.ModuleType): + """ + Return a Traversable object for the given package. + + """ + spec = wrap_spec(package) + reader = spec.loader.get_resource_reader(spec.name) + return reader.files() + + +@contextlib.contextmanager +def _tempfile( + reader, + suffix='', + # gh-93353: Keep a reference to call os.remove() in late Python + # finalization. + *, + _os_remove=os.remove, +): + # Not using tempfile.NamedTemporaryFile as it leads to deeper 'try' + # blocks due to the need to close the temporary file to work on Windows + # properly. + fd, raw_path = tempfile.mkstemp(suffix=suffix) + try: + try: + os.write(fd, reader()) + finally: + os.close(fd) + del reader + yield pathlib.Path(raw_path) + finally: + try: + _os_remove(raw_path) + except FileNotFoundError: + pass + + +def _temp_file(path): + return _tempfile(path.read_bytes, suffix=path.name) + + +def _is_present_dir(path: Traversable) -> bool: + """ + Some Traversables implement ``is_dir()`` to raise an + exception (i.e. ``FileNotFoundError``) when the + directory doesn't exist. This function wraps that call + to always return a boolean and only return True + if there's a dir and it exists. + """ + with contextlib.suppress(FileNotFoundError): + return path.is_dir() + return False + + +@functools.singledispatch +def as_file(path): + """ + Given a Traversable object, return that object as a + path on the local file system in a context manager. + """ + return _temp_dir(path) if _is_present_dir(path) else _temp_file(path) + + +@as_file.register(pathlib.Path) +@contextlib.contextmanager +def _(path): + """ + Degenerate behavior for pathlib.Path objects. + """ + yield path + + +@contextlib.contextmanager +def _temp_path(dir: tempfile.TemporaryDirectory): + """ + Wrap tempfile.TemporyDirectory to return a pathlib object. + """ + with dir as result: + yield pathlib.Path(result) + + +@contextlib.contextmanager +def _temp_dir(path): + """ + Given a traversable dir, recursively replicate the whole tree + to the file system in a context manager. + """ + assert path.is_dir() + with _temp_path(tempfile.TemporaryDirectory()) as temp_dir: + yield _write_contents(temp_dir, path) + + +def _write_contents(target, source): + child = target.joinpath(source.name) + if source.is_dir(): + child.mkdir() + for item in source.iterdir(): + _write_contents(child, item) + else: + child.write_bytes(source.read_bytes()) + return child diff --git a/venv/Lib/site-packages/setuptools/_vendor/importlib_resources/_compat.py b/venv/Lib/site-packages/setuptools/_vendor/importlib_resources/_compat.py new file mode 100644 index 0000000..8b5b1d2 --- /dev/null +++ b/venv/Lib/site-packages/setuptools/_vendor/importlib_resources/_compat.py @@ -0,0 +1,108 @@ +# flake8: noqa + +import abc +import os +import sys +import pathlib +from contextlib import suppress +from typing import Union + + +if sys.version_info >= (3, 10): + from zipfile import Path as ZipPath # type: ignore +else: + from ..zipp import Path as ZipPath # type: ignore + + +try: + from typing import runtime_checkable # type: ignore +except ImportError: + + def runtime_checkable(cls): # type: ignore + return cls + + +try: + from typing import Protocol # type: ignore +except ImportError: + Protocol = abc.ABC # type: ignore + + +class TraversableResourcesLoader: + """ + Adapt loaders to provide TraversableResources and other + compatibility. + + Used primarily for Python 3.9 and earlier where the native + loaders do not yet implement TraversableResources. + """ + + def __init__(self, spec): + self.spec = spec + + @property + def path(self): + return self.spec.origin + + def get_resource_reader(self, name): + from . import readers, _adapters + + def _zip_reader(spec): + with suppress(AttributeError): + return readers.ZipReader(spec.loader, spec.name) + + def _namespace_reader(spec): + with suppress(AttributeError, ValueError): + return readers.NamespaceReader(spec.submodule_search_locations) + + def _available_reader(spec): + with suppress(AttributeError): + return spec.loader.get_resource_reader(spec.name) + + def _native_reader(spec): + reader = _available_reader(spec) + return reader if hasattr(reader, 'files') else None + + def _file_reader(spec): + try: + path = pathlib.Path(self.path) + except TypeError: + return None + if path.exists(): + return readers.FileReader(self) + + return ( + # native reader if it supplies 'files' + _native_reader(self.spec) + or + # local ZipReader if a zip module + _zip_reader(self.spec) + or + # local NamespaceReader if a namespace module + _namespace_reader(self.spec) + or + # local FileReader + _file_reader(self.spec) + # fallback - adapt the spec ResourceReader to TraversableReader + or _adapters.CompatibilityFiles(self.spec) + ) + + +def wrap_spec(package): + """ + Construct a package spec with traversable compatibility + on the spec/loader/reader. + + Supersedes _adapters.wrap_spec to use TraversableResourcesLoader + from above for older Python compatibility (<3.10). + """ + from . import _adapters + + return _adapters.SpecLoaderAdapter(package.__spec__, TraversableResourcesLoader) + + +if sys.version_info >= (3, 9): + StrPath = Union[str, os.PathLike[str]] +else: + # PathLike is only subscriptable at runtime in 3.9+ + StrPath = Union[str, "os.PathLike[str]"] diff --git a/venv/Lib/site-packages/setuptools/_vendor/importlib_resources/_itertools.py b/venv/Lib/site-packages/setuptools/_vendor/importlib_resources/_itertools.py new file mode 100644 index 0000000..cce0558 --- /dev/null +++ b/venv/Lib/site-packages/setuptools/_vendor/importlib_resources/_itertools.py @@ -0,0 +1,35 @@ +from itertools import filterfalse + +from typing import ( + Callable, + Iterable, + Iterator, + Optional, + Set, + TypeVar, + Union, +) + +# Type and type variable definitions +_T = TypeVar('_T') +_U = TypeVar('_U') + + +def unique_everseen( + iterable: Iterable[_T], key: Optional[Callable[[_T], _U]] = None +) -> Iterator[_T]: + "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[Union[_T, _U]] = 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/site-packages/setuptools/_vendor/importlib_resources/_legacy.py b/venv/Lib/site-packages/setuptools/_vendor/importlib_resources/_legacy.py new file mode 100644 index 0000000..b1ea810 --- /dev/null +++ b/venv/Lib/site-packages/setuptools/_vendor/importlib_resources/_legacy.py @@ -0,0 +1,120 @@ +import functools +import os +import pathlib +import types +import warnings + +from typing import Union, Iterable, ContextManager, BinaryIO, TextIO, Any + +from . import _common + +Package = Union[types.ModuleType, str] +Resource = str + + +def deprecated(func): + @functools.wraps(func) + def wrapper(*args, **kwargs): + warnings.warn( + f"{func.__name__} is deprecated. Use files() instead. " + "Refer to https://importlib-resources.readthedocs.io" + "/en/latest/using.html#migrating-from-legacy for migration advice.", + DeprecationWarning, + stacklevel=2, + ) + return func(*args, **kwargs) + + return wrapper + + +def normalize_path(path: Any) -> str: + """Normalize a path by ensuring it is a string. + + If the resulting string contains path separators, an exception is raised. + """ + str_path = str(path) + parent, file_name = os.path.split(str_path) + if parent: + raise ValueError(f'{path!r} must be only a file name') + return file_name + + +@deprecated +def open_binary(package: Package, resource: Resource) -> BinaryIO: + """Return a file-like object opened for binary reading of the resource.""" + return (_common.files(package) / normalize_path(resource)).open('rb') + + +@deprecated +def read_binary(package: Package, resource: Resource) -> bytes: + """Return the binary contents of the resource.""" + return (_common.files(package) / normalize_path(resource)).read_bytes() + + +@deprecated +def open_text( + package: Package, + resource: Resource, + encoding: str = 'utf-8', + errors: str = 'strict', +) -> TextIO: + """Return a file-like object opened for text reading of the resource.""" + return (_common.files(package) / normalize_path(resource)).open( + 'r', encoding=encoding, errors=errors + ) + + +@deprecated +def read_text( + package: Package, + resource: Resource, + encoding: str = 'utf-8', + errors: str = 'strict', +) -> str: + """Return the decoded string of the resource. + + The decoding-related arguments have the same semantics as those of + bytes.decode(). + """ + with open_text(package, resource, encoding, errors) as fp: + return fp.read() + + +@deprecated +def contents(package: Package) -> Iterable[str]: + """Return an iterable of entries in `package`. + + Note that not all entries are resources. Specifically, directories are + not considered resources. Use `is_resource()` on each entry returned here + to check if it is a resource or not. + """ + return [path.name for path in _common.files(package).iterdir()] + + +@deprecated +def is_resource(package: Package, name: str) -> bool: + """True if `name` is a resource inside `package`. + + Directories are *not* resources. + """ + resource = normalize_path(name) + return any( + traversable.name == resource and traversable.is_file() + for traversable in _common.files(package).iterdir() + ) + + +@deprecated +def path( + package: Package, + resource: Resource, +) -> ContextManager[pathlib.Path]: + """A context manager providing a file path object to the resource. + + If the resource does not already exist on its own on the file system, + a temporary file will be created. If the file was created, the file + will be deleted upon exiting the context manager (no exception is + raised if the file was deleted prior to the context manager + exiting). + """ + return _common.as_file(_common.files(package) / normalize_path(resource)) diff --git a/venv/Lib/site-packages/setuptools/_vendor/importlib_resources/abc.py b/venv/Lib/site-packages/setuptools/_vendor/importlib_resources/abc.py new file mode 100644 index 0000000..23b6aea --- /dev/null +++ b/venv/Lib/site-packages/setuptools/_vendor/importlib_resources/abc.py @@ -0,0 +1,170 @@ +import abc +import io +import itertools +import pathlib +from typing import Any, BinaryIO, Iterable, Iterator, NoReturn, Text, Optional + +from ._compat import runtime_checkable, Protocol, StrPath + + +__all__ = ["ResourceReader", "Traversable", "TraversableResources"] + + +class ResourceReader(metaclass=abc.ABCMeta): + """Abstract base class for loaders to provide resource reading support.""" + + @abc.abstractmethod + def open_resource(self, resource: Text) -> BinaryIO: + """Return an opened, file-like object for binary reading. + + The 'resource' argument is expected to represent only a file name. + If the resource cannot be found, FileNotFoundError is raised. + """ + # This deliberately raises FileNotFoundError instead of + # NotImplementedError so that if this method is accidentally called, + # it'll still do the right thing. + raise FileNotFoundError + + @abc.abstractmethod + def resource_path(self, resource: Text) -> Text: + """Return the file system path to the specified resource. + + The 'resource' argument is expected to represent only a file name. + If the resource does not exist on the file system, raise + FileNotFoundError. + """ + # This deliberately raises FileNotFoundError instead of + # NotImplementedError so that if this method is accidentally called, + # it'll still do the right thing. + raise FileNotFoundError + + @abc.abstractmethod + def is_resource(self, path: Text) -> bool: + """Return True if the named 'path' is a resource. + + Files are resources, directories are not. + """ + raise FileNotFoundError + + @abc.abstractmethod + def contents(self) -> Iterable[str]: + """Return an iterable of entries in `package`.""" + raise FileNotFoundError + + +class TraversalError(Exception): + pass + + +@runtime_checkable +class Traversable(Protocol): + """ + An object with a subset of pathlib.Path methods suitable for + traversing directories and opening files. + + Any exceptions that occur when accessing the backing resource + may propagate unaltered. + """ + + @abc.abstractmethod + def iterdir(self) -> Iterator["Traversable"]: + """ + Yield Traversable objects in self + """ + + def read_bytes(self) -> bytes: + """ + Read contents of self as bytes + """ + with self.open('rb') as strm: + return strm.read() + + def read_text(self, encoding: Optional[str] = None) -> str: + """ + Read contents of self as text + """ + with self.open(encoding=encoding) as strm: + return strm.read() + + @abc.abstractmethod + def is_dir(self) -> bool: + """ + Return True if self is a directory + """ + + @abc.abstractmethod + def is_file(self) -> bool: + """ + Return True if self is a file + """ + + def joinpath(self, *descendants: StrPath) -> "Traversable": + """ + Return Traversable resolved with any descendants applied. + + Each descendant should be a path segment relative to self + and each may contain multiple levels separated by + ``posixpath.sep`` (``/``). + """ + if not descendants: + return self + names = itertools.chain.from_iterable( + path.parts for path in map(pathlib.PurePosixPath, descendants) + ) + target = next(names) + matches = ( + traversable for traversable in self.iterdir() if traversable.name == target + ) + try: + match = next(matches) + except StopIteration: + raise TraversalError( + "Target not found during traversal.", target, list(names) + ) + return match.joinpath(*names) + + def __truediv__(self, child: StrPath) -> "Traversable": + """ + Return Traversable child in self + """ + return self.joinpath(child) + + @abc.abstractmethod + def open(self, mode='r', *args, **kwargs): + """ + mode may be 'r' or 'rb' to open as text or binary. Return a handle + suitable for reading (same as pathlib.Path.open). + + When opening as text, accepts encoding parameters such as those + accepted by io.TextIOWrapper. + """ + + @property + @abc.abstractmethod + def name(self) -> str: + """ + The base name of this object without any parent references. + """ + + +class TraversableResources(ResourceReader): + """ + The required interface for providing traversable + resources. + """ + + @abc.abstractmethod + def files(self) -> "Traversable": + """Return a Traversable object for the loaded package.""" + + def open_resource(self, resource: StrPath) -> io.BufferedReader: + return self.files().joinpath(resource).open('rb') + + def resource_path(self, resource: Any) -> NoReturn: + raise FileNotFoundError(resource) + + def is_resource(self, path: StrPath) -> bool: + return self.files().joinpath(path).is_file() + + def contents(self) -> Iterator[str]: + return (item.name for item in self.files().iterdir()) diff --git a/venv/Lib/site-packages/setuptools/_vendor/importlib_resources/py.typed b/venv/Lib/site-packages/setuptools/_vendor/importlib_resources/py.typed new file mode 100644 index 0000000..e69de29 diff --git a/venv/Lib/site-packages/setuptools/_vendor/importlib_resources/readers.py b/venv/Lib/site-packages/setuptools/_vendor/importlib_resources/readers.py new file mode 100644 index 0000000..ab34db7 --- /dev/null +++ b/venv/Lib/site-packages/setuptools/_vendor/importlib_resources/readers.py @@ -0,0 +1,120 @@ +import collections +import pathlib +import operator + +from . import abc + +from ._itertools import unique_everseen +from ._compat import ZipPath + + +def remove_duplicates(items): + return iter(collections.OrderedDict.fromkeys(items)) + + +class FileReader(abc.TraversableResources): + def __init__(self, loader): + self.path = pathlib.Path(loader.path).parent + + def resource_path(self, resource): + """ + Return the file system path to prevent + `resources.path()` from creating a temporary + copy. + """ + return str(self.path.joinpath(resource)) + + def files(self): + return self.path + + +class ZipReader(abc.TraversableResources): + def __init__(self, loader, module): + _, _, name = module.rpartition('.') + self.prefix = loader.prefix.replace('\\', '/') + name + '/' + self.archive = loader.archive + + def open_resource(self, resource): + try: + return super().open_resource(resource) + except KeyError as exc: + raise FileNotFoundError(exc.args[0]) + + def is_resource(self, path): + # workaround for `zipfile.Path.is_file` returning true + # for non-existent paths. + target = self.files().joinpath(path) + return target.is_file() and target.exists() + + def files(self): + return ZipPath(self.archive, self.prefix) + + +class MultiplexedPath(abc.Traversable): + """ + Given a series of Traversable objects, implement a merged + version of the interface across all objects. Useful for + namespace packages which may be multihomed at a single + name. + """ + + def __init__(self, *paths): + self._paths = list(map(pathlib.Path, remove_duplicates(paths))) + if not self._paths: + message = 'MultiplexedPath must contain at least one path' + raise FileNotFoundError(message) + if not all(path.is_dir() for path in self._paths): + raise NotADirectoryError('MultiplexedPath only supports directories') + + def iterdir(self): + files = (file for path in self._paths for file in path.iterdir()) + return unique_everseen(files, key=operator.attrgetter('name')) + + def read_bytes(self): + raise FileNotFoundError(f'{self} is not a file') + + def read_text(self, *args, **kwargs): + raise FileNotFoundError(f'{self} is not a file') + + def is_dir(self): + return True + + def is_file(self): + return False + + def joinpath(self, *descendants): + try: + return super().joinpath(*descendants) + except abc.TraversalError: + # One of the paths did not resolve (a directory does not exist). + # Just return something that will not exist. + return self._paths[0].joinpath(*descendants) + + def open(self, *args, **kwargs): + raise FileNotFoundError(f'{self} is not a file') + + @property + def name(self): + return self._paths[0].name + + def __repr__(self): + paths = ', '.join(f"'{path}'" for path in self._paths) + return f'MultiplexedPath({paths})' + + +class NamespaceReader(abc.TraversableResources): + def __init__(self, namespace_path): + if 'NamespacePath' not in str(namespace_path): + raise ValueError('Invalid path') + self.path = MultiplexedPath(*list(namespace_path)) + + def resource_path(self, resource): + """ + Return the file system path to prevent + `resources.path()` from creating a temporary + copy. + """ + return str(self.path.joinpath(resource)) + + def files(self): + return self.path diff --git a/venv/Lib/site-packages/setuptools/_vendor/importlib_resources/simple.py b/venv/Lib/site-packages/setuptools/_vendor/importlib_resources/simple.py new file mode 100644 index 0000000..7770c92 --- /dev/null +++ b/venv/Lib/site-packages/setuptools/_vendor/importlib_resources/simple.py @@ -0,0 +1,106 @@ +""" +Interface adapters for low-level readers. +""" + +import abc +import io +import itertools +from typing import BinaryIO, List + +from .abc import Traversable, TraversableResources + + +class SimpleReader(abc.ABC): + """ + The minimum, low-level interface required from a resource + provider. + """ + + @property + @abc.abstractmethod + def package(self) -> str: + """ + The name of the package for which this reader loads resources. + """ + + @abc.abstractmethod + def children(self) -> List['SimpleReader']: + """ + Obtain an iterable of SimpleReader for available + child containers (e.g. directories). + """ + + @abc.abstractmethod + def resources(self) -> List[str]: + """ + Obtain available named resources for this virtual package. + """ + + @abc.abstractmethod + def open_binary(self, resource: str) -> BinaryIO: + """ + Obtain a File-like for a named resource. + """ + + @property + def name(self): + return self.package.split('.')[-1] + + +class ResourceContainer(Traversable): + """ + Traversable container for a package's resources via its reader. + """ + + def __init__(self, reader: SimpleReader): + self.reader = reader + + def is_dir(self): + return True + + def is_file(self): + return False + + def iterdir(self): + files = (ResourceHandle(self, name) for name in self.reader.resources) + dirs = map(ResourceContainer, self.reader.children()) + return itertools.chain(files, dirs) + + def open(self, *args, **kwargs): + raise IsADirectoryError() + + +class ResourceHandle(Traversable): + """ + Handle to a named resource in a ResourceReader. + """ + + def __init__(self, parent: ResourceContainer, name: str): + self.parent = parent + self.name = name # type: ignore + + def is_file(self): + return True + + def is_dir(self): + return False + + def open(self, mode='r', *args, **kwargs): + stream = self.parent.reader.open_binary(self.name) + if 'b' not in mode: + stream = io.TextIOWrapper(*args, **kwargs) + return stream + + def joinpath(self, name): + raise RuntimeError("Cannot traverse into a resource") + + +class TraversableReader(TraversableResources, SimpleReader): + """ + A TraversableResources based on SimpleReader. Resource providers + may derive from this class to provide the TraversableResources + interface by supplying the SimpleReader interface. + """ + + def files(self): + return ResourceContainer(self) diff --git a/venv/Lib/site-packages/setuptools/_vendor/jaraco/__init__.py b/venv/Lib/site-packages/setuptools/_vendor/jaraco/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/venv/Lib/site-packages/setuptools/_vendor/jaraco/__pycache__/__init__.cpython-312.pyc b/venv/Lib/site-packages/setuptools/_vendor/jaraco/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..de74b3f004caa02c6a4efe9f1a74122b763180fe GIT binary patch literal 211 zcmZ8bK?=e!5Nxc12z`i$*t>|AdKBr!TOg$ED$%wno21fy#5edBUm?Cgdh5x=qXRoT zGt6P0QM3pg@I_Dlzdik9n7ZU1bitAw9sy3A8+>QPWX+BmQ2&_iRBkJ%jb@1|WPN6- z%#B5$sqOm`=nXP04X_sKmhEN6yrYfCui^}vX)v}iN|ie1u2LwBF9JobV$NkP4d<(- Z8|Rx!6}E&e&fEmzPJ8hQ2_YCd^#we|Jih<{ literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/setuptools/_vendor/jaraco/__pycache__/context.cpython-312.pyc b/venv/Lib/site-packages/setuptools/_vendor/jaraco/__pycache__/context.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..557d66ae2ba858c27b512ce5e0c2a751339bb2fe GIT binary patch literal 14374 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 U`S6)Nmm4E Iterator[str | os.PathLike]: + """ + >>> tmp_path = getfixture('tmp_path') + >>> with pushd(tmp_path): + ... assert os.getcwd() == os.fspath(tmp_path) + >>> assert os.getcwd() != os.fspath(tmp_path) + """ + + orig = os.getcwd() + os.chdir(dir) + try: + yield dir + finally: + os.chdir(orig) + + +@contextlib.contextmanager +def tarball( + url, target_dir: str | os.PathLike | None = None +) -> Iterator[str | os.PathLike]: + """ + Get a tarball, extract it, yield, then clean up. + + >>> import urllib.request + >>> url = getfixture('tarfile_served') + >>> target = getfixture('tmp_path') / 'out' + >>> tb = tarball(url, target_dir=target) + >>> import pathlib + >>> with tb as extracted: + ... contents = pathlib.Path(extracted, 'contents.txt').read_text(encoding='utf-8') + >>> assert not os.path.exists(extracted) + """ + if target_dir is None: + target_dir = os.path.basename(url).replace('.tar.gz', '').replace('.tgz', '') + # In the tar command, use --strip-components=1 to strip the first path and + # then + # use -C to cause the files to be extracted to {target_dir}. This ensures + # that we always know where the files were extracted. + os.mkdir(target_dir) + try: + req = urllib.request.urlopen(url) + with tarfile.open(fileobj=req, mode='r|*') as tf: + tf.extractall(path=target_dir, filter=strip_first_component) + yield target_dir + finally: + shutil.rmtree(target_dir) + + +def strip_first_component( + member: tarfile.TarInfo, + path, +) -> tarfile.TarInfo: + _, member.name = member.name.split('/', 1) + return member + + +def _compose(*cmgrs): + """ + Compose any number of dependent context managers into a single one. + + The last, innermost context manager may take arbitrary arguments, but + each successive context manager should accept the result from the + previous as a single parameter. + + Like :func:`jaraco.functools.compose`, behavior works from right to + left, so the context manager should be indicated from outermost to + innermost. + + Example, to create a context manager to change to a temporary + directory: + + >>> temp_dir_as_cwd = _compose(pushd, temp_dir) + >>> with temp_dir_as_cwd() as dir: + ... assert os.path.samefile(os.getcwd(), dir) + """ + + def compose_two(inner, outer): + def composed(*args, **kwargs): + with inner(*args, **kwargs) as saved, outer(saved) as res: + yield res + + return contextlib.contextmanager(composed) + + return functools.reduce(compose_two, reversed(cmgrs)) + + +tarball_cwd = _compose(pushd, tarball) + + +@contextlib.contextmanager +def tarball_context(*args, **kwargs): + warnings.warn( + "tarball_context is deprecated. Use tarball or tarball_cwd instead.", + DeprecationWarning, + stacklevel=2, + ) + pushd_ctx = kwargs.pop('pushd', pushd) + with tarball(*args, **kwargs) as tball, pushd_ctx(tball) as dir: + yield dir + + +def infer_compression(url): + """ + Given a URL or filename, infer the compression code for tar. + + >>> infer_compression('http://foo/bar.tar.gz') + 'z' + >>> infer_compression('http://foo/bar.tgz') + 'z' + >>> infer_compression('file.bz') + 'j' + >>> infer_compression('file.xz') + 'J' + """ + warnings.warn( + "infer_compression is deprecated with no replacement", + DeprecationWarning, + stacklevel=2, + ) + # cheat and just assume it's the last two characters + compression_indicator = url[-2:] + mapping = dict(gz='z', bz='j', xz='J') + # Assume 'z' (gzip) if no match + return mapping.get(compression_indicator, 'z') + + +@contextlib.contextmanager +def temp_dir(remover=shutil.rmtree): + """ + Create a temporary directory context. Pass a custom remover + to override the removal behavior. + + >>> import pathlib + >>> with temp_dir() as the_dir: + ... assert os.path.isdir(the_dir) + ... _ = pathlib.Path(the_dir).joinpath('somefile').write_text('contents', encoding='utf-8') + >>> assert not os.path.exists(the_dir) + """ + temp_dir = tempfile.mkdtemp() + try: + yield temp_dir + finally: + remover(temp_dir) + + +@contextlib.contextmanager +def repo_context(url, branch=None, quiet=True, dest_ctx=temp_dir): + """ + Check out the repo indicated by url. + + If dest_ctx is supplied, it should be a context manager + to yield the target directory for the check out. + """ + exe = 'git' if 'git' in url else 'hg' + with dest_ctx() as repo_dir: + cmd = [exe, 'clone', url, repo_dir] + if branch: + cmd.extend(['--branch', branch]) + devnull = open(os.path.devnull, 'w') + stdout = devnull if quiet else None + subprocess.check_call(cmd, stdout=stdout) + yield repo_dir + + +def null(): + """ + A null context suitable to stand in for a meaningful context. + + >>> with null() as value: + ... assert value is None + + This context is most useful when dealing with two or more code + branches but only some need a context. Wrap the others in a null + context to provide symmetry across all options. + """ + warnings.warn( + "null is deprecated. Use contextlib.nullcontext", + DeprecationWarning, + stacklevel=2, + ) + return contextlib.nullcontext() + + +class ExceptionTrap: + """ + A context manager that will catch certain exceptions and provide an + indication they occurred. + + >>> with ExceptionTrap() as trap: + ... raise Exception() + >>> bool(trap) + True + + >>> with ExceptionTrap() as trap: + ... pass + >>> bool(trap) + False + + >>> with ExceptionTrap(ValueError) as trap: + ... raise ValueError("1 + 1 is not 3") + >>> bool(trap) + True + >>> trap.value + ValueError('1 + 1 is not 3') + >>> trap.tb + + + >>> with ExceptionTrap(ValueError) as trap: + ... raise Exception() + Traceback (most recent call last): + ... + Exception + + >>> bool(trap) + False + """ + + exc_info = None, None, None + + def __init__(self, exceptions=(Exception,)): + self.exceptions = exceptions + + def __enter__(self): + return self + + @property + def type(self): + return self.exc_info[0] + + @property + def value(self): + return self.exc_info[1] + + @property + def tb(self): + return self.exc_info[2] + + def __exit__(self, *exc_info): + type = exc_info[0] + matches = type and issubclass(type, self.exceptions) + if matches: + self.exc_info = exc_info + return matches + + def __bool__(self): + return bool(self.type) + + def raises(self, func, *, _test=bool): + """ + Wrap func and replace the result with the truth + value of the trap (True if an exception occurred). + + First, give the decorator an alias to support Python 3.8 + Syntax. + + >>> raises = ExceptionTrap(ValueError).raises + + Now decorate a function that always fails. + + >>> @raises + ... def fail(): + ... raise ValueError('failed') + >>> fail() + True + """ + + @functools.wraps(func) + def wrapper(*args, **kwargs): + with ExceptionTrap(self.exceptions) as trap: + func(*args, **kwargs) + return _test(trap) + + return wrapper + + def passes(self, func): + """ + Wrap func and replace the result with the truth + value of the trap (True if no exception). + + First, give the decorator an alias to support Python 3.8 + Syntax. + + >>> passes = ExceptionTrap(ValueError).passes + + Now decorate a function that always fails. + + >>> @passes + ... def fail(): + ... raise ValueError('failed') + + >>> fail() + False + """ + return self.raises(func, _test=operator.not_) + + +class suppress(contextlib.suppress, contextlib.ContextDecorator): + """ + A version of contextlib.suppress with decorator support. + + >>> @suppress(KeyError) + ... def key_error(): + ... {}[''] + >>> key_error() + """ + + +class on_interrupt(contextlib.ContextDecorator): + """ + Replace a KeyboardInterrupt with SystemExit(1) + + >>> def do_interrupt(): + ... raise KeyboardInterrupt() + >>> on_interrupt('error')(do_interrupt)() + Traceback (most recent call last): + ... + SystemExit: 1 + >>> on_interrupt('error', code=255)(do_interrupt)() + Traceback (most recent call last): + ... + SystemExit: 255 + >>> on_interrupt('suppress')(do_interrupt)() + >>> with __import__('pytest').raises(KeyboardInterrupt): + ... on_interrupt('ignore')(do_interrupt)() + """ + + def __init__(self, action='error', /, code=1): + self.action = action + self.code = code + + def __enter__(self): + return self + + def __exit__(self, exctype, excinst, exctb): + if exctype is not KeyboardInterrupt or self.action == 'ignore': + return + elif self.action == 'error': + raise SystemExit(self.code) from excinst + return self.action == 'suppress' diff --git a/venv/Lib/site-packages/setuptools/_vendor/jaraco/functools/__init__.py b/venv/Lib/site-packages/setuptools/_vendor/jaraco/functools/__init__.py new file mode 100644 index 0000000..130b87a --- /dev/null +++ b/venv/Lib/site-packages/setuptools/_vendor/jaraco/functools/__init__.py @@ -0,0 +1,633 @@ +import collections.abc +import functools +import inspect +import itertools +import operator +import time +import types +import warnings + +import setuptools.extern.more_itertools + + +def compose(*funcs): + """ + Compose any number of unary functions into a single unary function. + + >>> import textwrap + >>> expected = str.strip(textwrap.dedent(compose.__doc__)) + >>> strip_and_dedent = compose(str.strip, textwrap.dedent) + >>> strip_and_dedent(compose.__doc__) == expected + True + + Compose also allows the innermost function to take arbitrary arguments. + + >>> round_three = lambda x: round(x, ndigits=3) + >>> f = compose(round_three, int.__truediv__) + >>> [f(3*x, x+1) for x in range(1,10)] + [1.5, 2.0, 2.25, 2.4, 2.5, 2.571, 2.625, 2.667, 2.7] + """ + + def compose_two(f1, f2): + return lambda *args, **kwargs: f1(f2(*args, **kwargs)) + + return functools.reduce(compose_two, funcs) + + +def once(func): + """ + Decorate func so it's only ever called the first time. + + This decorator can ensure that an expensive or non-idempotent function + will not be expensive on subsequent calls and is idempotent. + + >>> add_three = once(lambda a: a+3) + >>> add_three(3) + 6 + >>> add_three(9) + 6 + >>> add_three('12') + 6 + + To reset the stored value, simply clear the property ``saved_result``. + + >>> del add_three.saved_result + >>> add_three(9) + 12 + >>> add_three(8) + 12 + + Or invoke 'reset()' on it. + + >>> add_three.reset() + >>> add_three(-3) + 0 + >>> add_three(0) + 0 + """ + + @functools.wraps(func) + def wrapper(*args, **kwargs): + if not hasattr(wrapper, 'saved_result'): + wrapper.saved_result = func(*args, **kwargs) + return wrapper.saved_result + + wrapper.reset = lambda: vars(wrapper).__delitem__('saved_result') + return wrapper + + +def method_cache(method, cache_wrapper=functools.lru_cache()): + """ + Wrap lru_cache to support storing the cache data in the object instances. + + Abstracts the common paradigm where the method explicitly saves an + underscore-prefixed protected property on first call and returns that + subsequently. + + >>> class MyClass: + ... calls = 0 + ... + ... @method_cache + ... def method(self, value): + ... self.calls += 1 + ... return value + + >>> a = MyClass() + >>> a.method(3) + 3 + >>> for x in range(75): + ... res = a.method(x) + >>> a.calls + 75 + + Note that the apparent behavior will be exactly like that of lru_cache + except that the cache is stored on each instance, so values in one + instance will not flush values from another, and when an instance is + deleted, so are the cached values for that instance. + + >>> b = MyClass() + >>> for x in range(35): + ... res = b.method(x) + >>> b.calls + 35 + >>> a.method(0) + 0 + >>> a.calls + 75 + + Note that if method had been decorated with ``functools.lru_cache()``, + a.calls would have been 76 (due to the cached value of 0 having been + flushed by the 'b' instance). + + Clear the cache with ``.cache_clear()`` + + >>> a.method.cache_clear() + + Same for a method that hasn't yet been called. + + >>> c = MyClass() + >>> c.method.cache_clear() + + Another cache wrapper may be supplied: + + >>> cache = functools.lru_cache(maxsize=2) + >>> MyClass.method2 = method_cache(lambda self: 3, cache_wrapper=cache) + >>> a = MyClass() + >>> a.method2() + 3 + + Caution - do not subsequently wrap the method with another decorator, such + as ``@property``, which changes the semantics of the function. + + See also + http://code.activestate.com/recipes/577452-a-memoize-decorator-for-instance-methods/ + for another implementation and additional justification. + """ + + def wrapper(self, *args, **kwargs): + # it's the first call, replace the method with a cached, bound method + bound_method = types.MethodType(method, self) + cached_method = cache_wrapper(bound_method) + setattr(self, method.__name__, cached_method) + return cached_method(*args, **kwargs) + + # Support cache clear even before cache has been created. + wrapper.cache_clear = lambda: None + + return _special_method_cache(method, cache_wrapper) or wrapper + + +def _special_method_cache(method, cache_wrapper): + """ + Because Python treats special methods differently, it's not + possible to use instance attributes to implement the cached + methods. + + Instead, install the wrapper method under a different name + and return a simple proxy to that wrapper. + + https://github.com/jaraco/jaraco.functools/issues/5 + """ + name = method.__name__ + special_names = '__getattr__', '__getitem__' + + if name not in special_names: + return None + + wrapper_name = '__cached' + name + + def proxy(self, /, *args, **kwargs): + if wrapper_name not in vars(self): + bound = types.MethodType(method, self) + cache = cache_wrapper(bound) + setattr(self, wrapper_name, cache) + else: + cache = getattr(self, wrapper_name) + return cache(*args, **kwargs) + + return proxy + + +def apply(transform): + """ + Decorate a function with a transform function that is + invoked on results returned from the decorated function. + + >>> @apply(reversed) + ... def get_numbers(start): + ... "doc for get_numbers" + ... return range(start, start+3) + >>> list(get_numbers(4)) + [6, 5, 4] + >>> get_numbers.__doc__ + 'doc for get_numbers' + """ + + def wrap(func): + return functools.wraps(func)(compose(transform, func)) + + return wrap + + +def result_invoke(action): + r""" + Decorate a function with an action function that is + invoked on the results returned from the decorated + function (for its side effect), then return the original + result. + + >>> @result_invoke(print) + ... def add_two(a, b): + ... return a + b + >>> x = add_two(2, 3) + 5 + >>> x + 5 + """ + + def wrap(func): + @functools.wraps(func) + def wrapper(*args, **kwargs): + result = func(*args, **kwargs) + action(result) + return result + + return wrapper + + return wrap + + +def invoke(f, /, *args, **kwargs): + """ + Call a function for its side effect after initialization. + + The benefit of using the decorator instead of simply invoking a function + after defining it is that it makes explicit the author's intent for the + function to be called immediately. Whereas if one simply calls the + function immediately, it's less obvious if that was intentional or + incidental. It also avoids repeating the name - the two actions, defining + the function and calling it immediately are modeled separately, but linked + by the decorator construct. + + The benefit of having a function construct (opposed to just invoking some + behavior inline) is to serve as a scope in which the behavior occurs. It + avoids polluting the global namespace with local variables, provides an + anchor on which to attach documentation (docstring), keeps the behavior + logically separated (instead of conceptually separated or not separated at + all), and provides potential to re-use the behavior for testing or other + purposes. + + This function is named as a pithy way to communicate, "call this function + primarily for its side effect", or "while defining this function, also + take it aside and call it". It exists because there's no Python construct + for "define and call" (nor should there be, as decorators serve this need + just fine). The behavior happens immediately and synchronously. + + >>> @invoke + ... def func(): print("called") + called + >>> func() + called + + Use functools.partial to pass parameters to the initial call + + >>> @functools.partial(invoke, name='bingo') + ... def func(name): print('called with', name) + called with bingo + """ + f(*args, **kwargs) + return f + + +class Throttler: + """Rate-limit a function (or other callable).""" + + def __init__(self, func, max_rate=float('Inf')): + if isinstance(func, Throttler): + func = func.func + self.func = func + self.max_rate = max_rate + self.reset() + + def reset(self): + self.last_called = 0 + + def __call__(self, *args, **kwargs): + self._wait() + return self.func(*args, **kwargs) + + def _wait(self): + """Ensure at least 1/max_rate seconds from last call.""" + elapsed = time.time() - self.last_called + must_wait = 1 / self.max_rate - elapsed + time.sleep(max(0, must_wait)) + self.last_called = time.time() + + def __get__(self, obj, owner=None): + return first_invoke(self._wait, functools.partial(self.func, obj)) + + +def first_invoke(func1, func2): + """ + Return a function that when invoked will invoke func1 without + any parameters (for its side effect) and then invoke func2 + with whatever parameters were passed, returning its result. + """ + + def wrapper(*args, **kwargs): + func1() + return func2(*args, **kwargs) + + return wrapper + + +method_caller = first_invoke( + lambda: warnings.warn( + '`jaraco.functools.method_caller` is deprecated, ' + 'use `operator.methodcaller` instead', + DeprecationWarning, + stacklevel=3, + ), + operator.methodcaller, +) + + +def retry_call(func, cleanup=lambda: None, retries=0, trap=()): + """ + Given a callable func, trap the indicated exceptions + for up to 'retries' times, invoking cleanup on the + exception. On the final attempt, allow any exceptions + to propagate. + """ + attempts = itertools.count() if retries == float('inf') else range(retries) + for _ in attempts: + try: + return func() + except trap: + cleanup() + + return func() + + +def retry(*r_args, **r_kwargs): + """ + Decorator wrapper for retry_call. Accepts arguments to retry_call + except func and then returns a decorator for the decorated function. + + Ex: + + >>> @retry(retries=3) + ... def my_func(a, b): + ... "this is my funk" + ... print(a, b) + >>> my_func.__doc__ + 'this is my funk' + """ + + def decorate(func): + @functools.wraps(func) + def wrapper(*f_args, **f_kwargs): + bound = functools.partial(func, *f_args, **f_kwargs) + return retry_call(bound, *r_args, **r_kwargs) + + return wrapper + + return decorate + + +def print_yielded(func): + """ + Convert a generator into a function that prints all yielded elements. + + >>> @print_yielded + ... def x(): + ... yield 3; yield None + >>> x() + 3 + None + """ + print_all = functools.partial(map, print) + print_results = compose(more_itertools.consume, print_all, func) + return functools.wraps(func)(print_results) + + +def pass_none(func): + """ + Wrap func so it's not called if its first param is None. + + >>> print_text = pass_none(print) + >>> print_text('text') + text + >>> print_text(None) + """ + + @functools.wraps(func) + def wrapper(param, /, *args, **kwargs): + if param is not None: + return func(param, *args, **kwargs) + return None + + return wrapper + + +def assign_params(func, namespace): + """ + Assign parameters from namespace where func solicits. + + >>> def func(x, y=3): + ... print(x, y) + >>> assigned = assign_params(func, dict(x=2, z=4)) + >>> assigned() + 2 3 + + The usual errors are raised if a function doesn't receive + its required parameters: + + >>> assigned = assign_params(func, dict(y=3, z=4)) + >>> assigned() + Traceback (most recent call last): + TypeError: func() ...argument... + + It even works on methods: + + >>> class Handler: + ... def meth(self, arg): + ... print(arg) + >>> assign_params(Handler().meth, dict(arg='crystal', foo='clear'))() + crystal + """ + sig = inspect.signature(func) + params = sig.parameters.keys() + call_ns = {k: namespace[k] for k in params if k in namespace} + return functools.partial(func, **call_ns) + + +def save_method_args(method): + """ + Wrap a method such that when it is called, the args and kwargs are + saved on the method. + + >>> class MyClass: + ... @save_method_args + ... def method(self, a, b): + ... print(a, b) + >>> my_ob = MyClass() + >>> my_ob.method(1, 2) + 1 2 + >>> my_ob._saved_method.args + (1, 2) + >>> my_ob._saved_method.kwargs + {} + >>> my_ob.method(a=3, b='foo') + 3 foo + >>> my_ob._saved_method.args + () + >>> my_ob._saved_method.kwargs == dict(a=3, b='foo') + True + + The arguments are stored on the instance, allowing for + different instance to save different args. + + >>> your_ob = MyClass() + >>> your_ob.method({str('x'): 3}, b=[4]) + {'x': 3} [4] + >>> your_ob._saved_method.args + ({'x': 3},) + >>> my_ob._saved_method.args + () + """ + args_and_kwargs = collections.namedtuple('args_and_kwargs', 'args kwargs') + + @functools.wraps(method) + def wrapper(self, /, *args, **kwargs): + attr_name = '_saved_' + method.__name__ + attr = args_and_kwargs(args, kwargs) + setattr(self, attr_name, attr) + return method(self, *args, **kwargs) + + return wrapper + + +def except_(*exceptions, replace=None, use=None): + """ + Replace the indicated exceptions, if raised, with the indicated + literal replacement or evaluated expression (if present). + + >>> safe_int = except_(ValueError)(int) + >>> safe_int('five') + >>> safe_int('5') + 5 + + Specify a literal replacement with ``replace``. + + >>> safe_int_r = except_(ValueError, replace=0)(int) + >>> safe_int_r('five') + 0 + + Provide an expression to ``use`` to pass through particular parameters. + + >>> safe_int_pt = except_(ValueError, use='args[0]')(int) + >>> safe_int_pt('five') + 'five' + + """ + + def decorate(func): + @functools.wraps(func) + def wrapper(*args, **kwargs): + try: + return func(*args, **kwargs) + except exceptions: + try: + return eval(use) + except TypeError: + return replace + + return wrapper + + return decorate + + +def identity(x): + """ + Return the argument. + + >>> o = object() + >>> identity(o) is o + True + """ + return x + + +def bypass_when(check, *, _op=identity): + """ + Decorate a function to return its parameter when ``check``. + + >>> bypassed = [] # False + + >>> @bypass_when(bypassed) + ... def double(x): + ... return x * 2 + >>> double(2) + 4 + >>> bypassed[:] = [object()] # True + >>> double(2) + 2 + """ + + def decorate(func): + @functools.wraps(func) + def wrapper(param, /): + return param if _op(check) else func(param) + + return wrapper + + return decorate + + +def bypass_unless(check): + """ + Decorate a function to return its parameter unless ``check``. + + >>> enabled = [object()] # True + + >>> @bypass_unless(enabled) + ... def double(x): + ... return x * 2 + >>> double(2) + 4 + >>> del enabled[:] # False + >>> double(2) + 2 + """ + return bypass_when(check, _op=operator.not_) + + +@functools.singledispatch +def _splat_inner(args, func): + """Splat args to func.""" + return func(*args) + + +@_splat_inner.register +def _(args: collections.abc.Mapping, func): + """Splat kargs to func as kwargs.""" + return func(**args) + + +def splat(func): + """ + Wrap func to expect its parameters to be passed positionally in a tuple. + + Has a similar effect to that of ``itertools.starmap`` over + simple ``map``. + + >>> pairs = [(-1, 1), (0, 2)] + >>> setuptools.extern.more_itertools.consume(itertools.starmap(print, pairs)) + -1 1 + 0 2 + >>> setuptools.extern.more_itertools.consume(map(splat(print), pairs)) + -1 1 + 0 2 + + The approach generalizes to other iterators that don't have a "star" + equivalent, such as a "starfilter". + + >>> list(filter(splat(operator.add), pairs)) + [(0, 2)] + + Splat also accepts a mapping argument. + + >>> def is_nice(msg, code): + ... return "smile" in msg or code == 0 + >>> msgs = [ + ... dict(msg='smile!', code=20), + ... dict(msg='error :(', code=1), + ... dict(msg='unknown', code=0), + ... ] + >>> for msg in filter(splat(is_nice), msgs): + ... print(msg) + {'msg': 'smile!', 'code': 20} + {'msg': 'unknown', 'code': 0} + """ + return functools.wraps(func)(functools.partial(_splat_inner, func=func)) diff --git a/venv/Lib/site-packages/setuptools/_vendor/jaraco/functools/__init__.pyi b/venv/Lib/site-packages/setuptools/_vendor/jaraco/functools/__init__.pyi new file mode 100644 index 0000000..c2b9ab1 --- /dev/null +++ b/venv/Lib/site-packages/setuptools/_vendor/jaraco/functools/__init__.pyi @@ -0,0 +1,128 @@ +from collections.abc import Callable, Hashable, Iterator +from functools import partial +from operator import methodcaller +import sys +from typing import ( + Any, + Generic, + Protocol, + TypeVar, + overload, +) + +if sys.version_info >= (3, 10): + from typing import Concatenate, ParamSpec +else: + from typing_extensions import Concatenate, ParamSpec + +_P = ParamSpec('_P') +_R = TypeVar('_R') +_T = TypeVar('_T') +_R1 = TypeVar('_R1') +_R2 = TypeVar('_R2') +_V = TypeVar('_V') +_S = TypeVar('_S') +_R_co = TypeVar('_R_co', covariant=True) + +class _OnceCallable(Protocol[_P, _R]): + saved_result: _R + reset: Callable[[], None] + def __call__(self, *args: _P.args, **kwargs: _P.kwargs) -> _R: ... + +class _ProxyMethodCacheWrapper(Protocol[_R_co]): + cache_clear: Callable[[], None] + def __call__(self, *args: Hashable, **kwargs: Hashable) -> _R_co: ... + +class _MethodCacheWrapper(Protocol[_R_co]): + def cache_clear(self) -> None: ... + def __call__(self, *args: Hashable, **kwargs: Hashable) -> _R_co: ... + +# `compose()` overloads below will cover most use cases. + +@overload +def compose( + __func1: Callable[[_R], _T], + __func2: Callable[_P, _R], + /, +) -> Callable[_P, _T]: ... +@overload +def compose( + __func1: Callable[[_R], _T], + __func2: Callable[[_R1], _R], + __func3: Callable[_P, _R1], + /, +) -> Callable[_P, _T]: ... +@overload +def compose( + __func1: Callable[[_R], _T], + __func2: Callable[[_R2], _R], + __func3: Callable[[_R1], _R2], + __func4: Callable[_P, _R1], + /, +) -> Callable[_P, _T]: ... +def once(func: Callable[_P, _R]) -> _OnceCallable[_P, _R]: ... +def method_cache( + method: Callable[..., _R], + cache_wrapper: Callable[[Callable[..., _R]], _MethodCacheWrapper[_R]] = ..., +) -> _MethodCacheWrapper[_R] | _ProxyMethodCacheWrapper[_R]: ... +def apply( + transform: Callable[[_R], _T] +) -> Callable[[Callable[_P, _R]], Callable[_P, _T]]: ... +def result_invoke( + action: Callable[[_R], Any] +) -> Callable[[Callable[_P, _R]], Callable[_P, _R]]: ... +def invoke( + f: Callable[_P, _R], /, *args: _P.args, **kwargs: _P.kwargs +) -> Callable[_P, _R]: ... +def call_aside( + f: Callable[_P, _R], *args: _P.args, **kwargs: _P.kwargs +) -> Callable[_P, _R]: ... + +class Throttler(Generic[_R]): + last_called: float + func: Callable[..., _R] + max_rate: float + def __init__( + self, func: Callable[..., _R] | Throttler[_R], max_rate: float = ... + ) -> None: ... + def reset(self) -> None: ... + def __call__(self, *args: Any, **kwargs: Any) -> _R: ... + def __get__(self, obj: Any, owner: type[Any] | None = ...) -> Callable[..., _R]: ... + +def first_invoke( + func1: Callable[..., Any], func2: Callable[_P, _R] +) -> Callable[_P, _R]: ... + +method_caller: Callable[..., methodcaller] + +def retry_call( + func: Callable[..., _R], + cleanup: Callable[..., None] = ..., + retries: int | float = ..., + trap: type[BaseException] | tuple[type[BaseException], ...] = ..., +) -> _R: ... +def retry( + cleanup: Callable[..., None] = ..., + retries: int | float = ..., + trap: type[BaseException] | tuple[type[BaseException], ...] = ..., +) -> Callable[[Callable[..., _R]], Callable[..., _R]]: ... +def print_yielded(func: Callable[_P, Iterator[Any]]) -> Callable[_P, None]: ... +def pass_none( + func: Callable[Concatenate[_T, _P], _R] +) -> Callable[Concatenate[_T, _P], _R]: ... +def assign_params( + func: Callable[..., _R], namespace: dict[str, Any] +) -> partial[_R]: ... +def save_method_args( + method: Callable[Concatenate[_S, _P], _R] +) -> Callable[Concatenate[_S, _P], _R]: ... +def except_( + *exceptions: type[BaseException], replace: Any = ..., use: Any = ... +) -> Callable[[Callable[_P, Any]], Callable[_P, Any]]: ... +def identity(x: _T) -> _T: ... +def bypass_when( + check: _V, *, _op: Callable[[_V], Any] = ... +) -> Callable[[Callable[[_T], _R]], Callable[[_T], _T | _R]]: ... +def bypass_unless( + check: Any, +) -> Callable[[Callable[[_T], _R]], Callable[[_T], _T | _R]]: ... diff --git a/venv/Lib/site-packages/setuptools/_vendor/jaraco/functools/__pycache__/__init__.cpython-312.pyc b/venv/Lib/site-packages/setuptools/_vendor/jaraco/functools/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..335ec4c6ecad964cf3d47f907887b706c470c921 GIT binary patch literal 23010 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 Y_k45r9R)Y2XeU2fZ0uH#ifrBg2gi%++5i9m literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/setuptools/_vendor/jaraco/functools/py.typed b/venv/Lib/site-packages/setuptools/_vendor/jaraco/functools/py.typed new file mode 100644 index 0000000..e69de29 diff --git a/venv/Lib/site-packages/setuptools/_vendor/jaraco/text/__init__.py b/venv/Lib/site-packages/setuptools/_vendor/jaraco/text/__init__.py new file mode 100644 index 0000000..a0306d5 --- /dev/null +++ b/venv/Lib/site-packages/setuptools/_vendor/jaraco/text/__init__.py @@ -0,0 +1,599 @@ +import re +import itertools +import textwrap +import functools + +try: + from importlib.resources import files # type: ignore +except ImportError: # pragma: nocover + from setuptools.extern.importlib_resources import files # type: ignore + +from setuptools.extern.jaraco.functools import compose, method_cache +from setuptools.extern.jaraco.context import ExceptionTrap + + +def substitution(old, new): + """ + Return a function that will perform a substitution on a string + """ + return lambda s: s.replace(old, new) + + +def multi_substitution(*substitutions): + """ + Take a sequence of pairs specifying substitutions, and create + a function that performs those substitutions. + + >>> multi_substitution(('foo', 'bar'), ('bar', 'baz'))('foo') + 'baz' + """ + substitutions = itertools.starmap(substitution, substitutions) + # compose function applies last function first, so reverse the + # substitutions to get the expected order. + substitutions = reversed(tuple(substitutions)) + return compose(*substitutions) + + +class FoldedCase(str): + """ + A case insensitive string class; behaves just like str + except compares equal when the only variation is case. + + >>> s = FoldedCase('hello world') + + >>> s == 'Hello World' + True + + >>> 'Hello World' == s + True + + >>> s != 'Hello World' + False + + >>> s.index('O') + 4 + + >>> s.split('O') + ['hell', ' w', 'rld'] + + >>> sorted(map(FoldedCase, ['GAMMA', 'alpha', 'Beta'])) + ['alpha', 'Beta', 'GAMMA'] + + Sequence membership is straightforward. + + >>> "Hello World" in [s] + True + >>> s in ["Hello World"] + True + + You may test for set inclusion, but candidate and elements + must both be folded. + + >>> FoldedCase("Hello World") in {s} + True + >>> s in {FoldedCase("Hello World")} + True + + String inclusion works as long as the FoldedCase object + is on the right. + + >>> "hello" in FoldedCase("Hello World") + True + + But not if the FoldedCase object is on the left: + + >>> FoldedCase('hello') in 'Hello World' + False + + In that case, use ``in_``: + + >>> FoldedCase('hello').in_('Hello World') + True + + >>> FoldedCase('hello') > FoldedCase('Hello') + False + """ + + def __lt__(self, other): + return self.lower() < other.lower() + + def __gt__(self, other): + return self.lower() > other.lower() + + def __eq__(self, other): + return self.lower() == other.lower() + + def __ne__(self, other): + return self.lower() != other.lower() + + def __hash__(self): + return hash(self.lower()) + + def __contains__(self, other): + return super().lower().__contains__(other.lower()) + + def in_(self, other): + "Does self appear in other?" + return self in FoldedCase(other) + + # cache lower since it's likely to be called frequently. + @method_cache + def lower(self): + return super().lower() + + def index(self, sub): + return self.lower().index(sub.lower()) + + def split(self, splitter=' ', maxsplit=0): + pattern = re.compile(re.escape(splitter), re.I) + return pattern.split(self, maxsplit) + + +# Python 3.8 compatibility +_unicode_trap = ExceptionTrap(UnicodeDecodeError) + + +@_unicode_trap.passes +def is_decodable(value): + r""" + Return True if the supplied value is decodable (using the default + encoding). + + >>> is_decodable(b'\xff') + False + >>> is_decodable(b'\x32') + True + """ + value.decode() + + +def is_binary(value): + r""" + Return True if the value appears to be binary (that is, it's a byte + string and isn't decodable). + + >>> is_binary(b'\xff') + True + >>> is_binary('\xff') + False + """ + return isinstance(value, bytes) and not is_decodable(value) + + +def trim(s): + r""" + Trim something like a docstring to remove the whitespace that + is common due to indentation and formatting. + + >>> trim("\n\tfoo = bar\n\t\tbar = baz\n") + 'foo = bar\n\tbar = baz' + """ + return textwrap.dedent(s).strip() + + +def wrap(s): + """ + Wrap lines of text, retaining existing newlines as + paragraph markers. + + >>> print(wrap(lorem_ipsum)) + Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do + eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad + minim veniam, quis nostrud exercitation ullamco laboris nisi ut + aliquip ex ea commodo consequat. Duis aute irure dolor in + reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla + pariatur. Excepteur sint occaecat cupidatat non proident, sunt in + culpa qui officia deserunt mollit anim id est laborum. + + Curabitur pretium tincidunt lacus. Nulla gravida orci a odio. Nullam + varius, turpis et commodo pharetra, est eros bibendum elit, nec luctus + magna felis sollicitudin mauris. Integer in mauris eu nibh euismod + gravida. Duis ac tellus et risus vulputate vehicula. Donec lobortis + risus a elit. Etiam tempor. Ut ullamcorper, ligula eu tempor congue, + eros est euismod turpis, id tincidunt sapien risus a quam. Maecenas + fermentum consequat mi. Donec fermentum. Pellentesque malesuada nulla + a mi. Duis sapien sem, aliquet nec, commodo eget, consequat quis, + neque. Aliquam faucibus, elit ut dictum aliquet, felis nisl adipiscing + sapien, sed malesuada diam lacus eget erat. Cras mollis scelerisque + nunc. Nullam arcu. Aliquam consequat. Curabitur augue lorem, dapibus + quis, laoreet et, pretium ac, nisi. Aenean magna nisl, mollis quis, + molestie eu, feugiat in, orci. In hac habitasse platea dictumst. + """ + paragraphs = s.splitlines() + wrapped = ('\n'.join(textwrap.wrap(para)) for para in paragraphs) + return '\n\n'.join(wrapped) + + +def unwrap(s): + r""" + Given a multi-line string, return an unwrapped version. + + >>> wrapped = wrap(lorem_ipsum) + >>> wrapped.count('\n') + 20 + >>> unwrapped = unwrap(wrapped) + >>> unwrapped.count('\n') + 1 + >>> print(unwrapped) + Lorem ipsum dolor sit amet, consectetur adipiscing ... + Curabitur pretium tincidunt lacus. Nulla gravida orci ... + + """ + paragraphs = re.split(r'\n\n+', s) + cleaned = (para.replace('\n', ' ') for para in paragraphs) + return '\n'.join(cleaned) + + + + +class Splitter(object): + """object that will split a string with the given arguments for each call + + >>> s = Splitter(',') + >>> s('hello, world, this is your, master calling') + ['hello', ' world', ' this is your', ' master calling'] + """ + + def __init__(self, *args): + self.args = args + + def __call__(self, s): + return s.split(*self.args) + + +def indent(string, prefix=' ' * 4): + """ + >>> indent('foo') + ' foo' + """ + return prefix + string + + +class WordSet(tuple): + """ + Given an identifier, return the words that identifier represents, + whether in camel case, underscore-separated, etc. + + >>> WordSet.parse("camelCase") + ('camel', 'Case') + + >>> WordSet.parse("under_sep") + ('under', 'sep') + + Acronyms should be retained + + >>> WordSet.parse("firstSNL") + ('first', 'SNL') + + >>> WordSet.parse("you_and_I") + ('you', 'and', 'I') + + >>> WordSet.parse("A simple test") + ('A', 'simple', 'test') + + Multiple caps should not interfere with the first cap of another word. + + >>> WordSet.parse("myABCClass") + ('my', 'ABC', 'Class') + + The result is a WordSet, so you can get the form you need. + + >>> WordSet.parse("myABCClass").underscore_separated() + 'my_ABC_Class' + + >>> WordSet.parse('a-command').camel_case() + 'ACommand' + + >>> WordSet.parse('someIdentifier').lowered().space_separated() + 'some identifier' + + Slices of the result should return another WordSet. + + >>> WordSet.parse('taken-out-of-context')[1:].underscore_separated() + 'out_of_context' + + >>> WordSet.from_class_name(WordSet()).lowered().space_separated() + 'word set' + + >>> example = WordSet.parse('figured it out') + >>> example.headless_camel_case() + 'figuredItOut' + >>> example.dash_separated() + 'figured-it-out' + + """ + + _pattern = re.compile('([A-Z]?[a-z]+)|([A-Z]+(?![a-z]))') + + def capitalized(self): + return WordSet(word.capitalize() for word in self) + + def lowered(self): + return WordSet(word.lower() for word in self) + + def camel_case(self): + return ''.join(self.capitalized()) + + def headless_camel_case(self): + words = iter(self) + first = next(words).lower() + new_words = itertools.chain((first,), WordSet(words).camel_case()) + return ''.join(new_words) + + def underscore_separated(self): + return '_'.join(self) + + def dash_separated(self): + return '-'.join(self) + + def space_separated(self): + return ' '.join(self) + + def trim_right(self, item): + """ + Remove the item from the end of the set. + + >>> WordSet.parse('foo bar').trim_right('foo') + ('foo', 'bar') + >>> WordSet.parse('foo bar').trim_right('bar') + ('foo',) + >>> WordSet.parse('').trim_right('bar') + () + """ + return self[:-1] if self and self[-1] == item else self + + def trim_left(self, item): + """ + Remove the item from the beginning of the set. + + >>> WordSet.parse('foo bar').trim_left('foo') + ('bar',) + >>> WordSet.parse('foo bar').trim_left('bar') + ('foo', 'bar') + >>> WordSet.parse('').trim_left('bar') + () + """ + return self[1:] if self and self[0] == item else self + + def trim(self, item): + """ + >>> WordSet.parse('foo bar').trim('foo') + ('bar',) + """ + return self.trim_left(item).trim_right(item) + + def __getitem__(self, item): + result = super(WordSet, self).__getitem__(item) + if isinstance(item, slice): + result = WordSet(result) + return result + + @classmethod + def parse(cls, identifier): + matches = cls._pattern.finditer(identifier) + return WordSet(match.group(0) for match in matches) + + @classmethod + def from_class_name(cls, subject): + return cls.parse(subject.__class__.__name__) + + +# for backward compatibility +words = WordSet.parse + + +def simple_html_strip(s): + r""" + Remove HTML from the string `s`. + + >>> str(simple_html_strip('')) + '' + + >>> print(simple_html_strip('A stormy day in paradise')) + A stormy day in paradise + + >>> print(simple_html_strip('Somebody tell the truth.')) + Somebody tell the truth. + + >>> print(simple_html_strip('What about
\nmultiple lines?')) + What about + multiple lines? + """ + html_stripper = re.compile('()|(<[^>]*>)|([^<]+)', re.DOTALL) + texts = (match.group(3) or '' for match in html_stripper.finditer(s)) + return ''.join(texts) + + +class SeparatedValues(str): + """ + A string separated by a separator. Overrides __iter__ for getting + the values. + + >>> list(SeparatedValues('a,b,c')) + ['a', 'b', 'c'] + + Whitespace is stripped and empty values are discarded. + + >>> list(SeparatedValues(' a, b , c, ')) + ['a', 'b', 'c'] + """ + + separator = ',' + + def __iter__(self): + parts = self.split(self.separator) + return filter(None, (part.strip() for part in parts)) + + +class Stripper: + r""" + Given a series of lines, find the common prefix and strip it from them. + + >>> lines = [ + ... 'abcdefg\n', + ... 'abc\n', + ... 'abcde\n', + ... ] + >>> res = Stripper.strip_prefix(lines) + >>> res.prefix + 'abc' + >>> list(res.lines) + ['defg\n', '\n', 'de\n'] + + If no prefix is common, nothing should be stripped. + + >>> lines = [ + ... 'abcd\n', + ... '1234\n', + ... ] + >>> res = Stripper.strip_prefix(lines) + >>> res.prefix = '' + >>> list(res.lines) + ['abcd\n', '1234\n'] + """ + + def __init__(self, prefix, lines): + self.prefix = prefix + self.lines = map(self, lines) + + @classmethod + def strip_prefix(cls, lines): + prefix_lines, lines = itertools.tee(lines) + prefix = functools.reduce(cls.common_prefix, prefix_lines) + return cls(prefix, lines) + + def __call__(self, line): + if not self.prefix: + return line + null, prefix, rest = line.partition(self.prefix) + return rest + + @staticmethod + def common_prefix(s1, s2): + """ + Return the common prefix of two lines. + """ + index = min(len(s1), len(s2)) + while s1[:index] != s2[:index]: + index -= 1 + return s1[:index] + + +def remove_prefix(text, prefix): + """ + Remove the prefix from the text if it exists. + + >>> remove_prefix('underwhelming performance', 'underwhelming ') + 'performance' + + >>> remove_prefix('something special', 'sample') + 'something special' + """ + null, prefix, rest = text.rpartition(prefix) + return rest + + +def remove_suffix(text, suffix): + """ + Remove the suffix from the text if it exists. + + >>> remove_suffix('name.git', '.git') + 'name' + + >>> remove_suffix('something special', 'sample') + 'something special' + """ + rest, suffix, null = text.partition(suffix) + return rest + + +def normalize_newlines(text): + r""" + Replace alternate newlines with the canonical newline. + + >>> normalize_newlines('Lorem Ipsum\u2029') + 'Lorem Ipsum\n' + >>> normalize_newlines('Lorem Ipsum\r\n') + 'Lorem Ipsum\n' + >>> normalize_newlines('Lorem Ipsum\x85') + 'Lorem Ipsum\n' + """ + newlines = ['\r\n', '\r', '\n', '\u0085', '\u2028', '\u2029'] + pattern = '|'.join(newlines) + return re.sub(pattern, '\n', text) + + +def _nonblank(str): + return str and not str.startswith('#') + + +@functools.singledispatch +def yield_lines(iterable): + r""" + Yield valid lines of a string or iterable. + + >>> list(yield_lines('')) + [] + >>> list(yield_lines(['foo', 'bar'])) + ['foo', 'bar'] + >>> list(yield_lines('foo\nbar')) + ['foo', 'bar'] + >>> list(yield_lines('\nfoo\n#bar\nbaz #comment')) + ['foo', 'baz #comment'] + >>> list(yield_lines(['foo\nbar', 'baz', 'bing\n\n\n'])) + ['foo', 'bar', 'baz', 'bing'] + """ + return itertools.chain.from_iterable(map(yield_lines, iterable)) + + +@yield_lines.register(str) +def _(text): + return filter(_nonblank, map(str.strip, text.splitlines())) + + +def drop_comment(line): + """ + Drop comments. + + >>> drop_comment('foo # bar') + 'foo' + + A hash without a space may be in a URL. + + >>> drop_comment('http://example.com/foo#bar') + 'http://example.com/foo#bar' + """ + return line.partition(' #')[0] + + +def join_continuation(lines): + r""" + Join lines continued by a trailing backslash. + + >>> list(join_continuation(['foo \\', 'bar', 'baz'])) + ['foobar', 'baz'] + >>> list(join_continuation(['foo \\', 'bar', 'baz'])) + ['foobar', 'baz'] + >>> list(join_continuation(['foo \\', 'bar \\', 'baz'])) + ['foobarbaz'] + + Not sure why, but... + The character preceeding the backslash is also elided. + + >>> list(join_continuation(['goo\\', 'dly'])) + ['godly'] + + A terrible idea, but... + If no line is available to continue, suppress the lines. + + >>> list(join_continuation(['foo', 'bar\\', 'baz\\'])) + ['foo'] + """ + lines = iter(lines) + for item in lines: + while item.endswith('\\'): + try: + item = item[:-2].strip() + next(lines) + except StopIteration: + return + yield item diff --git a/venv/Lib/site-packages/setuptools/_vendor/jaraco/text/__pycache__/__init__.cpython-312.pyc b/venv/Lib/site-packages/setuptools/_vendor/jaraco/text/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..e5547e859d4ba578c75485d99ac1fb3866dc5b99 GIT binary patch literal 24449 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 JZ;OcZ{{So}5WxTd literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/setuptools/_vendor/more_itertools/__init__.py b/venv/Lib/site-packages/setuptools/_vendor/more_itertools/__init__.py new file mode 100644 index 0000000..19a169f --- /dev/null +++ b/venv/Lib/site-packages/setuptools/_vendor/more_itertools/__init__.py @@ -0,0 +1,4 @@ +from .more import * # noqa +from .recipes import * # noqa + +__version__ = '8.8.0' diff --git a/venv/Lib/site-packages/setuptools/_vendor/more_itertools/__init__.pyi b/venv/Lib/site-packages/setuptools/_vendor/more_itertools/__init__.pyi new file mode 100644 index 0000000..96f6e36 --- /dev/null +++ b/venv/Lib/site-packages/setuptools/_vendor/more_itertools/__init__.pyi @@ -0,0 +1,2 @@ +from .more import * +from .recipes import * diff --git a/venv/Lib/site-packages/setuptools/_vendor/more_itertools/__pycache__/__init__.cpython-312.pyc b/venv/Lib/site-packages/setuptools/_vendor/more_itertools/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..63450773d48a3fc15d77ef49b3712fb34107b216 GIT binary patch literal 296 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%0A1%{LjW)5 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 XIX`GCJG@`{FZ;`n`22sNswn?2Vah!| literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/setuptools/_vendor/more_itertools/more.py b/venv/Lib/site-packages/setuptools/_vendor/more_itertools/more.py new file mode 100644 index 0000000..e6fca4d --- /dev/null +++ b/venv/Lib/site-packages/setuptools/_vendor/more_itertools/more.py @@ -0,0 +1,3824 @@ +import warnings + +from collections import Counter, defaultdict, deque, abc +from collections.abc import Sequence +from functools import partial, reduce, wraps +from heapq import merge, heapify, heapreplace, heappop +from itertools import ( + chain, + compress, + count, + cycle, + dropwhile, + groupby, + islice, + repeat, + starmap, + takewhile, + tee, + zip_longest, +) +from math import exp, factorial, floor, log +from queue import Empty, Queue +from random import random, randrange, uniform +from operator import itemgetter, mul, sub, gt, lt +from sys import hexversion, maxsize +from time import monotonic + +from .recipes import ( + consume, + flatten, + pairwise, + powerset, + take, + unique_everseen, +) + +__all__ = [ + 'AbortThread', + 'adjacent', + 'always_iterable', + 'always_reversible', + 'bucket', + 'callback_iter', + 'chunked', + 'circular_shifts', + 'collapse', + 'collate', + 'consecutive_groups', + 'consumer', + 'countable', + 'count_cycle', + 'mark_ends', + 'difference', + 'distinct_combinations', + 'distinct_permutations', + 'distribute', + 'divide', + 'exactly_n', + 'filter_except', + 'first', + 'groupby_transform', + 'ilen', + 'interleave_longest', + 'interleave', + 'intersperse', + 'islice_extended', + 'iterate', + 'ichunked', + 'is_sorted', + 'last', + 'locate', + 'lstrip', + 'make_decorator', + 'map_except', + 'map_reduce', + 'nth_or_last', + 'nth_permutation', + 'nth_product', + 'numeric_range', + 'one', + 'only', + 'padded', + 'partitions', + 'set_partitions', + 'peekable', + 'repeat_last', + 'replace', + 'rlocate', + 'rstrip', + 'run_length', + 'sample', + 'seekable', + 'SequenceView', + 'side_effect', + 'sliced', + 'sort_together', + 'split_at', + 'split_after', + 'split_before', + 'split_when', + 'split_into', + 'spy', + 'stagger', + 'strip', + 'substrings', + 'substrings_indexes', + 'time_limited', + 'unique_to_each', + 'unzip', + 'windowed', + 'with_iter', + 'UnequalIterablesError', + 'zip_equal', + 'zip_offset', + 'windowed_complete', + 'all_unique', + 'value_chain', + 'product_index', + 'combination_index', + 'permutation_index', +] + +_marker = object() + + +def chunked(iterable, n, strict=False): + """Break *iterable* into lists of length *n*: + + >>> list(chunked([1, 2, 3, 4, 5, 6], 3)) + [[1, 2, 3], [4, 5, 6]] + + By the default, the last yielded list will have fewer than *n* elements + if the length of *iterable* is not divisible by *n*: + + >>> list(chunked([1, 2, 3, 4, 5, 6, 7, 8], 3)) + [[1, 2, 3], [4, 5, 6], [7, 8]] + + To use a fill-in value instead, see the :func:`grouper` recipe. + + If the length of *iterable* is not divisible by *n* and *strict* is + ``True``, then ``ValueError`` will be raised before the last + list is yielded. + + """ + iterator = iter(partial(take, n, iter(iterable)), []) + if strict: + + def ret(): + for chunk in iterator: + if len(chunk) != n: + raise ValueError('iterable is not divisible by n.') + yield chunk + + return iter(ret()) + else: + return iterator + + +def first(iterable, default=_marker): + """Return the first item of *iterable*, or *default* if *iterable* is + empty. + + >>> first([0, 1, 2, 3]) + 0 + >>> first([], 'some default') + 'some default' + + If *default* is not provided and there are no items in the iterable, + raise ``ValueError``. + + :func:`first` is useful when you have a generator of expensive-to-retrieve + values and want any arbitrary one. It is marginally shorter than + ``next(iter(iterable), default)``. + + """ + try: + return next(iter(iterable)) + except StopIteration as e: + if default is _marker: + raise ValueError( + 'first() was called on an empty iterable, and no ' + 'default value was provided.' + ) from e + return default + + +def last(iterable, default=_marker): + """Return the last item of *iterable*, or *default* if *iterable* is + empty. + + >>> last([0, 1, 2, 3]) + 3 + >>> last([], 'some default') + 'some default' + + If *default* is not provided and there are no items in the iterable, + raise ``ValueError``. + """ + try: + if isinstance(iterable, Sequence): + return iterable[-1] + # Work around https://bugs.python.org/issue38525 + elif hasattr(iterable, '__reversed__') and (hexversion != 0x030800F0): + return next(reversed(iterable)) + else: + return deque(iterable, maxlen=1)[-1] + except (IndexError, TypeError, StopIteration): + if default is _marker: + raise ValueError( + 'last() was called on an empty iterable, and no default was ' + 'provided.' + ) + return default + + +def nth_or_last(iterable, n, default=_marker): + """Return the nth or the last item of *iterable*, + or *default* if *iterable* is empty. + + >>> nth_or_last([0, 1, 2, 3], 2) + 2 + >>> nth_or_last([0, 1], 2) + 1 + >>> nth_or_last([], 0, 'some default') + 'some default' + + If *default* is not provided and there are no items in the iterable, + raise ``ValueError``. + """ + return last(islice(iterable, n + 1), default=default) + + +class peekable: + """Wrap an iterator to allow lookahead and prepending elements. + + Call :meth:`peek` on the result to get the value that will be returned + by :func:`next`. This won't advance the iterator: + + >>> p = peekable(['a', 'b']) + >>> p.peek() + 'a' + >>> next(p) + 'a' + + Pass :meth:`peek` a default value to return that instead of raising + ``StopIteration`` when the iterator is exhausted. + + >>> p = peekable([]) + >>> p.peek('hi') + 'hi' + + peekables also offer a :meth:`prepend` method, which "inserts" items + at the head of the iterable: + + >>> p = peekable([1, 2, 3]) + >>> p.prepend(10, 11, 12) + >>> next(p) + 10 + >>> p.peek() + 11 + >>> list(p) + [11, 12, 1, 2, 3] + + peekables can be indexed. Index 0 is the item that will be returned by + :func:`next`, index 1 is the item after that, and so on: + The values up to the given index will be cached. + + >>> p = peekable(['a', 'b', 'c', 'd']) + >>> p[0] + 'a' + >>> p[1] + 'b' + >>> next(p) + 'a' + + Negative indexes are supported, but be aware that they will cache the + remaining items in the source iterator, which may require significant + storage. + + To check whether a peekable is exhausted, check its truth value: + + >>> p = peekable(['a', 'b']) + >>> if p: # peekable has items + ... list(p) + ['a', 'b'] + >>> if not p: # peekable is exhausted + ... list(p) + [] + + """ + + def __init__(self, iterable): + self._it = iter(iterable) + self._cache = deque() + + def __iter__(self): + return self + + def __bool__(self): + try: + self.peek() + except StopIteration: + return False + return True + + def peek(self, default=_marker): + """Return the item that will be next returned from ``next()``. + + Return ``default`` if there are no items left. If ``default`` is not + provided, raise ``StopIteration``. + + """ + if not self._cache: + try: + self._cache.append(next(self._it)) + except StopIteration: + if default is _marker: + raise + return default + return self._cache[0] + + def prepend(self, *items): + """Stack up items to be the next ones returned from ``next()`` or + ``self.peek()``. The items will be returned in + first in, first out order:: + + >>> p = peekable([1, 2, 3]) + >>> p.prepend(10, 11, 12) + >>> next(p) + 10 + >>> list(p) + [11, 12, 1, 2, 3] + + It is possible, by prepending items, to "resurrect" a peekable that + previously raised ``StopIteration``. + + >>> p = peekable([]) + >>> next(p) + Traceback (most recent call last): + ... + StopIteration + >>> p.prepend(1) + >>> next(p) + 1 + >>> next(p) + Traceback (most recent call last): + ... + StopIteration + + """ + self._cache.extendleft(reversed(items)) + + def __next__(self): + if self._cache: + return self._cache.popleft() + + return next(self._it) + + def _get_slice(self, index): + # Normalize the slice's arguments + step = 1 if (index.step is None) else index.step + if step > 0: + start = 0 if (index.start is None) else index.start + stop = maxsize if (index.stop is None) else index.stop + elif step < 0: + start = -1 if (index.start is None) else index.start + stop = (-maxsize - 1) if (index.stop is None) else index.stop + else: + raise ValueError('slice step cannot be zero') + + # If either the start or stop index is negative, we'll need to cache + # the rest of the iterable in order to slice from the right side. + if (start < 0) or (stop < 0): + self._cache.extend(self._it) + # Otherwise we'll need to find the rightmost index and cache to that + # point. + else: + n = min(max(start, stop) + 1, maxsize) + cache_len = len(self._cache) + if n >= cache_len: + self._cache.extend(islice(self._it, n - cache_len)) + + return list(self._cache)[index] + + def __getitem__(self, index): + if isinstance(index, slice): + return self._get_slice(index) + + cache_len = len(self._cache) + if index < 0: + self._cache.extend(self._it) + elif index >= cache_len: + self._cache.extend(islice(self._it, index + 1 - cache_len)) + + return self._cache[index] + + +def collate(*iterables, **kwargs): + """Return a sorted merge of the items from each of several already-sorted + *iterables*. + + >>> list(collate('ACDZ', 'AZ', 'JKL')) + ['A', 'A', 'C', 'D', 'J', 'K', 'L', 'Z', 'Z'] + + Works lazily, keeping only the next value from each iterable in memory. Use + :func:`collate` to, for example, perform a n-way mergesort of items that + don't fit in memory. + + If a *key* function is specified, the iterables will be sorted according + to its result: + + >>> key = lambda s: int(s) # Sort by numeric value, not by string + >>> list(collate(['1', '10'], ['2', '11'], key=key)) + ['1', '2', '10', '11'] + + + If the *iterables* are sorted in descending order, set *reverse* to + ``True``: + + >>> list(collate([5, 3, 1], [4, 2, 0], reverse=True)) + [5, 4, 3, 2, 1, 0] + + If the elements of the passed-in iterables are out of order, you might get + unexpected results. + + On Python 3.5+, this function is an alias for :func:`heapq.merge`. + + """ + warnings.warn( + "collate is no longer part of more_itertools, use heapq.merge", + DeprecationWarning, + ) + return merge(*iterables, **kwargs) + + +def consumer(func): + """Decorator that automatically advances a PEP-342-style "reverse iterator" + to its first yield point so you don't have to call ``next()`` on it + manually. + + >>> @consumer + ... def tally(): + ... i = 0 + ... while True: + ... print('Thing number %s is %s.' % (i, (yield))) + ... i += 1 + ... + >>> t = tally() + >>> t.send('red') + Thing number 0 is red. + >>> t.send('fish') + Thing number 1 is fish. + + Without the decorator, you would have to call ``next(t)`` before + ``t.send()`` could be used. + + """ + + @wraps(func) + def wrapper(*args, **kwargs): + gen = func(*args, **kwargs) + next(gen) + return gen + + return wrapper + + +def ilen(iterable): + """Return the number of items in *iterable*. + + >>> ilen(x for x in range(1000000) if x % 3 == 0) + 333334 + + This consumes the iterable, so handle with care. + + """ + # This approach was selected because benchmarks showed it's likely the + # fastest of the known implementations at the time of writing. + # See GitHub tracker: #236, #230. + counter = count() + deque(zip(iterable, counter), maxlen=0) + return next(counter) + + +def iterate(func, start): + """Return ``start``, ``func(start)``, ``func(func(start))``, ... + + >>> from itertools import islice + >>> list(islice(iterate(lambda x: 2*x, 1), 10)) + [1, 2, 4, 8, 16, 32, 64, 128, 256, 512] + + """ + while True: + yield start + start = func(start) + + +def with_iter(context_manager): + """Wrap an iterable in a ``with`` statement, so it closes once exhausted. + + For example, this will close the file when the iterator is exhausted:: + + upper_lines = (line.upper() for line in with_iter(open('foo'))) + + Any context manager which returns an iterable is a candidate for + ``with_iter``. + + """ + with context_manager as iterable: + yield from iterable + + +def one(iterable, too_short=None, too_long=None): + """Return the first item from *iterable*, which is expected to contain only + that item. Raise an exception if *iterable* is empty or has more than one + item. + + :func:`one` is useful for ensuring that an iterable contains only one item. + For example, it can be used to retrieve the result of a database query + that is expected to return a single row. + + If *iterable* is empty, ``ValueError`` will be raised. You may specify a + different exception with the *too_short* keyword: + + >>> it = [] + >>> one(it) # doctest: +IGNORE_EXCEPTION_DETAIL + Traceback (most recent call last): + ... + ValueError: too many items in iterable (expected 1)' + >>> too_short = IndexError('too few items') + >>> one(it, too_short=too_short) # doctest: +IGNORE_EXCEPTION_DETAIL + Traceback (most recent call last): + ... + IndexError: too few items + + Similarly, if *iterable* contains more than one item, ``ValueError`` will + be raised. You may specify a different exception with the *too_long* + keyword: + + >>> it = ['too', 'many'] + >>> one(it) # doctest: +IGNORE_EXCEPTION_DETAIL + Traceback (most recent call last): + ... + ValueError: Expected exactly one item in iterable, but got 'too', + 'many', and perhaps more. + >>> too_long = RuntimeError + >>> one(it, too_long=too_long) # doctest: +IGNORE_EXCEPTION_DETAIL + Traceback (most recent call last): + ... + RuntimeError + + Note that :func:`one` attempts to advance *iterable* twice to ensure there + is only one item. See :func:`spy` or :func:`peekable` to check iterable + contents less destructively. + + """ + it = iter(iterable) + + try: + first_value = next(it) + except StopIteration as e: + raise ( + too_short or ValueError('too few items in iterable (expected 1)') + ) from e + + try: + second_value = next(it) + except StopIteration: + pass + else: + msg = ( + 'Expected exactly one item in iterable, but got {!r}, {!r}, ' + 'and perhaps more.'.format(first_value, second_value) + ) + raise too_long or ValueError(msg) + + return first_value + + +def distinct_permutations(iterable, r=None): + """Yield successive distinct permutations of the elements in *iterable*. + + >>> sorted(distinct_permutations([1, 0, 1])) + [(0, 1, 1), (1, 0, 1), (1, 1, 0)] + + Equivalent to ``set(permutations(iterable))``, except duplicates are not + generated and thrown away. For larger input sequences this is much more + efficient. + + Duplicate permutations arise when there are duplicated elements in the + input iterable. The number of items returned is + `n! / (x_1! * x_2! * ... * x_n!)`, where `n` is the total number of + items input, and each `x_i` is the count of a distinct item in the input + sequence. + + If *r* is given, only the *r*-length permutations are yielded. + + >>> sorted(distinct_permutations([1, 0, 1], r=2)) + [(0, 1), (1, 0), (1, 1)] + >>> sorted(distinct_permutations(range(3), r=2)) + [(0, 1), (0, 2), (1, 0), (1, 2), (2, 0), (2, 1)] + + """ + # Algorithm: https://w.wiki/Qai + def _full(A): + while True: + # Yield the permutation we have + yield tuple(A) + + # Find the largest index i such that A[i] < A[i + 1] + for i in range(size - 2, -1, -1): + if A[i] < A[i + 1]: + break + # If no such index exists, this permutation is the last one + else: + return + + # Find the largest index j greater than j such that A[i] < A[j] + for j in range(size - 1, i, -1): + if A[i] < A[j]: + break + + # Swap the value of A[i] with that of A[j], then reverse the + # sequence from A[i + 1] to form the new permutation + A[i], A[j] = A[j], A[i] + A[i + 1 :] = A[: i - size : -1] # A[i + 1:][::-1] + + # Algorithm: modified from the above + def _partial(A, r): + # Split A into the first r items and the last r items + head, tail = A[:r], A[r:] + right_head_indexes = range(r - 1, -1, -1) + left_tail_indexes = range(len(tail)) + + while True: + # Yield the permutation we have + yield tuple(head) + + # Starting from the right, find the first index of the head with + # value smaller than the maximum value of the tail - call it i. + pivot = tail[-1] + for i in right_head_indexes: + if head[i] < pivot: + break + pivot = head[i] + else: + return + + # Starting from the left, find the first value of the tail + # with a value greater than head[i] and swap. + for j in left_tail_indexes: + if tail[j] > head[i]: + head[i], tail[j] = tail[j], head[i] + break + # If we didn't find one, start from the right and find the first + # index of the head with a value greater than head[i] and swap. + else: + for j in right_head_indexes: + if head[j] > head[i]: + head[i], head[j] = head[j], head[i] + break + + # Reverse head[i + 1:] and swap it with tail[:r - (i + 1)] + tail += head[: i - r : -1] # head[i + 1:][::-1] + i += 1 + head[i:], tail[:] = tail[: r - i], tail[r - i :] + + items = sorted(iterable) + + size = len(items) + if r is None: + r = size + + if 0 < r <= size: + return _full(items) if (r == size) else _partial(items, r) + + return iter(() if r else ((),)) + + +def intersperse(e, iterable, n=1): + """Intersperse filler element *e* among the items in *iterable*, leaving + *n* items between each filler element. + + >>> list(intersperse('!', [1, 2, 3, 4, 5])) + [1, '!', 2, '!', 3, '!', 4, '!', 5] + + >>> list(intersperse(None, [1, 2, 3, 4, 5], n=2)) + [1, 2, None, 3, 4, None, 5] + + """ + if n == 0: + raise ValueError('n must be > 0') + elif n == 1: + # interleave(repeat(e), iterable) -> e, x_0, e, e, x_1, e, x_2... + # islice(..., 1, None) -> x_0, e, e, x_1, e, x_2... + return islice(interleave(repeat(e), iterable), 1, None) + else: + # interleave(filler, chunks) -> [e], [x_0, x_1], [e], [x_2, x_3]... + # islice(..., 1, None) -> [x_0, x_1], [e], [x_2, x_3]... + # flatten(...) -> x_0, x_1, e, x_2, x_3... + filler = repeat([e]) + chunks = chunked(iterable, n) + return flatten(islice(interleave(filler, chunks), 1, None)) + + +def unique_to_each(*iterables): + """Return the elements from each of the input iterables that aren't in the + other input iterables. + + For example, suppose you have a set of packages, each with a set of + dependencies:: + + {'pkg_1': {'A', 'B'}, 'pkg_2': {'B', 'C'}, 'pkg_3': {'B', 'D'}} + + If you remove one package, which dependencies can also be removed? + + If ``pkg_1`` is removed, then ``A`` is no longer necessary - it is not + associated with ``pkg_2`` or ``pkg_3``. Similarly, ``C`` is only needed for + ``pkg_2``, and ``D`` is only needed for ``pkg_3``:: + + >>> unique_to_each({'A', 'B'}, {'B', 'C'}, {'B', 'D'}) + [['A'], ['C'], ['D']] + + If there are duplicates in one input iterable that aren't in the others + they will be duplicated in the output. Input order is preserved:: + + >>> unique_to_each("mississippi", "missouri") + [['p', 'p'], ['o', 'u', 'r']] + + It is assumed that the elements of each iterable are hashable. + + """ + pool = [list(it) for it in iterables] + counts = Counter(chain.from_iterable(map(set, pool))) + uniques = {element for element in counts if counts[element] == 1} + return [list(filter(uniques.__contains__, it)) for it in pool] + + +def windowed(seq, n, fillvalue=None, step=1): + """Return a sliding window of width *n* over the given iterable. + + >>> all_windows = windowed([1, 2, 3, 4, 5], 3) + >>> list(all_windows) + [(1, 2, 3), (2, 3, 4), (3, 4, 5)] + + When the window is larger than the iterable, *fillvalue* is used in place + of missing values: + + >>> list(windowed([1, 2, 3], 4)) + [(1, 2, 3, None)] + + Each window will advance in increments of *step*: + + >>> list(windowed([1, 2, 3, 4, 5, 6], 3, fillvalue='!', step=2)) + [(1, 2, 3), (3, 4, 5), (5, 6, '!')] + + To slide into the iterable's items, use :func:`chain` to add filler items + to the left: + + >>> iterable = [1, 2, 3, 4] + >>> n = 3 + >>> padding = [None] * (n - 1) + >>> list(windowed(chain(padding, iterable), 3)) + [(None, None, 1), (None, 1, 2), (1, 2, 3), (2, 3, 4)] + """ + if n < 0: + raise ValueError('n must be >= 0') + if n == 0: + yield tuple() + return + if step < 1: + raise ValueError('step must be >= 1') + + window = deque(maxlen=n) + i = n + for _ in map(window.append, seq): + i -= 1 + if not i: + i = step + yield tuple(window) + + size = len(window) + if size < n: + yield tuple(chain(window, repeat(fillvalue, n - size))) + elif 0 < i < min(step, n): + window += (fillvalue,) * i + yield tuple(window) + + +def substrings(iterable): + """Yield all of the substrings of *iterable*. + + >>> [''.join(s) for s in substrings('more')] + ['m', 'o', 'r', 'e', 'mo', 'or', 're', 'mor', 'ore', 'more'] + + Note that non-string iterables can also be subdivided. + + >>> list(substrings([0, 1, 2])) + [(0,), (1,), (2,), (0, 1), (1, 2), (0, 1, 2)] + + """ + # The length-1 substrings + seq = [] + for item in iter(iterable): + seq.append(item) + yield (item,) + seq = tuple(seq) + item_count = len(seq) + + # And the rest + for n in range(2, item_count + 1): + for i in range(item_count - n + 1): + yield seq[i : i + n] + + +def substrings_indexes(seq, reverse=False): + """Yield all substrings and their positions in *seq* + + The items yielded will be a tuple of the form ``(substr, i, j)``, where + ``substr == seq[i:j]``. + + This function only works for iterables that support slicing, such as + ``str`` objects. + + >>> for item in substrings_indexes('more'): + ... print(item) + ('m', 0, 1) + ('o', 1, 2) + ('r', 2, 3) + ('e', 3, 4) + ('mo', 0, 2) + ('or', 1, 3) + ('re', 2, 4) + ('mor', 0, 3) + ('ore', 1, 4) + ('more', 0, 4) + + Set *reverse* to ``True`` to yield the same items in the opposite order. + + + """ + r = range(1, len(seq) + 1) + if reverse: + r = reversed(r) + return ( + (seq[i : i + L], i, i + L) for L in r for i in range(len(seq) - L + 1) + ) + + +class bucket: + """Wrap *iterable* and return an object that buckets it iterable into + child iterables based on a *key* function. + + >>> iterable = ['a1', 'b1', 'c1', 'a2', 'b2', 'c2', 'b3'] + >>> s = bucket(iterable, key=lambda x: x[0]) # Bucket by 1st character + >>> sorted(list(s)) # Get the keys + ['a', 'b', 'c'] + >>> a_iterable = s['a'] + >>> next(a_iterable) + 'a1' + >>> next(a_iterable) + 'a2' + >>> list(s['b']) + ['b1', 'b2', 'b3'] + + The original iterable will be advanced and its items will be cached until + they are used by the child iterables. This may require significant storage. + + By default, attempting to select a bucket to which no items belong will + exhaust the iterable and cache all values. + If you specify a *validator* function, selected buckets will instead be + checked against it. + + >>> from itertools import count + >>> it = count(1, 2) # Infinite sequence of odd numbers + >>> key = lambda x: x % 10 # Bucket by last digit + >>> validator = lambda x: x in {1, 3, 5, 7, 9} # Odd digits only + >>> s = bucket(it, key=key, validator=validator) + >>> 2 in s + False + >>> list(s[2]) + [] + + """ + + def __init__(self, iterable, key, validator=None): + self._it = iter(iterable) + self._key = key + self._cache = defaultdict(deque) + self._validator = validator or (lambda x: True) + + def __contains__(self, value): + if not self._validator(value): + return False + + try: + item = next(self[value]) + except StopIteration: + return False + else: + self._cache[value].appendleft(item) + + return True + + def _get_values(self, value): + """ + Helper to yield items from the parent iterator that match *value*. + Items that don't match are stored in the local cache as they + are encountered. + """ + while True: + # If we've cached some items that match the target value, emit + # the first one and evict it from the cache. + if self._cache[value]: + yield self._cache[value].popleft() + # Otherwise we need to advance the parent iterator to search for + # a matching item, caching the rest. + else: + while True: + try: + item = next(self._it) + except StopIteration: + return + item_value = self._key(item) + if item_value == value: + yield item + break + elif self._validator(item_value): + self._cache[item_value].append(item) + + def __iter__(self): + for item in self._it: + item_value = self._key(item) + if self._validator(item_value): + self._cache[item_value].append(item) + + yield from self._cache.keys() + + def __getitem__(self, value): + if not self._validator(value): + return iter(()) + + return self._get_values(value) + + +def spy(iterable, n=1): + """Return a 2-tuple with a list containing the first *n* elements of + *iterable*, and an iterator with the same items as *iterable*. + This allows you to "look ahead" at the items in the iterable without + advancing it. + + There is one item in the list by default: + + >>> iterable = 'abcdefg' + >>> head, iterable = spy(iterable) + >>> head + ['a'] + >>> list(iterable) + ['a', 'b', 'c', 'd', 'e', 'f', 'g'] + + You may use unpacking to retrieve items instead of lists: + + >>> (head,), iterable = spy('abcdefg') + >>> head + 'a' + >>> (first, second), iterable = spy('abcdefg', 2) + >>> first + 'a' + >>> second + 'b' + + The number of items requested can be larger than the number of items in + the iterable: + + >>> iterable = [1, 2, 3, 4, 5] + >>> head, iterable = spy(iterable, 10) + >>> head + [1, 2, 3, 4, 5] + >>> list(iterable) + [1, 2, 3, 4, 5] + + """ + it = iter(iterable) + head = take(n, it) + + return head.copy(), chain(head, it) + + +def interleave(*iterables): + """Return a new iterable yielding from each iterable in turn, + until the shortest is exhausted. + + >>> list(interleave([1, 2, 3], [4, 5], [6, 7, 8])) + [1, 4, 6, 2, 5, 7] + + For a version that doesn't terminate after the shortest iterable is + exhausted, see :func:`interleave_longest`. + + """ + return chain.from_iterable(zip(*iterables)) + + +def interleave_longest(*iterables): + """Return a new iterable yielding from each iterable in turn, + skipping any that are exhausted. + + >>> list(interleave_longest([1, 2, 3], [4, 5], [6, 7, 8])) + [1, 4, 6, 2, 5, 7, 3, 8] + + This function produces the same output as :func:`roundrobin`, but may + perform better for some inputs (in particular when the number of iterables + is large). + + """ + i = chain.from_iterable(zip_longest(*iterables, fillvalue=_marker)) + return (x for x in i if x is not _marker) + + +def collapse(iterable, base_type=None, levels=None): + """Flatten an iterable with multiple levels of nesting (e.g., a list of + lists of tuples) into non-iterable types. + + >>> iterable = [(1, 2), ([3, 4], [[5], [6]])] + >>> list(collapse(iterable)) + [1, 2, 3, 4, 5, 6] + + Binary and text strings are not considered iterable and + will not be collapsed. + + To avoid collapsing other types, specify *base_type*: + + >>> iterable = ['ab', ('cd', 'ef'), ['gh', 'ij']] + >>> list(collapse(iterable, base_type=tuple)) + ['ab', ('cd', 'ef'), 'gh', 'ij'] + + Specify *levels* to stop flattening after a certain level: + + >>> iterable = [('a', ['b']), ('c', ['d'])] + >>> list(collapse(iterable)) # Fully flattened + ['a', 'b', 'c', 'd'] + >>> list(collapse(iterable, levels=1)) # Only one level flattened + ['a', ['b'], 'c', ['d']] + + """ + + def walk(node, level): + if ( + ((levels is not None) and (level > levels)) + or isinstance(node, (str, bytes)) + or ((base_type is not None) and isinstance(node, base_type)) + ): + yield node + return + + try: + tree = iter(node) + except TypeError: + yield node + return + else: + for child in tree: + yield from walk(child, level + 1) + + yield from walk(iterable, 0) + + +def side_effect(func, iterable, chunk_size=None, before=None, after=None): + """Invoke *func* on each item in *iterable* (or on each *chunk_size* group + of items) before yielding the item. + + `func` must be a function that takes a single argument. Its return value + will be discarded. + + *before* and *after* are optional functions that take no arguments. They + will be executed before iteration starts and after it ends, respectively. + + `side_effect` can be used for logging, updating progress bars, or anything + that is not functionally "pure." + + Emitting a status message: + + >>> from more_itertools import consume + >>> func = lambda item: print('Received {}'.format(item)) + >>> consume(side_effect(func, range(2))) + Received 0 + Received 1 + + Operating on chunks of items: + + >>> pair_sums = [] + >>> func = lambda chunk: pair_sums.append(sum(chunk)) + >>> list(side_effect(func, [0, 1, 2, 3, 4, 5], 2)) + [0, 1, 2, 3, 4, 5] + >>> list(pair_sums) + [1, 5, 9] + + Writing to a file-like object: + + >>> from io import StringIO + >>> from more_itertools import consume + >>> f = StringIO() + >>> func = lambda x: print(x, file=f) + >>> before = lambda: print(u'HEADER', file=f) + >>> after = f.close + >>> it = [u'a', u'b', u'c'] + >>> consume(side_effect(func, it, before=before, after=after)) + >>> f.closed + True + + """ + try: + if before is not None: + before() + + if chunk_size is None: + for item in iterable: + func(item) + yield item + else: + for chunk in chunked(iterable, chunk_size): + func(chunk) + yield from chunk + finally: + if after is not None: + after() + + +def sliced(seq, n, strict=False): + """Yield slices of length *n* from the sequence *seq*. + + >>> list(sliced((1, 2, 3, 4, 5, 6), 3)) + [(1, 2, 3), (4, 5, 6)] + + By the default, the last yielded slice will have fewer than *n* elements + if the length of *seq* is not divisible by *n*: + + >>> list(sliced((1, 2, 3, 4, 5, 6, 7, 8), 3)) + [(1, 2, 3), (4, 5, 6), (7, 8)] + + If the length of *seq* is not divisible by *n* and *strict* is + ``True``, then ``ValueError`` will be raised before the last + slice is yielded. + + This function will only work for iterables that support slicing. + For non-sliceable iterables, see :func:`chunked`. + + """ + iterator = takewhile(len, (seq[i : i + n] for i in count(0, n))) + if strict: + + def ret(): + for _slice in iterator: + if len(_slice) != n: + raise ValueError("seq is not divisible by n.") + yield _slice + + return iter(ret()) + else: + return iterator + + +def split_at(iterable, pred, maxsplit=-1, keep_separator=False): + """Yield lists of items from *iterable*, where each list is delimited by + an item where callable *pred* returns ``True``. + + >>> list(split_at('abcdcba', lambda x: x == 'b')) + [['a'], ['c', 'd', 'c'], ['a']] + + >>> list(split_at(range(10), lambda n: n % 2 == 1)) + [[0], [2], [4], [6], [8], []] + + At most *maxsplit* splits are done. If *maxsplit* is not specified or -1, + then there is no limit on the number of splits: + + >>> list(split_at(range(10), lambda n: n % 2 == 1, maxsplit=2)) + [[0], [2], [4, 5, 6, 7, 8, 9]] + + By default, the delimiting items are not included in the output. + The include them, set *keep_separator* to ``True``. + + >>> list(split_at('abcdcba', lambda x: x == 'b', keep_separator=True)) + [['a'], ['b'], ['c', 'd', 'c'], ['b'], ['a']] + + """ + if maxsplit == 0: + yield list(iterable) + return + + buf = [] + it = iter(iterable) + for item in it: + if pred(item): + yield buf + if keep_separator: + yield [item] + if maxsplit == 1: + yield list(it) + return + buf = [] + maxsplit -= 1 + else: + buf.append(item) + yield buf + + +def split_before(iterable, pred, maxsplit=-1): + """Yield lists of items from *iterable*, where each list ends just before + an item for which callable *pred* returns ``True``: + + >>> list(split_before('OneTwo', lambda s: s.isupper())) + [['O', 'n', 'e'], ['T', 'w', 'o']] + + >>> list(split_before(range(10), lambda n: n % 3 == 0)) + [[0, 1, 2], [3, 4, 5], [6, 7, 8], [9]] + + At most *maxsplit* splits are done. If *maxsplit* is not specified or -1, + then there is no limit on the number of splits: + + >>> list(split_before(range(10), lambda n: n % 3 == 0, maxsplit=2)) + [[0, 1, 2], [3, 4, 5], [6, 7, 8, 9]] + """ + if maxsplit == 0: + yield list(iterable) + return + + buf = [] + it = iter(iterable) + for item in it: + if pred(item) and buf: + yield buf + if maxsplit == 1: + yield [item] + list(it) + return + buf = [] + maxsplit -= 1 + buf.append(item) + if buf: + yield buf + + +def split_after(iterable, pred, maxsplit=-1): + """Yield lists of items from *iterable*, where each list ends with an + item where callable *pred* returns ``True``: + + >>> list(split_after('one1two2', lambda s: s.isdigit())) + [['o', 'n', 'e', '1'], ['t', 'w', 'o', '2']] + + >>> list(split_after(range(10), lambda n: n % 3 == 0)) + [[0], [1, 2, 3], [4, 5, 6], [7, 8, 9]] + + At most *maxsplit* splits are done. If *maxsplit* is not specified or -1, + then there is no limit on the number of splits: + + >>> list(split_after(range(10), lambda n: n % 3 == 0, maxsplit=2)) + [[0], [1, 2, 3], [4, 5, 6, 7, 8, 9]] + + """ + if maxsplit == 0: + yield list(iterable) + return + + buf = [] + it = iter(iterable) + for item in it: + buf.append(item) + if pred(item) and buf: + yield buf + if maxsplit == 1: + yield list(it) + return + buf = [] + maxsplit -= 1 + if buf: + yield buf + + +def split_when(iterable, pred, maxsplit=-1): + """Split *iterable* into pieces based on the output of *pred*. + *pred* should be a function that takes successive pairs of items and + returns ``True`` if the iterable should be split in between them. + + For example, to find runs of increasing numbers, split the iterable when + element ``i`` is larger than element ``i + 1``: + + >>> list(split_when([1, 2, 3, 3, 2, 5, 2, 4, 2], lambda x, y: x > y)) + [[1, 2, 3, 3], [2, 5], [2, 4], [2]] + + At most *maxsplit* splits are done. If *maxsplit* is not specified or -1, + then there is no limit on the number of splits: + + >>> list(split_when([1, 2, 3, 3, 2, 5, 2, 4, 2], + ... lambda x, y: x > y, maxsplit=2)) + [[1, 2, 3, 3], [2, 5], [2, 4, 2]] + + """ + if maxsplit == 0: + yield list(iterable) + return + + it = iter(iterable) + try: + cur_item = next(it) + except StopIteration: + return + + buf = [cur_item] + for next_item in it: + if pred(cur_item, next_item): + yield buf + if maxsplit == 1: + yield [next_item] + list(it) + return + buf = [] + maxsplit -= 1 + + buf.append(next_item) + cur_item = next_item + + yield buf + + +def split_into(iterable, sizes): + """Yield a list of sequential items from *iterable* of length 'n' for each + integer 'n' in *sizes*. + + >>> list(split_into([1,2,3,4,5,6], [1,2,3])) + [[1], [2, 3], [4, 5, 6]] + + If the sum of *sizes* is smaller than the length of *iterable*, then the + remaining items of *iterable* will not be returned. + + >>> list(split_into([1,2,3,4,5,6], [2,3])) + [[1, 2], [3, 4, 5]] + + If the sum of *sizes* is larger than the length of *iterable*, fewer items + will be returned in the iteration that overruns *iterable* and further + lists will be empty: + + >>> list(split_into([1,2,3,4], [1,2,3,4])) + [[1], [2, 3], [4], []] + + When a ``None`` object is encountered in *sizes*, the returned list will + contain items up to the end of *iterable* the same way that itertools.slice + does: + + >>> list(split_into([1,2,3,4,5,6,7,8,9,0], [2,3,None])) + [[1, 2], [3, 4, 5], [6, 7, 8, 9, 0]] + + :func:`split_into` can be useful for grouping a series of items where the + sizes of the groups are not uniform. An example would be where in a row + from a table, multiple columns represent elements of the same feature + (e.g. a point represented by x,y,z) but, the format is not the same for + all columns. + """ + # convert the iterable argument into an iterator so its contents can + # be consumed by islice in case it is a generator + it = iter(iterable) + + for size in sizes: + if size is None: + yield list(it) + return + else: + yield list(islice(it, size)) + + +def padded(iterable, fillvalue=None, n=None, next_multiple=False): + """Yield the elements from *iterable*, followed by *fillvalue*, such that + at least *n* items are emitted. + + >>> list(padded([1, 2, 3], '?', 5)) + [1, 2, 3, '?', '?'] + + If *next_multiple* is ``True``, *fillvalue* will be emitted until the + number of items emitted is a multiple of *n*:: + + >>> list(padded([1, 2, 3, 4], n=3, next_multiple=True)) + [1, 2, 3, 4, None, None] + + If *n* is ``None``, *fillvalue* will be emitted indefinitely. + + """ + it = iter(iterable) + if n is None: + yield from chain(it, repeat(fillvalue)) + elif n < 1: + raise ValueError('n must be at least 1') + else: + item_count = 0 + for item in it: + yield item + item_count += 1 + + remaining = (n - item_count) % n if next_multiple else n - item_count + for _ in range(remaining): + yield fillvalue + + +def repeat_last(iterable, default=None): + """After the *iterable* is exhausted, keep yielding its last element. + + >>> list(islice(repeat_last(range(3)), 5)) + [0, 1, 2, 2, 2] + + If the iterable is empty, yield *default* forever:: + + >>> list(islice(repeat_last(range(0), 42), 5)) + [42, 42, 42, 42, 42] + + """ + item = _marker + for item in iterable: + yield item + final = default if item is _marker else item + yield from repeat(final) + + +def distribute(n, iterable): + """Distribute the items from *iterable* among *n* smaller iterables. + + >>> group_1, group_2 = distribute(2, [1, 2, 3, 4, 5, 6]) + >>> list(group_1) + [1, 3, 5] + >>> list(group_2) + [2, 4, 6] + + If the length of *iterable* is not evenly divisible by *n*, then the + length of the returned iterables will not be identical: + + >>> children = distribute(3, [1, 2, 3, 4, 5, 6, 7]) + >>> [list(c) for c in children] + [[1, 4, 7], [2, 5], [3, 6]] + + If the length of *iterable* is smaller than *n*, then the last returned + iterables will be empty: + + >>> children = distribute(5, [1, 2, 3]) + >>> [list(c) for c in children] + [[1], [2], [3], [], []] + + This function uses :func:`itertools.tee` and may require significant + storage. If you need the order items in the smaller iterables to match the + original iterable, see :func:`divide`. + + """ + if n < 1: + raise ValueError('n must be at least 1') + + children = tee(iterable, n) + return [islice(it, index, None, n) for index, it in enumerate(children)] + + +def stagger(iterable, offsets=(-1, 0, 1), longest=False, fillvalue=None): + """Yield tuples whose elements are offset from *iterable*. + The amount by which the `i`-th item in each tuple is offset is given by + the `i`-th item in *offsets*. + + >>> list(stagger([0, 1, 2, 3])) + [(None, 0, 1), (0, 1, 2), (1, 2, 3)] + >>> list(stagger(range(8), offsets=(0, 2, 4))) + [(0, 2, 4), (1, 3, 5), (2, 4, 6), (3, 5, 7)] + + By default, the sequence will end when the final element of a tuple is the + last item in the iterable. To continue until the first element of a tuple + is the last item in the iterable, set *longest* to ``True``:: + + >>> list(stagger([0, 1, 2, 3], longest=True)) + [(None, 0, 1), (0, 1, 2), (1, 2, 3), (2, 3, None), (3, None, None)] + + By default, ``None`` will be used to replace offsets beyond the end of the + sequence. Specify *fillvalue* to use some other value. + + """ + children = tee(iterable, len(offsets)) + + return zip_offset( + *children, offsets=offsets, longest=longest, fillvalue=fillvalue + ) + + +class UnequalIterablesError(ValueError): + def __init__(self, details=None): + msg = 'Iterables have different lengths' + if details is not None: + msg += (': index 0 has length {}; index {} has length {}').format( + *details + ) + + super().__init__(msg) + + +def _zip_equal_generator(iterables): + for combo in zip_longest(*iterables, fillvalue=_marker): + for val in combo: + if val is _marker: + raise UnequalIterablesError() + yield combo + + +def zip_equal(*iterables): + """``zip`` the input *iterables* together, but raise + ``UnequalIterablesError`` if they aren't all the same length. + + >>> it_1 = range(3) + >>> it_2 = iter('abc') + >>> list(zip_equal(it_1, it_2)) + [(0, 'a'), (1, 'b'), (2, 'c')] + + >>> it_1 = range(3) + >>> it_2 = iter('abcd') + >>> list(zip_equal(it_1, it_2)) # doctest: +IGNORE_EXCEPTION_DETAIL + Traceback (most recent call last): + ... + more_itertools.more.UnequalIterablesError: Iterables have different + lengths + + """ + if hexversion >= 0x30A00A6: + warnings.warn( + ( + 'zip_equal will be removed in a future version of ' + 'more-itertools. Use the builtin zip function with ' + 'strict=True instead.' + ), + DeprecationWarning, + ) + # Check whether the iterables are all the same size. + try: + first_size = len(iterables[0]) + for i, it in enumerate(iterables[1:], 1): + size = len(it) + if size != first_size: + break + else: + # If we didn't break out, we can use the built-in zip. + return zip(*iterables) + + # If we did break out, there was a mismatch. + raise UnequalIterablesError(details=(first_size, i, size)) + # If any one of the iterables didn't have a length, start reading + # them until one runs out. + except TypeError: + return _zip_equal_generator(iterables) + + +def zip_offset(*iterables, offsets, longest=False, fillvalue=None): + """``zip`` the input *iterables* together, but offset the `i`-th iterable + by the `i`-th item in *offsets*. + + >>> list(zip_offset('0123', 'abcdef', offsets=(0, 1))) + [('0', 'b'), ('1', 'c'), ('2', 'd'), ('3', 'e')] + + This can be used as a lightweight alternative to SciPy or pandas to analyze + data sets in which some series have a lead or lag relationship. + + By default, the sequence will end when the shortest iterable is exhausted. + To continue until the longest iterable is exhausted, set *longest* to + ``True``. + + >>> list(zip_offset('0123', 'abcdef', offsets=(0, 1), longest=True)) + [('0', 'b'), ('1', 'c'), ('2', 'd'), ('3', 'e'), (None, 'f')] + + By default, ``None`` will be used to replace offsets beyond the end of the + sequence. Specify *fillvalue* to use some other value. + + """ + if len(iterables) != len(offsets): + raise ValueError("Number of iterables and offsets didn't match") + + staggered = [] + for it, n in zip(iterables, offsets): + if n < 0: + staggered.append(chain(repeat(fillvalue, -n), it)) + elif n > 0: + staggered.append(islice(it, n, None)) + else: + staggered.append(it) + + if longest: + return zip_longest(*staggered, fillvalue=fillvalue) + + return zip(*staggered) + + +def sort_together(iterables, key_list=(0,), key=None, reverse=False): + """Return the input iterables sorted together, with *key_list* as the + priority for sorting. All iterables are trimmed to the length of the + shortest one. + + This can be used like the sorting function in a spreadsheet. If each + iterable represents a column of data, the key list determines which + columns are used for sorting. + + By default, all iterables are sorted using the ``0``-th iterable:: + + >>> iterables = [(4, 3, 2, 1), ('a', 'b', 'c', 'd')] + >>> sort_together(iterables) + [(1, 2, 3, 4), ('d', 'c', 'b', 'a')] + + Set a different key list to sort according to another iterable. + Specifying multiple keys dictates how ties are broken:: + + >>> iterables = [(3, 1, 2), (0, 1, 0), ('c', 'b', 'a')] + >>> sort_together(iterables, key_list=(1, 2)) + [(2, 3, 1), (0, 0, 1), ('a', 'c', 'b')] + + To sort by a function of the elements of the iterable, pass a *key* + function. Its arguments are the elements of the iterables corresponding to + the key list:: + + >>> names = ('a', 'b', 'c') + >>> lengths = (1, 2, 3) + >>> widths = (5, 2, 1) + >>> def area(length, width): + ... return length * width + >>> sort_together([names, lengths, widths], key_list=(1, 2), key=area) + [('c', 'b', 'a'), (3, 2, 1), (1, 2, 5)] + + Set *reverse* to ``True`` to sort in descending order. + + >>> sort_together([(1, 2, 3), ('c', 'b', 'a')], reverse=True) + [(3, 2, 1), ('a', 'b', 'c')] + + """ + if key is None: + # if there is no key function, the key argument to sorted is an + # itemgetter + key_argument = itemgetter(*key_list) + else: + # if there is a key function, call it with the items at the offsets + # specified by the key function as arguments + key_list = list(key_list) + if len(key_list) == 1: + # if key_list contains a single item, pass the item at that offset + # as the only argument to the key function + key_offset = key_list[0] + key_argument = lambda zipped_items: key(zipped_items[key_offset]) + else: + # if key_list contains multiple items, use itemgetter to return a + # tuple of items, which we pass as *args to the key function + get_key_items = itemgetter(*key_list) + key_argument = lambda zipped_items: key( + *get_key_items(zipped_items) + ) + + return list( + zip(*sorted(zip(*iterables), key=key_argument, reverse=reverse)) + ) + + +def unzip(iterable): + """The inverse of :func:`zip`, this function disaggregates the elements + of the zipped *iterable*. + + The ``i``-th iterable contains the ``i``-th element from each element + of the zipped iterable. The first element is used to to determine the + length of the remaining elements. + + >>> iterable = [('a', 1), ('b', 2), ('c', 3), ('d', 4)] + >>> letters, numbers = unzip(iterable) + >>> list(letters) + ['a', 'b', 'c', 'd'] + >>> list(numbers) + [1, 2, 3, 4] + + This is similar to using ``zip(*iterable)``, but it avoids reading + *iterable* into memory. Note, however, that this function uses + :func:`itertools.tee` and thus may require significant storage. + + """ + head, iterable = spy(iter(iterable)) + if not head: + # empty iterable, e.g. zip([], [], []) + return () + # spy returns a one-length iterable as head + head = head[0] + iterables = tee(iterable, len(head)) + + def itemgetter(i): + def getter(obj): + try: + return obj[i] + except IndexError: + # basically if we have an iterable like + # iter([(1, 2, 3), (4, 5), (6,)]) + # the second unzipped iterable would fail at the third tuple + # since it would try to access tup[1] + # same with the third unzipped iterable and the second tuple + # to support these "improperly zipped" iterables, + # we create a custom itemgetter + # which just stops the unzipped iterables + # at first length mismatch + raise StopIteration + + return getter + + return tuple(map(itemgetter(i), it) for i, it in enumerate(iterables)) + + +def divide(n, iterable): + """Divide the elements from *iterable* into *n* parts, maintaining + order. + + >>> group_1, group_2 = divide(2, [1, 2, 3, 4, 5, 6]) + >>> list(group_1) + [1, 2, 3] + >>> list(group_2) + [4, 5, 6] + + If the length of *iterable* is not evenly divisible by *n*, then the + length of the returned iterables will not be identical: + + >>> children = divide(3, [1, 2, 3, 4, 5, 6, 7]) + >>> [list(c) for c in children] + [[1, 2, 3], [4, 5], [6, 7]] + + If the length of the iterable is smaller than n, then the last returned + iterables will be empty: + + >>> children = divide(5, [1, 2, 3]) + >>> [list(c) for c in children] + [[1], [2], [3], [], []] + + This function will exhaust the iterable before returning and may require + significant storage. If order is not important, see :func:`distribute`, + which does not first pull the iterable into memory. + + """ + if n < 1: + raise ValueError('n must be at least 1') + + try: + iterable[:0] + except TypeError: + seq = tuple(iterable) + else: + seq = iterable + + q, r = divmod(len(seq), n) + + ret = [] + stop = 0 + for i in range(1, n + 1): + start = stop + stop += q + 1 if i <= r else q + ret.append(iter(seq[start:stop])) + + return ret + + +def always_iterable(obj, base_type=(str, bytes)): + """If *obj* is iterable, return an iterator over its items:: + + >>> obj = (1, 2, 3) + >>> list(always_iterable(obj)) + [1, 2, 3] + + If *obj* is not iterable, return a one-item iterable containing *obj*:: + + >>> obj = 1 + >>> list(always_iterable(obj)) + [1] + + If *obj* is ``None``, return an empty iterable: + + >>> obj = None + >>> list(always_iterable(None)) + [] + + By default, binary and text strings are not considered iterable:: + + >>> obj = 'foo' + >>> list(always_iterable(obj)) + ['foo'] + + If *base_type* is set, objects for which ``isinstance(obj, base_type)`` + returns ``True`` won't be considered iterable. + + >>> obj = {'a': 1} + >>> list(always_iterable(obj)) # Iterate over the dict's keys + ['a'] + >>> list(always_iterable(obj, base_type=dict)) # Treat dicts as a unit + [{'a': 1}] + + Set *base_type* to ``None`` to avoid any special handling and treat objects + Python considers iterable as iterable: + + >>> obj = 'foo' + >>> list(always_iterable(obj, base_type=None)) + ['f', 'o', 'o'] + """ + if obj is None: + return iter(()) + + if (base_type is not None) and isinstance(obj, base_type): + return iter((obj,)) + + try: + return iter(obj) + except TypeError: + return iter((obj,)) + + +def adjacent(predicate, iterable, distance=1): + """Return an iterable over `(bool, item)` tuples where the `item` is + drawn from *iterable* and the `bool` indicates whether + that item satisfies the *predicate* or is adjacent to an item that does. + + For example, to find whether items are adjacent to a ``3``:: + + >>> list(adjacent(lambda x: x == 3, range(6))) + [(False, 0), (False, 1), (True, 2), (True, 3), (True, 4), (False, 5)] + + Set *distance* to change what counts as adjacent. For example, to find + whether items are two places away from a ``3``: + + >>> list(adjacent(lambda x: x == 3, range(6), distance=2)) + [(False, 0), (True, 1), (True, 2), (True, 3), (True, 4), (True, 5)] + + This is useful for contextualizing the results of a search function. + For example, a code comparison tool might want to identify lines that + have changed, but also surrounding lines to give the viewer of the diff + context. + + The predicate function will only be called once for each item in the + iterable. + + See also :func:`groupby_transform`, which can be used with this function + to group ranges of items with the same `bool` value. + + """ + # Allow distance=0 mainly for testing that it reproduces results with map() + if distance < 0: + raise ValueError('distance must be at least 0') + + i1, i2 = tee(iterable) + padding = [False] * distance + selected = chain(padding, map(predicate, i1), padding) + adjacent_to_selected = map(any, windowed(selected, 2 * distance + 1)) + return zip(adjacent_to_selected, i2) + + +def groupby_transform(iterable, keyfunc=None, valuefunc=None, reducefunc=None): + """An extension of :func:`itertools.groupby` that can apply transformations + to the grouped data. + + * *keyfunc* is a function computing a key value for each item in *iterable* + * *valuefunc* is a function that transforms the individual items from + *iterable* after grouping + * *reducefunc* is a function that transforms each group of items + + >>> iterable = 'aAAbBBcCC' + >>> keyfunc = lambda k: k.upper() + >>> valuefunc = lambda v: v.lower() + >>> reducefunc = lambda g: ''.join(g) + >>> list(groupby_transform(iterable, keyfunc, valuefunc, reducefunc)) + [('A', 'aaa'), ('B', 'bbb'), ('C', 'ccc')] + + Each optional argument defaults to an identity function if not specified. + + :func:`groupby_transform` is useful when grouping elements of an iterable + using a separate iterable as the key. To do this, :func:`zip` the iterables + and pass a *keyfunc* that extracts the first element and a *valuefunc* + that extracts the second element:: + + >>> from operator import itemgetter + >>> keys = [0, 0, 1, 1, 1, 2, 2, 2, 3] + >>> values = 'abcdefghi' + >>> iterable = zip(keys, values) + >>> grouper = groupby_transform(iterable, itemgetter(0), itemgetter(1)) + >>> [(k, ''.join(g)) for k, g in grouper] + [(0, 'ab'), (1, 'cde'), (2, 'fgh'), (3, 'i')] + + Note that the order of items in the iterable is significant. + Only adjacent items are grouped together, so if you don't want any + duplicate groups, you should sort the iterable by the key function. + + """ + ret = groupby(iterable, keyfunc) + if valuefunc: + ret = ((k, map(valuefunc, g)) for k, g in ret) + if reducefunc: + ret = ((k, reducefunc(g)) for k, g in ret) + + return ret + + +class numeric_range(abc.Sequence, abc.Hashable): + """An extension of the built-in ``range()`` function whose arguments can + be any orderable numeric type. + + With only *stop* specified, *start* defaults to ``0`` and *step* + defaults to ``1``. The output items will match the type of *stop*: + + >>> list(numeric_range(3.5)) + [0.0, 1.0, 2.0, 3.0] + + With only *start* and *stop* specified, *step* defaults to ``1``. The + output items will match the type of *start*: + + >>> from decimal import Decimal + >>> start = Decimal('2.1') + >>> stop = Decimal('5.1') + >>> list(numeric_range(start, stop)) + [Decimal('2.1'), Decimal('3.1'), Decimal('4.1')] + + With *start*, *stop*, and *step* specified the output items will match + the type of ``start + step``: + + >>> from fractions import Fraction + >>> start = Fraction(1, 2) # Start at 1/2 + >>> stop = Fraction(5, 2) # End at 5/2 + >>> step = Fraction(1, 2) # Count by 1/2 + >>> list(numeric_range(start, stop, step)) + [Fraction(1, 2), Fraction(1, 1), Fraction(3, 2), Fraction(2, 1)] + + If *step* is zero, ``ValueError`` is raised. Negative steps are supported: + + >>> list(numeric_range(3, -1, -1.0)) + [3.0, 2.0, 1.0, 0.0] + + Be aware of the limitations of floating point numbers; the representation + of the yielded numbers may be surprising. + + ``datetime.datetime`` objects can be used for *start* and *stop*, if *step* + is a ``datetime.timedelta`` object: + + >>> import datetime + >>> start = datetime.datetime(2019, 1, 1) + >>> stop = datetime.datetime(2019, 1, 3) + >>> step = datetime.timedelta(days=1) + >>> items = iter(numeric_range(start, stop, step)) + >>> next(items) + datetime.datetime(2019, 1, 1, 0, 0) + >>> next(items) + datetime.datetime(2019, 1, 2, 0, 0) + + """ + + _EMPTY_HASH = hash(range(0, 0)) + + def __init__(self, *args): + argc = len(args) + if argc == 1: + (self._stop,) = args + self._start = type(self._stop)(0) + self._step = type(self._stop - self._start)(1) + elif argc == 2: + self._start, self._stop = args + self._step = type(self._stop - self._start)(1) + elif argc == 3: + self._start, self._stop, self._step = args + elif argc == 0: + raise TypeError( + 'numeric_range expected at least ' + '1 argument, got {}'.format(argc) + ) + else: + raise TypeError( + 'numeric_range expected at most ' + '3 arguments, got {}'.format(argc) + ) + + self._zero = type(self._step)(0) + if self._step == self._zero: + raise ValueError('numeric_range() arg 3 must not be zero') + self._growing = self._step > self._zero + self._init_len() + + def __bool__(self): + if self._growing: + return self._start < self._stop + else: + return self._start > self._stop + + def __contains__(self, elem): + if self._growing: + if self._start <= elem < self._stop: + return (elem - self._start) % self._step == self._zero + else: + if self._start >= elem > self._stop: + return (self._start - elem) % (-self._step) == self._zero + + return False + + def __eq__(self, other): + if isinstance(other, numeric_range): + empty_self = not bool(self) + empty_other = not bool(other) + if empty_self or empty_other: + return empty_self and empty_other # True if both empty + else: + return ( + self._start == other._start + and self._step == other._step + and self._get_by_index(-1) == other._get_by_index(-1) + ) + else: + return False + + def __getitem__(self, key): + if isinstance(key, int): + return self._get_by_index(key) + elif isinstance(key, slice): + step = self._step if key.step is None else key.step * self._step + + if key.start is None or key.start <= -self._len: + start = self._start + elif key.start >= self._len: + start = self._stop + else: # -self._len < key.start < self._len + start = self._get_by_index(key.start) + + if key.stop is None or key.stop >= self._len: + stop = self._stop + elif key.stop <= -self._len: + stop = self._start + else: # -self._len < key.stop < self._len + stop = self._get_by_index(key.stop) + + return numeric_range(start, stop, step) + else: + raise TypeError( + 'numeric range indices must be ' + 'integers or slices, not {}'.format(type(key).__name__) + ) + + def __hash__(self): + if self: + return hash((self._start, self._get_by_index(-1), self._step)) + else: + return self._EMPTY_HASH + + def __iter__(self): + values = (self._start + (n * self._step) for n in count()) + if self._growing: + return takewhile(partial(gt, self._stop), values) + else: + return takewhile(partial(lt, self._stop), values) + + def __len__(self): + return self._len + + def _init_len(self): + if self._growing: + start = self._start + stop = self._stop + step = self._step + else: + start = self._stop + stop = self._start + step = -self._step + distance = stop - start + if distance <= self._zero: + self._len = 0 + else: # distance > 0 and step > 0: regular euclidean division + q, r = divmod(distance, step) + self._len = int(q) + int(r != self._zero) + + def __reduce__(self): + return numeric_range, (self._start, self._stop, self._step) + + def __repr__(self): + if self._step == 1: + return "numeric_range({}, {})".format( + repr(self._start), repr(self._stop) + ) + else: + return "numeric_range({}, {}, {})".format( + repr(self._start), repr(self._stop), repr(self._step) + ) + + def __reversed__(self): + return iter( + numeric_range( + self._get_by_index(-1), self._start - self._step, -self._step + ) + ) + + def count(self, value): + return int(value in self) + + def index(self, value): + if self._growing: + if self._start <= value < self._stop: + q, r = divmod(value - self._start, self._step) + if r == self._zero: + return int(q) + else: + if self._start >= value > self._stop: + q, r = divmod(self._start - value, -self._step) + if r == self._zero: + return int(q) + + raise ValueError("{} is not in numeric range".format(value)) + + def _get_by_index(self, i): + if i < 0: + i += self._len + if i < 0 or i >= self._len: + raise IndexError("numeric range object index out of range") + return self._start + i * self._step + + +def count_cycle(iterable, n=None): + """Cycle through the items from *iterable* up to *n* times, yielding + the number of completed cycles along with each item. If *n* is omitted the + process repeats indefinitely. + + >>> list(count_cycle('AB', 3)) + [(0, 'A'), (0, 'B'), (1, 'A'), (1, 'B'), (2, 'A'), (2, 'B')] + + """ + iterable = tuple(iterable) + if not iterable: + return iter(()) + counter = count() if n is None else range(n) + return ((i, item) for i in counter for item in iterable) + + +def mark_ends(iterable): + """Yield 3-tuples of the form ``(is_first, is_last, item)``. + + >>> list(mark_ends('ABC')) + [(True, False, 'A'), (False, False, 'B'), (False, True, 'C')] + + Use this when looping over an iterable to take special action on its first + and/or last items: + + >>> iterable = ['Header', 100, 200, 'Footer'] + >>> total = 0 + >>> for is_first, is_last, item in mark_ends(iterable): + ... if is_first: + ... continue # Skip the header + ... if is_last: + ... continue # Skip the footer + ... total += item + >>> print(total) + 300 + """ + it = iter(iterable) + + try: + b = next(it) + except StopIteration: + return + + try: + for i in count(): + a = b + b = next(it) + yield i == 0, False, a + + except StopIteration: + yield i == 0, True, a + + +def locate(iterable, pred=bool, window_size=None): + """Yield the index of each item in *iterable* for which *pred* returns + ``True``. + + *pred* defaults to :func:`bool`, which will select truthy items: + + >>> list(locate([0, 1, 1, 0, 1, 0, 0])) + [1, 2, 4] + + Set *pred* to a custom function to, e.g., find the indexes for a particular + item. + + >>> list(locate(['a', 'b', 'c', 'b'], lambda x: x == 'b')) + [1, 3] + + If *window_size* is given, then the *pred* function will be called with + that many items. This enables searching for sub-sequences: + + >>> iterable = [0, 1, 2, 3, 0, 1, 2, 3, 0, 1, 2, 3] + >>> pred = lambda *args: args == (1, 2, 3) + >>> list(locate(iterable, pred=pred, window_size=3)) + [1, 5, 9] + + Use with :func:`seekable` to find indexes and then retrieve the associated + items: + + >>> from itertools import count + >>> from more_itertools import seekable + >>> source = (3 * n + 1 if (n % 2) else n // 2 for n in count()) + >>> it = seekable(source) + >>> pred = lambda x: x > 100 + >>> indexes = locate(it, pred=pred) + >>> i = next(indexes) + >>> it.seek(i) + >>> next(it) + 106 + + """ + if window_size is None: + return compress(count(), map(pred, iterable)) + + if window_size < 1: + raise ValueError('window size must be at least 1') + + it = windowed(iterable, window_size, fillvalue=_marker) + return compress(count(), starmap(pred, it)) + + +def lstrip(iterable, pred): + """Yield the items from *iterable*, but strip any from the beginning + for which *pred* returns ``True``. + + For example, to remove a set of items from the start of an iterable: + + >>> iterable = (None, False, None, 1, 2, None, 3, False, None) + >>> pred = lambda x: x in {None, False, ''} + >>> list(lstrip(iterable, pred)) + [1, 2, None, 3, False, None] + + This function is analogous to to :func:`str.lstrip`, and is essentially + an wrapper for :func:`itertools.dropwhile`. + + """ + return dropwhile(pred, iterable) + + +def rstrip(iterable, pred): + """Yield the items from *iterable*, but strip any from the end + for which *pred* returns ``True``. + + For example, to remove a set of items from the end of an iterable: + + >>> iterable = (None, False, None, 1, 2, None, 3, False, None) + >>> pred = lambda x: x in {None, False, ''} + >>> list(rstrip(iterable, pred)) + [None, False, None, 1, 2, None, 3] + + This function is analogous to :func:`str.rstrip`. + + """ + cache = [] + cache_append = cache.append + cache_clear = cache.clear + for x in iterable: + if pred(x): + cache_append(x) + else: + yield from cache + cache_clear() + yield x + + +def strip(iterable, pred): + """Yield the items from *iterable*, but strip any from the + beginning and end for which *pred* returns ``True``. + + For example, to remove a set of items from both ends of an iterable: + + >>> iterable = (None, False, None, 1, 2, None, 3, False, None) + >>> pred = lambda x: x in {None, False, ''} + >>> list(strip(iterable, pred)) + [1, 2, None, 3] + + This function is analogous to :func:`str.strip`. + + """ + return rstrip(lstrip(iterable, pred), pred) + + +class islice_extended: + """An extension of :func:`itertools.islice` that supports negative values + for *stop*, *start*, and *step*. + + >>> iterable = iter('abcdefgh') + >>> list(islice_extended(iterable, -4, -1)) + ['e', 'f', 'g'] + + Slices with negative values require some caching of *iterable*, but this + function takes care to minimize the amount of memory required. + + For example, you can use a negative step with an infinite iterator: + + >>> from itertools import count + >>> list(islice_extended(count(), 110, 99, -2)) + [110, 108, 106, 104, 102, 100] + + You can also use slice notation directly: + + >>> iterable = map(str, count()) + >>> it = islice_extended(iterable)[10:20:2] + >>> list(it) + ['10', '12', '14', '16', '18'] + + """ + + def __init__(self, iterable, *args): + it = iter(iterable) + if args: + self._iterable = _islice_helper(it, slice(*args)) + else: + self._iterable = it + + def __iter__(self): + return self + + def __next__(self): + return next(self._iterable) + + def __getitem__(self, key): + if isinstance(key, slice): + return islice_extended(_islice_helper(self._iterable, key)) + + raise TypeError('islice_extended.__getitem__ argument must be a slice') + + +def _islice_helper(it, s): + start = s.start + stop = s.stop + if s.step == 0: + raise ValueError('step argument must be a non-zero integer or None.') + step = s.step or 1 + + if step > 0: + start = 0 if (start is None) else start + + if start < 0: + # Consume all but the last -start items + cache = deque(enumerate(it, 1), maxlen=-start) + len_iter = cache[-1][0] if cache else 0 + + # Adjust start to be positive + i = max(len_iter + start, 0) + + # Adjust stop to be positive + if stop is None: + j = len_iter + elif stop >= 0: + j = min(stop, len_iter) + else: + j = max(len_iter + stop, 0) + + # Slice the cache + n = j - i + if n <= 0: + return + + for index, item in islice(cache, 0, n, step): + yield item + elif (stop is not None) and (stop < 0): + # Advance to the start position + next(islice(it, start, start), None) + + # When stop is negative, we have to carry -stop items while + # iterating + cache = deque(islice(it, -stop), maxlen=-stop) + + for index, item in enumerate(it): + cached_item = cache.popleft() + if index % step == 0: + yield cached_item + cache.append(item) + else: + # When both start and stop are positive we have the normal case + yield from islice(it, start, stop, step) + else: + start = -1 if (start is None) else start + + if (stop is not None) and (stop < 0): + # Consume all but the last items + n = -stop - 1 + cache = deque(enumerate(it, 1), maxlen=n) + len_iter = cache[-1][0] if cache else 0 + + # If start and stop are both negative they are comparable and + # we can just slice. Otherwise we can adjust start to be negative + # and then slice. + if start < 0: + i, j = start, stop + else: + i, j = min(start - len_iter, -1), None + + for index, item in list(cache)[i:j:step]: + yield item + else: + # Advance to the stop position + if stop is not None: + m = stop + 1 + next(islice(it, m, m), None) + + # stop is positive, so if start is negative they are not comparable + # and we need the rest of the items. + if start < 0: + i = start + n = None + # stop is None and start is positive, so we just need items up to + # the start index. + elif stop is None: + i = None + n = start + 1 + # Both stop and start are positive, so they are comparable. + else: + i = None + n = start - stop + if n <= 0: + return + + cache = list(islice(it, n)) + + yield from cache[i::step] + + +def always_reversible(iterable): + """An extension of :func:`reversed` that supports all iterables, not + just those which implement the ``Reversible`` or ``Sequence`` protocols. + + >>> print(*always_reversible(x for x in range(3))) + 2 1 0 + + If the iterable is already reversible, this function returns the + result of :func:`reversed()`. If the iterable is not reversible, + this function will cache the remaining items in the iterable and + yield them in reverse order, which may require significant storage. + """ + try: + return reversed(iterable) + except TypeError: + return reversed(list(iterable)) + + +def consecutive_groups(iterable, ordering=lambda x: x): + """Yield groups of consecutive items using :func:`itertools.groupby`. + The *ordering* function determines whether two items are adjacent by + returning their position. + + By default, the ordering function is the identity function. This is + suitable for finding runs of numbers: + + >>> iterable = [1, 10, 11, 12, 20, 30, 31, 32, 33, 40] + >>> for group in consecutive_groups(iterable): + ... print(list(group)) + [1] + [10, 11, 12] + [20] + [30, 31, 32, 33] + [40] + + For finding runs of adjacent letters, try using the :meth:`index` method + of a string of letters: + + >>> from string import ascii_lowercase + >>> iterable = 'abcdfgilmnop' + >>> ordering = ascii_lowercase.index + >>> for group in consecutive_groups(iterable, ordering): + ... print(list(group)) + ['a', 'b', 'c', 'd'] + ['f', 'g'] + ['i'] + ['l', 'm', 'n', 'o', 'p'] + + Each group of consecutive items is an iterator that shares it source with + *iterable*. When an an output group is advanced, the previous group is + no longer available unless its elements are copied (e.g., into a ``list``). + + >>> iterable = [1, 2, 11, 12, 21, 22] + >>> saved_groups = [] + >>> for group in consecutive_groups(iterable): + ... saved_groups.append(list(group)) # Copy group elements + >>> saved_groups + [[1, 2], [11, 12], [21, 22]] + + """ + for k, g in groupby( + enumerate(iterable), key=lambda x: x[0] - ordering(x[1]) + ): + yield map(itemgetter(1), g) + + +def difference(iterable, func=sub, *, initial=None): + """This function is the inverse of :func:`itertools.accumulate`. By default + it will compute the first difference of *iterable* using + :func:`operator.sub`: + + >>> from itertools import accumulate + >>> iterable = accumulate([0, 1, 2, 3, 4]) # produces 0, 1, 3, 6, 10 + >>> list(difference(iterable)) + [0, 1, 2, 3, 4] + + *func* defaults to :func:`operator.sub`, but other functions can be + specified. They will be applied as follows:: + + A, B, C, D, ... --> A, func(B, A), func(C, B), func(D, C), ... + + For example, to do progressive division: + + >>> iterable = [1, 2, 6, 24, 120] + >>> func = lambda x, y: x // y + >>> list(difference(iterable, func)) + [1, 2, 3, 4, 5] + + If the *initial* keyword is set, the first element will be skipped when + computing successive differences. + + >>> it = [10, 11, 13, 16] # from accumulate([1, 2, 3], initial=10) + >>> list(difference(it, initial=10)) + [1, 2, 3] + + """ + a, b = tee(iterable) + try: + first = [next(b)] + except StopIteration: + return iter([]) + + if initial is not None: + first = [] + + return chain(first, starmap(func, zip(b, a))) + + +class SequenceView(Sequence): + """Return a read-only view of the sequence object *target*. + + :class:`SequenceView` objects are analogous to Python's built-in + "dictionary view" types. They provide a dynamic view of a sequence's items, + meaning that when the sequence updates, so does the view. + + >>> seq = ['0', '1', '2'] + >>> view = SequenceView(seq) + >>> view + SequenceView(['0', '1', '2']) + >>> seq.append('3') + >>> view + SequenceView(['0', '1', '2', '3']) + + Sequence views support indexing, slicing, and length queries. They act + like the underlying sequence, except they don't allow assignment: + + >>> view[1] + '1' + >>> view[1:-1] + ['1', '2'] + >>> len(view) + 4 + + Sequence views are useful as an alternative to copying, as they don't + require (much) extra storage. + + """ + + def __init__(self, target): + if not isinstance(target, Sequence): + raise TypeError + self._target = target + + def __getitem__(self, index): + return self._target[index] + + def __len__(self): + return len(self._target) + + def __repr__(self): + return '{}({})'.format(self.__class__.__name__, repr(self._target)) + + +class seekable: + """Wrap an iterator to allow for seeking backward and forward. This + progressively caches the items in the source iterable so they can be + re-visited. + + Call :meth:`seek` with an index to seek to that position in the source + iterable. + + To "reset" an iterator, seek to ``0``: + + >>> from itertools import count + >>> it = seekable((str(n) for n in count())) + >>> next(it), next(it), next(it) + ('0', '1', '2') + >>> it.seek(0) + >>> next(it), next(it), next(it) + ('0', '1', '2') + >>> next(it) + '3' + + You can also seek forward: + + >>> it = seekable((str(n) for n in range(20))) + >>> it.seek(10) + >>> next(it) + '10' + >>> it.seek(20) # Seeking past the end of the source isn't a problem + >>> list(it) + [] + >>> it.seek(0) # Resetting works even after hitting the end + >>> next(it), next(it), next(it) + ('0', '1', '2') + + Call :meth:`peek` to look ahead one item without advancing the iterator: + + >>> it = seekable('1234') + >>> it.peek() + '1' + >>> list(it) + ['1', '2', '3', '4'] + >>> it.peek(default='empty') + 'empty' + + Before the iterator is at its end, calling :func:`bool` on it will return + ``True``. After it will return ``False``: + + >>> it = seekable('5678') + >>> bool(it) + True + >>> list(it) + ['5', '6', '7', '8'] + >>> bool(it) + False + + You may view the contents of the cache with the :meth:`elements` method. + That returns a :class:`SequenceView`, a view that updates automatically: + + >>> it = seekable((str(n) for n in range(10))) + >>> next(it), next(it), next(it) + ('0', '1', '2') + >>> elements = it.elements() + >>> elements + SequenceView(['0', '1', '2']) + >>> next(it) + '3' + >>> elements + SequenceView(['0', '1', '2', '3']) + + By default, the cache grows as the source iterable progresses, so beware of + wrapping very large or infinite iterables. Supply *maxlen* to limit the + size of the cache (this of course limits how far back you can seek). + + >>> from itertools import count + >>> it = seekable((str(n) for n in count()), maxlen=2) + >>> next(it), next(it), next(it), next(it) + ('0', '1', '2', '3') + >>> list(it.elements()) + ['2', '3'] + >>> it.seek(0) + >>> next(it), next(it), next(it), next(it) + ('2', '3', '4', '5') + >>> next(it) + '6' + + """ + + def __init__(self, iterable, maxlen=None): + self._source = iter(iterable) + if maxlen is None: + self._cache = [] + else: + self._cache = deque([], maxlen) + self._index = None + + def __iter__(self): + return self + + def __next__(self): + if self._index is not None: + try: + item = self._cache[self._index] + except IndexError: + self._index = None + else: + self._index += 1 + return item + + item = next(self._source) + self._cache.append(item) + return item + + def __bool__(self): + try: + self.peek() + except StopIteration: + return False + return True + + def peek(self, default=_marker): + try: + peeked = next(self) + except StopIteration: + if default is _marker: + raise + return default + if self._index is None: + self._index = len(self._cache) + self._index -= 1 + return peeked + + def elements(self): + return SequenceView(self._cache) + + def seek(self, index): + self._index = index + remainder = index - len(self._cache) + if remainder > 0: + consume(self, remainder) + + +class run_length: + """ + :func:`run_length.encode` compresses an iterable with run-length encoding. + It yields groups of repeated items with the count of how many times they + were repeated: + + >>> uncompressed = 'abbcccdddd' + >>> list(run_length.encode(uncompressed)) + [('a', 1), ('b', 2), ('c', 3), ('d', 4)] + + :func:`run_length.decode` decompresses an iterable that was previously + compressed with run-length encoding. It yields the items of the + decompressed iterable: + + >>> compressed = [('a', 1), ('b', 2), ('c', 3), ('d', 4)] + >>> list(run_length.decode(compressed)) + ['a', 'b', 'b', 'c', 'c', 'c', 'd', 'd', 'd', 'd'] + + """ + + @staticmethod + def encode(iterable): + return ((k, ilen(g)) for k, g in groupby(iterable)) + + @staticmethod + def decode(iterable): + return chain.from_iterable(repeat(k, n) for k, n in iterable) + + +def exactly_n(iterable, n, predicate=bool): + """Return ``True`` if exactly ``n`` items in the iterable are ``True`` + according to the *predicate* function. + + >>> exactly_n([True, True, False], 2) + True + >>> exactly_n([True, True, False], 1) + False + >>> exactly_n([0, 1, 2, 3, 4, 5], 3, lambda x: x < 3) + True + + The iterable will be advanced until ``n + 1`` truthy items are encountered, + so avoid calling it on infinite iterables. + + """ + return len(take(n + 1, filter(predicate, iterable))) == n + + +def circular_shifts(iterable): + """Return a list of circular shifts of *iterable*. + + >>> circular_shifts(range(4)) + [(0, 1, 2, 3), (1, 2, 3, 0), (2, 3, 0, 1), (3, 0, 1, 2)] + """ + lst = list(iterable) + return take(len(lst), windowed(cycle(lst), len(lst))) + + +def make_decorator(wrapping_func, result_index=0): + """Return a decorator version of *wrapping_func*, which is a function that + modifies an iterable. *result_index* is the position in that function's + signature where the iterable goes. + + This lets you use itertools on the "production end," i.e. at function + definition. This can augment what the function returns without changing the + function's code. + + For example, to produce a decorator version of :func:`chunked`: + + >>> from more_itertools import chunked + >>> chunker = make_decorator(chunked, result_index=0) + >>> @chunker(3) + ... def iter_range(n): + ... return iter(range(n)) + ... + >>> list(iter_range(9)) + [[0, 1, 2], [3, 4, 5], [6, 7, 8]] + + To only allow truthy items to be returned: + + >>> truth_serum = make_decorator(filter, result_index=1) + >>> @truth_serum(bool) + ... def boolean_test(): + ... return [0, 1, '', ' ', False, True] + ... + >>> list(boolean_test()) + [1, ' ', True] + + The :func:`peekable` and :func:`seekable` wrappers make for practical + decorators: + + >>> from more_itertools import peekable + >>> peekable_function = make_decorator(peekable) + >>> @peekable_function() + ... def str_range(*args): + ... return (str(x) for x in range(*args)) + ... + >>> it = str_range(1, 20, 2) + >>> next(it), next(it), next(it) + ('1', '3', '5') + >>> it.peek() + '7' + >>> next(it) + '7' + + """ + # See https://sites.google.com/site/bbayles/index/decorator_factory for + # notes on how this works. + def decorator(*wrapping_args, **wrapping_kwargs): + def outer_wrapper(f): + def inner_wrapper(*args, **kwargs): + result = f(*args, **kwargs) + wrapping_args_ = list(wrapping_args) + wrapping_args_.insert(result_index, result) + return wrapping_func(*wrapping_args_, **wrapping_kwargs) + + return inner_wrapper + + return outer_wrapper + + return decorator + + +def map_reduce(iterable, keyfunc, valuefunc=None, reducefunc=None): + """Return a dictionary that maps the items in *iterable* to categories + defined by *keyfunc*, transforms them with *valuefunc*, and + then summarizes them by category with *reducefunc*. + + *valuefunc* defaults to the identity function if it is unspecified. + If *reducefunc* is unspecified, no summarization takes place: + + >>> keyfunc = lambda x: x.upper() + >>> result = map_reduce('abbccc', keyfunc) + >>> sorted(result.items()) + [('A', ['a']), ('B', ['b', 'b']), ('C', ['c', 'c', 'c'])] + + Specifying *valuefunc* transforms the categorized items: + + >>> keyfunc = lambda x: x.upper() + >>> valuefunc = lambda x: 1 + >>> result = map_reduce('abbccc', keyfunc, valuefunc) + >>> sorted(result.items()) + [('A', [1]), ('B', [1, 1]), ('C', [1, 1, 1])] + + Specifying *reducefunc* summarizes the categorized items: + + >>> keyfunc = lambda x: x.upper() + >>> valuefunc = lambda x: 1 + >>> reducefunc = sum + >>> result = map_reduce('abbccc', keyfunc, valuefunc, reducefunc) + >>> sorted(result.items()) + [('A', 1), ('B', 2), ('C', 3)] + + You may want to filter the input iterable before applying the map/reduce + procedure: + + >>> all_items = range(30) + >>> items = [x for x in all_items if 10 <= x <= 20] # Filter + >>> keyfunc = lambda x: x % 2 # Evens map to 0; odds to 1 + >>> categories = map_reduce(items, keyfunc=keyfunc) + >>> sorted(categories.items()) + [(0, [10, 12, 14, 16, 18, 20]), (1, [11, 13, 15, 17, 19])] + >>> summaries = map_reduce(items, keyfunc=keyfunc, reducefunc=sum) + >>> sorted(summaries.items()) + [(0, 90), (1, 75)] + + Note that all items in the iterable are gathered into a list before the + summarization step, which may require significant storage. + + The returned object is a :obj:`collections.defaultdict` with the + ``default_factory`` set to ``None``, such that it behaves like a normal + dictionary. + + """ + valuefunc = (lambda x: x) if (valuefunc is None) else valuefunc + + ret = defaultdict(list) + for item in iterable: + key = keyfunc(item) + value = valuefunc(item) + ret[key].append(value) + + if reducefunc is not None: + for key, value_list in ret.items(): + ret[key] = reducefunc(value_list) + + ret.default_factory = None + return ret + + +def rlocate(iterable, pred=bool, window_size=None): + """Yield the index of each item in *iterable* for which *pred* returns + ``True``, starting from the right and moving left. + + *pred* defaults to :func:`bool`, which will select truthy items: + + >>> list(rlocate([0, 1, 1, 0, 1, 0, 0])) # Truthy at 1, 2, and 4 + [4, 2, 1] + + Set *pred* to a custom function to, e.g., find the indexes for a particular + item: + + >>> iterable = iter('abcb') + >>> pred = lambda x: x == 'b' + >>> list(rlocate(iterable, pred)) + [3, 1] + + If *window_size* is given, then the *pred* function will be called with + that many items. This enables searching for sub-sequences: + + >>> iterable = [0, 1, 2, 3, 0, 1, 2, 3, 0, 1, 2, 3] + >>> pred = lambda *args: args == (1, 2, 3) + >>> list(rlocate(iterable, pred=pred, window_size=3)) + [9, 5, 1] + + Beware, this function won't return anything for infinite iterables. + If *iterable* is reversible, ``rlocate`` will reverse it and search from + the right. Otherwise, it will search from the left and return the results + in reverse order. + + See :func:`locate` to for other example applications. + + """ + if window_size is None: + try: + len_iter = len(iterable) + return (len_iter - i - 1 for i in locate(reversed(iterable), pred)) + except TypeError: + pass + + return reversed(list(locate(iterable, pred, window_size))) + + +def replace(iterable, pred, substitutes, count=None, window_size=1): + """Yield the items from *iterable*, replacing the items for which *pred* + returns ``True`` with the items from the iterable *substitutes*. + + >>> iterable = [1, 1, 0, 1, 1, 0, 1, 1] + >>> pred = lambda x: x == 0 + >>> substitutes = (2, 3) + >>> list(replace(iterable, pred, substitutes)) + [1, 1, 2, 3, 1, 1, 2, 3, 1, 1] + + If *count* is given, the number of replacements will be limited: + + >>> iterable = [1, 1, 0, 1, 1, 0, 1, 1, 0] + >>> pred = lambda x: x == 0 + >>> substitutes = [None] + >>> list(replace(iterable, pred, substitutes, count=2)) + [1, 1, None, 1, 1, None, 1, 1, 0] + + Use *window_size* to control the number of items passed as arguments to + *pred*. This allows for locating and replacing subsequences. + + >>> iterable = [0, 1, 2, 5, 0, 1, 2, 5] + >>> window_size = 3 + >>> pred = lambda *args: args == (0, 1, 2) # 3 items passed to pred + >>> substitutes = [3, 4] # Splice in these items + >>> list(replace(iterable, pred, substitutes, window_size=window_size)) + [3, 4, 5, 3, 4, 5] + + """ + if window_size < 1: + raise ValueError('window_size must be at least 1') + + # Save the substitutes iterable, since it's used more than once + substitutes = tuple(substitutes) + + # Add padding such that the number of windows matches the length of the + # iterable + it = chain(iterable, [_marker] * (window_size - 1)) + windows = windowed(it, window_size) + + n = 0 + for w in windows: + # If the current window matches our predicate (and we haven't hit + # our maximum number of replacements), splice in the substitutes + # and then consume the following windows that overlap with this one. + # For example, if the iterable is (0, 1, 2, 3, 4...) + # and the window size is 2, we have (0, 1), (1, 2), (2, 3)... + # If the predicate matches on (0, 1), we need to zap (0, 1) and (1, 2) + if pred(*w): + if (count is None) or (n < count): + n += 1 + yield from substitutes + consume(windows, window_size - 1) + continue + + # If there was no match (or we've reached the replacement limit), + # yield the first item from the window. + if w and (w[0] is not _marker): + yield w[0] + + +def partitions(iterable): + """Yield all possible order-preserving partitions of *iterable*. + + >>> iterable = 'abc' + >>> for part in partitions(iterable): + ... print([''.join(p) for p in part]) + ['abc'] + ['a', 'bc'] + ['ab', 'c'] + ['a', 'b', 'c'] + + This is unrelated to :func:`partition`. + + """ + sequence = list(iterable) + n = len(sequence) + for i in powerset(range(1, n)): + yield [sequence[i:j] for i, j in zip((0,) + i, i + (n,))] + + +def set_partitions(iterable, k=None): + """ + Yield the set partitions of *iterable* into *k* parts. Set partitions are + not order-preserving. + + >>> iterable = 'abc' + >>> for part in set_partitions(iterable, 2): + ... print([''.join(p) for p in part]) + ['a', 'bc'] + ['ab', 'c'] + ['b', 'ac'] + + + If *k* is not given, every set partition is generated. + + >>> iterable = 'abc' + >>> for part in set_partitions(iterable): + ... print([''.join(p) for p in part]) + ['abc'] + ['a', 'bc'] + ['ab', 'c'] + ['b', 'ac'] + ['a', 'b', 'c'] + + """ + L = list(iterable) + n = len(L) + if k is not None: + if k < 1: + raise ValueError( + "Can't partition in a negative or zero number of groups" + ) + elif k > n: + return + + def set_partitions_helper(L, k): + n = len(L) + if k == 1: + yield [L] + elif n == k: + yield [[s] for s in L] + else: + e, *M = L + for p in set_partitions_helper(M, k - 1): + yield [[e], *p] + for p in set_partitions_helper(M, k): + for i in range(len(p)): + yield p[:i] + [[e] + p[i]] + p[i + 1 :] + + if k is None: + for k in range(1, n + 1): + yield from set_partitions_helper(L, k) + else: + yield from set_partitions_helper(L, k) + + +class time_limited: + """ + Yield items from *iterable* until *limit_seconds* have passed. + If the time limit expires before all items have been yielded, the + ``timed_out`` parameter will be set to ``True``. + + >>> from time import sleep + >>> def generator(): + ... yield 1 + ... yield 2 + ... sleep(0.2) + ... yield 3 + >>> iterable = time_limited(0.1, generator()) + >>> list(iterable) + [1, 2] + >>> iterable.timed_out + True + + Note that the time is checked before each item is yielded, and iteration + stops if the time elapsed is greater than *limit_seconds*. If your time + limit is 1 second, but it takes 2 seconds to generate the first item from + the iterable, the function will run for 2 seconds and not yield anything. + + """ + + def __init__(self, limit_seconds, iterable): + if limit_seconds < 0: + raise ValueError('limit_seconds must be positive') + self.limit_seconds = limit_seconds + self._iterable = iter(iterable) + self._start_time = monotonic() + self.timed_out = False + + def __iter__(self): + return self + + def __next__(self): + item = next(self._iterable) + if monotonic() - self._start_time > self.limit_seconds: + self.timed_out = True + raise StopIteration + + return item + + +def only(iterable, default=None, too_long=None): + """If *iterable* has only one item, return it. + If it has zero items, return *default*. + If it has more than one item, raise the exception given by *too_long*, + which is ``ValueError`` by default. + + >>> only([], default='missing') + 'missing' + >>> only([1]) + 1 + >>> only([1, 2]) # doctest: +IGNORE_EXCEPTION_DETAIL + Traceback (most recent call last): + ... + ValueError: Expected exactly one item in iterable, but got 1, 2, + and perhaps more.' + >>> only([1, 2], too_long=TypeError) # doctest: +IGNORE_EXCEPTION_DETAIL + Traceback (most recent call last): + ... + TypeError + + Note that :func:`only` attempts to advance *iterable* twice to ensure there + is only one item. See :func:`spy` or :func:`peekable` to check + iterable contents less destructively. + """ + it = iter(iterable) + first_value = next(it, default) + + try: + second_value = next(it) + except StopIteration: + pass + else: + msg = ( + 'Expected exactly one item in iterable, but got {!r}, {!r}, ' + 'and perhaps more.'.format(first_value, second_value) + ) + raise too_long or ValueError(msg) + + return first_value + + +def ichunked(iterable, n): + """Break *iterable* into sub-iterables with *n* elements each. + :func:`ichunked` is like :func:`chunked`, but it yields iterables + instead of lists. + + If the sub-iterables are read in order, the elements of *iterable* + won't be stored in memory. + If they are read out of order, :func:`itertools.tee` is used to cache + elements as necessary. + + >>> from itertools import count + >>> all_chunks = ichunked(count(), 4) + >>> c_1, c_2, c_3 = next(all_chunks), next(all_chunks), next(all_chunks) + >>> list(c_2) # c_1's elements have been cached; c_3's haven't been + [4, 5, 6, 7] + >>> list(c_1) + [0, 1, 2, 3] + >>> list(c_3) + [8, 9, 10, 11] + + """ + source = iter(iterable) + + while True: + # Check to see whether we're at the end of the source iterable + item = next(source, _marker) + if item is _marker: + return + + # Clone the source and yield an n-length slice + source, it = tee(chain([item], source)) + yield islice(it, n) + + # Advance the source iterable + consume(source, n) + + +def distinct_combinations(iterable, r): + """Yield the distinct combinations of *r* items taken from *iterable*. + + >>> list(distinct_combinations([0, 0, 1], 2)) + [(0, 0), (0, 1)] + + Equivalent to ``set(combinations(iterable))``, except duplicates are not + generated and thrown away. For larger input sequences this is much more + efficient. + + """ + if r < 0: + raise ValueError('r must be non-negative') + elif r == 0: + yield () + return + pool = tuple(iterable) + generators = [unique_everseen(enumerate(pool), key=itemgetter(1))] + current_combo = [None] * r + level = 0 + while generators: + try: + cur_idx, p = next(generators[-1]) + except StopIteration: + generators.pop() + level -= 1 + continue + current_combo[level] = p + if level + 1 == r: + yield tuple(current_combo) + else: + generators.append( + unique_everseen( + enumerate(pool[cur_idx + 1 :], cur_idx + 1), + key=itemgetter(1), + ) + ) + level += 1 + + +def filter_except(validator, iterable, *exceptions): + """Yield the items from *iterable* for which the *validator* function does + not raise one of the specified *exceptions*. + + *validator* is called for each item in *iterable*. + It should be a function that accepts one argument and raises an exception + if that item is not valid. + + >>> iterable = ['1', '2', 'three', '4', None] + >>> list(filter_except(int, iterable, ValueError, TypeError)) + ['1', '2', '4'] + + If an exception other than one given by *exceptions* is raised by + *validator*, it is raised like normal. + """ + for item in iterable: + try: + validator(item) + except exceptions: + pass + else: + yield item + + +def map_except(function, iterable, *exceptions): + """Transform each item from *iterable* with *function* and yield the + result, unless *function* raises one of the specified *exceptions*. + + *function* is called to transform each item in *iterable*. + It should be a accept one argument. + + >>> iterable = ['1', '2', 'three', '4', None] + >>> list(map_except(int, iterable, ValueError, TypeError)) + [1, 2, 4] + + If an exception other than one given by *exceptions* is raised by + *function*, it is raised like normal. + """ + for item in iterable: + try: + yield function(item) + except exceptions: + pass + + +def _sample_unweighted(iterable, k): + # Implementation of "Algorithm L" from the 1994 paper by Kim-Hung Li: + # "Reservoir-Sampling Algorithms of Time Complexity O(n(1+log(N/n)))". + + # Fill up the reservoir (collection of samples) with the first `k` samples + reservoir = take(k, iterable) + + # Generate random number that's the largest in a sample of k U(0,1) numbers + # Largest order statistic: https://en.wikipedia.org/wiki/Order_statistic + W = exp(log(random()) / k) + + # The number of elements to skip before changing the reservoir is a random + # number with a geometric distribution. Sample it using random() and logs. + next_index = k + floor(log(random()) / log(1 - W)) + + for index, element in enumerate(iterable, k): + + if index == next_index: + reservoir[randrange(k)] = element + # The new W is the largest in a sample of k U(0, `old_W`) numbers + W *= exp(log(random()) / k) + next_index += floor(log(random()) / log(1 - W)) + 1 + + return reservoir + + +def _sample_weighted(iterable, k, weights): + # Implementation of "A-ExpJ" from the 2006 paper by Efraimidis et al. : + # "Weighted random sampling with a reservoir". + + # Log-transform for numerical stability for weights that are small/large + weight_keys = (log(random()) / weight for weight in weights) + + # Fill up the reservoir (collection of samples) with the first `k` + # weight-keys and elements, then heapify the list. + reservoir = take(k, zip(weight_keys, iterable)) + heapify(reservoir) + + # The number of jumps before changing the reservoir is a random variable + # with an exponential distribution. Sample it using random() and logs. + smallest_weight_key, _ = reservoir[0] + weights_to_skip = log(random()) / smallest_weight_key + + for weight, element in zip(weights, iterable): + if weight >= weights_to_skip: + # The notation here is consistent with the paper, but we store + # the weight-keys in log-space for better numerical stability. + smallest_weight_key, _ = reservoir[0] + t_w = exp(weight * smallest_weight_key) + r_2 = uniform(t_w, 1) # generate U(t_w, 1) + weight_key = log(r_2) / weight + heapreplace(reservoir, (weight_key, element)) + smallest_weight_key, _ = reservoir[0] + weights_to_skip = log(random()) / smallest_weight_key + else: + weights_to_skip -= weight + + # Equivalent to [element for weight_key, element in sorted(reservoir)] + return [heappop(reservoir)[1] for _ in range(k)] + + +def sample(iterable, k, weights=None): + """Return a *k*-length list of elements chosen (without replacement) + from the *iterable*. Like :func:`random.sample`, but works on iterables + of unknown length. + + >>> iterable = range(100) + >>> sample(iterable, 5) # doctest: +SKIP + [81, 60, 96, 16, 4] + + An iterable with *weights* may also be given: + + >>> iterable = range(100) + >>> weights = (i * i + 1 for i in range(100)) + >>> sampled = sample(iterable, 5, weights=weights) # doctest: +SKIP + [79, 67, 74, 66, 78] + + The algorithm can also be used to generate weighted random permutations. + The relative weight of each item determines the probability that it + appears late in the permutation. + + >>> data = "abcdefgh" + >>> weights = range(1, len(data) + 1) + >>> sample(data, k=len(data), weights=weights) # doctest: +SKIP + ['c', 'a', 'b', 'e', 'g', 'd', 'h', 'f'] + """ + if k == 0: + return [] + + iterable = iter(iterable) + if weights is None: + return _sample_unweighted(iterable, k) + else: + weights = iter(weights) + return _sample_weighted(iterable, k, weights) + + +def is_sorted(iterable, key=None, reverse=False): + """Returns ``True`` if the items of iterable are in sorted order, and + ``False`` otherwise. *key* and *reverse* have the same meaning that they do + in the built-in :func:`sorted` function. + + >>> is_sorted(['1', '2', '3', '4', '5'], key=int) + True + >>> is_sorted([5, 4, 3, 1, 2], reverse=True) + False + + The function returns ``False`` after encountering the first out-of-order + item. If there are no out-of-order items, the iterable is exhausted. + """ + + compare = lt if reverse else gt + it = iterable if (key is None) else map(key, iterable) + return not any(starmap(compare, pairwise(it))) + + +class AbortThread(BaseException): + pass + + +class callback_iter: + """Convert a function that uses callbacks to an iterator. + + Let *func* be a function that takes a `callback` keyword argument. + For example: + + >>> def func(callback=None): + ... for i, c in [(1, 'a'), (2, 'b'), (3, 'c')]: + ... if callback: + ... callback(i, c) + ... return 4 + + + Use ``with callback_iter(func)`` to get an iterator over the parameters + that are delivered to the callback. + + >>> with callback_iter(func) as it: + ... for args, kwargs in it: + ... print(args) + (1, 'a') + (2, 'b') + (3, 'c') + + The function will be called in a background thread. The ``done`` property + indicates whether it has completed execution. + + >>> it.done + True + + If it completes successfully, its return value will be available + in the ``result`` property. + + >>> it.result + 4 + + Notes: + + * If the function uses some keyword argument besides ``callback``, supply + *callback_kwd*. + * If it finished executing, but raised an exception, accessing the + ``result`` property will raise the same exception. + * If it hasn't finished executing, accessing the ``result`` + property from within the ``with`` block will raise ``RuntimeError``. + * If it hasn't finished executing, accessing the ``result`` property from + outside the ``with`` block will raise a + ``more_itertools.AbortThread`` exception. + * Provide *wait_seconds* to adjust how frequently the it is polled for + output. + + """ + + def __init__(self, func, callback_kwd='callback', wait_seconds=0.1): + self._func = func + self._callback_kwd = callback_kwd + self._aborted = False + self._future = None + self._wait_seconds = wait_seconds + self._executor = __import__("concurrent.futures").futures.ThreadPoolExecutor(max_workers=1) + self._iterator = self._reader() + + def __enter__(self): + return self + + def __exit__(self, exc_type, exc_value, traceback): + self._aborted = True + self._executor.shutdown() + + def __iter__(self): + return self + + def __next__(self): + return next(self._iterator) + + @property + def done(self): + if self._future is None: + return False + return self._future.done() + + @property + def result(self): + if not self.done: + raise RuntimeError('Function has not yet completed') + + return self._future.result() + + def _reader(self): + q = Queue() + + def callback(*args, **kwargs): + if self._aborted: + raise AbortThread('canceled by user') + + q.put((args, kwargs)) + + self._future = self._executor.submit( + self._func, **{self._callback_kwd: callback} + ) + + while True: + try: + item = q.get(timeout=self._wait_seconds) + except Empty: + pass + else: + q.task_done() + yield item + + if self._future.done(): + break + + remaining = [] + while True: + try: + item = q.get_nowait() + except Empty: + break + else: + q.task_done() + remaining.append(item) + q.join() + yield from remaining + + +def windowed_complete(iterable, n): + """ + Yield ``(beginning, middle, end)`` tuples, where: + + * Each ``middle`` has *n* items from *iterable* + * Each ``beginning`` has the items before the ones in ``middle`` + * Each ``end`` has the items after the ones in ``middle`` + + >>> iterable = range(7) + >>> n = 3 + >>> for beginning, middle, end in windowed_complete(iterable, n): + ... print(beginning, middle, end) + () (0, 1, 2) (3, 4, 5, 6) + (0,) (1, 2, 3) (4, 5, 6) + (0, 1) (2, 3, 4) (5, 6) + (0, 1, 2) (3, 4, 5) (6,) + (0, 1, 2, 3) (4, 5, 6) () + + Note that *n* must be at least 0 and most equal to the length of + *iterable*. + + This function will exhaust the iterable and may require significant + storage. + """ + if n < 0: + raise ValueError('n must be >= 0') + + seq = tuple(iterable) + size = len(seq) + + if n > size: + raise ValueError('n must be <= len(seq)') + + for i in range(size - n + 1): + beginning = seq[:i] + middle = seq[i : i + n] + end = seq[i + n :] + yield beginning, middle, end + + +def all_unique(iterable, key=None): + """ + Returns ``True`` if all the elements of *iterable* are unique (no two + elements are equal). + + >>> all_unique('ABCB') + False + + If a *key* function is specified, it will be used to make comparisons. + + >>> all_unique('ABCb') + True + >>> all_unique('ABCb', str.lower) + False + + The function returns as soon as the first non-unique element is + encountered. Iterables with a mix of hashable and unhashable items can + be used, but the function will be slower for unhashable items. + """ + seenset = set() + seenset_add = seenset.add + seenlist = [] + seenlist_add = seenlist.append + for element in map(key, iterable) if key else iterable: + try: + if element in seenset: + return False + seenset_add(element) + except TypeError: + if element in seenlist: + return False + seenlist_add(element) + return True + + +def nth_product(index, *args): + """Equivalent to ``list(product(*args))[index]``. + + The products of *args* can be ordered lexicographically. + :func:`nth_product` computes the product at sort position *index* without + computing the previous products. + + >>> nth_product(8, range(2), range(2), range(2), range(2)) + (1, 0, 0, 0) + + ``IndexError`` will be raised if the given *index* is invalid. + """ + pools = list(map(tuple, reversed(args))) + ns = list(map(len, pools)) + + c = reduce(mul, ns) + + if index < 0: + index += c + + if not 0 <= index < c: + raise IndexError + + result = [] + for pool, n in zip(pools, ns): + result.append(pool[index % n]) + index //= n + + return tuple(reversed(result)) + + +def nth_permutation(iterable, r, index): + """Equivalent to ``list(permutations(iterable, r))[index]``` + + The subsequences of *iterable* that are of length *r* where order is + important can be ordered lexicographically. :func:`nth_permutation` + computes the subsequence at sort position *index* directly, without + computing the previous subsequences. + + >>> nth_permutation('ghijk', 2, 5) + ('h', 'i') + + ``ValueError`` will be raised If *r* is negative or greater than the length + of *iterable*. + ``IndexError`` will be raised if the given *index* is invalid. + """ + pool = list(iterable) + n = len(pool) + + if r is None or r == n: + r, c = n, factorial(n) + elif not 0 <= r < n: + raise ValueError + else: + c = factorial(n) // factorial(n - r) + + if index < 0: + index += c + + if not 0 <= index < c: + raise IndexError + + if c == 0: + return tuple() + + result = [0] * r + q = index * factorial(n) // c if r < n else index + for d in range(1, n + 1): + q, i = divmod(q, d) + if 0 <= n - d < r: + result[n - d] = i + if q == 0: + break + + return tuple(map(pool.pop, result)) + + +def value_chain(*args): + """Yield all arguments passed to the function in the same order in which + they were passed. If an argument itself is iterable then iterate over its + values. + + >>> list(value_chain(1, 2, 3, [4, 5, 6])) + [1, 2, 3, 4, 5, 6] + + Binary and text strings are not considered iterable and are emitted + as-is: + + >>> list(value_chain('12', '34', ['56', '78'])) + ['12', '34', '56', '78'] + + + Multiple levels of nesting are not flattened. + + """ + for value in args: + if isinstance(value, (str, bytes)): + yield value + continue + try: + yield from value + except TypeError: + yield value + + +def product_index(element, *args): + """Equivalent to ``list(product(*args)).index(element)`` + + The products of *args* can be ordered lexicographically. + :func:`product_index` computes the first index of *element* without + computing the previous products. + + >>> product_index([8, 2], range(10), range(5)) + 42 + + ``ValueError`` will be raised if the given *element* isn't in the product + of *args*. + """ + index = 0 + + for x, pool in zip_longest(element, args, fillvalue=_marker): + if x is _marker or pool is _marker: + raise ValueError('element is not a product of args') + + pool = tuple(pool) + index = index * len(pool) + pool.index(x) + + return index + + +def combination_index(element, iterable): + """Equivalent to ``list(combinations(iterable, r)).index(element)`` + + The subsequences of *iterable* that are of length *r* can be ordered + lexicographically. :func:`combination_index` computes the index of the + first *element*, without computing the previous combinations. + + >>> combination_index('adf', 'abcdefg') + 10 + + ``ValueError`` will be raised if the given *element* isn't one of the + combinations of *iterable*. + """ + element = enumerate(element) + k, y = next(element, (None, None)) + if k is None: + return 0 + + indexes = [] + pool = enumerate(iterable) + for n, x in pool: + if x == y: + indexes.append(n) + tmp, y = next(element, (None, None)) + if tmp is None: + break + else: + k = tmp + else: + raise ValueError('element is not a combination of iterable') + + n, _ = last(pool, default=(n, None)) + + # Python versiosn below 3.8 don't have math.comb + index = 1 + for i, j in enumerate(reversed(indexes), start=1): + j = n - j + if i <= j: + index += factorial(j) // (factorial(i) * factorial(j - i)) + + return factorial(n + 1) // (factorial(k + 1) * factorial(n - k)) - index + + +def permutation_index(element, iterable): + """Equivalent to ``list(permutations(iterable, r)).index(element)``` + + The subsequences of *iterable* that are of length *r* where order is + important can be ordered lexicographically. :func:`permutation_index` + computes the index of the first *element* directly, without computing + the previous permutations. + + >>> permutation_index([1, 3, 2], range(5)) + 19 + + ``ValueError`` will be raised if the given *element* isn't one of the + permutations of *iterable*. + """ + index = 0 + pool = list(iterable) + for i, x in zip(range(len(pool), -1, -1), element): + r = pool.index(x) + index = index * i + r + del pool[r] + + return index + + +class countable: + """Wrap *iterable* and keep a count of how many items have been consumed. + + The ``items_seen`` attribute starts at ``0`` and increments as the iterable + is consumed: + + >>> iterable = map(str, range(10)) + >>> it = countable(iterable) + >>> it.items_seen + 0 + >>> next(it), next(it) + ('0', '1') + >>> list(it) + ['2', '3', '4', '5', '6', '7', '8', '9'] + >>> it.items_seen + 10 + """ + + def __init__(self, iterable): + self._it = iter(iterable) + self.items_seen = 0 + + def __iter__(self): + return self + + def __next__(self): + item = next(self._it) + self.items_seen += 1 + + return item diff --git a/venv/Lib/site-packages/setuptools/_vendor/more_itertools/more.pyi b/venv/Lib/site-packages/setuptools/_vendor/more_itertools/more.pyi new file mode 100644 index 0000000..2fba9cb --- /dev/null +++ b/venv/Lib/site-packages/setuptools/_vendor/more_itertools/more.pyi @@ -0,0 +1,480 @@ +"""Stubs for more_itertools.more""" + +from typing import ( + Any, + Callable, + Container, + Dict, + Generic, + Hashable, + Iterable, + Iterator, + List, + Optional, + Reversible, + Sequence, + Sized, + Tuple, + Union, + TypeVar, + type_check_only, +) +from types import TracebackType +from typing_extensions import ContextManager, Protocol, Type, overload + +# Type and type variable definitions +_T = TypeVar('_T') +_U = TypeVar('_U') +_V = TypeVar('_V') +_W = TypeVar('_W') +_T_co = TypeVar('_T_co', covariant=True) +_GenFn = TypeVar('_GenFn', bound=Callable[..., Iterator[object]]) +_Raisable = Union[BaseException, 'Type[BaseException]'] + +@type_check_only +class _SizedIterable(Protocol[_T_co], Sized, Iterable[_T_co]): ... + +@type_check_only +class _SizedReversible(Protocol[_T_co], Sized, Reversible[_T_co]): ... + +def chunked( + iterable: Iterable[_T], n: int, strict: bool = ... +) -> Iterator[List[_T]]: ... +@overload +def first(iterable: Iterable[_T]) -> _T: ... +@overload +def first(iterable: Iterable[_T], default: _U) -> Union[_T, _U]: ... +@overload +def last(iterable: Iterable[_T]) -> _T: ... +@overload +def last(iterable: Iterable[_T], default: _U) -> Union[_T, _U]: ... +@overload +def nth_or_last(iterable: Iterable[_T], n: int) -> _T: ... +@overload +def nth_or_last( + iterable: Iterable[_T], n: int, default: _U +) -> Union[_T, _U]: ... + +class peekable(Generic[_T], Iterator[_T]): + def __init__(self, iterable: Iterable[_T]) -> None: ... + def __iter__(self) -> peekable[_T]: ... + def __bool__(self) -> bool: ... + @overload + def peek(self) -> _T: ... + @overload + def peek(self, default: _U) -> Union[_T, _U]: ... + def prepend(self, *items: _T) -> None: ... + def __next__(self) -> _T: ... + @overload + def __getitem__(self, index: int) -> _T: ... + @overload + def __getitem__(self, index: slice) -> List[_T]: ... + +def collate(*iterables: Iterable[_T], **kwargs: Any) -> Iterable[_T]: ... +def consumer(func: _GenFn) -> _GenFn: ... +def ilen(iterable: Iterable[object]) -> int: ... +def iterate(func: Callable[[_T], _T], start: _T) -> Iterator[_T]: ... +def with_iter( + context_manager: ContextManager[Iterable[_T]], +) -> Iterator[_T]: ... +def one( + iterable: Iterable[_T], + too_short: Optional[_Raisable] = ..., + too_long: Optional[_Raisable] = ..., +) -> _T: ... +def distinct_permutations( + iterable: Iterable[_T], r: Optional[int] = ... +) -> Iterator[Tuple[_T, ...]]: ... +def intersperse( + e: _U, iterable: Iterable[_T], n: int = ... +) -> Iterator[Union[_T, _U]]: ... +def unique_to_each(*iterables: Iterable[_T]) -> List[List[_T]]: ... +@overload +def windowed( + seq: Iterable[_T], n: int, *, step: int = ... +) -> Iterator[Tuple[Optional[_T], ...]]: ... +@overload +def windowed( + seq: Iterable[_T], n: int, fillvalue: _U, step: int = ... +) -> Iterator[Tuple[Union[_T, _U], ...]]: ... +def substrings(iterable: Iterable[_T]) -> Iterator[Tuple[_T, ...]]: ... +def substrings_indexes( + seq: Sequence[_T], reverse: bool = ... +) -> Iterator[Tuple[Sequence[_T], int, int]]: ... + +class bucket(Generic[_T, _U], Container[_U]): + def __init__( + self, + iterable: Iterable[_T], + key: Callable[[_T], _U], + validator: Optional[Callable[[object], object]] = ..., + ) -> None: ... + def __contains__(self, value: object) -> bool: ... + def __iter__(self) -> Iterator[_U]: ... + def __getitem__(self, value: object) -> Iterator[_T]: ... + +def spy( + iterable: Iterable[_T], n: int = ... +) -> Tuple[List[_T], Iterator[_T]]: ... +def interleave(*iterables: Iterable[_T]) -> Iterator[_T]: ... +def interleave_longest(*iterables: Iterable[_T]) -> Iterator[_T]: ... +def collapse( + iterable: Iterable[Any], + base_type: Optional[type] = ..., + levels: Optional[int] = ..., +) -> Iterator[Any]: ... +@overload +def side_effect( + func: Callable[[_T], object], + iterable: Iterable[_T], + chunk_size: None = ..., + before: Optional[Callable[[], object]] = ..., + after: Optional[Callable[[], object]] = ..., +) -> Iterator[_T]: ... +@overload +def side_effect( + func: Callable[[List[_T]], object], + iterable: Iterable[_T], + chunk_size: int, + before: Optional[Callable[[], object]] = ..., + after: Optional[Callable[[], object]] = ..., +) -> Iterator[_T]: ... +def sliced( + seq: Sequence[_T], n: int, strict: bool = ... +) -> Iterator[Sequence[_T]]: ... +def split_at( + iterable: Iterable[_T], + pred: Callable[[_T], object], + maxsplit: int = ..., + keep_separator: bool = ..., +) -> Iterator[List[_T]]: ... +def split_before( + iterable: Iterable[_T], pred: Callable[[_T], object], maxsplit: int = ... +) -> Iterator[List[_T]]: ... +def split_after( + iterable: Iterable[_T], pred: Callable[[_T], object], maxsplit: int = ... +) -> Iterator[List[_T]]: ... +def split_when( + iterable: Iterable[_T], + pred: Callable[[_T, _T], object], + maxsplit: int = ..., +) -> Iterator[List[_T]]: ... +def split_into( + iterable: Iterable[_T], sizes: Iterable[Optional[int]] +) -> Iterator[List[_T]]: ... +@overload +def padded( + iterable: Iterable[_T], + *, + n: Optional[int] = ..., + next_multiple: bool = ... +) -> Iterator[Optional[_T]]: ... +@overload +def padded( + iterable: Iterable[_T], + fillvalue: _U, + n: Optional[int] = ..., + next_multiple: bool = ..., +) -> Iterator[Union[_T, _U]]: ... +@overload +def repeat_last(iterable: Iterable[_T]) -> Iterator[_T]: ... +@overload +def repeat_last( + iterable: Iterable[_T], default: _U +) -> Iterator[Union[_T, _U]]: ... +def distribute(n: int, iterable: Iterable[_T]) -> List[Iterator[_T]]: ... +@overload +def stagger( + iterable: Iterable[_T], + offsets: _SizedIterable[int] = ..., + longest: bool = ..., +) -> Iterator[Tuple[Optional[_T], ...]]: ... +@overload +def stagger( + iterable: Iterable[_T], + offsets: _SizedIterable[int] = ..., + longest: bool = ..., + fillvalue: _U = ..., +) -> Iterator[Tuple[Union[_T, _U], ...]]: ... + +class UnequalIterablesError(ValueError): + def __init__( + self, details: Optional[Tuple[int, int, int]] = ... + ) -> None: ... + +def zip_equal(*iterables: Iterable[_T]) -> Iterator[Tuple[_T, ...]]: ... +@overload +def zip_offset( + *iterables: Iterable[_T], offsets: _SizedIterable[int], longest: bool = ... +) -> Iterator[Tuple[Optional[_T], ...]]: ... +@overload +def zip_offset( + *iterables: Iterable[_T], + offsets: _SizedIterable[int], + longest: bool = ..., + fillvalue: _U +) -> Iterator[Tuple[Union[_T, _U], ...]]: ... +def sort_together( + iterables: Iterable[Iterable[_T]], + key_list: Iterable[int] = ..., + key: Optional[Callable[..., Any]] = ..., + reverse: bool = ..., +) -> List[Tuple[_T, ...]]: ... +def unzip(iterable: Iterable[Sequence[_T]]) -> Tuple[Iterator[_T], ...]: ... +def divide(n: int, iterable: Iterable[_T]) -> List[Iterator[_T]]: ... +def always_iterable( + obj: object, + base_type: Union[ + type, Tuple[Union[type, Tuple[Any, ...]], ...], None + ] = ..., +) -> Iterator[Any]: ... +def adjacent( + predicate: Callable[[_T], bool], + iterable: Iterable[_T], + distance: int = ..., +) -> Iterator[Tuple[bool, _T]]: ... +def groupby_transform( + iterable: Iterable[_T], + keyfunc: Optional[Callable[[_T], _U]] = ..., + valuefunc: Optional[Callable[[_T], _V]] = ..., + reducefunc: Optional[Callable[..., _W]] = ..., +) -> Iterator[Tuple[_T, _W]]: ... + +class numeric_range(Generic[_T, _U], Sequence[_T], Hashable, Reversible[_T]): + @overload + def __init__(self, __stop: _T) -> None: ... + @overload + def __init__(self, __start: _T, __stop: _T) -> None: ... + @overload + def __init__(self, __start: _T, __stop: _T, __step: _U) -> None: ... + def __bool__(self) -> bool: ... + def __contains__(self, elem: object) -> bool: ... + def __eq__(self, other: object) -> bool: ... + @overload + def __getitem__(self, key: int) -> _T: ... + @overload + def __getitem__(self, key: slice) -> numeric_range[_T, _U]: ... + def __hash__(self) -> int: ... + def __iter__(self) -> Iterator[_T]: ... + def __len__(self) -> int: ... + def __reduce__( + self, + ) -> Tuple[Type[numeric_range[_T, _U]], Tuple[_T, _T, _U]]: ... + def __repr__(self) -> str: ... + def __reversed__(self) -> Iterator[_T]: ... + def count(self, value: _T) -> int: ... + def index(self, value: _T) -> int: ... # type: ignore + +def count_cycle( + iterable: Iterable[_T], n: Optional[int] = ... +) -> Iterable[Tuple[int, _T]]: ... +def mark_ends( + iterable: Iterable[_T], +) -> Iterable[Tuple[bool, bool, _T]]: ... +def locate( + iterable: Iterable[object], + pred: Callable[..., Any] = ..., + window_size: Optional[int] = ..., +) -> Iterator[int]: ... +def lstrip( + iterable: Iterable[_T], pred: Callable[[_T], object] +) -> Iterator[_T]: ... +def rstrip( + iterable: Iterable[_T], pred: Callable[[_T], object] +) -> Iterator[_T]: ... +def strip( + iterable: Iterable[_T], pred: Callable[[_T], object] +) -> Iterator[_T]: ... + +class islice_extended(Generic[_T], Iterator[_T]): + def __init__( + self, iterable: Iterable[_T], *args: Optional[int] + ) -> None: ... + def __iter__(self) -> islice_extended[_T]: ... + def __next__(self) -> _T: ... + def __getitem__(self, index: slice) -> islice_extended[_T]: ... + +def always_reversible(iterable: Iterable[_T]) -> Iterator[_T]: ... +def consecutive_groups( + iterable: Iterable[_T], ordering: Callable[[_T], int] = ... +) -> Iterator[Iterator[_T]]: ... +@overload +def difference( + iterable: Iterable[_T], + func: Callable[[_T, _T], _U] = ..., + *, + initial: None = ... +) -> Iterator[Union[_T, _U]]: ... +@overload +def difference( + iterable: Iterable[_T], func: Callable[[_T, _T], _U] = ..., *, initial: _U +) -> Iterator[_U]: ... + +class SequenceView(Generic[_T], Sequence[_T]): + def __init__(self, target: Sequence[_T]) -> None: ... + @overload + def __getitem__(self, index: int) -> _T: ... + @overload + def __getitem__(self, index: slice) -> Sequence[_T]: ... + def __len__(self) -> int: ... + +class seekable(Generic[_T], Iterator[_T]): + def __init__( + self, iterable: Iterable[_T], maxlen: Optional[int] = ... + ) -> None: ... + def __iter__(self) -> seekable[_T]: ... + def __next__(self) -> _T: ... + def __bool__(self) -> bool: ... + @overload + def peek(self) -> _T: ... + @overload + def peek(self, default: _U) -> Union[_T, _U]: ... + def elements(self) -> SequenceView[_T]: ... + def seek(self, index: int) -> None: ... + +class run_length: + @staticmethod + def encode(iterable: Iterable[_T]) -> Iterator[Tuple[_T, int]]: ... + @staticmethod + def decode(iterable: Iterable[Tuple[_T, int]]) -> Iterator[_T]: ... + +def exactly_n( + iterable: Iterable[_T], n: int, predicate: Callable[[_T], object] = ... +) -> bool: ... +def circular_shifts(iterable: Iterable[_T]) -> List[Tuple[_T, ...]]: ... +def make_decorator( + wrapping_func: Callable[..., _U], result_index: int = ... +) -> Callable[..., Callable[[Callable[..., Any]], Callable[..., _U]]]: ... +@overload +def map_reduce( + iterable: Iterable[_T], + keyfunc: Callable[[_T], _U], + valuefunc: None = ..., + reducefunc: None = ..., +) -> Dict[_U, List[_T]]: ... +@overload +def map_reduce( + iterable: Iterable[_T], + keyfunc: Callable[[_T], _U], + valuefunc: Callable[[_T], _V], + reducefunc: None = ..., +) -> Dict[_U, List[_V]]: ... +@overload +def map_reduce( + iterable: Iterable[_T], + keyfunc: Callable[[_T], _U], + valuefunc: None = ..., + reducefunc: Callable[[List[_T]], _W] = ..., +) -> Dict[_U, _W]: ... +@overload +def map_reduce( + iterable: Iterable[_T], + keyfunc: Callable[[_T], _U], + valuefunc: Callable[[_T], _V], + reducefunc: Callable[[List[_V]], _W], +) -> Dict[_U, _W]: ... +def rlocate( + iterable: Iterable[_T], + pred: Callable[..., object] = ..., + window_size: Optional[int] = ..., +) -> Iterator[int]: ... +def replace( + iterable: Iterable[_T], + pred: Callable[..., object], + substitutes: Iterable[_U], + count: Optional[int] = ..., + window_size: int = ..., +) -> Iterator[Union[_T, _U]]: ... +def partitions(iterable: Iterable[_T]) -> Iterator[List[List[_T]]]: ... +def set_partitions( + iterable: Iterable[_T], k: Optional[int] = ... +) -> Iterator[List[List[_T]]]: ... + +class time_limited(Generic[_T], Iterator[_T]): + def __init__( + self, limit_seconds: float, iterable: Iterable[_T] + ) -> None: ... + def __iter__(self) -> islice_extended[_T]: ... + def __next__(self) -> _T: ... + +@overload +def only( + iterable: Iterable[_T], *, too_long: Optional[_Raisable] = ... +) -> Optional[_T]: ... +@overload +def only( + iterable: Iterable[_T], default: _U, too_long: Optional[_Raisable] = ... +) -> Union[_T, _U]: ... +def ichunked(iterable: Iterable[_T], n: int) -> Iterator[Iterator[_T]]: ... +def distinct_combinations( + iterable: Iterable[_T], r: int +) -> Iterator[Tuple[_T, ...]]: ... +def filter_except( + validator: Callable[[Any], object], + iterable: Iterable[_T], + *exceptions: Type[BaseException] +) -> Iterator[_T]: ... +def map_except( + function: Callable[[Any], _U], + iterable: Iterable[_T], + *exceptions: Type[BaseException] +) -> Iterator[_U]: ... +def sample( + iterable: Iterable[_T], + k: int, + weights: Optional[Iterable[float]] = ..., +) -> List[_T]: ... +def is_sorted( + iterable: Iterable[_T], + key: Optional[Callable[[_T], _U]] = ..., + reverse: bool = False, +) -> bool: ... + +class AbortThread(BaseException): + pass + +class callback_iter(Generic[_T], Iterator[_T]): + def __init__( + self, + func: Callable[..., Any], + callback_kwd: str = ..., + wait_seconds: float = ..., + ) -> None: ... + def __enter__(self) -> callback_iter[_T]: ... + def __exit__( + self, + exc_type: Optional[Type[BaseException]], + exc_value: Optional[BaseException], + traceback: Optional[TracebackType], + ) -> Optional[bool]: ... + def __iter__(self) -> callback_iter[_T]: ... + def __next__(self) -> _T: ... + def _reader(self) -> Iterator[_T]: ... + @property + def done(self) -> bool: ... + @property + def result(self) -> Any: ... + +def windowed_complete( + iterable: Iterable[_T], n: int +) -> Iterator[Tuple[_T, ...]]: ... +def all_unique( + iterable: Iterable[_T], key: Optional[Callable[[_T], _U]] = ... +) -> bool: ... +def nth_product(index: int, *args: Iterable[_T]) -> Tuple[_T, ...]: ... +def nth_permutation( + iterable: Iterable[_T], r: int, index: int +) -> Tuple[_T, ...]: ... +def value_chain(*args: Union[_T, Iterable[_T]]) -> Iterable[_T]: ... +def product_index(element: Iterable[_T], *args: Iterable[_T]) -> int: ... +def combination_index( + element: Iterable[_T], iterable: Iterable[_T] +) -> int: ... +def permutation_index( + element: Iterable[_T], iterable: Iterable[_T] +) -> int: ... + +class countable(Generic[_T], Iterator[_T]): + def __init__(self, iterable: Iterable[_T]) -> None: ... + def __iter__(self) -> countable[_T]: ... + def __next__(self) -> _T: ... diff --git a/venv/Lib/site-packages/setuptools/_vendor/more_itertools/py.typed b/venv/Lib/site-packages/setuptools/_vendor/more_itertools/py.typed new file mode 100644 index 0000000..e69de29 diff --git a/venv/Lib/site-packages/setuptools/_vendor/more_itertools/recipes.py b/venv/Lib/site-packages/setuptools/_vendor/more_itertools/recipes.py new file mode 100644 index 0000000..521abd7 --- /dev/null +++ b/venv/Lib/site-packages/setuptools/_vendor/more_itertools/recipes.py @@ -0,0 +1,620 @@ +"""Imported from the recipes section of the itertools documentation. + +All functions taken from the recipes section of the itertools library docs +[1]_. +Some backward-compatible usability improvements have been made. + +.. [1] http://docs.python.org/library/itertools.html#recipes + +""" +import warnings +from collections import deque +from itertools import ( + chain, + combinations, + count, + cycle, + groupby, + islice, + repeat, + starmap, + tee, + zip_longest, +) +import operator +from random import randrange, sample, choice + +__all__ = [ + 'all_equal', + 'consume', + 'convolve', + 'dotproduct', + 'first_true', + 'flatten', + 'grouper', + 'iter_except', + 'ncycles', + 'nth', + 'nth_combination', + 'padnone', + 'pad_none', + 'pairwise', + 'partition', + 'powerset', + 'prepend', + 'quantify', + 'random_combination_with_replacement', + 'random_combination', + 'random_permutation', + 'random_product', + 'repeatfunc', + 'roundrobin', + 'tabulate', + 'tail', + 'take', + 'unique_everseen', + 'unique_justseen', +] + + +def take(n, iterable): + """Return first *n* items of the iterable as a list. + + >>> take(3, range(10)) + [0, 1, 2] + + If there are fewer than *n* items in the iterable, all of them are + returned. + + >>> take(10, range(3)) + [0, 1, 2] + + """ + return list(islice(iterable, n)) + + +def tabulate(function, start=0): + """Return an iterator over the results of ``func(start)``, + ``func(start + 1)``, ``func(start + 2)``... + + *func* should be a function that accepts one integer argument. + + If *start* is not specified it defaults to 0. It will be incremented each + time the iterator is advanced. + + >>> square = lambda x: x ** 2 + >>> iterator = tabulate(square, -3) + >>> take(4, iterator) + [9, 4, 1, 0] + + """ + return map(function, count(start)) + + +def tail(n, iterable): + """Return an iterator over the last *n* items of *iterable*. + + >>> t = tail(3, 'ABCDEFG') + >>> list(t) + ['E', 'F', 'G'] + + """ + return iter(deque(iterable, maxlen=n)) + + +def consume(iterator, n=None): + """Advance *iterable* by *n* steps. If *n* is ``None``, consume it + entirely. + + Efficiently exhausts an iterator without returning values. Defaults to + consuming the whole iterator, but an optional second argument may be + provided to limit consumption. + + >>> i = (x for x in range(10)) + >>> next(i) + 0 + >>> consume(i, 3) + >>> next(i) + 4 + >>> consume(i) + >>> next(i) + Traceback (most recent call last): + File "", line 1, in + StopIteration + + If the iterator has fewer items remaining than the provided limit, the + whole iterator will be consumed. + + >>> i = (x for x in range(3)) + >>> consume(i, 5) + >>> next(i) + Traceback (most recent call last): + File "", line 1, in + StopIteration + + """ + # Use functions that consume iterators at C speed. + if n is None: + # feed the entire iterator into a zero-length deque + deque(iterator, maxlen=0) + else: + # advance to the empty slice starting at position n + next(islice(iterator, n, n), None) + + +def nth(iterable, n, default=None): + """Returns the nth item or a default value. + + >>> l = range(10) + >>> nth(l, 3) + 3 + >>> nth(l, 20, "zebra") + 'zebra' + + """ + return next(islice(iterable, n, None), default) + + +def all_equal(iterable): + """ + Returns ``True`` if all the elements are equal to each other. + + >>> all_equal('aaaa') + True + >>> all_equal('aaab') + False + + """ + g = groupby(iterable) + return next(g, True) and not next(g, False) + + +def quantify(iterable, pred=bool): + """Return the how many times the predicate is true. + + >>> quantify([True, False, True]) + 2 + + """ + return sum(map(pred, iterable)) + + +def pad_none(iterable): + """Returns the sequence of elements and then returns ``None`` indefinitely. + + >>> take(5, pad_none(range(3))) + [0, 1, 2, None, None] + + Useful for emulating the behavior of the built-in :func:`map` function. + + See also :func:`padded`. + + """ + return chain(iterable, repeat(None)) + + +padnone = pad_none + + +def ncycles(iterable, n): + """Returns the sequence elements *n* times + + >>> list(ncycles(["a", "b"], 3)) + ['a', 'b', 'a', 'b', 'a', 'b'] + + """ + return chain.from_iterable(repeat(tuple(iterable), n)) + + +def dotproduct(vec1, vec2): + """Returns the dot product of the two iterables. + + >>> dotproduct([10, 10], [20, 20]) + 400 + + """ + return sum(map(operator.mul, vec1, vec2)) + + +def flatten(listOfLists): + """Return an iterator flattening one level of nesting in a list of lists. + + >>> list(flatten([[0, 1], [2, 3]])) + [0, 1, 2, 3] + + See also :func:`collapse`, which can flatten multiple levels of nesting. + + """ + return chain.from_iterable(listOfLists) + + +def repeatfunc(func, times=None, *args): + """Call *func* with *args* repeatedly, returning an iterable over the + results. + + If *times* is specified, the iterable will terminate after that many + repetitions: + + >>> from operator import add + >>> times = 4 + >>> args = 3, 5 + >>> list(repeatfunc(add, times, *args)) + [8, 8, 8, 8] + + If *times* is ``None`` the iterable will not terminate: + + >>> from random import randrange + >>> times = None + >>> args = 1, 11 + >>> take(6, repeatfunc(randrange, times, *args)) # doctest:+SKIP + [2, 4, 8, 1, 8, 4] + + """ + if times is None: + return starmap(func, repeat(args)) + return starmap(func, repeat(args, times)) + + +def _pairwise(iterable): + """Returns an iterator of paired items, overlapping, from the original + + >>> take(4, pairwise(count())) + [(0, 1), (1, 2), (2, 3), (3, 4)] + + On Python 3.10 and above, this is an alias for :func:`itertools.pairwise`. + + """ + a, b = tee(iterable) + next(b, None) + yield from zip(a, b) + + +try: + from itertools import pairwise as itertools_pairwise +except ImportError: + pairwise = _pairwise +else: + + def pairwise(iterable): + yield from itertools_pairwise(iterable) + + pairwise.__doc__ = _pairwise.__doc__ + + +def grouper(iterable, n, fillvalue=None): + """Collect data into fixed-length chunks or blocks. + + >>> list(grouper('ABCDEFG', 3, 'x')) + [('A', 'B', 'C'), ('D', 'E', 'F'), ('G', 'x', 'x')] + + """ + if isinstance(iterable, int): + warnings.warn( + "grouper expects iterable as first parameter", DeprecationWarning + ) + n, iterable = iterable, n + args = [iter(iterable)] * n + return zip_longest(fillvalue=fillvalue, *args) + + +def roundrobin(*iterables): + """Yields an item from each iterable, alternating between them. + + >>> list(roundrobin('ABC', 'D', 'EF')) + ['A', 'D', 'E', 'B', 'F', 'C'] + + This function produces the same output as :func:`interleave_longest`, but + may perform better for some inputs (in particular when the number of + iterables is small). + + """ + # Recipe credited to George Sakkis + pending = len(iterables) + nexts = cycle(iter(it).__next__ for it in iterables) + while pending: + try: + for next in nexts: + yield next() + except StopIteration: + pending -= 1 + nexts = cycle(islice(nexts, pending)) + + +def partition(pred, iterable): + """ + Returns a 2-tuple of iterables derived from the input iterable. + The first yields the items that have ``pred(item) == False``. + The second yields the items that have ``pred(item) == True``. + + >>> is_odd = lambda x: x % 2 != 0 + >>> iterable = range(10) + >>> even_items, odd_items = partition(is_odd, iterable) + >>> list(even_items), list(odd_items) + ([0, 2, 4, 6, 8], [1, 3, 5, 7, 9]) + + If *pred* is None, :func:`bool` is used. + + >>> iterable = [0, 1, False, True, '', ' '] + >>> false_items, true_items = partition(None, iterable) + >>> list(false_items), list(true_items) + ([0, False, ''], [1, True, ' ']) + + """ + if pred is None: + pred = bool + + evaluations = ((pred(x), x) for x in iterable) + t1, t2 = tee(evaluations) + return ( + (x for (cond, x) in t1 if not cond), + (x for (cond, x) in t2 if cond), + ) + + +def powerset(iterable): + """Yields all possible subsets of the iterable. + + >>> list(powerset([1, 2, 3])) + [(), (1,), (2,), (3,), (1, 2), (1, 3), (2, 3), (1, 2, 3)] + + :func:`powerset` will operate on iterables that aren't :class:`set` + instances, so repeated elements in the input will produce repeated elements + in the output. Use :func:`unique_everseen` on the input to avoid generating + duplicates: + + >>> seq = [1, 1, 0] + >>> list(powerset(seq)) + [(), (1,), (1,), (0,), (1, 1), (1, 0), (1, 0), (1, 1, 0)] + >>> from more_itertools import unique_everseen + >>> list(powerset(unique_everseen(seq))) + [(), (1,), (0,), (1, 0)] + + """ + s = list(iterable) + return chain.from_iterable(combinations(s, r) for r in range(len(s) + 1)) + + +def unique_everseen(iterable, key=None): + """ + Yield unique elements, preserving order. + + >>> list(unique_everseen('AAAABBBCCDAABBB')) + ['A', 'B', 'C', 'D'] + >>> list(unique_everseen('ABBCcAD', str.lower)) + ['A', 'B', 'C', 'D'] + + Sequences with a mix of hashable and unhashable items can be used. + The function will be slower (i.e., `O(n^2)`) for unhashable items. + + Remember that ``list`` objects are unhashable - you can use the *key* + parameter to transform the list to a tuple (which is hashable) to + avoid a slowdown. + + >>> iterable = ([1, 2], [2, 3], [1, 2]) + >>> list(unique_everseen(iterable)) # Slow + [[1, 2], [2, 3]] + >>> list(unique_everseen(iterable, key=tuple)) # Faster + [[1, 2], [2, 3]] + + Similary, you may want to convert unhashable ``set`` objects with + ``key=frozenset``. For ``dict`` objects, + ``key=lambda x: frozenset(x.items())`` can be used. + + """ + seenset = set() + seenset_add = seenset.add + seenlist = [] + seenlist_add = seenlist.append + use_key = key is not None + + for element in iterable: + k = key(element) if use_key else element + try: + if k not in seenset: + seenset_add(k) + yield element + except TypeError: + if k not in seenlist: + seenlist_add(k) + yield element + + +def unique_justseen(iterable, key=None): + """Yields elements in order, ignoring serial duplicates + + >>> list(unique_justseen('AAAABBBCCDAABBB')) + ['A', 'B', 'C', 'D', 'A', 'B'] + >>> list(unique_justseen('ABBCcAD', str.lower)) + ['A', 'B', 'C', 'A', 'D'] + + """ + return map(next, map(operator.itemgetter(1), groupby(iterable, key))) + + +def iter_except(func, exception, first=None): + """Yields results from a function repeatedly until an exception is raised. + + Converts a call-until-exception interface to an iterator interface. + Like ``iter(func, sentinel)``, but uses an exception instead of a sentinel + to end the loop. + + >>> l = [0, 1, 2] + >>> list(iter_except(l.pop, IndexError)) + [2, 1, 0] + + """ + try: + if first is not None: + yield first() + while 1: + yield func() + except exception: + pass + + +def first_true(iterable, default=None, pred=None): + """ + Returns the first true value in the iterable. + + If no true value is found, returns *default* + + If *pred* is not None, returns the first item for which + ``pred(item) == True`` . + + >>> first_true(range(10)) + 1 + >>> first_true(range(10), pred=lambda x: x > 5) + 6 + >>> first_true(range(10), default='missing', pred=lambda x: x > 9) + 'missing' + + """ + return next(filter(pred, iterable), default) + + +def random_product(*args, repeat=1): + """Draw an item at random from each of the input iterables. + + >>> random_product('abc', range(4), 'XYZ') # doctest:+SKIP + ('c', 3, 'Z') + + If *repeat* is provided as a keyword argument, that many items will be + drawn from each iterable. + + >>> random_product('abcd', range(4), repeat=2) # doctest:+SKIP + ('a', 2, 'd', 3) + + This equivalent to taking a random selection from + ``itertools.product(*args, **kwarg)``. + + """ + pools = [tuple(pool) for pool in args] * repeat + return tuple(choice(pool) for pool in pools) + + +def random_permutation(iterable, r=None): + """Return a random *r* length permutation of the elements in *iterable*. + + If *r* is not specified or is ``None``, then *r* defaults to the length of + *iterable*. + + >>> random_permutation(range(5)) # doctest:+SKIP + (3, 4, 0, 1, 2) + + This equivalent to taking a random selection from + ``itertools.permutations(iterable, r)``. + + """ + pool = tuple(iterable) + r = len(pool) if r is None else r + return tuple(sample(pool, r)) + + +def random_combination(iterable, r): + """Return a random *r* length subsequence of the elements in *iterable*. + + >>> random_combination(range(5), 3) # doctest:+SKIP + (2, 3, 4) + + This equivalent to taking a random selection from + ``itertools.combinations(iterable, r)``. + + """ + pool = tuple(iterable) + n = len(pool) + indices = sorted(sample(range(n), r)) + return tuple(pool[i] for i in indices) + + +def random_combination_with_replacement(iterable, r): + """Return a random *r* length subsequence of elements in *iterable*, + allowing individual elements to be repeated. + + >>> random_combination_with_replacement(range(3), 5) # doctest:+SKIP + (0, 0, 1, 2, 2) + + This equivalent to taking a random selection from + ``itertools.combinations_with_replacement(iterable, r)``. + + """ + pool = tuple(iterable) + n = len(pool) + indices = sorted(randrange(n) for i in range(r)) + return tuple(pool[i] for i in indices) + + +def nth_combination(iterable, r, index): + """Equivalent to ``list(combinations(iterable, r))[index]``. + + The subsequences of *iterable* that are of length *r* can be ordered + lexicographically. :func:`nth_combination` computes the subsequence at + sort position *index* directly, without computing the previous + subsequences. + + >>> nth_combination(range(5), 3, 5) + (0, 3, 4) + + ``ValueError`` will be raised If *r* is negative or greater than the length + of *iterable*. + ``IndexError`` will be raised if the given *index* is invalid. + """ + pool = tuple(iterable) + n = len(pool) + if (r < 0) or (r > n): + raise ValueError + + c = 1 + k = min(r, n - r) + for i in range(1, k + 1): + c = c * (n - k + i) // i + + if index < 0: + index += c + + if (index < 0) or (index >= c): + raise IndexError + + result = [] + while r: + c, n, r = c * r // n, n - 1, r - 1 + while index >= c: + index -= c + c, n = c * (n - r) // n, n - 1 + result.append(pool[-1 - n]) + + return tuple(result) + + +def prepend(value, iterator): + """Yield *value*, followed by the elements in *iterator*. + + >>> value = '0' + >>> iterator = ['1', '2', '3'] + >>> list(prepend(value, iterator)) + ['0', '1', '2', '3'] + + To prepend multiple values, see :func:`itertools.chain` + or :func:`value_chain`. + + """ + return chain([value], iterator) + + +def convolve(signal, kernel): + """Convolve the iterable *signal* with the iterable *kernel*. + + >>> signal = (1, 2, 3, 4, 5) + >>> kernel = [3, 2, 1] + >>> list(convolve(signal, kernel)) + [3, 8, 14, 20, 26, 14, 5] + + Note: the input arguments are not interchangeable, as the *kernel* + is immediately consumed and stored. + + """ + kernel = tuple(kernel)[::-1] + n = len(kernel) + window = deque([0], maxlen=n) * n + for x in chain(signal, repeat(0, n - 1)): + window.append(x) + yield sum(map(operator.mul, kernel, window)) diff --git a/venv/Lib/site-packages/setuptools/_vendor/more_itertools/recipes.pyi b/venv/Lib/site-packages/setuptools/_vendor/more_itertools/recipes.pyi new file mode 100644 index 0000000..5e39d96 --- /dev/null +++ b/venv/Lib/site-packages/setuptools/_vendor/more_itertools/recipes.pyi @@ -0,0 +1,103 @@ +"""Stubs for more_itertools.recipes""" +from typing import ( + Any, + Callable, + Iterable, + Iterator, + List, + Optional, + Tuple, + TypeVar, + Union, +) +from typing_extensions import overload, Type + +# Type and type variable definitions +_T = TypeVar('_T') +_U = TypeVar('_U') + +def take(n: int, iterable: Iterable[_T]) -> List[_T]: ... +def tabulate( + function: Callable[[int], _T], start: int = ... +) -> Iterator[_T]: ... +def tail(n: int, iterable: Iterable[_T]) -> Iterator[_T]: ... +def consume(iterator: Iterable[object], n: Optional[int] = ...) -> None: ... +@overload +def nth(iterable: Iterable[_T], n: int) -> Optional[_T]: ... +@overload +def nth(iterable: Iterable[_T], n: int, default: _U) -> Union[_T, _U]: ... +def all_equal(iterable: Iterable[object]) -> bool: ... +def quantify( + iterable: Iterable[_T], pred: Callable[[_T], bool] = ... +) -> int: ... +def pad_none(iterable: Iterable[_T]) -> Iterator[Optional[_T]]: ... +def padnone(iterable: Iterable[_T]) -> Iterator[Optional[_T]]: ... +def ncycles(iterable: Iterable[_T], n: int) -> Iterator[_T]: ... +def dotproduct(vec1: Iterable[object], vec2: Iterable[object]) -> object: ... +def flatten(listOfLists: Iterable[Iterable[_T]]) -> Iterator[_T]: ... +def repeatfunc( + func: Callable[..., _U], times: Optional[int] = ..., *args: Any +) -> Iterator[_U]: ... +def pairwise(iterable: Iterable[_T]) -> Iterator[Tuple[_T, _T]]: ... +@overload +def grouper( + iterable: Iterable[_T], n: int +) -> Iterator[Tuple[Optional[_T], ...]]: ... +@overload +def grouper( + iterable: Iterable[_T], n: int, fillvalue: _U +) -> Iterator[Tuple[Union[_T, _U], ...]]: ... +@overload +def grouper( # Deprecated interface + iterable: int, n: Iterable[_T] +) -> Iterator[Tuple[Optional[_T], ...]]: ... +@overload +def grouper( # Deprecated interface + iterable: int, n: Iterable[_T], fillvalue: _U +) -> Iterator[Tuple[Union[_T, _U], ...]]: ... +def roundrobin(*iterables: Iterable[_T]) -> Iterator[_T]: ... +def partition( + pred: Optional[Callable[[_T], object]], iterable: Iterable[_T] +) -> Tuple[Iterator[_T], Iterator[_T]]: ... +def powerset(iterable: Iterable[_T]) -> Iterator[Tuple[_T, ...]]: ... +def unique_everseen( + iterable: Iterable[_T], key: Optional[Callable[[_T], _U]] = ... +) -> Iterator[_T]: ... +def unique_justseen( + iterable: Iterable[_T], key: Optional[Callable[[_T], object]] = ... +) -> Iterator[_T]: ... +@overload +def iter_except( + func: Callable[[], _T], exception: Type[BaseException], first: None = ... +) -> Iterator[_T]: ... +@overload +def iter_except( + func: Callable[[], _T], + exception: Type[BaseException], + first: Callable[[], _U], +) -> Iterator[Union[_T, _U]]: ... +@overload +def first_true( + iterable: Iterable[_T], *, pred: Optional[Callable[[_T], object]] = ... +) -> Optional[_T]: ... +@overload +def first_true( + iterable: Iterable[_T], + default: _U, + pred: Optional[Callable[[_T], object]] = ..., +) -> Union[_T, _U]: ... +def random_product( + *args: Iterable[_T], repeat: int = ... +) -> Tuple[_T, ...]: ... +def random_permutation( + iterable: Iterable[_T], r: Optional[int] = ... +) -> Tuple[_T, ...]: ... +def random_combination(iterable: Iterable[_T], r: int) -> Tuple[_T, ...]: ... +def random_combination_with_replacement( + iterable: Iterable[_T], r: int +) -> Tuple[_T, ...]: ... +def nth_combination( + iterable: Iterable[_T], r: int, index: int +) -> Tuple[_T, ...]: ... +def prepend(value: _T, iterator: Iterable[_U]) -> Iterator[Union[_T, _U]]: ... +def convolve(signal: Iterable[_T], kernel: Iterable[_T]) -> Iterator[_T]: ... diff --git a/venv/Lib/site-packages/setuptools/_vendor/ordered_set.py b/venv/Lib/site-packages/setuptools/_vendor/ordered_set.py new file mode 100644 index 0000000..1487600 --- /dev/null +++ b/venv/Lib/site-packages/setuptools/_vendor/ordered_set.py @@ -0,0 +1,488 @@ +""" +An OrderedSet is a custom MutableSet that remembers its order, so that every +entry has an index that can be looked up. + +Based on a recipe originally posted to ActiveState Recipes by Raymond Hettiger, +and released under the MIT license. +""" +import itertools as it +from collections import deque + +try: + # Python 3 + from collections.abc import MutableSet, Sequence +except ImportError: + # Python 2.7 + from collections import MutableSet, Sequence + +SLICE_ALL = slice(None) +__version__ = "3.1" + + +def is_iterable(obj): + """ + Are we being asked to look up a list of things, instead of a single thing? + We check for the `__iter__` attribute so that this can cover types that + don't have to be known by this module, such as NumPy arrays. + + Strings, however, should be considered as atomic values to look up, not + iterables. The same goes for tuples, since they are immutable and therefore + valid entries. + + We don't need to check for the Python 2 `unicode` type, because it doesn't + have an `__iter__` attribute anyway. + """ + return ( + hasattr(obj, "__iter__") + and not isinstance(obj, str) + and not isinstance(obj, tuple) + ) + + +class OrderedSet(MutableSet, Sequence): + """ + An OrderedSet is a custom MutableSet that remembers its order, so that + every entry has an index that can be looked up. + + Example: + >>> 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/site-packages/setuptools/_vendor/packaging/__init__.py b/venv/Lib/site-packages/setuptools/_vendor/packaging/__init__.py new file mode 100644 index 0000000..e7c0aa1 --- /dev/null +++ b/venv/Lib/site-packages/setuptools/_vendor/packaging/__init__.py @@ -0,0 +1,15 @@ +# 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. + +__title__ = "packaging" +__summary__ = "Core utilities for Python packages" +__uri__ = "https://github.com/pypa/packaging" + +__version__ = "24.0" + +__author__ = "Donald Stufft and individual contributors" +__email__ = "donald@stufft.io" + +__license__ = "BSD-2-Clause or Apache-2.0" +__copyright__ = "2014 %s" % __author__ diff --git a/venv/Lib/site-packages/setuptools/_vendor/packaging/__pycache__/__init__.cpython-312.pyc b/venv/Lib/site-packages/setuptools/_vendor/packaging/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..b2cbd3df365e66f1ad61dd0281c52e2be9a337a1 GIT binary patch literal 579 zcmYLG&x_MQ6rQx*wrMw|2M_8&qM*=)G;8QV77?{QiAZ7b5&}b-nKloTnJ_;p$tnI7 z`Y-smc=A?ys`w8S_O>Udt;@c{<9##V``(*xemPDLnGk=x<=#T*?*ZIJ<9~5;VT$jF zpb!x&v`8atkY?B;)WaWY6KlLr|G zV2gP35rH* z%GS|GSVRZp-FX;Es&t_^&!mqHg>Z52o{upwFr&vsdE0%RbD}f)Mhwgr=FgFwn6z(N ymbLF8=V5Jc20`85^t`&W8$77{JLgs1+nhhC?cLzQTnr!m`0#u9;+rGRP2)eD+`j|> literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/setuptools/_vendor/packaging/__pycache__/_elffile.cpython-312.pyc b/venv/Lib/site-packages/setuptools/_vendor/packaging/__pycache__/_elffile.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..1fa8b8fda6314cf45fa2c49074d06800cfc77d2b GIT binary patch literal 5047 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 CA2^}_ literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/setuptools/_vendor/packaging/__pycache__/_manylinux.cpython-312.pyc b/venv/Lib/site-packages/setuptools/_vendor/packaging/__pycache__/_manylinux.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..fbdc5ec6365ec5699a7049be550db434fd80aefa GIT binary patch literal 9927 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 literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/setuptools/_vendor/packaging/__pycache__/_parser.cpython-312.pyc b/venv/Lib/site-packages/setuptools/_vendor/packaging/__pycache__/_parser.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..de3d49cba56f2e416a751a89ecf7b9d1979ed6d1 GIT binary patch literal 14085 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 literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/setuptools/_vendor/packaging/__pycache__/_tokenizer.cpython-312.pyc b/venv/Lib/site-packages/setuptools/_vendor/packaging/__pycache__/_tokenizer.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..95a88cbc382284df5825edc35f75dd7985e7ccf9 GIT binary patch literal 7958 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 literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/setuptools/_vendor/packaging/__pycache__/markers.cpython-312.pyc b/venv/Lib/site-packages/setuptools/_vendor/packaging/__pycache__/markers.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..12e020bd0f8bf9e372cf1af87d9d5e14640affbd GIT binary patch literal 10520 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 aMP;$~<_ntyK7ZT&HSQ$$89~UH;Qs)Zcor`J literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/setuptools/_vendor/packaging/__pycache__/metadata.cpython-312.pyc b/venv/Lib/site-packages/setuptools/_vendor/packaging/__pycache__/metadata.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..e1d2be46338a19470c870dcef2366ae054e1d905 GIT binary patch literal 26966 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 YiH8rYP?|gK19rY~uJ0!t-5E^$Z^yC1t^fc4 literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/setuptools/_vendor/packaging/__pycache__/tags.cpython-312.pyc b/venv/Lib/site-packages/setuptools/_vendor/packaging/__pycache__/tags.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..c4e47f1a4bbd6b0c04aa0b4a5bc3b1207faa1b62 GIT binary patch literal 21782 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 literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/setuptools/_vendor/packaging/__pycache__/utils.cpython-312.pyc b/venv/Lib/site-packages/setuptools/_vendor/packaging/__pycache__/utils.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..f87a21ff09a4ab68dd42d0c3b9a927db27353ebd GIT binary patch literal 7305 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 h;R=21Do&Lkz?~LSh_(=_aj^7P8*65y+DBo@}rp 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 literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/setuptools/_vendor/packaging/_elffile.py b/venv/Lib/site-packages/setuptools/_vendor/packaging/_elffile.py new file mode 100644 index 0000000..6fb19b3 --- /dev/null +++ b/venv/Lib/site-packages/setuptools/_vendor/packaging/_elffile.py @@ -0,0 +1,108 @@ +""" +ELF file parser. + +This provides a class ``ELFFile`` that parses an ELF executable in a similar +interface to ``ZipFile``. Only the read interface is implemented. + +Based on: https://gist.github.com/lyssdod/f51579ae8d93c8657a5564aefc2ffbca +ELF header: https://refspecs.linuxfoundation.org/elf/gabi4+/ch4.eheader.html +""" + +import enum +import os +import struct +from typing import IO, Optional, Tuple + + +class ELFInvalid(ValueError): + pass + + +class EIClass(enum.IntEnum): + C32 = 1 + C64 = 2 + + +class EIData(enum.IntEnum): + Lsb = 1 + Msb = 2 + + +class EMachine(enum.IntEnum): + I386 = 3 + S390 = 22 + Arm = 40 + X8664 = 62 + AArc64 = 183 + + +class ELFFile: + """ + Representation of an ELF executable. + """ + + def __init__(self, f: IO[bytes]) -> None: + self._f = f + + try: + ident = self._read("16B") + except struct.error: + raise ELFInvalid("unable to parse identification") + magic = bytes(ident[:4]) + if magic != b"\x7fELF": + raise ELFInvalid(f"invalid magic: {magic!r}") + + self.capacity = ident[4] # Format for program header (bitness). + self.encoding = ident[5] # Data structure encoding (endianness). + + try: + # e_fmt: Format for program header. + # p_fmt: Format for section header. + # p_idx: Indexes to find p_type, p_offset, and p_filesz. + e_fmt, self._p_fmt, self._p_idx = { + (1, 1): ("HHIIIIIHHH", ">IIIIIIII", (0, 1, 4)), # 32-bit MSB. + (2, 1): ("HHIQQQIHHH", ">IIQQQQQQ", (0, 2, 5)), # 64-bit MSB. + }[(self.capacity, self.encoding)] + except KeyError: + raise ELFInvalid( + f"unrecognized capacity ({self.capacity}) or " + f"encoding ({self.encoding})" + ) + + try: + ( + _, + self.machine, # Architecture type. + _, + _, + self._e_phoff, # Offset of program header. + _, + self.flags, # Processor-specific flags. + _, + self._e_phentsize, # Size of section. + self._e_phnum, # Number of sections. + ) = self._read(e_fmt) + except struct.error as e: + raise ELFInvalid("unable to parse machine and section information") from e + + def _read(self, fmt: str) -> Tuple[int, ...]: + return struct.unpack(fmt, self._f.read(struct.calcsize(fmt))) + + @property + def interpreter(self) -> Optional[str]: + """ + The path recorded in the ``PT_INTERP`` section header. + """ + for index in range(self._e_phnum): + self._f.seek(self._e_phoff + self._e_phentsize * index) + try: + data = self._read(self._p_fmt) + except struct.error: + continue + if data[self._p_idx[0]] != 3: # Not PT_INTERP. + continue + self._f.seek(data[self._p_idx[1]]) + return os.fsdecode(self._f.read(data[self._p_idx[2]])).strip("\0") + return None diff --git a/venv/Lib/site-packages/setuptools/_vendor/packaging/_manylinux.py b/venv/Lib/site-packages/setuptools/_vendor/packaging/_manylinux.py new file mode 100644 index 0000000..ad62505 --- /dev/null +++ b/venv/Lib/site-packages/setuptools/_vendor/packaging/_manylinux.py @@ -0,0 +1,260 @@ +import collections +import contextlib +import functools +import os +import re +import sys +import warnings +from typing import Dict, Generator, Iterator, NamedTuple, Optional, Sequence, Tuple + +from ._elffile import EIClass, EIData, ELFFile, EMachine + +EF_ARM_ABIMASK = 0xFF000000 +EF_ARM_ABI_VER5 = 0x05000000 +EF_ARM_ABI_FLOAT_HARD = 0x00000400 + + +# `os.PathLike` not a generic type until Python 3.9, so sticking with `str` +# as the type for `path` until then. +@contextlib.contextmanager +def _parse_elf(path: str) -> Generator[Optional[ELFFile], None, None]: + try: + with open(path, "rb") as f: + yield ELFFile(f) + except (OSError, TypeError, ValueError): + yield None + + +def _is_linux_armhf(executable: str) -> bool: + # hard-float ABI can be detected from the ELF header of the running + # process + # https://static.docs.arm.com/ihi0044/g/aaelf32.pdf + with _parse_elf(executable) as f: + return ( + f is not None + and f.capacity == EIClass.C32 + and f.encoding == EIData.Lsb + and f.machine == EMachine.Arm + and f.flags & EF_ARM_ABIMASK == EF_ARM_ABI_VER5 + and f.flags & EF_ARM_ABI_FLOAT_HARD == EF_ARM_ABI_FLOAT_HARD + ) + + +def _is_linux_i686(executable: str) -> bool: + with _parse_elf(executable) as f: + return ( + f is not None + and f.capacity == EIClass.C32 + and f.encoding == EIData.Lsb + and f.machine == EMachine.I386 + ) + + +def _have_compatible_abi(executable: str, archs: Sequence[str]) -> bool: + if "armv7l" in archs: + return _is_linux_armhf(executable) + if "i686" in archs: + return _is_linux_i686(executable) + allowed_archs = { + "x86_64", + "aarch64", + "ppc64", + "ppc64le", + "s390x", + "loongarch64", + "riscv64", + } + return any(arch in allowed_archs for arch in archs) + + +# If glibc ever changes its major version, we need to know what the last +# minor version was, so we can build the complete list of all versions. +# For now, guess what the highest minor version might be, assume it will +# be 50 for testing. Once this actually happens, update the dictionary +# with the actual value. +_LAST_GLIBC_MINOR: Dict[int, int] = collections.defaultdict(lambda: 50) + + +class _GLibCVersion(NamedTuple): + major: int + minor: int + + +def _glibc_version_string_confstr() -> 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/fcf1d003bf4f0100c/Lib/platform.py#L175-L183 + try: + # Should be a string like "glibc 2.17". + version_string: Optional[str] = os.confstr("CS_GNU_LIBC_VERSION") + assert version_string is not None + _, version = version_string.rsplit() + 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() -> 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. + # + # We must also handle the special case where the executable is not a + # dynamically linked executable. This can occur when using musl libc, + # for example. In this situation, dlopen() will error, leading to an + # OSError. Interestingly, at least in the case of musl, there is no + # errno set on the OSError. The single string argument used to construct + # OSError comes from libc itself and is therefore not portable to + # hard code here. In any case, failure to call dlopen() means we + # can proceed, so we bail on our attempt. + try: + process_namespace = ctypes.CDLL(None) + except OSError: + return 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: str = gnu_get_libc_version() + # py2 / py3 compatibility: + if not isinstance(version_str, str): + version_str = version_str.decode("ascii") + + return version_str + + +def _glibc_version_string() -> Optional[str]: + """Returns glibc version string, or None if not using glibc.""" + return _glibc_version_string_confstr() or _glibc_version_string_ctypes() + + +def _parse_glibc_version(version_str: str) -> Tuple[int, int]: + """Parse glibc 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( + f"Expected glibc version with 2 components major.minor," + f" got: {version_str}", + RuntimeWarning, + ) + return -1, -1 + return int(m.group("major")), int(m.group("minor")) + + +@functools.lru_cache() +def _get_glibc_version() -> Tuple[int, int]: + version_str = _glibc_version_string() + if version_str is None: + return (-1, -1) + return _parse_glibc_version(version_str) + + +# From PEP 513, PEP 600 +def _is_compatible(arch: str, version: _GLibCVersion) -> bool: + sys_glibc = _get_glibc_version() + if sys_glibc < version: + return False + # Check for presence of _manylinux module. + try: + import _manylinux + except ImportError: + return True + if hasattr(_manylinux, "manylinux_compatible"): + result = _manylinux.manylinux_compatible(version[0], version[1], arch) + if result is not None: + return bool(result) + return True + if version == _GLibCVersion(2, 5): + if hasattr(_manylinux, "manylinux1_compatible"): + return bool(_manylinux.manylinux1_compatible) + if version == _GLibCVersion(2, 12): + if hasattr(_manylinux, "manylinux2010_compatible"): + return bool(_manylinux.manylinux2010_compatible) + if version == _GLibCVersion(2, 17): + if hasattr(_manylinux, "manylinux2014_compatible"): + return bool(_manylinux.manylinux2014_compatible) + return True + + +_LEGACY_MANYLINUX_MAP = { + # CentOS 7 w/ glibc 2.17 (PEP 599) + (2, 17): "manylinux2014", + # CentOS 6 w/ glibc 2.12 (PEP 571) + (2, 12): "manylinux2010", + # CentOS 5 w/ glibc 2.5 (PEP 513) + (2, 5): "manylinux1", +} + + +def platform_tags(archs: Sequence[str]) -> Iterator[str]: + """Generate manylinux tags compatible to the current platform. + + :param archs: Sequence of compatible architectures. + The first one shall be the closest to the actual architecture and be the part of + platform tag after the ``linux_`` prefix, e.g. ``x86_64``. + The ``linux_`` prefix is assumed as a prerequisite for the current platform to + be manylinux-compatible. + + :returns: An iterator of compatible manylinux tags. + """ + if not _have_compatible_abi(sys.executable, archs): + return + # Oldest glibc to be supported regardless of architecture is (2, 17). + too_old_glibc2 = _GLibCVersion(2, 16) + if set(archs) & {"x86_64", "i686"}: + # On x86/i686 also oldest glibc to be supported is (2, 5). + too_old_glibc2 = _GLibCVersion(2, 4) + current_glibc = _GLibCVersion(*_get_glibc_version()) + glibc_max_list = [current_glibc] + # We can assume compatibility across glibc major versions. + # https://sourceware.org/bugzilla/show_bug.cgi?id=24636 + # + # Build a list of maximum glibc versions so that we can + # output the canonical list of all glibc from current_glibc + # down to too_old_glibc2, including all intermediary versions. + for glibc_major in range(current_glibc.major - 1, 1, -1): + glibc_minor = _LAST_GLIBC_MINOR[glibc_major] + glibc_max_list.append(_GLibCVersion(glibc_major, glibc_minor)) + for arch in archs: + for glibc_max in glibc_max_list: + if glibc_max.major == too_old_glibc2.major: + min_minor = too_old_glibc2.minor + else: + # For other glibc major versions oldest supported is (x, 0). + min_minor = -1 + for glibc_minor in range(glibc_max.minor, min_minor, -1): + glibc_version = _GLibCVersion(glibc_max.major, glibc_minor) + tag = "manylinux_{}_{}".format(*glibc_version) + if _is_compatible(arch, glibc_version): + yield f"{tag}_{arch}" + # Handle the legacy manylinux1, manylinux2010, manylinux2014 tags. + if glibc_version in _LEGACY_MANYLINUX_MAP: + legacy_tag = _LEGACY_MANYLINUX_MAP[glibc_version] + if _is_compatible(arch, glibc_version): + yield f"{legacy_tag}_{arch}" diff --git a/venv/Lib/site-packages/setuptools/_vendor/packaging/_musllinux.py b/venv/Lib/site-packages/setuptools/_vendor/packaging/_musllinux.py new file mode 100644 index 0000000..86419df --- /dev/null +++ b/venv/Lib/site-packages/setuptools/_vendor/packaging/_musllinux.py @@ -0,0 +1,83 @@ +"""PEP 656 support. + +This module implements logic to detect if the currently running Python is +linked against musl, and what musl version is used. +""" + +import functools +import re +import subprocess +import sys +from typing import Iterator, NamedTuple, Optional, Sequence + +from ._elffile import ELFFile + + +class _MuslVersion(NamedTuple): + major: int + minor: int + + +def _parse_musl_version(output: str) -> Optional[_MuslVersion]: + lines = [n for n in (n.strip() for n in output.splitlines()) if n] + if len(lines) < 2 or lines[0][:4] != "musl": + return None + m = re.match(r"Version (\d+)\.(\d+)", lines[1]) + if not m: + return None + return _MuslVersion(major=int(m.group(1)), minor=int(m.group(2))) + + +@functools.lru_cache() +def _get_musl_version(executable: str) -> Optional[_MuslVersion]: + """Detect currently-running musl runtime version. + + This is done by checking the specified executable's dynamic linking + information, and invoking the loader to parse its output for a version + string. If the loader is musl, the output would be something like:: + + musl libc (x86_64) + Version 1.2.2 + Dynamic Program Loader + """ + try: + with open(executable, "rb") as f: + ld = ELFFile(f).interpreter + except (OSError, TypeError, ValueError): + return None + if ld is None or "musl" not in ld: + return None + proc = subprocess.run([ld], stderr=subprocess.PIPE, text=True) + return _parse_musl_version(proc.stderr) + + +def platform_tags(archs: Sequence[str]) -> Iterator[str]: + """Generate musllinux tags compatible to the current platform. + + :param archs: Sequence of compatible architectures. + The first one shall be the closest to the actual architecture and be the part of + platform tag after the ``linux_`` prefix, e.g. ``x86_64``. + The ``linux_`` prefix is assumed as a prerequisite for the current platform to + be musllinux-compatible. + + :returns: An iterator of compatible musllinux tags. + """ + sys_musl = _get_musl_version(sys.executable) + if sys_musl is None: # Python not dynamically linked against musl. + return + for arch in archs: + for minor in range(sys_musl.minor, -1, -1): + yield f"musllinux_{sys_musl.major}_{minor}_{arch}" + + +if __name__ == "__main__": # pragma: no cover + import sysconfig + + plat = sysconfig.get_platform() + assert plat.startswith("linux-"), "not linux" + + print("plat:", plat) + print("musl:", _get_musl_version(sys.executable)) + print("tags:", end=" ") + for t in platform_tags(re.sub(r"[.-]", "_", plat.split("-", 1)[-1])): + print(t, end="\n ") diff --git a/venv/Lib/site-packages/setuptools/_vendor/packaging/_parser.py b/venv/Lib/site-packages/setuptools/_vendor/packaging/_parser.py new file mode 100644 index 0000000..684df75 --- /dev/null +++ b/venv/Lib/site-packages/setuptools/_vendor/packaging/_parser.py @@ -0,0 +1,356 @@ +"""Handwritten parser of dependency specifiers. + +The docstring for each __parse_* function contains ENBF-inspired grammar representing +the implementation. +""" + +import ast +from typing import Any, List, NamedTuple, Optional, Tuple, Union + +from ._tokenizer import DEFAULT_RULES, Tokenizer + + +class Node: + def __init__(self, value: str) -> None: + self.value = value + + def __str__(self) -> str: + return self.value + + def __repr__(self) -> str: + return f"<{self.__class__.__name__}('{self}')>" + + def serialize(self) -> str: + raise NotImplementedError + + +class Variable(Node): + def serialize(self) -> str: + return str(self) + + +class Value(Node): + def serialize(self) -> str: + return f'"{self}"' + + +class Op(Node): + def serialize(self) -> str: + return str(self) + + +MarkerVar = Union[Variable, Value] +MarkerItem = Tuple[MarkerVar, Op, MarkerVar] +# MarkerAtom = Union[MarkerItem, List["MarkerAtom"]] +# MarkerList = List[Union["MarkerList", MarkerAtom, str]] +# mypy does not support recursive type definition +# https://github.com/python/mypy/issues/731 +MarkerAtom = Any +MarkerList = List[Any] + + +class ParsedRequirement(NamedTuple): + name: str + url: str + extras: List[str] + specifier: str + marker: Optional[MarkerList] + + +# -------------------------------------------------------------------------------------- +# Recursive descent parser for dependency specifier +# -------------------------------------------------------------------------------------- +def parse_requirement(source: str) -> ParsedRequirement: + return _parse_requirement(Tokenizer(source, rules=DEFAULT_RULES)) + + +def _parse_requirement(tokenizer: Tokenizer) -> ParsedRequirement: + """ + requirement = WS? IDENTIFIER WS? extras WS? requirement_details + """ + tokenizer.consume("WS") + + name_token = tokenizer.expect( + "IDENTIFIER", expected="package name at the start of dependency specifier" + ) + name = name_token.text + tokenizer.consume("WS") + + extras = _parse_extras(tokenizer) + tokenizer.consume("WS") + + url, specifier, marker = _parse_requirement_details(tokenizer) + tokenizer.expect("END", expected="end of dependency specifier") + + return ParsedRequirement(name, url, extras, specifier, marker) + + +def _parse_requirement_details( + tokenizer: Tokenizer, +) -> Tuple[str, str, Optional[MarkerList]]: + """ + requirement_details = AT URL (WS requirement_marker?)? + | specifier WS? (requirement_marker)? + """ + + specifier = "" + url = "" + marker = None + + if tokenizer.check("AT"): + tokenizer.read() + tokenizer.consume("WS") + + url_start = tokenizer.position + url = tokenizer.expect("URL", expected="URL after @").text + if tokenizer.check("END", peek=True): + return (url, specifier, marker) + + tokenizer.expect("WS", expected="whitespace after URL") + + # The input might end after whitespace. + if tokenizer.check("END", peek=True): + return (url, specifier, marker) + + marker = _parse_requirement_marker( + tokenizer, span_start=url_start, after="URL and whitespace" + ) + else: + specifier_start = tokenizer.position + specifier = _parse_specifier(tokenizer) + tokenizer.consume("WS") + + if tokenizer.check("END", peek=True): + return (url, specifier, marker) + + marker = _parse_requirement_marker( + tokenizer, + span_start=specifier_start, + after=( + "version specifier" + if specifier + else "name and no valid version specifier" + ), + ) + + return (url, specifier, marker) + + +def _parse_requirement_marker( + tokenizer: Tokenizer, *, span_start: int, after: str +) -> MarkerList: + """ + requirement_marker = SEMICOLON marker WS? + """ + + if not tokenizer.check("SEMICOLON"): + tokenizer.raise_syntax_error( + f"Expected end or semicolon (after {after})", + span_start=span_start, + ) + tokenizer.read() + + marker = _parse_marker(tokenizer) + tokenizer.consume("WS") + + return marker + + +def _parse_extras(tokenizer: Tokenizer) -> List[str]: + """ + extras = (LEFT_BRACKET wsp* extras_list? wsp* RIGHT_BRACKET)? + """ + if not tokenizer.check("LEFT_BRACKET", peek=True): + return [] + + with tokenizer.enclosing_tokens( + "LEFT_BRACKET", + "RIGHT_BRACKET", + around="extras", + ): + tokenizer.consume("WS") + extras = _parse_extras_list(tokenizer) + tokenizer.consume("WS") + + return extras + + +def _parse_extras_list(tokenizer: Tokenizer) -> List[str]: + """ + extras_list = identifier (wsp* ',' wsp* identifier)* + """ + extras: List[str] = [] + + if not tokenizer.check("IDENTIFIER"): + return extras + + extras.append(tokenizer.read().text) + + while True: + tokenizer.consume("WS") + if tokenizer.check("IDENTIFIER", peek=True): + tokenizer.raise_syntax_error("Expected comma between extra names") + elif not tokenizer.check("COMMA"): + break + + tokenizer.read() + tokenizer.consume("WS") + + extra_token = tokenizer.expect("IDENTIFIER", expected="extra name after comma") + extras.append(extra_token.text) + + return extras + + +def _parse_specifier(tokenizer: Tokenizer) -> str: + """ + specifier = LEFT_PARENTHESIS WS? version_many WS? RIGHT_PARENTHESIS + | WS? version_many WS? + """ + with tokenizer.enclosing_tokens( + "LEFT_PARENTHESIS", + "RIGHT_PARENTHESIS", + around="version specifier", + ): + tokenizer.consume("WS") + parsed_specifiers = _parse_version_many(tokenizer) + tokenizer.consume("WS") + + return parsed_specifiers + + +def _parse_version_many(tokenizer: Tokenizer) -> str: + """ + version_many = (SPECIFIER (WS? COMMA WS? SPECIFIER)*)? + """ + parsed_specifiers = "" + while tokenizer.check("SPECIFIER"): + span_start = tokenizer.position + parsed_specifiers += tokenizer.read().text + if tokenizer.check("VERSION_PREFIX_TRAIL", peek=True): + tokenizer.raise_syntax_error( + ".* suffix can only be used with `==` or `!=` operators", + span_start=span_start, + span_end=tokenizer.position + 1, + ) + if tokenizer.check("VERSION_LOCAL_LABEL_TRAIL", peek=True): + tokenizer.raise_syntax_error( + "Local version label can only be used with `==` or `!=` operators", + span_start=span_start, + span_end=tokenizer.position, + ) + tokenizer.consume("WS") + if not tokenizer.check("COMMA"): + break + parsed_specifiers += tokenizer.read().text + tokenizer.consume("WS") + + return parsed_specifiers + + +# -------------------------------------------------------------------------------------- +# Recursive descent parser for marker expression +# -------------------------------------------------------------------------------------- +def parse_marker(source: str) -> MarkerList: + return _parse_full_marker(Tokenizer(source, rules=DEFAULT_RULES)) + + +def _parse_full_marker(tokenizer: Tokenizer) -> MarkerList: + retval = _parse_marker(tokenizer) + tokenizer.expect("END", expected="end of marker expression") + return retval + + +def _parse_marker(tokenizer: Tokenizer) -> MarkerList: + """ + marker = marker_atom (BOOLOP marker_atom)+ + """ + expression = [_parse_marker_atom(tokenizer)] + while tokenizer.check("BOOLOP"): + token = tokenizer.read() + expr_right = _parse_marker_atom(tokenizer) + expression.extend((token.text, expr_right)) + return expression + + +def _parse_marker_atom(tokenizer: Tokenizer) -> MarkerAtom: + """ + marker_atom = WS? LEFT_PARENTHESIS WS? marker WS? RIGHT_PARENTHESIS WS? + | WS? marker_item WS? + """ + + tokenizer.consume("WS") + if tokenizer.check("LEFT_PARENTHESIS", peek=True): + with tokenizer.enclosing_tokens( + "LEFT_PARENTHESIS", + "RIGHT_PARENTHESIS", + around="marker expression", + ): + tokenizer.consume("WS") + marker: MarkerAtom = _parse_marker(tokenizer) + tokenizer.consume("WS") + else: + marker = _parse_marker_item(tokenizer) + tokenizer.consume("WS") + return marker + + +def _parse_marker_item(tokenizer: Tokenizer) -> MarkerItem: + """ + marker_item = WS? marker_var WS? marker_op WS? marker_var WS? + """ + tokenizer.consume("WS") + marker_var_left = _parse_marker_var(tokenizer) + tokenizer.consume("WS") + marker_op = _parse_marker_op(tokenizer) + tokenizer.consume("WS") + marker_var_right = _parse_marker_var(tokenizer) + tokenizer.consume("WS") + return (marker_var_left, marker_op, marker_var_right) + + +def _parse_marker_var(tokenizer: Tokenizer) -> MarkerVar: + """ + marker_var = VARIABLE | QUOTED_STRING + """ + if tokenizer.check("VARIABLE"): + return process_env_var(tokenizer.read().text.replace(".", "_")) + elif tokenizer.check("QUOTED_STRING"): + return process_python_str(tokenizer.read().text) + else: + tokenizer.raise_syntax_error( + message="Expected a marker variable or quoted string" + ) + + +def process_env_var(env_var: str) -> Variable: + if env_var in ("platform_python_implementation", "python_implementation"): + return Variable("platform_python_implementation") + else: + return Variable(env_var) + + +def process_python_str(python_str: str) -> Value: + value = ast.literal_eval(python_str) + return Value(str(value)) + + +def _parse_marker_op(tokenizer: Tokenizer) -> Op: + """ + marker_op = IN | NOT IN | OP + """ + if tokenizer.check("IN"): + tokenizer.read() + return Op("in") + elif tokenizer.check("NOT"): + tokenizer.read() + tokenizer.expect("WS", expected="whitespace after 'not'") + tokenizer.expect("IN", expected="'in' after 'not'") + return Op("not in") + elif tokenizer.check("OP"): + return Op(tokenizer.read().text) + else: + return tokenizer.raise_syntax_error( + "Expected marker operator, one of " + "<=, <, !=, ==, >=, >, ~=, ===, in, not in" + ) diff --git a/venv/Lib/site-packages/setuptools/_vendor/packaging/_structures.py b/venv/Lib/site-packages/setuptools/_vendor/packaging/_structures.py new file mode 100644 index 0000000..90a6465 --- /dev/null +++ b/venv/Lib/site-packages/setuptools/_vendor/packaging/_structures.py @@ -0,0 +1,61 @@ +# 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. + + +class InfinityType: + def __repr__(self) -> str: + return "Infinity" + + def __hash__(self) -> int: + return hash(repr(self)) + + def __lt__(self, other: object) -> bool: + return False + + def __le__(self, other: object) -> bool: + return False + + def __eq__(self, other: object) -> bool: + return isinstance(other, self.__class__) + + def __gt__(self, other: object) -> bool: + return True + + def __ge__(self, other: object) -> bool: + return True + + def __neg__(self: object) -> "NegativeInfinityType": + return NegativeInfinity + + +Infinity = InfinityType() + + +class NegativeInfinityType: + def __repr__(self) -> str: + return "-Infinity" + + def __hash__(self) -> int: + return hash(repr(self)) + + def __lt__(self, other: object) -> bool: + return True + + def __le__(self, other: object) -> bool: + return True + + def __eq__(self, other: object) -> bool: + return isinstance(other, self.__class__) + + def __gt__(self, other: object) -> bool: + return False + + def __ge__(self, other: object) -> bool: + return False + + def __neg__(self: object) -> InfinityType: + return Infinity + + +NegativeInfinity = NegativeInfinityType() diff --git a/venv/Lib/site-packages/setuptools/_vendor/packaging/_tokenizer.py b/venv/Lib/site-packages/setuptools/_vendor/packaging/_tokenizer.py new file mode 100644 index 0000000..dd0d648 --- /dev/null +++ b/venv/Lib/site-packages/setuptools/_vendor/packaging/_tokenizer.py @@ -0,0 +1,192 @@ +import contextlib +import re +from dataclasses import dataclass +from typing import Dict, Iterator, NoReturn, Optional, Tuple, Union + +from .specifiers import Specifier + + +@dataclass +class Token: + name: str + text: str + position: int + + +class ParserSyntaxError(Exception): + """The provided source text could not be parsed correctly.""" + + def __init__( + self, + message: str, + *, + source: str, + span: Tuple[int, int], + ) -> None: + self.span = span + self.message = message + self.source = source + + super().__init__() + + def __str__(self) -> str: + marker = " " * self.span[0] + "~" * (self.span[1] - self.span[0]) + "^" + return "\n ".join([self.message, self.source, marker]) + + +DEFAULT_RULES: "Dict[str, Union[str, re.Pattern[str]]]" = { + "LEFT_PARENTHESIS": r"\(", + "RIGHT_PARENTHESIS": r"\)", + "LEFT_BRACKET": r"\[", + "RIGHT_BRACKET": r"\]", + "SEMICOLON": r";", + "COMMA": r",", + "QUOTED_STRING": re.compile( + r""" + ( + ('[^']*') + | + ("[^"]*") + ) + """, + re.VERBOSE, + ), + "OP": r"(===|==|~=|!=|<=|>=|<|>)", + "BOOLOP": r"\b(or|and)\b", + "IN": r"\bin\b", + "NOT": r"\bnot\b", + "VARIABLE": re.compile( + r""" + \b( + python_version + |python_full_version + |os[._]name + |sys[._]platform + |platform_(release|system) + |platform[._](version|machine|python_implementation) + |python_implementation + |implementation_(name|version) + |extra + )\b + """, + re.VERBOSE, + ), + "SPECIFIER": re.compile( + Specifier._operator_regex_str + Specifier._version_regex_str, + re.VERBOSE | re.IGNORECASE, + ), + "AT": r"\@", + "URL": r"[^ \t]+", + "IDENTIFIER": r"\b[a-zA-Z0-9][a-zA-Z0-9._-]*\b", + "VERSION_PREFIX_TRAIL": r"\.\*", + "VERSION_LOCAL_LABEL_TRAIL": r"\+[a-z0-9]+(?:[-_\.][a-z0-9]+)*", + "WS": r"[ \t]+", + "END": r"$", +} + + +class Tokenizer: + """Context-sensitive token parsing. + + Provides methods to examine the input stream to check whether the next token + matches. + """ + + def __init__( + self, + source: str, + *, + rules: "Dict[str, Union[str, re.Pattern[str]]]", + ) -> None: + self.source = source + self.rules: Dict[str, re.Pattern[str]] = { + name: re.compile(pattern) for name, pattern in rules.items() + } + self.next_token: Optional[Token] = None + self.position = 0 + + def consume(self, name: str) -> None: + """Move beyond provided token name, if at current position.""" + if self.check(name): + self.read() + + def check(self, name: str, *, peek: bool = False) -> bool: + """Check whether the next token has the provided name. + + By default, if the check succeeds, the token *must* be read before + another check. If `peek` is set to `True`, the token is not loaded and + would need to be checked again. + """ + assert ( + self.next_token is None + ), f"Cannot check for {name!r}, already have {self.next_token!r}" + assert name in self.rules, f"Unknown token name: {name!r}" + + expression = self.rules[name] + + match = expression.match(self.source, self.position) + if match is None: + return False + if not peek: + self.next_token = Token(name, match[0], self.position) + return True + + def expect(self, name: str, *, expected: str) -> Token: + """Expect a certain token name next, failing with a syntax error otherwise. + + The token is *not* read. + """ + if not self.check(name): + raise self.raise_syntax_error(f"Expected {expected}") + return self.read() + + def read(self) -> Token: + """Consume the next token and return it.""" + token = self.next_token + assert token is not None + + self.position += len(token.text) + self.next_token = None + + return token + + def raise_syntax_error( + self, + message: str, + *, + span_start: Optional[int] = None, + span_end: Optional[int] = None, + ) -> NoReturn: + """Raise ParserSyntaxError at the given position.""" + span = ( + self.position if span_start is None else span_start, + self.position if span_end is None else span_end, + ) + raise ParserSyntaxError( + message, + source=self.source, + span=span, + ) + + @contextlib.contextmanager + def enclosing_tokens( + self, open_token: str, close_token: str, *, around: str + ) -> Iterator[None]: + if self.check(open_token): + open_position = self.position + self.read() + else: + open_position = None + + yield + + if open_position is None: + return + + if not self.check(close_token): + self.raise_syntax_error( + f"Expected matching {close_token} for {open_token}, after {around}", + span_start=open_position, + ) + + self.read() diff --git a/venv/Lib/site-packages/setuptools/_vendor/packaging/markers.py b/venv/Lib/site-packages/setuptools/_vendor/packaging/markers.py new file mode 100644 index 0000000..8b98fca --- /dev/null +++ b/venv/Lib/site-packages/setuptools/_vendor/packaging/markers.py @@ -0,0 +1,252 @@ +# 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. + +import operator +import os +import platform +import sys +from typing import Any, Callable, Dict, List, Optional, Tuple, Union + +from ._parser import ( + MarkerAtom, + MarkerList, + Op, + Value, + Variable, + parse_marker as _parse_marker, +) +from ._tokenizer import ParserSyntaxError +from .specifiers import InvalidSpecifier, Specifier +from .utils import canonicalize_name + +__all__ = [ + "InvalidMarker", + "UndefinedComparison", + "UndefinedEnvironmentName", + "Marker", + "default_environment", +] + +Operator = Callable[[str, str], bool] + + +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. + """ + + +def _normalize_extra_values(results: Any) -> Any: + """ + Normalize extra values. + """ + if isinstance(results[0], tuple): + lhs, op, rhs = results[0] + if isinstance(lhs, Variable) and lhs.value == "extra": + normalized_extra = canonicalize_name(rhs.value) + rhs = Value(normalized_extra) + elif isinstance(rhs, Variable) and rhs.value == "extra": + normalized_extra = canonicalize_name(lhs.value) + lhs = Value(normalized_extra) + results[0] = lhs, op, rhs + return results + + +def _format_marker( + marker: Union[List[str], MarkerAtom, str], first: Optional[bool] = True +) -> str: + + assert isinstance(marker, (list, tuple, str)) + + # 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: Dict[str, Operator] = { + "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: str, op: Op, rhs: str) -> bool: + try: + spec = Specifier("".join([op.serialize(), rhs])) + except InvalidSpecifier: + pass + else: + return spec.contains(lhs, prereleases=True) + + oper: Optional[Operator] = _operators.get(op.serialize()) + if oper is None: + raise UndefinedComparison(f"Undefined {op!r} on {lhs!r} and {rhs!r}.") + + return oper(lhs, rhs) + + +def _normalize(*values: str, key: str) -> Tuple[str, ...]: + # PEP 685 – Comparison of extra names for optional distribution dependencies + # https://peps.python.org/pep-0685/ + # > When comparing extra names, tools MUST normalize the names being + # > compared using the semantics outlined in PEP 503 for names + if key == "extra": + return tuple(canonicalize_name(v) for v in values) + + # other environment markers don't have such standards + return values + + +def _evaluate_markers(markers: MarkerList, environment: Dict[str, str]) -> bool: + groups: List[List[bool]] = [[]] + + for marker in markers: + assert isinstance(marker, (list, tuple, str)) + + if isinstance(marker, list): + groups[-1].append(_evaluate_markers(marker, environment)) + elif isinstance(marker, tuple): + lhs, op, rhs = marker + + if isinstance(lhs, Variable): + environment_key = lhs.value + lhs_value = environment[environment_key] + rhs_value = rhs.value + else: + lhs_value = lhs.value + environment_key = rhs.value + rhs_value = environment[environment_key] + + lhs_value, rhs_value = _normalize(lhs_value, rhs_value, key=environment_key) + 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: "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() -> Dict[str, str]: + iver = format_full_version(sys.implementation.version) + implementation_name = sys.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: + def __init__(self, marker: str) -> None: + # Note: We create a Marker object without calling this constructor in + # packaging.requirements.Requirement. If any additional logic is + # added here, make sure to mirror/adapt Requirement. + try: + self._markers = _normalize_extra_values(_parse_marker(marker)) + # The attribute `_markers` can be described in terms of a recursive type: + # MarkerList = List[Union[Tuple[Node, ...], str, MarkerList]] + # + # For example, the following expression: + # python_version > "3.6" or (python_version == "3.6" and os_name == "unix") + # + # is parsed into: + # [ + # (, ')>, ), + # 'and', + # [ + # (, , ), + # 'or', + # (, , ) + # ] + # ] + except ParserSyntaxError as e: + raise InvalidMarker(str(e)) from e + + def __str__(self) -> str: + return _format_marker(self._markers) + + def __repr__(self) -> str: + return f"" + + def __hash__(self) -> int: + return hash((self.__class__.__name__, str(self))) + + def __eq__(self, other: Any) -> bool: + if not isinstance(other, Marker): + return NotImplemented + + return str(self) == str(other) + + def evaluate(self, environment: Optional[Dict[str, str]] = None) -> 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() + current_environment["extra"] = "" + if environment is not None: + current_environment.update(environment) + # The API used to allow setting extra to None. We need to handle this + # case for backwards compatibility. + if current_environment["extra"] is None: + current_environment["extra"] = "" + + return _evaluate_markers(self._markers, current_environment) diff --git a/venv/Lib/site-packages/setuptools/_vendor/packaging/metadata.py b/venv/Lib/site-packages/setuptools/_vendor/packaging/metadata.py new file mode 100644 index 0000000..fb27493 --- /dev/null +++ b/venv/Lib/site-packages/setuptools/_vendor/packaging/metadata.py @@ -0,0 +1,825 @@ +import email.feedparser +import email.header +import email.message +import email.parser +import email.policy +import sys +import typing +from typing import ( + Any, + Callable, + Dict, + Generic, + List, + Optional, + Tuple, + Type, + Union, + cast, +) + +from . import requirements, specifiers, utils, version as version_module + +T = typing.TypeVar("T") +if sys.version_info[:2] >= (3, 8): # pragma: no cover + from typing import Literal, TypedDict +else: # pragma: no cover + if typing.TYPE_CHECKING: + from typing_extensions import Literal, TypedDict + else: + try: + from typing_extensions import Literal, TypedDict + except ImportError: + + class Literal: + def __init_subclass__(*_args, **_kwargs): + pass + + class TypedDict: + def __init_subclass__(*_args, **_kwargs): + pass + + +try: + ExceptionGroup +except NameError: # pragma: no cover + + class ExceptionGroup(Exception): # noqa: N818 + """A minimal implementation of :external:exc:`ExceptionGroup` from Python 3.11. + + If :external:exc:`ExceptionGroup` is already defined by Python itself, + that version is used instead. + """ + + message: str + exceptions: List[Exception] + + def __init__(self, message: str, exceptions: List[Exception]) -> None: + self.message = message + self.exceptions = exceptions + + def __repr__(self) -> str: + return f"{self.__class__.__name__}({self.message!r}, {self.exceptions!r})" + +else: # pragma: no cover + ExceptionGroup = ExceptionGroup + + +class InvalidMetadata(ValueError): + """A metadata field contains invalid data.""" + + field: str + """The name of the field that contains invalid data.""" + + def __init__(self, field: str, message: str) -> None: + self.field = field + super().__init__(message) + + +# The RawMetadata class attempts to make as few assumptions about the underlying +# serialization formats as possible. The idea is that as long as a serialization +# formats offer some very basic primitives in *some* way then we can support +# serializing to and from that format. +class RawMetadata(TypedDict, total=False): + """A dictionary of raw core metadata. + + Each field in core metadata maps to a key of this dictionary (when data is + provided). The key is lower-case and underscores are used instead of dashes + compared to the equivalent core metadata field. Any core metadata field that + can be specified multiple times or can hold multiple values in a single + field have a key with a plural name. See :class:`Metadata` whose attributes + match the keys of this dictionary. + + Core metadata fields that can be specified multiple times are stored as a + list or dict depending on which is appropriate for the field. Any fields + which hold multiple values in a single field are stored as a list. + + """ + + # Metadata 1.0 - PEP 241 + metadata_version: str + name: str + version: str + platforms: List[str] + summary: str + description: str + keywords: List[str] + home_page: str + author: str + author_email: str + license: str + + # Metadata 1.1 - PEP 314 + supported_platforms: List[str] + download_url: str + classifiers: List[str] + requires: List[str] + provides: List[str] + obsoletes: List[str] + + # Metadata 1.2 - PEP 345 + maintainer: str + maintainer_email: str + requires_dist: List[str] + provides_dist: List[str] + obsoletes_dist: List[str] + requires_python: str + requires_external: List[str] + project_urls: Dict[str, str] + + # Metadata 2.0 + # PEP 426 attempted to completely revamp the metadata format + # but got stuck without ever being able to build consensus on + # it and ultimately ended up withdrawn. + # + # However, a number of tools had started emitting METADATA with + # `2.0` Metadata-Version, so for historical reasons, this version + # was skipped. + + # Metadata 2.1 - PEP 566 + description_content_type: str + provides_extra: List[str] + + # Metadata 2.2 - PEP 643 + dynamic: List[str] + + # Metadata 2.3 - PEP 685 + # No new fields were added in PEP 685, just some edge case were + # tightened up to provide better interoptability. + + +_STRING_FIELDS = { + "author", + "author_email", + "description", + "description_content_type", + "download_url", + "home_page", + "license", + "maintainer", + "maintainer_email", + "metadata_version", + "name", + "requires_python", + "summary", + "version", +} + +_LIST_FIELDS = { + "classifiers", + "dynamic", + "obsoletes", + "obsoletes_dist", + "platforms", + "provides", + "provides_dist", + "provides_extra", + "requires", + "requires_dist", + "requires_external", + "supported_platforms", +} + +_DICT_FIELDS = { + "project_urls", +} + + +def _parse_keywords(data: str) -> List[str]: + """Split a string of comma-separate keyboards into a list of keywords.""" + return [k.strip() for k in data.split(",")] + + +def _parse_project_urls(data: List[str]) -> Dict[str, str]: + """Parse a list of label/URL string pairings separated by a comma.""" + urls = {} + for pair in data: + # Our logic is slightly tricky here as we want to try and do + # *something* reasonable with malformed data. + # + # The main thing that we have to worry about, is data that does + # not have a ',' at all to split the label from the Value. There + # isn't a singular right answer here, and we will fail validation + # later on (if the caller is validating) so it doesn't *really* + # matter, but since the missing value has to be an empty str + # and our return value is dict[str, str], if we let the key + # be the missing value, then they'd have multiple '' values that + # overwrite each other in a accumulating dict. + # + # The other potentional issue is that it's possible to have the + # same label multiple times in the metadata, with no solid "right" + # answer with what to do in that case. As such, we'll do the only + # thing we can, which is treat the field as unparseable and add it + # to our list of unparsed fields. + parts = [p.strip() for p in pair.split(",", 1)] + parts.extend([""] * (max(0, 2 - len(parts)))) # Ensure 2 items + + # TODO: The spec doesn't say anything about if the keys should be + # considered case sensitive or not... logically they should + # be case-preserving and case-insensitive, but doing that + # would open up more cases where we might have duplicate + # entries. + label, url = parts + if label in urls: + # The label already exists in our set of urls, so this field + # is unparseable, and we can just add the whole thing to our + # unparseable data and stop processing it. + raise KeyError("duplicate labels in project urls") + urls[label] = url + + return urls + + +def _get_payload(msg: email.message.Message, source: Union[bytes, str]) -> str: + """Get the body of the message.""" + # If our source is a str, then our caller has managed encodings for us, + # and we don't need to deal with it. + if isinstance(source, str): + payload: str = msg.get_payload() + return payload + # If our source is a bytes, then we're managing the encoding and we need + # to deal with it. + else: + bpayload: bytes = msg.get_payload(decode=True) + try: + return bpayload.decode("utf8", "strict") + except UnicodeDecodeError: + raise ValueError("payload in an invalid encoding") + + +# The various parse_FORMAT functions here are intended to be as lenient as +# possible in their parsing, while still returning a correctly typed +# RawMetadata. +# +# To aid in this, we also generally want to do as little touching of the +# data as possible, except where there are possibly some historic holdovers +# that make valid data awkward to work with. +# +# While this is a lower level, intermediate format than our ``Metadata`` +# class, some light touch ups can make a massive difference in usability. + +# Map METADATA fields to RawMetadata. +_EMAIL_TO_RAW_MAPPING = { + "author": "author", + "author-email": "author_email", + "classifier": "classifiers", + "description": "description", + "description-content-type": "description_content_type", + "download-url": "download_url", + "dynamic": "dynamic", + "home-page": "home_page", + "keywords": "keywords", + "license": "license", + "maintainer": "maintainer", + "maintainer-email": "maintainer_email", + "metadata-version": "metadata_version", + "name": "name", + "obsoletes": "obsoletes", + "obsoletes-dist": "obsoletes_dist", + "platform": "platforms", + "project-url": "project_urls", + "provides": "provides", + "provides-dist": "provides_dist", + "provides-extra": "provides_extra", + "requires": "requires", + "requires-dist": "requires_dist", + "requires-external": "requires_external", + "requires-python": "requires_python", + "summary": "summary", + "supported-platform": "supported_platforms", + "version": "version", +} +_RAW_TO_EMAIL_MAPPING = {raw: email for email, raw in _EMAIL_TO_RAW_MAPPING.items()} + + +def parse_email(data: Union[bytes, str]) -> Tuple[RawMetadata, Dict[str, List[str]]]: + """Parse a distribution's metadata stored as email headers (e.g. from ``METADATA``). + + This function returns a two-item tuple of dicts. The first dict is of + recognized fields from the core metadata specification. Fields that can be + parsed and translated into Python's built-in types are converted + appropriately. All other fields are left as-is. Fields that are allowed to + appear multiple times are stored as lists. + + The second dict contains all other fields from the metadata. This includes + any unrecognized fields. It also includes any fields which are expected to + be parsed into a built-in type but were not formatted appropriately. Finally, + any fields that are expected to appear only once but are repeated are + included in this dict. + + """ + raw: Dict[str, Union[str, List[str], Dict[str, str]]] = {} + unparsed: Dict[str, List[str]] = {} + + if isinstance(data, str): + parsed = email.parser.Parser(policy=email.policy.compat32).parsestr(data) + else: + parsed = email.parser.BytesParser(policy=email.policy.compat32).parsebytes(data) + + # We have to wrap parsed.keys() in a set, because in the case of multiple + # values for a key (a list), the key will appear multiple times in the + # list of keys, but we're avoiding that by using get_all(). + for name in frozenset(parsed.keys()): + # Header names in RFC are case insensitive, so we'll normalize to all + # lower case to make comparisons easier. + name = name.lower() + + # We use get_all() here, even for fields that aren't multiple use, + # because otherwise someone could have e.g. two Name fields, and we + # would just silently ignore it rather than doing something about it. + headers = parsed.get_all(name) or [] + + # The way the email module works when parsing bytes is that it + # unconditionally decodes the bytes as ascii using the surrogateescape + # handler. When you pull that data back out (such as with get_all() ), + # it looks to see if the str has any surrogate escapes, and if it does + # it wraps it in a Header object instead of returning the string. + # + # As such, we'll look for those Header objects, and fix up the encoding. + value = [] + # Flag if we have run into any issues processing the headers, thus + # signalling that the data belongs in 'unparsed'. + valid_encoding = True + for h in headers: + # It's unclear if this can return more types than just a Header or + # a str, so we'll just assert here to make sure. + assert isinstance(h, (email.header.Header, str)) + + # If it's a header object, we need to do our little dance to get + # the real data out of it. In cases where there is invalid data + # we're going to end up with mojibake, but there's no obvious, good + # way around that without reimplementing parts of the Header object + # ourselves. + # + # That should be fine since, if mojibacked happens, this key is + # going into the unparsed dict anyways. + if isinstance(h, email.header.Header): + # The Header object stores it's data as chunks, and each chunk + # can be independently encoded, so we'll need to check each + # of them. + chunks: List[Tuple[bytes, Optional[str]]] = [] + for bin, encoding in email.header.decode_header(h): + try: + bin.decode("utf8", "strict") + except UnicodeDecodeError: + # Enable mojibake. + encoding = "latin1" + valid_encoding = False + else: + encoding = "utf8" + chunks.append((bin, encoding)) + + # Turn our chunks back into a Header object, then let that + # Header object do the right thing to turn them into a + # string for us. + value.append(str(email.header.make_header(chunks))) + # This is already a string, so just add it. + else: + value.append(h) + + # We've processed all of our values to get them into a list of str, + # but we may have mojibake data, in which case this is an unparsed + # field. + if not valid_encoding: + unparsed[name] = value + continue + + raw_name = _EMAIL_TO_RAW_MAPPING.get(name) + if raw_name is None: + # This is a bit of a weird situation, we've encountered a key that + # we don't know what it means, so we don't know whether it's meant + # to be a list or not. + # + # Since we can't really tell one way or another, we'll just leave it + # as a list, even though it may be a single item list, because that's + # what makes the most sense for email headers. + unparsed[name] = value + continue + + # If this is one of our string fields, then we'll check to see if our + # value is a list of a single item. If it is then we'll assume that + # it was emitted as a single string, and unwrap the str from inside + # the list. + # + # If it's any other kind of data, then we haven't the faintest clue + # what we should parse it as, and we have to just add it to our list + # of unparsed stuff. + if raw_name in _STRING_FIELDS and len(value) == 1: + raw[raw_name] = value[0] + # If this is one of our list of string fields, then we can just assign + # the value, since email *only* has strings, and our get_all() call + # above ensures that this is a list. + elif raw_name in _LIST_FIELDS: + raw[raw_name] = value + # Special Case: Keywords + # The keywords field is implemented in the metadata spec as a str, + # but it conceptually is a list of strings, and is serialized using + # ", ".join(keywords), so we'll do some light data massaging to turn + # this into what it logically is. + elif raw_name == "keywords" and len(value) == 1: + raw[raw_name] = _parse_keywords(value[0]) + # Special Case: Project-URL + # The project urls is implemented in the metadata spec as a list of + # specially-formatted strings that represent a key and a value, which + # is fundamentally a mapping, however the email format doesn't support + # mappings in a sane way, so it was crammed into a list of strings + # instead. + # + # We will do a little light data massaging to turn this into a map as + # it logically should be. + elif raw_name == "project_urls": + try: + raw[raw_name] = _parse_project_urls(value) + except KeyError: + unparsed[name] = value + # Nothing that we've done has managed to parse this, so it'll just + # throw it in our unparseable data and move on. + else: + unparsed[name] = value + + # We need to support getting the Description from the message payload in + # addition to getting it from the the headers. This does mean, though, there + # is the possibility of it being set both ways, in which case we put both + # in 'unparsed' since we don't know which is right. + try: + payload = _get_payload(parsed, data) + except ValueError: + unparsed.setdefault("description", []).append( + parsed.get_payload(decode=isinstance(data, bytes)) + ) + else: + if payload: + # Check to see if we've already got a description, if so then both + # it, and this body move to unparseable. + if "description" in raw: + description_header = cast(str, raw.pop("description")) + unparsed.setdefault("description", []).extend( + [description_header, payload] + ) + elif "description" in unparsed: + unparsed["description"].append(payload) + else: + raw["description"] = payload + + # We need to cast our `raw` to a metadata, because a TypedDict only support + # literal key names, but we're computing our key names on purpose, but the + # way this function is implemented, our `TypedDict` can only have valid key + # names. + return cast(RawMetadata, raw), unparsed + + +_NOT_FOUND = object() + + +# Keep the two values in sync. +_VALID_METADATA_VERSIONS = ["1.0", "1.1", "1.2", "2.1", "2.2", "2.3"] +_MetadataVersion = Literal["1.0", "1.1", "1.2", "2.1", "2.2", "2.3"] + +_REQUIRED_ATTRS = frozenset(["metadata_version", "name", "version"]) + + +class _Validator(Generic[T]): + """Validate a metadata field. + + All _process_*() methods correspond to a core metadata field. The method is + called with the field's raw value. If the raw value is valid it is returned + in its "enriched" form (e.g. ``version.Version`` for the ``Version`` field). + If the raw value is invalid, :exc:`InvalidMetadata` is raised (with a cause + as appropriate). + """ + + name: str + raw_name: str + added: _MetadataVersion + + def __init__( + self, + *, + added: _MetadataVersion = "1.0", + ) -> None: + self.added = added + + def __set_name__(self, _owner: "Metadata", name: str) -> None: + self.name = name + self.raw_name = _RAW_TO_EMAIL_MAPPING[name] + + def __get__(self, instance: "Metadata", _owner: Type["Metadata"]) -> T: + # With Python 3.8, the caching can be replaced with functools.cached_property(). + # No need to check the cache as attribute lookup will resolve into the + # instance's __dict__ before __get__ is called. + cache = instance.__dict__ + value = instance._raw.get(self.name) + + # To make the _process_* methods easier, we'll check if the value is None + # and if this field is NOT a required attribute, and if both of those + # things are true, we'll skip the the converter. This will mean that the + # converters never have to deal with the None union. + if self.name in _REQUIRED_ATTRS or value is not None: + try: + converter: Callable[[Any], T] = getattr(self, f"_process_{self.name}") + except AttributeError: + pass + else: + value = converter(value) + + cache[self.name] = value + try: + del instance._raw[self.name] # type: ignore[misc] + except KeyError: + pass + + return cast(T, value) + + def _invalid_metadata( + self, msg: str, cause: Optional[Exception] = None + ) -> InvalidMetadata: + exc = InvalidMetadata( + self.raw_name, msg.format_map({"field": repr(self.raw_name)}) + ) + exc.__cause__ = cause + return exc + + def _process_metadata_version(self, value: str) -> _MetadataVersion: + # Implicitly makes Metadata-Version required. + if value not in _VALID_METADATA_VERSIONS: + raise self._invalid_metadata(f"{value!r} is not a valid metadata version") + return cast(_MetadataVersion, value) + + def _process_name(self, value: str) -> str: + if not value: + raise self._invalid_metadata("{field} is a required field") + # Validate the name as a side-effect. + try: + utils.canonicalize_name(value, validate=True) + except utils.InvalidName as exc: + raise self._invalid_metadata( + f"{value!r} is invalid for {{field}}", cause=exc + ) + else: + return value + + def _process_version(self, value: str) -> version_module.Version: + if not value: + raise self._invalid_metadata("{field} is a required field") + try: + return version_module.parse(value) + except version_module.InvalidVersion as exc: + raise self._invalid_metadata( + f"{value!r} is invalid for {{field}}", cause=exc + ) + + def _process_summary(self, value: str) -> str: + """Check the field contains no newlines.""" + if "\n" in value: + raise self._invalid_metadata("{field} must be a single line") + return value + + def _process_description_content_type(self, value: str) -> str: + content_types = {"text/plain", "text/x-rst", "text/markdown"} + message = email.message.EmailMessage() + message["content-type"] = value + + content_type, parameters = ( + # Defaults to `text/plain` if parsing failed. + message.get_content_type().lower(), + message["content-type"].params, + ) + # Check if content-type is valid or defaulted to `text/plain` and thus was + # not parseable. + if content_type not in content_types or content_type not in value.lower(): + raise self._invalid_metadata( + f"{{field}} must be one of {list(content_types)}, not {value!r}" + ) + + charset = parameters.get("charset", "UTF-8") + if charset != "UTF-8": + raise self._invalid_metadata( + f"{{field}} can only specify the UTF-8 charset, not {list(charset)}" + ) + + markdown_variants = {"GFM", "CommonMark"} + variant = parameters.get("variant", "GFM") # Use an acceptable default. + if content_type == "text/markdown" and variant not in markdown_variants: + raise self._invalid_metadata( + f"valid Markdown variants for {{field}} are {list(markdown_variants)}, " + f"not {variant!r}", + ) + return value + + def _process_dynamic(self, value: List[str]) -> List[str]: + for dynamic_field in map(str.lower, value): + if dynamic_field in {"name", "version", "metadata-version"}: + raise self._invalid_metadata( + f"{value!r} is not allowed as a dynamic field" + ) + elif dynamic_field not in _EMAIL_TO_RAW_MAPPING: + raise self._invalid_metadata(f"{value!r} is not a valid dynamic field") + return list(map(str.lower, value)) + + def _process_provides_extra( + self, + value: List[str], + ) -> List[utils.NormalizedName]: + normalized_names = [] + try: + for name in value: + normalized_names.append(utils.canonicalize_name(name, validate=True)) + except utils.InvalidName as exc: + raise self._invalid_metadata( + f"{name!r} is invalid for {{field}}", cause=exc + ) + else: + return normalized_names + + def _process_requires_python(self, value: str) -> specifiers.SpecifierSet: + try: + return specifiers.SpecifierSet(value) + except specifiers.InvalidSpecifier as exc: + raise self._invalid_metadata( + f"{value!r} is invalid for {{field}}", cause=exc + ) + + def _process_requires_dist( + self, + value: List[str], + ) -> List[requirements.Requirement]: + reqs = [] + try: + for req in value: + reqs.append(requirements.Requirement(req)) + except requirements.InvalidRequirement as exc: + raise self._invalid_metadata(f"{req!r} is invalid for {{field}}", cause=exc) + else: + return reqs + + +class Metadata: + """Representation of distribution metadata. + + Compared to :class:`RawMetadata`, this class provides objects representing + metadata fields instead of only using built-in types. Any invalid metadata + will cause :exc:`InvalidMetadata` to be raised (with a + :py:attr:`~BaseException.__cause__` attribute as appropriate). + """ + + _raw: RawMetadata + + @classmethod + def from_raw(cls, data: RawMetadata, *, validate: bool = True) -> "Metadata": + """Create an instance from :class:`RawMetadata`. + + If *validate* is true, all metadata will be validated. All exceptions + related to validation will be gathered and raised as an :class:`ExceptionGroup`. + """ + ins = cls() + ins._raw = data.copy() # Mutations occur due to caching enriched values. + + if validate: + exceptions: List[Exception] = [] + try: + metadata_version = ins.metadata_version + metadata_age = _VALID_METADATA_VERSIONS.index(metadata_version) + except InvalidMetadata as metadata_version_exc: + exceptions.append(metadata_version_exc) + metadata_version = None + + # Make sure to check for the fields that are present, the required + # fields (so their absence can be reported). + fields_to_check = frozenset(ins._raw) | _REQUIRED_ATTRS + # Remove fields that have already been checked. + fields_to_check -= {"metadata_version"} + + for key in fields_to_check: + try: + if metadata_version: + # Can't use getattr() as that triggers descriptor protocol which + # will fail due to no value for the instance argument. + try: + field_metadata_version = cls.__dict__[key].added + except KeyError: + exc = InvalidMetadata(key, f"unrecognized field: {key!r}") + exceptions.append(exc) + continue + field_age = _VALID_METADATA_VERSIONS.index( + field_metadata_version + ) + if field_age > metadata_age: + field = _RAW_TO_EMAIL_MAPPING[key] + exc = InvalidMetadata( + field, + "{field} introduced in metadata version " + "{field_metadata_version}, not {metadata_version}", + ) + exceptions.append(exc) + continue + getattr(ins, key) + except InvalidMetadata as exc: + exceptions.append(exc) + + if exceptions: + raise ExceptionGroup("invalid metadata", exceptions) + + return ins + + @classmethod + def from_email( + cls, data: Union[bytes, str], *, validate: bool = True + ) -> "Metadata": + """Parse metadata from email headers. + + If *validate* is true, the metadata will be validated. All exceptions + related to validation will be gathered and raised as an :class:`ExceptionGroup`. + """ + raw, unparsed = parse_email(data) + + if validate: + exceptions: list[Exception] = [] + for unparsed_key in unparsed: + if unparsed_key in _EMAIL_TO_RAW_MAPPING: + message = f"{unparsed_key!r} has invalid data" + else: + message = f"unrecognized field: {unparsed_key!r}" + exceptions.append(InvalidMetadata(unparsed_key, message)) + + if exceptions: + raise ExceptionGroup("unparsed", exceptions) + + try: + return cls.from_raw(raw, validate=validate) + except ExceptionGroup as exc_group: + raise ExceptionGroup( + "invalid or unparsed metadata", exc_group.exceptions + ) from None + + metadata_version: _Validator[_MetadataVersion] = _Validator() + """:external:ref:`core-metadata-metadata-version` + (required; validated to be a valid metadata version)""" + name: _Validator[str] = _Validator() + """:external:ref:`core-metadata-name` + (required; validated using :func:`~packaging.utils.canonicalize_name` and its + *validate* parameter)""" + version: _Validator[version_module.Version] = _Validator() + """:external:ref:`core-metadata-version` (required)""" + dynamic: _Validator[Optional[List[str]]] = _Validator( + added="2.2", + ) + """:external:ref:`core-metadata-dynamic` + (validated against core metadata field names and lowercased)""" + platforms: _Validator[Optional[List[str]]] = _Validator() + """:external:ref:`core-metadata-platform`""" + supported_platforms: _Validator[Optional[List[str]]] = _Validator(added="1.1") + """:external:ref:`core-metadata-supported-platform`""" + summary: _Validator[Optional[str]] = _Validator() + """:external:ref:`core-metadata-summary` (validated to contain no newlines)""" + description: _Validator[Optional[str]] = _Validator() # TODO 2.1: can be in body + """:external:ref:`core-metadata-description`""" + description_content_type: _Validator[Optional[str]] = _Validator(added="2.1") + """:external:ref:`core-metadata-description-content-type` (validated)""" + keywords: _Validator[Optional[List[str]]] = _Validator() + """:external:ref:`core-metadata-keywords`""" + home_page: _Validator[Optional[str]] = _Validator() + """:external:ref:`core-metadata-home-page`""" + download_url: _Validator[Optional[str]] = _Validator(added="1.1") + """:external:ref:`core-metadata-download-url`""" + author: _Validator[Optional[str]] = _Validator() + """:external:ref:`core-metadata-author`""" + author_email: _Validator[Optional[str]] = _Validator() + """:external:ref:`core-metadata-author-email`""" + maintainer: _Validator[Optional[str]] = _Validator(added="1.2") + """:external:ref:`core-metadata-maintainer`""" + maintainer_email: _Validator[Optional[str]] = _Validator(added="1.2") + """:external:ref:`core-metadata-maintainer-email`""" + license: _Validator[Optional[str]] = _Validator() + """:external:ref:`core-metadata-license`""" + classifiers: _Validator[Optional[List[str]]] = _Validator(added="1.1") + """:external:ref:`core-metadata-classifier`""" + requires_dist: _Validator[Optional[List[requirements.Requirement]]] = _Validator( + added="1.2" + ) + """:external:ref:`core-metadata-requires-dist`""" + requires_python: _Validator[Optional[specifiers.SpecifierSet]] = _Validator( + added="1.2" + ) + """:external:ref:`core-metadata-requires-python`""" + # Because `Requires-External` allows for non-PEP 440 version specifiers, we + # don't do any processing on the values. + requires_external: _Validator[Optional[List[str]]] = _Validator(added="1.2") + """:external:ref:`core-metadata-requires-external`""" + project_urls: _Validator[Optional[Dict[str, str]]] = _Validator(added="1.2") + """:external:ref:`core-metadata-project-url`""" + # PEP 685 lets us raise an error if an extra doesn't pass `Name` validation + # regardless of metadata version. + provides_extra: _Validator[Optional[List[utils.NormalizedName]]] = _Validator( + added="2.1", + ) + """:external:ref:`core-metadata-provides-extra`""" + provides_dist: _Validator[Optional[List[str]]] = _Validator(added="1.2") + """:external:ref:`core-metadata-provides-dist`""" + obsoletes_dist: _Validator[Optional[List[str]]] = _Validator(added="1.2") + """:external:ref:`core-metadata-obsoletes-dist`""" + requires: _Validator[Optional[List[str]]] = _Validator(added="1.1") + """``Requires`` (deprecated)""" + provides: _Validator[Optional[List[str]]] = _Validator(added="1.1") + """``Provides`` (deprecated)""" + obsoletes: _Validator[Optional[List[str]]] = _Validator(added="1.1") + """``Obsoletes`` (deprecated)""" diff --git a/venv/Lib/site-packages/setuptools/_vendor/packaging/py.typed b/venv/Lib/site-packages/setuptools/_vendor/packaging/py.typed new file mode 100644 index 0000000..e69de29 diff --git a/venv/Lib/site-packages/setuptools/_vendor/packaging/requirements.py b/venv/Lib/site-packages/setuptools/_vendor/packaging/requirements.py new file mode 100644 index 0000000..bdc43a7 --- /dev/null +++ b/venv/Lib/site-packages/setuptools/_vendor/packaging/requirements.py @@ -0,0 +1,90 @@ +# 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 typing import Any, Iterator, Optional, Set + +from ._parser import parse_requirement as _parse_requirement +from ._tokenizer import ParserSyntaxError +from .markers import Marker, _normalize_extra_values +from .specifiers import SpecifierSet +from .utils import canonicalize_name + + +class InvalidRequirement(ValueError): + """ + An invalid requirement was found, users should refer to PEP 508. + """ + + +class Requirement: + """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: str) -> None: + try: + parsed = _parse_requirement(requirement_string) + except ParserSyntaxError as e: + raise InvalidRequirement(str(e)) from e + + self.name: str = parsed.name + self.url: Optional[str] = parsed.url or None + self.extras: Set[str] = set(parsed.extras or []) + self.specifier: SpecifierSet = SpecifierSet(parsed.specifier) + self.marker: Optional[Marker] = None + if parsed.marker is not None: + self.marker = Marker.__new__(Marker) + self.marker._markers = _normalize_extra_values(parsed.marker) + + def _iter_parts(self, name: str) -> Iterator[str]: + yield name + + if self.extras: + formatted_extras = ",".join(sorted(self.extras)) + yield f"[{formatted_extras}]" + + if self.specifier: + yield str(self.specifier) + + if self.url: + yield f"@ {self.url}" + if self.marker: + yield " " + + if self.marker: + yield f"; {self.marker}" + + def __str__(self) -> str: + return "".join(self._iter_parts(self.name)) + + def __repr__(self) -> str: + return f"" + + def __hash__(self) -> int: + return hash( + ( + self.__class__.__name__, + *self._iter_parts(canonicalize_name(self.name)), + ) + ) + + def __eq__(self, other: Any) -> bool: + if not isinstance(other, Requirement): + return NotImplemented + + return ( + canonicalize_name(self.name) == canonicalize_name(other.name) + and self.extras == other.extras + and self.specifier == other.specifier + and self.url == other.url + and self.marker == other.marker + ) diff --git a/venv/Lib/site-packages/setuptools/_vendor/packaging/specifiers.py b/venv/Lib/site-packages/setuptools/_vendor/packaging/specifiers.py new file mode 100644 index 0000000..2d015ba --- /dev/null +++ b/venv/Lib/site-packages/setuptools/_vendor/packaging/specifiers.py @@ -0,0 +1,1017 @@ +# 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. +""" +.. testsetup:: + + from packaging.specifiers import Specifier, SpecifierSet, InvalidSpecifier + from packaging.version import Version +""" + +import abc +import itertools +import re +from typing import Callable, Iterable, Iterator, List, Optional, Tuple, TypeVar, Union + +from .utils import canonicalize_version +from .version import Version + +UnparsedVersion = Union[Version, str] +UnparsedVersionVar = TypeVar("UnparsedVersionVar", bound=UnparsedVersion) +CallableOperator = Callable[[Version, str], bool] + + +def _coerce_version(version: UnparsedVersion) -> Version: + if not isinstance(version, Version): + version = Version(version) + return version + + +class InvalidSpecifier(ValueError): + """ + Raised when attempting to create a :class:`Specifier` with a specifier + string that is invalid. + + >>> Specifier("lolwat") + Traceback (most recent call last): + ... + packaging.specifiers.InvalidSpecifier: Invalid specifier: 'lolwat' + """ + + +class BaseSpecifier(metaclass=abc.ABCMeta): + @abc.abstractmethod + def __str__(self) -> str: + """ + Returns the str representation of this Specifier-like object. This + should be representative of the Specifier itself. + """ + + @abc.abstractmethod + def __hash__(self) -> int: + """ + Returns a hash value for this Specifier-like object. + """ + + @abc.abstractmethod + def __eq__(self, other: object) -> bool: + """ + Returns a boolean representing whether or not the two Specifier-like + objects are equal. + + :param other: The other object to check against. + """ + + @property + @abc.abstractmethod + def prereleases(self) -> Optional[bool]: + """Whether or not pre-releases as a whole are allowed. + + This can be set to either ``True`` or ``False`` to explicitly enable or disable + prereleases or it can be set to ``None`` (the default) to use default semantics. + """ + + @prereleases.setter + def prereleases(self, value: bool) -> None: + """Setter for :attr:`prereleases`. + + :param value: The value to set. + """ + + @abc.abstractmethod + def contains(self, item: str, prereleases: Optional[bool] = None) -> bool: + """ + Determines if the given item is contained within this specifier. + """ + + @abc.abstractmethod + def filter( + self, iterable: Iterable[UnparsedVersionVar], prereleases: Optional[bool] = None + ) -> Iterator[UnparsedVersionVar]: + """ + Takes an iterable of items and filters them so that only items which + are contained within this specifier are allowed in it. + """ + + +class Specifier(BaseSpecifier): + """This class abstracts handling of version specifiers. + + .. tip:: + + It is generally not required to instantiate this manually. You should instead + prefer to work with :class:`SpecifierSet` instead, which can parse + comma-separated version specifiers (which is what package metadata contains). + """ + + _operator_regex_str = r""" + (?P(~=|==|!=|<=|>=|<|>|===)) + """ + _version_regex_str = r""" + (?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;)]* # The arbitrary version can be just about anything, + # we match everything except for whitespace, a + # semi-colon for marker support, and a closing paren + # since versions can be enclosed in them. + ) + | + (?: + # 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 + + # You cannot use a wild card and a pre-release, post-release, a dev or + # local version together so group them with a | and make them optional. + (?: + \.\* # Wild card syntax of .* + | + (?: # pre release + [-_\.]? + (alpha|beta|preview|pre|a|b|c|rc) + [-_\.]? + [0-9]* + )? + (?: # post release + (?:-[0-9]+)|(?:[-_\.]?(post|rev|r)[-_\.]?[0-9]*) + )? + (?:[-_\.]?dev[-_\.]?[0-9]*)? # dev release + (?:\+[a-z0-9]+(?:[-_\.][a-z0-9]+)*)? # local + )? + ) + | + (?: + # 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 + [-_\.]? + (alpha|beta|preview|pre|a|b|c|rc) + [-_\.]? + [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", + } + + def __init__(self, spec: str = "", prereleases: Optional[bool] = None) -> None: + """Initialize a Specifier instance. + + :param spec: + The string representation of a specifier which will be parsed and + normalized before use. + :param prereleases: + This tells the specifier if it should accept prerelease versions if + applicable or not. The default of ``None`` will autodetect it from the + given specifiers. + :raises InvalidSpecifier: + If the given specifier is invalid (i.e. bad syntax). + """ + match = self._regex.search(spec) + if not match: + raise InvalidSpecifier(f"Invalid specifier: '{spec}'") + + self._spec: Tuple[str, str] = ( + match.group("operator").strip(), + match.group("version").strip(), + ) + + # Store whether or not this Specifier should accept prereleases + self._prereleases = prereleases + + # https://github.com/python/mypy/pull/13475#pullrequestreview-1079784515 + @property # type: ignore[override] + def prereleases(self) -> 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 Version(version).is_prerelease: + return True + + return False + + @prereleases.setter + def prereleases(self, value: bool) -> None: + self._prereleases = value + + @property + def operator(self) -> str: + """The operator of this specifier. + + >>> Specifier("==1.2.3").operator + '==' + """ + return self._spec[0] + + @property + def version(self) -> str: + """The version of this specifier. + + >>> Specifier("==1.2.3").version + '1.2.3' + """ + return self._spec[1] + + def __repr__(self) -> str: + """A representation of the Specifier that shows all internal state. + + >>> Specifier('>=1.0.0') + =1.0.0')> + >>> Specifier('>=1.0.0', prereleases=False) + =1.0.0', prereleases=False)> + >>> Specifier('>=1.0.0', prereleases=True) + =1.0.0', prereleases=True)> + """ + pre = ( + f", prereleases={self.prereleases!r}" + if self._prereleases is not None + else "" + ) + + return f"<{self.__class__.__name__}({str(self)!r}{pre})>" + + def __str__(self) -> str: + """A string representation of the Specifier that can be round-tripped. + + >>> str(Specifier('>=1.0.0')) + '>=1.0.0' + >>> str(Specifier('>=1.0.0', prereleases=False)) + '>=1.0.0' + """ + return "{}{}".format(*self._spec) + + @property + def _canonical_spec(self) -> Tuple[str, str]: + canonical_version = canonicalize_version( + self._spec[1], + strip_trailing_zero=(self._spec[0] != "~="), + ) + return self._spec[0], canonical_version + + def __hash__(self) -> int: + return hash(self._canonical_spec) + + def __eq__(self, other: object) -> bool: + """Whether or not the two Specifier-like objects are equal. + + :param other: The other object to check against. + + The value of :attr:`prereleases` is ignored. + + >>> Specifier("==1.2.3") == Specifier("== 1.2.3.0") + True + >>> (Specifier("==1.2.3", prereleases=False) == + ... Specifier("==1.2.3", prereleases=True)) + True + >>> Specifier("==1.2.3") == "==1.2.3" + True + >>> Specifier("==1.2.3") == Specifier("==1.2.4") + False + >>> Specifier("==1.2.3") == Specifier("~=1.2.3") + False + """ + if isinstance(other, str): + try: + other = self.__class__(str(other)) + except InvalidSpecifier: + return NotImplemented + elif not isinstance(other, self.__class__): + return NotImplemented + + return self._canonical_spec == other._canonical_spec + + def _get_operator(self, op: str) -> CallableOperator: + operator_callable: CallableOperator = getattr( + self, f"_compare_{self._operators[op]}" + ) + return operator_callable + + def _compare_compatible(self, prospective: Version, spec: 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 suffix segments. + prefix = _version_join( + list(itertools.takewhile(_is_not_suffix, _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 + ) + + def _compare_equal(self, prospective: Version, spec: 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. + normalized_prospective = canonicalize_version( + prospective.public, strip_trailing_zero=False + ) + # Get the normalized version string ignoring the trailing .* + normalized_spec = canonicalize_version(spec[:-2], strip_trailing_zero=False) + # Split the spec out by bangs and dots, and pretend that there is + # an implicit dot in between a release segment and a pre-release segment. + split_spec = _version_split(normalized_spec) + + # Split the prospective version out by bangs and dots, and pretend + # that there is an implicit dot in between a release segment and + # a pre-release segment. + split_prospective = _version_split(normalized_prospective) + + # 0-pad the prospective version before shortening it to get the correct + # shortened version. + padded_prospective, _ = _pad_version(split_prospective, split_spec) + + # 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 = padded_prospective[: len(split_spec)] + + return shortened_prospective == split_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 + + def _compare_not_equal(self, prospective: Version, spec: str) -> bool: + return not self._compare_equal(prospective, spec) + + def _compare_less_than_equal(self, prospective: Version, spec: str) -> bool: + + # NB: Local version identifiers are NOT permitted in the version + # specifier, so local version labels can be universally removed from + # the prospective version. + return Version(prospective.public) <= Version(spec) + + def _compare_greater_than_equal(self, prospective: Version, spec: str) -> bool: + + # NB: Local version identifiers are NOT permitted in the version + # specifier, so local version labels can be universally removed from + # the prospective version. + return Version(prospective.public) >= Version(spec) + + def _compare_less_than(self, prospective: Version, spec_str: 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 + + def _compare_greater_than(self, prospective: Version, spec_str: 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: Version, spec: str) -> bool: + return str(prospective).lower() == str(spec).lower() + + def __contains__(self, item: Union[str, Version]) -> bool: + """Return whether or not the item is contained in this specifier. + + :param item: The item to check for. + + This is used for the ``in`` operator and behaves the same as + :meth:`contains` with no ``prereleases`` argument passed. + + >>> "1.2.3" in Specifier(">=1.2.3") + True + >>> Version("1.2.3") in Specifier(">=1.2.3") + True + >>> "1.0.0" in Specifier(">=1.2.3") + False + >>> "1.3.0a1" in Specifier(">=1.2.3") + False + >>> "1.3.0a1" in Specifier(">=1.2.3", prereleases=True) + True + """ + return self.contains(item) + + def contains( + self, item: UnparsedVersion, prereleases: Optional[bool] = None + ) -> bool: + """Return whether or not the item is contained in this specifier. + + :param item: + The item to check for, which can be a version string or a + :class:`Version` instance. + :param prereleases: + Whether or not to match prereleases with this Specifier. If set to + ``None`` (the default), it uses :attr:`prereleases` to determine + whether or not prereleases are allowed. + + >>> Specifier(">=1.2.3").contains("1.2.3") + True + >>> Specifier(">=1.2.3").contains(Version("1.2.3")) + True + >>> Specifier(">=1.2.3").contains("1.0.0") + False + >>> Specifier(">=1.2.3").contains("1.3.0a1") + False + >>> Specifier(">=1.2.3", prereleases=True).contains("1.3.0a1") + True + >>> Specifier(">=1.2.3").contains("1.3.0a1", prereleases=True) + True + """ + + # Determine if prereleases are to be allowed or not. + if prereleases is None: + prereleases = self.prereleases + + # Normalize item to a Version, this allows us to have a shortcut for + # "2.0" in Specifier(">=2") + normalized_item = _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: CallableOperator = self._get_operator(self.operator) + return operator_callable(normalized_item, self.version) + + def filter( + self, iterable: Iterable[UnparsedVersionVar], prereleases: Optional[bool] = None + ) -> Iterator[UnparsedVersionVar]: + """Filter items in the given iterable, that match the specifier. + + :param iterable: + An iterable that can contain version strings and :class:`Version` instances. + The items in the iterable will be filtered according to the specifier. + :param prereleases: + Whether or not to allow prereleases in the returned iterator. If set to + ``None`` (the default), it will be intelligently decide whether to allow + prereleases or not (based on the :attr:`prereleases` attribute, and + whether the only versions matching are prereleases). + + This method is smarter than just ``filter(Specifier().contains, [...])`` + because it implements the rule from :pep:`440` that a prerelease item + SHOULD be accepted if no other versions match the given specifier. + + >>> list(Specifier(">=1.2.3").filter(["1.2", "1.3", "1.5a1"])) + ['1.3'] + >>> list(Specifier(">=1.2.3").filter(["1.2", "1.2.3", "1.3", Version("1.4")])) + ['1.2.3', '1.3', ] + >>> list(Specifier(">=1.2.3").filter(["1.2", "1.5a1"])) + ['1.5a1'] + >>> list(Specifier(">=1.2.3").filter(["1.3", "1.5a1"], prereleases=True)) + ['1.3', '1.5a1'] + >>> list(Specifier(">=1.2.3", prereleases=True).filter(["1.3", "1.5a1"])) + ['1.3', '1.5a1'] + """ + + 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 = _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 in case 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 + + +_prefix_regex = re.compile(r"^([0-9]+)((?:a|b|c|rc)[0-9]+)$") + + +def _version_split(version: str) -> List[str]: + """Split version into components. + + The split components are intended for version comparison. The logic does + not attempt to retain the original version string, so joining the + components back with :func:`_version_join` may not produce the original + version string. + """ + result: List[str] = [] + + epoch, _, rest = version.rpartition("!") + result.append(epoch or "0") + + for item in rest.split("."): + match = _prefix_regex.search(item) + if match: + result.extend(match.groups()) + else: + result.append(item) + return result + + +def _version_join(components: List[str]) -> str: + """Join split version components into a version string. + + This function assumes the input came from :func:`_version_split`, where the + first component must be the epoch (either empty or numeric), and all other + components numeric. + """ + epoch, *rest = components + return f"{epoch}!{'.'.join(rest)}" + + +def _is_not_suffix(segment: str) -> bool: + return not any( + segment.startswith(prefix) for prefix in ("dev", "a", "b", "rc", "post") + ) + + +def _pad_version(left: List[str], right: 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.from_iterable(left_split)), + list(itertools.chain.from_iterable(right_split)), + ) + + +class SpecifierSet(BaseSpecifier): + """This class abstracts handling of a set of version specifiers. + + It can be passed a single specifier (``>=3.0``), a comma-separated list of + specifiers (``>=3.0,!=3.1``), or no specifier at all. + """ + + def __init__( + self, specifiers: str = "", prereleases: Optional[bool] = None + ) -> None: + """Initialize a SpecifierSet instance. + + :param specifiers: + The string representation of a specifier or a comma-separated list of + specifiers which will be parsed and normalized before use. + :param prereleases: + This tells the SpecifierSet if it should accept prerelease versions if + applicable or not. The default of ``None`` will autodetect it from the + given specifiers. + + :raises InvalidSpecifier: + If the given ``specifiers`` are not parseable than this exception will be + raised. + """ + + # 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()] + + # Make each individual specifier a Specifier and save in a frozen set for later. + self._specs = frozenset(map(Specifier, split_specifiers)) + + # Store our prereleases value so we can use it later to determine if + # we accept prereleases or not. + self._prereleases = prereleases + + @property + def prereleases(self) -> 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: bool) -> None: + self._prereleases = value + + def __repr__(self) -> str: + """A representation of the specifier set that shows all internal state. + + Note that the ordering of the individual specifiers within the set may not + match the input string. + + >>> SpecifierSet('>=1.0.0,!=2.0.0') + =1.0.0')> + >>> SpecifierSet('>=1.0.0,!=2.0.0', prereleases=False) + =1.0.0', prereleases=False)> + >>> SpecifierSet('>=1.0.0,!=2.0.0', prereleases=True) + =1.0.0', prereleases=True)> + """ + pre = ( + f", prereleases={self.prereleases!r}" + if self._prereleases is not None + else "" + ) + + return f"" + + def __str__(self) -> str: + """A string representation of the specifier set that can be round-tripped. + + Note that the ordering of the individual specifiers within the set may not + match the input string. + + >>> str(SpecifierSet(">=1.0.0,!=1.0.1")) + '!=1.0.1,>=1.0.0' + >>> str(SpecifierSet(">=1.0.0,!=1.0.1", prereleases=False)) + '!=1.0.1,>=1.0.0' + """ + return ",".join(sorted(str(s) for s in self._specs)) + + def __hash__(self) -> int: + return hash(self._specs) + + def __and__(self, other: Union["SpecifierSet", str]) -> "SpecifierSet": + """Return a SpecifierSet which is a combination of the two sets. + + :param other: The other object to combine with. + + >>> SpecifierSet(">=1.0.0,!=1.0.1") & '<=2.0.0,!=2.0.1' + =1.0.0')> + >>> SpecifierSet(">=1.0.0,!=1.0.1") & SpecifierSet('<=2.0.0,!=2.0.1') + =1.0.0')> + """ + if isinstance(other, str): + 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: object) -> bool: + """Whether or not the two SpecifierSet-like objects are equal. + + :param other: The other object to check against. + + The value of :attr:`prereleases` is ignored. + + >>> SpecifierSet(">=1.0.0,!=1.0.1") == SpecifierSet(">=1.0.0,!=1.0.1") + True + >>> (SpecifierSet(">=1.0.0,!=1.0.1", prereleases=False) == + ... SpecifierSet(">=1.0.0,!=1.0.1", prereleases=True)) + True + >>> SpecifierSet(">=1.0.0,!=1.0.1") == ">=1.0.0,!=1.0.1" + True + >>> SpecifierSet(">=1.0.0,!=1.0.1") == SpecifierSet(">=1.0.0") + False + >>> SpecifierSet(">=1.0.0,!=1.0.1") == SpecifierSet(">=1.0.0,!=1.0.2") + False + """ + if isinstance(other, (str, Specifier)): + other = SpecifierSet(str(other)) + elif not isinstance(other, SpecifierSet): + return NotImplemented + + return self._specs == other._specs + + def __len__(self) -> int: + """Returns the number of specifiers in this specifier set.""" + return len(self._specs) + + def __iter__(self) -> Iterator[Specifier]: + """ + Returns an iterator over all the underlying :class:`Specifier` instances + in this specifier set. + + >>> sorted(SpecifierSet(">=1.0.0,!=1.0.1"), key=str) + [, =1.0.0')>] + """ + return iter(self._specs) + + def __contains__(self, item: UnparsedVersion) -> bool: + """Return whether or not the item is contained in this specifier. + + :param item: The item to check for. + + This is used for the ``in`` operator and behaves the same as + :meth:`contains` with no ``prereleases`` argument passed. + + >>> "1.2.3" in SpecifierSet(">=1.0.0,!=1.0.1") + True + >>> Version("1.2.3") in SpecifierSet(">=1.0.0,!=1.0.1") + True + >>> "1.0.1" in SpecifierSet(">=1.0.0,!=1.0.1") + False + >>> "1.3.0a1" in SpecifierSet(">=1.0.0,!=1.0.1") + False + >>> "1.3.0a1" in SpecifierSet(">=1.0.0,!=1.0.1", prereleases=True) + True + """ + return self.contains(item) + + def contains( + self, + item: UnparsedVersion, + prereleases: Optional[bool] = None, + installed: Optional[bool] = None, + ) -> bool: + """Return whether or not the item is contained in this SpecifierSet. + + :param item: + The item to check for, which can be a version string or a + :class:`Version` instance. + :param prereleases: + Whether or not to match prereleases with this SpecifierSet. If set to + ``None`` (the default), it uses :attr:`prereleases` to determine + whether or not prereleases are allowed. + + >>> SpecifierSet(">=1.0.0,!=1.0.1").contains("1.2.3") + True + >>> SpecifierSet(">=1.0.0,!=1.0.1").contains(Version("1.2.3")) + True + >>> SpecifierSet(">=1.0.0,!=1.0.1").contains("1.0.1") + False + >>> SpecifierSet(">=1.0.0,!=1.0.1").contains("1.3.0a1") + False + >>> SpecifierSet(">=1.0.0,!=1.0.1", prereleases=True).contains("1.3.0a1") + True + >>> SpecifierSet(">=1.0.0,!=1.0.1").contains("1.3.0a1", prereleases=True) + True + """ + # Ensure that our item is a Version instance. + if not isinstance(item, Version): + item = Version(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 + + if installed and item.is_prerelease: + item = Version(item.base_version) + + # 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: Iterable[UnparsedVersionVar], prereleases: Optional[bool] = None + ) -> Iterator[UnparsedVersionVar]: + """Filter items in the given iterable, that match the specifiers in this set. + + :param iterable: + An iterable that can contain version strings and :class:`Version` instances. + The items in the iterable will be filtered according to the specifier. + :param prereleases: + Whether or not to allow prereleases in the returned iterator. If set to + ``None`` (the default), it will be intelligently decide whether to allow + prereleases or not (based on the :attr:`prereleases` attribute, and + whether the only versions matching are prereleases). + + This method is smarter than just ``filter(SpecifierSet(...).contains, [...])`` + because it implements the rule from :pep:`440` that a prerelease item + SHOULD be accepted if no other versions match the given specifier. + + >>> list(SpecifierSet(">=1.2.3").filter(["1.2", "1.3", "1.5a1"])) + ['1.3'] + >>> list(SpecifierSet(">=1.2.3").filter(["1.2", "1.3", Version("1.4")])) + ['1.3', ] + >>> list(SpecifierSet(">=1.2.3").filter(["1.2", "1.5a1"])) + [] + >>> list(SpecifierSet(">=1.2.3").filter(["1.3", "1.5a1"], prereleases=True)) + ['1.3', '1.5a1'] + >>> list(SpecifierSet(">=1.2.3", prereleases=True).filter(["1.3", "1.5a1"])) + ['1.3', '1.5a1'] + + An "empty" SpecifierSet will filter items based on the presence of prerelease + versions in the set. + + >>> list(SpecifierSet("").filter(["1.3", "1.5a1"])) + ['1.3'] + >>> list(SpecifierSet("").filter(["1.5a1"])) + ['1.5a1'] + >>> list(SpecifierSet("", prereleases=True).filter(["1.3", "1.5a1"])) + ['1.3', '1.5a1'] + >>> list(SpecifierSet("").filter(["1.3", "1.5a1"], prereleases=True)) + ['1.3', '1.5a1'] + """ + # 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 iter(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. + else: + filtered: List[UnparsedVersionVar] = [] + found_prereleases: List[UnparsedVersionVar] = [] + + for item in iterable: + parsed_version = _coerce_version(item) + + # 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 iter(found_prereleases) + + return iter(filtered) diff --git a/venv/Lib/site-packages/setuptools/_vendor/packaging/tags.py b/venv/Lib/site-packages/setuptools/_vendor/packaging/tags.py new file mode 100644 index 0000000..89f1926 --- /dev/null +++ b/venv/Lib/site-packages/setuptools/_vendor/packaging/tags.py @@ -0,0 +1,571 @@ +# 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. + +import logging +import platform +import re +import struct +import subprocess +import sys +import sysconfig +from importlib.machinery import EXTENSION_SUFFIXES +from typing import ( + Dict, + FrozenSet, + Iterable, + Iterator, + List, + Optional, + Sequence, + Tuple, + Union, + cast, +) + +from . import _manylinux, _musllinux + +logger = logging.getLogger(__name__) + +PythonVersion = Sequence[int] +MacVersion = Tuple[int, int] + +INTERPRETER_SHORT_NAMES: Dict[str, str] = { + "python": "py", # Generic. + "cpython": "cp", + "pypy": "pp", + "ironpython": "ip", + "jython": "jy", +} + + +_32_BIT_INTERPRETER = struct.calcsize("P") == 4 + + +class Tag: + """ + A representation of the tag triple for a wheel. + + Instances are considered immutable and thus are hashable. Equality checking + is also supported. + """ + + __slots__ = ["_interpreter", "_abi", "_platform", "_hash"] + + def __init__(self, interpreter: str, abi: str, platform: str) -> None: + self._interpreter = interpreter.lower() + self._abi = abi.lower() + self._platform = platform.lower() + # The __hash__ of every single element in a Set[Tag] will be evaluated each time + # that a set calls its `.disjoint()` method, which may be called hundreds of + # times when scanning a page of links for packages with tags matching that + # Set[Tag]. Pre-computing the value here produces significant speedups for + # downstream consumers. + self._hash = hash((self._interpreter, self._abi, self._platform)) + + @property + def interpreter(self) -> str: + return self._interpreter + + @property + def abi(self) -> str: + return self._abi + + @property + def platform(self) -> str: + return self._platform + + def __eq__(self, other: object) -> bool: + if not isinstance(other, Tag): + return NotImplemented + + return ( + (self._hash == other._hash) # Short-circuit ASAP for perf reasons. + and (self._platform == other._platform) + and (self._abi == other._abi) + and (self._interpreter == other._interpreter) + ) + + def __hash__(self) -> int: + return self._hash + + def __str__(self) -> str: + return f"{self._interpreter}-{self._abi}-{self._platform}" + + def __repr__(self) -> str: + return f"<{self} @ {id(self)}>" + + +def parse_tag(tag: str) -> FrozenSet[Tag]: + """ + Parses the provided tag (e.g. `py3-none-any`) into a frozenset of Tag instances. + + Returning a set is required due to the possibility that the tag is a + compressed tag set. + """ + 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 _get_config_var(name: str, warn: bool = False) -> Union[int, str, None]: + value: Union[int, str, None] = 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: str) -> str: + return string.replace(".", "_").replace("-", "_").replace(" ", "_") + + +def _is_threaded_cpython(abis: List[str]) -> bool: + """ + Determine if the ABI corresponds to a threaded (`--disable-gil`) build. + + The threaded builds are indicated by a "t" in the abiflags. + """ + if len(abis) == 0: + return False + # expect e.g., cp313 + m = re.match(r"cp\d+(.*)", abis[0]) + if not m: + return False + abiflags = m.group(1) + return "t" in abiflags + + +def _abi3_applies(python_version: PythonVersion, threading: bool) -> bool: + """ + Determine if the Python version supports abi3. + + PEP 384 was first implemented in Python 3.2. The threaded (`--disable-gil`) + builds do not support abi3. + """ + return len(python_version) > 1 and tuple(python_version) >= (3, 2) and not threading + + +def _cpython_abis(py_version: PythonVersion, warn: bool = False) -> List[str]: + py_version = tuple(py_version) # To allow for version comparison. + abis = [] + version = _version_nodot(py_version[:2]) + threading = 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, 13) and _get_config_var("Py_GIL_DISABLED", warn): + threading = "t" + 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(f"cp{version}{threading}") + abis.insert(0, f"cp{version}{threading}{debug}{pymalloc}{ucs4}") + return abis + + +def cpython_tags( + python_version: Optional[PythonVersion] = None, + abis: Optional[Iterable[str]] = None, + platforms: Optional[Iterable[str]] = None, + *, + warn: bool = False, +) -> 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. + """ + if not python_version: + python_version = sys.version_info[:2] + + interpreter = f"cp{_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_) + + threading = _is_threaded_cpython(abis) + use_abi3 = _abi3_applies(python_version, threading) + if use_abi3: + yield from (Tag(interpreter, "abi3", platform_) for platform_ in platforms) + yield from (Tag(interpreter, "none", platform_) for platform_ in platforms) + + if use_abi3: + 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() -> List[str]: + """ + Return the ABI tag based on EXT_SUFFIX. + """ + # The following are examples of `EXT_SUFFIX`. + # We want to keep the parts which are related to the ABI and remove the + # parts which are related to the platform: + # - linux: '.cpython-310-x86_64-linux-gnu.so' => cp310 + # - mac: '.cpython-310-darwin.so' => cp310 + # - win: '.cp310-win_amd64.pyd' => cp310 + # - win: '.pyd' => cp37 (uses _cpython_abis()) + # - pypy: '.pypy38-pp73-x86_64-linux-gnu.so' => pypy38_pp73 + # - graalpy: '.graalpy-38-native-x86_64-darwin.dylib' + # => graalpy_38_native + + ext_suffix = _get_config_var("EXT_SUFFIX", warn=True) + if not isinstance(ext_suffix, str) or ext_suffix[0] != ".": + raise SystemError("invalid sysconfig.get_config_var('EXT_SUFFIX')") + parts = ext_suffix.split(".") + if len(parts) < 3: + # CPython3.7 and earlier uses ".pyd" on Windows. + return _cpython_abis(sys.version_info[:2]) + soabi = parts[1] + if soabi.startswith("cpython"): + # non-windows + abi = "cp" + soabi.split("-")[1] + elif soabi.startswith("cp"): + # windows + abi = soabi.split("-")[0] + elif soabi.startswith("pypy"): + abi = "-".join(soabi.split("-")[:2]) + elif soabi.startswith("graalpy"): + abi = "-".join(soabi.split("-")[:3]) + elif soabi: + # pyston, ironpython, others? + abi = soabi + else: + return [] + return [_normalize_string(abi)] + + +def generic_tags( + interpreter: Optional[str] = None, + abis: Optional[Iterable[str]] = None, + platforms: Optional[Iterable[str]] = None, + *, + warn: bool = False, +) -> 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. + """ + 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() + else: + abis = list(abis) + platforms = list(platforms or platform_tags()) + 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: 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 f"py{_version_nodot(py_version[:2])}" + yield f"py{py_version[0]}" + if len(py_version) > 1: + for minor in range(py_version[1] - 1, -1, -1): + yield f"py{_version_nodot((py_version[0], minor))}" + + +def compatible_tags( + python_version: Optional[PythonVersion] = None, + interpreter: Optional[str] = None, + platforms: Optional[Iterable[str]] = None, +) -> 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: str, is_32bit: bool = _32_BIT_INTERPRETER) -> str: + if not is_32bit: + return arch + + if arch.startswith("ppc"): + return "ppc" + + return "i386" + + +def _mac_binary_formats(version: MacVersion, cpu_arch: 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"]) + + if cpu_arch in {"arm64", "x86_64"}: + formats.append("universal2") + + if cpu_arch in {"x86_64", "i386", "ppc64", "ppc", "intel"}: + formats.append("universal") + + return formats + + +def mac_platforms( + version: Optional[MacVersion] = None, arch: Optional[str] = None +) -> 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() + if version is None: + version = cast("MacVersion", tuple(map(int, version_str.split(".")[:2]))) + if version == (10, 16): + # When built against an older macOS SDK, Python will report macOS 10.16 + # instead of the real version. + version_str = subprocess.run( + [ + sys.executable, + "-sS", + "-c", + "import platform; print(platform.mac_ver()[0])", + ], + check=True, + env={"SYSTEM_VERSION_COMPAT": "0"}, + stdout=subprocess.PIPE, + text=True, + ).stdout + 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 + + if (10, 0) <= version and version < (11, 0): + # Prior to Mac OS 11, each yearly release of Mac OS bumped the + # "minor" version number. The major version was always 10. + for minor_version in range(version[1], -1, -1): + compat_version = 10, minor_version + binary_formats = _mac_binary_formats(compat_version, arch) + for binary_format in binary_formats: + yield "macosx_{major}_{minor}_{binary_format}".format( + major=10, minor=minor_version, binary_format=binary_format + ) + + if version >= (11, 0): + # Starting with Mac OS 11, each yearly release bumps the major version + # number. The minor versions are now the midyear updates. + for major_version in range(version[0], 10, -1): + compat_version = major_version, 0 + binary_formats = _mac_binary_formats(compat_version, arch) + for binary_format in binary_formats: + yield "macosx_{major}_{minor}_{binary_format}".format( + major=major_version, minor=0, binary_format=binary_format + ) + + if version >= (11, 0): + # Mac OS 11 on x86_64 is compatible with binaries from previous releases. + # Arm64 support was introduced in 11.0, so no Arm binaries from previous + # releases exist. + # + # However, the "universal2" binary format can have a + # macOS version earlier than 11.0 when the x86_64 part of the binary supports + # that version of macOS. + if arch == "x86_64": + for minor_version in range(16, 3, -1): + compat_version = 10, 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, + ) + else: + for minor_version in range(16, 3, -1): + compat_version = 10, minor_version + binary_format = "universal2" + yield "macosx_{major}_{minor}_{binary_format}".format( + major=compat_version[0], + minor=compat_version[1], + binary_format=binary_format, + ) + + +def _linux_platforms(is_32bit: bool = _32_BIT_INTERPRETER) -> Iterator[str]: + linux = _normalize_string(sysconfig.get_platform()) + if not linux.startswith("linux_"): + # we should never be here, just yield the sysconfig one and return + yield linux + return + if is_32bit: + if linux == "linux_x86_64": + linux = "linux_i686" + elif linux == "linux_aarch64": + linux = "linux_armv8l" + _, arch = linux.split("_", 1) + archs = {"armv8l": ["armv8l", "armv7l"]}.get(arch, [arch]) + yield from _manylinux.platform_tags(archs) + yield from _musllinux.platform_tags(archs) + for arch in archs: + yield f"linux_{arch}" + + +def _generic_platforms() -> Iterator[str]: + yield _normalize_string(sysconfig.get_platform()) + + +def platform_tags() -> 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() -> str: + """ + Returns the name of the running interpreter. + + Some implementations have a reserved, two-letter abbreviation which will + be returned when appropriate. + """ + name = sys.implementation.name + return INTERPRETER_SHORT_NAMES.get(name) or name + + +def interpreter_version(*, warn: bool = False) -> str: + """ + Returns the version of the running interpreter. + """ + 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: PythonVersion) -> str: + return "".join(map(str, version)) + + +def sys_tags(*, warn: bool = False) -> 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. + """ + + interp_name = interpreter_name() + if interp_name == "cp": + yield from cpython_tags(warn=warn) + else: + yield from generic_tags() + + if interp_name == "pp": + interp = "pp3" + elif interp_name == "cp": + interp = "cp" + interpreter_version(warn=warn) + else: + interp = None + yield from compatible_tags(interpreter=interp) diff --git a/venv/Lib/site-packages/setuptools/_vendor/packaging/utils.py b/venv/Lib/site-packages/setuptools/_vendor/packaging/utils.py new file mode 100644 index 0000000..c2c2f75 --- /dev/null +++ b/venv/Lib/site-packages/setuptools/_vendor/packaging/utils.py @@ -0,0 +1,172 @@ +# 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. + +import re +from typing import FrozenSet, NewType, Tuple, Union, cast + +from .tags import Tag, parse_tag +from .version import InvalidVersion, Version + +BuildTag = Union[Tuple[()], Tuple[int, str]] +NormalizedName = NewType("NormalizedName", str) + + +class InvalidName(ValueError): + """ + An invalid distribution name; users should refer to the packaging user guide. + """ + + +class InvalidWheelFilename(ValueError): + """ + An invalid wheel filename was found, users should refer to PEP 427. + """ + + +class InvalidSdistFilename(ValueError): + """ + An invalid sdist filename was found, users should refer to the packaging user guide. + """ + + +# Core metadata spec for `Name` +_validate_regex = re.compile( + r"^([A-Z0-9]|[A-Z0-9][A-Z0-9._-]*[A-Z0-9])$", re.IGNORECASE +) +_canonicalize_regex = re.compile(r"[-_.]+") +_normalized_regex = re.compile(r"^([a-z0-9]|[a-z0-9]([a-z0-9-](?!--))*[a-z0-9])$") +# PEP 427: The build number must start with a digit. +_build_tag_regex = re.compile(r"(\d+)(.*)") + + +def canonicalize_name(name: str, *, validate: bool = False) -> NormalizedName: + if validate and not _validate_regex.match(name): + raise InvalidName(f"name is invalid: {name!r}") + # This is taken from PEP 503. + value = _canonicalize_regex.sub("-", name).lower() + return cast(NormalizedName, value) + + +def is_normalized_name(name: str) -> bool: + return _normalized_regex.match(name) is not None + + +def canonicalize_version( + version: Union[Version, str], *, strip_trailing_zero: bool = True +) -> str: + """ + This is very similar to Version.__str__, but has one subtle difference + with the way it handles the release segment. + """ + if isinstance(version, str): + try: + parsed = Version(version) + except InvalidVersion: + # Legacy versions cannot be normalized + return version + else: + parsed = version + + parts = [] + + # Epoch + if parsed.epoch != 0: + parts.append(f"{parsed.epoch}!") + + # Release segment + release_segment = ".".join(str(x) for x in parsed.release) + if strip_trailing_zero: + # NB: This strips trailing '.0's to normalize + release_segment = re.sub(r"(\.0)+$", "", release_segment) + parts.append(release_segment) + + # Pre-release + if parsed.pre is not None: + parts.append("".join(str(x) for x in parsed.pre)) + + # Post-release + if parsed.post is not None: + parts.append(f".post{parsed.post}") + + # Development release + if parsed.dev is not None: + parts.append(f".dev{parsed.dev}") + + # Local version segment + if parsed.local is not None: + parts.append(f"+{parsed.local}") + + return "".join(parts) + + +def parse_wheel_filename( + filename: str, +) -> Tuple[NormalizedName, Version, BuildTag, FrozenSet[Tag]]: + if not filename.endswith(".whl"): + raise InvalidWheelFilename( + f"Invalid wheel filename (extension must be '.whl'): {filename}" + ) + + filename = filename[:-4] + dashes = filename.count("-") + if dashes not in (4, 5): + raise InvalidWheelFilename( + f"Invalid wheel filename (wrong number of parts): {filename}" + ) + + parts = filename.split("-", dashes - 2) + name_part = parts[0] + # See PEP 427 for the rules on escaping the project name. + if "__" in name_part or re.match(r"^[\w\d._]*$", name_part, re.UNICODE) is None: + raise InvalidWheelFilename(f"Invalid project name: {filename}") + name = canonicalize_name(name_part) + + try: + version = Version(parts[1]) + except InvalidVersion as e: + raise InvalidWheelFilename( + f"Invalid wheel filename (invalid version): {filename}" + ) from e + + if dashes == 5: + build_part = parts[2] + build_match = _build_tag_regex.match(build_part) + if build_match is None: + raise InvalidWheelFilename( + f"Invalid build number: {build_part} in '{filename}'" + ) + build = cast(BuildTag, (int(build_match.group(1)), build_match.group(2))) + else: + build = () + tags = parse_tag(parts[-1]) + return (name, version, build, tags) + + +def parse_sdist_filename(filename: str) -> Tuple[NormalizedName, Version]: + if filename.endswith(".tar.gz"): + file_stem = filename[: -len(".tar.gz")] + elif filename.endswith(".zip"): + file_stem = filename[: -len(".zip")] + else: + raise InvalidSdistFilename( + f"Invalid sdist filename (extension must be '.tar.gz' or '.zip'):" + f" {filename}" + ) + + # We are requiring a PEP 440 version, which cannot contain dashes, + # so we split on the last dash. + name_part, sep, version_part = file_stem.rpartition("-") + if not sep: + raise InvalidSdistFilename(f"Invalid sdist filename: {filename}") + + name = canonicalize_name(name_part) + + try: + version = Version(version_part) + except InvalidVersion as e: + raise InvalidSdistFilename( + f"Invalid sdist filename (invalid version): {filename}" + ) from e + + return (name, version) diff --git a/venv/Lib/site-packages/setuptools/_vendor/packaging/version.py b/venv/Lib/site-packages/setuptools/_vendor/packaging/version.py new file mode 100644 index 0000000..5faab9b --- /dev/null +++ b/venv/Lib/site-packages/setuptools/_vendor/packaging/version.py @@ -0,0 +1,563 @@ +# 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. +""" +.. testsetup:: + + from packaging.version import parse, Version +""" + +import itertools +import re +from typing import Any, Callable, NamedTuple, Optional, SupportsInt, Tuple, Union + +from ._structures import Infinity, InfinityType, NegativeInfinity, NegativeInfinityType + +__all__ = ["VERSION_PATTERN", "parse", "Version", "InvalidVersion"] + +LocalType = Tuple[Union[int, str], ...] + +CmpPrePostDevType = Union[InfinityType, NegativeInfinityType, Tuple[str, int]] +CmpLocalType = Union[ + NegativeInfinityType, + Tuple[Union[Tuple[int, str], Tuple[NegativeInfinityType, Union[int, str]]], ...], +] +CmpKey = Tuple[ + int, + Tuple[int, ...], + CmpPrePostDevType, + CmpPrePostDevType, + CmpPrePostDevType, + CmpLocalType, +] +VersionComparisonMethod = Callable[[CmpKey, CmpKey], bool] + + +class _Version(NamedTuple): + epoch: int + release: Tuple[int, ...] + dev: Optional[Tuple[str, int]] + pre: Optional[Tuple[str, int]] + post: Optional[Tuple[str, int]] + local: Optional[LocalType] + + +def parse(version: str) -> "Version": + """Parse the given version string. + + >>> parse('1.0.dev1') + + + :param version: The version string to parse. + :raises InvalidVersion: When the version string is not a valid version. + """ + return Version(version) + + +class InvalidVersion(ValueError): + """Raised when a version string is not a valid version. + + >>> Version("invalid") + Traceback (most recent call last): + ... + packaging.version.InvalidVersion: Invalid version: 'invalid' + """ + + +class _BaseVersion: + _key: Tuple[Any, ...] + + def __hash__(self) -> int: + return hash(self._key) + + # Please keep the duplicated `isinstance` check + # in the six comparisons hereunder + # unless you find a way to avoid adding overhead function calls. + def __lt__(self, other: "_BaseVersion") -> bool: + if not isinstance(other, _BaseVersion): + return NotImplemented + + return self._key < other._key + + def __le__(self, other: "_BaseVersion") -> bool: + if not isinstance(other, _BaseVersion): + return NotImplemented + + return self._key <= other._key + + def __eq__(self, other: object) -> bool: + if not isinstance(other, _BaseVersion): + return NotImplemented + + return self._key == other._key + + def __ge__(self, other: "_BaseVersion") -> bool: + if not isinstance(other, _BaseVersion): + return NotImplemented + + return self._key >= other._key + + def __gt__(self, other: "_BaseVersion") -> bool: + if not isinstance(other, _BaseVersion): + return NotImplemented + + return self._key > other._key + + def __ne__(self, other: object) -> bool: + if not isinstance(other, _BaseVersion): + return NotImplemented + + return self._key != other._key + + +# 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
+            [-_\.]?
+            (?Palpha|a|beta|b|preview|pre|c|rc)
+            [-_\.]?
+            (?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
+"""
+
+VERSION_PATTERN = _VERSION_PATTERN
+"""
+A string containing the regular expression used to match a valid version.
+
+The pattern is not anchored at either end, and is intended for embedding in larger
+expressions (for example, matching a version number as part of a file name). The
+regular expression should be compiled with the ``re.VERBOSE`` and ``re.IGNORECASE``
+flags set.
+
+:meta hide-value:
+"""
+
+
+class Version(_BaseVersion):
+    """This class abstracts handling of a project's versions.
+
+    A :class:`Version` instance is comparison aware and can be compared and
+    sorted using the standard Python interfaces.
+
+    >>> v1 = Version("1.0a5")
+    >>> v2 = Version("1.0")
+    >>> v1
+    
+    >>> v2
+    
+    >>> v1 < v2
+    True
+    >>> v1 == v2
+    False
+    >>> v1 > v2
+    False
+    >>> v1 >= v2
+    False
+    >>> v1 <= v2
+    True
+    """
+
+    _regex = re.compile(r"^\s*" + VERSION_PATTERN + r"\s*$", re.VERBOSE | re.IGNORECASE)
+    _key: CmpKey
+
+    def __init__(self, version: str) -> None:
+        """Initialize a Version object.
+
+        :param version:
+            The string representation of a version which will be parsed and normalized
+            before use.
+        :raises InvalidVersion:
+            If the ``version`` does not conform to PEP 440 in any way then this
+            exception will be raised.
+        """
+
+        # Validate the version and parse it into pieces
+        match = self._regex.search(version)
+        if not match:
+            raise InvalidVersion(f"Invalid version: '{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) -> str:
+        """A representation of the Version that shows all internal state.
+
+        >>> Version('1.0.0')
+        
+        """
+        return f""
+
+    def __str__(self) -> str:
+        """A string representation of the version that can be rounded-tripped.
+
+        >>> str(Version("1.0a5"))
+        '1.0a5'
+        """
+        parts = []
+
+        # Epoch
+        if self.epoch != 0:
+            parts.append(f"{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(f".post{self.post}")
+
+        # Development release
+        if self.dev is not None:
+            parts.append(f".dev{self.dev}")
+
+        # Local version segment
+        if self.local is not None:
+            parts.append(f"+{self.local}")
+
+        return "".join(parts)
+
+    @property
+    def epoch(self) -> int:
+        """The epoch of the version.
+
+        >>> Version("2.0.0").epoch
+        0
+        >>> Version("1!2.0.0").epoch
+        1
+        """
+        return self._version.epoch
+
+    @property
+    def release(self) -> Tuple[int, ...]:
+        """The components of the "release" segment of the version.
+
+        >>> Version("1.2.3").release
+        (1, 2, 3)
+        >>> Version("2.0.0").release
+        (2, 0, 0)
+        >>> Version("1!2.0.0.post0").release
+        (2, 0, 0)
+
+        Includes trailing zeroes but not the epoch or any pre-release / development /
+        post-release suffixes.
+        """
+        return self._version.release
+
+    @property
+    def pre(self) -> Optional[Tuple[str, int]]:
+        """The pre-release segment of the version.
+
+        >>> print(Version("1.2.3").pre)
+        None
+        >>> Version("1.2.3a1").pre
+        ('a', 1)
+        >>> Version("1.2.3b1").pre
+        ('b', 1)
+        >>> Version("1.2.3rc1").pre
+        ('rc', 1)
+        """
+        return self._version.pre
+
+    @property
+    def post(self) -> Optional[int]:
+        """The post-release number of the version.
+
+        >>> print(Version("1.2.3").post)
+        None
+        >>> Version("1.2.3.post1").post
+        1
+        """
+        return self._version.post[1] if self._version.post else None
+
+    @property
+    def dev(self) -> Optional[int]:
+        """The development number of the version.
+
+        >>> print(Version("1.2.3").dev)
+        None
+        >>> Version("1.2.3.dev1").dev
+        1
+        """
+        return self._version.dev[1] if self._version.dev else None
+
+    @property
+    def local(self) -> Optional[str]:
+        """The local version segment of the version.
+
+        >>> print(Version("1.2.3").local)
+        None
+        >>> Version("1.2.3+abc").local
+        'abc'
+        """
+        if self._version.local:
+            return ".".join(str(x) for x in self._version.local)
+        else:
+            return None
+
+    @property
+    def public(self) -> str:
+        """The public portion of the version.
+
+        >>> Version("1.2.3").public
+        '1.2.3'
+        >>> Version("1.2.3+abc").public
+        '1.2.3'
+        >>> Version("1.2.3+abc.dev1").public
+        '1.2.3'
+        """
+        return str(self).split("+", 1)[0]
+
+    @property
+    def base_version(self) -> str:
+        """The "base version" of the version.
+
+        >>> Version("1.2.3").base_version
+        '1.2.3'
+        >>> Version("1.2.3+abc").base_version
+        '1.2.3'
+        >>> Version("1!1.2.3+abc.dev1").base_version
+        '1!1.2.3'
+
+        The "base version" is the public version of the project without any pre or post
+        release markers.
+        """
+        parts = []
+
+        # Epoch
+        if self.epoch != 0:
+            parts.append(f"{self.epoch}!")
+
+        # Release segment
+        parts.append(".".join(str(x) for x in self.release))
+
+        return "".join(parts)
+
+    @property
+    def is_prerelease(self) -> bool:
+        """Whether this version is a pre-release.
+
+        >>> Version("1.2.3").is_prerelease
+        False
+        >>> Version("1.2.3a1").is_prerelease
+        True
+        >>> Version("1.2.3b1").is_prerelease
+        True
+        >>> Version("1.2.3rc1").is_prerelease
+        True
+        >>> Version("1.2.3dev1").is_prerelease
+        True
+        """
+        return self.dev is not None or self.pre is not None
+
+    @property
+    def is_postrelease(self) -> bool:
+        """Whether this version is a post-release.
+
+        >>> Version("1.2.3").is_postrelease
+        False
+        >>> Version("1.2.3.post1").is_postrelease
+        True
+        """
+        return self.post is not None
+
+    @property
+    def is_devrelease(self) -> bool:
+        """Whether this version is a development release.
+
+        >>> Version("1.2.3").is_devrelease
+        False
+        >>> Version("1.2.3.dev1").is_devrelease
+        True
+        """
+        return self.dev is not None
+
+    @property
+    def major(self) -> int:
+        """The first item of :attr:`release` or ``0`` if unavailable.
+
+        >>> Version("1.2.3").major
+        1
+        """
+        return self.release[0] if len(self.release) >= 1 else 0
+
+    @property
+    def minor(self) -> int:
+        """The second item of :attr:`release` or ``0`` if unavailable.
+
+        >>> Version("1.2.3").minor
+        2
+        >>> Version("1").minor
+        0
+        """
+        return self.release[1] if len(self.release) >= 2 else 0
+
+    @property
+    def micro(self) -> int:
+        """The third item of :attr:`release` or ``0`` if unavailable.
+
+        >>> Version("1.2.3").micro
+        3
+        >>> Version("1").micro
+        0
+        """
+        return self.release[2] if len(self.release) >= 3 else 0
+
+
+def _parse_letter_version(
+    letter: Optional[str], number: Union[str, bytes, SupportsInt, None]
+) -> 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: Optional[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: int,
+    release: Tuple[int, ...],
+    pre: Optional[Tuple[str, int]],
+    post: Optional[Tuple[str, int]],
+    dev: Optional[Tuple[str, int]],
+    local: Optional[LocalType],
+) -> 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: CmpPrePostDevType = NegativeInfinity
+    # 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: CmpPrePostDevType = NegativeInfinity
+
+    else:
+        _post = post
+
+    # Versions without a development segment should sort after those with one.
+    if dev is None:
+        _dev: CmpPrePostDevType = Infinity
+
+    else:
+        _dev = dev
+
+    if local is None:
+        # Versions without a local segment should sort before those with one.
+        _local: CmpLocalType = NegativeInfinity
+    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/site-packages/setuptools/_vendor/tomli/__init__.py b/venv/Lib/site-packages/setuptools/_vendor/tomli/__init__.py
new file mode 100644
index 0000000..4c6ec97
--- /dev/null
+++ b/venv/Lib/site-packages/setuptools/_vendor/tomli/__init__.py
@@ -0,0 +1,11 @@
+# SPDX-License-Identifier: MIT
+# SPDX-FileCopyrightText: 2021 Taneli Hukkinen
+# Licensed to PSF under a Contributor Agreement.
+
+__all__ = ("loads", "load", "TOMLDecodeError")
+__version__ = "2.0.1"  # DO NOT EDIT THIS LINE MANUALLY. LET bump2version UTILITY DO IT
+
+from ._parser import TOMLDecodeError, load, loads
+
+# Pretend this exception was created here.
+TOMLDecodeError.__module__ = __name__
diff --git a/venv/Lib/site-packages/setuptools/_vendor/tomli/__pycache__/__init__.cpython-312.pyc b/venv/Lib/site-packages/setuptools/_vendor/tomli/__pycache__/__init__.cpython-312.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..440db95824bad12ac1923edfb6bcbb321bb882d3
GIT binary patch
literal 406
zcmZ8cu};G<5OwN=RxNEO76v31qzp+YBSI`KTY(O+M3zozY$^*U4t7%1PW=FOzJYIH
zVn9OK5CdYX)U6W^(1jb`y(itfd-vRKH<2mz?%h0C2z^vyQ?);`@(p=J6orUdp+##H
zi&}z1oYhgRzV6mj^Oev}6&XIrU_rAz-)eE;#)&U|R0Lm7?lqt<+YD68fNNJ}28OjDg8YRq3GyxJx3t{*ZTA5{N
zeb+xG*T$rOkPKv8BrMG}=}XGf2^sM?FBJ1e#e6Q9chAO}=Zs8QIweCsCYAECD87v*
zOcTxWBFm)|IxxXfDpionMDPHBr#uJnv)Q_F@*Cn*bxeZlNxg4ii7m@oHc=~B)ZaQg
Xi`FOVy`bK53+){&y02(gZJViY#a42W

literal 0
HcmV?d00001

diff --git a/venv/Lib/site-packages/setuptools/_vendor/tomli/__pycache__/_parser.cpython-312.pyc b/venv/Lib/site-packages/setuptools/_vendor/tomli/__pycache__/_parser.cpython-312.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..789deecd453f0fd414584aa1033511c15c0a04fc
GIT binary patch
literal 26949
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

literal 0
HcmV?d00001

diff --git a/venv/Lib/site-packages/setuptools/_vendor/tomli/__pycache__/_re.cpython-312.pyc b/venv/Lib/site-packages/setuptools/_vendor/tomli/__pycache__/_re.cpython-312.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..cfbddd8fbc430a0830b51d3f02b0df87b2ac7d08
GIT binary patch
literal 3930
zcmai1?Qavw8J~Ue+uDxfID`Pl-O?1u1{@Mz3Iy&V$ji|?$f1P0o*YNc#=A+JTYIC~
zH6>{7Rc6}C(#8eXs%FZLpO6JTr#fD|{o`kENFd|v4#A4|+wIV?nYV0R9
zDeB|nh6;J;7SfedMh^gB2;vZ8Ha`XK7sx_WwX;+NA@{5#LE2_)*5+*9_SjzAXZ!7d
zE!aU5bd;OLtW-4A@YomdIG1PF*n9$U01{NynyF*
zd3MyUw;HIO^h1yGx9UI)08l+AavB;0NW5p&H^xvX)o-Fb28IxM)tQ
z6?KXZlK^#MTE|#5px_mMmtOgCIBxnm_$kPJKhPb
z=cf&agOiMTBI41e@`mXIC=@Cwvw(@a4wpC6m?U0mbUf5I$3LUWIAxgF31s!0IY~*M
zBF|95KdI--vqCnL%bRL>+ECM!0A?EN2Biaqyp~TX?x+)#PP0cR6`JGnOQy__Un;MO6Ae3e?Sq=0cyC)W
zc-Q^)>;CAvKSGHHNyvg+Hu
z5s5AIF7929twr{(`uA=$?^-;!6#mM%+4beMZ@9bf{xP)BGP|$X`=I^A&xRIuejD9$
zFS=)W&+^b(RJtE-yDJy_ANZO!-q^by+O@j7{dQ!NV|NR4>|-wyVjJOY^U1m79Q!aF
zon<$Im93VQoBg+Y?l%32y&rskm4AN|JR{etFDqX$G6gkV1ts8(M;5d5*o-zk8frUr^g(k6%
zhZK?lw$c(dB#xd2Oi&5Opp$sKi_mXC=lH8t%M;2V=ZTaUK0rjax!sR|Fit~PL=Wqm
z7khplnd@90EcQagHXxM^O*dkTA202`*RXHBvH8Z4#llkey~egD9xfnka-NX4gghQE
zj)Q_{?g(NN4c#|Dy!MU&+uS?C0v~FE$xl`B9TZiX!YJ(X0iy}=m{o+X(NPU`4eyn=
zi{)7%(SinP;0JI$P&i6DidQ^tAT)_MCWjXjG?Tn|JE5xyz`M!tP0~rw8AOQ0zmfr`
zp?cmlJuPblaPV=E86(02&=t{o$FVt3SEAaO%GAtp|~vi^qO_>X)aM-d>AHtA1%CST~!WzdCpIGkZ~93yQ0}
zNP`Baie&bwA8_&lxknu@1}w$7Lc~Q%(V{n;K#In8?2%e
zzfBmFTU$evdx}hjtMla1CGBv8RBAj=P(PKz1ghvQNbgEog=H)tJJ357!kN#dDd=PJ
z(r{Qz{>zw|hU)_pjgB`3Bh{cAIiVDJEu`S(Fm!3q@ul_bbVgGh-$#9eXFnL~a{@3k
z@Zp8CeS=O2$nzicoHj}3Zjl<|d>Gfly75sf5;o~QoPSzXC%>O1&6%o7AP
zeg|ENV;JT?0%BVJh5Elm{okRt{)z(Mp{9SLH%kJl|ClL;9)_A`&(6O$_g;yOc=(ct
zg5mkLxwa=Q4PmwzDD6Y_J7@8HVXm-va4oXG7+SAuo*kQ?nwu)Iyx=MMk+-2ZP(oac
zDFu9>px#vw6C!LexVckg&NGku8N>z`j^1aQ|IPXs?Y-S6M>F
z+mgjJYIoI>AX;g@uAjy%%qX1kU3DL;7AFdp1Y`VdB78hYM@s|k>=&Yd*Sq3zZXHg{
Wg|R(zioMy@lugLOXiN#W)YdP)b8z_p

literal 0
HcmV?d00001

diff --git a/venv/Lib/site-packages/setuptools/_vendor/tomli/_parser.py b/venv/Lib/site-packages/setuptools/_vendor/tomli/_parser.py
new file mode 100644
index 0000000..f1bb0aa
--- /dev/null
+++ b/venv/Lib/site-packages/setuptools/_vendor/tomli/_parser.py
@@ -0,0 +1,691 @@
+# SPDX-License-Identifier: MIT
+# SPDX-FileCopyrightText: 2021 Taneli Hukkinen
+# Licensed to PSF under a Contributor Agreement.
+
+from __future__ import annotations
+
+from collections.abc import Iterable
+import string
+from types import MappingProxyType
+from typing import Any, BinaryIO, NamedTuple
+
+from ._re import (
+    RE_DATETIME,
+    RE_LOCALTIME,
+    RE_NUMBER,
+    match_to_datetime,
+    match_to_localtime,
+    match_to_number,
+)
+from ._types import Key, ParseFloat, Pos
+
+ASCII_CTRL = frozenset(chr(i) for i in range(32)) | frozenset(chr(127))
+
+# Neither of these sets include quotation mark or backslash. They are
+# currently handled as separate cases in the parser functions.
+ILLEGAL_BASIC_STR_CHARS = ASCII_CTRL - frozenset("\t")
+ILLEGAL_MULTILINE_BASIC_STR_CHARS = ASCII_CTRL - frozenset("\t\n")
+
+ILLEGAL_LITERAL_STR_CHARS = ILLEGAL_BASIC_STR_CHARS
+ILLEGAL_MULTILINE_LITERAL_STR_CHARS = ILLEGAL_MULTILINE_BASIC_STR_CHARS
+
+ILLEGAL_COMMENT_CHARS = ILLEGAL_BASIC_STR_CHARS
+
+TOML_WS = frozenset(" \t")
+TOML_WS_AND_NEWLINE = TOML_WS | frozenset("\n")
+BARE_KEY_CHARS = frozenset(string.ascii_letters + string.digits + "-_")
+KEY_INITIAL_CHARS = BARE_KEY_CHARS | frozenset("\"'")
+HEXDIGIT_CHARS = frozenset(string.hexdigits)
+
+BASIC_STR_ESCAPE_REPLACEMENTS = MappingProxyType(
+    {
+        "\\b": "\u0008",  # backspace
+        "\\t": "\u0009",  # tab
+        "\\n": "\u000A",  # linefeed
+        "\\f": "\u000C",  # form feed
+        "\\r": "\u000D",  # carriage return
+        '\\"': "\u0022",  # quote
+        "\\\\": "\u005C",  # backslash
+    }
+)
+
+
+class TOMLDecodeError(ValueError):
+    """An error raised if a document is not valid TOML."""
+
+
+def load(__fp: BinaryIO, *, parse_float: ParseFloat = float) -> dict[str, Any]:
+    """Parse TOML from a binary file object."""
+    b = __fp.read()
+    try:
+        s = b.decode()
+    except AttributeError:
+        raise TypeError(
+            "File must be opened in binary mode, e.g. use `open('foo.toml', 'rb')`"
+        ) from None
+    return loads(s, parse_float=parse_float)
+
+
+def loads(__s: str, *, parse_float: ParseFloat = float) -> dict[str, Any]:  # noqa: C901
+    """Parse TOML from a string."""
+
+    # The spec allows converting "\r\n" to "\n", even in string
+    # literals. Let's do so to simplify parsing.
+    src = __s.replace("\r\n", "\n")
+    pos = 0
+    out = Output(NestedDict(), Flags())
+    header: Key = ()
+    parse_float = make_safe_parse_float(parse_float)
+
+    # Parse one statement at a time
+    # (typically means one line in TOML source)
+    while True:
+        # 1. Skip line leading whitespace
+        pos = skip_chars(src, pos, TOML_WS)
+
+        # 2. Parse rules. Expect one of the following:
+        #    - end of file
+        #    - end of line
+        #    - comment
+        #    - key/value pair
+        #    - append dict to list (and move to its namespace)
+        #    - create dict (and move to its namespace)
+        # Skip trailing whitespace when applicable.
+        try:
+            char = src[pos]
+        except IndexError:
+            break
+        if char == "\n":
+            pos += 1
+            continue
+        if char in KEY_INITIAL_CHARS:
+            pos = key_value_rule(src, pos, out, header, parse_float)
+            pos = skip_chars(src, pos, TOML_WS)
+        elif char == "[":
+            try:
+                second_char: str | None = src[pos + 1]
+            except IndexError:
+                second_char = None
+            out.flags.finalize_pending()
+            if second_char == "[":
+                pos, header = create_list_rule(src, pos, out)
+            else:
+                pos, header = create_dict_rule(src, pos, out)
+            pos = skip_chars(src, pos, TOML_WS)
+        elif char != "#":
+            raise suffixed_err(src, pos, "Invalid statement")
+
+        # 3. Skip comment
+        pos = skip_comment(src, pos)
+
+        # 4. Expect end of line or end of file
+        try:
+            char = src[pos]
+        except IndexError:
+            break
+        if char != "\n":
+            raise suffixed_err(
+                src, pos, "Expected newline or end of document after a statement"
+            )
+        pos += 1
+
+    return out.data.dict
+
+
+class Flags:
+    """Flags that map to parsed keys/namespaces."""
+
+    # Marks an immutable namespace (inline array or inline table).
+    FROZEN = 0
+    # Marks a nest that has been explicitly created and can no longer
+    # be opened using the "[table]" syntax.
+    EXPLICIT_NEST = 1
+
+    def __init__(self) -> None:
+        self._flags: dict[str, dict] = {}
+        self._pending_flags: set[tuple[Key, int]] = set()
+
+    def add_pending(self, key: Key, flag: int) -> None:
+        self._pending_flags.add((key, flag))
+
+    def finalize_pending(self) -> None:
+        for key, flag in self._pending_flags:
+            self.set(key, flag, recursive=False)
+        self._pending_flags.clear()
+
+    def unset_all(self, key: Key) -> None:
+        cont = self._flags
+        for k in key[:-1]:
+            if k not in cont:
+                return
+            cont = cont[k]["nested"]
+        cont.pop(key[-1], None)
+
+    def set(self, key: Key, flag: int, *, recursive: bool) -> None:  # noqa: A003
+        cont = self._flags
+        key_parent, key_stem = key[:-1], key[-1]
+        for k in key_parent:
+            if k not in cont:
+                cont[k] = {"flags": set(), "recursive_flags": set(), "nested": {}}
+            cont = cont[k]["nested"]
+        if key_stem not in cont:
+            cont[key_stem] = {"flags": set(), "recursive_flags": set(), "nested": {}}
+        cont[key_stem]["recursive_flags" if recursive else "flags"].add(flag)
+
+    def is_(self, key: Key, flag: int) -> bool:
+        if not key:
+            return False  # document root has no flags
+        cont = self._flags
+        for k in key[:-1]:
+            if k not in cont:
+                return False
+            inner_cont = cont[k]
+            if flag in inner_cont["recursive_flags"]:
+                return True
+            cont = inner_cont["nested"]
+        key_stem = key[-1]
+        if key_stem in cont:
+            cont = cont[key_stem]
+            return flag in cont["flags"] or flag in cont["recursive_flags"]
+        return False
+
+
+class NestedDict:
+    def __init__(self) -> None:
+        # The parsed content of the TOML document
+        self.dict: dict[str, Any] = {}
+
+    def get_or_create_nest(
+        self,
+        key: Key,
+        *,
+        access_lists: bool = True,
+    ) -> dict:
+        cont: Any = self.dict
+        for k in key:
+            if k not in cont:
+                cont[k] = {}
+            cont = cont[k]
+            if access_lists and isinstance(cont, list):
+                cont = cont[-1]
+            if not isinstance(cont, dict):
+                raise KeyError("There is no nest behind this key")
+        return cont
+
+    def append_nest_to_list(self, key: Key) -> None:
+        cont = self.get_or_create_nest(key[:-1])
+        last_key = key[-1]
+        if last_key in cont:
+            list_ = cont[last_key]
+            if not isinstance(list_, list):
+                raise KeyError("An object other than list found behind this key")
+            list_.append({})
+        else:
+            cont[last_key] = [{}]
+
+
+class Output(NamedTuple):
+    data: NestedDict
+    flags: Flags
+
+
+def skip_chars(src: str, pos: Pos, chars: Iterable[str]) -> Pos:
+    try:
+        while src[pos] in chars:
+            pos += 1
+    except IndexError:
+        pass
+    return pos
+
+
+def skip_until(
+    src: str,
+    pos: Pos,
+    expect: str,
+    *,
+    error_on: frozenset[str],
+    error_on_eof: bool,
+) -> Pos:
+    try:
+        new_pos = src.index(expect, pos)
+    except ValueError:
+        new_pos = len(src)
+        if error_on_eof:
+            raise suffixed_err(src, new_pos, f"Expected {expect!r}") from None
+
+    if not error_on.isdisjoint(src[pos:new_pos]):
+        while src[pos] not in error_on:
+            pos += 1
+        raise suffixed_err(src, pos, f"Found invalid character {src[pos]!r}")
+    return new_pos
+
+
+def skip_comment(src: str, pos: Pos) -> Pos:
+    try:
+        char: str | None = src[pos]
+    except IndexError:
+        char = None
+    if char == "#":
+        return skip_until(
+            src, pos + 1, "\n", error_on=ILLEGAL_COMMENT_CHARS, error_on_eof=False
+        )
+    return pos
+
+
+def skip_comments_and_array_ws(src: str, pos: Pos) -> Pos:
+    while True:
+        pos_before_skip = pos
+        pos = skip_chars(src, pos, TOML_WS_AND_NEWLINE)
+        pos = skip_comment(src, pos)
+        if pos == pos_before_skip:
+            return pos
+
+
+def create_dict_rule(src: str, pos: Pos, out: Output) -> tuple[Pos, Key]:
+    pos += 1  # Skip "["
+    pos = skip_chars(src, pos, TOML_WS)
+    pos, key = parse_key(src, pos)
+
+    if out.flags.is_(key, Flags.EXPLICIT_NEST) or out.flags.is_(key, Flags.FROZEN):
+        raise suffixed_err(src, pos, f"Cannot declare {key} twice")
+    out.flags.set(key, Flags.EXPLICIT_NEST, recursive=False)
+    try:
+        out.data.get_or_create_nest(key)
+    except KeyError:
+        raise suffixed_err(src, pos, "Cannot overwrite a value") from None
+
+    if not src.startswith("]", pos):
+        raise suffixed_err(src, pos, "Expected ']' at the end of a table declaration")
+    return pos + 1, key
+
+
+def create_list_rule(src: str, pos: Pos, out: Output) -> tuple[Pos, Key]:
+    pos += 2  # Skip "[["
+    pos = skip_chars(src, pos, TOML_WS)
+    pos, key = parse_key(src, pos)
+
+    if out.flags.is_(key, Flags.FROZEN):
+        raise suffixed_err(src, pos, f"Cannot mutate immutable namespace {key}")
+    # Free the namespace now that it points to another empty list item...
+    out.flags.unset_all(key)
+    # ...but this key precisely is still prohibited from table declaration
+    out.flags.set(key, Flags.EXPLICIT_NEST, recursive=False)
+    try:
+        out.data.append_nest_to_list(key)
+    except KeyError:
+        raise suffixed_err(src, pos, "Cannot overwrite a value") from None
+
+    if not src.startswith("]]", pos):
+        raise suffixed_err(src, pos, "Expected ']]' at the end of an array declaration")
+    return pos + 2, key
+
+
+def key_value_rule(
+    src: str, pos: Pos, out: Output, header: Key, parse_float: ParseFloat
+) -> Pos:
+    pos, key, value = parse_key_value_pair(src, pos, parse_float)
+    key_parent, key_stem = key[:-1], key[-1]
+    abs_key_parent = header + key_parent
+
+    relative_path_cont_keys = (header + key[:i] for i in range(1, len(key)))
+    for cont_key in relative_path_cont_keys:
+        # Check that dotted key syntax does not redefine an existing table
+        if out.flags.is_(cont_key, Flags.EXPLICIT_NEST):
+            raise suffixed_err(src, pos, f"Cannot redefine namespace {cont_key}")
+        # Containers in the relative path can't be opened with the table syntax or
+        # dotted key/value syntax in following table sections.
+        out.flags.add_pending(cont_key, Flags.EXPLICIT_NEST)
+
+    if out.flags.is_(abs_key_parent, Flags.FROZEN):
+        raise suffixed_err(
+            src, pos, f"Cannot mutate immutable namespace {abs_key_parent}"
+        )
+
+    try:
+        nest = out.data.get_or_create_nest(abs_key_parent)
+    except KeyError:
+        raise suffixed_err(src, pos, "Cannot overwrite a value") from None
+    if key_stem in nest:
+        raise suffixed_err(src, pos, "Cannot overwrite a value")
+    # Mark inline table and array namespaces recursively immutable
+    if isinstance(value, (dict, list)):
+        out.flags.set(header + key, Flags.FROZEN, recursive=True)
+    nest[key_stem] = value
+    return pos
+
+
+def parse_key_value_pair(
+    src: str, pos: Pos, parse_float: ParseFloat
+) -> tuple[Pos, Key, Any]:
+    pos, key = parse_key(src, pos)
+    try:
+        char: str | None = src[pos]
+    except IndexError:
+        char = None
+    if char != "=":
+        raise suffixed_err(src, pos, "Expected '=' after a key in a key/value pair")
+    pos += 1
+    pos = skip_chars(src, pos, TOML_WS)
+    pos, value = parse_value(src, pos, parse_float)
+    return pos, key, value
+
+
+def parse_key(src: str, pos: Pos) -> tuple[Pos, Key]:
+    pos, key_part = parse_key_part(src, pos)
+    key: Key = (key_part,)
+    pos = skip_chars(src, pos, TOML_WS)
+    while True:
+        try:
+            char: str | None = src[pos]
+        except IndexError:
+            char = None
+        if char != ".":
+            return pos, key
+        pos += 1
+        pos = skip_chars(src, pos, TOML_WS)
+        pos, key_part = parse_key_part(src, pos)
+        key += (key_part,)
+        pos = skip_chars(src, pos, TOML_WS)
+
+
+def parse_key_part(src: str, pos: Pos) -> tuple[Pos, str]:
+    try:
+        char: str | None = src[pos]
+    except IndexError:
+        char = None
+    if char in BARE_KEY_CHARS:
+        start_pos = pos
+        pos = skip_chars(src, pos, BARE_KEY_CHARS)
+        return pos, src[start_pos:pos]
+    if char == "'":
+        return parse_literal_str(src, pos)
+    if char == '"':
+        return parse_one_line_basic_str(src, pos)
+    raise suffixed_err(src, pos, "Invalid initial character for a key part")
+
+
+def parse_one_line_basic_str(src: str, pos: Pos) -> tuple[Pos, str]:
+    pos += 1
+    return parse_basic_str(src, pos, multiline=False)
+
+
+def parse_array(src: str, pos: Pos, parse_float: ParseFloat) -> tuple[Pos, list]:
+    pos += 1
+    array: list = []
+
+    pos = skip_comments_and_array_ws(src, pos)
+    if src.startswith("]", pos):
+        return pos + 1, array
+    while True:
+        pos, val = parse_value(src, pos, parse_float)
+        array.append(val)
+        pos = skip_comments_and_array_ws(src, pos)
+
+        c = src[pos : pos + 1]
+        if c == "]":
+            return pos + 1, array
+        if c != ",":
+            raise suffixed_err(src, pos, "Unclosed array")
+        pos += 1
+
+        pos = skip_comments_and_array_ws(src, pos)
+        if src.startswith("]", pos):
+            return pos + 1, array
+
+
+def parse_inline_table(src: str, pos: Pos, parse_float: ParseFloat) -> tuple[Pos, dict]:
+    pos += 1
+    nested_dict = NestedDict()
+    flags = Flags()
+
+    pos = skip_chars(src, pos, TOML_WS)
+    if src.startswith("}", pos):
+        return pos + 1, nested_dict.dict
+    while True:
+        pos, key, value = parse_key_value_pair(src, pos, parse_float)
+        key_parent, key_stem = key[:-1], key[-1]
+        if flags.is_(key, Flags.FROZEN):
+            raise suffixed_err(src, pos, f"Cannot mutate immutable namespace {key}")
+        try:
+            nest = nested_dict.get_or_create_nest(key_parent, access_lists=False)
+        except KeyError:
+            raise suffixed_err(src, pos, "Cannot overwrite a value") from None
+        if key_stem in nest:
+            raise suffixed_err(src, pos, f"Duplicate inline table key {key_stem!r}")
+        nest[key_stem] = value
+        pos = skip_chars(src, pos, TOML_WS)
+        c = src[pos : pos + 1]
+        if c == "}":
+            return pos + 1, nested_dict.dict
+        if c != ",":
+            raise suffixed_err(src, pos, "Unclosed inline table")
+        if isinstance(value, (dict, list)):
+            flags.set(key, Flags.FROZEN, recursive=True)
+        pos += 1
+        pos = skip_chars(src, pos, TOML_WS)
+
+
+def parse_basic_str_escape(
+    src: str, pos: Pos, *, multiline: bool = False
+) -> tuple[Pos, str]:
+    escape_id = src[pos : pos + 2]
+    pos += 2
+    if multiline and escape_id in {"\\ ", "\\\t", "\\\n"}:
+        # Skip whitespace until next non-whitespace character or end of
+        # the doc. Error if non-whitespace is found before newline.
+        if escape_id != "\\\n":
+            pos = skip_chars(src, pos, TOML_WS)
+            try:
+                char = src[pos]
+            except IndexError:
+                return pos, ""
+            if char != "\n":
+                raise suffixed_err(src, pos, "Unescaped '\\' in a string")
+            pos += 1
+        pos = skip_chars(src, pos, TOML_WS_AND_NEWLINE)
+        return pos, ""
+    if escape_id == "\\u":
+        return parse_hex_char(src, pos, 4)
+    if escape_id == "\\U":
+        return parse_hex_char(src, pos, 8)
+    try:
+        return pos, BASIC_STR_ESCAPE_REPLACEMENTS[escape_id]
+    except KeyError:
+        raise suffixed_err(src, pos, "Unescaped '\\' in a string") from None
+
+
+def parse_basic_str_escape_multiline(src: str, pos: Pos) -> tuple[Pos, str]:
+    return parse_basic_str_escape(src, pos, multiline=True)
+
+
+def parse_hex_char(src: str, pos: Pos, hex_len: int) -> tuple[Pos, str]:
+    hex_str = src[pos : pos + hex_len]
+    if len(hex_str) != hex_len or not HEXDIGIT_CHARS.issuperset(hex_str):
+        raise suffixed_err(src, pos, "Invalid hex value")
+    pos += hex_len
+    hex_int = int(hex_str, 16)
+    if not is_unicode_scalar_value(hex_int):
+        raise suffixed_err(src, pos, "Escaped character is not a Unicode scalar value")
+    return pos, chr(hex_int)
+
+
+def parse_literal_str(src: str, pos: Pos) -> tuple[Pos, str]:
+    pos += 1  # Skip starting apostrophe
+    start_pos = pos
+    pos = skip_until(
+        src, pos, "'", error_on=ILLEGAL_LITERAL_STR_CHARS, error_on_eof=True
+    )
+    return pos + 1, src[start_pos:pos]  # Skip ending apostrophe
+
+
+def parse_multiline_str(src: str, pos: Pos, *, literal: bool) -> tuple[Pos, str]:
+    pos += 3
+    if src.startswith("\n", pos):
+        pos += 1
+
+    if literal:
+        delim = "'"
+        end_pos = skip_until(
+            src,
+            pos,
+            "'''",
+            error_on=ILLEGAL_MULTILINE_LITERAL_STR_CHARS,
+            error_on_eof=True,
+        )
+        result = src[pos:end_pos]
+        pos = end_pos + 3
+    else:
+        delim = '"'
+        pos, result = parse_basic_str(src, pos, multiline=True)
+
+    # Add at maximum two extra apostrophes/quotes if the end sequence
+    # is 4 or 5 chars long instead of just 3.
+    if not src.startswith(delim, pos):
+        return pos, result
+    pos += 1
+    if not src.startswith(delim, pos):
+        return pos, result + delim
+    pos += 1
+    return pos, result + (delim * 2)
+
+
+def parse_basic_str(src: str, pos: Pos, *, multiline: bool) -> tuple[Pos, str]:
+    if multiline:
+        error_on = ILLEGAL_MULTILINE_BASIC_STR_CHARS
+        parse_escapes = parse_basic_str_escape_multiline
+    else:
+        error_on = ILLEGAL_BASIC_STR_CHARS
+        parse_escapes = parse_basic_str_escape
+    result = ""
+    start_pos = pos
+    while True:
+        try:
+            char = src[pos]
+        except IndexError:
+            raise suffixed_err(src, pos, "Unterminated string") from None
+        if char == '"':
+            if not multiline:
+                return pos + 1, result + src[start_pos:pos]
+            if src.startswith('"""', pos):
+                return pos + 3, result + src[start_pos:pos]
+            pos += 1
+            continue
+        if char == "\\":
+            result += src[start_pos:pos]
+            pos, parsed_escape = parse_escapes(src, pos)
+            result += parsed_escape
+            start_pos = pos
+            continue
+        if char in error_on:
+            raise suffixed_err(src, pos, f"Illegal character {char!r}")
+        pos += 1
+
+
+def parse_value(  # noqa: C901
+    src: str, pos: Pos, parse_float: ParseFloat
+) -> tuple[Pos, Any]:
+    try:
+        char: str | None = src[pos]
+    except IndexError:
+        char = None
+
+    # IMPORTANT: order conditions based on speed of checking and likelihood
+
+    # Basic strings
+    if char == '"':
+        if src.startswith('"""', pos):
+            return parse_multiline_str(src, pos, literal=False)
+        return parse_one_line_basic_str(src, pos)
+
+    # Literal strings
+    if char == "'":
+        if src.startswith("'''", pos):
+            return parse_multiline_str(src, pos, literal=True)
+        return parse_literal_str(src, pos)
+
+    # Booleans
+    if char == "t":
+        if src.startswith("true", pos):
+            return pos + 4, True
+    if char == "f":
+        if src.startswith("false", pos):
+            return pos + 5, False
+
+    # Arrays
+    if char == "[":
+        return parse_array(src, pos, parse_float)
+
+    # Inline tables
+    if char == "{":
+        return parse_inline_table(src, pos, parse_float)
+
+    # Dates and times
+    datetime_match = RE_DATETIME.match(src, pos)
+    if datetime_match:
+        try:
+            datetime_obj = match_to_datetime(datetime_match)
+        except ValueError as e:
+            raise suffixed_err(src, pos, "Invalid date or datetime") from e
+        return datetime_match.end(), datetime_obj
+    localtime_match = RE_LOCALTIME.match(src, pos)
+    if localtime_match:
+        return localtime_match.end(), match_to_localtime(localtime_match)
+
+    # Integers and "normal" floats.
+    # The regex will greedily match any type starting with a decimal
+    # char, so needs to be located after handling of dates and times.
+    number_match = RE_NUMBER.match(src, pos)
+    if number_match:
+        return number_match.end(), match_to_number(number_match, parse_float)
+
+    # Special floats
+    first_three = src[pos : pos + 3]
+    if first_three in {"inf", "nan"}:
+        return pos + 3, parse_float(first_three)
+    first_four = src[pos : pos + 4]
+    if first_four in {"-inf", "+inf", "-nan", "+nan"}:
+        return pos + 4, parse_float(first_four)
+
+    raise suffixed_err(src, pos, "Invalid value")
+
+
+def suffixed_err(src: str, pos: Pos, msg: str) -> TOMLDecodeError:
+    """Return a `TOMLDecodeError` where error message is suffixed with
+    coordinates in source."""
+
+    def coord_repr(src: str, pos: Pos) -> str:
+        if pos >= len(src):
+            return "end of document"
+        line = src.count("\n", 0, pos) + 1
+        if line == 1:
+            column = pos + 1
+        else:
+            column = pos - src.rindex("\n", 0, pos)
+        return f"line {line}, column {column}"
+
+    return TOMLDecodeError(f"{msg} (at {coord_repr(src, pos)})")
+
+
+def is_unicode_scalar_value(codepoint: int) -> bool:
+    return (0 <= codepoint <= 55295) or (57344 <= codepoint <= 1114111)
+
+
+def make_safe_parse_float(parse_float: ParseFloat) -> ParseFloat:
+    """A decorator to make `parse_float` safe.
+
+    `parse_float` must not return dicts or lists, because these types
+    would be mixed with parsed TOML tables and arrays, thus confusing
+    the parser. The returned decorated callable raises `ValueError`
+    instead of returning illegal types.
+    """
+    # The default `float` callable never returns illegal types. Optimize it.
+    if parse_float is float:  # type: ignore[comparison-overlap]
+        return float
+
+    def safe_parse_float(float_str: str) -> Any:
+        float_value = parse_float(float_str)
+        if isinstance(float_value, (dict, list)):
+            raise ValueError("parse_float must not return dicts or lists")
+        return float_value
+
+    return safe_parse_float
diff --git a/venv/Lib/site-packages/setuptools/_vendor/tomli/_re.py b/venv/Lib/site-packages/setuptools/_vendor/tomli/_re.py
new file mode 100644
index 0000000..994bb74
--- /dev/null
+++ b/venv/Lib/site-packages/setuptools/_vendor/tomli/_re.py
@@ -0,0 +1,107 @@
+# SPDX-License-Identifier: MIT
+# SPDX-FileCopyrightText: 2021 Taneli Hukkinen
+# Licensed to PSF under a Contributor Agreement.
+
+from __future__ import annotations
+
+from datetime import date, datetime, time, timedelta, timezone, tzinfo
+from functools import lru_cache
+import re
+from typing import Any
+
+from ._types import ParseFloat
+
+# E.g.
+# - 00:32:00.999999
+# - 00:32:00
+_TIME_RE_STR = r"([01][0-9]|2[0-3]):([0-5][0-9]):([0-5][0-9])(?:\.([0-9]{1,6})[0-9]*)?"
+
+RE_NUMBER = re.compile(
+    r"""
+0
+(?:
+    x[0-9A-Fa-f](?:_?[0-9A-Fa-f])*   # hex
+    |
+    b[01](?:_?[01])*                 # bin
+    |
+    o[0-7](?:_?[0-7])*               # oct
+)
+|
+[+-]?(?:0|[1-9](?:_?[0-9])*)         # dec, integer part
+(?P
+    (?:\.[0-9](?:_?[0-9])*)?         # optional fractional part
+    (?:[eE][+-]?[0-9](?:_?[0-9])*)?  # optional exponent part
+)
+""",
+    flags=re.VERBOSE,
+)
+RE_LOCALTIME = re.compile(_TIME_RE_STR)
+RE_DATETIME = re.compile(
+    rf"""
+([0-9]{{4}})-(0[1-9]|1[0-2])-(0[1-9]|[12][0-9]|3[01])  # date, e.g. 1988-10-27
+(?:
+    [Tt ]
+    {_TIME_RE_STR}
+    (?:([Zz])|([+-])([01][0-9]|2[0-3]):([0-5][0-9]))?  # optional time offset
+)?
+""",
+    flags=re.VERBOSE,
+)
+
+
+def match_to_datetime(match: re.Match) -> datetime | date:
+    """Convert a `RE_DATETIME` match to `datetime.datetime` or `datetime.date`.
+
+    Raises ValueError if the match does not correspond to a valid date
+    or datetime.
+    """
+    (
+        year_str,
+        month_str,
+        day_str,
+        hour_str,
+        minute_str,
+        sec_str,
+        micros_str,
+        zulu_time,
+        offset_sign_str,
+        offset_hour_str,
+        offset_minute_str,
+    ) = match.groups()
+    year, month, day = int(year_str), int(month_str), int(day_str)
+    if hour_str is None:
+        return date(year, month, day)
+    hour, minute, sec = int(hour_str), int(minute_str), int(sec_str)
+    micros = int(micros_str.ljust(6, "0")) if micros_str else 0
+    if offset_sign_str:
+        tz: tzinfo | None = cached_tz(
+            offset_hour_str, offset_minute_str, offset_sign_str
+        )
+    elif zulu_time:
+        tz = timezone.utc
+    else:  # local date-time
+        tz = None
+    return datetime(year, month, day, hour, minute, sec, micros, tzinfo=tz)
+
+
+@lru_cache(maxsize=None)
+def cached_tz(hour_str: str, minute_str: str, sign_str: str) -> timezone:
+    sign = 1 if sign_str == "+" else -1
+    return timezone(
+        timedelta(
+            hours=sign * int(hour_str),
+            minutes=sign * int(minute_str),
+        )
+    )
+
+
+def match_to_localtime(match: re.Match) -> time:
+    hour_str, minute_str, sec_str, micros_str = match.groups()
+    micros = int(micros_str.ljust(6, "0")) if micros_str else 0
+    return time(int(hour_str), int(minute_str), int(sec_str), micros)
+
+
+def match_to_number(match: re.Match, parse_float: ParseFloat) -> Any:
+    if match.group("floatpart"):
+        return parse_float(match.group())
+    return int(match.group(), 0)
diff --git a/venv/Lib/site-packages/setuptools/_vendor/tomli/_types.py b/venv/Lib/site-packages/setuptools/_vendor/tomli/_types.py
new file mode 100644
index 0000000..d949412
--- /dev/null
+++ b/venv/Lib/site-packages/setuptools/_vendor/tomli/_types.py
@@ -0,0 +1,10 @@
+# SPDX-License-Identifier: MIT
+# SPDX-FileCopyrightText: 2021 Taneli Hukkinen
+# Licensed to PSF under a Contributor Agreement.
+
+from typing import Any, Callable, Tuple
+
+# Type annotations
+ParseFloat = Callable[[str], Any]
+Key = Tuple[str, ...]
+Pos = int
diff --git a/venv/Lib/site-packages/setuptools/_vendor/tomli/py.typed b/venv/Lib/site-packages/setuptools/_vendor/tomli/py.typed
new file mode 100644
index 0000000..7632ecf
--- /dev/null
+++ b/venv/Lib/site-packages/setuptools/_vendor/tomli/py.typed
@@ -0,0 +1 @@
+# Marker file for PEP 561
diff --git a/venv/Lib/site-packages/setuptools/_vendor/typing_extensions.py b/venv/Lib/site-packages/setuptools/_vendor/typing_extensions.py
new file mode 100644
index 0000000..9f1c7aa
--- /dev/null
+++ b/venv/Lib/site-packages/setuptools/_vendor/typing_extensions.py
@@ -0,0 +1,2296 @@
+import abc
+import collections
+import collections.abc
+import operator
+import sys
+import typing
+
+# After PEP 560, internal typing API was substantially reworked.
+# This is especially important for Protocol class which uses internal APIs
+# quite extensively.
+PEP_560 = sys.version_info[:3] >= (3, 7, 0)
+
+if PEP_560:
+    GenericMeta = type
+else:
+    # 3.6
+    from typing import GenericMeta, _type_vars  # noqa
+
+# The two functions below are copies of typing internal helpers.
+# They are needed by _ProtocolMeta
+
+
+def _no_slots_copy(dct):
+    dict_copy = dict(dct)
+    if '__slots__' in dict_copy:
+        for slot in dict_copy['__slots__']:
+            dict_copy.pop(slot, None)
+    return dict_copy
+
+
+def _check_generic(cls, parameters):
+    if not cls.__parameters__:
+        raise TypeError(f"{cls} is not a generic class")
+    alen = len(parameters)
+    elen = len(cls.__parameters__)
+    if alen != elen:
+        raise TypeError(f"Too {'many' if alen > elen else 'few'} arguments for {cls};"
+                        f" actual {alen}, expected {elen}")
+
+
+# Please keep __all__ alphabetized within each category.
+__all__ = [
+    # Super-special typing primitives.
+    'ClassVar',
+    'Concatenate',
+    'Final',
+    'ParamSpec',
+    'Self',
+    'Type',
+
+    # ABCs (from collections.abc).
+    'Awaitable',
+    'AsyncIterator',
+    'AsyncIterable',
+    'Coroutine',
+    'AsyncGenerator',
+    'AsyncContextManager',
+    'ChainMap',
+
+    # Concrete collection types.
+    'ContextManager',
+    'Counter',
+    'Deque',
+    'DefaultDict',
+    'OrderedDict',
+    'TypedDict',
+
+    # Structural checks, a.k.a. protocols.
+    'SupportsIndex',
+
+    # One-off things.
+    'Annotated',
+    'final',
+    'IntVar',
+    'Literal',
+    'NewType',
+    'overload',
+    'Protocol',
+    'runtime',
+    'runtime_checkable',
+    'Text',
+    'TypeAlias',
+    'TypeGuard',
+    'TYPE_CHECKING',
+]
+
+if PEP_560:
+    __all__.extend(["get_args", "get_origin", "get_type_hints"])
+
+# 3.6.2+
+if hasattr(typing, 'NoReturn'):
+    NoReturn = typing.NoReturn
+# 3.6.0-3.6.1
+else:
+    class _NoReturn(typing._FinalTypingBase, _root=True):
+        """Special type indicating functions that never return.
+        Example::
+
+          from typing import NoReturn
+
+          def stop() -> NoReturn:
+              raise Exception('no way')
+
+        This type is invalid in other positions, e.g., ``List[NoReturn]``
+        will fail in static type checkers.
+        """
+        __slots__ = ()
+
+        def __instancecheck__(self, obj):
+            raise TypeError("NoReturn cannot be used with isinstance().")
+
+        def __subclasscheck__(self, cls):
+            raise TypeError("NoReturn cannot be used with issubclass().")
+
+    NoReturn = _NoReturn(_root=True)
+
+# Some unconstrained type variables.  These are used by the container types.
+# (These are not for export.)
+T = typing.TypeVar('T')  # Any type.
+KT = typing.TypeVar('KT')  # Key type.
+VT = typing.TypeVar('VT')  # Value type.
+T_co = typing.TypeVar('T_co', covariant=True)  # Any type covariant containers.
+T_contra = typing.TypeVar('T_contra', contravariant=True)  # Ditto contravariant.
+
+ClassVar = typing.ClassVar
+
+# On older versions of typing there is an internal class named "Final".
+# 3.8+
+if hasattr(typing, 'Final') and sys.version_info[:2] >= (3, 7):
+    Final = typing.Final
+# 3.7
+elif sys.version_info[:2] >= (3, 7):
+    class _FinalForm(typing._SpecialForm, _root=True):
+
+        def __repr__(self):
+            return 'typing_extensions.' + self._name
+
+        def __getitem__(self, parameters):
+            item = typing._type_check(parameters,
+                                      f'{self._name} accepts only single type')
+            return typing._GenericAlias(self, (item,))
+
+    Final = _FinalForm('Final',
+                       doc="""A special typing construct to indicate that a name
+                       cannot be re-assigned or overridden in a subclass.
+                       For example:
+
+                           MAX_SIZE: Final = 9000
+                           MAX_SIZE += 1  # Error reported by type checker
+
+                           class Connection:
+                               TIMEOUT: Final[int] = 10
+                           class FastConnector(Connection):
+                               TIMEOUT = 1  # Error reported by type checker
+
+                       There is no runtime checking of these properties.""")
+# 3.6
+else:
+    class _Final(typing._FinalTypingBase, _root=True):
+        """A special typing construct to indicate that a name
+        cannot be re-assigned or overridden in a subclass.
+        For example:
+
+            MAX_SIZE: Final = 9000
+            MAX_SIZE += 1  # Error reported by type checker
+
+            class Connection:
+                TIMEOUT: Final[int] = 10
+            class FastConnector(Connection):
+                TIMEOUT = 1  # Error reported by type checker
+
+        There is no runtime checking of these properties.
+        """
+
+        __slots__ = ('__type__',)
+
+        def __init__(self, tp=None, **kwds):
+            self.__type__ = tp
+
+        def __getitem__(self, item):
+            cls = type(self)
+            if self.__type__ is None:
+                return cls(typing._type_check(item,
+                           f'{cls.__name__[1:]} accepts only single type.'),
+                           _root=True)
+            raise TypeError(f'{cls.__name__[1:]} cannot be further subscripted')
+
+        def _eval_type(self, globalns, localns):
+            new_tp = typing._eval_type(self.__type__, globalns, localns)
+            if new_tp == self.__type__:
+                return self
+            return type(self)(new_tp, _root=True)
+
+        def __repr__(self):
+            r = super().__repr__()
+            if self.__type__ is not None:
+                r += f'[{typing._type_repr(self.__type__)}]'
+            return r
+
+        def __hash__(self):
+            return hash((type(self).__name__, self.__type__))
+
+        def __eq__(self, other):
+            if not isinstance(other, _Final):
+                return NotImplemented
+            if self.__type__ is not None:
+                return self.__type__ == other.__type__
+            return self is other
+
+    Final = _Final(_root=True)
+
+
+# 3.8+
+if hasattr(typing, 'final'):
+    final = typing.final
+# 3.6-3.7
+else:
+    def final(f):
+        """This decorator can be used to indicate to type checkers that
+        the decorated method cannot be overridden, and decorated class
+        cannot be subclassed. For example:
+
+            class Base:
+                @final
+                def done(self) -> None:
+                    ...
+            class Sub(Base):
+                def done(self) -> None:  # Error reported by type checker
+                    ...
+            @final
+            class Leaf:
+                ...
+            class Other(Leaf):  # Error reported by type checker
+                ...
+
+        There is no runtime checking of these properties.
+        """
+        return f
+
+
+def IntVar(name):
+    return typing.TypeVar(name)
+
+
+# 3.8+:
+if hasattr(typing, 'Literal'):
+    Literal = typing.Literal
+# 3.7:
+elif sys.version_info[:2] >= (3, 7):
+    class _LiteralForm(typing._SpecialForm, _root=True):
+
+        def __repr__(self):
+            return 'typing_extensions.' + self._name
+
+        def __getitem__(self, parameters):
+            return typing._GenericAlias(self, parameters)
+
+    Literal = _LiteralForm('Literal',
+                           doc="""A type that can be used to indicate to type checkers
+                           that the corresponding value has a value literally equivalent
+                           to the provided parameter. For example:
+
+                               var: Literal[4] = 4
+
+                           The type checker understands that 'var' is literally equal to
+                           the value 4 and no other value.
+
+                           Literal[...] cannot be subclassed. There is no runtime
+                           checking verifying that the parameter is actually a value
+                           instead of a type.""")
+# 3.6:
+else:
+    class _Literal(typing._FinalTypingBase, _root=True):
+        """A type that can be used to indicate to type checkers that the
+        corresponding value has a value literally equivalent to the
+        provided parameter. For example:
+
+            var: Literal[4] = 4
+
+        The type checker understands that 'var' is literally equal to the
+        value 4 and no other value.
+
+        Literal[...] cannot be subclassed. There is no runtime checking
+        verifying that the parameter is actually a value instead of a type.
+        """
+
+        __slots__ = ('__values__',)
+
+        def __init__(self, values=None, **kwds):
+            self.__values__ = values
+
+        def __getitem__(self, values):
+            cls = type(self)
+            if self.__values__ is None:
+                if not isinstance(values, tuple):
+                    values = (values,)
+                return cls(values, _root=True)
+            raise TypeError(f'{cls.__name__[1:]} cannot be further subscripted')
+
+        def _eval_type(self, globalns, localns):
+            return self
+
+        def __repr__(self):
+            r = super().__repr__()
+            if self.__values__ is not None:
+                r += f'[{", ".join(map(typing._type_repr, self.__values__))}]'
+            return r
+
+        def __hash__(self):
+            return hash((type(self).__name__, self.__values__))
+
+        def __eq__(self, other):
+            if not isinstance(other, _Literal):
+                return NotImplemented
+            if self.__values__ is not None:
+                return self.__values__ == other.__values__
+            return self is other
+
+    Literal = _Literal(_root=True)
+
+
+_overload_dummy = typing._overload_dummy  # noqa
+overload = typing.overload
+
+
+# This is not a real generic class.  Don't use outside annotations.
+Type = typing.Type
+
+# Various ABCs mimicking those in collections.abc.
+# A few are simply re-exported for completeness.
+
+
+class _ExtensionsGenericMeta(GenericMeta):
+    def __subclasscheck__(self, subclass):
+        """This mimics a more modern GenericMeta.__subclasscheck__() logic
+        (that does not have problems with recursion) to work around interactions
+        between collections, typing, and typing_extensions on older
+        versions of Python, see https://github.com/python/typing/issues/501.
+        """
+        if self.__origin__ is not None:
+            if sys._getframe(1).f_globals['__name__'] not in ['abc', 'functools']:
+                raise TypeError("Parameterized generics cannot be used with class "
+                                "or instance checks")
+            return False
+        if not self.__extra__:
+            return super().__subclasscheck__(subclass)
+        res = self.__extra__.__subclasshook__(subclass)
+        if res is not NotImplemented:
+            return res
+        if self.__extra__ in subclass.__mro__:
+            return True
+        for scls in self.__extra__.__subclasses__():
+            if isinstance(scls, GenericMeta):
+                continue
+            if issubclass(subclass, scls):
+                return True
+        return False
+
+
+Awaitable = typing.Awaitable
+Coroutine = typing.Coroutine
+AsyncIterable = typing.AsyncIterable
+AsyncIterator = typing.AsyncIterator
+
+# 3.6.1+
+if hasattr(typing, 'Deque'):
+    Deque = typing.Deque
+# 3.6.0
+else:
+    class Deque(collections.deque, typing.MutableSequence[T],
+                metaclass=_ExtensionsGenericMeta,
+                extra=collections.deque):
+        __slots__ = ()
+
+        def __new__(cls, *args, **kwds):
+            if cls._gorg is Deque:
+                return collections.deque(*args, **kwds)
+            return typing._generic_new(collections.deque, cls, *args, **kwds)
+
+ContextManager = typing.ContextManager
+# 3.6.2+
+if hasattr(typing, 'AsyncContextManager'):
+    AsyncContextManager = typing.AsyncContextManager
+# 3.6.0-3.6.1
+else:
+    from _collections_abc import _check_methods as _check_methods_in_mro  # noqa
+
+    class AsyncContextManager(typing.Generic[T_co]):
+        __slots__ = ()
+
+        async def __aenter__(self):
+            return self
+
+        @abc.abstractmethod
+        async def __aexit__(self, exc_type, exc_value, traceback):
+            return None
+
+        @classmethod
+        def __subclasshook__(cls, C):
+            if cls is AsyncContextManager:
+                return _check_methods_in_mro(C, "__aenter__", "__aexit__")
+            return NotImplemented
+
+DefaultDict = typing.DefaultDict
+
+# 3.7.2+
+if hasattr(typing, 'OrderedDict'):
+    OrderedDict = typing.OrderedDict
+# 3.7.0-3.7.2
+elif (3, 7, 0) <= sys.version_info[:3] < (3, 7, 2):
+    OrderedDict = typing._alias(collections.OrderedDict, (KT, VT))
+# 3.6
+else:
+    class OrderedDict(collections.OrderedDict, typing.MutableMapping[KT, VT],
+                      metaclass=_ExtensionsGenericMeta,
+                      extra=collections.OrderedDict):
+
+        __slots__ = ()
+
+        def __new__(cls, *args, **kwds):
+            if cls._gorg is OrderedDict:
+                return collections.OrderedDict(*args, **kwds)
+            return typing._generic_new(collections.OrderedDict, cls, *args, **kwds)
+
+# 3.6.2+
+if hasattr(typing, 'Counter'):
+    Counter = typing.Counter
+# 3.6.0-3.6.1
+else:
+    class Counter(collections.Counter,
+                  typing.Dict[T, int],
+                  metaclass=_ExtensionsGenericMeta, extra=collections.Counter):
+
+        __slots__ = ()
+
+        def __new__(cls, *args, **kwds):
+            if cls._gorg is Counter:
+                return collections.Counter(*args, **kwds)
+            return typing._generic_new(collections.Counter, cls, *args, **kwds)
+
+# 3.6.1+
+if hasattr(typing, 'ChainMap'):
+    ChainMap = typing.ChainMap
+elif hasattr(collections, 'ChainMap'):
+    class ChainMap(collections.ChainMap, typing.MutableMapping[KT, VT],
+                   metaclass=_ExtensionsGenericMeta,
+                   extra=collections.ChainMap):
+
+        __slots__ = ()
+
+        def __new__(cls, *args, **kwds):
+            if cls._gorg is ChainMap:
+                return collections.ChainMap(*args, **kwds)
+            return typing._generic_new(collections.ChainMap, cls, *args, **kwds)
+
+# 3.6.1+
+if hasattr(typing, 'AsyncGenerator'):
+    AsyncGenerator = typing.AsyncGenerator
+# 3.6.0
+else:
+    class AsyncGenerator(AsyncIterator[T_co], typing.Generic[T_co, T_contra],
+                         metaclass=_ExtensionsGenericMeta,
+                         extra=collections.abc.AsyncGenerator):
+        __slots__ = ()
+
+NewType = typing.NewType
+Text = typing.Text
+TYPE_CHECKING = typing.TYPE_CHECKING
+
+
+def _gorg(cls):
+    """This function exists for compatibility with old typing versions."""
+    assert isinstance(cls, GenericMeta)
+    if hasattr(cls, '_gorg'):
+        return cls._gorg
+    while cls.__origin__ is not None:
+        cls = cls.__origin__
+    return cls
+
+
+_PROTO_WHITELIST = ['Callable', 'Awaitable',
+                    'Iterable', 'Iterator', 'AsyncIterable', 'AsyncIterator',
+                    'Hashable', 'Sized', 'Container', 'Collection', 'Reversible',
+                    'ContextManager', 'AsyncContextManager']
+
+
+def _get_protocol_attrs(cls):
+    attrs = set()
+    for base in cls.__mro__[:-1]:  # without object
+        if base.__name__ in ('Protocol', 'Generic'):
+            continue
+        annotations = getattr(base, '__annotations__', {})
+        for attr in list(base.__dict__.keys()) + list(annotations.keys()):
+            if (not attr.startswith('_abc_') and attr not in (
+                    '__abstractmethods__', '__annotations__', '__weakref__',
+                    '_is_protocol', '_is_runtime_protocol', '__dict__',
+                    '__args__', '__slots__',
+                    '__next_in_mro__', '__parameters__', '__origin__',
+                    '__orig_bases__', '__extra__', '__tree_hash__',
+                    '__doc__', '__subclasshook__', '__init__', '__new__',
+                    '__module__', '_MutableMapping__marker', '_gorg')):
+                attrs.add(attr)
+    return attrs
+
+
+def _is_callable_members_only(cls):
+    return all(callable(getattr(cls, attr, None)) for attr in _get_protocol_attrs(cls))
+
+
+# 3.8+
+if hasattr(typing, 'Protocol'):
+    Protocol = typing.Protocol
+# 3.7
+elif PEP_560:
+    from typing import _collect_type_vars  # noqa
+
+    def _no_init(self, *args, **kwargs):
+        if type(self)._is_protocol:
+            raise TypeError('Protocols cannot be instantiated')
+
+    class _ProtocolMeta(abc.ABCMeta):
+        # This metaclass is a bit unfortunate and exists only because of the lack
+        # of __instancehook__.
+        def __instancecheck__(cls, instance):
+            # We need this method for situations where attributes are
+            # assigned in __init__.
+            if ((not getattr(cls, '_is_protocol', False) or
+                 _is_callable_members_only(cls)) and
+                    issubclass(instance.__class__, cls)):
+                return True
+            if cls._is_protocol:
+                if all(hasattr(instance, attr) and
+                       (not callable(getattr(cls, attr, None)) or
+                        getattr(instance, attr) is not None)
+                       for attr in _get_protocol_attrs(cls)):
+                    return True
+            return super().__instancecheck__(instance)
+
+    class Protocol(metaclass=_ProtocolMeta):
+        # There is quite a lot of overlapping code with typing.Generic.
+        # Unfortunately it is hard to avoid this while these live in two different
+        # modules. The duplicated code will be removed when Protocol is moved to typing.
+        """Base class for protocol classes. Protocol classes are defined as::
+
+            class Proto(Protocol):
+                def meth(self) -> int:
+                    ...
+
+        Such classes are primarily used with static type checkers that recognize
+        structural subtyping (static duck-typing), for example::
+
+            class C:
+                def meth(self) -> int:
+                    return 0
+
+            def func(x: Proto) -> int:
+                return x.meth()
+
+            func(C())  # Passes static type check
+
+        See PEP 544 for details. Protocol classes decorated with
+        @typing_extensions.runtime act as simple-minded runtime protocol that checks
+        only the presence of given attributes, ignoring their type signatures.
+
+        Protocol classes can be generic, they are defined as::
+
+            class GenProto(Protocol[T]):
+                def meth(self) -> T:
+                    ...
+        """
+        __slots__ = ()
+        _is_protocol = True
+
+        def __new__(cls, *args, **kwds):
+            if cls is Protocol:
+                raise TypeError("Type Protocol cannot be instantiated; "
+                                "it can only be used as a base class")
+            return super().__new__(cls)
+
+        @typing._tp_cache
+        def __class_getitem__(cls, params):
+            if not isinstance(params, tuple):
+                params = (params,)
+            if not params and cls is not typing.Tuple:
+                raise TypeError(
+                    f"Parameter list to {cls.__qualname__}[...] cannot be empty")
+            msg = "Parameters to generic types must be types."
+            params = tuple(typing._type_check(p, msg) for p in params)  # noqa
+            if cls is Protocol:
+                # Generic can only be subscripted with unique type variables.
+                if not all(isinstance(p, typing.TypeVar) for p in params):
+                    i = 0
+                    while isinstance(params[i], typing.TypeVar):
+                        i += 1
+                    raise TypeError(
+                        "Parameters to Protocol[...] must all be type variables."
+                        f" Parameter {i + 1} is {params[i]}")
+                if len(set(params)) != len(params):
+                    raise TypeError(
+                        "Parameters to Protocol[...] must all be unique")
+            else:
+                # Subscripting a regular Generic subclass.
+                _check_generic(cls, params)
+            return typing._GenericAlias(cls, params)
+
+        def __init_subclass__(cls, *args, **kwargs):
+            tvars = []
+            if '__orig_bases__' in cls.__dict__:
+                error = typing.Generic in cls.__orig_bases__
+            else:
+                error = typing.Generic in cls.__bases__
+            if error:
+                raise TypeError("Cannot inherit from plain Generic")
+            if '__orig_bases__' in cls.__dict__:
+                tvars = _collect_type_vars(cls.__orig_bases__)
+                # Look for Generic[T1, ..., Tn] or Protocol[T1, ..., Tn].
+                # If found, tvars must be a subset of it.
+                # If not found, tvars is it.
+                # Also check for and reject plain Generic,
+                # and reject multiple Generic[...] and/or Protocol[...].
+                gvars = None
+                for base in cls.__orig_bases__:
+                    if (isinstance(base, typing._GenericAlias) and
+                            base.__origin__ in (typing.Generic, Protocol)):
+                        # for error messages
+                        the_base = base.__origin__.__name__
+                        if gvars is not None:
+                            raise TypeError(
+                                "Cannot inherit from Generic[...]"
+                                " and/or Protocol[...] multiple types.")
+                        gvars = base.__parameters__
+                if gvars is None:
+                    gvars = tvars
+                else:
+                    tvarset = set(tvars)
+                    gvarset = set(gvars)
+                    if not tvarset <= gvarset:
+                        s_vars = ', '.join(str(t) for t in tvars if t not in gvarset)
+                        s_args = ', '.join(str(g) for g in gvars)
+                        raise TypeError(f"Some type variables ({s_vars}) are"
+                                        f" not listed in {the_base}[{s_args}]")
+                    tvars = gvars
+            cls.__parameters__ = tuple(tvars)
+
+            # Determine if this is a protocol or a concrete subclass.
+            if not cls.__dict__.get('_is_protocol', None):
+                cls._is_protocol = any(b is Protocol for b in cls.__bases__)
+
+            # Set (or override) the protocol subclass hook.
+            def _proto_hook(other):
+                if not cls.__dict__.get('_is_protocol', None):
+                    return NotImplemented
+                if not getattr(cls, '_is_runtime_protocol', False):
+                    if sys._getframe(2).f_globals['__name__'] in ['abc', 'functools']:
+                        return NotImplemented
+                    raise TypeError("Instance and class checks can only be used with"
+                                    " @runtime protocols")
+                if not _is_callable_members_only(cls):
+                    if sys._getframe(2).f_globals['__name__'] in ['abc', 'functools']:
+                        return NotImplemented
+                    raise TypeError("Protocols with non-method members"
+                                    " don't support issubclass()")
+                if not isinstance(other, type):
+                    # Same error as for issubclass(1, int)
+                    raise TypeError('issubclass() arg 1 must be a class')
+                for attr in _get_protocol_attrs(cls):
+                    for base in other.__mro__:
+                        if attr in base.__dict__:
+                            if base.__dict__[attr] is None:
+                                return NotImplemented
+                            break
+                        annotations = getattr(base, '__annotations__', {})
+                        if (isinstance(annotations, typing.Mapping) and
+                                attr in annotations and
+                                isinstance(other, _ProtocolMeta) and
+                                other._is_protocol):
+                            break
+                    else:
+                        return NotImplemented
+                return True
+            if '__subclasshook__' not in cls.__dict__:
+                cls.__subclasshook__ = _proto_hook
+
+            # We have nothing more to do for non-protocols.
+            if not cls._is_protocol:
+                return
+
+            # Check consistency of bases.
+            for base in cls.__bases__:
+                if not (base in (object, typing.Generic) or
+                        base.__module__ == 'collections.abc' and
+                        base.__name__ in _PROTO_WHITELIST or
+                        isinstance(base, _ProtocolMeta) and base._is_protocol):
+                    raise TypeError('Protocols can only inherit from other'
+                                    f' protocols, got {repr(base)}')
+            cls.__init__ = _no_init
+# 3.6
+else:
+    from typing import _next_in_mro, _type_check  # noqa
+
+    def _no_init(self, *args, **kwargs):
+        if type(self)._is_protocol:
+            raise TypeError('Protocols cannot be instantiated')
+
+    class _ProtocolMeta(GenericMeta):
+        """Internal metaclass for Protocol.
+
+        This exists so Protocol classes can be generic without deriving
+        from Generic.
+        """
+        def __new__(cls, name, bases, namespace,
+                    tvars=None, args=None, origin=None, extra=None, orig_bases=None):
+            # This is just a version copied from GenericMeta.__new__ that
+            # includes "Protocol" special treatment. (Comments removed for brevity.)
+            assert extra is None  # Protocols should not have extra
+            if tvars is not None:
+                assert origin is not None
+                assert all(isinstance(t, typing.TypeVar) for t in tvars), tvars
+            else:
+                tvars = _type_vars(bases)
+                gvars = None
+                for base in bases:
+                    if base is typing.Generic:
+                        raise TypeError("Cannot inherit from plain Generic")
+                    if (isinstance(base, GenericMeta) and
+                            base.__origin__ in (typing.Generic, Protocol)):
+                        if gvars is not None:
+                            raise TypeError(
+                                "Cannot inherit from Generic[...] or"
+                                " Protocol[...] multiple times.")
+                        gvars = base.__parameters__
+                if gvars is None:
+                    gvars = tvars
+                else:
+                    tvarset = set(tvars)
+                    gvarset = set(gvars)
+                    if not tvarset <= gvarset:
+                        s_vars = ", ".join(str(t) for t in tvars if t not in gvarset)
+                        s_args = ", ".join(str(g) for g in gvars)
+                        cls_name = "Generic" if any(b.__origin__ is typing.Generic
+                                                    for b in bases) else "Protocol"
+                        raise TypeError(f"Some type variables ({s_vars}) are"
+                                        f" not listed in {cls_name}[{s_args}]")
+                    tvars = gvars
+
+            initial_bases = bases
+            if (extra is not None and type(extra) is abc.ABCMeta and
+                    extra not in bases):
+                bases = (extra,) + bases
+            bases = tuple(_gorg(b) if isinstance(b, GenericMeta) else b
+                          for b in bases)
+            if any(isinstance(b, GenericMeta) and b is not typing.Generic for b in bases):
+                bases = tuple(b for b in bases if b is not typing.Generic)
+            namespace.update({'__origin__': origin, '__extra__': extra})
+            self = super(GenericMeta, cls).__new__(cls, name, bases, namespace,
+                                                   _root=True)
+            super(GenericMeta, self).__setattr__('_gorg',
+                                                 self if not origin else
+                                                 _gorg(origin))
+            self.__parameters__ = tvars
+            self.__args__ = tuple(... if a is typing._TypingEllipsis else
+                                  () if a is typing._TypingEmpty else
+                                  a for a in args) if args else None
+            self.__next_in_mro__ = _next_in_mro(self)
+            if orig_bases is None:
+                self.__orig_bases__ = initial_bases
+            elif origin is not None:
+                self._abc_registry = origin._abc_registry
+                self._abc_cache = origin._abc_cache
+            if hasattr(self, '_subs_tree'):
+                self.__tree_hash__ = (hash(self._subs_tree()) if origin else
+                                      super(GenericMeta, self).__hash__())
+            return self
+
+        def __init__(cls, *args, **kwargs):
+            super().__init__(*args, **kwargs)
+            if not cls.__dict__.get('_is_protocol', None):
+                cls._is_protocol = any(b is Protocol or
+                                       isinstance(b, _ProtocolMeta) and
+                                       b.__origin__ is Protocol
+                                       for b in cls.__bases__)
+            if cls._is_protocol:
+                for base in cls.__mro__[1:]:
+                    if not (base in (object, typing.Generic) or
+                            base.__module__ == 'collections.abc' and
+                            base.__name__ in _PROTO_WHITELIST or
+                            isinstance(base, typing.TypingMeta) and base._is_protocol or
+                            isinstance(base, GenericMeta) and
+                            base.__origin__ is typing.Generic):
+                        raise TypeError(f'Protocols can only inherit from other'
+                                        f' protocols, got {repr(base)}')
+
+                cls.__init__ = _no_init
+
+            def _proto_hook(other):
+                if not cls.__dict__.get('_is_protocol', None):
+                    return NotImplemented
+                if not isinstance(other, type):
+                    # Same error as for issubclass(1, int)
+                    raise TypeError('issubclass() arg 1 must be a class')
+                for attr in _get_protocol_attrs(cls):
+                    for base in other.__mro__:
+                        if attr in base.__dict__:
+                            if base.__dict__[attr] is None:
+                                return NotImplemented
+                            break
+                        annotations = getattr(base, '__annotations__', {})
+                        if (isinstance(annotations, typing.Mapping) and
+                                attr in annotations and
+                                isinstance(other, _ProtocolMeta) and
+                                other._is_protocol):
+                            break
+                    else:
+                        return NotImplemented
+                return True
+            if '__subclasshook__' not in cls.__dict__:
+                cls.__subclasshook__ = _proto_hook
+
+        def __instancecheck__(self, instance):
+            # We need this method for situations where attributes are
+            # assigned in __init__.
+            if ((not getattr(self, '_is_protocol', False) or
+                    _is_callable_members_only(self)) and
+                    issubclass(instance.__class__, self)):
+                return True
+            if self._is_protocol:
+                if all(hasattr(instance, attr) and
+                        (not callable(getattr(self, attr, None)) or
+                         getattr(instance, attr) is not None)
+                        for attr in _get_protocol_attrs(self)):
+                    return True
+            return super(GenericMeta, self).__instancecheck__(instance)
+
+        def __subclasscheck__(self, cls):
+            if self.__origin__ is not None:
+                if sys._getframe(1).f_globals['__name__'] not in ['abc', 'functools']:
+                    raise TypeError("Parameterized generics cannot be used with class "
+                                    "or instance checks")
+                return False
+            if (self.__dict__.get('_is_protocol', None) and
+                    not self.__dict__.get('_is_runtime_protocol', None)):
+                if sys._getframe(1).f_globals['__name__'] in ['abc',
+                                                              'functools',
+                                                              'typing']:
+                    return False
+                raise TypeError("Instance and class checks can only be used with"
+                                " @runtime protocols")
+            if (self.__dict__.get('_is_runtime_protocol', None) and
+                    not _is_callable_members_only(self)):
+                if sys._getframe(1).f_globals['__name__'] in ['abc',
+                                                              'functools',
+                                                              'typing']:
+                    return super(GenericMeta, self).__subclasscheck__(cls)
+                raise TypeError("Protocols with non-method members"
+                                " don't support issubclass()")
+            return super(GenericMeta, self).__subclasscheck__(cls)
+
+        @typing._tp_cache
+        def __getitem__(self, params):
+            # We also need to copy this from GenericMeta.__getitem__ to get
+            # special treatment of "Protocol". (Comments removed for brevity.)
+            if not isinstance(params, tuple):
+                params = (params,)
+            if not params and _gorg(self) is not typing.Tuple:
+                raise TypeError(
+                    f"Parameter list to {self.__qualname__}[...] cannot be empty")
+            msg = "Parameters to generic types must be types."
+            params = tuple(_type_check(p, msg) for p in params)
+            if self in (typing.Generic, Protocol):
+                if not all(isinstance(p, typing.TypeVar) for p in params):
+                    raise TypeError(
+                        f"Parameters to {repr(self)}[...] must all be type variables")
+                if len(set(params)) != len(params):
+                    raise TypeError(
+                        f"Parameters to {repr(self)}[...] must all be unique")
+                tvars = params
+                args = params
+            elif self in (typing.Tuple, typing.Callable):
+                tvars = _type_vars(params)
+                args = params
+            elif self.__origin__ in (typing.Generic, Protocol):
+                raise TypeError(f"Cannot subscript already-subscripted {repr(self)}")
+            else:
+                _check_generic(self, params)
+                tvars = _type_vars(params)
+                args = params
+
+            prepend = (self,) if self.__origin__ is None else ()
+            return self.__class__(self.__name__,
+                                  prepend + self.__bases__,
+                                  _no_slots_copy(self.__dict__),
+                                  tvars=tvars,
+                                  args=args,
+                                  origin=self,
+                                  extra=self.__extra__,
+                                  orig_bases=self.__orig_bases__)
+
+    class Protocol(metaclass=_ProtocolMeta):
+        """Base class for protocol classes. Protocol classes are defined as::
+
+          class Proto(Protocol):
+              def meth(self) -> int:
+                  ...
+
+        Such classes are primarily used with static type checkers that recognize
+        structural subtyping (static duck-typing), for example::
+
+          class C:
+              def meth(self) -> int:
+                  return 0
+
+          def func(x: Proto) -> int:
+              return x.meth()
+
+          func(C())  # Passes static type check
+
+        See PEP 544 for details. Protocol classes decorated with
+        @typing_extensions.runtime act as simple-minded runtime protocol that checks
+        only the presence of given attributes, ignoring their type signatures.
+
+        Protocol classes can be generic, they are defined as::
+
+          class GenProto(Protocol[T]):
+              def meth(self) -> T:
+                  ...
+        """
+        __slots__ = ()
+        _is_protocol = True
+
+        def __new__(cls, *args, **kwds):
+            if _gorg(cls) is Protocol:
+                raise TypeError("Type Protocol cannot be instantiated; "
+                                "it can be used only as a base class")
+            return typing._generic_new(cls.__next_in_mro__, cls, *args, **kwds)
+
+
+# 3.8+
+if hasattr(typing, 'runtime_checkable'):
+    runtime_checkable = typing.runtime_checkable
+# 3.6-3.7
+else:
+    def runtime_checkable(cls):
+        """Mark a protocol class as a runtime protocol, so that it
+        can be used with isinstance() and issubclass(). Raise TypeError
+        if applied to a non-protocol class.
+
+        This allows a simple-minded structural check very similar to the
+        one-offs in collections.abc such as Hashable.
+        """
+        if not isinstance(cls, _ProtocolMeta) or not cls._is_protocol:
+            raise TypeError('@runtime_checkable can be only applied to protocol classes,'
+                            f' got {cls!r}')
+        cls._is_runtime_protocol = True
+        return cls
+
+
+# Exists for backwards compatibility.
+runtime = runtime_checkable
+
+
+# 3.8+
+if hasattr(typing, 'SupportsIndex'):
+    SupportsIndex = typing.SupportsIndex
+# 3.6-3.7
+else:
+    @runtime_checkable
+    class SupportsIndex(Protocol):
+        __slots__ = ()
+
+        @abc.abstractmethod
+        def __index__(self) -> int:
+            pass
+
+
+if sys.version_info >= (3, 9, 2):
+    # The standard library TypedDict in Python 3.8 does not store runtime information
+    # about which (if any) keys are optional.  See https://bugs.python.org/issue38834
+    # The standard library TypedDict in Python 3.9.0/1 does not honour the "total"
+    # keyword with old-style TypedDict().  See https://bugs.python.org/issue42059
+    TypedDict = typing.TypedDict
+else:
+    def _check_fails(cls, other):
+        try:
+            if sys._getframe(1).f_globals['__name__'] not in ['abc',
+                                                              'functools',
+                                                              'typing']:
+                # Typed dicts are only for static structural subtyping.
+                raise TypeError('TypedDict does not support instance and class checks')
+        except (AttributeError, ValueError):
+            pass
+        return False
+
+    def _dict_new(*args, **kwargs):
+        if not args:
+            raise TypeError('TypedDict.__new__(): not enough arguments')
+        _, args = args[0], args[1:]  # allow the "cls" keyword be passed
+        return dict(*args, **kwargs)
+
+    _dict_new.__text_signature__ = '($cls, _typename, _fields=None, /, **kwargs)'
+
+    def _typeddict_new(*args, total=True, **kwargs):
+        if not args:
+            raise TypeError('TypedDict.__new__(): not enough arguments')
+        _, args = args[0], args[1:]  # allow the "cls" keyword be passed
+        if args:
+            typename, args = args[0], args[1:]  # allow the "_typename" keyword be passed
+        elif '_typename' in kwargs:
+            typename = kwargs.pop('_typename')
+            import warnings
+            warnings.warn("Passing '_typename' as keyword argument is deprecated",
+                          DeprecationWarning, stacklevel=2)
+        else:
+            raise TypeError("TypedDict.__new__() missing 1 required positional "
+                            "argument: '_typename'")
+        if args:
+            try:
+                fields, = args  # allow the "_fields" keyword be passed
+            except ValueError:
+                raise TypeError('TypedDict.__new__() takes from 2 to 3 '
+                                f'positional arguments but {len(args) + 2} '
+                                'were given')
+        elif '_fields' in kwargs and len(kwargs) == 1:
+            fields = kwargs.pop('_fields')
+            import warnings
+            warnings.warn("Passing '_fields' as keyword argument is deprecated",
+                          DeprecationWarning, stacklevel=2)
+        else:
+            fields = None
+
+        if fields is None:
+            fields = kwargs
+        elif kwargs:
+            raise TypeError("TypedDict takes either a dict or keyword arguments,"
+                            " but not both")
+
+        ns = {'__annotations__': dict(fields)}
+        try:
+            # Setting correct module is necessary to make typed dict classes pickleable.
+            ns['__module__'] = sys._getframe(1).f_globals.get('__name__', '__main__')
+        except (AttributeError, ValueError):
+            pass
+
+        return _TypedDictMeta(typename, (), ns, total=total)
+
+    _typeddict_new.__text_signature__ = ('($cls, _typename, _fields=None,'
+                                         ' /, *, total=True, **kwargs)')
+
+    class _TypedDictMeta(type):
+        def __init__(cls, name, bases, ns, total=True):
+            super().__init__(name, bases, ns)
+
+        def __new__(cls, name, bases, ns, total=True):
+            # Create new typed dict class object.
+            # This method is called directly when TypedDict is subclassed,
+            # or via _typeddict_new when TypedDict is instantiated. This way
+            # TypedDict supports all three syntaxes described in its docstring.
+            # Subclasses and instances of TypedDict return actual dictionaries
+            # via _dict_new.
+            ns['__new__'] = _typeddict_new if name == 'TypedDict' else _dict_new
+            tp_dict = super().__new__(cls, name, (dict,), ns)
+
+            annotations = {}
+            own_annotations = ns.get('__annotations__', {})
+            own_annotation_keys = set(own_annotations.keys())
+            msg = "TypedDict('Name', {f0: t0, f1: t1, ...}); each t must be a type"
+            own_annotations = {
+                n: typing._type_check(tp, msg) for n, tp in own_annotations.items()
+            }
+            required_keys = set()
+            optional_keys = set()
+
+            for base in bases:
+                annotations.update(base.__dict__.get('__annotations__', {}))
+                required_keys.update(base.__dict__.get('__required_keys__', ()))
+                optional_keys.update(base.__dict__.get('__optional_keys__', ()))
+
+            annotations.update(own_annotations)
+            if total:
+                required_keys.update(own_annotation_keys)
+            else:
+                optional_keys.update(own_annotation_keys)
+
+            tp_dict.__annotations__ = annotations
+            tp_dict.__required_keys__ = frozenset(required_keys)
+            tp_dict.__optional_keys__ = frozenset(optional_keys)
+            if not hasattr(tp_dict, '__total__'):
+                tp_dict.__total__ = total
+            return tp_dict
+
+        __instancecheck__ = __subclasscheck__ = _check_fails
+
+    TypedDict = _TypedDictMeta('TypedDict', (dict,), {})
+    TypedDict.__module__ = __name__
+    TypedDict.__doc__ = \
+        """A simple typed name space. At runtime it is equivalent to a plain dict.
+
+        TypedDict creates a dictionary type that expects all of its
+        instances to have a certain set of keys, with each key
+        associated with a value of a consistent type. This expectation
+        is not checked at runtime but is only enforced by type checkers.
+        Usage::
+
+            class Point2D(TypedDict):
+                x: int
+                y: int
+                label: str
+
+            a: Point2D = {'x': 1, 'y': 2, 'label': 'good'}  # OK
+            b: Point2D = {'z': 3, 'label': 'bad'}           # Fails type check
+
+            assert Point2D(x=1, y=2, label='first') == dict(x=1, y=2, label='first')
+
+        The type info can be accessed via the Point2D.__annotations__ dict, and
+        the Point2D.__required_keys__ and Point2D.__optional_keys__ frozensets.
+        TypedDict supports two additional equivalent forms::
+
+            Point2D = TypedDict('Point2D', x=int, y=int, label=str)
+            Point2D = TypedDict('Point2D', {'x': int, 'y': int, 'label': str})
+
+        The class syntax is only supported in Python 3.6+, while two other
+        syntax forms work for Python 2.7 and 3.2+
+        """
+
+
+# Python 3.9+ has PEP 593 (Annotated and modified get_type_hints)
+if hasattr(typing, 'Annotated'):
+    Annotated = typing.Annotated
+    get_type_hints = typing.get_type_hints
+    # Not exported and not a public API, but needed for get_origin() and get_args()
+    # to work.
+    _AnnotatedAlias = typing._AnnotatedAlias
+# 3.7-3.8
+elif PEP_560:
+    class _AnnotatedAlias(typing._GenericAlias, _root=True):
+        """Runtime representation of an annotated type.
+
+        At its core 'Annotated[t, dec1, dec2, ...]' is an alias for the type 't'
+        with extra annotations. The alias behaves like a normal typing alias,
+        instantiating is the same as instantiating the underlying type, binding
+        it to types is also the same.
+        """
+        def __init__(self, origin, metadata):
+            if isinstance(origin, _AnnotatedAlias):
+                metadata = origin.__metadata__ + metadata
+                origin = origin.__origin__
+            super().__init__(origin, origin)
+            self.__metadata__ = metadata
+
+        def copy_with(self, params):
+            assert len(params) == 1
+            new_type = params[0]
+            return _AnnotatedAlias(new_type, self.__metadata__)
+
+        def __repr__(self):
+            return (f"typing_extensions.Annotated[{typing._type_repr(self.__origin__)}, "
+                    f"{', '.join(repr(a) for a in self.__metadata__)}]")
+
+        def __reduce__(self):
+            return operator.getitem, (
+                Annotated, (self.__origin__,) + self.__metadata__
+            )
+
+        def __eq__(self, other):
+            if not isinstance(other, _AnnotatedAlias):
+                return NotImplemented
+            if self.__origin__ != other.__origin__:
+                return False
+            return self.__metadata__ == other.__metadata__
+
+        def __hash__(self):
+            return hash((self.__origin__, self.__metadata__))
+
+    class Annotated:
+        """Add context specific metadata to a type.
+
+        Example: Annotated[int, runtime_check.Unsigned] indicates to the
+        hypothetical runtime_check module that this type is an unsigned int.
+        Every other consumer of this type can ignore this metadata and treat
+        this type as int.
+
+        The first argument to Annotated must be a valid type (and will be in
+        the __origin__ field), the remaining arguments are kept as a tuple in
+        the __extra__ field.
+
+        Details:
+
+        - It's an error to call `Annotated` with less than two arguments.
+        - Nested Annotated are flattened::
+
+            Annotated[Annotated[T, Ann1, Ann2], Ann3] == Annotated[T, Ann1, Ann2, Ann3]
+
+        - Instantiating an annotated type is equivalent to instantiating the
+        underlying type::
+
+            Annotated[C, Ann1](5) == C(5)
+
+        - Annotated can be used as a generic type alias::
+
+            Optimized = Annotated[T, runtime.Optimize()]
+            Optimized[int] == Annotated[int, runtime.Optimize()]
+
+            OptimizedList = Annotated[List[T], runtime.Optimize()]
+            OptimizedList[int] == Annotated[List[int], runtime.Optimize()]
+        """
+
+        __slots__ = ()
+
+        def __new__(cls, *args, **kwargs):
+            raise TypeError("Type Annotated cannot be instantiated.")
+
+        @typing._tp_cache
+        def __class_getitem__(cls, params):
+            if not isinstance(params, tuple) or len(params) < 2:
+                raise TypeError("Annotated[...] should be used "
+                                "with at least two arguments (a type and an "
+                                "annotation).")
+            msg = "Annotated[t, ...]: t must be a type."
+            origin = typing._type_check(params[0], msg)
+            metadata = tuple(params[1:])
+            return _AnnotatedAlias(origin, metadata)
+
+        def __init_subclass__(cls, *args, **kwargs):
+            raise TypeError(
+                f"Cannot subclass {cls.__module__}.Annotated"
+            )
+
+    def _strip_annotations(t):
+        """Strips the annotations from a given type.
+        """
+        if isinstance(t, _AnnotatedAlias):
+            return _strip_annotations(t.__origin__)
+        if isinstance(t, typing._GenericAlias):
+            stripped_args = tuple(_strip_annotations(a) for a in t.__args__)
+            if stripped_args == t.__args__:
+                return t
+            res = t.copy_with(stripped_args)
+            res._special = t._special
+            return res
+        return t
+
+    def get_type_hints(obj, globalns=None, localns=None, include_extras=False):
+        """Return type hints for an object.
+
+        This is often the same as obj.__annotations__, but it handles
+        forward references encoded as string literals, adds Optional[t] if a
+        default value equal to None is set and recursively replaces all
+        'Annotated[T, ...]' with 'T' (unless 'include_extras=True').
+
+        The argument may be a module, class, method, or function. The annotations
+        are returned as a dictionary. For classes, annotations include also
+        inherited members.
+
+        TypeError is raised if the argument is not of a type that can contain
+        annotations, and an empty dictionary is returned if no annotations are
+        present.
+
+        BEWARE -- the behavior of globalns and localns is counterintuitive
+        (unless you are familiar with how eval() and exec() work).  The
+        search order is locals first, then globals.
+
+        - If no dict arguments are passed, an attempt is made to use the
+          globals from obj (or the respective module's globals for classes),
+          and these are also used as the locals.  If the object does not appear
+          to have globals, an empty dictionary is used.
+
+        - If one dict argument is passed, it is used for both globals and
+          locals.
+
+        - If two dict arguments are passed, they specify globals and
+          locals, respectively.
+        """
+        hint = typing.get_type_hints(obj, globalns=globalns, localns=localns)
+        if include_extras:
+            return hint
+        return {k: _strip_annotations(t) for k, t in hint.items()}
+# 3.6
+else:
+
+    def _is_dunder(name):
+        """Returns True if name is a __dunder_variable_name__."""
+        return len(name) > 4 and name.startswith('__') and name.endswith('__')
+
+    # Prior to Python 3.7 types did not have `copy_with`. A lot of the equality
+    # checks, argument expansion etc. are done on the _subs_tre. As a result we
+    # can't provide a get_type_hints function that strips out annotations.
+
+    class AnnotatedMeta(typing.GenericMeta):
+        """Metaclass for Annotated"""
+
+        def __new__(cls, name, bases, namespace, **kwargs):
+            if any(b is not object for b in bases):
+                raise TypeError("Cannot subclass " + str(Annotated))
+            return super().__new__(cls, name, bases, namespace, **kwargs)
+
+        @property
+        def __metadata__(self):
+            return self._subs_tree()[2]
+
+        def _tree_repr(self, tree):
+            cls, origin, metadata = tree
+            if not isinstance(origin, tuple):
+                tp_repr = typing._type_repr(origin)
+            else:
+                tp_repr = origin[0]._tree_repr(origin)
+            metadata_reprs = ", ".join(repr(arg) for arg in metadata)
+            return f'{cls}[{tp_repr}, {metadata_reprs}]'
+
+        def _subs_tree(self, tvars=None, args=None):  # noqa
+            if self is Annotated:
+                return Annotated
+            res = super()._subs_tree(tvars=tvars, args=args)
+            # Flatten nested Annotated
+            if isinstance(res[1], tuple) and res[1][0] is Annotated:
+                sub_tp = res[1][1]
+                sub_annot = res[1][2]
+                return (Annotated, sub_tp, sub_annot + res[2])
+            return res
+
+        def _get_cons(self):
+            """Return the class used to create instance of this type."""
+            if self.__origin__ is None:
+                raise TypeError("Cannot get the underlying type of a "
+                                "non-specialized Annotated type.")
+            tree = self._subs_tree()
+            while isinstance(tree, tuple) and tree[0] is Annotated:
+                tree = tree[1]
+            if isinstance(tree, tuple):
+                return tree[0]
+            else:
+                return tree
+
+        @typing._tp_cache
+        def __getitem__(self, params):
+            if not isinstance(params, tuple):
+                params = (params,)
+            if self.__origin__ is not None:  # specializing an instantiated type
+                return super().__getitem__(params)
+            elif not isinstance(params, tuple) or len(params) < 2:
+                raise TypeError("Annotated[...] should be instantiated "
+                                "with at least two arguments (a type and an "
+                                "annotation).")
+            else:
+                msg = "Annotated[t, ...]: t must be a type."
+                tp = typing._type_check(params[0], msg)
+                metadata = tuple(params[1:])
+            return self.__class__(
+                self.__name__,
+                self.__bases__,
+                _no_slots_copy(self.__dict__),
+                tvars=_type_vars((tp,)),
+                # Metadata is a tuple so it won't be touched by _replace_args et al.
+                args=(tp, metadata),
+                origin=self,
+            )
+
+        def __call__(self, *args, **kwargs):
+            cons = self._get_cons()
+            result = cons(*args, **kwargs)
+            try:
+                result.__orig_class__ = self
+            except AttributeError:
+                pass
+            return result
+
+        def __getattr__(self, attr):
+            # For simplicity we just don't relay all dunder names
+            if self.__origin__ is not None and not _is_dunder(attr):
+                return getattr(self._get_cons(), attr)
+            raise AttributeError(attr)
+
+        def __setattr__(self, attr, value):
+            if _is_dunder(attr) or attr.startswith('_abc_'):
+                super().__setattr__(attr, value)
+            elif self.__origin__ is None:
+                raise AttributeError(attr)
+            else:
+                setattr(self._get_cons(), attr, value)
+
+        def __instancecheck__(self, obj):
+            raise TypeError("Annotated cannot be used with isinstance().")
+
+        def __subclasscheck__(self, cls):
+            raise TypeError("Annotated cannot be used with issubclass().")
+
+    class Annotated(metaclass=AnnotatedMeta):
+        """Add context specific metadata to a type.
+
+        Example: Annotated[int, runtime_check.Unsigned] indicates to the
+        hypothetical runtime_check module that this type is an unsigned int.
+        Every other consumer of this type can ignore this metadata and treat
+        this type as int.
+
+        The first argument to Annotated must be a valid type, the remaining
+        arguments are kept as a tuple in the __metadata__ field.
+
+        Details:
+
+        - It's an error to call `Annotated` with less than two arguments.
+        - Nested Annotated are flattened::
+
+            Annotated[Annotated[T, Ann1, Ann2], Ann3] == Annotated[T, Ann1, Ann2, Ann3]
+
+        - Instantiating an annotated type is equivalent to instantiating the
+        underlying type::
+
+            Annotated[C, Ann1](5) == C(5)
+
+        - Annotated can be used as a generic type alias::
+
+            Optimized = Annotated[T, runtime.Optimize()]
+            Optimized[int] == Annotated[int, runtime.Optimize()]
+
+            OptimizedList = Annotated[List[T], runtime.Optimize()]
+            OptimizedList[int] == Annotated[List[int], runtime.Optimize()]
+        """
+
+# Python 3.8 has get_origin() and get_args() but those implementations aren't
+# Annotated-aware, so we can't use those. Python 3.9's versions don't support
+# ParamSpecArgs and ParamSpecKwargs, so only Python 3.10's versions will do.
+if sys.version_info[:2] >= (3, 10):
+    get_origin = typing.get_origin
+    get_args = typing.get_args
+# 3.7-3.9
+elif PEP_560:
+    try:
+        # 3.9+
+        from typing import _BaseGenericAlias
+    except ImportError:
+        _BaseGenericAlias = typing._GenericAlias
+    try:
+        # 3.9+
+        from typing import GenericAlias
+    except ImportError:
+        GenericAlias = typing._GenericAlias
+
+    def get_origin(tp):
+        """Get the unsubscripted version of a type.
+
+        This supports generic types, Callable, Tuple, Union, Literal, Final, ClassVar
+        and Annotated. Return None for unsupported types. Examples::
+
+            get_origin(Literal[42]) is Literal
+            get_origin(int) is None
+            get_origin(ClassVar[int]) is ClassVar
+            get_origin(Generic) is Generic
+            get_origin(Generic[T]) is Generic
+            get_origin(Union[T, int]) is Union
+            get_origin(List[Tuple[T, T]][int]) == list
+            get_origin(P.args) is P
+        """
+        if isinstance(tp, _AnnotatedAlias):
+            return Annotated
+        if isinstance(tp, (typing._GenericAlias, GenericAlias, _BaseGenericAlias,
+                           ParamSpecArgs, ParamSpecKwargs)):
+            return tp.__origin__
+        if tp is typing.Generic:
+            return typing.Generic
+        return None
+
+    def get_args(tp):
+        """Get type arguments with all substitutions performed.
+
+        For unions, basic simplifications used by Union constructor are performed.
+        Examples::
+            get_args(Dict[str, int]) == (str, int)
+            get_args(int) == ()
+            get_args(Union[int, Union[T, int], str][int]) == (int, str)
+            get_args(Union[int, Tuple[T, int]][str]) == (int, Tuple[str, int])
+            get_args(Callable[[], T][int]) == ([], int)
+        """
+        if isinstance(tp, _AnnotatedAlias):
+            return (tp.__origin__,) + tp.__metadata__
+        if isinstance(tp, (typing._GenericAlias, GenericAlias)):
+            if getattr(tp, "_special", False):
+                return ()
+            res = tp.__args__
+            if get_origin(tp) is collections.abc.Callable and res[0] is not Ellipsis:
+                res = (list(res[:-1]), res[-1])
+            return res
+        return ()
+
+
+# 3.10+
+if hasattr(typing, 'TypeAlias'):
+    TypeAlias = typing.TypeAlias
+# 3.9
+elif sys.version_info[:2] >= (3, 9):
+    class _TypeAliasForm(typing._SpecialForm, _root=True):
+        def __repr__(self):
+            return 'typing_extensions.' + self._name
+
+    @_TypeAliasForm
+    def TypeAlias(self, parameters):
+        """Special marker indicating that an assignment should
+        be recognized as a proper type alias definition by type
+        checkers.
+
+        For example::
+
+            Predicate: TypeAlias = Callable[..., bool]
+
+        It's invalid when used anywhere except as in the example above.
+        """
+        raise TypeError(f"{self} is not subscriptable")
+# 3.7-3.8
+elif sys.version_info[:2] >= (3, 7):
+    class _TypeAliasForm(typing._SpecialForm, _root=True):
+        def __repr__(self):
+            return 'typing_extensions.' + self._name
+
+    TypeAlias = _TypeAliasForm('TypeAlias',
+                               doc="""Special marker indicating that an assignment should
+                               be recognized as a proper type alias definition by type
+                               checkers.
+
+                               For example::
+
+                                   Predicate: TypeAlias = Callable[..., bool]
+
+                               It's invalid when used anywhere except as in the example
+                               above.""")
+# 3.6
+else:
+    class _TypeAliasMeta(typing.TypingMeta):
+        """Metaclass for TypeAlias"""
+
+        def __repr__(self):
+            return 'typing_extensions.TypeAlias'
+
+    class _TypeAliasBase(typing._FinalTypingBase, metaclass=_TypeAliasMeta, _root=True):
+        """Special marker indicating that an assignment should
+        be recognized as a proper type alias definition by type
+        checkers.
+
+        For example::
+
+            Predicate: TypeAlias = Callable[..., bool]
+
+        It's invalid when used anywhere except as in the example above.
+        """
+        __slots__ = ()
+
+        def __instancecheck__(self, obj):
+            raise TypeError("TypeAlias cannot be used with isinstance().")
+
+        def __subclasscheck__(self, cls):
+            raise TypeError("TypeAlias cannot be used with issubclass().")
+
+        def __repr__(self):
+            return 'typing_extensions.TypeAlias'
+
+    TypeAlias = _TypeAliasBase(_root=True)
+
+
+# Python 3.10+ has PEP 612
+if hasattr(typing, 'ParamSpecArgs'):
+    ParamSpecArgs = typing.ParamSpecArgs
+    ParamSpecKwargs = typing.ParamSpecKwargs
+# 3.6-3.9
+else:
+    class _Immutable:
+        """Mixin to indicate that object should not be copied."""
+        __slots__ = ()
+
+        def __copy__(self):
+            return self
+
+        def __deepcopy__(self, memo):
+            return self
+
+    class ParamSpecArgs(_Immutable):
+        """The args for a ParamSpec object.
+
+        Given a ParamSpec object P, P.args is an instance of ParamSpecArgs.
+
+        ParamSpecArgs objects have a reference back to their ParamSpec:
+
+        P.args.__origin__ is P
+
+        This type is meant for runtime introspection and has no special meaning to
+        static type checkers.
+        """
+        def __init__(self, origin):
+            self.__origin__ = origin
+
+        def __repr__(self):
+            return f"{self.__origin__.__name__}.args"
+
+    class ParamSpecKwargs(_Immutable):
+        """The kwargs for a ParamSpec object.
+
+        Given a ParamSpec object P, P.kwargs is an instance of ParamSpecKwargs.
+
+        ParamSpecKwargs objects have a reference back to their ParamSpec:
+
+        P.kwargs.__origin__ is P
+
+        This type is meant for runtime introspection and has no special meaning to
+        static type checkers.
+        """
+        def __init__(self, origin):
+            self.__origin__ = origin
+
+        def __repr__(self):
+            return f"{self.__origin__.__name__}.kwargs"
+
+# 3.10+
+if hasattr(typing, 'ParamSpec'):
+    ParamSpec = typing.ParamSpec
+# 3.6-3.9
+else:
+
+    # Inherits from list as a workaround for Callable checks in Python < 3.9.2.
+    class ParamSpec(list):
+        """Parameter specification variable.
+
+        Usage::
+
+           P = ParamSpec('P')
+
+        Parameter specification variables exist primarily for the benefit of static
+        type checkers.  They are used to forward the parameter types of one
+        callable to another callable, a pattern commonly found in higher order
+        functions and decorators.  They are only valid when used in ``Concatenate``,
+        or s the first argument to ``Callable``. In Python 3.10 and higher,
+        they are also supported in user-defined Generics at runtime.
+        See class Generic for more information on generic types.  An
+        example for annotating a decorator::
+
+           T = TypeVar('T')
+           P = ParamSpec('P')
+
+           def add_logging(f: Callable[P, T]) -> Callable[P, T]:
+               '''A type-safe decorator to add logging to a function.'''
+               def inner(*args: P.args, **kwargs: P.kwargs) -> T:
+                   logging.info(f'{f.__name__} was called')
+                   return f(*args, **kwargs)
+               return inner
+
+           @add_logging
+           def add_two(x: float, y: float) -> float:
+               '''Add two numbers together.'''
+               return x + y
+
+        Parameter specification variables defined with covariant=True or
+        contravariant=True can be used to declare covariant or contravariant
+        generic types.  These keyword arguments are valid, but their actual semantics
+        are yet to be decided.  See PEP 612 for details.
+
+        Parameter specification variables can be introspected. e.g.:
+
+           P.__name__ == 'T'
+           P.__bound__ == None
+           P.__covariant__ == False
+           P.__contravariant__ == False
+
+        Note that only parameter specification variables defined in global scope can
+        be pickled.
+        """
+
+        # Trick Generic __parameters__.
+        __class__ = typing.TypeVar
+
+        @property
+        def args(self):
+            return ParamSpecArgs(self)
+
+        @property
+        def kwargs(self):
+            return ParamSpecKwargs(self)
+
+        def __init__(self, name, *, bound=None, covariant=False, contravariant=False):
+            super().__init__([self])
+            self.__name__ = name
+            self.__covariant__ = bool(covariant)
+            self.__contravariant__ = bool(contravariant)
+            if bound:
+                self.__bound__ = typing._type_check(bound, 'Bound must be a type.')
+            else:
+                self.__bound__ = None
+
+            # for pickling:
+            try:
+                def_mod = sys._getframe(1).f_globals.get('__name__', '__main__')
+            except (AttributeError, ValueError):
+                def_mod = None
+            if def_mod != 'typing_extensions':
+                self.__module__ = def_mod
+
+        def __repr__(self):
+            if self.__covariant__:
+                prefix = '+'
+            elif self.__contravariant__:
+                prefix = '-'
+            else:
+                prefix = '~'
+            return prefix + self.__name__
+
+        def __hash__(self):
+            return object.__hash__(self)
+
+        def __eq__(self, other):
+            return self is other
+
+        def __reduce__(self):
+            return self.__name__
+
+        # Hack to get typing._type_check to pass.
+        def __call__(self, *args, **kwargs):
+            pass
+
+        if not PEP_560:
+            # Only needed in 3.6.
+            def _get_type_vars(self, tvars):
+                if self not in tvars:
+                    tvars.append(self)
+
+
+# 3.6-3.9
+if not hasattr(typing, 'Concatenate'):
+    # Inherits from list as a workaround for Callable checks in Python < 3.9.2.
+    class _ConcatenateGenericAlias(list):
+
+        # Trick Generic into looking into this for __parameters__.
+        if PEP_560:
+            __class__ = typing._GenericAlias
+        else:
+            __class__ = typing._TypingBase
+
+        # Flag in 3.8.
+        _special = False
+        # Attribute in 3.6 and earlier.
+        _gorg = typing.Generic
+
+        def __init__(self, origin, args):
+            super().__init__(args)
+            self.__origin__ = origin
+            self.__args__ = args
+
+        def __repr__(self):
+            _type_repr = typing._type_repr
+            return (f'{_type_repr(self.__origin__)}'
+                    f'[{", ".join(_type_repr(arg) for arg in self.__args__)}]')
+
+        def __hash__(self):
+            return hash((self.__origin__, self.__args__))
+
+        # Hack to get typing._type_check to pass in Generic.
+        def __call__(self, *args, **kwargs):
+            pass
+
+        @property
+        def __parameters__(self):
+            return tuple(
+                tp for tp in self.__args__ if isinstance(tp, (typing.TypeVar, ParamSpec))
+            )
+
+        if not PEP_560:
+            # Only required in 3.6.
+            def _get_type_vars(self, tvars):
+                if self.__origin__ and self.__parameters__:
+                    typing._get_type_vars(self.__parameters__, tvars)
+
+
+# 3.6-3.9
+@typing._tp_cache
+def _concatenate_getitem(self, parameters):
+    if parameters == ():
+        raise TypeError("Cannot take a Concatenate of no types.")
+    if not isinstance(parameters, tuple):
+        parameters = (parameters,)
+    if not isinstance(parameters[-1], ParamSpec):
+        raise TypeError("The last parameter to Concatenate should be a "
+                        "ParamSpec variable.")
+    msg = "Concatenate[arg, ...]: each arg must be a type."
+    parameters = tuple(typing._type_check(p, msg) for p in parameters)
+    return _ConcatenateGenericAlias(self, parameters)
+
+
+# 3.10+
+if hasattr(typing, 'Concatenate'):
+    Concatenate = typing.Concatenate
+    _ConcatenateGenericAlias = typing._ConcatenateGenericAlias # noqa
+# 3.9
+elif sys.version_info[:2] >= (3, 9):
+    @_TypeAliasForm
+    def Concatenate(self, parameters):
+        """Used in conjunction with ``ParamSpec`` and ``Callable`` to represent a
+        higher order function which adds, removes or transforms parameters of a
+        callable.
+
+        For example::
+
+           Callable[Concatenate[int, P], int]
+
+        See PEP 612 for detailed information.
+        """
+        return _concatenate_getitem(self, parameters)
+# 3.7-8
+elif sys.version_info[:2] >= (3, 7):
+    class _ConcatenateForm(typing._SpecialForm, _root=True):
+        def __repr__(self):
+            return 'typing_extensions.' + self._name
+
+        def __getitem__(self, parameters):
+            return _concatenate_getitem(self, parameters)
+
+    Concatenate = _ConcatenateForm(
+        'Concatenate',
+        doc="""Used in conjunction with ``ParamSpec`` and ``Callable`` to represent a
+        higher order function which adds, removes or transforms parameters of a
+        callable.
+
+        For example::
+
+           Callable[Concatenate[int, P], int]
+
+        See PEP 612 for detailed information.
+        """)
+# 3.6
+else:
+    class _ConcatenateAliasMeta(typing.TypingMeta):
+        """Metaclass for Concatenate."""
+
+        def __repr__(self):
+            return 'typing_extensions.Concatenate'
+
+    class _ConcatenateAliasBase(typing._FinalTypingBase,
+                                metaclass=_ConcatenateAliasMeta,
+                                _root=True):
+        """Used in conjunction with ``ParamSpec`` and ``Callable`` to represent a
+        higher order function which adds, removes or transforms parameters of a
+        callable.
+
+        For example::
+
+           Callable[Concatenate[int, P], int]
+
+        See PEP 612 for detailed information.
+        """
+        __slots__ = ()
+
+        def __instancecheck__(self, obj):
+            raise TypeError("Concatenate cannot be used with isinstance().")
+
+        def __subclasscheck__(self, cls):
+            raise TypeError("Concatenate cannot be used with issubclass().")
+
+        def __repr__(self):
+            return 'typing_extensions.Concatenate'
+
+        def __getitem__(self, parameters):
+            return _concatenate_getitem(self, parameters)
+
+    Concatenate = _ConcatenateAliasBase(_root=True)
+
+# 3.10+
+if hasattr(typing, 'TypeGuard'):
+    TypeGuard = typing.TypeGuard
+# 3.9
+elif sys.version_info[:2] >= (3, 9):
+    class _TypeGuardForm(typing._SpecialForm, _root=True):
+        def __repr__(self):
+            return 'typing_extensions.' + self._name
+
+    @_TypeGuardForm
+    def TypeGuard(self, parameters):
+        """Special typing form used to annotate the return type of a user-defined
+        type guard function.  ``TypeGuard`` only accepts a single type argument.
+        At runtime, functions marked this way should return a boolean.
+
+        ``TypeGuard`` aims to benefit *type narrowing* -- a technique used by static
+        type checkers to determine a more precise type of an expression within a
+        program's code flow.  Usually type narrowing is done by analyzing
+        conditional code flow and applying the narrowing to a block of code.  The
+        conditional expression here is sometimes referred to as a "type guard".
+
+        Sometimes it would be convenient to use a user-defined boolean function
+        as a type guard.  Such a function should use ``TypeGuard[...]`` as its
+        return type to alert static type checkers to this intention.
+
+        Using  ``-> TypeGuard`` tells the static type checker that for a given
+        function:
+
+        1. The return value is a boolean.
+        2. If the return value is ``True``, the type of its argument
+        is the type inside ``TypeGuard``.
+
+        For example::
+
+            def is_str(val: Union[str, float]):
+                # "isinstance" type guard
+                if isinstance(val, str):
+                    # Type of ``val`` is narrowed to ``str``
+                    ...
+                else:
+                    # Else, type of ``val`` is narrowed to ``float``.
+                    ...
+
+        Strict type narrowing is not enforced -- ``TypeB`` need not be a narrower
+        form of ``TypeA`` (it can even be a wider form) and this may lead to
+        type-unsafe results.  The main reason is to allow for things like
+        narrowing ``List[object]`` to ``List[str]`` even though the latter is not
+        a subtype of the former, since ``List`` is invariant.  The responsibility of
+        writing type-safe type guards is left to the user.
+
+        ``TypeGuard`` also works with type variables.  For more information, see
+        PEP 647 (User-Defined Type Guards).
+        """
+        item = typing._type_check(parameters, f'{self} accepts only single type.')
+        return typing._GenericAlias(self, (item,))
+# 3.7-3.8
+elif sys.version_info[:2] >= (3, 7):
+    class _TypeGuardForm(typing._SpecialForm, _root=True):
+
+        def __repr__(self):
+            return 'typing_extensions.' + self._name
+
+        def __getitem__(self, parameters):
+            item = typing._type_check(parameters,
+                                      f'{self._name} accepts only a single type')
+            return typing._GenericAlias(self, (item,))
+
+    TypeGuard = _TypeGuardForm(
+        'TypeGuard',
+        doc="""Special typing form used to annotate the return type of a user-defined
+        type guard function.  ``TypeGuard`` only accepts a single type argument.
+        At runtime, functions marked this way should return a boolean.
+
+        ``TypeGuard`` aims to benefit *type narrowing* -- a technique used by static
+        type checkers to determine a more precise type of an expression within a
+        program's code flow.  Usually type narrowing is done by analyzing
+        conditional code flow and applying the narrowing to a block of code.  The
+        conditional expression here is sometimes referred to as a "type guard".
+
+        Sometimes it would be convenient to use a user-defined boolean function
+        as a type guard.  Such a function should use ``TypeGuard[...]`` as its
+        return type to alert static type checkers to this intention.
+
+        Using  ``-> TypeGuard`` tells the static type checker that for a given
+        function:
+
+        1. The return value is a boolean.
+        2. If the return value is ``True``, the type of its argument
+        is the type inside ``TypeGuard``.
+
+        For example::
+
+            def is_str(val: Union[str, float]):
+                # "isinstance" type guard
+                if isinstance(val, str):
+                    # Type of ``val`` is narrowed to ``str``
+                    ...
+                else:
+                    # Else, type of ``val`` is narrowed to ``float``.
+                    ...
+
+        Strict type narrowing is not enforced -- ``TypeB`` need not be a narrower
+        form of ``TypeA`` (it can even be a wider form) and this may lead to
+        type-unsafe results.  The main reason is to allow for things like
+        narrowing ``List[object]`` to ``List[str]`` even though the latter is not
+        a subtype of the former, since ``List`` is invariant.  The responsibility of
+        writing type-safe type guards is left to the user.
+
+        ``TypeGuard`` also works with type variables.  For more information, see
+        PEP 647 (User-Defined Type Guards).
+        """)
+# 3.6
+else:
+    class _TypeGuard(typing._FinalTypingBase, _root=True):
+        """Special typing form used to annotate the return type of a user-defined
+        type guard function.  ``TypeGuard`` only accepts a single type argument.
+        At runtime, functions marked this way should return a boolean.
+
+        ``TypeGuard`` aims to benefit *type narrowing* -- a technique used by static
+        type checkers to determine a more precise type of an expression within a
+        program's code flow.  Usually type narrowing is done by analyzing
+        conditional code flow and applying the narrowing to a block of code.  The
+        conditional expression here is sometimes referred to as a "type guard".
+
+        Sometimes it would be convenient to use a user-defined boolean function
+        as a type guard.  Such a function should use ``TypeGuard[...]`` as its
+        return type to alert static type checkers to this intention.
+
+        Using  ``-> TypeGuard`` tells the static type checker that for a given
+        function:
+
+        1. The return value is a boolean.
+        2. If the return value is ``True``, the type of its argument
+        is the type inside ``TypeGuard``.
+
+        For example::
+
+            def is_str(val: Union[str, float]):
+                # "isinstance" type guard
+                if isinstance(val, str):
+                    # Type of ``val`` is narrowed to ``str``
+                    ...
+                else:
+                    # Else, type of ``val`` is narrowed to ``float``.
+                    ...
+
+        Strict type narrowing is not enforced -- ``TypeB`` need not be a narrower
+        form of ``TypeA`` (it can even be a wider form) and this may lead to
+        type-unsafe results.  The main reason is to allow for things like
+        narrowing ``List[object]`` to ``List[str]`` even though the latter is not
+        a subtype of the former, since ``List`` is invariant.  The responsibility of
+        writing type-safe type guards is left to the user.
+
+        ``TypeGuard`` also works with type variables.  For more information, see
+        PEP 647 (User-Defined Type Guards).
+        """
+
+        __slots__ = ('__type__',)
+
+        def __init__(self, tp=None, **kwds):
+            self.__type__ = tp
+
+        def __getitem__(self, item):
+            cls = type(self)
+            if self.__type__ is None:
+                return cls(typing._type_check(item,
+                           f'{cls.__name__[1:]} accepts only a single type.'),
+                           _root=True)
+            raise TypeError(f'{cls.__name__[1:]} cannot be further subscripted')
+
+        def _eval_type(self, globalns, localns):
+            new_tp = typing._eval_type(self.__type__, globalns, localns)
+            if new_tp == self.__type__:
+                return self
+            return type(self)(new_tp, _root=True)
+
+        def __repr__(self):
+            r = super().__repr__()
+            if self.__type__ is not None:
+                r += f'[{typing._type_repr(self.__type__)}]'
+            return r
+
+        def __hash__(self):
+            return hash((type(self).__name__, self.__type__))
+
+        def __eq__(self, other):
+            if not isinstance(other, _TypeGuard):
+                return NotImplemented
+            if self.__type__ is not None:
+                return self.__type__ == other.__type__
+            return self is other
+
+    TypeGuard = _TypeGuard(_root=True)
+
+if hasattr(typing, "Self"):
+    Self = typing.Self
+elif sys.version_info[:2] >= (3, 7):
+    # Vendored from cpython typing._SpecialFrom
+    class _SpecialForm(typing._Final, _root=True):
+        __slots__ = ('_name', '__doc__', '_getitem')
+
+        def __init__(self, getitem):
+            self._getitem = getitem
+            self._name = getitem.__name__
+            self.__doc__ = getitem.__doc__
+
+        def __getattr__(self, item):
+            if item in {'__name__', '__qualname__'}:
+                return self._name
+
+            raise AttributeError(item)
+
+        def __mro_entries__(self, bases):
+            raise TypeError(f"Cannot subclass {self!r}")
+
+        def __repr__(self):
+            return f'typing_extensions.{self._name}'
+
+        def __reduce__(self):
+            return self._name
+
+        def __call__(self, *args, **kwds):
+            raise TypeError(f"Cannot instantiate {self!r}")
+
+        def __or__(self, other):
+            return typing.Union[self, other]
+
+        def __ror__(self, other):
+            return typing.Union[other, self]
+
+        def __instancecheck__(self, obj):
+            raise TypeError(f"{self} cannot be used with isinstance()")
+
+        def __subclasscheck__(self, cls):
+            raise TypeError(f"{self} cannot be used with issubclass()")
+
+        @typing._tp_cache
+        def __getitem__(self, parameters):
+            return self._getitem(self, parameters)
+
+    @_SpecialForm
+    def Self(self, params):
+        """Used to spell the type of "self" in classes.
+
+        Example::
+
+          from typing import Self
+
+          class ReturnsSelf:
+              def parse(self, data: bytes) -> Self:
+                  ...
+                  return self
+
+        """
+
+        raise TypeError(f"{self} is not subscriptable")
+else:
+    class _Self(typing._FinalTypingBase, _root=True):
+        """Used to spell the type of "self" in classes.
+
+        Example::
+
+          from typing import Self
+
+          class ReturnsSelf:
+              def parse(self, data: bytes) -> Self:
+                  ...
+                  return self
+
+        """
+
+        __slots__ = ()
+
+        def __instancecheck__(self, obj):
+            raise TypeError(f"{self} cannot be used with isinstance().")
+
+        def __subclasscheck__(self, cls):
+            raise TypeError(f"{self} cannot be used with issubclass().")
+
+    Self = _Self(_root=True)
+
+
+if hasattr(typing, 'Required'):
+    Required = typing.Required
+    NotRequired = typing.NotRequired
+elif sys.version_info[:2] >= (3, 9):
+    class _ExtensionsSpecialForm(typing._SpecialForm, _root=True):
+        def __repr__(self):
+            return 'typing_extensions.' + self._name
+
+    @_ExtensionsSpecialForm
+    def Required(self, parameters):
+        """A special typing construct to mark a key of a total=False TypedDict
+        as required. For example:
+
+            class Movie(TypedDict, total=False):
+                title: Required[str]
+                year: int
+
+            m = Movie(
+                title='The Matrix',  # typechecker error if key is omitted
+                year=1999,
+            )
+
+        There is no runtime checking that a required key is actually provided
+        when instantiating a related TypedDict.
+        """
+        item = typing._type_check(parameters, f'{self._name} accepts only single type')
+        return typing._GenericAlias(self, (item,))
+
+    @_ExtensionsSpecialForm
+    def NotRequired(self, parameters):
+        """A special typing construct to mark a key of a TypedDict as
+        potentially missing. For example:
+
+            class Movie(TypedDict):
+                title: str
+                year: NotRequired[int]
+
+            m = Movie(
+                title='The Matrix',  # typechecker error if key is omitted
+                year=1999,
+            )
+        """
+        item = typing._type_check(parameters, f'{self._name} accepts only single type')
+        return typing._GenericAlias(self, (item,))
+
+elif sys.version_info[:2] >= (3, 7):
+    class _RequiredForm(typing._SpecialForm, _root=True):
+        def __repr__(self):
+            return 'typing_extensions.' + self._name
+
+        def __getitem__(self, parameters):
+            item = typing._type_check(parameters,
+                                      '{} accepts only single type'.format(self._name))
+            return typing._GenericAlias(self, (item,))
+
+    Required = _RequiredForm(
+        'Required',
+        doc="""A special typing construct to mark a key of a total=False TypedDict
+        as required. For example:
+
+            class Movie(TypedDict, total=False):
+                title: Required[str]
+                year: int
+
+            m = Movie(
+                title='The Matrix',  # typechecker error if key is omitted
+                year=1999,
+            )
+
+        There is no runtime checking that a required key is actually provided
+        when instantiating a related TypedDict.
+        """)
+    NotRequired = _RequiredForm(
+        'NotRequired',
+        doc="""A special typing construct to mark a key of a TypedDict as
+        potentially missing. For example:
+
+            class Movie(TypedDict):
+                title: str
+                year: NotRequired[int]
+
+            m = Movie(
+                title='The Matrix',  # typechecker error if key is omitted
+                year=1999,
+            )
+        """)
+else:
+    # NOTE: Modeled after _Final's implementation when _FinalTypingBase available
+    class _MaybeRequired(typing._FinalTypingBase, _root=True):
+        __slots__ = ('__type__',)
+
+        def __init__(self, tp=None, **kwds):
+            self.__type__ = tp
+
+        def __getitem__(self, item):
+            cls = type(self)
+            if self.__type__ is None:
+                return cls(typing._type_check(item,
+                           '{} accepts only single type.'.format(cls.__name__[1:])),
+                           _root=True)
+            raise TypeError('{} cannot be further subscripted'
+                            .format(cls.__name__[1:]))
+
+        def _eval_type(self, globalns, localns):
+            new_tp = typing._eval_type(self.__type__, globalns, localns)
+            if new_tp == self.__type__:
+                return self
+            return type(self)(new_tp, _root=True)
+
+        def __repr__(self):
+            r = super().__repr__()
+            if self.__type__ is not None:
+                r += '[{}]'.format(typing._type_repr(self.__type__))
+            return r
+
+        def __hash__(self):
+            return hash((type(self).__name__, self.__type__))
+
+        def __eq__(self, other):
+            if not isinstance(other, type(self)):
+                return NotImplemented
+            if self.__type__ is not None:
+                return self.__type__ == other.__type__
+            return self is other
+
+    class _Required(_MaybeRequired, _root=True):
+        """A special typing construct to mark a key of a total=False TypedDict
+        as required. For example:
+
+            class Movie(TypedDict, total=False):
+                title: Required[str]
+                year: int
+
+            m = Movie(
+                title='The Matrix',  # typechecker error if key is omitted
+                year=1999,
+            )
+
+        There is no runtime checking that a required key is actually provided
+        when instantiating a related TypedDict.
+        """
+
+    class _NotRequired(_MaybeRequired, _root=True):
+        """A special typing construct to mark a key of a TypedDict as
+        potentially missing. For example:
+
+            class Movie(TypedDict):
+                title: str
+                year: NotRequired[int]
+
+            m = Movie(
+                title='The Matrix',  # typechecker error if key is omitted
+                year=1999,
+            )
+        """
+
+    Required = _Required(_root=True)
+    NotRequired = _NotRequired(_root=True)
diff --git a/venv/Lib/site-packages/setuptools/_vendor/zipp.py b/venv/Lib/site-packages/setuptools/_vendor/zipp.py
new file mode 100644
index 0000000..26b723c
--- /dev/null
+++ b/venv/Lib/site-packages/setuptools/_vendor/zipp.py
@@ -0,0 +1,329 @@
+import io
+import posixpath
+import zipfile
+import itertools
+import contextlib
+import sys
+import pathlib
+
+if sys.version_info < (3, 7):
+    from collections import OrderedDict
+else:
+    OrderedDict = dict
+
+
+__all__ = ['Path']
+
+
+def _parents(path):
+    """
+    Given a path with elements separated by
+    posixpath.sep, generate all parents of that path.
+
+    >>> list(_parents('b/d'))
+    ['b']
+    >>> list(_parents('/b/d/'))
+    ['/b']
+    >>> list(_parents('b/d/f/'))
+    ['b/d', 'b']
+    >>> list(_parents('b'))
+    []
+    >>> list(_parents(''))
+    []
+    """
+    return itertools.islice(_ancestry(path), 1, None)
+
+
+def _ancestry(path):
+    """
+    Given a path with elements separated by
+    posixpath.sep, generate all elements of that path
+
+    >>> list(_ancestry('b/d'))
+    ['b/d', 'b']
+    >>> list(_ancestry('/b/d/'))
+    ['/b/d', '/b']
+    >>> list(_ancestry('b/d/f/'))
+    ['b/d/f', 'b/d', 'b']
+    >>> list(_ancestry('b'))
+    ['b']
+    >>> list(_ancestry(''))
+    []
+    """
+    path = path.rstrip(posixpath.sep)
+    while path and path != posixpath.sep:
+        yield path
+        path, tail = posixpath.split(path)
+
+
+_dedupe = OrderedDict.fromkeys
+"""Deduplicate an iterable in original order"""
+
+
+def _difference(minuend, subtrahend):
+    """
+    Return items in minuend not in subtrahend, retaining order
+    with O(1) lookup.
+    """
+    return itertools.filterfalse(set(subtrahend).__contains__, minuend)
+
+
+class CompleteDirs(zipfile.ZipFile):
+    """
+    A ZipFile subclass that ensures that implied directories
+    are always included in the namelist.
+    """
+
+    @staticmethod
+    def _implied_dirs(names):
+        parents = itertools.chain.from_iterable(map(_parents, names))
+        as_dirs = (p + posixpath.sep for p in parents)
+        return _dedupe(_difference(as_dirs, names))
+
+    def namelist(self):
+        names = super(CompleteDirs, self).namelist()
+        return names + list(self._implied_dirs(names))
+
+    def _name_set(self):
+        return set(self.namelist())
+
+    def resolve_dir(self, name):
+        """
+        If the name represents a directory, return that name
+        as a directory (with the trailing slash).
+        """
+        names = self._name_set()
+        dirname = name + '/'
+        dir_match = name not in names and dirname in names
+        return dirname if dir_match else name
+
+    @classmethod
+    def make(cls, source):
+        """
+        Given a source (filename or zipfile), return an
+        appropriate CompleteDirs subclass.
+        """
+        if isinstance(source, CompleteDirs):
+            return source
+
+        if not isinstance(source, zipfile.ZipFile):
+            return cls(_pathlib_compat(source))
+
+        # Only allow for FastLookup when supplied zipfile is read-only
+        if 'r' not in source.mode:
+            cls = CompleteDirs
+
+        source.__class__ = cls
+        return source
+
+
+class FastLookup(CompleteDirs):
+    """
+    ZipFile subclass to ensure implicit
+    dirs exist and are resolved rapidly.
+    """
+
+    def namelist(self):
+        with contextlib.suppress(AttributeError):
+            return self.__names
+        self.__names = super(FastLookup, self).namelist()
+        return self.__names
+
+    def _name_set(self):
+        with contextlib.suppress(AttributeError):
+            return self.__lookup
+        self.__lookup = super(FastLookup, self)._name_set()
+        return self.__lookup
+
+
+def _pathlib_compat(path):
+    """
+    For path-like objects, convert to a filename for compatibility
+    on Python 3.6.1 and earlier.
+    """
+    try:
+        return path.__fspath__()
+    except AttributeError:
+        return str(path)
+
+
+class Path:
+    """
+    A pathlib-compatible interface for zip files.
+
+    Consider a zip file with this structure::
+
+        .
+        ├── a.txt
+        └── b
+            ├── c.txt
+            └── d
+                └── e.txt
+
+    >>> data = io.BytesIO()
+    >>> zf = zipfile.ZipFile(data, 'w')
+    >>> zf.writestr('a.txt', 'content of a')
+    >>> zf.writestr('b/c.txt', 'content of c')
+    >>> zf.writestr('b/d/e.txt', 'content of e')
+    >>> zf.filename = 'mem/abcde.zip'
+
+    Path accepts the zipfile object itself or a filename
+
+    >>> root = Path(zf)
+
+    From there, several path operations are available.
+
+    Directory iteration (including the zip file itself):
+
+    >>> a, b = root.iterdir()
+    >>> a
+    Path('mem/abcde.zip', 'a.txt')
+    >>> b
+    Path('mem/abcde.zip', 'b/')
+
+    name property:
+
+    >>> b.name
+    'b'
+
+    join with divide operator:
+
+    >>> c = b / 'c.txt'
+    >>> c
+    Path('mem/abcde.zip', 'b/c.txt')
+    >>> c.name
+    'c.txt'
+
+    Read text:
+
+    >>> c.read_text()
+    'content of c'
+
+    existence:
+
+    >>> c.exists()
+    True
+    >>> (b / 'missing.txt').exists()
+    False
+
+    Coercion to string:
+
+    >>> import os
+    >>> str(c).replace(os.sep, posixpath.sep)
+    'mem/abcde.zip/b/c.txt'
+
+    At the root, ``name``, ``filename``, and ``parent``
+    resolve to the zipfile. Note these attributes are not
+    valid and will raise a ``ValueError`` if the zipfile
+    has no filename.
+
+    >>> root.name
+    'abcde.zip'
+    >>> str(root.filename).replace(os.sep, posixpath.sep)
+    'mem/abcde.zip'
+    >>> str(root.parent)
+    'mem'
+    """
+
+    __repr = "{self.__class__.__name__}({self.root.filename!r}, {self.at!r})"
+
+    def __init__(self, root, at=""):
+        """
+        Construct a Path from a ZipFile or filename.
+
+        Note: When the source is an existing ZipFile object,
+        its type (__class__) will be mutated to a
+        specialized type. If the caller wishes to retain the
+        original type, the caller should either create a
+        separate ZipFile object or pass a filename.
+        """
+        self.root = FastLookup.make(root)
+        self.at = at
+
+    def open(self, mode='r', *args, pwd=None, **kwargs):
+        """
+        Open this entry as text or binary following the semantics
+        of ``pathlib.Path.open()`` by passing arguments through
+        to io.TextIOWrapper().
+        """
+        if self.is_dir():
+            raise IsADirectoryError(self)
+        zip_mode = mode[0]
+        if not self.exists() and zip_mode == 'r':
+            raise FileNotFoundError(self)
+        stream = self.root.open(self.at, zip_mode, pwd=pwd)
+        if 'b' in mode:
+            if args or kwargs:
+                raise ValueError("encoding args invalid for binary operation")
+            return stream
+        return io.TextIOWrapper(stream, *args, **kwargs)
+
+    @property
+    def name(self):
+        return pathlib.Path(self.at).name or self.filename.name
+
+    @property
+    def suffix(self):
+        return pathlib.Path(self.at).suffix or self.filename.suffix
+
+    @property
+    def suffixes(self):
+        return pathlib.Path(self.at).suffixes or self.filename.suffixes
+
+    @property
+    def stem(self):
+        return pathlib.Path(self.at).stem or self.filename.stem
+
+    @property
+    def filename(self):
+        return pathlib.Path(self.root.filename).joinpath(self.at)
+
+    def read_text(self, *args, **kwargs):
+        with self.open('r', *args, **kwargs) as strm:
+            return strm.read()
+
+    def read_bytes(self):
+        with self.open('rb') as strm:
+            return strm.read()
+
+    def _is_child(self, path):
+        return posixpath.dirname(path.at.rstrip("/")) == self.at.rstrip("/")
+
+    def _next(self, at):
+        return self.__class__(self.root, at)
+
+    def is_dir(self):
+        return not self.at or self.at.endswith("/")
+
+    def is_file(self):
+        return self.exists() and not self.is_dir()
+
+    def exists(self):
+        return self.at in self.root._name_set()
+
+    def iterdir(self):
+        if not self.is_dir():
+            raise ValueError("Can't listdir a file")
+        subs = map(self._next, self.root.namelist())
+        return filter(self._is_child, subs)
+
+    def __str__(self):
+        return posixpath.join(self.root.filename, self.at)
+
+    def __repr__(self):
+        return self.__repr.format(self=self)
+
+    def joinpath(self, *other):
+        next = posixpath.join(self.at, *map(_pathlib_compat, other))
+        return self._next(self.root.resolve_dir(next))
+
+    __truediv__ = joinpath
+
+    @property
+    def parent(self):
+        if not self.at:
+            return self.filename.parent
+        parent_at = posixpath.dirname(self.at.rstrip('/'))
+        if parent_at:
+            parent_at += '/'
+        return self._next(parent_at)
diff --git a/venv/Lib/site-packages/setuptools/archive_util.py b/venv/Lib/site-packages/setuptools/archive_util.py
new file mode 100644
index 0000000..6b8460b
--- /dev/null
+++ b/venv/Lib/site-packages/setuptools/archive_util.py
@@ -0,0 +1,216 @@
+"""Utilities for extracting common archive formats"""
+
+import zipfile
+import tarfile
+import os
+import shutil
+import posixpath
+import contextlib
+from distutils.errors import DistutilsError
+
+from ._path 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:
+        _unpack_zipfile_obj(z, extract_dir, progress_filter)
+
+
+def _unpack_zipfile_obj(zipfile_obj, extract_dir, progress_filter=default_filter):
+    """Internal/private API used by other parts of setuptools.
+    Similar to ``unpack_zipfile``, but receives an already opened :obj:`zipfile.ZipFile`
+    object instead of a filename.
+    """
+    for info in zipfile_obj.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 = zipfile_obj.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 _resolve_tar_file_or_dir(tar_obj, tar_member_obj):
+    """Resolve any links and extract link targets as normal files."""
+    while tar_member_obj is not None and (
+        tar_member_obj.islnk() or tar_member_obj.issym()
+    ):
+        linkpath = tar_member_obj.linkname
+        if tar_member_obj.issym():
+            base = posixpath.dirname(tar_member_obj.name)
+            linkpath = posixpath.join(base, linkpath)
+            linkpath = posixpath.normpath(linkpath)
+        tar_member_obj = tar_obj._getmember(linkpath)
+
+    is_file_or_dir = tar_member_obj is not None and (
+        tar_member_obj.isfile() or tar_member_obj.isdir()
+    )
+    if is_file_or_dir:
+        return tar_member_obj
+
+    raise LookupError('Got unknown file type')
+
+
+def _iter_open_tar(tar_obj, extract_dir, progress_filter):
+    """Emit member-destination pairs from a tar archive."""
+    # don't do any chowning!
+    tar_obj.chown = lambda *args: None
+
+    with contextlib.closing(tar_obj):
+        for member in tar_obj:
+            name = member.name
+            # don't extract absolute paths or ones with .. in them
+            if name.startswith('/') or '..' in name.split('/'):
+                continue
+
+            prelim_dst = os.path.join(extract_dir, *name.split('/'))
+
+            try:
+                member = _resolve_tar_file_or_dir(tar_obj, member)
+            except LookupError:
+                continue
+
+            final_dst = progress_filter(name, prelim_dst)
+            if not final_dst:
+                continue
+
+            if final_dst.endswith(os.sep):
+                final_dst = final_dst[:-1]
+
+            yield member, final_dst
+
+
+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 as e:
+        raise UnrecognizedFormat(
+            "%s is not a compressed or uncompressed tar file" % (filename,)
+        ) from e
+
+    for member, final_dst in _iter_open_tar(
+        tarobj,
+        extract_dir,
+        progress_filter,
+    ):
+        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/site-packages/setuptools/build_meta.py b/venv/Lib/site-packages/setuptools/build_meta.py
new file mode 100644
index 0000000..2decd2d
--- /dev/null
+++ b/venv/Lib/site-packages/setuptools/build_meta.py
@@ -0,0 +1,514 @@
+"""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 shlex
+import sys
+import tokenize
+import shutil
+import contextlib
+import tempfile
+import warnings
+from pathlib import Path
+from typing import Dict, Iterator, List, Optional, Union
+
+import setuptools
+import distutils
+from . import errors
+from ._path import same_path
+from ._reqs import parse_strings
+from .warnings import SetuptoolsDeprecationWarning
+from distutils.util import strtobool
+
+
+__all__ = [
+    'get_requires_for_build_sdist',
+    'get_requires_for_build_wheel',
+    'prepare_metadata_for_build_wheel',
+    'build_wheel',
+    'build_sdist',
+    'get_requires_for_build_editable',
+    'prepare_metadata_for_build_editable',
+    'build_editable',
+    '__legacy__',
+    'SetupRequirementsError',
+]
+
+SETUPTOOLS_ENABLE_FEATURES = os.getenv("SETUPTOOLS_ENABLE_FEATURES", "").lower()
+LEGACY_EDITABLE = "legacy-editable" in SETUPTOOLS_ENABLE_FEATURES.replace("_", "-")
+
+
+class SetupRequirementsError(BaseException):
+    def __init__(self, specifiers):
+        self.specifiers = specifiers
+
+
+class Distribution(setuptools.dist.Distribution):
+    def fetch_build_eggs(self, specifiers):
+        specifier_list = list(parse_strings(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
+
+
+@contextlib.contextmanager
+def no_install_setup_requires():
+    """Temporarily disable installing setup_requires
+
+    Under PEP 517, the backend reports build dependencies to the frontend,
+    and the frontend is responsible for ensuring they're installed.
+    So setuptools (acting as a backend) should not try to install them.
+    """
+    orig = setuptools._install_setup_requires
+    setuptools._install_setup_requires = lambda attrs: None
+    try:
+        yield
+    finally:
+        setuptools._install_setup_requires = orig
+
+
+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))
+    try:
+        (file,) = matching
+    except ValueError:
+        raise ValueError(
+            'No distribution was found. Ensure that `setup.py` '
+            'is not empty and that it calls `setup()`.'
+        ) from None
+    return file
+
+
+def _open_setup_script(setup_script):
+    if not os.path.exists(setup_script):
+        # Supply a default setup.py
+        return io.StringIO("from setuptools import setup; setup()")
+
+    return tokenize.open(setup_script)
+
+
+@contextlib.contextmanager
+def suppress_known_deprecation():
+    with warnings.catch_warnings():
+        warnings.filterwarnings('ignore', 'setup.py install is deprecated')
+        yield
+
+
+_ConfigSettings = Optional[Dict[str, Union[str, List[str], None]]]
+"""
+Currently the user can run::
+
+    pip install -e . --config-settings key=value
+    python -m build -C--key=value -C key=value
+
+- pip will pass both key and value as strings and overwriting repeated keys
+  (pypa/pip#11059).
+- build will accumulate values associated with repeated keys in a list.
+  It will also accept keys with no associated value.
+  This means that an option passed by build can be ``str | list[str] | None``.
+- PEP 517 specifies that ``config_settings`` is an optional dict.
+"""
+
+
+class _ConfigSettingsTranslator:
+    """Translate ``config_settings`` into distutils-style command arguments.
+    Only a limited number of options is currently supported.
+    """
+
+    # See pypa/setuptools#1928 pypa/setuptools#2491
+
+    def _get_config(self, key: str, config_settings: _ConfigSettings) -> List[str]:
+        """
+        Get the value of a specific key in ``config_settings`` as a list of strings.
+
+        >>> fn = _ConfigSettingsTranslator()._get_config
+        >>> fn("--global-option", None)
+        []
+        >>> fn("--global-option", {})
+        []
+        >>> fn("--global-option", {'--global-option': 'foo'})
+        ['foo']
+        >>> fn("--global-option", {'--global-option': ['foo']})
+        ['foo']
+        >>> fn("--global-option", {'--global-option': 'foo'})
+        ['foo']
+        >>> fn("--global-option", {'--global-option': 'foo bar'})
+        ['foo', 'bar']
+        """
+        cfg = config_settings or {}
+        opts = cfg.get(key) or []
+        return shlex.split(opts) if isinstance(opts, str) else opts
+
+    def _global_args(self, config_settings: _ConfigSettings) -> Iterator[str]:
+        """
+        Let the user specify ``verbose`` or ``quiet`` + escape hatch via
+        ``--global-option``.
+        Note: ``-v``, ``-vv``, ``-vvv`` have similar effects in setuptools,
+        so we just have to cover the basic scenario ``-v``.
+
+        >>> fn = _ConfigSettingsTranslator()._global_args
+        >>> list(fn(None))
+        []
+        >>> list(fn({"verbose": "False"}))
+        ['-q']
+        >>> list(fn({"verbose": "1"}))
+        ['-v']
+        >>> list(fn({"--verbose": None}))
+        ['-v']
+        >>> list(fn({"verbose": "true", "--global-option": "-q --no-user-cfg"}))
+        ['-v', '-q', '--no-user-cfg']
+        >>> list(fn({"--quiet": None}))
+        ['-q']
+        """
+        cfg = config_settings or {}
+        falsey = {"false", "no", "0", "off"}
+        if "verbose" in cfg or "--verbose" in cfg:
+            level = str(cfg.get("verbose") or cfg.get("--verbose") or "1")
+            yield ("-q" if level.lower() in falsey else "-v")
+        if "quiet" in cfg or "--quiet" in cfg:
+            level = str(cfg.get("quiet") or cfg.get("--quiet") or "1")
+            yield ("-v" if level.lower() in falsey else "-q")
+
+        yield from self._get_config("--global-option", config_settings)
+
+    def __dist_info_args(self, config_settings: _ConfigSettings) -> Iterator[str]:
+        """
+        The ``dist_info`` command accepts ``tag-date`` and ``tag-build``.
+
+        .. warning::
+           We cannot use this yet as it requires the ``sdist`` and ``bdist_wheel``
+           commands run in ``build_sdist`` and ``build_wheel`` to reuse the egg-info
+           directory created in ``prepare_metadata_for_build_wheel``.
+
+        >>> fn = _ConfigSettingsTranslator()._ConfigSettingsTranslator__dist_info_args
+        >>> list(fn(None))
+        []
+        >>> list(fn({"tag-date": "False"}))
+        ['--no-date']
+        >>> list(fn({"tag-date": None}))
+        ['--no-date']
+        >>> list(fn({"tag-date": "true", "tag-build": ".a"}))
+        ['--tag-date', '--tag-build', '.a']
+        """
+        cfg = config_settings or {}
+        if "tag-date" in cfg:
+            val = strtobool(str(cfg["tag-date"] or "false"))
+            yield ("--tag-date" if val else "--no-date")
+        if "tag-build" in cfg:
+            yield from ["--tag-build", str(cfg["tag-build"])]
+
+    def _editable_args(self, config_settings: _ConfigSettings) -> Iterator[str]:
+        """
+        The ``editable_wheel`` command accepts ``editable-mode=strict``.
+
+        >>> fn = _ConfigSettingsTranslator()._editable_args
+        >>> list(fn(None))
+        []
+        >>> list(fn({"editable-mode": "strict"}))
+        ['--mode', 'strict']
+        """
+        cfg = config_settings or {}
+        mode = cfg.get("editable-mode") or cfg.get("editable_mode")
+        if not mode:
+            return
+        yield from ["--mode", str(mode)]
+
+    def _arbitrary_args(self, config_settings: _ConfigSettings) -> Iterator[str]:
+        """
+        Users may expect to pass arbitrary lists of arguments to a command
+        via "--global-option" (example provided in PEP 517 of a "escape hatch").
+
+        >>> fn = _ConfigSettingsTranslator()._arbitrary_args
+        >>> list(fn(None))
+        []
+        >>> list(fn({}))
+        []
+        >>> list(fn({'--build-option': 'foo'}))
+        ['foo']
+        >>> list(fn({'--build-option': ['foo']}))
+        ['foo']
+        >>> list(fn({'--build-option': 'foo'}))
+        ['foo']
+        >>> list(fn({'--build-option': 'foo bar'}))
+        ['foo', 'bar']
+        >>> list(fn({'--global-option': 'foo'}))
+        []
+        """
+        yield from self._get_config("--build-option", config_settings)
+
+
+class _BuildMetaBackend(_ConfigSettingsTranslator):
+    def _get_build_requires(self, config_settings, requirements):
+        sys.argv = [
+            *sys.argv[:1],
+            *self._global_args(config_settings),
+            "egg_info",
+        ]
+        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__ = os.path.abspath(setup_script)
+        __name__ = '__main__'
+
+        with _open_setup_script(__file__) as f:
+            code = f.read().replace(r'\r\n', r'\n')
+
+        try:
+            exec(code, locals())
+        except SystemExit as e:
+            if e.code:
+                raise
+            # We ignore exit code indicating success
+            SetuptoolsDeprecationWarning.emit(
+                "Running `setup.py` directly as CLI tool is deprecated.",
+                "Please avoid using `sys.exit(0)` or similar statements "
+                "that don't fit in the paradigm of a configuration file.",
+                see_url="https://blog.ganssle.io/articles/2021/10/"
+                "setup-py-deprecated.html",
+            )
+
+    def get_requires_for_build_wheel(self, config_settings=None):
+        return self._get_build_requires(config_settings, requirements=['wheel'])
+
+    def get_requires_for_build_sdist(self, config_settings=None):
+        return self._get_build_requires(config_settings, requirements=[])
+
+    def _bubble_up_info_directory(self, metadata_directory: str, suffix: str) -> str:
+        """
+        PEP 517 requires that the .dist-info directory be placed in the
+        metadata_directory. To comply, we MUST copy the directory to the root.
+
+        Returns the basename of the info directory, e.g. `proj-0.0.0.dist-info`.
+        """
+        info_dir = self._find_info_directory(metadata_directory, suffix)
+        if not same_path(info_dir.parent, metadata_directory):
+            shutil.move(str(info_dir), metadata_directory)
+            # PEP 517 allow other files and dirs to exist in metadata_directory
+        return info_dir.name
+
+    def _find_info_directory(self, metadata_directory: str, suffix: str) -> Path:
+        for parent, dirs, _ in os.walk(metadata_directory):
+            candidates = [f for f in dirs if f.endswith(suffix)]
+
+            if len(candidates) != 0 or len(dirs) != 1:
+                assert len(candidates) == 1, f"Multiple {suffix} directories found"
+                return Path(parent, candidates[0])
+
+        msg = f"No {suffix} directory found in {metadata_directory}"
+        raise errors.InternalError(msg)
+
+    def prepare_metadata_for_build_wheel(
+        self, metadata_directory, config_settings=None
+    ):
+        sys.argv = [
+            *sys.argv[:1],
+            *self._global_args(config_settings),
+            "dist_info",
+            "--output-dir",
+            metadata_directory,
+            "--keep-egg-info",
+        ]
+        with no_install_setup_requires():
+            self.run_setup()
+
+        self._bubble_up_info_directory(metadata_directory, ".egg-info")
+        return self._bubble_up_info_directory(metadata_directory, ".dist-info")
+
+    def _build_with_temp_dir(
+        self,
+        setup_command,
+        result_extension,
+        result_directory,
+        config_settings,
+        arbitrary_args=(),
+    ):
+        result_directory = os.path.abspath(result_directory)
+
+        # Build in a temporary directory, then copy to the target.
+        os.makedirs(result_directory, exist_ok=True)
+        temp_opts = {"prefix": ".tmp-", "dir": result_directory}
+
+        with tempfile.TemporaryDirectory(**temp_opts) as tmp_dist_dir:
+            sys.argv = [
+                *sys.argv[:1],
+                *self._global_args(config_settings),
+                *setup_command,
+                "--dist-dir",
+                tmp_dist_dir,
+                *arbitrary_args,
+            ]
+            with no_install_setup_requires():
+                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
+    ):
+        with suppress_known_deprecation():
+            return self._build_with_temp_dir(
+                ['bdist_wheel'],
+                '.whl',
+                wheel_directory,
+                config_settings,
+                self._arbitrary_args(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
+        )
+
+    def _get_dist_info_dir(self, metadata_directory: Optional[str]) -> Optional[str]:
+        if not metadata_directory:
+            return None
+        dist_info_candidates = list(Path(metadata_directory).glob("*.dist-info"))
+        assert len(dist_info_candidates) <= 1
+        return str(dist_info_candidates[0]) if dist_info_candidates else None
+
+    if not LEGACY_EDITABLE:
+        # PEP660 hooks:
+        # build_editable
+        # get_requires_for_build_editable
+        # prepare_metadata_for_build_editable
+        def build_editable(
+            self, wheel_directory, config_settings=None, metadata_directory=None
+        ):
+            # XXX can or should we hide our editable_wheel command normally?
+            info_dir = self._get_dist_info_dir(metadata_directory)
+            opts = ["--dist-info-dir", info_dir] if info_dir else []
+            cmd = ["editable_wheel", *opts, *self._editable_args(config_settings)]
+            with suppress_known_deprecation():
+                return self._build_with_temp_dir(
+                    cmd, ".whl", wheel_directory, config_settings
+                )
+
+        def get_requires_for_build_editable(self, config_settings=None):
+            return self.get_requires_for_build_wheel(config_settings)
+
+        def prepare_metadata_for_build_editable(
+            self, metadata_directory, config_settings=None
+        ):
+            return self.prepare_metadata_for_build_wheel(
+                metadata_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().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
+
+if not LEGACY_EDITABLE:
+    get_requires_for_build_editable = _BACKEND.get_requires_for_build_editable
+    prepare_metadata_for_build_editable = _BACKEND.prepare_metadata_for_build_editable
+    build_editable = _BACKEND.build_editable
+
+
+# The legacy backend
+__legacy__ = _BuildMetaLegacyBackend()
diff --git a/venv/Lib/site-packages/setuptools/cli-32.exe b/venv/Lib/site-packages/setuptools/cli-32.exe
new file mode 100644
index 0000000000000000000000000000000000000000..65c3cd99cc7433f271a5b9387abdd1ddb949d1a6
GIT binary patch
literal 11776
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
I{a-)-138F7F#rGn

literal 0
HcmV?d00001

diff --git a/venv/Lib/site-packages/setuptools/cli-64.exe b/venv/Lib/site-packages/setuptools/cli-64.exe
new file mode 100644
index 0000000000000000000000000000000000000000..3ea50eebfe3f0113b231a318cc1ad6e238afd60d
GIT binary patch
literal 14336
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
AO8@`>

literal 0
HcmV?d00001

diff --git a/venv/Lib/site-packages/setuptools/cli-arm64.exe b/venv/Lib/site-packages/setuptools/cli-arm64.exe
new file mode 100644
index 0000000000000000000000000000000000000000..da96455a07a0bad4cde5dc5626544325f82c722b
GIT binary patch
literal 13824
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
I{a-)-138F7F#rGn

literal 0
HcmV?d00001

diff --git a/venv/Lib/site-packages/setuptools/command/__init__.py b/venv/Lib/site-packages/setuptools/command/__init__.py
new file mode 100644
index 0000000..5acd768
--- /dev/null
+++ b/venv/Lib/site-packages/setuptools/command/__init__.py
@@ -0,0 +1,12 @@
+from distutils.command.bdist import bdist
+import sys
+
+if 'egg' not in bdist.format_commands:
+    try:
+        bdist.format_commands['egg'] = ('bdist_egg', "Python .egg file")
+    except TypeError:
+        # For backward compatibility with older distutils (stdlib)
+        bdist.format_command['egg'] = ('bdist_egg', "Python .egg file")
+        bdist.format_commands.append('egg')
+
+del bdist, sys
diff --git a/venv/Lib/site-packages/setuptools/command/__pycache__/__init__.cpython-312.pyc b/venv/Lib/site-packages/setuptools/command/__pycache__/__init__.cpython-312.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..04f3171138bc57d091e7f04dd9e495f1aa59517d
GIT binary patch
literal 663
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

literal 0
HcmV?d00001

diff --git a/venv/Lib/site-packages/setuptools/command/__pycache__/alias.cpython-312.pyc b/venv/Lib/site-packages/setuptools/command/__pycache__/alias.cpython-312.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..9575100e8f20db5d250a5371ea732c5745567450
GIT binary patch
literal 3549
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
bmEXDMvGe6?TezB+`6bT#v1JSIV`KUMEfRC0

literal 0
HcmV?d00001

diff --git a/venv/Lib/site-packages/setuptools/command/__pycache__/bdist_rpm.cpython-312.pyc b/venv/Lib/site-packages/setuptools/command/__pycache__/bdist_rpm.cpython-312.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..164902af3ec0be8b4005d47b075a96e957d3dbf1
GIT binary patch
literal 1996
zcma)7-ESL35a0W-eR1QDv`W>67B=b^SIM?YK`IqOs#;V)Aw;bZl4UrZZ`aPH>pQc1
z7h^Z6c<2L>kkY=8{sBAy;XmP}QK=H0kPrfd^5!-aM4yZo;1K}01Q(>s}ZMnNjNiimz2
zkqp%q^z`MvZCy(ab+xaU*=>24-By8`N_6Zx4Z?2V`%GM+T}~Z4aDDGXn|rR;c9M4q
zRno)GF<{mZMRkRsx=NI~M%3G=t`iMtrmvfsXsk(G5m>z2v9s_lk72#PNI7>2#lbwq
zgtqLE1$b8vH|e~+==vN7J|=#w)bbhgd+;3>^Rb05xfR)u~VQ0{?7ztXWiyG;prj}1X&5Ubr}RDPJiu7|J9y(jMi?lwc2O_8{CMj4ce
z%)(EWW#>yEYbe;2Ey%(=vxp+gP4w?F>uGrvw1gH$Alau)vg@fO1qISaycUcRx3@1G
z*yBgs654y+M`mtzaBu_)2-xvE5IY&%8O1HDy03z+fU+hK0&Q7|p34{%7IWJ17vTlZGVn)`bWp>5fI+b;q!)hH(@wL;D!gM`!~0F=lW
z0nN;Enf6Gh9XE&$0AQ>%7m@CBx6NguBMt7Mp>#o6OUNSNr|j~@286^#OdD5xC+tu!
z5RJ<|0em&)Tu0`jG8Ya80Ja`&3OAsQMd~d!X53~&o+_i@8~cJu-Pd;8r`qlFGWZ-6
z_O9T2N^m7E1NjX7Xq?StW9Jx33
zgK_d#V|=x^QrtAAHjJrzuRb*J4wymz!Z748!SJ`h0EVSPyq<^Q=b?2EZ_CP(as{ob
zj@m|!`q4>*menP7L3(&*$zE7bd*r!t=#!EBuKLAk9qcWu*MTZA*F$Dzb}$hq6S5W~
z3i=ukUi;70|Cs`V?5X(O;B>O%iic0#l(T5La_VO2LC3%xAyx|aWmD&}C<$arR>!_U
zEzzZp)p8k)a-4RV?NC$W6QBiP0#T$h*P}vC3qU+SD4$OP864fK^)rTE0x$Bg?E+5ntXz?WBI=yBVdJ`Kwp`c%KEICjSS0@aa&fDx2%p&
zLMHif%lag=St`j{7V#a+ioqI3@eSgKz)BL9Y@|3fk`Xcmx6vatn=u}ji<#*iY-I9@
zwv>a*7vz|Q8Owz1c{!b`yE%z+=~Zkn&U$3@l0P;PH#9#k-R*jRYP^wr^_zSe1hR#N
joDM@xQIwz1+5711Z|M9V*=Z$zM_()6Ej~fO#_#_Dg$(+0

literal 0
HcmV?d00001

diff --git a/venv/Lib/site-packages/setuptools/command/__pycache__/build.cpython-312.pyc b/venv/Lib/site-packages/setuptools/command/__pycache__/build.cpython-312.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..bacd8fa4d3b00cc94ac027942f10652c52a08f4e
GIT binary patch
literal 6328
zcmbtY?Qayv8Q;AZ`}_t32qACHgwWt~@i_@Cm0UtYag$PkK`|)G$<^L^caB@w+uh9U
z+MF9Cs#29mt%CZZq~D18B~ki!^h;Fvs=leJP`@}aQPX_t@0r=V+p{5Qm6bf+
zo1L9`=6Qb4@A>WgZen88z$N}V-Tdu?hVgg$(Y?yq&AN-5Hw@QsP2Xsnb+cWl7uv;o
z(WJhDUuu`@L%1)xC4abGsaH&+Ww_;EoAr@`@q*zFT{hfdKJ;!;&sQ(2-U@n0c(KoH#8V_hg(_PKhuMozSnEX?ZE}eD`*NMw#g&3p`GuI1GI`$0NZV
zJNClhC0hhuaQ1KsgVKt6!HPQRh`!?&;&fEsI=;7*PUw5-2rr7IL+f|1%wA*t5PE(L
z4{)Pyx<6#B2_0koiX}X2Wm3sM#alxz)Enq`p2;=4*vp?FP9$I1j
z7Mfq0Zy0g!;W^shhHK7v-E;~q!zp}k*f1`duVOU>%=lU1CnF`}VgWscn6|90RYX

Abw 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 literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/setuptools/command/__pycache__/build_clib.cpython-312.pyc b/venv/Lib/site-packages/setuptools/command/__pycache__/build_clib.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..ec38412b57903b58b83f362d25da4a1991a652c7 GIT binary patch literal 3866 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 literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/setuptools/command/__pycache__/dist_info.cpython-312.pyc b/venv/Lib/site-packages/setuptools/command/__pycache__/dist_info.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..67985c5d7be53e2105ad1c941e63854e00bea973 GIT binary patch literal 5436 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 literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/setuptools/command/__pycache__/editable_wheel.cpython-312.pyc b/venv/Lib/site-packages/setuptools/command/__pycache__/editable_wheel.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..7ab1467cd1c824ea293cee4a42cde5b587d4e472 GIT binary patch literal 47723 zcmb@v34C0~c_;Y#J^?h)xDWE-Ab|#f21xJ{FA2oKLnKHO&;?0uHqj3x$j0H<4U&KW z{b)sr!I%!AksLx}c?BiTPf(=9@Wi`;XR|YuoOqNyGH5oGhC9$M{fl;z?96W#BIU@^ z%(#4Q$5&r{SAF%rNl~wx!=t0mVOKFrm%I`)^9V=JLa%`*wOD`aZA`a z?CN*1xHaq^&gst?&h5`-zir{XVNbt@#qHtz;e!4G7I%cb!-f5Y!$ti?!^Qo@!zKMC z!=?SD!)5(t!{znl72Z57_KU;S{nf)Y{WZh2{k6k& z{dMe_J6toorGE>H=Y+Qo*Z0@6cy731*w^o4@w{;3@V5SKEba+64LA2U5BvN5!!7+S zNXriugj8vA3~bp;*08_=y&O{cMVFOcB61a z2vxi+geoP+yCx;|aHy)kBebc%lRe#xr=lckPrF!ZHBxJ&3N5vprPd;~POHHYmbL|H zTcsmf&Z8`~9;ppl&SN2;bUf53Jw9j-ZF||=e5jhxXu&-q2q5dB&PseydCFTvts1n34&cov zHo%Q0$L1*YNo;@AvkPe|A4xy%<`27s)`7ZQ)w^VoW z$9%L?Lv4(n4ISx!E_4*79g|Y~pExGT;_0r_;_lt8e({Vn77h+b5odiwijI%@$0o#( zbXf|Ij@A2}PQPCq8I4N&_BlmSJT)STql4^I!_vTo;K)#9I3k{thor&q1j-+Y4vmgT zsL_a8Jk@Mycq}Z@Ls6}`+SG_R6rpmFX+Z6kh%^?I(L*6IG$cy{(NTGVvJZtM@$A`i z<3r)l*|TDBBqWYUs2BZX(F@|>P#8TmDvph!2Y6=$qg1d~gkL;1!sO zGbHVi@i7cZB!alPYh-+w(gwm3nlL)xGbSzQ$Dxs=4P!442gfMaX$&0h1%1-+*r<$; zcB(z$b0u^7o;lqW=s4Qd@%XWxBS~}T&_Fb4KZYSEkWgaiwWRsPP$ZhPoeYkRVf2&s zQ)4tb!En-kS{{v#4vdDAmcDTeP14pkF(y43#47{ANc8Ur&H5}!TgT||Flw314UCMU zYT=>DAeE4`1PHQ{R&>y)97#Gyg2Pe-z$sA^ZM{)hslRz_qU{kR-{R-~fB{R8StF*5vC?m88ET z%#VUCY=984vq)fU0&zzOUkeQ36ZAkje_oB-lu3u=JS7^449G)c(a1Y^?J1h0-GHv7 zF>o4rEayhYM?wQMw(4b3BKHpd(T_!}J48XO5F;tw=EOvHvw?A>B&$pj_@hlC_02L!k z-xvq@p%jryR62=&n4vA2e0VBjHd4tgvNSw;Sqk~~V`lKO21bVgF(@k-jRK^j5&F6` zJRT-Qcy??edSP@#%tStCMXJ;3GhH3+eO;YCKPxI}<@HM1ly)Yqyzr!%Iw5HV?i-g! z1{6wx^)3yN34izh7QroH$`J8H*#inLrsh;qdy(g&I;>hwK~M*4+BjuQDZ!eOMTuTE z$YoK7ntoBG8r1~cLxw5C_lzOq_f79A6w6D7DPzcVsR5K{X*5U8dNEgx>GzO1WC>Z{ zv#G0#LC|Y6#b}3K!DsK8EIBrE8M787jsjE+Q1*$F9>3TpPwW%xd1u!9lh%$?Cr`Kc zO_pNLW2p2ul;Ms9ohmF6JgQe87N>eX0QLGiG!leEqjC^&1NogSn1;?V|1QtqhfIc4_7#Njl z)dRo-*l4YghOm~(Y!Of!PPiM18w#ygpsSuwt&Ly(ax+Sl&yQnOYEc$TTHpfIbpM6u zaG20cM3Mr)@e!Y0_G9#twi8`F$GUp@k`}B8lALq}0>jeqIW#p8kO?m&T^vVu5n`1^ zN-;~rLs6hd(?B>P@1PV5`$&W!8qNRm8y)+edjfTdu<*Ig(SdQQN#wbXQEL8kr!kAs zqs^zsCnv*F^A+hFhD3VqvNUq}xf4U@o}*vQ3Kobwr+0M=hLx~?iwZmeMz4T6NZPQ5 zVt}HPMQP~DuOy#F%_5f(ToZng=UwxP32#Hp+pz3zxa%pr<$T5Ys(Z$=>hipilPKLD z!++QIwSv+_L0zn%Zn^%@QgrFUa`%(VTb_y+JiY9G`mW0}d-RU0a?Rb4a5u-?&8wcq zb)zM}aNRAG@Bd|fZcgrJqTs2Tdm@%syKJw$k1u24btUbA0If`cK+=hh9u19$Deevg zzA+vQD^F|z8X+WGd;88D>*$mB;$?Xkf~0F89E?OjGhG-B$sS5EVYr&?4}%@Tt*av9IR}xy9GIS943Q9a+sOy4HQy;#oJAS?cEY&Ud_ia9u!j zA#jgw_f3K||5JL#3#KwBpK$g7-vUBqiE0~Ckoj-ne zcr**f_B$t|P7Y)dF+2qN@~krU%|scVZ4%FNdQobTt`gm-!3KX$C|!Lc;-_`pujFe{ z-a8Ajbwa!lyey%XqBJ-N63*`&K!dbPMQQhU0;gnKF*$+*lV_2v7qg((tA>aXOruRj zor9+HrhZ$<4hG2{av*esoCv}2B6Nk^2;HF^DM!j>EQm3b3z9xBlqcneJYdH1@mC;u zKUDsRqZbavq~vdNU31;+l2C|emSAdsGo@R$_cBlCYhT)Ehqo*Fj2-StbxK_fTmKZ zc|r>ull5Hyc2?#$A?h)C=rZF;KviROhSSYF#=%2 zw`B>q77O{=6ol>PG#GInpxWC{AB*rBOdjq$CH9=^6Fb^ZoDe&YVU_PY@l2C=tf#N* zOi%j>@l?-=XZEQ~fx@cjD-1(LKGP&4>zYAvA50oTlN3=H2I2)U0RW(=Jk=ozik((F zPH%$m0L?j>OKHtS6)I&Mn`|J`mR5`)dPQnx)ffF*KArBLv=FX8*pn0nlm(o@H<1&B zniXm=Buohb4e;47EjRn6kss_Ay~(Z#;T=Pd&z!U~x)tq8=CByml{k!~YfK)!h{n(nyak!rEF$T2^W!&c};`#$wqczRzu7cgXjO(H|BLP2Antfwx$e;>; zRQ!ax*wlOzg3s8KG}CDMoJlv`Rj35TAP)rO(a~tqI2r+EI}Qp)mV5Ck##>>ZD1|;M ze~psO1g=T*Mew~oqx=M=KaC(lU?nP0;9;{M6$UnEz(8h(Z&MKjwt#>SLs4;xmt;Y3xTFvtGbTu|*&~r=( zwL(+_!8$?yI)GCHzl(+HJNiAXUB!C38oOAk#`HUYzMQo(TmA|?pBhuYF>Q=is%aND zt14RV-Zv$OlF*C0*wjO5?a3f&&Gk`8*u7EyI@x z(d7rEkq8J_AUcJEPZr8j3f5BAv1HMCDH>2AITTQn^RCFCa5?cxoT_{VRZSKIxV(v{ z<27*jj;^Z%62p>72UwQ@=^Un9(t*VhggvMn`5AiCOte_iP9I~j+(4&vZv6at(As@N zV-cAMK%a?E#R0_S5CWB2=V;j^kB>|iWXwUNy+S$v4T5XJx-r*Lu z-n>w_?B2ZM_9HRju8Fy8R@`;#Rv{-p;ocH+Zv$2Y`uDbKr9VqF`2r;iO zweM>`D7=5Ly}qN)lzol3`j9o^+PSDU#q|{dQ!Cq~)uxV$cQG}yV&*A>z9wMx(3ou0 zSGiOm$HXvP%rZ^Wo~tjV+*4Qk2_c%VK0!cV)n9t zx)fUCVu_klzpM4;Wnm36_sCoDc+#oUHc9)*uDI9ceJS2_CSKY*b8NM?{_TTr9h^C~ z;ucp4(znLkTUXo-h|>(aacZ@?F;TrYR=szo^CS1BPrSvm(L{+aR^p3$8x!8Pn73{5 z#EQ3l+1H1% zsHV*C(khK>+A?LisDU_7qfiAlg8)D?^~gO)`_ka|IXDI_AiT-s#i~p=!YLXebmZp| zBuzlCNgG&^(ePy{=?(O#ZyZxpBr?&qOk2XRup)i7GPIlYq{=86csijZ2!Ng+9JGwd@G?13Y;Uw8@)w1e+ZmHRPm>HVV_O5r_V^I@;5Q zsD_-Q8Z-ipW`#nvbQK0cRcPoKa!^4{ha}V3GwE#fLuSu7HJ^>+Ci+6s3RMxf+N24J z6OL@m=fk7tut-D2qpZS7C(pzIVE`hnA&5GlkD(c<4R`^O4xxam8KBG?Sf!0w{uo6> z2+08p?LCm#d;HGc1Tq`Pl!#j8J&C%CK z7sByP`{P9ikeKJ4Jw9)a=WSWGZ{b5aLT$?(A0e7w3UL&ebZ}NWJajH;iw=)b4F@!| zfEP3*GmnNnXxOJM`oK+TE6lVtB~>GVr%ifl3IZ}ha1HVG_aVGi1<>kXPTRiX4bz4< z>|gPQdBYoytYBuz3T9kPuB?kHNKHHSZvvSSzl>|j8PE`G$ckS&FzYdmC7L_7WUfOK?w_t{SFX0gc~fzqA_QiNj5;!|itNclfF8V-+M0ec}{2~I%E=cHANu4eHt zllf_{0=J2b+adXtqAL#oUz|NF4l$+u1Y~qf1$ZUM)Z~7Ds%E1BvN3!S@<4nt1WgHI z($z5tnYg+T0_UOg&;;WbkSJ+JAW`Rvztn8uq06ZX6{QslbEnJC@NHjeQy{5>avf+J zlqA#qC{jgymCNsjMw*$Og4m!f!z6(d4?>qUE~S>$2}yR#1u2L&v~XRnss}|!ljs}^ zf?@z5iQ1xGBSz5Hp+RU#NoA$gO@#Rc4FnXYc%AweRaDx+l}j{A>Bu^znOZrM7C(vD zA^M@v#_Jb>-k)om<*k4yVvq|scN>U|pHoLxuEjICo&uV2avg&19^WQLPqB566Hts^ zcWY~yO0IK-_c^G^k`;Ue^>uD&BmgbX(4YiU0cC~3Ye|<%{?LyczVe{=qqO>{KLNJ* zDSro*(RJrzJT)ajc}pvi-l0~goVm6iafRmpdG)ln?0apFVOp#q? zhCe}3k$(e}0@=YP!BeOT?bbZr*`BzkcE+++STg5$wP(h$mRIoF&bi!$oeNtREep9z z*0}f4c-~_(=Cy*N*FtlL7D5Y8F78@5wDd^4xGP@JJ!5;|gU_r&VdsM+XSvsb`T zYkcg->gHOySAJ5yY0bTPzA#a}D^|U0dH0cc^-+AS!a0)%IZO4HnY8g+`A}l(zS!1%OZ(zmyW`#?GxpUAk=j_g>T6E;9*g-Ni~9~!!q&TXcf!6o zX5YMGufA)~N!T~V?3-5X;;KC_VHaa|@gsZ9Mu{kqc#L;UBJx18jzsE|E8~JFS|M-9 zX5nEH(N5ucENS3Dvz9ZZ6NP4dNuDz6yrgUonWoITaQ;R^T0XN%07MKocCnlq>3ZX! zS(vc+tdo0tC0c*T%qwl`Vfh*i2hT%g!=AGFAObr!r=iDWPTEU-Nm%x<^`ggT<#>`r z21yTL47{vj4ItnP!3gY9WcfWjm;V5P&!S7x*}R3l)^Lc6`%H*MsP1gave_yx;O9nW zVFL>Jdt{4TK#4#G+OSVqQh=uxV5d>p zS_Ozk&LVCVmi^3ww0{sWpRc3exhLr|g{zD3WNN+#_1`;A9{}KB!K8E#8l7EaJ zfK*c9BrVLODrr+n;A_Vsp2$B!ut8W+3}A-YD3pPGsZ%O5xkZ~1+;k&*HfYj?$Ssr$ z9=4(imKim5GqzQN%Ep+dabZu~)3y;RA61~TDOS+5@N~Rjw+fXfSMnNHJw>S@G;*aJyXD`cdBYRZq#S+*fkvB8knrVw-oxH}AQ#`QXxyk35Ig z0lAODbiz|Kdw%A{WqTEaV#R=uBu8mXFR^S4JO1y8FzlEGkeSp9*h!UJF|Ef#z?nVQ zKnVpamkM~#fx(-$Oj`l&S%6lbHmsa%IW)%mfjKmMiyW3Is|s*BO+h4v&n*8wZpq>R zln2V#r&5hSp@(kvP$8ZGi%duOukj?4fFW6iyo4Y^RaPhxRT`#$Kt;M6rC}TMcnGve z@C~9QS8!m-tBTpH=7ttFEtTCqvSL56N=ssG%wD@<-?Ca(l_=X8E87__+kHKE#xiq- zJuQpb%jSG5_PPiC+KT0LHLoxgTGGT}W_+K4$bUfOK_H?J8dK{%1UP`}PGa~UfHPBM zDkg}rjLe*`E(I@X0~^((4VU(y;|l;xS#X_RoA5P7m;u@rE+aSDs6$!88uqw6)+ z<5(mzE{U~myIQwn6%7%G1ICRk&%xUa!>~L&4*gZ6Wner4Ls7-dHzE!Xoxc!O%{o#T z&+UGVyBwRq>P##Z2A}M5wNLKI$P)|`_X;+5Y=T%>ZZn$6rs}-y(a|d~L6^mIqhyV7 z0X8Wx#Ey)^c9ijkWMTxya4;Cg1JDH-h)$~oGj!o5T)GuHS+Wvmz$~l9E2HwIpiEZ1 z#5I$x_9&P`B15Es(a1yuIkn=hTma|5o@plSj8{avf>BtI!+2iH%aL{+V2%h7Z3Rf0 zqp-D>e-G)f|BD0%VKhTCH0h%0qf9PLY&K16kxORG2E+RV$uuuH)j<^hMij44!W2xf zndado=#0nQQ^}qBJ*{2<8g6 zbEmx>5W`?9OfYjj5;)Mb4W^i4ZTJkI&SW{5HlamWWy>-wR%z;k$_`>9nQ&H>Jkg#L zW;M8>xM=VaRSQ~h?9zZuE=s`u49r9E!swL^&BoS=@eyT{1SZcI+diQ-mfdNiLzE!_ zd2~3X9pdHPJN>Qrx3QB>GXzEUju)`wfQg?Jt6Q=^Bf~Q)4>jTqPW?Rf?1m;u7`<^B zduiCD=M)%q%8-n1ImfyseTHid=L58{L^8}$b}xuQY!b*~LX95WP>~DK=vZW5ON(w; z2Ls!&pnqtzMH*=#`}jz-MK?9p#AD3HI7>%$W6)`EqeDRJ=NLGpSmT}L`-&pKVj}~D z$VkFRw#KYSxoBuZ6{)zv;CMJXp@WJhDP*F`A+ow=w%xQP2pg1uku&VLb-bv80+8Yd zKmjtLC0V3^EGOhJf~1r8B^h0)YGOhpX=>v4kuX`Ff%sDpD$s-w8AB#ArYLjdu6j!n z-rAVAcK&GG+q~><=B5uNj@%T(;=}7kLs9-(et9B)ODum&JimU~)37d>e0w1Rc%r5T>C( z65=xy_*)3Ehm`h@3d4qJ5aL+iv(TNoRT9$?YlH*JQLbro$efjY$f|u)(cio1BD$t6 zA);o@3=vvcXhCTGz7+}`jX^@)a(VvLa7sg&)AlL*!?eM&2hTMnf?kGN8=&D8S>GVzh#OLaf zsY_%Z1zQk&X=t8ww)#~(Giml;xj>sctda3SSQEg0hVk6ZkZMo5lsFSR8?N{Yl6jo* zW+5{^You}+G04a|vaw9sNM;ZXp4ZJf<*Sr#9Uln~ja-t6E#Ovb*2o2Hm%|1=BBP_S zB+0)<&&Xsf>4D6e$pBzaie=;cMAF4A-x#UwE94aA0Oe2PRGUwkrFls5>YR zT!0dX3%s1H@@&_h&M|7)Az!zvs*j|Jv;!vSrjrF~8no1k)t;>JmoRfxtLWf`LvkM> z-G#a~q~1BPOHAZO5!(hEzOP~KM*axFHQ{4>^WDPIM4>-c=wEmtUbr8&6NOc`dR~D9 zd^I=!=7sAQW?x8@G{#CA<0Vb;+~yh6YN3c{<+XDa^X>CP^Up3C7Hb!a7DLM=`)Ay1 zMP+k!Z#KW)yigf0+CAf3brmICl`&W4ifhxVyEx&lin*&++?zis+qQUMrR=ep+_l23 z3*i;|FvW&f3J=UU?&egi3wB5A+|`9L7FirvdW1zjG_6%{nZNL3^I~6O*OAz+Bk|Uw ziPn>`)|2t-o@F;=(*-wAT|YI~nW*%~D*el?hgaMk%owmP=B}H6dSPP4{Rj-T6IHun zRl634ZkK-8wtTubUe$NKXQpd*&uU3UqNFKS(zNi*l4+?oUeXQ`PJY>^f-$G{^+)FW z7rpU{UF-B~sqE*T(z&wvu7y1-p0-s_S;Dg==0V+7JT0GFkl}O6^?44O@j11H1?z=^ zr+nF7_PGPkSTSF+Y)X_evt}YSBdWakWn>s*{6Ukcd!Olp7E`z1^1+V7_U;GtOREYF6gKP$F$Z_fEyg$;RrR%7Yj z=K5K^4LN`2w{-7u{H)DJ`S)3lm~EIxG*hs_IBk8V6&NeCaG$d%QDr?~=H3dv#8d5J zJNX2dLkKF~Bw4$>n{ceUw#k0oh%u|oh#OUy^xc3A`)3gda8~8}DJ=g47!}xASzuHS zNp@h;b&T^LM5#`Cz&3+F*E!^*U13iGw8Iyehl#$QL2Ht>KzQ{0dF)FwGh+4wq^X9i ze?`yTRJ___5$Yk#Q$9WaC;XVKP?4l+vq!wB^0i@_m`@_tHDRq}(_A#Mxiz-Ab+L1) zbji8AqvLj8yyWq|c6{V+Tek>R+hz`MW^FH87JN7C_WU7Fi)r#~fD(q?Bu%+1T z2^T#XAHYvYMQzaQDDCJ?u`oyNv*g!Gw(E2k%tW$NIu^A)(~h*ZX0byb7hsT+YI>1{ za#Ft?GG8z-k(zPJ5iw3V zR;~8xgUV3fg1W#u+1Uy60^+_QMuMSZ@E9IRRZU9q`LP zLlwBFsfS{d2z>b|=9GR)eT;$~6m%oNc1YTUJ;4m(9Dx&{G5Uy>U`QaB8@Z_)Lb3Nh z!fF)=2O*}2GV@HnKhgxARi5JI^#Y9y4V=bSl)OOS=-3HzAJGj-qa?$Wn2E9&xkD3_ z+Y2ZPdXnd}{VjZJYfhRMg`{1pMLwaPw2CBihk%Tj2!`#4g&hU<&Omt|Axo%@1jJt; zb)ynJgJhz;QxPu$Ca*5tGT%4fy->Z-v9N2oXge(Rip%GA^4%`+;-(qbT3%%$uP&BX zH{bX6GjBZ;&uf`6-*x4^b}-?skKw!W%sF1yw&q{iJG=pObm^`Ud0j% zLHQ4=6fJexF3=gS+?i-1Z*wefbKE7)pPBEx<7$XCA9~+^mkjy0#)`KliW_6ajSD*# zBJtuj*zS9)=0b^0EwN24i-mt;UV8S!`uN^6%X@m`-o9mbAJp%j%^Hrbi|6fGw(nsm z7TkCnI9BvX%&#qQt%B51}XS8 z3JB+LW0r4Ij8Fp8Z!rC;OvY$(E(I`4j_USqW?^A|rkAuZ`oG`4~A76ENX6s(J&!dxf$EtVVsXnk&6E8iu zY(KPaGZ$K~ow(;VTgoVS(s0k6XDPT>%|vHAu20O>%|+%r z=B~c(U(RcYIehnX@cjO!LR7Y?+>$%rabLjg-rjOc`TAb7W#@eFeF3+1cd2FX-Q4_j z3-0(BVm6BLk5SAiI4eJMA;x=|I(Pto+NCam8$4r#;OIayN#-(S4Vkga*DRTpeYMt* z1$$hrAuD#n+TbArJ9^18hWyvaD!)Sk<6bmoB5if2FPrdppBA-Spo-+vh%i73bO4u= z#~ysJJj(}_XRqRm_lfZQCI{z#Xp`UJT7;y7I}qka6iy+=DXwjD+Sm=Si;6y= z8~CH|qEhmgs1)mg9*j^1LSR$%C}m*t^>d1SL4iPTX3R}Cx)l5t^)8{9mm#$?T3o9| z;<}0MzUGC2cglGDVEgUb+mYK1KlwWBb1bfVcEM8h*XBbU3aOL<{Arg4g@kxgcbiM_ z&mzG;3+*x|NQ7%DGhh#XT0+*4ji8djryX8fQsL8qJZbyOC)~aq`9GonzO`Tkv80>! zWzyinHBuyPz-S;%_`B~%0Cgm`Dv=a`Uy#ZO0d#@#%Y8Trsl(x)aNWB4Vu?9@@#Mi0Ui8O-=0B!BP0alscDjR0ZAKA-z*M8OX(-@~8peQ1) zvs#rHUnVi9)-2XG7IX^lDnC?m4+J@t$u1_Igw?tB%#z(^1jKMw;+w>p-#1NZBp}3(GauzejG06WXGJa_$w^^`rBG8iwilB@n zrASB$;6c*PL`R@d8JH<%DIEQ3xHD-}z?;NTz+&(@l4+SD)2z>cINpl?j;eeFPXTcS z#kYK~_)y8kP-6Fy*zO}M6-VO*M`3p8-85f6U$#&)_hQ`Zhh?2T=VtEp+_|dx=-U%- zP0U|fEL<+xy=4E$-o6nJlMw@(XY+^Z?d_K4XQ_p0;NedBO>QVqb3ezNV-3s*GP#lwH1X`={2(kBRzR$Q( z5xnJ0MErz(NZAv;z`a6Isgn-v13r7w9EM;{{teVbeilK}I*2_~*rG?UAm61Fo1%h* zWKr>36L_lqOb>jHy~B5!W7kdOd@ATN{61Nj2DHi=_PaFR1o)(lF?t=JIC5?byjDX4 zuhPNs+?Kebb=lswE*N$?)-9$y_l)T?yWsHL%)6d9XIj}rR&gu#7HFeqTztUIC^7Y7 z15SzQi@*F|k(7=R*)o&?ntWxWkx57O2=T-1|k8S$ZKThp2MobOajlV2GSrMJ9$>nxex! zOv+X4^MH7bCidCPsxt*GHEL8If~gLinkYUY31v5->hKc91O!~&&I}Ut@9JAAf|o}j zf(UVSYziUqpV8-xGf%Uw(-!n39)H(xOVDwsZV{YHv7|iLHdbZRXugZ`=)$`+W!Z)0 z+@Z$xyKb13&DjUu*YLKEU$0xHgzr={2^X1ZWg#_mDGnn8j6HH4J_#b0h((!o1})Is zy#IF)OFBs9!uykJ z3DP4}7FP4ma)nJb^Ud+9R`RV;zE%eFx$kXGl(ojnTH|FqK&$26Eh)Bg~oW% zu0+wJv7$$j?p(9yT(`ySrE`UIQ8H8C8{4{fdCMao+4ub;$+;7#3XMMc5;e=bK(lJ5 zw$R~fy?q_#z~8}$a_dV~YDWFd2_X}_9V!YOHWTDZ6wW9H)&82%n;N|#S5dbRNYZMi zbRi=!(z=k-7eAxCG^Xf6W;j?LvzO29!fpV2qt-7)^OpH5AK6=2cO=bBK}WP-nl^yo zi!Cd(GN#S|y`{8b&_$Zn6oOT=z0W2gR%bvqm`6Wrv;;4iLiH=@J7v|>i`uq6{SDoR z$VHN#WL%h8%2*9RcR~Nu-v&@5nYz{qRYPh7$=u>|%G4)G=aa-ra(8uX>d>r#DVqe# z71AKW4e57~!d6S#&&>?-Il%$(;h3sk5Yd71*O4(zs{q#@s^3w#FJit1CM!~AOXkD> zna2EilyXh@B&X=Li}9RlXtx|Y*mnI__a(erV%{xrZ@sdgzw?iKe$*59cFc6H(RTk= zA4z!YV&1wRoO%1nx1L;pC~P~FU5*{lVmWrOav%fo6n@F%i?i(&?Ym6x@3OQXvL*Ah z=JHB%H$DVSX^fwSO#cT|kxeGtGp{Mbqzk-;dCHs(Z9NQ?Mk*ih4iUl`*9KQPSr9L6 z^q(@lr|sBfh|TGVHL53f3 zL2gEXZE!Gc(x$T_fN`rBfqHMUIAyREOhM@}ydHTR#bavI#`@}5UiJK`SY8`k6y+qE2RpX5^voLL)rrg$Sro`!3V9Eev=VpMT?Ez)$Haf8V!8VH$=!2}6 z4Tx3Z4b5^0y{TMyuGtHLHEzLn3hTq$`aB3uX5nIoCh0+CIsmAe#tG$C5X(IPggsz_ zoxb@B7H9yjQQk(-cMf%dE60gp(wE91xe1A|i)Ro_Kuf-Xf+GQiKx1otBx!*Uq2Y)O zm-u*hY;;U+rMH|+w~@5*c^^sU@`$?gBk7>o!6k2oOG(4!q+Qw30q0&Su27K&sw59Y z2=CMK#xG7LkoE3l$;P?BR?VN|VdP`Hm9owy*lx0g!X@*vU0ksrT&rp!2R?^muo8|TxA?Us!)zzF1YX!4FKk};My#-P+1oc`k9qr6 z?Vg+N>+adUxt-XtcDK0do?vq9oats(1q_XLCE;PsO;M`s6^YM1OM<}NIl7JZneAGr^F zYQpm`?&k}IPZ${K^uDvQquiP%tudqFY;it}2r>C!!x7_Gw#f-LoZewq7B(ENCJy#8 z$HyDgZx3X`Q#cR56D!(X6fTnz3A!-uR8-}g`O+Kd^=RTmL7#p%dR6)Fprrp!GN9*DVg80eb&`VeR`hw>gfCca1I6UQ# z@|_0Ec`!F##X^H^PcYzU60uQ0lCKOwkxJ&&eCvYhCKWc?PClbVMa>Eue=@7(4ucgn zqi&=6WpSnmhBeBM(Pe0YbjQqG^&MrW;QvDj|1Smqje=qtJzDSCNBJxkkd{vDb8!Q? z0f>-pmP&hUMTewu5w_!l~0i)-;OwcV%NJ&)UR z@Lr|KQuy~yyG5}Mp{fqxPrHat7^t{8xQwP|Q zC(Y^XgbONQ%NSk1c;K9D3*5C-yU7j{y5KnQ6lCYxz3kda1(MhQOI zg(Y8&rrcGMNjH&scs`XFV?Zr;wr2L+Yo#$y zr=my7^%&$H)Q~BYCT+|SxFmmR8IKM&@0~PZ56h$tqEgyPH)-52!b)0EWibBS!rjAp`BP=xW*&1RGU?+}J zIP~>o;Z2)!} zdLvYTm3)(-8svDNDcWceR|y(^=gTn@hh;(ke60T~>KU0t0QB!Ix%J2^5P6VH@$$BnT_-;5`}fcM`7`ldPsIzL2G>wrerxEJp+xbvSn;+y z#pLnVwdSdvKeq6~($To5YsT_PQ|p>Le~tD9zFqcKS)#fvR^4`|`jJI>X;<9S{&P>~ zO5K5_C*pM-E1u5#7E?~{eUqixIpbO{5K1d=O}sLZC~1q8wB0GeD}Qe>RXOi^yz3?* z$Fq`OGv5aDnCK5D-kC_Ww8vW7@3b7beI}lNH0D0KZbDWVM?p1G|tHDg^XC|QA1zQTnoORjin z=k4?Hf*$OISCo!h=St_jvoFqHfd=Z6f~tAjf_47tg1oqOv2>|?c~fV+pi9r3p`o;W zWPj|7d)25JV`P51rMY{PIc?t4(#V$9!-)JdF*2Dn>_a#h^3M*hNKraTi6AYVk&z1t z%Cj>sWg8KT!opM#8X;t#$-;b*rs!1X8_im>s%B@6C@6@2ga5!0SlF11{gP1Cf-#YR zKn5f+G!75K*gwheq2m9Z3X?ksZy05bn~*zcf@b3zvP@~tJ&4rFm8_GYMpQMX+4uyU zh>^)7=6Bhp5iHl z{eAZE-XW0c_nj7%RA*o4C!k~Ecto^I6UWyfHB}s+4KUtSUc>gL6!m{LRu^&sO_7w% zbo)I`Sk4^jX!NO8FDOS;3IQs8V>aQJS{ppi>RTAplhRU_L!&zNZ>S|eU=LbK_xPJt zU~f{kG?|0d020kqfIDncw&TMcT!@2tJ+ms2aEcFEfdF2c7~}(raSRNcl`saF&4EGM zwM*>$fO2pX4%z{b(<(g*g%};_MohNii0*}0Qq*noY|A}_l|`9|%YJ=YOshLMJ~F^|CNsM$ zss@aP`7u*$vuo;hRenSYJnhlsL{2VYhN6{M45#Km%S!TQIzmsxaeXwknac$3BY;%W zIN<0!I}tC4&*H?B)BGqa?6YP*?6^dsp%0P?9O5TIf=C~NNP~G@xEzFW7CGaq}l2M#uUN#oFH(sr0=pJS(R@ElA`j-7f+ zhF=>LMOY`9cj6RI@aPJ3^>m!-q~qu;Y~10&COjU&!31dZrKFj_42&zy=%n==HqH?( zbA^|a&c6~V#ptO9LTnN~0TpP0;B?^vIV zd32@V7%jhfH*>D%%${GdZ&Knn@>cD+H(l3Vv+Xx>SM!QUJ@D=6&s`|zb1DoT4=hz& zsq@|T`omV!2Ug4BJZudjcX_19NL!{grGOvROT|9AAPqbdVepJgow@1^2R>2eI>Sv= zF1w~u+%u;*(8AHN>H;@yVi-u{mDM-Nk~U;{&#LAFFQZ|ps9VjYE6H``pvo~Z^`*wq zW;lkSKG1Shn8t}!G=(&|CIS9s7|NNFPg6yR9_L9 zrD}>(CmjZ90z^$Z2&-)ON3TYCbO%Mb)2Hq7<1_KNA zz35`cQboL_Yi1n&vB-5_DcMz&EMQ|q>59E=4eI8Rm93Bb$)0#l$EVPY;IOjXo5!yo zpB;x=LU$e96Y>2SE6`L*Z;!q;x^!iJG`{u7hkNd9Jp&!e=TMGRes05O*|$Hp;^(^4 z=Gac<9JzjEwrg$|4rX9(u|BU(&qkm7FEA7Fl#%zA?dxVk{%Hf_m|#5Tt()(N7c?xK zTXwhp7jmt&{majb)c3!P5D))@c1x$r^uF(4hiLg=s~zzV_E7j!p}3>T_|swo#YIbp z&+*eOHi|bnI_;L9?#k^nS^m^yLHtkcCZzwV%hFkBW9&BHCDe<;(CPd8Lyo#M5Q7V8$iPWQJ9NR7|U4gWt61lEN9wATasM_J@~{BF#gO(ACS;md0rlWJ9Ll zv*3l!uoEq@n3J2^v*gN`D@~f&darJgVh&_TJb<%gVVic2Xcf$4;#Ih|Ugv9?#KuO@ z5{->cnjRU&oFjsd*||Y@LkG$#u7fv}LS&wHk|>V+QmsyLL?bKr(4j+O`j)MRXX}IY z&o_xL)YR+~YoP6{nFcM`g8kwx!xQ8mxQ09NrFK57h$gW<=s)+*T+%%l5PW zr|t*@ccgX5Kdj)4QA6JX%6M_S4>m$-dcn0~FFB9~*ML)$r3lgFm^JLsV@M06D4(|3 z=EXmCQn2?KOz@GTT0eMx>9uAu+%y80P&AW2zl6wl4Vnacw_=3>FEu)X&c<@<_HWls z=gBLrM%og3;}-}!1>OCQ;Sa38Mf>EjB-5oxxA#K2ZJ;d^urwzvJx;=tOL0#+2%&Lb z6r{aNS|Vd%9FJ<|l5f)`X(DN1yPM=+$79vvTd@^ng7E5eoRnoZ#TbzX`|$d&nEV@t z4tdsJxQk~xW{W;E365OS!@pskdvdvS*ZjcSLvIZ&ntpfWPU)`2@r3>0rzYg)Lh@fb z@TdV6rA)N^f1x-o=l(iX+eu9ID2{i4!9Dd4oHQzDRIsF7=$)hq+#JXpl3we}sr(c@ zvMPIVSsOJnN72as9lb!oNovP+L|_8Ben>cNc-runIZuCRni*IDRnWVzZSe(Izr}Jp zmUEuI){Wrju6($Lp4}hM+dO}Gp2J3w*lH67@Rkwa|&mrVKV_>4K-zYll zHm6xw61&96g5N}h6rx1hWnQ{?Ma~10WJnxr|l)_~OPhEg9`Ps8j3ebulQ5J6)sgnT_vpt6(A*iqET!=#nssMq+luaP=={Rf{ zuyecc(JLePPV@r*2t!3FfY-p66kv{)pt6?K<43)Y@6$4$qxFX}52R#nQL^{QMC8Ht z?+Q8zOfbM`6YE5;ixkh{d}3)$4=m<3tWIR+g$ zfTx5ZGcN7xFzuW&e+o8C&DR{q%w3>T_8}rO|&p2(oqLRawD{KoL&H~~t4s38z z0`(hL?m1zZubf7abb)mQEU^j6e<)9wdN1je0m4ysCOL33(?R|w#vEIjMnlQm5tyXn zydCw88}!6*HEHDuIH6D}*AR$czz9hB#X*%6mrQ&4GJ)#P@QW=VbiPmLt&^{uyi-^| zeifG-f|KlY6&7ya$RxN|8iuSd#HjZTDV^NeOTn`i$D5tX*Mhs7zPZd6s2MWb&&6Xl%ouov0D)(%aA+Ml3a={%UichtzG_5JIJyA*_AMpa6G{O4P_N z0IpJ1_A=Y`OpQ>n9wSv6AD{!zLbLofL0mBk;41sP19PT@-X+uTfBiL6EbqV<5kdto zIlP7&Ht)s}VNE!b+60KYcoVXX4Qo~bR%yJEc_~e;PHQU6#z>0|O&8fG$g>DG8YuiD z8v8G)u_TS-jrDfTeFM`b_-_Y)GI*!0bGH3f_jkKv-Y%up-g?7t=htKGWSZ#mw<(~B zjRbA_vxIa(^a;vG!E5w2Qsc9)t)|V|pQEEESMm-d%m7+V8*iV(Ts7o3oE-<7f ziA#zkS~Dk4l~nKzJ!Uw?gU~<%A`Jx3;pjd{1DS0Gqk)kqGt12Y$Fd*|blR5sm(v0c zq6L_JM5ZG2B#j6K-=`5dfj8LhsKWBOhPbyDrUKR0yY4&)z8vlwUtg(&zktR4x9#!D zWAU8hG5c{C2vl3I9RodDZT-wCxXb5!aaY~4x$cX5E+jEt;kPzDVrKhL(xy4B;?!yP z{{lhwZe~uyQLW4itGD4{>HxVcrE9b9e^c?EIw%lrX&e>QjI<18Tg5-S7uj{| z;424T#pyJ)%l6t&kg)sKp;r!JxFJQo_}0akw>6%(ec8U9Tf%-1Wv7iC^$uHa2=S;0 z8uZBo!*8I#E*oT`wz6^@I;8@hH`}Dq)U_ewBzhVOSJol93Ei$i2ZXRYv!b6XKrTMS)q=)Y{9l2f#K-GQ)UGpA2ACzUI0$EuvN?!+=~IA z9jDx|8e9eT;y?>atO$4xPUMj8ixi980rl8uB*I)>gY5v{5|*G7=3dg_GYk;QO}NjR0@tXz^!+9JR}`_wgw`}OXvNKHP1su3g6hAALEIGqoZdTSEl6VOdUk7 zXV0<>^e_t7QiH^~L1m+8Sf8R(-njd0)C!MCOhlR-C}NpbI4COiX`*HsCN?nbIA;>| z-M_3IYL89$s1Qlfa|iLok!DG8f`{H`E(Ey!I+x6*Ys%UF=P2BZqYUZ`>M&FbrZAxh z4Wm{S#d3ww-*OE=nQ4jt?!V#UN4#Pa4V^lWv@or>{OgEw{}K#sFgeIoyaqf%6P2TT z=x$_~!Wo9hV+sg!#Ws&`2Gm3w3IaJzuTk*d69Tz`ve=ruUdnVI4ofXuc)D_D2d*Dl zbr-IBytf>$IOb|!#aR+-`NgkA=61b0Ip6-ui}T-DsEOw{^TT!8S1UHXx%c(G@OD|V zuzmhmyuv?o0y_p*Kp{6RId1QWm$GA>I>};g|10}nJ2-!0$#JKy{cdqh+*8BO+4@gQ{v!^AX+jQ-#e5IdPC-SF15Koe)S$Hl$*WHK)Dt-M4c8A_P{2Gk z>d0ye9&S|8+QZ%g66_*<>3L{R6qIq2STTj@Wn#ifm+CDB)}0`p*m$y$WrJ$wtGPC^ zd<1FfB*efMfKxPn6#Q>Aexx4a-5DU?GNyAgv`{ftfRuvY zHL5WBJnTH!20Vz>u%+8Trwgwq}a~*sKQ)Oqyxqf6^|)zDbTqAvvFb zSdq|Kr126wD2C@EvrOlW^BY{VNeks$iBwGcB{AJ{a{xlnN*Sc5;nE z9DyQXhHlKzixo;1Ac|#2iY!)}9gxIA_XP1O32R$|EiBHIay%icrtcr{Hk)N>Md->P zCJlag83S6CRFUt)UQeknzU6z=xzPuY=z=P$t2(cbGH|MmT|m*?d9?Qz$Zglk*OwQZpz?rKfA zcE?=17b9`k{%f6-@6h!_b6exCYKZLI*7@NdkN@$DKYDS^?w)xT`+4Vf-m#1Dd$-pL zrv&xTuNW;>Hp6KHgd9j)>1bZFm5z|oSI%XaPh{V>o=yI&ix^1KP1=&lZtkiFdKacGfR;U4`!{B57$B;Fu|Xm=l){g#2^{ZVtBz4M4(+^Im{o|5qAL zt2|8peYpP!Y^BE8=;WfuApaPBpW;_h{tw8_f-h)5s__e)rrG(elixf!TQ?v2$h>XM zZ2i{b-+X-5ykag|b>&@qoOR&&)DV%9xb4L08&W&qWtWFFeb$-&Uy5%eyS!U^g=+xvaobF_H+YspTUbv zJS1O3EafCjBHm@vVkK=MnVmC*s*=|!{tD%m|3^G#FRRO~O0JPn_+(|E2W7xF*no75 zvl7@Hpi+Vc09T|1g}}t0JO$DTucab<&*ZF%%wJ8_YE>iiTeDhHhGh3@N#%_BJ8s_D zTKiD|wTVIM%ZLC{5%MeBs*{bB&8`i}t_4AAHpvJ+s1nP7XTxNn(qO)!8R!CLw0`t9 z+poly`2b#FUCuf&=@^L+rXb_BK0HZ*NNfeCLmv2Zg8IqYto@pv$wA%QgpvTgdHFZ5 zT)%R2>iQI&0I)q)xqWeGqOB{|*7c$J-{<~$ZmjLecpe>m`;fjpgrZWhKnkOfRad31 ziRzv8%66zG!Qhj1k)kJQQMqSP_cIk9#p&B2k!;1WYnt%pn9SuxOw^azJr34AB=(!E zdSJ)U;~eS|5_D6Scwg(7GryVhdQQA>Ydo)h+0G9gqx04dBa7TYK_>-lf5X2eK>vUO zl0C70;-nePT{eLU1S8}s=VjrZ(F)^WkJ0k9;lAByY2eu@XRruF2-n@x3hrd~@ccK< zzwi$Sad^fEo!6!0ImymRC)1E7@if+=ZnnebD_?^g1LxGz86!ZEu%PFittDl?Pv?TD zU!-%)15_<25W*6?`jb~NoM_H#3bk2ubTkY*eZD~tXEwo;8GA-cCEB1qq1rl~C)-b- zhGn;SP<%o0UQsPiJA2wscJ-ca@965K2fC1eo}F&*I~q8B0uPU#I?>s6M$Lg|;x@4c z=z~5KxG*|;DG;b}I-SfxmKabsEb$)NCyKS=No-6?=`^Cp9%H%rp?-{(3Q2=ZauJ|* zH{e+CCY(MM4pW`5V*p=bM)J_kvl+~O_;kMmG+TqLLa64m&+FTUYDIW5S45y1gcBL3 zfW6po1!9sBDDt_)1RHW9(Ef8{{_bA=KzbMisP&=8N`LA#phl?_2I`Ku_dlfzD@QJ4v0YXjR=#bjC$Ec?*0ULMkSH>41}BKj?avue(x4qDr4 z=^KPp9x#)*#8DAr@vcCxF|lBi7--^cLGP(O;nx+t4K=D|9KDCp;IA=~T!}ttSd`w@ zA&~8K<$$JsNrACv)p6rR!X&Fv2d(GR2I3UjDT_?F&P8k>Dv2D@(I?;Q5rW4IoX3eq zL%<3i0un~8cTzfz`vRrDQ(8kX&$$X}u+j$FD8iv3>jX*xri>W`VQXlJjwoi6BxUB1 z?_l(J`g5@b_IHX~VF-$9w0dgUB6z1k`^5_N6X+LPVLElxHD;Y?y0RT-+qKhC@ zD~0khJl60m)e4=jJ&L5%nBK$qYtQ^_&PEzCvQRdq*B*dM8}z}~3d0a*A~KD1O2KFZ zOI`2_J)x`v^s($CLc_?AB*$s=Go_*ojO?`NIjs)yg9AUMu{Mk`jKWhm;hA<4)Or9YL&R$2(Q`#qhmta z+n`J{+S8SwiKlaE!G(vHLwWnuwh-_%TR#G?X=9Kz!ctBe-rvn?A zls4#~tA0!UO6HqBqQstn@Kd~qGjQ2L4KK7fDT*!F)E_trcJz{ydr(*+YPuZ30H|s0 z(uD4rwhAds$U{}-WC)~-C*D<ifVRAZ474i_6@e`q zJ>-@^2HAlorLc@&+7E<-!{$12F(ew`Foe&869%K=-j85bJ@yu9KkM`?}zD0J?!{f>7)0}E1!X>+|Hs~@d0_b*R zVTbfyU|0~-ke@@Y;9dgYvoFy88Ms25hUz(`MBsBt(WdtSu;KCmhR2N5q?4K1o*`|A zH^)>Lr0^K1^K;|MIn)YuublqgEcS6tvGQ$>0QjeYUjQzkQ?-+%75~>XrL;Ek8qsPe zik-Bjkv(P20}+f3%(R6ZjMSqFCTuu{MFzgOkrf;qJ`RSM8bGfpbpufWNQF;^D7j@} zl=OyADk}J95yh#;ZF9Q$9K30xSkUUkv_L=K1c4+RIFQ_uWyKD0l#QC5>gC*^QnQdm zOA@wB!(u^Q2hoSzt^fx9V}MhcDh+0v(WZzi9|Mmq)8=oa`2BvZZme(7E`IED>K1VQ zcxbT!;!;pidbeGHa3C0_eUq$nq>xsg($)qLN8DM3GEk{C_c0*gT|u7AQ@*5iiPB&q zxKqzSPwgmF2k$C7QaALz0)p*>kUgXTA?1U?Edk;=m}h`GBJb(?xTXOFGAVeM#t-%r z8&{W_T{CG+mI3tBquCv)gM4DL3V9i2?7({ph(t;=rfE5}aVD>K3)gYO> zu#C(xfp%hd6m%RJl?-A^`7z!tG<8~r!GB;DG^ZBQtQ10a8p=c>@K;MZ4?1A{94jKj zzd_4TBr+~>(|vh2@Ww{b7S1f{ed6TF_A}2Q*4fq9e(Xdqe2({abp<+4b@XPUG(tWy z1!V0QAy;KD!-?*WYe(0Og&sR@jY9s8Zyw{)F3RT%@}s?MbT%X=o_v;&A`^jUc?o;gv(fhdZ13hu2%qzBa$)KYMZf;tL*kAf`}kO*7e zO2KOs)Kf4=0Thga{1yd&PC+9D+YlrjU};XU6FB+ebeEp^DQKrYFQnjc`uW!s{6`A@ zhJyb@!BGm%P!>0Wr1>1M4%_X__5!gT6wEfA*|ah8l497{Zhe@ z_g8|0{m=O;!Sk`O`D3BzW1;f@5b8b_N{#etp?*S1qyNnL*>Up z*~fwpuYPVc8;qX{2!81l3|l`I{2vS3S-9#wHPp>^ekS0ye#T(*ny=-p+f9Y$Wx?~AyU%RcJNwjU0^L6w zvD*xf&R+dY!0q!wui?>81q7cpbsG!^W}Tl2bpQN}(Qi2LseoYpoFUgxHHVWuaGNi> zPq+1&d_&QE;ktm^Lfw73tv75nSXXn4XCv2#*G&{}^e;pfI~K0K6Y=!|r(uis z9mMbEm(F?T2Ifj*`8DenB#^hx;&mIv?1I%fQ#%uxY53;X?>i`^+-}%0SAw&A4Lj(d zINa8Yiww03=7rwH+J&dz$z2!lb7}AGj-TLzh_1I<4I;KL%(h)0S~pR=u3^EuFtAYi z7TSZ~cP%+H(rgFpcm`fCo9&6^*2XM#_bqrrRV|nc!R?o6e(&oe>jHi*7UIa{w#5rS z+H{|Ou9tfa)>(M}#BIKf-4;#kcDt6{K1A>0*?N=N;F&!GkE#aG++iGWXYhQMn`da4 zYfO~;W99zO1pHX9J8fVCzCLKQ8J;w(I&;=dEK<68-ZbAkZ^y<7C1ub4rHtbDOIQGy C4yb|v literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/setuptools/command/__pycache__/egg_info.cpython-312.pyc b/venv/Lib/site-packages/setuptools/command/__pycache__/egg_info.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..c7e5f70ca16957dc6c7501219e936feb9b598305 GIT binary patch literal 35408 zcmcJ&32+?OnI@Q77qTu?;UaFZ00IOH1idj#2@5jawhsBXuaj-DM;J$6{#GaHLifi5Ub&9Gd9cdF zao0JK>*7RSG==yfQ?A{xy9;)f8VfVJs>LFj3 zkKGr9YKQ8&>ezi@Xw6W4S3U0Y#G+8c(AuuGJZIv>;=xXJkTSnP_u#XcC-v@6xGuj~ z@&?z{Wa18U;;J_|vGhHRCtY3ZSzH<7%7g2JrSDnN@y#s00`ZkW^LyrWtck@|A+|bL zuEn;9HK@H+T#ayp=tH;aq7+kBhymQ>1KFZQUS&$$Fdm| O?Ly(7bs;BX}TchomOFAIkT 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 BIcER> literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/setuptools/command/__pycache__/install_egg_info.cpython-312.pyc b/venv/Lib/site-packages/setuptools/command/__pycache__/install_egg_info.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..305f25959e14c579f1a1890d052e73aba0a3d92e GIT binary patch literal 3767 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 scsY*y3cdClEC2ui literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/setuptools/command/__pycache__/register.cpython-312.pyc b/venv/Lib/site-packages/setuptools/command/__pycache__/register.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..3d13b021b952a08a678faa389677313f665b506f GIT binary patch literal 1013 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 Hj<53{nc-MJ literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/setuptools/command/__pycache__/saveopts.cpython-312.pyc b/venv/Lib/site-packages/setuptools/command/__pycache__/saveopts.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..d6cccbbfcb837ca6dd2f4f68af88173440eebf56 GIT binary patch literal 1275 zcmZ8gy>Ht_6hHDPS+eCajnpK~P!J#rMFFG)2nq+WkRdkE0;%1g9}B^Ppm`^iGCx!v z>B0&HWboj@gPg?~J38>tj{P4xDMlgnmb&QFEsfG8Q{G8b9QXlz_ujkT{ocFxj=wK2 zFCviW?|SP{M(7{m%q5o@lbc{1A%ckHAzv~iUp8b3$>?)LtYv5g_mit#Y zHJL9_;>MN}1}(SEm%?uBhJm$ZGwRIHGc9G#7WS6d2`=`r}!kx+!0wX$Vuh{pN^0Tnfl69=lc0HM`Xdxqysh0r*fxw;V`%z zU#c`Aa`35w4!%-QyfjC{?Mid|d`)GDe{k;4t@+R;w2NwZGs?q8uapf%6GN#Lc!{_y zj@+$844D3@KQD-p2DVT60*Q95C<$r{ zTw&B}aYdwn$3f%R4cd%j$0r;^298;%2!9_>abjWh4RJb{@Pf*Jm|bo zweh8EXL@y{uMG8--|(s49O(~+`h!z_{S}gzn}_NE9~uMa*`0Cu%Cp94`NsI}>fnn} zr8=xskMZ%>r&L|QvYiFXBB2A}B|@1Kxzpb}x(W=>QFINK=!#I{RZNuv z22(-pp+A-S+2w~%9!``}Zgqwgu+5snKR)kgBkN}aLNLwgJBS3!jPny=z032a(45tI gKhj`B{79?<>Lr#W=`VEub^b$Xb^r1I2$t!ow*{9)vj6}9 literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/setuptools/command/__pycache__/sdist.cpython-312.pyc b/venv/Lib/site-packages/setuptools/command/__pycache__/sdist.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..ca6d155fef3c2b3f6bf416071a936616fdfec200 GIT binary patch literal 11394 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 Gxqky)v{R`7 literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/setuptools/command/__pycache__/test.cpython-312.pyc b/venv/Lib/site-packages/setuptools/command/__pycache__/test.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..7b71fcfbef809d61b779a00d7e2b416c3f37f212 GIT binary patch literal 13215 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 z zG=i4TqLhz+DUc-JxYhBetjS0gdpB=5qbF(6cO8Nk?&I2c!H z>`EBIMKCzbuWN!`ei}&z(J4uc)uYzMupfy6iAJcm4kRXpO!!BE@heDhC5+;LIx1Vi zS`1fQ7=TKN2m}d2SQ$uW)H4S^m%ARb{#31eF*-VmGqkqJt>A0~kf0d{QarlMjC$3H z`Zc;B0_C8rClX08QAz~4qsk!$$UGFuDa_JXX$yg^{F3Sba3KH$?uqaysHWh;4})~2 zy3l_Cq7jf0f|3bA1yn6Vm7EeFpeJ~is-V*gNz}DmE2y}Hnl+Jk<_U`xhNhg9oIvRx zp-ui6GO#pjaUN?HtEcjzlks>_(|hQ)m$}xwOk+!~VRxos_s#RU&Lf%5BUHe-TV63U zJUx6}K^Y}m-a2JPn=~4zdGE}b=`%|W`ymXY;hAN=dxiIbHjHs3*AokJre@c&v*QzQ z<=fBas&-_mcHDGjtM)I3vfg75SmLSBtgj1F*0cK_V=3Ee>ZRF=4%A7vu6V0Z%31dM zQ8nHA$O@g-u;(KSe4?JY^>M!PvAi92_>)5wy$7uydMbMN*go9i#PS|n?}4%ppLAgP zpskN{tmBj@9Z-4}gqL0D87bvSz&8Rf4{=?TptgjL2(_O<$2J{P!gOa)A(8#Qo*90;rGrwC0JX<#W_Ou5zeQqy(fH{=qQBJBi)?HaWnnPK+7Ad#rMCV65aXVGU|I zFl0FyyCj8x7eh3_38eb)a z{I+k)?@Zj7Smpz(KL0{x*4MVgx1pflv5`ZgZclG?D-60v`SZyCATRpzd@VJbZ@zLU6-$OyZaoVzzJ2Cphz%%4DE4jfK- z<4dUrmZnZ9xZ*elQmOD*<0>1!umx*s%!R}c6W%VWtmgG?q^{^P25y33g<1fjB<}WO zv?6BFK@3MksoDgSj~CHG8Vk5fK_ntvX&Z&9a@2Tf8zRZEQMg(71Xb5DWJxa|M*_zw ztP9M!R_I<}%_Tp8j;TsYsn_4?2hbpc#143dXg%*~_S)r~(3%liZ&ofoc}rOq`f|d_ zjBqk5oXQGkmYioO^1+!#!<7PcG<+=|&V=DoVlo0zY;IAKBP2@0J5(ORQi$OaCnsS# z(vR8Gn9=+2B9>l;Om$&&DM-Mv=zv*eKB$77xO#5{*D0{BTm#7#Zb*-$M}J zMG#IfSJ1dM`j80{6^eo45UXdg0khNaC;uF>H<@2@2k!d%uAaKjIjihfPv5WNZ2tSa z*LId&+s@dl?^#&ejt5pKtzqfD-EM2W@3q+~0eg0D&bB3E+j8GhXWNE-TGp_%_9SEX zt=I&u@t&o|c91q==3C?7b8Rn_?pb)-KCKQ*_nWG1Tkm)Aw)V9q#$Jx|c4BKe&f5uw zPlO58Hd{Z7^#UDwkHc?zMs#C=7IGQ3Mc&0s!tC!c`vzusvsEwZd-R_{bOC;oM5aVb zi-IUK+bC7jVpTn(7l-H-!hilC)3{U`gG$Ztn$g{j(DO!jS11}EO%gm%{leyBGl&-# z0m`Zob>9Ui4^gRIaj*u3Ood0Y-n4G)HWDV`NV1Ct{bJC*cGf5ZYj^8eEUgc$1J8zL zoSO>f19s}9Ks)U8@XQ!V2Ekg_RVEzlhELl7N literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/setuptools/command/__pycache__/upload.cpython-312.pyc b/venv/Lib/site-packages/setuptools/command/__pycache__/upload.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..a2a0ee3d24f65bd7c94fbf9117927b89a1e0e576 GIT binary patch literal 983 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 C|M*P+ literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/setuptools/command/_requirestxt.py b/venv/Lib/site-packages/setuptools/command/_requirestxt.py new file mode 100644 index 0000000..b0c2d70 --- /dev/null +++ b/venv/Lib/site-packages/setuptools/command/_requirestxt.py @@ -0,0 +1,129 @@ +"""Helper code used to generate ``requires.txt`` files in the egg-info directory. + +The ``requires.txt`` file has an specific format: + - Environment markers need to be part of the section headers and + should not be part of the requirement spec itself. + +See https://setuptools.pypa.io/en/latest/deprecated/python_eggs.html#requires-txt +""" + +import io +from collections import defaultdict +from itertools import filterfalse +from typing import Dict, List, Tuple, Mapping, TypeVar + +from .. import _reqs +from ..extern.jaraco.text import yield_lines +from ..extern.packaging.requirements import Requirement + + +# dict can work as an ordered set +_T = TypeVar("_T") +_Ordered = Dict[_T, None] +_ordered = dict +_StrOrIter = _reqs._StrOrIter + + +def _prepare( + install_requires: _StrOrIter, extras_require: Mapping[str, _StrOrIter] +) -> Tuple[List[str], Dict[str, List[str]]]: + """Given values for ``install_requires`` and ``extras_require`` + create modified versions in a way that can be written in ``requires.txt`` + """ + extras = _convert_extras_requirements(extras_require) + return _move_install_requirements_markers(install_requires, extras) + + +def _convert_extras_requirements( + extras_require: Mapping[str, _StrOrIter], +) -> Mapping[str, _Ordered[Requirement]]: + """ + Convert requirements in `extras_require` of the form + `"extra": ["barbazquux; {marker}"]` to + `"extra:{marker}": ["barbazquux"]`. + """ + output: Mapping[str, _Ordered[Requirement]] = defaultdict(dict) + for section, v in extras_require.items(): + # Do not strip empty sections. + output[section] + for r in _reqs.parse(v): + output[section + _suffix_for(r)].setdefault(r) + + return output + + +def _move_install_requirements_markers( + install_requires: _StrOrIter, extras_require: Mapping[str, _Ordered[Requirement]] +) -> Tuple[List[str], Dict[str, List[str]]]: + """ + The ``requires.txt`` file has an specific format: + - Environment markers need to be part of the section headers and + should not be part of the requirement spec itself. + + 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. + + inst_reqs = list(_reqs.parse(install_requires)) + simple_reqs = filter(_no_marker, inst_reqs) + complex_reqs = filterfalse(_no_marker, inst_reqs) + simple_install_requires = list(map(str, simple_reqs)) + + for r in complex_reqs: + extras_require[':' + str(r.marker)].setdefault(r) + + expanded_extras = dict( + # list(dict.fromkeys(...)) ensures a list of unique strings + (k, list(dict.fromkeys(str(r) for r in map(_clean_req, v)))) + for k, v in extras_require.items() + ) + + return simple_install_requires, expanded_extras + + +def _suffix_for(req): + """Return the 'extras_require' suffix for a given requirement.""" + return ':' + str(req.marker) if req.marker else '' + + +def _clean_req(req): + """Given a Requirement, remove environment markers and return it""" + r = Requirement(str(req)) # create a copy before modifying + r.marker = None + return r + + +def _no_marker(req): + return not req.marker + + +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 = io.StringIO() + install_requires, extras_require = _prepare( + dist.install_requires or (), dist.extras_require or {} + ) + _write_requirements(data, install_requires) + 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()) diff --git a/venv/Lib/site-packages/setuptools/command/alias.py b/venv/Lib/site-packages/setuptools/command/alias.py new file mode 100644 index 0000000..e7b4d54 --- /dev/null +++ b/venv/Lib/site-packages/setuptools/command/alias.py @@ -0,0 +1,78 @@ +from distutils.errors import DistutilsOptionError + +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/site-packages/setuptools/command/bdist_egg.py b/venv/Lib/site-packages/setuptools/command/bdist_egg.py new file mode 100644 index 0000000..3687efd --- /dev/null +++ b/venv/Lib/site-packages/setuptools/command/bdist_egg.py @@ -0,0 +1,461 @@ +"""setuptools.command.bdist_egg + +Build .egg distributions""" + +from distutils.dir_util import remove_tree, mkpath +from distutils import log +from types import CodeType +import sys +import os +import re +import textwrap +import marshal + +from setuptools.extension import Library +from setuptools import Command +from .._path import ensure_directory + +from sysconfig import get_path, get_python_version + + +def _get_purelib(): + return get_path("purelib") + + +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, importlib.util + __file__ = pkg_resources.resource_filename(__name__, %r) + __loader__ = None; del __bootstrap__, __loader__ + spec = importlib.util.spec_from_file_location(__name__,__file__) + mod = importlib.util.module_from_spec(spec) + spec.loader.exec_module(mod) + __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 " + "(by default uses `pkg_resources.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: + from pkg_resources import get_build_platform + + 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 = ei_cmd._get_egg_basename( + py_version=get_python_version(), + platform=self.distribution.has_ext_modules() and self.plat_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): # noqa: C901 # is too complex (14) # FIXME + # 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): + return 'w' + + 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 + yield from walker + + +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] + 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""" + yield from code.co_names + for const in code.co_consts: + if isinstance(const, str): + yield const + elif isinstance(const, CodeType): + yield from iter_symbols(const) + + +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" + ) + return False + + +# 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/site-packages/setuptools/command/bdist_rpm.py b/venv/Lib/site-packages/setuptools/command/bdist_rpm.py new file mode 100644 index 0000000..70ed6b6 --- /dev/null +++ b/venv/Lib/site-packages/setuptools/command/bdist_rpm.py @@ -0,0 +1,39 @@ +import distutils.command.bdist_rpm as orig + +from ..warnings import SetuptoolsDeprecationWarning + + +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. + """ + + def run(self): + SetuptoolsDeprecationWarning.emit( + "Deprecated command", + """ + bdist_rpm is deprecated and will be removed in a future version. + Use bdist_wheel (wheel packages) instead. + """, + see_url="https://github.com/pypa/setuptools/issues/1988", + due_date=(2023, 10, 30), # Deprecation introduced in 22 Oct 2021. + ) + + # ensure distro name is up-to-date + self.run_command('egg_info') + + orig.bdist_rpm.run(self) + + def _make_spec_file(self): + spec = orig.bdist_rpm._make_spec_file(self) + return [ + line.replace( + "setup.py install ", + "setup.py install --single-version-externally-managed ", + ).replace("%setup", "%setup -n %{name}-%{unmangled_version}") + for line in spec + ] diff --git a/venv/Lib/site-packages/setuptools/command/build.py b/venv/Lib/site-packages/setuptools/command/build.py new file mode 100644 index 0000000..afda7e3 --- /dev/null +++ b/venv/Lib/site-packages/setuptools/command/build.py @@ -0,0 +1,140 @@ +from typing import Dict, List, Protocol +from distutils.command.build import build as _build + +from ..warnings import SetuptoolsDeprecationWarning + +_ORIGINAL_SUBCOMMANDS = {"build_py", "build_clib", "build_ext", "build_scripts"} + + +class build(_build): + # copy to avoid sharing the object with parent class + sub_commands = _build.sub_commands[:] + + def get_sub_commands(self): + subcommands = {cmd[0] for cmd in _build.sub_commands} + if subcommands - _ORIGINAL_SUBCOMMANDS: + SetuptoolsDeprecationWarning.emit( + "Direct usage of `distutils` commands", + """ + It seems that you are using `distutils.command.build` to add + new subcommands. Using `distutils` directly is considered deprecated, + please use `setuptools.command.build`. + """, + due_date=(2023, 12, 13), # Warning introduced in 13 Jun 2022. + see_url="https://peps.python.org/pep-0632/", + ) + self.sub_commands = _build.sub_commands + return super().get_sub_commands() + + +class SubCommand(Protocol): + """In order to support editable installations (see :pep:`660`) all + build subcommands **SHOULD** implement this protocol. They also **MUST** inherit + from ``setuptools.Command``. + + When creating an :pep:`editable wheel <660>`, ``setuptools`` will try to evaluate + custom ``build`` subcommands using the following procedure: + + 1. ``setuptools`` will set the ``editable_mode`` attribute to ``True`` + 2. ``setuptools`` will execute the ``run()`` command. + + .. important:: + Subcommands **SHOULD** take advantage of ``editable_mode=True`` to adequate + its behaviour or perform optimisations. + + For example, if a subcommand doesn't need to generate an extra file and + all it does is to copy a source file into the build directory, + ``run()`` **SHOULD** simply "early return". + + Similarly, if the subcommand creates files that would be placed alongside + Python files in the final distribution, during an editable install + the command **SHOULD** generate these files "in place" (i.e. write them to + the original source directory, instead of using the build directory). + Note that ``get_output_mapping()`` should reflect that and include mappings + for "in place" builds accordingly. + + 3. ``setuptools`` use any knowledge it can derive from the return values of + ``get_outputs()`` and ``get_output_mapping()`` to create an editable wheel. + When relevant ``setuptools`` **MAY** attempt to use file links based on the value + of ``get_output_mapping()``. Alternatively, ``setuptools`` **MAY** attempt to use + :doc:`import hooks ` to redirect any attempt to import + to the directory with the original source code and other files built in place. + + Please note that custom sub-commands **SHOULD NOT** rely on ``run()`` being + executed (or not) to provide correct return values for ``get_outputs()``, + ``get_output_mapping()`` or ``get_source_files()``. The ``get_*`` methods should + work independently of ``run()``. + """ + + editable_mode: bool = False + """Boolean flag that will be set to ``True`` when setuptools is used for an + editable installation (see :pep:`660`). + Implementations **SHOULD** explicitly set the default value of this attribute to + ``False``. + When subcommands run, they can use this flag to perform optimizations or change + their behaviour accordingly. + """ + + build_lib: str + """String representing the directory where the build artifacts should be stored, + e.g. ``build/lib``. + For example, if a distribution wants to provide a Python module named ``pkg.mod``, + then a corresponding file should be written to ``{build_lib}/package/module.py``. + A way of thinking about this is that the files saved under ``build_lib`` + would be eventually copied to one of the directories in :obj:`site.PREFIXES` + upon installation. + + A command that produces platform-independent files (e.g. compiling text templates + into Python functions), **CAN** initialize ``build_lib`` by copying its value from + the ``build_py`` command. On the other hand, a command that produces + platform-specific files **CAN** initialize ``build_lib`` by copying its value from + the ``build_ext`` command. In general this is done inside the ``finalize_options`` + method with the help of the ``set_undefined_options`` command:: + + def finalize_options(self): + self.set_undefined_options("build_py", ("build_lib", "build_lib")) + ... + """ + + def initialize_options(self): + """(Required by the original :class:`setuptools.Command` interface)""" + + def finalize_options(self): + """(Required by the original :class:`setuptools.Command` interface)""" + + def run(self): + """(Required by the original :class:`setuptools.Command` interface)""" + + def get_source_files(self) -> List[str]: + """ + Return a list of all files that are used by the command to create the expected + outputs. + For example, if your build command transpiles Java files into Python, you should + list here all the Java files. + The primary purpose of this function is to help populating the ``sdist`` + with all the files necessary to build the distribution. + All files should be strings relative to the project root directory. + """ + + def get_outputs(self) -> List[str]: + """ + Return a list of files intended for distribution as they would have been + produced by the build. + These files should be strings in the form of + ``"{build_lib}/destination/file/path"``. + + .. note:: + The return value of ``get_output()`` should include all files used as keys + in ``get_output_mapping()`` plus files that are generated during the build + and don't correspond to any source file already present in the project. + """ + + def get_output_mapping(self) -> Dict[str, str]: + """ + Return a mapping between destination files as they would be produced by the + build (dict keys) into the respective existing (source) files (dict values). + Existing (source) files should be represented as strings relative to the project + root directory. + Destination files should be strings in the form of + ``"{build_lib}/destination/file/path"``. + """ diff --git a/venv/Lib/site-packages/setuptools/command/build_clib.py b/venv/Lib/site-packages/setuptools/command/build_clib.py new file mode 100644 index 0000000..acd4d1d --- /dev/null +++ b/venv/Lib/site-packages/setuptools/command/build_clib.py @@ -0,0 +1,104 @@ +import distutils.command.build_clib as orig +from distutils.errors import DistutilsSetupError +from distutils import log + +try: + from distutils._modified import newer_pairwise_group +except ImportError: + # fallback for SETUPTOOLS_USE_DISTUTILS=stdlib + from .._distutils._modified 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 = sorted(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/site-packages/setuptools/command/build_ext.py b/venv/Lib/site-packages/setuptools/command/build_ext.py new file mode 100644 index 0000000..b5c98c8 --- /dev/null +++ b/venv/Lib/site-packages/setuptools/command/build_ext.py @@ -0,0 +1,459 @@ +import os +import sys +import itertools +from importlib.machinery import EXTENSION_SUFFIXES +from importlib.util import cache_from_source as _compiled_file_name +from typing import Dict, Iterator, List, Tuple +from pathlib import Path + +from distutils.command.build_ext import build_ext as _du_build_ext +from distutils.ccompiler import new_compiler +from distutils.sysconfig import customize_compiler, get_config_var +from distutils import log + +from setuptools.errors import BaseError +from setuptools.extension import Extension, Library + +try: + # Attempt to use Cython for building extensions, if available + from Cython.Distutils.build_ext import build_ext as _build_ext # type: ignore[import-not-found] # Cython not installed on CI tests + + # 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") +# Not publicly exposed in typeshed distutils stubs, but this is done on purpose +# See https://github.com/pypa/setuptools/pull/4228#issuecomment-1959856400 +from distutils.sysconfig import _config_vars as _CONFIG_VARS # type: ignore # 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 # type: ignore[import-not-found] # https://github.com/python/mypy/issues/13002 + + 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 + return None + + +class build_ext(_build_ext): + editable_mode: bool = False + inplace: bool = False + + 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 _get_inplace_equivalent(self, build_py, ext: Extension) -> Tuple[str, str]: + 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) + inplace_file = os.path.join(package_dir, os.path.basename(filename)) + regular_file = os.path.join(self.build_lib, filename) + return (inplace_file, regular_file) + + def copy_extensions_to_source(self): + build_py = self.get_finalized_command('build_py') + for ext in self.extensions: + inplace_file, regular_file = self._get_inplace_equivalent(build_py, ext) + + # Always copy, even if source is older than destination, to ensure + # that the right extensions for the current Python/platform are + # used. + if os.path.exists(regular_file) or not ext.optional: + self.copy_file(regular_file, inplace_file, level=self.verbose) + + if ext._needs_stub: + inplace_stub = self._get_equivalent_stub(ext, inplace_file) + self._write_stub_file(inplace_stub, ext, compile=True) + # Always compile stub and remove the original (leave the cache behind) + # (this behaviour was observed in previous iterations of the code) + + def _get_equivalent_stub(self, ext: Extension, output_file: str) -> str: + dir_ = os.path.dirname(output_file) + _, _, name = ext.name.rpartition(".") + return f"{os.path.join(dir_, name)}.py" + + def _get_output_mapping(self) -> Iterator[Tuple[str, str]]: + if not self.inplace: + return + + build_py = self.get_finalized_command('build_py') + opt = self.get_finalized_command('install_lib').optimize or "" + + for ext in self.extensions: + inplace_file, regular_file = self._get_inplace_equivalent(build_py, ext) + yield (regular_file, inplace_file) + + if ext._needs_stub: + # This version of `build_ext` always builds artifacts in another dir, + # when "inplace=True" is given it just copies them back. + # This is done in the `copy_extensions_to_source` function, which + # always compile stub files via `_compile_and_remove_stub`. + # At the end of the process, a `.pyc` stub file is created without the + # corresponding `.py`. + + inplace_stub = self._get_equivalent_stub(ext, inplace_file) + regular_stub = self._get_equivalent_stub(ext, regular_file) + inplace_cache = _compiled_file_name(inplace_stub, optimization=opt) + output_cache = _compiled_file_name(regular_stub, optimization=opt) + yield (output_cache, inplace_cache) + + def get_ext_filename(self, fullname): + so_ext = os.getenv('SETUPTOOLS_EXT_SUFFIX') + if so_ext: + filename = os.path.join(*fullname.split('.')) + so_ext + else: + filename = _build_ext.get_ext_filename(self, fullname) + so_ext = get_config_var('EXT_SUFFIX') + + if fullname in self.ext_map: + ext = self.ext_map[fullname] + use_abi3 = ext.py_limited_api and get_abi3_suffix() + if use_abi3: + filename = filename[: -len(so_ext)] + so_ext = get_abi3_suffix() + filename = filename + so_ext + 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 = {} + self.editable_mode = False + + 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) + + if self.editable_mode: + self.inplace = True + + 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: + build_lib = self.get_finalized_command('build_py').build_lib + self.write_stub(build_lib, 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_source_files(self) -> List[str]: + return [*_build_ext.get_source_files(self), *self._get_internal_depends()] + + def _get_internal_depends(self) -> Iterator[str]: + """Yield ``ext.depends`` that are contained by the project directory""" + project_root = Path(self.distribution.src_root or os.curdir).resolve() + depends = (dep for ext in self.extensions for dep in ext.depends) + + def skip(orig_path: str, reason: str) -> None: + log.info( + "dependency %s won't be automatically " + "included in the manifest: the path %s", + orig_path, + reason, + ) + + for dep in depends: + path = Path(dep) + + if path.is_absolute(): + skip(dep, "must be relative") + continue + + if ".." in path.parts: + skip(dep, "can't have `..` segments") + continue + + try: + resolved = (project_root / path).resolve(strict=True) + except OSError: + skip(dep, "doesn't exist") + continue + + try: + resolved.relative_to(project_root) + except ValueError: + skip(dep, "must be inside the project root") + continue + + yield path.as_posix() + + def get_outputs(self) -> List[str]: + if self.inplace: + return list(self.get_output_mapping().keys()) + return sorted(_build_ext.get_outputs(self) + self.__get_stubs_outputs()) + + def get_output_mapping(self) -> Dict[str, str]: + """See :class:`setuptools.commands.build.SubCommand`""" + mapping = self._get_output_mapping() + return dict(sorted(mapping, key=lambda x: x[0])) + + 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): + stub_file = os.path.join(output_dir, *ext._full_name.split('.')) + '.py' + self._write_stub_file(stub_file, ext, compile) + + def _write_stub_file(self, stub_file: str, ext: Extension, compile=False): + log.info("writing stub loader for %s to %s", ext._full_name, stub_file) + if compile and os.path.exists(stub_file): + raise BaseError(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, importlib.util" + 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)"), + " spec = importlib.util.spec_from_file_location(", + " __name__, __file__)", + " mod = importlib.util.module_from_spec(spec)", + " spec.loader.exec_module(mod)", + " finally:", + if_dl(" sys.setdlopenflags(old_flags)"), + " os.chdir(old_dir)", + "__bootstrap__()", + "", # terminal \n + ]) + ) + f.close() + if compile: + self._compile_and_remove_stub(stub_file) + + def _compile_and_remove_stub(self, stub_file: str): + 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/site-packages/setuptools/command/build_py.py b/venv/Lib/site-packages/setuptools/command/build_py.py new file mode 100644 index 0000000..3f40b06 --- /dev/null +++ b/venv/Lib/site-packages/setuptools/command/build_py.py @@ -0,0 +1,393 @@ +from functools import partial +from glob import glob +from distutils.util import convert_path +import distutils.command.build_py as orig +import os +import fnmatch +import textwrap +import distutils.errors +import itertools +import stat +from pathlib import Path +from typing import Dict, Iterable, Iterator, List, Optional, Tuple + +from ..extern.more_itertools import unique_everseen +from ..warnings import SetuptoolsDeprecationWarning + + +_IMPLICIT_DATA_FILES = ('*.pyi', 'py.typed') + + +def make_writable(target): + os.chmod(target, os.stat(target).st_mode | stat.S_IWRITE) + + +class build_py(orig.build_py): + """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. + """ + + editable_mode: bool = False + existing_egg_info_dir: Optional[str] = None #: Private API, internal use only. + + 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 = [] + + def copy_file( + self, infile, outfile, preserve_mode=1, preserve_times=1, link=None, level=1 + ): + # Overwrite base class to allow using links + if link: + infile = str(Path(infile).resolve()) + outfile = str(Path(outfile).resolve()) + return super().copy_file( + infile, outfile, preserve_mode, preserve_times, link, level + ) + + def run(self): + """Build modules, packages, and copy data files to build directory""" + if not (self.py_modules or self.packages) or self.editable_mode: + return + + if self.py_modules: + self.build_modules() + + if self.packages: + self.build_packages() + self.build_package_data() + + # Only compile actual .py files, using our base class' idea of what our + # output files are. + self.byte_compile(orig.build_py.get_outputs(self, include_bytecode=0)) + + def __getattr__(self, attr): + "lazily compute data files" + if attr == 'data_files': + self.data_files = self._get_data_files() + return self.data_files + return orig.build_py.__getattr__(self, attr) + + def build_module(self, module, module_file, package): + outfile, copied = orig.build_py.build_module(self, module, module_file, package) + if copied: + self.__updated_files.append(outfile) + return outfile, copied + + def _get_data_files(self): + """Generate list of '(package,src_dir,build_dir,filenames)' tuples""" + self.analyze_manifest() + return list(map(self._get_pkg_data_files, self.packages or ())) + + def get_data_files_without_manifest(self): + """ + Generate list of ``(package,src_dir,build_dir,filenames)`` tuples, + but without triggering any attempt to analyze or build the manifest. + """ + # Prevent eventual errors from unset `manifest_files` + # (that would otherwise be set by `analyze_manifest`) + self.__dict__.setdefault('manifest_files', {}) + 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, + extra_patterns=_IMPLICIT_DATA_FILES, + ) + globs_expanded = map(partial(glob, recursive=True), 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 get_outputs(self, include_bytecode=1) -> List[str]: + """See :class:`setuptools.commands.build.SubCommand`""" + if self.editable_mode: + return list(self.get_output_mapping().keys()) + return super().get_outputs(include_bytecode) + + def get_output_mapping(self) -> Dict[str, str]: + """See :class:`setuptools.commands.build.SubCommand`""" + mapping = itertools.chain( + self._get_package_data_output_mapping(), + self._get_module_mapping(), + ) + return dict(sorted(mapping, key=lambda x: x[0])) + + def _get_module_mapping(self) -> Iterator[Tuple[str, str]]: + """Iterate over all modules producing (dest, src) pairs.""" + for package, module, module_file in self.find_all_modules(): + package = package.split('.') + filename = self.get_module_outfile(self.build_lib, package, module) + yield (filename, module_file) + + def _get_package_data_output_mapping(self) -> Iterator[Tuple[str, str]]: + """Iterate over package data producing (dest, src) pairs.""" + for package, src_dir, build_dir, filenames in self.data_files: + for filename in filenames: + target = os.path.join(build_dir, filename) + srcfile = os.path.join(src_dir, filename) + yield (target, srcfile) + + def build_package_data(self): + """Copy data files into build directory""" + for target, srcfile in self._get_package_data_output_mapping(): + self.mkpath(os.path.dirname(target)) + _outf, _copied = self.copy_file(srcfile, target) + make_writable(target) + + 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 + + if ( + getattr(self, 'existing_egg_info_dir', None) + and Path(self.existing_egg_info_dir, "SOURCES.txt").exists() + ): + egg_info_dir = self.existing_egg_info_dir + manifest = Path(egg_info_dir, "SOURCES.txt") + files = manifest.read_text(encoding="utf-8").splitlines() + else: + self.run_command('egg_info') + ei_cmd = self.get_finalized_command('egg_info') + egg_info_dir = ei_cmd.egg_info + files = ei_cmd.filelist.files + + check = _IncludePackageDataAbuse() + for path in self._filter_build_files(files, egg_info_dir): + 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 f == oldf: + if check.is_module(f): + continue # it's a module, not data + else: + importable = check.importable_subpackage(src_dirs[d], f) + if importable: + check.warn(importable) + mf.setdefault(src_dirs[d], []).append(path) + + def _filter_build_files(self, files: Iterable[str], egg_info: str) -> Iterator[str]: + """ + ``build_meta`` may try to create egg_info outside of the project directory, + and this can be problematic for certain plugins (reported in issue #3500). + + Extensions might also include between their sources files created on the + ``build_lib`` and ``build_temp`` directories. + + This function should filter this case of invalid files out. + """ + build = self.get_finalized_command("build") + build_dirs = (egg_info, self.build_lib, build.build_temp, build.build_base) + norm_dirs = [os.path.normpath(p) for p in build_dirs if p] + + for file in files: + norm_path = os.path.normpath(file) + if not os.path.isabs(file) or all(d not in norm_path for d in norm_dirs): + yield file + + 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 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) + self.editable_mode = False + self.existing_egg_info_dir = None + + 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, extra_patterns=()): + """ + 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( + extra_patterns, + 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 + ) + + +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) + + +class _IncludePackageDataAbuse: + """Inform users that package or module is included as 'data file'""" + + class _Warning(SetuptoolsDeprecationWarning): + _SUMMARY = """ + Package {importable!r} is absent from the `packages` configuration. + """ + + _DETAILS = """ + ############################ + # Package would be ignored # + ############################ + Python recognizes {importable!r} as an importable package[^1], + but it is absent from setuptools' `packages` configuration. + + This leads to an ambiguous overall configuration. If you want to distribute this + package, please make sure that {importable!r} is explicitly added + to the `packages` configuration field. + + Alternatively, you can also rely on setuptools' discovery methods + (for example by using `find_namespace_packages(...)`/`find_namespace:` + instead of `find_packages(...)`/`find:`). + + You can read more about "package discovery" on setuptools documentation page: + + - https://setuptools.pypa.io/en/latest/userguide/package_discovery.html + + If you don't want {importable!r} to be distributed and are + already explicitly excluding {importable!r} via + `find_namespace_packages(...)/find_namespace` or `find_packages(...)/find`, + you can try to use `exclude_package_data`, or `include-package-data=False` in + combination with a more fine grained `package-data` configuration. + + You can read more about "package data files" on setuptools documentation page: + + - https://setuptools.pypa.io/en/latest/userguide/datafiles.html + + + [^1]: For Python, any directory (with suitable naming) can be imported, + even if it does not contain any `.py` files. + On the other hand, currently there is no concept of package data + directory, all directories are treated like packages. + """ + # _DUE_DATE: still not defined as this is particularly controversial. + # Warning initially introduced in May 2022. See issue #3340 for discussion. + + def __init__(self): + self._already_warned = set() + + def is_module(self, file): + return file.endswith(".py") and file[: -len(".py")].isidentifier() + + def importable_subpackage(self, parent, file): + pkg = Path(file).parent + parts = list(itertools.takewhile(str.isidentifier, pkg.parts)) + if parts: + return ".".join([parent, *parts]) + return None + + def warn(self, importable): + if importable not in self._already_warned: + self._Warning.emit(importable=importable) + self._already_warned.add(importable) diff --git a/venv/Lib/site-packages/setuptools/command/develop.py b/venv/Lib/site-packages/setuptools/command/develop.py new file mode 100644 index 0000000..d8c1b49 --- /dev/null +++ b/venv/Lib/site-packages/setuptools/command/develop.py @@ -0,0 +1,192 @@ +from distutils.util import convert_path +from distutils import log +from distutils.errors import DistutilsOptionError +import os +import glob + +from setuptools.command.easy_install import easy_install +from setuptools import _normalization +from setuptools import _path +from setuptools import namespaces +import setuptools + + +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): + import pkg_resources + + ei = self.get_finalized_command("egg_info") + 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 = ( + _normalization.filename_component_broken(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 = _path.normpath(self.egg_base) + egg_path = _path.normpath(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 = _path.normpath(os.path.join(install_dir, egg_path, path_to_setup)) + curdir = _path.normpath(os.curdir) + if resolved != curdir: + raise DistutilsOptionError( + "Can't get a consistent path to setup script from" + " installation directory", + resolved, + curdir, + ) + return path_to_setup + + def install_for_development(self): + self.run_command('egg_info') + + # Build extensions in-place + self.reinitialize_command('build_ext', inplace=1) + self.run_command('build_ext') + + 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 open(script_path) as strm: + script_text = strm.read() + self.install_script(dist, script_name, script_text, script_path) + + return None + + 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/site-packages/setuptools/command/dist_info.py b/venv/Lib/site-packages/setuptools/command/dist_info.py new file mode 100644 index 0000000..52c0721 --- /dev/null +++ b/venv/Lib/site-packages/setuptools/command/dist_info.py @@ -0,0 +1,106 @@ +""" +Create a dist_info directory +As defined in the wheel specification +""" + +import os +import shutil +from contextlib import contextmanager +from distutils import log +from distutils.core import Command +from pathlib import Path +from typing import cast + +from .. import _normalization +from .egg_info import egg_info as egg_info_cls + + +class dist_info(Command): + """ + This command is private and reserved for internal use of setuptools, + users should rely on ``setuptools.build_meta`` APIs. + """ + + description = "DO NOT CALL DIRECTLY, INTERNAL ONLY: create .dist-info directory" + + user_options = [ + ( + 'output-dir=', + 'o', + "directory inside of which the .dist-info will be" + "created (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]"), + ('keep-egg-info', None, "*TRANSITIONAL* will be removed in the future"), + ] + + boolean_options = ['tag-date', 'keep-egg-info'] + negative_opt = {'no-date': 'tag-date'} + + def initialize_options(self): + self.output_dir = None + self.name = None + self.dist_info_dir = None + self.tag_date = None + self.tag_build = None + self.keep_egg_info = False + + def finalize_options(self): + dist = self.distribution + project_dir = dist.src_root or os.curdir + self.output_dir = Path(self.output_dir or project_dir) + + egg_info = cast(egg_info_cls, self.reinitialize_command("egg_info")) + egg_info.egg_base = str(self.output_dir) + + if self.tag_date: + egg_info.tag_date = self.tag_date + else: + self.tag_date = egg_info.tag_date + + if self.tag_build: + egg_info.tag_build = self.tag_build + else: + self.tag_build = egg_info.tag_build + + egg_info.finalize_options() + self.egg_info = egg_info + + name = _normalization.safer_name(dist.get_name()) + version = _normalization.safer_best_effort_version(dist.get_version()) + self.name = f"{name}-{version}" + self.dist_info_dir = os.path.join(self.output_dir, f"{self.name}.dist-info") + + @contextmanager + def _maybe_bkp_dir(self, dir_path: str, requires_bkp: bool): + if requires_bkp: + bkp_name = f"{dir_path}.__bkp__" + _rm(bkp_name, ignore_errors=True) + shutil.copytree(dir_path, bkp_name, dirs_exist_ok=True, symlinks=True) + try: + yield + finally: + _rm(dir_path, ignore_errors=True) + shutil.move(bkp_name, dir_path) + else: + yield + + def run(self): + self.output_dir.mkdir(parents=True, exist_ok=True) + self.egg_info.run() + egg_info_dir = self.egg_info.egg_info + assert os.path.isdir(egg_info_dir), ".egg-info dir should have been created" + + log.info("creating '{}'".format(os.path.abspath(self.dist_info_dir))) + bdist_wheel = self.get_finalized_command('bdist_wheel') + + # TODO: if bdist_wheel if merged into setuptools, just add "keep_egg_info" there + with self._maybe_bkp_dir(egg_info_dir, self.keep_egg_info): + bdist_wheel.egg2dist(egg_info_dir, self.dist_info_dir) + + +def _rm(dir_name, **opts): + if os.path.isdir(dir_name): + shutil.rmtree(dir_name, **opts) diff --git a/venv/Lib/site-packages/setuptools/command/easy_install.py b/venv/Lib/site-packages/setuptools/command/easy_install.py new file mode 100644 index 0000000..87a68c2 --- /dev/null +++ b/venv/Lib/site-packages/setuptools/command/easy_install.py @@ -0,0 +1,2361 @@ +""" +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.pypa.io/en/latest/deprecated/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 import log, dir_util +from distutils.command.build_scripts import first_line_re +from distutils.spawn import find_executable +from distutils.command import install +import sys +import os +from typing import Dict, List +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 +import configparser +import sysconfig + +from sysconfig import get_path + +from setuptools import Command +from setuptools.sandbox import run_setup +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.warnings import SetuptoolsDeprecationWarning, SetuptoolsWarning +from setuptools.wheel import Wheel +from pkg_resources import ( + normalize_path, + resource_string, + get_distribution, + find_distributions, + Environment, + Requirement, + Distribution, + PathMetadata, + EggMetadata, + WorkingSet, + DistributionNotFound, + VersionConflict, + DEVELOP_DIST, +) +import pkg_resources +from ..compat import py39, py311 +from .._path import ensure_directory +from ..extern.jaraco.text import yield_lines + + +# Turn on PEP440Warnings +warnings.filterwarnings("default", category=pkg_resources.PEP440Warning) + +__all__ = [ + 'easy_install', + 'PthDistributions', + 'extract_wininst_cfg', + 'get_exe_prefixes', +] + + +def is_64bit(): + return struct.calcsize("P") == 8 + + +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): + EasyInstallDeprecationWarning.emit() + + # 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 + self.install_userbase = site.USER_BASE + self.install_usersite = site.USER_SITE + 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 = {} + # 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): # noqa: C901 # is too complex (25) # FIXME + self.version and self._render_version() + + py_version = sys.version.split()[0] + + self.config_vars = dict(sysconfig.get_config_vars()) + + self.config_vars.update({ + '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': f'{sys.version_info.major}.{sys.version_info.minor}', + 'py_version_nodot': f'{sys.version_info.major}{sys.version_info.minor}', + 'sys_prefix': self.config_vars['prefix'], + 'sys_exec_prefix': self.config_vars['exec_prefix'], + # Only POSIX systems have abiflags + 'abiflags': getattr(sys, 'abiflags', ''), + # Only python 3.9+ has platlibdir + 'platlibdir': getattr(sys, 'platlibdir', 'lib'), + }) + with contextlib.suppress(AttributeError): + # only for distutils outside stdlib + self.config_vars.update({ + 'implementation_lower': install._get_implementation().lower(), + 'implementation': install._get_implementation(), + }) + + # pypa/distutils#113 Python 3.9 compat + self.config_vars.setdefault( + 'py_version_nodot_plat', + getattr(sys, 'windir', '').replace('.', ''), + ) + + self.config_vars['userbase'] = self.install_userbase + self.config_vars['usersite'] = self.install_usersite + if self.user and not site.ENABLE_USER_SITE: + 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')) + self.all_site_dirs = get_site_dirs() + self.all_site_dirs.extend(self._process_site_dirs(self.site_dirs)) + + if not self.editable: + self.check_site_dir() + default_index = os.getenv("__EASYINSTALL_INDEX", "https://pypi.org/simple/") + # ^ Private API for testing purposes only + self.index_url = self.index_url or default_index + 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, str): + 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')) + self.optimize = self._validate_optimize(self.optimize) + + 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 = [] + + @staticmethod + def _process_site_dirs(site_dirs): + if site_dirs is None: + return + + normpath = map(normalize_path, sys.path) + site_dirs = [os.path.expanduser(s.strip()) for s in 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: + yield normalize_path(d) + + @staticmethod + def _validate_optimize(value): + try: + value = int(value) + if value not in range(3): + raise ValueError + except ValueError as e: + raise DistutilsOptionError("--optimize must be 0, 1, or 2") from e + + return value + + def _fix_install_dir_for_user_site(self): + """ + Fix the install_dir if "--user" was used. + """ + if not self.user: + 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 = f'{os.name}_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): # noqa: C901 # is too complex (12) # FIXME + """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: + 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, 'wb').close() + os.unlink(testfile) + except OSError: + 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 self.multi_version and not os.path.exists(pth_file): + self.pth_file = None # 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.pypa.io/en/latest/deprecated/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): # noqa: C901 + """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', encoding="utf-8") + f.write('OK') + f.close() + """ + ) + + '\n' + ) + try: + if ok_exists: + os.unlink(ok_file) + dirname = os.path.dirname(ok_file) + os.makedirs(dirname, exist_ok=True) + f = open(pth_file, 'w', encoding=py39.LOCALE_ENCODING) + # ^-- Requires encoding="locale" instead of "utf-8" (python/cpython#77102). + except OSError: + 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="easy_install-") + try: + # cast to str as workaround for #709 and #710 and #712 + yield str(tmpdir) + finally: + os.path.exists(tmpdir) and _rmtree(tmpdir) + + def easy_install(self, spec, deps=False): + 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 + return None + + def select_scheme(self, name): + try: + install._select_scheme(self, name) + except AttributeError: + # stdlib distutils + install.install.select_scheme(self, name.replace('posix', 'unix')) + + # FIXME: 'easy_install.process_distribution' is too complex (12) + def process_distribution( # noqa: C901 + 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)) from e + except VersionConflict as e: + raise DistutilsError(e.report()) from e + 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: # TODO: is it safe to use utf-8? + 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 + installer_map = { + '.egg': self.install_egg, + '.exe': self.install_exe, + '.whl': self.install_wheel, + } + try: + install_dist = installer_map[dist_filename.lower()[-4:]] + except KeyError: + pass + else: + return [install_dist(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) + + # FIXME: 'easy_install.install_egg' is too complex (11) + def install_egg(self, egg_path, tmpdir): # noqa: C901 + 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 ( + os.path.exists(destination) and os.path.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') # TODO: probably it is safe to use utf-8 + 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) + + # FIXME: 'easy_install.exe_to_egg' is too complex (12) + def exe_to_egg(self, dist_filename, egg_tmp): # noqa: C901 + """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') # TODO: probably it is safe to use utf-8 + 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],)) from v + + 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] = 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): # noqa: C901 # is too complex (11) # FIXME + if self.pth_file is None: + return + + for d in self.pth_file[dist.key]: # drop old entries + if not self.multi_version and d.location == dist.location: + continue + + 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 self.dry_run: + return + + self.pth_file.save() + + if dist.key != 'setuptools': + return + + # 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) + + with open(filename, 'wt', encoding=py39.LOCALE_ENCODING) as f: + # Requires encoding="locale" instead of "utf-8" (python/cpython#77102). + f.write(self.pth_file.make_relative(dist.location) + '\n') + + 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.pypa.io/en/latest/deprecated/easy_install.html#custom-installation-locations + + + Please make the appropriate changes for your system and try again. + """ + ).strip() + + def create_home_path(self): + """Create directories under ~.""" + if not self.user: + return + home = convert_path(os.path.expanduser("~")) + for path in only_strs(self.config_vars.values()): + 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 = dict(config_vars) + 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 not prefix: + continue + + 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': + continue + + # 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' not in prefix: + continue + + home = os.environ.get('HOME') + if not home: + continue + + 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') + + sitedirs.extend(s for s in lib_paths if s not in sitedirs) + + if site.ENABLE_USER_SITE: + sitedirs.append(site.USER_SITE) + + with contextlib.suppress(AttributeError): + sitedirs.extend(site.getsitepackages()) + + return list(map(normalize_path, sitedirs)) + + +def expand_paths(inputs): # noqa: C901 # is too complex (11) # FIXME + """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 + with open(os.path.join(dirname, name), encoding=py39.LOCALE_ENCODING) as f: + # Requires encoding="locale" instead of "utf-8" (python/cpython#77102). + lines = list(yield_lines(f)) + + # Yield existing non-dupe, non-import directory lines from it + for line in lines: + if line.startswith("import"): + continue + + line = normalize_path(line.rstrip()) + if line in seen: + continue + + 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).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""" + + 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.paths, self.dirty = self._load() + # keep a copy if someone manually updates the paths attribute on the instance + self._init_paths = self.paths[:] + super().__init__([], None, None) + for path in yield_lines(self.paths): + list(map(self.add, find_distributions(path, True))) + + def _load_raw(self): + paths = [] + dirty = saw_import = False + seen = dict.fromkeys(self.sitedirs) + f = open(self.filename, 'rt', encoding=py39.LOCALE_ENCODING) + # ^-- Requires encoding="locale" instead of "utf-8" (python/cpython#77102). + for line in f: + path = line.rstrip() + # still keep imports and empty/commented lines for formatting + paths.append(path) + if line.startswith(('import ', 'from ')): + saw_import = True + continue + stripped_path = path.strip() + if not stripped_path or stripped_path.startswith('#'): + continue + # skip non-existent paths, in case somebody deleted a package + # manually, and duplicate paths as well + normalized_path = normalize_path(os.path.join(self.basedir, path)) + if normalized_path in seen or not os.path.exists(normalized_path): + log.debug("cleaned up dirty or duplicated %r", path) + dirty = True + paths.pop() + continue + seen[normalized_path] = 1 + f.close() + # remove any trailing empty/blank line + while paths and not paths[-1].strip(): + paths.pop() + dirty = True + return paths, dirty or (paths and saw_import) + + def _load(self): + if os.path.isfile(self.filename): + return self._load_raw() + return [], False + + def save(self): + """Write changed .pth file back to disk""" + # first reload the file + last_paths, last_dirty = self._load() + # and check that there are no difference with what we have. + # there can be difference if someone else has written to the file + # since we first loaded it. + # we don't want to lose the eventual new paths added since then. + for path in last_paths[:]: + if path not in self.paths: + self.paths.append(path) + log.info("detected new path %r", path) + last_dirty = True + else: + last_paths.remove(path) + # also, re-check that all paths are still valid before saving them + for path in self.paths[:]: + if path not in last_paths and not path.startswith(( + 'import ', + 'from ', + '#', + )): + absolute_path = os.path.join(self.basedir, path) + if not os.path.exists(absolute_path): + self.paths.remove(path) + log.info("removing now non-existent path %r", path) + last_dirty = True + + self.dirty |= last_dirty or self.paths != self._init_paths + 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', encoding=py39.LOCALE_ENCODING) as f: + # Requires encoding="locale" instead of "utf-8" (python/cpython#77102). + f.write(data) + elif os.path.exists(self.filename): + log.debug("Deleting empty %s", self.filename) + os.unlink(self.filename) + + self.dirty = False + self._init_paths[:] = self.paths[:] + + @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 + super().add(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 + super().remove(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 + yield from lines + 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 # type: ignore[misc] # Overwriting type + + +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() + # TODO: This code doesn't make sense. What is it trying to do? + raise (ev[0], ev[1] + (" %s %s" % (func, arg))) + + +def update_dist_caches(dist_path, fix_zipimporter_caches): + """ + Fix any globally cached `dist_path` related data + + `dist_path` should be a path of a newly installed egg distribution (zipped + or unzipped). + + sys.path_importer_cache contains finder objects that have been cached when + importing data from the original distribution. Any such finders need to be + cleared since the replacement distribution might be packaged differently, + e.g. a zipped egg distribution might get replaced with an unzipped egg + folder or vice versa. Having the old finders cached may then cause Python + to attempt loading modules from the replacement distribution using an + incorrect loader. + + zipimport.zipimporter objects are Python loaders charged with importing + data packaged inside zip archives. If stale loaders referencing the + original distribution, are left behind, they can fail to load modules from + the replacement distribution. E.g. if an old zipimport.zipimporter instance + is used to load data from a new zipped egg archive, it may cause the + operation to attempt to locate the requested data in the wrong location - + one indicated by the original distribution's zip archive directory + information. Such an operation may then fail outright, e.g. report having + read a 'bad local file header', or even worse, it may fail silently & + return invalid data. + + zipimport._zip_directory_cache contains cached zip archive directory + information for all existing zipimport.zipimporter instances and all such + instances connected to the same archive share the same cached directory + information. + + If asked, and the underlying Python implementation allows it, we can fix + all existing zipimport.zipimporter instances instead of having to track + them down and remove them one by one, by updating their shared cached zip + archive directory information. This, of course, assumes that the + replacement distribution is packaged as a zipped egg. + + If not asked to fix existing zipimport.zipimporter instances, we still do + our best to clear any remaining zipimport.zipimporter related cached data + that might somehow later get used when attempting to load data from the new + distribution and thus cause such load operations to fail. Note that when + tracking down such remaining stale data, we can not catch every conceivable + usage from here, and we clear only those that we know of and have found to + cause problems if left alive. Any remaining caches should be updated by + whomever is in charge of maintaining them, i.e. they should be ready to + handle us replacing their zip archives with new distributions at runtime. + + """ + # There are several other known sources of stale zipimport.zipimporter + # instances that we do not clear here, but might if ever given a reason to + # do so: + # * Global setuptools pkg_resources.working_set (a.k.a. 'master working + # set') may contain distributions which may in turn contain their + # zipimport.zipimporter loaders. + # * Several zipimport.zipimporter loaders held by local variables further + # up the function call stack when running the setuptools installation. + # * Already loaded modules may have their __loader__ attribute set to the + # exact loader instance used when importing them. Python 3.4 docs state + # that this information is intended mostly for introspection and so is + # not expected to cause us problems. + normalized_path = normalize_path(dist_path) + _uncache(normalized_path, sys.path_importer_cache) + if fix_zipimporter_caches: + _replace_zip_directory_cache_data(normalized_path) + else: + # Here, even though we do not want to fix existing and now stale + # zipimporter cache information, we still want to remove it. Related to + # Python's zip archive directory information cache, we clear each of + # its stale entries in two phases: + # 1. Clear the entry so attempting to access zip archive information + # via any existing stale zipimport.zipimporter instances fails. + # 2. Remove the entry from the cache so any newly constructed + # zipimport.zipimporter instances do not end up using old stale + # zip archive directory information. + # This whole stale data removal step does not seem strictly necessary, + # but has been left in because it was done before we started replacing + # the zip archive directory information cache content if possible, and + # there are no relevant unit tests that we can depend on to tell us if + # this is really needed. + _remove_and_clear_zip_directory_cache_data(normalized_path) + + +def _collect_zipimporter_cache_entries(normalized_path, cache): + """ + Return zipimporter cache entry keys related to a given normalized path. + + Alternative path spellings (e.g. those using different character case or + those using alternative path separators) related to the same path are + included. Any sub-path entries are included as well, i.e. those + corresponding to zip archives embedded in other zip archives. + + """ + result = [] + prefix_len = len(normalized_path) + for p in cache: + np = normalize_path(p) + if np.startswith(normalized_path) and np[prefix_len : prefix_len + 1] in ( + os.sep, + '', + ): + result.append(p) + return result + + +def _update_zipimporter_cache(normalized_path, cache, updater=None): + """ + Update zipimporter cache data for a given normalized path. + + Any sub-path entries are processed as well, i.e. those corresponding to zip + archives embedded in other zip archives. + + Given updater is a callable taking a cache entry key and the original entry + (after already removing the entry from the cache), and expected to update + the entry and possibly return a new one to be inserted in its place. + Returning None indicates that the entry should not be replaced with a new + one. If no updater is given, the cache entries are simply removed without + any additional processing, the same as if the updater simply returned None. + + """ + for p in _collect_zipimporter_cache_entries(normalized_path, cache): + # N.B. pypy's custom zipimport._zip_directory_cache implementation does + # not support the complete dict interface: + # * Does not support item assignment, thus not allowing this function + # to be used only for removing existing cache entries. + # * Does not support the dict.pop() method, forcing us to use the + # get/del patterns instead. For more detailed information see the + # following links: + # https://github.com/pypa/setuptools/issues/202#issuecomment-202913420 + # https://foss.heptapod.net/pypy/pypy/-/blob/144c4e65cb6accb8e592f3a7584ea38265d1873c/pypy/module/zipimport/interp_zipimport.py + 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 open(executable, encoding='latin-1') as fp: + magic = fp.read(2) + except OSError: + 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: object, **kwargs: object) -> None: # type: ignore[misc] # Mypy re-uses the imported definition anyway + pass + + +def chmod(path, mode): + log.debug("changing mode of %s to %o", path, mode) + try: + _chmod(path, mode) + except OSError 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: List[str] = [] + split_args: Dict[str, bool] = 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 + import re + import sys + + # for compatibility with easy_install; see #2198 + __requires__ = %(spec)r + + try: + from importlib.metadata import distribution + except ImportError: + try: + from importlib_metadata import distribution + except ImportError: + from pkg_resources import load_entry_point + + + def importlib_load_entry_point(spec, group, name): + dist_name, _, _ = spec.partition('==') + matches = ( + entry_point + for entry_point in distribution(dist_name).entry_points + if entry_point.group == group and entry_point.name == name + ) + return next(matches).load() + + + globals().setdefault('load_entry_point', importlib_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_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) + yield from args + + @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 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 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()) + SetuptoolsWarning.emit(msg) + 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 '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') + + +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(): + if get_platform() == "win-arm64": + launcher_fn = launcher_fn.replace(".", "-arm64.") + else: + 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') + return manifest.decode('utf-8') % vars() + + +def _rmtree(path, ignore_errors=False, onexc=auto_chmod): + return py311.shutil_rmtree(path, ignore_errors, onexc) + + +def current_umask(): + tmp = os.umask(0o022) + os.umask(tmp) + return tmp + + +def only_strs(values): + """ + Exclude non-str values. Ref #3063. + """ + return filter(lambda val: isinstance(val, str), values) + + +class EasyInstallDeprecationWarning(SetuptoolsDeprecationWarning): + _SUMMARY = "easy_install command is deprecated." + _DETAILS = """ + Please avoid running ``setup.py`` and ``easy_install``. + Instead, use pypa/build, pypa/installer or other + standards-based tools. + """ + _SEE_URL = "https://github.com/pypa/setuptools/issues/917" + # _DUE_DATE not defined yet diff --git a/venv/Lib/site-packages/setuptools/command/editable_wheel.py b/venv/Lib/site-packages/setuptools/command/editable_wheel.py new file mode 100644 index 0000000..4d21e22 --- /dev/null +++ b/venv/Lib/site-packages/setuptools/command/editable_wheel.py @@ -0,0 +1,906 @@ +""" +Create a wheel that, when installed, will make the source package 'editable' +(add it to the interpreter's path, including metadata) per PEP 660. Replaces +'setup.py develop'. + +.. note:: + One of the mechanisms briefly mentioned in PEP 660 to implement editable installs is + to create a separated directory inside ``build`` and use a .pth file to point to that + directory. In the context of this file such directory is referred as + *auxiliary build directory* or ``auxiliary_dir``. +""" + +import logging +import io +import os +import shutil +import traceback +from contextlib import suppress +from enum import Enum +from inspect import cleandoc +from itertools import chain, starmap +from pathlib import Path +from tempfile import TemporaryDirectory +from typing import ( + TYPE_CHECKING, + Dict, + Iterable, + Iterator, + List, + Mapping, + Optional, + Protocol, + Tuple, + TypeVar, + cast, +) + +from .. import ( + Command, + _normalization, + _path, + errors, + namespaces, +) +from .._path import StrPath +from ..compat import py39 +from ..discovery import find_package_path +from ..dist import Distribution +from ..warnings import ( + InformationOnly, + SetuptoolsDeprecationWarning, + SetuptoolsWarning, +) +from .build import build as build_cls +from .build_py import build_py as build_py_cls +from .dist_info import dist_info as dist_info_cls +from .egg_info import egg_info as egg_info_cls +from .install import install as install_cls +from .install_scripts import install_scripts as install_scripts_cls + +if TYPE_CHECKING: + from wheel.wheelfile import WheelFile # type:ignore[import-untyped] # noqa + +_P = TypeVar("_P", bound=StrPath) +_logger = logging.getLogger(__name__) + + +class _EditableMode(Enum): + """ + Possible editable installation modes: + `lenient` (new files automatically added to the package - DEFAULT); + `strict` (requires a new installation when files are added/removed); or + `compat` (attempts to emulate `python setup.py develop` - DEPRECATED). + """ + + STRICT = "strict" + LENIENT = "lenient" + COMPAT = "compat" # TODO: Remove `compat` after Dec/2022. + + @classmethod + def convert(cls, mode: Optional[str]) -> "_EditableMode": + if not mode: + return _EditableMode.LENIENT # default + + _mode = mode.upper() + if _mode not in _EditableMode.__members__: + raise errors.OptionError(f"Invalid editable mode: {mode!r}. Try: 'strict'.") + + if _mode == "COMPAT": + SetuptoolsDeprecationWarning.emit( + "Compat editable installs", + """ + The 'compat' editable mode is transitional and will be removed + in future versions of `setuptools`. + Please adapt your code accordingly to use either the 'strict' or the + 'lenient' modes. + """, + see_docs="userguide/development_mode.html", + # TODO: define due_date + # There is a series of shortcomings with the available editable install + # methods, and they are very controversial. This is something that still + # needs work. + # Moreover, `pip` is still hiding this warning, so users are not aware. + ) + + return _EditableMode[_mode] + + +_STRICT_WARNING = """ +New or renamed files may not be automatically picked up without a new installation. +""" + +_LENIENT_WARNING = """ +Options like `package-data`, `include/exclude-package-data` or +`packages.find.exclude/include` may have no effect. +""" + + +class editable_wheel(Command): + """Build 'editable' wheel for development. + This command is private and reserved for internal use of setuptools, + users should rely on ``setuptools.build_meta`` APIs. + """ + + description = "DO NOT CALL DIRECTLY, INTERNAL ONLY: create PEP 660 editable wheel" + + user_options = [ + ("dist-dir=", "d", "directory to put final built distributions in"), + ("dist-info-dir=", "I", "path to a pre-build .dist-info directory"), + ("mode=", None, cleandoc(_EditableMode.__doc__ or "")), + ] + + def initialize_options(self): + self.dist_dir = None + self.dist_info_dir = None + self.project_dir = None + self.mode = None + + def finalize_options(self): + dist = self.distribution + self.project_dir = dist.src_root or os.curdir + self.package_dir = dist.package_dir or {} + self.dist_dir = Path(self.dist_dir or os.path.join(self.project_dir, "dist")) + + def run(self): + try: + self.dist_dir.mkdir(exist_ok=True) + self._ensure_dist_info() + + # Add missing dist_info files + self.reinitialize_command("bdist_wheel") + bdist_wheel = self.get_finalized_command("bdist_wheel") + bdist_wheel.write_wheelfile(self.dist_info_dir) + + self._create_wheel_file(bdist_wheel) + except Exception: + traceback.print_exc() + project = self.distribution.name or self.distribution.get_name() + _DebuggingTips.emit(project=project) + raise + + def _ensure_dist_info(self): + if self.dist_info_dir is None: + dist_info = cast(dist_info_cls, self.reinitialize_command("dist_info")) + dist_info.output_dir = self.dist_dir + dist_info.ensure_finalized() + dist_info.run() + self.dist_info_dir = dist_info.dist_info_dir + else: + assert str(self.dist_info_dir).endswith(".dist-info") + assert Path(self.dist_info_dir, "METADATA").exists() + + def _install_namespaces(self, installation_dir, pth_prefix): + # XXX: Only required to support the deprecated namespace practice + dist = self.distribution + if not dist.namespace_packages: + return + + src_root = Path(self.project_dir, self.package_dir.get("", ".")).resolve() + installer = _NamespaceInstaller(dist, installation_dir, pth_prefix, src_root) + installer.install_namespaces() + + def _find_egg_info_dir(self) -> Optional[str]: + parent_dir = Path(self.dist_info_dir).parent if self.dist_info_dir else Path() + candidates = map(str, parent_dir.glob("*.egg-info")) + return next(candidates, None) + + def _configure_build( + self, name: str, unpacked_wheel: StrPath, build_lib: StrPath, tmp_dir: StrPath + ): + """Configure commands to behave in the following ways: + + - Build commands can write to ``build_lib`` if they really want to... + (but this folder is expected to be ignored and modules are expected to live + in the project directory...) + - Binary extensions should be built in-place (editable_mode = True) + - Data/header/script files are not part of the "editable" specification + so they are written directly to the unpacked_wheel directory. + """ + # Non-editable files (data, headers, scripts) are written directly to the + # unpacked_wheel + + dist = self.distribution + wheel = str(unpacked_wheel) + build_lib = str(build_lib) + data = str(Path(unpacked_wheel, f"{name}.data", "data")) + headers = str(Path(unpacked_wheel, f"{name}.data", "headers")) + scripts = str(Path(unpacked_wheel, f"{name}.data", "scripts")) + + # egg-info may be generated again to create a manifest (used for package data) + egg_info = cast( + egg_info_cls, dist.reinitialize_command("egg_info", reinit_subcommands=True) + ) + egg_info.egg_base = str(tmp_dir) + egg_info.ignore_egg_info_in_manifest = True + + build = cast( + build_cls, dist.reinitialize_command("build", reinit_subcommands=True) + ) + install = cast( + install_cls, dist.reinitialize_command("install", reinit_subcommands=True) + ) + + build.build_platlib = build.build_purelib = build.build_lib = build_lib + install.install_purelib = install.install_platlib = install.install_lib = wheel + install.install_scripts = build.build_scripts = scripts + install.install_headers = headers + install.install_data = data + + install_scripts = cast( + install_scripts_cls, dist.get_command_obj("install_scripts") + ) + install_scripts.no_ep = True + + build.build_temp = str(tmp_dir) + + build_py = cast(build_py_cls, dist.get_command_obj("build_py")) + build_py.compile = False + build_py.existing_egg_info_dir = self._find_egg_info_dir() + + self._set_editable_mode() + + build.ensure_finalized() + install.ensure_finalized() + + def _set_editable_mode(self): + """Set the ``editable_mode`` flag in the build sub-commands""" + dist = self.distribution + build = dist.get_command_obj("build") + # TODO: Update typeshed distutils stubs to overload non-None return type by default + for cmd_name in build.get_sub_commands(): + cmd = dist.get_command_obj(cmd_name) + if hasattr(cmd, "editable_mode"): + cmd.editable_mode = True + elif hasattr(cmd, "inplace"): + cmd.inplace = True # backward compatibility with distutils + + def _collect_build_outputs(self) -> Tuple[List[str], Dict[str, str]]: + files: List[str] = [] + mapping: Dict[str, str] = {} + build = self.get_finalized_command("build") + + for cmd_name in build.get_sub_commands(): + cmd = self.get_finalized_command(cmd_name) + if hasattr(cmd, "get_outputs"): + files.extend(cmd.get_outputs() or []) + if hasattr(cmd, "get_output_mapping"): + mapping.update(cmd.get_output_mapping() or {}) + + return files, mapping + + def _run_build_commands( + self, + dist_name: str, + unpacked_wheel: StrPath, + build_lib: StrPath, + tmp_dir: StrPath, + ) -> Tuple[List[str], Dict[str, str]]: + self._configure_build(dist_name, unpacked_wheel, build_lib, tmp_dir) + self._run_build_subcommands() + files, mapping = self._collect_build_outputs() + self._run_install("headers") + self._run_install("scripts") + self._run_install("data") + return files, mapping + + def _run_build_subcommands(self) -> None: + """ + Issue #3501 indicates that some plugins/customizations might rely on: + + 1. ``build_py`` not running + 2. ``build_py`` always copying files to ``build_lib`` + + However both these assumptions may be false in editable_wheel. + This method implements a temporary workaround to support the ecosystem + while the implementations catch up. + """ + # TODO: Once plugins/customisations had the chance to catch up, replace + # `self._run_build_subcommands()` with `self.run_command("build")`. + # Also remove _safely_run, TestCustomBuildPy. Suggested date: Aug/2023. + build = self.get_finalized_command("build") + for name in build.get_sub_commands(): + cmd = self.get_finalized_command(name) + if name == "build_py" and type(cmd) != build_py_cls: + self._safely_run(name) + else: + self.run_command(name) + + def _safely_run(self, cmd_name: str): + try: + return self.run_command(cmd_name) + except Exception: + SetuptoolsDeprecationWarning.emit( + "Customization incompatible with editable install", + f""" + {traceback.format_exc()} + + If you are seeing this warning it is very likely that a setuptools + plugin or customization overrides the `{cmd_name}` command, without + taking into consideration how editable installs run build steps + starting from setuptools v64.0.0. + + Plugin authors and developers relying on custom build steps are + encouraged to update their `{cmd_name}` implementation considering the + information about editable installs in + https://setuptools.pypa.io/en/latest/userguide/extension.html. + + For the time being `setuptools` will silence this error and ignore + the faulty command, but this behaviour will change in future versions. + """, + # TODO: define due_date + # There is a series of shortcomings with the available editable install + # methods, and they are very controversial. This is something that still + # needs work. + ) + + def _create_wheel_file(self, bdist_wheel): + from wheel.wheelfile import WheelFile + + dist_info = self.get_finalized_command("dist_info") + dist_name = dist_info.name + tag = "-".join(bdist_wheel.get_tag()) + build_tag = "0.editable" # According to PEP 427 needs to start with digit + archive_name = f"{dist_name}-{build_tag}-{tag}.whl" + wheel_path = Path(self.dist_dir, archive_name) + if wheel_path.exists(): + wheel_path.unlink() + + unpacked_wheel = TemporaryDirectory(suffix=archive_name) + build_lib = TemporaryDirectory(suffix=".build-lib") + build_tmp = TemporaryDirectory(suffix=".build-temp") + + with unpacked_wheel as unpacked, build_lib as lib, build_tmp as tmp: + unpacked_dist_info = Path(unpacked, Path(self.dist_info_dir).name) + shutil.copytree(self.dist_info_dir, unpacked_dist_info) + self._install_namespaces(unpacked, dist_name) + files, mapping = self._run_build_commands(dist_name, unpacked, lib, tmp) + strategy = self._select_strategy(dist_name, tag, lib) + with strategy, WheelFile(wheel_path, "w") as wheel_obj: + strategy(wheel_obj, files, mapping) + wheel_obj.write_files(unpacked) + + return wheel_path + + def _run_install(self, category: str): + has_category = getattr(self.distribution, f"has_{category}", None) + if has_category and has_category(): + _logger.info(f"Installing {category} as non editable") + self.run_command(f"install_{category}") + + def _select_strategy( + self, + name: str, + tag: str, + build_lib: StrPath, + ) -> "EditableStrategy": + """Decides which strategy to use to implement an editable installation.""" + build_name = f"__editable__.{name}-{tag}" + project_dir = Path(self.project_dir) + mode = _EditableMode.convert(self.mode) + + if mode is _EditableMode.STRICT: + auxiliary_dir = _empty_dir(Path(self.project_dir, "build", build_name)) + return _LinkTree(self.distribution, name, auxiliary_dir, build_lib) + + packages = _find_packages(self.distribution) + has_simple_layout = _simple_layout(packages, self.package_dir, project_dir) + is_compat_mode = mode is _EditableMode.COMPAT + if set(self.package_dir) == {""} and has_simple_layout or is_compat_mode: + # src-layout(ish) is relatively safe for a simple pth file + src_dir = self.package_dir.get("", ".") + return _StaticPth(self.distribution, name, [Path(project_dir, src_dir)]) + + # Use a MetaPathFinder to avoid adding accidental top-level packages/modules + return _TopLevelFinder(self.distribution, name) + + +class EditableStrategy(Protocol): + def __call__( + self, wheel: "WheelFile", files: List[str], mapping: Dict[str, str] + ): ... + + def __enter__(self): ... + + def __exit__(self, _exc_type, _exc_value, _traceback): ... + + +class _StaticPth: + def __init__(self, dist: Distribution, name: str, path_entries: List[Path]): + self.dist = dist + self.name = name + self.path_entries = path_entries + + def __call__(self, wheel: "WheelFile", files: List[str], mapping: Dict[str, str]): + entries = "\n".join(str(p.resolve()) for p in self.path_entries) + contents = _encode_pth(f"{entries}\n") + wheel.writestr(f"__editable__.{self.name}.pth", contents) + + def __enter__(self): + msg = f""" + Editable install will be performed using .pth file to extend `sys.path` with: + {list(map(os.fspath, self.path_entries))!r} + """ + _logger.warning(msg + _LENIENT_WARNING) + return self + + def __exit__(self, _exc_type, _exc_value, _traceback): ... + + +class _LinkTree(_StaticPth): + """ + Creates a ``.pth`` file that points to a link tree in the ``auxiliary_dir``. + + This strategy will only link files (not dirs), so it can be implemented in + any OS, even if that means using hardlinks instead of symlinks. + + By collocating ``auxiliary_dir`` and the original source code, limitations + with hardlinks should be avoided. + """ + + def __init__( + self, + dist: Distribution, + name: str, + auxiliary_dir: StrPath, + build_lib: StrPath, + ): + self.auxiliary_dir = Path(auxiliary_dir) + self.build_lib = Path(build_lib).resolve() + # TODO: Update typeshed distutils stubs to overload non-None return type by default + self._file = dist.get_command_obj("build_py").copy_file # type: ignore[union-attr] + super().__init__(dist, name, [self.auxiliary_dir]) + + def __call__(self, wheel: "WheelFile", files: List[str], mapping: Dict[str, str]): + self._create_links(files, mapping) + super().__call__(wheel, files, mapping) + + def _normalize_output(self, file: str) -> Optional[str]: + # Files relative to build_lib will be normalized to None + with suppress(ValueError): + path = Path(file).resolve().relative_to(self.build_lib) + return str(path).replace(os.sep, '/') + return None + + def _create_file(self, relative_output: str, src_file: str, link=None): + dest = self.auxiliary_dir / relative_output + if not dest.parent.is_dir(): + dest.parent.mkdir(parents=True) + # TODO: Update typeshed distutils stubs so distutils.cmd.Command.copy_file, accepts PathLike + # same with methods used by copy_file + self._file(src_file, dest, link=link) # type: ignore[arg-type] + + def _create_links(self, outputs, output_mapping): + self.auxiliary_dir.mkdir(parents=True, exist_ok=True) + link_type = "sym" if _can_symlink_files(self.auxiliary_dir) else "hard" + mappings = {self._normalize_output(k): v for k, v in output_mapping.items()} + mappings.pop(None, None) # remove files that are not relative to build_lib + + for output in outputs: + relative = self._normalize_output(output) + if relative and relative not in mappings: + self._create_file(relative, output) + + for relative, src in mappings.items(): + self._create_file(relative, src, link=link_type) + + def __enter__(self): + msg = "Strict editable install will be performed using a link tree.\n" + _logger.warning(msg + _STRICT_WARNING) + return self + + def __exit__(self, _exc_type, _exc_value, _traceback): + msg = f"""\n + Strict editable installation performed using the auxiliary directory: + {self.auxiliary_dir} + + Please be careful to not remove this directory, otherwise you might not be able + to import/use your package. + """ + InformationOnly.emit("Editable installation.", msg) + + +class _TopLevelFinder: + def __init__(self, dist: Distribution, name: str): + self.dist = dist + self.name = name + + def __call__(self, wheel: "WheelFile", files: List[str], mapping: Dict[str, str]): + src_root = self.dist.src_root or os.curdir + top_level = chain(_find_packages(self.dist), _find_top_level_modules(self.dist)) + package_dir = self.dist.package_dir or {} + roots = _find_package_roots(top_level, package_dir, src_root) + + namespaces_: Dict[str, List[str]] = dict( + chain( + _find_namespaces(self.dist.packages or [], roots), + ((ns, []) for ns in _find_virtual_namespaces(roots)), + ) + ) + + legacy_namespaces = { + pkg: find_package_path(pkg, roots, self.dist.src_root or "") + for pkg in self.dist.namespace_packages or [] + } + + mapping = {**roots, **legacy_namespaces} + # ^-- We need to explicitly add the legacy_namespaces to the mapping to be + # able to import their modules even if another package sharing the same + # namespace is installed in a conventional (non-editable) way. + + name = f"__editable__.{self.name}.finder" + finder = _normalization.safe_identifier(name) + content = bytes(_finder_template(name, mapping, namespaces_), "utf-8") + wheel.writestr(f"{finder}.py", content) + + content = _encode_pth(f"import {finder}; {finder}.install()") + wheel.writestr(f"__editable__.{self.name}.pth", content) + + def __enter__(self): + msg = "Editable install will be performed using a meta path finder.\n" + _logger.warning(msg + _LENIENT_WARNING) + return self + + def __exit__(self, _exc_type, _exc_value, _traceback): + msg = """\n + Please be careful with folders in your working directory with the same + name as your package as they may take precedence during imports. + """ + InformationOnly.emit("Editable installation.", msg) + + +def _encode_pth(content: str) -> bytes: + """.pth files are always read with 'locale' encoding, the recommendation + from the cpython core developers is to write them as ``open(path, "w")`` + and ignore warnings (see python/cpython#77102, pypa/setuptools#3937). + This function tries to simulate this behaviour without having to create an + actual file, in a way that supports a range of active Python versions. + (There seems to be some variety in the way different version of Python handle + ``encoding=None``, not all of them use ``locale.getpreferredencoding(False)``). + """ + with io.BytesIO() as buffer: + wrapper = io.TextIOWrapper(buffer, encoding=py39.LOCALE_ENCODING) + wrapper.write(content) + wrapper.flush() + buffer.seek(0) + return buffer.read() + + +def _can_symlink_files(base_dir: Path) -> bool: + with TemporaryDirectory(dir=str(base_dir.resolve())) as tmp: + path1, path2 = Path(tmp, "file1.txt"), Path(tmp, "file2.txt") + path1.write_text("file1", encoding="utf-8") + with suppress(AttributeError, NotImplementedError, OSError): + os.symlink(path1, path2) + if path2.is_symlink() and path2.read_text(encoding="utf-8") == "file1": + return True + + try: + os.link(path1, path2) # Ensure hard links can be created + except Exception as ex: + msg = ( + "File system does not seem to support either symlinks or hard links. " + "Strict editable installs require one of them to be supported." + ) + raise LinksNotSupported(msg) from ex + return False + + +def _simple_layout( + packages: Iterable[str], package_dir: Dict[str, str], project_dir: StrPath +) -> bool: + """Return ``True`` if: + - all packages are contained by the same parent directory, **and** + - all packages become importable if the parent directory is added to ``sys.path``. + + >>> _simple_layout(['a'], {"": "src"}, "/tmp/myproj") + True + >>> _simple_layout(['a', 'a.b'], {"": "src"}, "/tmp/myproj") + True + >>> _simple_layout(['a', 'a.b'], {}, "/tmp/myproj") + True + >>> _simple_layout(['a', 'a.a1', 'a.a1.a2', 'b'], {"": "src"}, "/tmp/myproj") + True + >>> _simple_layout(['a', 'a.a1', 'a.a1.a2', 'b'], {"a": "a", "b": "b"}, ".") + True + >>> _simple_layout(['a', 'a.a1', 'a.a1.a2', 'b'], {"a": "_a", "b": "_b"}, ".") + False + >>> _simple_layout(['a', 'a.a1', 'a.a1.a2', 'b'], {"a": "_a"}, "/tmp/myproj") + False + >>> _simple_layout(['a', 'a.a1', 'a.a1.a2', 'b'], {"a.a1.a2": "_a2"}, ".") + False + >>> _simple_layout(['a', 'a.b'], {"": "src", "a.b": "_ab"}, "/tmp/myproj") + False + >>> # Special cases, no packages yet: + >>> _simple_layout([], {"": "src"}, "/tmp/myproj") + True + >>> _simple_layout([], {"a": "_a", "": "src"}, "/tmp/myproj") + False + """ + layout = {pkg: find_package_path(pkg, package_dir, project_dir) for pkg in packages} + if not layout: + return set(package_dir) in ({}, {""}) + parent = os.path.commonpath(starmap(_parent_path, layout.items())) + return all( + _path.same_path(Path(parent, *key.split('.')), value) + for key, value in layout.items() + ) + + +def _parent_path(pkg, pkg_path): + """Infer the parent path containing a package, that if added to ``sys.path`` would + allow importing that package. + When ``pkg`` is directly mapped into a directory with a different name, return its + own path. + >>> _parent_path("a", "src/a") + 'src' + >>> _parent_path("b", "src/c") + 'src/c' + """ + parent = pkg_path[: -len(pkg)] if pkg_path.endswith(pkg) else pkg_path + return parent.rstrip("/" + os.sep) + + +def _find_packages(dist: Distribution) -> Iterator[str]: + yield from iter(dist.packages or []) + + py_modules = dist.py_modules or [] + nested_modules = [mod for mod in py_modules if "." in mod] + if dist.ext_package: + yield dist.ext_package + else: + ext_modules = dist.ext_modules or [] + nested_modules += [x.name for x in ext_modules if "." in x.name] + + for module in nested_modules: + package, _, _ = module.rpartition(".") + yield package + + +def _find_top_level_modules(dist: Distribution) -> Iterator[str]: + py_modules = dist.py_modules or [] + yield from (mod for mod in py_modules if "." not in mod) + + if not dist.ext_package: + ext_modules = dist.ext_modules or [] + yield from (x.name for x in ext_modules if "." not in x.name) + + +def _find_package_roots( + packages: Iterable[str], + package_dir: Mapping[str, str], + src_root: StrPath, +) -> Dict[str, str]: + pkg_roots: Dict[str, str] = { + pkg: _absolute_root(find_package_path(pkg, package_dir, src_root)) + for pkg in sorted(packages) + } + + return _remove_nested(pkg_roots) + + +def _absolute_root(path: StrPath) -> str: + """Works for packages and top-level modules""" + path_ = Path(path) + parent = path_.parent + + if path_.exists(): + return str(path_.resolve()) + else: + return str(parent.resolve() / path_.name) + + +def _find_virtual_namespaces(pkg_roots: Dict[str, str]) -> Iterator[str]: + """By carefully designing ``package_dir``, it is possible to implement the logical + structure of PEP 420 in a package without the corresponding directories. + + Moreover a parent package can be purposefully/accidentally skipped in the discovery + phase (e.g. ``find_packages(include=["mypkg.*"])``, when ``mypkg.foo`` is included + by ``mypkg`` itself is not). + We consider this case to also be a virtual namespace (ignoring the original + directory) to emulate a non-editable installation. + + This function will try to find these kinds of namespaces. + """ + for pkg in pkg_roots: + if "." not in pkg: + continue + parts = pkg.split(".") + for i in range(len(parts) - 1, 0, -1): + partial_name = ".".join(parts[:i]) + path = Path(find_package_path(partial_name, pkg_roots, "")) + if not path.exists() or partial_name not in pkg_roots: + # partial_name not in pkg_roots ==> purposefully/accidentally skipped + yield partial_name + + +def _find_namespaces( + packages: List[str], pkg_roots: Dict[str, str] +) -> Iterator[Tuple[str, List[str]]]: + for pkg in packages: + path = find_package_path(pkg, pkg_roots, "") + if Path(path).exists() and not Path(path, "__init__.py").exists(): + yield (pkg, [path]) + + +def _remove_nested(pkg_roots: Dict[str, str]) -> Dict[str, str]: + output = dict(pkg_roots.copy()) + + for pkg, path in reversed(list(pkg_roots.items())): + if any( + pkg != other and _is_nested(pkg, path, other, other_path) + for other, other_path in pkg_roots.items() + ): + output.pop(pkg) + + return output + + +def _is_nested(pkg: str, pkg_path: str, parent: str, parent_path: str) -> bool: + """ + Return ``True`` if ``pkg`` is nested inside ``parent`` both logically and in the + file system. + >>> _is_nested("a.b", "path/a/b", "a", "path/a") + True + >>> _is_nested("a.b", "path/a/b", "a", "otherpath/a") + False + >>> _is_nested("a.b", "path/a/b", "c", "path/c") + False + >>> _is_nested("a.a", "path/a/a", "a", "path/a") + True + >>> _is_nested("b.a", "path/b/a", "a", "path/a") + False + """ + norm_pkg_path = _path.normpath(pkg_path) + rest = pkg.replace(parent, "", 1).strip(".").split(".") + return pkg.startswith(parent) and norm_pkg_path == _path.normpath( + Path(parent_path, *rest) + ) + + +def _empty_dir(dir_: _P) -> _P: + """Create a directory ensured to be empty. Existing files may be removed.""" + shutil.rmtree(dir_, ignore_errors=True) + os.makedirs(dir_) + return dir_ + + +class _NamespaceInstaller(namespaces.Installer): + def __init__(self, distribution, installation_dir, editable_name, src_root): + self.distribution = distribution + self.src_root = src_root + self.installation_dir = installation_dir + self.editable_name = editable_name + self.outputs = [] + self.dry_run = False + + def _get_nspkg_file(self): + """Installation target.""" + return os.path.join(self.installation_dir, self.editable_name + self.nspkg_ext) + + def _get_root(self): + """Where the modules/packages should be loaded from.""" + return repr(str(self.src_root)) + + +_FINDER_TEMPLATE = """\ +import sys +from importlib.machinery import ModuleSpec, PathFinder +from importlib.machinery import all_suffixes as module_suffixes +from importlib.util import spec_from_file_location +from itertools import chain +from pathlib import Path + +MAPPING = {mapping!r} +NAMESPACES = {namespaces!r} +PATH_PLACEHOLDER = {name!r} + ".__path_hook__" + + +class _EditableFinder: # MetaPathFinder + @classmethod + def find_spec(cls, fullname, path=None, target=None): + extra_path = [] + + # Top-level packages and modules (we know these exist in the FS) + if fullname in MAPPING: + pkg_path = MAPPING[fullname] + return cls._find_spec(fullname, Path(pkg_path)) + + # Handle immediate children modules (required for namespaces to work) + # To avoid problems with case sensitivity in the file system we delegate + # to the importlib.machinery implementation. + parent, _, child = fullname.rpartition(".") + if parent and parent in MAPPING: + return PathFinder.find_spec(fullname, path=[MAPPING[parent], *extra_path]) + + # Other levels of nesting should be handled automatically by importlib + # using the parent path. + return None + + @classmethod + def _find_spec(cls, fullname, candidate_path): + init = candidate_path / "__init__.py" + candidates = (candidate_path.with_suffix(x) for x in module_suffixes()) + for candidate in chain([init], candidates): + if candidate.exists(): + return spec_from_file_location(fullname, candidate) + + +class _EditableNamespaceFinder: # PathEntryFinder + @classmethod + def _path_hook(cls, path): + if path == PATH_PLACEHOLDER: + return cls + raise ImportError + + @classmethod + def _paths(cls, fullname): + # Ensure __path__ is not empty for the spec to be considered a namespace. + return NAMESPACES[fullname] or MAPPING.get(fullname) or [PATH_PLACEHOLDER] + + @classmethod + def find_spec(cls, fullname, target=None): + if fullname in NAMESPACES: + spec = ModuleSpec(fullname, None, is_package=True) + spec.submodule_search_locations = cls._paths(fullname) + return spec + return None + + @classmethod + def find_module(cls, fullname): + return None + + +def install(): + if not any(finder == _EditableFinder for finder in sys.meta_path): + sys.meta_path.append(_EditableFinder) + + if not NAMESPACES: + return + + if not any(hook == _EditableNamespaceFinder._path_hook for hook in sys.path_hooks): + # PathEntryFinder is needed to create NamespaceSpec without private APIS + sys.path_hooks.append(_EditableNamespaceFinder._path_hook) + if PATH_PLACEHOLDER not in sys.path: + sys.path.append(PATH_PLACEHOLDER) # Used just to trigger the path hook +""" + + +def _finder_template( + name: str, mapping: Mapping[str, str], namespaces: Dict[str, List[str]] +) -> str: + """Create a string containing the code for the``MetaPathFinder`` and + ``PathEntryFinder``. + """ + mapping = dict(sorted(mapping.items(), key=lambda p: p[0])) + return _FINDER_TEMPLATE.format(name=name, mapping=mapping, namespaces=namespaces) + + +class LinksNotSupported(errors.FileError): + """File system does not seem to support either symlinks or hard links.""" + + +class _DebuggingTips(SetuptoolsWarning): + _SUMMARY = "Problem in editable installation." + _DETAILS = """ + An error happened while installing `{project}` in editable mode. + + The following steps are recommended to help debug this problem: + + - Try to install the project normally, without using the editable mode. + Does the error still persist? + (If it does, try fixing the problem before attempting the editable mode). + - If you are using binary extensions, make sure you have all OS-level + dependencies installed (e.g. compilers, toolchains, binary libraries, ...). + - Try the latest version of setuptools (maybe the error was already fixed). + - If you (or your project dependencies) are using any setuptools extension + or customization, make sure they support the editable mode. + + After following the steps above, if the problem still persists and + you think this is related to how setuptools handles editable installations, + please submit a reproducible example + (see https://stackoverflow.com/help/minimal-reproducible-example) to: + + https://github.com/pypa/setuptools/issues + """ + _SEE_DOCS = "userguide/development_mode.html" diff --git a/venv/Lib/site-packages/setuptools/command/egg_info.py b/venv/Lib/site-packages/setuptools/command/egg_info.py new file mode 100644 index 0000000..62d2fee --- /dev/null +++ b/venv/Lib/site-packages/setuptools/command/egg_info.py @@ -0,0 +1,737 @@ +"""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 functools +import os +import re +import sys +import time +import collections + +from .._importlib import metadata +from .. import _entry_points, _normalization +from . import _requirestxt + +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 +import setuptools.unicode_utils as unicode_utils +from setuptools.glob import glob + +from setuptools.extern import packaging +from ..warnings import SetuptoolsDeprecationWarning + + +PY_MAJOR = '{}.{}'.format(*sys.version_info) + + +def translate_pattern(glob): # noqa: C901 # is too complex (14) # FIXME + """ + 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 _normalization.safe_name(self.distribution.get_name()) + + def tagged_version(self): + tagged = self._maybe_tag(self.distribution.get_version()) + return _normalization.safe_version(tagged) + + def _maybe_tag(self, version): + """ + egg_info may be called more than once for a distribution, + in which case the version string already contains all tags. + """ + return ( + version + if self.vtags and self._already_tagged(version) + else version + self.vtags + ) + + def _already_tagged(self, version: str) -> bool: + # Depending on their format, tags may change with version normalization. + # So in addition the regular tags, we have to search for the normalized ones. + return version.endswith(self.vtags) or version.endswith(self._safe_tags()) + + def _safe_tags(self) -> str: + # To implement this we can rely on `safe_version` pretending to be version 0 + # followed by tags. Then we simply discard the starting 0 (fake version number) + try: + return _normalization.safe_version(f"0{self.vtags}")[1:] + except packaging.version.InvalidVersion: + return _normalization.safe_name(self.vtags.replace(' ', '.')) + + def tags(self) -> str: + 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.ignore_egg_info_in_manifest = 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 = packaging.version.Version(self.egg_version) + + try: + is_version = isinstance(parsed_version, packaging.version.Version) + spec = "%s==%s" if is_version else "%s===%s" + packaging.requirements.Requirement(spec % (self.egg_name, self.egg_version)) + except ValueError as e: + raise distutils.errors.DistutilsOptionError( + "Invalid distribution name or version syntax: %s-%s" + % (self.egg_name, self.egg_version) + ) from e + + 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 = _normalization.filename_component(self.egg_name) + '.egg-info' + if self.egg_base != os.curdir: + self.egg_info = os.path.join(self.egg_base, self.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 + key = getattr(pd, "key", None) or getattr(pd, "name", None) + if pd is not None and key == self.egg_name.lower(): + pd._version = self.egg_version + pd._parsed_version = packaging.version.Version(self.egg_version) + self.distribution._patched_dist = None + + def _get_egg_basename(self, py_version=PY_MAJOR, platform=None): + """Compute filename of the output egg. Private API.""" + return _egg_basename(self.egg_name, self.egg_version, py_version, platform) + + 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) + 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) + try: + os.utime(self.egg_info, None) + except OSError as e: + msg = f"Cannot update time stamp of directory '{self.egg_info}'" + raise distutils.errors.DistutilsFileError(msg) from e + for ep in metadata.entry_points(group='egg_info.writers'): + writer = ep.load() + 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.ignore_egg_info_dir = self.ignore_egg_info_in_manifest + mm.manifest = manifest_filename + mm.run() + self.filelist = mm.filelist + + +class FileList(_FileList): + # Implementations of the various MANIFEST.in commands + + def __init__(self, warn=None, debug_print=None, ignore_egg_info_dir=False): + super().__init__(warn, debug_print) + self.ignore_egg_info_dir = ignore_egg_info_dir + + 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) + + action_map = { + 'include': self.include, + 'exclude': self.exclude, + 'global-include': self.global_include, + 'global-exclude': self.global_exclude, + 'recursive-include': functools.partial( + self.recursive_include, + dir, + ), + 'recursive-exclude': functools.partial( + self.recursive_exclude, + dir, + ), + 'graft': self.graft, + 'prune': self.prune, + } + log_map = { + 'include': "warning: no files found matching '%s'", + 'exclude': ("warning: no previously-included files found " "matching '%s'"), + 'global-include': ( + "warning: no files found matching '%s' " "anywhere in distribution" + ), + 'global-exclude': ( + "warning: no previously-included files matching " + "'%s' found anywhere in distribution" + ), + 'recursive-include': ( + "warning: no files found matching '%s' " "under directory '%s'" + ), + 'recursive-exclude': ( + "warning: no previously-included files matching " + "'%s' found under directory '%s'" + ), + 'graft': "warning: no directories found matching '%s'", + 'prune': "no previously-included directories found matching '%s'", + } + + try: + process_action = action_map[action] + except KeyError: + msg = f"Invalid MANIFEST.in: unknown action {action!r} in {line!r}" + raise DistutilsInternalError(msg) from None + + # 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. + + action_is_recursive = action.startswith('recursive-') + if action in {'graft', 'prune'}: + patterns = [dir_pattern] + extra_log_args = (dir,) if action_is_recursive else () + log_tmpl = log_map[action] + + self.debug_print( + ' '.join( + [action] + ([dir] if action_is_recursive else []) + patterns, + ) + ) + for pattern in patterns: + if not process_action(pattern): + log.warn(log_tmpl, pattern, *extra_log_args) + + 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: + # ignore egg-info paths + is_egg_info = ".egg-info" in u_path or b".egg-info" in utf8_path + if self.ignore_egg_info_dir and is_egg_info: + return False + # 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 + self.ignore_egg_info_dir = False + + def finalize_options(self): + pass + + def run(self): + self.filelist = FileList(ignore_egg_info_dir=self.ignore_egg_info_dir) + 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.add_license_files() + self._add_referenced_files() + 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.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 add_license_files(self): + license_files = self.distribution.metadata.license_files or [] + for lf in license_files: + log.info("adding license file '%s'", lf) + self.filelist.extend(license_files) + + def _add_referenced_files(self): + """Add files referenced by the config (e.g. `file:` directive) to filelist""" + referenced = getattr(self.distribution, '_referenced_files', []) + # ^-- fallback if dist comes from distutils or is a custom class + for rf in referenced: + log.debug("adding file referenced by config '%s'", rf) + self.filelist.extend(referenced) + + 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 _safe_data_files(self, build_py): + """ + The parent class implementation of this method + (``sdist``) will try to include data files, which + might cause recursion problems when + ``include_package_data=True``. + + Therefore, avoid triggering any attempt of + analyzing/building the manifest again. + """ + if hasattr(build_py, 'get_data_files_without_manifest'): + return build_py.get_data_files_without_manifest() + + SetuptoolsDeprecationWarning.emit( + "`build_py` command does not inherit from setuptools' `build_py`.", + """ + Custom 'build_py' does not implement 'get_data_files_without_manifest'. + Please extend command classes from setuptools instead of distutils. + """, + see_url="https://peps.python.org/pep-0632/", + # due_date not defined yet, old projects might still do it? + ) + return build_py.get_data_files() + + +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): + """ + Unused: left to avoid errors when updating (from source) from <= 67.8. + Old installations have a .dist-info directory with the entry-point + ``depends.txt = setuptools.command.egg_info:warn_depends_obsolete``. + This may trigger errors when running the first egg_info in build_meta. + TODO: Remove this function in a version sufficiently > 68. + """ + + +# Export API used in entry_points +write_requirements = _requirestxt.write_requirements +write_setup_requirements = _requirestxt.write_setup_requirements + + +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): + eps = _entry_points.load(cmd.distribution.entry_points) + defn = _entry_points.render(eps) + cmd.write_or_delete_file('entry points', filename, defn, True) + + +def _egg_basename(egg_name, egg_version, py_version=None, platform=None): + """Compute filename of the output egg. Private API.""" + name = _normalization.filename_component(egg_name) + version = _normalization.filename_component(egg_version) + egg = f"{name}-{version}-py{py_version or PY_MAJOR}" + if platform: + egg += f"-{platform}" + return egg + + +class EggInfoDeprecationWarning(SetuptoolsDeprecationWarning): + """Deprecated behavior warning for EggInfo, bypassing suppression.""" diff --git a/venv/Lib/site-packages/setuptools/command/install.py b/venv/Lib/site-packages/setuptools/command/install.py new file mode 100644 index 0000000..56c1155 --- /dev/null +++ b/venv/Lib/site-packages/setuptools/command/install.py @@ -0,0 +1,155 @@ +from distutils.errors import DistutilsArgError +import inspect +import glob +import platform +import distutils.command.install as orig +from typing import cast + +import setuptools +from ..warnings import SetuptoolsDeprecationWarning, SetuptoolsWarning +from .bdist_egg import bdist_egg as bdist_egg_cls + +# 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): + SetuptoolsDeprecationWarning.emit( + "setup.py install is deprecated.", + """ + Please avoid running ``setup.py`` directly. + Instead, use pypa/build, pypa/installer or other + standards-based tools. + """, + see_url="https://blog.ganssle.io/articles/2021/10/setup-py-deprecated.html", + # TODO: Document how to bootstrap setuptools without install + # (e.g. by unziping the wheel file) + # and then add a due_date to this warning. + ) + + 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 = '' + return None + + 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() + + return None + + @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." + SetuptoolsWarning.emit(msg) + if platform.python_implementation() == 'IronPython': + msg = "For best results, pass -X:Frames to enable call stack." + SetuptoolsWarning.emit(msg) + return True + + frames = inspect.getouterframes(run_frame) + for frame in frames[2:4]: + (caller,) = frame[:1] + info = inspect.getframeinfo(caller) + caller_module = caller.f_globals.get('__name__', '') + + if caller_module == "setuptools.dist" and info.function == "run_command": + # Starting from v61.0.0 setuptools overwrites dist.run_command + continue + + return caller_module == 'distutils.dist' and info.function == 'run_commands' + + return False + + 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') + bdist_egg = cast(bdist_egg_cls, self.distribution.get_command_obj('bdist_egg')) + args = [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/site-packages/setuptools/command/install_egg_info.py b/venv/Lib/site-packages/setuptools/command/install_egg_info.py new file mode 100644 index 0000000..a1d2e81 --- /dev/null +++ b/venv/Lib/site-packages/setuptools/command/install_egg_info.py @@ -0,0 +1,57 @@ +from distutils import log, dir_util +import os + +from setuptools import Command +from setuptools import namespaces +from setuptools.archive_util import unpack_archive +from .._path import ensure_directory + + +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 = f"{ei_cmd._get_egg_basename()}.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: + 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/site-packages/setuptools/command/install_lib.py b/venv/Lib/site-packages/setuptools/command/install_lib.py new file mode 100644 index 0000000..32ff65e --- /dev/null +++ b/venv/Lib/site-packages/setuptools/command/install_lib.py @@ -0,0 +1,125 @@ +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/site-packages/setuptools/command/install_scripts.py b/venv/Lib/site-packages/setuptools/command/install_scripts.py new file mode 100644 index 0000000..72b2e45 --- /dev/null +++ b/venv/Lib/site-packages/setuptools/command/install_scripts.py @@ -0,0 +1,66 @@ +from distutils import log +import distutils.command.install_scripts as orig +import os +import sys + +from .._path import 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): + 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 + self._install_ep_scripts() + + def _install_ep_scripts(self): + # Delay import side-effects + from pkg_resources import Distribution, PathMetadata + from . import easy_install as ei + + 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) + writer = ei.ScriptWriter + 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/site-packages/setuptools/command/launcher manifest.xml b/venv/Lib/site-packages/setuptools/command/launcher manifest.xml new file mode 100644 index 0000000..5972a96 --- /dev/null +++ b/venv/Lib/site-packages/setuptools/command/launcher manifest.xml @@ -0,0 +1,15 @@ + + + + + + + + + + + + diff --git a/venv/Lib/site-packages/setuptools/command/register.py b/venv/Lib/site-packages/setuptools/command/register.py new file mode 100644 index 0000000..b8266b9 --- /dev/null +++ b/venv/Lib/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/site-packages/setuptools/command/rotate.py b/venv/Lib/site-packages/setuptools/command/rotate.py new file mode 100644 index 0000000..6f73721 --- /dev/null +++ b/venv/Lib/site-packages/setuptools/command/rotate.py @@ -0,0 +1,63 @@ +from distutils.util import convert_path +from distutils import log +from distutils.errors import DistutilsOptionError +import os +import shutil +from typing import List + +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: List[str] = [] + + 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 as e: + raise DistutilsOptionError("--keep must be an integer") from e + if isinstance(self.match, str): + 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/site-packages/setuptools/command/saveopts.py b/venv/Lib/site-packages/setuptools/command/saveopts.py new file mode 100644 index 0000000..f175de1 --- /dev/null +++ b/venv/Lib/site-packages/setuptools/command/saveopts.py @@ -0,0 +1,21 @@ +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/site-packages/setuptools/command/sdist.py b/venv/Lib/site-packages/setuptools/command/sdist.py new file mode 100644 index 0000000..d455f44 --- /dev/null +++ b/venv/Lib/site-packages/setuptools/command/sdist.py @@ -0,0 +1,204 @@ +from distutils import log +import distutils.command.sdist as orig +import os +import contextlib +from itertools import chain + +from .._importlib import metadata +from .build import _ORIGINAL_SUBCOMMANDS + +_default_revctrl = list + + +def walk_revctrl(dirname=''): + """Find all files under revision control""" + for ep in metadata.entry_points(group='setuptools.file_finders'): + yield from ep.load()(dirname) + + +class sdist(orig.sdist): + """Smart sdist that finds anything supported by revision control""" + + user_options = [ + ('formats=', None, "formats for source distribution (comma-separated list)"), + ( + 'keep-temp', + 'k', + "keep the distribution tree around after creating " + "archive file(s)", + ), + ( + 'dist-dir=', + 'd', + "directory to put the source distribution archive(s) in " "[default: dist]", + ), + ( + 'owner=', + 'u', + "Owner name used when creating a tar file [default: current user]", + ), + ( + 'group=', + 'g', + "Group name used when creating a tar file [default: current group]", + ), + ] + + 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) + + 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: + os.link = orig_val + + def add_defaults(self): + super().add_defaults() + self._add_defaults_build_sub_commands() + + def _add_defaults_optional(self): + 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 _add_defaults_build_sub_commands(self): + build = self.get_finalized_command("build") + missing_cmds = set(build.get_sub_commands()) - _ORIGINAL_SUBCOMMANDS + # ^-- the original built-in sub-commands are already handled by default. + cmds = (self.get_finalized_command(c) for c in missing_cmds) + files = (c.get_source_files() for c in cmds if hasattr(c, "get_source_files")) + self.filelist.extend(chain.from_iterable(files)) + + def _safe_data_files(self, build_py): + """ + Since the ``sdist`` class is also used to compute the MANIFEST + (via :obj:`setuptools.command.egg_info.manifest_maker`), + there might be recursion problems when trying to obtain the list of + data_files and ``include_package_data=True`` (which in turn depends on + the files included in the MANIFEST). + + To avoid that, ``manifest_maker`` should be able to overwrite this + method and avoid recursive attempts to build/analyze the MANIFEST. + """ + 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: + 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 open(self.manifest, 'rb') as fp: + first_line = fp.readline() + return first_line != b'# file GENERATED by distutils, do NOT edit\n' + + 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. + try: + line = line.decode('UTF-8') + except UnicodeDecodeError: + log.warn("%r not UTF-8 decodable -- skipping" % line) + continue + # ignore comments and blank lines + line = line.strip() + if line.startswith('#') or not line: + continue + self.filelist.append(line) + manifest.close() diff --git a/venv/Lib/site-packages/setuptools/command/setopt.py b/venv/Lib/site-packages/setuptools/command/setopt.py new file mode 100644 index 0000000..f9a6075 --- /dev/null +++ b/venv/Lib/site-packages/setuptools/command/setopt.py @@ -0,0 +1,138 @@ +from distutils.util import convert_path +from distutils import log +from distutils.errors import DistutilsOptionError +import distutils +import os +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.optionxform = lambda x: x + 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/site-packages/setuptools/command/test.py b/venv/Lib/site-packages/setuptools/command/test.py new file mode 100644 index 0000000..0a128f2 --- /dev/null +++ b/venv/Lib/site-packages/setuptools/command/test.py @@ -0,0 +1,250 @@ +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 pkg_resources import ( + resource_listdir, + resource_exists, + normalize_path, + working_set, + evaluate_marker, + add_activation_listener, + require, +) +from .._importlib import metadata +from setuptools import Command +from setuptools.extern.more_itertools import unique_everseen +from setuptools.extern.jaraco.functools import pass_none + + +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: + 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=()): + 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): + 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 + @pass_none + def _resolve_as_ep(val): + """ + Load the indicated attribute value, called, as a as if it were + specified as an entry point. + """ + return metadata.EntryPoint(value=val, name=None, group=None).load()() diff --git a/venv/Lib/site-packages/setuptools/command/upload.py b/venv/Lib/site-packages/setuptools/command/upload.py new file mode 100644 index 0000000..ec7f81e --- /dev/null +++ b/venv/Lib/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/site-packages/setuptools/command/upload_docs.py b/venv/Lib/site-packages/setuptools/command/upload_docs.py new file mode 100644 index 0000000..3fbbb62 --- /dev/null +++ b/venv/Lib/site-packages/setuptools/command/upload_docs.py @@ -0,0 +1,221 @@ +"""upload_docs + +Implements a Distutils 'upload_docs' subcommand (upload documentation to +sites other than PyPi such as devpi). +""" + +from base64 import standard_b64encode +from distutils import log +from distutils.errors import DistutilsOptionError +import os +import zipfile +import tempfile +import shutil +import itertools +import functools +import http.client +import urllib.parse + +from .._importlib import metadata +from ..warnings import SetuptoolsDeprecationWarning + +from .upload import upload + + +def _encode(s): + return s.encode('utf-8', 'surrogateescape') + + +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 sites other than PyPi such as devpi' + + 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): + return bool( + self.upload_dir is None + and metadata.entry_points(group='distutils.commands', name='build_sphinx') + ) + + sub_commands = [('build_sphinx', has_sphinx)] # type: ignore[list-item] # TODO: Fix in typeshed distutils stubs + + def initialize_options(self): + upload.initialize_options(self) + self.upload_dir = None + self.target_dir = None + + def finalize_options(self): + log.warn( + "Upload_docs command is deprecated. Use Read the Docs " + "(https://readthedocs.org) instead." + ) + 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 = dict(build_sphinx.builder_target_dirs)['html'] + else: + build = self.get_finalized_command('build') + self.target_dir = os.path.join(build.build_base, 'docs') + else: + self.ensure_dirname('upload_dir') + self.target_dir = self.upload_dir + self.announce('Using upload directory %s' % self.target_dir) + + def create_zipfile(self, filename): + zip_file = zipfile.ZipFile(filename, "w") + try: + self.mkpath(self.target_dir) # just in case + for root, dirs, files in os.walk(self.target_dir): + if root == self.target_dir and not files: + 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): + SetuptoolsDeprecationWarning.emit( + "Deprecated command", + """ + upload_docs is deprecated and will be removed in a future version. + Instead, use tools like devpi and Read the Docs; or lower level tools like + httpie and curl to interact directly with your hosting service API. + """, + due_date=(2023, 9, 26), # warning introduced in 27 Jul 2022 + ) + + # 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).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 OSError 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/site-packages/setuptools/compat/__init__.py b/venv/Lib/site-packages/setuptools/compat/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/venv/Lib/site-packages/setuptools/compat/__pycache__/__init__.cpython-312.pyc b/venv/Lib/site-packages/setuptools/compat/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..4888c2773129cfad745eea17acc0faa4221933e7 GIT binary patch literal 203 zcmX@j%ge<81Vs;Z(?IlN5P=Rpvj9b=GgLBYGWxA#C}INgK7-W!%5t`f2`x@7Dh87= zF8Rr&xv6<2#WBwLDVcfcF#(y$C8b5Fx&ftCRXM4;<*7-Vu^lyd}dx|NqoFsLFF$Fo80`A(wtPgB37W) Sj6hrrVtiy~WMnL22C@Jszd5e} literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/setuptools/compat/__pycache__/py310.cpython-312.pyc b/venv/Lib/site-packages/setuptools/compat/__pycache__/py310.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..27ac199e4a4fb837e898660a39fde64d1874a166 GIT binary patch literal 389 zcmYLCze~eF6u#>vjnr1q;_50v()wc)aj7_oba4p>Ax*B~>?IfOF0JY0=;-EO;Nail zq>B(;L=dtSx^?nWEPe34_q~trd+)W?Y5-m0+ZjIV`BN>cs{NDsr6x}x1R-R=1z|0M zE+pXASQ*zdL?f)PnM*HnQ4TdTm=T*Uf*^;sSytxj!cshDj1KHtiL_8|YOrBpRx-8g z1(LZeEYI^9^Sq)tCPGr4cr+PuQ3Z-^NmQ09&YA3xJ4Hky>RNvRbB$mT^-l4P)Tt%n z3m#-KNtDDr9?~SjeHy4t5T~C_r;IogGLTdeJSNE)U(o@sHYfFikslF>*T1>}k5gZD z)BNPAz+WQB^sA}*4 literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/setuptools/compat/__pycache__/py311.cpython-312.pyc b/venv/Lib/site-packages/setuptools/compat/__pycache__/py311.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..1760df2ef5edbed118861bd8bb58a5132d36d29e GIT binary patch literal 833 zcmah{&ubGw6rP#gWMi5b)M^w&gSV|DrFameNrj3Bkx=|`2@5gZog~|CcH_)$YC-}% z6f~!LP!eywiamJrfAA8uAZ~jRPq_)nL3;AdCZYD^gL&`Gy!qby-kbRxk4F&*`ko0*WQGr9^+4;je77Aj0EE?`44$PO-$CRXL4xSH3cDMFYKEU_QG0risfkPCJq z8?(%_1F#0()PTojSKtEK2+zC|+eU5NCHv&Vw9;erij;xLPNXCKAK8}nMiaeg1VAm(Iy7I0%!l^Bl#hW5WPpL?31$X%k`35 z^%Y2I+GU4{R9ps?N~N}sE30?3r<^h#XziY(do^miTw8Sv)2?W1rtW%-rq{ey%cAKm zy3S3PY7J^Pw1?)pCU)t1N#870DAzc3y}Ii-7GKnz8q{5^H*YO1W$H~oQmmG2!=kKp zomV~Aw2G|eGDsf%>zJekA5!KkCo-xAvaDYkf;B3?d67h=?gE zFeSzu23<79KigzS+N~Pivf}cvtVZ-FdfLV6pIJf zPEllnjQ9^c2MSmnDbepr@{5w}-~OuHI7(dH?Q}c+CwtjXp@T$j5Xp^Y9J)CUquBY~ zY&Q%1J0GhDb1MURg(V<`O77=XiJb$NiB}B@Fwu7q4mcb~mTQjTSv1Fz5D+iQ1>m=Y RF+N1mLlhr}6?_*5kiP>#!x8`h literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/setuptools/compat/__pycache__/py39.cpython-312.pyc b/venv/Lib/site-packages/setuptools/compat/__pycache__/py39.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..531813a175ae454262690163af0cc03e032d7da0 GIT binary patch literal 332 zcmX@j%ge<81Vs;Z)7~;NFgylvV1NnA_-q1XOlL@8NMX!jh+;@($b!khR08P~rtBD? zIm{`{QB0MLDU2XeljS8yg`XzVOJ*Sb5@gjaww(Or#GF)3=3C6gmBqJs%2JDpGxPJ} zGxO5&Z}I#1J3IQg#=H7C`@4AhxvymS3^L(YnzL0*XfaSFn2d4BPcF?(%_}L6an4W4 z%uA06$V@INElSl5D6Oi>N!2Y+O)AbTNsTE>%`1!X$xMm?@pKCkld}`kQ;TDYQ%gz< zO7in_iuIH8a|;qn^b0DDE%gd2e{tC4=BJeAq}mnn0IdZE2t%;~kodsN$jEq`f$M_+ egS^spIlYT=dK=U)$yrX&Xyo5f(#T%~@)-d2Ut9A4 literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/setuptools/compat/py310.py b/venv/Lib/site-packages/setuptools/compat/py310.py new file mode 100644 index 0000000..f7d53d6 --- /dev/null +++ b/venv/Lib/site-packages/setuptools/compat/py310.py @@ -0,0 +1,10 @@ +import sys + + +__all__ = ['tomllib'] + + +if sys.version_info >= (3, 11): + import tomllib +else: # pragma: no cover + from setuptools.extern import tomli as tomllib diff --git a/venv/Lib/site-packages/setuptools/compat/py311.py b/venv/Lib/site-packages/setuptools/compat/py311.py new file mode 100644 index 0000000..28175b1 --- /dev/null +++ b/venv/Lib/site-packages/setuptools/compat/py311.py @@ -0,0 +1,12 @@ +import sys +import shutil + + +def shutil_rmtree(path, ignore_errors=False, onexc=None): + if sys.version_info >= (3, 12): + return shutil.rmtree(path, ignore_errors, onexc=onexc) + + def _handler(fn, path, excinfo): + return onexc(fn, path, excinfo[1]) + + return shutil.rmtree(path, ignore_errors, onerror=_handler) diff --git a/venv/Lib/site-packages/setuptools/compat/py39.py b/venv/Lib/site-packages/setuptools/compat/py39.py new file mode 100644 index 0000000..04a4abe --- /dev/null +++ b/venv/Lib/site-packages/setuptools/compat/py39.py @@ -0,0 +1,9 @@ +import sys + +# Explicitly use the ``"locale"`` encoding in versions that support it, +# otherwise just rely on the implicit handling of ``encoding=None``. +# Since all platforms that support ``EncodingWarning`` also support +# ``encoding="locale"``, this can be used to suppress the warning. +# However, please try to use UTF-8 when possible +# (.pth files are the notorious exception: python/cpython#77102, pypa/setuptools#3937). +LOCALE_ENCODING = "locale" if sys.version_info >= (3, 10) else None diff --git a/venv/Lib/site-packages/setuptools/config/__init__.py b/venv/Lib/site-packages/setuptools/config/__init__.py new file mode 100644 index 0000000..fcc7d00 --- /dev/null +++ b/venv/Lib/site-packages/setuptools/config/__init__.py @@ -0,0 +1,43 @@ +"""For backward compatibility, expose main functions from +``setuptools.config.setupcfg`` +""" + +from functools import wraps +from typing import Callable, TypeVar, cast + +from ..warnings import SetuptoolsDeprecationWarning +from . import setupcfg + +Fn = TypeVar("Fn", bound=Callable) + +__all__ = ('parse_configuration', 'read_configuration') + + +def _deprecation_notice(fn: Fn) -> Fn: + @wraps(fn) + def _wrapper(*args, **kwargs): + SetuptoolsDeprecationWarning.emit( + "Deprecated API usage.", + f""" + As setuptools moves its configuration towards `pyproject.toml`, + `{__name__}.{fn.__name__}` became deprecated. + + For the time being, you can use the `{setupcfg.__name__}` module + to access a backward compatible API, but this module is provisional + and might be removed in the future. + + To read project metadata, consider using + ``build.util.project_wheel_metadata`` (https://pypi.org/project/build/). + For simple scenarios, you can also try parsing the file directly + with the help of ``configparser``. + """, + # due_date not defined yet, because the community still heavily relies on it + # Warning introduced in 24 Mar 2022 + ) + return fn(*args, **kwargs) + + return cast(Fn, _wrapper) + + +read_configuration = _deprecation_notice(setupcfg.read_configuration) +parse_configuration = _deprecation_notice(setupcfg.parse_configuration) diff --git a/venv/Lib/site-packages/setuptools/config/__pycache__/__init__.cpython-312.pyc b/venv/Lib/site-packages/setuptools/config/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..cd38cda2d5c7cf2c71e3bcffd93fe455e13bba49 GIT binary patch literal 2028 zcmZ`)-)kI29G~6WyKFAGBu!|nw$&l6(hJEQ#-dP4p(agLq!d~!9tXR7J9C$b`^%ZR z#5*bkESQ2w9~2`h`rw07AN)UjNvIHA6)o0>zFoNr(kH*OcboL84(!bA%xAvyneX>A z-}$~!$RjBH_I&;4EJA;%pR^eLPWLG`^s|2am{hxTz5FZZsp1$*Dsg8Pn@Qoa3++58S@96jP+an1QeR3RLewAm!fdb(Fen z6V)}MT{CY2MwNFosn#S#rb0khwKRN}P*K0&rU=z49X$nK_kE~7(jMMswI6es*egm& z`-VS7By(L~-P>pnhG52!x%B^k5ZTX%QesML*Y&H20okv#4>TZ}yU;$EI-U}I_TtO9 zDVzo^wJUl2P&q5`e&Demu2F%R6rda>fxhdp43(S&wxd?W!&U0ak_>&%o*S@bKhR3; z^ER$Qw0(+6Kkk&2@&h;}QMp2~WPqws5X2mAg-z@_0g!}on6sVFAYwK!B2AADESDj6 zT$c)goqsFcqw1`4xYm?lhl#!c3_W1J#ssL)@doTU0l_|NtVrO3IaNL&7;cE7QE!5@ z=p+0uhX8YkGN@dEeJUN|NM}xYO)x?^90N`@u#0Win#?1mret2JztUP?q13a|^|p;q ztVkJ&Q{^%^nw3J{DEFb|WOcb%8ZfFnDVQI@sf9}ehqF-Z`_l15h$U}fb(?@s??ep1 zgu#uJUTeVEI+H62WQBSW4(r&qdn!mSBe(6ry^97nL87rieI{emvH}RYWpQ;UAVC-o zZ-_Xj@=!zYaTpZpa$&sjV0H0S^)*4cNb2fR=r(;CNKsu33DB%wWUli1{KaOw?a}#l zS`$pt>KYB!s;{zIRsGFJkmEqlqPj2Na*uA=vRJ^RWtE~9@KPBoqP+dIl?oChU#T~QwLBx+R_F|m7AEdz4-Dt;9Y+(BH%@Mk&ul%vGd{Cz&UC>t zc!w)jaCO7^h#Jrn2l2#%Oas`S@Cf40;qhSu!(aLBo{Qe%10cY z#U8)dgaE3XEqc*LNqiI5$`m{T5OT$ckEyBv)i)^FEIGjg^K|b9pW(+~N+n_8Le?`bo27op&zm{8~X407dD1ExyQ68w_fZZXue$N rs^;EFG+q4q&~9;Qr?_l-Jm2g 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 L|B}O<@yh=T@IaI1 literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/setuptools/config/__pycache__/expand.cpython-312.pyc b/venv/Lib/site-packages/setuptools/config/__pycache__/expand.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..d00cf0a11168e32284b91d711c1250b7d6521193 GIT binary patch literal 25014 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 q(yCPBw!7GHji "Distribution": + """Apply configuration dict read with :func:`read_configuration`""" + + if not config: + return dist # short-circuit unrelated pyproject.toml file + + root_dir = os.path.dirname(filename) or "." + + _apply_project_table(dist, config, root_dir) + _apply_tool_table(dist, config, filename) + + current_directory = os.getcwd() + os.chdir(root_dir) + try: + dist._finalize_requires() + dist._finalize_license_files() + finally: + os.chdir(current_directory) + + return dist + + +def _apply_project_table(dist: "Distribution", config: dict, root_dir: StrPath): + project_table = config.get("project", {}).copy() + if not project_table: + return # short-circuit + + _handle_missing_dynamic(dist, project_table) + _unify_entry_points(project_table) + + for field, value in project_table.items(): + norm_key = json_compatible_key(field) + corresp = PYPROJECT_CORRESPONDENCE.get(norm_key, norm_key) + if callable(corresp): + corresp(dist, value, root_dir) + else: + _set_config(dist, corresp, value) + + +def _apply_tool_table(dist: "Distribution", config: dict, filename: StrPath): + tool_table = config.get("tool", {}).get("setuptools", {}) + if not tool_table: + return # short-circuit + + for field, value in tool_table.items(): + norm_key = json_compatible_key(field) + + if norm_key in TOOL_TABLE_REMOVALS: + suggestion = cleandoc(TOOL_TABLE_REMOVALS[norm_key]) + msg = f""" + The parameter `tool.setuptools.{field}` was long deprecated + and has been removed from `pyproject.toml`. + """ + raise RemovedConfigError("\n".join([cleandoc(msg), suggestion])) + + norm_key = TOOL_TABLE_RENAMES.get(norm_key, norm_key) + _set_config(dist, norm_key, value) + + _copy_command_options(config, dist, filename) + + +def _handle_missing_dynamic(dist: "Distribution", project_table: dict): + """Be temporarily forgiving with ``dynamic`` fields not listed in ``dynamic``""" + dynamic = set(project_table.get("dynamic", [])) + for field, getter in _PREVIOUSLY_DEFINED.items(): + if not (field in project_table or field in dynamic): + value = getter(dist) + if value: + _MissingDynamic.emit(field=field, value=value) + project_table[field] = _RESET_PREVIOUSLY_DEFINED.get(field) + + +def json_compatible_key(key: str) -> str: + """As defined in :pep:`566#json-compatible-metadata`""" + return key.lower().replace("-", "_") + + +def _set_config(dist: "Distribution", field: str, value: Any): + setter = getattr(dist.metadata, f"set_{field}", None) + if setter: + setter(value) + elif hasattr(dist.metadata, field) or field in SETUPTOOLS_PATCHES: + setattr(dist.metadata, field, value) + else: + setattr(dist, field, value) + + +_CONTENT_TYPES = { + ".md": "text/markdown", + ".rst": "text/x-rst", + ".txt": "text/plain", +} + + +def _guess_content_type(file: str) -> Optional[str]: + _, ext = os.path.splitext(file.lower()) + if not ext: + return None + + if ext in _CONTENT_TYPES: + return _CONTENT_TYPES[ext] + + valid = ", ".join(f"{k} ({v})" for k, v in _CONTENT_TYPES.items()) + msg = f"only the following file extensions are recognized: {valid}." + raise ValueError(f"Undefined content type for {file}, {msg}") + + +def _long_description(dist: "Distribution", val: _DictOrStr, root_dir: StrPath): + from setuptools.config import expand + + if isinstance(val, str): + file: Union[str, list] = val + text = expand.read_files(file, root_dir) + ctype = _guess_content_type(val) + else: + file = val.get("file") or [] + text = val.get("text") or expand.read_files(file, root_dir) + ctype = val["content-type"] + + _set_config(dist, "long_description", text) + + if ctype: + _set_config(dist, "long_description_content_type", ctype) + + if file: + dist._referenced_files.add(cast(str, file)) + + +def _license(dist: "Distribution", val: dict, root_dir: StrPath): + from setuptools.config import expand + + if "file" in val: + _set_config(dist, "license", expand.read_files([val["file"]], root_dir)) + dist._referenced_files.add(val["file"]) + else: + _set_config(dist, "license", val["text"]) + + +def _people(dist: "Distribution", val: List[dict], _root_dir: StrPath, kind: str): + field = [] + email_field = [] + for person in val: + if "name" not in person: + email_field.append(person["email"]) + elif "email" not in person: + field.append(person["name"]) + else: + addr = Address(display_name=person["name"], addr_spec=person["email"]) + email_field.append(str(addr)) + + if field: + _set_config(dist, kind, ", ".join(field)) + if email_field: + _set_config(dist, f"{kind}_email", ", ".join(email_field)) + + +def _project_urls(dist: "Distribution", val: dict, _root_dir): + _set_config(dist, "project_urls", val) + + +def _python_requires(dist: "Distribution", val: dict, _root_dir): + from setuptools.extern.packaging.specifiers import SpecifierSet + + _set_config(dist, "python_requires", SpecifierSet(val)) + + +def _dependencies(dist: "Distribution", val: list, _root_dir): + if getattr(dist, "install_requires", []): + msg = "`install_requires` overwritten in `pyproject.toml` (dependencies)" + SetuptoolsWarning.emit(msg) + dist.install_requires = val + + +def _optional_dependencies(dist: "Distribution", val: dict, _root_dir): + existing = getattr(dist, "extras_require", None) or {} + dist.extras_require = {**existing, **val} + + +def _unify_entry_points(project_table: dict): + project = project_table + entry_points = project.pop("entry-points", project.pop("entry_points", {})) + renaming = {"scripts": "console_scripts", "gui_scripts": "gui_scripts"} + for key, value in list(project.items()): # eager to allow modifications + norm_key = json_compatible_key(key) + if norm_key in renaming: + # Don't skip even if value is empty (reason: reset missing `dynamic`) + entry_points[renaming[norm_key]] = project.pop(key) + + if entry_points: + project["entry-points"] = { + name: [f"{k} = {v}" for k, v in group.items()] + for name, group in entry_points.items() + if group # now we can skip empty groups + } + # Sometimes this will set `project["entry-points"] = {}`, and that is + # intentional (for resetting configurations that are missing `dynamic`). + + +def _copy_command_options(pyproject: dict, dist: "Distribution", filename: StrPath): + tool_table = pyproject.get("tool", {}) + cmdclass = tool_table.get("setuptools", {}).get("cmdclass", {}) + valid_options = _valid_command_options(cmdclass) + + cmd_opts = dist.command_options + for cmd, config in pyproject.get("tool", {}).get("distutils", {}).items(): + cmd = json_compatible_key(cmd) + valid = valid_options.get(cmd, set()) + cmd_opts.setdefault(cmd, {}) + for key, value in config.items(): + key = json_compatible_key(key) + cmd_opts[cmd][key] = (str(filename), value) + if key not in valid: + # To avoid removing options that are specified dynamically we + # just log a warn... + _logger.warning(f"Command option {cmd}.{key} is not defined") + + +def _valid_command_options(cmdclass: Mapping = EMPTY) -> Dict[str, Set[str]]: + from .._importlib import metadata + from setuptools.dist import Distribution + + valid_options = {"global": _normalise_cmd_options(Distribution.global_options)} + + unloaded_entry_points = metadata.entry_points(group='distutils.commands') + loaded_entry_points = (_load_ep(ep) for ep in unloaded_entry_points) + entry_points = (ep for ep in loaded_entry_points if ep) + for cmd, cmd_class in chain(entry_points, cmdclass.items()): + opts = valid_options.get(cmd, set()) + opts = opts | _normalise_cmd_options(getattr(cmd_class, "user_options", [])) + valid_options[cmd] = opts + + return valid_options + + +def _load_ep(ep: "metadata.EntryPoint") -> Optional[Tuple[str, Type]]: + # Ignore all the errors + try: + return (ep.name, ep.load()) + except Exception as ex: + msg = f"{ex.__class__.__name__} while trying to load entry-point {ep.name}" + _logger.warning(f"{msg}: {ex}") + return None + + +def _normalise_cmd_option_key(name: str) -> str: + return json_compatible_key(name).strip("_=") + + +def _normalise_cmd_options(desc: "_OptionsList") -> Set[str]: + return {_normalise_cmd_option_key(fancy_option[0]) for fancy_option in desc} + + +def _get_previous_entrypoints(dist: "Distribution") -> Dict[str, list]: + ignore = ("console_scripts", "gui_scripts") + value = getattr(dist, "entry_points", None) or {} + return {k: v for k, v in value.items() if k not in ignore} + + +def _get_previous_scripts(dist: "Distribution") -> Optional[list]: + value = getattr(dist, "entry_points", None) or {} + return value.get("console_scripts") + + +def _get_previous_gui_scripts(dist: "Distribution") -> Optional[list]: + value = getattr(dist, "entry_points", None) or {} + return value.get("gui_scripts") + + +def _attrgetter(attr): + """ + Similar to ``operator.attrgetter`` but returns None if ``attr`` is not found + >>> from types import SimpleNamespace + >>> obj = SimpleNamespace(a=42, b=SimpleNamespace(c=13)) + >>> _attrgetter("a")(obj) + 42 + >>> _attrgetter("b.c")(obj) + 13 + >>> _attrgetter("d")(obj) is None + True + """ + return partial(reduce, lambda acc, x: getattr(acc, x, None), attr.split(".")) + + +def _some_attrgetter(*items): + """ + Return the first "truth-y" attribute or None + >>> from types import SimpleNamespace + >>> obj = SimpleNamespace(a=42, b=SimpleNamespace(c=13)) + >>> _some_attrgetter("d", "a", "b.c")(obj) + 42 + >>> _some_attrgetter("d", "e", "b.c", "a")(obj) + 13 + >>> _some_attrgetter("d", "e", "f")(obj) is None + True + """ + + def _acessor(obj): + values = (_attrgetter(i)(obj) for i in items) + return next((i for i in values if i is not None), None) + + return _acessor + + +PYPROJECT_CORRESPONDENCE: Dict[str, _Correspondence] = { + "readme": _long_description, + "license": _license, + "authors": partial(_people, kind="author"), + "maintainers": partial(_people, kind="maintainer"), + "urls": _project_urls, + "dependencies": _dependencies, + "optional_dependencies": _optional_dependencies, + "requires_python": _python_requires, +} + +TOOL_TABLE_RENAMES = {"script_files": "scripts"} +TOOL_TABLE_REMOVALS = { + "namespace_packages": """ + Please migrate to implicit native namespaces instead. + See https://packaging.python.org/en/latest/guides/packaging-namespace-packages/. + """, +} + +SETUPTOOLS_PATCHES = { + "long_description_content_type", + "project_urls", + "provides_extras", + "license_file", + "license_files", +} + +_PREVIOUSLY_DEFINED = { + "name": _attrgetter("metadata.name"), + "version": _attrgetter("metadata.version"), + "description": _attrgetter("metadata.description"), + "readme": _attrgetter("metadata.long_description"), + "requires-python": _some_attrgetter("python_requires", "metadata.python_requires"), + "license": _attrgetter("metadata.license"), + "authors": _some_attrgetter("metadata.author", "metadata.author_email"), + "maintainers": _some_attrgetter("metadata.maintainer", "metadata.maintainer_email"), + "keywords": _attrgetter("metadata.keywords"), + "classifiers": _attrgetter("metadata.classifiers"), + "urls": _attrgetter("metadata.project_urls"), + "entry-points": _get_previous_entrypoints, + "scripts": _get_previous_scripts, + "gui-scripts": _get_previous_gui_scripts, + "dependencies": _attrgetter("install_requires"), + "optional-dependencies": _attrgetter("extras_require"), +} + + +_RESET_PREVIOUSLY_DEFINED: dict = { + # Fix improper setting: given in `setup.py`, but not listed in `dynamic` + # dict: pyproject name => value to which reset + "license": {}, + "authors": [], + "maintainers": [], + "keywords": [], + "classifiers": [], + "urls": {}, + "entry-points": {}, + "scripts": {}, + "gui-scripts": {}, + "dependencies": [], + "optional-dependencies": {}, +} + + +class _MissingDynamic(SetuptoolsWarning): + _SUMMARY = "`{field}` defined outside of `pyproject.toml` is ignored." + + _DETAILS = """ + The following seems to be defined outside of `pyproject.toml`: + + `{field} = {value!r}` + + According to the spec (see the link below), however, setuptools CANNOT + consider this value unless `{field}` is listed as `dynamic`. + + https://packaging.python.org/en/latest/specifications/pyproject-toml/#declaring-project-metadata-the-project-table + + To prevent this problem, you can list `{field}` under `dynamic` or alternatively + remove the `[project]` table from your file and rely entirely on other means of + configuration. + """ + # TODO: Consider removing this check in the future? + # There is a trade-off here between improving "debug-ability" and the cost + # of running/testing/maintaining these unnecessary checks... + + @classmethod + def details(cls, field: str, value: Any) -> str: + return cls._DETAILS.format(field=field, value=value) diff --git a/venv/Lib/site-packages/setuptools/config/_validate_pyproject/__init__.py b/venv/Lib/site-packages/setuptools/config/_validate_pyproject/__init__.py new file mode 100644 index 0000000..dbe6cb4 --- /dev/null +++ b/venv/Lib/site-packages/setuptools/config/_validate_pyproject/__init__.py @@ -0,0 +1,34 @@ +from functools import reduce +from typing import Any, Callable, Dict + +from . import formats +from .error_reporting import detailed_errors, ValidationError +from .extra_validations import EXTRA_VALIDATIONS +from .fastjsonschema_exceptions import JsonSchemaException, JsonSchemaValueException +from .fastjsonschema_validations import validate as _validate + +__all__ = [ + "validate", + "FORMAT_FUNCTIONS", + "EXTRA_VALIDATIONS", + "ValidationError", + "JsonSchemaException", + "JsonSchemaValueException", +] + + +FORMAT_FUNCTIONS: Dict[str, Callable[[str], bool]] = { + fn.__name__.replace("_", "-"): fn + for fn in formats.__dict__.values() + if callable(fn) and not fn.__name__.startswith("_") +} + + +def validate(data: Any) -> bool: + """Validate the given ``data`` object using JSON Schema + This function raises ``ValidationError`` if ``data`` is invalid. + """ + with detailed_errors(): + _validate(data, custom_formats=FORMAT_FUNCTIONS) + reduce(lambda acc, fn: fn(acc), EXTRA_VALIDATIONS, data) + return True diff --git a/venv/Lib/site-packages/setuptools/config/_validate_pyproject/__pycache__/__init__.cpython-312.pyc b/venv/Lib/site-packages/setuptools/config/_validate_pyproject/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..acb421cdd8696d3fda7842e59af4b488f1faafaf GIT binary patch literal 1880 zcmZ`(U2GIp6h8B_JGIdjfEKXcDH z-?`tnw4?wR{jIZNxCEnUUp?q(G^eCRWGK;a8I#S zH|}Y=7V$AR;U)E?m(o*_9Cw?%X1zJ$HMhk}>uE2eXCgV_wt8)Py9AS9C%=^RZA6=X zrE!q0@ARu%e1p4PX9nR@uw)0{_fG;`1sTZp1-Ol87mFTSfa$Ip|16#Ec~+|NcBocS+0WKL1JV#QOWK}iDXOiE;Ol8vd3=jN6Bs>ik(KC zYG7f%?X!;>Y!ushayk3(n^E$2PeW_@~4Unzx;Lcl2VxxKucIX0R}N=KSyoI&M45 z0)NDsBA$6_)*@x$5boUC#4r`|RD)3xS89PtK2FnkjB!vj%N+JxIw zV2w5l(ju%RV^iv+8NsS}EK{V&E7Z5@JwpXR9A5?BjgOJs1pZXl==)KSfD4OgEdWOx z*VZ>uqaTPdSv*@ZZ=f4ddmVV9;e&{&$C6-9eHnUyNi*1u%lH=wZe(Yb%P=G5WAoSQ z#UwdCMRJqQ72@Z{$3+_BDPg@nn1J9^;eAudkFG@uuu?^pib|g1(7#5_hVHiQA z41W#SYQ?myP%imQ_~vq(*9P7!jxj=6M2mxgRq=?=S#cn+F_+>6gj$7?&I^_Kd6#s~ zkO}5+QpDu16wf&mMIm*TP3r@5lCUDfc9wY%xUAa>{E{=-Z8VT#l;_Gc5LN0n499o4 zVRV(}HWU4>=}p+?iTRF(Wmmr&Sfbmu)uxwL_aD8(SJnO>n=(%{++7!@n@s%vllEiEx3Pkx4d?fU$*9qy1kowN zIG-zHA*lcw?t7I}hjRVV~ zfi*~DV%am3*72NCy|z)5r~rG!-!;R)JH;@96l9lcx2bKNNt)G#%ZWdVAf#zxDhyISKugkwCQa@>#CHeZo^2slTZVjzvd#l;rJ4aTted{rZX&()JIJBnjsH!`b zmF1Cp>b{lUs(Rp&g06R=b!q<+TYh`RS{eIlaOJ&fw&(7So@#sV2B?y}p4Oz+r6cQr ZpXJvci_brgGU&&|jq=?K1?^;?`Y#3|+P(k) literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/setuptools/config/_validate_pyproject/__pycache__/error_reporting.cpython-312.pyc b/venv/Lib/site-packages/setuptools/config/_validate_pyproject/__pycache__/error_reporting.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..79ff2142f7582ff864b3572cf9ed0b87fd2284b0 GIT binary patch literal 17803 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 MozaOMG#q>S4@0r#X8-^I literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/setuptools/config/_validate_pyproject/__pycache__/fastjsonschema_exceptions.cpython-312.pyc b/venv/Lib/site-packages/setuptools/config/_validate_pyproject/__pycache__/fastjsonschema_exceptions.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..2ce992bd76d4d83d214bff4e801abe5efff5622e GIT binary patch literal 2857 zcmai0Uu+ab7@ytS`&U{IQ=mWs4u4EJpa<4QLSs~o>%wceoSj18sGD1|s$b=d$TJ0@$UTJb8s!HA_ zR_rRVG!~nWwWOMq#zC53S|p82X%eKVHPS>~$)^9tjYHGv(R?mHnjafGVm383Y3neu zycPJHVAP0WkrCBWMq+g}8w)zm3eO!j%dBFYxn#1cZ+q_ai8c!DO+zrsLLv1^v`{D+ z!k>U0LhdLOsACto!RtA>W9YsXq;%aiDooddHeIiHR?We2yRN@qGo02;Qr9id)b;yh z8LfOece+3So?u)^nji4YT7|j3$e;Es+a1rJw@tssSRCMoNmMUQLf^ERGJJhgH}{62NO~ z_~bQmNf}8ElWZc`x$2nM1SV!sY%0j zjH1IRT))b=Z!^)~CUwxm;MNKg0lyMQ_^*D8kF!jY*QZ5Na?idInJ^O}sAX!}OStyhRj zK%pThSZHu=)c;52Xg4bDK1#d8OXFDHyL)V{ZZvDE>;e1$5-S0`h$5UdRkqEtw0iZv zWiS9>Db<{k?L_8((Cyc&th=9fyR}M@aXhJFT(T>*3JCjpFRwkXP~ebuo9G9(6~U<& z(ZwOQElr>!^n>AAU@WT9*I69ykY39*LnxP{laRb{B@>)G&aD1^+M9c+FL%7D4Qk0L z%YyKP2!Mj(b8uJ`hRw=hLRpO(W)ptP6F@#!ZW2G5noTsw4Jf${WxiF}6c{&%9+ldL z3S5|vq!9xSvst!=23%OMNh1zibZ(v8SBA35ATDZg&eG9m*F121S> zMJ3P>CfKS8=9Kt0IErsavI9vMlAS<;w62?uA%w0AtSDGwDV^c%xS$ir^uEn9J{KCZ z2bPJKfjpTZzoj~6lMhlmZ*~2Y+PB!X_nLOSeR2D4=-L*y?SwA9xceY<8Ohy9%hF|r z?*Zr_h=jzs`E;$KJQ3lC%bKl0CL$a|86lw;2FRRZs^i2|$Ka{FtTYr$xp)GMRj#KS z%BSk}b`9n(E5kq)IhrC_btoY41f_=0pBsEvf9DKfUv+F>;+9qUE;uI8ptM&6dY4iO zRiI&>PVdY@?7h8r_I{~-o&GBQa9{t=nZaAzzOlY5emn7*_G4yn zAvO3fbRq?-@3!@6Uub>WI+PF#iPDEKxJdv9=$~!89pq6AuFeXjjypD(nTXiWxYLI=h3lD>2%0zoWQP2n>71*7T)(Z| z%iPT@c6KcSe`WT{FPZ%hGW&1$-8*^bgTa~xfVo9qUuyvZBQ#K_=MBpo;b5k;O{C6CmOcrtQnleh#gY)T zU66kWoO`_a?;>H7YSycMntHYQPxcx|C*$qKDIiNRMNxhwX+W-h$F<(M{@MOdoQGQu n%%oNlF(q~D@Ct!$rJX9dg%^5P2=ps&DIi)nc;YcmNdx}_3UtgA literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/setuptools/config/_validate_pyproject/__pycache__/fastjsonschema_validations.cpython-312.pyc b/venv/Lib/site-packages/setuptools/config/_validate_pyproject/__pycache__/fastjsonschema_validations.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..17b9d9df3450db8e873061f18042e0317b584647 GIT binary patch literal 173366 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

44uD&&fi7nSJ3$!9r)Aa4e5V_4qB0> zpP}=Aq4T@w{5^C)iM}EI@6c&rz+upVpMGmd%g}+Yg1`HX4s>io`b~^?0I5cFn$Wq0 zyk?{hp>r4=74rT*I$xkOj7|w1G&o5A19W~3oqveVDmrWEe2LCKLgzU;>*#z3ofqi* zIyxKZ{9|1~#OB8y)yH zLinzaU2+7S6X4`e^rhmyY$%zY$()&)%Vf{^gJ*o{v~Mxrycv@3-lUyNf-mdyc&f=f z3q@D@0_N4xmr5sd5h1uetEOr|{#2E$W>iUo`Q+cMRw5Nl-MN0h7jjEvBcVtz4OLD0 zGs$=`yCeVR%@C++HI`IDl|eL#WF_xjA(F0UmGno0fo#6DHZXs_*4HNq znpjgv?1=>zi}};Fk;MEG$?TaxGLiK~5@*sEQMNBY3oQBmol!x&Grk#+@eHN_dEc!5 z!!!BYbrbBod+7UbRI?Fh^1w?a+a?J#%c12YyDKL)QSd(W`y;1Qc1&ICsS$Ed+O#}XY{Y{5wX4$JlH$M?o;$? z{_);P_mhezdneylI?ncClne|)RX(;awC#1GEs7J8ipjs(hmy2ypR0&zOHEk*Y>h_}NlYqkaU_Wk_l+Xe zVKbp5Z72EyGeRKl%WSr%Qj4Z)2rT|mB&i0HU)&rnZ11z#IoPw{_#o7tB)a1tu0d=`|Eq> zBv!2Us8G}IQm*xzQkj1>XVHVX@(7W%Eq>6duJ+gL6fe z?ne3yI4_RT&gH3&s95U^7I5awH`0dz)8`;!-a*n274%GJv#E^7<5FHVXvYFO2xD+^L>5EfpkQ7wBx>Z4jFRiM(tTW{tsy2L`aa;xQ@s2hq!S4Lu6 zx&^5b+xS}svvRMOimsk0;i~RQhCBhdlxJ^91yn;Nfs%&0l8dKys3qR!P)i)DfFuzj z6Q=ZwO8Y8>-~}Fv5h8)Cv<1|Jz>1xzifJ_qT*&KiVIiu+#kzKjU0Rc|bo-_?Ec4>r zjtEeMr?zu$O41``0!Lh_<7-&ewn($4QfY{dwa#pBV})OjjmrGp7KfVhF=*k%o4aI*Q@=B#@OD0x1@ouT%&6WkwSJ}I;Em_ zhEzMc(nWP*P=8BvO}u^A6iPz0dnoq)1+i6ivaTJ3O7H9v3;XZ%p7|`iIJ;*!O{3i_ ziUsuSn!&bly1Hw`?Zb6jlAs;8P-!;p2&jr>r&!9)2w2yO%lkOJO zuu26ao~Lr_mL~mn{+7+)j?}bjtePwKjlG_%!?Oi3_ zBt3AItsZsmN;_+{s+CC6QKYg7h{rP4uw}gKH z+sOOcfEjDcAQ+CAjsW;$lP`Z_V{Nk96eI6`L{ zy~-}dDFFQ`GlJ(-5FvajiQqn!LRdaEdm7SbJJYAZC#BDxR&|4SvfFCN>54D_k{04Dle2%f$Sf~7BuFyB`~80)rL%8 ze+fZ*-E|!xd_9SvxnaBk;JFb*NZnXK(BGWC2@t)Ry9J=TIgc=Y+j<*d;EwVxfbs4G zg70n^A#!&X!FxA-7s^ZB&Y(|oZ|oj`aL<1q;r`SEfWSQ$LgYabVfMiy!t{gu14uV; zZwYD;tx}A1Ek-MJ_3k6j3LZFDk78~jXVa(JyJi0bn1s1^!Xph5K51gPXUIX zE+H&GEkoW(CuY}@Qo6Nxo->%Fc{cBIKY5Ea#cZo@3|rJOXH;)U`zuHj96%Z zaVRlnK*_|2P^lQL3OQ~S6C@&{Vp2q;RZNzMX%#a^L`lUshcR+!n8_2N8(|hkkfTsD zS~U_jHRB<|r)K7eu&S9H5sPYOP=j%XG|U7M3u?wn#Dto$X|O5tD#lKnu!?aI;ZiY^ zM5Hy$0uf6ZW>8D{Xc-+5c@3i{VphW#w3v@&fH4wB>Vq*YNLXN@3Ob}snF)~wJq%EcIU95Z&t7!7euF-A+A z*&w4Mj#PC+oUz5Rs92myrZM$mni;m9x^BBB{4O~i7Bab_`NUxt~=VjO#haS_K6 zXTTmCI8w&mER!TgKFbWwVTfUlF%vN{$GC{lⓈ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

q$*Lbn9Bxaxvg~c>|$~bb}C%&8* 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 Hxo-at>0YIh literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/setuptools/config/_validate_pyproject/__pycache__/formats.cpython-312.pyc b/venv/Lib/site-packages/setuptools/config/_validate_pyproject/__pycache__/formats.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..99fefaed37c4d0d6442f350dbfbe091dd0bca94c GIT binary patch literal 13472 zcmb_CZEzdcad)`G2LTe`_oqmo_zg+~^=ZkJM1`VAnG!|O6lGf^WeCI_Nl+kw+#N*` z1smFR8!B-tDo#RpG9JT8W=vJdSm|V1xBXGt>DcbHGX)H0NS)=5I?YUznNEvXp2TK; zw7Yk>!v`rv9p{mF-*4Z(efxIz?Yn=@%QF)Y_nZ5TF^1V>NhtKd>8Z-73OH)q;BY$gdE!4XrX zsa7?|<=!Th{BRyi4_l$1ZmLFYO{J`!vj(Ba8Zd@FOsY-IL2a#?Po+U4LEP3U<>5ls z$Q7|Bu6Uegb6%#0OIY)8=|ktVaHZcP*xc8t?ICMW%h)^(suiG?=MXtaL7TT?x^lRR zB-nhdy#U&4(#pk9X41+fP~Hg`7LDuJQpnfph*Jbx_A{}3*~)j?u_Az9bCFmgU$JCCQcg|X?IeKVru5nDjlaVBemg1Y z-IFel4>HC+kI3;ZuWK2OvYCN!^|+igA8(gatb%;=@qwdU!0(>y7}?YM)TLeao%Z9{ z^pH@;$eq(53>@V-FXs}tBmjUp>PW(Bv2O)a$0YkR0yi!|3;uHoCTL{tsyFYwa6&T3m zyRfs@?{;}RMqI5y`Sd`&f`wLNZ?S6!s*aJnGgmoY@c4b^ApOFq&=RB}s+Ww(TFJn3 z;tcO|Csm<*;^7Pv_0hF+N=;FZYRgG#0+_!xJCBcbyD85-Gk?Q&-FV8 zyH9mLFX^XUqI=R#@~HC#?9Xh@ceRgR5CAzj9zE%I&)@+SM!Wp1$2T!L;Bf{S0ykDZceCm<$S22$y8(xjVua)^h7MipoB}XDeE_HO6d> zpAuwF#oeOv1O@4)fPC{(NjVWNa!@}e9aQkmF8_>|Wqf{-8Ta^D#^qx?K0$POy)F^X zALAcqE-UN`+q#-Hn1~C)he6T}q4=2D-Rz0=?Gwqp}6lJhu9Ha6y>tKfb4Ce!e zc5AgT%Wftht4DR}j43mC5ztq54T-)CJ8?_bKXi-WE1{T1EtyJk^ei^u`CTA3Y(+A6 zWB^1sG8>$R%-2zFXl$@ZjFT++vWeB5Bvhk9l^Q%bM~RwTVd?9{_lVbZYU_Ezt`DB- zz6PpNKq?}_jsl(L$$8y8#p+%rQkxzkr_{TkItWshQpbg;N4KlDlN58! z(<0&~2bO-B2+^rF6)Lh;|0dbKJy#rsu^LYlD?G8?>Qr`4@=9q2uiOF?SrB>P1BcMcN7-li;STLRvA@{A(6iXNDBLX^_~H1`FIxjh` znRpO3!1U6E#QS)uB5g&u>}T9x_B@; zgZde9y!Ei%EK!2smGZc0m&favR&e6K0ys)mK+EeHbD+rPKpb>pWvQFz&^Becao3C& z&{5`>awa%&l5?>fFVWD0l?*KB_OqPa-~^s`P1}upH-M0VE5In2fj@y@O`sioUDJ==k>eTbRzHr&<|}f;U$O`h>vOOCv?LnbmM(<`$E@-wP4-a z9J4mZtt|_u?&jn#6@@3(avBo^r9VrqG~X?(c-6jW+^B6?uRRc}J#goR)zNtE;G%KK z8Z$kyVJo{ae|`R&Ut6~|#B2?biS@=qvBpF1x`hIIfFu#?7eJaO_f%W4~ z%LyIzo=$(lVsryqT2S@H10JfD!NZ6C!eQM7I3L3lcm>fG&I+uK#TK;NvI1wUfSPd% zW6s3V$my*9WnwsoH9%|z&z%wcVwR-mE_Y4`+nw(m;{8{-E{%;`SK!bNO>)fT%ObX< z6Zi6ECV5j~JObD-99VxUAjaPyBTw`9&A&={X8fMndUvcsN889hlIoWa&M32|Ys5}5&@H{Z%ynmV*m>cL- z#y-a}F0bH+ZoVcFko1XYX@SuNE8`hdYHpEnjrnIp#v|@#pr_!O4tVDn;ff~^z-0&? zFI+gF29S37WQ6)}+^I zqz6#tE=gu}O&pt{{ZLX+@O$8Z2p!EK!U;Jguq2dV=aMBIl(4@X=D^VF?~(Ge_tQ@C z=b?jSb>eEZ<0P4!PQ3FuouK$>f%A^18PeG3@p(k2Ggy($Iu5nzn*dcn6AqM2Wz8b} zifyB&ZjoLyRV55W#jZvA&-1ogVcPOQpUYY!eR*>PUk%l8FGTyDe;%TAqzas@M?j9z z77?u7id}Y}Zy^*sVhhCmn%YJB=cXzdEm`OC254|=G=u^TKbijE?*;@xWkE%DV96Om zutHn<$Ydv0H$%WZK_{xmo|wW(vO>yW6iR+iPx^Lp$N4AZ{nsF|`gxs*mWCX*Q>`KG zZUX8ExMjPwEOfAj{|_9vGHVK0)D-Na)T-L1t|NWRL%`A7VReO82DMdUL%z~vm^Wm53b)B}?uN%^cxeNKPRkYw&Z=j`tudhXod8L9f& z&c5E0okQKufr|r!=T3Kb4LMWI!E3$B-CC8lt!Xj^q%DXF&L@ET2z<6+67Z^c0rl@- z&Z{Cyl@}<0)5d2BM5Z}4uif#bWIi_idU3yyq$y7)*P+Jy(`ivH*~DenB#TjR9UX$>{X@Cnmts(6ijw z%mi34oG6;7X}FUqyRF{V!Ltt`tdLqv(!bz^``b%Q~Nt?tmy(+8>VCa1X zY}8Ww&|V3hK5xgp+J?2pBk{&#>y1yx8lR3go{ra^S)#vfOXR|pI847`d(9TLHGO1z zX64}9M{XUteQbSKS8P|;YF~WUGf~qspMPc{imSnX$=k6}+k^w}<(Dnz-OP*DoL!4Ih`&axS(X0li+mnc+clY5iC6h-ckMvmeGZw z2y6@H2G!_A$Oz~X`%~iAI=$YMuo4Aj%egmm!$Z-!{db=Io8g}fuTnp8MypT8^1I`7 z_ouX8zw1-0PG6TWRq6-HuscCu8eu<^Q)Lo_4>q!KMX+z^+U=fsL8Bt zeb1WA()0oVNQRW#0WSKwe*QAlgWIOinXSwPeiE8^4@R2zBzZx4wY7X?8(QJszH;lz?df$#Pt4J?8j3rHqNbs(Dmn*7$}5aO=Jz{FULes;0&*J1 zWCd2!KLUErw3V~ul;-kC4GDo+EaL2PNR71+mBa)Y(tTgM?W=Mbs73el`=Ec&&?<;C zV?qyHrqOR8nZP|EpL(e<=>kadDYBRmNK6t6iQx15R6XSVJK4O7Vb;Q(zUU$k{z zVj8GUg8LdT$0;W@?1OF!`)38ASJZ5fDUHIZPApu_(>celN&vzjd;Fc0aWSNBG+ zv4#+Fl@Kh5BWui@0W1#9ir60KYb2iwc<83ADLmDjI)|*`^VM3-{ch6S?>XAl+XUB6 zxRDB6Q0VdV%(Uy8XL@Fuar>tOAb-X@;Eb75{n68|D;y&$WKhkDXBL&O|os19Ocn{8#E?6 z_1JTy+~jpz#*9aiCK;4jcsCY|vo78T!c#f%C~72wj}yUp%uhfe$-K%yJvy0$49?|| zJ>}?MAS1jA(E>^^x;y#rBm8>#fIPt;gPB*IK()g%7!RLwC!W$f1>kD^1az z`{L#Mm-N4|6)kly_ucIKsCaMW!ds(nj>c{KqNaVH-?J3M^90OqD~9I@eTkxC7CVoh zFcI&Wa!wRe?-h{{2TMm@7#$fEj$LXVdEpp5S;3Rwu}k*jLF35itfTePuApUfc31PL z17dqihLA?HTs|_jka?I0Qw8#QLV2x%*CUURa=C4huLl7ZkI^sTC!qTXjez`hOLfdr z9k)CYrJvY(c=6cDn0x~=55puS{)GZKR@gr)q_GFL^ zC5P;JMTx3;UcttnCm-Q+Ey(b>@&{n7U3DQ(Z*{c7d#3D>MN<+<7rCSiysXL@nS=iPQy8Hd$lgS$>JhW^H(z1Y%JXhw}#^ipa-uc=T$8dKT`)@aYuwI)%?@DIFOE5(Cda`fjaRBXGB-;d!Pf znAeSqhtE`C^~~j9(`C5#zzeDT%If#ReFQ$v0EGjO(=1$;K<~hJNWOMv$ZnAG(`YS! zh9JL?OI3s2UFXi8?LOIk(wW^us@}eFpmS)bd$31s@5$#W0swfhBZsG%>0e(mfbPS?v)E`mi-&~)nP9DT(tg7)Y`X5 zZI~=;w%sw)?nUaJ$#%nb-3G_Uv;(e7TiH~Tj{hc`HXzB;&kanDAtlMu-{eBnRf4|> zuWj3n(9BMG1iU}Y>)|y3St={2I)Ih^sVsfv5YUO7)1xnS`w?l(h1h2W)`&+{HBYuR zwKVrcN-obEM7-O`0lv2+LWWeE3JVxpFKYha?Orxmv&_tv*R{7ulJLQM$~+Cm`F2L1 z_D$)$F+}lIqV^&ZGKT0!k5Jz|d}PQtZ%V^H53o0F#U2cGYd^mWR=7!y{OW%p@C{JN zR6n>svikXhyl3*Q08Udx*U}kZdCOE4QJTbh)N`g87%fc^6+9mymv$~U-E3O6#|obR zg5L|%1$86vR(SXe{x4v< zP4aT^z-6WtzFk1)HoPwJE`fosIKW+{$vFXC<|yIPM4Nmg%OC>Y{A42ZLq@YKz}cH= zSv=~9ss|zY()&b))dBk~n`h`W=i4Cq(gZ3(0_^}u$v584JBr=$R@s|nKd4x@?TT4D zmG2#1s%s$aIsB72HO>zfJiIu7!@~^TGZCJ+Ty9P=Mr5>ITv$p_-po`RXFJB`Z$W<=*R}>3uyIPO zmi;&V5qG?_<;8RI^RT#KG%vC*{t-C!-*CL;o$OFw;begDbW{*$X>Z2y^fL*IJCV60&jB3YkVV{E-p^dHB zv#(+fO%0hRv;2IUrlSh4ckq7?U4UT;;iqt|gBv4BeoRo1TI3UG&Xg$CpyO_b7PABi|&ib!FaD9tBJ7KH=>$&&a0lSDI7+7=}WH;Ss4 z_AR&HY>$x9+5^#@2k$%+tvU2g;Xf4rZE?J)BTD3NFfCD{dZVT}Jod)aYf~$Gqphc- zyU%>MH)=ooA^)%9KZ)_0p(s(cQDu)3n68MQ$?4~bPWX7N>{CJqJM*8WZg6=(=68uw!c_Dr zVIqq+Xv^2ny@*bcoMYe$(Hj5(eKGDGBw_Y9m?Uh_@pp+KN*+X#EqBYB;$`-aX!LK8 zEt|p#;NJHd4Ly0pd%I0NHN?+KtfwgA{X9!g74?3(4r<=7Br#t_VZNs36hXazoP_uT OD)&?&^+5p%@&5s*JpGse literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/setuptools/config/_validate_pyproject/error_reporting.py b/venv/Lib/site-packages/setuptools/config/_validate_pyproject/error_reporting.py new file mode 100644 index 0000000..d44e290 --- /dev/null +++ b/venv/Lib/site-packages/setuptools/config/_validate_pyproject/error_reporting.py @@ -0,0 +1,318 @@ +import io +import json +import logging +import os +import re +from contextlib import contextmanager +from textwrap import indent, wrap +from typing import Any, Dict, Iterator, List, Optional, Sequence, Union, cast + +from .fastjsonschema_exceptions import JsonSchemaValueException + +_logger = logging.getLogger(__name__) + +_MESSAGE_REPLACEMENTS = { + "must be named by propertyName definition": "keys must be named by", + "one of contains definition": "at least one item that matches", + " same as const definition:": "", + "only specified items": "only items matching the definition", +} + +_SKIP_DETAILS = ( + "must not be empty", + "is always invalid", + "must not be there", +) + +_NEED_DETAILS = {"anyOf", "oneOf", "allOf", "contains", "propertyNames", "not", "items"} + +_CAMEL_CASE_SPLITTER = re.compile(r"\W+|([A-Z][^A-Z\W]*)") +_IDENTIFIER = re.compile(r"^[\w_]+$", re.I) + +_TOML_JARGON = { + "object": "table", + "property": "key", + "properties": "keys", + "property names": "keys", +} + + +class ValidationError(JsonSchemaValueException): + """Report violations of a given JSON schema. + + This class extends :exc:`~fastjsonschema.JsonSchemaValueException` + by adding the following properties: + + - ``summary``: an improved version of the ``JsonSchemaValueException`` error message + with only the necessary information) + + - ``details``: more contextual information about the error like the failing schema + itself and the value that violates the schema. + + Depending on the level of the verbosity of the ``logging`` configuration + the exception message will be only ``summary`` (default) or a combination of + ``summary`` and ``details`` (when the logging level is set to :obj:`logging.DEBUG`). + """ + + summary = "" + details = "" + _original_message = "" + + @classmethod + def _from_jsonschema(cls, ex: JsonSchemaValueException): + formatter = _ErrorFormatting(ex) + obj = cls(str(formatter), ex.value, formatter.name, ex.definition, ex.rule) + debug_code = os.getenv("JSONSCHEMA_DEBUG_CODE_GENERATION", "false").lower() + if debug_code != "false": # pragma: no cover + obj.__cause__, obj.__traceback__ = ex.__cause__, ex.__traceback__ + obj._original_message = ex.message + obj.summary = formatter.summary + obj.details = formatter.details + return obj + + +@contextmanager +def detailed_errors(): + try: + yield + except JsonSchemaValueException as ex: + raise ValidationError._from_jsonschema(ex) from None + + +class _ErrorFormatting: + def __init__(self, ex: JsonSchemaValueException): + self.ex = ex + self.name = f"`{self._simplify_name(ex.name)}`" + self._original_message = self.ex.message.replace(ex.name, self.name) + self._summary = "" + self._details = "" + + def __str__(self) -> str: + if _logger.getEffectiveLevel() <= logging.DEBUG and self.details: + return f"{self.summary}\n\n{self.details}" + + return self.summary + + @property + def summary(self) -> str: + if not self._summary: + self._summary = self._expand_summary() + + return self._summary + + @property + def details(self) -> str: + if not self._details: + self._details = self._expand_details() + + return self._details + + def _simplify_name(self, name): + x = len("data.") + return name[x:] if name.startswith("data.") else name + + def _expand_summary(self): + msg = self._original_message + + for bad, repl in _MESSAGE_REPLACEMENTS.items(): + msg = msg.replace(bad, repl) + + if any(substring in msg for substring in _SKIP_DETAILS): + return msg + + schema = self.ex.rule_definition + if self.ex.rule in _NEED_DETAILS and schema: + summary = _SummaryWriter(_TOML_JARGON) + return f"{msg}:\n\n{indent(summary(schema), ' ')}" + + return msg + + def _expand_details(self) -> str: + optional = [] + desc_lines = self.ex.definition.pop("$$description", []) + desc = self.ex.definition.pop("description", None) or " ".join(desc_lines) + if desc: + description = "\n".join( + wrap( + desc, + width=80, + initial_indent=" ", + subsequent_indent=" ", + break_long_words=False, + ) + ) + optional.append(f"DESCRIPTION:\n{description}") + schema = json.dumps(self.ex.definition, indent=4) + value = json.dumps(self.ex.value, indent=4) + defaults = [ + f"GIVEN VALUE:\n{indent(value, ' ')}", + f"OFFENDING RULE: {self.ex.rule!r}", + f"DEFINITION:\n{indent(schema, ' ')}", + ] + return "\n\n".join(optional + defaults) + + +class _SummaryWriter: + _IGNORE = {"description", "default", "title", "examples"} + + def __init__(self, jargon: Optional[Dict[str, str]] = None): + self.jargon: Dict[str, str] = jargon or {} + # Clarify confusing terms + self._terms = { + "anyOf": "at least one of the following", + "oneOf": "exactly one of the following", + "allOf": "all of the following", + "not": "(*NOT* the following)", + "prefixItems": f"{self._jargon('items')} (in order)", + "items": "items", + "contains": "contains at least one of", + "propertyNames": ( + f"non-predefined acceptable {self._jargon('property names')}" + ), + "patternProperties": f"{self._jargon('properties')} named via pattern", + "const": "predefined value", + "enum": "one of", + } + # Attributes that indicate that the definition is easy and can be done + # inline (e.g. string and number) + self._guess_inline_defs = [ + "enum", + "const", + "maxLength", + "minLength", + "pattern", + "format", + "minimum", + "maximum", + "exclusiveMinimum", + "exclusiveMaximum", + "multipleOf", + ] + + def _jargon(self, term: Union[str, List[str]]) -> Union[str, List[str]]: + if isinstance(term, list): + return [self.jargon.get(t, t) for t in term] + return self.jargon.get(term, term) + + def __call__( + self, + schema: Union[dict, List[dict]], + prefix: str = "", + *, + _path: Sequence[str] = (), + ) -> str: + if isinstance(schema, list): + return self._handle_list(schema, prefix, _path) + + filtered = self._filter_unecessary(schema, _path) + simple = self._handle_simple_dict(filtered, _path) + if simple: + return f"{prefix}{simple}" + + child_prefix = self._child_prefix(prefix, " ") + item_prefix = self._child_prefix(prefix, "- ") + indent = len(prefix) * " " + with io.StringIO() as buffer: + for i, (key, value) in enumerate(filtered.items()): + child_path = [*_path, key] + line_prefix = prefix if i == 0 else indent + buffer.write(f"{line_prefix}{self._label(child_path)}:") + # ^ just the first item should receive the complete prefix + if isinstance(value, dict): + filtered = self._filter_unecessary(value, child_path) + simple = self._handle_simple_dict(filtered, child_path) + buffer.write( + f" {simple}" + if simple + else f"\n{self(value, child_prefix, _path=child_path)}" + ) + elif isinstance(value, list) and ( + key != "type" or self._is_property(child_path) + ): + children = self._handle_list(value, item_prefix, child_path) + sep = " " if children.startswith("[") else "\n" + buffer.write(f"{sep}{children}") + else: + buffer.write(f" {self._value(value, child_path)}\n") + return buffer.getvalue() + + def _is_unecessary(self, path: Sequence[str]) -> bool: + if self._is_property(path) or not path: # empty path => instruction @ root + return False + key = path[-1] + return any(key.startswith(k) for k in "$_") or key in self._IGNORE + + def _filter_unecessary(self, schema: dict, path: Sequence[str]): + return { + key: value + for key, value in schema.items() + if not self._is_unecessary([*path, key]) + } + + def _handle_simple_dict(self, value: dict, path: Sequence[str]) -> Optional[str]: + inline = any(p in value for p in self._guess_inline_defs) + simple = not any(isinstance(v, (list, dict)) for v in value.values()) + if inline or simple: + return f"{{{', '.join(self._inline_attrs(value, path))}}}\n" + return None + + def _handle_list( + self, schemas: list, prefix: str = "", path: Sequence[str] = () + ) -> str: + if self._is_unecessary(path): + return "" + + repr_ = repr(schemas) + if all(not isinstance(e, (dict, list)) for e in schemas) and len(repr_) < 60: + return f"{repr_}\n" + + item_prefix = self._child_prefix(prefix, "- ") + return "".join( + self(v, item_prefix, _path=[*path, f"[{i}]"]) for i, v in enumerate(schemas) + ) + + def _is_property(self, path: Sequence[str]): + """Check if the given path can correspond to an arbitrarily named property""" + counter = 0 + for key in path[-2::-1]: + if key not in {"properties", "patternProperties"}: + break + counter += 1 + + # If the counter if even, the path correspond to a JSON Schema keyword + # otherwise it can be any arbitrary string naming a property + return counter % 2 == 1 + + def _label(self, path: Sequence[str]) -> str: + *parents, key = path + if not self._is_property(path): + norm_key = _separate_terms(key) + return self._terms.get(key) or " ".join(self._jargon(norm_key)) + + if parents[-1] == "patternProperties": + return f"(regex {key!r})" + return repr(key) # property name + + def _value(self, value: Any, path: Sequence[str]) -> str: + if path[-1] == "type" and not self._is_property(path): + type_ = self._jargon(value) + return ( + f"[{', '.join(type_)}]" if isinstance(value, list) else cast(str, type_) + ) + return repr(value) + + def _inline_attrs(self, schema: dict, path: Sequence[str]) -> Iterator[str]: + for key, value in schema.items(): + child_path = [*path, key] + yield f"{self._label(child_path)}: {self._value(value, child_path)}" + + def _child_prefix(self, parent_prefix: str, child_prefix: str) -> str: + return len(parent_prefix) * " " + child_prefix + + +def _separate_terms(word: str) -> List[str]: + """ + >>> _separate_terms("FooBar-foo") + ['foo', 'bar', 'foo'] + """ + return [w.lower() for w in _CAMEL_CASE_SPLITTER.split(word) if w] diff --git a/venv/Lib/site-packages/setuptools/config/_validate_pyproject/extra_validations.py b/venv/Lib/site-packages/setuptools/config/_validate_pyproject/extra_validations.py new file mode 100644 index 0000000..4130a42 --- /dev/null +++ b/venv/Lib/site-packages/setuptools/config/_validate_pyproject/extra_validations.py @@ -0,0 +1,36 @@ +"""The purpose of this module is implement PEP 621 validations that are +difficult to express as a JSON Schema (or that are not supported by the current +JSON Schema library). +""" + +from typing import Mapping, TypeVar + +from .error_reporting import ValidationError + +T = TypeVar("T", bound=Mapping) + + +class RedefiningStaticFieldAsDynamic(ValidationError): + """According to PEP 621: + + Build back-ends MUST raise an error if the metadata specifies a field + statically as well as being listed in dynamic. + """ + + +def validate_project_dynamic(pyproject: T) -> T: + project_table = pyproject.get("project", {}) + dynamic = project_table.get("dynamic", []) + + for field in dynamic: + if field in project_table: + msg = f"You cannot provide a value for `project.{field}` and " + msg += "list it under `project.dynamic` at the same time" + name = f"data.project.{field}" + value = {field: project_table[field], "...": " # ...", "dynamic": dynamic} + raise RedefiningStaticFieldAsDynamic(msg, value, name, rule="PEP 621") + + return pyproject + + +EXTRA_VALIDATIONS = (validate_project_dynamic,) diff --git a/venv/Lib/site-packages/setuptools/config/_validate_pyproject/fastjsonschema_exceptions.py b/venv/Lib/site-packages/setuptools/config/_validate_pyproject/fastjsonschema_exceptions.py new file mode 100644 index 0000000..d2dddd6 --- /dev/null +++ b/venv/Lib/site-packages/setuptools/config/_validate_pyproject/fastjsonschema_exceptions.py @@ -0,0 +1,51 @@ +import re + + +SPLIT_RE = re.compile(r'[\.\[\]]+') + + +class JsonSchemaException(ValueError): + """ + Base exception of ``fastjsonschema`` library. + """ + + +class JsonSchemaValueException(JsonSchemaException): + """ + Exception raised by validation function. Available properties: + + * ``message`` containing human-readable information what is wrong (e.g. ``data.property[index] must be smaller than or equal to 42``), + * invalid ``value`` (e.g. ``60``), + * ``name`` of a path in the data structure (e.g. ``data.property[index]``), + * ``path`` as an array in the data structure (e.g. ``['data', 'property', 'index']``), + * the whole ``definition`` which the ``value`` has to fulfil (e.g. ``{'type': 'number', 'maximum': 42}``), + * ``rule`` which the ``value`` is breaking (e.g. ``maximum``) + * and ``rule_definition`` (e.g. ``42``). + + .. versionchanged:: 2.14.0 + Added all extra properties. + """ + + def __init__(self, message, value=None, name=None, definition=None, rule=None): + super().__init__(message) + self.message = message + self.value = value + self.name = name + self.definition = definition + self.rule = rule + + @property + def path(self): + return [item for item in SPLIT_RE.split(self.name) if item != ''] + + @property + def rule_definition(self): + if not self.rule or not self.definition: + return None + return self.definition.get(self.rule) + + +class JsonSchemaDefinitionException(JsonSchemaException): + """ + Exception raised by generator of validation function. + """ diff --git a/venv/Lib/site-packages/setuptools/config/_validate_pyproject/fastjsonschema_validations.py b/venv/Lib/site-packages/setuptools/config/_validate_pyproject/fastjsonschema_validations.py new file mode 100644 index 0000000..8b852bb --- /dev/null +++ b/venv/Lib/site-packages/setuptools/config/_validate_pyproject/fastjsonschema_validations.py @@ -0,0 +1,1051 @@ +# noqa +# flake8: noqa +# pylint: skip-file +# mypy: ignore-errors +# yapf: disable +# pylama:skip=1 + + +# *** PLEASE DO NOT MODIFY DIRECTLY: Automatically generated code *** + + +VERSION = "2.16.3" +import re +from .fastjsonschema_exceptions import JsonSchemaValueException + + +REGEX_PATTERNS = { + '^.*$': re.compile('^.*$'), + '.+': re.compile('.+'), + '^.+$': re.compile('^.+$'), + 'idn-email_re_pattern': re.compile('^[^@]+@[^@]+\\.[^@]+\\Z') +} + +NoneType = type(None) + +def validate(data, custom_formats={}, name_prefix=None): + validate_https___packaging_python_org_en_latest_specifications_declaring_build_dependencies(data, custom_formats, (name_prefix or "data") + "") + return data + +def validate_https___packaging_python_org_en_latest_specifications_declaring_build_dependencies(data, custom_formats={}, name_prefix=None): + if not isinstance(data, (dict)): + raise JsonSchemaValueException("" + (name_prefix or "data") + " must be object", value=data, name="" + (name_prefix or "data") + "", definition={'$schema': 'http://json-schema.org/draft-07/schema', '$id': 'https://packaging.python.org/en/latest/specifications/declaring-build-dependencies/', 'title': 'Data structure for ``pyproject.toml`` files', '$$description': ['File format containing build-time configurations for the Python ecosystem. ', ':pep:`517` initially defined a build-system independent format for source trees', 'which was complemented by :pep:`518` to provide a way of specifying dependencies ', 'for building Python projects.', 'Please notice the ``project`` table (as initially defined in :pep:`621`) is not included', 'in this schema and should be considered separately.'], 'type': 'object', 'additionalProperties': False, 'properties': {'build-system': {'type': 'object', 'description': 'Table used to store build-related data', 'additionalProperties': False, 'properties': {'requires': {'type': 'array', '$$description': ['List of dependencies in the :pep:`508` format required to execute the build', 'system. Please notice that the resulting dependency graph', '**MUST NOT contain cycles**'], 'items': {'type': 'string'}}, 'build-backend': {'type': 'string', 'description': 'Python object that will be used to perform the build according to :pep:`517`', 'format': 'pep517-backend-reference'}, 'backend-path': {'type': 'array', '$$description': ['List of directories to be prepended to ``sys.path`` when loading the', 'back-end, and running its hooks'], 'items': {'type': 'string', '$comment': 'Should be a path (TODO: enforce it with format?)'}}}, 'required': ['requires']}, 'project': {'$schema': 'http://json-schema.org/draft-07/schema', '$id': 'https://packaging.python.org/en/latest/specifications/declaring-project-metadata/', 'title': 'Package metadata stored in the ``project`` table', '$$description': ['Data structure for the **project** table inside ``pyproject.toml``', '(as initially defined in :pep:`621`)'], 'type': 'object', 'properties': {'name': {'type': 'string', 'description': 'The name (primary identifier) of the project. MUST be statically defined.', 'format': 'pep508-identifier'}, 'version': {'type': 'string', 'description': 'The version of the project as supported by :pep:`440`.', 'format': 'pep440'}, 'description': {'type': 'string', '$$description': ['The `summary description of the project', '`_']}, 'readme': {'$$description': ['`Full/detailed description of the project in the form of a README', '`_', "with meaning similar to the one defined in `core metadata's Description", '`_'], 'oneOf': [{'type': 'string', '$$description': ['Relative path to a text file (UTF-8) containing the full description', 'of the project. If the file path ends in case-insensitive ``.md`` or', '``.rst`` suffixes, then the content-type is respectively', '``text/markdown`` or ``text/x-rst``']}, {'type': 'object', 'allOf': [{'anyOf': [{'properties': {'file': {'type': 'string', '$$description': ['Relative path to a text file containing the full description', 'of the project.']}}, 'required': ['file']}, {'properties': {'text': {'type': 'string', 'description': 'Full text describing the project.'}}, 'required': ['text']}]}, {'properties': {'content-type': {'type': 'string', '$$description': ['Content-type (:rfc:`1341`) of the full description', '(e.g. ``text/markdown``). The ``charset`` parameter is assumed', 'UTF-8 when not present.'], '$comment': 'TODO: add regex pattern or format?'}}, 'required': ['content-type']}]}]}, 'requires-python': {'type': 'string', 'format': 'pep508-versionspec', '$$description': ['`The Python version requirements of the project', '`_.']}, 'license': {'description': '`Project license `_.', 'oneOf': [{'properties': {'file': {'type': 'string', '$$description': ['Relative path to the file (UTF-8) which contains the license for the', 'project.']}}, 'required': ['file']}, {'properties': {'text': {'type': 'string', '$$description': ['The license of the project whose meaning is that of the', '`License field from the core metadata', '`_.']}}, 'required': ['text']}]}, 'authors': {'type': 'array', 'items': {'$ref': '#/definitions/author'}, '$$description': ["The people or organizations considered to be the 'authors' of the project.", 'The exact meaning is open to interpretation (e.g. original or primary authors,', 'current maintainers, or owners of the package).']}, 'maintainers': {'type': 'array', 'items': {'$ref': '#/definitions/author'}, '$$description': ["The people or organizations considered to be the 'maintainers' of the project.", 'Similarly to ``authors``, the exact meaning is open to interpretation.']}, 'keywords': {'type': 'array', 'items': {'type': 'string'}, 'description': 'List of keywords to assist searching for the distribution in a larger catalog.'}, 'classifiers': {'type': 'array', 'items': {'type': 'string', 'format': 'trove-classifier', 'description': '`PyPI classifier `_.'}, '$$description': ['`Trove classifiers `_', 'which apply to the project.']}, 'urls': {'type': 'object', 'description': 'URLs associated with the project in the form ``label => value``.', 'additionalProperties': False, 'patternProperties': {'^.+$': {'type': 'string', 'format': 'url'}}}, 'scripts': {'$ref': '#/definitions/entry-point-group', '$$description': ['Instruct the installer to create command-line wrappers for the given', '`entry points `_.']}, 'gui-scripts': {'$ref': '#/definitions/entry-point-group', '$$description': ['Instruct the installer to create GUI wrappers for the given', '`entry points `_.', 'The difference between ``scripts`` and ``gui-scripts`` is only relevant in', 'Windows.']}, 'entry-points': {'$$description': ['Instruct the installer to expose the given modules/functions via', '``entry-point`` discovery mechanism (useful for plugins).', 'More information available in the `Python packaging guide', '`_.'], 'propertyNames': {'format': 'python-entrypoint-group'}, 'additionalProperties': False, 'patternProperties': {'^.+$': {'$ref': '#/definitions/entry-point-group'}}}, 'dependencies': {'type': 'array', 'description': 'Project (mandatory) dependencies.', 'items': {'$ref': '#/definitions/dependency'}}, 'optional-dependencies': {'type': 'object', 'description': 'Optional dependency for the project', 'propertyNames': {'format': 'pep508-identifier'}, 'additionalProperties': False, 'patternProperties': {'^.+$': {'type': 'array', 'items': {'$ref': '#/definitions/dependency'}}}}, 'dynamic': {'type': 'array', '$$description': ['Specifies which fields are intentionally unspecified and expected to be', 'dynamically provided by build tools'], 'items': {'enum': ['version', 'description', 'readme', 'requires-python', 'license', 'authors', 'maintainers', 'keywords', 'classifiers', 'urls', 'scripts', 'gui-scripts', 'entry-points', 'dependencies', 'optional-dependencies']}}}, 'required': ['name'], 'additionalProperties': False, 'if': {'not': {'required': ['dynamic'], 'properties': {'dynamic': {'contains': {'const': 'version'}, '$$description': ['version is listed in ``dynamic``']}}}, '$$comment': ['According to :pep:`621`:', ' If the core metadata specification lists a field as "Required", then', ' the metadata MUST specify the field statically or list it in dynamic', 'In turn, `core metadata`_ defines:', ' The required fields are: Metadata-Version, Name, Version.', ' All the other fields are optional.', 'Since ``Metadata-Version`` is defined by the build back-end, ``name`` and', '``version`` are the only mandatory information in ``pyproject.toml``.', '.. _core metadata: https://packaging.python.org/specifications/core-metadata/']}, 'then': {'required': ['version'], '$$description': ['version should be statically defined in the ``version`` field']}, 'definitions': {'author': {'$id': '#/definitions/author', 'title': 'Author or Maintainer', '$comment': 'https://peps.python.org/pep-0621/#authors-maintainers', 'type': 'object', 'additionalProperties': False, 'properties': {'name': {'type': 'string', '$$description': ['MUST be a valid email name, i.e. whatever can be put as a name, before an', 'email, in :rfc:`822`.']}, 'email': {'type': 'string', 'format': 'idn-email', 'description': 'MUST be a valid email address'}}}, 'entry-point-group': {'$id': '#/definitions/entry-point-group', 'title': 'Entry-points', 'type': 'object', '$$description': ['Entry-points are grouped together to indicate what sort of capabilities they', 'provide.', 'See the `packaging guides', '`_', 'and `setuptools docs', '`_', 'for more information.'], 'propertyNames': {'format': 'python-entrypoint-name'}, 'additionalProperties': False, 'patternProperties': {'^.+$': {'type': 'string', '$$description': ['Reference to a Python object. It is either in the form', '``importable.module``, or ``importable.module:object.attr``.'], 'format': 'python-entrypoint-reference', '$comment': 'https://packaging.python.org/specifications/entry-points/'}}}, 'dependency': {'$id': '#/definitions/dependency', 'title': 'Dependency', 'type': 'string', 'description': 'Project dependency specification according to PEP 508', 'format': 'pep508'}}}, 'tool': {'type': 'object', 'properties': {'distutils': {'$schema': 'http://json-schema.org/draft-07/schema', '$id': 'https://docs.python.org/3/install/', 'title': '``tool.distutils`` table', '$$description': ['Originally, ``distutils`` allowed developers to configure arguments for', '``setup.py`` scripts via `distutils configuration files', '`_.', '``tool.distutils`` subtables could be used with the same purpose', '(NOT CURRENTLY IMPLEMENTED).'], 'type': 'object', 'properties': {'global': {'type': 'object', 'description': 'Global options applied to all ``distutils`` commands'}}, 'patternProperties': {'.+': {'type': 'object'}}, '$comment': 'TODO: Is there a practical way of making this schema more specific?'}, 'setuptools': {'$schema': 'http://json-schema.org/draft-07/schema', '$id': 'https://setuptools.pypa.io/en/latest/references/keywords.html', 'title': '``tool.setuptools`` table', '$$description': ['Please notice for the time being the ``setuptools`` project does not specify', 'a way of configuring builds via ``pyproject.toml``.', 'Therefore this schema should be taken just as a *"thought experiment"* on how', 'this *might be done*, by following the principles established in', '`ini2toml `_.', 'It considers only ``setuptools`` `parameters', '`_', 'that can currently be configured via ``setup.cfg`` and are not covered by :pep:`621`', 'but intentionally excludes ``dependency_links`` and ``setup_requires``.', 'NOTE: ``scripts`` was renamed to ``script-files`` to avoid confusion with', 'entry-point based scripts (defined in :pep:`621`).'], 'type': 'object', 'additionalProperties': False, 'properties': {'platforms': {'type': 'array', 'items': {'type': 'string'}}, 'provides': {'$$description': ['Package and virtual package names contained within this package', '**(not supported by pip)**'], 'type': 'array', 'items': {'type': 'string', 'format': 'pep508-identifier'}}, 'obsoletes': {'$$description': ['Packages which this package renders obsolete', '**(not supported by pip)**'], 'type': 'array', 'items': {'type': 'string', 'format': 'pep508-identifier'}}, 'zip-safe': {'description': 'Whether the project can be safely installed and run from a zip file.', 'type': 'boolean'}, 'script-files': {'description': 'Legacy way of defining scripts (entry-points are preferred).', 'type': 'array', 'items': {'type': 'string'}, '$comment': 'TODO: is this field deprecated/should be removed?'}, 'eager-resources': {'$$description': ['Resources that should be extracted together, if any of them is needed,', 'or if any C extensions included in the project are imported.'], 'type': 'array', 'items': {'type': 'string'}}, 'packages': {'$$description': ['Packages that should be included in the distribution.', 'It can be given either as a list of package identifiers', 'or as a ``dict``-like structure with a single key ``find``', 'which corresponds to a dynamic call to', '``setuptools.config.expand.find_packages`` function.', 'The ``find`` key is associated with a nested ``dict``-like structure that can', 'contain ``where``, ``include``, ``exclude`` and ``namespaces`` keys,', 'mimicking the keyword arguments of the associated function.'], 'oneOf': [{'title': 'Array of Python package identifiers', 'type': 'array', 'items': {'$ref': '#/definitions/package-name'}}, {'$ref': '#/definitions/find-directive'}]}, 'package-dir': {'$$description': [':class:`dict`-like structure mapping from package names to directories where their', 'code can be found.', 'The empty string (as key) means that all packages are contained inside', 'the given directory will be included in the distribution.'], 'type': 'object', 'additionalProperties': False, 'propertyNames': {'oneOf': [{'const': ''}, {'$ref': '#/definitions/package-name'}]}, 'patternProperties': {'^.*$': {'type': 'string'}}}, 'package-data': {'$$description': ['Mapping from package names to lists of glob patterns.', 'Usually this option is not needed when using ``include-package-data = true``', 'For more information on how to include data files, check ``setuptools`` `docs', '`_.'], 'type': 'object', 'additionalProperties': False, 'propertyNames': {'oneOf': [{'format': 'python-module-name'}, {'const': '*'}]}, 'patternProperties': {'^.*$': {'type': 'array', 'items': {'type': 'string'}}}}, 'include-package-data': {'$$description': ['Automatically include any data files inside the package directories', 'that are specified by ``MANIFEST.in``', 'For more information on how to include data files, check ``setuptools`` `docs', '`_.'], 'type': 'boolean'}, 'exclude-package-data': {'$$description': ['Mapping from package names to lists of glob patterns that should be excluded', 'For more information on how to include data files, check ``setuptools`` `docs', '`_.'], 'type': 'object', 'additionalProperties': False, 'propertyNames': {'oneOf': [{'format': 'python-module-name'}, {'const': '*'}]}, 'patternProperties': {'^.*$': {'type': 'array', 'items': {'type': 'string'}}}}, 'namespace-packages': {'type': 'array', 'items': {'type': 'string', 'format': 'python-module-name'}, '$comment': 'https://setuptools.pypa.io/en/latest/userguide/package_discovery.html'}, 'py-modules': {'description': 'Modules that setuptools will manipulate', 'type': 'array', 'items': {'type': 'string', 'format': 'python-module-name'}, '$comment': 'TODO: clarify the relationship with ``packages``'}, 'data-files': {'$$description': ['**DEPRECATED**: dict-like structure where each key represents a directory and', 'the value is a list of glob patterns that should be installed in them.', "Please notice this don't work with wheels. See `data files support", '`_'], 'type': 'object', 'patternProperties': {'^.*$': {'type': 'array', 'items': {'type': 'string'}}}}, 'cmdclass': {'$$description': ['Mapping of distutils-style command names to ``setuptools.Command`` subclasses', 'which in turn should be represented by strings with a qualified class name', '(i.e., "dotted" form with module), e.g.::\n\n', ' cmdclass = {mycmd = "pkg.subpkg.module.CommandClass"}\n\n', 'The command class should be a directly defined at the top-level of the', 'containing module (no class nesting).'], 'type': 'object', 'patternProperties': {'^.*$': {'type': 'string', 'format': 'python-qualified-identifier'}}}, 'license-files': {'type': 'array', 'items': {'type': 'string'}, '$$description': ['PROVISIONAL: List of glob patterns for all license files being distributed.', '(might become standard with PEP 639).', "By default: ``['LICEN[CS]E*', 'COPYING*', 'NOTICE*', 'AUTHORS*']``"], '$comment': 'TODO: revise if PEP 639 is accepted. Probably ``project.license-files``?'}, 'dynamic': {'type': 'object', 'description': 'Instructions for loading :pep:`621`-related metadata dynamically', 'additionalProperties': False, 'properties': {'version': {'$$description': ['A version dynamically loaded via either the ``attr:`` or ``file:``', 'directives. Please make sure the given file or attribute respects :pep:`440`.'], 'oneOf': [{'$ref': '#/definitions/attr-directive'}, {'$ref': '#/definitions/file-directive'}]}, 'classifiers': {'$ref': '#/definitions/file-directive'}, 'description': {'$ref': '#/definitions/file-directive'}, 'dependencies': {'$ref': '#/definitions/file-directive'}, 'entry-points': {'$ref': '#/definitions/file-directive'}, 'optional-dependencies': {'type': 'object', 'propertyNames': {'format': 'python-identifier'}, 'additionalProperties': False, 'patternProperties': {'.+': {'$ref': '#/definitions/file-directive'}}}, 'readme': {'anyOf': [{'$ref': '#/definitions/file-directive'}, {'properties': {'content-type': {'type': 'string'}}}], 'required': ['file']}}}}, 'definitions': {'package-name': {'$id': '#/definitions/package-name', 'title': 'Valid package name', 'description': 'Valid package name (importable or PEP 561).', 'type': 'string', 'anyOf': [{'format': 'python-module-name'}, {'format': 'pep561-stub-name'}]}, 'file-directive': {'$id': '#/definitions/file-directive', 'title': "'file:' directive", 'description': 'Value is read from a file (or list of files and then concatenated)', 'type': 'object', 'additionalProperties': False, 'properties': {'file': {'oneOf': [{'type': 'string'}, {'type': 'array', 'items': {'type': 'string'}}]}}, 'required': ['file']}, 'attr-directive': {'title': "'attr:' directive", '$id': '#/definitions/attr-directive', '$$description': ['Value is read from a module attribute. Supports callables and iterables;', 'unsupported types are cast via ``str()``'], 'type': 'object', 'additionalProperties': False, 'properties': {'attr': {'type': 'string'}}, 'required': ['attr']}, 'find-directive': {'$id': '#/definitions/find-directive', 'title': "'find:' directive", 'type': 'object', 'additionalProperties': False, 'properties': {'find': {'type': 'object', '$$description': ['Dynamic `package discovery', '`_.'], 'additionalProperties': False, 'properties': {'where': {'description': 'Directories to be searched for packages (Unix-style relative path)', 'type': 'array', 'items': {'type': 'string'}}, 'exclude': {'type': 'array', '$$description': ['Exclude packages that match the values listed in this field.', "Can container shell-style wildcards (e.g. ``'pkg.*'``)"], 'items': {'type': 'string'}}, 'include': {'type': 'array', '$$description': ['Restrict the found packages to just the ones listed in this field.', "Can container shell-style wildcards (e.g. ``'pkg.*'``)"], 'items': {'type': 'string'}}, 'namespaces': {'type': 'boolean', '$$description': ['When ``True``, directories without a ``__init__.py`` file will also', 'be scanned for :pep:`420`-style implicit namespaces']}}}}}}}}}}, 'project': {'$schema': 'http://json-schema.org/draft-07/schema', '$id': 'https://packaging.python.org/en/latest/specifications/declaring-project-metadata/', 'title': 'Package metadata stored in the ``project`` table', '$$description': ['Data structure for the **project** table inside ``pyproject.toml``', '(as initially defined in :pep:`621`)'], 'type': 'object', 'properties': {'name': {'type': 'string', 'description': 'The name (primary identifier) of the project. MUST be statically defined.', 'format': 'pep508-identifier'}, 'version': {'type': 'string', 'description': 'The version of the project as supported by :pep:`440`.', 'format': 'pep440'}, 'description': {'type': 'string', '$$description': ['The `summary description of the project', '`_']}, 'readme': {'$$description': ['`Full/detailed description of the project in the form of a README', '`_', "with meaning similar to the one defined in `core metadata's Description", '`_'], 'oneOf': [{'type': 'string', '$$description': ['Relative path to a text file (UTF-8) containing the full description', 'of the project. If the file path ends in case-insensitive ``.md`` or', '``.rst`` suffixes, then the content-type is respectively', '``text/markdown`` or ``text/x-rst``']}, {'type': 'object', 'allOf': [{'anyOf': [{'properties': {'file': {'type': 'string', '$$description': ['Relative path to a text file containing the full description', 'of the project.']}}, 'required': ['file']}, {'properties': {'text': {'type': 'string', 'description': 'Full text describing the project.'}}, 'required': ['text']}]}, {'properties': {'content-type': {'type': 'string', '$$description': ['Content-type (:rfc:`1341`) of the full description', '(e.g. ``text/markdown``). The ``charset`` parameter is assumed', 'UTF-8 when not present.'], '$comment': 'TODO: add regex pattern or format?'}}, 'required': ['content-type']}]}]}, 'requires-python': {'type': 'string', 'format': 'pep508-versionspec', '$$description': ['`The Python version requirements of the project', '`_.']}, 'license': {'description': '`Project license `_.', 'oneOf': [{'properties': {'file': {'type': 'string', '$$description': ['Relative path to the file (UTF-8) which contains the license for the', 'project.']}}, 'required': ['file']}, {'properties': {'text': {'type': 'string', '$$description': ['The license of the project whose meaning is that of the', '`License field from the core metadata', '`_.']}}, 'required': ['text']}]}, 'authors': {'type': 'array', 'items': {'$ref': '#/definitions/author'}, '$$description': ["The people or organizations considered to be the 'authors' of the project.", 'The exact meaning is open to interpretation (e.g. original or primary authors,', 'current maintainers, or owners of the package).']}, 'maintainers': {'type': 'array', 'items': {'$ref': '#/definitions/author'}, '$$description': ["The people or organizations considered to be the 'maintainers' of the project.", 'Similarly to ``authors``, the exact meaning is open to interpretation.']}, 'keywords': {'type': 'array', 'items': {'type': 'string'}, 'description': 'List of keywords to assist searching for the distribution in a larger catalog.'}, 'classifiers': {'type': 'array', 'items': {'type': 'string', 'format': 'trove-classifier', 'description': '`PyPI classifier `_.'}, '$$description': ['`Trove classifiers `_', 'which apply to the project.']}, 'urls': {'type': 'object', 'description': 'URLs associated with the project in the form ``label => value``.', 'additionalProperties': False, 'patternProperties': {'^.+$': {'type': 'string', 'format': 'url'}}}, 'scripts': {'$ref': '#/definitions/entry-point-group', '$$description': ['Instruct the installer to create command-line wrappers for the given', '`entry points `_.']}, 'gui-scripts': {'$ref': '#/definitions/entry-point-group', '$$description': ['Instruct the installer to create GUI wrappers for the given', '`entry points `_.', 'The difference between ``scripts`` and ``gui-scripts`` is only relevant in', 'Windows.']}, 'entry-points': {'$$description': ['Instruct the installer to expose the given modules/functions via', '``entry-point`` discovery mechanism (useful for plugins).', 'More information available in the `Python packaging guide', '`_.'], 'propertyNames': {'format': 'python-entrypoint-group'}, 'additionalProperties': False, 'patternProperties': {'^.+$': {'$ref': '#/definitions/entry-point-group'}}}, 'dependencies': {'type': 'array', 'description': 'Project (mandatory) dependencies.', 'items': {'$ref': '#/definitions/dependency'}}, 'optional-dependencies': {'type': 'object', 'description': 'Optional dependency for the project', 'propertyNames': {'format': 'pep508-identifier'}, 'additionalProperties': False, 'patternProperties': {'^.+$': {'type': 'array', 'items': {'$ref': '#/definitions/dependency'}}}}, 'dynamic': {'type': 'array', '$$description': ['Specifies which fields are intentionally unspecified and expected to be', 'dynamically provided by build tools'], 'items': {'enum': ['version', 'description', 'readme', 'requires-python', 'license', 'authors', 'maintainers', 'keywords', 'classifiers', 'urls', 'scripts', 'gui-scripts', 'entry-points', 'dependencies', 'optional-dependencies']}}}, 'required': ['name'], 'additionalProperties': False, 'if': {'not': {'required': ['dynamic'], 'properties': {'dynamic': {'contains': {'const': 'version'}, '$$description': ['version is listed in ``dynamic``']}}}, '$$comment': ['According to :pep:`621`:', ' If the core metadata specification lists a field as "Required", then', ' the metadata MUST specify the field statically or list it in dynamic', 'In turn, `core metadata`_ defines:', ' The required fields are: Metadata-Version, Name, Version.', ' All the other fields are optional.', 'Since ``Metadata-Version`` is defined by the build back-end, ``name`` and', '``version`` are the only mandatory information in ``pyproject.toml``.', '.. _core metadata: https://packaging.python.org/specifications/core-metadata/']}, 'then': {'required': ['version'], '$$description': ['version should be statically defined in the ``version`` field']}, 'definitions': {'author': {'$id': '#/definitions/author', 'title': 'Author or Maintainer', '$comment': 'https://peps.python.org/pep-0621/#authors-maintainers', 'type': 'object', 'additionalProperties': False, 'properties': {'name': {'type': 'string', '$$description': ['MUST be a valid email name, i.e. whatever can be put as a name, before an', 'email, in :rfc:`822`.']}, 'email': {'type': 'string', 'format': 'idn-email', 'description': 'MUST be a valid email address'}}}, 'entry-point-group': {'$id': '#/definitions/entry-point-group', 'title': 'Entry-points', 'type': 'object', '$$description': ['Entry-points are grouped together to indicate what sort of capabilities they', 'provide.', 'See the `packaging guides', '`_', 'and `setuptools docs', '`_', 'for more information.'], 'propertyNames': {'format': 'python-entrypoint-name'}, 'additionalProperties': False, 'patternProperties': {'^.+$': {'type': 'string', '$$description': ['Reference to a Python object. It is either in the form', '``importable.module``, or ``importable.module:object.attr``.'], 'format': 'python-entrypoint-reference', '$comment': 'https://packaging.python.org/specifications/entry-points/'}}}, 'dependency': {'$id': '#/definitions/dependency', 'title': 'Dependency', 'type': 'string', 'description': 'Project dependency specification according to PEP 508', 'format': 'pep508'}}}}, rule='type') + data_is_dict = isinstance(data, dict) + if data_is_dict: + data_keys = set(data.keys()) + if "build-system" in data_keys: + data_keys.remove("build-system") + data__buildsystem = data["build-system"] + if not isinstance(data__buildsystem, (dict)): + raise JsonSchemaValueException("" + (name_prefix or "data") + ".build-system must be object", value=data__buildsystem, name="" + (name_prefix or "data") + ".build-system", definition={'type': 'object', 'description': 'Table used to store build-related data', 'additionalProperties': False, 'properties': {'requires': {'type': 'array', '$$description': ['List of dependencies in the :pep:`508` format required to execute the build', 'system. Please notice that the resulting dependency graph', '**MUST NOT contain cycles**'], 'items': {'type': 'string'}}, 'build-backend': {'type': 'string', 'description': 'Python object that will be used to perform the build according to :pep:`517`', 'format': 'pep517-backend-reference'}, 'backend-path': {'type': 'array', '$$description': ['List of directories to be prepended to ``sys.path`` when loading the', 'back-end, and running its hooks'], 'items': {'type': 'string', '$comment': 'Should be a path (TODO: enforce it with format?)'}}}, 'required': ['requires']}, rule='type') + data__buildsystem_is_dict = isinstance(data__buildsystem, dict) + if data__buildsystem_is_dict: + data__buildsystem_len = len(data__buildsystem) + if not all(prop in data__buildsystem for prop in ['requires']): + raise JsonSchemaValueException("" + (name_prefix or "data") + ".build-system must contain ['requires'] properties", value=data__buildsystem, name="" + (name_prefix or "data") + ".build-system", definition={'type': 'object', 'description': 'Table used to store build-related data', 'additionalProperties': False, 'properties': {'requires': {'type': 'array', '$$description': ['List of dependencies in the :pep:`508` format required to execute the build', 'system. Please notice that the resulting dependency graph', '**MUST NOT contain cycles**'], 'items': {'type': 'string'}}, 'build-backend': {'type': 'string', 'description': 'Python object that will be used to perform the build according to :pep:`517`', 'format': 'pep517-backend-reference'}, 'backend-path': {'type': 'array', '$$description': ['List of directories to be prepended to ``sys.path`` when loading the', 'back-end, and running its hooks'], 'items': {'type': 'string', '$comment': 'Should be a path (TODO: enforce it with format?)'}}}, 'required': ['requires']}, rule='required') + data__buildsystem_keys = set(data__buildsystem.keys()) + if "requires" in data__buildsystem_keys: + data__buildsystem_keys.remove("requires") + data__buildsystem__requires = data__buildsystem["requires"] + if not isinstance(data__buildsystem__requires, (list, tuple)): + raise JsonSchemaValueException("" + (name_prefix or "data") + ".build-system.requires must be array", value=data__buildsystem__requires, name="" + (name_prefix or "data") + ".build-system.requires", definition={'type': 'array', '$$description': ['List of dependencies in the :pep:`508` format required to execute the build', 'system. Please notice that the resulting dependency graph', '**MUST NOT contain cycles**'], 'items': {'type': 'string'}}, rule='type') + data__buildsystem__requires_is_list = isinstance(data__buildsystem__requires, (list, tuple)) + if data__buildsystem__requires_is_list: + data__buildsystem__requires_len = len(data__buildsystem__requires) + for data__buildsystem__requires_x, data__buildsystem__requires_item in enumerate(data__buildsystem__requires): + if not isinstance(data__buildsystem__requires_item, (str)): + raise JsonSchemaValueException("" + (name_prefix or "data") + ".build-system.requires[{data__buildsystem__requires_x}]".format(**locals()) + " must be string", value=data__buildsystem__requires_item, name="" + (name_prefix or "data") + ".build-system.requires[{data__buildsystem__requires_x}]".format(**locals()) + "", definition={'type': 'string'}, rule='type') + if "build-backend" in data__buildsystem_keys: + data__buildsystem_keys.remove("build-backend") + data__buildsystem__buildbackend = data__buildsystem["build-backend"] + if not isinstance(data__buildsystem__buildbackend, (str)): + raise JsonSchemaValueException("" + (name_prefix or "data") + ".build-system.build-backend must be string", value=data__buildsystem__buildbackend, name="" + (name_prefix or "data") + ".build-system.build-backend", definition={'type': 'string', 'description': 'Python object that will be used to perform the build according to :pep:`517`', 'format': 'pep517-backend-reference'}, rule='type') + if isinstance(data__buildsystem__buildbackend, str): + if not custom_formats["pep517-backend-reference"](data__buildsystem__buildbackend): + raise JsonSchemaValueException("" + (name_prefix or "data") + ".build-system.build-backend must be pep517-backend-reference", value=data__buildsystem__buildbackend, name="" + (name_prefix or "data") + ".build-system.build-backend", definition={'type': 'string', 'description': 'Python object that will be used to perform the build according to :pep:`517`', 'format': 'pep517-backend-reference'}, rule='format') + if "backend-path" in data__buildsystem_keys: + data__buildsystem_keys.remove("backend-path") + data__buildsystem__backendpath = data__buildsystem["backend-path"] + if not isinstance(data__buildsystem__backendpath, (list, tuple)): + raise JsonSchemaValueException("" + (name_prefix or "data") + ".build-system.backend-path must be array", value=data__buildsystem__backendpath, name="" + (name_prefix or "data") + ".build-system.backend-path", definition={'type': 'array', '$$description': ['List of directories to be prepended to ``sys.path`` when loading the', 'back-end, and running its hooks'], 'items': {'type': 'string', '$comment': 'Should be a path (TODO: enforce it with format?)'}}, rule='type') + data__buildsystem__backendpath_is_list = isinstance(data__buildsystem__backendpath, (list, tuple)) + if data__buildsystem__backendpath_is_list: + data__buildsystem__backendpath_len = len(data__buildsystem__backendpath) + for data__buildsystem__backendpath_x, data__buildsystem__backendpath_item in enumerate(data__buildsystem__backendpath): + if not isinstance(data__buildsystem__backendpath_item, (str)): + raise JsonSchemaValueException("" + (name_prefix or "data") + ".build-system.backend-path[{data__buildsystem__backendpath_x}]".format(**locals()) + " must be string", value=data__buildsystem__backendpath_item, name="" + (name_prefix or "data") + ".build-system.backend-path[{data__buildsystem__backendpath_x}]".format(**locals()) + "", definition={'type': 'string', '$comment': 'Should be a path (TODO: enforce it with format?)'}, rule='type') + if data__buildsystem_keys: + raise JsonSchemaValueException("" + (name_prefix or "data") + ".build-system must not contain "+str(data__buildsystem_keys)+" properties", value=data__buildsystem, name="" + (name_prefix or "data") + ".build-system", definition={'type': 'object', 'description': 'Table used to store build-related data', 'additionalProperties': False, 'properties': {'requires': {'type': 'array', '$$description': ['List of dependencies in the :pep:`508` format required to execute the build', 'system. Please notice that the resulting dependency graph', '**MUST NOT contain cycles**'], 'items': {'type': 'string'}}, 'build-backend': {'type': 'string', 'description': 'Python object that will be used to perform the build according to :pep:`517`', 'format': 'pep517-backend-reference'}, 'backend-path': {'type': 'array', '$$description': ['List of directories to be prepended to ``sys.path`` when loading the', 'back-end, and running its hooks'], 'items': {'type': 'string', '$comment': 'Should be a path (TODO: enforce it with format?)'}}}, 'required': ['requires']}, rule='additionalProperties') + if "project" in data_keys: + data_keys.remove("project") + data__project = data["project"] + validate_https___packaging_python_org_en_latest_specifications_declaring_project_metadata(data__project, custom_formats, (name_prefix or "data") + ".project") + if "tool" in data_keys: + data_keys.remove("tool") + data__tool = data["tool"] + if not isinstance(data__tool, (dict)): + raise JsonSchemaValueException("" + (name_prefix or "data") + ".tool must be object", value=data__tool, name="" + (name_prefix or "data") + ".tool", definition={'type': 'object', 'properties': {'distutils': {'$schema': 'http://json-schema.org/draft-07/schema', '$id': 'https://docs.python.org/3/install/', 'title': '``tool.distutils`` table', '$$description': ['Originally, ``distutils`` allowed developers to configure arguments for', '``setup.py`` scripts via `distutils configuration files', '`_.', '``tool.distutils`` subtables could be used with the same purpose', '(NOT CURRENTLY IMPLEMENTED).'], 'type': 'object', 'properties': {'global': {'type': 'object', 'description': 'Global options applied to all ``distutils`` commands'}}, 'patternProperties': {'.+': {'type': 'object'}}, '$comment': 'TODO: Is there a practical way of making this schema more specific?'}, 'setuptools': {'$schema': 'http://json-schema.org/draft-07/schema', '$id': 'https://setuptools.pypa.io/en/latest/references/keywords.html', 'title': '``tool.setuptools`` table', '$$description': ['Please notice for the time being the ``setuptools`` project does not specify', 'a way of configuring builds via ``pyproject.toml``.', 'Therefore this schema should be taken just as a *"thought experiment"* on how', 'this *might be done*, by following the principles established in', '`ini2toml `_.', 'It considers only ``setuptools`` `parameters', '`_', 'that can currently be configured via ``setup.cfg`` and are not covered by :pep:`621`', 'but intentionally excludes ``dependency_links`` and ``setup_requires``.', 'NOTE: ``scripts`` was renamed to ``script-files`` to avoid confusion with', 'entry-point based scripts (defined in :pep:`621`).'], 'type': 'object', 'additionalProperties': False, 'properties': {'platforms': {'type': 'array', 'items': {'type': 'string'}}, 'provides': {'$$description': ['Package and virtual package names contained within this package', '**(not supported by pip)**'], 'type': 'array', 'items': {'type': 'string', 'format': 'pep508-identifier'}}, 'obsoletes': {'$$description': ['Packages which this package renders obsolete', '**(not supported by pip)**'], 'type': 'array', 'items': {'type': 'string', 'format': 'pep508-identifier'}}, 'zip-safe': {'description': 'Whether the project can be safely installed and run from a zip file.', 'type': 'boolean'}, 'script-files': {'description': 'Legacy way of defining scripts (entry-points are preferred).', 'type': 'array', 'items': {'type': 'string'}, '$comment': 'TODO: is this field deprecated/should be removed?'}, 'eager-resources': {'$$description': ['Resources that should be extracted together, if any of them is needed,', 'or if any C extensions included in the project are imported.'], 'type': 'array', 'items': {'type': 'string'}}, 'packages': {'$$description': ['Packages that should be included in the distribution.', 'It can be given either as a list of package identifiers', 'or as a ``dict``-like structure with a single key ``find``', 'which corresponds to a dynamic call to', '``setuptools.config.expand.find_packages`` function.', 'The ``find`` key is associated with a nested ``dict``-like structure that can', 'contain ``where``, ``include``, ``exclude`` and ``namespaces`` keys,', 'mimicking the keyword arguments of the associated function.'], 'oneOf': [{'title': 'Array of Python package identifiers', 'type': 'array', 'items': {'$ref': '#/definitions/package-name'}}, {'$ref': '#/definitions/find-directive'}]}, 'package-dir': {'$$description': [':class:`dict`-like structure mapping from package names to directories where their', 'code can be found.', 'The empty string (as key) means that all packages are contained inside', 'the given directory will be included in the distribution.'], 'type': 'object', 'additionalProperties': False, 'propertyNames': {'oneOf': [{'const': ''}, {'$ref': '#/definitions/package-name'}]}, 'patternProperties': {'^.*$': {'type': 'string'}}}, 'package-data': {'$$description': ['Mapping from package names to lists of glob patterns.', 'Usually this option is not needed when using ``include-package-data = true``', 'For more information on how to include data files, check ``setuptools`` `docs', '`_.'], 'type': 'object', 'additionalProperties': False, 'propertyNames': {'oneOf': [{'format': 'python-module-name'}, {'const': '*'}]}, 'patternProperties': {'^.*$': {'type': 'array', 'items': {'type': 'string'}}}}, 'include-package-data': {'$$description': ['Automatically include any data files inside the package directories', 'that are specified by ``MANIFEST.in``', 'For more information on how to include data files, check ``setuptools`` `docs', '`_.'], 'type': 'boolean'}, 'exclude-package-data': {'$$description': ['Mapping from package names to lists of glob patterns that should be excluded', 'For more information on how to include data files, check ``setuptools`` `docs', '`_.'], 'type': 'object', 'additionalProperties': False, 'propertyNames': {'oneOf': [{'format': 'python-module-name'}, {'const': '*'}]}, 'patternProperties': {'^.*$': {'type': 'array', 'items': {'type': 'string'}}}}, 'namespace-packages': {'type': 'array', 'items': {'type': 'string', 'format': 'python-module-name'}, '$comment': 'https://setuptools.pypa.io/en/latest/userguide/package_discovery.html'}, 'py-modules': {'description': 'Modules that setuptools will manipulate', 'type': 'array', 'items': {'type': 'string', 'format': 'python-module-name'}, '$comment': 'TODO: clarify the relationship with ``packages``'}, 'data-files': {'$$description': ['**DEPRECATED**: dict-like structure where each key represents a directory and', 'the value is a list of glob patterns that should be installed in them.', "Please notice this don't work with wheels. See `data files support", '`_'], 'type': 'object', 'patternProperties': {'^.*$': {'type': 'array', 'items': {'type': 'string'}}}}, 'cmdclass': {'$$description': ['Mapping of distutils-style command names to ``setuptools.Command`` subclasses', 'which in turn should be represented by strings with a qualified class name', '(i.e., "dotted" form with module), e.g.::\n\n', ' cmdclass = {mycmd = "pkg.subpkg.module.CommandClass"}\n\n', 'The command class should be a directly defined at the top-level of the', 'containing module (no class nesting).'], 'type': 'object', 'patternProperties': {'^.*$': {'type': 'string', 'format': 'python-qualified-identifier'}}}, 'license-files': {'type': 'array', 'items': {'type': 'string'}, '$$description': ['PROVISIONAL: List of glob patterns for all license files being distributed.', '(might become standard with PEP 639).', "By default: ``['LICEN[CS]E*', 'COPYING*', 'NOTICE*', 'AUTHORS*']``"], '$comment': 'TODO: revise if PEP 639 is accepted. Probably ``project.license-files``?'}, 'dynamic': {'type': 'object', 'description': 'Instructions for loading :pep:`621`-related metadata dynamically', 'additionalProperties': False, 'properties': {'version': {'$$description': ['A version dynamically loaded via either the ``attr:`` or ``file:``', 'directives. Please make sure the given file or attribute respects :pep:`440`.'], 'oneOf': [{'$ref': '#/definitions/attr-directive'}, {'$ref': '#/definitions/file-directive'}]}, 'classifiers': {'$ref': '#/definitions/file-directive'}, 'description': {'$ref': '#/definitions/file-directive'}, 'dependencies': {'$ref': '#/definitions/file-directive'}, 'entry-points': {'$ref': '#/definitions/file-directive'}, 'optional-dependencies': {'type': 'object', 'propertyNames': {'format': 'python-identifier'}, 'additionalProperties': False, 'patternProperties': {'.+': {'$ref': '#/definitions/file-directive'}}}, 'readme': {'anyOf': [{'$ref': '#/definitions/file-directive'}, {'properties': {'content-type': {'type': 'string'}}}], 'required': ['file']}}}}, 'definitions': {'package-name': {'$id': '#/definitions/package-name', 'title': 'Valid package name', 'description': 'Valid package name (importable or PEP 561).', 'type': 'string', 'anyOf': [{'format': 'python-module-name'}, {'format': 'pep561-stub-name'}]}, 'file-directive': {'$id': '#/definitions/file-directive', 'title': "'file:' directive", 'description': 'Value is read from a file (or list of files and then concatenated)', 'type': 'object', 'additionalProperties': False, 'properties': {'file': {'oneOf': [{'type': 'string'}, {'type': 'array', 'items': {'type': 'string'}}]}}, 'required': ['file']}, 'attr-directive': {'title': "'attr:' directive", '$id': '#/definitions/attr-directive', '$$description': ['Value is read from a module attribute. Supports callables and iterables;', 'unsupported types are cast via ``str()``'], 'type': 'object', 'additionalProperties': False, 'properties': {'attr': {'type': 'string'}}, 'required': ['attr']}, 'find-directive': {'$id': '#/definitions/find-directive', 'title': "'find:' directive", 'type': 'object', 'additionalProperties': False, 'properties': {'find': {'type': 'object', '$$description': ['Dynamic `package discovery', '`_.'], 'additionalProperties': False, 'properties': {'where': {'description': 'Directories to be searched for packages (Unix-style relative path)', 'type': 'array', 'items': {'type': 'string'}}, 'exclude': {'type': 'array', '$$description': ['Exclude packages that match the values listed in this field.', "Can container shell-style wildcards (e.g. ``'pkg.*'``)"], 'items': {'type': 'string'}}, 'include': {'type': 'array', '$$description': ['Restrict the found packages to just the ones listed in this field.', "Can container shell-style wildcards (e.g. ``'pkg.*'``)"], 'items': {'type': 'string'}}, 'namespaces': {'type': 'boolean', '$$description': ['When ``True``, directories without a ``__init__.py`` file will also', 'be scanned for :pep:`420`-style implicit namespaces']}}}}}}}}}, rule='type') + data__tool_is_dict = isinstance(data__tool, dict) + if data__tool_is_dict: + data__tool_keys = set(data__tool.keys()) + if "distutils" in data__tool_keys: + data__tool_keys.remove("distutils") + data__tool__distutils = data__tool["distutils"] + validate_https___docs_python_org_3_install(data__tool__distutils, custom_formats, (name_prefix or "data") + ".tool.distutils") + if "setuptools" in data__tool_keys: + data__tool_keys.remove("setuptools") + data__tool__setuptools = data__tool["setuptools"] + validate_https___setuptools_pypa_io_en_latest_references_keywords_html(data__tool__setuptools, custom_formats, (name_prefix or "data") + ".tool.setuptools") + if data_keys: + raise JsonSchemaValueException("" + (name_prefix or "data") + " must not contain "+str(data_keys)+" properties", value=data, name="" + (name_prefix or "data") + "", definition={'$schema': 'http://json-schema.org/draft-07/schema', '$id': 'https://packaging.python.org/en/latest/specifications/declaring-build-dependencies/', 'title': 'Data structure for ``pyproject.toml`` files', '$$description': ['File format containing build-time configurations for the Python ecosystem. ', ':pep:`517` initially defined a build-system independent format for source trees', 'which was complemented by :pep:`518` to provide a way of specifying dependencies ', 'for building Python projects.', 'Please notice the ``project`` table (as initially defined in :pep:`621`) is not included', 'in this schema and should be considered separately.'], 'type': 'object', 'additionalProperties': False, 'properties': {'build-system': {'type': 'object', 'description': 'Table used to store build-related data', 'additionalProperties': False, 'properties': {'requires': {'type': 'array', '$$description': ['List of dependencies in the :pep:`508` format required to execute the build', 'system. Please notice that the resulting dependency graph', '**MUST NOT contain cycles**'], 'items': {'type': 'string'}}, 'build-backend': {'type': 'string', 'description': 'Python object that will be used to perform the build according to :pep:`517`', 'format': 'pep517-backend-reference'}, 'backend-path': {'type': 'array', '$$description': ['List of directories to be prepended to ``sys.path`` when loading the', 'back-end, and running its hooks'], 'items': {'type': 'string', '$comment': 'Should be a path (TODO: enforce it with format?)'}}}, 'required': ['requires']}, 'project': {'$schema': 'http://json-schema.org/draft-07/schema', '$id': 'https://packaging.python.org/en/latest/specifications/declaring-project-metadata/', 'title': 'Package metadata stored in the ``project`` table', '$$description': ['Data structure for the **project** table inside ``pyproject.toml``', '(as initially defined in :pep:`621`)'], 'type': 'object', 'properties': {'name': {'type': 'string', 'description': 'The name (primary identifier) of the project. MUST be statically defined.', 'format': 'pep508-identifier'}, 'version': {'type': 'string', 'description': 'The version of the project as supported by :pep:`440`.', 'format': 'pep440'}, 'description': {'type': 'string', '$$description': ['The `summary description of the project', '`_']}, 'readme': {'$$description': ['`Full/detailed description of the project in the form of a README', '`_', "with meaning similar to the one defined in `core metadata's Description", '`_'], 'oneOf': [{'type': 'string', '$$description': ['Relative path to a text file (UTF-8) containing the full description', 'of the project. If the file path ends in case-insensitive ``.md`` or', '``.rst`` suffixes, then the content-type is respectively', '``text/markdown`` or ``text/x-rst``']}, {'type': 'object', 'allOf': [{'anyOf': [{'properties': {'file': {'type': 'string', '$$description': ['Relative path to a text file containing the full description', 'of the project.']}}, 'required': ['file']}, {'properties': {'text': {'type': 'string', 'description': 'Full text describing the project.'}}, 'required': ['text']}]}, {'properties': {'content-type': {'type': 'string', '$$description': ['Content-type (:rfc:`1341`) of the full description', '(e.g. ``text/markdown``). The ``charset`` parameter is assumed', 'UTF-8 when not present.'], '$comment': 'TODO: add regex pattern or format?'}}, 'required': ['content-type']}]}]}, 'requires-python': {'type': 'string', 'format': 'pep508-versionspec', '$$description': ['`The Python version requirements of the project', '`_.']}, 'license': {'description': '`Project license `_.', 'oneOf': [{'properties': {'file': {'type': 'string', '$$description': ['Relative path to the file (UTF-8) which contains the license for the', 'project.']}}, 'required': ['file']}, {'properties': {'text': {'type': 'string', '$$description': ['The license of the project whose meaning is that of the', '`License field from the core metadata', '`_.']}}, 'required': ['text']}]}, 'authors': {'type': 'array', 'items': {'$ref': '#/definitions/author'}, '$$description': ["The people or organizations considered to be the 'authors' of the project.", 'The exact meaning is open to interpretation (e.g. original or primary authors,', 'current maintainers, or owners of the package).']}, 'maintainers': {'type': 'array', 'items': {'$ref': '#/definitions/author'}, '$$description': ["The people or organizations considered to be the 'maintainers' of the project.", 'Similarly to ``authors``, the exact meaning is open to interpretation.']}, 'keywords': {'type': 'array', 'items': {'type': 'string'}, 'description': 'List of keywords to assist searching for the distribution in a larger catalog.'}, 'classifiers': {'type': 'array', 'items': {'type': 'string', 'format': 'trove-classifier', 'description': '`PyPI classifier `_.'}, '$$description': ['`Trove classifiers `_', 'which apply to the project.']}, 'urls': {'type': 'object', 'description': 'URLs associated with the project in the form ``label => value``.', 'additionalProperties': False, 'patternProperties': {'^.+$': {'type': 'string', 'format': 'url'}}}, 'scripts': {'$ref': '#/definitions/entry-point-group', '$$description': ['Instruct the installer to create command-line wrappers for the given', '`entry points `_.']}, 'gui-scripts': {'$ref': '#/definitions/entry-point-group', '$$description': ['Instruct the installer to create GUI wrappers for the given', '`entry points `_.', 'The difference between ``scripts`` and ``gui-scripts`` is only relevant in', 'Windows.']}, 'entry-points': {'$$description': ['Instruct the installer to expose the given modules/functions via', '``entry-point`` discovery mechanism (useful for plugins).', 'More information available in the `Python packaging guide', '`_.'], 'propertyNames': {'format': 'python-entrypoint-group'}, 'additionalProperties': False, 'patternProperties': {'^.+$': {'$ref': '#/definitions/entry-point-group'}}}, 'dependencies': {'type': 'array', 'description': 'Project (mandatory) dependencies.', 'items': {'$ref': '#/definitions/dependency'}}, 'optional-dependencies': {'type': 'object', 'description': 'Optional dependency for the project', 'propertyNames': {'format': 'pep508-identifier'}, 'additionalProperties': False, 'patternProperties': {'^.+$': {'type': 'array', 'items': {'$ref': '#/definitions/dependency'}}}}, 'dynamic': {'type': 'array', '$$description': ['Specifies which fields are intentionally unspecified and expected to be', 'dynamically provided by build tools'], 'items': {'enum': ['version', 'description', 'readme', 'requires-python', 'license', 'authors', 'maintainers', 'keywords', 'classifiers', 'urls', 'scripts', 'gui-scripts', 'entry-points', 'dependencies', 'optional-dependencies']}}}, 'required': ['name'], 'additionalProperties': False, 'if': {'not': {'required': ['dynamic'], 'properties': {'dynamic': {'contains': {'const': 'version'}, '$$description': ['version is listed in ``dynamic``']}}}, '$$comment': ['According to :pep:`621`:', ' If the core metadata specification lists a field as "Required", then', ' the metadata MUST specify the field statically or list it in dynamic', 'In turn, `core metadata`_ defines:', ' The required fields are: Metadata-Version, Name, Version.', ' All the other fields are optional.', 'Since ``Metadata-Version`` is defined by the build back-end, ``name`` and', '``version`` are the only mandatory information in ``pyproject.toml``.', '.. _core metadata: https://packaging.python.org/specifications/core-metadata/']}, 'then': {'required': ['version'], '$$description': ['version should be statically defined in the ``version`` field']}, 'definitions': {'author': {'$id': '#/definitions/author', 'title': 'Author or Maintainer', '$comment': 'https://peps.python.org/pep-0621/#authors-maintainers', 'type': 'object', 'additionalProperties': False, 'properties': {'name': {'type': 'string', '$$description': ['MUST be a valid email name, i.e. whatever can be put as a name, before an', 'email, in :rfc:`822`.']}, 'email': {'type': 'string', 'format': 'idn-email', 'description': 'MUST be a valid email address'}}}, 'entry-point-group': {'$id': '#/definitions/entry-point-group', 'title': 'Entry-points', 'type': 'object', '$$description': ['Entry-points are grouped together to indicate what sort of capabilities they', 'provide.', 'See the `packaging guides', '`_', 'and `setuptools docs', '`_', 'for more information.'], 'propertyNames': {'format': 'python-entrypoint-name'}, 'additionalProperties': False, 'patternProperties': {'^.+$': {'type': 'string', '$$description': ['Reference to a Python object. It is either in the form', '``importable.module``, or ``importable.module:object.attr``.'], 'format': 'python-entrypoint-reference', '$comment': 'https://packaging.python.org/specifications/entry-points/'}}}, 'dependency': {'$id': '#/definitions/dependency', 'title': 'Dependency', 'type': 'string', 'description': 'Project dependency specification according to PEP 508', 'format': 'pep508'}}}, 'tool': {'type': 'object', 'properties': {'distutils': {'$schema': 'http://json-schema.org/draft-07/schema', '$id': 'https://docs.python.org/3/install/', 'title': '``tool.distutils`` table', '$$description': ['Originally, ``distutils`` allowed developers to configure arguments for', '``setup.py`` scripts via `distutils configuration files', '`_.', '``tool.distutils`` subtables could be used with the same purpose', '(NOT CURRENTLY IMPLEMENTED).'], 'type': 'object', 'properties': {'global': {'type': 'object', 'description': 'Global options applied to all ``distutils`` commands'}}, 'patternProperties': {'.+': {'type': 'object'}}, '$comment': 'TODO: Is there a practical way of making this schema more specific?'}, 'setuptools': {'$schema': 'http://json-schema.org/draft-07/schema', '$id': 'https://setuptools.pypa.io/en/latest/references/keywords.html', 'title': '``tool.setuptools`` table', '$$description': ['Please notice for the time being the ``setuptools`` project does not specify', 'a way of configuring builds via ``pyproject.toml``.', 'Therefore this schema should be taken just as a *"thought experiment"* on how', 'this *might be done*, by following the principles established in', '`ini2toml `_.', 'It considers only ``setuptools`` `parameters', '`_', 'that can currently be configured via ``setup.cfg`` and are not covered by :pep:`621`', 'but intentionally excludes ``dependency_links`` and ``setup_requires``.', 'NOTE: ``scripts`` was renamed to ``script-files`` to avoid confusion with', 'entry-point based scripts (defined in :pep:`621`).'], 'type': 'object', 'additionalProperties': False, 'properties': {'platforms': {'type': 'array', 'items': {'type': 'string'}}, 'provides': {'$$description': ['Package and virtual package names contained within this package', '**(not supported by pip)**'], 'type': 'array', 'items': {'type': 'string', 'format': 'pep508-identifier'}}, 'obsoletes': {'$$description': ['Packages which this package renders obsolete', '**(not supported by pip)**'], 'type': 'array', 'items': {'type': 'string', 'format': 'pep508-identifier'}}, 'zip-safe': {'description': 'Whether the project can be safely installed and run from a zip file.', 'type': 'boolean'}, 'script-files': {'description': 'Legacy way of defining scripts (entry-points are preferred).', 'type': 'array', 'items': {'type': 'string'}, '$comment': 'TODO: is this field deprecated/should be removed?'}, 'eager-resources': {'$$description': ['Resources that should be extracted together, if any of them is needed,', 'or if any C extensions included in the project are imported.'], 'type': 'array', 'items': {'type': 'string'}}, 'packages': {'$$description': ['Packages that should be included in the distribution.', 'It can be given either as a list of package identifiers', 'or as a ``dict``-like structure with a single key ``find``', 'which corresponds to a dynamic call to', '``setuptools.config.expand.find_packages`` function.', 'The ``find`` key is associated with a nested ``dict``-like structure that can', 'contain ``where``, ``include``, ``exclude`` and ``namespaces`` keys,', 'mimicking the keyword arguments of the associated function.'], 'oneOf': [{'title': 'Array of Python package identifiers', 'type': 'array', 'items': {'$ref': '#/definitions/package-name'}}, {'$ref': '#/definitions/find-directive'}]}, 'package-dir': {'$$description': [':class:`dict`-like structure mapping from package names to directories where their', 'code can be found.', 'The empty string (as key) means that all packages are contained inside', 'the given directory will be included in the distribution.'], 'type': 'object', 'additionalProperties': False, 'propertyNames': {'oneOf': [{'const': ''}, {'$ref': '#/definitions/package-name'}]}, 'patternProperties': {'^.*$': {'type': 'string'}}}, 'package-data': {'$$description': ['Mapping from package names to lists of glob patterns.', 'Usually this option is not needed when using ``include-package-data = true``', 'For more information on how to include data files, check ``setuptools`` `docs', '`_.'], 'type': 'object', 'additionalProperties': False, 'propertyNames': {'oneOf': [{'format': 'python-module-name'}, {'const': '*'}]}, 'patternProperties': {'^.*$': {'type': 'array', 'items': {'type': 'string'}}}}, 'include-package-data': {'$$description': ['Automatically include any data files inside the package directories', 'that are specified by ``MANIFEST.in``', 'For more information on how to include data files, check ``setuptools`` `docs', '`_.'], 'type': 'boolean'}, 'exclude-package-data': {'$$description': ['Mapping from package names to lists of glob patterns that should be excluded', 'For more information on how to include data files, check ``setuptools`` `docs', '`_.'], 'type': 'object', 'additionalProperties': False, 'propertyNames': {'oneOf': [{'format': 'python-module-name'}, {'const': '*'}]}, 'patternProperties': {'^.*$': {'type': 'array', 'items': {'type': 'string'}}}}, 'namespace-packages': {'type': 'array', 'items': {'type': 'string', 'format': 'python-module-name'}, '$comment': 'https://setuptools.pypa.io/en/latest/userguide/package_discovery.html'}, 'py-modules': {'description': 'Modules that setuptools will manipulate', 'type': 'array', 'items': {'type': 'string', 'format': 'python-module-name'}, '$comment': 'TODO: clarify the relationship with ``packages``'}, 'data-files': {'$$description': ['**DEPRECATED**: dict-like structure where each key represents a directory and', 'the value is a list of glob patterns that should be installed in them.', "Please notice this don't work with wheels. See `data files support", '`_'], 'type': 'object', 'patternProperties': {'^.*$': {'type': 'array', 'items': {'type': 'string'}}}}, 'cmdclass': {'$$description': ['Mapping of distutils-style command names to ``setuptools.Command`` subclasses', 'which in turn should be represented by strings with a qualified class name', '(i.e., "dotted" form with module), e.g.::\n\n', ' cmdclass = {mycmd = "pkg.subpkg.module.CommandClass"}\n\n', 'The command class should be a directly defined at the top-level of the', 'containing module (no class nesting).'], 'type': 'object', 'patternProperties': {'^.*$': {'type': 'string', 'format': 'python-qualified-identifier'}}}, 'license-files': {'type': 'array', 'items': {'type': 'string'}, '$$description': ['PROVISIONAL: List of glob patterns for all license files being distributed.', '(might become standard with PEP 639).', "By default: ``['LICEN[CS]E*', 'COPYING*', 'NOTICE*', 'AUTHORS*']``"], '$comment': 'TODO: revise if PEP 639 is accepted. Probably ``project.license-files``?'}, 'dynamic': {'type': 'object', 'description': 'Instructions for loading :pep:`621`-related metadata dynamically', 'additionalProperties': False, 'properties': {'version': {'$$description': ['A version dynamically loaded via either the ``attr:`` or ``file:``', 'directives. Please make sure the given file or attribute respects :pep:`440`.'], 'oneOf': [{'$ref': '#/definitions/attr-directive'}, {'$ref': '#/definitions/file-directive'}]}, 'classifiers': {'$ref': '#/definitions/file-directive'}, 'description': {'$ref': '#/definitions/file-directive'}, 'dependencies': {'$ref': '#/definitions/file-directive'}, 'entry-points': {'$ref': '#/definitions/file-directive'}, 'optional-dependencies': {'type': 'object', 'propertyNames': {'format': 'python-identifier'}, 'additionalProperties': False, 'patternProperties': {'.+': {'$ref': '#/definitions/file-directive'}}}, 'readme': {'anyOf': [{'$ref': '#/definitions/file-directive'}, {'properties': {'content-type': {'type': 'string'}}}], 'required': ['file']}}}}, 'definitions': {'package-name': {'$id': '#/definitions/package-name', 'title': 'Valid package name', 'description': 'Valid package name (importable or PEP 561).', 'type': 'string', 'anyOf': [{'format': 'python-module-name'}, {'format': 'pep561-stub-name'}]}, 'file-directive': {'$id': '#/definitions/file-directive', 'title': "'file:' directive", 'description': 'Value is read from a file (or list of files and then concatenated)', 'type': 'object', 'additionalProperties': False, 'properties': {'file': {'oneOf': [{'type': 'string'}, {'type': 'array', 'items': {'type': 'string'}}]}}, 'required': ['file']}, 'attr-directive': {'title': "'attr:' directive", '$id': '#/definitions/attr-directive', '$$description': ['Value is read from a module attribute. Supports callables and iterables;', 'unsupported types are cast via ``str()``'], 'type': 'object', 'additionalProperties': False, 'properties': {'attr': {'type': 'string'}}, 'required': ['attr']}, 'find-directive': {'$id': '#/definitions/find-directive', 'title': "'find:' directive", 'type': 'object', 'additionalProperties': False, 'properties': {'find': {'type': 'object', '$$description': ['Dynamic `package discovery', '`_.'], 'additionalProperties': False, 'properties': {'where': {'description': 'Directories to be searched for packages (Unix-style relative path)', 'type': 'array', 'items': {'type': 'string'}}, 'exclude': {'type': 'array', '$$description': ['Exclude packages that match the values listed in this field.', "Can container shell-style wildcards (e.g. ``'pkg.*'``)"], 'items': {'type': 'string'}}, 'include': {'type': 'array', '$$description': ['Restrict the found packages to just the ones listed in this field.', "Can container shell-style wildcards (e.g. ``'pkg.*'``)"], 'items': {'type': 'string'}}, 'namespaces': {'type': 'boolean', '$$description': ['When ``True``, directories without a ``__init__.py`` file will also', 'be scanned for :pep:`420`-style implicit namespaces']}}}}}}}}}}, 'project': {'$schema': 'http://json-schema.org/draft-07/schema', '$id': 'https://packaging.python.org/en/latest/specifications/declaring-project-metadata/', 'title': 'Package metadata stored in the ``project`` table', '$$description': ['Data structure for the **project** table inside ``pyproject.toml``', '(as initially defined in :pep:`621`)'], 'type': 'object', 'properties': {'name': {'type': 'string', 'description': 'The name (primary identifier) of the project. MUST be statically defined.', 'format': 'pep508-identifier'}, 'version': {'type': 'string', 'description': 'The version of the project as supported by :pep:`440`.', 'format': 'pep440'}, 'description': {'type': 'string', '$$description': ['The `summary description of the project', '`_']}, 'readme': {'$$description': ['`Full/detailed description of the project in the form of a README', '`_', "with meaning similar to the one defined in `core metadata's Description", '`_'], 'oneOf': [{'type': 'string', '$$description': ['Relative path to a text file (UTF-8) containing the full description', 'of the project. If the file path ends in case-insensitive ``.md`` or', '``.rst`` suffixes, then the content-type is respectively', '``text/markdown`` or ``text/x-rst``']}, {'type': 'object', 'allOf': [{'anyOf': [{'properties': {'file': {'type': 'string', '$$description': ['Relative path to a text file containing the full description', 'of the project.']}}, 'required': ['file']}, {'properties': {'text': {'type': 'string', 'description': 'Full text describing the project.'}}, 'required': ['text']}]}, {'properties': {'content-type': {'type': 'string', '$$description': ['Content-type (:rfc:`1341`) of the full description', '(e.g. ``text/markdown``). The ``charset`` parameter is assumed', 'UTF-8 when not present.'], '$comment': 'TODO: add regex pattern or format?'}}, 'required': ['content-type']}]}]}, 'requires-python': {'type': 'string', 'format': 'pep508-versionspec', '$$description': ['`The Python version requirements of the project', '`_.']}, 'license': {'description': '`Project license `_.', 'oneOf': [{'properties': {'file': {'type': 'string', '$$description': ['Relative path to the file (UTF-8) which contains the license for the', 'project.']}}, 'required': ['file']}, {'properties': {'text': {'type': 'string', '$$description': ['The license of the project whose meaning is that of the', '`License field from the core metadata', '`_.']}}, 'required': ['text']}]}, 'authors': {'type': 'array', 'items': {'$ref': '#/definitions/author'}, '$$description': ["The people or organizations considered to be the 'authors' of the project.", 'The exact meaning is open to interpretation (e.g. original or primary authors,', 'current maintainers, or owners of the package).']}, 'maintainers': {'type': 'array', 'items': {'$ref': '#/definitions/author'}, '$$description': ["The people or organizations considered to be the 'maintainers' of the project.", 'Similarly to ``authors``, the exact meaning is open to interpretation.']}, 'keywords': {'type': 'array', 'items': {'type': 'string'}, 'description': 'List of keywords to assist searching for the distribution in a larger catalog.'}, 'classifiers': {'type': 'array', 'items': {'type': 'string', 'format': 'trove-classifier', 'description': '`PyPI classifier `_.'}, '$$description': ['`Trove classifiers `_', 'which apply to the project.']}, 'urls': {'type': 'object', 'description': 'URLs associated with the project in the form ``label => value``.', 'additionalProperties': False, 'patternProperties': {'^.+$': {'type': 'string', 'format': 'url'}}}, 'scripts': {'$ref': '#/definitions/entry-point-group', '$$description': ['Instruct the installer to create command-line wrappers for the given', '`entry points `_.']}, 'gui-scripts': {'$ref': '#/definitions/entry-point-group', '$$description': ['Instruct the installer to create GUI wrappers for the given', '`entry points `_.', 'The difference between ``scripts`` and ``gui-scripts`` is only relevant in', 'Windows.']}, 'entry-points': {'$$description': ['Instruct the installer to expose the given modules/functions via', '``entry-point`` discovery mechanism (useful for plugins).', 'More information available in the `Python packaging guide', '`_.'], 'propertyNames': {'format': 'python-entrypoint-group'}, 'additionalProperties': False, 'patternProperties': {'^.+$': {'$ref': '#/definitions/entry-point-group'}}}, 'dependencies': {'type': 'array', 'description': 'Project (mandatory) dependencies.', 'items': {'$ref': '#/definitions/dependency'}}, 'optional-dependencies': {'type': 'object', 'description': 'Optional dependency for the project', 'propertyNames': {'format': 'pep508-identifier'}, 'additionalProperties': False, 'patternProperties': {'^.+$': {'type': 'array', 'items': {'$ref': '#/definitions/dependency'}}}}, 'dynamic': {'type': 'array', '$$description': ['Specifies which fields are intentionally unspecified and expected to be', 'dynamically provided by build tools'], 'items': {'enum': ['version', 'description', 'readme', 'requires-python', 'license', 'authors', 'maintainers', 'keywords', 'classifiers', 'urls', 'scripts', 'gui-scripts', 'entry-points', 'dependencies', 'optional-dependencies']}}}, 'required': ['name'], 'additionalProperties': False, 'if': {'not': {'required': ['dynamic'], 'properties': {'dynamic': {'contains': {'const': 'version'}, '$$description': ['version is listed in ``dynamic``']}}}, '$$comment': ['According to :pep:`621`:', ' If the core metadata specification lists a field as "Required", then', ' the metadata MUST specify the field statically or list it in dynamic', 'In turn, `core metadata`_ defines:', ' The required fields are: Metadata-Version, Name, Version.', ' All the other fields are optional.', 'Since ``Metadata-Version`` is defined by the build back-end, ``name`` and', '``version`` are the only mandatory information in ``pyproject.toml``.', '.. _core metadata: https://packaging.python.org/specifications/core-metadata/']}, 'then': {'required': ['version'], '$$description': ['version should be statically defined in the ``version`` field']}, 'definitions': {'author': {'$id': '#/definitions/author', 'title': 'Author or Maintainer', '$comment': 'https://peps.python.org/pep-0621/#authors-maintainers', 'type': 'object', 'additionalProperties': False, 'properties': {'name': {'type': 'string', '$$description': ['MUST be a valid email name, i.e. whatever can be put as a name, before an', 'email, in :rfc:`822`.']}, 'email': {'type': 'string', 'format': 'idn-email', 'description': 'MUST be a valid email address'}}}, 'entry-point-group': {'$id': '#/definitions/entry-point-group', 'title': 'Entry-points', 'type': 'object', '$$description': ['Entry-points are grouped together to indicate what sort of capabilities they', 'provide.', 'See the `packaging guides', '`_', 'and `setuptools docs', '`_', 'for more information.'], 'propertyNames': {'format': 'python-entrypoint-name'}, 'additionalProperties': False, 'patternProperties': {'^.+$': {'type': 'string', '$$description': ['Reference to a Python object. It is either in the form', '``importable.module``, or ``importable.module:object.attr``.'], 'format': 'python-entrypoint-reference', '$comment': 'https://packaging.python.org/specifications/entry-points/'}}}, 'dependency': {'$id': '#/definitions/dependency', 'title': 'Dependency', 'type': 'string', 'description': 'Project dependency specification according to PEP 508', 'format': 'pep508'}}}}, rule='additionalProperties') + return data + +def validate_https___setuptools_pypa_io_en_latest_references_keywords_html(data, custom_formats={}, name_prefix=None): + if not isinstance(data, (dict)): + raise JsonSchemaValueException("" + (name_prefix or "data") + " must be object", value=data, name="" + (name_prefix or "data") + "", definition={'$schema': 'http://json-schema.org/draft-07/schema', '$id': 'https://setuptools.pypa.io/en/latest/references/keywords.html', 'title': '``tool.setuptools`` table', '$$description': ['Please notice for the time being the ``setuptools`` project does not specify', 'a way of configuring builds via ``pyproject.toml``.', 'Therefore this schema should be taken just as a *"thought experiment"* on how', 'this *might be done*, by following the principles established in', '`ini2toml `_.', 'It considers only ``setuptools`` `parameters', '`_', 'that can currently be configured via ``setup.cfg`` and are not covered by :pep:`621`', 'but intentionally excludes ``dependency_links`` and ``setup_requires``.', 'NOTE: ``scripts`` was renamed to ``script-files`` to avoid confusion with', 'entry-point based scripts (defined in :pep:`621`).'], 'type': 'object', 'additionalProperties': False, 'properties': {'platforms': {'type': 'array', 'items': {'type': 'string'}}, 'provides': {'$$description': ['Package and virtual package names contained within this package', '**(not supported by pip)**'], 'type': 'array', 'items': {'type': 'string', 'format': 'pep508-identifier'}}, 'obsoletes': {'$$description': ['Packages which this package renders obsolete', '**(not supported by pip)**'], 'type': 'array', 'items': {'type': 'string', 'format': 'pep508-identifier'}}, 'zip-safe': {'description': 'Whether the project can be safely installed and run from a zip file.', 'type': 'boolean'}, 'script-files': {'description': 'Legacy way of defining scripts (entry-points are preferred).', 'type': 'array', 'items': {'type': 'string'}, '$comment': 'TODO: is this field deprecated/should be removed?'}, 'eager-resources': {'$$description': ['Resources that should be extracted together, if any of them is needed,', 'or if any C extensions included in the project are imported.'], 'type': 'array', 'items': {'type': 'string'}}, 'packages': {'$$description': ['Packages that should be included in the distribution.', 'It can be given either as a list of package identifiers', 'or as a ``dict``-like structure with a single key ``find``', 'which corresponds to a dynamic call to', '``setuptools.config.expand.find_packages`` function.', 'The ``find`` key is associated with a nested ``dict``-like structure that can', 'contain ``where``, ``include``, ``exclude`` and ``namespaces`` keys,', 'mimicking the keyword arguments of the associated function.'], 'oneOf': [{'title': 'Array of Python package identifiers', 'type': 'array', 'items': {'$id': '#/definitions/package-name', 'title': 'Valid package name', 'description': 'Valid package name (importable or PEP 561).', 'type': 'string', 'anyOf': [{'format': 'python-module-name'}, {'format': 'pep561-stub-name'}]}}, {'$id': '#/definitions/find-directive', 'title': "'find:' directive", 'type': 'object', 'additionalProperties': False, 'properties': {'find': {'type': 'object', '$$description': ['Dynamic `package discovery', '`_.'], 'additionalProperties': False, 'properties': {'where': {'description': 'Directories to be searched for packages (Unix-style relative path)', 'type': 'array', 'items': {'type': 'string'}}, 'exclude': {'type': 'array', '$$description': ['Exclude packages that match the values listed in this field.', "Can container shell-style wildcards (e.g. ``'pkg.*'``)"], 'items': {'type': 'string'}}, 'include': {'type': 'array', '$$description': ['Restrict the found packages to just the ones listed in this field.', "Can container shell-style wildcards (e.g. ``'pkg.*'``)"], 'items': {'type': 'string'}}, 'namespaces': {'type': 'boolean', '$$description': ['When ``True``, directories without a ``__init__.py`` file will also', 'be scanned for :pep:`420`-style implicit namespaces']}}}}}]}, 'package-dir': {'$$description': [':class:`dict`-like structure mapping from package names to directories where their', 'code can be found.', 'The empty string (as key) means that all packages are contained inside', 'the given directory will be included in the distribution.'], 'type': 'object', 'additionalProperties': False, 'propertyNames': {'oneOf': [{'const': ''}, {'$id': '#/definitions/package-name', 'title': 'Valid package name', 'description': 'Valid package name (importable or PEP 561).', 'type': 'string', 'anyOf': [{'format': 'python-module-name'}, {'format': 'pep561-stub-name'}]}]}, 'patternProperties': {'^.*$': {'type': 'string'}}}, 'package-data': {'$$description': ['Mapping from package names to lists of glob patterns.', 'Usually this option is not needed when using ``include-package-data = true``', 'For more information on how to include data files, check ``setuptools`` `docs', '`_.'], 'type': 'object', 'additionalProperties': False, 'propertyNames': {'oneOf': [{'format': 'python-module-name'}, {'const': '*'}]}, 'patternProperties': {'^.*$': {'type': 'array', 'items': {'type': 'string'}}}}, 'include-package-data': {'$$description': ['Automatically include any data files inside the package directories', 'that are specified by ``MANIFEST.in``', 'For more information on how to include data files, check ``setuptools`` `docs', '`_.'], 'type': 'boolean'}, 'exclude-package-data': {'$$description': ['Mapping from package names to lists of glob patterns that should be excluded', 'For more information on how to include data files, check ``setuptools`` `docs', '`_.'], 'type': 'object', 'additionalProperties': False, 'propertyNames': {'oneOf': [{'format': 'python-module-name'}, {'const': '*'}]}, 'patternProperties': {'^.*$': {'type': 'array', 'items': {'type': 'string'}}}}, 'namespace-packages': {'type': 'array', 'items': {'type': 'string', 'format': 'python-module-name'}, '$comment': 'https://setuptools.pypa.io/en/latest/userguide/package_discovery.html'}, 'py-modules': {'description': 'Modules that setuptools will manipulate', 'type': 'array', 'items': {'type': 'string', 'format': 'python-module-name'}, '$comment': 'TODO: clarify the relationship with ``packages``'}, 'data-files': {'$$description': ['**DEPRECATED**: dict-like structure where each key represents a directory and', 'the value is a list of glob patterns that should be installed in them.', "Please notice this don't work with wheels. See `data files support", '`_'], 'type': 'object', 'patternProperties': {'^.*$': {'type': 'array', 'items': {'type': 'string'}}}}, 'cmdclass': {'$$description': ['Mapping of distutils-style command names to ``setuptools.Command`` subclasses', 'which in turn should be represented by strings with a qualified class name', '(i.e., "dotted" form with module), e.g.::\n\n', ' cmdclass = {mycmd = "pkg.subpkg.module.CommandClass"}\n\n', 'The command class should be a directly defined at the top-level of the', 'containing module (no class nesting).'], 'type': 'object', 'patternProperties': {'^.*$': {'type': 'string', 'format': 'python-qualified-identifier'}}}, 'license-files': {'type': 'array', 'items': {'type': 'string'}, '$$description': ['PROVISIONAL: List of glob patterns for all license files being distributed.', '(might become standard with PEP 639).', "By default: ``['LICEN[CS]E*', 'COPYING*', 'NOTICE*', 'AUTHORS*']``"], '$comment': 'TODO: revise if PEP 639 is accepted. Probably ``project.license-files``?'}, 'dynamic': {'type': 'object', 'description': 'Instructions for loading :pep:`621`-related metadata dynamically', 'additionalProperties': False, 'properties': {'version': {'$$description': ['A version dynamically loaded via either the ``attr:`` or ``file:``', 'directives. Please make sure the given file or attribute respects :pep:`440`.'], 'oneOf': [{'title': "'attr:' directive", '$id': '#/definitions/attr-directive', '$$description': ['Value is read from a module attribute. Supports callables and iterables;', 'unsupported types are cast via ``str()``'], 'type': 'object', 'additionalProperties': False, 'properties': {'attr': {'type': 'string'}}, 'required': ['attr']}, {'$id': '#/definitions/file-directive', 'title': "'file:' directive", 'description': 'Value is read from a file (or list of files and then concatenated)', 'type': 'object', 'additionalProperties': False, 'properties': {'file': {'oneOf': [{'type': 'string'}, {'type': 'array', 'items': {'type': 'string'}}]}}, 'required': ['file']}]}, 'classifiers': {'$id': '#/definitions/file-directive', 'title': "'file:' directive", 'description': 'Value is read from a file (or list of files and then concatenated)', 'type': 'object', 'additionalProperties': False, 'properties': {'file': {'oneOf': [{'type': 'string'}, {'type': 'array', 'items': {'type': 'string'}}]}}, 'required': ['file']}, 'description': {'$id': '#/definitions/file-directive', 'title': "'file:' directive", 'description': 'Value is read from a file (or list of files and then concatenated)', 'type': 'object', 'additionalProperties': False, 'properties': {'file': {'oneOf': [{'type': 'string'}, {'type': 'array', 'items': {'type': 'string'}}]}}, 'required': ['file']}, 'dependencies': {'$id': '#/definitions/file-directive', 'title': "'file:' directive", 'description': 'Value is read from a file (or list of files and then concatenated)', 'type': 'object', 'additionalProperties': False, 'properties': {'file': {'oneOf': [{'type': 'string'}, {'type': 'array', 'items': {'type': 'string'}}]}}, 'required': ['file']}, 'entry-points': {'$id': '#/definitions/file-directive', 'title': "'file:' directive", 'description': 'Value is read from a file (or list of files and then concatenated)', 'type': 'object', 'additionalProperties': False, 'properties': {'file': {'oneOf': [{'type': 'string'}, {'type': 'array', 'items': {'type': 'string'}}]}}, 'required': ['file']}, 'optional-dependencies': {'type': 'object', 'propertyNames': {'format': 'python-identifier'}, 'additionalProperties': False, 'patternProperties': {'.+': {'$id': '#/definitions/file-directive', 'title': "'file:' directive", 'description': 'Value is read from a file (or list of files and then concatenated)', 'type': 'object', 'additionalProperties': False, 'properties': {'file': {'oneOf': [{'type': 'string'}, {'type': 'array', 'items': {'type': 'string'}}]}}, 'required': ['file']}}}, 'readme': {'anyOf': [{'$id': '#/definitions/file-directive', 'title': "'file:' directive", 'description': 'Value is read from a file (or list of files and then concatenated)', 'type': 'object', 'additionalProperties': False, 'properties': {'file': {'oneOf': [{'type': 'string'}, {'type': 'array', 'items': {'type': 'string'}}]}}, 'required': ['file']}, {'properties': {'content-type': {'type': 'string'}}}], 'required': ['file']}}}}, 'definitions': {'package-name': {'$id': '#/definitions/package-name', 'title': 'Valid package name', 'description': 'Valid package name (importable or PEP 561).', 'type': 'string', 'anyOf': [{'format': 'python-module-name'}, {'format': 'pep561-stub-name'}]}, 'file-directive': {'$id': '#/definitions/file-directive', 'title': "'file:' directive", 'description': 'Value is read from a file (or list of files and then concatenated)', 'type': 'object', 'additionalProperties': False, 'properties': {'file': {'oneOf': [{'type': 'string'}, {'type': 'array', 'items': {'type': 'string'}}]}}, 'required': ['file']}, 'attr-directive': {'title': "'attr:' directive", '$id': '#/definitions/attr-directive', '$$description': ['Value is read from a module attribute. Supports callables and iterables;', 'unsupported types are cast via ``str()``'], 'type': 'object', 'additionalProperties': False, 'properties': {'attr': {'type': 'string'}}, 'required': ['attr']}, 'find-directive': {'$id': '#/definitions/find-directive', 'title': "'find:' directive", 'type': 'object', 'additionalProperties': False, 'properties': {'find': {'type': 'object', '$$description': ['Dynamic `package discovery', '`_.'], 'additionalProperties': False, 'properties': {'where': {'description': 'Directories to be searched for packages (Unix-style relative path)', 'type': 'array', 'items': {'type': 'string'}}, 'exclude': {'type': 'array', '$$description': ['Exclude packages that match the values listed in this field.', "Can container shell-style wildcards (e.g. ``'pkg.*'``)"], 'items': {'type': 'string'}}, 'include': {'type': 'array', '$$description': ['Restrict the found packages to just the ones listed in this field.', "Can container shell-style wildcards (e.g. ``'pkg.*'``)"], 'items': {'type': 'string'}}, 'namespaces': {'type': 'boolean', '$$description': ['When ``True``, directories without a ``__init__.py`` file will also', 'be scanned for :pep:`420`-style implicit namespaces']}}}}}}}, rule='type') + data_is_dict = isinstance(data, dict) + if data_is_dict: + data_keys = set(data.keys()) + if "platforms" in data_keys: + data_keys.remove("platforms") + data__platforms = data["platforms"] + if not isinstance(data__platforms, (list, tuple)): + raise JsonSchemaValueException("" + (name_prefix or "data") + ".platforms must be array", value=data__platforms, name="" + (name_prefix or "data") + ".platforms", definition={'type': 'array', 'items': {'type': 'string'}}, rule='type') + data__platforms_is_list = isinstance(data__platforms, (list, tuple)) + if data__platforms_is_list: + data__platforms_len = len(data__platforms) + for data__platforms_x, data__platforms_item in enumerate(data__platforms): + if not isinstance(data__platforms_item, (str)): + raise JsonSchemaValueException("" + (name_prefix or "data") + ".platforms[{data__platforms_x}]".format(**locals()) + " must be string", value=data__platforms_item, name="" + (name_prefix or "data") + ".platforms[{data__platforms_x}]".format(**locals()) + "", definition={'type': 'string'}, rule='type') + if "provides" in data_keys: + data_keys.remove("provides") + data__provides = data["provides"] + if not isinstance(data__provides, (list, tuple)): + raise JsonSchemaValueException("" + (name_prefix or "data") + ".provides must be array", value=data__provides, name="" + (name_prefix or "data") + ".provides", definition={'$$description': ['Package and virtual package names contained within this package', '**(not supported by pip)**'], 'type': 'array', 'items': {'type': 'string', 'format': 'pep508-identifier'}}, rule='type') + data__provides_is_list = isinstance(data__provides, (list, tuple)) + if data__provides_is_list: + data__provides_len = len(data__provides) + for data__provides_x, data__provides_item in enumerate(data__provides): + if not isinstance(data__provides_item, (str)): + raise JsonSchemaValueException("" + (name_prefix or "data") + ".provides[{data__provides_x}]".format(**locals()) + " must be string", value=data__provides_item, name="" + (name_prefix or "data") + ".provides[{data__provides_x}]".format(**locals()) + "", definition={'type': 'string', 'format': 'pep508-identifier'}, rule='type') + if isinstance(data__provides_item, str): + if not custom_formats["pep508-identifier"](data__provides_item): + raise JsonSchemaValueException("" + (name_prefix or "data") + ".provides[{data__provides_x}]".format(**locals()) + " must be pep508-identifier", value=data__provides_item, name="" + (name_prefix or "data") + ".provides[{data__provides_x}]".format(**locals()) + "", definition={'type': 'string', 'format': 'pep508-identifier'}, rule='format') + if "obsoletes" in data_keys: + data_keys.remove("obsoletes") + data__obsoletes = data["obsoletes"] + if not isinstance(data__obsoletes, (list, tuple)): + raise JsonSchemaValueException("" + (name_prefix or "data") + ".obsoletes must be array", value=data__obsoletes, name="" + (name_prefix or "data") + ".obsoletes", definition={'$$description': ['Packages which this package renders obsolete', '**(not supported by pip)**'], 'type': 'array', 'items': {'type': 'string', 'format': 'pep508-identifier'}}, rule='type') + data__obsoletes_is_list = isinstance(data__obsoletes, (list, tuple)) + if data__obsoletes_is_list: + data__obsoletes_len = len(data__obsoletes) + for data__obsoletes_x, data__obsoletes_item in enumerate(data__obsoletes): + if not isinstance(data__obsoletes_item, (str)): + raise JsonSchemaValueException("" + (name_prefix or "data") + ".obsoletes[{data__obsoletes_x}]".format(**locals()) + " must be string", value=data__obsoletes_item, name="" + (name_prefix or "data") + ".obsoletes[{data__obsoletes_x}]".format(**locals()) + "", definition={'type': 'string', 'format': 'pep508-identifier'}, rule='type') + if isinstance(data__obsoletes_item, str): + if not custom_formats["pep508-identifier"](data__obsoletes_item): + raise JsonSchemaValueException("" + (name_prefix or "data") + ".obsoletes[{data__obsoletes_x}]".format(**locals()) + " must be pep508-identifier", value=data__obsoletes_item, name="" + (name_prefix or "data") + ".obsoletes[{data__obsoletes_x}]".format(**locals()) + "", definition={'type': 'string', 'format': 'pep508-identifier'}, rule='format') + if "zip-safe" in data_keys: + data_keys.remove("zip-safe") + data__zipsafe = data["zip-safe"] + if not isinstance(data__zipsafe, (bool)): + raise JsonSchemaValueException("" + (name_prefix or "data") + ".zip-safe must be boolean", value=data__zipsafe, name="" + (name_prefix or "data") + ".zip-safe", definition={'description': 'Whether the project can be safely installed and run from a zip file.', 'type': 'boolean'}, rule='type') + if "script-files" in data_keys: + data_keys.remove("script-files") + data__scriptfiles = data["script-files"] + if not isinstance(data__scriptfiles, (list, tuple)): + raise JsonSchemaValueException("" + (name_prefix or "data") + ".script-files must be array", value=data__scriptfiles, name="" + (name_prefix or "data") + ".script-files", definition={'description': 'Legacy way of defining scripts (entry-points are preferred).', 'type': 'array', 'items': {'type': 'string'}, '$comment': 'TODO: is this field deprecated/should be removed?'}, rule='type') + data__scriptfiles_is_list = isinstance(data__scriptfiles, (list, tuple)) + if data__scriptfiles_is_list: + data__scriptfiles_len = len(data__scriptfiles) + for data__scriptfiles_x, data__scriptfiles_item in enumerate(data__scriptfiles): + if not isinstance(data__scriptfiles_item, (str)): + raise JsonSchemaValueException("" + (name_prefix or "data") + ".script-files[{data__scriptfiles_x}]".format(**locals()) + " must be string", value=data__scriptfiles_item, name="" + (name_prefix or "data") + ".script-files[{data__scriptfiles_x}]".format(**locals()) + "", definition={'type': 'string'}, rule='type') + if "eager-resources" in data_keys: + data_keys.remove("eager-resources") + data__eagerresources = data["eager-resources"] + if not isinstance(data__eagerresources, (list, tuple)): + raise JsonSchemaValueException("" + (name_prefix or "data") + ".eager-resources must be array", value=data__eagerresources, name="" + (name_prefix or "data") + ".eager-resources", definition={'$$description': ['Resources that should be extracted together, if any of them is needed,', 'or if any C extensions included in the project are imported.'], 'type': 'array', 'items': {'type': 'string'}}, rule='type') + data__eagerresources_is_list = isinstance(data__eagerresources, (list, tuple)) + if data__eagerresources_is_list: + data__eagerresources_len = len(data__eagerresources) + for data__eagerresources_x, data__eagerresources_item in enumerate(data__eagerresources): + if not isinstance(data__eagerresources_item, (str)): + raise JsonSchemaValueException("" + (name_prefix or "data") + ".eager-resources[{data__eagerresources_x}]".format(**locals()) + " must be string", value=data__eagerresources_item, name="" + (name_prefix or "data") + ".eager-resources[{data__eagerresources_x}]".format(**locals()) + "", definition={'type': 'string'}, rule='type') + if "packages" in data_keys: + data_keys.remove("packages") + data__packages = data["packages"] + data__packages_one_of_count1 = 0 + if data__packages_one_of_count1 < 2: + try: + if not isinstance(data__packages, (list, tuple)): + raise JsonSchemaValueException("" + (name_prefix or "data") + ".packages must be array", value=data__packages, name="" + (name_prefix or "data") + ".packages", definition={'title': 'Array of Python package identifiers', 'type': 'array', 'items': {'$id': '#/definitions/package-name', 'title': 'Valid package name', 'description': 'Valid package name (importable or PEP 561).', 'type': 'string', 'anyOf': [{'format': 'python-module-name'}, {'format': 'pep561-stub-name'}]}}, rule='type') + data__packages_is_list = isinstance(data__packages, (list, tuple)) + if data__packages_is_list: + data__packages_len = len(data__packages) + for data__packages_x, data__packages_item in enumerate(data__packages): + validate_https___setuptools_pypa_io_en_latest_references_keywords_html__definitions_package_name(data__packages_item, custom_formats, (name_prefix or "data") + ".packages[{data__packages_x}]".format(**locals())) + data__packages_one_of_count1 += 1 + except JsonSchemaValueException: pass + if data__packages_one_of_count1 < 2: + try: + validate_https___setuptools_pypa_io_en_latest_references_keywords_html__definitions_find_directive(data__packages, custom_formats, (name_prefix or "data") + ".packages") + data__packages_one_of_count1 += 1 + except JsonSchemaValueException: pass + if data__packages_one_of_count1 != 1: + raise JsonSchemaValueException("" + (name_prefix or "data") + ".packages must be valid exactly by one definition" + (" (" + str(data__packages_one_of_count1) + " matches found)"), value=data__packages, name="" + (name_prefix or "data") + ".packages", definition={'$$description': ['Packages that should be included in the distribution.', 'It can be given either as a list of package identifiers', 'or as a ``dict``-like structure with a single key ``find``', 'which corresponds to a dynamic call to', '``setuptools.config.expand.find_packages`` function.', 'The ``find`` key is associated with a nested ``dict``-like structure that can', 'contain ``where``, ``include``, ``exclude`` and ``namespaces`` keys,', 'mimicking the keyword arguments of the associated function.'], 'oneOf': [{'title': 'Array of Python package identifiers', 'type': 'array', 'items': {'$id': '#/definitions/package-name', 'title': 'Valid package name', 'description': 'Valid package name (importable or PEP 561).', 'type': 'string', 'anyOf': [{'format': 'python-module-name'}, {'format': 'pep561-stub-name'}]}}, {'$id': '#/definitions/find-directive', 'title': "'find:' directive", 'type': 'object', 'additionalProperties': False, 'properties': {'find': {'type': 'object', '$$description': ['Dynamic `package discovery', '`_.'], 'additionalProperties': False, 'properties': {'where': {'description': 'Directories to be searched for packages (Unix-style relative path)', 'type': 'array', 'items': {'type': 'string'}}, 'exclude': {'type': 'array', '$$description': ['Exclude packages that match the values listed in this field.', "Can container shell-style wildcards (e.g. ``'pkg.*'``)"], 'items': {'type': 'string'}}, 'include': {'type': 'array', '$$description': ['Restrict the found packages to just the ones listed in this field.', "Can container shell-style wildcards (e.g. ``'pkg.*'``)"], 'items': {'type': 'string'}}, 'namespaces': {'type': 'boolean', '$$description': ['When ``True``, directories without a ``__init__.py`` file will also', 'be scanned for :pep:`420`-style implicit namespaces']}}}}}]}, rule='oneOf') + if "package-dir" in data_keys: + data_keys.remove("package-dir") + data__packagedir = data["package-dir"] + if not isinstance(data__packagedir, (dict)): + raise JsonSchemaValueException("" + (name_prefix or "data") + ".package-dir must be object", value=data__packagedir, name="" + (name_prefix or "data") + ".package-dir", definition={'$$description': [':class:`dict`-like structure mapping from package names to directories where their', 'code can be found.', 'The empty string (as key) means that all packages are contained inside', 'the given directory will be included in the distribution.'], 'type': 'object', 'additionalProperties': False, 'propertyNames': {'oneOf': [{'const': ''}, {'$id': '#/definitions/package-name', 'title': 'Valid package name', 'description': 'Valid package name (importable or PEP 561).', 'type': 'string', 'anyOf': [{'format': 'python-module-name'}, {'format': 'pep561-stub-name'}]}]}, 'patternProperties': {'^.*$': {'type': 'string'}}}, rule='type') + data__packagedir_is_dict = isinstance(data__packagedir, dict) + if data__packagedir_is_dict: + data__packagedir_keys = set(data__packagedir.keys()) + for data__packagedir_key, data__packagedir_val in data__packagedir.items(): + if REGEX_PATTERNS['^.*$'].search(data__packagedir_key): + if data__packagedir_key in data__packagedir_keys: + data__packagedir_keys.remove(data__packagedir_key) + if not isinstance(data__packagedir_val, (str)): + raise JsonSchemaValueException("" + (name_prefix or "data") + ".package-dir.{data__packagedir_key}".format(**locals()) + " must be string", value=data__packagedir_val, name="" + (name_prefix or "data") + ".package-dir.{data__packagedir_key}".format(**locals()) + "", definition={'type': 'string'}, rule='type') + if data__packagedir_keys: + raise JsonSchemaValueException("" + (name_prefix or "data") + ".package-dir must not contain "+str(data__packagedir_keys)+" properties", value=data__packagedir, name="" + (name_prefix or "data") + ".package-dir", definition={'$$description': [':class:`dict`-like structure mapping from package names to directories where their', 'code can be found.', 'The empty string (as key) means that all packages are contained inside', 'the given directory will be included in the distribution.'], 'type': 'object', 'additionalProperties': False, 'propertyNames': {'oneOf': [{'const': ''}, {'$id': '#/definitions/package-name', 'title': 'Valid package name', 'description': 'Valid package name (importable or PEP 561).', 'type': 'string', 'anyOf': [{'format': 'python-module-name'}, {'format': 'pep561-stub-name'}]}]}, 'patternProperties': {'^.*$': {'type': 'string'}}}, rule='additionalProperties') + data__packagedir_len = len(data__packagedir) + if data__packagedir_len != 0: + data__packagedir_property_names = True + for data__packagedir_key in data__packagedir: + try: + data__packagedir_key_one_of_count2 = 0 + if data__packagedir_key_one_of_count2 < 2: + try: + if data__packagedir_key != "": + raise JsonSchemaValueException("" + (name_prefix or "data") + ".package-dir must be same as const definition: ", value=data__packagedir_key, name="" + (name_prefix or "data") + ".package-dir", definition={'const': ''}, rule='const') + data__packagedir_key_one_of_count2 += 1 + except JsonSchemaValueException: pass + if data__packagedir_key_one_of_count2 < 2: + try: + validate_https___setuptools_pypa_io_en_latest_references_keywords_html__definitions_package_name(data__packagedir_key, custom_formats, (name_prefix or "data") + ".package-dir") + data__packagedir_key_one_of_count2 += 1 + except JsonSchemaValueException: pass + if data__packagedir_key_one_of_count2 != 1: + raise JsonSchemaValueException("" + (name_prefix or "data") + ".package-dir must be valid exactly by one definition" + (" (" + str(data__packagedir_key_one_of_count2) + " matches found)"), value=data__packagedir_key, name="" + (name_prefix or "data") + ".package-dir", definition={'oneOf': [{'const': ''}, {'$id': '#/definitions/package-name', 'title': 'Valid package name', 'description': 'Valid package name (importable or PEP 561).', 'type': 'string', 'anyOf': [{'format': 'python-module-name'}, {'format': 'pep561-stub-name'}]}]}, rule='oneOf') + except JsonSchemaValueException: + data__packagedir_property_names = False + if not data__packagedir_property_names: + raise JsonSchemaValueException("" + (name_prefix or "data") + ".package-dir must be named by propertyName definition", value=data__packagedir, name="" + (name_prefix or "data") + ".package-dir", definition={'$$description': [':class:`dict`-like structure mapping from package names to directories where their', 'code can be found.', 'The empty string (as key) means that all packages are contained inside', 'the given directory will be included in the distribution.'], 'type': 'object', 'additionalProperties': False, 'propertyNames': {'oneOf': [{'const': ''}, {'$id': '#/definitions/package-name', 'title': 'Valid package name', 'description': 'Valid package name (importable or PEP 561).', 'type': 'string', 'anyOf': [{'format': 'python-module-name'}, {'format': 'pep561-stub-name'}]}]}, 'patternProperties': {'^.*$': {'type': 'string'}}}, rule='propertyNames') + if "package-data" in data_keys: + data_keys.remove("package-data") + data__packagedata = data["package-data"] + if not isinstance(data__packagedata, (dict)): + raise JsonSchemaValueException("" + (name_prefix or "data") + ".package-data must be object", value=data__packagedata, name="" + (name_prefix or "data") + ".package-data", definition={'$$description': ['Mapping from package names to lists of glob patterns.', 'Usually this option is not needed when using ``include-package-data = true``', 'For more information on how to include data files, check ``setuptools`` `docs', '`_.'], 'type': 'object', 'additionalProperties': False, 'propertyNames': {'oneOf': [{'format': 'python-module-name'}, {'const': '*'}]}, 'patternProperties': {'^.*$': {'type': 'array', 'items': {'type': 'string'}}}}, rule='type') + data__packagedata_is_dict = isinstance(data__packagedata, dict) + if data__packagedata_is_dict: + data__packagedata_keys = set(data__packagedata.keys()) + for data__packagedata_key, data__packagedata_val in data__packagedata.items(): + if REGEX_PATTERNS['^.*$'].search(data__packagedata_key): + if data__packagedata_key in data__packagedata_keys: + data__packagedata_keys.remove(data__packagedata_key) + if not isinstance(data__packagedata_val, (list, tuple)): + raise JsonSchemaValueException("" + (name_prefix or "data") + ".package-data.{data__packagedata_key}".format(**locals()) + " must be array", value=data__packagedata_val, name="" + (name_prefix or "data") + ".package-data.{data__packagedata_key}".format(**locals()) + "", definition={'type': 'array', 'items': {'type': 'string'}}, rule='type') + data__packagedata_val_is_list = isinstance(data__packagedata_val, (list, tuple)) + if data__packagedata_val_is_list: + data__packagedata_val_len = len(data__packagedata_val) + for data__packagedata_val_x, data__packagedata_val_item in enumerate(data__packagedata_val): + if not isinstance(data__packagedata_val_item, (str)): + raise JsonSchemaValueException("" + (name_prefix or "data") + ".package-data.{data__packagedata_key}[{data__packagedata_val_x}]".format(**locals()) + " must be string", value=data__packagedata_val_item, name="" + (name_prefix or "data") + ".package-data.{data__packagedata_key}[{data__packagedata_val_x}]".format(**locals()) + "", definition={'type': 'string'}, rule='type') + if data__packagedata_keys: + raise JsonSchemaValueException("" + (name_prefix or "data") + ".package-data must not contain "+str(data__packagedata_keys)+" properties", value=data__packagedata, name="" + (name_prefix or "data") + ".package-data", definition={'$$description': ['Mapping from package names to lists of glob patterns.', 'Usually this option is not needed when using ``include-package-data = true``', 'For more information on how to include data files, check ``setuptools`` `docs', '`_.'], 'type': 'object', 'additionalProperties': False, 'propertyNames': {'oneOf': [{'format': 'python-module-name'}, {'const': '*'}]}, 'patternProperties': {'^.*$': {'type': 'array', 'items': {'type': 'string'}}}}, rule='additionalProperties') + data__packagedata_len = len(data__packagedata) + if data__packagedata_len != 0: + data__packagedata_property_names = True + for data__packagedata_key in data__packagedata: + try: + data__packagedata_key_one_of_count3 = 0 + if data__packagedata_key_one_of_count3 < 2: + try: + if isinstance(data__packagedata_key, str): + if not custom_formats["python-module-name"](data__packagedata_key): + raise JsonSchemaValueException("" + (name_prefix or "data") + ".package-data must be python-module-name", value=data__packagedata_key, name="" + (name_prefix or "data") + ".package-data", definition={'format': 'python-module-name'}, rule='format') + data__packagedata_key_one_of_count3 += 1 + except JsonSchemaValueException: pass + if data__packagedata_key_one_of_count3 < 2: + try: + if data__packagedata_key != "*": + raise JsonSchemaValueException("" + (name_prefix or "data") + ".package-data must be same as const definition: *", value=data__packagedata_key, name="" + (name_prefix or "data") + ".package-data", definition={'const': '*'}, rule='const') + data__packagedata_key_one_of_count3 += 1 + except JsonSchemaValueException: pass + if data__packagedata_key_one_of_count3 != 1: + raise JsonSchemaValueException("" + (name_prefix or "data") + ".package-data must be valid exactly by one definition" + (" (" + str(data__packagedata_key_one_of_count3) + " matches found)"), value=data__packagedata_key, name="" + (name_prefix or "data") + ".package-data", definition={'oneOf': [{'format': 'python-module-name'}, {'const': '*'}]}, rule='oneOf') + except JsonSchemaValueException: + data__packagedata_property_names = False + if not data__packagedata_property_names: + raise JsonSchemaValueException("" + (name_prefix or "data") + ".package-data must be named by propertyName definition", value=data__packagedata, name="" + (name_prefix or "data") + ".package-data", definition={'$$description': ['Mapping from package names to lists of glob patterns.', 'Usually this option is not needed when using ``include-package-data = true``', 'For more information on how to include data files, check ``setuptools`` `docs', '`_.'], 'type': 'object', 'additionalProperties': False, 'propertyNames': {'oneOf': [{'format': 'python-module-name'}, {'const': '*'}]}, 'patternProperties': {'^.*$': {'type': 'array', 'items': {'type': 'string'}}}}, rule='propertyNames') + if "include-package-data" in data_keys: + data_keys.remove("include-package-data") + data__includepackagedata = data["include-package-data"] + if not isinstance(data__includepackagedata, (bool)): + raise JsonSchemaValueException("" + (name_prefix or "data") + ".include-package-data must be boolean", value=data__includepackagedata, name="" + (name_prefix or "data") + ".include-package-data", definition={'$$description': ['Automatically include any data files inside the package directories', 'that are specified by ``MANIFEST.in``', 'For more information on how to include data files, check ``setuptools`` `docs', '`_.'], 'type': 'boolean'}, rule='type') + if "exclude-package-data" in data_keys: + data_keys.remove("exclude-package-data") + data__excludepackagedata = data["exclude-package-data"] + if not isinstance(data__excludepackagedata, (dict)): + raise JsonSchemaValueException("" + (name_prefix or "data") + ".exclude-package-data must be object", value=data__excludepackagedata, name="" + (name_prefix or "data") + ".exclude-package-data", definition={'$$description': ['Mapping from package names to lists of glob patterns that should be excluded', 'For more information on how to include data files, check ``setuptools`` `docs', '`_.'], 'type': 'object', 'additionalProperties': False, 'propertyNames': {'oneOf': [{'format': 'python-module-name'}, {'const': '*'}]}, 'patternProperties': {'^.*$': {'type': 'array', 'items': {'type': 'string'}}}}, rule='type') + data__excludepackagedata_is_dict = isinstance(data__excludepackagedata, dict) + if data__excludepackagedata_is_dict: + data__excludepackagedata_keys = set(data__excludepackagedata.keys()) + for data__excludepackagedata_key, data__excludepackagedata_val in data__excludepackagedata.items(): + if REGEX_PATTERNS['^.*$'].search(data__excludepackagedata_key): + if data__excludepackagedata_key in data__excludepackagedata_keys: + data__excludepackagedata_keys.remove(data__excludepackagedata_key) + if not isinstance(data__excludepackagedata_val, (list, tuple)): + raise JsonSchemaValueException("" + (name_prefix or "data") + ".exclude-package-data.{data__excludepackagedata_key}".format(**locals()) + " must be array", value=data__excludepackagedata_val, name="" + (name_prefix or "data") + ".exclude-package-data.{data__excludepackagedata_key}".format(**locals()) + "", definition={'type': 'array', 'items': {'type': 'string'}}, rule='type') + data__excludepackagedata_val_is_list = isinstance(data__excludepackagedata_val, (list, tuple)) + if data__excludepackagedata_val_is_list: + data__excludepackagedata_val_len = len(data__excludepackagedata_val) + for data__excludepackagedata_val_x, data__excludepackagedata_val_item in enumerate(data__excludepackagedata_val): + if not isinstance(data__excludepackagedata_val_item, (str)): + raise JsonSchemaValueException("" + (name_prefix or "data") + ".exclude-package-data.{data__excludepackagedata_key}[{data__excludepackagedata_val_x}]".format(**locals()) + " must be string", value=data__excludepackagedata_val_item, name="" + (name_prefix or "data") + ".exclude-package-data.{data__excludepackagedata_key}[{data__excludepackagedata_val_x}]".format(**locals()) + "", definition={'type': 'string'}, rule='type') + if data__excludepackagedata_keys: + raise JsonSchemaValueException("" + (name_prefix or "data") + ".exclude-package-data must not contain "+str(data__excludepackagedata_keys)+" properties", value=data__excludepackagedata, name="" + (name_prefix or "data") + ".exclude-package-data", definition={'$$description': ['Mapping from package names to lists of glob patterns that should be excluded', 'For more information on how to include data files, check ``setuptools`` `docs', '`_.'], 'type': 'object', 'additionalProperties': False, 'propertyNames': {'oneOf': [{'format': 'python-module-name'}, {'const': '*'}]}, 'patternProperties': {'^.*$': {'type': 'array', 'items': {'type': 'string'}}}}, rule='additionalProperties') + data__excludepackagedata_len = len(data__excludepackagedata) + if data__excludepackagedata_len != 0: + data__excludepackagedata_property_names = True + for data__excludepackagedata_key in data__excludepackagedata: + try: + data__excludepackagedata_key_one_of_count4 = 0 + if data__excludepackagedata_key_one_of_count4 < 2: + try: + if isinstance(data__excludepackagedata_key, str): + if not custom_formats["python-module-name"](data__excludepackagedata_key): + raise JsonSchemaValueException("" + (name_prefix or "data") + ".exclude-package-data must be python-module-name", value=data__excludepackagedata_key, name="" + (name_prefix or "data") + ".exclude-package-data", definition={'format': 'python-module-name'}, rule='format') + data__excludepackagedata_key_one_of_count4 += 1 + except JsonSchemaValueException: pass + if data__excludepackagedata_key_one_of_count4 < 2: + try: + if data__excludepackagedata_key != "*": + raise JsonSchemaValueException("" + (name_prefix or "data") + ".exclude-package-data must be same as const definition: *", value=data__excludepackagedata_key, name="" + (name_prefix or "data") + ".exclude-package-data", definition={'const': '*'}, rule='const') + data__excludepackagedata_key_one_of_count4 += 1 + except JsonSchemaValueException: pass + if data__excludepackagedata_key_one_of_count4 != 1: + raise JsonSchemaValueException("" + (name_prefix or "data") + ".exclude-package-data must be valid exactly by one definition" + (" (" + str(data__excludepackagedata_key_one_of_count4) + " matches found)"), value=data__excludepackagedata_key, name="" + (name_prefix or "data") + ".exclude-package-data", definition={'oneOf': [{'format': 'python-module-name'}, {'const': '*'}]}, rule='oneOf') + except JsonSchemaValueException: + data__excludepackagedata_property_names = False + if not data__excludepackagedata_property_names: + raise JsonSchemaValueException("" + (name_prefix or "data") + ".exclude-package-data must be named by propertyName definition", value=data__excludepackagedata, name="" + (name_prefix or "data") + ".exclude-package-data", definition={'$$description': ['Mapping from package names to lists of glob patterns that should be excluded', 'For more information on how to include data files, check ``setuptools`` `docs', '`_.'], 'type': 'object', 'additionalProperties': False, 'propertyNames': {'oneOf': [{'format': 'python-module-name'}, {'const': '*'}]}, 'patternProperties': {'^.*$': {'type': 'array', 'items': {'type': 'string'}}}}, rule='propertyNames') + if "namespace-packages" in data_keys: + data_keys.remove("namespace-packages") + data__namespacepackages = data["namespace-packages"] + if not isinstance(data__namespacepackages, (list, tuple)): + raise JsonSchemaValueException("" + (name_prefix or "data") + ".namespace-packages must be array", value=data__namespacepackages, name="" + (name_prefix or "data") + ".namespace-packages", definition={'type': 'array', 'items': {'type': 'string', 'format': 'python-module-name'}, '$comment': 'https://setuptools.pypa.io/en/latest/userguide/package_discovery.html'}, rule='type') + data__namespacepackages_is_list = isinstance(data__namespacepackages, (list, tuple)) + if data__namespacepackages_is_list: + data__namespacepackages_len = len(data__namespacepackages) + for data__namespacepackages_x, data__namespacepackages_item in enumerate(data__namespacepackages): + if not isinstance(data__namespacepackages_item, (str)): + raise JsonSchemaValueException("" + (name_prefix or "data") + ".namespace-packages[{data__namespacepackages_x}]".format(**locals()) + " must be string", value=data__namespacepackages_item, name="" + (name_prefix or "data") + ".namespace-packages[{data__namespacepackages_x}]".format(**locals()) + "", definition={'type': 'string', 'format': 'python-module-name'}, rule='type') + if isinstance(data__namespacepackages_item, str): + if not custom_formats["python-module-name"](data__namespacepackages_item): + raise JsonSchemaValueException("" + (name_prefix or "data") + ".namespace-packages[{data__namespacepackages_x}]".format(**locals()) + " must be python-module-name", value=data__namespacepackages_item, name="" + (name_prefix or "data") + ".namespace-packages[{data__namespacepackages_x}]".format(**locals()) + "", definition={'type': 'string', 'format': 'python-module-name'}, rule='format') + if "py-modules" in data_keys: + data_keys.remove("py-modules") + data__pymodules = data["py-modules"] + if not isinstance(data__pymodules, (list, tuple)): + raise JsonSchemaValueException("" + (name_prefix or "data") + ".py-modules must be array", value=data__pymodules, name="" + (name_prefix or "data") + ".py-modules", definition={'description': 'Modules that setuptools will manipulate', 'type': 'array', 'items': {'type': 'string', 'format': 'python-module-name'}, '$comment': 'TODO: clarify the relationship with ``packages``'}, rule='type') + data__pymodules_is_list = isinstance(data__pymodules, (list, tuple)) + if data__pymodules_is_list: + data__pymodules_len = len(data__pymodules) + for data__pymodules_x, data__pymodules_item in enumerate(data__pymodules): + if not isinstance(data__pymodules_item, (str)): + raise JsonSchemaValueException("" + (name_prefix or "data") + ".py-modules[{data__pymodules_x}]".format(**locals()) + " must be string", value=data__pymodules_item, name="" + (name_prefix or "data") + ".py-modules[{data__pymodules_x}]".format(**locals()) + "", definition={'type': 'string', 'format': 'python-module-name'}, rule='type') + if isinstance(data__pymodules_item, str): + if not custom_formats["python-module-name"](data__pymodules_item): + raise JsonSchemaValueException("" + (name_prefix or "data") + ".py-modules[{data__pymodules_x}]".format(**locals()) + " must be python-module-name", value=data__pymodules_item, name="" + (name_prefix or "data") + ".py-modules[{data__pymodules_x}]".format(**locals()) + "", definition={'type': 'string', 'format': 'python-module-name'}, rule='format') + if "data-files" in data_keys: + data_keys.remove("data-files") + data__datafiles = data["data-files"] + if not isinstance(data__datafiles, (dict)): + raise JsonSchemaValueException("" + (name_prefix or "data") + ".data-files must be object", value=data__datafiles, name="" + (name_prefix or "data") + ".data-files", definition={'$$description': ['**DEPRECATED**: dict-like structure where each key represents a directory and', 'the value is a list of glob patterns that should be installed in them.', "Please notice this don't work with wheels. See `data files support", '`_'], 'type': 'object', 'patternProperties': {'^.*$': {'type': 'array', 'items': {'type': 'string'}}}}, rule='type') + data__datafiles_is_dict = isinstance(data__datafiles, dict) + if data__datafiles_is_dict: + data__datafiles_keys = set(data__datafiles.keys()) + for data__datafiles_key, data__datafiles_val in data__datafiles.items(): + if REGEX_PATTERNS['^.*$'].search(data__datafiles_key): + if data__datafiles_key in data__datafiles_keys: + data__datafiles_keys.remove(data__datafiles_key) + if not isinstance(data__datafiles_val, (list, tuple)): + raise JsonSchemaValueException("" + (name_prefix or "data") + ".data-files.{data__datafiles_key}".format(**locals()) + " must be array", value=data__datafiles_val, name="" + (name_prefix or "data") + ".data-files.{data__datafiles_key}".format(**locals()) + "", definition={'type': 'array', 'items': {'type': 'string'}}, rule='type') + data__datafiles_val_is_list = isinstance(data__datafiles_val, (list, tuple)) + if data__datafiles_val_is_list: + data__datafiles_val_len = len(data__datafiles_val) + for data__datafiles_val_x, data__datafiles_val_item in enumerate(data__datafiles_val): + if not isinstance(data__datafiles_val_item, (str)): + raise JsonSchemaValueException("" + (name_prefix or "data") + ".data-files.{data__datafiles_key}[{data__datafiles_val_x}]".format(**locals()) + " must be string", value=data__datafiles_val_item, name="" + (name_prefix or "data") + ".data-files.{data__datafiles_key}[{data__datafiles_val_x}]".format(**locals()) + "", definition={'type': 'string'}, rule='type') + if "cmdclass" in data_keys: + data_keys.remove("cmdclass") + data__cmdclass = data["cmdclass"] + if not isinstance(data__cmdclass, (dict)): + raise JsonSchemaValueException("" + (name_prefix or "data") + ".cmdclass must be object", value=data__cmdclass, name="" + (name_prefix or "data") + ".cmdclass", definition={'$$description': ['Mapping of distutils-style command names to ``setuptools.Command`` subclasses', 'which in turn should be represented by strings with a qualified class name', '(i.e., "dotted" form with module), e.g.::\n\n', ' cmdclass = {mycmd = "pkg.subpkg.module.CommandClass"}\n\n', 'The command class should be a directly defined at the top-level of the', 'containing module (no class nesting).'], 'type': 'object', 'patternProperties': {'^.*$': {'type': 'string', 'format': 'python-qualified-identifier'}}}, rule='type') + data__cmdclass_is_dict = isinstance(data__cmdclass, dict) + if data__cmdclass_is_dict: + data__cmdclass_keys = set(data__cmdclass.keys()) + for data__cmdclass_key, data__cmdclass_val in data__cmdclass.items(): + if REGEX_PATTERNS['^.*$'].search(data__cmdclass_key): + if data__cmdclass_key in data__cmdclass_keys: + data__cmdclass_keys.remove(data__cmdclass_key) + if not isinstance(data__cmdclass_val, (str)): + raise JsonSchemaValueException("" + (name_prefix or "data") + ".cmdclass.{data__cmdclass_key}".format(**locals()) + " must be string", value=data__cmdclass_val, name="" + (name_prefix or "data") + ".cmdclass.{data__cmdclass_key}".format(**locals()) + "", definition={'type': 'string', 'format': 'python-qualified-identifier'}, rule='type') + if isinstance(data__cmdclass_val, str): + if not custom_formats["python-qualified-identifier"](data__cmdclass_val): + raise JsonSchemaValueException("" + (name_prefix or "data") + ".cmdclass.{data__cmdclass_key}".format(**locals()) + " must be python-qualified-identifier", value=data__cmdclass_val, name="" + (name_prefix or "data") + ".cmdclass.{data__cmdclass_key}".format(**locals()) + "", definition={'type': 'string', 'format': 'python-qualified-identifier'}, rule='format') + if "license-files" in data_keys: + data_keys.remove("license-files") + data__licensefiles = data["license-files"] + if not isinstance(data__licensefiles, (list, tuple)): + raise JsonSchemaValueException("" + (name_prefix or "data") + ".license-files must be array", value=data__licensefiles, name="" + (name_prefix or "data") + ".license-files", definition={'type': 'array', 'items': {'type': 'string'}, '$$description': ['PROVISIONAL: List of glob patterns for all license files being distributed.', '(might become standard with PEP 639).', "By default: ``['LICEN[CS]E*', 'COPYING*', 'NOTICE*', 'AUTHORS*']``"], '$comment': 'TODO: revise if PEP 639 is accepted. Probably ``project.license-files``?'}, rule='type') + data__licensefiles_is_list = isinstance(data__licensefiles, (list, tuple)) + if data__licensefiles_is_list: + data__licensefiles_len = len(data__licensefiles) + for data__licensefiles_x, data__licensefiles_item in enumerate(data__licensefiles): + if not isinstance(data__licensefiles_item, (str)): + raise JsonSchemaValueException("" + (name_prefix or "data") + ".license-files[{data__licensefiles_x}]".format(**locals()) + " must be string", value=data__licensefiles_item, name="" + (name_prefix or "data") + ".license-files[{data__licensefiles_x}]".format(**locals()) + "", definition={'type': 'string'}, rule='type') + if "dynamic" in data_keys: + data_keys.remove("dynamic") + data__dynamic = data["dynamic"] + if not isinstance(data__dynamic, (dict)): + raise JsonSchemaValueException("" + (name_prefix or "data") + ".dynamic must be object", value=data__dynamic, name="" + (name_prefix or "data") + ".dynamic", definition={'type': 'object', 'description': 'Instructions for loading :pep:`621`-related metadata dynamically', 'additionalProperties': False, 'properties': {'version': {'$$description': ['A version dynamically loaded via either the ``attr:`` or ``file:``', 'directives. Please make sure the given file or attribute respects :pep:`440`.'], 'oneOf': [{'title': "'attr:' directive", '$id': '#/definitions/attr-directive', '$$description': ['Value is read from a module attribute. Supports callables and iterables;', 'unsupported types are cast via ``str()``'], 'type': 'object', 'additionalProperties': False, 'properties': {'attr': {'type': 'string'}}, 'required': ['attr']}, {'$id': '#/definitions/file-directive', 'title': "'file:' directive", 'description': 'Value is read from a file (or list of files and then concatenated)', 'type': 'object', 'additionalProperties': False, 'properties': {'file': {'oneOf': [{'type': 'string'}, {'type': 'array', 'items': {'type': 'string'}}]}}, 'required': ['file']}]}, 'classifiers': {'$id': '#/definitions/file-directive', 'title': "'file:' directive", 'description': 'Value is read from a file (or list of files and then concatenated)', 'type': 'object', 'additionalProperties': False, 'properties': {'file': {'oneOf': [{'type': 'string'}, {'type': 'array', 'items': {'type': 'string'}}]}}, 'required': ['file']}, 'description': {'$id': '#/definitions/file-directive', 'title': "'file:' directive", 'description': 'Value is read from a file (or list of files and then concatenated)', 'type': 'object', 'additionalProperties': False, 'properties': {'file': {'oneOf': [{'type': 'string'}, {'type': 'array', 'items': {'type': 'string'}}]}}, 'required': ['file']}, 'dependencies': {'$id': '#/definitions/file-directive', 'title': "'file:' directive", 'description': 'Value is read from a file (or list of files and then concatenated)', 'type': 'object', 'additionalProperties': False, 'properties': {'file': {'oneOf': [{'type': 'string'}, {'type': 'array', 'items': {'type': 'string'}}]}}, 'required': ['file']}, 'entry-points': {'$id': '#/definitions/file-directive', 'title': "'file:' directive", 'description': 'Value is read from a file (or list of files and then concatenated)', 'type': 'object', 'additionalProperties': False, 'properties': {'file': {'oneOf': [{'type': 'string'}, {'type': 'array', 'items': {'type': 'string'}}]}}, 'required': ['file']}, 'optional-dependencies': {'type': 'object', 'propertyNames': {'format': 'python-identifier'}, 'additionalProperties': False, 'patternProperties': {'.+': {'$id': '#/definitions/file-directive', 'title': "'file:' directive", 'description': 'Value is read from a file (or list of files and then concatenated)', 'type': 'object', 'additionalProperties': False, 'properties': {'file': {'oneOf': [{'type': 'string'}, {'type': 'array', 'items': {'type': 'string'}}]}}, 'required': ['file']}}}, 'readme': {'anyOf': [{'$id': '#/definitions/file-directive', 'title': "'file:' directive", 'description': 'Value is read from a file (or list of files and then concatenated)', 'type': 'object', 'additionalProperties': False, 'properties': {'file': {'oneOf': [{'type': 'string'}, {'type': 'array', 'items': {'type': 'string'}}]}}, 'required': ['file']}, {'properties': {'content-type': {'type': 'string'}}}], 'required': ['file']}}}, rule='type') + data__dynamic_is_dict = isinstance(data__dynamic, dict) + if data__dynamic_is_dict: + data__dynamic_keys = set(data__dynamic.keys()) + if "version" in data__dynamic_keys: + data__dynamic_keys.remove("version") + data__dynamic__version = data__dynamic["version"] + data__dynamic__version_one_of_count5 = 0 + if data__dynamic__version_one_of_count5 < 2: + try: + validate_https___setuptools_pypa_io_en_latest_references_keywords_html__definitions_attr_directive(data__dynamic__version, custom_formats, (name_prefix or "data") + ".dynamic.version") + data__dynamic__version_one_of_count5 += 1 + except JsonSchemaValueException: pass + if data__dynamic__version_one_of_count5 < 2: + try: + validate_https___setuptools_pypa_io_en_latest_references_keywords_html__definitions_file_directive(data__dynamic__version, custom_formats, (name_prefix or "data") + ".dynamic.version") + data__dynamic__version_one_of_count5 += 1 + except JsonSchemaValueException: pass + if data__dynamic__version_one_of_count5 != 1: + raise JsonSchemaValueException("" + (name_prefix or "data") + ".dynamic.version must be valid exactly by one definition" + (" (" + str(data__dynamic__version_one_of_count5) + " matches found)"), value=data__dynamic__version, name="" + (name_prefix or "data") + ".dynamic.version", definition={'$$description': ['A version dynamically loaded via either the ``attr:`` or ``file:``', 'directives. Please make sure the given file or attribute respects :pep:`440`.'], 'oneOf': [{'title': "'attr:' directive", '$id': '#/definitions/attr-directive', '$$description': ['Value is read from a module attribute. Supports callables and iterables;', 'unsupported types are cast via ``str()``'], 'type': 'object', 'additionalProperties': False, 'properties': {'attr': {'type': 'string'}}, 'required': ['attr']}, {'$id': '#/definitions/file-directive', 'title': "'file:' directive", 'description': 'Value is read from a file (or list of files and then concatenated)', 'type': 'object', 'additionalProperties': False, 'properties': {'file': {'oneOf': [{'type': 'string'}, {'type': 'array', 'items': {'type': 'string'}}]}}, 'required': ['file']}]}, rule='oneOf') + if "classifiers" in data__dynamic_keys: + data__dynamic_keys.remove("classifiers") + data__dynamic__classifiers = data__dynamic["classifiers"] + validate_https___setuptools_pypa_io_en_latest_references_keywords_html__definitions_file_directive(data__dynamic__classifiers, custom_formats, (name_prefix or "data") + ".dynamic.classifiers") + if "description" in data__dynamic_keys: + data__dynamic_keys.remove("description") + data__dynamic__description = data__dynamic["description"] + validate_https___setuptools_pypa_io_en_latest_references_keywords_html__definitions_file_directive(data__dynamic__description, custom_formats, (name_prefix or "data") + ".dynamic.description") + if "dependencies" in data__dynamic_keys: + data__dynamic_keys.remove("dependencies") + data__dynamic__dependencies = data__dynamic["dependencies"] + validate_https___setuptools_pypa_io_en_latest_references_keywords_html__definitions_file_directive(data__dynamic__dependencies, custom_formats, (name_prefix or "data") + ".dynamic.dependencies") + if "entry-points" in data__dynamic_keys: + data__dynamic_keys.remove("entry-points") + data__dynamic__entrypoints = data__dynamic["entry-points"] + validate_https___setuptools_pypa_io_en_latest_references_keywords_html__definitions_file_directive(data__dynamic__entrypoints, custom_formats, (name_prefix or "data") + ".dynamic.entry-points") + if "optional-dependencies" in data__dynamic_keys: + data__dynamic_keys.remove("optional-dependencies") + data__dynamic__optionaldependencies = data__dynamic["optional-dependencies"] + if not isinstance(data__dynamic__optionaldependencies, (dict)): + raise JsonSchemaValueException("" + (name_prefix or "data") + ".dynamic.optional-dependencies must be object", value=data__dynamic__optionaldependencies, name="" + (name_prefix or "data") + ".dynamic.optional-dependencies", definition={'type': 'object', 'propertyNames': {'format': 'python-identifier'}, 'additionalProperties': False, 'patternProperties': {'.+': {'$id': '#/definitions/file-directive', 'title': "'file:' directive", 'description': 'Value is read from a file (or list of files and then concatenated)', 'type': 'object', 'additionalProperties': False, 'properties': {'file': {'oneOf': [{'type': 'string'}, {'type': 'array', 'items': {'type': 'string'}}]}}, 'required': ['file']}}}, rule='type') + data__dynamic__optionaldependencies_is_dict = isinstance(data__dynamic__optionaldependencies, dict) + if data__dynamic__optionaldependencies_is_dict: + data__dynamic__optionaldependencies_keys = set(data__dynamic__optionaldependencies.keys()) + for data__dynamic__optionaldependencies_key, data__dynamic__optionaldependencies_val in data__dynamic__optionaldependencies.items(): + if REGEX_PATTERNS['.+'].search(data__dynamic__optionaldependencies_key): + if data__dynamic__optionaldependencies_key in data__dynamic__optionaldependencies_keys: + data__dynamic__optionaldependencies_keys.remove(data__dynamic__optionaldependencies_key) + validate_https___setuptools_pypa_io_en_latest_references_keywords_html__definitions_file_directive(data__dynamic__optionaldependencies_val, custom_formats, (name_prefix or "data") + ".dynamic.optional-dependencies.{data__dynamic__optionaldependencies_key}".format(**locals())) + if data__dynamic__optionaldependencies_keys: + raise JsonSchemaValueException("" + (name_prefix or "data") + ".dynamic.optional-dependencies must not contain "+str(data__dynamic__optionaldependencies_keys)+" properties", value=data__dynamic__optionaldependencies, name="" + (name_prefix or "data") + ".dynamic.optional-dependencies", definition={'type': 'object', 'propertyNames': {'format': 'python-identifier'}, 'additionalProperties': False, 'patternProperties': {'.+': {'$id': '#/definitions/file-directive', 'title': "'file:' directive", 'description': 'Value is read from a file (or list of files and then concatenated)', 'type': 'object', 'additionalProperties': False, 'properties': {'file': {'oneOf': [{'type': 'string'}, {'type': 'array', 'items': {'type': 'string'}}]}}, 'required': ['file']}}}, rule='additionalProperties') + data__dynamic__optionaldependencies_len = len(data__dynamic__optionaldependencies) + if data__dynamic__optionaldependencies_len != 0: + data__dynamic__optionaldependencies_property_names = True + for data__dynamic__optionaldependencies_key in data__dynamic__optionaldependencies: + try: + if isinstance(data__dynamic__optionaldependencies_key, str): + if not custom_formats["python-identifier"](data__dynamic__optionaldependencies_key): + raise JsonSchemaValueException("" + (name_prefix or "data") + ".dynamic.optional-dependencies must be python-identifier", value=data__dynamic__optionaldependencies_key, name="" + (name_prefix or "data") + ".dynamic.optional-dependencies", definition={'format': 'python-identifier'}, rule='format') + except JsonSchemaValueException: + data__dynamic__optionaldependencies_property_names = False + if not data__dynamic__optionaldependencies_property_names: + raise JsonSchemaValueException("" + (name_prefix or "data") + ".dynamic.optional-dependencies must be named by propertyName definition", value=data__dynamic__optionaldependencies, name="" + (name_prefix or "data") + ".dynamic.optional-dependencies", definition={'type': 'object', 'propertyNames': {'format': 'python-identifier'}, 'additionalProperties': False, 'patternProperties': {'.+': {'$id': '#/definitions/file-directive', 'title': "'file:' directive", 'description': 'Value is read from a file (or list of files and then concatenated)', 'type': 'object', 'additionalProperties': False, 'properties': {'file': {'oneOf': [{'type': 'string'}, {'type': 'array', 'items': {'type': 'string'}}]}}, 'required': ['file']}}}, rule='propertyNames') + if "readme" in data__dynamic_keys: + data__dynamic_keys.remove("readme") + data__dynamic__readme = data__dynamic["readme"] + data__dynamic__readme_any_of_count6 = 0 + if not data__dynamic__readme_any_of_count6: + try: + validate_https___setuptools_pypa_io_en_latest_references_keywords_html__definitions_file_directive(data__dynamic__readme, custom_formats, (name_prefix or "data") + ".dynamic.readme") + data__dynamic__readme_any_of_count6 += 1 + except JsonSchemaValueException: pass + if not data__dynamic__readme_any_of_count6: + try: + data__dynamic__readme_is_dict = isinstance(data__dynamic__readme, dict) + if data__dynamic__readme_is_dict: + data__dynamic__readme_keys = set(data__dynamic__readme.keys()) + if "content-type" in data__dynamic__readme_keys: + data__dynamic__readme_keys.remove("content-type") + data__dynamic__readme__contenttype = data__dynamic__readme["content-type"] + if not isinstance(data__dynamic__readme__contenttype, (str)): + raise JsonSchemaValueException("" + (name_prefix or "data") + ".dynamic.readme.content-type must be string", value=data__dynamic__readme__contenttype, name="" + (name_prefix or "data") + ".dynamic.readme.content-type", definition={'type': 'string'}, rule='type') + data__dynamic__readme_any_of_count6 += 1 + except JsonSchemaValueException: pass + if not data__dynamic__readme_any_of_count6: + raise JsonSchemaValueException("" + (name_prefix or "data") + ".dynamic.readme cannot be validated by any definition", value=data__dynamic__readme, name="" + (name_prefix or "data") + ".dynamic.readme", definition={'anyOf': [{'$id': '#/definitions/file-directive', 'title': "'file:' directive", 'description': 'Value is read from a file (or list of files and then concatenated)', 'type': 'object', 'additionalProperties': False, 'properties': {'file': {'oneOf': [{'type': 'string'}, {'type': 'array', 'items': {'type': 'string'}}]}}, 'required': ['file']}, {'properties': {'content-type': {'type': 'string'}}}], 'required': ['file']}, rule='anyOf') + data__dynamic__readme_is_dict = isinstance(data__dynamic__readme, dict) + if data__dynamic__readme_is_dict: + data__dynamic__readme_len = len(data__dynamic__readme) + if not all(prop in data__dynamic__readme for prop in ['file']): + raise JsonSchemaValueException("" + (name_prefix or "data") + ".dynamic.readme must contain ['file'] properties", value=data__dynamic__readme, name="" + (name_prefix or "data") + ".dynamic.readme", definition={'anyOf': [{'$id': '#/definitions/file-directive', 'title': "'file:' directive", 'description': 'Value is read from a file (or list of files and then concatenated)', 'type': 'object', 'additionalProperties': False, 'properties': {'file': {'oneOf': [{'type': 'string'}, {'type': 'array', 'items': {'type': 'string'}}]}}, 'required': ['file']}, {'properties': {'content-type': {'type': 'string'}}}], 'required': ['file']}, rule='required') + if data__dynamic_keys: + raise JsonSchemaValueException("" + (name_prefix or "data") + ".dynamic must not contain "+str(data__dynamic_keys)+" properties", value=data__dynamic, name="" + (name_prefix or "data") + ".dynamic", definition={'type': 'object', 'description': 'Instructions for loading :pep:`621`-related metadata dynamically', 'additionalProperties': False, 'properties': {'version': {'$$description': ['A version dynamically loaded via either the ``attr:`` or ``file:``', 'directives. Please make sure the given file or attribute respects :pep:`440`.'], 'oneOf': [{'title': "'attr:' directive", '$id': '#/definitions/attr-directive', '$$description': ['Value is read from a module attribute. Supports callables and iterables;', 'unsupported types are cast via ``str()``'], 'type': 'object', 'additionalProperties': False, 'properties': {'attr': {'type': 'string'}}, 'required': ['attr']}, {'$id': '#/definitions/file-directive', 'title': "'file:' directive", 'description': 'Value is read from a file (or list of files and then concatenated)', 'type': 'object', 'additionalProperties': False, 'properties': {'file': {'oneOf': [{'type': 'string'}, {'type': 'array', 'items': {'type': 'string'}}]}}, 'required': ['file']}]}, 'classifiers': {'$id': '#/definitions/file-directive', 'title': "'file:' directive", 'description': 'Value is read from a file (or list of files and then concatenated)', 'type': 'object', 'additionalProperties': False, 'properties': {'file': {'oneOf': [{'type': 'string'}, {'type': 'array', 'items': {'type': 'string'}}]}}, 'required': ['file']}, 'description': {'$id': '#/definitions/file-directive', 'title': "'file:' directive", 'description': 'Value is read from a file (or list of files and then concatenated)', 'type': 'object', 'additionalProperties': False, 'properties': {'file': {'oneOf': [{'type': 'string'}, {'type': 'array', 'items': {'type': 'string'}}]}}, 'required': ['file']}, 'dependencies': {'$id': '#/definitions/file-directive', 'title': "'file:' directive", 'description': 'Value is read from a file (or list of files and then concatenated)', 'type': 'object', 'additionalProperties': False, 'properties': {'file': {'oneOf': [{'type': 'string'}, {'type': 'array', 'items': {'type': 'string'}}]}}, 'required': ['file']}, 'entry-points': {'$id': '#/definitions/file-directive', 'title': "'file:' directive", 'description': 'Value is read from a file (or list of files and then concatenated)', 'type': 'object', 'additionalProperties': False, 'properties': {'file': {'oneOf': [{'type': 'string'}, {'type': 'array', 'items': {'type': 'string'}}]}}, 'required': ['file']}, 'optional-dependencies': {'type': 'object', 'propertyNames': {'format': 'python-identifier'}, 'additionalProperties': False, 'patternProperties': {'.+': {'$id': '#/definitions/file-directive', 'title': "'file:' directive", 'description': 'Value is read from a file (or list of files and then concatenated)', 'type': 'object', 'additionalProperties': False, 'properties': {'file': {'oneOf': [{'type': 'string'}, {'type': 'array', 'items': {'type': 'string'}}]}}, 'required': ['file']}}}, 'readme': {'anyOf': [{'$id': '#/definitions/file-directive', 'title': "'file:' directive", 'description': 'Value is read from a file (or list of files and then concatenated)', 'type': 'object', 'additionalProperties': False, 'properties': {'file': {'oneOf': [{'type': 'string'}, {'type': 'array', 'items': {'type': 'string'}}]}}, 'required': ['file']}, {'properties': {'content-type': {'type': 'string'}}}], 'required': ['file']}}}, rule='additionalProperties') + if data_keys: + raise JsonSchemaValueException("" + (name_prefix or "data") + " must not contain "+str(data_keys)+" properties", value=data, name="" + (name_prefix or "data") + "", definition={'$schema': 'http://json-schema.org/draft-07/schema', '$id': 'https://setuptools.pypa.io/en/latest/references/keywords.html', 'title': '``tool.setuptools`` table', '$$description': ['Please notice for the time being the ``setuptools`` project does not specify', 'a way of configuring builds via ``pyproject.toml``.', 'Therefore this schema should be taken just as a *"thought experiment"* on how', 'this *might be done*, by following the principles established in', '`ini2toml `_.', 'It considers only ``setuptools`` `parameters', '`_', 'that can currently be configured via ``setup.cfg`` and are not covered by :pep:`621`', 'but intentionally excludes ``dependency_links`` and ``setup_requires``.', 'NOTE: ``scripts`` was renamed to ``script-files`` to avoid confusion with', 'entry-point based scripts (defined in :pep:`621`).'], 'type': 'object', 'additionalProperties': False, 'properties': {'platforms': {'type': 'array', 'items': {'type': 'string'}}, 'provides': {'$$description': ['Package and virtual package names contained within this package', '**(not supported by pip)**'], 'type': 'array', 'items': {'type': 'string', 'format': 'pep508-identifier'}}, 'obsoletes': {'$$description': ['Packages which this package renders obsolete', '**(not supported by pip)**'], 'type': 'array', 'items': {'type': 'string', 'format': 'pep508-identifier'}}, 'zip-safe': {'description': 'Whether the project can be safely installed and run from a zip file.', 'type': 'boolean'}, 'script-files': {'description': 'Legacy way of defining scripts (entry-points are preferred).', 'type': 'array', 'items': {'type': 'string'}, '$comment': 'TODO: is this field deprecated/should be removed?'}, 'eager-resources': {'$$description': ['Resources that should be extracted together, if any of them is needed,', 'or if any C extensions included in the project are imported.'], 'type': 'array', 'items': {'type': 'string'}}, 'packages': {'$$description': ['Packages that should be included in the distribution.', 'It can be given either as a list of package identifiers', 'or as a ``dict``-like structure with a single key ``find``', 'which corresponds to a dynamic call to', '``setuptools.config.expand.find_packages`` function.', 'The ``find`` key is associated with a nested ``dict``-like structure that can', 'contain ``where``, ``include``, ``exclude`` and ``namespaces`` keys,', 'mimicking the keyword arguments of the associated function.'], 'oneOf': [{'title': 'Array of Python package identifiers', 'type': 'array', 'items': {'$id': '#/definitions/package-name', 'title': 'Valid package name', 'description': 'Valid package name (importable or PEP 561).', 'type': 'string', 'anyOf': [{'format': 'python-module-name'}, {'format': 'pep561-stub-name'}]}}, {'$id': '#/definitions/find-directive', 'title': "'find:' directive", 'type': 'object', 'additionalProperties': False, 'properties': {'find': {'type': 'object', '$$description': ['Dynamic `package discovery', '`_.'], 'additionalProperties': False, 'properties': {'where': {'description': 'Directories to be searched for packages (Unix-style relative path)', 'type': 'array', 'items': {'type': 'string'}}, 'exclude': {'type': 'array', '$$description': ['Exclude packages that match the values listed in this field.', "Can container shell-style wildcards (e.g. ``'pkg.*'``)"], 'items': {'type': 'string'}}, 'include': {'type': 'array', '$$description': ['Restrict the found packages to just the ones listed in this field.', "Can container shell-style wildcards (e.g. ``'pkg.*'``)"], 'items': {'type': 'string'}}, 'namespaces': {'type': 'boolean', '$$description': ['When ``True``, directories without a ``__init__.py`` file will also', 'be scanned for :pep:`420`-style implicit namespaces']}}}}}]}, 'package-dir': {'$$description': [':class:`dict`-like structure mapping from package names to directories where their', 'code can be found.', 'The empty string (as key) means that all packages are contained inside', 'the given directory will be included in the distribution.'], 'type': 'object', 'additionalProperties': False, 'propertyNames': {'oneOf': [{'const': ''}, {'$id': '#/definitions/package-name', 'title': 'Valid package name', 'description': 'Valid package name (importable or PEP 561).', 'type': 'string', 'anyOf': [{'format': 'python-module-name'}, {'format': 'pep561-stub-name'}]}]}, 'patternProperties': {'^.*$': {'type': 'string'}}}, 'package-data': {'$$description': ['Mapping from package names to lists of glob patterns.', 'Usually this option is not needed when using ``include-package-data = true``', 'For more information on how to include data files, check ``setuptools`` `docs', '`_.'], 'type': 'object', 'additionalProperties': False, 'propertyNames': {'oneOf': [{'format': 'python-module-name'}, {'const': '*'}]}, 'patternProperties': {'^.*$': {'type': 'array', 'items': {'type': 'string'}}}}, 'include-package-data': {'$$description': ['Automatically include any data files inside the package directories', 'that are specified by ``MANIFEST.in``', 'For more information on how to include data files, check ``setuptools`` `docs', '`_.'], 'type': 'boolean'}, 'exclude-package-data': {'$$description': ['Mapping from package names to lists of glob patterns that should be excluded', 'For more information on how to include data files, check ``setuptools`` `docs', '`_.'], 'type': 'object', 'additionalProperties': False, 'propertyNames': {'oneOf': [{'format': 'python-module-name'}, {'const': '*'}]}, 'patternProperties': {'^.*$': {'type': 'array', 'items': {'type': 'string'}}}}, 'namespace-packages': {'type': 'array', 'items': {'type': 'string', 'format': 'python-module-name'}, '$comment': 'https://setuptools.pypa.io/en/latest/userguide/package_discovery.html'}, 'py-modules': {'description': 'Modules that setuptools will manipulate', 'type': 'array', 'items': {'type': 'string', 'format': 'python-module-name'}, '$comment': 'TODO: clarify the relationship with ``packages``'}, 'data-files': {'$$description': ['**DEPRECATED**: dict-like structure where each key represents a directory and', 'the value is a list of glob patterns that should be installed in them.', "Please notice this don't work with wheels. See `data files support", '`_'], 'type': 'object', 'patternProperties': {'^.*$': {'type': 'array', 'items': {'type': 'string'}}}}, 'cmdclass': {'$$description': ['Mapping of distutils-style command names to ``setuptools.Command`` subclasses', 'which in turn should be represented by strings with a qualified class name', '(i.e., "dotted" form with module), e.g.::\n\n', ' cmdclass = {mycmd = "pkg.subpkg.module.CommandClass"}\n\n', 'The command class should be a directly defined at the top-level of the', 'containing module (no class nesting).'], 'type': 'object', 'patternProperties': {'^.*$': {'type': 'string', 'format': 'python-qualified-identifier'}}}, 'license-files': {'type': 'array', 'items': {'type': 'string'}, '$$description': ['PROVISIONAL: List of glob patterns for all license files being distributed.', '(might become standard with PEP 639).', "By default: ``['LICEN[CS]E*', 'COPYING*', 'NOTICE*', 'AUTHORS*']``"], '$comment': 'TODO: revise if PEP 639 is accepted. Probably ``project.license-files``?'}, 'dynamic': {'type': 'object', 'description': 'Instructions for loading :pep:`621`-related metadata dynamically', 'additionalProperties': False, 'properties': {'version': {'$$description': ['A version dynamically loaded via either the ``attr:`` or ``file:``', 'directives. Please make sure the given file or attribute respects :pep:`440`.'], 'oneOf': [{'title': "'attr:' directive", '$id': '#/definitions/attr-directive', '$$description': ['Value is read from a module attribute. Supports callables and iterables;', 'unsupported types are cast via ``str()``'], 'type': 'object', 'additionalProperties': False, 'properties': {'attr': {'type': 'string'}}, 'required': ['attr']}, {'$id': '#/definitions/file-directive', 'title': "'file:' directive", 'description': 'Value is read from a file (or list of files and then concatenated)', 'type': 'object', 'additionalProperties': False, 'properties': {'file': {'oneOf': [{'type': 'string'}, {'type': 'array', 'items': {'type': 'string'}}]}}, 'required': ['file']}]}, 'classifiers': {'$id': '#/definitions/file-directive', 'title': "'file:' directive", 'description': 'Value is read from a file (or list of files and then concatenated)', 'type': 'object', 'additionalProperties': False, 'properties': {'file': {'oneOf': [{'type': 'string'}, {'type': 'array', 'items': {'type': 'string'}}]}}, 'required': ['file']}, 'description': {'$id': '#/definitions/file-directive', 'title': "'file:' directive", 'description': 'Value is read from a file (or list of files and then concatenated)', 'type': 'object', 'additionalProperties': False, 'properties': {'file': {'oneOf': [{'type': 'string'}, {'type': 'array', 'items': {'type': 'string'}}]}}, 'required': ['file']}, 'dependencies': {'$id': '#/definitions/file-directive', 'title': "'file:' directive", 'description': 'Value is read from a file (or list of files and then concatenated)', 'type': 'object', 'additionalProperties': False, 'properties': {'file': {'oneOf': [{'type': 'string'}, {'type': 'array', 'items': {'type': 'string'}}]}}, 'required': ['file']}, 'entry-points': {'$id': '#/definitions/file-directive', 'title': "'file:' directive", 'description': 'Value is read from a file (or list of files and then concatenated)', 'type': 'object', 'additionalProperties': False, 'properties': {'file': {'oneOf': [{'type': 'string'}, {'type': 'array', 'items': {'type': 'string'}}]}}, 'required': ['file']}, 'optional-dependencies': {'type': 'object', 'propertyNames': {'format': 'python-identifier'}, 'additionalProperties': False, 'patternProperties': {'.+': {'$id': '#/definitions/file-directive', 'title': "'file:' directive", 'description': 'Value is read from a file (or list of files and then concatenated)', 'type': 'object', 'additionalProperties': False, 'properties': {'file': {'oneOf': [{'type': 'string'}, {'type': 'array', 'items': {'type': 'string'}}]}}, 'required': ['file']}}}, 'readme': {'anyOf': [{'$id': '#/definitions/file-directive', 'title': "'file:' directive", 'description': 'Value is read from a file (or list of files and then concatenated)', 'type': 'object', 'additionalProperties': False, 'properties': {'file': {'oneOf': [{'type': 'string'}, {'type': 'array', 'items': {'type': 'string'}}]}}, 'required': ['file']}, {'properties': {'content-type': {'type': 'string'}}}], 'required': ['file']}}}}, 'definitions': {'package-name': {'$id': '#/definitions/package-name', 'title': 'Valid package name', 'description': 'Valid package name (importable or PEP 561).', 'type': 'string', 'anyOf': [{'format': 'python-module-name'}, {'format': 'pep561-stub-name'}]}, 'file-directive': {'$id': '#/definitions/file-directive', 'title': "'file:' directive", 'description': 'Value is read from a file (or list of files and then concatenated)', 'type': 'object', 'additionalProperties': False, 'properties': {'file': {'oneOf': [{'type': 'string'}, {'type': 'array', 'items': {'type': 'string'}}]}}, 'required': ['file']}, 'attr-directive': {'title': "'attr:' directive", '$id': '#/definitions/attr-directive', '$$description': ['Value is read from a module attribute. Supports callables and iterables;', 'unsupported types are cast via ``str()``'], 'type': 'object', 'additionalProperties': False, 'properties': {'attr': {'type': 'string'}}, 'required': ['attr']}, 'find-directive': {'$id': '#/definitions/find-directive', 'title': "'find:' directive", 'type': 'object', 'additionalProperties': False, 'properties': {'find': {'type': 'object', '$$description': ['Dynamic `package discovery', '`_.'], 'additionalProperties': False, 'properties': {'where': {'description': 'Directories to be searched for packages (Unix-style relative path)', 'type': 'array', 'items': {'type': 'string'}}, 'exclude': {'type': 'array', '$$description': ['Exclude packages that match the values listed in this field.', "Can container shell-style wildcards (e.g. ``'pkg.*'``)"], 'items': {'type': 'string'}}, 'include': {'type': 'array', '$$description': ['Restrict the found packages to just the ones listed in this field.', "Can container shell-style wildcards (e.g. ``'pkg.*'``)"], 'items': {'type': 'string'}}, 'namespaces': {'type': 'boolean', '$$description': ['When ``True``, directories without a ``__init__.py`` file will also', 'be scanned for :pep:`420`-style implicit namespaces']}}}}}}}, rule='additionalProperties') + return data + +def validate_https___setuptools_pypa_io_en_latest_references_keywords_html__definitions_file_directive(data, custom_formats={}, name_prefix=None): + if not isinstance(data, (dict)): + raise JsonSchemaValueException("" + (name_prefix or "data") + " must be object", value=data, name="" + (name_prefix or "data") + "", definition={'$id': '#/definitions/file-directive', 'title': "'file:' directive", 'description': 'Value is read from a file (or list of files and then concatenated)', 'type': 'object', 'additionalProperties': False, 'properties': {'file': {'oneOf': [{'type': 'string'}, {'type': 'array', 'items': {'type': 'string'}}]}}, 'required': ['file']}, rule='type') + data_is_dict = isinstance(data, dict) + if data_is_dict: + data_len = len(data) + if not all(prop in data for prop in ['file']): + raise JsonSchemaValueException("" + (name_prefix or "data") + " must contain ['file'] properties", value=data, name="" + (name_prefix or "data") + "", definition={'$id': '#/definitions/file-directive', 'title': "'file:' directive", 'description': 'Value is read from a file (or list of files and then concatenated)', 'type': 'object', 'additionalProperties': False, 'properties': {'file': {'oneOf': [{'type': 'string'}, {'type': 'array', 'items': {'type': 'string'}}]}}, 'required': ['file']}, rule='required') + data_keys = set(data.keys()) + if "file" in data_keys: + data_keys.remove("file") + data__file = data["file"] + data__file_one_of_count7 = 0 + if data__file_one_of_count7 < 2: + try: + if not isinstance(data__file, (str)): + raise JsonSchemaValueException("" + (name_prefix or "data") + ".file must be string", value=data__file, name="" + (name_prefix or "data") + ".file", definition={'type': 'string'}, rule='type') + data__file_one_of_count7 += 1 + except JsonSchemaValueException: pass + if data__file_one_of_count7 < 2: + try: + if not isinstance(data__file, (list, tuple)): + raise JsonSchemaValueException("" + (name_prefix or "data") + ".file must be array", value=data__file, name="" + (name_prefix or "data") + ".file", definition={'type': 'array', 'items': {'type': 'string'}}, rule='type') + data__file_is_list = isinstance(data__file, (list, tuple)) + if data__file_is_list: + data__file_len = len(data__file) + for data__file_x, data__file_item in enumerate(data__file): + if not isinstance(data__file_item, (str)): + raise JsonSchemaValueException("" + (name_prefix or "data") + ".file[{data__file_x}]".format(**locals()) + " must be string", value=data__file_item, name="" + (name_prefix or "data") + ".file[{data__file_x}]".format(**locals()) + "", definition={'type': 'string'}, rule='type') + data__file_one_of_count7 += 1 + except JsonSchemaValueException: pass + if data__file_one_of_count7 != 1: + raise JsonSchemaValueException("" + (name_prefix or "data") + ".file must be valid exactly by one definition" + (" (" + str(data__file_one_of_count7) + " matches found)"), value=data__file, name="" + (name_prefix or "data") + ".file", definition={'oneOf': [{'type': 'string'}, {'type': 'array', 'items': {'type': 'string'}}]}, rule='oneOf') + if data_keys: + raise JsonSchemaValueException("" + (name_prefix or "data") + " must not contain "+str(data_keys)+" properties", value=data, name="" + (name_prefix or "data") + "", definition={'$id': '#/definitions/file-directive', 'title': "'file:' directive", 'description': 'Value is read from a file (or list of files and then concatenated)', 'type': 'object', 'additionalProperties': False, 'properties': {'file': {'oneOf': [{'type': 'string'}, {'type': 'array', 'items': {'type': 'string'}}]}}, 'required': ['file']}, rule='additionalProperties') + return data + +def validate_https___setuptools_pypa_io_en_latest_references_keywords_html__definitions_attr_directive(data, custom_formats={}, name_prefix=None): + if not isinstance(data, (dict)): + raise JsonSchemaValueException("" + (name_prefix or "data") + " must be object", value=data, name="" + (name_prefix or "data") + "", definition={'title': "'attr:' directive", '$id': '#/definitions/attr-directive', '$$description': ['Value is read from a module attribute. Supports callables and iterables;', 'unsupported types are cast via ``str()``'], 'type': 'object', 'additionalProperties': False, 'properties': {'attr': {'type': 'string'}}, 'required': ['attr']}, rule='type') + data_is_dict = isinstance(data, dict) + if data_is_dict: + data_len = len(data) + if not all(prop in data for prop in ['attr']): + raise JsonSchemaValueException("" + (name_prefix or "data") + " must contain ['attr'] properties", value=data, name="" + (name_prefix or "data") + "", definition={'title': "'attr:' directive", '$id': '#/definitions/attr-directive', '$$description': ['Value is read from a module attribute. Supports callables and iterables;', 'unsupported types are cast via ``str()``'], 'type': 'object', 'additionalProperties': False, 'properties': {'attr': {'type': 'string'}}, 'required': ['attr']}, rule='required') + data_keys = set(data.keys()) + if "attr" in data_keys: + data_keys.remove("attr") + data__attr = data["attr"] + if not isinstance(data__attr, (str)): + raise JsonSchemaValueException("" + (name_prefix or "data") + ".attr must be string", value=data__attr, name="" + (name_prefix or "data") + ".attr", definition={'type': 'string'}, rule='type') + if data_keys: + raise JsonSchemaValueException("" + (name_prefix or "data") + " must not contain "+str(data_keys)+" properties", value=data, name="" + (name_prefix or "data") + "", definition={'title': "'attr:' directive", '$id': '#/definitions/attr-directive', '$$description': ['Value is read from a module attribute. Supports callables and iterables;', 'unsupported types are cast via ``str()``'], 'type': 'object', 'additionalProperties': False, 'properties': {'attr': {'type': 'string'}}, 'required': ['attr']}, rule='additionalProperties') + return data + +def validate_https___setuptools_pypa_io_en_latest_references_keywords_html__definitions_find_directive(data, custom_formats={}, name_prefix=None): + if not isinstance(data, (dict)): + raise JsonSchemaValueException("" + (name_prefix or "data") + " must be object", value=data, name="" + (name_prefix or "data") + "", definition={'$id': '#/definitions/find-directive', 'title': "'find:' directive", 'type': 'object', 'additionalProperties': False, 'properties': {'find': {'type': 'object', '$$description': ['Dynamic `package discovery', '`_.'], 'additionalProperties': False, 'properties': {'where': {'description': 'Directories to be searched for packages (Unix-style relative path)', 'type': 'array', 'items': {'type': 'string'}}, 'exclude': {'type': 'array', '$$description': ['Exclude packages that match the values listed in this field.', "Can container shell-style wildcards (e.g. ``'pkg.*'``)"], 'items': {'type': 'string'}}, 'include': {'type': 'array', '$$description': ['Restrict the found packages to just the ones listed in this field.', "Can container shell-style wildcards (e.g. ``'pkg.*'``)"], 'items': {'type': 'string'}}, 'namespaces': {'type': 'boolean', '$$description': ['When ``True``, directories without a ``__init__.py`` file will also', 'be scanned for :pep:`420`-style implicit namespaces']}}}}}, rule='type') + data_is_dict = isinstance(data, dict) + if data_is_dict: + data_keys = set(data.keys()) + if "find" in data_keys: + data_keys.remove("find") + data__find = data["find"] + if not isinstance(data__find, (dict)): + raise JsonSchemaValueException("" + (name_prefix or "data") + ".find must be object", value=data__find, name="" + (name_prefix or "data") + ".find", definition={'type': 'object', '$$description': ['Dynamic `package discovery', '`_.'], 'additionalProperties': False, 'properties': {'where': {'description': 'Directories to be searched for packages (Unix-style relative path)', 'type': 'array', 'items': {'type': 'string'}}, 'exclude': {'type': 'array', '$$description': ['Exclude packages that match the values listed in this field.', "Can container shell-style wildcards (e.g. ``'pkg.*'``)"], 'items': {'type': 'string'}}, 'include': {'type': 'array', '$$description': ['Restrict the found packages to just the ones listed in this field.', "Can container shell-style wildcards (e.g. ``'pkg.*'``)"], 'items': {'type': 'string'}}, 'namespaces': {'type': 'boolean', '$$description': ['When ``True``, directories without a ``__init__.py`` file will also', 'be scanned for :pep:`420`-style implicit namespaces']}}}, rule='type') + data__find_is_dict = isinstance(data__find, dict) + if data__find_is_dict: + data__find_keys = set(data__find.keys()) + if "where" in data__find_keys: + data__find_keys.remove("where") + data__find__where = data__find["where"] + if not isinstance(data__find__where, (list, tuple)): + raise JsonSchemaValueException("" + (name_prefix or "data") + ".find.where must be array", value=data__find__where, name="" + (name_prefix or "data") + ".find.where", definition={'description': 'Directories to be searched for packages (Unix-style relative path)', 'type': 'array', 'items': {'type': 'string'}}, rule='type') + data__find__where_is_list = isinstance(data__find__where, (list, tuple)) + if data__find__where_is_list: + data__find__where_len = len(data__find__where) + for data__find__where_x, data__find__where_item in enumerate(data__find__where): + if not isinstance(data__find__where_item, (str)): + raise JsonSchemaValueException("" + (name_prefix or "data") + ".find.where[{data__find__where_x}]".format(**locals()) + " must be string", value=data__find__where_item, name="" + (name_prefix or "data") + ".find.where[{data__find__where_x}]".format(**locals()) + "", definition={'type': 'string'}, rule='type') + if "exclude" in data__find_keys: + data__find_keys.remove("exclude") + data__find__exclude = data__find["exclude"] + if not isinstance(data__find__exclude, (list, tuple)): + raise JsonSchemaValueException("" + (name_prefix or "data") + ".find.exclude must be array", value=data__find__exclude, name="" + (name_prefix or "data") + ".find.exclude", definition={'type': 'array', '$$description': ['Exclude packages that match the values listed in this field.', "Can container shell-style wildcards (e.g. ``'pkg.*'``)"], 'items': {'type': 'string'}}, rule='type') + data__find__exclude_is_list = isinstance(data__find__exclude, (list, tuple)) + if data__find__exclude_is_list: + data__find__exclude_len = len(data__find__exclude) + for data__find__exclude_x, data__find__exclude_item in enumerate(data__find__exclude): + if not isinstance(data__find__exclude_item, (str)): + raise JsonSchemaValueException("" + (name_prefix or "data") + ".find.exclude[{data__find__exclude_x}]".format(**locals()) + " must be string", value=data__find__exclude_item, name="" + (name_prefix or "data") + ".find.exclude[{data__find__exclude_x}]".format(**locals()) + "", definition={'type': 'string'}, rule='type') + if "include" in data__find_keys: + data__find_keys.remove("include") + data__find__include = data__find["include"] + if not isinstance(data__find__include, (list, tuple)): + raise JsonSchemaValueException("" + (name_prefix or "data") + ".find.include must be array", value=data__find__include, name="" + (name_prefix or "data") + ".find.include", definition={'type': 'array', '$$description': ['Restrict the found packages to just the ones listed in this field.', "Can container shell-style wildcards (e.g. ``'pkg.*'``)"], 'items': {'type': 'string'}}, rule='type') + data__find__include_is_list = isinstance(data__find__include, (list, tuple)) + if data__find__include_is_list: + data__find__include_len = len(data__find__include) + for data__find__include_x, data__find__include_item in enumerate(data__find__include): + if not isinstance(data__find__include_item, (str)): + raise JsonSchemaValueException("" + (name_prefix or "data") + ".find.include[{data__find__include_x}]".format(**locals()) + " must be string", value=data__find__include_item, name="" + (name_prefix or "data") + ".find.include[{data__find__include_x}]".format(**locals()) + "", definition={'type': 'string'}, rule='type') + if "namespaces" in data__find_keys: + data__find_keys.remove("namespaces") + data__find__namespaces = data__find["namespaces"] + if not isinstance(data__find__namespaces, (bool)): + raise JsonSchemaValueException("" + (name_prefix or "data") + ".find.namespaces must be boolean", value=data__find__namespaces, name="" + (name_prefix or "data") + ".find.namespaces", definition={'type': 'boolean', '$$description': ['When ``True``, directories without a ``__init__.py`` file will also', 'be scanned for :pep:`420`-style implicit namespaces']}, rule='type') + if data__find_keys: + raise JsonSchemaValueException("" + (name_prefix or "data") + ".find must not contain "+str(data__find_keys)+" properties", value=data__find, name="" + (name_prefix or "data") + ".find", definition={'type': 'object', '$$description': ['Dynamic `package discovery', '`_.'], 'additionalProperties': False, 'properties': {'where': {'description': 'Directories to be searched for packages (Unix-style relative path)', 'type': 'array', 'items': {'type': 'string'}}, 'exclude': {'type': 'array', '$$description': ['Exclude packages that match the values listed in this field.', "Can container shell-style wildcards (e.g. ``'pkg.*'``)"], 'items': {'type': 'string'}}, 'include': {'type': 'array', '$$description': ['Restrict the found packages to just the ones listed in this field.', "Can container shell-style wildcards (e.g. ``'pkg.*'``)"], 'items': {'type': 'string'}}, 'namespaces': {'type': 'boolean', '$$description': ['When ``True``, directories without a ``__init__.py`` file will also', 'be scanned for :pep:`420`-style implicit namespaces']}}}, rule='additionalProperties') + if data_keys: + raise JsonSchemaValueException("" + (name_prefix or "data") + " must not contain "+str(data_keys)+" properties", value=data, name="" + (name_prefix or "data") + "", definition={'$id': '#/definitions/find-directive', 'title': "'find:' directive", 'type': 'object', 'additionalProperties': False, 'properties': {'find': {'type': 'object', '$$description': ['Dynamic `package discovery', '`_.'], 'additionalProperties': False, 'properties': {'where': {'description': 'Directories to be searched for packages (Unix-style relative path)', 'type': 'array', 'items': {'type': 'string'}}, 'exclude': {'type': 'array', '$$description': ['Exclude packages that match the values listed in this field.', "Can container shell-style wildcards (e.g. ``'pkg.*'``)"], 'items': {'type': 'string'}}, 'include': {'type': 'array', '$$description': ['Restrict the found packages to just the ones listed in this field.', "Can container shell-style wildcards (e.g. ``'pkg.*'``)"], 'items': {'type': 'string'}}, 'namespaces': {'type': 'boolean', '$$description': ['When ``True``, directories without a ``__init__.py`` file will also', 'be scanned for :pep:`420`-style implicit namespaces']}}}}}, rule='additionalProperties') + return data + +def validate_https___setuptools_pypa_io_en_latest_references_keywords_html__definitions_package_name(data, custom_formats={}, name_prefix=None): + if not isinstance(data, (str)): + raise JsonSchemaValueException("" + (name_prefix or "data") + " must be string", value=data, name="" + (name_prefix or "data") + "", definition={'$id': '#/definitions/package-name', 'title': 'Valid package name', 'description': 'Valid package name (importable or PEP 561).', 'type': 'string', 'anyOf': [{'format': 'python-module-name'}, {'format': 'pep561-stub-name'}]}, rule='type') + data_any_of_count8 = 0 + if not data_any_of_count8: + try: + if isinstance(data, str): + if not custom_formats["python-module-name"](data): + raise JsonSchemaValueException("" + (name_prefix or "data") + " must be python-module-name", value=data, name="" + (name_prefix or "data") + "", definition={'format': 'python-module-name'}, rule='format') + data_any_of_count8 += 1 + except JsonSchemaValueException: pass + if not data_any_of_count8: + try: + if isinstance(data, str): + if not custom_formats["pep561-stub-name"](data): + raise JsonSchemaValueException("" + (name_prefix or "data") + " must be pep561-stub-name", value=data, name="" + (name_prefix or "data") + "", definition={'format': 'pep561-stub-name'}, rule='format') + data_any_of_count8 += 1 + except JsonSchemaValueException: pass + if not data_any_of_count8: + raise JsonSchemaValueException("" + (name_prefix or "data") + " cannot be validated by any definition", value=data, name="" + (name_prefix or "data") + "", definition={'$id': '#/definitions/package-name', 'title': 'Valid package name', 'description': 'Valid package name (importable or PEP 561).', 'type': 'string', 'anyOf': [{'format': 'python-module-name'}, {'format': 'pep561-stub-name'}]}, rule='anyOf') + return data + +def validate_https___docs_python_org_3_install(data, custom_formats={}, name_prefix=None): + if not isinstance(data, (dict)): + raise JsonSchemaValueException("" + (name_prefix or "data") + " must be object", value=data, name="" + (name_prefix or "data") + "", definition={'$schema': 'http://json-schema.org/draft-07/schema', '$id': 'https://docs.python.org/3/install/', 'title': '``tool.distutils`` table', '$$description': ['Originally, ``distutils`` allowed developers to configure arguments for', '``setup.py`` scripts via `distutils configuration files', '`_.', '``tool.distutils`` subtables could be used with the same purpose', '(NOT CURRENTLY IMPLEMENTED).'], 'type': 'object', 'properties': {'global': {'type': 'object', 'description': 'Global options applied to all ``distutils`` commands'}}, 'patternProperties': {'.+': {'type': 'object'}}, '$comment': 'TODO: Is there a practical way of making this schema more specific?'}, rule='type') + data_is_dict = isinstance(data, dict) + if data_is_dict: + data_keys = set(data.keys()) + if "global" in data_keys: + data_keys.remove("global") + data__global = data["global"] + if not isinstance(data__global, (dict)): + raise JsonSchemaValueException("" + (name_prefix or "data") + ".global must be object", value=data__global, name="" + (name_prefix or "data") + ".global", definition={'type': 'object', 'description': 'Global options applied to all ``distutils`` commands'}, rule='type') + for data_key, data_val in data.items(): + if REGEX_PATTERNS['.+'].search(data_key): + if data_key in data_keys: + data_keys.remove(data_key) + if not isinstance(data_val, (dict)): + raise JsonSchemaValueException("" + (name_prefix or "data") + ".{data_key}".format(**locals()) + " must be object", value=data_val, name="" + (name_prefix or "data") + ".{data_key}".format(**locals()) + "", definition={'type': 'object'}, rule='type') + return data + +def validate_https___packaging_python_org_en_latest_specifications_declaring_project_metadata(data, custom_formats={}, name_prefix=None): + if not isinstance(data, (dict)): + raise JsonSchemaValueException("" + (name_prefix or "data") + " must be object", value=data, name="" + (name_prefix or "data") + "", definition={'$schema': 'http://json-schema.org/draft-07/schema', '$id': 'https://packaging.python.org/en/latest/specifications/declaring-project-metadata/', 'title': 'Package metadata stored in the ``project`` table', '$$description': ['Data structure for the **project** table inside ``pyproject.toml``', '(as initially defined in :pep:`621`)'], 'type': 'object', 'properties': {'name': {'type': 'string', 'description': 'The name (primary identifier) of the project. MUST be statically defined.', 'format': 'pep508-identifier'}, 'version': {'type': 'string', 'description': 'The version of the project as supported by :pep:`440`.', 'format': 'pep440'}, 'description': {'type': 'string', '$$description': ['The `summary description of the project', '`_']}, 'readme': {'$$description': ['`Full/detailed description of the project in the form of a README', '`_', "with meaning similar to the one defined in `core metadata's Description", '`_'], 'oneOf': [{'type': 'string', '$$description': ['Relative path to a text file (UTF-8) containing the full description', 'of the project. If the file path ends in case-insensitive ``.md`` or', '``.rst`` suffixes, then the content-type is respectively', '``text/markdown`` or ``text/x-rst``']}, {'type': 'object', 'allOf': [{'anyOf': [{'properties': {'file': {'type': 'string', '$$description': ['Relative path to a text file containing the full description', 'of the project.']}}, 'required': ['file']}, {'properties': {'text': {'type': 'string', 'description': 'Full text describing the project.'}}, 'required': ['text']}]}, {'properties': {'content-type': {'type': 'string', '$$description': ['Content-type (:rfc:`1341`) of the full description', '(e.g. ``text/markdown``). The ``charset`` parameter is assumed', 'UTF-8 when not present.'], '$comment': 'TODO: add regex pattern or format?'}}, 'required': ['content-type']}]}]}, 'requires-python': {'type': 'string', 'format': 'pep508-versionspec', '$$description': ['`The Python version requirements of the project', '`_.']}, 'license': {'description': '`Project license `_.', 'oneOf': [{'properties': {'file': {'type': 'string', '$$description': ['Relative path to the file (UTF-8) which contains the license for the', 'project.']}}, 'required': ['file']}, {'properties': {'text': {'type': 'string', '$$description': ['The license of the project whose meaning is that of the', '`License field from the core metadata', '`_.']}}, 'required': ['text']}]}, 'authors': {'type': 'array', 'items': {'$id': '#/definitions/author', 'title': 'Author or Maintainer', '$comment': 'https://peps.python.org/pep-0621/#authors-maintainers', 'type': 'object', 'additionalProperties': False, 'properties': {'name': {'type': 'string', '$$description': ['MUST be a valid email name, i.e. whatever can be put as a name, before an', 'email, in :rfc:`822`.']}, 'email': {'type': 'string', 'format': 'idn-email', 'description': 'MUST be a valid email address'}}}, '$$description': ["The people or organizations considered to be the 'authors' of the project.", 'The exact meaning is open to interpretation (e.g. original or primary authors,', 'current maintainers, or owners of the package).']}, 'maintainers': {'type': 'array', 'items': {'$id': '#/definitions/author', 'title': 'Author or Maintainer', '$comment': 'https://peps.python.org/pep-0621/#authors-maintainers', 'type': 'object', 'additionalProperties': False, 'properties': {'name': {'type': 'string', '$$description': ['MUST be a valid email name, i.e. whatever can be put as a name, before an', 'email, in :rfc:`822`.']}, 'email': {'type': 'string', 'format': 'idn-email', 'description': 'MUST be a valid email address'}}}, '$$description': ["The people or organizations considered to be the 'maintainers' of the project.", 'Similarly to ``authors``, the exact meaning is open to interpretation.']}, 'keywords': {'type': 'array', 'items': {'type': 'string'}, 'description': 'List of keywords to assist searching for the distribution in a larger catalog.'}, 'classifiers': {'type': 'array', 'items': {'type': 'string', 'format': 'trove-classifier', 'description': '`PyPI classifier `_.'}, '$$description': ['`Trove classifiers `_', 'which apply to the project.']}, 'urls': {'type': 'object', 'description': 'URLs associated with the project in the form ``label => value``.', 'additionalProperties': False, 'patternProperties': {'^.+$': {'type': 'string', 'format': 'url'}}}, 'scripts': {'$id': '#/definitions/entry-point-group', 'title': 'Entry-points', 'type': 'object', '$$description': ['Entry-points are grouped together to indicate what sort of capabilities they', 'provide.', 'See the `packaging guides', '`_', 'and `setuptools docs', '`_', 'for more information.'], 'propertyNames': {'format': 'python-entrypoint-name'}, 'additionalProperties': False, 'patternProperties': {'^.+$': {'type': 'string', '$$description': ['Reference to a Python object. It is either in the form', '``importable.module``, or ``importable.module:object.attr``.'], 'format': 'python-entrypoint-reference', '$comment': 'https://packaging.python.org/specifications/entry-points/'}}}, 'gui-scripts': {'$id': '#/definitions/entry-point-group', 'title': 'Entry-points', 'type': 'object', '$$description': ['Entry-points are grouped together to indicate what sort of capabilities they', 'provide.', 'See the `packaging guides', '`_', 'and `setuptools docs', '`_', 'for more information.'], 'propertyNames': {'format': 'python-entrypoint-name'}, 'additionalProperties': False, 'patternProperties': {'^.+$': {'type': 'string', '$$description': ['Reference to a Python object. It is either in the form', '``importable.module``, or ``importable.module:object.attr``.'], 'format': 'python-entrypoint-reference', '$comment': 'https://packaging.python.org/specifications/entry-points/'}}}, 'entry-points': {'$$description': ['Instruct the installer to expose the given modules/functions via', '``entry-point`` discovery mechanism (useful for plugins).', 'More information available in the `Python packaging guide', '`_.'], 'propertyNames': {'format': 'python-entrypoint-group'}, 'additionalProperties': False, 'patternProperties': {'^.+$': {'$id': '#/definitions/entry-point-group', 'title': 'Entry-points', 'type': 'object', '$$description': ['Entry-points are grouped together to indicate what sort of capabilities they', 'provide.', 'See the `packaging guides', '`_', 'and `setuptools docs', '`_', 'for more information.'], 'propertyNames': {'format': 'python-entrypoint-name'}, 'additionalProperties': False, 'patternProperties': {'^.+$': {'type': 'string', '$$description': ['Reference to a Python object. It is either in the form', '``importable.module``, or ``importable.module:object.attr``.'], 'format': 'python-entrypoint-reference', '$comment': 'https://packaging.python.org/specifications/entry-points/'}}}}}, 'dependencies': {'type': 'array', 'description': 'Project (mandatory) dependencies.', 'items': {'$id': '#/definitions/dependency', 'title': 'Dependency', 'type': 'string', 'description': 'Project dependency specification according to PEP 508', 'format': 'pep508'}}, 'optional-dependencies': {'type': 'object', 'description': 'Optional dependency for the project', 'propertyNames': {'format': 'pep508-identifier'}, 'additionalProperties': False, 'patternProperties': {'^.+$': {'type': 'array', 'items': {'$id': '#/definitions/dependency', 'title': 'Dependency', 'type': 'string', 'description': 'Project dependency specification according to PEP 508', 'format': 'pep508'}}}}, 'dynamic': {'type': 'array', '$$description': ['Specifies which fields are intentionally unspecified and expected to be', 'dynamically provided by build tools'], 'items': {'enum': ['version', 'description', 'readme', 'requires-python', 'license', 'authors', 'maintainers', 'keywords', 'classifiers', 'urls', 'scripts', 'gui-scripts', 'entry-points', 'dependencies', 'optional-dependencies']}}}, 'required': ['name'], 'additionalProperties': False, 'if': {'not': {'required': ['dynamic'], 'properties': {'dynamic': {'contains': {'const': 'version'}, '$$description': ['version is listed in ``dynamic``']}}}, '$$comment': ['According to :pep:`621`:', ' If the core metadata specification lists a field as "Required", then', ' the metadata MUST specify the field statically or list it in dynamic', 'In turn, `core metadata`_ defines:', ' The required fields are: Metadata-Version, Name, Version.', ' All the other fields are optional.', 'Since ``Metadata-Version`` is defined by the build back-end, ``name`` and', '``version`` are the only mandatory information in ``pyproject.toml``.', '.. _core metadata: https://packaging.python.org/specifications/core-metadata/']}, 'then': {'required': ['version'], '$$description': ['version should be statically defined in the ``version`` field']}, 'definitions': {'author': {'$id': '#/definitions/author', 'title': 'Author or Maintainer', '$comment': 'https://peps.python.org/pep-0621/#authors-maintainers', 'type': 'object', 'additionalProperties': False, 'properties': {'name': {'type': 'string', '$$description': ['MUST be a valid email name, i.e. whatever can be put as a name, before an', 'email, in :rfc:`822`.']}, 'email': {'type': 'string', 'format': 'idn-email', 'description': 'MUST be a valid email address'}}}, 'entry-point-group': {'$id': '#/definitions/entry-point-group', 'title': 'Entry-points', 'type': 'object', '$$description': ['Entry-points are grouped together to indicate what sort of capabilities they', 'provide.', 'See the `packaging guides', '`_', 'and `setuptools docs', '`_', 'for more information.'], 'propertyNames': {'format': 'python-entrypoint-name'}, 'additionalProperties': False, 'patternProperties': {'^.+$': {'type': 'string', '$$description': ['Reference to a Python object. It is either in the form', '``importable.module``, or ``importable.module:object.attr``.'], 'format': 'python-entrypoint-reference', '$comment': 'https://packaging.python.org/specifications/entry-points/'}}}, 'dependency': {'$id': '#/definitions/dependency', 'title': 'Dependency', 'type': 'string', 'description': 'Project dependency specification according to PEP 508', 'format': 'pep508'}}}, rule='type') + data_is_dict = isinstance(data, dict) + if data_is_dict: + data_len = len(data) + if not all(prop in data for prop in ['name']): + raise JsonSchemaValueException("" + (name_prefix or "data") + " must contain ['name'] properties", value=data, name="" + (name_prefix or "data") + "", definition={'$schema': 'http://json-schema.org/draft-07/schema', '$id': 'https://packaging.python.org/en/latest/specifications/declaring-project-metadata/', 'title': 'Package metadata stored in the ``project`` table', '$$description': ['Data structure for the **project** table inside ``pyproject.toml``', '(as initially defined in :pep:`621`)'], 'type': 'object', 'properties': {'name': {'type': 'string', 'description': 'The name (primary identifier) of the project. MUST be statically defined.', 'format': 'pep508-identifier'}, 'version': {'type': 'string', 'description': 'The version of the project as supported by :pep:`440`.', 'format': 'pep440'}, 'description': {'type': 'string', '$$description': ['The `summary description of the project', '`_']}, 'readme': {'$$description': ['`Full/detailed description of the project in the form of a README', '`_', "with meaning similar to the one defined in `core metadata's Description", '`_'], 'oneOf': [{'type': 'string', '$$description': ['Relative path to a text file (UTF-8) containing the full description', 'of the project. If the file path ends in case-insensitive ``.md`` or', '``.rst`` suffixes, then the content-type is respectively', '``text/markdown`` or ``text/x-rst``']}, {'type': 'object', 'allOf': [{'anyOf': [{'properties': {'file': {'type': 'string', '$$description': ['Relative path to a text file containing the full description', 'of the project.']}}, 'required': ['file']}, {'properties': {'text': {'type': 'string', 'description': 'Full text describing the project.'}}, 'required': ['text']}]}, {'properties': {'content-type': {'type': 'string', '$$description': ['Content-type (:rfc:`1341`) of the full description', '(e.g. ``text/markdown``). The ``charset`` parameter is assumed', 'UTF-8 when not present.'], '$comment': 'TODO: add regex pattern or format?'}}, 'required': ['content-type']}]}]}, 'requires-python': {'type': 'string', 'format': 'pep508-versionspec', '$$description': ['`The Python version requirements of the project', '`_.']}, 'license': {'description': '`Project license `_.', 'oneOf': [{'properties': {'file': {'type': 'string', '$$description': ['Relative path to the file (UTF-8) which contains the license for the', 'project.']}}, 'required': ['file']}, {'properties': {'text': {'type': 'string', '$$description': ['The license of the project whose meaning is that of the', '`License field from the core metadata', '`_.']}}, 'required': ['text']}]}, 'authors': {'type': 'array', 'items': {'$id': '#/definitions/author', 'title': 'Author or Maintainer', '$comment': 'https://peps.python.org/pep-0621/#authors-maintainers', 'type': 'object', 'additionalProperties': False, 'properties': {'name': {'type': 'string', '$$description': ['MUST be a valid email name, i.e. whatever can be put as a name, before an', 'email, in :rfc:`822`.']}, 'email': {'type': 'string', 'format': 'idn-email', 'description': 'MUST be a valid email address'}}}, '$$description': ["The people or organizations considered to be the 'authors' of the project.", 'The exact meaning is open to interpretation (e.g. original or primary authors,', 'current maintainers, or owners of the package).']}, 'maintainers': {'type': 'array', 'items': {'$id': '#/definitions/author', 'title': 'Author or Maintainer', '$comment': 'https://peps.python.org/pep-0621/#authors-maintainers', 'type': 'object', 'additionalProperties': False, 'properties': {'name': {'type': 'string', '$$description': ['MUST be a valid email name, i.e. whatever can be put as a name, before an', 'email, in :rfc:`822`.']}, 'email': {'type': 'string', 'format': 'idn-email', 'description': 'MUST be a valid email address'}}}, '$$description': ["The people or organizations considered to be the 'maintainers' of the project.", 'Similarly to ``authors``, the exact meaning is open to interpretation.']}, 'keywords': {'type': 'array', 'items': {'type': 'string'}, 'description': 'List of keywords to assist searching for the distribution in a larger catalog.'}, 'classifiers': {'type': 'array', 'items': {'type': 'string', 'format': 'trove-classifier', 'description': '`PyPI classifier `_.'}, '$$description': ['`Trove classifiers `_', 'which apply to the project.']}, 'urls': {'type': 'object', 'description': 'URLs associated with the project in the form ``label => value``.', 'additionalProperties': False, 'patternProperties': {'^.+$': {'type': 'string', 'format': 'url'}}}, 'scripts': {'$id': '#/definitions/entry-point-group', 'title': 'Entry-points', 'type': 'object', '$$description': ['Entry-points are grouped together to indicate what sort of capabilities they', 'provide.', 'See the `packaging guides', '`_', 'and `setuptools docs', '`_', 'for more information.'], 'propertyNames': {'format': 'python-entrypoint-name'}, 'additionalProperties': False, 'patternProperties': {'^.+$': {'type': 'string', '$$description': ['Reference to a Python object. It is either in the form', '``importable.module``, or ``importable.module:object.attr``.'], 'format': 'python-entrypoint-reference', '$comment': 'https://packaging.python.org/specifications/entry-points/'}}}, 'gui-scripts': {'$id': '#/definitions/entry-point-group', 'title': 'Entry-points', 'type': 'object', '$$description': ['Entry-points are grouped together to indicate what sort of capabilities they', 'provide.', 'See the `packaging guides', '`_', 'and `setuptools docs', '`_', 'for more information.'], 'propertyNames': {'format': 'python-entrypoint-name'}, 'additionalProperties': False, 'patternProperties': {'^.+$': {'type': 'string', '$$description': ['Reference to a Python object. It is either in the form', '``importable.module``, or ``importable.module:object.attr``.'], 'format': 'python-entrypoint-reference', '$comment': 'https://packaging.python.org/specifications/entry-points/'}}}, 'entry-points': {'$$description': ['Instruct the installer to expose the given modules/functions via', '``entry-point`` discovery mechanism (useful for plugins).', 'More information available in the `Python packaging guide', '`_.'], 'propertyNames': {'format': 'python-entrypoint-group'}, 'additionalProperties': False, 'patternProperties': {'^.+$': {'$id': '#/definitions/entry-point-group', 'title': 'Entry-points', 'type': 'object', '$$description': ['Entry-points are grouped together to indicate what sort of capabilities they', 'provide.', 'See the `packaging guides', '`_', 'and `setuptools docs', '`_', 'for more information.'], 'propertyNames': {'format': 'python-entrypoint-name'}, 'additionalProperties': False, 'patternProperties': {'^.+$': {'type': 'string', '$$description': ['Reference to a Python object. It is either in the form', '``importable.module``, or ``importable.module:object.attr``.'], 'format': 'python-entrypoint-reference', '$comment': 'https://packaging.python.org/specifications/entry-points/'}}}}}, 'dependencies': {'type': 'array', 'description': 'Project (mandatory) dependencies.', 'items': {'$id': '#/definitions/dependency', 'title': 'Dependency', 'type': 'string', 'description': 'Project dependency specification according to PEP 508', 'format': 'pep508'}}, 'optional-dependencies': {'type': 'object', 'description': 'Optional dependency for the project', 'propertyNames': {'format': 'pep508-identifier'}, 'additionalProperties': False, 'patternProperties': {'^.+$': {'type': 'array', 'items': {'$id': '#/definitions/dependency', 'title': 'Dependency', 'type': 'string', 'description': 'Project dependency specification according to PEP 508', 'format': 'pep508'}}}}, 'dynamic': {'type': 'array', '$$description': ['Specifies which fields are intentionally unspecified and expected to be', 'dynamically provided by build tools'], 'items': {'enum': ['version', 'description', 'readme', 'requires-python', 'license', 'authors', 'maintainers', 'keywords', 'classifiers', 'urls', 'scripts', 'gui-scripts', 'entry-points', 'dependencies', 'optional-dependencies']}}}, 'required': ['name'], 'additionalProperties': False, 'if': {'not': {'required': ['dynamic'], 'properties': {'dynamic': {'contains': {'const': 'version'}, '$$description': ['version is listed in ``dynamic``']}}}, '$$comment': ['According to :pep:`621`:', ' If the core metadata specification lists a field as "Required", then', ' the metadata MUST specify the field statically or list it in dynamic', 'In turn, `core metadata`_ defines:', ' The required fields are: Metadata-Version, Name, Version.', ' All the other fields are optional.', 'Since ``Metadata-Version`` is defined by the build back-end, ``name`` and', '``version`` are the only mandatory information in ``pyproject.toml``.', '.. _core metadata: https://packaging.python.org/specifications/core-metadata/']}, 'then': {'required': ['version'], '$$description': ['version should be statically defined in the ``version`` field']}, 'definitions': {'author': {'$id': '#/definitions/author', 'title': 'Author or Maintainer', '$comment': 'https://peps.python.org/pep-0621/#authors-maintainers', 'type': 'object', 'additionalProperties': False, 'properties': {'name': {'type': 'string', '$$description': ['MUST be a valid email name, i.e. whatever can be put as a name, before an', 'email, in :rfc:`822`.']}, 'email': {'type': 'string', 'format': 'idn-email', 'description': 'MUST be a valid email address'}}}, 'entry-point-group': {'$id': '#/definitions/entry-point-group', 'title': 'Entry-points', 'type': 'object', '$$description': ['Entry-points are grouped together to indicate what sort of capabilities they', 'provide.', 'See the `packaging guides', '`_', 'and `setuptools docs', '`_', 'for more information.'], 'propertyNames': {'format': 'python-entrypoint-name'}, 'additionalProperties': False, 'patternProperties': {'^.+$': {'type': 'string', '$$description': ['Reference to a Python object. It is either in the form', '``importable.module``, or ``importable.module:object.attr``.'], 'format': 'python-entrypoint-reference', '$comment': 'https://packaging.python.org/specifications/entry-points/'}}}, 'dependency': {'$id': '#/definitions/dependency', 'title': 'Dependency', 'type': 'string', 'description': 'Project dependency specification according to PEP 508', 'format': 'pep508'}}}, rule='required') + data_keys = set(data.keys()) + if "name" in data_keys: + data_keys.remove("name") + data__name = data["name"] + if not isinstance(data__name, (str)): + raise JsonSchemaValueException("" + (name_prefix or "data") + ".name must be string", value=data__name, name="" + (name_prefix or "data") + ".name", definition={'type': 'string', 'description': 'The name (primary identifier) of the project. MUST be statically defined.', 'format': 'pep508-identifier'}, rule='type') + if isinstance(data__name, str): + if not custom_formats["pep508-identifier"](data__name): + raise JsonSchemaValueException("" + (name_prefix or "data") + ".name must be pep508-identifier", value=data__name, name="" + (name_prefix or "data") + ".name", definition={'type': 'string', 'description': 'The name (primary identifier) of the project. MUST be statically defined.', 'format': 'pep508-identifier'}, rule='format') + if "version" in data_keys: + data_keys.remove("version") + data__version = data["version"] + if not isinstance(data__version, (str)): + raise JsonSchemaValueException("" + (name_prefix or "data") + ".version must be string", value=data__version, name="" + (name_prefix or "data") + ".version", definition={'type': 'string', 'description': 'The version of the project as supported by :pep:`440`.', 'format': 'pep440'}, rule='type') + if isinstance(data__version, str): + if not custom_formats["pep440"](data__version): + raise JsonSchemaValueException("" + (name_prefix or "data") + ".version must be pep440", value=data__version, name="" + (name_prefix or "data") + ".version", definition={'type': 'string', 'description': 'The version of the project as supported by :pep:`440`.', 'format': 'pep440'}, rule='format') + if "description" in data_keys: + data_keys.remove("description") + data__description = data["description"] + if not isinstance(data__description, (str)): + raise JsonSchemaValueException("" + (name_prefix or "data") + ".description must be string", value=data__description, name="" + (name_prefix or "data") + ".description", definition={'type': 'string', '$$description': ['The `summary description of the project', '`_']}, rule='type') + if "readme" in data_keys: + data_keys.remove("readme") + data__readme = data["readme"] + data__readme_one_of_count9 = 0 + if data__readme_one_of_count9 < 2: + try: + if not isinstance(data__readme, (str)): + raise JsonSchemaValueException("" + (name_prefix or "data") + ".readme must be string", value=data__readme, name="" + (name_prefix or "data") + ".readme", definition={'type': 'string', '$$description': ['Relative path to a text file (UTF-8) containing the full description', 'of the project. If the file path ends in case-insensitive ``.md`` or', '``.rst`` suffixes, then the content-type is respectively', '``text/markdown`` or ``text/x-rst``']}, rule='type') + data__readme_one_of_count9 += 1 + except JsonSchemaValueException: pass + if data__readme_one_of_count9 < 2: + try: + if not isinstance(data__readme, (dict)): + raise JsonSchemaValueException("" + (name_prefix or "data") + ".readme must be object", value=data__readme, name="" + (name_prefix or "data") + ".readme", definition={'type': 'object', 'allOf': [{'anyOf': [{'properties': {'file': {'type': 'string', '$$description': ['Relative path to a text file containing the full description', 'of the project.']}}, 'required': ['file']}, {'properties': {'text': {'type': 'string', 'description': 'Full text describing the project.'}}, 'required': ['text']}]}, {'properties': {'content-type': {'type': 'string', '$$description': ['Content-type (:rfc:`1341`) of the full description', '(e.g. ``text/markdown``). The ``charset`` parameter is assumed', 'UTF-8 when not present.'], '$comment': 'TODO: add regex pattern or format?'}}, 'required': ['content-type']}]}, rule='type') + data__readme_any_of_count10 = 0 + if not data__readme_any_of_count10: + try: + data__readme_is_dict = isinstance(data__readme, dict) + if data__readme_is_dict: + data__readme_len = len(data__readme) + if not all(prop in data__readme for prop in ['file']): + raise JsonSchemaValueException("" + (name_prefix or "data") + ".readme must contain ['file'] properties", value=data__readme, name="" + (name_prefix or "data") + ".readme", definition={'properties': {'file': {'type': 'string', '$$description': ['Relative path to a text file containing the full description', 'of the project.']}}, 'required': ['file']}, rule='required') + data__readme_keys = set(data__readme.keys()) + if "file" in data__readme_keys: + data__readme_keys.remove("file") + data__readme__file = data__readme["file"] + if not isinstance(data__readme__file, (str)): + raise JsonSchemaValueException("" + (name_prefix or "data") + ".readme.file must be string", value=data__readme__file, name="" + (name_prefix or "data") + ".readme.file", definition={'type': 'string', '$$description': ['Relative path to a text file containing the full description', 'of the project.']}, rule='type') + data__readme_any_of_count10 += 1 + except JsonSchemaValueException: pass + if not data__readme_any_of_count10: + try: + data__readme_is_dict = isinstance(data__readme, dict) + if data__readme_is_dict: + data__readme_len = len(data__readme) + if not all(prop in data__readme for prop in ['text']): + raise JsonSchemaValueException("" + (name_prefix or "data") + ".readme must contain ['text'] properties", value=data__readme, name="" + (name_prefix or "data") + ".readme", definition={'properties': {'text': {'type': 'string', 'description': 'Full text describing the project.'}}, 'required': ['text']}, rule='required') + data__readme_keys = set(data__readme.keys()) + if "text" in data__readme_keys: + data__readme_keys.remove("text") + data__readme__text = data__readme["text"] + if not isinstance(data__readme__text, (str)): + raise JsonSchemaValueException("" + (name_prefix or "data") + ".readme.text must be string", value=data__readme__text, name="" + (name_prefix or "data") + ".readme.text", definition={'type': 'string', 'description': 'Full text describing the project.'}, rule='type') + data__readme_any_of_count10 += 1 + except JsonSchemaValueException: pass + if not data__readme_any_of_count10: + raise JsonSchemaValueException("" + (name_prefix or "data") + ".readme cannot be validated by any definition", value=data__readme, name="" + (name_prefix or "data") + ".readme", definition={'anyOf': [{'properties': {'file': {'type': 'string', '$$description': ['Relative path to a text file containing the full description', 'of the project.']}}, 'required': ['file']}, {'properties': {'text': {'type': 'string', 'description': 'Full text describing the project.'}}, 'required': ['text']}]}, rule='anyOf') + data__readme_is_dict = isinstance(data__readme, dict) + if data__readme_is_dict: + data__readme_len = len(data__readme) + if not all(prop in data__readme for prop in ['content-type']): + raise JsonSchemaValueException("" + (name_prefix or "data") + ".readme must contain ['content-type'] properties", value=data__readme, name="" + (name_prefix or "data") + ".readme", definition={'properties': {'content-type': {'type': 'string', '$$description': ['Content-type (:rfc:`1341`) of the full description', '(e.g. ``text/markdown``). The ``charset`` parameter is assumed', 'UTF-8 when not present.'], '$comment': 'TODO: add regex pattern or format?'}}, 'required': ['content-type']}, rule='required') + data__readme_keys = set(data__readme.keys()) + if "content-type" in data__readme_keys: + data__readme_keys.remove("content-type") + data__readme__contenttype = data__readme["content-type"] + if not isinstance(data__readme__contenttype, (str)): + raise JsonSchemaValueException("" + (name_prefix or "data") + ".readme.content-type must be string", value=data__readme__contenttype, name="" + (name_prefix or "data") + ".readme.content-type", definition={'type': 'string', '$$description': ['Content-type (:rfc:`1341`) of the full description', '(e.g. ``text/markdown``). The ``charset`` parameter is assumed', 'UTF-8 when not present.'], '$comment': 'TODO: add regex pattern or format?'}, rule='type') + data__readme_one_of_count9 += 1 + except JsonSchemaValueException: pass + if data__readme_one_of_count9 != 1: + raise JsonSchemaValueException("" + (name_prefix or "data") + ".readme must be valid exactly by one definition" + (" (" + str(data__readme_one_of_count9) + " matches found)"), value=data__readme, name="" + (name_prefix or "data") + ".readme", definition={'$$description': ['`Full/detailed description of the project in the form of a README', '`_', "with meaning similar to the one defined in `core metadata's Description", '`_'], 'oneOf': [{'type': 'string', '$$description': ['Relative path to a text file (UTF-8) containing the full description', 'of the project. If the file path ends in case-insensitive ``.md`` or', '``.rst`` suffixes, then the content-type is respectively', '``text/markdown`` or ``text/x-rst``']}, {'type': 'object', 'allOf': [{'anyOf': [{'properties': {'file': {'type': 'string', '$$description': ['Relative path to a text file containing the full description', 'of the project.']}}, 'required': ['file']}, {'properties': {'text': {'type': 'string', 'description': 'Full text describing the project.'}}, 'required': ['text']}]}, {'properties': {'content-type': {'type': 'string', '$$description': ['Content-type (:rfc:`1341`) of the full description', '(e.g. ``text/markdown``). The ``charset`` parameter is assumed', 'UTF-8 when not present.'], '$comment': 'TODO: add regex pattern or format?'}}, 'required': ['content-type']}]}]}, rule='oneOf') + if "requires-python" in data_keys: + data_keys.remove("requires-python") + data__requirespython = data["requires-python"] + if not isinstance(data__requirespython, (str)): + raise JsonSchemaValueException("" + (name_prefix or "data") + ".requires-python must be string", value=data__requirespython, name="" + (name_prefix or "data") + ".requires-python", definition={'type': 'string', 'format': 'pep508-versionspec', '$$description': ['`The Python version requirements of the project', '`_.']}, rule='type') + if isinstance(data__requirespython, str): + if not custom_formats["pep508-versionspec"](data__requirespython): + raise JsonSchemaValueException("" + (name_prefix or "data") + ".requires-python must be pep508-versionspec", value=data__requirespython, name="" + (name_prefix or "data") + ".requires-python", definition={'type': 'string', 'format': 'pep508-versionspec', '$$description': ['`The Python version requirements of the project', '`_.']}, rule='format') + if "license" in data_keys: + data_keys.remove("license") + data__license = data["license"] + data__license_one_of_count11 = 0 + if data__license_one_of_count11 < 2: + try: + data__license_is_dict = isinstance(data__license, dict) + if data__license_is_dict: + data__license_len = len(data__license) + if not all(prop in data__license for prop in ['file']): + raise JsonSchemaValueException("" + (name_prefix or "data") + ".license must contain ['file'] properties", value=data__license, name="" + (name_prefix or "data") + ".license", definition={'properties': {'file': {'type': 'string', '$$description': ['Relative path to the file (UTF-8) which contains the license for the', 'project.']}}, 'required': ['file']}, rule='required') + data__license_keys = set(data__license.keys()) + if "file" in data__license_keys: + data__license_keys.remove("file") + data__license__file = data__license["file"] + if not isinstance(data__license__file, (str)): + raise JsonSchemaValueException("" + (name_prefix or "data") + ".license.file must be string", value=data__license__file, name="" + (name_prefix or "data") + ".license.file", definition={'type': 'string', '$$description': ['Relative path to the file (UTF-8) which contains the license for the', 'project.']}, rule='type') + data__license_one_of_count11 += 1 + except JsonSchemaValueException: pass + if data__license_one_of_count11 < 2: + try: + data__license_is_dict = isinstance(data__license, dict) + if data__license_is_dict: + data__license_len = len(data__license) + if not all(prop in data__license for prop in ['text']): + raise JsonSchemaValueException("" + (name_prefix or "data") + ".license must contain ['text'] properties", value=data__license, name="" + (name_prefix or "data") + ".license", definition={'properties': {'text': {'type': 'string', '$$description': ['The license of the project whose meaning is that of the', '`License field from the core metadata', '`_.']}}, 'required': ['text']}, rule='required') + data__license_keys = set(data__license.keys()) + if "text" in data__license_keys: + data__license_keys.remove("text") + data__license__text = data__license["text"] + if not isinstance(data__license__text, (str)): + raise JsonSchemaValueException("" + (name_prefix or "data") + ".license.text must be string", value=data__license__text, name="" + (name_prefix or "data") + ".license.text", definition={'type': 'string', '$$description': ['The license of the project whose meaning is that of the', '`License field from the core metadata', '`_.']}, rule='type') + data__license_one_of_count11 += 1 + except JsonSchemaValueException: pass + if data__license_one_of_count11 != 1: + raise JsonSchemaValueException("" + (name_prefix or "data") + ".license must be valid exactly by one definition" + (" (" + str(data__license_one_of_count11) + " matches found)"), value=data__license, name="" + (name_prefix or "data") + ".license", definition={'description': '`Project license `_.', 'oneOf': [{'properties': {'file': {'type': 'string', '$$description': ['Relative path to the file (UTF-8) which contains the license for the', 'project.']}}, 'required': ['file']}, {'properties': {'text': {'type': 'string', '$$description': ['The license of the project whose meaning is that of the', '`License field from the core metadata', '`_.']}}, 'required': ['text']}]}, rule='oneOf') + if "authors" in data_keys: + data_keys.remove("authors") + data__authors = data["authors"] + if not isinstance(data__authors, (list, tuple)): + raise JsonSchemaValueException("" + (name_prefix or "data") + ".authors must be array", value=data__authors, name="" + (name_prefix or "data") + ".authors", definition={'type': 'array', 'items': {'$id': '#/definitions/author', 'title': 'Author or Maintainer', '$comment': 'https://peps.python.org/pep-0621/#authors-maintainers', 'type': 'object', 'additionalProperties': False, 'properties': {'name': {'type': 'string', '$$description': ['MUST be a valid email name, i.e. whatever can be put as a name, before an', 'email, in :rfc:`822`.']}, 'email': {'type': 'string', 'format': 'idn-email', 'description': 'MUST be a valid email address'}}}, '$$description': ["The people or organizations considered to be the 'authors' of the project.", 'The exact meaning is open to interpretation (e.g. original or primary authors,', 'current maintainers, or owners of the package).']}, rule='type') + data__authors_is_list = isinstance(data__authors, (list, tuple)) + if data__authors_is_list: + data__authors_len = len(data__authors) + for data__authors_x, data__authors_item in enumerate(data__authors): + validate_https___packaging_python_org_en_latest_specifications_declaring_project_metadata___definitions_author(data__authors_item, custom_formats, (name_prefix or "data") + ".authors[{data__authors_x}]".format(**locals())) + if "maintainers" in data_keys: + data_keys.remove("maintainers") + data__maintainers = data["maintainers"] + if not isinstance(data__maintainers, (list, tuple)): + raise JsonSchemaValueException("" + (name_prefix or "data") + ".maintainers must be array", value=data__maintainers, name="" + (name_prefix or "data") + ".maintainers", definition={'type': 'array', 'items': {'$id': '#/definitions/author', 'title': 'Author or Maintainer', '$comment': 'https://peps.python.org/pep-0621/#authors-maintainers', 'type': 'object', 'additionalProperties': False, 'properties': {'name': {'type': 'string', '$$description': ['MUST be a valid email name, i.e. whatever can be put as a name, before an', 'email, in :rfc:`822`.']}, 'email': {'type': 'string', 'format': 'idn-email', 'description': 'MUST be a valid email address'}}}, '$$description': ["The people or organizations considered to be the 'maintainers' of the project.", 'Similarly to ``authors``, the exact meaning is open to interpretation.']}, rule='type') + data__maintainers_is_list = isinstance(data__maintainers, (list, tuple)) + if data__maintainers_is_list: + data__maintainers_len = len(data__maintainers) + for data__maintainers_x, data__maintainers_item in enumerate(data__maintainers): + validate_https___packaging_python_org_en_latest_specifications_declaring_project_metadata___definitions_author(data__maintainers_item, custom_formats, (name_prefix or "data") + ".maintainers[{data__maintainers_x}]".format(**locals())) + if "keywords" in data_keys: + data_keys.remove("keywords") + data__keywords = data["keywords"] + if not isinstance(data__keywords, (list, tuple)): + raise JsonSchemaValueException("" + (name_prefix or "data") + ".keywords must be array", value=data__keywords, name="" + (name_prefix or "data") + ".keywords", definition={'type': 'array', 'items': {'type': 'string'}, 'description': 'List of keywords to assist searching for the distribution in a larger catalog.'}, rule='type') + data__keywords_is_list = isinstance(data__keywords, (list, tuple)) + if data__keywords_is_list: + data__keywords_len = len(data__keywords) + for data__keywords_x, data__keywords_item in enumerate(data__keywords): + if not isinstance(data__keywords_item, (str)): + raise JsonSchemaValueException("" + (name_prefix or "data") + ".keywords[{data__keywords_x}]".format(**locals()) + " must be string", value=data__keywords_item, name="" + (name_prefix or "data") + ".keywords[{data__keywords_x}]".format(**locals()) + "", definition={'type': 'string'}, rule='type') + if "classifiers" in data_keys: + data_keys.remove("classifiers") + data__classifiers = data["classifiers"] + if not isinstance(data__classifiers, (list, tuple)): + raise JsonSchemaValueException("" + (name_prefix or "data") + ".classifiers must be array", value=data__classifiers, name="" + (name_prefix or "data") + ".classifiers", definition={'type': 'array', 'items': {'type': 'string', 'format': 'trove-classifier', 'description': '`PyPI classifier `_.'}, '$$description': ['`Trove classifiers `_', 'which apply to the project.']}, rule='type') + data__classifiers_is_list = isinstance(data__classifiers, (list, tuple)) + if data__classifiers_is_list: + data__classifiers_len = len(data__classifiers) + for data__classifiers_x, data__classifiers_item in enumerate(data__classifiers): + if not isinstance(data__classifiers_item, (str)): + raise JsonSchemaValueException("" + (name_prefix or "data") + ".classifiers[{data__classifiers_x}]".format(**locals()) + " must be string", value=data__classifiers_item, name="" + (name_prefix or "data") + ".classifiers[{data__classifiers_x}]".format(**locals()) + "", definition={'type': 'string', 'format': 'trove-classifier', 'description': '`PyPI classifier `_.'}, rule='type') + if isinstance(data__classifiers_item, str): + if not custom_formats["trove-classifier"](data__classifiers_item): + raise JsonSchemaValueException("" + (name_prefix or "data") + ".classifiers[{data__classifiers_x}]".format(**locals()) + " must be trove-classifier", value=data__classifiers_item, name="" + (name_prefix or "data") + ".classifiers[{data__classifiers_x}]".format(**locals()) + "", definition={'type': 'string', 'format': 'trove-classifier', 'description': '`PyPI classifier `_.'}, rule='format') + if "urls" in data_keys: + data_keys.remove("urls") + data__urls = data["urls"] + if not isinstance(data__urls, (dict)): + raise JsonSchemaValueException("" + (name_prefix or "data") + ".urls must be object", value=data__urls, name="" + (name_prefix or "data") + ".urls", definition={'type': 'object', 'description': 'URLs associated with the project in the form ``label => value``.', 'additionalProperties': False, 'patternProperties': {'^.+$': {'type': 'string', 'format': 'url'}}}, rule='type') + data__urls_is_dict = isinstance(data__urls, dict) + if data__urls_is_dict: + data__urls_keys = set(data__urls.keys()) + for data__urls_key, data__urls_val in data__urls.items(): + if REGEX_PATTERNS['^.+$'].search(data__urls_key): + if data__urls_key in data__urls_keys: + data__urls_keys.remove(data__urls_key) + if not isinstance(data__urls_val, (str)): + raise JsonSchemaValueException("" + (name_prefix or "data") + ".urls.{data__urls_key}".format(**locals()) + " must be string", value=data__urls_val, name="" + (name_prefix or "data") + ".urls.{data__urls_key}".format(**locals()) + "", definition={'type': 'string', 'format': 'url'}, rule='type') + if isinstance(data__urls_val, str): + if not custom_formats["url"](data__urls_val): + raise JsonSchemaValueException("" + (name_prefix or "data") + ".urls.{data__urls_key}".format(**locals()) + " must be url", value=data__urls_val, name="" + (name_prefix or "data") + ".urls.{data__urls_key}".format(**locals()) + "", definition={'type': 'string', 'format': 'url'}, rule='format') + if data__urls_keys: + raise JsonSchemaValueException("" + (name_prefix or "data") + ".urls must not contain "+str(data__urls_keys)+" properties", value=data__urls, name="" + (name_prefix or "data") + ".urls", definition={'type': 'object', 'description': 'URLs associated with the project in the form ``label => value``.', 'additionalProperties': False, 'patternProperties': {'^.+$': {'type': 'string', 'format': 'url'}}}, rule='additionalProperties') + if "scripts" in data_keys: + data_keys.remove("scripts") + data__scripts = data["scripts"] + validate_https___packaging_python_org_en_latest_specifications_declaring_project_metadata___definitions_entry_point_group(data__scripts, custom_formats, (name_prefix or "data") + ".scripts") + if "gui-scripts" in data_keys: + data_keys.remove("gui-scripts") + data__guiscripts = data["gui-scripts"] + validate_https___packaging_python_org_en_latest_specifications_declaring_project_metadata___definitions_entry_point_group(data__guiscripts, custom_formats, (name_prefix or "data") + ".gui-scripts") + if "entry-points" in data_keys: + data_keys.remove("entry-points") + data__entrypoints = data["entry-points"] + data__entrypoints_is_dict = isinstance(data__entrypoints, dict) + if data__entrypoints_is_dict: + data__entrypoints_keys = set(data__entrypoints.keys()) + for data__entrypoints_key, data__entrypoints_val in data__entrypoints.items(): + if REGEX_PATTERNS['^.+$'].search(data__entrypoints_key): + if data__entrypoints_key in data__entrypoints_keys: + data__entrypoints_keys.remove(data__entrypoints_key) + validate_https___packaging_python_org_en_latest_specifications_declaring_project_metadata___definitions_entry_point_group(data__entrypoints_val, custom_formats, (name_prefix or "data") + ".entry-points.{data__entrypoints_key}".format(**locals())) + if data__entrypoints_keys: + raise JsonSchemaValueException("" + (name_prefix or "data") + ".entry-points must not contain "+str(data__entrypoints_keys)+" properties", value=data__entrypoints, name="" + (name_prefix or "data") + ".entry-points", definition={'$$description': ['Instruct the installer to expose the given modules/functions via', '``entry-point`` discovery mechanism (useful for plugins).', 'More information available in the `Python packaging guide', '`_.'], 'propertyNames': {'format': 'python-entrypoint-group'}, 'additionalProperties': False, 'patternProperties': {'^.+$': {'$id': '#/definitions/entry-point-group', 'title': 'Entry-points', 'type': 'object', '$$description': ['Entry-points are grouped together to indicate what sort of capabilities they', 'provide.', 'See the `packaging guides', '`_', 'and `setuptools docs', '`_', 'for more information.'], 'propertyNames': {'format': 'python-entrypoint-name'}, 'additionalProperties': False, 'patternProperties': {'^.+$': {'type': 'string', '$$description': ['Reference to a Python object. It is either in the form', '``importable.module``, or ``importable.module:object.attr``.'], 'format': 'python-entrypoint-reference', '$comment': 'https://packaging.python.org/specifications/entry-points/'}}}}}, rule='additionalProperties') + data__entrypoints_len = len(data__entrypoints) + if data__entrypoints_len != 0: + data__entrypoints_property_names = True + for data__entrypoints_key in data__entrypoints: + try: + if isinstance(data__entrypoints_key, str): + if not custom_formats["python-entrypoint-group"](data__entrypoints_key): + raise JsonSchemaValueException("" + (name_prefix or "data") + ".entry-points must be python-entrypoint-group", value=data__entrypoints_key, name="" + (name_prefix or "data") + ".entry-points", definition={'format': 'python-entrypoint-group'}, rule='format') + except JsonSchemaValueException: + data__entrypoints_property_names = False + if not data__entrypoints_property_names: + raise JsonSchemaValueException("" + (name_prefix or "data") + ".entry-points must be named by propertyName definition", value=data__entrypoints, name="" + (name_prefix or "data") + ".entry-points", definition={'$$description': ['Instruct the installer to expose the given modules/functions via', '``entry-point`` discovery mechanism (useful for plugins).', 'More information available in the `Python packaging guide', '`_.'], 'propertyNames': {'format': 'python-entrypoint-group'}, 'additionalProperties': False, 'patternProperties': {'^.+$': {'$id': '#/definitions/entry-point-group', 'title': 'Entry-points', 'type': 'object', '$$description': ['Entry-points are grouped together to indicate what sort of capabilities they', 'provide.', 'See the `packaging guides', '`_', 'and `setuptools docs', '`_', 'for more information.'], 'propertyNames': {'format': 'python-entrypoint-name'}, 'additionalProperties': False, 'patternProperties': {'^.+$': {'type': 'string', '$$description': ['Reference to a Python object. It is either in the form', '``importable.module``, or ``importable.module:object.attr``.'], 'format': 'python-entrypoint-reference', '$comment': 'https://packaging.python.org/specifications/entry-points/'}}}}}, rule='propertyNames') + if "dependencies" in data_keys: + data_keys.remove("dependencies") + data__dependencies = data["dependencies"] + if not isinstance(data__dependencies, (list, tuple)): + raise JsonSchemaValueException("" + (name_prefix or "data") + ".dependencies must be array", value=data__dependencies, name="" + (name_prefix or "data") + ".dependencies", definition={'type': 'array', 'description': 'Project (mandatory) dependencies.', 'items': {'$id': '#/definitions/dependency', 'title': 'Dependency', 'type': 'string', 'description': 'Project dependency specification according to PEP 508', 'format': 'pep508'}}, rule='type') + data__dependencies_is_list = isinstance(data__dependencies, (list, tuple)) + if data__dependencies_is_list: + data__dependencies_len = len(data__dependencies) + for data__dependencies_x, data__dependencies_item in enumerate(data__dependencies): + validate_https___packaging_python_org_en_latest_specifications_declaring_project_metadata___definitions_dependency(data__dependencies_item, custom_formats, (name_prefix or "data") + ".dependencies[{data__dependencies_x}]".format(**locals())) + if "optional-dependencies" in data_keys: + data_keys.remove("optional-dependencies") + data__optionaldependencies = data["optional-dependencies"] + if not isinstance(data__optionaldependencies, (dict)): + raise JsonSchemaValueException("" + (name_prefix or "data") + ".optional-dependencies must be object", value=data__optionaldependencies, name="" + (name_prefix or "data") + ".optional-dependencies", definition={'type': 'object', 'description': 'Optional dependency for the project', 'propertyNames': {'format': 'pep508-identifier'}, 'additionalProperties': False, 'patternProperties': {'^.+$': {'type': 'array', 'items': {'$id': '#/definitions/dependency', 'title': 'Dependency', 'type': 'string', 'description': 'Project dependency specification according to PEP 508', 'format': 'pep508'}}}}, rule='type') + data__optionaldependencies_is_dict = isinstance(data__optionaldependencies, dict) + if data__optionaldependencies_is_dict: + data__optionaldependencies_keys = set(data__optionaldependencies.keys()) + for data__optionaldependencies_key, data__optionaldependencies_val in data__optionaldependencies.items(): + if REGEX_PATTERNS['^.+$'].search(data__optionaldependencies_key): + if data__optionaldependencies_key in data__optionaldependencies_keys: + data__optionaldependencies_keys.remove(data__optionaldependencies_key) + if not isinstance(data__optionaldependencies_val, (list, tuple)): + raise JsonSchemaValueException("" + (name_prefix or "data") + ".optional-dependencies.{data__optionaldependencies_key}".format(**locals()) + " must be array", value=data__optionaldependencies_val, name="" + (name_prefix or "data") + ".optional-dependencies.{data__optionaldependencies_key}".format(**locals()) + "", definition={'type': 'array', 'items': {'$id': '#/definitions/dependency', 'title': 'Dependency', 'type': 'string', 'description': 'Project dependency specification according to PEP 508', 'format': 'pep508'}}, rule='type') + data__optionaldependencies_val_is_list = isinstance(data__optionaldependencies_val, (list, tuple)) + if data__optionaldependencies_val_is_list: + data__optionaldependencies_val_len = len(data__optionaldependencies_val) + for data__optionaldependencies_val_x, data__optionaldependencies_val_item in enumerate(data__optionaldependencies_val): + validate_https___packaging_python_org_en_latest_specifications_declaring_project_metadata___definitions_dependency(data__optionaldependencies_val_item, custom_formats, (name_prefix or "data") + ".optional-dependencies.{data__optionaldependencies_key}[{data__optionaldependencies_val_x}]".format(**locals())) + if data__optionaldependencies_keys: + raise JsonSchemaValueException("" + (name_prefix or "data") + ".optional-dependencies must not contain "+str(data__optionaldependencies_keys)+" properties", value=data__optionaldependencies, name="" + (name_prefix or "data") + ".optional-dependencies", definition={'type': 'object', 'description': 'Optional dependency for the project', 'propertyNames': {'format': 'pep508-identifier'}, 'additionalProperties': False, 'patternProperties': {'^.+$': {'type': 'array', 'items': {'$id': '#/definitions/dependency', 'title': 'Dependency', 'type': 'string', 'description': 'Project dependency specification according to PEP 508', 'format': 'pep508'}}}}, rule='additionalProperties') + data__optionaldependencies_len = len(data__optionaldependencies) + if data__optionaldependencies_len != 0: + data__optionaldependencies_property_names = True + for data__optionaldependencies_key in data__optionaldependencies: + try: + if isinstance(data__optionaldependencies_key, str): + if not custom_formats["pep508-identifier"](data__optionaldependencies_key): + raise JsonSchemaValueException("" + (name_prefix or "data") + ".optional-dependencies must be pep508-identifier", value=data__optionaldependencies_key, name="" + (name_prefix or "data") + ".optional-dependencies", definition={'format': 'pep508-identifier'}, rule='format') + except JsonSchemaValueException: + data__optionaldependencies_property_names = False + if not data__optionaldependencies_property_names: + raise JsonSchemaValueException("" + (name_prefix or "data") + ".optional-dependencies must be named by propertyName definition", value=data__optionaldependencies, name="" + (name_prefix or "data") + ".optional-dependencies", definition={'type': 'object', 'description': 'Optional dependency for the project', 'propertyNames': {'format': 'pep508-identifier'}, 'additionalProperties': False, 'patternProperties': {'^.+$': {'type': 'array', 'items': {'$id': '#/definitions/dependency', 'title': 'Dependency', 'type': 'string', 'description': 'Project dependency specification according to PEP 508', 'format': 'pep508'}}}}, rule='propertyNames') + if "dynamic" in data_keys: + data_keys.remove("dynamic") + data__dynamic = data["dynamic"] + if not isinstance(data__dynamic, (list, tuple)): + raise JsonSchemaValueException("" + (name_prefix or "data") + ".dynamic must be array", value=data__dynamic, name="" + (name_prefix or "data") + ".dynamic", definition={'type': 'array', '$$description': ['Specifies which fields are intentionally unspecified and expected to be', 'dynamically provided by build tools'], 'items': {'enum': ['version', 'description', 'readme', 'requires-python', 'license', 'authors', 'maintainers', 'keywords', 'classifiers', 'urls', 'scripts', 'gui-scripts', 'entry-points', 'dependencies', 'optional-dependencies']}}, rule='type') + data__dynamic_is_list = isinstance(data__dynamic, (list, tuple)) + if data__dynamic_is_list: + data__dynamic_len = len(data__dynamic) + for data__dynamic_x, data__dynamic_item in enumerate(data__dynamic): + if data__dynamic_item not in ['version', 'description', 'readme', 'requires-python', 'license', 'authors', 'maintainers', 'keywords', 'classifiers', 'urls', 'scripts', 'gui-scripts', 'entry-points', 'dependencies', 'optional-dependencies']: + raise JsonSchemaValueException("" + (name_prefix or "data") + ".dynamic[{data__dynamic_x}]".format(**locals()) + " must be one of ['version', 'description', 'readme', 'requires-python', 'license', 'authors', 'maintainers', 'keywords', 'classifiers', 'urls', 'scripts', 'gui-scripts', 'entry-points', 'dependencies', 'optional-dependencies']", value=data__dynamic_item, name="" + (name_prefix or "data") + ".dynamic[{data__dynamic_x}]".format(**locals()) + "", definition={'enum': ['version', 'description', 'readme', 'requires-python', 'license', 'authors', 'maintainers', 'keywords', 'classifiers', 'urls', 'scripts', 'gui-scripts', 'entry-points', 'dependencies', 'optional-dependencies']}, rule='enum') + if data_keys: + raise JsonSchemaValueException("" + (name_prefix or "data") + " must not contain "+str(data_keys)+" properties", value=data, name="" + (name_prefix or "data") + "", definition={'$schema': 'http://json-schema.org/draft-07/schema', '$id': 'https://packaging.python.org/en/latest/specifications/declaring-project-metadata/', 'title': 'Package metadata stored in the ``project`` table', '$$description': ['Data structure for the **project** table inside ``pyproject.toml``', '(as initially defined in :pep:`621`)'], 'type': 'object', 'properties': {'name': {'type': 'string', 'description': 'The name (primary identifier) of the project. MUST be statically defined.', 'format': 'pep508-identifier'}, 'version': {'type': 'string', 'description': 'The version of the project as supported by :pep:`440`.', 'format': 'pep440'}, 'description': {'type': 'string', '$$description': ['The `summary description of the project', '`_']}, 'readme': {'$$description': ['`Full/detailed description of the project in the form of a README', '`_', "with meaning similar to the one defined in `core metadata's Description", '`_'], 'oneOf': [{'type': 'string', '$$description': ['Relative path to a text file (UTF-8) containing the full description', 'of the project. If the file path ends in case-insensitive ``.md`` or', '``.rst`` suffixes, then the content-type is respectively', '``text/markdown`` or ``text/x-rst``']}, {'type': 'object', 'allOf': [{'anyOf': [{'properties': {'file': {'type': 'string', '$$description': ['Relative path to a text file containing the full description', 'of the project.']}}, 'required': ['file']}, {'properties': {'text': {'type': 'string', 'description': 'Full text describing the project.'}}, 'required': ['text']}]}, {'properties': {'content-type': {'type': 'string', '$$description': ['Content-type (:rfc:`1341`) of the full description', '(e.g. ``text/markdown``). The ``charset`` parameter is assumed', 'UTF-8 when not present.'], '$comment': 'TODO: add regex pattern or format?'}}, 'required': ['content-type']}]}]}, 'requires-python': {'type': 'string', 'format': 'pep508-versionspec', '$$description': ['`The Python version requirements of the project', '`_.']}, 'license': {'description': '`Project license `_.', 'oneOf': [{'properties': {'file': {'type': 'string', '$$description': ['Relative path to the file (UTF-8) which contains the license for the', 'project.']}}, 'required': ['file']}, {'properties': {'text': {'type': 'string', '$$description': ['The license of the project whose meaning is that of the', '`License field from the core metadata', '`_.']}}, 'required': ['text']}]}, 'authors': {'type': 'array', 'items': {'$id': '#/definitions/author', 'title': 'Author or Maintainer', '$comment': 'https://peps.python.org/pep-0621/#authors-maintainers', 'type': 'object', 'additionalProperties': False, 'properties': {'name': {'type': 'string', '$$description': ['MUST be a valid email name, i.e. whatever can be put as a name, before an', 'email, in :rfc:`822`.']}, 'email': {'type': 'string', 'format': 'idn-email', 'description': 'MUST be a valid email address'}}}, '$$description': ["The people or organizations considered to be the 'authors' of the project.", 'The exact meaning is open to interpretation (e.g. original or primary authors,', 'current maintainers, or owners of the package).']}, 'maintainers': {'type': 'array', 'items': {'$id': '#/definitions/author', 'title': 'Author or Maintainer', '$comment': 'https://peps.python.org/pep-0621/#authors-maintainers', 'type': 'object', 'additionalProperties': False, 'properties': {'name': {'type': 'string', '$$description': ['MUST be a valid email name, i.e. whatever can be put as a name, before an', 'email, in :rfc:`822`.']}, 'email': {'type': 'string', 'format': 'idn-email', 'description': 'MUST be a valid email address'}}}, '$$description': ["The people or organizations considered to be the 'maintainers' of the project.", 'Similarly to ``authors``, the exact meaning is open to interpretation.']}, 'keywords': {'type': 'array', 'items': {'type': 'string'}, 'description': 'List of keywords to assist searching for the distribution in a larger catalog.'}, 'classifiers': {'type': 'array', 'items': {'type': 'string', 'format': 'trove-classifier', 'description': '`PyPI classifier `_.'}, '$$description': ['`Trove classifiers `_', 'which apply to the project.']}, 'urls': {'type': 'object', 'description': 'URLs associated with the project in the form ``label => value``.', 'additionalProperties': False, 'patternProperties': {'^.+$': {'type': 'string', 'format': 'url'}}}, 'scripts': {'$id': '#/definitions/entry-point-group', 'title': 'Entry-points', 'type': 'object', '$$description': ['Entry-points are grouped together to indicate what sort of capabilities they', 'provide.', 'See the `packaging guides', '`_', 'and `setuptools docs', '`_', 'for more information.'], 'propertyNames': {'format': 'python-entrypoint-name'}, 'additionalProperties': False, 'patternProperties': {'^.+$': {'type': 'string', '$$description': ['Reference to a Python object. It is either in the form', '``importable.module``, or ``importable.module:object.attr``.'], 'format': 'python-entrypoint-reference', '$comment': 'https://packaging.python.org/specifications/entry-points/'}}}, 'gui-scripts': {'$id': '#/definitions/entry-point-group', 'title': 'Entry-points', 'type': 'object', '$$description': ['Entry-points are grouped together to indicate what sort of capabilities they', 'provide.', 'See the `packaging guides', '`_', 'and `setuptools docs', '`_', 'for more information.'], 'propertyNames': {'format': 'python-entrypoint-name'}, 'additionalProperties': False, 'patternProperties': {'^.+$': {'type': 'string', '$$description': ['Reference to a Python object. It is either in the form', '``importable.module``, or ``importable.module:object.attr``.'], 'format': 'python-entrypoint-reference', '$comment': 'https://packaging.python.org/specifications/entry-points/'}}}, 'entry-points': {'$$description': ['Instruct the installer to expose the given modules/functions via', '``entry-point`` discovery mechanism (useful for plugins).', 'More information available in the `Python packaging guide', '`_.'], 'propertyNames': {'format': 'python-entrypoint-group'}, 'additionalProperties': False, 'patternProperties': {'^.+$': {'$id': '#/definitions/entry-point-group', 'title': 'Entry-points', 'type': 'object', '$$description': ['Entry-points are grouped together to indicate what sort of capabilities they', 'provide.', 'See the `packaging guides', '`_', 'and `setuptools docs', '`_', 'for more information.'], 'propertyNames': {'format': 'python-entrypoint-name'}, 'additionalProperties': False, 'patternProperties': {'^.+$': {'type': 'string', '$$description': ['Reference to a Python object. It is either in the form', '``importable.module``, or ``importable.module:object.attr``.'], 'format': 'python-entrypoint-reference', '$comment': 'https://packaging.python.org/specifications/entry-points/'}}}}}, 'dependencies': {'type': 'array', 'description': 'Project (mandatory) dependencies.', 'items': {'$id': '#/definitions/dependency', 'title': 'Dependency', 'type': 'string', 'description': 'Project dependency specification according to PEP 508', 'format': 'pep508'}}, 'optional-dependencies': {'type': 'object', 'description': 'Optional dependency for the project', 'propertyNames': {'format': 'pep508-identifier'}, 'additionalProperties': False, 'patternProperties': {'^.+$': {'type': 'array', 'items': {'$id': '#/definitions/dependency', 'title': 'Dependency', 'type': 'string', 'description': 'Project dependency specification according to PEP 508', 'format': 'pep508'}}}}, 'dynamic': {'type': 'array', '$$description': ['Specifies which fields are intentionally unspecified and expected to be', 'dynamically provided by build tools'], 'items': {'enum': ['version', 'description', 'readme', 'requires-python', 'license', 'authors', 'maintainers', 'keywords', 'classifiers', 'urls', 'scripts', 'gui-scripts', 'entry-points', 'dependencies', 'optional-dependencies']}}}, 'required': ['name'], 'additionalProperties': False, 'if': {'not': {'required': ['dynamic'], 'properties': {'dynamic': {'contains': {'const': 'version'}, '$$description': ['version is listed in ``dynamic``']}}}, '$$comment': ['According to :pep:`621`:', ' If the core metadata specification lists a field as "Required", then', ' the metadata MUST specify the field statically or list it in dynamic', 'In turn, `core metadata`_ defines:', ' The required fields are: Metadata-Version, Name, Version.', ' All the other fields are optional.', 'Since ``Metadata-Version`` is defined by the build back-end, ``name`` and', '``version`` are the only mandatory information in ``pyproject.toml``.', '.. _core metadata: https://packaging.python.org/specifications/core-metadata/']}, 'then': {'required': ['version'], '$$description': ['version should be statically defined in the ``version`` field']}, 'definitions': {'author': {'$id': '#/definitions/author', 'title': 'Author or Maintainer', '$comment': 'https://peps.python.org/pep-0621/#authors-maintainers', 'type': 'object', 'additionalProperties': False, 'properties': {'name': {'type': 'string', '$$description': ['MUST be a valid email name, i.e. whatever can be put as a name, before an', 'email, in :rfc:`822`.']}, 'email': {'type': 'string', 'format': 'idn-email', 'description': 'MUST be a valid email address'}}}, 'entry-point-group': {'$id': '#/definitions/entry-point-group', 'title': 'Entry-points', 'type': 'object', '$$description': ['Entry-points are grouped together to indicate what sort of capabilities they', 'provide.', 'See the `packaging guides', '`_', 'and `setuptools docs', '`_', 'for more information.'], 'propertyNames': {'format': 'python-entrypoint-name'}, 'additionalProperties': False, 'patternProperties': {'^.+$': {'type': 'string', '$$description': ['Reference to a Python object. It is either in the form', '``importable.module``, or ``importable.module:object.attr``.'], 'format': 'python-entrypoint-reference', '$comment': 'https://packaging.python.org/specifications/entry-points/'}}}, 'dependency': {'$id': '#/definitions/dependency', 'title': 'Dependency', 'type': 'string', 'description': 'Project dependency specification according to PEP 508', 'format': 'pep508'}}}, rule='additionalProperties') + try: + try: + data_is_dict = isinstance(data, dict) + if data_is_dict: + data_len = len(data) + if not all(prop in data for prop in ['dynamic']): + raise JsonSchemaValueException("" + (name_prefix or "data") + " must contain ['dynamic'] properties", value=data, name="" + (name_prefix or "data") + "", definition={'required': ['dynamic'], 'properties': {'dynamic': {'contains': {'const': 'version'}, '$$description': ['version is listed in ``dynamic``']}}}, rule='required') + data_keys = set(data.keys()) + if "dynamic" in data_keys: + data_keys.remove("dynamic") + data__dynamic = data["dynamic"] + data__dynamic_is_list = isinstance(data__dynamic, (list, tuple)) + if data__dynamic_is_list: + data__dynamic_contains = False + for data__dynamic_key in data__dynamic: + try: + if data__dynamic_key != "version": + raise JsonSchemaValueException("" + (name_prefix or "data") + ".dynamic must be same as const definition: version", value=data__dynamic_key, name="" + (name_prefix or "data") + ".dynamic", definition={'const': 'version'}, rule='const') + data__dynamic_contains = True + break + except JsonSchemaValueException: pass + if not data__dynamic_contains: + raise JsonSchemaValueException("" + (name_prefix or "data") + ".dynamic must contain one of contains definition", value=data__dynamic, name="" + (name_prefix or "data") + ".dynamic", definition={'contains': {'const': 'version'}, '$$description': ['version is listed in ``dynamic``']}, rule='contains') + except JsonSchemaValueException: pass + else: + raise JsonSchemaValueException("" + (name_prefix or "data") + " must NOT match a disallowed definition", value=data, name="" + (name_prefix or "data") + "", definition={'not': {'required': ['dynamic'], 'properties': {'dynamic': {'contains': {'const': 'version'}, '$$description': ['version is listed in ``dynamic``']}}}, '$$comment': ['According to :pep:`621`:', ' If the core metadata specification lists a field as "Required", then', ' the metadata MUST specify the field statically or list it in dynamic', 'In turn, `core metadata`_ defines:', ' The required fields are: Metadata-Version, Name, Version.', ' All the other fields are optional.', 'Since ``Metadata-Version`` is defined by the build back-end, ``name`` and', '``version`` are the only mandatory information in ``pyproject.toml``.', '.. _core metadata: https://packaging.python.org/specifications/core-metadata/']}, rule='not') + except JsonSchemaValueException: + pass + else: + data_is_dict = isinstance(data, dict) + if data_is_dict: + data_len = len(data) + if not all(prop in data for prop in ['version']): + raise JsonSchemaValueException("" + (name_prefix or "data") + " must contain ['version'] properties", value=data, name="" + (name_prefix or "data") + "", definition={'required': ['version'], '$$description': ['version should be statically defined in the ``version`` field']}, rule='required') + return data + +def validate_https___packaging_python_org_en_latest_specifications_declaring_project_metadata___definitions_dependency(data, custom_formats={}, name_prefix=None): + if not isinstance(data, (str)): + raise JsonSchemaValueException("" + (name_prefix or "data") + " must be string", value=data, name="" + (name_prefix or "data") + "", definition={'$id': '#/definitions/dependency', 'title': 'Dependency', 'type': 'string', 'description': 'Project dependency specification according to PEP 508', 'format': 'pep508'}, rule='type') + if isinstance(data, str): + if not custom_formats["pep508"](data): + raise JsonSchemaValueException("" + (name_prefix or "data") + " must be pep508", value=data, name="" + (name_prefix or "data") + "", definition={'$id': '#/definitions/dependency', 'title': 'Dependency', 'type': 'string', 'description': 'Project dependency specification according to PEP 508', 'format': 'pep508'}, rule='format') + return data + +def validate_https___packaging_python_org_en_latest_specifications_declaring_project_metadata___definitions_entry_point_group(data, custom_formats={}, name_prefix=None): + if not isinstance(data, (dict)): + raise JsonSchemaValueException("" + (name_prefix or "data") + " must be object", value=data, name="" + (name_prefix or "data") + "", definition={'$id': '#/definitions/entry-point-group', 'title': 'Entry-points', 'type': 'object', '$$description': ['Entry-points are grouped together to indicate what sort of capabilities they', 'provide.', 'See the `packaging guides', '`_', 'and `setuptools docs', '`_', 'for more information.'], 'propertyNames': {'format': 'python-entrypoint-name'}, 'additionalProperties': False, 'patternProperties': {'^.+$': {'type': 'string', '$$description': ['Reference to a Python object. It is either in the form', '``importable.module``, or ``importable.module:object.attr``.'], 'format': 'python-entrypoint-reference', '$comment': 'https://packaging.python.org/specifications/entry-points/'}}}, rule='type') + data_is_dict = isinstance(data, dict) + if data_is_dict: + data_keys = set(data.keys()) + for data_key, data_val in data.items(): + if REGEX_PATTERNS['^.+$'].search(data_key): + if data_key in data_keys: + data_keys.remove(data_key) + if not isinstance(data_val, (str)): + raise JsonSchemaValueException("" + (name_prefix or "data") + ".{data_key}".format(**locals()) + " must be string", value=data_val, name="" + (name_prefix or "data") + ".{data_key}".format(**locals()) + "", definition={'type': 'string', '$$description': ['Reference to a Python object. It is either in the form', '``importable.module``, or ``importable.module:object.attr``.'], 'format': 'python-entrypoint-reference', '$comment': 'https://packaging.python.org/specifications/entry-points/'}, rule='type') + if isinstance(data_val, str): + if not custom_formats["python-entrypoint-reference"](data_val): + raise JsonSchemaValueException("" + (name_prefix or "data") + ".{data_key}".format(**locals()) + " must be python-entrypoint-reference", value=data_val, name="" + (name_prefix or "data") + ".{data_key}".format(**locals()) + "", definition={'type': 'string', '$$description': ['Reference to a Python object. It is either in the form', '``importable.module``, or ``importable.module:object.attr``.'], 'format': 'python-entrypoint-reference', '$comment': 'https://packaging.python.org/specifications/entry-points/'}, rule='format') + if data_keys: + raise JsonSchemaValueException("" + (name_prefix or "data") + " must not contain "+str(data_keys)+" properties", value=data, name="" + (name_prefix or "data") + "", definition={'$id': '#/definitions/entry-point-group', 'title': 'Entry-points', 'type': 'object', '$$description': ['Entry-points are grouped together to indicate what sort of capabilities they', 'provide.', 'See the `packaging guides', '`_', 'and `setuptools docs', '`_', 'for more information.'], 'propertyNames': {'format': 'python-entrypoint-name'}, 'additionalProperties': False, 'patternProperties': {'^.+$': {'type': 'string', '$$description': ['Reference to a Python object. It is either in the form', '``importable.module``, or ``importable.module:object.attr``.'], 'format': 'python-entrypoint-reference', '$comment': 'https://packaging.python.org/specifications/entry-points/'}}}, rule='additionalProperties') + data_len = len(data) + if data_len != 0: + data_property_names = True + for data_key in data: + try: + if isinstance(data_key, str): + if not custom_formats["python-entrypoint-name"](data_key): + raise JsonSchemaValueException("" + (name_prefix or "data") + " must be python-entrypoint-name", value=data_key, name="" + (name_prefix or "data") + "", definition={'format': 'python-entrypoint-name'}, rule='format') + except JsonSchemaValueException: + data_property_names = False + if not data_property_names: + raise JsonSchemaValueException("" + (name_prefix or "data") + " must be named by propertyName definition", value=data, name="" + (name_prefix or "data") + "", definition={'$id': '#/definitions/entry-point-group', 'title': 'Entry-points', 'type': 'object', '$$description': ['Entry-points are grouped together to indicate what sort of capabilities they', 'provide.', 'See the `packaging guides', '`_', 'and `setuptools docs', '`_', 'for more information.'], 'propertyNames': {'format': 'python-entrypoint-name'}, 'additionalProperties': False, 'patternProperties': {'^.+$': {'type': 'string', '$$description': ['Reference to a Python object. It is either in the form', '``importable.module``, or ``importable.module:object.attr``.'], 'format': 'python-entrypoint-reference', '$comment': 'https://packaging.python.org/specifications/entry-points/'}}}, rule='propertyNames') + return data + +def validate_https___packaging_python_org_en_latest_specifications_declaring_project_metadata___definitions_author(data, custom_formats={}, name_prefix=None): + if not isinstance(data, (dict)): + raise JsonSchemaValueException("" + (name_prefix or "data") + " must be object", value=data, name="" + (name_prefix or "data") + "", definition={'$id': '#/definitions/author', 'title': 'Author or Maintainer', '$comment': 'https://peps.python.org/pep-0621/#authors-maintainers', 'type': 'object', 'additionalProperties': False, 'properties': {'name': {'type': 'string', '$$description': ['MUST be a valid email name, i.e. whatever can be put as a name, before an', 'email, in :rfc:`822`.']}, 'email': {'type': 'string', 'format': 'idn-email', 'description': 'MUST be a valid email address'}}}, rule='type') + data_is_dict = isinstance(data, dict) + if data_is_dict: + data_keys = set(data.keys()) + if "name" in data_keys: + data_keys.remove("name") + data__name = data["name"] + if not isinstance(data__name, (str)): + raise JsonSchemaValueException("" + (name_prefix or "data") + ".name must be string", value=data__name, name="" + (name_prefix or "data") + ".name", definition={'type': 'string', '$$description': ['MUST be a valid email name, i.e. whatever can be put as a name, before an', 'email, in :rfc:`822`.']}, rule='type') + if "email" in data_keys: + data_keys.remove("email") + data__email = data["email"] + if not isinstance(data__email, (str)): + raise JsonSchemaValueException("" + (name_prefix or "data") + ".email must be string", value=data__email, name="" + (name_prefix or "data") + ".email", definition={'type': 'string', 'format': 'idn-email', 'description': 'MUST be a valid email address'}, rule='type') + if isinstance(data__email, str): + if not REGEX_PATTERNS["idn-email_re_pattern"].match(data__email): + raise JsonSchemaValueException("" + (name_prefix or "data") + ".email must be idn-email", value=data__email, name="" + (name_prefix or "data") + ".email", definition={'type': 'string', 'format': 'idn-email', 'description': 'MUST be a valid email address'}, rule='format') + if data_keys: + raise JsonSchemaValueException("" + (name_prefix or "data") + " must not contain "+str(data_keys)+" properties", value=data, name="" + (name_prefix or "data") + "", definition={'$id': '#/definitions/author', 'title': 'Author or Maintainer', '$comment': 'https://peps.python.org/pep-0621/#authors-maintainers', 'type': 'object', 'additionalProperties': False, 'properties': {'name': {'type': 'string', '$$description': ['MUST be a valid email name, i.e. whatever can be put as a name, before an', 'email, in :rfc:`822`.']}, 'email': {'type': 'string', 'format': 'idn-email', 'description': 'MUST be a valid email address'}}}, rule='additionalProperties') + return data diff --git a/venv/Lib/site-packages/setuptools/config/_validate_pyproject/formats.py b/venv/Lib/site-packages/setuptools/config/_validate_pyproject/formats.py new file mode 100644 index 0000000..e739616 --- /dev/null +++ b/venv/Lib/site-packages/setuptools/config/_validate_pyproject/formats.py @@ -0,0 +1,275 @@ +import logging +import os +import re +import string +import typing +from itertools import chain as _chain + +if typing.TYPE_CHECKING: + from typing_extensions import Literal + +_logger = logging.getLogger(__name__) + +# ------------------------------------------------------------------------------------- +# PEP 440 + +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
+"""
+
+VERSION_REGEX = re.compile(r"^\s*" + VERSION_PATTERN + r"\s*$", re.X | re.I)
+
+
+def pep440(version: str) -> bool:
+    return VERSION_REGEX.match(version) is not None
+
+
+# -------------------------------------------------------------------------------------
+# PEP 508
+
+PEP508_IDENTIFIER_PATTERN = r"([A-Z0-9]|[A-Z0-9][A-Z0-9._-]*[A-Z0-9])"
+PEP508_IDENTIFIER_REGEX = re.compile(f"^{PEP508_IDENTIFIER_PATTERN}$", re.I)
+
+
+def pep508_identifier(name: str) -> bool:
+    return PEP508_IDENTIFIER_REGEX.match(name) is not None
+
+
+try:
+    try:
+        from packaging import requirements as _req
+    except ImportError:  # pragma: no cover
+        # let's try setuptools vendored version
+        from setuptools._vendor.packaging import requirements as _req  # type: ignore
+
+    def pep508(value: str) -> bool:
+        try:
+            _req.Requirement(value)
+            return True
+        except _req.InvalidRequirement:
+            return False
+
+except ImportError:  # pragma: no cover
+    _logger.warning(
+        "Could not find an installation of `packaging`. Requirements, dependencies and "
+        "versions might not be validated. "
+        "To enforce validation, please install `packaging`."
+    )
+
+    def pep508(value: str) -> bool:
+        return True
+
+
+def pep508_versionspec(value: str) -> bool:
+    """Expression that can be used to specify/lock versions (including ranges)"""
+    if any(c in value for c in (";", "]", "@")):
+        # In PEP 508:
+        # conditional markers, extras and URL specs are not included in the
+        # versionspec
+        return False
+    # Let's pretend we have a dependency called `requirement` with the given
+    # version spec, then we can reuse the pep508 function for validation:
+    return pep508(f"requirement{value}")
+
+
+# -------------------------------------------------------------------------------------
+# PEP 517
+
+
+def pep517_backend_reference(value: str) -> bool:
+    module, _, obj = value.partition(":")
+    identifiers = (i.strip() for i in _chain(module.split("."), obj.split(".")))
+    return all(python_identifier(i) for i in identifiers if i)
+
+
+# -------------------------------------------------------------------------------------
+# Classifiers - PEP 301
+
+
+def _download_classifiers() -> str:
+    import ssl
+    from email.message import Message
+    from urllib.request import urlopen
+
+    url = "https://pypi.org/pypi?:action=list_classifiers"
+    context = ssl.create_default_context()
+    with urlopen(url, context=context) as response:
+        headers = Message()
+        headers["content_type"] = response.getheader("content-type", "text/plain")
+        return response.read().decode(headers.get_param("charset", "utf-8"))
+
+
+class _TroveClassifier:
+    """The ``trove_classifiers`` package is the official way of validating classifiers,
+    however this package might not be always available.
+    As a workaround we can still download a list from PyPI.
+    We also don't want to be over strict about it, so simply skipping silently is an
+    option (classifiers will be validated anyway during the upload to PyPI).
+    """
+
+    downloaded: typing.Union[None, "Literal[False]", typing.Set[str]]
+
+    def __init__(self):
+        self.downloaded = None
+        self._skip_download = False
+        # None => not cached yet
+        # False => cache not available
+        self.__name__ = "trove_classifier"  # Emulate a public function
+
+    def _disable_download(self):
+        # This is a private API. Only setuptools has the consent of using it.
+        self._skip_download = True
+
+    def __call__(self, value: str) -> bool:
+        if self.downloaded is False or self._skip_download is True:
+            return True
+
+        if os.getenv("NO_NETWORK") or os.getenv("VALIDATE_PYPROJECT_NO_NETWORK"):
+            self.downloaded = False
+            msg = (
+                "Install ``trove-classifiers`` to ensure proper validation. "
+                "Skipping download of classifiers list from PyPI (NO_NETWORK)."
+            )
+            _logger.debug(msg)
+            return True
+
+        if self.downloaded is None:
+            msg = (
+                "Install ``trove-classifiers`` to ensure proper validation. "
+                "Meanwhile a list of classifiers will be downloaded from PyPI."
+            )
+            _logger.debug(msg)
+            try:
+                self.downloaded = set(_download_classifiers().splitlines())
+            except Exception:
+                self.downloaded = False
+                _logger.debug("Problem with download, skipping validation")
+                return True
+
+        return value in self.downloaded or value.lower().startswith("private ::")
+
+
+try:
+    from trove_classifiers import classifiers as _trove_classifiers
+
+    def trove_classifier(value: str) -> bool:
+        return value in _trove_classifiers or value.lower().startswith("private ::")
+
+except ImportError:  # pragma: no cover
+    trove_classifier = _TroveClassifier()
+
+
+# -------------------------------------------------------------------------------------
+# Stub packages - PEP 561
+
+
+def pep561_stub_name(value: str) -> bool:
+    top, *children = value.split(".")
+    if not top.endswith("-stubs"):
+        return False
+    return python_module_name(".".join([top[: -len("-stubs")], *children]))
+
+
+# -------------------------------------------------------------------------------------
+# Non-PEP related
+
+
+def url(value: str) -> bool:
+    from urllib.parse import urlparse
+
+    try:
+        parts = urlparse(value)
+        if not parts.scheme:
+            _logger.warning(
+                "For maximum compatibility please make sure to include a "
+                "`scheme` prefix in your URL (e.g. 'http://'). "
+                f"Given value: {value}"
+            )
+            if not (value.startswith("/") or value.startswith("\\") or "@" in value):
+                parts = urlparse(f"http://{value}")
+
+        return bool(parts.scheme and parts.netloc)
+    except Exception:
+        return False
+
+
+# https://packaging.python.org/specifications/entry-points/
+ENTRYPOINT_PATTERN = r"[^\[\s=]([^=]*[^\s=])?"
+ENTRYPOINT_REGEX = re.compile(f"^{ENTRYPOINT_PATTERN}$", re.I)
+RECOMMEDED_ENTRYPOINT_PATTERN = r"[\w.-]+"
+RECOMMEDED_ENTRYPOINT_REGEX = re.compile(f"^{RECOMMEDED_ENTRYPOINT_PATTERN}$", re.I)
+ENTRYPOINT_GROUP_PATTERN = r"\w+(\.\w+)*"
+ENTRYPOINT_GROUP_REGEX = re.compile(f"^{ENTRYPOINT_GROUP_PATTERN}$", re.I)
+
+
+def python_identifier(value: str) -> bool:
+    return value.isidentifier()
+
+
+def python_qualified_identifier(value: str) -> bool:
+    if value.startswith(".") or value.endswith("."):
+        return False
+    return all(python_identifier(m) for m in value.split("."))
+
+
+def python_module_name(value: str) -> bool:
+    return python_qualified_identifier(value)
+
+
+def python_entrypoint_group(value: str) -> bool:
+    return ENTRYPOINT_GROUP_REGEX.match(value) is not None
+
+
+def python_entrypoint_name(value: str) -> bool:
+    if not ENTRYPOINT_REGEX.match(value):
+        return False
+    if not RECOMMEDED_ENTRYPOINT_REGEX.match(value):
+        msg = f"Entry point `{value}` does not follow recommended pattern: "
+        msg += RECOMMEDED_ENTRYPOINT_PATTERN
+        _logger.warning(msg)
+    return True
+
+
+def python_entrypoint_reference(value: str) -> bool:
+    module, _, rest = value.partition(":")
+    if "[" in rest:
+        obj, _, extras_ = rest.partition("[")
+        if extras_.strip()[-1] != "]":
+            return False
+        extras = (x.strip() for x in extras_.strip(string.whitespace + "[]").split(","))
+        if not all(pep508_identifier(e) for e in extras):
+            return False
+        _logger.warning(f"`{value}` - using extras for entry points is not recommended")
+    else:
+        obj = rest
+
+    module_parts = module.split(".")
+    identifiers = _chain(module_parts, obj.split(".")) if rest else module_parts
+    return all(python_identifier(i.strip()) for i in identifiers)
diff --git a/venv/Lib/site-packages/setuptools/config/expand.py b/venv/Lib/site-packages/setuptools/config/expand.py
new file mode 100644
index 0000000..0d8d58a
--- /dev/null
+++ b/venv/Lib/site-packages/setuptools/config/expand.py
@@ -0,0 +1,462 @@
+"""Utility functions to expand configuration directives or special values
+(such glob patterns).
+
+We can split the process of interpreting configuration files into 2 steps:
+
+1. The parsing the file contents from strings to value objects
+   that can be understand by Python (for example a string with a comma
+   separated list of keywords into an actual Python list of strings).
+
+2. The expansion (or post-processing) of these values according to the
+   semantics ``setuptools`` assign to them (for example a configuration field
+   with the ``file:`` directive should be expanded from a list of file paths to
+   a single string with the contents of those files concatenated)
+
+This module focus on the second step, and therefore allow sharing the expansion
+functions among several configuration file formats.
+
+**PRIVATE MODULE**: API reserved for setuptools internal usage only.
+"""
+
+import ast
+import importlib
+import os
+import pathlib
+import sys
+from glob import iglob
+from configparser import ConfigParser
+from importlib.machinery import ModuleSpec
+from itertools import chain
+from typing import (
+    TYPE_CHECKING,
+    Callable,
+    Dict,
+    Iterable,
+    Iterator,
+    List,
+    Mapping,
+    Optional,
+    Tuple,
+    TypeVar,
+    Union,
+    cast,
+)
+from pathlib import Path
+from types import ModuleType
+
+from distutils.errors import DistutilsOptionError
+
+from .._path import same_path as _same_path, StrPath
+from ..warnings import SetuptoolsWarning
+
+if TYPE_CHECKING:
+    from setuptools.dist import Distribution  # noqa
+    from setuptools.discovery import ConfigDiscovery  # noqa
+    from distutils.dist import DistributionMetadata  # noqa
+
+chain_iter = chain.from_iterable
+_K = TypeVar("_K")
+_V = TypeVar("_V", covariant=True)
+
+
+class StaticModule:
+    """Proxy to a module object that avoids executing arbitrary code."""
+
+    def __init__(self, name: str, spec: ModuleSpec):
+        module = ast.parse(pathlib.Path(spec.origin).read_bytes())  # type: ignore[arg-type] # Let it raise an error on None
+        vars(self).update(locals())
+        del self.self
+
+    def _find_assignments(self) -> Iterator[Tuple[ast.AST, ast.AST]]:
+        for statement in self.module.body:
+            if isinstance(statement, ast.Assign):
+                yield from ((target, statement.value) for target in statement.targets)
+            elif isinstance(statement, ast.AnnAssign) and statement.value:
+                yield (statement.target, statement.value)
+
+    def __getattr__(self, attr):
+        """Attempt to load an attribute "statically", via :func:`ast.literal_eval`."""
+        try:
+            return next(
+                ast.literal_eval(value)
+                for target, value in self._find_assignments()
+                if isinstance(target, ast.Name) and target.id == attr
+            )
+        except Exception as e:
+            raise AttributeError(f"{self.name} has no attribute {attr}") from e
+
+
+def glob_relative(
+    patterns: Iterable[str], root_dir: Optional[StrPath] = None
+) -> List[str]:
+    """Expand the list of glob patterns, but preserving relative paths.
+
+    :param list[str] patterns: List of glob patterns
+    :param str root_dir: Path to which globs should be relative
+                         (current directory by default)
+    :rtype: list
+    """
+    glob_characters = {'*', '?', '[', ']', '{', '}'}
+    expanded_values = []
+    root_dir = root_dir or os.getcwd()
+    for value in patterns:
+        # Has globby characters?
+        if any(char in value for char in glob_characters):
+            # then expand the glob pattern while keeping paths *relative*:
+            glob_path = os.path.abspath(os.path.join(root_dir, value))
+            expanded_values.extend(
+                sorted(
+                    os.path.relpath(path, root_dir).replace(os.sep, "/")
+                    for path in iglob(glob_path, recursive=True)
+                )
+            )
+
+        else:
+            # take the value as-is
+            path = os.path.relpath(value, root_dir).replace(os.sep, "/")
+            expanded_values.append(path)
+
+    return expanded_values
+
+
+def read_files(filepaths: Union[str, bytes, Iterable[StrPath]], root_dir=None) -> str:
+    """Return the content of the files concatenated using ``\n`` as str
+
+    This function is sandboxed and won't reach anything outside ``root_dir``
+
+    (By default ``root_dir`` is the current directory).
+    """
+    from setuptools.extern.more_itertools import always_iterable
+
+    root_dir = os.path.abspath(root_dir or os.getcwd())
+    _filepaths = (os.path.join(root_dir, path) for path in always_iterable(filepaths))
+    return '\n'.join(
+        _read_file(path)
+        for path in _filter_existing_files(_filepaths)
+        if _assert_local(path, root_dir)
+    )
+
+
+def _filter_existing_files(filepaths: Iterable[StrPath]) -> Iterator[StrPath]:
+    for path in filepaths:
+        if os.path.isfile(path):
+            yield path
+        else:
+            SetuptoolsWarning.emit(f"File {path!r} cannot be found")
+
+
+def _read_file(filepath: Union[bytes, StrPath]) -> str:
+    with open(filepath, encoding='utf-8') as f:
+        return f.read()
+
+
+def _assert_local(filepath: StrPath, root_dir: str):
+    if Path(os.path.abspath(root_dir)) not in Path(os.path.abspath(filepath)).parents:
+        msg = f"Cannot access {filepath!r} (or anything outside {root_dir!r})"
+        raise DistutilsOptionError(msg)
+
+    return True
+
+
+def read_attr(
+    attr_desc: str,
+    package_dir: Optional[Mapping[str, str]] = None,
+    root_dir: Optional[StrPath] = None,
+):
+    """Reads the value of an attribute from a module.
+
+    This function will try to read the attributed statically first
+    (via :func:`ast.literal_eval`), and only evaluate the module if it fails.
+
+    Examples:
+        read_attr("package.attr")
+        read_attr("package.module.attr")
+
+    :param str attr_desc: Dot-separated string describing how to reach the
+        attribute (see examples above)
+    :param dict[str, str] package_dir: Mapping of package names to their
+        location in disk (represented by paths relative to ``root_dir``).
+    :param str root_dir: Path to directory containing all the packages in
+        ``package_dir`` (current directory by default).
+    :rtype: str
+    """
+    root_dir = root_dir or os.getcwd()
+    attrs_path = attr_desc.strip().split('.')
+    attr_name = attrs_path.pop()
+    module_name = '.'.join(attrs_path)
+    module_name = module_name or '__init__'
+    _parent_path, path, module_name = _find_module(module_name, package_dir, root_dir)
+    spec = _find_spec(module_name, path)
+
+    try:
+        return getattr(StaticModule(module_name, spec), attr_name)
+    except Exception:
+        # fallback to evaluate module
+        module = _load_spec(spec, module_name)
+        return getattr(module, attr_name)
+
+
+def _find_spec(module_name: str, module_path: Optional[StrPath]) -> ModuleSpec:
+    spec = importlib.util.spec_from_file_location(module_name, module_path)
+    spec = spec or importlib.util.find_spec(module_name)
+
+    if spec is None:
+        raise ModuleNotFoundError(module_name)
+
+    return spec
+
+
+def _load_spec(spec: ModuleSpec, module_name: str) -> ModuleType:
+    name = getattr(spec, "__name__", module_name)
+    if name in sys.modules:
+        return sys.modules[name]
+    module = importlib.util.module_from_spec(spec)
+    sys.modules[name] = module  # cache (it also ensures `==` works on loaded items)
+    spec.loader.exec_module(module)  # type: ignore
+    return module
+
+
+def _find_module(
+    module_name: str, package_dir: Optional[Mapping[str, str]], root_dir: StrPath
+) -> Tuple[StrPath, Optional[str], str]:
+    """Given a module (that could normally be imported by ``module_name``
+    after the build is complete), find the path to the parent directory where
+    it is contained and the canonical name that could be used to import it
+    considering the ``package_dir`` in the build configuration and ``root_dir``
+    """
+    parent_path = root_dir
+    module_parts = module_name.split('.')
+    if package_dir:
+        if module_parts[0] in package_dir:
+            # A custom path was specified for the module we want to import
+            custom_path = package_dir[module_parts[0]]
+            parts = custom_path.rsplit('/', 1)
+            if len(parts) > 1:
+                parent_path = os.path.join(root_dir, parts[0])
+                parent_module = parts[1]
+            else:
+                parent_module = custom_path
+            module_name = ".".join([parent_module, *module_parts[1:]])
+        elif '' in package_dir:
+            # A custom parent directory was specified for all root modules
+            parent_path = os.path.join(root_dir, package_dir[''])
+
+    path_start = os.path.join(parent_path, *module_name.split("."))
+    candidates = chain(
+        (f"{path_start}.py", os.path.join(path_start, "__init__.py")),
+        iglob(f"{path_start}.*"),
+    )
+    module_path = next((x for x in candidates if os.path.isfile(x)), None)
+    return parent_path, module_path, module_name
+
+
+def resolve_class(
+    qualified_class_name: str,
+    package_dir: Optional[Mapping[str, str]] = None,
+    root_dir: Optional[StrPath] = None,
+) -> Callable:
+    """Given a qualified class name, return the associated class object"""
+    root_dir = root_dir or os.getcwd()
+    idx = qualified_class_name.rfind('.')
+    class_name = qualified_class_name[idx + 1 :]
+    pkg_name = qualified_class_name[:idx]
+
+    _parent_path, path, module_name = _find_module(pkg_name, package_dir, root_dir)
+    module = _load_spec(_find_spec(module_name, path), module_name)
+    return getattr(module, class_name)
+
+
+def cmdclass(
+    values: Dict[str, str],
+    package_dir: Optional[Mapping[str, str]] = None,
+    root_dir: Optional[StrPath] = None,
+) -> Dict[str, Callable]:
+    """Given a dictionary mapping command names to strings for qualified class
+    names, apply :func:`resolve_class` to the dict values.
+    """
+    return {k: resolve_class(v, package_dir, root_dir) for k, v in values.items()}
+
+
+def find_packages(
+    *,
+    namespaces=True,
+    fill_package_dir: Optional[Dict[str, str]] = None,
+    root_dir: Optional[StrPath] = None,
+    **kwargs,
+) -> List[str]:
+    """Works similarly to :func:`setuptools.find_packages`, but with all
+    arguments given as keyword arguments. Moreover, ``where`` can be given
+    as a list (the results will be simply concatenated).
+
+    When the additional keyword argument ``namespaces`` is ``True``, it will
+    behave like :func:`setuptools.find_namespace_packages`` (i.e. include
+    implicit namespaces as per :pep:`420`).
+
+    The ``where`` argument will be considered relative to ``root_dir`` (or the current
+    working directory when ``root_dir`` is not given).
+
+    If the ``fill_package_dir`` argument is passed, this function will consider it as a
+    similar data structure to the ``package_dir`` configuration parameter add fill-in
+    any missing package location.
+
+    :rtype: list
+    """
+    from setuptools.discovery import construct_package_dir
+    from setuptools.extern.more_itertools import unique_everseen, always_iterable
+
+    if namespaces:
+        from setuptools.discovery import PEP420PackageFinder as PackageFinder
+    else:
+        from setuptools.discovery import PackageFinder  # type: ignore
+
+    root_dir = root_dir or os.curdir
+    where = kwargs.pop('where', ['.'])
+    packages: List[str] = []
+    fill_package_dir = {} if fill_package_dir is None else fill_package_dir
+    search = list(unique_everseen(always_iterable(where)))
+
+    if len(search) == 1 and all(not _same_path(search[0], x) for x in (".", root_dir)):
+        fill_package_dir.setdefault("", search[0])
+
+    for path in search:
+        package_path = _nest_path(root_dir, path)
+        pkgs = PackageFinder.find(package_path, **kwargs)
+        packages.extend(pkgs)
+        if pkgs and not (
+            fill_package_dir.get("") == path or os.path.samefile(package_path, root_dir)
+        ):
+            fill_package_dir.update(construct_package_dir(pkgs, path))
+
+    return packages
+
+
+def _nest_path(parent: StrPath, path: StrPath) -> str:
+    path = parent if path in {".", ""} else os.path.join(parent, path)
+    return os.path.normpath(path)
+
+
+def version(value: Union[Callable, Iterable[Union[str, int]], str]) -> str:
+    """When getting the version directly from an attribute,
+    it should be normalised to string.
+    """
+    if callable(value):
+        value = value()
+
+    value = cast(Iterable[Union[str, int]], value)
+
+    if not isinstance(value, str):
+        if hasattr(value, '__iter__'):
+            value = '.'.join(map(str, value))
+        else:
+            value = '%s' % value
+
+    return value
+
+
+def canonic_package_data(package_data: dict) -> dict:
+    if "*" in package_data:
+        package_data[""] = package_data.pop("*")
+    return package_data
+
+
+def canonic_data_files(
+    data_files: Union[list, dict], root_dir: Optional[StrPath] = None
+) -> List[Tuple[str, List[str]]]:
+    """For compatibility with ``setup.py``, ``data_files`` should be a list
+    of pairs instead of a dict.
+
+    This function also expands glob patterns.
+    """
+    if isinstance(data_files, list):
+        return data_files
+
+    return [
+        (dest, glob_relative(patterns, root_dir))
+        for dest, patterns in data_files.items()
+    ]
+
+
+def entry_points(text: str, text_source="entry-points") -> Dict[str, dict]:
+    """Given the contents of entry-points file,
+    process it into a 2-level dictionary (``dict[str, dict[str, str]]``).
+    The first level keys are entry-point groups, the second level keys are
+    entry-point names, and the second level values are references to objects
+    (that correspond to the entry-point value).
+    """
+    parser = ConfigParser(default_section=None, delimiters=("=",))  # type: ignore
+    parser.optionxform = str  # case sensitive
+    parser.read_string(text, text_source)
+    groups = {k: dict(v.items()) for k, v in parser.items()}
+    groups.pop(parser.default_section, None)
+    return groups
+
+
+class EnsurePackagesDiscovered:
+    """Some expand functions require all the packages to already be discovered before
+    they run, e.g. :func:`read_attr`, :func:`resolve_class`, :func:`cmdclass`.
+
+    Therefore in some cases we will need to run autodiscovery during the evaluation of
+    the configuration. However, it is better to postpone calling package discovery as
+    much as possible, because some parameters can influence it (e.g. ``package_dir``),
+    and those might not have been processed yet.
+    """
+
+    def __init__(self, distribution: "Distribution"):
+        self._dist = distribution
+        self._called = False
+
+    def __call__(self):
+        """Trigger the automatic package discovery, if it is still necessary."""
+        if not self._called:
+            self._called = True
+            self._dist.set_defaults(name=False)  # Skip name, we can still be parsing
+
+    def __enter__(self):
+        return self
+
+    def __exit__(self, _exc_type, _exc_value, _traceback):
+        if self._called:
+            self._dist.set_defaults.analyse_name()  # Now we can set a default name
+
+    def _get_package_dir(self) -> Mapping[str, str]:
+        self()
+        pkg_dir = self._dist.package_dir
+        return {} if pkg_dir is None else pkg_dir
+
+    @property
+    def package_dir(self) -> Mapping[str, str]:
+        """Proxy to ``package_dir`` that may trigger auto-discovery when used."""
+        return LazyMappingProxy(self._get_package_dir)
+
+
+class LazyMappingProxy(Mapping[_K, _V]):
+    """Mapping proxy that delays resolving the target object, until really needed.
+
+    >>> def obtain_mapping():
+    ...     print("Running expensive function!")
+    ...     return {"key": "value", "other key": "other value"}
+    >>> mapping = LazyMappingProxy(obtain_mapping)
+    >>> mapping["key"]
+    Running expensive function!
+    'value'
+    >>> mapping["other key"]
+    'other value'
+    """
+
+    def __init__(self, obtain_mapping_value: Callable[[], Mapping[_K, _V]]):
+        self._obtain = obtain_mapping_value
+        self._value: Optional[Mapping[_K, _V]] = None
+
+    def _target(self) -> Mapping[_K, _V]:
+        if self._value is None:
+            self._value = self._obtain()
+        return self._value
+
+    def __getitem__(self, key: _K) -> _V:
+        return self._target()[key]
+
+    def __len__(self) -> int:
+        return len(self._target())
+
+    def __iter__(self) -> Iterator[_K]:
+        return iter(self._target())
diff --git a/venv/Lib/site-packages/setuptools/config/pyprojecttoml.py b/venv/Lib/site-packages/setuptools/config/pyprojecttoml.py
new file mode 100644
index 0000000..ff97679
--- /dev/null
+++ b/venv/Lib/site-packages/setuptools/config/pyprojecttoml.py
@@ -0,0 +1,441 @@
+"""
+Load setuptools configuration from ``pyproject.toml`` files.
+
+**PRIVATE MODULE**: API reserved for setuptools internal usage only.
+
+To read project metadata, consider using
+``build.util.project_wheel_metadata`` (https://pypi.org/project/build/).
+For simple scenarios, you can also try parsing the file directly
+with the help of ``tomllib`` or ``tomli``.
+"""
+
+import logging
+import os
+from contextlib import contextmanager
+from functools import partial
+from typing import TYPE_CHECKING, Callable, Dict, Mapping, Optional, Set
+
+from .._path import StrPath
+from ..errors import FileError, InvalidConfigError
+from ..warnings import SetuptoolsWarning
+from . import expand as _expand
+from ._apply_pyprojecttoml import _PREVIOUSLY_DEFINED, _MissingDynamic
+from ._apply_pyprojecttoml import apply as _apply
+
+if TYPE_CHECKING:
+    from setuptools.dist import Distribution  # noqa
+    from typing_extensions import Self
+
+_logger = logging.getLogger(__name__)
+
+
+def load_file(filepath: StrPath) -> dict:
+    from ..compat.py310 import tomllib
+
+    with open(filepath, "rb") as file:
+        return tomllib.load(file)
+
+
+def validate(config: dict, filepath: StrPath) -> bool:
+    from . import _validate_pyproject as validator
+
+    trove_classifier = validator.FORMAT_FUNCTIONS.get("trove-classifier")
+    if hasattr(trove_classifier, "_disable_download"):
+        # Improve reproducibility by default. See issue 31 for validate-pyproject.
+        trove_classifier._disable_download()  # type: ignore
+
+    try:
+        return validator.validate(config)
+    except validator.ValidationError as ex:
+        summary = f"configuration error: {ex.summary}"
+        if ex.name.strip("`") != "project":
+            # Probably it is just a field missing/misnamed, not worthy the verbosity...
+            _logger.debug(summary)
+            _logger.debug(ex.details)
+
+        error = f"invalid pyproject.toml config: {ex.name}."
+        raise ValueError(f"{error}\n{summary}") from None
+
+
+def apply_configuration(
+    dist: "Distribution",
+    filepath: StrPath,
+    ignore_option_errors=False,
+) -> "Distribution":
+    """Apply the configuration from a ``pyproject.toml`` file into an existing
+    distribution object.
+    """
+    config = read_configuration(filepath, True, ignore_option_errors, dist)
+    return _apply(dist, config, filepath)
+
+
+def read_configuration(
+    filepath: StrPath,
+    expand=True,
+    ignore_option_errors=False,
+    dist: Optional["Distribution"] = None,
+):
+    """Read given configuration file and returns options from it as a dict.
+
+    :param str|unicode filepath: Path to configuration file in the ``pyproject.toml``
+        format.
+
+    :param bool expand: Whether to expand directives and other computed values
+        (i.e. post-process the given configuration)
+
+    :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.
+
+    :param Distribution|None: Distribution object to which the configuration refers.
+        If not given a dummy object will be created and discarded after the
+        configuration is read. This is used for auto-discovery of packages and in the
+        case a dynamic configuration (e.g. ``attr`` or ``cmdclass``) is expanded.
+        When ``expand=False`` this object is simply ignored.
+
+    :rtype: dict
+    """
+    filepath = os.path.abspath(filepath)
+
+    if not os.path.isfile(filepath):
+        raise FileError(f"Configuration file {filepath!r} does not exist.")
+
+    asdict = load_file(filepath) or {}
+    project_table = asdict.get("project", {})
+    tool_table = asdict.get("tool", {})
+    setuptools_table = tool_table.get("setuptools", {})
+    if not asdict or not (project_table or setuptools_table):
+        return {}  # User is not using pyproject to configure setuptools
+
+    if "distutils" in tool_table:
+        _ExperimentalConfiguration.emit(subject="[tool.distutils]")
+
+    # There is an overall sense in the community that making include_package_data=True
+    # the default would be an improvement.
+    # `ini2toml` backfills include_package_data=False when nothing is explicitly given,
+    # therefore setting a default here is backwards compatible.
+    if dist and getattr(dist, "include_package_data", None) is not None:
+        setuptools_table.setdefault("include-package-data", dist.include_package_data)
+    else:
+        setuptools_table.setdefault("include-package-data", True)
+    # Persist changes:
+    asdict["tool"] = tool_table
+    tool_table["setuptools"] = setuptools_table
+
+    with _ignore_errors(ignore_option_errors):
+        # Don't complain about unrelated errors (e.g. tools not using the "tool" table)
+        subset = {"project": project_table, "tool": {"setuptools": setuptools_table}}
+        validate(subset, filepath)
+
+    if expand:
+        root_dir = os.path.dirname(filepath)
+        return expand_configuration(asdict, root_dir, ignore_option_errors, dist)
+
+    return asdict
+
+
+def expand_configuration(
+    config: dict,
+    root_dir: Optional[StrPath] = None,
+    ignore_option_errors: bool = False,
+    dist: Optional["Distribution"] = None,
+) -> dict:
+    """Given a configuration with unresolved fields (e.g. dynamic, cmdclass, ...)
+    find their final values.
+
+    :param dict config: Dict containing the configuration for the distribution
+    :param str root_dir: Top-level directory for the distribution/project
+        (the same directory where ``pyproject.toml`` is place)
+    :param bool ignore_option_errors: see :func:`read_configuration`
+    :param Distribution|None: Distribution object to which the configuration refers.
+        If not given a dummy object will be created and discarded after the
+        configuration is read. Used in the case a dynamic configuration
+        (e.g. ``attr`` or ``cmdclass``).
+
+    :rtype: dict
+    """
+    return _ConfigExpander(config, root_dir, ignore_option_errors, dist).expand()
+
+
+class _ConfigExpander:
+    def __init__(
+        self,
+        config: dict,
+        root_dir: Optional[StrPath] = None,
+        ignore_option_errors: bool = False,
+        dist: Optional["Distribution"] = None,
+    ):
+        self.config = config
+        self.root_dir = root_dir or os.getcwd()
+        self.project_cfg = config.get("project", {})
+        self.dynamic = self.project_cfg.get("dynamic", [])
+        self.setuptools_cfg = config.get("tool", {}).get("setuptools", {})
+        self.dynamic_cfg = self.setuptools_cfg.get("dynamic", {})
+        self.ignore_option_errors = ignore_option_errors
+        self._dist = dist
+        self._referenced_files: Set[str] = set()
+
+    def _ensure_dist(self) -> "Distribution":
+        from setuptools.dist import Distribution
+
+        attrs = {"src_root": self.root_dir, "name": self.project_cfg.get("name", None)}
+        return self._dist or Distribution(attrs)
+
+    def _process_field(self, container: dict, field: str, fn: Callable):
+        if field in container:
+            with _ignore_errors(self.ignore_option_errors):
+                container[field] = fn(container[field])
+
+    def _canonic_package_data(self, field="package-data"):
+        package_data = self.setuptools_cfg.get(field, {})
+        return _expand.canonic_package_data(package_data)
+
+    def expand(self):
+        self._expand_packages()
+        self._canonic_package_data()
+        self._canonic_package_data("exclude-package-data")
+
+        # A distribution object is required for discovering the correct package_dir
+        dist = self._ensure_dist()
+        ctx = _EnsurePackagesDiscovered(dist, self.project_cfg, self.setuptools_cfg)
+        with ctx as ensure_discovered:
+            package_dir = ensure_discovered.package_dir
+            self._expand_data_files()
+            self._expand_cmdclass(package_dir)
+            self._expand_all_dynamic(dist, package_dir)
+
+        dist._referenced_files.update(self._referenced_files)
+        return self.config
+
+    def _expand_packages(self):
+        packages = self.setuptools_cfg.get("packages")
+        if packages is None or isinstance(packages, (list, tuple)):
+            return
+
+        find = packages.get("find")
+        if isinstance(find, dict):
+            find["root_dir"] = self.root_dir
+            find["fill_package_dir"] = self.setuptools_cfg.setdefault("package-dir", {})
+            with _ignore_errors(self.ignore_option_errors):
+                self.setuptools_cfg["packages"] = _expand.find_packages(**find)
+
+    def _expand_data_files(self):
+        data_files = partial(_expand.canonic_data_files, root_dir=self.root_dir)
+        self._process_field(self.setuptools_cfg, "data-files", data_files)
+
+    def _expand_cmdclass(self, package_dir: Mapping[str, str]):
+        root_dir = self.root_dir
+        cmdclass = partial(_expand.cmdclass, package_dir=package_dir, root_dir=root_dir)
+        self._process_field(self.setuptools_cfg, "cmdclass", cmdclass)
+
+    def _expand_all_dynamic(self, dist: "Distribution", package_dir: Mapping[str, str]):
+        special = (  # need special handling
+            "version",
+            "readme",
+            "entry-points",
+            "scripts",
+            "gui-scripts",
+            "classifiers",
+            "dependencies",
+            "optional-dependencies",
+        )
+        # `_obtain` functions are assumed to raise appropriate exceptions/warnings.
+        obtained_dynamic = {
+            field: self._obtain(dist, field, package_dir)
+            for field in self.dynamic
+            if field not in special
+        }
+        obtained_dynamic.update(
+            self._obtain_entry_points(dist, package_dir) or {},
+            version=self._obtain_version(dist, package_dir),
+            readme=self._obtain_readme(dist),
+            classifiers=self._obtain_classifiers(dist),
+            dependencies=self._obtain_dependencies(dist),
+            optional_dependencies=self._obtain_optional_dependencies(dist),
+        )
+        # `None` indicates there is nothing in `tool.setuptools.dynamic` but the value
+        # might have already been set by setup.py/extensions, so avoid overwriting.
+        updates = {k: v for k, v in obtained_dynamic.items() if v is not None}
+        self.project_cfg.update(updates)
+
+    def _ensure_previously_set(self, dist: "Distribution", field: str):
+        previous = _PREVIOUSLY_DEFINED[field](dist)
+        if previous is None and not self.ignore_option_errors:
+            msg = (
+                f"No configuration found for dynamic {field!r}.\n"
+                "Some dynamic fields need to be specified via `tool.setuptools.dynamic`"
+                "\nothers must be specified via the equivalent attribute in `setup.py`."
+            )
+            raise InvalidConfigError(msg)
+
+    def _expand_directive(
+        self, specifier: str, directive, package_dir: Mapping[str, str]
+    ):
+        from setuptools.extern.more_itertools import always_iterable
+
+        with _ignore_errors(self.ignore_option_errors):
+            root_dir = self.root_dir
+            if "file" in directive:
+                self._referenced_files.update(always_iterable(directive["file"]))
+                return _expand.read_files(directive["file"], root_dir)
+            if "attr" in directive:
+                return _expand.read_attr(directive["attr"], package_dir, root_dir)
+            raise ValueError(f"invalid `{specifier}`: {directive!r}")
+        return None
+
+    def _obtain(self, dist: "Distribution", field: str, package_dir: Mapping[str, str]):
+        if field in self.dynamic_cfg:
+            return self._expand_directive(
+                f"tool.setuptools.dynamic.{field}",
+                self.dynamic_cfg[field],
+                package_dir,
+            )
+        self._ensure_previously_set(dist, field)
+        return None
+
+    def _obtain_version(self, dist: "Distribution", package_dir: Mapping[str, str]):
+        # Since plugins can set version, let's silently skip if it cannot be obtained
+        if "version" in self.dynamic and "version" in self.dynamic_cfg:
+            return _expand.version(self._obtain(dist, "version", package_dir))
+        return None
+
+    def _obtain_readme(self, dist: "Distribution") -> Optional[Dict[str, str]]:
+        if "readme" not in self.dynamic:
+            return None
+
+        dynamic_cfg = self.dynamic_cfg
+        if "readme" in dynamic_cfg:
+            return {
+                "text": self._obtain(dist, "readme", {}),
+                "content-type": dynamic_cfg["readme"].get("content-type", "text/x-rst"),
+            }
+
+        self._ensure_previously_set(dist, "readme")
+        return None
+
+    def _obtain_entry_points(
+        self, dist: "Distribution", package_dir: Mapping[str, str]
+    ) -> Optional[Dict[str, dict]]:
+        fields = ("entry-points", "scripts", "gui-scripts")
+        if not any(field in self.dynamic for field in fields):
+            return None
+
+        text = self._obtain(dist, "entry-points", package_dir)
+        if text is None:
+            return None
+
+        groups = _expand.entry_points(text)
+        expanded = {"entry-points": groups}
+
+        def _set_scripts(field: str, group: str):
+            if group in groups:
+                value = groups.pop(group)
+                if field not in self.dynamic:
+                    raise InvalidConfigError(_MissingDynamic.details(field, value))
+                expanded[field] = value
+
+        _set_scripts("scripts", "console_scripts")
+        _set_scripts("gui-scripts", "gui_scripts")
+
+        return expanded
+
+    def _obtain_classifiers(self, dist: "Distribution"):
+        if "classifiers" in self.dynamic:
+            value = self._obtain(dist, "classifiers", {})
+            if value:
+                return value.splitlines()
+        return None
+
+    def _obtain_dependencies(self, dist: "Distribution"):
+        if "dependencies" in self.dynamic:
+            value = self._obtain(dist, "dependencies", {})
+            if value:
+                return _parse_requirements_list(value)
+        return None
+
+    def _obtain_optional_dependencies(self, dist: "Distribution"):
+        if "optional-dependencies" not in self.dynamic:
+            return None
+        if "optional-dependencies" in self.dynamic_cfg:
+            optional_dependencies_map = self.dynamic_cfg["optional-dependencies"]
+            assert isinstance(optional_dependencies_map, dict)
+            return {
+                group: _parse_requirements_list(
+                    self._expand_directive(
+                        f"tool.setuptools.dynamic.optional-dependencies.{group}",
+                        directive,
+                        {},
+                    )
+                )
+                for group, directive in optional_dependencies_map.items()
+            }
+        self._ensure_previously_set(dist, "optional-dependencies")
+        return None
+
+
+def _parse_requirements_list(value):
+    return [
+        line
+        for line in value.splitlines()
+        if line.strip() and not line.strip().startswith("#")
+    ]
+
+
+@contextmanager
+def _ignore_errors(ignore_option_errors: bool):
+    if not ignore_option_errors:
+        yield
+        return
+
+    try:
+        yield
+    except Exception as ex:
+        _logger.debug(f"ignored error: {ex.__class__.__name__} - {ex}")
+
+
+class _EnsurePackagesDiscovered(_expand.EnsurePackagesDiscovered):
+    def __init__(
+        self, distribution: "Distribution", project_cfg: dict, setuptools_cfg: dict
+    ):
+        super().__init__(distribution)
+        self._project_cfg = project_cfg
+        self._setuptools_cfg = setuptools_cfg
+
+    def __enter__(self) -> "Self":
+        """When entering the context, the values of ``packages``, ``py_modules`` and
+        ``package_dir`` that are missing in ``dist`` are copied from ``setuptools_cfg``.
+        """
+        dist, cfg = self._dist, self._setuptools_cfg
+        package_dir: Dict[str, str] = cfg.setdefault("package-dir", {})
+        package_dir.update(dist.package_dir or {})
+        dist.package_dir = package_dir  # needs to be the same object
+
+        dist.set_defaults._ignore_ext_modules()  # pyproject.toml-specific behaviour
+
+        # Set `name`, `py_modules` and `packages` in dist to short-circuit
+        # auto-discovery, but avoid overwriting empty lists purposefully set by users.
+        if dist.metadata.name is None:
+            dist.metadata.name = self._project_cfg.get("name")
+        if dist.py_modules is None:
+            dist.py_modules = cfg.get("py-modules")
+        if dist.packages is None:
+            dist.packages = cfg.get("packages")
+
+        return super().__enter__()
+
+    def __exit__(self, exc_type, exc_value, traceback):
+        """When exiting the context, if values of ``packages``, ``py_modules`` and
+        ``package_dir`` are missing in ``setuptools_cfg``, copy from ``dist``.
+        """
+        # If anything was discovered set them back, so they count in the final config.
+        self._setuptools_cfg.setdefault("packages", self._dist.packages)
+        self._setuptools_cfg.setdefault("py-modules", self._dist.py_modules)
+        return super().__exit__(exc_type, exc_value, traceback)
+
+
+class _ExperimentalConfiguration(SetuptoolsWarning):
+    _SUMMARY = (
+        "`{subject}` in `pyproject.toml` is still *experimental* "
+        "and likely to change in future releases."
+    )
diff --git a/venv/Lib/site-packages/setuptools/config/setupcfg.py b/venv/Lib/site-packages/setuptools/config/setupcfg.py
new file mode 100644
index 0000000..2912d3e
--- /dev/null
+++ b/venv/Lib/site-packages/setuptools/config/setupcfg.py
@@ -0,0 +1,776 @@
+"""
+Load setuptools configuration from ``setup.cfg`` files.
+
+**API will be made private in the future**
+
+To read project metadata, consider using
+``build.util.project_wheel_metadata`` (https://pypi.org/project/build/).
+For simple scenarios, you can also try parsing the file directly
+with the help of ``configparser``.
+"""
+
+import contextlib
+import functools
+import os
+from collections import defaultdict
+from functools import partial
+from functools import wraps
+from typing import (
+    TYPE_CHECKING,
+    Callable,
+    Any,
+    Dict,
+    Generic,
+    Iterable,
+    List,
+    Optional,
+    Set,
+    Tuple,
+    TypeVar,
+    Union,
+)
+
+from .._path import StrPath
+from ..errors import FileError, OptionError
+from ..extern.packaging.markers import default_environment as marker_env
+from ..extern.packaging.requirements import InvalidRequirement, Requirement
+from ..extern.packaging.specifiers import SpecifierSet
+from ..extern.packaging.version import InvalidVersion, Version
+from ..warnings import SetuptoolsDeprecationWarning
+from . import expand
+
+if TYPE_CHECKING:
+    from distutils.dist import DistributionMetadata  # noqa
+
+    from setuptools.dist import Distribution  # noqa
+
+SingleCommandOptions = Dict["str", Tuple["str", Any]]
+"""Dict that associate the name of the options of a particular command to a
+tuple. The first element of the tuple indicates the origin of the option value
+(e.g. the name of the configuration file where it was read from),
+while the second element of the tuple is the option value itself
+"""
+AllCommandOptions = Dict["str", SingleCommandOptions]  # cmd name => its options
+Target = TypeVar("Target", bound=Union["Distribution", "DistributionMetadata"])
+
+
+def read_configuration(
+    filepath: StrPath, find_others=False, ignore_option_errors=False
+) -> dict:
+    """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
+
+    dist = Distribution()
+    filenames = dist.find_config_files() if find_others else []
+    handlers = _apply(dist, filepath, filenames, ignore_option_errors)
+    return configuration_to_dict(handlers)
+
+
+def apply_configuration(dist: "Distribution", filepath: StrPath) -> "Distribution":
+    """Apply the configuration from a ``setup.cfg`` file into an existing
+    distribution object.
+    """
+    _apply(dist, filepath)
+    dist._finalize_requires()
+    return dist
+
+
+def _apply(
+    dist: "Distribution",
+    filepath: StrPath,
+    other_files: Iterable[StrPath] = (),
+    ignore_option_errors: bool = False,
+) -> Tuple["ConfigHandler", ...]:
+    """Read configuration from ``filepath`` and applies to the ``dist`` object."""
+    from setuptools.dist import _Distribution
+
+    filepath = os.path.abspath(filepath)
+
+    if not os.path.isfile(filepath):
+        raise FileError(f'Configuration file {filepath} does not exist.')
+
+    current_directory = os.getcwd()
+    os.chdir(os.path.dirname(filepath))
+    filenames = [*other_files, filepath]
+
+    try:
+        _Distribution.parse_config_files(dist, filenames=filenames)  # type: ignore[arg-type] # TODO: fix in disutils stubs
+        handlers = parse_configuration(
+            dist, dist.command_options, ignore_option_errors=ignore_option_errors
+        )
+        dist._finalize_license_files()
+    finally:
+        os.chdir(current_directory)
+
+    return handlers
+
+
+def _get_option(target_obj: Target, key: str):
+    """
+    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 = f'get_{key}'
+    by_attribute = functools.partial(getattr, target_obj, key)
+    getter = getattr(target_obj, getter_name, by_attribute)
+    return getter()
+
+
+def configuration_to_dict(handlers: Tuple["ConfigHandler", ...]) -> dict:
+    """Returns configuration data gathered by given handlers as a dict.
+
+    :param list[ConfigHandler] handlers: Handlers list,
+        usually from parse_configuration()
+
+    :rtype: dict
+    """
+    config_dict: 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: "Distribution",
+    command_options: AllCommandOptions,
+    ignore_option_errors=False,
+) -> Tuple["ConfigMetadataHandler", "ConfigOptionsHandler"]:
+    """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
+    """
+    with expand.EnsurePackagesDiscovered(distribution) as ensure_discovered:
+        options = ConfigOptionsHandler(
+            distribution,
+            command_options,
+            ignore_option_errors,
+            ensure_discovered,
+        )
+
+        options.parse()
+        if not distribution.package_dir:
+            distribution.package_dir = options.package_dir  # Filled by `find_packages`
+
+        meta = ConfigMetadataHandler(
+            distribution.metadata,
+            command_options,
+            ignore_option_errors,
+            ensure_discovered,
+            distribution.package_dir,
+            distribution.src_root,
+        )
+        meta.parse()
+        distribution._referenced_files.update(
+            options._referenced_files, meta._referenced_files
+        )
+
+    return meta, options
+
+
+def _warn_accidental_env_marker_misconfig(label: str, orig_value: str, parsed: list):
+    """Because users sometimes misinterpret this configuration:
+
+    [options.extras_require]
+    foo = bar;python_version<"4"
+
+    It looks like one requirement with an environment marker
+    but because there is no newline, it's parsed as two requirements
+    with a semicolon as separator.
+
+    Therefore, if:
+        * input string does not contain a newline AND
+        * parsed result contains two requirements AND
+        * parsing of the two parts from the result (";")
+        leads in a valid Requirement with a valid marker
+    a UserWarning is shown to inform the user about the possible problem.
+    """
+    if "\n" in orig_value or len(parsed) != 2:
+        return
+
+    markers = marker_env().keys()
+
+    try:
+        req = Requirement(parsed[1])
+        if req.name in markers:
+            _AmbiguousMarker.emit(field=label, req=parsed[1])
+    except InvalidRequirement as ex:
+        if any(parsed[1].startswith(marker) for marker in markers):
+            msg = _AmbiguousMarker.message(field=label, req=parsed[1])
+            raise InvalidRequirement(msg) from ex
+
+
+class ConfigHandler(Generic[Target]):
+    """Handles metadata supplied in configuration files."""
+
+    section_prefix: str
+    """Prefix for config sections handled by this handler.
+    Must be provided by class heirs.
+
+    """
+
+    aliases: Dict[str, str] = {}
+    """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: Target,
+        options: AllCommandOptions,
+        ignore_option_errors,
+        ensure_discovered: expand.EnsurePackagesDiscovered,
+    ):
+        self.ignore_option_errors = ignore_option_errors
+        self.target_obj = target_obj
+        self.sections = dict(self._section_options(options))
+        self.set_options: List[str] = []
+        self.ensure_discovered = ensure_discovered
+        self._referenced_files: Set[str] = set()
+        """After parsing configurations, this property will enumerate
+        all files referenced by the "file:" directive. Private API for setuptools only.
+        """
+
+    @classmethod
+    def _section_options(cls, options: AllCommandOptions):
+        for full_name, value in options.items():
+            pre, sep, name = full_name.partition(cls.section_prefix)
+            if pre:
+                continue
+            yield name.lstrip('.'), value
+
+    @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):
+        target_obj = self.target_obj
+
+        # Translate alias into real name.
+        option_name = self.aliases.get(option_name, option_name)
+
+        try:
+            current_value = getattr(target_obj, option_name)
+        except AttributeError as e:
+            raise KeyError(option_name) from e
+
+        if current_value:
+            # Already inhabited. Skipping.
+            return
+
+        try:
+            parsed = self.parsers.get(option_name, lambda x: x)(value)
+        except (Exception,) * self.ignore_option_errors:
+            return
+
+        simple_setter = functools.partial(target_obj.__setattr__, option_name)
+        setter = getattr(target_obj, 'set_%s' % option_name, simple_setter)
+        setter(parsed)
+
+        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 OptionError(f"Unable to parse option value to dict: {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
+
+    def _parse_file(self, value, root_dir: StrPath):
+        """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, str):
+            return value
+
+        if not value.startswith(include_directive):
+            return value
+
+        spec = value[len(include_directive) :]
+        filepaths = [path.strip() for path in spec.split(',')]
+        self._referenced_files.update(filepaths)
+        return expand.read_files(filepaths, root_dir)
+
+    def _parse_attr(self, value, package_dir, root_dir: StrPath):
+        """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
+
+        attr_desc = value.replace(attr_directive, '')
+
+        # Make sure package_dir is populated correctly, so `attr:` directives can work
+        package_dir.update(self.ensure_discovered.package_dir)
+        return expand.read_attr(attr_desc, package_dir, root_dir)
+
+    @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_with_key(cls, section_options, values_parser):
+        """Parses section options into a dictionary.
+
+        Applies a given parser to each option in a section.
+
+        :param dict section_options:
+        :param callable values_parser: function with 2 args corresponding to key, value
+        :rtype: dict
+        """
+        value = {}
+        for key, (_, val) in section_options.items():
+            value[key] = values_parser(key, val)
+        return value
+
+    @classmethod
+    def _parse_section_to_dict(cls, section_options, values_parser=None):
+        """Parses section options into a dictionary.
+
+        Optionally applies a given parser to each value.
+
+        :param dict section_options:
+        :param callable values_parser: function with 1 arg corresponding to option value
+        :rtype: dict
+        """
+        parser = (lambda _, v: values_parser(v)) if values_parser else (lambda _, v: v)
+        return cls._parse_section_to_dict_with_key(section_options, parser)
+
+    def parse_section(self, section_options):
+        """Parses configuration file section.
+
+        :param dict section_options:
+        """
+        for name, (_, value) in section_options.items():
+            with contextlib.suppress(KeyError):
+                # Keep silent for a new option may appear anytime.
+                self[name] = value
+
+    def parse(self) -> None:
+        """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: Optional[Callable] = getattr(
+                self,
+                # Dots in section names are translated into dunderscores.
+                ('parse_section%s' % method_postfix).replace('.', '__'),
+                None,
+            )
+
+            if section_parser_method is None:
+                raise OptionError(
+                    "Unsupported distribution option section: "
+                    f"[{self.section_prefix}.{section_name}]"
+                )
+
+            section_parser_method(section_options)
+
+    def _deprecated_config_handler(self, func, msg, **kw):
+        """this function will wrap around parameters that are deprecated
+
+        :param msg: deprecation message
+        :param func: function to be wrapped around
+        """
+
+        @wraps(func)
+        def config_handler(*args, **kwargs):
+            kw.setdefault("stacklevel", 2)
+            _DeprecatedConfig.emit("Deprecated config in `setup.cfg`", msg, **kw)
+            return func(*args, **kwargs)
+
+        return config_handler
+
+
+class ConfigMetadataHandler(ConfigHandler["DistributionMetadata"]):
+    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: "DistributionMetadata",
+        options: AllCommandOptions,
+        ignore_option_errors: bool,
+        ensure_discovered: expand.EnsurePackagesDiscovered,
+        package_dir: Optional[dict] = None,
+        root_dir: StrPath = os.curdir,
+    ):
+        super().__init__(target_obj, options, ignore_option_errors, ensure_discovered)
+        self.package_dir = package_dir
+        self.root_dir = root_dir
+
+    @property
+    def parsers(self):
+        """Metadata item name to parser function mapping."""
+        parse_list = self._parse_list
+        parse_file = partial(self._parse_file, root_dir=self.root_dir)
+        parse_dict = self._parse_dict
+        exclude_files_parser = self._exclude_files_parser
+
+        return {
+            'platforms': parse_list,
+            'keywords': parse_list,
+            'provides': parse_list,
+            '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, self.root_dir)
+
+        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
+            try:
+                Version(version)
+            except InvalidVersion as e:
+                raise OptionError(
+                    f'Version loaded from {value} does not '
+                    f'comply with PEP 440: {version}'
+                ) from e
+
+            return version
+
+        return expand.version(self._parse_attr(value, self.package_dir, self.root_dir))
+
+
+class ConfigOptionsHandler(ConfigHandler["Distribution"]):
+    section_prefix = 'options'
+
+    def __init__(
+        self,
+        target_obj: "Distribution",
+        options: AllCommandOptions,
+        ignore_option_errors: bool,
+        ensure_discovered: expand.EnsurePackagesDiscovered,
+    ):
+        super().__init__(target_obj, options, ignore_option_errors, ensure_discovered)
+        self.root_dir = target_obj.src_root
+        self.package_dir: Dict[str, str] = {}  # To be filled by `find_packages`
+
+    @classmethod
+    def _parse_list_semicolon(cls, value):
+        return cls._parse_list(value, separator=';')
+
+    def _parse_file_in_root(self, value):
+        return self._parse_file(value, root_dir=self.root_dir)
+
+    def _parse_requirements_list(self, label: str, value: str):
+        # Parse a requirements list, either by reading in a `file:`, or a list.
+        parsed = self._parse_list_semicolon(self._parse_file_in_root(value))
+        _warn_accidental_env_marker_misconfig(label, value, parsed)
+        # Filter it to only include lines that are not comments. `parse_list`
+        # will have stripped each line and filtered out empties.
+        return [line for line in parsed if not line.startswith("#")]
+
+    @property
+    def parsers(self):
+        """Metadata item name to parser function mapping."""
+        parse_list = self._parse_list
+        parse_bool = self._parse_bool
+        parse_dict = self._parse_dict
+        parse_cmdclass = self._parse_cmdclass
+
+        return {
+            'zip_safe': parse_bool,
+            'include_package_data': parse_bool,
+            'package_dir': parse_dict,
+            'scripts': parse_list,
+            'eager_resources': parse_list,
+            'dependency_links': parse_list,
+            'namespace_packages': self._deprecated_config_handler(
+                parse_list,
+                "The namespace_packages parameter is deprecated, "
+                "consider using implicit namespaces instead (PEP 420).",
+                # TODO: define due date, see setuptools.dist:check_nsp.
+            ),
+            'install_requires': partial(
+                self._parse_requirements_list, "install_requires"
+            ),
+            'setup_requires': self._parse_list_semicolon,
+            'tests_require': self._parse_list_semicolon,
+            'packages': self._parse_packages,
+            'entry_points': self._parse_file_in_root,
+            'py_modules': parse_list,
+            'python_requires': SpecifierSet,
+            'cmdclass': parse_cmdclass,
+        }
+
+    def _parse_cmdclass(self, value):
+        package_dir = self.ensure_discovered.package_dir
+        return expand.cmdclass(self._parse_dict(value), package_dir, self.root_dir)
+
+    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)
+
+        # Read function arguments from a dedicated section.
+        find_kwargs = self.parse_section_packages__find(
+            self.sections.get('packages.find', {})
+        )
+
+        find_kwargs.update(
+            namespaces=(trimmed_value == find_directives[1]),
+            root_dir=self.root_dir,
+            fill_package_dir=self.package_dir,
+        )
+
+        return expand.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):
+        package_data = self._parse_section_to_dict(section_options, self._parse_list)
+        return expand.canonic_package_data(package_data)
+
+    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:
+        """
+        parsed = self._parse_section_to_dict_with_key(
+            section_options,
+            lambda k, v: self._parse_requirements_list(f"extras_require[{k}]", v),
+        )
+
+        self['extras_require'] = parsed
+
+    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'] = expand.canonic_data_files(parsed, self.root_dir)
+
+
+class _AmbiguousMarker(SetuptoolsDeprecationWarning):
+    _SUMMARY = "Ambiguous requirement marker."
+    _DETAILS = """
+    One of the parsed requirements in `{field}` looks like a valid environment marker:
+
+        {req!r}
+
+    Please make sure that the configuration file is correct.
+    You can use dangling lines to avoid this problem.
+    """
+    _SEE_DOCS = "userguide/declarative_config.html#opt-2"
+    # TODO: should we include due_date here? Initially introduced in 6 Aug 2022.
+    # Does this make sense with latest version of packaging?
+
+    @classmethod
+    def message(cls, **kw):
+        docs = f"https://setuptools.pypa.io/en/latest/{cls._SEE_DOCS}"
+        return cls._format(cls._SUMMARY, cls._DETAILS, see_url=docs, format_args=kw)
+
+
+class _DeprecatedConfig(SetuptoolsDeprecationWarning):
+    _SEE_DOCS = "userguide/declarative_config.html"
diff --git a/venv/Lib/site-packages/setuptools/dep_util.py b/venv/Lib/site-packages/setuptools/dep_util.py
new file mode 100644
index 0000000..998ffa2
--- /dev/null
+++ b/venv/Lib/site-packages/setuptools/dep_util.py
@@ -0,0 +1,16 @@
+from ._distutils import _modified
+from .warnings import SetuptoolsDeprecationWarning
+
+
+def __getattr__(name):
+    if name not in ['newer_group', 'newer_pairwise_group']:
+        raise AttributeError(name)
+    SetuptoolsDeprecationWarning.emit(
+        "dep_util is Deprecated. Use functions from setuptools.modified instead.",
+        "Please use `setuptools.modified` instead of `setuptools.dep_util`.",
+        see_url="https://github.com/pypa/setuptools/pull/4069",
+        due_date=(2024, 5, 21),
+        # Warning added in v69.0.0 on 2023/11/20,
+        # See https://github.com/pypa/setuptools/discussions/4128
+    )
+    return getattr(_modified, name)
diff --git a/venv/Lib/site-packages/setuptools/depends.py b/venv/Lib/site-packages/setuptools/depends.py
new file mode 100644
index 0000000..c0ca84d
--- /dev/null
+++ b/venv/Lib/site-packages/setuptools/depends.py
@@ -0,0 +1,180 @@
+import sys
+import marshal
+import contextlib
+import dis
+
+
+from . import _imp
+from ._imp import find_module, PY_COMPILED, PY_FROZEN, PY_SOURCE
+from .extern.packaging.version import Version
+
+
+__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 = Version
+
+        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 self.format(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(str(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 = _imp.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 = _imp.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 = dis.opmap['STORE_NAME']
+    STORE_GLOBAL = dis.opmap['STORE_GLOBAL']
+    LOAD_CONST = dis.opmap['LOAD_CONST']
+
+    const = default
+
+    for byte_code in dis.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
+
+    return None
+
+
+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/site-packages/setuptools/discovery.py b/venv/Lib/site-packages/setuptools/discovery.py
new file mode 100644
index 0000000..571be12
--- /dev/null
+++ b/venv/Lib/site-packages/setuptools/discovery.py
@@ -0,0 +1,613 @@
+"""Automatic discovery of Python modules and packages (for inclusion in the
+distribution) and other config values.
+
+For the purposes of this module, the following nomenclature is used:
+
+- "src-layout": a directory representing a Python project that contains a "src"
+  folder. Everything under the "src" folder is meant to be included in the
+  distribution when packaging the project. Example::
+
+    .
+    ├── tox.ini
+    ├── pyproject.toml
+    └── src/
+        └── mypkg/
+            ├── __init__.py
+            ├── mymodule.py
+            └── my_data_file.txt
+
+- "flat-layout": a Python project that does not use "src-layout" but instead
+  have a directory under the project root for each package::
+
+    .
+    ├── tox.ini
+    ├── pyproject.toml
+    └── mypkg/
+        ├── __init__.py
+        ├── mymodule.py
+        └── my_data_file.txt
+
+- "single-module": a project that contains a single Python script direct under
+  the project root (no directory used)::
+
+    .
+    ├── tox.ini
+    ├── pyproject.toml
+    └── mymodule.py
+
+"""
+
+import itertools
+import os
+from fnmatch import fnmatchcase
+from glob import glob
+from pathlib import Path
+from typing import (
+    TYPE_CHECKING,
+    Dict,
+    Iterable,
+    Iterator,
+    List,
+    Mapping,
+    Optional,
+    Tuple,
+)
+
+import _distutils_hack.override  # noqa: F401
+
+from ._path import StrPath
+from distutils import log
+from distutils.util import convert_path
+
+StrIter = Iterator[str]
+
+chain_iter = itertools.chain.from_iterable
+
+if TYPE_CHECKING:
+    from setuptools import Distribution  # noqa
+
+
+def _valid_name(path: StrPath) -> bool:
+    # Ignore invalid names that cannot be imported directly
+    return os.path.basename(path).isidentifier()
+
+
+class _Filter:
+    """
+    Given a list of patterns, create a callable that will be true only if
+    the input matches at least one of the patterns.
+    """
+
+    def __init__(self, *patterns: str):
+        self._patterns = dict.fromkeys(patterns)
+
+    def __call__(self, item: str) -> bool:
+        return any(fnmatchcase(item, pat) for pat in self._patterns)
+
+    def __contains__(self, item: str) -> bool:
+        return item in self._patterns
+
+
+class _Finder:
+    """Base class that exposes functionality for module/package finders"""
+
+    ALWAYS_EXCLUDE: Tuple[str, ...] = ()
+    DEFAULT_EXCLUDE: Tuple[str, ...] = ()
+
+    @classmethod
+    def find(
+        cls,
+        where: StrPath = '.',
+        exclude: Iterable[str] = (),
+        include: Iterable[str] = ('*',),
+    ) -> List[str]:
+        """Return a list of all Python items (packages or modules, depending on
+        the finder implementation) found within directory 'where'.
+
+        'where' is the root directory which will be searched.
+        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 names to exclude; '*' can be used
+        as a wildcard in the names.
+        When finding packages, 'foo.*' will exclude all subpackages of 'foo'
+        (but not 'foo' itself).
+
+        'include' is a sequence of names to include.
+        If it's specified, only the named items will be included.
+        If it's not specified, all found items will be included.
+        'include' can contain shell style wildcard patterns just like
+        'exclude'.
+        """
+
+        exclude = exclude or cls.DEFAULT_EXCLUDE
+        return list(
+            cls._find_iter(
+                convert_path(str(where)),
+                _Filter(*cls.ALWAYS_EXCLUDE, *exclude),
+                _Filter(*include),
+            )
+        )
+
+    @classmethod
+    def _find_iter(cls, where: StrPath, exclude: _Filter, include: _Filter) -> StrIter:
+        raise NotImplementedError
+
+
+class PackageFinder(_Finder):
+    """
+    Generate a list of all Python packages found within a directory
+    """
+
+    ALWAYS_EXCLUDE = ("ez_setup", "*__pycache__")
+
+    @classmethod
+    def _find_iter(cls, where: StrPath, exclude: _Filter, include: _Filter) -> StrIter:
+        """
+        All the packages found in 'where' that pass the 'include' filter, but
+        not the 'exclude' filter.
+        """
+        for root, dirs, files in os.walk(str(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, package):
+                    continue
+
+                # Should this package be included?
+                if include(package) and not exclude(package):
+                    yield package
+
+                # Early pruning if there is nothing else to be scanned
+                if f"{package}*" in exclude or f"{package}.*" in exclude:
+                    continue
+
+                # 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: StrPath, _package_name: str) -> bool:
+        """Does a directory look like a package?"""
+        return os.path.isfile(os.path.join(path, '__init__.py'))
+
+
+class PEP420PackageFinder(PackageFinder):
+    @staticmethod
+    def _looks_like_package(_path: StrPath, _package_name: str) -> bool:
+        return True
+
+
+class ModuleFinder(_Finder):
+    """Find isolated Python modules.
+    This function will **not** recurse subdirectories.
+    """
+
+    @classmethod
+    def _find_iter(cls, where: StrPath, exclude: _Filter, include: _Filter) -> StrIter:
+        for file in glob(os.path.join(where, "*.py")):
+            module, _ext = os.path.splitext(os.path.basename(file))
+
+            if not cls._looks_like_module(module):
+                continue
+
+            if include(module) and not exclude(module):
+                yield module
+
+    _looks_like_module = staticmethod(_valid_name)
+
+
+# We have to be extra careful in the case of flat layout to not include files
+# and directories not meant for distribution (e.g. tool-related)
+
+
+class FlatLayoutPackageFinder(PEP420PackageFinder):
+    _EXCLUDE = (
+        "ci",
+        "bin",
+        "debian",
+        "doc",
+        "docs",
+        "documentation",
+        "manpages",
+        "news",
+        "newsfragments",
+        "changelog",
+        "test",
+        "tests",
+        "unit_test",
+        "unit_tests",
+        "example",
+        "examples",
+        "scripts",
+        "tools",
+        "util",
+        "utils",
+        "python",
+        "build",
+        "dist",
+        "venv",
+        "env",
+        "requirements",
+        # ---- Task runners / Build tools ----
+        "tasks",  # invoke
+        "fabfile",  # fabric
+        "site_scons",  # SCons
+        # ---- Other tools ----
+        "benchmark",
+        "benchmarks",
+        "exercise",
+        "exercises",
+        "htmlcov",  # Coverage.py
+        # ---- Hidden directories/Private packages ----
+        "[._]*",
+    )
+
+    DEFAULT_EXCLUDE = tuple(chain_iter((p, f"{p}.*") for p in _EXCLUDE))
+    """Reserved package names"""
+
+    @staticmethod
+    def _looks_like_package(_path: StrPath, package_name: str) -> bool:
+        names = package_name.split('.')
+        # Consider PEP 561
+        root_pkg_is_valid = names[0].isidentifier() or names[0].endswith("-stubs")
+        return root_pkg_is_valid and all(name.isidentifier() for name in names[1:])
+
+
+class FlatLayoutModuleFinder(ModuleFinder):
+    DEFAULT_EXCLUDE = (
+        "setup",
+        "conftest",
+        "test",
+        "tests",
+        "example",
+        "examples",
+        "build",
+        # ---- Task runners ----
+        "toxfile",
+        "noxfile",
+        "pavement",
+        "dodo",
+        "tasks",
+        "fabfile",
+        # ---- Other tools ----
+        "[Ss][Cc]onstruct",  # SCons
+        "conanfile",  # Connan: C/C++ build tool
+        "manage",  # Django
+        "benchmark",
+        "benchmarks",
+        "exercise",
+        "exercises",
+        # ---- Hidden files/Private modules ----
+        "[._]*",
+    )
+    """Reserved top-level module names"""
+
+
+def _find_packages_within(root_pkg: str, pkg_dir: StrPath) -> List[str]:
+    nested = PEP420PackageFinder.find(pkg_dir)
+    return [root_pkg] + [".".join((root_pkg, n)) for n in nested]
+
+
+class ConfigDiscovery:
+    """Fill-in metadata and options that can be automatically derived
+    (from other metadata/options, the file system or conventions)
+    """
+
+    def __init__(self, distribution: "Distribution"):
+        self.dist = distribution
+        self._called = False
+        self._disabled = False
+        self._skip_ext_modules = False
+
+    def _disable(self):
+        """Internal API to disable automatic discovery"""
+        self._disabled = True
+
+    def _ignore_ext_modules(self):
+        """Internal API to disregard ext_modules.
+
+        Normally auto-discovery would not be triggered if ``ext_modules`` are set
+        (this is done for backward compatibility with existing packages relying on
+        ``setup.py`` or ``setup.cfg``). However, ``setuptools`` can call this function
+        to ignore given ``ext_modules`` and proceed with the auto-discovery if
+        ``packages`` and ``py_modules`` are not given (e.g. when using pyproject.toml
+        metadata).
+        """
+        self._skip_ext_modules = True
+
+    @property
+    def _root_dir(self) -> StrPath:
+        # The best is to wait until `src_root` is set in dist, before using _root_dir.
+        return self.dist.src_root or os.curdir
+
+    @property
+    def _package_dir(self) -> Dict[str, str]:
+        if self.dist.package_dir is None:
+            return {}
+        return self.dist.package_dir
+
+    def __call__(self, force=False, name=True, ignore_ext_modules=False):
+        """Automatically discover missing configuration fields
+        and modifies the given ``distribution`` object in-place.
+
+        Note that by default this will only have an effect the first time the
+        ``ConfigDiscovery`` object is called.
+
+        To repeatedly invoke automatic discovery (e.g. when the project
+        directory changes), please use ``force=True`` (or create a new
+        ``ConfigDiscovery`` instance).
+        """
+        if force is False and (self._called or self._disabled):
+            # Avoid overhead of multiple calls
+            return
+
+        self._analyse_package_layout(ignore_ext_modules)
+        if name:
+            self.analyse_name()  # depends on ``packages`` and ``py_modules``
+
+        self._called = True
+
+    def _explicitly_specified(self, ignore_ext_modules: bool) -> bool:
+        """``True`` if the user has specified some form of package/module listing"""
+        ignore_ext_modules = ignore_ext_modules or self._skip_ext_modules
+        ext_modules = not (self.dist.ext_modules is None or ignore_ext_modules)
+        return (
+            self.dist.packages is not None
+            or self.dist.py_modules is not None
+            or ext_modules
+            or hasattr(self.dist, "configuration")
+            and self.dist.configuration
+            # ^ Some projects use numpy.distutils.misc_util.Configuration
+        )
+
+    def _analyse_package_layout(self, ignore_ext_modules: bool) -> bool:
+        if self._explicitly_specified(ignore_ext_modules):
+            # For backward compatibility, just try to find modules/packages
+            # when nothing is given
+            return True
+
+        log.debug(
+            "No `packages` or `py_modules` configuration, performing "
+            "automatic discovery."
+        )
+
+        return (
+            self._analyse_explicit_layout()
+            or self._analyse_src_layout()
+            # flat-layout is the trickiest for discovery so it should be last
+            or self._analyse_flat_layout()
+        )
+
+    def _analyse_explicit_layout(self) -> bool:
+        """The user can explicitly give a package layout via ``package_dir``"""
+        package_dir = self._package_dir.copy()  # don't modify directly
+        package_dir.pop("", None)  # This falls under the "src-layout" umbrella
+        root_dir = self._root_dir
+
+        if not package_dir:
+            return False
+
+        log.debug(f"`explicit-layout` detected -- analysing {package_dir}")
+        pkgs = chain_iter(
+            _find_packages_within(pkg, os.path.join(root_dir, parent_dir))
+            for pkg, parent_dir in package_dir.items()
+        )
+        self.dist.packages = list(pkgs)
+        log.debug(f"discovered packages -- {self.dist.packages}")
+        return True
+
+    def _analyse_src_layout(self) -> bool:
+        """Try to find all packages or modules under the ``src`` directory
+        (or anything pointed by ``package_dir[""]``).
+
+        The "src-layout" is relatively safe for automatic discovery.
+        We assume that everything within is meant to be included in the
+        distribution.
+
+        If ``package_dir[""]`` is not given, but the ``src`` directory exists,
+        this function will set ``package_dir[""] = "src"``.
+        """
+        package_dir = self._package_dir
+        src_dir = os.path.join(self._root_dir, package_dir.get("", "src"))
+        if not os.path.isdir(src_dir):
+            return False
+
+        log.debug(f"`src-layout` detected -- analysing {src_dir}")
+        package_dir.setdefault("", os.path.basename(src_dir))
+        self.dist.package_dir = package_dir  # persist eventual modifications
+        self.dist.packages = PEP420PackageFinder.find(src_dir)
+        self.dist.py_modules = ModuleFinder.find(src_dir)
+        log.debug(f"discovered packages -- {self.dist.packages}")
+        log.debug(f"discovered py_modules -- {self.dist.py_modules}")
+        return True
+
+    def _analyse_flat_layout(self) -> bool:
+        """Try to find all packages and modules under the project root.
+
+        Since the ``flat-layout`` is more dangerous in terms of accidentally including
+        extra files/directories, this function is more conservative and will raise an
+        error if multiple packages or modules are found.
+
+        This assumes that multi-package dists are uncommon and refuse to support that
+        use case in order to be able to prevent unintended errors.
+        """
+        log.debug(f"`flat-layout` detected -- analysing {self._root_dir}")
+        return self._analyse_flat_packages() or self._analyse_flat_modules()
+
+    def _analyse_flat_packages(self) -> bool:
+        self.dist.packages = FlatLayoutPackageFinder.find(self._root_dir)
+        top_level = remove_nested_packages(remove_stubs(self.dist.packages))
+        log.debug(f"discovered packages -- {self.dist.packages}")
+        self._ensure_no_accidental_inclusion(top_level, "packages")
+        return bool(top_level)
+
+    def _analyse_flat_modules(self) -> bool:
+        self.dist.py_modules = FlatLayoutModuleFinder.find(self._root_dir)
+        log.debug(f"discovered py_modules -- {self.dist.py_modules}")
+        self._ensure_no_accidental_inclusion(self.dist.py_modules, "modules")
+        return bool(self.dist.py_modules)
+
+    def _ensure_no_accidental_inclusion(self, detected: List[str], kind: str):
+        if len(detected) > 1:
+            from inspect import cleandoc
+
+            from setuptools.errors import PackageDiscoveryError
+
+            msg = f"""Multiple top-level {kind} discovered in a flat-layout: {detected}.
+
+            To avoid accidental inclusion of unwanted files or directories,
+            setuptools will not proceed with this build.
+
+            If you are trying to create a single distribution with multiple {kind}
+            on purpose, you should not rely on automatic discovery.
+            Instead, consider the following options:
+
+            1. set up custom discovery (`find` directive with `include` or `exclude`)
+            2. use a `src-layout`
+            3. explicitly set `py_modules` or `packages` with a list of names
+
+            To find more information, look for "package discovery" on setuptools docs.
+            """
+            raise PackageDiscoveryError(cleandoc(msg))
+
+    def analyse_name(self):
+        """The packages/modules are the essential contribution of the author.
+        Therefore the name of the distribution can be derived from them.
+        """
+        if self.dist.metadata.name or self.dist.name:
+            # get_name() is not reliable (can return "UNKNOWN")
+            return
+
+        log.debug("No `name` configuration, performing automatic discovery")
+
+        name = (
+            self._find_name_single_package_or_module()
+            or self._find_name_from_packages()
+        )
+        if name:
+            self.dist.metadata.name = name
+
+    def _find_name_single_package_or_module(self) -> Optional[str]:
+        """Exactly one module or package"""
+        for field in ('packages', 'py_modules'):
+            items = getattr(self.dist, field, None) or []
+            if items and len(items) == 1:
+                log.debug(f"Single module/package detected, name: {items[0]}")
+                return items[0]
+
+        return None
+
+    def _find_name_from_packages(self) -> Optional[str]:
+        """Try to find the root package that is not a PEP 420 namespace"""
+        if not self.dist.packages:
+            return None
+
+        packages = remove_stubs(sorted(self.dist.packages, key=len))
+        package_dir = self.dist.package_dir or {}
+
+        parent_pkg = find_parent_package(packages, package_dir, self._root_dir)
+        if parent_pkg:
+            log.debug(f"Common parent package detected, name: {parent_pkg}")
+            return parent_pkg
+
+        log.warn("No parent package detected, impossible to derive `name`")
+        return None
+
+
+def remove_nested_packages(packages: List[str]) -> List[str]:
+    """Remove nested packages from a list of packages.
+
+    >>> remove_nested_packages(["a", "a.b1", "a.b2", "a.b1.c1"])
+    ['a']
+    >>> remove_nested_packages(["a", "b", "c.d", "c.d.e.f", "g.h", "a.a1"])
+    ['a', 'b', 'c.d', 'g.h']
+    """
+    pkgs = sorted(packages, key=len)
+    top_level = pkgs[:]
+    size = len(pkgs)
+    for i, name in enumerate(reversed(pkgs)):
+        if any(name.startswith(f"{other}.") for other in top_level):
+            top_level.pop(size - i - 1)
+
+    return top_level
+
+
+def remove_stubs(packages: List[str]) -> List[str]:
+    """Remove type stubs (:pep:`561`) from a list of packages.
+
+    >>> remove_stubs(["a", "a.b", "a-stubs", "a-stubs.b.c", "b", "c-stubs"])
+    ['a', 'a.b', 'b']
+    """
+    return [pkg for pkg in packages if not pkg.split(".")[0].endswith("-stubs")]
+
+
+def find_parent_package(
+    packages: List[str], package_dir: Mapping[str, str], root_dir: StrPath
+) -> Optional[str]:
+    """Find the parent package that is not a namespace."""
+    packages = sorted(packages, key=len)
+    common_ancestors = []
+    for i, name in enumerate(packages):
+        if not all(n.startswith(f"{name}.") for n in packages[i + 1 :]):
+            # Since packages are sorted by length, this condition is able
+            # to find a list of all common ancestors.
+            # When there is divergence (e.g. multiple root packages)
+            # the list will be empty
+            break
+        common_ancestors.append(name)
+
+    for name in common_ancestors:
+        pkg_path = find_package_path(name, package_dir, root_dir)
+        init = os.path.join(pkg_path, "__init__.py")
+        if os.path.isfile(init):
+            return name
+
+    return None
+
+
+def find_package_path(
+    name: str, package_dir: Mapping[str, str], root_dir: StrPath
+) -> str:
+    """Given a package name, return the path where it should be found on
+    disk, considering the ``package_dir`` option.
+
+    >>> path = find_package_path("my.pkg", {"": "root/is/nested"}, ".")
+    >>> path.replace(os.sep, "/")
+    './root/is/nested/my/pkg'
+
+    >>> path = find_package_path("my.pkg", {"my": "root/is/nested"}, ".")
+    >>> path.replace(os.sep, "/")
+    './root/is/nested/pkg'
+
+    >>> path = find_package_path("my.pkg", {"my.pkg": "root/is/nested"}, ".")
+    >>> path.replace(os.sep, "/")
+    './root/is/nested'
+
+    >>> path = find_package_path("other.pkg", {"my.pkg": "root/is/nested"}, ".")
+    >>> path.replace(os.sep, "/")
+    './other/pkg'
+    """
+    parts = name.split(".")
+    for i in range(len(parts), 0, -1):
+        # Look backwards, the most specific package_dir first
+        partial_name = ".".join(parts[:i])
+        if partial_name in package_dir:
+            parent = package_dir[partial_name]
+            return os.path.join(root_dir, parent, *parts[i:])
+
+    parent = package_dir.get("") or ""
+    return os.path.join(root_dir, *parent.split("/"), *parts)
+
+
+def construct_package_dir(packages: List[str], package_path: StrPath) -> Dict[str, str]:
+    parent_pkgs = remove_nested_packages(packages)
+    prefix = Path(package_path).parts
+    return {pkg: "/".join([*prefix, *pkg.split(".")]) for pkg in parent_pkgs}
diff --git a/venv/Lib/site-packages/setuptools/dist.py b/venv/Lib/site-packages/setuptools/dist.py
new file mode 100644
index 0000000..6350e38
--- /dev/null
+++ b/venv/Lib/site-packages/setuptools/dist.py
@@ -0,0 +1,972 @@
+__all__ = ['Distribution']
+
+
+import io
+import itertools
+import numbers
+import os
+import re
+import sys
+from contextlib import suppress
+from glob import iglob
+from pathlib import Path
+from typing import TYPE_CHECKING, Dict, List, MutableMapping, Optional, Set, Tuple
+
+import distutils.cmd
+import distutils.command
+import distutils.core
+import distutils.dist
+import distutils.log
+from distutils.debug import DEBUG
+from distutils.errors import DistutilsOptionError, DistutilsSetupError
+from distutils.fancy_getopt import translate_longopt
+from distutils.util import strtobool
+
+from .extern.more_itertools import partition, unique_everseen
+from .extern.ordered_set import OrderedSet
+from .extern.packaging.markers import InvalidMarker, Marker
+from .extern.packaging.specifiers import InvalidSpecifier, SpecifierSet
+from .extern.packaging.version import Version
+
+from . import _entry_points
+from . import _normalization
+from . import _reqs
+from . import command as _  # noqa  -- imported for side-effects
+from ._importlib import metadata
+from .config import setupcfg, pyprojecttoml
+from .discovery import ConfigDiscovery
+from .monkey import get_unpatched
+from .warnings import InformationOnly, SetuptoolsDeprecationWarning
+
+
+sequence = tuple, list
+
+
+def check_importable(dist, attr, value):
+    try:
+        ep = metadata.EntryPoint(value=value, name=None, group=None)
+        assert not ep.extras
+    except (TypeError, ValueError, AttributeError, AssertionError) as e:
+        raise DistutilsSetupError(
+            "%r must be importable 'module:attrs' string (got %r)" % (attr, value)
+        ) from e
+
+
+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) as e:
+        raise DistutilsSetupError(
+            "%r must be a list of strings (got %r)" % (attr, value)
+        ) from e
+
+
+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,
+            )
+        SetuptoolsDeprecationWarning.emit(
+            "The namespace_packages parameter is deprecated.",
+            "Please replace its usage with implicit namespaces (PEP 420).",
+            see_docs="references/keywords.html#keyword-namespace-packages",
+            # TODO: define due_date, it may break old packages that are no longer
+            # maintained (e.g. sphinxcontrib extensions) when installed from source.
+            # Warning officially introduced in May 2022, however the deprecation
+            # was mentioned much earlier in the docs (May 2020, see #2149).
+        )
+
+
+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) as e:
+        raise DistutilsSetupError(
+            "'extras_require' must be a dictionary whose values are "
+            "strings or lists of strings containing valid project/version "
+            "requirement specifiers."
+        ) from e
+
+
+def _check_extra(extra, reqs):
+    name, sep, marker = extra.partition(':')
+    try:
+        _check_marker(marker)
+    except InvalidMarker:
+        msg = f"Invalid environment marker: {marker} ({extra!r})"
+        raise DistutilsSetupError(msg) from None
+    list(_reqs.parse(reqs))
+
+
+def _check_marker(marker):
+    if not marker:
+        return
+    m = Marker(marker)
+    m.evaluate()
+
+
+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 invalid_unless_false(dist, attr, value):
+    if not value:
+        DistDeprecationWarning.emit(f"{attr} is ignored.")
+        # TODO: should there be a `due_date` here?
+        return
+    raise DistutilsSetupError(f"{attr} is invalid.")
+
+
+def check_requirements(dist, attr, value):
+    """Verify that install_requires is a valid requirements list"""
+    try:
+        list(_reqs.parse(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)) from error
+
+
+def check_specifier(dist, attr, value):
+    """Verify that value is a valid version specifier"""
+    try:
+        SpecifierSet(value)
+    except (InvalidSpecifier, AttributeError) as error:
+        tmpl = (
+            "{attr!r} must be a string " "containing valid version specifiers; {error}"
+        )
+        raise DistutilsSetupError(tmpl.format(attr=attr, error=error)) from error
+
+
+def check_entry_points(dist, attr, value):
+    """Verify that entry_points map is parseable"""
+    try:
+        _entry_points.load(value)
+    except Exception as e:
+        raise DistutilsSetupError(e) from e
+
+
+def check_test_suite(dist, attr, value):
+    if not isinstance(value, str):
+        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, str):
+            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,
+            )
+
+
+if TYPE_CHECKING:
+    # Work around a mypy issue where type[T] can't be used as a base: https://github.com/python/mypy/issues/10962
+    _Distribution = distutils.core.Distribution
+else:
+    _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': lambda: None,
+        'project_urls': dict,
+        'provides_extras': OrderedSet,
+        'license_file': lambda: None,
+        'license_files': lambda: None,
+        'install_requires': list,
+        'extras_require': dict,
+    }
+
+    _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
+        name = _normalization.safe_name(str(attrs['name'])).lower()
+        with suppress(metadata.PackageNotFoundError):
+            dist = metadata.distribution(name)
+            if dist is not None and not dist.read_text('PKG-INFO'):
+                dist._version = _normalization.safe_version(str(attrs['version']))
+                self._patched_dist = dist
+
+    def __init__(self, attrs: Optional[MutableMapping] = None) -> None:
+        have_package_data = hasattr(self, "package_data")
+        if not have_package_data:
+            self.package_data: Dict[str, List[str]] = {}
+        attrs = attrs or {}
+        self.dist_files: List[Tuple[str, str, str]] = []
+        # 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 metadata.entry_points(group='distutils.setup_keywords'):
+            vars(self).setdefault(ep.name, None)
+
+        metadata_only = set(self._DISTUTILS_UNSUPPORTED_METADATA)
+        metadata_only -= {"install_requires", "extras_require"}
+        dist_attrs = {k: v for k, v in attrs.items() if k not in metadata_only}
+        _Distribution.__init__(self, dist_attrs)
+
+        # Private API (setuptools-use only, not restricted to Distribution)
+        # Stores files that are referenced by the configuration and need to be in the
+        # sdist (e.g. `version = file: VERSION.txt`)
+        self._referenced_files: Set[str] = set()
+
+        self.set_defaults = ConfigDiscovery(self)
+
+        self._set_metadata_defaults(attrs)
+
+        self.metadata.version = self._normalize_version(self.metadata.version)
+        self._finalize_requires()
+
+    def _validate_metadata(self):
+        required = {"name"}
+        provided = {
+            key
+            for key in vars(self.metadata)
+            if getattr(self.metadata, key, None) is not None
+        }
+        missing = required - provided
+
+        if missing:
+            msg = f"Required package metadata is missing: {missing}"
+            raise DistutilsSetupError(msg)
+
+    def _set_metadata_defaults(self, attrs):
+        """
+        Fill-in missing metadata fields not supported by distutils.
+        Some fields may have been set by other tools (e.g. pbr).
+        Those fields (vars(self.metadata)) take precedence to
+        supplied attrs.
+        """
+        for option, default in self._DISTUTILS_UNSUPPORTED_METADATA.items():
+            vars(self.metadata).setdefault(option, attrs.get(option, default()))
+
+    @staticmethod
+    def _normalize_version(version):
+        from . import sic
+
+        if isinstance(version, numbers.Number):
+            # Some people apparently take "version number" too literally :)
+            version = str(version)
+        elif isinstance(version, sic) or version is None:
+            return version
+
+        normalized = str(Version(version))
+        if version != normalized:
+            InformationOnly.emit(f"Normalizing '{version}' to '{normalized}'")
+            return normalized
+        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
+
+        self._normalize_requires()
+        self.metadata.install_requires = self.install_requires
+        self.metadata.extras_require = self.extras_require
+
+        if self.extras_require:
+            for extra in self.extras_require.keys():
+                # Setuptools allows a weird ": syntax for extras
+                extra = extra.split(':')[0]
+                if extra:
+                    self.metadata.provides_extras.add(extra)
+
+    def _normalize_requires(self):
+        """Make sure requirement-related attributes exist and are normalized"""
+        install_requires = getattr(self, "install_requires", None) or []
+        extras_require = getattr(self, "extras_require", None) or {}
+        self.install_requires = list(map(str, _reqs.parse(install_requires)))
+        self.extras_require = {
+            k: list(map(str, _reqs.parse(v or []))) for k, v in extras_require.items()
+        }
+
+    def _finalize_license_files(self) -> None:
+        """Compute names of all license files which should be included."""
+        license_files: Optional[List[str]] = self.metadata.license_files
+        patterns: List[str] = license_files if license_files else []
+
+        license_file: Optional[str] = self.metadata.license_file
+        if license_file and license_file not in patterns:
+            patterns.append(license_file)
+
+        if license_files is None and license_file is None:
+            # Default patterns match the ones wheel uses
+            # See https://wheel.readthedocs.io/en/stable/user_guide.html
+            # -> 'Including license files in the generated wheel file'
+            patterns = ['LICEN[CS]E*', 'COPYING*', 'NOTICE*', 'AUTHORS*']
+
+        self.metadata.license_files = list(
+            unique_everseen(self._expand_patterns(patterns))
+        )
+
+    @staticmethod
+    def _expand_patterns(patterns):
+        """
+        >>> list(Distribution._expand_patterns(['LICENSE']))
+        ['LICENSE']
+        >>> list(Distribution._expand_patterns(['setup.cfg', 'LIC*']))
+        ['setup.cfg', 'LICENSE']
+        """
+        return (
+            path
+            for pattern in patterns
+            for path in sorted(iglob(pattern))
+            if not path.endswith('~') and os.path.isfile(path)
+        )
+
+    # FIXME: 'Distribution._parse_config_files' is too complex (14)
+    def _parse_config_files(self, filenames=None):  # noqa: C901
+        """
+        Adapted from distutils.dist.Distribution.parse_config_files,
+        this method provides the same functionality in subtly-improved
+        ways.
+        """
+        from configparser import ConfigParser
+
+        # Ignore install directory options if we have a venv
+        ignore_options = (
+            []
+            if sys.prefix == sys.base_prefix
+            else [
+                'install-base',
+                'install-platbase',
+                'install-lib',
+                'install-platlib',
+                'install-purelib',
+                'install-headers',
+                'install-scripts',
+                'install-data',
+                'prefix',
+                'exec-prefix',
+                'home',
+                'user',
+                'root',
+            ]
+        )
+
+        ignore_options = frozenset(ignore_options)
+
+        if filenames is None:
+            filenames = self.find_config_files()
+
+        if DEBUG:
+            self.announce("Distribution.parse_config_files():")
+
+        parser = ConfigParser()
+        parser.optionxform = str
+        for filename in filenames:
+            with open(filename, encoding='utf-8') as reader:
+                if DEBUG:
+                    self.announce("  reading {filename}".format(**locals()))
+                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__' or opt in ignore_options:
+                        continue
+
+                    val = parser.get(section, opt)
+                    opt = self.warn_dash_deprecation(opt, section)
+                    opt = self.make_option_lowercase(opt, section)
+                    opt_dict[opt] = (filename, val)
+
+            # Make the ConfigParser forget everything (so we retain
+            # the original filenames that options come from)
+            parser.__init__()
+
+        if 'global' not in self.command_options:
+            return
+
+        # If there was a "global" section in the config file, use it
+        # to set Distribution options.
+
+        for opt, (src, val) in self.command_options['global'].items():
+            alias = self.negative_opt.get(opt)
+            if alias:
+                val = not strtobool(val)
+            elif opt in ('verbose', 'dry_run'):  # ugh!
+                val = strtobool(val)
+
+            try:
+                setattr(self, alias or opt, val)
+            except ValueError as e:
+                raise DistutilsOptionError(e) from e
+
+    def warn_dash_deprecation(self, opt, section):
+        if section in (
+            'options.extras_require',
+            'options.data_files',
+        ):
+            return opt
+
+        underscore_opt = opt.replace('-', '_')
+        commands = list(
+            itertools.chain(
+                distutils.command.__all__,
+                self._setuptools_commands(),
+            )
+        )
+        if (
+            not section.startswith('options')
+            and section != 'metadata'
+            and section not in commands
+        ):
+            return underscore_opt
+
+        if '-' in opt:
+            SetuptoolsDeprecationWarning.emit(
+                "Invalid dash-separated options",
+                f"""
+                Usage of dash-separated {opt!r} will not be supported in future
+                versions. Please use the underscore name {underscore_opt!r} instead.
+                """,
+                see_docs="userguide/declarative_config.html",
+                due_date=(2024, 9, 26),
+                # Warning initially introduced in 3 Mar 2021
+            )
+        return underscore_opt
+
+    def _setuptools_commands(self):
+        try:
+            return metadata.distribution('setuptools').entry_points.names
+        except metadata.PackageNotFoundError:
+            # during bootstrapping, distribution doesn't exist
+            return []
+
+    def make_option_lowercase(self, opt, section):
+        if section != 'metadata' or opt.islower():
+            return opt
+
+        lowercase_opt = opt.lower()
+        SetuptoolsDeprecationWarning.emit(
+            "Invalid uppercase configuration",
+            f"""
+            Usage of uppercase key {opt!r} in {section!r} will not be supported in
+            future versions. Please use lowercase {lowercase_opt!r} instead.
+            """,
+            see_docs="userguide/declarative_config.html",
+            due_date=(2024, 9, 26),
+            # Warning initially introduced in 6 Mar 2021
+        )
+        return lowercase_opt
+
+    # FIXME: 'Distribution._set_command_options' is too complex (14)
+    def _set_command_options(self, command_obj, option_dict=None):  # noqa: C901
+        """
+        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, str)
+                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 e:
+                raise DistutilsOptionError(e) from e
+
+    def _get_project_config_files(self, filenames):
+        """Add default file and split between INI and TOML"""
+        tomlfiles = []
+        standard_project_metadata = Path(self.src_root or os.curdir, "pyproject.toml")
+        if filenames is not None:
+            parts = partition(lambda f: Path(f).suffix == ".toml", filenames)
+            filenames = list(parts[0])  # 1st element => predicate is False
+            tomlfiles = list(parts[1])  # 2nd element => predicate is True
+        elif standard_project_metadata.exists():
+            tomlfiles = [standard_project_metadata]
+        return filenames, tomlfiles
+
+    def parse_config_files(self, filenames=None, ignore_option_errors=False):
+        """Parses configuration files from various levels
+        and loads configuration.
+        """
+        inifiles, tomlfiles = self._get_project_config_files(filenames)
+
+        self._parse_config_files(filenames=inifiles)
+
+        setupcfg.parse_configuration(
+            self, self.command_options, ignore_option_errors=ignore_option_errors
+        )
+        for filename in tomlfiles:
+            pyprojecttoml.apply_configuration(self, filename, ignore_option_errors)
+
+        self._finalize_requires()
+        self._finalize_license_files()
+
+    def fetch_build_eggs(self, requires):
+        """Resolve pre-setup requirements"""
+        from .installer import _fetch_build_eggs
+
+        return _fetch_build_eggs(self, requires)
+
+    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)
+
+        defined = metadata.entry_points(group=group)
+        filtered = itertools.filterfalse(self._removed, defined)
+        loaded = map(lambda e: e.load(), filtered)
+        for ep in sorted(loaded, key=by_order):
+            ep(self)
+
+    @staticmethod
+    def _removed(ep):
+        """
+        When removing an entry point, if metadata is loaded
+        from an older version of Setuptools, that removed
+        entry point will attempt to be loaded and will fail.
+        See #2765 for more details.
+        """
+        removed = {
+            # removed 2021-09-05
+            '2to3_doctests',
+        }
+        return ep.name in removed
+
+    def _finalize_setup_keywords(self):
+        for ep in metadata.entry_points(group='distutils.setup_keywords'):
+            value = getattr(self, ep.name, None)
+            if value is not None:
+                ep.load()(self, ep.name, value)
+
+    def get_egg_cache_dir(self):
+        from . import windows_support
+
+        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 .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 = metadata.entry_points(group='distutils.commands', name=command)
+        for ep in eps:
+            self.cmdclass[command] = cmdclass = ep.load()
+            return cmdclass
+        else:
+            return _Distribution.get_command_class(self, command)
+
+    def print_commands(self):
+        for ep in metadata.entry_points(group='distutils.commands'):
+            if ep.name not in self.cmdclass:
+                cmdclass = ep.load()
+                self.cmdclass[ep.name] = cmdclass
+        return _Distribution.print_commands(self)
+
+    def get_command_list(self):
+        for ep in metadata.entry_points(group='distutils.commands'):
+            if ep.name not in self.cmdclass:
+                cmdclass = ep.load()
+                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
+
+        return False
+
+    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 as e:
+            raise DistutilsSetupError("%s: No such distribution setting" % name) from e
+        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 as e:
+            raise DistutilsSetupError("%s: No such distribution setting" % name) from e
+        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"""
+
+        yield from self.packages or ()
+
+        yield from self.py_modules or ()
+
+        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 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
+        sys.stdout.reconfigure(encoding='utf-8')
+        try:
+            return _Distribution.handle_display_options(self, option_order)
+        finally:
+            sys.stdout.reconfigure(encoding=encoding)
+
+    def run_command(self, command):
+        self.set_defaults()
+        # Postpone defaults until all explicit configuration is considered
+        # (setup() args, config files, command line and plugins)
+
+        super().run_command(command)
+
+
+class DistDeprecationWarning(SetuptoolsDeprecationWarning):
+    """Class for warning about deprecations in dist in
+    setuptools. Not ignored by default, unlike DeprecationWarning."""
diff --git a/venv/Lib/site-packages/setuptools/errors.py b/venv/Lib/site-packages/setuptools/errors.py
new file mode 100644
index 0000000..67a5a1d
--- /dev/null
+++ b/venv/Lib/site-packages/setuptools/errors.py
@@ -0,0 +1,66 @@
+"""setuptools.errors
+
+Provides exceptions used by setuptools modules.
+"""
+
+from distutils import errors as _distutils_errors
+
+
+# Re-export errors from distutils to facilitate the migration to PEP632
+
+ByteCompileError = _distutils_errors.DistutilsByteCompileError
+CCompilerError = _distutils_errors.CCompilerError
+ClassError = _distutils_errors.DistutilsClassError
+CompileError = _distutils_errors.CompileError
+ExecError = _distutils_errors.DistutilsExecError
+FileError = _distutils_errors.DistutilsFileError
+InternalError = _distutils_errors.DistutilsInternalError
+LibError = _distutils_errors.LibError
+LinkError = _distutils_errors.LinkError
+ModuleError = _distutils_errors.DistutilsModuleError
+OptionError = _distutils_errors.DistutilsOptionError
+PlatformError = _distutils_errors.DistutilsPlatformError
+PreprocessError = _distutils_errors.PreprocessError
+SetupError = _distutils_errors.DistutilsSetupError
+TemplateError = _distutils_errors.DistutilsTemplateError
+UnknownFileError = _distutils_errors.UnknownFileError
+
+# The root error class in the hierarchy
+BaseError = _distutils_errors.DistutilsError
+
+
+class InvalidConfigError(OptionError):
+    """Error used for invalid configurations."""
+
+
+class RemovedConfigError(OptionError):
+    """Error used for configurations that were deprecated and removed."""
+
+
+class RemovedCommandError(BaseError, 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``.
+    """
+
+
+class PackageDiscoveryError(BaseError, RuntimeError):
+    """Impossible to perform automatic discovery of packages and/or modules.
+
+    The current project layout or given discovery options can lead to problems when
+    scanning the project directory.
+
+    Setuptools might also refuse to complete auto-discovery if an error prone condition
+    is detected (e.g. when a project is organised as a flat-layout but contains
+    multiple directories that can be taken as top-level packages inside a single
+    distribution [*]_). In these situations the users are encouraged to be explicit
+    about which packages to include or to make the discovery parameters more specific.
+
+    .. [*] Since multi-package distributions are uncommon it is very likely that the
+       developers did not intend for all the directories to be packaged, and are just
+       leaving auxiliary code in the repository top-level, such as maintenance-related
+       scripts.
+    """
diff --git a/venv/Lib/site-packages/setuptools/extension.py b/venv/Lib/site-packages/setuptools/extension.py
new file mode 100644
index 0000000..8caad78
--- /dev/null
+++ b/venv/Lib/site-packages/setuptools/extension.py
@@ -0,0 +1,152 @@
+import re
+import functools
+import distutils.core
+import distutils.errors
+import distutils.extension
+from typing import TYPE_CHECKING
+
+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
+if TYPE_CHECKING:
+    # Work around a mypy issue where type[T] can't be used as a base: https://github.com/python/mypy/issues/10962
+    _Extension = distutils.core.Extension
+else:
+    _Extension = get_unpatched(distutils.core.Extension)
+
+
+class Extension(_Extension):
+    """
+    Describes a single extension module.
+
+    This means that all source files will be compiled into a single binary file
+    ``.`` (with ```` derived from ``name`` and
+    ```` defined by one of the values in
+    ``importlib.machinery.EXTENSION_SUFFIXES``).
+
+    In the case ``.pyx`` files are passed as ``sources and`` ``Cython`` is **not**
+    installed in the build environment, ``setuptools`` may also try to look for the
+    equivalent ``.cpp`` or ``.c`` files.
+
+    :arg str name:
+      the full name of the extension, including any packages -- ie.
+      *not* a filename or pathname, but Python dotted name
+
+    :arg list[str] sources:
+      list of source filenames, relative to the distribution root
+      (where the setup script lives), in Unix form (slash-separated)
+      for portability.  Source files may be C, C++, SWIG (.i),
+      platform-specific resource files, or whatever else is recognized
+      by the "build_ext" command as source for a Python extension.
+
+    :keyword list[str] include_dirs:
+      list of directories to search for C/C++ header files (in Unix
+      form for portability)
+
+    :keyword list[tuple[str, str|None]] define_macros:
+      list of macros to define; each macro is defined using a 2-tuple:
+      the first item corresponding to the name of the macro and the second
+      item either a string with its value or None to
+      define it without a particular value (equivalent of "#define
+      FOO" in source or -DFOO on Unix C compiler command line)
+
+    :keyword list[str] undef_macros:
+      list of macros to undefine explicitly
+
+    :keyword list[str] library_dirs:
+      list of directories to search for C/C++ libraries at link time
+
+    :keyword list[str] libraries:
+      list of library names (not filenames or paths) to link against
+
+    :keyword list[str] runtime_library_dirs:
+      list of directories to search for C/C++ libraries at run time
+      (for shared extensions, this is when the extension is loaded).
+      Setting this will cause an exception during build on Windows
+      platforms.
+
+    :keyword list[str] extra_objects:
+      list of extra files to link with (eg. object files not implied
+      by 'sources', static library that must be explicitly specified,
+      binary resource files, etc.)
+
+    :keyword list[str] extra_compile_args:
+      any extra platform- and compiler-specific information to use
+      when compiling the source files in 'sources'.  For platforms and
+      compilers where "command line" makes sense, this is typically a
+      list of command-line arguments, but for other platforms it could
+      be anything.
+
+    :keyword list[str] extra_link_args:
+      any extra platform- and compiler-specific information to use
+      when linking object files together to create the extension (or
+      to create a new static Python interpreter).  Similar
+      interpretation as for 'extra_compile_args'.
+
+    :keyword list[str] export_symbols:
+      list of symbols to be exported from a shared extension.  Not
+      used on all platforms, and not generally necessary for Python
+      extensions, which typically export exactly one symbol: "init" +
+      extension_name.
+
+    :keyword list[str] swig_opts:
+      any extra options to pass to SWIG if a source file has the .i
+      extension.
+
+    :keyword list[str] depends:
+      list of files that the extension depends on
+
+    :keyword str language:
+      extension language (i.e. "c", "c++", "objc"). Will be detected
+      from the source extensions if not provided.
+
+    :keyword bool optional:
+      specifies that a build failure in the extension should not abort the
+      build process, but simply not install the failing extension.
+
+    :keyword bool py_limited_api:
+      opt-in flag for the usage of :doc:`Python's limited API `.
+
+    :raises setuptools.errors.PlatformError: if 'runtime_library_dirs' is
+      specified on Windows. (since v63)
+    """
+
+    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)
+        super().__init__(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/site-packages/setuptools/extern/__init__.py b/venv/Lib/site-packages/setuptools/extern/__init__.py
new file mode 100644
index 0000000..427b27c
--- /dev/null
+++ b/venv/Lib/site-packages/setuptools/extern/__init__.py
@@ -0,0 +1,85 @@
+import importlib.util
+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 _module_matches_namespace(self, fullname):
+        """Figure out if the target module is vendored."""
+        root, base, target = fullname.partition(self.root_name + '.')
+        return not root and any(map(target.startswith, self.vendored_names))
+
+    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 create_module(self, spec):
+        return self.load_module(spec.name)
+
+    def exec_module(self, module):
+        pass
+
+    def find_spec(self, fullname, path=None, target=None):
+        """Return a module spec for vendored names."""
+        return (
+            importlib.util.spec_from_loader(fullname, self)
+            if self._module_matches_namespace(fullname)
+            else None
+        )
+
+    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',
+    'ordered_set',
+    'more_itertools',
+    'importlib_metadata',
+    'zipp',
+    'importlib_resources',
+    'jaraco',
+    'typing_extensions',
+    'tomli',
+    'backports',
+)
+VendorImporter(__name__, names, 'setuptools._vendor').install()
diff --git a/venv/Lib/site-packages/setuptools/extern/__pycache__/__init__.cpython-312.pyc b/venv/Lib/site-packages/setuptools/extern/__pycache__/__init__.cpython-312.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..4d59980e8a47c68280cbe096ab1436ae6d55ce1d
GIT binary patch
literal 4141
zcmb_fU2GKB6~1@=_Q&g88ykZijEC@JFJMDU38e{X2tR=U8x-0|v0RPDJJ)#3?#|}U
zEcR~K5{h^*YEqy|Yby1@sUj7GDp6Hc`xxc5FDxdqJK>>KU)s0DMoNnM&~xtWEQ{l`
zFCE!u?%bbq@1FCW@0|PR_Vze|hyQJO;@>es{*E8vqc$3=W6+o<7O`lKj8gHIMkR^7
zK`iMivE;i_FmqJ4lu<<@OlB%mEcLD&^iQ=nG_}0$HrkYwX4$o2j{E74Wf&Jgs^UO2#qUlZQc4LY@;c$JzE?9QqqV5zu
z+bNj2TzUAiP{b^u0H!$JB;(Vz%k+ZjmAIH=7hDe-%mS6!OXfx9>J!|_>)dfXqhRLQ
zh*+J;DlHbkHX3ksQW&60tHfvydkxbPQkCw~pc}?RqmimSL){IOROwWtxv1H&s3nC?
zgH_F4%Oovle2vY3NeX_%2!2~y^<&MG`m)PBKNR{`9j*->{?pK>rx%AZ
zbIOOQRaio@4HsZrAa^^wS6>2ghEPCnl}zEtn#ZCQ==lYmrxYS7c$ekh%F{HhRL%-W
zprI}>lV>LZd))POK$jlaIK&cm6?D_p%@A?nNPsF-@sy7EB$KAX19vFLfOPT9I!}W1
z+g!1Sfrzj4t+7zcSO>1#0^%R!t1F})C#kOM&NXL2t|hlWh;Cm_cKjszFX7?$H}%qN
z6OOP#gMR_%Uqb^dLZ@1727UN|om$rfoxnS2rx502pk-E?m1mWzlpr&drz!D5t|DF&
z?p0+9k+NFWdqP;=Gx3PR0<(O`}4OcL0
zgl_FXWPz
z&-OmuPCR5r@$4X6rsQ4nL4KOd$nO%t1!v*{0w7KUGn*b@@-pKBkwI`m#6zm`96je`
zao8+a&@?SQ#MUz^5J0W`{0G1zgFg;HHai$H211&%_ewTr*2{Xq;d#touo3jb-*~BD
zVHQIS2b~T2)m;Zer0kS*j1Mt?+T;bG@x6N1DY&IvKwXXbkS4`!u3NV2aeKUk5i@d!
zq+@&&*dfRn8=>XLfuKbSHVnu;Wym!_1h{@%kiAcE?r>j&^b9fKYiNO-n~sX$;xC{w
zBrM*KVk>IYikwZ@Ga}EK1rMUrv0Q{lXygVbq5&I0^fgXw8AHYHA3y*hM#^^qU=BDd0cZl%|QAiBM!&7fC;)xb`e(fZ@>cTh`Fp
zbE5}for{2hq@C(oN)6Of0}IM;VjssAQ@ig+cYmFlU~ceZkWJ=Enebs~@NFQ%BWVqQ
zwH~UC&9H2Eq_qjMWeiik;A}WhG!bmy%3IhLww#2lJrcD2Dm$
z0_0AS%ZA>D@`yv35aup4yC`XcVgiC1z2c5!(h47dv-zT>qDuRwI>{0>}VVBKmU
z`Wc6r=t2==zJd%S;;UKU1z{A!n%Tg$mCb8h*&I0xYTQF0L)Xa$Qzp@w!Gf^wcBJ3hdm9$|hz*JA0r#c-F(|WeS!K
zC#+k8Of@Tv<}|2QLOM{~u
zxgvhDsdog!j~|2s3mgb*_^Jq3{wnsaW4EW#R|40(%HM)17#bqLfb=Q-f^7VP#J|*z
rQabpUpsb&g+G5fb_3>sxQ*$%7Hs062vq%muks~#7([^<]+)\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 as e:
+        raise DistutilsError(
+            "Not a URL, existing file, or requirement spec: %r" % (spec,)
+        ) from e
+
+
+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)
+    yield from distros_for_location(url, base, metadata)
+    if fragment:
+        match = EGG_FRAGMENT.match(fragment)
+        if match:
+            yield from interpret_distro_name(
+                url, match.group(1), metadata, precedence=CHECKOUT_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 the interpretation 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!
+    """
+
+    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
+
+    # find the pivot (p) that splits the name from the version.
+    # infer the version as the first item that has a digit.
+    for p in range(len(parts)):
+        if parts[p][:1].isdigit():
+            break
+    else:
+        p = len(parts)
+
+    yield Distribution(
+        location,
+        metadata,
+        '-'.join(parts[:p]),
+        '-'.join(parts[p:]),
+        py_version=py_version,
+        precedence=precedence,
+        platform=platform,
+    )
+
+
+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{0,10}=\s{0,10}['"]?([^'" >]+)[^>]*)>""", re.I)
+"""
+Regex for an HTML tag with 'rel="val"' attributes.
+"""
+
+
+@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,
+    ):
+        super().__init__(*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 = []
+        self.opener = urllib.request.urlopen
+
+    def add(self, dist):
+        # ignore invalid versions
+        try:
+            parse_version(dist.version)
+        except Exception:
+            return None
+        return super().add(dist)
+
+    # FIXME: 'PackageIndex.process_url' is too complex (14)
+    def process_url(self, url, retrieve=False):  # noqa: C901
+        """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 "
+            "https://setuptools.pypa.io/en/latest/deprecated/"
+            "easy_install.html#restricting-downloads-with-allow-hosts for details).\n"
+        )
+        if fatal:
+            raise DistutilsError(msg % url)
+        else:
+            self.warn(msg, url)
+            return False
+
+    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 _scan(self, link):
+        # Process a URL to see if it's for a package page
+        NO_MATCH_SENTINEL = None, None
+        if not link.startswith(self.index_url):
+            return NO_MATCH_SENTINEL
+
+        parts = list(map(urllib.parse.unquote, link[len(self.index_url) :].split('/')))
+        if len(parts) != 2 or '#' in parts[1]:
+            return NO_MATCH_SENTINEL
+
+        # 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)
+
+    def process_index(self, url, page):
+        """Process the contents of a PyPI page"""
+
+        # process an index page into the package-page index
+        for match in HREF.finditer(page):
+            try:
+                self._scan(urllib.parse.urljoin(url, htmldecode(match.group(1))))
+            except ValueError:
+                pass
+
+        pkg, ver = self._scan(url)  # ensure this page is in the page index
+        if not pkg:
+            return ""  # no sense double-scanning non-package pages
+
+        # 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
+        )
+
+    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().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(  # noqa: C901  # is too complex (14)  # FIXME
+        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
+
+            return None
+
+        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,
+            )
+            return None
+        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)
+                if not (os.path.exists(dst) and os.path.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 = headers.get_all('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
+
+    # FIXME:
+    def open_url(self, url, warning=None):  # noqa: C901  # is too complex (12)
+        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)) from v
+        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)
+                ) from v
+        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)
+                ) from v
+        except (http.client.HTTPException, OSError) as v:
+            if warning:
+                self.warn(warning, v)
+            else:
+                raise DistutilsError("Download error for %s: %s" % (url, v)) from 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._invalid_download_html(url, headers, filename)
+        else:
+            return filename
+
+    def _invalid_download_html(self, url, headers, filename):
+        os.unlink(filename)
+        raise DistutilsError(f"Unexpected HTML page found at {url}")
+
+    def _download_svn(self, url, _filename):
+        raise DistutilsError(f"Invalid config, SVN download is not supported: {url}")
+
+    @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 html.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):
+    """
+    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'], '')
+        super().__init__(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
+        return None
+
+
+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('{name}'.format(name=f))
+        else:
+            tmpl = (
+                "{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 = io.StringIO(body)
+    return urllib.error.HTTPError(url, status, message, headers, body_stream)
diff --git a/venv/Lib/site-packages/setuptools/sandbox.py b/venv/Lib/site-packages/setuptools/sandbox.py
new file mode 100644
index 0000000..6c095e0
--- /dev/null
+++ b/venv/Lib/site-packages/setuptools/sandbox.py
@@ -0,0 +1,529 @@
+import os
+import sys
+import tempfile
+import operator
+import functools
+import itertools
+import re
+import contextlib
+import pickle
+import textwrap
+import builtins
+from typing import Union, List
+
+import pkg_resources
+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  # type: ignore[name-defined] # Check for global variable
+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
+    """
+    os.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, serialize, and restore it
+    later.
+    """
+
+    def __enter__(self):
+        return self
+
+    def __exit__(self, type, exc, tb):
+        if not exc:
+            return False
+
+        # 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)
+        raise exc.with_traceback(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():
+            with save_path():
+                hide_setuptools()
+                with save_argv():
+                    with override_temp(temp_dir):
+                        with pushd(setup_dir):
+                            # ensure setuptools commands are available
+                            __import__('setuptools')
+                            yield
+
+
+_MODULES_TO_HIDE = {
+    'setuptools',
+    'distutils',
+    'pkg_resources',
+    'Cython',
+    '_distutils_hack',
+}
+
+
+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
+    """
+    base_module = mod_name.split('.', 1)[0]
+    return base_module in _MODULES_TO_HIDE
+
+
+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.
+    """
+    _distutils_hack = sys.modules.get('_distutils_hack', None)
+    if _distutils_hack is not None:
+        _distutils_hack._remove_shim()
+
+    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())
+
+            with DirectorySandbox(setup_dir):
+                ns = dict(__file__=setup_script, __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: str):  # type: ignore[misc] # https://github.com/pypa/setuptools/pull/4099
+        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: str, original=None):  # type: ignore[misc] # https://github.com/pypa/setuptools/pull/4099
+        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: str):  # type: ignore[misc] # https://github.com/pypa/setuptools/pull/4099
+        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: str):  # type: ignore[misc] # https://github.com/pypa/setuptools/pull/4099
+        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: List[Union[str, re.Pattern]] = []
+    "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/site-packages/setuptools/script (dev).tmpl b/venv/Lib/site-packages/setuptools/script (dev).tmpl
new file mode 100644
index 0000000..39a24b0
--- /dev/null
+++ b/venv/Lib/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/site-packages/setuptools/script.tmpl b/venv/Lib/site-packages/setuptools/script.tmpl
new file mode 100644
index 0000000..ff5efbc
--- /dev/null
+++ b/venv/Lib/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/site-packages/setuptools/unicode_utils.py b/venv/Lib/site-packages/setuptools/unicode_utils.py
new file mode 100644
index 0000000..d43dcc1
--- /dev/null
+++ b/venv/Lib/site-packages/setuptools/unicode_utils.py
@@ -0,0 +1,44 @@
+import unicodedata
+import sys
+
+
+# HFS Plus uses decomposed UTF-8
+def decompose(path):
+    if isinstance(path, str):
+        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, str):
+        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
+
+    return None
+
+
+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/site-packages/setuptools/version.py b/venv/Lib/site-packages/setuptools/version.py
new file mode 100644
index 0000000..ec253c4
--- /dev/null
+++ b/venv/Lib/site-packages/setuptools/version.py
@@ -0,0 +1,6 @@
+from ._importlib import metadata
+
+try:
+    __version__ = metadata.version('setuptools') or '0.dev0+unknown'
+except Exception:
+    __version__ = '0.dev0+unknown'
diff --git a/venv/Lib/site-packages/setuptools/warnings.py b/venv/Lib/site-packages/setuptools/warnings.py
new file mode 100644
index 0000000..b3e252c
--- /dev/null
+++ b/venv/Lib/site-packages/setuptools/warnings.py
@@ -0,0 +1,105 @@
+"""Provide basic warnings used by setuptools modules.
+
+Using custom classes (other than ``UserWarning``) allow users to set
+``PYTHONWARNINGS`` filters to run tests and prepare for upcoming changes in
+setuptools.
+"""
+
+import os
+import warnings
+from datetime import date
+from inspect import cleandoc
+from textwrap import indent
+from typing import Optional, Tuple
+
+_DueDate = Tuple[int, int, int]  # time tuple
+_INDENT = 8 * " "
+_TEMPLATE = f"""{80 * '*'}\n{{details}}\n{80 * '*'}"""
+
+
+class SetuptoolsWarning(UserWarning):
+    """Base class in ``setuptools`` warning hierarchy."""
+
+    @classmethod
+    def emit(
+        cls,
+        summary: Optional[str] = None,
+        details: Optional[str] = None,
+        due_date: Optional[_DueDate] = None,
+        see_docs: Optional[str] = None,
+        see_url: Optional[str] = None,
+        stacklevel: int = 2,
+        **kwargs,
+    ):
+        """Private: reserved for ``setuptools`` internal use only"""
+        # Default values:
+        summary_ = summary or getattr(cls, "_SUMMARY", None) or ""
+        details_ = details or getattr(cls, "_DETAILS", None) or ""
+        due_date = due_date or getattr(cls, "_DUE_DATE", None)
+        docs_ref = see_docs or getattr(cls, "_SEE_DOCS", None)
+        docs_url = docs_ref and f"https://setuptools.pypa.io/en/latest/{docs_ref}"
+        see_url = see_url or getattr(cls, "_SEE_URL", None)
+        due = date(*due_date) if due_date else None
+
+        text = cls._format(summary_, details_, due, see_url or docs_url, kwargs)
+        if due and due < date.today() and _should_enforce():
+            raise cls(text)
+        warnings.warn(text, cls, stacklevel=stacklevel + 1)
+
+    @classmethod
+    def _format(
+        cls,
+        summary: str,
+        details: str,
+        due_date: Optional[date] = None,
+        see_url: Optional[str] = None,
+        format_args: Optional[dict] = None,
+    ):
+        """Private: reserved for ``setuptools`` internal use only"""
+        today = date.today()
+        summary = cleandoc(summary).format_map(format_args or {})
+        possible_parts = [
+            cleandoc(details).format_map(format_args or {}),
+            (
+                f"\nBy {due_date:%Y-%b-%d}, you need to update your project and remove "
+                "deprecated calls\nor your builds will no longer be supported."
+                if due_date and due_date > today
+                else None
+            ),
+            (
+                "\nThis deprecation is overdue, please update your project and remove "
+                "deprecated\ncalls to avoid build errors in the future."
+                if due_date and due_date < today
+                else None
+            ),
+            (f"\nSee {see_url} for details." if see_url else None),
+        ]
+        parts = [x for x in possible_parts if x]
+        if parts:
+            body = indent(_TEMPLATE.format(details="\n".join(parts)), _INDENT)
+            return "\n".join([summary, "!!\n", body, "\n!!"])
+        return summary
+
+
+class InformationOnly(SetuptoolsWarning):
+    """Currently there is no clear way of displaying messages to the users
+    that use the setuptools backend directly via ``pip``.
+    The only thing that might work is a warning, although it is not the
+    most appropriate tool for the job...
+
+    See pypa/packaging-problems#558.
+    """
+
+
+class SetuptoolsDeprecationWarning(SetuptoolsWarning):
+    """
+    Base class for warning deprecations in ``setuptools``
+
+    This class is not derived from ``DeprecationWarning``, and as such is
+    visible by default.
+    """
+
+
+def _should_enforce():
+    enforce = os.getenv("SETUPTOOLS_ENFORCE_DEPRECATION", "false").lower()
+    return enforce in ("true", "on", "ok", "1")
diff --git a/venv/Lib/site-packages/setuptools/wheel.py b/venv/Lib/site-packages/setuptools/wheel.py
new file mode 100644
index 0000000..9861b5c
--- /dev/null
+++ b/venv/Lib/site-packages/setuptools/wheel.py
@@ -0,0 +1,234 @@
+"""Wheels support."""
+
+import email
+import itertools
+import functools
+import os
+import posixpath
+import re
+import zipfile
+import contextlib
+
+from distutils.util import get_platform
+
+import setuptools
+from setuptools.extern.packaging.version import Version as parse_version
+from setuptools.extern.packaging.tags import sys_tags
+from setuptools.extern.packaging.utils import canonicalize_name
+from setuptools.command.egg_info import write_requirements, _egg_basename
+from setuptools.archive_util import _unpack_zipfile_obj
+
+
+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"
+
+
+@functools.lru_cache(maxsize=None)
+def _get_supported_tags():
+    # We calculate the supported tags only once, otherwise calling
+    # this method on thousands of wheels takes seconds instead of
+    # milliseconds.
+    return {(t.interpreter, t.abi, t.platform) for t in sys_tags()}
+
+
+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)
+
+
+@contextlib.contextmanager
+def disable_info_traces():
+    """
+    Temporarily disable info traces.
+    """
+    from distutils import log
+
+    saved = log.set_threshold(log.WARN)
+    try:
+        yield
+    finally:
+        log.set_threshold(saved)
+
+
+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 compatible with the current platform?"""
+        return next((True for t in self.tags() if t in _get_supported_tags()), False)
+
+    def egg_name(self):
+        return (
+            _egg_basename(
+                self.project_name,
+                self.version,
+                platform=(None if self.platform == 'any' else get_platform()),
+            )
+            + ".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):
+        import pkg_resources
+
+        def get_metadata(name):
+            with zf.open(posixpath.join(dist_info, name)) as fp:
+                value = fp.read().decode('utf-8')
+                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.
+        _unpack_zipfile_obj(zf, 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(map(raw_req, dist.requires()))
+        extras_require = {
+            extra: [
+                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,
+            ),
+        )
+        with disable_info_traces():
+            write_requirements(
+                setup_dist.get_command_obj('egg_info'),
+                None,
+                os.path.join(egg_info, 'requires.txt'),
+            )
+
+    @staticmethod
+    def _move_data_entries(destination_eggdir, dist_data):
+        """Move data entries to their correct location."""
+        dist_data = os.path.join(destination_eggdir, dist_data)
+        dist_data_scripts = os.path.join(dist_data, 'scripts')
+        if os.path.exists(dist_data_scripts):
+            egg_info_scripts = os.path.join(destination_eggdir, 'EGG-INFO', 'scripts')
+            os.mkdir(egg_info_scripts)
+            for entry in os.listdir(dist_data_scripts):
+                # Remove bytecode, as it's not properly handled
+                # during easy_install scripts install phase.
+                if entry.endswith('.pyc'):
+                    os.unlink(os.path.join(dist_data_scripts, entry))
+                else:
+                    os.rename(
+                        os.path.join(dist_data_scripts, entry),
+                        os.path.join(egg_info_scripts, entry),
+                    )
+            os.rmdir(dist_data_scripts)
+        for subdir in filter(
+            os.path.exists,
+            (
+                os.path.join(dist_data, d)
+                for d in ('data', 'headers', 'purelib', 'platlib')
+            ),
+        ):
+            unpack(subdir, destination_eggdir)
+        if os.path.exists(dist_data):
+            os.rmdir(dist_data)
+
+    @staticmethod
+    def _fix_namespace_packages(egg_info, destination_eggdir):
+        namespace_packages = os.path.join(egg_info, 'namespace_packages.txt')
+        if os.path.exists(namespace_packages):
+            with open(namespace_packages) as fp:
+                namespace_packages = fp.read().split()
+            for mod in namespace_packages:
+                mod_dir = os.path.join(destination_eggdir, *mod.split('.'))
+                mod_init = os.path.join(mod_dir, '__init__.py')
+                if 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/site-packages/setuptools/windows_support.py b/venv/Lib/site-packages/setuptools/windows_support.py
new file mode 100644
index 0000000..8299ac1
--- /dev/null
+++ b/venv/Lib/site-packages/setuptools/windows_support.py
@@ -0,0 +1,30 @@
+import platform
+
+
+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 https://stackoverflow.com/questions/19622133/
+
+    `path` must be text.
+    """
+    import ctypes
+
+    __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()